KO Myung-Hun wrote:
> + if (hints && (hints->ai_flags & AI_NUMERICHOST))
> + {
> + int valid = 0;
> +
> +# ifdef HAVE_IPV6
> + valid |= !valid &&
> + (hints->ai_family == AF_INET6 ||
> + hints->ai_family == AF_UNSPEC) &&
> + inet_pton (AF_INET6, nodename, &addr6) == 1;
> +# endif
> +# ifdef HAVE_IPV4
> + valid |= !valid &&
> + (hints->ai_family == AF_INET ||
> + hints->ai_family == AF_UNSPEC) &&
> + inet_pton (AF_INET, nodename, &addr4) == 1;
> +# endif
> + if (!valid)
> + return EAI_NONAME;
> + }
The logic seems right, but is hard to understand when written in this way.
With a separate function, it becomes much clearer.
> if (res == EAI_NONAME)
> - return 0;
> + return (flags & (AI_NUMERICHOST | AI_NUMERICSERV)) ? 2 : 0;
This does only part of the necessary checks.
I prefer to commit this patch. Tested on a glibc system, with
'gl_cv_func_getaddrinfo=no ./configure ...'
2025-02-12 Bruno Haible <[email protected]>
getaddrinfo: Support the AI_NUMERICHOST flag.
* lib/getaddrinfo.c (is_numeric_host): New function.
(getaddrinfo): Accept and implement the AI_NUMERICHOST flag.
* modules/getaddrinfo (Depends-on): Add inet_pton.
* tests/test-getaddrinfo.c: Include <ctype.h>.
(simple): In pass 3, pass the AI_NUMERICHOST flag.
(main): Add a pass 3.
diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c
index cd045c5121..4a0aeab9f1 100644
--- a/lib/getaddrinfo.c
+++ b/lib/getaddrinfo.c
@@ -169,16 +169,43 @@ validate_family (int family)
{
/* FIXME: Support more families. */
# if HAVE_IPV4
- if (family == PF_INET)
- return true;
+ if (family == PF_INET)
+ return true;
# endif
# if HAVE_IPV6
- if (family == PF_INET6)
- return true;
+ if (family == PF_INET6)
+ return true;
# endif
- if (family == PF_UNSPEC)
- return true;
- return false;
+ if (family == PF_UNSPEC)
+ return true;
+ return false;
+}
+
+static bool
+is_numeric_host (const char *host, int family)
+{
+# if HAVE_IPV4
+ if (family == PF_INET || family == PF_UNSPEC)
+ {
+ /* glibc supports IPv4 addresses in numbers-and-dots notation, that is,
+ also hexadecimal and octal number formats and formats that don't
+ require all four bytes to be explicitly written, via inet_aton().
+ But POSIX doesn't require support for these legacy formats. Therefore
+ we are free to use inet_pton() instead of inet_aton(). */
+ struct in_addr addr;
+ if (inet_pton (AF_INET, host, &addr))
+ return true;
+ }
+# endif
+# if HAVE_IPV6
+ if (family == PF_INET6 || family == PF_UNSPEC)
+ {
+ struct in6_addr addr;
+ if (inet_pton (AF_INET6, host, &addr))
+ return true;
+ }
+# endif
+ return false;
}
/* Translate name of a service location and/or a service name to set of
@@ -213,7 +240,7 @@ getaddrinfo (const char *restrict nodename,
return getaddrinfo_ptr (nodename, servname, hints, res);
# endif
- if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE)))
+ if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE|AI_NUMERICHOST)))
/* FIXME: Support more flags. */
return EAI_BADFLAGS;
@@ -225,12 +252,18 @@ getaddrinfo (const char *restrict nodename,
/* FIXME: Support other socktype. */
return EAI_SOCKTYPE; /* FIXME: Better return code? */
- if (!nodename)
+ if (nodename != NULL)
+ {
+ if (hints && (hints->ai_flags & AI_NUMERICHOST) != 0
+ && !is_numeric_host (nodename, hints->ai_family))
+ return EAI_NONAME;
+ }
+ else
{
if (!(hints->ai_flags & AI_PASSIVE))
return EAI_NONAME;
-# ifdef HAVE_IPV6
+# if HAVE_IPV6
nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0";
# else
nodename = "0.0.0.0";
diff --git a/modules/getaddrinfo b/modules/getaddrinfo
index b31d395635..265886e6c0 100644
--- a/modules/getaddrinfo
+++ b/modules/getaddrinfo
@@ -13,6 +13,7 @@ extensions
gettext-h [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1
|| test $HAVE_DECL_GAI_STRERROR = 0 || test $REPLACE_GAI_STRERROR = 1]
gnulib-i18n [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1
|| test $HAVE_DECL_GAI_STRERROR = 0 || test $REPLACE_GAI_STRERROR = 1]
inet_ntop [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1]
+inet_pton [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1]
snprintf [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1]
bool [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1]
strdup [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1]
diff --git a/tests/test-getaddrinfo.c b/tests/test-getaddrinfo.c
index d09c4c1a24..8f35e6abc6 100644
--- a/tests/test-getaddrinfo.c
+++ b/tests/test-getaddrinfo.c
@@ -34,6 +34,7 @@ SIGNATURE_CHECK (getaddrinfo, int, (char const *, char const
*,
#endif
#include <arpa/inet.h>
+#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
@@ -81,7 +82,7 @@ simple (int pass, char const *host, char const *service)
else
{
memset (&hints, 0, sizeof (hints));
- hints.ai_flags = AI_CANONNAME;
+ hints.ai_flags = AI_CANONNAME | (pass == 3 ? AI_NUMERICHOST : 0);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints_p = &hints;
@@ -92,6 +93,9 @@ simple (int pass, char const *host, char const *service)
dbgprintf ("res %d: %s\n", res, gai_strerror (res));
+ if (pass == 3 && ! isdigit (host[0]))
+ return res != EAI_NONAME;
+
if (res != 0)
{
/* EAI_AGAIN is returned if no network is available. Don't fail
@@ -171,6 +175,12 @@ simple (int pass, char const *host, char const *service)
#define SERV3 "http"
#define HOST4 "google.org"
#define SERV4 "ldap"
+#if HAVE_IPV4
+# define NUMERICHOSTV4 "1.2.3.4"
+#endif
+#if HAVE_IPV6
+# define NUMERICHOSTV6 "2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF"
+#endif
int main (void)
{
@@ -183,5 +193,15 @@ int main (void)
+ simple (2, HOST1, SERV1)
+ simple (2, HOST2, SERV2)
+ simple (2, HOST3, SERV3)
- + simple (2, HOST4, SERV4));
+ + simple (2, HOST4, SERV4)
+#if HAVE_IPV4
+ + simple (3, NUMERICHOSTV4, SERV1)
+#endif
+#if HAVE_IPV6
+ + simple (3, NUMERICHOSTV6, SERV1)
+#endif
+ + simple (3, HOST1, SERV1)
+ + simple (3, HOST2, SERV2)
+ + simple (3, HOST3, SERV3)
+ + simple (3, HOST4, SERV4));
}