xref: /netbsd-src/external/mpl/bind/dist/lib/dns/rdataset.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
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