]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/hostname-util.c
Merge pull request #2966 from evverx/udev-test-dont-rely-on-underlying-fs
[thirdparty/systemd.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 bool hostname_is_set(void) {
34 struct utsname u;
35
36 assert_se(uname(&u) >= 0);
37
38 if (isempty(u.nodename))
39 return false;
40
41 /* This is the built-in kernel default host name */
42 if (streq(u.nodename, "(none)"))
43 return false;
44
45 return true;
46 }
47
48 char* gethostname_malloc(void) {
49 struct utsname u;
50
51 /* This call tries to return something useful, either the actual hostname
52 * or it makes something up. The only reason it might fail is OOM.
53 * It might even return "localhost" if that's set. */
54
55 assert_se(uname(&u) >= 0);
56
57 if (isempty(u.nodename) || streq(u.nodename, "(none)"))
58 return strdup(u.sysname);
59
60 return strdup(u.nodename);
61 }
62
63 int gethostname_strict(char **ret) {
64 struct utsname u;
65 char *k;
66
67 /* This call will rather fail than make up a name. It will not return "localhost" either. */
68
69 assert_se(uname(&u) >= 0);
70
71 if (isempty(u.nodename))
72 return -ENXIO;
73
74 if (streq(u.nodename, "(none)"))
75 return -ENXIO;
76
77 if (is_localhost(u.nodename))
78 return -ENXIO;
79
80 k = strdup(u.nodename);
81 if (!k)
82 return -ENOMEM;
83
84 *ret = k;
85 return 0;
86 }
87
88 static bool hostname_valid_char(char c) {
89 return
90 (c >= 'a' && c <= 'z') ||
91 (c >= 'A' && c <= 'Z') ||
92 (c >= '0' && c <= '9') ||
93 c == '-' ||
94 c == '_' ||
95 c == '.';
96 }
97
98 /**
99 * Check if s looks like a valid host name or FQDN. This does not do
100 * full DNS validation, but only checks if the name is composed of
101 * allowed characters and the length is not above the maximum allowed
102 * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
103 * allow_trailing_dot is true and at least two components are present
104 * in the name. Note that due to the restricted charset and length
105 * this call is substantially more conservative than
106 * dns_name_is_valid().
107 */
108 bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
109 unsigned n_dots = 0;
110 const char *p;
111 bool dot;
112
113 if (isempty(s))
114 return false;
115
116 /* Doesn't accept empty hostnames, hostnames with
117 * leading dots, and hostnames with multiple dots in a
118 * sequence. Also ensures that the length stays below
119 * HOST_NAME_MAX. */
120
121 for (p = s, dot = true; *p; p++) {
122 if (*p == '.') {
123 if (dot)
124 return false;
125
126 dot = true;
127 n_dots++;
128 } else {
129 if (!hostname_valid_char(*p))
130 return false;
131
132 dot = false;
133 }
134 }
135
136 if (dot && (n_dots < 2 || !allow_trailing_dot))
137 return false;
138
139 if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
140 * Linux, but DNS allows domain names
141 * up to 255 characters */
142 return false;
143
144 return true;
145 }
146
147 char* hostname_cleanup(char *s) {
148 char *p, *d;
149 bool dot;
150
151 assert(s);
152
153 strshorten(s, HOST_NAME_MAX);
154
155 for (p = s, d = s, dot = true; *p; p++) {
156 if (*p == '.') {
157 if (dot)
158 continue;
159
160 *(d++) = '.';
161 dot = true;
162 } else if (hostname_valid_char(*p)) {
163 *(d++) = *p;
164 dot = false;
165 }
166
167 }
168
169 if (dot && d > s)
170 d[-1] = 0;
171 else
172 *d = 0;
173
174 return s;
175 }
176
177 bool is_localhost(const char *hostname) {
178 assert(hostname);
179
180 /* This tries to identify local host and domain names
181 * described in RFC6761 plus the redhatism of localdomain */
182
183 return strcaseeq(hostname, "localhost") ||
184 strcaseeq(hostname, "localhost.") ||
185 strcaseeq(hostname, "localhost.localdomain") ||
186 strcaseeq(hostname, "localhost.localdomain.") ||
187 endswith_no_case(hostname, ".localhost") ||
188 endswith_no_case(hostname, ".localhost.") ||
189 endswith_no_case(hostname, ".localhost.localdomain") ||
190 endswith_no_case(hostname, ".localhost.localdomain.");
191 }
192
193 bool is_gateway_hostname(const char *hostname) {
194 assert(hostname);
195
196 /* This tries to identify the valid syntaxes for the our
197 * synthetic "gateway" host. */
198
199 return
200 strcaseeq(hostname, "gateway") ||
201 strcaseeq(hostname, "gateway.");
202 }
203
204 int sethostname_idempotent(const char *s) {
205 char buf[HOST_NAME_MAX + 1] = {};
206
207 assert(s);
208
209 if (gethostname(buf, sizeof(buf)) < 0)
210 return -errno;
211
212 if (streq(buf, s))
213 return 0;
214
215 if (sethostname(s, strlen(s)) < 0)
216 return -errno;
217
218 return 1;
219 }
220
221 int read_hostname_config(const char *path, char **hostname) {
222 _cleanup_fclose_ FILE *f = NULL;
223 char l[LINE_MAX];
224 char *name = NULL;
225
226 assert(path);
227 assert(hostname);
228
229 f = fopen(path, "re");
230 if (!f)
231 return -errno;
232
233 /* may have comments, ignore them */
234 FOREACH_LINE(l, f, return -errno) {
235 truncate_nl(l);
236 if (l[0] != '\0' && l[0] != '#') {
237 /* found line with value */
238 name = hostname_cleanup(l);
239 name = strdup(name);
240 if (!name)
241 return -ENOMEM;
242 break;
243 }
244 }
245
246 if (!name)
247 /* no non-empty line found */
248 return -ENOENT;
249
250 *hostname = name;
251 return 0;
252 }