source: azure_iot_hub_f767zi/trunk/asp_baseplatform/lwip/lwip-2.1.2/src/apps/mdns/mdns.c@ 457

Last change on this file since 457 was 457, checked in by coas-nagasima, 4 years ago

ファイルを追加

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