]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-link-bus.c
Merge pull request #8554 from poettering/chase-trail-slash
[thirdparty/systemd.git] / src / resolve / resolved-link-bus.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2016 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "alloc-util.h"
22 #include "bus-common-errors.h"
23 #include "bus-util.h"
24 #include "parse-util.h"
25 #include "resolve-util.h"
26 #include "resolved-bus.h"
27 #include "resolved-link-bus.h"
28 #include "resolved-resolv-conf.h"
29 #include "strv.h"
30
31 static int property_get_dnssec_mode(
32 sd_bus *bus,
33 const char *path,
34 const char *interface,
35 const char *property,
36 sd_bus_message *reply,
37 void *userdata,
38 sd_bus_error *error) {
39
40 Link *l = userdata;
41
42 assert(reply);
43 assert(l);
44
45 return sd_bus_message_append(reply, "s", dnssec_mode_to_string(link_get_dnssec_mode(l)));
46 }
47
48 static int property_get_dns(
49 sd_bus *bus,
50 const char *path,
51 const char *interface,
52 const char *property,
53 sd_bus_message *reply,
54 void *userdata,
55 sd_bus_error *error) {
56
57 Link *l = userdata;
58 DnsServer *s;
59 int r;
60
61 assert(reply);
62 assert(l);
63
64 r = sd_bus_message_open_container(reply, 'a', "(iay)");
65 if (r < 0)
66 return r;
67
68 LIST_FOREACH(servers, s, l->dns_servers) {
69 r = bus_dns_server_append(reply, s, false);
70 if (r < 0)
71 return r;
72 }
73
74 return sd_bus_message_close_container(reply);
75 }
76
77 static int property_get_domains(
78 sd_bus *bus,
79 const char *path,
80 const char *interface,
81 const char *property,
82 sd_bus_message *reply,
83 void *userdata,
84 sd_bus_error *error) {
85
86 Link *l = userdata;
87 DnsSearchDomain *d;
88 int r;
89
90 assert(reply);
91 assert(l);
92
93 r = sd_bus_message_open_container(reply, 'a', "(sb)");
94 if (r < 0)
95 return r;
96
97 LIST_FOREACH(domains, d, l->search_domains) {
98 r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only);
99 if (r < 0)
100 return r;
101 }
102
103 return sd_bus_message_close_container(reply);
104 }
105
106 static int property_get_scopes_mask(
107 sd_bus *bus,
108 const char *path,
109 const char *interface,
110 const char *property,
111 sd_bus_message *reply,
112 void *userdata,
113 sd_bus_error *error) {
114
115 Link *l = userdata;
116 uint64_t mask;
117
118 assert(reply);
119 assert(l);
120
121 mask = (l->unicast_scope ? SD_RESOLVED_DNS : 0) |
122 (l->llmnr_ipv4_scope ? SD_RESOLVED_LLMNR_IPV4 : 0) |
123 (l->llmnr_ipv6_scope ? SD_RESOLVED_LLMNR_IPV6 : 0) |
124 (l->mdns_ipv4_scope ? SD_RESOLVED_MDNS_IPV4 : 0) |
125 (l->mdns_ipv6_scope ? SD_RESOLVED_MDNS_IPV6 : 0);
126
127 return sd_bus_message_append(reply, "t", mask);
128 }
129
130 static int property_get_ntas(
131 sd_bus *bus,
132 const char *path,
133 const char *interface,
134 const char *property,
135 sd_bus_message *reply,
136 void *userdata,
137 sd_bus_error *error) {
138
139 Link *l = userdata;
140 const char *name;
141 Iterator i;
142 int r;
143
144 assert(reply);
145 assert(l);
146
147 r = sd_bus_message_open_container(reply, 'a', "s");
148 if (r < 0)
149 return r;
150
151 SET_FOREACH(name, l->dnssec_negative_trust_anchors, i) {
152 r = sd_bus_message_append(reply, "s", name);
153 if (r < 0)
154 return r;
155 }
156
157 return sd_bus_message_close_container(reply);
158 }
159
160 static int property_get_dnssec_supported(
161 sd_bus *bus,
162 const char *path,
163 const char *interface,
164 const char *property,
165 sd_bus_message *reply,
166 void *userdata,
167 sd_bus_error *error) {
168
169 Link *l = userdata;
170
171 assert(reply);
172 assert(l);
173
174 return sd_bus_message_append(reply, "b", link_dnssec_supported(l));
175 }
176
177 static int verify_unmanaged_link(Link *l, sd_bus_error *error) {
178 assert(l);
179
180 if (l->flags & IFF_LOOPBACK)
181 return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->name);
182 if (l->is_managed)
183 return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is managed.", l->name);
184
185 return 0;
186 }
187
188 int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
189 _cleanup_free_ struct in_addr_data *dns = NULL;
190 size_t allocated = 0, n = 0;
191 Link *l = userdata;
192 unsigned i;
193 int r;
194
195 assert(message);
196 assert(l);
197
198 r = verify_unmanaged_link(l, error);
199 if (r < 0)
200 return r;
201
202 r = sd_bus_message_enter_container(message, 'a', "(iay)");
203 if (r < 0)
204 return r;
205
206 for (;;) {
207 int family;
208 size_t sz;
209 const void *d;
210
211 assert_cc(sizeof(int) == sizeof(int32_t));
212
213 r = sd_bus_message_enter_container(message, 'r', "iay");
214 if (r < 0)
215 return r;
216 if (r == 0)
217 break;
218
219 r = sd_bus_message_read(message, "i", &family);
220 if (r < 0)
221 return r;
222
223 if (!IN_SET(family, AF_INET, AF_INET6))
224 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
225
226 r = sd_bus_message_read_array(message, 'y', &d, &sz);
227 if (r < 0)
228 return r;
229 if (sz != FAMILY_ADDRESS_SIZE(family))
230 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
231
232 if (!dns_server_address_valid(family, d))
233 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
234
235 r = sd_bus_message_exit_container(message);
236 if (r < 0)
237 return r;
238
239 if (!GREEDY_REALLOC(dns, allocated, n+1))
240 return -ENOMEM;
241
242 dns[n].family = family;
243 memcpy(&dns[n].address, d, sz);
244 n++;
245 }
246
247 r = sd_bus_message_exit_container(message);
248 if (r < 0)
249 return r;
250
251 dns_server_mark_all(l->dns_servers);
252
253 for (i = 0; i < n; i++) {
254 DnsServer *s;
255
256 s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address, 0);
257 if (s)
258 dns_server_move_back_and_unmark(s);
259 else {
260 r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0);
261 if (r < 0)
262 goto clear;
263 }
264
265 }
266
267 dns_server_unlink_marked(l->dns_servers);
268 link_allocate_scopes(l);
269
270 (void) link_save_user(l);
271 (void) manager_write_resolv_conf(l->manager);
272
273 return sd_bus_reply_method_return(message, NULL);
274
275 clear:
276 dns_server_unlink_all(l->dns_servers);
277 return r;
278 }
279
280 int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
281 Link *l = userdata;
282 int r;
283
284 assert(message);
285 assert(l);
286
287 r = verify_unmanaged_link(l, error);
288 if (r < 0)
289 return r;
290
291 r = sd_bus_message_enter_container(message, 'a', "(sb)");
292 if (r < 0)
293 return r;
294
295 for (;;) {
296 const char *name;
297 int route_only;
298
299 r = sd_bus_message_read(message, "(sb)", &name, &route_only);
300 if (r < 0)
301 return r;
302 if (r == 0)
303 break;
304
305 r = dns_name_is_valid(name);
306 if (r < 0)
307 return r;
308 if (r == 0)
309 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
310 if (!route_only && dns_name_is_root(name))
311 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
312 }
313
314 dns_search_domain_mark_all(l->search_domains);
315
316 r = sd_bus_message_rewind(message, false);
317 if (r < 0)
318 return r;
319
320 for (;;) {
321 DnsSearchDomain *d;
322 const char *name;
323 int route_only;
324
325 r = sd_bus_message_read(message, "(sb)", &name, &route_only);
326 if (r < 0)
327 goto clear;
328 if (r == 0)
329 break;
330
331 r = dns_search_domain_find(l->search_domains, name, &d);
332 if (r < 0)
333 goto clear;
334
335 if (r > 0)
336 dns_search_domain_move_back_and_unmark(d);
337 else {
338 r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
339 if (r < 0)
340 goto clear;
341 }
342
343 d->route_only = route_only;
344 }
345
346 r = sd_bus_message_exit_container(message);
347 if (r < 0)
348 goto clear;
349
350 dns_search_domain_unlink_marked(l->search_domains);
351
352 (void) link_save_user(l);
353 (void) manager_write_resolv_conf(l->manager);
354
355 return sd_bus_reply_method_return(message, NULL);
356
357 clear:
358 dns_search_domain_unlink_all(l->search_domains);
359 return r;
360 }
361
362 int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
363 Link *l = userdata;
364 ResolveSupport mode;
365 const char *llmnr;
366 int r;
367
368 assert(message);
369 assert(l);
370
371 r = verify_unmanaged_link(l, error);
372 if (r < 0)
373 return r;
374
375 r = sd_bus_message_read(message, "s", &llmnr);
376 if (r < 0)
377 return r;
378
379 if (isempty(llmnr))
380 mode = RESOLVE_SUPPORT_YES;
381 else {
382 mode = resolve_support_from_string(llmnr);
383 if (mode < 0)
384 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
385 }
386
387 l->llmnr_support = mode;
388 link_allocate_scopes(l);
389 link_add_rrs(l, false);
390
391 (void) link_save_user(l);
392
393 return sd_bus_reply_method_return(message, NULL);
394 }
395
396 int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
397 Link *l = userdata;
398 ResolveSupport mode;
399 const char *mdns;
400 int r;
401
402 assert(message);
403 assert(l);
404
405 r = verify_unmanaged_link(l, error);
406 if (r < 0)
407 return r;
408
409 r = sd_bus_message_read(message, "s", &mdns);
410 if (r < 0)
411 return r;
412
413 if (isempty(mdns))
414 mode = RESOLVE_SUPPORT_NO;
415 else {
416 mode = resolve_support_from_string(mdns);
417 if (mode < 0)
418 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
419 }
420
421 l->mdns_support = mode;
422 link_allocate_scopes(l);
423 link_add_rrs(l, false);
424
425 (void) link_save_user(l);
426
427 return sd_bus_reply_method_return(message, NULL);
428 }
429
430 int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
431 Link *l = userdata;
432 const char *dnssec;
433 DnssecMode mode;
434 int r;
435
436 assert(message);
437 assert(l);
438
439 r = verify_unmanaged_link(l, error);
440 if (r < 0)
441 return r;
442
443 r = sd_bus_message_read(message, "s", &dnssec);
444 if (r < 0)
445 return r;
446
447 if (isempty(dnssec))
448 mode = _DNSSEC_MODE_INVALID;
449 else {
450 mode = dnssec_mode_from_string(dnssec);
451 if (mode < 0)
452 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
453 }
454
455 link_set_dnssec_mode(l, mode);
456
457 (void) link_save_user(l);
458
459 return sd_bus_reply_method_return(message, NULL);
460 }
461
462 int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
463 _cleanup_set_free_free_ Set *ns = NULL;
464 _cleanup_strv_free_ char **ntas = NULL;
465 Link *l = userdata;
466 int r;
467 char **i;
468
469 assert(message);
470 assert(l);
471
472 r = verify_unmanaged_link(l, error);
473 if (r < 0)
474 return r;
475
476 r = sd_bus_message_read_strv(message, &ntas);
477 if (r < 0)
478 return r;
479
480 STRV_FOREACH(i, ntas) {
481 r = dns_name_is_valid(*i);
482 if (r < 0)
483 return r;
484 if (r == 0)
485 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i);
486 }
487
488 ns = set_new(&dns_name_hash_ops);
489 if (!ns)
490 return -ENOMEM;
491
492 STRV_FOREACH(i, ntas) {
493 r = set_put_strdup(ns, *i);
494 if (r < 0)
495 return r;
496 }
497
498 set_free_free(l->dnssec_negative_trust_anchors);
499 l->dnssec_negative_trust_anchors = TAKE_PTR(ns);
500
501 (void) link_save_user(l);
502
503 return sd_bus_reply_method_return(message, NULL);
504 }
505
506 int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error) {
507 Link *l = userdata;
508 int r;
509
510 assert(message);
511 assert(l);
512
513 r = verify_unmanaged_link(l, error);
514 if (r < 0)
515 return r;
516
517 link_flush_settings(l);
518 link_allocate_scopes(l);
519 link_add_rrs(l, false);
520
521 (void) link_save_user(l);
522 (void) manager_write_resolv_conf(l->manager);
523
524 return sd_bus_reply_method_return(message, NULL);
525 }
526
527 const sd_bus_vtable link_vtable[] = {
528 SD_BUS_VTABLE_START(0),
529
530 SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
531 SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
532 SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
533 SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
534 SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0),
535 SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
536 SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
537 SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0),
538
539 SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0),
540 SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, 0),
541 SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0),
542 SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0),
543 SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0),
544 SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, 0),
545 SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, 0),
546
547 SD_BUS_VTABLE_END
548 };
549
550 int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
551 _cleanup_free_ char *e = NULL;
552 Manager *m = userdata;
553 int ifindex;
554 Link *link;
555 int r;
556
557 assert(bus);
558 assert(path);
559 assert(interface);
560 assert(found);
561 assert(m);
562
563 r = sd_bus_path_decode(path, "/org/freedesktop/resolve1/link", &e);
564 if (r <= 0)
565 return 0;
566
567 r = parse_ifindex(e, &ifindex);
568 if (r < 0)
569 return 0;
570
571 link = hashmap_get(m->links, INT_TO_PTR(ifindex));
572 if (!link)
573 return 0;
574
575 *found = link;
576 return 1;
577 }
578
579 char *link_bus_path(Link *link) {
580 _cleanup_free_ char *ifindex = NULL;
581 char *p;
582 int r;
583
584 assert(link);
585
586 if (asprintf(&ifindex, "%i", link->ifindex) < 0)
587 return NULL;
588
589 r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifindex, &p);
590 if (r < 0)
591 return NULL;
592
593 return p;
594 }
595
596 int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
597 _cleanup_strv_free_ char **l = NULL;
598 Manager *m = userdata;
599 Link *link;
600 Iterator i;
601 unsigned c = 0;
602
603 assert(bus);
604 assert(path);
605 assert(m);
606 assert(nodes);
607
608 l = new0(char*, hashmap_size(m->links) + 1);
609 if (!l)
610 return -ENOMEM;
611
612 HASHMAP_FOREACH(link, m->links, i) {
613 char *p;
614
615 p = link_bus_path(link);
616 if (!p)
617 return -ENOMEM;
618
619 l[c++] = p;
620 }
621
622 l[c] = NULL;
623 *nodes = TAKE_PTR(l);
624
625 return 1;
626 }