1 /*	$NetBSD: opt_41.c,v 1.10 2022/09/23 12:15:31 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 /* RFC2671 */
17 
18 #ifndef RDATA_GENERIC_OPT_41_C
19 #define RDATA_GENERIC_OPT_41_C
20 
21 #define RRTYPE_OPT_ATTRIBUTES                                   \
22 	(DNS_RDATATYPEATTR_SINGLETON | DNS_RDATATYPEATTR_META | \
23 	 DNS_RDATATYPEATTR_NOTQUESTION)
24 
25 #include <isc/utf8.h>
26 
27 static isc_result_t
fromtext_opt(ARGS_FROMTEXT)28 fromtext_opt(ARGS_FROMTEXT) {
29 	/*
30 	 * OPT records do not have a text format.
31 	 */
32 
33 	REQUIRE(type == dns_rdatatype_opt);
34 
35 	UNUSED(type);
36 	UNUSED(rdclass);
37 	UNUSED(lexer);
38 	UNUSED(origin);
39 	UNUSED(options);
40 	UNUSED(target);
41 	UNUSED(callbacks);
42 
43 	return (ISC_R_NOTIMPLEMENTED);
44 }
45 
46 static isc_result_t
totext_opt(ARGS_TOTEXT)47 totext_opt(ARGS_TOTEXT) {
48 	isc_region_t r;
49 	isc_region_t or ;
50 	uint16_t option;
51 	uint16_t length;
52 	char buf[sizeof("64000 64000")];
53 
54 	/*
55 	 * OPT records do not have a text format.
56 	 */
57 
58 	REQUIRE(rdata->type == dns_rdatatype_opt);
59 
60 	dns_rdata_toregion(rdata, &r);
61 	while (r.length > 0) {
62 		option = uint16_fromregion(&r);
63 		isc_region_consume(&r, 2);
64 		length = uint16_fromregion(&r);
65 		isc_region_consume(&r, 2);
66 		snprintf(buf, sizeof(buf), "%u %u", option, length);
67 		RETERR(str_totext(buf, target));
68 		INSIST(r.length >= length);
69 		if (length > 0) {
70 			if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
71 				RETERR(str_totext(" (", target));
72 			}
73 			RETERR(str_totext(tctx->linebreak, target));
74 			or = r;
75 			or.length = length;
76 			if (tctx->width == 0) { /* No splitting */
77 				RETERR(isc_base64_totext(& or, 60, "", target));
78 			} else {
79 				RETERR(isc_base64_totext(& or, tctx->width - 2,
80 							 tctx->linebreak,
81 							 target));
82 			}
83 			isc_region_consume(&r, length);
84 			if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
85 				RETERR(str_totext(" )", target));
86 			}
87 		}
88 		if (r.length > 0) {
89 			RETERR(str_totext(" ", target));
90 		}
91 	}
92 
93 	return (ISC_R_SUCCESS);
94 }
95 
96 static isc_result_t
fromwire_opt(ARGS_FROMWIRE)97 fromwire_opt(ARGS_FROMWIRE) {
98 	isc_region_t sregion;
99 	isc_region_t tregion;
100 	uint16_t opt;
101 	uint16_t length;
102 	unsigned int total;
103 
104 	REQUIRE(type == dns_rdatatype_opt);
105 
106 	UNUSED(type);
107 	UNUSED(rdclass);
108 	UNUSED(dctx);
109 	UNUSED(options);
110 
111 	isc_buffer_activeregion(source, &sregion);
112 	if (sregion.length == 0) {
113 		return (ISC_R_SUCCESS);
114 	}
115 	total = 0;
116 	while (sregion.length != 0) {
117 		if (sregion.length < 4) {
118 			return (ISC_R_UNEXPECTEDEND);
119 		}
120 		opt = uint16_fromregion(&sregion);
121 		isc_region_consume(&sregion, 2);
122 		length = uint16_fromregion(&sregion);
123 		isc_region_consume(&sregion, 2);
124 		total += 4;
125 		if (sregion.length < length) {
126 			return (ISC_R_UNEXPECTEDEND);
127 		}
128 		switch (opt) {
129 		case DNS_OPT_LLQ:
130 			if (length != 18U) {
131 				return (DNS_R_OPTERR);
132 			}
133 			isc_region_consume(&sregion, length);
134 			break;
135 		case DNS_OPT_CLIENT_SUBNET: {
136 			uint16_t family;
137 			uint8_t addrlen;
138 			uint8_t scope;
139 			uint8_t addrbytes;
140 
141 			if (length < 4) {
142 				return (DNS_R_OPTERR);
143 			}
144 			family = uint16_fromregion(&sregion);
145 			isc_region_consume(&sregion, 2);
146 			addrlen = uint8_fromregion(&sregion);
147 			isc_region_consume(&sregion, 1);
148 			scope = uint8_fromregion(&sregion);
149 			isc_region_consume(&sregion, 1);
150 
151 			switch (family) {
152 			case 0:
153 				/*
154 				 * XXXMUKS: In queries and replies, if
155 				 * FAMILY is set to 0, SOURCE
156 				 * PREFIX-LENGTH and SCOPE PREFIX-LENGTH
157 				 * must be 0 and ADDRESS should not be
158 				 * present as the address and prefix
159 				 * lengths don't make sense because the
160 				 * family is unknown.
161 				 */
162 				if (addrlen != 0U || scope != 0U) {
163 					return (DNS_R_OPTERR);
164 				}
165 				break;
166 			case 1:
167 				if (addrlen > 32U || scope > 32U) {
168 					return (DNS_R_OPTERR);
169 				}
170 				break;
171 			case 2:
172 				if (addrlen > 128U || scope > 128U) {
173 					return (DNS_R_OPTERR);
174 				}
175 				break;
176 			default:
177 				return (DNS_R_OPTERR);
178 			}
179 			addrbytes = (addrlen + 7) / 8;
180 			if (addrbytes + 4 != length) {
181 				return (DNS_R_OPTERR);
182 			}
183 
184 			if (addrbytes != 0U && (addrlen % 8) != 0) {
185 				uint8_t bits = ~0U << (8 - (addrlen % 8));
186 				bits &= sregion.base[addrbytes - 1];
187 				if (bits != sregion.base[addrbytes - 1]) {
188 					return (DNS_R_OPTERR);
189 				}
190 			}
191 			isc_region_consume(&sregion, addrbytes);
192 			break;
193 		}
194 		case DNS_OPT_EXPIRE:
195 			/*
196 			 * Request has zero length.  Response is 32 bits.
197 			 */
198 			if (length != 0 && length != 4) {
199 				return (DNS_R_OPTERR);
200 			}
201 			isc_region_consume(&sregion, length);
202 			break;
203 		case DNS_OPT_COOKIE:
204 			/*
205 			 * Client cookie alone has length 8.
206 			 * Client + server cookie is 8 + [8..32].
207 			 */
208 			if (length != 8 && (length < 16 || length > 40)) {
209 				return (DNS_R_OPTERR);
210 			}
211 			isc_region_consume(&sregion, length);
212 			break;
213 		case DNS_OPT_KEY_TAG:
214 			if (length == 0 || (length % 2) != 0) {
215 				return (DNS_R_OPTERR);
216 			}
217 			isc_region_consume(&sregion, length);
218 			break;
219 		case DNS_OPT_EDE:
220 			if (length < 2) {
221 				return (DNS_R_OPTERR);
222 			}
223 			/* UTF-8 Byte Order Mark is not permitted. RFC 5198 */
224 			if (isc_utf8_bom(sregion.base + 2, length - 2)) {
225 				return (DNS_R_OPTERR);
226 			}
227 			/*
228 			 * The EXTRA-TEXT field is specified as UTF-8, and
229 			 * therefore must be validated for correctness
230 			 * according to RFC 3269 security considerations.
231 			 */
232 			if (!isc_utf8_valid(sregion.base + 2, length - 2)) {
233 				return (DNS_R_OPTERR);
234 			}
235 			isc_region_consume(&sregion, length);
236 			break;
237 		case DNS_OPT_CLIENT_TAG:
238 			FALLTHROUGH;
239 		case DNS_OPT_SERVER_TAG:
240 			if (length != 2) {
241 				return (DNS_R_OPTERR);
242 			}
243 			isc_region_consume(&sregion, length);
244 			break;
245 		default:
246 			isc_region_consume(&sregion, length);
247 			break;
248 		}
249 		total += length;
250 	}
251 
252 	isc_buffer_activeregion(source, &sregion);
253 	isc_buffer_availableregion(target, &tregion);
254 	if (tregion.length < total) {
255 		return (ISC_R_NOSPACE);
256 	}
257 	memmove(tregion.base, sregion.base, total);
258 	isc_buffer_forward(source, total);
259 	isc_buffer_add(target, total);
260 
261 	return (ISC_R_SUCCESS);
262 }
263 
264 static isc_result_t
towire_opt(ARGS_TOWIRE)265 towire_opt(ARGS_TOWIRE) {
266 	REQUIRE(rdata->type == dns_rdatatype_opt);
267 
268 	UNUSED(cctx);
269 
270 	return (mem_tobuffer(target, rdata->data, rdata->length));
271 }
272 
273 static int
compare_opt(ARGS_COMPARE)274 compare_opt(ARGS_COMPARE) {
275 	isc_region_t r1;
276 	isc_region_t r2;
277 
278 	REQUIRE(rdata1->type == rdata2->type);
279 	REQUIRE(rdata1->rdclass == rdata2->rdclass);
280 	REQUIRE(rdata1->type == dns_rdatatype_opt);
281 
282 	dns_rdata_toregion(rdata1, &r1);
283 	dns_rdata_toregion(rdata2, &r2);
284 	return (isc_region_compare(&r1, &r2));
285 }
286 
287 static isc_result_t
fromstruct_opt(ARGS_FROMSTRUCT)288 fromstruct_opt(ARGS_FROMSTRUCT) {
289 	dns_rdata_opt_t *opt = source;
290 	isc_region_t region;
291 	uint16_t length;
292 
293 	REQUIRE(type == dns_rdatatype_opt);
294 	REQUIRE(opt != NULL);
295 	REQUIRE(opt->common.rdtype == type);
296 	REQUIRE(opt->common.rdclass == rdclass);
297 	REQUIRE(opt->options != NULL || opt->length == 0);
298 
299 	UNUSED(type);
300 	UNUSED(rdclass);
301 
302 	region.base = opt->options;
303 	region.length = opt->length;
304 	while (region.length >= 4) {
305 		isc_region_consume(&region, 2); /* opt */
306 		length = uint16_fromregion(&region);
307 		isc_region_consume(&region, 2);
308 		if (region.length < length) {
309 			return (ISC_R_UNEXPECTEDEND);
310 		}
311 		isc_region_consume(&region, length);
312 	}
313 	if (region.length != 0) {
314 		return (ISC_R_UNEXPECTEDEND);
315 	}
316 
317 	return (mem_tobuffer(target, opt->options, opt->length));
318 }
319 
320 static isc_result_t
tostruct_opt(ARGS_TOSTRUCT)321 tostruct_opt(ARGS_TOSTRUCT) {
322 	dns_rdata_opt_t *opt = target;
323 	isc_region_t r;
324 
325 	REQUIRE(rdata->type == dns_rdatatype_opt);
326 	REQUIRE(opt != NULL);
327 
328 	opt->common.rdclass = rdata->rdclass;
329 	opt->common.rdtype = rdata->type;
330 	ISC_LINK_INIT(&opt->common, link);
331 
332 	dns_rdata_toregion(rdata, &r);
333 	opt->length = r.length;
334 	opt->options = mem_maybedup(mctx, r.base, r.length);
335 	if (opt->options == NULL) {
336 		return (ISC_R_NOMEMORY);
337 	}
338 
339 	opt->offset = 0;
340 	opt->mctx = mctx;
341 	return (ISC_R_SUCCESS);
342 }
343 
344 static void
freestruct_opt(ARGS_FREESTRUCT)345 freestruct_opt(ARGS_FREESTRUCT) {
346 	dns_rdata_opt_t *opt = source;
347 
348 	REQUIRE(opt != NULL);
349 	REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
350 
351 	if (opt->mctx == NULL) {
352 		return;
353 	}
354 
355 	if (opt->options != NULL) {
356 		isc_mem_free(opt->mctx, opt->options);
357 	}
358 	opt->mctx = NULL;
359 }
360 
361 static isc_result_t
additionaldata_opt(ARGS_ADDLDATA)362 additionaldata_opt(ARGS_ADDLDATA) {
363 	REQUIRE(rdata->type == dns_rdatatype_opt);
364 
365 	UNUSED(rdata);
366 	UNUSED(add);
367 	UNUSED(arg);
368 
369 	return (ISC_R_SUCCESS);
370 }
371 
372 static isc_result_t
digest_opt(ARGS_DIGEST)373 digest_opt(ARGS_DIGEST) {
374 	/*
375 	 * OPT records are not digested.
376 	 */
377 
378 	REQUIRE(rdata->type == dns_rdatatype_opt);
379 
380 	UNUSED(rdata);
381 	UNUSED(digest);
382 	UNUSED(arg);
383 
384 	return (ISC_R_NOTIMPLEMENTED);
385 }
386 
387 static bool
checkowner_opt(ARGS_CHECKOWNER)388 checkowner_opt(ARGS_CHECKOWNER) {
389 	REQUIRE(type == dns_rdatatype_opt);
390 
391 	UNUSED(type);
392 	UNUSED(rdclass);
393 	UNUSED(wildcard);
394 
395 	return (dns_name_equal(name, dns_rootname));
396 }
397 
398 static bool
checknames_opt(ARGS_CHECKNAMES)399 checknames_opt(ARGS_CHECKNAMES) {
400 	REQUIRE(rdata->type == dns_rdatatype_opt);
401 
402 	UNUSED(rdata);
403 	UNUSED(owner);
404 	UNUSED(bad);
405 
406 	return (true);
407 }
408 
409 static int
casecompare_opt(ARGS_COMPARE)410 casecompare_opt(ARGS_COMPARE) {
411 	return (compare_opt(rdata1, rdata2));
412 }
413 
414 isc_result_t
dns_rdata_opt_first(dns_rdata_opt_t * opt)415 dns_rdata_opt_first(dns_rdata_opt_t *opt) {
416 	REQUIRE(opt != NULL);
417 	REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
418 	REQUIRE(opt->options != NULL || opt->length == 0);
419 
420 	if (opt->length == 0) {
421 		return (ISC_R_NOMORE);
422 	}
423 
424 	opt->offset = 0;
425 	return (ISC_R_SUCCESS);
426 }
427 
428 isc_result_t
dns_rdata_opt_next(dns_rdata_opt_t * opt)429 dns_rdata_opt_next(dns_rdata_opt_t *opt) {
430 	isc_region_t r;
431 	uint16_t length;
432 
433 	REQUIRE(opt != NULL);
434 	REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
435 	REQUIRE(opt->options != NULL && opt->length != 0);
436 	REQUIRE(opt->offset < opt->length);
437 
438 	INSIST(opt->offset + 4 <= opt->length);
439 	r.base = opt->options + opt->offset + 2;
440 	r.length = opt->length - opt->offset - 2;
441 	length = uint16_fromregion(&r);
442 	INSIST(opt->offset + 4 + length <= opt->length);
443 	opt->offset = opt->offset + 4 + length;
444 	if (opt->offset == opt->length) {
445 		return (ISC_R_NOMORE);
446 	}
447 	return (ISC_R_SUCCESS);
448 }
449 
450 isc_result_t
dns_rdata_opt_current(dns_rdata_opt_t * opt,dns_rdata_opt_opcode_t * opcode)451 dns_rdata_opt_current(dns_rdata_opt_t *opt, dns_rdata_opt_opcode_t *opcode) {
452 	isc_region_t r;
453 
454 	REQUIRE(opt != NULL);
455 	REQUIRE(opcode != NULL);
456 	REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
457 	REQUIRE(opt->options != NULL);
458 	REQUIRE(opt->offset < opt->length);
459 
460 	INSIST(opt->offset + 4 <= opt->length);
461 	r.base = opt->options + opt->offset;
462 	r.length = opt->length - opt->offset;
463 
464 	opcode->opcode = uint16_fromregion(&r);
465 	isc_region_consume(&r, 2);
466 	opcode->length = uint16_fromregion(&r);
467 	isc_region_consume(&r, 2);
468 	opcode->data = r.base;
469 	INSIST(opt->offset + 4 + opcode->length <= opt->length);
470 
471 	return (ISC_R_SUCCESS);
472 }
473 
474 #endif /* RDATA_GENERIC_OPT_41_C */
475