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