1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
24 #include "alloc-util.h"
25 #include "dns-domain.h"
27 #include "fileio-label.h"
29 #include "ordered-set.h"
30 #include "resolved-conf.h"
31 #include "resolved-resolv-conf.h"
32 #include "string-util.h"
35 int manager_read_resolv_conf(Manager
*m
) {
36 _cleanup_fclose_
FILE *f
= NULL
;
44 /* Reads the system /etc/resolv.conf, if it exists and is not
45 * symlinked to our own resolv.conf instance */
47 if (!m
->read_resolv_conf
)
50 r
= stat("/etc/resolv.conf", &st
);
55 r
= log_warning_errno(errno
, "Failed to stat /etc/resolv.conf: %m");
59 /* Have we already seen the file? */
60 t
= timespec_load(&st
.st_mtim
);
61 if (t
== m
->resolv_conf_mtime
)
64 m
->resolv_conf_mtime
= t
;
66 /* Is it symlinked to our own file? */
67 if (stat("/run/systemd/resolve/resolv.conf", &own
) >= 0 &&
68 st
.st_dev
== own
.st_dev
&&
69 st
.st_ino
== own
.st_ino
) {
74 f
= fopen("/etc/resolv.conf", "re");
79 r
= log_warning_errno(errno
, "Failed to open /etc/resolv.conf: %m");
83 if (fstat(fileno(f
), &st
) < 0) {
84 r
= log_error_errno(errno
, "Failed to stat open file: %m");
88 dns_server_mark_all(m
->dns_servers
);
89 dns_search_domain_mark_all(m
->search_domains
);
91 FOREACH_LINE(line
, f
, r
= -errno
; goto clear
) {
96 if (*l
== '#' || *l
== ';')
99 a
= first_word(l
, "nameserver");
101 r
= manager_add_dns_server_by_string(m
, DNS_SERVER_SYSTEM
, a
);
103 log_warning_errno(r
, "Failed to parse DNS server address '%s', ignoring.", a
);
108 a
= first_word(l
, "domain");
109 if (!a
) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */
110 a
= first_word(l
, "search");
112 r
= manager_parse_search_domains_and_warn(m
, a
);
114 log_warning_errno(r
, "Failed to parse search domain string '%s', ignoring.", a
);
118 /* Flush out all servers and search domains that are still
119 * marked. Those are then ones that didn't appear in the new
120 * /etc/resolv.conf */
121 dns_server_unlink_marked(m
->dns_servers
);
122 dns_search_domain_unlink_marked(m
->search_domains
);
124 /* Whenever /etc/resolv.conf changes, start using the first
125 * DNS server of it. This is useful to deal with broken
126 * network managing implementations (like NetworkManager),
127 * that when connecting to a VPN place both the VPN DNS
128 * servers and the local ones in /etc/resolv.conf. Without
129 * resetting the DNS server to use back to the first entry we
130 * will continue to use the local one thus being unable to
131 * resolve VPN domains. */
132 manager_set_dns_server(m
, m
->dns_servers
);
137 dns_server_unlink_all(m
->dns_servers
);
138 dns_search_domain_unlink_all(m
->search_domains
);
142 static void write_resolv_conf_server(DnsServer
*s
, FILE *f
, unsigned *count
) {
143 _cleanup_free_
char *t
= NULL
;
150 r
= in_addr_to_string(s
->family
, &s
->address
, &t
);
152 log_warning_errno(r
, "Invalid DNS address. Ignoring: %m");
157 fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f
);
160 fprintf(f
, "nameserver %s\n", t
);
163 static void write_resolv_conf_search(
173 if (*count
>= MAXDNSRCH
||
174 *length
+ strlen(domain
) > 256) {
175 if (*count
== MAXDNSRCH
)
176 fputs(" # Too many search domains configured, remaining ones ignored.", f
);
178 fputs(" # Total length of all search domains is too long, remaining ones ignored.", f
);
183 (*length
) += strlen(domain
);
190 static int write_resolv_conf_contents(FILE *f
, OrderedSet
*dns
, OrderedSet
*domains
) {
193 fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
194 "# Third party programs must not access this file directly, but\n"
195 "# only through the symlink at /etc/resolv.conf. To manage\n"
196 "# resolv.conf(5) in a different way, replace the symlink by a\n"
197 "# static file or a different symlink.\n\n", f
);
199 if (ordered_set_isempty(dns
))
200 fputs("# No DNS servers known.\n", f
);
205 ORDERED_SET_FOREACH(s
, dns
, i
)
206 write_resolv_conf_server(s
, f
, &count
);
209 if (!ordered_set_isempty(domains
)) {
210 unsigned length
= 0, count
= 0;
214 ORDERED_SET_FOREACH(domain
, domains
, i
)
215 write_resolv_conf_search(domain
, f
, &count
, &length
);
219 return fflush_and_check(f
);
222 int manager_write_resolv_conf(Manager
*m
) {
224 #define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
226 _cleanup_ordered_set_free_ OrderedSet
*dns
= NULL
, *domains
= NULL
;
227 _cleanup_free_
char *temp_path
= NULL
;
228 _cleanup_fclose_
FILE *f
= NULL
;
233 /* Read the system /etc/resolv.conf first */
234 manager_read_resolv_conf(m
);
236 /* Add the full list to a set, to filter out duplicates */
237 r
= manager_compile_dns_servers(m
, &dns
);
241 r
= manager_compile_search_domains(m
, &domains
);
245 r
= fopen_temporary_label(PRIVATE_RESOLV_CONF
, PRIVATE_RESOLV_CONF
, &f
, &temp_path
);
249 fchmod(fileno(f
), 0644);
251 r
= write_resolv_conf_contents(f
, dns
, domains
);
255 if (rename(temp_path
, PRIVATE_RESOLV_CONF
) < 0) {
263 (void) unlink(PRIVATE_RESOLV_CONF
);
264 (void) unlink(temp_path
);