This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 345643d5ae4 libs/libc/netdb: default ai_protocol and multi-transport
in getaddrinfo
345643d5ae4 is described below
commit 345643d5ae409f288e311bc8f400820cf382a9b8
Author: Swatantra Yadav <[email protected]>
AuthorDate: Fri Oct 16 00:09:23 2026 +0530
libs/libc/netdb: default ai_protocol and multi-transport in getaddrinfo
When getaddrinfo() is called with hints->ai_socktype set to
SOCK_STREAM or SOCK_DGRAM and hints->ai_protocol set to 0,
standard networking implementations (KAME, BSD, glibc, musl)
populate ai_protocol with IPPROTO_TCP or IPPROTO_UDP
respectively.
Previously, NuttX left ai_protocol as 0, which broke applications
and runtimes (such as WAMR) that validate or rely on standard
ai_protocol values.
Furthermore, POSIX requires that when both ai_socktype and
ai_protocol are unspecified (0), getaddrinfo() returns all
supported transports (e.g., both SOCK_STREAM/IPPROTO_TCP and
SOCK_DGRAM/IPPROTO_UDP) for each resolved address.
This patch:
1. Normalizes ai_protocol to IPPROTO_TCP for SOCK_STREAM and
IPPROTO_UDP for SOCK_DGRAM when ai_protocol is 0 under Internet
address families (AF_INET, AF_INET6, AF_UNSPEC).
2. Validates compatibility between ai_socktype and ai_protocol,
returning EAI_SOCKTYPE for incompatible combinations.
3. Preserves explicit non-zero ai_protocol values without
overwriting them during service resolution.
4. Returns separate records for all supported transports (both TCP
and UDP) when ai_socktype and ai_protocol are 0.
5. Rejects SOCK_RAW with EAI_SERVICE when resolving named services.
Resolves #16693
Signed-off-by: Swatantra Yadav <[email protected]>
---
libs/libc/netdb/lib_getaddrinfo.c | 503 ++++++++++++++++++++++----------------
1 file changed, 288 insertions(+), 215 deletions(-)
diff --git a/libs/libc/netdb/lib_getaddrinfo.c
b/libs/libc/netdb/lib_getaddrinfo.c
index fe1cb844a48..ceb8f8953f6 100644
--- a/libs/libc/netdb/lib_getaddrinfo.c
+++ b/libs/libc/netdb/lib_getaddrinfo.c
@@ -30,15 +30,24 @@
#include <string.h>
#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
#include <nuttx/net/loopback.h>
#include <netpacket/rpmsg.h>
#include <netpacket/vm_sockets.h>
-#include <netdb.h>
#include <sys/un.h>
#include "libc.h"
#include "lib_netdb.h"
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define AI_VALID_FLAGS (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | \
+ AI_NUMERICSERV | AI_V4MAPPED | AI_ALL | \
+ AI_ADDRCONFIG)
+
/****************************************************************************
* Private Data Types
****************************************************************************/
@@ -56,70 +65,100 @@ struct ai_s
} sa;
};
+struct transport_s
+{
+ int socktype;
+ int proto;
+ int port;
+};
+
/****************************************************************************
* Private Functions
****************************************************************************/
-FAR static struct ai_s *alloc_ai(int family, int socktype, int protocol,
- int port, FAR const void *addr)
+/****************************************************************************
+ * Name: add_transports
+ ****************************************************************************/
+
+static FAR struct addrinfo **
+add_transports(FAR struct addrinfo **prev_ai, int family,
+ FAR const struct transport_s *transports,
+ int ntransports, FAR const void *addr,
+ FAR const char *canonname)
{
FAR struct ai_s *ai;
+ int t;
- ai = lib_zalloc(sizeof(struct ai_s));
- if (ai == NULL)
+ for (t = 0; t < ntransports; t++)
{
- return ai;
- }
+ ai = lib_zalloc(sizeof(struct ai_s));
+ if (ai == NULL)
+ {
+ return NULL;
+ }
- ai->ai.ai_addr = (FAR struct sockaddr *)&ai->sa;
- ai->ai.ai_family = family;
- ai->ai.ai_socktype = socktype;
- ai->ai.ai_protocol = protocol;
+ ai->ai.ai_addr = (FAR struct sockaddr *)&ai->sa;
+ ai->ai.ai_family = family;
+ ai->ai.ai_socktype = transports[t].socktype;
+ ai->ai.ai_protocol = transports[t].proto;
+ ai->ai.ai_canonname = (FAR char *)canonname;
- switch (family)
- {
+ switch (family)
+ {
#ifdef CONFIG_NET_LOCAL
- case AF_LOCAL:
- ai->ai.ai_addrlen = sizeof(struct sockaddr_un);
- ai->sa.sun.sun_family = AF_LOCAL;
- strlcpy(ai->sa.sun.sun_path, addr, sizeof(ai->sa.sun.sun_path));
- break;
+ case AF_LOCAL:
+ ai->ai.ai_addrlen = sizeof(struct sockaddr_un);
+ ai->sa.sun.sun_family = AF_LOCAL;
+ strlcpy(ai->sa.sun.sun_path, addr, sizeof(ai->sa.sun.sun_path));
+ break;
#endif
#ifdef CONFIG_NET_IPv4
- case AF_INET:
- ai->ai.ai_addrlen = sizeof(struct sockaddr_in);
- ai->sa.sin.sin_family = AF_INET;
- ai->sa.sin.sin_port = port; /* Already network order */
- memcpy(&ai->sa.sin.sin_addr, addr, sizeof(ai->sa.sin.sin_addr));
- break;
+ case AF_INET:
+ ai->ai.ai_addrlen = sizeof(struct sockaddr_in);
+ ai->sa.sin.sin_family = AF_INET;
+ ai->sa.sin.sin_port = transports[t].port;
+ if (addr != NULL)
+ {
+ memcpy(&ai->sa.sin.sin_addr, addr, sizeof(struct in_addr));
+ }
+ break;
#endif
#ifdef CONFIG_NET_IPv6
- case AF_INET6:
- ai->ai.ai_addrlen = sizeof(struct sockaddr_in6);
- ai->sa.sin6.sin6_family = AF_INET6;
- ai->sa.sin6.sin6_port = port; /* Already network order */
- memcpy(&ai->sa.sin6.sin6_addr, addr, sizeof(ai->sa.sin6.sin6_addr));
- break;
+ case AF_INET6:
+ ai->ai.ai_addrlen = sizeof(struct sockaddr_in6);
+ ai->sa.sin6.sin6_family = AF_INET6;
+ ai->sa.sin6.sin6_port = transports[t].port;
+ if (addr != NULL)
+ {
+ memcpy(&ai->sa.sin6.sin6_addr, addr,
+ sizeof(struct in6_addr));
+ }
+ break;
#endif
#ifdef CONFIG_NET_RPMSG
- case AF_RPMSG:
- ai->ai.ai_addrlen = sizeof(struct sockaddr_rpmsg);
- ai->sa.srp.rp_family = AF_RPMSG;
- strlcpy(ai->sa.srp.rp_cpu, addr, sizeof(ai->sa.srp.rp_cpu));
- snprintf(ai->sa.srp.rp_name, sizeof(ai->sa.srp.rp_name), "%d", port);
- break;
+ case AF_RPMSG:
+ ai->ai.ai_addrlen = sizeof(struct sockaddr_rpmsg);
+ ai->sa.srp.rp_family = AF_RPMSG;
+ strlcpy(ai->sa.srp.rp_cpu, addr, sizeof(ai->sa.srp.rp_cpu));
+ snprintf(ai->sa.srp.rp_name, sizeof(ai->sa.srp.rp_name), "%d",
+ transports[t].port);
+ break;
#endif
#ifdef CONFIG_NET_VSOCK
- case AF_VSOCK:
- ai->ai.ai_addrlen = sizeof(struct sockaddr_vm);
- ai->sa.svm.svm_family = AF_VSOCK;
- ai->sa.svm.svm_cid = strtoul(addr, NULL, 0);
- ai->sa.svm.svm_port = ntohl(port);
- break;
+ case AF_VSOCK:
+ ai->ai.ai_addrlen = sizeof(struct sockaddr_vm);
+ ai->sa.svm.svm_family = AF_VSOCK;
+ ai->sa.svm.svm_cid = strtoul(addr, NULL, 0);
+ ai->sa.svm.svm_port = ntohl(transports[t].port);
+ break;
#endif
+ }
+
+ *prev_ai = (FAR struct addrinfo *)ai;
+ prev_ai = &ai->ai.ai_next;
}
- return ai;
+ return prev_ai;
}
/****************************************************************************
@@ -133,18 +172,17 @@ FAR static struct ai_s *alloc_ai(int family, int
socktype, int protocol,
int getaddrinfo(FAR const char *hostname, FAR const char *servname,
FAR const struct addrinfo *hint, FAR struct addrinfo **res)
{
+ FAR const char *canon = NULL;
+ FAR char *hostbuffer = NULL;
+ FAR struct addrinfo **prev_ai = res;
+ struct hostent_s host;
+ struct transport_s transports[2];
+ int ntransports = 0;
int family = AF_UNSPEC;
- int port = 0;
int flags = 0;
int proto = 0;
int socktype = 0;
- FAR char *hostbuffer;
- struct hostent_s host;
- FAR struct ai_s *ai;
- FAR struct ai_s *prev_ai = NULL;
- const int valid_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST |
- AI_NUMERICSERV | AI_V4MAPPED | AI_ALL |
- AI_ADDRCONFIG;
+ int port = 0;
int ret = OK;
int i;
@@ -153,33 +191,85 @@ int getaddrinfo(FAR const char *hostname, FAR const char
*servname,
return EAI_NONAME;
}
- if (hint)
+ if (hint != NULL)
{
family = hint->ai_family;
flags = hint->ai_flags;
proto = hint->ai_protocol;
socktype = hint->ai_socktype;
- if ((flags & valid_flags) != flags)
+ if ((flags & AI_VALID_FLAGS) != flags)
{
return EAI_BADFLAGS;
}
- if (family != AF_INET &&
- family != AF_INET6 &&
- family != AF_LOCAL &&
- family != AF_RPMSG &&
- family != AF_VSOCK &&
- family != AF_UNSPEC)
+ switch (family)
+ {
+#ifdef CONFIG_NET_IPv4
+ case AF_INET:
+#endif
+#ifdef CONFIG_NET_IPv6
+ case AF_INET6:
+#endif
+#ifdef CONFIG_NET_LOCAL
+ case AF_LOCAL:
+#endif
+#ifdef CONFIG_NET_RPMSG
+ case AF_RPMSG:
+#endif
+#ifdef CONFIG_NET_VSOCK
+ case AF_VSOCK:
+#endif
+ case AF_UNSPEC:
+ break;
+ default:
+ return EAI_FAMILY;
+ }
+
+ if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC)
{
- return EAI_FAMILY;
+ if (socktype == SOCK_STREAM)
+ {
+ if (proto == 0)
+ {
+ proto = IPPROTO_TCP;
+ }
+ else if (proto != IPPROTO_TCP)
+ {
+ return EAI_SOCKTYPE;
+ }
+ }
+ else if (socktype == SOCK_DGRAM)
+ {
+ if (proto == 0)
+ {
+ proto = IPPROTO_UDP;
+ }
+ else if (proto != IPPROTO_UDP)
+ {
+ return EAI_SOCKTYPE;
+ }
+ }
+ else if (socktype == 0)
+ {
+ if (proto == IPPROTO_TCP)
+ {
+ socktype = SOCK_STREAM;
+ }
+ else if (proto == IPPROTO_UDP)
+ {
+ socktype = SOCK_DGRAM;
+ }
+ }
+ else if (socktype != SOCK_RAW)
+ {
+ return EAI_SOCKTYPE;
+ }
}
}
if (servname != NULL)
{
- struct servent ent;
- FAR struct servent *sp;
FAR char *endp;
port = strtol(servname, &endp, 10);
@@ -193,186 +283,189 @@ int getaddrinfo(FAR const char *hostname, FAR const
char *servname,
{
return EAI_NONAME;
}
- else if (getservbyname_r(servname, NULL, &ent, NULL, 0, &sp) == OK)
+ else
{
- /* The s_port field of struct servent is required to
- * be in network byte order (per OpenGroup.org)
- */
+ struct servent ent;
+ FAR struct servent *sp;
+
+ if (socktype == SOCK_RAW ||
+ (proto != 0 && proto != IPPROTO_TCP && proto != IPPROTO_UDP))
+ {
+ return EAI_SERVICE;
+ }
- port = sp->s_port;
+ if (socktype != SOCK_DGRAM && proto != IPPROTO_UDP &&
+ getservbyname_r(servname, "tcp", &ent, NULL, 0, &sp) == OK)
+ {
+ transports[ntransports].socktype = SOCK_STREAM;
+ transports[ntransports].proto = IPPROTO_TCP;
+ transports[ntransports++].port = sp->s_port;
+ }
+
+ if (socktype != SOCK_STREAM && proto != IPPROTO_TCP &&
+ getservbyname_r(servname, "udp", &ent, NULL, 0, &sp) == OK)
+ {
+ transports[ntransports].socktype = SOCK_DGRAM;
+ transports[ntransports].proto = IPPROTO_UDP;
+ transports[ntransports++].port = sp->s_port;
+ }
+
+ if (ntransports == 0)
+ {
+ return EAI_SERVICE;
+ }
+
+ port = transports[0].port;
+ if (ntransports == 1)
+ {
+ socktype = transports[0].socktype;
+ proto = transports[0].proto;
+ }
+ }
+ }
+
+ if (ntransports == 0)
+ {
+ if (socktype == 0 && proto == 0)
+ {
+ transports[0].socktype = SOCK_STREAM;
+ transports[0].proto = IPPROTO_TCP;
+ transports[0].port = port;
+ transports[1].socktype = SOCK_DGRAM;
+ transports[1].proto = IPPROTO_UDP;
+ transports[1].port = port;
+ ntransports = 2;
}
else
{
- return EAI_SERVICE;
+ transports[0].socktype = (socktype != 0) ? socktype : SOCK_RAW;
+ transports[0].proto = (proto != 0) ? proto :
+ (transports[0].socktype == SOCK_STREAM ?
+ IPPROTO_TCP : 0);
+ transports[0].port = port;
+ ntransports = 1;
}
}
- *res = NULL;
+ /* Override transports for local socket families */
+
+#if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_RPMSG) || \
+ defined(CONFIG_NET_VSOCK)
+ if (family == AF_LOCAL || family == AF_RPMSG || family == AF_VSOCK)
+ {
+ transports[0].socktype = socktype;
+ transports[0].proto = proto;
+ transports[0].port = port;
+ ntransports = 1;
+ }
+#endif
- /* If hostname is not NULL, then the AI_PASSIVE flag is ignored. */
+ *res = NULL;
- if ((flags & AI_PASSIVE) != 0 && hostname == NULL)
+ if (hostname == NULL)
{
-#ifdef CONFIG_NET_IPv4
- if (family == AF_INET || family == AF_UNSPEC)
+#if !defined(CONFIG_NET_LOOPBACK) && !defined(CONFIG_NET_VSOCK)
+ if ((flags & AI_PASSIVE) == 0)
{
- struct in_addr addr;
-
- memset(&addr, 0, sizeof(struct in_addr));
- ai = alloc_ai(AF_INET, socktype, proto, port, &addr);
- if (ai != NULL)
- {
- *res = (FAR struct addrinfo *)ai;
- }
+ return EAI_FAIL;
}
#endif
-#ifdef CONFIG_NET_IPv6
- if (family == AF_INET6 || family == AF_UNSPEC)
+#ifdef CONFIG_NET_IPv4
+ if (family == AF_INET || family == AF_UNSPEC)
{
- struct in6_addr addr;
+ FAR const void *addr = NULL;
- memset(&addr, 0, sizeof(struct in6_addr));
- ai = alloc_ai(AF_INET6, socktype, proto, port, &addr);
- if (ai != NULL)
+#ifdef CONFIG_NET_LOOPBACK
+ if ((flags & AI_PASSIVE) == 0)
{
- /* Can return both IPv4 and IPv6 loopback. */
-
- if (*res != NULL)
- {
- (*res)->ai_next = (FAR struct addrinfo *)ai;
- }
- else
- {
- *res = (FAR struct addrinfo *)ai;
- }
+ addr = &g_lo_ipv4addr;
}
- }
#endif
-#if defined(CONFIG_NET_RPMSG)
- if (family == AF_RPMSG || family == AF_UNSPEC)
- {
- ai = alloc_ai(AF_RPMSG, socktype, proto, port, "");
- if (ai != NULL)
+ if ((flags & AI_PASSIVE) != 0 || addr != NULL)
{
- if (*res != NULL)
- {
- (*res)->ai_next = (FAR struct addrinfo *)ai;
- }
- else
+ prev_ai = add_transports(prev_ai, AF_INET, transports,
+ ntransports, addr, NULL);
+ if (prev_ai == NULL)
{
- *res = (FAR struct addrinfo *)ai;
+ goto errout_nomem;
}
}
}
#endif
-#if defined(CONFIG_NET_VSOCK)
- if (family == AF_VSOCK || family == AF_UNSPEC)
+#ifdef CONFIG_NET_IPv6
+ if (family == AF_INET6 || family == AF_UNSPEC)
{
- /* "-1" <--> VMADDR_CID_ANY */
+ FAR const void *addr = NULL;
- ai = alloc_ai(AF_VSOCK, socktype, proto, port, "-1");
- if (ai != NULL)
+#ifdef CONFIG_NET_LOOPBACK
+ if ((flags & AI_PASSIVE) == 0)
{
- if (*res != NULL)
- {
- (*res)->ai_next = (FAR struct addrinfo *)ai;
- }
- else
- {
- *res = (FAR struct addrinfo *)ai;
- }
+ addr = &g_lo_ipv6addr;
}
- }
#endif
- return (*res != NULL) ? OK : EAI_MEMORY;
- }
-
- if (hostname == NULL)
- {
-#ifdef CONFIG_NET_LOOPBACK
- /* Local service. */
-
-#ifdef CONFIG_NET_IPv4
- if (family == AF_INET || family == AF_UNSPEC)
- {
- ai = alloc_ai(AF_INET, socktype, proto, port,
- &g_lo_ipv4addr);
- if (ai != NULL)
+ if ((flags & AI_PASSIVE) != 0 || addr != NULL)
{
- *res = (FAR struct addrinfo *)ai;
+ prev_ai = add_transports(prev_ai, AF_INET6, transports,
+ ntransports, addr, NULL);
+ if (prev_ai == NULL)
+ {
+ goto errout_nomem;
+ }
}
}
#endif
-#ifdef CONFIG_NET_IPv6
- if (family == AF_INET6 || family == AF_UNSPEC)
+#if defined(CONFIG_NET_RPMSG)
+ if (((flags & AI_PASSIVE) != 0) &&
+ (family == AF_RPMSG || family == AF_UNSPEC))
{
- ai = alloc_ai(AF_INET6, socktype, proto, port,
- &g_lo_ipv6addr);
- if (ai != NULL)
+ prev_ai = add_transports(prev_ai, AF_RPMSG, transports,
+ ntransports, "", NULL);
+ if (prev_ai == NULL)
{
- /* Can return both IPv4 and IPv6 loopback. */
-
- if (*res != NULL)
- {
- (*res)->ai_next = (FAR struct addrinfo *)ai;
- }
- else
- {
- *res = (FAR struct addrinfo *)ai;
- }
+ goto errout_nomem;
}
}
#endif
-#endif
#if defined(CONFIG_NET_VSOCK)
if (family == AF_VSOCK || family == AF_UNSPEC)
{
- /* "1" <--> VMADDR_CID_LOCAL */
+ FAR const char *cid = ((flags & AI_PASSIVE) != 0) ? "-1" : "1";
- ai = alloc_ai(AF_VSOCK, socktype, proto, port, "1");
- if (ai != NULL)
+ prev_ai = add_transports(prev_ai, AF_VSOCK, transports,
+ ntransports, cid, NULL);
+ if (prev_ai == NULL)
{
- if (*res != NULL)
- {
- (*res)->ai_next = (FAR struct addrinfo *)ai;
- }
- else
- {
- *res = (FAR struct addrinfo *)ai;
- }
+ goto errout_nomem;
}
}
#endif
-#if defined(CONFIG_NET_LOOPBACK) || defined(CONFIG_NET_VSOCK)
- return (*res != NULL) ? OK : EAI_MEMORY;
-#else
- /* Local service, but no loopback so cannot succeed. */
+ return (*res != NULL) ? OK : EAI_NONAME;
+ }
- return EAI_FAIL;
-#endif
+ if ((flags & AI_CANONNAME) != 0)
+ {
+ canon = hostname;
}
-#if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_RPMSG) ||
defined(CONFIG_NET_VSOCK)
+#if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_RPMSG) || \
+ defined(CONFIG_NET_VSOCK)
if (family == AF_LOCAL || family == AF_RPMSG || family == AF_VSOCK)
{
- ai = alloc_ai(family, socktype, proto, port, hostname);
- if (ai != NULL)
+ prev_ai = add_transports(prev_ai, family, transports,
+ ntransports, hostname, canon);
+ if (prev_ai == NULL)
{
- *res = (FAR struct addrinfo *)ai;
- if (flags & AI_CANONNAME)
- {
- ai->ai.ai_canonname = (FAR char *)hostname;
- }
+ goto errout_nomem;
}
- return (*res != NULL) ? OK : EAI_MEMORY;
+ return OK;
}
#endif
@@ -399,46 +492,26 @@ int getaddrinfo(FAR const char *hostname, FAR const char
*servname,
continue;
}
- /* REVISIT: filter by socktype and protocol not implemented. */
-
- ai = alloc_ai(host.h_addrtypes[i], socktype, proto, port,
- host.h_addr_list[i]);
- if (ai == NULL)
+ prev_ai = add_transports(prev_ai, host.h_addrtypes[i],
+ transports, ntransports,
+ host.h_addr_list[i], canon);
+ if (prev_ai == NULL)
{
- if (*res)
- {
- freeaddrinfo(*res);
- }
-
- lib_free(hostbuffer);
- return EAI_MEMORY;
+ goto errout_nomem;
}
+ }
- /* REVISIT: grok canonical name.
- *
- * OpenGroup: "if the canonical name is not available, then
- * ai_canonname shall refer to the hostname argument or a string
- * with the same contents."
- */
-
- ai->ai.ai_canonname = (FAR char *)hostname;
-
- /* Add result to linked list.
- * TODO: RFC 3484/6724 destination address sort not implemented.
- */
-
- if (prev_ai != NULL)
- {
- prev_ai->ai.ai_next = (FAR struct addrinfo *)ai;
- }
- else
- {
- *res = (FAR struct addrinfo *)ai;
- }
+ lib_free(hostbuffer);
+ return (*res != NULL) ? OK : EAI_FAMILY;
- prev_ai = ai;
+errout_nomem:
+ if (*res != NULL)
+ {
+ freeaddrinfo(*res);
+ *res = NULL;
}
lib_free(hostbuffer);
- return (*res != NULL) ? OK : EAI_FAMILY;
+
+ return EAI_MEMORY;
}