1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2013  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18254897Serwin/* $Id: nslookup.c,v 1.130 2011/12/16 23:01:16 each Exp $ */
19135446Strhodes
20135446Strhodes#include <config.h>
21135446Strhodes
22135446Strhodes#include <stdlib.h>
23254897Serwin#include <unistd.h>
24135446Strhodes
25135446Strhodes#include <isc/app.h>
26135446Strhodes#include <isc/buffer.h>
27135446Strhodes#include <isc/commandline.h>
28135446Strhodes#include <isc/event.h>
29135446Strhodes#include <isc/parseint.h>
30193149Sdougb#include <isc/print.h>
31135446Strhodes#include <isc/string.h>
32135446Strhodes#include <isc/timer.h>
33135446Strhodes#include <isc/util.h>
34135446Strhodes#include <isc/task.h>
35135446Strhodes#include <isc/netaddr.h>
36135446Strhodes
37135446Strhodes#include <dns/message.h>
38135446Strhodes#include <dns/name.h>
39135446Strhodes#include <dns/fixedname.h>
40135446Strhodes#include <dns/rdata.h>
41135446Strhodes#include <dns/rdataclass.h>
42135446Strhodes#include <dns/rdataset.h>
43135446Strhodes#include <dns/rdatastruct.h>
44135446Strhodes#include <dns/rdatatype.h>
45135446Strhodes#include <dns/byaddr.h>
46135446Strhodes
47135446Strhodes#include <dig/dig.h>
48135446Strhodes
49254897Serwin#if defined(HAVE_READLINE)
50254897Serwin#include <readline/readline.h>
51254897Serwin#include <readline/history.h>
52254897Serwin#endif
53254897Serwin
54135446Strhodesstatic isc_boolean_t short_form = ISC_TRUE,
55135446Strhodes	tcpmode = ISC_FALSE,
56135446Strhodes	identify = ISC_FALSE, stats = ISC_TRUE,
57135446Strhodes	comments = ISC_TRUE, section_question = ISC_TRUE,
58135446Strhodes	section_answer = ISC_TRUE, section_authority = ISC_TRUE,
59135446Strhodes	section_additional = ISC_TRUE, recurse = ISC_TRUE,
60170222Sdougb	aaonly = ISC_FALSE, nofail = ISC_TRUE;
61170222Sdougb
62254897Serwinstatic isc_boolean_t interactive;
63254897Serwin
64135446Strhodesstatic isc_boolean_t in_use = ISC_FALSE;
65135446Strhodesstatic char defclass[MXRD] = "IN";
66135446Strhodesstatic char deftype[MXRD] = "A";
67135446Strhodesstatic isc_event_t *global_event = NULL;
68245163Serwinstatic int query_error = 1, print_error = 0;
69135446Strhodes
70135446Strhodesstatic char domainopt[DNS_NAME_MAXTEXT];
71135446Strhodes
72135446Strhodesstatic const char *rcodetext[] = {
73135446Strhodes	"NOERROR",
74135446Strhodes	"FORMERR",
75135446Strhodes	"SERVFAIL",
76135446Strhodes	"NXDOMAIN",
77135446Strhodes	"NOTIMP",
78135446Strhodes	"REFUSED",
79135446Strhodes	"YXDOMAIN",
80135446Strhodes	"YXRRSET",
81135446Strhodes	"NXRRSET",
82135446Strhodes	"NOTAUTH",
83135446Strhodes	"NOTZONE",
84135446Strhodes	"RESERVED11",
85135446Strhodes	"RESERVED12",
86135446Strhodes	"RESERVED13",
87135446Strhodes	"RESERVED14",
88135446Strhodes	"RESERVED15",
89135446Strhodes	"BADVERS"
90135446Strhodes};
91135446Strhodes
92135446Strhodesstatic const char *rtypetext[] = {
93135446Strhodes	"rtype_0 = ",			/* 0 */
94135446Strhodes	"internet address = ",		/* 1 */
95135446Strhodes	"nameserver = ",		/* 2 */
96135446Strhodes	"md = ",			/* 3 */
97135446Strhodes	"mf = ",			/* 4 */
98135446Strhodes	"canonical name = ",		/* 5 */
99135446Strhodes	"soa = ",			/* 6 */
100135446Strhodes	"mb = ",			/* 7 */
101135446Strhodes	"mg = ",			/* 8 */
102135446Strhodes	"mr = ",			/* 9 */
103135446Strhodes	"rtype_10 = ",			/* 10 */
104135446Strhodes	"protocol = ",			/* 11 */
105135446Strhodes	"name = ",			/* 12 */
106135446Strhodes	"hinfo = ",			/* 13 */
107135446Strhodes	"minfo = ",			/* 14 */
108135446Strhodes	"mail exchanger = ",		/* 15 */
109135446Strhodes	"text = ",			/* 16 */
110135446Strhodes	"rp = ",       			/* 17 */
111135446Strhodes	"afsdb = ",			/* 18 */
112135446Strhodes	"x25 address = ",		/* 19 */
113135446Strhodes	"isdn address = ",		/* 20 */
114135446Strhodes	"rt = ",			/* 21 */
115135446Strhodes	"nsap = ",			/* 22 */
116135446Strhodes	"nsap_ptr = ",			/* 23 */
117135446Strhodes	"signature = ",			/* 24 */
118135446Strhodes	"key = ",			/* 25 */
119135446Strhodes	"px = ",			/* 26 */
120135446Strhodes	"gpos = ",			/* 27 */
121135446Strhodes	"has AAAA address ",		/* 28 */
122135446Strhodes	"loc = ",			/* 29 */
123135446Strhodes	"next = ",			/* 30 */
124135446Strhodes	"rtype_31 = ",			/* 31 */
125135446Strhodes	"rtype_32 = ",			/* 32 */
126135446Strhodes	"service = ",			/* 33 */
127135446Strhodes	"rtype_34 = ",			/* 34 */
128135446Strhodes	"naptr = ",			/* 35 */
129135446Strhodes	"kx = ",			/* 36 */
130135446Strhodes	"cert = ",			/* 37 */
131135446Strhodes	"v6 address = ",		/* 38 */
132135446Strhodes	"dname = ",			/* 39 */
133135446Strhodes	"rtype_40 = ",			/* 40 */
134135446Strhodes	"optional = "			/* 41 */
135135446Strhodes};
136135446Strhodes
137135446Strhodes#define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0]))
138135446Strhodes
139135446Strhodesstatic void flush_lookup_list(void);
140135446Strhodesstatic void getinput(isc_task_t *task, isc_event_t *event);
141135446Strhodes
142193149Sdougbstatic char *
143193149Sdougbrcode_totext(dns_rcode_t rcode)
144193149Sdougb{
145193149Sdougb	static char buf[sizeof("?65535")];
146193149Sdougb	union {
147193149Sdougb		const char *consttext;
148193149Sdougb		char *deconsttext;
149193149Sdougb	} totext;
150193149Sdougb
151193149Sdougb	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
152193149Sdougb		snprintf(buf, sizeof(buf), "?%u", rcode);
153193149Sdougb		totext.deconsttext = buf;
154193149Sdougb	} else
155193149Sdougb		totext.consttext = rcodetext[rcode];
156193149Sdougb	return totext.deconsttext;
157193149Sdougb}
158193149Sdougb
159135446Strhodesvoid
160135446Strhodesdighost_shutdown(void) {
161135446Strhodes	isc_event_t *event = global_event;
162135446Strhodes
163135446Strhodes	flush_lookup_list();
164135446Strhodes	debug("dighost_shutdown()");
165135446Strhodes
166135446Strhodes	if (!in_use) {
167135446Strhodes		isc_app_shutdown();
168135446Strhodes		return;
169135446Strhodes	}
170135446Strhodes
171135446Strhodes	isc_task_send(global_task, &event);
172135446Strhodes}
173135446Strhodes
174135446Strhodesstatic void
175135446Strhodesprintsoa(dns_rdata_t *rdata) {
176135446Strhodes	dns_rdata_soa_t soa;
177135446Strhodes	isc_result_t result;
178135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
179135446Strhodes
180135446Strhodes	result = dns_rdata_tostruct(rdata, &soa, NULL);
181135446Strhodes	check_result(result, "dns_rdata_tostruct");
182135446Strhodes
183135446Strhodes	dns_name_format(&soa.origin, namebuf, sizeof(namebuf));
184135446Strhodes	printf("\torigin = %s\n", namebuf);
185135446Strhodes	dns_name_format(&soa.contact, namebuf, sizeof(namebuf));
186135446Strhodes	printf("\tmail addr = %s\n", namebuf);
187135446Strhodes	printf("\tserial = %u\n", soa.serial);
188135446Strhodes	printf("\trefresh = %u\n", soa.refresh);
189135446Strhodes	printf("\tretry = %u\n", soa.retry);
190135446Strhodes	printf("\texpire = %u\n", soa.expire);
191135446Strhodes	printf("\tminimum = %u\n", soa.minimum);
192135446Strhodes	dns_rdata_freestruct(&soa);
193135446Strhodes}
194135446Strhodes
195135446Strhodesstatic void
196135446Strhodesprinta(dns_rdata_t *rdata) {
197135446Strhodes	isc_result_t result;
198135446Strhodes	char text[sizeof("255.255.255.255")];
199135446Strhodes	isc_buffer_t b;
200135446Strhodes
201135446Strhodes	isc_buffer_init(&b, text, sizeof(text));
202135446Strhodes	result = dns_rdata_totext(rdata, NULL, &b);
203135446Strhodes	check_result(result, "dns_rdata_totext");
204135446Strhodes	printf("Address: %.*s\n", (int)isc_buffer_usedlength(&b),
205135446Strhodes	       (char *)isc_buffer_base(&b));
206135446Strhodes}
207135446Strhodes#ifdef DIG_SIGCHASE
208135446Strhodes/* Just for compatibility : not use in host program */
209135446Strhodesisc_result_t
210135446Strhodesprintrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
211135446Strhodes	      isc_buffer_t *target)
212135446Strhodes{
213135446Strhodes	UNUSED(owner_name);
214135446Strhodes	UNUSED(rdataset);
215135446Strhodes	UNUSED(target);
216135446Strhodes	return(ISC_FALSE);
217135446Strhodes}
218135446Strhodes#endif
219135446Strhodesstatic void
220135446Strhodesprintrdata(dns_rdata_t *rdata) {
221135446Strhodes	isc_result_t result;
222135446Strhodes	isc_buffer_t *b = NULL;
223135446Strhodes	unsigned int size = 1024;
224135446Strhodes	isc_boolean_t done = ISC_FALSE;
225135446Strhodes
226135446Strhodes	if (rdata->type < N_KNOWN_RRTYPES)
227135446Strhodes		printf("%s", rtypetext[rdata->type]);
228135446Strhodes	else
229135446Strhodes		printf("rdata_%d = ", rdata->type);
230135446Strhodes
231135446Strhodes	while (!done) {
232135446Strhodes		result = isc_buffer_allocate(mctx, &b, size);
233135446Strhodes		if (result != ISC_R_SUCCESS)
234135446Strhodes			check_result(result, "isc_buffer_allocate");
235135446Strhodes		result = dns_rdata_totext(rdata, NULL, b);
236135446Strhodes		if (result == ISC_R_SUCCESS) {
237135446Strhodes			printf("%.*s\n", (int)isc_buffer_usedlength(b),
238135446Strhodes			       (char *)isc_buffer_base(b));
239135446Strhodes			done = ISC_TRUE;
240135446Strhodes		} else if (result != ISC_R_NOSPACE)
241135446Strhodes			check_result(result, "dns_rdata_totext");
242135446Strhodes		isc_buffer_free(&b);
243135446Strhodes		size *= 2;
244135446Strhodes	}
245135446Strhodes}
246135446Strhodes
247135446Strhodesstatic isc_result_t
248135446Strhodesprintsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
249135446Strhodes	     dns_section_t section) {
250135446Strhodes	isc_result_t result, loopresult;
251135446Strhodes	dns_name_t *name;
252135446Strhodes	dns_rdataset_t *rdataset = NULL;
253135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
254135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
255135446Strhodes
256135446Strhodes	UNUSED(query);
257135446Strhodes	UNUSED(headers);
258135446Strhodes
259135446Strhodes	debug("printsection()");
260135446Strhodes
261135446Strhodes	result = dns_message_firstname(msg, section);
262135446Strhodes	if (result == ISC_R_NOMORE)
263135446Strhodes		return (ISC_R_SUCCESS);
264135446Strhodes	else if (result != ISC_R_SUCCESS)
265135446Strhodes		return (result);
266135446Strhodes	for (;;) {
267135446Strhodes		name = NULL;
268135446Strhodes		dns_message_currentname(msg, section,
269135446Strhodes					&name);
270135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
271135446Strhodes		     rdataset != NULL;
272135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
273135446Strhodes			loopresult = dns_rdataset_first(rdataset);
274135446Strhodes			while (loopresult == ISC_R_SUCCESS) {
275135446Strhodes				dns_rdataset_current(rdataset, &rdata);
276135446Strhodes				switch (rdata.type) {
277135446Strhodes				case dns_rdatatype_a:
278135446Strhodes					if (section != DNS_SECTION_ANSWER)
279135446Strhodes						goto def_short_section;
280135446Strhodes					dns_name_format(name, namebuf,
281135446Strhodes							sizeof(namebuf));
282135446Strhodes					printf("Name:\t%s\n", namebuf);
283135446Strhodes					printa(&rdata);
284135446Strhodes					break;
285135446Strhodes				case dns_rdatatype_soa:
286135446Strhodes					dns_name_format(name, namebuf,
287135446Strhodes							sizeof(namebuf));
288135446Strhodes					printf("%s\n", namebuf);
289135446Strhodes					printsoa(&rdata);
290135446Strhodes					break;
291135446Strhodes				default:
292135446Strhodes				def_short_section:
293135446Strhodes					dns_name_format(name, namebuf,
294135446Strhodes							sizeof(namebuf));
295135446Strhodes					printf("%s\t", namebuf);
296135446Strhodes					printrdata(&rdata);
297135446Strhodes					break;
298135446Strhodes				}
299135446Strhodes				dns_rdata_reset(&rdata);
300135446Strhodes				loopresult = dns_rdataset_next(rdataset);
301135446Strhodes			}
302135446Strhodes		}
303135446Strhodes		result = dns_message_nextname(msg, section);
304135446Strhodes		if (result == ISC_R_NOMORE)
305135446Strhodes			break;
306135446Strhodes		else if (result != ISC_R_SUCCESS) {
307135446Strhodes			return (result);
308135446Strhodes		}
309135446Strhodes	}
310135446Strhodes	return (ISC_R_SUCCESS);
311135446Strhodes}
312135446Strhodes
313135446Strhodesstatic isc_result_t
314135446Strhodesdetailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
315135446Strhodes	     dns_section_t section) {
316135446Strhodes	isc_result_t result, loopresult;
317135446Strhodes	dns_name_t *name;
318135446Strhodes	dns_rdataset_t *rdataset = NULL;
319135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
320135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
321135446Strhodes
322135446Strhodes	UNUSED(query);
323135446Strhodes
324135446Strhodes	debug("detailsection()");
325135446Strhodes
326135446Strhodes	if (headers) {
327135446Strhodes		switch (section) {
328135446Strhodes		case DNS_SECTION_QUESTION:
329135446Strhodes			puts("    QUESTIONS:");
330135446Strhodes			break;
331135446Strhodes		case DNS_SECTION_ANSWER:
332135446Strhodes			puts("    ANSWERS:");
333135446Strhodes			break;
334135446Strhodes		case DNS_SECTION_AUTHORITY:
335135446Strhodes			puts("    AUTHORITY RECORDS:");
336135446Strhodes			break;
337135446Strhodes		case DNS_SECTION_ADDITIONAL:
338135446Strhodes			puts("    ADDITIONAL RECORDS:");
339135446Strhodes			break;
340135446Strhodes		}
341135446Strhodes	}
342135446Strhodes
343135446Strhodes	result = dns_message_firstname(msg, section);
344135446Strhodes	if (result == ISC_R_NOMORE)
345135446Strhodes		return (ISC_R_SUCCESS);
346135446Strhodes	else if (result != ISC_R_SUCCESS)
347135446Strhodes		return (result);
348135446Strhodes	for (;;) {
349135446Strhodes		name = NULL;
350135446Strhodes		dns_message_currentname(msg, section,
351135446Strhodes					&name);
352135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
353135446Strhodes		     rdataset != NULL;
354135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
355135446Strhodes			if (section == DNS_SECTION_QUESTION) {
356135446Strhodes				dns_name_format(name, namebuf,
357135446Strhodes						sizeof(namebuf));
358135446Strhodes				printf("\t%s, ", namebuf);
359135446Strhodes				dns_rdatatype_format(rdataset->type,
360135446Strhodes						     namebuf,
361135446Strhodes						     sizeof(namebuf));
362135446Strhodes				printf("type = %s, ", namebuf);
363135446Strhodes				dns_rdataclass_format(rdataset->rdclass,
364135446Strhodes						      namebuf,
365135446Strhodes						      sizeof(namebuf));
366135446Strhodes				printf("class = %s\n", namebuf);
367135446Strhodes			}
368135446Strhodes			loopresult = dns_rdataset_first(rdataset);
369135446Strhodes			while (loopresult == ISC_R_SUCCESS) {
370135446Strhodes				dns_rdataset_current(rdataset, &rdata);
371135446Strhodes
372135446Strhodes				dns_name_format(name, namebuf,
373135446Strhodes						sizeof(namebuf));
374135446Strhodes				printf("    ->  %s\n", namebuf);
375135446Strhodes
376135446Strhodes				switch (rdata.type) {
377135446Strhodes				case dns_rdatatype_soa:
378135446Strhodes					printsoa(&rdata);
379135446Strhodes					break;
380135446Strhodes				default:
381135446Strhodes					printf("\t");
382135446Strhodes					printrdata(&rdata);
383135446Strhodes				}
384135446Strhodes				dns_rdata_reset(&rdata);
385204619Sdougb				printf("\tttl = %u\n", rdataset->ttl);
386135446Strhodes				loopresult = dns_rdataset_next(rdataset);
387135446Strhodes			}
388135446Strhodes		}
389135446Strhodes		result = dns_message_nextname(msg, section);
390135446Strhodes		if (result == ISC_R_NOMORE)
391135446Strhodes			break;
392135446Strhodes		else if (result != ISC_R_SUCCESS) {
393135446Strhodes			return (result);
394135446Strhodes		}
395135446Strhodes	}
396135446Strhodes	return (ISC_R_SUCCESS);
397135446Strhodes}
398135446Strhodes
399135446Strhodesvoid
400135446Strhodesreceived(int bytes, isc_sockaddr_t *from, dig_query_t *query)
401135446Strhodes{
402135446Strhodes	UNUSED(bytes);
403135446Strhodes	UNUSED(from);
404135446Strhodes	UNUSED(query);
405135446Strhodes}
406135446Strhodes
407135446Strhodesvoid
408135446Strhodestrying(char *frm, dig_lookup_t *lookup) {
409135446Strhodes	UNUSED(frm);
410135446Strhodes	UNUSED(lookup);
411135446Strhodes
412135446Strhodes}
413135446Strhodes
414135446Strhodesisc_result_t
415135446Strhodesprintmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
416193149Sdougb	char servtext[ISC_SOCKADDR_FORMATSIZE];
417135446Strhodes
418245163Serwin	/* I've we've gotten this far, we've reached a server. */
419245163Serwin	query_error = 0;
420245163Serwin
421135446Strhodes	debug("printmessage()");
422135446Strhodes
423135446Strhodes	isc_sockaddr_format(&query->sockaddr, servtext, sizeof(servtext));
424143731Sdougb	printf("Server:\t\t%s\n", query->userarg);
425135446Strhodes	printf("Address:\t%s\n", servtext);
426193149Sdougb
427135446Strhodes	puts("");
428135446Strhodes
429135446Strhodes	if (!short_form) {
430135446Strhodes		isc_boolean_t headers = ISC_TRUE;
431135446Strhodes		puts("------------");
432135446Strhodes		/*		detailheader(query, msg);*/
433135446Strhodes		detailsection(query, msg, headers, DNS_SECTION_QUESTION);
434135446Strhodes		detailsection(query, msg, headers, DNS_SECTION_ANSWER);
435135446Strhodes		detailsection(query, msg, headers, DNS_SECTION_AUTHORITY);
436135446Strhodes		detailsection(query, msg, headers, DNS_SECTION_ADDITIONAL);
437135446Strhodes		puts("------------");
438135446Strhodes	}
439135446Strhodes
440135446Strhodes	if (msg->rcode != 0) {
441135446Strhodes		char nametext[DNS_NAME_FORMATSIZE];
442135446Strhodes		dns_name_format(query->lookup->name,
443135446Strhodes				nametext, sizeof(nametext));
444174187Sdougb		printf("** server can't find %s: %s\n",
445262706Serwin		       nametext, rcode_totext(msg->rcode));
446135446Strhodes		debug("returning with rcode == 0");
447245163Serwin
448245163Serwin		/* the lookup failed */
449245163Serwin		print_error |= 1;
450135446Strhodes		return (ISC_R_SUCCESS);
451135446Strhodes	}
452135446Strhodes
453135446Strhodes	if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0)
454135446Strhodes		puts("Non-authoritative answer:");
455135446Strhodes	if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER]))
456135446Strhodes		printsection(query, msg, headers, DNS_SECTION_ANSWER);
457135446Strhodes	else
458135446Strhodes		printf("*** Can't find %s: No answer\n",
459135446Strhodes		       query->lookup->textname);
460135446Strhodes
461135446Strhodes	if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) &&
462135446Strhodes	    (query->lookup->rdtype != dns_rdatatype_a)) {
463135446Strhodes		puts("\nAuthoritative answers can be found from:");
464135446Strhodes		printsection(query, msg, headers,
465135446Strhodes			     DNS_SECTION_AUTHORITY);
466135446Strhodes		printsection(query, msg, headers,
467135446Strhodes			     DNS_SECTION_ADDITIONAL);
468135446Strhodes	}
469135446Strhodes	return (ISC_R_SUCCESS);
470135446Strhodes}
471135446Strhodes
472135446Strhodesstatic void
473135446Strhodesshow_settings(isc_boolean_t full, isc_boolean_t serv_only) {
474135446Strhodes	dig_server_t *srv;
475135446Strhodes	isc_sockaddr_t sockaddr;
476135446Strhodes	dig_searchlist_t *listent;
477193149Sdougb	isc_result_t result;
478135446Strhodes
479135446Strhodes	srv = ISC_LIST_HEAD(server_list);
480135446Strhodes
481135446Strhodes	while (srv != NULL) {
482135446Strhodes		char sockstr[ISC_SOCKADDR_FORMATSIZE];
483135446Strhodes
484193149Sdougb		result = get_address(srv->servername, port, &sockaddr);
485193149Sdougb		check_result(result, "get_address");
486193149Sdougb
487135446Strhodes		isc_sockaddr_format(&sockaddr, sockstr, sizeof(sockstr));
488135446Strhodes		printf("Default server: %s\nAddress: %s\n",
489143731Sdougb			srv->userarg, sockstr);
490135446Strhodes		if (!full)
491135446Strhodes			return;
492135446Strhodes		srv = ISC_LIST_NEXT(srv, link);
493135446Strhodes	}
494135446Strhodes	if (serv_only)
495135446Strhodes		return;
496135446Strhodes	printf("\nSet options:\n");
497135446Strhodes	printf("  %s\t\t\t%s\t\t%s\n",
498135446Strhodes	       tcpmode ? "vc" : "novc",
499135446Strhodes	       short_form ? "nodebug" : "debug",
500135446Strhodes	       debugging ? "d2" : "nod2");
501135446Strhodes	printf("  %s\t\t%s\n",
502135446Strhodes	       usesearch ? "search" : "nosearch",
503135446Strhodes	       recurse ? "recurse" : "norecurse");
504262706Serwin	printf("  timeout = %d\t\tretry = %d\tport = %d\tndots = %d\n",
505262706Serwin	       timeout, tries, port, ndots);
506135446Strhodes	printf("  querytype = %-8s\tclass = %s\n", deftype, defclass);
507135446Strhodes	printf("  srchlist = ");
508135446Strhodes	for (listent = ISC_LIST_HEAD(search_list);
509135446Strhodes	     listent != NULL;
510135446Strhodes	     listent = ISC_LIST_NEXT(listent, link)) {
511135446Strhodes		     printf("%s", listent->origin);
512135446Strhodes		     if (ISC_LIST_NEXT(listent, link) != NULL)
513135446Strhodes			     printf("/");
514135446Strhodes	}
515135446Strhodes	printf("\n");
516135446Strhodes}
517135446Strhodes
518135446Strhodesstatic isc_boolean_t
519135446Strhodestesttype(char *typetext) {
520135446Strhodes	isc_result_t result;
521135446Strhodes	isc_textregion_t tr;
522135446Strhodes	dns_rdatatype_t rdtype;
523135446Strhodes
524135446Strhodes	tr.base = typetext;
525135446Strhodes	tr.length = strlen(typetext);
526135446Strhodes	result = dns_rdatatype_fromtext(&rdtype, &tr);
527135446Strhodes	if (result == ISC_R_SUCCESS)
528135446Strhodes		return (ISC_TRUE);
529135446Strhodes	else {
530135446Strhodes		printf("unknown query type: %s\n", typetext);
531135446Strhodes		return (ISC_FALSE);
532135446Strhodes	}
533135446Strhodes}
534135446Strhodes
535135446Strhodesstatic isc_boolean_t
536135446Strhodestestclass(char *typetext) {
537135446Strhodes	isc_result_t result;
538135446Strhodes	isc_textregion_t tr;
539135446Strhodes	dns_rdataclass_t rdclass;
540135446Strhodes
541135446Strhodes	tr.base = typetext;
542135446Strhodes	tr.length = strlen(typetext);
543135446Strhodes	result = dns_rdataclass_fromtext(&rdclass, &tr);
544193149Sdougb	if (result == ISC_R_SUCCESS)
545135446Strhodes		return (ISC_TRUE);
546135446Strhodes	else {
547135446Strhodes		printf("unknown query class: %s\n", typetext);
548135446Strhodes		return (ISC_FALSE);
549135446Strhodes	}
550135446Strhodes}
551135446Strhodes
552135446Strhodesstatic void
553135446Strhodesset_port(const char *value) {
554135446Strhodes	isc_uint32_t n;
555135446Strhodes	isc_result_t result = parse_uint(&n, value, 65535, "port");
556135446Strhodes	if (result == ISC_R_SUCCESS)
557135446Strhodes		port = (isc_uint16_t) n;
558135446Strhodes}
559135446Strhodes
560135446Strhodesstatic void
561135446Strhodesset_timeout(const char *value) {
562135446Strhodes	isc_uint32_t n;
563135446Strhodes	isc_result_t result = parse_uint(&n, value, UINT_MAX, "timeout");
564135446Strhodes	if (result == ISC_R_SUCCESS)
565135446Strhodes		timeout = n;
566135446Strhodes}
567135446Strhodes
568135446Strhodesstatic void
569135446Strhodesset_tries(const char *value) {
570135446Strhodes	isc_uint32_t n;
571135446Strhodes	isc_result_t result = parse_uint(&n, value, INT_MAX, "tries");
572135446Strhodes	if (result == ISC_R_SUCCESS)
573135446Strhodes		tries = n;
574135446Strhodes}
575135446Strhodes
576135446Strhodesstatic void
577262706Serwinset_ndots(const char *value) {
578262706Serwin	isc_uint32_t n;
579262706Serwin	isc_result_t result = parse_uint(&n, value, 128, "ndots");
580262706Serwin	if (result == ISC_R_SUCCESS)
581262706Serwin		ndots = n;
582262706Serwin}
583262706Serwin
584262706Serwinstatic void
585135446Strhodessetoption(char *opt) {
586135446Strhodes	if (strncasecmp(opt, "all", 4) == 0) {
587135446Strhodes		show_settings(ISC_TRUE, ISC_FALSE);
588135446Strhodes	} else if (strncasecmp(opt, "class=", 6) == 0) {
589135446Strhodes		if (testclass(&opt[6]))
590225361Sdougb			strlcpy(defclass, &opt[6], sizeof(defclass));
591135446Strhodes	} else if (strncasecmp(opt, "cl=", 3) == 0) {
592135446Strhodes		if (testclass(&opt[3]))
593225361Sdougb			strlcpy(defclass, &opt[3], sizeof(defclass));
594135446Strhodes	} else if (strncasecmp(opt, "type=", 5) == 0) {
595135446Strhodes		if (testtype(&opt[5]))
596225361Sdougb			strlcpy(deftype, &opt[5], sizeof(deftype));
597135446Strhodes	} else if (strncasecmp(opt, "ty=", 3) == 0) {
598135446Strhodes		if (testtype(&opt[3]))
599225361Sdougb			strlcpy(deftype, &opt[3], sizeof(deftype));
600135446Strhodes	} else if (strncasecmp(opt, "querytype=", 10) == 0) {
601135446Strhodes		if (testtype(&opt[10]))
602225361Sdougb			strlcpy(deftype, &opt[10], sizeof(deftype));
603135446Strhodes	} else if (strncasecmp(opt, "query=", 6) == 0) {
604135446Strhodes		if (testtype(&opt[6]))
605225361Sdougb			strlcpy(deftype, &opt[6], sizeof(deftype));
606135446Strhodes	} else if (strncasecmp(opt, "qu=", 3) == 0) {
607135446Strhodes		if (testtype(&opt[3]))
608225361Sdougb			strlcpy(deftype, &opt[3], sizeof(deftype));
609135446Strhodes	} else if (strncasecmp(opt, "q=", 2) == 0) {
610135446Strhodes		if (testtype(&opt[2]))
611225361Sdougb			strlcpy(deftype, &opt[2], sizeof(deftype));
612135446Strhodes	} else if (strncasecmp(opt, "domain=", 7) == 0) {
613225361Sdougb		strlcpy(domainopt, &opt[7], sizeof(domainopt));
614135446Strhodes		set_search_domain(domainopt);
615135446Strhodes		usesearch = ISC_TRUE;
616135446Strhodes	} else if (strncasecmp(opt, "do=", 3) == 0) {
617225361Sdougb		strlcpy(domainopt, &opt[3], sizeof(domainopt));
618135446Strhodes		set_search_domain(domainopt);
619135446Strhodes		usesearch = ISC_TRUE;
620135446Strhodes	} else if (strncasecmp(opt, "port=", 5) == 0) {
621135446Strhodes		set_port(&opt[5]);
622135446Strhodes	} else if (strncasecmp(opt, "po=", 3) == 0) {
623135446Strhodes		set_port(&opt[3]);
624135446Strhodes	} else if (strncasecmp(opt, "timeout=", 8) == 0) {
625135446Strhodes		set_timeout(&opt[8]);
626135446Strhodes	} else if (strncasecmp(opt, "t=", 2) == 0) {
627135446Strhodes		set_timeout(&opt[2]);
628193149Sdougb	} else if (strncasecmp(opt, "rec", 3) == 0) {
629135446Strhodes		recurse = ISC_TRUE;
630135446Strhodes	} else if (strncasecmp(opt, "norec", 5) == 0) {
631135446Strhodes		recurse = ISC_FALSE;
632135446Strhodes	} else if (strncasecmp(opt, "retry=", 6) == 0) {
633135446Strhodes		set_tries(&opt[6]);
634135446Strhodes	} else if (strncasecmp(opt, "ret=", 4) == 0) {
635135446Strhodes		set_tries(&opt[4]);
636193149Sdougb	} else if (strncasecmp(opt, "def", 3) == 0) {
637135446Strhodes		usesearch = ISC_TRUE;
638135446Strhodes	} else if (strncasecmp(opt, "nodef", 5) == 0) {
639135446Strhodes		usesearch = ISC_FALSE;
640193149Sdougb	} else if (strncasecmp(opt, "vc", 3) == 0) {
641135446Strhodes		tcpmode = ISC_TRUE;
642135446Strhodes	} else if (strncasecmp(opt, "novc", 5) == 0) {
643135446Strhodes		tcpmode = ISC_FALSE;
644193149Sdougb	} else if (strncasecmp(opt, "deb", 3) == 0) {
645135446Strhodes		short_form = ISC_FALSE;
646170222Sdougb		showsearch = ISC_TRUE;
647135446Strhodes	} else if (strncasecmp(opt, "nodeb", 5) == 0) {
648135446Strhodes		short_form = ISC_TRUE;
649170222Sdougb		showsearch = ISC_FALSE;
650193149Sdougb	} else if (strncasecmp(opt, "d2", 2) == 0) {
651135446Strhodes		debugging = ISC_TRUE;
652135446Strhodes	} else if (strncasecmp(opt, "nod2", 4) == 0) {
653135446Strhodes		debugging = ISC_FALSE;
654135446Strhodes	} else if (strncasecmp(opt, "search", 3) == 0) {
655135446Strhodes		usesearch = ISC_TRUE;
656135446Strhodes	} else if (strncasecmp(opt, "nosearch", 5) == 0) {
657135446Strhodes		usesearch = ISC_FALSE;
658135446Strhodes	} else if (strncasecmp(opt, "sil", 3) == 0) {
659135446Strhodes		/* deprecation_msg = ISC_FALSE; */
660170222Sdougb	} else if (strncasecmp(opt, "fail", 3) == 0) {
661170222Sdougb		nofail=ISC_FALSE;
662170222Sdougb	} else if (strncasecmp(opt, "nofail", 3) == 0) {
663170222Sdougb		nofail=ISC_TRUE;
664262706Serwin	} else if (strncasecmp(opt, "ndots=", 6) == 0) {
665262706Serwin		set_ndots(&opt[6]);
666135446Strhodes	} else {
667193149Sdougb		printf("*** Invalid option: %s\n", opt);
668135446Strhodes	}
669135446Strhodes}
670135446Strhodes
671135446Strhodesstatic void
672135446Strhodesaddlookup(char *opt) {
673135446Strhodes	dig_lookup_t *lookup;
674135446Strhodes	isc_result_t result;
675135446Strhodes	isc_textregion_t tr;
676135446Strhodes	dns_rdatatype_t rdtype;
677135446Strhodes	dns_rdataclass_t rdclass;
678135446Strhodes	char store[MXNAME];
679135446Strhodes
680135446Strhodes	debug("addlookup()");
681135446Strhodes	tr.base = deftype;
682135446Strhodes	tr.length = strlen(deftype);
683135446Strhodes	result = dns_rdatatype_fromtext(&rdtype, &tr);
684135446Strhodes	if (result != ISC_R_SUCCESS) {
685135446Strhodes		printf("unknown query type: %s\n", deftype);
686135446Strhodes		rdclass = dns_rdatatype_a;
687135446Strhodes	}
688135446Strhodes	tr.base = defclass;
689135446Strhodes	tr.length = strlen(defclass);
690135446Strhodes	result = dns_rdataclass_fromtext(&rdclass, &tr);
691135446Strhodes	if (result != ISC_R_SUCCESS) {
692135446Strhodes		printf("unknown query class: %s\n", defclass);
693135446Strhodes		rdclass = dns_rdataclass_in;
694135446Strhodes	}
695135446Strhodes	lookup = make_empty_lookup();
696135446Strhodes	if (get_reverse(store, sizeof(store), opt, lookup->ip6_int, ISC_TRUE)
697135446Strhodes	    == ISC_R_SUCCESS) {
698225361Sdougb		strlcpy(lookup->textname, store, sizeof(lookup->textname));
699135446Strhodes		lookup->rdtype = dns_rdatatype_ptr;
700135446Strhodes		lookup->rdtypeset = ISC_TRUE;
701135446Strhodes	} else {
702225361Sdougb		strlcpy(lookup->textname, opt, sizeof(lookup->textname));
703135446Strhodes		lookup->rdtype = rdtype;
704135446Strhodes		lookup->rdtypeset = ISC_TRUE;
705135446Strhodes	}
706135446Strhodes	lookup->rdclass = rdclass;
707135446Strhodes	lookup->rdclassset = ISC_TRUE;
708135446Strhodes	lookup->trace = ISC_FALSE;
709135446Strhodes	lookup->trace_root = lookup->trace;
710135446Strhodes	lookup->ns_search_only = ISC_FALSE;
711135446Strhodes	lookup->identify = identify;
712135446Strhodes	lookup->recurse = recurse;
713135446Strhodes	lookup->aaonly = aaonly;
714135446Strhodes	lookup->retries = tries;
715135446Strhodes	lookup->udpsize = 0;
716135446Strhodes	lookup->comments = comments;
717135446Strhodes	lookup->tcp_mode = tcpmode;
718135446Strhodes	lookup->stats = stats;
719135446Strhodes	lookup->section_question = section_question;
720135446Strhodes	lookup->section_answer = section_answer;
721135446Strhodes	lookup->section_authority = section_authority;
722135446Strhodes	lookup->section_additional = section_additional;
723135446Strhodes	lookup->new_search = ISC_TRUE;
724170222Sdougb	if (nofail)
725170222Sdougb		lookup->servfail_stops = ISC_FALSE;
726135446Strhodes	ISC_LIST_INIT(lookup->q);
727135446Strhodes	ISC_LINK_INIT(lookup, link);
728135446Strhodes	ISC_LIST_APPEND(lookup_list, lookup, link);
729135446Strhodes	lookup->origin = NULL;
730135446Strhodes	ISC_LIST_INIT(lookup->my_server_list);
731135446Strhodes	debug("looking up %s", lookup->textname);
732135446Strhodes}
733135446Strhodes
734135446Strhodesstatic void
735254897Serwindo_next_command(char *input) {
736135446Strhodes	char *ptr, *arg;
737135446Strhodes
738135446Strhodes	ptr = next_token(&input, " \t\r\n");
739135446Strhodes	if (ptr == NULL)
740254897Serwin		return;
741135446Strhodes	arg = next_token(&input, " \t\r\n");
742135446Strhodes	if ((strcasecmp(ptr, "set") == 0) &&
743135446Strhodes	    (arg != NULL))
744135446Strhodes		setoption(arg);
745135446Strhodes	else if ((strcasecmp(ptr, "server") == 0) ||
746135446Strhodes		 (strcasecmp(ptr, "lserver") == 0)) {
747143731Sdougb		isc_app_block();
748135446Strhodes		set_nameserver(arg);
749170222Sdougb		check_ra = ISC_FALSE;
750143731Sdougb		isc_app_unblock();
751135446Strhodes		show_settings(ISC_TRUE, ISC_TRUE);
752135446Strhodes	} else if (strcasecmp(ptr, "exit") == 0) {
753135446Strhodes		in_use = ISC_FALSE;
754135446Strhodes	} else if (strcasecmp(ptr, "help") == 0 ||
755135446Strhodes		   strcasecmp(ptr, "?") == 0) {
756135446Strhodes		printf("The '%s' command is not yet implemented.\n", ptr);
757135446Strhodes	} else if (strcasecmp(ptr, "finger") == 0 ||
758135446Strhodes		   strcasecmp(ptr, "root") == 0 ||
759135446Strhodes		   strcasecmp(ptr, "ls") == 0 ||
760135446Strhodes		   strcasecmp(ptr, "view") == 0) {
761135446Strhodes		printf("The '%s' command is not implemented.\n", ptr);
762135446Strhodes	} else
763135446Strhodes		addlookup(ptr);
764254897Serwin}
765254897Serwin
766254897Serwinstatic void
767254897Serwinget_next_command(void) {
768254897Serwin	char *buf;
769254897Serwin	char *ptr;
770254897Serwin
771254897Serwin	fflush(stdout);
772254897Serwin	buf = isc_mem_allocate(mctx, COMMSIZE);
773254897Serwin	if (buf == NULL)
774254897Serwin		fatal("memory allocation failure");
775254897Serwin	isc_app_block();
776254897Serwin	if (interactive) {
777254897Serwin#ifdef HAVE_READLINE
778254897Serwin		ptr = readline("> ");
779262706Serwin		if (ptr != NULL)
780262706Serwin			add_history(ptr);
781254897Serwin#else
782254897Serwin		fputs("> ", stderr);
783254897Serwin		fflush(stderr);
784254897Serwin		ptr = fgets(buf, COMMSIZE, stdin);
785254897Serwin#endif
786254897Serwin	} else
787254897Serwin		ptr = fgets(buf, COMMSIZE, stdin);
788254897Serwin	isc_app_unblock();
789254897Serwin	if (ptr == NULL) {
790254897Serwin		in_use = ISC_FALSE;
791254897Serwin	} else
792254897Serwin		do_next_command(ptr);
793254897Serwin#ifdef HAVE_READLINE
794254897Serwin	if (interactive)
795254897Serwin		free(ptr);
796254897Serwin#endif
797135446Strhodes	isc_mem_free(mctx, buf);
798135446Strhodes}
799135446Strhodes
800135446Strhodesstatic void
801135446Strhodesparse_args(int argc, char **argv) {
802135446Strhodes	isc_boolean_t have_lookup = ISC_FALSE;
803135446Strhodes
804135446Strhodes	usesearch = ISC_TRUE;
805135446Strhodes	for (argc--, argv++; argc > 0; argc--, argv++) {
806135446Strhodes		debug("main parsing %s", argv[0]);
807135446Strhodes		if (argv[0][0] == '-') {
808135446Strhodes			if (argv[0][1] != 0)
809135446Strhodes				setoption(&argv[0][1]);
810135446Strhodes			else
811135446Strhodes				have_lookup = ISC_TRUE;
812135446Strhodes		} else {
813135446Strhodes			if (!have_lookup) {
814135446Strhodes				have_lookup = ISC_TRUE;
815135446Strhodes				in_use = ISC_TRUE;
816135446Strhodes				addlookup(argv[0]);
817170222Sdougb			} else {
818170222Sdougb				set_nameserver(argv[0]);
819170222Sdougb				check_ra = ISC_FALSE;
820135446Strhodes			}
821135446Strhodes		}
822135446Strhodes	}
823135446Strhodes}
824135446Strhodes
825135446Strhodesstatic void
826135446Strhodesflush_lookup_list(void) {
827135446Strhodes	dig_lookup_t *l, *lp;
828135446Strhodes	dig_query_t *q, *qp;
829135446Strhodes	dig_server_t *s, *sp;
830135446Strhodes
831135446Strhodes	lookup_counter = 0;
832135446Strhodes	l = ISC_LIST_HEAD(lookup_list);
833135446Strhodes	while (l != NULL) {
834135446Strhodes		q = ISC_LIST_HEAD(l->q);
835135446Strhodes		while (q != NULL) {
836135446Strhodes			if (q->sock != NULL) {
837135446Strhodes				isc_socket_cancel(q->sock, NULL,
838135446Strhodes						  ISC_SOCKCANCEL_ALL);
839135446Strhodes				isc_socket_detach(&q->sock);
840135446Strhodes			}
841135446Strhodes			if (ISC_LINK_LINKED(&q->recvbuf, link))
842135446Strhodes				ISC_LIST_DEQUEUE(q->recvlist, &q->recvbuf,
843135446Strhodes						 link);
844135446Strhodes			if (ISC_LINK_LINKED(&q->lengthbuf, link))
845135446Strhodes				ISC_LIST_DEQUEUE(q->lengthlist, &q->lengthbuf,
846135446Strhodes						 link);
847135446Strhodes			isc_buffer_invalidate(&q->recvbuf);
848135446Strhodes			isc_buffer_invalidate(&q->lengthbuf);
849135446Strhodes			qp = q;
850135446Strhodes			q = ISC_LIST_NEXT(q, link);
851135446Strhodes			ISC_LIST_DEQUEUE(l->q, qp, link);
852135446Strhodes			isc_mem_free(mctx, qp);
853135446Strhodes		}
854135446Strhodes		s = ISC_LIST_HEAD(l->my_server_list);
855135446Strhodes		while (s != NULL) {
856135446Strhodes			sp = s;
857135446Strhodes			s = ISC_LIST_NEXT(s, link);
858135446Strhodes			ISC_LIST_DEQUEUE(l->my_server_list, sp, link);
859135446Strhodes			isc_mem_free(mctx, sp);
860135446Strhodes
861135446Strhodes		}
862135446Strhodes		if (l->sendmsg != NULL)
863135446Strhodes			dns_message_destroy(&l->sendmsg);
864135446Strhodes		if (l->timer != NULL)
865135446Strhodes			isc_timer_detach(&l->timer);
866135446Strhodes		lp = l;
867135446Strhodes		l = ISC_LIST_NEXT(l, link);
868135446Strhodes		ISC_LIST_DEQUEUE(lookup_list, lp, link);
869135446Strhodes		isc_mem_free(mctx, lp);
870135446Strhodes	}
871135446Strhodes}
872135446Strhodes
873135446Strhodesstatic void
874135446Strhodesgetinput(isc_task_t *task, isc_event_t *event) {
875135446Strhodes	UNUSED(task);
876135446Strhodes	if (global_event == NULL)
877135446Strhodes		global_event = event;
878135446Strhodes	while (in_use) {
879135446Strhodes		get_next_command();
880135446Strhodes		if (ISC_LIST_HEAD(lookup_list) != NULL) {
881135446Strhodes			start_lookup();
882135446Strhodes			return;
883135446Strhodes		}
884135446Strhodes	}
885135446Strhodes	isc_app_shutdown();
886135446Strhodes}
887135446Strhodes
888135446Strhodesint
889135446Strhodesmain(int argc, char **argv) {
890135446Strhodes	isc_result_t result;
891135446Strhodes
892254897Serwin	interactive = ISC_TF(isatty(0));
893254897Serwin
894135446Strhodes	ISC_LIST_INIT(lookup_list);
895135446Strhodes	ISC_LIST_INIT(server_list);
896135446Strhodes	ISC_LIST_INIT(search_list);
897135446Strhodes
898170222Sdougb	check_ra = ISC_TRUE;
899170222Sdougb
900135446Strhodes	result = isc_app_start();
901135446Strhodes	check_result(result, "isc_app_start");
902135446Strhodes
903135446Strhodes	setup_libs();
904135446Strhodes	progname = argv[0];
905135446Strhodes
906135446Strhodes	parse_args(argc, argv);
907135446Strhodes
908135446Strhodes	setup_system();
909135446Strhodes	if (domainopt[0] != '\0')
910135446Strhodes		set_search_domain(domainopt);
911135446Strhodes	if (in_use)
912135446Strhodes		result = isc_app_onrun(mctx, global_task, onrun_callback,
913135446Strhodes				       NULL);
914135446Strhodes	else
915135446Strhodes		result = isc_app_onrun(mctx, global_task, getinput, NULL);
916135446Strhodes	check_result(result, "isc_app_onrun");
917135446Strhodes	in_use = ISC_TF(!in_use);
918135446Strhodes
919135446Strhodes	(void)isc_app_run();
920135446Strhodes
921135446Strhodes	puts("");
922135446Strhodes	debug("done, and starting to shut down");
923135446Strhodes	if (global_event != NULL)
924135446Strhodes		isc_event_free(&global_event);
925135446Strhodes	cancel_all();
926135446Strhodes	destroy_libs();
927135446Strhodes	isc_app_finish();
928135446Strhodes
929245163Serwin	return (query_error | print_error);
930135446Strhodes}
931