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