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