• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  * MDNS responder implementation
4  *
5  * @defgroup mdns MDNS
6  * @ingroup apps
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/apps/mdns.h"
59 #include "lwip/apps/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;
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 /**
661  * Check which replies we should send for a host/netif based on question
662  * @param netif The network interface that received the question
663  * @param rr Domain/type/class from a question
664  * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for
665  *                         if reply bit has REPLY_HOST_PTR_V6 set
666  * @return Bitmask of which replies to send
667  */
668 static int
check_host(struct netif * netif,struct mdns_rr_info * rr,u8_t * reverse_v6_reply)669 check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
670 {
671   err_t res;
672   int replies = 0;
673   struct mdns_domain mydomain;
674 
675   LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
676 
677   if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
678     /* Invalid class */
679     return replies;
680   }
681 
682   /* Handle PTR for our addresses */
683   if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) {
684 #if LWIP_IPV6
685     int i;
686     for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
687       if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
688         res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i));
689         if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
690           replies |= REPLY_HOST_PTR_V6;
691           /* Mark which addresses where requested */
692           if (reverse_v6_reply) {
693             *reverse_v6_reply |= (1 << i);
694           }
695         }
696       }
697     }
698 #endif
699 #if LWIP_IPV4
700     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
701       res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif));
702       if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
703         replies |= REPLY_HOST_PTR_V4;
704       }
705     }
706 #endif
707   }
708 
709   res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
710   /* Handle requests for our hostname */
711   if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
712     /* TODO return NSEC if unsupported protocol requested */
713 #if LWIP_IPV4
714     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
715         && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) {
716       replies |= REPLY_HOST_A;
717     }
718 #endif
719 #if LWIP_IPV6
720     if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) {
721       replies |= REPLY_HOST_AAAA;
722     }
723 #endif
724   }
725 
726   return replies;
727 }
728 
729 /**
730  * Check which replies we should send for a service based on question
731  * @param service A registered MDNS service
732  * @param rr Domain/type/class from a question
733  * @return Bitmask of which replies to send
734  */
735 static int
check_service(struct mdns_service * service,struct mdns_rr_info * rr)736 check_service(struct mdns_service *service, struct mdns_rr_info *rr)
737 {
738   err_t res;
739   int replies = 0;
740   struct mdns_domain mydomain;
741 
742   if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
743     /* Invalid class */
744     return 0;
745   }
746 
747   res = mdns_build_dnssd_domain(&mydomain);
748   if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
749       (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
750     /* Request for all service types */
751     replies |= REPLY_SERVICE_TYPE_PTR;
752   }
753 
754   res = mdns_build_service_domain(&mydomain, service, 0);
755   if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
756       (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
757     /* Request for the instance of my service */
758     replies |= REPLY_SERVICE_NAME_PTR;
759   }
760 
761   res = mdns_build_service_domain(&mydomain, service, 1);
762   if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
763     /* Request for info about my service */
764     if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) {
765       replies |= REPLY_SERVICE_SRV;
766     }
767     if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) {
768       replies |= REPLY_SERVICE_TXT;
769     }
770   }
771 
772   return replies;
773 }
774 
775 /**
776  * Return bytes needed to write before jump for best result of compressing supplied domain
777  * against domain in outpacket starting at specified offset.
778  * If a match is found, offset is updated to where to jump to
779  * @param pbuf Pointer to pbuf with the partially constructed DNS packet
780  * @param offset Start position of a domain written earlier. If this location is suitable
781  *               for compression, the pointer is updated to where in the domain to jump to.
782  * @param domain The domain to write
783  * @return Number of bytes to write of the new domain before writing a jump to the offset.
784  *         If compression can not be done against this previous domain name, the full new
785  *         domain length is returned.
786  */
787 u16_t
mdns_compress_domain(struct pbuf * pbuf,u16_t * offset,struct mdns_domain * domain)788 mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
789 {
790   struct mdns_domain target;
791   u16_t target_end;
792   u8_t target_len;
793   u8_t writelen = 0;
794   u8_t *ptr;
795   if (pbuf == NULL) {
796     return domain->length;
797   }
798   target_end = mdns_readname(pbuf, *offset, &target);
799   if (target_end == MDNS_READNAME_ERROR) {
800     return domain->length;
801   }
802   target_len = (u8_t)(target_end - *offset);
803   ptr = domain->name;
804   while (writelen < domain->length) {
805     u8_t domainlen = (u8_t)(domain->length - writelen);
806     u8_t labellen;
807     if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
808       /* Compare domains if target is long enough, and we have enough left of the domain */
809       u8_t targetpos = (u8_t)(target.length - domainlen);
810       if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
811         /* We are checking at or beyond a jump in the original, stop looking */
812         break;
813       }
814       if (target.length >= domainlen &&
815           memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
816         *offset += targetpos;
817         return writelen;
818       }
819     }
820     /* Skip to next label in domain */
821     labellen = *ptr;
822     writelen += 1 + labellen;
823     ptr += 1 + labellen;
824   }
825   /* Nothing found */
826   return domain->length;
827 }
828 
829 /**
830  * Write domain to outpacket. Compression will be attempted,
831  * unless domain->skip_compression is set.
832  * @param outpkt The outpacket to write to
833  * @param domain The domain name to write
834  * @return ERR_OK on success, an err_t otherwise
835  */
836 static err_t
mdns_write_domain(struct mdns_outpacket * outpkt,struct mdns_domain * domain)837 mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
838 {
839   int i;
840   err_t res;
841   u16_t writelen = domain->length;
842   u16_t jump_offset = 0;
843   u16_t jump;
844 
845   if (!domain->skip_compression) {
846     for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
847       u16_t offset = outpkt->domain_offsets[i];
848       if (offset) {
849         u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
850         if (len < writelen) {
851           writelen = len;
852           jump_offset = offset;
853         }
854       }
855     }
856   }
857 
858   if (writelen) {
859     /* Write uncompressed part of name */
860     res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
861     if (res != ERR_OK) {
862       return res;
863     }
864 
865     /* Store offset of this new domain */
866     for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
867       if (outpkt->domain_offsets[i] == 0) {
868         outpkt->domain_offsets[i] = outpkt->write_offset;
869         break;
870       }
871     }
872 
873     outpkt->write_offset += writelen;
874   }
875   if (jump_offset) {
876     /* Write jump */
877     jump = lwip_htons(DOMAIN_JUMP | jump_offset);
878     res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
879     if (res != ERR_OK) {
880       return res;
881     }
882     outpkt->write_offset += DOMAIN_JUMP_SIZE;
883   }
884   return ERR_OK;
885 }
886 
887 /**
888  * Write a question to an outpacket
889  * A question contains domain, type and class. Since an answer also starts with these fields this function is also
890  * called from mdns_add_answer().
891  * @param outpkt The outpacket to write to
892  * @param domain The domain name the answer is for
893  * @param type The DNS type of the answer (like 'AAAA', 'SRV')
894  * @param klass The DNS type of the answer (like 'IN')
895  * @param unicast If highest bit in class should be set, to instruct the responder to
896  *                reply with a unicast packet
897  * @return ERR_OK on success, an err_t otherwise
898  */
899 static err_t
mdns_add_question(struct mdns_outpacket * outpkt,struct mdns_domain * domain,u16_t type,u16_t klass,u16_t unicast)900 mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast)
901 {
902   u16_t question_len;
903   u16_t field16;
904   err_t res;
905 
906   if (!outpkt->pbuf) {
907     /* If no pbuf is active, allocate one */
908     outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
909     if (!outpkt->pbuf) {
910       return ERR_MEM;
911     }
912     outpkt->write_offset = SIZEOF_DNS_HDR;
913   }
914 
915   /* Worst case calculation. Domain string might be compressed */
916   question_len = domain->length + sizeof(type) + sizeof(klass);
917   if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) {
918     /* No space */
919     return ERR_MEM;
920   }
921 
922   /* Write name */
923   res = mdns_write_domain(outpkt, domain);
924   if (res != ERR_OK) {
925     return res;
926   }
927 
928   /* Write type */
929   field16 = lwip_htons(type);
930   res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
931   if (res != ERR_OK) {
932     return res;
933   }
934   outpkt->write_offset += sizeof(field16);
935 
936   /* Write class */
937   if (unicast) {
938     klass |= 0x8000;
939   }
940   field16 = lwip_htons(klass);
941   res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
942   if (res != ERR_OK) {
943     return res;
944   }
945   outpkt->write_offset += sizeof(field16);
946 
947   return ERR_OK;
948 }
949 
950 /**
951  * Write answer to reply packet.
952  * buf or answer_domain can be null. The rd_length written will be buf_length +
953  * size of (compressed) domain. Most uses will need either buf or answer_domain,
954  * special case is SRV that starts with 3 u16 and then a domain name.
955  * @param reply The outpacket to write to
956  * @param domain The domain name the answer is for
957  * @param type The DNS type of the answer (like 'AAAA', 'SRV')
958  * @param klass The DNS type of the answer (like 'IN')
959  * @param cache_flush If highest bit in class should be set, to instruct receiver that
960  *                    this reply replaces any earlier answer for this domain/type/class
961  * @param ttl Validity time in seconds to send out for IP address data in DNS replies
962  * @param buf Pointer to buffer of answer data
963  * @param buf_length Length of variable data
964  * @param answer_domain A domain to write after any buffer data as answer
965  * @return ERR_OK on success, an err_t otherwise
966  */
967 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)968 mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush,
969                 u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain)
970 {
971   u16_t answer_len;
972   u16_t field16;
973   u16_t rdlen_offset;
974   u16_t answer_offset;
975   u32_t field32;
976   err_t res;
977 
978   if (!reply->pbuf) {
979     /* If no pbuf is active, allocate one */
980     reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
981     if (!reply->pbuf) {
982       return ERR_MEM;
983     }
984     reply->write_offset = SIZEOF_DNS_HDR;
985   }
986 
987   /* Worst case calculation. Domain strings might be compressed */
988   answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/;
989   if (buf) {
990     answer_len += (u16_t)buf_length;
991   }
992   if (answer_domain) {
993     answer_len += answer_domain->length;
994   }
995   if (reply->write_offset + answer_len > reply->pbuf->tot_len) {
996     /* No space */
997     return ERR_MEM;
998   }
999 
1000   /* Answer starts with same data as question, then more fields */
1001   mdns_add_question(reply, domain, type, klass, cache_flush);
1002 
1003   /* Write TTL */
1004   field32 = lwip_htonl(ttl);
1005   res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset);
1006   if (res != ERR_OK) {
1007     return res;
1008   }
1009   reply->write_offset += sizeof(field32);
1010 
1011   /* Store offsets and skip forward to the data */
1012   rdlen_offset = reply->write_offset;
1013   reply->write_offset += sizeof(field16);
1014   answer_offset = reply->write_offset;
1015 
1016   if (buf) {
1017     /* Write static data */
1018     res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset);
1019     if (res != ERR_OK) {
1020       return res;
1021     }
1022     reply->write_offset += (u16_t)buf_length;
1023   }
1024 
1025   if (answer_domain) {
1026     /* Write name answer (compressed if possible) */
1027     res = mdns_write_domain(reply, answer_domain);
1028     if (res != ERR_OK) {
1029       return res;
1030     }
1031   }
1032 
1033   /* Write rd_length after when we know the answer size */
1034   field16 = lwip_htons(reply->write_offset - answer_offset);
1035   res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset);
1036 
1037   return res;
1038 }
1039 
1040 /**
1041  * Helper function for mdns_read_question/mdns_read_answer
1042  * Reads a domain, type and class from the packet
1043  * @param pkt The MDNS packet to read from. The parse_offset field will be
1044  *            incremented to point to the next unparsed byte.
1045  * @param info The struct to fill with domain, type and class
1046  * @return ERR_OK on success, an err_t otherwise
1047  */
1048 static err_t
mdns_read_rr_info(struct mdns_packet * pkt,struct mdns_rr_info * info)1049 mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info)
1050 {
1051   u16_t field16, copied;
1052   pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain);
1053   if (pkt->parse_offset == MDNS_READNAME_ERROR) {
1054     return ERR_VAL;
1055   }
1056 
1057   copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1058   if (copied != sizeof(field16)) {
1059     return ERR_VAL;
1060   }
1061   pkt->parse_offset += copied;
1062   info->type = lwip_ntohs(field16);
1063 
1064   copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1065   if (copied != sizeof(field16)) {
1066     return ERR_VAL;
1067   }
1068   pkt->parse_offset += copied;
1069   info->klass = lwip_ntohs(field16);
1070 
1071   return ERR_OK;
1072 }
1073 
1074 /**
1075  * Read a question from the packet.
1076  * All questions have to be read before the answers.
1077  * @param pkt The MDNS packet to read from. The questions_left field will be decremented
1078  *            and the parse_offset will be updated.
1079  * @param question The struct to fill with question data
1080  * @return ERR_OK on success, an err_t otherwise
1081  */
1082 static err_t
mdns_read_question(struct mdns_packet * pkt,struct mdns_question * question)1083 mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question)
1084 {
1085   /* Safety check */
1086   if (pkt->pbuf->tot_len < pkt->parse_offset) {
1087     return ERR_VAL;
1088   }
1089 
1090   if (pkt->questions_left) {
1091     err_t res;
1092     pkt->questions_left--;
1093 
1094     memset(question, 0, sizeof(struct mdns_question));
1095     res = mdns_read_rr_info(pkt, &question->info);
1096     if (res != ERR_OK) {
1097       return res;
1098     }
1099 
1100     /* Extract unicast flag from class field */
1101     question->unicast = question->info.klass & 0x8000;
1102     question->info.klass &= 0x7FFF;
1103 
1104     return ERR_OK;
1105   }
1106   return ERR_VAL;
1107 }
1108 
1109 /**
1110  * Read an answer from the packet
1111  * The variable length reply is not copied, its pbuf offset and length is stored instead.
1112  * @param pkt The MDNS packet to read. The answers_left field will be decremented and
1113  *            the parse_offset will be updated.
1114  * @param answer The struct to fill with answer data
1115  * @return ERR_OK on success, an err_t otherwise
1116  */
1117 static err_t
mdns_read_answer(struct mdns_packet * pkt,struct mdns_answer * answer)1118 mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer)
1119 {
1120   /* Read questions first */
1121   if (pkt->questions_left) {
1122     return ERR_VAL;
1123   }
1124 
1125   /* Safety check */
1126   if (pkt->pbuf->tot_len < pkt->parse_offset) {
1127     return ERR_VAL;
1128   }
1129 
1130   if (pkt->answers_left) {
1131     u16_t copied, field16;
1132     u32_t ttl;
1133     err_t res;
1134     pkt->answers_left--;
1135 
1136     memset(answer, 0, sizeof(struct mdns_answer));
1137     res = mdns_read_rr_info(pkt, &answer->info);
1138     if (res != ERR_OK) {
1139       return res;
1140     }
1141 
1142     /* Extract cache_flush flag from class field */
1143     answer->cache_flush = answer->info.klass & 0x8000;
1144     answer->info.klass &= 0x7FFF;
1145 
1146     copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset);
1147     if (copied != sizeof(ttl)) {
1148       return ERR_VAL;
1149     }
1150     pkt->parse_offset += copied;
1151     answer->ttl = lwip_ntohl(ttl);
1152 
1153     copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1154     if (copied != sizeof(field16)) {
1155       return ERR_VAL;
1156     }
1157     pkt->parse_offset += copied;
1158     answer->rd_length = lwip_ntohs(field16);
1159 
1160     answer->rd_offset = pkt->parse_offset;
1161     pkt->parse_offset += answer->rd_length;
1162 
1163     return ERR_OK;
1164   }
1165   return ERR_VAL;
1166 }
1167 
1168 #if LWIP_IPV4
1169 /** Write an IPv4 address (A) RR to outpacket */
1170 static err_t
mdns_add_a_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif)1171 mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
1172 {
1173   struct mdns_domain host;
1174   mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1175   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
1176   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);
1177 }
1178 
1179 /** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */
1180 static err_t
mdns_add_hostv4_ptr_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif)1181 mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
1182 {
1183   struct mdns_domain host, revhost;
1184   mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1185   mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
1186   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
1187   return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
1188 }
1189 #endif
1190 
1191 #if LWIP_IPV6
1192 /** Write an IPv6 address (AAAA) RR to outpacket */
1193 static err_t
mdns_add_aaaa_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif,int addrindex)1194 mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
1195 {
1196   struct mdns_domain host;
1197   mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1198   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
1199   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);
1200 }
1201 
1202 /** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
1203 static err_t
mdns_add_hostv6_ptr_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif,int addrindex)1204 mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
1205 {
1206   struct mdns_domain host, revhost;
1207   mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1208   mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
1209   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
1210   return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
1211 }
1212 #endif
1213 
1214 /** Write an all-services -> servicetype PTR RR to outpacket */
1215 static err_t
mdns_add_servicetype_ptr_answer(struct mdns_outpacket * reply,struct mdns_service * service)1216 mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
1217 {
1218   struct mdns_domain service_type, service_dnssd;
1219   mdns_build_service_domain(&service_type, service, 0);
1220   mdns_build_dnssd_domain(&service_dnssd);
1221   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n"));
1222   return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type);
1223 }
1224 
1225 /** Write a servicetype -> servicename PTR RR to outpacket */
1226 static err_t
mdns_add_servicename_ptr_answer(struct mdns_outpacket * reply,struct mdns_service * service)1227 mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
1228 {
1229   struct mdns_domain service_type, service_instance;
1230   mdns_build_service_domain(&service_type, service, 0);
1231   mdns_build_service_domain(&service_instance, service, 1);
1232   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n"));
1233   return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance);
1234 }
1235 
1236 /** Write a SRV RR to outpacket */
1237 static err_t
mdns_add_srv_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct mdns_host * mdns,struct mdns_service * service)1238 mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service)
1239 {
1240   struct mdns_domain service_instance, srvhost;
1241   u16_t srvdata[3];
1242   mdns_build_service_domain(&service_instance, service, 1);
1243   mdns_build_host_domain(&srvhost, mdns);
1244   if (reply->legacy_query) {
1245     /* RFC 6762 section 18.14:
1246      * In legacy unicast responses generated to answer legacy queries,
1247      * name compression MUST NOT be performed on SRV records.
1248      */
1249     srvhost.skip_compression = 1;
1250   }
1251   srvdata[0] = lwip_htons(SRV_PRIORITY);
1252   srvdata[1] = lwip_htons(SRV_WEIGHT);
1253   srvdata[2] = lwip_htons(service->port);
1254   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n"));
1255   return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
1256                          (const u8_t *) &srvdata, sizeof(srvdata), &srvhost);
1257 }
1258 
1259 /** Write a TXT RR to outpacket */
1260 static err_t
mdns_add_txt_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct mdns_service * service)1261 mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service)
1262 {
1263   struct mdns_domain service_instance;
1264   mdns_build_service_domain(&service_instance, service, 1);
1265   mdns_prepare_txtdata(service);
1266   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n"));
1267   return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
1268                          (u8_t *) &service->txtdata.name, service->txtdata.length, NULL);
1269 }
1270 
1271 /**
1272  * Setup outpacket as a reply to the incoming packet
1273  */
1274 static void
mdns_init_outpacket(struct mdns_outpacket * out,struct mdns_packet * in)1275 mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
1276 {
1277   memset(out, 0, sizeof(struct mdns_outpacket));
1278   out->cache_flush = 1;
1279   out->netif = in->netif;
1280 
1281   /* Copy source IP/port to use when responding unicast, or to choose
1282    * which pcb to use for multicast (IPv4/IPv6)
1283    */
1284   SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t));
1285   out->dest_port = in->source_port;
1286 
1287   if (in->source_port != LWIP_IANA_PORT_MDNS) {
1288     out->unicast_reply = 1;
1289     out->cache_flush = 0;
1290     if (in->questions == 1) {
1291       out->legacy_query = 1;
1292       out->tx_id = in->tx_id;
1293     }
1294   }
1295 
1296   if (in->recv_unicast) {
1297     out->unicast_reply = 1;
1298   }
1299 }
1300 
1301 /**
1302  * Send chosen answers as a reply
1303  *
1304  * Add all selected answers (first write will allocate pbuf)
1305  * Add additional answers based on the selected answers
1306  * Send the packet
1307  */
1308 static err_t
mdns_send_outpacket(struct mdns_outpacket * outpkt,u8_t flags)1309 mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
1310 {
1311   struct mdns_service *service;
1312   err_t res = ERR_ARG;
1313   int i;
1314   struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif);
1315   u16_t answers = 0;
1316 
1317   /* Write answers to host questions */
1318 #if LWIP_IPV4
1319   if (outpkt->host_replies & REPLY_HOST_A) {
1320     res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1321     if (res != ERR_OK) {
1322       goto cleanup;
1323     }
1324     answers++;
1325   }
1326   if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
1327     res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1328     if (res != ERR_OK) {
1329       goto cleanup;
1330     }
1331     answers++;
1332   }
1333 #endif
1334 #if LWIP_IPV6
1335   if (outpkt->host_replies & REPLY_HOST_AAAA) {
1336     int addrindex;
1337     for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
1338       if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
1339         res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1340         if (res != ERR_OK) {
1341           goto cleanup;
1342         }
1343         answers++;
1344       }
1345     }
1346   }
1347   if (outpkt->host_replies & REPLY_HOST_PTR_V6) {
1348     u8_t rev_addrs = outpkt->host_reverse_v6_replies;
1349     int addrindex = 0;
1350     while (rev_addrs) {
1351       if (rev_addrs & 1) {
1352         res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1353         if (res != ERR_OK) {
1354           goto cleanup;
1355         }
1356         answers++;
1357       }
1358       addrindex++;
1359       rev_addrs >>= 1;
1360     }
1361   }
1362 #endif
1363 
1364   /* Write answers to service questions */
1365   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1366     service = mdns->services[i];
1367     if (!service) {
1368       continue;
1369     }
1370 
1371     if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) {
1372       res = mdns_add_servicetype_ptr_answer(outpkt, service);
1373       if (res != ERR_OK) {
1374         goto cleanup;
1375       }
1376       answers++;
1377     }
1378 
1379     if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
1380       res = mdns_add_servicename_ptr_answer(outpkt, service);
1381       if (res != ERR_OK) {
1382         goto cleanup;
1383       }
1384       answers++;
1385     }
1386 
1387     if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
1388       res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
1389       if (res != ERR_OK) {
1390         goto cleanup;
1391       }
1392       answers++;
1393     }
1394 
1395     if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
1396       res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
1397       if (res != ERR_OK) {
1398         goto cleanup;
1399       }
1400       answers++;
1401     }
1402   }
1403 
1404   /* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */
1405   if (flags & DNS_FLAG1_RESPONSE) {
1406     outpkt->answers += answers;
1407   } else {
1408     outpkt->authoritative += answers;
1409   }
1410 
1411   /* All answers written, add additional RRs */
1412   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1413     service = mdns->services[i];
1414     if (!service) {
1415       continue;
1416     }
1417 
1418     if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
1419       /* Our service instance requested, include SRV & TXT
1420        * if they are already not requested. */
1421       if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) {
1422         res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
1423         if (res != ERR_OK) {
1424           goto cleanup;
1425         }
1426         outpkt->additional++;
1427       }
1428 
1429       if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) {
1430         res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
1431         if (res != ERR_OK) {
1432           goto cleanup;
1433         }
1434         outpkt->additional++;
1435       }
1436     }
1437 
1438     /* If service instance, SRV, record or an IP address is requested,
1439      * supply all addresses for the host
1440      */
1441     if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) ||
1442         (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) {
1443 #if LWIP_IPV6
1444       if (!(outpkt->host_replies & REPLY_HOST_AAAA)) {
1445         int addrindex;
1446         for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
1447           if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
1448             res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1449             if (res != ERR_OK) {
1450               goto cleanup;
1451             }
1452             outpkt->additional++;
1453           }
1454         }
1455       }
1456 #endif
1457 #if LWIP_IPV4
1458       if (!(outpkt->host_replies & REPLY_HOST_A) &&
1459           !ip4_addr_isany_val(*netif_ip4_addr(outpkt->netif))) {
1460         res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1461         if (res != ERR_OK) {
1462           goto cleanup;
1463         }
1464         outpkt->additional++;
1465       }
1466 #endif
1467     }
1468   }
1469 
1470   if (outpkt->pbuf) {
1471     const ip_addr_t *mcast_destaddr;
1472     struct dns_hdr hdr;
1473 
1474     /* Write header */
1475     memset(&hdr, 0, sizeof(hdr));
1476     hdr.flags1 = flags;
1477     hdr.numquestions = lwip_htons(outpkt->questions);
1478     hdr.numanswers = lwip_htons(outpkt->answers);
1479     hdr.numauthrr = lwip_htons(outpkt->authoritative);
1480     hdr.numextrarr = lwip_htons(outpkt->additional);
1481     hdr.id = lwip_htons(outpkt->tx_id);
1482     pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
1483 
1484     /* Shrink packet */
1485     pbuf_realloc(outpkt->pbuf, outpkt->write_offset);
1486 
1487     if (IP_IS_V6_VAL(outpkt->dest_addr)) {
1488 #if LWIP_IPV6
1489       mcast_destaddr = &v6group;
1490 #endif
1491     } else {
1492 #if LWIP_IPV4
1493       mcast_destaddr = &v4group;
1494 #endif
1495     }
1496     /* Send created packet */
1497     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
1498     if (outpkt->unicast_reply) {
1499       res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
1500     } else {
1501       res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
1502     }
1503   }
1504 
1505 cleanup:
1506   if (outpkt->pbuf) {
1507     pbuf_free(outpkt->pbuf);
1508     outpkt->pbuf = NULL;
1509   }
1510   return res;
1511 }
1512 
1513 /**
1514  * Send unsolicited answer containing all our known data
1515  * @param netif The network interface to send on
1516  * @param destination The target address to send to (usually multicast address)
1517  */
1518 static void
mdns_announce(struct netif * netif,const ip_addr_t * destination)1519 mdns_announce(struct netif *netif, const ip_addr_t *destination)
1520 {
1521   struct mdns_outpacket announce;
1522   int i;
1523   struct mdns_host *mdns = NETIF_TO_HOST(netif);
1524 
1525   memset(&announce, 0, sizeof(announce));
1526   announce.netif = netif;
1527   announce.cache_flush = 1;
1528 #if LWIP_IPV4
1529   if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
1530     announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
1531   }
1532 #endif
1533 #if LWIP_IPV6
1534   for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
1535     if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
1536       announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6;
1537       announce.host_reverse_v6_replies |= (1 << i);
1538     }
1539   }
1540 #endif
1541 
1542   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1543     struct mdns_service *serv = mdns->services[i];
1544     if (serv) {
1545       announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR |
1546                                  REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
1547     }
1548   }
1549 
1550   announce.dest_port = LWIP_IANA_PORT_MDNS;
1551   SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
1552   mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
1553 }
1554 
1555 /**
1556  * Handle question MDNS packet
1557  * 1. Parse all questions and set bits what answers to send
1558  * 2. Clear pending answers if known answers are supplied
1559  * 3. Put chosen answers in new packet and send as reply
1560  */
1561 static void
mdns_handle_question(struct mdns_packet * pkt)1562 mdns_handle_question(struct mdns_packet *pkt)
1563 {
1564   struct mdns_service *service;
1565   struct mdns_outpacket reply;
1566   int replies = 0;
1567   int i;
1568   err_t res;
1569   struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif);
1570 
1571   if (mdns->probing_state != MDNS_PROBING_COMPLETE) {
1572     /* Don't answer questions until we've verified our domains via probing */
1573     /* @todo we should check incoming questions during probing for tiebreaking */
1574     return;
1575   }
1576 
1577   mdns_init_outpacket(&reply, pkt);
1578 
1579   while (pkt->questions_left) {
1580     struct mdns_question q;
1581 
1582     res = mdns_read_question(pkt, &q);
1583     if (res != ERR_OK) {
1584       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n"));
1585       return;
1586     }
1587 
1588     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain "));
1589     mdns_domain_debug_print(&q.info.domain);
1590     LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass));
1591 
1592     if (q.unicast) {
1593       /* Reply unicast if any question is unicast */
1594       reply.unicast_reply = 1;
1595     }
1596 
1597     reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies);
1598     replies |= reply.host_replies;
1599 
1600     for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1601       service = mdns->services[i];
1602       if (!service) {
1603         continue;
1604       }
1605       reply.serv_replies[i] |= check_service(service, &q.info);
1606       replies |= reply.serv_replies[i];
1607     }
1608 
1609     if (replies && reply.legacy_query) {
1610       /* Add question to reply packet (legacy packet only has 1 question) */
1611       res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
1612       reply.questions = 1;
1613       if (res != ERR_OK) {
1614         goto cleanup;
1615       }
1616     }
1617   }
1618 
1619   /* Handle known answers */
1620   while (pkt->answers_left) {
1621     struct mdns_answer ans;
1622     u8_t rev_v6;
1623     int match;
1624 
1625     res = mdns_read_answer(pkt, &ans);
1626     if (res != ERR_OK) {
1627       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n"));
1628       goto cleanup;
1629     }
1630 
1631     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain "));
1632     mdns_domain_debug_print(&ans.info.domain);
1633     LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
1634 
1635 
1636     if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) {
1637       /* Skip known answers for ANY type & class */
1638       continue;
1639     }
1640 
1641     rev_v6 = 0;
1642     match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6);
1643     if (match && (ans.ttl > (mdns->dns_ttl / 2))) {
1644       /* The RR in the known answer matches an RR we are planning to send,
1645        * and the TTL is less than half gone.
1646        * If the payload matches we should not send that answer.
1647        */
1648       if (ans.info.type == DNS_RRTYPE_PTR) {
1649         /* Read domain and compare */
1650         struct mdns_domain known_ans, my_ans;
1651         u16_t len;
1652         len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
1653         res = mdns_build_host_domain(&my_ans, mdns);
1654         if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1655 #if LWIP_IPV4
1656           if (match & REPLY_HOST_PTR_V4) {
1657             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
1658             reply.host_replies &= ~REPLY_HOST_PTR_V4;
1659           }
1660 #endif
1661 #if LWIP_IPV6
1662           if (match & REPLY_HOST_PTR_V6) {
1663             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n"));
1664             reply.host_reverse_v6_replies &= ~rev_v6;
1665             if (reply.host_reverse_v6_replies == 0) {
1666               reply.host_replies &= ~REPLY_HOST_PTR_V6;
1667             }
1668           }
1669 #endif
1670         }
1671       } else if (match & REPLY_HOST_A) {
1672 #if LWIP_IPV4
1673         if (ans.rd_length == sizeof(ip4_addr_t) &&
1674             pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) {
1675           LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n"));
1676           reply.host_replies &= ~REPLY_HOST_A;
1677         }
1678 #endif
1679       } else if (match & REPLY_HOST_AAAA) {
1680 #if LWIP_IPV6
1681         if (ans.rd_length == sizeof(ip6_addr_p_t) &&
1682             /* TODO this clears all AAAA responses if first addr is set as known */
1683             pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
1684           LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
1685           reply.host_replies &= ~REPLY_HOST_AAAA;
1686         }
1687 #endif
1688       }
1689     }
1690 
1691     for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1692       service = mdns->services[i];
1693       if (!service) {
1694         continue;
1695       }
1696       match = reply.serv_replies[i] & check_service(service, &ans.info);
1697       if (match && (ans.ttl > (service->dns_ttl / 2))) {
1698         /* The RR in the known answer matches an RR we are planning to send,
1699          * and the TTL is less than half gone.
1700          * If the payload matches we should not send that answer.
1701          */
1702         if (ans.info.type == DNS_RRTYPE_PTR) {
1703           /* Read domain and compare */
1704           struct mdns_domain known_ans, my_ans;
1705           u16_t len;
1706           len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
1707           if (len != MDNS_READNAME_ERROR) {
1708             if (match & REPLY_SERVICE_TYPE_PTR) {
1709               res = mdns_build_service_domain(&my_ans, service, 0);
1710               if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1711                 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n"));
1712                 reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR;
1713               }
1714             }
1715             if (match & REPLY_SERVICE_NAME_PTR) {
1716               res = mdns_build_service_domain(&my_ans, service, 1);
1717               if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1718                 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n"));
1719                 reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR;
1720               }
1721             }
1722           }
1723         } else if (match & REPLY_SERVICE_SRV) {
1724           /* Read and compare to my SRV record */
1725           u16_t field16, len, read_pos;
1726           struct mdns_domain known_ans, my_ans;
1727           read_pos = ans.rd_offset;
1728           do {
1729             /* Check priority field */
1730             len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1731             if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) {
1732               break;
1733             }
1734             read_pos += len;
1735             /* Check weight field */
1736             len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1737             if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) {
1738               break;
1739             }
1740             read_pos += len;
1741             /* Check port field */
1742             len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1743             if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) {
1744               break;
1745             }
1746             read_pos += len;
1747             /* Check host field */
1748             len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
1749             mdns_build_host_domain(&my_ans, mdns);
1750             if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
1751               break;
1752             }
1753             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
1754             reply.serv_replies[i] &= ~REPLY_SERVICE_SRV;
1755           } while (0);
1756         } else if (match & REPLY_SERVICE_TXT) {
1757           mdns_prepare_txtdata(service);
1758           if (service->txtdata.length == ans.rd_length &&
1759               pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) {
1760             LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n"));
1761             reply.serv_replies[i] &= ~REPLY_SERVICE_TXT;
1762           }
1763         }
1764       }
1765     }
1766   }
1767 
1768   mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
1769 
1770 cleanup:
1771   if (reply.pbuf) {
1772     /* This should only happen if we fail to alloc/write question for legacy query */
1773     pbuf_free(reply.pbuf);
1774     reply.pbuf = NULL;
1775   }
1776 }
1777 
1778 /**
1779  * Handle response MDNS packet
1780  * Only prints debug for now. Will need more code to do conflict resolution.
1781  */
1782 static void
mdns_handle_response(struct mdns_packet * pkt)1783 mdns_handle_response(struct mdns_packet *pkt)
1784 {
1785   struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
1786 
1787   /* Ignore all questions */
1788   while (pkt->questions_left) {
1789     struct mdns_question q;
1790     err_t res;
1791 
1792     res = mdns_read_question(pkt, &q);
1793     if (res != ERR_OK) {
1794       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n"));
1795       return;
1796     }
1797   }
1798 
1799   while (pkt->answers_left) {
1800     struct mdns_answer ans;
1801     err_t res;
1802 
1803     res = mdns_read_answer(pkt, &ans);
1804     if (res != ERR_OK) {
1805       LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n"));
1806       return;
1807     }
1808 
1809     LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
1810     mdns_domain_debug_print(&ans.info.domain);
1811     LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
1812 
1813     /*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST
1814       be silently ignored" so drop answer if we haven't started probing yet*/
1815     if ((mdns->probing_state == MDNS_PROBING_ONGOING) && (mdns->probes_sent > 0)) {
1816       struct mdns_domain domain;
1817       u8_t i;
1818       u8_t conflict = 0;
1819 
1820       res = mdns_build_host_domain(&domain, mdns);
1821       if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
1822         LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!"));
1823         conflict = 1;
1824       }
1825 
1826       for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1827         struct mdns_service* service = mdns->services[i];
1828         if (!service) {
1829           continue;
1830         }
1831         res = mdns_build_service_domain(&domain, service, 1);
1832         if ((res == ERR_OK) && mdns_domain_eq(&ans.info.domain, &domain)) {
1833           LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!"));
1834           conflict = 1;
1835         }
1836       }
1837 
1838       if (conflict != 0) {
1839         sys_untimeout(mdns_probe, pkt->netif);
1840         if (mdns_name_result_cb != NULL) {
1841           mdns_name_result_cb(pkt->netif, MDNS_PROBING_CONFLICT);
1842         }
1843       }
1844     }
1845   }
1846 }
1847 
1848 /**
1849  * Receive input function for MDNS packets.
1850  * Handles both IPv4 and IPv6 UDP pcbs.
1851  */
1852 static void
mdns_recv(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)1853 mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
1854 {
1855   struct dns_hdr hdr;
1856   struct mdns_packet packet;
1857   struct netif *recv_netif = ip_current_input_netif();
1858   u16_t offset = 0;
1859 
1860   LWIP_UNUSED_ARG(arg);
1861   LWIP_UNUSED_ARG(pcb);
1862 
1863   LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr) ? 6 : 4, p->tot_len));
1864 
1865   if (NETIF_TO_HOST(recv_netif) == NULL) {
1866     /* From netif not configured for MDNS */
1867     goto dealloc;
1868   }
1869 
1870   if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) {
1871     /* Too small */
1872     goto dealloc;
1873   }
1874   offset += SIZEOF_DNS_HDR;
1875 
1876   if (DNS_HDR_GET_OPCODE(&hdr)) {
1877     /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */
1878     goto dealloc;
1879   }
1880 
1881   memset(&packet, 0, sizeof(packet));
1882   SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr));
1883   packet.source_port = port;
1884   packet.netif = recv_netif;
1885   packet.pbuf = p;
1886   packet.parse_offset = offset;
1887   packet.tx_id = lwip_ntohs(hdr.id);
1888   packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions);
1889   packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr);
1890 
1891 #if LWIP_IPV6
1892   if (IP_IS_V6(ip_current_dest_addr())) {
1893     /* instead of having one 'v6group' per netif, just compare zoneless here */
1894     if (!ip_addr_cmp_zoneless(ip_current_dest_addr(), &v6group)) {
1895       packet.recv_unicast = 1;
1896     }
1897   }
1898 #endif
1899 #if LWIP_IPV4
1900   if (!IP_IS_V6(ip_current_dest_addr())) {
1901     if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) {
1902       packet.recv_unicast = 1;
1903     }
1904   }
1905 #endif
1906 
1907   if (hdr.flags1 & DNS_FLAG1_RESPONSE) {
1908     mdns_handle_response(&packet);
1909   } else {
1910     mdns_handle_question(&packet);
1911   }
1912 
1913 dealloc:
1914   pbuf_free(p);
1915 }
1916 
1917 #if LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK
1918 static void
mdns_netif_ext_status_callback(struct netif * netif,netif_nsc_reason_t reason,const netif_ext_callback_args_t * args)1919 mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
1920 {
1921   LWIP_UNUSED_ARG(args);
1922 
1923   /* MDNS enabled on netif? */
1924   if (NETIF_TO_HOST(netif) == NULL) {
1925     return;
1926   }
1927 
1928   if (reason & LWIP_NSC_STATUS_CHANGED) {
1929     if (args->status_changed.state != 0) {
1930       mdns_resp_restart(netif);
1931     }
1932     /* TODO: send goodbye message */
1933   }
1934   if (reason & LWIP_NSC_LINK_CHANGED) {
1935     if (args->link_changed.state != 0) {
1936       mdns_resp_restart(netif);
1937     }
1938   }
1939   if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED |
1940       LWIP_NSC_IPV4_NETMASK_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
1941       LWIP_NSC_IPV6_SET | LWIP_NSC_IPV6_ADDR_STATE_CHANGED)) {
1942     mdns_resp_announce(netif);
1943   }
1944 }
1945 #endif /* LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK */
1946 
1947 static err_t
mdns_send_probe(struct netif * netif,const ip_addr_t * destination)1948 mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
1949 {
1950   struct mdns_host* mdns;
1951   struct mdns_outpacket pkt;
1952   struct mdns_domain domain;
1953   u8_t i;
1954   err_t res;
1955 
1956   mdns = NETIF_TO_HOST(netif);
1957 
1958   memset(&pkt, 0, sizeof(pkt));
1959   pkt.netif = netif;
1960 
1961   /* Add unicast questions with rtype ANY for all our desired records */
1962   mdns_build_host_domain(&domain, mdns);
1963   res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
1964   if (res != ERR_OK) {
1965     goto cleanup;
1966   }
1967   pkt.questions++;
1968   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1969     struct mdns_service* service = mdns->services[i];
1970     if (!service) {
1971       continue;
1972     }
1973     mdns_build_service_domain(&domain, service, 1);
1974     res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
1975     if (res != ERR_OK) {
1976       goto cleanup;
1977     }
1978     pkt.questions++;
1979   }
1980 
1981   /* Add answers to the questions above into the authority section for tiebreaking */
1982 #if LWIP_IPV4
1983   if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
1984     pkt.host_replies = REPLY_HOST_A;
1985   }
1986 #endif
1987 #if LWIP_IPV6
1988   for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
1989     if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
1990       pkt.host_replies |= REPLY_HOST_AAAA;
1991     }
1992   }
1993 #endif
1994 
1995   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1996     struct mdns_service *serv = mdns->services[i];
1997     if (serv) {
1998       pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
1999     }
2000   }
2001 
2002   pkt.tx_id = 0;
2003   pkt.dest_port = LWIP_IANA_PORT_MDNS;
2004   SMEMCPY(&pkt.dest_addr, destination, sizeof(pkt.dest_addr));
2005   res = mdns_send_outpacket(&pkt, 0);
2006 
2007 cleanup:
2008   if (pkt.pbuf) {
2009     pbuf_free(pkt.pbuf);
2010     pkt.pbuf = NULL;
2011   }
2012   return res;
2013 }
2014 
2015 /**
2016  * Timer callback for probing network.
2017  */
2018 static void
mdns_probe(void * arg)2019 mdns_probe(void* arg)
2020 {
2021   struct netif *netif = (struct netif *)arg;
2022   struct mdns_host* mdns = NETIF_TO_HOST(netif);
2023 
2024   if(mdns->probes_sent >= MDNS_PROBE_COUNT) {
2025     /* probing successful, announce the new name */
2026     mdns->probing_state = MDNS_PROBING_COMPLETE;
2027     mdns_resp_announce(netif);
2028     if (mdns_name_result_cb != NULL) {
2029       mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL);
2030     }
2031   } else {
2032 #if LWIP_IPV4
2033     /*if ipv4 wait with probing until address is set*/
2034     if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) &&
2035         mdns_send_probe(netif, IP4_ADDR_ANY) == ERR_OK)
2036 #endif
2037     {
2038 #if LWIP_IPV6
2039       if (mdns_send_probe(netif, IP6_ADDR_ANY) == ERR_OK)
2040 #endif
2041       {
2042         mdns->probes_sent++;
2043       }
2044     }
2045     sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe, netif);
2046   }
2047 }
2048 
2049 /**
2050  * @ingroup mdns
2051  * Activate MDNS responder for a network interface.
2052  * @param netif The network interface to activate.
2053  * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
2054  *                 with the IP addresses of the netif. The hostname will be copied, the
2055  *                 given pointer can be on the stack.
2056  * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies
2057  * @return ERR_OK if netif was added, an err_t otherwise
2058  */
2059 err_t
mdns_resp_add_netif(struct netif * netif,const char * hostname,u32_t dns_ttl)2060 mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
2061 {
2062   err_t res;
2063   struct mdns_host *mdns;
2064 
2065   LWIP_ASSERT_CORE_LOCKED();
2066   LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL);
2067   LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2068 
2069   LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL);
2070   mdns = (struct mdns_host *) mem_calloc(1, sizeof(struct mdns_host));
2071   LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
2072 
2073   netif_set_client_data(netif, mdns_netif_client_id, mdns);
2074 
2075   MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
2076   mdns->dns_ttl = dns_ttl;
2077   mdns->probes_sent = 0;
2078   mdns->probing_state = MDNS_PROBING_NOT_STARTED;
2079 
2080   /* Join multicast groups */
2081 #if LWIP_IPV4
2082   res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group));
2083   if (res != ERR_OK) {
2084     goto cleanup;
2085   }
2086 #endif
2087 #if LWIP_IPV6
2088   res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group));
2089   if (res != ERR_OK) {
2090     goto cleanup;
2091   }
2092 #endif
2093 
2094   mdns_resp_restart(netif);
2095 
2096   return ERR_OK;
2097 
2098 cleanup:
2099   mem_free(mdns);
2100   netif_set_client_data(netif, mdns_netif_client_id, NULL);
2101   return res;
2102 }
2103 
2104 /**
2105  * @ingroup mdns
2106  * Stop responding to MDNS queries on this interface, leave multicast groups,
2107  * and free the helper structure and any of its services.
2108  * @param netif The network interface to remove.
2109  * @return ERR_OK if netif was removed, an err_t otherwise
2110  */
2111 err_t
mdns_resp_remove_netif(struct netif * netif)2112 mdns_resp_remove_netif(struct netif *netif)
2113 {
2114   int i;
2115   struct mdns_host *mdns;
2116 
2117   LWIP_ASSERT_CORE_LOCKED();
2118   LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif);
2119   mdns = NETIF_TO_HOST(netif);
2120   LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
2121 
2122   if (mdns->probing_state == MDNS_PROBING_ONGOING) {
2123     sys_untimeout(mdns_probe, netif);
2124   }
2125 
2126   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
2127     struct mdns_service *service = mdns->services[i];
2128     if (service) {
2129       mem_free(service);
2130     }
2131   }
2132 
2133   /* Leave multicast groups */
2134 #if LWIP_IPV4
2135   igmp_leavegroup_netif(netif, ip_2_ip4(&v4group));
2136 #endif
2137 #if LWIP_IPV6
2138   mld6_leavegroup_netif(netif, ip_2_ip6(&v6group));
2139 #endif
2140 
2141   mem_free(mdns);
2142   netif_set_client_data(netif, mdns_netif_client_id, NULL);
2143   return ERR_OK;
2144 }
2145 
2146 /**
2147  * @ingroup mdns
2148  * Update MDNS hostname for a network interface.
2149  * @param netif The network interface to activate.
2150  * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
2151  *                 with the IP addresses of the netif. The hostname will be copied, the
2152  *                 given pointer can be on the stack.
2153  * @return ERR_OK if name could be set on netif, an err_t otherwise
2154  */
2155 err_t
mdns_resp_rename_netif(struct netif * netif,const char * hostname)2156 mdns_resp_rename_netif(struct netif *netif, const char *hostname)
2157 {
2158   struct mdns_host *mdns;
2159   size_t len;
2160 
2161   LWIP_ASSERT_CORE_LOCKED();
2162   len = strlen(hostname);
2163   LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL);
2164   LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2165   mdns = NETIF_TO_HOST(netif);
2166   LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2167 
2168   MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
2169   mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
2170 
2171   mdns_resp_restart(netif);
2172 
2173   return ERR_OK;
2174 }
2175 
2176 /**
2177  * @ingroup mdns
2178  * Add a service to the selected network interface.
2179  * @param netif The network interface to publish this service on
2180  * @param name The name of the service
2181  * @param service The service type, like "_http"
2182  * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP
2183  *              for others ("_udp")
2184  * @param port The port the service listens to
2185  * @param dns_ttl Validity time in seconds to send out for service data in DNS replies
2186  * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
2187  *               allow dynamic replies.
2188  * @param txt_data Userdata pointer for txt_fn
2189  * @return service_id if the service was added to the netif, an err_t otherwise
2190  */
2191 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)2192 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)
2193 {
2194   s8_t i;
2195   s8_t slot = -1;
2196   struct mdns_service *srv;
2197   struct mdns_host *mdns;
2198 
2199   LWIP_ASSERT_CORE_LOCKED();
2200   LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif);
2201   mdns = NETIF_TO_HOST(netif);
2202   LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2203 
2204   LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2205   LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2206   LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL);
2207 
2208   for (i = 0; i < MDNS_MAX_SERVICES; i++) {
2209     if (mdns->services[i] == NULL) {
2210       slot = i;
2211       break;
2212     }
2213   }
2214   LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
2215 
2216   srv = (struct mdns_service *)mem_calloc(1, sizeof(struct mdns_service));
2217   LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
2218 
2219   MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
2220   MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
2221   srv->txt_fn = txt_fn;
2222   srv->txt_userdata = txt_data;
2223   srv->proto = (u16_t)proto;
2224   srv->port = port;
2225   srv->dns_ttl = dns_ttl;
2226 
2227   mdns->services[slot] = srv;
2228 
2229   mdns_resp_restart(netif);
2230 
2231   return slot;
2232 }
2233 
2234 /**
2235  * @ingroup mdns
2236  * Delete a service on the selected network interface.
2237  * @param netif The network interface on which service should be removed
2238  * @param slot The service slot number returned by mdns_resp_add_service
2239  * @return ERR_OK if the service was removed from the netif, an err_t otherwise
2240  */
2241 err_t
mdns_resp_del_service(struct netif * netif,s8_t slot)2242 mdns_resp_del_service(struct netif *netif, s8_t slot)
2243 {
2244   struct mdns_host *mdns;
2245   struct mdns_service *srv;
2246   LWIP_ASSERT("mdns_resp_del_service: netif != NULL", netif);
2247   mdns = NETIF_TO_HOST(netif);
2248   LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2249   LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
2250   LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
2251 
2252   srv = mdns->services[slot];
2253   mdns->services[slot] = NULL;
2254   mem_free(srv);
2255   return ERR_OK;
2256 }
2257 
2258 /**
2259  * @ingroup mdns
2260  * Update name for an MDNS service.
2261  * @param netif The network interface to activate.
2262  * @param slot The service slot number returned by mdns_resp_add_service
2263  * @param name The new name for the service
2264  * @return ERR_OK if name could be set on service, an err_t otherwise
2265  */
2266 err_t
mdns_resp_rename_service(struct netif * netif,s8_t slot,const char * name)2267 mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name)
2268 {
2269   struct mdns_service *srv;
2270   struct mdns_host *mdns;
2271   size_t len;
2272 
2273   LWIP_ASSERT_CORE_LOCKED();
2274   len = strlen(name);
2275   LWIP_ASSERT("mdns_resp_rename_service: netif != NULL", netif);
2276   mdns = NETIF_TO_HOST(netif);
2277   LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2278   LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2279   LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
2280   LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
2281 
2282   srv = mdns->services[slot];
2283 
2284   MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
2285   srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */
2286 
2287   mdns_resp_restart(netif);
2288 
2289   return ERR_OK;
2290 }
2291 
2292 /**
2293  * @ingroup mdns
2294  * Call this function from inside the service_get_txt_fn_t callback to add text data.
2295  * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
2296  * @param service The service provided to the get_txt callback
2297  * @param txt String to add to the TXT field.
2298  * @param txt_len Length of string
2299  * @return ERR_OK if the string was added to the reply, an err_t otherwise
2300  */
2301 err_t
mdns_resp_add_service_txtitem(struct mdns_service * service,const char * txt,u8_t txt_len)2302 mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
2303 {
2304   LWIP_ASSERT_CORE_LOCKED();
2305   LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
2306 
2307   /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
2308   return mdns_domain_add_label(&service->txtdata, txt, txt_len);
2309 }
2310 
2311 /**
2312  * @ingroup mdns
2313  * Send unsolicited answer containing all our known data
2314  * @param netif The network interface to send on
2315  */
2316 void
mdns_resp_announce(struct netif * netif)2317 mdns_resp_announce(struct netif *netif)
2318 {
2319   struct mdns_host* mdns;
2320   LWIP_ASSERT_CORE_LOCKED();
2321   LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return);
2322 
2323   mdns = NETIF_TO_HOST(netif);
2324   if (mdns == NULL) {
2325     return;
2326   }
2327 
2328   if (mdns->probing_state == MDNS_PROBING_COMPLETE) {
2329     /* Announce on IPv6 and IPv4 */
2330 #if LWIP_IPV6
2331     mdns_announce(netif, IP6_ADDR_ANY);
2332 #endif
2333 #if LWIP_IPV4
2334     if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
2335       mdns_announce(netif, IP4_ADDR_ANY);
2336     }
2337 #endif
2338   } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */
2339 }
2340 
2341 /** Register a callback function that is called if probing is completed successfully
2342  * or with a conflict. */
2343 void
mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)2344 mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)
2345 {
2346   mdns_name_result_cb = cb;
2347 }
2348 
2349 /**
2350  * @ingroup mdns
2351  * Restart mdns responder. Call this when cable is connected after being disconnected or
2352  * administrative interface is set up after being down
2353  * @param netif The network interface to send on
2354  */
2355 void
mdns_resp_restart(struct netif * netif)2356 mdns_resp_restart(struct netif *netif)
2357 {
2358   struct mdns_host* mdns;
2359   LWIP_ASSERT_CORE_LOCKED();
2360   LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return);
2361 
2362   mdns = NETIF_TO_HOST(netif);
2363   if (mdns == NULL) {
2364     return;
2365   }
2366 
2367   if (mdns->probing_state == MDNS_PROBING_ONGOING) {
2368     sys_untimeout(mdns_probe, netif);
2369   }
2370   /* @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)*/
2371   mdns->probes_sent = 0;
2372   mdns->probing_state = MDNS_PROBING_ONGOING;
2373   sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe, netif);
2374 }
2375 
2376 /**
2377  * @ingroup mdns
2378  * Initiate MDNS responder. Will open UDP sockets on port 5353
2379  */
2380 void
mdns_resp_init(void)2381 mdns_resp_init(void)
2382 {
2383   err_t res;
2384 
2385   /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
2386 
2387   mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
2388   LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
2389 #if LWIP_MULTICAST_TX_OPTIONS
2390   udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
2391 #else
2392   mdns_pcb->ttl = MDNS_TTL;
2393 #endif
2394   res = udp_bind(mdns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_MDNS);
2395   LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
2396   LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
2397   udp_recv(mdns_pcb, mdns_recv, NULL);
2398 
2399   mdns_netif_client_id = netif_alloc_client_data_id();
2400 
2401 #if MDNS_RESP_USENETIF_EXTCALLBACK
2402   /* register for netif events when started on first netif */
2403   netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback);
2404 #endif
2405 }
2406 
2407 #endif /* LWIP_MDNS_RESPONDER */
2408