mirror of
https://github.com/isc-projects/bind9.git
synced 2026-02-25 19:04:57 -05:00
wire_test is not only used by the dnstap system test, but also in fuzz testing. it doesn't need to be installed, but it's useful to have it built when BIND is. this commit moves it back from bin/tests/system to bin/tests, as a noinst_PROGRAM so that it's built by "make all" but not installed.
362 lines
8.1 KiB
C
362 lines
8.1 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <isc/buffer.h>
|
|
#include <isc/commandline.h>
|
|
#include <isc/file.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/print.h>
|
|
#include <isc/string.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/message.h>
|
|
#include <dns/result.h>
|
|
|
|
int parseflags = 0;
|
|
isc_mem_t *mctx = NULL;
|
|
bool printmemstats = false;
|
|
bool dorender = false;
|
|
|
|
static void
|
|
process_message(isc_buffer_t *source);
|
|
|
|
static isc_result_t
|
|
printmessage(dns_message_t *msg);
|
|
|
|
static inline void
|
|
CHECKRESULT(isc_result_t result, const char *msg) {
|
|
if (result != ISC_R_SUCCESS) {
|
|
printf("%s: %s\n", msg, dns_result_totext(result));
|
|
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static int
|
|
fromhex(char c) {
|
|
if (c >= '0' && c <= '9') {
|
|
return (c - '0');
|
|
} else if (c >= 'a' && c <= 'f') {
|
|
return (c - 'a' + 10);
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
return (c - 'A' + 10);
|
|
}
|
|
|
|
fprintf(stderr, "bad input format: %02x\n", c);
|
|
exit(3);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
static void
|
|
usage(void) {
|
|
fprintf(stderr, "wire_test [-b] [-d] [-p] [-r] [-s]\n");
|
|
fprintf(stderr, " [-m {usage|trace|record|size|mctx}]\n");
|
|
fprintf(stderr, " [filename]\n\n");
|
|
fprintf(stderr, "\t-b\tBest-effort parsing (ignore some errors)\n");
|
|
fprintf(stderr, "\t-d\tRead input as raw binary data\n");
|
|
fprintf(stderr, "\t-p\tPreserve order of the records in messages\n");
|
|
fprintf(stderr, "\t-r\tAfter parsing, re-render the message\n");
|
|
fprintf(stderr, "\t-s\tPrint memory statistics\n");
|
|
fprintf(stderr, "\t-t\tTCP mode - ignore the first 2 bytes\n");
|
|
}
|
|
|
|
static isc_result_t
|
|
printmessage(dns_message_t *msg) {
|
|
isc_buffer_t b;
|
|
char *buf = NULL;
|
|
int len = 1024;
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
do {
|
|
buf = isc_mem_get(mctx, len);
|
|
|
|
isc_buffer_init(&b, buf, len);
|
|
result = dns_message_totext(msg, &dns_master_style_debug, 0,
|
|
&b);
|
|
if (result == ISC_R_NOSPACE) {
|
|
isc_mem_put(mctx, buf, len);
|
|
len *= 2;
|
|
} else if (result == ISC_R_SUCCESS) {
|
|
printf("%.*s\n", (int)isc_buffer_usedlength(&b), buf);
|
|
}
|
|
} while (result == ISC_R_NOSPACE);
|
|
|
|
if (buf != NULL) {
|
|
isc_mem_put(mctx, buf, len);
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[]) {
|
|
isc_buffer_t *input = NULL;
|
|
bool need_close = false;
|
|
bool tcp = false;
|
|
bool rawdata = false;
|
|
isc_result_t result;
|
|
uint8_t c;
|
|
FILE *f;
|
|
int ch;
|
|
|
|
#define CMDLINE_FLAGS "bdm:prst"
|
|
/*
|
|
* Process memory debugging argument first.
|
|
*/
|
|
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
|
|
switch (ch) {
|
|
case 'm':
|
|
if (strcasecmp(isc_commandline_argument, "record") == 0)
|
|
{
|
|
isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
|
|
}
|
|
if (strcasecmp(isc_commandline_argument, "trace") == 0)
|
|
{
|
|
isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
|
|
}
|
|
if (strcasecmp(isc_commandline_argument, "usage") == 0)
|
|
{
|
|
isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
|
|
}
|
|
if (strcasecmp(isc_commandline_argument, "size") == 0) {
|
|
isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
|
|
}
|
|
if (strcasecmp(isc_commandline_argument, "mctx") == 0) {
|
|
isc_mem_debugging |= ISC_MEM_DEBUGCTX;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
isc_commandline_reset = true;
|
|
|
|
isc_mem_create(&mctx);
|
|
|
|
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
|
|
switch (ch) {
|
|
case 'b':
|
|
parseflags |= DNS_MESSAGEPARSE_BESTEFFORT;
|
|
break;
|
|
case 'd':
|
|
rawdata = true;
|
|
break;
|
|
case 'm':
|
|
break;
|
|
case 'p':
|
|
parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER;
|
|
break;
|
|
case 'r':
|
|
dorender = true;
|
|
break;
|
|
case 's':
|
|
printmemstats = true;
|
|
break;
|
|
case 't':
|
|
tcp = true;
|
|
break;
|
|
default:
|
|
usage();
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
argc -= isc_commandline_index;
|
|
argv += isc_commandline_index;
|
|
|
|
if (argc >= 1) {
|
|
f = fopen(argv[0], "r");
|
|
if (f == NULL) {
|
|
fprintf(stderr, "%s: fopen failed\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
need_close = true;
|
|
} else {
|
|
f = stdin;
|
|
}
|
|
|
|
isc_buffer_allocate(mctx, &input, 64 * 1024);
|
|
|
|
if (rawdata) {
|
|
while (fread(&c, 1, 1, f) != 0) {
|
|
result = isc_buffer_reserve(&input, 1);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
isc_buffer_putuint8(input, (uint8_t)c);
|
|
}
|
|
} else {
|
|
char s[BUFSIZ];
|
|
|
|
while (fgets(s, sizeof(s), f) != NULL) {
|
|
char *rp = s, *wp = s;
|
|
size_t i, len = 0;
|
|
|
|
while (*rp != '\0') {
|
|
if (*rp == '#') {
|
|
break;
|
|
}
|
|
if (*rp != ' ' && *rp != '\t' && *rp != '\r' &&
|
|
*rp != '\n') {
|
|
*wp++ = *rp;
|
|
len++;
|
|
}
|
|
rp++;
|
|
}
|
|
if (len == 0U) {
|
|
continue;
|
|
}
|
|
if (len % 2 != 0U) {
|
|
fprintf(stderr, "bad input format: %lu\n",
|
|
(unsigned long)len);
|
|
exit(1);
|
|
}
|
|
|
|
rp = s;
|
|
for (i = 0; i < len; i += 2) {
|
|
c = fromhex(*rp++);
|
|
c *= 16;
|
|
c += fromhex(*rp++);
|
|
result = isc_buffer_reserve(&input, 1);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
isc_buffer_putuint8(input, (uint8_t)c);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (need_close) {
|
|
fclose(f);
|
|
}
|
|
|
|
if (tcp) {
|
|
while (isc_buffer_remaininglength(input) != 0) {
|
|
unsigned int tcplen;
|
|
|
|
if (isc_buffer_remaininglength(input) < 2) {
|
|
fprintf(stderr, "premature end of packet\n");
|
|
exit(1);
|
|
}
|
|
tcplen = isc_buffer_getuint16(input);
|
|
|
|
if (isc_buffer_remaininglength(input) < tcplen) {
|
|
fprintf(stderr, "premature end of packet\n");
|
|
exit(1);
|
|
}
|
|
process_message(input);
|
|
}
|
|
} else {
|
|
process_message(input);
|
|
}
|
|
|
|
if (input != NULL) {
|
|
isc_buffer_free(&input);
|
|
}
|
|
|
|
if (printmemstats) {
|
|
isc_mem_stats(mctx, stdout);
|
|
}
|
|
isc_mem_destroy(&mctx);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
process_message(isc_buffer_t *source) {
|
|
dns_message_t *message;
|
|
isc_result_t result;
|
|
int i;
|
|
|
|
message = NULL;
|
|
result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
|
|
CHECKRESULT(result, "dns_message_create failed");
|
|
|
|
result = dns_message_parse(message, source, parseflags);
|
|
if (result == DNS_R_RECOVERABLE) {
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
CHECKRESULT(result, "dns_message_parse failed");
|
|
|
|
result = printmessage(message);
|
|
CHECKRESULT(result, "printmessage() failed");
|
|
|
|
if (printmemstats) {
|
|
isc_mem_stats(mctx, stdout);
|
|
}
|
|
|
|
if (dorender) {
|
|
unsigned char b2[64 * 1024];
|
|
isc_buffer_t buffer;
|
|
dns_compress_t cctx;
|
|
|
|
isc_buffer_init(&buffer, b2, sizeof(b2));
|
|
|
|
/*
|
|
* XXXMLG
|
|
* Changing this here is a hack, and should not be done in
|
|
* reasonable application code, ever.
|
|
*/
|
|
message->from_to_wire = DNS_MESSAGE_INTENTRENDER;
|
|
|
|
for (i = 0; i < DNS_SECTION_MAX; i++) {
|
|
message->counts[i] = 0; /* Another hack XXX */
|
|
}
|
|
|
|
result = dns_compress_init(&cctx, -1, mctx);
|
|
CHECKRESULT(result, "dns_compress_init() failed");
|
|
|
|
result = dns_message_renderbegin(message, &cctx, &buffer);
|
|
CHECKRESULT(result, "dns_message_renderbegin() failed");
|
|
|
|
result = dns_message_rendersection(message,
|
|
DNS_SECTION_QUESTION, 0);
|
|
CHECKRESULT(result, "dns_message_rendersection(QUESTION) "
|
|
"failed");
|
|
|
|
result = dns_message_rendersection(message, DNS_SECTION_ANSWER,
|
|
0);
|
|
CHECKRESULT(result, "dns_message_rendersection(ANSWER) failed");
|
|
|
|
result = dns_message_rendersection(message,
|
|
DNS_SECTION_AUTHORITY, 0);
|
|
CHECKRESULT(result, "dns_message_rendersection(AUTHORITY) "
|
|
"failed");
|
|
|
|
result = dns_message_rendersection(message,
|
|
DNS_SECTION_ADDITIONAL, 0);
|
|
CHECKRESULT(result, "dns_message_rendersection(ADDITIONAL) "
|
|
"failed");
|
|
|
|
dns_message_renderend(message);
|
|
|
|
dns_compress_invalidate(&cctx);
|
|
|
|
message->from_to_wire = DNS_MESSAGE_INTENTPARSE;
|
|
dns_message_destroy(&message);
|
|
|
|
printf("Message rendered.\n");
|
|
if (printmemstats) {
|
|
isc_mem_stats(mctx, stdout);
|
|
}
|
|
|
|
result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
|
|
&message);
|
|
CHECKRESULT(result, "dns_message_create failed");
|
|
|
|
result = dns_message_parse(message, &buffer, parseflags);
|
|
CHECKRESULT(result, "dns_message_parse failed");
|
|
|
|
result = printmessage(message);
|
|
CHECKRESULT(result, "printmessage() failed");
|
|
}
|
|
dns_message_destroy(&message);
|
|
}
|