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