]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-manager.c
event: pull in sd-event.h from event-util.h
[thirdparty/systemd.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
34 static 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
69 int 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
99 static int manager_parse_config_file(Manager *m) {
100 int r;
101
102 assert(m);
103
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);
107 if (r < 0)
108 log_warning("Failed to parse configuration file: %s", strerror(-r));
109
110 return r;
111 }
112
113 int manager_new(Manager **ret) {
114 _cleanup_manager_free_ Manager *m = NULL;
115 int r;
116
117 assert(ret);
118
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
146 void manager_free(Manager *m) {
147 Address *address;
148
149 if (!m)
150 return;
151
152 sd_event_unref(m->event);
153
154 while ((address = m->fallback_dns)) {
155 LIST_REMOVE(addresses, m->fallback_dns, address);
156 free(address);
157 }
158
159 free(m);
160 }
161
162 static void append_dns(FILE *f, void *dns, unsigned char family, unsigned *count) {
163 char buf[INET6_ADDRSTRLEN];
164 const char *address;
165
166 assert(f);
167 assert(dns);
168 assert(count);
169
170 address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN);
171 if (!address) {
172 log_warning("Invalid DNS address. Ignoring.");
173 return;
174 }
175
176 if (*count == MAXNS)
177 fputs("# Too many DNS servers configured, the following entries "
178 "may be ignored\n", f);
179
180 fprintf(f, "nameserver %s\n", address);
181
182 (*count) ++;
183 }
184
185 int manager_update_resolv_conf(Manager *m) {
186 const char *path = "/run/systemd/resolve/resolv.conf";
187 _cleanup_free_ char *temp_path = NULL;
188 _cleanup_fclose_ FILE *f = NULL;
189 _cleanup_free_ unsigned *indices = NULL;
190 Address *address;
191 unsigned count = 0;
192 int n, r, i;
193
194 assert(m);
195
196 r = fopen_temporary(path, &f, &temp_path);
197 if (r < 0)
198 return r;
199
200 fchmod(fileno(f), 0644);
201
202 fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
203 "# Third party programs must not access this file directly, but\n"
204 "# only through the symlink at /etc/resolv.conf. To manage\n"
205 "# resolv.conf(5) in a different way, replace the symlink by a\n"
206 "# static file or a different symlink.\n\n", f);
207
208 n = sd_network_get_ifindices(&indices);
209 if (n < 0)
210 n = 0;
211
212 for (i = 0; i < n; i++) {
213 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
214 struct in_addr *nameservers;
215 struct in6_addr *nameservers6;
216 size_t nameservers_size;
217
218 r = sd_network_dhcp_use_dns(indices[i]);
219 if (r > 0) {
220 r = sd_network_get_dhcp_lease(indices[i], &lease);
221 if (r >= 0) {
222 r = sd_dhcp_lease_get_dns(lease, &nameservers, &nameservers_size);
223 if (r >= 0) {
224 unsigned j;
225
226 for (j = 0; j < nameservers_size; j++)
227 append_dns(f, &nameservers[j], AF_INET, &count);
228 }
229 }
230 }
231
232 r = sd_network_get_dns(indices[i], &nameservers, &nameservers_size);
233 if (r >= 0) {
234 unsigned j;
235
236 for (j = 0; j < nameservers_size; j++)
237 append_dns(f, &nameservers[j], AF_INET, &count);
238
239 free(nameservers);
240 }
241
242 r = sd_network_get_dns6(indices[i], &nameservers6, &nameservers_size);
243 if (r >= 0) {
244 unsigned j;
245
246 for (j = 0; j < nameservers_size; j++)
247 append_dns(f, &nameservers6[j], AF_INET6, &count);
248
249 free(nameservers6);
250 }
251 }
252
253 LIST_FOREACH(addresses, address, m->fallback_dns)
254 append_dns(f, &address->in_addr, address->family, &count);
255
256 fflush(f);
257
258 if (ferror(f) || rename(temp_path, path) < 0) {
259 r = -errno;
260 unlink(path);
261 unlink(temp_path);
262 return r;
263 }
264
265 return 0;
266 }
267
268 static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents,
269 void *userdata) {
270 Manager *m = userdata;
271 int r;
272
273 assert(m);
274
275 r = manager_update_resolv_conf(m);
276 if (r < 0)
277 log_warning("Could not update resolv.conf: %s", strerror(-r));
278
279 sd_network_monitor_flush(m->network_monitor);
280
281 return 0;
282 }
283
284 int manager_network_monitor_listen(Manager *m) {
285 _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
286 _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL;
287 int r, fd, events;
288
289 r = sd_network_monitor_new(NULL, &monitor);
290 if (r < 0)
291 return r;
292
293 fd = sd_network_monitor_get_fd(monitor);
294 if (fd < 0)
295 return fd;
296
297 events = sd_network_monitor_get_events(monitor);
298 if (events < 0)
299 return events;
300
301 r = sd_event_add_io(m->event, &event_source, fd, events,
302 &manager_network_event_handler, m);
303 if (r < 0)
304 return r;
305
306 m->network_monitor = monitor;
307 m->network_event_source = event_source;
308 monitor = NULL;
309 event_source = NULL;
310
311 return 0;
312 }