diff options
author | Bruno Haible <bruno@clisp.org> | 2023-04-01 17:14:43 +0200 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2023-04-01 17:14:43 +0200 |
commit | c5cdf41d436261b279c172753ac727335d44b9fd (patch) | |
tree | 683471c293193efb703f178b6c41b366f3fe4a02 | |
parent | 2d1963afbf456ea8ac21378a814d5230f41eeb72 (diff) | |
download | gettext-c5cdf41d436261b279c172753ac727335d44b9fd.tar.gz |
hostname: Use more modern network APIs.
Reported by Jens Petersen <petersen@redhat.com>
at <https://savannah.gnu.org/bugs/?63983>.
* autogen.sh (GNULIB_MODULES_TOOLS_FOR_SRC): Add getaddrinfo.
* gettext-tools/src/hostname.c (HAVE_GETADDRINFO): Define to 1.
Do the #includes for gethostname() after those for getaddrinfo().
(ipv4_is_linklocal, ipv6_is_linklocal): New functions.
(print_hostname): For long_format and ip_format, prefer getaddrinfo over
gethostbyname.
* gettext-tools/src/Makefile.am (hostname_LDADD): New variable.
-rwxr-xr-x | autogen.sh | 1 | ||||
-rw-r--r-- | gettext-tools/src/Makefile.am | 1 | ||||
-rw-r--r-- | gettext-tools/src/hostname.c | 231 |
3 files changed, 178 insertions, 55 deletions
diff --git a/autogen.sh b/autogen.sh index f63348ff4..76a276bdb 100755 --- a/autogen.sh +++ b/autogen.sh @@ -166,6 +166,7 @@ if ! $skip_gnulib; then full-write fwriteerror gcd + getaddrinfo getline getopt-gnu gettext diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index 59af8aaa7..0d6979fe2 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -377,6 +377,7 @@ msgfilter_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD) msggrep_LDADD = $(LIBGREP) libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD) msginit_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD) msguniq_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD) +hostname_LDADD = $(LDADD) $(GETADDRINFO_LIB) # Specify when to relink the programs. msgcmp_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD) diff --git a/gettext-tools/src/hostname.c b/gettext-tools/src/hostname.c index e90963761..fa26b5304 100644 --- a/gettext-tools/src/hostname.c +++ b/gettext-tools/src/hostname.c @@ -1,5 +1,5 @@ /* Display hostname in various forms. - Copyright (C) 2001-2003, 2006-2007, 2012, 2014, 2018-2022 Free Software + Copyright (C) 2001-2003, 2006-2007, 2012, 2014, 2018-2023 Free Software Foundation, Inc. Written by Bruno Haible <haible@clisp.cons.org>, 2001. @@ -32,30 +32,18 @@ # define WIN32_NATIVE #endif -/* Get gethostname(). */ -#include <unistd.h> - -#ifdef WIN32_NATIVE -/* Native Woe32 API lacks gethostname() but has GetComputerName() instead. */ -# include <windows.h> -#else -/* Some systems, like early Solaris versions, lack gethostname() but - have uname() instead. */ -# if !HAVE_GETHOSTNAME -# include <sys/utsname.h> -# endif -#endif -/* Get MAXHOSTNAMELEN. */ -#if HAVE_SYS_PARAM_H -# include <sys/param.h> -#endif -#ifndef MAXHOSTNAMELEN -# define MAXHOSTNAMELEN 64 -#endif +/* We use the getaddrinfo and getnameinfo implementation from gnulib. */ +#define HAVE_GETADDRINFO 1 +/* Support for using getaddrinfo() and getnameinfo(). */ +#if HAVE_GETADDRINFO +# include <sys/types.h> +# include <sys/socket.h> /* defines AF_INET, AF_INET6 */ +# include <netdb.h> /* declares getaddrinfo(), getnameinfo() */ +# include <netinet/in.h> /* defines struct sockaddr_in, struct sockaddr_in6 */ /* Support for using gethostbyname(). */ -#if HAVE_GETHOSTBYNAME +#elif HAVE_GETHOSTBYNAME # include <sys/types.h> # include <sys/socket.h> /* defines AF_INET, AF_INET6 */ # include <netinet/in.h> /* declares ntohs(), defines struct sockaddr_in */ @@ -78,6 +66,33 @@ # include <netdb.h> /* defines struct hostent, declares gethostbyname() */ #endif + +/* Do these includes after the network-related ones, because on native Windows, + the #include <winsock2.h> must precede the #include <windows.h>. */ + +/* Get gethostname(). */ +#include <unistd.h> + +#ifdef WIN32_NATIVE +/* Native Woe32 API lacks gethostname() but has GetComputerName() instead. */ +# include <windows.h> +#else +/* Some systems, like early Solaris versions, lack gethostname() but + have uname() instead. */ +# if !HAVE_GETHOSTNAME +# include <sys/utsname.h> +# endif +#endif + +/* Get MAXHOSTNAMELEN. */ +#if HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif +#ifndef MAXHOSTNAMELEN +# define MAXHOSTNAMELEN 64 +#endif + + /* Include this after <sys/socket.h>, to avoid a syntax error on BeOS. */ #include <stdbool.h> @@ -314,16 +329,31 @@ xgethostname () # endif #endif +/* Tests whether an IPv4 address is link-local. */ +static bool +ipv4_is_linklocal (const struct in_addr *addr) +{ + return (((const unsigned char *) addr)[0] == 169) + && (((const unsigned char *) addr)[1] == 254); +} + +#if HAVE_IPV6 +/* Tests whether an IPv6 address is link-local. */ +static bool +ipv6_is_linklocal (const struct in6_addr *addr) +{ + /* Cf. IN6_IS_ADDR_LINKLOCAL macro. */ + return (((const unsigned char *) addr)[0] == 0xFE) + && ((((const unsigned char *) addr)[1] & 0xC0) == 0x80); +} +#endif + /* Print the hostname according to the specified format. */ static void print_hostname () { char *hostname; char *dot; -#if HAVE_GETHOSTBYNAME - struct hostent *h; - size_t i; -#endif hostname = xgethostname (); @@ -343,44 +373,135 @@ print_hostname () break; case long_format: +#if HAVE_GETADDRINFO + /* Look for netwide usable hostname and aliases using getaddrinfo(). + getnameinfo() is not even needed. */ + { + struct addrinfo hints; + struct addrinfo *res; + int ret; + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; /* either AF_INET or AF_INET6 is ok */ + hints.ai_socktype = SOCK_STREAM; /* or SOCK_DGRAM or 0 */ + hints.ai_protocol = 0; /* any protocol is ok */ + hints.ai_flags = AI_CANONNAME; + + ret = getaddrinfo (hostname, NULL, &hints, &res); + if (ret == 0) + { + struct addrinfo *p; + + for (p = res; p != NULL; p = p->ai_next) + { + /* Typically p->ai_socktype == SOCK_STREAM, p->ai_protocol == IPPROTO_TCP, + or p->ai_socktype == SOCK_DGRAM, p->ai_protocol == IPPROTO_UDP. */ + /* p->ai_canonname is only set on the first 'struct addrinfo'. */ + if (p->ai_canonname != NULL) + printf ("%s\n", p->ai_canonname); + } + + freeaddrinfo (res); + } + else + printf ("%s\n", hostname); + } +#elif HAVE_GETHOSTBYNAME /* Look for netwide usable hostname and aliases using gethostbyname(). */ -#if HAVE_GETHOSTBYNAME - h = gethostbyname (hostname); - if (h != NULL) - { - printf ("%s\n", h->h_name); - if (h->h_aliases != NULL) - for (i = 0; h->h_aliases[i] != NULL; i++) - printf ("%s\n", h->h_aliases[i]); - } - else + { + struct hostent *h; + size_t i; + + h = gethostbyname (hostname); + if (h != NULL) + { + printf ("%s\n", h->h_name); + if (h->h_aliases != NULL) + for (i = 0; h->h_aliases[i] != NULL; i++) + printf ("%s\n", h->h_aliases[i]); + } + else + printf ("%s\n", hostname); + } +#else + printf ("%s\n", hostname); #endif - printf ("%s\n", hostname); break; case ip_format: - /* Look for netwide usable IP addresses using gethostbyname(). */ -#if HAVE_GETHOSTBYNAME - h = gethostbyname (hostname); - if (h != NULL && h->h_addr_list != NULL) - for (i = 0; h->h_addr_list[i] != NULL; i++) +#if HAVE_GETADDRINFO + /* Look for netwide usable IP addresses using getaddrinfo() and + getnameinfo(). */ + { + struct addrinfo hints; + struct addrinfo *res; + int ret; + char host[1025]; + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; /* either AF_INET or AF_INET6 is ok */ + hints.ai_socktype = SOCK_STREAM; /* or SOCK_DGRAM */ + hints.ai_protocol = 0; /* any protocol is ok */ + hints.ai_flags = 0; + + ret = getaddrinfo (hostname, NULL, &hints, &res); + if (ret == 0) { -#if HAVE_IPV6 - if (h->h_addrtype == AF_INET6) - { - char buffer[45+1]; - ipv6_ntop (buffer, *(const struct in6_addr*) h->h_addr_list[i]); - printf("[%s]\n", buffer); - } - else -#endif - if (h->h_addrtype == AF_INET) + struct addrinfo *p; + + for (p = res; p != NULL; p = p->ai_next) { - char buffer[15+1]; - ipv4_ntop (buffer, *(const struct in_addr*) h->h_addr_list[i]); - printf("[%s]\n", buffer); + /* Typically p->ai_socktype == SOCK_STREAM, p->ai_protocol == IPPROTO_TCP, + or p->ai_socktype == SOCK_DGRAM, p->ai_protocol == IPPROTO_UDP. */ + /* Ignore link-local addresses. + <https://en.wikipedia.org/wiki/Link-local_address>. */ + if (!((p->ai_family == AF_INET + && ipv4_is_linklocal (&((const struct sockaddr_in *) p->ai_addr)->sin_addr)) +# if HAVE_IPV6 + || (p->ai_family == AF_INET6 + && ipv6_is_linklocal (&((const struct sockaddr_in6 *) p->ai_addr)->sin6_addr)) +# endif + ) ) + if (getnameinfo (p->ai_addr, p->ai_addrlen, + host, sizeof (host), + NULL, 0, + NI_NUMERICHOST) + == 0) + { + printf ("[%.*s]\n", (int) sizeof (host), host); + } } + + freeaddrinfo (res); } + } +#elif HAVE_GETHOSTBYNAME + /* Look for netwide usable IP addresses using gethostbyname(). */ + { + struct hostent *h; + size_t i; + + h = gethostbyname (hostname); + if (h != NULL && h->h_addr_list != NULL) + for (i = 0; h->h_addr_list[i] != NULL; i++) + { +# if HAVE_IPV6 + if (h->h_addrtype == AF_INET6) + { + char buffer[45+1]; + ipv6_ntop (buffer, *(const struct in6_addr*) h->h_addr_list[i]); + printf("[%s]\n", buffer); + } + else +# endif + if (h->h_addrtype == AF_INET) + { + char buffer[15+1]; + ipv4_ntop (buffer, *(const struct in_addr*) h->h_addr_list[i]); + printf("[%s]\n", buffer); + } + } + } #endif break; |