]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
3443. [bug] The NOQNAME proof was not being returned from cached
authorMark Andrews <marka@isc.org>
Tue, 18 Dec 2012 22:55:02 +0000 (09:55 +1100)
committerMark Andrews <marka@isc.org>
Tue, 18 Dec 2012 22:55:02 +0000 (09:55 +1100)
                        insecure responses. [RT #21409]

24 files changed:
CHANGES
bin/tests/system/conf.sh.in
bin/tests/system/wildcard/clean.sh [new file with mode: 0644]
bin/tests/system/wildcard/ns1/named.conf [new file with mode: 0644]
bin/tests/system/wildcard/ns1/nsec.db.in [new file with mode: 0644]
bin/tests/system/wildcard/ns1/nsec3.db.in [new file with mode: 0644]
bin/tests/system/wildcard/ns1/private.nsec.db.in [new file with mode: 0644]
bin/tests/system/wildcard/ns1/private.nsec3.db.in [new file with mode: 0644]
bin/tests/system/wildcard/ns1/root.db.in [new file with mode: 0644]
bin/tests/system/wildcard/ns1/sign.sh [new file with mode: 0755]
bin/tests/system/wildcard/ns2/hints [new file with mode: 0644]
bin/tests/system/wildcard/ns2/named.conf [new file with mode: 0644]
bin/tests/system/wildcard/ns3/hints [new file with mode: 0644]
bin/tests/system/wildcard/ns3/named.conf [new file with mode: 0644]
bin/tests/system/wildcard/ns4/named.conf [new file with mode: 0644]
bin/tests/system/wildcard/setup.sh [new file with mode: 0644]
bin/tests/system/wildcard/tests.sh [new file with mode: 0644]
lib/dns/include/dns/nsec.h
lib/dns/include/dns/nsec3.h
lib/dns/include/dns/types.h
lib/dns/nsec.c
lib/dns/nsec3.c
lib/dns/resolver.c
lib/dns/validator.c

diff --git a/CHANGES b/CHANGES
index a0def2e0a51fdf0a0cf7a11686c5d33873b06ce2..8ee1d0b76a405bc307092abb7e2391420ae077ab 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+3443.  [bug]           The NOQNAME proof was not being returned from cached
+                       insecure responses. [RT #21409]
+
 3442.  [port]          Net::DNS 0.69 introduced a non backwards compatible
                        change. [RT #32216]
 
index f180b24b0879326b85d147fc44138de64c9435f9..8e5e7dc434346e044d0e48a8d20a5a9a486e6a1f 100644 (file)
@@ -61,7 +61,7 @@ SUBDIRS="acl allow_query addzone autosign builtin cacheclean checkconf
          inline limits logfileconfig lwresd masterfile masterformat metadata
          notify nsupdate pending pkcs11 redirect resolver rndc rpz rrsetorder
         rsabigexponent sortlist smartsign staticstub statistics stub
-        tkey tsig tsiggss unknown upforwd verify views xfer xferquota
+        tkey tsig tsiggss unknown upforwd verify views wildcard xfer xferquota
         zonechecks"
 
 # PERL will be an empty string if no perl interpreter was found.
diff --git a/bin/tests/system/wildcard/clean.sh b/bin/tests/system/wildcard/clean.sh
new file mode 100644 (file)
index 0000000..650092c
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007, 2010  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: clean.sh,v 1.1.2.1 2010/06/01 03:55:01 marka Exp $
+
+rm -f random.data
+rm -f ns*/named.run
+rm -f ns1/K*
+rm -f ns1/*.db
+rm -f ns1/*.signed
+rm -f ns1/dsset-*
+rm -f ns1/trusted.conf
+rm -f ns1/private.nsec.conf
+rm -f ns1/private.nsec3.conf
+rm -f ns1/signer.err
+rm -f */named.memstats
+rm -f dig.out.ns*.test*
diff --git a/bin/tests/system/wildcard/ns1/named.conf b/bin/tests/system/wildcard/ns1/named.conf
new file mode 100644 (file)
index 0000000..84b606b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.1.2.3 2010/06/01 07:04:49 marka Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.1;
+       notify-source 10.53.0.1;
+       transfer-source 10.53.0.1;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.1; };
+       listen-on-v6 { none; };
+       recursion no;
+       notify yes;
+       dnssec-enable yes;
+};
+
+zone "." { type master; file "root.db.signed"; };
+
+zone "nsec" { type master; file "nsec.db.signed"; };
+zone "private.nsec" { type master; file "private.nsec.db.signed"; };
+
+/*
+ * The contents of nsec3 and private.nsec3 are specially choosen to
+ * have seperate NSEC3 records for the "no qname proof" and the
+ * "closest encloser proof".
+ */
+zone "nsec3" { type master; file "nsec3.db.signed"; };
+zone "private.nsec3" { type master; file "private.nsec3.db.signed"; };
diff --git a/bin/tests/system/wildcard/ns1/nsec.db.in b/bin/tests/system/wildcard/ns1/nsec.db.in
new file mode 100644 (file)
index 0000000..5cdcace
--- /dev/null
@@ -0,0 +1,7 @@
+; Copyright
+$TTL 120
+@      SOA a.root-servers.nil. hostmaster.root-servers.nil. 1 1800 900 604800 86400
+@      NS  a.root-servers.nil.
+private NS  a.root-servers.nil.
+*.wild CNAME a.
+a.wild A   1.2.3.5
diff --git a/bin/tests/system/wildcard/ns1/nsec3.db.in b/bin/tests/system/wildcard/ns1/nsec3.db.in
new file mode 100644 (file)
index 0000000..5cdcace
--- /dev/null
@@ -0,0 +1,7 @@
+; Copyright
+$TTL 120
+@      SOA a.root-servers.nil. hostmaster.root-servers.nil. 1 1800 900 604800 86400
+@      NS  a.root-servers.nil.
+private NS  a.root-servers.nil.
+*.wild CNAME a.
+a.wild A   1.2.3.5
diff --git a/bin/tests/system/wildcard/ns1/private.nsec.db.in b/bin/tests/system/wildcard/ns1/private.nsec.db.in
new file mode 100644 (file)
index 0000000..e90b801
--- /dev/null
@@ -0,0 +1,6 @@
+; Copyright
+$TTL 120
+@      SOA a.root-servers.nil. hostmaster.root-servers.nil. 1 1800 900 604800 86400
+@      NS  a.root-servers.nil.
+*.wild CNAME a.
+a.wild A   1.2.3.5
diff --git a/bin/tests/system/wildcard/ns1/private.nsec3.db.in b/bin/tests/system/wildcard/ns1/private.nsec3.db.in
new file mode 100644 (file)
index 0000000..cc0e773
--- /dev/null
@@ -0,0 +1,8 @@
+; Copyright
+
+$TTL 120
+@      SOA a.root-servers.nil. hostmaster.root-servers.nil. 1 1800 900 604800 86400
+@      NS  a.root-servers.nil.
+b      A   1.2.3.4
+*.wild CNAME a.
+a.wild A   1.2.3.5
diff --git a/bin/tests/system/wildcard/ns1/root.db.in b/bin/tests/system/wildcard/ns1/root.db.in
new file mode 100644 (file)
index 0000000..eda13ea
--- /dev/null
@@ -0,0 +1,7 @@
+; Copyright
+$TTL 120
+@      SOA a.root-servers.nil hostmaster.root-servers.nil 1 1800 900 604800 86400
+@      NS  a.root-servers.nil
+a.root-servers.nil A 10.53.0.1
+nsec   NS  a.root-servers.nil
+nsec3  NS  a.root-servers.nil
diff --git a/bin/tests/system/wildcard/ns1/sign.sh b/bin/tests/system/wildcard/ns1/sign.sh
new file mode 100755 (executable)
index 0000000..a7b63f1
--- /dev/null
@@ -0,0 +1,120 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007, 2009, 2010  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: sign.sh,v 1.1.2.2 2010/06/01 06:38:47 marka Exp $
+
+SYSTEMTESTTOP=../..
+. $SYSTEMTESTTOP/conf.sh
+
+RANDFILE=../random.data
+dssets=
+
+zone=nsec.
+infile=nsec.db.in
+zonefile=nsec.db
+outfile=nsec.db.signed
+dssets="$dssets dsset-$zone"
+
+keyname1=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null` 
+keyname2=`$KEYGEN -f KSK -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null`
+
+cat $infile $keyname1.key $keyname2.key > $zonefile
+
+$SIGNER -r $RANDFILE -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err
+echo "I: signed $zone"
+
+zone=private.nsec.
+infile=private.nsec.db.in
+zonefile=private.nsec.db
+outfile=private.nsec.db.signed
+
+keyname1=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null` 
+keyname2=`$KEYGEN -f KSK -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null`
+
+cat $infile $keyname1.key $keyname2.key > $zonefile
+
+$SIGNER -r $RANDFILE -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err
+echo "I: signed $zone"
+
+grep -v '^;' $keyname2.key | $PERL -n -e '
+local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split;
+local $key = join("", @rest);
+print <<EOF
+trusted-keys {
+    "$dn" $flags $proto $alg "$key";
+};
+EOF
+' > private.nsec.conf
+
+zone=nsec3.
+infile=nsec3.db.in
+zonefile=nsec3.db
+outfile=nsec3.db.signed
+dssets="$dssets dsset-$zone"
+
+keyname1=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone $zone 2> /dev/null` 
+keyname2=`$KEYGEN -f KSK -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone $zone 2> /dev/null`
+
+cat $infile $keyname1.key $keyname2.key > $zonefile
+
+$SIGNER -r $RANDFILE -3 - -i 10 -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err
+echo "I: signed $zone"
+
+zone=private.nsec3.
+infile=private.nsec3.db.in
+zonefile=private.nsec3.db
+outfile=private.nsec3.db.signed
+
+keyname1=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone $zone 2> /dev/null` 
+keyname2=`$KEYGEN -f KSK -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone $zone 2> /dev/null`
+
+cat $infile $keyname1.key $keyname2.key > $zonefile
+
+$SIGNER -r $RANDFILE -3 - -i 10 -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err
+echo "I: signed $zone"
+
+grep -v '^;' $keyname2.key | $PERL -n -e '
+local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split;
+local $key = join("", @rest);
+print <<EOF
+trusted-keys {
+    "$dn" $flags $proto $alg "$key";
+};
+EOF
+' > private.nsec3.conf
+
+zone=.
+infile=root.db.in
+zonefile=root.db
+outfile=root.db.signed
+
+keyname1=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null`
+keyname2=`$KEYGEN -f KSK -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null`
+
+cat $infile $keyname1.key $keyname2.key $dssets >$zonefile
+
+$SIGNER -r $RANDFILE -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err
+echo "I: signed $zone"
+
+grep -v '^;' $keyname2.key | $PERL -n -e '
+local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split;
+local $key = join("", @rest);
+print <<EOF
+trusted-keys {
+    "$dn" $flags $proto $alg "$key";
+};
+EOF
+' > trusted.conf
diff --git a/bin/tests/system/wildcard/ns2/hints b/bin/tests/system/wildcard/ns2/hints
new file mode 100644 (file)
index 0000000..21b5bfa
--- /dev/null
@@ -0,0 +1,18 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: hints,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $
+
+. 0 NS ns.root-servers.nil.
+ns.root-servers.nil. 0 A 10.53.0.1
diff --git a/bin/tests/system/wildcard/ns2/named.conf b/bin/tests/system/wildcard/ns2/named.conf
new file mode 100644 (file)
index 0000000..0baec5f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.2;
+       notify-source 10.53.0.2;
+       transfer-source 10.53.0.2;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.2; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+};
+
+zone "." { type hint; file "hints"; };
diff --git a/bin/tests/system/wildcard/ns3/hints b/bin/tests/system/wildcard/ns3/hints
new file mode 100644 (file)
index 0000000..21b5bfa
--- /dev/null
@@ -0,0 +1,18 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: hints,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $
+
+. 0 NS ns.root-servers.nil.
+ns.root-servers.nil. 0 A 10.53.0.1
diff --git a/bin/tests/system/wildcard/ns3/named.conf b/bin/tests/system/wildcard/ns3/named.conf
new file mode 100644 (file)
index 0000000..32e0426
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.3;
+       notify-source 10.53.0.3;
+       transfer-source 10.53.0.3;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+};
+
+include "../ns1/trusted.conf";
+
+zone "." { type hint; file "hints"; };
diff --git a/bin/tests/system/wildcard/ns4/named.conf b/bin/tests/system/wildcard/ns4/named.conf
new file mode 100644 (file)
index 0000000..d0cb869
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.4;
+       notify-source 10.53.0.4;
+       transfer-source 10.53.0.4;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.4; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+       forward only;
+       forwarders { 10.53.0.2; };
+};
+
+include "../ns1/trusted.conf";
+include "../ns1/private.nsec.conf";
+include "../ns1/private.nsec3.conf";
diff --git a/bin/tests/system/wildcard/setup.sh b/bin/tests/system/wildcard/setup.sh
new file mode 100644 (file)
index 0000000..df21b29
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: setup.sh,v 1.1.2.1 2010/06/01 03:55:01 marka Exp $
+
+../../../tools/genrandom 400 random.data
+
+(cd ns1 && sh -e sign.sh)
diff --git a/bin/tests/system/wildcard/tests.sh b/bin/tests/system/wildcard/tests.sh
new file mode 100644 (file)
index 0000000..b037778
--- /dev/null
@@ -0,0 +1,136 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007, 2010  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: tests.sh,v 1.1.2.3 2010/06/01 06:57:31 marka Exp $
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+status=0
+n=0
+
+rm -f dig.out.*
+
+DIGOPTS="+tcp +noadd +nosea +nostat +nocmd +dnssec -p 5300"
+
+n=`expr $n + 1`
+echo "I: checking that NSEC wildcard non-existance proof is returned auth ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.nsec +norec @10.53.0.1 > dig.out.ns1.test$n || ret=1
+grep -i 'a\.wild\.nsec\..*NSEC.*nsec\..*NSEC'  dig.out.ns1.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that NSEC wildcard non-existance proof is returned non-validating ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.nsec @10.53.0.2 > dig.out.ns2.test$n || ret=1
+grep -i 'a\.wild\.nsec\..*NSEC.*nsec\..*NSEC'  dig.out.ns2.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns2.test$n > /dev/null && ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that NSEC wildcard non-existance proof is returned validating ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.nsec @10.53.0.3 > dig.out.ns3.test$n || ret=1
+grep -i 'a\.wild\.nsec\..*NSEC.*nsec\..*NSEC'  dig.out.ns3.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns3.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that returned NSEC wildcard non-existance proof validates ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.nsec @10.53.0.4 > dig.out.ns4.test$n || ret=1
+grep -i 'a\.wild\.nsec\..*NSEC.*nsec\..*NSEC'  dig.out.ns4.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns4.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that NSEC wildcard non-existance proof is returned private, validating ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.private.nsec @10.53.0.3 > dig.out.ns3.test$n || ret=1
+grep -i 'a\.wild\.private\.nsec\..*NSEC.*private\.nsec\..*NSEC'  dig.out.ns3.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns3.test$n > /dev/null && ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that returned NSEC wildcard non-existance proof for private zone validates ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.private.nsec @10.53.0.4 > dig.out.ns4.test$n || ret=1
+grep -i 'a\.wild\.private\.nsec\..*NSEC.*private\.nsec\..*NSEC'  dig.out.ns4.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns4.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that NSEC3 wildcard non-existance proof is returned auth ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.nsec3 +norec @10.53.0.1 > dig.out.ns1.test$n || ret=1
+grep -i 'O3TJ8D9AJ54CBTFCQCJ3QK49CH7SF6H9\.nsec3\..*V5DLFB6UJNHR94LQ61FO607KGK12H88A'  dig.out.ns1.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that NSEC3 wildcard non-existance proof is returned non-validating ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.nsec3 @10.53.0.2 > dig.out.ns2.test$n || ret=1
+grep -i 'O3TJ8D9AJ54CBTFCQCJ3QK49CH7SF6H9\.nsec3\..*V5DLFB6UJNHR94LQ61FO607KGK12H88A'  dig.out.ns2.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns2.test$n > /dev/null && ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that NSEC3 wildcard non-existance proof is returned validating ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.nsec3 @10.53.0.3 > dig.out.ns3.test$n || ret=1
+grep -i 'O3TJ8D9AJ54CBTFCQCJ3QK49CH7SF6H9\.nsec3\..*V5DLFB6UJNHR94LQ61FO607KGK12H88A'  dig.out.ns3.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns3.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that returned NSEC3 wildcard non-existance proof validates ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.nsec3 @10.53.0.4 > dig.out.ns4.test$n || ret=1
+grep -i 'O3TJ8D9AJ54CBTFCQCJ3QK49CH7SF6H9\.nsec3\..*V5DLFB6UJNHR94LQ61FO607KGK12H88A'  dig.out.ns4.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns4.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that NSEC3 wildcard non-existance proof is returned private, validating ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.private.nsec3 @10.53.0.3 > dig.out.ns3.test$n || ret=1
+grep -i 'UDBSP4R8OUOT6HSO39VD8B5LMOSHRD5N\.private\.nsec3\..*NSEC3.*ASDRUIB7GO00OR92S5OUGI404LT27RNU' dig.out.ns3.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns3.test$n > /dev/null && ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: checking that returned NSEC3 wildcard non-existance proof for private zone validates ($n)"
+ret=0
+$DIG $DIGOPTS a b.wild.private.nsec3 @10.53.0.4 > dig.out.ns4.test$n || ret=1
+grep -i 'UDBSP4R8OUOT6HSO39VD8B5LMOSHRD5N\.private\.nsec3\..*NSEC3.*ASDRUIB7GO00OR92S5OUGI404LT27RNU' dig.out.ns4.test$n > /dev/null || ret=1
+grep -i 'flags:.* ad[ ;]'  dig.out.ns4.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:exit status: $status"
+exit $status
index bdcd2eeccf2bed36611a19a206a9cf964b2c3df9..62c1c94c83b8da2f3c1c889949a92a712bbffc11 100644 (file)
@@ -98,6 +98,19 @@ dns_nsec_isset(const unsigned char *array, unsigned int type);
  * Test if the corresponding 'type' bit is set in 'array'.
  */
 
+isc_result_t
+dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name, 
+                       dns_name_t *nsecname, dns_rdataset_t *nsecset, 
+                       isc_boolean_t *exists, isc_boolean_t *data, 
+                       dns_name_t *wild, dns_nseclog_t log, void *arg);
+/*%
+ * Return ISC_R_SUCCESS if we can determine that the name doesn't exist
+ * or we can determine whether there is data or not at the name.
+ * If the name does not exist return the wildcard name.
+ *
+ * Return ISC_R_IGNORE when the NSEC is not the appropriate one.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_NSEC_H */
index 1b89cceb53348eb3a96fb727e707359003e66d6f..8cf14dc1e6339855f9abbcaada887141929ff891 100644 (file)
@@ -248,6 +248,14 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver,
  * Mark NSEC3PARAM for deletion.
  */
 
+isc_result_t
+dns_nsec3_noexistnodata(dns_rdatatype_t type, dns_name_t* name, 
+                        dns_name_t *nsec3name, dns_rdataset_t *nsec3set, 
+                        dns_name_t *zonename, isc_boolean_t *exists, 
+                        isc_boolean_t *data, isc_boolean_t *optout, 
+                        isc_boolean_t *unknown, isc_boolean_t *setclosest, 
+                        isc_boolean_t *setnearest, dns_name_t *closest, 
+                        dns_name_t *nearest, dns_nseclog_t logit, void *arg);
 
 ISC_LANG_ENDDECLS
 
index 0874faad96dfb85a76c571cae0c7b8b6e495872b..2aa7459986efd00d9d3203ba2d7655cb1ce36082 100644 (file)
@@ -400,4 +400,7 @@ typedef isc_boolean_t
 typedef isc_result_t
 (*dns_deserializefunc_t)(void *, FILE *, off_t);
 
+typedef void
+(*dns_nseclog_t)(void *val, int , const char *, ...);
+
 #endif /* DNS_TYPES_H */
index 69207d09dec40324dd4e48c1e9986df59306e481..5ae1cf8739194029c94ac960767b53cb93a86787 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <config.h>
 
+#include <isc/log.h>
 #include <isc/string.h>
 #include <isc/util.h>
 
@@ -290,3 +291,161 @@ dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version,
        }
        return (result);
 }
+
+/*%
+ * Return ISC_R_SUCCESS if we can determine that the name doesn't exist
+ * or we can determine whether there is data or not at the name.
+ * If the name does not exist return the wildcard name.
+ *
+ * Return ISC_R_IGNORE when the NSEC is not the appropriate one.
+ */
+isc_result_t
+dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name,
+                      dns_name_t *nsecname, dns_rdataset_t *nsecset,
+                      isc_boolean_t *exists, isc_boolean_t *data,
+                      dns_name_t *wild, dns_nseclog_t logit, void *arg)
+{
+       int order;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       isc_result_t result;
+       dns_namereln_t relation;
+       unsigned int olabels, nlabels, labels;
+       dns_rdata_nsec_t nsec;
+       isc_boolean_t atparent;
+       isc_boolean_t ns;
+       isc_boolean_t soa;
+
+       REQUIRE(exists != NULL);
+       REQUIRE(data != NULL);
+       REQUIRE(nsecset != NULL &&
+               nsecset->type == dns_rdatatype_nsec);
+
+       result = dns_rdataset_first(nsecset);
+       if (result != ISC_R_SUCCESS) {
+               (*logit)(arg, ISC_LOG_DEBUG(3), "failure processing NSEC set");
+               return (result);
+       }
+       dns_rdataset_current(nsecset, &rdata);
+
+       (*logit)(arg, ISC_LOG_DEBUG(3), "looking for relevant nsec");
+       relation = dns_name_fullcompare(name, nsecname, &order, &olabels);
+
+       if (order < 0) {
+               /*
+                * The name is not within the NSEC range.
+                */
+               (*logit)(arg, ISC_LOG_DEBUG(3),
+                             "NSEC does not cover name, before NSEC");
+               return (ISC_R_IGNORE);
+       }
+
+       if (order == 0) {
+               /*
+                * The names are the same.   If we are validating "."
+                * then atparent should not be set as there is no parent.
+                */
+               atparent = (olabels != 1) && dns_rdatatype_atparent(type);
+               ns = dns_nsec_typepresent(&rdata, dns_rdatatype_ns);
+               soa = dns_nsec_typepresent(&rdata, dns_rdatatype_soa);
+               if (ns && !soa) {
+                       if (!atparent) {
+                               /*
+                                * This NSEC record is from somewhere higher in
+                                * the DNS, and at the parent of a delegation.
+                                * It can not be legitimately used here.
+                                */
+                               (*logit)(arg, ISC_LOG_DEBUG(3),
+                                             "ignoring parent nsec");
+                               return (ISC_R_IGNORE);
+                       }
+               } else if (atparent && ns && soa) {
+                       /*
+                        * This NSEC record is from the child.
+                        * It can not be legitimately used here.
+                        */
+                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                     "ignoring child nsec");
+                       return (ISC_R_IGNORE);
+               }
+               if (type == dns_rdatatype_cname || type == dns_rdatatype_nxt ||
+                   type == dns_rdatatype_nsec || type == dns_rdatatype_key ||
+                   !dns_nsec_typepresent(&rdata, dns_rdatatype_cname)) {
+                       *exists = ISC_TRUE;
+                       *data = dns_nsec_typepresent(&rdata, type);
+                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                     "nsec proves name exists (owner) data=%d",
+                                     *data);
+                       return (ISC_R_SUCCESS);
+               }
+               (*logit)(arg, ISC_LOG_DEBUG(3), "NSEC proves CNAME exists");
+               return (ISC_R_IGNORE);
+       }
+
+       if (relation == dns_namereln_subdomain &&
+           dns_nsec_typepresent(&rdata, dns_rdatatype_ns) &&
+           !dns_nsec_typepresent(&rdata, dns_rdatatype_soa))
+       {
+               /*
+                * This NSEC record is from somewhere higher in
+                * the DNS, and at the parent of a delegation.
+                * It can not be legitimately used here.
+                */
+               (*logit)(arg, ISC_LOG_DEBUG(3), "ignoring parent nsec");
+               return (ISC_R_IGNORE);
+       }
+
+       result = dns_rdata_tostruct(&rdata, &nsec, NULL);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       relation = dns_name_fullcompare(&nsec.next, name, &order, &nlabels);
+       if (order == 0) {
+               dns_rdata_freestruct(&nsec);
+               (*logit)(arg, ISC_LOG_DEBUG(3),
+                             "ignoring nsec matches next name");
+               return (ISC_R_IGNORE);
+       }
+
+       if (order < 0 && !dns_name_issubdomain(nsecname, &nsec.next)) {
+               /*
+                * The name is not within the NSEC range.
+                */
+               dns_rdata_freestruct(&nsec);
+               (*logit)(arg, ISC_LOG_DEBUG(3),
+                           "ignoring nsec because name is past end of range");
+               return (ISC_R_IGNORE);
+       }
+
+       if (order > 0 && relation == dns_namereln_subdomain) {
+               (*logit)(arg, ISC_LOG_DEBUG(3),
+                             "nsec proves name exist (empty)");
+               dns_rdata_freestruct(&nsec);
+               *exists = ISC_TRUE;
+               *data = ISC_FALSE;
+               return (ISC_R_SUCCESS);
+       }
+       if (wild != NULL) {
+               dns_name_t common;
+               dns_name_init(&common, NULL);
+               if (olabels > nlabels) {
+                       labels = dns_name_countlabels(nsecname);
+                       dns_name_getlabelsequence(nsecname, labels - olabels,
+                                                 olabels, &common);
+               } else {
+                       labels = dns_name_countlabels(&nsec.next);
+                       dns_name_getlabelsequence(&nsec.next, labels - nlabels,
+                                                 nlabels, &common);
+               }
+               result = dns_name_concatenate(dns_wildcardname, &common,
+                                              wild, NULL);
+               if (result != ISC_R_SUCCESS) {
+                       dns_rdata_freestruct(&nsec);
+                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                   "failure generating wildcard name");
+                       return (result);
+               }
+       }
+       dns_rdata_freestruct(&nsec);
+       (*logit)(arg, ISC_LOG_DEBUG(3), "nsec range ok");
+       *exists = ISC_FALSE;
+       return (ISC_R_SUCCESS);
+}
index b63e11787fd7498938290632395bb79983a9171e..935f515d23ed83ac2d3d0e72a1aa539f8e56508e 100644 (file)
@@ -22,6 +22,7 @@
 #include <isc/buffer.h>
 #include <isc/hex.h>
 #include <isc/iterated_hash.h>
+#include <isc/log.h>
 #include <isc/string.h>
 #include <isc/util.h>
 
@@ -1802,3 +1803,285 @@ dns_nsec3_maxiterations(dns_db_t *db, dns_dbversion_t *version,
                dns_rdataset_disassociate(&rdataset);
        return (result);
 }
+
+isc_result_t
+dns_nsec3_noexistnodata(dns_rdatatype_t type, dns_name_t* name,
+                       dns_name_t *nsec3name, dns_rdataset_t *nsec3set,
+                       dns_name_t *zonename, isc_boolean_t *exists,
+                       isc_boolean_t *data, isc_boolean_t *optout,
+                       isc_boolean_t *unknown, isc_boolean_t *setclosest,
+                       isc_boolean_t *setnearest, dns_name_t *closest,
+                       dns_name_t *nearest, dns_nseclog_t logit, void *arg)
+{
+       char namebuf[DNS_NAME_FORMATSIZE];
+       dns_fixedname_t fzone;
+       dns_fixedname_t qfixed;
+       dns_label_t hashlabel;
+       dns_name_t *qname;
+       dns_name_t *zone;
+       dns_rdata_nsec3_t nsec3;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       int order;
+       int scope;
+       isc_boolean_t atparent;
+       isc_boolean_t first;
+       isc_boolean_t ns;
+       isc_boolean_t soa;
+       isc_buffer_t buffer;
+       isc_result_t answer = ISC_R_IGNORE;
+       isc_result_t result;
+       unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+       unsigned char owner[NSEC3_MAX_HASH_LENGTH];
+       unsigned int length;
+       unsigned int qlabels;
+       unsigned int zlabels;
+
+       REQUIRE((exists == NULL && data == NULL) ||
+               (exists != NULL && data != NULL));
+       REQUIRE(nsec3set != NULL && nsec3set->type == dns_rdatatype_nsec3);
+       REQUIRE((setclosest == NULL && closest == NULL) ||
+               (setclosest != NULL && closest != NULL));
+       REQUIRE((setnearest == NULL && nearest == NULL) ||
+               (setnearest != NULL && nearest != NULL));
+
+       result = dns_rdataset_first(nsec3set);
+       if (result != ISC_R_SUCCESS) {
+               (*logit)(arg, ISC_LOG_DEBUG(3), "failure processing NSEC3 set");
+               return (result);
+       }
+
+       dns_rdataset_current(nsec3set, &rdata);
+
+       result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       (*logit)(arg, ISC_LOG_DEBUG(3), "looking for relevant NSEC3");
+
+       dns_fixedname_init(&fzone);
+       zone = dns_fixedname_name(&fzone);
+       zlabels = dns_name_countlabels(nsec3name);
+
+       /*
+        * NSEC3 records must have two or more labels to be valid.
+        */
+       if (zlabels < 2)
+               return (ISC_R_IGNORE);
+
+       /*
+        * Strip off the NSEC3 hash to get the zone.
+        */
+       zlabels--;
+       dns_name_split(nsec3name, zlabels, NULL, zone);
+
+       /*
+        * If not below the zone name we can ignore this record.
+        */
+       if (!dns_name_issubdomain(name, zone))
+               return (ISC_R_IGNORE);
+
+       /*
+        * Is this zone the same or deeper than the current zone?
+        */
+       if (dns_name_countlabels(zonename) == 0 ||
+           dns_name_issubdomain(zone, zonename))
+               dns_name_copy(zone, zonename, NULL);
+
+       if (!dns_name_equal(zone, zonename))
+               return (ISC_R_IGNORE);
+
+       /*
+        * Are we only looking for the most enclosing zone?
+        */
+       if (exists == NULL || data == NULL)
+               return (ISC_R_SUCCESS);
+
+       /*
+        * Only set unknown once we are sure that this NSEC3 is from
+        * the deepest covering zone.
+        */
+       if (!dns_nsec3_supportedhash(nsec3.hash)) {
+               if (unknown != NULL)
+                       *unknown = ISC_TRUE;
+               return (ISC_R_IGNORE);
+       }
+
+       /*
+        * Recover the hash from the first label.
+        */
+       dns_name_getlabel(nsec3name, 0, &hashlabel);
+       isc_region_consume(&hashlabel, 1);
+       isc_buffer_init(&buffer, owner, sizeof(owner));
+       result = isc_base32hex_decoderegion(&hashlabel, &buffer);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       /*
+        * The hash lengths should match.  If not ignore the record.
+        */
+       if (isc_buffer_usedlength(&buffer) != nsec3.next_length)
+               return (ISC_R_IGNORE);
+
+       /*
+        * Work out what this NSEC3 covers.
+        * Inside (<0) or outside (>=0).
+        */
+       scope = memcmp(owner, nsec3.next, nsec3.next_length);
+
+       /*
+        * Prepare to compute all the hashes.
+        */
+       dns_fixedname_init(&qfixed);
+       qname = dns_fixedname_name(&qfixed);
+       dns_name_downcase(name, qname, NULL);
+       qlabels = dns_name_countlabels(qname);
+       first = ISC_TRUE;
+
+       while (qlabels >= zlabels) {
+               length = isc_iterated_hash(hash, nsec3.hash, nsec3.iterations,
+                                          nsec3.salt, nsec3.salt_length,
+                                          qname->ndata, qname->length);
+               /*
+                * The computed hash length should match.
+                */
+               if (length != nsec3.next_length) {
+                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                "ignoring NSEC bad length %u vs %u",
+                                length, nsec3.next_length);
+                       return (ISC_R_IGNORE);
+               }
+
+               order = memcmp(hash, owner, length);
+               if (first && order == 0) {
+                       /*
+                        * The hashes are the same.
+                        */
+                       atparent = dns_rdatatype_atparent(type);
+                       ns = dns_nsec3_typepresent(&rdata, dns_rdatatype_ns);
+                       soa = dns_nsec3_typepresent(&rdata, dns_rdatatype_soa);
+                       if (ns && !soa) {
+                               if (!atparent) {
+                                       /*
+                                        * This NSEC3 record is from somewhere
+                                        * higher in the DNS, and at the
+                                        * parent of a delegation. It can not
+                                        * be legitimately used here.
+                                        */
+                                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                                "ignoring parent NSEC3");
+                                       return (ISC_R_IGNORE);
+                               }
+                       } else if (atparent && ns && soa) {
+                               /*
+                                * This NSEC3 record is from the child.
+                                * It can not be legitimately used here.
+                                */
+                               (*logit)(arg, ISC_LOG_DEBUG(3),
+                                        "ignoring child NSEC3");
+                               return (ISC_R_IGNORE);
+                       }
+                       if (type == dns_rdatatype_cname ||
+                           type == dns_rdatatype_nxt ||
+                           type == dns_rdatatype_nsec ||
+                           type == dns_rdatatype_key ||
+                           !dns_nsec3_typepresent(&rdata, dns_rdatatype_cname)) {
+                               *exists = ISC_TRUE;
+                               *data = dns_nsec3_typepresent(&rdata, type);
+                               (*logit)(arg, ISC_LOG_DEBUG(3),
+                                        "NSEC3 proves name exists (owner) "
+                                        "data=%d", *data);
+                               return (ISC_R_SUCCESS);
+                       }
+                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                "NSEC3 proves CNAME exists");
+                       return (ISC_R_IGNORE);
+               }
+
+               if (order == 0 &&
+                   dns_nsec3_typepresent(&rdata, dns_rdatatype_ns) &&
+                   !dns_nsec3_typepresent(&rdata, dns_rdatatype_soa))
+               {
+                       /*
+                        * This NSEC3 record is from somewhere higher in
+                        * the DNS, and at the parent of a delegation.
+                        * It can not be legitimately used here.
+                        */
+                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                "ignoring parent NSEC3");
+                       return (ISC_R_IGNORE);
+               }
+
+               /*
+                * Potential closest encloser.
+                */
+               if (order == 0) {
+                       if (closest != NULL &&
+                           (dns_name_countlabels(closest) == 0 ||
+                            dns_name_issubdomain(qname, closest)) &&
+                           !dns_nsec3_typepresent(&rdata, dns_rdatatype_ds) &&
+                           !dns_nsec3_typepresent(&rdata, dns_rdatatype_dname) &&
+                           (dns_nsec3_typepresent(&rdata, dns_rdatatype_soa) ||
+                            !dns_nsec3_typepresent(&rdata, dns_rdatatype_ns)))
+                       {
+
+                               dns_name_format(qname, namebuf,
+                                               sizeof(namebuf));
+                               (*logit)(arg, ISC_LOG_DEBUG(3),
+                                        "NSEC3 indicates potential closest "
+                                        "encloser: '%s'", namebuf);
+                               dns_name_copy(qname, closest, NULL);
+                               *setclosest = ISC_TRUE;
+                       }
+                       dns_name_format(qname, namebuf, sizeof(namebuf));
+                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                "NSEC3 at super-domain %s", namebuf);
+                       return (answer);
+               }
+
+               /*
+                * Find if the name does not exist.
+                *
+                * We continue as we need to find the name closest to the
+                * closest encloser that doesn't exist.
+                *
+                * We also need to continue to ensure that we are not
+                * proving the non-existence of a record in a sub-zone.
+                * If that would be the case we will return ISC_R_IGNORE
+                * above.
+                */
+               if ((scope < 0 && order > 0 &&
+                    memcmp(hash, nsec3.next, length) < 0) ||
+                   (scope >= 0 && (order > 0 ||
+                                   memcmp(hash, nsec3.next, length) < 0)))
+               {
+                       char namebuf[DNS_NAME_FORMATSIZE];
+
+                       dns_name_format(qname, namebuf, sizeof(namebuf));
+                       (*logit)(arg, ISC_LOG_DEBUG(3), "NSEC3 proves "
+                                "name does not exist: '%s'", namebuf);
+                       if (nearest != NULL &&
+                           (dns_name_countlabels(nearest) == 0 ||
+                            dns_name_issubdomain(nearest, qname))) {
+                               dns_name_copy(qname, nearest, NULL);
+                               *setnearest = ISC_TRUE;
+                       }
+
+                       *exists = ISC_FALSE;
+                       *data = ISC_FALSE;
+                       if (optout != NULL) {
+                               if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0)
+                                       (*logit)(arg, ISC_LOG_DEBUG(3),
+                                                "NSEC3 indicates optout");
+                               *optout =
+                                   ISC_TF(nsec3.flags & DNS_NSEC3FLAG_OPTOUT);
+                       }
+                       answer = ISC_R_SUCCESS;
+               }
+
+               qlabels--;
+               if (qlabels > 0)
+                       dns_name_split(qname, qlabels, NULL, qname);
+               first = ISC_FALSE;
+       }
+       return (answer);
+}
index 4ffbec35bd86e8c35f28bbfd85254d8e39ec238f..5c76ef684a37ca1809dcaf4d5e03212654a63d12 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <config.h>
 
+#include <isc/log.h>
 #include <isc/platform.h>
 #include <isc/print.h>
 #include <isc/string.h>
@@ -43,6 +44,8 @@
 #include <dns/log.h>
 #include <dns/message.h>
 #include <dns/ncache.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
 #include <dns/opcode.h>
 #include <dns/peer.h>
 #include <dns/rbt.h>
@@ -76,7 +79,7 @@
                                      DNS_LOGCATEGORY_RESOLVER, \
                                      DNS_LOGMODULE_RESOLVER, \
                                      ISC_LOG_DEBUG(3), \
-                                     "fctx %p(%s'): %s", fctx, fctx->info, (m))
+                                     "fctx %p(%s): %s", fctx, fctx->info, (m))
 #define FCTXTRACE2(m1, m2) \
                        isc_log_write(dns_lctx, \
                                      DNS_LOGCATEGORY_RESOLVER, \
@@ -475,6 +478,9 @@ static void validated(isc_task_t *task, isc_event_t *event);
 static isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t locked);
 static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
                    isc_result_t reason, badnstype_t badtype);
+static inline isc_result_t findnoqname(fetchctx_t *fctx, dns_name_t *name,
+                                      dns_rdatatype_t type,
+                                      dns_name_t **noqname);
 
 /*%
  * Increment resolver-related statistics counters.
@@ -4252,7 +4258,6 @@ validated(isc_task_t *task, isc_event_t *event) {
        FCTXTRACE("validation OK");
 
        if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) {
-
                result = dns_rdataset_addnoqname(vevent->rdataset,
                                   vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]);
                RUNTIME_CHECK(result == ISC_R_SUCCESS);
@@ -4263,6 +4268,18 @@ validated(isc_task_t *task, isc_event_t *event) {
                                 vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER]);
                        RUNTIME_CHECK(result == ISC_R_SUCCESS);
                }
+       } else if (vevent->rdataset->trust == dns_trust_answer &&
+                  vevent->rdataset->type != dns_rdatatype_rrsig)
+       {
+               isc_result_t tresult;
+               dns_name_t *noqname = NULL;
+               tresult = findnoqname(fctx, vevent->name,
+                                     vevent->rdataset->type, &noqname);
+               if (tresult == ISC_R_SUCCESS && noqname != NULL) {
+                       tresult = dns_rdataset_addnoqname(vevent->rdataset,
+                                                         noqname);
+                       RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
+               }
        }
 
        /*
@@ -4403,6 +4420,133 @@ validated(isc_task_t *task, isc_event_t *event) {
        isc_event_free(&event);
 }
 
+static void
+log(void *arg, int level, const char *fmt, ...) {
+       char msgbuf[2048];
+       va_list args;
+       fetchctx_t *fctx = arg;
+
+       va_start(args, fmt);
+       vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
+       va_end(args);
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+                     DNS_LOGMODULE_RESOLVER, level,
+                     "fctx %p(%s): %s", fctx, fctx->info, msgbuf);
+}
+
+static inline isc_result_t
+findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
+           dns_name_t **noqname)
+{
+       dns_rdataset_t *nrdataset, *next, *sigrdataset;
+       dns_rdata_rrsig_t rrsig;
+       isc_result_t result;
+       unsigned int labels;
+       dns_section_t section;
+       dns_name_t *zonename;
+       dns_fixedname_t fzonename;
+       dns_name_t *closest;
+       dns_fixedname_t fclosest;
+       dns_name_t *nearest;
+       dns_fixedname_t fnearest;
+
+       FCTXTRACE("findnoqname");
+
+       REQUIRE(noqname != NULL && *noqname == NULL);
+
+       /*
+        * Find the SIG for this rdataset, if we have it.
+        */
+       for (sigrdataset = ISC_LIST_HEAD(name->list);
+            sigrdataset != NULL;
+            sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
+               if (sigrdataset->type == dns_rdatatype_rrsig &&
+                   sigrdataset->covers == type)
+                       break;
+       }
+
+       if (sigrdataset == NULL)
+               return (ISC_R_NOTFOUND);
+
+       labels = dns_name_countlabels(name);
+
+       for (result = dns_rdataset_first(sigrdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(sigrdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+               dns_rdataset_current(sigrdataset, &rdata);
+               result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+               /* Wildcard has rrsig.labels < labels - 1. */
+               if (rrsig.labels + 1 >= labels)
+                       continue;
+               break;
+       }
+
+       if (result == ISC_R_NOMORE)
+               return (ISC_R_NOTFOUND);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       dns_fixedname_init(&fzonename);
+       zonename = dns_fixedname_name(&fzonename);
+       dns_fixedname_init(&fclosest);
+       closest = dns_fixedname_name(&fclosest);
+       dns_fixedname_init(&fnearest);
+       nearest = dns_fixedname_name(&fnearest);
+
+#define NXND(x) ((x) == ISC_R_SUCCESS)
+
+       section = DNS_SECTION_AUTHORITY;
+       for (result = dns_message_firstname(fctx->rmessage, section);
+            result == ISC_R_SUCCESS;
+            result = dns_message_nextname(fctx->rmessage, section)) {
+               dns_name_t *nsec = NULL;
+               dns_message_currentname(fctx->rmessage, section, &nsec);
+               for (nrdataset = ISC_LIST_HEAD(nsec->list);
+                     nrdataset != NULL; nrdataset = next) {
+                       isc_boolean_t data = ISC_FALSE, exists = ISC_FALSE;
+                       isc_boolean_t optout = ISC_FALSE, unknown = ISC_FALSE;
+                       isc_boolean_t setclosest = ISC_FALSE;
+                       isc_boolean_t setnearest = ISC_FALSE;
+                       char namebuf[DNS_NAME_FORMATSIZE];
+
+                       next = ISC_LIST_NEXT(nrdataset, link);
+                       if (nrdataset->type != dns_rdatatype_nsec &&
+                           nrdataset->type != dns_rdatatype_nsec3)
+                               continue;
+                       dns_name_format(nsec, namebuf, sizeof(namebuf));
+                       if (nrdataset->type == dns_rdatatype_nsec &&
+                           NXND(dns_nsec_noexistnodata(type, name, nsec,
+                                                       nrdataset, &exists,
+                                                       &data, NULL, log,
+                                                       fctx)))
+                       {
+                               if (!exists)
+                                       *noqname = nsec;
+                       }
+
+                       if (nrdataset->type == dns_rdatatype_nsec3 &&
+                           NXND(dns_nsec3_noexistnodata(type, name, nsec,
+                                                        nrdataset, zonename,
+                                                        &exists, &data,
+                                                        &optout, &unknown,
+                                                        &setclosest,
+                                                        &setnearest,
+                                                        closest, nearest,
+                                                        log, fctx)))
+                       {
+                               if (!exists && setnearest)
+                                       *noqname = nsec;
+                       }
+               }
+       }
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+       return (result);
+}
+
 static inline isc_result_t
 cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
           isc_stdtime_t now)
@@ -4535,6 +4679,17 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
                if (rdataset->ttl > res->view->maxcachettl)
                        rdataset->ttl = res->view->maxcachettl;
 
+               /*
+                * Find the SIG for this rdataset, if we have it.
+                */
+               for (sigrdataset = ISC_LIST_HEAD(name->list);
+                    sigrdataset != NULL;
+                    sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
+                       if (sigrdataset->type == dns_rdatatype_rrsig &&
+                           sigrdataset->covers == rdataset->type)
+                               break;
+               }
+
                /*
                 * If this RRset is in a secure domain, is in bailiwick,
                 * and is not glue, attempt DNSSEC validation.  (We do not
@@ -4555,16 +4710,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
                         */
                        if (rdataset->type == dns_rdatatype_rrsig)
                                continue;
-                       /*
-                        * Find the SIG for this rdataset, if we have it.
-                        */
-                       for (sigrdataset = ISC_LIST_HEAD(name->list);
-                            sigrdataset != NULL;
-                            sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
-                               if (sigrdataset->type == dns_rdatatype_rrsig &&
-                                   sigrdataset->covers == rdataset->type)
-                                       break;
-                       }
+
                        if (sigrdataset == NULL) {
                                if (!ANSWER(rdataset) && need_validation) {
                                        /*
@@ -4725,6 +4871,21 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
                                options = DNS_DBADD_FORCE;
                        } else
                                options = 0;
+
+                       if (ANSWER(rdataset) &&
+                          rdataset->type != dns_rdatatype_rrsig) {
+                               isc_result_t tresult;
+                               dns_name_t *noqname = NULL;
+                               tresult = findnoqname(fctx, name,
+                                                     rdataset->type, &noqname);
+                               if (tresult == ISC_R_SUCCESS &&
+                                   noqname != NULL) {
+                                       tresult = dns_rdataset_addnoqname(
+                                                           rdataset, noqname);
+                                       RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
+                               }
+                       }
+
                        /*
                         * Now we can add the rdataset.
                         */
@@ -4733,6 +4894,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
                                                    rdataset,
                                                    options,
                                                    addedrdataset);
+
                        if (result == DNS_R_UNCHANGED) {
                                if (ANSWER(rdataset) &&
                                    ardataset != NULL &&
index f85125a19965669798241d252385df39f4bd61d1..9dcb9ad179f694724f8d5a21d79b25957a2daec0 100644 (file)
@@ -157,7 +157,7 @@ validator_logv(dns_validator_t *val, isc_logcategory_t *category,
      ISC_FORMAT_PRINTF(5, 0);
 
 static void
-validator_log(dns_validator_t *val, int level, const char *fmt, ...)
+validator_log(void *val, int level, const char *fmt, ...)
      ISC_FORMAT_PRINTF(3, 4);
 
 static void
@@ -843,452 +843,6 @@ cnamevalidated(isc_task_t *task, isc_event_t *event) {
                destroy(val);
 }
 
-/*%
- * Return ISC_R_SUCCESS if we can determine that the name doesn't exist
- * or we can determine whether there is data or not at the name.
- * If the name does not exist return the wildcard name.
- *
- * Return ISC_R_IGNORE when the NSEC is not the appropriate one.
- */
-static isc_result_t
-nsecnoexistnodata(dns_validator_t *val, dns_name_t *name, dns_name_t *nsecname,
-                 dns_rdataset_t *nsecset, isc_boolean_t *exists,
-                 isc_boolean_t *data, dns_name_t *wild)
-{
-       int order;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
-       isc_result_t result;
-       dns_namereln_t relation;
-       unsigned int olabels, nlabels, labels;
-       dns_rdata_nsec_t nsec;
-       isc_boolean_t atparent;
-       isc_boolean_t ns;
-       isc_boolean_t soa;
-
-       REQUIRE(exists != NULL);
-       REQUIRE(data != NULL);
-       REQUIRE(nsecset != NULL &&
-               nsecset->type == dns_rdatatype_nsec);
-
-       result = dns_rdataset_first(nsecset);
-       if (result != ISC_R_SUCCESS) {
-               validator_log(val, ISC_LOG_DEBUG(3),
-                       "failure processing NSEC set");
-               return (result);
-       }
-       dns_rdataset_current(nsecset, &rdata);
-
-       validator_log(val, ISC_LOG_DEBUG(3), "looking for relevant nsec");
-       relation = dns_name_fullcompare(name, nsecname, &order, &olabels);
-
-       if (order < 0) {
-               /*
-                * The name is not within the NSEC range.
-                */
-               validator_log(val, ISC_LOG_DEBUG(3),
-                             "NSEC does not cover name, before NSEC");
-               return (ISC_R_IGNORE);
-       }
-
-       if (order == 0) {
-               /*
-                * The names are the same.   If we are validating "."
-                * then atparent should not be set as there is no parent.
-                */
-               atparent = (olabels != 1) &&
-                          dns_rdatatype_atparent(val->event->type);
-               ns = dns_nsec_typepresent(&rdata, dns_rdatatype_ns);
-               soa = dns_nsec_typepresent(&rdata, dns_rdatatype_soa);
-               if (ns && !soa) {
-                       if (!atparent) {
-                               /*
-                                * This NSEC record is from somewhere higher in
-                                * the DNS, and at the parent of a delegation.
-                                * It can not be legitimately used here.
-                                */
-                               validator_log(val, ISC_LOG_DEBUG(3),
-                                             "ignoring parent nsec");
-                               return (ISC_R_IGNORE);
-                       }
-               } else if (atparent && ns && soa) {
-                       /*
-                        * This NSEC record is from the child.
-                        * It can not be legitimately used here.
-                        */
-                       validator_log(val, ISC_LOG_DEBUG(3),
-                                     "ignoring child nsec");
-                       return (ISC_R_IGNORE);
-               }
-               if (val->event->type == dns_rdatatype_cname ||
-                   val->event->type == dns_rdatatype_nxt ||
-                   val->event->type == dns_rdatatype_nsec ||
-                   val->event->type == dns_rdatatype_key ||
-                   !dns_nsec_typepresent(&rdata, dns_rdatatype_cname)) {
-                       *exists = ISC_TRUE;
-                       *data = dns_nsec_typepresent(&rdata, val->event->type);
-                       validator_log(val, ISC_LOG_DEBUG(3),
-                                     "nsec proves name exists (owner) data=%d",
-                                     *data);
-                       return (ISC_R_SUCCESS);
-               }
-               validator_log(val, ISC_LOG_DEBUG(3), "NSEC proves CNAME exists");
-               return (ISC_R_IGNORE);
-       }
-
-       if (relation == dns_namereln_subdomain &&
-           dns_nsec_typepresent(&rdata, dns_rdatatype_ns) &&
-           !dns_nsec_typepresent(&rdata, dns_rdatatype_soa))
-       {
-               /*
-                * This NSEC record is from somewhere higher in
-                * the DNS, and at the parent of a delegation.
-                * It can not be legitimately used here.
-                */
-               validator_log(val, ISC_LOG_DEBUG(3), "ignoring parent nsec");
-               return (ISC_R_IGNORE);
-       }
-
-       result = dns_rdata_tostruct(&rdata, &nsec, NULL);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-       relation = dns_name_fullcompare(&nsec.next, name, &order, &nlabels);
-       if (order == 0) {
-               dns_rdata_freestruct(&nsec);
-               validator_log(val, ISC_LOG_DEBUG(3),
-                             "ignoring nsec matches next name");
-               return (ISC_R_IGNORE);
-       }
-
-       if (order < 0 && !dns_name_issubdomain(nsecname, &nsec.next)) {
-               /*
-                * The name is not within the NSEC range.
-                */
-               dns_rdata_freestruct(&nsec);
-               validator_log(val, ISC_LOG_DEBUG(3),
-                           "ignoring nsec because name is past end of range");
-               return (ISC_R_IGNORE);
-       }
-
-       if (order > 0 && relation == dns_namereln_subdomain) {
-               validator_log(val, ISC_LOG_DEBUG(3),
-                             "nsec proves name exist (empty)");
-               dns_rdata_freestruct(&nsec);
-               *exists = ISC_TRUE;
-               *data = ISC_FALSE;
-               return (ISC_R_SUCCESS);
-       }
-       if (wild != NULL) {
-               dns_name_t common;
-               dns_name_init(&common, NULL);
-               if (olabels > nlabels) {
-                       labels = dns_name_countlabels(nsecname);
-                       dns_name_getlabelsequence(nsecname, labels - olabels,
-                                                 olabels, &common);
-               } else {
-                       labels = dns_name_countlabels(&nsec.next);
-                       dns_name_getlabelsequence(&nsec.next, labels - nlabels,
-                                                 nlabels, &common);
-               }
-               result = dns_name_concatenate(dns_wildcardname, &common,
-                                              wild, NULL);
-               if (result != ISC_R_SUCCESS) {
-                       dns_rdata_freestruct(&nsec);
-                       validator_log(val, ISC_LOG_DEBUG(3),
-                                   "failure generating wildcard name");
-                       return (result);
-               }
-       }
-       dns_rdata_freestruct(&nsec);
-       validator_log(val, ISC_LOG_DEBUG(3), "nsec range ok");
-       *exists = ISC_FALSE;
-       return (ISC_R_SUCCESS);
-}
-
-static isc_result_t
-nsec3noexistnodata(dns_validator_t *val, dns_name_t* name,
-                  dns_name_t *nsec3name, dns_rdataset_t *nsec3set,
-                  dns_name_t *zonename, isc_boolean_t *exists,
-                  isc_boolean_t *data, isc_boolean_t *optout,
-                  isc_boolean_t *unknown, isc_boolean_t *setclosest,
-                  isc_boolean_t *setnearest, dns_name_t *closest,
-                  dns_name_t *nearest)
-{
-       char namebuf[DNS_NAME_FORMATSIZE];
-       dns_fixedname_t fzone;
-       dns_fixedname_t qfixed;
-       dns_label_t hashlabel;
-       dns_name_t *qname;
-       dns_name_t *zone;
-       dns_rdata_nsec3_t nsec3;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
-       int order;
-       int scope;
-       isc_boolean_t atparent;
-       isc_boolean_t first;
-       isc_boolean_t ns;
-       isc_boolean_t soa;
-       isc_buffer_t buffer;
-       isc_result_t answer = ISC_R_IGNORE;
-       isc_result_t result;
-       unsigned char hash[NSEC3_MAX_HASH_LENGTH];
-       unsigned char owner[NSEC3_MAX_HASH_LENGTH];
-       unsigned int length;
-       unsigned int qlabels;
-       unsigned int zlabels;
-
-       REQUIRE((exists == NULL && data == NULL) ||
-               (exists != NULL && data != NULL));
-       REQUIRE(nsec3set != NULL && nsec3set->type == dns_rdatatype_nsec3);
-       REQUIRE((setclosest == NULL && closest == NULL) ||
-               (setclosest != NULL && closest != NULL));
-       REQUIRE((setnearest == NULL && nearest == NULL) ||
-               (setnearest != NULL && nearest != NULL));
-
-       result = dns_rdataset_first(nsec3set);
-       if (result != ISC_R_SUCCESS) {
-               validator_log(val, ISC_LOG_DEBUG(3),
-                       "failure processing NSEC3 set");
-               return (result);
-       }
-
-       dns_rdataset_current(nsec3set, &rdata);
-
-       result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-
-       validator_log(val, ISC_LOG_DEBUG(3), "looking for relevant NSEC3");
-
-       dns_fixedname_init(&fzone);
-       zone = dns_fixedname_name(&fzone);
-       zlabels = dns_name_countlabels(nsec3name);
-
-       /*
-        * NSEC3 records must have two or more labels to be valid.
-        */
-       if (zlabels < 2)
-               return (ISC_R_IGNORE);
-
-       /*
-        * Strip off the NSEC3 hash to get the zone.
-        */
-       zlabels--;
-       dns_name_split(nsec3name, zlabels, NULL, zone);
-
-       /*
-        * If not below the zone name we can ignore this record.
-        */
-       if (!dns_name_issubdomain(name, zone))
-               return (ISC_R_IGNORE);
-
-       /*
-        * Is this zone the same or deeper than the current zone?
-        */
-       if (dns_name_countlabels(zonename) == 0 ||
-           dns_name_issubdomain(zone, zonename))
-               dns_name_copy(zone, zonename, NULL);
-
-       if (!dns_name_equal(zone, zonename))
-               return (ISC_R_IGNORE);
-
-       /*
-        * Are we only looking for the most enclosing zone?
-        */
-       if (exists == NULL || data == NULL)
-               return (ISC_R_SUCCESS);
-
-       /*
-        * Only set unknown once we are sure that this NSEC3 is from
-        * the deepest covering zone.
-        */
-       if (!dns_nsec3_supportedhash(nsec3.hash)) {
-               if (unknown != NULL)
-                       *unknown = ISC_TRUE;
-               return (ISC_R_IGNORE);
-       }
-
-       /*
-        * Recover the hash from the first label.
-        */
-       dns_name_getlabel(nsec3name, 0, &hashlabel);
-       isc_region_consume(&hashlabel, 1);
-       isc_buffer_init(&buffer, owner, sizeof(owner));
-       result = isc_base32hex_decoderegion(&hashlabel, &buffer);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-
-       /*
-        * The hash lengths should match.  If not ignore the record.
-        */
-       if (isc_buffer_usedlength(&buffer) != nsec3.next_length)
-               return (ISC_R_IGNORE);
-
-       /*
-        * Work out what this NSEC3 covers.
-        * Inside (<0) or outside (>=0).
-        */
-       scope = memcmp(owner, nsec3.next, nsec3.next_length);
-
-       /*
-        * Prepare to compute all the hashes.
-        */
-       dns_fixedname_init(&qfixed);
-       qname = dns_fixedname_name(&qfixed);
-       dns_name_downcase(name, qname, NULL);
-       qlabels = dns_name_countlabels(qname);
-       first = ISC_TRUE;
-
-       while (qlabels >= zlabels) {
-               length = isc_iterated_hash(hash, nsec3.hash, nsec3.iterations,
-                                          nsec3.salt, nsec3.salt_length,
-                                          qname->ndata, qname->length);
-               /*
-                * The computed hash length should match.
-                */
-               if (length != nsec3.next_length) {
-                       validator_log(val, ISC_LOG_DEBUG(3),
-                                     "ignoring NSEC bad length %u vs %u",
-                                     length, nsec3.next_length);
-                       return (ISC_R_IGNORE);
-               }
-
-               order = memcmp(hash, owner, length);
-               if (first && order == 0) {
-                       /*
-                        * The hashes are the same.
-                        */
-                       atparent = dns_rdatatype_atparent(val->event->type);
-                       ns = dns_nsec3_typepresent(&rdata, dns_rdatatype_ns);
-                       soa = dns_nsec3_typepresent(&rdata, dns_rdatatype_soa);
-                       if (ns && !soa) {
-                               if (!atparent) {
-                                       /*
-                                        * This NSEC3 record is from somewhere
-                                        * higher in the DNS, and at the
-                                        * parent of a delegation. It can not
-                                        * be legitimately used here.
-                                        */
-                                       validator_log(val, ISC_LOG_DEBUG(3),
-                                                     "ignoring parent NSEC3");
-                                       return (ISC_R_IGNORE);
-                               }
-                       } else if (atparent && ns && soa) {
-                               /*
-                                * This NSEC3 record is from the child.
-                                * It can not be legitimately used here.
-                                */
-                               validator_log(val, ISC_LOG_DEBUG(3),
-                                             "ignoring child NSEC3");
-                               return (ISC_R_IGNORE);
-                       }
-                       if (val->event->type == dns_rdatatype_cname ||
-                           val->event->type == dns_rdatatype_nxt ||
-                           val->event->type == dns_rdatatype_nsec ||
-                           val->event->type == dns_rdatatype_key ||
-                           !dns_nsec3_typepresent(&rdata, dns_rdatatype_cname)) {
-                               *exists = ISC_TRUE;
-                               *data = dns_nsec3_typepresent(&rdata,
-                                                             val->event->type);
-                               validator_log(val, ISC_LOG_DEBUG(3),
-                                             "NSEC3 proves name exists (owner) "
-                                             "data=%d", *data);
-                               return (ISC_R_SUCCESS);
-                       }
-                       validator_log(val, ISC_LOG_DEBUG(3),
-                                     "NSEC3 proves CNAME exists");
-                       return (ISC_R_IGNORE);
-               }
-
-               if (order == 0 &&
-                   dns_nsec3_typepresent(&rdata, dns_rdatatype_ns) &&
-                   !dns_nsec3_typepresent(&rdata, dns_rdatatype_soa))
-               {
-                       /*
-                        * This NSEC3 record is from somewhere higher in
-                        * the DNS, and at the parent of a delegation.
-                        * It can not be legitimately used here.
-                        */
-                       validator_log(val, ISC_LOG_DEBUG(3),
-                                     "ignoring parent NSEC3");
-                       return (ISC_R_IGNORE);
-               }
-
-               /*
-                * Potential closest encloser.
-                */
-               if (order == 0) {
-                       if (closest != NULL &&
-                           (dns_name_countlabels(closest) == 0 ||
-                            dns_name_issubdomain(qname, closest)) &&
-                           !dns_nsec3_typepresent(&rdata, dns_rdatatype_ds) &&
-                           !dns_nsec3_typepresent(&rdata, dns_rdatatype_dname) &&
-                           (dns_nsec3_typepresent(&rdata, dns_rdatatype_soa) ||
-                            !dns_nsec3_typepresent(&rdata, dns_rdatatype_ns)))
-                       {
-
-                               dns_name_format(qname, namebuf,
-                                               sizeof(namebuf));
-                               validator_log(val, ISC_LOG_DEBUG(3),
-                                             "NSEC3 indicates potential "
-                                             "closest encloser: '%s'",
-                                              namebuf);
-                               dns_name_copy(qname, closest, NULL);
-                               *setclosest = ISC_TRUE;
-                       }
-                       dns_name_format(qname, namebuf, sizeof(namebuf));
-                       validator_log(val, ISC_LOG_DEBUG(3),
-                                     "NSEC3 at super-domain %s", namebuf);
-                       return (answer);
-               }
-
-               /*
-                * Find if the name does not exist.
-                *
-                * We continue as we need to find the name closest to the
-                * closest encloser that doesn't exist.
-                *
-                * We also need to continue to ensure that we are not
-                * proving the non-existence of a record in a sub-zone.
-                * If that would be the case we will return ISC_R_IGNORE
-                * above.
-                */
-               if ((scope < 0 && order > 0 &&
-                    memcmp(hash, nsec3.next, length) < 0) ||
-                   (scope >= 0 && (order > 0 ||
-                                   memcmp(hash, nsec3.next, length) < 0)))
-               {
-                       char namebuf[DNS_NAME_FORMATSIZE];
-
-                       dns_name_format(qname, namebuf, sizeof(namebuf));
-                       validator_log(val, ISC_LOG_DEBUG(3), "NSEC3 proves "
-                                     "name does not exist: '%s'", namebuf);
-                       if (nearest != NULL &&
-                           (dns_name_countlabels(nearest) == 0 ||
-                            dns_name_issubdomain(nearest, qname))) {
-                               dns_name_copy(qname, nearest, NULL);
-                               *setnearest = ISC_TRUE;
-                       }
-
-                       *exists = ISC_FALSE;
-                       *data = ISC_FALSE;
-                       if (optout != NULL) {
-                               if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0)
-                                       validator_log(val, ISC_LOG_DEBUG(3),
-                                                     "NSEC3 indicates optout");
-                               *optout =
-                                   ISC_TF(nsec3.flags & DNS_NSEC3FLAG_OPTOUT);
-                       }
-                       answer = ISC_R_SUCCESS;
-               }
-
-               qlabels--;
-               if (qlabels > 0)
-                       dns_name_split(qname, qlabels, NULL, qname);
-               first = ISC_FALSE;
-       }
-       return (answer);
-}
-
 /*%
  * Callback for when NSEC records have been validated.
  *
@@ -1344,8 +898,9 @@ authvalidated(isc_task_t *task, isc_event_t *event) {
                    rdataset->trust == dns_trust_secure &&
                    (NEEDNODATA(val) || NEEDNOQNAME(val)) &&
                    !FOUNDNODATA(val) && !FOUNDNOQNAME(val) &&
-                   nsecnoexistnodata(val, val->event->name, devent->name,
-                                     rdataset, &exists, &data, wild)
+                   dns_nsec_noexistnodata(val->event->type, val->event->name,
+                                          devent->name, rdataset, &exists,
+                                          &data, wild, validator_log, val)
                                      == ISC_R_SUCCESS)
                {
                        if (exists && !data) {
@@ -2845,8 +2400,9 @@ checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename)
                if (rdataset->type == dns_rdatatype_nsec &&
                    (NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
                    !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
-                   nsecnoexistnodata(val, wild, name, rdataset,
-                                     &exists, &data, NULL)
+                   dns_nsec_noexistnodata(val->event->type, wild, name,
+                                          rdataset, &exists, &data, NULL,
+                                          validator_log, val)
                                       == ISC_R_SUCCESS)
                {
                        dns_name_t **proofs = val->event->proofs;
@@ -2869,10 +2425,11 @@ checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename)
                if (rdataset->type == dns_rdatatype_nsec3 &&
                    (NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
                    !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
-                   nsec3noexistnodata(val, wild, name, rdataset,
-                                      zonename, &exists, &data,
-                                      NULL, NULL, NULL, NULL, NULL,
-                                      NULL) == ISC_R_SUCCESS)
+                   dns_nsec3_noexistnodata(val->event->type, wild, name,
+                                           rdataset, zonename, &exists, &data,
+                                           NULL, NULL, NULL, NULL, NULL, NULL,
+                                           validator_log, val)
+                                           == ISC_R_SUCCESS)
                {
                        dns_name_t **proofs = val->event->proofs;
                        if (exists && !data)
@@ -2934,11 +2491,12 @@ findnsec3proofs(dns_validator_t *val) {
                    rdataset->trust != dns_trust_secure)
                        continue;
 
-               result = nsec3noexistnodata(val, val->event->name,
-                                           name, rdataset,
-                                           zonename, NULL, NULL, NULL,
-                                           NULL, NULL, NULL, NULL,
-                                           NULL);
+               result = dns_nsec3_noexistnodata(val->event->type,
+                                                val->event->name, name,
+                                                rdataset, zonename, NULL,
+                                                NULL, NULL, NULL, NULL, NULL,
+                                                NULL, NULL, validator_log,
+                                                val);
                if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS) {
                        if (dns_rdataset_isassociated(&trdataset))
                                dns_rdataset_disassociate(&trdataset);
@@ -2986,11 +2544,13 @@ findnsec3proofs(dns_validator_t *val) {
                setclosest = setnearest = ISC_FALSE;
                optout = ISC_FALSE;
                unknown = ISC_FALSE;
-               result = nsec3noexistnodata(val, val->event->name, name,
-                                           rdataset, zonename, &exists,
-                                           &data, &optout, &unknown,
-                                           setclosestp, &setnearest,
-                                           closestp, nearest);
+               result = dns_nsec3_noexistnodata(val->event->type,
+                                                val->event->name,
+                                                name, rdataset, zonename,
+                                                &exists, &data, &optout,
+                                                &unknown, setclosestp,
+                                                &setnearest, closestp,
+                                                nearest, validator_log, val);
                if (unknown)
                        val->attributes |= VALATTR_FOUNDUNKNOWN;
                if (result != ISC_R_SUCCESS)
@@ -4366,7 +3926,7 @@ validator_logv(dns_validator_t *val, isc_logcategory_t *category,
 }
 
 static void
-validator_log(dns_validator_t *val, int level, const char *fmt, ...) {
+validator_log(void *val, int level, const char *fmt, ...) {
        va_list ap;
 
        if (! isc_log_wouldlog(dns_lctx, level))