Home | History | Annotate | Line # | Download | only in delv
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