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