diff --git a/ChangeLog b/ChangeLog index 81fcdc972c5..ecebfe3ed59 100755 --- a/ChangeLog +++ b/ChangeLog @@ -10702,10 +10702,16 @@ logic in preparation for IPv6 support (2015-07-12): - Rename include/nuttx/net/dnsclient.h to dns.h - Move internal DNS prototypes from dns.h to libc/netdb/lib_dns.h - - Global standard DNS definitions from libc/netdb/dns_soccket.c to dns.h - - Eliminate dns_gethostip(). This is now an internal part of gethostbyname() + - Global standard DNS definitions from libc/netdb/dns_soccket.c to + dns.h + - Eliminate dns_gethostip(). This is now an internal part of + gethostbyname() - Eliminate interface dns_whois(). Not needed in this new context. * include/nuttx/net/ and libc/netdb: Modifications to DNS client logic and to dns_get/setserver() interfaces to support DNS clients at IPv6 - addresses (still no support for resolution to IPv6 addresses) (2015-07-12). + addresses (still no support for resolution to IPv6 addresses) + (2015-07-12). + * include/nuttx/net/ and libc/netdb: Implement the low-level network DNS + packet protocol to request and receive IPv6 address mappings + (2015-07-12). \ No newline at end of file diff --git a/include/nuttx/net/dns.h b/include/nuttx/net/dns.h index cac5436caef..3728b72e08c 100644 --- a/include/nuttx/net/dns.h +++ b/include/nuttx/net/dns.h @@ -53,6 +53,60 @@ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +/* DNS classes */ + +#define DNS_CLASS_IN 1 /* RFC 1035 Internet */ +#define DNS_CLASS CH 3 /* N/A Chaos */ +#define DNS_CLASS_HS 4 /* N/A Hesiod */ +#define DNS_CLASS_QNONE 254 /* RFC 2136 QCLASS NONE */ +#define DNS_CLASS_QANY 255 /* RFC 1035 QCLASS ANY */ + +/* DNS resource record types */ + +#define DNS_RECTYPE_A 1 /* RFC 1035 IPv4 ddress record */ +#define DNS_RECTYPE_AAAA 28 /* RFC 3596 IPv6 address record */ +#define DNS_RECTYPE_AFSDB 18 /* RFC 1183 AFS database record */ +#define DNS_RECTYPE_APL 42 /* RFC 3123 Address Prefix List */ +#define DNS_RECTYPE_CAA 257 /* RFC 6844 Certification Authority Authorization */ +#define DNS_RECTYPE_CDNSKEY 60 /* RFC 7344 Child DNSKEY */ +#define DNS_RECTYPE_CDS 59 /* RFC 7344 Child DS */ +#define DNS_RECTYPE_CERT 37 /* RFC 4398 Certificate record */ +#define DNS_RECTYPE_CNAME 5 /* RFC 1035 Canonical name record */ +#define DNS_RECTYPE_DHCID 49 /* RFC 4701 DHCP identifier */ +#define DNS_RECTYPE_DLV 32769 /* RFC 4431 DNSSEC Lookaside Validation record */ +#define DNS_RECTYPE_DNAME 39 /* RFC 2672 Delegation Name */ +#define DNS_RECTYPE_DNSKEY 48 /* RFC 4034 DNS Key record */ +#define DNS_RECTYPE_DS 43 /* RFC 4034 Delegation signer */ +#define DNS_RECTYPE_HIP 55 /* RFC 5205 Host Identity Protocol */ +#define DNS_RECTYPE_IPSECKEY 45 /* RFC 4025 IPsec Key */ +#define DNS_RECTYPE_KEY 25 /* RFC 2535 and RFC 2930 Key record */ +#define DNS_RECTYPE_KX 36 /* RFC 2230 Key eXchanger record */ +#define DNS_RECTYPE_LOC 29 /* RFC 1876 Location record */ +#define DNS_RECTYPE_MX 15 /* RFC 1035 Mail exchange record */ +#define DNS_RECTYPE_NAPTR 35 /* RFC 3403 Naming Authority Pointer */ +#define DNS_RECTYPE_NS 2 /* RFC 1035 Name server record */ +#define DNS_RECTYPE_NSEC 47 /* RFC 4034 Next-Secure record */ +#define DNS_RECTYPE_NSEC3 50 /* RFC 5155 NSEC record version 3 */ +#define DNS_RECTYPE_NSEC3PARAM 51 /* RFC 5155 NSEC3 parameters */ +#define DNS_RECTYPE_PTR 12 /* RFC 1035 Pointer record */ +#define DNS_RECTYPE_RRSIG 46 /* RFC 4034 DNSSEC signature */ +#define DNS_RECTYPE_RP 17 /* RFC 1183 Responsible person */ +#define DNS_RECTYPE_SIG 24 /* RFC 2535 Signature */ +#define DNS_RECTYPE_SOA 6 /* RFC 1035 and RFC 2308 Start of [a zone of] authority record */ +#define DNS_RECTYPE_SRV 33 /* RFC 2782 Service locator */ +#define DNS_RECTYPE_SSHFP 44 /* RFC 4255 SSH Public Key Fingerprint */ +#define DNS_RECTYPE_TA 32768 /* N/A DNSSEC Trust Authorities */ +#define DNS_RECTYPE_TKEY 249 /* RFC 2930 Secret key record */ +#define DNS_RECTYPE_TLSA 52 /* RFC 6698 TLSA certificate association */ +#define DNS_RECTYPE_TSIG 250 /* RFC 2845 Transaction Signature */ +#define DNS_RECTYPE_TXT 16 /* RFC 1035[1] Text record */ + +#define DNS_RECTYPE_ALL 255 /* RFC 1035 All cached records */ +#define DNS_RECTYPE_AXFR 252 /* RFC 1035 Authoritative Zone Transfer */ +#define DNS_RECTYPE_IXFR 251 /* RFC 1996 Incremental Zone Transfer */ +#define DNS_RECTYPE_OPT 41 /* RFC 6891 Option */ + +/* Flag1 bit definitions */ #define DNS_FLAG1_RESPONSE 0x80 #define DNS_FLAG1_OPCODE_STATUS 0x10 @@ -61,6 +115,9 @@ #define DNS_FLAG1_AUTHORATIVE 0x04 #define DNS_FLAG1_TRUNC 0x02 #define DNS_FLAG1_RD 0x01 + +/* Flag2 bit definitions */ + #define DNS_FLAG2_RA 0x80 #define DNS_FLAG2_ERR_MASK 0x0f #define DNS_FLAG2_ERR_NONE 0x00 @@ -87,19 +144,20 @@ struct dns_header_s struct dns_answer_s { - /* DNS answer record starts with either a domain name or a pointer - * to a name already present somewhere in the packet. - */ - uint16_t type; uint16_t class; uint16_t ttl[2]; uint16_t len; -#if 0 /* REVISIT: Not yet support for IPv6 */ - struct in6_addr ipaddr; -#else - struct in_addr ipaddr; + + union + { +#ifdef CONFIG_NET_IPv4 + struct in_addr ipv4; #endif +#ifdef CONFIG_NET_IPv6 + struct in6_addr ipv6; +#endif + } u; }; /**************************************************************************** diff --git a/libc/netdb/lib_dnsclient.c b/libc/netdb/lib_dnsclient.c index 66b7f2d9cf7..50d4421ae98 100644 --- a/libc/netdb/lib_dnsclient.c +++ b/libc/netdb/lib_dnsclient.c @@ -109,9 +109,9 @@ static union dns_server_u g_dns_server; * ****************************************************************************/ -static FAR unsigned char *dns_parse_name(FAR unsigned char *query) +static FAR uint8_t *dns_parse_name(FAR uint8_t *query) { - unsigned char n; + uint8_t n; do { @@ -138,15 +138,14 @@ static FAR unsigned char *dns_parse_name(FAR unsigned char *query) ****************************************************************************/ static int dns_send_query(int sd, FAR const char *name, - FAR union dns_server_u *uaddr) + FAR union dns_server_u *uaddr, uint16_t rectype) { register FAR struct dns_header_s *hdr; - FAR char *query; - FAR char *nptr; - FAR const char *nameptr; + FAR uint8_t *dest; + FAR uint8_t *nptr; + FAR const char *src; uint8_t seqno = g_seqno++; - static unsigned char endquery[] = {0, 0, 1, 0, 1}; - char buffer[SEND_BUFFER_SIZE]; + uint8_t buffer[SEND_BUFFER_SIZE]; socklen_t addrlen; int errcode; int ret; @@ -157,26 +156,36 @@ static int dns_send_query(int sd, FAR const char *name, hdr->id = htons(seqno); hdr->flags1 = DNS_FLAG1_RD; hdr->numquestions = HTONS(1); - query = buffer + 12; + dest = buffer + 12; /* Convert hostname into suitable query format. */ - nameptr = name - 1; + src = name - 1; do { - nameptr++; - nptr = query++; - for (n = 0; *nameptr != '.' && *nameptr != 0; nameptr++) - { - *query++ = *nameptr; - n++; - } + /* Copy the name string */ - *nptr = n; - } - while (*nameptr != 0); + src++; + nptr = dest++; + for (n = 0; *src != '.' && *src != 0; src++) + { + *dest++ = *(uint8_t *)src; + n++; + } - memcpy(query, endquery, 5); + /* Pre-pend the name length */ + + *nptr = n; + } + while (*src != '\0'); + + /* Add NUL termination, DNS record type, and DNS class */ + + *dest++ = '\0'; /* NUL termination */ + *dest++ = (rectype >> 8); /* DNS record type (big endian) */ + *dest++ = (rectype & 0xff); + *dest++ = (DNS_CLASS_IN >> 8); /* DNS record class (big endian) */ + *dest++ = (DNS_CLASS_IN & 0xff); /* Send the request */ @@ -198,7 +207,7 @@ static int dns_send_query(int sd, FAR const char *name, } #endif - ret = sendto(sd, buffer, query + 5 - buffer, 0, &uaddr->addr, addrlen); + ret = sendto(sd, buffer, dest - buffer, 0, &uaddr->addr, addrlen); /* Return the negated errno value on sendto failure */ @@ -223,7 +232,7 @@ static int dns_send_query(int sd, FAR const char *name, static int dns_recv_response(int sd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { - FAR unsigned char *nameptr; + FAR uint8_t *nameptr; char buffer[RECV_BUFFER_SIZE]; FAR struct dns_answer_s *ans; FAR struct dns_header_s *hdr; @@ -278,7 +287,7 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, #ifdef CONFIG_DEBUG_NET { int d = 64; - nameptr = dns_parse_name((unsigned char *)buffer + 12) + 4; + nameptr = dns_parse_name((uint8_t *)buffer + 12) + 4; for (;;) { @@ -296,7 +305,7 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, } #endif - nameptr = dns_parse_name((unsigned char *)buffer + 12) + 4; + nameptr = dns_parse_name((uint8_t *)buffer + 12) + 4; for (; nanswers > 0; nanswers--) { @@ -318,43 +327,36 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, nameptr = dns_parse_name(nameptr); } - ans = (struct dns_answer_s *)nameptr; - nvdbg("Answer: type %x, class %x, ttl %x, length %x \n", /* 0x%08X\n", */ + ans = (FAR struct dns_answer_s *)nameptr; + + nvdbg("Answer: type=%04x, class=%04x, ttl=%06x, length=%04x \n", htons(ans->type), htons(ans->class), (htons(ans->ttl[0]) << 16) | htons(ans->ttl[1]), - htons(ans->len) /* , ans->ipaddr.s_addr */); + htons(ans->len)); - /* Check for IP address type and Internet class. Others are discarded. */ - -#ifdef CONFIG_NET_IPv6 -# warning Missing IPv6 support! -#endif + /* Check for IPv4/6 address type and Internet class. Others are discarded. */ #ifdef CONFIG_NET_IPv4 - if (ans->type == HTONS(1) && - ans->class == HTONS(1) && + if (ans->type == HTONS(DNS_RECTYPE_A) && + ans->class == HTONS(DNS_CLASS_IN) && ans->len == HTONS(4)) { - ans->ipaddr.s_addr = *(FAR uint32_t *)(nameptr + 10); + ans->u.ipv4.s_addr = *(FAR uint32_t *)(nameptr + 10); - nvdbg("IP address %d.%d.%d.%d\n", - (ans->ipaddr.s_addr ) & 0xff, - (ans->ipaddr.s_addr >> 8 ) & 0xff, - (ans->ipaddr.s_addr >> 16 ) & 0xff, - (ans->ipaddr.s_addr >> 24 ) & 0xff); + nvdbg("IPv4 address: %d.%d.%d.%d\n", + (ans->u.ipv4.s_addr ) & 0xff, + (ans->u.ipv4.s_addr >> 8 ) & 0xff, + (ans->u.ipv4.s_addr >> 16 ) & 0xff, + (ans->u.ipv4.s_addr >> 24 ) & 0xff); - /* TODO: we should really check that this IP address is the one - * we want. - */ - - if (*addrlen >=sizeof(struct sockaddr_in)) + if (*addrlen >= sizeof(struct sockaddr_in)) { FAR struct sockaddr_in *inaddr; inaddr = (FAR struct sockaddr_in *)addr; inaddr->sin_family = AF_INET; inaddr->sin_port = 0; - inaddr->sin_addr.s_addr = ans->ipaddr.s_addr; + inaddr->sin_addr.s_addr = ans->u.ipv4.s_addr; *addrlen = sizeof(struct sockaddr_in); return OK; @@ -365,6 +367,38 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, } } else +#endif +#ifdef CONFIG_NET_IPv6 + if (ans->type == HTONS(DNS_RECTYPE_AAAA) && + ans->class == HTONS(DNS_CLASS_IN) && + ans->len == HTONS(16)) + { + memcpy(&ans->u.ipv6.s6_addr, nameptr + 10, 16); + + nvdbg("IPv6 address: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + htons(ans->u.ipv6.s6_addr[7]), htons(ans->u.ipv6.s6_addr[6]), + htons(ans->u.ipv6.s6_addr[5]), htons(ans->u.ipv6.s6_addr[4]), + htons(ans->u.ipv6.s6_addr[3]), htons(ans->u.ipv6.s6_addr[2]), + htons(ans->u.ipv6.s6_addr[1]), htons(ans->u.ipv6.s6_addr[0])); + + if (*addrlen >= sizeof(struct sockaddr_in6)) + { + FAR struct sockaddr_in6 *inaddr; + + inaddr = (FAR struct sockaddr_in6 *)addr; + inaddr->sin6_family = AF_INET; + inaddr->sin6_port = 0; + memcpy(inaddr->sin6_addr.s6_addr, ans->u.ipv6.s6_addr, 16); + + *addrlen = sizeof(struct sockaddr_in6); + return OK; + } + else + { + return -ERANGE; + } + } + else #endif { nameptr = nameptr + 10 + htons(ans->len); @@ -443,6 +477,10 @@ int dns_bind(void) int dns_query(int sd, FAR const char *hostname, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { +#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv4) + int noipv4 = false; + int noipv6 = false; +#endif int retries; int ret; @@ -450,36 +488,118 @@ int dns_query(int sd, FAR const char *hostname, FAR struct sockaddr *addr, for (retries = 0; retries < 3; retries++) { - /* Send the query */ +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + /* If we know that the IPv4 address is not available, then don't try + * again. + */ - ret = dns_send_query(sd, hostname, &g_dns_server); - if (ret < 0) + if (!noipv4) +#endif { - ndbg("ERROR: dns_send_query failed: %d\n", ret); - return ret; + /* Send the IPv4 query */ + + ret = dns_send_query(sd, hostname, &g_dns_server, DNS_RECTYPE_A); + if (ret < 0) + { + ndbg("ERROR: IPv4 dns_send_query failed: %d\n", ret); + return ret; + } + + /* Obtain the IPv4 response */ + + ret = dns_recv_response(sd, addr, addrlen); + if (ret >= 0) + { + /* IPv4 response received successfully */ + + return OK; + } + + /* Handle errors */ + + ndbg("ERROR: IPv4 dns_recv_response failed: %d\n", ret); + +#ifdef CONFIG_NET_IPv6 + if (ret != -EADDRNOTAVAIL) + { + /* The IPv4 address is not available. */ + + noipv4 = true; + if (noipv6) + { + /* Neither address is available */ + + return ret; + } + } + else +#endif + if (ret != -EAGAIN) + { + /* Some failure other than receive timeout occurred */ + + return ret; + } } +#endif - /* Obtain the response */ +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + /* If we know that the IPv4 address is not available, then don't try + * again. + */ - ret = dns_recv_response(sd, addr, addrlen); - if (ret >= 0) + if (!noipv6) +#endif { - /* Response received successfully */ - /* Save the host address -- Needs fixed for IPv6 */ + /* Send the IPv6 query */ - return OK; - } - - /* Handler errors */ - - ndbg("ERROR: dns_recv_response failed: %d\n", ret); - - if (ret != -EAGAIN) - { - /* Some failure other than receive timeout occurred */ - - return ret; + ret = dns_send_query(sd, hostname, &g_dns_server, + DNS_RECTYPE_AAAA); + if (ret < 0) + { + ndbg("ERROR: IPv6 dns_send_query failed: %d\n", ret); + return ret; + } + + /* Obtain the IPv6 response */ + + ret = dns_recv_response(sd, addr, addrlen); + if (ret >= 0) + { + /* IPv6 response received successfully */ + + return OK; + } + + /* Handle errors */ + + ndbg("ERROR: IPv6 dns_recv_response failed: %d\n", ret); + +#ifdef CONFIG_NET_IPv4 + if (ret != -EADDRNOTAVAIL) + { + /* The IPv6 address is not available. */ + + noipv6 = true; + if (noipv4) + { + /* Neither address is available */ + + return ret; + } + } + else +#endif + if (ret != -EAGAIN) + { + /* Some failure other than receive timeout occurred */ + + return ret; + } } +#endif } return -ETIMEDOUT;