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;
43 static int resolve_host(sd_bus *bus, const char *name) {
45 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
46 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
47 const char *canonical = NULL;
53 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
55 r = sd_bus_message_new_method_call(
58 "org.freedesktop.resolve1",
59 "/org/freedesktop/resolve1",
60 "org.freedesktop.resolve1.Manager",
63 return bus_log_create_error(r);
65 r = sd_bus_message_set_auto_start(req, false);
67 return bus_log_create_error(r);
69 r = sd_bus_message_append(req, "si", name, arg_family);
71 return bus_log_create_error(r);
73 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
75 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
79 r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
81 return bus_log_parse_error(r);
83 while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
87 _cleanup_free_ char *pretty = NULL;
88 char ifname[IF_NAMESIZE] = "";
90 r = sd_bus_message_read(reply, "i", &family);
92 return bus_log_parse_error(r);
94 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
96 return bus_log_parse_error(r);
98 r = sd_bus_message_read(reply, "i", &ifindex);
100 return bus_log_parse_error(r);
102 r = sd_bus_message_exit_container(reply);
104 return bus_log_parse_error(r);
106 if (!IN_SET(family, AF_INET, AF_INET6)) {
107 log_debug("%s: skipping entry with family %hu (%s)", name, family, af_to_name(family) ?: "unknown");
111 if (sz != FAMILY_ADDRESS_SIZE(family)) {
112 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
113 name, sz, af_to_name(family) ?: "unknown");
118 log_error("%s: systemd-resolved returned invalid interface index %i",
126 t = if_indextoname(ifindex, ifname);
128 log_error("Failed to resolve interface name for index %i", ifindex);
133 if (arg_ifindex > 0 && ifindex > 0 && ifindex != arg_ifindex) {
134 log_debug("%s: skipping entry with ifindex %i (%s)",
135 name, ifindex, ifname);
139 r = in_addr_to_string(family, a, &pretty);
141 log_error("%s: failed to print address: %s", name, strerror(-r));
145 printf("%*s%s %s%s%s\n",
146 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
148 isempty(ifname) ? "" : "%", ifname);
153 return bus_log_parse_error(r);
155 r = sd_bus_message_exit_container(reply);
157 return bus_log_parse_error(r);
159 r = sd_bus_message_read(reply, "s", &canonical);
161 return bus_log_parse_error(r);
163 if (!streq(name, canonical)) {
164 printf("%*s%s (%s)\n",
165 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
170 log_error("%s: no addresses found", name);
177 static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
178 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
179 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
180 _cleanup_free_ char *pretty = NULL;
181 char ifname[IF_NAMESIZE] = "";
187 assert(IN_SET(family, AF_INET, AF_INET6));
190 r = in_addr_to_string(family, address, &pretty);
197 t = if_indextoname(ifindex, ifname);
199 log_error("Failed to resolve interface name for index %i", ifindex);
204 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
206 r = sd_bus_message_new_method_call(
209 "org.freedesktop.resolve1",
210 "/org/freedesktop/resolve1",
211 "org.freedesktop.resolve1.Manager",
214 return bus_log_create_error(r);
216 r = sd_bus_message_set_auto_start(req, false);
218 return bus_log_create_error(r);
220 r = sd_bus_message_append(req, "i", family);
222 return bus_log_create_error(r);
224 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
226 return bus_log_create_error(r);
228 r = sd_bus_message_append(req, "i", ifindex);
230 return bus_log_create_error(r);
232 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
234 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
238 r = sd_bus_message_enter_container(reply, 'a', "s");
240 return bus_log_create_error(r);
242 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
244 printf("%*s%s%s%s %s\n",
245 (int) strlen(pretty), c == 0 ? pretty : "",
246 isempty(ifname) ? "" : "%", ifname,
253 return bus_log_parse_error(r);
255 r = sd_bus_message_exit_container(reply);
257 return bus_log_parse_error(r);
260 log_error("%s: no names found", pretty);
267 static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
268 const char *percent, *a;
272 percent = strchr(s, '%');
274 r = safe_atoi(percent+1, &ifi);
275 if (r < 0 || ifi <= 0) {
276 ifi = if_nametoindex(percent+1);
281 a = strndupa(s, percent - s);
285 r = in_addr_from_string_auto(a, family, address);
293 static int resolve_record(sd_bus *bus, const char *name) {
295 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
296 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
302 log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type));
304 r = sd_bus_message_new_method_call(
307 "org.freedesktop.resolve1",
308 "/org/freedesktop/resolve1",
309 "org.freedesktop.resolve1.Manager",
312 return bus_log_create_error(r);
314 r = sd_bus_message_set_auto_start(req, false);
316 return bus_log_create_error(r);
318 r = sd_bus_message_append(req, "sqq", name, arg_class, arg_type);
320 return bus_log_create_error(r);
322 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
324 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
328 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
330 return bus_log_parse_error(r);
332 while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
333 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
334 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
335 _cleanup_free_ char *s = NULL;
340 r = sd_bus_message_read(reply, "qq", &c, &t);
342 return bus_log_parse_error(r);
344 r = sd_bus_message_read_array(reply, 'y', &d, &l);
346 return bus_log_parse_error(r);
348 r = sd_bus_message_exit_container(reply);
350 return bus_log_parse_error(r);
352 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
356 r = dns_packet_append_blob(p, d, l, NULL);
360 r = dns_packet_read_rr(p, &rr, NULL);
362 log_error("Failed to parse RR.");
366 r = dns_resource_record_to_string(rr, &s);
368 log_error("Failed to format RR.");
376 return bus_log_parse_error(r);
378 r = sd_bus_message_exit_container(reply);
380 return bus_log_parse_error(r);
383 log_error("%s: no records found", name);
390 static void help(void) {
391 printf("%s [OPTIONS...]\n\n"
392 "Resolve IPv4 or IPv6 addresses.\n\n"
393 " -h --help Show this help\n"
394 " --version Show package version\n"
395 " -4 Resolve IPv4 addresses\n"
396 " -6 Resolve IPv6 addresses\n"
397 " -i INTERFACE Filter by interface\n"
398 " -t --type=TYPE Query RR with DNS type\n"
399 " -c --class=CLASS Query RR with DNS class\n"
400 , program_invocation_short_name);
403 static int parse_argv(int argc, char *argv[]) {
408 static const struct option options[] = {
409 { "help", no_argument, NULL, 'h' },
410 { "version", no_argument, NULL, ARG_VERSION },
411 { "type", no_argument, NULL, 't' },
412 { "class", no_argument, NULL, 'c' },
421 while ((c = getopt_long(argc, argv, "h46i:t:c:", options, NULL)) >= 0) {
426 return 0; /* done */;
429 puts(PACKAGE_STRING);
430 puts(SYSTEMD_FEATURES);
434 arg_family = AF_INET;
438 arg_family = AF_INET6;
442 arg_ifindex = if_nametoindex(optarg);
443 if (arg_ifindex <= 0) {
444 log_error("Unknown interfaces %s: %m", optarg);
450 r = dns_type_from_string(optarg, &arg_type);
452 log_error("Failed to parse RR record type %s", optarg);
458 r = dns_class_from_string(optarg, &arg_class);
460 log_error("Failed to parse RR record class %s", optarg);
469 assert_not_reached("Unhandled option");
473 if (arg_type == 0 && arg_class != 0) {
474 log_error("--class= may only be used in conjunction with --type=");
478 if (arg_type != 0 && arg_class == 0)
479 arg_class = DNS_CLASS_IN;
481 return 1 /* work to do */;
484 int main(int argc, char **argv) {
485 _cleanup_bus_unref_ sd_bus *bus = NULL;
488 log_parse_environment();
491 r = parse_argv(argc, argv);
495 if (optind >= argc) {
496 log_error("No arguments passed");
501 r = sd_bus_open_system(&bus);
503 log_error("sd_bus_open_system: %s", strerror(-r));
507 while (argv[optind]) {
508 int family, ifindex, k;
509 union in_addr_union a;
512 k = resolve_record(bus, argv[optind]);
514 k = parse_address(argv[optind], &family, &a, &ifindex);
516 k = resolve_address(bus, family, &a, ifindex);
518 k = resolve_host(bus, argv[optind]);
528 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;