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/>.
22 #include <arpa/inet.h>
28 #include "bus-error.h"
29 #include "bus-errors.h"
30 #include "in-addr-util.h"
34 #include "resolved-dns-packet.h"
35 #include "resolved-def.h"
37 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
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;
46 static void print_source(int ifindex, uint64_t flags) {
51 if (ifindex <= 0 && flags == 0)
54 fputs("\n-- Information acquired via", stdout);
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" : "");
63 char ifname[IF_NAMESIZE] = "";
64 printf(" interface %s", strna(if_indextoname(ifindex, ifname)));
71 static int resolve_host(sd_bus *bus, const char *name) {
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;
82 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
84 r = sd_bus_message_new_method_call(
87 "org.freedesktop.resolve1",
88 "/org/freedesktop/resolve1",
89 "org.freedesktop.resolve1.Manager",
92 return bus_log_create_error(r);
94 r = sd_bus_message_set_auto_start(req, false);
96 return bus_log_create_error(r);
98 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
100 return bus_log_create_error(r);
102 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
104 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
108 r = sd_bus_message_read(reply, "i", &ifindex);
110 return bus_log_parse_error(r);
112 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
114 return bus_log_parse_error(r);
116 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
120 _cleanup_free_ char *pretty = NULL;
121 char ifname[IF_NAMESIZE] = "";
123 r = sd_bus_message_read(reply, "i", &family);
125 return bus_log_parse_error(r);
127 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
129 return bus_log_parse_error(r);
131 r = sd_bus_message_exit_container(reply);
133 return bus_log_parse_error(r);
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");
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");
149 t = if_indextoname(ifindex, ifname);
151 log_error("Failed to resolve interface name for index %i", ifindex);
156 r = in_addr_to_string(family, a, &pretty);
158 log_error("%s: failed to print address: %s", name, strerror(-r));
162 printf("%*s%s %s%s%s\n",
163 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
165 isempty(ifname) ? "" : "%", ifname);
170 return bus_log_parse_error(r);
172 r = sd_bus_message_exit_container(reply);
174 return bus_log_parse_error(r);
176 r = sd_bus_message_read(reply, "st", &canonical, &flags);
178 return bus_log_parse_error(r);
180 if (!streq(name, canonical)) {
181 printf("%*s%s (%s)\n",
182 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
187 log_error("%s: no addresses found", name);
191 print_source(ifindex, flags);
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] = "";
207 assert(IN_SET(family, AF_INET, AF_INET6));
210 r = in_addr_to_string(family, address, &pretty);
217 t = if_indextoname(ifindex, ifname);
219 log_error("Failed to resolve interface name for index %i", ifindex);
224 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
226 r = sd_bus_message_new_method_call(
229 "org.freedesktop.resolve1",
230 "/org/freedesktop/resolve1",
231 "org.freedesktop.resolve1.Manager",
234 return bus_log_create_error(r);
236 r = sd_bus_message_set_auto_start(req, false);
238 return bus_log_create_error(r);
240 r = sd_bus_message_append(req, "ii", ifindex, family);
242 return bus_log_create_error(r);
244 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
246 return bus_log_create_error(r);
248 r = sd_bus_message_append(req, "t", arg_flags);
250 return bus_log_create_error(r);
252 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
254 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
258 r = sd_bus_message_read(reply, "i", &ifindex);
260 return bus_log_parse_error(r);
262 r = sd_bus_message_enter_container(reply, 'a', "s");
264 return bus_log_create_error(r);
266 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
268 printf("%*s%s%s%s %s\n",
269 (int) strlen(pretty), c == 0 ? pretty : "",
270 isempty(ifname) ? "" : "%", ifname,
277 return bus_log_parse_error(r);
279 r = sd_bus_message_exit_container(reply);
281 return bus_log_parse_error(r);
283 r = sd_bus_message_read(reply, "t", &flags);
285 return bus_log_parse_error(r);
288 log_error("%s: no names found", pretty);
292 print_source(ifindex, flags);
297 static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
298 const char *percent, *a;
302 percent = strchr(s, '%');
304 r = safe_atoi(percent+1, &ifi);
305 if (r < 0 || ifi <= 0) {
306 ifi = if_nametoindex(percent+1);
311 a = strndupa(s, percent - s);
315 r = in_addr_from_string_auto(a, family, address);
323 static int resolve_record(sd_bus *bus, const char *name) {
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;
333 log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type));
335 r = sd_bus_message_new_method_call(
338 "org.freedesktop.resolve1",
339 "/org/freedesktop/resolve1",
340 "org.freedesktop.resolve1.Manager",
343 return bus_log_create_error(r);
345 r = sd_bus_message_set_auto_start(req, false);
347 return bus_log_create_error(r);
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);
352 return bus_log_create_error(r);
354 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
356 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
360 r = sd_bus_message_read(reply, "i", &ifindex);
362 return bus_log_parse_error(r);
364 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
366 return bus_log_parse_error(r);
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;
376 r = sd_bus_message_read(reply, "qq", &c, &t);
378 return bus_log_parse_error(r);
380 r = sd_bus_message_read_array(reply, 'y', &d, &l);
382 return bus_log_parse_error(r);
384 r = sd_bus_message_exit_container(reply);
386 return bus_log_parse_error(r);
388 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
392 r = dns_packet_append_blob(p, d, l, NULL);
396 r = dns_packet_read_rr(p, &rr, NULL);
398 log_error("Failed to parse RR.");
402 r = dns_resource_record_to_string(rr, &s);
404 log_error("Failed to format RR.");
412 return bus_log_parse_error(r);
414 r = sd_bus_message_exit_container(reply);
416 return bus_log_parse_error(r);
418 r = sd_bus_message_read(reply, "t", &flags);
420 return bus_log_parse_error(r);
423 log_error("%s: no records found", name);
427 print_source(ifindex, flags);
432 static void help_dns_types(void) {
437 puts("Known dns types:");
438 for (i = 0; i < _DNS_TYPE_MAX; i++) {
439 t = dns_type_to_string(i);
445 static void help_dns_classes(void) {
450 puts("Known dns classes:");
451 for (i = 0; i < _DNS_CLASS_MAX; i++) {
452 t = dns_class_to_string(i);
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);
473 static int parse_argv(int argc, char *argv[]) {
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' },
494 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
499 return 0; /* done */;
502 puts(PACKAGE_STRING);
503 puts(SYSTEMD_FEATURES);
507 arg_family = AF_INET;
511 arg_family = AF_INET6;
515 arg_ifindex = if_nametoindex(optarg);
516 if (arg_ifindex <= 0) {
517 log_error("Unknown interfaces %s: %m", optarg);
523 if (streq(optarg, "help")) {
528 arg_type = dns_type_from_string(optarg);
530 log_error("Failed to parse RR record type %s", optarg);
533 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
538 if (streq(optarg, "help")) {
543 r = dns_class_from_string(optarg, &arg_class);
545 log_error("Failed to parse RR record class %s", optarg);
553 r = parse_boolean(optarg);
555 log_error("Failed to parse --legend= argument");
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;
574 log_error("Unknown protocol specifier: %s", optarg);
584 assert_not_reached("Unhandled option");
587 if (arg_type == 0 && arg_class != 0) {
588 log_error("--class= may only be used in conjunction with --type=");
592 if (arg_type != 0 && arg_class == 0)
593 arg_class = DNS_CLASS_IN;
595 return 1 /* work to do */;
598 int main(int argc, char **argv) {
599 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
602 log_parse_environment();
605 r = parse_argv(argc, argv);
609 if (optind >= argc) {
610 log_error("No arguments passed");
615 r = sd_bus_open_system(&bus);
617 log_error("sd_bus_open_system: %s", strerror(-r));
621 while (argv[optind]) {
622 int family, ifindex, k;
623 union in_addr_union a;
626 k = resolve_record(bus, argv[optind]);
628 k = parse_address(argv[optind], &family, &a, &ifindex);
630 k = resolve_address(bus, family, &a, ifindex);
632 k = resolve_host(bus, argv[optind]);
642 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;