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