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 <hostname>, '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 <hostname>.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 <hostname>.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