]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve-host/resolve-host.c
resolve-host: enable auto start of resolved
[thirdparty/systemd.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 <net/if.h>
23 #include <getopt.h>
24
25 #include "sd-bus.h"
26 #include "bus-util.h"
27 #include "bus-error.h"
28 #include "in-addr-util.h"
29 #include "af-list.h"
30 #include "build.h"
31
32 #include "resolved-dns-packet.h"
33 #include "resolved-def.h"
34
35 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
36
37 static int arg_family = AF_UNSPEC;
38 static int arg_ifindex = 0;
39 static int arg_type = 0;
40 static uint16_t arg_class = 0;
41 static bool arg_legend = true;
42 static uint64_t arg_flags = 0;
43
44 static 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 }
68
69 static int resolve_host(sd_bus *bus, const char *name) {
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;
73 const char *canonical = NULL;
74 unsigned c = 0;
75 int r, ifindex;
76 uint64_t flags;
77
78 assert(name);
79
80 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
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");
89 if (r < 0)
90 return bus_log_create_error(r);
91
92 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
93 if (r < 0)
94 return bus_log_create_error(r);
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
102 r = sd_bus_message_read(reply, "i", &ifindex);
103 if (r < 0)
104 return bus_log_parse_error(r);
105
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) {
111 const void *a;
112 int family;
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);
118 if (r < 0)
119 return bus_log_parse_error(r);
120
121 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
122 if (r < 0)
123 return bus_log_parse_error(r);
124
125 r = sd_bus_message_exit_container(reply);
126 if (r < 0)
127 return bus_log_parse_error(r);
128
129 if (!IN_SET(family, AF_INET, AF_INET6)) {
130 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
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
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
150 r = in_addr_to_string(family, a, &pretty);
151 if (r < 0) {
152 log_error_errno(r, "%s: failed to print address: %m", name);
153 continue;
154 }
155
156 printf("%*s%s %s%s%s\n",
157 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
158 pretty,
159 isempty(ifname) ? "" : "%", ifname);
160
161 c++;
162 }
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
170 r = sd_bus_message_read(reply, "st", &canonical, &flags);
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 }
179
180 if (c == 0) {
181 log_error("%s: no addresses found", name);
182 return -ESRCH;
183 }
184
185 print_source(ifindex, flags);
186
187 return 0;
188 }
189
190 static 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] = "";
195 uint64_t flags;
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
234 r = sd_bus_message_append(req, "ii", ifindex, family);
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
242 r = sd_bus_message_append(req, "t", arg_flags);
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
252 r = sd_bus_message_read(reply, "i", &ifindex);
253 if (r < 0)
254 return bus_log_parse_error(r);
255
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++;
269 }
270 if (r < 0)
271 return bus_log_parse_error(r);
272
273 r = sd_bus_message_exit_container(reply);
274 if (r < 0)
275 return bus_log_parse_error(r);
276
277 r = sd_bus_message_read(reply, "t", &flags);
278 if (r < 0)
279 return bus_log_parse_error(r);
280
281 if (c == 0) {
282 log_error("%s: no names found", pretty);
283 return -ESRCH;
284 }
285
286 print_source(ifindex, flags);
287
288 return 0;
289 }
290
291 static 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;
314 return 0;
315 }
316
317 static 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;
322 uint64_t flags;
323 int r, ifindex;
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 assert((uint16_t) arg_type == arg_type);
340 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
341 if (r < 0)
342 return bus_log_create_error(r);
343
344 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
345 if (r < 0) {
346 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
347 return r;
348 }
349
350 r = sd_bus_message_read(reply, "i", &ifindex);
351 if (r < 0)
352 return bus_log_parse_error(r);
353
354 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
355 if (r < 0)
356 return bus_log_parse_error(r);
357
358 while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
359 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
360 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
361 _cleanup_free_ char *s = NULL;
362 uint16_t c, t;
363 const void *d;
364 size_t l;
365
366 r = sd_bus_message_read(reply, "qq", &c, &t);
367 if (r < 0)
368 return bus_log_parse_error(r);
369
370 r = sd_bus_message_read_array(reply, 'y', &d, &l);
371 if (r < 0)
372 return bus_log_parse_error(r);
373
374 r = sd_bus_message_exit_container(reply);
375 if (r < 0)
376 return bus_log_parse_error(r);
377
378 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
379 if (r < 0)
380 return log_oom();
381
382 r = dns_packet_append_blob(p, d, l, NULL);
383 if (r < 0)
384 return log_oom();
385
386 r = dns_packet_read_rr(p, &rr, NULL);
387 if (r < 0) {
388 log_error("Failed to parse RR.");
389 return r;
390 }
391
392 r = dns_resource_record_to_string(rr, &s);
393 if (r < 0) {
394 log_error("Failed to format RR.");
395 return r;
396 }
397
398 printf("%s\n", s);
399 n++;
400 }
401 if (r < 0)
402 return bus_log_parse_error(r);
403
404 r = sd_bus_message_exit_container(reply);
405 if (r < 0)
406 return bus_log_parse_error(r);
407
408 r = sd_bus_message_read(reply, "t", &flags);
409 if (r < 0)
410 return bus_log_parse_error(r);
411
412 if (n == 0) {
413 log_error("%s: no records found", name);
414 return -ESRCH;
415 }
416
417 print_source(ifindex, flags);
418
419 return 0;
420 }
421
422 static void help_dns_types(void) {
423 int i;
424 const char *t;
425
426 if (arg_legend)
427 puts("Known dns types:");
428 for (i = 0; i < _DNS_TYPE_MAX; i++) {
429 t = dns_type_to_string(i);
430 if (t)
431 puts(t);
432 }
433 }
434
435 static void help_dns_classes(void) {
436 int i;
437 const char *t;
438
439 if (arg_legend)
440 puts("Known dns classes:");
441 for (i = 0; i < _DNS_CLASS_MAX; i++) {
442 t = dns_class_to_string(i);
443 if (t)
444 puts(t);
445 }
446 }
447
448 static void help(void) {
449 printf("%s [OPTIONS...]\n\n"
450 "Resolve IPv4 or IPv6 addresses.\n\n"
451 " -h --help Show this help\n"
452 " --version Show package version\n"
453 " -4 Resolve IPv4 addresses\n"
454 " -6 Resolve IPv6 addresses\n"
455 " -i INTERFACE Look on interface\n"
456 " -p --protocol=PROTOCOL Look via protocol\n"
457 " -t --type=TYPE Query RR with DNS type\n"
458 " -c --class=CLASS Query RR with DNS class\n"
459 " --legend[=BOOL] Do [not] print column headers\n"
460 , program_invocation_short_name);
461 }
462
463 static int parse_argv(int argc, char *argv[]) {
464 enum {
465 ARG_VERSION = 0x100,
466 ARG_LEGEND,
467 };
468
469 static const struct option options[] = {
470 { "help", no_argument, NULL, 'h' },
471 { "version", no_argument, NULL, ARG_VERSION },
472 { "type", required_argument, NULL, 't' },
473 { "class", required_argument, NULL, 'c' },
474 { "legend", optional_argument, NULL, ARG_LEGEND },
475 { "protocol", required_argument, NULL, 'p' },
476 {}
477 };
478
479 int c, r;
480
481 assert(argc >= 0);
482 assert(argv);
483
484 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
485 switch(c) {
486
487 case 'h':
488 help();
489 return 0; /* done */;
490
491 case ARG_VERSION:
492 puts(PACKAGE_STRING);
493 puts(SYSTEMD_FEATURES);
494 return 0 /* done */;
495
496 case '4':
497 arg_family = AF_INET;
498 break;
499
500 case '6':
501 arg_family = AF_INET6;
502 break;
503
504 case 'i':
505 arg_ifindex = if_nametoindex(optarg);
506 if (arg_ifindex <= 0)
507 return log_error_errno(errno, "Unknown interfaces %s: %m", optarg);
508 break;
509
510 case 't':
511 if (streq(optarg, "help")) {
512 help_dns_types();
513 return 0;
514 }
515
516 arg_type = dns_type_from_string(optarg);
517 if (arg_type < 0) {
518 log_error("Failed to parse RR record type %s", optarg);
519 return arg_type;
520 }
521 assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
522
523 break;
524
525 case 'c':
526 if (streq(optarg, "help")) {
527 help_dns_classes();
528 return 0;
529 }
530
531 r = dns_class_from_string(optarg, &arg_class);
532 if (r < 0) {
533 log_error("Failed to parse RR record class %s", optarg);
534 return r;
535 }
536
537 break;
538
539 case ARG_LEGEND:
540 if (optarg) {
541 r = parse_boolean(optarg);
542 if (r < 0) {
543 log_error("Failed to parse --legend= argument");
544 return r;
545 }
546
547 arg_legend = !!r;
548 } else
549 arg_legend = false;
550 break;
551
552 case 'p':
553 if (streq(optarg, "dns"))
554 arg_flags |= SD_RESOLVED_DNS;
555 else if (streq(optarg, "llmnr"))
556 arg_flags |= SD_RESOLVED_LLMNR;
557 else if (streq(optarg, "llmnr-ipv4"))
558 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
559 else if (streq(optarg, "llmnr-ipv6"))
560 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
561 else {
562 log_error("Unknown protocol specifier: %s", optarg);
563 return -EINVAL;
564 }
565
566 break;
567
568 case '?':
569 return -EINVAL;
570
571 default:
572 assert_not_reached("Unhandled option");
573 }
574
575 if (arg_type == 0 && arg_class != 0) {
576 log_error("--class= may only be used in conjunction with --type=");
577 return -EINVAL;
578 }
579
580 if (arg_type != 0 && arg_class == 0)
581 arg_class = DNS_CLASS_IN;
582
583 return 1 /* work to do */;
584 }
585
586 int main(int argc, char **argv) {
587 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
588 int r;
589
590 log_parse_environment();
591 log_open();
592
593 r = parse_argv(argc, argv);
594 if (r <= 0)
595 goto finish;
596
597 if (optind >= argc) {
598 log_error("No arguments passed");
599 r = -EINVAL;
600 goto finish;
601 }
602
603 r = sd_bus_open_system(&bus);
604 if (r < 0) {
605 log_error_errno(r, "sd_bus_open_system: %m");
606 goto finish;
607 }
608
609 while (argv[optind]) {
610 int family, ifindex, k;
611 union in_addr_union a;
612
613 if (arg_type != 0)
614 k = resolve_record(bus, argv[optind]);
615 else {
616 k = parse_address(argv[optind], &family, &a, &ifindex);
617 if (k >= 0)
618 k = resolve_address(bus, family, &a, ifindex);
619 else
620 k = resolve_host(bus, argv[optind]);
621 }
622
623 if (r == 0)
624 r = k;
625
626 optind++;
627 }
628
629 finish:
630 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
631 }