]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve-host/resolve-host.c
resolved: make DnsQuestion logic handle NULL arrays as empty arrays
[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
ZJS
22#include <net/if.h>
23#include <getopt.h>
24
25#include "sd-bus.h"
26#include "bus-util.h"
27#include "bus-error.h"
bdef7319
ZJS
28#include "in-addr-util.h"
29#include "af-list.h"
30#include "build.h"
31
2d4c5cbc 32#include "resolved-dns-packet.h"
51323288 33#include "resolved-def.h"
2d4c5cbc 34
bdef7319
ZJS
35#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
36
37static int arg_family = AF_UNSPEC;
38static int arg_ifindex = 0;
de292aa1 39static int arg_type = 0;
2d4c5cbc 40static uint16_t arg_class = 0;
b93312f5 41static bool arg_legend = true;
51323288
LP
42static uint64_t arg_flags = 0;
43
74998408
TG
44static void print_source(int ifindex, uint64_t flags, usec_t rtt) {
45 char rtt_str[FORMAT_TIMESTAMP_MAX];
51323288
LP
46
47 if (!arg_legend)
48 return;
49
50 if (ifindex <= 0 && flags == 0)
51 return;
52
53 fputs("\n-- Information acquired via", stdout);
54
55 if (flags != 0)
56 printf(" protocol%s%s%s",
57 flags & SD_RESOLVED_DNS ? " DNS" :"",
58 flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
59 flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "");
60
61 if (ifindex > 0) {
62 char ifname[IF_NAMESIZE] = "";
63 printf(" interface %s", strna(if_indextoname(ifindex, ifname)));
64 }
65
74998408
TG
66 assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
67
68 printf(" in %s", rtt_str);
69
51323288
LP
70 fputc('.', stdout);
71 fputc('\n', stdout);
72}
bdef7319 73
79266746 74static int resolve_host(sd_bus *bus, const char *name) {
bdef7319
ZJS
75
76 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
77 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
79266746 78 const char *canonical = NULL;
bdef7319 79 unsigned c = 0;
51323288
LP
80 int r, ifindex;
81 uint64_t flags;
74998408 82 usec_t ts;
bdef7319
ZJS
83
84 assert(name);
85
79266746 86 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
bdef7319
ZJS
87
88 r = sd_bus_message_new_method_call(
89 bus,
90 &req,
91 "org.freedesktop.resolve1",
92 "/org/freedesktop/resolve1",
93 "org.freedesktop.resolve1.Manager",
94 "ResolveHostname");
02dd6e18
LP
95 if (r < 0)
96 return bus_log_create_error(r);
bdef7319 97
51323288 98 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
02dd6e18
LP
99 if (r < 0)
100 return bus_log_create_error(r);
bdef7319 101
74998408
TG
102 ts = now(CLOCK_MONOTONIC);
103
bdef7319
ZJS
104 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
105 if (r < 0) {
106 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
107 return r;
108 }
109
74998408
TG
110 ts = now(CLOCK_MONOTONIC) - ts;
111
51323288 112 r = sd_bus_message_read(reply, "i", &ifindex);
02dd6e18
LP
113 if (r < 0)
114 return bus_log_parse_error(r);
bdef7319 115
51323288
LP
116 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
117 if (r < 0)
118 return bus_log_parse_error(r);
119
120 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
bdef7319 121 const void *a;
51323288 122 int family;
bdef7319
ZJS
123 size_t sz;
124 _cleanup_free_ char *pretty = NULL;
125 char ifname[IF_NAMESIZE] = "";
126
127 r = sd_bus_message_read(reply, "i", &family);
02dd6e18
LP
128 if (r < 0)
129 return bus_log_parse_error(r);
bdef7319
ZJS
130
131 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
02dd6e18
LP
132 if (r < 0)
133 return bus_log_parse_error(r);
bdef7319 134
bdef7319 135 r = sd_bus_message_exit_container(reply);
02dd6e18
LP
136 if (r < 0)
137 return bus_log_parse_error(r);
bdef7319 138
79266746 139 if (!IN_SET(family, AF_INET, AF_INET6)) {
be636413 140 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
bdef7319
ZJS
141 continue;
142 }
143
144 if (sz != FAMILY_ADDRESS_SIZE(family)) {
145 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
146 name, sz, af_to_name(family) ?: "unknown");
147 continue;
148 }
149
bdef7319
ZJS
150 if (ifindex > 0) {
151 char *t;
152
153 t = if_indextoname(ifindex, ifname);
154 if (!t) {
155 log_error("Failed to resolve interface name for index %i", ifindex);
156 continue;
157 }
158 }
159
bdef7319
ZJS
160 r = in_addr_to_string(family, a, &pretty);
161 if (r < 0) {
da927ba9 162 log_error_errno(r, "%s: failed to print address: %m", name);
bdef7319
ZJS
163 continue;
164 }
165
79266746
LP
166 printf("%*s%s %s%s%s\n",
167 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
168 pretty,
169 isempty(ifname) ? "" : "%", ifname);
bdef7319
ZJS
170
171 c++;
172 }
79266746
LP
173 if (r < 0)
174 return bus_log_parse_error(r);
175
176 r = sd_bus_message_exit_container(reply);
177 if (r < 0)
178 return bus_log_parse_error(r);
179
51323288 180 r = sd_bus_message_read(reply, "st", &canonical, &flags);
79266746
LP
181 if (r < 0)
182 return bus_log_parse_error(r);
183
184 if (!streq(name, canonical)) {
185 printf("%*s%s (%s)\n",
186 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
187 canonical);
188 }
bdef7319
ZJS
189
190 if (c == 0) {
191 log_error("%s: no addresses found", name);
79266746
LP
192 return -ESRCH;
193 }
194
74998408 195 print_source(ifindex, flags, ts);
51323288 196
79266746
LP
197 return 0;
198}
199
200static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
201 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
202 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
203 _cleanup_free_ char *pretty = NULL;
204 char ifname[IF_NAMESIZE] = "";
51323288 205 uint64_t flags;
79266746
LP
206 unsigned c = 0;
207 const char *n;
74998408 208 usec_t ts;
79266746
LP
209 int r;
210
211 assert(bus);
212 assert(IN_SET(family, AF_INET, AF_INET6));
213 assert(address);
214
215 r = in_addr_to_string(family, address, &pretty);
216 if (r < 0)
217 return log_oom();
218
219 if (ifindex > 0) {
220 char *t;
221
222 t = if_indextoname(ifindex, ifname);
223 if (!t) {
224 log_error("Failed to resolve interface name for index %i", ifindex);
225 return -errno;
226 }
227 }
228
229 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
230
231 r = sd_bus_message_new_method_call(
232 bus,
233 &req,
234 "org.freedesktop.resolve1",
235 "/org/freedesktop/resolve1",
236 "org.freedesktop.resolve1.Manager",
237 "ResolveAddress");
238 if (r < 0)
239 return bus_log_create_error(r);
240
241 r = sd_bus_message_set_auto_start(req, false);
242 if (r < 0)
243 return bus_log_create_error(r);
244
51323288 245 r = sd_bus_message_append(req, "ii", ifindex, family);
79266746
LP
246 if (r < 0)
247 return bus_log_create_error(r);
248
249 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
250 if (r < 0)
251 return bus_log_create_error(r);
252
51323288 253 r = sd_bus_message_append(req, "t", arg_flags);
79266746
LP
254 if (r < 0)
255 return bus_log_create_error(r);
256
74998408
TG
257 ts = now(CLOCK_MONOTONIC);
258
79266746
LP
259 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
260 if (r < 0) {
261 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
262 return r;
263 }
264
74998408
TG
265 ts = now(CLOCK_MONOTONIC) - ts;
266
51323288
LP
267 r = sd_bus_message_read(reply, "i", &ifindex);
268 if (r < 0)
269 return bus_log_parse_error(r);
270
79266746
LP
271 r = sd_bus_message_enter_container(reply, 'a', "s");
272 if (r < 0)
273 return bus_log_create_error(r);
274
275 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
276
277 printf("%*s%s%s%s %s\n",
278 (int) strlen(pretty), c == 0 ? pretty : "",
279 isempty(ifname) ? "" : "%", ifname,
280 c == 0 ? ":" : " ",
281 n);
282
283 c++;
bdef7319 284 }
79266746
LP
285 if (r < 0)
286 return bus_log_parse_error(r);
bdef7319 287
02dd6e18
LP
288 r = sd_bus_message_exit_container(reply);
289 if (r < 0)
290 return bus_log_parse_error(r);
291
51323288
LP
292 r = sd_bus_message_read(reply, "t", &flags);
293 if (r < 0)
294 return bus_log_parse_error(r);
295
79266746
LP
296 if (c == 0) {
297 log_error("%s: no names found", pretty);
298 return -ESRCH;
299 }
300
74998408 301 print_source(ifindex, flags, ts);
51323288 302
79266746
LP
303 return 0;
304}
305
306static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
307 const char *percent, *a;
308 int ifi = 0;
309 int r;
310
311 percent = strchr(s, '%');
312 if (percent) {
313 r = safe_atoi(percent+1, &ifi);
314 if (r < 0 || ifi <= 0) {
315 ifi = if_nametoindex(percent+1);
316 if (ifi <= 0)
317 return -EINVAL;
318 }
319
320 a = strndupa(s, percent - s);
321 } else
322 a = s;
323
324 r = in_addr_from_string_auto(a, family, address);
325 if (r < 0)
326 return r;
327
328 *ifindex = ifi;
02dd6e18 329 return 0;
bdef7319
ZJS
330}
331
2d4c5cbc
LP
332static int resolve_record(sd_bus *bus, const char *name) {
333
334 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
335 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
336 unsigned n = 0;
51323288
LP
337 uint64_t flags;
338 int r, ifindex;
74998408 339 usec_t ts;
2d4c5cbc
LP
340
341 assert(name);
342
343 log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type));
344
345 r = sd_bus_message_new_method_call(
346 bus,
347 &req,
348 "org.freedesktop.resolve1",
349 "/org/freedesktop/resolve1",
350 "org.freedesktop.resolve1.Manager",
351 "ResolveRecord");
352 if (r < 0)
353 return bus_log_create_error(r);
354
de292aa1 355 assert((uint16_t) arg_type == arg_type);
51323288 356 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
2d4c5cbc
LP
357 if (r < 0)
358 return bus_log_create_error(r);
359
74998408
TG
360 ts = now(CLOCK_MONOTONIC);
361
2d4c5cbc
LP
362 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
363 if (r < 0) {
364 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
365 return r;
366 }
367
74998408
TG
368 ts = now(CLOCK_MONOTONIC) - ts;
369
51323288
LP
370 r = sd_bus_message_read(reply, "i", &ifindex);
371 if (r < 0)
372 return bus_log_parse_error(r);
373
2d4c5cbc
LP
374 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
375 if (r < 0)
376 return bus_log_parse_error(r);
377
378 while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
379 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
380 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
381 _cleanup_free_ char *s = NULL;
382 uint16_t c, t;
383 const void *d;
384 size_t l;
385
386 r = sd_bus_message_read(reply, "qq", &c, &t);
387 if (r < 0)
388 return bus_log_parse_error(r);
389
390 r = sd_bus_message_read_array(reply, 'y', &d, &l);
391 if (r < 0)
392 return bus_log_parse_error(r);
393
394 r = sd_bus_message_exit_container(reply);
395 if (r < 0)
396 return bus_log_parse_error(r);
397
398 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
399 if (r < 0)
400 return log_oom();
401
402 r = dns_packet_append_blob(p, d, l, NULL);
403 if (r < 0)
404 return log_oom();
405
406 r = dns_packet_read_rr(p, &rr, NULL);
407 if (r < 0) {
408 log_error("Failed to parse RR.");
409 return r;
410 }
411
412 r = dns_resource_record_to_string(rr, &s);
413 if (r < 0) {
414 log_error("Failed to format RR.");
415 return r;
416 }
417
418 printf("%s\n", s);
419 n++;
420 }
421 if (r < 0)
422 return bus_log_parse_error(r);
423
424 r = sd_bus_message_exit_container(reply);
425 if (r < 0)
426 return bus_log_parse_error(r);
427
51323288
LP
428 r = sd_bus_message_read(reply, "t", &flags);
429 if (r < 0)
430 return bus_log_parse_error(r);
431
2d4c5cbc
LP
432 if (n == 0) {
433 log_error("%s: no records found", name);
434 return -ESRCH;
435 }
436
74998408 437 print_source(ifindex, flags, ts);
51323288 438
2d4c5cbc
LP
439 return 0;
440}
441
b93312f5
ZJS
442static void help_dns_types(void) {
443 int i;
444 const char *t;
445
446 if (arg_legend)
447 puts("Known dns types:");
448 for (i = 0; i < _DNS_TYPE_MAX; i++) {
449 t = dns_type_to_string(i);
450 if (t)
451 puts(t);
452 }
453}
454
455static void help_dns_classes(void) {
456 int i;
457 const char *t;
458
459 if (arg_legend)
460 puts("Known dns classes:");
461 for (i = 0; i < _DNS_CLASS_MAX; i++) {
462 t = dns_class_to_string(i);
463 if (t)
464 puts(t);
465 }
466}
467
bdef7319
ZJS
468static void help(void) {
469 printf("%s [OPTIONS...]\n\n"
470 "Resolve IPv4 or IPv6 addresses.\n\n"
51323288
LP
471 " -h --help Show this help\n"
472 " --version Show package version\n"
473 " -4 Resolve IPv4 addresses\n"
474 " -6 Resolve IPv6 addresses\n"
475 " -i INTERFACE Look on interface\n"
476 " -p --protocol=PROTOCOL Look via protocol\n"
477 " -t --type=TYPE Query RR with DNS type\n"
478 " -c --class=CLASS Query RR with DNS class\n"
dad29dff 479 " --legend[=BOOL] Do [not] print column headers\n"
79266746 480 , program_invocation_short_name);
bdef7319
ZJS
481}
482
483static int parse_argv(int argc, char *argv[]) {
484 enum {
485 ARG_VERSION = 0x100,
dad29dff 486 ARG_LEGEND,
bdef7319
ZJS
487 };
488
489 static const struct option options[] = {
51323288
LP
490 { "help", no_argument, NULL, 'h' },
491 { "version", no_argument, NULL, ARG_VERSION },
492 { "type", required_argument, NULL, 't' },
493 { "class", required_argument, NULL, 'c' },
dad29dff 494 { "legend", optional_argument, NULL, ARG_LEGEND },
51323288 495 { "protocol", required_argument, NULL, 'p' },
bdef7319
ZJS
496 {}
497 };
498
2d4c5cbc 499 int c, r;
bdef7319
ZJS
500
501 assert(argc >= 0);
502 assert(argv);
503
51323288 504 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
bdef7319
ZJS
505 switch(c) {
506
507 case 'h':
508 help();
509 return 0; /* done */;
510
511 case ARG_VERSION:
512 puts(PACKAGE_STRING);
513 puts(SYSTEMD_FEATURES);
514 return 0 /* done */;
515
516 case '4':
517 arg_family = AF_INET;
518 break;
519
520 case '6':
521 arg_family = AF_INET6;
522 break;
523
524 case 'i':
525 arg_ifindex = if_nametoindex(optarg);
4a62c710
MS
526 if (arg_ifindex <= 0)
527 return log_error_errno(errno, "Unknown interfaces %s: %m", optarg);
2d4c5cbc
LP
528 break;
529
530 case 't':
b93312f5
ZJS
531 if (streq(optarg, "help")) {
532 help_dns_types();
533 return 0;
534 }
535
de292aa1
ZJS
536 arg_type = dns_type_from_string(optarg);
537 if (arg_type < 0) {
2d4c5cbc 538 log_error("Failed to parse RR record type %s", optarg);
590baf91 539 return arg_type;
2d4c5cbc 540 }
de292aa1 541 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
b93312f5 542
2d4c5cbc
LP
543 break;
544
545 case 'c':
b93312f5
ZJS
546 if (streq(optarg, "help")) {
547 help_dns_classes();
548 return 0;
549 }
550
2d4c5cbc
LP
551 r = dns_class_from_string(optarg, &arg_class);
552 if (r < 0) {
553 log_error("Failed to parse RR record class %s", optarg);
554 return r;
bdef7319 555 }
b93312f5
ZJS
556
557 break;
558
dad29dff
LP
559 case ARG_LEGEND:
560 if (optarg) {
561 r = parse_boolean(optarg);
562 if (r < 0) {
563 log_error("Failed to parse --legend= argument");
564 return r;
565 }
566
567 arg_legend = !!r;
568 } else
569 arg_legend = false;
bdef7319
ZJS
570 break;
571
51323288
LP
572 case 'p':
573 if (streq(optarg, "dns"))
574 arg_flags |= SD_RESOLVED_DNS;
575 else if (streq(optarg, "llmnr"))
576 arg_flags |= SD_RESOLVED_LLMNR;
577 else if (streq(optarg, "llmnr-ipv4"))
578 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
579 else if (streq(optarg, "llmnr-ipv6"))
580 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
581 else {
582 log_error("Unknown protocol specifier: %s", optarg);
583 return -EINVAL;
584 }
585
586 break;
587
bdef7319
ZJS
588 case '?':
589 return -EINVAL;
590
591 default:
592 assert_not_reached("Unhandled option");
593 }
594
2d4c5cbc
LP
595 if (arg_type == 0 && arg_class != 0) {
596 log_error("--class= may only be used in conjunction with --type=");
597 return -EINVAL;
598 }
599
600 if (arg_type != 0 && arg_class == 0)
601 arg_class = DNS_CLASS_IN;
602
bdef7319
ZJS
603 return 1 /* work to do */;
604}
605
bdef7319 606int main(int argc, char **argv) {
03976f7b 607 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
bdef7319
ZJS
608 int r;
609
610 log_parse_environment();
611 log_open();
612
613 r = parse_argv(argc, argv);
614 if (r <= 0)
79266746
LP
615 goto finish;
616
617 if (optind >= argc) {
618 log_error("No arguments passed");
619 r = -EINVAL;
620 goto finish;
621 }
bdef7319
ZJS
622
623 r = sd_bus_open_system(&bus);
624 if (r < 0) {
da927ba9 625 log_error_errno(r, "sd_bus_open_system: %m");
79266746 626 goto finish;
bdef7319
ZJS
627 }
628
629 while (argv[optind]) {
79266746
LP
630 int family, ifindex, k;
631 union in_addr_union a;
632
2d4c5cbc
LP
633 if (arg_type != 0)
634 k = resolve_record(bus, argv[optind]);
635 else {
636 k = parse_address(argv[optind], &family, &a, &ifindex);
637 if (k >= 0)
638 k = resolve_address(bus, family, &a, ifindex);
639 else
640 k = resolve_host(bus, argv[optind]);
641 }
bdef7319 642
bdef7319
ZJS
643 if (r == 0)
644 r = k;
79266746
LP
645
646 optind++;
bdef7319
ZJS
647 }
648
79266746 649finish:
bdef7319
ZJS
650 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
651}