xref: /netbsd-src/external/mpl/bind/dist/lib/dns/ncache.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: ncache.c,v 1.10 2025/01/26 16:25:23 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 
21 #include <isc/buffer.h>
22 #include <isc/util.h>
23 
24 #include <dns/db.h>
25 #include <dns/message.h>
26 #include <dns/ncache.h>
27 #include <dns/rdata.h>
28 #include <dns/rdatalist.h>
29 #include <dns/rdataset.h>
30 #include <dns/rdatastruct.h>
31 
32 #define DNS_NCACHE_RDATA 100U
33 
34 /*
35  * The format of an ncache rdata is a sequence of zero or more records
36  * of the following format:
37  *
38  *	owner name
39  *	type
40  *	trust
41  *	rdata count
42  *	rdata length			These two occur 'rdata
43  *	rdata				count' times.
44  *
45  */
46 
47 static uint8_t
48 atomic_getuint8(isc_buffer_t *b) {
49 	atomic_uchar *cp = isc_buffer_current(b);
50 	uint8_t ret = atomic_load_relaxed(cp);
51 	isc_buffer_forward(b, 1);
52 	return ret;
53 }
54 
55 static isc_result_t
56 addoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
57 	  dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl,
58 	  dns_ttl_t maxttl, bool optout, bool secure,
59 	  dns_rdataset_t *addedrdataset);
60 
61 static isc_result_t
62 copy_rdataset(dns_rdataset_t *rdataset, isc_buffer_t *buffer) {
63 	isc_result_t result;
64 	unsigned int count;
65 	isc_region_t ar, r;
66 	dns_rdata_t rdata = DNS_RDATA_INIT;
67 
68 	/*
69 	 * Copy the rdataset count to the buffer.
70 	 */
71 	isc_buffer_availableregion(buffer, &ar);
72 	if (ar.length < 2) {
73 		return ISC_R_NOSPACE;
74 	}
75 	count = dns_rdataset_count(rdataset);
76 	INSIST(count <= 65535);
77 	isc_buffer_putuint16(buffer, (uint16_t)count);
78 
79 	result = dns_rdataset_first(rdataset);
80 	while (result == ISC_R_SUCCESS) {
81 		dns_rdataset_current(rdataset, &rdata);
82 		dns_rdata_toregion(&rdata, &r);
83 		INSIST(r.length <= 65535);
84 		isc_buffer_availableregion(buffer, &ar);
85 		if (ar.length < 2) {
86 			return ISC_R_NOSPACE;
87 		}
88 		/*
89 		 * Copy the rdata length to the buffer.
90 		 */
91 		isc_buffer_putuint16(buffer, (uint16_t)r.length);
92 		/*
93 		 * Copy the rdata to the buffer.
94 		 */
95 		result = isc_buffer_copyregion(buffer, &r);
96 		if (result != ISC_R_SUCCESS) {
97 			return result;
98 		}
99 		dns_rdata_reset(&rdata);
100 		result = dns_rdataset_next(rdataset);
101 	}
102 	if (result != ISC_R_NOMORE) {
103 		return result;
104 	}
105 
106 	return ISC_R_SUCCESS;
107 }
108 
109 isc_result_t
110 dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
111 	       dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl,
112 	       dns_ttl_t maxttl, dns_rdataset_t *addedrdataset) {
113 	return addoptout(message, cache, node, covers, now, minttl, maxttl,
114 			 false, false, addedrdataset);
115 }
116 
117 isc_result_t
118 dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
119 		     dns_dbnode_t *node, dns_rdatatype_t covers,
120 		     isc_stdtime_t now, dns_ttl_t minttl, dns_ttl_t maxttl,
121 		     bool optout, dns_rdataset_t *addedrdataset) {
122 	return addoptout(message, cache, node, covers, now, minttl, maxttl,
123 			 optout, true, addedrdataset);
124 }
125 
126 static isc_result_t
127 addoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
128 	  dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl,
129 	  dns_ttl_t maxttl, bool optout, bool secure,
130 	  dns_rdataset_t *addedrdataset) {
131 	isc_result_t result;
132 	isc_buffer_t buffer;
133 	isc_region_t r;
134 	dns_rdataset_t *rdataset;
135 	dns_rdatatype_t type;
136 	dns_name_t *name;
137 	dns_ttl_t ttl;
138 	dns_trust_t trust;
139 	dns_rdata_t rdata[DNS_NCACHE_RDATA];
140 	dns_rdataset_t ncrdataset;
141 	dns_rdatalist_t ncrdatalist;
142 	unsigned char data[65536];
143 	unsigned int next = 0;
144 
145 	/*
146 	 * Convert the authority data from 'message' into a negative cache
147 	 * rdataset, and store it in 'cache' at 'node'.
148 	 */
149 
150 	REQUIRE(message != NULL);
151 
152 	/*
153 	 * We assume that all data in the authority section has been
154 	 * validated by the caller.
155 	 */
156 
157 	/*
158 	 * Initialize the list.
159 	 */
160 	dns_rdatalist_init(&ncrdatalist);
161 	ncrdatalist.rdclass = dns_db_class(cache);
162 	ncrdatalist.covers = covers;
163 	ncrdatalist.ttl = maxttl;
164 
165 	/*
166 	 * Build an ncache rdatas into buffer.
167 	 */
168 	ttl = maxttl;
169 	trust = 0xffff;
170 	isc_buffer_init(&buffer, data, sizeof(data));
171 	if (message->counts[DNS_SECTION_AUTHORITY]) {
172 		result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
173 	} else {
174 		result = ISC_R_NOMORE;
175 	}
176 	while (result == ISC_R_SUCCESS) {
177 		name = NULL;
178 		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
179 		if (name->attributes.ncache) {
180 			for (rdataset = ISC_LIST_HEAD(name->list);
181 			     rdataset != NULL;
182 			     rdataset = ISC_LIST_NEXT(rdataset, link))
183 			{
184 				if ((rdataset->attributes &
185 				     DNS_RDATASETATTR_NCACHE) == 0)
186 				{
187 					continue;
188 				}
189 				type = rdataset->type;
190 				if (type == dns_rdatatype_rrsig) {
191 					type = rdataset->covers;
192 				}
193 				if (type == dns_rdatatype_soa ||
194 				    type == dns_rdatatype_nsec ||
195 				    type == dns_rdatatype_nsec3)
196 				{
197 					if (ttl > rdataset->ttl) {
198 						ttl = rdataset->ttl;
199 					}
200 					if (ttl < minttl) {
201 						ttl = minttl;
202 					}
203 					if (trust > rdataset->trust) {
204 						trust = rdataset->trust;
205 					}
206 					/*
207 					 * Copy the owner name to the buffer.
208 					 */
209 					dns_name_toregion(name, &r);
210 					result = isc_buffer_copyregion(&buffer,
211 								       &r);
212 					if (result != ISC_R_SUCCESS) {
213 						return result;
214 					}
215 					/*
216 					 * Copy the type to the buffer.
217 					 */
218 					isc_buffer_availableregion(&buffer, &r);
219 					if (r.length < 3) {
220 						return ISC_R_NOSPACE;
221 					}
222 					isc_buffer_putuint16(&buffer,
223 							     rdataset->type);
224 					isc_buffer_putuint8(
225 						&buffer,
226 						(unsigned char)rdataset->trust);
227 					/*
228 					 * Copy the rdataset into the buffer.
229 					 */
230 					result = copy_rdataset(rdataset,
231 							       &buffer);
232 					if (result != ISC_R_SUCCESS) {
233 						return result;
234 					}
235 
236 					if (next >= DNS_NCACHE_RDATA) {
237 						return ISC_R_NOSPACE;
238 					}
239 					dns_rdata_init(&rdata[next]);
240 					isc_buffer_remainingregion(&buffer, &r);
241 					rdata[next].data = r.base;
242 					rdata[next].length = r.length;
243 					rdata[next].rdclass =
244 						ncrdatalist.rdclass;
245 					rdata[next].type = 0;
246 					rdata[next].flags = 0;
247 					ISC_LIST_APPEND(ncrdatalist.rdata,
248 							&rdata[next], link);
249 					isc_buffer_forward(&buffer, r.length);
250 					next++;
251 				}
252 			}
253 		}
254 		result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
255 	}
256 	if (result != ISC_R_NOMORE) {
257 		return result;
258 	}
259 
260 	if (trust == 0xffff) {
261 		if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 &&
262 		    message->counts[DNS_SECTION_ANSWER] == 0)
263 		{
264 			/*
265 			 * The response has aa set and we haven't followed
266 			 * any CNAME or DNAME chains.
267 			 */
268 			trust = dns_trust_authauthority;
269 		} else {
270 			trust = dns_trust_additional;
271 		}
272 		ttl = 0;
273 	}
274 
275 	INSIST(trust != 0xffff);
276 
277 	ncrdatalist.ttl = ttl;
278 
279 	dns_rdataset_init(&ncrdataset);
280 	dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset);
281 	if (!secure && trust > dns_trust_answer) {
282 		trust = dns_trust_answer;
283 	}
284 	ncrdataset.trust = trust;
285 	ncrdataset.attributes |= DNS_RDATASETATTR_NEGATIVE;
286 	if (message->rcode == dns_rcode_nxdomain) {
287 		ncrdataset.attributes |= DNS_RDATASETATTR_NXDOMAIN;
288 	}
289 	if (optout) {
290 		ncrdataset.attributes |= DNS_RDATASETATTR_OPTOUT;
291 	}
292 
293 	return dns_db_addrdataset(cache, node, NULL, now, &ncrdataset, 0,
294 				  addedrdataset);
295 }
296 
297 isc_result_t
298 dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
299 		  isc_buffer_t *target, unsigned int options,
300 		  unsigned int *countp) {
301 	dns_rdata_t rdata = DNS_RDATA_INIT;
302 	isc_result_t result;
303 	isc_region_t remaining, tavailable;
304 	isc_buffer_t source, savedbuffer, rdlen;
305 	dns_name_t name;
306 	dns_rdatatype_t type;
307 	unsigned int i, rcount, count;
308 
309 	/*
310 	 * Convert the negative caching rdataset 'rdataset' to wire format,
311 	 * compressing names as specified in 'cctx', and storing the result in
312 	 * 'target'.
313 	 */
314 
315 	REQUIRE(rdataset != NULL);
316 	REQUIRE(rdataset->type == 0);
317 	REQUIRE((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
318 
319 	savedbuffer = *target;
320 	count = 0;
321 
322 	result = dns_rdataset_first(rdataset);
323 	while (result == ISC_R_SUCCESS) {
324 		dns_rdataset_current(rdataset, &rdata);
325 		isc_buffer_init(&source, rdata.data, rdata.length);
326 		isc_buffer_add(&source, rdata.length);
327 		dns_name_init(&name, NULL);
328 		isc_buffer_remainingregion(&source, &remaining);
329 		dns_name_fromregion(&name, &remaining);
330 		INSIST(remaining.length >= name.length);
331 		isc_buffer_forward(&source, name.length);
332 		remaining.length -= name.length;
333 
334 		INSIST(remaining.length >= 5);
335 		type = isc_buffer_getuint16(&source);
336 		isc_buffer_forward(&source, 1);
337 		rcount = isc_buffer_getuint16(&source);
338 
339 		for (i = 0; i < rcount; i++) {
340 			/*
341 			 * Get the length of this rdata and set up an
342 			 * rdata structure for it.
343 			 */
344 			isc_buffer_remainingregion(&source, &remaining);
345 			INSIST(remaining.length >= 2);
346 			dns_rdata_reset(&rdata);
347 			rdata.length = isc_buffer_getuint16(&source);
348 			isc_buffer_remainingregion(&source, &remaining);
349 			rdata.data = remaining.base;
350 			rdata.type = type;
351 			rdata.rdclass = rdataset->rdclass;
352 			INSIST(remaining.length >= rdata.length);
353 			isc_buffer_forward(&source, rdata.length);
354 
355 			if ((options & DNS_NCACHETOWIRE_OMITDNSSEC) != 0 &&
356 			    dns_rdatatype_isdnssec(type))
357 			{
358 				continue;
359 			}
360 
361 			/*
362 			 * Write the name.
363 			 */
364 			dns_compress_setpermitted(cctx, true);
365 			result = dns_name_towire(&name, cctx, target, NULL);
366 			if (result != ISC_R_SUCCESS) {
367 				goto rollback;
368 			}
369 
370 			/*
371 			 * See if we have space for type, class, ttl, and
372 			 * rdata length.  Write the type, class, and ttl.
373 			 */
374 			isc_buffer_availableregion(target, &tavailable);
375 			if (tavailable.length < 10) {
376 				result = ISC_R_NOSPACE;
377 				goto rollback;
378 			}
379 			isc_buffer_putuint16(target, type);
380 			isc_buffer_putuint16(target, rdataset->rdclass);
381 			isc_buffer_putuint32(target, rdataset->ttl);
382 
383 			/*
384 			 * Save space for rdata length.
385 			 */
386 			rdlen = *target;
387 			isc_buffer_add(target, 2);
388 
389 			/*
390 			 * Write the rdata.
391 			 */
392 			result = dns_rdata_towire(&rdata, cctx, target);
393 			if (result != ISC_R_SUCCESS) {
394 				goto rollback;
395 			}
396 
397 			/*
398 			 * Set the rdata length field to the compressed
399 			 * length.
400 			 */
401 			INSIST((target->used >= rdlen.used + 2) &&
402 			       (target->used - rdlen.used - 2 < 65536));
403 			isc_buffer_putuint16(
404 				&rdlen,
405 				(uint16_t)(target->used - rdlen.used - 2));
406 
407 			count++;
408 		}
409 		INSIST(isc_buffer_remaininglength(&source) == 0);
410 		result = dns_rdataset_next(rdataset);
411 		dns_rdata_reset(&rdata);
412 	}
413 	if (result != ISC_R_NOMORE) {
414 		goto rollback;
415 	}
416 
417 	*countp = count;
418 
419 	return ISC_R_SUCCESS;
420 
421 rollback:
422 	dns_compress_rollback(cctx, savedbuffer.used);
423 	*countp = 0;
424 	*target = savedbuffer;
425 
426 	return result;
427 }
428 
429 static void
430 rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) {
431 	UNUSED(rdataset);
432 }
433 
434 static isc_result_t
435 rdataset_first(dns_rdataset_t *rdataset) {
436 	unsigned char *raw;
437 	unsigned int count;
438 
439 	raw = rdataset->ncache.raw;
440 	count = raw[0] * 256 + raw[1];
441 	if (count == 0) {
442 		rdataset->ncache.iter_pos = NULL;
443 		return ISC_R_NOMORE;
444 	}
445 	/*
446 	 * iter_count is the number of rdata beyond the cursor position,
447 	 * so we decrement the total count by one before storing it.
448 	 */
449 	rdataset->ncache.iter_pos = raw + 2;
450 	rdataset->ncache.iter_count = count - 1;
451 	return ISC_R_SUCCESS;
452 }
453 
454 static isc_result_t
455 rdataset_next(dns_rdataset_t *rdataset) {
456 	unsigned int count;
457 	unsigned int length;
458 	unsigned char *raw;
459 
460 	raw = rdataset->ncache.iter_pos;
461 	count = rdataset->ncache.iter_count;
462 	if (count == 0) {
463 		rdataset->ncache.iter_pos = NULL;
464 		return ISC_R_NOMORE;
465 	}
466 
467 	length = raw[0] * 256 + raw[1];
468 	rdataset->ncache.iter_pos = raw + 2 + length;
469 	rdataset->ncache.iter_count = count - 1;
470 	return ISC_R_SUCCESS;
471 }
472 
473 static void
474 rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
475 	unsigned char *raw;
476 	isc_region_t r;
477 
478 	raw = rdataset->ncache.iter_pos;
479 	REQUIRE(raw != NULL);
480 
481 	r.length = raw[0] * 256 + raw[1];
482 	r.base = raw + 2;
483 	dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
484 }
485 
486 static void
487 rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) {
488 	*target = *source;
489 	target->ncache.iter_pos = NULL;
490 	target->ncache.iter_count = 0;
491 }
492 
493 static unsigned int
494 rdataset_count(dns_rdataset_t *rdataset) {
495 	unsigned char *raw;
496 	unsigned int count;
497 
498 	raw = rdataset->ncache.raw;
499 	count = raw[0] * 256 + raw[1];
500 
501 	return count;
502 }
503 
504 static void
505 rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
506 	atomic_uchar *raw;
507 
508 	raw = (atomic_uchar *)rdataset->ncache.raw;
509 	atomic_store_relaxed(&raw[-1], (unsigned char)trust);
510 	rdataset->trust = trust;
511 }
512 
513 static dns_rdatasetmethods_t rdataset_methods = {
514 	.disassociate = rdataset_disassociate,
515 	.first = rdataset_first,
516 	.next = rdataset_next,
517 	.current = rdataset_current,
518 	.clone = rdataset_clone,
519 	.count = rdataset_count,
520 	.settrust = rdataset_settrust,
521 };
522 
523 isc_result_t
524 dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
525 		       dns_rdatatype_t type, dns_rdataset_t *rdataset) {
526 	isc_result_t result;
527 	dns_rdata_t rdata = DNS_RDATA_INIT;
528 	isc_region_t remaining;
529 	isc_buffer_t source;
530 	dns_name_t tname;
531 	dns_rdatatype_t ttype;
532 	dns_trust_t trust = dns_trust_none;
533 	dns_rdataset_t rclone;
534 
535 	REQUIRE(ncacherdataset != NULL);
536 	REQUIRE(DNS_RDATASET_VALID(ncacherdataset));
537 	REQUIRE(ncacherdataset->type == 0);
538 	REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
539 	REQUIRE(name != NULL);
540 	REQUIRE(!dns_rdataset_isassociated(rdataset));
541 	REQUIRE(type != dns_rdatatype_rrsig);
542 
543 	dns_rdataset_init(&rclone);
544 	dns_rdataset_clone(ncacherdataset, &rclone);
545 	result = dns_rdataset_first(&rclone);
546 	while (result == ISC_R_SUCCESS) {
547 		dns_rdataset_current(&rclone, &rdata);
548 		isc_buffer_init(&source, rdata.data, rdata.length);
549 		isc_buffer_add(&source, rdata.length);
550 		dns_name_init(&tname, NULL);
551 		isc_buffer_remainingregion(&source, &remaining);
552 		dns_name_fromregion(&tname, &remaining);
553 		INSIST(remaining.length >= tname.length);
554 		isc_buffer_forward(&source, tname.length);
555 		remaining.length -= tname.length;
556 
557 		INSIST(remaining.length >= 3);
558 		ttype = isc_buffer_getuint16(&source);
559 
560 		if (ttype == type && dns_name_equal(&tname, name)) {
561 			trust = atomic_getuint8(&source);
562 			INSIST(trust <= dns_trust_ultimate);
563 			isc_buffer_remainingregion(&source, &remaining);
564 			break;
565 		}
566 		result = dns_rdataset_next(&rclone);
567 		dns_rdata_reset(&rdata);
568 	}
569 	dns_rdataset_disassociate(&rclone);
570 	if (result == ISC_R_NOMORE) {
571 		return ISC_R_NOTFOUND;
572 	}
573 	if (result != ISC_R_SUCCESS) {
574 		return result;
575 	}
576 
577 	INSIST(remaining.length != 0);
578 
579 	rdataset->methods = &rdataset_methods;
580 	rdataset->rdclass = ncacherdataset->rdclass;
581 	rdataset->type = type;
582 	rdataset->covers = 0;
583 	rdataset->ttl = ncacherdataset->ttl;
584 	rdataset->trust = trust;
585 	rdataset->ncache.raw = remaining.base;
586 	rdataset->ncache.iter_pos = NULL;
587 	rdataset->ncache.iter_count = 0;
588 
589 	return ISC_R_SUCCESS;
590 }
591 
592 isc_result_t
593 dns_ncache_getsigrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
594 			  dns_rdatatype_t covers, dns_rdataset_t *rdataset) {
595 	dns_name_t tname;
596 	dns_rdata_rrsig_t rrsig;
597 	dns_rdata_t rdata = DNS_RDATA_INIT;
598 	dns_rdataset_t rclone;
599 	dns_rdatatype_t type;
600 	dns_trust_t trust = dns_trust_none;
601 	isc_buffer_t source;
602 	isc_region_t remaining, sigregion;
603 	isc_result_t result;
604 	unsigned char *raw;
605 	unsigned int count;
606 
607 	REQUIRE(ncacherdataset != NULL);
608 	REQUIRE(ncacherdataset->type == 0);
609 	REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
610 	REQUIRE(name != NULL);
611 	REQUIRE(!dns_rdataset_isassociated(rdataset));
612 
613 	dns_rdataset_init(&rclone);
614 	dns_rdataset_clone(ncacherdataset, &rclone);
615 	result = dns_rdataset_first(&rclone);
616 	while (result == ISC_R_SUCCESS) {
617 		dns_rdataset_current(&rclone, &rdata);
618 		isc_buffer_init(&source, rdata.data, rdata.length);
619 		isc_buffer_add(&source, rdata.length);
620 		dns_name_init(&tname, NULL);
621 		isc_buffer_remainingregion(&source, &remaining);
622 		dns_name_fromregion(&tname, &remaining);
623 		INSIST(remaining.length >= tname.length);
624 		isc_buffer_forward(&source, tname.length);
625 		isc_region_consume(&remaining, tname.length);
626 
627 		INSIST(remaining.length >= 2);
628 		type = isc_buffer_getuint16(&source);
629 		isc_region_consume(&remaining, 2);
630 
631 		if (type != dns_rdatatype_rrsig ||
632 		    !dns_name_equal(&tname, name))
633 		{
634 			result = dns_rdataset_next(&rclone);
635 			dns_rdata_reset(&rdata);
636 			continue;
637 		}
638 
639 		INSIST(remaining.length >= 1);
640 		trust = atomic_getuint8(&source);
641 		INSIST(trust <= dns_trust_ultimate);
642 		isc_region_consume(&remaining, 1);
643 
644 		raw = remaining.base;
645 		count = raw[0] * 256 + raw[1];
646 		INSIST(count > 0);
647 		raw += 2;
648 		sigregion.length = raw[0] * 256 + raw[1];
649 		raw += 2;
650 		sigregion.base = raw;
651 		dns_rdata_reset(&rdata);
652 		dns_rdata_fromregion(&rdata, rdataset->rdclass,
653 				     dns_rdatatype_rrsig, &sigregion);
654 		(void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
655 		if (rrsig.covered == covers) {
656 			isc_buffer_remainingregion(&source, &remaining);
657 			break;
658 		}
659 
660 		result = dns_rdataset_next(&rclone);
661 		dns_rdata_reset(&rdata);
662 	}
663 	dns_rdataset_disassociate(&rclone);
664 	if (result == ISC_R_NOMORE) {
665 		return ISC_R_NOTFOUND;
666 	}
667 	if (result != ISC_R_SUCCESS) {
668 		return result;
669 	}
670 
671 	INSIST(remaining.length != 0);
672 
673 	rdataset->methods = &rdataset_methods;
674 	rdataset->rdclass = ncacherdataset->rdclass;
675 	rdataset->type = dns_rdatatype_rrsig;
676 	rdataset->covers = covers;
677 	rdataset->ttl = ncacherdataset->ttl;
678 	rdataset->trust = trust;
679 	rdataset->ncache.raw = remaining.base;
680 	rdataset->ncache.iter_pos = NULL;
681 	rdataset->ncache.iter_count = 0;
682 
683 	return ISC_R_SUCCESS;
684 }
685 
686 void
687 dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
688 		   dns_rdataset_t *rdataset) {
689 	dns_rdata_t rdata = DNS_RDATA_INIT;
690 	dns_trust_t trust;
691 	isc_region_t remaining, sigregion;
692 	isc_buffer_t source;
693 	dns_name_t tname;
694 	dns_rdatatype_t type, covers;
695 	unsigned int count;
696 	dns_rdata_rrsig_t rrsig;
697 	unsigned char *raw;
698 
699 	REQUIRE(ncacherdataset != NULL);
700 	REQUIRE(ncacherdataset->type == 0);
701 	REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
702 	REQUIRE(found != NULL);
703 	REQUIRE(!dns_rdataset_isassociated(rdataset));
704 
705 	dns_rdataset_current(ncacherdataset, &rdata);
706 	isc_buffer_init(&source, rdata.data, rdata.length);
707 	isc_buffer_add(&source, rdata.length);
708 
709 	dns_name_init(&tname, NULL);
710 	isc_buffer_remainingregion(&source, &remaining);
711 	dns_name_fromregion(found, &remaining);
712 	INSIST(remaining.length >= found->length);
713 	isc_buffer_forward(&source, found->length);
714 	remaining.length -= found->length;
715 
716 	INSIST(remaining.length >= 5);
717 	type = isc_buffer_getuint16(&source);
718 	trust = atomic_getuint8(&source);
719 	INSIST(trust <= dns_trust_ultimate);
720 	isc_buffer_remainingregion(&source, &remaining);
721 
722 	covers = 0;
723 	if (type == dns_rdatatype_rrsig) {
724 		/*
725 		 * Extract covers from RRSIG.
726 		 */
727 		raw = remaining.base;
728 		count = raw[0] * 256 + raw[1];
729 		INSIST(count > 0);
730 		raw += 2;
731 		sigregion.length = raw[0] * 256 + raw[1];
732 		raw += 2;
733 		sigregion.base = raw;
734 		dns_rdata_reset(&rdata);
735 		dns_rdata_fromregion(&rdata, ncacherdataset->rdclass, type,
736 				     &sigregion);
737 		(void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
738 		covers = rrsig.covered;
739 	}
740 
741 	rdataset->methods = &rdataset_methods;
742 	rdataset->rdclass = ncacherdataset->rdclass;
743 	rdataset->type = type;
744 	rdataset->covers = covers;
745 	rdataset->ttl = ncacherdataset->ttl;
746 	rdataset->trust = trust;
747 	rdataset->ncache.raw = remaining.base;
748 	rdataset->ncache.iter_pos = NULL;
749 	rdataset->ncache.iter_count = 0;
750 }
751