wire_test.c revision 1.4
1/*	$NetBSD: wire_test.c,v 1.4 2020/05/24 19:46:13 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 <inttypes.h>
15#include <stdbool.h>
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;
31bool printmemstats = false;
32bool dorender = 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
59	fprintf(stderr, "bad input format: %02x\n", c);
60	exit(3);
61	/* NOTREACHED */
62}
63
64static void
65usage(void) {
66	fprintf(stderr, "wire_test [-b] [-d] [-p] [-r] [-s]\n");
67	fprintf(stderr, "          [-m {usage|trace|record|size|mctx}]\n");
68	fprintf(stderr, "          [filename]\n\n");
69	fprintf(stderr, "\t-b\tBest-effort parsing (ignore some errors)\n");
70	fprintf(stderr, "\t-d\tRead input as raw binary data\n");
71	fprintf(stderr, "\t-p\tPreserve order of the records in messages\n");
72	fprintf(stderr, "\t-r\tAfter parsing, re-render the message\n");
73	fprintf(stderr, "\t-s\tPrint memory statistics\n");
74	fprintf(stderr, "\t-t\tTCP mode - ignore the first 2 bytes\n");
75}
76
77static isc_result_t
78printmessage(dns_message_t *msg) {
79	isc_buffer_t b;
80	char *buf = NULL;
81	int len = 1024;
82	isc_result_t result = ISC_R_SUCCESS;
83
84	do {
85		buf = isc_mem_get(mctx, len);
86
87		isc_buffer_init(&b, buf, len);
88		result = dns_message_totext(msg, &dns_master_style_debug, 0,
89					    &b);
90		if (result == ISC_R_NOSPACE) {
91			isc_mem_put(mctx, buf, len);
92			len *= 2;
93		} else if (result == ISC_R_SUCCESS) {
94			printf("%.*s\n", (int)isc_buffer_usedlength(&b), buf);
95		}
96	} while (result == ISC_R_NOSPACE);
97
98	if (buf != NULL) {
99		isc_mem_put(mctx, buf, len);
100	}
101
102	return (result);
103}
104
105int
106main(int argc, char *argv[]) {
107	isc_buffer_t *input = NULL;
108	bool need_close = false;
109	bool tcp = false;
110	bool rawdata = false;
111	isc_result_t result;
112	uint8_t c;
113	FILE *f;
114	int ch;
115
116#define CMDLINE_FLAGS "bdm:prst"
117	/*
118	 * Process memory debugging argument first.
119	 */
120	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
121		switch (ch) {
122		case 'm':
123			if (strcasecmp(isc_commandline_argument, "record") == 0)
124			{
125				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
126			}
127			if (strcasecmp(isc_commandline_argument, "trace") == 0)
128			{
129				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
130			}
131			if (strcasecmp(isc_commandline_argument, "usage") == 0)
132			{
133				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
134			}
135			if (strcasecmp(isc_commandline_argument, "size") == 0) {
136				isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
137			}
138			if (strcasecmp(isc_commandline_argument, "mctx") == 0) {
139				isc_mem_debugging |= ISC_MEM_DEBUGCTX;
140			}
141			break;
142		default:
143			break;
144		}
145	}
146	isc_commandline_reset = true;
147
148	isc_mem_create(&mctx);
149
150	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
151		switch (ch) {
152		case 'b':
153			parseflags |= DNS_MESSAGEPARSE_BESTEFFORT;
154			break;
155		case 'd':
156			rawdata = true;
157			break;
158		case 'm':
159			break;
160		case 'p':
161			parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER;
162			break;
163		case 'r':
164			dorender = true;
165			break;
166		case 's':
167			printmemstats = true;
168			break;
169		case 't':
170			tcp = true;
171			break;
172		default:
173			usage();
174			exit(1);
175		}
176	}
177
178	argc -= isc_commandline_index;
179	argv += isc_commandline_index;
180
181	if (argc >= 1) {
182		f = fopen(argv[0], "r");
183		if (f == NULL) {
184			fprintf(stderr, "%s: fopen failed\n", argv[0]);
185			exit(1);
186		}
187		need_close = true;
188	} else {
189		f = stdin;
190	}
191
192	isc_buffer_allocate(mctx, &input, 64 * 1024);
193
194	if (rawdata) {
195		while (fread(&c, 1, 1, f) != 0) {
196			result = isc_buffer_reserve(&input, 1);
197			RUNTIME_CHECK(result == ISC_R_SUCCESS);
198			isc_buffer_putuint8(input, (uint8_t)c);
199		}
200	} else {
201		char s[BUFSIZ];
202
203		while (fgets(s, sizeof(s), f) != NULL) {
204			char *rp = s, *wp = s;
205			size_t i, len = 0;
206
207			while (*rp != '\0') {
208				if (*rp == '#') {
209					break;
210				}
211				if (*rp != ' ' && *rp != '\t' && *rp != '\r' &&
212				    *rp != '\n') {
213					*wp++ = *rp;
214					len++;
215				}
216				rp++;
217			}
218			if (len == 0U) {
219				continue;
220			}
221			if (len % 2 != 0U) {
222				fprintf(stderr, "bad input format: %lu\n",
223					(unsigned long)len);
224				exit(1);
225			}
226
227			rp = s;
228			for (i = 0; i < len; i += 2) {
229				c = fromhex(*rp++);
230				c *= 16;
231				c += fromhex(*rp++);
232				result = isc_buffer_reserve(&input, 1);
233				RUNTIME_CHECK(result == ISC_R_SUCCESS);
234				isc_buffer_putuint8(input, (uint8_t)c);
235			}
236		}
237	}
238
239	if (need_close) {
240		fclose(f);
241	}
242
243	if (tcp) {
244		while (isc_buffer_remaininglength(input) != 0) {
245			unsigned int tcplen;
246
247			if (isc_buffer_remaininglength(input) < 2) {
248				fprintf(stderr, "premature end of packet\n");
249				exit(1);
250			}
251			tcplen = isc_buffer_getuint16(input);
252
253			if (isc_buffer_remaininglength(input) < tcplen) {
254				fprintf(stderr, "premature end of packet\n");
255				exit(1);
256			}
257			process_message(input);
258		}
259	} else {
260		process_message(input);
261	}
262
263	if (input != NULL) {
264		isc_buffer_free(&input);
265	}
266
267	if (printmemstats) {
268		isc_mem_stats(mctx, stdout);
269	}
270	isc_mem_destroy(&mctx);
271
272	return (0);
273}
274
275static void
276process_message(isc_buffer_t *source) {
277	dns_message_t *message;
278	isc_result_t result;
279	int i;
280
281	message = NULL;
282	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
283	CHECKRESULT(result, "dns_message_create failed");
284
285	result = dns_message_parse(message, source, parseflags);
286	if (result == DNS_R_RECOVERABLE) {
287		result = ISC_R_SUCCESS;
288	}
289	CHECKRESULT(result, "dns_message_parse failed");
290
291	result = printmessage(message);
292	CHECKRESULT(result, "printmessage() failed");
293
294	if (printmemstats) {
295		isc_mem_stats(mctx, stdout);
296	}
297
298	if (dorender) {
299		unsigned char b2[64 * 1024];
300		isc_buffer_t buffer;
301		dns_compress_t cctx;
302
303		isc_buffer_init(&buffer, b2, sizeof(b2));
304
305		/*
306		 * XXXMLG
307		 * Changing this here is a hack, and should not be done in
308		 * reasonable application code, ever.
309		 */
310		message->from_to_wire = DNS_MESSAGE_INTENTRENDER;
311
312		for (i = 0; i < DNS_SECTION_MAX; i++) {
313			message->counts[i] = 0; /* Another hack XXX */
314		}
315
316		result = dns_compress_init(&cctx, -1, mctx);
317		CHECKRESULT(result, "dns_compress_init() failed");
318
319		result = dns_message_renderbegin(message, &cctx, &buffer);
320		CHECKRESULT(result, "dns_message_renderbegin() failed");
321
322		result = dns_message_rendersection(message,
323						   DNS_SECTION_QUESTION, 0);
324		CHECKRESULT(result, "dns_message_rendersection(QUESTION) "
325				    "failed");
326
327		result = dns_message_rendersection(message, DNS_SECTION_ANSWER,
328						   0);
329		CHECKRESULT(result, "dns_message_rendersection(ANSWER) failed");
330
331		result = dns_message_rendersection(message,
332						   DNS_SECTION_AUTHORITY, 0);
333		CHECKRESULT(result, "dns_message_rendersection(AUTHORITY) "
334				    "failed");
335
336		result = dns_message_rendersection(message,
337						   DNS_SECTION_ADDITIONAL, 0);
338		CHECKRESULT(result, "dns_message_rendersection(ADDITIONAL) "
339				    "failed");
340
341		dns_message_renderend(message);
342
343		dns_compress_invalidate(&cctx);
344
345		message->from_to_wire = DNS_MESSAGE_INTENTPARSE;
346		dns_message_destroy(&message);
347
348		printf("Message rendered.\n");
349		if (printmemstats) {
350			isc_mem_stats(mctx, stdout);
351		}
352
353		result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
354					    &message);
355		CHECKRESULT(result, "dns_message_create failed");
356
357		result = dns_message_parse(message, &buffer, parseflags);
358		CHECKRESULT(result, "dns_message_parse failed");
359
360		result = printmessage(message);
361		CHECKRESULT(result, "printmessage() failed");
362	}
363	dns_message_destroy(&message);
364}
365