]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve-host/resolve-host.c
resolved: internalize string buffer of dns_resource_record_to_string()
[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
bdef7319 22#include <getopt.h>
cf0fbc49 23#include <net/if.h>
bdef7319
ZJS
24
25#include "sd-bus.h"
3f6fd1ba
LP
26
27#include "af-list.h"
b5efdb8a 28#include "alloc-util.h"
bdef7319 29#include "bus-error.h"
3f6fd1ba 30#include "bus-util.h"
45ec7efb 31#include "escape.h"
bdef7319 32#include "in-addr-util.h"
6bedfcbb 33#include "parse-util.h"
51323288 34#include "resolved-def.h"
3f6fd1ba 35#include "resolved-dns-packet.h"
2d4c5cbc 36
bdef7319
ZJS
37#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
38
39static int arg_family = AF_UNSPEC;
40static int arg_ifindex = 0;
4b548ef3 41static uint16_t arg_type = 0;
2d4c5cbc 42static uint16_t arg_class = 0;
b93312f5 43static bool arg_legend = true;
51323288 44static uint64_t arg_flags = 0;
45ec7efb 45static bool arg_resolve_service = false;
51323288 46
78c6a153 47static void print_source(uint64_t flags, usec_t rtt) {
74998408 48 char rtt_str[FORMAT_TIMESTAMP_MAX];
51323288
LP
49
50 if (!arg_legend)
51 return;
52
78c6a153 53 if (flags == 0)
51323288
LP
54 return;
55
56 fputs("\n-- Information acquired via", stdout);
57
58 if (flags != 0)
59 printf(" protocol%s%s%s",
60 flags & SD_RESOLVED_DNS ? " DNS" :"",
61 flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
62 flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "");
63
74998408
TG
64 assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
65
66 printf(" in %s", rtt_str);
67
51323288
LP
68 fputc('.', stdout);
69 fputc('\n', stdout);
931851e8
LP
70
71 printf("-- Data is authenticated: %s\n", yes_no(flags & SD_RESOLVED_AUTHENTICATED));
51323288 72}
bdef7319 73
79266746 74static int resolve_host(sd_bus *bus, const char *name) {
bdef7319 75
4afd3348
LP
76 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
77 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
79266746 78 const char *canonical = NULL;
78c6a153 79 char ifname[IF_NAMESIZE] = "";
bdef7319 80 unsigned c = 0;
78c6a153 81 int r;
51323288 82 uint64_t flags;
74998408 83 usec_t ts;
bdef7319
ZJS
84
85 assert(name);
86
78c6a153
LP
87 if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
88 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
89
90 log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
bdef7319
ZJS
91
92 r = sd_bus_message_new_method_call(
93 bus,
94 &req,
95 "org.freedesktop.resolve1",
96 "/org/freedesktop/resolve1",
97 "org.freedesktop.resolve1.Manager",
98 "ResolveHostname");
02dd6e18
LP
99 if (r < 0)
100 return bus_log_create_error(r);
bdef7319 101
51323288 102 r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
02dd6e18
LP
103 if (r < 0)
104 return bus_log_create_error(r);
bdef7319 105
74998408
TG
106 ts = now(CLOCK_MONOTONIC);
107
bdef7319 108 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
45ec7efb
LP
109 if (r < 0)
110 return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r));
bdef7319 111
74998408
TG
112 ts = now(CLOCK_MONOTONIC) - ts;
113
78c6a153 114 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
02dd6e18
LP
115 if (r < 0)
116 return bus_log_parse_error(r);
bdef7319 117
78c6a153 118 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
bdef7319 119 _cleanup_free_ char *pretty = NULL;
78c6a153 120 int ifindex, family;
45ec7efb
LP
121 const void *a;
122 size_t sz;
78c6a153
LP
123
124 assert_cc(sizeof(int) == sizeof(int32_t));
bdef7319 125
78c6a153 126 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
02dd6e18
LP
127 if (r < 0)
128 return bus_log_parse_error(r);
bdef7319
ZJS
129
130 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
02dd6e18
LP
131 if (r < 0)
132 return bus_log_parse_error(r);
bdef7319 133
bdef7319 134 r = sd_bus_message_exit_container(reply);
02dd6e18
LP
135 if (r < 0)
136 return bus_log_parse_error(r);
bdef7319 137
79266746 138 if (!IN_SET(family, AF_INET, AF_INET6)) {
be636413 139 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
bdef7319
ZJS
140 continue;
141 }
142
143 if (sz != FAMILY_ADDRESS_SIZE(family)) {
78c6a153 144 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
45ec7efb 145 return -EINVAL;
bdef7319
ZJS
146 }
147
78c6a153
LP
148 ifname[0] = 0;
149 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
150 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
bdef7319 151
bdef7319 152 r = in_addr_to_string(family, a, &pretty);
78c6a153
LP
153 if (r < 0)
154 return log_error_errno(r, "Failed to print address for %s: %m", name);
bdef7319 155
79266746
LP
156 printf("%*s%s %s%s%s\n",
157 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
158 pretty,
159 isempty(ifname) ? "" : "%", ifname);
bdef7319
ZJS
160
161 c++;
162 }
79266746
LP
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
51323288 170 r = sd_bus_message_read(reply, "st", &canonical, &flags);
79266746
LP
171 if (r < 0)
172 return bus_log_parse_error(r);
173
ece174c5 174 if (!streq(name, canonical))
79266746
LP
175 printf("%*s%s (%s)\n",
176 (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
177 canonical);
bdef7319
ZJS
178
179 if (c == 0) {
180 log_error("%s: no addresses found", name);
79266746
LP
181 return -ESRCH;
182 }
183
78c6a153 184 print_source(flags, ts);
51323288 185
79266746
LP
186 return 0;
187}
188
189static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
4afd3348
LP
190 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
191 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
79266746
LP
192 _cleanup_free_ char *pretty = NULL;
193 char ifname[IF_NAMESIZE] = "";
51323288 194 uint64_t flags;
79266746 195 unsigned c = 0;
74998408 196 usec_t ts;
79266746
LP
197 int r;
198
199 assert(bus);
200 assert(IN_SET(family, AF_INET, AF_INET6));
201 assert(address);
202
78c6a153
LP
203 if (ifindex <= 0)
204 ifindex = arg_ifindex;
205
79266746
LP
206 r = in_addr_to_string(family, address, &pretty);
207 if (r < 0)
208 return log_oom();
209
78c6a153
LP
210 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
211 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
79266746
LP
212
213 log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
214
215 r = sd_bus_message_new_method_call(
216 bus,
217 &req,
218 "org.freedesktop.resolve1",
219 "/org/freedesktop/resolve1",
220 "org.freedesktop.resolve1.Manager",
221 "ResolveAddress");
222 if (r < 0)
223 return bus_log_create_error(r);
224
51323288 225 r = sd_bus_message_append(req, "ii", ifindex, family);
79266746
LP
226 if (r < 0)
227 return bus_log_create_error(r);
228
229 r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
230 if (r < 0)
231 return bus_log_create_error(r);
232
51323288 233 r = sd_bus_message_append(req, "t", arg_flags);
79266746
LP
234 if (r < 0)
235 return bus_log_create_error(r);
236
74998408
TG
237 ts = now(CLOCK_MONOTONIC);
238
79266746
LP
239 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
240 if (r < 0) {
241 log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
242 return r;
243 }
244
74998408
TG
245 ts = now(CLOCK_MONOTONIC) - ts;
246
78c6a153 247 r = sd_bus_message_enter_container(reply, 'a', "(is)");
79266746
LP
248 if (r < 0)
249 return bus_log_create_error(r);
250
78c6a153
LP
251 while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) {
252 const char *n;
253
254 assert_cc(sizeof(int) == sizeof(int32_t));
79266746 255
78c6a153
LP
256 r = sd_bus_message_read(reply, "is", &ifindex, &n);
257 if (r < 0)
258 return r;
259
260 r = sd_bus_message_exit_container(reply);
261 if (r < 0)
262 return r;
263
264 ifname[0] = 0;
265 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
266 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
267
268 printf("%*s%*s%*s%s %s\n",
79266746 269 (int) strlen(pretty), c == 0 ? pretty : "",
78c6a153
LP
270 isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%",
271 (int) strlen(ifname), c == 0 ? ifname : "",
79266746
LP
272 c == 0 ? ":" : " ",
273 n);
274
275 c++;
bdef7319 276 }
79266746
LP
277 if (r < 0)
278 return bus_log_parse_error(r);
bdef7319 279
02dd6e18
LP
280 r = sd_bus_message_exit_container(reply);
281 if (r < 0)
282 return bus_log_parse_error(r);
283
51323288
LP
284 r = sd_bus_message_read(reply, "t", &flags);
285 if (r < 0)
286 return bus_log_parse_error(r);
287
79266746
LP
288 if (c == 0) {
289 log_error("%s: no names found", pretty);
290 return -ESRCH;
291 }
292
78c6a153 293 print_source(flags, ts);
51323288 294
79266746
LP
295 return 0;
296}
297
298static int parse_address(const char *s, int *family, union in_addr_union *address, int *ifindex) {
299 const char *percent, *a;
300 int ifi = 0;
301 int r;
302
303 percent = strchr(s, '%');
304 if (percent) {
6ad623a3 305 if (parse_ifindex(percent+1, &ifi) < 0) {
79266746
LP
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;
02dd6e18 320 return 0;
bdef7319
ZJS
321}
322
2d4c5cbc
LP
323static int resolve_record(sd_bus *bus, const char *name) {
324
4afd3348
LP
325 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
326 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
78c6a153 327 char ifname[IF_NAMESIZE] = "";
2d4c5cbc 328 unsigned n = 0;
51323288 329 uint64_t flags;
78c6a153 330 int r;
74998408 331 usec_t ts;
2d4c5cbc
LP
332
333 assert(name);
334
78c6a153
LP
335 if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
336 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
337
338 log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type), isempty(ifname) ? "*" : ifname);
2d4c5cbc
LP
339
340 r = sd_bus_message_new_method_call(
341 bus,
342 &req,
343 "org.freedesktop.resolve1",
344 "/org/freedesktop/resolve1",
345 "org.freedesktop.resolve1.Manager",
346 "ResolveRecord");
347 if (r < 0)
348 return bus_log_create_error(r);
349
51323288 350 r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
2d4c5cbc
LP
351 if (r < 0)
352 return bus_log_create_error(r);
353
74998408
TG
354 ts = now(CLOCK_MONOTONIC);
355
2d4c5cbc
LP
356 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
357 if (r < 0) {
358 log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
359 return r;
360 }
361
74998408
TG
362 ts = now(CLOCK_MONOTONIC) - ts;
363
78c6a153 364 r = sd_bus_message_enter_container(reply, 'a', "(iqqay)");
51323288
LP
365 if (r < 0)
366 return bus_log_parse_error(r);
367
78c6a153 368 while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
2d4c5cbc
LP
369 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
370 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
7b50eb2e 371 const char *s;
2d4c5cbc 372 uint16_t c, t;
78c6a153 373 int ifindex;
2d4c5cbc
LP
374 const void *d;
375 size_t l;
376
78c6a153
LP
377 assert_cc(sizeof(int) == sizeof(int32_t));
378
379 r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t);
2d4c5cbc
LP
380 if (r < 0)
381 return bus_log_parse_error(r);
382
383 r = sd_bus_message_read_array(reply, 'y', &d, &l);
384 if (r < 0)
385 return bus_log_parse_error(r);
386
387 r = sd_bus_message_exit_container(reply);
388 if (r < 0)
389 return bus_log_parse_error(r);
390
391 r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
392 if (r < 0)
393 return log_oom();
394
f6a5fec6
LP
395 p->refuse_compression = true;
396
2d4c5cbc
LP
397 r = dns_packet_append_blob(p, d, l, NULL);
398 if (r < 0)
399 return log_oom();
400
d2579eec 401 r = dns_packet_read_rr(p, &rr, NULL, NULL);
40667ebe
LP
402 if (r < 0)
403 return log_error_errno(r, "Failed to parse RR.");
2d4c5cbc 404
7b50eb2e
LP
405 s = dns_resource_record_to_string(rr);
406 if (!s) {
2d4c5cbc 407 log_error("Failed to format RR.");
7b50eb2e 408 return -ENOMEM;
2d4c5cbc
LP
409 }
410
78c6a153
LP
411 ifname[0] = 0;
412 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
413 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
414
415 printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
2d4c5cbc
LP
416 n++;
417 }
418 if (r < 0)
419 return bus_log_parse_error(r);
420
421 r = sd_bus_message_exit_container(reply);
422 if (r < 0)
423 return bus_log_parse_error(r);
424
51323288
LP
425 r = sd_bus_message_read(reply, "t", &flags);
426 if (r < 0)
427 return bus_log_parse_error(r);
428
2d4c5cbc
LP
429 if (n == 0) {
430 log_error("%s: no records found", name);
431 return -ESRCH;
432 }
433
78c6a153 434 print_source(flags, ts);
51323288 435
2d4c5cbc
LP
436 return 0;
437}
438
45ec7efb
LP
439static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
440 const char *canonical_name, *canonical_type, *canonical_domain;
4afd3348
LP
441 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
442 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
45ec7efb
LP
443 char ifname[IF_NAMESIZE] = "";
444 size_t indent, sz;
445 uint64_t flags;
446 const char *p;
447 unsigned c;
448 usec_t ts;
449 int r;
450
451 assert(bus);
452 assert(domain);
453
454 if (isempty(name))
455 name = NULL;
456 if (isempty(type))
457 type = NULL;
458
459 if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
460 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
461
462 if (name)
463 log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
464 else if (type)
465 log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
466 else
467 log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
468
469 r = sd_bus_message_new_method_call(
470 bus,
471 &req,
472 "org.freedesktop.resolve1",
473 "/org/freedesktop/resolve1",
474 "org.freedesktop.resolve1.Manager",
475 "ResolveService");
476 if (r < 0)
477 return bus_log_create_error(r);
478
479 r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags);
480 if (r < 0)
481 return bus_log_create_error(r);
482
483 ts = now(CLOCK_MONOTONIC);
484
485 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
486 if (r < 0)
487 return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
488
489 ts = now(CLOCK_MONOTONIC) - ts;
490
491 r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)");
492 if (r < 0)
493 return bus_log_parse_error(r);
494
495 indent =
496 (name ? strlen(name) + 1 : 0) +
497 (type ? strlen(type) + 1 : 0) +
498 strlen(domain) + 2;
499
500 c = 0;
501 while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) {
502 uint16_t priority, weight, port;
503 const char *hostname, *canonical;
504
505 r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname);
506 if (r < 0)
507 return bus_log_parse_error(r);
508
509 if (name)
510 printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " ");
511 if (type)
512 printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " ");
513
514 printf("%*s%s %s:%u [priority=%u, weight=%u]\n",
515 (int) strlen(domain), c == 0 ? domain : "",
516 c == 0 ? ":" : " ",
517 hostname, port,
518 priority, weight);
519
520 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
521 if (r < 0)
522 return bus_log_parse_error(r);
523
524 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
525 _cleanup_free_ char *pretty = NULL;
526 int ifindex, family;
527 const void *a;
528
529 assert_cc(sizeof(int) == sizeof(int32_t));
530
531 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
532 if (r < 0)
533 return bus_log_parse_error(r);
534
535 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
536 if (r < 0)
537 return bus_log_parse_error(r);
538
539 r = sd_bus_message_exit_container(reply);
540 if (r < 0)
541 return bus_log_parse_error(r);
542
543 if (!IN_SET(family, AF_INET, AF_INET6)) {
544 log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
545 continue;
546 }
547
548 if (sz != FAMILY_ADDRESS_SIZE(family)) {
549 log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
550 return -EINVAL;
551 }
552
553 ifname[0] = 0;
554 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
555 log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
556
557 r = in_addr_to_string(family, a, &pretty);
558 if (r < 0)
559 return log_error_errno(r, "Failed to print address for %s: %m", name);
560
561 printf("%*s%s%s%s\n", (int) indent, "", pretty, isempty(ifname) ? "" : "%s", ifname);
562 }
563 if (r < 0)
564 return bus_log_parse_error(r);
565
566 r = sd_bus_message_exit_container(reply);
567 if (r < 0)
568 return bus_log_parse_error(r);
569
570 r = sd_bus_message_read(reply, "s", &canonical);
571 if (r < 0)
572 return bus_log_parse_error(r);
573
574 if (!streq(hostname, canonical))
575 printf("%*s(%s)\n", (int) indent, "", canonical);
576
577 r = sd_bus_message_exit_container(reply);
578 if (r < 0)
579 return bus_log_parse_error(r);
580
581 c++;
582 }
583 if (r < 0)
584 return bus_log_parse_error(r);
585
586 r = sd_bus_message_exit_container(reply);
587 if (r < 0)
588 return bus_log_parse_error(r);
589
590 r = sd_bus_message_enter_container(reply, 'a', "ay");
591 if (r < 0)
592 return bus_log_parse_error(r);
593
594 c = 0;
595 while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) {
596 _cleanup_free_ char *escaped = NULL;
597
598 escaped = cescape_length(p, sz);
599 if (!escaped)
600 return log_oom();
601
602 printf("%*s%s\n", (int) indent, "", escaped);
603 c++;
604 }
605 if (r < 0)
606 return bus_log_parse_error(r);
607
608 r = sd_bus_message_exit_container(reply);
609 if (r < 0)
610 return bus_log_parse_error(r);
611
612 r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags);
613 if (r < 0)
614 return bus_log_parse_error(r);
615
616 if (isempty(canonical_name))
617 canonical_name = NULL;
618 if (isempty(canonical_type))
619 canonical_type = NULL;
620
621 if (!streq_ptr(name, canonical_name) ||
622 !streq_ptr(type, canonical_type) ||
623 !streq_ptr(domain, canonical_domain)) {
624
625 printf("%*s(", (int) indent, "");
626
627 if (canonical_name)
628 printf("%s/", canonical_name);
629 if (canonical_type)
630 printf("%s/", canonical_type);
631
632 printf("%s)\n", canonical_domain);
633 }
634
635 print_source(flags, ts);
636
637 return 0;
638}
639
b93312f5
ZJS
640static void help_dns_types(void) {
641 int i;
642 const char *t;
643
644 if (arg_legend)
09b1fe14 645 puts("Known DNS RR types:");
b93312f5
ZJS
646 for (i = 0; i < _DNS_TYPE_MAX; i++) {
647 t = dns_type_to_string(i);
648 if (t)
649 puts(t);
650 }
651}
652
653static void help_dns_classes(void) {
654 int i;
655 const char *t;
656
657 if (arg_legend)
09b1fe14 658 puts("Known DNS RR classes:");
b93312f5
ZJS
659 for (i = 0; i < _DNS_CLASS_MAX; i++) {
660 t = dns_class_to_string(i);
661 if (t)
662 puts(t);
663 }
664}
665
bdef7319 666static void help(void) {
45ec7efb
LP
667 printf("%s [OPTIONS...] NAME...\n"
668 "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
669 "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n"
670 " -h --help Show this help\n"
671 " --version Show package version\n"
672 " -4 Resolve IPv4 addresses\n"
673 " -6 Resolve IPv6 addresses\n"
674 " -i INTERFACE Look on interface\n"
675 " -p --protocol=PROTOCOL Look via protocol\n"
676 " -t --type=TYPE Query RR with DNS type\n"
677 " -c --class=CLASS Query RR with DNS class\n"
678 " --service Resolve service (SRV)\n"
679 " --service-address=BOOL Do [not] resolve address for services\n"
680 " --service-txt=BOOL Do [not] resolve TXT records for services\n"
681 " --cname=BOOL Do [not] follow CNAME redirects\n"
801ad6a6 682 " --search=BOOL Do [not] use search domains\n"
45ec7efb
LP
683 " --legend=BOOL Do [not] print column headers\n"
684 , program_invocation_short_name, program_invocation_short_name);
bdef7319
ZJS
685}
686
687static int parse_argv(int argc, char *argv[]) {
688 enum {
689 ARG_VERSION = 0x100,
dad29dff 690 ARG_LEGEND,
45ec7efb
LP
691 ARG_SERVICE,
692 ARG_CNAME,
693 ARG_SERVICE_ADDRESS,
694 ARG_SERVICE_TXT,
801ad6a6 695 ARG_SEARCH,
bdef7319
ZJS
696 };
697
698 static const struct option options[] = {
45ec7efb
LP
699 { "help", no_argument, NULL, 'h' },
700 { "version", no_argument, NULL, ARG_VERSION },
701 { "type", required_argument, NULL, 't' },
702 { "class", required_argument, NULL, 'c' },
703 { "legend", required_argument, NULL, ARG_LEGEND },
704 { "protocol", required_argument, NULL, 'p' },
705 { "cname", required_argument, NULL, ARG_CNAME },
706 { "service", no_argument, NULL, ARG_SERVICE },
707 { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
708 { "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
801ad6a6 709 { "search", required_argument, NULL, ARG_SEARCH },
bdef7319
ZJS
710 {}
711 };
712
2d4c5cbc 713 int c, r;
bdef7319
ZJS
714
715 assert(argc >= 0);
716 assert(argv);
717
51323288 718 while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
bdef7319
ZJS
719 switch(c) {
720
721 case 'h':
722 help();
723 return 0; /* done */;
724
725 case ARG_VERSION:
3f6fd1ba 726 return version();
bdef7319
ZJS
727
728 case '4':
729 arg_family = AF_INET;
730 break;
731
732 case '6':
733 arg_family = AF_INET6;
734 break;
735
3fa4999b
LP
736 case 'i': {
737 int ifi;
738
6ad623a3 739 if (parse_ifindex(optarg, &ifi) >= 0)
3fa4999b
LP
740 arg_ifindex = ifi;
741 else {
742 ifi = if_nametoindex(optarg);
743 if (ifi <= 0)
744 return log_error_errno(errno, "Unknown interface %s: %m", optarg);
745
746 arg_ifindex = ifi;
747 }
748
2d4c5cbc 749 break;
3fa4999b 750 }
2d4c5cbc
LP
751
752 case 't':
b93312f5
ZJS
753 if (streq(optarg, "help")) {
754 help_dns_types();
755 return 0;
756 }
757
4b548ef3
LP
758 r = dns_type_from_string(optarg);
759 if (r < 0) {
2d4c5cbc 760 log_error("Failed to parse RR record type %s", optarg);
4b548ef3 761 return r;
2d4c5cbc 762 }
4b548ef3
LP
763 arg_type = (uint16_t) r;
764 assert((int) arg_type == r);
b93312f5 765
2d4c5cbc
LP
766 break;
767
768 case 'c':
b93312f5
ZJS
769 if (streq(optarg, "help")) {
770 help_dns_classes();
771 return 0;
772 }
773
4b548ef3 774 r = dns_class_from_string(optarg);
2d4c5cbc
LP
775 if (r < 0) {
776 log_error("Failed to parse RR record class %s", optarg);
777 return r;
bdef7319 778 }
4b548ef3
LP
779 arg_class = (uint16_t) r;
780 assert((int) arg_class == r);
b93312f5
ZJS
781
782 break;
783
dad29dff 784 case ARG_LEGEND:
45ec7efb
LP
785 r = parse_boolean(optarg);
786 if (r < 0)
787 return log_error_errno(r, "Failed to parse --legend= argument");
788
789 arg_legend = r;
bdef7319
ZJS
790 break;
791
51323288
LP
792 case 'p':
793 if (streq(optarg, "dns"))
794 arg_flags |= SD_RESOLVED_DNS;
795 else if (streq(optarg, "llmnr"))
796 arg_flags |= SD_RESOLVED_LLMNR;
797 else if (streq(optarg, "llmnr-ipv4"))
798 arg_flags |= SD_RESOLVED_LLMNR_IPV4;
799 else if (streq(optarg, "llmnr-ipv6"))
800 arg_flags |= SD_RESOLVED_LLMNR_IPV6;
801 else {
802 log_error("Unknown protocol specifier: %s", optarg);
803 return -EINVAL;
804 }
805
806 break;
807
45ec7efb
LP
808 case ARG_SERVICE:
809 arg_resolve_service = true;
810 break;
811
812 case ARG_CNAME:
813 r = parse_boolean(optarg);
814 if (r < 0)
815 return log_error_errno(r, "Failed to parse --cname= argument.");
816 if (r == 0)
817 arg_flags |= SD_RESOLVED_NO_CNAME;
818 else
819 arg_flags &= ~SD_RESOLVED_NO_CNAME;
820 break;
821
822 case ARG_SERVICE_ADDRESS:
823 r = parse_boolean(optarg);
824 if (r < 0)
825 return log_error_errno(r, "Failed to parse --service-address= argument.");
826 if (r == 0)
827 arg_flags |= SD_RESOLVED_NO_ADDRESS;
828 else
829 arg_flags &= ~SD_RESOLVED_NO_ADDRESS;
830 break;
831
832 case ARG_SERVICE_TXT:
833 r = parse_boolean(optarg);
834 if (r < 0)
835 return log_error_errno(r, "Failed to parse --service-txt= argument.");
836 if (r == 0)
837 arg_flags |= SD_RESOLVED_NO_TXT;
838 else
839 arg_flags &= ~SD_RESOLVED_NO_TXT;
840 break;
841
801ad6a6
LP
842 case ARG_SEARCH:
843 r = parse_boolean(optarg);
844 if (r < 0)
845 return log_error_errno(r, "Failed to parse --search argument.");
846 if (r == 0)
847 arg_flags |= SD_RESOLVED_NO_SEARCH;
848 else
849 arg_flags &= ~SD_RESOLVED_NO_SEARCH;
850 break;
851
bdef7319
ZJS
852 case '?':
853 return -EINVAL;
854
855 default:
856 assert_not_reached("Unhandled option");
857 }
858
2d4c5cbc 859 if (arg_type == 0 && arg_class != 0) {
45ec7efb
LP
860 log_error("--class= may only be used in conjunction with --type=.");
861 return -EINVAL;
862 }
863
864 if (arg_type != 0 && arg_resolve_service) {
865 log_error("--service and --type= may not be combined.");
2d4c5cbc
LP
866 return -EINVAL;
867 }
868
869 if (arg_type != 0 && arg_class == 0)
870 arg_class = DNS_CLASS_IN;
871
bdef7319
ZJS
872 return 1 /* work to do */;
873}
874
bdef7319 875int main(int argc, char **argv) {
4afd3348 876 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
bdef7319
ZJS
877 int r;
878
879 log_parse_environment();
880 log_open();
881
882 r = parse_argv(argc, argv);
883 if (r <= 0)
79266746
LP
884 goto finish;
885
886 if (optind >= argc) {
887 log_error("No arguments passed");
888 r = -EINVAL;
889 goto finish;
890 }
bdef7319
ZJS
891
892 r = sd_bus_open_system(&bus);
893 if (r < 0) {
da927ba9 894 log_error_errno(r, "sd_bus_open_system: %m");
79266746 895 goto finish;
bdef7319
ZJS
896 }
897
45ec7efb
LP
898 if (arg_resolve_service) {
899
900 if (argc < optind + 1) {
901 log_error("Domain specification required.");
902 r = -EINVAL;
903 goto finish;
904
905 } else if (argc == optind + 1)
906 r = resolve_service(bus, NULL, NULL, argv[optind]);
907 else if (argc == optind + 2)
908 r = resolve_service(bus, NULL, argv[optind], argv[optind+1]);
909 else if (argc == optind + 3)
910 r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
911 else {
912 log_error("Too many arguments");
913 r = -EINVAL;
914 goto finish;
915 }
916
917 goto finish;
918 }
919
bdef7319 920 while (argv[optind]) {
79266746
LP
921 int family, ifindex, k;
922 union in_addr_union a;
923
2d4c5cbc
LP
924 if (arg_type != 0)
925 k = resolve_record(bus, argv[optind]);
926 else {
927 k = parse_address(argv[optind], &family, &a, &ifindex);
928 if (k >= 0)
929 k = resolve_address(bus, family, &a, ifindex);
930 else
931 k = resolve_host(bus, argv[optind]);
932 }
bdef7319 933
bdef7319
ZJS
934 if (r == 0)
935 r = k;
79266746
LP
936
937 optind++;
bdef7319
ZJS
938 }
939
79266746 940finish:
bdef7319
ZJS
941 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
942}