]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
hostname: Use more modern network APIs.
authorBruno Haible <bruno@clisp.org>
Sat, 1 Apr 2023 15:14:43 +0000 (17:14 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 1 Apr 2023 15:14:43 +0000 (17:14 +0200)
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.

autogen.sh
gettext-tools/src/Makefile.am
gettext-tools/src/hostname.c

index f63348ff402ff920882a1ee5614d625c59766415..76a276bdb0cc874ed2273e31cfcc6ee0db2acac6 100755 (executable)
@@ -166,6 +166,7 @@ if ! $skip_gnulib; then
     full-write
     fwriteerror
     gcd
+    getaddrinfo
     getline
     getopt-gnu
     gettext
index 59af8aaa7746c280d11c209d880ed6fe1a38a1c1..0d6979fe241f63968886c622278ee9ee7b659b7a 100644 (file)
@@ -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)
index e909637616359349db230739053edad00cbe79b5..fa26b5304dbb477cceff5012856f56e26e2e2041 100644 (file)
@@ -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.
 
 # 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 */
 # 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;