Marco Zecchini
/
Example_RTOS
Rtos API example
Embed:
(wiki syntax)
Show/hide line numbers
lwip_mdns.c
Go to the documentation of this file.
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 Sun Jul 17 2022 08:25:24 by 1.7.2