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