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 r
= dns_packet_append_blob(p
, d
, l
, NULL
);
399 r
= dns_packet_read_rr(p
, &rr
, NULL
);
401 log_error("Failed to parse RR.");
405 r
= dns_resource_record_to_string(rr
, &s
);
407 log_error("Failed to format RR.");
412 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
413 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
415 printf("%s%s%s\n", s
, isempty(ifname
) ? "" : " # interface ", ifname
);
419 return bus_log_parse_error(r
);
421 r
= sd_bus_message_exit_container(reply
);
423 return bus_log_parse_error(r
);
425 r
= sd_bus_message_read(reply
, "t", &flags
);
427 return bus_log_parse_error(r
);
430 log_error("%s: no records found", name
);
434 print_source(flags
, ts
);
439 static void help_dns_types(void) {
444 puts("Known dns types:");
445 for (i
= 0; i
< _DNS_TYPE_MAX
; i
++) {
446 t
= dns_type_to_string(i
);
452 static void help_dns_classes(void) {
457 puts("Known dns classes:");
458 for (i
= 0; i
< _DNS_CLASS_MAX
; i
++) {
459 t
= dns_class_to_string(i
);
465 static void help(void) {
466 printf("%s [OPTIONS...]\n\n"
467 "Resolve IPv4 or IPv6 addresses.\n\n"
468 " -h --help Show this help\n"
469 " --version Show package version\n"
470 " -4 Resolve IPv4 addresses\n"
471 " -6 Resolve IPv6 addresses\n"
472 " -i INTERFACE Look on interface\n"
473 " -p --protocol=PROTOCOL Look via protocol\n"
474 " -t --type=TYPE Query RR with DNS type\n"
475 " -c --class=CLASS Query RR with DNS class\n"
476 " --legend[=BOOL] Do [not] print column headers\n"
477 , program_invocation_short_name
);
480 static int parse_argv(int argc
, char *argv
[]) {
486 static const struct option options
[] = {
487 { "help", no_argument
, NULL
, 'h' },
488 { "version", no_argument
, NULL
, ARG_VERSION
},
489 { "type", required_argument
, NULL
, 't' },
490 { "class", required_argument
, NULL
, 'c' },
491 { "legend", optional_argument
, NULL
, ARG_LEGEND
},
492 { "protocol", required_argument
, NULL
, 'p' },
501 while ((c
= getopt_long(argc
, argv
, "h46i:t:c:p:", options
, NULL
)) >= 0)
506 return 0; /* done */;
509 puts(PACKAGE_STRING
);
510 puts(SYSTEMD_FEATURES
);
514 arg_family
= AF_INET
;
518 arg_family
= AF_INET6
;
522 arg_ifindex
= if_nametoindex(optarg
);
523 if (arg_ifindex
<= 0)
524 return log_error_errno(errno
, "Unknown interfaces %s: %m", optarg
);
528 if (streq(optarg
, "help")) {
533 arg_type
= dns_type_from_string(optarg
);
535 log_error("Failed to parse RR record type %s", optarg
);
538 assert(arg_type
> 0 && (uint16_t) arg_type
== arg_type
);
543 if (streq(optarg
, "help")) {
548 r
= dns_class_from_string(optarg
, &arg_class
);
550 log_error("Failed to parse RR record class %s", optarg
);
558 r
= parse_boolean(optarg
);
560 log_error("Failed to parse --legend= argument");
570 if (streq(optarg
, "dns"))
571 arg_flags
|= SD_RESOLVED_DNS
;
572 else if (streq(optarg
, "llmnr"))
573 arg_flags
|= SD_RESOLVED_LLMNR
;
574 else if (streq(optarg
, "llmnr-ipv4"))
575 arg_flags
|= SD_RESOLVED_LLMNR_IPV4
;
576 else if (streq(optarg
, "llmnr-ipv6"))
577 arg_flags
|= SD_RESOLVED_LLMNR_IPV6
;
579 log_error("Unknown protocol specifier: %s", optarg
);
589 assert_not_reached("Unhandled option");
592 if (arg_type
== 0 && arg_class
!= 0) {
593 log_error("--class= may only be used in conjunction with --type=");
597 if (arg_type
!= 0 && arg_class
== 0)
598 arg_class
= DNS_CLASS_IN
;
600 return 1 /* work to do */;
603 int main(int argc
, char **argv
) {
604 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
607 log_parse_environment();
610 r
= parse_argv(argc
, argv
);
614 if (optind
>= argc
) {
615 log_error("No arguments passed");
620 r
= sd_bus_open_system(&bus
);
622 log_error_errno(r
, "sd_bus_open_system: %m");
626 while (argv
[optind
]) {
627 int family
, ifindex
, k
;
628 union in_addr_union a
;
631 k
= resolve_record(bus
, argv
[optind
]);
633 k
= parse_address(argv
[optind
], &family
, &a
, &ifindex
);
635 k
= resolve_address(bus
, family
, &a
, ifindex
);
637 k
= resolve_host(bus
, argv
[optind
]);
647 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;