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(®ion, 2); /* opt */ 277 length = uint16_fromregion(®ion); 278 isc_region_consume(®ion, 2); 279 if (region.length < length) 280 return (ISC_R_UNEXPECTEDEND); 281 isc_region_consume(®ion, 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