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 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 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 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 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 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 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 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 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 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 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 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 410 casecompare_opt(ARGS_COMPARE) { 411 return (compare_opt(rdata1, rdata2)); 412 } 413 414 isc_result_t 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 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 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