From: Matthijs Mekking Date: Fri, 13 Aug 2021 07:24:11 +0000 (+0200) Subject: Add qmin test cases when RRset has expired X-Git-Tag: v9.17.18~37^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=322626ab5bf594d70fa82122bd21869aeaa9db98;p=thirdparty%2Fbind9.git Add qmin test cases when RRset has expired 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. --- diff --git a/bin/tests/system/qmin/ans2/ans.py b/bin/tests/system/qmin/ans2/ans.py index 730495eddb0..f607112c20e 100755 --- a/bin/tests/system/qmin/ans2/ans.py +++ b/bin/tests/system/qmin/ans2/ans.py @@ -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." diff --git a/bin/tests/system/qmin/ans3/ans.py b/bin/tests/system/qmin/ans3/ans.py index d8c77669507..e5eb12ac0de 100755 --- a/bin/tests/system/qmin/ans3/ans.py +++ b/bin/tests/system/qmin/ans3/ans.py @@ -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 diff --git a/bin/tests/system/qmin/ans4/ans.py b/bin/tests/system/qmin/ans4/ans.py index b5a993c320c..a26483bddf1 100755 --- a/bin/tests/system/qmin/ans4/ans.py +++ b/bin/tests/system/qmin/ans4/ans.py @@ -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 diff --git a/bin/tests/system/qmin/ns1/root.db b/bin/tests/system/qmin/ns1/root.db index 3b38a33bbdc..351500adbb5 100644 --- a/bin/tests/system/qmin/ns1/root.db +++ b/bin/tests/system/qmin/ns1/root.db @@ -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 diff --git a/bin/tests/system/qmin/tests.sh b/bin/tests/system/qmin/tests.sh index e6a26846431..5d85cf28b85 100755 --- a/bin/tests/system/qmin/tests.sh +++ b/bin/tests/system/qmin/tests.sh @@ -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