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) */
err_t
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
err_t
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
{
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 addr", (addr != NULL), return ERR_ARG;);
if ((addr_cnt == 0) || (addr_cnt > DNS_MAX_HOST_IP)) {
return ERR_ARG;
}
#if LWIP_MPU_COMPATIBLE
if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
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).name = name;
#endif /* LWIP_MPU_COMPATIBLE */
API_VAR_REF(msg).addr_cnt = addr_cnt;
#if LWIP_IPV4 && LWIP_IPV6
API_VAR_REF(msg).dns_addrtype = dns_addrtype;
#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 */
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);
}
}
@@ -2237,8 +2237,8 @@ lwip_netconn_do_gethostbyname(void *arg)
LWIP_DNS_ADDRTYPE_DEFAULT;
#endif
API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype_n(msg->name,
API_EXPR_REF(msg->addr), msg->addr_cnt, lwip_netconn_do_dns_found, msg, addrtype);
#if LWIP_TCPIP_CORE_LOCKING
/* For core locking, only block if we need to wait for answer/timeout */
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];
/* query host IP address */
err = netconn_gethostbyname(name, addr);
err = netconn_gethostbyname_n(name, addr, DNS_MAX_HOST_IP);
if (err != ERR_OK) {
LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
h_errno = HOST_NOT_FOUND;
@@ -492,7 +492,7 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
#endif /* ESP_LWIP */
}
#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) {
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
*/
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;
#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])) {
ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr[j]);
LWIP_DEBUGF(DNS_DEBUG, ("\n"));
if (addr) {
if (addr && out_idx < addr_cnt) {
ip_addr_copy(addr[out_idx], dns_table[i].ipaddr[j]);
out_idx++;
}
@@ -1625,6 +1625,12 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
entry->state = DNS_STATE_NEW;
entry->seqno = dns_seqno;
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(req->reqaddrtype, dns_addrtype);
req->found = found;
@@ -1708,7 +1714,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 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!)
* @param found a callback function to be called on success, failure or timeout (only if
* ERR_INPROGRESS is returned!)
@@ -1721,6 +1727,29 @@ static bool dns_server_is_set (void)
err_t
dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
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;
#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])) {
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 (dns_pcbs[0] == NULL) {
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? */
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;
}
#if LWIP_IPV4 && LWIP_IPV6
@@ -1785,7 +1817,7 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call
} else {
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;
}
}
+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) */
#if LWIP_DNS
#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);
#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)
#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);
#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)
#endif /* LWIP_IPV4 && LWIP_IPV6 */
#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,
dns_found_callback found, void *callback_arg,
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);
#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).
lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg
(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 {
/** Hostname to query or dotted IP address string */
#if LWIP_MPU_COMPATIBLE
@@ -179,6 +186,8 @@ struct dns_api_msg {
/** Type of resolve call */
u8_t dns_addrtype;
#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
should wait on it. */
sys_sem_t API_MSG_M_DEF_SEM(sem);