]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-resolv-conf.c
resolved: add code to map DNSSEC digest types to strings and back
[thirdparty/systemd.git] / src / resolve / resolved-resolv-conf.c
CommitLineData
f8dc7e34
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Tom Gundersen <teg@jklm.no>
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
22#include <resolv.h>
23
24#include "alloc-util.h"
25#include "dns-domain.h"
26#include "fd-util.h"
27#include "fileio-label.h"
28#include "fileio.h"
29#include "ordered-set.h"
30#include "resolved-conf.h"
31#include "resolved-resolv-conf.h"
32#include "string-util.h"
33#include "strv.h"
34
35int manager_read_resolv_conf(Manager *m) {
36 _cleanup_fclose_ FILE *f = NULL;
37 struct stat st, own;
38 char line[LINE_MAX];
f8dc7e34
LP
39 usec_t t;
40 int r;
41
42 assert(m);
43
44 /* Reads the system /etc/resolv.conf, if it exists and is not
45 * symlinked to our own resolv.conf instance */
46
47 if (!m->read_resolv_conf)
48 return 0;
49
50 r = stat("/etc/resolv.conf", &st);
51 if (r < 0) {
52 if (errno == ENOENT)
bf7fabd6
LP
53 return 0;
54
55 r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m");
f8dc7e34
LP
56 goto clear;
57 }
58
59 /* Have we already seen the file? */
60 t = timespec_load(&st.st_mtim);
61 if (t == m->resolv_conf_mtime)
62 return 0;
63
f8dc7e34
LP
64 /* Is it symlinked to our own file? */
65 if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
66 st.st_dev == own.st_dev &&
bf7fabd6
LP
67 st.st_ino == own.st_ino)
68 return 0;
f8dc7e34
LP
69
70 f = fopen("/etc/resolv.conf", "re");
71 if (!f) {
72 if (errno == ENOENT)
bf7fabd6
LP
73 return 0;
74
75 r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
f8dc7e34
LP
76 goto clear;
77 }
78
79 if (fstat(fileno(f), &st) < 0) {
80 r = log_error_errno(errno, "Failed to stat open file: %m");
81 goto clear;
82 }
83
4b95f179 84 dns_server_mark_all(m->dns_servers);
a51c1048 85 dns_search_domain_mark_all(m->search_domains);
f8dc7e34
LP
86
87 FOREACH_LINE(line, f, r = -errno; goto clear) {
f8dc7e34
LP
88 const char *a;
89 char *l;
90
f8dc7e34
LP
91 l = strstrip(line);
92 if (*l == '#' || *l == ';')
93 continue;
94
95 a = first_word(l, "nameserver");
96 if (a) {
97 r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a);
98 if (r < 0)
99 log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
100
101 continue;
102 }
a51c1048
LP
103
104 a = first_word(l, "domain");
105 if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */
106 a = first_word(l, "search");
107 if (a) {
108 r = manager_parse_search_domains_and_warn(m, a);
109 if (r < 0)
110 log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a);
111 }
f8dc7e34
LP
112 }
113
bf7fabd6
LP
114 m->resolv_conf_mtime = t;
115
a51c1048
LP
116 /* Flush out all servers and search domains that are still
117 * marked. Those are then ones that didn't appear in the new
118 * /etc/resolv.conf */
4b95f179 119 dns_server_unlink_marked(m->dns_servers);
a51c1048 120 dns_search_domain_unlink_marked(m->search_domains);
f8dc7e34
LP
121
122 /* Whenever /etc/resolv.conf changes, start using the first
123 * DNS server of it. This is useful to deal with broken
124 * network managing implementations (like NetworkManager),
125 * that when connecting to a VPN place both the VPN DNS
126 * servers and the local ones in /etc/resolv.conf. Without
127 * resetting the DNS server to use back to the first entry we
128 * will continue to use the local one thus being unable to
129 * resolve VPN domains. */
130 manager_set_dns_server(m, m->dns_servers);
131
452b4e32
LP
132 /* Unconditionally flush the cache when /etc/resolv.conf is
133 * modified, even if the data it contained was completely
134 * identical to the previous version we used. We do this
135 * because altering /etc/resolv.conf is typically done when
136 * the network configuration changes, and that should be
137 * enough to flush the global unicast DNS cache. */
138 if (m->unicast_scope)
139 dns_cache_flush(&m->unicast_scope->cache);
140
f8dc7e34
LP
141 return 0;
142
143clear:
4b95f179 144 dns_server_unlink_all(m->dns_servers);
a51c1048 145 dns_search_domain_unlink_all(m->search_domains);
f8dc7e34
LP
146 return r;
147}
148
149static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
150 _cleanup_free_ char *t = NULL;
151 int r;
152
153 assert(s);
154 assert(f);
155 assert(count);
156
157 r = in_addr_to_string(s->family, &s->address, &t);
158 if (r < 0) {
159 log_warning_errno(r, "Invalid DNS address. Ignoring: %m");
160 return;
161 }
162
163 if (*count == MAXNS)
164 fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
165 (*count) ++;
166
167 fprintf(f, "nameserver %s\n", t);
168}
169
170static void write_resolv_conf_search(
171 const char *domain,
172 FILE *f,
173 unsigned *count,
174 unsigned *length) {
175
176 assert(domain);
177 assert(f);
178 assert(length);
179
180 if (*count >= MAXDNSRCH ||
181 *length + strlen(domain) > 256) {
182 if (*count == MAXDNSRCH)
183 fputs(" # Too many search domains configured, remaining ones ignored.", f);
184 if (*length <= 256)
185 fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
186
187 return;
188 }
189
190 (*length) += strlen(domain);
191 (*count) ++;
192
193 fputc(' ', f);
194 fputs(domain, f);
195}
196
197static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
198 Iterator i;
199
200 fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
201 "# Third party programs must not access this file directly, but\n"
202 "# only through the symlink at /etc/resolv.conf. To manage\n"
203 "# resolv.conf(5) in a different way, replace the symlink by a\n"
204 "# static file or a different symlink.\n\n", f);
205
206 if (ordered_set_isempty(dns))
207 fputs("# No DNS servers known.\n", f);
208 else {
209 unsigned count = 0;
210 DnsServer *s;
211
212 ORDERED_SET_FOREACH(s, dns, i)
213 write_resolv_conf_server(s, f, &count);
214 }
215
216 if (!ordered_set_isempty(domains)) {
217 unsigned length = 0, count = 0;
218 char *domain;
219
220 fputs("search", f);
221 ORDERED_SET_FOREACH(domain, domains, i)
222 write_resolv_conf_search(domain, f, &count, &length);
223 fputs("\n", f);
224 }
225
226 return fflush_and_check(f);
227}
228
229int manager_write_resolv_conf(Manager *m) {
9176a57c
LP
230
231 #define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
232
233 _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
f8dc7e34
LP
234 _cleanup_free_ char *temp_path = NULL;
235 _cleanup_fclose_ FILE *f = NULL;
f8dc7e34
LP
236 int r;
237
238 assert(m);
239
240 /* Read the system /etc/resolv.conf first */
241 manager_read_resolv_conf(m);
242
243 /* Add the full list to a set, to filter out duplicates */
9176a57c
LP
244 r = manager_compile_dns_servers(m, &dns);
245 if (r < 0)
246 return r;
f8dc7e34 247
9176a57c
LP
248 r = manager_compile_search_domains(m, &domains);
249 if (r < 0)
250 return r;
f8dc7e34 251
9176a57c 252 r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path);
f8dc7e34
LP
253 if (r < 0)
254 return r;
255
256 fchmod(fileno(f), 0644);
257
258 r = write_resolv_conf_contents(f, dns, domains);
259 if (r < 0)
260 goto fail;
261
9176a57c 262 if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) {
f8dc7e34
LP
263 r = -errno;
264 goto fail;
265 }
266
267 return 0;
268
269fail:
9176a57c 270 (void) unlink(PRIVATE_RESOLV_CONF);
f8dc7e34
LP
271 (void) unlink(temp_path);
272 return r;
273}