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