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