delv.c revision 1.1.1.12 1 /* $NetBSD: delv.c,v 1.1.1.12 2024/09/22 00:06:07 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16 #include <arpa/inet.h>
17 #include <bind.keys.h>
18 #include <inttypes.h>
19 #include <netdb.h>
20 #include <netinet/in.h>
21 #include <signal.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 #include <isc/app.h>
31 #include <isc/attributes.h>
32 #include <isc/base64.h>
33 #include <isc/buffer.h>
34 #include <isc/hex.h>
35 #include <isc/log.h>
36 #include <isc/managers.h>
37 #include <isc/md.h>
38 #include <isc/mem.h>
39 #include <isc/netmgr.h>
40 #include <isc/parseint.h>
41 #include <isc/print.h>
42 #include <isc/result.h>
43 #include <isc/sockaddr.h>
44 #include <isc/string.h>
45 #include <isc/task.h>
46 #include <isc/timer.h>
47 #include <isc/util.h>
48
49 #include <dns/byaddr.h>
50 #include <dns/client.h>
51 #include <dns/fixedname.h>
52 #include <dns/keytable.h>
53 #include <dns/keyvalues.h>
54 #include <dns/log.h>
55 #include <dns/masterdump.h>
56 #include <dns/name.h>
57 #include <dns/rdata.h>
58 #include <dns/rdataclass.h>
59 #include <dns/rdataset.h>
60 #include <dns/rdatastruct.h>
61 #include <dns/rdatatype.h>
62 #include <dns/secalg.h>
63 #include <dns/view.h>
64
65 #include <dst/dst.h>
66
67 #include <isccfg/log.h>
68 #include <isccfg/namedconf.h>
69
70 #include <irs/resconf.h>
71
72 #define CHECK(r) \
73 do { \
74 result = (r); \
75 if (result != ISC_R_SUCCESS) \
76 goto cleanup; \
77 } while (0)
78
79 #define MAXNAME (DNS_NAME_MAXTEXT + 1)
80
81 /*
82 * Default maximum number of chained queries before we give up
83 * to prevent CNAME loops.
84 */
85 #define MAX_RESTARTS 11
86
87 /* Variables used internally by delv. */
88 char *progname;
89 static isc_mem_t *mctx = NULL;
90 static isc_log_t *lctx = NULL;
91
92 /* Configurables */
93 static char *server = NULL;
94 static const char *port = "53";
95 static isc_sockaddr_t *srcaddr4 = NULL, *srcaddr6 = NULL;
96 static isc_sockaddr_t a4, a6;
97 static char *curqname = NULL, *qname = NULL;
98 static bool classset = false;
99 static dns_rdatatype_t qtype = dns_rdatatype_none;
100 static bool typeset = false;
101
102 static unsigned int styleflags = 0;
103 static uint32_t splitwidth = 0xffffffff;
104 static bool showcomments = true, showdnssec = true, showtrust = true,
105 rrcomments = true, noclass = false, nocrypto = false, nottl = false,
106 multiline = false, short_form = false, print_unknown_format = false,
107 yaml = false;
108
109 static bool resolve_trace = false, validator_trace = false,
110 message_trace = false;
111
112 static bool use_ipv4 = true, use_ipv6 = true;
113
114 static bool cdflag = false, no_sigs = false, root_validation = true;
115
116 static bool use_tcp = false;
117
118 static char *anchorfile = NULL;
119 static char *trust_anchor = NULL;
120 static int num_keys = 0;
121
122 static dns_fixedname_t afn;
123 static dns_name_t *anchor_name = NULL;
124
125 /* Default bind.keys contents */
126 static char anchortext[] = TRUST_ANCHORS;
127
128 /*
129 * Static function prototypes
130 */
131 static isc_result_t
132 get_reverse(char *reverse, size_t len, char *value, bool strict);
133
134 static isc_result_t
135 parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc);
136
137 static void
138 usage(void) {
139 fprintf(stderr,
140 "Usage: delv [@server] {q-opt} {d-opt} [domain] [q-type] "
141 "[q-class]\n"
142 "Where: domain is in the Domain Name System\n"
143 " q-class is one of (in,hs,ch,...) [default: in]\n"
144 " q-type is one of "
145 "(a,any,mx,ns,soa,hinfo,axfr,txt,...) "
146 "[default:a]\n"
147 " q-opt is one of:\n"
148 " -4 (use IPv4 query "
149 "transport "
150 "only)\n"
151 " -6 (use IPv6 query "
152 "transport "
153 "only)\n"
154 " -a anchor-file (specify root trust "
155 "anchor)\n"
156 " -b address[#port] (bind to source "
157 "address/port)\n"
158 " -c class (option included for "
159 "compatibility;\n"
160 " -d level (set debugging level)\n"
161 " -h (print help and exit)\n"
162 " -i (disable DNSSEC "
163 "validation)\n"
164 " -m (enable memory usage "
165 "debugging)\n"
166 " -p port (specify port number)\n"
167 " -q name (specify query name)\n"
168 " -t type (specify query type)\n"
169 " only IN is supported)\n"
170 " -v (print version and "
171 "exit)\n"
172 " -x dot-notation (shortcut for reverse "
173 "lookups)\n"
174 " d-opt is of the form +keyword[=value], where "
175 "keyword "
176 "is:\n"
177 " +[no]all (Set or clear all "
178 "display "
179 "flags)\n"
180 " +[no]class (Control display of "
181 "class)\n"
182 " +[no]comments (Control display of "
183 "comment lines)\n"
184 " +[no]crypto (Control display of "
185 "cryptographic\n"
186 " fields in records)\n"
187 " +[no]dlv (Obsolete)\n"
188 " +[no]dnssec (Display DNSSEC "
189 "records)\n"
190 " +[no]mtrace (Trace messages "
191 "received)\n"
192 " +[no]multiline (Print records in an "
193 "expanded format)\n"
194 " +[no]root (DNSSEC validation trust "
195 "anchor)\n"
196 " +[no]rrcomments (Control display of "
197 "per-record "
198 "comments)\n"
199 " +[no]rtrace (Trace resolver "
200 "fetches)\n"
201 " +[no]short (Short form answer)\n"
202 " +[no]split=## (Split hex/base64 fields "
203 "into chunks)\n"
204 " +[no]tcp (TCP mode)\n"
205 " +[no]ttl (Control display of ttls "
206 "in records)\n"
207 " +[no]trust (Control display of "
208 "trust "
209 "level)\n"
210 " +[no]unknownformat (Print RDATA in RFC 3597 "
211 "\"unknown\" format)\n"
212 " +[no]vtrace (Trace validation "
213 "process)\n"
214 " +[no]yaml (Present the results as "
215 "YAML)\n");
216 exit(EXIT_FAILURE);
217 }
218
219 noreturn static void
220 fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
221
222 static void
223 fatal(const char *format, ...) {
224 va_list args;
225
226 fflush(stdout);
227 fprintf(stderr, "%s: ", progname);
228 va_start(args, format);
229 vfprintf(stderr, format, args);
230 va_end(args);
231 fprintf(stderr, "\n");
232 _exit(EXIT_FAILURE);
233 }
234
235 static void
236 warn(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
237
238 static void
239 warn(const char *format, ...) {
240 va_list args;
241
242 fflush(stdout);
243 fprintf(stderr, "%s: warning: ", progname);
244 va_start(args, format);
245 vfprintf(stderr, format, args);
246 va_end(args);
247 fprintf(stderr, "\n");
248 }
249
250 static isc_logcategory_t categories[] = { { "delv", 0 }, { NULL, 0 } };
251 #define LOGCATEGORY_DEFAULT (&categories[0])
252 #define LOGMODULE_DEFAULT (&modules[0])
253
254 static isc_logmodule_t modules[] = { { "delv", 0 }, { NULL, 0 } };
255
256 static void
257 delv_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
258
259 static void
260 delv_log(int level, const char *fmt, ...) {
261 va_list ap;
262 char msgbuf[2048];
263
264 if (!isc_log_wouldlog(lctx, level)) {
265 return;
266 }
267
268 va_start(ap, fmt);
269
270 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
271 isc_log_write(lctx, LOGCATEGORY_DEFAULT, LOGMODULE_DEFAULT, level, "%s",
272 msgbuf);
273 va_end(ap);
274 }
275
276 static int loglevel = 0;
277
278 static void
279 setup_logging(FILE *errout) {
280 isc_result_t result;
281 isc_logdestination_t destination;
282 isc_logconfig_t *logconfig = NULL;
283
284 isc_log_create(mctx, &lctx, &logconfig);
285 isc_log_registercategories(lctx, categories);
286 isc_log_registermodules(lctx, modules);
287 isc_log_setcontext(lctx);
288 dns_log_init(lctx);
289 dns_log_setcontext(lctx);
290 cfg_log_init(lctx);
291
292 destination.file.stream = errout;
293 destination.file.name = NULL;
294 destination.file.versions = ISC_LOG_ROLLNEVER;
295 destination.file.maximum_size = 0;
296 isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC,
297 ISC_LOG_DYNAMIC, &destination,
298 ISC_LOG_PRINTPREFIX);
299
300 isc_log_setdebuglevel(lctx, loglevel);
301 isc_log_settag(logconfig, ";; ");
302
303 result = isc_log_usechannel(logconfig, "stderr",
304 ISC_LOGCATEGORY_DEFAULT, NULL);
305 if (result != ISC_R_SUCCESS) {
306 fatal("Couldn't attach to log channel 'stderr'");
307 }
308
309 if (resolve_trace && loglevel < 1) {
310 isc_log_createchannel(logconfig, "resolver", ISC_LOG_TOFILEDESC,
311 ISC_LOG_DEBUG(1), &destination,
312 ISC_LOG_PRINTPREFIX);
313
314 result = isc_log_usechannel(logconfig, "resolver",
315 DNS_LOGCATEGORY_RESOLVER,
316 DNS_LOGMODULE_RESOLVER);
317 if (result != ISC_R_SUCCESS) {
318 fatal("Couldn't attach to log channel 'resolver'");
319 }
320 }
321
322 if (validator_trace && loglevel < 3) {
323 isc_log_createchannel(logconfig, "validator",
324 ISC_LOG_TOFILEDESC, ISC_LOG_DEBUG(3),
325 &destination, ISC_LOG_PRINTPREFIX);
326
327 result = isc_log_usechannel(logconfig, "validator",
328 DNS_LOGCATEGORY_DNSSEC,
329 DNS_LOGMODULE_VALIDATOR);
330 if (result != ISC_R_SUCCESS) {
331 fatal("Couldn't attach to log channel 'validator'");
332 }
333 }
334
335 if (message_trace && loglevel < 10) {
336 isc_log_createchannel(logconfig, "messages", ISC_LOG_TOFILEDESC,
337 ISC_LOG_DEBUG(10), &destination,
338 ISC_LOG_PRINTPREFIX);
339
340 result = isc_log_usechannel(logconfig, "messages",
341 DNS_LOGCATEGORY_RESOLVER,
342 DNS_LOGMODULE_PACKETS);
343 if (result != ISC_R_SUCCESS) {
344 fatal("Couldn't attach to log channel 'messagse'");
345 }
346 }
347 }
348
349 static void
350 print_status(dns_rdataset_t *rdataset) {
351 char buf[1024] = { 0 };
352
353 REQUIRE(rdataset != NULL);
354
355 if (!showtrust || !dns_rdataset_isassociated(rdataset)) {
356 return;
357 }
358
359 buf[0] = '\0';
360
361 if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
362 strlcat(buf, "negative response", sizeof(buf));
363 strlcat(buf, (yaml ? "_" : ", "), sizeof(buf));
364 }
365
366 switch (rdataset->trust) {
367 case dns_trust_none:
368 strlcat(buf, "untrusted", sizeof(buf));
369 break;
370 case dns_trust_pending_additional:
371 strlcat(buf, "signed additional data", sizeof(buf));
372 if (!yaml) {
373 strlcat(buf, ", ", sizeof(buf));
374 }
375 strlcat(buf, "pending validation", sizeof(buf));
376 break;
377 case dns_trust_pending_answer:
378 strlcat(buf, "signed answer", sizeof(buf));
379 if (!yaml) {
380 strlcat(buf, ", ", sizeof(buf));
381 }
382 strlcat(buf, "pending validation", sizeof(buf));
383 break;
384 case dns_trust_additional:
385 strlcat(buf, "unsigned additional data", sizeof(buf));
386 break;
387 case dns_trust_glue:
388 strlcat(buf, "glue data", sizeof(buf));
389 break;
390 case dns_trust_answer:
391 if (root_validation) {
392 strlcat(buf, "unsigned answer", sizeof(buf));
393 } else {
394 strlcat(buf, "answer not validated", sizeof(buf));
395 }
396 break;
397 case dns_trust_authauthority:
398 strlcat(buf, "authority data", sizeof(buf));
399 break;
400 case dns_trust_authanswer:
401 strlcat(buf, "authoritative", sizeof(buf));
402 break;
403 case dns_trust_secure:
404 strlcat(buf, "fully validated", sizeof(buf));
405 break;
406 case dns_trust_ultimate:
407 strlcat(buf, "ultimate trust", sizeof(buf));
408 break;
409 }
410
411 if (yaml) {
412 char *p;
413
414 /* Convert spaces to underscores for YAML */
415 for (p = buf; p != NULL && *p != '\0'; p++) {
416 if (*p == ' ') {
417 *p = '_';
418 }
419 }
420
421 printf(" - %s:\n", buf);
422 } else {
423 printf("; %s\n", buf);
424 }
425 }
426
427 static isc_result_t
428 printdata(dns_rdataset_t *rdataset, dns_name_t *owner,
429 dns_master_style_t *style) {
430 isc_result_t result = ISC_R_SUCCESS;
431 static dns_trust_t trust;
432 static bool first = true;
433 isc_buffer_t target;
434 isc_region_t r;
435 char *t = NULL;
436 int len = 2048;
437
438 if (!dns_rdataset_isassociated(rdataset)) {
439 char namebuf[DNS_NAME_FORMATSIZE];
440 dns_name_format(owner, namebuf, sizeof(namebuf));
441 delv_log(ISC_LOG_DEBUG(4), "WARN: empty rdataset %s", namebuf);
442 return (ISC_R_SUCCESS);
443 }
444
445 if (!showdnssec && rdataset->type == dns_rdatatype_rrsig) {
446 return (ISC_R_SUCCESS);
447 }
448
449 if (first || rdataset->trust != trust) {
450 if (!first && showtrust && !short_form && !yaml) {
451 putchar('\n');
452 }
453 print_status(rdataset);
454 trust = rdataset->trust;
455 first = false;
456 }
457
458 do {
459 t = isc_mem_get(mctx, len);
460
461 isc_buffer_init(&target, t, len);
462 if (short_form) {
463 dns_rdata_t rdata = DNS_RDATA_INIT;
464 for (result = dns_rdataset_first(rdataset);
465 result == ISC_R_SUCCESS;
466 result = dns_rdataset_next(rdataset))
467 {
468 if ((rdataset->attributes &
469 DNS_RDATASETATTR_NEGATIVE) != 0)
470 {
471 continue;
472 }
473
474 dns_rdataset_current(rdataset, &rdata);
475 result = dns_rdata_tofmttext(
476 &rdata, dns_rootname, styleflags, 0,
477 splitwidth, " ", &target);
478 if (result != ISC_R_SUCCESS) {
479 break;
480 }
481
482 if (isc_buffer_availablelength(&target) < 1) {
483 result = ISC_R_NOSPACE;
484 break;
485 }
486
487 isc_buffer_putstr(&target, "\n");
488
489 dns_rdata_reset(&rdata);
490 }
491 } else {
492 dns_indent_t indent = { " ", 2 };
493 if (!yaml && (rdataset->attributes &
494 DNS_RDATASETATTR_NEGATIVE) != 0)
495 {
496 isc_buffer_putstr(&target, "; ");
497 }
498 result = dns_master_rdatasettotext(
499 owner, rdataset, style, yaml ? &indent : NULL,
500 &target);
501 }
502
503 if (result == ISC_R_NOSPACE) {
504 isc_mem_put(mctx, t, len);
505 len += 1024;
506 } else if (result == ISC_R_NOMORE) {
507 result = ISC_R_SUCCESS;
508 } else {
509 CHECK(result);
510 }
511 } while (result == ISC_R_NOSPACE);
512
513 isc_buffer_usedregion(&target, &r);
514 printf("%.*s", (int)r.length, (char *)r.base);
515
516 cleanup:
517 if (t != NULL) {
518 isc_mem_put(mctx, t, len);
519 }
520
521 return (ISC_R_SUCCESS);
522 }
523
524 static isc_result_t
525 setup_style(dns_master_style_t **stylep) {
526 isc_result_t result;
527 dns_master_style_t *style = NULL;
528
529 REQUIRE(stylep != NULL && *stylep == NULL);
530
531 styleflags |= DNS_STYLEFLAG_REL_OWNER;
532 if (yaml) {
533 styleflags |= DNS_STYLEFLAG_YAML;
534 } else {
535 if (showcomments) {
536 styleflags |= DNS_STYLEFLAG_COMMENT;
537 }
538 if (print_unknown_format) {
539 styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
540 }
541 if (rrcomments) {
542 styleflags |= DNS_STYLEFLAG_RRCOMMENT;
543 }
544 if (nottl) {
545 styleflags |= DNS_STYLEFLAG_NO_TTL;
546 }
547 if (noclass) {
548 styleflags |= DNS_STYLEFLAG_NO_CLASS;
549 }
550 if (nocrypto) {
551 styleflags |= DNS_STYLEFLAG_NOCRYPTO;
552 }
553 if (multiline) {
554 styleflags |= DNS_STYLEFLAG_MULTILINE;
555 styleflags |= DNS_STYLEFLAG_COMMENT;
556 }
557 }
558
559 if (multiline || (nottl && noclass)) {
560 result = dns_master_stylecreate(&style, styleflags, 24, 24, 24,
561 32, 80, 8, splitwidth, mctx);
562 } else if (nottl || noclass) {
563 result = dns_master_stylecreate(&style, styleflags, 24, 24, 32,
564 40, 80, 8, splitwidth, mctx);
565 } else {
566 result = dns_master_stylecreate(&style, styleflags, 24, 32, 40,
567 48, 80, 8, splitwidth, mctx);
568 }
569
570 if (result == ISC_R_SUCCESS) {
571 *stylep = style;
572 }
573 return (result);
574 }
575
576 static isc_result_t
577 convert_name(dns_fixedname_t *fn, dns_name_t **name, const char *text) {
578 isc_result_t result;
579 isc_buffer_t b;
580 dns_name_t *n;
581 unsigned int len;
582
583 REQUIRE(fn != NULL && name != NULL && text != NULL);
584 len = strlen(text);
585
586 isc_buffer_constinit(&b, text, len);
587 isc_buffer_add(&b, len);
588 n = dns_fixedname_initname(fn);
589
590 result = dns_name_fromtext(n, &b, dns_rootname, 0, NULL);
591 if (result != ISC_R_SUCCESS) {
592 delv_log(ISC_LOG_ERROR, "failed to convert QNAME %s: %s", text,
593 isc_result_totext(result));
594 return (result);
595 }
596
597 *name = n;
598 return (ISC_R_SUCCESS);
599 }
600
601 static isc_result_t
602 key_fromconfig(const cfg_obj_t *key, dns_client_t *client) {
603 dns_rdata_dnskey_t dnskey;
604 dns_rdata_ds_t ds;
605 uint32_t rdata1, rdata2, rdata3;
606 const char *datastr = NULL, *keynamestr = NULL, *atstr = NULL;
607 unsigned char data[4096];
608 isc_buffer_t databuf;
609 unsigned char rrdata[4096];
610 isc_buffer_t rrdatabuf;
611 isc_region_t r;
612 dns_fixedname_t fkeyname;
613 dns_name_t *keyname;
614 isc_result_t result;
615 bool match_root = false;
616 enum {
617 INITIAL_KEY,
618 STATIC_KEY,
619 INITIAL_DS,
620 STATIC_DS,
621 TRUSTED
622 } anchortype;
623 const cfg_obj_t *obj;
624
625 keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
626 CHECK(convert_name(&fkeyname, &keyname, keynamestr));
627
628 if (!root_validation) {
629 return (ISC_R_SUCCESS);
630 }
631
632 if (anchor_name) {
633 match_root = dns_name_equal(keyname, anchor_name);
634 }
635
636 if (!match_root) {
637 return (ISC_R_SUCCESS);
638 }
639
640 if (!root_validation) {
641 return (ISC_R_SUCCESS);
642 }
643
644 delv_log(ISC_LOG_DEBUG(3), "adding trust anchor %s", trust_anchor);
645
646 /* if DNSKEY, flags; if DS, key tag */
647 rdata1 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata1"));
648
649 /* if DNSKEY, protocol; if DS, algorithm */
650 rdata2 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata2"));
651
652 /* if DNSKEY, algorithm; if DS, digest type */
653 rdata3 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata3"));
654
655 /* What type of trust anchor is this? */
656 obj = cfg_tuple_get(key, "anchortype");
657 if (cfg_obj_isvoid(obj)) {
658 /*
659 * "anchortype" is not defined, this must be a static-key
660 * configured with trusted-keys.
661 */
662 anchortype = STATIC_KEY;
663 } else {
664 atstr = cfg_obj_asstring(obj);
665 if (strcasecmp(atstr, "static-key") == 0) {
666 anchortype = STATIC_KEY;
667 } else if (strcasecmp(atstr, "static-ds") == 0) {
668 anchortype = STATIC_DS;
669 } else if (strcasecmp(atstr, "initial-key") == 0) {
670 anchortype = INITIAL_KEY;
671 } else if (strcasecmp(atstr, "initial-ds") == 0) {
672 anchortype = INITIAL_DS;
673 } else {
674 delv_log(ISC_LOG_ERROR,
675 "key '%s': invalid initialization method '%s'",
676 keynamestr, atstr);
677 result = ISC_R_FAILURE;
678 goto cleanup;
679 }
680 }
681
682 isc_buffer_init(&databuf, data, sizeof(data));
683 isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
684
685 if (rdata1 > 0xffff) {
686 CHECK(ISC_R_RANGE);
687 }
688 if (rdata2 > 0xff) {
689 CHECK(ISC_R_RANGE);
690 }
691 if (rdata3 > 0xff) {
692 CHECK(ISC_R_RANGE);
693 }
694
695 switch (anchortype) {
696 case STATIC_KEY:
697 case INITIAL_KEY:
698 case TRUSTED:
699 dnskey.common.rdclass = dns_rdataclass_in;
700 dnskey.common.rdtype = dns_rdatatype_dnskey;
701 dnskey.mctx = NULL;
702
703 ISC_LINK_INIT(&dnskey.common, link);
704
705 dnskey.flags = (uint16_t)rdata1;
706 dnskey.protocol = (uint8_t)rdata2;
707 dnskey.algorithm = (uint8_t)rdata3;
708
709 datastr = cfg_obj_asstring(cfg_tuple_get(key, "data"));
710 CHECK(isc_base64_decodestring(datastr, &databuf));
711 isc_buffer_usedregion(&databuf, &r);
712 dnskey.datalen = r.length;
713 dnskey.data = r.base;
714
715 CHECK(dns_rdata_fromstruct(NULL, dnskey.common.rdclass,
716 dnskey.common.rdtype, &dnskey,
717 &rrdatabuf));
718 CHECK(dns_client_addtrustedkey(client, dns_rdataclass_in,
719 dns_rdatatype_dnskey, keyname,
720 &rrdatabuf));
721 break;
722 case INITIAL_DS:
723 case STATIC_DS:
724 ds.common.rdclass = dns_rdataclass_in;
725 ds.common.rdtype = dns_rdatatype_ds;
726 ds.mctx = NULL;
727
728 ISC_LINK_INIT(&ds.common, link);
729
730 ds.key_tag = (uint16_t)rdata1;
731 ds.algorithm = (uint8_t)rdata2;
732 ds.digest_type = (uint8_t)rdata3;
733
734 datastr = cfg_obj_asstring(cfg_tuple_get(key, "data"));
735 CHECK(isc_hex_decodestring(datastr, &databuf));
736 isc_buffer_usedregion(&databuf, &r);
737
738 switch (ds.digest_type) {
739 case DNS_DSDIGEST_SHA1:
740 if (r.length != ISC_SHA1_DIGESTLENGTH) {
741 CHECK(ISC_R_UNEXPECTEDEND);
742 }
743 break;
744 case DNS_DSDIGEST_SHA256:
745 if (r.length != ISC_SHA256_DIGESTLENGTH) {
746 CHECK(ISC_R_UNEXPECTEDEND);
747 }
748 break;
749 case DNS_DSDIGEST_SHA384:
750 if (r.length != ISC_SHA384_DIGESTLENGTH) {
751 CHECK(ISC_R_UNEXPECTEDEND);
752 }
753 break;
754 }
755
756 ds.length = r.length;
757 ds.digest = r.base;
758
759 CHECK(dns_rdata_fromstruct(NULL, ds.common.rdclass,
760 ds.common.rdtype, &ds, &rrdatabuf));
761 CHECK(dns_client_addtrustedkey(client, dns_rdataclass_in,
762 dns_rdatatype_ds, keyname,
763 &rrdatabuf));
764 }
765
766 num_keys++;
767
768 cleanup:
769 if (result == DST_R_NOCRYPTO) {
770 cfg_obj_log(key, lctx, ISC_LOG_ERROR, "no crypto support");
771 } else if (result == DST_R_UNSUPPORTEDALG) {
772 cfg_obj_log(key, lctx, ISC_LOG_WARNING,
773 "skipping trusted key '%s': %s", keynamestr,
774 isc_result_totext(result));
775 result = ISC_R_SUCCESS;
776 } else if (result != ISC_R_SUCCESS) {
777 cfg_obj_log(key, lctx, ISC_LOG_ERROR,
778 "failed to add trusted key '%s': %s", keynamestr,
779 isc_result_totext(result));
780 result = ISC_R_FAILURE;
781 }
782
783 return (result);
784 }
785
786 static isc_result_t
787 load_keys(const cfg_obj_t *keys, dns_client_t *client) {
788 const cfg_listelt_t *elt, *elt2;
789 const cfg_obj_t *key, *keylist;
790 isc_result_t result = ISC_R_SUCCESS;
791
792 for (elt = cfg_list_first(keys); elt != NULL; elt = cfg_list_next(elt))
793 {
794 keylist = cfg_listelt_value(elt);
795
796 for (elt2 = cfg_list_first(keylist); elt2 != NULL;
797 elt2 = cfg_list_next(elt2))
798 {
799 key = cfg_listelt_value(elt2);
800 CHECK(key_fromconfig(key, client));
801 }
802 }
803
804 cleanup:
805 if (result == DST_R_NOCRYPTO) {
806 result = ISC_R_SUCCESS;
807 }
808 return (result);
809 }
810
811 static isc_result_t
812 setup_dnsseckeys(dns_client_t *client) {
813 isc_result_t result;
814 cfg_parser_t *parser = NULL;
815 const cfg_obj_t *trusted_keys = NULL;
816 const cfg_obj_t *managed_keys = NULL;
817 const cfg_obj_t *trust_anchors = NULL;
818 cfg_obj_t *bindkeys = NULL;
819 const char *filename = anchorfile;
820
821 if (!root_validation) {
822 return (ISC_R_SUCCESS);
823 }
824
825 if (filename == NULL) {
826 filename = SYSCONFDIR "/bind.keys";
827 }
828
829 if (trust_anchor == NULL) {
830 trust_anchor = isc_mem_strdup(mctx, ".");
831 }
832
833 if (trust_anchor != NULL) {
834 CHECK(convert_name(&afn, &anchor_name, trust_anchor));
835 }
836
837 CHECK(cfg_parser_create(mctx, dns_lctx, &parser));
838
839 if (access(filename, R_OK) != 0) {
840 if (anchorfile != NULL) {
841 fatal("Unable to read key file '%s'", anchorfile);
842 }
843 } else {
844 result = cfg_parse_file(parser, filename, &cfg_type_bindkeys,
845 &bindkeys);
846 if (result != ISC_R_SUCCESS) {
847 if (anchorfile != NULL) {
848 fatal("Unable to load keys from '%s'",
849 anchorfile);
850 }
851 }
852 }
853
854 if (bindkeys == NULL) {
855 isc_buffer_t b;
856
857 isc_buffer_init(&b, anchortext, sizeof(anchortext) - 1);
858 isc_buffer_add(&b, sizeof(anchortext) - 1);
859 cfg_parser_reset(parser);
860 result = cfg_parse_buffer(parser, &b, NULL, 0,
861 &cfg_type_bindkeys, 0, &bindkeys);
862 if (result != ISC_R_SUCCESS) {
863 fatal("Unable to parse built-in keys");
864 }
865 }
866
867 INSIST(bindkeys != NULL);
868 cfg_map_get(bindkeys, "trusted-keys", &trusted_keys);
869 cfg_map_get(bindkeys, "managed-keys", &managed_keys);
870 cfg_map_get(bindkeys, "trust-anchors", &trust_anchors);
871
872 if (trusted_keys != NULL) {
873 CHECK(load_keys(trusted_keys, client));
874 }
875 if (managed_keys != NULL) {
876 CHECK(load_keys(managed_keys, client));
877 }
878 if (trust_anchors != NULL) {
879 CHECK(load_keys(trust_anchors, client));
880 }
881 result = ISC_R_SUCCESS;
882
883 if (num_keys == 0) {
884 fatal("No trusted keys were loaded");
885 }
886
887 cleanup:
888 if (bindkeys != NULL) {
889 cfg_obj_destroy(parser, &bindkeys);
890 }
891 if (parser != NULL) {
892 cfg_parser_destroy(&parser);
893 }
894 if (result != ISC_R_SUCCESS) {
895 delv_log(ISC_LOG_ERROR, "setup_dnsseckeys: %s",
896 isc_result_totext(result));
897 }
898 return (result);
899 }
900
901 static isc_result_t
902 addserver(dns_client_t *client) {
903 struct addrinfo hints, *res, *cur;
904 int gaierror;
905 struct in_addr in4;
906 struct in6_addr in6;
907 isc_sockaddr_t *sa;
908 isc_sockaddrlist_t servers;
909 uint32_t destport;
910 isc_result_t result;
911 dns_name_t *name = NULL;
912
913 result = parse_uint(&destport, port, 0xffff, "port");
914 if (result != ISC_R_SUCCESS) {
915 fatal("Couldn't parse port number");
916 }
917
918 ISC_LIST_INIT(servers);
919
920 if (inet_pton(AF_INET, server, &in4) == 1) {
921 if (!use_ipv4) {
922 fatal("Use of IPv4 disabled by -6");
923 }
924 sa = isc_mem_get(mctx, sizeof(*sa));
925 ISC_LINK_INIT(sa, link);
926 isc_sockaddr_fromin(sa, &in4, destport);
927 ISC_LIST_APPEND(servers, sa, link);
928 } else if (inet_pton(AF_INET6, server, &in6) == 1) {
929 if (!use_ipv6) {
930 fatal("Use of IPv6 disabled by -4");
931 }
932 sa = isc_mem_get(mctx, sizeof(*sa));
933 ISC_LINK_INIT(sa, link);
934 isc_sockaddr_fromin6(sa, &in6, destport);
935 ISC_LIST_APPEND(servers, sa, link);
936 } else {
937 memset(&hints, 0, sizeof(hints));
938 if (!use_ipv6) {
939 hints.ai_family = AF_INET;
940 } else if (!use_ipv4) {
941 hints.ai_family = AF_INET6;
942 } else {
943 hints.ai_family = AF_UNSPEC;
944 }
945 hints.ai_socktype = SOCK_DGRAM;
946 hints.ai_protocol = IPPROTO_UDP;
947 gaierror = getaddrinfo(server, port, &hints, &res);
948 if (gaierror != 0) {
949 delv_log(ISC_LOG_ERROR, "getaddrinfo failed: %s",
950 gai_strerror(gaierror));
951 return (ISC_R_FAILURE);
952 }
953
954 result = ISC_R_SUCCESS;
955 for (cur = res; cur != NULL; cur = cur->ai_next) {
956 if (cur->ai_family != AF_INET &&
957 cur->ai_family != AF_INET6)
958 {
959 continue;
960 }
961 sa = isc_mem_get(mctx, sizeof(*sa));
962 memset(sa, 0, sizeof(*sa));
963 ISC_LINK_INIT(sa, link);
964 memmove(&sa->type, cur->ai_addr, cur->ai_addrlen);
965 sa->length = (unsigned int)cur->ai_addrlen;
966 ISC_LIST_APPEND(servers, sa, link);
967 }
968 freeaddrinfo(res);
969 CHECK(result);
970 }
971
972 CHECK(dns_client_setservers(client, dns_rdataclass_in, name, &servers));
973
974 cleanup:
975 while (!ISC_LIST_EMPTY(servers)) {
976 sa = ISC_LIST_HEAD(servers);
977 ISC_LIST_UNLINK(servers, sa, link);
978 isc_mem_put(mctx, sa, sizeof(*sa));
979 }
980
981 if (result != ISC_R_SUCCESS) {
982 delv_log(ISC_LOG_ERROR, "addserver: %s",
983 isc_result_totext(result));
984 }
985
986 return (result);
987 }
988
989 static isc_result_t
990 findserver(dns_client_t *client) {
991 isc_result_t result;
992 irs_resconf_t *resconf = NULL;
993 isc_sockaddrlist_t *nameservers;
994 isc_sockaddr_t *sa, *next;
995 uint32_t destport;
996
997 result = parse_uint(&destport, port, 0xffff, "port");
998 if (result != ISC_R_SUCCESS) {
999 fatal("Couldn't parse port number");
1000 }
1001
1002 result = irs_resconf_load(mctx, "/etc/resolv.conf", &resconf);
1003 if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
1004 delv_log(ISC_LOG_ERROR, "irs_resconf_load: %s",
1005 isc_result_totext(result));
1006 goto cleanup;
1007 }
1008
1009 /* Get nameservers from resolv.conf */
1010 nameservers = irs_resconf_getnameservers(resconf);
1011 for (sa = ISC_LIST_HEAD(*nameservers); sa != NULL; sa = next) {
1012 next = ISC_LIST_NEXT(sa, link);
1013
1014 /* Set destination port */
1015 if (sa->type.sa.sa_family == AF_INET && use_ipv4) {
1016 sa->type.sin.sin_port = htons(destport);
1017 continue;
1018 }
1019 if (sa->type.sa.sa_family == AF_INET6 && use_ipv6) {
1020 sa->type.sin6.sin6_port = htons(destport);
1021 continue;
1022 }
1023
1024 /* Incompatible protocol family */
1025 ISC_LIST_UNLINK(*nameservers, sa, link);
1026 isc_mem_put(mctx, sa, sizeof(*sa));
1027 }
1028
1029 /* None found, use localhost */
1030 if (ISC_LIST_EMPTY(*nameservers)) {
1031 if (use_ipv4) {
1032 struct in_addr localhost;
1033 localhost.s_addr = htonl(INADDR_LOOPBACK);
1034 sa = isc_mem_get(mctx, sizeof(*sa));
1035 isc_sockaddr_fromin(sa, &localhost, destport);
1036
1037 ISC_LINK_INIT(sa, link);
1038 ISC_LIST_APPEND(*nameservers, sa, link);
1039 }
1040
1041 if (use_ipv6) {
1042 sa = isc_mem_get(mctx, sizeof(*sa));
1043 isc_sockaddr_fromin6(sa, &in6addr_loopback, destport);
1044
1045 ISC_LINK_INIT(sa, link);
1046 ISC_LIST_APPEND(*nameservers, sa, link);
1047 }
1048 }
1049
1050 result = dns_client_setservers(client, dns_rdataclass_in, NULL,
1051 nameservers);
1052 if (result != ISC_R_SUCCESS) {
1053 delv_log(ISC_LOG_ERROR, "dns_client_setservers: %s",
1054 isc_result_totext(result));
1055 }
1056
1057 cleanup:
1058 if (resconf != NULL) {
1059 irs_resconf_destroy(&resconf);
1060 }
1061 return (result);
1062 }
1063
1064 static isc_result_t
1065 parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) {
1066 uint32_t n;
1067 isc_result_t result = isc_parse_uint32(&n, value, 10);
1068 if (result == ISC_R_SUCCESS && n > max) {
1069 result = ISC_R_RANGE;
1070 }
1071 if (result != ISC_R_SUCCESS) {
1072 printf("invalid %s '%s': %s\n", desc, value,
1073 isc_result_totext(result));
1074 return (result);
1075 }
1076 *uip = n;
1077 return (ISC_R_SUCCESS);
1078 }
1079
1080 static void
1081 plus_option(char *option) {
1082 isc_result_t result;
1083 char *cmd, *value, *last = NULL;
1084 bool state = true;
1085
1086 INSIST(option != NULL);
1087
1088 cmd = strtok_r(option, "=", &last);
1089 if (cmd == NULL) {
1090 printf(";; Invalid option %s\n", option);
1091 return;
1092 }
1093 if (strncasecmp(cmd, "no", 2) == 0) {
1094 cmd += 2;
1095 state = false;
1096 }
1097
1098 value = strtok_r(NULL, "\0", &last);
1099
1100 #define FULLCHECK(A) \
1101 do { \
1102 size_t _l = strlen(cmd); \
1103 if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
1104 goto invalid_option; \
1105 } while (0)
1106
1107 switch (cmd[0]) {
1108 case 'a': /* all */
1109 FULLCHECK("all");
1110 showcomments = state;
1111 rrcomments = state;
1112 showtrust = state;
1113 break;
1114 case 'c':
1115 switch (cmd[1]) {
1116 case 'd': /* cdflag */
1117 FULLCHECK("cdflag");
1118 cdflag = state;
1119 break;
1120 case 'l': /* class */
1121 FULLCHECK("class");
1122 noclass = !state;
1123 break;
1124 case 'o': /* comments */
1125 FULLCHECK("comments");
1126 showcomments = state;
1127 break;
1128 case 'r': /* crypto */
1129 FULLCHECK("crypto");
1130 nocrypto = !state;
1131 break;
1132 default:
1133 goto invalid_option;
1134 }
1135 break;
1136 case 'd':
1137 switch (cmd[1]) {
1138 case 'l': /* dlv */
1139 FULLCHECK("dlv");
1140 if (state) {
1141 fprintf(stderr, "Invalid option: "
1142 "+dlv is obsolete\n");
1143 exit(EXIT_FAILURE);
1144 }
1145 break;
1146 case 'n': /* dnssec */
1147 FULLCHECK("dnssec");
1148 showdnssec = state;
1149 break;
1150 default:
1151 goto invalid_option;
1152 }
1153 break;
1154 case 'm':
1155 switch (cmd[1]) {
1156 case 't': /* mtrace */
1157 message_trace = state;
1158 if (state) {
1159 resolve_trace = state;
1160 }
1161 break;
1162 case 'u': /* multiline */
1163 FULLCHECK("multiline");
1164 multiline = state;
1165 break;
1166 default:
1167 goto invalid_option;
1168 }
1169 break;
1170 case 'r':
1171 switch (cmd[1]) {
1172 case 'o': /* root */
1173 FULLCHECK("root");
1174 if (state && no_sigs) {
1175 break;
1176 }
1177 root_validation = state;
1178 if (value != NULL) {
1179 trust_anchor = isc_mem_strdup(mctx, value);
1180 }
1181 break;
1182 case 'r': /* rrcomments */
1183 FULLCHECK("rrcomments");
1184 rrcomments = state;
1185 break;
1186 case 't': /* rtrace */
1187 FULLCHECK("rtrace");
1188 resolve_trace = state;
1189 break;
1190 default:
1191 goto invalid_option;
1192 }
1193 break;
1194 case 's':
1195 switch (cmd[1]) {
1196 case 'h': /* short */
1197 FULLCHECK("short");
1198 short_form = state;
1199 if (short_form) {
1200 multiline = false;
1201 showcomments = false;
1202 showtrust = false;
1203 showdnssec = false;
1204 }
1205 break;
1206 case 'p': /* split */
1207 FULLCHECK("split");
1208 if (value != NULL && !state) {
1209 goto invalid_option;
1210 }
1211 if (!state) {
1212 splitwidth = 0;
1213 break;
1214 } else if (value == NULL) {
1215 break;
1216 }
1217
1218 result = parse_uint(&splitwidth, value, 1023, "split");
1219 if (splitwidth % 4 != 0) {
1220 splitwidth = ((splitwidth + 3) / 4) * 4;
1221 warn("split must be a multiple of 4; "
1222 "adjusting to %d",
1223 splitwidth);
1224 }
1225 /*
1226 * There is an adjustment done in the
1227 * totext_<rrtype>() functions which causes
1228 * splitwidth to shrink. This is okay when we're
1229 * using the default width but incorrect in this
1230 * case, so we correct for it
1231 */
1232 if (splitwidth) {
1233 splitwidth += 3;
1234 }
1235 if (result != ISC_R_SUCCESS) {
1236 fatal("Couldn't parse split");
1237 }
1238 break;
1239 default:
1240 goto invalid_option;
1241 }
1242 break;
1243 case 'u':
1244 FULLCHECK("unknownformat");
1245 print_unknown_format = state;
1246 break;
1247 case 't':
1248 switch (cmd[1]) {
1249 case 'c': /* tcp */
1250 FULLCHECK("tcp");
1251 use_tcp = state;
1252 break;
1253 case 'r': /* trust */
1254 FULLCHECK("trust");
1255 showtrust = state;
1256 break;
1257 case 't': /* ttl */
1258 FULLCHECK("ttl");
1259 nottl = !state;
1260 break;
1261 default:
1262 goto invalid_option;
1263 }
1264 break;
1265 case 'v': /* vtrace */
1266 FULLCHECK("vtrace");
1267 validator_trace = state;
1268 if (state) {
1269 resolve_trace = state;
1270 }
1271 break;
1272 case 'y': /* yaml */
1273 FULLCHECK("yaml");
1274 yaml = state;
1275 if (state) {
1276 rrcomments = false;
1277 }
1278 break;
1279 default:
1280 invalid_option:
1281 /*
1282 * We can also add a "need_value:" case here if we ever
1283 * add a plus-option that requires a specified value
1284 */
1285 fprintf(stderr, "Invalid option: +%s\n", option);
1286 usage();
1287 }
1288 return;
1289 }
1290
1291 /*
1292 * options: "46a:b:c:d:himp:q:t:vx:";
1293 */
1294 static const char *single_dash_opts = "46himv";
1295 static const char *dash_opts = "46abcdhimpqtvx";
1296
1297 static bool
1298 dash_option(char *option, char *next, bool *open_type_class) {
1299 char opt, *value;
1300 isc_result_t result;
1301 bool value_from_next;
1302 isc_textregion_t tr;
1303 dns_rdatatype_t rdtype;
1304 dns_rdataclass_t rdclass;
1305 char textname[MAXNAME];
1306 struct in_addr in4;
1307 struct in6_addr in6;
1308 in_port_t srcport;
1309 uint32_t num;
1310 char *hash;
1311
1312 while (strpbrk(option, single_dash_opts) == &option[0]) {
1313 /*
1314 * Since the -[46himv] options do not take an argument,
1315 * account for them (in any number and/or combination)
1316 * if they appear as the first character(s) of a q-opt.
1317 */
1318 opt = option[0];
1319 switch (opt) {
1320 case '4':
1321 if (isc_net_probeipv4() != ISC_R_SUCCESS) {
1322 fatal("IPv4 networking not available");
1323 }
1324 if (use_ipv6) {
1325 isc_net_disableipv6();
1326 use_ipv6 = false;
1327 }
1328 break;
1329 case '6':
1330 if (isc_net_probeipv6() != ISC_R_SUCCESS) {
1331 fatal("IPv6 networking not available");
1332 }
1333 if (use_ipv4) {
1334 isc_net_disableipv4();
1335 use_ipv4 = false;
1336 }
1337 break;
1338 case 'h':
1339 usage();
1340 exit(EXIT_SUCCESS);
1341 case 'i':
1342 no_sigs = true;
1343 root_validation = false;
1344 break;
1345 case 'm':
1346 /* handled in preparse_args() */
1347 break;
1348 case 'v':
1349 fprintf(stderr, "delv %s\n", PACKAGE_VERSION);
1350 exit(EXIT_SUCCESS);
1351 default:
1352 UNREACHABLE();
1353 }
1354 if (strlen(option) > 1U) {
1355 option = &option[1];
1356 } else {
1357 return (false);
1358 }
1359 }
1360 opt = option[0];
1361 if (strlen(option) > 1U) {
1362 value_from_next = false;
1363 value = &option[1];
1364 } else {
1365 value_from_next = true;
1366 value = next;
1367 }
1368 if (value == NULL) {
1369 goto invalid_option;
1370 }
1371 switch (opt) {
1372 case 'a':
1373 anchorfile = isc_mem_strdup(mctx, value);
1374 return (value_from_next);
1375 case 'b':
1376 hash = strchr(value, '#');
1377 if (hash != NULL) {
1378 result = parse_uint(&num, hash + 1, 0xffff, "port");
1379 if (result != ISC_R_SUCCESS) {
1380 fatal("Couldn't parse port number");
1381 }
1382 srcport = num;
1383 *hash = '\0';
1384 } else {
1385 srcport = 0;
1386 }
1387
1388 if (inet_pton(AF_INET, value, &in4) == 1) {
1389 if (srcaddr4 != NULL) {
1390 fatal("Only one local address per family "
1391 "can be specified\n");
1392 }
1393 isc_sockaddr_fromin(&a4, &in4, srcport);
1394 srcaddr4 = &a4;
1395 } else if (inet_pton(AF_INET6, value, &in6) == 1) {
1396 if (srcaddr6 != NULL) {
1397 fatal("Only one local address per family "
1398 "can be specified\n");
1399 }
1400 isc_sockaddr_fromin6(&a6, &in6, srcport);
1401 srcaddr6 = &a6;
1402 } else {
1403 if (hash != NULL) {
1404 *hash = '#';
1405 }
1406 fatal("Invalid address %s", value);
1407 }
1408 if (hash != NULL) {
1409 *hash = '#';
1410 }
1411 return (value_from_next);
1412 case 'c':
1413 if (classset) {
1414 warn("extra query class");
1415 }
1416
1417 *open_type_class = false;
1418 tr.base = value;
1419 tr.length = strlen(value);
1420 result = dns_rdataclass_fromtext(&rdclass,
1421 (isc_textregion_t *)&tr);
1422 if (result == ISC_R_SUCCESS) {
1423 classset = true;
1424 } else if (rdclass != dns_rdataclass_in) {
1425 warn("ignoring non-IN query class");
1426 } else {
1427 warn("ignoring invalid class");
1428 }
1429 return (value_from_next);
1430 case 'd':
1431 result = parse_uint(&num, value, 99, "debug level");
1432 if (result != ISC_R_SUCCESS) {
1433 fatal("Couldn't parse debug level");
1434 }
1435 loglevel = num;
1436 return (value_from_next);
1437 case 'p':
1438 port = value;
1439 return (value_from_next);
1440 case 'q':
1441 if (curqname != NULL) {
1442 warn("extra query name");
1443 isc_mem_free(mctx, curqname);
1444 }
1445 curqname = isc_mem_strdup(mctx, value);
1446 return (value_from_next);
1447 case 't':
1448 *open_type_class = false;
1449 tr.base = value;
1450 tr.length = strlen(value);
1451 result = dns_rdatatype_fromtext(&rdtype,
1452 (isc_textregion_t *)&tr);
1453 if (result == ISC_R_SUCCESS) {
1454 if (typeset) {
1455 warn("extra query type");
1456 }
1457 if (rdtype == dns_rdatatype_ixfr ||
1458 rdtype == dns_rdatatype_axfr)
1459 {
1460 fatal("Transfer not supported");
1461 }
1462 qtype = rdtype;
1463 typeset = true;
1464 } else {
1465 warn("ignoring invalid type");
1466 }
1467 return (value_from_next);
1468 case 'x':
1469 result = get_reverse(textname, sizeof(textname), value, false);
1470 if (result == ISC_R_SUCCESS) {
1471 if (curqname != NULL) {
1472 isc_mem_free(mctx, curqname);
1473 warn("extra query name");
1474 }
1475 curqname = isc_mem_strdup(mctx, textname);
1476 if (typeset) {
1477 warn("extra query type");
1478 }
1479 qtype = dns_rdatatype_ptr;
1480 typeset = true;
1481 } else {
1482 fprintf(stderr, "Invalid IP address %s\n", value);
1483 exit(EXIT_FAILURE);
1484 }
1485 return (value_from_next);
1486 invalid_option:
1487 default:
1488 fprintf(stderr, "Invalid option: -%s\n", option);
1489 usage();
1490 }
1491 UNREACHABLE();
1492 return (false);
1493 }
1494
1495 /*
1496 * Check for -m first to determine whether to enable
1497 * memory debugging when setting up the memory context.
1498 */
1499 static void
1500 preparse_args(int argc, char **argv) {
1501 bool ipv4only = false, ipv6only = false;
1502 char *option;
1503
1504 for (argc--, argv++; argc > 0; argc--, argv++) {
1505 if (argv[0][0] != '-') {
1506 continue;
1507 }
1508
1509 option = &argv[0][1];
1510 while (strpbrk(option, single_dash_opts) == &option[0]) {
1511 switch (option[0]) {
1512 case 'm':
1513 isc_mem_debugging = ISC_MEM_DEBUGTRACE |
1514 ISC_MEM_DEBUGRECORD;
1515 break;
1516 case '4':
1517 if (ipv6only) {
1518 fatal("only one of -4 and -6 allowed");
1519 }
1520 ipv4only = true;
1521 break;
1522 case '6':
1523 if (ipv4only) {
1524 fatal("only one of -4 and -6 allowed");
1525 }
1526 ipv6only = true;
1527 break;
1528 }
1529 option = &option[1];
1530 }
1531
1532 if (strlen(option) == 0U) {
1533 continue;
1534 }
1535
1536 /* Look for dash value option. */
1537 if (strpbrk(option, dash_opts) != &option[0] ||
1538 strlen(option) > 1U)
1539 {
1540 /* Error or value in option. */
1541 continue;
1542 }
1543
1544 /* Dash value is next argument so we need to skip it. */
1545 argc--;
1546 argv++;
1547
1548 /* Handle missing argument */
1549 if (argc == 0) {
1550 break;
1551 }
1552 }
1553 }
1554
1555 /*
1556 * Argument parsing is based on dig, but simplified: only one
1557 * QNAME/QCLASS/QTYPE tuple can be specified, and options have
1558 * been removed that aren't applicable to delv. The interface
1559 * should be familiar to dig users, however.
1560 */
1561 static void
1562 parse_args(int argc, char **argv) {
1563 isc_result_t result;
1564 isc_textregion_t tr;
1565 dns_rdatatype_t rdtype;
1566 dns_rdataclass_t rdclass;
1567 bool open_type_class = true;
1568
1569 for (; argc > 0; argc--, argv++) {
1570 if (argv[0][0] == '@') {
1571 server = &argv[0][1];
1572 } else if (argv[0][0] == '+') {
1573 plus_option(&argv[0][1]);
1574 } else if (argv[0][0] == '-') {
1575 if (argc <= 1) {
1576 if (dash_option(&argv[0][1], NULL,
1577 &open_type_class))
1578 {
1579 argc--;
1580 argv++;
1581 }
1582 } else {
1583 if (dash_option(&argv[0][1], argv[1],
1584 &open_type_class))
1585 {
1586 argc--;
1587 argv++;
1588 }
1589 }
1590 } else {
1591 /*
1592 * Anything which isn't an option
1593 */
1594 if (open_type_class) {
1595 tr.base = argv[0];
1596 tr.length = strlen(argv[0]);
1597 result = dns_rdatatype_fromtext(
1598 &rdtype, (isc_textregion_t *)&tr);
1599 if (result == ISC_R_SUCCESS) {
1600 if (typeset) {
1601 warn("extra query type");
1602 }
1603 if (rdtype == dns_rdatatype_ixfr ||
1604 rdtype == dns_rdatatype_axfr)
1605 {
1606 fatal("Transfer not supported");
1607 }
1608 qtype = rdtype;
1609 typeset = true;
1610 continue;
1611 }
1612 result = dns_rdataclass_fromtext(
1613 &rdclass, (isc_textregion_t *)&tr);
1614 if (result == ISC_R_SUCCESS) {
1615 if (classset) {
1616 warn("extra query class");
1617 } else if (rdclass != dns_rdataclass_in)
1618 {
1619 warn("ignoring non-IN "
1620 "query class");
1621 }
1622 continue;
1623 }
1624 }
1625
1626 if (curqname == NULL) {
1627 curqname = isc_mem_strdup(mctx, argv[0]);
1628 }
1629 }
1630 }
1631
1632 /*
1633 * If no qname or qtype specified, search for root/NS
1634 * If no qtype specified, use A
1635 */
1636 if (!typeset) {
1637 qtype = dns_rdatatype_a;
1638 }
1639
1640 if (curqname == NULL) {
1641 qname = isc_mem_strdup(mctx, ".");
1642
1643 if (!typeset) {
1644 qtype = dns_rdatatype_ns;
1645 }
1646 } else {
1647 qname = curqname;
1648 }
1649 }
1650
1651 static isc_result_t
1652 append_str(const char *text, int len, char **p, char *end) {
1653 if (len > end - *p) {
1654 return (ISC_R_NOSPACE);
1655 }
1656 memmove(*p, text, len);
1657 *p += len;
1658 return (ISC_R_SUCCESS);
1659 }
1660
1661 static isc_result_t
1662 reverse_octets(const char *in, char **p, char *end) {
1663 char *dot = strchr(in, '.');
1664 int len;
1665 if (dot != NULL) {
1666 isc_result_t result;
1667 result = reverse_octets(dot + 1, p, end);
1668 if (result != ISC_R_SUCCESS) {
1669 return (result);
1670 }
1671 result = append_str(".", 1, p, end);
1672 if (result != ISC_R_SUCCESS) {
1673 return (result);
1674 }
1675 len = (int)(dot - in);
1676 } else {
1677 len = strlen(in);
1678 }
1679 return (append_str(in, len, p, end));
1680 }
1681
1682 static isc_result_t
1683 get_reverse(char *reverse, size_t len, char *value, bool strict) {
1684 int r;
1685 isc_result_t result;
1686 isc_netaddr_t addr;
1687
1688 addr.family = AF_INET6;
1689 r = inet_pton(AF_INET6, value, &addr.type.in6);
1690 if (r > 0) {
1691 /* This is a valid IPv6 address. */
1692 dns_fixedname_t fname;
1693 dns_name_t *name;
1694 unsigned int options = 0;
1695
1696 name = dns_fixedname_initname(&fname);
1697 result = dns_byaddr_createptrname(&addr, options, name);
1698 if (result != ISC_R_SUCCESS) {
1699 return (result);
1700 }
1701 dns_name_format(name, reverse, (unsigned int)len);
1702 return (ISC_R_SUCCESS);
1703 } else {
1704 /*
1705 * Not a valid IPv6 address. Assume IPv4.
1706 * If 'strict' is not set, construct the
1707 * in-addr.arpa name by blindly reversing
1708 * octets whether or not they look like integers,
1709 * so that this can be used for RFC2317 names
1710 * and such.
1711 */
1712 char *p = reverse;
1713 char *end = reverse + len;
1714 if (strict && inet_pton(AF_INET, value, &addr.type.in) != 1) {
1715 return (DNS_R_BADDOTTEDQUAD);
1716 }
1717 result = reverse_octets(value, &p, end);
1718 if (result != ISC_R_SUCCESS) {
1719 return (result);
1720 }
1721 result = append_str(".in-addr.arpa.", 15, &p, end);
1722 if (result != ISC_R_SUCCESS) {
1723 return (result);
1724 }
1725 return (ISC_R_SUCCESS);
1726 }
1727 }
1728
1729 int
1730 main(int argc, char *argv[]) {
1731 dns_client_t *client = NULL;
1732 isc_result_t result;
1733 dns_fixedname_t qfn;
1734 dns_name_t *query_name, *response_name;
1735 char namestr[DNS_NAME_FORMATSIZE];
1736 dns_rdataset_t *rdataset;
1737 dns_namelist_t namelist;
1738 unsigned int resopt;
1739 isc_appctx_t *actx = NULL;
1740 isc_nm_t *netmgr = NULL;
1741 isc_taskmgr_t *taskmgr = NULL;
1742 isc_timermgr_t *timermgr = NULL;
1743 dns_master_style_t *style = NULL;
1744 struct sigaction sa;
1745
1746 progname = argv[0];
1747 preparse_args(argc, argv);
1748
1749 argc--;
1750 argv++;
1751
1752 isc_mem_create(&mctx);
1753
1754 result = dst_lib_init(mctx, NULL);
1755 if (result != ISC_R_SUCCESS) {
1756 fatal("dst_lib_init failed: %d", result);
1757 }
1758
1759 CHECK(isc_appctx_create(mctx, &actx));
1760
1761 isc_managers_create(mctx, 1, 0, &netmgr, &taskmgr, &timermgr);
1762
1763 parse_args(argc, argv);
1764
1765 CHECK(setup_style(&style));
1766
1767 setup_logging(stderr);
1768
1769 CHECK(isc_app_ctxstart(actx));
1770
1771 /* Unblock SIGINT if it's been blocked by isc_app_ctxstart() */
1772 memset(&sa, 0, sizeof(sa));
1773 sa.sa_handler = SIG_DFL;
1774 if (sigfillset(&sa.sa_mask) != 0 || sigaction(SIGINT, &sa, NULL) < 0) {
1775 fatal("Couldn't set up signal handler");
1776 }
1777
1778 /* Create client */
1779 result = dns_client_create(mctx, actx, taskmgr, netmgr, timermgr, 0,
1780 &client, srcaddr4, srcaddr6);
1781 if (result != ISC_R_SUCCESS) {
1782 delv_log(ISC_LOG_ERROR, "dns_client_create: %s",
1783 isc_result_totext(result));
1784 goto cleanup;
1785 }
1786
1787 dns_client_setmaxrestarts(client, MAX_RESTARTS);
1788
1789 /* Set the nameserver */
1790 if (server != NULL) {
1791 addserver(client);
1792 } else {
1793 findserver(client);
1794 }
1795
1796 CHECK(setup_dnsseckeys(client));
1797
1798 /* Construct QNAME */
1799 CHECK(convert_name(&qfn, &query_name, qname));
1800
1801 /* Set up resolution options */
1802 resopt = DNS_CLIENTRESOPT_NOCDFLAG;
1803 if (no_sigs) {
1804 resopt |= DNS_CLIENTRESOPT_NODNSSEC;
1805 }
1806 if (!root_validation) {
1807 resopt |= DNS_CLIENTRESOPT_NOVALIDATE;
1808 }
1809 if (cdflag) {
1810 resopt &= ~DNS_CLIENTRESOPT_NOCDFLAG;
1811 }
1812 if (use_tcp) {
1813 resopt |= DNS_CLIENTRESOPT_TCP;
1814 }
1815
1816 /* Perform resolution */
1817 ISC_LIST_INIT(namelist);
1818 result = dns_client_resolve(client, query_name, dns_rdataclass_in,
1819 qtype, resopt, &namelist);
1820 if (result != ISC_R_SUCCESS && !yaml) {
1821 delv_log(ISC_LOG_ERROR, "resolution failed: %s",
1822 isc_result_totext(result));
1823 }
1824
1825 if (yaml) {
1826 printf("type: DELV_RESULT\n");
1827 dns_name_format(query_name, namestr, sizeof(namestr));
1828 printf("query_name: %s\n", namestr);
1829 printf("status: %s\n", isc_result_totext(result));
1830 printf("records:\n");
1831 }
1832
1833 for (response_name = ISC_LIST_HEAD(namelist); response_name != NULL;
1834 response_name = ISC_LIST_NEXT(response_name, link))
1835 {
1836 for (rdataset = ISC_LIST_HEAD(response_name->list);
1837 rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link))
1838 {
1839 result = printdata(rdataset, response_name, style);
1840 if (result != ISC_R_SUCCESS) {
1841 delv_log(ISC_LOG_ERROR, "print data failed");
1842 }
1843 }
1844 }
1845
1846 dns_client_freeresanswer(client, &namelist);
1847
1848 cleanup:
1849 if (trust_anchor != NULL) {
1850 isc_mem_free(mctx, trust_anchor);
1851 }
1852 if (anchorfile != NULL) {
1853 isc_mem_free(mctx, anchorfile);
1854 }
1855 if (qname != NULL) {
1856 isc_mem_free(mctx, qname);
1857 }
1858 if (style != NULL) {
1859 dns_master_styledestroy(&style, mctx);
1860 }
1861 if (client != NULL) {
1862 dns_client_detach(&client);
1863 }
1864
1865 isc_managers_destroy(&netmgr, &taskmgr, &timermgr);
1866
1867 if (actx != NULL) {
1868 isc_appctx_destroy(&actx);
1869 }
1870 if (lctx != NULL) {
1871 isc_log_destroy(&lctx);
1872 }
1873 isc_mem_detach(&mctx);
1874
1875 dst_lib_destroy();
1876
1877 return (0);
1878 }
1879