nslookup.c revision 254897
1226584Sdim/*
2226584Sdim * Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
3226584Sdim * Copyright (C) 2000-2003  Internet Software Consortium.
4226584Sdim *
5226584Sdim * Permission to use, copy, modify, and/or distribute this software for any
6226584Sdim * purpose with or without fee is hereby granted, provided that the above
7226584Sdim * copyright notice and this permission notice appear in all copies.
8226584Sdim *
9226584Sdim * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10226584Sdim * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11226584Sdim * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12226584Sdim * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13226584Sdim * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14226584Sdim * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15226584Sdim * PERFORMANCE OF THIS SOFTWARE.
16263508Sdim */
17263508Sdim
18234353Sdim/* $Id: nslookup.c,v 1.130 2011/12/16 23:01:16 each Exp $ */
19226584Sdim
20226584Sdim#include <config.h>
21226584Sdim
22226584Sdim#include <stdlib.h>
23226584Sdim#include <unistd.h>
24263508Sdim
25234353Sdim#include <isc/app.h>
26263508Sdim#include <isc/buffer.h>
27234353Sdim#include <isc/commandline.h>
28263508Sdim#include <isc/event.h>
29263508Sdim#include <isc/parseint.h>
30263508Sdim#include <isc/print.h>
31263508Sdim#include <isc/string.h>
32263508Sdim#include <isc/timer.h>
33263508Sdim#include <isc/util.h>
34263508Sdim#include <isc/task.h>
35263508Sdim#include <isc/netaddr.h>
36263508Sdim
37263508Sdim#include <dns/message.h>
38263508Sdim#include <dns/name.h>
39263508Sdim#include <dns/fixedname.h>
40263508Sdim#include <dns/rdata.h>
41263508Sdim#include <dns/rdataclass.h>
42263508Sdim#include <dns/rdataset.h>
43263508Sdim#include <dns/rdatastruct.h>
44263508Sdim#include <dns/rdatatype.h>
45263508Sdim#include <dns/byaddr.h>
46263508Sdim
47263508Sdim#include <dig/dig.h>
48263508Sdim
49263508Sdim#if defined(HAVE_READLINE)
50263508Sdim#include <readline/readline.h>
51263508Sdim#include <readline/history.h>
52263508Sdim#endif
53263508Sdim
54263508Sdimstatic isc_boolean_t short_form = ISC_TRUE,
55263508Sdim	tcpmode = ISC_FALSE,
56263508Sdim	identify = ISC_FALSE, stats = ISC_TRUE,
57263508Sdim	comments = ISC_TRUE, section_question = ISC_TRUE,
58263508Sdim	section_answer = ISC_TRUE, section_authority = ISC_TRUE,
59263508Sdim	section_additional = ISC_TRUE, recurse = ISC_TRUE,
60263508Sdim	aaonly = ISC_FALSE, nofail = ISC_TRUE;
61263508Sdim
62263508Sdimstatic isc_boolean_t interactive;
63263508Sdim
64263508Sdimstatic isc_boolean_t in_use = ISC_FALSE;
65263508Sdimstatic char defclass[MXRD] = "IN";
66263508Sdimstatic char deftype[MXRD] = "A";
67263508Sdimstatic isc_event_t *global_event = NULL;
68263508Sdimstatic int query_error = 1, print_error = 0;
69263508Sdim
70263508Sdimstatic char domainopt[DNS_NAME_MAXTEXT];
71263508Sdim
72263508Sdimstatic const char *rcodetext[] = {
73263508Sdim	"NOERROR",
74263508Sdim	"FORMERR",
75263508Sdim	"SERVFAIL",
76263508Sdim	"NXDOMAIN",
77263508Sdim	"NOTIMP",
78263508Sdim	"REFUSED",
79263508Sdim	"YXDOMAIN",
80263508Sdim	"YXRRSET",
81263508Sdim	"NXRRSET",
82263508Sdim	"NOTAUTH",
83263508Sdim	"NOTZONE",
84263508Sdim	"RESERVED11",
85263508Sdim	"RESERVED12",
86263508Sdim	"RESERVED13",
87263508Sdim	"RESERVED14",
88263508Sdim	"RESERVED15",
89263508Sdim	"BADVERS"
90263508Sdim};
91263508Sdim
92263508Sdimstatic const char *rtypetext[] = {
93263508Sdim	"rtype_0 = ",			/* 0 */
94263508Sdim	"internet address = ",		/* 1 */
95263508Sdim	"nameserver = ",		/* 2 */
96263508Sdim	"md = ",			/* 3 */
97263508Sdim	"mf = ",			/* 4 */
98263508Sdim	"canonical name = ",		/* 5 */
99263508Sdim	"soa = ",			/* 6 */
100234353Sdim	"mb = ",			/* 7 */
101226584Sdim	"mg = ",			/* 8 */
102226584Sdim	"mr = ",			/* 9 */
103263508Sdim	"rtype_10 = ",			/* 10 */
104263508Sdim	"protocol = ",			/* 11 */
105263508Sdim	"name = ",			/* 12 */
106263508Sdim	"hinfo = ",			/* 13 */
107263508Sdim	"minfo = ",			/* 14 */
108234353Sdim	"mail exchanger = ",		/* 15 */
109263508Sdim	"text = ",			/* 16 */
110263508Sdim	"rp = ",       			/* 17 */
111263508Sdim	"afsdb = ",			/* 18 */
112263508Sdim	"x25 address = ",		/* 19 */
113263508Sdim	"isdn address = ",		/* 20 */
114263508Sdim	"rt = ",			/* 21 */
115263508Sdim	"nsap = ",			/* 22 */
116263508Sdim	"nsap_ptr = ",			/* 23 */
117263508Sdim	"signature = ",			/* 24 */
118226584Sdim	"key = ",			/* 25 */
119263508Sdim	"px = ",			/* 26 */
120226584Sdim	"gpos = ",			/* 27 */
121226584Sdim	"has AAAA address ",		/* 28 */
122226584Sdim	"loc = ",			/* 29 */
123226584Sdim	"next = ",			/* 30 */
124226584Sdim	"rtype_31 = ",			/* 31 */
125226584Sdim	"rtype_32 = ",			/* 32 */
126226584Sdim	"service = ",			/* 33 */
127226584Sdim	"rtype_34 = ",			/* 34 */
128226584Sdim	"naptr = ",			/* 35 */
129226584Sdim	"kx = ",			/* 36 */
130226584Sdim	"cert = ",			/* 37 */
131226584Sdim	"v6 address = ",		/* 38 */
132249423Sdim	"dname = ",			/* 39 */
133249423Sdim	"rtype_40 = ",			/* 40 */
134249423Sdim	"optional = "			/* 41 */
135226584Sdim};
136226584Sdim
137249423Sdim#define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0]))
138226584Sdim
139226584Sdimstatic void flush_lookup_list(void);
140226584Sdimstatic void getinput(isc_task_t *task, isc_event_t *event);
141226584Sdim
142226584Sdimstatic char *
143226584Sdimrcode_totext(dns_rcode_t rcode)
144226584Sdim{
145226584Sdim	static char buf[sizeof("?65535")];
146249423Sdim	union {
147249423Sdim		const char *consttext;
148249423Sdim		char *deconsttext;
149249423Sdim	} totext;
150249423Sdim
151249423Sdim	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
152249423Sdim		snprintf(buf, sizeof(buf), "?%u", rcode);
153249423Sdim		totext.deconsttext = buf;
154226584Sdim	} else
155234353Sdim		totext.consttext = rcodetext[rcode];
156249423Sdim	return totext.deconsttext;
157249423Sdim}
158249423Sdim
159263508Sdimvoid
160263508Sdimdighost_shutdown(void) {
161234353Sdim	isc_event_t *event = global_event;
162226584Sdim
163226584Sdim	flush_lookup_list();
164226584Sdim	debug("dighost_shutdown()");
165226584Sdim
166226584Sdim	if (!in_use) {
167226584Sdim		isc_app_shutdown();
168226584Sdim		return;
169226584Sdim	}
170226584Sdim
171263508Sdim	isc_task_send(global_task, &event);
172263508Sdim}
173263508Sdim
174263508Sdimstatic void
175263508Sdimprintsoa(dns_rdata_t *rdata) {
176263508Sdim	dns_rdata_soa_t soa;
177263508Sdim	isc_result_t result;
178263508Sdim	char namebuf[DNS_NAME_FORMATSIZE];
179263508Sdim
180263508Sdim	result = dns_rdata_tostruct(rdata, &soa, NULL);
181263508Sdim	check_result(result, "dns_rdata_tostruct");
182263508Sdim
183263508Sdim	dns_name_format(&soa.origin, namebuf, sizeof(namebuf));
184263508Sdim	printf("\torigin = %s\n", namebuf);
185226584Sdim	dns_name_format(&soa.contact, namebuf, sizeof(namebuf));
186226584Sdim	printf("\tmail addr = %s\n", namebuf);
187249423Sdim	printf("\tserial = %u\n", soa.serial);
188249423Sdim	printf("\trefresh = %u\n", soa.refresh);
189226584Sdim	printf("\tretry = %u\n", soa.retry);
190249423Sdim	printf("\texpire = %u\n", soa.expire);
191249423Sdim	printf("\tminimum = %u\n", soa.minimum);
192226584Sdim	dns_rdata_freestruct(&soa);
193226584Sdim}
194226584Sdim
195226584Sdimstatic void
196226584Sdimprinta(dns_rdata_t *rdata) {
197263508Sdim	isc_result_t result;
198226584Sdim	char text[sizeof("255.255.255.255")];
199263508Sdim	isc_buffer_t b;
200263508Sdim
201263508Sdim	isc_buffer_init(&b, text, sizeof(text));
202226584Sdim	result = dns_rdata_totext(rdata, NULL, &b);
203226584Sdim	check_result(result, "dns_rdata_totext");
204226584Sdim	printf("Address: %.*s\n", (int)isc_buffer_usedlength(&b),
205226584Sdim	       (char *)isc_buffer_base(&b));
206234353Sdim}
207234353Sdim#ifdef DIG_SIGCHASE
208226584Sdim/* Just for compatibility : not use in host program */
209226584Sdimisc_result_t
210263508Sdimprintrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
211263508Sdim	      isc_buffer_t *target)
212249423Sdim{
213263508Sdim	UNUSED(owner_name);
214249423Sdim	UNUSED(rdataset);
215263508Sdim	UNUSED(target);
216263508Sdim	return(ISC_FALSE);
217249423Sdim}
218249423Sdim#endif
219263508Sdimstatic void
220249423Sdimprintrdata(dns_rdata_t *rdata) {
221249423Sdim	isc_result_t result;
222249423Sdim	isc_buffer_t *b = NULL;
223263508Sdim	unsigned int size = 1024;
224263508Sdim	isc_boolean_t done = ISC_FALSE;
225263508Sdim
226249423Sdim	if (rdata->type < N_KNOWN_RRTYPES)
227249423Sdim		printf("%s", rtypetext[rdata->type]);
228249423Sdim	else
229249423Sdim		printf("rdata_%d = ", rdata->type);
230263508Sdim
231263508Sdim	while (!done) {
232263508Sdim		result = isc_buffer_allocate(mctx, &b, size);
233263508Sdim		if (result != ISC_R_SUCCESS)
234263508Sdim			check_result(result, "isc_buffer_allocate");
235263508Sdim		result = dns_rdata_totext(rdata, NULL, b);
236263508Sdim		if (result == ISC_R_SUCCESS) {
237263508Sdim			printf("%.*s\n", (int)isc_buffer_usedlength(b),
238234353Sdim			       (char *)isc_buffer_base(b));
239263508Sdim			done = ISC_TRUE;
240263508Sdim		} else if (result != ISC_R_NOSPACE)
241263508Sdim			check_result(result, "dns_rdata_totext");
242263508Sdim		isc_buffer_free(&b);
243263508Sdim		size *= 2;
244263508Sdim	}
245263508Sdim}
246263508Sdim
247263508Sdimstatic isc_result_t
248263508Sdimprintsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
249263508Sdim	     dns_section_t section) {
250263508Sdim	isc_result_t result, loopresult;
251263508Sdim	dns_name_t *name;
252263508Sdim	dns_rdataset_t *rdataset = NULL;
253263508Sdim	dns_rdata_t rdata = DNS_RDATA_INIT;
254263508Sdim	char namebuf[DNS_NAME_FORMATSIZE];
255263508Sdim
256263508Sdim	UNUSED(query);
257263508Sdim	UNUSED(headers);
258263508Sdim
259263508Sdim	debug("printsection()");
260263508Sdim
261263508Sdim	result = dns_message_firstname(msg, section);
262263508Sdim	if (result == ISC_R_NOMORE)
263249423Sdim		return (ISC_R_SUCCESS);
264249423Sdim	else if (result != ISC_R_SUCCESS)
265249423Sdim		return (result);
266249423Sdim	for (;;) {
267263508Sdim		name = NULL;
268263508Sdim		dns_message_currentname(msg, section,
269263508Sdim					&name);
270263508Sdim		for (rdataset = ISC_LIST_HEAD(name->list);
271263508Sdim		     rdataset != NULL;
272263508Sdim		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
273263508Sdim			loopresult = dns_rdataset_first(rdataset);
274263508Sdim			while (loopresult == ISC_R_SUCCESS) {
275263508Sdim				dns_rdataset_current(rdataset, &rdata);
276263508Sdim				switch (rdata.type) {
277263508Sdim				case dns_rdatatype_a:
278263508Sdim					if (section != DNS_SECTION_ANSWER)
279263508Sdim						goto def_short_section;
280263508Sdim					dns_name_format(name, namebuf,
281263508Sdim							sizeof(namebuf));
282263508Sdim					printf("Name:\t%s\n", namebuf);
283263508Sdim					printa(&rdata);
284263508Sdim					break;
285263508Sdim				case dns_rdatatype_soa:
286263508Sdim					dns_name_format(name, namebuf,
287263508Sdim							sizeof(namebuf));
288263508Sdim					printf("%s\n", namebuf);
289263508Sdim					printsoa(&rdata);
290263508Sdim					break;
291263508Sdim				default:
292263508Sdim				def_short_section:
293263508Sdim					dns_name_format(name, namebuf,
294263508Sdim							sizeof(namebuf));
295263508Sdim					printf("%s\t", namebuf);
296263508Sdim					printrdata(&rdata);
297263508Sdim					break;
298263508Sdim				}
299263508Sdim				dns_rdata_reset(&rdata);
300263508Sdim				loopresult = dns_rdataset_next(rdataset);
301263508Sdim			}
302263508Sdim		}
303263508Sdim		result = dns_message_nextname(msg, section);
304263508Sdim		if (result == ISC_R_NOMORE)
305263508Sdim			break;
306263508Sdim		else if (result != ISC_R_SUCCESS) {
307263508Sdim			return (result);
308263508Sdim		}
309226584Sdim	}
310226584Sdim	return (ISC_R_SUCCESS);
311226584Sdim}
312263508Sdim
313263508Sdimstatic isc_result_t
314263508Sdimdetailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
315263508Sdim	     dns_section_t section) {
316263508Sdim	isc_result_t result, loopresult;
317263508Sdim	dns_name_t *name;
318263508Sdim	dns_rdataset_t *rdataset = NULL;
319234353Sdim	dns_rdata_t rdata = DNS_RDATA_INIT;
320263508Sdim	char namebuf[DNS_NAME_FORMATSIZE];
321234353Sdim
322226584Sdim	UNUSED(query);
323226584Sdim
324226584Sdim	debug("detailsection()");
325263508Sdim
326226584Sdim	if (headers) {
327226584Sdim		switch (section) {
328234353Sdim		case DNS_SECTION_QUESTION:
329249423Sdim			puts("    QUESTIONS:");
330234353Sdim			break;
331234353Sdim		case DNS_SECTION_ANSWER:
332226584Sdim			puts("    ANSWERS:");
333234353Sdim			break;
334249423Sdim		case DNS_SECTION_AUTHORITY:
335249423Sdim			puts("    AUTHORITY RECORDS:");
336249423Sdim			break;
337249423Sdim		case DNS_SECTION_ADDITIONAL:
338249423Sdim			puts("    ADDITIONAL RECORDS:");
339249423Sdim			break;
340249423Sdim		}
341249423Sdim	}
342249423Sdim
343249423Sdim	result = dns_message_firstname(msg, section);
344249423Sdim	if (result == ISC_R_NOMORE)
345249423Sdim		return (ISC_R_SUCCESS);
346249423Sdim	else if (result != ISC_R_SUCCESS)
347249423Sdim		return (result);
348234353Sdim	for (;;) {
349249423Sdim		name = NULL;
350234353Sdim		dns_message_currentname(msg, section,
351249423Sdim					&name);
352249423Sdim		for (rdataset = ISC_LIST_HEAD(name->list);
353234353Sdim		     rdataset != NULL;
354249423Sdim		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
355249423Sdim			if (section == DNS_SECTION_QUESTION) {
356249423Sdim				dns_name_format(name, namebuf,
357249423Sdim						sizeof(namebuf));
358249423Sdim				printf("\t%s, ", namebuf);
359249423Sdim				dns_rdatatype_format(rdataset->type,
360249423Sdim						     namebuf,
361249423Sdim						     sizeof(namebuf));
362249423Sdim				printf("type = %s, ", namebuf);
363249423Sdim				dns_rdataclass_format(rdataset->rdclass,
364249423Sdim						      namebuf,
365249423Sdim						      sizeof(namebuf));
366249423Sdim				printf("class = %s\n", namebuf);
367249423Sdim			}
368249423Sdim			loopresult = dns_rdataset_first(rdataset);
369249423Sdim			while (loopresult == ISC_R_SUCCESS) {
370249423Sdim				dns_rdataset_current(rdataset, &rdata);
371249423Sdim
372249423Sdim				dns_name_format(name, namebuf,
373263508Sdim						sizeof(namebuf));
374234353Sdim				printf("    ->  %s\n", namebuf);
375234353Sdim
376234353Sdim				switch (rdata.type) {
377234353Sdim				case dns_rdatatype_soa:
378234353Sdim					printsoa(&rdata);
379234353Sdim					break;
380234353Sdim				default:
381234353Sdim					printf("\t");
382249423Sdim					printrdata(&rdata);
383234353Sdim				}
384234353Sdim				dns_rdata_reset(&rdata);
385234353Sdim				printf("\tttl = %u\n", rdataset->ttl);
386234353Sdim				loopresult = dns_rdataset_next(rdataset);
387234353Sdim			}
388263508Sdim		}
389263508Sdim		result = dns_message_nextname(msg, section);
390263508Sdim		if (result == ISC_R_NOMORE)
391249423Sdim			break;
392249423Sdim		else if (result != ISC_R_SUCCESS) {
393249423Sdim			return (result);
394249423Sdim		}
395249423Sdim	}
396249423Sdim	return (ISC_R_SUCCESS);
397249423Sdim}
398249423Sdim
399249423Sdimvoid
400249423Sdimreceived(int bytes, isc_sockaddr_t *from, dig_query_t *query)
401249423Sdim{
402249423Sdim	UNUSED(bytes);
403249423Sdim	UNUSED(from);
404249423Sdim	UNUSED(query);
405249423Sdim}
406249423Sdim
407234353Sdimvoid
408234353Sdimtrying(char *frm, dig_lookup_t *lookup) {
409234353Sdim	UNUSED(frm);
410234353Sdim	UNUSED(lookup);
411263508Sdim
412263508Sdim}
413263508Sdim
414249423Sdimisc_result_t
415249423Sdimprintmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
416249423Sdim	char servtext[ISC_SOCKADDR_FORMATSIZE];
417249423Sdim
418249423Sdim	/* I've we've gotten this far, we've reached a server. */
419249423Sdim	query_error = 0;
420249423Sdim
421249423Sdim	debug("printmessage()");
422249423Sdim
423249423Sdim	isc_sockaddr_format(&query->sockaddr, servtext, sizeof(servtext));
424249423Sdim	printf("Server:\t\t%s\n", query->userarg);
425249423Sdim	printf("Address:\t%s\n", servtext);
426234353Sdim
427234353Sdim	puts("");
428234353Sdim
429249423Sdim	if (!short_form) {
430249423Sdim		isc_boolean_t headers = ISC_TRUE;
431249423Sdim		puts("------------");
432249423Sdim		/*		detailheader(query, msg);*/
433249423Sdim		detailsection(query, msg, headers, DNS_SECTION_QUESTION);
434249423Sdim		detailsection(query, msg, headers, DNS_SECTION_ANSWER);
435249423Sdim		detailsection(query, msg, headers, DNS_SECTION_AUTHORITY);
436249423Sdim		detailsection(query, msg, headers, DNS_SECTION_ADDITIONAL);
437249423Sdim		puts("------------");
438249423Sdim	}
439249423Sdim
440249423Sdim	if (msg->rcode != 0) {
441249423Sdim		char nametext[DNS_NAME_FORMATSIZE];
442249423Sdim		dns_name_format(query->lookup->name,
443249423Sdim				nametext, sizeof(nametext));
444249423Sdim		printf("** server can't find %s: %s\n",
445249423Sdim		       (msg->rcode != dns_rcode_nxdomain) ? nametext :
446249423Sdim		       query->lookup->textname, rcode_totext(msg->rcode));
447263508Sdim		debug("returning with rcode == 0");
448263508Sdim
449263508Sdim		/* the lookup failed */
450263508Sdim		print_error |= 1;
451		return (ISC_R_SUCCESS);
452	}
453
454	if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0)
455		puts("Non-authoritative answer:");
456	if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER]))
457		printsection(query, msg, headers, DNS_SECTION_ANSWER);
458	else
459		printf("*** Can't find %s: No answer\n",
460		       query->lookup->textname);
461
462	if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) &&
463	    (query->lookup->rdtype != dns_rdatatype_a)) {
464		puts("\nAuthoritative answers can be found from:");
465		printsection(query, msg, headers,
466			     DNS_SECTION_AUTHORITY);
467		printsection(query, msg, headers,
468			     DNS_SECTION_ADDITIONAL);
469	}
470	return (ISC_R_SUCCESS);
471}
472
473static void
474show_settings(isc_boolean_t full, isc_boolean_t serv_only) {
475	dig_server_t *srv;
476	isc_sockaddr_t sockaddr;
477	dig_searchlist_t *listent;
478	isc_result_t result;
479
480	srv = ISC_LIST_HEAD(server_list);
481
482	while (srv != NULL) {
483		char sockstr[ISC_SOCKADDR_FORMATSIZE];
484
485		result = get_address(srv->servername, port, &sockaddr);
486		check_result(result, "get_address");
487
488		isc_sockaddr_format(&sockaddr, sockstr, sizeof(sockstr));
489		printf("Default server: %s\nAddress: %s\n",
490			srv->userarg, sockstr);
491		if (!full)
492			return;
493		srv = ISC_LIST_NEXT(srv, link);
494	}
495	if (serv_only)
496		return;
497	printf("\nSet options:\n");
498	printf("  %s\t\t\t%s\t\t%s\n",
499	       tcpmode ? "vc" : "novc",
500	       short_form ? "nodebug" : "debug",
501	       debugging ? "d2" : "nod2");
502	printf("  %s\t\t%s\n",
503	       usesearch ? "search" : "nosearch",
504	       recurse ? "recurse" : "norecurse");
505	printf("  timeout = %d\t\tretry = %d\tport = %d\n",
506	       timeout, tries, port);
507	printf("  querytype = %-8s\tclass = %s\n", deftype, defclass);
508	printf("  srchlist = ");
509	for (listent = ISC_LIST_HEAD(search_list);
510	     listent != NULL;
511	     listent = ISC_LIST_NEXT(listent, link)) {
512		     printf("%s", listent->origin);
513		     if (ISC_LIST_NEXT(listent, link) != NULL)
514			     printf("/");
515	}
516	printf("\n");
517}
518
519static isc_boolean_t
520testtype(char *typetext) {
521	isc_result_t result;
522	isc_textregion_t tr;
523	dns_rdatatype_t rdtype;
524
525	tr.base = typetext;
526	tr.length = strlen(typetext);
527	result = dns_rdatatype_fromtext(&rdtype, &tr);
528	if (result == ISC_R_SUCCESS)
529		return (ISC_TRUE);
530	else {
531		printf("unknown query type: %s\n", typetext);
532		return (ISC_FALSE);
533	}
534}
535
536static isc_boolean_t
537testclass(char *typetext) {
538	isc_result_t result;
539	isc_textregion_t tr;
540	dns_rdataclass_t rdclass;
541
542	tr.base = typetext;
543	tr.length = strlen(typetext);
544	result = dns_rdataclass_fromtext(&rdclass, &tr);
545	if (result == ISC_R_SUCCESS)
546		return (ISC_TRUE);
547	else {
548		printf("unknown query class: %s\n", typetext);
549		return (ISC_FALSE);
550	}
551}
552
553static void
554set_port(const char *value) {
555	isc_uint32_t n;
556	isc_result_t result = parse_uint(&n, value, 65535, "port");
557	if (result == ISC_R_SUCCESS)
558		port = (isc_uint16_t) n;
559}
560
561static void
562set_timeout(const char *value) {
563	isc_uint32_t n;
564	isc_result_t result = parse_uint(&n, value, UINT_MAX, "timeout");
565	if (result == ISC_R_SUCCESS)
566		timeout = n;
567}
568
569static void
570set_tries(const char *value) {
571	isc_uint32_t n;
572	isc_result_t result = parse_uint(&n, value, INT_MAX, "tries");
573	if (result == ISC_R_SUCCESS)
574		tries = n;
575}
576
577static void
578setoption(char *opt) {
579	if (strncasecmp(opt, "all", 4) == 0) {
580		show_settings(ISC_TRUE, ISC_FALSE);
581	} else if (strncasecmp(opt, "class=", 6) == 0) {
582		if (testclass(&opt[6]))
583			strlcpy(defclass, &opt[6], sizeof(defclass));
584	} else if (strncasecmp(opt, "cl=", 3) == 0) {
585		if (testclass(&opt[3]))
586			strlcpy(defclass, &opt[3], sizeof(defclass));
587	} else if (strncasecmp(opt, "type=", 5) == 0) {
588		if (testtype(&opt[5]))
589			strlcpy(deftype, &opt[5], sizeof(deftype));
590	} else if (strncasecmp(opt, "ty=", 3) == 0) {
591		if (testtype(&opt[3]))
592			strlcpy(deftype, &opt[3], sizeof(deftype));
593	} else if (strncasecmp(opt, "querytype=", 10) == 0) {
594		if (testtype(&opt[10]))
595			strlcpy(deftype, &opt[10], sizeof(deftype));
596	} else if (strncasecmp(opt, "query=", 6) == 0) {
597		if (testtype(&opt[6]))
598			strlcpy(deftype, &opt[6], sizeof(deftype));
599	} else if (strncasecmp(opt, "qu=", 3) == 0) {
600		if (testtype(&opt[3]))
601			strlcpy(deftype, &opt[3], sizeof(deftype));
602	} else if (strncasecmp(opt, "q=", 2) == 0) {
603		if (testtype(&opt[2]))
604			strlcpy(deftype, &opt[2], sizeof(deftype));
605	} else if (strncasecmp(opt, "domain=", 7) == 0) {
606		strlcpy(domainopt, &opt[7], sizeof(domainopt));
607		set_search_domain(domainopt);
608		usesearch = ISC_TRUE;
609	} else if (strncasecmp(opt, "do=", 3) == 0) {
610		strlcpy(domainopt, &opt[3], sizeof(domainopt));
611		set_search_domain(domainopt);
612		usesearch = ISC_TRUE;
613	} else if (strncasecmp(opt, "port=", 5) == 0) {
614		set_port(&opt[5]);
615	} else if (strncasecmp(opt, "po=", 3) == 0) {
616		set_port(&opt[3]);
617	} else if (strncasecmp(opt, "timeout=", 8) == 0) {
618		set_timeout(&opt[8]);
619	} else if (strncasecmp(opt, "t=", 2) == 0) {
620		set_timeout(&opt[2]);
621	} else if (strncasecmp(opt, "rec", 3) == 0) {
622		recurse = ISC_TRUE;
623	} else if (strncasecmp(opt, "norec", 5) == 0) {
624		recurse = ISC_FALSE;
625	} else if (strncasecmp(opt, "retry=", 6) == 0) {
626		set_tries(&opt[6]);
627	} else if (strncasecmp(opt, "ret=", 4) == 0) {
628		set_tries(&opt[4]);
629	} else if (strncasecmp(opt, "def", 3) == 0) {
630		usesearch = ISC_TRUE;
631	} else if (strncasecmp(opt, "nodef", 5) == 0) {
632		usesearch = ISC_FALSE;
633	} else if (strncasecmp(opt, "vc", 3) == 0) {
634		tcpmode = ISC_TRUE;
635	} else if (strncasecmp(opt, "novc", 5) == 0) {
636		tcpmode = ISC_FALSE;
637	} else if (strncasecmp(opt, "deb", 3) == 0) {
638		short_form = ISC_FALSE;
639		showsearch = ISC_TRUE;
640	} else if (strncasecmp(opt, "nodeb", 5) == 0) {
641		short_form = ISC_TRUE;
642		showsearch = ISC_FALSE;
643	} else if (strncasecmp(opt, "d2", 2) == 0) {
644		debugging = ISC_TRUE;
645	} else if (strncasecmp(opt, "nod2", 4) == 0) {
646		debugging = ISC_FALSE;
647	} else if (strncasecmp(opt, "search", 3) == 0) {
648		usesearch = ISC_TRUE;
649	} else if (strncasecmp(opt, "nosearch", 5) == 0) {
650		usesearch = ISC_FALSE;
651	} else if (strncasecmp(opt, "sil", 3) == 0) {
652		/* deprecation_msg = ISC_FALSE; */
653	} else if (strncasecmp(opt, "fail", 3) == 0) {
654		nofail=ISC_FALSE;
655	} else if (strncasecmp(opt, "nofail", 3) == 0) {
656		nofail=ISC_TRUE;
657	} else {
658		printf("*** Invalid option: %s\n", opt);
659	}
660}
661
662static void
663addlookup(char *opt) {
664	dig_lookup_t *lookup;
665	isc_result_t result;
666	isc_textregion_t tr;
667	dns_rdatatype_t rdtype;
668	dns_rdataclass_t rdclass;
669	char store[MXNAME];
670
671	debug("addlookup()");
672	tr.base = deftype;
673	tr.length = strlen(deftype);
674	result = dns_rdatatype_fromtext(&rdtype, &tr);
675	if (result != ISC_R_SUCCESS) {
676		printf("unknown query type: %s\n", deftype);
677		rdclass = dns_rdatatype_a;
678	}
679	tr.base = defclass;
680	tr.length = strlen(defclass);
681	result = dns_rdataclass_fromtext(&rdclass, &tr);
682	if (result != ISC_R_SUCCESS) {
683		printf("unknown query class: %s\n", defclass);
684		rdclass = dns_rdataclass_in;
685	}
686	lookup = make_empty_lookup();
687	if (get_reverse(store, sizeof(store), opt, lookup->ip6_int, ISC_TRUE)
688	    == ISC_R_SUCCESS) {
689		strlcpy(lookup->textname, store, sizeof(lookup->textname));
690		lookup->rdtype = dns_rdatatype_ptr;
691		lookup->rdtypeset = ISC_TRUE;
692	} else {
693		strlcpy(lookup->textname, opt, sizeof(lookup->textname));
694		lookup->rdtype = rdtype;
695		lookup->rdtypeset = ISC_TRUE;
696	}
697	lookup->rdclass = rdclass;
698	lookup->rdclassset = ISC_TRUE;
699	lookup->trace = ISC_FALSE;
700	lookup->trace_root = lookup->trace;
701	lookup->ns_search_only = ISC_FALSE;
702	lookup->identify = identify;
703	lookup->recurse = recurse;
704	lookup->aaonly = aaonly;
705	lookup->retries = tries;
706	lookup->udpsize = 0;
707	lookup->comments = comments;
708	lookup->tcp_mode = tcpmode;
709	lookup->stats = stats;
710	lookup->section_question = section_question;
711	lookup->section_answer = section_answer;
712	lookup->section_authority = section_authority;
713	lookup->section_additional = section_additional;
714	lookup->new_search = ISC_TRUE;
715	if (nofail)
716		lookup->servfail_stops = ISC_FALSE;
717	ISC_LIST_INIT(lookup->q);
718	ISC_LINK_INIT(lookup, link);
719	ISC_LIST_APPEND(lookup_list, lookup, link);
720	lookup->origin = NULL;
721	ISC_LIST_INIT(lookup->my_server_list);
722	debug("looking up %s", lookup->textname);
723}
724
725static void
726do_next_command(char *input) {
727	char *ptr, *arg;
728
729	ptr = next_token(&input, " \t\r\n");
730	if (ptr == NULL)
731		return;
732	arg = next_token(&input, " \t\r\n");
733	if ((strcasecmp(ptr, "set") == 0) &&
734	    (arg != NULL))
735		setoption(arg);
736	else if ((strcasecmp(ptr, "server") == 0) ||
737		 (strcasecmp(ptr, "lserver") == 0)) {
738		isc_app_block();
739		set_nameserver(arg);
740		check_ra = ISC_FALSE;
741		isc_app_unblock();
742		show_settings(ISC_TRUE, ISC_TRUE);
743	} else if (strcasecmp(ptr, "exit") == 0) {
744		in_use = ISC_FALSE;
745	} else if (strcasecmp(ptr, "help") == 0 ||
746		   strcasecmp(ptr, "?") == 0) {
747		printf("The '%s' command is not yet implemented.\n", ptr);
748	} else if (strcasecmp(ptr, "finger") == 0 ||
749		   strcasecmp(ptr, "root") == 0 ||
750		   strcasecmp(ptr, "ls") == 0 ||
751		   strcasecmp(ptr, "view") == 0) {
752		printf("The '%s' command is not implemented.\n", ptr);
753	} else
754		addlookup(ptr);
755}
756
757static void
758get_next_command(void) {
759	char *buf;
760	char *ptr;
761
762	fflush(stdout);
763	buf = isc_mem_allocate(mctx, COMMSIZE);
764	if (buf == NULL)
765		fatal("memory allocation failure");
766	isc_app_block();
767	if (interactive) {
768#ifdef HAVE_READLINE
769		ptr = readline("> ");
770		add_history(ptr);
771#else
772		fputs("> ", stderr);
773		fflush(stderr);
774		ptr = fgets(buf, COMMSIZE, stdin);
775#endif
776	} else
777		ptr = fgets(buf, COMMSIZE, stdin);
778	isc_app_unblock();
779	if (ptr == NULL) {
780		in_use = ISC_FALSE;
781	} else
782		do_next_command(ptr);
783#ifdef HAVE_READLINE
784	if (interactive)
785		free(ptr);
786#endif
787	isc_mem_free(mctx, buf);
788}
789
790static void
791parse_args(int argc, char **argv) {
792	isc_boolean_t have_lookup = ISC_FALSE;
793
794	usesearch = ISC_TRUE;
795	for (argc--, argv++; argc > 0; argc--, argv++) {
796		debug("main parsing %s", argv[0]);
797		if (argv[0][0] == '-') {
798			if (argv[0][1] != 0)
799				setoption(&argv[0][1]);
800			else
801				have_lookup = ISC_TRUE;
802		} else {
803			if (!have_lookup) {
804				have_lookup = ISC_TRUE;
805				in_use = ISC_TRUE;
806				addlookup(argv[0]);
807			} else {
808				set_nameserver(argv[0]);
809				check_ra = ISC_FALSE;
810			}
811		}
812	}
813}
814
815static void
816flush_lookup_list(void) {
817	dig_lookup_t *l, *lp;
818	dig_query_t *q, *qp;
819	dig_server_t *s, *sp;
820
821	lookup_counter = 0;
822	l = ISC_LIST_HEAD(lookup_list);
823	while (l != NULL) {
824		q = ISC_LIST_HEAD(l->q);
825		while (q != NULL) {
826			if (q->sock != NULL) {
827				isc_socket_cancel(q->sock, NULL,
828						  ISC_SOCKCANCEL_ALL);
829				isc_socket_detach(&q->sock);
830			}
831			if (ISC_LINK_LINKED(&q->recvbuf, link))
832				ISC_LIST_DEQUEUE(q->recvlist, &q->recvbuf,
833						 link);
834			if (ISC_LINK_LINKED(&q->lengthbuf, link))
835				ISC_LIST_DEQUEUE(q->lengthlist, &q->lengthbuf,
836						 link);
837			isc_buffer_invalidate(&q->recvbuf);
838			isc_buffer_invalidate(&q->lengthbuf);
839			qp = q;
840			q = ISC_LIST_NEXT(q, link);
841			ISC_LIST_DEQUEUE(l->q, qp, link);
842			isc_mem_free(mctx, qp);
843		}
844		s = ISC_LIST_HEAD(l->my_server_list);
845		while (s != NULL) {
846			sp = s;
847			s = ISC_LIST_NEXT(s, link);
848			ISC_LIST_DEQUEUE(l->my_server_list, sp, link);
849			isc_mem_free(mctx, sp);
850
851		}
852		if (l->sendmsg != NULL)
853			dns_message_destroy(&l->sendmsg);
854		if (l->timer != NULL)
855			isc_timer_detach(&l->timer);
856		lp = l;
857		l = ISC_LIST_NEXT(l, link);
858		ISC_LIST_DEQUEUE(lookup_list, lp, link);
859		isc_mem_free(mctx, lp);
860	}
861}
862
863static void
864getinput(isc_task_t *task, isc_event_t *event) {
865	UNUSED(task);
866	if (global_event == NULL)
867		global_event = event;
868	while (in_use) {
869		get_next_command();
870		if (ISC_LIST_HEAD(lookup_list) != NULL) {
871			start_lookup();
872			return;
873		}
874	}
875	isc_app_shutdown();
876}
877
878int
879main(int argc, char **argv) {
880	isc_result_t result;
881
882	interactive = ISC_TF(isatty(0));
883
884	ISC_LIST_INIT(lookup_list);
885	ISC_LIST_INIT(server_list);
886	ISC_LIST_INIT(search_list);
887
888	check_ra = ISC_TRUE;
889
890	result = isc_app_start();
891	check_result(result, "isc_app_start");
892
893	setup_libs();
894	progname = argv[0];
895
896	parse_args(argc, argv);
897
898	setup_system();
899	if (domainopt[0] != '\0')
900		set_search_domain(domainopt);
901	if (in_use)
902		result = isc_app_onrun(mctx, global_task, onrun_callback,
903				       NULL);
904	else
905		result = isc_app_onrun(mctx, global_task, getinput, NULL);
906	check_result(result, "isc_app_onrun");
907	in_use = ISC_TF(!in_use);
908
909	(void)isc_app_run();
910
911	puts("");
912	debug("done, and starting to shut down");
913	if (global_event != NULL)
914		isc_event_free(&global_event);
915	cancel_all();
916	destroy_libs();
917	isc_app_finish();
918
919	return (query_error | print_error);
920}
921