xref: /netbsd-src/external/mpl/bind/dist/lib/dns/skr.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1*bcda20f6Schristos /*	$NetBSD: skr.c,v 1.2 2025/01/26 16:25:25 christos Exp $	*/
29689912eSchristos 
39689912eSchristos /*
49689912eSchristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
59689912eSchristos  *
69689912eSchristos  * SPDX-License-Identifier: MPL-2.0
79689912eSchristos  *
89689912eSchristos  * This Source Code Form is subject to the terms of the Mozilla Public
99689912eSchristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
109689912eSchristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
119689912eSchristos  *
129689912eSchristos  * See the COPYRIGHT file distributed with this work for additional
139689912eSchristos  * information regarding copyright ownership.
149689912eSchristos  */
159689912eSchristos 
169689912eSchristos /*! \file */
179689912eSchristos 
189689912eSchristos #include <isc/lex.h>
199689912eSchristos #include <isc/log.h>
209689912eSchristos 
219689912eSchristos #include <dns/callbacks.h>
229689912eSchristos #include <dns/fixedname.h>
239689912eSchristos #include <dns/rdata.h>
249689912eSchristos #include <dns/rdataclass.h>
259689912eSchristos #include <dns/rdatatype.h>
269689912eSchristos #include <dns/skr.h>
279689912eSchristos #include <dns/time.h>
289689912eSchristos #include <dns/ttl.h>
299689912eSchristos 
309689912eSchristos #define CHECK(op)                            \
319689912eSchristos 	do {                                 \
329689912eSchristos 		result = (op);               \
339689912eSchristos 		if (result != ISC_R_SUCCESS) \
349689912eSchristos 			goto failure;        \
359689912eSchristos 	} while (0)
369689912eSchristos 
379689912eSchristos #define READLINE(lex, opt, token)
389689912eSchristos 
399689912eSchristos #define NEXTTOKEN(lex, opt, token)                       \
409689912eSchristos 	{                                                \
419689912eSchristos 		ret = isc_lex_gettoken(lex, opt, token); \
429689912eSchristos 		if (ret != ISC_R_SUCCESS)                \
439689912eSchristos 			goto cleanup;                    \
449689912eSchristos 	}
459689912eSchristos 
469689912eSchristos #define BADTOKEN()                           \
479689912eSchristos 	{                                    \
489689912eSchristos 		ret = ISC_R_UNEXPECTEDTOKEN; \
499689912eSchristos 		goto cleanup;                \
509689912eSchristos 	}
519689912eSchristos 
529689912eSchristos #define TOKENSIZ (8 * 1024)
539689912eSchristos #define STR(t)	 ((t).value.as_textregion.base)
549689912eSchristos 
559689912eSchristos static isc_result_t
569689912eSchristos parse_rr(isc_lex_t *lex, isc_mem_t *mctx, char *owner, dns_name_t *origin,
579689912eSchristos 	 dns_rdataclass_t rdclass, isc_buffer_t *buf, dns_ttl_t *ttl,
589689912eSchristos 	 dns_rdatatype_t *rdtype, dns_rdata_t **rdata) {
599689912eSchristos 	dns_rdatacallbacks_t callbacks;
609689912eSchristos 	dns_fixedname_t dfname;
619689912eSchristos 	dns_name_t *dname = NULL;
629689912eSchristos 	dns_rdataclass_t clas;
639689912eSchristos 	isc_buffer_t b;
649689912eSchristos 	isc_token_t token;
659689912eSchristos 	unsigned int opt = ISC_LEXOPT_EOL;
669689912eSchristos 	isc_result_t ret = ISC_R_SUCCESS;
679689912eSchristos 
689689912eSchristos 	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
699689912eSchristos 
709689912eSchristos 	/* Read the domain name */
719689912eSchristos 	if (!strcmp(owner, "@")) {
729689912eSchristos 		BADTOKEN();
739689912eSchristos 	}
749689912eSchristos 	dname = dns_fixedname_initname(&dfname);
759689912eSchristos 	isc_buffer_init(&b, owner, strlen(owner));
769689912eSchristos 	isc_buffer_add(&b, strlen(owner));
779689912eSchristos 	ret = dns_name_fromtext(dname, &b, dns_rootname, 0, NULL);
789689912eSchristos 	if (ret != ISC_R_SUCCESS) {
799689912eSchristos 		return ret;
809689912eSchristos 	}
819689912eSchristos 	if (dns_name_compare(dname, origin) != 0) {
829689912eSchristos 		return DNS_R_BADOWNERNAME;
839689912eSchristos 	}
849689912eSchristos 	isc_buffer_clear(&b);
859689912eSchristos 
869689912eSchristos 	/* Read the next word: either TTL, class, or type */
879689912eSchristos 	NEXTTOKEN(lex, opt, &token);
889689912eSchristos 	if (token.type != isc_tokentype_string) {
899689912eSchristos 		BADTOKEN();
909689912eSchristos 	}
919689912eSchristos 
929689912eSchristos 	/* If it's a TTL, read the next one */
939689912eSchristos 	ret = dns_ttl_fromtext(&token.value.as_textregion, ttl);
949689912eSchristos 	if (ret == ISC_R_SUCCESS) {
959689912eSchristos 		NEXTTOKEN(lex, opt, &token);
969689912eSchristos 	}
979689912eSchristos 	if (token.type != isc_tokentype_string) {
989689912eSchristos 		BADTOKEN();
999689912eSchristos 	}
1009689912eSchristos 
1019689912eSchristos 	/* If it's a class, read the next one */
1029689912eSchristos 	ret = dns_rdataclass_fromtext(&clas, &token.value.as_textregion);
1039689912eSchristos 	if (ret == ISC_R_SUCCESS) {
1049689912eSchristos 		if (clas != rdclass) {
1059689912eSchristos 			BADTOKEN();
1069689912eSchristos 		}
1079689912eSchristos 		NEXTTOKEN(lex, opt, &token);
1089689912eSchristos 	}
1099689912eSchristos 	if (token.type != isc_tokentype_string) {
1109689912eSchristos 		BADTOKEN();
1119689912eSchristos 	}
1129689912eSchristos 
1139689912eSchristos 	/* Must be the record type */
1149689912eSchristos 	ret = dns_rdatatype_fromtext(rdtype, &token.value.as_textregion);
1159689912eSchristos 	if (ret != ISC_R_SUCCESS) {
1169689912eSchristos 		BADTOKEN();
1179689912eSchristos 	}
1189689912eSchristos 	switch (*rdtype) {
1199689912eSchristos 	case dns_rdatatype_dnskey:
1209689912eSchristos 	case dns_rdatatype_cdnskey:
1219689912eSchristos 	case dns_rdatatype_cds:
1229689912eSchristos 	case dns_rdatatype_rrsig:
1239689912eSchristos 		/* Allowed record types */
1249689912eSchristos 		break;
1259689912eSchristos 	default:
1269689912eSchristos 		BADTOKEN();
1279689912eSchristos 	}
1289689912eSchristos 
1299689912eSchristos 	dns_rdatacallbacks_init(&callbacks);
1309689912eSchristos 	ret = dns_rdata_fromtext(*rdata, rdclass, *rdtype, lex, dname, 0, mctx,
1319689912eSchristos 				 buf, &callbacks);
1329689912eSchristos cleanup:
1339689912eSchristos 	isc_lex_setcomments(lex, 0);
1349689912eSchristos 	return ret;
1359689912eSchristos }
1369689912eSchristos 
1379689912eSchristos static void
1389689912eSchristos skrbundle_create(isc_mem_t *mctx, isc_stdtime_t inception,
1399689912eSchristos 		 dns_skrbundle_t **bp) {
1409689912eSchristos 	dns_skrbundle_t *b;
1419689912eSchristos 
1429689912eSchristos 	REQUIRE(bp != NULL && *bp == NULL);
1439689912eSchristos 
1449689912eSchristos 	b = isc_mem_get(mctx, sizeof(*b));
1459689912eSchristos 	b->magic = DNS_SKRBUNDLE_MAGIC;
1469689912eSchristos 	b->inception = inception;
1479689912eSchristos 	dns_diff_init(mctx, &b->diff);
1489689912eSchristos 
1499689912eSchristos 	ISC_LINK_INIT(b, link);
1509689912eSchristos 
1519689912eSchristos 	*bp = b;
1529689912eSchristos }
1539689912eSchristos 
1549689912eSchristos static void
1559689912eSchristos skrbundle_addtuple(dns_skrbundle_t *bundle, dns_difftuple_t **tuple) {
1569689912eSchristos 	REQUIRE(DNS_DIFFTUPLE_VALID(*tuple));
1579689912eSchristos 	REQUIRE(DNS_SKRBUNDLE_VALID(bundle));
1589689912eSchristos 	REQUIRE(DNS_DIFF_VALID(&bundle->diff));
1599689912eSchristos 
1609689912eSchristos 	dns_diff_append(&bundle->diff, tuple);
1619689912eSchristos }
1629689912eSchristos 
1639689912eSchristos isc_result_t
1649689912eSchristos dns_skrbundle_getsig(dns_skrbundle_t *bundle, dst_key_t *key,
1659689912eSchristos 		     dns_rdatatype_t covering_type, dns_rdata_t *sigrdata) {
1669689912eSchristos 	isc_result_t result = ISC_R_SUCCESS;
1679689912eSchristos 
1689689912eSchristos 	REQUIRE(DNS_SKRBUNDLE_VALID(bundle));
1699689912eSchristos 	REQUIRE(DNS_DIFF_VALID(&bundle->diff));
1709689912eSchristos 
1719689912eSchristos 	dns_difftuple_t *tuple = ISC_LIST_HEAD(bundle->diff.tuples);
1729689912eSchristos 	while (tuple != NULL) {
1739689912eSchristos 		dns_rdata_rrsig_t rrsig;
1749689912eSchristos 
1759689912eSchristos 		if (tuple->op != DNS_DIFFOP_ADDRESIGN) {
1769689912eSchristos 			tuple = ISC_LIST_NEXT(tuple, link);
1779689912eSchristos 			continue;
1789689912eSchristos 		}
1799689912eSchristos 		INSIST(tuple->rdata.type == dns_rdatatype_rrsig);
1809689912eSchristos 
1819689912eSchristos 		result = dns_rdata_tostruct(&tuple->rdata, &rrsig, NULL);
1829689912eSchristos 		if (result != ISC_R_SUCCESS) {
1839689912eSchristos 			return result;
1849689912eSchristos 		}
1859689912eSchristos 
1869689912eSchristos 		/*
1879689912eSchristos 		 * Check if covering type matches, and if the signature is
1889689912eSchristos 		 * generated by 'key'.
1899689912eSchristos 		 */
1909689912eSchristos 		if (rrsig.covered == covering_type &&
1919689912eSchristos 		    rrsig.keyid == dst_key_id(key))
1929689912eSchristos 		{
1939689912eSchristos 			dns_rdata_clone(&tuple->rdata, sigrdata);
1949689912eSchristos 			return ISC_R_SUCCESS;
1959689912eSchristos 		}
1969689912eSchristos 
1979689912eSchristos 		tuple = ISC_LIST_NEXT(tuple, link);
1989689912eSchristos 	}
1999689912eSchristos 
2009689912eSchristos 	return ISC_R_NOTFOUND;
2019689912eSchristos }
2029689912eSchristos 
2039689912eSchristos void
2049689912eSchristos dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
2059689912eSchristos 	       dns_rdataclass_t rdclass, dns_skr_t **skrp) {
2069689912eSchristos 	isc_time_t now;
2079689912eSchristos 	dns_skr_t *skr = NULL;
2089689912eSchristos 
2099689912eSchristos 	REQUIRE(skrp != NULL && *skrp == NULL);
2109689912eSchristos 	REQUIRE(mctx != NULL);
2119689912eSchristos 
2129689912eSchristos 	UNUSED(origin);
2139689912eSchristos 	UNUSED(rdclass);
2149689912eSchristos 
2159689912eSchristos 	now = isc_time_now();
2169689912eSchristos 	skr = isc_mem_get(mctx, sizeof(*skr));
2179689912eSchristos 	*skr = (dns_skr_t){
2189689912eSchristos 		.magic = DNS_SKR_MAGIC,
2199689912eSchristos 		.filename = isc_mem_strdup(mctx, filename),
2209689912eSchristos 		.loadtime = now,
2219689912eSchristos 	};
2229689912eSchristos 	/*
2239689912eSchristos 	 * A list is not the best structure to store bundles that
2249689912eSchristos 	 * we need to look up, but we don't expect many bundles
2259689912eSchristos 	 * per SKR so it is acceptable for now.
2269689912eSchristos 	 */
2279689912eSchristos 	ISC_LIST_INIT(skr->bundles);
2289689912eSchristos 
2299689912eSchristos 	isc_mem_attach(mctx, &skr->mctx);
2309689912eSchristos 	isc_refcount_init(&skr->references, 1);
2319689912eSchristos 	*skrp = skr;
2329689912eSchristos }
2339689912eSchristos 
2349689912eSchristos static void
2359689912eSchristos addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) {
2369689912eSchristos 	REQUIRE(DNS_SKR_VALID(skr));
2379689912eSchristos 	REQUIRE(DNS_SKRBUNDLE_VALID(*bundlep));
2389689912eSchristos 
2399689912eSchristos 	ISC_LIST_APPEND(skr->bundles, *bundlep, link);
2409689912eSchristos 	*bundlep = NULL;
2419689912eSchristos }
2429689912eSchristos 
2439689912eSchristos isc_result_t
2449689912eSchristos dns_skr_read(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
2459689912eSchristos 	     dns_rdataclass_t rdclass, dns_ttl_t dnskeyttl, dns_skr_t **skrp) {
2469689912eSchristos 	isc_result_t result;
2479689912eSchristos 	dns_skrbundle_t *bundle = NULL;
2489689912eSchristos 	char bundlebuf[1024];
2499689912eSchristos 	uint32_t bundle_id;
2509689912eSchristos 	isc_lex_t *lex = NULL;
2519689912eSchristos 	isc_lexspecials_t specials;
2529689912eSchristos 	isc_token_t token;
2539689912eSchristos 	unsigned int opt = ISC_LEXOPT_EOL;
2549689912eSchristos 
2559689912eSchristos 	REQUIRE(DNS_SKR_VALID(*skrp));
2569689912eSchristos 
2579689912eSchristos 	isc_lex_create(mctx, TOKENSIZ, &lex);
2589689912eSchristos 	memset(specials, 0, sizeof(specials));
2599689912eSchristos 	specials['('] = 1;
2609689912eSchristos 	specials[')'] = 1;
2619689912eSchristos 	specials['"'] = 1;
2629689912eSchristos 	isc_lex_setspecials(lex, specials);
2639689912eSchristos 	result = isc_lex_openfile(lex, filename);
2649689912eSchristos 	if (result != ISC_R_SUCCESS) {
2659689912eSchristos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2669689912eSchristos 			      DNS_LOGMODULE_ZONE, ISC_LOG_ERROR,
2679689912eSchristos 			      "unable to open ksr file %s: %s", filename,
2689689912eSchristos 			      isc_result_totext(result));
2699689912eSchristos 		isc_lex_destroy(&lex);
2709689912eSchristos 		return result;
2719689912eSchristos 	}
2729689912eSchristos 
2739689912eSchristos 	for (result = isc_lex_gettoken(lex, opt, &token);
2749689912eSchristos 	     result == ISC_R_SUCCESS;
2759689912eSchristos 	     result = isc_lex_gettoken(lex, opt, &token))
2769689912eSchristos 	{
2779689912eSchristos 		if (token.type == isc_tokentype_eol) {
2789689912eSchristos 			continue;
2799689912eSchristos 		}
2809689912eSchristos 
2819689912eSchristos 		if (token.type != isc_tokentype_string) {
2829689912eSchristos 			CHECK(DNS_R_SYNTAX);
2839689912eSchristos 		}
2849689912eSchristos 
2859689912eSchristos 		if (strcmp(STR(token), ";;") == 0) {
2869689912eSchristos 			/* New bundle */
2879689912eSchristos 			CHECK(isc_lex_gettoken(lex, opt, &token));
2889689912eSchristos 			if (token.type != isc_tokentype_string ||
2899689912eSchristos 			    strcmp(STR(token), "SignedKeyResponse") != 0)
2909689912eSchristos 			{
2919689912eSchristos 				CHECK(DNS_R_SYNTAX);
2929689912eSchristos 			}
2939689912eSchristos 
2949689912eSchristos 			/* Version */
2959689912eSchristos 			CHECK(isc_lex_gettoken(lex, opt, &token));
2969689912eSchristos 			if (token.type != isc_tokentype_string ||
2979689912eSchristos 			    strcmp(STR(token), "1.0") != 0)
2989689912eSchristos 			{
2999689912eSchristos 				CHECK(DNS_R_SYNTAX);
3009689912eSchristos 			}
3019689912eSchristos 
3029689912eSchristos 			/* Date and time of bundle */
3039689912eSchristos 			CHECK(isc_lex_gettoken(lex, opt, &token));
3049689912eSchristos 			if (token.type != isc_tokentype_string) {
3059689912eSchristos 				CHECK(DNS_R_SYNTAX);
3069689912eSchristos 			}
3079689912eSchristos 			if (strcmp(STR(token), "generated") == 0) {
3089689912eSchristos 				/* Final bundle */
3099689912eSchristos 				goto readline;
3109689912eSchristos 			}
3119689912eSchristos 			if (token.type != isc_tokentype_string) {
3129689912eSchristos 				CHECK(DNS_R_SYNTAX);
3139689912eSchristos 			}
3149689912eSchristos 
3159689912eSchristos 			/* Add previous bundle */
3169689912eSchristos 			if (bundle != NULL) {
3179689912eSchristos 				addbundle(*skrp, &bundle);
3189689912eSchristos 			}
3199689912eSchristos 
3209689912eSchristos 			/* Create new bundle */
3219689912eSchristos 			sscanf(STR(token), "%s", bundlebuf);
3229689912eSchristos 			CHECK(dns_time32_fromtext(bundlebuf, &bundle_id));
3239689912eSchristos 			bundle = NULL;
3249689912eSchristos 			skrbundle_create(mctx, (isc_stdtime_t)bundle_id,
3259689912eSchristos 					 &bundle);
3269689912eSchristos 
3279689912eSchristos 		readline:
3289689912eSchristos 			/* Read remainder of header line */
3299689912eSchristos 			do {
3309689912eSchristos 				CHECK(isc_lex_gettoken(lex, opt, &token));
3319689912eSchristos 			} while (token.type != isc_tokentype_eol);
3329689912eSchristos 		} else {
3339689912eSchristos 			isc_buffer_t buf;
3349689912eSchristos 			dns_rdata_t *rdata = NULL;
3359689912eSchristos 			u_char rdatabuf[DST_KEY_MAXSIZE];
3369689912eSchristos 			dns_rdatatype_t rdtype;
3379689912eSchristos 
3389689912eSchristos 			/* Parse record */
3399689912eSchristos 			rdata = isc_mem_get(mctx, sizeof(*rdata));
3409689912eSchristos 			dns_rdata_init(rdata);
3419689912eSchristos 			isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf));
3429689912eSchristos 			result = parse_rr(lex, mctx, STR(token), origin,
3439689912eSchristos 					  rdclass, &buf, &dnskeyttl, &rdtype,
3449689912eSchristos 					  &rdata);
3459689912eSchristos 			if (result != ISC_R_SUCCESS) {
3469689912eSchristos 				isc_log_write(
3479689912eSchristos 					dns_lctx, DNS_LOGCATEGORY_GENERAL,
3489689912eSchristos 					DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1),
3499689912eSchristos 					"read skr file %s(%lu) parse rr "
3509689912eSchristos 					"failed: %s",
3519689912eSchristos 					filename, isc_lex_getsourceline(lex),
3529689912eSchristos 					isc_result_totext(result));
3539689912eSchristos 				isc_mem_put(mctx, rdata, sizeof(*rdata));
3549689912eSchristos 				goto failure;
3559689912eSchristos 			}
3569689912eSchristos 
3579689912eSchristos 			/* Create new diff tuple */
3589689912eSchristos 			dns_diffop_t op = (rdtype == dns_rdatatype_rrsig)
3599689912eSchristos 						  ? DNS_DIFFOP_ADDRESIGN
3609689912eSchristos 						  : DNS_DIFFOP_ADD;
3619689912eSchristos 			dns_difftuple_t *tuple = NULL;
3629689912eSchristos 
3639689912eSchristos 			dns_difftuple_create((*skrp)->mctx, op, origin,
3649689912eSchristos 					     dnskeyttl, rdata, &tuple);
3659689912eSchristos 
3669689912eSchristos 			skrbundle_addtuple(bundle, &tuple);
3679689912eSchristos 			INSIST(tuple == NULL);
3689689912eSchristos 
3699689912eSchristos 			isc_mem_put(mctx, rdata, sizeof(*rdata));
3709689912eSchristos 		}
3719689912eSchristos 	}
3729689912eSchristos 
3739689912eSchristos 	if (result != ISC_R_EOF) {
3749689912eSchristos 		CHECK(DNS_R_SYNTAX);
3759689912eSchristos 	}
3769689912eSchristos 	result = ISC_R_SUCCESS;
3779689912eSchristos 
3789689912eSchristos 	/* Add final bundle */
3799689912eSchristos 	if (bundle != NULL) {
3809689912eSchristos 		addbundle(*skrp, &bundle);
3819689912eSchristos 	}
3829689912eSchristos 
3839689912eSchristos failure:
3849689912eSchristos 	if (result != ISC_R_SUCCESS) {
3859689912eSchristos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
3869689912eSchristos 			      DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1),
3879689912eSchristos 			      "read skr file %s(%lu) failed: %s", filename,
3889689912eSchristos 			      isc_lex_getsourceline(lex),
3899689912eSchristos 			      isc_result_totext(result));
3909689912eSchristos 	}
3919689912eSchristos 
3929689912eSchristos 	/* Clean up */
3939689912eSchristos 	isc_lex_destroy(&lex);
3949689912eSchristos 	return result;
3959689912eSchristos }
3969689912eSchristos 
3979689912eSchristos dns_skrbundle_t *
3989689912eSchristos dns_skr_lookup(dns_skr_t *skr, isc_stdtime_t time, uint32_t sigval) {
3999689912eSchristos 	dns_skrbundle_t *b, *next;
4009689912eSchristos 
4019689912eSchristos 	REQUIRE(DNS_SKR_VALID(skr));
4029689912eSchristos 
4039689912eSchristos 	for (b = ISC_LIST_HEAD(skr->bundles); b != NULL; b = next) {
4049689912eSchristos 		next = ISC_LIST_NEXT(b, link);
4059689912eSchristos 		if (next == NULL) {
4069689912eSchristos 			isc_stdtime_t expired = b->inception + sigval;
4079689912eSchristos 			if (b->inception <= time && time < expired) {
4089689912eSchristos 				return b;
4099689912eSchristos 			}
4109689912eSchristos 			return NULL;
4119689912eSchristos 		}
4129689912eSchristos 		if (b->inception <= time && time < next->inception) {
4139689912eSchristos 			return b;
4149689912eSchristos 		}
4159689912eSchristos 	}
4169689912eSchristos 
4179689912eSchristos 	return NULL;
4189689912eSchristos }
4199689912eSchristos 
4209689912eSchristos void
4219689912eSchristos dns_skr_attach(dns_skr_t *source, dns_skr_t **targetp) {
4229689912eSchristos 	REQUIRE(DNS_SKR_VALID(source));
4239689912eSchristos 	REQUIRE(targetp != NULL && *targetp == NULL);
4249689912eSchristos 
4259689912eSchristos 	isc_refcount_increment(&source->references);
4269689912eSchristos 	*targetp = source;
4279689912eSchristos }
4289689912eSchristos 
4299689912eSchristos void
4309689912eSchristos dns_skr_detach(dns_skr_t **skrp) {
4319689912eSchristos 	REQUIRE(skrp != NULL && DNS_SKR_VALID(*skrp));
4329689912eSchristos 
4339689912eSchristos 	dns_skr_t *skr = *skrp;
4349689912eSchristos 	*skrp = NULL;
4359689912eSchristos 
4369689912eSchristos 	if (isc_refcount_decrement(&skr->references) == 1) {
4379689912eSchristos 		dns_skr_destroy(skr);
4389689912eSchristos 	}
4399689912eSchristos }
4409689912eSchristos 
4419689912eSchristos void
4429689912eSchristos dns_skr_destroy(dns_skr_t *skr) {
4439689912eSchristos 	dns_skrbundle_t *b, *next;
4449689912eSchristos 
4459689912eSchristos 	REQUIRE(DNS_SKR_VALID(skr));
4469689912eSchristos 
4479689912eSchristos 	for (b = ISC_LIST_HEAD(skr->bundles); b != NULL; b = next) {
4489689912eSchristos 		next = ISC_LIST_NEXT(b, link);
4499689912eSchristos 		ISC_LIST_UNLINK(skr->bundles, b, link);
4509689912eSchristos 		dns_diff_clear(&b->diff);
4519689912eSchristos 		isc_mem_put(skr->mctx, b, sizeof(*b));
4529689912eSchristos 	}
4539689912eSchristos 	INSIST(ISC_LIST_EMPTY(skr->bundles));
4549689912eSchristos 
4559689912eSchristos 	isc_mem_free(skr->mctx, skr->filename);
4569689912eSchristos 	isc_mem_putanddetach(&skr->mctx, skr, sizeof(*skr));
4579689912eSchristos }
458