[9.20] fix: usr: Missing DNSSEC information when CD bit is set in query

The RRSIGs for glue records were not being cached correctly for CD=1 queries.  This has been fixed.

Closes #5502

Backport of MR !10938

Merge branch 'backport-5502-fix-missing-rrsig-with-cd-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!10956
This commit is contained in:
Mark Andrews 2025-09-11 18:47:33 +10:00
commit 968a6be41f
8 changed files with 74 additions and 80 deletions

View file

@ -4754,5 +4754,16 @@ n=$((n + 1))
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
echo_i "test that RRSIGS are returned for glue name with CD=1 ($n)"
ret=0
dig_with_opts @10.53.0.4 ns3.secure.example A +cd >dig.out.ns4.test$n
grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
grep "ANSWER: 2," dig.out.ns4.test$n >/dev/null || ret=1
grep "ns3\.secure\.example\..[0-9]*.IN.A.10\.53\.0.3" dig.out.ns4.test$n >/dev/null || ret=1
grep "ns3\.secure\.example\..[0-9]*.IN.RRSIG.A " dig.out.ns4.test$n >/dev/null || ret=1
n=$((n + 1))
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1

View file

@ -302,6 +302,7 @@ enum {
#define DNS_DBADD_EXACT 0x04
#define DNS_DBADD_EXACTTTL 0x08
#define DNS_DBADD_PREFETCH 0x10
#define DNS_DBADD_EQUALOK 0x20
/*@}*/
/*%
@ -1248,6 +1249,10 @@ dns__db_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
* the old and new rdata sets. If #DNS_DBADD_EXACTTTL is set then both
* the old and new rdata sets must have the same ttl.
*
* \li If the #DNS_DBADD_EQUALOK option is set, and the database is not
* changed, compare the old and new rdatasets; if they are equal,
* return #ISC_R_SUCCESS instead of #DNS_R_UNCHANGED.
*
* \li The 'now' field is ignored if 'db' is a zone database. If 'db' is
* a cache database, then the added rdataset will expire no later than
* now + rdataset->ttl.
@ -1277,8 +1282,12 @@ dns__db_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
* Returns:
*
* \li #ISC_R_SUCCESS
* \li #DNS_R_UNCHANGED The operation did not change
* anything. \li #ISC_R_NOMEMORY \li #DNS_R_NOTEXACT
* \li #DNS_R_UNCHANGED The operation did not change anything.
* \li #ISC_R_NOMEMORY
* \li #DNS_R_NOTEXACT The TTL didn't match and #DNS_DBADD_EXACTTTL
* was set, or there was a partial overlap
* between the old and new rdatasets and
* DNS_DBADD_EXACT was set.
*
* \li Other results are possible, depending upon the database
* implementation used.

View file

@ -101,8 +101,6 @@ 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')
@ -693,14 +691,4 @@ 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

View file

@ -2993,13 +2993,23 @@ find_header:
*/
if (trust < header->trust && (ACTIVE(header, now) || header_nx))
{
dns_slabheader_destroy(&newheader);
if (addedrdataset != NULL) {
bindrdataset(qpdb, qpnode, header, now,
nlocktype, tlocktype,
addedrdataset DNS__DB_FLARG_PASS);
isc_result_t result = DNS_R_UNCHANGED;
bindrdataset(qpdb, qpnode, header, now, nlocktype,
tlocktype,
addedrdataset DNS__DB_FLARG_PASS);
if (ACTIVE(header, now) &&
(options & DNS_DBADD_EQUALOK) != 0 &&
dns_rdataslab_equalx(
(unsigned char *)header,
(unsigned char *)newheader,
(unsigned int)(sizeof(*newheader)),
qpdb->common.rdclass,
(dns_rdatatype_t)header->type))
{
result = ISC_R_SUCCESS;
}
return DNS_R_UNCHANGED;
dns_slabheader_destroy(&newheader);
return result;
}
/*

View file

@ -2729,14 +2729,24 @@ find_header:
if (rbtversion == NULL && trust < header->trust &&
(ACTIVE(header, now) || header_nx))
{
dns_slabheader_destroy(&newheader);
if (addedrdataset != NULL) {
dns__rbtdb_bindrdataset(
rbtdb, rbtnode, header, now,
isc_rwlocktype_write,
addedrdataset DNS__DB_FLARG_PASS);
result = DNS_R_UNCHANGED;
dns__rbtdb_bindrdataset(
rbtdb, rbtnode, header, now,
isc_rwlocktype_write,
addedrdataset DNS__DB_FLARG_PASS);
if (ACTIVE(header, now) &&
(options & DNS_DBADD_EQUALOK) != 0 &&
dns_rdataslab_equalx(
(unsigned char *)header,
(unsigned char *)newheader,
(unsigned int)(sizeof(*newheader)),
rbtdb->common.rdclass,
(dns_rdatatype_t)header->type))
{
result = ISC_R_SUCCESS;
}
return DNS_R_UNCHANGED;
dns_slabheader_destroy(&newheader);
return result;
}
/*

View file

@ -676,18 +676,3 @@ 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;
}

View file

@ -125,9 +125,6 @@ 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
@ -1158,7 +1155,6 @@ dns_rdatasetmethods_t dns_rdataslab_rdatasetmethods = {
.clearprefetch = rdataset_clearprefetch,
.setownercase = rdataset_setownercase,
.getownercase = rdataset_getownercase,
.equals = rdataset_equals,
};
/* Fixed RRSet helper macros */
@ -1477,18 +1473,3 @@ 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);
}

View file

@ -5877,7 +5877,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
bool have_answer = false;
isc_result_t result, eresult = ISC_R_SUCCESS;
dns_fetchresponse_t *resp = NULL;
unsigned int options;
unsigned int options = 0, equalok = 0;
bool fail;
unsigned int valoptions = 0;
bool checknta = true;
@ -6089,6 +6089,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
}
if (!need_validation || !ANSWER(rdataset)) {
options = 0;
equalok = 0;
if (ANSWER(rdataset) &&
rdataset->type != dns_rdatatype_rrsig)
{
@ -6114,10 +6115,23 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
{
options |= DNS_DBADD_FORCE;
}
/*
* If we're validating and passing the added
* rdataset back to the caller, then we ask
* dns_db_addrdataset() to compare the old and
* new rdatasets whenever the result would
* normally have been DNS_R_UNCHANGED, and to
* return ISC_R_SUCCESS if they compare equal.
* This allows us to continue and cache RRSIGs
* in that case.
*/
if (!need_validation && ardataset != NULL) {
equalok = DNS_DBADD_EQUALOK;
}
addedrdataset = ardataset;
result = dns_db_addrdataset(
fctx->cache, node, NULL, now, rdataset,
options, addedrdataset);
options | equalok, addedrdataset);
if (result == DNS_R_UNCHANGED) {
result = ISC_R_SUCCESS;
if (!need_validation &&
@ -6141,25 +6155,11 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
DNS_R_NCACHENXRRSET;
}
continue;
} else if (!need_validation &&
ardataset != NULL &&
sigrdataset != NULL &&
!dns_rdataset_equals(
rdataset, ardataset))
{
/*
* 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, and we
* should carry on caching
* it. Otherwise, move on.
*/
}
if (equalok) {
continue;
}
result = ISC_R_SUCCESS;
}
if (result != ISC_R_SUCCESS) {
break;