(root)/
gettext-0.22.4/
gettext-tools/
src/
hostname.c
       1  /* Display hostname in various forms.
       2     Copyright (C) 2001-2003, 2006-2007, 2012, 2014, 2018-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <haible@clisp.cons.org>, 2001.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation; either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  
      19  #ifdef HAVE_CONFIG_H
      20  # include "config.h"
      21  #endif
      22  
      23  #include <errno.h>
      24  #include <getopt.h>
      25  #include <stdio.h>
      26  #include <stdlib.h>
      27  #include <string.h>
      28  #include <locale.h>
      29  
      30  #if defined _WIN32
      31  # define WIN32_NATIVE
      32  #endif
      33  
      34  
      35  /* We use the getaddrinfo and getnameinfo implementation from gnulib.  */
      36  #define HAVE_GETADDRINFO 1
      37  
      38  /* Support for using getaddrinfo() and getnameinfo().  */
      39  #if HAVE_GETADDRINFO
      40  # include <sys/types.h>
      41  # include <sys/socket.h> /* defines AF_INET, AF_INET6 */
      42  # include <netdb.h>      /* declares getaddrinfo(), getnameinfo() */
      43  # include <netinet/in.h> /* defines struct sockaddr_in, struct sockaddr_in6 */
      44  /* Support for using gethostbyname().  */
      45  #elif HAVE_GETHOSTBYNAME
      46  # include <sys/types.h>
      47  # include <sys/socket.h> /* defines AF_INET, AF_INET6 */
      48  # include <netinet/in.h> /* declares ntohs(), defines struct sockaddr_in */
      49  # if HAVE_ARPA_INET_H
      50  #  include <arpa/inet.h> /* declares inet_ntoa(), inet_ntop() */
      51  # endif
      52  # if HAVE_IPV6
      53  #  if !defined(__CYGWIN__) /* Cygwin has only s6_addr, no s6_addr16 */
      54  #   if defined(__APPLE__) && defined(__MACH__) /* MacOS X */
      55  #    define in6_u __u6_addr
      56  #    define u6_addr16 __u6_addr16
      57  #   endif
      58      /* Use s6_addr16 for portability.  See RFC 2553.  */
      59  #   ifndef s6_addr16
      60  #    define s6_addr16 in6_u.u6_addr16
      61  #   endif
      62  #   define HAVE_IN6_S6_ADDR16 1
      63  #  endif
      64  # endif
      65  # include <netdb.h> /* defines struct hostent, declares gethostbyname() */
      66  #endif
      67  
      68  
      69  /* Do these includes after the network-related ones, because on native Windows,
      70     the #include <winsock2.h> must precede the #include <windows.h>.  */
      71  
      72  /* Get gethostname().  */
      73  #include <unistd.h>
      74  
      75  #ifdef WIN32_NATIVE
      76  /* Native Woe32 API lacks gethostname() but has GetComputerName() instead.  */
      77  # include <windows.h>
      78  #else
      79  /* Some systems, like early Solaris versions, lack gethostname() but
      80     have uname() instead.  */
      81  # if !HAVE_GETHOSTNAME
      82  #  include <sys/utsname.h>
      83  # endif
      84  #endif
      85  
      86  /* Get MAXHOSTNAMELEN.  */
      87  #if HAVE_SYS_PARAM_H
      88  # include <sys/param.h>
      89  #endif
      90  #ifndef MAXHOSTNAMELEN
      91  # define MAXHOSTNAMELEN 64
      92  #endif
      93  
      94  
      95  /* Include this after <sys/socket.h>, to avoid a syntax error on BeOS.  */
      96  #include <stdbool.h>
      97  
      98  #include "noreturn.h"
      99  #include "closeout.h"
     100  #include "error.h"
     101  #include "error-progname.h"
     102  #include "progname.h"
     103  #include "relocatable.h"
     104  #include "basename-lgpl.h"
     105  #include "xalloc.h"
     106  #include "propername.h"
     107  #include "gettext.h"
     108  
     109  #define _(str) gettext (str)
     110  
     111  
     112  /* Output format.  */
     113  static enum { default_format, short_format, long_format, ip_format } format;
     114  
     115  /* Long options.  */
     116  static const struct option long_options[] =
     117  {
     118    { "fqdn", no_argument, NULL, 'f' },
     119    { "help", no_argument, NULL, 'h' },
     120    { "ip-address", no_argument, NULL, 'i' },
     121    { "long", no_argument, NULL, 'f' },
     122    { "short", no_argument, NULL, 's' },
     123    { "version", no_argument, NULL, 'V' },
     124    { NULL, 0, NULL, 0 }
     125  };
     126  
     127  
     128  /* Forward declaration of local functions.  */
     129  _GL_NORETURN_FUNC static void usage (int status);
     130  static void print_hostname (void);
     131  
     132  int
     133  main (int argc, char *argv[])
     134  {
     135    int optchar;
     136    bool do_help;
     137    bool do_version;
     138  
     139    /* Set program name for messages.  */
     140    set_program_name (argv[0]);
     141    error_print_progname = maybe_print_progname;
     142  
     143    /* Set locale via LC_ALL.  */
     144    setlocale (LC_ALL, "");
     145  
     146    /* Set the text message domain.  */
     147    bindtextdomain (PACKAGE, relocate (LOCALEDIR));
     148    textdomain (PACKAGE);
     149  
     150    /* Ensure that write errors on stdout are detected.  */
     151    atexit (close_stdout);
     152  
     153    /* Set default values for variables.  */
     154    do_help = false;
     155    do_version = false;
     156    format = default_format;
     157  
     158    /* Parse command line options.  */
     159    while ((optchar = getopt_long (argc, argv, "fhisV", long_options, NULL))
     160           != EOF)
     161      switch (optchar)
     162      {
     163      case '\0':          /* Long option.  */
     164        break;
     165      case 'f':
     166        format = long_format;
     167        break;
     168      case 's':
     169        format = short_format;
     170        break;
     171      case 'i':
     172        format = ip_format;
     173        break;
     174      case 'h':
     175        do_help = true;
     176        break;
     177      case 'V':
     178        do_version = true;
     179        break;
     180      default:
     181        usage (EXIT_FAILURE);
     182        /* NOTREACHED */
     183      }
     184  
     185    /* Version information requested.  */
     186    if (do_version)
     187      {
     188        printf ("%s (GNU %s) %s\n", last_component (program_name),
     189                PACKAGE, VERSION);
     190        /* xgettext: no-wrap */
     191        printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
     192  License GPLv3+: GNU GPL version 3 or later <%s>\n\
     193  This is free software: you are free to change and redistribute it.\n\
     194  There is NO WARRANTY, to the extent permitted by law.\n\
     195  "),
     196                "2001-2023", "https://gnu.org/licenses/gpl.html");
     197        printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
     198        exit (EXIT_SUCCESS);
     199      }
     200  
     201    /* Help is requested.  */
     202    if (do_help)
     203      usage (EXIT_SUCCESS);
     204  
     205    /* Test for extraneous arguments.  */
     206    if (optind != argc)
     207      error (EXIT_FAILURE, 0, _("too many arguments"));
     208  
     209    /* Get and print the hostname.  */
     210    print_hostname ();
     211  
     212    exit (EXIT_SUCCESS);
     213  }
     214  
     215  /* Display usage information and exit.  */
     216  static void
     217  usage (int status)
     218  {
     219    if (status != EXIT_SUCCESS)
     220      fprintf (stderr, _("Try '%s --help' for more information.\n"),
     221               program_name);
     222    else
     223      {
     224        printf (_("\
     225  Usage: %s [OPTION]\n\
     226  "), program_name);
     227        printf ("\n");
     228        printf (_("\
     229  Print the machine's hostname.\n"));
     230        printf ("\n");
     231        printf (_("\
     232  Output format:\n"));
     233        printf (_("\
     234    -s, --short                 short host name\n"));
     235        printf (_("\
     236    -f, --fqdn, --long          long host name, includes fully qualified domain\n\
     237                                  name, and aliases\n"));
     238        printf (_("\
     239    -i, --ip-address            addresses for the hostname\n"));
     240        printf ("\n");
     241        printf (_("\
     242  Informative output:\n"));
     243        printf (_("\
     244    -h, --help                  display this help and exit\n"));
     245        printf (_("\
     246    -V, --version               output version information and exit\n"));
     247        printf ("\n");
     248        /* TRANSLATORS: The first placeholder is the web address of the Savannah
     249           project of this package.  The second placeholder is the bug-reporting
     250           email address for this package.  Please add _another line_ saying
     251           "Report translation bugs to <...>\n" with the address for translation
     252           bugs (typically your translation team's web or email address).  */
     253        printf(_("\
     254  Report bugs in the bug tracker at <%s>\n\
     255  or by email to <%s>.\n"),
     256               "https://savannah.gnu.org/projects/gettext",
     257               "bug-gettext@gnu.org");
     258      }
     259  
     260    exit (status);
     261  }
     262  
     263  /* Returns an xmalloc()ed string containing the machine's host name.  */
     264  static char *
     265  xgethostname ()
     266  {
     267  #ifdef WIN32_NATIVE
     268    char hostname[MAX_COMPUTERNAME_LENGTH+1];
     269    DWORD size = sizeof (hostname);
     270  
     271    if (!GetComputerName (hostname, &size))
     272      error (EXIT_FAILURE, 0, _("could not get host name"));
     273    return xstrdup (hostname);
     274  #elif HAVE_GETHOSTNAME
     275    char hostname[MAXHOSTNAMELEN+1];
     276  
     277    if (gethostname (hostname, MAXHOSTNAMELEN) < 0)
     278      error (EXIT_FAILURE, errno, _("could not get host name"));
     279    hostname[MAXHOSTNAMELEN] = '\0';
     280    return xstrdup (hostname);
     281  #else
     282    struct utsname utsname;
     283  
     284    if (uname (&utsname) < 0)
     285      error (EXIT_FAILURE, errno, _("could not get host name"));
     286    return xstrdup (utsname.nodename);
     287  #endif
     288  }
     289  
     290  /* Converts an AF_INET address to a printable, presentable format.
     291     BUFFER is an array with at least 15+1 bytes.  ADDR is 'struct in_addr'.  */
     292  #if HAVE_INET_NTOP
     293  # define ipv4_ntop(buffer,addr) \
     294      inet_ntop (AF_INET, &addr, buffer, 15+1)
     295  #else
     296  # define ipv4_ntop(buffer,addr) \
     297      strcpy (buffer, inet_ntoa (addr))
     298  #endif
     299  
     300  #if HAVE_IPV6
     301  /* Converts an AF_INET6 address to a printable, presentable format.
     302     BUFFER is an array with at least 45+1 bytes.  ADDR is 'struct in6_addr'.  */
     303  # if HAVE_INET_NTOP
     304  #  define ipv6_ntop(buffer,addr) \
     305       inet_ntop (AF_INET6, &addr, buffer, 45+1)
     306  # elif HAVE_IN6_S6_ADDR16
     307  #  define ipv6_ntop(buffer,addr) \
     308       sprintf (buffer, "%x:%x:%x:%x:%x:%x:%x:%x", \
     309                ntohs ((addr).s6_addr16[0]), \
     310                ntohs ((addr).s6_addr16[1]), \
     311                ntohs ((addr).s6_addr16[2]), \
     312                ntohs ((addr).s6_addr16[3]), \
     313                ntohs ((addr).s6_addr16[4]), \
     314                ntohs ((addr).s6_addr16[5]), \
     315                ntohs ((addr).s6_addr16[6]), \
     316                ntohs ((addr).s6_addr16[7]))
     317  # else
     318  #  define ipv6_ntop(buffer,addr) \
     319       sprintf (buffer, "%x:%x:%x:%x:%x:%x:%x:%x", \
     320                ((addr).s6_addr[0] << 8) | (addr).s6_addr[1], \
     321                ((addr).s6_addr[2] << 8) | (addr).s6_addr[3], \
     322                ((addr).s6_addr[4] << 8) | (addr).s6_addr[5], \
     323                ((addr).s6_addr[6] << 8) | (addr).s6_addr[7], \
     324                ((addr).s6_addr[8] << 8) | (addr).s6_addr[9], \
     325                ((addr).s6_addr[10] << 8) | (addr).s6_addr[11], \
     326                ((addr).s6_addr[12] << 8) | (addr).s6_addr[13], \
     327                ((addr).s6_addr[14] << 8) | (addr).s6_addr[15])
     328  # endif
     329  #endif
     330  
     331  /* Tests whether an IPv4 address is link-local.  */
     332  static bool
     333  ipv4_is_linklocal (const struct in_addr *addr)
     334  {
     335    return (((const unsigned char *) addr)[0] == 169)
     336           && (((const unsigned char *) addr)[1] == 254);
     337  }
     338  
     339  #if HAVE_IPV6
     340  /* Tests whether an IPv6 address is link-local.  */
     341  static bool
     342  ipv6_is_linklocal (const struct in6_addr *addr)
     343  {
     344    /* Cf. IN6_IS_ADDR_LINKLOCAL macro.  */
     345    return (((const unsigned char *) addr)[0] == 0xFE)
     346           && ((((const unsigned char *) addr)[1] & 0xC0) == 0x80);
     347  }
     348  #endif
     349  
     350  /* Print the hostname according to the specified format.  */
     351  static void
     352  print_hostname ()
     353  {
     354    char *hostname;
     355    char *dot;
     356  
     357    hostname = xgethostname ();
     358  
     359    switch (format)
     360      {
     361      case default_format:
     362        /* Print the hostname, as returned by the system call.  */
     363        printf ("%s\n", hostname);
     364        break;
     365  
     366      case short_format:
     367        /* Print only the part before the first dot.  */
     368        dot = strchr (hostname, '.');
     369        if (dot != NULL)
     370          *dot = '\0';
     371        printf ("%s\n", hostname);
     372        break;
     373  
     374      case long_format:
     375  #if HAVE_GETADDRINFO
     376        /* Look for netwide usable hostname and aliases using getaddrinfo().
     377           getnameinfo() is not even needed.  */
     378        {
     379          struct addrinfo hints;
     380          struct addrinfo *res;
     381          int ret;
     382  
     383          memset (&hints, 0, sizeof (hints));
     384          hints.ai_family = AF_UNSPEC; /* either AF_INET or AF_INET6 is ok */
     385          hints.ai_socktype = SOCK_STREAM; /* or SOCK_DGRAM or 0 */
     386          hints.ai_protocol = 0; /* any protocol is ok */
     387          hints.ai_flags = AI_CANONNAME;
     388  
     389          ret = getaddrinfo (hostname, NULL, &hints, &res);
     390          if (ret == 0)
     391            {
     392              struct addrinfo *p;
     393  
     394              for (p = res; p != NULL; p = p->ai_next)
     395                {
     396                  /* Typically p->ai_socktype == SOCK_STREAM, p->ai_protocol == IPPROTO_TCP,
     397                     or        p->ai_socktype == SOCK_DGRAM, p->ai_protocol == IPPROTO_UDP.  */
     398                  /* p->ai_canonname is only set on the first 'struct addrinfo'.  */
     399                  if (p->ai_canonname != NULL)
     400                    printf ("%s\n", p->ai_canonname);
     401                }
     402  
     403              freeaddrinfo (res);
     404            }
     405          else
     406            printf ("%s\n", hostname);
     407        }
     408  #elif HAVE_GETHOSTBYNAME
     409        /* Look for netwide usable hostname and aliases using gethostbyname().  */
     410        {
     411          struct hostent *h;
     412          size_t i;
     413  
     414          h = gethostbyname (hostname);
     415          if (h != NULL)
     416            {
     417              printf ("%s\n", h->h_name);
     418              if (h->h_aliases != NULL)
     419                for (i = 0; h->h_aliases[i] != NULL; i++)
     420                  printf ("%s\n", h->h_aliases[i]);
     421            }
     422          else
     423            printf ("%s\n", hostname);
     424        }
     425  #else
     426        printf ("%s\n", hostname);
     427  #endif
     428        break;
     429  
     430      case ip_format:
     431  #if HAVE_GETADDRINFO
     432        /* Look for netwide usable IP addresses using getaddrinfo() and
     433           getnameinfo().  */
     434        {
     435          struct addrinfo hints;
     436          struct addrinfo *res;
     437          int ret;
     438          char host[1025];
     439  
     440          memset (&hints, 0, sizeof (hints));
     441          hints.ai_family = AF_UNSPEC; /* either AF_INET or AF_INET6 is ok */
     442          hints.ai_socktype = SOCK_STREAM; /* or SOCK_DGRAM */
     443          hints.ai_protocol = 0; /* any protocol is ok */
     444          hints.ai_flags = 0;
     445  
     446          ret = getaddrinfo (hostname, NULL, &hints, &res);
     447          if (ret == 0)
     448            {
     449              struct addrinfo *p;
     450  
     451              for (p = res; p != NULL; p = p->ai_next)
     452                {
     453                  /* Typically p->ai_socktype == SOCK_STREAM, p->ai_protocol == IPPROTO_TCP,
     454                     or        p->ai_socktype == SOCK_DGRAM, p->ai_protocol == IPPROTO_UDP.  */
     455                  /* Ignore link-local addresses.
     456                     <https://en.wikipedia.org/wiki/Link-local_address>.  */
     457                  if (!((p->ai_family == AF_INET
     458                         && ipv4_is_linklocal (&((const struct sockaddr_in *) p->ai_addr)->sin_addr))
     459  # if HAVE_IPV6
     460                        || (p->ai_family == AF_INET6
     461                            && ipv6_is_linklocal (&((const struct sockaddr_in6 *) p->ai_addr)->sin6_addr))
     462  # endif
     463                     ) )
     464                    if (getnameinfo (p->ai_addr, p->ai_addrlen,
     465                                     host, sizeof (host),
     466                                     NULL, 0,
     467                                     NI_NUMERICHOST)
     468                        == 0)
     469                      {
     470                        printf ("[%.*s]\n", (int) sizeof (host), host);
     471                      }
     472                }
     473  
     474              freeaddrinfo (res);
     475            }
     476        }
     477  #elif HAVE_GETHOSTBYNAME
     478        /* Look for netwide usable IP addresses using gethostbyname().  */
     479        {
     480          struct hostent *h;
     481          size_t i;
     482  
     483          h = gethostbyname (hostname);
     484          if (h != NULL && h->h_addr_list != NULL)
     485            for (i = 0; h->h_addr_list[i] != NULL; i++)
     486              {
     487  # if HAVE_IPV6
     488                if (h->h_addrtype == AF_INET6)
     489                  {
     490                    char buffer[45+1];
     491                    ipv6_ntop (buffer, *(const struct in6_addr*) h->h_addr_list[i]);
     492                    printf("[%s]\n", buffer);
     493                  }
     494                else
     495  # endif
     496                if (h->h_addrtype == AF_INET)
     497                  {
     498                    char buffer[15+1];
     499                    ipv4_ntop (buffer, *(const struct in_addr*) h->h_addr_list[i]);
     500                    printf("[%s]\n", buffer);
     501                  }
     502              }
     503        }
     504  #endif
     505        break;
     506  
     507      default:
     508        abort ();
     509      }
     510  }