145386Swpaul/*	$NetBSD: ncache.c,v 1.9 2024/02/21 22:52:07 christos Exp $	*/
245386Swpaul
345386Swpaul/*
445386Swpaul * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
545386Swpaul *
645386Swpaul * SPDX-License-Identifier: MPL-2.0
745386Swpaul *
845386Swpaul * This Source Code Form is subject to the terms of the Mozilla Public
945386Swpaul * License, v. 2.0. If a copy of the MPL was not distributed with this
1045386Swpaul * file, you can obtain one at https://mozilla.org/MPL/2.0/.
1145386Swpaul *
1245386Swpaul * See the COPYRIGHT file distributed with this work for additional
1345386Swpaul * information regarding copyright ownership.
1445386Swpaul */
1545386Swpaul
1645386Swpaul/*! \file */
1745386Swpaul
1845386Swpaul#include <inttypes.h>
1945386Swpaul#include <stdbool.h>
2045386Swpaul
2145386Swpaul#include <isc/buffer.h>
2245386Swpaul#include <isc/util.h>
2345386Swpaul
2445386Swpaul#include <dns/db.h>
2545386Swpaul#include <dns/message.h>
2645386Swpaul#include <dns/ncache.h>
2745386Swpaul#include <dns/rdata.h>
2845386Swpaul#include <dns/rdatalist.h>
2945386Swpaul#include <dns/rdataset.h>
3045386Swpaul#include <dns/rdatastruct.h>
3145386Swpaul
3245386Swpaul#define DNS_NCACHE_RDATA 100U
3345386Swpaul
3445386Swpaul/*
3545386Swpaul * The format of an ncache rdata is a sequence of zero or more records of
3645386Swpaul * the following format:
3745386Swpaul *
3845386Swpaul *	owner name
3945386Swpaul *	type
4045386Swpaul *	trust
4145386Swpaul *	rdata count
4245386Swpaul *		rdata length			These two occur 'rdata count'
4345386Swpaul *		rdata				times.
4445386Swpaul *
4545386Swpaul */
4645386Swpaul
4745386Swpaulstatic isc_result_t
4845386Swpauladdoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
4945386Swpaul	  dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl,
5045386Swpaul	  dns_ttl_t maxttl, bool optout, bool secure,
5145386Swpaul	  dns_rdataset_t *addedrdataset);
5245386Swpaul
5345386Swpaulstatic isc_result_t
5445386Swpaulcopy_rdataset(dns_rdataset_t *rdataset, isc_buffer_t *buffer) {
5545386Swpaul	isc_result_t result;
5645386Swpaul	unsigned int count;
5745386Swpaul	isc_region_t ar, r;
5845386Swpaul	dns_rdata_t rdata = DNS_RDATA_INIT;
5945386Swpaul
6045386Swpaul	/*
6145386Swpaul	 * Copy the rdataset count to the buffer.
6245386Swpaul	 */
6345386Swpaul	isc_buffer_availableregion(buffer, &ar);
6445386Swpaul	if (ar.length < 2) {
6545386Swpaul		return (ISC_R_NOSPACE);
6645386Swpaul	}
6745386Swpaul	count = dns_rdataset_count(rdataset);
6845386Swpaul	INSIST(count <= 65535);
6945386Swpaul	isc_buffer_putuint16(buffer, (uint16_t)count);
7045386Swpaul
7145386Swpaul	result = dns_rdataset_first(rdataset);
7245386Swpaul	while (result == ISC_R_SUCCESS) {
7345386Swpaul		dns_rdataset_current(rdataset, &rdata);
7445386Swpaul		dns_rdata_toregion(&rdata, &r);
7545386Swpaul		INSIST(r.length <= 65535);
7645386Swpaul		isc_buffer_availableregion(buffer, &ar);
7745386Swpaul		if (ar.length < 2) {
7845386Swpaul			return (ISC_R_NOSPACE);
7945386Swpaul		}
8045386Swpaul		/*
8145386Swpaul		 * Copy the rdata length to the buffer.
8245386Swpaul		 */
8345386Swpaul		isc_buffer_putuint16(buffer, (uint16_t)r.length);
8445386Swpaul		/*
8545386Swpaul		 * Copy the rdata to the buffer.
8645386Swpaul		 */
8745386Swpaul		result = isc_buffer_copyregion(buffer, &r);
8845386Swpaul		if (result != ISC_R_SUCCESS) {
8945386Swpaul			return (result);
9045386Swpaul		}
9145386Swpaul		dns_rdata_reset(&rdata);
9245386Swpaul		result = dns_rdataset_next(rdataset);
9345386Swpaul	}
9445386Swpaul	if (result != ISC_R_NOMORE) {
9545386Swpaul		return (result);
9645386Swpaul	}
9745386Swpaul
9845386Swpaul	return (ISC_R_SUCCESS);
9945386Swpaul}
10045386Swpaul
10145386Swpaulisc_result_t
10245386Swpauldns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
10345386Swpaul	       dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl,
10445386Swpaul	       dns_ttl_t maxttl, dns_rdataset_t *addedrdataset) {
10545386Swpaul	return (addoptout(message, cache, node, covers, now, minttl, maxttl,
10645386Swpaul			  false, false, addedrdataset));
10745386Swpaul}
10845386Swpaul
10945386Swpaulisc_result_t
11045386Swpauldns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
11145386Swpaul		     dns_dbnode_t *node, dns_rdatatype_t covers,
11245386Swpaul		     isc_stdtime_t now, dns_ttl_t minttl, dns_ttl_t maxttl,
11345386Swpaul		     bool optout, dns_rdataset_t *addedrdataset) {
11445386Swpaul	return (addoptout(message, cache, node, covers, now, minttl, maxttl,
11545386Swpaul			  optout, true, addedrdataset));
11645386Swpaul}
11745386Swpaul
11845386Swpaulstatic isc_result_t
11945386Swpauladdoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
12045386Swpaul	  dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl,
12145386Swpaul	  dns_ttl_t maxttl, bool optout, bool secure,
12245386Swpaul	  dns_rdataset_t *addedrdataset) {
12345386Swpaul	isc_result_t result;
12445386Swpaul	isc_buffer_t buffer;
12545386Swpaul	isc_region_t r;
12645386Swpaul	dns_rdataset_t *rdataset;
12745386Swpaul	dns_rdatatype_t type;
12845386Swpaul	dns_name_t *name;
12945386Swpaul	dns_ttl_t ttl;
13045386Swpaul	dns_trust_t trust;
13145386Swpaul	dns_rdata_t rdata[DNS_NCACHE_RDATA];
13245386Swpaul	dns_rdataset_t ncrdataset;
13345386Swpaul	dns_rdatalist_t ncrdatalist;
13445386Swpaul	unsigned char data[65536];
13545386Swpaul	unsigned int next = 0;
13645386Swpaul
13745386Swpaul	/*
13845386Swpaul	 * Convert the authority data from 'message' into a negative cache
13945386Swpaul	 * rdataset, and store it in 'cache' at 'node'.
14045386Swpaul	 */
14145386Swpaul
14245386Swpaul	REQUIRE(message != NULL);
14345386Swpaul
14445386Swpaul	/*
14545386Swpaul	 * We assume that all data in the authority section has been
14645386Swpaul	 * validated by the caller.
14745386Swpaul	 */
14845386Swpaul
14945386Swpaul	/*
15045386Swpaul	 * Initialize the list.
15145386Swpaul	 */
15245386Swpaul	dns_rdatalist_init(&ncrdatalist);
15345386Swpaul	ncrdatalist.rdclass = dns_db_class(cache);
15445386Swpaul	ncrdatalist.covers = covers;
15545386Swpaul	ncrdatalist.ttl = maxttl;
15645386Swpaul
15745386Swpaul	/*
15845386Swpaul	 * Build an ncache rdatas into buffer.
15945386Swpaul	 */
16045386Swpaul	ttl = maxttl;
16145386Swpaul	trust = 0xffff;
16245386Swpaul	isc_buffer_init(&buffer, data, sizeof(data));
16345386Swpaul	if (message->counts[DNS_SECTION_AUTHORITY]) {
16445386Swpaul		result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
16545386Swpaul	} else {
16645386Swpaul		result = ISC_R_NOMORE;
16745386Swpaul	}
16845386Swpaul	while (result == ISC_R_SUCCESS) {
16945386Swpaul		name = NULL;
17045386Swpaul		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
17145386Swpaul		if ((name->attributes & DNS_NAMEATTR_NCACHE) != 0) {
17245386Swpaul			for (rdataset = ISC_LIST_HEAD(name->list);
17345386Swpaul			     rdataset != NULL;
17445386Swpaul			     rdataset = ISC_LIST_NEXT(rdataset, link))
17545386Swpaul			{
17645386Swpaul				if ((rdataset->attributes &
17745386Swpaul				     DNS_RDATASETATTR_NCACHE) == 0)
17845386Swpaul				{
17945386Swpaul					continue;
18045386Swpaul				}
18145386Swpaul				type = rdataset->type;
18245386Swpaul				if (type == dns_rdatatype_rrsig) {
18345386Swpaul					type = rdataset->covers;
18445386Swpaul				}
18545386Swpaul				if (type == dns_rdatatype_soa ||
18645386Swpaul				    type == dns_rdatatype_nsec ||
18745386Swpaul				    type == dns_rdatatype_nsec3)
18845386Swpaul				{
18945386Swpaul					if (ttl > rdataset->ttl) {
19045386Swpaul						ttl = rdataset->ttl;
19145386Swpaul					}
19245386Swpaul					if (ttl < minttl) {
19345386Swpaul						ttl = minttl;
19445386Swpaul					}
19545386Swpaul					if (trust > rdataset->trust) {
19645386Swpaul						trust = rdataset->trust;
19745386Swpaul					}
19845386Swpaul					/*
19945386Swpaul					 * Copy the owner name to the buffer.
20045386Swpaul					 */
20145386Swpaul					dns_name_toregion(name, &r);
20245386Swpaul					result = isc_buffer_copyregion(&buffer,
20345386Swpaul								       &r);
20445386Swpaul					if (result != ISC_R_SUCCESS) {
20545386Swpaul						return (result);
20645386Swpaul					}
20745386Swpaul					/*
20845386Swpaul					 * Copy the type to the buffer.
20945386Swpaul					 */
21045386Swpaul					isc_buffer_availableregion(&buffer, &r);
21145386Swpaul					if (r.length < 3) {
21245386Swpaul						return (ISC_R_NOSPACE);
21345386Swpaul					}
21445386Swpaul					isc_buffer_putuint16(&buffer,
21545386Swpaul							     rdataset->type);
21645386Swpaul					isc_buffer_putuint8(
21745386Swpaul						&buffer,
21845386Swpaul						(unsigned char)rdataset->trust);
21945386Swpaul					/*
22045386Swpaul					 * Copy the rdataset into the buffer.
22145386Swpaul					 */
22245386Swpaul					result = copy_rdataset(rdataset,
22345386Swpaul							       &buffer);
22445386Swpaul					if (result != ISC_R_SUCCESS) {
22545386Swpaul						return (result);
22645386Swpaul					}
22745386Swpaul
22845386Swpaul					if (next >= DNS_NCACHE_RDATA) {
22945386Swpaul						return (ISC_R_NOSPACE);
23045386Swpaul					}
23145386Swpaul					dns_rdata_init(&rdata[next]);
23245386Swpaul					isc_buffer_remainingregion(&buffer, &r);
23345386Swpaul					rdata[next].data = r.base;
23445386Swpaul					rdata[next].length = r.length;
23545386Swpaul					rdata[next].rdclass =
23645386Swpaul						ncrdatalist.rdclass;
23745386Swpaul					rdata[next].type = 0;
23845386Swpaul					rdata[next].flags = 0;
23945386Swpaul					ISC_LIST_APPEND(ncrdatalist.rdata,
24045386Swpaul							&rdata[next], link);
24145386Swpaul					isc_buffer_forward(&buffer, r.length);
24245386Swpaul					next++;
24345386Swpaul				}
24445386Swpaul			}
24545386Swpaul		}
24645386Swpaul		result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
24745386Swpaul	}
24845386Swpaul	if (result != ISC_R_NOMORE) {
24945386Swpaul		return (result);
25045386Swpaul	}
25145386Swpaul
25245386Swpaul	if (trust == 0xffff) {
25345386Swpaul		if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 &&
25445386Swpaul		    message->counts[DNS_SECTION_ANSWER] == 0)
25545386Swpaul		{
25645386Swpaul			/*
25745386Swpaul			 * The response has aa set and we haven't followed
25845386Swpaul			 * any CNAME or DNAME chains.
25945386Swpaul			 */
26045386Swpaul			trust = dns_trust_authauthority;
26145386Swpaul		} else {
26245386Swpaul			trust = dns_trust_additional;
26345386Swpaul		}
26445386Swpaul		ttl = 0;
26545386Swpaul	}
26645386Swpaul
26745386Swpaul	INSIST(trust != 0xffff);
26845386Swpaul
26945386Swpaul	ncrdatalist.ttl = ttl;
27045386Swpaul
27145386Swpaul	dns_rdataset_init(&ncrdataset);
27245386Swpaul	RUNTIME_CHECK(dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset) ==
27345386Swpaul		      ISC_R_SUCCESS);
27445386Swpaul	if (!secure && trust > dns_trust_answer) {
27545386Swpaul		trust = dns_trust_answer;
27645386Swpaul	}
27745386Swpaul	ncrdataset.trust = trust;
27845386Swpaul	ncrdataset.attributes |= DNS_RDATASETATTR_NEGATIVE;
27945386Swpaul	if (message->rcode == dns_rcode_nxdomain) {
28045386Swpaul		ncrdataset.attributes |= DNS_RDATASETATTR_NXDOMAIN;
28145386Swpaul	}
28245386Swpaul	if (optout) {
28345386Swpaul		ncrdataset.attributes |= DNS_RDATASETATTR_OPTOUT;
28445386Swpaul	}
28545386Swpaul
28645386Swpaul	return (dns_db_addrdataset(cache, node, NULL, now, &ncrdataset, 0,
28745386Swpaul				   addedrdataset));
28845386Swpaul}
28945386Swpaul
29045386Swpaulisc_result_t
29145386Swpauldns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
29245386Swpaul		  isc_buffer_t *target, unsigned int options,
29345386Swpaul		  unsigned int *countp) {
29445386Swpaul	dns_rdata_t rdata = DNS_RDATA_INIT;
29545386Swpaul	isc_result_t result;
29645386Swpaul	isc_region_t remaining, tavailable;
29745386Swpaul	isc_buffer_t source, savedbuffer, rdlen;
29845386Swpaul	dns_name_t name;
29945386Swpaul	dns_rdatatype_t type;
30045386Swpaul	unsigned int i, rcount, count;
30145386Swpaul
30245386Swpaul	/*
30345386Swpaul	 * Convert the negative caching rdataset 'rdataset' to wire format,
30445386Swpaul	 * compressing names as specified in 'cctx', and storing the result in
30545386Swpaul	 * 'target'.
30645386Swpaul	 */
30745386Swpaul
30845386Swpaul	REQUIRE(rdataset != NULL);
30945386Swpaul	REQUIRE(rdataset->type == 0);
31045386Swpaul	REQUIRE((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
31145386Swpaul
31245386Swpaul	savedbuffer = *target;
31345386Swpaul	count = 0;
31445386Swpaul
31545386Swpaul	result = dns_rdataset_first(rdataset);
31645386Swpaul	while (result == ISC_R_SUCCESS) {
31745386Swpaul		dns_rdataset_current(rdataset, &rdata);
31845386Swpaul		isc_buffer_init(&source, rdata.data, rdata.length);
31945386Swpaul		isc_buffer_add(&source, rdata.length);
32045386Swpaul		dns_name_init(&name, NULL);
32145386Swpaul		isc_buffer_remainingregion(&source, &remaining);
32245386Swpaul		dns_name_fromregion(&name, &remaining);
32345386Swpaul		INSIST(remaining.length >= name.length);
32445386Swpaul		isc_buffer_forward(&source, name.length);
32545386Swpaul		remaining.length -= name.length;
32645386Swpaul
32745386Swpaul		INSIST(remaining.length >= 5);
32845386Swpaul		type = isc_buffer_getuint16(&source);
32945386Swpaul		isc_buffer_forward(&source, 1);
33045386Swpaul		rcount = isc_buffer_getuint16(&source);
33145386Swpaul
33245386Swpaul		for (i = 0; i < rcount; i++) {
33345386Swpaul			/*
33445386Swpaul			 * Get the length of this rdata and set up an
33545386Swpaul			 * rdata structure for it.
33645386Swpaul			 */
33745386Swpaul			isc_buffer_remainingregion(&source, &remaining);
33845386Swpaul			INSIST(remaining.length >= 2);
33945386Swpaul			dns_rdata_reset(&rdata);
34045386Swpaul			rdata.length = isc_buffer_getuint16(&source);
34145386Swpaul			isc_buffer_remainingregion(&source, &remaining);
34245386Swpaul			rdata.data = remaining.base;
34345386Swpaul			rdata.type = type;
34445386Swpaul			rdata.rdclass = rdataset->rdclass;
34545386Swpaul			INSIST(remaining.length >= rdata.length);
34645386Swpaul			isc_buffer_forward(&source, rdata.length);
34745386Swpaul
34845386Swpaul			if ((options & DNS_NCACHETOWIRE_OMITDNSSEC) != 0 &&
34945386Swpaul			    dns_rdatatype_isdnssec(type))
35045386Swpaul			{
35145386Swpaul				continue;
35245386Swpaul			}
35345386Swpaul
35445386Swpaul			/*
35545386Swpaul			 * Write the name.
35645386Swpaul			 */
35745386Swpaul			dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
35845386Swpaul			result = dns_name_towire(&name, cctx, target);
35945386Swpaul			if (result != ISC_R_SUCCESS) {
36045386Swpaul				goto rollback;
36145386Swpaul			}
36245386Swpaul
36345386Swpaul			/*
36445386Swpaul			 * See if we have space for type, class, ttl, and
36545386Swpaul			 * rdata length.  Write the type, class, and ttl.
36645386Swpaul			 */
36745386Swpaul			isc_buffer_availableregion(target, &tavailable);
36845386Swpaul			if (tavailable.length < 10) {
36945386Swpaul				result = ISC_R_NOSPACE;
37045386Swpaul				goto rollback;
37145386Swpaul			}
37245386Swpaul			isc_buffer_putuint16(target, type);
37345386Swpaul			isc_buffer_putuint16(target, rdataset->rdclass);
37445386Swpaul			isc_buffer_putuint32(target, rdataset->ttl);
37545386Swpaul
37645386Swpaul			/*
37745386Swpaul			 * Save space for rdata length.
37845386Swpaul			 */
37945386Swpaul			rdlen = *target;
38045386Swpaul			isc_buffer_add(target, 2);
38145386Swpaul
38245386Swpaul			/*
38345386Swpaul			 * Write the rdata.
38445386Swpaul			 */
38545386Swpaul			result = dns_rdata_towire(&rdata, cctx, target);
38645386Swpaul			if (result != ISC_R_SUCCESS) {
38745386Swpaul				goto rollback;
38845386Swpaul			}
38945386Swpaul
39045386Swpaul			/*
39145386Swpaul			 * Set the rdata length field to the compressed
39245386Swpaul			 * length.
39345386Swpaul			 */
39445386Swpaul			INSIST((target->used >= rdlen.used + 2) &&
39545386Swpaul			       (target->used - rdlen.used - 2 < 65536));
39645386Swpaul			isc_buffer_putuint16(
39745386Swpaul				&rdlen,
39845386Swpaul				(uint16_t)(target->used - rdlen.used - 2));
39945386Swpaul
40045386Swpaul			count++;
40145386Swpaul		}
40245386Swpaul		INSIST(isc_buffer_remaininglength(&source) == 0);
40345386Swpaul		result = dns_rdataset_next(rdataset);
40445386Swpaul		dns_rdata_reset(&rdata);
40545386Swpaul	}
40645386Swpaul	if (result != ISC_R_NOMORE) {
40745386Swpaul		goto rollback;
40845386Swpaul	}
40945386Swpaul
41045386Swpaul	*countp = count;
41145386Swpaul
41245386Swpaul	return (ISC_R_SUCCESS);
41345386Swpaul
41445386Swpaulrollback:
41545386Swpaul	INSIST(savedbuffer.used < 65536);
41645386Swpaul	dns_compress_rollback(cctx, (uint16_t)savedbuffer.used);
41745386Swpaul	*countp = 0;
41845386Swpaul	*target = savedbuffer;
41945386Swpaul
42045386Swpaul	return (result);
42145386Swpaul}
42245386Swpaul
42345386Swpaulstatic void
42445386Swpaulrdataset_disassociate(dns_rdataset_t *rdataset) {
42545386Swpaul	UNUSED(rdataset);
42645386Swpaul}
42745386Swpaul
42845386Swpaulstatic isc_result_t
42945386Swpaulrdataset_first(dns_rdataset_t *rdataset) {
43045386Swpaul	unsigned char *raw = rdataset->private3;
43145386Swpaul	unsigned int count;
43245386Swpaul
43345386Swpaul	count = raw[0] * 256 + raw[1];
43445386Swpaul	if (count == 0) {
43545386Swpaul		rdataset->private5 = NULL;
43645386Swpaul		return (ISC_R_NOMORE);
43745386Swpaul	}
43845386Swpaul	raw += 2;
43945386Swpaul	/*
44045386Swpaul	 * The privateuint4 field is the number of rdata beyond the cursor
44145386Swpaul	 * position, so we decrement the total count by one before storing
44245386Swpaul	 * it.
44345386Swpaul	 */
44445386Swpaul	count--;
44545386Swpaul	rdataset->privateuint4 = count;
44645386Swpaul	rdataset->private5 = raw;
44745386Swpaul
44845386Swpaul	return (ISC_R_SUCCESS);
44945386Swpaul}
45045386Swpaul
45145386Swpaulstatic isc_result_t
45245386Swpaulrdataset_next(dns_rdataset_t *rdataset) {
45345386Swpaul	unsigned int count;
45445386Swpaul	unsigned int length;
45545386Swpaul	unsigned char *raw;
45645386Swpaul
45745386Swpaul	count = rdataset->privateuint4;
45845386Swpaul	if (count == 0) {
45945386Swpaul		return (ISC_R_NOMORE);
46045386Swpaul	}
46145386Swpaul	count--;
46245386Swpaul	rdataset->privateuint4 = count;
46345386Swpaul	raw = rdataset->private5;
46445386Swpaul	length = raw[0] * 256 + raw[1];
46545386Swpaul	raw += length + 2;
46645386Swpaul	rdataset->private5 = raw;
46745386Swpaul
46845386Swpaul	return (ISC_R_SUCCESS);
46945386Swpaul}
47045386Swpaul
47145386Swpaulstatic void
47245386Swpaulrdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
47345386Swpaul	unsigned char *raw = rdataset->private5;
47445386Swpaul	isc_region_t r;
47545386Swpaul
47645386Swpaul	REQUIRE(raw != NULL);
47745386Swpaul
47845386Swpaul	r.length = raw[0] * 256 + raw[1];
47945386Swpaul	raw += 2;
48045386Swpaul	r.base = raw;
48145386Swpaul	dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
48245386Swpaul}
48345386Swpaul
48445386Swpaulstatic void
48545386Swpaulrdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
48645386Swpaul	*target = *source;
48745386Swpaul
48845386Swpaul	/*
48945386Swpaul	 * Reset iterator state.
49045386Swpaul	 */
49145386Swpaul	target->privateuint4 = 0;
49245386Swpaul	target->private5 = NULL;
49345386Swpaul}
49445386Swpaul
49545386Swpaulstatic unsigned int
49645386Swpaulrdataset_count(dns_rdataset_t *rdataset) {
49745386Swpaul	unsigned char *raw = rdataset->private3;
49845386Swpaul	unsigned int count;
49945386Swpaul
50045386Swpaul	count = raw[0] * 256 + raw[1];
50145386Swpaul
50245386Swpaul	return (count);
50345386Swpaul}
50445386Swpaul
50545386Swpaulstatic void
50645386Swpaulrdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
50745386Swpaul	unsigned char *raw = rdataset->private3;
50845386Swpaul
50945386Swpaul	raw[-1] = (unsigned char)trust;
51045386Swpaul	rdataset->trust = trust;
51145386Swpaul}
51245386Swpaul
51345386Swpaulstatic dns_rdatasetmethods_t rdataset_methods = {
51445386Swpaul	rdataset_disassociate,
51545386Swpaul	rdataset_first,
51645386Swpaul	rdataset_next,
51745386Swpaul	rdataset_current,
51845386Swpaul	rdataset_clone,
51945386Swpaul	rdataset_count,
52045386Swpaul	NULL,		   /* addnoqname */
52145386Swpaul	NULL,		   /* getnoqname */
52245386Swpaul	NULL,		   /* addclosest */
52345386Swpaul	NULL,		   /* getclosest */
52445386Swpaul	rdataset_settrust, /* settrust */
52545386Swpaul	NULL,		   /* expire */
52645386Swpaul	NULL,		   /* clearprefetch */
52745386Swpaul	NULL,		   /* setownercase */
52845386Swpaul	NULL,		   /* getownercase */
52945386Swpaul	NULL		   /* addglue */
53045386Swpaul};
53145386Swpaul
53245386Swpaulisc_result_t
53345386Swpauldns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
53445386Swpaul		       dns_rdatatype_t type, dns_rdataset_t *rdataset) {
53545386Swpaul	isc_result_t result;
53645386Swpaul	dns_rdata_t rdata = DNS_RDATA_INIT;
53745386Swpaul	isc_region_t remaining;
53845386Swpaul	isc_buffer_t source;
53945386Swpaul	dns_name_t tname;
54045386Swpaul	dns_rdatatype_t ttype;
54145386Swpaul	dns_trust_t trust = dns_trust_none;
54245386Swpaul	dns_rdataset_t rclone;
54345386Swpaul
54445386Swpaul	REQUIRE(ncacherdataset != NULL);
54545386Swpaul	REQUIRE(ncacherdataset->type == 0);
54645386Swpaul	REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
54745386Swpaul	REQUIRE(name != NULL);
54845386Swpaul	REQUIRE(!dns_rdataset_isassociated(rdataset));
54945386Swpaul	REQUIRE(type != dns_rdatatype_rrsig);
55045386Swpaul
55145386Swpaul	dns_rdataset_init(&rclone);
55245386Swpaul	dns_rdataset_clone(ncacherdataset, &rclone);
55345386Swpaul	result = dns_rdataset_first(&rclone);
55445386Swpaul	while (result == ISC_R_SUCCESS) {
55545386Swpaul		dns_rdataset_current(&rclone, &rdata);
55645386Swpaul		isc_buffer_init(&source, rdata.data, rdata.length);
55745386Swpaul		isc_buffer_add(&source, rdata.length);
55845386Swpaul		dns_name_init(&tname, NULL);
55945386Swpaul		isc_buffer_remainingregion(&source, &remaining);
56045386Swpaul		dns_name_fromregion(&tname, &remaining);
56145386Swpaul		INSIST(remaining.length >= tname.length);
56245386Swpaul		isc_buffer_forward(&source, tname.length);
56345386Swpaul		remaining.length -= tname.length;
56445386Swpaul
56545386Swpaul		INSIST(remaining.length >= 3);
56645386Swpaul		ttype = isc_buffer_getuint16(&source);
56745386Swpaul
56845386Swpaul		if (ttype == type && dns_name_equal(&tname, name)) {
56945386Swpaul			trust = isc_buffer_getuint8(&source);
57045386Swpaul			INSIST(trust <= dns_trust_ultimate);
57145386Swpaul			isc_buffer_remainingregion(&source, &remaining);
57245386Swpaul			break;
57345386Swpaul		}
57445386Swpaul		result = dns_rdataset_next(&rclone);
57545386Swpaul		dns_rdata_reset(&rdata);
57645386Swpaul	}
57745386Swpaul	dns_rdataset_disassociate(&rclone);
57845386Swpaul	if (result == ISC_R_NOMORE) {
57945386Swpaul		return (ISC_R_NOTFOUND);
58045386Swpaul	}
58145386Swpaul	if (result != ISC_R_SUCCESS) {
58245386Swpaul		return (result);
58345386Swpaul	}
58445386Swpaul
58545386Swpaul	INSIST(remaining.length != 0);
58645386Swpaul
58745386Swpaul	rdataset->methods = &rdataset_methods;
58845386Swpaul	rdataset->rdclass = ncacherdataset->rdclass;
58945386Swpaul	rdataset->type = type;
59045386Swpaul	rdataset->covers = 0;
59145386Swpaul	rdataset->ttl = ncacherdataset->ttl;
59245386Swpaul	rdataset->trust = trust;
59345386Swpaul	rdataset->private1 = NULL;
59445386Swpaul	rdataset->private2 = NULL;
59545386Swpaul
59645386Swpaul	rdataset->private3 = remaining.base;
59745386Swpaul
59845386Swpaul	/*
59945386Swpaul	 * Reset iterator state.
60045386Swpaul	 */
60145386Swpaul	rdataset->privateuint4 = 0;
60245386Swpaul	rdataset->private5 = NULL;
60345386Swpaul	rdataset->private6 = NULL;
60445386Swpaul	return (ISC_R_SUCCESS);
60545386Swpaul}
60645386Swpaul
60745386Swpaulisc_result_t
60845386Swpauldns_ncache_getsigrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
60945386Swpaul			  dns_rdatatype_t covers, dns_rdataset_t *rdataset) {
61045386Swpaul	dns_name_t tname;
61145386Swpaul	dns_rdata_rrsig_t rrsig;
61245386Swpaul	dns_rdata_t rdata = DNS_RDATA_INIT;
61345386Swpaul	dns_rdataset_t rclone;
61445386Swpaul	dns_rdatatype_t type;
61545386Swpaul	dns_trust_t trust = dns_trust_none;
61645386Swpaul	isc_buffer_t source;
61745386Swpaul	isc_region_t remaining, sigregion;
61845386Swpaul	isc_result_t result;
61945386Swpaul	unsigned char *raw;
62045386Swpaul	unsigned int count;
62145386Swpaul
62245386Swpaul	REQUIRE(ncacherdataset != NULL);
62345386Swpaul	REQUIRE(ncacherdataset->type == 0);
62445386Swpaul	REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
62545386Swpaul	REQUIRE(name != NULL);
62645386Swpaul	REQUIRE(!dns_rdataset_isassociated(rdataset));
62745386Swpaul
62845386Swpaul	dns_rdataset_init(&rclone);
62945386Swpaul	dns_rdataset_clone(ncacherdataset, &rclone);
63045386Swpaul	result = dns_rdataset_first(&rclone);
63145386Swpaul	while (result == ISC_R_SUCCESS) {
63245386Swpaul		dns_rdataset_current(&rclone, &rdata);
63345386Swpaul		isc_buffer_init(&source, rdata.data, rdata.length);
63445386Swpaul		isc_buffer_add(&source, rdata.length);
63545386Swpaul		dns_name_init(&tname, NULL);
63645386Swpaul		isc_buffer_remainingregion(&source, &remaining);
63745386Swpaul		dns_name_fromregion(&tname, &remaining);
63845386Swpaul		INSIST(remaining.length >= tname.length);
63945386Swpaul		isc_buffer_forward(&source, tname.length);
64045386Swpaul		isc_region_consume(&remaining, tname.length);
64145386Swpaul
64245386Swpaul		INSIST(remaining.length >= 2);
64345386Swpaul		type = isc_buffer_getuint16(&source);
64445386Swpaul		isc_region_consume(&remaining, 2);
64545386Swpaul
64645386Swpaul		if (type != dns_rdatatype_rrsig ||
64745386Swpaul		    !dns_name_equal(&tname, name))
64845386Swpaul		{
64945386Swpaul			result = dns_rdataset_next(&rclone);
65045386Swpaul			dns_rdata_reset(&rdata);
65145386Swpaul			continue;
65245386Swpaul		}
65345386Swpaul
65445386Swpaul		INSIST(remaining.length >= 1);
65545386Swpaul		trust = isc_buffer_getuint8(&source);
65645386Swpaul		INSIST(trust <= dns_trust_ultimate);
65745386Swpaul		isc_region_consume(&remaining, 1);
65845386Swpaul
65945386Swpaul		raw = remaining.base;
66045386Swpaul		count = raw[0] * 256 + raw[1];
66145386Swpaul		INSIST(count > 0);
66245386Swpaul		raw += 2;
66345386Swpaul		sigregion.length = raw[0] * 256 + raw[1];
66445386Swpaul		raw += 2;
66545386Swpaul		sigregion.base = raw;
66645386Swpaul		dns_rdata_reset(&rdata);
66745386Swpaul		dns_rdata_fromregion(&rdata, rdataset->rdclass,
66845386Swpaul				     dns_rdatatype_rrsig, &sigregion);
66945386Swpaul		(void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
67045386Swpaul		if (rrsig.covered == covers) {
67145386Swpaul			isc_buffer_remainingregion(&source, &remaining);
67245386Swpaul			break;
67345386Swpaul		}
67445386Swpaul
67545386Swpaul		result = dns_rdataset_next(&rclone);
67645386Swpaul		dns_rdata_reset(&rdata);
67745386Swpaul	}
67845386Swpaul	dns_rdataset_disassociate(&rclone);
67945386Swpaul	if (result == ISC_R_NOMORE) {
68045386Swpaul		return (ISC_R_NOTFOUND);
68145386Swpaul	}
68245386Swpaul	if (result != ISC_R_SUCCESS) {
68345386Swpaul		return (result);
68445386Swpaul	}
68545386Swpaul
68645386Swpaul	INSIST(remaining.length != 0);
68745386Swpaul
68845386Swpaul	rdataset->methods = &rdataset_methods;
68945386Swpaul	rdataset->rdclass = ncacherdataset->rdclass;
69045386Swpaul	rdataset->type = dns_rdatatype_rrsig;
69145386Swpaul	rdataset->covers = covers;
69245386Swpaul	rdataset->ttl = ncacherdataset->ttl;
69345386Swpaul	rdataset->trust = trust;
69445386Swpaul	rdataset->private1 = NULL;
69545386Swpaul	rdataset->private2 = NULL;
69645386Swpaul
69745386Swpaul	rdataset->private3 = remaining.base;
69845386Swpaul
69945386Swpaul	/*
70045386Swpaul	 * Reset iterator state.
70145386Swpaul	 */
70245386Swpaul	rdataset->privateuint4 = 0;
70345386Swpaul	rdataset->private5 = NULL;
70445386Swpaul	rdataset->private6 = NULL;
70545386Swpaul	return (ISC_R_SUCCESS);
70645386Swpaul}
70745386Swpaul
70845386Swpaulvoid
70945386Swpauldns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
71045386Swpaul		   dns_rdataset_t *rdataset) {
71145386Swpaul	dns_rdata_t rdata = DNS_RDATA_INIT;
71245386Swpaul	dns_trust_t trust;
71345386Swpaul	isc_region_t remaining, sigregion;
71445386Swpaul	isc_buffer_t source;
71545386Swpaul	dns_name_t tname;
71645386Swpaul	dns_rdatatype_t type;
71745386Swpaul	unsigned int count;
71845386Swpaul	dns_rdata_rrsig_t rrsig;
71945386Swpaul	unsigned char *raw;
72045386Swpaul
72145386Swpaul	REQUIRE(ncacherdataset != NULL);
72245386Swpaul	REQUIRE(ncacherdataset->type == 0);
72345386Swpaul	REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
72445386Swpaul	REQUIRE(found != NULL);
72545386Swpaul	REQUIRE(!dns_rdataset_isassociated(rdataset));
72645386Swpaul
72745386Swpaul	dns_rdataset_current(ncacherdataset, &rdata);
72845386Swpaul	isc_buffer_init(&source, rdata.data, rdata.length);
72945386Swpaul	isc_buffer_add(&source, rdata.length);
73045386Swpaul
73145386Swpaul	dns_name_init(&tname, NULL);
73245386Swpaul	isc_buffer_remainingregion(&source, &remaining);
73345386Swpaul	dns_name_fromregion(found, &remaining);
73445386Swpaul	INSIST(remaining.length >= found->length);
73545386Swpaul	isc_buffer_forward(&source, found->length);
73645386Swpaul	remaining.length -= found->length;
73745386Swpaul
73845386Swpaul	INSIST(remaining.length >= 5);
73945386Swpaul	type = isc_buffer_getuint16(&source);
74045386Swpaul	trust = isc_buffer_getuint8(&source);
74145386Swpaul	INSIST(trust <= dns_trust_ultimate);
74245386Swpaul	isc_buffer_remainingregion(&source, &remaining);
74345386Swpaul
74445386Swpaul	rdataset->methods = &rdataset_methods;
74545386Swpaul	rdataset->rdclass = ncacherdataset->rdclass;
74645386Swpaul	rdataset->type = type;
74745386Swpaul	if (type == dns_rdatatype_rrsig) {
74845386Swpaul		/*
74945386Swpaul		 * Extract covers from RRSIG.
75045386Swpaul		 */
75145386Swpaul		raw = remaining.base;
75245386Swpaul		count = raw[0] * 256 + raw[1];
75345386Swpaul		INSIST(count > 0);
75445386Swpaul		raw += 2;
75545386Swpaul		sigregion.length = raw[0] * 256 + raw[1];
75645386Swpaul		raw += 2;
75745386Swpaul		sigregion.base = raw;
75845386Swpaul		dns_rdata_reset(&rdata);
75945386Swpaul		dns_rdata_fromregion(&rdata, ncacherdataset->rdclass, type,
76045386Swpaul				     &sigregion);
76145386Swpaul		(void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
76245386Swpaul		rdataset->covers = rrsig.covered;
76345386Swpaul	} else {
76445386Swpaul		rdataset->covers = 0;
76545386Swpaul	}
76645386Swpaul	rdataset->ttl = ncacherdataset->ttl;
76745386Swpaul	rdataset->trust = trust;
76845386Swpaul	rdataset->private1 = NULL;
76945386Swpaul	rdataset->private2 = NULL;
77045386Swpaul
77145386Swpaul	rdataset->private3 = remaining.base;
77245386Swpaul
77345386Swpaul	/*
77445386Swpaul	 * Reset iterator state.
77545386Swpaul	 */
77645386Swpaul	rdataset->privateuint4 = 0;
77745386Swpaul	rdataset->private5 = NULL;
77845386Swpaul	rdataset->private6 = NULL;
77945386Swpaul}
78045386Swpaul