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