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