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