]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve-host/resolve-host.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[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 "parse-util.h"
32 #include "resolved-def.h"
33 #include "resolved-dns-packet.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 if (c == 0) {
177 log_error("%s: no addresses found", name);
178 return -ESRCH;
179 }
180
181 print_source(flags, ts);
182
183 return 0;
184 }
185
186 static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
187 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
188 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
189 _cleanup_free_ char *pretty = NULL;
190 char ifname[IF_NAMESIZE] = "";
191 uint64_t flags;
192 unsigned c = 0;
193 usec_t ts;
194 int r;
195
196 assert(bus);
197 assert(IN_SET(family, AF_INET, AF_INET6));
198 assert(address);
199
200 if (ifindex <= 0)
201 ifindex = arg_ifindex;
202
203 r = in_addr_to_string(family, address, &pretty);
204 if (r < 0)
205 return log_oom();
206
207 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
208 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
209
210 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
211
212 r = sd_bus_message_new_method_call(
213 bus,
214 &req,
215 "org.freedesktop.resolve1",
216 "/org/freedesktop/resolve1",
217 "org.freedesktop.resolve1.Manager",
218 "ResolveAddress");
219 if (r < 0)
220 return bus_log_create_error(r);
221
222 r = sd_bus_message_append(req, "ii", ifindex, family);
223 if (r < 0)
224 return bus_log_create_error(r);
225
226 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
227 if (r < 0)
228 return bus_log_create_error(r);
229
230 r = sd_bus_message_append(req, "t", arg_flags);
231 if (r < 0)
232 return bus_log_create_error(r);
233
234 ts = now(CLOCK_MONOTONIC);
235
236 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
237 if (r < 0) {
238 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
239 return r;
240 }
241
242 ts = now(CLOCK_MONOTONIC) - ts;
243
244 r = sd_bus_message_enter_container(reply, 'a', "(is)");
245 if (r < 0)
246 return bus_log_create_error(r);
247
248 while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) {
249 const char *n;
250
251 assert_cc(sizeof(int) == sizeof(int32_t));
252
253 r = sd_bus_message_read(reply, "is", &ifindex, &n);
254 if (r < 0)
255 return r;
256
257 r = sd_bus_message_exit_container(reply);
258 if (r < 0)
259 return r;
260
261 ifname[0] = 0;
262 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
263 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
264
265 printf("%*s%*s%*s%s %s\n",
266 (int) strlen(pretty), c == 0 ? pretty : "",
267 isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%",
268 (int) strlen(ifname), c == 0 ? ifname : "",
269 c == 0 ? ":" : " ",
270 n);
271
272 c++;
273 }
274 if (r < 0)
275 return bus_log_parse_error(r);
276
277 r = sd_bus_message_exit_container(reply);
278 if (r < 0)
279 return bus_log_parse_error(r);
280
281 r = sd_bus_message_read(reply, "t", &flags);
282 if (r < 0)
283 return bus_log_parse_error(r);
284
285 if (c == 0) {
286 log_error("%s: no names found", pretty);
287 return -ESRCH;
288 }
289
290 print_source(flags, ts);
291
292 return 0;
293 }
294
295 static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
296 const char *percent, *a;
297 int ifi = 0;
298 int r;
299
300 percent = strchr(s, '%');
301 if (percent) {
302 r = safe_atoi(percent+1, &ifi);
303 if (r < 0 || 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 (safe_atoi(optarg, &ifi) >= 0 && 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 }