/* $NetBSD: rdataset_test.c,v 1.4 2026/09/17 18:01:18 christos Exp $ */ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ #include #include /* IWYU pragma: keep */ #include #include #include #include #include #include #define UNIT_TESTING #include #include #include #include #include #include #include #include #include #include /* test trimming of rdataset TTLs */ ISC_RUN_TEST_IMPL(trimttl) { dns_rdataset_t rdataset, sigrdataset; dns_rdata_rrsig_t rrsig; isc_stdtime_t ttltimenow, ttltimeexpire; ttltimenow = 10000000; ttltimeexpire = ttltimenow + 800; UNUSED(state); dns_rdataset_init(&rdataset); dns_rdataset_init(&sigrdataset); rdataset.ttl = 900; sigrdataset.ttl = 1000; rrsig.timeexpire = ttltimeexpire; rrsig.originalttl = 1000; dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow, true); assert_int_equal(rdataset.ttl, 800); assert_int_equal(sigrdataset.ttl, 800); rdataset.ttl = 900; sigrdataset.ttl = 1000; rrsig.timeexpire = ttltimenow - 200; rrsig.originalttl = 1000; dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow, true); assert_int_equal(rdataset.ttl, 120); assert_int_equal(sigrdataset.ttl, 120); rdataset.ttl = 900; sigrdataset.ttl = 1000; rrsig.timeexpire = ttltimenow - 200; rrsig.originalttl = 1000; dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow, false); assert_int_equal(rdataset.ttl, 0); assert_int_equal(sigrdataset.ttl, 0); sigrdataset.ttl = 900; rdataset.ttl = 1000; rrsig.timeexpire = ttltimeexpire; rrsig.originalttl = 1000; dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow, true); assert_int_equal(rdataset.ttl, 800); assert_int_equal(sigrdataset.ttl, 800); sigrdataset.ttl = 900; rdataset.ttl = 1000; rrsig.timeexpire = ttltimenow - 200; rrsig.originalttl = 1000; dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow, true); assert_int_equal(rdataset.ttl, 120); assert_int_equal(sigrdataset.ttl, 120); sigrdataset.ttl = 900; rdataset.ttl = 1000; rrsig.timeexpire = ttltimenow - 200; rrsig.originalttl = 1000; dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow, false); assert_int_equal(rdataset.ttl, 0); assert_int_equal(sigrdataset.ttl, 0); } /* * A rdataset at the NOQNAME proof owner, in wire order. */ typedef struct { dns_rdatatype_t type; dns_rdatatype_t covers; dns_ttl_t ttl; } proofset_t; #define ANSWER_TTL 3600 #define MAXSETS 4 static void addset(dns_name_t *owner, dns_rdatalist_t *rdatalist, dns_rdataset_t *rdataset, dns_rdatatype_t type, dns_rdatatype_t covers, dns_ttl_t ttl) { dns_rdatalist_init(rdatalist); rdatalist->rdclass = dns_rdataclass_in; rdatalist->type = type; rdatalist->covers = covers; rdatalist->ttl = ttl; dns_rdataset_init(rdataset); dns_rdatalist_tordataset(rdatalist, rdataset); if (owner != NULL) { ISC_LIST_APPEND(owner->list, rdataset, link); } } /* * Check that dns_rdataset_addnoqname() selects the proof of 'type' at an * owner carrying 'sets' in wire order, and that dns_rdataset_getnoqname() * then returns that same proof rather than one chosen by wire order. */ static void check_noqname(const proofset_t *sets, size_t nsets, dns_rdatatype_t type, isc_result_t expected) { dns_fixedname_t fowner; dns_name_t *owner = dns_fixedname_initname(&fowner); dns_rdatalist_t rdatalists[MAXSETS], answerlist; dns_rdataset_t rdatasets[MAXSETS], answer; dns_rdataset_t neg = DNS_RDATASET_INIT, negsig = DNS_RDATASET_INIT; dns_name_t found = DNS_NAME_INITEMPTY; dns_ttl_t ttl = ANSWER_TTL; isc_result_t result; assert_true(nsets <= MAXSETS); result = dns_name_fromstring(owner, "proof.example.", dns_rootname, 0, NULL); assert_int_equal(result, ISC_R_SUCCESS); for (size_t i = 0; i < nsets; i++) { addset(owner, &rdatalists[i], &rdatasets[i], sets[i].type, sets[i].covers, sets[i].ttl); if (sets[i].type == type || (sets[i].type == dns_rdatatype_rrsig && sets[i].covers == type)) { ttl = ISC_MIN(ttl, sets[i].ttl); } } addset(NULL, &answerlist, &answer, dns_rdatatype_a, 0, ANSWER_TTL); result = dns_rdataset_addnoqname(&answer, owner, type); assert_int_equal(result, expected); if (result != ISC_R_SUCCESS) { assert_false((answer.attributes & DNS_RDATASETATTR_NOQNAME) != 0); assert_int_equal(answer.ttl, ANSWER_TTL); return; } assert_true((answer.attributes & DNS_RDATASETATTR_NOQNAME) != 0); assert_int_equal(answer.ttl, ttl); result = dns_rdataset_getnoqname(&answer, &found, &neg, &negsig); assert_int_equal(result, ISC_R_SUCCESS); assert_true(dns_name_equal(&found, owner)); assert_int_equal(neg.type, type); assert_int_equal(negsig.type, dns_rdatatype_rrsig); assert_int_equal(negsig.covers, type); dns_rdataset_disassociate(&neg); dns_rdataset_disassociate(&negsig); dns_rdataset_disassociate(&answer); } #define CHECK_NOQNAME(sets, type, expected) \ check_noqname(sets, sizeof(sets) / sizeof((sets)[0]), type, expected) /* test NOQNAME proof selection by dns_rdataset_{add,get}noqname() */ ISC_RUN_TEST_IMPL(noqname) { /* Signed NSEC followed by an unsigned NSEC3 (#5985). */ const proofset_t nsec_unsigned_nsec3[] = { { dns_rdatatype_nsec, 0, 300 }, { dns_rdatatype_rrsig, dns_rdatatype_nsec, 200 }, { dns_rdatatype_nsec3, 0, 100 }, }; /* Signed NSEC3 followed by an unsigned NSEC (#6369). */ const proofset_t nsec3_unsigned_nsec[] = { { dns_rdatatype_nsec3, 0, 300 }, { dns_rdatatype_rrsig, dns_rdatatype_nsec3, 200 }, { dns_rdatatype_nsec, 0, 100 }, }; /* Both denial types signed, in both wire orders. */ const proofset_t nsec_nsec3[] = { { dns_rdatatype_nsec, 0, 300 }, { dns_rdatatype_rrsig, dns_rdatatype_nsec, 300 }, { dns_rdatatype_nsec3, 0, 200 }, { dns_rdatatype_rrsig, dns_rdatatype_nsec3, 200 }, }; const proofset_t nsec3_nsec[] = { { dns_rdatatype_nsec3, 0, 200 }, { dns_rdatatype_rrsig, dns_rdatatype_nsec3, 200 }, { dns_rdatatype_nsec, 0, 300 }, { dns_rdatatype_rrsig, dns_rdatatype_nsec, 300 }, }; /* RRSIG preceding the rdataset it covers. */ const proofset_t rrsig_first[] = { { dns_rdatatype_rrsig, dns_rdatatype_nsec, 300 }, { dns_rdatatype_nsec, 0, 300 }, }; /* No signed denial at all. */ const proofset_t unsigned_only[] = { { dns_rdatatype_nsec, 0, 300 }, { dns_rdatatype_nsec3, 0, 300 }, }; UNUSED(state); CHECK_NOQNAME(nsec_unsigned_nsec3, dns_rdatatype_nsec, ISC_R_SUCCESS); CHECK_NOQNAME(nsec_unsigned_nsec3, dns_rdatatype_nsec3, ISC_R_NOTFOUND); CHECK_NOQNAME(nsec3_unsigned_nsec, dns_rdatatype_nsec3, ISC_R_SUCCESS); CHECK_NOQNAME(nsec3_unsigned_nsec, dns_rdatatype_nsec, ISC_R_NOTFOUND); /* The caller's choice wins regardless of wire order. */ CHECK_NOQNAME(nsec_nsec3, dns_rdatatype_nsec, ISC_R_SUCCESS); CHECK_NOQNAME(nsec_nsec3, dns_rdatatype_nsec3, ISC_R_SUCCESS); CHECK_NOQNAME(nsec3_nsec, dns_rdatatype_nsec, ISC_R_SUCCESS); CHECK_NOQNAME(nsec3_nsec, dns_rdatatype_nsec3, ISC_R_SUCCESS); CHECK_NOQNAME(rrsig_first, dns_rdatatype_nsec, ISC_R_SUCCESS); CHECK_NOQNAME(unsigned_only, dns_rdatatype_nsec, ISC_R_NOTFOUND); CHECK_NOQNAME(unsigned_only, dns_rdatatype_nsec3, ISC_R_NOTFOUND); check_noqname(NULL, 0, dns_rdatatype_nsec, ISC_R_NOTFOUND); } ISC_TEST_LIST_START ISC_TEST_ENTRY(trimttl) ISC_TEST_ENTRY(noqname) ISC_TEST_LIST_END ISC_TEST_MAIN