From dcb01b91218b5d3be913b14abdec0fd95ae06b40 Mon Sep 17 00:00:00 2001 From: Abhik Roy Date: Mon, 18 Mar 2024 23:40:14 +1100 Subject: [PATCH] dns: Add support for multiple DNS/IP records 2.1.3-esp: f1746813 feat(lwip): Added multiple dns ip support 2.1.3-esp: bced058f dns: Fixed incorrect handling of 0.0.0.0 --- src/api/api_msg.c | 6 +- src/api/netdb.c | 349 +++++++++++++++++++++++++++-------------- src/core/dns.c | 148 +++++++++++------ src/include/lwip/opt.h | 5 + 4 files changed, 337 insertions(+), 171 deletions(-) diff --git a/src/api/api_msg.c b/src/api/api_msg.c index 09aca160..4db89c01 100644 --- a/src/api/api_msg.c +++ b/src/api/api_msg.c @@ -2199,6 +2199,7 @@ done: static void lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg) { + u8_t i; struct dns_api_msg *msg = (struct dns_api_msg *)arg; /* we trust the internal implementation to be correct :-) */ @@ -2210,7 +2211,10 @@ lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg) } else { /* address was resolved */ API_EXPR_DEREF(msg->err) = ERR_OK; - API_EXPR_DEREF(msg->addr) = *ipaddr; + + for (i=0; iaddr+i) = *(ipaddr+i); + } } /* wake up the application task waiting in netconn_gethostbyname */ sys_sem_signal(API_EXPR_REF_SEM(msg->sem)); diff --git a/src/api/netdb.c b/src/api/netdb.c index f52dff0c..6ca853b7 100644 --- a/src/api/netdb.c +++ b/src/api/netdb.c @@ -51,8 +51,8 @@ /** helper struct for gethostbyname_r to access the char* buffer */ struct gethostbyname_r_helper { - ip_addr_t *addr_list[2]; - ip_addr_t addr; + ip_addr_t *addr_list[DNS_MAX_HOST_IP+1]; /* The last entry in the list is always NULL */ + ip_addr_t addr[DNS_MAX_HOST_IP]; char *aliases; }; @@ -79,10 +79,21 @@ int h_errno; #define HOSTENT_STORAGE static #endif /* LWIP_DNS_API_STATIC_HOSTENT */ +/* Counts IP addresses in addr array until a zero IP address is encountered */ +#define COUNT_NON_ZERO_IP_ADDRESSES(addr, ipaddr_cnt) \ + do { \ + ipaddr_cnt = 0; \ + for (i = 0; i < DNS_MAX_HOST_IP; i++) { \ + if (!ip_addr_cmp(&addr_zero, &addr[i])) { \ + ipaddr_cnt++; \ + } \ + } \ + } while(0) + /** * Returns an entry containing addresses of address family AF_INET * for the host with name name. - * Due to dns_gethostbyname limitations, only one address is returned. + * dns_gethostbyname can return as many address as configured in DNS_MAX_HOST_IP. * * @param name the hostname to resolve * @return an entry containing addresses of address family AF_INET @@ -91,35 +102,51 @@ int h_errno; struct hostent * lwip_gethostbyname(const char *name) { + u8_t i; err_t err; - ip_addr_t addr; + ip_addr_t addr[DNS_MAX_HOST_IP]={0}, addr_zero={0}; + u8_t ipaddr_cnt = 0; /* buffer variables for lwip_gethostbyname() */ HOSTENT_STORAGE struct hostent s_hostent; HOSTENT_STORAGE char *s_aliases; - HOSTENT_STORAGE ip_addr_t s_hostent_addr; - HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2]; + HOSTENT_STORAGE ip_addr_t s_hostent_addr[DNS_MAX_HOST_IP]; + HOSTENT_STORAGE ip_addr_t *s_phostent_addr[DNS_MAX_HOST_IP+1]; /* The last entry in the list is always NULL */ HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1]; /* query host IP address */ - err = netconn_gethostbyname(name, &addr); + err = netconn_gethostbyname(name, addr); if (err != ERR_OK) { LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); h_errno = HOST_NOT_FOUND; return NULL; } - /* fill hostent */ - s_hostent_addr = addr; - s_phostent_addr[0] = &s_hostent_addr; - s_phostent_addr[1] = NULL; + COUNT_NON_ZERO_IP_ADDRESSES(addr, ipaddr_cnt); + + if (ipaddr_cnt == 0) { + /* handle 0.0.0.0 */ + s_hostent_addr[0] = addr[0]; + s_phostent_addr[0] = &s_hostent_addr[0]; + s_phostent_addr[1] = NULL; + } else { + for (i=0; iaddr); + err = netconn_gethostbyname(name, h->addr); if (err != ERR_OK) { LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); *h_errnop = HOST_NOT_FOUND; @@ -212,14 +243,28 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, MEMCPY(hostname, name, namelen); hostname[namelen] = 0; - /* fill hostent */ - h->addr_list[0] = &h->addr; - h->addr_list[1] = NULL; + COUNT_NON_ZERO_IP_ADDRESSES(h->addr, ipaddr_cnt); + + if (ipaddr_cnt == 0) { + /* handle 0.0.0.0 */ + h->addr_list[0] = &h->addr[0]; + h->addr_list[1] = NULL; + } else { + for (i=0; iaddr[i])) { + h->addr_list[i] = &h->addr[i]; + } else { + break; + } + } + h->addr_list[i] = NULL; + } + h->aliases = NULL; ret->h_name = hostname; ret->h_aliases = &h->aliases; - ret->h_addrtype = (IPADDR_TYPE_V4 == IP_GET_TYPE(&h->addr)? AF_INET : AF_INET6); - ret->h_length = IP_ADDR_RAW_SIZE(h->addr); + ret->h_addrtype = (IPADDR_TYPE_V4 == IP_GET_TYPE(&h->addr[0])? AF_INET : AF_INET6); + ret->h_length = IP_ADDR_RAW_SIZE(h->addr[0]); ret->h_addr_list = (char **)&h->addr_list; /* set result != NULL */ @@ -249,119 +294,34 @@ lwip_freeaddrinfo(struct addrinfo *ai) } /** - * Translates the name of a service location (for example, a host name) and/or - * a service name and returns a set of socket addresses and associated - * information to be used in creating a socket with which to address the - * specified service. - * Memory for the result is allocated internally and must be freed by calling - * lwip_freeaddrinfo()! + * Creates a new address information (addrinfo) structure based on the provided parameters. * - * Due to a limitation in dns_gethostbyname, only the first address of a - * host is returned. - * Also, service names are not supported (only port numbers)! + * @param addr IP address to be used. + * @param nodename Optional node name associated with the address. + * @param hints Pointer to a struct addrinfo containing hints for the address resolution. + * @param port_nr Port number associated with the address. + * @param res Pointer to a pointer to struct addrinfo to store the created address information. + * @param idx Index of the address info in the list of returned address info. * - * @param nodename descriptive name or address string of the host - * (may be NULL -> local address) - * @param servname port number as string of NULL - * @param hints structure containing input values that set socktype and protocol - * @param res pointer to a pointer where to store the result (set to NULL on failure) - * @return 0 on success, non-zero on failure - * - * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG + * @return Returns ERR_OK on success, or an error code (EAI_FAIL, EAI_MEMORY) on failure. */ -int -lwip_getaddrinfo(const char *nodename, const char *servname, - const struct addrinfo *hints, struct addrinfo **res) +static int +create_addrinfo(ip_addr_t addr, const char *nodename, const struct addrinfo *hints, + int port_nr, struct addrinfo **res, const unsigned char idx) { - err_t err; - ip_addr_t addr; - struct addrinfo *ai; - struct sockaddr_storage *sa = NULL; - int port_nr = 0; size_t total_size; size_t namelen = 0; + struct addrinfo *ai; + struct sockaddr_storage *sa = NULL; +#if ESP_LWIP && LWIP_IPV4 && LWIP_IPV6 int ai_family; - if (res == NULL) { - return EAI_FAIL; - } - *res = NULL; - if ((nodename == NULL) && (servname == NULL)) { - return EAI_NONAME; - } - if (hints != NULL) { ai_family = hints->ai_family; - if ((ai_family != AF_UNSPEC) -#if LWIP_IPV4 - && (ai_family != AF_INET) -#endif /* LWIP_IPV4 */ -#if LWIP_IPV6 - && (ai_family != AF_INET6) -#endif /* LWIP_IPV6 */ - ) { - return EAI_FAMILY; - } } else { ai_family = AF_UNSPEC; } - if (servname != NULL) { - /* service name specified: convert to port number - * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */ - port_nr = atoi(servname); - if (port_nr == 0 && (servname[0] != '0')) { - /* atoi failed - service was not numeric */ - return EAI_SERVICE; - } - if ((port_nr < 0) || (port_nr > 0xffff)) { - return EAI_SERVICE; - } - } - - if (nodename != NULL) { - /* service location specified, try to resolve */ - if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) { - /* no DNS lookup, just parse for an address string */ - if (!ipaddr_aton(nodename, &addr)) { - return EAI_NONAME; - } -#if LWIP_IPV4 && LWIP_IPV6 - if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) || - (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) { - return EAI_NONAME; - } -#endif /* LWIP_IPV4 && LWIP_IPV6 */ - } else { -#if LWIP_IPV4 && LWIP_IPV6 - /* AF_UNSPEC: prefer IPv4 */ - u8_t type = NETCONN_DNS_IPV4_IPV6; - if (ai_family == AF_INET) { - type = NETCONN_DNS_IPV4; - } else if (ai_family == AF_INET6) { - type = NETCONN_DNS_IPV6; -#if ESP_LWIP - if (hints->ai_flags & AI_V4MAPPED) { - type = NETCONN_DNS_IPV6_IPV4; - } -#endif /* ESP_LWIP */ - } -#endif /* LWIP_IPV4 && LWIP_IPV6 */ - err = netconn_gethostbyname_addrtype(nodename, &addr, type); - if (err != ERR_OK) { - return EAI_FAIL; - } - } - } else { - /* service location specified, use loopback address */ - if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) { - ip_addr_set_any_val(ai_family == AF_INET6, addr); - } else { - ip_addr_set_loopback_val(ai_family == AF_INET6, addr); - } - } - -#if ESP_LWIP && LWIP_IPV4 && LWIP_IPV6 if (ai_family == AF_INET6 && (hints->ai_flags & AI_V4MAPPED) && IP_GET_TYPE(&addr) == IPADDR_TYPE_V4) { /* Convert native V4 address to a V4-mapped IPV6 address */ @@ -419,7 +379,8 @@ lwip_getaddrinfo(const char *nodename, const char *servname, ai->ai_socktype = hints->ai_socktype; ai->ai_protocol = hints->ai_protocol; } - if (nodename != NULL) { + if ((idx == 0) && (nodename != NULL) && (hints != NULL) + && (hints->ai_flags & AI_CANONNAME)) { /* copy nodename to canonname if specified */ ai->ai_canonname = ((char *)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage)); MEMCPY(ai->ai_canonname, nodename, namelen); @@ -430,6 +391,156 @@ lwip_getaddrinfo(const char *nodename, const char *servname, *res = ai; + return ERR_OK; +} + +/** + * Translates the name of a service location (for example, a host name) and/or + * a service name and returns a set of socket addresses and associated + * information to be used in creating a socket with which to address the + * specified service. + * Memory for the result is allocated internally and must be freed by calling + * lwip_freeaddrinfo()! + * + * dns_gethostbyname can return as many address as configured in DNS_MAX_HOST_IP. + * Also, service names are not supported (only port numbers)! + * + * @param nodename descriptive name or address string of the host + * (may be NULL -> local address) + * @param servname port number as string of NULL + * @param hints structure containing input values that set socktype and protocol + * @param res pointer to a pointer where to store the result (set to NULL on failure) + * @return 0 on success, non-zero on failure + * + * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG + */ +int +lwip_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + err_t err; + ip_addr_t addr[DNS_MAX_HOST_IP]={0}, addr_zero={0}; + u8_t ipaddr_cnt = 0; + struct addrinfo *ai=NULL, *ai_head=NULL, *ai_tail=NULL; + int port_nr = 0; + int ai_family; + int ret; + u8_t i; + + if (res == NULL) { + return EAI_FAIL; + } + *res = NULL; + if ((nodename == NULL) && (servname == NULL)) { + return EAI_NONAME; + } + + if (hints != NULL) { + ai_family = hints->ai_family; + if ((ai_family != AF_UNSPEC) +#if LWIP_IPV4 + && (ai_family != AF_INET) +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 + && (ai_family != AF_INET6) +#endif /* LWIP_IPV6 */ + ) { + return EAI_FAMILY; + } + } else { + ai_family = AF_UNSPEC; + } + + if (servname != NULL) { + /* service name specified: convert to port number + * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */ + port_nr = atoi(servname); + if (port_nr == 0 && (servname[0] != '0')) { + /* atoi failed - service was not numeric */ + return EAI_SERVICE; + } + if ((port_nr < 0) || (port_nr > 0xffff)) { + return EAI_SERVICE; + } + } + + if (nodename != NULL) { + /* service location specified, try to resolve */ + if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) { + /* no DNS lookup, just parse for an address string */ + if (!ipaddr_aton(nodename, &addr[0])) { + return EAI_NONAME; + } +#if LWIP_IPV4 && LWIP_IPV6 + if ((IP_IS_V6_VAL(addr[0]) && ai_family == AF_INET) || + (IP_IS_V4_VAL(addr[0]) && ai_family == AF_INET6)) { + return EAI_NONAME; + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + } else { +#if LWIP_IPV4 && LWIP_IPV6 + /* AF_UNSPEC: prefer IPv4 */ + u8_t type = NETCONN_DNS_IPV4_IPV6; + if (ai_family == AF_INET) { + type = NETCONN_DNS_IPV4; + } else if (ai_family == AF_INET6) { + type = NETCONN_DNS_IPV6; +#if ESP_LWIP + if (hints->ai_flags & AI_V4MAPPED) { + type = NETCONN_DNS_IPV6_IPV4; + } +#endif /* ESP_LWIP */ + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + err = netconn_gethostbyname_addrtype(nodename, addr, type); + if (err != ERR_OK) { + return EAI_FAIL; + } + } + } else { + /* service location specified, use loopback address */ + if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) { + ip_addr_set_any_val(ai_family == AF_INET6, addr[0]); + } else { + ip_addr_set_loopback_val(ai_family == AF_INET6, addr[0]); + } + } + + COUNT_NON_ZERO_IP_ADDRESSES(addr, ipaddr_cnt); + + if (ipaddr_cnt == 0) { + /* handle 0.0.0.0 */ + ret = create_addrinfo(addr[0], nodename, hints, port_nr, &ai, 0); + if (ret != ERR_OK) { + *res = NULL; + return ret; + } + *res = ai; + } else { + for (i=0; iai_next = ai; + } + ai_tail = ai; + ai_tail->ai_next = NULL; + } + } + } + *res = ai_head; + } + return 0; } diff --git a/src/core/dns.c b/src/core/dns.c index 4401318c..b3288820 100644 --- a/src/core/dns.c +++ b/src/core/dns.c @@ -227,8 +227,9 @@ typedef enum { /** DNS table entry */ struct dns_table_entry { - u32_t ttl; - ip_addr_t ipaddr; + u32_t ttl[DNS_MAX_HOST_IP]; + ip_addr_t ipaddr[DNS_MAX_HOST_IP]; + u8_t ipaddr_cnt; u16_t txid; u8_t state; u8_t server_idx; @@ -680,7 +681,6 @@ static err_t dns_lookup(const char *name, size_t hostnamelen, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)) { size_t namelen; - u8_t i; #if DNS_LOCAL_HOSTLIST if (dns_lookup_local(name, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) { return ERR_OK; @@ -694,18 +694,29 @@ dns_lookup(const char *name, size_t hostnamelen, ip_addr_t *addr LWIP_DNS_ADDRTY namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH - 1); /* Walk through name list, return entry if found. If not, return NULL. */ - for (i = 0; i < DNS_TABLE_SIZE; ++i) { - if ((dns_table[i].state == DNS_STATE_DONE) && - (lwip_strnicmp(name, dns_table[i].name, namelen) == 0) && - !dns_table[i].name[namelen] && - LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) { - LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); - ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr); - LWIP_DEBUGF(DNS_DEBUG, ("\n")); - if (addr) { - ip_addr_copy(*addr, dns_table[i].ipaddr); + { + u8_t i, j; + u8_t out_idx=0; + + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + if ((dns_table[i].state == DNS_STATE_DONE) && + (lwip_strnicmp(name, dns_table[i].name, namelen) == 0) && + !dns_table[i].name[namelen]) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); + for (j=0; jstate) { case DNS_STATE_NEW: @@ -1146,7 +1158,7 @@ dns_check_entry(u8_t i) } #endif /* send DNS packet for this entry */ - err = dns_send(i); + err = dns_send(idx); if (err != ERR_OK) { LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, ("dns_send returned error: %s\n", lwip_strerr(err))); @@ -1173,7 +1185,7 @@ dns_check_entry(u8_t i) } else { LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name)); /* call specified callback function if provided */ - dns_call_found(i, NULL); + dns_call_found(idx, NULL); /* flush this entry */ entry->state = DNS_STATE_UNUSED; break; @@ -1184,7 +1196,7 @@ dns_check_entry(u8_t i) } /* send DNS packet for this entry */ - err = dns_send(i); + err = dns_send(idx); if (err != ERR_OK) { LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, ("dns_send returned error: %s\n", lwip_strerr(err))); @@ -1193,7 +1205,15 @@ dns_check_entry(u8_t i) break; case DNS_STATE_DONE: /* if the time to live is nul */ - if ((entry->ttl == 0) || (--entry->ttl == 0)) { + initial_ipaddr_cnt = entry->ipaddr_cnt; + for (i=0; ittl[i] == 0) || (--entry->ttl[i] == 0)) { + ip_addr_set_zero(&entry->ipaddr[i]); + entry->ipaddr_cnt--; + } + } + + if (entry->ipaddr_cnt == 0) { LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name)); /* flush this entry, there cannot be any related pending entries in this state */ entry->state = DNS_STATE_UNUSED; @@ -1222,32 +1242,39 @@ dns_check_entries(void) } /** - * Save TTL and call dns_call_found for correct response. + * Call dns_call_found for correct response. */ static void -dns_correct_response(u8_t idx, u32_t ttl) +dns_correct_response(u8_t idx) { + u8_t i; + u8_t initial_ipaddr_cnt; struct dns_table_entry *entry = &dns_table[idx]; entry->state = DNS_STATE_DONE; LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name)); - ip_addr_debug_print_val(DNS_DEBUG, entry->ipaddr); - LWIP_DEBUGF(DNS_DEBUG, ("\n")); - - /* read the answer resource record's TTL, and maximize it if needed */ - entry->ttl = ttl; - if (entry->ttl > DNS_MAX_TTL) { - entry->ttl = DNS_MAX_TTL; + for (i=0; iipaddr_cnt; i++) { + ip_addr_debug_print_val(DNS_DEBUG, entry->ipaddr[i]); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); } - dns_call_found(idx, &entry->ipaddr); - if (entry->ttl == 0) { - /* RFC 883, page 29: "Zero values are - interpreted to mean that the RR can only be used for the - transaction in progress, and should not be cached." - -> flush this entry now */ - /* entry reused during callback? */ + dns_call_found(idx, entry->ipaddr); + + initial_ipaddr_cnt = entry->ipaddr_cnt; + for (i=0; ittl[i] == 0) { + /* RFC 883, page 29: "Zero values are + interpreted to mean that the RR can only be used for the + transaction in progress, and should not be cached." + -> flush this entry now */ + /* entry reused during callback? */ + ip_addr_set_zero(&entry->ipaddr[i]); + entry->ipaddr_cnt--; + } + } + + if (entry->ipaddr_cnt == 0) { if (entry->state == DNS_STATE_DONE) { entry->state = DNS_STATE_UNUSED; } @@ -1261,6 +1288,7 @@ static void dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { u8_t i; + u8_t initial_ipaddr_cnt; u16_t txid; u16_t res_idx; struct dns_hdr hdr; @@ -1356,6 +1384,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, goto ignore_packet; } } else { + initial_ipaddr_cnt = entry->ipaddr_cnt; while ((nanswers > 0) && (res_idx < p->tot_len)) { /* skip answer resource record's host name */ res_idx = dns_skip_name(p, res_idx); @@ -1372,7 +1401,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, } res_idx = (u16_t)(res_idx + SIZEOF_DNS_ANSWER); - if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) { + if ((ans.cls == PP_HTONS(DNS_RRCLASS_IN)) && (entry->ipaddr_cnt < DNS_MAX_HOST_IP)) { #if LWIP_IPV4 if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) { #if LWIP_IPV4 && LWIP_IPV6 @@ -1384,11 +1413,14 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, if (pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx) != sizeof(ip4_addr_t)) { goto ignore_packet; /* ignore this packet */ } - ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr); - pbuf_free(p); - /* handle correct response */ - dns_correct_response(i, lwip_ntohl(ans.ttl)); - return; + ip_addr_copy_from_ip4(entry->ipaddr[entry->ipaddr_cnt], ip4addr); + + /* read the answer resource record's TTL, and maximize it if needed */ + entry->ttl[entry->ipaddr_cnt] = lwip_ntohl(ans.ttl); + if (entry->ttl[entry->ipaddr_cnt] > DNS_MAX_TTL) { + entry->ttl[entry->ipaddr_cnt] = DNS_MAX_TTL; + } + entry->ipaddr_cnt++; } } #endif /* LWIP_IPV4 */ @@ -1404,11 +1436,14 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, goto ignore_packet; /* ignore this packet */ } /* @todo: scope ip6addr? Might be required for link-local addresses at least? */ - ip_addr_copy_from_ip6_packed(dns_table[i].ipaddr, ip6addr); - pbuf_free(p); - /* handle correct response */ - dns_correct_response(i, lwip_ntohl(ans.ttl)); - return; + ip_addr_copy_from_ip6_packed(entry->ipaddr[entry->ipaddr_cnt], ip6addr); + + /* read the answer resource record's TTL, and maximize it if needed */ + entry->ttl[entry->ipaddr_cnt] = lwip_ntohl(ans.ttl); + if (entry->ttl[entry->ipaddr_cnt] > DNS_MAX_TTL) { + entry->ttl[entry->ipaddr_cnt] = DNS_MAX_TTL; + } + entry->ipaddr_cnt++; } } #endif /* LWIP_IPV6 */ @@ -1420,6 +1455,14 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, res_idx = (u16_t)(res_idx + lwip_htons(ans.len)); --nanswers; } + + if (initial_ipaddr_cnt < entry->ipaddr_cnt) { + pbuf_free(p); + /* handle correct response */ + dns_correct_response(i); + return; + } + #if LWIP_IPV4 && LWIP_IPV6 if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) { @@ -1442,6 +1485,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, pbuf_free(p); dns_call_found(i, NULL); dns_table[i].state = DNS_STATE_UNUSED; + entry->ipaddr_cnt = 0; return; } } @@ -1565,6 +1609,7 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, /* fill the entry */ entry->state = DNS_STATE_NEW; entry->seqno = dns_seqno; + entry->ipaddr_cnt = 0; LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype); LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype); req->found = found; @@ -1578,6 +1623,7 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, /* failed to get a UDP pcb */ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name)); entry->state = DNS_STATE_UNUSED; + entry->ipaddr_cnt = 0; req->found = NULL; return ERR_MEM; } @@ -1616,7 +1662,7 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, * - ERR_ARG: dns client not initialized or invalid hostname * * @param hostname the hostname that is to be queried - * @param addr pointer to a ip_addr_t where to store the address if it is already + * @param addr pointer to an array of ip_addr_t where to store the addresses if they are already * cached in the dns_table (only valid if ERR_OK is returned!) * @param found a callback function to be called on success, failure or timeout (only if * ERR_INPROGRESS is returned!) @@ -1647,7 +1693,7 @@ static bool dns_server_is_set (void) * @ingroup dns * Like dns_gethostbyname, but returned address type can be controlled: * @param hostname the hostname that is to be queried - * @param addr pointer to a ip_addr_t where to store the address if it is already + * @param addr pointer to an array of ip_addr_t where to store the addresses if they are already * cached in the dns_table (only valid if ERR_OK is returned!) * @param found a callback function to be called on success, failure or timeout (only if * ERR_INPROGRESS is returned!) diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index 28a5147c..1507c3ff 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -1153,6 +1153,11 @@ #define DNS_MAX_NAME_LENGTH 256 #endif +/** DNS maximum number of ip address stored per host. */ +#if !defined DNS_MAX_HOST_IP || defined __DOXYGEN__ +#define DNS_MAX_HOST_IP 1 +#endif + /** The maximum of DNS servers * The first server can be initialized automatically by defining * DNS_SERVER_ADDRESS(ipaddr), where 'ipaddr' is an 'ip_addr_t*'