]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve-host/resolve-host.c
resolved: rework synthesizing logic
[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 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 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 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 puts(PACKAGE_STRING);
510 puts(SYSTEMD_FEATURES);
511 return 0 /* done */;
512
513 case '4':
514 arg_family = AF_INET;
515 break;
516
517 case '6':
518 arg_family = AF_INET6;
519 break;
520
521 case 'i':
522 arg_ifindex = if_nametoindex(optarg);
523 if (arg_ifindex <= 0)
524 return log_error_errno(errno, "Unknown interfaces %s: %m", optarg);
525 break;
526
527 case 't':
528 if (streq(optarg, "help")) {
529 help_dns_types();
530 return 0;
531 }
532
533 arg_type = dns_type_from_string(optarg);
534 if (arg_type < 0) {
535 log_error("Failed to parse RR record type %s", optarg);
536 return arg_type;
537 }
538 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
539
540 break;
541
542 case 'c':
543 if (streq(optarg, "help")) {
544 help_dns_classes();
545 return 0;
546 }
547
548 r = dns_class_from_string(optarg, &arg_class);
549 if (r < 0) {
550 log_error("Failed to parse RR record class %s", optarg);
551 return r;
552 }
553
554 break;
555
556 case ARG_LEGEND:
557 if (optarg) {
558 r = parse_boolean(optarg);
559 if (r < 0) {
560 log_error("Failed to parse --legend= argument");
561 return r;
562 }
563
564 arg_legend = !!r;
565 } else
566 arg_legend = false;
567 break;
568
569 case 'p':
570 if (streq(optarg, "dns"))
571 arg_flags |= SD_RESOLVED_DNS;
572 else if (streq(optarg, "llmnr"))
573 arg_flags |= SD_RESOLVED_LLMNR;
574 else if (streq(optarg, "llmnr-ipv4"))
575 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
576 else if (streq(optarg, "llmnr-ipv6"))
577 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
578 else {
579 log_error("Unknown protocol specifier: %s", optarg);
580 return -EINVAL;
581 }
582
583 break;
584
585 case '?':
586 return -EINVAL;
587
588 default:
589 assert_not_reached("Unhandled option");
590 }
591
592 if (arg_type == 0 && arg_class != 0) {
593 log_error("--class= may only be used in conjunction with --type=");
594 return -EINVAL;
595 }
596
597 if (arg_type != 0 && arg_class == 0)
598 arg_class = DNS_CLASS_IN;
599
600 return 1 /* work to do */;
601 }
602
603 int main(int argc, char **argv) {
604 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
605 int r;
606
607 log_parse_environment();
608 log_open();
609
610 r = parse_argv(argc, argv);
611 if (r <= 0)
612 goto finish;
613
614 if (optind >= argc) {
615 log_error("No arguments passed");
616 r = -EINVAL;
617 goto finish;
618 }
619
620 r = sd_bus_open_system(&bus);
621 if (r < 0) {
622 log_error_errno(r, "sd_bus_open_system: %m");
623 goto finish;
624 }
625
626 while (argv[optind]) {
627 int family, ifindex, k;
628 union in_addr_union a;
629
630 if (arg_type != 0)
631 k = resolve_record(bus, argv[optind]);
632 else {
633 k = parse_address(argv[optind], &family, &a, &ifindex);
634 if (k >= 0)
635 k = resolve_address(bus, family, &a, ifindex);
636 else
637 k = resolve_host(bus, argv[optind]);
638 }
639
640 if (r == 0)
641 r = k;
642
643 optind++;
644 }
645
646 finish:
647 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
648 }