]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add qmin test cases when RRset has expired
authorMatthijs Mekking <matthijs@isc.org>
Fri, 13 Aug 2021 07:24:11 +0000 (09:24 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 13 Aug 2021 07:42:34 +0000 (09:42 +0200)
Add test cases for GL #2665: The QNAME minimization (if enabled) should
also occur on the second query, after the RRsets have expired from
cache. BIND will still have the entries in cache, but marked stale.
These stale entries should not prevent the resolver from minimizing
the QNAME. We query for the test domain a.b.stale. in all cases (QNAME
minimization off, strict mode, and relaxed mode) and expect it to
behave the same the second time we have a stale delegation structure in
cache.

bin/tests/system/qmin/ans2/ans.py
bin/tests/system/qmin/ans3/ans.py
bin/tests/system/qmin/ans4/ans.py
bin/tests/system/qmin/ns1/root.db
bin/tests/system/qmin/tests.sh

index 730495eddb087718099078e3e3f12250bb7bed00..f607112c20effb66adf1bc4c145b7621239a9230 100755 (executable)
@@ -51,6 +51,12 @@ def logquery(type, qname):
 # 8.2.6.0.1.0.0.2.ip6.arpa IN NS ns3.good
 # 1.0.0.2.ip6.arpa. IN NS ns2.good
 # ip6.arpa. IN NS ns2.good
+#
+# For stale. it serves:
+# a.b. NS ns.a.b.stale.
+# ns.a.b.stale. IN A 10.53.0.3
+# b. NS ns.b.stale.
+# ns.b.stale. IN A 10.53.0.4
 ############################################################################
 def create_response(msg):
     m = dns.message.from_wire(msg)
@@ -110,6 +116,31 @@ def create_response(msg):
             r.authority.append(dns.rrset.from_text("ip6.arpa.", 30, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
             r.set_rcode(NXDOMAIN)
         return r
+    elif lqname.endswith("stale."):
+        if lqname.endswith("a.b.stale."):
+            # Delegate to ns.a.b.stale.
+            r.authority.append(dns.rrset.from_text("a.b.stale.", 2, IN, NS, "ns.a.b.stale."))
+            r.additional.append(dns.rrset.from_text("ns.a.b.stale.", 2, IN, A, "10.53.0.3"))
+        elif lqname.endswith("b.stale."):
+            # Delegate to ns.b.stale.
+            r.authority.append(dns.rrset.from_text("b.stale.", 2, IN, NS, "ns.b.stale."))
+            r.additional.append(dns.rrset.from_text("ns.b.stale.", 2, IN, A, "10.53.0.4"))
+        elif lqname == "stale." and rrtype == NS:
+            # NS query at the apex.
+            r.answer.append(dns.rrset.from_text("stale.", 2, IN, NS, "ns2.stale."))
+            r.flags |= dns.flags.AA
+        elif lqname == "stale." and rrtype == SOA:
+            # SOA query at the apex.
+            r.answer.append(dns.rrset.from_text("stale.", 2, IN, SOA, "ns2.stale. hostmaster.stale. 1 2 3 4 5"))
+            r.flags |= dns.flags.AA
+        elif lqname == "stale.":
+            # NODATA answer
+            r.authority.append(dns.rrset.from_text("stale.", 2, IN, SOA, "ns2.stale. hostmaster.arpa. 1 2 3 4 5"))
+        else:
+            # NXDOMAIN
+            r.authority.append(dns.rrset.from_text("stale.", 2, IN, SOA, "ns2.stale. hostmaster.arpa. 1 2 3 4 5"))
+            r.set_rcode(NXDOMAIN)
+        return r
     elif lqname.endswith("bad."):
         bad = True
         suffix = "bad."
index d8c77669507f988fd007f7cbbdf84bd5447ccbdf..e5eb12ac0de7ee9ae319a651a5df1754a91ae98e 100755 (executable)
@@ -43,6 +43,9 @@ def logquery(type, qname):
 # For bad. it works the same as for good., but returns NXDOMAIN to non-empty terminals
 #
 # For ugly. it works the same as for good., but returns garbage to non-empty terminals
+#
+# For stale. it serves:
+# a.b.stale. IN TXT peekaboo (resolver did not do qname minimization)
 ############################################################################
 def create_response(msg):
     m = dns.message.from_wire(msg)
@@ -85,7 +88,30 @@ def create_response(msg):
         suffix = "slow."
         lqname = lqname[:-5]
     elif lqname.endswith("8.2.6.0.1.0.0.2.ip6.arpa."):
-        ip6req = True 
+        ip6req = True
+    elif lqname.endswith("a.b.stale."):
+        if lqname == "a.b.stale.":
+            if rrtype == TXT:
+                # Direct query.
+                r.answer.append(dns.rrset.from_text(lqname, 1, IN, TXT, "peekaboo"))
+                r.flags |= dns.flags.AA
+            elif rrtype == NS:
+                # NS a.b.
+                r.answer.append(dns.rrset.from_text(lqname, 1, IN, NS, "ns.a.b.stale."))
+                r.additional.append(dns.rrset.from_text("ns.a.b.stale.", 1, IN, A, "10.53.0.3"))
+                r.flags |= dns.flags.AA
+            elif rrtype == SOA:
+                # SOA a.b.
+                r.answer.append(dns.rrset.from_text(lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5"))
+                r.flags |= dns.flags.AA
+            else:
+                # NODATA.
+                r.authority.append(dns.rrset.from_text(lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5"))
+        else:
+            r.authority.append(dns.rrset.from_text(lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5"))
+            r.set_rcode(NXDOMAIN)
+            # NXDOMAIN.
+        return r
     else:
         r.set_rcode(REFUSED)
         return r
index b5a993c320c7af1e0d2b93ec745f0cb3daf4bcc8..a26483bddf1ed71ee2a15c9f85731b0d1ec1778c 100755 (executable)
@@ -44,6 +44,9 @@ def logquery(type, qname):
 # For bad. it works the same as for good., but returns NXDOMAIN to non-empty terminals
 #
 # For ugly. it works the same as for good., but returns garbage to non-empty terminals
+#
+# For stale. it serves:
+# a.b.stale. IN TXT hooray (resolver did do qname minimization)
 ############################################################################
 def create_response(msg):
     m = dns.message.from_wire(msg)
@@ -87,6 +90,42 @@ def create_response(msg):
         lqname = lqname[:-5]
     elif lqname.endswith("1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa."):
         ip6req = True
+    elif lqname.endswith("b.stale."):
+        if lqname == "a.b.stale.":
+            if rrtype == TXT:
+                # Direct query.
+                r.answer.append(dns.rrset.from_text(lqname, 1, IN, TXT, "hooray"))
+                r.flags |= dns.flags.AA
+            elif rrtype == NS:
+                # NS a.b.
+                r.answer.append(dns.rrset.from_text(lqname, 1, IN, NS, "ns.a.b.stale."))
+                r.additional.append(dns.rrset.from_text("ns.a.b.stale.", 1, IN, A, "10.53.0.3"))
+                r.flags |= dns.flags.AA
+            elif rrtype == SOA:
+                # SOA a.b.
+                r.answer.append(dns.rrset.from_text(lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5"))
+                r.flags |= dns.flags.AA
+            else:
+                # NODATA.
+                r.authority.append(dns.rrset.from_text(lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5"))
+        elif lqname == "b.stale.":
+            if rrtype == NS:
+                # NS b.
+                r.answer.append(dns.rrset.from_text(lqname, 1, IN, NS, "ns.b.stale."))
+                r.additional.append(dns.rrset.from_text("ns.b.stale.", 1, IN, A, "10.53.0.4"))
+                r.flags |= dns.flags.AA
+            elif rrtype == SOA:
+                # SOA b.
+                r.answer.append(dns.rrset.from_text(lqname, 1, IN, SOA, "b.stale. hostmaster.b.stale. 1 2 3 4 5"))
+                r.flags |= dns.flags.AA
+            else:
+                # NODATA.
+                r.authority.append(dns.rrset.from_text(lqname, 1, IN, SOA, "b.stale. hostmaster.b.stale. 1 2 3 4 5"))
+        else:
+            r.authority.append(dns.rrset.from_text(lqname, 1, IN, SOA, "b.stale. hostmaster.b.stale. 1 2 3 4 5"))
+            r.set_rcode(NXDOMAIN)
+            # NXDOMAIN.
+        return r
     else:
         r.set_rcode(REFUSED)
         return r
index 3b38a33bbdcdb62aaae4140eb559ac4cef0480e5..351500adbb5a8f7920d4c359e06f5f575e6c85fa 100644 (file)
@@ -33,3 +33,7 @@ ns2.ugly.             A       10.53.0.2
 
 fwd.                   NS      ns2.fwd.
 ns2.fwd.               A       10.53.0.2
+
+$TTL 2
+stale.                 NS      ns2.stale.
+ns2.stale.             A       10.53.0.2
index e6a26846431e469ccde674206022db0f6e138809..5d85cf28b85cd4146d4d31dd74e2f24ff28bfd5c 100755 (executable)
@@ -392,6 +392,146 @@ grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
 # ;; ANSWER SECTION:
 # test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. 1 IN TXT "long_ip6_name"
 grep 'ip6\.arpa.*TXT.*long_ip6_name' dig.out.test$n > /dev/null || ret=1
+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`
+
+# Below are test cases for GL #2665: The QNAME minimization (if enabled) should
+# also occur on the second query, after the RRsets have expired from cache.
+# BIND will still have the entries in cache, but marked stale. These stale
+# entries should not prevent the resolver from minimizing the QNAME.
+# We query for the test domain a.b.stale. in all cases (QNAME minimization off,
+# strict mode, and relaxed mode) and expect it to behave the same the second
+# time when we have a stale delegation structure in cache.
+n=`expr $n + 1`
+echo_i "query for .stale is not minimized when qname-minimization is off ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.5 flush
+$DIG $DIGOPTS @10.53.0.5 txt a.b.stale. > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "a\.b\.stale\..*1.*IN.*TXT.*peekaboo" dig.out.test$n > /dev/null || ret=1
+sleep 1
+echo "TXT a.b.stale." | $DIFF ans2/query.log - > /dev/null || ret=1
+echo "TXT a.b.stale." | $DIFF ans3/query.log - > /dev/null || ret=1
+test -f  ans4/query.log && ret=1
+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 .stale is properly minimized when qname-minimization is in strict mode ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.6 flush
+$DIG $DIGOPTS @10.53.0.6 txt a.b.stale. > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" 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 ns.b.stale.
+ADDR ns2.stale.
+NS b.stale.
+NS stale.
+__EOF
+test -f  ans3/query.log && ret=1
+sort ans4/query.log > ans4/query.log.sorted
+cat << __EOF | $DIFF ans4/query.log.sorted - > /dev/null || ret=1
+ADDR ns.b.stale.
+NS b.stale.
+TXT a.b.stale.
+__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 .stale is properly minimized when qname-minimization is in relaxed mode ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.7 flush
+$DIG $DIGOPTS @10.53.0.7 txt a.b.stale. > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" 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 _.b.stale.
+ADDR ns.b.stale.
+ADDR ns2.stale.
+__EOF
+test -f  ans3/query.log && ret=1
+sort ans4/query.log > ans4/query.log.sorted
+cat << __EOF | $DIFF ans4/query.log.sorted - > /dev/null || ret=1
+ADDR ns.b.stale.
+TXT a.b.stale.
+__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`
+
+echo_i "sleep 2, allow entries in cache to go stale"
+sleep 2
+
+n=`expr $n + 1`
+echo_i "query for .stale is not minimized when qname-minimization is off (stale cache) ($n)"
+ret=0
+$CLEANQL
+$DIG $DIGOPTS @10.53.0.5 txt a.b.stale. > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "a\.b\.stale\..*1.*IN.*TXT.*peekaboo" dig.out.test$n > /dev/null || ret=1
+sleep 1
+echo "TXT a.b.stale." | $DIFF ans2/query.log - > /dev/null || ret=1
+echo "TXT a.b.stale." | $DIFF ans3/query.log - > /dev/null || ret=1
+test -f  ans4/query.log && ret=1
+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 .stale is properly minimized when qname-minimization is in strict mode (stale cache) ($n)"
+ret=0
+$CLEANQL
+$DIG $DIGOPTS @10.53.0.6 txt a.b.stale. > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" 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
+NS b.stale.
+NS stale.
+__EOF
+test -f  ans3/query.log && ret=1
+sort ans4/query.log > ans4/query.log.sorted
+cat << __EOF | $DIFF ans4/query.log.sorted - > /dev/null || ret=1
+NS b.stale.
+TXT a.b.stale.
+__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 .stale is properly minimized when qname-minimization is in relaxed mode (stale cache) ($n)"
+ret=0
+$CLEANQL
+$DIG $DIGOPTS @10.53.0.7 txt a.b.stale. > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" 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 _.b.stale.
+__EOF
+test -f  ans3/query.log && ret=1
+sort ans4/query.log > ans4/query.log.sorted
+cat << __EOF | $DIFF ans4/query.log.sorted - > /dev/null || ret=1
+TXT a.b.stale.
+__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`
 
 echo_i "exit status: $status"
 [ $status -eq 0 ] || exit 1