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