]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
CommitLineData
bdef7319
ZJS
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
2d4c5cbc 34#include "resolved-dns-packet.h"
51323288 35#include "resolved-def.h"
2d4c5cbc 36
bdef7319
ZJS
37#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
38
39static int arg_family = AF_UNSPEC;
40static int arg_ifindex = 0;
de292aa1 41static int arg_type = 0;
2d4c5cbc 42static uint16_t arg_class = 0;
b93312f5 43static bool arg_legend = true;
51323288
LP
44static uint64_t arg_flags = 0;
45
46static 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}
bdef7319 70
79266746 71static int resolve_host(sd_bus *bus, const char *name) {
bdef7319
ZJS
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;
79266746 75 const char *canonical = NULL;
bdef7319 76 unsigned c = 0;
51323288
LP
77 int r, ifindex;
78 uint64_t flags;
bdef7319
ZJS
79
80 assert(name);
81
79266746 82 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
bdef7319
ZJS
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");
02dd6e18
LP
91 if (r < 0)
92 return bus_log_create_error(r);
bdef7319
ZJS
93
94 r = sd_bus_message_set_auto_start(req, false);
02dd6e18
LP
95 if (r < 0)
96 return bus_log_create_error(r);
bdef7319 97
51323288 98 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
02dd6e18
LP
99 if (r < 0)
100 return bus_log_create_error(r);
bdef7319
ZJS
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
51323288 108 r = sd_bus_message_read(reply, "i", &ifindex);
02dd6e18
LP
109 if (r < 0)
110 return bus_log_parse_error(r);
bdef7319 111
51323288
LP
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) {
bdef7319 117 const void *a;
51323288 118 int family;
bdef7319
ZJS
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);
02dd6e18
LP
124 if (r < 0)
125 return bus_log_parse_error(r);
bdef7319
ZJS
126
127 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
02dd6e18
LP
128 if (r < 0)
129 return bus_log_parse_error(r);
bdef7319 130
bdef7319 131 r = sd_bus_message_exit_container(reply);
02dd6e18
LP
132 if (r < 0)
133 return bus_log_parse_error(r);
bdef7319 134
79266746 135 if (!IN_SET(family, AF_INET, AF_INET6)) {
be636413 136 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
bdef7319
ZJS
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
bdef7319
ZJS
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
bdef7319
ZJS
156 r = in_addr_to_string(family, a, &pretty);
157 if (r < 0) {
da927ba9 158 log_error_errno(r, "%s: failed to print address: %m", name);
bdef7319
ZJS
159 continue;
160 }
161
79266746
LP
162 printf("%*s%s %s%s%s\n",
163 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
164 pretty,
165 isempty(ifname) ? "" : "%", ifname);
bdef7319
ZJS
166
167 c++;
168 }
79266746
LP
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
51323288 176 r = sd_bus_message_read(reply, "st", &canonical, &flags);
79266746
LP
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 }
bdef7319
ZJS
185
186 if (c == 0) {
187 log_error("%s: no addresses found", name);
79266746
LP
188 return -ESRCH;
189 }
190
51323288
LP
191 print_source(ifindex, flags);
192
79266746
LP
193 return 0;
194}
195
196static 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] = "";
51323288 201 uint64_t flags;
79266746
LP
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
51323288 240 r = sd_bus_message_append(req, "ii", ifindex, family);
79266746
LP
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
51323288 248 r = sd_bus_message_append(req, "t", arg_flags);
79266746
LP
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
51323288
LP
258 r = sd_bus_message_read(reply, "i", &ifindex);
259 if (r < 0)
260 return bus_log_parse_error(r);
261
79266746
LP
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++;
bdef7319 275 }
79266746
LP
276 if (r < 0)
277 return bus_log_parse_error(r);
bdef7319 278
02dd6e18
LP
279 r = sd_bus_message_exit_container(reply);
280 if (r < 0)
281 return bus_log_parse_error(r);
282
51323288
LP
283 r = sd_bus_message_read(reply, "t", &flags);
284 if (r < 0)
285 return bus_log_parse_error(r);
286
79266746
LP
287 if (c == 0) {
288 log_error("%s: no names found", pretty);
289 return -ESRCH;
290 }
291
51323288
LP
292 print_source(ifindex, flags);
293
79266746
LP
294 return 0;
295}
296
297static 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;
02dd6e18 320 return 0;
bdef7319
ZJS
321}
322
2d4c5cbc
LP
323static 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;
51323288
LP
328 uint64_t flags;
329 int r, ifindex;
2d4c5cbc
LP
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
de292aa1 349 assert((uint16_t) arg_type == arg_type);
51323288 350 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
2d4c5cbc
LP
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
51323288
LP
360 r = sd_bus_message_read(reply, "i", &ifindex);
361 if (r < 0)
362 return bus_log_parse_error(r);
363
2d4c5cbc
LP
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
51323288
LP
418 r = sd_bus_message_read(reply, "t", &flags);
419 if (r < 0)
420 return bus_log_parse_error(r);
421
2d4c5cbc
LP
422 if (n == 0) {
423 log_error("%s: no records found", name);
424 return -ESRCH;
425 }
426
51323288
LP
427 print_source(ifindex, flags);
428
2d4c5cbc
LP
429 return 0;
430}
431
b93312f5
ZJS
432static 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
445static 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
bdef7319
ZJS
458static void help(void) {
459 printf("%s [OPTIONS...]\n\n"
460 "Resolve IPv4 or IPv6 addresses.\n\n"
51323288
LP
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"
dad29dff 469 " --legend[=BOOL] Do [not] print column headers\n"
79266746 470 , program_invocation_short_name);
bdef7319
ZJS
471}
472
473static int parse_argv(int argc, char *argv[]) {
474 enum {
475 ARG_VERSION = 0x100,
dad29dff 476 ARG_LEGEND,
bdef7319
ZJS
477 };
478
479 static const struct option options[] = {
51323288
LP
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' },
dad29dff 484 { "legend", optional_argument, NULL, ARG_LEGEND },
51323288 485 { "protocol", required_argument, NULL, 'p' },
bdef7319
ZJS
486 {}
487 };
488
2d4c5cbc 489 int c, r;
bdef7319
ZJS
490
491 assert(argc >= 0);
492 assert(argv);
493
51323288 494 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
bdef7319
ZJS
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) {
56f64d95 517 log_error_errno(errno, "Unknown interfaces %s: %m", optarg);
2d4c5cbc
LP
518 return -errno;
519 }
520 break;
521
522 case 't':
b93312f5
ZJS
523 if (streq(optarg, "help")) {
524 help_dns_types();
525 return 0;
526 }
527
de292aa1
ZJS
528 arg_type = dns_type_from_string(optarg);
529 if (arg_type < 0) {
2d4c5cbc 530 log_error("Failed to parse RR record type %s", optarg);
590baf91 531 return arg_type;
2d4c5cbc 532 }
de292aa1 533 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
b93312f5 534
2d4c5cbc
LP
535 break;
536
537 case 'c':
b93312f5
ZJS
538 if (streq(optarg, "help")) {
539 help_dns_classes();
540 return 0;
541 }
542
2d4c5cbc
LP
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;
bdef7319 547 }
b93312f5
ZJS
548
549 break;
550
dad29dff
LP
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;
bdef7319
ZJS
562 break;
563
51323288
LP
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
bdef7319
ZJS
580 case '?':
581 return -EINVAL;
582
583 default:
584 assert_not_reached("Unhandled option");
585 }
586
2d4c5cbc
LP
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
bdef7319
ZJS
595 return 1 /* work to do */;
596}
597
bdef7319 598int main(int argc, char **argv) {
24996861 599 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
bdef7319
ZJS
600 int r;
601
602 log_parse_environment();
603 log_open();
604
605 r = parse_argv(argc, argv);
606 if (r <= 0)
79266746
LP
607 goto finish;
608
609 if (optind >= argc) {
610 log_error("No arguments passed");
611 r = -EINVAL;
612 goto finish;
613 }
bdef7319
ZJS
614
615 r = sd_bus_open_system(&bus);
616 if (r < 0) {
da927ba9 617 log_error_errno(r, "sd_bus_open_system: %m");
79266746 618 goto finish;
bdef7319
ZJS
619 }
620
621 while (argv[optind]) {
79266746
LP
622 int family, ifindex, k;
623 union in_addr_union a;
624
2d4c5cbc
LP
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 }
bdef7319 634
bdef7319
ZJS
635 if (r == 0)
636 r = k;
79266746
LP
637
638 optind++;
bdef7319
ZJS
639 }
640
79266746 641finish:
bdef7319
ZJS
642 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
643}