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
) {
49 if (ifindex
<= 0 && flags
== 0)
52 fputs("\n-- Information acquired via", stdout
);
55 printf(" protocol%s%s%s",
56 flags
& SD_RESOLVED_DNS
? " DNS" :"",
57 flags
& SD_RESOLVED_LLMNR_IPV4
? " LLMNR/IPv4" : "",
58 flags
& SD_RESOLVED_LLMNR_IPV6
? " LLMNR/IPv6" : "");
61 char ifname
[IF_NAMESIZE
] = "";
62 printf(" interface %s", strna(if_indextoname(ifindex
, ifname
)));
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
;
80 log_debug("Resolving %s (family %s, ifindex %i).", name
, af_to_name(arg_family
) ?: "*", arg_ifindex
);
82 r
= sd_bus_message_new_method_call(
85 "org.freedesktop.resolve1",
86 "/org/freedesktop/resolve1",
87 "org.freedesktop.resolve1.Manager",
90 return bus_log_create_error(r
);
92 r
= sd_bus_message_append(req
, "isit", arg_ifindex
, name
, arg_family
, arg_flags
);
94 return bus_log_create_error(r
);
96 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
98 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
102 r
= sd_bus_message_read(reply
, "i", &ifindex
);
104 return bus_log_parse_error(r
);
106 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
108 return bus_log_parse_error(r
);
110 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
114 _cleanup_free_
char *pretty
= NULL
;
115 char ifname
[IF_NAMESIZE
] = "";
117 r
= sd_bus_message_read(reply
, "i", &family
);
119 return bus_log_parse_error(r
);
121 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
123 return bus_log_parse_error(r
);
125 r
= sd_bus_message_exit_container(reply
);
127 return bus_log_parse_error(r
);
129 if (!IN_SET(family
, AF_INET
, AF_INET6
)) {
130 log_debug("%s: skipping entry with family %d (%s)", name
, family
, af_to_name(family
) ?: "unknown");
134 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
135 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
136 name
, sz
, af_to_name(family
) ?: "unknown");
143 t
= if_indextoname(ifindex
, ifname
);
145 log_error("Failed to resolve interface name for index %i", ifindex
);
150 r
= in_addr_to_string(family
, a
, &pretty
);
152 log_error_errno(r
, "%s: failed to print address: %m", name
);
156 printf("%*s%s %s%s%s\n",
157 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
159 isempty(ifname
) ? "" : "%", ifname
);
164 return bus_log_parse_error(r
);
166 r
= sd_bus_message_exit_container(reply
);
168 return bus_log_parse_error(r
);
170 r
= sd_bus_message_read(reply
, "st", &canonical
, &flags
);
172 return bus_log_parse_error(r
);
174 if (!streq(name
, canonical
)) {
175 printf("%*s%s (%s)\n",
176 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
181 log_error("%s: no addresses found", name
);
185 print_source(ifindex
, flags
);
190 static int resolve_address(sd_bus
*bus
, int family
, const union in_addr_union
*address
, int ifindex
) {
191 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
192 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
193 _cleanup_free_
char *pretty
= NULL
;
194 char ifname
[IF_NAMESIZE
] = "";
201 assert(IN_SET(family
, AF_INET
, AF_INET6
));
204 r
= in_addr_to_string(family
, address
, &pretty
);
211 t
= if_indextoname(ifindex
, ifname
);
213 log_error("Failed to resolve interface name for index %i", ifindex
);
218 log_debug("Resolving %s%s%s.", pretty
, isempty(ifname
) ? "" : "%", ifname
);
220 r
= sd_bus_message_new_method_call(
223 "org.freedesktop.resolve1",
224 "/org/freedesktop/resolve1",
225 "org.freedesktop.resolve1.Manager",
228 return bus_log_create_error(r
);
230 r
= sd_bus_message_set_auto_start(req
, false);
232 return bus_log_create_error(r
);
234 r
= sd_bus_message_append(req
, "ii", ifindex
, family
);
236 return bus_log_create_error(r
);
238 r
= sd_bus_message_append_array(req
, 'y', address
, FAMILY_ADDRESS_SIZE(family
));
240 return bus_log_create_error(r
);
242 r
= sd_bus_message_append(req
, "t", arg_flags
);
244 return bus_log_create_error(r
);
246 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
248 log_error("%s: resolve call failed: %s", pretty
, bus_error_message(&error
, r
));
252 r
= sd_bus_message_read(reply
, "i", &ifindex
);
254 return bus_log_parse_error(r
);
256 r
= sd_bus_message_enter_container(reply
, 'a', "s");
258 return bus_log_create_error(r
);
260 while ((r
= sd_bus_message_read(reply
, "s", &n
)) > 0) {
262 printf("%*s%s%s%s %s\n",
263 (int) strlen(pretty
), c
== 0 ? pretty
: "",
264 isempty(ifname
) ? "" : "%", ifname
,
271 return bus_log_parse_error(r
);
273 r
= sd_bus_message_exit_container(reply
);
275 return bus_log_parse_error(r
);
277 r
= sd_bus_message_read(reply
, "t", &flags
);
279 return bus_log_parse_error(r
);
282 log_error("%s: no names found", pretty
);
286 print_source(ifindex
, flags
);
291 static int parse_address(const char *s
, int *family
, union in_addr_union
*address
, int *ifindex
) {
292 const char *percent
, *a
;
296 percent
= strchr(s
, '%');
298 r
= safe_atoi(percent
+1, &ifi
);
299 if (r
< 0 || ifi
<= 0) {
300 ifi
= if_nametoindex(percent
+1);
305 a
= strndupa(s
, percent
- s
);
309 r
= in_addr_from_string_auto(a
, family
, address
);
317 static int resolve_record(sd_bus
*bus
, const char *name
) {
319 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
320 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
327 log_debug("Resolving %s %s %s.", name
, dns_class_to_string(arg_class
), dns_type_to_string(arg_type
));
329 r
= sd_bus_message_new_method_call(
332 "org.freedesktop.resolve1",
333 "/org/freedesktop/resolve1",
334 "org.freedesktop.resolve1.Manager",
337 return bus_log_create_error(r
);
339 assert((uint16_t) arg_type
== arg_type
);
340 r
= sd_bus_message_append(req
, "isqqt", arg_ifindex
, name
, arg_class
, arg_type
, arg_flags
);
342 return bus_log_create_error(r
);
344 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
346 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
350 r
= sd_bus_message_read(reply
, "i", &ifindex
);
352 return bus_log_parse_error(r
);
354 r
= sd_bus_message_enter_container(reply
, 'a', "(qqay)");
356 return bus_log_parse_error(r
);
358 while ((r
= sd_bus_message_enter_container(reply
, 'r', "qqay")) > 0) {
359 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
360 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
361 _cleanup_free_
char *s
= NULL
;
366 r
= sd_bus_message_read(reply
, "qq", &c
, &t
);
368 return bus_log_parse_error(r
);
370 r
= sd_bus_message_read_array(reply
, 'y', &d
, &l
);
372 return bus_log_parse_error(r
);
374 r
= sd_bus_message_exit_container(reply
);
376 return bus_log_parse_error(r
);
378 r
= dns_packet_new(&p
, DNS_PROTOCOL_DNS
, 0);
382 r
= dns_packet_append_blob(p
, d
, l
, NULL
);
386 r
= dns_packet_read_rr(p
, &rr
, NULL
);
388 log_error("Failed to parse RR.");
392 r
= dns_resource_record_to_string(rr
, &s
);
394 log_error("Failed to format RR.");
402 return bus_log_parse_error(r
);
404 r
= sd_bus_message_exit_container(reply
);
406 return bus_log_parse_error(r
);
408 r
= sd_bus_message_read(reply
, "t", &flags
);
410 return bus_log_parse_error(r
);
413 log_error("%s: no records found", name
);
417 print_source(ifindex
, flags
);
422 static void help_dns_types(void) {
427 puts("Known dns types:");
428 for (i
= 0; i
< _DNS_TYPE_MAX
; i
++) {
429 t
= dns_type_to_string(i
);
435 static void help_dns_classes(void) {
440 puts("Known dns classes:");
441 for (i
= 0; i
< _DNS_CLASS_MAX
; i
++) {
442 t
= dns_class_to_string(i
);
448 static void help(void) {
449 printf("%s [OPTIONS...]\n\n"
450 "Resolve IPv4 or IPv6 addresses.\n\n"
451 " -h --help Show this help\n"
452 " --version Show package version\n"
453 " -4 Resolve IPv4 addresses\n"
454 " -6 Resolve IPv6 addresses\n"
455 " -i INTERFACE Look on interface\n"
456 " -p --protocol=PROTOCOL Look via protocol\n"
457 " -t --type=TYPE Query RR with DNS type\n"
458 " -c --class=CLASS Query RR with DNS class\n"
459 " --legend[=BOOL] Do [not] print column headers\n"
460 , program_invocation_short_name
);
463 static int parse_argv(int argc
, char *argv
[]) {
469 static const struct option options
[] = {
470 { "help", no_argument
, NULL
, 'h' },
471 { "version", no_argument
, NULL
, ARG_VERSION
},
472 { "type", required_argument
, NULL
, 't' },
473 { "class", required_argument
, NULL
, 'c' },
474 { "legend", optional_argument
, NULL
, ARG_LEGEND
},
475 { "protocol", required_argument
, NULL
, 'p' },
484 while ((c
= getopt_long(argc
, argv
, "h46i:t:c:p:", options
, NULL
)) >= 0)
489 return 0; /* done */;
492 puts(PACKAGE_STRING
);
493 puts(SYSTEMD_FEATURES
);
497 arg_family
= AF_INET
;
501 arg_family
= AF_INET6
;
505 arg_ifindex
= if_nametoindex(optarg
);
506 if (arg_ifindex
<= 0)
507 return log_error_errno(errno
, "Unknown interfaces %s: %m", optarg
);
511 if (streq(optarg
, "help")) {
516 arg_type
= dns_type_from_string(optarg
);
518 log_error("Failed to parse RR record type %s", optarg
);
521 assert(arg_type
> 0 && (uint16_t) arg_type
== arg_type
);
526 if (streq(optarg
, "help")) {
531 r
= dns_class_from_string(optarg
, &arg_class
);
533 log_error("Failed to parse RR record class %s", optarg
);
541 r
= parse_boolean(optarg
);
543 log_error("Failed to parse --legend= argument");
553 if (streq(optarg
, "dns"))
554 arg_flags
|= SD_RESOLVED_DNS
;
555 else if (streq(optarg
, "llmnr"))
556 arg_flags
|= SD_RESOLVED_LLMNR
;
557 else if (streq(optarg
, "llmnr-ipv4"))
558 arg_flags
|= SD_RESOLVED_LLMNR_IPV4
;
559 else if (streq(optarg
, "llmnr-ipv6"))
560 arg_flags
|= SD_RESOLVED_LLMNR_IPV6
;
562 log_error("Unknown protocol specifier: %s", optarg
);
572 assert_not_reached("Unhandled option");
575 if (arg_type
== 0 && arg_class
!= 0) {
576 log_error("--class= may only be used in conjunction with --type=");
580 if (arg_type
!= 0 && arg_class
== 0)
581 arg_class
= DNS_CLASS_IN
;
583 return 1 /* work to do */;
586 int main(int argc
, char **argv
) {
587 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
590 log_parse_environment();
593 r
= parse_argv(argc
, argv
);
597 if (optind
>= argc
) {
598 log_error("No arguments passed");
603 r
= sd_bus_open_system(&bus
);
605 log_error_errno(r
, "sd_bus_open_system: %m");
609 while (argv
[optind
]) {
610 int family
, ifindex
, k
;
611 union in_addr_union a
;
614 k
= resolve_record(bus
, argv
[optind
]);
616 k
= parse_address(argv
[optind
], &family
, &a
, &ifindex
);
618 k
= resolve_address(bus
, family
, &a
, ifindex
);
620 k
= resolve_host(bus
, argv
[optind
]);
630 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;