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 "resolved-def.h"
32 #include "resolved-dns-packet.h"
34 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
36 static int arg_family
= AF_UNSPEC
;
37 static int arg_ifindex
= 0;
38 static int arg_type
= 0;
39 static uint16_t arg_class
= 0;
40 static bool arg_legend
= true;
41 static uint64_t arg_flags
= 0;
43 static void print_source(uint64_t flags
, usec_t rtt
) {
44 char rtt_str
[FORMAT_TIMESTAMP_MAX
];
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" : "");
60 assert_se(format_timespan(rtt_str
, sizeof(rtt_str
), rtt
, 100));
62 printf(" in %s", rtt_str
);
68 static int resolve_host(sd_bus
*bus
, const char *name
) {
70 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
71 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
72 const char *canonical
= NULL
;
73 char ifname
[IF_NAMESIZE
] = "";
81 if (arg_ifindex
> 0 && !if_indextoname(arg_ifindex
, ifname
))
82 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", arg_ifindex
);
84 log_debug("Resolving %s (family %s, interface %s).", name
, af_to_name(arg_family
) ?: "*", isempty(ifname
) ? "*" : ifname
);
86 r
= sd_bus_message_new_method_call(
89 "org.freedesktop.resolve1",
90 "/org/freedesktop/resolve1",
91 "org.freedesktop.resolve1.Manager",
94 return bus_log_create_error(r
);
96 r
= sd_bus_message_append(req
, "isit", arg_ifindex
, name
, arg_family
, arg_flags
);
98 return bus_log_create_error(r
);
100 ts
= now(CLOCK_MONOTONIC
);
102 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
104 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
108 ts
= now(CLOCK_MONOTONIC
) - ts
;
110 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
112 return bus_log_parse_error(r
);
114 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
117 _cleanup_free_
char *pretty
= NULL
;
120 assert_cc(sizeof(int) == sizeof(int32_t));
122 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
124 return bus_log_parse_error(r
);
126 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
128 return bus_log_parse_error(r
);
130 r
= sd_bus_message_exit_container(reply
);
132 return bus_log_parse_error(r
);
134 if (!IN_SET(family
, AF_INET
, AF_INET6
)) {
135 log_debug("%s: skipping entry with family %d (%s)", name
, family
, af_to_name(family
) ?: "unknown");
139 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
140 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name
, sz
, af_to_name(family
) ?: "unknown");
145 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
146 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
148 r
= in_addr_to_string(family
, a
, &pretty
);
150 return log_error_errno(r
, "Failed to print address for %s: %m", name
);
152 printf("%*s%s %s%s%s\n",
153 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
155 isempty(ifname
) ? "" : "%", ifname
);
160 return bus_log_parse_error(r
);
162 r
= sd_bus_message_exit_container(reply
);
164 return bus_log_parse_error(r
);
166 r
= sd_bus_message_read(reply
, "st", &canonical
, &flags
);
168 return bus_log_parse_error(r
);
170 if (!streq(name
, canonical
))
171 printf("%*s%s (%s)\n",
172 (int) strlen(name
), c
== 0 ? name
: "", c
== 0 ? ":" : " ",
176 log_error("%s: no addresses found", name
);
180 print_source(flags
, ts
);
185 static int resolve_address(sd_bus
*bus
, int family
, const union in_addr_union
*address
, int ifindex
) {
186 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
187 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
188 _cleanup_free_
char *pretty
= NULL
;
189 char ifname
[IF_NAMESIZE
] = "";
196 assert(IN_SET(family
, AF_INET
, AF_INET6
));
200 ifindex
= arg_ifindex
;
202 r
= in_addr_to_string(family
, address
, &pretty
);
206 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
207 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
209 log_debug("Resolving %s%s%s.", pretty
, isempty(ifname
) ? "" : "%", ifname
);
211 r
= sd_bus_message_new_method_call(
214 "org.freedesktop.resolve1",
215 "/org/freedesktop/resolve1",
216 "org.freedesktop.resolve1.Manager",
219 return bus_log_create_error(r
);
221 r
= sd_bus_message_append(req
, "ii", ifindex
, family
);
223 return bus_log_create_error(r
);
225 r
= sd_bus_message_append_array(req
, 'y', address
, FAMILY_ADDRESS_SIZE(family
));
227 return bus_log_create_error(r
);
229 r
= sd_bus_message_append(req
, "t", arg_flags
);
231 return bus_log_create_error(r
);
233 ts
= now(CLOCK_MONOTONIC
);
235 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
237 log_error("%s: resolve call failed: %s", pretty
, bus_error_message(&error
, r
));
241 ts
= now(CLOCK_MONOTONIC
) - ts
;
243 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
245 return bus_log_create_error(r
);
247 while ((r
= sd_bus_message_enter_container(reply
, 'r', "is")) > 0) {
250 assert_cc(sizeof(int) == sizeof(int32_t));
252 r
= sd_bus_message_read(reply
, "is", &ifindex
, &n
);
256 r
= sd_bus_message_exit_container(reply
);
261 if (ifindex
> 0 && !if_indextoname(ifindex
, ifname
))
262 log_warning_errno(errno
, "Failed to resolve interface name for index %i: %m", ifindex
);
264 printf("%*s%*s%*s%s %s\n",
265 (int) strlen(pretty
), c
== 0 ? pretty
: "",
266 isempty(ifname
) ? 0 : 1, c
> 0 || isempty(ifname
) ? "" : "%",
267 (int) strlen(ifname
), c
== 0 ? ifname
: "",
274 return bus_log_parse_error(r
);
276 r
= sd_bus_message_exit_container(reply
);
278 return bus_log_parse_error(r
);
280 r
= sd_bus_message_read(reply
, "t", &flags
);
282 return bus_log_parse_error(r
);
285 log_error("%s: no names found", pretty
);
289 print_source(flags
, ts
);
294 static int parse_address(const char *s
, int *family
, union in_addr_union
*address
, int *ifindex
) {
295 const char *percent
, *a
;
299 percent
= strchr(s
, '%');
301 r
= safe_atoi(percent
+1, &ifi
);
302 if (r
< 0 || ifi
<= 0) {
303 ifi
= if_nametoindex(percent
+1);
308 a
= strndupa(s
, percent
- s
);
312 r
= in_addr_from_string_auto(a
, family
, address
);
320 static int resolve_record(sd_bus
*bus
, const char *name
) {
322 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
323 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
324 char ifname
[IF_NAMESIZE
] = "";
332 if (arg_ifindex
> 0 && !if_indextoname(arg_ifindex
, ifname
))
333 return log_error_errno(errno
, "Failed to resolve interface name for index %i: %m", arg_ifindex
);
335 log_debug("Resolving %s %s %s (interface %s).", name
, dns_class_to_string(arg_class
), dns_type_to_string(arg_type
), isempty(ifname
) ? "*" : ifname
);
337 r
= sd_bus_message_new_method_call(
340 "org.freedesktop.resolve1",
341 "/org/freedesktop/resolve1",
342 "org.freedesktop.resolve1.Manager",
345 return bus_log_create_error(r
);
347 assert((uint16_t) arg_type
== arg_type
);
348 r
= sd_bus_message_append(req
, "isqqt", arg_ifindex
, name
, arg_class
, arg_type
, arg_flags
);
350 return bus_log_create_error(r
);
352 ts
= now(CLOCK_MONOTONIC
);
354 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
356 log_error("%s: resolve call failed: %s", name
, bus_error_message(&error
, r
));
360 ts
= now(CLOCK_MONOTONIC
) - ts
;
362 r
= sd_bus_message_enter_container(reply
, 'a', "(iqqay)");
364 return bus_log_parse_error(r
);
366 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iqqay")) > 0) {
367 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
368 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
369 _cleanup_free_
char *s
= NULL
;
375 assert_cc(sizeof(int) == sizeof(int32_t));
377 r
= sd_bus_message_read(reply
, "iqq", &ifindex
, &c
, &t
);
379 return bus_log_parse_error(r
);
381 r
= sd_bus_message_read_array(reply
, 'y', &d
, &l
);
383 return bus_log_parse_error(r
);
385 r
= sd_bus_message_exit_container(reply
);
387 return bus_log_parse_error(r
);
389 r
= dns_packet_new(&p
, DNS_PROTOCOL_DNS
, 0);
393 p
->refuse_compression
= true;
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 RR 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 RR 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 */;
512 arg_family
= AF_INET
;
516 arg_family
= AF_INET6
;
522 if (safe_atoi(optarg
, &ifi
) >= 0 && ifi
> 0)
525 ifi
= if_nametoindex(optarg
);
527 return log_error_errno(errno
, "Unknown interface %s: %m", optarg
);
536 if (streq(optarg
, "help")) {
541 arg_type
= dns_type_from_string(optarg
);
543 log_error("Failed to parse RR record type %s", optarg
);
546 assert(arg_type
> 0 && (uint16_t) arg_type
== arg_type
);
551 if (streq(optarg
, "help")) {
556 r
= dns_class_from_string(optarg
, &arg_class
);
558 log_error("Failed to parse RR record class %s", optarg
);
566 r
= parse_boolean(optarg
);
568 log_error("Failed to parse --legend= argument");
578 if (streq(optarg
, "dns"))
579 arg_flags
|= SD_RESOLVED_DNS
;
580 else if (streq(optarg
, "llmnr"))
581 arg_flags
|= SD_RESOLVED_LLMNR
;
582 else if (streq(optarg
, "llmnr-ipv4"))
583 arg_flags
|= SD_RESOLVED_LLMNR_IPV4
;
584 else if (streq(optarg
, "llmnr-ipv6"))
585 arg_flags
|= SD_RESOLVED_LLMNR_IPV6
;
587 log_error("Unknown protocol specifier: %s", optarg
);
597 assert_not_reached("Unhandled option");
600 if (arg_type
== 0 && arg_class
!= 0) {
601 log_error("--class= may only be used in conjunction with --type=");
605 if (arg_type
!= 0 && arg_class
== 0)
606 arg_class
= DNS_CLASS_IN
;
608 return 1 /* work to do */;
611 int main(int argc
, char **argv
) {
612 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
615 log_parse_environment();
618 r
= parse_argv(argc
, argv
);
622 if (optind
>= argc
) {
623 log_error("No arguments passed");
628 r
= sd_bus_open_system(&bus
);
630 log_error_errno(r
, "sd_bus_open_system: %m");
634 while (argv
[optind
]) {
635 int family
, ifindex
, k
;
636 union in_addr_union a
;
639 k
= resolve_record(bus
, argv
[optind
]);
641 k
= parse_address(argv
[optind
], &family
, &a
, &ifindex
);
643 k
= resolve_address(bus
, family
, &a
, ifindex
);
645 k
= resolve_host(bus
, argv
[optind
]);
655 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;