chiark / gitweb /
61e2c39252012656afcef189a7f014da3a6ccb74
[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                 log_error("sd_bus_message_new_method_call: %s", strerror(-r));
60                 return r;
61         }
62
63         r = sd_bus_message_set_auto_start(req, false);
64         if (r < 0) {
65                 log_error("sd_bus_message_set_auto_start: %s", strerror(-r));
66                 return r;
67         }
68
69         r = sd_bus_message_append(req, "si", name, AF_UNSPEC);
70         if (r < 0) {
71                 log_error("sd_bus_message_append: %s", strerror(-r));
72                 return r;
73         }
74
75         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
76         if (r < 0) {
77                 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
78                 return r;
79         }
80
81         r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
82         if (r < 0) {
83                 log_error("%s: failed to parse reply: %s", name, bus_error_message(&error, r));
84                 return r;
85         }
86
87         while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
88                 const void *a;
89                 int family, ifindex;
90                 size_t sz;
91                 _cleanup_free_ char *pretty = NULL;
92                 char ifname[IF_NAMESIZE] = "";
93
94                 r = sd_bus_message_read(reply, "i", &family);
95                 if (r < 0) {
96                         log_error("Cannot parse message, aborting.");
97                         return -EBADMSG;
98                 }
99
100                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
101                 if (r < 0) {
102                         log_error("Cannot parse message, aborting.");
103                         return -EBADMSG;
104                 }
105
106                 r = sd_bus_message_read(reply, "i", &ifindex);
107                 if (r < 0) {
108                         log_error("Cannot parse message, aborting.");
109                         return -EBADMSG;
110                 }
111
112                 r = sd_bus_message_exit_container(reply);
113                 if (r < 0) {
114                         log_error("Cannot parse message, aborting.");
115                         return -EBADMSG;
116                 }
117
118                 if ((_family != AF_UNSPEC && family != _family) ||
119                     !IN_SET(family, AF_INET, AF_INET6)) {
120                         log_debug("%s: skipping entry with family %hu (%s)",
121                                   name, family, af_to_name(family) ?: "unknown");
122                         continue;
123                 }
124
125                 if (sz != FAMILY_ADDRESS_SIZE(family)) {
126                         log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
127                                   name, sz, af_to_name(family) ?: "unknown");
128                         continue;
129                 }
130
131                 if (ifindex < 0) {
132                         log_error("%s: systemd-resolved returned invalid interface index %i",
133                                   name, ifindex);
134                         continue;
135                 }
136
137                 if (ifindex > 0) {
138                         char *t;
139
140                         t = if_indextoname(ifindex, ifname);
141                         if (!t) {
142                                 log_error("Failed to resolve interface name for index %i", ifindex);
143                                 continue;
144                         }
145                 }
146
147                 if (_ifindex > 0 && ifindex > 0 && ifindex != _ifindex) {
148                         log_debug("%s: skipping entry with ifindex %i (%s)",
149                                   name, ifindex, ifname);
150                         continue;
151                 }
152
153                 r = in_addr_to_string(family, a, &pretty);
154                 if (r < 0) {
155                         log_error("%s: failed to print address: %s", name, strerror(-r));
156                         continue;
157                 }
158
159                 log_info("%*s%s %s%s%.*s",
160                          (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
161                          pretty,
162                          *ifname ? "%" : "", (int) sizeof(ifname), *ifname ? ifname: "");
163
164                 c++;
165         }
166
167         if (c == 0) {
168                 log_error("%s: no addresses found", name);
169                 return -ENONET;
170         }
171
172         return sd_bus_message_exit_container(reply);
173 }
174
175 static void help(void) {
176         printf("%s [OPTIONS...]\n\n"
177                "Resolve IPv4 or IPv6 addresses.\n\n"
178                "Options:\n"
179                "  -4                       Resolve IPv4 addresses\n"
180                "  -6                       Resolve IPv6 addresses\n"
181                "  -i INTERFACE             Filter by interface\n"
182                "  -h --help                Show this help and exit\n"
183                "  --version                Print version string and exit\n"
184                , program_invocation_short_name
185                );
186 }
187
188 static int parse_argv(int argc, char *argv[]) {
189         enum {
190                 ARG_VERSION = 0x100,
191         };
192
193         static const struct option options[] = {
194                 { "help",        no_argument,       NULL, 'h'           },
195                 { "version",     no_argument,       NULL, ARG_VERSION   },
196                 {}
197         };
198
199         int c;
200
201         assert(argc >= 0);
202         assert(argv);
203
204         while ((c = getopt_long(argc, argv, "h46i:", options, NULL)) >= 0)
205                 switch(c) {
206
207                 case 'h':
208                         help();
209                         return 0; /* done */;
210
211                 case ARG_VERSION:
212                         puts(PACKAGE_STRING);
213                         puts(SYSTEMD_FEATURES);
214                         return 0 /* done */;
215
216                 case '4':
217                         arg_family = AF_INET;
218                         break;
219
220                 case '6':
221                         arg_family = AF_INET6;
222                         break;
223
224                 case 'i':
225                         arg_ifindex = if_nametoindex(optarg);
226                         if (arg_ifindex <= 0) {
227                                 log_error("Unknown interfaces %s: %m", optarg);
228                                 return -EINVAL;
229                         }
230                         break;
231
232                 case '?':
233                         return -EINVAL;
234
235                 default:
236                         assert_not_reached("Unhandled option");
237                 }
238
239         return 1 /* work to do */;
240 }
241
242
243 int main(int argc, char **argv) {
244         _cleanup_bus_unref_ sd_bus *bus = NULL;
245         int r;
246
247         log_parse_environment();
248         log_open();
249
250         r = parse_argv(argc, argv);
251         if (r <= 0)
252                 goto end;
253
254         r = sd_bus_open_system(&bus);
255         if (r < 0) {
256                 log_error("sd_bus_open_system: %s", strerror(-r));
257                 goto end;
258         }
259
260         while (argv[optind]) {
261                 int k;
262
263                 k = resolve_host(bus, argv[optind++], arg_family, arg_ifindex);
264                 if (r == 0)
265                         r = k;
266         }
267
268  end:
269         return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
270 }