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