1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
7 #include "hostname-util.h"
10 #include "string-util.h"
12 #include "user-util.h"
14 char* get_default_hostname_raw(void) {
17 /* Returns the default hostname, and leaves any ??? in place. */
19 const char *e
= secure_getenv("SYSTEMD_DEFAULT_HOSTNAME");
21 if (hostname_is_valid(e
, VALID_HOSTNAME_QUESTION_MARK
))
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
, VALID_HOSTNAME_QUESTION_MARK
))
35 log_debug("Invalid hostname in os-release, ignoring: %s", f
);
38 return strdup(FALLBACK_HOSTNAME
);
41 bool valid_ldh_char(char c
) {
42 /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
44 return ascii_isalpha(c
) ||
49 bool hostname_is_valid(const char *s
, ValidHostnameFlags flags
) {
54 /* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only
55 * checks if the name is composed of allowed characters and the length is not above the maximum
56 * allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if
57 * VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note
58 * that due to the restricted charset and length this call is substantially more conservative than
59 * dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames
60 * with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */
65 if (streq(s
, ".host")) /* Used by the container logic to denote the "root container" */
66 return FLAGS_SET(flags
, VALID_HOSTNAME_DOT_HOST
);
68 for (p
= s
, dot
= hyphen
= true; *p
; p
++)
77 } else if (*p
== '-') {
85 if (!valid_ldh_char(*p
) && (*p
!= '?' || !FLAGS_SET(flags
, VALID_HOSTNAME_QUESTION_MARK
)))
92 if (dot
&& (n_dots
< 2 || !FLAGS_SET(flags
, VALID_HOSTNAME_TRAILING_DOT
)))
97 if (p
-s
> HOST_NAME_MAX
) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to
104 char* hostname_cleanup(char *s
) {
110 for (p
= s
, d
= s
, dot
= hyphen
= true; *p
&& d
- s
< HOST_NAME_MAX
; p
++)
119 } else if (*p
== '-') {
127 } else if (valid_ldh_char(*p
) || *p
== '?') {
133 if (d
> s
&& IN_SET(d
[-1], '-', '.'))
134 /* The dot can occur at most once, but we might have multiple
135 * hyphens, hence the loop */
142 bool is_localhost(const char *hostname
) {
145 /* This tries to identify local host and domain names
146 * described in RFC6761 plus the redhatism of localdomain */
148 return STRCASE_IN_SET(
152 "localhost.localdomain",
153 "localhost.localdomain.") ||
154 endswith_no_case(hostname
, ".localhost") ||
155 endswith_no_case(hostname
, ".localhost.") ||
156 endswith_no_case(hostname
, ".localhost.localdomain") ||
157 endswith_no_case(hostname
, ".localhost.localdomain.");
160 const char* etc_hostname(void) {
161 static const char *cached
= NULL
;
164 cached
= secure_getenv("SYSTEMD_ETC_HOSTNAME") ?: "/etc/hostname";
169 const char* etc_machine_info(void) {
170 static const char *cached
= NULL
;
173 cached
= secure_getenv("SYSTEMD_ETC_MACHINE_INFO") ?: "/etc/machine-info";
178 int get_pretty_hostname(char **ret
) {
179 _cleanup_free_
char *n
= NULL
;
184 r
= parse_env_file(NULL
, etc_machine_info(), "PRETTY_HOSTNAME", &n
);
195 int split_user_at_host(const char *s
, char **ret_user
, char **ret_host
) {
196 _cleanup_free_
char *u
= NULL
, *h
= NULL
;
198 /* Splits a user@host expression (one of those we accept on --machine= and similar). Returns NULL in
199 * each of the two return parameters if that part was left empty. */
203 const char *rhs
= strchr(s
, '@');
205 if (ret_user
&& rhs
> s
) {
206 u
= strndup(s
, rhs
- s
);
211 if (ret_host
&& rhs
[1] != 0) {
229 *ret_user
= TAKE_PTR(u
);
231 *ret_host
= TAKE_PTR(h
);
233 return !!rhs
; /* return > 0 if '@' was specified, 0 otherwise */
236 int machine_spec_valid(const char *s
) {
237 _cleanup_free_
char *u
= NULL
, *h
= NULL
;
242 r
= split_user_at_host(s
, &u
, &h
);
248 if (u
&& !valid_user_group_name(u
, VALID_USER_RELAX
| VALID_USER_ALLOW_NUMERIC
))
251 if (h
&& !hostname_is_valid(h
, VALID_HOSTNAME_DOT_HOST
))