xref: /netbsd-src/external/mpl/bind/dist/fuzz/old.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: old.c,v 1.2 2025/01/26 16:25:21 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 <isc/ascii.h>
17 #include <isc/buffer.h>
18 #include <isc/result.h>
19 #include <isc/types.h>
20 #include <isc/util.h>
21 
22 #include <dns/compress.h>
23 #include <dns/types.h>
24 
25 /*
26  */
27 
28 #include "old.h"
29 
30 /*
31  * code copied from lib/dns/name.c as of commit
32  * 6967973568fe80b03e1729259f8907ce8792be34
33  */
34 
35 typedef enum { fw_start = 0, fw_ordinary, fw_newcurrent } fw_state;
36 
37 #define VALID_NAME(n) ISC_MAGIC_VALID(n, DNS_NAME_MAGIC)
38 
39 #define INIT_OFFSETS(name, var, default_offsets) \
40 	if ((name)->offsets != NULL)             \
41 		var = (name)->offsets;           \
42 	else                                     \
43 		var = (default_offsets);
44 
45 #define MAKE_EMPTY(name)                           \
46 	do {                                       \
47 		name->ndata = NULL;                \
48 		name->length = 0;                  \
49 		name->labels = 0;                  \
50 		name->attributes.absolute = false; \
51 	} while (0)
52 
53 #define BINDABLE(name) (!name->attributes.readonly && !name->attributes.dynamic)
54 
55 isc_result_t
56 old_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx,
57 		  unsigned int options, isc_buffer_t *target) {
58 	unsigned char *cdata, *ndata;
59 	unsigned int cused; /* Bytes of compressed name data used */
60 	unsigned int nused, labels, n, nmax;
61 	unsigned int current, new_current, biggest_pointer;
62 	bool done;
63 	fw_state state = fw_start;
64 	unsigned int c;
65 	unsigned char *offsets;
66 	dns_offsets_t odata;
67 	bool downcase;
68 	bool seen_pointer;
69 
70 	/*
71 	 * Copy the possibly-compressed name at source into target,
72 	 * decompressing it.  Loop prevention is performed by checking
73 	 * the new pointer against biggest_pointer.
74 	 */
75 
76 	REQUIRE(VALID_NAME(name));
77 	REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) ||
78 		(target == NULL && ISC_BUFFER_VALID(name->buffer)));
79 
80 	downcase = ((options & DNS_NAME_DOWNCASE) != 0);
81 
82 	if (target == NULL && name->buffer != NULL) {
83 		target = name->buffer;
84 		isc_buffer_clear(target);
85 	}
86 
87 	REQUIRE(BINDABLE(name));
88 
89 	INIT_OFFSETS(name, offsets, odata);
90 
91 	/*
92 	 * Make 'name' empty in case of failure.
93 	 */
94 	MAKE_EMPTY(name);
95 
96 	/*
97 	 * Initialize things to make the compiler happy; they're not required.
98 	 */
99 	n = 0;
100 	new_current = 0;
101 
102 	/*
103 	 * Set up.
104 	 */
105 	labels = 0;
106 	done = false;
107 
108 	ndata = isc_buffer_used(target);
109 	nused = 0;
110 	seen_pointer = false;
111 
112 	/*
113 	 * Find the maximum number of uncompressed target name
114 	 * bytes we are willing to generate.  This is the smaller
115 	 * of the available target buffer length and the
116 	 * maximum legal domain name length (255).
117 	 */
118 	nmax = isc_buffer_availablelength(target);
119 	if (nmax > DNS_NAME_MAXWIRE) {
120 		nmax = DNS_NAME_MAXWIRE;
121 	}
122 
123 	cdata = isc_buffer_current(source);
124 	cused = 0;
125 
126 	current = source->current;
127 	biggest_pointer = current;
128 
129 	/*
130 	 * Note:  The following code is not optimized for speed, but
131 	 * rather for correctness.  Speed will be addressed in the future.
132 	 */
133 
134 	while (current < source->active && !done) {
135 		c = *cdata++;
136 		current++;
137 		if (!seen_pointer) {
138 			cused++;
139 		}
140 
141 		switch (state) {
142 		case fw_start:
143 			if (c < 64) {
144 				offsets[labels] = nused;
145 				labels++;
146 				if (nused + c + 1 > nmax) {
147 					goto full;
148 				}
149 				nused += c + 1;
150 				*ndata++ = c;
151 				if (c == 0) {
152 					done = true;
153 				}
154 				n = c;
155 				state = fw_ordinary;
156 			} else if (c >= 192) {
157 				/*
158 				 * 14-bit compression pointer
159 				 */
160 				if (!dns_decompress_getpermitted(dctx)) {
161 					return DNS_R_DISALLOWED;
162 				}
163 				new_current = c & 0x3F;
164 				state = fw_newcurrent;
165 			} else {
166 				return DNS_R_BADLABELTYPE;
167 			}
168 			break;
169 		case fw_ordinary:
170 			if (downcase) {
171 				c = isc_ascii_tolower(c);
172 			}
173 			*ndata++ = c;
174 			n--;
175 			if (n == 0) {
176 				state = fw_start;
177 			}
178 			break;
179 		case fw_newcurrent:
180 			new_current *= 256;
181 			new_current += c;
182 			if (new_current >= biggest_pointer) {
183 				return DNS_R_BADPOINTER;
184 			}
185 			biggest_pointer = new_current;
186 			current = new_current;
187 			cdata = (unsigned char *)source->base + current;
188 			seen_pointer = true;
189 			state = fw_start;
190 			break;
191 		default:
192 			FATAL_ERROR("Unknown state %d", state);
193 			/* Does not return. */
194 		}
195 	}
196 
197 	if (!done) {
198 		return ISC_R_UNEXPECTEDEND;
199 	}
200 
201 	name->ndata = (unsigned char *)target->base + target->used;
202 	name->labels = labels;
203 	name->length = nused;
204 	name->attributes.absolute = true;
205 
206 	isc_buffer_forward(source, cused);
207 	isc_buffer_add(target, name->length);
208 
209 	return ISC_R_SUCCESS;
210 
211 full:
212 	if (nmax == DNS_NAME_MAXWIRE) {
213 		/*
214 		 * The name did not fit even though we had a buffer
215 		 * big enough to fit a maximum-length name.
216 		 */
217 		return DNS_R_NAMETOOLONG;
218 	} else {
219 		/*
220 		 * The name might fit if only the caller could give us a
221 		 * big enough buffer.
222 		 */
223 		return ISC_R_NOSPACE;
224 	}
225 }
226