Home | History | Annotate | Line # | Download | only in dns
      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