From: Mark Andrews Date: Thu, 18 Jul 2024 03:27:23 +0000 (+1000) Subject: Don't leak the original QTYPE to parent zone X-Git-Tag: v9.21.7~56^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=de519cd1c90786f6c3672d6f14b3ea4fdde8723f;p=thirdparty%2Fbind9.git Don't leak the original QTYPE to parent zone When performing QNAME minimization, named now sends an NS query for the original QNAME, to prevent the parent zone from receiving the QTYPE. For example, when looking up example.com/A, we now send NS queries for both com and example.com before sending the A query to the servers for example.com. Previously, an A query for example.com would have been sent to the servers for com. Several system tests needed to be adjusted for the new query pattern: - Some queries in the serve-stale test were sent to the wrong server. - The synthfromdnssec test could fail due to timing issues; this has been addressed by adding a 1-second delay. - The cookie test could fail due to the a change in the count of TSIG records received in the "check that missing COOKIE with a valid TSIG signed response does not trigger TCP fallback" test case. - The GL #4652 regression test case in the chain system test depends on a particular query order, which no longer occurs when QNAME minimization is active. We now disable qname-minimization for that test. --- diff --git a/bin/tests/system/chain/ns7/named.conf.in b/bin/tests/system/chain/ns7/named.conf.in index 0d95bc88c87..751521ba0cb 100644 --- a/bin/tests/system/chain/ns7/named.conf.in +++ b/bin/tests/system/chain/ns7/named.conf.in @@ -28,6 +28,7 @@ options { } except-from { "example"; }; + qname-minimization disabled; // Regression test for GL #4652 }; trust-anchors { }; diff --git a/bin/tests/system/cookie/tests.sh b/bin/tests/system/cookie/tests.sh index babe6133efc..73473885f09 100755 --- a/bin/tests/system/cookie/tests.sh +++ b/bin/tests/system/cookie/tests.sh @@ -552,16 +552,21 @@ sys.exit(1)'; then $DIG $DIGOPTS @10.53.0.1 tsig. >dig.out.test$n.1 || ret=1 grep "status: NOERROR" dig.out.test$n.1 >/dev/null || ret=1 rndc_dumpdb ns1 + # prime cache with NS response for QNAME minimisation grep "$pat" ns1/named_dump.db.test$n >/dev/null || ret=1 + $DIG $DIGOPTS @10.53.0.1 NS nocookie.tsig >dig.out.test$n.2 || ret=1 + grep "status: NOERROR" dig.out.test$n.2 >/dev/null || ret=1 # check the disabled server response nextpart ns1/named.run >/dev/null - $DIG $DIGOPTS @10.53.0.1 nocookie.tsig >dig.out.test$n.2 || ret=1 - grep "status: NOERROR" dig.out.test$n.2 >/dev/null || ret=1 - grep 'A.10\.53\.0\.9' dig.out.test$n.2 >/dev/null || ret=1 - grep 'A.10\.53\.0\.10' dig.out.test$n.2 >/dev/null || ret=1 + $DIG $DIGOPTS @10.53.0.1 nocookie.tsig >dig.out.test$n.3 || ret=1 + grep "status: NOERROR" dig.out.test$n.3 >/dev/null || ret=1 + grep 'A.10\.53\.0\.9' dig.out.test$n.3 >/dev/null || ret=1 + grep 'A.10\.53\.0\.10' dig.out.test$n.3 >/dev/null || ret=1 nextpart ns1/named.run >named.run.test$n count=$(grep -c ') [0-9][0-9]* NOERROR 0' named.run.test$n) test $count -eq 2 || ret=1 + count=$(grep -c '^; COOKIE: ................................' named.run.test$n) + test $count -eq 1 || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) fi diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index d3ee1bc8b4e..c389c5f6c7a 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -2179,7 +2179,7 @@ echo_i "checking RRSIG query from cache ($n)" ret=0 dig_with_opts normalthenrrsig.secure.example. @10.53.0.4 a >/dev/null || ret=1 ans=$(dig_with_opts +short normalthenrrsig.secure.example. @10.53.0.4 rrsig) || ret=1 -expect=$(dig_with_opts +short normalthenrrsig.secure.example. @10.53.0.3 rrsig | grep '^A') || ret=1 +expect=$(dig_with_opts +short normalthenrrsig.secure.example. @10.53.0.3 rrsig | grep '^\(A\|NSEC\)') || ret=1 test "$ans" = "$expect" || ret=1 # also check that RA is set dig_with_opts normalthenrrsig.secure.example. @10.53.0.4 rrsig >dig.out.ns4.test$n || ret=1 @@ -3768,7 +3768,7 @@ status=$((status + ret)) echo_i "checking EDE code 1 for bad alg mnemonic ($n)" ret=0 dig_with_opts @10.53.0.4 badalg.secure.example >dig.out.ns4.test$n || ret=1 -grep "; EDE: 1 (Unsupported DNSKEY Algorithm): (ECDSAP256SHA256 badalg.secure.example/A)" dig.out.ns4.test$n >/dev/null || ret=1 +grep "; EDE: 1 (Unsupported DNSKEY Algorithm): (ECDSAP256SHA256 badalg.secure.example/NSEC)" dig.out.ns4.test$n >/dev/null || ret=1 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1 n=$((n + 1)) test "$ret" -eq 0 || echo_i "failed" diff --git a/bin/tests/system/mirror/tests.sh b/bin/tests/system/mirror/tests.sh index 77a88f581f9..2d02320006f 100644 --- a/bin/tests/system/mirror/tests.sh +++ b/bin/tests/system/mirror/tests.sh @@ -385,7 +385,7 @@ $DIG $DIGOPTS @10.53.0.3 foo.initially-unavailable. A >dig.out.ns3.test$n.1 2>&1 grep "NOERROR" dig.out.ns3.test$n.1 >/dev/null || ret=1 grep "flags:.* ad" dig.out.ns3.test$n.1 >/dev/null || ret=1 # Sanity check: the authoritative server should have been queried. -nextpart ns2/named.run | grep "query 'foo.initially-unavailable/A/IN'" >/dev/null || ret=1 +nextpart ns2/named.run | grep "query 'foo.initially-unavailable/NS/IN'" >/dev/null || ret=1 # Reconfigure ns2 so that the zone can be mirrored on ns3. sed '/^zone "initially-unavailable" {$/,/^};$/ { s/10.53.0.254/10.53.0.3/ @@ -403,7 +403,7 @@ $DIG $DIGOPTS @10.53.0.3 foo.initially-unavailable. A >dig.out.ns3.test$n.2 2>&1 grep "NOERROR" dig.out.ns3.test$n.2 >/dev/null || ret=1 grep "flags:.* ad" dig.out.ns3.test$n.2 >/dev/null || ret=1 # Ensure the authoritative server was not queried. -nextpart ns2/named.run | grep "query 'foo.initially-unavailable/A/IN'" >/dev/null && ret=1 +nextpart ns2/named.run | grep "query 'foo.initially-unavailable/NS/IN'" >/dev/null && ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -434,7 +434,7 @@ $DIG $DIGOPTS @10.53.0.3 foo.initially-unavailable. A >dig.out.ns3.test$n 2>&1 | grep "NOERROR" dig.out.ns3.test$n >/dev/null || ret=1 grep "flags:.* ad" dig.out.ns3.test$n >/dev/null || ret=1 # Sanity check: the authoritative server should have been queried. -nextpart ns2/named.run | grep "query 'foo.initially-unavailable/A/IN'" >/dev/null || ret=1 +nextpart ns2/named.run | grep "query 'foo.initially-unavailable/NS/IN'" >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) diff --git a/bin/tests/system/qmin/ans4/ans.py b/bin/tests/system/qmin/ans4/ans.py index 517217aec11..f5fe3cc03f8 100755 --- a/bin/tests/system/qmin/ans4/ans.py +++ b/bin/tests/system/qmin/ans4/ans.py @@ -104,9 +104,10 @@ def create_response(msg): r.answer.append(dns.rrset.from_text(lqname, 1, IN, TXT, "hooray")) elif rrtype == NS: # NS a.b. + # This is only returned if a query for b.stale/NS has been made 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") + dns.rrset.from_text("ns.a.b.stale.", 1, IN, A, "10.53.0.4") ) elif rrtype == SOA: # SOA a.b. @@ -126,7 +127,7 @@ def create_response(msg): r.flags |= dns.flags.AA if rrtype == A: r.answer.append( - dns.rrset.from_text("ns.a.b.stale.", 1, IN, A, "10.53.0.3") + dns.rrset.from_text("ns.a.b.stale.", 1, IN, A, "10.53.0.4") ) else: # NODATA. diff --git a/bin/tests/system/qmin/tests.sh b/bin/tests/system/qmin/tests.sh index 6cf8d2b9d07..7973a607118 100755 --- a/bin/tests/system/qmin/tests.sh +++ b/bin/tests/system/qmin/tests.sh @@ -127,12 +127,14 @@ ADDR a.bit.longer.ns.name.good. ADDR ns2.good. ADDR ns3.good. ADDR ns3.good. +NS a.bit.longer.ns.name.good. NS bit.longer.ns.name.good. NS boing.good. NS good. NS longer.ns.name.good. NS name.good. NS ns.name.good. +NS ns3.good. NS zoop.boing.good. __EOF cat <<__EOF | diff ans3/query.log - >/dev/null || ret=1 @@ -165,11 +167,13 @@ ADDR a.bit.longer.ns.name.good. ADDR ns2.good. ADDR ns3.good. ADDR ns3.good. +NS a.bit.longer.ns.name.good. NS bit.longer.ns.name.good. NS boing.good. NS longer.ns.name.good. NS name.good. NS ns.name.good. +NS ns3.good. NS zoop.boing.good. __EOF cat <<__EOF | diff ans3/query.log - >/dev/null || ret=1 @@ -221,6 +225,7 @@ ADDR ns3.bad. ADDR ns3.bad. NS boing.bad. NS name.bad. +NS ns3.bad. __EOF cat <<__EOF | diff ans3/query.log - >/dev/null || ret=1 ADDR icky.icky.icky.ptang.zoop.boing.bad. @@ -271,6 +276,7 @@ ADDR ns3.ugly. NS boing.ugly. NS name.ugly. NS name.ugly. +NS ns3.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 @@ -302,11 +308,13 @@ ADDR a.bit.longer.ns.name.slow. ADDR ns2.slow. ADDR ns3.slow. ADDR ns3.slow. +NS a.bit.longer.ns.name.slow. NS bit.longer.ns.name.slow. NS boing.slow. NS longer.ns.name.slow. NS name.slow. NS ns.name.slow. +NS ns3.slow. NS slow. NS zoop.boing.slow. __EOF @@ -340,6 +348,7 @@ NS 8.f.4.0.1.0.0.2.ip6.arpa. NS 0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. NS 0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. NS 0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. +NS 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. PTR 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. __EOF for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done @@ -362,12 +371,14 @@ ADDR a.bit.longer.ns.name.good. ADDR ns2.good. ADDR ns3.good. ADDR ns3.good. +NS a.bit.longer.ns.name.good. NS bit.longer.ns.name.good. NS boing.good. NS good. NS longer.ns.name.good. NS name.good. NS ns.name.good. +NS ns3.good. NS zoop.boing.good. __EOF cat <<__EOF | diff ans3/query.log - >/dev/null || ret=1 @@ -449,6 +460,7 @@ 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.a.b.stale. ADDR ns.b.stale. ADDR ns2.stale. NS b.stale. @@ -457,7 +469,9 @@ __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.a.b.stale. ADDR ns.b.stale. +NS a.b.stale. NS b.stale. TXT a.b.stale. __EOF @@ -476,6 +490,7 @@ 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.a.b.stale. ADDR ns.b.stale. ADDR ns2.stale. NS b.stale. @@ -483,7 +498,9 @@ __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.a.b.stale. ADDR ns.b.stale. +NS a.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 @@ -519,6 +536,7 @@ 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.a.b.stale. ADDR ns.b.stale. ADDR ns2.stale. NS b.stale. @@ -527,7 +545,9 @@ __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.a.b.stale. ADDR ns.b.stale. +NS a.b.stale. NS b.stale. TXT a.b.stale. __EOF @@ -546,6 +566,7 @@ 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.a.b.stale. ADDR ns.b.stale. ADDR ns2.stale. NS b.stale. @@ -553,7 +574,9 @@ __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.a.b.stale. ADDR ns.b.stale. +NS a.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 diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh index 95e8617ad95..2669021c620 100755 --- a/bin/tests/system/resolver/tests.sh +++ b/bin/tests/system/resolver/tests.sh @@ -729,10 +729,10 @@ if ${FEATURETEST} --enable-querytrace; then grep "status: SERVFAIL" dig.ns5.out.${n} >/dev/null || ret=1 check_namedrun() { nextpartpeek ns5/named.run >nextpart.out.${n} - grep 'resolving tcpalso.no-questions/A for [^:]*: empty question section, accepting it anyway as TC=1' nextpart.out.${n} >/dev/null || return 1 - grep '(tcpalso.no-questions/A): connecting via TCP' nextpart.out.${n} >/dev/null || return 1 - grep 'resolving tcpalso.no-questions/A for [^:]*: empty question section$' nextpart.out.${n} >/dev/null || return 1 - grep '(tcpalso.no-questions/A): nextitem' nextpart.out.${n} >/dev/null || return 1 + grep 'resolving tcpalso.no-questions/NS for [^:]*: empty question section, accepting it anyway as TC=1' nextpart.out.${n} >/dev/null || return 1 + grep '(tcpalso.no-questions/NS): connecting via TCP' nextpart.out.${n} >/dev/null || return 1 + grep 'resolving tcpalso.no-questions/NS for [^:]*: empty question section$' nextpart.out.${n} >/dev/null || return 1 + grep '(tcpalso.no-questions/NS): nextitem' nextpart.out.${n} >/dev/null || return 1 return 0 } retry_quiet 12 check_namedrun || ret=1 diff --git a/bin/tests/system/rpzextra/tests_rpzextra.py b/bin/tests/system/rpzextra/tests_rpzextra.py index 33b6d1529de..424830ed80a 100644 --- a/bin/tests/system/rpzextra/tests_rpzextra.py +++ b/bin/tests/system/rpzextra/tests_rpzextra.py @@ -111,8 +111,8 @@ def test_rpz_passthru_logging(): expected_rcode=dns.rcode.NOERROR, ) assert res_allowed_any.answer == [ - dns.rrset.from_text("allowed.", 300, "IN", "NS", "ns1.allowed."), dns.rrset.from_text("allowed.", 300, "IN", "A", "10.53.0.2"), + dns.rrset.from_text("allowed.", 300, "IN", "NS", "ns1.allowed."), ] # The comparison above doesn't compare the TTL values, and we want to # make sure that the "passthru" rpz doesn't cap the TTL with max-policy-ttl. diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh index 42b94dbeb5a..714adab7d78 100755 --- a/bin/tests/system/serve-stale/tests.sh +++ b/bin/tests/system/serve-stale/tests.sh @@ -115,10 +115,12 @@ sleep 2 # stale for somewhere between 3500-3599 seconds. echo_i "check rndc dump stale data.example ($n)" rndc_dumpdb ns1 || ret=1 -awk '/; stale since [0-9]*/ { x=$0; getline; print x, $0}' ns1/named_dump.db.test$n \ +# add in inherited owner names +awk '$1 ~ /^[0-9][0-9]*$/ { $0 = last " " $0 } $1 != ";" { last = $1 } { print }' ns1/named_dump.db.test$n >named_dump.db.test$n +awk '/; stale since [0-9]*/ { x=$0; getline; print x, $0}' named_dump.db.test$n \ | grep "; stale since [0-9]* data\.example.*3[56]...*TXT.*A text record with a 2 second ttl" >/dev/null 2>&1 || ret=1 # Also make sure the not expired data does not have a stale comment. -awk '/; authanswer/ { x=$0; getline; print x, $0}' ns1/named_dump.db.test$n \ +awk '/; authanswer/ { x=$0; getline; print x, $0}' named_dump.db.test$n \ | grep "; authanswer longttl\.example.*[56]...*TXT.*A text record with a 600 second ttl" >/dev/null 2>&1 || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) @@ -1664,16 +1666,15 @@ status=$((status + ret)) # Check that expired records are dumped. echo_i "check rndc dump expired data.example ($n)" ret=0 -awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \ - | grep "; expired (awaiting cleanup) data\.example\..*A text record with a 2 second ttl" >/dev/null 2>&1 || ret=1 -awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \ - | grep "; expired (awaiting cleanup) nodata\.example\." >/dev/null 2>&1 || ret=1 -awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \ - | grep "; expired (awaiting cleanup) nxdomain\.example\." >/dev/null 2>&1 || ret=1 -awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \ - | grep "; expired (awaiting cleanup) othertype\.example\." >/dev/null 2>&1 || ret=1 +# add in inherited owner names +awk '$1 ~ /^[0-9][0-9]*$/ { $0 = last " " $0 } $1 != ";" { last = $1 } { print }' ns5/named_dump.db.test$n >named_dump.db.test$n +# extract expired records +awk '/; expired/ { x=$0; getline; print x, $0}' named_dump.db.test$n >expired.test$n +grep "; expired (awaiting cleanup) data\.example\..*A text record with a 2 second ttl" expired.test$n >/dev/null 2>&1 || ret=1 +grep "; expired (awaiting cleanup) nodata\.example\." expired.test$n >/dev/null 2>&1 || ret=1 +grep "; expired (awaiting cleanup) nxdomain\.example\." expired.test$n >/dev/null 2>&1 || ret=1 # Also make sure the not expired data does not have an expired comment. -awk '/; authanswer/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \ +awk '/; authanswer/ { x=$0; getline; print x, $0}' named_dump.db.test$n \ | grep "; authanswer longttl\.example.*A text record with a 600 second ttl" >/dev/null 2>&1 || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) diff --git a/bin/tests/system/serve-stale/tests_sh_serve_stale.py b/bin/tests/system/serve-stale/tests_sh_serve_stale.py index 18b84e8b791..1c3177c4a49 100644 --- a/bin/tests/system/serve-stale/tests_sh_serve_stale.py +++ b/bin/tests/system/serve-stale/tests_sh_serve_stale.py @@ -14,6 +14,8 @@ import pytest pytestmark = pytest.mark.extra_artifacts( [ "dig.out.*", + "expired.test*", + "named_dump.db.test*", "rndc.out.*", "ans*/ans.run", "ns*/named.stats*", diff --git a/bin/tests/system/synthfromdnssec/tests.sh b/bin/tests/system/synthfromdnssec/tests.sh index e4b77e48e4e..dac6a2cc9e7 100644 --- a/bin/tests/system/synthfromdnssec/tests.sh +++ b/bin/tests/system/synthfromdnssec/tests.sh @@ -414,10 +414,10 @@ for ns in 2 4 5 6; do check_status NOERROR dig.out.ns${ns}.test$n || ret=1 if [ ${synth} = yes ]; then check_synth_cname b.wild-cname.example. dig.out.ns${ns}.test$n || ret=1 - nextpart ns1/named.run | grep b.wild-cname.example/A >/dev/null && ret=1 + nextpart ns1/named.run | grep b.wild-cname.example/NS >/dev/null && ret=1 else check_nosynth_cname b.wild-cname.example. dig.out.ns${ns}.test$n || ret=1 - nextpart ns1/named.run | grep b.wild-cname.example/A >/dev/null || ret=1 + nextpart ns1/named.run | grep b.wild-cname.example/NS >/dev/null || ret=1 fi grep "ns1.example.*.IN.A" dig.out.ns${ns}.test$n >/dev/null || ret=1 digcomp wildcname.out dig.out.ns${ns}.test$n || ret=1 @@ -470,6 +470,7 @@ for ns in 2 4 5 6; do check_nosynth_aaaa b.wild-2-nsec-afterdata.example. dig.out.a.ns${ns}.test$n || ret=1 # nextpart ns1/named.run >/dev/null + sleep 1 dig_with_opts b.wild-2-nsec-afterdata.example. @10.53.0.${ns} TLSA >dig.out.ns${ns}.test$n || ret=1 check_ad_flag $ad dig.out.ns${ns}.test$n || ret=1 check_status NOERROR dig.out.ns${ns}.test$n || ret=1 @@ -531,7 +532,7 @@ for ns in 2 4 5 6; do check_ad_flag no dig.out.ns${ns}.test$n || ret=1 check_status NOERROR dig.out.ns${ns}.test$n || ret=1 check_nosynth_cname b.wild-cname.insecure.example dig.out.ns${ns}.test$n || ret=1 - nextpart ns1/named.run | grep b.wild-cname.insecure.example/A >/dev/null || ret=1 + nextpart ns1/named.run | grep b.wild-cname.insecure.example/NS >/dev/null || ret=1 grep "ns1.insecure.example.*.IN.A" dig.out.ns${ns}.test$n >/dev/null || ret=1 digcomp insecure.wildcname.out dig.out.ns${ns}.test$n || ret=1 n=$((n + 1)) diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index c1f9579cace..9a5ba9b872c 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -401,6 +401,7 @@ struct fetchctx { dns_rdatatype_t qmintype; dns_fetch_t *qminfetch; dns_rdataset_t qminrrset; + dns_rdataset_t qminsigrrset; dns_fixedname_t qmindcfname; dns_name_t *qmindcname; dns_fixedname_t fwdfname; @@ -697,6 +698,9 @@ fctx__done(fetchctx_t *fctx, isc_result_t result, const char *func, static void resume_qmin(void *arg); +static void +clone_results(fetchctx_t *fctx); + static isc_result_t get_attached_fctx(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name, dns_rdatatype_t type, const dns_name_t *domain, @@ -4139,7 +4143,8 @@ fctx_try(fetchctx_t *fctx, bool retrying) { &fctx->nameservers, NULL, NULL, 0, options | DNS_FETCHOPT_QMINFETCH, 0, fctx->qc, fctx->gqc, fctx->loop, resume_qmin, fctx, &fctx->edectx, - &fctx->qminrrset, NULL, &fctx->qminfetch); + &fctx->qminrrset, &fctx->qminsigrrset, + &fctx->qminfetch); if (result != ISC_R_SUCCESS) { fetchctx_unref(fctx); goto done; @@ -4192,6 +4197,11 @@ resume_qmin(void *arg) { unsigned int findoptions = 0; dns_name_t *fname = NULL, *dcname = NULL; dns_fixedname_t ffixed, dcfixed; + dns_rdataset_t rdataset; + dns_rdataset_t sigrdataset; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + bool fixup_result = false; REQUIRE(VALID_FCTX(fctx)); @@ -4204,16 +4214,27 @@ resume_qmin(void *arg) { fname = dns_fixedname_initname(&ffixed); dcname = dns_fixedname_initname(&dcfixed); + dns_rdataset_init(&rdataset); + dns_rdataset_init(&sigrdataset); + if (resp->node != NULL) { + dns_db_attachnode(resp->db, resp->node, &node); dns_db_detachnode(resp->db, &resp->node); } if (resp->db != NULL) { + dns_db_attach(resp->db, &db); dns_db_detach(&resp->db); } if (dns_rdataset_isassociated(resp->rdataset)) { + dns_rdataset_clone(resp->rdataset, &rdataset); dns_rdataset_disassociate(resp->rdataset); } + if (dns_rdataset_isassociated(resp->sigrdataset)) { + dns_rdataset_clone(resp->sigrdataset, &sigrdataset); + dns_rdataset_disassociate(resp->sigrdataset); + } + dns_name_copy(resp->foundname, fname); result = resp->result; @@ -4247,6 +4268,37 @@ resume_qmin(void *arg) { goto cleanup; } + if (result == DNS_R_NXDOMAIN && + fctx->qmin_labels == dns_name_countlabels(fctx->name)) + { + LOCK(&fctx->lock); + resp = ISC_LIST_HEAD(fctx->resps); + if (resp != NULL) { + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_clone(&rdataset, + resp->rdataset); + } + if (dns_rdataset_isassociated(&sigrdataset) && + resp->sigrdataset != NULL) + { + dns_rdataset_clone(&sigrdataset, + resp->sigrdataset); + } + if (db != NULL) { + dns_db_attach(db, &resp->db); + } + if (node != NULL) { + dns_db_attachnode(db, node, + &resp->node); + } + dns_name_copy(fname, resp->foundname); + clone_results(fctx); + UNLOCK(&fctx->lock); + goto cleanup; + } + UNLOCK(&fctx->lock); + } + /* ...or disable minimization in relaxed mode */ fctx->qmin_labels = DNS_NAME_MAXLABELS; @@ -4274,6 +4326,53 @@ resume_qmin(void *arg) { { fctx->force_qmin_warning = true; } + + /* + * We have got a CNAME or DNAME respone to the NS query + * so we are done in almost all cases. + */ + if ((result == DNS_R_CNAME || result == DNS_R_DNAME) && + fctx->qmin_labels == dns_name_countlabels(fctx->name) && + fctx->type != dns_rdatatype_key && + fctx->type != dns_rdatatype_nsec && + fctx->type != dns_rdatatype_any && + fctx->type != dns_rdatatype_sig && + fctx->type != dns_rdatatype_rrsig) + { + LOCK(&fctx->lock); + resp = ISC_LIST_HEAD(fctx->resps); + if (resp != NULL) { + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_clone(&rdataset, + resp->rdataset); + } + if (dns_rdataset_isassociated(&sigrdataset) && + resp->sigrdataset != NULL) + { + dns_rdataset_clone(&sigrdataset, + resp->sigrdataset); + } + if (db != NULL) { + dns_db_attach(db, &resp->db); + } + if (node != NULL) { + dns_db_attachnode(db, node, + &resp->node); + } + dns_name_copy(fname, resp->foundname); + if (result == DNS_R_CNAME && + dns_rdataset_isassociated(&rdataset) && + fctx->type == dns_rdatatype_cname) + { + fixup_result = true; + } + clone_results(fctx); + UNLOCK(&fctx->lock); + goto cleanup; + } + UNLOCK(&fctx->lock); + } + /* * Any other result will *not* cause a failure in strict * mode, or cause minimization to be disabled in relaxed @@ -4345,9 +4444,21 @@ resume_qmin(void *arg) { fctx_try(fctx, true); cleanup: + if (node != NULL) { + dns_db_detachnode(db, &node); + } + if (db != NULL) { + dns_db_detach(&db); + } + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + if (dns_rdataset_isassociated(&sigrdataset)) { + dns_rdataset_disassociate(&sigrdataset); + } if (result != ISC_R_SUCCESS) { /* An error occurred, tear down whole fctx */ - fctx_done_unref(fctx, result); + fctx_done_unref(fctx, fixup_result ? ISC_R_SUCCESS : result); } fetchctx_detach(&fctx); } @@ -4659,6 +4770,7 @@ fctx_create(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name, dns_rdataset_init(&fctx->nameservers); dns_rdataset_init(&fctx->qminrrset); + dns_rdataset_init(&fctx->qminsigrrset); dns_rdataset_init(&fctx->nsrrset); fctx->start = isc_time_now(); @@ -10337,13 +10449,13 @@ fctx_minimize_qname(fetchctx_t *fctx) { } else if (fctx->qmin_labels < 35) { fctx->qmin_labels = 35; } else { - fctx->qmin_labels = nlabels; + fctx->qmin_labels = nlabels + 1; } } else if (fctx->qmin_labels > DNS_QMIN_MAXLABELS) { fctx->qmin_labels = DNS_NAME_MAXLABELS; } - if (fctx->qmin_labels < nlabels) { + if (fctx->qmin_labels <= nlabels) { dns_rdataset_t rdataset; dns_fixedname_t fixed; dns_name_t *fname = dns_fixedname_initname(&fixed); @@ -10377,10 +10489,18 @@ fctx_minimize_qname(fetchctx_t *fctx) { break; } break; - } while (fctx->qmin_labels < nlabels); + } while (fctx->qmin_labels <= nlabels); } - if (fctx->qmin_labels < nlabels) { + /* + * DS lookups come from the parent zone so we don't need to do a + * NS lookup at the QNAME. If the QTYPE is NS we are not leaking + * the type if we just do the final NS lookup. + */ + if (fctx->qmin_labels < nlabels || + (fctx->type != dns_rdatatype_ns && fctx->type != dns_rdatatype_ds && + fctx->qmin_labels == nlabels)) + { dns_name_copy(&name, fctx->qminname); fctx->qmintype = dns_rdatatype_ns; fctx->minimized = true;