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