chiark / gitweb /
72ab24c70eb9dbb33dbefb7c37e54d2167bf0286
[elogind.git] / src / resolve-host / resolve-host.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Zbigniew JÄ™drzejewski-Szmek
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <arpa/inet.h>
23 #include <net/if.h>
24 #include <getopt.h>
25
26 #include "sd-bus.h"
27 #include "bus-util.h"
28 #include "bus-error.h"
29 #include "bus-errors.h"
30 #include "in-addr-util.h"
31 #include "af-list.h"
32 #include "build.h"
33
34 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
35
36 static int arg_family = AF_UNSPEC;
37 static int arg_ifindex = 0;
38
39 static int resolve_host(sd_bus *bus, const char *name, int _family, int _ifindex) {
40
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         unsigned c = 0;
44         int r;
45
46         assert(name);
47
48         log_debug("Resolving %s (family %s)",
49                   name, af_to_name(_family));
50
51         r = sd_bus_message_new_method_call(
52                         bus,
53                         &req,
54                         "org.freedesktop.resolve1",
55                         "/org/freedesktop/resolve1",
56                         "org.freedesktop.resolve1.Manager",
57                         "ResolveHostname");
58         if (r < 0)
59                 return bus_log_create_error(r);
60
61         r = sd_bus_message_set_auto_start(req, false);
62         if (r < 0)
63                 return bus_log_create_error(r);
64
65         r = sd_bus_message_append(req, "si", name, AF_UNSPEC);
66         if (r < 0)
67                 return bus_log_create_error(r);
68
69         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
70         if (r < 0) {
71                 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
72                 return r;
73         }
74
75         r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
76         if (r < 0)
77                 return bus_log_parse_error(r);
78
79         while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
80                 const void *a;
81                 int family, ifindex;
82                 size_t sz;
83                 _cleanup_free_ char *pretty = NULL;
84                 char ifname[IF_NAMESIZE] = "";
85
86                 r = sd_bus_message_read(reply, "i", &family);
87                 if (r < 0)
88                         return bus_log_parse_error(r);
89
90                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
91                 if (r < 0)
92                         return bus_log_parse_error(r);
93
94                 r = sd_bus_message_read(reply, "i", &ifindex);
95                 if (r < 0)
96                         return bus_log_parse_error(r);
97
98                 r = sd_bus_message_exit_container(reply);
99                 if (r < 0)
100                         return bus_log_parse_error(r);
101
102                 if ((_family != AF_UNSPEC && family != _family) ||
103                     !IN_SET(family, AF_INET, AF_INET6)) {
104                         log_debug("%s: skipping entry with family %hu (%s)",
105                                   name, family, af_to_name(family) ?: "unknown");
106                         continue;
107                 }
108
109                 if (sz != FAMILY_ADDRESS_SIZE(family)) {
110                         log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
111                                   name, sz, af_to_name(family) ?: "unknown");
112                         continue;
113                 }
114
115                 if (ifindex < 0) {
116                         log_error("%s: systemd-resolved returned invalid interface index %i",
117                                   name, ifindex);
118                         continue;
119                 }
120
121                 if (ifindex > 0) {
122                         char *t;
123
124                         t = if_indextoname(ifindex, ifname);
125                         if (!t) {
126                                 log_error("Failed to resolve interface name for index %i", ifindex);
127                                 continue;
128                         }
129                 }
130
131                 if (_ifindex > 0 && ifindex > 0 && ifindex != _ifindex) {
132                         log_debug("%s: skipping entry with ifindex %i (%s)",
133                                   name, ifindex, ifname);
134                         continue;
135                 }
136
137                 r = in_addr_to_string(family, a, &pretty);
138                 if (r < 0) {
139                         log_error("%s: failed to print address: %s", name, strerror(-r));
140                         continue;
141                 }
142
143                 log_info("%*s%s %s%s%.*s",
144                          (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
145                          pretty,
146                          *ifname ? "%" : "", (int) sizeof(ifname), *ifname ? ifname: "");
147
148                 c++;
149         }
150
151         if (c == 0) {
152                 log_error("%s: no addresses found", name);
153                 return -ENONET;
154         }
155
156         r = sd_bus_message_exit_container(reply);
157         if (r < 0)
158                 return bus_log_parse_error(r);
159
160         return 0;
161 }
162
163 static void help(void) {
164         printf("%s [OPTIONS...]\n\n"
165                "Resolve IPv4 or IPv6 addresses.\n\n"
166                "Options:\n"
167                "  -4                       Resolve IPv4 addresses\n"
168                "  -6                       Resolve IPv6 addresses\n"
169                "  -i INTERFACE             Filter by interface\n"
170                "  -h --help                Show this help and exit\n"
171                "  --version                Print version string and exit\n"
172                , program_invocation_short_name
173                );
174 }
175
176 static int parse_argv(int argc, char *argv[]) {
177         enum {
178                 ARG_VERSION = 0x100,
179         };
180
181         static const struct option options[] = {
182                 { "help",        no_argument,       NULL, 'h'           },
183                 { "version",     no_argument,       NULL, ARG_VERSION   },
184                 {}
185         };
186
187         int c;
188
189         assert(argc >= 0);
190         assert(argv);
191
192         while ((c = getopt_long(argc, argv, "h46i:", options, NULL)) >= 0)
193                 switch(c) {
194
195                 case 'h':
196                         help();
197                         return 0; /* done */;
198
199                 case ARG_VERSION:
200                         puts(PACKAGE_STRING);
201                         puts(SYSTEMD_FEATURES);
202                         return 0 /* done */;
203
204                 case '4':
205                         arg_family = AF_INET;
206                         break;
207
208                 case '6':
209                         arg_family = AF_INET6;
210                         break;
211
212                 case 'i':
213                         arg_ifindex = if_nametoindex(optarg);
214                         if (arg_ifindex <= 0) {
215                                 log_error("Unknown interfaces %s: %m", optarg);
216                                 return -EINVAL;
217                         }
218                         break;
219
220                 case '?':
221                         return -EINVAL;
222
223                 default:
224                         assert_not_reached("Unhandled option");
225                 }
226
227         return 1 /* work to do */;
228 }
229
230 int main(int argc, char **argv) {
231         _cleanup_bus_unref_ sd_bus *bus = NULL;
232         int r;
233
234         log_parse_environment();
235         log_open();
236
237         r = parse_argv(argc, argv);
238         if (r <= 0)
239                 goto end;
240
241         r = sd_bus_open_system(&bus);
242         if (r < 0) {
243                 log_error("sd_bus_open_system: %s", strerror(-r));
244                 goto end;
245         }
246
247         while (argv[optind]) {
248                 int k;
249
250                 k = resolve_host(bus, argv[optind++], arg_family, arg_ifindex);
251                 if (r == 0)
252                         r = k;
253         }
254
255  end:
256         return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
257 }