]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/hostname-util.c
util: introduce memcmp_safe()
[thirdparty/systemd.git] / src / basic / hostname-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
958b66ea 2
11c3a366
TA
3#include <errno.h>
4#include <limits.h>
5#include <stdio.h>
6#include <string.h>
07630cea 7#include <sys/utsname.h>
11c3a366 8#include <unistd.h>
958b66ea 9
f35cb39e
LP
10#include "alloc-util.h"
11#include "def.h"
3ffd4af2 12#include "fd-util.h"
0d39fa9c 13#include "fileio.h"
3ffd4af2 14#include "hostname-util.h"
11c3a366 15#include "macro.h"
07630cea 16#include "string-util.h"
958b66ea
LP
17
18bool 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
33char* gethostname_malloc(void) {
34 struct utsname u;
35
953d28cc
ZJS
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. */
8e1ad1ea 39
958b66ea
LP
40 assert_se(uname(&u) >= 0);
41
42 if (isempty(u.nodename) || streq(u.nodename, "(none)"))
d91e8e1b 43 return strdup(FALLBACK_HOSTNAME);
958b66ea
LP
44
45 return strdup(u.nodename);
46}
47
8e1ad1ea
LP
48int 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
958b66ea
LP
73static bool hostname_valid_char(char c) {
74 return
75 (c >= 'a' && c <= 'z') ||
76 (c >= 'A' && c <= 'Z') ||
77 (c >= '0' && c <= '9') ||
4c701096 78 IN_SET(c, '-', '_', '.');
958b66ea
LP
79}
80
8fb49443 81/**
b59abc4d 82 * Check if s looks like a valid host name or FQDN. This does not do
8fb49443
ZJS
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
b59abc4d
LP
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
06d91ad7 89 * dns_name_is_valid().
8fb49443 90 */
b59abc4d
LP
91bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
92 unsigned n_dots = 0;
958b66ea
LP
93 const char *p;
94 bool dot;
95
96 if (isempty(s))
97 return false;
98
8fb49443 99 /* Doesn't accept empty hostnames, hostnames with
958b66ea
LP
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;
313cefa1 110 n_dots++;
958b66ea
LP
111 } else {
112 if (!hostname_valid_char(*p))
113 return false;
114
115 dot = false;
116 }
117 }
118
b59abc4d 119 if (dot && (n_dots < 2 || !allow_trailing_dot))
958b66ea
LP
120 return false;
121
b59abc4d
LP
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 */
958b66ea
LP
125 return false;
126
127 return true;
128}
129
ae691c1d 130char* hostname_cleanup(char *s) {
958b66ea
LP
131 char *p, *d;
132 bool dot;
133
134 assert(s);
135
46e1a227
TB
136 strshorten(s, HOST_NAME_MAX);
137
958b66ea
LP
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)) {
ae691c1d 146 *(d++) = *p;
958b66ea
LP
147 dot = false;
148 }
958b66ea
LP
149 }
150
151 if (dot && d > s)
152 d[-1] = 0;
153 else
154 *d = 0;
155
958b66ea
LP
156 return s;
157}
158
159bool is_localhost(const char *hostname) {
160 assert(hostname);
161
162 /* This tries to identify local host and domain names
63003524 163 * described in RFC6761 plus the redhatism of localdomain */
958b66ea 164
90365b04
ZJS
165 return strcaseeq(hostname, "localhost") ||
166 strcaseeq(hostname, "localhost.") ||
63003524
DH
167 strcaseeq(hostname, "localhost.localdomain") ||
168 strcaseeq(hostname, "localhost.localdomain.") ||
90365b04
ZJS
169 endswith_no_case(hostname, ".localhost") ||
170 endswith_no_case(hostname, ".localhost.") ||
63003524
DH
171 endswith_no_case(hostname, ".localhost.localdomain") ||
172 endswith_no_case(hostname, ".localhost.localdomain.");
958b66ea
LP
173}
174
46a5e0e7
LP
175bool 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
5248e7e1
ZJS
182 strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.")
183#if ENABLE_COMPAT_GATEWAY_HOSTNAME
184 || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.")
185#endif
186 ;
46a5e0e7
LP
187}
188
958b66ea
LP
189int 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}
139e5336 205
2de2abad
LB
206int 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
f35cb39e
LP
238int read_etc_hostname_stream(FILE *f, char **ret) {
239 int r;
139e5336 240
f35cb39e
LP
241 assert(f);
242 assert(ret);
139e5336 243
f35cb39e
LP
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;
139e5336 253
f35cb39e
LP
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)
139e5336 267 return -ENOMEM;
f35cb39e
LP
268
269 *ret = copy;
270 return 0;
139e5336
MP
271 }
272 }
f35cb39e 273}
139e5336 274
f35cb39e
LP
275int 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);
139e5336 288
139e5336 289}