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