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(uint64_t flags
, usec_t rtt
) {
45 char rtt_str
[FORMAT_TIMESTAMP_MAX
];
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" : "");
61 assert_se(format_timespan(rtt_str
, sizeof(rtt_str
), rtt
, 100));
63 printf(" in %s", rtt_str
);
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
;
74 char ifname
[IF_NAMESIZE
] = "";
82 if (arg_ifindex
> 0 && !if_indextoname(arg_ifindex
, ifname
))
83 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", arg_ifindex
);
85 log_debug("Resolving %s (family %s, interface %s).", name
, af_to_name(arg_family
) ?: "*", isempty(ifname
) ? "*" : ifname
);
87 r
= sd_bus_message_new_method_call(
90 "org.freedesktop.resolve1",
91 "/org/freedesktop/resolve1",
92 "org.freedesktop.resolve1.Manager",
95 return bus_log_create_error(r
);
97 r
= sd_bus_message_append(req
, "isit", arg_ifindex
, name
, arg_family
, arg_flags
);
99 return bus_log_create_error(r
);
101 ts
= now(CLOCK_MONOTONIC
);
103 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
105 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
109 ts
= now(CLOCK_MONOTONIC
) - ts
;
111 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
113 return bus_log_parse_error(r
);
115 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
118 _cleanup_free_
char *pretty
= NULL
;
121 assert_cc(sizeof(int) == sizeof(int32_t));
123 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &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", name
, sz
, af_to_name(family
) ?: "unknown");
146 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
147 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
149 r
= in_addr_to_string(family
, a
, &pretty
);
151 return log_error_errno(r
, "Failed to print address for %s: %m", name
);
153 printf("%*s%s %s%s%s\n",
154 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
156 isempty(ifname
) ? "" : "%", ifname
);
161 return bus_log_parse_error(r
);
163 r
= sd_bus_message_exit_container(reply
);
165 return bus_log_parse_error(r
);
167 r
= sd_bus_message_read(reply
, "st", &canonical
, &flags
);
169 return bus_log_parse_error(r
);
171 if (!streq(name
, canonical
)) {
172 printf("%*s%s (%s)\n",
173 (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 r
= safe_atoi(percent
+1, &ifi
);
304 if (r
< 0 || ifi
<= 0) {
305 ifi
= if_nametoindex(percent
+1);
310 a
= strndupa(s
, percent
- s
);
314 r
= in_addr_from_string_auto(a
, family
, address
);
322 static int resolve_record(sd_bus
*bus
, const char *name
) {
324 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
325 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
326 char ifname
[IF_NAMESIZE
] = "";
334 if (arg_ifindex
> 0 && !if_indextoname(arg_ifindex
, ifname
))
335 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", arg_ifindex
);
337 log_debug("Resolving %s %s %s (interface %s).", name
, dns_class_to_string(arg_class
), dns_type_to_string(arg_type
), isempty(ifname
) ? "*" : ifname
);
339 r
= sd_bus_message_new_method_call(
342 "org.freedesktop.resolve1",
343 "/org/freedesktop/resolve1",
344 "org.freedesktop.resolve1.Manager",
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 ts
= now(CLOCK_MONOTONIC
);
356 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
358 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
362 ts
= now(CLOCK_MONOTONIC
) - ts
;
364 r
= sd_bus_message_enter_container(reply
, 'a', "(iqqay)");
366 return bus_log_parse_error(r
);
368 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iqqay")) > 0) {
369 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
370 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
371 _cleanup_free_
char *s
= NULL
;
377 assert_cc(sizeof(int) == sizeof(int32_t));
379 r
= sd_bus_message_read(reply
, "iqq", &ifindex
, &c
, &t
);
381 return bus_log_parse_error(r
);
383 r
= sd_bus_message_read_array(reply
, 'y', &d
, &l
);
385 return bus_log_parse_error(r
);
387 r
= sd_bus_message_exit_container(reply
);
389 return bus_log_parse_error(r
);
391 r
= dns_packet_new(&p
, DNS_PROTOCOL_DNS
, 0);
395 p
->refuse_compression
= true;
397 r
= dns_packet_append_blob(p
, d
, l
, NULL
);
401 r
= dns_packet_read_rr(p
, &rr
, NULL
);
403 log_error("Failed to parse RR.");
407 r
= dns_resource_record_to_string(rr
, &s
);
409 log_error("Failed to format RR.");
414 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
415 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
417 printf("%s%s%s\n", s
, isempty(ifname
) ? "" : " # interface ", ifname
);
421 return bus_log_parse_error(r
);
423 r
= sd_bus_message_exit_container(reply
);
425 return bus_log_parse_error(r
);
427 r
= sd_bus_message_read(reply
, "t", &flags
);
429 return bus_log_parse_error(r
);
432 log_error("%s: no records found", name
);
436 print_source(flags
, ts
);
441 static void help_dns_types(void) {
446 puts("Known DNS RR types:");
447 for (i
= 0; i
< _DNS_TYPE_MAX
; i
++) {
448 t
= dns_type_to_string(i
);
454 static void help_dns_classes(void) {
459 puts("Known DNS RR classes:");
460 for (i
= 0; i
< _DNS_CLASS_MAX
; i
++) {
461 t
= dns_class_to_string(i
);
467 static void help(void) {
468 printf("%s [OPTIONS...]\n\n"
469 "Resolve IPv4 or IPv6 addresses.\n\n"
470 " -h --help Show this help\n"
471 " --version Show package version\n"
472 " -4 Resolve IPv4 addresses\n"
473 " -6 Resolve IPv6 addresses\n"
474 " -i INTERFACE Look on interface\n"
475 " -p --protocol=PROTOCOL Look via protocol\n"
476 " -t --type=TYPE Query RR with DNS type\n"
477 " -c --class=CLASS Query RR with DNS class\n"
478 " --legend[=BOOL] Do [not] print column headers\n"
479 , program_invocation_short_name
);
482 static int parse_argv(int argc
, char *argv
[]) {
488 static const struct option options
[] = {
489 { "help", no_argument
, NULL
, 'h' },
490 { "version", no_argument
, NULL
, ARG_VERSION
},
491 { "type", required_argument
, NULL
, 't' },
492 { "class", required_argument
, NULL
, 'c' },
493 { "legend", optional_argument
, NULL
, ARG_LEGEND
},
494 { "protocol", required_argument
, NULL
, 'p' },
503 while ((c
= getopt_long(argc
, argv
, "h46i:t:c:p:", options
, NULL
)) >= 0)
508 return 0; /* done */;
511 puts(PACKAGE_STRING
);
512 puts(SYSTEMD_FEATURES
);
516 arg_family
= AF_INET
;
520 arg_family
= AF_INET6
;
526 if (safe_atoi(optarg
, &ifi
) >= 0 && ifi
> 0)
529 ifi
= if_nametoindex(optarg
);
531 return log_error_errno(errno
, "Unknown interface %s: %m", optarg
);
540 if (streq(optarg
, "help")) {
545 arg_type
= dns_type_from_string(optarg
);
547 log_error("Failed to parse RR record type %s", optarg
);
550 assert(arg_type
> 0 && (uint16_t) arg_type
== arg_type
);
555 if (streq(optarg
, "help")) {
560 r
= dns_class_from_string(optarg
, &arg_class
);
562 log_error("Failed to parse RR record class %s", optarg
);
570 r
= parse_boolean(optarg
);
572 log_error("Failed to parse --legend= argument");
582 if (streq(optarg
, "dns"))
583 arg_flags
|= SD_RESOLVED_DNS
;
584 else if (streq(optarg
, "llmnr"))
585 arg_flags
|= SD_RESOLVED_LLMNR
;
586 else if (streq(optarg
, "llmnr-ipv4"))
587 arg_flags
|= SD_RESOLVED_LLMNR_IPV4
;
588 else if (streq(optarg
, "llmnr-ipv6"))
589 arg_flags
|= SD_RESOLVED_LLMNR_IPV6
;
591 log_error("Unknown protocol specifier: %s", optarg
);
601 assert_not_reached("Unhandled option");
604 if (arg_type
== 0 && arg_class
!= 0) {
605 log_error("--class= may only be used in conjunction with --type=");
609 if (arg_type
!= 0 && arg_class
== 0)
610 arg_class
= DNS_CLASS_IN
;
612 return 1 /* work to do */;
615 int main(int argc
, char **argv
) {
616 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
619 log_parse_environment();
622 r
= parse_argv(argc
, argv
);
626 if (optind
>= argc
) {
627 log_error("No arguments passed");
632 r
= sd_bus_open_system(&bus
);
634 log_error_errno(r
, "sd_bus_open_system: %m");
638 while (argv
[optind
]) {
639 int family
, ifindex
, k
;
640 union in_addr_union a
;
643 k
= resolve_record(bus
, argv
[optind
]);
645 k
= parse_address(argv
[optind
], &family
, &a
, &ifindex
);
647 k
= resolve_address(bus
, family
, &a
, ifindex
);
649 k
= resolve_host(bus
, argv
[optind
]);
659 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;