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(®ion, 2); /* opt */
306 length = uint16_fromregion(®ion);
307 isc_region_consume(®ion, 2);
308 if (region.length < length) {
309 return (ISC_R_UNEXPECTEDEND);
310 }
311 isc_region_consume(®ion, 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