[BACK]Return to nslookup.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / dig

File: [local] / src / usr.bin / dig / nslookup.c (download)

Revision 1.9, Tue Feb 25 17:01:56 2020 UTC (4 years, 3 months ago) by deraadt
Branch: MAIN
Changes since 1.8: +16 -4 lines

traditional nslookup would not fatal if the server name was undiscoverable.
change lower level code to return an error, and bump the fatal for host(1)
upwards, then tweak nslookup to work nicely

/*
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
 *
 * Permission to use, copy, modify, and/or 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.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC 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.
 */

#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <isc/app.h>
#include <isc/buffer.h>
#include <isc/event.h>
#include <isc/util.h>
#include <isc/task.h>

#include <dns/message.h>
#include <dns/name.h>
#include <dns/fixedname.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdataset.h>
#include <dns/rdatatype.h>

#include "dig.h"

static isc_boolean_t short_form = ISC_TRUE,
	tcpmode = ISC_FALSE,
	identify = ISC_FALSE, stats = ISC_TRUE,
	comments = ISC_TRUE, section_question = ISC_TRUE,
	section_answer = ISC_TRUE, section_authority = ISC_TRUE,
	section_additional = ISC_TRUE, recurse = ISC_TRUE,
	aaonly = ISC_FALSE, nofail = ISC_TRUE;

static isc_boolean_t interactive;

static isc_boolean_t in_use = ISC_FALSE;
static char defclass[MXRD] = "IN";
static char deftype[MXRD] = "A";
static isc_event_t *global_event = NULL;
static int query_error = 1, print_error = 0;

static char domainopt[DNS_NAME_MAXTEXT];

static const char *rcodetext[] = {
	"NOERROR",
	"FORMERR",
	"SERVFAIL",
	"NXDOMAIN",
	"NOTIMP",
	"REFUSED",
	"YXDOMAIN",
	"YXRRSET",
	"NXRRSET",
	"NOTAUTH",
	"NOTZONE",
	"RESERVED11",
	"RESERVED12",
	"RESERVED13",
	"RESERVED14",
	"RESERVED15",
	"BADVERS"
};

static const char *rtypetext[] = {
	"rtype_0 = ",			/* 0 */
	"internet address = ",		/* 1 */
	"nameserver = ",		/* 2 */
	"md = ",			/* 3 */
	"mf = ",			/* 4 */
	"canonical name = ",		/* 5 */
	"soa = ",			/* 6 */
	"mb = ",			/* 7 */
	"mg = ",			/* 8 */
	"mr = ",			/* 9 */
	"rtype_10 = ",			/* 10 */
	"protocol = ",			/* 11 */
	"name = ",			/* 12 */
	"hinfo = ",			/* 13 */
	"minfo = ",			/* 14 */
	"mail exchanger = ",		/* 15 */
	"text = ",			/* 16 */
	"rp = ",       			/* 17 */
	"afsdb = ",			/* 18 */
	"x25 address = ",		/* 19 */
	"isdn address = ",		/* 20 */
	"rt = ",			/* 21 */
	"nsap = ",			/* 22 */
	"nsap_ptr = ",			/* 23 */
	"signature = ",			/* 24 */
	"key = ",			/* 25 */
	"px = ",			/* 26 */
	"gpos = ",			/* 27 */
	"has AAAA address ",		/* 28 */
	"loc = ",			/* 29 */
	"next = ",			/* 30 */
	"rtype_31 = ",			/* 31 */
	"rtype_32 = ",			/* 32 */
	"service = ",			/* 33 */
	"rtype_34 = ",			/* 34 */
	"naptr = ",			/* 35 */
	"kx = ",			/* 36 */
	"cert = ",			/* 37 */
	"v6 address = ",		/* 38 */
	"dname = ",			/* 39 */
	"rtype_40 = ",			/* 40 */
	"optional = "			/* 41 */
};

#define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0]))

static void flush_lookup_list(void);
static void getinput(isc_task_t *task, isc_event_t *event);

static char *
rcode_totext(dns_rcode_t rcode)
{
	static char buf[sizeof("?65535")];
	union {
		const char *consttext;
		char *deconsttext;
	} totext;

	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
		snprintf(buf, sizeof(buf), "?%u", rcode);
		totext.deconsttext = buf;
	} else
		totext.consttext = rcodetext[rcode];
	return totext.deconsttext;
}

static void
query_finished(void) {
	isc_event_t *event = global_event;

	flush_lookup_list();
	debug("dighost_shutdown()");

	if (!in_use) {
		isc_app_shutdown();
		return;
	}

	isc_task_send(global_task, &event);
}

static void
printsoa(dns_rdata_t *rdata) {
	dns_rdata_soa_t soa;
	isc_result_t result;
	char namebuf[DNS_NAME_FORMATSIZE];

	result = dns_rdata_tostruct_soa(rdata, &soa);
	check_result(result, "dns_rdata_tostruct_soa");

	dns_name_format(&soa.origin, namebuf, sizeof(namebuf));
	printf("\torigin = %s\n", namebuf);
	dns_name_format(&soa.contact, namebuf, sizeof(namebuf));
	printf("\tmail addr = %s\n", namebuf);
	printf("\tserial = %u\n", soa.serial);
	printf("\trefresh = %u\n", soa.refresh);
	printf("\tretry = %u\n", soa.retry);
	printf("\texpire = %u\n", soa.expire);
	printf("\tminimum = %u\n", soa.minimum);
	dns_rdata_freestruct_soa(&soa);
}

static void
printa(dns_rdata_t *rdata) {
	isc_result_t result;
	char text[sizeof("255.255.255.255")];
	isc_buffer_t b;

	isc_buffer_init(&b, text, sizeof(text));
	result = dns_rdata_totext(rdata, NULL, &b);
	check_result(result, "dns_rdata_totext");
	printf("Address: %.*s\n", (int)isc_buffer_usedlength(&b),
	       (char *)isc_buffer_base(&b));
}
static void
printrdata(dns_rdata_t *rdata) {
	isc_result_t result;
	isc_buffer_t *b = NULL;
	unsigned int size = 1024;
	isc_boolean_t done = ISC_FALSE;

	if (rdata->type < N_KNOWN_RRTYPES)
		printf("%s", rtypetext[rdata->type]);
	else
		printf("rdata_%d = ", rdata->type);

	while (!done) {
		result = isc_buffer_allocate(&b, size);
		if (result != ISC_R_SUCCESS)
			check_result(result, "isc_buffer_allocate");
		result = dns_rdata_totext(rdata, NULL, b);
		if (result == ISC_R_SUCCESS) {
			printf("%.*s\n", (int)isc_buffer_usedlength(b),
			       (char *)isc_buffer_base(b));
			done = ISC_TRUE;
		} else if (result != ISC_R_NOSPACE)
			check_result(result, "dns_rdata_totext");
		isc_buffer_free(&b);
		size *= 2;
	}
}

static isc_result_t
printsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
	     dns_section_t section) {
	isc_result_t result, loopresult;
	dns_name_t *name;
	dns_rdataset_t *rdataset = NULL;
	dns_rdata_t rdata = DNS_RDATA_INIT;
	char namebuf[DNS_NAME_FORMATSIZE];

	UNUSED(query);
	UNUSED(headers);

	debug("printsection()");

	result = dns_message_firstname(msg, section);
	if (result == ISC_R_NOMORE)
		return (ISC_R_SUCCESS);
	else if (result != ISC_R_SUCCESS)
		return (result);
	for (;;) {
		name = NULL;
		dns_message_currentname(msg, section,
					&name);
		for (rdataset = ISC_LIST_HEAD(name->list);
		     rdataset != NULL;
		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
			loopresult = dns_rdataset_first(rdataset);
			while (loopresult == ISC_R_SUCCESS) {
				dns_rdataset_current(rdataset, &rdata);
				switch (rdata.type) {
				case dns_rdatatype_a:
					if (section != DNS_SECTION_ANSWER)
						goto def_short_section;
					dns_name_format(name, namebuf,
							sizeof(namebuf));
					printf("Name:\t%s\n", namebuf);
					printa(&rdata);
					break;
				case dns_rdatatype_soa:
					dns_name_format(name, namebuf,
							sizeof(namebuf));
					printf("%s\n", namebuf);
					printsoa(&rdata);
					break;
				default:
				def_short_section:
					dns_name_format(name, namebuf,
							sizeof(namebuf));
					printf("%s\t", namebuf);
					printrdata(&rdata);
					break;
				}
				dns_rdata_reset(&rdata);
				loopresult = dns_rdataset_next(rdataset);
			}
		}
		result = dns_message_nextname(msg, section);
		if (result == ISC_R_NOMORE)
			break;
		else if (result != ISC_R_SUCCESS) {
			return (result);
		}
	}
	return (ISC_R_SUCCESS);
}

static isc_result_t
detailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
	     dns_section_t section) {
	isc_result_t result, loopresult;
	dns_name_t *name;
	dns_rdataset_t *rdataset = NULL;
	dns_rdata_t rdata = DNS_RDATA_INIT;
	char namebuf[DNS_NAME_FORMATSIZE];

	UNUSED(query);

	debug("detailsection()");

	if (headers) {
		switch (section) {
		case DNS_SECTION_QUESTION:
			puts("    QUESTIONS:");
			break;
		case DNS_SECTION_ANSWER:
			puts("    ANSWERS:");
			break;
		case DNS_SECTION_AUTHORITY:
			puts("    AUTHORITY RECORDS:");
			break;
		case DNS_SECTION_ADDITIONAL:
			puts("    ADDITIONAL RECORDS:");
			break;
		}
	}

	result = dns_message_firstname(msg, section);
	if (result == ISC_R_NOMORE)
		return (ISC_R_SUCCESS);
	else if (result != ISC_R_SUCCESS)
		return (result);
	for (;;) {
		name = NULL;
		dns_message_currentname(msg, section,
					&name);
		for (rdataset = ISC_LIST_HEAD(name->list);
		     rdataset != NULL;
		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
			if (section == DNS_SECTION_QUESTION) {
				dns_name_format(name, namebuf,
						sizeof(namebuf));
				printf("\t%s, ", namebuf);
				dns_rdatatype_format(rdataset->type,
						     namebuf,
						     sizeof(namebuf));
				printf("type = %s, ", namebuf);
				dns_rdataclass_format(rdataset->rdclass,
						      namebuf,
						      sizeof(namebuf));
				printf("class = %s\n", namebuf);
			}
			loopresult = dns_rdataset_first(rdataset);
			while (loopresult == ISC_R_SUCCESS) {
				dns_rdataset_current(rdataset, &rdata);

				dns_name_format(name, namebuf,
						sizeof(namebuf));
				printf("    ->  %s\n", namebuf);

				switch (rdata.type) {
				case dns_rdatatype_soa:
					printsoa(&rdata);
					break;
				default:
					printf("\t");
					printrdata(&rdata);
				}
				dns_rdata_reset(&rdata);
				printf("\tttl = %u\n", rdataset->ttl);
				loopresult = dns_rdataset_next(rdataset);
			}
		}
		result = dns_message_nextname(msg, section);
		if (result == ISC_R_NOMORE)
			break;
		else if (result != ISC_R_SUCCESS) {
			return (result);
		}
	}
	return (ISC_R_SUCCESS);
}

static void
received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query)
{
	UNUSED(bytes);
	UNUSED(from);
	UNUSED(query);
}

static void
trying(char *frm, dig_lookup_t *lookup) {
	UNUSED(frm);
	UNUSED(lookup);
}

static isc_result_t
printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
	char servtext[ISC_SOCKADDR_FORMATSIZE];

	/* I've we've gotten this far, we've reached a server. */
	query_error = 0;

	debug("printmessage()");

	isc_sockaddr_format(&query->sockaddr, servtext, sizeof(servtext));
	printf("Server:\t\t%s\n", query->userarg);
	printf("Address:\t%s\n", servtext);

	puts("");

	if (!short_form) {
		puts("------------");
		/*		detailheader(query, msg);*/
		detailsection(query, msg, ISC_TRUE, DNS_SECTION_QUESTION);
		detailsection(query, msg, ISC_TRUE, DNS_SECTION_ANSWER);
		detailsection(query, msg, ISC_TRUE, DNS_SECTION_AUTHORITY);
		detailsection(query, msg, ISC_TRUE, DNS_SECTION_ADDITIONAL);
		puts("------------");
	}

	if (msg->rcode != 0) {
		char nametext[DNS_NAME_FORMATSIZE];
		dns_name_format(query->lookup->name,
				nametext, sizeof(nametext));
		printf("** server can't find %s: %s\n",
		       nametext, rcode_totext(msg->rcode));
		debug("returning with rcode == 0");

		/* the lookup failed */
		print_error |= 1;
		return (ISC_R_SUCCESS);
	}

	if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0)
		puts("Non-authoritative answer:");
	if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER]))
		printsection(query, msg, headers, DNS_SECTION_ANSWER);
	else
		printf("*** Can't find %s: No answer\n",
		       query->lookup->textname);

	if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) &&
	    (query->lookup->rdtype != dns_rdatatype_a)) {
		puts("\nAuthoritative answers can be found from:");
		printsection(query, msg, headers,
			     DNS_SECTION_AUTHORITY);
		printsection(query, msg, headers,
			     DNS_SECTION_ADDITIONAL);
	}
	return (ISC_R_SUCCESS);
}

static void
show_settings(isc_boolean_t full, isc_boolean_t serv_only) {
	dig_server_t *srv;
	isc_sockaddr_t sockaddr;
	dig_searchlist_t *listent;
	isc_result_t result;

	srv = ISC_LIST_HEAD(server_list);

	while (srv != NULL) {
		char sockstr[ISC_SOCKADDR_FORMATSIZE];

		result = get_address(srv->servername, port, &sockaddr);
		check_result(result, "get_address");

		isc_sockaddr_format(&sockaddr, sockstr, sizeof(sockstr));
		printf("Default server: %s\nAddress: %s\n",
			srv->userarg, sockstr);
		if (!full)
			return;
		srv = ISC_LIST_NEXT(srv, link);
	}
	if (serv_only)
		return;
	printf("\nSet options:\n");
	printf("  %s\t\t\t%s\t\t%s\n",
	       tcpmode ? "vc" : "novc",
	       short_form ? "nodebug" : "debug",
	       debugging ? "d2" : "nod2");
	printf("  %s\t\t%s\n",
	       usesearch ? "search" : "nosearch",
	       recurse ? "recurse" : "norecurse");
	printf("  timeout = %u\t\tretry = %d\tport = %u\tndots = %d\n",
	       timeout, tries, port, ndots);
	printf("  querytype = %-8s\tclass = %s\n", deftype, defclass);
	printf("  srchlist = ");
	for (listent = ISC_LIST_HEAD(search_list);
	     listent != NULL;
	     listent = ISC_LIST_NEXT(listent, link)) {
		     printf("%s", listent->origin);
		     if (ISC_LIST_NEXT(listent, link) != NULL)
			     printf("/");
	}
	printf("\n");
}

static isc_boolean_t
testtype(char *typetext) {
	isc_result_t result;
	isc_textregion_t tr;
	dns_rdatatype_t rdtype;

	tr.base = typetext;
	tr.length = strlen(typetext);
	result = dns_rdatatype_fromtext(&rdtype, &tr);
	if (result == ISC_R_SUCCESS)
		return (ISC_TRUE);
	else {
		printf("unknown query type: %s\n", typetext);
		return (ISC_FALSE);
	}
}

static isc_boolean_t
testclass(char *typetext) {
	isc_result_t result;
	isc_textregion_t tr;
	dns_rdataclass_t rdclass;

	tr.base = typetext;
	tr.length = strlen(typetext);
	result = dns_rdataclass_fromtext(&rdclass, &tr);
	if (result == ISC_R_SUCCESS)
		return (ISC_TRUE);
	else {
		printf("unknown query class: %s\n", typetext);
		return (ISC_FALSE);
	}
}

static void
set_port(const char *value) {
	uint32_t n;
	isc_result_t result = parse_uint(&n, value, 65535, "port");
	if (result == ISC_R_SUCCESS)
		port = (uint16_t) n;
}

static void
set_timeout(const char *value) {
	uint32_t n;
	isc_result_t result = parse_uint(&n, value, UINT_MAX, "timeout");
	if (result == ISC_R_SUCCESS)
		timeout = n;
}

static void
set_tries(const char *value) {
	uint32_t n;
	isc_result_t result = parse_uint(&n, value, INT_MAX, "tries");
	if (result == ISC_R_SUCCESS)
		tries = n;
}

static void
set_ndots(const char *value) {
	uint32_t n;
	isc_result_t result = parse_uint(&n, value, 128, "ndots");
	if (result == ISC_R_SUCCESS)
		ndots = n;
}

static void
version(void) {
	fputs("nslookup " VERSION "\n", stderr);
}

static void
setoption(char *opt) {
	size_t l = strlen(opt);

#define CHECKOPT(A, N) \
	((l >= N) && (l < sizeof(A)) && (strncasecmp(opt, A, l) == 0))

	if (CHECKOPT("all", 3)) {
		show_settings(ISC_TRUE, ISC_FALSE);
	} else if (strncasecmp(opt, "class=", 6) == 0) {
		if (testclass(&opt[6]))
			strlcpy(defclass, &opt[6], sizeof(defclass));
	} else if (strncasecmp(opt, "cl=", 3) == 0) {
		if (testclass(&opt[3]))
			strlcpy(defclass, &opt[3], sizeof(defclass));
	} else if (strncasecmp(opt, "type=", 5) == 0) {
		if (testtype(&opt[5]))
			strlcpy(deftype, &opt[5], sizeof(deftype));
	} else if (strncasecmp(opt, "ty=", 3) == 0) {
		if (testtype(&opt[3]))
			strlcpy(deftype, &opt[3], sizeof(deftype));
	} else if (strncasecmp(opt, "querytype=", 10) == 0) {
		if (testtype(&opt[10]))
			strlcpy(deftype, &opt[10], sizeof(deftype));
	} else if (strncasecmp(opt, "query=", 6) == 0) {
		if (testtype(&opt[6]))
			strlcpy(deftype, &opt[6], sizeof(deftype));
	} else if (strncasecmp(opt, "qu=", 3) == 0) {
		if (testtype(&opt[3]))
			strlcpy(deftype, &opt[3], sizeof(deftype));
	} else if (strncasecmp(opt, "q=", 2) == 0) {
		if (testtype(&opt[2]))
			strlcpy(deftype, &opt[2], sizeof(deftype));
	} else if (strncasecmp(opt, "domain=", 7) == 0) {
		strlcpy(domainopt, &opt[7], sizeof(domainopt));
		set_search_domain(domainopt);
		usesearch = ISC_TRUE;
	} else if (strncasecmp(opt, "do=", 3) == 0) {
		strlcpy(domainopt, &opt[3], sizeof(domainopt));
		set_search_domain(domainopt);
		usesearch = ISC_TRUE;
	} else if (strncasecmp(opt, "port=", 5) == 0) {
		set_port(&opt[5]);
	} else if (strncasecmp(opt, "po=", 3) == 0) {
		set_port(&opt[3]);
	} else if (strncasecmp(opt, "timeout=", 8) == 0) {
		set_timeout(&opt[8]);
	} else if (strncasecmp(opt, "t=", 2) == 0) {
		set_timeout(&opt[2]);
	} else if (CHECKOPT("recurse", 3)) {
		recurse = ISC_TRUE;
	} else if (CHECKOPT("norecurse", 5)) {
		recurse = ISC_FALSE;
	} else if (strncasecmp(opt, "retry=", 6) == 0) {
		set_tries(&opt[6]);
	} else if (strncasecmp(opt, "ret=", 4) == 0) {
		set_tries(&opt[4]);
	} else if (CHECKOPT("defname", 3)) {
		usesearch = ISC_TRUE;
	} else if (CHECKOPT("nodefname", 5)) {
		usesearch = ISC_FALSE;
	} else if (CHECKOPT("vc", 2) == 0) {
		tcpmode = ISC_TRUE;
	} else if (CHECKOPT("novc", 4) == 0) {
		tcpmode = ISC_FALSE;
	} else if (CHECKOPT("debug", 3) == 0) {
		short_form = ISC_FALSE;
		showsearch = ISC_TRUE;
	} else if (CHECKOPT("nodebug", 5) == 0) {
		short_form = ISC_TRUE;
		showsearch = ISC_FALSE;
	} else if (CHECKOPT("d2", 2) == 0) {
		debugging = ISC_TRUE;
	} else if (CHECKOPT("nod2", 4) == 0) {
		debugging = ISC_FALSE;
	} else if (CHECKOPT("search", 3) == 0) {
		usesearch = ISC_TRUE;
	} else if (CHECKOPT("nosearch", 5) == 0) {
		usesearch = ISC_FALSE;
	} else if (CHECKOPT("sil", 3) == 0) {
		/* deprecation_msg = ISC_FALSE; */
	} else if (CHECKOPT("fail", 3) == 0) {
		nofail=ISC_FALSE;
	} else if (CHECKOPT("nofail", 5) == 0) {
		nofail=ISC_TRUE;
	} else if (strncasecmp(opt, "ndots=", 6) == 0) {
		set_ndots(&opt[6]);
	} else {
		printf("*** Invalid option: %s\n", opt);
	}
}

static void
addlookup(char *opt) {
	dig_lookup_t *lookup;
	isc_result_t result;
	isc_textregion_t tr;
	dns_rdatatype_t rdtype;
	dns_rdataclass_t rdclass;
	char store[MXNAME];

	debug("addlookup()");
	tr.base = deftype;
	tr.length = strlen(deftype);
	result = dns_rdatatype_fromtext(&rdtype, &tr);
	if (result != ISC_R_SUCCESS) {
		printf("unknown query type: %s\n", deftype);
		rdclass = dns_rdatatype_a;
	}
	tr.base = defclass;
	tr.length = strlen(defclass);
	result = dns_rdataclass_fromtext(&rdclass, &tr);
	if (result != ISC_R_SUCCESS) {
		printf("unknown query class: %s\n", defclass);
		rdclass = dns_rdataclass_in;
	}
	lookup = make_empty_lookup();
	if (get_reverse(store, sizeof(store), opt, lookup->ip6_int, ISC_TRUE)
	    == ISC_R_SUCCESS) {
		strlcpy(lookup->textname, store, sizeof(lookup->textname));
		lookup->rdtype = dns_rdatatype_ptr;
		lookup->rdtypeset = ISC_TRUE;
	} else {
		strlcpy(lookup->textname, opt, sizeof(lookup->textname));
		lookup->rdtype = rdtype;
		lookup->rdtypeset = ISC_TRUE;
	}
	lookup->rdclass = rdclass;
	lookup->rdclassset = ISC_TRUE;
	lookup->trace = ISC_FALSE;
	lookup->trace_root = lookup->trace;
	lookup->ns_search_only = ISC_FALSE;
	lookup->identify = identify;
	lookup->recurse = recurse;
	lookup->aaonly = aaonly;
	lookup->retries = tries;
	lookup->udpsize = 0;
	lookup->comments = comments;
	lookup->tcp_mode = tcpmode;
	lookup->stats = stats;
	lookup->section_question = section_question;
	lookup->section_answer = section_answer;
	lookup->section_authority = section_authority;
	lookup->section_additional = section_additional;
	lookup->new_search = ISC_TRUE;
	if (nofail)
		lookup->servfail_stops = ISC_FALSE;
	ISC_LIST_INIT(lookup->q);
	ISC_LINK_INIT(lookup, link);
	ISC_LIST_APPEND(lookup_list, lookup, link);
	lookup->origin = NULL;
	ISC_LIST_INIT(lookup->my_server_list);
	debug("looking up %s", lookup->textname);
}

static void
do_next_command(char *input) {
	char *ptr, *arg;

	ptr = next_token(&input, " \t\r\n");
	if (ptr == NULL)
		return;
	arg = next_token(&input, " \t\r\n");
	if ((strcasecmp(ptr, "set") == 0) &&
	    (arg != NULL))
		setoption(arg);
	else if ((strcasecmp(ptr, "server") == 0) ||
		 (strcasecmp(ptr, "lserver") == 0)) {
		isc_result_t res;

		if (arg == NULL)
			printf("usage: server hostname\n");
		else if ((res = set_nameserver(arg))) {
			printf("couldn't get address for '%s': %s\n",
			    arg, isc_result_totext(res));
		} else {
			check_ra = ISC_FALSE;
			show_settings(ISC_TRUE, ISC_TRUE);
		}
	} else if (strcasecmp(ptr, "exit") == 0) {
		in_use = ISC_FALSE;
	} else if (strcasecmp(ptr, "help") == 0 ||
		   strcasecmp(ptr, "?") == 0) {
		printf("The '%s' command is not yet implemented.\n", ptr);
	} else if (strcasecmp(ptr, "finger") == 0 ||
		   strcasecmp(ptr, "root") == 0 ||
		   strcasecmp(ptr, "ls") == 0 ||
		   strcasecmp(ptr, "view") == 0) {
		printf("The '%s' command is not implemented.\n", ptr);
	} else
		addlookup(ptr);
}

static void
get_next_command(void) {
	char *buf;
	char *ptr;

	fflush(stdout);
	buf = malloc(COMMSIZE);
	if (buf == NULL)
		fatal("memory allocation failure");
	if (interactive) {
		fputs("> ", stderr);
		fflush(stderr);
		ptr = fgets(buf, COMMSIZE, stdin);
	} else
		ptr = fgets(buf, COMMSIZE, stdin);
	if (ptr == NULL) {
		in_use = ISC_FALSE;
	} else
		do_next_command(ptr);
	free(buf);
}

static void
parse_args(int argc, char **argv) {
	isc_boolean_t have_lookup = ISC_FALSE;

	usesearch = ISC_TRUE;
	for (argc--, argv++; argc > 0; argc--, argv++) {
		debug("main parsing %s", argv[0]);
		if (argv[0][0] == '-') {
			if (strncasecmp(argv[0], "-ver", 4) == 0) {
				version();
				exit(0);
			} else if (argv[0][1] != 0) {
				setoption(&argv[0][1]);
			} else
				have_lookup = ISC_TRUE;
		} else {
			if (!have_lookup) {
				have_lookup = ISC_TRUE;
				in_use = ISC_TRUE;
				addlookup(argv[0]);
			} else {
				isc_result_t res;

				if ((res = set_nameserver(argv[0])))
					fatal("couldn't get address for '%s': %s",
					    argv[0], isc_result_totext(res));
				check_ra = ISC_FALSE;
			}
		}
	}
}

static void
flush_lookup_list(void) {
	dig_lookup_t *l, *lp;
	dig_query_t *q, *qp;
	dig_server_t *s, *sp;

	lookup_counter = 0;
	l = ISC_LIST_HEAD(lookup_list);
	while (l != NULL) {
		q = ISC_LIST_HEAD(l->q);
		while (q != NULL) {
			if (q->sock != NULL) {
				isc_socket_cancel(q->sock, NULL,
						  ISC_SOCKCANCEL_ALL);
				isc_socket_detach(&q->sock);
			}
			if (ISC_LINK_LINKED(&q->recvbuf, link))
				ISC_LIST_DEQUEUE(q->recvlist, &q->recvbuf,
						 link);
			if (ISC_LINK_LINKED(&q->lengthbuf, link))
				ISC_LIST_DEQUEUE(q->lengthlist, &q->lengthbuf,
						 link);
			isc_buffer_invalidate(&q->recvbuf);
			isc_buffer_invalidate(&q->lengthbuf);
			qp = q;
			q = ISC_LIST_NEXT(q, link);
			ISC_LIST_DEQUEUE(l->q, qp, link);
			free(qp);
		}
		s = ISC_LIST_HEAD(l->my_server_list);
		while (s != NULL) {
			sp = s;
			s = ISC_LIST_NEXT(s, link);
			ISC_LIST_DEQUEUE(l->my_server_list, sp, link);
			free(sp);

		}
		if (l->sendmsg != NULL)
			dns_message_destroy(&l->sendmsg);
		lp = l;
		l = ISC_LIST_NEXT(l, link);
		ISC_LIST_DEQUEUE(lookup_list, lp, link);
		free(lp);
	}
}

static void
getinput(isc_task_t *task, isc_event_t *event) {
	UNUSED(task);
	if (global_event == NULL)
		global_event = event;
	while (in_use) {
		get_next_command();
		if (ISC_LIST_HEAD(lookup_list) != NULL) {
			start_lookup();
			return;
		}
	}
	isc_app_shutdown();
}

int
nslookup_main(int argc, char **argv) {
	isc_result_t result;

	interactive = ISC_TF(isatty(0));

	ISC_LIST_INIT(lookup_list);
	ISC_LIST_INIT(server_list);
	ISC_LIST_INIT(root_hints_server_list);
	ISC_LIST_INIT(search_list);

	check_ra = ISC_TRUE;

	/* setup dighost callbacks */
	dighost_printmessage = printmessage;
	dighost_received = received;
	dighost_trying = trying;
	dighost_shutdown = query_finished;

	result = isc_app_start();
	check_result(result, "isc_app_start");

	if (pledge("stdio rpath inet dns", NULL) == -1) {
		perror("pledge");
		exit(1);
	}

	setup_libs();
	progname = argv[0];

	if (pledge("stdio inet dns", NULL) == -1) {
		perror("pledge");
		exit(1);
	}

	parse_args(argc, argv);

	setup_system(ISC_FALSE, ISC_FALSE);
	if (domainopt[0] != '\0')
		set_search_domain(domainopt);
	if (in_use)
		result = isc_app_onrun(global_task, onrun_callback,
				       NULL);
	else
		result = isc_app_onrun(global_task, getinput, NULL);
	check_result(result, "isc_app_onrun");
	in_use = ISC_TF(!in_use);

	(void)isc_app_run();

	puts("");
	debug("done, and starting to shut down");
	if (global_event != NULL)
		isc_event_free(&global_event);
	cancel_all();
	destroy_libs();

	return (query_error | print_error);
}