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