]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/hostname-util.c
tree-wide: remove Lennart's copyright lines
[thirdparty/systemd.git] / src / basic / hostname-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <limits.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <sys/utsname.h>
8 #include <unistd.h>
9
10 #include "alloc-util.h"
11 #include "def.h"
12 #include "fd-util.h"
13 #include "fileio.h"
14 #include "hostname-util.h"
15 #include "macro.h"
16 #include "string-util.h"
17
18 bool hostname_is_set(void) {
19 struct utsname u;
20
21 assert_se(uname(&u) >= 0);
22
23 if (isempty(u.nodename))
24 return false;
25
26 /* This is the built-in kernel default host name */
27 if (streq(u.nodename, "(none)"))
28 return false;
29
30 return true;
31 }
32
33 char* gethostname_malloc(void) {
34 struct utsname u;
35
36 /* This call tries to return something useful, either the actual hostname
37 * or it makes something up. The only reason it might fail is OOM.
38 * It might even return "localhost" if that's set. */
39
40 assert_se(uname(&u) >= 0);
41
42 if (isempty(u.nodename) || streq(u.nodename, "(none)"))
43 return strdup(FALLBACK_HOSTNAME);
44
45 return strdup(u.nodename);
46 }
47
48 int gethostname_strict(char **ret) {
49 struct utsname u;
50 char *k;
51
52 /* This call will rather fail than make up a name. It will not return "localhost" either. */
53
54 assert_se(uname(&u) >= 0);
55
56 if (isempty(u.nodename))
57 return -ENXIO;
58
59 if (streq(u.nodename, "(none)"))
60 return -ENXIO;
61
62 if (is_localhost(u.nodename))
63 return -ENXIO;
64
65 k = strdup(u.nodename);
66 if (!k)
67 return -ENOMEM;
68
69 *ret = k;
70 return 0;
71 }
72
73 static bool hostname_valid_char(char c) {
74 return
75 (c >= 'a' && c <= 'z') ||
76 (c >= 'A' && c <= 'Z') ||
77 (c >= '0' && c <= '9') ||
78 IN_SET(c, '-', '_', '.');
79 }
80
81 /**
82 * Check if s looks like a valid host name or FQDN. This does not do
83 * full DNS validation, but only checks if the name is composed of
84 * allowed characters and the length is not above the maximum allowed
85 * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
86 * allow_trailing_dot is true and at least two components are present
87 * in the name. Note that due to the restricted charset and length
88 * this call is substantially more conservative than
89 * dns_name_is_valid().
90 */
91 bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
92 unsigned n_dots = 0;
93 const char *p;
94 bool dot;
95
96 if (isempty(s))
97 return false;
98
99 /* Doesn't accept empty hostnames, hostnames with
100 * leading dots, and hostnames with multiple dots in a
101 * sequence. Also ensures that the length stays below
102 * HOST_NAME_MAX. */
103
104 for (p = s, dot = true; *p; p++) {
105 if (*p == '.') {
106 if (dot)
107 return false;
108
109 dot = true;
110 n_dots++;
111 } else {
112 if (!hostname_valid_char(*p))
113 return false;
114
115 dot = false;
116 }
117 }
118
119 if (dot && (n_dots < 2 || !allow_trailing_dot))
120 return false;
121
122 if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
123 * Linux, but DNS allows domain names
124 * up to 255 characters */
125 return false;
126
127 return true;
128 }
129
130 char* hostname_cleanup(char *s) {
131 char *p, *d;
132 bool dot;
133
134 assert(s);
135
136 strshorten(s, HOST_NAME_MAX);
137
138 for (p = s, d = s, dot = true; *p; p++) {
139 if (*p == '.') {
140 if (dot)
141 continue;
142
143 *(d++) = '.';
144 dot = true;
145 } else if (hostname_valid_char(*p)) {
146 *(d++) = *p;
147 dot = false;
148 }
149 }
150
151 if (dot && d > s)
152 d[-1] = 0;
153 else
154 *d = 0;
155
156 return s;
157 }
158
159 bool is_localhost(const char *hostname) {
160 assert(hostname);
161
162 /* This tries to identify local host and domain names
163 * described in RFC6761 plus the redhatism of localdomain */
164
165 return strcaseeq(hostname, "localhost") ||
166 strcaseeq(hostname, "localhost.") ||
167 strcaseeq(hostname, "localhost.localdomain") ||
168 strcaseeq(hostname, "localhost.localdomain.") ||
169 endswith_no_case(hostname, ".localhost") ||
170 endswith_no_case(hostname, ".localhost.") ||
171 endswith_no_case(hostname, ".localhost.localdomain") ||
172 endswith_no_case(hostname, ".localhost.localdomain.");
173 }
174
175 bool is_gateway_hostname(const char *hostname) {
176 assert(hostname);
177
178 /* This tries to identify the valid syntaxes for the our
179 * synthetic "gateway" host. */
180
181 return
182 strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.")
183 #if ENABLE_COMPAT_GATEWAY_HOSTNAME
184 || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.")
185 #endif
186 ;
187 }
188
189 int sethostname_idempotent(const char *s) {
190 char buf[HOST_NAME_MAX + 1] = {};
191
192 assert(s);
193
194 if (gethostname(buf, sizeof(buf)) < 0)
195 return -errno;
196
197 if (streq(buf, s))
198 return 0;
199
200 if (sethostname(s, strlen(s)) < 0)
201 return -errno;
202
203 return 1;
204 }
205
206 int shorten_overlong(const char *s, char **ret) {
207 char *h, *p;
208
209 /* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
210 * whatever comes earlier. */
211
212 assert(s);
213
214 h = strdup(s);
215 if (!h)
216 return -ENOMEM;
217
218 if (hostname_is_valid(h, false)) {
219 *ret = h;
220 return 0;
221 }
222
223 p = strchr(h, '.');
224 if (p)
225 *p = 0;
226
227 strshorten(h, HOST_NAME_MAX);
228
229 if (!hostname_is_valid(h, false)) {
230 free(h);
231 return -EDOM;
232 }
233
234 *ret = h;
235 return 1;
236 }
237
238 int read_etc_hostname_stream(FILE *f, char **ret) {
239 int r;
240
241 assert(f);
242 assert(ret);
243
244 for (;;) {
245 _cleanup_free_ char *line = NULL;
246 char *p;
247
248 r = read_line(f, LONG_LINE_MAX, &line);
249 if (r < 0)
250 return r;
251 if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
252 return -ENOENT;
253
254 p = strstrip(line);
255
256 /* File may have empty lines or comments, ignore them */
257 if (!IN_SET(*p, '\0', '#')) {
258 char *copy;
259
260 hostname_cleanup(p); /* normalize the hostname */
261
262 if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */
263 return -EBADMSG;
264
265 copy = strdup(p);
266 if (!copy)
267 return -ENOMEM;
268
269 *ret = copy;
270 return 0;
271 }
272 }
273 }
274
275 int read_etc_hostname(const char *path, char **ret) {
276 _cleanup_fclose_ FILE *f = NULL;
277
278 assert(ret);
279
280 if (!path)
281 path = "/etc/hostname";
282
283 f = fopen(path, "re");
284 if (!f)
285 return -errno;
286
287 return read_etc_hostname_stream(f, ret);
288
289 }