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