1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Zbigniew Jędrzejewski-Szmek
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.
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.
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/>.
27 #include "bus-error.h"
28 #include "in-addr-util.h"
32 #include "resolved-dns-packet.h"
33 #include "resolved-def.h"
35 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
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;
44 static void print_source(int ifindex, uint64_t flags) {
49 if (ifindex <= 0 && flags == 0)
52 fputs("\n-- Information acquired via", stdout);
55 printf(" protocol%s%s%s",
56 flags & SD_RESOLVED_DNS ? " DNS" :"",
57 flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
58 flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "");
61 char ifname[IF_NAMESIZE] = "";
62 printf(" interface %s", strna(if_indextoname(ifindex, ifname)));
69 static int resolve_host(sd_bus *bus, const char *name) {
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;
80 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
82 r = sd_bus_message_new_method_call(
85 "org.freedesktop.resolve1",
86 "/org/freedesktop/resolve1",
87 "org.freedesktop.resolve1.Manager",
90 return bus_log_create_error(r);
92 r = sd_bus_message_set_auto_start(req, false);
94 return bus_log_create_error(r);
96 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
98 return bus_log_create_error(r);
100 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
102 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
106 r = sd_bus_message_read(reply, "i", &ifindex);
108 return bus_log_parse_error(r);
110 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
112 return bus_log_parse_error(r);
114 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
118 _cleanup_free_ char *pretty = NULL;
119 char ifname[IF_NAMESIZE] = "";
121 r = sd_bus_message_read(reply, "i", &family);
123 return bus_log_parse_error(r);
125 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
127 return bus_log_parse_error(r);
129 r = sd_bus_message_exit_container(reply);
131 return bus_log_parse_error(r);
133 if (!IN_SET(family, AF_INET, AF_INET6)) {
134 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
138 if (sz != FAMILY_ADDRESS_SIZE(family)) {
139 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
140 name, sz, af_to_name(family) ?: "unknown");
147 t = if_indextoname(ifindex, ifname);
149 log_error("Failed to resolve interface name for index %i", ifindex);
154 r = in_addr_to_string(family, a, &pretty);
156 log_error_errno(r, "%s: failed to print address: %m", name);
160 printf("%*s%s %s%s%s\n",
161 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
163 isempty(ifname) ? "" : "%", ifname);
168 return bus_log_parse_error(r);
170 r = sd_bus_message_exit_container(reply);
172 return bus_log_parse_error(r);
174 r = sd_bus_message_read(reply, "st", &canonical, &flags);
176 return bus_log_parse_error(r);
178 if (!streq(name, canonical)) {
179 printf("%*s%s (%s)\n",
180 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
185 log_error("%s: no addresses found", name);
189 print_source(ifindex, flags);
194 static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
195 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
196 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
197 _cleanup_free_ char *pretty = NULL;
198 char ifname[IF_NAMESIZE] = "";
205 assert(IN_SET(family, AF_INET, AF_INET6));
208 r = in_addr_to_string(family, address, &pretty);
215 t = if_indextoname(ifindex, ifname);
217 log_error("Failed to resolve interface name for index %i", ifindex);
222 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
224 r = sd_bus_message_new_method_call(
227 "org.freedesktop.resolve1",
228 "/org/freedesktop/resolve1",
229 "org.freedesktop.resolve1.Manager",
232 return bus_log_create_error(r);
234 r = sd_bus_message_set_auto_start(req, false);
236 return bus_log_create_error(r);
238 r = sd_bus_message_append(req, "ii", ifindex, family);
240 return bus_log_create_error(r);
242 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
244 return bus_log_create_error(r);
246 r = sd_bus_message_append(req, "t", arg_flags);
248 return bus_log_create_error(r);
250 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
252 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
256 r = sd_bus_message_read(reply, "i", &ifindex);
258 return bus_log_parse_error(r);
260 r = sd_bus_message_enter_container(reply, 'a', "s");
262 return bus_log_create_error(r);
264 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
266 printf("%*s%s%s%s %s\n",
267 (int) strlen(pretty), c == 0 ? pretty : "",
268 isempty(ifname) ? "" : "%", ifname,
275 return bus_log_parse_error(r);
277 r = sd_bus_message_exit_container(reply);
279 return bus_log_parse_error(r);
281 r = sd_bus_message_read(reply, "t", &flags);
283 return bus_log_parse_error(r);
286 log_error("%s: no names found", pretty);
290 print_source(ifindex, flags);
295 static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
296 const char *percent, *a;
300 percent = strchr(s, '%');
302 r = safe_atoi(percent+1, &ifi);
303 if (r < 0 || ifi <= 0) {
304 ifi = if_nametoindex(percent+1);
309 a = strndupa(s, percent - s);
313 r = in_addr_from_string_auto(a, family, address);
321 static int resolve_record(sd_bus *bus, const char *name) {
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;
331 log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type));
333 r = sd_bus_message_new_method_call(
336 "org.freedesktop.resolve1",
337 "/org/freedesktop/resolve1",
338 "org.freedesktop.resolve1.Manager",
341 return bus_log_create_error(r);
343 r = sd_bus_message_set_auto_start(req, false);
345 return bus_log_create_error(r);
347 assert((uint16_t) arg_type == arg_type);
348 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
350 return bus_log_create_error(r);
352 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
354 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
358 r = sd_bus_message_read(reply, "i", &ifindex);
360 return bus_log_parse_error(r);
362 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
364 return bus_log_parse_error(r);
366 while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
367 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
368 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
369 _cleanup_free_ char *s = NULL;
374 r = sd_bus_message_read(reply, "qq", &c, &t);
376 return bus_log_parse_error(r);
378 r = sd_bus_message_read_array(reply, 'y', &d, &l);
380 return bus_log_parse_error(r);
382 r = sd_bus_message_exit_container(reply);
384 return bus_log_parse_error(r);
386 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
390 r = dns_packet_append_blob(p, d, l, NULL);
394 r = dns_packet_read_rr(p, &rr, NULL);
396 log_error("Failed to parse RR.");
400 r = dns_resource_record_to_string(rr, &s);
402 log_error("Failed to format RR.");
410 return bus_log_parse_error(r);
412 r = sd_bus_message_exit_container(reply);
414 return bus_log_parse_error(r);
416 r = sd_bus_message_read(reply, "t", &flags);
418 return bus_log_parse_error(r);
421 log_error("%s: no records found", name);
425 print_source(ifindex, flags);
430 static void help_dns_types(void) {
435 puts("Known dns types:");
436 for (i = 0; i < _DNS_TYPE_MAX; i++) {
437 t = dns_type_to_string(i);
443 static void help_dns_classes(void) {
448 puts("Known dns classes:");
449 for (i = 0; i < _DNS_CLASS_MAX; i++) {
450 t = dns_class_to_string(i);
456 static void help(void) {
457 printf("%s [OPTIONS...]\n\n"
458 "Resolve IPv4 or IPv6 addresses.\n\n"
459 " -h --help Show this help\n"
460 " --version Show package version\n"
461 " -4 Resolve IPv4 addresses\n"
462 " -6 Resolve IPv6 addresses\n"
463 " -i INTERFACE Look on interface\n"
464 " -p --protocol=PROTOCOL Look via protocol\n"
465 " -t --type=TYPE Query RR with DNS type\n"
466 " -c --class=CLASS Query RR with DNS class\n"
467 " --legend[=BOOL] Do [not] print column headers\n"
468 , program_invocation_short_name);
471 static int parse_argv(int argc, char *argv[]) {
477 static const struct option options[] = {
478 { "help", no_argument, NULL, 'h' },
479 { "version", no_argument, NULL, ARG_VERSION },
480 { "type", required_argument, NULL, 't' },
481 { "class", required_argument, NULL, 'c' },
482 { "legend", optional_argument, NULL, ARG_LEGEND },
483 { "protocol", required_argument, NULL, 'p' },
492 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
497 return 0; /* done */;
500 puts(PACKAGE_STRING);
501 puts(SYSTEMD_FEATURES);
505 arg_family = AF_INET;
509 arg_family = AF_INET6;
513 arg_ifindex = if_nametoindex(optarg);
514 if (arg_ifindex <= 0)
515 return log_error_errno(errno, "Unknown interfaces %s: %m", optarg);
519 if (streq(optarg, "help")) {
524 arg_type = dns_type_from_string(optarg);
526 log_error("Failed to parse RR record type %s", optarg);
529 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
534 if (streq(optarg, "help")) {
539 r = dns_class_from_string(optarg, &arg_class);
541 log_error("Failed to parse RR record class %s", optarg);
549 r = parse_boolean(optarg);
551 log_error("Failed to parse --legend= argument");
561 if (streq(optarg, "dns"))
562 arg_flags |= SD_RESOLVED_DNS;
563 else if (streq(optarg, "llmnr"))
564 arg_flags |= SD_RESOLVED_LLMNR;
565 else if (streq(optarg, "llmnr-ipv4"))
566 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
567 else if (streq(optarg, "llmnr-ipv6"))
568 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
570 log_error("Unknown protocol specifier: %s", optarg);
580 assert_not_reached("Unhandled option");
583 if (arg_type == 0 && arg_class != 0) {
584 log_error("--class= may only be used in conjunction with --type=");
588 if (arg_type != 0 && arg_class == 0)
589 arg_class = DNS_CLASS_IN;
591 return 1 /* work to do */;
594 int main(int argc, char **argv) {
595 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
598 log_parse_environment();
601 r = parse_argv(argc, argv);
605 if (optind >= argc) {
606 log_error("No arguments passed");
611 r = sd_bus_open_system(&bus);
613 log_error_errno(r, "sd_bus_open_system: %m");
617 while (argv[optind]) {
618 int family, ifindex, k;
619 union in_addr_union a;
622 k = resolve_record(bus, argv[optind]);
624 k = parse_address(argv[optind], &family, &a, &ifindex);
626 k = resolve_address(bus, family, &a, ifindex);
628 k = resolve_host(bus, argv[optind]);
638 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;