From 322626ab5bf594d70fa82122bd21869aeaa9db98 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 13 Aug 2021 09:24:11 +0200 Subject: [PATCH 1/5] 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. --- bin/tests/system/qmin/ans2/ans.py | 31 +++++++ bin/tests/system/qmin/ans3/ans.py | 28 +++++- bin/tests/system/qmin/ans4/ans.py | 39 +++++++++ bin/tests/system/qmin/ns1/root.db | 4 + bin/tests/system/qmin/tests.sh | 140 ++++++++++++++++++++++++++++++ 5 files changed, 241 insertions(+), 1 deletion(-) diff --git a/bin/tests/system/qmin/ans2/ans.py b/bin/tests/system/qmin/ans2/ans.py index 730495eddb..f607112c20 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 d8c7766950..e5eb12ac0d 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 b5a993c320..a26483bddf 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 3b38a33bbd..351500adbb 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 e6a2684643..5d85cf28b8 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 From e2d48968647b520d4d54972ec7cd7581cb548c0d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 10 Aug 2021 12:18:12 +0200 Subject: [PATCH 2/5] Add extra checks for !ANCIENT(header) There are some occurrences where we check if a header exists in the rbtdb. These cases require that the header is also not marked as ancient (aka ready for cleanup). These cases involve finding certain data in cache. --- lib/dns/rbtdb.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 98df4377c2..bfca24a5b9 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -3523,7 +3523,9 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep, for (header = node->data; header != NULL; header = header->next) { if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header)) { + !IGNORE(header) && EXISTS(header) && + !ANCIENT(header)) + { break; } } @@ -3582,7 +3584,9 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep, for (header = wnode->data; header != NULL; header = header->next) { if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header)) { + !IGNORE(header) && EXISTS(header) && + !ANCIENT(header)) + { break; } } @@ -4677,11 +4681,13 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) { &header_prev)) { /* Do nothing. */ } else if (header->type == dns_rdatatype_dname && - EXISTS(header)) { + EXISTS(header) && !ANCIENT(header)) + { dname_header = header; header_prev = header; } else if (header->type == RBTDB_RDATATYPE_SIGDNAME && - EXISTS(header)) { + EXISTS(header) && !ANCIENT(header)) + { sigdname_header = header; header_prev = header; } else { @@ -4751,7 +4757,7 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node, if (check_stale_header(node, header, &locktype, lock, search, &header_prev)) { /* Do nothing. */ - } else if (EXISTS(header)) { + } else if (EXISTS(header) && !ANCIENT(header)) { /* * We've found an extant rdataset. See if * we're interested in it. @@ -5373,7 +5379,7 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, if (check_stale_header(node, header, &locktype, lock, &search, &header_prev)) { /* Do nothing. */ - } else if (EXISTS(header)) { + } else if (EXISTS(header) && !ANCIENT(header)) { /* * If we found a type we were looking for, remember * it. From bc448fb3b1483141c0bb3c5f344244612433560d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 11 Aug 2021 10:31:56 +0200 Subject: [PATCH 3/5] Don't use stale nodes when looking up a zonecut When looking up a zonecut in cache, we use 'dns_rbt_findnode' to find the closest matching node. This function however does not take into account stale nodes. When we do find a stale node and use it, this has implications for subsequent lookups. For example, this may break QNAME minimization because we are using a deeper zonecut than we should have. Check the header for staleness and if so, and stale entries are not accepted, look for the deepest zonecut from this node up. --- lib/dns/rbtdb.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index bfca24a5b9..32355c1398 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -5363,6 +5363,7 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, } else if (!dcnull) { dns_name_copy(dcname, foundname); } + /* * We now go looking for an NS rdataset at the node. */ @@ -5378,7 +5379,22 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, header_next = header->next; if (check_stale_header(node, header, &locktype, lock, &search, &header_prev)) { - /* Do nothing. */ + /* + * The function dns_rbt_findnode found us the a matching + * node for 'name' and stored the result in 'dcname'. + * This is the deepest known zonecut in our database. + * However, this node may be stale and if serve-stale + * is not enabled (in other words 'stale-answer-enable' + * is set to no), this node may not be used as a + * zonecut we know about. If so, find the deepest + * zonecut from this node up and return that instead. + */ + NODE_UNLOCK(lock, locktype); + result = find_deepest_zonecut(&search, node, nodep, + foundname, rdataset, + sigrdataset); + dns_name_copy(foundname, dcname); + goto tree_exit; } else if (EXISTS(header) && !ANCIENT(header)) { /* * If we found a type we were looking for, remember From 192329d3c60f74174a8f79110e5ca2b1f6936006 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 13 Aug 2021 09:52:50 +0200 Subject: [PATCH 4/5] Add change entry and release note for GL #2665 --- CHANGES | 5 +++++ doc/notes/notes-current.rst | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index 02e21e6513..b8fa5e403e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +5694. [bug] BIND looks up the deepest zone cut in cache in order + to iterate a query. When this node is stale, it may + bypass QNAME minimization. This has been fixed. + [GL #2665] + 5693. [func] Restore support for reading 'timeout' and 'attempts' options from /etc/resolv.conf, and use their values in dig, host and nslookup. (Previously this was diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index b0fa7eaab8..b6cee02efe 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -82,3 +82,7 @@ Bug Fixes When the affinity is not set, tests show a slight dip in the authoritative performance of around 5% (ranging from 3.8% to 7.8%), but the recursive performance is now consistently improved. :gl:`#2822` + +- When following QNAME minimization, BIND could use a stale zonecut from cache + to resolve the query, resulting in a non-minimized query. This has been + fixed :gl:`#2665` From f79876b2d58ac472b9a9175420660ffb0dd3f62c Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Mon, 16 Aug 2021 14:15:17 +1000 Subject: [PATCH 5/5] More correctly implement ends with label sequence check string.endswith("label.sequence") doesn't check for the implict period before "label.sequence" when matching longer strings. "foo.label.sequence" should match but "foolabel.sequence shouldn't". --- bin/tests/system/qmin/ans2/ans.py | 37 +++++++++++++++++-------------- bin/tests/system/qmin/ans3/ans.py | 21 ++++++++++-------- bin/tests/system/qmin/ans4/ans.py | 21 ++++++++++-------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/bin/tests/system/qmin/ans2/ans.py b/bin/tests/system/qmin/ans2/ans.py index f607112c20..1430401235 100755 --- a/bin/tests/system/qmin/ans2/ans.py +++ b/bin/tests/system/qmin/ans2/ans.py @@ -31,6 +31,9 @@ def logquery(type, qname): with open("qlog", "a") as f: f.write("%s %s\n", type, qname) +def endswith(domain, labels): + return domain.endswith("." + labels) or domain == labels + ############################################################################ # Respond to a DNS query. # For good. it serves: @@ -81,9 +84,9 @@ def create_response(msg): r = dns.message.make_response(m) r.set_rcode(NOERROR) - if lqname.endswith("1.0.0.2.ip6.arpa."): + if endswith(lqname, "1.0.0.2.ip6.arpa."): # Direct query - give direct answer - if lqname.endswith("8.2.6.0.1.0.0.2.ip6.arpa."): + if endswith(lqname, "8.2.6.0.1.0.0.2.ip6.arpa."): # Delegate to ns3 r.authority.append(dns.rrset.from_text("8.2.6.0.1.0.0.2.ip6.arpa.", 60, IN, NS, "ns3.good.")) r.additional.append(dns.rrset.from_text("ns3.good.", 60, IN, A, "10.53.0.3")) @@ -95,7 +98,7 @@ def create_response(msg): # NS query at the apex r.answer.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 30, IN, NS, "ns2.good.")) r.flags |= dns.flags.AA - elif "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.".endswith(lqname): + elif endswith("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.", lqname): # NODATA answer r.authority.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 30, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1")) else: @@ -103,12 +106,12 @@ def create_response(msg): r.authority.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 30, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1")) r.set_rcode(NXDOMAIN) return r - elif lqname.endswith("ip6.arpa."): + elif endswith(lqname, "ip6.arpa."): if lqname == "ip6.arpa." and rrtype == NS: # NS query at the apex r.answer.append(dns.rrset.from_text("ip6.arpa.", 30, IN, NS, "ns2.good.")) r.flags |= dns.flags.AA - elif "1.0.0.2.ip6.arpa.".endswith(lqname): + elif endswith("1.0.0.2.ip6.arpa.", lqname): # NODATA answer r.authority.append(dns.rrset.from_text("ip6.arpa.", 30, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1")) else: @@ -116,12 +119,12 @@ 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."): + elif endswith(lqname, "stale."): + if endswith(lqname, "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."): + elif endswith(lqname, "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")) @@ -141,22 +144,22 @@ def create_response(msg): 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."): + elif endswith(lqname, "bad."): bad = True suffix = "bad." lqname = lqname[:-4] - elif lqname.endswith("ugly."): + elif endswith(lqname, "ugly."): ugly = True suffix = "ugly." lqname = lqname[:-5] - elif lqname.endswith("good."): + elif endswith(lqname, "good."): suffix = "good." lqname = lqname[:-5] - elif lqname.endswith("slow."): + elif endswith(lqname, "slow."): slow = True suffix = "slow." lqname = lqname[:-5] - elif lqname.endswith("fwd."): + elif endswith(lqname, "fwd."): suffix = "fwd." lqname = lqname[:-4] else: @@ -164,7 +167,7 @@ def create_response(msg): return r # Good/bad/ugly differs only in how we treat non-empty terminals - if lqname.endswith("zoop.boing."): + if endswith(lqname, "zoop.boing."): r.authority.append(dns.rrset.from_text("zoop.boing." + suffix, 1, IN, NS, "ns3." + suffix)) elif lqname == "many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z." and rrtype == A: r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.2")) @@ -199,9 +202,9 @@ def create_response(msg): else: r.authority.append(dns.rrset.from_text(suffix, 1, IN, SOA, "ns2." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1")) if bad or not \ - ("icky.icky.icky.ptang.zoop.boing.".endswith(lqname) or \ - "many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.".endswith(lqname) or \ - "a.bit.longer.ns.name.".endswith(lqname)): + (endswith("icky.icky.icky.ptang.zoop.boing.", lqname) or \ + endswith("many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.", lqname) or \ + endswith("a.bit.longer.ns.name.", lqname)): r.set_rcode(NXDOMAIN) if ugly: r.set_rcode(FORMERR) diff --git a/bin/tests/system/qmin/ans3/ans.py b/bin/tests/system/qmin/ans3/ans.py index e5eb12ac0d..6d583904a9 100755 --- a/bin/tests/system/qmin/ans3/ans.py +++ b/bin/tests/system/qmin/ans3/ans.py @@ -31,6 +31,9 @@ def logquery(type, qname): with open("qlog", "a") as f: f.write("%s %s\n", type, qname) +def endswith(domain, labels): + return domain.endswith("." + labels) or domain == labels + ############################################################################ # Respond to a DNS query. # For good. it serves: @@ -72,24 +75,24 @@ def create_response(msg): ip6req = False - if lqname.endswith("bad."): + if endswith(lqname, "bad."): bad = True suffix = "bad." lqname = lqname[:-4] - elif lqname.endswith("ugly."): + elif endswith(lqname, "ugly."): ugly = True suffix = "ugly." lqname = lqname[:-5] - elif lqname.endswith("good."): + elif endswith(lqname, "good."): suffix = "good." lqname = lqname[:-5] - elif lqname.endswith("slow."): + elif endswith(lqname, "slow."): slow = True suffix = "slow." lqname = lqname[:-5] - elif lqname.endswith("8.2.6.0.1.0.0.2.ip6.arpa."): + elif endswith(lqname, "8.2.6.0.1.0.0.2.ip6.arpa."): ip6req = True - elif lqname.endswith("a.b.stale."): + elif endswith(lqname, "a.b.stale."): if lqname == "a.b.stale.": if rrtype == TXT: # Direct query. @@ -120,15 +123,15 @@ def create_response(msg): if lqname == "zoop.boing." and rrtype == NS: r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, NS, "ns3."+suffix)) r.flags |= dns.flags.AA - elif lqname.endswith("icky.ptang.zoop.boing."): + elif endswith(lqname, "icky.ptang.zoop.boing."): r.authority.append(dns.rrset.from_text("icky.ptang.zoop.boing." + suffix, 1, IN, NS, "a.bit.longer.ns.name." + suffix)) - elif "icky.ptang.zoop.boing.".endswith(lqname): + elif endswith("icky.ptang.zoop.boing.", lqname): r.authority.append(dns.rrset.from_text("zoop.boing." + suffix, 1, IN, SOA, "ns3." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1")) if bad: r.set_rcode(NXDOMAIN) if ugly: r.set_rcode(FORMERR) - elif lqname.endswith("zoop.boing."): + elif endswith(lqname, "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) elif ip6req: diff --git a/bin/tests/system/qmin/ans4/ans.py b/bin/tests/system/qmin/ans4/ans.py index a26483bddf..110c09679d 100755 --- a/bin/tests/system/qmin/ans4/ans.py +++ b/bin/tests/system/qmin/ans4/ans.py @@ -31,6 +31,9 @@ def logquery(type, qname): with open("qlog", "a") as f: f.write("%s %s\n", type, qname) +def endswith(domain, labels): + return domain.endswith("." + labels) or domain == labels + ############################################################################ # Respond to a DNS query. # For good. it serves: @@ -73,24 +76,24 @@ def create_response(msg): ip6req = False - if lqname.endswith("bad."): + if endswith(lqname, "bad."): bad = True suffix = "bad." lqname = lqname[:-4] - elif lqname.endswith("ugly."): + elif endswith(lqname, "ugly."): ugly = True suffix = "ugly." lqname = lqname[:-5] - elif lqname.endswith("good."): + elif endswith(lqname, "good."): suffix = "good." lqname = lqname[:-5] - elif lqname.endswith("slow."): + elif endswith(lqname, "slow."): slow = True suffix = "slow." lqname = lqname[:-5] - elif lqname.endswith("1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa."): + elif endswith(lqname, "1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa."): ip6req = True - elif lqname.endswith("b.stale."): + elif endswith(lqname, "b.stale."): if lqname == "a.b.stale.": if rrtype == TXT: # Direct query. @@ -140,9 +143,9 @@ def create_response(msg): elif lqname == "icky.ptang.zoop.boing." and rrtype == NS: r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, NS, "a.bit.longer.ns.name."+suffix)) r.flags |= dns.flags.AA - elif lqname.endswith("icky.ptang.zoop.boing."): + elif endswith(lqname, "icky.ptang.zoop.boing."): r.authority.append(dns.rrset.from_text("icky.ptang.zoop.boing." + suffix, 1, IN, SOA, "ns2." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1")) - if bad or not "more.icky.icky.icky.ptang.zoop.boing.".endswith(lqname): + if bad or not endswith("more.icky.icky.icky.ptang.zoop.boing.", lqname): r.set_rcode(NXDOMAIN) if ugly: r.set_rcode(FORMERR) @@ -150,7 +153,7 @@ def create_response(msg): r.flags |= dns.flags.AA if lqname == "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." and rrtype == TXT: r.answer.append(dns.rrset.from_text("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")) - elif "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.".endswith(lqname): + elif endswith("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.", lqname): #NODATA answer r.authority.append(dns.rrset.from_text("1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", 60, IN, SOA, "ns4.good. hostmaster.arpa. 2018050100 120 30 320 16")) else: