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