]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve-host/resolve-host.c
resolved: allow passing on which protocol, family and interface to look something up
[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 34#include "resolved-dns-packet.h"
51323288 35#include "resolved-def.h"
2d4c5cbc 36
bdef7319
ZJS
37#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
38
39static int arg_family = AF_UNSPEC;
40static int arg_ifindex = 0;
de292aa1 41static int arg_type = 0;
2d4c5cbc 42static uint16_t arg_class = 0;
b93312f5 43static bool arg_legend = true;
51323288
LP
44static uint64_t arg_flags = 0;
45
46static void print_source(int ifindex, uint64_t flags) {
47
48 if (!arg_legend)
49 return;
50
51 if (ifindex <= 0 && flags == 0)
52 return;
53
54 fputs("\n-- Information acquired via", stdout);
55
56 if (flags != 0)
57 printf(" protocol%s%s%s",
58 flags & SD_RESOLVED_DNS ? " DNS" :"",
59 flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
60 flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "");
61
62 if (ifindex > 0) {
63 char ifname[IF_NAMESIZE] = "";
64 printf(" interface %s", strna(if_indextoname(ifindex, ifname)));
65 }
66
67 fputc('.', stdout);
68 fputc('\n', stdout);
69}
bdef7319 70
79266746 71static int resolve_host(sd_bus *bus, const char *name) {
bdef7319
ZJS
72
73 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
74 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
79266746 75 const char *canonical = NULL;
bdef7319 76 unsigned c = 0;
51323288
LP
77 int r, ifindex;
78 uint64_t flags;
bdef7319
ZJS
79
80 assert(name);
81
79266746 82 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
bdef7319
ZJS
83
84 r = sd_bus_message_new_method_call(
85 bus,
86 &req,
87 "org.freedesktop.resolve1",
88 "/org/freedesktop/resolve1",
89 "org.freedesktop.resolve1.Manager",
90 "ResolveHostname");
02dd6e18
LP
91 if (r < 0)
92 return bus_log_create_error(r);
bdef7319
ZJS
93
94 r = sd_bus_message_set_auto_start(req, false);
02dd6e18
LP
95 if (r < 0)
96 return bus_log_create_error(r);
bdef7319 97
51323288 98 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
02dd6e18
LP
99 if (r < 0)
100 return bus_log_create_error(r);
bdef7319
ZJS
101
102 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
103 if (r < 0) {
104 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
105 return r;
106 }
107
51323288 108 r = sd_bus_message_read(reply, "i", &ifindex);
02dd6e18
LP
109 if (r < 0)
110 return bus_log_parse_error(r);
bdef7319 111
51323288
LP
112 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
113 if (r < 0)
114 return bus_log_parse_error(r);
115
116 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
bdef7319 117 const void *a;
51323288 118 int family;
bdef7319
ZJS
119 size_t sz;
120 _cleanup_free_ char *pretty = NULL;
121 char ifname[IF_NAMESIZE] = "";
122
123 r = sd_bus_message_read(reply, "i", &family);
02dd6e18
LP
124 if (r < 0)
125 return bus_log_parse_error(r);
bdef7319
ZJS
126
127 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
02dd6e18
LP
128 if (r < 0)
129 return bus_log_parse_error(r);
bdef7319 130
bdef7319 131 r = sd_bus_message_exit_container(reply);
02dd6e18
LP
132 if (r < 0)
133 return bus_log_parse_error(r);
bdef7319 134
79266746 135 if (!IN_SET(family, AF_INET, AF_INET6)) {
be636413 136 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
bdef7319
ZJS
137 continue;
138 }
139
140 if (sz != FAMILY_ADDRESS_SIZE(family)) {
141 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s",
142 name, sz, af_to_name(family) ?: "unknown");
143 continue;
144 }
145
bdef7319
ZJS
146 if (ifindex > 0) {
147 char *t;
148
149 t = if_indextoname(ifindex, ifname);
150 if (!t) {
151 log_error("Failed to resolve interface name for index %i", ifindex);
152 continue;
153 }
154 }
155
bdef7319
ZJS
156 r = in_addr_to_string(family, a, &pretty);
157 if (r < 0) {
158 log_error("%s: failed to print address: %s", name, strerror(-r));
159 continue;
160 }
161
79266746
LP
162 printf("%*s%s %s%s%s\n",
163 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
164 pretty,
165 isempty(ifname) ? "" : "%", ifname);
bdef7319
ZJS
166
167 c++;
168 }
79266746
LP
169 if (r < 0)
170 return bus_log_parse_error(r);
171
172 r = sd_bus_message_exit_container(reply);
173 if (r < 0)
174 return bus_log_parse_error(r);
175
51323288 176 r = sd_bus_message_read(reply, "st", &canonical, &flags);
79266746
LP
177 if (r < 0)
178 return bus_log_parse_error(r);
179
180 if (!streq(name, canonical)) {
181 printf("%*s%s (%s)\n",
182 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
183 canonical);
184 }
bdef7319
ZJS
185
186 if (c == 0) {
187 log_error("%s: no addresses found", name);
79266746
LP
188 return -ESRCH;
189 }
190
51323288
LP
191 print_source(ifindex, flags);
192
79266746
LP
193 return 0;
194}
195
196static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
197 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
198 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
199 _cleanup_free_ char *pretty = NULL;
200 char ifname[IF_NAMESIZE] = "";
51323288 201 uint64_t flags;
79266746
LP
202 unsigned c = 0;
203 const char *n;
204 int r;
205
206 assert(bus);
207 assert(IN_SET(family, AF_INET, AF_INET6));
208 assert(address);
209
210 r = in_addr_to_string(family, address, &pretty);
211 if (r < 0)
212 return log_oom();
213
214 if (ifindex > 0) {
215 char *t;
216
217 t = if_indextoname(ifindex, ifname);
218 if (!t) {
219 log_error("Failed to resolve interface name for index %i", ifindex);
220 return -errno;
221 }
222 }
223
224 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
225
226 r = sd_bus_message_new_method_call(
227 bus,
228 &req,
229 "org.freedesktop.resolve1",
230 "/org/freedesktop/resolve1",
231 "org.freedesktop.resolve1.Manager",
232 "ResolveAddress");
233 if (r < 0)
234 return bus_log_create_error(r);
235
236 r = sd_bus_message_set_auto_start(req, false);
237 if (r < 0)
238 return bus_log_create_error(r);
239
51323288 240 r = sd_bus_message_append(req, "ii", ifindex, family);
79266746
LP
241 if (r < 0)
242 return bus_log_create_error(r);
243
244 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
245 if (r < 0)
246 return bus_log_create_error(r);
247
51323288 248 r = sd_bus_message_append(req, "t", arg_flags);
79266746
LP
249 if (r < 0)
250 return bus_log_create_error(r);
251
252 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
253 if (r < 0) {
254 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
255 return r;
256 }
257
51323288
LP
258 r = sd_bus_message_read(reply, "i", &ifindex);
259 if (r < 0)
260 return bus_log_parse_error(r);
261
79266746
LP
262 r = sd_bus_message_enter_container(reply, 'a', "s");
263 if (r < 0)
264 return bus_log_create_error(r);
265
266 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
267
268 printf("%*s%s%s%s %s\n",
269 (int) strlen(pretty), c == 0 ? pretty : "",
270 isempty(ifname) ? "" : "%", ifname,
271 c == 0 ? ":" : " ",
272 n);
273
274 c++;
bdef7319 275 }
79266746
LP
276 if (r < 0)
277 return bus_log_parse_error(r);
bdef7319 278
02dd6e18
LP
279 r = sd_bus_message_exit_container(reply);
280 if (r < 0)
281 return bus_log_parse_error(r);
282
51323288
LP
283 r = sd_bus_message_read(reply, "t", &flags);
284 if (r < 0)
285 return bus_log_parse_error(r);
286
79266746
LP
287 if (c == 0) {
288 log_error("%s: no names found", pretty);
289 return -ESRCH;
290 }
291
51323288
LP
292 print_source(ifindex, flags);
293
79266746
LP
294 return 0;
295}
296
297static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
298 const char *percent, *a;
299 int ifi = 0;
300 int r;
301
302 percent = strchr(s, '%');
303 if (percent) {
304 r = safe_atoi(percent+1, &ifi);
305 if (r < 0 || ifi <= 0) {
306 ifi = if_nametoindex(percent+1);
307 if (ifi <= 0)
308 return -EINVAL;
309 }
310
311 a = strndupa(s, percent - s);
312 } else
313 a = s;
314
315 r = in_addr_from_string_auto(a, family, address);
316 if (r < 0)
317 return r;
318
319 *ifindex = ifi;
02dd6e18 320 return 0;
bdef7319
ZJS
321}
322
2d4c5cbc
LP
323static int resolve_record(sd_bus *bus, const char *name) {
324
325 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
326 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
327 unsigned n = 0;
51323288
LP
328 uint64_t flags;
329 int r, ifindex;
2d4c5cbc
LP
330
331 assert(name);
332
333 log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type));
334
335 r = sd_bus_message_new_method_call(
336 bus,
337 &req,
338 "org.freedesktop.resolve1",
339 "/org/freedesktop/resolve1",
340 "org.freedesktop.resolve1.Manager",
341 "ResolveRecord");
342 if (r < 0)
343 return bus_log_create_error(r);
344
345 r = sd_bus_message_set_auto_start(req, false);
346 if (r < 0)
347 return bus_log_create_error(r);
348
de292aa1 349 assert((uint16_t) arg_type == arg_type);
51323288 350 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
2d4c5cbc
LP
351 if (r < 0)
352 return bus_log_create_error(r);
353
354 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
355 if (r < 0) {
356 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
357 return r;
358 }
359
51323288
LP
360 r = sd_bus_message_read(reply, "i", &ifindex);
361 if (r < 0)
362 return bus_log_parse_error(r);
363
2d4c5cbc
LP
364 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
365 if (r < 0)
366 return bus_log_parse_error(r);
367
368 while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
369 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
370 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
371 _cleanup_free_ char *s = NULL;
372 uint16_t c, t;
373 const void *d;
374 size_t l;
375
376 r = sd_bus_message_read(reply, "qq", &c, &t);
377 if (r < 0)
378 return bus_log_parse_error(r);
379
380 r = sd_bus_message_read_array(reply, 'y', &d, &l);
381 if (r < 0)
382 return bus_log_parse_error(r);
383
384 r = sd_bus_message_exit_container(reply);
385 if (r < 0)
386 return bus_log_parse_error(r);
387
388 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
389 if (r < 0)
390 return log_oom();
391
392 r = dns_packet_append_blob(p, d, l, NULL);
393 if (r < 0)
394 return log_oom();
395
396 r = dns_packet_read_rr(p, &rr, NULL);
397 if (r < 0) {
398 log_error("Failed to parse RR.");
399 return r;
400 }
401
402 r = dns_resource_record_to_string(rr, &s);
403 if (r < 0) {
404 log_error("Failed to format RR.");
405 return r;
406 }
407
408 printf("%s\n", s);
409 n++;
410 }
411 if (r < 0)
412 return bus_log_parse_error(r);
413
414 r = sd_bus_message_exit_container(reply);
415 if (r < 0)
416 return bus_log_parse_error(r);
417
51323288
LP
418 r = sd_bus_message_read(reply, "t", &flags);
419 if (r < 0)
420 return bus_log_parse_error(r);
421
2d4c5cbc
LP
422 if (n == 0) {
423 log_error("%s: no records found", name);
424 return -ESRCH;
425 }
426
51323288
LP
427 print_source(ifindex, flags);
428
2d4c5cbc
LP
429 return 0;
430}
431
b93312f5
ZJS
432static void help_dns_types(void) {
433 int i;
434 const char *t;
435
436 if (arg_legend)
437 puts("Known dns types:");
438 for (i = 0; i < _DNS_TYPE_MAX; i++) {
439 t = dns_type_to_string(i);
440 if (t)
441 puts(t);
442 }
443}
444
445static void help_dns_classes(void) {
446 int i;
447 const char *t;
448
449 if (arg_legend)
450 puts("Known dns classes:");
451 for (i = 0; i < _DNS_CLASS_MAX; i++) {
452 t = dns_class_to_string(i);
453 if (t)
454 puts(t);
455 }
456}
457
bdef7319
ZJS
458static void help(void) {
459 printf("%s [OPTIONS...]\n\n"
460 "Resolve IPv4 or IPv6 addresses.\n\n"
51323288
LP
461 " -h --help Show this help\n"
462 " --version Show package version\n"
463 " -4 Resolve IPv4 addresses\n"
464 " -6 Resolve IPv6 addresses\n"
465 " -i INTERFACE Look on interface\n"
466 " -p --protocol=PROTOCOL Look via protocol\n"
467 " -t --type=TYPE Query RR with DNS type\n"
468 " -c --class=CLASS Query RR with DNS class\n"
469 " --no-legend Do not print column headers\n"
79266746 470 , program_invocation_short_name);
bdef7319
ZJS
471}
472
473static int parse_argv(int argc, char *argv[]) {
474 enum {
475 ARG_VERSION = 0x100,
b93312f5 476 ARG_NO_LEGEND,
bdef7319
ZJS
477 };
478
479 static const struct option options[] = {
51323288
LP
480 { "help", no_argument, NULL, 'h' },
481 { "version", no_argument, NULL, ARG_VERSION },
482 { "type", required_argument, NULL, 't' },
483 { "class", required_argument, NULL, 'c' },
484 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
485 { "protocol", required_argument, NULL, 'p' },
bdef7319
ZJS
486 {}
487 };
488
2d4c5cbc 489 int c, r;
bdef7319
ZJS
490
491 assert(argc >= 0);
492 assert(argv);
493
51323288 494 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
bdef7319
ZJS
495 switch(c) {
496
497 case 'h':
498 help();
499 return 0; /* done */;
500
501 case ARG_VERSION:
502 puts(PACKAGE_STRING);
503 puts(SYSTEMD_FEATURES);
504 return 0 /* done */;
505
506 case '4':
507 arg_family = AF_INET;
508 break;
509
510 case '6':
511 arg_family = AF_INET6;
512 break;
513
514 case 'i':
515 arg_ifindex = if_nametoindex(optarg);
516 if (arg_ifindex <= 0) {
517 log_error("Unknown interfaces %s: %m", optarg);
2d4c5cbc
LP
518 return -errno;
519 }
520 break;
521
522 case 't':
b93312f5
ZJS
523 if (streq(optarg, "help")) {
524 help_dns_types();
525 return 0;
526 }
527
de292aa1
ZJS
528 arg_type = dns_type_from_string(optarg);
529 if (arg_type < 0) {
2d4c5cbc 530 log_error("Failed to parse RR record type %s", optarg);
590baf91 531 return arg_type;
2d4c5cbc 532 }
de292aa1 533 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
b93312f5 534
2d4c5cbc
LP
535 break;
536
537 case 'c':
b93312f5
ZJS
538 if (streq(optarg, "help")) {
539 help_dns_classes();
540 return 0;
541 }
542
2d4c5cbc
LP
543 r = dns_class_from_string(optarg, &arg_class);
544 if (r < 0) {
545 log_error("Failed to parse RR record class %s", optarg);
546 return r;
bdef7319 547 }
b93312f5
ZJS
548
549 break;
550
551 case ARG_NO_LEGEND:
552 arg_legend = false;
bdef7319
ZJS
553 break;
554
51323288
LP
555 case 'p':
556 if (streq(optarg, "dns"))
557 arg_flags |= SD_RESOLVED_DNS;
558 else if (streq(optarg, "llmnr"))
559 arg_flags |= SD_RESOLVED_LLMNR;
560 else if (streq(optarg, "llmnr-ipv4"))
561 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
562 else if (streq(optarg, "llmnr-ipv6"))
563 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
564 else {
565 log_error("Unknown protocol specifier: %s", optarg);
566 return -EINVAL;
567 }
568
569 break;
570
bdef7319
ZJS
571 case '?':
572 return -EINVAL;
573
574 default:
575 assert_not_reached("Unhandled option");
576 }
577
2d4c5cbc
LP
578 if (arg_type == 0 && arg_class != 0) {
579 log_error("--class= may only be used in conjunction with --type=");
580 return -EINVAL;
581 }
582
583 if (arg_type != 0 && arg_class == 0)
584 arg_class = DNS_CLASS_IN;
585
bdef7319
ZJS
586 return 1 /* work to do */;
587}
588
bdef7319 589int main(int argc, char **argv) {
24996861 590 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
bdef7319
ZJS
591 int r;
592
593 log_parse_environment();
594 log_open();
595
596 r = parse_argv(argc, argv);
597 if (r <= 0)
79266746
LP
598 goto finish;
599
600 if (optind >= argc) {
601 log_error("No arguments passed");
602 r = -EINVAL;
603 goto finish;
604 }
bdef7319
ZJS
605
606 r = sd_bus_open_system(&bus);
607 if (r < 0) {
608 log_error("sd_bus_open_system: %s", strerror(-r));
79266746 609 goto finish;
bdef7319
ZJS
610 }
611
612 while (argv[optind]) {
79266746
LP
613 int family, ifindex, k;
614 union in_addr_union a;
615
2d4c5cbc
LP
616 if (arg_type != 0)
617 k = resolve_record(bus, argv[optind]);
618 else {
619 k = parse_address(argv[optind], &family, &a, &ifindex);
620 if (k >= 0)
621 k = resolve_address(bus, family, &a, ifindex);
622 else
623 k = resolve_host(bus, argv[optind]);
624 }
bdef7319 625
bdef7319
ZJS
626 if (r == 0)
627 r = k;
79266746
LP
628
629 optind++;
bdef7319
ZJS
630 }
631
79266746 632finish:
bdef7319
ZJS
633 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
634}