LCOV - code coverage report
Current view: top level - basic - hostname-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 107 148 72.3 %
Date: 2019-08-22 15:41:25 Functions: 8 12 66.7 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <limits.h>
       5             : #include <stdio.h>
       6             : #include <string.h>
       7             : #include <sys/utsname.h>
       8             : #include <unistd.h>
       9             : 
      10             : #include "alloc-util.h"
      11             : #include "fd-util.h"
      12             : #include "fileio.h"
      13             : #include "hostname-util.h"
      14             : #include "macro.h"
      15             : #include "string-util.h"
      16             : 
      17           0 : bool hostname_is_set(void) {
      18             :         struct utsname u;
      19             : 
      20           0 :         assert_se(uname(&u) >= 0);
      21             : 
      22           0 :         if (isempty(u.nodename))
      23           0 :                 return false;
      24             : 
      25             :         /* This is the built-in kernel default host name */
      26           0 :         if (streq(u.nodename, "(none)"))
      27           0 :                 return false;
      28             : 
      29           0 :         return true;
      30             : }
      31             : 
      32          12 : char* gethostname_malloc(void) {
      33             :         struct utsname u;
      34             : 
      35             :         /* This call tries to return something useful, either the actual hostname
      36             :          * or it makes something up. The only reason it might fail is OOM.
      37             :          * It might even return "localhost" if that's set. */
      38             : 
      39          12 :         assert_se(uname(&u) >= 0);
      40             : 
      41          12 :         if (isempty(u.nodename) || streq(u.nodename, "(none)"))
      42           0 :                 return strdup(FALLBACK_HOSTNAME);
      43             : 
      44          12 :         return strdup(u.nodename);
      45             : }
      46             : 
      47           0 : int gethostname_strict(char **ret) {
      48             :         struct utsname u;
      49             :         char *k;
      50             : 
      51             :         /* This call will rather fail than make up a name. It will not return "localhost" either. */
      52             : 
      53           0 :         assert_se(uname(&u) >= 0);
      54             : 
      55           0 :         if (isempty(u.nodename))
      56           0 :                 return -ENXIO;
      57             : 
      58           0 :         if (streq(u.nodename, "(none)"))
      59           0 :                 return -ENXIO;
      60             : 
      61           0 :         if (is_localhost(u.nodename))
      62           0 :                 return -ENXIO;
      63             : 
      64           0 :         k = strdup(u.nodename);
      65           0 :         if (!k)
      66           0 :                 return -ENOMEM;
      67             : 
      68           0 :         *ret = k;
      69           0 :         return 0;
      70             : }
      71             : 
      72        2683 : bool valid_ldh_char(char c) {
      73             :         return
      74        2683 :                 (c >= 'a' && c <= 'z') ||
      75        1032 :                 (c >= 'A' && c <= 'Z') ||
      76        5366 :                 (c >= '0' && c <= '9') ||
      77             :                 c == '-';
      78             : }
      79             : 
      80             : /**
      81             :  * Check if s looks like a valid host name or FQDN. This does not do
      82             :  * full DNS validation, but only checks if the name is composed of
      83             :  * allowed characters and the length is not above the maximum allowed
      84             :  * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
      85             :  * allow_trailing_dot is true and at least two components are present
      86             :  * in the name. Note that due to the restricted charset and length
      87             :  * this call is substantially more conservative than
      88             :  * dns_name_is_valid().
      89             :  */
      90          70 : bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
      91          70 :         unsigned n_dots = 0;
      92             :         const char *p;
      93             :         bool dot, hyphen;
      94             : 
      95          70 :         if (isempty(s))
      96           5 :                 return false;
      97             : 
      98             :         /* Doesn't accept empty hostnames, hostnames with
      99             :          * leading dots, and hostnames with multiple dots in a
     100             :          * sequence. Also ensures that the length stays below
     101             :          * HOST_NAME_MAX. */
     102             : 
     103         904 :         for (p = s, dot = hyphen = true; *p; p++)
     104         857 :                 if (*p == '.') {
     105          45 :                         if (dot || hyphen)
     106          11 :                                 return false;
     107             : 
     108          34 :                         dot = true;
     109          34 :                         hyphen = false;
     110          34 :                         n_dots++;
     111             : 
     112         812 :                 } else if (*p == '-') {
     113          39 :                         if (dot)
     114           0 :                                 return false;
     115             : 
     116          39 :                         dot = false;
     117          39 :                         hyphen = true;
     118             : 
     119             :                 } else {
     120         773 :                         if (!valid_ldh_char(*p))
     121           7 :                                 return false;
     122             : 
     123         766 :                         dot = false;
     124         766 :                         hyphen = false;
     125             :                 }
     126             : 
     127          47 :         if (dot && (n_dots < 2 || !allow_trailing_dot))
     128           6 :                 return false;
     129          41 :         if (hyphen)
     130           0 :                 return false;
     131             : 
     132          41 :         if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
     133             :                                   * Linux, but DNS allows domain names
     134             :                                   * up to 255 characters */
     135           5 :                 return false;
     136             : 
     137          36 :         return true;
     138             : }
     139             : 
     140          24 : char* hostname_cleanup(char *s) {
     141             :         char *p, *d;
     142             :         bool dot, hyphen;
     143             : 
     144          24 :         assert(s);
     145             : 
     146         326 :         for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++)
     147         302 :                 if (*p == '.') {
     148          31 :                         if (dot || hyphen)
     149          16 :                                 continue;
     150             : 
     151          15 :                         *(d++) = '.';
     152          15 :                         dot = true;
     153          15 :                         hyphen = false;
     154             : 
     155         271 :                 } else if (*p == '-') {
     156          11 :                         if (dot)
     157           4 :                                 continue;
     158             : 
     159           7 :                         *(d++) = '-';
     160           7 :                         dot = false;
     161           7 :                         hyphen = true;
     162             : 
     163         260 :                 } else if (valid_ldh_char(*p)) {
     164         254 :                         *(d++) = *p;
     165         254 :                         dot = false;
     166         254 :                         hyphen = false;
     167             :                 }
     168             : 
     169          24 :         if (d > s && IN_SET(d[-1], '-', '.'))
     170             :                 /* The dot can occur at most once, but we might have multiple
     171             :                  * hyphens, hence the loop */
     172           7 :                 d--;
     173          24 :         *d = 0;
     174             : 
     175          24 :         return s;
     176             : }
     177             : 
     178          41 : bool is_localhost(const char *hostname) {
     179          41 :         assert(hostname);
     180             : 
     181             :         /* This tries to identify local host and domain names
     182             :          * described in RFC6761 plus the redhatism of localdomain */
     183             : 
     184          81 :         return strcaseeq(hostname, "localhost") ||
     185          40 :                strcaseeq(hostname, "localhost.") ||
     186          40 :                strcaseeq(hostname, "localhost.localdomain") ||
     187          39 :                strcaseeq(hostname, "localhost.localdomain.") ||
     188          39 :                endswith_no_case(hostname, ".localhost") ||
     189          39 :                endswith_no_case(hostname, ".localhost.") ||
     190         120 :                endswith_no_case(hostname, ".localhost.localdomain") ||
     191          39 :                endswith_no_case(hostname, ".localhost.localdomain.");
     192             : }
     193             : 
     194           0 : bool is_gateway_hostname(const char *hostname) {
     195           0 :         assert(hostname);
     196             : 
     197             :         /* This tries to identify the valid syntaxes for the our
     198             :          * synthetic "gateway" host. */
     199             : 
     200             :         return
     201           0 :                 strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.")
     202             : #if ENABLE_COMPAT_GATEWAY_HOSTNAME
     203             :                 || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.")
     204             : #endif
     205             :                 ;
     206             : }
     207             : 
     208           0 : int sethostname_idempotent(const char *s) {
     209           0 :         char buf[HOST_NAME_MAX + 1] = {};
     210             : 
     211           0 :         assert(s);
     212             : 
     213           0 :         if (gethostname(buf, sizeof(buf)) < 0)
     214           0 :                 return -errno;
     215             : 
     216           0 :         if (streq(buf, s))
     217           0 :                 return 0;
     218             : 
     219           0 :         if (sethostname(s, strlen(s)) < 0)
     220           0 :                 return -errno;
     221             : 
     222           0 :         return 1;
     223             : }
     224             : 
     225           5 : int shorten_overlong(const char *s, char **ret) {
     226             :         char *h, *p;
     227             : 
     228             :         /* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
     229             :          * whatever comes earlier. */
     230             : 
     231           5 :         assert(s);
     232             : 
     233           5 :         h = strdup(s);
     234           5 :         if (!h)
     235           0 :                 return -ENOMEM;
     236             : 
     237           5 :         if (hostname_is_valid(h, false)) {
     238           2 :                 *ret = h;
     239           2 :                 return 0;
     240             :         }
     241             : 
     242           3 :         p = strchr(h, '.');
     243           3 :         if (p)
     244           2 :                 *p = 0;
     245             : 
     246           3 :         strshorten(h, HOST_NAME_MAX);
     247             : 
     248           3 :         if (!hostname_is_valid(h, false)) {
     249           1 :                 free(h);
     250           1 :                 return -EDOM;
     251             :         }
     252             : 
     253           2 :         *ret = h;
     254           2 :         return 1;
     255             : }
     256             : 
     257           5 : int read_etc_hostname_stream(FILE *f, char **ret) {
     258             :         int r;
     259             : 
     260           5 :         assert(f);
     261           5 :         assert(ret);
     262             : 
     263           4 :         for (;;) {
     264           9 :                 _cleanup_free_ char *line = NULL;
     265             :                 char *p;
     266             : 
     267           9 :                 r = read_line(f, LONG_LINE_MAX, &line);
     268           9 :                 if (r < 0)
     269           0 :                         return r;
     270           9 :                 if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
     271           1 :                         return -ENOENT;
     272             : 
     273           8 :                 p = strstrip(line);
     274             : 
     275             :                 /* File may have empty lines or comments, ignore them */
     276           8 :                 if (!IN_SET(*p, '\0', '#')) {
     277             :                         char *copy;
     278             : 
     279           4 :                         hostname_cleanup(p); /* normalize the hostname */
     280             : 
     281           4 :                         if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */
     282           0 :                                 return -EBADMSG;
     283             : 
     284           4 :                         copy = strdup(p);
     285           4 :                         if (!copy)
     286           0 :                                 return -ENOMEM;
     287             : 
     288           4 :                         *ret = copy;
     289           4 :                         return 0;
     290             :                 }
     291             :         }
     292             : }
     293             : 
     294           6 : int read_etc_hostname(const char *path, char **ret) {
     295           6 :         _cleanup_fclose_ FILE *f = NULL;
     296             : 
     297           6 :         assert(ret);
     298             : 
     299           6 :         if (!path)
     300           0 :                 path = "/etc/hostname";
     301             : 
     302           6 :         f = fopen(path, "re");
     303           6 :         if (!f)
     304           1 :                 return -errno;
     305             : 
     306           5 :         return read_etc_hostname_stream(f, ret);
     307             : 
     308             : }

Generated by: LCOV version 1.14