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