1/*	$NetBSD: dns_rdata_fromwire_text.c,v 1.7 2024/02/21 22:51:58 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 <assert.h>
17#include <stddef.h>
18#include <stdint.h>
19#include <string.h>
20
21#include <isc/buffer.h>
22#include <isc/lex.h>
23#include <isc/mem.h>
24#include <isc/result.h>
25#include <isc/util.h>
26
27#include <dns/callbacks.h>
28#include <dns/compress.h>
29#include <dns/master.h>
30#include <dns/rdata.h>
31#include <dns/rdatatype.h>
32
33#include "fuzz.h"
34
35bool debug = false;
36
37/*
38 * Fuzz input to dns_rdata_fromwire(). Then convert the result
39 * to text, back to wire format, to multiline text, and back to wire
40 * format again, checking for consistency throughout the sequence.
41 */
42
43static isc_mem_t *mctx = NULL;
44static isc_lex_t *lex = NULL;
45
46int
47LLVMFuzzerInitialize(int *argc __attribute__((unused)),
48		     char ***argv __attribute__((unused))) {
49	isc_lexspecials_t specials;
50
51	isc_mem_create(&mctx);
52	CHECK(isc_lex_create(mctx, 64, &lex));
53
54	memset(specials, 0, sizeof(specials));
55	specials[0] = 1;
56	specials['('] = 1;
57	specials[')'] = 1;
58	specials['"'] = 1;
59	isc_lex_setspecials(lex, specials);
60	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
61
62	return (0);
63}
64
65static void
66nullmsg(dns_rdatacallbacks_t *cb, const char *fmt, ...) {
67	va_list args;
68
69	UNUSED(cb);
70
71	if (debug) {
72		va_start(args, fmt);
73		vfprintf(stderr, fmt, args);
74		fprintf(stderr, "\n");
75		va_end(args);
76	}
77}
78
79int
80LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
81	char totext[64 * 1044 * 4];
82	dns_compress_t cctx;
83	dns_decompress_t dctx;
84	dns_rdatatype_t rdtype;
85	dns_rdataclass_t rdclass;
86	dns_rdatatype_t typelist[256] = { 1000 }; /* unknown */
87	dns_rdataclass_t classlist[] = { dns_rdataclass_in, dns_rdataclass_hs,
88					 dns_rdataclass_ch, dns_rdataclass_any,
89					 60 };
90	dns_rdata_t rdata1 = DNS_RDATA_INIT, rdata2 = DNS_RDATA_INIT,
91		    rdata3 = DNS_RDATA_INIT;
92	dns_rdatacallbacks_t callbacks;
93	isc_buffer_t source, target;
94	isc_result_t result;
95	unsigned char fromtext[1024];
96	unsigned char fromwire[1024];
97	unsigned char towire[1024];
98	unsigned int classes = (sizeof(classlist) / sizeof(classlist[0]));
99	unsigned int types = 1, flags, t;
100
101	/*
102	 * First 2 bytes are used to select type and class.
103	 * dns_rdata_fromwire() only accepts input up to 2^16-1 octets.
104	 */
105	if (size < 2 || size > 0xffff + 2) {
106		return (0);
107	}
108
109	/*
110	 * Append known types to list.
111	 */
112	for (t = 1; t <= 0x10000; t++) {
113		char typebuf[256];
114		if (dns_rdatatype_ismeta(t)) {
115			continue;
116		}
117		dns_rdatatype_format(t, typebuf, sizeof(typebuf));
118		if (strncmp(typebuf, "TYPE", 4) != 0) {
119			/* Assert when we need to grow typelist. */
120			assert(types < sizeof(typelist) / sizeof(typelist[0]));
121			typelist[types++] = t;
122		}
123	}
124
125	/*
126	 * Random type and class from a limited set.
127	 */
128	rdtype = typelist[(*data++) % types];
129	size--;
130	rdclass = classlist[(*data++) % classes];
131	size--;
132
133	if (debug) {
134		fprintf(stderr, "type=%u, class=%u\n", rdtype, rdclass);
135	}
136
137	dns_rdatacallbacks_init(&callbacks);
138	callbacks.warn = callbacks.error = nullmsg;
139
140	/* Disallow decompression as we are reading a packet */
141	dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE);
142
143	isc_buffer_constinit(&source, data, size);
144	isc_buffer_add(&source, size);
145	isc_buffer_setactive(&source, size);
146
147	isc_buffer_init(&target, fromwire, sizeof(fromwire));
148
149	/*
150	 * Reject invalid rdata.
151	 */
152	CHECK(dns_rdata_fromwire(&rdata1, rdclass, rdtype, &source, &dctx, 0,
153				 &target));
154	assert(rdata1.length == size);
155
156	/*
157	 * Convert to text from wire.
158	 */
159	isc_buffer_init(&target, totext, sizeof(totext) - 1);
160	result = dns_rdata_totext(&rdata1, NULL, &target);
161	assert(result == ISC_R_SUCCESS);
162
163	/*
164	 * Make debugging easier by NUL terminating.
165	 */
166	totext[isc_buffer_usedlength(&target)] = 0;
167
168	/*
169	 * Convert to wire from text.
170	 */
171	isc_buffer_constinit(&source, totext, isc_buffer_usedlength(&target));
172	isc_buffer_add(&source, isc_buffer_usedlength(&target));
173	CHECK(isc_lex_openbuffer(lex, &source));
174
175	isc_buffer_init(&target, fromtext, sizeof(fromtext));
176	result = dns_rdata_fromtext(&rdata2, rdclass, rdtype, lex, dns_rootname,
177				    0, mctx, &target, &callbacks);
178	if (debug && result != ISC_R_SUCCESS) {
179		fprintf(stderr, "'%s'\n", totext);
180	}
181	assert(result == ISC_R_SUCCESS);
182	assert(rdata2.length == size);
183	assert(!memcmp(rdata2.data, data, size));
184
185	/*
186	 * Convert to multi-line text from wire.
187	 */
188	isc_buffer_init(&target, totext, sizeof(totext));
189	flags = dns_master_styleflags(&dns_master_style_default);
190	result = dns_rdata_tofmttext(&rdata1, dns_rootname, flags, 80 - 32, 4,
191				     "\n", &target);
192	assert(result == ISC_R_SUCCESS);
193
194	/*
195	 * Convert to wire from text.
196	 */
197	isc_buffer_constinit(&source, totext, isc_buffer_usedlength(&target));
198	isc_buffer_add(&source, isc_buffer_usedlength(&target));
199	CHECK(isc_lex_openbuffer(lex, &source));
200
201	isc_buffer_init(&target, fromtext, sizeof(fromtext));
202	result = dns_rdata_fromtext(&rdata3, rdclass, rdtype, lex, dns_rootname,
203				    0, mctx, &target, &callbacks);
204	assert(result == ISC_R_SUCCESS);
205	assert(rdata3.length == size);
206	assert(!memcmp(rdata3.data, data, size));
207
208	/*
209	 * Convert rdata back to wire.
210	 */
211	CHECK(dns_compress_init(&cctx, -1, mctx));
212	dns_compress_disable(&cctx);
213	isc_buffer_init(&target, towire, sizeof(towire));
214	result = dns_rdata_towire(&rdata1, &cctx, &target);
215	dns_compress_invalidate(&cctx);
216	assert(result == ISC_R_SUCCESS);
217	assert(target.used == size);
218	assert(!memcmp(target.base, data, size));
219
220	return (0);
221}
222