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