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 "bus-error.h"
30 #include "in-addr-util.h"
31 #include "parse-util.h"
32 #include "resolved-def.h"
33 #include "resolved-dns-packet.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 ? ":" : " ",
177 log_error("%s: no addresses found", name
);
181 print_source(flags
, ts
);
186 static int resolve_address(sd_bus
*bus
, int family
, const union in_addr_union
*address
, int ifindex
) {
187 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
188 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
189 _cleanup_free_
char *pretty
= NULL
;
190 char ifname
[IF_NAMESIZE
] = "";
197 assert(IN_SET(family
, AF_INET
, AF_INET6
));
201 ifindex
= arg_ifindex
;
203 r
= in_addr_to_string(family
, address
, &pretty
);
207 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
208 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
210 log_debug("Resolving %s%s%s.", pretty
, isempty(ifname
) ? "" : "%", ifname
);
212 r
= sd_bus_message_new_method_call(
215 "org.freedesktop.resolve1",
216 "/org/freedesktop/resolve1",
217 "org.freedesktop.resolve1.Manager",
220 return bus_log_create_error(r
);
222 r
= sd_bus_message_append(req
, "ii", ifindex
, family
);
224 return bus_log_create_error(r
);
226 r
= sd_bus_message_append_array(req
, 'y', address
, FAMILY_ADDRESS_SIZE(family
));
228 return bus_log_create_error(r
);
230 r
= sd_bus_message_append(req
, "t", arg_flags
);
232 return bus_log_create_error(r
);
234 ts
= now(CLOCK_MONOTONIC
);
236 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
238 log_error("%s: resolve call failed: %s", pretty
, bus_error_message(&error
, r
));
242 ts
= now(CLOCK_MONOTONIC
) - ts
;
244 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
246 return bus_log_create_error(r
);
248 while ((r
= sd_bus_message_enter_container(reply
, 'r', "is")) > 0) {
251 assert_cc(sizeof(int) == sizeof(int32_t));
253 r
= sd_bus_message_read(reply
, "is", &ifindex
, &n
);
257 r
= sd_bus_message_exit_container(reply
);
262 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
263 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
265 printf("%*s%*s%*s%s %s\n",
266 (int) strlen(pretty
), c
== 0 ? pretty
: "",
267 isempty(ifname
) ? 0 : 1, c
> 0 || isempty(ifname
) ? "" : "%",
268 (int) strlen(ifname
), c
== 0 ? ifname
: "",
275 return bus_log_parse_error(r
);
277 r
= sd_bus_message_exit_container(reply
);
279 return bus_log_parse_error(r
);
281 r
= sd_bus_message_read(reply
, "t", &flags
);
283 return bus_log_parse_error(r
);
286 log_error("%s: no names found", pretty
);
290 print_source(flags
, ts
);
295 static int parse_address(const char *s
, int *family
, union in_addr_union
*address
, int *ifindex
) {
296 const char *percent
, *a
;
300 percent
= strchr(s
, '%');
302 r
= safe_atoi(percent
+1, &ifi
);
303 if (r
< 0 || 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 (safe_atoi(optarg
, &ifi
) >= 0 && 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
;