chiark / gitweb /
treewide: auto-convert the simple cases to log_*_errno()
[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 <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
34 #include "resolved-dns-packet.h"
35 #include "resolved-def.h"
36
37 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
38
39 static int arg_family = AF_UNSPEC;
40 static int arg_ifindex = 0;
41 static int arg_type = 0;
42 static uint16_t arg_class = 0;
43 static bool arg_legend = true;
44 static uint64_t arg_flags = 0;
45
46 static 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 }
70
71 static int resolve_host(sd_bus *bus, const char *name) {
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;
75         const char *canonical = NULL;
76         unsigned c = 0;
77         int r, ifindex;
78         uint64_t flags;
79
80         assert(name);
81
82         log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
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");
91         if (r < 0)
92                 return bus_log_create_error(r);
93
94         r = sd_bus_message_set_auto_start(req, false);
95         if (r < 0)
96                 return bus_log_create_error(r);
97
98         r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
99         if (r < 0)
100                 return bus_log_create_error(r);
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
108         r = sd_bus_message_read(reply, "i", &ifindex);
109         if (r < 0)
110                 return bus_log_parse_error(r);
111
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) {
117                 const void *a;
118                 int family;
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);
124                 if (r < 0)
125                         return bus_log_parse_error(r);
126
127                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
128                 if (r < 0)
129                         return bus_log_parse_error(r);
130
131                 r = sd_bus_message_exit_container(reply);
132                 if (r < 0)
133                         return bus_log_parse_error(r);
134
135                 if (!IN_SET(family, AF_INET, AF_INET6)) {
136                         log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
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
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
156                 r = in_addr_to_string(family, a, &pretty);
157                 if (r < 0) {
158                         log_error_errno(-r, "%s: failed to print address: %m", name);
159                         continue;
160                 }
161
162                 printf("%*s%s %s%s%s\n",
163                        (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
164                        pretty,
165                        isempty(ifname) ? "" : "%", ifname);
166
167                 c++;
168         }
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
176         r = sd_bus_message_read(reply, "st", &canonical, &flags);
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         }
185
186         if (c == 0) {
187                 log_error("%s: no addresses found", name);
188                 return -ESRCH;
189         }
190
191         print_source(ifindex, flags);
192
193         return 0;
194 }
195
196 static 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] = "";
201         uint64_t flags;
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
240         r = sd_bus_message_append(req, "ii", ifindex, family);
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
248         r = sd_bus_message_append(req, "t", arg_flags);
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
258         r = sd_bus_message_read(reply, "i", &ifindex);
259         if (r < 0)
260                 return bus_log_parse_error(r);
261
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++;
275         }
276         if (r < 0)
277                 return bus_log_parse_error(r);
278
279         r = sd_bus_message_exit_container(reply);
280         if (r < 0)
281                 return bus_log_parse_error(r);
282
283         r = sd_bus_message_read(reply, "t", &flags);
284         if (r < 0)
285                 return bus_log_parse_error(r);
286
287         if (c == 0) {
288                 log_error("%s: no names found", pretty);
289                 return -ESRCH;
290         }
291
292         print_source(ifindex, flags);
293
294         return 0;
295 }
296
297 static 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;
320         return 0;
321 }
322
323 static 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;
328         uint64_t flags;
329         int r, ifindex;
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
349         assert((uint16_t) arg_type == arg_type);
350         r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
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
360         r = sd_bus_message_read(reply, "i", &ifindex);
361         if (r < 0)
362                 return bus_log_parse_error(r);
363
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
418         r = sd_bus_message_read(reply, "t", &flags);
419         if (r < 0)
420                 return bus_log_parse_error(r);
421
422         if (n == 0) {
423                 log_error("%s: no records found", name);
424                 return -ESRCH;
425         }
426
427         print_source(ifindex, flags);
428
429         return 0;
430 }
431
432 static 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
445 static 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
458 static void help(void) {
459         printf("%s [OPTIONS...]\n\n"
460                "Resolve IPv4 or IPv6 addresses.\n\n"
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                "     --legend[=BOOL]      Do [not] print column headers\n"
470                , program_invocation_short_name);
471 }
472
473 static int parse_argv(int argc, char *argv[]) {
474         enum {
475                 ARG_VERSION = 0x100,
476                 ARG_LEGEND,
477         };
478
479         static const struct option options[] = {
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                 { "legend", optional_argument,    NULL, ARG_LEGEND    },
485                 { "protocol",  required_argument, NULL, 'p'           },
486                 {}
487         };
488
489         int c, r;
490
491         assert(argc >= 0);
492         assert(argv);
493
494         while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
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);
518                                 return -errno;
519                         }
520                         break;
521
522                 case 't':
523                         if (streq(optarg, "help")) {
524                                 help_dns_types();
525                                 return 0;
526                         }
527
528                         arg_type = dns_type_from_string(optarg);
529                         if (arg_type < 0) {
530                                 log_error("Failed to parse RR record type %s", optarg);
531                                 return arg_type;
532                         }
533                         assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
534
535                         break;
536
537                 case 'c':
538                         if (streq(optarg, "help")) {
539                                 help_dns_classes();
540                                 return 0;
541                         }
542
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;
547                         }
548
549                         break;
550
551                 case ARG_LEGEND:
552                         if (optarg) {
553                                 r = parse_boolean(optarg);
554                                 if (r < 0) {
555                                         log_error("Failed to parse --legend= argument");
556                                         return r;
557                                 }
558
559                                 arg_legend = !!r;
560                         } else
561                                 arg_legend = false;
562                         break;
563
564                 case 'p':
565                         if (streq(optarg, "dns"))
566                                 arg_flags |= SD_RESOLVED_DNS;
567                         else if (streq(optarg, "llmnr"))
568                                 arg_flags |= SD_RESOLVED_LLMNR;
569                         else if (streq(optarg, "llmnr-ipv4"))
570                                 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
571                         else if (streq(optarg, "llmnr-ipv6"))
572                                 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
573                         else {
574                                 log_error("Unknown protocol specifier: %s", optarg);
575                                 return -EINVAL;
576                         }
577
578                         break;
579
580                 case '?':
581                         return -EINVAL;
582
583                 default:
584                         assert_not_reached("Unhandled option");
585                 }
586
587         if (arg_type == 0 && arg_class != 0) {
588                 log_error("--class= may only be used in conjunction with --type=");
589                 return -EINVAL;
590         }
591
592         if (arg_type != 0 && arg_class == 0)
593                 arg_class = DNS_CLASS_IN;
594
595         return 1 /* work to do */;
596 }
597
598 int main(int argc, char **argv) {
599         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
600         int r;
601
602         log_parse_environment();
603         log_open();
604
605         r = parse_argv(argc, argv);
606         if (r <= 0)
607                 goto finish;
608
609         if (optind >= argc) {
610                 log_error("No arguments passed");
611                 r = -EINVAL;
612                 goto finish;
613         }
614
615         r = sd_bus_open_system(&bus);
616         if (r < 0) {
617                 log_error_errno(-r, "sd_bus_open_system: %m");
618                 goto finish;
619         }
620
621         while (argv[optind]) {
622                 int family, ifindex, k;
623                 union in_addr_union a;
624
625                 if (arg_type != 0)
626                         k = resolve_record(bus, argv[optind]);
627                 else {
628                         k = parse_address(argv[optind], &family, &a, &ifindex);
629                         if (k >= 0)
630                                 k = resolve_address(bus, family, &a, ifindex);
631                         else
632                                 k = resolve_host(bus, argv[optind]);
633                 }
634
635                 if (r == 0)
636                         r = k;
637
638                 optind++;
639         }
640
641 finish:
642         return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
643 }