5248e7e1f1
This changes the symbolic name for the default gateway from "gateway" to "_gateway". A new configuration option -Dcompat-gateway-hostname=true|false is added. If it is set, the old name is also supported, but the new name is used as the canonical name in either case. This is intended as a temporary measure to make the transition easier, and the option should be removed after a few releases, at which point only the new name will be used. The old "gateway" name mostly works OK, but hasn't gained widespread acceptance because of the following (potential) conflicts: - it is completely legal to have a host called "gateway" - there is no guarantee that "gateway" will not be registered as a TLD, even though this currently seems unlikely. (Even then, there would be no conflict except for the case when the top-level domain itself was being resolved. The "gateway" or "_gateway" labels have only special meaning when the whole name consists of a single label, so resolution of any subdomain of the hypothetical gateway. TLD would still work OK. ) Moving to "_gateway" avoids those issues because underscores are not allowed in host names (RFC 1123, §2.1) and avoids potential conflicts with local or global names. v2: - simplify the logic to hardcode "_gateway" and allow -Dcompat-gateway-hostname=true as a temporary measure.
255 lines
7 KiB
C
255 lines
7 KiB
C
/***
|
|
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.")
|
|
#if ENABLE_COMPAT_GATEWAY_HOSTNAME
|
|
|| strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.")
|
|
#endif
|
|
;
|
|
}
|
|
|
|
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;
|
|
}
|