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 r
= sd_bus_message_set_auto_start(req
, false);
341 return bus_log_create_error(r
);
343 assert((uint16_t) arg_type
== arg_type
);
344 r
= sd_bus_message_append(req
, "isqqt", arg_ifindex
, name
, arg_class
, arg_type
, arg_flags
);
346 return bus_log_create_error(r
);
348 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
350 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
354 r
= sd_bus_message_read(reply
, "i", &ifindex
);
356 return bus_log_parse_error(r
);
358 r
= sd_bus_message_enter_container(reply
, 'a', "(qqay)");
360 return bus_log_parse_error(r
);
362 while ((r
= sd_bus_message_enter_container(reply
, 'r', "qqay")) > 0) {
363 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
364 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
365 _cleanup_free_
char *s
= NULL
;
370 r
= sd_bus_message_read(reply
, "qq", &c
, &t
);
372 return bus_log_parse_error(r
);
374 r
= sd_bus_message_read_array(reply
, 'y', &d
, &l
);
376 return bus_log_parse_error(r
);
378 r
= sd_bus_message_exit_container(reply
);
380 return bus_log_parse_error(r
);
382 r
= dns_packet_new(&p
, DNS_PROTOCOL_DNS
, 0);
386 r
= dns_packet_append_blob(p
, d
, l
, NULL
);
390 r
= dns_packet_read_rr(p
, &rr
, NULL
);
392 log_error("Failed to parse RR.");
396 r
= dns_resource_record_to_string(rr
, &s
);
398 log_error("Failed to format RR.");
406 return bus_log_parse_error(r
);
408 r
= sd_bus_message_exit_container(reply
);
410 return bus_log_parse_error(r
);
412 r
= sd_bus_message_read(reply
, "t", &flags
);
414 return bus_log_parse_error(r
);
417 log_error("%s: no records found", name
);
421 print_source(ifindex
, flags
);
426 static void help_dns_types(void) {
431 puts("Known dns types:");
432 for (i
= 0; i
< _DNS_TYPE_MAX
; i
++) {
433 t
= dns_type_to_string(i
);
439 static void help_dns_classes(void) {
444 puts("Known dns classes:");
445 for (i
= 0; i
< _DNS_CLASS_MAX
; i
++) {
446 t
= dns_class_to_string(i
);
452 static void help(void) {
453 printf("%s [OPTIONS...]\n\n"
454 "Resolve IPv4 or IPv6 addresses.\n\n"
455 " -h --help Show this help\n"
456 " --version Show package version\n"
457 " -4 Resolve IPv4 addresses\n"
458 " -6 Resolve IPv6 addresses\n"
459 " -i INTERFACE Look on interface\n"
460 " -p --protocol=PROTOCOL Look via protocol\n"
461 " -t --type=TYPE Query RR with DNS type\n"
462 " -c --class=CLASS Query RR with DNS class\n"
463 " --legend[=BOOL] Do [not] print column headers\n"
464 , program_invocation_short_name
);
467 static int parse_argv(int argc
, char *argv
[]) {
473 static const struct option options
[] = {
474 { "help", no_argument
, NULL
, 'h' },
475 { "version", no_argument
, NULL
, ARG_VERSION
},
476 { "type", required_argument
, NULL
, 't' },
477 { "class", required_argument
, NULL
, 'c' },
478 { "legend", optional_argument
, NULL
, ARG_LEGEND
},
479 { "protocol", required_argument
, NULL
, 'p' },
488 while ((c
= getopt_long(argc
, argv
, "h46i:t:c:p:", options
, NULL
)) >= 0)
493 return 0; /* done */;
496 puts(PACKAGE_STRING
);
497 puts(SYSTEMD_FEATURES
);
501 arg_family
= AF_INET
;
505 arg_family
= AF_INET6
;
509 arg_ifindex
= if_nametoindex(optarg
);
510 if (arg_ifindex
<= 0)
511 return log_error_errno(errno
, "Unknown interfaces %s: %m", optarg
);
515 if (streq(optarg
, "help")) {
520 arg_type
= dns_type_from_string(optarg
);
522 log_error("Failed to parse RR record type %s", optarg
);
525 assert(arg_type
> 0 && (uint16_t) arg_type
== arg_type
);
530 if (streq(optarg
, "help")) {
535 r
= dns_class_from_string(optarg
, &arg_class
);
537 log_error("Failed to parse RR record class %s", optarg
);
545 r
= parse_boolean(optarg
);
547 log_error("Failed to parse --legend= argument");
557 if (streq(optarg
, "dns"))
558 arg_flags
|= SD_RESOLVED_DNS
;
559 else if (streq(optarg
, "llmnr"))
560 arg_flags
|= SD_RESOLVED_LLMNR
;
561 else if (streq(optarg
, "llmnr-ipv4"))
562 arg_flags
|= SD_RESOLVED_LLMNR_IPV4
;
563 else if (streq(optarg
, "llmnr-ipv6"))
564 arg_flags
|= SD_RESOLVED_LLMNR_IPV6
;
566 log_error("Unknown protocol specifier: %s", optarg
);
576 assert_not_reached("Unhandled option");
579 if (arg_type
== 0 && arg_class
!= 0) {
580 log_error("--class= may only be used in conjunction with --type=");
584 if (arg_type
!= 0 && arg_class
== 0)
585 arg_class
= DNS_CLASS_IN
;
587 return 1 /* work to do */;
590 int main(int argc
, char **argv
) {
591 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
594 log_parse_environment();
597 r
= parse_argv(argc
, argv
);
601 if (optind
>= argc
) {
602 log_error("No arguments passed");
607 r
= sd_bus_open_system(&bus
);
609 log_error_errno(r
, "sd_bus_open_system: %m");
613 while (argv
[optind
]) {
614 int family
, ifindex
, k
;
615 union in_addr_union a
;
618 k
= resolve_record(bus
, argv
[optind
]);
620 k
= parse_address(argv
[optind
], &family
, &a
, &ifindex
);
622 k
= resolve_address(bus
, family
, &a
, ifindex
);
624 k
= resolve_host(bus
, argv
[optind
]);
634 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;