From: Mark Andrews Date: Wed, 10 Sep 2025 06:18:41 +0000 (+1000) Subject: Fix missing RRSIGs for "glue" lookups with CD=1 X-Git-Tag: v9.18.41~16^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=018ecbb0c821583e3d69c4267be22de6f18955bf;p=thirdparty%2Fbind9.git Fix missing RRSIGs for "glue" lookups with CD=1 The code to test whether to store the RRSIGs on DNS_R_UNCHANGED with CD=1 was failing because the comparison methods of the two rdatatset instances were not compatible. Move the testing into dns_db_addrdataset(), and request it by setting the DNS_ADD_EQUALOK option. If the option is set and the old and new rrsets compare as equal, dns_db_addrdataset() returns ISC_R_SUCCESS instead of DNS_R_UNCHANGED. (cherry picked from commit b954a1df43e6e6e5ff60f1da1240ece644b7e190) --- diff --git a/lib/dns/dnsrps.c b/lib/dns/dnsrps.c index e1bf958e57d..3cbc98583ca 100644 --- a/lib/dns/dnsrps.c +++ b/lib/dns/dnsrps.c @@ -996,7 +996,6 @@ static dns_rdatasetmethods_t rpsdb_rdataset_methods = { NULL, NULL, NULL, - NULL }; static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods = { diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 5eb2cea58fd..882b568b68c 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -287,6 +287,7 @@ struct dns_dbonupdatelistener { #define DNS_DBADD_EXACT 0x04 #define DNS_DBADD_EXACTTTL 0x08 #define DNS_DBADD_PREFETCH 0x10 +#define DNS_DBADD_EQUALOK 0x20 /*@}*/ /*% @@ -1228,6 +1229,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. @@ -1257,8 +1262,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/rbtdb.c b/lib/dns/rbtdb.c index 54363c0319e..8fedf2cff9f 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -6510,13 +6510,22 @@ find_header: if (rbtversion == NULL && trust < header->trust && (ACTIVE(header, now) || header_nx)) { - free_rdataset(rbtdb, rbtdb->common.mctx, newheader); - if (addedrdataset != NULL) { - bind_rdataset(rbtdb, rbtnode, header, now, - isc_rwlocktype_write, - addedrdataset); + result = DNS_R_UNCHANGED; + bind_rdataset(rbtdb, rbtnode, header, now, + isc_rwlocktype_write, addedrdataset); + 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; + free_rdataset(rbtdb, rbtdb->common.mctx, newheader); + return result; } /* diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index ff845ec1d84..2c354f565ed 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -6331,7 +6331,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_fetchevent_t *event = NULL; - unsigned int options; + unsigned int options = 0, equalok = 0; isc_task_t *task; bool fail; unsigned int valoptions = 0; @@ -6548,6 +6548,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) { @@ -6573,10 +6574,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 && @@ -6600,25 +6614,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;