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