wire_test.c revision 1.2
1/*	$NetBSD: wire_test.c,v 1.2 2018/08/12 13:02:28 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14#include <config.h>
15
16#include <stdlib.h>
17
18#include <isc/buffer.h>
19#include <isc/commandline.h>
20#include <isc/file.h>
21#include <isc/mem.h>
22#include <isc/print.h>
23#include <isc/string.h>
24#include <isc/util.h>
25
26#include <dns/message.h>
27#include <dns/result.h>
28
29int parseflags = 0;
30isc_mem_t *mctx = NULL;
31isc_boolean_t printmemstats = ISC_FALSE;
32isc_boolean_t dorender = ISC_FALSE;
33
34static void
35process_message(isc_buffer_t *source);
36
37static isc_result_t
38printmessage(dns_message_t *msg);
39
40static inline void
41CHECKRESULT(isc_result_t result, const char *msg) {
42	if (result != ISC_R_SUCCESS) {
43		printf("%s: %s\n", msg, dns_result_totext(result));
44
45		exit(1);
46	}
47}
48
49static int
50fromhex(char c) {
51	if (c >= '0' && c <= '9')
52		return (c - '0');
53	else if (c >= 'a' && c <= 'f')
54		return (c - 'a' + 10);
55	else if (c >= 'A' && c <= 'F')
56		return (c - 'A' + 10);
57
58	fprintf(stderr, "bad input format: %02x\n", c);
59	exit(3);
60	/* NOTREACHED */
61}
62
63static void
64usage(void) {
65	fprintf(stderr, "wire_test [-b] [-d] [-p] [-r] [-s]\n");
66	fprintf(stderr, "          [-m {usage|trace|record|size|mctx}]\n");
67	fprintf(stderr, "          [filename]\n\n");
68	fprintf(stderr, "\t-b\tBest-effort parsing (ignore some errors)\n");
69	fprintf(stderr, "\t-d\tRead input as raw binary data\n");
70	fprintf(stderr, "\t-p\tPreserve order of the records in messages\n");
71	fprintf(stderr, "\t-r\tAfter parsing, re-render the message\n");
72	fprintf(stderr, "\t-s\tPrint memory statistics\n");
73	fprintf(stderr, "\t-t\tTCP mode - ignore the first 2 bytes\n");
74}
75
76static isc_result_t
77printmessage(dns_message_t *msg) {
78	isc_buffer_t b;
79	char *buf = NULL;
80	int len = 1024;
81	isc_result_t result = ISC_R_SUCCESS;
82
83	do {
84		buf = isc_mem_get(mctx, len);
85		if (buf == NULL) {
86			result = ISC_R_NOMEMORY;
87			break;
88		}
89
90		isc_buffer_init(&b, buf, len);
91		result = dns_message_totext(msg, &dns_master_style_debug,
92					    0, &b);
93		if (result == ISC_R_NOSPACE) {
94			isc_mem_put(mctx, buf, len);
95			len *= 2;
96		} else if (result == ISC_R_SUCCESS)
97			printf("%.*s\n", (int) isc_buffer_usedlength(&b), buf);
98	} while (result == ISC_R_NOSPACE);
99
100	if (buf != NULL)
101		isc_mem_put(mctx, buf, len);
102
103	return (result);
104}
105
106int
107main(int argc, char *argv[]) {
108	isc_buffer_t *input = NULL;
109	isc_boolean_t need_close = ISC_FALSE;
110	isc_boolean_t tcp = ISC_FALSE;
111	isc_boolean_t rawdata = ISC_FALSE;
112	isc_result_t result;
113	isc_uint8_t c;
114	FILE *f;
115	int ch;
116
117#define CMDLINE_FLAGS "bdm:prst"
118	/*
119	 * Process memory debugging argument first.
120	 */
121	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
122		switch (ch) {
123		case 'm':
124			if (strcasecmp(isc_commandline_argument, "record") == 0)
125				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
126			if (strcasecmp(isc_commandline_argument, "trace") == 0)
127				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
128			if (strcasecmp(isc_commandline_argument, "usage") == 0)
129				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
130			if (strcasecmp(isc_commandline_argument, "size") == 0)
131				isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
132			if (strcasecmp(isc_commandline_argument, "mctx") == 0)
133				isc_mem_debugging |= ISC_MEM_DEBUGCTX;
134			break;
135		default:
136			break;
137		}
138	}
139	isc_commandline_reset = ISC_TRUE;
140
141	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
142
143	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
144		switch (ch) {
145			case 'b':
146				parseflags |= DNS_MESSAGEPARSE_BESTEFFORT;
147				break;
148			case 'd':
149				rawdata = ISC_TRUE;
150				break;
151			case 'm':
152				break;
153			case 'p':
154				parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER;
155				break;
156			case 'r':
157				dorender = ISC_TRUE;
158				break;
159			case 's':
160				printmemstats = ISC_TRUE;
161				break;
162			case 't':
163				tcp = ISC_TRUE;
164				break;
165			default:
166				usage();
167				exit(1);
168		}
169	}
170
171	argc -= isc_commandline_index;
172	argv += isc_commandline_index;
173
174	if (argc >= 1) {
175		f = fopen(argv[0], "r");
176		if (f == NULL) {
177			fprintf(stderr, "%s: fopen failed\n", argv[0]);
178			exit(1);
179		}
180		need_close = ISC_TRUE;
181	} else
182		f = stdin;
183
184	result = isc_buffer_allocate(mctx, &input, 64 * 1024);
185	RUNTIME_CHECK(result == ISC_R_SUCCESS);
186
187	if (rawdata) {
188		while (fread(&c, 1, 1, f) != 0) {
189			result = isc_buffer_reserve(&input, 1);
190			RUNTIME_CHECK(result == ISC_R_SUCCESS);
191			isc_buffer_putuint8(input, (isc_uint8_t) c);
192		}
193	} else {
194		char s[BUFSIZ];
195
196		while (fgets(s, sizeof(s), f) != NULL) {
197			char *rp = s, *wp = s;
198			size_t i, len = 0;
199
200			while (*rp != '\0') {
201				if (*rp == '#')
202					break;
203				if (*rp != ' ' && *rp != '\t' &&
204				    *rp != '\r' && *rp != '\n') {
205					*wp++ = *rp;
206					len++;
207				}
208				rp++;
209			}
210			if (len == 0U)
211				continue;
212			if (len % 2 != 0U) {
213				fprintf(stderr, "bad input format: %lu\n",
214				       (unsigned long)len);
215				exit(1);
216			}
217
218			rp = s;
219			for (i = 0; i < len; i += 2) {
220				c = fromhex(*rp++);
221				c *= 16;
222				c += fromhex(*rp++);
223				result = isc_buffer_reserve(&input, 1);
224				RUNTIME_CHECK(result == ISC_R_SUCCESS);
225				isc_buffer_putuint8(input, (isc_uint8_t) c);
226			}
227		}
228	}
229
230	if (need_close)
231		fclose(f);
232
233	if (tcp) {
234		while (isc_buffer_remaininglength(input) != 0) {
235			unsigned int tcplen;
236
237			if (isc_buffer_remaininglength(input) < 2) {
238				fprintf(stderr, "premature end of packet\n");
239				exit(1);
240			}
241			tcplen = isc_buffer_getuint16(input);
242
243			if (isc_buffer_remaininglength(input) < tcplen) {
244				fprintf(stderr, "premature end of packet\n");
245				exit(1);
246			}
247			process_message(input);
248		}
249	} else
250		process_message(input);
251
252	if (input != NULL)
253		isc_buffer_free(&input);
254
255	if (printmemstats)
256		isc_mem_stats(mctx, stdout);
257	isc_mem_destroy(&mctx);
258
259	return (0);
260}
261
262static void
263process_message(isc_buffer_t *source) {
264	dns_message_t *message;
265	isc_result_t result;
266	int i;
267
268	message = NULL;
269	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
270	CHECKRESULT(result, "dns_message_create failed");
271
272	result = dns_message_parse(message, source, parseflags);
273	if (result == DNS_R_RECOVERABLE)
274		result = ISC_R_SUCCESS;
275	CHECKRESULT(result, "dns_message_parse failed");
276
277	result = printmessage(message);
278	CHECKRESULT(result, "printmessage() failed");
279
280	if (printmemstats)
281		isc_mem_stats(mctx, stdout);
282
283	if (dorender) {
284		unsigned char b2[64 * 1024];
285		isc_buffer_t buffer;
286		dns_compress_t cctx;
287
288		isc_buffer_init(&buffer, b2, sizeof(b2));
289
290		/*
291		 * XXXMLG
292		 * Changing this here is a hack, and should not be done in
293		 * reasonable application code, ever.
294		*/
295		message->from_to_wire = DNS_MESSAGE_INTENTRENDER;
296
297		for (i = 0; i < DNS_SECTION_MAX; i++)
298			message->counts[i] = 0;  /* Another hack XXX */
299
300		result = dns_compress_init(&cctx, -1, mctx);
301		CHECKRESULT(result, "dns_compress_init() failed");
302
303		result = dns_message_renderbegin(message, &cctx, &buffer);
304		CHECKRESULT(result, "dns_message_renderbegin() failed");
305
306		result = dns_message_rendersection(message,
307						   DNS_SECTION_QUESTION, 0);
308		CHECKRESULT(result,
309			    "dns_message_rendersection(QUESTION) failed");
310
311		result = dns_message_rendersection(message,
312						   DNS_SECTION_ANSWER, 0);
313		CHECKRESULT(result,
314			    "dns_message_rendersection(ANSWER) failed");
315
316		result = dns_message_rendersection(message,
317						   DNS_SECTION_AUTHORITY, 0);
318		CHECKRESULT(result,
319			    "dns_message_rendersection(AUTHORITY) failed");
320
321		result = dns_message_rendersection(message,
322						   DNS_SECTION_ADDITIONAL, 0);
323		CHECKRESULT(result,
324			    "dns_message_rendersection(ADDITIONAL) failed");
325
326		dns_message_renderend(message);
327
328		dns_compress_invalidate(&cctx);
329
330		message->from_to_wire = DNS_MESSAGE_INTENTPARSE;
331		dns_message_destroy(&message);
332
333		printf("Message rendered.\n");
334		if (printmemstats)
335			isc_mem_stats(mctx, stdout);
336
337		result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
338					    &message);
339		CHECKRESULT(result, "dns_message_create failed");
340
341		result = dns_message_parse(message, &buffer, parseflags);
342		CHECKRESULT(result, "dns_message_parse failed");
343
344		result = printmessage(message);
345		CHECKRESULT(result, "printmessage() failed");
346	}
347	dns_message_destroy(&message);
348}
349