xref: /netbsd/external/mpl/bind/dist/bin/tests/wire_test.c (revision 4ac1c27e)
1*4ac1c27eSchristos /*	$NetBSD: wire_test.c,v 1.8 2023/01/25 21:43:24 christos Exp $	*/
2e2b1b9c0Schristos 
3e2b1b9c0Schristos /*
4e2b1b9c0Schristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5e2b1b9c0Schristos  *
6c0b5d9fbSchristos  * SPDX-License-Identifier: MPL-2.0
7c0b5d9fbSchristos  *
8e2b1b9c0Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
9e2b1b9c0Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
1073584a28Schristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11e2b1b9c0Schristos  *
12e2b1b9c0Schristos  * See the COPYRIGHT file distributed with this work for additional
13e2b1b9c0Schristos  * information regarding copyright ownership.
14e2b1b9c0Schristos  */
15e2b1b9c0Schristos 
16f2e20987Schristos #include <inttypes.h>
17f2e20987Schristos #include <stdbool.h>
18e2b1b9c0Schristos #include <stdlib.h>
19e2b1b9c0Schristos 
20e2b1b9c0Schristos #include <isc/buffer.h>
21e2b1b9c0Schristos #include <isc/commandline.h>
22e2b1b9c0Schristos #include <isc/file.h>
23e2b1b9c0Schristos #include <isc/mem.h>
24e2b1b9c0Schristos #include <isc/print.h>
25e2b1b9c0Schristos #include <isc/string.h>
26e2b1b9c0Schristos #include <isc/util.h>
27e2b1b9c0Schristos 
28e2b1b9c0Schristos #include <dns/message.h>
29e2b1b9c0Schristos #include <dns/result.h>
30e2b1b9c0Schristos 
31e2b1b9c0Schristos int parseflags = 0;
32e2b1b9c0Schristos isc_mem_t *mctx = NULL;
33f2e20987Schristos bool printmemstats = false;
34f2e20987Schristos bool dorender = false;
35e2b1b9c0Schristos 
36e2b1b9c0Schristos static void
37e2b1b9c0Schristos process_message(isc_buffer_t *source);
38e2b1b9c0Schristos 
39e2b1b9c0Schristos static isc_result_t
40e2b1b9c0Schristos printmessage(dns_message_t *msg);
41e2b1b9c0Schristos 
42c0b5d9fbSchristos static void
CHECKRESULT(isc_result_t result,const char * msg)43e2b1b9c0Schristos CHECKRESULT(isc_result_t result, const char *msg) {
44e2b1b9c0Schristos 	if (result != ISC_R_SUCCESS) {
45e2b1b9c0Schristos 		printf("%s: %s\n", msg, dns_result_totext(result));
46e2b1b9c0Schristos 
47e2b1b9c0Schristos 		exit(1);
48e2b1b9c0Schristos 	}
49e2b1b9c0Schristos }
50e2b1b9c0Schristos 
51e2b1b9c0Schristos static int
fromhex(char c)52e2b1b9c0Schristos fromhex(char c) {
539742fdb4Schristos 	if (c >= '0' && c <= '9') {
54e2b1b9c0Schristos 		return (c - '0');
559742fdb4Schristos 	} else if (c >= 'a' && c <= 'f') {
56e2b1b9c0Schristos 		return (c - 'a' + 10);
579742fdb4Schristos 	} else if (c >= 'A' && c <= 'F') {
58e2b1b9c0Schristos 		return (c - 'A' + 10);
599742fdb4Schristos 	}
60e2b1b9c0Schristos 
61e2b1b9c0Schristos 	fprintf(stderr, "bad input format: %02x\n", c);
62e2b1b9c0Schristos 	exit(3);
63e2b1b9c0Schristos }
64e2b1b9c0Schristos 
65e2b1b9c0Schristos static void
usage(void)66e2b1b9c0Schristos usage(void) {
67e2b1b9c0Schristos 	fprintf(stderr, "wire_test [-b] [-d] [-p] [-r] [-s]\n");
68e2b1b9c0Schristos 	fprintf(stderr, "          [-m {usage|trace|record|size|mctx}]\n");
69e2b1b9c0Schristos 	fprintf(stderr, "          [filename]\n\n");
70e2b1b9c0Schristos 	fprintf(stderr, "\t-b\tBest-effort parsing (ignore some errors)\n");
71e2b1b9c0Schristos 	fprintf(stderr, "\t-d\tRead input as raw binary data\n");
72e2b1b9c0Schristos 	fprintf(stderr, "\t-p\tPreserve order of the records in messages\n");
73e2b1b9c0Schristos 	fprintf(stderr, "\t-r\tAfter parsing, re-render the message\n");
74e2b1b9c0Schristos 	fprintf(stderr, "\t-s\tPrint memory statistics\n");
75e2b1b9c0Schristos 	fprintf(stderr, "\t-t\tTCP mode - ignore the first 2 bytes\n");
76e2b1b9c0Schristos }
77e2b1b9c0Schristos 
78e2b1b9c0Schristos static isc_result_t
printmessage(dns_message_t * msg)79e2b1b9c0Schristos printmessage(dns_message_t *msg) {
80e2b1b9c0Schristos 	isc_buffer_t b;
81e2b1b9c0Schristos 	char *buf = NULL;
82e2b1b9c0Schristos 	int len = 1024;
83e2b1b9c0Schristos 	isc_result_t result = ISC_R_SUCCESS;
84e2b1b9c0Schristos 
85e2b1b9c0Schristos 	do {
86e2b1b9c0Schristos 		buf = isc_mem_get(mctx, len);
87e2b1b9c0Schristos 
88e2b1b9c0Schristos 		isc_buffer_init(&b, buf, len);
899742fdb4Schristos 		result = dns_message_totext(msg, &dns_master_style_debug, 0,
909742fdb4Schristos 					    &b);
91e2b1b9c0Schristos 		if (result == ISC_R_NOSPACE) {
92e2b1b9c0Schristos 			isc_mem_put(mctx, buf, len);
93e2b1b9c0Schristos 			len *= 2;
949742fdb4Schristos 		} else if (result == ISC_R_SUCCESS) {
95e2b1b9c0Schristos 			printf("%.*s\n", (int)isc_buffer_usedlength(&b), buf);
969742fdb4Schristos 		}
97e2b1b9c0Schristos 	} while (result == ISC_R_NOSPACE);
98e2b1b9c0Schristos 
999742fdb4Schristos 	if (buf != NULL) {
100e2b1b9c0Schristos 		isc_mem_put(mctx, buf, len);
1019742fdb4Schristos 	}
102e2b1b9c0Schristos 
103e2b1b9c0Schristos 	return (result);
104e2b1b9c0Schristos }
105e2b1b9c0Schristos 
106e2b1b9c0Schristos int
main(int argc,char * argv[])107e2b1b9c0Schristos main(int argc, char *argv[]) {
108e2b1b9c0Schristos 	isc_buffer_t *input = NULL;
109f2e20987Schristos 	bool need_close = false;
110f2e20987Schristos 	bool tcp = false;
111f2e20987Schristos 	bool rawdata = false;
112e2b1b9c0Schristos 	isc_result_t result;
113f2e20987Schristos 	uint8_t c;
114e2b1b9c0Schristos 	FILE *f;
115e2b1b9c0Schristos 	int ch;
116e2b1b9c0Schristos 
117e2b1b9c0Schristos #define CMDLINE_FLAGS "bdm:prst"
118e2b1b9c0Schristos 	/*
119e2b1b9c0Schristos 	 * Process memory debugging argument first.
120e2b1b9c0Schristos 	 */
121e2b1b9c0Schristos 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
122e2b1b9c0Schristos 		switch (ch) {
123e2b1b9c0Schristos 		case 'm':
124e2b1b9c0Schristos 			if (strcasecmp(isc_commandline_argument, "record") == 0)
1259742fdb4Schristos 			{
126e2b1b9c0Schristos 				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
1279742fdb4Schristos 			}
128e2b1b9c0Schristos 			if (strcasecmp(isc_commandline_argument, "trace") == 0)
1299742fdb4Schristos 			{
130e2b1b9c0Schristos 				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
1319742fdb4Schristos 			}
132e2b1b9c0Schristos 			if (strcasecmp(isc_commandline_argument, "usage") == 0)
1339742fdb4Schristos 			{
134e2b1b9c0Schristos 				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
1359742fdb4Schristos 			}
1369742fdb4Schristos 			if (strcasecmp(isc_commandline_argument, "size") == 0) {
137e2b1b9c0Schristos 				isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
1389742fdb4Schristos 			}
1399742fdb4Schristos 			if (strcasecmp(isc_commandline_argument, "mctx") == 0) {
140e2b1b9c0Schristos 				isc_mem_debugging |= ISC_MEM_DEBUGCTX;
1419742fdb4Schristos 			}
142e2b1b9c0Schristos 			break;
143e2b1b9c0Schristos 		default:
144e2b1b9c0Schristos 			break;
145e2b1b9c0Schristos 		}
146e2b1b9c0Schristos 	}
147f2e20987Schristos 	isc_commandline_reset = true;
148e2b1b9c0Schristos 
1499742fdb4Schristos 	isc_mem_create(&mctx);
150e2b1b9c0Schristos 
151e2b1b9c0Schristos 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
152e2b1b9c0Schristos 		switch (ch) {
153e2b1b9c0Schristos 		case 'b':
154e2b1b9c0Schristos 			parseflags |= DNS_MESSAGEPARSE_BESTEFFORT;
155e2b1b9c0Schristos 			break;
156e2b1b9c0Schristos 		case 'd':
157f2e20987Schristos 			rawdata = true;
158e2b1b9c0Schristos 			break;
159e2b1b9c0Schristos 		case 'm':
160e2b1b9c0Schristos 			break;
161e2b1b9c0Schristos 		case 'p':
162e2b1b9c0Schristos 			parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER;
163e2b1b9c0Schristos 			break;
164e2b1b9c0Schristos 		case 'r':
165f2e20987Schristos 			dorender = true;
166e2b1b9c0Schristos 			break;
167e2b1b9c0Schristos 		case 's':
168f2e20987Schristos 			printmemstats = true;
169e2b1b9c0Schristos 			break;
170e2b1b9c0Schristos 		case 't':
171f2e20987Schristos 			tcp = true;
172e2b1b9c0Schristos 			break;
173e2b1b9c0Schristos 		default:
174e2b1b9c0Schristos 			usage();
175e2b1b9c0Schristos 			exit(1);
176e2b1b9c0Schristos 		}
177e2b1b9c0Schristos 	}
178e2b1b9c0Schristos 
179e2b1b9c0Schristos 	argc -= isc_commandline_index;
180e2b1b9c0Schristos 	argv += isc_commandline_index;
181e2b1b9c0Schristos 
182e2b1b9c0Schristos 	if (argc >= 1) {
183e2b1b9c0Schristos 		f = fopen(argv[0], "r");
184e2b1b9c0Schristos 		if (f == NULL) {
185e2b1b9c0Schristos 			fprintf(stderr, "%s: fopen failed\n", argv[0]);
186e2b1b9c0Schristos 			exit(1);
187e2b1b9c0Schristos 		}
188f2e20987Schristos 		need_close = true;
1899742fdb4Schristos 	} else {
190e2b1b9c0Schristos 		f = stdin;
1919742fdb4Schristos 	}
192e2b1b9c0Schristos 
1939742fdb4Schristos 	isc_buffer_allocate(mctx, &input, 64 * 1024);
194e2b1b9c0Schristos 
195e2b1b9c0Schristos 	if (rawdata) {
196e2b1b9c0Schristos 		while (fread(&c, 1, 1, f) != 0) {
197e2b1b9c0Schristos 			result = isc_buffer_reserve(&input, 1);
198e2b1b9c0Schristos 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
199f2e20987Schristos 			isc_buffer_putuint8(input, (uint8_t)c);
200e2b1b9c0Schristos 		}
201e2b1b9c0Schristos 	} else {
202e2b1b9c0Schristos 		char s[BUFSIZ];
203e2b1b9c0Schristos 
204e2b1b9c0Schristos 		while (fgets(s, sizeof(s), f) != NULL) {
205e2b1b9c0Schristos 			char *rp = s, *wp = s;
206e2b1b9c0Schristos 			size_t i, len = 0;
207e2b1b9c0Schristos 
208e2b1b9c0Schristos 			while (*rp != '\0') {
2099742fdb4Schristos 				if (*rp == '#') {
210e2b1b9c0Schristos 					break;
2119742fdb4Schristos 				}
2129742fdb4Schristos 				if (*rp != ' ' && *rp != '\t' && *rp != '\r' &&
213*4ac1c27eSchristos 				    *rp != '\n')
214*4ac1c27eSchristos 				{
215e2b1b9c0Schristos 					*wp++ = *rp;
216e2b1b9c0Schristos 					len++;
217e2b1b9c0Schristos 				}
218e2b1b9c0Schristos 				rp++;
219e2b1b9c0Schristos 			}
2209742fdb4Schristos 			if (len == 0U) {
221e2b1b9c0Schristos 				continue;
2229742fdb4Schristos 			}
223e2b1b9c0Schristos 			if (len % 2 != 0U) {
224e2b1b9c0Schristos 				fprintf(stderr, "bad input format: %lu\n",
225e2b1b9c0Schristos 					(unsigned long)len);
226e2b1b9c0Schristos 				exit(1);
227e2b1b9c0Schristos 			}
228e2b1b9c0Schristos 
229e2b1b9c0Schristos 			rp = s;
230e2b1b9c0Schristos 			for (i = 0; i < len; i += 2) {
231e2b1b9c0Schristos 				c = fromhex(*rp++);
232e2b1b9c0Schristos 				c *= 16;
233e2b1b9c0Schristos 				c += fromhex(*rp++);
234e2b1b9c0Schristos 				result = isc_buffer_reserve(&input, 1);
235e2b1b9c0Schristos 				RUNTIME_CHECK(result == ISC_R_SUCCESS);
236f2e20987Schristos 				isc_buffer_putuint8(input, (uint8_t)c);
237e2b1b9c0Schristos 			}
238e2b1b9c0Schristos 		}
239e2b1b9c0Schristos 	}
240e2b1b9c0Schristos 
2419742fdb4Schristos 	if (need_close) {
242e2b1b9c0Schristos 		fclose(f);
2439742fdb4Schristos 	}
244e2b1b9c0Schristos 
245e2b1b9c0Schristos 	if (tcp) {
246e2b1b9c0Schristos 		while (isc_buffer_remaininglength(input) != 0) {
247e2b1b9c0Schristos 			unsigned int tcplen;
248e2b1b9c0Schristos 
249e2b1b9c0Schristos 			if (isc_buffer_remaininglength(input) < 2) {
250e2b1b9c0Schristos 				fprintf(stderr, "premature end of packet\n");
251e2b1b9c0Schristos 				exit(1);
252e2b1b9c0Schristos 			}
253e2b1b9c0Schristos 			tcplen = isc_buffer_getuint16(input);
254e2b1b9c0Schristos 
255e2b1b9c0Schristos 			if (isc_buffer_remaininglength(input) < tcplen) {
256e2b1b9c0Schristos 				fprintf(stderr, "premature end of packet\n");
257e2b1b9c0Schristos 				exit(1);
258e2b1b9c0Schristos 			}
259e2b1b9c0Schristos 			process_message(input);
260e2b1b9c0Schristos 		}
2619742fdb4Schristos 	} else {
262e2b1b9c0Schristos 		process_message(input);
2639742fdb4Schristos 	}
264e2b1b9c0Schristos 
265e2b1b9c0Schristos 	isc_buffer_free(&input);
266e2b1b9c0Schristos 
2679742fdb4Schristos 	if (printmemstats) {
268e2b1b9c0Schristos 		isc_mem_stats(mctx, stdout);
2699742fdb4Schristos 	}
270e2b1b9c0Schristos 	isc_mem_destroy(&mctx);
271e2b1b9c0Schristos 
272e2b1b9c0Schristos 	return (0);
273e2b1b9c0Schristos }
274e2b1b9c0Schristos 
275e2b1b9c0Schristos static void
process_message(isc_buffer_t * source)276e2b1b9c0Schristos process_message(isc_buffer_t *source) {
277e2b1b9c0Schristos 	dns_message_t *message;
278e2b1b9c0Schristos 	isc_result_t result;
279e2b1b9c0Schristos 	int i;
280e2b1b9c0Schristos 
281e2b1b9c0Schristos 	message = NULL;
28273584a28Schristos 	dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
283e2b1b9c0Schristos 
284e2b1b9c0Schristos 	result = dns_message_parse(message, source, parseflags);
2859742fdb4Schristos 	if (result == DNS_R_RECOVERABLE) {
286e2b1b9c0Schristos 		result = ISC_R_SUCCESS;
2879742fdb4Schristos 	}
288e2b1b9c0Schristos 	CHECKRESULT(result, "dns_message_parse failed");
289e2b1b9c0Schristos 
290e2b1b9c0Schristos 	result = printmessage(message);
291e2b1b9c0Schristos 	CHECKRESULT(result, "printmessage() failed");
292e2b1b9c0Schristos 
2939742fdb4Schristos 	if (printmemstats) {
294e2b1b9c0Schristos 		isc_mem_stats(mctx, stdout);
2959742fdb4Schristos 	}
296e2b1b9c0Schristos 
297e2b1b9c0Schristos 	if (dorender) {
298e2b1b9c0Schristos 		unsigned char b2[64 * 1024];
299e2b1b9c0Schristos 		isc_buffer_t buffer;
300e2b1b9c0Schristos 		dns_compress_t cctx;
301e2b1b9c0Schristos 
302e2b1b9c0Schristos 		isc_buffer_init(&buffer, b2, sizeof(b2));
303e2b1b9c0Schristos 
304e2b1b9c0Schristos 		/*
305e2b1b9c0Schristos 		 * XXXMLG
306e2b1b9c0Schristos 		 * Changing this here is a hack, and should not be done in
307e2b1b9c0Schristos 		 * reasonable application code, ever.
308e2b1b9c0Schristos 		 */
309e2b1b9c0Schristos 		message->from_to_wire = DNS_MESSAGE_INTENTRENDER;
310e2b1b9c0Schristos 
3119742fdb4Schristos 		for (i = 0; i < DNS_SECTION_MAX; i++) {
312e2b1b9c0Schristos 			message->counts[i] = 0; /* Another hack XXX */
3139742fdb4Schristos 		}
314e2b1b9c0Schristos 
315e2b1b9c0Schristos 		result = dns_compress_init(&cctx, -1, mctx);
316e2b1b9c0Schristos 		CHECKRESULT(result, "dns_compress_init() failed");
317e2b1b9c0Schristos 
318e2b1b9c0Schristos 		result = dns_message_renderbegin(message, &cctx, &buffer);
319e2b1b9c0Schristos 		CHECKRESULT(result, "dns_message_renderbegin() failed");
320e2b1b9c0Schristos 
321e2b1b9c0Schristos 		result = dns_message_rendersection(message,
322e2b1b9c0Schristos 						   DNS_SECTION_QUESTION, 0);
3239742fdb4Schristos 		CHECKRESULT(result, "dns_message_rendersection(QUESTION) "
3249742fdb4Schristos 				    "failed");
325e2b1b9c0Schristos 
3269742fdb4Schristos 		result = dns_message_rendersection(message, DNS_SECTION_ANSWER,
3279742fdb4Schristos 						   0);
3289742fdb4Schristos 		CHECKRESULT(result, "dns_message_rendersection(ANSWER) failed");
329e2b1b9c0Schristos 
330e2b1b9c0Schristos 		result = dns_message_rendersection(message,
331e2b1b9c0Schristos 						   DNS_SECTION_AUTHORITY, 0);
3329742fdb4Schristos 		CHECKRESULT(result, "dns_message_rendersection(AUTHORITY) "
3339742fdb4Schristos 				    "failed");
334e2b1b9c0Schristos 
335e2b1b9c0Schristos 		result = dns_message_rendersection(message,
336e2b1b9c0Schristos 						   DNS_SECTION_ADDITIONAL, 0);
3379742fdb4Schristos 		CHECKRESULT(result, "dns_message_rendersection(ADDITIONAL) "
3389742fdb4Schristos 				    "failed");
339e2b1b9c0Schristos 
340e2b1b9c0Schristos 		dns_message_renderend(message);
341e2b1b9c0Schristos 
342e2b1b9c0Schristos 		dns_compress_invalidate(&cctx);
343e2b1b9c0Schristos 
344e2b1b9c0Schristos 		message->from_to_wire = DNS_MESSAGE_INTENTPARSE;
34573584a28Schristos 		dns_message_detach(&message);
346e2b1b9c0Schristos 
347e2b1b9c0Schristos 		printf("Message rendered.\n");
3489742fdb4Schristos 		if (printmemstats) {
349e2b1b9c0Schristos 			isc_mem_stats(mctx, stdout);
3509742fdb4Schristos 		}
351e2b1b9c0Schristos 
35273584a28Schristos 		dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
353e2b1b9c0Schristos 
354e2b1b9c0Schristos 		result = dns_message_parse(message, &buffer, parseflags);
355e2b1b9c0Schristos 		CHECKRESULT(result, "dns_message_parse failed");
356e2b1b9c0Schristos 
357e2b1b9c0Schristos 		result = printmessage(message);
358e2b1b9c0Schristos 		CHECKRESULT(result, "printmessage() failed");
359e2b1b9c0Schristos 	}
36073584a28Schristos 	dns_message_detach(&message);
361e2b1b9c0Schristos }
362