LCOV - code coverage report
Current view: top level - basic - hostname-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 107 148 72.3 %
Date: 2019-08-23 13:36:53 Functions: 8 12 66.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 99 152 65.1 %

           Branch data     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                 :         48 : 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         [ -  + ]:         48 :         assert_se(uname(&u) >= 0);
      40                 :            : 
      41   [ +  -  -  + ]:         48 :         if (isempty(u.nodename) || streq(u.nodename, "(none)"))
      42                 :          0 :                 return strdup(FALLBACK_HOSTNAME);
      43                 :            : 
      44                 :         48 :         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                 :      10732 : bool valid_ldh_char(char c) {
      73                 :            :         return
      74   [ +  +  +  + ]:      10732 :                 (c >= 'a' && c <= 'z') ||
      75   [ +  +  +  + ]:       4128 :                 (c >= 'A' && c <= 'Z') ||
      76   [ +  +  +  +  :      21464 :                 (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                 :        280 : bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
      91                 :        280 :         unsigned n_dots = 0;
      92                 :            :         const char *p;
      93                 :            :         bool dot, hyphen;
      94                 :            : 
      95         [ +  + ]:        280 :         if (isempty(s))
      96                 :         20 :                 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         [ +  + ]:       3616 :         for (p = s, dot = hyphen = true; *p; p++)
     104         [ +  + ]:       3428 :                 if (*p == '.') {
     105   [ +  +  -  + ]:        180 :                         if (dot || hyphen)
     106                 :         44 :                                 return false;
     107                 :            : 
     108                 :        136 :                         dot = true;
     109                 :        136 :                         hyphen = false;
     110                 :        136 :                         n_dots++;
     111                 :            : 
     112         [ +  + ]:       3248 :                 } else if (*p == '-') {
     113         [ -  + ]:        156 :                         if (dot)
     114                 :          0 :                                 return false;
     115                 :            : 
     116                 :        156 :                         dot = false;
     117                 :        156 :                         hyphen = true;
     118                 :            : 
     119                 :            :                 } else {
     120         [ +  + ]:       3092 :                         if (!valid_ldh_char(*p))
     121                 :         28 :                                 return false;
     122                 :            : 
     123                 :       3064 :                         dot = false;
     124                 :       3064 :                         hyphen = false;
     125                 :            :                 }
     126                 :            : 
     127   [ +  +  +  +  :        188 :         if (dot && (n_dots < 2 || !allow_trailing_dot))
                   +  + ]
     128                 :         24 :                 return false;
     129         [ -  + ]:        164 :         if (hyphen)
     130                 :          0 :                 return false;
     131                 :            : 
     132         [ +  + ]:        164 :         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                 :         20 :                 return false;
     136                 :            : 
     137                 :        144 :         return true;
     138                 :            : }
     139                 :            : 
     140                 :         96 : char* hostname_cleanup(char *s) {
     141                 :            :         char *p, *d;
     142                 :            :         bool dot, hyphen;
     143                 :            : 
     144         [ -  + ]:         96 :         assert(s);
     145                 :            : 
     146   [ +  +  +  + ]:       1304 :         for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++)
     147         [ +  + ]:       1208 :                 if (*p == '.') {
     148   [ +  +  +  + ]:        124 :                         if (dot || hyphen)
     149                 :         64 :                                 continue;
     150                 :            : 
     151                 :         60 :                         *(d++) = '.';
     152                 :         60 :                         dot = true;
     153                 :         60 :                         hyphen = false;
     154                 :            : 
     155         [ +  + ]:       1084 :                 } else if (*p == '-') {
     156         [ +  + ]:         44 :                         if (dot)
     157                 :         16 :                                 continue;
     158                 :            : 
     159                 :         28 :                         *(d++) = '-';
     160                 :         28 :                         dot = false;
     161                 :         28 :                         hyphen = true;
     162                 :            : 
     163         [ +  + ]:       1040 :                 } else if (valid_ldh_char(*p)) {
     164                 :       1016 :                         *(d++) = *p;
     165                 :       1016 :                         dot = false;
     166                 :       1016 :                         hyphen = false;
     167                 :            :                 }
     168                 :            : 
     169   [ +  +  +  +  :         96 :         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                 :         28 :                 d--;
     173                 :         96 :         *d = 0;
     174                 :            : 
     175                 :         96 :         return s;
     176                 :            : }
     177                 :            : 
     178                 :        164 : bool is_localhost(const char *hostname) {
     179         [ -  + ]:        164 :         assert(hostname);
     180                 :            : 
     181                 :            :         /* This tries to identify local host and domain names
     182                 :            :          * described in RFC6761 plus the redhatism of localdomain */
     183                 :            : 
     184                 :        324 :         return strcaseeq(hostname, "localhost") ||
     185         [ +  - ]:        160 :                strcaseeq(hostname, "localhost.") ||
     186         [ +  + ]:        160 :                strcaseeq(hostname, "localhost.localdomain") ||
     187         [ +  - ]:        156 :                strcaseeq(hostname, "localhost.localdomain.") ||
     188         [ +  - ]:        156 :                endswith_no_case(hostname, ".localhost") ||
     189         [ +  - ]:        156 :                endswith_no_case(hostname, ".localhost.") ||
     190   [ +  +  +  - ]:        480 :                endswith_no_case(hostname, ".localhost.localdomain") ||
     191         [ -  + ]:        156 :                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                 :         20 : 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         [ -  + ]:         20 :         assert(s);
     232                 :            : 
     233                 :         20 :         h = strdup(s);
     234         [ -  + ]:         20 :         if (!h)
     235                 :          0 :                 return -ENOMEM;
     236                 :            : 
     237         [ +  + ]:         20 :         if (hostname_is_valid(h, false)) {
     238                 :          8 :                 *ret = h;
     239                 :          8 :                 return 0;
     240                 :            :         }
     241                 :            : 
     242                 :         12 :         p = strchr(h, '.');
     243         [ +  + ]:         12 :         if (p)
     244                 :          8 :                 *p = 0;
     245                 :            : 
     246                 :         12 :         strshorten(h, HOST_NAME_MAX);
     247                 :            : 
     248         [ +  + ]:         12 :         if (!hostname_is_valid(h, false)) {
     249                 :          4 :                 free(h);
     250                 :          4 :                 return -EDOM;
     251                 :            :         }
     252                 :            : 
     253                 :          8 :         *ret = h;
     254                 :          8 :         return 1;
     255                 :            : }
     256                 :            : 
     257                 :         20 : int read_etc_hostname_stream(FILE *f, char **ret) {
     258                 :            :         int r;
     259                 :            : 
     260         [ -  + ]:         20 :         assert(f);
     261         [ -  + ]:         20 :         assert(ret);
     262                 :            : 
     263                 :         16 :         for (;;) {
     264         [ +  + ]:         36 :                 _cleanup_free_ char *line = NULL;
     265                 :            :                 char *p;
     266                 :            : 
     267                 :         36 :                 r = read_line(f, LONG_LINE_MAX, &line);
     268         [ -  + ]:         36 :                 if (r < 0)
     269                 :          0 :                         return r;
     270         [ +  + ]:         36 :                 if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
     271                 :          4 :                         return -ENOENT;
     272                 :            : 
     273                 :         32 :                 p = strstrip(line);
     274                 :            : 
     275                 :            :                 /* File may have empty lines or comments, ignore them */
     276   [ +  +  +  + ]:         32 :                 if (!IN_SET(*p, '\0', '#')) {
     277                 :            :                         char *copy;
     278                 :            : 
     279                 :         16 :                         hostname_cleanup(p); /* normalize the hostname */
     280                 :            : 
     281         [ -  + ]:         16 :                         if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */
     282                 :          0 :                                 return -EBADMSG;
     283                 :            : 
     284                 :         16 :                         copy = strdup(p);
     285         [ -  + ]:         16 :                         if (!copy)
     286                 :          0 :                                 return -ENOMEM;
     287                 :            : 
     288                 :         16 :                         *ret = copy;
     289                 :         16 :                         return 0;
     290                 :            :                 }
     291                 :            :         }
     292                 :            : }
     293                 :            : 
     294                 :         24 : int read_etc_hostname(const char *path, char **ret) {
     295                 :         24 :         _cleanup_fclose_ FILE *f = NULL;
     296                 :            : 
     297         [ -  + ]:         24 :         assert(ret);
     298                 :            : 
     299         [ -  + ]:         24 :         if (!path)
     300                 :          0 :                 path = "/etc/hostname";
     301                 :            : 
     302                 :         24 :         f = fopen(path, "re");
     303         [ +  + ]:         24 :         if (!f)
     304                 :          4 :                 return -errno;
     305                 :            : 
     306                 :         20 :         return read_etc_hostname_stream(f, ret);
     307                 :            : 
     308                 :            : }

Generated by: LCOV version 1.14