1/* $NetBSD: opt_41.c,v 1.11 2024/02/21 22:52:13 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 27static isc_result_t 28fromtext_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 46static isc_result_t 47totext_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 96static isc_result_t 97fromwire_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 264static isc_result_t 265towire_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 273static int 274compare_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 287static isc_result_t 288fromstruct_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 320static isc_result_t 321tostruct_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 344static void 345freestruct_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 361static isc_result_t 362additionaldata_opt(ARGS_ADDLDATA) { 363 REQUIRE(rdata->type == dns_rdatatype_opt); 364 365 UNUSED(rdata); 366 UNUSED(owner); 367 UNUSED(add); 368 UNUSED(arg); 369 370 return (ISC_R_SUCCESS); 371} 372 373static isc_result_t 374digest_opt(ARGS_DIGEST) { 375 /* 376 * OPT records are not digested. 377 */ 378 379 REQUIRE(rdata->type == dns_rdatatype_opt); 380 381 UNUSED(rdata); 382 UNUSED(digest); 383 UNUSED(arg); 384 385 return (ISC_R_NOTIMPLEMENTED); 386} 387 388static bool 389checkowner_opt(ARGS_CHECKOWNER) { 390 REQUIRE(type == dns_rdatatype_opt); 391 392 UNUSED(type); 393 UNUSED(rdclass); 394 UNUSED(wildcard); 395 396 return (dns_name_equal(name, dns_rootname)); 397} 398 399static bool 400checknames_opt(ARGS_CHECKNAMES) { 401 REQUIRE(rdata->type == dns_rdatatype_opt); 402 403 UNUSED(rdata); 404 UNUSED(owner); 405 UNUSED(bad); 406 407 return (true); 408} 409 410static int 411casecompare_opt(ARGS_COMPARE) { 412 return (compare_opt(rdata1, rdata2)); 413} 414 415isc_result_t 416dns_rdata_opt_first(dns_rdata_opt_t *opt) { 417 REQUIRE(opt != NULL); 418 REQUIRE(opt->common.rdtype == dns_rdatatype_opt); 419 REQUIRE(opt->options != NULL || opt->length == 0); 420 421 if (opt->length == 0) { 422 return (ISC_R_NOMORE); 423 } 424 425 opt->offset = 0; 426 return (ISC_R_SUCCESS); 427} 428 429isc_result_t 430dns_rdata_opt_next(dns_rdata_opt_t *opt) { 431 isc_region_t r; 432 uint16_t length; 433 434 REQUIRE(opt != NULL); 435 REQUIRE(opt->common.rdtype == dns_rdatatype_opt); 436 REQUIRE(opt->options != NULL && opt->length != 0); 437 REQUIRE(opt->offset < opt->length); 438 439 INSIST(opt->offset + 4 <= opt->length); 440 r.base = opt->options + opt->offset + 2; 441 r.length = opt->length - opt->offset - 2; 442 length = uint16_fromregion(&r); 443 INSIST(opt->offset + 4 + length <= opt->length); 444 opt->offset = opt->offset + 4 + length; 445 if (opt->offset == opt->length) { 446 return (ISC_R_NOMORE); 447 } 448 return (ISC_R_SUCCESS); 449} 450 451isc_result_t 452dns_rdata_opt_current(dns_rdata_opt_t *opt, dns_rdata_opt_opcode_t *opcode) { 453 isc_region_t r; 454 455 REQUIRE(opt != NULL); 456 REQUIRE(opcode != NULL); 457 REQUIRE(opt->common.rdtype == dns_rdatatype_opt); 458 REQUIRE(opt->options != NULL); 459 REQUIRE(opt->offset < opt->length); 460 461 INSIST(opt->offset + 4 <= opt->length); 462 r.base = opt->options + opt->offset; 463 r.length = opt->length - opt->offset; 464 465 opcode->opcode = uint16_fromregion(&r); 466 isc_region_consume(&r, 2); 467 opcode->length = uint16_fromregion(&r); 468 isc_region_consume(&r, 2); 469 opcode->data = r.base; 470 INSIST(opt->offset + 4 + opcode->length <= opt->length); 471 472 return (ISC_R_SUCCESS); 473} 474 475#endif /* RDATA_GENERIC_OPT_41_C */ 476