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