Check whether a rejected rrset is different

Add a new dns_rdataset_equals() function to check whether two
rdatasets are equal in DNSSEC terms.

When an rdataset being cached is rejected because its trust
level is lower than the existing rdataset, we now check to see
whether the rejected data was identical to the existing data.
This allows us to cache a potentially useful RRSIG when handling
CD=1 queries, while still rejecting RRSIGs that would definitely
have resulted in a validation failure.

(cherry picked from commit 6aba56ae89)
This commit is contained in:
Evan Hunt
2025-02-19 18:29:34 -08:00
parent 9d4aa15c1f
commit fad9b3771f
4 changed files with 68 additions and 12 deletions
+12
View File
@@ -101,6 +101,8 @@ typedef struct dns_rdatasetmethods {
void (*getownercase)(const dns_rdataset_t *rdataset, dns_name_t *name);
isc_result_t (*addglue)(dns_rdataset_t *rdataset,
dns_dbversion_t *version, dns_message_t *msg);
bool (*equals)(const dns_rdataset_t *rdataset1,
const dns_rdataset_t *rdataset2);
} dns_rdatasetmethods_t;
#define DNS_RDATASET_MAGIC ISC_MAGIC('D', 'N', 'S', 'R')
@@ -696,4 +698,14 @@ dns_trust_totext(dns_trust_t trust);
* Display trust in textual form.
*/
bool
dns_rdataset_equals(const dns_rdataset_t *rdataset1,
const dns_rdataset_t *rdataset2);
/*%<
* Returns true if the rdata in the rdataset is equal.
*
* Requires:
* \li 'rdataset1' is a valid rdataset.
* \li 'rdataset2' is a valid rdataset.
*/
ISC_LANG_ENDDECLS
+15
View File
@@ -676,3 +676,18 @@ dns_rdataset_trimttl(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
rdataset->ttl = ttl;
sigrdataset->ttl = ttl;
}
bool
dns_rdataset_equals(const dns_rdataset_t *rdataset1,
const dns_rdataset_t *rdataset2) {
REQUIRE(DNS_RDATASET_VALID(rdataset1));
REQUIRE(DNS_RDATASET_VALID(rdataset2));
if (rdataset1->methods->equals != NULL &&
rdataset1->methods->equals == rdataset2->methods->equals)
{
return (rdataset1->methods->equals)(rdataset1, rdataset2);
}
return false;
}
+19
View File
@@ -125,6 +125,9 @@ static void
rdataset_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name);
static void
rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name);
static bool
rdataset_equals(const dns_rdataset_t *rdataset1,
const dns_rdataset_t *rdataset2);
/*% Note: the "const void *" are just to make qsort happy. */
static int
@@ -1156,6 +1159,7 @@ dns_rdatasetmethods_t dns_rdataslab_rdatasetmethods = {
.clearprefetch = rdataset_clearprefetch,
.setownercase = rdataset_setownercase,
.getownercase = rdataset_getownercase,
.equals = rdataset_equals,
};
/* Fixed RRSet helper macros */
@@ -1474,3 +1478,18 @@ rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
unlock:
dns_db_unlocknode(header->db, header->node, isc_rwlocktype_read);
}
static bool
rdataset_equals(const dns_rdataset_t *rdataset1,
const dns_rdataset_t *rdataset2) {
if (rdataset1->rdclass != rdataset2->rdclass ||
rdataset1->type != rdataset2->type)
{
return false;
}
unsigned char *header1 = rdataset1->slab.raw - sizeof(dns_slabheader_t);
unsigned char *header2 = rdataset2->slab.raw - sizeof(dns_slabheader_t);
return dns_rdataslab_equalx(header1, header2, sizeof(dns_slabheader_t),
rdataset1->rdclass, rdataset2->type);
}
+22 -12
View File
@@ -6096,33 +6096,43 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
if (result == DNS_R_UNCHANGED) {
result = ISC_R_SUCCESS;
if (!need_validation &&
ardataset != NULL &&
NEGATIVE(ardataset))
ardataset != NULL)
{
/*
* The answer in the
* cache is better than
* the answer we found,
* and is a negative
* cache entry, so we
* the answer we found.
* If it's a negative
* cache entry, we
* must set eresult
* appropriately.
*/
if (NXDOMAIN(ardataset)) {
eresult =
DNS_R_NCACHENXDOMAIN;
} else {
} else if (NEGATIVE(ardataset))
{
eresult =
DNS_R_NCACHENXRRSET;
}
/*
* We have a negative
* response from the
* cache so don't
* attempt to add the
* RRSIG rrset.
* The cache wasn't updated
* because something was
* already there. If the
* data was the same as what
* we were trying to add,
* then sigrdataset might
* still be useful. If
* not, move on.
*/
continue;
if (sigrdataset != NULL &&
!dns_rdataset_equals(
rdataset,
addedrdataset))
{
continue;
}
}
}
if (result != ISC_R_SUCCESS) {