dns: add addr_cnt to DNS resolution to fix multi-IP buffer overflow

Introduce dns_gethostbyname_addrtype_n and netconn_gethostbyname_*_n so
callers pass how many ip_addr_t slots they provide. Thread addr_cnt
through dns_lookup, dns_enqueue, dns_requests, and struct dns_api_msg;
copy only addr_cnt entries in lwip_netconn_do_dns_found instead of always
writing DNS_MAX_HOST_IP.

Keep the single-address helpers as thin wrappers (addr_cnt == 1).
lwip_gethostbyname and lwip_getaddrinfo use DNS_MAX_HOST_IP via the _n
APIs. Bound dns_lookup cache output to addr_cnt; zero new dns_table
entry ipaddr slots. Clarify the cached-address doc for
dns_gethostbyname_addrtype.
This commit is contained in:
Abhik Roy
2026-05-12 22:44:22 +10:00
parent a4e2409d0d
commit 6233a15612
7 changed files with 115 additions and 10 deletions
+41
View File
@@ -1272,9 +1272,46 @@ netconn_join_leave_group_netif(struct netconn *conn,
/** @param dns_addrtype IP address type (IPv4 / IPv6) */ /** @param dns_addrtype IP address type (IPv4 / IPv6) */
err_t err_t
netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype) netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
{
return netconn_gethostbyname_addrtype_n(name, addr, 1, dns_addrtype);
}
/**
* @ingroup netconn_common
* Execute a DNS query, can return multiple IP addresses
*
* @param name a string representation of the DNS host name to query
* @param addr a preallocated array of ip_addr_t where to store the resolved IP addresses
* @param addr_cnt number of addresses requested; must be > 0 and <= DNS_MAX_HOST_IP
* @param dns_addrtype IP address type (IPv4 / IPv6)
* @return ERR_OK: resolving succeeded
* ERR_MEM: memory error, try again later
* ERR_ARG: dns client not initialized, invalid hostname, or invalid addr_cnt
* ERR_VAL: dns server response was invalid
*/
err_t
netconn_gethostbyname_addrtype_n(const char *name, ip_addr_t *addr, u8_t addr_cnt, u8_t dns_addrtype)
#else #else
err_t err_t
netconn_gethostbyname(const char *name, ip_addr_t *addr) netconn_gethostbyname(const char *name, ip_addr_t *addr)
{
return netconn_gethostbyname_n(name, addr, 1);
}
/**
* @ingroup netconn_common
* Execute a DNS query, can return multiple IP addresses
*
* @param name a string representation of the DNS host name to query
* @param addr a preallocated array of ip_addr_t where to store the resolved IP addresses
* @param addr_cnt number of addresses requested; must be > 0 and <= DNS_MAX_HOST_IP
* @return ERR_OK: resolving succeeded
* ERR_MEM: memory error, try again later
* ERR_ARG: dns client not initialized, invalid hostname, or invalid addr_cnt
* ERR_VAL: dns server response was invalid
*/
err_t
netconn_gethostbyname_n(const char *name, ip_addr_t *addr, u8_t addr_cnt)
#endif #endif
{ {
API_VAR_DECLARE(struct dns_api_msg, msg); API_VAR_DECLARE(struct dns_api_msg, msg);
@@ -1286,6 +1323,9 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr)
LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
if ((addr_cnt == 0) || (addr_cnt > DNS_MAX_HOST_IP)) {
return ERR_ARG;
}
#if LWIP_MPU_COMPATIBLE #if LWIP_MPU_COMPATIBLE
if (strlen(name) >= DNS_MAX_NAME_LENGTH) { if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
return ERR_ARG; return ERR_ARG;
@@ -1314,6 +1354,7 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr)
API_VAR_REF(msg).addr = API_VAR_REF(addr); API_VAR_REF(msg).addr = API_VAR_REF(addr);
API_VAR_REF(msg).name = name; API_VAR_REF(msg).name = name;
#endif /* LWIP_MPU_COMPATIBLE */ #endif /* LWIP_MPU_COMPATIBLE */
API_VAR_REF(msg).addr_cnt = addr_cnt;
#if LWIP_IPV4 && LWIP_IPV6 #if LWIP_IPV4 && LWIP_IPV6
API_VAR_REF(msg).dns_addrtype = dns_addrtype; API_VAR_REF(msg).dns_addrtype = dns_addrtype;
#endif /* LWIP_IPV4 && LWIP_IPV6 */ #endif /* LWIP_IPV4 && LWIP_IPV6 */
+3 -3
View File
@@ -2212,7 +2212,7 @@ lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
/* address was resolved */ /* address was resolved */
API_EXPR_DEREF(msg->err) = ERR_OK; API_EXPR_DEREF(msg->err) = ERR_OK;
for (i=0; i<DNS_MAX_HOST_IP; i++) { for (i=0; i<msg->addr_cnt; i++) {
API_EXPR_DEREF(msg->addr+i) = *(ipaddr+i); API_EXPR_DEREF(msg->addr+i) = *(ipaddr+i);
} }
} }
@@ -2237,8 +2237,8 @@ lwip_netconn_do_gethostbyname(void *arg)
LWIP_DNS_ADDRTYPE_DEFAULT; LWIP_DNS_ADDRTYPE_DEFAULT;
#endif #endif
API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name, API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype_n(msg->name,
API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype); API_EXPR_REF(msg->addr), msg->addr_cnt, lwip_netconn_do_dns_found, msg, addrtype);
#if LWIP_TCPIP_CORE_LOCKING #if LWIP_TCPIP_CORE_LOCKING
/* For core locking, only block if we need to wait for answer/timeout */ /* For core locking, only block if we need to wait for answer/timeout */
if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) { if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
+2 -2
View File
@@ -115,7 +115,7 @@ lwip_gethostbyname(const char *name)
HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1]; HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
/* query host IP address */ /* query host IP address */
err = netconn_gethostbyname(name, addr); err = netconn_gethostbyname_n(name, addr, DNS_MAX_HOST_IP);
if (err != ERR_OK) { if (err != ERR_OK) {
LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
h_errno = HOST_NOT_FOUND; h_errno = HOST_NOT_FOUND;
@@ -492,7 +492,7 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
#endif /* ESP_LWIP */ #endif /* ESP_LWIP */
} }
#endif /* LWIP_IPV4 && LWIP_IPV6 */ #endif /* LWIP_IPV4 && LWIP_IPV6 */
err = netconn_gethostbyname_addrtype(nodename, addr, type); err = netconn_gethostbyname_addrtype_n(nodename, addr, DNS_MAX_HOST_IP, type);
if (err != ERR_OK) { if (err != ERR_OK) {
return EAI_FAIL; return EAI_FAIL;
} }
+37 -5
View File
@@ -693,7 +693,7 @@ dns_local_addhost(const char *hostname, const ip_addr_t *addr)
* @return ERR_OK if found, ERR_ARG if not found * @return ERR_OK if found, ERR_ARG if not found
*/ */
static err_t static err_t
dns_lookup(const char *name, size_t hostnamelen, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)) dns_lookup(const char *name, size_t hostnamelen, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype), u8_t addr_cnt)
{ {
size_t namelen; size_t namelen;
#if DNS_LOCAL_HOSTLIST #if DNS_LOCAL_HOSTLIST
@@ -722,7 +722,7 @@ dns_lookup(const char *name, size_t hostnamelen, ip_addr_t *addr LWIP_DNS_ADDRTY
if (LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr[j])) { if (LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr[j])) {
ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr[j]); ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr[j]);
LWIP_DEBUGF(DNS_DEBUG, ("\n")); LWIP_DEBUGF(DNS_DEBUG, ("\n"));
if (addr) { if (addr && out_idx < addr_cnt) {
ip_addr_copy(addr[out_idx], dns_table[i].ipaddr[j]); ip_addr_copy(addr[out_idx], dns_table[i].ipaddr[j]);
out_idx++; out_idx++;
} }
@@ -1625,6 +1625,12 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
entry->state = DNS_STATE_NEW; entry->state = DNS_STATE_NEW;
entry->seqno = dns_seqno; entry->seqno = dns_seqno;
entry->ipaddr_cnt = 0; entry->ipaddr_cnt = 0;
{
u8_t j;
for (j = 0; j < DNS_MAX_HOST_IP; j++) {
ip_addr_set_zero(&entry->ipaddr[j]);
}
}
LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype); LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype);
LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype); LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype);
req->found = found; req->found = found;
@@ -1708,7 +1714,7 @@ static bool dns_server_is_set (void)
* @ingroup dns * @ingroup dns
* Like dns_gethostbyname, but returned address type can be controlled: * Like dns_gethostbyname, but returned address type can be controlled:
* @param hostname the hostname that is to be queried * @param hostname the hostname that is to be queried
* @param addr pointer to an array of ip_addr_t where to store the addresses if they are already * @param addr pointer to an ip_addr_t where to store the address if it is already
* cached in the dns_table (only valid if ERR_OK is returned!) * 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 * @param found a callback function to be called on success, failure or timeout (only if
* ERR_INPROGRESS is returned!) * ERR_INPROGRESS is returned!)
@@ -1721,6 +1727,29 @@ static bool dns_server_is_set (void)
err_t err_t
dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found, dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
void *callback_arg, u8_t dns_addrtype) void *callback_arg, u8_t dns_addrtype)
{
return dns_gethostbyname_addrtype_n(hostname, addr, 1, found, callback_arg, dns_addrtype);
}
/**
* @ingroup dns
* Like dns_gethostbyname_addrtype, but can return multiple addresses:
* @param hostname the hostname that is to be queried
* @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 addr_cnt number of addresses requested; must be > 0 and <= DNS_MAX_HOST_IP
* @param found a callback function to be called on success, failure or timeout (only if
* ERR_INPROGRESS is returned!)
* @param callback_arg argument to pass to the callback function
* @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
* - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
* - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
* - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
* @return ERR_ARG if addr_cnt is 0 or greater than DNS_MAX_HOST_IP (among other invalid parameters)
*/
err_t
dns_gethostbyname_addrtype_n(const char *hostname, ip_addr_t *addr, u8_t addr_cnt, dns_found_callback found,
void *callback_arg, u8_t dns_addrtype)
{ {
size_t hostnamelen; size_t hostnamelen;
#if LWIP_DNS_SUPPORT_MDNS_QUERIES #if LWIP_DNS_SUPPORT_MDNS_QUERIES
@@ -1732,6 +1761,9 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call
(!hostname) || (!hostname[0])) { (!hostname) || (!hostname[0])) {
return ERR_ARG; return ERR_ARG;
} }
if ((addr_cnt == 0) || (addr_cnt > DNS_MAX_HOST_IP)) {
return ERR_ARG;
}
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0) #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
if (dns_pcbs[0] == NULL) { if (dns_pcbs[0] == NULL) {
return ERR_ARG; return ERR_ARG;
@@ -1773,7 +1805,7 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call
} }
} }
/* already have this address cached? */ /* already have this address cached? */
if (dns_lookup(hostname, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) { if (dns_lookup(hostname, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype), addr_cnt) == ERR_OK) {
return ERR_OK; return ERR_OK;
} }
#if LWIP_IPV4 && LWIP_IPV6 #if LWIP_IPV4 && LWIP_IPV6
@@ -1785,7 +1817,7 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call
} else { } else {
fallback = LWIP_DNS_ADDRTYPE_IPV4; fallback = LWIP_DNS_ADDRTYPE_IPV4;
} }
if (dns_lookup(hostname, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) { if (dns_lookup(hostname, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(fallback), addr_cnt) == ERR_OK) {
return ERR_OK; return ERR_OK;
} }
} }
+4
View File
@@ -358,10 +358,14 @@ err_t netconn_join_leave_group_netif(struct netconn *conn, const ip_addr_t *mu
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
#if LWIP_DNS #if LWIP_DNS
#if LWIP_IPV4 && LWIP_IPV6 #if LWIP_IPV4 && LWIP_IPV6
err_t netconn_gethostbyname_addrtype_n(const char *name, ip_addr_t *addr, u8_t addr_cnt, u8_t dns_addrtype);
err_t netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype); err_t netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype);
#define netconn_gethostbyname_n(name, addr, addr_cnt) netconn_gethostbyname_addrtype_n(name, addr, addr_cnt, NETCONN_DNS_DEFAULT)
#define netconn_gethostbyname(name, addr) netconn_gethostbyname_addrtype(name, addr, NETCONN_DNS_DEFAULT) #define netconn_gethostbyname(name, addr) netconn_gethostbyname_addrtype(name, addr, NETCONN_DNS_DEFAULT)
#else /* LWIP_IPV4 && LWIP_IPV6 */ #else /* LWIP_IPV4 && LWIP_IPV6 */
err_t netconn_gethostbyname_n(const char *name, ip_addr_t *addr, u8_t addr_cnt);
err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
#define netconn_gethostbyname_addrtype_n(name, addr, addr_cnt, dns_addrtype) netconn_gethostbyname_n(name, addr, addr_cnt)
#define netconn_gethostbyname_addrtype(name, addr, dns_addrtype) netconn_gethostbyname(name, addr) #define netconn_gethostbyname_addrtype(name, addr, dns_addrtype) netconn_gethostbyname(name, addr)
#endif /* LWIP_IPV4 && LWIP_IPV6 */ #endif /* LWIP_IPV4 && LWIP_IPV6 */
#endif /* LWIP_DNS */ #endif /* LWIP_DNS */
+19
View File
@@ -111,6 +111,25 @@ err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr,
err_t dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, err_t dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr,
dns_found_callback found, void *callback_arg, dns_found_callback found, void *callback_arg,
u8_t dns_addrtype); u8_t dns_addrtype);
/**
* @ingroup dns
* Like dns_gethostbyname_addrtype, but can return multiple addresses:
* @param hostname the hostname that is to be queried
* @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 addr_cnt number of addresses requested; must be > 0 and <= DNS_MAX_HOST_IP
* @param found a callback function to be called on success, failure or timeout (only if
* ERR_INPROGRESS is returned!)
* @param callback_arg argument to pass to the callback function
* @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
* - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
* - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
* - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
* @return ERR_ARG if addr_cnt is 0 or greater than DNS_MAX_HOST_IP (among other invalid parameters)
*/
err_t dns_gethostbyname_addrtype_n(const char *hostname, ip_addr_t *addr, u8_t addr_cnt,
dns_found_callback found, void *callback_arg,
u8_t dns_addrtype);
void dns_clear_cache(void); void dns_clear_cache(void);
#if DNS_LOCAL_HOSTLIST #if DNS_LOCAL_HOSTLIST
+9
View File
@@ -166,6 +166,13 @@ struct api_msg {
it has its own struct (to avoid struct api_msg getting bigger than necessary). it has its own struct (to avoid struct api_msg getting bigger than necessary).
lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg
(see netconn_gethostbyname). */ (see netconn_gethostbyname). */
#if LWIP_MPU_COMPATIBLE && (DNS_MAX_HOST_IP > 1)
/* When LWIP_MPU_COMPATIBLE is enabled dns_api_msg embeds a single ip_addr_t
(not a pointer), so the multi-address copy loop in lwip_netconn_do_dns_found
would overflow into adjacent struct members. Either disable MPU compat or
keep DNS_MAX_HOST_IP == 1 until dns_api_msg is extended to an array. */
#error "DNS_MAX_HOST_IP > 1 is not supported with LWIP_MPU_COMPATIBLE"
#endif
struct dns_api_msg { struct dns_api_msg {
/** Hostname to query or dotted IP address string */ /** Hostname to query or dotted IP address string */
#if LWIP_MPU_COMPATIBLE #if LWIP_MPU_COMPATIBLE
@@ -179,6 +186,8 @@ struct dns_api_msg {
/** Type of resolve call */ /** Type of resolve call */
u8_t dns_addrtype; u8_t dns_addrtype;
#endif /* LWIP_IPV4 && LWIP_IPV6 */ #endif /* LWIP_IPV4 && LWIP_IPV6 */
/** Number of addresses requested */
u8_t addr_cnt;
/** This semaphore is posted when the name is resolved, the application thread /** This semaphore is posted when the name is resolved, the application thread
should wait on it. */ should wait on it. */
sys_sem_t API_MSG_M_DEF_SEM(sem); sys_sem_t API_MSG_M_DEF_SEM(sem);