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 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
36 static int arg_family = AF_UNSPEC;
37 static int arg_ifindex = 0;
39 static int resolve_host(sd_bus *bus, const char *name) {
41 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
42 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
43 const char *canonical = NULL;
49 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
51 r = sd_bus_message_new_method_call(
54 "org.freedesktop.resolve1",
55 "/org/freedesktop/resolve1",
56 "org.freedesktop.resolve1.Manager",
59 return bus_log_create_error(r);
61 r = sd_bus_message_set_auto_start(req, false);
63 return bus_log_create_error(r);
65 r = sd_bus_message_append(req, "si", name, arg_family);
67 return bus_log_create_error(r);
69 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
71 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
75 r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
77 return bus_log_parse_error(r);
79 while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
83 _cleanup_free_ char *pretty = NULL;
84 char ifname[IF_NAMESIZE] = "";
86 r = sd_bus_message_read(reply, "i", &family);
88 return bus_log_parse_error(r);
90 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
92 return bus_log_parse_error(r);
94 r = sd_bus_message_read(reply, "i", &ifindex);
96 return bus_log_parse_error(r);
98 r = sd_bus_message_exit_container(reply);
100 return bus_log_parse_error(r);
102 if (!IN_SET(family, AF_INET, AF_INET6)) {
103 log_debug("%s: skipping entry with family %hu (%s)", name, family, af_to_name(family) ?: "unknown");
107 if (sz != FAMILY_ADDRESS_SIZE(family)) {
108 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
109 name, sz, af_to_name(family) ?: "unknown");
114 log_error("%s: systemd-resolved returned invalid interface index %i",
122 t = if_indextoname(ifindex, ifname);
124 log_error("Failed to resolve interface name for index %i", ifindex);
129 if (arg_ifindex > 0 && ifindex > 0 && ifindex != arg_ifindex) {
130 log_debug("%s: skipping entry with ifindex %i (%s)",
131 name, ifindex, ifname);
135 r = in_addr_to_string(family, a, &pretty);
137 log_error("%s: failed to print address: %s", name, strerror(-r));
141 printf("%*s%s %s%s%s\n",
142 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
144 isempty(ifname) ? "" : "%", ifname);
149 return bus_log_parse_error(r);
151 r = sd_bus_message_exit_container(reply);
153 return bus_log_parse_error(r);
155 r = sd_bus_message_read(reply, "s", &canonical);
157 return bus_log_parse_error(r);
159 if (!streq(name, canonical)) {
160 printf("%*s%s (%s)\n",
161 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
166 log_error("%s: no addresses found", name);
173 static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
174 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
175 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
176 _cleanup_free_ char *pretty = NULL;
177 char ifname[IF_NAMESIZE] = "";
183 assert(IN_SET(family, AF_INET, AF_INET6));
186 r = in_addr_to_string(family, address, &pretty);
193 t = if_indextoname(ifindex, ifname);
195 log_error("Failed to resolve interface name for index %i", ifindex);
200 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
202 r = sd_bus_message_new_method_call(
205 "org.freedesktop.resolve1",
206 "/org/freedesktop/resolve1",
207 "org.freedesktop.resolve1.Manager",
210 return bus_log_create_error(r);
212 r = sd_bus_message_set_auto_start(req, false);
214 return bus_log_create_error(r);
216 r = sd_bus_message_append(req, "i", family);
218 return bus_log_create_error(r);
220 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
222 return bus_log_create_error(r);
224 r = sd_bus_message_append(req, "i", ifindex);
226 return bus_log_create_error(r);
228 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
230 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
234 r = sd_bus_message_enter_container(reply, 'a', "s");
236 return bus_log_create_error(r);
238 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
240 printf("%*s%s%s%s %s\n",
241 (int) strlen(pretty), c == 0 ? pretty : "",
242 isempty(ifname) ? "" : "%", ifname,
249 return bus_log_parse_error(r);
251 r = sd_bus_message_exit_container(reply);
253 return bus_log_parse_error(r);
256 log_error("%s: no names found", pretty);
263 static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
264 const char *percent, *a;
268 percent = strchr(s, '%');
270 r = safe_atoi(percent+1, &ifi);
271 if (r < 0 || ifi <= 0) {
272 ifi = if_nametoindex(percent+1);
277 a = strndupa(s, percent - s);
281 r = in_addr_from_string_auto(a, family, address);
289 static void help(void) {
290 printf("%s [OPTIONS...]\n\n"
291 "Resolve IPv4 or IPv6 addresses.\n\n"
292 " -h --help Show this help\n"
293 " --version Show package version\n"
294 " -4 Resolve IPv4 addresses\n"
295 " -6 Resolve IPv6 addresses\n"
296 " -i INTERFACE Filter by interface\n"
297 , program_invocation_short_name);
300 static int parse_argv(int argc, char *argv[]) {
305 static const struct option options[] = {
306 { "help", no_argument, NULL, 'h' },
307 { "version", no_argument, NULL, ARG_VERSION },
316 while ((c = getopt_long(argc, argv, "h46i:", options, NULL)) >= 0) {
321 return 0; /* done */;
324 puts(PACKAGE_STRING);
325 puts(SYSTEMD_FEATURES);
329 arg_family = AF_INET;
333 arg_family = AF_INET6;
337 arg_ifindex = if_nametoindex(optarg);
338 if (arg_ifindex <= 0) {
339 log_error("Unknown interfaces %s: %m", optarg);
348 assert_not_reached("Unhandled option");
352 return 1 /* work to do */;
355 int main(int argc, char **argv) {
356 _cleanup_bus_unref_ sd_bus *bus = NULL;
359 log_parse_environment();
362 r = parse_argv(argc, argv);
366 if (optind >= argc) {
367 log_error("No arguments passed");
372 r = sd_bus_open_system(&bus);
374 log_error("sd_bus_open_system: %s", strerror(-r));
378 while (argv[optind]) {
379 int family, ifindex, k;
380 union in_addr_union a;
382 k = parse_address(argv[optind], &family, &a, &ifindex);
384 k = resolve_address(bus, family, &a, ifindex);
386 k = resolve_host(bus, argv[optind]);
395 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;