diff --git a/CHANGELOG b/CHANGELOG index 59e5be95..21376eff 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,9 @@ HISTORY ++ New features: + 2016-12-09: Simon Goldschmidt + * dns.c: added one-shot multicast DNS queries + 2016-11-24: Ambroz Bizjak, David van Moolenbroek * tcp_out.c: Optimize passing contiguous nocopy buffers to tcp_write (bug #46290) diff --git a/src/core/dns.c b/src/core/dns.c index 997a548e..056672b7 100644 --- a/src/core/dns.c +++ b/src/core/dns.c @@ -24,6 +24,11 @@ * the resolver code calls a specified callback function (which * must be implemented by the module that uses the resolver). * + * Multicast DNS queries are supported for names ending on ".local". + * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762 + * chapter 5.1), this is not a fully compliant implementation of continuous + * mDNS querying! + * * All functions must be called from TCPIP thread. * * @see @ref netconn_common for thread-safe access. @@ -71,6 +76,7 @@ /** @todo: define good default values (rfc compliance) */ /** @todo: improve answer parsing, more checkings... */ /** @todo: check RFC1035 - 7.3. Processing responses */ +/** @todo: one-shot mDNS: dual-stack fallback to another IP version */ /*----------------------------------------------------------------------------- * Includes @@ -175,6 +181,12 @@ static u16_t dns_txid; #define LWIP_DNS_SET_ADDRTYPE(x, y) #endif /* LWIP_IPV4 && LWIP_IPV6 */ +#if LWIP_DNS_SUPPORT_MDNS_QUERIES +#define LWIP_DNS_ISMDNS_ARG(x) , x +#else +#define LWIP_DNS_ISMDNS_ARG(x) +#endif + /** DNS query message structure. No packing needed: only used locally on the stack. */ struct dns_query { @@ -224,6 +236,9 @@ struct dns_table_entry { #if LWIP_IPV4 && LWIP_IPV6 u8_t reqaddrtype; #endif /* LWIP_IPV4 && LWIP_IPV6 */ +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + u8_t is_mdns; +#endif }; /** DNS request table entry: used when dns_gehostbyname cannot answer the @@ -287,6 +302,13 @@ static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS]; static ip_addr_t dns_servers[DNS_MAX_SERVERS]; +#if LWIP_IPV4 +const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT; +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 +const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT; +#endif /* LWIP_IPV6 */ + /** * Initialize the resolver: set up the UDP pcb and configure the default server * (if DNS_SERVER_ADDRESS is set). @@ -680,7 +702,11 @@ dns_send(u8_t idx) LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", (u16_t)(entry->server_idx), entry->name)); LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS); - if (ip_addr_isany_val(dns_servers[entry->server_idx])) { + if (ip_addr_isany_val(dns_servers[entry->server_idx]) +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + && !entry->is_mdns +#endif + ) { /* DNS server not valid anymore, e.g. PPP netif has been shut down */ /* call specified callback function if provided */ dns_call_found(idx, NULL); @@ -693,6 +719,8 @@ dns_send(u8_t idx) p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 + SIZEOF_DNS_QUERY), PBUF_RAM); if (p != NULL) { + const ip_addr_t* dst; + u16_t dst_port; /* fill dns header */ memset(&hdr, 0, SIZEOF_DNS_HDR); hdr.id = lwip_htons(entry->txid); @@ -735,7 +763,30 @@ dns_send(u8_t idx) /* send dns packet */ LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n", entry->txid, entry->name, entry->server_idx)); - err = udp_sendto(dns_pcbs[pcb_idx], p, &dns_servers[entry->server_idx], DNS_SERVER_PORT); +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + if (entry->is_mdns) { + dst_port = DNS_MQUERY_PORT; +#if LWIP_IPV6 + if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) + { + dst = &dns_mquery_v6group; + } +#endif +#if LWIP_IPV4 && LWIP_IPV6 + else +#endif +#if LWIP_IPV4 + { + dst = &dns_mquery_v4group; + } +#endif + } else +#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */ + { + dst_port = DNS_SERVER_PORT; + dst = &dns_servers[entry->server_idx]; + } + err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port); /* free pbuf */ pbuf_free(p); @@ -938,7 +989,11 @@ dns_check_entry(u8_t i) case DNS_STATE_ASKING: if (--entry->tmr == 0) { if (++entry->retries == DNS_MAX_RETRIES) { - if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])) { + if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1]) +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + && !entry->is_mdns +#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */ + ) { /* change of server */ entry->server_idx++; entry->tmr = 1; @@ -1075,10 +1130,15 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, goto memerr; /* ignore this packet */ } - /* Check whether response comes from the same network address to which the - question was sent. (RFC 5452) */ - if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) { - goto memerr; /* ignore this packet */ +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + if (!entry->is_mdns) +#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */ + { + /* Check whether response comes from the same network address to which the + question was sent. (RFC 5452) */ + if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) { + goto memerr; /* ignore this packet */ + } } /* Check if the name in the "question" part match with the name in the entry and @@ -1210,7 +1270,7 @@ memerr: */ static err_t dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, - void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)) + void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns)) { u8_t i; u8_t lseq, lseqi; @@ -1326,6 +1386,10 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx))); #endif +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + entry->is_mdns = is_mdns; +#endif + dns_seqno++; /* force to send query without waiting timer */ @@ -1381,6 +1445,9 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call void *callback_arg, u8_t dns_addrtype) { size_t hostnamelen; +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + u8_t is_mdns; +#endif /* not initialized or no valid server yet, or invalid addr pointer * or invalid hostname or invalid hostname length */ if ((addr == NULL) || @@ -1437,13 +1504,25 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call LWIP_UNUSED_ARG(dns_addrtype); #endif /* LWIP_IPV4 && LWIP_IPV6 */ - /* prevent calling found callback if no server is set, return error instead */ - if (ip_addr_isany_val(dns_servers[0])) { - return ERR_VAL; +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) { + is_mdns = 1; + } else { + is_mdns = 0; + } + + if (!is_mdns) +#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */ + { + /* prevent calling found callback if no server is set, return error instead */ + if (ip_addr_isany_val(dns_servers[0])) { + return ERR_VAL; + } } /* queue query with specified callback */ - return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)); + return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype) + LWIP_DNS_ISMDNS_ARG(is_mdns)); } #endif /* LWIP_DNS */ diff --git a/src/include/lwip/dns.h b/src/include/lwip/dns.h index 00d5a78c..3753a53a 100644 --- a/src/include/lwip/dns.h +++ b/src/include/lwip/dns.h @@ -84,6 +84,13 @@ struct local_hostlist_entry { #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ #endif /* DNS_LOCAL_HOSTLIST */ +#if LWIP_IPV4 +extern const ip_addr_t dns_mquery_v4group; +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 +extern const ip_addr_t dns_mquery_v6group; +#endif /* LWIP_IPV6 */ + /** Callback which is invoked when a hostname is found. * A function of this type must be implemented by the application using the DNS resolver. * @param name pointer to the name that was looked up. diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index 61305016..304609be 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -1057,6 +1057,12 @@ #if !defined DNS_LOCAL_HOSTLIST_IS_DYNAMIC || defined __DOXYGEN__ #define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/** Set this to 1 to enable querying ".local" names via mDNS + * using a One-Shot Multicast DNS Query */ +#if !defined LWIP_DNS_SUPPORT_MDNS_QUERIES || defined __DOXYGEN__ +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 0 +#endif /** * @} */ diff --git a/src/include/lwip/prot/dns.h b/src/include/lwip/prot/dns.h index 0a99ab0c..94782d6e 100644 --- a/src/include/lwip/prot/dns.h +++ b/src/include/lwip/prot/dns.h @@ -115,6 +115,24 @@ PACK_STRUCT_END #endif #define SIZEOF_DNS_HDR 12 + +/* Multicast DNS definitions */ + +/** UDP port for multicast DNS queries */ +#ifndef DNS_MQUERY_PORT +#define DNS_MQUERY_PORT 5353 +#endif + +/* IPv4 group for multicast DNS queries: 224.0.0.251 */ +#ifndef DNS_MQUERY_IPV4_GROUP_INIT +#define DNS_MQUERY_IPV4_GROUP_INIT IPADDR4_INIT_BYTES(224,0,0,251) +#endif + +/* IPv6 group for multicast DNS queries: FF02::FB */ +#ifndef DNS_MQUERY_IPV6_GROUP_INIT +#define DNS_MQUERY_IPV6_GROUP_INIT IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB) +#endif + #ifdef __cplusplus } #endif