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
, usec_t rtt
) {
45 char rtt_str
[FORMAT_TIMESTAMP_MAX
];
50 if (ifindex
<= 0 && flags
== 0)
53 fputs("\n-- Information acquired via", stdout
);
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" : "");
62 char ifname
[IF_NAMESIZE
] = "";
63 printf(" interface %s", strna(if_indextoname(ifindex
, ifname
)));
66 assert_se(format_timespan(rtt_str
, sizeof(rtt_str
), rtt
, 100));
68 printf(" in %s", rtt_str
);
74 static int resolve_host(sd_bus
*bus
, const char *name
) {
76 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
77 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
78 const char *canonical
= NULL
;
86 log_debug("Resolving %s (family %s, ifindex %i).", name
, af_to_name(arg_family
) ?: "*", arg_ifindex
);
88 r
= sd_bus_message_new_method_call(
91 "org.freedesktop.resolve1",
92 "/org/freedesktop/resolve1",
93 "org.freedesktop.resolve1.Manager",
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 ts
= now(CLOCK_MONOTONIC
);
104 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
106 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
110 ts
= now(CLOCK_MONOTONIC
) - ts
;
112 r
= sd_bus_message_read(reply
, "i", &ifindex
);
114 return bus_log_parse_error(r
);
116 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
118 return bus_log_parse_error(r
);
120 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
124 _cleanup_free_
char *pretty
= NULL
;
125 char ifname
[IF_NAMESIZE
] = "";
127 r
= sd_bus_message_read(reply
, "i", &family
);
129 return bus_log_parse_error(r
);
131 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
133 return bus_log_parse_error(r
);
135 r
= sd_bus_message_exit_container(reply
);
137 return bus_log_parse_error(r
);
139 if (!IN_SET(family
, AF_INET
, AF_INET6
)) {
140 log_debug("%s: skipping entry with family %d (%s)", name
, family
, af_to_name(family
) ?: "unknown");
144 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
145 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
146 name
, sz
, af_to_name(family
) ?: "unknown");
153 t
= if_indextoname(ifindex
, ifname
);
155 log_error("Failed to resolve interface name for index %i", ifindex
);
160 r
= in_addr_to_string(family
, a
, &pretty
);
162 log_error_errno(r
, "%s: failed to print address: %m", name
);
166 printf("%*s%s %s%s%s\n",
167 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
169 isempty(ifname
) ? "" : "%", ifname
);
174 return bus_log_parse_error(r
);
176 r
= sd_bus_message_exit_container(reply
);
178 return bus_log_parse_error(r
);
180 r
= sd_bus_message_read(reply
, "st", &canonical
, &flags
);
182 return bus_log_parse_error(r
);
184 if (!streq(name
, canonical
)) {
185 printf("%*s%s (%s)\n",
186 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
191 log_error("%s: no addresses found", name
);
195 print_source(ifindex
, flags
, ts
);
200 static int resolve_address(sd_bus
*bus
, int family
, const union in_addr_union
*address
, int ifindex
) {
201 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
202 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
203 _cleanup_free_
char *pretty
= NULL
;
204 char ifname
[IF_NAMESIZE
] = "";
212 assert(IN_SET(family
, AF_INET
, AF_INET6
));
215 r
= in_addr_to_string(family
, address
, &pretty
);
222 t
= if_indextoname(ifindex
, ifname
);
224 log_error("Failed to resolve interface name for index %i", ifindex
);
229 log_debug("Resolving %s%s%s.", pretty
, isempty(ifname
) ? "" : "%", ifname
);
231 r
= sd_bus_message_new_method_call(
234 "org.freedesktop.resolve1",
235 "/org/freedesktop/resolve1",
236 "org.freedesktop.resolve1.Manager",
239 return bus_log_create_error(r
);
241 r
= sd_bus_message_set_auto_start(req
, false);
243 return bus_log_create_error(r
);
245 r
= sd_bus_message_append(req
, "ii", ifindex
, family
);
247 return bus_log_create_error(r
);
249 r
= sd_bus_message_append_array(req
, 'y', address
, FAMILY_ADDRESS_SIZE(family
));
251 return bus_log_create_error(r
);
253 r
= sd_bus_message_append(req
, "t", arg_flags
);
255 return bus_log_create_error(r
);
257 ts
= now(CLOCK_MONOTONIC
);
259 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
261 log_error("%s: resolve call failed: %s", pretty
, bus_error_message(&error
, r
));
265 ts
= now(CLOCK_MONOTONIC
) - ts
;
267 r
= sd_bus_message_read(reply
, "i", &ifindex
);
269 return bus_log_parse_error(r
);
271 r
= sd_bus_message_enter_container(reply
, 'a', "s");
273 return bus_log_create_error(r
);
275 while ((r
= sd_bus_message_read(reply
, "s", &n
)) > 0) {
277 printf("%*s%s%s%s %s\n",
278 (int) strlen(pretty
), c
== 0 ? pretty
: "",
279 isempty(ifname
) ? "" : "%", ifname
,
286 return bus_log_parse_error(r
);
288 r
= sd_bus_message_exit_container(reply
);
290 return bus_log_parse_error(r
);
292 r
= sd_bus_message_read(reply
, "t", &flags
);
294 return bus_log_parse_error(r
);
297 log_error("%s: no names found", pretty
);
301 print_source(ifindex
, flags
, ts
);
306 static int parse_address(const char *s
, int *family
, union in_addr_union
*address
, int *ifindex
) {
307 const char *percent
, *a
;
311 percent
= strchr(s
, '%');
313 r
= safe_atoi(percent
+1, &ifi
);
314 if (r
< 0 || ifi
<= 0) {
315 ifi
= if_nametoindex(percent
+1);
320 a
= strndupa(s
, percent
- s
);
324 r
= in_addr_from_string_auto(a
, family
, address
);
332 static int resolve_record(sd_bus
*bus
, const char *name
) {
334 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
335 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
343 log_debug("Resolving %s %s %s.", name
, dns_class_to_string(arg_class
), dns_type_to_string(arg_type
));
345 r
= sd_bus_message_new_method_call(
348 "org.freedesktop.resolve1",
349 "/org/freedesktop/resolve1",
350 "org.freedesktop.resolve1.Manager",
353 return bus_log_create_error(r
);
355 assert((uint16_t) arg_type
== arg_type
);
356 r
= sd_bus_message_append(req
, "isqqt", arg_ifindex
, name
, arg_class
, arg_type
, arg_flags
);
358 return bus_log_create_error(r
);
360 ts
= now(CLOCK_MONOTONIC
);
362 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
364 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
368 ts
= now(CLOCK_MONOTONIC
) - ts
;
370 r
= sd_bus_message_read(reply
, "i", &ifindex
);
372 return bus_log_parse_error(r
);
374 r
= sd_bus_message_enter_container(reply
, 'a', "(qqay)");
376 return bus_log_parse_error(r
);
378 while ((r
= sd_bus_message_enter_container(reply
, 'r', "qqay")) > 0) {
379 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
380 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
381 _cleanup_free_
char *s
= NULL
;
386 r
= sd_bus_message_read(reply
, "qq", &c
, &t
);
388 return bus_log_parse_error(r
);
390 r
= sd_bus_message_read_array(reply
, 'y', &d
, &l
);
392 return bus_log_parse_error(r
);
394 r
= sd_bus_message_exit_container(reply
);
396 return bus_log_parse_error(r
);
398 r
= dns_packet_new(&p
, DNS_PROTOCOL_DNS
, 0);
402 r
= dns_packet_append_blob(p
, d
, l
, NULL
);
406 r
= dns_packet_read_rr(p
, &rr
, NULL
);
408 log_error("Failed to parse RR.");
412 r
= dns_resource_record_to_string(rr
, &s
);
414 log_error("Failed to format RR.");
422 return bus_log_parse_error(r
);
424 r
= sd_bus_message_exit_container(reply
);
426 return bus_log_parse_error(r
);
428 r
= sd_bus_message_read(reply
, "t", &flags
);
430 return bus_log_parse_error(r
);
433 log_error("%s: no records found", name
);
437 print_source(ifindex
, flags
, ts
);
442 static void help_dns_types(void) {
447 puts("Known dns types:");
448 for (i
= 0; i
< _DNS_TYPE_MAX
; i
++) {
449 t
= dns_type_to_string(i
);
455 static void help_dns_classes(void) {
460 puts("Known dns classes:");
461 for (i
= 0; i
< _DNS_CLASS_MAX
; i
++) {
462 t
= dns_class_to_string(i
);
468 static void help(void) {
469 printf("%s [OPTIONS...]\n\n"
470 "Resolve IPv4 or IPv6 addresses.\n\n"
471 " -h --help Show this help\n"
472 " --version Show package version\n"
473 " -4 Resolve IPv4 addresses\n"
474 " -6 Resolve IPv6 addresses\n"
475 " -i INTERFACE Look on interface\n"
476 " -p --protocol=PROTOCOL Look via protocol\n"
477 " -t --type=TYPE Query RR with DNS type\n"
478 " -c --class=CLASS Query RR with DNS class\n"
479 " --legend[=BOOL] Do [not] print column headers\n"
480 , program_invocation_short_name
);
483 static int parse_argv(int argc
, char *argv
[]) {
489 static const struct option options
[] = {
490 { "help", no_argument
, NULL
, 'h' },
491 { "version", no_argument
, NULL
, ARG_VERSION
},
492 { "type", required_argument
, NULL
, 't' },
493 { "class", required_argument
, NULL
, 'c' },
494 { "legend", optional_argument
, NULL
, ARG_LEGEND
},
495 { "protocol", required_argument
, NULL
, 'p' },
504 while ((c
= getopt_long(argc
, argv
, "h46i:t:c:p:", options
, NULL
)) >= 0)
509 return 0; /* done */;
512 puts(PACKAGE_STRING
);
513 puts(SYSTEMD_FEATURES
);
517 arg_family
= AF_INET
;
521 arg_family
= AF_INET6
;
525 arg_ifindex
= if_nametoindex(optarg
);
526 if (arg_ifindex
<= 0)
527 return log_error_errno(errno
, "Unknown interfaces %s: %m", optarg
);
531 if (streq(optarg
, "help")) {
536 arg_type
= dns_type_from_string(optarg
);
538 log_error("Failed to parse RR record type %s", optarg
);
541 assert(arg_type
> 0 && (uint16_t) arg_type
== arg_type
);
546 if (streq(optarg
, "help")) {
551 r
= dns_class_from_string(optarg
, &arg_class
);
553 log_error("Failed to parse RR record class %s", optarg
);
561 r
= parse_boolean(optarg
);
563 log_error("Failed to parse --legend= argument");
573 if (streq(optarg
, "dns"))
574 arg_flags
|= SD_RESOLVED_DNS
;
575 else if (streq(optarg
, "llmnr"))
576 arg_flags
|= SD_RESOLVED_LLMNR
;
577 else if (streq(optarg
, "llmnr-ipv4"))
578 arg_flags
|= SD_RESOLVED_LLMNR_IPV4
;
579 else if (streq(optarg
, "llmnr-ipv6"))
580 arg_flags
|= SD_RESOLVED_LLMNR_IPV6
;
582 log_error("Unknown protocol specifier: %s", optarg
);
592 assert_not_reached("Unhandled option");
595 if (arg_type
== 0 && arg_class
!= 0) {
596 log_error("--class= may only be used in conjunction with --type=");
600 if (arg_type
!= 0 && arg_class
== 0)
601 arg_class
= DNS_CLASS_IN
;
603 return 1 /* work to do */;
606 int main(int argc
, char **argv
) {
607 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
610 log_parse_environment();
613 r
= parse_argv(argc
, argv
);
617 if (optind
>= argc
) {
618 log_error("No arguments passed");
623 r
= sd_bus_open_system(&bus
);
625 log_error_errno(r
, "sd_bus_open_system: %m");
629 while (argv
[optind
]) {
630 int family
, ifindex
, k
;
631 union in_addr_union a
;
634 k
= resolve_record(bus
, argv
[optind
]);
636 k
= parse_address(argv
[optind
], &family
, &a
, &ifindex
);
638 k
= resolve_address(bus
, family
, &a
, ifindex
);
640 k
= resolve_host(bus
, argv
[optind
]);
650 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;