wire_test.c revision 1.8
1/*	$NetBSD: wire_test.c,v 1.8 2023/01/25 21:43:24 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				{
215					*wp++ = *rp;
216					len++;
217				}
218				rp++;
219			}
220			if (len == 0U) {
221				continue;
222			}
223			if (len % 2 != 0U) {
224				fprintf(stderr, "bad input format: %lu\n",
225					(unsigned long)len);
226				exit(1);
227			}
228
229			rp = s;
230			for (i = 0; i < len; i += 2) {
231				c = fromhex(*rp++);
232				c *= 16;
233				c += fromhex(*rp++);
234				result = isc_buffer_reserve(&input, 1);
235				RUNTIME_CHECK(result == ISC_R_SUCCESS);
236				isc_buffer_putuint8(input, (uint8_t)c);
237			}
238		}
239	}
240
241	if (need_close) {
242		fclose(f);
243	}
244
245	if (tcp) {
246		while (isc_buffer_remaininglength(input) != 0) {
247			unsigned int tcplen;
248
249			if (isc_buffer_remaininglength(input) < 2) {
250				fprintf(stderr, "premature end of packet\n");
251				exit(1);
252			}
253			tcplen = isc_buffer_getuint16(input);
254
255			if (isc_buffer_remaininglength(input) < tcplen) {
256				fprintf(stderr, "premature end of packet\n");
257				exit(1);
258			}
259			process_message(input);
260		}
261	} else {
262		process_message(input);
263	}
264
265	isc_buffer_free(&input);
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	dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
283
284	result = dns_message_parse(message, source, parseflags);
285	if (result == DNS_R_RECOVERABLE) {
286		result = ISC_R_SUCCESS;
287	}
288	CHECKRESULT(result, "dns_message_parse failed");
289
290	result = printmessage(message);
291	CHECKRESULT(result, "printmessage() failed");
292
293	if (printmemstats) {
294		isc_mem_stats(mctx, stdout);
295	}
296
297	if (dorender) {
298		unsigned char b2[64 * 1024];
299		isc_buffer_t buffer;
300		dns_compress_t cctx;
301
302		isc_buffer_init(&buffer, b2, sizeof(b2));
303
304		/*
305		 * XXXMLG
306		 * Changing this here is a hack, and should not be done in
307		 * reasonable application code, ever.
308		 */
309		message->from_to_wire = DNS_MESSAGE_INTENTRENDER;
310
311		for (i = 0; i < DNS_SECTION_MAX; i++) {
312			message->counts[i] = 0; /* Another hack XXX */
313		}
314
315		result = dns_compress_init(&cctx, -1, mctx);
316		CHECKRESULT(result, "dns_compress_init() failed");
317
318		result = dns_message_renderbegin(message, &cctx, &buffer);
319		CHECKRESULT(result, "dns_message_renderbegin() failed");
320
321		result = dns_message_rendersection(message,
322						   DNS_SECTION_QUESTION, 0);
323		CHECKRESULT(result, "dns_message_rendersection(QUESTION) "
324				    "failed");
325
326		result = dns_message_rendersection(message, DNS_SECTION_ANSWER,
327						   0);
328		CHECKRESULT(result, "dns_message_rendersection(ANSWER) failed");
329
330		result = dns_message_rendersection(message,
331						   DNS_SECTION_AUTHORITY, 0);
332		CHECKRESULT(result, "dns_message_rendersection(AUTHORITY) "
333				    "failed");
334
335		result = dns_message_rendersection(message,
336						   DNS_SECTION_ADDITIONAL, 0);
337		CHECKRESULT(result, "dns_message_rendersection(ADDITIONAL) "
338				    "failed");
339
340		dns_message_renderend(message);
341
342		dns_compress_invalidate(&cctx);
343
344		message->from_to_wire = DNS_MESSAGE_INTENTPARSE;
345		dns_message_detach(&message);
346
347		printf("Message rendered.\n");
348		if (printmemstats) {
349			isc_mem_stats(mctx, stdout);
350		}
351
352		dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
353
354		result = dns_message_parse(message, &buffer, parseflags);
355		CHECKRESULT(result, "dns_message_parse failed");
356
357		result = printmessage(message);
358		CHECKRESULT(result, "printmessage() failed");
359	}
360	dns_message_detach(&message);
361}
362