dighost.c 73.8 KB
Newer Older
1
/*
Brian Wellington's avatar
Brian Wellington committed
2
 * Copyright (C) 2000, 2001  Internet Software Consortium.
3
 *
4 5 6
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
7
 *
8 9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 17
 */

18
/* $Id: dighost.c,v 1.186 2001/01/18 22:21:30 bwelling Exp $ */
19

20 21 22 23 24 25 26 27 28
/*
 * Notice to programmers:  Do not use this code as an example of how to
 * use the ISC library to perform DNS lookups.  Dig and Host both operate
 * on the request level, since they allow fine-tuning of output and are
 * intended as debugging tools.  As a result, they perform many of the
 * functions which could be better handled using the dns_resolver
 * functions in most applications.
 */

29 30 31
#include <config.h>
#include <stdlib.h>
#include <unistd.h>
32 33
#include <netdb.h>
#include <string.h>
34
#include <limits.h>
35

36
#include <dns/byaddr.h>
37
#include <dns/fixedname.h>
38
#include <dns/message.h>
Brian Wellington's avatar
Brian Wellington committed
39
#include <dns/name.h>
40
#ifdef DNS_OPT_NEWCODES
41
#include <dns/opt.h>
42
#endif /* DNS_OPT_NEWCODES */
43 44
#include <dns/rdata.h>
#include <dns/rdataclass.h>
Michael Sawyer's avatar
Michael Sawyer committed
45
#include <dns/rdatalist.h>
46
#include <dns/rdataset.h>
Michael Sawyer's avatar
Michael Sawyer committed
47
#include <dns/rdatastruct.h>
48 49
#include <dns/rdatatype.h>
#include <dns/result.h>
50
#include <dns/tsig.h>
51

52
#include <dst/dst.h>
53

Michael Sawyer's avatar
Michael Sawyer committed
54 55 56 57
#include <isc/app.h>
#include <isc/base64.h>
#include <isc/entropy.h>
#include <isc/lang.h>
58
#include <isc/netaddr.h>
Michael Sawyer's avatar
Michael Sawyer committed
59
#include <isc/netdb.h>
Mark Andrews's avatar
Mark Andrews committed
60
#include <isc/print.h>
Michael Sawyer's avatar
Michael Sawyer committed
61
#include <isc/result.h>
Michael Sawyer's avatar
Michael Sawyer committed
62 63 64 65 66
#include <isc/string.h>
#include <isc/task.h>
#include <isc/timer.h>
#include <isc/types.h>
#include <isc/util.h>
67

68 69
#include <dig/dig.h>

70 71 72 73 74 75 76 77 78 79 80 81
#ifdef HAVE_ADDRINFO
#ifdef HAVE_GETADDRINFO
#ifdef HAVE_GAISTRERROR
#define USE_GETADDRINFO
#endif
#endif
#endif

#ifdef USE_GETADDRINFO
extern int h_errno;
#endif

82
ISC_LIST(dig_lookup_t) lookup_list;
83
dig_serverlist_t server_list;
84
ISC_LIST(dig_searchlist_t) search_list;
85

86
isc_boolean_t
87
	have_ipv4 = ISC_FALSE,
88 89 90
	have_ipv6 = ISC_FALSE,
	specified_source = ISC_FALSE,
	free_now = ISC_FALSE,
91
	cancel_now = ISC_FALSE,
92 93
	usesearch = ISC_FALSE,
	qr = ISC_FALSE,
94
	is_dst_up = ISC_FALSE;
95
in_port_t port = 53;
96
unsigned int timeout = 0;
97 98
isc_mem_t *mctx = NULL;
isc_taskmgr_t *taskmgr = NULL;
99
isc_task_t *global_task = NULL;
100 101
isc_timermgr_t *timermgr = NULL;
isc_socketmgr_t *socketmgr = NULL;
102
isc_sockaddr_t bind_address;
Michael Sawyer's avatar
Michael Sawyer committed
103
isc_sockaddr_t bind_any;
104
isc_buffer_t rootbuf;
105
int sendcount = 0;
106
int recvcount = 0;
Michael Sawyer's avatar
Michael Sawyer committed
107
int sockcount = 0;
108
int ndots = -1;
109
int tries = 2;
110
int lookup_counter = 0;
111

112 113 114 115 116 117 118 119 120 121
/*
 * Exit Codes:
 *   0   Everything went well, including things like NXDOMAIN
 *   1   Usage error
 *   7   Got too many RR's or Names
 *   8   Couldn't open batch file
 *   9   No reply from server
 *   10  Internal error
 */
int exitcode = 0;
122
char keynametext[MXNAME];
123
char keyfile[MXNAME] = "";
124
char keysecret[MXNAME] = "";
125 126 127
dns_name_t keyname;
isc_buffer_t *namebuf = NULL;
dns_tsigkey_t *key = NULL;
128
isc_boolean_t validated = ISC_TRUE;
129
isc_entropy_t *entp = NULL;
130
isc_mempool_t *commctx = NULL;
131
isc_boolean_t debugging = ISC_FALSE;
132
isc_boolean_t memdebugging = ISC_FALSE;
Michael Sawyer's avatar
Michael Sawyer committed
133
char *progname = NULL;
134
isc_mutex_t lookup_lock;
135
dig_lookup_t *current_lookup = NULL;
136
isc_uint32_t rr_limit = INT_MAX;
137 138 139

/*
 * Apply and clear locks at the event level in global task.
140
 * Can I get rid of these using shutdown events?  XXX
141 142
 */
#define LOCK_LOOKUP {\
Brian Wellington's avatar
Brian Wellington committed
143 144 145
	debug("lock_lookup %s:%d", __FILE__, __LINE__);\
	check_result(isc_mutex_lock((&lookup_lock)), "isc_mutex_lock");\
	debug("success");\
146 147
}
#define UNLOCK_LOOKUP {\
Brian Wellington's avatar
Brian Wellington committed
148 149 150
	debug("unlock_lookup %s:%d", __FILE__, __LINE__);\
	check_result(isc_mutex_unlock((&lookup_lock)),\
		     "isc_mutex_unlock");\
151
}
152

153
static void
154 155
cancel_lookup(dig_lookup_t *lookup);

Michael Sawyer's avatar
Michael Sawyer committed
156 157 158 159 160 161
static void
recv_done(isc_task_t *task, isc_event_t *event);

static void
connect_timeout(isc_task_t *task, isc_event_t *event);

Mark Andrews's avatar
Mark Andrews committed
162
char *
163 164 165 166 167 168 169 170 171
next_token(char **stringp, const char *delim) {
	char *res;

	do {
		res = strsep(stringp, delim);
		if (res == NULL)
			break;
	} while (*res == '\0');
	return (res);
Brian Wellington's avatar
Brian Wellington committed
172
}
173

174 175 176
static int
count_dots(char *string) {
	char *s;
177
	int i = 0;
178 179

	s = string;
180
	while (*s != '\0') {
181 182 183 184 185 186 187
		if (*s == '.')
			i++;
		s++;
	}
	return (i);
}

188
static void
189
hex_dump(isc_buffer_t *b) {
190 191 192
	unsigned int len;
	isc_region_t r;

193
	isc_buffer_usedregion(b, &r);
194

195
	printf("%d bytes\n", r.length);
196
	for (len = 0; len < r.length; len++) {
197
		printf("%02x ", r.base[len]);
198
		if (len % 16 == 15)
199 200
			printf("\n");
	}
Michael Sawyer's avatar
Michael Sawyer committed
201
	if (len % 16 != 0)
202 203 204
		printf("\n");
}

205 206

isc_result_t
207
get_reverse(char *reverse, char *value, isc_boolean_t nibble) {
208 209 210 211 212 213
	int adrs[4];
	char working[MXNAME];
	int i, n;
	isc_result_t result;

	result = DNS_R_BADDOTTEDQUAD;
214
	reverse[0] = 0;
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254

	debug("get_reverse(%s)", value);
	if (strspn(value, "0123456789.") == strlen(value)) {
		n = sscanf(value, "%d.%d.%d.%d",
			   &adrs[0], &adrs[1],
			   &adrs[2], &adrs[3]);
		if (n == 0) {
			return (DNS_R_BADDOTTEDQUAD);
		}
		for (i = n - 1; i >= 0; i--) {
			snprintf(working, MXNAME/8, "%d.",
				 adrs[i]);
			strncat(reverse, working, MXNAME);
		}
		strncat(reverse, "in-addr.arpa.", MXNAME);
		result = ISC_R_SUCCESS;
	} else if (strspn(value, "0123456789abcdefABCDEF:") 
		   == strlen(value)) {
		isc_netaddr_t addr;
		dns_fixedname_t fname;
		dns_name_t *name;
		isc_buffer_t b;
		
		addr.family = AF_INET6;
		n = inet_pton(AF_INET6, value, &addr.type.in6);
		if (n <= 0)
			return (DNS_R_BADDOTTEDQUAD);
		dns_fixedname_init(&fname);
		name = dns_fixedname_name(&fname);
		result = dns_byaddr_createptrname(&addr, nibble,
						  name);
		if (result != ISC_R_SUCCESS)
			return (result);
		isc_buffer_init(&b, reverse, MXNAME);
		result = dns_name_totext(name, ISC_FALSE, &b);
		isc_buffer_putuint8(&b, 0);
	}
	return (result);
}

255
void
David Lawrence's avatar
David Lawrence committed
256
fatal(const char *format, ...) {
257 258
	va_list args;

259
	fprintf(stderr, "%s: ", progname);
260
	va_start(args, format);
261 262 263
	vfprintf(stderr, format, args);
	va_end(args);
	fprintf(stderr, "\n");
264 265
	if (exitcode < 10)
		exitcode = 10;
266
	exit(exitcode);
267 268
}

269
void
David Lawrence's avatar
David Lawrence committed
270
debug(const char *format, ...) {
271 272
	va_list args;

273
	if (debugging) {
274
		va_start(args, format);
275 276 277 278
		vfprintf(stderr, format, args);
		va_end(args);
		fprintf(stderr, "\n");
	}
279 280
}

281
void
David Lawrence's avatar
David Lawrence committed
282
check_result(isc_result_t result, const char *msg) {
283
	if (result != ISC_R_SUCCESS) {
284
		fatal("%s: %s", msg, isc_result_totext(result));
285
	}
286 287
}

Michael Sawyer's avatar
Michael Sawyer committed
288 289 290 291 292
/*
 * Create a server structure, which is part of the lookup structure.
 * This is little more than a linked list of servers to query in hopes
 * of finding the answer the user is looking for
 */
293 294 295 296 297 298
dig_server_t *
make_server(const char *servname) {
	dig_server_t *srv;

	REQUIRE(servname != NULL);

299
	debug("make_server(%s)", servname);
300 301 302 303 304
	srv = isc_mem_allocate(mctx, sizeof(struct dig_server));
	if (srv == NULL)
		fatal("Memory allocation failure in %s:%d",
		      __FILE__, __LINE__);
	strncpy(srv->servername, servname, MXNAME);
305
	srv->servername[MXNAME-1] = 0;
306
	ISC_LINK_INIT(srv, link);
307 308 309
	return (srv);
}

310
/*
Michael Sawyer's avatar
Michael Sawyer committed
311 312 313
 * Produce a cloned server list.  The dest list must have already had
 * ISC_LIST_INIT applied.
 */
314
void
315
clone_server_list(dig_serverlist_t src,
316 317 318 319 320 321 322 323
		  dig_serverlist_t *dest)
{
	dig_server_t *srv, *newsrv;

	debug("clone_server_list()");
	srv = ISC_LIST_HEAD(src);
	while (srv != NULL) {
		newsrv = make_server(srv->servername);
324
		ISC_LINK_INIT(newsrv, link);
325 326 327 328 329
		ISC_LIST_ENQUEUE(*dest, newsrv, link);
		srv = ISC_LIST_NEXT(srv, link);
	}
}

Michael Sawyer's avatar
Michael Sawyer committed
330 331 332 333 334 335
/*
 * Create an empty lookup structure, which holds all the information needed
 * to get an answer to a user's question.  This structure contains two
 * linked lists: the server list (servers to query) and the query list
 * (outstanding queries which have been made to the listed servers).
 */
336
dig_lookup_t *
337
make_empty_lookup(void) {
338 339
	dig_lookup_t *looknew;

Michael Sawyer's avatar
Michael Sawyer committed
340
	debug("make_empty_lookup()");
341

342
	INSIST(!free_now);
343

344
	looknew = isc_mem_allocate(mctx, sizeof(struct dig_lookup));
345
	if (looknew == NULL)
346
		fatal("Memory allocation failure in %s:%d",
347
		       __FILE__, __LINE__);
348
	looknew->pending = ISC_TRUE;
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
349 350
	looknew->textname[0] = 0;
	looknew->cmdline[0] = 0; /* Not copied in clone_lookup! */
Michael Sawyer's avatar
Michael Sawyer committed
351 352 353 354
	looknew->rdtype = dns_rdatatype_none;
	looknew->rdclass = dns_rdataclass_none;
	looknew->rdtypeset = ISC_FALSE;
	looknew->rdclassset = ISC_FALSE;
355
	looknew->sendspace = NULL;
356 357 358
	looknew->sendmsg = NULL;
	looknew->name = NULL;
	looknew->oname = NULL;
359 360
	looknew->timer = NULL;
	looknew->xfr_q = NULL;
Michael Sawyer's avatar
Michael Sawyer committed
361
	looknew->current_query = NULL;
362 363 364 365 366
	looknew->doing_xfr = ISC_FALSE;
	looknew->ixfr_serial = ISC_FALSE;
	looknew->trace = ISC_FALSE;
	looknew->trace_root = ISC_FALSE;
	looknew->identify = ISC_FALSE;
Michael Sawyer's avatar
Michael Sawyer committed
367
	looknew->ignore = ISC_FALSE;
368
	looknew->servfail_stops = ISC_FALSE;
369
	looknew->besteffort = ISC_TRUE;
370
	looknew->dnssec = ISC_FALSE;
371 372
	looknew->udpsize = 0;
	looknew->recurse = ISC_TRUE;
Michael Sawyer's avatar
Michael Sawyer committed
373
	looknew->aaonly = ISC_FALSE;
374 375 376 377 378 379 380 381
	looknew->adflag = ISC_FALSE;
	looknew->cdflag = ISC_FALSE;
	looknew->ns_search_only = ISC_FALSE;
	looknew->origin = NULL;
	looknew->querysig = NULL;
	looknew->retries = tries;
	looknew->nsfound = 0;
	looknew->tcp_mode = ISC_FALSE;
382
	looknew->nibble = ISC_FALSE;
383 384 385 386 387 388 389
	looknew->comments = ISC_TRUE;
	looknew->stats = ISC_TRUE;
	looknew->section_question = ISC_TRUE;
	looknew->section_answer = ISC_TRUE;
	looknew->section_authority = ISC_TRUE;
	looknew->section_additional = ISC_TRUE;
	looknew->new_search = ISC_FALSE;
390
#ifdef DNS_OPT_NEWCODES_LIVE
391 392
	looknew->zonename[0] = 0;
	looknew->viewname[0] = 0;
393
#endif /* DNS_OPT_NEWCODES_LIVE */
394
	ISC_LINK_INIT(looknew, link);
395
	ISC_LIST_INIT(looknew->q);
396
	ISC_LIST_INIT(looknew->my_server_list);
397 398 399
	return (looknew);
}

400
/*
Michael Sawyer's avatar
Michael Sawyer committed
401 402 403
 * Clone a lookup, perhaps copying the server list.  This does not clone
 * the query list, since it will be regenerated by the setup_lookup()
 * function, nor does it queue up the new lookup for processing.
404 405 406
 * Caution: If you don't clone the servers, you MUST clone the server
 * list seperately from somewhere else, or construct it by hand.
 */
407 408 409 410 411 412 413 414 415 416 417
dig_lookup_t *
clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
	dig_lookup_t *looknew;

	debug("clone_lookup()");

	INSIST(!free_now);

	looknew = make_empty_lookup();
	INSIST(looknew != NULL);
	strncpy(looknew->textname, lookold-> textname, MXNAME);
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
418
	looknew->textname[MXNAME-1] = 0;
419 420
	looknew->rdtype = lookold->rdtype;
	looknew->rdclass = lookold->rdclass;
Michael Sawyer's avatar
Michael Sawyer committed
421 422
	looknew->rdtypeset = lookold->rdtypeset;
	looknew->rdclassset = lookold->rdclassset;
423
	looknew->doing_xfr = lookold->doing_xfr;
Michael Sawyer's avatar
Michael Sawyer committed
424
	looknew->ixfr_serial = lookold->ixfr_serial;
425 426 427
	looknew->trace = lookold->trace;
	looknew->trace_root = lookold->trace_root;
	looknew->identify = lookold->identify;
Michael Sawyer's avatar
Michael Sawyer committed
428
	looknew->ignore = lookold->ignore;
429
	looknew->servfail_stops = lookold->servfail_stops;
430
	looknew->besteffort = lookold->besteffort;
431
	looknew->dnssec = lookold->dnssec;
432 433
	looknew->udpsize = lookold->udpsize;
	looknew->recurse = lookold->recurse;
Brian Wellington's avatar
Brian Wellington committed
434
	looknew->aaonly = lookold->aaonly;
435 436
	looknew->adflag = lookold->adflag;
	looknew->cdflag = lookold->cdflag;
437 438 439 440 441 442 443 444
	looknew->ns_search_only = lookold->ns_search_only;
	looknew->tcp_mode = lookold->tcp_mode;
	looknew->comments = lookold->comments;
	looknew->stats = lookold->stats;
	looknew->section_question = lookold->section_question;
	looknew->section_answer = lookold->section_answer;
	looknew->section_authority = lookold->section_authority;
	looknew->section_additional = lookold->section_additional;
Michael Sawyer's avatar
Michael Sawyer committed
445
	looknew->retries = lookold->retries;
446
#ifdef DNS_OPT_NEWCODES_LIVE
447 448
	strncpy(looknew->viewname, lookold-> viewname, MXNAME);
	strncpy(looknew->zonename, lookold-> zonename, MXNAME);
449
#endif /* DNS_OPT_NEWCODES_LIVE */
450

451 452 453
	if (servers)
		clone_server_list(lookold->my_server_list,
				  &looknew->my_server_list);
454 455 456
	return (looknew);
}

Michael Sawyer's avatar
Michael Sawyer committed
457 458 459 460 461 462 463 464
/*
 * Requeue a lookup for further processing, perhaps copying the server
 * list.  The new lookup structure is returned to the caller, and is
 * queued for processing.  If servers are not cloned in the requeue, they
 * must be added before allowing the current event to complete, since the
 * completion of the event may result in the next entry on the lookup
 * queue getting run.
 */
465 466 467 468 469 470 471 472 473 474 475 476 477
dig_lookup_t *
requeue_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
	dig_lookup_t *looknew;

	debug("requeue_lookup()");

	lookup_counter++;
	if (lookup_counter > LOOKUP_LIMIT)
		fatal("Too many lookups");

	looknew = clone_lookup(lookold, servers);
	INSIST(looknew != NULL);

Michael Sawyer's avatar
Michael Sawyer committed
478
	debug("before insertion, init@%p "
479 480
	       "-> %p, new@%p -> %p",
	      lookold, lookold->link.next, looknew, looknew->link.next);
Michael Sawyer's avatar
Michael Sawyer committed
481
	ISC_LIST_PREPEND(lookup_list, looknew, link);
Michael Sawyer's avatar
Michael Sawyer committed
482
	debug("after insertion, init -> "
483
	      "%p, new = %p, new -> %p",
484
	      lookold, looknew, looknew->link.next);
485
	return (looknew);
486
}
487

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503

static void
setup_text_key(void) {
	isc_result_t result;
	isc_buffer_t secretbuf;
	int secretsize;
	unsigned char *secretstore;
	isc_stdtime_t now;

	debug("setup_text_key()");
	result = isc_buffer_allocate(mctx, &namebuf, MXNAME);
	check_result(result, "isc_buffer_allocate");
	dns_name_init(&keyname, NULL);
	check_result(result, "dns_name_init");
	isc_buffer_putstr(namebuf, keynametext);
	secretsize = strlen(keysecret) * 3 / 4;
504
	secretstore = isc_mem_allocate(mctx, secretsize);
505 506 507 508 509 510 511 512 513
	if (secretstore == NULL)
		fatal("Memory allocation failure in %s:%d",
		      __FILE__, __LINE__);
	isc_buffer_init(&secretbuf, secretstore, secretsize);
	result = isc_base64_decodestring(mctx, keysecret,
					 &secretbuf);
	if (result != ISC_R_SUCCESS) {
		printf(";; Couldn't create key %s: %s\n",
		       keynametext, isc_result_totext(result));
514
		goto failure;
515 516 517
	}
	secretsize = isc_buffer_usedlength(&secretbuf);
	isc_stdtime_get(&now);
518

519 520 521 522 523 524
	result = dns_name_fromtext(&keyname, namebuf,
				   dns_rootname, ISC_FALSE,
				   namebuf);
	if (result != ISC_R_SUCCESS) {
		printf(";; Couldn't create key %s: %s\n",
		       keynametext, dns_result_totext(result));
525
		goto failure;
526 527 528 529
	}
	result = dns_tsigkey_create(&keyname, dns_tsig_hmacmd5_name,
				    secretstore, secretsize,
				    ISC_TRUE, NULL, now, now, mctx,
530
				    NULL, &key);
531 532 533 534
	if (result != ISC_R_SUCCESS) {
		printf(";; Couldn't create key %s: %s\n",
		       keynametext, dns_result_totext(result));
	}
535
 failure:
536
	isc_mem_free(mctx, secretstore);
537 538 539 540 541 542 543 544 545 546 547 548 549 550
	dns_name_invalidate(&keyname);
	isc_buffer_free(&namebuf);
}


static void
setup_file_key(void) {
	isc_result_t result;
	isc_buffer_t secretbuf;
	unsigned char *secretstore = NULL;
	int secretlen;
	dst_key_t *dstkey = NULL;
	isc_stdtime_t now;

551

552 553 554 555 556 557 558 559
	debug("setup_file_key()");
	result = dst_key_fromnamedfile(keyfile, DST_TYPE_PRIVATE,
				       mctx, &dstkey);
	if (result != ISC_R_SUCCESS) {
		fprintf(stderr, "Couldn't read key from %s: %s\n",
			keyfile, isc_result_totext(result));
		goto failure;
	}
560 561 562
	/*
	 * Get key size in bits, convert to bytes, rounding up (?)
	 */
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
	secretlen = (dst_key_size(dstkey) + 7) >> 3;
	secretstore = isc_mem_allocate(mctx, secretlen);
	if (secretstore == NULL)
		fatal("out of memory");
	isc_buffer_init(&secretbuf, secretstore, secretlen);
	result = dst_key_tobuffer(dstkey, &secretbuf);
	if (result != ISC_R_SUCCESS) {
		fprintf(stderr, "Couldn't read key from %s: %s\n",
			keyfile, isc_result_totext(result));
		goto failure;
	}
	isc_stdtime_get(&now);
	dns_name_init(&keyname, NULL);
	dns_name_clone(dst_key_name(dstkey), &keyname);
	result = dns_tsigkey_create(&keyname, dns_tsig_hmacmd5_name,
				    secretstore, secretlen,
				    ISC_TRUE, NULL, now, now, mctx,
580
				    NULL, &key);
581 582 583 584 585 586 587 588 589 590 591
	if (result != ISC_R_SUCCESS) {
		printf(";; Couldn't create key %s: %s\n",
		       keynametext, dns_result_totext(result));
	}
 failure:
	if (dstkey != NULL)
		dst_key_free(&dstkey);
	if (secretstore != NULL)
		isc_mem_free(mctx, secretstore);
}

592 593 594 595 596 597 598 599 600 601 602 603 604
static dig_searchlist_t *
make_searchlist_entry(char *domain) {
	dig_searchlist_t *search;
	search = isc_mem_allocate(mctx, sizeof(*search));
	if (search == NULL)
		fatal("Memory allocation failure in %s:%d",
		      __FILE__, __LINE__);
	strncpy(search->origin, domain, MXNAME);
	search->origin[MXNAME-1] = 0;
	ISC_LINK_INIT(search, link);
	return (search);
}

Michael Sawyer's avatar
Michael Sawyer committed
605 606 607 608
/*
 * Setup the system as a whole, reading key information and resolv.conf
 * settings.
 */
609
void
610
setup_system(void) {
611 612 613 614
	char rcinput[MXNAME];
	FILE *fp;
	char *ptr;
	dig_server_t *srv;
615
	dig_searchlist_t *search, *domain = NULL;
616
	isc_boolean_t get_servers;
617
	char *input;
618

619
	debug("setup_system()");
Michael Sawyer's avatar
Michael Sawyer committed
620

621
	free_now = ISC_FALSE;
622
	get_servers = ISC_TF(server_list.head == NULL);
623
	fp = fopen(RESOLVCONF, "r");
624
	/* XXX Use lwres resolv.conf reader */
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
	if (fp == NULL)
		goto no_file;

	while (fgets(rcinput, MXNAME, fp) != 0) {
		input = rcinput;
		ptr = next_token(&input, " \t\r\n");
		if (ptr != NULL) {
			if (get_servers &&
			    strcasecmp(ptr, "nameserver") == 0) {
				debug("got a nameserver line");
				ptr = next_token(&input, " \t\r\n");
				if (ptr != NULL) {
					srv = make_server(ptr);
					ISC_LIST_APPEND(server_list, srv, link);
				}
			} else if (strcasecmp(ptr, "options") == 0) {
				ptr = next_token(&input, " \t\r\n");
				if (ptr != NULL) {
					if((strncasecmp(ptr, "ndots:",
							6) == 0) &&
					   (ndots == -1)) {
						ndots = atoi(
							     &ptr[6]);
						debug("ndots is %d.",
						      ndots);
650
					}
651
				}
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
			} else if (strcasecmp(ptr, "search") == 0){
				while ((ptr = next_token(&input, " \t\r\n"))
				       != NULL) {
					debug("adding search %s", ptr);
					search = make_searchlist_entry(ptr);
					ISC_LIST_INITANDAPPEND(search_list,
							       search, link);
				}
			} else if (strcasecmp(ptr, "domain") == 0) {
				while ((ptr = next_token(&input, " \t\r\n"))
				       != NULL) {
					if (domain != NULL)
						isc_mem_free(mctx, domain);
					domain = make_searchlist_entry(ptr);
				}
667 668 669
			}
		}
	}
670 671
	fclose(fp);
 no_file:
672

673
	if (ISC_LIST_EMPTY(search_list) && domain != NULL) {
674
		ISC_LIST_INITANDAPPEND(search_list, domain, link);
675 676 677 678 679
		domain = NULL;
	}
	if (domain != NULL)
		isc_mem_free(mctx, domain);
	
680 681 682
	if (ndots == -1)
		ndots = 1;

683
	if (server_list.head == NULL) {
684
		srv = make_server("127.0.0.1");
685 686
		ISC_LIST_APPEND(server_list, srv, link);
	}
687

688 689 690 691
	if (keyfile[0] != 0)
		setup_file_key();
	else if (keysecret[0] != 0)
		setup_text_key();
692
}
693

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
static void
clear_searchlist(void) {
	dig_searchlist_t *search;
	while ((search = ISC_LIST_HEAD(search_list)) != NULL) {
		ISC_LIST_UNLINK(search_list, search, link);
		isc_mem_free(mctx, search);
	}
}

/*
 * Override the search list derived from resolv.conf by 'domain'.
 */
void
set_search_domain(char *domain) {
	dig_searchlist_t *search;
	
	clear_searchlist();
	search = make_searchlist_entry(domain);
	ISC_LIST_APPEND(search_list, search, link);
}

Michael Sawyer's avatar
Michael Sawyer committed
715 716 717
/*
 * Setup the ISC and DNS libraries for use by the system.
 */
718
void
719
setup_libs(void) {
720 721
	isc_result_t result;

722
	debug("setup_libs()");
723 724 725 726 727 728

	/*
	 * Warning: This is not particularly good randomness.  We'll
	 * just use random() now for getting id values, but doing so
	 * does NOT insure that id's cann't be guessed.
	 */
729
	srandom(getpid() + (int)&setup_libs);
730

731
	result = isc_net_probeipv4();
732 733
	if (result == ISC_R_SUCCESS)
		have_ipv4 = ISC_TRUE;
734 735 736

	result = isc_net_probeipv6();
	if (result == ISC_R_SUCCESS)
737
		have_ipv6 = ISC_TRUE;
738 739
	if (!have_ipv6 && !have_ipv4)
		fatal("can't find either v4 or v6 networking");
740

741 742
	result = isc_mem_create(0, 0, &mctx);
	check_result(result, "isc_mem_create");
743

744
	result = isc_taskmgr_create(mctx, 1, 0, &taskmgr);
745
	check_result(result, "isc_taskmgr_create");
746

747
	result = isc_task_create(taskmgr, 0, &global_task);
748
	check_result(result, "isc_task_create");
749

750
	result = isc_timermgr_create(mctx, &timermgr);
751
	check_result(result, "isc_timermgr_create");
752

753
	result = isc_socketmgr_create(mctx, &socketmgr);
754
	check_result(result, "isc_socketmgr_create");
755

756
	result = isc_entropy_create(mctx, &entp);
757 758
	check_result(result, "isc_entropy_create");

759
	result = dst_lib_init(mctx, entp, 0);
760 761
	check_result(result, "dst_lib_init");
	is_dst_up = ISC_TRUE;
762 763 764 765 766 767 768 769 770 771

	result = isc_mempool_create(mctx, COMMSIZE, &commctx);
	check_result(result, "isc_mempool_create");
	isc_mempool_setname(commctx, "COMMPOOL");
	/*
	 * 6 and 2 set as reasonable parameters for 3 or 4 nameserver
	 * systems.
	 */
	isc_mempool_setfreemax(commctx, 6);
	isc_mempool_setfillcount(commctx, 2);
772 773 774

	result = isc_mutex_init(&lookup_lock);
	check_result(result, "isc_mutex_init");
775 776

	dns_result_register();
777 778
}

Michael Sawyer's avatar
Michael Sawyer committed
779 780 781 782
/*
 * Add EDNS0 option record to a message.  Currently, the only supported
 * option is UDP buffer size.
 */
783
static void
784 785 786 787 788
add_opt(dns_message_t *msg, isc_uint16_t udpsize, isc_boolean_t dnssec
#ifdef DNS_OPT_NEWCODES_LIVE
	, dns_optlist_t optlist
#endif /* DNS_OPT_NEWCODES_LIVE */
	)
789
{
790 791 792 793
	dns_rdataset_t *rdataset = NULL;
	dns_rdatalist_t *rdatalist = NULL;
	dns_rdata_t *rdata = NULL;
	isc_result_t result;
794
#ifdef DNS_OPT_NEWCODES_LIVE
795 796
	isc_buffer_t *rdatabuf = NULL;
	unsigned int i, optsize = 0;
797
#endif /* DNS_OPT_NEWCODES_LIVE */
798

799
	debug("add_opt()");
800
	result = dns_message_gettemprdataset(msg, &rdataset);
801 802
	check_result(result, "dns_message_gettemprdataset");
	dns_rdataset_init(rdataset);
803
	result = dns_message_gettemprdatalist(msg, &rdatalist);
804
	check_result(result, "dns_message_gettemprdatalist");
805
	result = dns_message_gettemprdata(msg, &rdata);
806
	check_result(result, "dns_message_gettemprdata");
807

Michael Sawyer's avatar
Michael Sawyer committed
808
	debug("setting udp size of %d", udpsize);
809 810 811 812
	rdatalist->type = dns_rdatatype_opt;
	rdatalist->covers = 0;
	rdatalist->rdclass = udpsize;
	rdatalist->ttl = 0;
813 814
	if (dnssec)
		rdatalist->ttl = DNS_MESSAGEEXTFLAG_DO;
815 816
	rdata->data = NULL;
	rdata->length = 0;
817
#ifdef DNS_OPT_NEWCODES_LIVE
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
818
	for (i = 0; i < optlist.used; i++)
819 820 821 822 823 824
		optsize += optlist.attrs[i].value.length + 4;
	result = isc_buffer_allocate(mctx, &rdatabuf, optsize);
	check_result(result, "isc_buffer_allocate");
	result = dns_opt_add(rdata, &optlist, rdatabuf);
	check_result(result, "dns_opt_add");
	dns_message_takebuffer(msg, &rdatabuf);
825
#endif /* DNS_OPT_NEWCODES_LIVE */
826 827 828 829
	ISC_LIST_INIT(rdatalist->rdata);
	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
	dns_rdatalist_tordataset(rdatalist, rdataset);
	result = dns_message_setopt(msg, rdataset);
830
	check_result(result, "dns_message_setopt");
831 832
}

Michael Sawyer's avatar
Michael Sawyer committed
833
/*
834
 * Add a question section to a message, asking for the specified name,
Michael Sawyer's avatar
Michael Sawyer committed
835 836
 * type, and class.
 */
837
static void
Michael Sawyer's avatar
Michael Sawyer committed
838 839
add_question(dns_message_t *message, dns_name_t *name,
	     dns_rdataclass_t rdclass, dns_rdatatype_t rdtype)
840 841 842 843
{
	dns_rdataset_t *rdataset;
	isc_result_t result;

844
	debug("add_question()");
845 846 847 848 849 850 851 852
	rdataset = NULL;
	result = dns_message_gettemprdataset(message, &rdataset);
	check_result(result, "dns_message_gettemprdataset()");
	dns_rdataset_init(rdataset);
	dns_rdataset_makequestion(rdataset, rdclass, rdtype);
	ISC_LIST_APPEND(name->list, rdataset, link);
}

Michael Sawyer's avatar
Michael Sawyer committed
853
/*
Michael Sawyer's avatar
Michael Sawyer committed
854 855 856 857 858 859
 * Check if we're done with all the queued lookups, which is true iff
 * all sockets, sends, and recvs are accounted for (counters == 0),
 * and the lookup list is empty.
 * If we are done, pass control back out to dighost_shutdown() (which is
 * part of dig.c, host.c, or nslookup.c) to either shutdown the system as
 * a whole or reseed the lookup list.
Michael Sawyer's avatar
Michael Sawyer committed
860
 */
861 862 863
static void
check_if_done(void) {
	debug("check_if_done()");
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
864
	debug("list %s", ISC_LIST_EMPTY(lookup_list) ? "empty" : "full");
865 866
	if (ISC_LIST_EMPTY(lookup_list) && current_lookup == NULL &&
	    sendcount == 0) {
867 868
		INSIST(sockcount == 0);
		INSIST(recvcount == 0);
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
		debug("shutting down");
		dighost_shutdown();
	}
}

/*
 * Clear out a query when we're done with it.  WARNING: This routine
 * WILL invalidate the query pointer.
 */
static void
clear_query(dig_query_t *query) {
	dig_lookup_t *lookup;

	REQUIRE(query != NULL);

Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
884
	debug("clear_query(%p)", query);
885

886
	lookup = query->lookup;
887

Michael Sawyer's avatar
Michael Sawyer committed
888 889 890
	if (lookup->current_query == query)
		lookup->current_query = NULL;

891 892 893 894 895 896 897
	ISC_LIST_UNLINK(lookup->q, query, link);
	if (ISC_LINK_LINKED(&query->recvbuf, link))
		ISC_LIST_DEQUEUE(query->recvlist, &query->recvbuf,
				 link);
	if (ISC_LINK_LINKED(&query->lengthbuf, link))
		ISC_LIST_DEQUEUE(query->lengthlist, &query->lengthbuf,
				 link);
898
	INSIST(query->recvspace != NULL);
899 900 901 902 903
	if (query->sock != NULL) {
		isc_socket_detach(&query->sock);
		sockcount--;
		debug("sockcount=%d", sockcount);
	}
904
	isc_mempool_put(commctx, query->recvspace);
905 906 907 908
	isc_buffer_invalidate(&query->recvbuf);
	isc_buffer_invalidate(&query->lengthbuf);
	isc_mem_free(mctx, query);
}
909

Michael Sawyer's avatar
Michael Sawyer committed
910 911 912 913 914
/*
 * Try and clear out a lookup if we're done with it.  Return ISC_TRUE if
 * the lookup was successfully cleared.  If ISC_TRUE is returned, the
 * lookup pointer has been invalidated.
 */
Michael Sawyer's avatar
Michael Sawyer committed
915
static isc_boolean_t
916 917
try_clear_lookup(dig_lookup_t *lookup) {
	dig_server_t *s;
918
	dig_query_t *q;
919 920 921 922
	void *ptr;

	REQUIRE(lookup != NULL);

923 924 925
	debug("try_clear_lookup(%p)", lookup);

	if (ISC_LIST_HEAD(lookup->q) != NULL) {
926 927 928
		if (debugging) {
			q = ISC_LIST_HEAD(lookup->q);
			while (q != NULL) {
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
929
				debug("query to %s still pending",
930 931 932
				       q->servname);
				q = ISC_LIST_NEXT(q, link);
			}
933
		return (ISC_FALSE);
934
		}
935
	}
936 937 938 939
	/*
	 * At this point, we know there are no queries on the lookup,
	 * so can make it go away also.
	 */
940
	debug("cleared");
941 942 943 944 945 946
	s = ISC_LIST_HEAD(lookup->my_server_list);
	while (s != NULL) {
		debug("freeing server %p belonging to %p",
		      s, lookup);
		ptr = s;
		s = ISC_LIST_NEXT(s, link);
947
		ISC_LIST_DEQUEUE(lookup->my_server_list,
948 949
				 (dig_server_t *)ptr, link);
		isc_mem_free(mctx, ptr);
950 951 952 953 954 955 956 957 958
	}
	if (lookup->sendmsg != NULL)
		dns_message_destroy(&lookup->sendmsg);
	if (lookup->querysig != NULL) {
		debug("freeing buffer %p", lookup->querysig);
		isc_buffer_free(&lookup->querysig);
	}
	if (lookup->timer != NULL)
		isc_timer_detach(&lookup->timer);
959
	if (lookup->sendspace != NULL)
960
		isc_mempool_put(commctx, lookup->sendspace);
961

962
	isc_mem_free(mctx, lookup);
963
	return (ISC_TRUE);
964 965
}

966 967 968 969

/*
 * If we can, start the next lookup in the queue running.
 * This assumes that the lookup on the head of the queue hasn't been
970 971
 * started yet.  It also removes the lookup from the head of the queue,
 * setting the current_lookup pointer pointing to it.
972
 */
973 974 975
void
start_lookup(void) {
	debug("start_lookup()");
976 977
	if (cancel_now)
		return;
978 979 980 981 982 983 984

	/*
	 * If there's a current lookup running, we really shouldn't get
	 * here.
	 */
	INSIST(current_lookup == NULL);

985
	current_lookup = ISC_LIST_HEAD(lookup_list);
986 987 988
	/*
	 * Put the current lookup somewhere so cancel_all can find it
	 */
989