]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve-host/resolve-host.c
Merge pull request #958 from stefwalter/fix-journalctl-f-regression
[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(int ifindex, uint64_t flags, usec_t rtt) {
45 char rtt_str[FORMAT_TIMESTAMP_MAX];
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
66 assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
67
68 printf(" in %s", rtt_str);
69
70 fputc('.', stdout);
71 fputc('\n', stdout);
72 }
73
74 static int resolve_host(sd_bus *bus, const char *name) {
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;
78 const char *canonical = NULL;
79 unsigned c = 0;
80 int r, ifindex;
81 uint64_t flags;
82 usec_t ts;
83
84 assert(name);
85
86 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
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_read(reply, "i", &ifindex);
113 if (r < 0)
114 return bus_log_parse_error(r);
115
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) {
121 const void *a;
122 int family;
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);
128 if (r < 0)
129 return bus_log_parse_error(r);
130
131 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
132 if (r < 0)
133 return bus_log_parse_error(r);
134
135 r = sd_bus_message_exit_container(reply);
136 if (r < 0)
137 return bus_log_parse_error(r);
138
139 if (!IN_SET(family, AF_INET, AF_INET6)) {
140 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
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
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
160 r = in_addr_to_string(family, a, &pretty);
161 if (r < 0) {
162 log_error_errno(r, "%s: failed to print address: %m", name);
163 continue;
164 }
165
166 printf("%*s%s %s%s%s\n",
167 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
168 pretty,
169 isempty(ifname) ? "" : "%", ifname);
170
171 c++;
172 }
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
180 r = sd_bus_message_read(reply, "st", &canonical, &flags);
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 }
189
190 if (c == 0) {
191 log_error("%s: no addresses found", name);
192 return -ESRCH;
193 }
194
195 print_source(ifindex, flags, ts);
196
197 return 0;
198 }
199
200 static 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] = "";
205 uint64_t flags;
206 unsigned c = 0;
207 const char *n;
208 usec_t ts;
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
245 r = sd_bus_message_append(req, "ii", ifindex, family);
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
253 r = sd_bus_message_append(req, "t", arg_flags);
254 if (r < 0)
255 return bus_log_create_error(r);
256
257 ts = now(CLOCK_MONOTONIC);
258
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
265 ts = now(CLOCK_MONOTONIC) - ts;
266
267 r = sd_bus_message_read(reply, "i", &ifindex);
268 if (r < 0)
269 return bus_log_parse_error(r);
270
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++;
284 }
285 if (r < 0)
286 return bus_log_parse_error(r);
287
288 r = sd_bus_message_exit_container(reply);
289 if (r < 0)
290 return bus_log_parse_error(r);
291
292 r = sd_bus_message_read(reply, "t", &flags);
293 if (r < 0)
294 return bus_log_parse_error(r);
295
296 if (c == 0) {
297 log_error("%s: no names found", pretty);
298 return -ESRCH;
299 }
300
301 print_source(ifindex, flags, ts);
302
303 return 0;
304 }
305
306 static 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;
329 return 0;
330 }
331
332 static 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;
337 uint64_t flags;
338 int r, ifindex;
339 usec_t ts;
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
355 assert((uint16_t) arg_type == arg_type);
356 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
357 if (r < 0)
358 return bus_log_create_error(r);
359
360 ts = now(CLOCK_MONOTONIC);
361
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
368 ts = now(CLOCK_MONOTONIC) - ts;
369
370 r = sd_bus_message_read(reply, "i", &ifindex);
371 if (r < 0)
372 return bus_log_parse_error(r);
373
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
428 r = sd_bus_message_read(reply, "t", &flags);
429 if (r < 0)
430 return bus_log_parse_error(r);
431
432 if (n == 0) {
433 log_error("%s: no records found", name);
434 return -ESRCH;
435 }
436
437 print_source(ifindex, flags, ts);
438
439 return 0;
440 }
441
442 static 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
455 static 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
468 static void help(void) {
469 printf("%s [OPTIONS...]\n\n"
470 "Resolve IPv4 or IPv6 addresses.\n\n"
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"
479 " --legend[=BOOL] Do [not] print column headers\n"
480 , program_invocation_short_name);
481 }
482
483 static int parse_argv(int argc, char *argv[]) {
484 enum {
485 ARG_VERSION = 0x100,
486 ARG_LEGEND,
487 };
488
489 static const struct option options[] = {
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' },
494 { "legend", optional_argument, NULL, ARG_LEGEND },
495 { "protocol", required_argument, NULL, 'p' },
496 {}
497 };
498
499 int c, r;
500
501 assert(argc >= 0);
502 assert(argv);
503
504 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
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);
526 if (arg_ifindex <= 0)
527 return log_error_errno(errno, "Unknown interfaces %s: %m", optarg);
528 break;
529
530 case 't':
531 if (streq(optarg, "help")) {
532 help_dns_types();
533 return 0;
534 }
535
536 arg_type = dns_type_from_string(optarg);
537 if (arg_type < 0) {
538 log_error("Failed to parse RR record type %s", optarg);
539 return arg_type;
540 }
541 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
542
543 break;
544
545 case 'c':
546 if (streq(optarg, "help")) {
547 help_dns_classes();
548 return 0;
549 }
550
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;
555 }
556
557 break;
558
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;
570 break;
571
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
588 case '?':
589 return -EINVAL;
590
591 default:
592 assert_not_reached("Unhandled option");
593 }
594
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
603 return 1 /* work to do */;
604 }
605
606 int main(int argc, char **argv) {
607 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
608 int r;
609
610 log_parse_environment();
611 log_open();
612
613 r = parse_argv(argc, argv);
614 if (r <= 0)
615 goto finish;
616
617 if (optind >= argc) {
618 log_error("No arguments passed");
619 r = -EINVAL;
620 goto finish;
621 }
622
623 r = sd_bus_open_system(&bus);
624 if (r < 0) {
625 log_error_errno(r, "sd_bus_open_system: %m");
626 goto finish;
627 }
628
629 while (argv[optind]) {
630 int family, ifindex, k;
631 union in_addr_union a;
632
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 }
642
643 if (r == 0)
644 r = k;
645
646 optind++;
647 }
648
649 finish:
650 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
651 }