]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
958b66ea | 2 | |
11c3a366 TA |
3 | #include <errno.h> |
4 | #include <limits.h> | |
5 | #include <stdio.h> | |
05c6f341 | 6 | #include <stdlib.h> |
07630cea | 7 | #include <sys/utsname.h> |
11c3a366 | 8 | #include <unistd.h> |
958b66ea | 9 | |
f35cb39e | 10 | #include "alloc-util.h" |
0da2bb74 | 11 | #include "env-file.h" |
3ffd4af2 | 12 | #include "hostname-util.h" |
05c6f341 | 13 | #include "os-util.h" |
07630cea | 14 | #include "string-util.h" |
ddd6a22a | 15 | #include "strv.h" |
958b66ea | 16 | |
05c6f341 | 17 | char* get_default_hostname(void) { |
e7637751 ZJS |
18 | int r; |
19 | ||
05c6f341 ZJS |
20 | const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME"); |
21 | if (e) { | |
22 | if (hostname_is_valid(e, 0)) | |
23 | return strdup(e); | |
24 | log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e); | |
25 | } | |
26 | ||
e7637751 ZJS |
27 | _cleanup_free_ char *f = NULL; |
28 | r = parse_os_release(NULL, "DEFAULT_HOSTNAME", &f); | |
29 | if (r < 0) | |
30 | log_debug_errno(r, "Failed to parse os-release, ignoring: %m"); | |
31 | else if (f) { | |
32 | if (hostname_is_valid(f, 0)) | |
33 | return TAKE_PTR(f); | |
34 | log_debug("Invalid hostname in os-release, ignoring: %s", f); | |
35 | } | |
36 | ||
05c6f341 ZJS |
37 | return strdup(FALLBACK_HOSTNAME); |
38 | } | |
39 | ||
0995accd YW |
40 | int gethostname_full(GetHostnameFlags flags, char **ret) { |
41 | _cleanup_free_ char *buf = NULL, *fallback = NULL; | |
958b66ea | 42 | struct utsname u; |
e97708fa | 43 | const char *s; |
958b66ea | 44 | |
0995accd | 45 | assert(ret); |
8e1ad1ea | 46 | |
958b66ea LP |
47 | assert_se(uname(&u) >= 0); |
48 | ||
e97708fa | 49 | s = u.nodename; |
9383fa08 | 50 | if (isempty(s) || streq(s, "(none)") || |
0995accd YW |
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)) | |
54 | return -ENXIO; | |
55 | ||
56 | s = fallback = get_default_hostname(); | |
05c6f341 | 57 | if (!s) |
0995accd | 58 | return -ENOMEM; |
05c6f341 | 59 | |
0995accd YW |
60 | if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.') |
61 | return -ENXIO; | |
e97708fa ZJS |
62 | } |
63 | ||
0995accd | 64 | if (FLAGS_SET(flags, GET_HOSTNAME_SHORT)) |
e8bec624 | 65 | buf = strdupcspn(s, "."); |
0995accd YW |
66 | else |
67 | buf = strdup(s); | |
68 | if (!buf) | |
8e1ad1ea LP |
69 | return -ENOMEM; |
70 | ||
0995accd | 71 | *ret = TAKE_PTR(buf); |
8e1ad1ea LP |
72 | return 0; |
73 | } | |
74 | ||
d65652f1 | 75 | bool valid_ldh_char(char c) { |
9e815cf2 LP |
76 | /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */ |
77 | ||
ff25d338 LP |
78 | return ascii_isalpha(c) || |
79 | ascii_isdigit(c) || | |
d65652f1 | 80 | c == '-'; |
958b66ea LP |
81 | } |
82 | ||
52ef5dd7 | 83 | bool hostname_is_valid(const char *s, ValidHostnameFlags flags) { |
b59abc4d | 84 | unsigned n_dots = 0; |
958b66ea | 85 | const char *p; |
d65652f1 | 86 | bool dot, hyphen; |
958b66ea | 87 | |
52ef5dd7 LP |
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. */ | |
95 | ||
958b66ea LP |
96 | if (isempty(s)) |
97 | return false; | |
98 | ||
52ef5dd7 LP |
99 | if (streq(s, ".host")) /* Used by the container logic to denote the "root container" */ |
100 | return FLAGS_SET(flags, VALID_HOSTNAME_DOT_HOST); | |
958b66ea | 101 | |
d65652f1 | 102 | for (p = s, dot = hyphen = true; *p; p++) |
958b66ea | 103 | if (*p == '.') { |
d65652f1 | 104 | if (dot || hyphen) |
958b66ea LP |
105 | return false; |
106 | ||
107 | dot = true; | |
d65652f1 | 108 | hyphen = false; |
313cefa1 | 109 | n_dots++; |
d65652f1 ZJS |
110 | |
111 | } else if (*p == '-') { | |
112 | if (dot) | |
113 | return false; | |
114 | ||
115 | dot = false; | |
116 | hyphen = true; | |
117 | ||
958b66ea | 118 | } else { |
d65652f1 | 119 | if (!valid_ldh_char(*p)) |
958b66ea LP |
120 | return false; |
121 | ||
122 | dot = false; | |
d65652f1 | 123 | hyphen = false; |
958b66ea | 124 | } |
958b66ea | 125 | |
52ef5dd7 | 126 | if (dot && (n_dots < 2 || !FLAGS_SET(flags, VALID_HOSTNAME_TRAILING_DOT))) |
958b66ea | 127 | return false; |
d65652f1 ZJS |
128 | if (hyphen) |
129 | return false; | |
958b66ea | 130 | |
52ef5dd7 LP |
131 | if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to |
132 | * 255 characters */ | |
958b66ea LP |
133 | return false; |
134 | ||
135 | return true; | |
136 | } | |
137 | ||
ae691c1d | 138 | char* hostname_cleanup(char *s) { |
958b66ea | 139 | char *p, *d; |
d65652f1 | 140 | bool dot, hyphen; |
958b66ea LP |
141 | |
142 | assert(s); | |
143 | ||
5fe7a0a7 | 144 | for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++) |
958b66ea | 145 | if (*p == '.') { |
d65652f1 | 146 | if (dot || hyphen) |
958b66ea LP |
147 | continue; |
148 | ||
149 | *(d++) = '.'; | |
150 | dot = true; | |
d65652f1 ZJS |
151 | hyphen = false; |
152 | ||
153 | } else if (*p == '-') { | |
154 | if (dot) | |
155 | continue; | |
156 | ||
157 | *(d++) = '-'; | |
158 | dot = false; | |
159 | hyphen = true; | |
160 | ||
161 | } else if (valid_ldh_char(*p)) { | |
ae691c1d | 162 | *(d++) = *p; |
958b66ea | 163 | dot = false; |
d65652f1 | 164 | hyphen = false; |
958b66ea | 165 | } |
958b66ea | 166 | |
d65652f1 ZJS |
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 */ | |
170 | d--; | |
171 | *d = 0; | |
958b66ea | 172 | |
958b66ea LP |
173 | return s; |
174 | } | |
175 | ||
176 | bool is_localhost(const char *hostname) { | |
177 | assert(hostname); | |
178 | ||
179 | /* This tries to identify local host and domain names | |
63003524 | 180 | * described in RFC6761 plus the redhatism of localdomain */ |
958b66ea | 181 | |
ddd6a22a LP |
182 | return STRCASE_IN_SET( |
183 | hostname, | |
184 | "localhost", | |
185 | "localhost.", | |
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."); | |
958b66ea | 192 | } |
0da2bb74 LP |
193 | |
194 | int get_pretty_hostname(char **ret) { | |
195 | _cleanup_free_ char *n = NULL; | |
196 | int r; | |
197 | ||
198 | assert(ret); | |
199 | ||
200 | r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &n); | |
201 | if (r < 0) | |
202 | return r; | |
203 | ||
204 | if (isempty(n)) | |
205 | return -ENXIO; | |
206 | ||
207 | *ret = TAKE_PTR(n); | |
208 | return 0; | |
209 | } |