| /*** |
| This file is part of systemd. |
| |
| Copyright 2015 Lennart Poettering |
| |
| systemd is free software; you can redistribute it and/or modify it |
| under the terms of the GNU Lesser General Public License as published by |
| the Free Software Foundation; either version 2.1 of the License, or |
| (at your option) any later version. |
| |
| systemd is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with systemd; If not, see <http://www.gnu.org/licenses/>. |
| ***/ |
| |
| #include <errno.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/utsname.h> |
| #include <unistd.h> |
| |
| #include "fd-util.h" |
| #include "fileio.h" |
| #include "hostname-util.h" |
| #include "macro.h" |
| #include "string-util.h" |
| |
| bool hostname_is_set(void) { |
| struct utsname u; |
| |
| assert_se(uname(&u) >= 0); |
| |
| if (isempty(u.nodename)) |
| return false; |
| |
| /* This is the built-in kernel default host name */ |
| if (streq(u.nodename, "(none)")) |
| return false; |
| |
| return true; |
| } |
| |
| char* gethostname_malloc(void) { |
| struct utsname u; |
| |
| /* This call tries to return something useful, either the actual hostname |
| * or it makes something up. The only reason it might fail is OOM. |
| * It might even return "localhost" if that's set. */ |
| |
| assert_se(uname(&u) >= 0); |
| |
| if (isempty(u.nodename) || streq(u.nodename, "(none)")) |
| return strdup(FALLBACK_HOSTNAME); |
| |
| return strdup(u.nodename); |
| } |
| |
| int gethostname_strict(char **ret) { |
| struct utsname u; |
| char *k; |
| |
| /* This call will rather fail than make up a name. It will not return "localhost" either. */ |
| |
| assert_se(uname(&u) >= 0); |
| |
| if (isempty(u.nodename)) |
| return -ENXIO; |
| |
| if (streq(u.nodename, "(none)")) |
| return -ENXIO; |
| |
| if (is_localhost(u.nodename)) |
| return -ENXIO; |
| |
| k = strdup(u.nodename); |
| if (!k) |
| return -ENOMEM; |
| |
| *ret = k; |
| return 0; |
| } |
| |
| static bool hostname_valid_char(char c) { |
| return |
| (c >= 'a' && c <= 'z') || |
| (c >= 'A' && c <= 'Z') || |
| (c >= '0' && c <= '9') || |
| c == '-' || |
| c == '_' || |
| c == '.'; |
| } |
| |
| /** |
| * Check if s looks like a valid host name or FQDN. This does not do |
| * full DNS validation, but only checks if the name is composed of |
| * allowed characters and the length is not above the maximum allowed |
| * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if |
| * allow_trailing_dot is true and at least two components are present |
| * in the name. Note that due to the restricted charset and length |
| * this call is substantially more conservative than |
| * dns_name_is_valid(). |
| */ |
| bool hostname_is_valid(const char *s, bool allow_trailing_dot) { |
| unsigned n_dots = 0; |
| const char *p; |
| bool dot; |
| |
| if (isempty(s)) |
| return false; |
| |
| /* Doesn't accept empty hostnames, hostnames with |
| * leading dots, and hostnames with multiple dots in a |
| * sequence. Also ensures that the length stays below |
| * HOST_NAME_MAX. */ |
| |
| for (p = s, dot = true; *p; p++) { |
| if (*p == '.') { |
| if (dot) |
| return false; |
| |
| dot = true; |
| n_dots++; |
| } else { |
| if (!hostname_valid_char(*p)) |
| return false; |
| |
| dot = false; |
| } |
| } |
| |
| if (dot && (n_dots < 2 || !allow_trailing_dot)) |
| return false; |
| |
| if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on |
| * Linux, but DNS allows domain names |
| * up to 255 characters */ |
| return false; |
| |
| return true; |
| } |
| |
| char* hostname_cleanup(char *s) { |
| char *p, *d; |
| bool dot; |
| |
| assert(s); |
| |
| strshorten(s, HOST_NAME_MAX); |
| |
| for (p = s, d = s, dot = true; *p; p++) { |
| if (*p == '.') { |
| if (dot) |
| continue; |
| |
| *(d++) = '.'; |
| dot = true; |
| } else if (hostname_valid_char(*p)) { |
| *(d++) = *p; |
| dot = false; |
| } |
| } |
| |
| if (dot && d > s) |
| d[-1] = 0; |
| else |
| *d = 0; |
| |
| return s; |
| } |
| |
| bool is_localhost(const char *hostname) { |
| assert(hostname); |
| |
| /* This tries to identify local host and domain names |
| * described in RFC6761 plus the redhatism of localdomain */ |
| |
| return strcaseeq(hostname, "localhost") || |
| strcaseeq(hostname, "localhost.") || |
| strcaseeq(hostname, "localhost.localdomain") || |
| strcaseeq(hostname, "localhost.localdomain.") || |
| endswith_no_case(hostname, ".localhost") || |
| endswith_no_case(hostname, ".localhost.") || |
| endswith_no_case(hostname, ".localhost.localdomain") || |
| endswith_no_case(hostname, ".localhost.localdomain."); |
| } |
| |
| bool is_gateway_hostname(const char *hostname) { |
| assert(hostname); |
| |
| /* This tries to identify the valid syntaxes for the our |
| * synthetic "gateway" host. */ |
| |
| return |
| strcaseeq(hostname, "gateway") || |
| strcaseeq(hostname, "gateway."); |
| } |
| |
| int sethostname_idempotent(const char *s) { |
| char buf[HOST_NAME_MAX + 1] = {}; |
| |
| assert(s); |
| |
| if (gethostname(buf, sizeof(buf)) < 0) |
| return -errno; |
| |
| if (streq(buf, s)) |
| return 0; |
| |
| if (sethostname(s, strlen(s)) < 0) |
| return -errno; |
| |
| return 1; |
| } |
| |
| int read_hostname_config(const char *path, char **hostname) { |
| _cleanup_fclose_ FILE *f = NULL; |
| char l[LINE_MAX]; |
| char *name = NULL; |
| |
| assert(path); |
| assert(hostname); |
| |
| f = fopen(path, "re"); |
| if (!f) |
| return -errno; |
| |
| /* may have comments, ignore them */ |
| FOREACH_LINE(l, f, return -errno) { |
| truncate_nl(l); |
| if (l[0] != '\0' && l[0] != '#') { |
| /* found line with value */ |
| name = hostname_cleanup(l); |
| name = strdup(name); |
| if (!name) |
| return -ENOMEM; |
| break; |
| } |
| } |
| |
| if (!name) |
| /* no non-empty line found */ |
| return -ENOENT; |
| |
| *hostname = name; |
| return 0; |
| } |