]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve-host/resolve-host.c
Unify parse_argv style
[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 <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
36 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
37
38 static int arg_family = AF_UNSPEC;
39 static int arg_ifindex = 0;
40 static uint16_t arg_type = 0;
41 static uint16_t arg_class = 0;
42 static bool arg_legend = true;
43
44 static int resolve_host(sd_bus *bus, const char *name) {
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;
48 const char *canonical = NULL;
49 unsigned c = 0;
50 int r;
51
52 assert(name);
53
54 log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
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");
63 if (r < 0)
64 return bus_log_create_error(r);
65
66 r = sd_bus_message_set_auto_start(req, false);
67 if (r < 0)
68 return bus_log_create_error(r);
69
70 r = sd_bus_message_append(req, "si", name, arg_family);
71 if (r < 0)
72 return bus_log_create_error(r);
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)");
81 if (r < 0)
82 return bus_log_parse_error(r);
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);
92 if (r < 0)
93 return bus_log_parse_error(r);
94
95 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
96 if (r < 0)
97 return bus_log_parse_error(r);
98
99 r = sd_bus_message_read(reply, "i", &ifindex);
100 if (r < 0)
101 return bus_log_parse_error(r);
102
103 r = sd_bus_message_exit_container(reply);
104 if (r < 0)
105 return bus_log_parse_error(r);
106
107 if (!IN_SET(family, AF_INET, AF_INET6)) {
108 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
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
134 if (arg_ifindex > 0 && ifindex > 0 && ifindex != arg_ifindex) {
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
146 printf("%*s%s %s%s%s\n",
147 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
148 pretty,
149 isempty(ifname) ? "" : "%", ifname);
150
151 c++;
152 }
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 }
169
170 if (c == 0) {
171 log_error("%s: no addresses found", name);
172 return -ESRCH;
173 }
174
175 return 0;
176 }
177
178 static 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++;
252 }
253 if (r < 0)
254 return bus_log_parse_error(r);
255
256 r = sd_bus_message_exit_container(reply);
257 if (r < 0)
258 return bus_log_parse_error(r);
259
260 if (c == 0) {
261 log_error("%s: no names found", pretty);
262 return -ESRCH;
263 }
264
265 return 0;
266 }
267
268 static 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;
291 return 0;
292 }
293
294 static 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
319 r = sd_bus_message_append(req, "sqq", name, arg_class, arg_type);
320 if (r < 0)
321 return bus_log_create_error(r);
322
323 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
324 if (r < 0) {
325 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
326 return r;
327 }
328
329 r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
330 if (r < 0)
331 return bus_log_parse_error(r);
332
333 while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
334 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
335 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
336 _cleanup_free_ char *s = NULL;
337 uint16_t c, t;
338 const void *d;
339 size_t l;
340
341 r = sd_bus_message_read(reply, "qq", &c, &t);
342 if (r < 0)
343 return bus_log_parse_error(r);
344
345 r = sd_bus_message_read_array(reply, 'y', &d, &l);
346 if (r < 0)
347 return bus_log_parse_error(r);
348
349 r = sd_bus_message_exit_container(reply);
350 if (r < 0)
351 return bus_log_parse_error(r);
352
353 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
354 if (r < 0)
355 return log_oom();
356
357 r = dns_packet_append_blob(p, d, l, NULL);
358 if (r < 0)
359 return log_oom();
360
361 r = dns_packet_read_rr(p, &rr, NULL);
362 if (r < 0) {
363 log_error("Failed to parse RR.");
364 return r;
365 }
366
367 r = dns_resource_record_to_string(rr, &s);
368 if (r < 0) {
369 log_error("Failed to format RR.");
370 return r;
371 }
372
373 printf("%s\n", s);
374 n++;
375 }
376 if (r < 0)
377 return bus_log_parse_error(r);
378
379 r = sd_bus_message_exit_container(reply);
380 if (r < 0)
381 return bus_log_parse_error(r);
382
383 if (n == 0) {
384 log_error("%s: no records found", name);
385 return -ESRCH;
386 }
387
388 return 0;
389 }
390
391 static void help_dns_types(void) {
392 int i;
393 const char *t;
394
395 if (arg_legend)
396 puts("Known dns types:");
397 for (i = 0; i < _DNS_TYPE_MAX; i++) {
398 t = dns_type_to_string(i);
399 if (t)
400 puts(t);
401 }
402 }
403
404 static void help_dns_classes(void) {
405 int i;
406 const char *t;
407
408 if (arg_legend)
409 puts("Known dns classes:");
410 for (i = 0; i < _DNS_CLASS_MAX; i++) {
411 t = dns_class_to_string(i);
412 if (t)
413 puts(t);
414 }
415 }
416
417 static void help(void) {
418 printf("%s [OPTIONS...]\n\n"
419 "Resolve IPv4 or IPv6 addresses.\n\n"
420 " -h --help Show this help\n"
421 " --version Show package version\n"
422 " -4 Resolve IPv4 addresses\n"
423 " -6 Resolve IPv6 addresses\n"
424 " -i INTERFACE Filter by interface\n"
425 " -t --type=TYPE Query RR with DNS type\n"
426 " -c --class=CLASS Query RR with DNS class\n"
427 " --no-legend Do not print column headers\n"
428 , program_invocation_short_name);
429 }
430
431 static int parse_argv(int argc, char *argv[]) {
432 enum {
433 ARG_VERSION = 0x100,
434 ARG_NO_LEGEND,
435 };
436
437 static const struct option options[] = {
438 { "help", no_argument, NULL, 'h' },
439 { "version", no_argument, NULL, ARG_VERSION },
440 { "type", no_argument, NULL, 't' },
441 { "class", no_argument, NULL, 'c' },
442 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
443 {}
444 };
445
446 int c, r;
447
448 assert(argc >= 0);
449 assert(argv);
450
451 while ((c = getopt_long(argc, argv, "h46i:t:c:", options, NULL)) >= 0)
452 switch(c) {
453
454 case 'h':
455 help();
456 return 0; /* done */;
457
458 case ARG_VERSION:
459 puts(PACKAGE_STRING);
460 puts(SYSTEMD_FEATURES);
461 return 0 /* done */;
462
463 case '4':
464 arg_family = AF_INET;
465 break;
466
467 case '6':
468 arg_family = AF_INET6;
469 break;
470
471 case 'i':
472 arg_ifindex = if_nametoindex(optarg);
473 if (arg_ifindex <= 0) {
474 log_error("Unknown interfaces %s: %m", optarg);
475 return -errno;
476 }
477 break;
478
479 case 't':
480 if (streq(optarg, "help")) {
481 help_dns_types();
482 return 0;
483 }
484
485 r = dns_type_from_string(optarg, &arg_type);
486 if (r < 0) {
487 log_error("Failed to parse RR record type %s", optarg);
488 return r;
489 }
490
491 break;
492
493 case 'c':
494 if (streq(optarg, "help")) {
495 help_dns_classes();
496 return 0;
497 }
498
499 r = dns_class_from_string(optarg, &arg_class);
500 if (r < 0) {
501 log_error("Failed to parse RR record class %s", optarg);
502 return r;
503 }
504
505 break;
506
507 case ARG_NO_LEGEND:
508 arg_legend = false;
509 break;
510
511 case '?':
512 return -EINVAL;
513
514 default:
515 assert_not_reached("Unhandled option");
516 }
517
518 if (arg_type == 0 && arg_class != 0) {
519 log_error("--class= may only be used in conjunction with --type=");
520 return -EINVAL;
521 }
522
523 if (arg_type != 0 && arg_class == 0)
524 arg_class = DNS_CLASS_IN;
525
526 return 1 /* work to do */;
527 }
528
529 int main(int argc, char **argv) {
530 _cleanup_bus_unref_ sd_bus *bus = NULL;
531 int r;
532
533 log_parse_environment();
534 log_open();
535
536 r = parse_argv(argc, argv);
537 if (r <= 0)
538 goto finish;
539
540 if (optind >= argc) {
541 log_error("No arguments passed");
542 r = -EINVAL;
543 goto finish;
544 }
545
546 r = sd_bus_open_system(&bus);
547 if (r < 0) {
548 log_error("sd_bus_open_system: %s", strerror(-r));
549 goto finish;
550 }
551
552 while (argv[optind]) {
553 int family, ifindex, k;
554 union in_addr_union a;
555
556 if (arg_type != 0)
557 k = resolve_record(bus, argv[optind]);
558 else {
559 k = parse_address(argv[optind], &family, &a, &ifindex);
560 if (k >= 0)
561 k = resolve_address(bus, family, &a, ifindex);
562 else
563 k = resolve_host(bus, argv[optind]);
564 }
565
566 if (r == 0)
567 r = k;
568
569 optind++;
570 }
571
572 finish:
573 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
574 }