]> git.proxmox.com Git - systemd.git/blame - src/basic/hostname-util.c
Merge tag 'upstream/229'
[systemd.git] / src / basic / hostname-util.c
CommitLineData
e3bff60a
MP
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
4c89c718
MP
20#include <bits/local_lim.h>
21#include <errno.h>
22#include <limits.h>
23#include <stdio.h>
24#include <string.h>
db2df898 25#include <sys/utsname.h>
4c89c718 26#include <unistd.h>
e3bff60a 27
db2df898
MP
28#include "fd-util.h"
29#include "fileio.h"
e3bff60a 30#include "hostname-util.h"
4c89c718 31#include "macro.h"
db2df898 32#include "string-util.h"
e3bff60a
MP
33
34bool 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
49char* gethostname_malloc(void) {
50 struct utsname u;
51
52 assert_se(uname(&u) >= 0);
53
54 if (isempty(u.nodename) || streq(u.nodename, "(none)"))
55 return strdup(u.sysname);
56
57 return strdup(u.nodename);
58}
59
60static bool hostname_valid_char(char c) {
61 return
62 (c >= 'a' && c <= 'z') ||
63 (c >= 'A' && c <= 'Z') ||
64 (c >= '0' && c <= '9') ||
65 c == '-' ||
66 c == '_' ||
67 c == '.';
68}
69
13d276d0
MP
70/**
71 * Check if s looks like a valid host name or FQDN. This does not do
72 * full DNS validation, but only checks if the name is composed of
73 * allowed characters and the length is not above the maximum allowed
74 * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
75 * allow_trailing_dot is true and at least two components are present
76 * in the name. Note that due to the restricted charset and length
77 * this call is substantially more conservative than
4c89c718 78 * dns_name_is_valid().
13d276d0
MP
79 */
80bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
81 unsigned n_dots = 0;
e3bff60a
MP
82 const char *p;
83 bool dot;
84
85 if (isempty(s))
86 return false;
87
13d276d0 88 /* Doesn't accept empty hostnames, hostnames with
e3bff60a
MP
89 * leading dots, and hostnames with multiple dots in a
90 * sequence. Also ensures that the length stays below
91 * HOST_NAME_MAX. */
92
93 for (p = s, dot = true; *p; p++) {
94 if (*p == '.') {
95 if (dot)
96 return false;
97
98 dot = true;
13d276d0 99 n_dots ++;
e3bff60a
MP
100 } else {
101 if (!hostname_valid_char(*p))
102 return false;
103
104 dot = false;
105 }
106 }
107
13d276d0 108 if (dot && (n_dots < 2 || !allow_trailing_dot))
e3bff60a
MP
109 return false;
110
13d276d0
MP
111 if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
112 * Linux, but DNS allows domain names
113 * up to 255 characters */
e3bff60a
MP
114 return false;
115
116 return true;
117}
118
13d276d0 119char* hostname_cleanup(char *s) {
e3bff60a
MP
120 char *p, *d;
121 bool dot;
122
123 assert(s);
124
125 for (p = s, d = s, dot = true; *p; p++) {
126 if (*p == '.') {
127 if (dot)
128 continue;
129
130 *(d++) = '.';
131 dot = true;
132 } else if (hostname_valid_char(*p)) {
13d276d0 133 *(d++) = *p;
e3bff60a
MP
134 dot = false;
135 }
136
137 }
138
139 if (dot && d > s)
140 d[-1] = 0;
141 else
142 *d = 0;
143
144 strshorten(s, HOST_NAME_MAX);
145
146 return s;
147}
148
149bool is_localhost(const char *hostname) {
150 assert(hostname);
151
152 /* This tries to identify local host and domain names
153 * described in RFC6761 plus the redhatism of .localdomain */
154
13d276d0
MP
155 return strcaseeq(hostname, "localhost") ||
156 strcaseeq(hostname, "localhost.") ||
157 strcaseeq(hostname, "localdomain.") ||
158 strcaseeq(hostname, "localdomain") ||
159 endswith_no_case(hostname, ".localhost") ||
160 endswith_no_case(hostname, ".localhost.") ||
161 endswith_no_case(hostname, ".localdomain") ||
162 endswith_no_case(hostname, ".localdomain.");
163}
164
165bool is_gateway_hostname(const char *hostname) {
166 assert(hostname);
167
168 /* This tries to identify the valid syntaxes for the our
169 * synthetic "gateway" host. */
170
171 return
172 strcaseeq(hostname, "gateway") ||
173 strcaseeq(hostname, "gateway.");
e3bff60a
MP
174}
175
176int sethostname_idempotent(const char *s) {
177 char buf[HOST_NAME_MAX + 1] = {};
178
179 assert(s);
180
181 if (gethostname(buf, sizeof(buf)) < 0)
182 return -errno;
183
184 if (streq(buf, s))
185 return 0;
186
187 if (sethostname(s, strlen(s)) < 0)
188 return -errno;
189
190 return 1;
191}
192
193int read_hostname_config(const char *path, char **hostname) {
194 _cleanup_fclose_ FILE *f = NULL;
195 char l[LINE_MAX];
196 char *name = NULL;
197
198 assert(path);
199 assert(hostname);
200
201 f = fopen(path, "re");
202 if (!f)
203 return -errno;
204
205 /* may have comments, ignore them */
206 FOREACH_LINE(l, f, return -errno) {
207 truncate_nl(l);
208 if (l[0] != '\0' && l[0] != '#') {
209 /* found line with value */
13d276d0 210 name = hostname_cleanup(l);
e3bff60a
MP
211 name = strdup(name);
212 if (!name)
213 return -ENOMEM;
214 break;
215 }
216 }
217
218 if (!name)
219 /* no non-empty line found */
220 return -ENOENT;
221
222 *hostname = name;
223 return 0;
224}