diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 05954bf936..cf7ce13d3a 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -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 diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 254b7d38e5..081c8ae834 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -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. diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h index 37e1bc689b..0dc54cf58e 100644 --- a/lib/dns/include/dns/rdataset.h +++ b/lib/dns/include/dns/rdataset.h @@ -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 diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index c631f27231..2ef95b9cf4 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -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; } /* diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 4cb3b9cd47..105f5f1693 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -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; } /* diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index 1118e1531d..b6043ba7ea 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -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; -} diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c index dd891f22b3..134febb4aa 100644 --- a/lib/dns/rdataslab.c +++ b/lib/dns/rdataslab.c @@ -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); -} diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index bcac380e8f..7163b54a8b 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -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;