dig.c 60.1 KB
Newer Older
Brian Wellington's avatar
Brian Wellington committed
1
/*
2
 * Copyright (C) 2000-2017  Internet Systems Consortium, Inc. ("ISC")
3
 *
4
5
6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8
 */

9
/*! \file */
10

11
12
#include <config.h>
#include <stdlib.h>
Brian Wellington's avatar
Brian Wellington committed
13
#include <time.h>
14
#include <ctype.h>
15

David Lawrence's avatar
David Lawrence committed
16
#include <isc/app.h>
17
#include <isc/netaddr.h>
18
#include <isc/parseint.h>
Mark Andrews's avatar
Mark Andrews committed
19
#include <isc/print.h>
David Lawrence's avatar
David Lawrence committed
20
#include <isc/string.h>
21
#include <isc/task.h>
22
#include <isc/util.h>
David Lawrence's avatar
David Lawrence committed
23

24
25
#include <pk11/site.h>

26
27
#include <dns/byaddr.h>
#include <dns/fixedname.h>
28
#include <dns/masterdump.h>
29
#include <dns/message.h>
Brian Wellington's avatar
Brian Wellington committed
30
#include <dns/name.h>
31
#include <dns/rcode.h>
32
33
34
#include <dns/rdata.h>
#include <dns/rdataset.h>
#include <dns/rdatatype.h>
35
#include <dns/rdataclass.h>
36
#include <dns/result.h>
37
#include <dns/tsig.h>
38

Mark Andrews's avatar
Mark Andrews committed
39
40
#include <dig/dig.h>

41
42
#define ADD_STRING(b, s) { 				\
	if (strlen(s) >= isc_buffer_availablelength(b)) \
Automatic Updater's avatar
Automatic Updater committed
43
		return (ISC_R_NOSPACE); 		\
44
45
46
	else 						\
		isc_buffer_putstr(b, s); 		\
}
47

48
#define DIG_MAX_ADDRESSES 20
49

50
dig_lookup_t *default_lookup = NULL;
51

Brian Wellington's avatar
Brian Wellington committed
52
53
54
static char *batchname = NULL;
static FILE *batchfp = NULL;
static char *argv0;
55
static int addresscount = 0;
56

Brian Wellington's avatar
Brian Wellington committed
57
static char domainopt[DNS_NAME_MAXTEXT];
58
static char hexcookie[81];
59

Brian Wellington's avatar
Brian Wellington committed
60
static isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
61
	ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
62
#ifdef DIG_SIGCHASE
63
	multiline = ISC_FALSE, nottl = ISC_FALSE, noclass = ISC_FALSE,
Evan Hunt's avatar
Evan Hunt committed
64
	nocrypto = ISC_FALSE, ttlunits = ISC_FALSE,
65
#endif
Evan Hunt's avatar
Evan Hunt committed
66
	ipv4only = ISC_FALSE, ipv6only = ISC_FALSE;
67
static isc_uint32_t splitwidth = 0xffffffff;
68

Evan Hunt's avatar
Evan Hunt committed
69
/*% rrcomments are neither explicitly enabled nor disabled by default */
70
#ifdef DIG_SIGCHASE
Evan Hunt's avatar
Evan Hunt committed
71
static int rrcomments = 0;
72
#endif
Evan Hunt's avatar
Evan Hunt committed
73

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

94
95
96
97
98
static const char *
rcode_totext(dns_rcode_t rcode) {
	static char buf[64];
	isc_buffer_t b;
	isc_result_t result;
99

100
101
102
103
104
105
106
107
108
	memset(buf, 0, sizeof(buf));
	isc_buffer_init(&b, buf + 1, sizeof(buf) - 2);
	result = dns_rcode_totext(rcode, &b);
	RUNTIME_CHECK(result == ISC_R_SUCCESS);
	if (strspn(buf + 1, "0123456789") == strlen(buf + 1)) {
		buf[0] = '?';
		return(buf);
	}
	return (buf + 1);
109
110
}

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

Francis Dupont's avatar
Francis Dupont committed
120
121
122
ISC_PLATFORM_NORETURN_PRE static void
usage(void) ISC_PLATFORM_NORETURN_POST;

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
"                 -4                  (use IPv4 query transport only)\n"
"                 -6                  (use IPv6 query transport only)\n"
Mark Andrews's avatar
Mark Andrews committed
149
"                 -b address[#port]   (bind to source address/port)\n"
150
151
152
153
154
"                 -c class            (specify query class)\n"
"                 -f filename         (batch mode)\n"
"                 -i                  (use IP6.INT for IPv6 reverse lookups)\n"
"                 -k keyfile          (specify tsig key file)\n"
"                 -m                  (enable memory usage debugging)\n"
155
"                 -p port             (specify port number)\n"
156
"                 -q name             (specify query name)\n"
157
"                 -t type             (specify query type)\n"
158
"                 -u                  (display times in usec instead of msec)\n"
159
"                 -x dot-notation     (shortcut for reverse lookups)\n"
160
"                 -y [hmac:]name:key  (specify named base64 tsig key)\n"
161
"        d-opt    is of the form +keyword[=value], where keyword is:\n"
162
"                 +[no]aaflag         (Set AA flag in query (+[no]aaflag))\n"
163
"                 +[no]aaonly         (Set AA flag in query (+[no]aaflag))\n"
164
"                 +[no]additional     (Control display of additional section)\n"
165
"                 +[no]adflag         (Set AD flag in query (default on))\n"
166
167
168
169
170
171
172
173
"                 +[no]all            (Set or clear all display flags)\n"
"                 +[no]answer         (Control display of answer section)\n"
"                 +[no]authority      (Control display of authority section)\n"
"                 +[no]badcookie      (Retry BADCOOKIE responses)\n"
"                 +[no]besteffort     (Try to parse even illegal messages)\n"
"                 +bufsize=###        (Set EDNS0 Max UDP packet size)\n"
"                 +[no]cdflag         (Set checking disabled flag in query)\n"
"                 +[no]class          (Control display of class in records)\n"
174
"                 +[no]cmd            (Control display of command line)\n"
175
"                 +[no]comments       (Control display of comment lines)\n"
176
"                 +[no]cookie         (Add a COOKIE option to the request)\n"
177
178
"                 +[no]crypto         (Control display of cryptographic "
				       "fields in records)\n"
179
"                 +[no]defname        (Use search list (+[no]search))\n"
180
"                 +[no]dnssec         (Request DNSSEC records)\n"
181
182
183
184
185
186
187
"                 +domain=###         (Set default domainname)\n"
"                 +[no]dscp[=###]     (Set the DSCP value to ### [0..63])\n"
"                 +[no]edns[=###]     (Set EDNS version) [0]\n"
"                 +ednsflags=###      (Set EDNS flag bits)\n"
"                 +[no]ednsnegotiation (Set EDNS version negotiation)\n"
"                 +ednsopt=###[:value] (Send specified EDNS option)\n"
"                 +noednsopt          (Clear list of +ednsopt options)\n"
188
"                 +[no]expire         (Request time to expire)\n"
189
"                 +[no]fail           (Don't try next server on SERVFAIL)\n"
190
"                 +[no]header-only    (Send query without a question section)\n"
191
"                 +[no]identify       (ID responders in short answers)\n"
192
"                 +[no]idnout         (convert IDN response)\n"
193
"                 +[no]ignore         (Don't revert to TCP for TC responses.)\n"
194
"                 +[no]keepalive      (Request EDNS TCP keepalive)\n"
195
"                 +[no]keepopen       (Keep the TCP socket open between queries)\n"
196
"                 +[no]mapped         (Allow mapped IPv4 over IPv6)\n"
197
198
199
200
201
"                 +[no]multiline      (Print records in an expanded format)\n"
"                 +ndots=###          (Set search NDOTS value)\n"
"                 +[no]nsid           (Request Name Server ID)\n"
"                 +[no]nssearch       (Search all authoritative nameservers)\n"
"                 +[no]onesoa         (AXFR prints only one soa record)\n"
Mark Andrews's avatar
Mark Andrews committed
202
"                 +[no]opcode=###     (Set the opcode of the request)\n"
203
"                 +padding=###        (Set padding block size [0])\n"
204
205
206
207
208
209
210
211
212
213
214
"                 +[no]qr             (Print question before sending)\n"
"                 +[no]question       (Control display of question section)\n"
"                 +[no]rdflag         (Recursive mode (+[no]recurse))\n"
"                 +[no]recurse        (Recursive mode (+[no]rdflag))\n"
"                 +retry=###          (Set number of UDP retries) [2]\n"
"                 +[no]rrcomments     (Control display of per-record "
					"comments)\n"
"                 +[no]search         (Set whether to use searchlist)\n"
"                 +[no]short          (Display nothing except short\n"
"                                      form of answer)\n"
"                 +[no]showsearch     (Search with intermediate results)\n"
215
216
217
#ifdef DIG_SIGCHASE
"                 +[no]sigchase       (Chase DNSSEC signatures)\n"
#endif
218
"                 +[no]split=##       (Split hex/base64 fields into chunks)\n"
219
220
221
222
223
224
225
226
227
228
229
230
231
232
"                 +[no]stats          (Control display of statistics)\n"
"                 +subnet=addr        (Set edns-client-subnet option)\n"
"                 +[no]tcp            (TCP mode (+[no]vc))\n"
"                 +timeout=###        (Set query timeout) [5]\n"
#if defined(DIG_SIGCHASE) && DIG_SIGCHASE_TD
"                 +[no]topdown        (Do +sigchase in top-down mode)\n"
#endif
"                 +[no]trace          (Trace delegation down from root [+dnssec])\n"
#ifdef DIG_SIGCHASE
"                 +trusted-key=####   (Trusted Key to use with +sigchase)\n"
#endif
"                 +tries=###          (Set number of UDP attempts) [3]\n"
"                 +[no]ttlid          (Control display of ttls in records)\n"
"                 +[no]ttlunits       (Display TTLs in human-readable units)\n"
233
"                 +[no]unknownformat  (Print RDATA in RFC 3597 \"unknown\" format)\n"
234
235
"                 +[no]vc             (TCP mode (+[no]tcp))\n"
"                 +[no]zflag          (Set Z flag in query)\n"
236
"        global d-opts and servers (before host name) affect all queries.\n"
237
238
239
"        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",
240
	stdout);
241
}
242

243
/*%
Michael Sawyer's avatar
Michael Sawyer committed
244
245
 * Callback from dighost.c to print the received message.
 */
246
void
247
received(int bytes, isc_sockaddr_t *from, dig_query_t *query) {
248
249
	isc_uint64_t diff;
	time_t tnow;
Evan Hunt's avatar
Evan Hunt committed
250
251
	struct tm tmnow;
	char time_str[100];
252
253
254
	char fromtext[ISC_SOCKADDR_FORMATSIZE];

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

256
	if (query->lookup->stats && !short_form) {
Mark Andrews's avatar
Mark Andrews committed
257
		diff = isc_time_microdiff(&query->time_recv, &query->time_sent);
258
		if (query->lookup->use_usec)
259
260
261
			printf(";; Query time: %ld usec\n", (long) diff);
		else
			printf(";; Query time: %ld msec\n", (long) diff / 1000);
262
		printf(";; SERVER: %s(%s)\n", fromtext, query->servname);
263
		time(&tnow);
Evan Hunt's avatar
Evan Hunt committed
264
265
		tmnow  = *localtime(&tnow);
		if (strftime(time_str, sizeof(time_str),
266
			     "%a %b %d %H:%M:%S %Z %Y", &tmnow) > 0U)
Evan Hunt's avatar
Evan Hunt committed
267
			printf(";; WHEN: %s\n", time_str);
268
		if (query->lookup->doing_xfr) {
269
270
271
272
			printf(";; XFR size: %u records (messages %u, "
			       "bytes %" ISC_PRINT_QUADFORMAT "u)\n",
			       query->rr_count, query->msg_count,
			       query->byte_count);
273
		} else {
274
			printf(";; MSG SIZE  rcvd: %u\n", bytes);
275
		}
276
		if (key != NULL) {
277
			if (!validated)
278
279
				puts(";; WARNING -- Some TSIG could not "
				     "be validated");
280
281
		}
		if ((key == NULL) && (keysecret[0] != 0)) {
282
			puts(";; WARNING -- TSIG key was not used.");
283
		}
284
		puts("");
285
	} else if (query->lookup->identify && !short_form) {
Mark Andrews's avatar
Mark Andrews committed
286
		diff = isc_time_microdiff(&query->time_recv, &query->time_sent);
287
		if (query->lookup->use_usec)
288
289
290
			printf(";; Received %" ISC_PRINT_QUADFORMAT "u bytes "
			       "from %s(%s) in %ld us\n\n",
			       query->lookup->doing_xfr
Tinderbox User's avatar
Tinderbox User committed
291
292
				 ? query->byte_count
				 : (isc_uint64_t)bytes,
293
294
295
296
297
			       fromtext, query->userarg, (long) diff);
		else
			printf(";; Received %" ISC_PRINT_QUADFORMAT "u bytes "
			       "from %s(%s) in %ld ms\n\n",
			       query->lookup->doing_xfr
Tinderbox User's avatar
Tinderbox User committed
298
299
				 ?  query->byte_count
				 : (isc_uint64_t)bytes,
300
			       fromtext, query->userarg, (long) diff / 1000);
301
302
303
	}
}

Michael Sawyer's avatar
Michael Sawyer committed
304
305
306
/*
 * Callback from dighost.c to print that it is trying a server.
 * Not used in dig.
307
 * XXX print_trying
Michael Sawyer's avatar
Michael Sawyer committed
308
 */
309
void
Brian Wellington's avatar
Brian Wellington committed
310
trying(char *frm, dig_lookup_t *lookup) {
311
312
	UNUSED(frm);
	UNUSED(lookup);
313
}
314

315
/*%
Michael Sawyer's avatar
Michael Sawyer committed
316
317
 * Internal print routine used to print short form replies.
 */
318
static isc_result_t
319
say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
320
	isc_result_t result;
321
	isc_uint64_t diff;
322
	char store[sizeof("12345678901234567890")];
323
	unsigned int styleflags = 0;
324

325
326
327
328
329
	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, " ");
330
	}
331

Evan Hunt's avatar
Evan Hunt committed
332
	/* Turn on rrcomments if explicitly enabled */
333
	if (query->lookup->rrcomments > 0)
334
		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
335
	if (query->lookup->nocrypto)
336
		styleflags |= DNS_STYLEFLAG_NOCRYPTO;
337
338
	if (query->lookup->print_unknown_format)
		styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
339
340
	result = dns_rdata_tofmttext(rdata, NULL, styleflags, 0,
				     splitwidth, " ", buf);
341
342
	if (result == ISC_R_NOSPACE)
		return (result);
343
	check_result(result, "dns_rdata_totext");
344
	if (query->lookup->identify) {
Mark Andrews's avatar
Mark Andrews committed
345
		diff = isc_time_microdiff(&query->time_recv, &query->time_sent);
346
347
		ADD_STRING(buf, " from server ");
		ADD_STRING(buf, query->servname);
348
		if (query->lookup->use_usec)
349
350
351
			snprintf(store, 19, " in %ld us.", (long) diff);
		else
			snprintf(store, 19, " in %ld ms.", (long) diff / 1000);
352
		ADD_STRING(buf, store);
353
	}
354
	ADD_STRING(buf, "\n");
355
	return (ISC_R_SUCCESS);
356
357
}

358
/*%
Michael Sawyer's avatar
Michael Sawyer committed
359
360
 * short_form message print handler.  Calls above say_message()
 */
361
static isc_result_t
362
363
short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
	     isc_buffer_t *buf, dig_query_t *query)
364
{
365
	dns_name_t *name;
366
	dns_rdataset_t *rdataset;
367
	isc_result_t result, loopresult;
368
	dns_name_t empty_name;
369
	dns_rdata_t rdata = DNS_RDATA_INIT;
370

371
	UNUSED(flags);
372

373
	dns_name_init(&empty_name, NULL);
374
	result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
375
376
377
378
379
380
381
	if (result == ISC_R_NOMORE)
		return (ISC_R_SUCCESS);
	else if (result != ISC_R_SUCCESS)
		return (result);

	for (;;) {
		name = NULL;
382
		dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
383
384
385
386

		for (rdataset = ISC_LIST_HEAD(name->list);
		     rdataset != NULL;
		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
387
388
389
390
391
			loopresult = dns_rdataset_first(rdataset);
			while (loopresult == ISC_R_SUCCESS) {
				dns_rdataset_current(rdataset, &rdata);
				result = say_message(&rdata, query,
						     buf);
392
393
				if (result == ISC_R_NOSPACE)
					return (result);
394
395
				check_result(result, "say_message");
				loopresult = dns_rdataset_next(rdataset);
396
				dns_rdata_reset(&rdata);
397
			}
398
		}
399
		result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
400
401
402
403
		if (result == ISC_R_NOMORE)
			break;
		else if (result != ISC_R_SUCCESS)
			return (result);
404
	}
405

406
	return (ISC_R_SUCCESS);
407
}
408
409
410
411
412
413
414
415
416
417
418
419
420
#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;
Evan Hunt's avatar
Evan Hunt committed
421
422
	if (ttlunits)
		styleflags |= DNS_STYLEFLAG_TTL_UNITS;
423
424
425
426
	if (nottl)
		styleflags |= DNS_STYLEFLAG_NO_TTL;
	if (noclass)
		styleflags |= DNS_STYLEFLAG_NO_CLASS;
427
428
	if (nocrypto)
		styleflags |= DNS_STYLEFLAG_NOCRYPTO;
Evan Hunt's avatar
Evan Hunt committed
429
430
431
	/* Turn on rrcomments if explicitly enabled */
	if (rrcomments > 0)
		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
432
433
434
435
436
437
438
439
	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;
Evan Hunt's avatar
Evan Hunt committed
440
441
442
		/* Turn on rrcomments if not explicitly disabled */
		if (rrcomments >= 0)
			styleflags |= DNS_STYLEFLAG_RRCOMMENT;
443
	}
444

445
	if (multiline || (nottl && noclass))
446
447
448
		result = dns_master_stylecreate2(&style, styleflags,
						24, 24, 24, 32, 80, 8,
						splitwidth, mctx);
449
	else if (nottl || noclass)
450
451
452
		result = dns_master_stylecreate2(&style, styleflags,
						24, 24, 32, 40, 80, 8,
						splitwidth, mctx);
Automatic Updater's avatar
Automatic Updater committed
453
	else
454
455
456
		result = dns_master_stylecreate2(&style, styleflags,
						24, 32, 40, 48, 80, 8,
						splitwidth, mctx);
457
458
459
460
461
462
	check_result(result, "dns_master_stylecreate");

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

	if (style != NULL)
		dns_master_styledestroy(&style, mctx);
Automatic Updater's avatar
Automatic Updater committed
463

464
465
466
	return(result);
}
#endif
467

468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
static isc_boolean_t
isdotlocal(dns_message_t *msg) {
	isc_result_t result;
	static unsigned char local_ndata[] = { "\005local\0" };
	static unsigned char local_offsets[] = { 0, 6 };
	static dns_name_t local = {
		DNS_NAME_MAGIC,
		local_ndata, 7, 2,
		DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
		local_offsets, NULL,
		{(void *)-1, (void *)-1},
		{NULL, NULL}
	};

	for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
	     result == ISC_R_SUCCESS;
	     result = dns_message_nextname(msg, DNS_SECTION_QUESTION))
	{
		dns_name_t *name = NULL;
		dns_message_currentname(msg, DNS_SECTION_QUESTION, &name);
		if (dns_name_issubdomain(name, &local))
			return (ISC_TRUE);
	}
	return (ISC_FALSE);
}

Michael Sawyer's avatar
Michael Sawyer committed
494
495
496
/*
 * Callback from dighost.c to print the reply from a server
 */
497
isc_result_t
498
printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
499
	isc_result_t result;
500
	dns_messagetextflag_t flags;
501
	isc_buffer_t *buf = NULL;
502
	unsigned int len = OUTPUTBUF;
503
504
505
506
	dns_master_style_t *style = NULL;
	unsigned int styleflags = 0;

	styleflags |= DNS_STYLEFLAG_REL_OWNER;
507
508
	if (query->lookup->comments)
		styleflags |= DNS_STYLEFLAG_COMMENT;
509
510
	if (query->lookup->print_unknown_format)
		styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
Evan Hunt's avatar
Evan Hunt committed
511
	/* Turn on rrcomments if explicitly enabled */
512
	if (query->lookup->rrcomments > 0)
513
		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
514
	if (query->lookup->ttlunits)
Evan Hunt's avatar
Evan Hunt committed
515
		styleflags |= DNS_STYLEFLAG_TTL_UNITS;
516
	if (query->lookup->nottl)
517
		styleflags |= DNS_STYLEFLAG_NO_TTL;
518
	if (query->lookup->noclass)
519
		styleflags |= DNS_STYLEFLAG_NO_CLASS;
520
	if (query->lookup->nocrypto)
521
		styleflags |= DNS_STYLEFLAG_NOCRYPTO;
522
	if (query->lookup->multiline) {
523
524
525
526
527
528
		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;
Evan Hunt's avatar
Evan Hunt committed
529
		/* Turn on rrcomments unless explicitly disabled */
530
		if (query->lookup->rrcomments >= 0)
Evan Hunt's avatar
Evan Hunt committed
531
			styleflags |= DNS_STYLEFLAG_RRCOMMENT;
532
	}
533
534
	if (query->lookup->multiline ||
	    (query->lookup->nottl && query->lookup->noclass))
535
		result = dns_master_stylecreate2(&style, styleflags,
Automatic Updater's avatar
Automatic Updater committed
536
						 24, 24, 24, 32, 80, 8,
537
						 splitwidth, mctx);
538
	else if (query->lookup->nottl || query->lookup->noclass)
539
540
541
		result = dns_master_stylecreate2(&style, styleflags,
						 24, 24, 32, 40, 80, 8,
						 splitwidth, mctx);
Automatic Updater's avatar
Automatic Updater committed
542
	else
543
544
545
		result = dns_master_stylecreate2(&style, styleflags,
						 24, 32, 40, 48, 80, 8,
						 splitwidth, mctx);
546
	check_result(result, "dns_master_stylecreate");
547

548
	if (query->lookup->cmdline[0] != 0) {
549
550
		if (!short_form)
			fputs(query->lookup->cmdline, stdout);
551
552
		query->lookup->cmdline[0]=0;
	}
553
554
555
	debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
	      query->lookup->comments ? "comments" : "nocomments",
	      short_form ? "short_form" : "long_form");
556
557
558
559
560
561

	flags = 0;
	if (!headers) {
		flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
	}
562
563
	if (query->lookup->onesoa &&
	    query->lookup->rdtype == dns_rdatatype_axfr)
564
565
		flags |= (query->msg_count == 0) ? DNS_MESSAGETEXTFLAG_ONESOA :
						   DNS_MESSAGETEXTFLAG_OMITSOA;
566
567
568
	if (!query->lookup->comments)
		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;

569
	result = isc_buffer_allocate(mctx, &buf, len);
570
	check_result(result, "isc_buffer_allocate");
571
572

	if (query->lookup->comments && !short_form) {
573
		if (query->lookup->cmdline[0] != 0)
574
			printf("; %s\n", query->lookup->cmdline);
575
576
577
578
		if (msg == query->lookup->sendmsg)
			printf(";; Sending:\n");
		else
			printf(";; Got answer:\n");
579

580
		if (headers) {
581
582
583
584
585
586
			if (isdotlocal(msg)) {
				printf(";; WARNING: .local is reserved for "
				       "Multicast DNS\n;; You are currently "
				       "testing what happens when an mDNS "
				       "query is leaked to DNS\n");
			}
587
588
			printf(";; ->>HEADER<<- opcode: %s, status: %s, "
			       "id: %u\n",
589
590
			       opcodetext[msg->opcode],
			       rcode_totext(msg->rcode),
591
			       msg->id);
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
			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");
607
608
			if ((msg->flags & 0x0040U) != 0)
				printf("; MBZ: 0x4");
609

610
611
612
613
614
615
			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]);
616

617
618
			if (msg != query->lookup->sendmsg &&
			    (msg->flags & DNS_MESSAGEFLAG_RD) != 0 &&
619
			    (msg->flags & DNS_MESSAGEFLAG_RA) == 0)
Mark Andrews's avatar
Mark Andrews committed
620
				printf(";; WARNING: recursion requested "
621
				       "but not available\n");
622
		}
623
624
625
626
627
		if (msg != query->lookup->sendmsg &&
		    query->lookup->edns != -1 && msg->opt == NULL &&
		    (msg->rcode == dns_rcode_formerr ||
		     msg->rcode == dns_rcode_notimp))
			printf("\n;; WARNING: EDNS query returned status "
628
629
630
			       "%s - retry with '%s+noedns'\n",
			       rcode_totext(msg->rcode),
			       query->lookup->dnssec ? "+nodnssec ": "");
631
		if (msg != query->lookup->sendmsg && extrabytes != 0U)
Evan Hunt's avatar
Evan Hunt committed
632
			printf(";; WARNING: Message has %u extra byte%s at "
633
			       "end\n", extrabytes, extrabytes != 0 ? "s" : "");
634
	}
635

636
repopulate_buffer:
Ben Cottrell's avatar
Ben Cottrell committed
637

638
	if (query->lookup->comments && headers && !short_form) {
Ben Cottrell's avatar
Ben Cottrell committed
639
640
		result = dns_message_pseudosectiontotext(msg,
			 DNS_PSEUDOSECTION_OPT,
641
			 style, flags, buf);
642
		if (result == ISC_R_NOSPACE) {
643
buftoosmall:
644
645
646
647
			len += OUTPUTBUF;
			isc_buffer_free(&buf);
			result = isc_buffer_allocate(mctx, &buf, len);
			if (result == ISC_R_SUCCESS)
648
649
				goto repopulate_buffer;
			else
650
				goto cleanup;
651
		}
Ben Cottrell's avatar
Ben Cottrell committed
652
653
654
655
		check_result(result,
		     "dns_message_pseudosectiontotext");
	}

656
657
658
659
	if (query->lookup->section_question && headers) {
		if (!short_form) {
			result = dns_message_sectiontotext(msg,
						       DNS_SECTION_QUESTION,
660
						       style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
661
			if (result == ISC_R_NOSPACE)
662
				goto buftoosmall;
663
664
			check_result(result, "dns_message_sectiontotext");
		}
665
	}
666
667
668
669
	if (query->lookup->section_answer) {
		if (!short_form) {
			result = dns_message_sectiontotext(msg,
						       DNS_SECTION_ANSWER,
670
						       style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
671
			if (result == ISC_R_NOSPACE)
672
				goto buftoosmall;
673
			check_result(result, "dns_message_sectiontotext");
674
		} else {
675
			result = short_answer(msg, flags, buf, query);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
676
			if (result == ISC_R_NOSPACE)
677
				goto buftoosmall;
678
			check_result(result, "short_answer");
679
		}
680
	}
681
682
683
684
	if (query->lookup->section_authority) {
		if (!short_form) {
			result = dns_message_sectiontotext(msg,
						       DNS_SECTION_AUTHORITY,
685
						       style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
686
			if (result == ISC_R_NOSPACE)
687
				goto buftoosmall;
688
689
			check_result(result, "dns_message_sectiontotext");
		}
690
	}
691
692
693
694
	if (query->lookup->section_additional) {
		if (!short_form) {
			result = dns_message_sectiontotext(msg,
						      DNS_SECTION_ADDITIONAL,
695
						      style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
696
			if (result == ISC_R_NOSPACE)
697
				goto buftoosmall;
698
			check_result(result, "dns_message_sectiontotext");
699
700
701
			/*
			 * Only print the signature on the first record.
			 */
702
703
704
705
			if (headers) {
				result = dns_message_pseudosectiontotext(
						   msg,
						   DNS_PSEUDOSECTION_TSIG,
706
						   style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
707
				if (result == ISC_R_NOSPACE)
708
					goto buftoosmall;
709
710
711
712
713
				check_result(result,
					  "dns_message_pseudosectiontotext");
				result = dns_message_pseudosectiontotext(
						   msg,
						   DNS_PSEUDOSECTION_SIG0,
714
						   style, flags, buf);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
715
				if (result == ISC_R_NOSPACE)
716
					goto buftoosmall;
717
718
719
				check_result(result,
					   "dns_message_pseudosectiontotext");
			}
720
		}
721
	}
722

723
	if (headers && query->lookup->comments && !short_form)
724
		printf("\n");
725

726
727
	printf("%.*s", (int)isc_buffer_usedlength(buf),
	       (char *)isc_buffer_base(buf));
728
	isc_buffer_free(&buf);
729
730
731
732

cleanup:
	if (style != NULL)
		dns_master_styledestroy(&style, mctx);
733
734
	return (result);
}
735

736
/*%
Michael Sawyer's avatar
Michael Sawyer committed
737
738
 * print the greeting message when the program first starts up.
 */
739
static void
740
741
printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
	int i;
Evan Hunt's avatar
Evan Hunt committed
742
	size_t remaining;
743
744
	static isc_boolean_t first = ISC_TRUE;
	char append[MXNAME];
745
746

	if (printcmd) {
747
		lookup->cmdline[sizeof(lookup->cmdline) - 1] = 0;
748
749
750
751
		snprintf(lookup->cmdline, sizeof(lookup->cmdline),
			 "%s; <<>> DiG " VERSION " <<>>",
			 first?"\n":"");
		i = 1;
752
		while (i < argc) {
753
			snprintf(append, sizeof(append), " %s", argv[i++]);
754
755
756
			remaining = sizeof(lookup->cmdline) -
				    strlen(lookup->cmdline) - 1;
			strncat(lookup->cmdline, append, remaining);
757
		}
758
759
760
		remaining = sizeof(lookup->cmdline) -
			    strlen(lookup->cmdline) - 1;
		strncat(lookup->cmdline, "\n", remaining);
761
762
763
764
765
766
767
		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;
768
			strncat(lookup->cmdline, append, remaining);
769
		}
770
		if (first) {
Automatic Updater's avatar
Automatic Updater committed
771
			snprintf(append, sizeof(append),
772
				 ";; global options:%s%s\n",
Automatic Updater's avatar
Automatic Updater committed
773
774
				 short_form ? " +short" : "",
				 printcmd ? " +cmd" : "");
775
			first = ISC_FALSE;
776
777
778
			remaining = sizeof(lookup->cmdline) -
				    strlen(lookup->cmdline) - 1;
			strncat(lookup->cmdline, append, remaining);
779
780
781
782
		}
	}
}

783
/*%
784
785
 * 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
786
 * by that routine.
787
 * XXX doc options
788
 */
789
790

static void
791
plus_option(const char *option, isc_boolean_t is_batchfile,
792
793
	    dig_lookup_t *lookup)
{
794
	isc_result_t result;
795
	char option_store[256];
796
	char *cmd, *value, *ptr, *code;
797
	isc_uint32_t num;
798
	isc_boolean_t state = ISC_TRUE;
799
	size_t n;
800
801

	strncpy(option_store, option, sizeof(option_store));
802
	option_store[sizeof(option_store)-1]=0;
803
	ptr = option_store;
804
	cmd = next_token(&ptr, "=");
805
	if (cmd == NULL) {
806
		printf(";; Invalid option %s\n", option_store);
807
808
		return;
	}
Andreas Gustafsson's avatar
spacing    
Andreas Gustafsson committed
809
	value = ptr;
810
	if (strncasecmp(cmd, "no", 2)==0) {
811
812
813
		cmd += 2;
		state = ISC_FALSE;
	}
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828

#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)

829
	switch (cmd[0]) {
830
	case 'a':
831
		switch (cmd[1]) {
832
833
		case 'a': /* aaonly / aaflag */
			FULLCHECK2("aaonly", "aaflag");
834
835
			lookup->aaonly = state;
			break;
Automatic Updater's avatar
Automatic Updater committed
836
		case 'd':
837
			switch (cmd[2]) {
838
			case 'd': /* additional */
839
				FULLCHECK("additional");
840
841
842
				lookup->section_additional = state;
				break;
			case 'f': /* adflag */
843
			case '\0': /* +ad is a synonym for +adflag */
844
				FULLCHECK("adflag");
845
				lookup->adflag = state;
846
847
848
849
850
851
				break;
			default:
				goto invalid_option;
			}
			break;
		case 'l': /* all */
852
			FULLCHECK("all");
853
854
855
856
857
			lookup->section_question = state;
			lookup->section_authority = state;
			lookup->section_answer = state;
			lookup->section_additional = state;
			lookup->comments = state;
858
859
			lookup->stats =