Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
lwip_mdns.c
00001 /** 00002 * @file 00003 * MDNS responder implementation 00004 * 00005 * @defgroup mdns MDNS 00006 * @ingroup apps 00007 * 00008 * RFC 6762 - Multicast DNS\n 00009 * RFC 6763 - DNS-Based Service Discovery\n 00010 * 00011 * @verbinclude mdns.txt 00012 * 00013 * Things left to implement: 00014 * ------------------------- 00015 * 00016 * - Tiebreaking for simultaneous probing 00017 * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off... 00018 * - Checking that source address of unicast requests are on the same network 00019 * - Limiting multicast responses to 1 per second per resource record 00020 * - Fragmenting replies if required 00021 * - Handling multi-packet known answers 00022 * - Individual known answer detection for all local IPv6 addresses 00023 * - Dynamic size of outgoing packet 00024 */ 00025 00026 /* 00027 * Copyright (c) 2015 Verisure Innovation AB 00028 * All rights reserved. 00029 * 00030 * Redistribution and use in source and binary forms, with or without modification, 00031 * are permitted provided that the following conditions are met: 00032 * 00033 * 1. Redistributions of source code must retain the above copyright notice, 00034 * this list of conditions and the following disclaimer. 00035 * 2. Redistributions in binary form must reproduce the above copyright notice, 00036 * this list of conditions and the following disclaimer in the documentation 00037 * and/or other materials provided with the distribution. 00038 * 3. The name of the author may not be used to endorse or promote products 00039 * derived from this software without specific prior written permission. 00040 * 00041 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 00042 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 00043 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 00044 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00045 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 00046 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00047 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00048 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 00049 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 00050 * OF SUCH DAMAGE. 00051 * 00052 * This file is part of the lwIP TCP/IP stack. 00053 * 00054 * Author: Erik Ekman <erik@kryo.se> 00055 * 00056 */ 00057 00058 #include "lwip/apps/mdns.h" 00059 #include "lwip/apps/mdns_priv.h" 00060 #include "lwip/netif.h" 00061 #include "lwip/udp.h" 00062 #include "lwip/ip_addr.h" 00063 #include "lwip/mem.h" 00064 #include "lwip/prot/dns.h" 00065 #include "lwip/prot/iana.h" 00066 #include "lwip/timeouts.h" 00067 00068 #include <string.h> 00069 00070 #if LWIP_MDNS_RESPONDER 00071 00072 #if (LWIP_IPV4 && !LWIP_IGMP) 00073 #error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h" 00074 #endif 00075 #if (LWIP_IPV6 && !LWIP_IPV6_MLD) 00076 #error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h" 00077 #endif 00078 #if (!LWIP_UDP) 00079 #error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h" 00080 #endif 00081 00082 #if LWIP_IPV4 00083 #include "lwip/igmp.h" 00084 /* IPv4 multicast group 224.0.0.251 */ 00085 static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT; 00086 #endif 00087 00088 #if LWIP_IPV6 00089 #include "lwip/mld6.h" 00090 /* IPv6 multicast group FF02::FB */ 00091 static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT; 00092 #endif 00093 00094 #define MDNS_TTL 255 00095 00096 /* Stored offsets to beginning of domain names 00097 * Used for compression. 00098 */ 00099 #define NUM_DOMAIN_OFFSETS 10 00100 #define DOMAIN_JUMP_SIZE 2 00101 #define DOMAIN_JUMP 0xc000 00102 00103 static u8_t mdns_netif_client_id; 00104 static struct udp_pcb *mdns_pcb; 00105 #if MDNS_RESP_USENETIF_EXTCALLBACK 00106 NETIF_DECLARE_EXT_CALLBACK(netif_callback) 00107 #endif 00108 static mdns_name_result_cb_t mdns_name_result_cb; 00109 00110 #define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id)) 00111 00112 #define TOPDOMAIN_LOCAL "local" 00113 00114 #define REVERSE_PTR_TOPDOMAIN "arpa" 00115 #define REVERSE_PTR_V4_DOMAIN "in-addr" 00116 #define REVERSE_PTR_V6_DOMAIN "ip6" 00117 00118 #define SRV_PRIORITY 0 00119 #define SRV_WEIGHT 0 00120 00121 /* Payload size allocated for each outgoing UDP packet */ 00122 #define OUTPACKET_SIZE 500 00123 00124 /* Lookup from hostname -> IPv4 */ 00125 #define REPLY_HOST_A 0x01 00126 /* Lookup from IPv4/v6 -> hostname */ 00127 #define REPLY_HOST_PTR_V4 0x02 00128 /* Lookup from hostname -> IPv6 */ 00129 #define REPLY_HOST_AAAA 0x04 00130 /* Lookup from hostname -> IPv6 */ 00131 #define REPLY_HOST_PTR_V6 0x08 00132 00133 /* Lookup for service types */ 00134 #define REPLY_SERVICE_TYPE_PTR 0x10 00135 /* Lookup for instances of service */ 00136 #define REPLY_SERVICE_NAME_PTR 0x20 00137 /* Lookup for location of service instance */ 00138 #define REPLY_SERVICE_SRV 0x40 00139 /* Lookup for text info on service instance */ 00140 #define REPLY_SERVICE_TXT 0x80 00141 00142 #define MDNS_PROBE_DELAY_MS 250 00143 #define MDNS_PROBE_COUNT 3 00144 #ifdef LWIP_RAND 00145 /* first probe timeout SHOULD be random 0-250 ms*/ 00146 #define MDNS_INITIAL_PROBE_DELAY_MS (LWIP_RAND() % MDNS_PROBE_DELAY_MS) 00147 #else 00148 #define MDNS_INITIAL_PROBE_DELAY_MS MDNS_PROBE_DELAY_MS 00149 #endif 00150 00151 #define MDNS_PROBING_NOT_STARTED 0 00152 #define MDNS_PROBING_ONGOING 1 00153 #define MDNS_PROBING_COMPLETE 2 00154 00155 static const char *dnssd_protos[] = { 00156 "_udp", /* DNSSD_PROTO_UDP */ 00157 "_tcp", /* DNSSD_PROTO_TCP */ 00158 }; 00159 00160 /** Description of a service */ 00161 struct mdns_service { 00162 /** TXT record to answer with */ 00163 struct mdns_domain txtdata; 00164 /** Name of service, like 'myweb' */ 00165 char name[MDNS_LABEL_MAXLEN + 1]; 00166 /** Type of service, like '_http' */ 00167 char service[MDNS_LABEL_MAXLEN + 1]; 00168 /** Callback function and userdata 00169 * to update txtdata buffer */ 00170 service_get_txt_fn_t txt_fn; 00171 void *txt_userdata; 00172 /** TTL in seconds of SRV/TXT replies */ 00173 u32_t dns_ttl; 00174 /** Protocol, TCP or UDP */ 00175 u16_t proto; 00176 /** Port of the service */ 00177 u16_t port; 00178 }; 00179 00180 /** Description of a host/netif */ 00181 struct mdns_host { 00182 /** Hostname */ 00183 char name[MDNS_LABEL_MAXLEN + 1]; 00184 /** Pointer to services */ 00185 struct mdns_service *services[MDNS_MAX_SERVICES]; 00186 /** TTL in seconds of A/AAAA/PTR replies */ 00187 u32_t dns_ttl; 00188 /** Number of probes sent for the current name */ 00189 u8_t probes_sent; 00190 /** State in probing sequence */ 00191 u8_t probing_state; 00192 }; 00193 00194 /** Information about received packet */ 00195 struct mdns_packet { 00196 /** Sender IP/port */ 00197 ip_addr_t source_addr; 00198 u16_t source_port; 00199 /** If packet was received unicast */ 00200 u16_t recv_unicast; 00201 /** Netif that received the packet */ 00202 struct netif *netif; 00203 /** Packet data */ 00204 struct pbuf *pbuf; 00205 /** Current parsing offset in packet */ 00206 u16_t parse_offset; 00207 /** Identifier. Used in legacy queries */ 00208 u16_t tx_id; 00209 /** Number of questions in packet, 00210 * read from packet header */ 00211 u16_t questions; 00212 /** Number of unparsed questions */ 00213 u16_t questions_left; 00214 /** Number of answers in packet, 00215 * (sum of normal, authoritative and additional answers) 00216 * read from packet header */ 00217 u16_t answers; 00218 /** Number of unparsed answers */ 00219 u16_t answers_left; 00220 }; 00221 00222 /** Information about outgoing packet */ 00223 struct mdns_outpacket { 00224 /** Netif to send the packet on */ 00225 struct netif *netif; 00226 /** Packet data */ 00227 struct pbuf *pbuf; 00228 /** Current write offset in packet */ 00229 u16_t write_offset; 00230 /** Identifier. Used in legacy queries */ 00231 u16_t tx_id; 00232 /** Destination IP/port if sent unicast */ 00233 ip_addr_t dest_addr; 00234 u16_t dest_port; 00235 /** Number of questions written */ 00236 u16_t questions; 00237 /** Number of normal answers written */ 00238 u16_t answers; 00239 /** Number of authoritative answers written */ 00240 u16_t authoritative; 00241 /** Number of additional answers written */ 00242 u16_t additional; 00243 /** Offsets for written domain names in packet. 00244 * Used for compression */ 00245 u16_t domain_offsets[NUM_DOMAIN_OFFSETS]; 00246 /** If all answers in packet should set cache_flush bit */ 00247 u8_t cache_flush; 00248 /** If reply should be sent unicast */ 00249 u8_t unicast_reply; 00250 /** If legacy query. (tx_id needed, and write 00251 * question again in reply before answer) */ 00252 u8_t legacy_query; 00253 /* Reply bitmask for host information */ 00254 u8_t host_replies; 00255 /* Bitmask for which reverse IPv6 hosts to answer */ 00256 u8_t host_reverse_v6_replies; 00257 /* Reply bitmask per service */ 00258 u8_t serv_replies[MDNS_MAX_SERVICES]; 00259 }; 00260 00261 /** Domain, type and class. 00262 * Shared between questions and answers */ 00263 struct mdns_rr_info { 00264 struct mdns_domain domain; 00265 u16_t type; 00266 u16_t klass; 00267 }; 00268 00269 struct mdns_question { 00270 struct mdns_rr_info info; 00271 /** unicast reply requested */ 00272 u16_t unicast; 00273 }; 00274 00275 struct mdns_answer { 00276 struct mdns_rr_info info; 00277 /** cache flush command bit */ 00278 u16_t cache_flush; 00279 /* Validity time in seconds */ 00280 u32_t ttl; 00281 /** Length of variable answer */ 00282 u16_t rd_length; 00283 /** Offset of start of variable answer in packet */ 00284 u16_t rd_offset; 00285 }; 00286 00287 static err_t mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags); 00288 static void mdns_probe(void* arg); 00289 00290 static err_t 00291 mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len) 00292 { 00293 if (len > MDNS_LABEL_MAXLEN) { 00294 return ERR_VAL; 00295 } 00296 if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) { 00297 return ERR_VAL; 00298 } 00299 /* Allow only zero marker on last byte */ 00300 if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) { 00301 return ERR_VAL; 00302 } 00303 domain->name[domain->length] = len; 00304 domain->length++; 00305 return ERR_OK; 00306 } 00307 00308 /** 00309 * Add a label part to a domain 00310 * @param domain The domain to add a label to 00311 * @param label The label to add, like <hostname>, 'local', 'com' or '' 00312 * @param len The length of the label 00313 * @return ERR_OK on success, an err_t otherwise if label too long 00314 */ 00315 err_t 00316 mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len) 00317 { 00318 err_t err = mdns_domain_add_label_base(domain, len); 00319 if (err != ERR_OK) { 00320 return err; 00321 } 00322 if (len) { 00323 MEMCPY(&domain->name[domain->length], label, len); 00324 domain->length += len; 00325 } 00326 return ERR_OK; 00327 } 00328 00329 /** 00330 * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf) 00331 */ 00332 static err_t 00333 mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len) 00334 { 00335 err_t err = mdns_domain_add_label_base(domain, len); 00336 if (err != ERR_OK) { 00337 return err; 00338 } 00339 if (len) { 00340 if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) { 00341 /* take back the ++ done before */ 00342 domain->length--; 00343 return ERR_ARG; 00344 } 00345 domain->length += len; 00346 } 00347 return ERR_OK; 00348 } 00349 00350 /** 00351 * Internal readname function with max 6 levels of recursion following jumps 00352 * while decompressing name 00353 */ 00354 static u16_t 00355 mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth) 00356 { 00357 u8_t c; 00358 00359 do { 00360 if (depth > 5) { 00361 /* Too many jumps */ 00362 return MDNS_READNAME_ERROR; 00363 } 00364 00365 c = pbuf_get_at(p, offset); 00366 offset++; 00367 00368 /* is this a compressed label? */ 00369 if ((c & 0xc0) == 0xc0) { 00370 u16_t jumpaddr; 00371 if (offset >= p->tot_len) { 00372 /* Make sure both jump bytes fit in the packet */ 00373 return MDNS_READNAME_ERROR; 00374 } 00375 jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff)); 00376 offset++; 00377 if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) { 00378 u16_t res; 00379 /* Recursive call, maximum depth will be checked */ 00380 res = mdns_readname_loop(p, jumpaddr, domain, depth + 1); 00381 /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */ 00382 if (res == MDNS_READNAME_ERROR) { 00383 return res; 00384 } 00385 } else { 00386 return MDNS_READNAME_ERROR; 00387 } 00388 break; 00389 } 00390 00391 /* normal label */ 00392 if (c <= MDNS_LABEL_MAXLEN) { 00393 err_t res; 00394 00395 if (c + domain->length >= MDNS_DOMAIN_MAXLEN) { 00396 return MDNS_READNAME_ERROR; 00397 } 00398 res = mdns_domain_add_label_pbuf(domain, p, offset, c); 00399 if (res != ERR_OK) { 00400 return MDNS_READNAME_ERROR; 00401 } 00402 offset += c; 00403 } else { 00404 /* bad length byte */ 00405 return MDNS_READNAME_ERROR; 00406 } 00407 } while (c != 0); 00408 00409 return offset; 00410 } 00411 00412 /** 00413 * Read possibly compressed domain name from packet buffer 00414 * @param p The packet 00415 * @param offset start position of domain name in packet 00416 * @param domain The domain name destination 00417 * @return The new offset after the domain, or MDNS_READNAME_ERROR 00418 * if reading failed 00419 */ 00420 u16_t 00421 mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain) 00422 { 00423 memset(domain, 0, sizeof(struct mdns_domain)); 00424 return mdns_readname_loop(p, offset, domain, 0); 00425 } 00426 00427 /** 00428 * Print domain name to debug output 00429 * @param domain The domain name 00430 */ 00431 static void 00432 mdns_domain_debug_print(struct mdns_domain *domain) 00433 { 00434 u8_t *src = domain->name; 00435 u8_t i; 00436 00437 while (*src) { 00438 u8_t label_len = *src; 00439 src++; 00440 for (i = 0; i < label_len; i++) { 00441 LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i])); 00442 } 00443 src += label_len; 00444 LWIP_DEBUGF(MDNS_DEBUG, (".")); 00445 } 00446 } 00447 00448 /** 00449 * Return 1 if contents of domains match (case-insensitive) 00450 * @param a Domain name to compare 1 00451 * @param b Domain name to compare 2 00452 * @return 1 if domains are equal ignoring case, 0 otherwise 00453 */ 00454 int 00455 mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b) 00456 { 00457 u8_t *ptra, *ptrb; 00458 u8_t len; 00459 int res; 00460 00461 if (a->length != b->length) { 00462 return 0; 00463 } 00464 00465 ptra = a->name; 00466 ptrb = b->name; 00467 while (*ptra && *ptrb && ptra < &a->name[a->length]) { 00468 if (*ptra != *ptrb) { 00469 return 0; 00470 } 00471 len = *ptra; 00472 ptra++; 00473 ptrb++; 00474 res = lwip_strnicmp((char *) ptra, (char *) ptrb, len); 00475 if (res != 0) { 00476 return 0; 00477 } 00478 ptra += len; 00479 ptrb += len; 00480 } 00481 if (*ptra != *ptrb && ptra < &a->name[a->length]) { 00482 return 0; 00483 } 00484 return 1; 00485 } 00486 00487 /** 00488 * Call user supplied function to setup TXT data 00489 * @param service The service to build TXT record for 00490 */ 00491 static void 00492 mdns_prepare_txtdata(struct mdns_service *service) 00493 { 00494 memset(&service->txtdata, 0, sizeof(struct mdns_domain)); 00495 if (service->txt_fn) { 00496 service->txt_fn(service, service->txt_userdata); 00497 } 00498 } 00499 00500 #if LWIP_IPV4 00501 /** 00502 * Build domain for reverse lookup of IPv4 address 00503 * like 12.0.168.192.in-addr.arpa. for 192.168.0.12 00504 * @param domain Where to write the domain name 00505 * @param addr Pointer to an IPv4 address to encode 00506 * @return ERR_OK if domain was written, an err_t otherwise 00507 */ 00508 static err_t 00509 mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr) 00510 { 00511 int i; 00512 err_t res; 00513 const u8_t *ptr; 00514 00515 LWIP_UNUSED_ARG(res); 00516 if (!domain || !addr) { 00517 return ERR_ARG; 00518 } 00519 memset(domain, 0, sizeof(struct mdns_domain)); 00520 ptr = (const u8_t *) addr; 00521 for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) { 00522 char buf[4]; 00523 u8_t val = ptr[i]; 00524 00525 lwip_itoa(buf, sizeof(buf), val); 00526 res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf)); 00527 LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); 00528 } 00529 res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN) - 1)); 00530 LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); 00531 res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1)); 00532 LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); 00533 res = mdns_domain_add_label(domain, NULL, 0); 00534 LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); 00535 00536 return ERR_OK; 00537 } 00538 #endif 00539 00540 #if LWIP_IPV6 00541 /** 00542 * Build domain for reverse lookup of IP address 00543 * 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 00544 * @param domain Where to write the domain name 00545 * @param addr Pointer to an IPv6 address to encode 00546 * @return ERR_OK if domain was written, an err_t otherwise 00547 */ 00548 static err_t 00549 mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr) 00550 { 00551 int i; 00552 err_t res; 00553 const u8_t *ptr; 00554 LWIP_UNUSED_ARG(res); 00555 if (!domain || !addr) { 00556 return ERR_ARG; 00557 } 00558 memset(domain, 0, sizeof(struct mdns_domain)); 00559 ptr = (const u8_t *) addr; 00560 for (i = sizeof(ip6_addr_p_t) - 1; i >= 0; i--) { 00561 char buf; 00562 u8_t byte = ptr[i]; 00563 int j; 00564 for (j = 0; j < 2; j++) { 00565 if ((byte & 0x0F) < 0xA) { 00566 buf = '0' + (byte & 0x0F); 00567 } else { 00568 buf = 'a' + (byte & 0x0F) - 0xA; 00569 } 00570 res = mdns_domain_add_label(domain, &buf, sizeof(buf)); 00571 LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); 00572 byte >>= 4; 00573 } 00574 } 00575 res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN) - 1)); 00576 LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); 00577 res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1)); 00578 LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); 00579 res = mdns_domain_add_label(domain, NULL, 0); 00580 LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); 00581 00582 return ERR_OK; 00583 } 00584 #endif 00585 00586 /* Add .local. to domain */ 00587 static err_t 00588 mdns_add_dotlocal(struct mdns_domain *domain) 00589 { 00590 err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL) - 1)); 00591 LWIP_UNUSED_ARG(res); 00592 LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res); 00593 return mdns_domain_add_label(domain, NULL, 0); 00594 } 00595 00596 /** 00597 * Build the <hostname>.local. domain name 00598 * @param domain Where to write the domain name 00599 * @param mdns TMDNS netif descriptor. 00600 * @return ERR_OK if domain <hostname>.local. was written, an err_t otherwise 00601 */ 00602 static err_t 00603 mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns) 00604 { 00605 err_t res; 00606 LWIP_UNUSED_ARG(res); 00607 memset(domain, 0, sizeof(struct mdns_domain)); 00608 LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL); 00609 res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name)); 00610 LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res); 00611 return mdns_add_dotlocal(domain); 00612 } 00613 00614 /** 00615 * Build the lookup-all-services special DNS-SD domain name 00616 * @param domain Where to write the domain name 00617 * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise 00618 */ 00619 static err_t 00620 mdns_build_dnssd_domain(struct mdns_domain *domain) 00621 { 00622 err_t res; 00623 LWIP_UNUSED_ARG(res); 00624 memset(domain, 0, sizeof(struct mdns_domain)); 00625 res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services") - 1)); 00626 LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); 00627 res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd") - 1)); 00628 LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); 00629 res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP])); 00630 LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); 00631 return mdns_add_dotlocal(domain); 00632 } 00633 00634 /** 00635 * Build domain name for a service 00636 * @param domain Where to write the domain name 00637 * @param service The service struct, containing service name, type and protocol 00638 * @param include_name Whether to include the service name in the domain 00639 * @return ERR_OK if domain was written. If service name is included, 00640 * <name>.<type>.<proto>.local. will be written, otherwise <type>.<proto>.local. 00641 * An err_t is returned on error. 00642 */ 00643 static err_t 00644 mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name) 00645 { 00646 err_t res; 00647 LWIP_UNUSED_ARG(res); 00648 memset(domain, 0, sizeof(struct mdns_domain)); 00649 if (include_name) { 00650 res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name)); 00651 LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); 00652 } 00653 res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service)); 00654 LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); 00655 res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto])); 00656 LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); 00657 return mdns_add_dotlocal(domain); 00658 } 00659 00660 /** 00661 * Check which replies we should send for a host/netif based on question 00662 * @param netif The network interface that received the question 00663 * @param rr Domain/type/class from a question 00664 * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for 00665 * if reply bit has REPLY_HOST_PTR_V6 set 00666 * @return Bitmask of which replies to send 00667 */ 00668 static int 00669 check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply) 00670 { 00671 err_t res; 00672 int replies = 0; 00673 struct mdns_domain mydomain; 00674 00675 LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */ 00676 00677 if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) { 00678 /* Invalid class */ 00679 return replies; 00680 } 00681 00682 /* Handle PTR for our addresses */ 00683 if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) { 00684 #if LWIP_IPV6 00685 int i; 00686 for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { 00687 if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { 00688 res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i)); 00689 if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { 00690 replies |= REPLY_HOST_PTR_V6; 00691 /* Mark which addresses where requested */ 00692 if (reverse_v6_reply) { 00693 *reverse_v6_reply |= (1 << i); 00694 } 00695 } 00696 } 00697 } 00698 #endif 00699 #if LWIP_IPV4 00700 if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) { 00701 res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif)); 00702 if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { 00703 replies |= REPLY_HOST_PTR_V4; 00704 } 00705 } 00706 #endif 00707 } 00708 00709 res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif)); 00710 /* Handle requests for our hostname */ 00711 if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { 00712 /* TODO return NSEC if unsupported protocol requested */ 00713 #if LWIP_IPV4 00714 if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) 00715 && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) { 00716 replies |= REPLY_HOST_A; 00717 } 00718 #endif 00719 #if LWIP_IPV6 00720 if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) { 00721 replies |= REPLY_HOST_AAAA; 00722 } 00723 #endif 00724 } 00725 00726 return replies; 00727 } 00728 00729 /** 00730 * Check which replies we should send for a service based on question 00731 * @param service A registered MDNS service 00732 * @param rr Domain/type/class from a question 00733 * @return Bitmask of which replies to send 00734 */ 00735 static int 00736 check_service(struct mdns_service *service, struct mdns_rr_info *rr) 00737 { 00738 err_t res; 00739 int replies = 0; 00740 struct mdns_domain mydomain; 00741 00742 if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) { 00743 /* Invalid class */ 00744 return 0; 00745 } 00746 00747 res = mdns_build_dnssd_domain(&mydomain); 00748 if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) && 00749 (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) { 00750 /* Request for all service types */ 00751 replies |= REPLY_SERVICE_TYPE_PTR; 00752 } 00753 00754 res = mdns_build_service_domain(&mydomain, service, 0); 00755 if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) && 00756 (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) { 00757 /* Request for the instance of my service */ 00758 replies |= REPLY_SERVICE_NAME_PTR; 00759 } 00760 00761 res = mdns_build_service_domain(&mydomain, service, 1); 00762 if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { 00763 /* Request for info about my service */ 00764 if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) { 00765 replies |= REPLY_SERVICE_SRV; 00766 } 00767 if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) { 00768 replies |= REPLY_SERVICE_TXT; 00769 } 00770 } 00771 00772 return replies; 00773 } 00774 00775 /** 00776 * Return bytes needed to write before jump for best result of compressing supplied domain 00777 * against domain in outpacket starting at specified offset. 00778 * If a match is found, offset is updated to where to jump to 00779 * @param pbuf Pointer to pbuf with the partially constructed DNS packet 00780 * @param offset Start position of a domain written earlier. If this location is suitable 00781 * for compression, the pointer is updated to where in the domain to jump to. 00782 * @param domain The domain to write 00783 * @return Number of bytes to write of the new domain before writing a jump to the offset. 00784 * If compression can not be done against this previous domain name, the full new 00785 * domain length is returned. 00786 */ 00787 u16_t 00788 mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain) 00789 { 00790 struct mdns_domain target; 00791 u16_t target_end; 00792 u8_t target_len; 00793 u8_t writelen = 0; 00794 u8_t *ptr; 00795 if (pbuf == NULL) { 00796 return domain->length; 00797 } 00798 target_end = mdns_readname(pbuf, *offset, &target); 00799 if (target_end == MDNS_READNAME_ERROR) { 00800 return domain->length; 00801 } 00802 target_len = (u8_t)(target_end - *offset); 00803 ptr = domain->name; 00804 while (writelen < domain->length) { 00805 u8_t domainlen = (u8_t)(domain->length - writelen); 00806 u8_t labellen; 00807 if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) { 00808 /* Compare domains if target is long enough, and we have enough left of the domain */ 00809 u8_t targetpos = (u8_t)(target.length - domainlen); 00810 if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) { 00811 /* We are checking at or beyond a jump in the original, stop looking */ 00812 break; 00813 } 00814 if (target.length >= domainlen && 00815 memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) { 00816 *offset += targetpos; 00817 return writelen; 00818 } 00819 } 00820 /* Skip to next label in domain */ 00821 labellen = *ptr; 00822 writelen += 1 + labellen; 00823 ptr += 1 + labellen; 00824 } 00825 /* Nothing found */ 00826 return domain->length; 00827 } 00828 00829 /** 00830 * Write domain to outpacket. Compression will be attempted, 00831 * unless domain->skip_compression is set. 00832 * @param outpkt The outpacket to write to 00833 * @param domain The domain name to write 00834 * @return ERR_OK on success, an err_t otherwise 00835 */ 00836 static err_t 00837 mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain) 00838 { 00839 int i; 00840 err_t res; 00841 u16_t writelen = domain->length; 00842 u16_t jump_offset = 0; 00843 u16_t jump; 00844 00845 if (!domain->skip_compression) { 00846 for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) { 00847 u16_t offset = outpkt->domain_offsets[i]; 00848 if (offset) { 00849 u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain); 00850 if (len < writelen) { 00851 writelen = len; 00852 jump_offset = offset; 00853 } 00854 } 00855 } 00856 } 00857 00858 if (writelen) { 00859 /* Write uncompressed part of name */ 00860 res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset); 00861 if (res != ERR_OK) { 00862 return res; 00863 } 00864 00865 /* Store offset of this new domain */ 00866 for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) { 00867 if (outpkt->domain_offsets[i] == 0) { 00868 outpkt->domain_offsets[i] = outpkt->write_offset; 00869 break; 00870 } 00871 } 00872 00873 outpkt->write_offset += writelen; 00874 } 00875 if (jump_offset) { 00876 /* Write jump */ 00877 jump = lwip_htons(DOMAIN_JUMP | jump_offset); 00878 res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset); 00879 if (res != ERR_OK) { 00880 return res; 00881 } 00882 outpkt->write_offset += DOMAIN_JUMP_SIZE; 00883 } 00884 return ERR_OK; 00885 } 00886 00887 /** 00888 * Write a question to an outpacket 00889 * A question contains domain, type and class. Since an answer also starts with these fields this function is also 00890 * called from mdns_add_answer(). 00891 * @param outpkt The outpacket to write to 00892 * @param domain The domain name the answer is for 00893 * @param type The DNS type of the answer (like 'AAAA', 'SRV') 00894 * @param klass The DNS type of the answer (like 'IN') 00895 * @param unicast If highest bit in class should be set, to instruct the responder to 00896 * reply with a unicast packet 00897 * @return ERR_OK on success, an err_t otherwise 00898 */ 00899 static err_t 00900 mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast) 00901 { 00902 u16_t question_len; 00903 u16_t field16; 00904 err_t res; 00905 00906 if (!outpkt->pbuf) { 00907 /* If no pbuf is active, allocate one */ 00908 outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM); 00909 if (!outpkt->pbuf) { 00910 return ERR_MEM; 00911 } 00912 outpkt->write_offset = SIZEOF_DNS_HDR; 00913 } 00914 00915 /* Worst case calculation. Domain string might be compressed */ 00916 question_len = domain->length + sizeof(type) + sizeof(klass); 00917 if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) { 00918 /* No space */ 00919 return ERR_MEM; 00920 } 00921 00922 /* Write name */ 00923 res = mdns_write_domain(outpkt, domain); 00924 if (res != ERR_OK) { 00925 return res; 00926 } 00927 00928 /* Write type */ 00929 field16 = lwip_htons(type); 00930 res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset); 00931 if (res != ERR_OK) { 00932 return res; 00933 } 00934 outpkt->write_offset += sizeof(field16); 00935 00936 /* Write class */ 00937 if (unicast) { 00938 klass |= 0x8000; 00939 } 00940 field16 = lwip_htons(klass); 00941 res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset); 00942 if (res != ERR_OK) { 00943 return res; 00944 } 00945 outpkt->write_offset += sizeof(field16); 00946 00947 return ERR_OK; 00948 } 00949 00950 /** 00951 * Write answer to reply packet. 00952 * buf or answer_domain can be null. The rd_length written will be buf_length + 00953 * size of (compressed) domain. Most uses will need either buf or answer_domain, 00954 * special case is SRV that starts with 3 u16 and then a domain name. 00955 * @param reply The outpacket to write to 00956 * @param domain The domain name the answer is for 00957 * @param type The DNS type of the answer (like 'AAAA', 'SRV') 00958 * @param klass The DNS type of the answer (like 'IN') 00959 * @param cache_flush If highest bit in class should be set, to instruct receiver that 00960 * this reply replaces any earlier answer for this domain/type/class 00961 * @param ttl Validity time in seconds to send out for IP address data in DNS replies 00962 * @param buf Pointer to buffer of answer data 00963 * @param buf_length Length of variable data 00964 * @param answer_domain A domain to write after any buffer data as answer 00965 * @return ERR_OK on success, an err_t otherwise 00966 */ 00967 static err_t 00968 mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush, 00969 u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain) 00970 { 00971 u16_t answer_len; 00972 u16_t field16; 00973 u16_t rdlen_offset; 00974 u16_t answer_offset; 00975 u32_t field32; 00976 err_t res; 00977 00978 if (!reply->pbuf) { 00979 /* If no pbuf is active, allocate one */ 00980 reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM); 00981 if (!reply->pbuf) { 00982 return ERR_MEM; 00983 } 00984 reply->write_offset = SIZEOF_DNS_HDR; 00985 } 00986 00987 /* Worst case calculation. Domain strings might be compressed */ 00988 answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/; 00989 if (buf) { 00990 answer_len += (u16_t)buf_length; 00991 } 00992 if (answer_domain) { 00993 answer_len += answer_domain->length; 00994 } 00995 if (reply->write_offset + answer_len > reply->pbuf->tot_len) { 00996 /* No space */ 00997 return ERR_MEM; 00998 } 00999 01000 /* Answer starts with same data as question, then more fields */ 01001 mdns_add_question(reply, domain, type, klass, cache_flush); 01002 01003 /* Write TTL */ 01004 field32 = lwip_htonl(ttl); 01005 res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset); 01006 if (res != ERR_OK) { 01007 return res; 01008 } 01009 reply->write_offset += sizeof(field32); 01010 01011 /* Store offsets and skip forward to the data */ 01012 rdlen_offset = reply->write_offset; 01013 reply->write_offset += sizeof(field16); 01014 answer_offset = reply->write_offset; 01015 01016 if (buf) { 01017 /* Write static data */ 01018 res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset); 01019 if (res != ERR_OK) { 01020 return res; 01021 } 01022 reply->write_offset += (u16_t)buf_length; 01023 } 01024 01025 if (answer_domain) { 01026 /* Write name answer (compressed if possible) */ 01027 res = mdns_write_domain(reply, answer_domain); 01028 if (res != ERR_OK) { 01029 return res; 01030 } 01031 } 01032 01033 /* Write rd_length after when we know the answer size */ 01034 field16 = lwip_htons(reply->write_offset - answer_offset); 01035 res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset); 01036 01037 return res; 01038 } 01039 01040 /** 01041 * Helper function for mdns_read_question/mdns_read_answer 01042 * Reads a domain, type and class from the packet 01043 * @param pkt The MDNS packet to read from. The parse_offset field will be 01044 * incremented to point to the next unparsed byte. 01045 * @param info The struct to fill with domain, type and class 01046 * @return ERR_OK on success, an err_t otherwise 01047 */ 01048 static err_t 01049 mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info) 01050 { 01051 u16_t field16, copied; 01052 pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain); 01053 if (pkt->parse_offset == MDNS_READNAME_ERROR) { 01054 return ERR_VAL; 01055 } 01056 01057 copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); 01058 if (copied != sizeof(field16)) { 01059 return ERR_VAL; 01060 } 01061 pkt->parse_offset += copied; 01062 info->type = lwip_ntohs(field16); 01063 01064 copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); 01065 if (copied != sizeof(field16)) { 01066 return ERR_VAL; 01067 } 01068 pkt->parse_offset += copied; 01069 info->klass = lwip_ntohs(field16); 01070 01071 return ERR_OK; 01072 } 01073 01074 /** 01075 * Read a question from the packet. 01076 * All questions have to be read before the answers. 01077 * @param pkt The MDNS packet to read from. The questions_left field will be decremented 01078 * and the parse_offset will be updated. 01079 * @param question The struct to fill with question data 01080 * @return ERR_OK on success, an err_t otherwise 01081 */ 01082 static err_t 01083 mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question) 01084 { 01085 /* Safety check */ 01086 if (pkt->pbuf->tot_len < pkt->parse_offset) { 01087 return ERR_VAL; 01088 } 01089 01090 if (pkt->questions_left) { 01091 err_t res; 01092 pkt->questions_left--; 01093 01094 memset(question, 0, sizeof(struct mdns_question)); 01095 res = mdns_read_rr_info(pkt, &question->info); 01096 if (res != ERR_OK) { 01097 return res; 01098 } 01099 01100 /* Extract unicast flag from class field */ 01101 question->unicast = question->info.klass & 0x8000; 01102 question->info.klass &= 0x7FFF; 01103 01104 return ERR_OK; 01105 } 01106 return ERR_VAL; 01107 } 01108 01109 /** 01110 * Read an answer from the packet 01111 * The variable length reply is not copied, its pbuf offset and length is stored instead. 01112 * @param pkt The MDNS packet to read. The answers_left field will be decremented and 01113 * the parse_offset will be updated. 01114 * @param answer The struct to fill with answer data 01115 * @return ERR_OK on success, an err_t otherwise 01116 */ 01117 static err_t 01118 mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer) 01119 { 01120 /* Read questions first */ 01121 if (pkt->questions_left) { 01122 return ERR_VAL; 01123 } 01124 01125 /* Safety check */ 01126 if (pkt->pbuf->tot_len < pkt->parse_offset) { 01127 return ERR_VAL; 01128 } 01129 01130 if (pkt->answers_left) { 01131 u16_t copied, field16; 01132 u32_t ttl; 01133 err_t res; 01134 pkt->answers_left--; 01135 01136 memset(answer, 0, sizeof(struct mdns_answer)); 01137 res = mdns_read_rr_info(pkt, &answer->info); 01138 if (res != ERR_OK) { 01139 return res; 01140 } 01141 01142 /* Extract cache_flush flag from class field */ 01143 answer->cache_flush = answer->info.klass & 0x8000; 01144 answer->info.klass &= 0x7FFF; 01145 01146 copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset); 01147 if (copied != sizeof(ttl)) { 01148 return ERR_VAL; 01149 } 01150 pkt->parse_offset += copied; 01151 answer->ttl = lwip_ntohl(ttl); 01152 01153 copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); 01154 if (copied != sizeof(field16)) { 01155 return ERR_VAL; 01156 } 01157 pkt->parse_offset += copied; 01158 answer->rd_length = lwip_ntohs(field16); 01159 01160 answer->rd_offset = pkt->parse_offset; 01161 pkt->parse_offset += answer->rd_length; 01162 01163 return ERR_OK; 01164 } 01165 return ERR_VAL; 01166 } 01167 01168 #if LWIP_IPV4 01169 /** Write an IPv4 address (A) RR to outpacket */ 01170 static err_t 01171 mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif) 01172 { 01173 struct mdns_domain host; 01174 mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); 01175 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n")); 01176 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); 01177 } 01178 01179 /** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */ 01180 static err_t 01181 mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif) 01182 { 01183 struct mdns_domain host, revhost; 01184 mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); 01185 mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif)); 01186 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n")); 01187 return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host); 01188 } 01189 #endif 01190 01191 #if LWIP_IPV6 01192 /** Write an IPv6 address (AAAA) RR to outpacket */ 01193 static err_t 01194 mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex) 01195 { 01196 struct mdns_domain host; 01197 mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); 01198 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n")); 01199 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); 01200 } 01201 01202 /** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */ 01203 static err_t 01204 mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex) 01205 { 01206 struct mdns_domain host, revhost; 01207 mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); 01208 mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex)); 01209 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n")); 01210 return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host); 01211 } 01212 #endif 01213 01214 /** Write an all-services -> servicetype PTR RR to outpacket */ 01215 static err_t 01216 mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service) 01217 { 01218 struct mdns_domain service_type, service_dnssd; 01219 mdns_build_service_domain(&service_type, service, 0); 01220 mdns_build_dnssd_domain(&service_dnssd); 01221 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n")); 01222 return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type); 01223 } 01224 01225 /** Write a servicetype -> servicename PTR RR to outpacket */ 01226 static err_t 01227 mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service) 01228 { 01229 struct mdns_domain service_type, service_instance; 01230 mdns_build_service_domain(&service_type, service, 0); 01231 mdns_build_service_domain(&service_instance, service, 1); 01232 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n")); 01233 return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance); 01234 } 01235 01236 /** Write a SRV RR to outpacket */ 01237 static err_t 01238 mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service) 01239 { 01240 struct mdns_domain service_instance, srvhost; 01241 u16_t srvdata[3]; 01242 mdns_build_service_domain(&service_instance, service, 1); 01243 mdns_build_host_domain(&srvhost, mdns); 01244 if (reply->legacy_query) { 01245 /* RFC 6762 section 18.14: 01246 * In legacy unicast responses generated to answer legacy queries, 01247 * name compression MUST NOT be performed on SRV records. 01248 */ 01249 srvhost.skip_compression = 1; 01250 } 01251 srvdata[0] = lwip_htons(SRV_PRIORITY); 01252 srvdata[1] = lwip_htons(SRV_WEIGHT); 01253 srvdata[2] = lwip_htons(service->port); 01254 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n")); 01255 return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl, 01256 (const u8_t *) &srvdata, sizeof(srvdata), &srvhost); 01257 } 01258 01259 /** Write a TXT RR to outpacket */ 01260 static err_t 01261 mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service) 01262 { 01263 struct mdns_domain service_instance; 01264 mdns_build_service_domain(&service_instance, service, 1); 01265 mdns_prepare_txtdata(service); 01266 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n")); 01267 return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl, 01268 (u8_t *) &service->txtdata.name, service->txtdata.length, NULL); 01269 } 01270 01271 /** 01272 * Setup outpacket as a reply to the incoming packet 01273 */ 01274 static void 01275 mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in) 01276 { 01277 memset(out, 0, sizeof(struct mdns_outpacket)); 01278 out->cache_flush = 1; 01279 out->netif = in->netif; 01280 01281 /* Copy source IP/port to use when responding unicast, or to choose 01282 * which pcb to use for multicast (IPv4/IPv6) 01283 */ 01284 SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t)); 01285 out->dest_port = in->source_port; 01286 01287 if (in->source_port != LWIP_IANA_PORT_MDNS) { 01288 out->unicast_reply = 1; 01289 out->cache_flush = 0; 01290 if (in->questions == 1) { 01291 out->legacy_query = 1; 01292 out->tx_id = in->tx_id; 01293 } 01294 } 01295 01296 if (in->recv_unicast) { 01297 out->unicast_reply = 1; 01298 } 01299 } 01300 01301 /** 01302 * Send chosen answers as a reply 01303 * 01304 * Add all selected answers (first write will allocate pbuf) 01305 * Add additional answers based on the selected answers 01306 * Send the packet 01307 */ 01308 static err_t 01309 mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags) 01310 { 01311 struct mdns_service *service; 01312 err_t res = ERR_ARG; 01313 int i; 01314 struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif); 01315 u16_t answers = 0; 01316 01317 /* Write answers to host questions */ 01318 #if LWIP_IPV4 01319 if (outpkt->host_replies & REPLY_HOST_A) { 01320 res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif); 01321 if (res != ERR_OK) { 01322 goto cleanup; 01323 } 01324 answers++; 01325 } 01326 if (outpkt->host_replies & REPLY_HOST_PTR_V4) { 01327 res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif); 01328 if (res != ERR_OK) { 01329 goto cleanup; 01330 } 01331 answers++; 01332 } 01333 #endif 01334 #if LWIP_IPV6 01335 if (outpkt->host_replies & REPLY_HOST_AAAA) { 01336 int addrindex; 01337 for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) { 01338 if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) { 01339 res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); 01340 if (res != ERR_OK) { 01341 goto cleanup; 01342 } 01343 answers++; 01344 } 01345 } 01346 } 01347 if (outpkt->host_replies & REPLY_HOST_PTR_V6) { 01348 u8_t rev_addrs = outpkt->host_reverse_v6_replies; 01349 int addrindex = 0; 01350 while (rev_addrs) { 01351 if (rev_addrs & 1) { 01352 res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); 01353 if (res != ERR_OK) { 01354 goto cleanup; 01355 } 01356 answers++; 01357 } 01358 addrindex++; 01359 rev_addrs >>= 1; 01360 } 01361 } 01362 #endif 01363 01364 /* Write answers to service questions */ 01365 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 01366 service = mdns->services[i]; 01367 if (!service) { 01368 continue; 01369 } 01370 01371 if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) { 01372 res = mdns_add_servicetype_ptr_answer(outpkt, service); 01373 if (res != ERR_OK) { 01374 goto cleanup; 01375 } 01376 answers++; 01377 } 01378 01379 if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) { 01380 res = mdns_add_servicename_ptr_answer(outpkt, service); 01381 if (res != ERR_OK) { 01382 goto cleanup; 01383 } 01384 answers++; 01385 } 01386 01387 if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) { 01388 res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service); 01389 if (res != ERR_OK) { 01390 goto cleanup; 01391 } 01392 answers++; 01393 } 01394 01395 if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) { 01396 res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service); 01397 if (res != ERR_OK) { 01398 goto cleanup; 01399 } 01400 answers++; 01401 } 01402 } 01403 01404 /* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */ 01405 if (flags & DNS_FLAG1_RESPONSE) { 01406 outpkt->answers += answers; 01407 } else { 01408 outpkt->authoritative += answers; 01409 } 01410 01411 /* All answers written, add additional RRs */ 01412 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 01413 service = mdns->services[i]; 01414 if (!service) { 01415 continue; 01416 } 01417 01418 if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) { 01419 /* Our service instance requested, include SRV & TXT 01420 * if they are already not requested. */ 01421 if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) { 01422 res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service); 01423 if (res != ERR_OK) { 01424 goto cleanup; 01425 } 01426 outpkt->additional++; 01427 } 01428 01429 if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) { 01430 res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service); 01431 if (res != ERR_OK) { 01432 goto cleanup; 01433 } 01434 outpkt->additional++; 01435 } 01436 } 01437 01438 /* If service instance, SRV, record or an IP address is requested, 01439 * supply all addresses for the host 01440 */ 01441 if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) || 01442 (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) { 01443 #if LWIP_IPV6 01444 if (!(outpkt->host_replies & REPLY_HOST_AAAA)) { 01445 int addrindex; 01446 for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) { 01447 if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) { 01448 res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); 01449 if (res != ERR_OK) { 01450 goto cleanup; 01451 } 01452 outpkt->additional++; 01453 } 01454 } 01455 } 01456 #endif 01457 #if LWIP_IPV4 01458 if (!(outpkt->host_replies & REPLY_HOST_A) && 01459 !ip4_addr_isany_val(*netif_ip4_addr(outpkt->netif))) { 01460 res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif); 01461 if (res != ERR_OK) { 01462 goto cleanup; 01463 } 01464 outpkt->additional++; 01465 } 01466 #endif 01467 } 01468 } 01469 01470 if (outpkt->pbuf) { 01471 const ip_addr_t *mcast_destaddr; 01472 struct dns_hdr hdr; 01473 01474 /* Write header */ 01475 memset(&hdr, 0, sizeof(hdr)); 01476 hdr.flags1 = flags; 01477 hdr.numquestions = lwip_htons(outpkt->questions); 01478 hdr.numanswers = lwip_htons(outpkt->answers); 01479 hdr.numauthrr = lwip_htons(outpkt->authoritative); 01480 hdr.numextrarr = lwip_htons(outpkt->additional); 01481 hdr.id = lwip_htons(outpkt->tx_id); 01482 pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr)); 01483 01484 /* Shrink packet */ 01485 pbuf_realloc(outpkt->pbuf, outpkt->write_offset); 01486 01487 if (IP_IS_V6_VAL(outpkt->dest_addr)) { 01488 #if LWIP_IPV6 01489 mcast_destaddr = &v6group; 01490 #endif 01491 } else { 01492 #if LWIP_IPV4 01493 mcast_destaddr = &v4group; 01494 #endif 01495 } 01496 /* Send created packet */ 01497 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply)); 01498 if (outpkt->unicast_reply) { 01499 res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif); 01500 } else { 01501 res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif); 01502 } 01503 } 01504 01505 cleanup: 01506 if (outpkt->pbuf) { 01507 pbuf_free(outpkt->pbuf); 01508 outpkt->pbuf = NULL; 01509 } 01510 return res; 01511 } 01512 01513 /** 01514 * Send unsolicited answer containing all our known data 01515 * @param netif The network interface to send on 01516 * @param destination The target address to send to (usually multicast address) 01517 */ 01518 static void 01519 mdns_announce(struct netif *netif, const ip_addr_t *destination) 01520 { 01521 struct mdns_outpacket announce; 01522 int i; 01523 struct mdns_host *mdns = NETIF_TO_HOST(netif); 01524 01525 memset(&announce, 0, sizeof(announce)); 01526 announce.netif = netif; 01527 announce.cache_flush = 1; 01528 #if LWIP_IPV4 01529 if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) { 01530 announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4; 01531 } 01532 #endif 01533 #if LWIP_IPV6 01534 for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { 01535 if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { 01536 announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6; 01537 announce.host_reverse_v6_replies |= (1 << i); 01538 } 01539 } 01540 #endif 01541 01542 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 01543 struct mdns_service *serv = mdns->services[i]; 01544 if (serv) { 01545 announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR | 01546 REPLY_SERVICE_SRV | REPLY_SERVICE_TXT; 01547 } 01548 } 01549 01550 announce.dest_port = LWIP_IANA_PORT_MDNS; 01551 SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr)); 01552 mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE); 01553 } 01554 01555 /** 01556 * Handle question MDNS packet 01557 * 1. Parse all questions and set bits what answers to send 01558 * 2. Clear pending answers if known answers are supplied 01559 * 3. Put chosen answers in new packet and send as reply 01560 */ 01561 static void 01562 mdns_handle_question(struct mdns_packet *pkt) 01563 { 01564 struct mdns_service *service; 01565 struct mdns_outpacket reply; 01566 int replies = 0; 01567 int i; 01568 err_t res; 01569 struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif); 01570 01571 if (mdns->probing_state != MDNS_PROBING_COMPLETE) { 01572 /* Don't answer questions until we've verified our domains via probing */ 01573 /* @todo we should check incoming questions during probing for tiebreaking */ 01574 return; 01575 } 01576 01577 mdns_init_outpacket(&reply, pkt); 01578 01579 while (pkt->questions_left) { 01580 struct mdns_question q; 01581 01582 res = mdns_read_question(pkt, &q); 01583 if (res != ERR_OK) { 01584 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n")); 01585 return; 01586 } 01587 01588 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain ")); 01589 mdns_domain_debug_print(&q.info.domain); 01590 LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass)); 01591 01592 if (q.unicast) { 01593 /* Reply unicast if any question is unicast */ 01594 reply.unicast_reply = 1; 01595 } 01596 01597 reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies); 01598 replies |= reply.host_replies; 01599 01600 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 01601 service = mdns->services[i]; 01602 if (!service) { 01603 continue; 01604 } 01605 reply.serv_replies[i] |= check_service(service, &q.info); 01606 replies |= reply.serv_replies[i]; 01607 } 01608 01609 if (replies && reply.legacy_query) { 01610 /* Add question to reply packet (legacy packet only has 1 question) */ 01611 res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0); 01612 reply.questions = 1; 01613 if (res != ERR_OK) { 01614 goto cleanup; 01615 } 01616 } 01617 } 01618 01619 /* Handle known answers */ 01620 while (pkt->answers_left) { 01621 struct mdns_answer ans; 01622 u8_t rev_v6; 01623 int match; 01624 01625 res = mdns_read_answer(pkt, &ans); 01626 if (res != ERR_OK) { 01627 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n")); 01628 goto cleanup; 01629 } 01630 01631 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain ")); 01632 mdns_domain_debug_print(&ans.info.domain); 01633 LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass)); 01634 01635 01636 if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) { 01637 /* Skip known answers for ANY type & class */ 01638 continue; 01639 } 01640 01641 rev_v6 = 0; 01642 match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6); 01643 if (match && (ans.ttl > (mdns->dns_ttl / 2))) { 01644 /* The RR in the known answer matches an RR we are planning to send, 01645 * and the TTL is less than half gone. 01646 * If the payload matches we should not send that answer. 01647 */ 01648 if (ans.info.type == DNS_RRTYPE_PTR) { 01649 /* Read domain and compare */ 01650 struct mdns_domain known_ans, my_ans; 01651 u16_t len; 01652 len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans); 01653 res = mdns_build_host_domain(&my_ans, mdns); 01654 if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { 01655 #if LWIP_IPV4 01656 if (match & REPLY_HOST_PTR_V4) { 01657 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n")); 01658 reply.host_replies &= ~REPLY_HOST_PTR_V4; 01659 } 01660 #endif 01661 #if LWIP_IPV6 01662 if (match & REPLY_HOST_PTR_V6) { 01663 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n")); 01664 reply.host_reverse_v6_replies &= ~rev_v6; 01665 if (reply.host_reverse_v6_replies == 0) { 01666 reply.host_replies &= ~REPLY_HOST_PTR_V6; 01667 } 01668 } 01669 #endif 01670 } 01671 } else if (match & REPLY_HOST_A) { 01672 #if LWIP_IPV4 01673 if (ans.rd_length == sizeof(ip4_addr_t) && 01674 pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) { 01675 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n")); 01676 reply.host_replies &= ~REPLY_HOST_A; 01677 } 01678 #endif 01679 } else if (match & REPLY_HOST_AAAA) { 01680 #if LWIP_IPV6 01681 if (ans.rd_length == sizeof(ip6_addr_p_t) && 01682 /* TODO this clears all AAAA responses if first addr is set as known */ 01683 pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) { 01684 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n")); 01685 reply.host_replies &= ~REPLY_HOST_AAAA; 01686 } 01687 #endif 01688 } 01689 } 01690 01691 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 01692 service = mdns->services[i]; 01693 if (!service) { 01694 continue; 01695 } 01696 match = reply.serv_replies[i] & check_service(service, &ans.info); 01697 if (match && (ans.ttl > (service->dns_ttl / 2))) { 01698 /* The RR in the known answer matches an RR we are planning to send, 01699 * and the TTL is less than half gone. 01700 * If the payload matches we should not send that answer. 01701 */ 01702 if (ans.info.type == DNS_RRTYPE_PTR) { 01703 /* Read domain and compare */ 01704 struct mdns_domain known_ans, my_ans; 01705 u16_t len; 01706 len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans); 01707 if (len != MDNS_READNAME_ERROR) { 01708 if (match & REPLY_SERVICE_TYPE_PTR) { 01709 res = mdns_build_service_domain(&my_ans, service, 0); 01710 if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { 01711 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n")); 01712 reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR; 01713 } 01714 } 01715 if (match & REPLY_SERVICE_NAME_PTR) { 01716 res = mdns_build_service_domain(&my_ans, service, 1); 01717 if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { 01718 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n")); 01719 reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR; 01720 } 01721 } 01722 } 01723 } else if (match & REPLY_SERVICE_SRV) { 01724 /* Read and compare to my SRV record */ 01725 u16_t field16, len, read_pos; 01726 struct mdns_domain known_ans, my_ans; 01727 read_pos = ans.rd_offset; 01728 do { 01729 /* Check priority field */ 01730 len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); 01731 if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) { 01732 break; 01733 } 01734 read_pos += len; 01735 /* Check weight field */ 01736 len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); 01737 if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) { 01738 break; 01739 } 01740 read_pos += len; 01741 /* Check port field */ 01742 len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); 01743 if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) { 01744 break; 01745 } 01746 read_pos += len; 01747 /* Check host field */ 01748 len = mdns_readname(pkt->pbuf, read_pos, &known_ans); 01749 mdns_build_host_domain(&my_ans, mdns); 01750 if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) { 01751 break; 01752 } 01753 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n")); 01754 reply.serv_replies[i] &= ~REPLY_SERVICE_SRV; 01755 } while (0); 01756 } else if (match & REPLY_SERVICE_TXT) { 01757 mdns_prepare_txtdata(service); 01758 if (service->txtdata.length == ans.rd_length && 01759 pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) { 01760 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n")); 01761 reply.serv_replies[i] &= ~REPLY_SERVICE_TXT; 01762 } 01763 } 01764 } 01765 } 01766 } 01767 01768 mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE); 01769 01770 cleanup: 01771 if (reply.pbuf) { 01772 /* This should only happen if we fail to alloc/write question for legacy query */ 01773 pbuf_free(reply.pbuf); 01774 reply.pbuf = NULL; 01775 } 01776 } 01777 01778 /** 01779 * Handle response MDNS packet 01780 * Only prints debug for now. Will need more code to do conflict resolution. 01781 */ 01782 static void 01783 mdns_handle_response(struct mdns_packet *pkt) 01784 { 01785 struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif); 01786 01787 /* Ignore all questions */ 01788 while (pkt->questions_left) { 01789 struct mdns_question q; 01790 err_t res; 01791 01792 res = mdns_read_question(pkt, &q); 01793 if (res != ERR_OK) { 01794 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n")); 01795 return; 01796 } 01797 } 01798 01799 while (pkt->answers_left) { 01800 struct mdns_answer ans; 01801 err_t res; 01802 01803 res = mdns_read_answer(pkt, &ans); 01804 if (res != ERR_OK) { 01805 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n")); 01806 return; 01807 } 01808 01809 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain ")); 01810 mdns_domain_debug_print(&ans.info.domain); 01811 LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass)); 01812 01813 /*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST 01814 be silently ignored" so drop answer if we haven't started probing yet*/ 01815 if ((mdns->probing_state == MDNS_PROBING_ONGOING) && (mdns->probes_sent > 0)) { 01816 struct mdns_domain domain; 01817 u8_t i; 01818 u8_t conflict = 0; 01819 01820 res = mdns_build_host_domain(&domain, mdns); 01821 if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) { 01822 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!")); 01823 conflict = 1; 01824 } 01825 01826 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 01827 struct mdns_service* service = mdns->services[i]; 01828 if (!service) { 01829 continue; 01830 } 01831 res = mdns_build_service_domain(&domain, service, 1); 01832 if ((res == ERR_OK) && mdns_domain_eq(&ans.info.domain, &domain)) { 01833 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!")); 01834 conflict = 1; 01835 } 01836 } 01837 01838 if (conflict != 0) { 01839 sys_untimeout(mdns_probe, pkt->netif); 01840 if (mdns_name_result_cb != NULL) { 01841 mdns_name_result_cb(pkt->netif, MDNS_PROBING_CONFLICT); 01842 } 01843 } 01844 } 01845 } 01846 } 01847 01848 /** 01849 * Receive input function for MDNS packets. 01850 * Handles both IPv4 and IPv6 UDP pcbs. 01851 */ 01852 static void 01853 mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) 01854 { 01855 struct dns_hdr hdr; 01856 struct mdns_packet packet; 01857 struct netif *recv_netif = ip_current_input_netif(); 01858 u16_t offset = 0; 01859 01860 LWIP_UNUSED_ARG(arg); 01861 LWIP_UNUSED_ARG(pcb); 01862 01863 LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr) ? 6 : 4, p->tot_len)); 01864 01865 if (NETIF_TO_HOST(recv_netif) == NULL) { 01866 /* From netif not configured for MDNS */ 01867 goto dealloc; 01868 } 01869 01870 if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) { 01871 /* Too small */ 01872 goto dealloc; 01873 } 01874 offset += SIZEOF_DNS_HDR; 01875 01876 if (DNS_HDR_GET_OPCODE(&hdr)) { 01877 /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */ 01878 goto dealloc; 01879 } 01880 01881 memset(&packet, 0, sizeof(packet)); 01882 SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr)); 01883 packet.source_port = port; 01884 packet.netif = recv_netif; 01885 packet.pbuf = p; 01886 packet.parse_offset = offset; 01887 packet.tx_id = lwip_ntohs(hdr.id); 01888 packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions); 01889 packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr); 01890 01891 #if LWIP_IPV6 01892 if (IP_IS_V6(ip_current_dest_addr())) { 01893 /* instead of having one 'v6group' per netif, just compare zoneless here */ 01894 if (!ip_addr_cmp_zoneless(ip_current_dest_addr(), &v6group)) { 01895 packet.recv_unicast = 1; 01896 } 01897 } 01898 #endif 01899 #if LWIP_IPV4 01900 if (!IP_IS_V6(ip_current_dest_addr())) { 01901 if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) { 01902 packet.recv_unicast = 1; 01903 } 01904 } 01905 #endif 01906 01907 if (hdr.flags1 & DNS_FLAG1_RESPONSE) { 01908 mdns_handle_response(&packet); 01909 } else { 01910 mdns_handle_question(&packet); 01911 } 01912 01913 dealloc: 01914 pbuf_free(p); 01915 } 01916 01917 #if LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK 01918 static void 01919 mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args) 01920 { 01921 LWIP_UNUSED_ARG(args); 01922 01923 /* MDNS enabled on netif? */ 01924 if (NETIF_TO_HOST(netif) == NULL) { 01925 return; 01926 } 01927 01928 if (reason & LWIP_NSC_STATUS_CHANGED) { 01929 if (args->status_changed.state != 0) { 01930 mdns_resp_restart(netif); 01931 } 01932 /* TODO: send goodbye message */ 01933 } 01934 if (reason & LWIP_NSC_LINK_CHANGED) { 01935 if (args->link_changed.state != 0) { 01936 mdns_resp_restart(netif); 01937 } 01938 } 01939 if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED | 01940 LWIP_NSC_IPV4_NETMASK_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED | 01941 LWIP_NSC_IPV6_SET | LWIP_NSC_IPV6_ADDR_STATE_CHANGED)) { 01942 mdns_resp_announce(netif); 01943 } 01944 } 01945 #endif /* LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK */ 01946 01947 static err_t 01948 mdns_send_probe(struct netif* netif, const ip_addr_t *destination) 01949 { 01950 struct mdns_host* mdns; 01951 struct mdns_outpacket pkt; 01952 struct mdns_domain domain; 01953 u8_t i; 01954 err_t res; 01955 01956 mdns = NETIF_TO_HOST(netif); 01957 01958 memset(&pkt, 0, sizeof(pkt)); 01959 pkt.netif = netif; 01960 01961 /* Add unicast questions with rtype ANY for all our desired records */ 01962 mdns_build_host_domain(&domain, mdns); 01963 res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1); 01964 if (res != ERR_OK) { 01965 goto cleanup; 01966 } 01967 pkt.questions++; 01968 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 01969 struct mdns_service* service = mdns->services[i]; 01970 if (!service) { 01971 continue; 01972 } 01973 mdns_build_service_domain(&domain, service, 1); 01974 res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1); 01975 if (res != ERR_OK) { 01976 goto cleanup; 01977 } 01978 pkt.questions++; 01979 } 01980 01981 /* Add answers to the questions above into the authority section for tiebreaking */ 01982 #if LWIP_IPV4 01983 if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) { 01984 pkt.host_replies = REPLY_HOST_A; 01985 } 01986 #endif 01987 #if LWIP_IPV6 01988 for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { 01989 if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { 01990 pkt.host_replies |= REPLY_HOST_AAAA; 01991 } 01992 } 01993 #endif 01994 01995 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 01996 struct mdns_service *serv = mdns->services[i]; 01997 if (serv) { 01998 pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT; 01999 } 02000 } 02001 02002 pkt.tx_id = 0; 02003 pkt.dest_port = LWIP_IANA_PORT_MDNS; 02004 SMEMCPY(&pkt.dest_addr, destination, sizeof(pkt.dest_addr)); 02005 res = mdns_send_outpacket(&pkt, 0); 02006 02007 cleanup: 02008 if (pkt.pbuf) { 02009 pbuf_free(pkt.pbuf); 02010 pkt.pbuf = NULL; 02011 } 02012 return res; 02013 } 02014 02015 /** 02016 * Timer callback for probing network. 02017 */ 02018 static void 02019 mdns_probe(void* arg) 02020 { 02021 struct netif *netif = (struct netif *)arg; 02022 struct mdns_host* mdns = NETIF_TO_HOST(netif); 02023 02024 if(mdns->probes_sent >= MDNS_PROBE_COUNT) { 02025 /* probing successful, announce the new name */ 02026 mdns->probing_state = MDNS_PROBING_COMPLETE; 02027 mdns_resp_announce(netif); 02028 if (mdns_name_result_cb != NULL) { 02029 mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL); 02030 } 02031 } else { 02032 #if LWIP_IPV4 02033 /*if ipv4 wait with probing until address is set*/ 02034 if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) && 02035 mdns_send_probe(netif, IP4_ADDR_ANY) == ERR_OK) 02036 #endif 02037 { 02038 #if LWIP_IPV6 02039 if (mdns_send_probe(netif, IP6_ADDR_ANY) == ERR_OK) 02040 #endif 02041 { 02042 mdns->probes_sent++; 02043 } 02044 } 02045 sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe, netif); 02046 } 02047 } 02048 02049 /** 02050 * @ingroup mdns 02051 * Activate MDNS responder for a network interface. 02052 * @param netif The network interface to activate. 02053 * @param hostname Name to use. Queries for <hostname>.local will be answered 02054 * with the IP addresses of the netif. The hostname will be copied, the 02055 * given pointer can be on the stack. 02056 * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies 02057 * @return ERR_OK if netif was added, an err_t otherwise 02058 */ 02059 err_t 02060 mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl) 02061 { 02062 err_t res; 02063 struct mdns_host *mdns; 02064 02065 LWIP_ASSERT_CORE_LOCKED(); 02066 LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL); 02067 LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL); 02068 02069 LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL); 02070 mdns = (struct mdns_host *) mem_calloc(1, sizeof(struct mdns_host)); 02071 LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM); 02072 02073 netif_set_client_data(netif, mdns_netif_client_id, mdns); 02074 02075 MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname))); 02076 mdns->dns_ttl = dns_ttl; 02077 mdns->probes_sent = 0; 02078 mdns->probing_state = MDNS_PROBING_NOT_STARTED; 02079 02080 /* Join multicast groups */ 02081 #if LWIP_IPV4 02082 res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group)); 02083 if (res != ERR_OK) { 02084 goto cleanup; 02085 } 02086 #endif 02087 #if LWIP_IPV6 02088 res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group)); 02089 if (res != ERR_OK) { 02090 goto cleanup; 02091 } 02092 #endif 02093 02094 mdns_resp_restart(netif); 02095 02096 return ERR_OK; 02097 02098 cleanup: 02099 mem_free(mdns); 02100 netif_set_client_data(netif, mdns_netif_client_id, NULL); 02101 return res; 02102 } 02103 02104 /** 02105 * @ingroup mdns 02106 * Stop responding to MDNS queries on this interface, leave multicast groups, 02107 * and free the helper structure and any of its services. 02108 * @param netif The network interface to remove. 02109 * @return ERR_OK if netif was removed, an err_t otherwise 02110 */ 02111 err_t 02112 mdns_resp_remove_netif(struct netif *netif) 02113 { 02114 int i; 02115 struct mdns_host *mdns; 02116 02117 LWIP_ASSERT_CORE_LOCKED(); 02118 LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif); 02119 mdns = NETIF_TO_HOST(netif); 02120 LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL); 02121 02122 if (mdns->probing_state == MDNS_PROBING_ONGOING) { 02123 sys_untimeout(mdns_probe, netif); 02124 } 02125 02126 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 02127 struct mdns_service *service = mdns->services[i]; 02128 if (service) { 02129 mem_free(service); 02130 } 02131 } 02132 02133 /* Leave multicast groups */ 02134 #if LWIP_IPV4 02135 igmp_leavegroup_netif(netif, ip_2_ip4(&v4group)); 02136 #endif 02137 #if LWIP_IPV6 02138 mld6_leavegroup_netif(netif, ip_2_ip6(&v6group)); 02139 #endif 02140 02141 mem_free(mdns); 02142 netif_set_client_data(netif, mdns_netif_client_id, NULL); 02143 return ERR_OK; 02144 } 02145 02146 /** 02147 * @ingroup mdns 02148 * Update MDNS hostname for a network interface. 02149 * @param netif The network interface to activate. 02150 * @param hostname Name to use. Queries for <hostname>.local will be answered 02151 * with the IP addresses of the netif. The hostname will be copied, the 02152 * given pointer can be on the stack. 02153 * @return ERR_OK if name could be set on netif, an err_t otherwise 02154 */ 02155 err_t 02156 mdns_resp_rename_netif(struct netif *netif, const char *hostname) 02157 { 02158 struct mdns_host *mdns; 02159 size_t len; 02160 02161 LWIP_ASSERT_CORE_LOCKED(); 02162 len = strlen(hostname); 02163 LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL); 02164 LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL); 02165 mdns = NETIF_TO_HOST(netif); 02166 LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL); 02167 02168 MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, len)); 02169 mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */ 02170 02171 mdns_resp_restart(netif); 02172 02173 return ERR_OK; 02174 } 02175 02176 /** 02177 * @ingroup mdns 02178 * Add a service to the selected network interface. 02179 * @param netif The network interface to publish this service on 02180 * @param name The name of the service 02181 * @param service The service type, like "_http" 02182 * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP 02183 * for others ("_udp") 02184 * @param port The port the service listens to 02185 * @param dns_ttl Validity time in seconds to send out for service data in DNS replies 02186 * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to 02187 * allow dynamic replies. 02188 * @param txt_data Userdata pointer for txt_fn 02189 * @return service_id if the service was added to the netif, an err_t otherwise 02190 */ 02191 s8_t 02192 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) 02193 { 02194 s8_t i; 02195 s8_t slot = -1; 02196 struct mdns_service *srv; 02197 struct mdns_host *mdns; 02198 02199 LWIP_ASSERT_CORE_LOCKED(); 02200 LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif); 02201 mdns = NETIF_TO_HOST(netif); 02202 LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL); 02203 02204 LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL); 02205 LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL); 02206 LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL); 02207 02208 for (i = 0; i < MDNS_MAX_SERVICES; i++) { 02209 if (mdns->services[i] == NULL) { 02210 slot = i; 02211 break; 02212 } 02213 } 02214 LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM); 02215 02216 srv = (struct mdns_service *)mem_calloc(1, sizeof(struct mdns_service)); 02217 LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM); 02218 02219 MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name))); 02220 MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service))); 02221 srv->txt_fn = txt_fn; 02222 srv->txt_userdata = txt_data; 02223 srv->proto = (u16_t)proto; 02224 srv->port = port; 02225 srv->dns_ttl = dns_ttl; 02226 02227 mdns->services[slot] = srv; 02228 02229 mdns_resp_restart(netif); 02230 02231 return slot; 02232 } 02233 02234 /** 02235 * @ingroup mdns 02236 * Delete a service on the selected network interface. 02237 * @param netif The network interface on which service should be removed 02238 * @param slot The service slot number returned by mdns_resp_add_service 02239 * @return ERR_OK if the service was removed from the netif, an err_t otherwise 02240 */ 02241 err_t 02242 mdns_resp_del_service(struct netif *netif, s8_t slot) 02243 { 02244 struct mdns_host *mdns; 02245 struct mdns_service *srv; 02246 LWIP_ASSERT("mdns_resp_del_service: netif != NULL", netif); 02247 mdns = NETIF_TO_HOST(netif); 02248 LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL); 02249 LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL); 02250 LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL); 02251 02252 srv = mdns->services[slot]; 02253 mdns->services[slot] = NULL; 02254 mem_free(srv); 02255 return ERR_OK; 02256 } 02257 02258 /** 02259 * @ingroup mdns 02260 * Update name for an MDNS service. 02261 * @param netif The network interface to activate. 02262 * @param slot The service slot number returned by mdns_resp_add_service 02263 * @param name The new name for the service 02264 * @return ERR_OK if name could be set on service, an err_t otherwise 02265 */ 02266 err_t 02267 mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name) 02268 { 02269 struct mdns_service *srv; 02270 struct mdns_host *mdns; 02271 size_t len; 02272 02273 LWIP_ASSERT_CORE_LOCKED(); 02274 len = strlen(name); 02275 LWIP_ASSERT("mdns_resp_rename_service: netif != NULL", netif); 02276 mdns = NETIF_TO_HOST(netif); 02277 LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL); 02278 LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL); 02279 LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL); 02280 LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL); 02281 02282 srv = mdns->services[slot]; 02283 02284 MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, len)); 02285 srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */ 02286 02287 mdns_resp_restart(netif); 02288 02289 return ERR_OK; 02290 } 02291 02292 /** 02293 * @ingroup mdns 02294 * Call this function from inside the service_get_txt_fn_t callback to add text data. 02295 * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte. 02296 * @param service The service provided to the get_txt callback 02297 * @param txt String to add to the TXT field. 02298 * @param txt_len Length of string 02299 * @return ERR_OK if the string was added to the reply, an err_t otherwise 02300 */ 02301 err_t 02302 mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len) 02303 { 02304 LWIP_ASSERT_CORE_LOCKED(); 02305 LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service); 02306 02307 /* Use a mdns_domain struct to store txt chunks since it is the same encoding */ 02308 return mdns_domain_add_label(&service->txtdata, txt, txt_len); 02309 } 02310 02311 /** 02312 * @ingroup mdns 02313 * Send unsolicited answer containing all our known data 02314 * @param netif The network interface to send on 02315 */ 02316 void 02317 mdns_resp_announce(struct netif *netif) 02318 { 02319 struct mdns_host* mdns; 02320 LWIP_ASSERT_CORE_LOCKED(); 02321 LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return); 02322 02323 mdns = NETIF_TO_HOST(netif); 02324 if (mdns == NULL) { 02325 return; 02326 } 02327 02328 if (mdns->probing_state == MDNS_PROBING_COMPLETE) { 02329 /* Announce on IPv6 and IPv4 */ 02330 #if LWIP_IPV6 02331 mdns_announce(netif, IP6_ADDR_ANY); 02332 #endif 02333 #if LWIP_IPV4 02334 if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) { 02335 mdns_announce(netif, IP4_ADDR_ANY); 02336 } 02337 #endif 02338 } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */ 02339 } 02340 02341 /** Register a callback function that is called if probing is completed successfully 02342 * or with a conflict. */ 02343 void 02344 mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb) 02345 { 02346 mdns_name_result_cb = cb; 02347 } 02348 02349 /** 02350 * @ingroup mdns 02351 * Restart mdns responder. Call this when cable is connected after being disconnected or 02352 * administrative interface is set up after being down 02353 * @param netif The network interface to send on 02354 */ 02355 void 02356 mdns_resp_restart(struct netif *netif) 02357 { 02358 struct mdns_host* mdns; 02359 LWIP_ASSERT_CORE_LOCKED(); 02360 LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return); 02361 02362 mdns = NETIF_TO_HOST(netif); 02363 if (mdns == NULL) { 02364 return; 02365 } 02366 02367 if (mdns->probing_state == MDNS_PROBING_ONGOING) { 02368 sys_untimeout(mdns_probe, netif); 02369 } 02370 /* @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)*/ 02371 mdns->probes_sent = 0; 02372 mdns->probing_state = MDNS_PROBING_ONGOING; 02373 sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe, netif); 02374 } 02375 02376 /** 02377 * @ingroup mdns 02378 * Initiate MDNS responder. Will open UDP sockets on port 5353 02379 */ 02380 void 02381 mdns_resp_init(void) 02382 { 02383 err_t res; 02384 02385 /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */ 02386 02387 mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); 02388 LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL); 02389 #if LWIP_MULTICAST_TX_OPTIONS 02390 udp_set_multicast_ttl(mdns_pcb, MDNS_TTL); 02391 #else 02392 mdns_pcb->ttl = MDNS_TTL; 02393 #endif 02394 res = udp_bind(mdns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_MDNS); 02395 LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */ 02396 LWIP_ASSERT("Failed to bind pcb", res == ERR_OK); 02397 udp_recv(mdns_pcb, mdns_recv, NULL); 02398 02399 mdns_netif_client_id = netif_alloc_client_data_id(); 02400 02401 #if MDNS_RESP_USENETIF_EXTCALLBACK 02402 /* register for netif events when started on first netif */ 02403 netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback); 02404 #endif 02405 } 02406 02407 #endif /* LWIP_MDNS_RESPONDER */
Generated on Tue Jul 12 2022 13:54:29 by
