]> git.proxmox.com Git - systemd.git/blame - src/resolve/resolvectl.c
bump version to 252.11-pve1
[systemd.git] / src / resolve / resolvectl.c
CommitLineData
a032b68d 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
4c89c718
MP
2
3#include <getopt.h>
bb4f798a 4#include <locale.h>
4c89c718
MP
5#include <net/if.h>
6
7#include "sd-bus.h"
5a920b42 8#include "sd-netlink.h"
4c89c718
MP
9
10#include "af-list.h"
11#include "alloc-util.h"
52ad194e 12#include "bus-common-errors.h"
4c89c718 13#include "bus-error.h"
a10f5d05
MB
14#include "bus-locator.h"
15#include "bus-map-properties.h"
16#include "bus-message-util.h"
52ad194e 17#include "dns-domain.h"
086111aa 18#include "errno-list.h"
4c89c718 19#include "escape.h"
46cdbd49 20#include "format-table.h"
f2dec872 21#include "format-util.h"
aa27b158 22#include "gcrypt-util.h"
3a6ce677 23#include "hostname-util.h"
086111aa 24#include "json.h"
6e866b33
MB
25#include "main-func.h"
26#include "missing_network.h"
5a920b42 27#include "netlink-util.h"
ea0999c9 28#include "openssl-util.h"
5a920b42 29#include "pager.h"
3a6ce677 30#include "parse-argument.h"
4c89c718 31#include "parse-util.h"
6e866b33 32#include "pretty-print.h"
3a6ce677 33#include "process-util.h"
b012e921
MB
34#include "resolvconf-compat.h"
35#include "resolvectl.h"
4c89c718
MP
36#include "resolved-def.h"
37#include "resolved-dns-packet.h"
3a6ce677 38#include "resolved-util.h"
46cdbd49 39#include "socket-netlink.h"
a10f5d05 40#include "sort-util.h"
46cdbd49 41#include "stdio-util.h"
b012e921 42#include "string-table.h"
5a920b42 43#include "strv.h"
4c89c718 44#include "terminal-util.h"
a032b68d 45#include "utf8.h"
086111aa 46#include "varlink.h"
ea0999c9 47#include "verb-log-control.h"
b012e921 48#include "verbs.h"
4c89c718 49
4c89c718 50static int arg_family = AF_UNSPEC;
6e866b33
MB
51static int arg_ifindex = 0;
52static char *arg_ifname = NULL;
4c89c718
MP
53static uint16_t arg_type = 0;
54static uint16_t arg_class = 0;
55static bool arg_legend = true;
56static uint64_t arg_flags = 0;
086111aa 57static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
6e866b33 58static PagerFlags arg_pager_flags = 0;
b012e921
MB
59bool arg_ifindex_permissive = false; /* If true, don't generate an error if the specified interface index doesn't exist */
60static const char *arg_service_family = NULL;
aa27b158
MP
61
62typedef enum RawType {
63 RAW_NONE,
64 RAW_PAYLOAD,
65 RAW_PACKET,
66} RawType;
67static RawType arg_raw = RAW_NONE;
68
b012e921 69ExecutionMode arg_mode = MODE_RESOLVE_HOST;
52ad194e 70
b012e921
MB
71char **arg_set_dns = NULL;
72char **arg_set_domain = NULL;
73static const char *arg_set_llmnr = NULL;
74static const char *arg_set_mdns = NULL;
75static const char *arg_set_dns_over_tls = NULL;
76static const char *arg_set_dnssec = NULL;
77static char **arg_set_nta = NULL;
aa27b158 78
6e866b33
MB
79STATIC_DESTRUCTOR_REGISTER(arg_ifname, freep);
80STATIC_DESTRUCTOR_REGISTER(arg_set_dns, strv_freep);
81STATIC_DESTRUCTOR_REGISTER(arg_set_domain, strv_freep);
82STATIC_DESTRUCTOR_REGISTER(arg_set_nta, strv_freep);
83
b012e921
MB
84typedef enum StatusMode {
85 STATUS_ALL,
86 STATUS_DNS,
87 STATUS_DOMAIN,
6e866b33 88 STATUS_DEFAULT_ROUTE,
b012e921
MB
89 STATUS_LLMNR,
90 STATUS_MDNS,
91 STATUS_PRIVATE,
92 STATUS_DNSSEC,
93 STATUS_NTA,
94} StatusMode;
95
a10f5d05
MB
96typedef struct InterfaceInfo {
97 int index;
98 const char *name;
99} InterfaceInfo;
100
101static int interface_info_compare(const InterfaceInfo *a, const InterfaceInfo *b) {
102 int r;
103
104 r = CMP(a->index, b->index);
105 if (r != 0)
106 return r;
107
108 return strcmp_ptr(a->name, b->name);
109}
110
2345c4ad
LB
111int ifname_mangle_full(const char *s, bool drop_protocol_specifier) {
112 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
113 _cleanup_strv_free_ char **found = NULL;
114 int r;
6e866b33
MB
115
116 assert(s);
117
2345c4ad
LB
118 if (drop_protocol_specifier) {
119 _cleanup_free_ char *buf = NULL;
120 int ifindex_longest_name = -ENODEV;
6e866b33 121
2345c4ad 122 /* When invoked as resolvconf, drop the protocol specifier(s) at the end. */
f2dec872 123
2345c4ad
LB
124 buf = strdup(s);
125 if (!buf)
126 return log_oom();
6e866b33 127
2345c4ad
LB
128 for (;;) {
129 r = rtnl_resolve_interface(&rtnl, buf);
130 if (r > 0) {
131 if (ifindex_longest_name <= 0)
132 ifindex_longest_name = r;
6e866b33 133
2345c4ad
LB
134 r = strv_extend(&found, buf);
135 if (r < 0)
136 return log_oom();
137 }
6e866b33 138
2345c4ad
LB
139 char *dot = strrchr(buf, '.');
140 if (!dot)
141 break;
6e866b33 142
2345c4ad
LB
143 *dot = '\0';
144 }
626cb2db 145
2345c4ad
LB
146 unsigned n = strv_length(found);
147 if (n > 1) {
148 _cleanup_free_ char *joined = NULL;
626cb2db 149
2345c4ad
LB
150 joined = strv_join(found, ", ");
151 log_warning("Found multiple interfaces (%s) matching with '%s'. Using '%s' (ifindex=%i).",
152 strna(joined), s, found[0], ifindex_longest_name);
626cb2db 153
2345c4ad
LB
154 } else if (n == 1) {
155 const char *proto;
156
157 proto = ASSERT_PTR(startswith(s, found[0]));
158 if (!isempty(proto))
159 log_info("Dropped protocol specifier '%s' from '%s'. Using '%s' (ifindex=%i).",
160 proto, s, found[0], ifindex_longest_name);
161 }
162
163 r = ifindex_longest_name;
626cb2db 164 } else
2345c4ad
LB
165 r = rtnl_resolve_interface(&rtnl, s);
166 if (r < 0) {
167 if (ERRNO_IS_DEVICE_ABSENT(r) && arg_ifindex_permissive) {
168 log_debug_errno(r, "Interface '%s' not found, but -f specified, ignoring: %m", s);
169 return 0; /* done */
170 }
171 return log_error_errno(r, "Failed to resolve interface \"%s\": %m", s);
172 }
173
174 if (arg_ifindex > 0 && arg_ifindex != r)
175 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified multiple different interfaces. Refusing.");
176
177 arg_ifindex = r;
178 return free_and_strdup_warn(&arg_ifname, found ? found[0] : s); /* found */
626cb2db
MB
179}
180
4c89c718 181static void print_source(uint64_t flags, usec_t rtt) {
4c89c718
MP
182 if (!arg_legend)
183 return;
184
185 if (flags == 0)
186 return;
187
6e866b33 188 printf("\n%s-- Information acquired via", ansi_grey());
4c89c718 189
9e294e28
MB
190 printf(" protocol%s%s%s%s%s",
191 flags & SD_RESOLVED_DNS ? " DNS" :"",
192 flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
193 flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
194 flags & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
195 flags & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
4c89c718 196
6e866b33 197 printf(" in %s.%s\n"
3a6ce677 198 "%s-- Data is authenticated: %s; Data was acquired via local or encrypted transport: %s%s\n",
ea0999c9
MB
199 FORMAT_TIMESPAN(rtt, 100),
200 ansi_normal(),
3a6ce677
BR
201 ansi_grey(),
202 yes_no(flags & SD_RESOLVED_AUTHENTICATED),
203 yes_no(flags & SD_RESOLVED_CONFIDENTIAL),
204 ansi_normal());
205
206 if ((flags & (SD_RESOLVED_FROM_MASK|SD_RESOLVED_SYNTHETIC)) != 0)
207 printf("%s-- Data from:%s%s%s%s%s%s\n",
208 ansi_grey(),
209 FLAGS_SET(flags, SD_RESOLVED_SYNTHETIC) ? " synthetic" : "",
210 FLAGS_SET(flags, SD_RESOLVED_FROM_CACHE) ? " cache" : "",
211 FLAGS_SET(flags, SD_RESOLVED_FROM_ZONE) ? " zone" : "",
212 FLAGS_SET(flags, SD_RESOLVED_FROM_TRUST_ANCHOR) ? " trust-anchor" : "",
213 FLAGS_SET(flags, SD_RESOLVED_FROM_NETWORK) ? " network" : "",
214 ansi_normal());
6e866b33 215}
4c89c718 216
6e866b33 217static void print_ifindex_comment(int printed_so_far, int ifindex) {
ea0999c9
MB
218 char ifname[IF_NAMESIZE];
219 int r;
4c89c718 220
6e866b33
MB
221 if (ifindex <= 0)
222 return;
223
ea0999c9
MB
224 r = format_ifname(ifindex, ifname);
225 if (r < 0)
226 return (void) log_warning_errno(r, "Failed to resolve interface name for index %i, ignoring: %m", ifindex);
227
228 printf("%*s%s-- link: %s%s",
229 60 > printed_so_far ? 60 - printed_so_far : 0, " ", /* Align comment to the 60th column */
230 ansi_grey(), ifname, ansi_normal());
4c89c718
MP
231}
232
82126c13
LB
233static int resolve_host_error(const char *name, int r, const sd_bus_error *error) {
234 if (sd_bus_error_has_name(error, BUS_ERROR_DNS_NXDOMAIN))
235 return log_error_errno(r, "%s: %s", name, bus_error_message(error, r));
236
237 return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(error, r));
238}
239
4c89c718 240static int resolve_host(sd_bus *bus, const char *name) {
4c89c718
MP
241 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
242 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
243 const char *canonical = NULL;
4c89c718 244 unsigned c = 0;
4c89c718
MP
245 uint64_t flags;
246 usec_t ts;
6e866b33 247 int r;
4c89c718
MP
248
249 assert(name);
250
6e866b33 251 log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
4c89c718 252
a10f5d05 253 r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname");
4c89c718
MP
254 if (r < 0)
255 return bus_log_create_error(r);
256
257 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
258 if (r < 0)
259 return bus_log_create_error(r);
260
261 ts = now(CLOCK_MONOTONIC);
262
1d42b86d 263 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
4c89c718 264 if (r < 0)
82126c13 265 return resolve_host_error(name, r, &error);
4c89c718
MP
266
267 ts = now(CLOCK_MONOTONIC) - ts;
268
269 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
270 if (r < 0)
271 return bus_log_parse_error(r);
272
273 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
274 _cleanup_free_ char *pretty = NULL;
6e866b33 275 int ifindex, family, k;
a10f5d05 276 union in_addr_union a;
4c89c718
MP
277
278 assert_cc(sizeof(int) == sizeof(int32_t));
279
a10f5d05 280 r = sd_bus_message_read(reply, "i", &ifindex);
4c89c718
MP
281 if (r < 0)
282 return bus_log_parse_error(r);
283
a10f5d05
MB
284 sd_bus_error_free(&error);
285 r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
286 if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
287 return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
4c89c718
MP
288
289 r = sd_bus_message_exit_container(reply);
290 if (r < 0)
291 return bus_log_parse_error(r);
292
a10f5d05
MB
293 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
294 log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
4c89c718
MP
295 continue;
296 }
297
a10f5d05 298 r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
4c89c718
MP
299 if (r < 0)
300 return log_error_errno(r, "Failed to print address for %s: %m", name);
301
6e866b33
MB
302 k = printf("%*s%s %s%s%s",
303 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
304 ansi_highlight(), pretty, ansi_normal());
305
306 print_ifindex_comment(k, ifindex);
307 fputc('\n', stdout);
4c89c718
MP
308
309 c++;
310 }
311 if (r < 0)
312 return bus_log_parse_error(r);
313
314 r = sd_bus_message_exit_container(reply);
315 if (r < 0)
316 return bus_log_parse_error(r);
317
318 r = sd_bus_message_read(reply, "st", &canonical, &flags);
319 if (r < 0)
320 return bus_log_parse_error(r);
321
322 if (!streq(name, canonical))
323 printf("%*s%s (%s)\n",
324 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
325 canonical);
326
a032b68d
MB
327 if (c == 0)
328 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
329 "%s: no addresses found", name);
4c89c718
MP
330
331 print_source(flags, ts);
332
333 return 0;
334}
335
336static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
337 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
338 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
339 _cleanup_free_ char *pretty = NULL;
4c89c718
MP
340 uint64_t flags;
341 unsigned c = 0;
342 usec_t ts;
343 int r;
344
345 assert(bus);
346 assert(IN_SET(family, AF_INET, AF_INET6));
347 assert(address);
348
349 if (ifindex <= 0)
350 ifindex = arg_ifindex;
351
5a920b42 352 r = in_addr_ifindex_to_string(family, address, ifindex, &pretty);
4c89c718
MP
353 if (r < 0)
354 return log_oom();
355
6e866b33 356 log_debug("Resolving %s.", pretty);
4c89c718 357
a10f5d05 358 r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveAddress");
4c89c718
MP
359 if (r < 0)
360 return bus_log_create_error(r);
361
362 r = sd_bus_message_append(req, "ii", ifindex, family);
363 if (r < 0)
364 return bus_log_create_error(r);
365
366 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
367 if (r < 0)
368 return bus_log_create_error(r);
369
370 r = sd_bus_message_append(req, "t", arg_flags);
371 if (r < 0)
372 return bus_log_create_error(r);
373
374 ts = now(CLOCK_MONOTONIC);
375
1d42b86d 376 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
6e866b33
MB
377 if (r < 0)
378 return log_error_errno(r, "%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
4c89c718
MP
379
380 ts = now(CLOCK_MONOTONIC) - ts;
381
382 r = sd_bus_message_enter_container(reply, 'a', "(is)");
383 if (r < 0)
384 return bus_log_create_error(r);
385
386 while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) {
387 const char *n;
6e866b33 388 int k;
4c89c718
MP
389
390 assert_cc(sizeof(int) == sizeof(int32_t));
391
392 r = sd_bus_message_read(reply, "is", &ifindex, &n);
393 if (r < 0)
394 return r;
395
396 r = sd_bus_message_exit_container(reply);
397 if (r < 0)
398 return r;
399
6e866b33
MB
400 k = printf("%*s%s %s%s%s",
401 (int) strlen(pretty), c == 0 ? pretty : "",
402 c == 0 ? ":" : " ",
403 ansi_highlight(), n, ansi_normal());
4c89c718 404
6e866b33
MB
405 print_ifindex_comment(k, ifindex);
406 fputc('\n', stdout);
4c89c718
MP
407
408 c++;
409 }
410 if (r < 0)
411 return bus_log_parse_error(r);
412
413 r = sd_bus_message_exit_container(reply);
414 if (r < 0)
415 return bus_log_parse_error(r);
416
417 r = sd_bus_message_read(reply, "t", &flags);
418 if (r < 0)
419 return bus_log_parse_error(r);
420
a032b68d
MB
421 if (c == 0)
422 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
423 "%s: no names found", pretty);
4c89c718
MP
424
425 print_source(flags, ts);
426
427 return 0;
428}
429
aa27b158
MP
430static int output_rr_packet(const void *d, size_t l, int ifindex) {
431 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
aa27b158 432 int r;
aa27b158 433
086111aa 434 r = dns_resource_record_new_from_raw(&rr, d, l);
aa27b158
MP
435 if (r < 0)
436 return log_error_errno(r, "Failed to parse RR: %m");
437
438 if (arg_raw == RAW_PAYLOAD) {
439 void *data;
440 ssize_t k;
441
442 k = dns_resource_record_payload(rr, &data);
443 if (k < 0)
444 return log_error_errno(k, "Cannot dump RR: %m");
445 fwrite(data, 1, k, stdout);
446 } else {
447 const char *s;
6e866b33 448 int k;
aa27b158
MP
449
450 s = dns_resource_record_to_string(rr);
451 if (!s)
452 return log_oom();
453
6e866b33
MB
454 k = printf("%s", s);
455 print_ifindex_comment(k, ifindex);
456 fputc('\n', stdout);
aa27b158
MP
457 }
458
459 return 0;
460}
461
3a6ce677
BR
462static int idna_candidate(const char *name, char **ret) {
463 _cleanup_free_ char *idnafied = NULL;
464 int r;
465
466 assert(name);
467 assert(ret);
468
469 r = dns_name_apply_idna(name, &idnafied);
470 if (r < 0)
471 return log_error_errno(r, "Failed to apply IDNA to name '%s': %m", name);
472 if (r > 0 && !streq(name, idnafied)) {
473 *ret = TAKE_PTR(idnafied);
474 return true;
475 }
476
477 *ret = NULL;
478 return false;
479}
480
481static bool single_label_nonsynthetic(const char *name) {
482 _cleanup_free_ char *first_label = NULL;
483 int r;
484
485 if (!dns_name_is_single_label(name))
486 return false;
487
488 if (is_localhost(name) || is_gateway_hostname(name))
489 return false;
490
491 r = resolve_system_hostname(NULL, &first_label);
492 if (r < 0) {
493 log_warning_errno(r, "Failed to determine the hostname: %m");
494 return false;
495 }
496
497 return !streq(name, first_label);
498}
499
8a584da2 500static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) {
4c89c718
MP
501 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
502 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3a6ce677
BR
503 _cleanup_free_ char *idnafied = NULL;
504 bool needs_authentication = false;
4c89c718
MP
505 unsigned n = 0;
506 uint64_t flags;
4c89c718 507 usec_t ts;
3a6ce677 508 int r;
4c89c718
MP
509
510 assert(name);
511
6e866b33 512 log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(arg_ifname) ? "*" : arg_ifname);
4c89c718 513
3a6ce677
BR
514 if (dns_name_dot_suffixed(name) == 0 && single_label_nonsynthetic(name))
515 log_notice("(Note that search domains are not appended when --type= is specified. "
516 "Please specify fully qualified domain names, or remove --type= switch from invocation in order to request regular hostname resolution.)");
517
518 r = idna_candidate(name, &idnafied);
519 if (r < 0)
520 return r;
521 if (r > 0)
522 log_notice("(Note that IDNA translation is not applied when --type= is specified. "
523 "Please specify translated domain names — i.e. '%s' — when resolving raw records, or remove --type= switch from invocation in order to request regular hostname resolution.",
524 idnafied);
525
a10f5d05 526 r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveRecord");
4c89c718
MP
527 if (r < 0)
528 return bus_log_create_error(r);
529
530 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, arg_flags);
531 if (r < 0)
532 return bus_log_create_error(r);
533
534 ts = now(CLOCK_MONOTONIC);
535
1d42b86d 536 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
4c89c718 537 if (r < 0) {
8a584da2
MP
538 if (warn_missing || r != -ENXIO)
539 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
4c89c718
MP
540 return r;
541 }
542
543 ts = now(CLOCK_MONOTONIC) - ts;
544
545 r = sd_bus_message_enter_container(reply, 'a', "(iqqay)");
546 if (r < 0)
547 return bus_log_parse_error(r);
548
549 while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
4c89c718
MP
550 uint16_t c, t;
551 int ifindex;
552 const void *d;
553 size_t l;
554
555 assert_cc(sizeof(int) == sizeof(int32_t));
556
557 r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t);
558 if (r < 0)
559 return bus_log_parse_error(r);
560
561 r = sd_bus_message_read_array(reply, 'y', &d, &l);
562 if (r < 0)
563 return bus_log_parse_error(r);
564
565 r = sd_bus_message_exit_container(reply);
566 if (r < 0)
567 return bus_log_parse_error(r);
568
aa27b158
MP
569 if (arg_raw == RAW_PACKET) {
570 uint64_t u64 = htole64(l);
4c89c718 571
aa27b158
MP
572 fwrite(&u64, sizeof(u64), 1, stdout);
573 fwrite(d, 1, l, stdout);
574 } else {
575 r = output_rr_packet(d, l, ifindex);
576 if (r < 0)
577 return r;
578 }
4c89c718 579
aa27b158
MP
580 if (dns_type_needs_authentication(t))
581 needs_authentication = true;
4c89c718 582
4c89c718
MP
583 n++;
584 }
585 if (r < 0)
586 return bus_log_parse_error(r);
587
588 r = sd_bus_message_exit_container(reply);
589 if (r < 0)
590 return bus_log_parse_error(r);
591
592 r = sd_bus_message_read(reply, "t", &flags);
593 if (r < 0)
594 return bus_log_parse_error(r);
595
596 if (n == 0) {
8a584da2
MP
597 if (warn_missing)
598 log_error("%s: no records found", name);
4c89c718
MP
599 return -ESRCH;
600 }
601
602 print_source(flags, ts);
603
aa27b158
MP
604 if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) {
605 fflush(stdout);
606
607 fprintf(stderr, "\n%s"
608 "WARNING: The resources shown contain cryptographic key data which could not be\n"
609 " authenticated. It is not suitable to authenticate any communication.\n"
610 " This is usually indication that DNSSEC authentication was not enabled\n"
611 " or is not available for the selected protocol or DNS servers.%s\n",
612 ansi_highlight_red(),
613 ansi_normal());
614 }
615
4c89c718
MP
616 return 0;
617}
618
619static int resolve_rfc4501(sd_bus *bus, const char *name) {
620 uint16_t type = 0, class = 0;
621 const char *p, *q, *n;
622 int r;
623
624 assert(bus);
625 assert(name);
626 assert(startswith(name, "dns:"));
627
628 /* Parse RFC 4501 dns: URIs */
629
630 p = name + 4;
631
632 if (p[0] == '/') {
633 const char *e;
634
635 if (p[1] != '/')
636 goto invalid;
637
638 e = strchr(p + 2, '/');
639 if (!e)
640 goto invalid;
641
642 if (e != p + 2)
643 log_warning("DNS authority specification not supported; ignoring specified authority.");
644
645 p = e + 1;
646 }
647
648 q = strchr(p, '?');
649 if (q) {
ea0999c9 650 n = strndupa_safe(p, q - p);
4c89c718
MP
651 q++;
652
653 for (;;) {
654 const char *f;
655
656 f = startswith_no_case(q, "class=");
657 if (f) {
658 _cleanup_free_ char *t = NULL;
659 const char *e;
660
6e866b33
MB
661 if (class != 0)
662 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
663 "DNS class specified twice.");
4c89c718
MP
664
665 e = strchrnul(f, ';');
666 t = strndup(f, e - f);
667 if (!t)
668 return log_oom();
669
670 r = dns_class_from_string(t);
6e866b33 671 if (r < 0)
3a6ce677 672 return log_error_errno(r, "Unknown DNS class %s.", t);
4c89c718
MP
673
674 class = r;
675
676 if (*e == ';') {
677 q = e + 1;
678 continue;
679 }
680
681 break;
682 }
683
684 f = startswith_no_case(q, "type=");
685 if (f) {
686 _cleanup_free_ char *t = NULL;
687 const char *e;
688
6e866b33
MB
689 if (type != 0)
690 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
691 "DNS type specified twice.");
4c89c718
MP
692
693 e = strchrnul(f, ';');
694 t = strndup(f, e - f);
695 if (!t)
696 return log_oom();
697
698 r = dns_type_from_string(t);
6e866b33 699 if (r < 0)
3a6ce677 700 return log_error_errno(r, "Unknown DNS type %s: %m", t);
4c89c718
MP
701
702 type = r;
703
704 if (*e == ';') {
705 q = e + 1;
706 continue;
707 }
708
709 break;
710 }
711
712 goto invalid;
713 }
714 } else
715 n = p;
716
4c89c718 717 if (class == 0)
aa27b158
MP
718 class = arg_class ?: DNS_CLASS_IN;
719 if (type == 0)
720 type = arg_type ?: DNS_TYPE_A;
4c89c718 721
8a584da2 722 return resolve_record(bus, n, class, type, true);
4c89c718
MP
723
724invalid:
6e866b33
MB
725 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
726 "Invalid DNS URI: %s", name);
4c89c718
MP
727}
728
b012e921
MB
729static int verb_query(int argc, char **argv, void *userdata) {
730 sd_bus *bus = userdata;
b012e921
MB
731 int q, r = 0;
732
733 if (arg_type != 0)
734 STRV_FOREACH(p, argv + 1) {
735 q = resolve_record(bus, *p, arg_class, arg_type, true);
736 if (q < 0)
737 r = q;
738 }
739
740 else
741 STRV_FOREACH(p, argv + 1) {
742 if (startswith(*p, "dns:"))
743 q = resolve_rfc4501(bus, *p);
744 else {
745 int family, ifindex;
746 union in_addr_union a;
747
748 q = in_addr_ifindex_from_string_auto(*p, &family, &a, &ifindex);
749 if (q >= 0)
750 q = resolve_address(bus, family, &a, ifindex);
751 else
752 q = resolve_host(bus, *p);
753 }
754 if (q < 0)
755 r = q;
756 }
757
758 return r;
759}
760
4c89c718
MP
761static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
762 const char *canonical_name, *canonical_type, *canonical_domain;
763 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
764 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
4c89c718
MP
765 size_t indent, sz;
766 uint64_t flags;
767 const char *p;
768 unsigned c;
769 usec_t ts;
770 int r;
771
772 assert(bus);
773 assert(domain);
774
5a920b42
MP
775 name = empty_to_null(name);
776 type = empty_to_null(type);
4c89c718 777
4c89c718 778 if (name)
6e866b33 779 log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
4c89c718 780 else if (type)
6e866b33 781 log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
4c89c718 782 else
6e866b33 783 log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
4c89c718 784
a10f5d05 785 r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveService");
4c89c718
MP
786 if (r < 0)
787 return bus_log_create_error(r);
788
789 r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags);
790 if (r < 0)
791 return bus_log_create_error(r);
792
793 ts = now(CLOCK_MONOTONIC);
794
1d42b86d 795 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
4c89c718
MP
796 if (r < 0)
797 return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
798
799 ts = now(CLOCK_MONOTONIC) - ts;
800
801 r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)");
802 if (r < 0)
803 return bus_log_parse_error(r);
804
805 indent =
806 (name ? strlen(name) + 1 : 0) +
807 (type ? strlen(type) + 1 : 0) +
808 strlen(domain) + 2;
809
810 c = 0;
811 while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) {
812 uint16_t priority, weight, port;
813 const char *hostname, *canonical;
814
815 r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname);
816 if (r < 0)
817 return bus_log_parse_error(r);
818
819 if (name)
820 printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " ");
821 if (type)
822 printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " ");
823
824 printf("%*s%s %s:%u [priority=%u, weight=%u]\n",
825 (int) strlen(domain), c == 0 ? domain : "",
826 c == 0 ? ":" : " ",
827 hostname, port,
828 priority, weight);
829
830 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
831 if (r < 0)
832 return bus_log_parse_error(r);
833
834 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
835 _cleanup_free_ char *pretty = NULL;
6e866b33 836 int ifindex, family, k;
086111aa 837 union in_addr_union a;
4c89c718
MP
838
839 assert_cc(sizeof(int) == sizeof(int32_t));
840
a10f5d05 841 r = sd_bus_message_read(reply, "i", &ifindex);
4c89c718
MP
842 if (r < 0)
843 return bus_log_parse_error(r);
844
a10f5d05
MB
845 sd_bus_error_free(&error);
846 r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
847 if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
848 return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
4c89c718
MP
849
850 r = sd_bus_message_exit_container(reply);
851 if (r < 0)
852 return bus_log_parse_error(r);
853
a10f5d05
MB
854 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
855 log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
4c89c718
MP
856 continue;
857 }
858
a10f5d05 859 r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
4c89c718
MP
860 if (r < 0)
861 return log_error_errno(r, "Failed to print address for %s: %m", name);
862
6e866b33
MB
863 k = printf("%*s%s", (int) indent, "", pretty);
864 print_ifindex_comment(k, ifindex);
865 fputc('\n', stdout);
4c89c718
MP
866 }
867 if (r < 0)
868 return bus_log_parse_error(r);
869
870 r = sd_bus_message_exit_container(reply);
871 if (r < 0)
872 return bus_log_parse_error(r);
873
874 r = sd_bus_message_read(reply, "s", &canonical);
875 if (r < 0)
876 return bus_log_parse_error(r);
877
878 if (!streq(hostname, canonical))
879 printf("%*s(%s)\n", (int) indent, "", canonical);
880
881 r = sd_bus_message_exit_container(reply);
882 if (r < 0)
883 return bus_log_parse_error(r);
884
885 c++;
886 }
887 if (r < 0)
888 return bus_log_parse_error(r);
889
890 r = sd_bus_message_exit_container(reply);
891 if (r < 0)
892 return bus_log_parse_error(r);
893
894 r = sd_bus_message_enter_container(reply, 'a', "ay");
895 if (r < 0)
896 return bus_log_parse_error(r);
897
4c89c718
MP
898 while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) {
899 _cleanup_free_ char *escaped = NULL;
900
901 escaped = cescape_length(p, sz);
902 if (!escaped)
903 return log_oom();
904
905 printf("%*s%s\n", (int) indent, "", escaped);
4c89c718
MP
906 }
907 if (r < 0)
908 return bus_log_parse_error(r);
909
910 r = sd_bus_message_exit_container(reply);
911 if (r < 0)
912 return bus_log_parse_error(r);
913
914 r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags);
915 if (r < 0)
916 return bus_log_parse_error(r);
917
5a920b42
MP
918 canonical_name = empty_to_null(canonical_name);
919 canonical_type = empty_to_null(canonical_type);
4c89c718
MP
920
921 if (!streq_ptr(name, canonical_name) ||
922 !streq_ptr(type, canonical_type) ||
923 !streq_ptr(domain, canonical_domain)) {
924
925 printf("%*s(", (int) indent, "");
926
927 if (canonical_name)
928 printf("%s/", canonical_name);
929 if (canonical_type)
930 printf("%s/", canonical_type);
931
932 printf("%s)\n", canonical_domain);
933 }
934
935 print_source(flags, ts);
936
937 return 0;
938}
939
b012e921
MB
940static int verb_service(int argc, char **argv, void *userdata) {
941 sd_bus *bus = userdata;
942
943 if (argc == 2)
944 return resolve_service(bus, NULL, NULL, argv[1]);
945 else if (argc == 3)
946 return resolve_service(bus, NULL, argv[1], argv[2]);
947 else
948 return resolve_service(bus, argv[1], argv[2], argv[3]);
949}
950
aa27b158
MP
951static int resolve_openpgp(sd_bus *bus, const char *address) {
952 const char *domain, *full;
953 int r;
954 _cleanup_free_ char *hashed = NULL;
955
956 assert(bus);
957 assert(address);
958
959 domain = strrchr(address, '@');
6e866b33
MB
960 if (!domain)
961 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
962 "Address does not contain '@': \"%s\"", address);
963 if (domain == address || domain[1] == '\0')
964 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
965 "Address starts or ends with '@': \"%s\"", address);
aa27b158
MP
966 domain++;
967
8a584da2 968 r = string_hashsum_sha256(address, domain - 1 - address, &hashed);
aa27b158
MP
969 if (r < 0)
970 return log_error_errno(r, "Hashing failed: %m");
971
8a584da2
MP
972 strshorten(hashed, 56);
973
aa27b158
MP
974 full = strjoina(hashed, "._openpgpkey.", domain);
975 log_debug("Looking up \"%s\".", full);
976
8a584da2
MP
977 r = resolve_record(bus, full,
978 arg_class ?: DNS_CLASS_IN,
979 arg_type ?: DNS_TYPE_OPENPGPKEY, false);
980
981 if (IN_SET(r, -ENXIO, -ESRCH)) { /* NXDOMAIN or NODATA? */
6e866b33 982 hashed = mfree(hashed);
8a584da2
MP
983 r = string_hashsum_sha224(address, domain - 1 - address, &hashed);
984 if (r < 0)
985 return log_error_errno(r, "Hashing failed: %m");
986
987 full = strjoina(hashed, "._openpgpkey.", domain);
988 log_debug("Looking up \"%s\".", full);
989
990 return resolve_record(bus, full,
991 arg_class ?: DNS_CLASS_IN,
992 arg_type ?: DNS_TYPE_OPENPGPKEY, true);
993 }
994
995 return r;
aa27b158
MP
996}
997
b012e921
MB
998static int verb_openpgp(int argc, char **argv, void *userdata) {
999 sd_bus *bus = userdata;
b012e921
MB
1000 int q, r = 0;
1001
1002 STRV_FOREACH(p, argv + 1) {
1003 q = resolve_openpgp(bus, *p);
1004 if (q < 0)
1005 r = q;
1006 }
1007
1008 return r;
1009}
1010
1011static int resolve_tlsa(sd_bus *bus, const char *family, const char *address) {
aa27b158
MP
1012 const char *port;
1013 uint16_t port_num = 443;
1014 _cleanup_free_ char *full = NULL;
1015 int r;
1016
1017 assert(bus);
1018 assert(address);
1019
1020 port = strrchr(address, ':');
1021 if (port) {
2897b343
MP
1022 r = parse_ip_port(port + 1, &port_num);
1023 if (r < 0)
aa27b158
MP
1024 return log_error_errno(r, "Invalid port \"%s\".", port + 1);
1025
ea0999c9 1026 address = strndupa_safe(address, port - address);
aa27b158
MP
1027 }
1028
b012e921 1029 r = asprintf(&full, "_%u._%s.%s",
aa27b158 1030 port_num,
b012e921 1031 family,
aa27b158
MP
1032 address);
1033 if (r < 0)
1034 return log_oom();
1035
1036 log_debug("Looking up \"%s\".", full);
1037
1038 return resolve_record(bus, full,
1039 arg_class ?: DNS_CLASS_IN,
8a584da2 1040 arg_type ?: DNS_TYPE_TLSA, true);
aa27b158
MP
1041}
1042
b012e921
MB
1043static bool service_family_is_valid(const char *s) {
1044 return STR_IN_SET(s, "tcp", "udp", "sctp");
1045}
1046
1047static int verb_tlsa(int argc, char **argv, void *userdata) {
1048 sd_bus *bus = userdata;
f5caa8fa 1049 char **args = argv + 1;
b012e921
MB
1050 const char *family = "tcp";
1051 int q, r = 0;
1052
1053 if (service_family_is_valid(argv[1])) {
1054 family = argv[1];
1055 args++;
1056 }
1057
1058 STRV_FOREACH(p, args) {
1059 q = resolve_tlsa(bus, family, *p);
1060 if (q < 0)
1061 r = q;
1062 }
1063
1064 return r;
1065}
1066
1067static int show_statistics(int argc, char **argv, void *userdata) {
4c89c718
MP
1068 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1069 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
46cdbd49 1070 _cleanup_(table_unrefp) Table *table = NULL;
086111aa 1071 sd_bus *bus = ASSERT_PTR(userdata);
4c89c718
MP
1072 uint64_t n_current_transactions, n_total_transactions,
1073 cache_size, n_cache_hit, n_cache_miss,
1074 n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
1075 int r, dnssec_supported;
1076
a10f5d05 1077 r = bus_get_property_trivial(bus, bus_resolve_mgr, "DNSSECSupported", &error, 'b', &dnssec_supported);
4c89c718
MP
1078 if (r < 0)
1079 return log_error_errno(r, "Failed to get DNSSEC supported state: %s", bus_error_message(&error, r));
1080
1081 printf("DNSSEC supported by current servers: %s%s%s\n\n",
1082 ansi_highlight(),
1083 yes_no(dnssec_supported),
1084 ansi_normal());
1085
a10f5d05 1086 r = bus_get_property(bus, bus_resolve_mgr, "TransactionStatistics", &error, &reply, "(tt)");
4c89c718
MP
1087 if (r < 0)
1088 return log_error_errno(r, "Failed to get transaction statistics: %s", bus_error_message(&error, r));
1089
1090 r = sd_bus_message_read(reply, "(tt)",
1091 &n_current_transactions,
1092 &n_total_transactions);
1093 if (r < 0)
1094 return bus_log_parse_error(r);
1095
4c89c718
MP
1096 reply = sd_bus_message_unref(reply);
1097
a10f5d05 1098 r = bus_get_property(bus, bus_resolve_mgr, "CacheStatistics", &error, &reply, "(ttt)");
4c89c718
MP
1099 if (r < 0)
1100 return log_error_errno(r, "Failed to get cache statistics: %s", bus_error_message(&error, r));
1101
1102 r = sd_bus_message_read(reply, "(ttt)",
1103 &cache_size,
1104 &n_cache_hit,
1105 &n_cache_miss);
1106 if (r < 0)
1107 return bus_log_parse_error(r);
1108
4c89c718
MP
1109 reply = sd_bus_message_unref(reply);
1110
a10f5d05 1111 r = bus_get_property(bus, bus_resolve_mgr, "DNSSECStatistics", &error, &reply, "(tttt)");
4c89c718
MP
1112 if (r < 0)
1113 return log_error_errno(r, "Failed to get DNSSEC statistics: %s", bus_error_message(&error, r));
1114
1115 r = sd_bus_message_read(reply, "(tttt)",
1116 &n_dnssec_secure,
1117 &n_dnssec_insecure,
1118 &n_dnssec_bogus,
1119 &n_dnssec_indeterminate);
1120 if (r < 0)
1121 return bus_log_parse_error(r);
1122
46cdbd49
BR
1123 table = table_new("key", "value");
1124 if (!table)
1125 return log_oom();
1126
1127 table_set_header(table, false);
1128
1129 r = table_add_many(table,
1130 TABLE_STRING, "Transactions",
1131 TABLE_SET_COLOR, ansi_highlight(),
1132 TABLE_EMPTY,
1133 TABLE_STRING, "Current Transactions:",
1134 TABLE_SET_ALIGN_PERCENT, 100,
1135 TABLE_UINT64, n_current_transactions,
1136 TABLE_STRING, "Total Transactions:",
1137 TABLE_UINT64, n_total_transactions,
1138 TABLE_EMPTY, TABLE_EMPTY,
1139 TABLE_STRING, "Cache",
1140 TABLE_SET_COLOR, ansi_highlight(),
1141 TABLE_SET_ALIGN_PERCENT, 0,
1142 TABLE_EMPTY,
1143 TABLE_STRING, "Current Cache Size:",
1144 TABLE_SET_ALIGN_PERCENT, 100,
1145 TABLE_UINT64, cache_size,
1146 TABLE_STRING, "Cache Hits:",
1147 TABLE_UINT64, n_cache_hit,
1148 TABLE_STRING, "Cache Misses:",
1149 TABLE_UINT64, n_cache_miss,
1150 TABLE_EMPTY, TABLE_EMPTY,
1151 TABLE_STRING, "DNSSEC Verdicts",
1152 TABLE_SET_COLOR, ansi_highlight(),
1153 TABLE_SET_ALIGN_PERCENT, 0,
1154 TABLE_EMPTY,
1155 TABLE_STRING, "Secure:",
1156 TABLE_SET_ALIGN_PERCENT, 100,
1157 TABLE_UINT64, n_dnssec_secure,
1158 TABLE_STRING, "Insecure:",
1159 TABLE_UINT64, n_dnssec_insecure,
1160 TABLE_STRING, "Bogus:",
1161 TABLE_UINT64, n_dnssec_bogus,
1162 TABLE_STRING, "Indeterminate:",
1163 TABLE_UINT64, n_dnssec_indeterminate);
1164 if (r < 0)
1165 table_log_add_error(r);
1166
1167 r = table_print(table, NULL);
1168 if (r < 0)
a10f5d05 1169 return table_log_print_error(r);
4c89c718
MP
1170
1171 return 0;
1172}
1173
b012e921 1174static int reset_statistics(int argc, char **argv, void *userdata) {
4c89c718 1175 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b012e921 1176 sd_bus *bus = userdata;
4c89c718
MP
1177 int r;
1178
a10f5d05 1179 r = bus_call_method(bus, bus_resolve_mgr, "ResetStatistics", &error, NULL, NULL);
4c89c718
MP
1180 if (r < 0)
1181 return log_error_errno(r, "Failed to reset statistics: %s", bus_error_message(&error, r));
1182
1183 return 0;
1184}
1185
b012e921 1186static int flush_caches(int argc, char **argv, void *userdata) {
5a920b42 1187 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b012e921 1188 sd_bus *bus = userdata;
5a920b42
MP
1189 int r;
1190
a10f5d05 1191 r = bus_call_method(bus, bus_resolve_mgr, "FlushCaches", &error, NULL, NULL);
5a920b42
MP
1192 if (r < 0)
1193 return log_error_errno(r, "Failed to flush caches: %s", bus_error_message(&error, r));
1194
1195 return 0;
1196}
1197
b012e921 1198static int reset_server_features(int argc, char **argv, void *userdata) {
f5e65279 1199 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b012e921 1200 sd_bus *bus = userdata;
f5e65279
MB
1201 int r;
1202
a10f5d05 1203 r = bus_call_method(bus, bus_resolve_mgr, "ResetServerFeatures", &error, NULL, NULL);
f5e65279
MB
1204 if (r < 0)
1205 return log_error_errno(r, "Failed to reset server features: %s", bus_error_message(&error, r));
1206
1207 return 0;
1208}
1209
82126c13
LB
1210static int read_dns_server_one(
1211 sd_bus_message *m,
1212 bool with_ifindex, /* read "ifindex" reply that also carries an interface index */
1213 bool extended, /* read "extended" reply, i.e. with port number and server name */
1214 bool only_global, /* suppress entries with an (non-loopback) ifindex set (i.e. which are specific to some interface) */
1215 char **ret) {
1216
a10f5d05 1217 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b012e921 1218 _cleanup_free_ char *pretty = NULL;
a10f5d05
MB
1219 union in_addr_union a;
1220 const char *name = NULL;
82126c13
LB
1221 int32_t ifindex = 0;
1222 int family, r, k;
a10f5d05 1223 uint16_t port = 0;
b012e921
MB
1224
1225 assert(m);
1226 assert(ret);
1227
a10f5d05 1228 r = sd_bus_message_enter_container(m, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay"));
b012e921
MB
1229 if (r <= 0)
1230 return r;
1231
1232 if (with_ifindex) {
1233 r = sd_bus_message_read(m, "i", &ifindex);
1234 if (r < 0)
1235 return r;
1236 }
1237
a10f5d05
MB
1238 k = bus_message_read_in_addr_auto(m, &error, &family, &a);
1239 if (k < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
1240 return k;
b012e921 1241
a10f5d05
MB
1242 if (extended) {
1243 r = sd_bus_message_read(m, "q", &port);
1244 if (r < 0)
1245 return r;
1246
1247 r = sd_bus_message_read(m, "s", &name);
1248 if (r < 0)
1249 return r;
1250 }
b012e921
MB
1251
1252 r = sd_bus_message_exit_container(m);
1253 if (r < 0)
1254 return r;
1255
a10f5d05
MB
1256 if (k < 0) {
1257 log_debug("Invalid DNS server, ignoring: %s", bus_error_message(&error, k));
b012e921
MB
1258 *ret = NULL;
1259 return 1;
1260 }
1261
82126c13
LB
1262 if (only_global && ifindex > 0 && ifindex != LOOPBACK_IFINDEX) {
1263 /* This one has an (non-loopback) ifindex set, and we were told to suppress those. Hence do so. */
b012e921
MB
1264 *ret = NULL;
1265 return 1;
1266 }
1267
a10f5d05 1268 r = in_addr_port_ifindex_name_to_string(family, &a, port, ifindex, name, &pretty);
b012e921
MB
1269 if (r < 0)
1270 return r;
1271
1272 *ret = TAKE_PTR(pretty);
1273
1274 return 1;
1275}
1276
a10f5d05 1277static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
086111aa 1278 char ***l = ASSERT_PTR(userdata);
5a920b42
MP
1279 int r;
1280
1281 assert(bus);
1282 assert(member);
1283 assert(m);
5a920b42 1284
a10f5d05 1285 r = sd_bus_message_enter_container(m, 'a', extended ? "(iayqs)" : "(iay)");
5a920b42
MP
1286 if (r < 0)
1287 return r;
1288
1289 for (;;) {
f2dec872 1290 _cleanup_free_ char *pretty = NULL;
5a920b42 1291
82126c13 1292 r = read_dns_server_one(m, /* with_ifindex= */ false, extended, /* only_global= */ false, &pretty);
5a920b42
MP
1293 if (r < 0)
1294 return r;
1295 if (r == 0)
1296 break;
1297
b012e921 1298 if (isempty(pretty))
5a920b42 1299 continue;
5a920b42 1300
f2dec872 1301 r = strv_consume(l, TAKE_PTR(pretty));
5a920b42
MP
1302 if (r < 0)
1303 return r;
1304 }
1305
1306 r = sd_bus_message_exit_container(m);
1307 if (r < 0)
1308 return r;
1309
1310 return 0;
1311}
1312
a10f5d05
MB
1313static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1314 return map_link_dns_servers_internal(bus, member, m, error, userdata, false);
1315}
1316
1317static int map_link_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1318 return map_link_dns_servers_internal(bus, member, m, error, userdata, true);
1319}
1320
b012e921
MB
1321static int map_link_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1322 assert(m);
1323 assert(userdata);
1324
82126c13 1325 return read_dns_server_one(m, /* with_ifindex= */ false, /* extended= */ false, /* only_global= */ false, userdata);
a10f5d05
MB
1326}
1327
1328static int map_link_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1329 assert(m);
1330 assert(userdata);
1331
82126c13 1332 return read_dns_server_one(m, /* with_ifindex= */ false, /* extended= */ true, /* only_global= */ false, userdata);
b012e921
MB
1333}
1334
1335static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) {
1336 _cleanup_free_ char *str = NULL;
1337 int ifindex, route_only, r;
1338 const char *domain;
1339
1340 assert(m);
1341 assert(ret);
1342
1343 if (with_ifindex)
1344 r = sd_bus_message_read(m, "(isb)", &ifindex, &domain, &route_only);
1345 else
1346 r = sd_bus_message_read(m, "(sb)", &domain, &route_only);
1347 if (r <= 0)
1348 return r;
1349
1350 if (with_ifindex && ifindex != 0) {
1351 /* only show the global ones here */
1352 *ret = NULL;
1353 return 1;
1354 }
1355
1356 if (route_only)
f2dec872 1357 str = strjoin("~", domain);
b012e921
MB
1358 else
1359 str = strdup(domain);
1360 if (!str)
1361 return -ENOMEM;
1362
1363 *ret = TAKE_PTR(str);
1364
1365 return 1;
1366}
1367
5a920b42 1368static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
086111aa 1369 char ***l = ASSERT_PTR(userdata);
5a920b42
MP
1370 int r;
1371
1372 assert(bus);
1373 assert(member);
1374 assert(m);
5a920b42
MP
1375
1376 r = sd_bus_message_enter_container(m, 'a', "(sb)");
1377 if (r < 0)
1378 return r;
1379
1380 for (;;) {
f2dec872 1381 _cleanup_free_ char *pretty = NULL;
5a920b42 1382
b012e921 1383 r = read_domain_one(m, false, &pretty);
5a920b42
MP
1384 if (r < 0)
1385 return r;
1386 if (r == 0)
1387 break;
1388
b012e921
MB
1389 if (isempty(pretty))
1390 continue;
5a920b42 1391
f2dec872 1392 r = strv_consume(l, TAKE_PTR(pretty));
5a920b42
MP
1393 if (r < 0)
1394 return r;
1395 }
1396
1397 r = sd_bus_message_exit_container(m);
1398 if (r < 0)
1399 return r;
1400
a032b68d
MB
1401 strv_sort(*l);
1402
5a920b42
MP
1403 return 0;
1404}
1405
b012e921 1406static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
a032b68d
MB
1407 const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */
1408 int pos1, pos2;
1409
1410 if (ifname)
1411 printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, ifindex, ifname, &pos2, ansi_normal());
1412 else
1413 printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
1414
1415 size_t cols = columns(), position = pos2 - pos1 + 2;
b012e921 1416
a032b68d
MB
1417 STRV_FOREACH(i, p) {
1418 size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen).
1419 * If that happens, we'll just print one item per line. */
b012e921 1420
a032b68d
MB
1421 if (position <= indent || size_add(size_add(position, 1), our_len) < cols) {
1422 printf(" %s", *i);
1423 position = size_add(size_add(position, 1), our_len);
1424 } else {
086111aa 1425 printf("\n%*s%s", (int) indent, "", *i);
a032b68d
MB
1426 position = size_add(our_len, indent);
1427 }
1428 }
b012e921
MB
1429
1430 printf("\n");
1431
1432 return 0;
1433}
1434
a032b68d
MB
1435static int status_print_strv_global(char **p) {
1436 return status_print_strv_ifindex(0, NULL, p);
1437}
1438
1439typedef struct LinkInfo {
6e866b33
MB
1440 uint64_t scopes_mask;
1441 const char *llmnr;
1442 const char *mdns;
1443 const char *dns_over_tls;
1444 const char *dnssec;
1445 char *current_dns;
a10f5d05 1446 char *current_dns_ex;
6e866b33 1447 char **dns;
a10f5d05 1448 char **dns_ex;
6e866b33
MB
1449 char **domains;
1450 char **ntas;
1451 bool dnssec_supported;
1452 bool default_route;
a032b68d
MB
1453} LinkInfo;
1454
1455typedef struct GlobalInfo {
1456 char *current_dns;
1457 char *current_dns_ex;
1458 char **dns;
1459 char **dns_ex;
1460 char **fallback_dns;
1461 char **fallback_dns_ex;
1462 char **domains;
1463 char **ntas;
1464 const char *llmnr;
1465 const char *mdns;
1466 const char *dns_over_tls;
1467 const char *dnssec;
1468 const char *resolv_conf_mode;
1469 bool dnssec_supported;
1470} GlobalInfo;
6e866b33 1471
a032b68d 1472static void link_info_clear(LinkInfo *p) {
6e866b33 1473 free(p->current_dns);
a10f5d05 1474 free(p->current_dns_ex);
6e866b33 1475 strv_free(p->dns);
a10f5d05 1476 strv_free(p->dns_ex);
6e866b33
MB
1477 strv_free(p->domains);
1478 strv_free(p->ntas);
1479}
5a920b42 1480
a032b68d
MB
1481static void global_info_clear(GlobalInfo *p) {
1482 free(p->current_dns);
1483 free(p->current_dns_ex);
1484 strv_free(p->dns);
1485 strv_free(p->dns_ex);
1486 strv_free(p->fallback_dns);
1487 strv_free(p->fallback_dns_ex);
1488 strv_free(p->domains);
1489 strv_free(p->ntas);
1490}
1491
46cdbd49
BR
1492static int dump_list(Table *table, const char *prefix, char * const *l) {
1493 int r;
1494
1495 if (strv_isempty(l))
1496 return 0;
1497
1498 r = table_add_many(table,
1499 TABLE_STRING, prefix,
a032b68d 1500 TABLE_STRV_WRAPPED, l);
46cdbd49
BR
1501 if (r < 0)
1502 return table_log_add_error(r);
1503
1504 return 0;
1505}
1506
a032b68d
MB
1507static int strv_extend_extended_bool(char ***strv, const char *name, const char *value) {
1508 int r;
1509
1510 if (value) {
1511 r = parse_boolean(value);
1512 if (r >= 0)
1513 return strv_extendf(strv, "%s%s", plus_minus(r), name);
1514 }
1515
1516 return strv_extendf(strv, "%s=%s", name, value ?: "???");
1517}
1518
1519static char** link_protocol_status(const LinkInfo *info) {
1520 _cleanup_strv_free_ char **s = NULL;
1521
1522 if (strv_extendf(&s, "%sDefaultRoute", plus_minus(info->default_route)) < 0)
1523 return NULL;
1524
1525 if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
1526 return NULL;
1527
1528 if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
1529 return NULL;
1530
1531 if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
1532 return NULL;
1533
1534 if (strv_extendf(&s, "DNSSEC=%s/%s",
1535 info->dnssec ?: "???",
1536 info->dnssec_supported ? "supported" : "unsupported") < 0)
1537 return NULL;
1538
1539 return TAKE_PTR(s);
1540}
1541
1542static char** global_protocol_status(const GlobalInfo *info) {
1543 _cleanup_strv_free_ char **s = NULL;
1544
1545 if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
1546 return NULL;
1547
1548 if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
1549 return NULL;
1550
1551 if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
1552 return NULL;
1553
1554 if (strv_extendf(&s, "DNSSEC=%s/%s",
1555 info->dnssec ?: "???",
1556 info->dnssec_supported ? "supported" : "unsupported") < 0)
1557 return NULL;
1558
1559 return TAKE_PTR(s);
1560}
1561
6e866b33 1562static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
5a920b42 1563 static const struct bus_properties_map property_map[] = {
a032b68d
MB
1564 { "ScopesMask", "t", NULL, offsetof(LinkInfo, scopes_mask) },
1565 { "DNS", "a(iay)", map_link_dns_servers, offsetof(LinkInfo, dns) },
1566 { "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(LinkInfo, dns_ex) },
1567 { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(LinkInfo, current_dns) },
1568 { "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(LinkInfo, current_dns_ex) },
1569 { "Domains", "a(sb)", map_link_domains, offsetof(LinkInfo, domains) },
1570 { "DefaultRoute", "b", NULL, offsetof(LinkInfo, default_route) },
1571 { "LLMNR", "s", NULL, offsetof(LinkInfo, llmnr) },
1572 { "MulticastDNS", "s", NULL, offsetof(LinkInfo, mdns) },
1573 { "DNSOverTLS", "s", NULL, offsetof(LinkInfo, dns_over_tls) },
1574 { "DNSSEC", "s", NULL, offsetof(LinkInfo, dnssec) },
1575 { "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(LinkInfo, ntas) },
1576 { "DNSSECSupported", "b", NULL, offsetof(LinkInfo, dnssec_supported) },
5a920b42
MP
1577 {}
1578 };
2897b343 1579 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b012e921 1580 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
a032b68d 1581 _cleanup_(link_info_clear) LinkInfo link_info = {};
46cdbd49
BR
1582 _cleanup_(table_unrefp) Table *table = NULL;
1583 _cleanup_free_ char *p = NULL;
ea0999c9 1584 char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE];
5a920b42
MP
1585 int r;
1586
1587 assert(bus);
1588 assert(ifindex > 0);
5a920b42
MP
1589
1590 if (!name) {
ea0999c9
MB
1591 r = format_ifname(ifindex, ifname);
1592 if (r < 0)
1593 return log_error_errno(r, "Failed to resolve interface name for %i: %m", ifindex);
5a920b42
MP
1594
1595 name = ifname;
1596 }
1597
46cdbd49 1598 xsprintf(ifi, "%i", ifindex);
5a920b42
MP
1599 r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifi, &p);
1600 if (r < 0)
1601 return log_oom();
1602
1603 r = bus_map_all_properties(bus,
1604 "org.freedesktop.resolve1",
1605 p,
1606 property_map,
b012e921 1607 BUS_MAP_BOOLEAN_AS_BOOL,
2897b343 1608 &error,
b012e921 1609 &m,
5a920b42 1610 &link_info);
6e866b33
MB
1611 if (r < 0)
1612 return log_error_errno(r, "Failed to get link data for %i: %s", ifindex, bus_error_message(&error, r));
5a920b42 1613
ea0999c9 1614 pager_open(arg_pager_flags);
b012e921 1615
6e866b33 1616 if (mode == STATUS_DNS)
a10f5d05 1617 return status_print_strv_ifindex(ifindex, name, link_info.dns_ex ?: link_info.dns);
b012e921 1618
6e866b33
MB
1619 if (mode == STATUS_DOMAIN)
1620 return status_print_strv_ifindex(ifindex, name, link_info.domains);
1621
1622 if (mode == STATUS_NTA)
1623 return status_print_strv_ifindex(ifindex, name, link_info.ntas);
b012e921 1624
6e866b33
MB
1625 if (mode == STATUS_DEFAULT_ROUTE) {
1626 printf("%sLink %i (%s)%s: %s\n",
1627 ansi_highlight(), ifindex, name, ansi_normal(),
1628 yes_no(link_info.default_route));
1629
1630 return 0;
b012e921
MB
1631 }
1632
1633 if (mode == STATUS_LLMNR) {
1634 printf("%sLink %i (%s)%s: %s\n",
1635 ansi_highlight(), ifindex, name, ansi_normal(),
1636 strna(link_info.llmnr));
1637
6e866b33 1638 return 0;
b012e921
MB
1639 }
1640
1641 if (mode == STATUS_MDNS) {
1642 printf("%sLink %i (%s)%s: %s\n",
1643 ansi_highlight(), ifindex, name, ansi_normal(),
1644 strna(link_info.mdns));
1645
6e866b33 1646 return 0;
b012e921
MB
1647 }
1648
1649 if (mode == STATUS_PRIVATE) {
1650 printf("%sLink %i (%s)%s: %s\n",
1651 ansi_highlight(), ifindex, name, ansi_normal(),
1652 strna(link_info.dns_over_tls));
1653
6e866b33 1654 return 0;
b012e921
MB
1655 }
1656
1657 if (mode == STATUS_DNSSEC) {
1658 printf("%sLink %i (%s)%s: %s\n",
1659 ansi_highlight(), ifindex, name, ansi_normal(),
1660 strna(link_info.dnssec));
1661
6e866b33 1662 return 0;
b012e921 1663 }
5a920b42 1664
b012e921 1665 if (empty_line && *empty_line)
5a920b42
MP
1666 fputc('\n', stdout);
1667
1668 printf("%sLink %i (%s)%s\n",
1669 ansi_highlight(), ifindex, name, ansi_normal());
1670
46cdbd49
BR
1671 table = table_new("key", "value");
1672 if (!table)
1673 return log_oom();
1674
1675 table_set_header(table, false);
1676
1677 r = table_add_many(table,
1678 TABLE_STRING, "Current Scopes:",
1679 TABLE_SET_ALIGN_PERCENT, 100);
1680 if (r < 0)
1681 return table_log_add_error(r);
1682
5a920b42 1683 if (link_info.scopes_mask == 0)
46cdbd49
BR
1684 r = table_add_cell(table, NULL, TABLE_STRING, "none");
1685 else {
1686 _cleanup_free_ char *buf = NULL;
1687 size_t len;
1688
1689 if (asprintf(&buf, "%s%s%s%s%s",
1690 link_info.scopes_mask & SD_RESOLVED_DNS ? "DNS " : "",
1691 link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? "LLMNR/IPv4 " : "",
1692 link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? "LLMNR/IPv6 " : "",
1693 link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4 " : "",
1694 link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6 " : "") < 0)
1695 return log_oom();
5a920b42 1696
46cdbd49
BR
1697 len = strlen(buf);
1698 assert(len > 0);
1699 buf[len - 1] = '\0';
5a920b42 1700
46cdbd49
BR
1701 r = table_add_cell(table, NULL, TABLE_STRING, buf);
1702 }
1703 if (r < 0)
1704 return table_log_add_error(r);
1705
a032b68d
MB
1706 _cleanup_strv_free_ char **pstatus = link_protocol_status(&link_info);
1707 if (!pstatus)
1708 return log_oom();
1709
46cdbd49 1710 r = table_add_many(table,
a032b68d
MB
1711 TABLE_STRING, "Protocols:",
1712 TABLE_STRV_WRAPPED, pstatus);
46cdbd49
BR
1713 if (r < 0)
1714 return table_log_add_error(r);
1715
1716 if (link_info.current_dns) {
1717 r = table_add_many(table,
1718 TABLE_STRING, "Current DNS Server:",
a10f5d05 1719 TABLE_STRING, link_info.current_dns_ex ?: link_info.current_dns);
46cdbd49
BR
1720 if (r < 0)
1721 return table_log_add_error(r);
5a920b42
MP
1722 }
1723
a10f5d05 1724 r = dump_list(table, "DNS Servers:", link_info.dns_ex ?: link_info.dns);
46cdbd49
BR
1725 if (r < 0)
1726 return r;
1727
1728 r = dump_list(table, "DNS Domain:", link_info.domains);
1729 if (r < 0)
1730 return r;
1731
46cdbd49
BR
1732 r = table_print(table, NULL);
1733 if (r < 0)
a10f5d05 1734 return table_log_print_error(r);
46cdbd49 1735
b012e921
MB
1736 if (empty_line)
1737 *empty_line = true;
5a920b42 1738
6e866b33 1739 return 0;
5a920b42
MP
1740}
1741
a10f5d05 1742static int map_global_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
086111aa 1743 char ***l = ASSERT_PTR(userdata);
5a920b42
MP
1744 int r;
1745
1746 assert(bus);
1747 assert(member);
1748 assert(m);
5a920b42 1749
a10f5d05 1750 r = sd_bus_message_enter_container(m, 'a', extended ? "(iiayqs)" : "(iiay)");
5a920b42
MP
1751 if (r < 0)
1752 return r;
1753
1754 for (;;) {
f2dec872 1755 _cleanup_free_ char *pretty = NULL;
5a920b42 1756
82126c13 1757 r = read_dns_server_one(m, /* with_ifindex= */ true, extended, /* only_global= */ true, &pretty);
5a920b42
MP
1758 if (r < 0)
1759 return r;
1760 if (r == 0)
1761 break;
1762
b012e921 1763 if (isempty(pretty))
5a920b42 1764 continue;
5a920b42 1765
f2dec872 1766 r = strv_consume(l, TAKE_PTR(pretty));
5a920b42
MP
1767 if (r < 0)
1768 return r;
1769 }
1770
1771 r = sd_bus_message_exit_container(m);
1772 if (r < 0)
1773 return r;
1774
1775 return 0;
1776}
1777
a10f5d05
MB
1778static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1779 return map_global_dns_servers_internal(bus, member, m, error, userdata, false);
1780}
1781
1782static int map_global_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1783 return map_global_dns_servers_internal(bus, member, m, error, userdata, true);
1784}
1785
b012e921 1786static int map_global_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
82126c13 1787 return read_dns_server_one(m, /* with_ifindex= */ true, /* extended= */ false, /* only_global= */ true, userdata);
a10f5d05
MB
1788}
1789
1790static int map_global_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
82126c13 1791 return read_dns_server_one(m, /* with_ifindex= */ true, /* extended= */ true, /* only_global= */ true, userdata);
b012e921
MB
1792}
1793
5a920b42 1794static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
086111aa 1795 char ***l = ASSERT_PTR(userdata);
5a920b42
MP
1796 int r;
1797
1798 assert(bus);
1799 assert(member);
1800 assert(m);
5a920b42
MP
1801
1802 r = sd_bus_message_enter_container(m, 'a', "(isb)");
1803 if (r < 0)
1804 return r;
1805
1806 for (;;) {
f2dec872 1807 _cleanup_free_ char *pretty = NULL;
5a920b42 1808
b012e921 1809 r = read_domain_one(m, true, &pretty);
5a920b42
MP
1810 if (r < 0)
1811 return r;
1812 if (r == 0)
1813 break;
1814
b012e921 1815 if (isempty(pretty))
5a920b42
MP
1816 continue;
1817
f2dec872 1818 r = strv_consume(l, TAKE_PTR(pretty));
5a920b42
MP
1819 if (r < 0)
1820 return r;
1821 }
1822
1823 r = sd_bus_message_exit_container(m);
1824 if (r < 0)
1825 return r;
1826
a032b68d 1827 strv_sort(*l);
b012e921
MB
1828
1829 return 0;
1830}
1831
6e866b33 1832static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
5a920b42 1833 static const struct bus_properties_map property_map[] = {
a032b68d
MB
1834 { "DNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, dns) },
1835 { "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, dns_ex) },
1836 { "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, fallback_dns) },
1837 { "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, fallback_dns_ex) },
1838 { "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(GlobalInfo, current_dns) },
1839 { "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(GlobalInfo, current_dns_ex) },
1840 { "Domains", "a(isb)", map_global_domains, offsetof(GlobalInfo, domains) },
1841 { "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(GlobalInfo, ntas) },
1842 { "LLMNR", "s", NULL, offsetof(GlobalInfo, llmnr) },
1843 { "MulticastDNS", "s", NULL, offsetof(GlobalInfo, mdns) },
1844 { "DNSOverTLS", "s", NULL, offsetof(GlobalInfo, dns_over_tls) },
1845 { "DNSSEC", "s", NULL, offsetof(GlobalInfo, dnssec) },
1846 { "DNSSECSupported", "b", NULL, offsetof(GlobalInfo, dnssec_supported) },
1847 { "ResolvConfMode", "s", NULL, offsetof(GlobalInfo, resolv_conf_mode) },
5a920b42
MP
1848 {}
1849 };
2897b343 1850 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b012e921 1851 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
a032b68d 1852 _cleanup_(global_info_clear) GlobalInfo global_info = {};
46cdbd49 1853 _cleanup_(table_unrefp) Table *table = NULL;
5a920b42
MP
1854 int r;
1855
1856 assert(bus);
1857 assert(empty_line);
1858
1859 r = bus_map_all_properties(bus,
1860 "org.freedesktop.resolve1",
1861 "/org/freedesktop/resolve1",
1862 property_map,
b012e921 1863 BUS_MAP_BOOLEAN_AS_BOOL,
2897b343 1864 &error,
b012e921 1865 &m,
5a920b42 1866 &global_info);
6e866b33
MB
1867 if (r < 0)
1868 return log_error_errno(r, "Failed to get global data: %s", bus_error_message(&error, r));
5a920b42 1869
ea0999c9 1870 pager_open(arg_pager_flags);
b012e921 1871
6e866b33 1872 if (mode == STATUS_DNS)
a10f5d05 1873 return status_print_strv_global(global_info.dns_ex ?: global_info.dns);
b012e921 1874
6e866b33
MB
1875 if (mode == STATUS_DOMAIN)
1876 return status_print_strv_global(global_info.domains);
b012e921 1877
6e866b33
MB
1878 if (mode == STATUS_NTA)
1879 return status_print_strv_global(global_info.ntas);
b012e921
MB
1880
1881 if (mode == STATUS_LLMNR) {
1882 printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
1883 strna(global_info.llmnr));
1884
6e866b33 1885 return 0;
b012e921
MB
1886 }
1887
1888 if (mode == STATUS_MDNS) {
1889 printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
1890 strna(global_info.mdns));
1891
6e866b33 1892 return 0;
b012e921
MB
1893 }
1894
1895 if (mode == STATUS_PRIVATE) {
1896 printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
1897 strna(global_info.dns_over_tls));
1898
6e866b33 1899 return 0;
5a920b42
MP
1900 }
1901
b012e921
MB
1902 if (mode == STATUS_DNSSEC) {
1903 printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
1904 strna(global_info.dnssec));
1905
6e866b33 1906 return 0;
b012e921 1907 }
5a920b42
MP
1908
1909 printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
b012e921 1910
46cdbd49
BR
1911 table = table_new("key", "value");
1912 if (!table)
1913 return log_oom();
5a920b42 1914
46cdbd49
BR
1915 table_set_header(table, false);
1916
a032b68d
MB
1917 _cleanup_strv_free_ char **pstatus = global_protocol_status(&global_info);
1918 if (!pstatus)
1919 return log_oom();
1920
46cdbd49 1921 r = table_add_many(table,
a032b68d 1922 TABLE_STRING, "Protocols:",
46cdbd49 1923 TABLE_SET_ALIGN_PERCENT, 100,
a032b68d 1924 TABLE_STRV_WRAPPED, pstatus);
46cdbd49
BR
1925 if (r < 0)
1926 return table_log_add_error(r);
1927
a032b68d
MB
1928 if (global_info.resolv_conf_mode) {
1929 r = table_add_many(table,
1930 TABLE_STRING, "resolv.conf mode:",
1931 TABLE_STRING, global_info.resolv_conf_mode);
1932 if (r < 0)
1933 return table_log_add_error(r);
1934 }
1935
46cdbd49
BR
1936 if (global_info.current_dns) {
1937 r = table_add_many(table,
1938 TABLE_STRING, "Current DNS Server:",
a10f5d05 1939 TABLE_STRING, global_info.current_dns_ex ?: global_info.current_dns);
46cdbd49
BR
1940 if (r < 0)
1941 return table_log_add_error(r);
b012e921
MB
1942 }
1943
28085778 1944 r = dump_list(table, "DNS Servers", global_info.dns_ex ?: global_info.dns);
46cdbd49
BR
1945 if (r < 0)
1946 return r;
1947
28085778 1948 r = dump_list(table, "Fallback DNS Servers", global_info.fallback_dns_ex ?: global_info.fallback_dns);
46cdbd49
BR
1949 if (r < 0)
1950 return r;
1951
28085778 1952 r = dump_list(table, "DNS Domain", global_info.domains);
46cdbd49
BR
1953 if (r < 0)
1954 return r;
5a920b42 1955
46cdbd49
BR
1956 r = table_print(table, NULL);
1957 if (r < 0)
a10f5d05 1958 return table_log_print_error(r);
5a920b42
MP
1959
1960 *empty_line = true;
1961
6e866b33 1962 return 0;
5a920b42
MP
1963}
1964
b012e921 1965static int status_all(sd_bus *bus, StatusMode mode) {
5a920b42
MP
1966 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
1967 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
5a920b42
MP
1968 bool empty_line = false;
1969 int r;
1970
1971 assert(bus);
1972
b012e921 1973 r = status_global(bus, mode, &empty_line);
5a920b42
MP
1974 if (r < 0)
1975 return r;
1976
1977 r = sd_netlink_open(&rtnl);
1978 if (r < 0)
1979 return log_error_errno(r, "Failed to connect to netlink: %m");
1980
1981 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
1982 if (r < 0)
1983 return rtnl_log_create_error(r);
1984
086111aa 1985 r = sd_netlink_message_set_request_dump(req, true);
5a920b42
MP
1986 if (r < 0)
1987 return rtnl_log_create_error(r);
1988
1989 r = sd_netlink_call(rtnl, req, 0, &reply);
1990 if (r < 0)
1991 return log_error_errno(r, "Failed to enumerate links: %m");
1992
a10f5d05 1993 _cleanup_free_ InterfaceInfo *infos = NULL;
8b3d4ff0 1994 size_t n_infos = 0;
a10f5d05
MB
1995
1996 for (sd_netlink_message *i = reply; i; i = sd_netlink_message_next(i)) {
5a920b42 1997 const char *name;
a10f5d05 1998 int ifindex;
5a920b42
MP
1999 uint16_t type;
2000
a10f5d05
MB
2001 r = sd_netlink_message_get_type(i, &type);
2002 if (r < 0)
2003 return rtnl_log_parse_error(r);
5a920b42
MP
2004
2005 if (type != RTM_NEWLINK)
2006 continue;
2007
a10f5d05
MB
2008 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
2009 if (r < 0)
2010 return rtnl_log_parse_error(r);
5a920b42
MP
2011
2012 if (ifindex == LOOPBACK_IFINDEX)
2013 continue;
2014
a10f5d05
MB
2015 r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
2016 if (r < 0)
2017 return rtnl_log_parse_error(r);
2018
8b3d4ff0 2019 if (!GREEDY_REALLOC(infos, n_infos + 1))
a10f5d05
MB
2020 return log_oom();
2021
2022 infos[n_infos++] = (InterfaceInfo) { ifindex, name };
2023 }
2024
2025 typesafe_qsort(infos, n_infos, interface_info_compare);
5a920b42 2026
a10f5d05
MB
2027 r = 0;
2028 for (size_t i = 0; i < n_infos; i++) {
2029 int q = status_ifindex(bus, infos[i].index, infos[i].name, mode, &empty_line);
5a920b42
MP
2030 if (q < 0 && r >= 0)
2031 r = q;
2032 }
2033
2034 return r;
2035}
2036
b012e921
MB
2037static int verb_status(int argc, char **argv, void *userdata) {
2038 sd_bus *bus = userdata;
46cdbd49
BR
2039 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
2040 int r = 0;
52ad194e 2041
b012e921 2042 if (argc > 1) {
b012e921 2043 bool empty_line = false;
52ad194e 2044
b012e921 2045 STRV_FOREACH(ifname, argv + 1) {
46cdbd49 2046 int ifindex, q;
52ad194e 2047
8b3d4ff0 2048 ifindex = rtnl_resolve_interface(&rtnl, *ifname);
46cdbd49
BR
2049 if (ifindex < 0) {
2050 log_warning_errno(ifindex, "Failed to resolve interface \"%s\", ignoring: %m", *ifname);
b012e921 2051 continue;
f2dec872 2052 }
52ad194e 2053
b012e921 2054 q = status_ifindex(bus, ifindex, NULL, STATUS_ALL, &empty_line);
52ad194e 2055 if (q < 0)
b012e921
MB
2056 r = q;
2057 }
2058 } else
2059 r = status_all(bus, STATUS_ALL);
52ad194e 2060
b012e921
MB
2061 return r;
2062}
52ad194e 2063
a10f5d05 2064static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error, bool extended) {
b012e921 2065 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
6e866b33 2066 int r;
52ad194e 2067
a10f5d05 2068 r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS");
b012e921
MB
2069 if (r < 0)
2070 return bus_log_create_error(r);
52ad194e 2071
6e866b33 2072 r = sd_bus_message_append(req, "i", arg_ifindex);
b012e921
MB
2073 if (r < 0)
2074 return bus_log_create_error(r);
52ad194e 2075
a10f5d05 2076 r = sd_bus_message_open_container(req, 'a', extended ? "(iayqs)" : "(iay)");
b012e921
MB
2077 if (r < 0)
2078 return bus_log_create_error(r);
52ad194e 2079
6e866b33
MB
2080 /* If only argument is the empty string, then call SetLinkDNS() with an
2081 * empty list, which will clear the list of domains for an interface. */
f2dec872
BR
2082 if (!strv_equal(dns, STRV_MAKE("")))
2083 STRV_FOREACH(p, dns) {
a10f5d05 2084 _cleanup_free_ char *name = NULL;
6e866b33 2085 struct in_addr_data data;
a10f5d05
MB
2086 uint16_t port;
2087 int ifindex;
52ad194e 2088
a10f5d05 2089 r = in_addr_port_ifindex_name_from_string_auto(*p, &data.family, &data.address, &port, &ifindex, &name);
6e866b33
MB
2090 if (r < 0)
2091 return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
52ad194e 2092
a10f5d05
MB
2093 if (ifindex != 0 && ifindex != arg_ifindex)
2094 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifindex: %i", ifindex);
2095
2096 r = sd_bus_message_open_container(req, 'r', extended ? "iayqs" : "iay");
6e866b33
MB
2097 if (r < 0)
2098 return bus_log_create_error(r);
b012e921 2099
6e866b33
MB
2100 r = sd_bus_message_append(req, "i", data.family);
2101 if (r < 0)
2102 return bus_log_create_error(r);
52ad194e 2103
6e866b33
MB
2104 r = sd_bus_message_append_array(req, 'y', &data.address, FAMILY_ADDRESS_SIZE(data.family));
2105 if (r < 0)
2106 return bus_log_create_error(r);
52ad194e 2107
a10f5d05
MB
2108 if (extended) {
2109 r = sd_bus_message_append(req, "q", port);
2110 if (r < 0)
2111 return bus_log_create_error(r);
2112
2113 r = sd_bus_message_append(req, "s", name);
2114 if (r < 0)
2115 return bus_log_create_error(r);
2116 }
2117
6e866b33
MB
2118 r = sd_bus_message_close_container(req);
2119 if (r < 0)
2120 return bus_log_create_error(r);
2121 }
52ad194e 2122
b012e921
MB
2123 r = sd_bus_message_close_container(req);
2124 if (r < 0)
2125 return bus_log_create_error(r);
2126
a10f5d05
MB
2127 r = sd_bus_call(bus, req, 0, error, NULL);
2128 if (r < 0 && extended && sd_bus_error_has_name(error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
2129 sd_bus_error_free(error);
2130 return call_dns(bus, dns, locator, error, false);
2131 }
2132 return r;
b012e921
MB
2133}
2134
f2dec872 2135static int verb_dns(int argc, char **argv, void *userdata) {
b012e921 2136 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2137 sd_bus *bus = ASSERT_PTR(userdata);
6e866b33 2138 int r;
b012e921 2139
6e866b33
MB
2140 if (argc >= 2) {
2141 r = ifname_mangle(argv[1]);
2142 if (r < 0)
2143 return r;
52ad194e
MB
2144 }
2145
6e866b33 2146 if (arg_ifindex <= 0)
f2dec872 2147 return status_all(bus, STATUS_DNS);
6e866b33
MB
2148
2149 if (argc < 3)
f2dec872
BR
2150 return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL);
2151
a10f5d05 2152 r = call_dns(bus, argv + 2, bus_resolve_mgr, &error, true);
f2dec872
BR
2153 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2154 sd_bus_error_free(&error);
2155
a10f5d05 2156 r = call_dns(bus, argv + 2, bus_network_mgr, &error, true);
f2dec872
BR
2157 }
2158 if (r < 0) {
2159 if (arg_ifindex_permissive &&
2160 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2161 return 0;
2162
2163 return log_error_errno(r, "Failed to set DNS configuration: %s", bus_error_message(&error, r));
2164 }
2165
2166 return 0;
2167}
2168
a10f5d05 2169static int call_domain(sd_bus *bus, char **domain, const BusLocator *locator, sd_bus_error *error) {
f2dec872 2170 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
f2dec872 2171 int r;
b012e921 2172
a10f5d05 2173 r = bus_message_new_method_call(bus, &req, locator, "SetLinkDomains");
b012e921
MB
2174 if (r < 0)
2175 return bus_log_create_error(r);
2176
6e866b33 2177 r = sd_bus_message_append(req, "i", arg_ifindex);
b012e921
MB
2178 if (r < 0)
2179 return bus_log_create_error(r);
2180
2181 r = sd_bus_message_open_container(req, 'a', "(sb)");
2182 if (r < 0)
2183 return bus_log_create_error(r);
2184
6e866b33
MB
2185 /* If only argument is the empty string, then call SetLinkDomains() with an
2186 * empty list, which will clear the list of domains for an interface. */
f2dec872
BR
2187 if (!strv_equal(domain, STRV_MAKE("")))
2188 STRV_FOREACH(p, domain) {
6e866b33 2189 const char *n;
b012e921 2190
6e866b33 2191 n = **p == '~' ? *p + 1 : *p;
b012e921 2192
6e866b33
MB
2193 r = dns_name_is_valid(n);
2194 if (r < 0)
2195 return log_error_errno(r, "Failed to validate specified domain %s: %m", n);
a032b68d
MB
2196 if (r == 0)
2197 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2198 "Domain not valid: %s",
2199 n);
b012e921 2200
6e866b33
MB
2201 r = sd_bus_message_append(req, "(sb)", n, **p == '~');
2202 if (r < 0)
2203 return bus_log_create_error(r);
2204 }
52ad194e 2205
b012e921
MB
2206 r = sd_bus_message_close_container(req);
2207 if (r < 0)
2208 return bus_log_create_error(r);
52ad194e 2209
f2dec872
BR
2210 return sd_bus_call(bus, req, 0, error, NULL);
2211}
2212
2213static int verb_domain(int argc, char **argv, void *userdata) {
2214 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2215 sd_bus *bus = ASSERT_PTR(userdata);
f2dec872
BR
2216 int r;
2217
f2dec872
BR
2218 if (argc >= 2) {
2219 r = ifname_mangle(argv[1]);
2220 if (r < 0)
2221 return r;
2222 }
2223
2224 if (arg_ifindex <= 0)
2225 return status_all(bus, STATUS_DOMAIN);
2226
2227 if (argc < 3)
2228 return status_ifindex(bus, arg_ifindex, NULL, STATUS_DOMAIN, NULL);
2229
a10f5d05 2230 r = call_domain(bus, argv + 2, bus_resolve_mgr, &error);
f2dec872
BR
2231 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2232 sd_bus_error_free(&error);
52ad194e 2233
a10f5d05 2234 r = call_domain(bus, argv + 2, bus_network_mgr, &error);
f2dec872
BR
2235 }
2236 if (r < 0) {
b012e921
MB
2237 if (arg_ifindex_permissive &&
2238 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2239 return 0;
52ad194e 2240
b012e921
MB
2241 return log_error_errno(r, "Failed to set domain configuration: %s", bus_error_message(&error, r));
2242 }
2243
2244 return 0;
2245}
52ad194e 2246
6e866b33 2247static int verb_default_route(int argc, char **argv, void *userdata) {
b012e921 2248 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2249 sd_bus *bus = ASSERT_PTR(userdata);
6e866b33 2250 int r, b;
52ad194e 2251
6e866b33
MB
2252 if (argc >= 2) {
2253 r = ifname_mangle(argv[1]);
2254 if (r < 0)
2255 return r;
2256 }
b012e921 2257
6e866b33
MB
2258 if (arg_ifindex <= 0)
2259 return status_all(bus, STATUS_DEFAULT_ROUTE);
b012e921 2260
6e866b33
MB
2261 if (argc < 3)
2262 return status_ifindex(bus, arg_ifindex, NULL, STATUS_DEFAULT_ROUTE, NULL);
2263
2264 b = parse_boolean(argv[2]);
2265 if (b < 0)
2266 return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
2267
a10f5d05 2268 r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b);
f2dec872
BR
2269 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2270 sd_bus_error_free(&error);
2271
a10f5d05 2272 r = bus_call_method(bus, bus_network_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b);
f2dec872 2273 }
6e866b33 2274 if (r < 0) {
6e866b33
MB
2275 if (arg_ifindex_permissive &&
2276 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2277 return 0;
2278
2279 return log_error_errno(r, "Failed to set default route configuration: %s", bus_error_message(&error, r));
b012e921
MB
2280 }
2281
6e866b33
MB
2282 return 0;
2283}
2284
2285static int verb_llmnr(int argc, char **argv, void *userdata) {
2286 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2287 sd_bus *bus = ASSERT_PTR(userdata);
6e866b33
MB
2288 int r;
2289
6e866b33
MB
2290 if (argc >= 2) {
2291 r = ifname_mangle(argv[1]);
2292 if (r < 0)
2293 return r;
2294 }
2295
2296 if (arg_ifindex <= 0)
2297 return status_all(bus, STATUS_LLMNR);
2298
2299 if (argc < 3)
2300 return status_ifindex(bus, arg_ifindex, NULL, STATUS_LLMNR, NULL);
b012e921 2301
a10f5d05 2302 r = bus_call_method(bus, bus_resolve_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
f2dec872
BR
2303 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2304 sd_bus_error_free(&error);
2305
a10f5d05 2306 r = bus_call_method(bus, bus_network_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
f2dec872 2307 }
b012e921 2308 if (r < 0) {
b012e921
MB
2309 if (arg_ifindex_permissive &&
2310 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2311 return 0;
2312
2313 return log_error_errno(r, "Failed to set LLMNR configuration: %s", bus_error_message(&error, r));
2314 }
2315
2316 return 0;
2317}
2318
2319static int verb_mdns(int argc, char **argv, void *userdata) {
2320 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2321 sd_bus *bus = ASSERT_PTR(userdata);
6e866b33 2322 int r;
b012e921 2323
6e866b33
MB
2324 if (argc >= 2) {
2325 r = ifname_mangle(argv[1]);
2326 if (r < 0)
2327 return r;
b012e921
MB
2328 }
2329
6e866b33
MB
2330 if (arg_ifindex <= 0)
2331 return status_all(bus, STATUS_MDNS);
2332
2333 if (argc < 3)
2334 return status_ifindex(bus, arg_ifindex, NULL, STATUS_MDNS, NULL);
b012e921 2335
a10f5d05 2336 r = bus_call_method(bus, bus_resolve_mgr, "SetLinkMulticastDNS", &error, NULL, "is", arg_ifindex, argv[2]);
f2dec872
BR
2337 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2338 sd_bus_error_free(&error);
2339
a10f5d05 2340 r = bus_call_method(
f2dec872 2341 bus,
a10f5d05 2342 bus_network_mgr,
f2dec872
BR
2343 "SetLinkMulticastDNS",
2344 &error,
2345 NULL,
2346 "is", arg_ifindex, argv[2]);
2347 }
b012e921 2348 if (r < 0) {
b012e921
MB
2349 if (arg_ifindex_permissive &&
2350 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2351 return 0;
2352
2353 return log_error_errno(r, "Failed to set MulticastDNS configuration: %s", bus_error_message(&error, r));
2354 }
2355
2356 return 0;
2357}
2358
2359static int verb_dns_over_tls(int argc, char **argv, void *userdata) {
2360 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2361 sd_bus *bus = ASSERT_PTR(userdata);
6e866b33 2362 int r;
b012e921 2363
6e866b33
MB
2364 if (argc >= 2) {
2365 r = ifname_mangle(argv[1]);
2366 if (r < 0)
2367 return r;
b012e921
MB
2368 }
2369
6e866b33
MB
2370 if (arg_ifindex <= 0)
2371 return status_all(bus, STATUS_PRIVATE);
2372
2373 if (argc < 3)
2374 return status_ifindex(bus, arg_ifindex, NULL, STATUS_PRIVATE, NULL);
b012e921 2375
a10f5d05 2376 r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSOverTLS", &error, NULL, "is", arg_ifindex, argv[2]);
f2dec872
BR
2377 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2378 sd_bus_error_free(&error);
2379
a10f5d05 2380 r = bus_call_method(
f2dec872 2381 bus,
a10f5d05 2382 bus_network_mgr,
f2dec872
BR
2383 "SetLinkDNSOverTLS",
2384 &error,
2385 NULL,
2386 "is", arg_ifindex, argv[2]);
2387 }
b012e921 2388 if (r < 0) {
b012e921
MB
2389 if (arg_ifindex_permissive &&
2390 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2391 return 0;
2392
2393 return log_error_errno(r, "Failed to set DNSOverTLS configuration: %s", bus_error_message(&error, r));
2394 }
2395
2396 return 0;
2397}
2398
2399static int verb_dnssec(int argc, char **argv, void *userdata) {
2400 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2401 sd_bus *bus = ASSERT_PTR(userdata);
6e866b33 2402 int r;
b012e921 2403
6e866b33
MB
2404 if (argc >= 2) {
2405 r = ifname_mangle(argv[1]);
2406 if (r < 0)
2407 return r;
b012e921
MB
2408 }
2409
6e866b33
MB
2410 if (arg_ifindex <= 0)
2411 return status_all(bus, STATUS_DNSSEC);
2412
2413 if (argc < 3)
2414 return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNSSEC, NULL);
b012e921 2415
a10f5d05 2416 r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]);
f2dec872
BR
2417 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2418 sd_bus_error_free(&error);
2419
a10f5d05 2420 r = bus_call_method(bus, bus_network_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]);
f2dec872 2421 }
b012e921 2422 if (r < 0) {
b012e921
MB
2423 if (arg_ifindex_permissive &&
2424 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2425 return 0;
2426
2427 return log_error_errno(r, "Failed to set DNSSEC configuration: %s", bus_error_message(&error, r));
2428 }
2429
2430 return 0;
2431}
2432
a10f5d05 2433static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator, sd_bus_error *error) {
f2dec872
BR
2434 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
2435 int r;
2436
a10f5d05 2437 r = bus_message_new_method_call(bus, &req, locator, "SetLinkDNSSECNegativeTrustAnchors");
f2dec872
BR
2438 if (r < 0)
2439 return bus_log_create_error(r);
2440
2441 r = sd_bus_message_append(req, "i", arg_ifindex);
2442 if (r < 0)
2443 return bus_log_create_error(r);
2444
2445 r = sd_bus_message_append_strv(req, nta);
2446 if (r < 0)
2447 return bus_log_create_error(r);
2448
2449 return sd_bus_call(bus, req, 0, error, NULL);
2450}
2451
b012e921
MB
2452static int verb_nta(int argc, char **argv, void *userdata) {
2453 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2454 sd_bus *bus = ASSERT_PTR(userdata);
6e866b33
MB
2455 int r;
2456 bool clear;
b012e921 2457
6e866b33
MB
2458 if (argc >= 2) {
2459 r = ifname_mangle(argv[1]);
2460 if (r < 0)
2461 return r;
2462 }
2463
2464 if (arg_ifindex <= 0)
b012e921
MB
2465 return status_all(bus, STATUS_NTA);
2466
6e866b33
MB
2467 if (argc < 3)
2468 return status_ifindex(bus, arg_ifindex, NULL, STATUS_NTA, NULL);
b012e921 2469
6e866b33
MB
2470 /* If only argument is the empty string, then call SetLinkDNSSECNegativeTrustAnchors()
2471 * with an empty list, which will clear the list of domains for an interface. */
2472 clear = strv_equal(argv + 2, STRV_MAKE(""));
b012e921 2473
6e866b33
MB
2474 if (!clear)
2475 STRV_FOREACH(p, argv + 2) {
2476 r = dns_name_is_valid(*p);
2477 if (r < 0)
2478 return log_error_errno(r, "Failed to validate specified domain %s: %m", *p);
a032b68d
MB
2479 if (r == 0)
2480 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2481 "Domain not valid: %s",
2482 *p);
52ad194e 2483 }
52ad194e 2484
a10f5d05 2485 r = call_nta(bus, clear ? NULL : argv + 2, bus_resolve_mgr, &error);
f2dec872
BR
2486 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2487 sd_bus_error_free(&error);
b012e921 2488
a10f5d05 2489 r = call_nta(bus, clear ? NULL : argv + 2, bus_network_mgr, &error);
f2dec872 2490 }
b012e921 2491 if (r < 0) {
b012e921
MB
2492 if (arg_ifindex_permissive &&
2493 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2494 return 0;
52ad194e 2495
b012e921 2496 return log_error_errno(r, "Failed to set DNSSEC NTA configuration: %s", bus_error_message(&error, r));
52ad194e 2497 }
b012e921
MB
2498
2499 return 0;
52ad194e
MB
2500}
2501
b012e921 2502static int verb_revert_link(int argc, char **argv, void *userdata) {
52ad194e 2503 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
086111aa 2504 sd_bus *bus = ASSERT_PTR(userdata);
6e866b33 2505 int r;
52ad194e 2506
6e866b33
MB
2507 if (argc >= 2) {
2508 r = ifname_mangle(argv[1]);
2509 if (r < 0)
2510 return r;
b012e921
MB
2511 }
2512
6e866b33
MB
2513 if (arg_ifindex <= 0)
2514 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Interface argument required.");
2515
a10f5d05 2516 r = bus_call_method(bus, bus_resolve_mgr, "RevertLink", &error, NULL, "i", arg_ifindex);
f2dec872
BR
2517 if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
2518 sd_bus_error_free(&error);
2519
a10f5d05 2520 r = bus_call_method(bus, bus_network_mgr, "RevertLinkDNS", &error, NULL, "i", arg_ifindex);
f2dec872 2521 }
b012e921
MB
2522 if (r < 0) {
2523 if (arg_ifindex_permissive &&
2524 sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
2525 return 0;
2526
52ad194e 2527 return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r));
b012e921 2528 }
52ad194e
MB
2529
2530 return 0;
2531}
2532
a10f5d05 2533static int verb_log_level(int argc, char *argv[], void *userdata) {
086111aa 2534 sd_bus *bus = ASSERT_PTR(userdata);
a10f5d05 2535
ea0999c9 2536 assert(IN_SET(argc, 1, 2));
a10f5d05 2537
ea0999c9 2538 return verb_log_control_common(bus, "org.freedesktop.resolve1", argv[0], argc == 2 ? argv[1] : NULL);
a10f5d05
MB
2539}
2540
086111aa
LB
2541static int monitor_rkey_from_json(JsonVariant *v, DnsResourceKey **ret_key) {
2542 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
2543 uint16_t type = 0, class = 0;
2544 const char *name = NULL;
2545 int r;
2546
2547 JsonDispatch dispatch_table[] = {
2548 { "class", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&class), JSON_MANDATORY },
2549 { "type", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&type), JSON_MANDATORY },
2550 { "name", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&name), JSON_MANDATORY },
2551 {}
2552 };
2553
2554 assert(v);
2555 assert(ret_key);
2556
2557 r = json_dispatch(v, dispatch_table, NULL, 0, NULL);
2558 if (r < 0)
2559 return r;
2560
2561 key = dns_resource_key_new(class, type, name);
2562 if (!key)
2563 return -ENOMEM;
2564
2565 *ret_key = TAKE_PTR(key);
2566 return 0;
2567}
2568
2569static int print_question(char prefix, const char *color, JsonVariant *question) {
2570 JsonVariant *q = NULL;
2571 int r;
2572
2573 assert(color);
2574
2575 JSON_VARIANT_ARRAY_FOREACH(q, question) {
2576 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
2577 char buf[DNS_RESOURCE_KEY_STRING_MAX];
2578
2579 r = monitor_rkey_from_json(q, &key);
2580 if (r < 0) {
2581 log_warning_errno(r, "Received monitor message with invalid question key, ignoring: %m");
2582 continue;
2583 }
2584
2585 printf("%s%s %c%s: %s\n",
2586 color,
2587 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
2588 prefix,
2589 ansi_normal(),
2590 dns_resource_key_to_string(key, buf, sizeof(buf)));
2591 }
2592
2593 return 0;
2594}
2595
2596static int print_answer(JsonVariant *answer) {
2597 JsonVariant *a;
2598 int r;
2599
2600 JSON_VARIANT_ARRAY_FOREACH(a, answer) {
2601 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
2602 _cleanup_free_ void *d = NULL;
2603 JsonVariant *jraw;
2604 const char *s;
2605 size_t l;
2606
2607 jraw = json_variant_by_key(a, "raw");
2608 if (!jraw) {
2609 log_warning("Received monitor answer lacking valid raw data, ignoring.");
2610 continue;
2611 }
2612
2613 r = json_variant_unbase64(jraw, &d, &l);
2614 if (r < 0) {
2615 log_warning_errno(r, "Failed to undo base64 encoding of monitor answer raw data, ignoring.");
2616 continue;
2617 }
2618
2619 r = dns_resource_record_new_from_raw(&rr, d, l);
2620 if (r < 0) {
2621 log_warning_errno(r, "Failed to parse monitor answer RR, ignoring: %m");
2622 continue;
2623 }
2624
2625 s = dns_resource_record_to_string(rr);
2626 if (!s)
2627 return log_oom();
2628
2629 printf("%s%s A%s: %s\n",
2630 ansi_highlight_yellow(),
2631 special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
2632 ansi_normal(),
2633 s);
2634 }
2635
2636 return 0;
2637}
2638
2639static void monitor_query_dump(JsonVariant *v) {
2640 _cleanup_(json_variant_unrefp) JsonVariant *question = NULL, *answer = NULL, *collected_questions = NULL;
2641 int rcode = -1, error = 0, r;
2642 const char *state = NULL;
2643
2644 assert(v);
2645
2646 JsonDispatch dispatch_table[] = {
2647 { "question", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&question), JSON_MANDATORY },
2648 { "answer", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&answer), 0 },
2649 { "collectedQuestions", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&collected_questions), 0 },
2650 { "state", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&state), JSON_MANDATORY },
2651 { "rcode", JSON_VARIANT_INTEGER, json_dispatch_int, PTR_TO_SIZE(&rcode), 0 },
2652 { "errno", JSON_VARIANT_INTEGER, json_dispatch_int, PTR_TO_SIZE(&error), 0 },
2653 {}
2654 };
2655
2656 r = json_dispatch(v, dispatch_table, NULL, 0, NULL);
2657 if (r < 0)
2658 return (void) log_warning("Received malformed monitor message, ignoring.");
2659
2660 /* First show the current question */
2661 print_question('Q', ansi_highlight_cyan(), question);
2662
2663 /* And then show the questions that led to this one in case this was a CNAME chain */
2664 print_question('C', ansi_highlight_grey(), collected_questions);
2665
2666 printf("%s%s S%s: %s\n",
2667 streq_ptr(state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
2668 special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
2669 ansi_normal(),
2670 strna(streq_ptr(state, "errno") ? errno_to_name(error) :
2671 streq_ptr(state, "rcode-failure") ? dns_rcode_to_string(rcode) :
2672 state));
2673
2674 print_answer(answer);
2675}
2676
2677static int monitor_reply(
2678 Varlink *link,
2679 JsonVariant *parameters,
2680 const char *error_id,
2681 VarlinkReplyFlags flags,
2682 void *userdata) {
2683
2684 assert(link);
2685
2686 if (error_id) {
2687 bool disconnect;
2688
2689 disconnect = streq(error_id, VARLINK_ERROR_DISCONNECTED);
2690 if (disconnect)
2691 log_info("Disconnected.");
2692 else
2693 log_error("Varlink error: %s", error_id);
2694
2695 (void) sd_event_exit(ASSERT_PTR(varlink_get_event(link)), disconnect ? EXIT_SUCCESS : EXIT_FAILURE);
2696 return 0;
2697 }
2698
2699 if (json_variant_by_key(parameters, "ready")) {
2700 /* The first message coming in will just indicate that we are now subscribed. We let our
2701 * caller know if they asked for it. Once the caller sees this they should know that we are
2702 * not going to miss any queries anymore. */
2703 (void) sd_notify(/* unset_environment=false */ false, "READY=1");
2704 return 0;
2705 }
2706
2707 if (arg_json_format_flags & JSON_FORMAT_OFF) {
2708 monitor_query_dump(parameters);
2709 printf("\n");
2710 } else
2711 json_variant_dump(parameters, arg_json_format_flags, NULL, NULL);
2712
2713 fflush(stdout);
2714
2715 return 0;
2716}
2717
2718static int verb_monitor(int argc, char *argv[], void *userdata) {
2719 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
2720 _cleanup_(varlink_unrefp) Varlink *vl = NULL;
2721 int r, c;
2722
2723 r = sd_event_default(&event);
2724 if (r < 0)
2725 return log_error_errno(r, "Failed to get event loop: %m");
2726
2727 r = sd_event_set_signal_exit(event, true);
2728 if (r < 0)
2729 return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m");
2730
2731 r = varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
2732 if (r < 0)
2733 return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
2734
2735 r = varlink_set_relative_timeout(vl, USEC_INFINITY); /* We want the monitor to run basically forever */
2736 if (r < 0)
2737 return log_error_errno(r, "Failed to set varlink time-out: %m");
2738
2739 r = varlink_attach_event(vl, event, SD_EVENT_PRIORITY_NORMAL);
2740 if (r < 0)
2741 return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
2742
2743 r = varlink_bind_reply(vl, monitor_reply);
2744 if (r < 0)
2745 return log_error_errno(r, "Failed to bind reply callback to varlink connection: %m");
2746
2747 r = varlink_observe(vl, "io.systemd.Resolve.Monitor.SubscribeQueryResults", NULL);
2748 if (r < 0)
2749 return log_error_errno(r, "Failed to issue SubscribeQueryResults() varlink call: %m");
2750
2751 r = sd_event_loop(event);
2752 if (r < 0)
2753 return log_error_errno(r, "Failed to run event loop: %m");
2754
2755 r = sd_event_get_exit_code(event, &c);
2756 if (r < 0)
2757 return log_error_errno(r, "Failed to get exit code: %m");
2758
2759 return c;
2760}
2761
4c89c718
MP
2762static void help_protocol_types(void) {
2763 if (arg_legend)
2764 puts("Known protocol types:");
086111aa
LB
2765 puts("dns\n"
2766 "llmnr\n"
2767 "llmnr-ipv4\n"
2768 "llmnr-ipv6\n"
2769 "mdns\n"
2770 "mdns-ipv4\n"
2771 "mdns-ipv6");
4c89c718
MP
2772}
2773
2774static void help_dns_types(void) {
4c89c718
MP
2775 if (arg_legend)
2776 puts("Known DNS RR types:");
b012e921
MB
2777
2778 DUMP_STRING_TABLE(dns_type, int, _DNS_TYPE_MAX);
4c89c718
MP
2779}
2780
2781static void help_dns_classes(void) {
4c89c718
MP
2782 if (arg_legend)
2783 puts("Known DNS RR classes:");
b012e921
MB
2784
2785 DUMP_STRING_TABLE(dns_class, int, _DNS_CLASS_MAX);
4c89c718
MP
2786}
2787
6e866b33
MB
2788static int compat_help(void) {
2789 _cleanup_free_ char *link = NULL;
2790 int r;
2791
2792 r = terminal_urlify_man("resolvectl", "1", &link);
2793 if (r < 0)
2794 return log_oom();
2795
aa27b158
MP
2796 printf("%1$s [OPTIONS...] HOSTNAME|ADDRESS...\n"
2797 "%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n"
2798 "%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n"
2799 "%1$s [OPTIONS...] --statistics\n"
2800 "%1$s [OPTIONS...] --reset-statistics\n"
2801 "\n"
e1f67bc7 2802 "%2$sResolve domain names, IPv4 and IPv6 addresses, DNS records, and services.%3$s\n\n"
aa27b158
MP
2803 " -h --help Show this help\n"
2804 " --version Show package version\n"
5a920b42 2805 " --no-pager Do not pipe output into a pager\n"
aa27b158
MP
2806 " -4 Resolve IPv4 addresses\n"
2807 " -6 Resolve IPv6 addresses\n"
2808 " -i --interface=INTERFACE Look on interface\n"
2809 " -p --protocol=PROTO|help Look via protocol\n"
2810 " -t --type=TYPE|help Query RR with DNS type\n"
2811 " -c --class=CLASS|help Query RR with DNS class\n"
2812 " --service Resolve service (SRV)\n"
2813 " --service-address=BOOL Resolve address for services (default: yes)\n"
2814 " --service-txt=BOOL Resolve TXT records for services (default: yes)\n"
2815 " --openpgp Query OpenPGP public key\n"
2816 " --tlsa Query TLS public key\n"
2817 " --cname=BOOL Follow CNAME redirects (default: yes)\n"
2818 " --search=BOOL Use search domains for single-label names\n"
2819 " (default: yes)\n"
2820 " --raw[=payload|packet] Dump the answer as binary data\n"
2821 " --legend=BOOL Print headers and additional info (default: yes)\n"
2822 " --statistics Show resolver statistics\n"
2823 " --reset-statistics Reset resolver statistics\n"
5a920b42
MP
2824 " --status Show link and server status\n"
2825 " --flush-caches Flush all local DNS caches\n"
f5e65279
MB
2826 " --reset-server-features\n"
2827 " Forget learnt DNS server feature levels\n"
52ad194e
MB
2828 " --set-dns=SERVER Set per-interface DNS server address\n"
2829 " --set-domain=DOMAIN Set per-interface search domain\n"
2830 " --set-llmnr=MODE Set per-interface LLMNR mode\n"
2831 " --set-mdns=MODE Set per-interface MulticastDNS mode\n"
b012e921 2832 " --set-dnsovertls=MODE Set per-interface DNS-over-TLS mode\n"
52ad194e
MB
2833 " --set-dnssec=MODE Set per-interface DNSSEC mode\n"
2834 " --set-nta=DOMAIN Set per-interface DNSSEC NTA\n"
2835 " --revert Revert per-interface configuration\n"
3a6ce677
BR
2836 "\nSee the %4$s for details.\n",
2837 program_invocation_short_name,
2838 ansi_highlight(),
2839 ansi_normal(),
2840 link);
6e866b33
MB
2841
2842 return 0;
4c89c718
MP
2843}
2844
6e866b33
MB
2845static int native_help(void) {
2846 _cleanup_free_ char *link = NULL;
2847 int r;
2848
2849 r = terminal_urlify_man("resolvectl", "1", &link);
2850 if (r < 0)
2851 return log_oom();
2852
e1f67bc7 2853 printf("%s [OPTIONS...] COMMAND ...\n"
b012e921 2854 "\n"
e1f67bc7
MB
2855 "%sSend control commands to the network name resolution manager, or%s\n"
2856 "%sresolve domain names, IPv4 and IPv6 addresses, DNS records, and services.%s\n"
2857 "\nCommands:\n"
b012e921
MB
2858 " query HOSTNAME|ADDRESS... Resolve domain names, IPv4 and IPv6 addresses\n"
2859 " service [[NAME] TYPE] DOMAIN Resolve service (SRV)\n"
2860 " openpgp EMAIL@DOMAIN... Query OpenPGP public key\n"
2861 " tlsa DOMAIN[:PORT]... Query TLS public key\n"
2862 " status [LINK...] Show link and server status\n"
2863 " statistics Show resolver statistics\n"
2864 " reset-statistics Reset resolver statistics\n"
2865 " flush-caches Flush all local DNS caches\n"
2866 " reset-server-features Forget learnt DNS server feature levels\n"
086111aa 2867 " monitor Monitor DNS queries\n"
b012e921
MB
2868 " dns [LINK [SERVER...]] Get/set per-interface DNS server address\n"
2869 " domain [LINK [DOMAIN...]] Get/set per-interface search domain\n"
6e866b33 2870 " default-route [LINK [BOOL]] Get/set per-interface default route flag\n"
b012e921
MB
2871 " llmnr [LINK [MODE]] Get/set per-interface LLMNR mode\n"
2872 " mdns [LINK [MODE]] Get/set per-interface MulticastDNS mode\n"
2873 " dnsovertls [LINK [MODE]] Get/set per-interface DNS-over-TLS mode\n"
2874 " dnssec [LINK [MODE]] Get/set per-interface DNSSEC mode\n"
2875 " nta [LINK [DOMAIN...]] Get/set per-interface DNSSEC NTA\n"
2876 " revert LINK Revert per-interface configuration\n"
7de6915e 2877 " log-level [LEVEL] Get/set logging threshold for systemd-resolved\n"
e1f67bc7
MB
2878 "\nOptions:\n"
2879 " -h --help Show this help\n"
2880 " --version Show package version\n"
2881 " --no-pager Do not pipe output into a pager\n"
2882 " -4 Resolve IPv4 addresses\n"
2883 " -6 Resolve IPv6 addresses\n"
2884 " -i --interface=INTERFACE Look on interface\n"
2885 " -p --protocol=PROTO|help Look via protocol\n"
2886 " -t --type=TYPE|help Query RR with DNS type\n"
2887 " -c --class=CLASS|help Query RR with DNS class\n"
2888 " --service-address=BOOL Resolve address for services (default: yes)\n"
2889 " --service-txt=BOOL Resolve TXT records for services (default: yes)\n"
2890 " --cname=BOOL Follow CNAME redirects (default: yes)\n"
3a6ce677
BR
2891 " --validate=BOOL Allow DNSSEC validation (default: yes)\n"
2892 " --synthesize=BOOL Allow synthetic response (default: yes)\n"
2893 " --cache=BOOL Allow response from cache (default: yes)\n"
2894 " --zone=BOOL Allow response from locally registered mDNS/LLMNR\n"
2895 " records (default: yes)\n"
086111aa
LB
2896 " --trust-anchor=BOOL Allow response from local trust anchor (default:\n"
2897 " yes)\n"
3a6ce677 2898 " --network=BOOL Allow response from network (default: yes)\n"
086111aa
LB
2899 " --search=BOOL Use search domains for single-label names (default:\n"
2900 " yes)\n"
e1f67bc7
MB
2901 " --raw[=payload|packet] Dump the answer as binary data\n"
2902 " --legend=BOOL Print headers and additional info (default: yes)\n"
086111aa
LB
2903 " --json=MODE Output as JSON\n"
2904 " -j Same as --json=pretty on tty, --json=short\n"
2905 " otherwise\n"
3a6ce677
BR
2906 "\nSee the %s for details.\n",
2907 program_invocation_short_name,
2908 ansi_highlight(),
2909 ansi_normal(),
2910 ansi_highlight(),
2911 ansi_normal(),
2912 link);
6e866b33
MB
2913
2914 return 0;
b012e921
MB
2915}
2916
2917static int verb_help(int argc, char **argv, void *userdata) {
6e866b33 2918 return native_help();
b012e921
MB
2919}
2920
2921static int compat_parse_argv(int argc, char *argv[]) {
4c89c718
MP
2922 enum {
2923 ARG_VERSION = 0x100,
2924 ARG_LEGEND,
2925 ARG_SERVICE,
2926 ARG_CNAME,
2927 ARG_SERVICE_ADDRESS,
2928 ARG_SERVICE_TXT,
aa27b158
MP
2929 ARG_OPENPGP,
2930 ARG_TLSA,
2931 ARG_RAW,
4c89c718
MP
2932 ARG_SEARCH,
2933 ARG_STATISTICS,
2934 ARG_RESET_STATISTICS,
5a920b42
MP
2935 ARG_STATUS,
2936 ARG_FLUSH_CACHES,
f5e65279 2937 ARG_RESET_SERVER_FEATURES,
5a920b42 2938 ARG_NO_PAGER,
52ad194e
MB
2939 ARG_SET_DNS,
2940 ARG_SET_DOMAIN,
2941 ARG_SET_LLMNR,
2942 ARG_SET_MDNS,
b012e921 2943 ARG_SET_PRIVATE,
52ad194e
MB
2944 ARG_SET_DNSSEC,
2945 ARG_SET_NTA,
2946 ARG_REVERT_LINK,
4c89c718
MP
2947 };
2948
2949 static const struct option options[] = {
f5e65279
MB
2950 { "help", no_argument, NULL, 'h' },
2951 { "version", no_argument, NULL, ARG_VERSION },
2952 { "type", required_argument, NULL, 't' },
2953 { "class", required_argument, NULL, 'c' },
2954 { "legend", required_argument, NULL, ARG_LEGEND },
2955 { "interface", required_argument, NULL, 'i' },
2956 { "protocol", required_argument, NULL, 'p' },
2957 { "cname", required_argument, NULL, ARG_CNAME },
2958 { "service", no_argument, NULL, ARG_SERVICE },
2959 { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
2960 { "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
2961 { "openpgp", no_argument, NULL, ARG_OPENPGP },
2962 { "tlsa", optional_argument, NULL, ARG_TLSA },
2963 { "raw", optional_argument, NULL, ARG_RAW },
2964 { "search", required_argument, NULL, ARG_SEARCH },
2965 { "statistics", no_argument, NULL, ARG_STATISTICS, },
2966 { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS },
2967 { "status", no_argument, NULL, ARG_STATUS },
2968 { "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES },
2969 { "reset-server-features", no_argument, NULL, ARG_RESET_SERVER_FEATURES },
2970 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
52ad194e
MB
2971 { "set-dns", required_argument, NULL, ARG_SET_DNS },
2972 { "set-domain", required_argument, NULL, ARG_SET_DOMAIN },
2973 { "set-llmnr", required_argument, NULL, ARG_SET_LLMNR },
2974 { "set-mdns", required_argument, NULL, ARG_SET_MDNS },
b012e921 2975 { "set-dnsovertls", required_argument, NULL, ARG_SET_PRIVATE },
52ad194e
MB
2976 { "set-dnssec", required_argument, NULL, ARG_SET_DNSSEC },
2977 { "set-nta", required_argument, NULL, ARG_SET_NTA },
2978 { "revert", no_argument, NULL, ARG_REVERT_LINK },
4c89c718
MP
2979 {}
2980 };
2981
2982 int c, r;
2983
2984 assert(argc >= 0);
2985 assert(argv);
2986
2987 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
8f232108 2988 switch (c) {
4c89c718
MP
2989
2990 case 'h':
6e866b33 2991 return compat_help();
4c89c718
MP
2992
2993 case ARG_VERSION:
2994 return version();
2995
2996 case '4':
2997 arg_family = AF_INET;
2998 break;
2999
3000 case '6':
3001 arg_family = AF_INET6;
3002 break;
3003
98393f85 3004 case 'i':
6e866b33 3005 r = ifname_mangle(optarg);
b012e921
MB
3006 if (r < 0)
3007 return r;
4c89c718 3008 break;
4c89c718
MP
3009
3010 case 't':
3011 if (streq(optarg, "help")) {
3012 help_dns_types();
3013 return 0;
3014 }
3015
3016 r = dns_type_from_string(optarg);
3a6ce677
BR
3017 if (r < 0)
3018 return log_error_errno(r, "Failed to parse RR record type %s: %m", optarg);
3019
4c89c718
MP
3020 arg_type = (uint16_t) r;
3021 assert((int) arg_type == r);
3022
3023 arg_mode = MODE_RESOLVE_RECORD;
3024 break;
3025
3026 case 'c':
3027 if (streq(optarg, "help")) {
3028 help_dns_classes();
3029 return 0;
3030 }
3031
3032 r = dns_class_from_string(optarg);
3a6ce677
BR
3033 if (r < 0)
3034 return log_error_errno(r, "Failed to parse RR record class %s: %m", optarg);
3035
4c89c718
MP
3036 arg_class = (uint16_t) r;
3037 assert((int) arg_class == r);
3038
3039 break;
3040
3041 case ARG_LEGEND:
3a6ce677 3042 r = parse_boolean_argument("--legend=", optarg, &arg_legend);
4c89c718 3043 if (r < 0)
3a6ce677 3044 return r;
4c89c718
MP
3045 break;
3046
3047 case 'p':
3048 if (streq(optarg, "help")) {
3049 help_protocol_types();
3050 return 0;
3051 } else if (streq(optarg, "dns"))
3052 arg_flags |= SD_RESOLVED_DNS;
3053 else if (streq(optarg, "llmnr"))
3054 arg_flags |= SD_RESOLVED_LLMNR;
3055 else if (streq(optarg, "llmnr-ipv4"))
3056 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
3057 else if (streq(optarg, "llmnr-ipv6"))
3058 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
2897b343
MP
3059 else if (streq(optarg, "mdns"))
3060 arg_flags |= SD_RESOLVED_MDNS;
3061 else if (streq(optarg, "mdns-ipv4"))
3062 arg_flags |= SD_RESOLVED_MDNS_IPV4;
3063 else if (streq(optarg, "mdns-ipv6"))
3064 arg_flags |= SD_RESOLVED_MDNS_IPV6;
6e866b33
MB
3065 else
3066 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3067 "Unknown protocol specifier: %s", optarg);
4c89c718
MP
3068
3069 break;
3070
3071 case ARG_SERVICE:
3072 arg_mode = MODE_RESOLVE_SERVICE;
3073 break;
3074
aa27b158
MP
3075 case ARG_OPENPGP:
3076 arg_mode = MODE_RESOLVE_OPENPGP;
3077 break;
3078
3079 case ARG_TLSA:
3080 arg_mode = MODE_RESOLVE_TLSA;
b012e921
MB
3081 if (!optarg || service_family_is_valid(optarg))
3082 arg_service_family = optarg;
6e866b33
MB
3083 else
3084 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3085 "Unknown service family \"%s\".", optarg);
aa27b158
MP
3086 break;
3087
3088 case ARG_RAW:
6e866b33
MB
3089 if (on_tty())
3090 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
3091 "Refusing to write binary data to tty.");
aa27b158
MP
3092
3093 if (optarg == NULL || streq(optarg, "payload"))
3094 arg_raw = RAW_PAYLOAD;
3095 else if (streq(optarg, "packet"))
3096 arg_raw = RAW_PACKET;
6e866b33
MB
3097 else
3098 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3099 "Unknown --raw specifier \"%s\".",
3100 optarg);
aa27b158
MP
3101
3102 arg_legend = false;
3103 break;
3104
4c89c718 3105 case ARG_CNAME:
3a6ce677 3106 r = parse_boolean_argument("--cname=", optarg, NULL);
4c89c718 3107 if (r < 0)
3a6ce677 3108 return r;
aa27b158 3109 SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0);
4c89c718
MP
3110 break;
3111
3112 case ARG_SERVICE_ADDRESS:
3a6ce677 3113 r = parse_boolean_argument("--service-address=", optarg, NULL);
4c89c718 3114 if (r < 0)
3a6ce677 3115 return r;
aa27b158 3116 SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0);
4c89c718
MP
3117 break;
3118
3119 case ARG_SERVICE_TXT:
3a6ce677 3120 r = parse_boolean_argument("--service-txt=", optarg, NULL);
4c89c718 3121 if (r < 0)
3a6ce677 3122 return r;
aa27b158 3123 SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0);
4c89c718
MP
3124 break;
3125
3126 case ARG_SEARCH:
3a6ce677 3127 r = parse_boolean_argument("--search=", optarg, NULL);
4c89c718 3128 if (r < 0)
3a6ce677 3129 return r;
aa27b158 3130 SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0);
4c89c718
MP
3131 break;
3132
3133 case ARG_STATISTICS:
3134 arg_mode = MODE_STATISTICS;
3135 break;
3136
3137 case ARG_RESET_STATISTICS:
3138 arg_mode = MODE_RESET_STATISTICS;
3139 break;
3140
5a920b42
MP
3141 case ARG_FLUSH_CACHES:
3142 arg_mode = MODE_FLUSH_CACHES;
3143 break;
3144
f5e65279
MB
3145 case ARG_RESET_SERVER_FEATURES:
3146 arg_mode = MODE_RESET_SERVER_FEATURES;
3147 break;
3148
5a920b42
MP
3149 case ARG_STATUS:
3150 arg_mode = MODE_STATUS;
3151 break;
3152
3153 case ARG_NO_PAGER:
6e866b33 3154 arg_pager_flags |= PAGER_DISABLE;
5a920b42
MP
3155 break;
3156
b012e921
MB
3157 case ARG_SET_DNS:
3158 r = strv_extend(&arg_set_dns, optarg);
52ad194e 3159 if (r < 0)
52ad194e 3160 return log_oom();
52ad194e 3161
52ad194e
MB
3162 arg_mode = MODE_SET_LINK;
3163 break;
52ad194e 3164
b012e921 3165 case ARG_SET_DOMAIN:
52ad194e
MB
3166 r = strv_extend(&arg_set_domain, optarg);
3167 if (r < 0)
3168 return log_oom();
3169
3170 arg_mode = MODE_SET_LINK;
3171 break;
52ad194e
MB
3172
3173 case ARG_SET_LLMNR:
b012e921 3174 arg_set_llmnr = optarg;
52ad194e
MB
3175 arg_mode = MODE_SET_LINK;
3176 break;
3177
3178 case ARG_SET_MDNS:
b012e921
MB
3179 arg_set_mdns = optarg;
3180 arg_mode = MODE_SET_LINK;
3181 break;
52ad194e 3182
b012e921
MB
3183 case ARG_SET_PRIVATE:
3184 arg_set_dns_over_tls = optarg;
52ad194e
MB
3185 arg_mode = MODE_SET_LINK;
3186 break;
3187
3188 case ARG_SET_DNSSEC:
b012e921 3189 arg_set_dnssec = optarg;
52ad194e
MB
3190 arg_mode = MODE_SET_LINK;
3191 break;
3192
3193 case ARG_SET_NTA:
52ad194e
MB
3194 r = strv_extend(&arg_set_nta, optarg);
3195 if (r < 0)
3196 return log_oom();
3197
3198 arg_mode = MODE_SET_LINK;
3199 break;
3200
3201 case ARG_REVERT_LINK:
3202 arg_mode = MODE_REVERT_LINK;
3203 break;
3204
4c89c718
MP
3205 case '?':
3206 return -EINVAL;
3207
3208 default:
ea0999c9 3209 assert_not_reached();
4c89c718
MP
3210 }
3211
6e866b33
MB
3212 if (arg_type == 0 && arg_class != 0)
3213 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3214 "--class= may only be used in conjunction with --type=.");
4c89c718 3215
6e866b33
MB
3216 if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE)
3217 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3218 "--service and --type= may not be combined.");
4c89c718
MP
3219
3220 if (arg_type != 0 && arg_class == 0)
3221 arg_class = DNS_CLASS_IN;
3222
3223 if (arg_class != 0 && arg_type == 0)
3224 arg_type = DNS_TYPE_A;
3225
52ad194e
MB
3226 if (IN_SET(arg_mode, MODE_SET_LINK, MODE_REVERT_LINK)) {
3227
6e866b33
MB
3228 if (arg_ifindex <= 0)
3229 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3230 "--set-dns=, --set-domain=, --set-llmnr=, --set-mdns=, --set-dnsovertls=, --set-dnssec=, --set-nta= and --revert require --interface=.");
52ad194e
MB
3231 }
3232
4c89c718
MP
3233 return 1 /* work to do */;
3234}
3235
b012e921
MB
3236static int native_parse_argv(int argc, char *argv[]) {
3237 enum {
3238 ARG_VERSION = 0x100,
3239 ARG_LEGEND,
3240 ARG_CNAME,
3a6ce677
BR
3241 ARG_VALIDATE,
3242 ARG_SYNTHESIZE,
3243 ARG_CACHE,
3244 ARG_ZONE,
3245 ARG_TRUST_ANCHOR,
3246 ARG_NETWORK,
b012e921
MB
3247 ARG_SERVICE_ADDRESS,
3248 ARG_SERVICE_TXT,
3249 ARG_RAW,
3250 ARG_SEARCH,
3251 ARG_NO_PAGER,
086111aa 3252 ARG_JSON,
b012e921 3253 };
4c89c718 3254
b012e921
MB
3255 static const struct option options[] = {
3256 { "help", no_argument, NULL, 'h' },
3257 { "version", no_argument, NULL, ARG_VERSION },
3258 { "type", required_argument, NULL, 't' },
3259 { "class", required_argument, NULL, 'c' },
3260 { "legend", required_argument, NULL, ARG_LEGEND },
3261 { "interface", required_argument, NULL, 'i' },
3262 { "protocol", required_argument, NULL, 'p' },
3263 { "cname", required_argument, NULL, ARG_CNAME },
3a6ce677
BR
3264 { "validate", required_argument, NULL, ARG_VALIDATE },
3265 { "synthesize", required_argument, NULL, ARG_SYNTHESIZE },
3266 { "cache", required_argument, NULL, ARG_CACHE },
3267 { "zone", required_argument, NULL, ARG_ZONE },
3268 { "trust-anchor", required_argument, NULL, ARG_TRUST_ANCHOR },
3269 { "network", required_argument, NULL, ARG_NETWORK },
b012e921
MB
3270 { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
3271 { "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
3272 { "raw", optional_argument, NULL, ARG_RAW },
3273 { "search", required_argument, NULL, ARG_SEARCH },
3274 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
086111aa 3275 { "json", required_argument, NULL, ARG_JSON },
b012e921
MB
3276 {}
3277 };
4c89c718 3278
b012e921 3279 int c, r;
4c89c718 3280
b012e921
MB
3281 assert(argc >= 0);
3282 assert(argv);
4c89c718 3283
086111aa 3284 while ((c = getopt_long(argc, argv, "h46i:t:c:p:j", options, NULL)) >= 0)
8f232108 3285 switch (c) {
4c89c718 3286
b012e921 3287 case 'h':
6e866b33 3288 return native_help();
b012e921
MB
3289
3290 case ARG_VERSION:
3291 return version();
4c89c718 3292
b012e921
MB
3293 case '4':
3294 arg_family = AF_INET;
3295 break;
3296
3297 case '6':
3298 arg_family = AF_INET6;
3299 break;
3300
3301 case 'i':
6e866b33 3302 r = ifname_mangle(optarg);
b012e921
MB
3303 if (r < 0)
3304 return r;
b012e921
MB
3305 break;
3306
3307 case 't':
3308 if (streq(optarg, "help")) {
3309 help_dns_types();
3310 return 0;
3311 }
3312
3313 r = dns_type_from_string(optarg);
3a6ce677
BR
3314 if (r < 0)
3315 return log_error_errno(r, "Failed to parse RR record type %s: %m", optarg);
3316
b012e921
MB
3317 arg_type = (uint16_t) r;
3318 assert((int) arg_type == r);
3319
3320 break;
3321
3322 case 'c':
3323 if (streq(optarg, "help")) {
3324 help_dns_classes();
3325 return 0;
3326 }
3327
3328 r = dns_class_from_string(optarg);
3a6ce677
BR
3329 if (r < 0)
3330 return log_error_errno(r, "Failed to parse RR record class %s: %m", optarg);
3331
b012e921
MB
3332 arg_class = (uint16_t) r;
3333 assert((int) arg_class == r);
3334
3335 break;
4c89c718 3336
b012e921 3337 case ARG_LEGEND:
3a6ce677 3338 r = parse_boolean_argument("--legend=", optarg, &arg_legend);
b012e921 3339 if (r < 0)
3a6ce677 3340 return r;
b012e921
MB
3341 break;
3342
3343 case 'p':
3344 if (streq(optarg, "help")) {
3345 help_protocol_types();
3346 return 0;
3347 } else if (streq(optarg, "dns"))
3348 arg_flags |= SD_RESOLVED_DNS;
3349 else if (streq(optarg, "llmnr"))
3350 arg_flags |= SD_RESOLVED_LLMNR;
3351 else if (streq(optarg, "llmnr-ipv4"))
3352 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
3353 else if (streq(optarg, "llmnr-ipv6"))
3354 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
3355 else if (streq(optarg, "mdns"))
3356 arg_flags |= SD_RESOLVED_MDNS;
3357 else if (streq(optarg, "mdns-ipv4"))
3358 arg_flags |= SD_RESOLVED_MDNS_IPV4;
3359 else if (streq(optarg, "mdns-ipv6"))
3360 arg_flags |= SD_RESOLVED_MDNS_IPV6;
6e866b33
MB
3361 else
3362 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3363 "Unknown protocol specifier: %s",
3364 optarg);
4c89c718 3365
b012e921 3366 break;
4c89c718 3367
b012e921 3368 case ARG_RAW:
6e866b33
MB
3369 if (on_tty())
3370 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
3371 "Refusing to write binary data to tty.");
4c89c718 3372
b012e921
MB
3373 if (optarg == NULL || streq(optarg, "payload"))
3374 arg_raw = RAW_PAYLOAD;
3375 else if (streq(optarg, "packet"))
3376 arg_raw = RAW_PACKET;
6e866b33
MB
3377 else
3378 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3379 "Unknown --raw specifier \"%s\".",
3380 optarg);
4c89c718 3381
b012e921
MB
3382 arg_legend = false;
3383 break;
4c89c718 3384
b012e921 3385 case ARG_CNAME:
3a6ce677 3386 r = parse_boolean_argument("--cname=", optarg, NULL);
b012e921 3387 if (r < 0)
3a6ce677 3388 return r;
b012e921
MB
3389 SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0);
3390 break;
4c89c718 3391
3a6ce677
BR
3392 case ARG_VALIDATE:
3393 r = parse_boolean_argument("--validate=", optarg, NULL);
3394 if (r < 0)
3395 return r;
3396 SET_FLAG(arg_flags, SD_RESOLVED_NO_VALIDATE, r == 0);
3397 break;
3398
3399 case ARG_SYNTHESIZE:
3400 r = parse_boolean_argument("--synthesize=", optarg, NULL);
3401 if (r < 0)
3402 return r;
3403 SET_FLAG(arg_flags, SD_RESOLVED_NO_SYNTHESIZE, r == 0);
3404 break;
3405
3406 case ARG_CACHE:
3407 r = parse_boolean_argument("--cache=", optarg, NULL);
3408 if (r < 0)
3409 return r;
3410 SET_FLAG(arg_flags, SD_RESOLVED_NO_CACHE, r == 0);
3411 break;
3412
3413 case ARG_ZONE:
3414 r = parse_boolean_argument("--zone=", optarg, NULL);
3415 if (r < 0)
3416 return r;
3417 SET_FLAG(arg_flags, SD_RESOLVED_NO_ZONE, r == 0);
3418 break;
3419
3420 case ARG_TRUST_ANCHOR:
3421 r = parse_boolean_argument("--trust-anchor=", optarg, NULL);
3422 if (r < 0)
3423 return r;
3424 SET_FLAG(arg_flags, SD_RESOLVED_NO_TRUST_ANCHOR, r == 0);
3425 break;
3426
3427 case ARG_NETWORK:
3428 r = parse_boolean_argument("--network=", optarg, NULL);
3429 if (r < 0)
3430 return r;
3431 SET_FLAG(arg_flags, SD_RESOLVED_NO_NETWORK, r == 0);
3432 break;
3433
b012e921 3434 case ARG_SERVICE_ADDRESS:
3a6ce677 3435 r = parse_boolean_argument("--service-address=", optarg, NULL);
b012e921 3436 if (r < 0)
3a6ce677 3437 return r;
b012e921
MB
3438 SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0);
3439 break;
4c89c718 3440
b012e921 3441 case ARG_SERVICE_TXT:
3a6ce677 3442 r = parse_boolean_argument("--service-txt=", optarg, NULL);
b012e921 3443 if (r < 0)
3a6ce677 3444 return r;
b012e921
MB
3445 SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0);
3446 break;
4c89c718 3447
b012e921 3448 case ARG_SEARCH:
3a6ce677 3449 r = parse_boolean_argument("--search=", optarg, NULL);
b012e921 3450 if (r < 0)
3a6ce677 3451 return r;
b012e921
MB
3452 SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0);
3453 break;
4c89c718 3454
b012e921 3455 case ARG_NO_PAGER:
6e866b33 3456 arg_pager_flags |= PAGER_DISABLE;
b012e921 3457 break;
aa27b158 3458
086111aa
LB
3459 case ARG_JSON:
3460 r = parse_json_argument(optarg, &arg_json_format_flags);
3461 if (r <= 0)
3462 return r;
3463
3464 break;
3465
3466 case 'j':
3467 arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
3468 break;
3469
b012e921
MB
3470 case '?':
3471 return -EINVAL;
3472
3473 default:
ea0999c9 3474 assert_not_reached();
aa27b158
MP
3475 }
3476
6e866b33
MB
3477 if (arg_type == 0 && arg_class != 0)
3478 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3479 "--class= may only be used in conjunction with --type=.");
aa27b158 3480
b012e921
MB
3481 if (arg_type != 0 && arg_class == 0)
3482 arg_class = DNS_CLASS_IN;
aa27b158 3483
b012e921
MB
3484 if (arg_class != 0 && arg_type == 0)
3485 arg_type = DNS_TYPE_A;
aa27b158 3486
b012e921
MB
3487 return 1 /* work to do */;
3488}
aa27b158 3489
b012e921
MB
3490static int native_main(int argc, char *argv[], sd_bus *bus) {
3491
3492 static const Verb verbs[] = {
3493 { "help", VERB_ANY, VERB_ANY, 0, verb_help },
3494 { "status", VERB_ANY, VERB_ANY, VERB_DEFAULT, verb_status },
3495 { "query", 2, VERB_ANY, 0, verb_query },
3496 { "service", 2, 4, 0, verb_service },
3497 { "openpgp", 2, VERB_ANY, 0, verb_openpgp },
3498 { "tlsa", 2, VERB_ANY, 0, verb_tlsa },
3499 { "statistics", VERB_ANY, 1, 0, show_statistics },
3500 { "reset-statistics", VERB_ANY, 1, 0, reset_statistics },
3501 { "flush-caches", VERB_ANY, 1, 0, flush_caches },
3502 { "reset-server-features", VERB_ANY, 1, 0, reset_server_features },
3503 { "dns", VERB_ANY, VERB_ANY, 0, verb_dns },
3504 { "domain", VERB_ANY, VERB_ANY, 0, verb_domain },
6e866b33 3505 { "default-route", VERB_ANY, 3, 0, verb_default_route },
b012e921
MB
3506 { "llmnr", VERB_ANY, 3, 0, verb_llmnr },
3507 { "mdns", VERB_ANY, 3, 0, verb_mdns },
6e866b33 3508 { "dnsovertls", VERB_ANY, 3, 0, verb_dns_over_tls },
b012e921
MB
3509 { "dnssec", VERB_ANY, 3, 0, verb_dnssec },
3510 { "nta", VERB_ANY, VERB_ANY, 0, verb_nta },
6e866b33 3511 { "revert", VERB_ANY, 2, 0, verb_revert_link },
a10f5d05 3512 { "log-level", VERB_ANY, 2, 0, verb_log_level },
086111aa 3513 { "monitor", VERB_ANY, 1, 0, verb_monitor },
b012e921
MB
3514 {}
3515 };
aa27b158 3516
b012e921
MB
3517 return dispatch_verb(argc, argv, verbs, bus);
3518}
aa27b158 3519
b012e921
MB
3520static int translate(const char *verb, const char *single_arg, size_t num_args, char **args, sd_bus *bus) {
3521 char **fake, **p;
a10f5d05 3522 size_t num;
4c89c718 3523
b012e921
MB
3524 assert(verb);
3525 assert(num_args == 0 || args);
4c89c718 3526
b012e921 3527 num = !!single_arg + num_args + 1;
4c89c718 3528
b012e921
MB
3529 p = fake = newa0(char *, num + 1);
3530 *p++ = (char *) verb;
3531 if (single_arg)
3532 *p++ = (char *) single_arg;
a10f5d05 3533 for (size_t i = 0; i < num_args; i++)
b012e921 3534 *p++ = args[i];
5a920b42 3535
b012e921
MB
3536 optind = 0;
3537 return native_main((int) num, fake, bus);
3538}
3539
3540static int compat_main(int argc, char *argv[], sd_bus *bus) {
3541 int r = 0;
5a920b42 3542
b012e921
MB
3543 switch (arg_mode) {
3544 case MODE_RESOLVE_HOST:
3545 case MODE_RESOLVE_RECORD:
3546 return translate("query", NULL, argc - optind, argv + optind, bus);
3547
3548 case MODE_RESOLVE_SERVICE:
3549 return translate("service", NULL, argc - optind, argv + optind, bus);
3550
3551 case MODE_RESOLVE_OPENPGP:
3552 return translate("openpgp", NULL, argc - optind, argv + optind, bus);
3553
3554 case MODE_RESOLVE_TLSA:
3555 return translate("tlsa", arg_service_family, argc - optind, argv + optind, bus);
3556
3557 case MODE_STATISTICS:
3558 return translate("statistics", NULL, 0, NULL, bus);
3559
3560 case MODE_RESET_STATISTICS:
3561 return translate("reset-statistics", NULL, 0, NULL, bus);
3562
3563 case MODE_FLUSH_CACHES:
3564 return translate("flush-caches", NULL, 0, NULL, bus);
5a920b42 3565
f5e65279 3566 case MODE_RESET_SERVER_FEATURES:
b012e921
MB
3567 return translate("reset-server-features", NULL, 0, NULL, bus);
3568
3569 case MODE_STATUS:
3570 return translate("status", NULL, argc - optind, argv + optind, bus);
3571
3572 case MODE_SET_LINK:
6e866b33
MB
3573 assert(arg_ifname);
3574
b012e921
MB
3575 if (arg_set_dns) {
3576 r = translate("dns", arg_ifname, strv_length(arg_set_dns), arg_set_dns, bus);
3577 if (r < 0)
3578 return r;
f5e65279
MB
3579 }
3580
b012e921
MB
3581 if (arg_set_domain) {
3582 r = translate("domain", arg_ifname, strv_length(arg_set_domain), arg_set_domain, bus);
3583 if (r < 0)
3584 return r;
3585 }
f5e65279 3586
b012e921
MB
3587 if (arg_set_nta) {
3588 r = translate("nta", arg_ifname, strv_length(arg_set_nta), arg_set_nta, bus);
3589 if (r < 0)
3590 return r;
3591 }
5a920b42 3592
b012e921
MB
3593 if (arg_set_llmnr) {
3594 r = translate("llmnr", arg_ifname, 1, (char **) &arg_set_llmnr, bus);
3595 if (r < 0)
3596 return r;
3597 }
5a920b42 3598
b012e921
MB
3599 if (arg_set_mdns) {
3600 r = translate("mdns", arg_ifname, 1, (char **) &arg_set_mdns, bus);
3601 if (r < 0)
3602 return r;
3603 }
5a920b42 3604
b012e921
MB
3605 if (arg_set_dns_over_tls) {
3606 r = translate("dnsovertls", arg_ifname, 1, (char **) &arg_set_dns_over_tls, bus);
3607 if (r < 0)
3608 return r;
3609 }
52ad194e 3610
b012e921
MB
3611 if (arg_set_dnssec) {
3612 r = translate("dnssec", arg_ifname, 1, (char **) &arg_set_dnssec, bus);
3613 if (r < 0)
3614 return r;
52ad194e
MB
3615 }
3616
b012e921 3617 return r;
52ad194e
MB
3618
3619 case MODE_REVERT_LINK:
6e866b33
MB
3620 assert(arg_ifname);
3621
b012e921
MB
3622 return translate("revert", arg_ifname, 0, NULL, bus);
3623
3624 case _MODE_INVALID:
ea0999c9 3625 assert_not_reached();
b012e921
MB
3626 }
3627
3628 return 0;
3629}
3630
6e866b33
MB
3631static int run(int argc, char **argv) {
3632 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2345c4ad 3633 bool compat = false;
b012e921
MB
3634 int r;
3635
3636 setlocale(LC_ALL, "");
3a6ce677 3637 log_setup();
b012e921 3638
2345c4ad
LB
3639 if (invoked_as(argv, "resolvconf")) {
3640 compat = true;
b012e921 3641 r = resolvconf_parse_argv(argc, argv);
2345c4ad
LB
3642 } else if (invoked_as(argv, "systemd-resolve")) {
3643 compat = true;
b012e921 3644 r = compat_parse_argv(argc, argv);
2345c4ad 3645 } else
b012e921
MB
3646 r = native_parse_argv(argc, argv);
3647 if (r <= 0)
6e866b33 3648 return r;
52ad194e 3649
b012e921 3650 r = sd_bus_open_system(&bus);
6e866b33
MB
3651 if (r < 0)
3652 return log_error_errno(r, "sd_bus_open_system: %m");
5a920b42 3653
2345c4ad 3654 if (compat)
6e866b33 3655 return compat_main(argc, argv, bus);
52ad194e 3656
6e866b33 3657 return native_main(argc, argv, bus);
4c89c718 3658}
6e866b33
MB
3659
3660DEFINE_MAIN_FUNCTION(run);