]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-link-bus.c
man/systemd.mount: tmpfs automatically gains After=swap.target dep
[thirdparty/systemd.git] / src / resolve / resolved-link-bus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <net/if.h>
4 #include <netinet/in.h>
5 #include <sys/capability.h>
6
7 #include "alloc-util.h"
8 #include "bus-common-errors.h"
9 #include "bus-get-properties.h"
10 #include "bus-message-util.h"
11 #include "bus-polkit.h"
12 #include "log-link.h"
13 #include "parse-util.h"
14 #include "resolve-util.h"
15 #include "resolved-bus.h"
16 #include "resolved-link-bus.h"
17 #include "resolved-resolv-conf.h"
18 #include "socket-netlink.h"
19 #include "stdio-util.h"
20 #include "strv.h"
21 #include "user-util.h"
22
23 static BUS_DEFINE_PROPERTY_GET(property_get_dnssec_supported, "b", Link, link_dnssec_supported);
24 static BUS_DEFINE_PROPERTY_GET2(property_get_dnssec_mode, "s", Link, link_get_dnssec_mode, dnssec_mode_to_string);
25 static BUS_DEFINE_PROPERTY_GET2(property_get_llmnr_support, "s", Link, link_get_llmnr_support, resolve_support_to_string);
26 static BUS_DEFINE_PROPERTY_GET2(property_get_mdns_support, "s", Link, link_get_mdns_support, resolve_support_to_string);
27
28 static int property_get_dns_over_tls_mode(
29 sd_bus *bus,
30 const char *path,
31 const char *interface,
32 const char *property,
33 sd_bus_message *reply,
34 void *userdata,
35 sd_bus_error *error) {
36
37 Link *l = ASSERT_PTR(userdata);
38
39 assert(reply);
40
41 return sd_bus_message_append(reply, "s", dns_over_tls_mode_to_string(link_get_dns_over_tls_mode(l)));
42 }
43
44 static int property_get_dns_internal(
45 sd_bus *bus,
46 const char *path,
47 const char *interface,
48 const char *property,
49 sd_bus_message *reply,
50 void *userdata,
51 sd_bus_error *error,
52 bool extended) {
53
54 Link *l = ASSERT_PTR(userdata);
55 int r;
56
57 assert(reply);
58
59 r = sd_bus_message_open_container(reply, 'a', extended ? "(iayqs)" : "(iay)");
60 if (r < 0)
61 return r;
62
63 LIST_FOREACH(servers, s, l->dns_servers) {
64 r = bus_dns_server_append(reply, s, false, extended);
65 if (r < 0)
66 return r;
67 }
68
69 return sd_bus_message_close_container(reply);
70 }
71
72 static int property_get_dns(
73 sd_bus *bus,
74 const char *path,
75 const char *interface,
76 const char *property,
77 sd_bus_message *reply,
78 void *userdata,
79 sd_bus_error *error) {
80 return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, false);
81 }
82
83 static int property_get_dns_ex(
84 sd_bus *bus,
85 const char *path,
86 const char *interface,
87 const char *property,
88 sd_bus_message *reply,
89 void *userdata,
90 sd_bus_error *error) {
91 return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, true);
92 }
93
94 static int property_get_current_dns_server_internal(
95 sd_bus *bus,
96 const char *path,
97 const char *interface,
98 const char *property,
99 sd_bus_message *reply,
100 void *userdata,
101 sd_bus_error *error,
102 bool extended) {
103
104 DnsServer *s;
105
106 assert(reply);
107 assert(userdata);
108
109 s = *(DnsServer **) userdata;
110
111 return bus_dns_server_append(reply, s, false, extended);
112 }
113
114 static int property_get_current_dns_server(
115 sd_bus *bus,
116 const char *path,
117 const char *interface,
118 const char *property,
119 sd_bus_message *reply,
120 void *userdata,
121 sd_bus_error *error) {
122 return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false);
123 }
124
125 static int property_get_current_dns_server_ex(
126 sd_bus *bus,
127 const char *path,
128 const char *interface,
129 const char *property,
130 sd_bus_message *reply,
131 void *userdata,
132 sd_bus_error *error) {
133 return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true);
134 }
135
136 static int property_get_domains(
137 sd_bus *bus,
138 const char *path,
139 const char *interface,
140 const char *property,
141 sd_bus_message *reply,
142 void *userdata,
143 sd_bus_error *error) {
144
145 Link *l = ASSERT_PTR(userdata);
146 int r;
147
148 assert(reply);
149
150 r = sd_bus_message_open_container(reply, 'a', "(sb)");
151 if (r < 0)
152 return r;
153
154 LIST_FOREACH(domains, d, l->search_domains) {
155 r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only);
156 if (r < 0)
157 return r;
158 }
159
160 return sd_bus_message_close_container(reply);
161 }
162
163 static int property_get_default_route(
164 sd_bus *bus,
165 const char *path,
166 const char *interface,
167 const char *property,
168 sd_bus_message *reply,
169 void *userdata,
170 sd_bus_error *error) {
171
172 Link *l = ASSERT_PTR(userdata);
173
174 assert(reply);
175
176 /* Return what is configured, if there's something configured */
177 if (l->default_route >= 0)
178 return sd_bus_message_append(reply, "b", l->default_route);
179
180 /* Otherwise report what is in effect */
181 if (l->unicast_scope)
182 return sd_bus_message_append(reply, "b", dns_scope_is_default_route(l->unicast_scope));
183
184 return sd_bus_message_append(reply, "b", false);
185 }
186
187 static int property_get_scopes_mask(
188 sd_bus *bus,
189 const char *path,
190 const char *interface,
191 const char *property,
192 sd_bus_message *reply,
193 void *userdata,
194 sd_bus_error *error) {
195
196 Link *l = ASSERT_PTR(userdata);
197 uint64_t mask;
198
199 assert(reply);
200
201 mask = (l->unicast_scope ? SD_RESOLVED_DNS : 0) |
202 (l->llmnr_ipv4_scope ? SD_RESOLVED_LLMNR_IPV4 : 0) |
203 (l->llmnr_ipv6_scope ? SD_RESOLVED_LLMNR_IPV6 : 0) |
204 (l->mdns_ipv4_scope ? SD_RESOLVED_MDNS_IPV4 : 0) |
205 (l->mdns_ipv6_scope ? SD_RESOLVED_MDNS_IPV6 : 0);
206
207 return sd_bus_message_append(reply, "t", mask);
208 }
209
210 static int verify_unmanaged_link(Link *l, sd_bus_error *error) {
211 assert(l);
212
213 if (l->flags & IFF_LOOPBACK)
214 return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->ifname);
215 if (l->is_managed)
216 return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is managed.", l->ifname);
217
218 return 0;
219 }
220
221 static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) {
222 _cleanup_free_ char *j = NULL;
223 struct in_addr_full **dns;
224 bool changed = false;
225 Link *l = ASSERT_PTR(userdata);
226 size_t n;
227 int r;
228
229 assert(message);
230
231 r = verify_unmanaged_link(l, error);
232 if (r < 0)
233 return r;
234
235 r = bus_message_read_dns_servers(message, error, extended, &dns, &n);
236 if (r < 0)
237 return r;
238
239 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
240 "org.freedesktop.resolve1.set-dns-servers",
241 NULL, true, UID_INVALID,
242 &l->manager->polkit_registry, error);
243 if (r < 0)
244 goto finalize;
245 if (r == 0) {
246 r = 1; /* Polkit will call us back */
247 goto finalize;
248 }
249
250 for (size_t i = 0; i < n; i++) {
251 const char *s;
252
253 s = in_addr_full_to_string(dns[i]);
254 if (!s) {
255 r = -ENOMEM;
256 goto finalize;
257 }
258
259 if (!strextend_with_separator(&j, ", ", s)) {
260 r = -ENOMEM;
261 goto finalize;
262 }
263 }
264
265 bus_client_log(message, "DNS server change");
266
267 dns_server_mark_all(l->dns_servers);
268
269 for (size_t i = 0; i < n; i++) {
270 DnsServer *s;
271
272 s = dns_server_find(l->dns_servers, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
273 if (s)
274 dns_server_move_back_and_unmark(s);
275 else {
276 r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
277 if (r < 0) {
278 dns_server_unlink_all(l->dns_servers);
279 goto finalize;
280 }
281
282 changed = true;
283 }
284
285 }
286
287 changed = dns_server_unlink_marked(l->dns_servers) || changed;
288
289 if (changed) {
290 link_allocate_scopes(l);
291
292 (void) link_save_user(l);
293 (void) manager_write_resolv_conf(l->manager);
294 (void) manager_send_changed(l->manager, "DNS");
295
296 if (j)
297 log_link_info(l, "Bus client set DNS server list to: %s", j);
298 else
299 log_link_info(l, "Bus client reset DNS server list.");
300 }
301
302 r = sd_bus_reply_method_return(message, NULL);
303
304 finalize:
305 for (size_t i = 0; i < n; i++)
306 in_addr_full_free(dns[i]);
307 free(dns);
308
309 return r;
310 }
311
312 int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
313 return bus_link_method_set_dns_servers_internal(message, userdata, error, false);
314 }
315
316 int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
317 return bus_link_method_set_dns_servers_internal(message, userdata, error, true);
318 }
319
320 int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
321 _cleanup_free_ char *j = NULL;
322 Link *l = ASSERT_PTR(userdata);
323 bool changed = false;
324 int r;
325
326 assert(message);
327
328 r = verify_unmanaged_link(l, error);
329 if (r < 0)
330 return r;
331
332 r = sd_bus_message_enter_container(message, 'a', "(sb)");
333 if (r < 0)
334 return r;
335
336 for (;;) {
337 _cleanup_free_ char *prefixed = NULL;
338 const char *name;
339 int route_only;
340
341 r = sd_bus_message_read(message, "(sb)", &name, &route_only);
342 if (r < 0)
343 return r;
344 if (r == 0)
345 break;
346
347 r = dns_name_is_valid(name);
348 if (r < 0)
349 return r;
350 if (r == 0)
351 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
352 if (!route_only && dns_name_is_root(name))
353 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
354
355 if (route_only) {
356 prefixed = strjoin("~", name);
357 if (!prefixed)
358 return -ENOMEM;
359
360 name = prefixed;
361 }
362
363 if (!strextend_with_separator(&j, ", ", name))
364 return -ENOMEM;
365 }
366
367 r = sd_bus_message_rewind(message, false);
368 if (r < 0)
369 return r;
370
371 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
372 "org.freedesktop.resolve1.set-domains",
373 NULL, true, UID_INVALID,
374 &l->manager->polkit_registry, error);
375 if (r < 0)
376 return r;
377 if (r == 0)
378 return 1; /* Polkit will call us back */
379
380 bus_client_log(message, "dns domains change");
381
382 dns_search_domain_mark_all(l->search_domains);
383
384 for (;;) {
385 DnsSearchDomain *d;
386 const char *name;
387 int route_only;
388
389 r = sd_bus_message_read(message, "(sb)", &name, &route_only);
390 if (r < 0)
391 goto clear;
392 if (r == 0)
393 break;
394
395 r = dns_search_domain_find(l->search_domains, name, &d);
396 if (r < 0)
397 goto clear;
398
399 if (r > 0)
400 dns_search_domain_move_back_and_unmark(d);
401 else {
402 r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
403 if (r < 0)
404 goto clear;
405
406 changed = true;
407 }
408
409 d->route_only = route_only;
410 }
411
412 r = sd_bus_message_exit_container(message);
413 if (r < 0)
414 goto clear;
415
416 changed = dns_search_domain_unlink_marked(l->search_domains) || changed;
417
418 if (changed) {
419 (void) link_save_user(l);
420 (void) manager_write_resolv_conf(l->manager);
421
422 if (j)
423 log_link_info(l, "Bus client set search domain list to: %s", j);
424 else
425 log_link_info(l, "Bus client reset search domain list.");
426 }
427
428 return sd_bus_reply_method_return(message, NULL);
429
430 clear:
431 dns_search_domain_unlink_all(l->search_domains);
432 return r;
433 }
434
435 int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) {
436 Link *l = ASSERT_PTR(userdata);
437 int r, b;
438
439 assert(message);
440
441 r = verify_unmanaged_link(l, error);
442 if (r < 0)
443 return r;
444
445 r = sd_bus_message_read(message, "b", &b);
446 if (r < 0)
447 return r;
448
449 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
450 "org.freedesktop.resolve1.set-default-route",
451 NULL, true, UID_INVALID,
452 &l->manager->polkit_registry, error);
453 if (r < 0)
454 return r;
455 if (r == 0)
456 return 1; /* Polkit will call us back */
457
458 bus_client_log(message, "dns default route change");
459
460 if (l->default_route != b) {
461 l->default_route = b;
462
463 (void) link_save_user(l);
464 (void) manager_write_resolv_conf(l->manager);
465
466 log_link_info(l, "Bus client set default route setting: %s", yes_no(b));
467 }
468
469 return sd_bus_reply_method_return(message, NULL);
470 }
471
472 int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
473 Link *l = ASSERT_PTR(userdata);
474 ResolveSupport mode;
475 const char *llmnr;
476 int r;
477
478 assert(message);
479
480 r = verify_unmanaged_link(l, error);
481 if (r < 0)
482 return r;
483
484 r = sd_bus_message_read(message, "s", &llmnr);
485 if (r < 0)
486 return r;
487
488 if (isempty(llmnr))
489 mode = RESOLVE_SUPPORT_YES;
490 else {
491 mode = resolve_support_from_string(llmnr);
492 if (mode < 0)
493 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
494 }
495
496 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
497 "org.freedesktop.resolve1.set-llmnr",
498 NULL, true, UID_INVALID,
499 &l->manager->polkit_registry, error);
500 if (r < 0)
501 return r;
502 if (r == 0)
503 return 1; /* Polkit will call us back */
504
505 bus_client_log(message, "LLMNR change");
506
507 if (l->llmnr_support != mode) {
508 l->llmnr_support = mode;
509 link_allocate_scopes(l);
510 link_add_rrs(l, false);
511
512 (void) link_save_user(l);
513
514 log_link_info(l, "Bus client set LLMNR setting: %s", resolve_support_to_string(mode));
515 }
516
517 return sd_bus_reply_method_return(message, NULL);
518 }
519
520 int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
521 Link *l = ASSERT_PTR(userdata);
522 ResolveSupport mode;
523 const char *mdns;
524 int r;
525
526 assert(message);
527
528 r = verify_unmanaged_link(l, error);
529 if (r < 0)
530 return r;
531
532 r = sd_bus_message_read(message, "s", &mdns);
533 if (r < 0)
534 return r;
535
536 if (isempty(mdns))
537 mode = RESOLVE_SUPPORT_YES;
538 else {
539 mode = resolve_support_from_string(mdns);
540 if (mode < 0)
541 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
542 }
543
544 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
545 "org.freedesktop.resolve1.set-mdns",
546 NULL, true, UID_INVALID,
547 &l->manager->polkit_registry, error);
548 if (r < 0)
549 return r;
550 if (r == 0)
551 return 1; /* Polkit will call us back */
552
553 bus_client_log(message, "mDNS change");
554
555 if (l->mdns_support != mode) {
556 l->mdns_support = mode;
557 link_allocate_scopes(l);
558 link_add_rrs(l, false);
559
560 (void) link_save_user(l);
561
562 log_link_info(l, "Bus client set MulticastDNS setting: %s", resolve_support_to_string(mode));
563 }
564
565 return sd_bus_reply_method_return(message, NULL);
566 }
567
568 int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd_bus_error *error) {
569 Link *l = ASSERT_PTR(userdata);
570 const char *dns_over_tls;
571 DnsOverTlsMode mode;
572 int r;
573
574 assert(message);
575
576 r = verify_unmanaged_link(l, error);
577 if (r < 0)
578 return r;
579
580 r = sd_bus_message_read(message, "s", &dns_over_tls);
581 if (r < 0)
582 return r;
583
584 if (isempty(dns_over_tls))
585 mode = _DNS_OVER_TLS_MODE_INVALID;
586 else {
587 mode = dns_over_tls_mode_from_string(dns_over_tls);
588 if (mode < 0)
589 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
590 }
591
592 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
593 "org.freedesktop.resolve1.set-dns-over-tls",
594 NULL, true, UID_INVALID,
595 &l->manager->polkit_registry, error);
596 if (r < 0)
597 return r;
598 if (r == 0)
599 return 1; /* Polkit will call us back */
600
601 bus_client_log(message, "D-o-T change");
602
603 if (l->dns_over_tls_mode != mode) {
604 link_set_dns_over_tls_mode(l, mode);
605 link_allocate_scopes(l);
606
607 (void) link_save_user(l);
608
609 log_link_info(l, "Bus client set DNSOverTLS setting: %s",
610 mode < 0 ? "default" : dns_over_tls_mode_to_string(mode));
611 }
612
613 return sd_bus_reply_method_return(message, NULL);
614 }
615
616 int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
617 Link *l = ASSERT_PTR(userdata);
618 const char *dnssec;
619 DnssecMode mode;
620 int r;
621
622 assert(message);
623
624 r = verify_unmanaged_link(l, error);
625 if (r < 0)
626 return r;
627
628 r = sd_bus_message_read(message, "s", &dnssec);
629 if (r < 0)
630 return r;
631
632 if (isempty(dnssec))
633 mode = _DNSSEC_MODE_INVALID;
634 else {
635 mode = dnssec_mode_from_string(dnssec);
636 if (mode < 0)
637 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
638 }
639
640 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
641 "org.freedesktop.resolve1.set-dnssec",
642 NULL, true, UID_INVALID,
643 &l->manager->polkit_registry, error);
644 if (r < 0)
645 return r;
646 if (r == 0)
647 return 1; /* Polkit will call us back */
648
649 bus_client_log(message, "DNSSEC change");
650
651 if (l->dnssec_mode != mode) {
652 link_set_dnssec_mode(l, mode);
653 link_allocate_scopes(l);
654
655 (void) link_save_user(l);
656
657 log_link_info(l, "Bus client set DNSSEC setting: %s",
658 mode < 0 ? "default" : dnssec_mode_to_string(mode));
659 }
660
661 return sd_bus_reply_method_return(message, NULL);
662 }
663
664 int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
665 _cleanup_set_free_free_ Set *ns = NULL;
666 _cleanup_strv_free_ char **ntas = NULL;
667 _cleanup_free_ char *j = NULL;
668 Link *l = ASSERT_PTR(userdata);
669 int r;
670
671 assert(message);
672
673 r = verify_unmanaged_link(l, error);
674 if (r < 0)
675 return r;
676
677 ns = set_new(&dns_name_hash_ops);
678 if (!ns)
679 return -ENOMEM;
680
681 r = sd_bus_message_read_strv(message, &ntas);
682 if (r < 0)
683 return r;
684
685 STRV_FOREACH(i, ntas) {
686 r = dns_name_is_valid(*i);
687 if (r < 0)
688 return r;
689 if (r == 0)
690 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
691 "Invalid negative trust anchor domain: %s", *i);
692
693 r = set_put_strdup(&ns, *i);
694 if (r < 0)
695 return r;
696
697 if (!strextend_with_separator(&j, ", ", *i))
698 return -ENOMEM;
699 }
700
701 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
702 "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors",
703 NULL, true, UID_INVALID,
704 &l->manager->polkit_registry, error);
705 if (r < 0)
706 return r;
707 if (r == 0)
708 return 1; /* Polkit will call us back */
709
710 bus_client_log(message, "DNSSEC NTA change");
711
712 if (!set_equal(ns, l->dnssec_negative_trust_anchors)) {
713 set_free_free(l->dnssec_negative_trust_anchors);
714 l->dnssec_negative_trust_anchors = TAKE_PTR(ns);
715
716 (void) link_save_user(l);
717
718 if (j)
719 log_link_info(l, "Bus client set NTA list to: %s", j);
720 else
721 log_link_info(l, "Bus client reset NTA list.");
722 }
723
724 return sd_bus_reply_method_return(message, NULL);
725 }
726
727 int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error) {
728 Link *l = ASSERT_PTR(userdata);
729 int r;
730
731 assert(message);
732
733 r = verify_unmanaged_link(l, error);
734 if (r < 0)
735 return r;
736
737 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
738 "org.freedesktop.resolve1.revert",
739 NULL, true, UID_INVALID,
740 &l->manager->polkit_registry, error);
741 if (r < 0)
742 return r;
743 if (r == 0)
744 return 1; /* Polkit will call us back */
745
746 bus_client_log(message, "revert");
747
748 link_flush_settings(l);
749 link_allocate_scopes(l);
750 link_add_rrs(l, false);
751
752 (void) link_save_user(l);
753 (void) manager_write_resolv_conf(l->manager);
754 (void) manager_send_changed(l->manager, "DNS");
755
756 return sd_bus_reply_method_return(message, NULL);
757 }
758
759 static int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
760 _cleanup_free_ char *e = NULL;
761 Manager *m = ASSERT_PTR(userdata);
762 Link *link;
763 int ifindex, r;
764
765 assert(bus);
766 assert(path);
767 assert(interface);
768 assert(found);
769
770 r = sd_bus_path_decode(path, "/org/freedesktop/resolve1/link", &e);
771 if (r <= 0)
772 return 0;
773
774 ifindex = parse_ifindex(e);
775 if (ifindex < 0)
776 return 0;
777
778 link = hashmap_get(m->links, INT_TO_PTR(ifindex));
779 if (!link)
780 return 0;
781
782 *found = link;
783 return 1;
784 }
785
786 char *link_bus_path(const Link *link) {
787 char *p, ifindex[DECIMAL_STR_MAX(link->ifindex)];
788 int r;
789
790 assert(link);
791
792 xsprintf(ifindex, "%i", link->ifindex);
793
794 r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifindex, &p);
795 if (r < 0)
796 return NULL;
797
798 return p;
799 }
800
801 static int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
802 _cleanup_strv_free_ char **l = NULL;
803 Manager *m = ASSERT_PTR(userdata);
804 Link *link;
805 unsigned c = 0;
806
807 assert(bus);
808 assert(path);
809 assert(nodes);
810
811 l = new0(char*, hashmap_size(m->links) + 1);
812 if (!l)
813 return -ENOMEM;
814
815 HASHMAP_FOREACH(link, m->links) {
816 char *p;
817
818 p = link_bus_path(link);
819 if (!p)
820 return -ENOMEM;
821
822 l[c++] = p;
823 }
824
825 l[c] = NULL;
826 *nodes = TAKE_PTR(l);
827
828 return 1;
829 }
830
831 static const sd_bus_vtable link_vtable[] = {
832 SD_BUS_VTABLE_START(0),
833
834 SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
835 SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
836 SD_BUS_PROPERTY("DNSEx", "a(iayqs)", property_get_dns_ex, 0, 0),
837 SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0),
838 SD_BUS_PROPERTY("CurrentDNSServerEx", "(iayqs)", property_get_current_dns_server_ex, offsetof(Link, current_dns_server), 0),
839 SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
840 SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
841 SD_BUS_PROPERTY("LLMNR", "s", property_get_llmnr_support, 0, 0),
842 SD_BUS_PROPERTY("MulticastDNS", "s", property_get_mdns_support, 0, 0),
843 SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0),
844 SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
845 SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_string_set, offsetof(Link, dnssec_negative_trust_anchors), 0),
846 SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0),
847
848 SD_BUS_METHOD_WITH_ARGS("SetDNS",
849 SD_BUS_ARGS("a(iay)", addresses),
850 SD_BUS_NO_RESULT,
851 bus_link_method_set_dns_servers,
852 SD_BUS_VTABLE_UNPRIVILEGED),
853 SD_BUS_METHOD_WITH_ARGS("SetDNSEx",
854 SD_BUS_ARGS("a(iayqs)", addresses),
855 SD_BUS_NO_RESULT,
856 bus_link_method_set_dns_servers_ex,
857 SD_BUS_VTABLE_UNPRIVILEGED),
858 SD_BUS_METHOD_WITH_ARGS("SetDomains",
859 SD_BUS_ARGS("a(sb)", domains),
860 SD_BUS_NO_RESULT,
861 bus_link_method_set_domains,
862 SD_BUS_VTABLE_UNPRIVILEGED),
863 SD_BUS_METHOD_WITH_ARGS("SetDefaultRoute",
864 SD_BUS_ARGS("b", enable),
865 SD_BUS_NO_RESULT,
866 bus_link_method_set_default_route,
867 SD_BUS_VTABLE_UNPRIVILEGED),
868 SD_BUS_METHOD_WITH_ARGS("SetLLMNR",
869 SD_BUS_ARGS("s", mode),
870 SD_BUS_NO_RESULT,
871 bus_link_method_set_llmnr,
872 SD_BUS_VTABLE_UNPRIVILEGED),
873 SD_BUS_METHOD_WITH_ARGS("SetMulticastDNS",
874 SD_BUS_ARGS("s", mode),
875 SD_BUS_NO_RESULT,
876 bus_link_method_set_mdns,
877 SD_BUS_VTABLE_UNPRIVILEGED),
878 SD_BUS_METHOD_WITH_ARGS("SetDNSOverTLS",
879 SD_BUS_ARGS("s", mode),
880 SD_BUS_NO_RESULT,
881 bus_link_method_set_dns_over_tls,
882 SD_BUS_VTABLE_UNPRIVILEGED),
883 SD_BUS_METHOD_WITH_ARGS("SetDNSSEC",
884 SD_BUS_ARGS("s", mode),
885 SD_BUS_NO_RESULT,
886 bus_link_method_set_dnssec,
887 SD_BUS_VTABLE_UNPRIVILEGED),
888 SD_BUS_METHOD_WITH_ARGS("SetDNSSECNegativeTrustAnchors",
889 SD_BUS_ARGS("as", names),
890 SD_BUS_NO_RESULT,
891 bus_link_method_set_dnssec_negative_trust_anchors,
892 SD_BUS_VTABLE_UNPRIVILEGED),
893 SD_BUS_METHOD_WITH_ARGS("Revert",
894 SD_BUS_NO_ARGS,
895 SD_BUS_NO_RESULT,
896 bus_link_method_revert,
897 SD_BUS_VTABLE_UNPRIVILEGED),
898
899 SD_BUS_VTABLE_END
900 };
901
902 const BusObjectImplementation link_object = {
903 "/org/freedesktop/resolve1/link",
904 "org.freedesktop.resolve1.Link",
905 .fallback_vtables = BUS_FALLBACK_VTABLES({link_vtable, link_object_find}),
906 .node_enumerator = link_node_enumerator,
907 };