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