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