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/>.
22 #include <arpa/inet.h>
28 #include "bus-error.h"
29 #include "bus-errors.h"
30 #include "in-addr-util.h"
34 #include "resolved-dns-packet.h"
36 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
38 static int arg_family = AF_UNSPEC;
39 static int arg_ifindex = 0;
40 static int arg_type = 0;
41 static uint16_t arg_class = 0;
42 static bool arg_legend = true;
44 static int resolve_host(sd_bus *bus, const char *name) {
46 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
47 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
48 const char *canonical = NULL;
54 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
56 r = sd_bus_message_new_method_call(
59 "org.freedesktop.resolve1",
60 "/org/freedesktop/resolve1",
61 "org.freedesktop.resolve1.Manager",
64 return bus_log_create_error(r);
66 r = sd_bus_message_set_auto_start(req, false);
68 return bus_log_create_error(r);
70 r = sd_bus_message_append(req, "si", name, arg_family);
72 return bus_log_create_error(r);
74 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
76 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
80 r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
82 return bus_log_parse_error(r);
84 while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
88 _cleanup_free_ char *pretty = NULL;
89 char ifname[IF_NAMESIZE] = "";
91 r = sd_bus_message_read(reply, "i", &family);
93 return bus_log_parse_error(r);
95 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
97 return bus_log_parse_error(r);
99 r = sd_bus_message_read(reply, "i", &ifindex);
101 return bus_log_parse_error(r);
103 r = sd_bus_message_exit_container(reply);
105 return bus_log_parse_error(r);
107 if (!IN_SET(family, AF_INET, AF_INET6)) {
108 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
112 if (sz != FAMILY_ADDRESS_SIZE(family)) {
113 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
114 name, sz, af_to_name(family) ?: "unknown");
119 log_error("%s: systemd-resolved returned invalid interface index %i",
127 t = if_indextoname(ifindex, ifname);
129 log_error("Failed to resolve interface name for index %i", ifindex);
134 if (arg_ifindex > 0 && ifindex > 0 && ifindex != arg_ifindex) {
135 log_debug("%s: skipping entry with ifindex %i (%s)",
136 name, ifindex, ifname);
140 r = in_addr_to_string(family, a, &pretty);
142 log_error("%s: failed to print address: %s", name, strerror(-r));
146 printf("%*s%s %s%s%s\n",
147 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
149 isempty(ifname) ? "" : "%", ifname);
154 return bus_log_parse_error(r);
156 r = sd_bus_message_exit_container(reply);
158 return bus_log_parse_error(r);
160 r = sd_bus_message_read(reply, "s", &canonical);
162 return bus_log_parse_error(r);
164 if (!streq(name, canonical)) {
165 printf("%*s%s (%s)\n",
166 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
171 log_error("%s: no addresses found", name);
178 static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
179 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
180 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
181 _cleanup_free_ char *pretty = NULL;
182 char ifname[IF_NAMESIZE] = "";
188 assert(IN_SET(family, AF_INET, AF_INET6));
191 r = in_addr_to_string(family, address, &pretty);
198 t = if_indextoname(ifindex, ifname);
200 log_error("Failed to resolve interface name for index %i", ifindex);
205 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
207 r = sd_bus_message_new_method_call(
210 "org.freedesktop.resolve1",
211 "/org/freedesktop/resolve1",
212 "org.freedesktop.resolve1.Manager",
215 return bus_log_create_error(r);
217 r = sd_bus_message_set_auto_start(req, false);
219 return bus_log_create_error(r);
221 r = sd_bus_message_append(req, "i", 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, "i", ifindex);
231 return bus_log_create_error(r);
233 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
235 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
239 r = sd_bus_message_enter_container(reply, 'a', "s");
241 return bus_log_create_error(r);
243 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
245 printf("%*s%s%s%s %s\n",
246 (int) strlen(pretty), c == 0 ? pretty : "",
247 isempty(ifname) ? "" : "%", ifname,
254 return bus_log_parse_error(r);
256 r = sd_bus_message_exit_container(reply);
258 return bus_log_parse_error(r);
261 log_error("%s: no names found", pretty);
268 static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
269 const char *percent, *a;
273 percent = strchr(s, '%');
275 r = safe_atoi(percent+1, &ifi);
276 if (r < 0 || ifi <= 0) {
277 ifi = if_nametoindex(percent+1);
282 a = strndupa(s, percent - s);
286 r = in_addr_from_string_auto(a, family, address);
294 static int resolve_record(sd_bus *bus, const char *name) {
296 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
297 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
303 log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type));
305 r = sd_bus_message_new_method_call(
308 "org.freedesktop.resolve1",
309 "/org/freedesktop/resolve1",
310 "org.freedesktop.resolve1.Manager",
313 return bus_log_create_error(r);
315 r = sd_bus_message_set_auto_start(req, false);
317 return bus_log_create_error(r);
319 assert((uint16_t) arg_type == arg_type);
320 r = sd_bus_message_append(req, "sqq", name, arg_class, arg_type);
322 return bus_log_create_error(r);
324 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
326 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
330 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
332 return bus_log_parse_error(r);
334 while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
335 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
336 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
337 _cleanup_free_ char *s = NULL;
342 r = sd_bus_message_read(reply, "qq", &c, &t);
344 return bus_log_parse_error(r);
346 r = sd_bus_message_read_array(reply, 'y', &d, &l);
348 return bus_log_parse_error(r);
350 r = sd_bus_message_exit_container(reply);
352 return bus_log_parse_error(r);
354 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
358 r = dns_packet_append_blob(p, d, l, NULL);
362 r = dns_packet_read_rr(p, &rr, NULL);
364 log_error("Failed to parse RR.");
368 r = dns_resource_record_to_string(rr, &s);
370 log_error("Failed to format RR.");
378 return bus_log_parse_error(r);
380 r = sd_bus_message_exit_container(reply);
382 return bus_log_parse_error(r);
385 log_error("%s: no records found", name);
392 static void help_dns_types(void) {
397 puts("Known dns types:");
398 for (i = 0; i < _DNS_TYPE_MAX; i++) {
399 t = dns_type_to_string(i);
405 static void help_dns_classes(void) {
410 puts("Known dns classes:");
411 for (i = 0; i < _DNS_CLASS_MAX; i++) {
412 t = dns_class_to_string(i);
418 static void help(void) {
419 printf("%s [OPTIONS...]\n\n"
420 "Resolve IPv4 or IPv6 addresses.\n\n"
421 " -h --help Show this help\n"
422 " --version Show package version\n"
423 " -4 Resolve IPv4 addresses\n"
424 " -6 Resolve IPv6 addresses\n"
425 " -i INTERFACE Filter by interface\n"
426 " -t --type=TYPE Query RR with DNS type\n"
427 " -c --class=CLASS Query RR with DNS class\n"
428 " --no-legend Do not print column headers\n"
429 , program_invocation_short_name);
432 static int parse_argv(int argc, char *argv[]) {
438 static const struct option options[] = {
439 { "help", no_argument, NULL, 'h' },
440 { "version", no_argument, NULL, ARG_VERSION },
441 { "type", no_argument, NULL, 't' },
442 { "class", no_argument, NULL, 'c' },
443 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
452 while ((c = getopt_long(argc, argv, "h46i:t:c:", options, NULL)) >= 0)
457 return 0; /* done */;
460 puts(PACKAGE_STRING);
461 puts(SYSTEMD_FEATURES);
465 arg_family = AF_INET;
469 arg_family = AF_INET6;
473 arg_ifindex = if_nametoindex(optarg);
474 if (arg_ifindex <= 0) {
475 log_error("Unknown interfaces %s: %m", optarg);
481 if (streq(optarg, "help")) {
486 arg_type = dns_type_from_string(optarg);
488 log_error("Failed to parse RR record type %s", optarg);
491 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
496 if (streq(optarg, "help")) {
501 r = dns_class_from_string(optarg, &arg_class);
503 log_error("Failed to parse RR record class %s", optarg);
517 assert_not_reached("Unhandled option");
520 if (arg_type == 0 && arg_class != 0) {
521 log_error("--class= may only be used in conjunction with --type=");
525 if (arg_type != 0 && arg_class == 0)
526 arg_class = DNS_CLASS_IN;
528 return 1 /* work to do */;
531 int main(int argc, char **argv) {
532 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
535 log_parse_environment();
538 r = parse_argv(argc, argv);
542 if (optind >= argc) {
543 log_error("No arguments passed");
548 r = sd_bus_open_system(&bus);
550 log_error("sd_bus_open_system: %s", strerror(-r));
554 while (argv[optind]) {
555 int family, ifindex, k;
556 union in_addr_union a;
559 k = resolve_record(bus, argv[optind]);
561 k = parse_address(argv[optind], &family, &a, &ifindex);
563 k = resolve_address(bus, family, &a, ifindex);
565 k = resolve_host(bus, argv[optind]);
575 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;