1 /* $NetBSD: opt_41.c,v 1.13 2025/01/26 16:25:33 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 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 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 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 110 isc_buffer_activeregion(source, &sregion); 111 if (sregion.length == 0) { 112 return ISC_R_SUCCESS; 113 } 114 total = 0; 115 while (sregion.length != 0) { 116 if (sregion.length < 4) { 117 return ISC_R_UNEXPECTEDEND; 118 } 119 opt = uint16_fromregion(&sregion); 120 isc_region_consume(&sregion, 2); 121 length = uint16_fromregion(&sregion); 122 isc_region_consume(&sregion, 2); 123 total += 4; 124 if (sregion.length < length) { 125 return ISC_R_UNEXPECTEDEND; 126 } 127 switch (opt) { 128 case DNS_OPT_LLQ: 129 if (length != 18U) { 130 return DNS_R_OPTERR; 131 } 132 isc_region_consume(&sregion, length); 133 break; 134 case DNS_OPT_UL: 135 if (length != 4U && length != 8U) { 136 return DNS_R_OPTERR; 137 } 138 isc_region_consume(&sregion, length); 139 break; 140 case DNS_OPT_CLIENT_SUBNET: { 141 uint16_t family; 142 uint8_t addrlen; 143 uint8_t scope; 144 uint8_t addrbytes; 145 146 if (length < 4) { 147 return DNS_R_OPTERR; 148 } 149 family = uint16_fromregion(&sregion); 150 isc_region_consume(&sregion, 2); 151 addrlen = uint8_fromregion(&sregion); 152 isc_region_consume(&sregion, 1); 153 scope = uint8_fromregion(&sregion); 154 isc_region_consume(&sregion, 1); 155 156 switch (family) { 157 case 0: 158 /* 159 * XXXMUKS: In queries and replies, if 160 * FAMILY is set to 0, SOURCE 161 * PREFIX-LENGTH and SCOPE PREFIX-LENGTH 162 * must be 0 and ADDRESS should not be 163 * present as the address and prefix 164 * lengths don't make sense because the 165 * family is unknown. 166 */ 167 if (addrlen != 0U || scope != 0U) { 168 return DNS_R_OPTERR; 169 } 170 break; 171 case 1: 172 if (addrlen > 32U || scope > 32U) { 173 return DNS_R_OPTERR; 174 } 175 break; 176 case 2: 177 if (addrlen > 128U || scope > 128U) { 178 return DNS_R_OPTERR; 179 } 180 break; 181 default: 182 return DNS_R_OPTERR; 183 } 184 addrbytes = (addrlen + 7) / 8; 185 if (addrbytes + 4 != length) { 186 return DNS_R_OPTERR; 187 } 188 189 if (addrbytes != 0U && (addrlen % 8) != 0) { 190 uint8_t bits = ~0U << (8 - (addrlen % 8)); 191 bits &= sregion.base[addrbytes - 1]; 192 if (bits != sregion.base[addrbytes - 1]) { 193 return DNS_R_OPTERR; 194 } 195 } 196 isc_region_consume(&sregion, addrbytes); 197 break; 198 } 199 case DNS_OPT_EXPIRE: 200 /* 201 * Request has zero length. Response is 32 bits. 202 */ 203 if (length != 0 && length != 4) { 204 return DNS_R_OPTERR; 205 } 206 isc_region_consume(&sregion, length); 207 break; 208 case DNS_OPT_COOKIE: 209 /* 210 * Client cookie alone has length 8. 211 * Client + server cookie is 8 + [8..32]. 212 */ 213 if (length != 8 && (length < 16 || length > 40)) { 214 return DNS_R_OPTERR; 215 } 216 isc_region_consume(&sregion, length); 217 break; 218 case DNS_OPT_KEY_TAG: 219 if (length == 0 || (length % 2) != 0) { 220 return DNS_R_OPTERR; 221 } 222 isc_region_consume(&sregion, length); 223 break; 224 case DNS_OPT_EDE: 225 if (length < 2) { 226 return DNS_R_OPTERR; 227 } 228 /* UTF-8 Byte Order Mark is not permitted. RFC 5198 */ 229 if (isc_utf8_bom(sregion.base + 2, length - 2)) { 230 return DNS_R_OPTERR; 231 } 232 /* 233 * The EXTRA-TEXT field is specified as UTF-8, and 234 * therefore must be validated for correctness 235 * according to RFC 3269 security considerations. 236 */ 237 if (!isc_utf8_valid(sregion.base + 2, length - 2)) { 238 return DNS_R_OPTERR; 239 } 240 isc_region_consume(&sregion, length); 241 break; 242 case DNS_OPT_CLIENT_TAG: 243 FALLTHROUGH; 244 case DNS_OPT_SERVER_TAG: 245 if (length != 2) { 246 return DNS_R_OPTERR; 247 } 248 isc_region_consume(&sregion, length); 249 break; 250 default: 251 isc_region_consume(&sregion, length); 252 break; 253 } 254 total += length; 255 } 256 257 isc_buffer_activeregion(source, &sregion); 258 isc_buffer_availableregion(target, &tregion); 259 if (tregion.length < total) { 260 return ISC_R_NOSPACE; 261 } 262 memmove(tregion.base, sregion.base, total); 263 isc_buffer_forward(source, total); 264 isc_buffer_add(target, total); 265 266 return ISC_R_SUCCESS; 267 } 268 269 static isc_result_t 270 towire_opt(ARGS_TOWIRE) { 271 REQUIRE(rdata->type == dns_rdatatype_opt); 272 273 UNUSED(cctx); 274 275 return mem_tobuffer(target, rdata->data, rdata->length); 276 } 277 278 static int 279 compare_opt(ARGS_COMPARE) { 280 isc_region_t r1; 281 isc_region_t r2; 282 283 REQUIRE(rdata1->type == rdata2->type); 284 REQUIRE(rdata1->rdclass == rdata2->rdclass); 285 REQUIRE(rdata1->type == dns_rdatatype_opt); 286 287 dns_rdata_toregion(rdata1, &r1); 288 dns_rdata_toregion(rdata2, &r2); 289 return isc_region_compare(&r1, &r2); 290 } 291 292 static isc_result_t 293 fromstruct_opt(ARGS_FROMSTRUCT) { 294 dns_rdata_opt_t *opt = source; 295 isc_region_t region; 296 uint16_t length; 297 298 REQUIRE(type == dns_rdatatype_opt); 299 REQUIRE(opt != NULL); 300 REQUIRE(opt->common.rdtype == type); 301 REQUIRE(opt->common.rdclass == rdclass); 302 REQUIRE(opt->options != NULL || opt->length == 0); 303 304 UNUSED(type); 305 UNUSED(rdclass); 306 307 region.base = opt->options; 308 region.length = opt->length; 309 while (region.length >= 4) { 310 isc_region_consume(®ion, 2); /* opt */ 311 length = uint16_fromregion(®ion); 312 isc_region_consume(®ion, 2); 313 if (region.length < length) { 314 return ISC_R_UNEXPECTEDEND; 315 } 316 isc_region_consume(®ion, length); 317 } 318 if (region.length != 0) { 319 return ISC_R_UNEXPECTEDEND; 320 } 321 322 return mem_tobuffer(target, opt->options, opt->length); 323 } 324 325 static isc_result_t 326 tostruct_opt(ARGS_TOSTRUCT) { 327 dns_rdata_opt_t *opt = target; 328 isc_region_t r; 329 330 REQUIRE(rdata->type == dns_rdatatype_opt); 331 REQUIRE(opt != NULL); 332 333 opt->common.rdclass = rdata->rdclass; 334 opt->common.rdtype = rdata->type; 335 ISC_LINK_INIT(&opt->common, link); 336 337 dns_rdata_toregion(rdata, &r); 338 opt->length = r.length; 339 opt->options = mem_maybedup(mctx, r.base, r.length); 340 opt->offset = 0; 341 opt->mctx = mctx; 342 return ISC_R_SUCCESS; 343 } 344 345 static void 346 freestruct_opt(ARGS_FREESTRUCT) { 347 dns_rdata_opt_t *opt = source; 348 349 REQUIRE(opt != NULL); 350 REQUIRE(opt->common.rdtype == dns_rdatatype_opt); 351 352 if (opt->mctx == NULL) { 353 return; 354 } 355 356 if (opt->options != NULL) { 357 isc_mem_free(opt->mctx, opt->options); 358 } 359 opt->mctx = NULL; 360 } 361 362 static isc_result_t 363 additionaldata_opt(ARGS_ADDLDATA) { 364 REQUIRE(rdata->type == dns_rdatatype_opt); 365 366 UNUSED(rdata); 367 UNUSED(owner); 368 UNUSED(add); 369 UNUSED(arg); 370 371 return ISC_R_SUCCESS; 372 } 373 374 static isc_result_t 375 digest_opt(ARGS_DIGEST) { 376 /* 377 * OPT records are not digested. 378 */ 379 380 REQUIRE(rdata->type == dns_rdatatype_opt); 381 382 UNUSED(rdata); 383 UNUSED(digest); 384 UNUSED(arg); 385 386 return ISC_R_NOTIMPLEMENTED; 387 } 388 389 static bool 390 checkowner_opt(ARGS_CHECKOWNER) { 391 REQUIRE(type == dns_rdatatype_opt); 392 393 UNUSED(type); 394 UNUSED(rdclass); 395 UNUSED(wildcard); 396 397 return dns_name_equal(name, dns_rootname); 398 } 399 400 static bool 401 checknames_opt(ARGS_CHECKNAMES) { 402 REQUIRE(rdata->type == dns_rdatatype_opt); 403 404 UNUSED(rdata); 405 UNUSED(owner); 406 UNUSED(bad); 407 408 return true; 409 } 410 411 static int 412 casecompare_opt(ARGS_COMPARE) { 413 return compare_opt(rdata1, rdata2); 414 } 415 416 isc_result_t 417 dns_rdata_opt_first(dns_rdata_opt_t *opt) { 418 REQUIRE(opt != NULL); 419 REQUIRE(opt->common.rdtype == dns_rdatatype_opt); 420 REQUIRE(opt->options != NULL || opt->length == 0); 421 422 if (opt->length == 0) { 423 return ISC_R_NOMORE; 424 } 425 426 opt->offset = 0; 427 return ISC_R_SUCCESS; 428 } 429 430 isc_result_t 431 dns_rdata_opt_next(dns_rdata_opt_t *opt) { 432 isc_region_t r; 433 uint16_t length; 434 435 REQUIRE(opt != NULL); 436 REQUIRE(opt->common.rdtype == dns_rdatatype_opt); 437 REQUIRE(opt->options != NULL && opt->length != 0); 438 REQUIRE(opt->offset < opt->length); 439 440 INSIST(opt->offset + 4 <= opt->length); 441 r.base = opt->options + opt->offset + 2; 442 r.length = opt->length - opt->offset - 2; 443 length = uint16_fromregion(&r); 444 INSIST(opt->offset + 4 + length <= opt->length); 445 opt->offset = opt->offset + 4 + length; 446 if (opt->offset == opt->length) { 447 return ISC_R_NOMORE; 448 } 449 return ISC_R_SUCCESS; 450 } 451 452 isc_result_t 453 dns_rdata_opt_current(dns_rdata_opt_t *opt, dns_rdata_opt_opcode_t *opcode) { 454 isc_region_t r; 455 456 REQUIRE(opt != NULL); 457 REQUIRE(opcode != NULL); 458 REQUIRE(opt->common.rdtype == dns_rdatatype_opt); 459 REQUIRE(opt->options != NULL); 460 REQUIRE(opt->offset < opt->length); 461 462 INSIST(opt->offset + 4 <= opt->length); 463 r.base = opt->options + opt->offset; 464 r.length = opt->length - opt->offset; 465 466 opcode->opcode = uint16_fromregion(&r); 467 isc_region_consume(&r, 2); 468 opcode->length = uint16_fromregion(&r); 469 isc_region_consume(&r, 2); 470 opcode->data = r.base; 471 INSIST(opt->offset + 4 + opcode->length <= opt->length); 472 473 return ISC_R_SUCCESS; 474 } 475 476 #endif /* RDATA_GENERIC_OPT_41_C */ 477