1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include <sys/utsname.h>
10 #include "alloc-util.h"
12 #include "hostname-util.h"
14 #include "string-util.h"
17 char* get_default_hostname(void) {
20 const char *e
= secure_getenv("SYSTEMD_DEFAULT_HOSTNAME");
22 if (hostname_is_valid(e
, 0))
24 log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e
);
27 _cleanup_free_
char *f
= NULL
;
28 r
= parse_os_release(NULL
, "DEFAULT_HOSTNAME", &f
);
30 log_debug_errno(r
, "Failed to parse os-release, ignoring: %m");
32 if (hostname_is_valid(f
, 0))
34 log_debug("Invalid hostname in os-release, ignoring: %s", f
);
37 return strdup(FALLBACK_HOSTNAME
);
40 int gethostname_full(GetHostnameFlags flags
, char **ret
) {
41 _cleanup_free_
char *buf
= NULL
, *fallback
= NULL
;
47 assert_se(uname(&u
) >= 0);
50 if (isempty(s
) || streq(s
, "(none)") ||
51 (!FLAGS_SET(flags
, GET_HOSTNAME_ALLOW_LOCALHOST
) && is_localhost(s
)) ||
52 (FLAGS_SET(flags
, GET_HOSTNAME_SHORT
) && s
[0] == '.')) {
53 if (!FLAGS_SET(flags
, GET_HOSTNAME_FALLBACK_DEFAULT
))
56 s
= fallback
= get_default_hostname();
60 if (FLAGS_SET(flags
, GET_HOSTNAME_SHORT
) && s
[0] == '.')
64 if (FLAGS_SET(flags
, GET_HOSTNAME_SHORT
))
65 buf
= strdupcspn(s
, ".");
75 bool valid_ldh_char(char c
) {
76 /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
78 return ascii_isalpha(c
) ||
83 bool hostname_is_valid(const char *s
, ValidHostnameFlags flags
) {
88 /* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only
89 * checks if the name is composed of allowed characters and the length is not above the maximum
90 * allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if
91 * VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note
92 * that due to the restricted charset and length this call is substantially more conservative than
93 * dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames
94 * with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */
99 if (streq(s
, ".host")) /* Used by the container logic to denote the "root container" */
100 return FLAGS_SET(flags
, VALID_HOSTNAME_DOT_HOST
);
102 for (p
= s
, dot
= hyphen
= true; *p
; p
++)
111 } else if (*p
== '-') {
119 if (!valid_ldh_char(*p
))
126 if (dot
&& (n_dots
< 2 || !FLAGS_SET(flags
, VALID_HOSTNAME_TRAILING_DOT
)))
131 if (p
-s
> HOST_NAME_MAX
) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to
138 char* hostname_cleanup(char *s
) {
144 for (p
= s
, d
= s
, dot
= hyphen
= true; *p
&& d
- s
< HOST_NAME_MAX
; p
++)
153 } else if (*p
== '-') {
161 } else if (valid_ldh_char(*p
)) {
167 if (d
> s
&& IN_SET(d
[-1], '-', '.'))
168 /* The dot can occur at most once, but we might have multiple
169 * hyphens, hence the loop */
176 bool is_localhost(const char *hostname
) {
179 /* This tries to identify local host and domain names
180 * described in RFC6761 plus the redhatism of localdomain */
182 return STRCASE_IN_SET(
186 "localhost.localdomain",
187 "localhost.localdomain.") ||
188 endswith_no_case(hostname
, ".localhost") ||
189 endswith_no_case(hostname
, ".localhost.") ||
190 endswith_no_case(hostname
, ".localhost.localdomain") ||
191 endswith_no_case(hostname
, ".localhost.localdomain.");
194 int get_pretty_hostname(char **ret
) {
195 _cleanup_free_
char *n
= NULL
;
200 r
= parse_env_file(NULL
, "/etc/machine-info", "PRETTY_HOSTNAME", &n
);