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/>.
28 #include "alloc-util.h"
29 #include "bus-error.h"
31 #include "in-addr-util.h"
32 #include "parse-util.h"
33 #include "resolved-def.h"
34 #include "resolved-dns-packet.h"
36 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
38 static int arg_family
= AF_UNSPEC
;
39 static int arg_ifindex
= 0;
40 static int arg_type
= 0;
41 static uint16_t arg_class
= 0;
42 static bool arg_legend
= true;
43 static uint64_t arg_flags
= 0;
45 static void print_source(uint64_t flags
, usec_t rtt
) {
46 char rtt_str
[FORMAT_TIMESTAMP_MAX
];
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" : "");
62 assert_se(format_timespan(rtt_str
, sizeof(rtt_str
), rtt
, 100));
64 printf(" in %s", rtt_str
);
70 static int resolve_host(sd_bus
*bus
, const char *name
) {
72 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
73 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
74 const char *canonical
= NULL
;
75 char ifname
[IF_NAMESIZE
] = "";
83 if (arg_ifindex
> 0 && !if_indextoname(arg_ifindex
, ifname
))
84 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", arg_ifindex
);
86 log_debug("Resolving %s (family %s, interface %s).", name
, af_to_name(arg_family
) ?: "*", isempty(ifname
) ? "*" : ifname
);
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_enter_container(reply
, 'a', "(iiay)");
114 return bus_log_parse_error(r
);
116 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
119 _cleanup_free_
char *pretty
= NULL
;
122 assert_cc(sizeof(int) == sizeof(int32_t));
124 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
126 return bus_log_parse_error(r
);
128 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
130 return bus_log_parse_error(r
);
132 r
= sd_bus_message_exit_container(reply
);
134 return bus_log_parse_error(r
);
136 if (!IN_SET(family
, AF_INET
, AF_INET6
)) {
137 log_debug("%s: skipping entry with family %d (%s)", name
, family
, af_to_name(family
) ?: "unknown");
141 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
142 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name
, sz
, af_to_name(family
) ?: "unknown");
147 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
148 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
150 r
= in_addr_to_string(family
, a
, &pretty
);
152 return log_error_errno(r
, "Failed to print address for %s: %m", name
);
154 printf("%*s%s %s%s%s\n",
155 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
157 isempty(ifname
) ? "" : "%", ifname
);
162 return bus_log_parse_error(r
);
164 r
= sd_bus_message_exit_container(reply
);
166 return bus_log_parse_error(r
);
168 r
= sd_bus_message_read(reply
, "st", &canonical
, &flags
);
170 return bus_log_parse_error(r
);
172 if (!streq(name
, canonical
))
173 printf("%*s%s (%s)\n",
174 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
178 log_error("%s: no addresses found", name
);
182 print_source(flags
, ts
);
187 static int resolve_address(sd_bus
*bus
, int family
, const union in_addr_union
*address
, int ifindex
) {
188 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
189 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
190 _cleanup_free_
char *pretty
= NULL
;
191 char ifname
[IF_NAMESIZE
] = "";
198 assert(IN_SET(family
, AF_INET
, AF_INET6
));
202 ifindex
= arg_ifindex
;
204 r
= in_addr_to_string(family
, address
, &pretty
);
208 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
209 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
211 log_debug("Resolving %s%s%s.", pretty
, isempty(ifname
) ? "" : "%", ifname
);
213 r
= sd_bus_message_new_method_call(
216 "org.freedesktop.resolve1",
217 "/org/freedesktop/resolve1",
218 "org.freedesktop.resolve1.Manager",
221 return bus_log_create_error(r
);
223 r
= sd_bus_message_append(req
, "ii", ifindex
, family
);
225 return bus_log_create_error(r
);
227 r
= sd_bus_message_append_array(req
, 'y', address
, FAMILY_ADDRESS_SIZE(family
));
229 return bus_log_create_error(r
);
231 r
= sd_bus_message_append(req
, "t", arg_flags
);
233 return bus_log_create_error(r
);
235 ts
= now(CLOCK_MONOTONIC
);
237 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
239 log_error("%s: resolve call failed: %s", pretty
, bus_error_message(&error
, r
));
243 ts
= now(CLOCK_MONOTONIC
) - ts
;
245 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
247 return bus_log_create_error(r
);
249 while ((r
= sd_bus_message_enter_container(reply
, 'r', "is")) > 0) {
252 assert_cc(sizeof(int) == sizeof(int32_t));
254 r
= sd_bus_message_read(reply
, "is", &ifindex
, &n
);
258 r
= sd_bus_message_exit_container(reply
);
263 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
264 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
266 printf("%*s%*s%*s%s %s\n",
267 (int) strlen(pretty
), c
== 0 ? pretty
: "",
268 isempty(ifname
) ? 0 : 1, c
> 0 || isempty(ifname
) ? "" : "%",
269 (int) strlen(ifname
), c
== 0 ? ifname
: "",
276 return bus_log_parse_error(r
);
278 r
= sd_bus_message_exit_container(reply
);
280 return bus_log_parse_error(r
);
282 r
= sd_bus_message_read(reply
, "t", &flags
);
284 return bus_log_parse_error(r
);
287 log_error("%s: no names found", pretty
);
291 print_source(flags
, ts
);
296 static int parse_address(const char *s
, int *family
, union in_addr_union
*address
, int *ifindex
) {
297 const char *percent
, *a
;
301 percent
= strchr(s
, '%');
303 if (parse_ifindex(percent
+1, &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
;
325 char ifname
[IF_NAMESIZE
] = "";
333 if (arg_ifindex
> 0 && !if_indextoname(arg_ifindex
, ifname
))
334 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", arg_ifindex
);
336 log_debug("Resolving %s %s %s (interface %s).", name
, dns_class_to_string(arg_class
), dns_type_to_string(arg_type
), isempty(ifname
) ? "*" : ifname
);
338 r
= sd_bus_message_new_method_call(
341 "org.freedesktop.resolve1",
342 "/org/freedesktop/resolve1",
343 "org.freedesktop.resolve1.Manager",
346 return bus_log_create_error(r
);
348 assert((uint16_t) arg_type
== arg_type
);
349 r
= sd_bus_message_append(req
, "isqqt", arg_ifindex
, name
, arg_class
, arg_type
, arg_flags
);
351 return bus_log_create_error(r
);
353 ts
= now(CLOCK_MONOTONIC
);
355 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
357 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
361 ts
= now(CLOCK_MONOTONIC
) - ts
;
363 r
= sd_bus_message_enter_container(reply
, 'a', "(iqqay)");
365 return bus_log_parse_error(r
);
367 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iqqay")) > 0) {
368 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
369 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
370 _cleanup_free_
char *s
= NULL
;
376 assert_cc(sizeof(int) == sizeof(int32_t));
378 r
= sd_bus_message_read(reply
, "iqq", &ifindex
, &c
, &t
);
380 return bus_log_parse_error(r
);
382 r
= sd_bus_message_read_array(reply
, 'y', &d
, &l
);
384 return bus_log_parse_error(r
);
386 r
= sd_bus_message_exit_container(reply
);
388 return bus_log_parse_error(r
);
390 r
= dns_packet_new(&p
, DNS_PROTOCOL_DNS
, 0);
394 p
->refuse_compression
= true;
396 r
= dns_packet_append_blob(p
, d
, l
, NULL
);
400 r
= dns_packet_read_rr(p
, &rr
, NULL
);
402 log_error("Failed to parse RR.");
406 r
= dns_resource_record_to_string(rr
, &s
);
408 log_error("Failed to format RR.");
413 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
414 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
416 printf("%s%s%s\n", s
, isempty(ifname
) ? "" : " # interface ", ifname
);
420 return bus_log_parse_error(r
);
422 r
= sd_bus_message_exit_container(reply
);
424 return bus_log_parse_error(r
);
426 r
= sd_bus_message_read(reply
, "t", &flags
);
428 return bus_log_parse_error(r
);
431 log_error("%s: no records found", name
);
435 print_source(flags
, ts
);
440 static void help_dns_types(void) {
445 puts("Known DNS RR types:");
446 for (i
= 0; i
< _DNS_TYPE_MAX
; i
++) {
447 t
= dns_type_to_string(i
);
453 static void help_dns_classes(void) {
458 puts("Known DNS RR classes:");
459 for (i
= 0; i
< _DNS_CLASS_MAX
; i
++) {
460 t
= dns_class_to_string(i
);
466 static void help(void) {
467 printf("%s [OPTIONS...]\n\n"
468 "Resolve IPv4 or IPv6 addresses.\n\n"
469 " -h --help Show this help\n"
470 " --version Show package version\n"
471 " -4 Resolve IPv4 addresses\n"
472 " -6 Resolve IPv6 addresses\n"
473 " -i INTERFACE Look on interface\n"
474 " -p --protocol=PROTOCOL Look via protocol\n"
475 " -t --type=TYPE Query RR with DNS type\n"
476 " -c --class=CLASS Query RR with DNS class\n"
477 " --legend[=BOOL] Do [not] print column headers\n"
478 , program_invocation_short_name
);
481 static int parse_argv(int argc
, char *argv
[]) {
487 static const struct option options
[] = {
488 { "help", no_argument
, NULL
, 'h' },
489 { "version", no_argument
, NULL
, ARG_VERSION
},
490 { "type", required_argument
, NULL
, 't' },
491 { "class", required_argument
, NULL
, 'c' },
492 { "legend", optional_argument
, NULL
, ARG_LEGEND
},
493 { "protocol", required_argument
, NULL
, 'p' },
502 while ((c
= getopt_long(argc
, argv
, "h46i:t:c:p:", options
, NULL
)) >= 0)
507 return 0; /* done */;
513 arg_family
= AF_INET
;
517 arg_family
= AF_INET6
;
523 if (parse_ifindex(optarg
, &ifi
) >= 0)
526 ifi
= if_nametoindex(optarg
);
528 return log_error_errno(errno
, "Unknown interface %s: %m", optarg
);
537 if (streq(optarg
, "help")) {
542 arg_type
= dns_type_from_string(optarg
);
544 log_error("Failed to parse RR record type %s", optarg
);
547 assert(arg_type
> 0 && (uint16_t) arg_type
== arg_type
);
552 if (streq(optarg
, "help")) {
557 r
= dns_class_from_string(optarg
, &arg_class
);
559 log_error("Failed to parse RR record class %s", optarg
);
567 r
= parse_boolean(optarg
);
569 log_error("Failed to parse --legend= argument");
579 if (streq(optarg
, "dns"))
580 arg_flags
|= SD_RESOLVED_DNS
;
581 else if (streq(optarg
, "llmnr"))
582 arg_flags
|= SD_RESOLVED_LLMNR
;
583 else if (streq(optarg
, "llmnr-ipv4"))
584 arg_flags
|= SD_RESOLVED_LLMNR_IPV4
;
585 else if (streq(optarg
, "llmnr-ipv6"))
586 arg_flags
|= SD_RESOLVED_LLMNR_IPV6
;
588 log_error("Unknown protocol specifier: %s", optarg
);
598 assert_not_reached("Unhandled option");
601 if (arg_type
== 0 && arg_class
!= 0) {
602 log_error("--class= may only be used in conjunction with --type=");
606 if (arg_type
!= 0 && arg_class
== 0)
607 arg_class
= DNS_CLASS_IN
;
609 return 1 /* work to do */;
612 int main(int argc
, char **argv
) {
613 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
616 log_parse_environment();
619 r
= parse_argv(argc
, argv
);
623 if (optind
>= argc
) {
624 log_error("No arguments passed");
629 r
= sd_bus_open_system(&bus
);
631 log_error_errno(r
, "sd_bus_open_system: %m");
635 while (argv
[optind
]) {
636 int family
, ifindex
, k
;
637 union in_addr_union a
;
640 k
= resolve_record(bus
, argv
[optind
]);
642 k
= parse_address(argv
[optind
], &family
, &a
, &ifindex
);
644 k
= resolve_address(bus
, family
, &a
, ifindex
);
646 k
= resolve_host(bus
, argv
[optind
]);
656 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;