• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  * MDNS responder implementation
4  *
5  * @defgroup mdns MDNS
6  * @ingroup callbackstyle_api
7  *
8  * RFC 6762 - Multicast DNS\n
9  * RFC 6763 - DNS-Based Service Discovery\n
10  *
11  * @verbinclude mdns.txt
12  *
13  * Things left to implement:
14  * -------------------------
15  *
16  * - Tiebreaking for simultaneous probing
17  * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
18  * - Checking that source address of unicast requests are on the same network
19  * - Limiting multicast responses to 1 per second per resource record
20  * - Fragmenting replies if required
21  * - Handling multi-packet known answers
22  * - Individual known answer detection for all local IPv6 addresses
23  * - Dynamic size of outgoing packet
24  */
25 
26 /*
27  * Copyright (c) 2015 Verisure Innovation AB
28  * All rights reserved.
29  *
30  * Redistribution and use in source and binary forms, with or without modification,
31  * are permitted provided that the following conditions are met:
32  *
33  * 1. Redistributions of source code must retain the above copyright notice,
34  *    this list of conditions and the following disclaimer.
35  * 2. Redistributions in binary form must reproduce the above copyright notice,
36  *    this list of conditions and the following disclaimer in the documentation
37  *    and/or other materials provided with the distribution.
38  * 3. The name of the author may not be used to endorse or promote products
39  *    derived from this software without specific prior written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
43  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
44  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
46  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
49  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
50  * OF SUCH DAMAGE.
51  *
52  * This file is part of the lwIP TCP/IP stack.
53  *
54  * Author: Erik Ekman <erik@kryo.se>
55  *
56  */
57 
58 #include "lwip/mdns.h"
59 #include "lwip/mdns_priv.h"
60 #include "lwip/netif.h"
61 #include "lwip/udp.h"
62 #include "lwip/ip_addr.h"
63 #include "lwip/mem.h"
64 #include "lwip/prot/dns.h"
65 #include "lwip/prot/iana.h"
66 #include "lwip/timeouts.h"
67 
68 #include <string.h>
69 
70 #if LWIP_MDNS_RESPONDER
71 
72 #if (LWIP_IPV4 && !LWIP_IGMP)
73 #error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h"
74 #endif
75 #if (LWIP_IPV6 && !LWIP_IPV6_MLD)
76 #error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h"
77 #endif
78 #if (!LWIP_UDP)
79 #error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h"
80 #endif
81 
82 #if LWIP_IPV4
83 #include "lwip/igmp.h"
84 /* IPv4 multicast group 224.0.0.251 */
85 static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
86 #endif
87 
88 #if LWIP_IPV6
89 #include "lwip/mld6.h"
90 /* IPv6 multicast group FF02::FB */
91 static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
92 #endif
93 
94 #define MDNS_TTL  255
95 
96 /* Stored offsets to beginning of domain names
97  * Used for compression.
98  */
99 #define NUM_DOMAIN_OFFSETS 10
100 #define DOMAIN_JUMP_SIZE 2
101 #define DOMAIN_JUMP 0xc000
102 
103 static u8_t mdns_netif_client_id = NETIF_CLIENT_DATA_INVALID_INDEX;
104 static struct udp_pcb *mdns_pcb;
105 #if MDNS_RESP_USENETIF_EXTCALLBACK
NETIF_DECLARE_EXT_CALLBACK(netif_callback)106 NETIF_DECLARE_EXT_CALLBACK(netif_callback)
107 #endif
108 static mdns_name_result_cb_t mdns_name_result_cb;
109 
110 #define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
111 
112 #define TOPDOMAIN_LOCAL "local"
113 
114 #define REVERSE_PTR_TOPDOMAIN "arpa"
115 #define REVERSE_PTR_V4_DOMAIN "in-addr"
116 #define REVERSE_PTR_V6_DOMAIN "ip6"
117 
118 #define SRV_PRIORITY 0
119 #define SRV_WEIGHT   0
120 
121 /* Payload size allocated for each outgoing UDP packet */
122 #define OUTPACKET_SIZE 500
123 
124 /* Lookup from hostname -> IPv4 */
125 #define REPLY_HOST_A            0x01
126 /* Lookup from IPv4/v6 -> hostname */
127 #define REPLY_HOST_PTR_V4       0x02
128 /* Lookup from hostname -> IPv6 */
129 #define REPLY_HOST_AAAA         0x04
130 /* Lookup from hostname -> IPv6 */
131 #define REPLY_HOST_PTR_V6       0x08
132 
133 /* Lookup for service types */
134 #define REPLY_SERVICE_TYPE_PTR  0x10
135 /* Lookup for instances of service */
136 #define REPLY_SERVICE_NAME_PTR  0x20
137 /* Lookup for location of service instance */
138 #define REPLY_SERVICE_SRV       0x40
139 /* Lookup for text info on service instance */
140 #define REPLY_SERVICE_TXT       0x80
141 
142 #define MDNS_PROBE_DELAY_MS       250
143 #define MDNS_PROBE_COUNT          3
144 #ifdef LWIP_RAND
145 /* first probe timeout SHOULD be random 0-250 ms*/
146 #define MDNS_INITIAL_PROBE_DELAY_MS (LWIP_RAND() % MDNS_PROBE_DELAY_MS)
147 #else
148 #define MDNS_INITIAL_PROBE_DELAY_MS MDNS_PROBE_DELAY_MS
149 #endif
150 
151 #define MDNS_PROBING_NOT_STARTED  0
152 #define MDNS_PROBING_ONGOING      1
153 #define MDNS_PROBING_COMPLETE     2
154 
155 static const char *dnssd_protos[] = {
156   "_udp", /* DNSSD_PROTO_UDP */
157   "_tcp", /* DNSSD_PROTO_TCP */
158 };
159 
160 /** Description of a service */
161 struct mdns_service {
162   /** TXT record to answer with */
163   struct mdns_domain txtdata;
164   /** Name of service, like 'myweb' */
165   char name[MDNS_LABEL_MAXLEN + 1];
166   /** Type of service, like '_http' */
167   char service[MDNS_LABEL_MAXLEN + 1];
168   /** Callback function and userdata
169    * to update txtdata buffer */
170   service_get_txt_fn_t txt_fn;
171   void *txt_userdata;
172   /** TTL in seconds of SRV/TXT replies */
173   u32_t dns_ttl;
174   /** Protocol, TCP or UDP */
175   u16_t proto;
176   /** Port of the service */
177   u16_t port;
178 };
179 
180 /** Description of a host/netif */
181 struct mdns_host {
182   /** Hostname */
183   char name[MDNS_LABEL_MAXLEN + 1];
184   /** Pointer to services */
185   struct mdns_service *services[MDNS_MAX_SERVICES];
186   /** TTL in seconds of A/AAAA/PTR replies */
187   u32_t dns_ttl;
188   /** Number of probes sent for the current name */
189   u8_t probes_sent;
190   /** State in probing sequence */
191   u8_t probing_state;
192 };
193 
194 /** Information about received packet */
195 struct mdns_packet {
196   /** Sender IP/port */
197   ip_addr_t source_addr;
198   u16_t source_port;
199   /** If packet was received unicast */
200   u16_t recv_unicast;
201   /** Netif that received the packet */
202   struct netif *netif;
203   /** Packet data */
204   struct pbuf *pbuf;
205   /** Current parsing offset in packet */
206   u16_t parse_offset;
207   /** Identifier. Used in legacy queries */
208   u16_t tx_id;
209   /** Number of questions in packet,
210    *  read from packet header */
211   u16_t questions;
212   /** Number of unparsed questions */
213   u16_t questions_left;
214   /** Number of answers in packet,
215    *  (sum of normal, authoritative and additional answers)
216    *  read from packet header */
217   u16_t answers;
218   /** Number of unparsed answers */
219   u16_t answers_left;
220 };
221 
222 /** Information about outgoing packet */
223 struct mdns_outpacket {
224   /** Netif to send the packet on */
225   struct netif *netif;
226   /** Packet data */
227   struct pbuf *pbuf;
228   /** Current write offset in packet */
229   u16_t write_offset;
230   /** Identifier. Used in legacy queries */
231   u16_t tx_id;
232   /** Destination IP/port if sent unicast */
233   ip_addr_t dest_addr;
234   u16_t dest_port;
235   /** Number of questions written */
236   u16_t questions;
237   /** Number of normal answers written */
238   u16_t answers;
239   /** Number of authoritative answers written */
240   u16_t authoritative;
241   /** Number of additional answers written */
242   u16_t additional;
243   /** Offsets for written domain names in packet.
244    *  Used for compression */
245   u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
246   /** If all answers in packet should set cache_flush bit */
247   u8_t cache_flush;
248   /** If reply should be sent unicast */
249   u8_t unicast_reply;
250   /** If legacy query. (tx_id needed, and write
251    *  question again in reply before answer) */
252   u8_t legacy_query;
253   /* Reply bitmask for host information */
254   u8_t host_replies;
255   /* Bitmask for which reverse IPv6 hosts to answer */
256   u8_t host_reverse_v6_replies;
257   /* Reply bitmask per service */
258   u8_t serv_replies[MDNS_MAX_SERVICES];
259 };
260 
261 /** Domain, type and class.
262  *  Shared between questions and answers */
263 struct mdns_rr_info {
264   struct mdns_domain domain;
265   u16_t type;
266   u16_t klass;
267 };
268 
269 struct mdns_question {
270   struct mdns_rr_info info;
271   /** unicast reply requested */
272   u16_t unicast;
273 };
274 
275 struct mdns_answer {
276   struct mdns_rr_info info;
277   /** cache flush command bit */
278   u16_t cache_flush;
279   /* Validity time in seconds */
280   u32_t ttl;
281   /** Length of variable answer */
282   u16_t rd_length;
283   /** Offset of start of variable answer in packet */
284   u16_t rd_offset;
285 };
286 
287 static err_t mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags);
288 static void mdns_probe(void* arg);
289 
290 static err_t
mdns_domain_add_label_base(struct mdns_domain * domain,u8_t len)291 mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
292 {
293   if (len > MDNS_LABEL_MAXLEN) {
294     return ERR_VAL;
295   }
296   if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
297     return ERR_VAL;
298   }
299   /* Allow only zero marker on last byte */
300   if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
301     return ERR_VAL;
302   }
303   domain->name[domain->length] = len;
304   domain->length++;
305   return ERR_OK;
306 }
307 
308 /**
309  * Add a label part to a domain
310  * @param domain The domain to add a label to
311  * @param label The label to add, like &lt;hostname&gt;, 'local', 'com' or ''
312  * @param len The length of the label
313  * @return ERR_OK on success, an err_t otherwise if label too long
314  */
315 err_t
mdns_domain_add_label(struct mdns_domain * domain,const char * label,u8_t len)316 mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
317 {
318   err_t err = mdns_domain_add_label_base(domain, len);
319   if (err != ERR_OK) {
320     return err;
321   }
322   if (len) {
323     MEMCPY(&domain->name[domain->length], label, len);
324     domain->length += len;
325   }
326   return ERR_OK;
327 }
328 
329 /**
330  * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf)
331  */
332 static err_t
mdns_domain_add_label_pbuf(struct mdns_domain * domain,const struct pbuf * p,u16_t offset,u8_t len)333 mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len)
334 {
335   err_t err = mdns_domain_add_label_base(domain, len);
336   if (err != ERR_OK) {
337     return err;
338   }
339   if (len) {
340     if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) {
341       /* take back the ++ done before */
342       domain->length--;
343       return ERR_ARG;
344     }
345     domain->length += len;
346   }
347   return ERR_OK;
348 }
349 
350 /**
351  * Internal readname function with max 6 levels of recursion following jumps
352  * while decompressing name
353  */
354 static u16_t
mdns_readname_loop(struct pbuf * p,u16_t offset,struct mdns_domain * domain,unsigned depth)355 mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth)
356 {
357   u8_t c;
358 
359   do {
360     if (depth > 5) {
361       /* Too many jumps */
362       return MDNS_READNAME_ERROR;
363     }
364 
365     c = pbuf_get_at(p, offset);
366     offset++;
367 
368     /* is this a compressed label? */
369     if ((c & 0xc0) == 0xc0) {
370       u16_t jumpaddr;
371       if (offset >= p->tot_len) {
372         /* Make sure both jump bytes fit in the packet */
373         return MDNS_READNAME_ERROR;
374       }
375       jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff));
376       offset++;
377       if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) {
378         u16_t res;
379         /* Recursive call, maximum depth will be checked */
380         res = mdns_readname_loop(p, jumpaddr, domain, depth + 1);
381         /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */
382         if (res == MDNS_READNAME_ERROR) {
383           return res;
384         }
385       } else {
386         return MDNS_READNAME_ERROR;
387       }
388       break;
389     }
390 
391     /* normal label */
392     if (c <= MDNS_LABEL_MAXLEN) {
393       err_t res;
394 
395       if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
396         return MDNS_READNAME_ERROR;
397       }
398       res = mdns_domain_add_label_pbuf(domain, p, offset, c);
399       if (res != ERR_OK) {
400         return MDNS_READNAME_ERROR;
401       }
402       offset += c;
403     } else {
404       /* bad length byte */
405       return MDNS_READNAME_ERROR;
406     }
407   } while (c != 0);
408 
409   return offset;
410 }
411 
412 /**
413  * Read possibly compressed domain name from packet buffer
414  * @param p The packet
415  * @param offset start position of domain name in packet
416  * @param domain The domain name destination
417  * @return The new offset after the domain, or MDNS_READNAME_ERROR
418  *         if reading failed
419  */
420 u16_t
mdns_readname(struct pbuf * p,u16_t offset,struct mdns_domain * domain)421 mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain)
422 {
423   memset(domain, 0, sizeof(struct mdns_domain));
424   return mdns_readname_loop(p, offset, domain, 0);
425 }
426 
427 /**
428  * Print domain name to debug output
429  * @param domain The domain name
430  */
431 static void
mdns_domain_debug_print(struct mdns_domain * domain)432 mdns_domain_debug_print(struct mdns_domain *domain)
433 {
434   u8_t *src = domain->name;
435   u8_t i;
436 
437   while (*src) {
438     u8_t label_len = *src;
439     src++;
440     for (i = 0; i < label_len; i++) {
441       LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i]));
442     }
443     src += label_len;
444     LWIP_DEBUGF(MDNS_DEBUG, ("."));
445   }
446 }
447 
448 /**
449  * Return 1 if contents of domains match (case-insensitive)
450  * @param a Domain name to compare 1
451  * @param b Domain name to compare 2
452  * @return 1 if domains are equal ignoring case, 0 otherwise
453  */
454 int
mdns_domain_eq(struct mdns_domain * a,struct mdns_domain * b)455 mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b)
456 {
457   u8_t *ptra, *ptrb;
458   u8_t len;
459   int res;
460 
461   if (a->length != b->length) {
462     return 0;
463   }
464 
465   ptra = a->name;
466   ptrb = b->name;
467   while (*ptra && *ptrb && ptra < &a->name[a->length]) {
468     if (*ptra != *ptrb) {
469       return 0;
470     }
471     len = *ptra;
472     ptra++;
473     ptrb++;
474     res = lwip_strnicmp((char *) ptra, (char *) ptrb, len);
475     if (res != 0) {
476       return 0;
477     }
478     ptra += len;
479     ptrb += len;
480   }
481   if (*ptra != *ptrb && ptra < &a->name[a->length]) {
482     return 0;
483   }
484   return 1;
485 }
486 
487 /**
488  * Call user supplied function to setup TXT data
489  * @param service The service to build TXT record for
490  */
491 static void
mdns_prepare_txtdata(struct mdns_service * service)492 mdns_prepare_txtdata(struct mdns_service *service)
493 {
494   memset(&service->txtdata, 0, sizeof(struct mdns_domain));
495   if (service->txt_fn) {
496     service->txt_fn(service, service->txt_userdata);
497   }
498 }
499 
500 #if LWIP_IPV4
501 /**
502  * Build domain for reverse lookup of IPv4 address
503  * like 12.0.168.192.in-addr.arpa. for 192.168.0.12
504  * @param domain Where to write the domain name
505  * @param addr Pointer to an IPv4 address to encode
506  * @return ERR_OK if domain was written, an err_t otherwise
507  */
508 static err_t
mdns_build_reverse_v4_domain(struct mdns_domain * domain,const ip4_addr_t * addr)509 mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr)
510 {
511   int i;
512   err_t res;
513   const u8_t *ptr;
514 
515   LWIP_UNUSED_ARG(res);
516   if (!domain || !addr) {
517     return ERR_ARG;
518   }
519   memset(domain, 0, sizeof(struct mdns_domain));
520   ptr = (const u8_t *) addr;
521   for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) {
522     char buf[4];
523     u8_t val = ptr[i];
524 
525     lwip_itoa(buf, sizeof(buf), val);
526     res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf));
527     LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
528   }
529   res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN) - 1));
530   LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
531   res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
532   LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
533   res = mdns_domain_add_label(domain, NULL, 0);
534   LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
535 
536   return ERR_OK;
537 }
538 #endif
539 
540 #if LWIP_IPV6
541 /**
542  * Build domain for reverse lookup of IP address
543  * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab
544  * @param domain Where to write the domain name
545  * @param addr Pointer to an IPv6 address to encode
546  * @return ERR_OK if domain was written, an err_t otherwise
547  */
548 static err_t
mdns_build_reverse_v6_domain(struct mdns_domain * domain,const ip6_addr_t * addr)549 mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
550 {
551   int i;
552   err_t res;
553   const u8_t *ptr;
554   LWIP_UNUSED_ARG(res);
555   if (!domain || !addr) {
556     return ERR_ARG;
557   }
558   memset(domain, 0, sizeof(struct mdns_domain));
559   ptr = (const u8_t *) addr;
560   for (i = sizeof(ip6_addr_p_t) - 1; i >= 0; i--) {
561     char buf;
562     u8_t byte = ptr[i];
563     int j;
564     for (j = 0; j < 2; j++) {
565       if ((byte & 0x0F) < 0xA) {
566         buf = '0' + (byte & 0x0F);
567       } else {
568         buf = 'a' + (byte & 0x0F) - 0xA;
569       }
570       res = mdns_domain_add_label(domain, &buf, sizeof(buf));
571       LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
572       byte >>= 4;
573     }
574   }
575   res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN) - 1));
576   LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
577   res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
578   LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
579   res = mdns_domain_add_label(domain, NULL, 0);
580   LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
581 
582   return ERR_OK;
583 }
584 #endif
585 
586 /* Add .local. to domain */
587 static err_t
mdns_add_dotlocal(struct mdns_domain * domain)588 mdns_add_dotlocal(struct mdns_domain *domain)
589 {
590   err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL) - 1));
591   LWIP_UNUSED_ARG(res);
592   LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res);
593   return mdns_domain_add_label(domain, NULL, 0);
594 }
595 
596 /**
597  * Build the <hostname>.local. domain name
598  * @param domain Where to write the domain name
599  * @param mdns TMDNS netif descriptor.
600  * @return ERR_OK if domain <hostname>.local. was written, an err_t otherwise
601  */
602 static err_t
mdns_build_host_domain(struct mdns_domain * domain,struct mdns_host * mdns)603 mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
604 {
605   err_t res;
606   LWIP_UNUSED_ARG(res);
607   memset(domain, 0, sizeof(struct mdns_domain));
608   LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
609   res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
610   LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
611   return mdns_add_dotlocal(domain);
612 }
613 
614 /**
615  * Build the lookup-all-services special DNS-SD domain name
616  * @param domain Where to write the domain name
617  * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise
618  */
619 static err_t
mdns_build_dnssd_domain(struct mdns_domain * domain)620 mdns_build_dnssd_domain(struct mdns_domain *domain)
621 {
622   err_t res;
623   LWIP_UNUSED_ARG(res);
624   memset(domain, 0, sizeof(struct mdns_domain));
625   res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services") - 1));
626   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
627   res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd") - 1));
628   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
629   res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
630   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
631   return mdns_add_dotlocal(domain);
632 }
633 
634 /**
635  * Build domain name for a service
636  * @param domain Where to write the domain name
637  * @param service The service struct, containing service name, type and protocol
638  * @param include_name Whether to include the service name in the domain
639  * @return ERR_OK if domain was written. If service name is included,
640  *         <name>.<type>.<proto>.local. will be written, otherwise <type>.<proto>.local.
641  *         An err_t is returned on error.
642  */
643 static err_t
mdns_build_service_domain(struct mdns_domain * domain,struct mdns_service * service,int include_name)644 mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name)
645 {
646   err_t res;
647   LWIP_UNUSED_ARG(res);
648   memset(domain, 0, sizeof(struct mdns_domain));
649   if (include_name) {
650     res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name));
651     LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
652   }
653   res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
654   LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
655   res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
656   LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
657   return mdns_add_dotlocal(domain);
658 }
659 
660 static u8_t
mdns_extract_label(const char * str,const char delim)661 mdns_extract_label(const char *str, const char delim)
662 {
663   /* Assert: no wrap-around because strlen(str) < MDNS_DOMAIN_MAXLEN */
664   u8_t len = 0;
665   while (*str != '\0') {
666     if (*str == delim) {
667       return len;
668     }
669     len++;
670     str++;
671   }
672   return len;
673 }
674 
675 /**
676  * Build domain name from a custom string
677  * @param domain Where to write the domain name
678  * @param name The custom domain name which is delimited by the '.' character.
679                Excessive delimiters (if any) will be ignored.
680  * @return ERR_OK if domain was written.
681  *         An err_t is returned on error.
682  */
683 static err_t
mdns_build_custom_domain(struct mdns_domain * domain,const char * name)684 mdns_build_custom_domain(struct mdns_domain *domain, const char *name)
685 {
686   const char *curr = NULL;
687   u8_t len;
688   err_t res;
689 
690   (void)memset(domain, 0, sizeof(struct mdns_domain));
691 
692   if (name == NULL || strlen(name) >= MDNS_DOMAIN_MAXLEN) {
693     return ERR_VAL;
694   }
695 
696   curr = name;
697   while (*curr != '\0') {
698     while (*curr == '.') {
699       /* ignore excessive delimiters */
700       curr++;
701     }
702     len = mdns_extract_label(curr, '.');
703     if (len == 0) {
704       break;
705     }
706     res = mdns_domain_add_label(domain, curr, len);
707     if (res != ERR_OK) {
708       return res;
709     }
710     curr = curr + len;
711   }
712   if (curr == name) {
713     /* nothing is ever added */
714     return ERR_VAL;
715   }
716 
717   res = mdns_domain_add_label(domain, NULL, 0);
718   if (res != ERR_OK) {
719     return res;
720   }
721 
722   mdns_domain_debug_print(domain);
723   return ERR_OK;
724 }
725 
726 /**
727  * Check which replies we should send for a host/netif based on question
728  * @param netif The network interface that received the question
729  * @param rr Domain/type/class from a question
730  * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for
731  *                         if reply bit has REPLY_HOST_PTR_V6 set
732  * @return Bitmask of which replies to send
733  */
734 static int
check_host(struct netif * netif,struct mdns_rr_info * rr,u8_t * reverse_v6_reply)735 check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
736 {
737   err_t res;
738   int replies = 0;
739   struct mdns_domain mydomain;
740 
741   LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
742 
743   if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
744     /* Invalid class */
745     return replies;
746   }
747 
748   /* Handle PTR for our addresses */
749   if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) {
750 #if LWIP_IPV6
751     int i;
752     for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
753       if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
754         res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i));
755         if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
756           replies |= REPLY_HOST_PTR_V6;
757           /* Mark which addresses where requested */
758           if (reverse_v6_reply) {
759             *reverse_v6_reply |= (1 << i);
760           }
761         }
762       }
763     }
764 #endif
765 #if LWIP_IPV4
766     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
767       res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif));
768       if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
769         replies |= REPLY_HOST_PTR_V4;
770       }
771     }
772 #endif
773   }
774 
775   res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
776   /* Handle requests for our hostname */
777   if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
778     /* TODO return NSEC if unsupported protocol requested */
779 #if LWIP_IPV4
780     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
781         && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) {
782       replies |= REPLY_HOST_A;
783     }
784 #endif
785 #if LWIP_IPV6
786     if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) {
787       replies |= REPLY_HOST_AAAA;
788     }
789 #endif
790   }
791 
792   return replies;
793 }
794 
795 /**
796  * Check which replies we should send for a service based on question
797  * @param service A registered MDNS service
798  * @param rr Domain/type/class from a question
799  * @return Bitmask of which replies to send
800  */
801 static int
check_service(struct mdns_service * service,struct mdns_rr_info * rr)802 check_service(struct mdns_service *service, struct mdns_rr_info *rr)
803 {
804   err_t res;
805   int replies = 0;
806   struct mdns_domain mydomain;
807 
808   if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
809     /* Invalid class */
810     return 0;
811   }
812 
813   res = mdns_build_dnssd_domain(&mydomain);
814   if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
815       (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
816     /* Request for all service types */
817     replies |= REPLY_SERVICE_TYPE_PTR;
818   }
819 
820   res = mdns_build_service_domain(&mydomain, service, 0);
821   if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
822       (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
823     /* Request for the instance of my service */
824     replies |= REPLY_SERVICE_NAME_PTR;
825   }
826 
827   res = mdns_build_service_domain(&mydomain, service, 1);
828   if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
829     /* Request for info about my service */
830     if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) {
831       replies |= REPLY_SERVICE_SRV;
832     }
833     if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) {
834       replies |= REPLY_SERVICE_TXT;
835     }
836   }
837 
838   return replies;
839 }
840 
841 /**
842  * Return bytes needed to write before jump for best result of compressing supplied domain
843  * against domain in outpacket starting at specified offset.
844  * If a match is found, offset is updated to where to jump to
845  * @param pbuf Pointer to pbuf with the partially constructed DNS packet
846  * @param offset Start position of a domain written earlier. If this location is suitable
847  *               for compression, the pointer is updated to where in the domain to jump to.
848  * @param domain The domain to write
849  * @return Number of bytes to write of the new domain before writing a jump to the offset.
850  *         If compression can not be done against this previous domain name, the full new
851  *         domain length is returned.
852  */
853 u16_t
mdns_compress_domain(struct pbuf * pbuf,u16_t * offset,struct mdns_domain * domain)854 mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
855 {
856   struct mdns_domain target;
857   u16_t target_end;
858   u8_t target_len;
859   u8_t writelen = 0;
860   u8_t *ptr;
861   if (pbuf == NULL) {
862     return domain->length;
863   }
864   target_end = mdns_readname(pbuf, *offset, &target);
865   if (target_end == MDNS_READNAME_ERROR) {
866     return domain->length;
867   }
868   target_len = (u8_t)(target_end - *offset);
869   ptr = domain->name;
870   while (writelen < domain->length) {
871     u8_t domainlen = (u8_t)(domain->length - writelen);
872     u8_t labellen;
873     if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
874       /* Compare domains if target is long enough, and we have enough left of the domain */
875       u8_t targetpos = (u8_t)(target.length - domainlen);
876       if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
877         /* We are checking at or beyond a jump in the original, stop looking */
878         break;
879       }
880       if (target.length >= domainlen &&
881           memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
882         *offset += targetpos;
883         return writelen;
884       }
885     }
886     /* Skip to next label in domain */
887     labellen = *ptr;
888     writelen += 1 + labellen;
889     ptr += 1 + labellen;
890   }
891   /* Nothing found */
892   return domain->length;
893 }
894 
895 /**
896  * Write domain to outpacket. Compression will be attempted,
897  * unless domain->skip_compression is set.
898  * @param outpkt The outpacket to write to
899  * @param domain The domain name to write
900  * @return ERR_OK on success, an err_t otherwise
901  */
902 static err_t
mdns_write_domain(struct mdns_outpacket * outpkt,struct mdns_domain * domain)903 mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
904 {
905   int i;
906   err_t res;
907   u16_t writelen = domain->length;
908   u16_t jump_offset = 0;
909   u16_t jump;
910 
911   if (!domain->skip_compression) {
912     for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
913       u16_t offset = outpkt->domain_offsets[i];
914       if (offset) {
915         u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
916         if (len < writelen) {
917           writelen = len;
918           jump_offset = offset;
919         }
920       }
921     }
922   }
923 
924   if (writelen) {
925     /* Write uncompressed part of name */
926     res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
927     if (res != ERR_OK) {
928       return res;
929     }
930 
931     /* Store offset of this new domain */
932     for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
933       if (outpkt->domain_offsets[i] == 0) {
934         outpkt->domain_offsets[i] = outpkt->write_offset;
935         break;
936       }
937     }
938 
939     outpkt->write_offset += writelen;
940   }
941   if (jump_offset) {
942     /* Write jump */
943     jump = lwip_htons(DOMAIN_JUMP | jump_offset);
944     res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
945     if (res != ERR_OK) {
946       return res;
947     }
948     outpkt->write_offset += DOMAIN_JUMP_SIZE;
949   }
950   return ERR_OK;
951 }
952 
953 /**
954  * Write a question to an outpacket
955  * A question contains domain, type and class. Since an answer also starts with these fields this function is also
956  * called from mdns_add_answer().
957  * @param outpkt The outpacket to write to
958  * @param domain The domain name the answer is for
959  * @param type The DNS type of the answer (like 'AAAA', 'SRV')
960  * @param klass The DNS type of the answer (like 'IN')
961  * @param unicast If highest bit in class should be set, to instruct the responder to
962  *                reply with a unicast packet
963  * @return ERR_OK on success, an err_t otherwise
964  */
965 static err_t
mdns_add_question(struct mdns_outpacket * outpkt,struct mdns_domain * domain,u16_t type,u16_t klass,u16_t unicast)966 mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast)
967 {
968   u16_t question_len;
969   u16_t field16;
970   err_t res;
971 
972   if (!outpkt->pbuf) {
973     /* If no pbuf is active, allocate one */
974     outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
975     if (!outpkt->pbuf) {
976       return ERR_MEM;
977     }
978     outpkt->write_offset = SIZEOF_DNS_HDR;
979   }
980 
981   /* Worst case calculation. Domain string might be compressed */
982   question_len = domain->length + sizeof(type) + sizeof(klass);
983   if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) {
984     /* No space */
985     return ERR_MEM;
986   }
987 
988   /* Write name */
989   res = mdns_write_domain(outpkt, domain);
990   if (res != ERR_OK) {
991     return res;
992   }
993 
994   /* Write type */
995   field16 = lwip_htons(type);
996   res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
997   if (res != ERR_OK) {
998     return res;
999   }
1000   outpkt->write_offset += sizeof(field16);
1001 
1002   /* Write class */
1003   if (unicast) {
1004     klass |= 0x8000;
1005   }
1006   field16 = lwip_htons(klass);
1007   res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
1008   if (res != ERR_OK) {
1009     return res;
1010   }
1011   outpkt->write_offset += sizeof(field16);
1012 
1013   return ERR_OK;
1014 }
1015 
1016 /**
1017  * Write answer to reply packet.
1018  * buf or answer_domain can be null. The rd_length written will be buf_length +
1019  * size of (compressed) domain. Most uses will need either buf or answer_domain,
1020  * special case is SRV that starts with 3 u16 and then a domain name.
1021  * @param reply The outpacket to write to
1022  * @param domain The domain name the answer is for
1023  * @param type The DNS type of the answer (like 'AAAA', 'SRV')
1024  * @param klass The DNS type of the answer (like 'IN')
1025  * @param cache_flush If highest bit in class should be set, to instruct receiver that
1026  *                    this reply replaces any earlier answer for this domain/type/class
1027  * @param ttl Validity time in seconds to send out for IP address data in DNS replies
1028  * @param buf Pointer to buffer of answer data
1029  * @param buf_length Length of variable data
1030  * @param answer_domain A domain to write after any buffer data as answer
1031  * @return ERR_OK on success, an err_t otherwise
1032  */
1033 static err_t
mdns_add_answer(struct mdns_outpacket * reply,struct mdns_domain * domain,u16_t type,u16_t klass,u16_t cache_flush,u32_t ttl,const u8_t * buf,size_t buf_length,struct mdns_domain * answer_domain)1034 mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush,
1035                 u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain)
1036 {
1037   u16_t answer_len;
1038   u16_t field16;
1039   u16_t rdlen_offset;
1040   u16_t answer_offset;
1041   u32_t field32;
1042   err_t res;
1043 
1044   if (!reply->pbuf) {
1045     /* If no pbuf is active, allocate one */
1046     reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
1047     if (!reply->pbuf) {
1048       return ERR_MEM;
1049     }
1050     reply->write_offset = SIZEOF_DNS_HDR;
1051   }
1052 
1053   /* Worst case calculation. Domain strings might be compressed */
1054   answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/;
1055   if (buf) {
1056     answer_len += (u16_t)buf_length;
1057   }
1058   if (answer_domain) {
1059     answer_len += answer_domain->length;
1060   }
1061   if (reply->write_offset + answer_len > reply->pbuf->tot_len) {
1062     /* No space */
1063     return ERR_MEM;
1064   }
1065 
1066   /* Answer starts with same data as question, then more fields */
1067   mdns_add_question(reply, domain, type, klass, cache_flush);
1068 
1069   /* Write TTL */
1070   field32 = lwip_htonl(ttl);
1071   res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset);
1072   if (res != ERR_OK) {
1073     return res;
1074   }
1075   reply->write_offset += sizeof(field32);
1076 
1077   /* Store offsets and skip forward to the data */
1078   rdlen_offset = reply->write_offset;
1079   reply->write_offset += sizeof(field16);
1080   answer_offset = reply->write_offset;
1081 
1082   if (buf) {
1083     /* Write static data */
1084     res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset);
1085     if (res != ERR_OK) {
1086       return res;
1087     }
1088     reply->write_offset += (u16_t)buf_length;
1089   }
1090 
1091   if (answer_domain) {
1092     /* Write name answer (compressed if possible) */
1093     res = mdns_write_domain(reply, answer_domain);
1094     if (res != ERR_OK) {
1095       return res;
1096     }
1097   }
1098 
1099   /* Write rd_length after when we know the answer size */
1100   field16 = lwip_htons(reply->write_offset - answer_offset);
1101   res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset);
1102 
1103   return res;
1104 }
1105 
1106 /**
1107  * Helper function for mdns_read_question/mdns_read_answer
1108  * Reads a domain, type and class from the packet
1109  * @param pkt The MDNS packet to read from. The parse_offset field will be
1110  *            incremented to point to the next unparsed byte.
1111  * @param info The struct to fill with domain, type and class
1112  * @return ERR_OK on success, an err_t otherwise
1113  */
1114 static err_t
mdns_read_rr_info(struct mdns_packet * pkt,struct mdns_rr_info * info)1115 mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info)
1116 {
1117   u16_t field16, copied;
1118   pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain);
1119   if (pkt->parse_offset == MDNS_READNAME_ERROR) {
1120     return ERR_VAL;
1121   }
1122 
1123   copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1124   if (copied != sizeof(field16)) {
1125     return ERR_VAL;
1126   }
1127   pkt->parse_offset += copied;
1128   info->type = lwip_ntohs(field16);
1129 
1130   copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1131   if (copied != sizeof(field16)) {
1132     return ERR_VAL;
1133   }
1134   pkt->parse_offset += copied;
1135   info->klass = lwip_ntohs(field16);
1136 
1137   return ERR_OK;
1138 }
1139 
1140 /**
1141  * Read a question from the packet.
1142  * All questions have to be read before the answers.
1143  * @param pkt The MDNS packet to read from. The questions_left field will be decremented
1144  *            and the parse_offset will be updated.
1145  * @param question The struct to fill with question data
1146  * @return ERR_OK on success, an err_t otherwise
1147  */
1148 static err_t
mdns_read_question(struct mdns_packet * pkt,struct mdns_question * question)1149 mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question)
1150 {
1151   /* Safety check */
1152   if (pkt->pbuf->tot_len < pkt->parse_offset) {
1153     return ERR_VAL;
1154   }
1155 
1156   if (pkt->questions_left) {
1157     err_t res;
1158     pkt->questions_left--;
1159 
1160     memset(question, 0, sizeof(struct mdns_question));
1161     res = mdns_read_rr_info(pkt, &question->info);
1162     if (res != ERR_OK) {
1163       return res;
1164     }
1165 
1166     /* Extract unicast flag from class field */
1167     question->unicast = question->info.klass & 0x8000;
1168     question->info.klass &= 0x7FFF;
1169 
1170     return ERR_OK;
1171   }
1172   return ERR_VAL;
1173 }
1174 
1175 /**
1176  * Read an answer from the packet
1177  * The variable length reply is not copied, its pbuf offset and length is stored instead.
1178  * @param pkt The MDNS packet to read. The answers_left field will be decremented and
1179  *            the parse_offset will be updated.
1180  * @param answer The struct to fill with answer data
1181  * @return ERR_OK on success, an err_t otherwise
1182  */
1183 static err_t
mdns_read_answer(struct mdns_packet * pkt,struct mdns_answer * answer)1184 mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer)
1185 {
1186   /* Read questions first */
1187   if (pkt->questions_left) {
1188     return ERR_VAL;
1189   }
1190 
1191   /* Safety check */
1192   if (pkt->pbuf->tot_len < pkt->parse_offset) {
1193     return ERR_VAL;
1194   }
1195 
1196   if (pkt->answers_left) {
1197     u16_t copied, field16;
1198     u32_t ttl;
1199     err_t res;
1200     pkt->answers_left--;
1201 
1202     memset(answer, 0, sizeof(struct mdns_answer));
1203     res = mdns_read_rr_info(pkt, &answer->info);
1204     if (res != ERR_OK) {
1205       return res;
1206     }
1207 
1208     /* Extract cache_flush flag from class field */
1209     answer->cache_flush = answer->info.klass & 0x8000;
1210     answer->info.klass &= 0x7FFF;
1211 
1212     copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset);
1213     if (copied != sizeof(ttl)) {
1214       return ERR_VAL;
1215     }
1216     pkt->parse_offset += copied;
1217     answer->ttl = lwip_ntohl(ttl);
1218 
1219     copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1220     if (copied != sizeof(field16)) {
1221       return ERR_VAL;
1222     }
1223     pkt->parse_offset += copied;
1224     answer->rd_length = lwip_ntohs(field16);
1225 
1226     answer->rd_offset = pkt->parse_offset;
1227     pkt->parse_offset += answer->rd_length;
1228 
1229     return ERR_OK;
1230   }
1231   return ERR_VAL;
1232 }
1233 
1234 #if LWIP_IPV4
1235 /** Write an IPv4 address (A) RR to outpacket */
1236 static err_t
mdns_add_a_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif)1237 mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
1238 {
1239   struct mdns_domain host;
1240   err_t res = mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1241   if (res != ERR_OK) {
1242     return res;
1243   }
1244   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
1245   return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL);
1246 }
1247 
1248 /** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */
1249 static err_t
mdns_add_hostv4_ptr_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif)1250 mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
1251 {
1252   struct mdns_domain host, revhost;
1253   err_t res = mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1254   if (res != ERR_OK) {
1255     return res;
1256   }
1257   mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
1258   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
1259   return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
1260 }
1261 #endif
1262 
1263 #if LWIP_IPV6
1264 /** Write an IPv6 address (AAAA) RR to outpacket */
1265 static err_t
mdns_add_aaaa_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif,int addrindex)1266 mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
1267 {
1268   struct mdns_domain host;
1269   err_t res = mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1270   if (res != ERR_OK) {
1271     return res;
1272   }
1273   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
1274   return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_p_t), NULL);
1275 }
1276 
1277 /** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
1278 static err_t
mdns_add_hostv6_ptr_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif,int addrindex)1279 mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
1280 {
1281   struct mdns_domain host, revhost;
1282   err_t res = mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1283   if (res != ERR_OK) {
1284     return res;
1285   }
1286   mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
1287   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
1288   return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
1289 }
1290 #endif
1291 
1292 /** Write an all-services -> servicetype PTR RR to outpacket */
1293 static err_t
mdns_add_servicetype_ptr_answer(struct mdns_outpacket * reply,struct mdns_service * service)1294 mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
1295 {
1296   struct mdns_domain service_type, service_dnssd;
1297   err_t res;
1298   res = mdns_build_service_domain(&service_type, service, 0);
1299   if (res != ERR_OK) {
1300     return res;
1301   }
1302   res = mdns_build_dnssd_domain(&service_dnssd);
1303   if (res != ERR_OK) {
1304     return res;
1305   }
1306   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n"));
1307   return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type);
1308 }
1309 
1310 /** Write a servicetype -> servicename PTR RR to outpacket */
1311 static err_t
mdns_add_servicename_ptr_answer(struct mdns_outpacket * reply,struct mdns_service * service)1312 mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
1313 {
1314   struct mdns_domain service_type, service_instance;
1315   err_t res;
1316   res = mdns_build_service_domain(&service_type, service, 0);
1317   if (res != ERR_OK) {
1318     return res;
1319   }
1320   res = mdns_build_service_domain(&service_instance, service, 1);
1321   if (res != ERR_OK) {
1322     return res;
1323   }
1324   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n"));
1325   return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance);
1326 }
1327 
1328 /** Write a SRV RR to outpacket */
1329 static err_t
mdns_add_srv_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct mdns_host * mdns,struct mdns_service * service)1330 mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service)
1331 {
1332   struct mdns_domain service_instance, srvhost;
1333   u16_t srvdata[3];
1334   err_t res;
1335   res = mdns_build_service_domain(&service_instance, service, 1);
1336   if (res != ERR_OK) {
1337     return res;
1338   }
1339   res = mdns_build_host_domain(&srvhost, mdns);
1340   if (res != ERR_OK) {
1341     return res;
1342   }
1343   if (reply->legacy_query) {
1344     /* RFC 6762 section 18.14:
1345      * In legacy unicast responses generated to answer legacy queries,
1346      * name compression MUST NOT be performed on SRV records.
1347      */
1348     srvhost.skip_compression = 1;
1349   }
1350   srvdata[0] = lwip_htons(SRV_PRIORITY);
1351   srvdata[1] = lwip_htons(SRV_WEIGHT);
1352   srvdata[2] = lwip_htons(service->port);
1353   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n"));
1354   return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
1355                          (const u8_t *) &srvdata, sizeof(srvdata), &srvhost);
1356 }
1357 
1358 /** Write a TXT RR to outpacket */
1359 static err_t
mdns_add_txt_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct mdns_service * service)1360 mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service)
1361 {
1362   struct mdns_domain service_instance;
1363   err_t res;
1364   res = mdns_build_service_domain(&service_instance, service, 1);
1365   if (res != ERR_OK) {
1366     return res;
1367   }
1368   mdns_prepare_txtdata(service);
1369   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n"));
1370   return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
1371                          (u8_t *) &service->txtdata.name, service->txtdata.length, NULL);
1372 }
1373 
1374 /**
1375  * Setup outpacket as a reply to the incoming packet
1376  */
1377 static void
mdns_init_outpacket(struct mdns_outpacket * out,struct mdns_packet * in)1378 mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
1379 {
1380   memset(out, 0, sizeof(struct mdns_outpacket));
1381   out->cache_flush = 1;
1382   out->netif = in->netif;
1383 
1384   /* Copy source IP/port to use when responding unicast, or to choose
1385    * which pcb to use for multicast (IPv4/IPv6)
1386    */
1387   ip_addr_copy(out->dest_addr, in->source_addr);
1388   out->dest_port = in->source_port;
1389 
1390   if (in->source_port != LWIP_IANA_PORT_MDNS) {
1391     out->unicast_reply = 1;
1392     out->cache_flush = 0;
1393     if (in->questions == 1) {
1394       out->legacy_query = 1;
1395       out->tx_id = in->tx_id;
1396     }
1397   }
1398 
1399   if (in->recv_unicast) {
1400     out->unicast_reply = 1;
1401   }
1402 }
1403 
1404 /**
1405  * Send chosen answers as a reply
1406  *
1407  * Add all selected answers (first write will allocate pbuf)
1408  * Add additional answers based on the selected answers
1409  * Send the packet
1410  */
1411 static err_t
mdns_send_outpacket(struct mdns_outpacket * outpkt,u8_t flags)1412 mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
1413 {
1414   struct mdns_service *service;
1415   err_t res = ERR_ARG;
1416   int i;
1417   struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif);
1418   u16_t answers = 0;
1419 
1420   /* Write answers to host questions */
1421 #if LWIP_IPV4
1422   if (outpkt->host_replies & REPLY_HOST_A) {
1423     res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1424     if (res != ERR_OK) {
1425       goto cleanup;
1426     }
1427     answers++;
1428   }
1429   if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
1430     res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1431     if (res != ERR_OK) {
1432       goto cleanup;
1433     }
1434     answers++;
1435   }
1436 #endif
1437 #if LWIP_IPV6
1438   if (outpkt->host_replies & REPLY_HOST_AAAA) {
1439     int addrindex;
1440     for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
1441       if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
1442         res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1443         if (res != ERR_OK) {
1444           goto cleanup;
1445         }
1446         answers++;
1447       }
1448     }
1449   }
1450   if (outpkt->host_replies & REPLY_HOST_PTR_V6) {
1451     u8_t rev_addrs = outpkt->host_reverse_v6_replies;
1452     int addrindex = 0;
1453     while (rev_addrs) {
1454       if (rev_addrs & 1) {
1455         res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1456         if (res != ERR_OK) {
1457           goto cleanup;
1458         }
1459         answers++;
1460       }
1461       addrindex++;
1462       rev_addrs >>= 1;
1463     }
1464   }
1465 #endif
1466 
1467   /* Write answers to service questions */
1468   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1469     service = mdns->services[i];
1470     if (!service) {
1471       continue;
1472     }
1473 
1474     if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) {
1475       res = mdns_add_servicetype_ptr_answer(outpkt, service);
1476       if (res != ERR_OK) {
1477         goto cleanup;
1478       }
1479       answers++;
1480     }
1481 
1482     if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
1483       res = mdns_add_servicename_ptr_answer(outpkt, service);
1484       if (res != ERR_OK) {
1485         goto cleanup;
1486       }
1487       answers++;
1488     }
1489 
1490     if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
1491       res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
1492       if (res != ERR_OK) {
1493         goto cleanup;
1494       }
1495       answers++;
1496     }
1497 
1498     if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
1499       res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
1500       if (res != ERR_OK) {
1501         goto cleanup;
1502       }
1503       answers++;
1504     }
1505   }
1506 
1507   /* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */
1508   if (flags & DNS_FLAG1_RESPONSE) {
1509     outpkt->answers += answers;
1510   } else {
1511     outpkt->authoritative += answers;
1512   }
1513 
1514   /* All answers written, add additional RRs */
1515   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1516     service = mdns->services[i];
1517     if (!service) {
1518       continue;
1519     }
1520 
1521     if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
1522       /* Our service instance requested, include SRV & TXT
1523        * if they are already not requested. */
1524       if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) {
1525         res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
1526         if (res != ERR_OK) {
1527           goto cleanup;
1528         }
1529         outpkt->additional++;
1530       }
1531 
1532       if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) {
1533         res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
1534         if (res != ERR_OK) {
1535           goto cleanup;
1536         }
1537         outpkt->additional++;
1538       }
1539     }
1540 
1541     /* If service instance, SRV, record or an IP address is requested,
1542      * supply all addresses for the host
1543      */
1544     if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) ||
1545         (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) {
1546 #if LWIP_IPV6
1547       if (!(outpkt->host_replies & REPLY_HOST_AAAA)) {
1548         int addrindex;
1549         for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
1550           if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
1551             res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1552             if (res != ERR_OK) {
1553               goto cleanup;
1554             }
1555             outpkt->additional++;
1556           }
1557         }
1558       }
1559 #endif
1560 #if LWIP_IPV4
1561       if (!(outpkt->host_replies & REPLY_HOST_A) &&
1562           !ip4_addr_isany_val(*netif_ip4_addr(outpkt->netif))) {
1563         res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1564         if (res != ERR_OK) {
1565           goto cleanup;
1566         }
1567         outpkt->additional++;
1568       }
1569 #endif
1570     }
1571   }
1572 
1573   if (outpkt->pbuf) {
1574     const ip_addr_t *mcast_destaddr;
1575     struct dns_hdr hdr;
1576 
1577     /* Write header */
1578     memset(&hdr, 0, sizeof(hdr));
1579     hdr.flags1 = flags;
1580     hdr.numquestions = lwip_htons(outpkt->questions);
1581     hdr.numanswers = lwip_htons(outpkt->answers);
1582     hdr.numauthrr = lwip_htons(outpkt->authoritative);
1583     hdr.numextrarr = lwip_htons(outpkt->additional);
1584     hdr.id = lwip_htons(outpkt->tx_id);
1585     pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
1586 
1587     /* Shrink packet */
1588     pbuf_realloc(outpkt->pbuf, outpkt->write_offset);
1589 
1590     if (IP_IS_V6_VAL(outpkt->dest_addr)) {
1591 #if LWIP_IPV6
1592       mcast_destaddr = &v6group;
1593 #endif
1594     } else {
1595 #if LWIP_IPV4
1596       mcast_destaddr = &v4group;
1597 #endif
1598     }
1599     /* Send created packet */
1600     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
1601     if (outpkt->unicast_reply) {
1602       res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
1603     } else {
1604       res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
1605     }
1606   }
1607 
1608 cleanup:
1609   if (outpkt->pbuf) {
1610     pbuf_free(outpkt->pbuf);
1611     outpkt->pbuf = NULL;
1612   }
1613   return res;
1614 }
1615 
1616 /**
1617  * Send unsolicited answer containing all our known data
1618  * @param netif The network interface to send on
1619  * @param destination The target address to send to (usually multicast address)
1620  */
1621 static void
mdns_announce(struct netif * netif,const ip_addr_t * destination)1622 mdns_announce(struct netif *netif, const ip_addr_t *destination)
1623 {
1624   struct mdns_outpacket announce;
1625   int i;
1626   struct mdns_host *mdns = NETIF_TO_HOST(netif);
1627 
1628   memset(&announce, 0, sizeof(announce));
1629   announce.netif = netif;
1630   announce.cache_flush = 1;
1631 #if LWIP_IPV4
1632   if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
1633     announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
1634   }
1635 #endif
1636 #if LWIP_IPV6
1637   for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
1638     if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
1639       announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6;
1640       announce.host_reverse_v6_replies |= (1 << i);
1641     }
1642   }
1643 #endif
1644 
1645   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1646     struct mdns_service *serv = mdns->services[i];
1647     if (serv) {
1648       announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR |
1649                                  REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
1650     }
1651   }
1652 
1653   announce.dest_port = LWIP_IANA_PORT_MDNS;
1654   ip_addr_copy(announce.dest_addr, *destination);
1655   mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
1656 }
1657 
1658 /**
1659  * Handle question MDNS packet
1660  * 1. Parse all questions and set bits what answers to send
1661  * 2. Clear pending answers if known answers are supplied
1662  * 3. Put chosen answers in new packet and send as reply
1663  */
1664 static void
mdns_handle_question(struct mdns_packet * pkt)1665 mdns_handle_question(struct mdns_packet *pkt)
1666 {
1667   struct mdns_service *service;
1668   struct mdns_outpacket reply;
1669   int replies = 0;
1670   int i;
1671   err_t res;
1672   struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif);
1673 
1674   if (mdns->probing_state != MDNS_PROBING_COMPLETE) {
1675     /* Don't answer questions until we've verified our domains via probing */
1676     /* @todo we should check incoming questions during probing for tiebreaking */
1677     return;
1678   }
1679 
1680   mdns_init_outpacket(&reply, pkt);
1681 
1682   while (pkt->questions_left) {
1683     struct mdns_question q;
1684 
1685     res = mdns_read_question(pkt, &q);
1686     if (res != ERR_OK) {
1687       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n"));
1688       return;
1689     }
1690 
1691     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain "));
1692     mdns_domain_debug_print(&q.info.domain);
1693     LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass));
1694 
1695     if (q.unicast) {
1696       /* Reply unicast if any question is unicast */
1697       reply.unicast_reply = 1;
1698     }
1699 
1700     reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies);
1701     replies |= reply.host_replies;
1702 
1703     for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1704       service = mdns->services[i];
1705       if (!service) {
1706         continue;
1707       }
1708       reply.serv_replies[i] |= check_service(service, &q.info);
1709       replies |= reply.serv_replies[i];
1710     }
1711 
1712     if (replies && reply.legacy_query) {
1713       /* Add question to reply packet (legacy packet only has 1 question) */
1714       res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
1715       reply.questions = 1;
1716       if (res != ERR_OK) {
1717         goto cleanup;
1718       }
1719     }
1720   }
1721 
1722   /* Handle known answers */
1723   while (pkt->answers_left) {
1724     struct mdns_answer ans;
1725     u8_t rev_v6;
1726     int match;
1727 
1728     res = mdns_read_answer(pkt, &ans);
1729     if (res != ERR_OK) {
1730       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n"));
1731       goto cleanup;
1732     }
1733 
1734     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain "));
1735     mdns_domain_debug_print(&ans.info.domain);
1736     LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
1737 
1738 
1739     if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) {
1740       /* Skip known answers for ANY type & class */
1741       continue;
1742     }
1743 
1744     rev_v6 = 0;
1745     match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6);
1746     if (match && (ans.ttl > (mdns->dns_ttl / 2))) {
1747       /* The RR in the known answer matches an RR we are planning to send,
1748        * and the TTL is less than half gone.
1749        * If the payload matches we should not send that answer.
1750        */
1751       if (ans.info.type == DNS_RRTYPE_PTR) {
1752         /* Read domain and compare */
1753         struct mdns_domain known_ans, my_ans;
1754         u16_t len;
1755         len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
1756         res = mdns_build_host_domain(&my_ans, mdns);
1757         if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1758 #if LWIP_IPV4
1759           if (match & REPLY_HOST_PTR_V4) {
1760             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
1761             reply.host_replies &= ~REPLY_HOST_PTR_V4;
1762           }
1763 #endif
1764 #if LWIP_IPV6
1765           if (match & REPLY_HOST_PTR_V6) {
1766             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n"));
1767             reply.host_reverse_v6_replies &= ~rev_v6;
1768             if (reply.host_reverse_v6_replies == 0) {
1769               reply.host_replies &= ~REPLY_HOST_PTR_V6;
1770             }
1771           }
1772 #endif
1773         }
1774       } else if (match & REPLY_HOST_A) {
1775 #if LWIP_IPV4
1776         if (ans.rd_length == sizeof(ip4_addr_t) &&
1777             pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) {
1778           LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n"));
1779           reply.host_replies &= ~REPLY_HOST_A;
1780         }
1781 #endif
1782       } else if (match & REPLY_HOST_AAAA) {
1783 #if LWIP_IPV6
1784         if (ans.rd_length == sizeof(ip6_addr_p_t) &&
1785             /* TODO this clears all AAAA responses if first addr is set as known */
1786             pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
1787           LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
1788           reply.host_replies &= ~REPLY_HOST_AAAA;
1789         }
1790 #endif
1791       }
1792     }
1793 
1794     for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1795       service = mdns->services[i];
1796       if (!service) {
1797         continue;
1798       }
1799       match = reply.serv_replies[i] & check_service(service, &ans.info);
1800       if (match && (ans.ttl > (service->dns_ttl / 2))) {
1801         /* The RR in the known answer matches an RR we are planning to send,
1802          * and the TTL is less than half gone.
1803          * If the payload matches we should not send that answer.
1804          */
1805         if (ans.info.type == DNS_RRTYPE_PTR) {
1806           /* Read domain and compare */
1807           struct mdns_domain known_ans, my_ans;
1808           u16_t len;
1809           len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
1810           if (len != MDNS_READNAME_ERROR) {
1811             if (match & REPLY_SERVICE_TYPE_PTR) {
1812               res = mdns_build_service_domain(&my_ans, service, 0);
1813               if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1814                 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n"));
1815                 reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR;
1816               }
1817             }
1818             if (match & REPLY_SERVICE_NAME_PTR) {
1819               res = mdns_build_service_domain(&my_ans, service, 1);
1820               if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1821                 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n"));
1822                 reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR;
1823               }
1824             }
1825           }
1826         } else if (match & REPLY_SERVICE_SRV) {
1827           /* Read and compare to my SRV record */
1828           u16_t field16, len, read_pos;
1829           struct mdns_domain known_ans, my_ans;
1830           read_pos = ans.rd_offset;
1831           do {
1832             /* Check priority field */
1833             len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1834             if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) {
1835               break;
1836             }
1837             read_pos += len;
1838             /* Check weight field */
1839             len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1840             if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) {
1841               break;
1842             }
1843             read_pos += len;
1844             /* Check port field */
1845             len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1846             if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) {
1847               break;
1848             }
1849             read_pos += len;
1850             /* Check host field */
1851             len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
1852             (void)mdns_build_host_domain(&my_ans, mdns);
1853             if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
1854               break;
1855             }
1856             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
1857             reply.serv_replies[i] &= ~REPLY_SERVICE_SRV;
1858           } while (0);
1859         } else if (match & REPLY_SERVICE_TXT) {
1860           mdns_prepare_txtdata(service);
1861           if (service->txtdata.length == ans.rd_length &&
1862               pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) {
1863             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n"));
1864             reply.serv_replies[i] &= ~REPLY_SERVICE_TXT;
1865           }
1866         }
1867       }
1868     }
1869   }
1870 
1871   mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
1872 
1873 cleanup:
1874   if (reply.pbuf) {
1875     /* This should only happen if we fail to alloc/write question for legacy query */
1876     pbuf_free(reply.pbuf);
1877     reply.pbuf = NULL;
1878   }
1879 }
1880 
1881 /**
1882  * Handle response MDNS packet
1883  * Only prints debug for now. Will need more code to do conflict resolution.
1884  */
1885 static void
mdns_handle_response(struct mdns_packet * pkt)1886 mdns_handle_response(struct mdns_packet *pkt)
1887 {
1888   struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
1889 
1890   /* Ignore all questions */
1891   while (pkt->questions_left) {
1892     struct mdns_question q;
1893     err_t res;
1894 
1895     res = mdns_read_question(pkt, &q);
1896     if (res != ERR_OK) {
1897       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n"));
1898       return;
1899     }
1900   }
1901 
1902   while (pkt->answers_left) {
1903     struct mdns_answer ans;
1904     err_t res;
1905 
1906     res = mdns_read_answer(pkt, &ans);
1907     if (res != ERR_OK) {
1908       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n"));
1909       return;
1910     }
1911 
1912     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
1913     mdns_domain_debug_print(&ans.info.domain);
1914     LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
1915 
1916     /*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST
1917       be silently ignored" so drop answer if we haven't started probing yet*/
1918     if ((mdns->probing_state == MDNS_PROBING_ONGOING) && (mdns->probes_sent > 0)) {
1919       struct mdns_domain domain;
1920       u8_t i;
1921       u8_t conflict = 0;
1922 
1923       res = mdns_build_host_domain(&domain, mdns);
1924       if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
1925         LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!"));
1926         conflict = 1;
1927       }
1928 
1929       for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1930         struct mdns_service* service = mdns->services[i];
1931         if (!service) {
1932           continue;
1933         }
1934         res = mdns_build_service_domain(&domain, service, 1);
1935         if ((res == ERR_OK) && mdns_domain_eq(&ans.info.domain, &domain)) {
1936           LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!"));
1937           conflict = 1;
1938         }
1939       }
1940 
1941       if (conflict != 0) {
1942         sys_untimeout(mdns_probe, pkt->netif);
1943         if (mdns_name_result_cb != NULL) {
1944           mdns_name_result_cb(pkt->netif, MDNS_PROBING_CONFLICT);
1945         }
1946       }
1947     }
1948   }
1949 }
1950 
1951 /**
1952  * Receive input function for MDNS packets.
1953  * Handles both IPv4 and IPv6 UDP pcbs.
1954  */
1955 static void
mdns_recv(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)1956 mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
1957 {
1958   struct dns_hdr hdr;
1959   struct mdns_packet packet;
1960   struct netif *recv_netif = ip_current_input_netif();
1961   u16_t offset = 0;
1962 
1963   LWIP_UNUSED_ARG(arg);
1964   LWIP_UNUSED_ARG(pcb);
1965 
1966   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr) ? 6 : 4, p->tot_len));
1967 
1968   if (NETIF_TO_HOST(recv_netif) == NULL) {
1969     /* From netif not configured for MDNS */
1970     goto dealloc;
1971   }
1972 
1973   if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) {
1974     /* Too small */
1975     goto dealloc;
1976   }
1977   offset += SIZEOF_DNS_HDR;
1978 
1979   if (DNS_HDR_GET_OPCODE(&hdr)) {
1980     /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */
1981     goto dealloc;
1982   }
1983 
1984   memset(&packet, 0, sizeof(packet));
1985   ip_addr_copy(packet.source_addr, *addr);
1986   packet.source_port = port;
1987   packet.netif = recv_netif;
1988   packet.pbuf = p;
1989   packet.parse_offset = offset;
1990   packet.tx_id = lwip_ntohs(hdr.id);
1991   packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions);
1992   packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr);
1993 
1994 #if LWIP_IPV6
1995   if (IP_IS_V6(ip_current_dest_addr())) {
1996     /* instead of having one 'v6group' per netif, just compare zoneless here */
1997     if (!ip_addr_cmp_zoneless(ip_current_dest_addr(), &v6group)) {
1998       packet.recv_unicast = 1;
1999     }
2000   }
2001 #endif
2002 #if LWIP_IPV4
2003   if (!IP_IS_V6(ip_current_dest_addr())) {
2004     if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) {
2005       packet.recv_unicast = 1;
2006     }
2007   }
2008 #endif
2009 
2010   if (hdr.flags1 & DNS_FLAG1_RESPONSE) {
2011     mdns_handle_response(&packet);
2012   } else {
2013     mdns_handle_question(&packet);
2014   }
2015 
2016 dealloc:
2017   pbuf_free(p);
2018 }
2019 
2020 #if LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK
2021 static void
mdns_netif_ext_status_callback(struct netif * netif,netif_nsc_reason_t reason,const netif_ext_callback_args_t * args)2022 mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
2023 {
2024   LWIP_UNUSED_ARG(args);
2025 
2026   /* MDNS enabled on netif? */
2027   if (NETIF_TO_HOST(netif) == NULL) {
2028     return;
2029   }
2030 
2031   if (reason & LWIP_NSC_STATUS_CHANGED) {
2032     if (args->status_changed.state != 0) {
2033       mdns_resp_restart(netif);
2034     }
2035     /* TODO: send goodbye message */
2036   }
2037   if (reason & LWIP_NSC_LINK_CHANGED) {
2038     if (args->link_changed.state != 0) {
2039       mdns_resp_restart(netif);
2040     }
2041   }
2042   if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED |
2043       LWIP_NSC_IPV4_NETMASK_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
2044       LWIP_NSC_IPV6_SET | LWIP_NSC_IPV6_ADDR_STATE_CHANGED)) {
2045     mdns_resp_announce(netif);
2046   }
2047 }
2048 #endif /* LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK */
2049 
2050 static err_t
mdns_send_probe(struct netif * netif,const ip_addr_t * destination)2051 mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
2052 {
2053   struct mdns_host* mdns;
2054   struct mdns_outpacket pkt;
2055   struct mdns_domain domain;
2056   u8_t i;
2057   err_t res;
2058 
2059   mdns = NETIF_TO_HOST(netif);
2060 
2061   memset(&pkt, 0, sizeof(pkt));
2062   pkt.netif = netif;
2063 
2064   /* Add unicast questions with rtype ANY for all our desired records */
2065   res = mdns_build_host_domain(&domain, mdns);
2066   if (res != ERR_OK) {
2067     goto cleanup;
2068   }
2069   res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
2070   if (res != ERR_OK) {
2071     goto cleanup;
2072   }
2073   pkt.questions++;
2074   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
2075     struct mdns_service* service = mdns->services[i];
2076     if (!service) {
2077       continue;
2078     }
2079     res = mdns_build_service_domain(&domain, service, 1);
2080     if (res != ERR_OK) {
2081       goto cleanup;
2082     }
2083     res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
2084     if (res != ERR_OK) {
2085       goto cleanup;
2086     }
2087     pkt.questions++;
2088   }
2089 
2090   /* Add answers to the questions above into the authority section for tiebreaking */
2091 #if LWIP_IPV4
2092   if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
2093     pkt.host_replies = REPLY_HOST_A;
2094   }
2095 #endif
2096 #if LWIP_IPV6
2097   for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
2098     if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
2099       pkt.host_replies |= REPLY_HOST_AAAA;
2100     }
2101   }
2102 #endif
2103 
2104   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
2105     struct mdns_service *serv = mdns->services[i];
2106     if (serv) {
2107       pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
2108     }
2109   }
2110 
2111   pkt.tx_id = 0;
2112   pkt.dest_port = LWIP_IANA_PORT_MDNS;
2113   ip_addr_copy(pkt.dest_addr, *destination);
2114   res = mdns_send_outpacket(&pkt, 0);
2115 
2116 cleanup:
2117   if (pkt.pbuf) {
2118     pbuf_free(pkt.pbuf);
2119     pkt.pbuf = NULL;
2120   }
2121   return res;
2122 }
2123 
2124 /**
2125  * Timer callback for probing network.
2126  */
2127 static void
mdns_probe(void * arg)2128 mdns_probe(void* arg)
2129 {
2130   struct netif *netif = (struct netif *)arg;
2131   struct mdns_host* mdns = NETIF_TO_HOST(netif);
2132   err_t res4 = ERR_RTE;
2133   err_t res6 = ERR_RTE;
2134 #if LWIP_IPV6
2135   int i;
2136 #endif
2137 
2138   if(mdns->probes_sent >= MDNS_PROBE_COUNT) {
2139     /* probing successful, announce the new name */
2140     mdns->probing_state = MDNS_PROBING_COMPLETE;
2141     mdns_resp_announce(netif);
2142     if (mdns_name_result_cb != NULL) {
2143       mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL);
2144     }
2145   } else {
2146 #if LWIP_IPV4
2147     /*if ipv4 wait with probing until address is set*/
2148     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
2149       res4 = mdns_send_probe(netif, IP4_ADDR_ANY);
2150     }
2151 #endif
2152 #if LWIP_IPV6
2153     for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
2154       /* if ipv6 wait with probing until at least one address is set */
2155       if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
2156         res6 = mdns_send_probe(netif, IP6_ADDR_ANY);
2157         break;
2158       }
2159     }
2160 #endif
2161     if (res4 == ERR_OK || res6 == ERR_OK) {
2162       mdns->probes_sent++;
2163     }
2164     sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe, netif);
2165   }
2166 }
2167 
2168 static err_t
mdns_send_probe_to_domain(struct netif * netif,const ip_addr_t * destination,const char * names[],int cnt)2169 mdns_send_probe_to_domain(struct netif *netif, const ip_addr_t *destination, const char *names[], int cnt)
2170 {
2171   struct mdns_outpacket pkt;
2172   struct mdns_domain domain;
2173   int i;
2174   err_t res;
2175 
2176   memset(&pkt, 0, sizeof(pkt));
2177   pkt.netif = netif;
2178 
2179   for (i = 0; i < cnt; i++) {
2180     res = mdns_build_custom_domain(&domain, names[i]);
2181     if (res != ERR_OK) {
2182       goto cleanup;
2183     }
2184     res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
2185     if (res != ERR_OK) {
2186       goto cleanup;
2187     }
2188     pkt.questions++;
2189   }
2190 
2191   pkt.tx_id = 0;
2192   pkt.dest_port = LWIP_IANA_PORT_MDNS;
2193   ip_addr_copy(pkt.dest_addr, *destination);
2194   res = mdns_send_outpacket(&pkt, 0);
2195 
2196 cleanup:
2197   if (pkt.pbuf) {
2198     pbuf_free(pkt.pbuf);
2199     pkt.pbuf = NULL;
2200   }
2201   return res;
2202 }
2203 
2204 /**
2205  * @ingroup mdns
2206  * Send probe to domains.
2207  * @param netif The network interface to activate.
2208  * @param names The domains to be probed. Labels of each domain must be seperated by the '.' character,
2209                 and excessive delimiters (if any) will be ignored.
2210  * @param cnt The number of domains.
2211  * @return ERR_OK on success, an err_t otherwise
2212  */
2213 err_t
mdns_probe_domain(struct netif * netif,const char * names[],int cnt)2214 mdns_probe_domain(struct netif *netif, const char *names[], int cnt)
2215 {
2216   struct mdns_host* mdns;
2217   err_t res = ERR_OK;
2218 
2219   LWIP_ASSERT_CORE_LOCKED();
2220   LWIP_ERROR("mdns_probe_domain: mdns not inited", (mdns_pcb != NULL), return ERR_ARG);
2221 
2222   LWIP_ERROR("mdns_probe_domain: netif != NULL", (netif != NULL), return ERR_VAL);
2223   mdns = NETIF_TO_HOST(netif);
2224   LWIP_ERROR("mdns_probe_domain: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2225 
2226   LWIP_ERROR("mdns_probe_domain: invalid names", (names != NULL && cnt > 0), return ERR_VAL);
2227 
2228 #if LWIP_IPV4
2229   /* ipv4 send probe only when address is set */
2230   if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
2231     res = mdns_send_probe_to_domain(netif, IP4_ADDR_ANY, names, cnt);
2232   }
2233 #endif
2234 #if LWIP_IPV6
2235   if (res == ERR_OK) {
2236     res = mdns_send_probe_to_domain(netif, IP6_ADDR_ANY, names, cnt);
2237   }
2238 #endif
2239   return res;
2240 }
2241 
2242 /**
2243  * @ingroup mdns
2244  * Activate MDNS responder for a network interface.
2245  * @param netif The network interface to activate.
2246  * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
2247  *                 with the IP addresses of the netif. The hostname will be copied, the
2248  *                 given pointer can be on the stack.
2249  * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies
2250  * @return ERR_OK if netif was added, an err_t otherwise
2251  */
2252 err_t
mdns_resp_add_netif(struct netif * netif,const char * hostname,u32_t dns_ttl)2253 mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
2254 {
2255   err_t res;
2256   struct mdns_host *mdns;
2257 
2258   LWIP_ASSERT_CORE_LOCKED();
2259   LWIP_ERROR("mdns_resp_add_netif: mdns not inited", (mdns_pcb != NULL), return ERR_ARG);
2260   LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL);
2261   LWIP_ERROR("mdns_resp_add_netif: hostname != NULL", (hostname != NULL), return ERR_VAL);
2262   LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2263 
2264   LWIP_ERROR("mdns_resp_add_netif: Double add", (NETIF_TO_HOST(netif) == NULL), return ERR_VAL);
2265   mdns = (struct mdns_host *) mem_calloc(1, sizeof(struct mdns_host));
2266   LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
2267 
2268   netif_set_client_data(netif, mdns_netif_client_id, mdns);
2269 
2270   MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
2271   mdns->dns_ttl = dns_ttl;
2272   mdns->probes_sent = 0;
2273   mdns->probing_state = MDNS_PROBING_NOT_STARTED;
2274 
2275   /* Join multicast groups */
2276 #if LWIP_IPV4
2277   res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group));
2278   if (res != ERR_OK) {
2279     goto cleanup;
2280   }
2281 #endif
2282 #if LWIP_IPV6
2283   res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group));
2284   if (res != ERR_OK) {
2285     goto cleanup;
2286   }
2287 #endif
2288 
2289   mdns_resp_restart(netif);
2290 
2291   return ERR_OK;
2292 
2293 cleanup:
2294   mem_free(mdns);
2295   netif_set_client_data(netif, mdns_netif_client_id, NULL);
2296   return res;
2297 }
2298 
2299 /**
2300  * @ingroup mdns
2301  * Stop responding to MDNS queries on this interface, leave multicast groups,
2302  * and free the helper structure and any of its services.
2303  * @param netif The network interface to remove.
2304  * @return ERR_OK if netif was removed, an err_t otherwise
2305  */
2306 err_t
mdns_resp_remove_netif(struct netif * netif)2307 mdns_resp_remove_netif(struct netif *netif)
2308 {
2309   int i;
2310   struct mdns_host *mdns;
2311 
2312   LWIP_ASSERT_CORE_LOCKED();
2313   LWIP_ERROR("mdns_resp_remove_netif: mdns not inited", (mdns_pcb != NULL), return ERR_ARG);
2314   LWIP_ERROR("mdns_resp_remove_netif: Null pointer", (netif != NULL), return ERR_VAL);
2315   mdns = NETIF_TO_HOST(netif);
2316   LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
2317 
2318   if (mdns->probing_state == MDNS_PROBING_ONGOING) {
2319     sys_untimeout(mdns_probe, netif);
2320   }
2321 
2322   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
2323     struct mdns_service *service = mdns->services[i];
2324     if (service) {
2325       mem_free(service);
2326     }
2327   }
2328 
2329   /* Leave multicast groups */
2330 #if LWIP_IPV4
2331   igmp_leavegroup_netif(netif, ip_2_ip4(&v4group));
2332 #endif
2333 #if LWIP_IPV6
2334   mld6_leavegroup_netif(netif, ip_2_ip6(&v6group));
2335 #endif
2336 
2337   mem_free(mdns);
2338   netif_set_client_data(netif, mdns_netif_client_id, NULL);
2339   return ERR_OK;
2340 }
2341 
2342 /**
2343  * @ingroup mdns
2344  * Update MDNS hostname for a network interface.
2345  * @param netif The network interface to activate.
2346  * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
2347  *                 with the IP addresses of the netif. The hostname will be copied, the
2348  *                 given pointer can be on the stack.
2349  * @return ERR_OK if name could be set on netif, an err_t otherwise
2350  */
2351 err_t
mdns_resp_rename_netif(struct netif * netif,const char * hostname)2352 mdns_resp_rename_netif(struct netif *netif, const char *hostname)
2353 {
2354   struct mdns_host *mdns;
2355   size_t len;
2356 
2357   LWIP_ASSERT_CORE_LOCKED();
2358   LWIP_ERROR("mdns_resp_rename_netif: mdns not inited", (mdns_pcb != NULL), return ERR_ARG);
2359   LWIP_ERROR("mdns_resp_rename_netif: hostname != NULL", (hostname != NULL), return ERR_VAL);
2360   len = strlen(hostname);
2361   LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL);
2362   LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2363   mdns = NETIF_TO_HOST(netif);
2364   LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2365 
2366   MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
2367   mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
2368 
2369   mdns_resp_restart(netif);
2370 
2371   return ERR_OK;
2372 }
2373 
2374 /**
2375  * @ingroup mdns
2376  * Add a service to the selected network interface.
2377  * @param netif The network interface to publish this service on
2378  * @param name The name of the service
2379  * @param service The service type, like "_http"
2380  * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP
2381  *              for others ("_udp")
2382  * @param port The port the service listens to
2383  * @param dns_ttl Validity time in seconds to send out for service data in DNS replies
2384  * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
2385  *               allow dynamic replies.
2386  * @param txt_data Userdata pointer for txt_fn
2387  * @return service_id if the service was added to the netif, an err_t otherwise
2388  */
2389 s8_t
mdns_resp_add_service(struct netif * netif,const char * name,const char * service,enum mdns_sd_proto proto,u16_t port,u32_t dns_ttl,service_get_txt_fn_t txt_fn,void * txt_data)2390 mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data)
2391 {
2392   s8_t i;
2393   s8_t slot = -1;
2394   struct mdns_service *srv;
2395   struct mdns_host *mdns;
2396 
2397   LWIP_ASSERT_CORE_LOCKED();
2398   LWIP_ERROR("mdns_resp_add_service: mdns not inited", (mdns_pcb != NULL), return ERR_ARG);
2399   LWIP_ERROR("mdns_resp_add_service: netif != NULL", (netif != NULL), return ERR_VAL);
2400   mdns = NETIF_TO_HOST(netif);
2401   LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2402 
2403   LWIP_ERROR("mdns_resp_add_service: name != NULL", (name != NULL), return ERR_VAL);
2404   LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2405   LWIP_ERROR("mdns_resp_add_service: service != NULL", (service != NULL), return ERR_VAL);
2406   LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2407   LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL);
2408 
2409   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
2410     if (mdns->services[i] == NULL) {
2411       slot = i;
2412       break;
2413     }
2414   }
2415   LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
2416 
2417   srv = (struct mdns_service *)mem_calloc(1, sizeof(struct mdns_service));
2418   LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
2419 
2420   MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
2421   MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
2422   srv->txt_fn = txt_fn;
2423   srv->txt_userdata = txt_data;
2424   srv->proto = (u16_t)proto;
2425   srv->port = port;
2426   srv->dns_ttl = dns_ttl;
2427 
2428   mdns->services[slot] = srv;
2429 
2430   mdns_resp_restart(netif);
2431 
2432   return slot;
2433 }
2434 
2435 /**
2436  * @ingroup mdns
2437  * Delete a service on the selected network interface.
2438  * @param netif The network interface on which service should be removed
2439  * @param slot The service slot number returned by mdns_resp_add_service
2440  * @return ERR_OK if the service was removed from the netif, an err_t otherwise
2441  */
2442 err_t
mdns_resp_del_service(struct netif * netif,s8_t slot)2443 mdns_resp_del_service(struct netif *netif, s8_t slot)
2444 {
2445   struct mdns_host *mdns;
2446   struct mdns_service *srv;
2447 
2448   LWIP_ASSERT_CORE_LOCKED();
2449   LWIP_ERROR("mdns_resp_del_service: mdns not inited", (mdns_pcb != NULL), return ERR_ARG);
2450   LWIP_ERROR("mdns_resp_del_service: netif != NULL", (netif != NULL), return ERR_VAL);
2451   mdns = NETIF_TO_HOST(netif);
2452   LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2453   LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
2454   LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
2455 
2456   srv = mdns->services[slot];
2457   mdns->services[slot] = NULL;
2458   mem_free(srv);
2459   return ERR_OK;
2460 }
2461 
2462 /**
2463  * @ingroup mdns
2464  * Update name for an MDNS service.
2465  * @param netif The network interface to activate.
2466  * @param slot The service slot number returned by mdns_resp_add_service
2467  * @param name The new name for the service
2468  * @return ERR_OK if name could be set on service, an err_t otherwise
2469  */
2470 err_t
mdns_resp_rename_service(struct netif * netif,s8_t slot,const char * name)2471 mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name)
2472 {
2473   struct mdns_service *srv;
2474   struct mdns_host *mdns;
2475   size_t len;
2476 
2477   LWIP_ASSERT_CORE_LOCKED();
2478   LWIP_ERROR("mdns_resp_rename_service: mdns not inited", (mdns_pcb != NULL), return ERR_ARG);
2479   LWIP_ERROR("mdns_resp_rename_service: name != NULL", (name != NULL), return ERR_VAL);
2480   len = strlen(name);
2481   LWIP_ERROR("mdns_resp_rename_service: netif != NULL", (netif != NULL), return ERR_VAL);
2482   mdns = NETIF_TO_HOST(netif);
2483   LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2484   LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2485   LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
2486   LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
2487 
2488   srv = mdns->services[slot];
2489 
2490   MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
2491   srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */
2492 
2493   mdns_resp_restart(netif);
2494 
2495   return ERR_OK;
2496 }
2497 
2498 /**
2499  * @ingroup mdns
2500  * Call this function from inside the service_get_txt_fn_t callback to add text data.
2501  * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
2502  * @param service The service provided to the get_txt callback
2503  * @param txt String to add to the TXT field.
2504  * @param txt_len Length of string
2505  * @return ERR_OK if the string was added to the reply, an err_t otherwise
2506  * @note for safety, caller must ensure that txt_len <= strlen(txt)
2507  */
2508 err_t
mdns_resp_add_service_txtitem(struct mdns_service * service,const char * txt,u8_t txt_len)2509 mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
2510 {
2511   LWIP_ASSERT_CORE_LOCKED();
2512   LWIP_ERROR("mdns_resp_add_service_txtitem: mdns not inited", (mdns_pcb != NULL), return ERR_ARG);
2513   LWIP_ERROR("mdns_resp_add_service_txtitem: service != NULL", (service != NULL), return ERR_VAL);
2514   LWIP_ERROR("mdns_resp_add_service_txtitem: txt != NULL", (txt != NULL), return ERR_VAL);
2515 
2516   /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
2517   return mdns_domain_add_label(&service->txtdata, txt, txt_len);
2518 }
2519 
2520 /**
2521  * @ingroup mdns
2522  * Send unsolicited answer containing all our known data
2523  * @param netif The network interface to send on
2524  */
2525 void
mdns_resp_announce(struct netif * netif)2526 mdns_resp_announce(struct netif *netif)
2527 {
2528   struct mdns_host* mdns;
2529   LWIP_ASSERT_CORE_LOCKED();
2530   LWIP_ERROR("mdns_resp_announce: mdns not inited", (mdns_pcb != NULL), return);
2531   LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return);
2532 
2533   mdns = NETIF_TO_HOST(netif);
2534   if (mdns == NULL) {
2535     return;
2536   }
2537 
2538   if (mdns->probing_state == MDNS_PROBING_COMPLETE) {
2539     /* Announce on IPv6 and IPv4 */
2540 #if LWIP_IPV6
2541     mdns_announce(netif, IP6_ADDR_ANY);
2542 #endif
2543 #if LWIP_IPV4
2544     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
2545       mdns_announce(netif, IP4_ADDR_ANY);
2546     }
2547 #endif
2548   } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */
2549 }
2550 
2551 /**
2552  * @ingroup mdns
2553  * Register a callback function that is called if probing is completed successfully
2554  * or with a conflict.
2555  * @param cb The callback function
2556  */
2557 void
mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)2558 mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)
2559 {
2560   mdns_name_result_cb = cb;
2561 }
2562 
2563 /**
2564  * @ingroup mdns
2565  * Restart mdns responder. Call this when cable is connected after being disconnected or
2566  * administrative interface is set up after being down
2567  * @param netif The network interface to send on
2568  */
2569 void
mdns_resp_restart(struct netif * netif)2570 mdns_resp_restart(struct netif *netif)
2571 {
2572   struct mdns_host* mdns;
2573   LWIP_ASSERT_CORE_LOCKED();
2574   LWIP_ERROR("mdns_resp_restart: mdns not inited", (mdns_pcb != NULL), return);
2575   LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return);
2576 
2577   mdns = NETIF_TO_HOST(netif);
2578   if (mdns == NULL) {
2579     return;
2580   }
2581 
2582   if (mdns->probing_state == MDNS_PROBING_ONGOING) {
2583     sys_untimeout(mdns_probe, netif);
2584   }
2585   /* @todo if we've failed 15 times within a 10 second period we MUST wait 5 seconds (or wait 5 seconds every time except first)*/
2586   mdns->probes_sent = 0;
2587   mdns->probing_state = MDNS_PROBING_ONGOING;
2588   sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe, netif);
2589 }
2590 
2591 /**
2592  * @ingroup mdns
2593  * Initiate MDNS responder. Will open UDP sockets on port 5353
2594  */
2595 err_t
mdns_resp_init(void)2596 mdns_resp_init(void)
2597 {
2598   err_t res;
2599 
2600   /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
2601 
2602   LWIP_ERROR("mdns pcb already exists", (mdns_pcb == NULL), return ERR_ALREADY);
2603 
2604   mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
2605   LWIP_ERROR("Failed to allocate pcb", (mdns_pcb != NULL), return ERR_MEM);
2606 #if LWIP_MULTICAST_TX_OPTIONS
2607   udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
2608 #else
2609   mdns_pcb->ttl = MDNS_TTL;
2610 #endif
2611   res = udp_bind(mdns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_MDNS);
2612   LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
2613   LWIP_ERROR("Failed to bind pcb", (res == ERR_OK), return res);
2614   udp_recv(mdns_pcb, mdns_recv, NULL);
2615 
2616   if (mdns_netif_client_id == NETIF_CLIENT_DATA_INVALID_INDEX) {
2617     mdns_netif_client_id = netif_alloc_client_data_id();
2618   }
2619 
2620 #if MDNS_RESP_USENETIF_EXTCALLBACK
2621   /* register for netif events when started on first netif */
2622   netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback);
2623 #endif
2624 
2625   return ERR_OK;
2626 }
2627 
2628 /**
2629  * @ingroup mdns
2630  * Deinitiate MDNS responder.
2631  */
2632 void
mdns_resp_deinit(void)2633 mdns_resp_deinit(void)
2634 {
2635   if (mdns_pcb == NULL) {
2636     return;
2637   }
2638 #if MDNS_RESP_USENETIF_EXTCALLBACK
2639   netif_remove_ext_callback(&netif_callback);
2640 #endif
2641   udp_remove(mdns_pcb);
2642   mdns_pcb = NULL;
2643 }
2644 
2645 #endif /* LWIP_MDNS_RESPONDER */
2646