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