]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Don't leak the original QTYPE to parent zone
authorMark Andrews <marka@isc.org>
Thu, 18 Jul 2024 03:27:23 +0000 (13:27 +1000)
committerMark Andrews <marka@isc.org>
Fri, 14 Mar 2025 01:01:26 +0000 (01:01 +0000)
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.

12 files changed:
bin/tests/system/chain/ns7/named.conf.in
bin/tests/system/cookie/tests.sh
bin/tests/system/dnssec/tests.sh
bin/tests/system/mirror/tests.sh
bin/tests/system/qmin/ans4/ans.py
bin/tests/system/qmin/tests.sh
bin/tests/system/resolver/tests.sh
bin/tests/system/rpzextra/tests_rpzextra.py
bin/tests/system/serve-stale/tests.sh
bin/tests/system/serve-stale/tests_sh_serve_stale.py
bin/tests/system/synthfromdnssec/tests.sh
lib/dns/resolver.c

index 0d95bc88c87f4367938ec47ca8edd0fa17c0c4bb..751521ba0cbcc2f91c4beb9dda3ac49185ff0805 100644 (file)
@@ -28,6 +28,7 @@ options {
        } except-from {
                "example";
        };
+       qname-minimization disabled;  // Regression test for GL #4652
 };
 
 trust-anchors { };
index babe6133efc8aa594b34b95e671e7efe32a1377e..73473885f09f08d553f990d2f5e70e7c97fa2656 100755 (executable)
@@ -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
index d3ee1bc8b4e8517135d02975fab7cf681c68305c..c389c5f6c7a107b64d334f9b12d863df089210ee 100644 (file)
@@ -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"
index 77a88f581f92b99ecb9e8e0a9c1fd40b16ee832d..2d02320006fa1611eb9bc708160a0ce57aaa8c79 100644 (file)
@@ -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))
 
index 517217aec11a52be250c8b96c8321ff60c837eff..f5fe3cc03f8c2578d3a9cbe7118b4e7062a07a35 100755 (executable)
@@ -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.
index 6cf8d2b9d07d8fd53f17fd0f9320ca73ccaf7a42..7973a60711858e35a2bd04056a3499143883041f 100755 (executable)
@@ -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
index 95e8617ad95c43a7a60b1ac532792b479855d8bd..2669021c62029412e06e6d259390b579130e2053 100755 (executable)
@@ -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
index 33b6d1529de25bd44de4e375a64e176df0dc971d..424830ed80a691600ac5815842d33c2f4eb5224b 100644 (file)
@@ -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.
index 42b94dbeb5a0ebce3b0782ce587415dad5e31a12..714adab7d7896fcc6c0f47edc8c65041ecca0ea0 100755 (executable)
@@ -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))
index 18b84e8b791c7bcdaaa2a18e99ccc0b06b9f0988..1c3177c4a49c35e16f7c20420d943c99dcce8f0e 100644 (file)
@@ -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*",
index e4b77e48e4e20ce6569b18a1e39943bf281c11ec..dac6a2cc9e7f42c21573cd466c8f672193fd342e 100644 (file)
@@ -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))
index c1f9579cacecb7521987834b73547e9b17092d99..9a5ba9b872c8613b965649398cef156c4c160248 100644 (file)
@@ -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;