]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Use experimental "_ A" minimization in relaxed mode.
authorWitold Kręcicki <wpk@isc.org>
Tue, 28 May 2019 12:03:13 +0000 (14:03 +0200)
committerEvan Hunt <each@isc.org>
Thu, 30 May 2019 21:06:55 +0000 (14:06 -0700)
qname minimization, even in relaxed mode, can fail on
some very broken domains. In relaxed mode, instead of
asking for "foo.bar NS" ask for "_.foo.bar A" to either
get a delegation or NXDOMAIN. It will require more queries
than regular mode for proper NXDOMAINs.

bin/tests/system/conf.sh.common
bin/tests/system/qmin/ans3/ans.py
bin/tests/system/qmin/tests.sh
lib/dns/include/dns/resolver.h
lib/dns/resolver.c
lib/ns/query.c

index 38e43f2abcec6c6f4758b2052c39989149af2016..e32f1a28fbffcac40afd4ccfe02e133535d63f4c 100644 (file)
@@ -21,7 +21,7 @@ else
        TESTSOCK6=false
 fi
 
-TESTSOCK6="$TESTSOCK6"
+export LANG=C
 
 . ${TOP}/version
 
index 51d9ae2e7e6926c5811ad2e7b9e193e05066f813..839e71a025b7e6c59b02613752f2b8bf6463d450 100755 (executable)
@@ -98,7 +98,7 @@ def create_response(msg):
             r.set_rcode(NXDOMAIN)
         if ugly:
             r.set_rcode(FORMERR)
-    elif "zoop.boing.".endswith(lqname):
+    elif lqname.endswith("zoop.boing."):
         r.authority.append(dns.rrset.from_text("zoop.boing." + suffix, 1, IN, SOA, "ns3." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1"))
         r.set_rcode(NXDOMAIN)
     else:
index b67f3511e0020095a67f78e25bd74b0ce736b363..0ad69c45d2aa61d50a8e4024db7d46a83388cf4e 100755 (executable)
@@ -109,7 +109,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
 status=`expr $status + $ret`
 
 n=`expr $n + 1`
-echo_i "query for .good is properly minimized when qname-minimization is on ($n)"
+echo_i "query for .good is properly minimized when qname-minimization is in strict mode ($n)"
 ret=0
 $CLEANQL
 $RNDCCMD 10.53.0.6 flush
@@ -142,6 +142,37 @@ for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=`expr $status + $ret`
 
+n=`expr $n + 1`
+echo_i "query for .good is properly minimized when qname-minimization is in relaxed mode ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.7 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.7 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "icky.icky.icky.ptang.zoop.boing.good. 1  IN A    192.0.2.1" dig.out.test$n > /dev/null || ret=1
+sleep 1
+sort ans2/query.log > ans2/query.log.sorted
+cat << __EOF | $DIFF ans2/query.log.sorted - > /dev/null || ret=1
+ADDR _.boing.good.
+ADDR _.zoop.boing.good.
+ADDR a.bit.longer.ns.name.good.
+ADDR a.bit.longer.ns.name.good.
+ADDR ns2.good.
+ADDR ns3.good.
+ADDR ns3.good.
+__EOF
+cat << __EOF | $DIFF ans3/query.log - > /dev/null || ret=1
+ADDR _.ptang.zoop.boing.good.
+ADDR _.icky.ptang.zoop.boing.good.
+__EOF
+cat << __EOF | $DIFF ans4/query.log - > /dev/null || ret=1
+ADDR _.icky.icky.ptang.zoop.boing.good.
+ADDR icky.icky.icky.ptang.zoop.boing.good.
+__EOF
+for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
 n=`expr $n + 1`
 echo_i "query for .bad fails when qname-minimization is in strict mode ($n)"
 ret=0
@@ -171,17 +202,22 @@ grep "icky.icky.icky.ptang.zoop.boing.bad. 1 IN A 192.0.2.1" dig.out.test$n > /d
 sleep 1
 sort ans2/query.log > ans2/query.log.sorted
 cat << __EOF | $DIFF ans2/query.log.sorted - > /dev/null || ret=1
+ADDR _.boing.bad.
+ADDR _.zoop.boing.bad.
 ADDR a.bit.longer.ns.name.bad.
 ADDR a.bit.longer.ns.name.bad.
-ADDR icky.icky.icky.ptang.zoop.boing.bad.
 ADDR ns2.bad.
 ADDR ns3.bad.
 ADDR ns3.bad.
-NS bad.
-NS boing.bad.
 __EOF
-echo "ADDR icky.icky.icky.ptang.zoop.boing.bad." | $DIFF ans3/query.log - > /dev/null || ret=1
-echo "ADDR icky.icky.icky.ptang.zoop.boing.bad." | $DIFF ans4/query.log - > /dev/null || ret=1
+cat << __EOF | $DIFF ans3/query.log - > /dev/null || ret=1
+ADDR _.ptang.zoop.boing.bad.
+ADDR _.icky.ptang.zoop.boing.bad.
+__EOF
+cat << __EOF | $DIFF ans4/query.log - > /dev/null || ret=1
+ADDR _.icky.icky.ptang.zoop.boing.bad.
+ADDR icky.icky.icky.ptang.zoop.boing.bad.
+__EOF
 for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=`expr $status + $ret`
@@ -215,17 +251,17 @@ $DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.ugly. @10.53.0.7 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
 grep "icky.icky.icky.ptang.zoop.boing.ugly. 1  IN A    192.0.2.1" dig.out.test$n > /dev/null || ret=1
 sleep 1
+
 sort ans2/query.log > ans2/query.log.sorted
-cat << __EOF | $DIFF ans2/query.log.sorted - > /dev/null || ret=1
+cat << __EOF | $DIFF ans2/query.log.sorted - > /dev/null || cat ans2/query.log.sorted
+ADDR _.boing.ugly.
+ADDR _.boing.ugly.
 ADDR a.bit.longer.ns.name.ugly.
 ADDR a.bit.longer.ns.name.ugly.
 ADDR icky.icky.icky.ptang.zoop.boing.ugly.
 ADDR ns2.ugly.
 ADDR ns3.ugly.
 ADDR ns3.ugly.
-NS boing.ugly.
-NS boing.ugly.
-NS ugly.
 __EOF
 echo "ADDR icky.icky.icky.ptang.zoop.boing.ugly." | $DIFF ans3/query.log - > /dev/null || ret=1
 echo "ADDR icky.icky.icky.ptang.zoop.boing.ugly." | $DIFF ans4/query.log - > /dev/null || ret=1
index 53533723a65425ddc4c802fa4d31dd6ccfa1b522..3a074a8481dca146c53a8f6f18aa59ecd7dce1f8 100644 (file)
@@ -110,15 +110,20 @@ typedef enum {
 #define DNS_FETCHOPT_NOCACHED          0x00008000 /*%< Force cache update. */
 #define DNS_FETCHOPT_QMINIMIZE         0x00010000 /*%< Use qname
                                                        minimization. */
-#define DNS_FETCHOPT_QMIN_STRICT       0x00020000 /*%< Do not work around
+#define DNS_FETCHOPT_NOFOLLOW          0x00020000 /*%< Don't follow
+                                                       delegations */
+#define DNS_FETCHOPT_QMIN_STRICT       0x00040000 /*%< Do not work around
                                                        servers that return
                                                        errors on non-empty
                                                        terminals. */
-#define DNS_FETCHOPT_QMIN_SKIP_IP6A    0x00040000 /*%< Skip some labels
+#define DNS_FETCHOPT_QMIN_USE_A                0x00080000 /*%< Use A type queries
+                                                       instead of NS when
+                                                       doing minimization */
+#define DNS_FETCHOPT_QMIN_SKIP_IP6A    0x00100000 /*%< Skip some labels
                                                        when doing qname
                                                        minimization on
                                                        ip6.arpa. */
-#define DNS_FETCHOPT_NOFORWARD         0x00080000 /*%< Do not use forwarders
+#define DNS_FETCHOPT_NOFORWARD         0x00200000 /*%< Do not use forwarders
                                                        if possible. */
 
 /* Reserved in use by adb.c            0x00400000 */
index 0c55f741f38bd0508632fe0f2597a1911ce2ce2b..689a324dc28c951252614a24fb92cef50ed96977 100644 (file)
@@ -579,6 +579,11 @@ static unsigned char ip6_arpa_offsets[] = { 0, 4, 9 };
 static const dns_name_t ip6_arpa =
        DNS_NAME_INITABSOLUTE(ip6_arpa_data, ip6_arpa_offsets);
 
+static unsigned char underscore_data[]  = "\001_";
+static unsigned char underscore_offsets[] = { 0 };
+static const dns_name_t underscore_name =
+       DNS_NAME_INITNONABSOLUTE(underscore_data, underscore_offsets);
+
 static void destroy(dns_resolver_t *res);
 static void empty_bucket(dns_resolver_t *res);
 static isc_result_t resquery_send(resquery_t *query);
@@ -4055,6 +4060,14 @@ fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) {
        if (fctx->minimized && !fctx->forwarding) {
                unsigned int options = fctx->options;
                options &= ~DNS_FETCHOPT_QMINIMIZE;
+               /*
+                * In "_ A" mode we're asking for _.domain -
+                * resolver by default will follow delegations
+                * then, we don't want that.
+                */
+               if ((options & DNS_FETCHOPT_QMIN_USE_A) != 0) {
+                       options |= DNS_FETCHOPT_NOFOLLOW;
+               }
                fctx_increference(fctx);
                task = res->buckets[bucketnum].task;
                fctx_stoptimer(fctx);
@@ -4157,8 +4170,19 @@ resume_qmin(isc_task_t *task, isc_event_t *event) {
                goto cleanup;
        }
 
-       if (NXDOMAIN_RESULT(result) || result == DNS_R_FORMERR ||
-           result == DNS_R_REMOTEFORMERR || result == ISC_R_FAILURE)
+       /*
+        * If we're doing "_ A"-style minimization we can get
+        * NX answer to minimized query - we need to continue then.
+        *
+        * Otherwise - either disable minimization if we're
+        * in relaxed mode or fail if we're in strict mode.
+        */
+
+       if ((NXDOMAIN_RESULT(result) &&
+            (fctx->options & DNS_FETCHOPT_QMIN_USE_A) == 0) ||
+           result == DNS_R_FORMERR ||
+           result == DNS_R_REMOTEFORMERR ||
+           result == ISC_R_FAILURE)
        {
                if ((fctx->options & DNS_FETCHOPT_QMIN_STRICT) == 0) {
                        fctx->qmin_labels = DNS_MAX_LABELS + 1;
@@ -7607,7 +7631,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
                case DNS_R_CHASEDSSERVERS:
                        break;
                case DNS_R_DELEGATION:
-                       result = ISC_R_SUCCESS;
+                       /* With NOFOLLOW we want to pass the result code */
+                       if ((fctx->options & DNS_FETCHOPT_NOFOLLOW) == 0) {
+                               result = ISC_R_SUCCESS;
+                       }
                        break;
                default:
                        /*
@@ -7641,10 +7668,11 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
         * work to be queued to the DNSSEC validator.
         */
        if (WANTCACHE(fctx)) {
-               result = cache_message(fctx, query->addrinfo, rctx.now);
-               if (result != ISC_R_SUCCESS) {
-                       FCTXTRACE3("cache_message complete", result);
-                       rctx_done(&rctx, result);
+               isc_result_t tresult;
+               tresult = cache_message(fctx, query->addrinfo, rctx.now);
+               if (tresult != ISC_R_SUCCESS) {
+                       FCTXTRACE3("cache_message complete", tresult);
+                       rctx_done(&rctx, tresult);
                        return;
                }
        }
@@ -9194,18 +9222,21 @@ rctx_referral(respctx_t *rctx) {
         * reset the fetch context counters.
         *
         */
-       rctx->get_nameservers = true;
-       rctx->next_server = true;
-       rctx->fctx->restarts = 0;
-       rctx->fctx->referrals++;
-       rctx->fctx->querysent = 0;
-       rctx->fctx->lamecount = 0;
-       rctx->fctx->quotacount = 0;
-       rctx->fctx->neterr = 0;
-       rctx->fctx->badresp = 0;
-       rctx->fctx->adberr = 0;
+       if ((rctx->fctx->options & DNS_FETCHOPT_NOFOLLOW) == 0) {
+               rctx->get_nameservers = true;
+               rctx->next_server = true;
+               rctx->fctx->restarts = 0;
+               rctx->fctx->referrals++;
+               rctx->fctx->querysent = 0;
+               rctx->fctx->lamecount = 0;
+               rctx->fctx->quotacount = 0;
+               rctx->fctx->neterr = 0;
+               rctx->fctx->badresp = 0;
+               rctx->fctx->adberr = 0;
+       }
 
        return (ISC_R_COMPLETE);
+
 }
 
 /*
@@ -10471,9 +10502,28 @@ fctx_minimize_qname(fetchctx_t *fctx) {
                dns_name_split(&fctx->name,
                               fctx->qmin_labels,
                               NULL, dns_fixedname_name(&fname));
-               result = dns_name_dup(dns_fixedname_name(&fname), fctx->mctx,
-                                     &fctx->qminname);
-               fctx->qmintype = dns_rdatatype_ns;
+               if ((fctx->options & DNS_FETCHOPT_QMIN_USE_A) != 0) {
+                       isc_buffer_t dbuf;
+                       dns_fixedname_t tmpname;
+                       char ndata[DNS_NAME_MAXWIRE];
+                       isc_buffer_init(&dbuf, ndata, DNS_NAME_MAXWIRE);
+                       dns_fixedname_init(&tmpname);
+                       result = dns_name_concatenate(&underscore_name,
+                                                     dns_fixedname_name(&fname),
+                                                     dns_fixedname_name(&tmpname),
+                                                     &dbuf);
+                       if (result == ISC_R_SUCCESS) {
+                               result = dns_name_dup(dns_fixedname_name(&tmpname),
+                                                     fctx->mctx,
+                                                     &fctx->qminname);
+                       }
+                       fctx->qmintype = dns_rdatatype_a;
+               } else {
+                       result = dns_name_dup(dns_fixedname_name(&fname),
+                                             fctx->mctx,
+                                             &fctx->qminname);
+                       fctx->qmintype = dns_rdatatype_ns;
+               }
                fctx->minimized = true;
        } else {
                /* Minimization is done, we'll ask for whole qname */
@@ -10482,6 +10532,14 @@ fctx_minimize_qname(fetchctx_t *fctx) {
                fctx->minimized = false;
        }
 
+       char domainbuf[DNS_NAME_FORMATSIZE];
+       dns_name_format(&fctx->qminname, domainbuf, sizeof(domainbuf));
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+                     DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(5),
+                     "QNAME minimization - %s minimized, qmintype %d "
+                     "qminname %s", fctx->minimized ? "" : "not",
+                     fctx->qmintype, domainbuf);
+
        return (result);
 }
 
index a51bffe5fc8a98e95eb7952b6a2c2b3d93246405..06f2b2fb2774362970077745e3ee15f8f28820f2 100644 (file)
@@ -11088,6 +11088,8 @@ ns_query_start(ns_client_t *client) {
                                DNS_FETCHOPT_QMIN_SKIP_IP6A;
                if (client->view->qmin_strict) {
                        client->query.fetchoptions |= DNS_FETCHOPT_QMIN_STRICT;
+               } else {
+                       client->query.fetchoptions |= DNS_FETCHOPT_QMIN_USE_A;
                }
        }