From 4289293061a1da39c04e86fe6c02eaec29352f50 Mon Sep 17 00:00:00 2001 From: Jasper Verschueren Date: Fri, 12 Oct 2018 15:35:02 +0200 Subject: [PATCH] mDNS: add support for output delaying See below commit messages for more information mDNS: support for direct and delayed sends There are two ways to send a response, directly and after a delay. A probe or an announce msg are now send via the direct way and all responses are send via the delayed way. mDNS improved delay vs direct send behavior on questions All multicast answers are delayed at the moment. While all unicast answers are send out directly. A unicast answer is send when this is requested by the QU bit, when a unicast question was send or when the question originated from a legacy querier. mDNS: add probe query detection. If a probe query is detected a direct unicast respond is send. Independent of the QU/QM bit. mDNS split delayed multicast msgs into ipv4 and ipv6 buffers. We are implementing a two resolvers in one (IPv6 and IPv4 together). For directly send answers, this does not matter. But for delayed answers, we need to make a separate buffer for both. mDNS: addr bug, we should not clear full outmsg memset deleted also the dest_addr and dest_port, which should remain and is constant. This commit contains a function that resets only the needed parts of the outmsg struct. mDNS: do not multicast a rr within one second. RFC6762 section 6: prevent network flooding. When a multicast packet is send out, we start a timeout of 1s within this 1 second all multicast requests are ignored. We do not make a difference between the records, we set the delay for all records. mDNS: improved split for unicast vs multicast and direct vs delayed unicast delayed message are now possible and multicast direct msgs to. MDNS: changed printfs to lwip debug messages MDNS: change timeouts from max time to random time mDNS: send multicast response on QU questions if not multicasted recently. If a QU question is received, the responder should multicast the answer if it did not multicast that record within 25% of it's ttl. we implemented a stripped down version, meaning that we look at the records as one set and use one timer for all records. So if the responder multicasted a record within 30s of the QU question it will respond with a unicast answer. if not, it will respond multicast. mDNS: timeouts -> create function for mdns timeout handling mdns_set_timeout will check if the timer is running or not and will update the flag to running after starting the timer. Multicast timeouts were not set everywhere they needed to be. This is solved. mulit <-> multi typo fixed. mDNS: solve commenting and style issues mDNS: add #if LWIP_IPVx to new code LWIP_IPV4/6 can be enabled or disabled, all combination should work. --- src/apps/mdns/mdns.c | 553 +++++++++++++++++++++++----- src/apps/mdns/mdns_out.c | 238 +++++++++++- src/include/lwip/apps/mdns_domain.h | 4 + src/include/lwip/apps/mdns_out.h | 36 ++ src/include/lwip/apps/mdns_priv.h | 56 ++- 5 files changed, 784 insertions(+), 103 deletions(-) diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c index 7f469a2d..6fc10e62 100644 --- a/src/apps/mdns/mdns.c +++ b/src/apps/mdns/mdns.c @@ -80,6 +80,9 @@ #if (!LWIP_UDP) #error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h" #endif +#ifndef LWIP_RAND +#error "If you want to use MDNS, you have to define LWIP_RAND=(random function) in your lwipopts.h" +#endif #if LWIP_IPV4 #include "lwip/igmp.h" @@ -105,6 +108,13 @@ static mdns_name_result_cb_t mdns_name_result_cb; #define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id)) +/** Delayed response defines */ +#define MDNS_RESPONSE_DELAY_MAX 120 +#define MDNS_RESPONSE_DELAY_MIN 20 +#define MDNS_RESPONSE_DELAY (LWIP_RAND() %(MDNS_RESPONSE_DELAY_MAX - \ + MDNS_RESPONSE_DELAY_MIN) + MDNS_RESPONSE_DELAY_MIN) + +/** Probing defines */ #define MDNS_PROBE_DELAY_MS 250 #define MDNS_PROBE_COUNT 3 #ifdef LWIP_RAND @@ -136,12 +146,18 @@ struct mdns_packet { u16_t questions; /** Number of unparsed questions */ u16_t questions_left; - /** Number of answers in packet, - * (sum of normal, authoritative and additional answers) - * read from packet header */ + /** Number of answers in packet */ u16_t answers; /** Number of unparsed answers */ u16_t answers_left; + /** Number of authoritative answers in packet */ + u16_t authoritative; + /** Number of unparsed authoritative answers */ + u16_t authoritative_left; + /** Number of additional answers in packet */ + u16_t additional; + /** Number of unparsed additional answers */ + u16_t additional_left; }; /** Domain, type and class. @@ -172,11 +188,25 @@ struct mdns_answer { static void mdns_probe(void* arg); +/** + * Construction to make mdns struct accessible from mdns_out.c + * TODO: + * can we add the mdns struct to the netif like we do for dhcp, autoip,...? + * Then this is not needed any more. + * + * @param netif The network interface + * @return mdns struct + */ struct mdns_host* netif_mdns_data(struct netif *netif) { return NETIF_TO_HOST(netif); } +/** + * Construction to access the mdns udp pcb. + * + * @return udp_pcb struct of mdns + */ struct udp_pcb* get_mdns_pcb(void) { @@ -370,13 +400,14 @@ mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question) /** * Read an answer from the packet * The variable length reply is not copied, its pbuf offset and length is stored instead. - * @param pkt The MDNS packet to read. The answers_left field will be decremented and + * @param pkt The MDNS packet to read. The num_left field will be decremented and * the parse_offset will be updated. - * @param answer The struct to fill with answer data + * @param answer The struct to fill with answer data + * @param num_left number of answers left -> answers, authoritative or additional * @return ERR_OK on success, an err_t otherwise */ static err_t -mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer) +mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer, u16_t *num_left) { /* Read questions first */ if (pkt->questions_left) { @@ -388,11 +419,11 @@ mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer) return ERR_VAL; } - if (pkt->answers_left) { + if (*num_left) { u16_t copied, field16; u32_t ttl; err_t res; - pkt->answers_left--; + (*num_left)--; memset(answer, 0, sizeof(struct mdns_answer)); res = mdns_read_rr_info(pkt, &answer->info); @@ -426,35 +457,6 @@ mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer) return ERR_VAL; } -/** - * Setup outpacket as a reply to the incoming packet - */ -static void -mdns_init_outmsg_with_in_packet(struct mdns_outmsg *out, struct mdns_packet *in) -{ - memset(out, 0, sizeof(struct mdns_outmsg)); - out->cache_flush = 1; - - /* Copy source IP/port to use when responding unicast, or to choose - * which pcb to use for multicast (IPv4/IPv6) - */ - SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t)); - out->dest_port = in->source_port; - - if (in->source_port != LWIP_IANA_PORT_MDNS) { - out->unicast_reply = 1; - out->cache_flush = 0; - if (in->questions == 1) { - out->legacy_query = 1; - out->tx_id = in->tx_id; - } - } - - if (in->recv_unicast) { - out->unicast_reply = 1; - } -} - /** * Send unsolicited answer containing all our known data * @param netif The network interface to send on @@ -498,27 +500,21 @@ mdns_announce(struct netif *netif, const ip_addr_t *destination) } /** - * Handle question MDNS packet - * 1. Parse all questions and set bits what answers to send - * 2. Clear pending answers if known answers are supplied - * 3. Put chosen answers in new packet and send as reply + * Check the incomming packet and parse all questions + * + * @param netif network interface of incoming packet + * @param pkt incoming packet + * @param reply outgoing message + * @return err_t */ -static void -mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) +static err_t +mdns_parse_pkt_questions(struct netif *netif, struct mdns_packet *pkt, + struct mdns_outmsg *reply) { + struct mdns_host *mdns = NETIF_TO_HOST(netif); struct mdns_service *service; - struct mdns_outmsg reply; int i; err_t res; - struct mdns_host *mdns = NETIF_TO_HOST(netif); - - if (mdns->probing_state != MDNS_PROBING_COMPLETE) { - /* Don't answer questions until we've verified our domains via probing */ - /* @todo we should check incoming questions during probing for tiebreaking */ - return; - } - - mdns_init_outmsg_with_in_packet(&reply, pkt); while (pkt->questions_left) { struct mdns_question q; @@ -526,7 +522,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) res = mdns_read_question(pkt, &q); if (res != ERR_OK) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n")); - return; + return res; } LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain ")); @@ -534,32 +530,51 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass)); if (q.unicast) { - /* Reply unicast if any question is unicast */ - reply.unicast_reply = 1; + /* Reply unicast if it is requested in the question */ + reply->unicast_reply_requested = 1; } - reply.host_replies |= check_host(netif, &q.info, &reply.host_reverse_v6_replies); + reply->host_replies |= check_host(netif, &q.info, &reply->host_reverse_v6_replies); for (i = 0; i < MDNS_MAX_SERVICES; i++) { service = mdns->services[i]; if (!service) { continue; } - reply.serv_replies[i] |= check_service(service, &q.info); + reply->serv_replies[i] |= check_service(service, &q.info); } } - /* Handle known answers */ + return ERR_OK; +} + +/** + * Check the incomming packet and parse all (known) answers + * + * @param netif network interface of incoming packet + * @param pkt incoming packet + * @param reply outgoing message + * @return err_t + */ +static err_t +mdns_parse_pkt_known_answers(struct netif *netif, struct mdns_packet *pkt, + struct mdns_outmsg *reply) +{ + struct mdns_host *mdns = NETIF_TO_HOST(netif); + struct mdns_service *service; + int i; + err_t res; + while (pkt->answers_left) { struct mdns_answer ans; u8_t rev_v6; int match; u32_t rr_ttl = MDNS_TTL_120; - res = mdns_read_answer(pkt, &ans); + res = mdns_read_answer(pkt, &ans, &pkt->answers_left); if (res != ERR_OK) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n")); - return; + return res; } LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain ")); @@ -573,7 +588,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) } rev_v6 = 0; - match = reply.host_replies & check_host(netif, &ans.info, &rev_v6); + match = reply->host_replies & check_host(netif, &ans.info, &rev_v6); if (match && (ans.ttl > (rr_ttl / 2))) { /* The RR in the known answer matches an RR we are planning to send, * and the TTL is less than half gone. @@ -589,15 +604,15 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) #if LWIP_IPV4 if (match & REPLY_HOST_PTR_V4) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n")); - reply.host_replies &= ~REPLY_HOST_PTR_V4; + reply->host_replies &= ~REPLY_HOST_PTR_V4; } #endif #if LWIP_IPV6 if (match & REPLY_HOST_PTR_V6) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n")); - reply.host_reverse_v6_replies &= ~rev_v6; - if (reply.host_reverse_v6_replies == 0) { - reply.host_replies &= ~REPLY_HOST_PTR_V6; + reply->host_reverse_v6_replies &= ~rev_v6; + if (reply->host_reverse_v6_replies == 0) { + reply->host_replies &= ~REPLY_HOST_PTR_V6; } } #endif @@ -607,7 +622,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) if (ans.rd_length == sizeof(ip4_addr_t) && pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(netif), ans.rd_length) == 0) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n")); - reply.host_replies &= ~REPLY_HOST_A; + reply->host_replies &= ~REPLY_HOST_A; } #endif } else if (match & REPLY_HOST_AAAA) { @@ -616,7 +631,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) /* TODO this clears all AAAA responses if first addr is set as known */ pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(netif, 0), ans.rd_length) == 0) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n")); - reply.host_replies &= ~REPLY_HOST_AAAA; + reply->host_replies &= ~REPLY_HOST_AAAA; } #endif } @@ -627,7 +642,7 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) if (!service) { continue; } - match = reply.serv_replies[i] & check_service(service, &ans.info); + match = reply->serv_replies[i] & check_service(service, &ans.info); if (match & REPLY_SERVICE_TYPE_PTR) { rr_ttl = MDNS_TTL_4500; } @@ -646,14 +661,14 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) res = mdns_build_service_domain(&my_ans, service, 0); if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n")); - reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR; + reply->serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR; } } if (match & REPLY_SERVICE_NAME_PTR) { res = mdns_build_service_domain(&my_ans, service, 1); if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n")); - reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR; + reply->serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR; } } } @@ -688,37 +703,374 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) break; } LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n")); - reply.serv_replies[i] &= ~REPLY_SERVICE_SRV; + reply->serv_replies[i] &= ~REPLY_SERVICE_SRV; } while (0); } else if (match & REPLY_SERVICE_TXT) { mdns_prepare_txtdata(service); if (service->txtdata.length == ans.rd_length && pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n")); - reply.serv_replies[i] &= ~REPLY_SERVICE_TXT; + reply->serv_replies[i] &= ~REPLY_SERVICE_TXT; } } } } } - reply.flags = DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE; + return ERR_OK; +} - if(!reply.unicast_reply) { - reply.dest_port = LWIP_IANA_PORT_MDNS; +/** + * Check the incomming packet and parse all authoritative answers to see if the + * query is a probe query. + * + * @param netif network interface of incoming packet + * @param pkt incoming packet + * @param reply outgoing message + * @return err_t + */ +static err_t +mdns_parse_pkt_authoritative_answers(struct netif *netif, struct mdns_packet *pkt, + struct mdns_outmsg *reply) +{ + struct mdns_host *mdns = NETIF_TO_HOST(netif); + struct mdns_service *service; + int i; + err_t res; - if (IP_IS_V6_VAL(reply.dest_addr)) { -#if LWIP_IPV6 - SMEMCPY(&reply.dest_addr, &v6group, sizeof(reply.dest_addr)); -#endif - } else { -#if LWIP_IPV4 - SMEMCPY(&reply.dest_addr, &v4group, sizeof(reply.dest_addr)); -#endif + while (pkt->authoritative_left) { + struct mdns_answer ans; + u8_t rev_v6; + int match; + + res = mdns_read_answer(pkt, &ans, &pkt->authoritative_left); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n")); + return res; + } + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Authoritative answer for domain ")); + mdns_domain_debug_print(&ans.info.domain); + LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass)); + + + if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) { + /* Skip known answers for ANY type & class */ + continue; + } + + rev_v6 = 0; + match = reply->host_replies & check_host(netif, &ans.info, &rev_v6); + if (match) { + reply->probe_query_recv = 1; + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe for own host info received\r\n")); + } + + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + service = mdns->services[i]; + if (!service) { + continue; + } + match = reply->serv_replies[i] & check_service(service, &ans.info); + + if (match) { + reply->probe_query_recv = 1; + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe for own service info received\r\n")); + } } } - mdns_send_outpacket(&reply, netif); + return ERR_OK; +} + +/** + * Add / copy message to delaying message buffer. + * + * @param dest destination msg struct + * @param src source msg struct + */ +static void +mdns_add_msg_to_delayed(struct mdns_outmsg *dest, struct mdns_outmsg *src) +{ + dest->host_questions |= src->host_questions; + dest->host_replies |= src->host_replies; + dest->host_reverse_v6_replies |= src->host_reverse_v6_replies; + for (int i = 0; i < MDNS_MAX_SERVICES; i++) { + dest->serv_questions[i] |= src->serv_questions[i]; + dest->serv_replies[i] |= src->serv_replies[i]; + } + + dest->flags = src->flags; + dest->cache_flush = src->cache_flush; + dest->tx_id = src->tx_id; + dest->legacy_query = src->legacy_query; +} + +/** + * Handle question MDNS packet + * 1. Parse all questions and set bits what answers to send + * 2. Clear pending answers if known answers are supplied + * 3. Define which type of answer is requested + * 4. Send out packet or put it on hold until after random time + * + * @param pkt incoming packet + * @param netif network interface of incoming packet + */ +static void +mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) +{ + struct mdns_host *mdns = NETIF_TO_HOST(netif); + struct mdns_outmsg reply; + u8_t rrs_to_send; + u8_t shared_answer = 0; + u8_t delay_response = 1; + u8_t send_unicast = 0; + u8_t listen_to_QU_bit = 0; + int i; + err_t res; + + if (mdns->probing_state != MDNS_PROBING_COMPLETE) { + /* Don't answer questions until we've verified our domains via probing */ + /* @todo we should check incoming questions during probing for tiebreaking */ + return; + } + + memset(&reply, 0, sizeof(struct mdns_outmsg)); + + /* Parse question */ + res = mdns_parse_pkt_questions(netif, pkt, &reply); + if (res != ERR_OK) { + return; + } + /* Parse answers -> count as known answers because it's a question */ + res = mdns_parse_pkt_known_answers(netif, pkt, &reply); + if (res != ERR_OK) { + return; + } + /* Parse authoritative answers -> probing */ + /* If it's a probe query, we need to directly answer via unicast. */ + res = mdns_parse_pkt_authoritative_answers(netif, pkt, &reply); + if (res != ERR_OK) { + return; + } + /* Ignore additional answers -> do not have any need for them at the moment */ + if(pkt->additional) { + LWIP_DEBUGF(MDNS_DEBUG, + ("MDNS: Query contains additional answers -> they are discarded \r\n")); + } + + /* Any replies on question? */ + rrs_to_send = reply.host_replies | reply.host_questions; + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + rrs_to_send |= reply.serv_replies[i] | reply.serv_questions[i]; + } + + if (!rrs_to_send) { + /* This case is most common */ + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Nothing to answer\r\n")); + return; + } + + reply.flags = DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE; + + /* Detect if it's a legacy querier asking the question + * How to detect legacy DNS query? (RFC6762 section 6.7) + * - source port != 5353 + * - a legacy query can only contain 1 question + */ + if (pkt->source_port != LWIP_IANA_PORT_MDNS) { + if (pkt->questions == 1) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: request from legacy querier\r\n")); + reply.legacy_query = 1; + reply.tx_id = pkt->tx_id; + reply.cache_flush = 0; + } + else { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: ignore query if (src UDP port != 5353) && (!= legacy query)\r\n")); + return; + } + } + else { + reply.cache_flush = 1; + } + + /* Delaying response. + * Always delay the response, unicast or multicast, except when: + * - Answering to a single question with a unique answer (RFC6762 section 6) + * - Answering to a probe query via unicast (RFC6762 section 6) + * + * unique answer? -> not if it includes service type or name ptr's + */ + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + shared_answer |= (reply.serv_replies[i] & + (REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR)); + } + if (((pkt->questions == 1) && (!shared_answer)) || reply.probe_query_recv) { + delay_response = 0; + } + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: response %s delayed\r\n", (delay_response ? "randomly" : "not"))); + + /* Unicast / multicast response: + * Answering to (m)DNS querier via unicast response. + * When: + * a) Unicast reply requested && recently multicasted 1/4ttl (RFC6762 section 5.4) + * b) Direct unicast query to port 5353 (RFC6762 section 5.5) + * c) Reply to Legacy DNS querier (RFC6762 section 6.7) + * d) A probe message is received (RFC6762 section 6) + */ + +#if LWIP_IPV6 + if ((IP_IS_V6_VAL(pkt->source_addr) && mdns->ipv6.multicast_timeout_25TTL)) { + listen_to_QU_bit = 1; + } +#endif +#if LWIP_IPV4 + if ((IP_IS_V4_VAL(pkt->source_addr) && mdns->ipv4.multicast_timeout_25TTL)) { + listen_to_QU_bit = 1; + } +#endif + if ( (reply.unicast_reply_requested && listen_to_QU_bit) + || pkt->recv_unicast + || reply.legacy_query + || reply.probe_query_recv ) { + send_unicast = 1; + } + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: send response via %s\r\n", (send_unicast ? "unicast" : "multicast"))); + + /* Send out or put on waiting list */ + if (delay_response) { + if (send_unicast) { +#if LWIP_IPV6 + /* Add answers to IPv6 waiting list if: + * - it's a IPv6 incoming packet + * - no message is in it yet + */ + if (IP_IS_V6_VAL(pkt->source_addr) && !mdns->ipv6.unicast_msg_in_use) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: add answers to unicast IPv6 waiting list\r\n")); + SMEMCPY(&mdns->ipv6.delayed_msg_unicast.dest_addr, &pkt->source_addr, sizeof(ip_addr_t)); + mdns->ipv6.delayed_msg_unicast.dest_port = pkt->source_port; + + mdns_add_msg_to_delayed(&mdns->ipv6.delayed_msg_unicast, &reply); + + mdns_set_timeout(netif, MDNS_RESPONSE_DELAY, mdns_send_unicast_msg_delayed_ipv6, + &mdns->ipv6.unicast_msg_in_use); + } +#endif +#if LWIP_IPV4 + /* Add answers to IPv4 waiting list if: + * - it's a IPv4 incoming packet + * - no message is in it yet + */ + if (IP_IS_V4_VAL(pkt->source_addr) && !mdns->ipv4.unicast_msg_in_use) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: add answers to unicast IPv4 waiting list\r\n")); + SMEMCPY(&mdns->ipv4.delayed_msg_unicast.dest_addr, &pkt->source_addr, sizeof(ip_addr_t)); + mdns->ipv4.delayed_msg_unicast.dest_port = pkt->source_port; + + mdns_add_msg_to_delayed(&mdns->ipv4.delayed_msg_unicast, &reply); + + mdns_set_timeout(netif, MDNS_RESPONSE_DELAY, mdns_send_unicast_msg_delayed_ipv4, + &mdns->ipv4.unicast_msg_in_use); + } +#endif + } + else { +#if LWIP_IPV6 + /* Add answers to IPv6 waiting list if: + * - it's a IPv6 incoming packet + * - and the 1 second timeout is passed (RFC6762 section 6) + */ + if (IP_IS_V6_VAL(pkt->source_addr) && !mdns->ipv6.multicast_timeout) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: add answers to multicast IPv6 waiting list\r\n")); + + mdns_add_msg_to_delayed(&mdns->ipv6.delayed_msg_multicast, &reply); + + mdns_set_timeout(netif, MDNS_RESPONSE_DELAY, mdns_send_multicast_msg_delayed_ipv6, + &mdns->ipv6.multicast_msg_waiting); + } +#endif +#if LWIP_IPV4 + /* Add answers to IPv4 waiting list if: + * - it's a IPv4 incoming packet + * - and the 1 second timeout is passed (RFC6762 section 6) + */ + if (IP_IS_V4_VAL(pkt->source_addr) && !mdns->ipv4.multicast_timeout) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: add answers to multicast IPv4 waiting list\r\n")); + + mdns_add_msg_to_delayed(&mdns->ipv4.delayed_msg_multicast, &reply); + + mdns_set_timeout(netif, MDNS_RESPONSE_DELAY, mdns_send_multicast_msg_delayed_ipv4, + &mdns->ipv4.multicast_msg_waiting); + } +#endif + } + } + else { + if (send_unicast) { + /* Copy source IP/port to use when responding unicast */ + SMEMCPY(&reply.dest_addr, &pkt->source_addr, sizeof(ip_addr_t)); + reply.dest_port = pkt->source_port; + /* send answer directly via unicast */ + res = mdns_send_outpacket(&reply, netif); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Unicast answer could not be send\r\n")); + } + else { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Unicast answer send successfully\r\n")); + } + return; + } + else { + /* Set IP/port to use when responding multicast */ +#if LWIP_IPV6 + if (IP_IS_V6_VAL(pkt->source_addr)) { + if (mdns->ipv6.multicast_timeout) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: we just multicasted, ignore question\r\n")); + return; + } + SMEMCPY(&reply.dest_addr, &v6group, sizeof(ip_addr_t)); + } +#endif +#if LWIP_IPV4 + if (IP_IS_V4_VAL(pkt->source_addr)) { + if (mdns->ipv4.multicast_timeout) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: we just multicasted, ignore question\r\n")); + return; + } + SMEMCPY(&reply.dest_addr, &v4group, sizeof(ip_addr_t)); + } +#endif + reply.dest_port = LWIP_IANA_PORT_MDNS; + /* send answer directly via multicast */ + res = mdns_send_outpacket(&reply, netif); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Multicast answer could not be send\r\n")); + } + else { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Multicast answer send successfully\r\n")); +#if LWIP_IPV6 + if (IP_IS_V6_VAL(pkt->source_addr)) { + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv6, + &mdns->ipv6.multicast_timeout); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv6\n")); + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv6, + &mdns->ipv6.multicast_timeout_25TTL); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv6\n")); + } +#endif +#if LWIP_IPV4 + if (IP_IS_V4_VAL(pkt->source_addr)) { + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv4, + &mdns->ipv4.multicast_timeout); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv4\n")); + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv4, + &mdns->ipv4.multicast_timeout_25TTL); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv4\n")); + } +#endif + } + return; + } + } } /** @@ -746,7 +1098,7 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif) struct mdns_answer ans; err_t res; - res = mdns_read_answer(pkt, &ans); + res = mdns_read_answer(pkt, &ans, &pkt->answers_left); if (res != ERR_OK) { LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n")); return; @@ -831,7 +1183,9 @@ mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, packet.parse_offset = offset; packet.tx_id = lwip_ntohs(hdr.id); packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions); - packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr); + packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers); + packet.authoritative = packet.authoritative_left = lwip_ntohs(hdr.numauthrr); + packet.additional = packet.additional_left = lwip_ntohs(hdr.numextrarr); #if LWIP_IPV6 if (IP_IS_V6(ip_current_dest_addr())) { @@ -1006,6 +1360,19 @@ mdns_resp_add_netif(struct netif *netif, const char *hostname) mdns->probes_sent = 0; mdns->probing_state = MDNS_PROBING_NOT_STARTED; + /* Init delayed message structs with address and port */ +#if LWIP_IPV4 + mdns->ipv4.delayed_msg_multicast.dest_port = LWIP_IANA_PORT_MDNS; + SMEMCPY(&mdns->ipv4.delayed_msg_multicast.dest_addr, &v4group, + sizeof(ip_addr_t)); +#endif + +#if LWIP_IPV6 + mdns->ipv6.delayed_msg_multicast.dest_port = LWIP_IANA_PORT_MDNS; + SMEMCPY(&mdns->ipv6.delayed_msg_multicast.dest_addr, &v6group, + sizeof(ip_addr_t)); +#endif + /* Join multicast groups */ #if LWIP_IPV4 res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group)); @@ -1256,10 +1623,22 @@ mdns_resp_announce(struct netif *netif) /* Announce on IPv6 and IPv4 */ #if LWIP_IPV6 mdns_announce(netif, &v6group); + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv6, + &mdns->ipv6.multicast_timeout); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv6\n")); + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv6, + &mdns->ipv6.multicast_timeout_25TTL); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv6\n")); #endif #if LWIP_IPV4 if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) { mdns_announce(netif, &v4group); + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv4, + &mdns->ipv4.multicast_timeout); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv4\n")); + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv4, + &mdns->ipv4.multicast_timeout_25TTL); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv4\n")); } #endif } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */ diff --git a/src/apps/mdns/mdns_out.c b/src/apps/mdns/mdns_out.c index 351d2f7d..2b9cf328 100644 --- a/src/apps/mdns/mdns_out.c +++ b/src/apps/mdns/mdns_out.c @@ -54,6 +54,9 @@ /* Payload size allocated for each outgoing UDP packet */ #define OUTPACKET_SIZE 500 +/* Function prototypes */ +static void mdns_clear_outmsg(struct mdns_outmsg *outmsg); + /** * Call user supplied function to setup TXT data * @param service The service to build TXT record for @@ -740,8 +743,8 @@ mdns_send_outpacket(struct mdns_outmsg *msg, struct netif *netif) pbuf_realloc(outpkt.pbuf, outpkt.write_offset); /* Send created packet */ - LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", - outpkt.write_offset, msg->unicast_reply)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d\n", + outpkt.write_offset)); res = udp_sendto_if(get_mdns_pcb(), outpkt.pbuf, &msg->dest_addr, msg->dest_port, netif); } @@ -754,4 +757,235 @@ cleanup: return res; } +#if LWIP_IPV4 +/** + * Called by timeouts when timer is passed, allows multicast IPv4 traffic again. + * + * @param arg pointer to netif of timeout. + */ +void +mdns_multicast_timeout_reset_ipv4(void *arg) +{ + struct netif *netif = (struct netif*)arg; + struct mdns_host *mdns = netif_mdns_data(netif); + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout finished - IPv4\n")); + + mdns->ipv4.multicast_timeout = 0; +} + +/** + * Called by timeouts when timer is passed, allows to send an answer on a QU + * question via multicast. + * + * @param arg pointer to netif of timeout. + */ +void +mdns_multicast_timeout_25ttl_reset_ipv4(void *arg) +{ + struct netif *netif = (struct netif*)arg; + struct mdns_host *mdns = netif_mdns_data(netif); + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl finished - IPv4\n")); + + mdns->ipv4.multicast_timeout_25TTL = 0; +} + +/** + * Called by timeouts when timer is passed, sends out delayed multicast IPv4 response. + * + * @param arg pointer to netif of timeout. + */ +void +mdns_send_multicast_msg_delayed_ipv4(void *arg) +{ + struct netif *netif = (struct netif*)arg; + struct mdns_host *mdns = netif_mdns_data(netif); + err_t res; + + res = mdns_send_outpacket(&mdns->ipv4.delayed_msg_multicast, netif); + if(res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed multicast send failed - IPv4\n")); + } + else { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed multicast send successful - IPv4\n")); + mdns_clear_outmsg(&mdns->ipv4.delayed_msg_multicast); + mdns->ipv4.multicast_msg_waiting = 0; + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv4, + &mdns->ipv4.multicast_timeout); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv4\n")); + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv4, + &mdns->ipv4.multicast_timeout_25TTL); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv4\n")); + } +} + +/** + * Called by timeouts when timer is passed, sends out delayed unicast IPv4 response. + * + * @param arg pointer to netif of timeout. + */ +void +mdns_send_unicast_msg_delayed_ipv4(void *arg) +{ + struct netif *netif = (struct netif*)arg; + struct mdns_host *mdns = netif_mdns_data(netif); + err_t res; + + res = mdns_send_outpacket(&mdns->ipv4.delayed_msg_unicast, netif); + if(res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed unicast send failed - IPv4\n")); + } + else { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed unicast send successful - IPv4\n")); + mdns_clear_outmsg(&mdns->ipv4.delayed_msg_unicast); + mdns->ipv4.unicast_msg_in_use = 0; + } +} + +#endif + +#if LWIP_IPV6 +/** + * Called by timeouts when timer is passed, allows multicast IPv6 traffic again. + * + * @param arg pointer to netif of timeout. + */ +void +mdns_multicast_timeout_reset_ipv6(void *arg) +{ + struct netif *netif = (struct netif*)arg; + struct mdns_host *mdns = netif_mdns_data(netif); + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout finished - IPv6\n")); + + mdns->ipv6.multicast_timeout = 0; +} + +/** + * Called by timeouts when timer is passed, allows to send an answer on a QU + * question via multicast. + * + * @param arg pointer to netif of timeout. + */ +void +mdns_multicast_timeout_25ttl_reset_ipv6(void *arg) +{ + struct netif *netif = (struct netif*)arg; + struct mdns_host *mdns = netif_mdns_data(netif); + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl finished - IPv6\n")); + + mdns->ipv6.multicast_timeout_25TTL = 0; +} + +/** + * Called by timeouts when timer is passed, sends out delayed multicast IPv6 response. + * + * @param arg pointer to netif of timeout. + */ +void +mdns_send_multicast_msg_delayed_ipv6(void *arg) +{ + struct netif *netif = (struct netif*)arg; + struct mdns_host *mdns = netif_mdns_data(netif); + err_t res; + + res = mdns_send_outpacket(&mdns->ipv6.delayed_msg_multicast, netif); + if(res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed multicast send failed - IPv6\n")); + } + else { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed multicast send successful - IPv6\n")); + mdns_clear_outmsg(&mdns->ipv6.delayed_msg_multicast); + mdns->ipv6.multicast_msg_waiting = 0; + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT, mdns_multicast_timeout_reset_ipv6, + &mdns->ipv6.multicast_timeout); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout started - IPv6\n")); + mdns_set_timeout(netif, MDNS_MULTICAST_TIMEOUT_25TTL, mdns_multicast_timeout_25ttl_reset_ipv6, + &mdns->ipv6.multicast_timeout_25TTL); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: multicast timeout 1/4 of ttl started - IPv6\n")); + } +} + +/** + * Called by timeouts when timer is passed, sends out delayed unicast IPv6 response. + * + * @param arg pointer to netif of timeout. + */ +void +mdns_send_unicast_msg_delayed_ipv6(void *arg) +{ + struct netif *netif = (struct netif*)arg; + struct mdns_host *mdns = netif_mdns_data(netif); + err_t res; + + res = mdns_send_outpacket(&mdns->ipv6.delayed_msg_unicast, netif); + if(res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed unicast send failed - IPv6\n")); + } + else { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Delayed unicast send successful - IPv6\n")); + mdns_clear_outmsg(&mdns->ipv6.delayed_msg_unicast); + mdns->ipv6.unicast_msg_in_use = 0; + } +} + +#endif + +/** + * This function clears the output message without changing the destination + * address or port. This is useful for clearing the delayed msg structs without + * loosing the set IP. + * + * @param outmsg pointer to output message to clear. + */ +static void +mdns_clear_outmsg(struct mdns_outmsg *outmsg) +{ + int i; + + outmsg->tx_id = 0; + outmsg->flags = 0; + outmsg->cache_flush = 0; + outmsg->unicast_reply_requested = 0; + outmsg->legacy_query = 0; + outmsg->probe_query_recv = 0; + outmsg->host_questions = 0; + outmsg->host_replies = 0; + outmsg->host_reverse_v6_replies = 0; + + for(i = 0; i < MDNS_MAX_SERVICES; i++) { + outmsg->serv_questions[i] = 0; + outmsg->serv_replies[i] = 0; + } +} + +/** + * Sets a timer that calls the handler when finished. + * Depending on the busy_flag the timer is restarted or started. The flag is + * set before return. Sys_timeout does not give us this functionality. + * + * @param netif Network interface info + * @param msecs Time value to set + * @param handler Callback function to call + * @param busy_flag Pointer to flag that displays if the timer is running or not. + */ +void +mdns_set_timeout(struct netif *netif, u32_t msecs, sys_timeout_handler handler, + u8_t *busy_flag) +{ + if(*busy_flag) { + /* restart timer */ + sys_untimeout(handler, netif); + sys_timeout(msecs, handler, netif); + } + else { + /* start timer */ + sys_timeout(msecs, handler, netif); + } + /* Now we have a timer running */ + *busy_flag = 1; +} + #endif /* LWIP_MDNS_RESPONDER */ diff --git a/src/include/lwip/apps/mdns_domain.h b/src/include/lwip/apps/mdns_domain.h index a633fefc..941ce63c 100644 --- a/src/include/lwip/apps/mdns_domain.h +++ b/src/include/lwip/apps/mdns_domain.h @@ -54,8 +54,12 @@ err_t mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t u16_t mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain); void mdns_domain_debug_print(struct mdns_domain *domain); int mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b); +#if LWIP_IPV4 err_t mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr); +#endif +#if LWIP_IPV6 err_t mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr); +#endif err_t mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns); err_t mdns_build_dnssd_domain(struct mdns_domain *domain); err_t mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name); diff --git a/src/include/lwip/apps/mdns_out.h b/src/include/lwip/apps/mdns_out.h index 2f46fa6f..f36eeeda 100644 --- a/src/include/lwip/apps/mdns_out.h +++ b/src/include/lwip/apps/mdns_out.h @@ -42,6 +42,7 @@ #include "lwip/apps/mdns_opts.h" #include "lwip/apps/mdns_priv.h" #include "lwip/netif.h" +#include "lwip/timeouts.h" #ifdef __cplusplus extern "C" { @@ -73,7 +74,42 @@ extern "C" { /* Lookup for text info on service instance */ #define REPLY_SERVICE_TXT 0x80 +/* RFC6762 section 6: + * To protect the network against excessive packet flooding due to software bugs + * or malicious attack, a Multicast DNS responder MUST NOT (except in the one + * special case of answering probe queries) multicast a record on a given + * interface until at least one second has elapsed since the last time that + * record was multicast on that particular interface. + */ +#define MDNS_MULTICAST_TIMEOUT 1000 + +/* RFC6762 section 5.4: + * When receiving a question with the unicast-response bit set, a responder + * SHOULD usually respond with a unicast packet directed back to the querier. + * However, if the responder has not multicast that record recently (within one + * quarter of its TTL), then the responder SHOULD instead multicast the response + * so as to keep all the peer caches up to date, and to permit passive conflict + * detection. + * -> we implement a stripped down version. Depending on a timeout of 30s + * (25% of 120s) all QU questions are send via multicast or unicast. + */ +#define MDNS_MULTICAST_TIMEOUT_25TTL 30000 + err_t mdns_send_outpacket(struct mdns_outmsg *msg, struct netif *netif); +void mdns_set_timeout(struct netif *netif, u32_t msecs, + sys_timeout_handler handler, u8_t *busy_flag); +#if LWIP_IPV4 +void mdns_multicast_timeout_reset_ipv4(void *arg); +void mdns_multicast_timeout_25ttl_reset_ipv4(void *arg); +void mdns_send_multicast_msg_delayed_ipv4(void *arg); +void mdns_send_unicast_msg_delayed_ipv4(void *arg); +#endif +#if LWIP_IPV6 +void mdns_multicast_timeout_reset_ipv6(void *arg); +void mdns_multicast_timeout_25ttl_reset_ipv6(void *arg); +void mdns_send_multicast_msg_delayed_ipv6(void *arg); +void mdns_send_unicast_msg_delayed_ipv6(void *arg); +#endif void mdns_prepare_txtdata(struct mdns_service *service); #endif /* LWIP_MDNS_RESPONDER */ diff --git a/src/include/lwip/apps/mdns_priv.h b/src/include/lwip/apps/mdns_priv.h index 984aa474..c9784466 100644 --- a/src/include/lwip/apps/mdns_priv.h +++ b/src/include/lwip/apps/mdns_priv.h @@ -92,18 +92,6 @@ struct mdns_service { u16_t port; }; -/** Description of a host/netif */ -struct mdns_host { - /** Hostname */ - char name[MDNS_LABEL_MAXLEN + 1]; - /** Pointer to services */ - struct mdns_service *services[MDNS_MAX_SERVICES]; - /** Number of probes sent for the current name */ - u8_t probes_sent; - /** State in probing sequence */ - u8_t probing_state; -}; - /** mDNS output packet */ struct mdns_outpacket { /** Packet data */ @@ -134,11 +122,14 @@ struct mdns_outmsg { u16_t dest_port; /** If all answers in packet should set cache_flush bit */ u8_t cache_flush; - /** If reply should be sent unicast */ - u8_t unicast_reply; + /** If reply should be sent unicast (as requested) */ + u8_t unicast_reply_requested; /** If legacy query. (tx_id needed, and write * question again in reply before answer) */ u8_t legacy_query; + /** If the query is a probe msg we need to respond immediatly. Independent of + * the QU or QM flag. */ + u8_t probe_query_recv; /* Question bitmask for host information */ u8_t host_questions; /* Questions bitmask per service */ @@ -151,6 +142,43 @@ struct mdns_outmsg { u8_t serv_replies[MDNS_MAX_SERVICES]; }; +/** Delayed msg info */ +struct mdns_delayed_msg { + /** Timer state multicast */ + u8_t multicast_msg_waiting; + /** Multicast timeout on */ + u8_t multicast_timeout; + /** Output msg used for delayed multicast responses */ + struct mdns_outmsg delayed_msg_multicast; + /** Prefer multicast over unicast timeout -> 25% of TTL = we take 30s as + general delay. */ + u8_t multicast_timeout_25TTL; + /** Only send out new unicast message if previous was send */ + u8_t unicast_msg_in_use; + /** Output msg used for delayed unicast responses */ + struct mdns_outmsg delayed_msg_unicast; +}; + +/** Description of a host/netif */ +struct mdns_host { + /** Hostname */ + char name[MDNS_LABEL_MAXLEN + 1]; + /** Pointer to services */ + struct mdns_service *services[MDNS_MAX_SERVICES]; + /** Number of probes sent for the current name */ + u8_t probes_sent; + /** State in probing sequence */ + u8_t probing_state; +#if LWIP_IPV4 + /** delayed msg struct for IPv4 */ + struct mdns_delayed_msg ipv4; +#endif +#if LWIP_IPV6 + /** delayed msg struct for IPv6 */ + struct mdns_delayed_msg ipv6; +#endif +}; + #endif /* LWIP_MDNS_RESPONDER */ #ifdef __cplusplus