]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve-host/resolve-host.c
Always prefer our headers to system headers
[thirdparty/systemd.git] / src / resolve-host / resolve-host.c
CommitLineData
bdef7319
ZJS
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
2d4c5cbc
LP
34#include "resolved-dns-packet.h"
35
bdef7319
ZJS
36#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
37
38static int arg_family = AF_UNSPEC;
39static int arg_ifindex = 0;
2d4c5cbc
LP
40static uint16_t arg_type = 0;
41static uint16_t arg_class = 0;
bdef7319 42
79266746 43static int resolve_host(sd_bus *bus, const char *name) {
bdef7319
ZJS
44
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;
79266746 47 const char *canonical = NULL;
bdef7319
ZJS
48 unsigned c = 0;
49 int r;
50
51 assert(name);
52
79266746 53 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
bdef7319
ZJS
54
55 r = sd_bus_message_new_method_call(
56 bus,
57 &req,
58 "org.freedesktop.resolve1",
59 "/org/freedesktop/resolve1",
60 "org.freedesktop.resolve1.Manager",
61 "ResolveHostname");
02dd6e18
LP
62 if (r < 0)
63 return bus_log_create_error(r);
bdef7319
ZJS
64
65 r = sd_bus_message_set_auto_start(req, false);
02dd6e18
LP
66 if (r < 0)
67 return bus_log_create_error(r);
bdef7319 68
79266746 69 r = sd_bus_message_append(req, "si", name, arg_family);
02dd6e18
LP
70 if (r < 0)
71 return bus_log_create_error(r);
bdef7319
ZJS
72
73 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
74 if (r < 0) {
75 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
76 return r;
77 }
78
79 r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
02dd6e18
LP
80 if (r < 0)
81 return bus_log_parse_error(r);
bdef7319
ZJS
82
83 while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
84 const void *a;
85 int family, ifindex;
86 size_t sz;
87 _cleanup_free_ char *pretty = NULL;
88 char ifname[IF_NAMESIZE] = "";
89
90 r = sd_bus_message_read(reply, "i", &family);
02dd6e18
LP
91 if (r < 0)
92 return bus_log_parse_error(r);
bdef7319
ZJS
93
94 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
02dd6e18
LP
95 if (r < 0)
96 return bus_log_parse_error(r);
bdef7319
ZJS
97
98 r = sd_bus_message_read(reply, "i", &ifindex);
02dd6e18
LP
99 if (r < 0)
100 return bus_log_parse_error(r);
bdef7319
ZJS
101
102 r = sd_bus_message_exit_container(reply);
02dd6e18
LP
103 if (r < 0)
104 return bus_log_parse_error(r);
bdef7319 105
79266746
LP
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");
bdef7319
ZJS
108 continue;
109 }
110
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");
114 continue;
115 }
116
117 if (ifindex < 0) {
118 log_error("%s: systemd-resolved returned invalid interface index %i",
119 name, ifindex);
120 continue;
121 }
122
123 if (ifindex > 0) {
124 char *t;
125
126 t = if_indextoname(ifindex, ifname);
127 if (!t) {
128 log_error("Failed to resolve interface name for index %i", ifindex);
129 continue;
130 }
131 }
132
79266746 133 if (arg_ifindex > 0 && ifindex > 0 && ifindex != arg_ifindex) {
bdef7319
ZJS
134 log_debug("%s: skipping entry with ifindex %i (%s)",
135 name, ifindex, ifname);
136 continue;
137 }
138
139 r = in_addr_to_string(family, a, &pretty);
140 if (r < 0) {
141 log_error("%s: failed to print address: %s", name, strerror(-r));
142 continue;
143 }
144
79266746
LP
145 printf("%*s%s %s%s%s\n",
146 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
147 pretty,
148 isempty(ifname) ? "" : "%", ifname);
bdef7319
ZJS
149
150 c++;
151 }
79266746
LP
152 if (r < 0)
153 return bus_log_parse_error(r);
154
155 r = sd_bus_message_exit_container(reply);
156 if (r < 0)
157 return bus_log_parse_error(r);
158
159 r = sd_bus_message_read(reply, "s", &canonical);
160 if (r < 0)
161 return bus_log_parse_error(r);
162
163 if (!streq(name, canonical)) {
164 printf("%*s%s (%s)\n",
165 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
166 canonical);
167 }
bdef7319
ZJS
168
169 if (c == 0) {
170 log_error("%s: no addresses found", name);
79266746
LP
171 return -ESRCH;
172 }
173
174 return 0;
175}
176
177static 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] = "";
182 unsigned c = 0;
183 const char *n;
184 int r;
185
186 assert(bus);
187 assert(IN_SET(family, AF_INET, AF_INET6));
188 assert(address);
189
190 r = in_addr_to_string(family, address, &pretty);
191 if (r < 0)
192 return log_oom();
193
194 if (ifindex > 0) {
195 char *t;
196
197 t = if_indextoname(ifindex, ifname);
198 if (!t) {
199 log_error("Failed to resolve interface name for index %i", ifindex);
200 return -errno;
201 }
202 }
203
204 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
205
206 r = sd_bus_message_new_method_call(
207 bus,
208 &req,
209 "org.freedesktop.resolve1",
210 "/org/freedesktop/resolve1",
211 "org.freedesktop.resolve1.Manager",
212 "ResolveAddress");
213 if (r < 0)
214 return bus_log_create_error(r);
215
216 r = sd_bus_message_set_auto_start(req, false);
217 if (r < 0)
218 return bus_log_create_error(r);
219
220 r = sd_bus_message_append(req, "i", family);
221 if (r < 0)
222 return bus_log_create_error(r);
223
224 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
225 if (r < 0)
226 return bus_log_create_error(r);
227
228 r = sd_bus_message_append(req, "i", ifindex);
229 if (r < 0)
230 return bus_log_create_error(r);
231
232 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
233 if (r < 0) {
234 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
235 return r;
236 }
237
238 r = sd_bus_message_enter_container(reply, 'a', "s");
239 if (r < 0)
240 return bus_log_create_error(r);
241
242 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
243
244 printf("%*s%s%s%s %s\n",
245 (int) strlen(pretty), c == 0 ? pretty : "",
246 isempty(ifname) ? "" : "%", ifname,
247 c == 0 ? ":" : " ",
248 n);
249
250 c++;
bdef7319 251 }
79266746
LP
252 if (r < 0)
253 return bus_log_parse_error(r);
bdef7319 254
02dd6e18
LP
255 r = sd_bus_message_exit_container(reply);
256 if (r < 0)
257 return bus_log_parse_error(r);
258
79266746
LP
259 if (c == 0) {
260 log_error("%s: no names found", pretty);
261 return -ESRCH;
262 }
263
264 return 0;
265}
266
267static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
268 const char *percent, *a;
269 int ifi = 0;
270 int r;
271
272 percent = strchr(s, '%');
273 if (percent) {
274 r = safe_atoi(percent+1, &ifi);
275 if (r < 0 || ifi <= 0) {
276 ifi = if_nametoindex(percent+1);
277 if (ifi <= 0)
278 return -EINVAL;
279 }
280
281 a = strndupa(s, percent - s);
282 } else
283 a = s;
284
285 r = in_addr_from_string_auto(a, family, address);
286 if (r < 0)
287 return r;
288
289 *ifindex = ifi;
02dd6e18 290 return 0;
bdef7319
ZJS
291}
292
2d4c5cbc
LP
293static int resolve_record(sd_bus *bus, const char *name) {
294
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;
297 unsigned n = 0;
298 int r;
299
300 assert(name);
301
302 log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type));
303
304 r = sd_bus_message_new_method_call(
305 bus,
306 &req,
307 "org.freedesktop.resolve1",
308 "/org/freedesktop/resolve1",
309 "org.freedesktop.resolve1.Manager",
310 "ResolveRecord");
311 if (r < 0)
312 return bus_log_create_error(r);
313
314 r = sd_bus_message_set_auto_start(req, false);
315 if (r < 0)
316 return bus_log_create_error(r);
317
318 r = sd_bus_message_append(req, "sqq", name, arg_class, arg_type);
319 if (r < 0)
320 return bus_log_create_error(r);
321
322 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
323 if (r < 0) {
324 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
325 return r;
326 }
327
328 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
329 if (r < 0)
330 return bus_log_parse_error(r);
331
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;
336 uint16_t c, t;
337 const void *d;
338 size_t l;
339
340 r = sd_bus_message_read(reply, "qq", &c, &t);
341 if (r < 0)
342 return bus_log_parse_error(r);
343
344 r = sd_bus_message_read_array(reply, 'y', &d, &l);
345 if (r < 0)
346 return bus_log_parse_error(r);
347
348 r = sd_bus_message_exit_container(reply);
349 if (r < 0)
350 return bus_log_parse_error(r);
351
352 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
353 if (r < 0)
354 return log_oom();
355
356 r = dns_packet_append_blob(p, d, l, NULL);
357 if (r < 0)
358 return log_oom();
359
360 r = dns_packet_read_rr(p, &rr, NULL);
361 if (r < 0) {
362 log_error("Failed to parse RR.");
363 return r;
364 }
365
366 r = dns_resource_record_to_string(rr, &s);
367 if (r < 0) {
368 log_error("Failed to format RR.");
369 return r;
370 }
371
372 printf("%s\n", s);
373 n++;
374 }
375 if (r < 0)
376 return bus_log_parse_error(r);
377
378 r = sd_bus_message_exit_container(reply);
379 if (r < 0)
380 return bus_log_parse_error(r);
381
382 if (n == 0) {
383 log_error("%s: no records found", name);
384 return -ESRCH;
385 }
386
387 return 0;
388}
389
bdef7319
ZJS
390static void help(void) {
391 printf("%s [OPTIONS...]\n\n"
392 "Resolve IPv4 or IPv6 addresses.\n\n"
79266746
LP
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"
2d4c5cbc
LP
398 " -t --type=TYPE Query RR with DNS type\n"
399 " -c --class=CLASS Query RR with DNS class\n"
79266746 400 , program_invocation_short_name);
bdef7319
ZJS
401}
402
403static int parse_argv(int argc, char *argv[]) {
404 enum {
405 ARG_VERSION = 0x100,
406 };
407
408 static const struct option options[] = {
409 { "help", no_argument, NULL, 'h' },
410 { "version", no_argument, NULL, ARG_VERSION },
2d4c5cbc
LP
411 { "type", no_argument, NULL, 't' },
412 { "class", no_argument, NULL, 'c' },
bdef7319
ZJS
413 {}
414 };
415
2d4c5cbc 416 int c, r;
bdef7319
ZJS
417
418 assert(argc >= 0);
419 assert(argv);
420
2d4c5cbc 421 while ((c = getopt_long(argc, argv, "h46i:t:c:", options, NULL)) >= 0) {
bdef7319
ZJS
422 switch(c) {
423
424 case 'h':
425 help();
426 return 0; /* done */;
427
428 case ARG_VERSION:
429 puts(PACKAGE_STRING);
430 puts(SYSTEMD_FEATURES);
431 return 0 /* done */;
432
433 case '4':
434 arg_family = AF_INET;
435 break;
436
437 case '6':
438 arg_family = AF_INET6;
439 break;
440
441 case 'i':
442 arg_ifindex = if_nametoindex(optarg);
443 if (arg_ifindex <= 0) {
444 log_error("Unknown interfaces %s: %m", optarg);
2d4c5cbc
LP
445 return -errno;
446 }
447 break;
448
449 case 't':
450 r = dns_type_from_string(optarg, &arg_type);
451 if (r < 0) {
452 log_error("Failed to parse RR record type %s", optarg);
453 return r;
454 }
455 break;
456
457 case 'c':
458 r = dns_class_from_string(optarg, &arg_class);
459 if (r < 0) {
460 log_error("Failed to parse RR record class %s", optarg);
461 return r;
bdef7319
ZJS
462 }
463 break;
464
465 case '?':
466 return -EINVAL;
467
468 default:
469 assert_not_reached("Unhandled option");
470 }
79266746 471 }
bdef7319 472
2d4c5cbc
LP
473 if (arg_type == 0 && arg_class != 0) {
474 log_error("--class= may only be used in conjunction with --type=");
475 return -EINVAL;
476 }
477
478 if (arg_type != 0 && arg_class == 0)
479 arg_class = DNS_CLASS_IN;
480
bdef7319
ZJS
481 return 1 /* work to do */;
482}
483
bdef7319
ZJS
484int main(int argc, char **argv) {
485 _cleanup_bus_unref_ sd_bus *bus = NULL;
486 int r;
487
488 log_parse_environment();
489 log_open();
490
491 r = parse_argv(argc, argv);
492 if (r <= 0)
79266746
LP
493 goto finish;
494
495 if (optind >= argc) {
496 log_error("No arguments passed");
497 r = -EINVAL;
498 goto finish;
499 }
bdef7319
ZJS
500
501 r = sd_bus_open_system(&bus);
502 if (r < 0) {
503 log_error("sd_bus_open_system: %s", strerror(-r));
79266746 504 goto finish;
bdef7319
ZJS
505 }
506
507 while (argv[optind]) {
79266746
LP
508 int family, ifindex, k;
509 union in_addr_union a;
510
2d4c5cbc
LP
511 if (arg_type != 0)
512 k = resolve_record(bus, argv[optind]);
513 else {
514 k = parse_address(argv[optind], &family, &a, &ifindex);
515 if (k >= 0)
516 k = resolve_address(bus, family, &a, ifindex);
517 else
518 k = resolve_host(bus, argv[optind]);
519 }
bdef7319 520
bdef7319
ZJS
521 if (r == 0)
522 r = k;
79266746
LP
523
524 optind++;
bdef7319
ZJS
525 }
526
79266746 527finish:
bdef7319
ZJS
528 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
529}