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