1/*	$NetBSD$	*/
2
3/*
4 * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2001  Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* Id: wire_test.c,v 1.67 2007/06/19 23:46:59 tbox Exp  */
21
22#include <config.h>
23
24#include <stdlib.h>
25
26#include <isc/buffer.h>
27#include <isc/commandline.h>
28#include <isc/mem.h>
29#include <isc/string.h>
30#include <isc/util.h>
31
32#include <dns/result.h>
33
34#include "printmsg.h"
35
36int parseflags = 0;
37isc_mem_t *mctx;
38isc_boolean_t printmemstats = ISC_FALSE;
39isc_boolean_t dorender = ISC_FALSE;
40
41static void
42process_message(isc_buffer_t *source);
43
44static inline void
45CHECKRESULT(isc_result_t result, const char *msg) {
46	if (result != ISC_R_SUCCESS) {
47		printf("%s: %s\n", msg, dns_result_totext(result));
48
49		exit(1);
50	}
51}
52
53static int
54fromhex(char c) {
55	if (c >= '0' && c <= '9')
56		return (c - '0');
57	else if (c >= 'a' && c <= 'f')
58		return (c - 'a' + 10);
59	else if (c >= 'A' && c <= 'F')
60		return (c - 'A' + 10);
61
62	printf("bad input format: %02x\n", c);
63	exit(3);
64	/* NOTREACHED */
65}
66
67static void
68usage(void) {
69	fprintf(stderr, "wire_test [-p] [-b] [-s] [-r]\n");
70	fprintf(stderr, "\t-p\tPreserve order of the records in messages\n");
71	fprintf(stderr, "\t-b\tBest-effort parsing (ignore some errors)\n");
72	fprintf(stderr, "\t-s\tPrint memory statistics\n");
73	fprintf(stderr, "\t-r\tAfter parsing, re-render the message\n");
74	fprintf(stderr, "\t-t\tTCP mode - ignore the first 2 bytes\n");
75}
76
77int
78main(int argc, char *argv[]) {
79	char *rp, *wp;
80	unsigned char *bp;
81	isc_buffer_t source;
82	size_t len, i;
83	int n;
84	FILE *f;
85	isc_boolean_t need_close = ISC_FALSE;
86	unsigned char b[64 * 1024];
87	char s[4000];
88	isc_boolean_t tcp = ISC_FALSE;
89	int ch;
90
91	mctx = NULL;
92	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
93
94	while ((ch = isc_commandline_parse(argc, argv, "pbsrt")) != -1) {
95		switch (ch) {
96			case 'p':
97				parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER;
98				break;
99			case 'b':
100				parseflags |= DNS_MESSAGEPARSE_BESTEFFORT;
101				break;
102			case 's':
103				printmemstats = ISC_TRUE;
104				break;
105			case 'r':
106				dorender = ISC_TRUE;
107				break;
108			case 't':
109				tcp = ISC_TRUE;
110				break;
111			default:
112				usage();
113				exit(1);
114		}
115	}
116
117	argc -= isc_commandline_index;
118	argv += isc_commandline_index;
119
120	if (argc > 1) {
121		f = fopen(argv[1], "r");
122		if (f == NULL) {
123			printf("fopen failed\n");
124			exit(1);
125		}
126		need_close = ISC_TRUE;
127	} else
128		f = stdin;
129
130	bp = b;
131	while (fgets(s, sizeof(s), f) != NULL) {
132		rp = s;
133		wp = s;
134		len = 0;
135		while (*rp != '\0') {
136			if (*rp == '#')
137				break;
138			if (*rp != ' ' && *rp != '\t' &&
139			    *rp != '\r' && *rp != '\n') {
140				*wp++ = *rp;
141				len++;
142			}
143			rp++;
144		}
145		if (len == 0U)
146			break;
147		if (len % 2 != 0U) {
148			printf("bad input format: %lu\n", (unsigned long)len);
149			exit(1);
150		}
151		if (len > sizeof(b) * 2) {
152			printf("input too long\n");
153			exit(2);
154		}
155		rp = s;
156		for (i = 0; i < len; i += 2) {
157			n = fromhex(*rp++);
158			n *= 16;
159			n += fromhex(*rp++);
160			*bp++ = n;
161		}
162	}
163
164	if (need_close)
165		fclose(f);
166
167	if (tcp) {
168		unsigned char *p = b;
169		while (p < bp) {
170			unsigned int len;
171
172			if (p + 2 > bp) {
173				printf("premature end of packet\n");
174				exit(1);
175			}
176			len = p[0] << 8 | p[1];
177
178			if (p + 2 + len > bp) {
179				printf("premature end of packet\n");
180				exit(1);
181			}
182			isc_buffer_init(&source, p + 2, len);
183			isc_buffer_add(&source, len);
184			process_message(&source);
185			p += 2 + len;
186		}
187	} else {
188		isc_buffer_init(&source, b, sizeof(b));
189		isc_buffer_add(&source, bp - b);
190		process_message(&source);
191	}
192
193	if (printmemstats)
194		isc_mem_stats(mctx, stdout);
195	isc_mem_destroy(&mctx);
196
197	return (0);
198}
199
200static void
201process_message(isc_buffer_t *source) {
202	dns_message_t *message;
203	isc_result_t result;
204	int i;
205
206	message = NULL;
207	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
208	CHECKRESULT(result, "dns_message_create failed");
209
210	result = dns_message_parse(message, source, parseflags);
211	if (result == DNS_R_RECOVERABLE)
212		result = ISC_R_SUCCESS;
213	CHECKRESULT(result, "dns_message_parse failed");
214
215	result = printmessage(message);
216	CHECKRESULT(result, "printmessage() failed");
217
218	if (printmemstats)
219		isc_mem_stats(mctx, stdout);
220
221	if (dorender) {
222		unsigned char b2[64 * 1024];
223		isc_buffer_t buffer;
224		dns_compress_t cctx;
225
226		isc_buffer_init(&buffer, b2, sizeof(b2));
227
228		/*
229		 * XXXMLG
230		 * Changing this here is a hack, and should not be done in
231		 * reasonable application code, ever.
232	 	*/
233		message->from_to_wire = DNS_MESSAGE_INTENTRENDER;
234
235 		for (i = 0; i < DNS_SECTION_MAX; i++)
236			message->counts[i] = 0;  /* Another hack XXX */
237
238		result = dns_compress_init(&cctx, -1, mctx);
239		CHECKRESULT(result, "dns_compress_init() failed");
240
241		result = dns_message_renderbegin(message, &cctx, &buffer);
242		CHECKRESULT(result, "dns_message_renderbegin() failed");
243
244		result = dns_message_rendersection(message,
245						   DNS_SECTION_QUESTION, 0);
246		CHECKRESULT(result,
247			    "dns_message_rendersection(QUESTION) failed");
248
249		result = dns_message_rendersection(message,
250						   DNS_SECTION_ANSWER, 0);
251		CHECKRESULT(result,
252			    "dns_message_rendersection(ANSWER) failed");
253
254		result = dns_message_rendersection(message,
255						   DNS_SECTION_AUTHORITY, 0);
256		CHECKRESULT(result,
257			    "dns_message_rendersection(AUTHORITY) failed");
258
259		result = dns_message_rendersection(message,
260						   DNS_SECTION_ADDITIONAL, 0);
261		CHECKRESULT(result,
262			    "dns_message_rendersection(ADDITIONAL) failed");
263
264		dns_message_renderend(message);
265
266		dns_compress_invalidate(&cctx);
267
268		message->from_to_wire = DNS_MESSAGE_INTENTPARSE;
269		dns_message_destroy(&message);
270
271		printf("Message rendered.\n");
272		if (printmemstats)
273			isc_mem_stats(mctx, stdout);
274
275		result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
276					    &message);
277		CHECKRESULT(result, "dns_message_create failed");
278
279		result = dns_message_parse(message, &buffer, parseflags);
280		CHECKRESULT(result, "dns_message_parse failed");
281
282		result = printmessage(message);
283		CHECKRESULT(result, "printmessage() failed");
284	}
285	dns_message_destroy(&message);
286}
287