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