Blame src/libnm-systemd-shared/src/basic/hostname-util.c

Packit Service dff8e4
/* SPDX-License-Identifier: LGPL-2.1-or-later */
Packit Service dff8e4
Packit Service dff8e4
#include "nm-sd-adapt-shared.h"
Packit Service dff8e4
Packit Service dff8e4
#include <errno.h>
Packit Service dff8e4
#include <limits.h>
Packit Service dff8e4
#include <stdio.h>
Packit Service dff8e4
#include <sys/utsname.h>
Packit Service dff8e4
#include <unistd.h>
Packit Service dff8e4
Packit Service dff8e4
#include "alloc-util.h"
Packit Service dff8e4
#include "hostname-util.h"
Packit Service dff8e4
#include "string-util.h"
Packit Service dff8e4
#include "strv.h"
Packit Service dff8e4
Packit Service dff8e4
#if 0 /* NM_IGNORED */
Packit Service dff8e4
char* gethostname_malloc(void) {
Packit Service dff8e4
        struct utsname u;
Packit Service dff8e4
        const char *s;
Packit Service dff8e4
Packit Service dff8e4
        /* This call tries to return something useful, either the actual hostname
Packit Service dff8e4
         * or it makes something up. The only reason it might fail is OOM.
Packit Service dff8e4
         * It might even return "localhost" if that's set. */
Packit Service dff8e4
Packit Service dff8e4
        assert_se(uname(&u) >= 0);
Packit Service dff8e4
Packit Service dff8e4
        s = u.nodename;
Packit Service dff8e4
        if (isempty(s) || streq(s, "(none)"))
Packit Service dff8e4
                s = FALLBACK_HOSTNAME;
Packit Service dff8e4
Packit Service dff8e4
        return strdup(s);
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
char* gethostname_short_malloc(void) {
Packit Service dff8e4
        struct utsname u;
Packit Service dff8e4
        const char *s;
Packit Service dff8e4
Packit Service dff8e4
        /* Like above, but kills the FQDN part if present. */
Packit Service dff8e4
Packit Service dff8e4
        assert_se(uname(&u) >= 0);
Packit Service dff8e4
Packit Service dff8e4
        s = u.nodename;
Packit Service dff8e4
        if (isempty(s) || streq(s, "(none)") || s[0] == '.') {
Packit Service dff8e4
                s = FALLBACK_HOSTNAME;
Packit Service dff8e4
                assert(s[0] != '.');
Packit Service dff8e4
        }
Packit Service dff8e4
Packit Service dff8e4
        return strndup(s, strcspn(s, "."));
Packit Service dff8e4
}
Packit Service dff8e4
#endif /* NM_IGNORED */
Packit Service dff8e4
Packit Service dff8e4
int gethostname_strict(char **ret) {
Packit Service dff8e4
        struct utsname u;
Packit Service dff8e4
        char *k;
Packit Service dff8e4
Packit Service dff8e4
        /* This call will rather fail than make up a name. It will not return "localhost" either. */
Packit Service dff8e4
Packit Service dff8e4
        assert_se(uname(&u) >= 0);
Packit Service dff8e4
Packit Service dff8e4
        if (isempty(u.nodename))
Packit Service dff8e4
                return -ENXIO;
Packit Service dff8e4
Packit Service dff8e4
        if (streq(u.nodename, "(none)"))
Packit Service dff8e4
                return -ENXIO;
Packit Service dff8e4
Packit Service dff8e4
        if (is_localhost(u.nodename))
Packit Service dff8e4
                return -ENXIO;
Packit Service dff8e4
Packit Service dff8e4
        k = strdup(u.nodename);
Packit Service dff8e4
        if (!k)
Packit Service dff8e4
                return -ENOMEM;
Packit Service dff8e4
Packit Service dff8e4
        *ret = k;
Packit Service dff8e4
        return 0;
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
bool valid_ldh_char(char c) {
Packit Service dff8e4
        /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
Packit Service dff8e4
Packit Service dff8e4
        return
Packit Service dff8e4
                (c >= 'a' && c <= 'z') ||
Packit Service dff8e4
                (c >= 'A' && c <= 'Z') ||
Packit Service dff8e4
                (c >= '0' && c <= '9') ||
Packit Service dff8e4
                c == '-';
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
bool hostname_is_valid(const char *s, ValidHostnameFlags flags) {
Packit Service dff8e4
        unsigned n_dots = 0;
Packit Service dff8e4
        const char *p;
Packit Service dff8e4
        bool dot, hyphen;
Packit Service dff8e4
Packit Service dff8e4
        /* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only
Packit Service dff8e4
         * checks if the name is composed of allowed characters and the length is not above the maximum
Packit Service dff8e4
         * allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if
Packit Service dff8e4
         * VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note
Packit Service dff8e4
         * that due to the restricted charset and length this call is substantially more conservative than
Packit Service dff8e4
         * dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames
Packit Service dff8e4
         * with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */
Packit Service dff8e4
Packit Service dff8e4
        if (isempty(s))
Packit Service dff8e4
                return false;
Packit Service dff8e4
Packit Service dff8e4
        if (streq(s, ".host")) /* Used by the container logic to denote the "root container" */
Packit Service dff8e4
                return FLAGS_SET(flags, VALID_HOSTNAME_DOT_HOST);
Packit Service dff8e4
Packit Service dff8e4
        for (p = s, dot = hyphen = true; *p; p++)
Packit Service dff8e4
                if (*p == '.') {
Packit Service dff8e4
                        if (dot || hyphen)
Packit Service dff8e4
                                return false;
Packit Service dff8e4
Packit Service dff8e4
                        dot = true;
Packit Service dff8e4
                        hyphen = false;
Packit Service dff8e4
                        n_dots++;
Packit Service dff8e4
Packit Service dff8e4
                } else if (*p == '-') {
Packit Service dff8e4
                        if (dot)
Packit Service dff8e4
                                return false;
Packit Service dff8e4
Packit Service dff8e4
                        dot = false;
Packit Service dff8e4
                        hyphen = true;
Packit Service dff8e4
Packit Service dff8e4
                } else {
Packit Service dff8e4
                        if (!valid_ldh_char(*p))
Packit Service dff8e4
                                return false;
Packit Service dff8e4
Packit Service dff8e4
                        dot = false;
Packit Service dff8e4
                        hyphen = false;
Packit Service dff8e4
                }
Packit Service dff8e4
Packit Service dff8e4
        if (dot && (n_dots < 2 || !FLAGS_SET(flags, VALID_HOSTNAME_TRAILING_DOT)))
Packit Service dff8e4
                return false;
Packit Service dff8e4
        if (hyphen)
Packit Service dff8e4
                return false;
Packit Service dff8e4
Packit Service dff8e4
        if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to
Packit Service dff8e4
                                  * 255 characters */
Packit Service dff8e4
                return false;
Packit Service dff8e4
Packit Service dff8e4
        return true;
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
char* hostname_cleanup(char *s) {
Packit Service dff8e4
        char *p, *d;
Packit Service dff8e4
        bool dot, hyphen;
Packit Service dff8e4
Packit Service dff8e4
        assert(s);
Packit Service dff8e4
Packit Service dff8e4
        for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++)
Packit Service dff8e4
                if (*p == '.') {
Packit Service dff8e4
                        if (dot || hyphen)
Packit Service dff8e4
                                continue;
Packit Service dff8e4
Packit Service dff8e4
                        *(d++) = '.';
Packit Service dff8e4
                        dot = true;
Packit Service dff8e4
                        hyphen = false;
Packit Service dff8e4
Packit Service dff8e4
                } else if (*p == '-') {
Packit Service dff8e4
                        if (dot)
Packit Service dff8e4
                                continue;
Packit Service dff8e4
Packit Service dff8e4
                        *(d++) = '-';
Packit Service dff8e4
                        dot = false;
Packit Service dff8e4
                        hyphen = true;
Packit Service dff8e4
Packit Service dff8e4
                } else if (valid_ldh_char(*p)) {
Packit Service dff8e4
                        *(d++) = *p;
Packit Service dff8e4
                        dot = false;
Packit Service dff8e4
                        hyphen = false;
Packit Service dff8e4
                }
Packit Service dff8e4
Packit Service dff8e4
        if (d > s && IN_SET(d[-1], '-', '.'))
Packit Service dff8e4
                /* The dot can occur at most once, but we might have multiple
Packit Service dff8e4
                 * hyphens, hence the loop */
Packit Service dff8e4
                d--;
Packit Service dff8e4
        *d = 0;
Packit Service dff8e4
Packit Service dff8e4
        return s;
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
bool is_localhost(const char *hostname) {
Packit Service dff8e4
        assert(hostname);
Packit Service dff8e4
Packit Service dff8e4
        /* This tries to identify local host and domain names
Packit Service dff8e4
         * described in RFC6761 plus the redhatism of localdomain */
Packit Service dff8e4
Packit Service dff8e4
        return STRCASE_IN_SET(
Packit Service dff8e4
                        hostname,
Packit Service dff8e4
                        "localhost",
Packit Service dff8e4
                        "localhost.",
Packit Service dff8e4
                        "localhost.localdomain",
Packit Service dff8e4
                        "localhost.localdomain.") ||
Packit Service dff8e4
                endswith_no_case(hostname, ".localhost") ||
Packit Service dff8e4
                endswith_no_case(hostname, ".localhost.") ||
Packit Service dff8e4
                endswith_no_case(hostname, ".localhost.localdomain") ||
Packit Service dff8e4
                endswith_no_case(hostname, ".localhost.localdomain.");
Packit Service dff8e4
}