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