chiark / gitweb /
ae173991da2ca11cbb18102ea5fdd7650d3d690f
[elogind.git] / src / resolve / resolved-manager.c
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 <arpa/inet.h>
23 #include <resolv.h>
24 #include <linux/if.h>
25
26 #include "resolved.h"
27 #include "event-util.h"
28 #include "network-util.h"
29 #include "sd-dhcp-lease.h"
30 #include "dhcp-lease-internal.h"
31 #include "network-internal.h"
32 #include "conf-parser.h"
33 #include "mkdir.h"
34
35 static int set_fallback_dns(Manager *m, const char *string) {
36         char *word, *state;
37         size_t length;
38         int r;
39
40         assert(m);
41         assert(string);
42
43         FOREACH_WORD_QUOTED(word, length, string, state) {
44                 _cleanup_free_ Address *address = NULL;
45                 Address *tail;
46                 _cleanup_free_ char *addrstr = NULL;
47
48                 address = new0(Address, 1);
49                 if (!address)
50                         return -ENOMEM;
51
52                 addrstr = strndup(word, length);
53                 if (!addrstr)
54                         return -ENOMEM;
55
56                 r = net_parse_inaddr(addrstr, &address->family, &address->in_addr);
57                 if (r < 0) {
58                         log_debug("Ignoring invalid DNS address '%s'", addrstr);
59                         continue;
60                 }
61
62                 LIST_FIND_TAIL(addresses, m->fallback_dns, tail);
63                 LIST_INSERT_AFTER(addresses, m->fallback_dns, tail, address);
64                 address = NULL;
65         }
66
67         return 0;
68 }
69
70 int config_parse_dnsv(
71                 const char *unit,
72                 const char *filename,
73                 unsigned line,
74                 const char *section,
75                 unsigned section_line,
76                 const char *lvalue,
77                 int ltype,
78                 const char *rvalue,
79                 void *data,
80                 void *userdata) {
81
82         Manager *m = userdata;
83         Address *address;
84
85         assert(filename);
86         assert(lvalue);
87         assert(rvalue);
88         assert(m);
89
90         while ((address = m->fallback_dns)) {
91                 LIST_REMOVE(addresses, m->fallback_dns, address);
92                 free(address);
93         }
94
95         set_fallback_dns(m, rvalue);
96
97         return 0;
98 }
99
100 static int manager_parse_config_file(Manager *m) {
101         static const char fn[] = "/etc/systemd/resolved.conf";
102         _cleanup_fclose_ FILE *f = NULL;
103         int r;
104
105         assert(m);
106
107         f = fopen(fn, "re");
108         if (!f) {
109                 if (errno == ENOENT)
110                         return 0;
111
112                 log_warning("Failed to open configuration file %s: %m", fn);
113                 return -errno;
114         }
115
116         r = config_parse(NULL, fn, f, "Resolve\0", config_item_perf_lookup,
117                          (void*) resolved_gperf_lookup, false, false, m);
118         if (r < 0)
119                 log_warning("Failed to parse configuration file: %s", strerror(-r));
120
121         return r;
122 }
123
124 int manager_new(Manager **ret) {
125         _cleanup_manager_free_ Manager *m = NULL;
126         int r;
127
128         m = new0(Manager, 1);
129         if (!m)
130                 return -ENOMEM;
131
132         r = set_fallback_dns(m, DNS_SERVERS);
133         if (r < 0)
134                 return r;
135
136         r = manager_parse_config_file(m);
137         if (r < 0)
138                 return r;
139
140         r = sd_event_default(&m->event);
141         if (r < 0)
142                 return r;
143
144         sd_event_add_signal(m->event, NULL, SIGTERM, NULL,  NULL);
145         sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
146
147         sd_event_set_watchdog(m->event, true);
148
149         *ret = m;
150         m = NULL;
151
152         return 0;
153 }
154
155 void manager_free(Manager *m) {
156         Address *address;
157
158         if (!m)
159                 return;
160
161         sd_event_unref(m->event);
162
163         while ((address = m->fallback_dns)) {
164                 LIST_REMOVE(addresses, m->fallback_dns, address);
165                 free(address);
166         }
167
168         free(m);
169 }
170
171 static void append_dns(FILE *f, void *dns, unsigned char family, unsigned *count) {
172         char buf[INET6_ADDRSTRLEN];
173         const char *address;
174
175         assert(f);
176         assert(dns);
177         assert(count);
178
179         address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN);
180         if (!address) {
181                 log_warning("Invalid DNS address. Ignoring.");
182                 return;
183         }
184
185         if (*count == MAXNS)
186                 fputs("# Too many DNS servers configured, the following entries "
187                       "may be ignored\n", f);
188
189         fprintf(f, "nameserver %s\n", address);
190
191         (*count) ++;
192 }
193
194 int manager_update_resolv_conf(Manager *m) {
195         _cleanup_free_ char *temp_path = NULL;
196         _cleanup_fclose_ FILE *f = NULL;
197          _cleanup_free_ unsigned *indices = NULL;
198         Address *address;
199         unsigned count = 0;
200         int n, r, i;
201
202         assert(m);
203
204         r = fopen_temporary("/run/systemd/network/resolv.conf", &f, &temp_path);
205         if (r < 0)
206                 return r;
207
208         fchmod(fileno(f), 0644);
209
210         fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
211               "# Third party programs must not access this file directly, but\n"
212               "# only through the symlink at /etc/resolv.conf. To manage\n"
213               "# resolv.conf(5) in a different way, replace the symlink by a\n"
214               "# static file or a different symlink.\n\n", f);
215
216         n = sd_network_get_ifindices(&indices);
217         if (n < 0)
218                 n = 0;
219
220         for (i = 0; i < n; i++) {
221                 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
222                 struct in_addr *nameservers;
223                 struct in6_addr *nameservers6;
224                 size_t nameservers_size;
225
226                 r = sd_network_dhcp_use_dns(indices[i]);
227                 if (r > 0) {
228                         r = sd_network_get_dhcp_lease(indices[i], &lease);
229                         if (r >= 0) {
230                                 r = sd_dhcp_lease_get_dns(lease, &nameservers, &nameservers_size);
231                                 if (r >= 0) {
232                                         unsigned j;
233
234                                         for (j = 0; j < nameservers_size; j++)
235                                                 append_dns(f, &nameservers[j], AF_INET, &count);
236                                 }
237                         }
238                 }
239
240                 r = sd_network_get_dns(indices[i], &nameservers, &nameservers_size);
241                 if (r >= 0) {
242                         unsigned j;
243
244                         for (j = 0; j < nameservers_size; j++)
245                                 append_dns(f, &nameservers[j], AF_INET, &count);
246
247                         free(nameservers);
248                 }
249
250                 r = sd_network_get_dns6(indices[i], &nameservers6, &nameservers_size);
251                 if (r >= 0) {
252                         unsigned j;
253
254                         for (j = 0; j < nameservers_size; j++)
255                                 append_dns(f, &nameservers6[j], AF_INET6, &count);
256
257                         free(nameservers6);
258                 }
259         }
260
261         LIST_FOREACH(addresses, address, m->fallback_dns)
262                 append_dns(f, &address->in_addr, address->family, &count);
263
264         fflush(f);
265
266         if (ferror(f) || rename(temp_path, "/run/systemd/network/resolv.conf") < 0) {
267                 r = -errno;
268                 unlink("/run/systemd/network/resolv.conf");
269                 unlink(temp_path);
270                 return r;
271         }
272
273         return 0;
274 }
275
276 static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents,
277                                          void *userdata) {
278         Manager *m = userdata;
279         int r;
280
281         assert(m);
282
283         r = manager_update_resolv_conf(m);
284         if (r < 0)
285                 log_warning("Could not update resolv.conf: %s", strerror(-r));
286
287         sd_network_monitor_flush(m->network_monitor);
288
289         return 0;
290 }
291
292 int manager_network_monitor_listen(Manager *m) {
293         _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
294         _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL;
295         int r, fd, events;
296
297         r = sd_network_monitor_new(NULL, &monitor);
298         if (r < 0)
299                 return r;
300
301         fd = sd_network_monitor_get_fd(monitor);
302         if (fd < 0)
303                 return fd;
304
305         events = sd_network_monitor_get_events(monitor);
306         if (events < 0)
307                 return events;
308
309         r = sd_event_add_io(m->event, &event_source, fd, events,
310                             &manager_network_event_handler, m);
311         if (r < 0)
312                 return r;
313
314         m->network_monitor = monitor;
315         m->network_event_source = event_source;
316         monitor = NULL;
317         event_source = NULL;
318
319         return 0;
320 }