]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Check cached names for possible "forward only" clause
authorMark Andrews <marka@isc.org>
Mon, 28 Feb 2022 23:02:55 +0000 (10:02 +1100)
committerMichał Kępień <michal@isc.org>
Thu, 3 Mar 2022 12:33:00 +0000 (13:33 +0100)
When caching additional and glue data *not* from a forwarder, we must
check that there is no "forward only" clause covering the owner name
that would take precedence.  Such names would normally be allowed by
baliwick rules, but a "forward only" zone introduces a new baliwick
scope.

lib/dns/resolver.c

index ad7d875329f2acc7ee3ebbe64fd56876c2d8b3ef..6ef3547bcd8b982bdfb62e3ce52b266ffa16dcdb 100644 (file)
@@ -6234,37 +6234,74 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset,
                rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL;
 }
 
+/*
+ * Returns true if 'name' is external to the namespace for which
+ * the server being queried can answer, either because it's not a
+ * subdomain or because it's below a forward declaration.
+ */
 static inline bool
-name_external(const dns_name_t *name, fetchctx_t *fctx) {
-       if (ISFORWARDER(fctx->addrinfo)) {
-               isc_result_t result;
-               dns_fixedname_t fixed;
-               dns_forwarders_t *forwarders = NULL;
-               dns_name_t *fname;
+name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) {
+       isc_result_t result;
+       dns_forwarders_t *forwarders = NULL;
+       dns_fixedname_t fixed;
+       dns_name_t *fname = dns_fixedname_initname(&fixed);
+       dns_name_t suffix;
+       unsigned int labels;
 
-               if (!dns_name_issubdomain(name, fctx->fwdname)) {
-                       return (true);
-               }
+       /*
+        * The name is outside the queried namespace.
+        */
+       if (!dns_name_issubdomain(name, ISFORWARDER(fctx->addrinfo)
+                                               ? fctx->fwdname
+                                               : &fctx->domain))
+       {
+               return (true);
+       }
+
+       /*
+        * If the record lives in the parent zone, adjust the name so we
+        * look for the correct forward clause.
+        */
+       labels = dns_name_countlabels(name);
+       if (dns_rdatatype_atparent(type) && labels > 1U) {
+               dns_name_init(&suffix, NULL);
+               dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
+               name = &suffix;
+       }
+
+       /*
+        * Look for a forward declaration below 'name'.
+        */
+       result = dns_fwdtable_find2(fctx->res->view->fwdtable, name, fname,
+                                   &forwarders);
 
+       if (ISFORWARDER(fctx->addrinfo)) {
                /*
-                * Is there a child forwarder declaration that is better?
-                * This lookup should always succeed if the configuration
-                * has not changed.
+                * See if the forwarder declaration is better.
                 */
-               fname = dns_fixedname_initname(&fixed);
-               result = dns_fwdtable_find(fctx->res->view->fwdtable, name, fname,
-                                          &forwarders);
                if (result == ISC_R_SUCCESS) {
                        return (!dns_name_equal(fname, fctx->fwdname));
                }
 
                /*
-                * Play it safe if the configuration has changed.
+                * If the lookup failed, the configuration must have
+                * changed: play it safe and don't cache.
                 */
                return (true);
        }
 
-       return (!dns_name_issubdomain(name, &fctx->domain));
+       /*
+        * If name is covered by 'forward only' then we can't
+        * cache this repsonse.
+        */
+       if (result == ISC_R_SUCCESS &&
+           forwarders->fwdpolicy == dns_fwdpolicy_only &&
+           !ISC_LIST_EMPTY(forwarders->fwdrs))
+       {
+               return (true);
+       }
+
+       return (false);
 }
 
 static isc_result_t
@@ -6295,7 +6332,7 @@ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
        result = dns_message_findname(rmessage, section, addname,
                                      dns_rdatatype_any, 0, &name, NULL);
        if (result == ISC_R_SUCCESS) {
-               external = name_external(name, fctx);
+               external = name_external(name, type, fctx);
                if (type == dns_rdatatype_a) {
                        for (rdataset = ISC_LIST_HEAD(name->list);
                             rdataset != NULL;
@@ -7180,7 +7217,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
                        /*
                         * Don't accept DNAME from parent namespace.
                         */
-                       if (name_external(name, fctx)) {
+                       if (name_external(name, dns_rdatatype_dname, fctx)) {
                                continue;
                        }
 
@@ -7421,7 +7458,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
        while (!done && result == ISC_R_SUCCESS) {
                name = NULL;
                dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
-               if (!name_external(name, fctx)) {
+               if (!name_external(name, dns_rdatatype_ns, fctx)) {
                        /*
                         * We expect to find NS or SIG NS rdatasets, and
                         * nothing else.