diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h index 3c55717144..e81e195f90 100644 --- a/lib/dns/include/dns/rdataset.h +++ b/lib/dns/include/dns/rdataset.h @@ -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 diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index 1a6dc4d551..123cceb3ef 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -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; +} diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c index 3da416e2ef..74de47ab90 100644 --- a/lib/dns/rdataslab.c +++ b/lib/dns/rdataslab.c @@ -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); +} diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 0089b69aa3..b6ba7a5a3f 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -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) {