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 }