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