]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/hostname-util.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.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 "fd-util.h"
29 #include "fileio.h"
30 #include "hostname-util.h"
31 #include "macro.h"
32 #include "string-util.h"
33
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(FALLBACK_HOSTNAME);
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
89 static bool hostname_valid_char(char c) {
90 return
91 (c >= 'a' && c <= 'z') ||
92 (c >= 'A' && c <= 'Z') ||
93 (c >= '0' && c <= '9') ||
94 IN_SET(c, '-', '_', '.');
95 }
96
97 /**
98 * Check if s looks like a valid host name or FQDN. This does not do
99 * full DNS validation, but only checks if the name is composed of
100 * allowed characters and the length is not above the maximum allowed
101 * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
102 * allow_trailing_dot is true and at least two components are present
103 * in the name. Note that due to the restricted charset and length
104 * this call is substantially more conservative than
105 * dns_name_is_valid().
106 */
107 bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
108 unsigned n_dots = 0;
109 const char *p;
110 bool dot;
111
112 if (isempty(s))
113 return false;
114
115 /* Doesn't accept empty hostnames, hostnames with
116 * leading dots, and hostnames with multiple dots in a
117 * sequence. Also ensures that the length stays below
118 * HOST_NAME_MAX. */
119
120 for (p = s, dot = true; *p; p++) {
121 if (*p == '.') {
122 if (dot)
123 return false;
124
125 dot = true;
126 n_dots++;
127 } else {
128 if (!hostname_valid_char(*p))
129 return false;
130
131 dot = false;
132 }
133 }
134
135 if (dot && (n_dots < 2 || !allow_trailing_dot))
136 return false;
137
138 if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
139 * Linux, but DNS allows domain names
140 * up to 255 characters */
141 return false;
142
143 return true;
144 }
145
146 char* hostname_cleanup(char *s) {
147 char *p, *d;
148 bool dot;
149
150 assert(s);
151
152 strshorten(s, HOST_NAME_MAX);
153
154 for (p = s, d = s, dot = true; *p; p++) {
155 if (*p == '.') {
156 if (dot)
157 continue;
158
159 *(d++) = '.';
160 dot = true;
161 } else if (hostname_valid_char(*p)) {
162 *(d++) = *p;
163 dot = false;
164 }
165 }
166
167 if (dot && d > s)
168 d[-1] = 0;
169 else
170 *d = 0;
171
172 return s;
173 }
174
175 bool is_localhost(const char *hostname) {
176 assert(hostname);
177
178 /* This tries to identify local host and domain names
179 * described in RFC6761 plus the redhatism of localdomain */
180
181 return strcaseeq(hostname, "localhost") ||
182 strcaseeq(hostname, "localhost.") ||
183 strcaseeq(hostname, "localhost.localdomain") ||
184 strcaseeq(hostname, "localhost.localdomain.") ||
185 endswith_no_case(hostname, ".localhost") ||
186 endswith_no_case(hostname, ".localhost.") ||
187 endswith_no_case(hostname, ".localhost.localdomain") ||
188 endswith_no_case(hostname, ".localhost.localdomain.");
189 }
190
191 bool is_gateway_hostname(const char *hostname) {
192 assert(hostname);
193
194 /* This tries to identify the valid syntaxes for the our
195 * synthetic "gateway" host. */
196
197 return
198 strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.")
199 #if ENABLE_COMPAT_GATEWAY_HOSTNAME
200 || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.")
201 #endif
202 ;
203 }
204
205 int sethostname_idempotent(const char *s) {
206 char buf[HOST_NAME_MAX + 1] = {};
207
208 assert(s);
209
210 if (gethostname(buf, sizeof(buf)) < 0)
211 return -errno;
212
213 if (streq(buf, s))
214 return 0;
215
216 if (sethostname(s, strlen(s)) < 0)
217 return -errno;
218
219 return 1;
220 }
221
222 int read_hostname_config(const char *path, char **hostname) {
223 _cleanup_fclose_ FILE *f = NULL;
224 char l[LINE_MAX];
225 char *name = NULL;
226
227 assert(path);
228 assert(hostname);
229
230 f = fopen(path, "re");
231 if (!f)
232 return -errno;
233
234 /* may have comments, ignore them */
235 FOREACH_LINE(l, f, return -errno) {
236 truncate_nl(l);
237 if (!IN_SET(l[0], '\0', '#')) {
238 /* found line with value */
239 name = hostname_cleanup(l);
240 name = strdup(name);
241 if (!name)
242 return -ENOMEM;
243 break;
244 }
245 }
246
247 if (!name)
248 /* no non-empty line found */
249 return -ENOENT;
250
251 *hostname = name;
252 return 0;
253 }