dig.c 47.4 KB
Newer Older
Brian Wellington's avatar
Brian Wellington committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 2000-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
6
7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9
10
11
12
13
14
15
 * 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.
16
17
 */

18
/* $Id: dig.c,v 1.219 2008/02/05 06:57:52 marka Exp $ */
19
20

/*! \file */
21

22
23
#include <config.h>
#include <stdlib.h>
Brian Wellington's avatar
Brian Wellington committed
24
#include <time.h>
25
#include <ctype.h>
26

David Lawrence's avatar
David Lawrence committed
27
#include <isc/app.h>
28
#include <isc/netaddr.h>
29
#include <isc/parseint.h>
Mark Andrews's avatar
Mark Andrews committed
30
#include <isc/print.h>
David Lawrence's avatar
David Lawrence committed
31
32
#include <isc/string.h>
#include <isc/util.h>
33
#include <isc/task.h>
David Lawrence's avatar
David Lawrence committed
34

35
36
#include <dns/byaddr.h>
#include <dns/fixedname.h>
37
#include <dns/masterdump.h>
38
#include <dns/message.h>
Brian Wellington's avatar
Brian Wellington committed
39
#include <dns/name.h>
40
41
42
#include <dns/rdata.h>
#include <dns/rdataset.h>
#include <dns/rdatatype.h>
43
#include <dns/rdataclass.h>
44
#include <dns/result.h>
45
#include <dns/tsig.h>
46

47
48
#include <bind9/getaddresses.h>

Mark Andrews's avatar
Mark Andrews committed
49
50
#include <dig/dig.h>

51
52
53
54
55
56
#define ADD_STRING(b, s) { 				\
	if (strlen(s) >= isc_buffer_availablelength(b)) \
 		return (ISC_R_NOSPACE); 		\
	else 						\
		isc_buffer_putstr(b, s); 		\
}
57

58
#define DIG_MAX_ADDRESSES 20
59

60
dig_lookup_t *default_lookup = NULL;
61

Brian Wellington's avatar
Brian Wellington committed
62
63
64
static char *batchname = NULL;
static FILE *batchfp = NULL;
static char *argv0;
65
static int addresscount = 0;
66

Brian Wellington's avatar
Brian Wellington committed
67
static char domainopt[DNS_NAME_MAXTEXT];
68

Brian Wellington's avatar
Brian Wellington committed
69
static isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
70
	ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
71
	multiline = ISC_FALSE, nottl = ISC_FALSE, noclass = ISC_FALSE;
72

73
/*% opcode text */
David Lawrence's avatar
David Lawrence committed
74
static const char *opcodetext[] = {
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
	"QUERY",
	"IQUERY",
	"STATUS",
	"RESERVED3",
	"NOTIFY",
	"UPDATE",
	"RESERVED6",
	"RESERVED7",
	"RESERVED8",
	"RESERVED9",
	"RESERVED10",
	"RESERVED11",
	"RESERVED12",
	"RESERVED13",
	"RESERVED14",
	"RESERVED15"
};

93
/*% return code text */
David Lawrence's avatar
David Lawrence committed
94
static const char *rcodetext[] = {
95
96
97
98
	"NOERROR",
	"FORMERR",
	"SERVFAIL",
	"NXDOMAIN",
99
	"NOTIMP",
100
101
102
103
104
105
106
107
108
109
110
111
112
	"REFUSED",
	"YXDOMAIN",
	"YXRRSET",
	"NXRRSET",
	"NOTAUTH",
	"NOTZONE",
	"RESERVED11",
	"RESERVED12",
	"RESERVED13",
	"RESERVED14",
	"RESERVED15",
	"BADVERS"
};
113

114
/*% print usage */
115
static void
116
print_usage(FILE *fp) {
117
	fputs(
118
"Usage:  dig [@global-server] [domain] [q-type] [q-class] {q-opt}\n"
119
120
"            {global-d-opt} host [@local-server] {local-d-opt}\n"
"            [ host [@local-server] {local-d-opt} [...]]\n", fp);
121
122
123
}

static void
124
usage(void) {
125
126
127
128
129
130
	print_usage(stderr);
	fputs("\nUse \"dig -h\" (or \"dig -h | more\") "
	      "for complete list of options\n", stderr);
	exit(1);
}

131
/*% version */
132
133
134
135
136
static void
version(void) {
	fputs("DiG " VERSION "\n", stderr);
}

137
/*% help */
138
139
140
141
static void
help(void) {
	print_usage(stdout);
	fputs(
142
"Where:  domain	  is in the Domain Name System\n"
143
"        q-class  is one of (in,hs,ch,...) [default: in]\n"
144
"        q-type   is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default:a]\n"
145
"                 (Use ixfr=version for type ixfr)\n"
146
"        q-opt    is one of:\n"
147
148
"                 -x dot-notation     (shortcut for reverse lookups)\n"
"                 -i                  (use IP6.INT for IPv6 reverse lookups)\n"
149
"                 -f filename         (batch mode)\n"
Mark Andrews's avatar
Mark Andrews committed
150
"                 -b address[#port]   (bind to source address/port)\n"
151
"                 -p port             (specify port number)\n"
152
"                 -q name             (specify query name)\n"
153
154
"                 -t type             (specify query type)\n"
"                 -c class            (specify query class)\n"
155
"                 -k keyfile          (specify tsig key file)\n"
156
"                 -y [hmac:]name:key  (specify named base64 tsig key)\n"
157
158
"                 -4                  (use IPv4 query transport only)\n"
"                 -6                  (use IPv6 query transport only)\n"
159
160
"        d-opt    is of the form +keyword[=value], where keyword is:\n"
"                 +[no]vc             (TCP mode)\n"
161
162
163
"                 +[no]tcp            (TCP mode, alternate syntax)\n"
"                 +time=###           (Set query timeout) [5]\n"
"                 +tries=###          (Set number of UDP attempts) [3]\n"
164
"                 +retry=###          (Set number of UDP retries) [2]\n"
165
"                 +domain=###         (Set default domainname)\n"
166
"                 +bufsize=###        (Set EDNS0 Max UDP packet size)\n"
167
"                 +ndots=###          (Set NDOTS value)\n"
168
"                 +edns=###           (Set EDNS version)\n"
169
"                 +[no]search         (Set whether to use searchlist)\n"
170
"                 +[no]showsearch     (Search with intermediate results)\n"
171
"                 +[no]defname        (Ditto)\n"
Mark Andrews's avatar
Mark Andrews committed
172
"                 +[no]recurse        (Recursive mode)\n"
Michael Sawyer's avatar
Michael Sawyer committed
173
174
"                 +[no]ignore         (Don't revert to TCP for TC responses.)"
"\n"
175
"                 +[no]fail           (Don't try next server on SERVFAIL)\n"
Andreas Gustafsson's avatar
Andreas Gustafsson committed
176
"                 +[no]besteffort     (Try to parse even illegal messages)\n"
177
"                 +[no]aaonly         (Set AA flag in query (+[no]aaflag))\n"
178
179
"                 +[no]adflag         (Set AD flag in query)\n"
"                 +[no]cdflag         (Set CD flag in query)\n"
180
"                 +[no]cl             (Control display of class in records)\n"
181
"                 +[no]cmd            (Control display of command line)\n"
182
183
184
"                 +[no]comments       (Control display of comment lines)\n"
"                 +[no]question       (Control display of question)\n"
"                 +[no]answer         (Control display of answer)\n"
185
"                 +[no]authority      (Control display of authority)\n"
186
"                 +[no]additional     (Control display of additional)\n"
187
"                 +[no]stats          (Control display of statistics)\n"
188
189
"                 +[no]short          (Disable everything except short\n"
"                                      form of answer)\n"
190
"                 +[no]ttlid          (Control display of ttls in records)\n"
191
"                 +[no]all            (Set or clear all display flags)\n"
192
"                 +[no]qr             (Print question before sending)\n"
Brian Wellington's avatar
Brian Wellington committed
193
"                 +[no]nssearch       (Search all authoritative nameservers)\n"
194
195
"                 +[no]identify       (ID responders in short answers)\n"
"                 +[no]trace          (Trace delegation down from root)\n"
196
"                 +[no]dnssec         (Request DNSSEC records)\n"
197
198
199
200
201
202
203
#ifdef DIG_SIGCHASE
"                 +[no]sigchase       (Chase DNSSEC signatures)\n"
"                 +trusted-key=####   (Trusted Key when chasing DNSSEC sigs)\n"
#if DIG_SIGCHASE_TD
"                 +[no]topdown        (Do DNSSEC validation top down mode)\n"
#endif
#endif
204
"                 +[no]multiline      (Print records in an expanded format)\n"
205
"        global d-opts and servers (before host name) affect all queries.\n"
206
207
208
"        local d-opts and servers (after host name) affect only that lookup.\n"
"        -h                           (print help and exit)\n"
"        -v                           (print version and exit)\n",
209
	stdout);
210
}
211

212
/*%
Michael Sawyer's avatar
Michael Sawyer committed
213
214
 * Callback from dighost.c to print the received message.
 */
215
void
216
received(int bytes, isc_sockaddr_t *from, dig_query_t *query) {
217
218
219
	isc_uint64_t diff;
	isc_time_t now;
	time_t tnow;
220
221
222
	char fromtext[ISC_SOCKADDR_FORMATSIZE];

	isc_sockaddr_format(from, fromtext, sizeof(fromtext));
223

224
	TIME_NOW(&now);
225

226
	if (query->lookup->stats && !short_form) {
227
228
		diff = isc_time_microdiff(&now, &query->time_sent);
		printf(";; Query time: %ld msec\n", (long int)diff/1000);
229
		printf(";; SERVER: %s(%s)\n", fromtext, query->servname);
230
		time(&tnow);
231
		printf(";; WHEN: %s", ctime(&tnow));
232
		if (query->lookup->doing_xfr) {
233
234
235
236
			printf(";; XFR size: %u records (messages %u, "
			       "bytes %" ISC_PRINT_QUADFORMAT "u)\n",
			       query->rr_count, query->msg_count,
			       query->byte_count);
237
		} else {
238
			printf(";; MSG SIZE  rcvd: %u\n", bytes);
239

240
		}
241
		if (key != NULL) {
242
			if (!validated)
243
244
				puts(";; WARNING -- Some TSIG could not "
				     "be validated");
245
246
		}
		if ((key == NULL) && (keysecret[0] != 0)) {
247
			puts(";; WARNING -- TSIG key was not used.");
248
		}
249
		puts("");
250
251
	} else if (query->lookup->identify && !short_form) {
		diff = isc_time_microdiff(&now, &query->time_sent);
252
253
254
255
256
		printf(";; Received %" ISC_PRINT_QUADFORMAT "u bytes "
		       "from %s(%s) in %d ms\n\n",
		       query->lookup->doing_xfr ?
				query->byte_count : (isc_uint64_t)bytes,
		       fromtext, query->servname,
257
		       (int)diff/1000);
258
259
260
	}
}

Michael Sawyer's avatar
Michael Sawyer committed
261
262
263
/*
 * Callback from dighost.c to print that it is trying a server.
 * Not used in dig.
264
 * XXX print_trying
Michael Sawyer's avatar
Michael Sawyer committed
265
 */
266
void
Brian Wellington's avatar
Brian Wellington committed
267
trying(char *frm, dig_lookup_t *lookup) {
268
269
	UNUSED(frm);
	UNUSED(lookup);
270
}
271

272
/*%
Michael Sawyer's avatar
Michael Sawyer committed
273
274
 * Internal print routine used to print short form replies.
 */
275
static isc_result_t
276
say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
277
	isc_result_t result;
278
279
	isc_uint64_t diff;
	isc_time_t now;
280
	char store[sizeof("12345678901234567890")];
281

282
283
284
285
286
	if (query->lookup->trace || query->lookup->ns_search_only) {
		result = dns_rdatatype_totext(rdata->type, buf);
		if (result != ISC_R_SUCCESS)
			return (result);
		ADD_STRING(buf, " ");
287
	}
288
289
	result = dns_rdata_totext(rdata, NULL, buf);
	check_result(result, "dns_rdata_totext");
290
	if (query->lookup->identify) {
291
		TIME_NOW(&now);
292
		diff = isc_time_microdiff(&now, &query->time_sent);
293
294
		ADD_STRING(buf, " from server ");
		ADD_STRING(buf, query->servname);
295
		snprintf(store, 19, " in %d ms.", (int)diff/1000);
296
		ADD_STRING(buf, store);
297
	}
298
	ADD_STRING(buf, "\n");
299
	return (ISC_R_SUCCESS);
300
301
}

302
/*%
Michael Sawyer's avatar
Michael Sawyer committed
303
304
 * short_form message print handler.  Calls above say_message()
 */
305
static isc_result_t
306
307
short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
	     isc_buffer_t *buf, dig_query_t *query)
308
{
309
	dns_name_t *name;
310
311
	dns_rdataset_t *rdataset;
	isc_buffer_t target;
312
	isc_result_t result, loopresult;
313
314
	dns_name_t empty_name;
	char t[4096];
315
	dns_rdata_t rdata = DNS_RDATA_INIT;
316

317
	UNUSED(flags);
318

319
	dns_name_init(&empty_name, NULL);
320
	result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
321
322
323
324
325
326
327
	if (result == ISC_R_NOMORE)
		return (ISC_R_SUCCESS);
	else if (result != ISC_R_SUCCESS)
		return (result);

	for (;;) {
		name = NULL;
328
		dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
329

330
		isc_buffer_init(&target, t, sizeof(t));
331
332
333
334

		for (rdataset = ISC_LIST_HEAD(name->list);
		     rdataset != NULL;
		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
335
336
337
338
339
			loopresult = dns_rdataset_first(rdataset);
			while (loopresult == ISC_R_SUCCESS) {
				dns_rdataset_current(rdataset, &rdata);
				result = say_message(&rdata, query,
						     buf);
340
341
				check_result(result, "say_message");
				loopresult = dns_rdataset_next(rdataset);
342
				dns_rdata_reset(&rdata);
343
			}
344
		}
345
		result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
346
347
348
349
		if (result == ISC_R_NOMORE)
			break;
		else if (result != ISC_R_SUCCESS)
			return (result);
350
	}
351

352
	return (ISC_R_SUCCESS);
353
}
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#ifdef DIG_SIGCHASE
isc_result_t
printrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
	      isc_buffer_t *target)
{
	isc_result_t result;
	dns_master_style_t *style = NULL;
	unsigned int styleflags = 0;

	if (rdataset == NULL || owner_name == NULL || target == NULL)
		return(ISC_FALSE);

	styleflags |= DNS_STYLEFLAG_REL_OWNER;
	if (nottl)
		styleflags |= DNS_STYLEFLAG_NO_TTL;
	if (noclass)
		styleflags |= DNS_STYLEFLAG_NO_CLASS;
	if (multiline) {
		styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
		styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
		styleflags |= DNS_STYLEFLAG_REL_DATA;
		styleflags |= DNS_STYLEFLAG_OMIT_TTL;
		styleflags |= DNS_STYLEFLAG_TTL;
		styleflags |= DNS_STYLEFLAG_MULTILINE;
		styleflags |= DNS_STYLEFLAG_COMMENT;
	}
	if (multiline || (nottl && noclass))
		result = dns_master_stylecreate(&style, styleflags,
						24, 24, 24, 32, 80, 8, mctx);
	else if (nottl || noclass)
		result = dns_master_stylecreate(&style, styleflags,
						24, 24, 32, 40, 80, 8, mctx);
	else 
		result = dns_master_stylecreate(&style, styleflags,
						24, 32, 40, 48, 80, 8, mctx);
	check_result(result, "dns_master_stylecreate");

	result = dns_master_rdatasettotext(owner_name, rdataset, style, target);

	if (style != NULL)
		dns_master_styledestroy(&style, mctx);
  
	return(result);
}
#endif
399

Michael Sawyer's avatar
Michael Sawyer committed
400
401
402
/*
 * Callback from dighost.c to print the reply from a server
 */
403
isc_result_t
404
printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
405
	isc_result_t result;
406
	dns_messagetextflag_t flags;
407
	isc_buffer_t *buf = NULL;
408
	unsigned int len = OUTPUTBUF;
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
	dns_master_style_t *style = NULL;
	unsigned int styleflags = 0;

	styleflags |= DNS_STYLEFLAG_REL_OWNER;
	if (nottl)
		styleflags |= DNS_STYLEFLAG_NO_TTL;
	if (noclass)
		styleflags |= DNS_STYLEFLAG_NO_CLASS;
	if (multiline) {
		styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
		styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
		styleflags |= DNS_STYLEFLAG_REL_DATA;
		styleflags |= DNS_STYLEFLAG_OMIT_TTL;
		styleflags |= DNS_STYLEFLAG_TTL;
		styleflags |= DNS_STYLEFLAG_MULTILINE;
		styleflags |= DNS_STYLEFLAG_COMMENT;
	}
	if (multiline || (nottl && noclass))
		result = dns_master_stylecreate(&style, styleflags,
						24, 24, 24, 32, 80, 8, mctx);
	else if (nottl || noclass)
		result = dns_master_stylecreate(&style, styleflags,
						24, 24, 32, 40, 80, 8, mctx);
	else 
		result = dns_master_stylecreate(&style, styleflags,
						24, 32, 40, 48, 80, 8, mctx);
	check_result(result, "dns_master_stylecreate");
436

437
	if (query->lookup->cmdline[0] != 0) {
438
439
		if (!short_form)
			fputs(query->lookup->cmdline, stdout);
440
441
		query->lookup->cmdline[0]=0;
	}
442
443
444
	debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
	      query->lookup->comments ? "comments" : "nocomments",
	      short_form ? "short_form" : "long_form");
445
446
447
448
449
450
451
452
453

	flags = 0;
	if (!headers) {
		flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
	}
	if (!query->lookup->comments)
		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;

454
455
	result = ISC_R_SUCCESS;

456
	result = isc_buffer_allocate(mctx, &buf, len);
457
	check_result(result, "isc_buffer_allocate");
458
459

	if (query->lookup->comments && !short_form) {
460
		if (query->lookup->cmdline[0] != 0)
461
			printf("; %s\n", query->lookup->cmdline);
462
463
464
465
		if (msg == query->lookup->sendmsg)
			printf(";; Sending:\n");
		else
			printf(";; Got answer:\n");
466

467
		if (headers) {
468
469
470
471
			printf(";; ->>HEADER<<- opcode: %s, status: %s, "
			       "id: %u\n",
			       opcodetext[msg->opcode], rcodetext[msg->rcode],
			       msg->id);
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
			printf(";; flags:");
			if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
				printf(" qr");
			if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
				printf(" aa");
			if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
				printf(" tc");
			if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
				printf(" rd");
			if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
				printf(" ra");
			if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
				printf(" ad");
			if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
				printf(" cd");
487

488
489
490
491
492
493
			printf("; QUERY: %u, ANSWER: %u, "
			       "AUTHORITY: %u, ADDITIONAL: %u\n",
			       msg->counts[DNS_SECTION_QUESTION],
			       msg->counts[DNS_SECTION_ANSWER],
			       msg->counts[DNS_SECTION_AUTHORITY],
			       msg->counts[DNS_SECTION_ADDITIONAL]);
494

495
496
			if (msg != query->lookup->sendmsg &&
			    (msg->flags & DNS_MESSAGEFLAG_RD) != 0 &&
497
			    (msg->flags & DNS_MESSAGEFLAG_RA) == 0)
Mark Andrews's avatar
Mark Andrews committed
498
				printf(";; WARNING: recursion requested "
499
				       "but not available\n");
500
		}
501
502
503
		if (msg != query->lookup->sendmsg && extrabytes != 0U)
			printf(";; WARNING: Messages has %u extra byte%s at "
			       "end\n", extrabytes, extrabytes != 0 ? "s" : "");
504
	}
505

506
repopulate_buffer:
Ben Cottrell's avatar
Ben Cottrell committed
507

508
	if (query->lookup->comments && headers && !short_form) {
Ben Cottrell's avatar
Ben Cottrell committed
509
510
		result = dns_message_pseudosectiontotext(msg,
			 DNS_PSEUDOSECTION_OPT,
511
			 style, flags, buf);
512
		if (result == ISC_R_NOSPACE) {
513
buftoosmall:
514
515
516
517
			len += OUTPUTBUF;
			isc_buffer_free(&buf);
			result = isc_buffer_allocate(mctx, &buf, len);
			if (result == ISC_R_SUCCESS)
518
519
				goto repopulate_buffer;
			else
520
				goto cleanup;
521
		}
Ben Cottrell's avatar
Ben Cottrell committed
522
523
524
525
		check_result(result,
		     "dns_message_pseudosectiontotext");
	}

526
527
528
529
	if (query->lookup->section_question && headers) {
		if (!short_form) {
			result = dns_message_sectiontotext(msg,
						       DNS_SECTION_QUESTION,
530
						       style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
531
			if (result == ISC_R_NOSPACE)
532
				goto buftoosmall;
533
534
			check_result(result, "dns_message_sectiontotext");
		}
535
	}
536
537
538
539
	if (query->lookup->section_answer) {
		if (!short_form) {
			result = dns_message_sectiontotext(msg,
						       DNS_SECTION_ANSWER,
540
						       style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
541
			if (result == ISC_R_NOSPACE)
542
				goto buftoosmall;
543
			check_result(result, "dns_message_sectiontotext");
544
		} else {
545
			result = short_answer(msg, flags, buf, query);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
546
			if (result == ISC_R_NOSPACE)
547
				goto buftoosmall;
548
			check_result(result, "short_answer");
549
		}
550
	}
551
552
553
554
	if (query->lookup->section_authority) {
		if (!short_form) {
			result = dns_message_sectiontotext(msg,
						       DNS_SECTION_AUTHORITY,
555
						       style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
556
			if (result == ISC_R_NOSPACE)
557
				goto buftoosmall;
558
559
			check_result(result, "dns_message_sectiontotext");
		}
560
	}
561
562
563
564
	if (query->lookup->section_additional) {
		if (!short_form) {
			result = dns_message_sectiontotext(msg,
						      DNS_SECTION_ADDITIONAL,
565
						      style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
566
			if (result == ISC_R_NOSPACE)
567
				goto buftoosmall;
568
			check_result(result, "dns_message_sectiontotext");
569
570
571
			/*
			 * Only print the signature on the first record.
			 */
572
573
574
575
			if (headers) {
				result = dns_message_pseudosectiontotext(
						   msg,
						   DNS_PSEUDOSECTION_TSIG,
576
						   style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
577
				if (result == ISC_R_NOSPACE)
578
					goto buftoosmall;
579
580
581
582
583
				check_result(result,
					  "dns_message_pseudosectiontotext");
				result = dns_message_pseudosectiontotext(
						   msg,
						   DNS_PSEUDOSECTION_SIG0,
584
						   style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
585
				if (result == ISC_R_NOSPACE)
586
					goto buftoosmall;
587
588
589
				check_result(result,
					   "dns_message_pseudosectiontotext");
			}
590
		}
591
	}
592

593
	if (headers && query->lookup->comments && !short_form)
594
		printf("\n");
595

596
597
	printf("%.*s", (int)isc_buffer_usedlength(buf),
	       (char *)isc_buffer_base(buf));
598
	isc_buffer_free(&buf);
599
600
601
602

cleanup:
	if (style != NULL)
		dns_master_styledestroy(&style, mctx);
603
604
	return (result);
}
605

606
/*%
Michael Sawyer's avatar
Michael Sawyer committed
607
608
 * print the greeting message when the program first starts up.
 */
609
static void
610
611
printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
	int i;
612
	int remaining;
613
614
	static isc_boolean_t first = ISC_TRUE;
	char append[MXNAME];
615
616

	if (printcmd) {
617
		lookup->cmdline[sizeof(lookup->cmdline) - 1] = 0;
618
619
620
621
		snprintf(lookup->cmdline, sizeof(lookup->cmdline),
			 "%s; <<>> DiG " VERSION " <<>>",
			 first?"\n":"");
		i = 1;
622
		while (i < argc) {
623
			snprintf(append, sizeof(append), " %s", argv[i++]);
624
625
626
			remaining = sizeof(lookup->cmdline) -
				    strlen(lookup->cmdline) - 1;
			strncat(lookup->cmdline, append, remaining);
627
		}
628
629
630
		remaining = sizeof(lookup->cmdline) -
			    strlen(lookup->cmdline) - 1;
		strncat(lookup->cmdline, "\n", remaining);
631
632
633
634
635
636
637
		if (first && addresscount != 0) {
			snprintf(append, sizeof(append),
				 "; (%d server%s found)\n",
				 addresscount,
				 addresscount > 1 ? "s" : "");
			remaining = sizeof(lookup->cmdline) -
				    strlen(lookup->cmdline) - 1;
638
			strncat(lookup->cmdline, append, remaining);
639
		}
640
		if (first) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
641
			snprintf(append, sizeof(append), 
642
643
644
645
				 ";; global options: %s %s\n",
			       short_form ? "short_form" : "",
			       printcmd ? "printcmd" : "");
			first = ISC_FALSE;
646
647
648
			remaining = sizeof(lookup->cmdline) -
				    strlen(lookup->cmdline) - 1;
			strncat(lookup->cmdline, append, remaining);
649
650
651
652
		}
	}
}

653
static isc_uint32_t
Mark Andrews's avatar
Mark Andrews committed
654
parse_uint(char *arg, const char *desc, isc_uint32_t max) {
655
	isc_result_t result;
656
657
	isc_uint32_t tmp;

658
659
660
661
662
	result = isc_parse_uint32(&tmp, arg, 10);
	if (result == ISC_R_SUCCESS && tmp > max)
		result = ISC_R_RANGE;
	if (result != ISC_R_SUCCESS)
		fatal("%s '%s': %s", desc, arg, isc_result_totext(result));
663
664
665
	return (tmp);
}

666
/*%
667
668
 * We're not using isc_commandline_parse() here since the command line
 * syntax of dig is quite a bit different from that which can be described
669
 * by that routine.
670
 * XXX doc options
671
 */
672
673
674
675
676
677

static void
plus_option(char *option, isc_boolean_t is_batchfile,
	    dig_lookup_t *lookup)
{
	char option_store[256];
678
	char *cmd, *value, *ptr;
679
	isc_boolean_t state = ISC_TRUE;
680
681
682
#ifdef DIG_SIGCHASE
	size_t n;
#endif
683
684

	strncpy(option_store, option, sizeof(option_store));
685
	option_store[sizeof(option_store)-1]=0;
686
	ptr = option_store;
Andreas Gustafsson's avatar
spacing    
Andreas Gustafsson committed
687
	cmd = next_token(&ptr,"=");
688
	if (cmd == NULL) {
689
		printf(";; Invalid option %s\n", option_store);
690
691
		return;
	}
Andreas Gustafsson's avatar
spacing    
Andreas Gustafsson committed
692
	value = ptr;
693
	if (strncasecmp(cmd, "no", 2)==0) {
694
695
696
		cmd += 2;
		state = ISC_FALSE;
	}
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711

#define FULLCHECK(A) \
	do { \
		size_t _l = strlen(cmd); \
		if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
			goto invalid_option; \
	} while (0)
#define FULLCHECK2(A, B) \
	do { \
		size_t _l = strlen(cmd); \
		if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
		    (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
			goto invalid_option; \
	} while (0)

712
	switch (cmd[0]) {
713
	case 'a':
714
		switch (cmd[1]) {
715
716
		case 'a': /* aaonly / aaflag */
			FULLCHECK2("aaonly", "aaflag");
717
718
719
			lookup->aaonly = state;
			break;
		case 'd': 
720
			switch (cmd[2]) {
721
			case 'd': /* additional */
722
				FULLCHECK("additional");
723
724
725
				lookup->section_additional = state;
				break;
			case 'f': /* adflag */
726
				FULLCHECK("adflag");
727
				lookup->adflag = state;
728
729
730
731
732
733
				break;
			default:
				goto invalid_option;
			}
			break;
		case 'l': /* all */
734
			FULLCHECK("all");
735
736
737
738
739
			lookup->section_question = state;
			lookup->section_authority = state;
			lookup->section_answer = state;
			lookup->section_additional = state;
			lookup->comments = state;
740
741
			lookup->stats = state;
			printcmd = state;
742
743
			break;
		case 'n': /* answer */
744
			FULLCHECK("answer");
745
746
747
			lookup->section_answer = state;
			break;
		case 'u': /* authority */
748
			FULLCHECK("authority");
749
750
751
752
753
754
			lookup->section_authority = state;
			break;
		default:
			goto invalid_option;
		}
		break;
755
	case 'b':
756
		switch (cmd[1]) {
757
		case 'e':/* besteffort */
758
			FULLCHECK("besteffort");
759
760
761
			lookup->besteffort = state;
			break;
		case 'u':/* bufsize */
762
			FULLCHECK("bufsize");
763
764
765
766
			if (value == NULL)
				goto need_value;
			if (!state)
				goto invalid_option;
Mark Andrews's avatar
Mark Andrews committed
767
768
			lookup->udpsize = (isc_uint16_t) parse_uint(value,
						    "buffer size", COMMSIZE);
769
770
			break;
		default:
771
			goto invalid_option;
772
		}
773
774
		break;
	case 'c':
775
		switch (cmd[1]) {
776
		case 'd':/* cdflag */
777
			FULLCHECK("cdflag");
778
779
			lookup->cdflag = state;
			break;
780
781
		case 'l': /* cl */
			FULLCHECK("cl");
782
			noclass = ISC_TF(!state);
783
			break;
784
		case 'm': /* cmd */
785
			FULLCHECK("cmd");
786
787
788
			printcmd = state;
			break;
		case 'o': /* comments */
789
			FULLCHECK("comments");
790
			lookup->comments = state;
791
792
			if (lookup == default_lookup)
				pluscomm = state;
793
794
795
796
797
798
			break;
		default:
			goto invalid_option;
		}
		break;
	case 'd':
799
		switch (cmd[1]) {
800
		case 'e': /* defname */
801
			FULLCHECK("defname");
802
			usesearch = state;
803
			break;
804
		case 'n': /* dnssec */	
805
			FULLCHECK("dnssec");
806
807
			if (state && lookup->edns == -1)
				lookup->edns = 0;
808
809
			lookup->dnssec = state;
			break;
810
		case 'o': /* domain */	
811
			FULLCHECK("domain");
812
813
814
815
			if (value == NULL)
				goto need_value;
			if (!state)
				goto invalid_option;
816
817
			strncpy(domainopt, value, sizeof(domainopt));
			domainopt[sizeof(domainopt)-1] = '\0';
818
819
820
821
822
			break;
		default:
			goto invalid_option;
		}
		break;
823
824
825
826
827
828
829
830
831
832
	case 'e':
		FULLCHECK("edns");
		if (!state) {
			lookup->edns = -1;
			break;
		}
		if (value == NULL)
			goto need_value;
		lookup->edns = (isc_int16_t) parse_uint(value, "edns", 255);
		break;
833
	case 'f': /* fail */
834
		FULLCHECK("fail");
835
		lookup->servfail_stops = state;
836
		break;
837
	case 'i':
838
		switch (cmd[1]) {
839
		case 'd': /* identify */
840
			FULLCHECK("identify");
841
842
843
844
			lookup->identify = state;
			break;
		case 'g': /* ignore */
		default: /* Inherets default for compatibility */
845
			FULLCHECK("ignore");
846
847
848
			lookup->ignore = ISC_TRUE;
		}
		break;
849
	case 'm': /* multiline */
850
		FULLCHECK("multiline");
851
852
		multiline = state;
		break;
853
	case 'n':
854
		switch (cmd[1]) {
855
		case 'd': /* ndots */
856
			FULLCHECK("ndots");
857
858
859
860
			if (value == NULL)
				goto need_value;
			if (!state)
				goto invalid_option;
Mark Andrews's avatar
Mark Andrews committed
861
			ndots = parse_uint(value, "ndots", MAXNDOTS);
862
863
			break;
		case 's': /* nssearch */
864
			FULLCHECK("nssearch");
865
866
867
			lookup->ns_search_only = state;
			if (state) {
				lookup->trace_root = ISC_TRUE;
868
				lookup->recurse = ISC_TRUE;
869
870
871
872
873
874
				lookup->identify = ISC_TRUE;
				lookup->stats = ISC_FALSE;
				lookup->comments = ISC_FALSE;
				lookup->section_additional = ISC_FALSE;
				lookup->section_authority = ISC_FALSE;
				lookup->section_question = ISC_FALSE;
875
				lookup->rdtype = dns_rdatatype_ns;
Michael Sawyer's avatar
Michael Sawyer committed
876
				lookup->rdtypeset = ISC_TRUE;
877
878
879
880
881
882
883
884
				short_form = ISC_TRUE;
			}
			break;
		default:
			goto invalid_option;
		}
		break;
	case 'q': 
885
		switch (cmd[1]) {
886
		case 'r': /* qr */
887
			FULLCHECK("qr");
888
889
890
			qr = state;
			break;
		case 'u': /* question */
891
			FULLCHECK("question");
892
			lookup->section_question = state;
893
894
			if (lookup == default_lookup)
				plusquest = state;
895
896
897
898
899
			break;
		default:
			goto invalid_option;
		}
		break;
900
901
902
903
904
	case 'r':
		switch (cmd[1]) {
		case 'e':
			switch (cmd[2]) {
			case 'c': /* recurse */
905
				FULLCHECK("recurse");
906
907
				lookup->recurse = state;
				break;
908
			case 't': /* retry / retries */
909
				FULLCHECK2("retry", "retries");
910
911
912
913
914
915
916
917
918
919
920
				if (value == NULL)
					goto need_value;
				if (!state)
					goto invalid_option;
				lookup->retries = parse_uint(value, "retries",
						       MAXTRIES - 1);
				lookup->retries++;
				break;
			default:
				goto invalid_option;
			}
921
			break;
922
923
924
		default:
			goto invalid_option;
		}
925
926
		break;
	case 's':
927
		switch (cmd[1]) {
928
		case 'e': /* search */
929
			FULLCHECK("search");
930
931
			usesearch = state;
			break;