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 uint16_t 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 r = sd_bus_message_append(req, "sqq", name, arg_class, arg_type);
321 return bus_log_create_error(r);
323 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
325 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
329 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
331 return bus_log_parse_error(r);
333 while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
334 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
335 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
336 _cleanup_free_ char *s = NULL;
341 r = sd_bus_message_read(reply, "qq", &c, &t);
343 return bus_log_parse_error(r);
345 r = sd_bus_message_read_array(reply, 'y', &d, &l);
347 return bus_log_parse_error(r);
349 r = sd_bus_message_exit_container(reply);
351 return bus_log_parse_error(r);
353 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
357 r = dns_packet_append_blob(p, d, l, NULL);
361 r = dns_packet_read_rr(p, &rr, NULL);
363 log_error("Failed to parse RR.");
367 r = dns_resource_record_to_string(rr, &s);
369 log_error("Failed to format RR.");
377 return bus_log_parse_error(r);
379 r = sd_bus_message_exit_container(reply);
381 return bus_log_parse_error(r);
384 log_error("%s: no records found", name);
391 static void help_dns_types(void) {
396 puts("Known dns types:");
397 for (i = 0; i < _DNS_TYPE_MAX; i++) {
398 t = dns_type_to_string(i);
404 static void help_dns_classes(void) {
409 puts("Known dns classes:");
410 for (i = 0; i < _DNS_CLASS_MAX; i++) {
411 t = dns_class_to_string(i);
417 static void help(void) {
418 printf("%s [OPTIONS...]\n\n"
419 "Resolve IPv4 or IPv6 addresses.\n\n"
420 " -h --help Show this help\n"
421 " --version Show package version\n"
422 " -4 Resolve IPv4 addresses\n"
423 " -6 Resolve IPv6 addresses\n"
424 " -i INTERFACE Filter by interface\n"
425 " -t --type=TYPE Query RR with DNS type\n"
426 " -c --class=CLASS Query RR with DNS class\n"
427 " --no-legend Do not print column headers\n"
428 , program_invocation_short_name);
431 static int parse_argv(int argc, char *argv[]) {
437 static const struct option options[] = {
438 { "help", no_argument, NULL, 'h' },
439 { "version", no_argument, NULL, ARG_VERSION },
440 { "type", no_argument, NULL, 't' },
441 { "class", no_argument, NULL, 'c' },
442 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
451 while ((c = getopt_long(argc, argv, "h46i:t:c:", options, NULL)) >= 0) {
456 return 0; /* done */;
459 puts(PACKAGE_STRING);
460 puts(SYSTEMD_FEATURES);
464 arg_family = AF_INET;
468 arg_family = AF_INET6;
472 arg_ifindex = if_nametoindex(optarg);
473 if (arg_ifindex <= 0) {
474 log_error("Unknown interfaces %s: %m", optarg);
480 if (streq(optarg, "help")) {
485 r = dns_type_from_string(optarg, &arg_type);
487 log_error("Failed to parse RR record type %s", optarg);
494 if (streq(optarg, "help")) {
499 r = dns_class_from_string(optarg, &arg_class);
501 log_error("Failed to parse RR record class %s", optarg);
515 assert_not_reached("Unhandled option");
519 if (arg_type == 0 && arg_class != 0) {
520 log_error("--class= may only be used in conjunction with --type=");
524 if (arg_type != 0 && arg_class == 0)
525 arg_class = DNS_CLASS_IN;
527 return 1 /* work to do */;
530 int main(int argc, char **argv) {
531 _cleanup_bus_unref_ sd_bus *bus = NULL;
534 log_parse_environment();
537 r = parse_argv(argc, argv);
541 if (optind >= argc) {
542 log_error("No arguments passed");
547 r = sd_bus_open_system(&bus);
549 log_error("sd_bus_open_system: %s", strerror(-r));
553 while (argv[optind]) {
554 int family, ifindex, k;
555 union in_addr_union a;
558 k = resolve_record(bus, argv[optind]);
560 k = parse_address(argv[optind], &family, &a, &ifindex);
562 k = resolve_address(bus, family, &a, ifindex);
564 k = resolve_host(bus, argv[optind]);
574 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;