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