1 /* $NetBSD: rdataset.c,v 1.9 2025/01/26 16:25:24 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 /*! \file */ 17 18 #include <inttypes.h> 19 #include <stdbool.h> 20 #include <stdlib.h> 21 22 #include <isc/buffer.h> 23 #include <isc/mem.h> 24 #include <isc/random.h> 25 #include <isc/serial.h> 26 #include <isc/util.h> 27 28 #include <dns/compress.h> 29 #include <dns/fixedname.h> 30 #include <dns/name.h> 31 #include <dns/ncache.h> 32 #include <dns/rdata.h> 33 #include <dns/rdataset.h> 34 35 static const char *trustnames[] = { 36 "none", "pending-additional", 37 "pending-answer", "additional", 38 "glue", "answer", 39 "authauthority", "authanswer", 40 "secure", "local" /* aka ultimate */ 41 }; 42 43 const char * 44 dns_trust_totext(dns_trust_t trust) { 45 if (trust >= sizeof(trustnames) / sizeof(*trustnames)) { 46 return "bad"; 47 } 48 return trustnames[trust]; 49 } 50 51 void 52 dns_rdataset_init(dns_rdataset_t *rdataset) { 53 /* 54 * Make 'rdataset' a valid, disassociated rdataset. 55 */ 56 57 REQUIRE(rdataset != NULL); 58 59 *rdataset = (dns_rdataset_t){ 60 .magic = DNS_RDATASET_MAGIC, 61 .link = ISC_LINK_INITIALIZER, 62 .count = DNS_RDATASET_COUNT_UNDEFINED, 63 }; 64 } 65 66 void 67 dns_rdataset_invalidate(dns_rdataset_t *rdataset) { 68 /* 69 * Invalidate 'rdataset'. 70 */ 71 72 REQUIRE(DNS_RDATASET_VALID(rdataset)); 73 REQUIRE(rdataset->methods == NULL); 74 75 *rdataset = (dns_rdataset_t){ 76 .magic = 0, 77 .link = ISC_LINK_INITIALIZER, 78 .count = DNS_RDATASET_COUNT_UNDEFINED, 79 }; 80 } 81 82 void 83 dns__rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) { 84 /* 85 * Disassociate 'rdataset' from its rdata, allowing it to be reused. 86 */ 87 88 REQUIRE(DNS_RDATASET_VALID(rdataset)); 89 REQUIRE(rdataset->methods != NULL); 90 91 if (rdataset->methods->disassociate != NULL) { 92 (rdataset->methods->disassociate)(rdataset DNS__DB_FLARG_PASS); 93 } 94 *rdataset = (dns_rdataset_t){ 95 .magic = DNS_RDATASET_MAGIC, 96 .link = ISC_LINK_INITIALIZER, 97 .count = DNS_RDATASET_COUNT_UNDEFINED, 98 }; 99 } 100 101 bool 102 dns_rdataset_isassociated(dns_rdataset_t *rdataset) { 103 /* 104 * Is 'rdataset' associated? 105 */ 106 107 REQUIRE(DNS_RDATASET_VALID(rdataset)); 108 109 if (rdataset->methods != NULL) { 110 return true; 111 } 112 113 return false; 114 } 115 116 static isc_result_t 117 question_cursor(dns_rdataset_t *rdataset ISC_ATTR_UNUSED) { 118 return ISC_R_NOMORE; 119 } 120 121 static void 122 question_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) { 123 *target = *source; 124 } 125 126 static dns_rdatasetmethods_t question_methods = { 127 .first = question_cursor, 128 .next = question_cursor, 129 .clone = question_clone, 130 }; 131 132 void 133 dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass, 134 dns_rdatatype_t type) { 135 /* 136 * Make 'rdataset' a valid, associated, question rdataset, with a 137 * question class of 'rdclass' and type 'type'. 138 */ 139 140 REQUIRE(DNS_RDATASET_VALID(rdataset)); 141 REQUIRE(rdataset->methods == NULL); 142 143 rdataset->methods = &question_methods; 144 rdataset->rdclass = rdclass; 145 rdataset->type = type; 146 rdataset->attributes |= DNS_RDATASETATTR_QUESTION; 147 } 148 149 unsigned int 150 dns_rdataset_count(dns_rdataset_t *rdataset) { 151 /* 152 * Return the number of records in 'rdataset'. 153 */ 154 155 REQUIRE(DNS_RDATASET_VALID(rdataset)); 156 REQUIRE(rdataset->methods != NULL); 157 REQUIRE(rdataset->methods->count != NULL); 158 159 return (rdataset->methods->count)(rdataset); 160 } 161 162 void 163 dns__rdataset_clone(dns_rdataset_t *source, 164 dns_rdataset_t *target DNS__DB_FLARG) { 165 /* 166 * Make 'target' refer to the same rdataset as 'source'. 167 */ 168 169 REQUIRE(DNS_RDATASET_VALID(source)); 170 REQUIRE(source->methods != NULL); 171 REQUIRE(DNS_RDATASET_VALID(target)); 172 REQUIRE(target->methods == NULL); 173 174 (source->methods->clone)(source, target DNS__DB_FLARG_PASS); 175 } 176 177 isc_result_t 178 dns_rdataset_first(dns_rdataset_t *rdataset) { 179 /* 180 * Move the rdata cursor to the first rdata in the rdataset (if any). 181 */ 182 183 REQUIRE(DNS_RDATASET_VALID(rdataset)); 184 REQUIRE(rdataset->methods != NULL); 185 REQUIRE(rdataset->methods->first != NULL); 186 187 return (rdataset->methods->first)(rdataset); 188 } 189 190 isc_result_t 191 dns_rdataset_next(dns_rdataset_t *rdataset) { 192 /* 193 * Move the rdata cursor to the next rdata in the rdataset (if any). 194 */ 195 196 REQUIRE(DNS_RDATASET_VALID(rdataset)); 197 REQUIRE(rdataset->methods != NULL); 198 REQUIRE(rdataset->methods->next != NULL); 199 200 return (rdataset->methods->next)(rdataset); 201 } 202 203 void 204 dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { 205 /* 206 * Make 'rdata' refer to the current rdata. 207 */ 208 209 REQUIRE(DNS_RDATASET_VALID(rdataset)); 210 REQUIRE(rdataset->methods != NULL); 211 REQUIRE(rdataset->methods->current != NULL); 212 213 (rdataset->methods->current)(rdataset, rdata); 214 } 215 216 #define MAX_SHUFFLE 32 217 #define WANT_FIXED(r) (((r)->attributes & DNS_RDATASETATTR_FIXEDORDER) != 0) 218 #define WANT_RANDOM(r) (((r)->attributes & DNS_RDATASETATTR_RANDOMIZE) != 0) 219 #define WANT_CYCLIC(r) (((r)->attributes & DNS_RDATASETATTR_CYCLIC) != 0) 220 221 struct towire_sort { 222 int key; 223 dns_rdata_t *rdata; 224 }; 225 226 static int 227 towire_compare(const void *av, const void *bv) { 228 const struct towire_sort *a = (const struct towire_sort *)av; 229 const struct towire_sort *b = (const struct towire_sort *)bv; 230 return a->key - b->key; 231 } 232 233 static void 234 swap_rdata(dns_rdata_t *in, unsigned int a, unsigned int b) { 235 dns_rdata_t rdata = in[a]; 236 in[a] = in[b]; 237 in[b] = rdata; 238 } 239 240 static isc_result_t 241 towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name, 242 dns_compress_t *cctx, isc_buffer_t *target, 243 dns_rdatasetorderfunc_t order, const void *order_arg, bool partial, 244 unsigned int options, unsigned int *countp, 245 void **state ISC_ATTR_UNUSED) { 246 isc_region_t r; 247 isc_result_t result; 248 unsigned int i, count = 0, added; 249 isc_buffer_t savedbuffer, rdlen, rrbuffer; 250 unsigned int headlen; 251 bool question = false; 252 bool shuffle = false, sort = false; 253 bool want_random, want_cyclic; 254 dns_rdata_t in_fixed[MAX_SHUFFLE]; 255 dns_rdata_t *in = in_fixed; 256 struct towire_sort out_fixed[MAX_SHUFFLE]; 257 struct towire_sort *out = out_fixed; 258 dns_fixedname_t fixed; 259 dns_name_t *name = NULL; 260 uint16_t offset; 261 262 /* 263 * Convert 'rdataset' to wire format, compressing names as specified 264 * in cctx, and storing the result in 'target'. 265 */ 266 267 REQUIRE(DNS_RDATASET_VALID(rdataset)); 268 REQUIRE(rdataset->methods != NULL); 269 REQUIRE(countp != NULL); 270 REQUIRE(cctx != NULL && cctx->mctx != NULL); 271 272 want_random = WANT_RANDOM(rdataset); 273 want_cyclic = WANT_CYCLIC(rdataset); 274 275 if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) { 276 question = true; 277 count = 1; 278 result = dns_rdataset_first(rdataset); 279 INSIST(result == ISC_R_NOMORE); 280 } else if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { 281 /* 282 * This is a negative caching rdataset. 283 */ 284 unsigned int ncache_opts = 0; 285 if ((options & DNS_RDATASETTOWIRE_OMITDNSSEC) != 0) { 286 ncache_opts |= DNS_NCACHETOWIRE_OMITDNSSEC; 287 } 288 return dns_ncache_towire(rdataset, cctx, target, ncache_opts, 289 countp); 290 } else { 291 count = dns_rdataset_count(rdataset); 292 result = dns_rdataset_first(rdataset); 293 if (result == ISC_R_NOMORE) { 294 return ISC_R_SUCCESS; 295 } 296 if (result != ISC_R_SUCCESS) { 297 return result; 298 } 299 } 300 301 /* 302 * Do we want to sort and/or shuffle this answer? 303 */ 304 if (!question && count > 1 && rdataset->type != dns_rdatatype_rrsig) { 305 if (order != NULL) { 306 sort = true; 307 } 308 if (want_random || want_cyclic) { 309 shuffle = true; 310 } 311 } 312 313 if (shuffle || sort) { 314 if (count > MAX_SHUFFLE) { 315 in = isc_mem_cget(cctx->mctx, count, sizeof(*in)); 316 out = isc_mem_cget(cctx->mctx, count, sizeof(*out)); 317 if (in == NULL || out == NULL) { 318 shuffle = sort = false; 319 } 320 } 321 } 322 323 if (shuffle || sort) { 324 uint32_t seed = 0; 325 unsigned int j = 0; 326 327 /* 328 * First we get handles to all of the rdata. 329 */ 330 i = 0; 331 do { 332 INSIST(i < count); 333 dns_rdata_init(&in[i]); 334 dns_rdataset_current(rdataset, &in[i]); 335 i++; 336 result = dns_rdataset_next(rdataset); 337 } while (result == ISC_R_SUCCESS); 338 if (result != ISC_R_NOMORE) { 339 goto cleanup; 340 } 341 INSIST(i == count); 342 343 if (want_random) { 344 seed = isc_random32(); 345 } 346 347 if (want_cyclic && 348 (rdataset->count != DNS_RDATASET_COUNT_UNDEFINED)) 349 { 350 j = rdataset->count % count; 351 } 352 353 for (i = 0; i < count; i++) { 354 if (want_random) { 355 swap_rdata(in, j, j + seed % (count - j)); 356 } 357 358 out[i].key = (sort) ? (*order)(&in[j], order_arg) : 0; 359 out[i].rdata = &in[j]; 360 if (++j == count) { 361 j = 0; 362 } 363 } 364 /* 365 * Sortlist order. 366 */ 367 if (sort) { 368 qsort(out, count, sizeof(out[0]), towire_compare); 369 } 370 } 371 372 savedbuffer = *target; 373 i = 0; 374 added = 0; 375 376 name = dns_fixedname_initname(&fixed); 377 dns_name_copy(owner_name, name); 378 dns_rdataset_getownercase(rdataset, name); 379 offset = 0xffff; 380 381 name->attributes.nocompress |= owner_name->attributes.nocompress; 382 383 do { 384 /* 385 * Copy out the name, type, class, ttl. 386 */ 387 388 rrbuffer = *target; 389 dns_compress_setpermitted(cctx, true); 390 result = dns_name_towire(name, cctx, target, &offset); 391 if (result != ISC_R_SUCCESS) { 392 goto rollback; 393 } 394 headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t); 395 if (!question) { 396 headlen += sizeof(dns_ttl_t) + 2; 397 } /* XXX 2 for rdata len 398 */ 399 isc_buffer_availableregion(target, &r); 400 if (r.length < headlen) { 401 result = ISC_R_NOSPACE; 402 goto rollback; 403 } 404 isc_buffer_putuint16(target, rdataset->type); 405 isc_buffer_putuint16(target, rdataset->rdclass); 406 if (!question) { 407 dns_rdata_t rdata = DNS_RDATA_INIT; 408 409 isc_buffer_putuint32(target, rdataset->ttl); 410 411 /* 412 * Save space for rdlen. 413 */ 414 rdlen = *target; 415 isc_buffer_add(target, 2); 416 417 /* 418 * Copy out the rdata 419 */ 420 if (shuffle || sort) { 421 rdata = *(out[i].rdata); 422 } else { 423 dns_rdata_reset(&rdata); 424 dns_rdataset_current(rdataset, &rdata); 425 } 426 result = dns_rdata_towire(&rdata, cctx, target); 427 if (result != ISC_R_SUCCESS) { 428 goto rollback; 429 } 430 INSIST((target->used >= rdlen.used + 2) && 431 (target->used - rdlen.used - 2 < 65536)); 432 isc_buffer_putuint16( 433 &rdlen, 434 (uint16_t)(target->used - rdlen.used - 2)); 435 added++; 436 } 437 438 if (shuffle || sort) { 439 i++; 440 if (i == count) { 441 result = ISC_R_NOMORE; 442 } else { 443 result = ISC_R_SUCCESS; 444 } 445 } else { 446 result = dns_rdataset_next(rdataset); 447 } 448 } while (result == ISC_R_SUCCESS); 449 450 if (result != ISC_R_NOMORE) { 451 goto rollback; 452 } 453 454 *countp += count; 455 456 result = ISC_R_SUCCESS; 457 goto cleanup; 458 459 rollback: 460 if (partial && result == ISC_R_NOSPACE) { 461 dns_compress_rollback(cctx, rrbuffer.used); 462 *countp += added; 463 *target = rrbuffer; 464 goto cleanup; 465 } 466 dns_compress_rollback(cctx, savedbuffer.used); 467 *countp = 0; 468 *target = savedbuffer; 469 470 cleanup: 471 if (out != NULL && out != out_fixed) { 472 isc_mem_cput(cctx->mctx, out, count, sizeof(*out)); 473 } 474 if (in != NULL && in != in_fixed) { 475 isc_mem_cput(cctx->mctx, in, count, sizeof(*in)); 476 } 477 return result; 478 } 479 480 isc_result_t 481 dns_rdataset_towiresorted(dns_rdataset_t *rdataset, 482 const dns_name_t *owner_name, dns_compress_t *cctx, 483 isc_buffer_t *target, dns_rdatasetorderfunc_t order, 484 const void *order_arg, unsigned int options, 485 unsigned int *countp) { 486 return towiresorted(rdataset, owner_name, cctx, target, order, 487 order_arg, false, options, countp, NULL); 488 } 489 490 isc_result_t 491 dns_rdataset_towirepartial(dns_rdataset_t *rdataset, 492 const dns_name_t *owner_name, dns_compress_t *cctx, 493 isc_buffer_t *target, dns_rdatasetorderfunc_t order, 494 const void *order_arg, unsigned int options, 495 unsigned int *countp, void **state) { 496 REQUIRE(state == NULL); /* XXX remove when implemented */ 497 return towiresorted(rdataset, owner_name, cctx, target, order, 498 order_arg, true, options, countp, state); 499 } 500 501 isc_result_t 502 dns_rdataset_towire(dns_rdataset_t *rdataset, const dns_name_t *owner_name, 503 dns_compress_t *cctx, isc_buffer_t *target, 504 unsigned int options, unsigned int *countp) { 505 return towiresorted(rdataset, owner_name, cctx, target, NULL, NULL, 506 false, options, countp, NULL); 507 } 508 509 isc_result_t 510 dns_rdataset_additionaldata(dns_rdataset_t *rdataset, 511 const dns_name_t *owner_name, 512 dns_additionaldatafunc_t add, void *arg) { 513 dns_rdata_t rdata = DNS_RDATA_INIT; 514 isc_result_t result; 515 516 /* 517 * For each rdata in rdataset, call 'add' for each name and type in the 518 * rdata which is subject to additional section processing. 519 */ 520 521 REQUIRE(DNS_RDATASET_VALID(rdataset)); 522 REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0); 523 524 result = dns_rdataset_first(rdataset); 525 if (result != ISC_R_SUCCESS) { 526 return result; 527 } 528 529 do { 530 dns_rdataset_current(rdataset, &rdata); 531 result = dns_rdata_additionaldata(&rdata, owner_name, add, arg); 532 if (result == ISC_R_SUCCESS) { 533 result = dns_rdataset_next(rdataset); 534 } 535 dns_rdata_reset(&rdata); 536 } while (result == ISC_R_SUCCESS); 537 538 if (result != ISC_R_NOMORE) { 539 return result; 540 } 541 542 return ISC_R_SUCCESS; 543 } 544 545 isc_result_t 546 dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) { 547 REQUIRE(DNS_RDATASET_VALID(rdataset)); 548 REQUIRE(rdataset->methods != NULL); 549 if (rdataset->methods->addnoqname == NULL) { 550 return ISC_R_NOTIMPLEMENTED; 551 } 552 return (rdataset->methods->addnoqname)(rdataset, name); 553 } 554 555 isc_result_t 556 dns__rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, 557 dns_rdataset_t *neg, 558 dns_rdataset_t *negsig DNS__DB_FLARG) { 559 REQUIRE(DNS_RDATASET_VALID(rdataset)); 560 REQUIRE(rdataset->methods != NULL); 561 562 if (rdataset->methods->getnoqname == NULL) { 563 return ISC_R_NOTIMPLEMENTED; 564 } 565 return (rdataset->methods->getnoqname)(rdataset, name, neg, 566 negsig DNS__DB_FLARG_PASS); 567 } 568 569 isc_result_t 570 dns_rdataset_addclosest(dns_rdataset_t *rdataset, const dns_name_t *name) { 571 REQUIRE(DNS_RDATASET_VALID(rdataset)); 572 REQUIRE(rdataset->methods != NULL); 573 if (rdataset->methods->addclosest == NULL) { 574 return ISC_R_NOTIMPLEMENTED; 575 } 576 return (rdataset->methods->addclosest)(rdataset, name); 577 } 578 579 isc_result_t 580 dns__rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name, 581 dns_rdataset_t *neg, 582 dns_rdataset_t *negsig DNS__DB_FLARG) { 583 REQUIRE(DNS_RDATASET_VALID(rdataset)); 584 REQUIRE(rdataset->methods != NULL); 585 586 if (rdataset->methods->getclosest == NULL) { 587 return ISC_R_NOTIMPLEMENTED; 588 } 589 return (rdataset->methods->getclosest)(rdataset, name, neg, 590 negsig DNS__DB_FLARG_PASS); 591 } 592 593 void 594 dns_rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) { 595 REQUIRE(DNS_RDATASET_VALID(rdataset)); 596 REQUIRE(rdataset->methods != NULL); 597 598 if (rdataset->methods->settrust != NULL) { 599 (rdataset->methods->settrust)(rdataset, trust); 600 } else { 601 rdataset->trust = trust; 602 } 603 } 604 605 void 606 dns__rdataset_expire(dns_rdataset_t *rdataset DNS__DB_FLARG) { 607 REQUIRE(DNS_RDATASET_VALID(rdataset)); 608 REQUIRE(rdataset->methods != NULL); 609 610 if (rdataset->methods->expire != NULL) { 611 (rdataset->methods->expire)(rdataset DNS__DB_FLARG_PASS); 612 } 613 } 614 615 void 616 dns_rdataset_clearprefetch(dns_rdataset_t *rdataset) { 617 REQUIRE(DNS_RDATASET_VALID(rdataset)); 618 REQUIRE(rdataset->methods != NULL); 619 620 if (rdataset->methods->clearprefetch != NULL) { 621 (rdataset->methods->clearprefetch)(rdataset); 622 } 623 } 624 625 void 626 dns_rdataset_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) { 627 REQUIRE(DNS_RDATASET_VALID(rdataset)); 628 REQUIRE(rdataset->methods != NULL); 629 630 if (rdataset->methods->setownercase != NULL && 631 (rdataset->attributes & DNS_RDATASETATTR_KEEPCASE) == 0) 632 { 633 (rdataset->methods->setownercase)(rdataset, name); 634 } 635 } 636 637 void 638 dns_rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) { 639 REQUIRE(DNS_RDATASET_VALID(rdataset)); 640 REQUIRE(rdataset->methods != NULL); 641 642 if (rdataset->methods->getownercase != NULL && 643 (rdataset->attributes & DNS_RDATASETATTR_KEEPCASE) == 0) 644 { 645 (rdataset->methods->getownercase)(rdataset, name); 646 } 647 } 648 649 void 650 dns_rdataset_trimttl(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, 651 dns_rdata_rrsig_t *rrsig, isc_stdtime_t now, 652 bool acceptexpired) { 653 uint32_t ttl = 0; 654 655 REQUIRE(DNS_RDATASET_VALID(rdataset)); 656 REQUIRE(DNS_RDATASET_VALID(sigrdataset)); 657 REQUIRE(rrsig != NULL); 658 659 /* 660 * If we accept expired RRsets keep them for no more than 120 seconds. 661 */ 662 if (acceptexpired && 663 (isc_serial_le(rrsig->timeexpire, ((now + 120) & 0xffffffff)) || 664 isc_serial_le(rrsig->timeexpire, now))) 665 { 666 ttl = 120; 667 } else if (isc_serial_ge(rrsig->timeexpire, now)) { 668 ttl = rrsig->timeexpire - now; 669 } 670 671 ttl = ISC_MIN(ISC_MIN(rdataset->ttl, sigrdataset->ttl), 672 ISC_MIN(rrsig->originalttl, ttl)); 673 rdataset->ttl = ttl; 674 sigrdataset->ttl = ttl; 675 } 676