]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/hostname-util.c
Merge pull request #7388 from keszybz/doc-tweak
[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(FALLBACK_HOSTNAME);
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 IN_SET(c, '-', '_', '.');
94 }
95
96 /**
97 * Check if s looks like a valid host name or FQDN. This does not do
98 * full DNS validation, but only checks if the name is composed of
99 * allowed characters and the length is not above the maximum allowed
100 * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
101 * allow_trailing_dot is true and at least two components are present
102 * in the name. Note that due to the restricted charset and length
103 * this call is substantially more conservative than
104 * dns_name_is_valid().
105 */
106 bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
107 unsigned n_dots = 0;
108 const char *p;
109 bool dot;
110
111 if (isempty(s))
112 return false;
113
114 /* Doesn't accept empty hostnames, hostnames with
115 * leading dots, and hostnames with multiple dots in a
116 * sequence. Also ensures that the length stays below
117 * HOST_NAME_MAX. */
118
119 for (p = s, dot = true; *p; p++) {
120 if (*p == '.') {
121 if (dot)
122 return false;
123
124 dot = true;
125 n_dots++;
126 } else {
127 if (!hostname_valid_char(*p))
128 return false;
129
130 dot = false;
131 }
132 }
133
134 if (dot && (n_dots < 2 || !allow_trailing_dot))
135 return false;
136
137 if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
138 * Linux, but DNS allows domain names
139 * up to 255 characters */
140 return false;
141
142 return true;
143 }
144
145 char* hostname_cleanup(char *s) {
146 char *p, *d;
147 bool dot;
148
149 assert(s);
150
151 strshorten(s, HOST_NAME_MAX);
152
153 for (p = s, d = s, dot = true; *p; p++) {
154 if (*p == '.') {
155 if (dot)
156 continue;
157
158 *(d++) = '.';
159 dot = true;
160 } else if (hostname_valid_char(*p)) {
161 *(d++) = *p;
162 dot = false;
163 }
164 }
165
166 if (dot && d > s)
167 d[-1] = 0;
168 else
169 *d = 0;
170
171 return s;
172 }
173
174 bool is_localhost(const char *hostname) {
175 assert(hostname);
176
177 /* This tries to identify local host and domain names
178 * described in RFC6761 plus the redhatism of localdomain */
179
180 return strcaseeq(hostname, "localhost") ||
181 strcaseeq(hostname, "localhost.") ||
182 strcaseeq(hostname, "localhost.localdomain") ||
183 strcaseeq(hostname, "localhost.localdomain.") ||
184 endswith_no_case(hostname, ".localhost") ||
185 endswith_no_case(hostname, ".localhost.") ||
186 endswith_no_case(hostname, ".localhost.localdomain") ||
187 endswith_no_case(hostname, ".localhost.localdomain.");
188 }
189
190 bool is_gateway_hostname(const char *hostname) {
191 assert(hostname);
192
193 /* This tries to identify the valid syntaxes for the our
194 * synthetic "gateway" host. */
195
196 return
197 strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.")
198 #if ENABLE_COMPAT_GATEWAY_HOSTNAME
199 || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.")
200 #endif
201 ;
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 (!IN_SET(l[0], '\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 }