]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve-host/resolve-host.c
resolved: rework mDNS cache-flush bit handling
[thirdparty/systemd.git] / src / resolve-host / resolve-host.c
CommitLineData
bdef7319
ZJS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
bdef7319 22#include <getopt.h>
cf0fbc49 23#include <net/if.h>
bdef7319
ZJS
24
25#include "sd-bus.h"
3f6fd1ba
LP
26
27#include "af-list.h"
b5efdb8a 28#include "alloc-util.h"
bdef7319 29#include "bus-error.h"
3f6fd1ba 30#include "bus-util.h"
45ec7efb 31#include "escape.h"
bdef7319 32#include "in-addr-util.h"
6bedfcbb 33#include "parse-util.h"
51323288 34#include "resolved-def.h"
3f6fd1ba 35#include "resolved-dns-packet.h"
2d4c5cbc 36
bdef7319
ZJS
37#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
38
39static int arg_family = AF_UNSPEC;
40static int arg_ifindex = 0;
4b548ef3 41static uint16_t arg_type = 0;
2d4c5cbc 42static uint16_t arg_class = 0;
b93312f5 43static bool arg_legend = true;
51323288 44static uint64_t arg_flags = 0;
45ec7efb 45static bool arg_resolve_service = false;
51323288 46
78c6a153 47static void print_source(uint64_t flags, usec_t rtt) {
74998408 48 char rtt_str[FORMAT_TIMESTAMP_MAX];
51323288
LP
49
50 if (!arg_legend)
51 return;
52
78c6a153 53 if (flags == 0)
51323288
LP
54 return;
55
56 fputs("\n-- Information acquired via", stdout);
57
58 if (flags != 0)
59 printf(" protocol%s%s%s",
60 flags & SD_RESOLVED_DNS ? " DNS" :"",
61 flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
62 flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "");
63
74998408
TG
64 assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
65
66 printf(" in %s", rtt_str);
67
51323288
LP
68 fputc('.', stdout);
69 fputc('\n', stdout);
931851e8
LP
70
71 printf("-- Data is authenticated: %s\n", yes_no(flags & SD_RESOLVED_AUTHENTICATED));
51323288 72}
bdef7319 73
79266746 74static int resolve_host(sd_bus *bus, const char *name) {
bdef7319 75
4afd3348
LP
76 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
77 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
79266746 78 const char *canonical = NULL;
78c6a153 79 char ifname[IF_NAMESIZE] = "";
bdef7319 80 unsigned c = 0;
78c6a153 81 int r;
51323288 82 uint64_t flags;
74998408 83 usec_t ts;
bdef7319
ZJS
84
85 assert(name);
86
78c6a153
LP
87 if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
88 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
89
90 log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
bdef7319
ZJS
91
92 r = sd_bus_message_new_method_call(
93 bus,
94 &req,
95 "org.freedesktop.resolve1",
96 "/org/freedesktop/resolve1",
97 "org.freedesktop.resolve1.Manager",
98 "ResolveHostname");
02dd6e18
LP
99 if (r < 0)
100 return bus_log_create_error(r);
bdef7319 101
51323288 102 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
02dd6e18
LP
103 if (r < 0)
104 return bus_log_create_error(r);
bdef7319 105
74998408
TG
106 ts = now(CLOCK_MONOTONIC);
107
bdef7319 108 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
45ec7efb
LP
109 if (r < 0)
110 return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r));
bdef7319 111
74998408
TG
112 ts = now(CLOCK_MONOTONIC) - ts;
113
78c6a153 114 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
02dd6e18
LP
115 if (r < 0)
116 return bus_log_parse_error(r);
bdef7319 117
78c6a153 118 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
bdef7319 119 _cleanup_free_ char *pretty = NULL;
78c6a153 120 int ifindex, family;
45ec7efb
LP
121 const void *a;
122 size_t sz;
78c6a153
LP
123
124 assert_cc(sizeof(int) == sizeof(int32_t));
bdef7319 125
78c6a153 126 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
02dd6e18
LP
127 if (r < 0)
128 return bus_log_parse_error(r);
bdef7319
ZJS
129
130 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
02dd6e18
LP
131 if (r < 0)
132 return bus_log_parse_error(r);
bdef7319 133
bdef7319 134 r = sd_bus_message_exit_container(reply);
02dd6e18
LP
135 if (r < 0)
136 return bus_log_parse_error(r);
bdef7319 137
79266746 138 if (!IN_SET(family, AF_INET, AF_INET6)) {
be636413 139 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
bdef7319
ZJS
140 continue;
141 }
142
143 if (sz != FAMILY_ADDRESS_SIZE(family)) {
78c6a153 144 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
45ec7efb 145 return -EINVAL;
bdef7319
ZJS
146 }
147
78c6a153
LP
148 ifname[0] = 0;
149 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
150 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
bdef7319 151
bdef7319 152 r = in_addr_to_string(family, a, &pretty);
78c6a153
LP
153 if (r < 0)
154 return log_error_errno(r, "Failed to print address for %s: %m", name);
bdef7319 155
79266746
LP
156 printf("%*s%s %s%s%s\n",
157 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
158 pretty,
159 isempty(ifname) ? "" : "%", ifname);
bdef7319
ZJS
160
161 c++;
162 }
79266746
LP
163 if (r < 0)
164 return bus_log_parse_error(r);
165
166 r = sd_bus_message_exit_container(reply);
167 if (r < 0)
168 return bus_log_parse_error(r);
169
51323288 170 r = sd_bus_message_read(reply, "st", &canonical, &flags);
79266746
LP
171 if (r < 0)
172 return bus_log_parse_error(r);
173
ece174c5 174 if (!streq(name, canonical))
79266746
LP
175 printf("%*s%s (%s)\n",
176 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
177 canonical);
bdef7319
ZJS
178
179 if (c == 0) {
180 log_error("%s: no addresses found", name);
79266746
LP
181 return -ESRCH;
182 }
183
78c6a153 184 print_source(flags, ts);
51323288 185
79266746
LP
186 return 0;
187}
188
189static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
4afd3348
LP
190 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
191 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
79266746
LP
192 _cleanup_free_ char *pretty = NULL;
193 char ifname[IF_NAMESIZE] = "";
51323288 194 uint64_t flags;
79266746 195 unsigned c = 0;
74998408 196 usec_t ts;
79266746
LP
197 int r;
198
199 assert(bus);
200 assert(IN_SET(family, AF_INET, AF_INET6));
201 assert(address);
202
78c6a153
LP
203 if (ifindex <= 0)
204 ifindex = arg_ifindex;
205
79266746
LP
206 r = in_addr_to_string(family, address, &pretty);
207 if (r < 0)
208 return log_oom();
209
78c6a153
LP
210 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
211 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
79266746
LP
212
213 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
214
215 r = sd_bus_message_new_method_call(
216 bus,
217 &req,
218 "org.freedesktop.resolve1",
219 "/org/freedesktop/resolve1",
220 "org.freedesktop.resolve1.Manager",
221 "ResolveAddress");
222 if (r < 0)
223 return bus_log_create_error(r);
224
51323288 225 r = sd_bus_message_append(req, "ii", ifindex, family);
79266746
LP
226 if (r < 0)
227 return bus_log_create_error(r);
228
229 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
230 if (r < 0)
231 return bus_log_create_error(r);
232
51323288 233 r = sd_bus_message_append(req, "t", arg_flags);
79266746
LP
234 if (r < 0)
235 return bus_log_create_error(r);
236
74998408
TG
237 ts = now(CLOCK_MONOTONIC);
238
79266746
LP
239 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
240 if (r < 0) {
241 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
242 return r;
243 }
244
74998408
TG
245 ts = now(CLOCK_MONOTONIC) - ts;
246
78c6a153 247 r = sd_bus_message_enter_container(reply, 'a', "(is)");
79266746
LP
248 if (r < 0)
249 return bus_log_create_error(r);
250
78c6a153
LP
251 while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) {
252 const char *n;
253
254 assert_cc(sizeof(int) == sizeof(int32_t));
79266746 255
78c6a153
LP
256 r = sd_bus_message_read(reply, "is", &ifindex, &n);
257 if (r < 0)
258 return r;
259
260 r = sd_bus_message_exit_container(reply);
261 if (r < 0)
262 return r;
263
264 ifname[0] = 0;
265 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
266 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
267
268 printf("%*s%*s%*s%s %s\n",
79266746 269 (int) strlen(pretty), c == 0 ? pretty : "",
78c6a153
LP
270 isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%",
271 (int) strlen(ifname), c == 0 ? ifname : "",
79266746
LP
272 c == 0 ? ":" : " ",
273 n);
274
275 c++;
bdef7319 276 }
79266746
LP
277 if (r < 0)
278 return bus_log_parse_error(r);
bdef7319 279
02dd6e18
LP
280 r = sd_bus_message_exit_container(reply);
281 if (r < 0)
282 return bus_log_parse_error(r);
283
51323288
LP
284 r = sd_bus_message_read(reply, "t", &flags);
285 if (r < 0)
286 return bus_log_parse_error(r);
287
79266746
LP
288 if (c == 0) {
289 log_error("%s: no names found", pretty);
290 return -ESRCH;
291 }
292
78c6a153 293 print_source(flags, ts);
51323288 294
79266746
LP
295 return 0;
296}
297
298static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
299 const char *percent, *a;
300 int ifi = 0;
301 int r;
302
303 percent = strchr(s, '%');
304 if (percent) {
6ad623a3 305 if (parse_ifindex(percent+1, &ifi) < 0) {
79266746
LP
306 ifi = if_nametoindex(percent+1);
307 if (ifi <= 0)
308 return -EINVAL;
309 }
310
311 a = strndupa(s, percent - s);
312 } else
313 a = s;
314
315 r = in_addr_from_string_auto(a, family, address);
316 if (r < 0)
317 return r;
318
319 *ifindex = ifi;
02dd6e18 320 return 0;
bdef7319
ZJS
321}
322
2d4c5cbc
LP
323static int resolve_record(sd_bus *bus, const char *name) {
324
4afd3348
LP
325 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
326 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
78c6a153 327 char ifname[IF_NAMESIZE] = "";
2d4c5cbc 328 unsigned n = 0;
51323288 329 uint64_t flags;
78c6a153 330 int r;
74998408 331 usec_t ts;
2d4c5cbc
LP
332
333 assert(name);
334
78c6a153
LP
335 if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
336 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
337
338 log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type), isempty(ifname) ? "*" : ifname);
2d4c5cbc
LP
339
340 r = sd_bus_message_new_method_call(
341 bus,
342 &req,
343 "org.freedesktop.resolve1",
344 "/org/freedesktop/resolve1",
345 "org.freedesktop.resolve1.Manager",
346 "ResolveRecord");
347 if (r < 0)
348 return bus_log_create_error(r);
349
51323288 350 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
2d4c5cbc
LP
351 if (r < 0)
352 return bus_log_create_error(r);
353
74998408
TG
354 ts = now(CLOCK_MONOTONIC);
355
2d4c5cbc
LP
356 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
357 if (r < 0) {
358 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
359 return r;
360 }
361
74998408
TG
362 ts = now(CLOCK_MONOTONIC) - ts;
363
78c6a153 364 r = sd_bus_message_enter_container(reply, 'a', "(iqqay)");
51323288
LP
365 if (r < 0)
366 return bus_log_parse_error(r);
367
78c6a153 368 while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
2d4c5cbc
LP
369 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
370 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
371 _cleanup_free_ char *s = NULL;
372 uint16_t c, t;
78c6a153 373 int ifindex;
2d4c5cbc
LP
374 const void *d;
375 size_t l;
376
78c6a153
LP
377 assert_cc(sizeof(int) == sizeof(int32_t));
378
379 r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t);
2d4c5cbc
LP
380 if (r < 0)
381 return bus_log_parse_error(r);
382
383 r = sd_bus_message_read_array(reply, 'y', &d, &l);
384 if (r < 0)
385 return bus_log_parse_error(r);
386
387 r = sd_bus_message_exit_container(reply);
388 if (r < 0)
389 return bus_log_parse_error(r);
390
391 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
392 if (r < 0)
393 return log_oom();
394
f6a5fec6
LP
395 p->refuse_compression = true;
396
2d4c5cbc
LP
397 r = dns_packet_append_blob(p, d, l, NULL);
398 if (r < 0)
399 return log_oom();
400
d2579eec 401 r = dns_packet_read_rr(p, &rr, NULL, NULL);
2d4c5cbc
LP
402 if (r < 0) {
403 log_error("Failed to parse RR.");
404 return r;
405 }
406
407 r = dns_resource_record_to_string(rr, &s);
408 if (r < 0) {
409 log_error("Failed to format RR.");
410 return r;
411 }
412
78c6a153
LP
413 ifname[0] = 0;
414 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
415 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
416
417 printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
2d4c5cbc
LP
418 n++;
419 }
420 if (r < 0)
421 return bus_log_parse_error(r);
422
423 r = sd_bus_message_exit_container(reply);
424 if (r < 0)
425 return bus_log_parse_error(r);
426
51323288
LP
427 r = sd_bus_message_read(reply, "t", &flags);
428 if (r < 0)
429 return bus_log_parse_error(r);
430
2d4c5cbc
LP
431 if (n == 0) {
432 log_error("%s: no records found", name);
433 return -ESRCH;
434 }
435
78c6a153 436 print_source(flags, ts);
51323288 437
2d4c5cbc
LP
438 return 0;
439}
440
45ec7efb
LP
441static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
442 const char *canonical_name, *canonical_type, *canonical_domain;
4afd3348
LP
443 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
444 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
45ec7efb
LP
445 char ifname[IF_NAMESIZE] = "";
446 size_t indent, sz;
447 uint64_t flags;
448 const char *p;
449 unsigned c;
450 usec_t ts;
451 int r;
452
453 assert(bus);
454 assert(domain);
455
456 if (isempty(name))
457 name = NULL;
458 if (isempty(type))
459 type = NULL;
460
461 if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
462 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
463
464 if (name)
465 log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
466 else if (type)
467 log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
468 else
469 log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
470
471 r = sd_bus_message_new_method_call(
472 bus,
473 &req,
474 "org.freedesktop.resolve1",
475 "/org/freedesktop/resolve1",
476 "org.freedesktop.resolve1.Manager",
477 "ResolveService");
478 if (r < 0)
479 return bus_log_create_error(r);
480
481 r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags);
482 if (r < 0)
483 return bus_log_create_error(r);
484
485 ts = now(CLOCK_MONOTONIC);
486
487 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
488 if (r < 0)
489 return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
490
491 ts = now(CLOCK_MONOTONIC) - ts;
492
493 r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)");
494 if (r < 0)
495 return bus_log_parse_error(r);
496
497 indent =
498 (name ? strlen(name) + 1 : 0) +
499 (type ? strlen(type) + 1 : 0) +
500 strlen(domain) + 2;
501
502 c = 0;
503 while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) {
504 uint16_t priority, weight, port;
505 const char *hostname, *canonical;
506
507 r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname);
508 if (r < 0)
509 return bus_log_parse_error(r);
510
511 if (name)
512 printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " ");
513 if (type)
514 printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " ");
515
516 printf("%*s%s %s:%u [priority=%u, weight=%u]\n",
517 (int) strlen(domain), c == 0 ? domain : "",
518 c == 0 ? ":" : " ",
519 hostname, port,
520 priority, weight);
521
522 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
523 if (r < 0)
524 return bus_log_parse_error(r);
525
526 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
527 _cleanup_free_ char *pretty = NULL;
528 int ifindex, family;
529 const void *a;
530
531 assert_cc(sizeof(int) == sizeof(int32_t));
532
533 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
534 if (r < 0)
535 return bus_log_parse_error(r);
536
537 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
538 if (r < 0)
539 return bus_log_parse_error(r);
540
541 r = sd_bus_message_exit_container(reply);
542 if (r < 0)
543 return bus_log_parse_error(r);
544
545 if (!IN_SET(family, AF_INET, AF_INET6)) {
546 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
547 continue;
548 }
549
550 if (sz != FAMILY_ADDRESS_SIZE(family)) {
551 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
552 return -EINVAL;
553 }
554
555 ifname[0] = 0;
556 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
557 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
558
559 r = in_addr_to_string(family, a, &pretty);
560 if (r < 0)
561 return log_error_errno(r, "Failed to print address for %s: %m", name);
562
563 printf("%*s%s%s%s\n", (int) indent, "", pretty, isempty(ifname) ? "" : "%s", ifname);
564 }
565 if (r < 0)
566 return bus_log_parse_error(r);
567
568 r = sd_bus_message_exit_container(reply);
569 if (r < 0)
570 return bus_log_parse_error(r);
571
572 r = sd_bus_message_read(reply, "s", &canonical);
573 if (r < 0)
574 return bus_log_parse_error(r);
575
576 if (!streq(hostname, canonical))
577 printf("%*s(%s)\n", (int) indent, "", canonical);
578
579 r = sd_bus_message_exit_container(reply);
580 if (r < 0)
581 return bus_log_parse_error(r);
582
583 c++;
584 }
585 if (r < 0)
586 return bus_log_parse_error(r);
587
588 r = sd_bus_message_exit_container(reply);
589 if (r < 0)
590 return bus_log_parse_error(r);
591
592 r = sd_bus_message_enter_container(reply, 'a', "ay");
593 if (r < 0)
594 return bus_log_parse_error(r);
595
596 c = 0;
597 while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) {
598 _cleanup_free_ char *escaped = NULL;
599
600 escaped = cescape_length(p, sz);
601 if (!escaped)
602 return log_oom();
603
604 printf("%*s%s\n", (int) indent, "", escaped);
605 c++;
606 }
607 if (r < 0)
608 return bus_log_parse_error(r);
609
610 r = sd_bus_message_exit_container(reply);
611 if (r < 0)
612 return bus_log_parse_error(r);
613
614 r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags);
615 if (r < 0)
616 return bus_log_parse_error(r);
617
618 if (isempty(canonical_name))
619 canonical_name = NULL;
620 if (isempty(canonical_type))
621 canonical_type = NULL;
622
623 if (!streq_ptr(name, canonical_name) ||
624 !streq_ptr(type, canonical_type) ||
625 !streq_ptr(domain, canonical_domain)) {
626
627 printf("%*s(", (int) indent, "");
628
629 if (canonical_name)
630 printf("%s/", canonical_name);
631 if (canonical_type)
632 printf("%s/", canonical_type);
633
634 printf("%s)\n", canonical_domain);
635 }
636
637 print_source(flags, ts);
638
639 return 0;
640}
641
b93312f5
ZJS
642static void help_dns_types(void) {
643 int i;
644 const char *t;
645
646 if (arg_legend)
09b1fe14 647 puts("Known DNS RR types:");
b93312f5
ZJS
648 for (i = 0; i < _DNS_TYPE_MAX; i++) {
649 t = dns_type_to_string(i);
650 if (t)
651 puts(t);
652 }
653}
654
655static void help_dns_classes(void) {
656 int i;
657 const char *t;
658
659 if (arg_legend)
09b1fe14 660 puts("Known DNS RR classes:");
b93312f5
ZJS
661 for (i = 0; i < _DNS_CLASS_MAX; i++) {
662 t = dns_class_to_string(i);
663 if (t)
664 puts(t);
665 }
666}
667
bdef7319 668static void help(void) {
45ec7efb
LP
669 printf("%s [OPTIONS...] NAME...\n"
670 "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
671 "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n"
672 " -h --help Show this help\n"
673 " --version Show package version\n"
674 " -4 Resolve IPv4 addresses\n"
675 " -6 Resolve IPv6 addresses\n"
676 " -i INTERFACE Look on interface\n"
677 " -p --protocol=PROTOCOL Look via protocol\n"
678 " -t --type=TYPE Query RR with DNS type\n"
679 " -c --class=CLASS Query RR with DNS class\n"
680 " --service Resolve service (SRV)\n"
681 " --service-address=BOOL Do [not] resolve address for services\n"
682 " --service-txt=BOOL Do [not] resolve TXT records for services\n"
683 " --cname=BOOL Do [not] follow CNAME redirects\n"
801ad6a6 684 " --search=BOOL Do [not] use search domains\n"
45ec7efb
LP
685 " --legend=BOOL Do [not] print column headers\n"
686 , program_invocation_short_name, program_invocation_short_name);
bdef7319
ZJS
687}
688
689static int parse_argv(int argc, char *argv[]) {
690 enum {
691 ARG_VERSION = 0x100,
dad29dff 692 ARG_LEGEND,
45ec7efb
LP
693 ARG_SERVICE,
694 ARG_CNAME,
695 ARG_SERVICE_ADDRESS,
696 ARG_SERVICE_TXT,
801ad6a6 697 ARG_SEARCH,
bdef7319
ZJS
698 };
699
700 static const struct option options[] = {
45ec7efb
LP
701 { "help", no_argument, NULL, 'h' },
702 { "version", no_argument, NULL, ARG_VERSION },
703 { "type", required_argument, NULL, 't' },
704 { "class", required_argument, NULL, 'c' },
705 { "legend", required_argument, NULL, ARG_LEGEND },
706 { "protocol", required_argument, NULL, 'p' },
707 { "cname", required_argument, NULL, ARG_CNAME },
708 { "service", no_argument, NULL, ARG_SERVICE },
709 { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
710 { "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
801ad6a6 711 { "search", required_argument, NULL, ARG_SEARCH },
bdef7319
ZJS
712 {}
713 };
714
2d4c5cbc 715 int c, r;
bdef7319
ZJS
716
717 assert(argc >= 0);
718 assert(argv);
719
51323288 720 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
bdef7319
ZJS
721 switch(c) {
722
723 case 'h':
724 help();
725 return 0; /* done */;
726
727 case ARG_VERSION:
3f6fd1ba 728 return version();
bdef7319
ZJS
729
730 case '4':
731 arg_family = AF_INET;
732 break;
733
734 case '6':
735 arg_family = AF_INET6;
736 break;
737
3fa4999b
LP
738 case 'i': {
739 int ifi;
740
6ad623a3 741 if (parse_ifindex(optarg, &ifi) >= 0)
3fa4999b
LP
742 arg_ifindex = ifi;
743 else {
744 ifi = if_nametoindex(optarg);
745 if (ifi <= 0)
746 return log_error_errno(errno, "Unknown interface %s: %m", optarg);
747
748 arg_ifindex = ifi;
749 }
750
2d4c5cbc 751 break;
3fa4999b 752 }
2d4c5cbc
LP
753
754 case 't':
b93312f5
ZJS
755 if (streq(optarg, "help")) {
756 help_dns_types();
757 return 0;
758 }
759
4b548ef3
LP
760 r = dns_type_from_string(optarg);
761 if (r < 0) {
2d4c5cbc 762 log_error("Failed to parse RR record type %s", optarg);
4b548ef3 763 return r;
2d4c5cbc 764 }
4b548ef3
LP
765 arg_type = (uint16_t) r;
766 assert((int) arg_type == r);
b93312f5 767
2d4c5cbc
LP
768 break;
769
770 case 'c':
b93312f5
ZJS
771 if (streq(optarg, "help")) {
772 help_dns_classes();
773 return 0;
774 }
775
4b548ef3 776 r = dns_class_from_string(optarg);
2d4c5cbc
LP
777 if (r < 0) {
778 log_error("Failed to parse RR record class %s", optarg);
779 return r;
bdef7319 780 }
4b548ef3
LP
781 arg_class = (uint16_t) r;
782 assert((int) arg_class == r);
b93312f5
ZJS
783
784 break;
785
dad29dff 786 case ARG_LEGEND:
45ec7efb
LP
787 r = parse_boolean(optarg);
788 if (r < 0)
789 return log_error_errno(r, "Failed to parse --legend= argument");
790
791 arg_legend = r;
bdef7319
ZJS
792 break;
793
51323288
LP
794 case 'p':
795 if (streq(optarg, "dns"))
796 arg_flags |= SD_RESOLVED_DNS;
797 else if (streq(optarg, "llmnr"))
798 arg_flags |= SD_RESOLVED_LLMNR;
799 else if (streq(optarg, "llmnr-ipv4"))
800 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
801 else if (streq(optarg, "llmnr-ipv6"))
802 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
803 else {
804 log_error("Unknown protocol specifier: %s", optarg);
805 return -EINVAL;
806 }
807
808 break;
809
45ec7efb
LP
810 case ARG_SERVICE:
811 arg_resolve_service = true;
812 break;
813
814 case ARG_CNAME:
815 r = parse_boolean(optarg);
816 if (r < 0)
817 return log_error_errno(r, "Failed to parse --cname= argument.");
818 if (r == 0)
819 arg_flags |= SD_RESOLVED_NO_CNAME;
820 else
821 arg_flags &= ~SD_RESOLVED_NO_CNAME;
822 break;
823
824 case ARG_SERVICE_ADDRESS:
825 r = parse_boolean(optarg);
826 if (r < 0)
827 return log_error_errno(r, "Failed to parse --service-address= argument.");
828 if (r == 0)
829 arg_flags |= SD_RESOLVED_NO_ADDRESS;
830 else
831 arg_flags &= ~SD_RESOLVED_NO_ADDRESS;
832 break;
833
834 case ARG_SERVICE_TXT:
835 r = parse_boolean(optarg);
836 if (r < 0)
837 return log_error_errno(r, "Failed to parse --service-txt= argument.");
838 if (r == 0)
839 arg_flags |= SD_RESOLVED_NO_TXT;
840 else
841 arg_flags &= ~SD_RESOLVED_NO_TXT;
842 break;
843
801ad6a6
LP
844 case ARG_SEARCH:
845 r = parse_boolean(optarg);
846 if (r < 0)
847 return log_error_errno(r, "Failed to parse --search argument.");
848 if (r == 0)
849 arg_flags |= SD_RESOLVED_NO_SEARCH;
850 else
851 arg_flags &= ~SD_RESOLVED_NO_SEARCH;
852 break;
853
bdef7319
ZJS
854 case '?':
855 return -EINVAL;
856
857 default:
858 assert_not_reached("Unhandled option");
859 }
860
2d4c5cbc 861 if (arg_type == 0 && arg_class != 0) {
45ec7efb
LP
862 log_error("--class= may only be used in conjunction with --type=.");
863 return -EINVAL;
864 }
865
866 if (arg_type != 0 && arg_resolve_service) {
867 log_error("--service and --type= may not be combined.");
2d4c5cbc
LP
868 return -EINVAL;
869 }
870
871 if (arg_type != 0 && arg_class == 0)
872 arg_class = DNS_CLASS_IN;
873
bdef7319
ZJS
874 return 1 /* work to do */;
875}
876
bdef7319 877int main(int argc, char **argv) {
4afd3348 878 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
bdef7319
ZJS
879 int r;
880
881 log_parse_environment();
882 log_open();
883
884 r = parse_argv(argc, argv);
885 if (r <= 0)
79266746
LP
886 goto finish;
887
888 if (optind >= argc) {
889 log_error("No arguments passed");
890 r = -EINVAL;
891 goto finish;
892 }
bdef7319
ZJS
893
894 r = sd_bus_open_system(&bus);
895 if (r < 0) {
da927ba9 896 log_error_errno(r, "sd_bus_open_system: %m");
79266746 897 goto finish;
bdef7319
ZJS
898 }
899
45ec7efb
LP
900 if (arg_resolve_service) {
901
902 if (argc < optind + 1) {
903 log_error("Domain specification required.");
904 r = -EINVAL;
905 goto finish;
906
907 } else if (argc == optind + 1)
908 r = resolve_service(bus, NULL, NULL, argv[optind]);
909 else if (argc == optind + 2)
910 r = resolve_service(bus, NULL, argv[optind], argv[optind+1]);
911 else if (argc == optind + 3)
912 r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
913 else {
914 log_error("Too many arguments");
915 r = -EINVAL;
916 goto finish;
917 }
918
919 goto finish;
920 }
921
bdef7319 922 while (argv[optind]) {
79266746
LP
923 int family, ifindex, k;
924 union in_addr_union a;
925
2d4c5cbc
LP
926 if (arg_type != 0)
927 k = resolve_record(bus, argv[optind]);
928 else {
929 k = parse_address(argv[optind], &family, &a, &ifindex);
930 if (k >= 0)
931 k = resolve_address(bus, family, &a, ifindex);
932 else
933 k = resolve_host(bus, argv[optind]);
934 }
bdef7319 935
bdef7319
ZJS
936 if (r == 0)
937 r = k;
79266746
LP
938
939 optind++;
bdef7319
ZJS
940 }
941
79266746 942finish:
bdef7319
ZJS
943 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
944}