diff --git a/include/nuttx/net/dns.h b/include/nuttx/net/dns.h index 503e27350da..c0a87223d29 100644 --- a/include/nuttx/net/dns.h +++ b/include/nuttx/net/dns.h @@ -90,10 +90,12 @@ #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_OPENPGPKEY 61 /* RFC 7929 OpenPGP public key record */ #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_SMIMEA 53 /* RFC 8162 S/MIME cert association */ #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 */ @@ -101,7 +103,8 @@ #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_TXT 16 /* RFC 1035 Text record */ +#define DNS_RECTYPE_URI 256 /* RFC 7553 Uniform Resource Identifier */ #define DNS_RECTYPE_ALL 255 /* RFC 1035 All cached records */ #define DNS_RECTYPE_AXFR 252 /* RFC 1035 Authoritative Zone Transfer */ @@ -156,7 +159,7 @@ struct dns_question_s /* The DNS answer message structure */ -struct dns_answer_s +begin_packed_struct struct dns_answer_s { uint16_t type; uint16_t class; @@ -172,7 +175,7 @@ struct dns_answer_s struct in6_addr ipv6; #endif } u; -}; +} end_packed_struct; /* The type of the callback from dns_foreach_nameserver() */ diff --git a/libs/libc/netdb/Kconfig b/libs/libc/netdb/Kconfig index 08bc1be737e..b36c1dd4d38 100644 --- a/libs/libc/netdb/Kconfig +++ b/libs/libc/netdb/Kconfig @@ -102,6 +102,13 @@ config NETDB_DNSCLIENT_MAXRESPONSE can be received by the DNS resolver. The default is 96 but may need to be larger on enterprise networks (perhaps 176). +config NETDB_DNSCLIENT_MAXIP + int "Max number of IP addresses per host" + default 1 + ---help--- + This setting determines the maximum number of IP addresses + stored to the name resolution cache for a given host. + config NETDB_DNSCLIENT_RECV_TIMEOUT int "DNS receive timeout" default 30 diff --git a/libs/libc/netdb/lib_dns.h b/libs/libc/netdb/lib_dns.h index 4301973f7b8..6329dba9d09 100644 --- a/libs/libc/netdb/lib_dns.h +++ b/libs/libc/netdb/lib_dns.h @@ -62,12 +62,14 @@ # define CONFIG_NETDB_DNSCLIENT_ENTRIES 4 #endif -#define RESOLV_ENTRIES CONFIG_NETDB_DNSCLIENT_ENTRIES - #ifndef CONFIG_NETDB_DNSCLIENT_MAXRESPONSE # define CONFIG_NETDB_DNSCLIENT_MAXRESPONSE 96 #endif +#ifndef CONFIG_NETDB_DNSCLIENT_MAXIP +# define CONFIG_NETDB_DNSCLIENT_MAXIP 1 +#endif + #ifndef CONFIG_NETDB_DNSCLIENT_NAMESIZE # define CONFIG_NETDB_DNSCLIENT_NAMESIZE 32 #endif @@ -91,7 +93,7 @@ * alternative to sockaddr_storage. */ -union dns_server_u +union dns_addr_u { struct sockaddr addr; /* Common address representation */ #ifdef CONFIG_NET_IPv4 @@ -118,7 +120,7 @@ extern "C" #ifndef CONFIG_NETDB_RESOLVCONF /* The DNS server address */ -EXTERN union dns_server_u g_dns_server; +EXTERN union dns_addr_u g_dns_server; EXTERN bool g_dns_address; /* true: We have the address of the DNS server */ #endif @@ -184,19 +186,19 @@ int dns_bind(void); * Input Parameters: * sd - The socket descriptor previously initialized by dsn_bind(). * hostname - The hostname string to be resolved. - * addr - The location to return the IP address associated with the - * hostname - * addrlen - On entry, the size of the buffer backing up the 'addr' - * pointer. On return, this location will hold the actual size of - * the returned address. + * addr - The location to return the IP addresses associated with the + * hostname. + * naddr - On entry, the count of addresses backing up the 'addr' + * pointer. On return, this location will hold the actual count of + * the returned addresses. * * Returned Value: * Returns zero (OK) if the query was successful. * ****************************************************************************/ -int dns_query(int sd, FAR const char *hostname, FAR struct sockaddr *addr, - FAR socklen_t *addrlen); +int dns_query(int sd, FAR const char *hostname, FAR union dns_addr_u *addr, + FAR int *naddr); /**************************************************************************** * Name: dns_save_answer @@ -206,8 +208,8 @@ int dns_query(int sd, FAR const char *hostname, FAR struct sockaddr *addr, * * Input Parameters: * hostname - The hostname string to be cached. - * addr - The IP address associated with the hostname - * addrlen - The size of the of the IP address. + * addr - The IP addresses associated with the hostname. + * naddr - The count of the IP addresses. * * Returned Value: * None @@ -216,7 +218,7 @@ int dns_query(int sd, FAR const char *hostname, FAR struct sockaddr *addr, #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 void dns_save_answer(FAR const char *hostname, - FAR const struct sockaddr *addr, socklen_t addrlen); + FAR const union dns_addr_u *addr, int naddr); #endif /**************************************************************************** @@ -227,11 +229,11 @@ void dns_save_answer(FAR const char *hostname, * * Input Parameters: * hostname - The hostname string to be resolved. - * addr - The location to return the IP address associated with the - * hostname - * addrlen - On entry, the size of the buffer backing up the 'addr' - * pointer. On return, this location will hold the actual size of - * the returned address. + * addr - The location to return the IP addresses associated with the + * hostname. + * naddr - On entry, the count of addresses backing up the 'addr' + * pointer. On return, this location will hold the actual count of + * the returned addresses. * * Returned Value: * If the host name was successfully found in the DNS name resolution @@ -242,8 +244,8 @@ void dns_save_answer(FAR const char *hostname, ****************************************************************************/ #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 -int dns_find_answer(FAR const char *hostname, FAR struct sockaddr *addr, - FAR socklen_t *addrlen); +int dns_find_answer(FAR const char *hostname, FAR union dns_addr_u *addr, + FAR int *naddr); #endif #undef EXTERN diff --git a/libs/libc/netdb/lib_dnsaddserver.c b/libs/libc/netdb/lib_dnsaddserver.c index 69d9de23797..92520eb9e7d 100644 --- a/libs/libc/netdb/lib_dnsaddserver.c +++ b/libs/libc/netdb/lib_dnsaddserver.c @@ -60,7 +60,7 @@ #ifndef CONFIG_NETDB_RESOLVCONF /* The DNS server address */ -union dns_server_u g_dns_server; +union dns_addr_u g_dns_server; bool g_dns_address; /* true: We have the address of the DNS server */ #endif diff --git a/libs/libc/netdb/lib_dnscache.c b/libs/libc/netdb/lib_dnscache.c index 9e4e0619a57..ba4ebdd8bfb 100644 --- a/libs/libc/netdb/lib_dnscache.c +++ b/libs/libc/netdb/lib_dnscache.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -65,15 +66,20 @@ * Private Types ****************************************************************************/ -/* This described one entry in the cache of resolved hostnames */ +/* This described one entry in the cache of resolved hostnames. + * + * REVISIT: this consumes extra space, especially when multiple + * addresses per name are stored. + */ struct dns_cache_s { #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 - time_t ctime; /* Creation time */ + time_t ctime; /* Creation time */ #endif - char name[CONFIG_NETDB_DNSCLIENT_NAMESIZE]; - union dns_server_u addr; /* Resolved address */ + char name[CONFIG_NETDB_DNSCLIENT_NAMESIZE]; + uint8_t naddr; /* How many addresses per name */ + union dns_addr_u addr[CONFIG_NETDB_DNSCLIENT_MAXIP]; /* Resolved address */ }; /**************************************************************************** @@ -103,8 +109,8 @@ static struct dns_cache_s g_dns_cache[CONFIG_NETDB_DNSCLIENT_ENTRIES]; * * Input Parameters: * hostname - The hostname string to be cached. - * addr - The IP address associated with the hostname - * addrlen - The size of the of the IP address. + * addr - The IP addresses associated with the hostname. + * naddr - The count of the IP addresses. * * Returned Value: * None @@ -112,7 +118,7 @@ static struct dns_cache_s g_dns_cache[CONFIG_NETDB_DNSCLIENT_ENTRIES]; ****************************************************************************/ void dns_save_answer(FAR const char *hostname, - FAR const struct sockaddr *addr, socklen_t addrlen) + FAR const union dns_addr_u *addr, int naddr) { FAR struct dns_cache_s *entry; #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 @@ -121,6 +127,9 @@ void dns_save_answer(FAR const char *hostname, int next; int ndx; + naddr = MIN(naddr, CONFIG_NETDB_DNSCLIENT_MAXIP); + DEBUGASSERT(naddr >= 1 && naddr <= UCHAR_MAX); + /* Get exclusive access to the DNS cache */ dns_semtake(); @@ -161,7 +170,8 @@ void dns_save_answer(FAR const char *hostname, #endif strncpy(entry->name, hostname, CONFIG_NETDB_DNSCLIENT_NAMESIZE); - memcpy(&entry->addr.addr, addr, addrlen); + memcpy(&entry->addr, addr, naddr * sizeof(*addr)); + entry->naddr = naddr; /* Save the updated head index */ @@ -177,11 +187,11 @@ void dns_save_answer(FAR const char *hostname, * * Input Parameters: * hostname - The hostname string to be resolved. - * addr - The location to return the IP address associated with the - * hostname - * addrlen - On entry, the size of the buffer backing up the 'addr' - * pointer. On return, this location will hold the actual size of - * the returned address. + * addr - The location to return the IP addresses associated with the + * hostname. + * naddr - On entry, the count of addresses backing up the 'addr' + * pointer. On return, this location will hold the actual count of + * the returned addresses. * * Returned Value: * If the host name was successfully found in the DNS name resolution @@ -191,8 +201,8 @@ void dns_save_answer(FAR const char *hostname, * ****************************************************************************/ -int dns_find_answer(FAR const char *hostname, FAR struct sockaddr *addr, - FAR socklen_t *addrlen) +int dns_find_answer(FAR const char *hostname, FAR union dns_addr_u *addr, + FAR int *naddr) { FAR struct dns_cache_s *entry; #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 @@ -221,8 +231,6 @@ int dns_find_answer(FAR const char *hostname, FAR struct sockaddr *addr, ret = clock_gettime(DNS_CLOCK, &now); #endif - /* REVISIT: This is not thread safe */ - for (ndx = g_dns_tail; ndx != g_dns_head; ndx = next) { entry = &g_dns_cache[ndx]; @@ -263,41 +271,17 @@ int dns_find_answer(FAR const char *hostname, FAR struct sockaddr *addr, if (strncmp(hostname, entry->name, CONFIG_NETDB_DNSCLIENT_NAMESIZE) == 0) { - socklen_t inlen; - /* We have a match. Return the resolved host address */ -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - if (entry->addr.addr.sa_family == AF_INET) -#endif - { - inlen = sizeof(struct sockaddr_in); - } -#endif - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - else -#endif - { - inlen = sizeof(struct sockaddr_in6); - } -#endif /* Make sure that the address will fit in the caller-provided * buffer. */ - if (*addrlen < inlen) - { - ret = -ERANGE; - goto errout_with_sem; - } + *naddr = MIN(*naddr, entry->naddr); /* Return the address information */ - memcpy(addr, &entry->addr.addr, inlen); - *addrlen = inlen; + memcpy(addr, &entry->addr, *naddr * sizeof(*addr)); dns_semgive(); return OK; @@ -307,7 +291,6 @@ int dns_find_answer(FAR const char *hostname, FAR struct sockaddr *addr, ret = -ENOENT; -errout_with_sem: dns_semgive(); return ret; } diff --git a/libs/libc/netdb/lib_dnsforeach.c b/libs/libc/netdb/lib_dnsforeach.c index 7878f77ff84..258c1bdadd8 100644 --- a/libs/libc/netdb/lib_dnsforeach.c +++ b/libs/libc/netdb/lib_dnsforeach.c @@ -90,7 +90,7 @@ static FAR char *find_spaces(FAR char *ptr) int dns_foreach_nameserver(dns_callback_t callback, FAR void *arg) { - union dns_server_u u; + union dns_addr_u u; FAR FILE *stream; char line[DNS_MAX_LINE]; FAR char *addrstr; diff --git a/libs/libc/netdb/lib_dnsquery.c b/libs/libc/netdb/lib_dnsquery.c index 3c188f59ee3..9d2549143a8 100644 --- a/libs/libc/netdb/lib_dnsquery.c +++ b/libs/libc/netdb/lib_dnsquery.c @@ -88,8 +88,8 @@ struct dns_query_s int sd; /* DNS server socket */ int result; /* Explanation of the failure */ FAR const char *hostname; /* Hostname to lookup */ - FAR struct sockaddr *addr; /* Location to return host address */ - FAR socklen_t *addrlen; /* Length of the address */ + FAR union dns_addr_u *addr; /* Location to return host address */ + FAR int *naddr; /* Number of returned addresses */ }; /* Query info to check response against. */ @@ -205,7 +205,7 @@ static inline uint16_t dns_alloc_id(void) ****************************************************************************/ static int dns_send_query(int sd, FAR const char *name, - FAR union dns_server_u *uaddr, uint16_t rectype, + FAR union dns_addr_u *uaddr, uint16_t rectype, FAR struct dns_query_info_s *qinfo) { FAR struct dns_header_s *hdr; @@ -337,10 +337,13 @@ static int dns_send_query(int sd, FAR const char *name, * Description: * Called when new UDP data arrives * + * Returned Value: + * Returns number of valid IP address responses. Negated errno value is + * returned in all other cases. + * ****************************************************************************/ -static int dns_recv_response(int sd, FAR struct sockaddr *addr, - FAR socklen_t *addrlen, +static int dns_recv_response(int sd, FAR union dns_addr_u *addr, int *naddr, FAR struct dns_query_info_s *qinfo) { FAR uint8_t *nameptr; @@ -352,8 +355,9 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, FAR struct dns_question_s *que; uint16_t nquestions; uint16_t nanswers; - union dns_server_u recvaddr; + union dns_addr_u recvaddr; socklen_t raddrlen; + int naddr_read; int errcode; int ret; @@ -535,6 +539,9 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, nameptr += sizeof(struct dns_question_s); + ret = OK; + naddr_read = 0; + for (; nanswers > 0; nanswers--) { /* Each answer starts with a name */ @@ -542,7 +549,8 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, nameptr = dns_parse_name(nameptr, endofbuffer); if (nameptr == endofbuffer) { - return -EILSEQ; + ret = -EILSEQ; + break; } ans = (FAR struct dns_answer_s *)nameptr; @@ -562,7 +570,7 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, ans->len == HTONS(4) && nameptr + 10 + 4 <= endofbuffer) { - ans->u.ipv4.s_addr = *(FAR uint32_t *)(nameptr + 10); + nameptr += 10 + 4; ninfo("IPv4 address: %d.%d.%d.%d\n", (ans->u.ipv4.s_addr ) & 0xff, @@ -570,21 +578,25 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, (ans->u.ipv4.s_addr >> 16) & 0xff, (ans->u.ipv4.s_addr >> 24) & 0xff); - if (*addrlen >= sizeof(struct sockaddr_in)) + if (naddr_read < *naddr) { 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->u.ipv4.s_addr; + inaddr = (FAR struct sockaddr_in *)&addr[naddr_read].addr; + inaddr->sin_family = AF_INET; + inaddr->sin_port = 0; + inaddr->sin_addr.s_addr = ans->u.ipv4.s_addr; - *addrlen = sizeof(struct sockaddr_in); - return OK; + naddr_read++; + if (naddr_read >= *naddr) + { + break; + } } else { - return -ERANGE; + ret = -ERANGE; + break; } } else @@ -595,7 +607,7 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, ans->len == HTONS(16) && nameptr + 10 + 16 <= endofbuffer) { - memcpy(&ans->u.ipv6.s6_addr, nameptr + 10, 16); + nameptr += 10 + 16; ninfo("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]), @@ -603,21 +615,25 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, 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)) + if (naddr_read < *naddr) { FAR struct sockaddr_in6 *inaddr; - inaddr = (FAR struct sockaddr_in6 *)addr; - inaddr->sin6_family = AF_INET; + inaddr = (FAR struct sockaddr_in6 *)&addr[naddr_read].addr; + inaddr->sin6_family = AF_INET6; inaddr->sin6_port = 0; memcpy(inaddr->sin6_addr.s6_addr, ans->u.ipv6.s6_addr, 16); - *addrlen = sizeof(struct sockaddr_in6); - return OK; + naddr_read++; + if (naddr_read >= *naddr) + { + break; + } } else { - return -ERANGE; + ret = -ERANGE; + break; } } else @@ -627,7 +643,17 @@ static int dns_recv_response(int sd, FAR struct sockaddr *addr, } } - return -EADDRNOTAVAIL; + if (naddr_read == 0 && ret == OK) + { + ret = -EADDRNOTAVAIL; + } + else if (naddr_read > 0 && ret != OK) + { + nwarn("Got an IP, but further parse returned %d\n", ret); + } + + *naddr = naddr_read; + return (naddr_read > 0) ? naddr_read : ret; } /**************************************************************************** @@ -684,7 +710,7 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr, /* Send the IPv4 query */ ret = dns_send_query(query->sd, query->hostname, - (FAR union dns_server_u *)addr, + (FAR union dns_addr_u *)addr, DNS_RECTYPE_A, &qinfo); if (ret < 0) { @@ -699,7 +725,7 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr, /* Obtain the IPv4 response */ - ret = dns_recv_response(query->sd, query->addr, query->addrlen, + ret = dns_recv_response(query->sd, query->addr, query->naddr, &qinfo); if (ret >= 0) { @@ -709,7 +735,7 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr, /* Save the answer in the DNS cache */ dns_save_answer(query->hostname, query->addr, - *query->addrlen); + *query->naddr); #endif /* Return 1 to indicate to (1) stop the traversal, and (2) * indicate that the address was found. @@ -749,7 +775,7 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr, #ifdef CONFIG_NET_IPv6 /* Is this an IPv6 address? */ - if (query->addr->sa_family == AF_INET6) + if (addr->sa_family == AF_INET6) { /* Yes.. verify the address size */ @@ -767,7 +793,7 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr, /* Send the IPv6 query */ ret = dns_send_query(query->sd, query->hostname, - (FAR union dns_server_u *)addr, + (FAR union dns_addr_u *)addr, DNS_RECTYPE_AAAA, &qinfo); if (ret < 0) { @@ -782,7 +808,7 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr, /* Obtain the IPv6 response */ - ret = dns_recv_response(query->sd, query->addr, query->addrlen, + ret = dns_recv_response(query->sd, query->addr, query->naddr, &qinfo); if (ret >= 0) { @@ -791,7 +817,7 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr, #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 /* Save the answer in the DNS cache */ - dns_save_answer(query->hostname, query->addr, *query->addrlen); + dns_save_answer(query->hostname, query->addr, *query->naddr); #endif /* Return 1 to indicate to (1) stop the traversal, and (2) * indicate that the address was found. @@ -859,19 +885,19 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr, * Input Parameters: * sd - The socket descriptor previously initialized by dsn_bind(). * hostname - The hostname string to be resolved. - * addr - The location to return the IP address associated with the - * hostname - * addrlen - On entry, the size of the buffer backing up the 'addr' - * pointer. On return, this location will hold the actual size of - * the returned address. + * addr - The location to return the IP addresses associated with the + * hostname. + * naddr - On entry, the count of addresses backing up the 'addr' + * pointer. On return, this location will hold the actual count of + * the returned addresses. * * Returned Value: * Returns zero (OK) if the query was successful. * ****************************************************************************/ -int dns_query(int sd, FAR const char *hostname, FAR struct sockaddr *addr, - FAR socklen_t *addrlen) +int dns_query(int sd, FAR const char *hostname, FAR union dns_addr_u *addr, + FAR int *naddr) { FAR struct dns_query_s query; int ret; @@ -882,7 +908,7 @@ int dns_query(int sd, FAR const char *hostname, FAR struct sockaddr *addr, query.result = -EADDRNOTAVAIL; query.hostname = hostname; query.addr = addr; - query.addrlen = addrlen; + query.naddr = naddr; /* Perform the query. dns_foreach_nameserver() will return: * diff --git a/libs/libc/netdb/lib_gethostbynamer.c b/libs/libc/netdb/lib_gethostbynamer.c index e802be47ca4..4852a927f6e 100644 --- a/libs/libc/netdb/lib_gethostbynamer.c +++ b/libs/libc/netdb/lib_gethostbynamer.c @@ -66,7 +66,7 @@ struct hostent_info_s { - FAR char *hi_addrlist[2]; + FAR char *hi_addrlist[CONFIG_NETDB_DNSCLIENT_MAXIP + 1]; char hi_data[1]; }; @@ -360,9 +360,11 @@ static int lib_find_answer(FAR const char *name, FAR struct hostent *host, FAR char *ptr; FAR void *addrdata; socklen_t addrlen; + int naddr; int addrtype; int namelen; int ret; + int i; /* Verify that we have a buffer big enough to get started (it still may not * be big enough). @@ -379,13 +381,20 @@ static int lib_find_answer(FAR const char *name, FAR struct hostent *host, ptr = info->hi_data; buflen -= (sizeof(struct hostent_info_s) - 1); + /* Verify again that there is space for at least one address. */ + + if (buflen < sizeof(union dns_addr_u)) + { + return -ERANGE; + } + memset(host, 0, sizeof(struct hostent)); memset(info, 0, sizeof(struct hostent_info_s)); /* Try to get the host address using the DNS name server */ - addrlen = buflen; - ret = dns_find_answer(name, (FAR struct sockaddr *)ptr, &addrlen); + naddr = buflen / sizeof(union dns_addr_u); + ret = dns_find_answer(name, (FAR union dns_addr_u *)ptr, &naddr); if (ret < 0) { /* No, nothing found in the cache */ @@ -393,41 +402,45 @@ static int lib_find_answer(FAR const char *name, FAR struct hostent *host, return ret; } - /* Get the address type; verify the address size. */ + DEBUGASSERT(naddr <= CONFIG_NETDB_DNSCLIENT_MAXIP); + /* Get the address type. */ + + for (i = 0; i < naddr; i++) + { #ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv6 - if (((FAR struct sockaddr_in *)ptr)->sin_family == AF_INET) + if (((FAR struct sockaddr_in *)ptr)->sin_family == AF_INET) #endif - { - DEBUGASSERT(addrlen == sizeof(struct sockaddr_in)); - addrlen = sizeof(struct sockaddr_in); - addrtype = AF_INET; - addrdata = &((FAR struct sockaddr_in *)ptr)->sin_addr; - } + { + addrlen = sizeof(struct sockaddr_in); + addrtype = AF_INET; + addrdata = &((FAR struct sockaddr_in *)ptr)->sin_addr; + } #endif #ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv4 - else + else #endif - { - DEBUGASSERT(addrlen == sizeof(struct sockaddr_in6)); - addrlen = sizeof(struct sockaddr_in6); - addrtype = AF_INET6; - addrdata = &((FAR struct sockaddr_in6 *)ptr)->sin6_addr; + { + addrlen = sizeof(struct sockaddr_in6); + addrtype = AF_INET6; + addrdata = &((FAR struct sockaddr_in6 *)ptr)->sin6_addr; + } +#endif + + /* REVISIT: This assumes addresses are all either IPv4 or IPv6. */ + + info->hi_addrlist[i] = addrdata; + host->h_addrtype = addrtype; + host->h_length = addrlen; + + ptr += sizeof(union dns_addr_u); + buflen -= sizeof(union dns_addr_u); } -#endif - /* Yes.. Return the address that we obtained from the DNS cache. */ - - info->hi_addrlist[0] = addrdata; - host->h_addr_list = info->hi_addrlist; - host->h_addrtype = addrtype; - host->h_length = addrlen; - - ptr += addrlen; - buflen -= addrlen; + host->h_addr_list = info->hi_addrlist; /* And copy name */ @@ -459,7 +472,7 @@ static int lib_find_answer(FAR const char *name, FAR struct hostent *host, #ifdef CONFIG_NETDB_DNSCLIENT static int lib_dns_query(FAR const char *hostname, - FAR struct sockaddr *addr, socklen_t *addrlen) + FAR union dns_addr_u *addr, int *naddr) { int sd; int ret; @@ -474,7 +487,7 @@ static int lib_dns_query(FAR const char *hostname, /* Perform the query to get the IP address */ - ret = dns_query(sd, hostname, addr, addrlen); + ret = dns_query(sd, hostname, addr, naddr); /* Release the socket */ @@ -509,9 +522,11 @@ static int lib_dns_lookup(FAR const char *name, FAR struct hostent *host, FAR char *ptr; FAR void *addrdata; socklen_t addrlen; + int naddr; int addrtype; int namelen; int ret; + int i; /* Verify that we have a buffer big enough to get started (it still may not * be big enough). @@ -528,26 +543,36 @@ static int lib_dns_lookup(FAR const char *name, FAR struct hostent *host, ptr = info->hi_data; buflen -= (sizeof(struct hostent_info_s) - 1); + /* Verify again that there is space for at least one address. */ + + if (buflen < sizeof(union dns_addr_u)) + { + return -ERANGE; + } + memset(host, 0, sizeof(struct hostent)); memset(info, 0, sizeof(struct hostent_info_s)); /* Try to get the host address using the DNS name server */ - addrlen = buflen; - ret = lib_dns_query(name, (FAR struct sockaddr *)ptr, &addrlen); - - /* Was the DNS lookup successful? */ - - if (ret >= 0) + naddr = buflen / sizeof(union dns_addr_u); + ret = lib_dns_query(name, (FAR union dns_addr_u *)ptr, &naddr); + if (ret < 0) { - /* Get the address type; verify the address size. */ + return ret; + } + /* We can read more than maximum, limit here. */ + + naddr = MIN(naddr, CONFIG_NETDB_DNSCLIENT_MAXIP); + + for (i = 0; i < naddr; i++) + { #ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv6 if (((FAR struct sockaddr_in *)ptr)->sin_family == AF_INET) #endif { - DEBUGASSERT(addrlen == sizeof(struct sockaddr_in)); addrlen = sizeof(struct sockaddr_in); addrtype = AF_INET; addrdata = &((FAR struct sockaddr_in *)ptr)->sin_addr; @@ -559,40 +584,39 @@ static int lib_dns_lookup(FAR const char *name, FAR struct hostent *host, else #endif { - DEBUGASSERT(addrlen == sizeof(struct sockaddr_in6)); addrlen = sizeof(struct sockaddr_in6); addrtype = AF_INET6; addrdata = &((FAR struct sockaddr_in6 *)ptr)->sin6_addr; } #endif - /* Yes.. Return the address that we obtained from the DNS name server. */ + /* REVISIT: This assumes addresses are all either IPv4 or IPv6. */ - info->hi_addrlist[0] = addrdata; - host->h_addr_list = info->hi_addrlist; + info->hi_addrlist[i] = addrdata; host->h_addrtype = addrtype; host->h_length = addrlen; - ptr += addrlen; - buflen -= addrlen; - - /* And copy name */ - - namelen = strlen(name); - if ((namelen + 1) > buflen) - { - return -ERANGE; - } - - strncpy(ptr, name, buflen); - - /* Set the address to h_name */ - - host->h_name = ptr; - return OK; + ptr += sizeof(union dns_addr_u); + buflen -= sizeof(union dns_addr_u); } - return ret; + host->h_addr_list = info->hi_addrlist; + + /* And copy name */ + + namelen = strlen(name); + if ((namelen + 1) > buflen) + { + return -ERANGE; + } + + strncpy(ptr, name, buflen); + + /* Set the address to h_name */ + + host->h_name = ptr; + + return OK; } #endif /* CONFIG_NETDB_DNSCLIENT */