]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
2448. [func] Add NSEC3 support. [RT #15452]
authorMark Andrews <marka@isc.org>
Wed, 24 Sep 2008 02:46:23 +0000 (02:46 +0000)
committerMark Andrews <marka@isc.org>
Wed, 24 Sep 2008 02:46:23 +0000 (02:46 +0000)
120 files changed:
CHANGES
NSEC3-NOTES [new file with mode: 0644]
bin/dnssec/dnssec-keyfromlabel.c
bin/dnssec/dnssec-keyfromlabel.docbook
bin/dnssec/dnssec-keygen.c
bin/dnssec/dnssec-keygen.docbook
bin/dnssec/dnssec-signzone.c
bin/dnssec/dnssec-signzone.docbook
bin/dnssec/dnssectool.h
bin/named/config.c
bin/named/named.conf.docbook
bin/named/query.c
bin/named/update.c
bin/named/xfrout.c
bin/named/zoneconf.c
bin/nsupdate/nsupdate.c
bin/nsupdate/nsupdate.docbook
bin/tests/Makefile.in
bin/tests/db_test.c
bin/tests/nsec3hash.c [new file with mode: 0644]
bin/tests/nsecify.c
bin/tests/system/Makefile.in
bin/tests/system/dnssec/clean.sh
bin/tests/system/dnssec/ns1/sign.sh
bin/tests/system/dnssec/ns2/child.nsec3.example.db [new file with mode: 0644]
bin/tests/system/dnssec/ns2/child.optout.example.db [new file with mode: 0644]
bin/tests/system/dnssec/ns2/example.db.in
bin/tests/system/dnssec/ns2/named.conf
bin/tests/system/dnssec/ns2/sign.sh
bin/tests/system/dnssec/ns3/insecure.nsec3.example.db [new file with mode: 0644]
bin/tests/system/dnssec/ns3/insecure.optout.example.db [new file with mode: 0644]
bin/tests/system/dnssec/ns3/multiple.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/named.conf
bin/tests/system/dnssec/ns3/nsec3-unknown.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/nsec3.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/nsec3.nsec3.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/nsec3.optout.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/optout-unknown.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/optout.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/optout.nsec3.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/optout.optout.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/secure.example.db.in
bin/tests/system/dnssec/ns3/secure.nsec3.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/secure.optout.example.db.in [new file with mode: 0644]
bin/tests/system/dnssec/ns3/sign.sh
bin/tests/system/dnssec/ns7/named.conf [new file with mode: 0644]
bin/tests/system/dnssec/tests.sh
bin/tests/system/ifconfig.sh
contrib/sdb/bdb/zone2bdb.c
contrib/sdb/ldap/zone2ldap.c
contrib/sdb/pgsql/zonetodb.c
contrib/sdb/sqlite/zone2sqlite.c
doc/misc/options
lib/dns/Makefile.in
lib/dns/cache.c
lib/dns/db.c
lib/dns/diff.c
lib/dns/dst_api.c
lib/dns/gen.c
lib/dns/include/dns/db.h
lib/dns/include/dns/keyvalues.h
lib/dns/include/dns/masterdump.h
lib/dns/include/dns/ncache.h
lib/dns/include/dns/nsec.h
lib/dns/include/dns/nsec3.h
lib/dns/include/dns/rbt.h
lib/dns/include/dns/rcode.h
lib/dns/include/dns/rdataset.h
lib/dns/include/dns/rdatatype.h
lib/dns/include/dns/result.h
lib/dns/include/dns/types.h
lib/dns/include/dns/validator.h
lib/dns/include/dns/zone.h
lib/dns/include/dst/dst.h
lib/dns/journal.c
lib/dns/masterdump.c
lib/dns/message.c
lib/dns/ncache.c
lib/dns/nsec.c
lib/dns/nsec3.c [new file with mode: 0644]
lib/dns/opensslrsa_link.c
lib/dns/rbt.c
lib/dns/rbtdb.c
lib/dns/rcode.c
lib/dns/rdata.c
lib/dns/rdata/generic/nsec3_50.c [new file with mode: 0644]
lib/dns/rdata/generic/nsec3_50.h [new file with mode: 0644]
lib/dns/rdata/generic/nsec3param_51.c [new file with mode: 0644]
lib/dns/rdata/generic/nsec3param_51.h [new file with mode: 0644]
lib/dns/rdatalist.c
lib/dns/rdatalist_p.h
lib/dns/rdataset.c
lib/dns/rdataslab.c
lib/dns/resolver.c
lib/dns/result.c
lib/dns/rootns.c
lib/dns/sdb.c
lib/dns/sdlz.c
lib/dns/validator.c
lib/dns/win32/libdns.def
lib/dns/win32/libdns.dsp
lib/dns/win32/libdns.mak
lib/dns/zone.c
lib/isc/Makefile.in
lib/isc/buffer.c
lib/isc/commandline.c
lib/isc/hex.c
lib/isc/include/isc/base32.h [new file with mode: 0644]
lib/isc/include/isc/buffer.h
lib/isc/include/isc/hex.h
lib/isc/include/isc/iterated_hash.h [new file with mode: 0644]
lib/isc/include/isc/result.h
lib/isc/result.c
lib/isc/win32/libisc.def
lib/isc/win32/libisc.dsp
lib/isc/win32/libisc.mak
lib/isccfg/include/isccfg/grammar.h
lib/isccfg/namedconf.c
lib/isccfg/parser.c
util/copyrights

diff --git a/CHANGES b/CHANGES
index 4a270580aedd46386b027d09e0d0e0dcc4adbf06..07565f8ad36b438ae5b0d839224e10665aa3beaa 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+2448.  [func]          Add NSEC3 support. [RT #15452]
+
 2447.  [cleanup]       libbind has been split out as a seperate product.
 
 2446.  [func]          Add a new log message about build options on startup.
diff --git a/NSEC3-NOTES b/NSEC3-NOTES
new file mode 100644 (file)
index 0000000..6278cb9
--- /dev/null
@@ -0,0 +1,128 @@
+
+                       DNSSEC and UPDATE
+
+               Converting from insecure to secure
+
+As of BIND 9.6.0 it is possible to move a zone between being insecure
+to secure and back again.  A secure zone can be using NSEC or NSEC3.
+
+To move a zone from insecure to secure you need to configure named
+so that it can see the K* files which contain the public and private
+parts of the keys that will be used to sign the zone.  These files
+will have been generated by dnssec-keygen.  You can do this by
+placing them in the key-directory as specified in named.conf.
+
+       zone example.net {
+               type master;
+               allow-update { .... };
+               file "dynamic/example.net/example.net";
+               key-directory "dynamic/example.net";
+       };
+
+Assuming one KSK and ons ZSK DNSKEY key has been generated.  Then
+this will cause the zone to be signed with the ZSK and the DNSKEY
+RRset to be signed with the KSK DNSKEY.  A NSEC chain will also be
+generated as part of the initial signing process.
+
+       % nsupdate
+       > ttl 3600
+       > update add example.net DNSKEY 256 3 7 AwEAAZn17pUF0KpbPA2c7Gz76Vb18v0teKT3EyAGfBfL8eQ8al35zz3Y I1m/SAQBxIqMfLtIwqWPdgthsu36azGQAX8=
+       > update add example.net DNSKEY 257 3 7 AwEAAd/7odU/64o2LGsifbLtQmtO8dFDtTAZXSX2+X3e/UNlq9IHq3Y0 XtC0Iuawl/qkaKVxXe2lo8Ct+dM6UehyCqk=
+       > send
+
+While the update request will complete almost immediately the zone
+will not be completely signed until named has hand time to walk the
+zone and generate the NSEC and RRSIG records.  Initially the NSEC
+record at the zone apex will have the OPT bit set.  When the NSEC
+chain is complete the OPT bit will be cleared.  Additionally when
+the zone fully signed the private type (default TYPE65535) records
+will have a non zero value for the final octet. 
+
+The private type record has 5 octets.
+       algorithm (octet 1)
+       key id in network order (octet 2 and 3)
+       removal flag (octet 4)
+       complete flag (octet 5)
+
+If you wish to go straight to a secure zone using NSEC3 you should
+also add a NSECPARAM record to the update request with the flags
+field set to indicate whether the NSEC3 chain will have the OPTOUT
+bit set or not.
+
+       % nsupdate
+       > ttl 3600
+       > update add example.net DNSKEY 256 3 7 AwEAAZn17pUF0KpbPA2c7Gz76Vb18v0teKT3EyAGfBfL8eQ8al35zz3Y I1m/SAQBxIqMfLtIwqWPdgthsu36azGQAX8=
+       > update add example.net DNSKEY 257 3 7 AwEAAd/7odU/64o2LGsifbLtQmtO8dFDtTAZXSX2+X3e/UNlq9IHq3Y0 XtC0Iuawl/qkaKVxXe2lo8Ct+dM6UehyCqk=
+       > update add example.net NSEC3PARAM 1 1 100 1234567890
+       > send
+
+Again the update request will complete almost immediately however the
+NSEC3PARAM record will have additional flag bits set indicating that the
+NSEC3 chain is under construction.  When the NSEC3 chain is complete the
+flags field will be set to zero. 
+
+While the initial signing and NSEC/NSEC3 chain generation is happening
+other updates are possible.
+
+               DNSKEY roll overs via UPDATE
+
+It is possible to perform key rollovers via update.  You need to
+add the K* files for the new keys so that named can find them.  You
+can then add the new DNSKEY RRs via update.  Named will then cause
+the zone to be signed with the new keys.  When the signing is
+complete the private type records will be updated so that the last
+octet is non zero.
+
+If this is for a KSK you need to inform the parent and any trust
+anchor repositories of the new KSK.
+
+You should then wait for the maximum TLL in the zone before removing the
+old DNSKEY.  If it is a KSK that is being updated you also need to wait
+for the DS RRset in the parent to be updated and its TTL to expire.
+This ensures that all clients will be able to verify atleast a signature
+when you remove the old DNSKEY.
+
+The can be removed the old DNSKEY via UPDATE.  Take care to specify
+the correct key.  Named will clean out any signatures generated by
+the old key after the update completes.
+
+               NSEC3PARAM rollovers via UPDATE.
+
+Add the new NSEC3PARAM record via update.  When the new NSEC3 chain
+has been generated the NSEC3PARAM flag field will be zero.  At this
+point you can remove the old NSEC3PARAM record.  The old chain will
+be removed after the update request completes.
+
+               Converting from NSEC to NSEC3
+
+To do this you just need to add a NSEC3PARAM record.  When the
+conversion is complete the NSEC chain will have been removed and
+the NSEC3PARAM record will have a zero flag field.  The NSEC3 chain
+will be generated before the NSEC chain is destroyed.
+
+               Converting from NSEC3 to NSEC
+
+To do this remove all NSEC3PARAM records with a zero flag field.  The
+NSEC chain will be generated before the NSEC3 chain is removed.
+
+               Converting from secure to insecure
+
+To do this remove all the DNSKEY records.  Any NSEC or NSEC3 chains
+will be removed as will as associated NSEC3PARAM records.  The will
+take place after the update requests completes.
+
+               Periodic re-signing.
+
+Named will periodically re-sign RRsets which have not been re-signed
+as a result of some update action.  The signature lifetimes will
+be adjusted so as to spread the re-sign load over time rather than
+all at once.
+
+               NSEC3 and OPTOUT
+
+Named only supports creating new NSEC3 chains where all the NSEC3
+records in the zone have the same OPTOUT state.  Named supports
+UPDATES to zones where the NSEC3 records in the chain have mixed
+OPTOUT state.  Named does not support changing the OPTOUT state of
+an individual NSEC3 record, the entire chain needs to be changed if
+the OPTOUT state of an individual NSEC3 needs to be changed.
index 4002610b716bbab2a01a52eae5b92c9baeefacd9..e7587c39663de03a204a3d8070117321d896c604 100644 (file)
@@ -14,7 +14,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dnssec-keyfromlabel.c,v 1.3 2008/03/31 23:47:11 tbox Exp $ */
+/* $Id: dnssec-keyfromlabel.c,v 1.4 2008/09/24 02:46:21 marka Exp $ */
 
 /*! \file */
 
@@ -47,7 +47,8 @@
 const char *program = "dnssec-keyfromlabel";
 int verbose;
 
-static const char *algs = "RSA | RSAMD5 | DH | DSA | RSASHA1";
+static const char *algs = "RSA | RSAMD5 | DH | DSA | RSASHA1 |"
+                         " NSEC3DSA | NSEC3RSASHA1";
 
 static void
 usage(void) {
index dd452d30d06f9a630fc0a639e84d31f2b655cf01..71477e287469997882ea3aad24d918e882641d2c 100644 (file)
@@ -17,7 +17,7 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id: dnssec-keyfromlabel.docbook,v 1.3 2008/03/31 23:47:11 tbox Exp $ -->
+<!-- $Id: dnssec-keyfromlabel.docbook,v 1.4 2008/09/24 02:46:21 marka Exp $ -->
 <refentry id="man.dnssec-keyfromlabel">
   <refentryinfo>
     <date>february 8, 2008</date>
@@ -76,8 +76,8 @@
          <para>
            Selects the cryptographic algorithm.  The value of
            <option>algorithm</option> must be one of RSAMD5 (RSA)
-           or RSASHA1, DSA or DH (Diffie Hellman).  These values
-           are case insensitive.
+           or RSASHA1, DSA, NSEC3RSASHA1, NSEC3DSA or DH (Diffie Hellman).
+           These values are case insensitive.
          </para>
           <para>
             Note 1: that for DNSSEC, RSASHA1 is a mandatory to implement
index 3fd29b379cee7f4c2a8467021c17c453510ceba1..c9197924b9a29466d4b04aefcec6209d21781e0b 100644 (file)
@@ -29,7 +29,7 @@
  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dnssec-keygen.c,v 1.79 2007/08/28 07:20:42 tbox Exp $ */
+/* $Id: dnssec-keygen.c,v 1.80 2008/09/24 02:46:21 marka Exp $ */
 
 /*! \file */
 
@@ -62,8 +62,9 @@
 const char *program = "dnssec-keygen";
 int verbose;
 
-static const char *algs = "RSA | RSAMD5 | DH | DSA | RSASHA1 | HMAC-MD5 |"
-                         " HMAC-SHA1 | HMAC-SHA224 | HMAC-SHA256 | "
+static const char *algs = "RSA | RSAMD5 | DH | DSA | RSASHA1 | NSEC3DSA |"
+                         " NSEC3RSASHA1 | HMAC-MD5 |"
+                         " HMAC-SHA1 | HMAC-SHA224 | HMAC-SHA256 |"
                          " HMAC-SHA384 | HMAC-SHA512";
 
 static isc_boolean_t
@@ -82,8 +83,10 @@ usage(void) {
        fprintf(stderr, "    -b key size, in bits:\n");
        fprintf(stderr, "        RSAMD5:\t\t[512..%d]\n", MAX_RSA);
        fprintf(stderr, "        RSASHA1:\t\t[512..%d]\n", MAX_RSA);
+       fprintf(stderr, "        NSEC3RSASHA1:\t\t[512..%d]\n", MAX_RSA);
        fprintf(stderr, "        DH:\t\t[128..4096]\n");
        fprintf(stderr, "        DSA:\t\t[512..1024] and divisible by 64\n");
+       fprintf(stderr, "        NSEC3DSA:\t\t[512..1024] and divisible by 64\n");
        fprintf(stderr, "        HMAC-MD5:\t[1..512]\n");
        fprintf(stderr, "        HMAC-SHA1:\t[1..160]\n");
        fprintf(stderr, "        HMAC-SHA224:\t[1..224]\n");
@@ -303,6 +306,7 @@ main(int argc, char **argv) {
        switch (alg) {
        case DNS_KEYALG_RSAMD5:
        case DNS_KEYALG_RSASHA1:
+       case DNS_KEYALG_NSEC3RSASHA1:
                if (size != 0 && (size < 512 || size > MAX_RSA))
                        fatal("RSA key size %d out of range", size);
                break;
@@ -311,6 +315,7 @@ main(int argc, char **argv) {
                        fatal("DH key size %d out of range", size);
                break;
        case DNS_KEYALG_DSA:
+       case DNS_KEYALG_NSEC3DSA:
                if (size != 0 && !dsa_size_ok(size))
                        fatal("invalid DSS key size: %d", size);
                break;
@@ -370,8 +375,8 @@ main(int argc, char **argv) {
                break;
        }
 
-       if (!(alg == DNS_KEYALG_RSAMD5 || alg == DNS_KEYALG_RSASHA1) &&
-           rsa_exp != 0)
+       if (!(alg == DNS_KEYALG_RSAMD5 || alg == DNS_KEYALG_RSASHA1 ||
+             alg == DNS_KEYALG_NSEC3RSASHA1) && rsa_exp != 0)
                fatal("specified RSA exponent for a non-RSA key");
 
        if (alg != DNS_KEYALG_DH && generator != 0)
index 0414da76f9f593b81a20dcd22ea3b05cd11090b5..abb032a3ea8975c9f79144cbd1b1ba78e59bfac8 100644 (file)
@@ -18,7 +18,7 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id: dnssec-keygen.docbook,v 1.19 2007/06/18 23:47:17 tbox Exp $ -->
+<!-- $Id: dnssec-keygen.docbook,v 1.20 2008/09/24 02:46:21 marka Exp $ -->
 <refentry id="man.dnssec-keygen">
   <refentryinfo>
     <date>June 30, 2000</date>
           <para>
             Selects the cryptographic algorithm.  The value of
             <option>algorithm</option> must be one of RSAMD5 (RSA) or RSASHA1,
-                     DSA, DH (Diffie Hellman), or HMAC-MD5.  These values
-            are case insensitive.
+           DSA, NSEC3RSASHA1, NSEC3DSA, DH (Diffie Hellman), or HMAC-MD5.
+           These values are case insensitive.
           </para>
           <para>
             Note 1: that for DNSSEC, RSASHA1 is a mandatory to implement
-            algorithm,
-            and DSA is recommended.  For TSIG, HMAC-MD5 is mandatory.
+            algorithm, and DSA is recommended.  For TSIG, HMAC-MD5 is
+           mandatory.
           </para>
           <para>
             Note 2: HMAC-MD5 and DH automatically set the -k flag.
index 2297682172f762d615b9c716476da764e62362c5..c6e5f2f647c6e860766a51f602570c4a8845fab7 100644 (file)
@@ -29,7 +29,7 @@
  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dnssec-signzone.c,v 1.206 2008/06/02 23:47:04 tbox Exp $ */
+/* $Id: dnssec-signzone.c,v 1.207 2008/09/24 02:46:21 marka Exp $ */
 
 /*! \file */
 
 #include <time.h>
 
 #include <isc/app.h>
+#include <isc/base32.h>
 #include <isc/commandline.h>
 #include <isc/entropy.h>
 #include <isc/event.h>
 #include <isc/file.h>
 #include <isc/hash.h>
+#include <isc/hex.h>
 #include <isc/mem.h>
 #include <isc/mutex.h>
 #include <isc/os.h>
@@ -53,8 +55,8 @@
 #include <isc/stdio.h>
 #include <isc/string.h>
 #include <isc/task.h>
-#include <isc/util.h>
 #include <isc/time.h>
+#include <isc/util.h>
 
 #include <dns/db.h>
 #include <dns/dbiterator.h>
@@ -67,7 +69,9 @@
 #include <dns/master.h>
 #include <dns/masterdump.h>
 #include <dns/nsec.h>
+#include <dns/nsec3.h>
 #include <dns/rdata.h>
+#include <dns/rdatalist.h>
 #include <dns/rdataset.h>
 #include <dns/rdataclass.h>
 #include <dns/rdatasetiter.h>
 const char *program = "dnssec-signzone";
 int verbose;
 
+typedef struct hashlist hashlist_t;
+
+static int nsec_datatype = dns_rdatatype_nsec;
+
+#define IS_NSEC3       (nsec_datatype == dns_rdatatype_nsec3)
+#define OPTOUT(x)      (((x) & DNS_NSEC3FLAG_OPTOUT) != 0)
+
 #define BUFSIZE 2048
 #define MAXDSKEYS 8
 
@@ -138,6 +149,7 @@ static dns_dbversion_t *gversion;   /* The database version */
 static dns_dbiterator_t *gdbiter;      /* The database iterator */
 static dns_rdataclass_t gclass;                /* The class */
 static dns_name_t *gorigin;            /* The database origin */
+static int nsec3flags = 0;
 static isc_task_t *master = NULL;
 static unsigned int ntasks = 0;
 static isc_boolean_t shuttingdown = ISC_FALSE, finished = ISC_FALSE;
@@ -149,6 +161,8 @@ static dns_name_t *dlv = NULL;
 static dns_fixedname_t dlv_fixed;
 static dns_master_style_t *dsstyle = NULL;
 static unsigned int serialformat = SOA_SERIAL_KEEP;
+static unsigned int hash_length = 0;
+static isc_boolean_t unknownalg = ISC_FALSE;
 
 #define INCSTAT(counter)               \
        if (printstats) {               \
@@ -160,19 +174,8 @@ static unsigned int serialformat = SOA_SERIAL_KEEP;
 static void
 sign(isc_task_t *task, isc_event_t *event);
 
-
-static inline void
-set_bit(unsigned char *array, unsigned int index, unsigned int bit) {
-       unsigned int shift, mask;
-
-       shift = 7 - (index % 8);
-       mask = 1 << shift;
-
-       if (bit != 0)
-               array[index / 8] |= mask;
-       else
-               array[index / 8] &= (~mask & 0xFF);
-}
+static isc_boolean_t
+nsec3only(dns_dbnode_t *node);
 
 static void
 dumpnode(dns_name_t *name, dns_dbnode_t *node) {
@@ -562,6 +565,169 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name,
        isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
 }
 
+struct hashlist {
+       unsigned char *hashbuf;
+       size_t entries;
+       size_t size;
+       size_t length;
+};
+
+static void
+hashlist_init(hashlist_t *l, unsigned int nodes, unsigned int length) {
+
+       l->entries = 0;
+       l->length = length + 1;
+
+       if (nodes != 0) {
+               l->size = nodes;
+               l->hashbuf = malloc(l->size * l->length);
+               if (l->hashbuf == NULL)
+                       l->size = 0;
+       } else {
+               l->size = 0;
+               l->hashbuf = NULL;
+       }
+}
+
+static void
+hashlist_add(hashlist_t *l, const unsigned char *hash, size_t len)
+{
+
+       REQUIRE(len <= l->length);
+
+       if (l->entries == l->size) {
+               l->size = l->size * 2 + 100;
+               l->hashbuf = realloc(l->hashbuf, l->size * l->length);
+       }
+       memset(l->hashbuf + l->entries * l->length, 0, l->length);
+       memcpy(l->hashbuf + l->entries * l->length, hash, len);
+       l->entries++;
+}
+
+static void
+hashlist_add_dns_name(hashlist_t *l, /*const*/ dns_name_t *name,
+                     unsigned int hashalg, unsigned int iterations,
+                     const unsigned char *salt, size_t salt_length,
+                     isc_boolean_t speculative)
+{
+       char nametext[DNS_NAME_FORMATSIZE];
+       unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1];
+       unsigned int len;
+       size_t i;
+
+       len = isc_iterated_hash(hash, hashalg, iterations, salt, salt_length,
+                               name->ndata, name->length);
+       if (verbose) {
+               dns_name_format(name, nametext, sizeof nametext);
+               for (i = 0 ; i < len; i++)
+                       fprintf(stderr, "%02x", hash[i]);
+               fprintf(stderr, " %s\n", nametext);
+       }
+       hash[len++] = speculative ? 1 : 0;
+       hashlist_add(l, hash, len);
+}
+
+static int
+hashlist_comp(const void *a, const void *b) {
+       return (memcmp(a, b, hash_length + 1));
+}
+
+static void
+hashlist_sort(hashlist_t *l) {
+       qsort(l->hashbuf, l->entries, l->length, hashlist_comp);
+}
+
+static isc_boolean_t
+hashlist_hasdup(hashlist_t *l) {
+       unsigned char *current;
+       unsigned char *next = l->hashbuf;
+       size_t entries = l->entries;
+
+       /*
+        * Skip initial speculative wild card hashs.
+        */
+       while (entries > 0 && next[l->length-1] != 0) {
+               next += l->length;
+               entries--;
+       }
+
+       current = next;
+       while (entries-- > 1U) {
+               next += l->length;
+               if (next[l->length-1] != 0)
+                       continue;
+               if (memcmp(current, next, l->length - 1) == 0)
+                       return (ISC_TRUE);
+               current = next;
+       }
+       return (ISC_FALSE);
+}
+
+static const unsigned char *
+hashlist_findnext(const hashlist_t *l,
+                 const unsigned char hash[NSEC3_MAX_HASH_LENGTH])
+{
+       unsigned int entries = l->entries;
+       const unsigned char *next = bsearch(hash, l->hashbuf, l->entries,
+                                           l->length, hashlist_comp);
+       INSIST(next != NULL);
+
+       do {
+               if (next < l->hashbuf + (l->entries - 1) * l->length)
+                       next += l->length;
+               else
+                       next = l->hashbuf;
+               if (next[l->length - 1] == 0)
+                       break;
+       } while (entries-- > 1);
+       INSIST(entries != 0);
+       return (next);
+}
+
+static isc_boolean_t
+hashlist_exists(const hashlist_t *l,
+               const unsigned char hash[NSEC3_MAX_HASH_LENGTH])
+{
+       if (bsearch(hash, l->hashbuf, l->entries, l->length, hashlist_comp))
+               return (ISC_TRUE);
+       else
+               return (ISC_FALSE);
+}
+
+static void
+addnowildcardhash(hashlist_t *l, /*const*/ dns_name_t *name,
+                 unsigned int hashalg, unsigned int iterations,
+                 const unsigned char *salt, size_t salt_length)
+{
+       dns_fixedname_t fixed;
+       dns_name_t *wild;
+       dns_dbnode_t *node = NULL;
+       isc_result_t result;
+       char namestr[DNS_NAME_FORMATSIZE];
+
+       dns_fixedname_init(&fixed);
+       wild = dns_fixedname_name(&fixed);
+
+       result = dns_name_concatenate(dns_wildcardname, name, wild, NULL);
+       if (result == ISC_R_NOSPACE)
+               return;
+       check_result(result,"addnowildcardhash: dns_name_concatenate()");
+
+       result = dns_db_findnode(gdb, wild, ISC_FALSE, &node);
+       if (result == ISC_R_SUCCESS) {
+               dns_db_detachnode(gdb, &node);
+               return;
+       }
+
+       if (verbose) {
+               dns_name_format(wild, namestr, sizeof(namestr));
+               fprintf(stderr, "adding no-wildcardhash for %s\n", namestr);
+       }
+
+       hashlist_add_dns_name(l, wild, hashalg, iterations, salt, salt_length,
+                             ISC_TRUE);
+}
+
 static void
 opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
        dns_db_t **dbp)
@@ -677,91 +843,6 @@ loadds(dns_name_t *name, isc_uint32_t ttl, dns_rdataset_t *dsset) {
        return (result);
 }
 
-static isc_boolean_t
-nsec_setbit(dns_name_t *name, dns_rdataset_t *rdataset, dns_rdatatype_t type,
-          unsigned int val)
-{
-       isc_result_t result;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
-       dns_rdata_nsec_t nsec;
-       unsigned int newlen;
-       unsigned char bitmap[8192 + 512];
-       unsigned char nsecdata[8192 + 512 + DNS_NAME_MAXWIRE];
-       isc_boolean_t answer = ISC_FALSE;
-       unsigned int i, len, window;
-       int octet;
-
-       result = dns_rdataset_first(rdataset);
-       check_result(result, "dns_rdataset_first()");
-       dns_rdataset_current(rdataset, &rdata);
-       result = dns_rdata_tostruct(&rdata, &nsec, NULL);
-       check_result(result, "dns_rdata_tostruct");
-
-       INSIST(nsec.len <= sizeof(bitmap));
-
-       newlen = 0;
-
-       memset(bitmap, 0, sizeof(bitmap));
-       for (i = 0; i < nsec.len; i += len) {
-               INSIST(i + 2 <= nsec.len);
-               window = nsec.typebits[i];
-               len = nsec.typebits[i+1];
-               i += 2;
-               INSIST(len > 0 && len <= 32);
-               INSIST(i + len <= nsec.len);
-               memmove(&bitmap[window * 32 + 512], &nsec.typebits[i], len);
-       }
-       set_bit(bitmap + 512, type, val);
-       for (window = 0; window < 256; window++) {
-               for (octet = 31; octet >= 0; octet--)
-                       if (bitmap[window * 32 + 512 + octet] != 0)
-                               break;
-               if (octet < 0)
-                       continue;
-               bitmap[newlen] = window;
-               bitmap[newlen + 1] = octet + 1;
-               newlen += 2;
-               /*
-                * Overlapping move.
-                */
-               memmove(&bitmap[newlen], &bitmap[window * 32 + 512], octet + 1);
-               newlen += octet + 1;
-       }
-       if (newlen != nsec.len ||
-           memcmp(nsec.typebits, bitmap, newlen) != 0) {
-               dns_rdata_t newrdata = DNS_RDATA_INIT;
-               isc_buffer_t b;
-               dns_diff_t diff;
-               dns_difftuple_t *tuple = NULL;
-
-               dns_diff_init(mctx, &diff);
-               result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL, name,
-                                             rdataset->ttl, &rdata, &tuple);
-               check_result(result, "dns_difftuple_create");
-               dns_diff_append(&diff, &tuple);
-
-               nsec.typebits = bitmap;
-               nsec.len = newlen;
-               isc_buffer_init(&b, nsecdata, sizeof(nsecdata));
-               result = dns_rdata_fromstruct(&newrdata, rdata.rdclass,
-                                             dns_rdatatype_nsec, &nsec,
-                                             &b);
-               check_result(result, "dns_rdata_fromstruct");
-
-               result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
-                                             name, rdataset->ttl,
-                                             &newrdata, &tuple);
-               check_result(result, "dns_difftuple_create");
-               dns_diff_append(&diff, &tuple);
-               result = dns_diff_apply(&diff, gdb, gversion);
-               check_result(result, "dns_difftuple_apply");
-               dns_diff_clear(&diff);
-               answer = ISC_TRUE;
-       }
-       dns_rdata_freestruct(&nsec);
-       return (answer);
-}
-
 static isc_boolean_t
 delegation(dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp) {
        dns_rdataset_t nsset;
@@ -782,10 +863,25 @@ delegation(dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp) {
        return (ISC_TF(result == ISC_R_SUCCESS));
 }
 
+static isc_boolean_t
+secure(dns_name_t *name, dns_dbnode_t *node) {
+       dns_rdataset_t dsset;
+       isc_result_t result;
+
+       if (dns_name_equal(name, gorigin))
+               return (ISC_FALSE);
+
+       dns_rdataset_init(&dsset);
+       result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_ds,
+                                    0, 0, &dsset, NULL);
+       if (dns_rdataset_isassociated(&dsset))
+               dns_rdataset_disassociate(&dsset);
+
+       return (ISC_TF(result == ISC_R_SUCCESS));
+}
+
 /*%
- * Signs all records at a name.  This mostly just signs each set individually,
- * but also adds the RRSIG bit to any NSECs generated earlier, deals with
- * parent/child KEY signatures, and handles other exceptional cases.
+ * Signs all records at a name.
  */
 static void
 signname(dns_dbnode_t *node, dns_name_t *name) {
@@ -793,88 +889,18 @@ signname(dns_dbnode_t *node, dns_name_t *name) {
        dns_rdataset_t rdataset;
        dns_rdatasetiter_t *rdsiter;
        isc_boolean_t isdelegation = ISC_FALSE;
-       isc_boolean_t hasds = ISC_FALSE;
-       isc_boolean_t changed = ISC_FALSE;
        dns_diff_t del, add;
        char namestr[DNS_NAME_FORMATSIZE];
-       isc_uint32_t nsttl = 0;
 
+       dns_rdataset_init(&rdataset);
        dns_name_format(name, namestr, sizeof(namestr));
 
        /*
         * Determine if this is a delegation point.
         */
-       if (delegation(name, node, &nsttl))
+       if (delegation(name, node, NULL))
                isdelegation = ISC_TRUE;
 
-       /*
-        * If this is a delegation point, look for a DS set.
-        */
-       if (isdelegation) {
-               dns_rdataset_t dsset;
-               dns_rdataset_t sigdsset;
-
-               dns_rdataset_init(&dsset);
-               dns_rdataset_init(&sigdsset);
-               result = dns_db_findrdataset(gdb, node, gversion,
-                                            dns_rdatatype_ds,
-                                            0, 0, &dsset, &sigdsset);
-               if (result == ISC_R_SUCCESS) {
-                       dns_rdataset_disassociate(&dsset);
-                       if (generateds) {
-                               result = dns_db_deleterdataset(gdb, node,
-                                                              gversion,
-                                                              dns_rdatatype_ds,
-                                                              0);
-                               check_result(result, "dns_db_deleterdataset");
-                       } else
-                               hasds = ISC_TRUE;
-               }
-               if (generateds) {
-                       result = loadds(name, nsttl, &dsset);
-                       if (result == ISC_R_SUCCESS) {
-                               result = dns_db_addrdataset(gdb, node,
-                                                           gversion, 0,
-                                                           &dsset, 0, NULL);
-                               check_result(result, "dns_db_addrdataset");
-                               hasds = ISC_TRUE;
-                               dns_rdataset_disassociate(&dsset);
-                               if (dns_rdataset_isassociated(&sigdsset))
-                                       dns_rdataset_disassociate(&sigdsset);
-                       } else if (dns_rdataset_isassociated(&sigdsset)) {
-                               result = dns_db_deleterdataset(gdb, node,
-                                                           gversion,
-                                                           dns_rdatatype_rrsig,
-                                                           dns_rdatatype_ds);
-                               check_result(result, "dns_db_deleterdataset");
-                               dns_rdataset_disassociate(&sigdsset);
-                       }
-               } else if (dns_rdataset_isassociated(&sigdsset))
-                       dns_rdataset_disassociate(&sigdsset);
-       }
-
-       /*
-        * Make sure that NSEC bits are appropriately set.
-        */
-       dns_rdataset_init(&rdataset);
-       RUNTIME_CHECK(dns_db_findrdataset(gdb, node, gversion,
-                                         dns_rdatatype_nsec, 0, 0, &rdataset,
-                                         NULL) == ISC_R_SUCCESS);
-       if (!nokeys)
-               changed = nsec_setbit(name, &rdataset, dns_rdatatype_rrsig, 1);
-       if (changed) {
-               dns_rdataset_disassociate(&rdataset);
-               RUNTIME_CHECK(dns_db_findrdataset(gdb, node, gversion,
-                                                 dns_rdatatype_nsec, 0, 0,
-                                                 &rdataset,
-                                                 NULL) == ISC_R_SUCCESS);
-       }
-       if (hasds)
-               (void)nsec_setbit(name, &rdataset, dns_rdatatype_ds, 1);
-       else
-               (void)nsec_setbit(name, &rdataset, dns_rdatatype_ds, 0);
-       dns_rdataset_disassociate(&rdataset);
-
        /*
         * Now iterate through the rdatasets.
         */
@@ -897,7 +923,7 @@ signname(dns_dbnode_t *node, dns_name_t *name) {
                 * isn't a DS record.
                 */
                if (isdelegation) {
-                       if (rdataset.type != dns_rdatatype_nsec &&
+                       if (rdataset.type != nsec_datatype &&
                            rdataset.type != dns_rdatatype_ds)
                                goto skip;
                } else if (rdataset.type == dns_rdatatype_ds) {
@@ -951,6 +977,7 @@ active_node(dns_dbnode_t *node) {
        while (result == ISC_R_SUCCESS) {
                dns_rdatasetiter_current(rdsiter, &rdataset);
                if (rdataset.type != dns_rdatatype_nsec &&
+                   rdataset.type != dns_rdatatype_nsec3 &&
                    rdataset.type != dns_rdatatype_rrsig)
                        active = ISC_TRUE;
                dns_rdataset_disassociate(&rdataset);
@@ -963,7 +990,7 @@ active_node(dns_dbnode_t *node) {
                fatal("rdataset iteration failed: %s",
                      isc_result_totext(result));
 
-       if (!active) {
+       if (!active && nsec_datatype == dns_rdatatype_nsec) {
                /*%
                 * The node is empty of everything but NSEC / RRSIG records.
                 */
@@ -1022,6 +1049,32 @@ active_node(dns_dbnode_t *node) {
                        fatal("rdataset iteration failed: %s",
                              isc_result_totext(result));
                dns_rdatasetiter_destroy(&rdsiter2);
+
+#if 0
+               /*
+                * Delete all NSEC records and RRSIG(NSEC) if we are in
+                * NSEC3 mode and vica versa.
+                */
+               for (result = dns_rdatasetiter_first(rdsiter2);
+                    result == ISC_R_SUCCESS;
+                    result = dns_rdatasetiter_next(rdsiter2)) {
+                       dns_rdatasetiter_current(rdsiter, &rdataset);
+                       type = rdataset.type;
+                       covers = rdataset.covers;
+                       if (type == dns_rdatatype_rrsig)
+                               type = covers;
+                       dns_rdataset_disassociate(&rdataset);
+                       if (type == nsec_datatype ||
+                           (type != dns_rdatatype_nsec &&
+                            type != dns_rdatatype_nsec3))
+                               continue;
+                       if (covers != 0)
+                               type = dns_rdatatype_rrsig;
+                       result = dns_db_deleterdataset(gdb, node, gversion,
+                                                      type, covers);
+                       check_result(result, "dns_db_deleterdataset()");
+               }
+#endif
        }
        dns_rdatasetiter_destroy(&rdsiter);
 
@@ -1182,11 +1235,8 @@ presign(void) {
        isc_result_t result;
 
        gdbiter = NULL;
-       result = dns_db_createiterator(gdb, ISC_FALSE, &gdbiter);
+       result = dns_db_createiterator(gdb, 0, &gdbiter);
        check_result(result, "dns_db_createiterator()");
-
-       result = dns_dbiterator_first(gdbiter);
-       check_result(result, "dns_dbiterator_first()");
 }
 
 /*%
@@ -1199,6 +1249,8 @@ postsign(void) {
 
 /*%
  * Sign the apex of the zone.
+ * Note the origin may not be the first node if there are out of zone
+ * records.
  */
 static void
 signapex(void) {
@@ -1209,13 +1261,15 @@ signapex(void) {
 
        dns_fixedname_init(&fixed);
        name = dns_fixedname_name(&fixed);
+       result = dns_dbiterator_seek(gdbiter, gorigin);
+       check_result(result, "dns_dbiterator_seek()");
        result = dns_dbiterator_current(gdbiter, &node, name);
        check_result(result, "dns_dbiterator_current()");
        signname(node, name);
        dumpnode(name, node);
        cleannode(gdb, gversion, node);
        dns_db_detachnode(gdb, &node);
-       result = dns_dbiterator_next(gdbiter);
+       result = dns_dbiterator_first(gdbiter);
        if (result == ISC_R_NOMORE)
                finished = ISC_TRUE;
        else if (result != ISC_R_SUCCESS)
@@ -1236,6 +1290,8 @@ assignwork(isc_task_t *task, isc_task_t *worker) {
        dns_rdataset_t nsec;
        isc_boolean_t found;
        isc_result_t result;
+       static dns_name_t *zonecut = NULL;      /* Protected by namelock. */
+       static dns_fixedname_t fzonecut;        /* Protected by namelock. */
        static unsigned int ended = 0;          /* Protected by namelock. */
 
        if (shuttingdown)
@@ -1263,19 +1319,51 @@ assignwork(isc_task_t *task, isc_task_t *worker) {
                if (result != ISC_R_SUCCESS)
                        fatal("failure iterating database: %s",
                              isc_result_totext(result));
+               /*
+                * The origin was handled by signapex().
+                */
+               if (dns_name_equal(name, gorigin)) {
+                       dns_db_detachnode(gdb, &node);
+                       goto next;
+               }
+               /*
+                * Sort the zone data from the glue and out-of-zone data.
+                * For NSEC zones nodes with zone data have NSEC records.
+                * For NSEC3 zones the NSEC3 nodes are zone data but
+                * outside of the zone name space.  For the rest we need
+                * to track the bottom of zone cuts.
+                * Nodes which don't need to be signed are dumped here.
+                */
                dns_rdataset_init(&nsec);
                result = dns_db_findrdataset(gdb, node, gversion,
-                                            dns_rdatatype_nsec, 0, 0,
+                                            nsec_datatype, 0, 0,
                                             &nsec, NULL);
-               if (result == ISC_R_SUCCESS)
-                       found = ISC_TRUE;
-               else
-                       dumpnode(name, node);
                if (dns_rdataset_isassociated(&nsec))
                        dns_rdataset_disassociate(&nsec);
-               if (!found)
+               if (result == ISC_R_SUCCESS) {
+                       found = ISC_TRUE;
+               } else if (nsec_datatype == dns_rdatatype_nsec3) {
+                       if (dns_name_issubdomain(name, gorigin) &&
+                           (zonecut == NULL ||
+                            !dns_name_issubdomain(name, zonecut))) {
+                               if (delegation(name, node, NULL)) {
+                                       dns_fixedname_init(&fzonecut);
+                                       zonecut = dns_fixedname_name(&fzonecut);
+                                       dns_name_copy(name, zonecut, NULL);
+                                       if (!OPTOUT(nsec3flags) ||
+                                           secure(name, node))
+                                               found = ISC_TRUE;
+                               } else
+                                       found = ISC_TRUE;
+                       }
+               }
+
+               if (!found) {
+                       dumpnode(name, node);
                        dns_db_detachnode(gdb, &node);
+               }
 
+ next:
                result = dns_dbiterator_next(gdbiter);
                if (result == ISC_R_NOMORE) {
                        finished = ISC_TRUE;
@@ -1360,6 +1448,43 @@ sign(isc_task_t *task, isc_event_t *event) {
        isc_task_send(master, ISC_EVENT_PTR(&wevent));
 }
 
+/*%
+ * Update / remove the DS RRset.  Preserve RRSIG(DS) if possible.
+ */
+static void
+add_ds(dns_name_t *name, dns_dbnode_t *node, isc_uint32_t nsttl) {
+       dns_rdataset_t dsset;
+       dns_rdataset_t sigdsset;
+       isc_result_t result;
+
+       dns_rdataset_init(&dsset);
+       dns_rdataset_init(&sigdsset);
+       result = dns_db_findrdataset(gdb, node, gversion,
+                                    dns_rdatatype_ds,
+                                    0, 0, &dsset, &sigdsset);
+       if (result == ISC_R_SUCCESS) {
+               dns_rdataset_disassociate(&dsset);
+               result = dns_db_deleterdataset(gdb, node, gversion,
+                                              dns_rdatatype_ds, 0);
+               check_result(result, "dns_db_deleterdataset");
+       }
+       result = loadds(name, nsttl, &dsset);
+       if (result == ISC_R_SUCCESS) {
+               result = dns_db_addrdataset(gdb, node, gversion, 0,
+                                           &dsset, 0, NULL);
+               check_result(result, "dns_db_addrdataset");
+               dns_rdataset_disassociate(&dsset);
+               if (dns_rdataset_isassociated(&sigdsset))
+                       dns_rdataset_disassociate(&sigdsset);
+       } else if (dns_rdataset_isassociated(&sigdsset)) {
+               result = dns_db_deleterdataset(gdb, node, gversion,
+                                              dns_rdatatype_rrsig,
+                                              dns_rdatatype_ds);
+               check_result(result, "dns_db_deleterdataset");
+               dns_rdataset_disassociate(&sigdsset);
+       }
+}
+
 /*%
  * Generate NSEC records for the zone.
  */
@@ -1371,6 +1496,7 @@ nsecify(void) {
        dns_name_t *name, *nextname, *zonecut;
        isc_boolean_t done = ISC_FALSE;
        isc_result_t result;
+       isc_uint32_t nsttl = 0;
 
        dns_fixedname_init(&fname);
        name = dns_fixedname_name(&fname);
@@ -1379,7 +1505,7 @@ nsecify(void) {
        dns_fixedname_init(&fzonecut);
        zonecut = NULL;
 
-       result = dns_db_createiterator(gdb, ISC_FALSE, &dbiter);
+       result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter);
        check_result(result, "dns_db_createiterator()");
 
        result = dns_dbiterator_first(dbiter);
@@ -1387,9 +1513,11 @@ nsecify(void) {
 
        while (!done) {
                dns_dbiterator_current(dbiter, &node, name);
-               if (delegation(name, node, NULL)) {
+               if (delegation(name, node, &nsttl)) {
                        zonecut = dns_fixedname_name(&fzonecut);
                        dns_name_copy(name, zonecut, NULL);
+                       if (generateds)
+                               add_ds(name, node, nsttl);
                }
                result = dns_dbiterator_next(dbiter);
                nextnode = NULL;
@@ -1431,6 +1559,451 @@ nsecify(void) {
        dns_dbiterator_destroy(&dbiter);
 }
 
+/*%
+ * Does this node only contain NSEC3 records or RRSIG records or is empty.
+ */
+static isc_boolean_t
+nsec3only(dns_dbnode_t *node) {
+       dns_rdatasetiter_t *rdsiter = NULL;
+       isc_result_t result;
+       dns_rdataset_t rdataset;
+       isc_boolean_t answer = ISC_TRUE;
+
+       dns_rdataset_init(&rdataset);
+       result = dns_db_allrdatasets(gdb, node, gversion, 0, &rdsiter);
+       check_result(result, "dns_db_allrdatasets()");
+       result = dns_rdatasetiter_first(rdsiter);
+       while (result == ISC_R_SUCCESS) {
+               dns_rdatasetiter_current(rdsiter, &rdataset);
+               if (rdataset.type != dns_rdatatype_nsec3 &&
+                   rdataset.type != dns_rdatatype_rrsig) {
+                       answer = ISC_FALSE;
+                       result = ISC_R_NOMORE;
+               } else
+                       result = dns_rdatasetiter_next(rdsiter);
+               dns_rdataset_disassociate(&rdataset);
+       }
+       if (result != ISC_R_NOMORE)
+               fatal("rdataset iteration failed: %s",
+                     isc_result_totext(result));
+       dns_rdatasetiter_destroy(&rdsiter);
+       return (answer);
+}
+
+static void
+addnsec3param(const unsigned char *salt, size_t salt_length,
+             unsigned int iterations)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdata_nsec3param_t nsec3param;
+       unsigned char nsec3parambuf[5 + 255];
+       dns_rdatalist_t rdatalist;
+       dns_rdataset_t rdataset;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       isc_buffer_t b;
+       isc_result_t result;
+
+       dns_rdataset_init(&rdataset);
+
+       nsec3param.common.rdclass = gclass;
+       nsec3param.common.rdtype = dns_rdatatype_nsec3param;
+       ISC_LINK_INIT(&nsec3param.common, link);
+       nsec3param.mctx = NULL;
+       nsec3param.flags = 0;
+       nsec3param.hash = unknownalg ? DNS_NSEC3_UNKNOWNALG : dns_hash_sha1;
+       nsec3param.iterations = iterations;
+       nsec3param.salt_length = salt_length;
+       DE_CONST(salt, nsec3param.salt);
+
+       isc_buffer_init(&b, nsec3parambuf, sizeof(nsec3parambuf));
+       result = dns_rdata_fromstruct(&rdata, gclass,
+                                     dns_rdatatype_nsec3param,
+                                     &nsec3param, &b);
+       rdatalist.rdclass = rdata.rdclass;
+       rdatalist.type = rdata.type;
+       rdatalist.covers = 0;
+       rdatalist.ttl = 0;
+       ISC_LIST_INIT(rdatalist.rdata);
+       ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+       result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+       check_result(result, "dns_rdatalist_tordataset()");
+
+       result = dns_db_findnode(gdb, gorigin, ISC_TRUE, &node);
+       check_result(result, "dns_db_find(gorigin)");
+       result = dns_db_addrdataset(gdb, node, gversion, 0, &rdataset,
+                                   DNS_DBADD_MERGE, NULL);
+       if (result == DNS_R_UNCHANGED)
+               result = ISC_R_SUCCESS;
+       check_result(result, "addnsec3param: dns_db_addrdataset()");
+       dns_db_detachnode(gdb, &node);
+}
+
+static void
+addnsec3(dns_name_t *name, dns_dbnode_t *node,
+        const unsigned char *salt, size_t salt_length,
+        unsigned int iterations, hashlist_t *hashlist,
+        dns_ttl_t ttl)
+{
+       unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+       const unsigned char *nexthash;
+       unsigned char nsec3buffer[DNS_NSEC3_BUFFERSIZE];
+       dns_fixedname_t hashname;
+       dns_rdatalist_t rdatalist;
+       dns_rdataset_t rdataset;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       isc_result_t result;
+       dns_dbnode_t *nsec3node = NULL;
+       char namebuf[DNS_NAME_FORMATSIZE];
+       size_t hash_length;
+
+       dns_name_format(name, namebuf, sizeof(namebuf));
+
+       dns_fixedname_init(&hashname);
+       dns_rdataset_init(&rdataset);
+
+       dns_name_downcase(name, name, NULL);
+       result = dns_nsec3_hashname(&hashname, hash, &hash_length,
+                                   name, gorigin, dns_hash_sha1, iterations,
+                                   salt, salt_length);
+       check_result(result, "addnsec3: dns_nsec3_hashname()");
+       nexthash = hashlist_findnext(hashlist, hash);
+       result = dns_nsec3_buildrdata(gdb, gversion, node,
+                                     unknownalg ?
+                                         DNS_NSEC3_UNKNOWNALG : dns_hash_sha1,
+                                     nsec3flags, iterations,
+                                     salt, salt_length,
+                                     nexthash, ISC_SHA1_DIGESTLENGTH,
+                                     nsec3buffer, &rdata);
+       check_result(result, "addnsec3: dns_nsec3_buildrdata()");
+       rdatalist.rdclass = rdata.rdclass;
+       rdatalist.type = rdata.type;
+       rdatalist.covers = 0;
+       rdatalist.ttl = ttl;
+       ISC_LIST_INIT(rdatalist.rdata);
+       ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+       result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+       check_result(result, "dns_rdatalist_tordataset()");
+       result = dns_db_findnsec3node(gdb, dns_fixedname_name(&hashname),
+                                     ISC_TRUE, &nsec3node);
+       check_result(result, "addnsec3: dns_db_findnode()");
+       result = dns_db_addrdataset(gdb, nsec3node, gversion, 0, &rdataset,
+                                   0, NULL);
+       if (result == DNS_R_UNCHANGED)
+               result = ISC_R_SUCCESS;
+       check_result(result, "addnsec3: dns_db_addrdataset()");
+       dns_db_detachnode(gdb, &nsec3node);
+}
+
+/*%
+ * Clean out NSEC3 record and RRSIG(NSEC3) that are not in the hash list.
+ *
+ * Extract the hash from the first label of 'name' then see if it
+ * is in hashlist.  If 'name' is not in the hashlist then delete the
+ * any NSEC3 records which have the same parameters as the chain we
+ * are building.
+ *
+ * XXXMPA Should we also check that it of the form <hash>.<origin>?
+ */
+static void
+nsec3clean(dns_name_t *name, dns_dbnode_t *node,
+          unsigned int hashalg, unsigned int iterations,
+          const unsigned char *salt, size_t salt_length, hashlist_t *hashlist)
+{
+       dns_label_t label;
+       dns_rdata_nsec3_t nsec3;
+       dns_rdata_t rdata, delrdata;
+       dns_rdatalist_t rdatalist;
+       dns_rdataset_t rdataset, delrdataset;
+       isc_boolean_t delete_rrsigs = ISC_FALSE;
+       isc_buffer_t target;
+       isc_result_t result;
+       unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1];
+
+       /*
+        * Get the first label.
+        */
+       dns_name_getlabel(name, 0, &label);
+
+       /*
+        * We want just the label contents.
+        */
+       isc_region_consume(&label, 1);
+
+       /*
+        * Decode base32hex string.
+        */
+       isc_buffer_init(&target, hash, sizeof(hash) - 1);
+       result = isc_base32hex_decoderegion(&label, &target);
+       if (result != ISC_R_SUCCESS)
+               return;
+
+       hash[isc_buffer_usedlength(&target)] = 0;
+
+       if (hashlist_exists(hashlist, hash))
+               return;
+
+       /*
+        * Verify that the NSEC3 parameters match the current ones
+        * otherwise we are dealing with a different NSEC3 chain.
+        */
+       dns_rdataset_init(&rdataset);
+       dns_rdataset_init(&delrdataset);
+
+       result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_nsec3,
+                                    0, 0, &rdataset, NULL);
+       if (result != ISC_R_SUCCESS)
+               return;
+
+       /*
+        * Delete any matching NSEC3 records which have parameters that
+        * match the NSEC3 chain we are building.
+        */
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_init(&rdata);
+               dns_rdataset_current(&rdataset, &rdata);
+               dns_rdata_tostruct(&rdata, &nsec3, NULL);
+               if (nsec3.hash == hashalg &&
+                   nsec3.iterations == iterations &&
+                   nsec3.salt_length == salt_length &&
+                   !memcmp(nsec3.salt, salt, salt_length))
+                       break;
+               rdatalist.rdclass = rdata.rdclass;
+               rdatalist.type = rdata.type;
+               rdatalist.covers = 0;
+               rdatalist.ttl = rdataset.ttl;
+               ISC_LIST_INIT(rdatalist.rdata);
+               dns_rdata_init(&delrdata);
+               dns_rdata_clone(&rdata, &delrdata);
+               ISC_LIST_APPEND(rdatalist.rdata, &delrdata, link);
+               result = dns_rdatalist_tordataset(&rdatalist, &delrdataset);
+               check_result(result, "dns_rdatalist_tordataset()");
+               result = dns_db_subtractrdataset(gdb, node, gversion,
+                                                &delrdataset, 0, NULL);
+               dns_rdataset_disassociate(&delrdataset);
+               if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
+                       check_result(result, "dns_db_subtractrdataset(NSEC3)");
+               delete_rrsigs = ISC_TRUE;
+       }
+       dns_rdataset_disassociate(&rdataset);
+       if (result != ISC_R_NOMORE)
+               check_result(result, "dns_rdataset_first/next");
+
+       if (!delete_rrsigs)
+               return;
+       /*
+        * Delete the NSEC3 RRSIGs
+        */
+       result = dns_db_deleterdataset(gdb, node, gversion,
+                                      dns_rdatatype_rrsig,
+                                      dns_rdatatype_nsec3);
+       if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
+               check_result(result, "dns_db_deleterdataset(RRSIG(NSEC3))");
+}
+
+/*
+ * Generate NSEC3 records for the zone.
+ */
+static void
+nsec3ify(unsigned int hashalg, unsigned int iterations,
+        const unsigned char *salt, size_t salt_length, hashlist_t *hashlist)
+{
+       dns_dbiterator_t *dbiter = NULL;
+       dns_dbnode_t *node = NULL, *nextnode = NULL;
+       dns_fixedname_t fname, fnextname, fzonecut;
+       dns_name_t *name, *nextname, *zonecut;
+       isc_boolean_t done = ISC_FALSE;
+       isc_result_t result;
+       isc_boolean_t active;
+       isc_uint32_t nsttl = 0;
+       unsigned int count, nlabels;
+       int order;
+
+       dns_fixedname_init(&fname);
+       name = dns_fixedname_name(&fname);
+       dns_fixedname_init(&fnextname);
+       nextname = dns_fixedname_name(&fnextname);
+       dns_fixedname_init(&fzonecut);
+       zonecut = NULL;
+
+       /*
+        * Walk the zone generating the hash names.
+        */
+       result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter);
+       check_result(result, "dns_db_createiterator()");
+
+       result = dns_dbiterator_first(dbiter);
+       check_result(result, "dns_dbiterator_first()");
+
+       while (!done) {
+               dns_dbiterator_current(dbiter, &node, name);
+               result = dns_dbiterator_next(dbiter);
+               nextnode = NULL;
+               while (result == ISC_R_SUCCESS) {
+                       result = dns_dbiterator_current(dbiter, &nextnode,
+                                                       nextname);
+                       if (result != ISC_R_SUCCESS)
+                               break;
+                       active = active_node(nextnode);
+                       if (!active) {
+                               dns_db_detachnode(gdb, &nextnode);
+                               result = dns_dbiterator_next(dbiter);
+                               continue;
+                       }
+                       if (!dns_name_issubdomain(nextname, gorigin) ||
+                           (zonecut != NULL &&
+                            dns_name_issubdomain(nextname, zonecut))) {
+                               dns_db_detachnode(gdb, &nextnode);
+                               result = dns_dbiterator_next(dbiter);
+                               continue;
+                       }
+                       if (delegation(nextname, nextnode, &nsttl)) {
+                               zonecut = dns_fixedname_name(&fzonecut);
+                               dns_name_copy(nextname, zonecut, NULL);
+                               if (generateds)
+                                       add_ds(nextname, nextnode, nsttl);
+                               if (OPTOUT(nsec3flags) &&
+                                   !secure(nextname, nextnode)) {
+                                       dns_db_detachnode(gdb, &nextnode);
+                                       result = dns_dbiterator_next(dbiter);
+                                       continue;
+                               }
+                       }
+                       dns_db_detachnode(gdb, &nextnode);
+                       break;
+               }
+               if (result == ISC_R_NOMORE) {
+                       dns_name_copy(gorigin, nextname, NULL);
+                       done = ISC_TRUE;
+               } else if (result != ISC_R_SUCCESS)
+                       fatal("iterating through the database failed: %s",
+                             isc_result_totext(result));
+               dns_name_downcase(name, name, NULL);
+               hashlist_add_dns_name(hashlist, name, hashalg, iterations,
+                                     salt, salt_length, ISC_FALSE);
+               dns_db_detachnode(gdb, &node);
+               /*
+                * Add hashs for empty nodes.  Use closest encloser logic.
+                * The closest encloser either has data or is a empty
+                * node for another <name,nextname> span so we don't add
+                * it here.  Empty labels on nextname are within the span.
+                */
+               dns_name_downcase(nextname, nextname, NULL);
+               dns_name_fullcompare(name, nextname, &order, &nlabels);
+               addnowildcardhash(hashlist, name, hashalg, iterations,
+                                 salt, salt_length);
+               count = dns_name_countlabels(nextname);
+               while (count > nlabels + 1) {
+                       count--;
+                       dns_name_split(nextname, count, NULL, nextname);
+                       hashlist_add_dns_name(hashlist, nextname, hashalg,
+                                             iterations, salt, salt_length,
+                                             ISC_FALSE);
+                       addnowildcardhash(hashlist, nextname, hashalg,
+                                         iterations, salt, salt_length);
+               }
+       }
+       dns_dbiterator_destroy(&dbiter);
+
+       /*
+        * We have all the hashes now so we can sort them.
+        */
+       hashlist_sort(hashlist);
+
+       /*
+        * Check for duplicate hashes.  If found the salt needs to
+        * be changed.
+        */
+       if (hashlist_hasdup(hashlist))
+               fatal("Duplicate hash detected. Pick a different salt.");
+
+       /*
+        * Generate the nsec3 records.
+        */
+       zonecut = NULL;
+       done = ISC_FALSE;
+
+       addnsec3param(salt, salt_length, iterations);
+
+       result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter);
+       check_result(result, "dns_db_createiterator()");
+
+       result = dns_dbiterator_first(dbiter);
+       check_result(result, "dns_dbiterator_first()");
+
+       while (!done) {
+               dns_dbiterator_current(dbiter, &node, name);
+               result = dns_dbiterator_next(dbiter);
+               nextnode = NULL;
+               while (result == ISC_R_SUCCESS) {
+                       result = dns_dbiterator_current(dbiter, &nextnode,
+                                                       nextname);
+                       if (result != ISC_R_SUCCESS)
+                               break;
+                       /*
+                        * Cleanout NSEC3 RRsets which don't exist in the
+                        * hash table.
+                        */
+                       nsec3clean(nextname, nextnode, hashalg, iterations,
+                                  salt, salt_length, hashlist);
+                       /*
+                        * Skip NSEC3 only nodes when looking for the next
+                        * node in the zone.  Also skips now empty nodes.
+                        */
+                       if (nsec3only(nextnode)) {
+                               dns_db_detachnode(gdb, &nextnode);
+                               result = dns_dbiterator_next(dbiter);
+                               continue;
+                       }
+                       if (!dns_name_issubdomain(nextname, gorigin) ||
+                           (zonecut != NULL &&
+                            dns_name_issubdomain(nextname, zonecut))) {
+                               dns_db_detachnode(gdb, &nextnode);
+                               result = dns_dbiterator_next(dbiter);
+                               continue;
+                       }
+                       if (delegation(nextname, nextnode, NULL)) {
+                               zonecut = dns_fixedname_name(&fzonecut);
+                               dns_name_copy(nextname, zonecut, NULL);
+                               if (OPTOUT(nsec3flags) &&
+                                   !secure(nextname, nextnode)) {
+                                       dns_db_detachnode(gdb, &nextnode);
+                                       result = dns_dbiterator_next(dbiter);
+                                       continue;
+                               }
+                       }
+                       dns_db_detachnode(gdb, &nextnode);
+                       break;
+               }
+               if (result == ISC_R_NOMORE) {
+                       dns_name_copy(gorigin, nextname, NULL);
+                       done = ISC_TRUE;
+               } else if (result != ISC_R_SUCCESS)
+                       fatal("iterating through the database failed: %s",
+                             isc_result_totext(result));
+               /*
+                * We need to pause here to release the lock on the database.
+                */
+               dns_dbiterator_pause(dbiter);
+               addnsec3(name, node, salt, salt_length, iterations,
+                        hashlist, zonettl);
+               dns_db_detachnode(gdb, &node);
+               /*
+                * Add NSEC3's for empty nodes.  Use closest encloser logic.
+                */
+               dns_name_fullcompare(name, nextname, &order, &nlabels);
+               count = dns_name_countlabels(nextname);
+               while (count > nlabels + 1) {
+                       count--;
+                       dns_name_split(nextname, count, NULL, nextname);
+                       addnsec3(nextname, NULL, salt, salt_length,
+                                iterations, hashlist, zonettl);
+               }
+       }
+       dns_dbiterator_destroy(&dbiter);
+}
+
 /*%
  * Load the zone file from disk
  */
@@ -1801,6 +2374,9 @@ usage(void) {
        fprintf(stderr, "\t-n ncpus (number of cpus present)\n");
        fprintf(stderr, "\t-k key_signing_key\n");
        fprintf(stderr, "\t-l lookasidezone\n");
+       fprintf(stderr, "\t-3 salt (NSEC3 salt)\n");
+       fprintf(stderr, "\t-H interations (NSEC3 interations)\n");
+       fprintf(stderr, "\t-A (NSEC3 optout)\n");
        fprintf(stderr, "\t-z:\t");
        fprintf(stderr, "ignore KSK flag in DNSKEYs");
 
@@ -1865,6 +2441,36 @@ main(int argc, char *argv[]) {
        isc_task_t **tasks = NULL;
        isc_buffer_t b;
        int len;
+       unsigned int iterations = 100U;
+       const unsigned char *salt = NULL;
+       size_t salt_length = 0;
+       unsigned char saltbuf[255];
+       hashlist_t hashlist;
+
+#define CMDLINE_FLAGS "3:aAc:d:e:f:ghH:i:I:j:k:l:m:n:N:o:O:pr:s:StUv:z"
+
+       /*
+        * Process memory debugging arguement first.
+        */
+       while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+               switch (ch) {
+               case 'm':
+                       if (strcasecmp(isc_commandline_argument, "record") == 0)
+                               isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
+                       if (strcasecmp(isc_commandline_argument, "trace") == 0)
+                               isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
+                       if (strcasecmp(isc_commandline_argument, "usage") == 0)
+                               isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
+                       if (strcasecmp(isc_commandline_argument, "size") == 0)
+                               isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
+                       if (strcasecmp(isc_commandline_argument, "mctx") == 0)
+                               isc_mem_debugging |= ISC_MEM_DEBUGCTX;
+                       break;
+               default:
+                       break;
+               }
+       }
+       isc_commandline_reset = ISC_TRUE;
 
        masterstyle = &dns_master_style_explicitttl;
 
@@ -1878,10 +2484,32 @@ main(int argc, char *argv[]) {
 
        isc_commandline_errprint = ISC_FALSE;
 
-       while ((ch = isc_commandline_parse(argc, argv,
-                                   "ac:d:e:f:ghi:I:j:k:l:n:N:o:O:pr:s:Stv:z"))
-              != -1) {
+       while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
                switch (ch) {
+               case '3':
+                       if (strcmp(isc_commandline_argument, "-")) {
+                               isc_buffer_t target;
+                               char *sarg;
+
+                               sarg = isc_commandline_argument;
+                               isc_buffer_init(&target, saltbuf,
+                                               sizeof(saltbuf));
+                               result = isc_hex_decodestring(sarg, &target);
+                               check_result(result,
+                                            "isc_hex_decodestring(salt)");
+                               salt = saltbuf;
+                               salt_length = isc_buffer_usedlength(&target);
+                       } else {
+                               salt = saltbuf;
+                               salt_length = 0;
+                       }
+                       nsec_datatype = dns_rdatatype_nsec3;
+                       break;
+
+               case 'A':
+                       nsec3flags |= DNS_NSEC3FLAG_OPTOUT;
+                       break;
+
                case 'a':
                        tryverify = ISC_TRUE;
                        break;
@@ -1957,6 +2585,9 @@ main(int argc, char *argv[]) {
                        dskeyfile[ndskeys++] = isc_commandline_argument;
                        break;
 
+               case 'm':
+                       break;
+
                case 'n':
                        endp = NULL;
                        ntasks = strtol(isc_commandline_argument, &endp, 0);
@@ -1968,6 +2599,15 @@ main(int argc, char *argv[]) {
                        serialformatstr = isc_commandline_argument;
                        break;
 
+               case 'H':
+                       iterations = strtoul(isc_commandline_argument,
+                                            &endp, 0);
+                       if (*endp != '\0')
+                               fatal("iterations must be numeric");
+                       if (iterations > 0xffffU)
+                               fatal("iterations too big");
+                       break;
+
                case 'o':
                        origin = isc_commandline_argument;
                        break;
@@ -1998,6 +2638,10 @@ main(int argc, char *argv[]) {
                        printstats = ISC_TRUE;
                        break;
 
+               case 'U':       /* Undocumented for testing only. */
+                       unknownalg = ISC_TRUE;
+                       break;
+
                case 'v':
                        endp = NULL;
                        verbose = strtol(isc_commandline_argument, &endp, 0);
@@ -2041,7 +2685,7 @@ main(int argc, char *argv[]) {
                cycle = (endtime - starttime) / 4;
 
        if (ntasks == 0)
-               ntasks = isc_os_ncpus();
+               ntasks = isc_os_ncpus() * 2;
        vbprintf(4, "using %d cpus\n", ntasks);
 
        rdclass = strtoclass(classname);
@@ -2105,7 +2749,6 @@ main(int argc, char *argv[]) {
                                        0, 24, 0, 0, 0, 8, mctx);
        check_result(result, "dns_master_stylecreate");
 
-
        gdb = NULL;
        TIME_NOW(&timer_start);
        loadzone(file, origin, rdclass, &gdb);
@@ -2113,6 +2756,18 @@ main(int argc, char *argv[]) {
        gclass = dns_db_class(gdb);
        zonettl = soattl();
 
+       if (IS_NSEC3) {
+               isc_boolean_t answer;
+               hash_length = dns_nsec3_hashlength(dns_hash_sha1);
+               hashlist_init(&hashlist, dns_db_nodecount(gdb) * 2,
+                             hash_length);
+               result = dns_nsec_nseconly(gdb, gversion, &answer);
+               check_result(result, "dns_nsec_nseconly");
+               if (answer)
+                       fatal("NSEC3 generation requested with "
+                             "NSEC only DNSKEY");
+       }
+
        ISC_LIST_INIT(keylist);
 
        if (argc == 0) {
@@ -2199,6 +2854,15 @@ main(int argc, char *argv[]) {
                nokeys = ISC_TRUE;
        }
 
+       if (IS_NSEC3) {
+               unsigned int max;
+               result = dns_nsec3_maxiterations(gdb, NULL, mctx, &max);
+               check_result(result, "dns_nsec3_maxiterations()");
+               if (iterations > max)
+                       fatal("NSEC3 interations too big for weakest DNSKEY "
+                             "strength. Maximum iterations allowed %u.", max);
+       }
+
        warnifallksk(gdb);
 
        gversion = NULL;
@@ -2218,7 +2882,11 @@ main(int argc, char *argv[]) {
                        break;
        }
 
-       nsecify();
+       if (IS_NSEC3)
+               nsec3ify(dns_hash_sha1, iterations, salt, salt_length,
+                        &hashlist);
+       else
+               nsecify();
 
        if (!nokeys) {
                writeset("keyset-", dns_rdatatype_dnskey);
index 9bd7c966246df24b6fe9bab1cf6ec9a489b9e745..526b4b608710651b36202a5b2edc56cc4d79020d 100644 (file)
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
               [<!ENTITY mdash "&#8212;">]>
 <!--
- - Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
  - Copyright (C) 2000-2003  Internet Software Consortium.
  -
  - Permission to use, copy, modify, and/or distribute this software for any
@@ -18,7 +18,7 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id: dnssec-signzone.docbook,v 1.27 2007/06/18 23:47:18 tbox Exp $ -->
+<!-- $Id: dnssec-signzone.docbook,v 1.28 2008/09/24 02:46:21 marka Exp $ -->
 <refentry id="man.dnssec-signzone">
   <refentryinfo>
     <date>June 30, 2000</date>
@@ -41,6 +41,7 @@
       <year>2005</year>
       <year>2006</year>
       <year>2007</year>
+      <year>2008</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
     <copyright>
@@ -76,6 +77,9 @@
       <arg><option>-t</option></arg>
       <arg><option>-v <replaceable class="parameter">level</replaceable></option></arg>
       <arg><option>-z</option></arg>
+      <arg><option>-3 <replaceable class="parameter">salt</replaceable></option></arg>
+      <arg><option>-H <replaceable class="parameter">iterations</replaceable></option></arg>
+      <arg><option>-A</option></arg>
       <arg choice="req">zonefile</arg>
       <arg rep="repeat">key</arg>
     </cmdsynopsis>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>-3 <replaceable class="parameter">salt</replaceable></term>
+        <listitem>
+          <para>
+            Generate a NSEC3 chain with the given hex encoded salt.
+           A dash (<replaceable class="parameter">salt</replaceable>) can
+           be used to indicate that no salt is to be used when generating                  the NSEC3 chain.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-H <replaceable class="parameter">iterations</replaceable></term>
+        <listitem>
+          <para>
+           When generating a NSEC3 chain use this many interations.  The
+           default is 100.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-A</term>
+        <listitem>
+          <para>
+           When generating a NSEC3 chain set the OPTOUT flag on all
+           NSEC3 records and do not generate NSEC3 records for insecure
+           delegations.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term>zonefile</term>
         <listitem>
index a0b20e37e3212ad1502010fb0e9b13377e56d49e..299df70d0cbe1a82db6d1204ba9a94b17d9d74ae 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dnssectool.h,v 1.20 2007/06/19 23:46:59 tbox Exp $ */
+/* $Id: dnssectool.h,v 1.21 2008/09/24 02:46:21 marka Exp $ */
 
 #ifndef DNSSECTOOL_H
 #define DNSSECTOOL_H 1
@@ -41,7 +41,7 @@ vbprintf(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
 
 void
 type_format(const dns_rdatatype_t type, char *cp, unsigned int size);
-#define TYPE_FORMATSIZE 10
+#define TYPE_FORMATSIZE 20
 
 void
 alg_format(const dns_secalg_t alg, char *cp, unsigned int size);
index 3e4d0995861ea6e93b3ba0fc615bc39e121042dd..5ea0a114c184303ae36c8e569abf0d9279afab06 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: config.c,v 1.90 2008/09/04 05:56:42 marka Exp $ */
+/* $Id: config.c,v 1.91 2008/09/24 02:46:21 marka Exp $ */
 
 /*! \file */
 
@@ -145,6 +145,7 @@ options {\n\
        clients-per-query 10;\n\
        max-clients-per-query 100;\n\
        zero-no-soa-ttl-cache no;\n\
+       nsec3-test-zone no;\n\
 "
 
 "      /* zone */\n\
index 7762c834e1e4dd1cabc437b7c0335479d6b94646..a4a8044d04639481b7a6bfc67c4be1428b757df3 100644 (file)
@@ -17,7 +17,7 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id: named.conf.docbook,v 1.38 2008/09/04 05:56:42 marka Exp $ -->
+<!-- $Id: named.conf.docbook,v 1.39 2008/09/24 02:46:21 marka Exp $ -->
 <refentry>
   <refentryinfo>
     <date>Aug 13, 2004</date>
@@ -341,6 +341,8 @@ options {
        zero-no-soa-ttl <replaceable>boolean</replaceable>;
        zero-no-soa-ttl-cache <replaceable>boolean</replaceable>;
 
+       nsec3-test-zone <replaceable>boolean</replaceable>;  // testing only
+
        allow-v6-synthesis { <replaceable>address_match_element</replaceable>; ... }; // obsolete
        deallocate-on-exit <replaceable>boolean</replaceable>; // obsolete
        fake-iquery <replaceable>boolean</replaceable>; // obsolete
@@ -588,6 +590,8 @@ zone <replaceable>string</replaceable> <replaceable>optional_class</replaceable>
        try-tcp-refresh <replaceable>boolean</replaceable>;
        key-directory <replaceable>quoted_string</replaceable>;
 
+       nsec3-test-zone <replaceable>boolean</replaceable>;  // testing only
+
        ixfr-base <replaceable>quoted_string</replaceable>; // obsolete
        ixfr-tmp-file <replaceable>quoted_string</replaceable>; // obsolete
        maintain-ixfr-base <replaceable>boolean</replaceable>; // obsolete
index 1f1b052f2cadbb4d744dd6004536caa213d1bd0c..0b3fa9415d3531501620034b5a9da13a5bf77885 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: query.c,v 1.308 2008/08/26 06:09:18 marka Exp $ */
+/* $Id: query.c,v 1.309 2008/09/24 02:46:21 marka Exp $ */
 
 /*! \file */
 
@@ -25,6 +25,7 @@
 
 #include <isc/mem.h>
 #include <isc/util.h>
+#include <isc/hex.h>
 
 #include <dns/adb.h>
 #include <dns/byaddr.h>
@@ -36,6 +37,7 @@
 #include <dns/events.h>
 #include <dns/message.h>
 #include <dns/ncache.h>
+#include <dns/nsec3.h>
 #include <dns/order.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 #define SECURE(c)              (((c)->query.attributes & \
                                  NS_QUERYATTR_SECURE) != 0)
 
+/*% No QNAME Proof? */
+#define NOQNAME(r)             (((r)->attributes & \
+                                 DNS_RDATASETATTR_NOQNAME) != 0)
+
 #if 0
 #define CTRACE(m)       isc_log_write(ns_g_lctx, \
                                      NS_LOGCATEGORY_CLIENT, \
@@ -121,6 +127,13 @@ static isc_boolean_t
 validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
         dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
 
+static void
+query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
+                      dns_dbversion_t *version, ns_client_t *client,
+                      dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+                      dns_name_t *fname, isc_boolean_t exact,
+                      dns_name_t *found);
+
 /*%
  * Increment query statistics counters.
  */
@@ -2285,7 +2298,8 @@ query_addcnamelike(ns_client_t *client, dns_name_t *qname, dns_name_t *tname,
  */
 static void
 mark_secure(ns_client_t *client, dns_db_t *db, dns_name_t *name,
-           dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+           isc_uint32_t ttl, dns_rdataset_t *rdataset,
+           dns_rdataset_t *sigrdataset)
 {
        isc_result_t result;
        dns_dbnode_t *node = NULL;
@@ -2299,6 +2313,18 @@ mark_secure(ns_client_t *client, dns_db_t *db, dns_name_t *name,
        result = dns_db_findnode(db, name, ISC_TRUE, &node);
        if (result != ISC_R_SUCCESS)
                return;
+       /*
+        * Bound the validated ttls then minimise.
+        */
+       if (sigrdataset->ttl > ttl)
+               sigrdataset->ttl = ttl;
+       if (rdataset->ttl > ttl)
+               rdataset->ttl = ttl;
+       if (rdataset->ttl > sigrdataset->ttl)
+               rdataset->ttl = sigrdataset->ttl;
+       else
+               sigrdataset->ttl = rdataset->ttl;
+       
        (void)dns_db_addrdataset(db, node, NULL, client->now, rdataset,
                                 0, NULL);
        (void)dns_db_addrdataset(db, node, NULL, client->now, sigrdataset,
@@ -2422,8 +2448,9 @@ validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
                                   client->view->acceptexpired)) {
                                dst_key_free(&key);
                                dns_rdataset_disassociate(&keyrdataset);
-                               mark_secure(client, db, name, rdataset,
-                                           sigrdataset);
+                               mark_secure(client, db, name,
+                                           rrsig.originalttl,
+                                           rdataset, sigrdataset);
                                return (ISC_TRUE);
                        }
                        dst_key_free(&key);
@@ -2608,13 +2635,37 @@ query_addbestns(ns_client_t *client) {
        }
 }
 
+static void
+fixrdataset(ns_client_t *client, dns_rdataset_t **rdataset) {
+       if (*rdataset == NULL)
+               *rdataset = query_newrdataset(client);
+       else  if (dns_rdataset_isassociated(*rdataset))
+               dns_rdataset_disassociate(*rdataset);
+}
+
+static void
+fixfname(ns_client_t *client, dns_name_t **fname, isc_buffer_t **dbuf,
+        isc_buffer_t *nbuf)
+{
+       if (*fname == NULL) {
+               *dbuf = query_getnamebuf(client);
+               if (*dbuf == NULL)
+                       return;
+               *fname = query_newname(client, *dbuf, nbuf);
+       }
+}
+
 static void
 query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node,
-           dns_dbversion_t *version)
+           dns_dbversion_t *version, dns_name_t *name)
 {
+       dns_fixedname_t fixed;
+       dns_name_t *fname = NULL;
        dns_name_t *rname;
        dns_rdataset_t *rdataset, *sigrdataset;
+       isc_buffer_t *dbuf, b;
        isc_result_t result;
+       unsigned int count;
 
        CTRACE("query_addds");
        rname = NULL;
@@ -2635,16 +2686,17 @@ query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node,
        result = dns_db_findrdataset(db, node, version, dns_rdatatype_ds, 0,
                                     client->now, rdataset, sigrdataset);
        /*
-        * If we didn't find it, look for an NSEC. */
+        * If we didn't find it, look for an NSEC.
+        */
        if (result == ISC_R_NOTFOUND)
                result = dns_db_findrdataset(db, node, version,
                                             dns_rdatatype_nsec, 0, client->now,
                                             rdataset, sigrdataset);
        if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
-               goto cleanup;
+               goto addnsec3;
        if (!dns_rdataset_isassociated(rdataset) ||
            !dns_rdataset_isassociated(sigrdataset))
-               goto cleanup;
+               goto addnsec3;
 
        /*
         * We've already added the NS record, so if the name's not there,
@@ -2666,12 +2718,54 @@ query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node,
        ISC_LIST_APPEND(rname->list, sigrdataset, link);
        rdataset = NULL;
        sigrdataset = NULL;
+       return;
+
+   addnsec3:
+       /*
+        * Add the NSEC3 which proves the DS does not exist.
+        */
+       dbuf = query_getnamebuf(client);
+       if (dbuf == NULL)
+               goto cleanup;
+       fname = query_newname(client, dbuf, &b);
+       dns_fixedname_init(&fixed);
+       query_findclosestnsec3(name, db, version, client, rdataset,
+                              sigrdataset, fname, ISC_TRUE,
+                              dns_fixedname_name(&fixed));
+       if (!dns_rdataset_isassociated(rdataset))
+               goto cleanup;
+       query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf,
+                      DNS_SECTION_AUTHORITY);
+       /*
+        * Did we find the closest provable encloser instead?
+        * If so add the nearest to the closest provable encloser.
+        */
+       if (!dns_name_equal(name, dns_fixedname_name(&fixed))) {
+               count = dns_name_countlabels(dns_fixedname_name(&fixed)) + 1;
+               dns_name_getlabelsequence(name,
+                                         dns_name_countlabels(name) - count,
+                                         count, dns_fixedname_name(&fixed));
+               fixfname(client, &fname, &dbuf, &b);
+               fixrdataset(client, &rdataset);
+               fixrdataset(client, &sigrdataset);
+               if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
+                               goto cleanup;
+               query_findclosestnsec3(dns_fixedname_name(&fixed), db, version,
+                                      client, rdataset, sigrdataset, fname,
+                                      ISC_FALSE, NULL);
+               if (!dns_rdataset_isassociated(rdataset))
+                       goto cleanup;
+               query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf,
+                              DNS_SECTION_AUTHORITY);
+       }
 
  cleanup:
        if (rdataset != NULL)
                query_putrdataset(client, &rdataset);
        if (sigrdataset != NULL)
                query_putrdataset(client, &sigrdataset);
+       if (fname != NULL)
+               query_releasename(client, &fname);
 }
 
 static void
@@ -2686,12 +2780,14 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db,
        dns_name_t *wname;
        dns_dbnode_t *node;
        unsigned int options;
-       unsigned int olabels, nlabels;
+       unsigned int olabels, nlabels, labels;
        isc_result_t result;
        dns_rdata_t rdata = DNS_RDATA_INIT;
        dns_rdata_nsec_t nsec;
        isc_boolean_t have_wname;
        int order;
+       dns_fixedname_t cfixed;
+       dns_name_t *cname;
 
        CTRACE("query_addwildcardproof");
        fname = NULL;
@@ -2762,7 +2858,99 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db,
                             0, &node, fname, rdataset, sigrdataset);
        if (node != NULL)
                dns_db_detachnode(db, &node);
-       if (result == DNS_R_NXDOMAIN) {
+
+       if (!dns_rdataset_isassociated(rdataset)) {
+               /*
+                * fname contains the closest encloser.
+                */
+               dns_fixedname_init(&cfixed);
+               cname = dns_fixedname_name(&cfixed);
+               dns_name_copy(fname, cname, NULL);
+
+               /*
+                * Add closest (provable) encloser NSEC3.
+                */
+               query_findclosestnsec3(cname, db, NULL, client, rdataset,
+                                      sigrdataset, fname, ISC_TRUE, cname);
+               if (!dns_rdataset_isassociated(rdataset))
+                       goto cleanup;
+               query_addrrset(client, &fname, &rdataset, &sigrdataset,
+                              dbuf, DNS_SECTION_AUTHORITY);
+
+               if (fname == NULL) {
+                       dbuf = query_getnamebuf(client);
+                       if (dbuf == NULL)
+                               goto cleanup;
+                       fname = query_newname(client, dbuf, &b);
+               }
+
+               if (rdataset == NULL)
+                       rdataset = query_newrdataset(client);
+               else if (dns_rdataset_isassociated(rdataset))
+                       dns_rdataset_disassociate(rdataset);
+
+               if (sigrdataset == NULL)
+                       sigrdataset = query_newrdataset(client);
+               else if (dns_rdataset_isassociated(sigrdataset))
+                       dns_rdataset_disassociate(sigrdataset);
+
+               if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
+                       goto cleanup;
+               /*
+                * Add no qname proof.
+                */
+               labels = dns_name_countlabels(cname) + 1;
+               if (dns_name_countlabels(name) == labels)
+                       dns_name_copy(name, wname, NULL);
+               else
+                       dns_name_split(name, labels, NULL, wname);
+
+               query_findclosestnsec3(wname, db, NULL, client, rdataset,
+                                      sigrdataset, fname, ISC_FALSE, NULL);
+               if (!dns_rdataset_isassociated(rdataset))
+                       goto cleanup;
+               query_addrrset(client, &fname, &rdataset, &sigrdataset,
+                              dbuf, DNS_SECTION_AUTHORITY);
+
+               if (ispositive)
+                       goto cleanup;
+
+               /*
+                * Add the no wildcard proof.
+                */
+               if (fname == NULL) {
+                       dbuf = query_getnamebuf(client);
+                       if (dbuf == NULL)
+                               goto cleanup;
+                       fname = query_newname(client, dbuf, &b);
+               }
+
+               if (rdataset == NULL)
+                       rdataset = query_newrdataset(client);
+               else if (dns_rdataset_isassociated(rdataset))
+                       dns_rdataset_disassociate(rdataset);
+
+               if (sigrdataset == NULL)
+                       sigrdataset = query_newrdataset(client);
+               else if (dns_rdataset_isassociated(sigrdataset))
+                       dns_rdataset_disassociate(sigrdataset);
+
+               if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
+                       goto cleanup;
+               result = dns_name_concatenate(dns_wildcardname,
+                                             cname, wname, NULL);
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
+
+               query_findclosestnsec3(wname, db, NULL, client, rdataset,
+                                      sigrdataset, fname, ISC_FALSE, NULL);
+               if (!dns_rdataset_isassociated(rdataset))
+                       goto cleanup;
+               query_addrrset(client, &fname, &rdataset, &sigrdataset,
+                              dbuf, DNS_SECTION_AUTHORITY);
+
+               goto cleanup;
+       } else if (result == DNS_R_NXDOMAIN) {
                if (!ispositive)
                        result = dns_rdataset_first(rdataset);
                if (result == ISC_R_SUCCESS) {
@@ -2839,6 +3027,7 @@ query_addnxrrsetnsec(ns_client_t *client, dns_db_t *db,
 
        if (sigrdatasetp == NULL)
                return;
+
        sigrdataset = *sigrdatasetp;
        if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset))
                return;
@@ -3161,35 +3350,60 @@ static void
 query_addnoqnameproof(ns_client_t *client, dns_rdataset_t *rdataset) {
        isc_buffer_t *dbuf, b;
        dns_name_t *fname;
-       dns_rdataset_t *nsec, *nsecsig;
+       dns_rdataset_t *neg, *negsig;
        isc_result_t result = ISC_R_NOMEMORY;
 
        CTRACE("query_addnoqnameproof");
 
        fname = NULL;
-       nsec = NULL;
-       nsecsig = NULL;
+       neg = NULL;
+       negsig = NULL;
 
        dbuf = query_getnamebuf(client);
        if (dbuf == NULL)
                goto cleanup;
        fname = query_newname(client, dbuf, &b);
-       nsec = query_newrdataset(client);
-       nsecsig = query_newrdataset(client);
-       if (fname == NULL || nsec == NULL || nsecsig == NULL)
+       neg = query_newrdataset(client);
+       negsig = query_newrdataset(client);
+       if (fname == NULL || neg == NULL || negsig == NULL)
+               goto cleanup;
+
+       result = dns_rdataset_getnoqname(rdataset, fname, neg, negsig);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+       query_addrrset(client, &fname, &neg, &negsig, dbuf,
+                      DNS_SECTION_AUTHORITY);
+
+       if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) == 0)
                goto cleanup;
 
-       result = dns_rdataset_getnoqname(rdataset, fname, nsec, nsecsig);
+       if (fname == NULL) {
+               dbuf = query_getnamebuf(client);
+               if (dbuf == NULL)
+                       goto cleanup;
+               fname = query_newname(client, dbuf, &b);
+       }
+       if (neg == NULL)
+               neg = query_newrdataset(client);
+       else if (dns_rdataset_isassociated(neg))
+               dns_rdataset_disassociate(neg);
+       if (negsig == NULL)
+               negsig = query_newrdataset(client);
+       else if (dns_rdataset_isassociated(negsig))
+               dns_rdataset_disassociate(negsig);
+       if (fname == NULL || neg == NULL || negsig == NULL)
+               goto cleanup;
+       result = dns_rdataset_getclosest(rdataset, fname, neg, negsig);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
-       query_addrrset(client, &fname, &nsec, &nsecsig, dbuf,
+       query_addrrset(client, &fname, &neg, &negsig, dbuf,
                       DNS_SECTION_AUTHORITY);
 
  cleanup:
-       if (nsec != NULL)
-               query_putrdataset(client, &nsec);
-       if (nsecsig != NULL)
-               query_putrdataset(client, &nsecsig);
+       if (neg != NULL)
+               query_putrdataset(client, &neg);
+       if (negsig != NULL)
+               query_putrdataset(client, &negsig);
        if (fname != NULL)
                query_releasename(client, &fname);
 }
@@ -3327,6 +3541,95 @@ warn_rfc1918(ns_client_t *client, dns_name_t *fname, dns_rdataset_t *rdataset) {
        }
 }
 
+static void
+query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
+                      dns_dbversion_t *version, ns_client_t *client,
+                      dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+                      dns_name_t *fname, isc_boolean_t exact,
+                      dns_name_t *found)
+{
+       unsigned char salt[256];
+       size_t salt_length = sizeof(salt);
+       isc_uint16_t iterations;
+       isc_result_t result;
+       unsigned int dboptions;
+       dns_fixedname_t fixed;
+       dns_hash_t hash;
+       dns_name_t name;
+       int order;
+       unsigned int count;
+       dns_rdata_nsec3_t nsec3;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       isc_boolean_t optout;
+
+       salt_length = sizeof(salt);
+       result = dns_db_getnsec3parameters(db, version, &hash, NULL,
+                                          &iterations, salt, &salt_length);
+       if (result != ISC_R_SUCCESS)
+               return;
+
+       dns_name_init(&name, NULL);
+       dns_name_clone(qname, &name);
+
+       /*
+        * Map unknown algorithm to known value.
+        */
+       if (hash == DNS_NSEC3_UNKNOWNALG)
+               hash = 1;
+
+ again:
+       dns_fixedname_init(&fixed);
+       result = dns_nsec3_hashname(&fixed, NULL, NULL, &name,
+                                   dns_db_origin(db), hash,
+                                   iterations, salt, salt_length);
+       if (result != ISC_R_SUCCESS)
+               return;
+
+       dboptions = client->query.dboptions | DNS_DBFIND_FORCENSEC3;
+       result = dns_db_find(db, dns_fixedname_name(&fixed), version,
+                            dns_rdatatype_nsec3, dboptions, client->now,
+                            NULL, fname, rdataset, sigrdataset);
+
+       if (result == DNS_R_NXDOMAIN) {
+               if (!dns_rdataset_isassociated(rdataset)) {
+                       return;
+               }
+               result = dns_rdataset_first(rdataset);
+               INSIST(result == ISC_R_SUCCESS);
+               dns_rdataset_current(rdataset, &rdata);
+               dns_rdata_tostruct(&rdata, &nsec3, NULL);
+               dns_rdata_reset(&rdata);
+               optout = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0);
+               if (found != NULL && optout &&
+                   dns_name_fullcompare(&name, dns_db_origin(db), &order,
+                                        &count) == dns_namereln_subdomain) {
+                       dns_rdataset_disassociate(rdataset);
+                       if (dns_rdataset_isassociated(sigrdataset))
+                               dns_rdataset_disassociate(sigrdataset);
+                       count = dns_name_countlabels(&name) - 1;
+                       dns_name_getlabelsequence(&name, 1, count, &name);
+                       ns_client_log(client, DNS_LOGCATEGORY_DNSSEC,
+                                     NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(3),
+                                     "looking for closest provable encloser");
+                       goto again;
+               }
+               if (exact)
+                       ns_client_log(client, DNS_LOGCATEGORY_DNSSEC,
+                                     NS_LOGMODULE_QUERY, ISC_LOG_WARNING,
+                                     "expected a exact match NSEC3, got "
+                                     "a covering record");
+
+       } else if (result != ISC_R_SUCCESS) {
+               return;
+       } else if (!exact)
+               ns_client_log(client, DNS_LOGCATEGORY_DNSSEC,
+                             NS_LOGMODULE_QUERY, ISC_LOG_WARNING,
+                             "expected covering NSEC3, got an exact match");
+       if (found != NULL)
+               dns_name_copy(&name, found, NULL);
+       return;
+}
+
 /*
  * Do the bulk of query processing for the current query of 'client'.
  * If 'event' is non-NULL, we are returning from recursion and 'qtype'
@@ -3353,7 +3656,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
        isc_result_t result, eresult;
        dns_fixedname_t fixed;
        dns_fixedname_t wildcardname;
-       dns_dbversion_t *version;
+       dns_dbversion_t *version, *zversion;
        dns_zone_t *zone;
        dns_rdata_cname_t cname;
        dns_rdata_dname_t dname;
@@ -3378,6 +3681,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
        zrdataset = NULL;
        sigrdataset = NULL;
        zsigrdataset = NULL;
+       zversion = NULL;
        node = NULL;
        db = NULL;
        zdb = NULL;
@@ -3707,6 +4011,12 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                         * We're authoritative for an ancestor of QNAME.
                         */
                        if (!USECACHE(client) || !RECURSIONOK(client)) {
+                               dns_fixedname_t fixed;
+
+                               dns_fixedname_init(&fixed);
+                               dns_name_copy(fname,
+                                             dns_fixedname_name(&fixed), NULL);
+
                                /*
                                 * If we don't have a cache, this is the best
                                 * answer.
@@ -3740,8 +4050,9 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                               &rdataset, sigrdatasetp,
                                               dbuf, DNS_SECTION_AUTHORITY);
                                client->query.gluedb = NULL;
-                               if (WANTDNSSEC(client) && dns_db_issecure(db))
-                                       query_addds(client, db, node, version);
+                               if (WANTDNSSEC(client))
+                                       query_addds(client, db, node, version,
+                                                  dns_fixedname_name(&fixed));
                        } else {
                                /*
                                 * We might have a better answer or delegation
@@ -3760,6 +4071,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                zsigrdataset = sigrdataset;
                                sigrdataset = NULL;
                                dns_db_detachnode(db, &node);
+                               zversion = version;
                                version = NULL;
                                db = NULL;
                                dns_db_attach(client->view->cachedb, &db);
@@ -3793,6 +4105,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                zrdataset = NULL;
                                sigrdataset = zsigrdataset;
                                zsigrdataset = NULL;
+                               version = zversion;
+                               zversion = NULL;
                                /*
                                 * We don't clean up zdb here because we
                                 * may still need it.  It will get cleaned
@@ -3821,6 +4135,11 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                else
                                        QUERY_ERROR(DNS_R_SERVFAIL);
                        } else {
+                               dns_fixedname_t fixed;
+
+                               dns_fixedname_init(&fixed);
+                               dns_name_copy(fname,
+                                             dns_fixedname_name(&fixed), NULL);
                                /*
                                 * This is the best answer.
                                 */
@@ -3847,7 +4166,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                client->query.attributes &=
                                        ~NS_QUERYATTR_CACHEGLUEOK;
                                if (WANTDNSSEC(client))
-                                       query_addds(client, db, node, version);
+                                       query_addds(client, db, node, version,
+                                                  dns_fixedname_name(&fixed));
                        }
                }
                goto cleanup;
@@ -3856,6 +4176,80 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                /* FALLTHROUGH */
        case DNS_R_NXRRSET:
                INSIST(is_zone);
+               /*
+                * Look for a NSEC3 record if we don't have a NSEC record.
+                */
+               if (!dns_rdataset_isassociated(rdataset) &&
+                    WANTDNSSEC(client)) {
+                       if ((fname->attributes & DNS_NAMEATTR_WILDCARD) == 0) {
+                               dns_name_t *found;
+                               dns_name_t *qname;
+
+                               dns_fixedname_init(&fixed);
+                               found = dns_fixedname_name(&fixed);
+                               qname = client->query.qname;
+
+                               query_findclosestnsec3(qname, db, version,
+                                                      client, rdataset,
+                                                      sigrdataset, fname,
+                                                      ISC_TRUE, found);
+                               /*
+                                * Did we find the closest provable encloser
+                                * instead? If so add the nearest to the
+                                * closest provable encloser.
+                                */
+                               if (found &&
+                                   dns_rdataset_isassociated(rdataset) &&
+                                   !dns_name_equal(qname, found))
+                               { 
+                                       unsigned int count;
+                                       unsigned int skip;
+
+                                       /*
+                                        * Add the closest provable encloser.
+                                        */
+                                       query_addrrset(client, &fname,
+                                                      &rdataset, &sigrdataset,
+                                                      dbuf,
+                                                      DNS_SECTION_AUTHORITY);
+
+                                       count = dns_name_countlabels(found)
+                                                        + 1;
+                                       skip = dns_name_countlabels(qname) -
+                                                        count;
+                                       dns_name_getlabelsequence(qname, skip,
+                                                                 count,
+                                                                 found);
+
+                                       fixfname(client, &fname, &dbuf, &b);
+                                       fixrdataset(client, &rdataset);
+                                       fixrdataset(client, &sigrdataset);
+                                       if (fname == NULL ||
+                                           rdataset == NULL ||
+                                           sigrdataset == NULL) {
+                                               QUERY_ERROR(DNS_R_SERVFAIL);
+                                               goto cleanup;
+                                       }
+                                       /*
+                                        * 'nearest' doesn't exist so
+                                        * 'exist' is set to ISC_FALSE.
+                                        */
+                                       query_findclosestnsec3(found, db,
+                                                              version,
+                                                              client,
+                                                              rdataset,
+                                                              sigrdataset,
+                                                              fname,
+                                                              ISC_FALSE,
+                                                              NULL);
+                               }
+                       } else {
+                               query_releasename(client, &fname);
+                               query_addwildcardproof(client, db, version,
+                                                      client->query.qname,
+                                                      ISC_FALSE);
+                       }
+               }
                if (dns_rdataset_isassociated(rdataset)) {
                        /*
                         * If we've got a NSEC record, we need to save the
@@ -3863,7 +4257,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                         * below, and it needs to use the name buffer.
                         */
                        query_keepname(client, fname, dbuf);
-               } else {
+               } else if (fname != NULL) {
                        /*
                         * We're not going to use fname, and need to release
                         * our hold on the name buffer so query_addsoa()
@@ -3889,9 +4283,11 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                                     &sigrdataset);
                }
                goto cleanup;
+
        case DNS_R_EMPTYWILD:
                empty_wild = ISC_TRUE;
                /* FALLTHROUGH */
+
        case DNS_R_NXDOMAIN:
                INSIST(is_zone);
                if (dns_rdataset_isassociated(rdataset)) {
@@ -3901,7 +4297,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                         * below, and it needs to use the name buffer.
                         */
                        query_keepname(client, fname, dbuf);
-               } else {
+               } else if (fname != NULL) {
                        /*
                         * We're not going to use fname, and need to release
                         * our hold on the name buffer so query_addsoa()
@@ -3927,19 +4323,19 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        QUERY_ERROR(result);
                        goto cleanup;
                }
-               /*
-                * Add NSEC record if we found one.
-                */
-               if (dns_rdataset_isassociated(rdataset)) {
-                       if (WANTDNSSEC(client)) {
+
+               if (WANTDNSSEC(client)) {
+                       /*
+                        * Add NSEC record if we found one.
+                        */
+                       if (dns_rdataset_isassociated(rdataset))
                                query_addrrset(client, &fname, &rdataset,
                                               &sigrdataset,
                                               NULL, DNS_SECTION_AUTHORITY);
-                               query_addwildcardproof(client, db, version,
-                                                      client->query.qname,
-                                                      ISC_FALSE);
-                       }
+                       query_addwildcardproof(client, db, version,
+                                              client->query.qname, ISC_FALSE);
                }
+
                /*
                 * Set message rcode.
                 */
@@ -3948,6 +4344,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                else
                        client->message->rcode = dns_rcode_nxdomain;
                goto cleanup;
+
        case DNS_R_NCACHENXDOMAIN:
        case DNS_R_NCACHENXRRSET:
                INSIST(!is_zone);
@@ -3976,6 +4373,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                fname = NULL;
                rdataset = NULL;
                goto cleanup;
+
        case DNS_R_CNAME:
                /*
                 * Keep a copy of the rdataset.  We have to do this because
@@ -3998,8 +4396,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                      NULL);
                        need_wildcardproof = ISC_TRUE;
                }
-               if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0 &&
-                    WANTDNSSEC(client))
+               if (NOQNAME(rdataset) && WANTDNSSEC(client))
                        noqname = rdataset;
                else
                        noqname = NULL;
@@ -4218,15 +4615,21 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                dns_rdataset_disassociate(rdataset);
                        } else if ((qtype == dns_rdatatype_any ||
                             rdataset->type == qtype) && rdataset->type != 0) {
+                               if (NOQNAME(rdataset) && WANTDNSSEC(client))
+                                       noqname = rdataset;
+                               else
+                                       noqname = NULL;
                                query_addrrset(client,
                                               fname != NULL ? &fname : &tname,
                                               &rdataset, NULL,
                                               NULL, DNS_SECTION_ANSWER);
+                               if (noqname != NULL)
+                                       query_addnoqnameproof(client, noqname);
                                n++;
                                INSIST(tname != NULL);
                                /*
-                                * rdataset is non-NULL only in certain pathological
-                                * cases involving DNAMEs.
+                                * rdataset is non-NULL only in certain
+                                * pathological cases involving DNAMEs.
                                 */
                                if (rdataset != NULL)
                                        query_putrdataset(client, &rdataset);
@@ -4245,7 +4648,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                if (fname != NULL)
                        dns_message_puttempname(client->message, &fname);
 
-               if (n == 0) {
+               if (n == 0 && is_zone) {
                        /*
                         * We didn't match any rdatasets.
                         */
@@ -4306,8 +4709,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        sigrdatasetp = &sigrdataset;
                else
                        sigrdatasetp = NULL;
-               if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0 &&
-                    WANTDNSSEC(client))
+               if (NOQNAME(rdataset) && WANTDNSSEC(client))
                        noqname = rdataset;
                else
                        noqname = NULL;
index 67b67b679dfc45e836d5ce47d760a23dbd9d3ddc..1898e5e0c7628f74684de800b6bd8d529746de99 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: update.c,v 1.146 2008/04/03 05:55:51 marka Exp $ */
+/* $Id: update.c,v 1.147 2008/09/24 02:46:21 marka Exp $ */
 
 #include <config.h>
 
@@ -36,6 +36,7 @@
 #include <dns/keyvalues.h>
 #include <dns/message.h>
 #include <dns/nsec.h>
+#include <dns/nsec3.h>
 #include <dns/rdataclass.h>
 #include <dns/rdataset.h>
 #include <dns/rdatasetiter.h>
@@ -577,7 +578,11 @@ foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
                                        rr_action, rr_action_data));
 
        node = NULL;
-       result = dns_db_findnode(db, name, ISC_FALSE, &node);
+       if (type == dns_rdatatype_nsec3 ||
+           (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3))
+               result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
+       else
+               result = dns_db_findnode(db, name, ISC_FALSE, &node);
        if (result == ISC_R_NOTFOUND)
                return (ISC_R_SUCCESS);
        if (result != ISC_R_SUCCESS)
@@ -1044,13 +1049,14 @@ typedef struct {
 
 /*%
  * Return true iff 'db_rr' is neither a SOA nor an NS RR nor
- * an RRSIG nor a NSEC.
+ * an RRSIG nor an NSEC3PARAM nor a NSEC.
  */
 static isc_boolean_t
 type_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
        UNUSED(update_rr);
        return ((db_rr->type != dns_rdatatype_soa &&
                 db_rr->type != dns_rdatatype_ns &&
+                db_rr->type != dns_rdatatype_nsec3param &&
                 db_rr->type != dns_rdatatype_rrsig &&
                 db_rr->type != dns_rdatatype_nsec) ?
                ISC_TRUE : ISC_FALSE);
@@ -1137,13 +1143,27 @@ replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
                 * Compare the address and protocol fields only.  These
                 * form the first five bytes of the RR data.  Do a
                 * raw binary comparison; unpacking the WKS RRs using
-                * dns_rdata_tostruct() might be cleaner in some ways,
-                * but it would require us to pass around an mctx.
+                * dns_rdata_tostruct() might be cleaner in some ways.
                 */
                INSIST(db_rr->length >= 5 && update_rr->length >= 5);
                return (memcmp(db_rr->data, update_rr->data, 5) == 0 ?
                        ISC_TRUE : ISC_FALSE);
        }
+
+       if (db_rr->type == dns_rdatatype_nsec3param) {
+               if (db_rr->length != update_rr->length)
+                       return (ISC_FALSE);
+               INSIST(db_rr->length >= 4 && update_rr->length >= 4);
+               /*
+                * Replace records added in this UPDATE request.
+                */
+               if (db_rr->data[0] == update_rr->data[0] &&
+                   db_rr->data[1] & DNS_NSEC3FLAG_UPDATE &&
+                   update_rr->data[1] & DNS_NSEC3FLAG_UPDATE &&
+                   memcmp(db_rr->data+2, update_rr->data+2,
+                          update_rr->length - 2) == 0)
+                       return (ISC_TRUE);
+       }
        return (ISC_FALSE);
 }
 
@@ -1417,7 +1437,7 @@ namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected)
        dns_fixedname_init(&fixedname);
        child = dns_fixedname_name(&fixedname);
 
-       CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
+       CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit));
 
        for (result = dns_dbiterator_seek(dbit, name);
             result == ISC_R_SUCCESS;
@@ -1447,8 +1467,10 @@ static isc_result_t
 is_non_nsec_action(void *data, dns_rdataset_t *rrset) {
        UNUSED(data);
        if (!(rrset->type == dns_rdatatype_nsec ||
+             rrset->type == dns_rdatatype_nsec3 ||
              (rrset->type == dns_rdatatype_rrsig &&
-              rrset->covers == dns_rdatatype_nsec)))
+              (rrset->covers == dns_rdatatype_nsec ||
+               rrset->covers == dns_rdatatype_nsec3))))
                return (ISC_R_EXISTS);
        return (ISC_R_SUCCESS);
 }
@@ -1504,7 +1526,6 @@ uniqify_name_list(dns_diff_t *list) {
        return (result);
 }
 
-
 static isc_result_t
 is_glue(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
        isc_boolean_t *flag)
@@ -1534,6 +1555,50 @@ is_glue(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
        }
 }
 
+static isc_result_t
+is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+         isc_boolean_t *flag, isc_boolean_t *unsecure)
+{
+       isc_result_t result;
+       dns_fixedname_t foundname;
+       dns_fixedname_init(&foundname);
+       result = dns_db_find(db, name, ver, dns_rdatatype_any,
+                            DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
+                            (isc_stdtime_t) 0, NULL,
+                            dns_fixedname_name(&foundname),
+                            NULL, NULL);
+       if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) {
+               *flag = ISC_TRUE;
+               if (unsecure != NULL)
+                       *unsecure = ISC_FALSE;
+               return (ISC_R_SUCCESS);
+       } else if (result == DNS_R_ZONECUT) {
+               *flag = ISC_TRUE;
+               if (unsecure != NULL) {
+                       /*
+                        * We are at the zonecut.  Check to see if there
+                        * is a DS RRset.
+                        */
+                       if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0,
+                                       (isc_stdtime_t) 0, NULL,
+                                       dns_fixedname_name(&foundname),
+                                       NULL, NULL) == DNS_R_NXRRSET)
+                               *unsecure = ISC_TRUE;
+                       else 
+                               *unsecure = ISC_FALSE;
+               }
+               return (ISC_R_SUCCESS);
+       } else if (result == DNS_R_GLUE || result == DNS_R_DNAME ||
+                  result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) {
+               *flag = ISC_FALSE;
+               if (unsecure != NULL)
+                       *unsecure = ISC_FALSE;
+               return (ISC_R_SUCCESS);
+       } else {
+               return (result);
+       }
+}
+
 /*%
  * Find the next/previous name that has a NSEC record.
  * In other words, skip empty database nodes and names that
@@ -1551,7 +1616,7 @@ next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
        unsigned int wraps = 0;
        isc_boolean_t secure = dns_db_issecure(db);
 
-       CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
+       CHECK(dns_db_createiterator(db, 0, &dbit));
 
        CHECK(dns_dbiterator_seek(dbit, oldname));
        do {
@@ -1639,12 +1704,12 @@ has_opt_bit(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) {
 
 static void
 set_bit(unsigned char *array, unsigned int index) {
-       unsigned int shift, mask;
+       unsigned int shift, bit;
 
        shift = 7 - (index % 8);
-       mask = 1 << shift;
+       bit = 1 << shift;
 
-       array[index / 8] |= mask;
+       array[index / 8] |= bit;
 }
 
 /*%
@@ -1818,7 +1883,10 @@ add_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
        isc_buffer_init(&buffer, data, sizeof(data));
 
        /* Get the rdataset to sign. */
-       CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+       if (type == dns_rdatatype_nsec3)
+               CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node));
+       else
+               CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
        CHECK(dns_db_findrdataset(db, node, ver, type, 0,
                                  (isc_stdtime_t) 0, &rdataset, NULL));
        dns_db_detachnode(db, &node);
@@ -1936,12 +2004,12 @@ failure:
 }
 
 /*%
- * Update RRSIG and NSEC records affected by an update.  The original
+ * Update RRSIG, NSEC and NSEC3 records affected by an update.  The original
  * update, including the SOA serial update but exluding the RRSIG & NSEC
  * changes, is in "diff" and has already been applied to "newver" of "db".
  * The database version prior to the update is "oldver".
  *
- * The necessary RRSIG and NSEC changes will be applied to "newver"
+ * The necessary RRSIG, NSEC and NSEC3 changes will be applied to "newver"
  * and added (as a minimal diff) to "diff".
  *
  * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds.
@@ -1970,6 +2038,7 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
        dns_rdataset_t rdataset;
        dns_dbnode_t *node = NULL;
        isc_boolean_t check_ksk;
+       isc_boolean_t unsecure;
 
        dns_diff_init(client->mctx, &diffnames);
        dns_diff_init(client->mctx, &affected);
@@ -2022,7 +2091,7 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
                *deleted_zsk = ISC_FALSE;
 
        /*
-        * Get the NSEC's TTL from the SOA MINIMUM field.
+        * Get the NSEC/NSEC3 TTL from the SOA MINIMUM field.
         */
        CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
        dns_rdataset_init(&rdataset);
@@ -2098,6 +2167,7 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
                        }
                }
        }
+       update_log(client, zone, ISC_LOG_DEBUG(3), "updated data signatures");
 
        /* Remove orphaned NSECs and RRSIG NSECs. */
        for (t = ISC_LIST_HEAD(diffnames.tuples);
@@ -2111,6 +2181,19 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
                                        NULL, &sig_diff));
                }
        }
+       update_log(client, zone, ISC_LOG_DEBUG(3),
+                  "removed any orphaned NSEC records");
+
+       /*
+        * If we don't have a NSEC record at the origin then we need to
+        * update the NSEC3 records.
+        */
+       CHECK(rrset_exists(db, newver, dns_db_origin(db), dns_rdatatype_nsec,
+                          0, &flag));
+       if (!flag)
+               goto update_nsec3;
+
+       update_log(client, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC chain");
 
        /*
         * When a name is created or deleted, its predecessor needs to
@@ -2213,7 +2296,7 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
                                           dns_rdatatype_nsec, 0, &flag));
                        if (! flag)
                                CHECK(add_placeholder_nsec(db, newver, &t->name,
-                                                         diff));
+                                                          diff));
                }
        }
 
@@ -2259,6 +2342,9 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
                dns_diff_appendminimal(&nsec_mindiff, &t);
        }
 
+       update_log(client, zone, ISC_LOG_DEBUG(3),
+                  "signing rebuilt NSEC chain");
+
        /* Update RRSIG NSECs. */
        for (t = ISC_LIST_HEAD(nsec_mindiff.tuples);
             t != NULL;
@@ -2278,6 +2364,134 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
                }
        }
 
+ update_nsec3:
+
+       /* Record our changes for the journal. */
+       while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) {
+               ISC_LIST_UNLINK(sig_diff.tuples, t, link);
+               dns_diff_appendminimal(diff, &t);
+       }
+       while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) {
+               ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link);
+               dns_diff_appendminimal(diff, &t);
+       }
+
+       INSIST(ISC_LIST_EMPTY(sig_diff.tuples));
+       INSIST(ISC_LIST_EMPTY(nsec_diff.tuples));
+       INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples));
+
+       /*
+        * Check if we have any active NSEC3 chains by looking for a
+        * NSEC3PARAM RRset.
+        */
+       CHECK(rrset_exists(db, newver, dns_db_origin(db),
+                          dns_rdatatype_nsec3param, 0, &flag));
+       if (!flag) {
+               update_log(client, zone, ISC_LOG_DEBUG(3),
+                          "no NSEC3 chains to rebuild");
+               goto failure;
+       }
+
+       update_log(client, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC3 chains");
+
+       dns_diff_clear(&diffnames);
+       dns_diff_clear(&affected);
+
+       CHECK(dns_diff_sort(diff, temp_order));
+
+       /*
+        * Find names potentially affected by delegation changes
+        * (obscured by adding an NS or DNAME, or unobscured by
+        * removing one).
+        */
+       t = ISC_LIST_HEAD(diff->tuples);
+       while (t != NULL) {
+               dns_name_t *name = &t->name;
+
+               isc_boolean_t ns_existed, dname_existed;
+               isc_boolean_t ns_exists, dname_exists;
+
+               if (t->rdata.type == dns_rdatatype_nsec ||
+                   t->rdata.type == dns_rdatatype_rrsig) {
+                       t = ISC_LIST_NEXT(t, link);
+                       continue;
+               }
+
+               CHECK(namelist_append_name(&affected, name));
+
+               CHECK(rrset_exists(db, oldver, name, dns_rdatatype_ns, 0,
+                                  &ns_existed));
+               CHECK(rrset_exists(db, oldver, name, dns_rdatatype_dname, 0,
+                                  &dname_existed));
+               CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns, 0,
+                                  &ns_exists));
+               CHECK(rrset_exists(db, newver, name, dns_rdatatype_dname, 0,
+                                  &dname_exists));
+
+               if ((ns_exists || dname_exists) == (ns_existed || dname_existed))
+                       goto nextname;
+               /*
+                * There was a delegation change.  Mark all subdomains
+                * of t->name as potentially needing a NSEC3 update.
+                */
+               CHECK(namelist_append_subdomain(db, name, &affected));
+
+       nextname:
+               while (t != NULL && dns_name_equal(&t->name, name))
+                       t = ISC_LIST_NEXT(t, link);
+       }
+
+       for (t = ISC_LIST_HEAD(affected.tuples);
+            t != NULL;
+            t = ISC_LIST_NEXT(t, link)) {
+               dns_name_t *name = &t->name;
+
+               unsecure = ISC_FALSE;   /* Silence compiler warning. */
+               CHECK(is_active(db, newver, name, &flag, &unsecure));
+
+               if (!flag) {
+                       CHECK(dns_nsec3_delnsec3s(db, newver, name,
+                                                 &nsec_diff));
+               } else {
+                       CHECK(dns_nsec3_addnsec3s(db, newver, name, nsecttl,
+                                                 unsecure, &nsec_diff));
+               }
+       }
+
+       /*
+        * Minimize the set of NSEC3 updates so that we don't
+        * have to regenerate the RRSIG NSEC3s for NSEC3s that were
+        * replaced with identical ones.
+        */
+       while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) {
+               ISC_LIST_UNLINK(nsec_diff.tuples, t, link);
+               dns_diff_appendminimal(&nsec_mindiff, &t);
+       }
+
+       update_log(client, zone, ISC_LOG_DEBUG(3),
+                  "signing rebuilt NSEC3 chain");
+
+       /* Update RRSIG NSEC3s. */
+       for (t = ISC_LIST_HEAD(nsec_mindiff.tuples);
+            t != NULL;
+            t = ISC_LIST_NEXT(t, link))
+       {
+               if (t->op == DNS_DIFFOP_DEL) {
+                       CHECK(delete_if(true_p, db, newver, &t->name,
+                                       dns_rdatatype_rrsig,
+                                       dns_rdatatype_nsec3,
+                                       NULL, &sig_diff));
+               } else if (t->op == DNS_DIFFOP_ADD) {
+                       CHECK(add_sigs(client, zone, db, newver, &t->name,
+                                      dns_rdatatype_nsec3,
+                                      &sig_diff, zone_keys, nkeys,
+                                      client->mctx, inception, expire,
+                                      check_ksk));
+               } else {
+                       INSIST(0);
+               }
+       }
+
        /* Record our changes for the journal. */
        while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) {
                ISC_LIST_UNLINK(sig_diff.tuples, t, link);
@@ -2441,7 +2655,7 @@ ns_update_start(ns_client_t *client, isc_result_t sigresult) {
 
 /*%
  * DS records are not allowed to exist without corresponding NS records,
- * draft-ietf-dnsext-delegation-signer-11.txt, 2.2 Protocol Change,
+ * RFC 3658, 2.2 Protocol Change,
  * "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex".
  */
 
@@ -2580,23 +2794,340 @@ check_mx(ns_client_t *client, dns_zone_t *zone,
        return (ok ? ISC_R_SUCCESS : DNS_R_REFUSED);
 }
 
+static isc_result_t
+rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+         const dns_rdata_t *rdata, isc_boolean_t *flag)
+{
+       dns_rdataset_t rdataset;
+       dns_dbnode_t *node = NULL;
+       isc_result_t result;
+
+       dns_rdataset_init(&rdataset);
+       if (rdata->type == dns_rdatatype_nsec3)
+               CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node));
+       else
+               CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+       result = dns_db_findrdataset(db, node, ver, rdata->type, 0,
+                                    (isc_stdtime_t) 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND) {
+               *flag = ISC_FALSE;
+               result = ISC_R_SUCCESS;
+               goto failure;
+       }
+
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t myrdata = DNS_RDATA_INIT;
+               dns_rdataset_current(&rdataset, &myrdata);
+               if (!dns_rdata_compare(&myrdata, rdata))
+                       break;
+       }
+       dns_rdataset_disassociate(&rdataset);
+       if (result == ISC_R_SUCCESS) {
+               *flag = ISC_TRUE;
+       } else if (result == ISC_R_NOMORE) {
+               *flag = ISC_FALSE;
+               result = ISC_R_SUCCESS;
+       }
+
+ failure:
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       return (result);
+}
+
+static isc_result_t
+get_iterations(dns_db_t *db, dns_dbversion_t *ver, unsigned int *iterationsp) {
+       dns_dbnode_t *node = NULL;
+       dns_rdata_nsec3param_t nsec3param;
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+       unsigned int iterations = 0;
+
+       dns_rdataset_init(&rdataset);
+
+       result = dns_db_getoriginnode(db, &node);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+                                    0, (isc_stdtime_t) 0, &rdataset, NULL);
+       dns_db_detachnode(db, &node);
+       if (result == ISC_R_NOTFOUND)
+               goto success;
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+               dns_rdataset_current(&rdataset, &rdata);
+               CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+               if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0)
+                       continue;
+               if (nsec3param.iterations > iterations)
+                       iterations = nsec3param.iterations;
+       }
+       if (result != ISC_R_NOMORE)
+               goto failure;
+
+ success:
+       *iterationsp = iterations;
+       result = ISC_R_SUCCESS;
+
+ failure:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       return (result);
+}
+
+/*
+ * Prevent the zone entering a inconsistant state where
+ * NSEC only DNSKEYs are present with NSEC3 chains.
+ */
+static isc_result_t
+check_dnssec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+            dns_dbversion_t *ver, dns_diff_t *diff)
+{
+       dns_diff_t temp_diff;
+       dns_diffop_t op;
+       dns_difftuple_t *tuple, *newtuple = NULL, *next;
+       isc_boolean_t flag;
+       isc_result_t result;
+       unsigned int iterations = 0, max;
+       
+       dns_diff_init(diff->mctx, &temp_diff);
+
+       CHECK(dns_nsec_nseconly(db, ver, &flag));
+
+       if (flag) 
+               CHECK(dns_nsec3_active(db, ver, ISC_FALSE, &flag));
+       if (flag) {
+               update_log(client, zone, ISC_LOG_WARNING,
+                          "NSEC only DNSKEYs and NSEC3 chains not allowed");
+       } else {
+               CHECK(get_iterations(db, ver, &iterations));
+               CHECK(dns_nsec3_maxiterations(db, ver, client->mctx, &max));
+               if (iterations > max) {
+                       flag = ISC_TRUE;
+                       update_log(client, zone, ISC_LOG_WARNING,
+                                  "too many NSEC3 iterations (%u) for "
+                                  "weakest DNSKEY (%u)", iterations, max);
+               }
+       }
+       if (flag) {
+               for (tuple = ISC_LIST_HEAD(diff->tuples);
+                    tuple != NULL;
+                    tuple = next) {
+                       next = ISC_LIST_NEXT(tuple, link);
+                       if (tuple->rdata.type != dns_rdatatype_dnskey &&
+                           tuple->rdata.type != dns_rdatatype_nsec3param)
+                               continue;
+                       op = (tuple->op == DNS_DIFFOP_DEL) ?
+                            DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
+                       CHECK(dns_difftuple_create(temp_diff.mctx, op,
+                                                  &tuple->name, tuple->ttl,
+                                                  &tuple->rdata, &newtuple));
+                       CHECK(do_one_tuple(&newtuple, db, ver, &temp_diff));
+                       INSIST(newtuple == NULL);
+               }
+               for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+                    tuple != NULL;
+                    tuple = ISC_LIST_HEAD(temp_diff.tuples)) {
+                       ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+                       dns_diff_appendminimal(diff, &tuple);
+               }
+       }
+
+
+ failure:
+       dns_diff_clear(&temp_diff);
+       return (result);
+}
+
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+/*
+ * Delay NSEC3PARAM changes as they need to be applied to the whole zone.
+ */
+static isc_result_t
+add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+                      dns_name_t *name, dns_dbversion_t *ver, dns_diff_t *diff)
+{
+       isc_result_t result = ISC_R_SUCCESS;
+       dns_difftuple_t *tuple, *newtuple = NULL, *next;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+       dns_diff_t temp_diff;
+       dns_diffop_t op;
+       isc_boolean_t flag;
+
+       update_log(client, zone, ISC_LOG_DEBUG(3),
+                   "checking for NSEC3PARAM changes");
+
+       dns_diff_init(diff->mctx, &temp_diff);
+
+       /*
+        * Extract NSEC3PARAM tuples from list.
+        */
+       for (tuple = ISC_LIST_HEAD(diff->tuples);
+            tuple != NULL;
+            tuple = next) {
+
+               next = ISC_LIST_NEXT(tuple, link);
+
+               if (tuple->rdata.type != dns_rdatatype_nsec3param ||
+                   !dns_name_equal(name, &tuple->name))
+                       continue;
+               ISC_LIST_UNLINK(diff->tuples, tuple, link);
+               ISC_LIST_APPEND(temp_diff.tuples, tuple, link);
+       }
+
+       for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+            tuple != NULL; tuple = next) {
+
+               if (tuple->op == DNS_DIFFOP_ADD) {
+                       next = ISC_LIST_NEXT(tuple, link);
+                       while (next != NULL) {
+                               unsigned char *next_data = next->rdata.data;
+                               unsigned char *tuple_data = tuple->rdata.data;
+                               if (next_data[0] != tuple_data[0] ||
+                                       /* Ignore flags. */
+                                   next_data[2] != tuple_data[2] ||
+                                   next_data[3] != tuple_data[3] ||
+                                   next_data[4] != tuple_data[4] ||
+                                   !memcmp(&next_data[5], &tuple_data[5],
+                                           tuple_data[4])) {
+                                       next = ISC_LIST_NEXT(next, link);
+                                       continue;
+                               }
+                               op = (next->op == DNS_DIFFOP_DEL) ?
+                                    DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
+                               CHECK(dns_difftuple_create(diff->mctx, op,
+                                                          name, next->ttl,
+                                                          &next->rdata,
+                                                          &newtuple));
+                               CHECK(do_one_tuple(&newtuple, db, ver, diff));
+                               ISC_LIST_UNLINK(temp_diff.tuples, next, link);
+                               dns_diff_appendminimal(diff, &next);
+                               next = ISC_LIST_NEXT(tuple, link);
+                       }
+
+                       INSIST(tuple->rdata.data[1] & DNS_NSEC3FLAG_UPDATE);
+
+                       /*
+                        * See if we already have a CREATE request in progress.
+                        */
+                       dns_rdata_clone(&tuple->rdata, &rdata);
+                       INSIST(rdata.length <= sizeof(buf));
+                       memcpy(buf, rdata.data, rdata.length);
+                       buf[1] |= DNS_NSEC3FLAG_CREATE;
+                       buf[1] &= ~DNS_NSEC3FLAG_UPDATE;
+                       rdata.data = buf;
+
+                       CHECK(rr_exists(db, ver, name, &rdata, &flag));
+
+                       if (!flag) {
+                               CHECK(dns_difftuple_create(diff->mctx,
+                                                          DNS_DIFFOP_ADD,
+                                                          name, tuple->ttl,
+                                                          &rdata,
+                                                          &newtuple));
+                               CHECK(do_one_tuple(&newtuple, db, ver, diff));
+                       }
+                       /*
+                        * Remove the temporary add record.
+                        */
+                       CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+                                                  name, tuple->ttl,
+                                                  &tuple->rdata, &newtuple));
+                       CHECK(do_one_tuple(&newtuple, db, ver, diff));
+                       next = ISC_LIST_NEXT(tuple, link);
+                       ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+                       dns_diff_appendminimal(diff, &tuple);
+                       dns_rdata_reset(&rdata);
+               } else
+                       next = ISC_LIST_NEXT(tuple, link);
+       }
+
+       /*
+        * Reverse any pending changes.
+        */
+       for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+            tuple != NULL; tuple = next) {
+               next = ISC_LIST_NEXT(tuple, link);
+               if ((tuple->rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) != 0) {
+                       op = (tuple->op == DNS_DIFFOP_DEL) ?
+                            DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
+                       CHECK(dns_difftuple_create(diff->mctx, op, name,
+                                                  tuple->ttl, &tuple->rdata,
+                                                  &newtuple));
+                       CHECK(do_one_tuple(&newtuple, db, ver, diff));
+                       ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+                       dns_diff_appendminimal(diff, &tuple);
+               }
+       }
+
+       /*
+        * Convert deletions into delayed deletions.
+        */
+       for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+            tuple != NULL; tuple = next) {
+               next = ISC_LIST_NEXT(tuple, link);
+               /*
+                * See if we already have a REMOVE request in progress.
+                */
+               dns_rdata_clone(&tuple->rdata, &rdata);
+               INSIST(rdata.length <= sizeof(buf));
+               memcpy(buf, rdata.data, rdata.length);
+               buf[1] |= DNS_NSEC3FLAG_REMOVE;
+               rdata.data = buf;
+
+               CHECK(rr_exists(db, ver, name, &rdata, &flag));
+
+               if (!flag) {
+                       CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+                                                  name, tuple->ttl, &rdata,
+                                                  &newtuple));
+                       CHECK(do_one_tuple(&newtuple, db, ver, diff));
+               }
+               CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
+                                          tuple->ttl, &tuple->rdata,
+                                          &newtuple));
+               CHECK(do_one_tuple(&newtuple, db, ver, diff));
+               ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+               dns_diff_appendminimal(diff, &tuple);
+               dns_rdata_reset(&rdata);
+       }
+
+       result = ISC_R_SUCCESS;
+ failure:
+       dns_diff_clear(&temp_diff);
+       return (result);
+}
+#endif
+
+/*
+ * Add records to cause the delayed signing of the zone by added DNSKEY
+ * to remove the RRSIG records generated by a deleted DNSKEY.
+ */
 static isc_result_t
 add_signing_records(dns_db_t *db, dns_name_t *name, dns_dbversion_t *ver,
                    dns_rdatatype_t privatetype, dns_diff_t *diff)
 {
-       isc_result_t result = ISC_R_SUCCESS;
        dns_difftuple_t *tuple, *newtuple = NULL;
        dns_rdata_dnskey_t dnskey;
        dns_rdata_t rdata = DNS_RDATA_INIT;
-       unsigned char buf[4];
+       isc_boolean_t flag;
        isc_region_t r;
+       isc_result_t result = ISC_R_SUCCESS;
        isc_uint16_t keyid;
+       unsigned char buf[5];
 
        for (tuple = ISC_LIST_HEAD(diff->tuples);
             tuple != NULL;
             tuple = ISC_LIST_NEXT(tuple, link)) {
-               if (tuple->rdata.type != dns_rdatatype_dnskey ||
-                   tuple->op != DNS_DIFFOP_ADD)
+               if (tuple->rdata.type != dns_rdatatype_dnskey)
                        continue;
 
                dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL);
@@ -2611,20 +3142,114 @@ add_signing_records(dns_db_t *db, dns_name_t *name, dns_dbversion_t *ver,
                buf[0] = dnskey.algorithm;
                buf[1] = (keyid & 0xff00) >> 8;
                buf[2] = (keyid & 0xff);
-               buf[3] = 0;
+               buf[3] = (tuple->op == DNS_DIFFOP_ADD) ? 0 : 1;
+               buf[4] = 0;
                rdata.data = buf;
                rdata.length = sizeof(buf);
                rdata.type = privatetype;
                rdata.rdclass = tuple->rdata.rdclass;
 
-               CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
-                                          0, &rdata, &newtuple));
+               CHECK(rr_exists(db, ver, name, &rdata, &flag));
+               if (flag)
+                       continue;
+               CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+                                          name, 0, &rdata, &newtuple));
                CHECK(do_one_tuple(&newtuple, db, ver, diff));
                INSIST(newtuple == NULL);
+               /*
+                * Remove any record which says this operation has already
+                * completed.
+                */
+               buf[4] = 1;
+               CHECK(rr_exists(db, ver, name, &rdata, &flag));
+               if (flag) {
+                       CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+                                                  name, 0, &rdata, &newtuple));
+                       CHECK(do_one_tuple(&newtuple, db, ver, diff));
+                       INSIST(newtuple == NULL);
+               }
+       }
+ failure:
+       return (result);
+}
+                       
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+/*
+ * Mark all NSEC3 chains for deletion without creating a NSEC chain as
+ * a side effect of deleting the last chain. 
+ */
+static isc_result_t
+delete_chains(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
+             dns_diff_t *diff)
+{
+       dns_dbnode_t *node = NULL;
+       dns_difftuple_t *tuple = NULL;
+       dns_name_t next;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdataset_t rdataset;
+       isc_boolean_t flag;
+       isc_result_t result = ISC_R_SUCCESS;
+       unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+       
+       dns_name_init(&next, NULL);
+       dns_rdataset_init(&rdataset);
+
+       result = dns_db_getoriginnode(db, &node);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       /*
+        * Cause all NSEC3 chains to be deleted.
+        */
+       result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+                                    0, (isc_stdtime_t) 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND)
+               goto success;
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdataset_current(&rdataset, &rdata);
+               INSIST(rdata.length <= sizeof(buf));
+               memcpy(buf, rdata.data, rdata.length);
+               
+               if (buf[1] == (DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC)) {
+                       dns_rdata_reset(&rdata);
+                       continue;
+               }
+
+               CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+                                          origin, 0, &rdata, &tuple));
+               CHECK(do_one_tuple(&tuple, db, ver, diff));
+               INSIST(tuple == NULL);
+
+               buf[1] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC;
+               rdata.data = buf;
+
+               CHECK(rr_exists(db, ver, origin, &rdata, &flag));
+
+               if (!flag) {
+                       CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+                                                  origin, 0, &rdata, &tuple));
+                       CHECK(do_one_tuple(&tuple, db, ver, diff));
+                       INSIST(tuple == NULL);
+               }
+               dns_rdata_reset(&rdata);
        }
+       if (result != ISC_R_NOMORE)
+               goto failure;
+ success:
+       result = ISC_R_SUCCESS;
+
  failure:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       dns_db_detachnode(db, &node);
        return (result);
 }
+#endif
 
 static void
 update_action(isc_task_t *task, isc_event_t *event) {
@@ -2651,6 +3276,12 @@ update_action(isc_task_t *task, isc_event_t *event) {
        isc_boolean_t deleted_zsk;
        dns_difftuple_t *tuple;
        dns_rdata_dnskey_t dnskey;
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+       unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+#endif
+#if !defined(ALLOW_SECURE_TO_INSECURE) || !defined(ALLOW_INSECURE_TO_SECURE)
+       isc_boolean_t had_dnskey;
+#endif
 
        INSIST(event->ev_type == DNS_EVENT_UPDATE);
 
@@ -2853,7 +3484,11 @@ update_action(isc_task_t *task, isc_event_t *event) {
                 * is forbidden from updating NSEC records."
                 */
                if (dns_db_issecure(db)) {
-                       if (rdata.type == dns_rdatatype_nsec) {
+                       if (rdata.type == dns_rdatatype_nsec3) {
+                               FAILC(DNS_R_REFUSED,
+                                     "explicit NSEC3 updates are not allowed "
+                                     "in secure zones");
+                       } else if (rdata.type == dns_rdatatype_nsec) {
                                FAILC(DNS_R_REFUSED,
                                      "explicit NSEC updates are not allowed "
                                      "in secure zones");
@@ -2934,12 +3569,17 @@ update_action(isc_task_t *task, isc_event_t *event) {
                                           typebuf);
                                continue;
                        }
-                       if (rdata.type == dns_rdatatype_ns &&
+                       if ((rdata.type == dns_rdatatype_ns ||
+                            rdata.type == dns_rdatatype_dname) &&
                            dns_name_iswildcard(name)) {
+                               char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+                               dns_rdatatype_format(rdata.type, typebuf,
+                                                    sizeof(typebuf));
                                update_log(client, zone,
                                           LOGLEVEL_PROTOCOL,
-                                          "attempt to add wildcard NS record"
-                                          "ignored");
+                                          "attempt to add wildcard %s record "
+                                          "ignored", typebuf);
                                continue;
                        }
                        if (rdata.type == dns_rdatatype_cname) {
@@ -2992,6 +3632,43 @@ update_action(isc_task_t *task, isc_event_t *event) {
                                }
                                soa_serial_changed = ISC_TRUE;
                        }
+
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+                       if (rdata.type == dns_rdatatype_nsec3param) {
+                               /*
+                                * Ignore attempts to add NSEC3PARAM records
+                                * with any flags other than OPTOUT.
+                                */
+                               if ((rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) != 0) {
+                                       update_log(client, zone,
+                                                  LOGLEVEL_PROTOCOL,
+                                                  "attempt to add NSEC3PARAM "
+                                                  "record with non OPTOUT "
+                                                  "flag");
+                                       continue;
+                               }
+
+                               /*
+                                * Set the NSEC3CHAIN creation flag.
+                                */
+                               INSIST(rdata.length <= sizeof(buf));
+                               memcpy(buf, rdata.data, rdata.length);
+                               buf[1] |= DNS_NSEC3FLAG_UPDATE;
+                               rdata.data = buf;
+                               /*
+                                * Force the TTL to zero for NSEC3PARAM records.
+                                */
+                               ttl = 0;
+                       }
+#else
+                       if (rdata.type == dns_rdatatype_nsec3param) {
+                               update_log(client, zone, LOGLEVEL_PROTOCOL, 
+                                          "attempt to add NSEC3PARAM "
+                                          "record ignored");
+                               continue;
+                       };
+#endif
+
                        if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 &&
                            dns_name_internalwildcard(name)) {
                                char namestr[DNS_NAME_FORMATSIZE];
@@ -3034,8 +3711,10 @@ update_action(isc_task_t *task, isc_event_t *event) {
                                        dns_diff_clear(&ctx.del_diff);
                                        dns_diff_clear(&ctx.add_diff);
                                } else {
-                                       CHECK(do_diff(&ctx.del_diff, db, ver, &diff));
-                                       CHECK(do_diff(&ctx.add_diff, db, ver, &diff));
+                                       CHECK(do_diff(&ctx.del_diff, db, ver,
+                                                     &diff));
+                                       CHECK(do_diff(&ctx.add_diff, db, ver,
+                                                     &diff));
                                        CHECK(update_one_rr(db, ver, &diff,
                                                            DNS_DIFFOP_ADD,
                                                            name, ttl, &rdata));
@@ -3065,6 +3744,13 @@ update_action(isc_task_t *task, isc_event_t *event) {
                                                        dns_rdatatype_any, 0,
                                                        &rdata, &diff));
                                }
+#ifndef ALLOW_NSEC3PARAM_UPDATE
+                       } else if (rdata.type == dns_rdatatype_nsec3param) {
+                               update_log(client, zone, LOGLEVEL_PROTOCOL,
+                                          "attempt to delete a NSEC3PARAM "
+                                          "records ignored");
+                               continue;
+#endif
                        } else if (dns_name_equal(name, zonename) &&
                                   (rdata.type == dns_rdatatype_soa ||
                                    rdata.type == dns_rdatatype_ns)) {
@@ -3130,6 +3816,14 @@ update_action(isc_task_t *task, isc_event_t *event) {
        if (result != ISC_R_NOMORE)
                FAIL(result);
 
+       /*
+        * Check that any changes to DNSKEY/NSEC3PARAM records make sense.
+        * If they don't then back out all changes to DNSKEY/NSEC3PARAM
+        * records.
+        */
+       if (! ISC_LIST_EMPTY(diff.tuples))
+               CHECK(check_dnssec(client, zone, db, ver, &diff));
+
        /*
         * If any changes were made, increment the SOA serial number,
         * update RRSIGs and NSECs (if zone is secure), and write the update
@@ -3152,14 +3846,52 @@ update_action(isc_task_t *task, isc_event_t *event) {
 
                CHECK(remove_orphaned_ds(db, ver, &diff));
 
+               CHECK(rrset_exists(db, ver, zonename, dns_rdatatype_dnskey,
+                                  0, &has_dnskey));
+
+#if !defined(ALLOW_SECURE_TO_INSECURE) || !defined(ALLOW_INSECURE_TO_SECURE)
+               CHECK(rrset_exists(db, oldver, zonename, dns_rdatatype_dnskey,
+                                  0, &had_dnskey));
+
+#ifndef ALLOW_SECURE_TO_INSECURE
+               if (had_dnskey && !has_dnskey) {
+                       update_log(client, zone, LOGLEVEL_PROTOCOL,
+                                  "update rejected: all DNSKEY records "
+                                  "removed");
+                       result = DNS_R_REFUSED;
+                       goto failure;
+               }
+#endif
+#ifndef ALLOW_INSECURE_TO_SECURE
+               if (had_dnskey && !has_dnskey) {
+                       update_log(client, zone, LOGLEVEL_PROTOCOL,
+                                  "update rejected: DNSKEY record added");
+                       result = DNS_R_REFUSED;
+                       goto failure;
+               }
+#endif
+#endif
+
                CHECK(add_signing_records(db, zonename, ver,
                                          dns_zone_getprivatetype(zone),
                                          &diff));
 
-               CHECK(rrset_exists(db, ver, zonename, dns_rdatatype_dnskey,
-                                  0, &has_dnskey));
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+               CHECK(add_nsec3param_records(client, zone, db, zonename,
+                                            ver, &diff));
+#endif
 
-               if (has_dnskey && dns_db_isdnssec(db)) {
+               if (!has_dnskey) {
+                       /*
+                        * We are transitioning from secure to insecure.
+                        * Cause all NSEC3 chains to be deleted.  When the
+                        * the last signature for the DNSKEY records are
+                        * remove any NSEC chain present will also be removed.
+                        */
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+                        CHECK(delete_chains(db, ver, zonename, &diff));
+#endif
+               } else if (has_dnskey && dns_db_isdnssec(db)) {
                        isc_uint32_t interval;
                        interval = dns_zone_getsigvalidityinterval(zone);
                        result = update_signatures(client, zone, db, oldver,
@@ -3168,7 +3900,7 @@ update_action(isc_task_t *task, isc_event_t *event) {
                        if (result != ISC_R_SUCCESS) {
                                update_log(client, zone,
                                           ISC_LOG_ERROR,
-                                          "RRSIG/NSEC update failed: %s",
+                                          "RRSIG/NSEC/NSEC3 update failed: %s",
                                           isc_result_totext(result));
                                goto failure;
                        }
@@ -3214,6 +3946,13 @@ update_action(isc_task_t *task, isc_event_t *event) {
                 */
                dns_zone_notify(zone);
 
+               /*
+                * Cause the zone to be signed with the key that we
+                * have just added or have the corresponding signatures
+                * deleted.
+                *
+                * Note: we are already committed to this course of action.
+                */
                for (tuple = ISC_LIST_HEAD(diff.tuples);
                     tuple != NULL;
                     tuple = ISC_LIST_NEXT(tuple, link)) {
@@ -3221,8 +3960,7 @@ update_action(isc_task_t *task, isc_event_t *event) {
                        dns_secalg_t algorithm;
                        isc_uint16_t keyid;
 
-                       if (tuple->rdata.type != dns_rdatatype_dnskey ||
-                           tuple->op != DNS_DIFFOP_ADD)
+                       if (tuple->rdata.type != dns_rdatatype_dnskey)
                                continue;
 
                        dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL);
@@ -3235,13 +3973,43 @@ update_action(isc_task_t *task, isc_event_t *event) {
                        algorithm = dnskey.algorithm;
                        keyid = dst_region_computeid(&r, algorithm);
 
-                       result = dns_zone_signwithkey(zone, algorithm, keyid);
+                       result = dns_zone_signwithkey(zone, algorithm, keyid,
+                                       ISC_TF(tuple->op == DNS_DIFFOP_DEL));
                        if (result != ISC_R_SUCCESS) {
                                update_log(client, zone, ISC_LOG_ERROR,
                                           "dns_zone_signwithkey failed: %s",
                                           dns_result_totext(result));
                        }
                }
+
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+               /*
+                * Cause the zone to add/delete NSEC3 chains for the
+                * defered NSEC3PARAM changes. 
+                *
+                * Note: we are already committed to this course of action.
+                */
+               for (tuple = ISC_LIST_HEAD(diff.tuples);
+                    tuple != NULL;
+                    tuple = ISC_LIST_NEXT(tuple, link)) {
+                       dns_rdata_nsec3param_t nsec3param;
+
+                       if (tuple->rdata.type != dns_rdatatype_nsec3param ||
+                           tuple->op != DNS_DIFFOP_ADD)
+                               continue;
+
+                       dns_rdata_tostruct(&tuple->rdata, &nsec3param, NULL);
+                       if (nsec3param.flags == 0)
+                               continue;
+
+                       result = dns_zone_addnsec3chain(zone, &nsec3param);
+                       if (result != ISC_R_SUCCESS) {
+                               update_log(client, zone, ISC_LOG_ERROR,
+                                          "dns_zone_addnsec3chain failed: %s",
+                                          dns_result_totext(result));
+                       }
+               }
+#endif
        } else {
                update_log(client, zone, LOGLEVEL_DEBUG, "redundant request");
                dns_db_closeversion(db, &ver, ISC_TRUE);
index befd09ef8a32bf6fba3eac0315bfa41e64f50d89..cfa6baef3fb4737e09069de978aedcdea28d366a 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: xfrout.c,v 1.129 2008/08/15 19:36:49 jinmei Exp $ */
+/* $Id: xfrout.c,v 1.130 2008/09/24 02:46:21 marka Exp $ */
 
 #include <config.h>
 
@@ -169,7 +169,7 @@ db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
        it->ver = ver;
        it->now = now;
        it->node = NULL;
-       result = dns_db_createiterator(it->db, ISC_FALSE, &it->dbit);
+       result = dns_db_createiterator(it->db, 0, &it->dbit);
        if (result != ISC_R_SUCCESS)
                return (result);
        it->rdatasetit = NULL;
index d78cf3bfb5d410d1c87437c7094de6c66dbe3336..7a420b52b832fef6fd6310854c0ce0a2545420b8 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zoneconf.c,v 1.146 2008/05/21 23:47:00 tbox Exp $ */
+/* $Id: zoneconf.c,v 1.147 2008/09/24 02:46:21 marka Exp $ */
 
 /*% */
 
@@ -716,6 +716,12 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
                result = ns_config_get(maps, "zero-no-soa-ttl", &obj);
                INSIST(result == ISC_R_SUCCESS);
                dns_zone_setzeronosoattl(zone, cfg_obj_asboolean(obj));
+
+               obj = NULL;
+               result = ns_config_get(maps, "nsec3-test-zone", &obj);
+               INSIST(result == ISC_R_SUCCESS);
+               dns_zone_setoption(zone, DNS_ZONEOPT_NSEC3TESTZONE,
+                                  cfg_obj_asboolean(obj));
        }
 
        /*
index b7b4dec5e21cc1cbd89aa5b9e1b7fd936192a6ea..00ff9a0520247c8a11c0aa6b88f51b85be5f4bcc 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: nsupdate.c,v 1.159 2008/04/02 02:37:41 marka Exp $ */
+/* $Id: nsupdate.c,v 1.160 2008/09/24 02:46:21 marka Exp $ */
 
 /*! \file */
 
@@ -162,6 +162,8 @@ static unsigned int udp_retries = 3;
 static dns_rdataclass_t defaultclass = dns_rdataclass_in;
 static dns_rdataclass_t zoneclass = dns_rdataclass_none;
 static dns_message_t *answer = NULL;
+static isc_uint32_t default_ttl = 0;
+static isc_boolean_t default_ttl_set = ISC_FALSE;
 
 typedef struct nsu_requestinfo {
        dns_message_t *msg;
@@ -1386,6 +1388,33 @@ evaluate_zone(char *cmdline) {
        return (STATUS_MORE);
 }
 
+static isc_uint16_t
+evaluate_ttl(char *cmdline) {
+       char *word;
+       isc_result_t result;
+       isc_uint32_t ttl;
+
+       word = nsu_strsep(&cmdline, " \t\r\n");
+       if (*word == 0) {
+               fprintf(stderr, "could not ttl\n");
+               return (STATUS_SYNTAX);
+       }
+
+       result = isc_parse_uint32(&ttl, word, 10);
+       if (result != ISC_R_SUCCESS)
+               return (STATUS_SYNTAX);
+
+       if (ttl > TTL_MAX) {
+               fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
+                       word, TTL_MAX);
+               return (STATUS_SYNTAX);
+       }
+       default_ttl = ttl;
+       default_ttl_set = ISC_TRUE;
+       
+       return (STATUS_MORE);
+}
+
 static isc_uint16_t
 evaluate_class(char *cmdline) {
        char *word;
@@ -1470,6 +1499,9 @@ update_addordelete(char *cmdline, isc_boolean_t isdelete) {
                if (isdelete) {
                        ttl = 0;
                        goto parseclass;
+               } else if (default_ttl_set) {
+                       ttl = default_ttl;
+                       goto parseclass;
                } else {
                        fprintf(stderr, "ttl '%s': %s\n", word,
                                isc_result_totext(result));
@@ -1718,6 +1750,15 @@ get_next_command(void) {
                return (evaluate_class(cmdline));
        if (strcasecmp(word, "send") == 0)
                return (STATUS_SEND);
+       if (strcasecmp(word, "debug") == 0) {
+               if (debugging)
+                       ddebugging = ISC_TRUE;
+               else
+                       debugging = ISC_TRUE;
+               return (STATUS_MORE);
+       }
+       if (strcasecmp(word, "ttl") == 0)
+               return (evaluate_ttl(cmdline));
        if (strcasecmp(word, "show") == 0) {
                show_message(stdout, updatemsg, "Outgoing update query:");
                return (STATUS_MORE);
index 7b6814abccd74f25a52415d06ee08a682c6c0096..a5c8a52eced5b4d4923a4c47dc850e4538e76e5e 100644 (file)
@@ -18,7 +18,7 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id: nsupdate.docbook,v 1.32 2008/08/29 03:16:14 marka Exp $ -->
+<!-- $Id: nsupdate.docbook,v 1.33 2008/09/24 02:46:21 marka Exp $ -->
 <refentry id="man.nsupdate">
   <refentryinfo>
     <date>Jun 30, 2000</date>
@@ -55,6 +55,7 @@
     <cmdsynopsis>
       <command>nsupdate</command>
       <arg><option>-d</option></arg>
+      <arg><option>-D</option></arg>
       <group>
         <arg><option>-y <replaceable class="parameter"><optional>hmac:</optional>keyname:secret</replaceable></option></arg>
         <arg><option>-k <replaceable class="parameter">keyfile</replaceable></option></arg>
       This provides tracing information about the update requests that are
       made and the replies received from the name server.
     </para>
+    <para>
+      The <option>-D</option> option makes <command>nsupdate</command>
+      report additional debugging information to <option>-d</option>.
+    </para>
     <para>
       Transaction signatures can be used to authenticate the Dynamic DNS
       updates.
index 3af011df329657fd573520ed2150badcd81a026b..889919a93b119631775b8f854f5965c43ecec569 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.129 2007/06/19 23:46:59 tbox Exp $
+# $Id: Makefile.in,v 1.130 2008/09/24 02:46:21 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -62,6 +62,7 @@ XTARGETS =    adb_test@EXEEXT@ \
                gxba_test@EXEEXT@ \
                gxbn_test@EXEEXT@ \
                hash_test@EXEEXT@ \
+               nsec3hash@EXEEXT@ \
                fsaccess_test@EXEEXT@ \
                inter_test@EXEEXT@ \
                journalprint@EXEEXT@ \
@@ -285,6 +286,10 @@ cfg_test@EXEEXT@: cfg_test.@O@ ${ISCCFGDEPLIBS} ${ISCDEPLIBS}
        ${LIBTOOL_MODE_LINK} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ cfg_test.@O@ \
                ${ISCCFGLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
 
+nsec3hash@EXEEXT@: nsec3hash.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+       ${LIBTOOL_MODE_LINK} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ nsec3hash.@O@ \
+       ${DNSLIBS} ${ISCLIBS} ${LIBS}
+
 distclean::
        rm -f headerdep_test.sh
 
index bcbf3f0c726f16d81c2fabb517d6a11bfda15bad..8879ac980e42e1296d90aa4e339b34954b4d1b83 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: db_test.c,v 1.64 2007/06/19 23:46:59 tbox Exp $ */
+/* $Id: db_test.c,v 1.65 2008/09/24 02:46:21 marka Exp $ */
 
 /*! \file 
  * \author
@@ -177,8 +177,7 @@ list(dbinfo *dbi, char *seektext) {
                                dns_db_currentversion(dbi->db, &dbi->iversion);
                }
 
-               result = dns_db_createiterator(dbi->db, ISC_FALSE,
-                                              &dbi->dbiterator);
+               result = dns_db_createiterator(dbi->db, 0, &dbi->dbiterator);
                if (result == ISC_R_SUCCESS) {
                        if (seektext != NULL) {
                                len = strlen(seektext);
diff --git a/bin/tests/nsec3hash.c b/bin/tests/nsec3hash.c
new file mode 100644 (file)
index 0000000..4c6f892
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2006  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: nsec3hash.c,v 1.2 2008/09/24 02:46:21 marka Exp $ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <isc/base32.h>
+#include <isc/buffer.h>
+#include <isc/hex.h>
+#include <isc/iterated_hash.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/types.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/types.h>
+
+const char *program = "nsec3hash";
+
+static void
+fatal(const char *format, ...) {
+        va_list args;
+
+        fprintf(stderr, "%s: ", program);
+        va_start(args, format);
+        vfprintf(stderr, format, args);
+        va_end(args);
+        fprintf(stderr, "\n");
+        exit(1);
+}
+
+static void
+check_result(isc_result_t result, const char *message) {
+        if (result != ISC_R_SUCCESS)
+                fatal("%s: %s", message, isc_result_totext(result));
+}
+
+static void
+usage() {
+       fatal("salt hash iterations domain");
+}
+
+int 
+main(int argc, char **argv) {
+       dns_fixedname_t fixed;
+       dns_name_t *name;
+       isc_buffer_t buffer;
+       isc_region_t region;
+       isc_result_t result;
+       unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+       unsigned char salt[255];
+       unsigned char text[1024];
+       unsigned int hash_alg;
+       unsigned int length;
+       unsigned int iterations;
+       unsigned int salt_length;
+
+       if (argc != 5)
+               usage();
+
+       if (strcmp(argv[1], "-") == 0) {
+               salt_length = 0;
+               salt[0] = 0;
+       } else {
+               isc_buffer_init(&buffer, salt, sizeof(salt));
+               result = isc_hex_decodestring(argv[1], &buffer);
+               check_result(result, "isc_hex_decodestring(salt)");
+               salt_length = isc_buffer_usedlength(&buffer);
+               if (salt_length > 255U)
+                       fatal("salt too long");
+       }
+       hash_alg = atoi(argv[2]);
+       if (hash_alg > 255U)
+               fatal("hash algorithm too large");
+       iterations = atoi(argv[3]);
+       if (iterations > 0xffffU)
+               fatal("iterations to large");
+
+       dns_fixedname_init(&fixed);
+       name = dns_fixedname_name(&fixed);
+       isc_buffer_init(&buffer, argv[4], strlen(argv[4]));
+       isc_buffer_add(&buffer, strlen(argv[4]));
+       result = dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL);
+       check_result(result, "dns_name_fromtext() failed");
+
+       dns_name_downcase(name, name, NULL);
+       length = isc_iterated_hash(hash, hash_alg, iterations,  salt,
+                                  salt_length, name->ndata, name->length);
+       if (length == 0)
+               fatal("isc_iterated_hash failed");
+       region.base = hash;
+       region.length = length;
+       isc_buffer_init(&buffer, text, sizeof(text));
+       isc_base32hex_totext(&region, 1, "", &buffer);
+       fprintf(stdout, "%.*s (salt=%s, hash=%u, iterations=%u)\n",
+               (int)isc_buffer_usedlength(&buffer), text, argv[1], hash_alg, iterations);
+       exit(0);
+}
index abc447e8d3e3be036598b5076aada48d56b215d8..afc3a7ef8fa0f7b4a2d78774830979c20a502e38 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: nsecify.c,v 1.6 2007/06/19 23:46:59 tbox Exp $ */
+/* $Id: nsecify.c,v 1.7 2008/09/24 02:46:21 marka Exp $ */
 
 #include <config.h>
 
@@ -154,7 +154,7 @@ nsecify(char *filename) {
        result = dns_db_newversion(db, &wversion);
        check_result(result, "dns_db_newversion()");
        dbiter = NULL;
-       result = dns_db_createiterator(db, ISC_FALSE, &dbiter);
+       result = dns_db_createiterator(db, 0, &dbiter);
        check_result(result, "dns_db_createiterator()");
        result = dns_dbiterator_first(dbiter);
        node = NULL;
index d6c1dea936e4d1b05c7cdf6bc0c71f38002ac79f..306990a44aa799a94c174200954a50cb18651f5c 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.29 2007/06/19 23:47:00 tbox Exp $
+# $Id: Makefile.in,v 1.30 2008/09/24 02:46:21 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -34,7 +34,7 @@ check: test
 test: subdirs
        if test -f ./runall.sh; then sh ./runall.sh; fi
 
-clean distclean::
+testclean clean distclean::
        if test -f ./cleanall.sh; then sh ./cleanall.sh; fi
 
 distclean::
index 2df940d320aff2edb81cf723ea6f7f7d55f12e61..333a72258b50210ada54880d04f14222679b0a8d 100644 (file)
@@ -15,9 +15,9 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: clean.sh,v 1.21 2007/10/30 23:56:09 marka Exp $
+# $Id: clean.sh,v 1.22 2008/09/24 02:46:21 marka Exp $
 
-rm -f */K* */keyset-* */dsset-* */dlvset-* */signedkey-* */*.signed */trusted.conf */tmp*
+rm -f */K* */keyset-* */dsset-* */dlvset-* */signedkey-* */*.signed */trusted.conf */tmp* */*.jnl */*.bk
 rm -f ns1/root.db ns2/example.db ns3/secure.example.db
 rm -f ns3/unsecure.example.db ns3/bogus.example.db ns3/keyless.example.db
 rm -f ns3/dynamic.example.db ns3/dynamic.example.db.signed.jnl
@@ -26,4 +26,13 @@ rm -f */example.bk
 rm -f dig.out.*
 rm -f random.data
 rm -f ns2/dlv.db
+rm -f ns3/multiple.example.db ns3/nsec3-unknown.example.db ns3/nsec3.example.db
+rm -f ns3/optout-unknown.example.db ns3/optout.example.db
+rm -f ns7/multiple.example.bk ns7/nsec3.example.bk ns7/optout.example.bk
 rm -f */named.memstats
+rm -f ns3/nsec3.nsec3.example.db
+rm -f ns3/nsec3.optout.example.db
+rm -f ns3/optout.nsec3.example.db
+rm -f ns3/optout.optout.example.db
+rm -f ns3/secure.nsec3.example.db
+rm -f ns3/secure.optout.example.db
index fe8d1c36be2ed4804862fac937ea01a440e64230..bb5977d8861b0cec8ef2498b709bf4f33c19cd2f 100644 (file)
@@ -15,7 +15,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: sign.sh,v 1.23 2007/06/19 23:47:02 tbox Exp $
+# $Id: sign.sh,v 1.24 2008/09/24 02:46:21 marka Exp $
 
 SYSTEMTESTTOP=../..
 . $SYSTEMTESTTOP/conf.sh
@@ -53,3 +53,4 @@ cp trusted.conf ../ns2/trusted.conf
 cp trusted.conf ../ns3/trusted.conf
 cp trusted.conf ../ns4/trusted.conf
 cp trusted.conf ../ns6/trusted.conf
+cp trusted.conf ../ns7/trusted.conf
diff --git a/bin/tests/system/dnssec/ns2/child.nsec3.example.db b/bin/tests/system/dnssec/ns2/child.nsec3.example.db
new file mode 100644 (file)
index 0000000..e165ccb
--- /dev/null
@@ -0,0 +1,25 @@
+; Copyright (C) 2006  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: child.nsec3.example.db,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2006081400 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+@                      IN NS   ns2.example.
diff --git a/bin/tests/system/dnssec/ns2/child.optout.example.db b/bin/tests/system/dnssec/ns2/child.optout.example.db
new file mode 100644 (file)
index 0000000..adc5164
--- /dev/null
@@ -0,0 +1,25 @@
+; Copyright (C) 2006  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: child.optout.example.db,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2006081400 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+@                      IN NS   ns2.example.
index 531b3632dcf59a23d05ce9b311cb9ae9461ae973..b8a8bc07e3e68fa69d246435a506bbf6684994bf 100644 (file)
@@ -13,7 +13,7 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: example.db.in,v 1.17 2007/06/19 23:47:02 tbox Exp $
+; $Id: example.db.in,v 1.18 2008/09/24 02:46:21 marka Exp $
 
 $TTL 300       ; 5 minutes
 @                      IN SOA  mname1. . (
@@ -79,4 +79,19 @@ z                    A       10.0.0.26
 keyless                        NS      ns.keyless
 ns.keyless             A       10.53.0.3
 
+nsec3                  NS      ns.nsec3
+ns.nsec3               A       10.53.0.3
+
+optout                 NS      ns.optout
+ns.optout              A       10.53.0.3
+
+nsec3-unknown          NS      ns.nsec3-unknown
+ns.nsec3-unknown       A       10.53.0.3
+
+optout-unknown         NS      ns.optout-unknown
+ns.optout-unknown      A       10.53.0.3
+
+multiple               NS      ns.multiple
+ns.multiple            A       10.53.0.3
+
 *.wild                 A       10.0.0.27
index 4a884507607902d2661536c2913d1a2240cacbf3..bc7f51c8b9f54bca45997715de2d6e92847b5797 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: named.conf,v 1.28 2007/06/19 23:47:02 tbox Exp $ */
+/* $Id: named.conf,v 1.29 2008/09/24 02:46:21 marka Exp $ */
 
 // NS2
 
@@ -68,5 +68,16 @@ zone "rfc2335.example" {
         file "rfc2335.example.db";
 };
 
+zone "child.nsec3.example" {
+       type master;
+       file "child.nsec3.example.db";
+       allow-update { none; };
+};
+
+zone "child.optout.example" {
+       type master;
+       file "child.optout.example.db";
+       allow-update { none; };
+};
 
 include "trusted.conf";
index 61b0119f7ea3da560c28eca48307bedd1fb8247a..72e8f106fbfec6281cc785fcbd55090fb3fb2ef5 100644 (file)
@@ -15,7 +15,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: sign.sh,v 1.28 2007/06/19 23:47:02 tbox Exp $
+# $Id: sign.sh,v 1.29 2008/09/24 02:46:21 marka Exp $
 
 SYSTEMTESTTOP=../..
 . $SYSTEMTESTTOP/conf.sh
@@ -30,7 +30,7 @@ zonefile=example.db
 
 ( cd ../ns3 && sh sign.sh )
 
-for subdomain in secure bogus dynamic keyless
+for subdomain in secure bogus dynamic keyless nsec3 optout nsec3-unknown optout-unknown multiple
 do
        cp ../ns3/keyset-$subdomain.example. .
 done
diff --git a/bin/tests/system/dnssec/ns3/insecure.nsec3.example.db b/bin/tests/system/dnssec/ns3/insecure.nsec3.example.db
new file mode 100644 (file)
index 0000000..4518c2d
--- /dev/null
@@ -0,0 +1,31 @@
+; Copyright (C) 2008  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: insecure.nsec3.example.db,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
diff --git a/bin/tests/system/dnssec/ns3/insecure.optout.example.db b/bin/tests/system/dnssec/ns3/insecure.optout.example.db
new file mode 100644 (file)
index 0000000..0a3a45d
--- /dev/null
@@ -0,0 +1,31 @@
+; Copyright (C) 2008  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: insecure.optout.example.db,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
diff --git a/bin/tests/system/dnssec/ns3/multiple.example.db.in b/bin/tests/system/dnssec/ns3/multiple.example.db.in
new file mode 100644 (file)
index 0000000..5416ddb
--- /dev/null
@@ -0,0 +1,34 @@
+; Copyright (C) 2006  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: multiple.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a                        A       10.0.0.3
+*.e                    A       10.0.0.6
+child                  NS      ns2.example.
index 4d78e745e7d723a62235b38a08f1f0a2a06df4eb..4e340c16d01d9782fab91d47834936475485951b 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: named.conf,v 1.31 2007/06/19 23:47:02 tbox Exp $ */
+/* $Id: named.conf,v 1.32 2008/09/24 02:46:21 marka Exp $ */
 
 // NS3
 
@@ -70,11 +70,81 @@ zone "insecure.example" {
        allow-update { any; };
 };
 
+zone "insecure.nsec3.example" {
+       type master;
+       file "insecure.nsec3.example.db";
+       allow-update { any; };
+};
+
+zone "insecure.optout.example" {
+       type master;
+       file "insecure.optout.example.db";
+       allow-update { any; };
+};
+
 zone "keyless.example" {
        type master;
        file "keyless.example.db.signed";
 };
 
+zone "nsec3.example" {
+       type master;
+       file "nsec3.example.db.signed";
+};
+
+zone "optout.nsec3.example" {
+       type master;
+       file "optout.nsec3.example.db.signed";
+};
+
+zone "nsec3.nsec3.example" {
+       type master;
+       file "nsec3.nsec3.example.db.signed";
+};
+
+zone "secure.nsec3.example" {
+       type master;
+       file "secure.nsec3.example.db.signed";
+};
+
+zone "optout.example" {
+       type master;
+       file "optout.example.db.signed";
+};
+
+zone "secure.optout.example" {
+       type master;
+       file "secure.optout.example.db.signed";
+};
+
+zone "nsec3.optout.example" {
+       type master;
+       file "nsec3.optout.example.db.signed";
+};
+
+zone "optout.optout.example" {
+       type master;
+       file "optout.optout.example.db.signed";
+};
+
+zone "nsec3-unknown.example" {
+       type master;
+       nsec3-test-zone yes;
+       file "nsec3-unknown.example.db.signed";
+};
+
+zone "optout-unknown.example" {
+       type master;
+       nsec3-test-zone yes;
+       file "optout-unknown.example.db.signed";
+};
+
+zone "multiple.example" {
+       type master;
+       file "multiple.example.db.signed";
+       allow-update { any; };
+};
+
 zone "mustbesecure.example" {
        type master;
        file "mustbesecure.example.db";
diff --git a/bin/tests/system/dnssec/ns3/nsec3-unknown.example.db.in b/bin/tests/system/dnssec/ns3/nsec3-unknown.example.db.in
new file mode 100644 (file)
index 0000000..c0a3bf8
--- /dev/null
@@ -0,0 +1,34 @@
+; Copyright (C) 2006  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: nsec3-unknown.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a                        A       10.0.0.3
+*.e                    A       10.0.0.6
+child                  NS      ns2.example.
diff --git a/bin/tests/system/dnssec/ns3/nsec3.example.db.in b/bin/tests/system/dnssec/ns3/nsec3.example.db.in
new file mode 100644 (file)
index 0000000..660a1b6
--- /dev/null
@@ -0,0 +1,43 @@
+; Copyright (C) 2006  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: nsec3.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a                        A       10.0.0.3
+*.wild                 A       10.0.0.6
+child                  NS      ns2.example.
+insecure               NS      ns.insecure
+ns.insecure            A       10.53.0.3
+secure                 NS      ns.secure
+ns.secure              A       10.53.0.3
+nsec3                  NS      ns.nsec3
+ns.nsec3               A       10.53.0.3
+optout                 NS      ns.optout
+ns.optout              A       10.53.0.3
+02HC3EM7BDD011A0GMS3HKKJT2IF5VP8 A 10.0.0.17
diff --git a/bin/tests/system/dnssec/ns3/nsec3.nsec3.example.db.in b/bin/tests/system/dnssec/ns3/nsec3.nsec3.example.db.in
new file mode 100644 (file)
index 0000000..461d7d7
--- /dev/null
@@ -0,0 +1,41 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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: nsec3.nsec3.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a.a.a.a.a.a.a.e  A       10.0.0.27
+x                      CNAME   a
+
+private                        NS      ns.private
+ns.private             A       10.53.0.2
+
+insecure               NS      ns.insecure
+ns.insecure            A       10.53.0.2
+
diff --git a/bin/tests/system/dnssec/ns3/nsec3.optout.example.db.in b/bin/tests/system/dnssec/ns3/nsec3.optout.example.db.in
new file mode 100644 (file)
index 0000000..10d7b93
--- /dev/null
@@ -0,0 +1,41 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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: nsec3.optout.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a.a.a.a.a.a.a.e  A       10.0.0.27
+x                      CNAME   a
+
+private                        NS      ns.private
+ns.private             A       10.53.0.2
+
+insecure               NS      ns.insecure
+ns.insecure            A       10.53.0.2
+
diff --git a/bin/tests/system/dnssec/ns3/optout-unknown.example.db.in b/bin/tests/system/dnssec/ns3/optout-unknown.example.db.in
new file mode 100644 (file)
index 0000000..f6d5cef
--- /dev/null
@@ -0,0 +1,34 @@
+; Copyright (C) 2006  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: optout-unknown.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a                        A       10.0.0.3
+*.e                    A       10.0.0.6
+child                  NS      ns2.example.
diff --git a/bin/tests/system/dnssec/ns3/optout.example.db.in b/bin/tests/system/dnssec/ns3/optout.example.db.in
new file mode 100644 (file)
index 0000000..f661307
--- /dev/null
@@ -0,0 +1,45 @@
+; Copyright (C) 2006  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: optout.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20       ; refresh (20 seconds)
+                               20       ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a                        A       10.0.0.3
+*.wild                 A       10.0.0.6
+insecure               NS      ns.insecure
+ns.insecure            A       10.53.0.3
+secure                 NS      ns.secure
+ns.secure              A       10.53.0.3
+nsec3                  NS      ns.nsec3
+ns.nsec3               A       10.53.0.3
+optout                 NS      ns.optout
+ns.optout              A       10.53.0.3
+child                  NS      ns2.example.
+insecure.empty         NS      ns.insecure.empty
+ns.insecure.empty      A       10.53.0.3
+foo.*.empty-wild       NS      ns
diff --git a/bin/tests/system/dnssec/ns3/optout.nsec3.example.db.in b/bin/tests/system/dnssec/ns3/optout.nsec3.example.db.in
new file mode 100644 (file)
index 0000000..560ead2
--- /dev/null
@@ -0,0 +1,41 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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: optout.nsec3.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a.a.a.a.a.a.a.e  A       10.0.0.27
+x                      CNAME   a
+
+private                        NS      ns.private
+ns.private             A       10.53.0.2
+
+insecure               NS      ns.insecure
+ns.insecure            A       10.53.0.2
+
diff --git a/bin/tests/system/dnssec/ns3/optout.optout.example.db.in b/bin/tests/system/dnssec/ns3/optout.optout.example.db.in
new file mode 100644 (file)
index 0000000..e27019a
--- /dev/null
@@ -0,0 +1,41 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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: optout.optout.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a.a.a.a.a.a.a.e  A       10.0.0.27
+x                      CNAME   a
+
+private                        NS      ns.private
+ns.private             A       10.53.0.2
+
+insecure               NS      ns.insecure
+ns.insecure            A       10.53.0.2
+
index fe0559bafefb986a30b0449a43588dd02aeb552f..26e9f0ea69db5dfeec2ea205f3cdbf71e678f3e8 100644 (file)
@@ -13,7 +13,7 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: secure.example.db.in,v 1.11 2007/06/19 23:47:02 tbox Exp $
+; $Id: secure.example.db.in,v 1.12 2008/09/24 02:46:21 marka Exp $
 
 $TTL 300       ; 5 minutes
 @                      IN SOA  mname1. . (
@@ -30,6 +30,7 @@ a                     A       10.0.0.1
 b                      A       10.0.0.2
 d                      A       10.0.0.4
 z                      A       10.0.0.26
+a.a.a.a.a.a.a.a.a.a.e  A       10.0.0.27
 x                      CNAME   a
 
 private                        NS      ns.private
diff --git a/bin/tests/system/dnssec/ns3/secure.nsec3.example.db.in b/bin/tests/system/dnssec/ns3/secure.nsec3.example.db.in
new file mode 100644 (file)
index 0000000..4241bef
--- /dev/null
@@ -0,0 +1,41 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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: secure.nsec3.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a.a.a.a.a.a.a.e  A       10.0.0.27
+x                      CNAME   a
+
+private                        NS      ns.private
+ns.private             A       10.53.0.2
+
+insecure               NS      ns.insecure
+ns.insecure            A       10.53.0.2
+
diff --git a/bin/tests/system/dnssec/ns3/secure.optout.example.db.in b/bin/tests/system/dnssec/ns3/secure.optout.example.db.in
new file mode 100644 (file)
index 0000000..29b29e7
--- /dev/null
@@ -0,0 +1,41 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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: secure.optout.example.db.in,v 1.2 2008/09/24 02:46:21 marka Exp $
+
+$TTL 300       ; 5 minutes
+@                      IN SOA  mname1. . (
+                               2000042407 ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+                       NS      ns
+ns                     A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+d                      A       10.0.0.4
+z                      A       10.0.0.26
+a.a.a.a.a.a.a.a.a.a.e  A       10.0.0.27
+x                      CNAME   a
+
+private                        NS      ns.private
+ns.private             A       10.53.0.2
+
+insecure               NS      ns.insecure
+ns.insecure            A       10.53.0.2
+
index 69651ea067a16e32adfae0e44e01ca5eead4ffe2..6e1167936d1372a84cbf8abc4b238086060664bf 100644 (file)
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: sign.sh,v 1.23 2007/06/19 23:47:02 tbox Exp $
+# $Id: sign.sh,v 1.24 2008/09/24 02:46:21 marka Exp $
+
+SYSTEMTESTTOP=../..
+. $SYSTEMTESTTOP/conf.sh
 
 RANDFILE=../random.data
 
@@ -66,3 +69,156 @@ mv $zonefile.signed $zonefile.tmp
 <$zonefile.tmp perl -p -e 's/ keyless.example/ b.keyless.example/
     if /^a.b.keyless.example/../NXT/;' >$zonefile.signed
 rm -f $zonefile.tmp
+
+#
+#  NSEC3/NSEC test zone
+#
+zone=secure.nsec3.example.
+infile=secure.nsec3.example.db.in
+zonefile=secure.nsec3.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a RSAMD5 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+#  NSEC3/NSEC3 test zone
+#
+zone=nsec3.nsec3.example.
+infile=nsec3.nsec3.example.db.in
+zonefile=nsec3.nsec3.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -3 - -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+#  OPTOUT/NSEC3 test zone
+#
+zone=optout.nsec3.example.
+infile=optout.nsec3.example.db.in
+zonefile=optout.nsec3.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+# A nsec3 zone (non-optout).
+#
+zone=nsec3.example.
+infile=nsec3.example.db.in
+zonefile=nsec3.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -g -3 - -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+#  OPTOUT/NSEC test zone
+#
+zone=secure.optout.example.
+infile=secure.optout.example.db.in
+zonefile=secure.optout.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a RSAMD5 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+#  OPTOUT/NSEC3 test zone
+#
+zone=nsec3.optout.example.
+infile=nsec3.optout.example.db.in
+zonefile=nsec3.optout.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -3 - -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+#  OPTOUT/OPTOUT test zone
+#
+zone=optout.optout.example.
+infile=optout.optout.example.db.in
+zonefile=optout.optout.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+# A optout nsec3 zone.
+#
+zone=optout.example.
+infile=optout.example.db.in
+zonefile=optout.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -g -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+# A nsec3 zone (non-optout) with unknown hash algorithm.
+#
+zone=nsec3-unknown.example.
+infile=nsec3-unknown.example.db.in
+zonefile=nsec3-unknown.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -3 - -U -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+# A optout nsec3 zone.
+#
+zone=optout-unknown.example.
+infile=optout-unknown.example.db.in
+zonefile=optout-unknown.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -3 - -U -A -r $RANDFILE -o $zone $zonefile > /dev/null
+
+#
+# A multiple parameter nsec3 zone.
+#
+zone=multiple.example.
+infile=multiple.example.db.in
+zonefile=multiple.example.db
+
+keyname=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -r $RANDFILE -o $zone $zonefile > /dev/null
+mv $zonefile.signed $zonefile
+$SIGNER -3 - -r $RANDFILE -o $zone $zonefile > /dev/null
+mv $zonefile.signed $zonefile
+$SIGNER -3 AAAA -r $RANDFILE -o $zone $zonefile > /dev/null
+mv $zonefile.signed $zonefile
+$SIGNER -3 BBBB -r $RANDFILE -o $zone $zonefile > /dev/null
+mv $zonefile.signed $zonefile
+$SIGNER -3 CCCC -r $RANDFILE -o $zone $zonefile > /dev/null
+mv $zonefile.signed $zonefile
+$SIGNER -3 DDDD -r $RANDFILE -o $zone $zonefile > /dev/null
diff --git a/bin/tests/system/dnssec/ns7/named.conf b/bin/tests/system/dnssec/ns7/named.conf
new file mode 100644 (file)
index 0000000..622ffff
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2006  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.2 2008/09/24 02:46:21 marka Exp $ */
+
+// NS3
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.7;
+       notify-source 10.53.0.7;
+       transfer-source 10.53.0.7;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.7; };
+       listen-on-v6 { none; };
+       recursion no;
+       notify yes;
+       dnssec-enable yes;
+       dnssec-validation yes;
+};
+
+zone "." {
+       type hint;
+       file "../../common/root.hint";
+};
+
+zone "nsec3.example" {
+       type slave;
+       masters { 10.53.0.3; };
+       file "nsec3.example.bk";
+};
+
+zone "optout.example" {
+       type slave;
+       masters { 10.53.0.3; };
+       file "optout.example.bk";
+};
+
+zone "nsec3-unknown.example" {
+       type slave;
+       masters { 10.53.0.3; };
+       file "nsec3-unknown.example.bk";
+};
+
+zone "optout-unknown.example" {
+       type slave;
+       masters { 10.53.0.3; };
+       file "optout-unknown.example.bk";
+};
+
+zone "multiple.example" {
+       type slave;
+       masters { 10.53.0.3; };
+       file "multiple.example.bk";
+};
+
+include "trusted.conf";
index e676cb01fbb605f71ebcfd6c2b707635e3996374..cc129061266cc4cfa4aae41cabfc6221fb4b024e 100644 (file)
@@ -15,7 +15,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: tests.sh,v 1.51 2007/06/19 23:47:02 tbox Exp $
+# $Id: tests.sh,v 1.52 2008/09/24 02:46:21 marka Exp $
 
 SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
@@ -38,7 +38,7 @@ n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:checking positive validation ($n)"
+echo "I:checking positive validation NSEC ($n)"
 ret=0
 $DIG $DIGOPTS +noauth a.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
 $DIG $DIGOPTS +noauth a.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1
@@ -48,39 +48,181 @@ n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:checking positive wildcard validation ($n)"
+echo "I:checking positive validation NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.nsec3.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.nsec3.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking positive validation OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.optout.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.optout.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking positive wildcard validation NSEC ($n)"
 ret=0
 $DIG $DIGOPTS a.wild.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
 $DIG $DIGOPTS a.wild.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1
 $PERL ../digcomp.pl dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking positive wildcard validation NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS a.wild.nsec3.example. @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS a.wild.nsec3.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking positive wildcard validation OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS a.wild.optout.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS a.wild.optout.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
 n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:checking negative validation ($n)"
+echo "I:checking negative validation NXDOMAIN NSEC ($n)"
 ret=0
 $DIG $DIGOPTS +noauth q.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
 $DIG $DIGOPTS +noauth q.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1
 $PERL ../digcomp.pl dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking negative validation NXDOMAIN NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS +noauth q.nsec3.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth q.nsec3.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking negative validation NXDOMAIN OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS +noauth q.optout.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth q.optout.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1
+# Note - this is looking for failure, hence the &&
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking negative validation NODATA NSEC ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.example. @10.53.0.2 txt > dig.out.ns2.test$n || ret=1
+$DIG $DIGOPTS +noauth a.example. @10.53.0.4 txt > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "ANSWER: 0" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking negative validation NODATA NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.nsec3.example. \
+       @10.53.0.3 txt > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.nsec3.example. \
+       @10.53.0.4 txt > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "ANSWER: 0" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking negative validation NODATA OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.optout.example. \
+       @10.53.0.3 txt > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.optout.example. \
+       @10.53.0.4 txt > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "ANSWER: 0" dig.out.ns4.test$n > /dev/null || ret=1
 n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:checking negative wildcard validation ($n)"
+echo "I:checking negative wildcard validation NSEC ($n)"
 ret=0
 $DIG $DIGOPTS b.wild.example. @10.53.0.2 txt > dig.out.ns2.test$n || ret=1
 $DIG $DIGOPTS b.wild.example. @10.53.0.4 txt > dig.out.ns4.test$n || ret=1
 $PERL ../digcomp.pl dig.out.ns2.test$n dig.out.ns4.test$n || ret=1
 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking negative wildcard validation NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS b.wild.nsec3.example. @10.53.0.3 txt > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS b.wild.nsec3.example. @10.53.0.4 txt > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking negative wildcard validation OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS b.wild.optout.example. \
+       @10.53.0.3 txt > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS b.wild.optout.example. \
+       @10.53.0.4 txt > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+# Note - this is looking for failure, hence the &&
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
 n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
 # Check the insecure.example domain
 
-echo "I:checking 1-server insecurity proof ($n)"
+echo "I:checking 1-server insecurity proof NSEC ($n)"
 ret=0
 $DIG $DIGOPTS +noauth a.insecure.example. @10.53.0.3 a > dig.out.ns3.test$n || ret=1
 $DIG $DIGOPTS +noauth a.insecure.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1
@@ -92,7 +234,31 @@ n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:checking 1-server negative insecurity proof ($n)"
+echo "I:checking 1-server insecurity proof NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.insecure.nsec3.example. @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.insecure.nsec3.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+# Note - this is looking for failure, hence the &&
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking 1-server insecurity proof OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.insecure.optout.example. @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.insecure.optout.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+# Note - this is looking for failure, hence the &&
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking 1-server negative insecurity proof NSEC ($n)"
 ret=0
 $DIG $DIGOPTS q.insecure.example. a @10.53.0.3 \
        > dig.out.ns3.test$n || ret=1
@@ -106,7 +272,35 @@ n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:checking 1-server negative insecurity proof with SOA hack ($n)"
+echo "I:checking 1-server negative insecurity proof NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS q.insecure.nsec3.example. a @10.53.0.3 \
+       > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS q.insecure.nsec3.example. a @10.53.0.4 \
+       > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1
+# Note - this is looking for failure, hence the &&
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking 1-server negative insecurity proof OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS q.insecure.optout.example. a @10.53.0.3 \
+       > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS q.insecure.optout.example. a @10.53.0.4 \
+       > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1
+# Note - this is looking for failure, hence the &&
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking 1-server negative insecurity proof with SOA hack NSEC ($n)"
 ret=0
 $DIG $DIGOPTS r.insecure.example. soa @10.53.0.3 \
        > dig.out.ns3.test$n || ret=1
@@ -114,6 +308,37 @@ $DIG $DIGOPTS r.insecure.example. soa @10.53.0.4 \
        > dig.out.ns4.test$n || ret=1
 $PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
 grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1
+grep "0        IN      SOA" dig.out.ns4.test$n > /dev/null || ret=1
+# Note - this is looking for failure, hence the &&
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking 1-server negative insecurity proof with SOA hack NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS r.insecure.nsec3.example. soa @10.53.0.3 \
+       > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS r.insecure.nsec3.example. soa @10.53.0.4 \
+       > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1
+grep "0        IN      SOA" dig.out.ns4.test$n > /dev/null || ret=1
+# Note - this is looking for failure, hence the &&
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking 1-server negative insecurity proof with SOA hack OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS r.insecure.optout.example. soa @10.53.0.3 \
+       > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS r.insecure.optout.example. soa @10.53.0.4 \
+       > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1
+grep "0        IN      SOA" dig.out.ns4.test$n > /dev/null || ret=1
 # Note - this is looking for failure, hence the &&
 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null && ret=1
 n=`expr $n + 1`
@@ -122,16 +347,136 @@ status=`expr $status + $ret`
 
 # Check the secure.example domain
 
-echo "I:checking multi-stage positive validation ($n)"
+echo "I:checking multi-stage positive validation NSEC/NSEC ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.secure.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.secure.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking multi-stage positive validation NSEC/NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.nsec3.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.nsec3.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking multi-stage positive validation NSEC/OPTOUT ($n)"
 ret=0
-$DIG $DIGOPTS +noauth a.secure.example. @10.53.0.3 a > dig.out.ns3.test$n || ret=1
-$DIG $DIGOPTS +noauth a.secure.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$DIG $DIGOPTS +noauth a.optout.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.optout.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
 $PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking multi-stage positive validation NSEC3/NSEC ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.secure.nsec3.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.secure.nsec3.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking multi-stage positive validation NSEC3/NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.nsec3.nsec3.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.nsec3.nsec3.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking multi-stage positive validation NSEC3/OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.optout.nsec3.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.optout.nsec3.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking multi-stage positive validation OPTOUT/NSEC ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.secure.optout.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.secure.optout.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking multi-stage positive validation OPTOUT/NSEC3 ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.nsec3.optout.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.nsec3.optout.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking multi-stage positive validation OPTOUT/OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS +noauth a.optout.optout.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth a.optout.optout.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
 n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
+echo "I:checking empty NODATA OPTOUT ($n)"
+ret=0
+$DIG $DIGOPTS +noauth empty.optout.example. \
+       @10.53.0.3 a > dig.out.ns3.test$n || ret=1
+$DIG $DIGOPTS +noauth empty.optout.example. \
+       @10.53.0.4 a > dig.out.ns4.test$n || ret=1
+$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
+grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
+#grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
 # Check the bogus domain
 
 echo "I:checking failed validation ($n)"
index da448e10715003c0fd35af7f37b330ea66dbb710..779d6f95ee9634e7706a2e5f8c0a6e38229905da 100644 (file)
@@ -15,7 +15,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: ifconfig.sh,v 1.54 2008/07/25 20:40:07 fdupont Exp $
+# $Id: ifconfig.sh,v 1.55 2008/09/24 02:46:21 marka Exp $
 
 #
 # Set up interface aliases for bind9 system tests.
@@ -57,7 +57,7 @@ esac
 case "$1" in
 
     start|up)
-       for ns in 1 2 3 4 5 6
+       for ns in 1 2 3 4 5 6 7
        do
                if test -n "$base"
                then
@@ -120,7 +120,7 @@ case "$1" in
        ;;
 
     stop|down)
-       for ns in 6 5 4 3 2 1
+       for ns in 6 5 4 3 2 1
        do
                if test -n "$base"
                then
index 89f61c8943845a0fdbbf137b4b650c2ed460c28a..36326afe53608ecc6befa77833323390c3ce7a02 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone2bdb.c,v 1.1 2002/05/16 04:25:22 marka Exp $ */
+/* $Id: zone2bdb.c,v 1.2 2008/09/24 02:46:21 marka Exp $ */
 
 #include <stdio.h>
 
@@ -144,7 +144,7 @@ main(int argc, char *argv[])
 
        REQUIRE(dns_db_load(db, argv[2]) == ISC_R_SUCCESS);
 
-       REQUIRE(dns_db_createiterator(db, ISC_FALSE, &dbiter) == ISC_R_SUCCESS);
+       REQUIRE(dns_db_createiterator(db, 0, &dbiter) == ISC_R_SUCCESS);
 
        dns_rdataset_init(&rdataset);
        dns_rdata_init(&rdata);
index 90028b6d55f6da14a27da450c01b90fb1ab33c5e..c2820fdc2b58fcc0047f95fff5827a95d888a501 100644 (file)
@@ -211,7 +211,7 @@ main (int *argc, char **argv)
   result = dns_db_load (db, zonefile);
   isc_result_check (result, "Check Zone Syntax: dns_db_load");
 
-  result = dns_db_createiterator (db, ISC_FALSE, &dbit);
+  result = dns_db_createiterator (db, 0, &dbit);
   isc_result_check (result, "dns_db_createiterator");
 
   result = dns_dbiterator_first (dbit);
index bcc7242a42032f7abb1c3813df85387bf7db6c14..8e0b8abd11bcdc4545fcc66f99dc388ade00d3c9 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zonetodb.c,v 1.18 2007/06/18 23:47:33 tbox Exp $ */
+/* $Id: zonetodb.c,v 1.19 2008/09/24 02:46:21 marka Exp $ */
 
 #include <stdlib.h>
 #include <string.h>
@@ -230,7 +230,7 @@ main(int argc, char **argv) {
        PQclear(res);
 
        dbiter = NULL;
-       result = dns_db_createiterator(db, ISC_FALSE, &dbiter);
+       result = dns_db_createiterator(db, 0, &dbiter);
        check_result(result, "dns_db_createiterator()");
 
        result = dns_dbiterator_first(dbiter);
index b6d7fd2f53c51a0f02efc4014f06b95cf32ff03d..abaf52bf0219cabb85a91a53b5fead6be585f336 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone2sqlite.c,v 1.1 2007/03/05 05:30:22 marka Exp $ */
+/* $Id: zone2sqlite.c,v 1.2 2008/09/24 02:46:21 marka Exp $ */
 
 #include <stdlib.h>
 #include <string.h>
@@ -238,7 +238,7 @@ main(int argc, char *argv[])
     }
     
     dbiter = NULL;
-    result = dns_db_createiterator(db, ISC_FALSE, &dbiter);
+    result = dns_db_createiterator(db, 0, &dbiter);
     check_result(result, "dns_db_createiterator()");
     
     result = dns_dbiterator_first(dbiter);
index 627fca90ca4361ada5ed0312233675bf8de85cd2..18c32246f77ecb9cad3ee78e4ed428989ea677bb 100644 (file)
@@ -151,6 +151,7 @@ options {
         notify-source ( <ipv4_address> | * ) [ port ( <integer> | * ) ];
         notify-source-v6 ( <ipv6_address> | * ) [ port ( <integer> | * ) ];
         notify-to-soa <boolean>;
+        nsec3-test-zone <boolean>; // test only
         pid-file ( <quoted_string> | none );
         port <integer>;
         preferred-glue <string>;
@@ -327,6 +328,7 @@ view <string> <optional_class> {
         notify-source ( <ipv4_address> | * ) [ port ( <integer> | * ) ];
         notify-source-v6 ( <ipv6_address> | * ) [ port ( <integer> | * ) ];
         notify-to-soa <boolean>;
+        nsec3-test-zone <boolean>; // test only
         preferred-glue <string>;
         provide-ixfr <boolean>;
         query-source <querysource4>;
@@ -435,6 +437,7 @@ view <string> <optional_class> {
                 notify-source-v6 ( <ipv6_address> | * ) [ port ( <integer>
                     | * ) ];
                 notify-to-soa <boolean>;
+                nsec3-test-zone <boolean>; // test only
                 pubkey <integer> <integer> <integer>
                     <quoted_string>; // obsolete
                 sig-signing-nodes <integer>;
@@ -512,6 +515,7 @@ zone <string> <optional_class> {
         notify-source ( <ipv4_address> | * ) [ port ( <integer> | * ) ];
         notify-source-v6 ( <ipv6_address> | * ) [ port ( <integer> | * ) ];
         notify-to-soa <boolean>;
+        nsec3-test-zone <boolean>; // test only
         pubkey <integer> <integer> <integer> <quoted_string>; // obsolete
         sig-signing-nodes <integer>;
         sig-signing-signatures <integer>;
index 270ad8a6f7aad6a6921e0a3e624c451b93901389..ef5c12a5d0d76fcd1849f57571dca1cae47b6dc5 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.162 2008/04/01 23:47:10 tbox Exp $
+# $Id: Makefile.in,v 1.163 2008/09/24 02:46:22 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -60,7 +60,7 @@ DNSOBJS =     acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
                dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ iptable.@O@ journal.@O@ \
                keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \
                master.@O@ masterdump.@O@ message.@O@ \
-               name.@O@ ncache.@O@ nsec.@O@ order.@O@ peer.@O@ portlist.@O@ \
+               name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ order.@O@ peer.@O@ portlist.@O@ \
                rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
                rdatalist.@O@ \
                rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ request.@O@ \
@@ -86,7 +86,7 @@ DNSSRCS =     acache.c acl.c adb.c byaddr.c \
                dlz.c dnssec.c ds.c forward.c iptable.c journal.c \
                keytable.c lib.c log.c lookup.c \
                master.c masterdump.c message.c \
-               name.c ncache.c nsec.c order.c peer.c portlist.c \
+               name.c ncache.c nsec.c nsec3.c order.c peer.c portlist.c \
                rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \
                rdatalist.c \
                rdataset.c rdatasetiter.c rdataslab.c request.c \
index cc7cad4a1690cfa9f36b4b2837d832c4d300dac3..1b8c388b0e379ba2d15419ba72997fb2bd219d29 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: cache.c,v 1.79 2008/05/01 18:23:07 jinmei Exp $ */
+/* $Id: cache.c,v 1.80 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -861,7 +861,7 @@ dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
 
        REQUIRE(VALID_CACHE(cache));
 
-       result = dns_db_createiterator(cache->db, ISC_FALSE, &iterator);
+       result = dns_db_createiterator(cache->db, 0, &iterator);
        if (result != ISC_R_SUCCESS)
                return result;
 
index 5539962dc19670400789ff157c0ee8c65e964544..a4c28641701fb9a80166a89e40d3ca27cb5205a7 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: db.c,v 1.87 2008/04/03 05:55:52 marka Exp $ */
+/* $Id: db.c,v 1.88 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -464,6 +464,21 @@ dns_db_findnode(dns_db_t *db, dns_name_t *name,
        return ((db->methods->findnode)(db, name, create, nodep));
 }
 
+isc_result_t
+dns_db_findnsec3node(dns_db_t *db, dns_name_t *name,
+                    isc_boolean_t create, dns_dbnode_t **nodep)
+{
+
+       /*
+        * Find the node with name 'name'.
+        */
+
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(nodep != NULL && *nodep == NULL);
+
+       return ((db->methods->findnsec3node)(db, name, create, nodep));
+}
+
 isc_result_t
 dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
            dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
@@ -598,7 +613,7 @@ dns_db_printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
  ***/
 
 isc_result_t
-dns_db_createiterator(dns_db_t *db, isc_boolean_t relative_names,
+dns_db_createiterator(dns_db_t *db, unsigned int flags,
                      dns_dbiterator_t **iteratorp)
 {
        /*
@@ -608,7 +623,7 @@ dns_db_createiterator(dns_db_t *db, isc_boolean_t relative_names,
        REQUIRE(DNS_DB_VALID(db));
        REQUIRE(iteratorp != NULL && *iteratorp == NULL);
 
-       return (db->methods->createiterator(db, relative_names, iteratorp));
+       return (db->methods->createiterator(db, flags, iteratorp));
 }
 
 /***
@@ -869,6 +884,23 @@ dns_db_getrrsetstats(dns_db_t *db) {
        return (NULL);
 }
 
+isc_result_t
+dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+                         dns_hash_t *hash, isc_uint8_t *flags,
+                         isc_uint16_t *iterations,
+                         unsigned char *salt, size_t *salt_length)
+{
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(dns_db_iszone(db) == ISC_TRUE);
+
+       if (db->methods->getnsec3parameters != NULL)
+               return ((db->methods->getnsec3parameters)(db, version, hash,
+                                                         flags, iterations,
+                                                         salt, salt_length));
+
+       return (ISC_R_NOTFOUND);
+}
+
 isc_result_t
 dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
                      isc_stdtime_t resign)
index af90e953f0b5af840cc3712b24df2596cc0b5217..850ca91c4d009215435cf2361cb2b989a17fc105 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: diff.c,v 1.17 2008/04/02 02:37:42 marka Exp $ */
+/* $Id: diff.c,v 1.18 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -256,8 +256,6 @@ diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
                 * but such diffs should never be created in the first
                 * place.
                 */
-               node = NULL;
-               CHECK(dns_db_findnode(db, name, ISC_TRUE, &node));
 
                while (t != NULL && dns_name_equal(&t->name, name)) {
                        dns_rdatatype_t type, covers;
@@ -294,6 +292,15 @@ diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
                        ISC_LIST_INIT(rdl.rdata);
                        ISC_LINK_INIT(&rdl, link);
 
+                       node = NULL;
+                       if (type != dns_rdatatype_nsec3 &&
+                           covers != dns_rdatatype_nsec3)
+                               CHECK(dns_db_findnode(db, name, ISC_TRUE,
+                                                     &node));
+                       else
+                               CHECK(dns_db_findnsec3node(db, name, ISC_TRUE,
+                                                          &node));
+
                        offline = ISC_FALSE;
                        while (t != NULL &&
                               dns_name_equal(&t->name, name) &&
@@ -394,11 +401,11 @@ diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
                                        dns_rdataset_disassociate(modified);
                                CHECK(result);
                        }
+                       dns_db_detachnode(db, &node);
                        if (modified != NULL &&
                            dns_rdataset_isassociated(modified))
                                dns_rdataset_disassociate(modified);
                }
-               dns_db_detachnode(db, &node);
        }
        return (ISC_R_SUCCESS);
 
index 86864514b42141b66a81d60a70d0c993a588ea56..4bfcfe8df66fb7c088f76ee61c15ee79c894c1e4 100644 (file)
@@ -31,7 +31,7 @@
 
 /*
  * Principal Author: Brian Wellington
- * $Id: dst_api.c,v 1.14 2008/04/01 23:47:10 tbox Exp $
+ * $Id: dst_api.c,v 1.15 2008/09/24 02:46:22 marka Exp $
  */
 
 /*! \file */
@@ -183,8 +183,10 @@ dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) {
        RETERR(dst__openssl_init());
        RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5]));
        RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1]));
+       RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1]));
 #ifdef HAVE_OPENSSL_DSA
        RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_DSA]));
+       RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_NSEC3DSA]));
 #endif
        RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
 #endif /* OPENSSL */
@@ -843,9 +845,11 @@ dst_key_sigsize(const dst_key_t *key, unsigned int *n) {
        switch (key->key_alg) {
        case DST_ALG_RSAMD5:
        case DST_ALG_RSASHA1:
+       case DST_ALG_NSEC3RSASHA1:
                *n = (key->key_size + 7) / 8;
                break;
        case DST_ALG_DSA:
+       case DST_ALG_NSEC3DSA:
                *n = DNS_SIG_DSASIGSIZE;
                break;
        case DST_ALG_HMACMD5:
@@ -1058,7 +1062,9 @@ issymmetric(const dst_key_t *key) {
        switch (key->key_alg) {
        case DST_ALG_RSAMD5:
        case DST_ALG_RSASHA1:
+       case DST_ALG_NSEC3RSASHA1:
        case DST_ALG_DSA:
+       case DST_ALG_NSEC3DSA:
        case DST_ALG_DH:
                return (ISC_FALSE);
        case DST_ALG_HMACMD5:
@@ -1256,7 +1262,8 @@ algorithm_status(unsigned int alg) {
 #ifndef OPENSSL
        if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
            alg == DST_ALG_DSA || alg == DST_ALG_DH ||
-           alg == DST_ALG_HMACMD5)
+           alg == DST_ALG_HMACMD5 || alg == DST_ALG_NSEC3DSA ||
+           alg == DST_ALG_NSEC3RSASHA1)
                return (DST_R_NOCRYPTO);
 #endif
        return (DST_R_UNSUPPORTEDALG);
index 157dbfcaf7e3202af2d2d098ba9fecfa9a91f4f1..85d516fb4a17f6404e3cc8a19a447b44e5650d53 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: gen.c,v 1.81 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: gen.c,v 1.82 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -41,6 +41,8 @@
 #include "gen-unix.h"
 #endif
 
+#define TYPECLASSLEN 21
+
 #define FROMTEXTARGS "rdclass, type, lexer, origin, options, target, callbacks"
 #define FROMTEXTCLASS "rdclass"
 #define FROMTEXTTYPE "type"
@@ -134,21 +136,21 @@ const char copyright[] =
 struct cc {
        struct cc *next;
        int rdclass;
-       char classname[11];
+       char classname[TYPECLASSLEN];
 } *classes;
 
 struct tt {
        struct tt *next;
        int rdclass;
        int type;
-       char classname[11];
-       char typename[11];
+       char classname[TYPECLASSLEN];
+       char typename[TYPECLASSLEN];
        char dirname[256];              /* XXX Should be max path length */
 } *types;
 
 struct ttnam {
-       char typename[11];
-       char macroname[11];
+       char typename[TYPECLASSLEN];
+       char macroname[TYPECLASSLEN];
        char attr[256];
        unsigned int sorted;
        int type;
@@ -215,7 +217,7 @@ doswitch(const char *name, const char *function, const char *args,
        int first = 1;
        int lasttype = 0;
        int subswitch = 0;
-       char buf1[11], buf2[11];
+       char buf1[TYPECLASSLEN], buf2[TYPECLASSLEN];
        const char *result = " result =";
 
        if (res == NULL)
@@ -281,7 +283,7 @@ doswitch(const char *name, const char *function, const char *args,
 void
 dodecl(char *type, char *function, char *args) {
        struct tt *tt;
-       char buf1[11], buf2[11];
+       char buf1[TYPECLASSLEN], buf2[TYPECLASSLEN];
 
        fputs("\n", stdout);
        for (tt = types; tt; tt = tt->next)
@@ -332,7 +334,7 @@ insert_into_typenames(int type, const char *typename, const char *attr) {
                fprintf(stderr, "Error: typenames array too small\n");
                exit(1);
        }
-       
+
        if (strlen(typename) > sizeof(ttn->typename) - 1) {
                fprintf(stderr, "Error:  type name %s is too long\n",
                        typename);
@@ -392,6 +394,8 @@ add(int rdclass, const char *classname, int type, const char *typename,
        newtt->type = type;
        strcpy(newtt->classname, classname);
        strcpy(newtt->typename, typename);
+       if (strncmp(dirname, "./", 2) == 0)
+               dirname += 2;
        strcpy(newtt->dirname, dirname);
 
        tt = types;
@@ -449,16 +453,16 @@ add(int rdclass, const char *classname, int type, const char *typename,
 
 void
 sd(int rdclass, const char *classname, const char *dirname, char filetype) {
-       char buf[sizeof("0123456789_65535.h")];
-       char fmt[sizeof("%10[-0-9a-z]_%d.h")];
+       char buf[sizeof("01234567890123456789_65535.h")];
+       char fmt[sizeof("%20[-0-9a-z]_%d.h")];
        int type;
-       char typename[11];
+       char typename[TYPECLASSLEN];
        isc_dir_t dir;
 
        if (!start_directory(dirname, &dir))
                return;
 
-       sprintf(fmt,"%s%c", "%10[-0-9a-z]_%d.", filetype);
+       sprintf(fmt,"%s%c", "%20[-0-9a-z]_%d.", filetype);
        while (next_file(&dir)) {
                if (sscanf(dir.filename, fmt, typename, &type) != 2)
                        continue;
@@ -495,7 +499,7 @@ main(int argc, char **argv) {
        char buf[256];                  /* XXX Should be max path length */
        char srcdir[256];               /* XXX Should be max path length */
        int rdclass;
-       char classname[11];
+       char classname[TYPECLASSLEN];
        struct tt *tt;
        struct cc *cc;
        struct ttnam *ttn, *ttn2;
@@ -510,7 +514,7 @@ main(int argc, char **argv) {
        int structs = 0;
        int depend = 0;
        int c, i, j;
-       char buf1[11];
+       char buf1[TYPECLASSLEN];
        char filetype = 'c';
        FILE *fd;
        char *prefix = NULL;
@@ -594,7 +598,7 @@ main(int argc, char **argv) {
        sd(0, "", buf, filetype);
 
        if (time(&now) != -1) {
-               if ((tm = localtime(&now)) != NULL && tm->tm_year > 104) 
+               if ((tm = localtime(&now)) != NULL && tm->tm_year > 104)
                        sprintf(year, "-%d", tm->tm_year + 1900);
                else
                        year[0] = 0;
@@ -692,7 +696,7 @@ main(int argc, char **argv) {
                                "\t\t    strncasecmp(_s,(_tn),"
                                "(sizeof(_s) - 1)) == 0) { \\\n");
                fprintf(stdout, "\t\t\tif ((dns_rdatatype_attributes(_d) & "
-                                 "DNS_RDATATYPEATTR_RESERVED) != 0) \\\n");
+                                 "DNS_RDATATYPEATTR_RESERVED) != 0) \\\n");
                fprintf(stdout, "\t\t\t\treturn (ISC_R_NOTIMPLEMENTED); \\\n");
                fprintf(stdout, "\t\t\t*(_tp) = _d; \\\n");
                fprintf(stdout, "\t\t\treturn (ISC_R_SUCCESS); \\\n");
@@ -743,7 +747,7 @@ main(int argc, char **argv) {
                        if (ttn == NULL)
                                continue;
                        fprintf(stdout, "\tcase %u: return (%s); \\\n",
-                               i, upper(ttn->attr));
+                               i, upper(ttn->attr));
                }
                fprintf(stdout, "\t}\n");
 
@@ -755,7 +759,7 @@ main(int argc, char **argv) {
                                continue;
                        fprintf(stdout, "\tcase %u: return "
                                "(str_totext(\"%s\", target)); \\\n",
-                               i, upper(ttn->typename));
+                               i, upper(ttn->typename));
                }
                fprintf(stdout, "\t}\n");
 
index 05115325a2f76a5a8aba3f75d5a457ec7bc6e6f3..8e2246339b6f6cbd9c5ac643343c69d104a9f993 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: db.h,v 1.92 2008/04/03 05:55:52 marka Exp $ */
+/* $Id: db.h,v 1.93 2008/09/24 02:46:22 marka Exp $ */
 
 #ifndef DNS_DB_H
 #define DNS_DB_H 1
@@ -111,8 +111,7 @@ typedef struct dns_dbmethods {
                                      isc_stdtime_t now);
        void            (*printnode)(dns_db_t *db, dns_dbnode_t *node,
                                     FILE *out);
-       isc_result_t    (*createiterator)(dns_db_t *db,
-                                         isc_boolean_t relative_names,
+       isc_result_t    (*createiterator)(dns_db_t *db, unsigned int options,
                                          dns_dbiterator_t **iteratorp);
        isc_result_t    (*findrdataset)(dns_db_t *db, dns_dbnode_t *node,
                                        dns_dbversion_t *version,
@@ -148,6 +147,16 @@ typedef struct dns_dbmethods {
        isc_result_t    (*getoriginnode)(dns_db_t *db, dns_dbnode_t **nodep);
        void            (*transfernode)(dns_db_t *db, dns_dbnode_t **sourcep,
                                        dns_dbnode_t **targetp);
+       isc_result_t    (*getnsec3parameters)(dns_db_t *db,
+                                             dns_dbversion_t *version,
+                                             dns_hash_t *hash,
+                                             isc_uint8_t *flags,
+                                             isc_uint16_t *iterations,
+                                             unsigned char *salt,
+                                             size_t *salt_len);
+       isc_result_t    (*findnsec3node)(dns_db_t *db, dns_name_t *name,
+                                        isc_boolean_t create,
+                                        dns_dbnode_t **nodep);
        isc_result_t    (*setsigningtime)(dns_db_t *db,
                                          dns_rdataset_t *rdataset,
                                          isc_stdtime_t resign);
@@ -203,6 +212,7 @@ struct dns_db {
 #define DNS_DBFIND_NOEXACT             0x10
 #define DNS_DBFIND_FORCENSEC           0x20
 #define DNS_DBFIND_COVERINGNSEC                0x40
+#define DNS_DBFIND_FORCENSEC3          0x80
 /*@}*/
 
 /*@{*/
@@ -220,6 +230,15 @@ struct dns_db {
  */
 #define DNS_DBSUB_EXACT                        0x01
 
+/*@{*/
+/*%
+ * Iterator options
+ */
+#define DNS_DB_RELATIVENAMES   0x1
+#define DNS_DB_NSEC3ONLY       0x2
+#define DNS_DB_NONSEC3         0x4
+/*@}*/
+
 /*****
  ***** Methods
  *****/
@@ -811,7 +830,7 @@ dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
  *                                             name, and 'rdataset' contains
  *                                             the negative caching proof.
  *
- *     \li     #DNS_R_EMPTYNAME                        The name exists but there is
+ *     \li     #DNS_R_EMPTYNAME                The name exists but there is
  *                                             no data at the name.
  *
  *     \li     #DNS_R_COVERINGNSEC             The returned data is a NSEC
@@ -964,16 +983,17 @@ dns_db_printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out);
  ***/
 
 isc_result_t
-dns_db_createiterator(dns_db_t *db, isc_boolean_t relative_names,
+dns_db_createiterator(dns_db_t *db, unsigned int options,
                      dns_dbiterator_t **iteratorp);
 /*%<
  * Create an iterator for version 'version' of 'db'.
  *
  * Notes:
  *
- * \li If 'relative_names' is ISC_TRUE, then node names returned by the
- *     iterator will be relative to the iterator's current origin.  If
- *     #ISC_FALSE, then the node names will be absolute.
+ * \li One or more of the following options can be set.
+ *     #DNS_DB_RELATIVENAMES
+ *     #DNS_DB_NSEC3ONLY
+ *     #DNS_DB_NONSEC3
  *
  * Requires:
  *
@@ -1341,6 +1361,55 @@ dns_db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep);
  * \li #ISC_R_NOTFOUND - the DB implementation does not support this feature.
  */
 
+isc_result_t
+dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+                         dns_hash_t *hash, isc_uint8_t *flags,
+                         isc_uint16_t *interations,
+                         unsigned char *salt, size_t *salt_length);
+/*%<
+ * Get the NSEC3 parameters that are associated with this zone.
+ *
+ * Requires:
+ * \li 'db' is a valid zone database.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND - the DB implementation does not support this feature
+ *                       or this zone does not have NSEC3 records.
+ */
+
+isc_result_t
+dns_db_findnsec3node(dns_db_t *db, dns_name_t *name,
+                    isc_boolean_t create, dns_dbnode_t **nodep);
+/*%<
+ * Find the NSEC3 node with name 'name'.
+ *
+ * Notes:
+ * \li If 'create' is ISC_TRUE and no node with name 'name' exists, then
+ *     such a node will be created.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'name' is a valid, non-empty, absolute name.
+ *
+ * \li nodep != NULL && *nodep == NULL
+ *
+ * Ensures:
+ *
+ * \li On success, *nodep is attached to the node with name 'name'.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND                 If !create and name not found.
+ * \li #ISC_R_NOMEMORY                 Can only happen if create is ISC_TRUE.
+ *
+ * \li Other results are possible, depending upon the database
+ *     implementation used.
+ */
+
 isc_result_t
 dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
                      isc_stdtime_t resign);
@@ -1388,7 +1457,6 @@ dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset,
  * \li 'version' to be open for writing.
  */
 
-
 dns_stats_t *
 dns_db_getrrsetstats(dns_db_t *db);
 /*%<
index dec466f17799fb7387f686adb0facc4433d70fa7..df72fdf362c5b7f95ab6342c4ff57606330964f4 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: keyvalues.h,v 1.21 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: keyvalues.h,v 1.22 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_KEYVALUES_H
 #define DNS_KEYVALUES_H 1
 #define DNS_KEYALG_RSA         DNS_KEYALG_RSAMD5
 #define DNS_KEYALG_DH          2       /*%< Diffie Hellman KEY */
 #define DNS_KEYALG_DSA         3       /*%< DSA KEY */
-#define DNS_KEYALG_DSS         NS_ALG_DSA
+#define DNS_KEYALG_NSEC3DSA    6
+#define DNS_KEYALG_DSS         DNS_ALG_DSA
 #define DNS_KEYALG_ECC         4
 #define DNS_KEYALG_RSASHA1     5
+#define DNS_KEYALG_NSEC3RSASHA1        7
 #define DNS_KEYALG_INDIRECT    252
 #define DNS_KEYALG_PRIVATEDNS  253
 #define DNS_KEYALG_PRIVATEOID  254     /*%< Key begins with OID giving alg */
index 69e777777f7290fd6085f58f792998a711ed418f..42521b33596ecd1531b3aab47ac8645149858b9f 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: masterdump.h,v 1.41 2008/04/01 23:47:10 tbox Exp $ */
+/* $Id: masterdump.h,v 1.42 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_MASTERDUMP_H
 #define DNS_MASTERDUMP_H 1
@@ -332,6 +332,9 @@ dns_master_stylecreate(dns_master_style_t **style, unsigned int flags,
 void
 dns_master_styledestroy(dns_master_style_t **style, isc_mem_t *mctx);
 
+const char *
+dns_trust_totext(dns_trust_t trust);
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_MASTERDUMP_H */
index db55ed68a535bdf05aa7b9988a350698e495210f..f16970b6f1eb08757305438717d257ee5a1895e4 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: ncache.h,v 1.23 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: ncache.h,v 1.24 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_NCACHE_H
 #define DNS_NCACHE_H 1
@@ -63,6 +63,11 @@ isc_result_t
 dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
               dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
               dns_rdataset_t *addedrdataset);
+isc_result_t
+dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
+                    dns_dbnode_t *node, dns_rdatatype_t covers,
+                    isc_stdtime_t now, dns_ttl_t maxttl,
+                    isc_boolean_t optout, dns_rdataset_t *addedrdataset);
 /*%<
  * Convert the authority data from 'message' into a negative cache
  * rdataset, and store it in 'cache' at 'node' with a TTL limited to
@@ -71,6 +76,8 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
  * The 'covers' argument is the RR type whose nonexistence we are caching,
  * or dns_rdatatype_any when caching a NXDOMAIN response.
  *
+ * 'optout' indicates a DNS_RATASETATTR_OPTOUT should be set.
+ *
  * Note:
  *\li  If 'addedrdataset' is not NULL, then it will be attached to the added
  *     rdataset.  See dns_db_addrdataset() for more details.
@@ -154,6 +161,19 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
  *
  */
 
+void
+dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
+                  dns_rdataset_t *rdataset);
+
+/*%<
+ * Extract the current rdataset and name from a ncache entry.
+ *
+ * Requires:
+ * \li 'ncacherdataset' to be valid and to be a negative cache entry
+ * \li 'found' to be valid.
+ * \li 'rdataset' to be unassociated.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_NCACHE_H */
index e6d7973e9a75960e658387334bc318ff815c83db..46071c965cc157eded22ee9df4f4844206f696f7 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: nsec.h,v 1.10 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: nsec.h,v 1.11 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_NSEC_H
 #define DNS_NSEC_H 1
@@ -64,6 +64,17 @@ dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type);
  *\li  'nsec' points to a valid rdataset of type NSEC
  */
 
+isc_result_t 
+dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version,
+                  isc_boolean_t *answer);
+/*
+ * Report whether the DNSKEY RRset has a NSEC only algorithm.  Unknown
+ * algorithms are assumed to support NSEC3.
+ * 
+ * Requires:
+ *     'answer' to be non NULL.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_NSEC_H */
index 38993886afae48d0febbebdc857487f0417ca8b1..77bad3ada6d172503a2bcaedcc6fe6cde57fa385 100644 (file)
@@ -1,7 +1,8 @@
 /*
- * Copyright (C) 2008  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003  Internet Software Consortium.
  *
- * Permission to use, copy, modify, and/or distribute this software for any
+ * Permission to use, copy, modify, and 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.
  *
@@ -14,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: nsec3.h,v 1.3 2008/04/04 23:47:01 tbox Exp $ */
+/* $Id: nsec3.h,v 1.4 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_NSEC3_H
 #define DNS_NSEC3_H 1
 #include <isc/lang.h>
 #include <isc/iterated_hash.h>
 
-#include <dns/types.h>
+#include <dns/db.h>
+#include <dns/diff.h>
 #include <dns/name.h>
+#include <dns/rdatastruct.h>
+#include <dns/types.h>
 
 /*
- * hash = 1, iterations + optin = 3, salt length = 1, salt = 255 (max)
+ * hash = 1, flags =1, iterations = 2, salt length = 1, salt = 255 (max)
  * hash length = 1, hash = 255 (max), bitmap = 8192 + 512 (max)
  */
 #define DNS_NSEC3_BUFFERSIZE (6 + 255 + 255 + 8192 + 512)
+/*
+ * hash = 1, flags = 1, iterations = 2, salt length = 1, salt = 255 (max)
+ */
+#define DNS_NSEC3PARAM_BUFFERSIZE (5 + 255)
 
 /*
  * Test "unknown" algorithm.  Is mapped to dns_hash_sha1.
@@ -42,11 +50,12 @@ isc_result_t
 dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version,
                     dns_dbnode_t *node, unsigned int hashalg,
                     unsigned int optin, unsigned int iterations,
-                    const unsigned char *salt, size_t salt_length,
+                    const unsigned char *salt, size_t salt_length, 
                     const unsigned char *nexthash, size_t hash_length,
                     unsigned char *buffer, dns_rdata_t *rdata);
-/*
- * Build the rdata of a NSEC3 record.
+/*%<
+ * Build the rdata of a NSEC3 record for the data at 'node'.
+ * Note: 'node' is not the node where the NSEC3 record will be stored.
  *
  * Requires:
  *     buffer  Points to a temporary buffer of at least
@@ -54,21 +63,13 @@ dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version,
  *     rdata   Points to an initialized dns_rdata_t.
  *
  * Ensures:
- *      *rdata Contains a valid NSEC rdata.  The 'data' member refers
+ *      *rdata Contains a valid NSEC3 rdata.  The 'data' member refers
  *             to 'buffer'.
  */
 
-isc_result_t
-dns_nsec3_build(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
-               int auth_only, const char *salt, size_t salt_length,
-               int iterations, dns_ttl_t ttl);
-/*
- * Build an NSEC3 record and add it to a database.
- */
-
 isc_boolean_t
 dns_nsec3_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type);
-/*
+/*%<
  * Determine if a type is marked as present in an NSEC3 record.
  *
  * Requires:
@@ -81,24 +82,114 @@ dns_nsec3_hashname(dns_fixedname_t *result,
                   size_t *hash_length, dns_name_t *name, dns_name_t *origin,
                   dns_hash_t hashalg, unsigned int iterations,
                   const unsigned char *salt, size_t saltlength);
-/*
+/*%<
  * Make a hashed domain name from an unhashed one. If rethash is not NULL
  * the raw hash is stored there.
  */
 
 unsigned int
 dns_nsec3_hashlength(dns_hash_t hash);
-/*
+/*%<
  * Return the length of the hash produced by the specified algorithm
  * or zero when unknown.
  */
 
 isc_boolean_t
 dns_nsec3_supportedhash(dns_hash_t hash);
-/*
+/*%<
  * Return whether we support this hash algorithm or not.
  */
 
+isc_result_t
+dns_nsec3_addnsec3(dns_db_t *db, dns_dbversion_t *version,
+                   dns_name_t *name, const dns_rdata_nsec3param_t *nsec3param,
+                  dns_ttl_t nsecttl, isc_boolean_t unsecure, dns_diff_t *diff);
+
+isc_result_t
+dns_nsec3_addnsec3s(dns_db_t *db, dns_dbversion_t *version,
+                    dns_name_t *name, dns_ttl_t nsecttl,
+                    isc_boolean_t unsecure, dns_diff_t *diff);
+/*%<
+ * Add NSEC3 records for 'name', recording the change in 'diff'.
+ * Adjust previous NSEC3 records, if any, to reflect the addition.
+ * The existing NSEC3 records are removed.
+ *
+ * dns_nsec3_addnsec3() will only add records to the chain identified by
+ * 'nsec3param'.
+ *
+ * 'unsecure' should be set to reflect if this is a potentially
+ * unsecure delegation (no DS record).
+ *
+ * dns_nsec3_addnsec3s() will examine the NSEC3PARAM RRset to determine which
+ * chains to be updated.  NSEC3PARAM records with the DNS_NSEC3FLAG_CREATE
+ * will be preferentially choosen over NSEC3PARAM records without
+ * DNS_NSEC3FLAG_CREATE set.  NSEC3PARAM records with DNS_NSEC3FLAG_REMOVE
+ * set will be ignored by dns_nsec3_addnsec3s().  If DNS_NSEC3FLAG_CREATE
+ * is set then the new NSEC3 will have OPTOUT set to match the that in the
+ * NSEC3PARAM record otherwise OPTOUT will be inherited from the previous
+ * record in the chain.
+ *
+ * Requires:
+ *     'db' to be valid.
+ *     'version' to be valid or NULL.
+ *     'name' to be valid.
+ *     'nsec3param' to be valid.
+ *     'diff' to be valid.
+ */
+
+isc_result_t
+dns_nsec3_delnsec3(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+                   const dns_rdata_nsec3param_t *nsec3param, dns_diff_t *diff);
+
+isc_result_t
+dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+                    dns_diff_t *diff);
+/*%<
+ * Remove NSEC3 records for 'name', recording the change in 'diff'.
+ * Adjust previous NSEC3 records, if any, to reflect the removal.
+ *
+ * dns_nsec3_delnsec3() performs the above for the chain identified by
+ * 'nsec3param'.
+ *
+ * dns_nsec3_delnsec3s() examines the NSEC3PARAM RRset in a similar manner
+ * to dns_nsec3_addnsec3s().  Unlike dns_nsec3_addnsec3s() updated NSEC3
+ * records have the OPTOUT flag preserved.
+ *
+ * Requires:
+ *     'db' to be valid.
+ *     'version' to be valid or NULL.
+ *     'name' to be valid.
+ *     'nsec3param' to be valid.
+ *     'diff' to be valid.
+ */
+
+isc_result_t
+dns_nsec3_active(dns_db_t *db, dns_dbversion_t *version, 
+                 isc_boolean_t complete, isc_boolean_t *answer);
+/*%<
+ * Check if there are any complete/to be built NSEC3 chains.
+ * If 'complete' is ISC_TRUE only complete chains will be recognised.
+ *
+ * Requires:
+ *     'db' to be valid.
+ *     'version' to be valid or NULL.
+ *     'answer' to be non NULL.
+ */
+
+isc_result_t
+dns_nsec3_maxiterations(dns_db_t *db, dns_dbversion_t *version,
+                        isc_mem_t *mctx, unsigned int *iterationsp);
+/*%<
+ * Find the maximum permissible number of iterations allowed based on
+ * the key strength.
+ *
+ * Requires:
+ *     'db' to be valid.
+ *     'version' to be valid or NULL.
+ *     'mctx' to be valid.
+ *     'iterationsp' to be non NULL.
+ */
+
 ISC_LANG_ENDDECLS
 
-#endif /* DNS_NSEC2_H */
+#endif /* DNS_NSEC3_H */
index f5f00a9f01f5cfc70537350532e47f7ac93d2e72..26f4979184ffe8e1dfca3772b8ee5d91ba676b5c 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rbt.h,v 1.69 2007/10/19 17:15:53 explorer Exp $ */
+/* $Id: rbt.h,v 1.70 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_RBT_H
 #define DNS_RBT_H 1
@@ -72,69 +72,70 @@ ISC_LANG_BEGINDECLS
 typedef struct dns_rbtnode dns_rbtnode_t;
 struct dns_rbtnode {
 #if DNS_RBT_USEMAGIC
-        unsigned int magic;
+       unsigned int magic;
 #endif
-        dns_rbtnode_t *parent;
-        dns_rbtnode_t *left;
-        dns_rbtnode_t *right;
-        dns_rbtnode_t *down;
+       dns_rbtnode_t *parent;
+       dns_rbtnode_t *left;
+       dns_rbtnode_t *right;
+       dns_rbtnode_t *down;
 #ifdef DNS_RBT_USEHASH
-        dns_rbtnode_t *hashnext;
+       dns_rbtnode_t *hashnext;
 #endif
 
-        /*%
-         * Used for LRU cache.  This linked list is used to mark nodes which
-         * have no data any longer, but we cannot unlink at that exact moment
-         * because we did not or could not obtain a write lock on the tree.
-         */
-        ISC_LINK(dns_rbtnode_t) deadlink;
-
-        /*@{*/
-        /*!
-         * The following bitfields add up to a total bitwidth of 32.
-         * The range of values necessary for each item is indicated,
-         * but in the case of "attributes" the field is wider to accomodate
-         * possible future expansion.  "offsetlen" could be one bit
-         * narrower by always adjusting its value by 1 to find the real
-         * offsetlen, but doing so does not gain anything (except perhaps
-         * another bit for "attributes", which doesn't yet need any more).
-         *
-         * In each case below the "range" indicated is what's _necessary_ for
-         * the bitfield to hold, not what it actually _can_ hold.
-         */
-        unsigned int is_root : 1;       /*%< range is 0..1 */
-        unsigned int color : 1;         /*%< range is 0..1 */
-        unsigned int find_callback : 1; /*%< range is 0..1 */
-        unsigned int attributes : 4;    /*%< range is 0..2 */
-        unsigned int namelen : 8;       /*%< range is 1..255 */
-        unsigned int offsetlen : 8;     /*%< range is 1..128 */
-        unsigned int padbytes : 9;      /*%< range is 0..380 */
-        /*@}*/
+       /*%
+        * Used for LRU cache.  This linked list is used to mark nodes which
+        * have no data any longer, but we cannot unlink at that exact moment
+        * because we did not or could not obtain a write lock on the tree.
+        */
+       ISC_LINK(dns_rbtnode_t) deadlink;
+
+       /*@{*/
+       /*!
+        * The following bitfields add up to a total bitwidth of 32.
+        * The range of values necessary for each item is indicated,
+        * but in the case of "attributes" the field is wider to accomodate
+        * possible future expansion.  "offsetlen" could be one bit
+        * narrower by always adjusting its value by 1 to find the real
+        * offsetlen, but doing so does not gain anything (except perhaps
+        * another bit for "attributes", which doesn't yet need any more).
+        *
+        * In each case below the "range" indicated is what's _necessary_ for
+        * the bitfield to hold, not what it actually _can_ hold.
+        */
+       unsigned int is_root : 1;       /*%< range is 0..1 */
+       unsigned int color : 1;         /*%< range is 0..1 */
+       unsigned int find_callback : 1; /*%< range is 0..1 */
+       unsigned int attributes : 3;    /*%< range is 0..2 */
+       unsigned int nsec3 : 1;         /*%< range is 0..1 */
+       unsigned int namelen : 8;       /*%< range is 1..255 */
+       unsigned int offsetlen : 8;     /*%< range is 1..128 */
+       unsigned int padbytes : 9;      /*%< range is 0..380 */
+       /*@}*/
 
 #ifdef DNS_RBT_USEHASH
-        unsigned int hashval;
+       unsigned int hashval;
 #endif
 
-        /*@{*/
-        /*!
-         * These values are used in the RBT DB implementation.  The appropriate
-         * node lock must be held before accessing them.
-         */
-        void *data;
-        unsigned int dirty:1;
-        unsigned int wild:1;
-        unsigned int locknum:DNS_RBT_LOCKLENGTH;
+       /*@{*/
+       /*!
+        * These values are used in the RBT DB implementation.  The appropriate
+        * node lock must be held before accessing them.
+        */
+       void *data;
+       unsigned int dirty:1;
+       unsigned int wild:1;
+       unsigned int locknum:DNS_RBT_LOCKLENGTH;
 #ifndef DNS_RBT_USEISCREFCOUNT
-        unsigned int references:DNS_RBT_REFLENGTH;
+       unsigned int references:DNS_RBT_REFLENGTH;
 #else
-        isc_refcount_t references; /* note that this is not in the bitfield */
+       isc_refcount_t references; /* note that this is not in the bitfield */
 #endif
-        /*@}*/
+       /*@}*/
 };
 
 typedef isc_result_t (*dns_rbtfindcallback_t)(dns_rbtnode_t *node,
-                                              dns_name_t *name,
-                                              void *callback_arg);
+                                             dns_name_t *name,
+                                             void *callback_arg);
 
 /*****
  *****  Chain Info
@@ -191,41 +192,41 @@ typedef isc_result_t (*dns_rbtfindcallback_t)(dns_rbtnode_t *node,
 #define DNS_RBT_LEVELBLOCK 254
 
 typedef struct dns_rbtnodechain {
-        unsigned int            magic;
-        isc_mem_t *             mctx;
-        /*%
-         * The terminal node of the chain.  It is not in levels[].
-         * This is ostensibly private ... but in a pinch it could be
-         * used tell that the chain points nowhere without needing to
-         * call dns_rbtnodechain_current().
-         */
-        dns_rbtnode_t *         end;
-        /*%
-         * The maximum number of labels in a name is 128; bitstrings mean
-         * a conceptually very large number (which I have not bothered to
-         * compute) of logical levels because splitting can potentially occur
-         * at each bit.  However, DNSSEC restricts the number of "logical"
-         * labels in a name to 255, meaning only 254 pointers are needed
-         * in the worst case.
-         */
-        dns_rbtnode_t *         levels[DNS_RBT_LEVELBLOCK];
-        /*%
-         * level_count indicates how deep the chain points into the
-         * tree of trees, and is the index into the levels[] array.
-         * Thus, levels[level_count - 1] is the last level node stored.
-         * A chain that points to the top level of the tree of trees has
-         * a level_count of 0, the first level has a level_count of 1, and
-         * so on.
-         */
-        unsigned int            level_count;
-        /*%
-         * level_matches tells how many levels matched above the node
-         * returned by dns_rbt_findnode().  A match (partial or exact) found
-         * in the first level thus results in level_matches being set to 1.
-         * This is used by the rbtdb to set the start point for a recursive
-         * search of superdomains until the RR it is looking for is found.
-         */
-        unsigned int            level_matches;
+       unsigned int            magic;
+       isc_mem_t *             mctx;
+       /*%
+        * The terminal node of the chain.  It is not in levels[].
+        * This is ostensibly private ... but in a pinch it could be
+        * used tell that the chain points nowhere without needing to
+        * call dns_rbtnodechain_current().
+        */
+       dns_rbtnode_t *         end;
+       /*%
+        * The maximum number of labels in a name is 128; bitstrings mean
+        * a conceptually very large number (which I have not bothered to
+        * compute) of logical levels because splitting can potentially occur
+        * at each bit.  However, DNSSEC restricts the number of "logical"
+        * labels in a name to 255, meaning only 254 pointers are needed
+        * in the worst case.
+        */
+       dns_rbtnode_t *         levels[DNS_RBT_LEVELBLOCK];
+       /*%
+        * level_count indicates how deep the chain points into the
+        * tree of trees, and is the index into the levels[] array.
+        * Thus, levels[level_count - 1] is the last level node stored.
+        * A chain that points to the top level of the tree of trees has
+        * a level_count of 0, the first level has a level_count of 1, and
+        * so on.
+        */
+       unsigned int            level_count;
+       /*%
+        * level_matches tells how many levels matched above the node
+        * returned by dns_rbt_findnode().  A match (partial or exact) found
+        * in the first level thus results in level_matches being set to 1.
+        * This is used by the rbtdb to set the start point for a recursive
+        * search of superdomains until the RR it is looking for is found.
+        */
+       unsigned int            level_matches;
 } dns_rbtnodechain_t;
 
 /*****
@@ -233,7 +234,7 @@ typedef struct dns_rbtnodechain {
  *****/
 isc_result_t
 dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
-               void *deleter_arg, dns_rbt_t **rbtp);
+              void *deleter_arg, dns_rbt_t **rbtp);
 /*%<
  * Initialize a red-black tree of trees.
  *
@@ -337,7 +338,7 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep);
 
 isc_result_t
 dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
-                 dns_name_t *foundname, void **data);
+                dns_name_t *foundname, void **data);
 /*%<
  * Get the data pointer associated with 'name'.
  *
@@ -376,9 +377,9 @@ dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
 
 isc_result_t
 dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
-                 dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
-                 unsigned int options, dns_rbtfindcallback_t callback,
-                 void *callback_arg);
+                dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
+                unsigned int options, dns_rbtfindcallback_t callback,
+                void *callback_arg);
 /*%<
  * Find the node for 'name'.
  *
@@ -604,7 +605,7 @@ dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name);
 
 char *
 dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname,
-                       unsigned int size);
+                      unsigned int size);
 /*%<
  * Format the full name of a node for printing, using dns_name_format().
  *
@@ -717,7 +718,7 @@ dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain);
 
 isc_result_t
 dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name,
-                         dns_name_t *origin, dns_rbtnode_t **node);
+                        dns_name_t *origin, dns_rbtnode_t **node);
 /*%<
  * Provide the name, origin and node to which the chain is currently pointed.
  *
@@ -757,7 +758,7 @@ dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name,
 
 isc_result_t
 dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
-                       dns_name_t *name, dns_name_t *origin);
+                      dns_name_t *name, dns_name_t *origin);
 /*%<
  * Set the chain to the lexically first node in the tree of trees.
  *
@@ -783,7 +784,7 @@ dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
 
 isc_result_t
 dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
-                       dns_name_t *name, dns_name_t *origin);
+                      dns_name_t *name, dns_name_t *origin);
 /*%<
  * Set the chain to the lexically last node in the tree of trees.
  *
@@ -805,7 +806,7 @@ dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
 
 isc_result_t
 dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
-                      dns_name_t *origin);
+                     dns_name_t *origin);
 /*%<
  * Adjusts chain to point the DNSSEC predecessor of the name to which it
  * is currently pointed.
@@ -835,7 +836,7 @@ dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
 
 isc_result_t
 dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
-                      dns_name_t *origin);
+                     dns_name_t *origin);
 /*%<
  * Adjusts chain to point the DNSSEC successor of the name to which it
  * is currently pointed.
@@ -863,6 +864,19 @@ dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
  *\li   &lt;something_else>     Any error result from dns_name_concatenate.
  */
 
+isc_result_t
+dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name,
+                     dns_name_t *origin);
+/*%<
+ * Decend down if possible.
+ */
+
+isc_result_t
+dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name);
+/*%<
+ * Find the next node at the current depth in DNSSEC order.
+ */
+
 /*
  * Wrapper macros for manipulating the rbtnode reference counter:
  *   Since we selectively use isc_refcount_t for the reference counter of
@@ -872,52 +886,52 @@ dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
  */
 #ifdef DNS_RBT_USEISCREFCOUNT
 #define dns_rbtnode_refinit(node, n)                            \
-        do {                                                    \
-                isc_refcount_init(&(node)->references, (n));    \
-        } while (0)
+       do {                                                    \
+               isc_refcount_init(&(node)->references, (n));    \
+       } while (0)
 #define dns_rbtnode_refdestroy(node)                            \
-        do {                                                    \
-                isc_refcount_destroy(&(node)->references);      \
-        } while (0)
+       do {                                                    \
+               isc_refcount_destroy(&(node)->references);      \
+       } while (0)
 #define dns_rbtnode_refcurrent(node)                            \
-        isc_refcount_current(&(node)->references)
+       isc_refcount_current(&(node)->references)
 #define dns_rbtnode_refincrement0(node, refs)                   \
-        do {                                                    \
-                isc_refcount_increment0(&(node)->references, (refs)); \
-        } while (0)
+       do {                                                    \
+               isc_refcount_increment0(&(node)->references, (refs)); \
+       } while (0)
 #define dns_rbtnode_refincrement(node, refs)                    \
-        do {                                                    \
-                isc_refcount_increment(&(node)->references, (refs)); \
-        } while (0)
+       do {                                                    \
+               isc_refcount_increment(&(node)->references, (refs)); \
+       } while (0)
 #define dns_rbtnode_refdecrement(node, refs)                    \
-        do {                                                    \
-                isc_refcount_decrement(&(node)->references, (refs)); \
-        } while (0)
+       do {                                                    \
+               isc_refcount_decrement(&(node)->references, (refs)); \
+       } while (0)
 #else  /* DNS_RBT_USEISCREFCOUNT */
 #define dns_rbtnode_refinit(node, n)    ((node)->references = (n))
 #define dns_rbtnode_refdestroy(node)    (REQUIRE((node)->references == 0))
 #define dns_rbtnode_refcurrent(node)    ((node)->references)
 #define dns_rbtnode_refincrement0(node, refs)                   \
-        do {                                                    \
-                unsigned int *_tmp = (unsigned int *)(refs);    \
-                (node)->references++;                           \
-                if ((_tmp) != NULL)                             \
-                        (*_tmp) = (node)->references;           \
-        } while (0)
+       do {                                                    \
+               unsigned int *_tmp = (unsigned int *)(refs);    \
+               (node)->references++;                           \
+               if ((_tmp) != NULL)                             \
+                       (*_tmp) = (node)->references;           \
+       } while (0)
 #define dns_rbtnode_refincrement(node, refs)                    \
-        do {                                                    \
-                REQUIRE((node)->references > 0);                \
-                (node)->references++;                           \
-                if ((refs) != NULL)                             \
-                        (*refs) = (node)->references;           \
-        } while (0)
+       do {                                                    \
+               REQUIRE((node)->references > 0);                \
+               (node)->references++;                           \
+               if ((refs) != NULL)                             \
+                       (*refs) = (node)->references;           \
+       } while (0)
 #define dns_rbtnode_refdecrement(node, refs)                    \
-        do {                                                    \
-                REQUIRE((node)->references > 0);                \
-                (node)->references--;                           \
-                if ((refs) != NULL)                             \
-                        (*refs) = (node)->references;           \
-        } while (0)
+       do {                                                    \
+               REQUIRE((node)->references > 0);                \
+               (node)->references--;                           \
+               if ((refs) != NULL)                             \
+                       (*refs) = (node)->references;           \
+       } while (0)
 #endif /* DNS_RBT_USEISCREFCOUNT */
 
 ISC_LANG_ENDDECLS
index 57299d90a2f108c498c3810622b389ebe71caf9b..b72aa617a0563dd855ae659ebb7c562f89ed8777 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rcode.h,v 1.19 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: rcode.h,v 1.20 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_RCODE_H
 #define DNS_RCODE_H 1
@@ -93,6 +93,21 @@ isc_result_t dns_tsigrcode_totext(dns_rcode_t rcode, isc_buffer_t *target);
  *\li  #ISC_R_NOSPACE                  target buffer is too small
  */
 
+isc_result_t
+dns_hashalg_fromtext(unsigned char *hashalg, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a has algorithm value.
+ *
+ * Requires:
+ *\li  'hashalg' is a valid pointer.
+ *
+ *\li  'source' is a valid text region.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS                  on success
+ *\li  #DNS_R_UNKNOWN                  type is unknown
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_RCODE_H */
index 1ca8ea6b313d6d7fa597c6935fe3e54e2dff1db0..bfb6cd405b166c6973f90263f7d6509adb5199a3 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdataset.h,v 1.64 2008/04/01 23:47:10 tbox Exp $ */
+/* $Id: rdataset.h,v 1.65 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_RDATASET_H
 #define DNS_RDATASET_H 1
@@ -78,8 +78,14 @@ typedef struct dns_rdatasetmethods {
                                              dns_name_t *name);
        isc_result_t            (*getnoqname)(dns_rdataset_t *rdataset,
                                              dns_name_t *name,
-                                             dns_rdataset_t *nsec,
-                                             dns_rdataset_t *nsecsig);
+                                             dns_rdataset_t *neg,
+                                             dns_rdataset_t *negsig);
+       isc_result_t            (*addclosest)(dns_rdataset_t *rdataset,
+                                             dns_name_t *name);
+       isc_result_t            (*getclosest)(dns_rdataset_t *rdataset,
+                                             dns_name_t *name,
+                                             dns_rdataset_t *neg,
+                                             dns_rdataset_t *negsig);
        isc_result_t            (*getadditional)(dns_rdataset_t *rdataset,
                                                 dns_rdatasetadditional_t type,
                                                 dns_rdatatype_t qtype,
@@ -156,6 +162,7 @@ struct dns_rdataset {
        unsigned int                    privateuint4;
        void *                          private5;
        void *                          private6;
+       void *                          private7;
        /*@}*/
 
 };
@@ -191,6 +198,8 @@ struct dns_rdataset {
 #define DNS_RDATASETATTR_REQUIREDGLUE  0x00010000
 #define DNS_RDATASETATTR_LOADORDER     0x00020000
 #define DNS_RDATASETATTR_RESIGN                0x00040000
+#define DNS_RDATASETATTR_CLOSEST       0x00080000
+#define DNS_RDATASETATTR_OPTOUT                0x00100000      /*%< OPTOUT proof */
 
 /*%
  * _OMITDNSSEC:
@@ -484,14 +493,14 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
 
 isc_result_t
 dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
-                       dns_rdataset_t *nsec, dns_rdataset_t *nsecsig);
+                       dns_rdataset_t *neg, dns_rdataset_t *negsig);
 /*%<
  * Return the noqname proof for this record.
  *
  * Requires:
  *\li  'rdataset' to be valid and #DNS_RDATASETATTR_NOQNAME to be set.
  *\li  'name' to be valid.
- *\li  'nsec' and 'nsecsig' to be valid and not associated.
+ *\li  'neg' and 'negsig' to be valid and not associated.
  */
 
 isc_result_t
@@ -500,11 +509,37 @@ dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name);
  * Associate a noqname proof with this record.
  * Sets #DNS_RDATASETATTR_NOQNAME if successful.
  * Adjusts the 'rdataset->ttl' to minimum of the 'rdataset->ttl' and
- * the 'nsec' and 'rrsig(nsec)' ttl.
+ * the 'nsec'/'nsec3' and 'rrsig(nsec)'/'rrsig(nsec3)' ttl.
  *
  * Requires:
  *\li  'rdataset' to be valid and #DNS_RDATASETATTR_NOQNAME to be set.
- *\li  'name' to be valid and have NSEC and RRSIG(NSEC) rdatasets.
+ *\li  'name' to be valid and have NSEC or NSEC3 and associated RRSIG
+ *      rdatasets.
+ */
+
+isc_result_t
+dns_rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+                       dns_rdataset_t *nsec, dns_rdataset_t *nsecsig);
+/*%<
+ * Return the closest encloser for this record.
+ *
+ * Requires:
+ *\li  'rdataset' to be valid and #DNS_RDATASETATTR_CLOSEST to be set.
+ *\li  'name' to be valid.
+ *\li  'nsec' and 'nsecsig' to be valid and not associated.
+ */
+
+isc_result_t
+dns_rdataset_addclosest(dns_rdataset_t *rdataset, dns_name_t *name);
+/*%<
+ * Associate a closest encloset proof with this record.
+ * Sets #DNS_RDATASETATTR_CLOSEST if successful.
+ * Adjusts the 'rdataset->ttl' to minimum of the 'rdataset->ttl' and
+ * the 'nsec' and 'rrsig(nsec)' ttl.
+ *
+ * Requires:
+ *\li  'rdataset' to be valid and #DNS_RDATASETATTR_CLOSEST to be set.
+ *\li  'name' to be valid and have NSEC3 and RRSIG(NSEC3) rdatasets.
  */
 
 isc_result_t
index e4c0621a2451d1a81e277a498016e68ebabacd70..9c299bddb56152fa9e456ee4b83df47975162af4 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdatatype.h,v 1.24 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: rdatatype.h,v 1.25 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_RDATATYPE_H
 #define DNS_RDATATYPE_H 1
@@ -71,7 +71,8 @@ dns_rdatatype_format(dns_rdatatype_t rdtype,
  * The resulting string is guaranteed to be null-terminated.
  */
 
-#define DNS_RDATATYPE_FORMATSIZE sizeof("TYPE65535")
+#define DNS_RDATATYPE_FORMATSIZE sizeof("NSEC3PARAM")
+
 /*%<
  * Minimum size of array to pass to dns_rdatatype_format().
  * May need to be adjusted if a new RR type with a very long
index 37d61b5c6ef8191b9b050b628143209b00fa69da..c2feeabf845f69c7736c24e500c84ba5184781db 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: result.h,v 1.114 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: result.h,v 1.115 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_RESULT_H
 #define DNS_RESULT_H 1
 #define DNS_R_COVERINGNSEC             (ISC_RESULTCLASS_DNS + 101)
 #define DNS_R_MXISADDRESS              (ISC_RESULTCLASS_DNS + 102)
 #define DNS_R_DUPLICATE                        (ISC_RESULTCLASS_DNS + 103)
+#define DNS_R_INVALIDNSEC3             (ISC_RESULTCLASS_DNS + 104)
 
-#define DNS_R_NRESULTS                 104     /*%< Number of results */
+#define DNS_R_NRESULTS                 105     /*%< Number of results */
 
 /*
  * DNS wire format rcodes.
index 705cfb091beecac08a2233631939405ed702eafb..5223397c8f738ee694e3ca4dc70f603f89f01bef 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: types.h,v 1.129 2008/04/03 05:55:52 marka Exp $ */
+/* $Id: types.h,v 1.130 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_TYPES_H
 #define DNS_TYPES_H 1
@@ -69,6 +69,7 @@ typedef struct dns_fixedname                  dns_fixedname_t;
 typedef struct dns_forwarders                  dns_forwarders_t;
 typedef struct dns_fwdtable                    dns_fwdtable_t;
 typedef struct dns_iptable                     dns_iptable_t;
+typedef isc_uint32_t                           dns_iterations_t;
 typedef isc_uint16_t                           dns_keyflags_t;
 typedef struct dns_keynode                     dns_keynode_t;
 typedef struct dns_keytable                    dns_keytable_t;
@@ -131,6 +132,10 @@ typedef struct not_defined_gss_ctx *gss_ctx_id_t;
 #endif
 typedef struct dst_gssapi_signverifyctx dst_gssapi_signverifyctx_t;
 
+typedef enum {
+       dns_hash_sha1 = 1
+} dns_hash_t;
+
 typedef enum {
        dns_fwdpolicy_none = 0,
        dns_fwdpolicy_first = 1,
index 167e24258eeaeead9701d64991cd720d1a81b0db..1909297b1827e96d5d034ee283f8af1154543dd9 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: validator.h,v 1.39 2007/09/19 03:38:56 marka Exp $ */
+/* $Id: validator.h,v 1.40 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_VALIDATOR_H
 #define DNS_VALIDATOR_H 1
@@ -99,12 +99,17 @@ typedef struct dns_validatorevent {
        /*
         * Proofs to be cached.
         */
-       dns_name_t *                    proofs[3];
+       dns_name_t *                    proofs[4];
+       /*
+        * Optout proof seen.
+        */
+       isc_boolean_t                   optout;
 } dns_validatorevent_t;
 
 #define DNS_VALIDATOR_NOQNAMEPROOF 0
 #define DNS_VALIDATOR_NODATAPROOF 1
 #define DNS_VALIDATOR_NOWILDCARDPROOF 2
+#define DNS_VALIDATOR_CLOSESTENCLOSER 3
 
 /*%
  * A validator object represents a validation in progress.
@@ -139,11 +144,14 @@ struct dns_validator {
        dns_rdataset_t *                dsset;
        dns_rdataset_t *                soaset;
        dns_rdataset_t *                nsecset;
+       dns_rdataset_t *                nsec3set;
        dns_name_t *                    soaname;
        dns_rdataset_t                  frdataset;
        dns_rdataset_t                  fsigrdataset;
        dns_fixedname_t                 fname;
        dns_fixedname_t                 wild;
+       dns_fixedname_t                 nearest;
+       dns_fixedname_t                 closest;
        ISC_LINK(dns_validator_t)       link;
        dns_rdataset_t                  dlv;
        dns_fixedname_t                 dlvsep;
index 2158cc6a437e327177b1b227194998b584489ffd..c6f4444143b704a0435792e9b68a05f784a14d86 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone.h,v 1.159 2008/04/03 05:55:52 marka Exp $ */
+/* $Id: zone.h,v 1.160 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DNS_ZONE_H
 #define DNS_ZONE_H 1
@@ -33,6 +33,7 @@
 #include <isc/rwlock.h>
 
 #include <dns/masterdump.h>
+#include <dns/rdatastruct.h>
 #include <dns/types.h>
 
 typedef enum {
@@ -68,6 +69,7 @@ typedef enum {
 #define DNS_ZONEOPT_UPDATECHECKKSK 0x00800000U /*%< check dnskey KSK flag */
 #define DNS_ZONEOPT_TRYTCPREFRESH 0x01000000U  /*%< try tcp refresh on udp failure */
 #define DNS_ZONEOPT_NOTIFYTOSOA          0x02000000U   /*%< Notify the SOA MNAME */
+#define DNS_ZONEOPT_NSEC3TESTZONE 0x04000000U  /*%< nsec3-test-zone */
 
 #ifndef NOMINUM_PUBLIC
 /*
@@ -1605,7 +1607,7 @@ dns_zone_name(dns_zone_t *zone, char *buf, size_t len);
 
 isc_result_t
 dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata);
-/*
+/*%<
  * Check if this record meets the check-names policy.
  *
  * Requires:
@@ -1621,7 +1623,7 @@ dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata);
 
 void
 dns_zone_setacache(dns_zone_t *zone, dns_acache_t *acache);
-/*
+/*%<
  *     Associate the zone with an additional cache.
  *
  * Require:
@@ -1634,7 +1636,7 @@ dns_zone_setacache(dns_zone_t *zone, dns_acache_t *acache);
 
 void
 dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx);
-/*
+/*%<
  *     Set the post load integrity callback function 'checkmx'.
  *     'checkmx' will be called if the MX is not within the zone.
  *
@@ -1644,7 +1646,7 @@ dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx);
 
 void
 dns_zone_setchecksrv(dns_zone_t *zone, dns_checkmxfunc_t checksrv);
-/*
+/*%<
  *     Set the post load integrity callback function 'checksrv'.
  *     'checksrv' will be called if the SRV TARGET is not within the zone.
  *
@@ -1654,7 +1656,7 @@ dns_zone_setchecksrv(dns_zone_t *zone, dns_checkmxfunc_t checksrv);
 
 void
 dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns);
-/*
+/*%<
  *     Set the post load integrity callback function 'checkmx'.
  *     'checkmx' will be called if the MX is not within the zone.
  *
@@ -1664,7 +1666,7 @@ dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns);
 
 void
 dns_zone_setnotifydelay(dns_zone_t *zone, isc_uint32_t delay);
-/*
+/*%<
  * Set the minimum delay between sets of notify messages.
  *
  * Requires:
@@ -1673,7 +1675,7 @@ dns_zone_setnotifydelay(dns_zone_t *zone, isc_uint32_t delay);
 
 isc_uint32_t
 dns_zone_getnotifydelay(dns_zone_t *zone);
-/*
+/*%<
  * Get the minimum delay between sets of notify messages.
  *
  * Requires:
@@ -1682,7 +1684,7 @@ dns_zone_getnotifydelay(dns_zone_t *zone);
 
 void
 dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg);
-/*
+/*%<
  * Set the isself callback function and argument.
  *
  * isc_boolean_t
@@ -1696,24 +1698,30 @@ dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg);
 
 void
 dns_zone_setnodes(dns_zone_t *zone, isc_uint32_t nodes);
-/*
+/*%<
  * Set the number of nodes that will be checked per quantum.
  */
 
 void
 dns_zone_setsignatures(dns_zone_t *zone, isc_uint32_t signatures);
-/*
+/*%<
  * Set the number of signatures that will be generated per quantum.
  */
 
 isc_result_t
 dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
-                    isc_uint16_t keyid);
-/*
+                    isc_uint16_t keyid, isc_boolean_t delete);
+/*%<
  * Initiate/resume signing of the entire zone with the zone DNSKEY(s)
  * that match the given algorithm and keyid.
  */
 
+isc_result_t
+dns_zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param);
+/*%<
+ * Incrementally add a NSEC3 chain that corresponds to 'nsec3param'.
+ */
+
 void
 dns_zone_setprivatetype(dns_zone_t *zone, dns_rdatatype_t type);
 dns_rdatatype_t
index 5d7159645e1afe1665c53007bdefcfc6813b69f8..702ad719d21b1437b941bfa534b26ab8a8eb55f6 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dst.h,v 1.11 2008/04/01 23:47:10 tbox Exp $ */
+/* $Id: dst.h,v 1.12 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef DST_DST_H
 #define DST_DST_H 1
@@ -51,6 +51,8 @@ typedef struct dst_context    dst_context_t;
 #define DST_ALG_DSA            3
 #define DST_ALG_ECC            4
 #define DST_ALG_RSASHA1                5
+#define DST_ALG_NSEC3DSA       6
+#define DST_ALG_NSEC3RSASHA1   7
 #define DST_ALG_HMACMD5                157
 #define DST_ALG_GSSAPI         160
 #define DST_ALG_HMACSHA1       161     /* XXXMPA */
index 3c4ba77fd24c1b14cb2a8f55741a90feeca393af..99d15cb371759667f8b53c58fcc5bc4857a35b37 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: journal.c,v 1.101 2008/04/01 23:47:10 tbox Exp $ */
+/* $Id: journal.c,v 1.102 2008/09/24 02:46:22 marka Exp $ */
 
 #include <config.h>
 
@@ -1887,10 +1887,10 @@ dns_db_diff(isc_mem_t *mctx,
        if (result != ISC_R_SUCCESS)
                return (result);
 
-       result = dns_db_createiterator(db[0], ISC_FALSE, &dbit[0]);
+       result = dns_db_createiterator(db[0], 0, &dbit[0]);
        if (result != ISC_R_SUCCESS)
                goto cleanup_journal;
-       result = dns_db_createiterator(db[1], ISC_FALSE, &dbit[1]);
+       result = dns_db_createiterator(db[1], 0, &dbit[1]);
        if (result != ISC_R_SUCCESS)
                goto cleanup_interator0;
 
index 2a41b883e8944a8ca33952cbef981b3a8d3b2a20..3d464582047d55fbfdf8e5e8ff86638c13490bd0 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: masterdump.c,v 1.93 2008/08/13 02:20:09 jinmei Exp $ */
+/* $Id: masterdump.c,v 1.94 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -785,6 +785,13 @@ static const char *trustnames[] = {
        "local" /* aka ultimate */
 };
 
+const char *
+dns_trust_totext(dns_trust_t trust) {
+       if (trust >= sizeof(trustnames)/sizeof(*trustnames))
+               return ("bad");
+       return (trustnames[trust]);
+}
+
 static isc_result_t
 dump_rdatasets_text(isc_mem_t *mctx, dns_name_t *name,
                    dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
@@ -1187,7 +1194,7 @@ dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
 {
        dns_dumpctx_t *dctx;
        isc_result_t result;
-       isc_boolean_t relative;
+       unsigned int options;
 
        dctx = isc_mem_get(mctx, sizeof(*dctx));
        if (dctx == NULL)
@@ -1234,10 +1241,10 @@ dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
 
        if (dctx->format == dns_masterformat_text &&
            (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) {
-               relative = ISC_TRUE;
+               options = DNS_DB_RELATIVENAMES;
        } else
-               relative = ISC_FALSE;
-       result = dns_db_createiterator(dctx->db, relative, &dctx->dbiter);
+               options = 0;
+       result = dns_db_createiterator(dctx->db, options, &dctx->dbiter);
        if (result != ISC_R_SUCCESS)
                goto cleanup;
 
index d91bccc66dc2ffeecadb71b16f2b73ba91fcef92..3420e558695123ea8bdfb15aeb9a8a4850a50b4d 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: message.c,v 1.244 2008/07/28 08:39:52 marka Exp $ */
+/* $Id: message.c,v 1.245 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -95,6 +95,8 @@ hexdump(const char *msg, const char *msg2, void *base, size_t len) {
 #define VALID_PSEUDOSECTION(s) (((s) >= DNS_PSEUDOSECTION_ANY) \
                                 && ((s) < DNS_PSEUDOSECTION_MAX))
 
+#define OPTOUT(x) (((x)->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
+
 /*%
  * This is the size of each individual scratchpad buffer, and the numbers
  * of various block allocations used within the server.
@@ -1988,6 +1990,8 @@ dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
                                    (sectionid == DNS_SECTION_ANSWER ||
                                     sectionid == DNS_SECTION_AUTHORITY))
                                        msg->flags &= ~DNS_MESSAGEFLAG_AD;
+                               if (OPTOUT(rdataset))
+                                       msg->flags &= ~DNS_MESSAGEFLAG_AD;
 
                                rdataset->attributes |=
                                        DNS_RDATASETATTR_RENDERED;
index a779e01cac2b7877bff16e1894cf1806f78ff429..b833f9154718a116f31f8094c3cb77a40aa95292 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: ncache.c,v 1.41 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: ncache.c,v 1.42 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -30,6 +30,9 @@
 #include <dns/rdata.h>
 #include <dns/rdatalist.h>
 #include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+
+#define DNS_NCACHE_RDATA 20U
 
 /*
  * The format of an ncache rdata is a sequence of one or more records of
@@ -91,6 +94,16 @@ isc_result_t
 dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
               dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
               dns_rdataset_t *addedrdataset)
+{
+       return (dns_ncache_addoptout(message, cache, node, covers, now, maxttl,
+                                   ISC_FALSE, addedrdataset));
+}
+
+isc_result_t
+dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
+                    dns_dbnode_t *node, dns_rdatatype_t covers,
+                    isc_stdtime_t now, dns_ttl_t maxttl,
+                    isc_boolean_t optout, dns_rdataset_t *addedrdataset)
 {
        isc_result_t result;
        isc_buffer_t buffer;
@@ -100,10 +113,11 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
        dns_name_t *name;
        dns_ttl_t ttl;
        dns_trust_t trust;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdata_t rdata[DNS_NCACHE_RDATA];
        dns_rdataset_t ncrdataset;
        dns_rdatalist_t ncrdatalist;
        unsigned char data[4096];
+       unsigned int next = 0;
 
        /*
         * Convert the authority data from 'message' into a negative cache
@@ -118,7 +132,17 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
         */
 
        /*
-        * First, build an ncache rdata in buffer.
+        * Initialize the list.
+        */
+       ncrdatalist.rdclass = dns_db_class(cache);
+       ncrdatalist.type = 0;
+       ncrdatalist.covers = covers;
+       ncrdatalist.ttl = maxttl;
+       ISC_LIST_INIT(ncrdatalist.rdata);
+       ISC_LINK_INIT(&ncrdatalist, link);
+
+       /*
+        * Build an ncache rdatas into buffer.
         */
        ttl = maxttl;
        trust = 0xffff;
@@ -142,7 +166,8 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                                if (type == dns_rdatatype_rrsig)
                                        type = rdataset->covers;
                                if (type == dns_rdatatype_soa ||
-                                   type == dns_rdatatype_nsec) {
+                                   type == dns_rdatatype_nsec ||
+                                   type == dns_rdatatype_nsec3) {
                                        if (ttl > rdataset->ttl)
                                                ttl = rdataset->ttl;
                                        if (trust > rdataset->trust)
@@ -171,6 +196,21 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                                                               &buffer);
                                        if (result != ISC_R_SUCCESS)
                                                return (result);
+
+                                       if (next >= DNS_NCACHE_RDATA)
+                                               return (ISC_R_NOSPACE);
+                                       dns_rdata_init(&rdata[next]);
+                                       isc_buffer_remainingregion(&buffer, &r);
+                                       rdata[next].data = r.base;
+                                       rdata[next].length = r.length;
+                                       rdata[next].rdclass =
+                                               ncrdatalist.rdclass;
+                                       rdata[next].type = 0;
+                                       rdata[next].flags = 0;
+                                       ISC_LIST_APPEND(ncrdatalist.rdata,
+                                                       &rdata[next], link);
+                                       isc_buffer_forward(&buffer, r.length);
+                                       next++;
                                }
                        }
                }
@@ -226,27 +266,24 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                        trust = dns_trust_authauthority;
                } else
                        trust = dns_trust_additional;
+               /*
+                * Now add it to the cache.
+                */
+               if (next >= DNS_NCACHE_RDATA)
+                       return (ISC_R_NOSPACE);
+               dns_rdata_init(&rdata[next]);
+               isc_buffer_remainingregion(&buffer, &r);
+               rdata[next].data = r.base;
+               rdata[next].length = r.length;
+               rdata[next].rdclass = ncrdatalist.rdclass;
+               rdata[next].type = 0;
+               rdata[next].flags = 0;
+               ISC_LIST_APPEND(ncrdatalist.rdata, &rdata[next], link);
        }
 
-       /*
-        * Now add it to the cache.
-        */
        INSIST(trust != 0xffff);
-       isc_buffer_usedregion(&buffer, &r);
-       rdata.data = r.base;
-       rdata.length = r.length;
-       rdata.rdclass = dns_db_class(cache);
-       rdata.type = 0;
-       rdata.flags = 0;
-
-       ncrdatalist.rdclass = rdata.rdclass;
-       ncrdatalist.type = 0;
-       ncrdatalist.covers = covers;
-       ncrdatalist.ttl = ttl;
-       ISC_LIST_INIT(ncrdatalist.rdata);
-       ISC_LINK_INIT(&ncrdatalist, link);
 
-       ISC_LIST_APPEND(ncrdatalist.rdata, &rdata, link);
+       ncrdatalist.ttl = ttl;
 
        dns_rdataset_init(&ncrdataset);
        RUNTIME_CHECK(dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset)
@@ -254,6 +291,8 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
        ncrdataset.trust = trust;
        if (message->rcode == dns_rcode_nxdomain)
                ncrdataset.attributes |= DNS_RDATASETATTR_NXDOMAIN;
+       if (optout)
+               ncrdataset.attributes |= DNS_RDATASETATTR_OPTOUT;
 
        return (dns_db_addrdataset(cache, node, NULL, now, &ncrdataset,
                                   0, addedrdataset));
@@ -281,18 +320,14 @@ dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
        REQUIRE(rdataset != NULL);
        REQUIRE(rdataset->type == 0);
 
-       result = dns_rdataset_first(rdataset);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-       dns_rdataset_current(rdataset, &rdata);
-       INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
-       isc_buffer_init(&source, rdata.data, rdata.length);
-       isc_buffer_add(&source, rdata.length);
-
        savedbuffer = *target;
-
        count = 0;
-       do {
+
+       result = dns_rdataset_first(rdataset);
+       while (result == ISC_R_SUCCESS) {
+               dns_rdataset_current(rdataset, &rdata);
+               isc_buffer_init(&source, rdata.data, rdata.length);
+               isc_buffer_add(&source, rdata.length);
                dns_name_init(&name, NULL);
                isc_buffer_remainingregion(&source, &remaining);
                dns_name_fromregion(&name, &remaining);
@@ -370,8 +405,12 @@ dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
 
                        count++;
                }
-               isc_buffer_remainingregion(&source, &remaining);
-       } while (remaining.length > 0);
+               INSIST(isc_buffer_remaininglength(&source) == 0);
+               result = dns_rdataset_next(rdataset);
+               dns_rdata_reset(&rdata);
+       }
+       if (result != ISC_R_NOMORE)
+               goto rollback;
 
        *countp = count;
 
@@ -478,6 +517,8 @@ static dns_rdatasetmethods_t rdataset_methods = {
        NULL,
        NULL,
        NULL,
+       NULL,
+       NULL,
        NULL
 };
 
@@ -491,8 +532,6 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
        isc_buffer_t source;
        dns_name_t tname;
        dns_rdatatype_t ttype;
-       unsigned int i, rcount;
-       isc_uint16_t length;
 
        REQUIRE(ncacherdataset != NULL);
        REQUIRE(ncacherdataset->type == 0);
@@ -501,14 +540,10 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
        REQUIRE(type != dns_rdatatype_rrsig);
 
        result = dns_rdataset_first(ncacherdataset);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-       dns_rdataset_current(ncacherdataset, &rdata);
-       INSIST(dns_rdataset_next(ncacherdataset) == ISC_R_NOMORE);
-       isc_buffer_init(&source, rdata.data, rdata.length);
-       isc_buffer_add(&source, rdata.length);
-
-       do {
+       while (result == ISC_R_SUCCESS) {
+               dns_rdataset_current(ncacherdataset, &rdata);
+               isc_buffer_init(&source, rdata.data, rdata.length);
+               isc_buffer_add(&source, rdata.length);
                dns_name_init(&tname, NULL);
                isc_buffer_remainingregion(&source, &remaining);
                dns_name_fromregion(&tname, &remaining);
@@ -523,21 +558,15 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
                        isc_buffer_remainingregion(&source, &remaining);
                        break;
                }
-
-               rcount = isc_buffer_getuint16(&source);
-               for (i = 0; i < rcount; i++) {
-                       isc_buffer_remainingregion(&source, &remaining);
-                       INSIST(remaining.length >= 2);
-                       length = isc_buffer_getuint16(&source);
-                       isc_buffer_remainingregion(&source, &remaining);
-                       INSIST(remaining.length >= length);
-                       isc_buffer_forward(&source, length);
-               }
-               isc_buffer_remainingregion(&source, &remaining);
-       } while (remaining.length > 0);
-
-       if (remaining.length == 0)
+               result = dns_rdataset_next(ncacherdataset);
+               dns_rdata_reset(&rdata);
+       }
+       if (result == ISC_R_NOMORE)
                return (ISC_R_NOTFOUND);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       INSIST(remaining.length != 0);
 
        rdataset->methods = &rdataset_methods;
        rdataset->rdclass = ncacherdataset->rdclass;
@@ -555,5 +584,75 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
         */
        rdataset->privateuint4 = 0;
        rdataset->private5 = NULL;
+       rdataset->private6 = NULL;
        return (ISC_R_SUCCESS);
 }
+
+void
+dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
+                  dns_rdataset_t *rdataset)
+{
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       isc_region_t remaining, sigregion;
+       isc_buffer_t source;
+       dns_name_t tname;
+       dns_rdatatype_t type;
+       unsigned int count;
+       dns_rdata_rrsig_t rrsig;
+       unsigned char *raw;
+
+       REQUIRE(ncacherdataset != NULL);
+       REQUIRE(ncacherdataset->type == 0);
+       REQUIRE(found != NULL);
+       REQUIRE(!dns_rdataset_isassociated(rdataset));
+
+       dns_rdataset_current(ncacherdataset, &rdata);
+       isc_buffer_init(&source, rdata.data, rdata.length);
+       isc_buffer_add(&source, rdata.length);
+
+       dns_name_init(&tname, NULL);
+       isc_buffer_remainingregion(&source, &remaining);
+       dns_name_fromregion(found, &remaining);
+       INSIST(remaining.length >= found->length);
+       isc_buffer_forward(&source, found->length);
+       remaining.length -= found->length;
+
+       INSIST(remaining.length >= 4);
+       type = isc_buffer_getuint16(&source);
+       isc_buffer_remainingregion(&source, &remaining);
+
+       rdataset->methods = &rdataset_methods;
+       rdataset->rdclass = ncacherdataset->rdclass;
+       rdataset->type = type;
+       if (type == dns_rdatatype_rrsig) {
+               /*
+                * Extract covers from RRSIG.
+                */
+               raw = remaining.base;
+               count = raw[0] * 256 + raw[1];
+               INSIST(count > 0);
+               raw += 2;
+               sigregion.length = raw[0] * 256 + raw[1];
+               raw += 2;
+               sigregion.base = raw;
+               dns_rdata_reset(&rdata);
+               dns_rdata_fromregion(&rdata, rdataset->rdclass,
+                                    rdataset->type, &sigregion);
+               (void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
+               rdataset->covers = rrsig.covered;
+       } else
+               rdataset->covers = 0;
+       rdataset->ttl = ncacherdataset->ttl;
+       rdataset->trust = ncacherdataset->trust;
+       rdataset->private1 = NULL;
+       rdataset->private2 = NULL;
+
+       rdataset->private3 = remaining.base;
+
+       /*
+        * Reset iterator state.
+        */
+       rdataset->privateuint4 = 0;
+       rdataset->private5 = NULL;
+       rdataset->private6 = NULL;
+}
index 198690bf7a84d4a45a1f09a9de80a3b7b2a2f087..ad26015a29a9c12995ac25ca1b0837f2d94f81f4 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: nsec.c,v 1.9 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: nsec.c,v 1.10 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -33,6 +33,8 @@
 #include <dns/rdatastruct.h>
 #include <dns/result.h>
 
+#include <dst/dst.h>
+
 #define RETERR(x) do { \
        result = (x); \
        if (result != ISC_R_SUCCESS) \
@@ -88,6 +90,7 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version,
         */
        bm = r.base + r.length + 512;
        nsec_bits = r.base + r.length;
+       set_bit(bm, dns_rdatatype_rrsig, 1);
        set_bit(bm, dns_rdatatype_nsec, 1);
        max_type = dns_rdatatype_nsec;
        dns_rdataset_init(&rdataset);
@@ -100,7 +103,9 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version,
             result = dns_rdatasetiter_next(rdsiter))
        {
                dns_rdatasetiter_current(rdsiter, &rdataset);
-               if (rdataset.type != dns_rdatatype_nsec) {
+               if (rdataset.type != dns_rdatatype_nsec &&
+                   rdataset.type != dns_rdatatype_nsec3 &&
+                   rdataset.type != dns_rdatatype_rrsig) {
                        if (rdataset.type > max_type)
                                max_type = rdataset.type;
                        set_bit(bm, rdataset.type, 1);
@@ -197,7 +202,7 @@ dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type) {
        /* This should never fail */
        result = dns_rdata_tostruct(nsec, &nsecstruct, NULL);
        INSIST(result == ISC_R_SUCCESS);
-       
+
        present = ISC_FALSE;
        for (i = 0; i < nsecstruct.len; i += len) {
                INSIST(i + 2 <= nsecstruct.len);
@@ -218,3 +223,55 @@ dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type) {
        dns_rdata_freestruct(&nsec);
        return (present);
 }
+
+isc_result_t
+dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version,
+                 isc_boolean_t *answer)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdataset_t rdataset;
+       dns_rdata_dnskey_t dnskey;
+       isc_result_t result;
+
+       REQUIRE(answer != NULL);
+
+       dns_rdataset_init(&rdataset);
+
+       result = dns_db_getoriginnode(db, &node);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       
+       result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey,
+                                    0, 0, &rdataset, NULL);
+       dns_db_detachnode(db, &node);
+
+       if (result == ISC_R_NOTFOUND) {
+               *answer = ISC_FALSE;
+               return (ISC_R_SUCCESS);
+       }
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+
+               dns_rdataset_current(&rdataset, &rdata);
+               result = dns_rdata_tostruct(&rdata, &dnskey, NULL);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+               if (dnskey.algorithm == DST_ALG_RSAMD5 ||
+                   dnskey.algorithm == DST_ALG_RSASHA1 ||
+                   dnskey.algorithm == DST_ALG_DSA ||
+                   dnskey.algorithm == DST_ALG_ECC)
+                       break;
+       }
+       dns_rdataset_disassociate(&rdataset);
+       if (result == ISC_R_SUCCESS)
+               *answer = ISC_TRUE;
+       if (result == ISC_R_NOMORE) {
+               *answer = ISC_FALSE;
+               result = ISC_R_SUCCESS;
+       }
+       return (result);
+}
diff --git a/lib/dns/nsec3.c b/lib/dns/nsec3.c
new file mode 100644 (file)
index 0000000..8f08537
--- /dev/null
@@ -0,0 +1,1375 @@
+/*
+ * Copyright (C) 2006  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: nsec3.c,v 1.2 2008/09/24 02:46:22 marka Exp $ */
+
+#include <config.h>
+
+#include <isc/base32.h>
+#include <isc/buffer.h>
+#include <isc/hex.h>
+#include <isc/iterated_hash.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/dst.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/fixedname.h>
+#include <dns/nsec3.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+
+#define CHECK(x) do { \
+       result = (x); \
+       if (result != ISC_R_SUCCESS) \
+               goto failure; \
+       } while (0)
+
+#define OPTOUT(x) (((x) & DNS_NSEC3FLAG_OPTOUT) != 0)
+#define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0)
+#define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0)
+
+static void
+set_bit(unsigned char *array, unsigned int index, unsigned int bit) {
+       unsigned int shift, mask;
+
+       shift = 7 - (index % 8);
+       mask = 1 << shift;
+
+       if (bit != 0)
+               array[index / 8] |= mask;
+       else
+               array[index / 8] &= (~mask & 0xFF);
+}
+
+static unsigned int
+bit_isset(unsigned char *array, unsigned int index) {
+       unsigned int byte, shift, mask;
+
+       byte = array[index / 8];
+       shift = 7 - (index % 8);
+       mask = 1 << shift;
+
+       return ((byte & mask) != 0);
+}
+
+isc_result_t
+dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version,
+                    dns_dbnode_t *node, unsigned int hashalg,
+                    unsigned int flags, unsigned int iterations,
+                    const unsigned char *salt, size_t salt_length,
+                    const unsigned char *nexthash, size_t hash_length,
+                    unsigned char *buffer, dns_rdata_t *rdata)
+{
+       isc_result_t result;
+       dns_rdataset_t rdataset;
+       isc_region_t r;
+       unsigned int i, window;
+       int octet;
+       isc_boolean_t found;
+
+       unsigned char *nsec_bits, *bm;
+       unsigned int max_type;
+       dns_rdatasetiter_t *rdsiter;
+       unsigned char *p;
+
+       REQUIRE(salt_length < 256);
+       REQUIRE(hash_length < 256);
+       REQUIRE(flags <= 0xffU);
+       REQUIRE(hashalg <= 0xffU);
+       REQUIRE(iterations <= 0xffffU);
+
+       switch (hashalg) {
+       case dns_hash_sha1:
+               REQUIRE(hash_length == ISC_SHA1_DIGESTLENGTH); 
+               break;
+       }
+
+       memset(buffer, 0, DNS_NSEC3_BUFFERSIZE);
+
+       p = buffer;
+
+       *p++ = hashalg;
+       *p++ = flags;
+
+       *p++ = iterations >> 8;
+       *p++ = iterations;
+
+       *p++ = salt_length;
+       memcpy(p, salt, salt_length);
+       p += salt_length;
+
+       *p++ = hash_length;
+       memcpy(p, nexthash, hash_length);
+       p += hash_length;
+
+       r.length = p - buffer;
+       r.base = buffer;
+
+       /*
+        * Use the end of the space for a raw bitmap leaving enough
+        * space for the window identifiers and length octets.
+        */
+       bm = r.base + r.length + 512;
+       nsec_bits = r.base + r.length;
+       max_type = 0;
+       if (node == NULL)
+               goto collapse_bitmap;
+       dns_rdataset_init(&rdataset);
+       rdsiter = NULL;
+       result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       found = ISC_FALSE;
+       for (result = dns_rdatasetiter_first(rdsiter);
+            result == ISC_R_SUCCESS;
+            result = dns_rdatasetiter_next(rdsiter))
+       {
+               dns_rdatasetiter_current(rdsiter, &rdataset);
+               if (rdataset.type != dns_rdatatype_nsec &&
+                   rdataset.type != dns_rdatatype_nsec3 &&
+                   rdataset.type != dns_rdatatype_rrsig) {
+                       if (rdataset.type > max_type)
+                               max_type = rdataset.type;
+                       set_bit(bm, rdataset.type, 1);
+                       found = ISC_TRUE;
+               }
+               dns_rdataset_disassociate(&rdataset);
+       }
+       if (found) {
+               if (dns_rdatatype_rrsig > max_type)
+                       max_type = dns_rdatatype_rrsig;
+               set_bit(bm, dns_rdatatype_rrsig, 1);
+       }
+
+       /*
+        * At zone cuts, deny the existence of glue in the parent zone.
+        */
+       if (bit_isset(bm, dns_rdatatype_ns) &&
+           ! bit_isset(bm, dns_rdatatype_soa)) {
+               for (i = 0; i <= max_type; i++) {
+                       if (bit_isset(bm, i) &&
+                           ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i))
+                               set_bit(bm, i, 0);
+               }
+       }
+
+       dns_rdatasetiter_destroy(&rdsiter);
+       if (result != ISC_R_NOMORE)
+               return (result);
+
+ collapse_bitmap:
+       for (window = 0; window < 256; window++) {
+               if (window * 256 > max_type)
+                       break;
+               for (octet = 31; octet >= 0; octet--)
+                       if (bm[window * 32 + octet] != 0)
+                               break;
+               if (octet < 0)
+                       continue;
+               nsec_bits[0] = window;
+               nsec_bits[1] = octet + 1;
+               /*
+                * Note: potentially overlapping move.
+                */
+               memmove(&nsec_bits[2], &bm[window * 32], octet + 1);
+               nsec_bits += 3 + octet;
+       }
+       r.length = nsec_bits - r.base;
+       INSIST(r.length <= DNS_NSEC3_BUFFERSIZE);
+       dns_rdata_fromregion(rdata, dns_db_class(db), dns_rdatatype_nsec3, &r);
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_boolean_t
+dns_nsec3_typepresent(dns_rdata_t *rdata, dns_rdatatype_t type) {
+       dns_rdata_nsec3_t nsec3;
+       isc_result_t result;
+       isc_boolean_t present;
+       unsigned int i, len, window;
+
+       REQUIRE(rdata != NULL);
+       REQUIRE(rdata->type == dns_rdatatype_nsec3);
+
+       /* This should never fail */
+       result = dns_rdata_tostruct(rdata, &nsec3, NULL);
+       INSIST(result == ISC_R_SUCCESS);
+       
+       present = ISC_FALSE;
+       for (i = 0; i < nsec3.len; i += len) {
+               INSIST(i + 2 <= nsec3.len);
+               window = nsec3.typebits[i];
+               len = nsec3.typebits[i + 1];
+               INSIST(len > 0 && len <= 32);
+               i += 2;
+               INSIST(i + len <= nsec3.len);
+               if (window * 256 > type)
+                       break;
+               if ((window + 1) * 256 <= type)
+                       continue;
+               if (type < (window * 256) + len * 8)
+                       present = ISC_TF(bit_isset(&nsec3.typebits[i],
+                                                  type % 256));
+               break;
+       }
+       dns_rdata_freestruct(&nsec3);
+       return (present);
+}
+
+isc_result_t
+dns_nsec3_hashname(dns_fixedname_t *result,
+                  unsigned char rethash[NSEC3_MAX_HASH_LENGTH],
+                  size_t *hash_length, dns_name_t *name, dns_name_t *origin,
+                  dns_hash_t hashalg, unsigned int iterations,
+                  const unsigned char *salt, size_t saltlength)
+{
+       unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+       unsigned char nametext[DNS_NAME_FORMATSIZE];
+       dns_fixedname_t fixed;
+       dns_name_t *downcased;
+       isc_buffer_t namebuffer;
+       isc_region_t region;
+       size_t len;
+
+       if (rethash == NULL)
+               rethash = hash;
+
+       memset(rethash, 0, NSEC3_MAX_HASH_LENGTH);
+
+       dns_fixedname_init(&fixed);
+       downcased = dns_fixedname_name(&fixed);
+       dns_name_downcase(name, downcased, NULL);
+
+       /* hash the node name */
+       len = isc_iterated_hash(rethash, hashalg, iterations, salt, saltlength,
+                               downcased->ndata, downcased->length);
+       if (len == 0)
+               return (DNS_R_BADALG);
+
+       if (hash_length != NULL)
+               *hash_length = len;
+
+       /* convert the hash to base32hex */
+       region.base = rethash;
+       region.length = len;
+       isc_buffer_init(&namebuffer, nametext, sizeof nametext);
+       isc_base32hex_totext(&region, 1, "", &namebuffer);
+
+       /* convert the hex to a domain name */
+       dns_fixedname_init(result);
+       return (dns_name_fromtext(dns_fixedname_name(result), &namebuffer,
+                                 origin, 0, NULL));
+}
+
+unsigned int
+dns_nsec3_hashlength(dns_hash_t hash) {
+
+       switch (hash) {
+       case dns_hash_sha1: return(ISC_SHA1_DIGESTLENGTH);
+       }
+       return (0);
+}
+
+isc_boolean_t
+dns_nsec3_supportedhash(dns_hash_t hash) {
+       switch (hash) {
+       case dns_hash_sha1: return (ISC_TRUE);
+       }
+       return (ISC_FALSE);
+}
+
+/*%
+ * Update a single RR in version 'ver' of 'db' and log the
+ * update in 'diff'.
+ *
+ * Ensures:
+ * \li  '*tuple' == NULL.  Either the tuple is freed, or its
+ *      ownership has been transferred to the diff.
+ */
+static isc_result_t
+do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
+             dns_diff_t *diff)
+{
+       dns_diff_t temp_diff;
+       isc_result_t result;
+
+       /*
+        * Create a singleton diff.
+        */
+       dns_diff_init(diff->mctx, &temp_diff);
+       temp_diff.resign = diff->resign;
+       ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
+
+       /*
+        * Apply it to the database.
+        */
+       result = dns_diff_apply(&temp_diff, db, ver);
+       ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
+       if (result != ISC_R_SUCCESS) {
+               dns_difftuple_free(tuple);
+               return (result);
+       }
+
+       /*
+        * Merge it into the current pending journal entry.
+        */
+       dns_diff_appendminimal(diff, tuple);
+
+       /*
+        * Do not clear temp_diff.
+        */
+       return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Set '*exists' to true iff the given name exists, to false otherwise.
+ */
+static isc_result_t
+name_exists(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+            isc_boolean_t *exists)
+{
+        isc_result_t result;
+       dns_dbnode_t *node = NULL;
+       dns_rdatasetiter_t *iter = NULL;
+
+       result = dns_db_findnode(db, name, ISC_FALSE, &node);
+       if (result == ISC_R_NOTFOUND) {
+               *exists = ISC_FALSE;
+               return (ISC_R_SUCCESS);
+       }
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       result = dns_db_allrdatasets(db, node, version,
+                                    (isc_stdtime_t) 0, &iter);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_node;
+
+       result = dns_rdatasetiter_first(iter);
+       if (result == ISC_R_SUCCESS) {
+               *exists = ISC_TRUE;
+       } else if (result == ISC_R_NOMORE) {
+               *exists = ISC_FALSE;
+               result = ISC_R_SUCCESS;
+       } else
+               *exists = ISC_FALSE;
+       dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+       dns_db_detachnode(db, &node);
+       return (result);
+}
+
+static isc_boolean_t
+match_nsec3param(const dns_rdata_nsec3_t *nsec3,
+                const dns_rdata_nsec3param_t *nsec3param)
+{
+       if (nsec3->hash == nsec3param->hash &&
+           nsec3->iterations == nsec3param->iterations &&
+           nsec3->salt_length == nsec3param->salt_length &&
+           !memcmp(nsec3->salt, nsec3param->salt, nsec3->salt_length))
+               return (ISC_TRUE);
+       return (ISC_FALSE);
+}
+
+/*%
+ * Delete NSEC3 records at "name" which match "param", recording the
+ * change in "diff".
+ */
+static isc_result_t
+delete(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+       const dns_rdata_nsec3param_t *nsec3param, dns_diff_t *diff)
+{
+       dns_dbnode_t *node = NULL ;
+       dns_difftuple_t *tuple = NULL;
+       dns_rdata_nsec3_t nsec3;
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+
+       result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
+       if (result == ISC_R_NOTFOUND)
+               return (ISC_R_SUCCESS);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       dns_rdataset_init(&rdataset);
+       result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec3, 0,
+                                     (isc_stdtime_t) 0, &rdataset, NULL);
+
+       if (result == ISC_R_NOTFOUND) {
+               result = ISC_R_SUCCESS;
+               goto cleanup_node;
+       }
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_node;
+
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset))
+       {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+               dns_rdataset_current(&rdataset, &rdata);
+               CHECK(dns_rdata_tostruct(&rdata, &nsec3, NULL));
+
+               if (!match_nsec3param(&nsec3, nsec3param))
+                       continue;
+               
+               result = dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, name,
+                                             rdataset.ttl, &rdata, &tuple);
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+               result = do_one_tuple(&tuple, db, version, diff);
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+       }
+       if (result != ISC_R_NOMORE)
+               goto failure;
+       result = ISC_R_SUCCESS;
+
+ failure:
+       dns_rdataset_disassociate(&rdataset);
+ cleanup_node:
+       dns_db_detachnode(db, &node);
+
+       return (result);
+}
+
+#ifndef RFC5155_STRICT
+static isc_boolean_t
+better_param(dns_rdataset_t *nsec3paramset, dns_rdata_t *param) {
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+
+       if (REMOVE(param->data[1]))
+               return (ISC_TRUE);
+
+       dns_rdataset_init(&rdataset);
+       dns_rdataset_clone(nsec3paramset, &rdataset);
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata =  DNS_RDATA_INIT;
+               dns_rdataset_current(&rdataset, &rdata);
+               if (rdata.length != param->length)
+                       continue;
+               if (rdata.data[0] != param->data[0] ||
+                   REMOVE(rdata.data[1]) ||
+                   rdata.data[2] != param->data[2] ||
+                   rdata.data[3] != param->data[3] ||
+                   rdata.data[4] != param->data[4] ||
+                   memcmp(&rdata.data[5], &param->data[5], param->data[4]))
+                       continue;
+               if (CREATE(rdata.data[1]) && !CREATE(param->data[1])) {
+                       dns_rdataset_disassociate(&rdataset);
+                       return (ISC_TRUE);
+               }
+       }
+       dns_rdataset_disassociate(&rdataset);
+       return (ISC_FALSE);
+}
+#endif
+
+static isc_result_t
+find_nsec3(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *rdataset,
+          const dns_rdata_nsec3param_t *nsec3param)
+{
+       isc_result_t result;
+       for (result = dns_rdataset_first(rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+
+               dns_rdataset_current(rdataset, &rdata);
+               CHECK(dns_rdata_tostruct(&rdata, nsec3, NULL));
+               dns_rdata_reset(&rdata);
+               if (match_nsec3param(nsec3, nsec3param))
+                       break;
+       }
+ failure:
+       return (result);
+}
+
+isc_result_t
+dns_nsec3_addnsec3(dns_db_t *db, dns_dbversion_t *version,
+                  dns_name_t *name, const dns_rdata_nsec3param_t *nsec3param,
+                  dns_ttl_t nsecttl, isc_boolean_t unsecure, dns_diff_t *diff)
+{
+       dns_dbiterator_t *dbit = NULL;
+       dns_dbnode_t *node = NULL;
+       dns_dbnode_t *newnode = NULL;
+       dns_difftuple_t *tuple = NULL;
+       dns_fixedname_t fixed;
+       dns_fixedname_t fprev;
+       dns_hash_t hash;
+       dns_name_t *hashname;
+       dns_name_t *origin;
+       dns_name_t *prev;
+       dns_name_t empty;
+       dns_rdata_nsec3_t nsec3;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdataset_t rdataset;
+       int pass;
+       isc_boolean_t exists;
+       isc_boolean_t remove_unsecure = ISC_FALSE;
+       isc_uint8_t flags;
+       isc_buffer_t buffer;
+       isc_result_t result;
+       unsigned char *old_next;
+       unsigned char *salt;
+       unsigned char nexthash[NSEC3_MAX_HASH_LENGTH];
+       unsigned char nsec3buf[DNS_NSEC3_BUFFERSIZE];
+       unsigned int iterations;
+       unsigned int labels;
+       unsigned int next_length;
+       unsigned int old_length;
+       unsigned int salt_length;
+
+       dns_fixedname_init(&fixed);
+       hashname = dns_fixedname_name(&fixed);
+       dns_fixedname_init(&fprev);
+       prev = dns_fixedname_name(&fprev);
+
+       dns_rdataset_init(&rdataset);
+
+       origin = dns_db_origin(db);
+
+       /*
+        * Chain parameters.
+        */
+       hash = nsec3param->hash;
+       iterations = nsec3param->iterations;
+       salt_length = nsec3param->salt_length;
+       salt = nsec3param->salt;
+
+       /*
+        * Default flags for a new chain.
+        */
+       flags = nsec3param->flags & DNS_NSEC3FLAG_OPTOUT;
+
+       /*
+        * If this is the first NSEC3 in the chain nexthash will
+        * remain pointing to itself.
+        */
+       next_length = sizeof(nexthash);
+       CHECK(dns_nsec3_hashname(&fixed, nexthash, &next_length,
+                                name, origin, hash, iterations,
+                                salt, salt_length));
+
+       /*
+        * Create the node if it doesn't exist and hold
+        * a reference to it until we have added the NSEC3.
+        */
+       CHECK(dns_db_findnsec3node(db, hashname, ISC_TRUE, &newnode));
+
+       /*
+        * Seek the iterator to the 'newnode'.
+        */
+       CHECK(dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbit));
+       CHECK(dns_dbiterator_seek(dbit, hashname));
+       CHECK(dns_dbiterator_pause(dbit));
+       result = dns_db_findrdataset(db, newnode, version, dns_rdatatype_nsec3,
+                                    0, (isc_stdtime_t) 0, &rdataset, NULL);
+       /*
+        * If we updating a existing NSEC3 then find its
+        * next field.
+        */
+       if (result == ISC_R_SUCCESS) {
+               result = find_nsec3(&nsec3, &rdataset, nsec3param);
+               if (result == ISC_R_SUCCESS) {
+                       if (!CREATE(nsec3param->flags))
+                               flags = nsec3.flags;
+                       next_length = nsec3.next_length;
+                       INSIST(next_length <= sizeof(nexthash));
+                       memcpy(nexthash, nsec3.next, next_length);
+                       dns_rdataset_disassociate(&rdataset);
+                       /*
+                        * If the NSEC3 is not for a unsecure delegation then
+                        * we are just updating it.  If it is for a unsecure
+                        * delegation then we need find out if we need to
+                        * remove the NSEC3 record or not by examining the
+                        * previous NSEC3 record.
+                        */
+                       if (!unsecure)
+                               goto addnsec3;
+                       else
+                               remove_unsecure = ISC_TRUE;
+               } else {
+                       dns_rdataset_disassociate(&rdataset);
+                       if (result != ISC_R_NOMORE)
+                               goto failure;
+               }
+       }
+
+       /*
+        * Find the previous NSEC3 (if any) and update it if required.
+        */
+       pass = 0;
+       do {
+               result = dns_dbiterator_prev(dbit);
+               if (result == ISC_R_NOMORE) {
+                       pass++;
+                       CHECK(dns_dbiterator_last(dbit));
+               }
+               CHECK(dns_dbiterator_current(dbit, &node, prev));
+               CHECK(dns_dbiterator_pause(dbit));
+               result = dns_db_findrdataset(db, node, version,
+                                            dns_rdatatype_nsec3, 0,
+                                            (isc_stdtime_t) 0, &rdataset,
+                                            NULL);
+               dns_db_detachnode(db, &node);
+               if (result != ISC_R_SUCCESS)
+                       continue;
+
+               result = find_nsec3(&nsec3, &rdataset, nsec3param);
+               if (result == ISC_R_NOMORE) {
+                       dns_rdataset_disassociate(&rdataset);
+                       continue;
+               }
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+
+               if (remove_unsecure) {
+                       dns_rdataset_disassociate(&rdataset);
+                       /*
+                        * We have found the previous NSEC3 record and can now
+                        * see if the existing NSEC3 record needs to be
+                        * updated or deleted.
+                        */
+                       if (!OPTOUT(nsec3.flags)) {
+                               /*
+                                * Just update the NSEC3 record.
+                                */
+                               goto addnsec3;
+                       } else {
+                               /*
+                                * This is actually a deletion not a add.
+                                */
+                               result = dns_nsec3_delnsec3(db, version, name,
+                                                           nsec3param, diff);
+                               goto failure;
+                       }
+               } else {
+                       /*
+                        * Is this is a unsecure delegation we are adding?
+                        * If so no change is required.
+                        */
+                       if (OPTOUT(nsec3.flags) && unsecure) {
+                               dns_rdataset_disassociate(&rdataset);
+                               goto failure;
+                       }
+               }
+
+               old_next = nsec3.next;
+               old_length = nsec3.next_length;
+
+               /*
+                * Delete the old previous NSEC3.
+                */
+               CHECK(delete(db, version, prev, nsec3param, diff));
+
+               /*
+                * Fixup the previous NSEC3.
+                */
+               nsec3.next = nexthash;
+               nsec3.next_length = next_length;
+               isc_buffer_init(&buffer, nsec3buf, sizeof(nsec3buf));
+               CHECK(dns_rdata_fromstruct(&rdata, rdataset.rdclass,
+                                          dns_rdatatype_nsec3, &nsec3,
+                                          &buffer));
+               CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, prev,
+                                          rdataset.ttl, &rdata, &tuple));
+               CHECK(do_one_tuple(&tuple, db, version, diff));
+               INSIST(old_length <= sizeof(nexthash));
+               memcpy(nexthash, old_next, old_length);
+               if (!CREATE(nsec3param->flags))
+                       flags = nsec3.flags;
+               dns_rdata_reset(&rdata);
+               dns_rdataset_disassociate(&rdataset);
+               break;
+       } while (pass < 2);
+
+ addnsec3:
+       /*
+        * Create the NSEC3 RDATA.
+        */
+       CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+       CHECK(dns_nsec3_buildrdata(db, version, node, hash, flags, iterations,
+                                  salt, salt_length, nexthash, next_length,
+                                  nsec3buf, &rdata));
+       dns_db_detachnode(db, &node);
+       
+       /*
+        * Delete the old NSEC3 and record the change.
+        */
+       CHECK(delete(db, version, hashname, nsec3param, diff));
+       /*
+        * Add the new NSEC3 and record the change.
+        */
+       CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+                                  hashname, nsecttl, &rdata, &tuple));
+       CHECK(do_one_tuple(&tuple, db, version, diff));
+       INSIST(tuple == NULL);
+       dns_rdata_reset(&rdata);
+       dns_db_detachnode(db, &newnode);
+
+       /*
+        * Add missing NSEC3 records for empty nodes
+        */
+       dns_name_init(&empty, NULL);
+       dns_name_clone(name, &empty);
+       do {
+               labels = dns_name_countlabels(&empty) - 1;
+               if (labels <= dns_name_countlabels(origin))
+                       break;
+               dns_name_getlabelsequence(&empty, 1, labels, &empty);
+               CHECK(name_exists(db, version, &empty, &exists));
+               if (exists)
+                       break;
+               CHECK(dns_nsec3_hashname(&fixed, nexthash, &next_length,
+                                        &empty, origin, hash, iterations,
+                                        salt, salt_length));
+
+               /*
+                * Create the node if it doesn't exist and hold
+                * a reference to it until we have added the NSEC3
+                * or we discover we don't need to add make a change.
+                */
+               CHECK(dns_db_findnsec3node(db, hashname, ISC_TRUE, &newnode));
+               result = dns_db_findrdataset(db, newnode, version,
+                                            dns_rdatatype_nsec3, 0,
+                                            (isc_stdtime_t) 0, &rdataset,
+                                            NULL);
+               if (result == ISC_R_SUCCESS) {
+                       result = find_nsec3(&nsec3, &rdataset, nsec3param);
+                       dns_rdataset_disassociate(&rdataset);
+                       if (result == ISC_R_SUCCESS) {
+                               dns_db_detachnode(db, &newnode);
+                               break;
+                       }
+                       if (result != ISC_R_NOMORE)
+                               goto failure;
+               }
+
+               /*
+                * Find the previous NSEC3 and update it.
+                */
+               CHECK(dns_dbiterator_seek(dbit, hashname));
+               pass = 0;
+               do {
+                       result = dns_dbiterator_prev(dbit);
+                       if (result == ISC_R_NOMORE) {
+                               pass++;
+                               CHECK(dns_dbiterator_last(dbit));
+                       }
+                       CHECK(dns_dbiterator_current(dbit, &node, prev));
+                       CHECK(dns_dbiterator_pause(dbit));
+                       result = dns_db_findrdataset(db, node, version,
+                                                    dns_rdatatype_nsec3, 0,
+                                                    (isc_stdtime_t) 0,
+                                                    &rdataset, NULL);
+                       dns_db_detachnode(db, &node);
+                       if (result != ISC_R_SUCCESS)
+                               continue;
+                       result = find_nsec3(&nsec3, &rdataset, nsec3param);
+                       if (result == ISC_R_NOMORE) {
+                               dns_rdataset_disassociate(&rdataset);
+                               continue;
+                       }
+                       if (result != ISC_R_SUCCESS)
+                               goto failure;
+
+                       old_next = nsec3.next;
+                       old_length = nsec3.next_length;
+
+                       /*
+                        * Delete the old previous NSEC3.
+                        */
+                       CHECK(delete(db, version, prev, nsec3param, diff));
+
+                       /*
+                        * Fixup the previous NSEC3.
+                        */
+                       nsec3.next = nexthash;
+                       nsec3.next_length = next_length;
+                       isc_buffer_init(&buffer, nsec3buf,
+                                       sizeof(nsec3buf));
+                       CHECK(dns_rdata_fromstruct(&rdata, rdataset.rdclass,
+                                                  dns_rdatatype_nsec3, &nsec3,
+                                                  &buffer));
+                       CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+                                                  prev, rdataset.ttl, &rdata,
+                                                  &tuple));
+                       CHECK(do_one_tuple(&tuple, db, version, diff));
+                       INSIST(old_length <= sizeof(nexthash));
+                       memcpy(nexthash, old_next, old_length);
+                       if (!CREATE(nsec3param->flags))
+                               flags = nsec3.flags;
+                       dns_rdata_reset(&rdata);
+                       dns_rdataset_disassociate(&rdataset);
+                       break;
+               } while (pass < 2);
+
+               INSIST(pass < 2);
+
+               /*
+                * Create the NSEC3 RDATA for the empty node.
+                */
+               CHECK(dns_nsec3_buildrdata(db, version, NULL, hash, flags,
+                                          iterations, salt, salt_length,
+                                          nexthash, next_length, nsec3buf,
+                                          &rdata));
+               /*
+                * Delete the old NSEC3 and record the change.
+                */
+               CHECK(delete(db, version, hashname, nsec3param, diff));
+
+               /*
+                * Add the new NSEC3 and record the change.
+                */
+               CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+                                          hashname, nsecttl, &rdata, &tuple));
+               CHECK(do_one_tuple(&tuple, db, version, diff));
+               INSIST(tuple == NULL);
+               dns_rdata_reset(&rdata);
+               dns_db_detachnode(db, &newnode);
+       } while (1);
+
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+
+ failure:
+       if (dbit != NULL)
+               dns_dbiterator_destroy(&dbit);
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       if (newnode != NULL)
+               dns_db_detachnode(db, &newnode);
+       return (result);
+}
+
+/*%
+ * Add NSEC3 records for "name", recording the change in "diff".
+ * The existing NSEC3 records are removed.
+ */
+isc_result_t
+dns_nsec3_addnsec3s(dns_db_t *db, dns_dbversion_t *version,
+                   dns_name_t *name, dns_ttl_t nsecttl,
+                   isc_boolean_t unsecure, dns_diff_t *diff)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdata_nsec3param_t nsec3param;
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+
+       dns_rdataset_init(&rdataset);
+
+       /*
+        * Find the NSEC3 parameters for this zone.
+        */
+       result = dns_db_getoriginnode(db, &node);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       result = dns_db_findrdataset(db, node, version,
+                                    dns_rdatatype_nsec3param, 0, 0,
+                                    &rdataset, NULL);
+       dns_db_detachnode(db, &node);
+       if (result == ISC_R_NOTFOUND)
+               return (ISC_R_SUCCESS);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       /*
+        * Update each active NSEC3 chain.
+        */
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+
+               dns_rdataset_current(&rdataset, &rdata);
+               dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+
+#ifdef RFC5155_STRICT
+               if (nsec3param.flags != 0)
+                       continue;
+#else
+               if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0)
+                       continue;
+               if (better_param(&rdataset, &rdata))
+                       continue;
+#endif
+
+               /*
+                * We have a active chain.  Update it.
+                */
+               CHECK(dns_nsec3_addnsec3(db, version, name, &nsec3param,
+                                        nsecttl, unsecure, diff));
+       }
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+
+ failure:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+
+       return (result);
+}
+
+isc_result_t
+dns_nsec3_delnsec3(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+                   const dns_rdata_nsec3param_t *nsec3param, dns_diff_t *diff)
+{
+       dns_dbiterator_t *dbit = NULL;
+       dns_dbnode_t *node = NULL;
+       dns_difftuple_t *tuple = NULL;
+       dns_fixedname_t fixed;
+       dns_fixedname_t fprev;
+       dns_hash_t hash;
+       dns_name_t *hashname;
+       dns_name_t *origin;
+       dns_name_t *prev;
+       dns_name_t empty;
+       dns_rdata_nsec3_t nsec3;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdataset_t rdataset;
+       int pass;
+       isc_boolean_t exists;
+       isc_buffer_t buffer;
+       isc_result_t result;
+       unsigned char *salt;
+       unsigned char nexthash[NSEC3_MAX_HASH_LENGTH];
+       unsigned char nsec3buf[DNS_NSEC3_BUFFERSIZE];
+       unsigned int iterations;
+       unsigned int labels;
+       unsigned int next_length;
+       unsigned int salt_length;
+
+       dns_fixedname_init(&fixed);
+       hashname = dns_fixedname_name(&fixed);
+       dns_fixedname_init(&fprev);
+       prev = dns_fixedname_name(&fprev);
+
+       dns_rdataset_init(&rdataset);
+
+       origin = dns_db_origin(db);
+
+       /*
+        * Chain parameters.
+        */
+       hash = nsec3param->hash;
+       iterations = nsec3param->iterations;
+       salt_length = nsec3param->salt_length;
+       salt = nsec3param->salt;
+
+       /*
+        * If this is the first NSEC3 in the chain nexthash will
+        * remain pointing to itself.
+        */
+       next_length = sizeof(nexthash);
+       CHECK(dns_nsec3_hashname(&fixed, nexthash, &next_length,
+                                name, origin, hash, iterations,
+                                salt, salt_length));
+
+       CHECK(dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbit));
+
+       result = dns_dbiterator_seek(dbit, hashname);
+       if (result == ISC_R_NOTFOUND)
+               goto success;
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       CHECK(dns_dbiterator_current(dbit, &node, NULL));
+       CHECK(dns_dbiterator_pause(dbit));
+       result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec3,
+                                    0, (isc_stdtime_t) 0, &rdataset, NULL);
+       dns_db_detachnode(db, &node);
+       if (result == ISC_R_NOTFOUND)
+               goto success;
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       /*
+        * If we find a existing NSEC3 for this chain then save the
+        * next field.
+        */
+       result = find_nsec3(&nsec3, &rdataset, nsec3param);
+       if (result == ISC_R_SUCCESS) {
+               next_length = nsec3.next_length;
+               INSIST(next_length <= sizeof(nexthash));
+               memcpy(nexthash, nsec3.next, next_length);
+       }
+       dns_rdataset_disassociate(&rdataset);
+       if (result == ISC_R_NOMORE)
+               goto success;
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       /*
+        * Find the previous NSEC3 and update it.
+        */
+       pass = 0;
+       do {
+               result = dns_dbiterator_prev(dbit);
+               if (result == ISC_R_NOMORE) {
+                       pass++;
+                       CHECK(dns_dbiterator_last(dbit));
+               }
+               CHECK(dns_dbiterator_current(dbit, &node, prev));
+               CHECK(dns_dbiterator_pause(dbit));
+               result = dns_db_findrdataset(db, node, version,
+                                            dns_rdatatype_nsec3, 0,
+                                            (isc_stdtime_t) 0, &rdataset,
+                                            NULL);
+               dns_db_detachnode(db, &node);
+               if (result != ISC_R_SUCCESS)
+                       continue;
+               result = find_nsec3(&nsec3, &rdataset, nsec3param);
+               if (result == ISC_R_NOMORE) {
+                       dns_rdataset_disassociate(&rdataset);
+                       continue;
+               }
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+
+               /*
+                * Delete the old previous NSEC3.
+                */
+               CHECK(delete(db, version, prev, nsec3param, diff));
+
+               /*
+                * Fixup the previous NSEC3.
+                */
+               nsec3.next = nexthash;
+               nsec3.next_length = next_length;
+               isc_buffer_init(&buffer, nsec3buf, sizeof(nsec3buf));
+               CHECK(dns_rdata_fromstruct(&rdata, rdataset.rdclass,
+                                          dns_rdatatype_nsec3, &nsec3,
+                                          &buffer));
+               CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, prev,
+                                          rdataset.ttl, &rdata, &tuple));
+               CHECK(do_one_tuple(&tuple, db, version, diff));
+               dns_rdata_reset(&rdata);
+               dns_rdataset_disassociate(&rdataset);
+               break;
+       } while (pass < 2);
+
+       /*
+        * Delete the old NSEC3 and record the change.
+        */
+       CHECK(delete(db, version, hashname, nsec3param, diff));
+
+       /*
+        *  Delete NSEC3 records for now non active nodes.
+        */
+       dns_name_init(&empty, NULL);
+       dns_name_clone(name, &empty);
+       do {
+               labels = dns_name_countlabels(&empty) - 1;
+               if (labels <= dns_name_countlabels(origin))
+                       break;
+               dns_name_getlabelsequence(&empty, 1, labels, &empty);
+               CHECK(name_exists(db, version, &empty, &exists));
+               if (exists)
+                       break;
+
+               CHECK(dns_nsec3_hashname(&fixed, nexthash, &next_length,
+                                        &empty, origin, hash, iterations,
+                                        salt, salt_length));
+               result = dns_dbiterator_seek(dbit, hashname);
+               if (result == ISC_R_NOTFOUND)
+                       goto success;
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+
+               CHECK(dns_dbiterator_current(dbit, &node, NULL));
+               CHECK(dns_dbiterator_pause(dbit));
+               result = dns_db_findrdataset(db, node, version,
+                                            dns_rdatatype_nsec3, 0,
+                                            (isc_stdtime_t) 0, &rdataset,
+                                            NULL);
+               dns_db_detachnode(db, &node);
+               if (result == ISC_R_NOTFOUND)
+                       goto success;
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+
+               result = find_nsec3(&nsec3, &rdataset, nsec3param); 
+               if (result == ISC_R_SUCCESS) {
+                       next_length = nsec3.next_length;
+                       INSIST(next_length <= sizeof(nexthash));
+                       memcpy(nexthash, nsec3.next, next_length);
+               }
+               dns_rdataset_disassociate(&rdataset);
+               if (result == ISC_R_NOMORE)
+                       goto success;
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+
+               pass = 0;
+               do {
+                       result = dns_dbiterator_prev(dbit);
+                       if (result == ISC_R_NOMORE) {
+                               pass++;
+                               CHECK(dns_dbiterator_last(dbit));
+                       }
+                       CHECK(dns_dbiterator_current(dbit, &node, prev));
+                       CHECK(dns_dbiterator_pause(dbit));
+                       result = dns_db_findrdataset(db, node, version,
+                                                    dns_rdatatype_nsec3, 0,
+                                                    (isc_stdtime_t) 0,
+                                                    &rdataset, NULL);
+                       dns_db_detachnode(db, &node);
+                       if (result != ISC_R_SUCCESS)
+                               continue;
+                       result = find_nsec3(&nsec3, &rdataset, nsec3param); 
+                       if (result == ISC_R_NOMORE) {
+                               dns_rdataset_disassociate(&rdataset);
+                               continue;
+                       }
+                       if (result != ISC_R_SUCCESS)
+                               goto failure;
+
+                       /*
+                        * Delete the old previous NSEC3.
+                        */
+                       CHECK(delete(db, version, prev, nsec3param, diff));
+
+                       /*
+                        * Fixup the previous NSEC3.
+                        */
+                       nsec3.next = nexthash;
+                       nsec3.next_length = next_length;
+                       isc_buffer_init(&buffer, nsec3buf,
+                                       sizeof(nsec3buf));
+                       CHECK(dns_rdata_fromstruct(&rdata, rdataset.rdclass,
+                                                  dns_rdatatype_nsec3, &nsec3,
+                                                  &buffer));
+                       CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+                                                  prev, rdataset.ttl, &rdata,
+                                                  &tuple));
+                       CHECK(do_one_tuple(&tuple, db, version, diff));
+                       dns_rdata_reset(&rdata);
+                       dns_rdataset_disassociate(&rdataset);
+                       break;
+               } while (pass < 2);
+
+               INSIST(pass < 2);
+
+               /*
+                * Delete the old NSEC3 and record the change.
+                */
+               CHECK(delete(db, version, hashname, nsec3param, diff));
+       } while (1);
+
+ success:
+       result = ISC_R_SUCCESS;
+
+ failure:
+       if (dbit != NULL)
+               dns_dbiterator_destroy(&dbit);
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       return (result);
+}
+
+isc_result_t
+dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+                   dns_diff_t *diff)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdata_nsec3param_t nsec3param;
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+
+       dns_rdataset_init(&rdataset);
+
+       /*
+        * Find the NSEC3 parameters for this zone.
+        */
+       result = dns_db_getoriginnode(db, &node);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       result = dns_db_findrdataset(db, node, version,
+                                    dns_rdatatype_nsec3param, 0, 0,
+                                    &rdataset, NULL);
+       dns_db_detachnode(db, &node);
+       if (result == ISC_R_NOTFOUND)
+               return (ISC_R_SUCCESS);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       /*
+        * Update each active NSEC3 chain.
+        */
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+
+               dns_rdataset_current(&rdataset, &rdata);
+               dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+
+#ifdef RFC5155_STRICT
+               if (nsec3param.flags != 0)
+                       continue;
+#else
+               if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0)
+                       continue;
+               if (better_param(&rdataset, &rdata))
+                       continue;
+#endif
+
+               /*
+                * We have a active chain.  Update it.
+                */
+               CHECK(dns_nsec3_delnsec3(db, version, name, &nsec3param, diff));
+       }
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+
+ failure:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+
+       return (result);
+}
+
+isc_result_t
+dns_nsec3_active(dns_db_t *db, dns_dbversion_t *version,
+                isc_boolean_t complete, isc_boolean_t *answer)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdataset_t rdataset;
+       dns_rdata_nsec3param_t nsec3param;
+       isc_result_t result;
+
+       REQUIRE(answer != NULL);
+
+       dns_rdataset_init(&rdataset);
+
+       result = dns_db_getoriginnode(db, &node);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       result = dns_db_findrdataset(db, node, version,
+                                    dns_rdatatype_nsec3param, 0, 0,
+                                    &rdataset, NULL);
+       dns_db_detachnode(db, &node);
+
+       if (result == ISC_R_NOTFOUND) {
+               *answer = ISC_FALSE;
+               return (ISC_R_SUCCESS);
+       }
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+
+               dns_rdataset_current(&rdataset, &rdata);
+               result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+               if ((nsec3param.flags) == 0 ||
+                   (!complete && CREATE(nsec3param.flags)))
+                       break;
+       }
+       dns_rdataset_disassociate(&rdataset);
+       if (result == ISC_R_SUCCESS)
+               *answer = ISC_TRUE;
+       if (result == ISC_R_NOMORE) {
+               *answer = ISC_FALSE;
+               result = ISC_R_SUCCESS;
+       }
+       return (result);
+}
+
+isc_result_t
+dns_nsec3_maxiterations(dns_db_t *db, dns_dbversion_t *version,
+                       isc_mem_t *mctx, unsigned int *iterationsp)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdataset_t rdataset;
+       dst_key_t *key = NULL;
+       isc_buffer_t buffer;
+       isc_result_t result;
+       isc_uint16_t bits, minbits = 4096;
+
+       result = dns_db_getoriginnode(db, &node);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       dns_rdataset_init(&rdataset);
+       result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey,
+                                    0, 0, &rdataset, NULL);
+       dns_db_detachnode(db, &node);
+       if (result == ISC_R_NOTFOUND) {
+               *iterationsp = 0;
+               return (ISC_R_SUCCESS);
+       }
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+
+               dns_rdataset_current(&rdataset, &rdata);
+               isc_buffer_init(&buffer, rdata.data, rdata.length);
+               isc_buffer_add(&buffer, rdata.length);
+               CHECK(dst_key_fromdns(dns_db_origin(db), rdataset.rdclass,
+                                     &buffer, mctx, &key));
+               bits = dst_key_getbits(key);
+               dst_key_free(&key);
+               if (minbits > bits)
+                       minbits = bits;
+       }
+       if (result != ISC_R_NOMORE)
+               goto failure;
+
+       if (minbits <= 1024)
+               *iterationsp = 150;
+       else if (minbits <= 2048)
+               *iterationsp = 500;
+       else
+               *iterationsp = 2500;
+       result = ISC_R_SUCCESS;
+
+ failure:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       return (result);
+}
index 26d63f8ad7391fe1580f50b0a048a65147603d5e..aee7d21aea6cf204df050038d1263d4abfe92239 100644 (file)
@@ -17,7 +17,7 @@
 
 /*
  * Principal Author: Brian Wellington
- * $Id: opensslrsa_link.c,v 1.19 2008/04/01 23:47:10 tbox Exp $
+ * $Id: opensslrsa_link.c,v 1.20 2008/09/24 02:46:22 marka Exp $
  */
 #ifdef OPENSSL
 #ifndef USE_EVP
@@ -117,7 +117,8 @@ opensslrsa_createctx(dst_key_t *key, dst_context_t *dctx) {
 
        UNUSED(key);
        REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
-               dctx->key->key_alg == DST_ALG_RSASHA1);
+               dctx->key->key_alg == DST_ALG_RSASHA1 ||
+               dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
 
 #if USE_EVP
        evp_md_ctx = EVP_MD_CTX_create();
@@ -164,7 +165,8 @@ opensslrsa_destroyctx(dst_context_t *dctx) {
 #endif
 
        REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
-               dctx->key->key_alg == DST_ALG_RSASHA1);
+               dctx->key->key_alg == DST_ALG_RSASHA1 ||
+               dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
 
 #if USE_EVP
        if (evp_md_ctx != NULL) {
@@ -199,7 +201,8 @@ opensslrsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
 #endif
 
        REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
-               dctx->key->key_alg == DST_ALG_RSASHA1);
+               dctx->key->key_alg == DST_ALG_RSASHA1 ||
+               dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
 
 #if USE_EVP
        if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length)) {
@@ -239,7 +242,8 @@ opensslrsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
 #endif
 
        REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
-               dctx->key->key_alg == DST_ALG_RSASHA1);
+               dctx->key->key_alg == DST_ALG_RSASHA1 ||
+               dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
 
        isc_buffer_availableregion(sig, &r);
 
@@ -297,7 +301,8 @@ opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
 #endif
 
        REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
-               dctx->key->key_alg == DST_ALG_RSASHA1);
+               dctx->key->key_alg == DST_ALG_RSASHA1 ||
+               dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
 
 #if USE_EVP
        status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey);
index 9aa447c5fbc53f282fdec022ec802a2dcb5c602f..112591e0e733dbaa6fbdcc4d1d3f63cb3d5af176 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rbt.c,v 1.141 2008/03/31 13:11:32 fdupont Exp $ */
+/* $Id: rbt.c,v 1.142 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -527,6 +527,7 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
                                 * current node.
                                 */
                                new_current->is_root = current->is_root;
+                               new_current->nsec3 = current->nsec3;
                                PARENT(new_current)  = PARENT(current);
                                LEFT(new_current)    = LEFT(current);
                                RIGHT(new_current)   = RIGHT(current);
@@ -1445,6 +1446,7 @@ create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) {
        DIRTY(node) = 0;
        dns_rbtnode_refinit(node, 0);
        node->find_callback = 0;
+       node->nsec3 = 0;
 
        MAKE_BLACK(node);
 
@@ -2359,6 +2361,113 @@ dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
        return (result);
 }
 
+isc_result_t
+dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name,
+                     dns_name_t *origin)
+{
+       dns_rbtnode_t *current, *successor;
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_boolean_t new_origin = ISC_FALSE;
+
+       REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
+
+       successor = NULL;
+
+       current = chain->end;
+
+       if (DOWN(current) != NULL) {
+               /*
+                * Don't declare an origin change when the new origin is "."
+                * at the second level tree, because "." is already declared
+                * as the origin for the top level tree.
+                */
+               if (chain->level_count > 0 ||
+                   OFFSETLEN(current) > 1)
+                       new_origin = ISC_TRUE;
+
+               ADD_LEVEL(chain, current);
+               current = DOWN(current);
+
+               while (LEFT(current) != NULL)
+                       current = LEFT(current);
+
+               successor = current;
+       }
+
+       if (successor != NULL) {
+               chain->end = successor;
+
+               /*
+                * It is not necessary to use dns_rbtnodechain_current like
+                * the other functions because this function will never
+                * find a node in the topmost level.  This is because the
+                * root level will never be more than one name, and everything
+                * in the megatree is a successor to that node, down at
+                * the second level or below.
+                */
+
+               if (name != NULL)
+                       NODENAME(chain->end, name);
+
+               if (new_origin) {
+                       if (origin != NULL)
+                               result = chain_name(chain, origin, ISC_FALSE);
+
+                       if (result == ISC_R_SUCCESS)
+                               result = DNS_R_NEWORIGIN;
+
+               } else
+                       result = ISC_R_SUCCESS;
+
+       } else
+               result = ISC_R_NOMORE;
+
+       return (result);
+}
+
+isc_result_t
+dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name) {
+       dns_rbtnode_t *current, *previous, *successor;
+       isc_result_t result = ISC_R_SUCCESS;
+
+       REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
+
+       successor = NULL;
+
+       current = chain->end;
+
+       if (RIGHT(current) == NULL) {
+               while (! IS_ROOT(current)) {
+                       previous = current;
+                       current = PARENT(current);
+
+                       if (LEFT(current) == previous) {
+                               successor = current;
+                               break;
+                       }
+               }
+       } else {
+               current = RIGHT(current);
+
+               while (LEFT(current) != NULL)
+                       current = LEFT(current);
+
+               successor = current;
+       }
+
+       if (successor != NULL) {
+               chain->end = successor;
+
+               if (name != NULL)
+                       NODENAME(chain->end, name);
+
+               result = ISC_R_SUCCESS;
+       } else
+               result = ISC_R_NOMORE;
+
+       return (result);
+}
+
 isc_result_t
 dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
                      dns_name_t *origin)
index 3c6825ade8351a0ac474ad39edb900aa327f719c..daec21c3e410255fc248251a08e4b613c2152889 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rbtdb.c,v 1.262 2008/08/13 02:28:45 jinmei Exp $ */
+/* $Id: rbtdb.c,v 1.263 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
 #include <dns/log.h>
 #include <dns/masterdump.h>
 #include <dns/nsec.h>
+#include <dns/nsec3.h>
 #include <dns/rbt.h>
 #include <dns/rdata.h>
 #include <dns/rdataset.h>
 #include <dns/rdatasetiter.h>
 #include <dns/rdataslab.h>
+#include <dns/rdatastruct.h>
 #include <dns/result.h>
 #include <dns/stats.h>
 #include <dns/view.h>
@@ -98,10 +100,12 @@ typedef isc_uint32_t                    rbtdb_rdatatype_t;
 
 #define RBTDB_RDATATYPE_BASE(type)      ((dns_rdatatype_t)((type) & 0xFFFF))
 #define RBTDB_RDATATYPE_EXT(type)       ((dns_rdatatype_t)((type) >> 16))
-#define RBTDB_RDATATYPE_VALUE(b, e)     (((e) << 16) | (b))
+#define RBTDB_RDATATYPE_VALUE(b, e)     ((rbtdb_rdatatype_t)((e) << 16) | (b))
 
 #define RBTDB_RDATATYPE_SIGNSEC \
                RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec)
+#define RBTDB_RDATATYPE_SIGNSEC3 \
+               RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec3)
 #define RBTDB_RDATATYPE_SIGNS \
                RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ns)
 #define RBTDB_RDATATYPE_SIGCNAME \
@@ -193,9 +197,10 @@ typedef isc_mutex_t nodelock_t;
 #define RBTDB_VIRTUAL 300
 
 struct noqname {
-       dns_name_t name;
-       void *     nsec;
-       void *     nsecsig;
+       dns_name_t      name;
+       void *          neg;
+       void *          negsig;
+       dns_rdatatype_t type;
 };
 
 typedef struct acachectl acachectl_t;
@@ -210,6 +215,7 @@ typedef struct rdatasetheader {
        isc_uint16_t                    attributes;
        dns_trust_t                     trust;
        struct noqname                  *noqname;
+       struct noqname                  *closest;
        /*%<
         * We don't use the LIST macros, because the LIST structure has
         * both head and tail pointers, and is doubly linked.
@@ -275,6 +281,7 @@ typedef ISC_LIST(dns_rbtnode_t)         rbtnodelist_t;
 #define RDATASET_ATTR_NXDOMAIN          0x0010
 #define RDATASET_ATTR_RESIGN            0x0020
 #define RDATASET_ATTR_STATCOUNT         0x0040
+#define RDATASET_ATTR_OPTOUT           0x0080
 
 typedef struct acache_cbarg {
        dns_rdatasetadditional_t        type;
@@ -311,6 +318,8 @@ struct acachectl {
        (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0)
 #define RESIGN(header) \
        (((header)->attributes & RDATASET_ATTR_RESIGN) != 0)
+#define OPTOUT(header) \
+       (((header)->attributes & RDATASET_ATTR_OPTOUT) != 0)
 
 #define DEFAULT_NODE_LOCK_COUNT         7       /*%< Should be prime. */
 #define DEFAULT_CACHE_NODE_LOCK_COUNT   1009    /*%< Should be prime. */
@@ -346,6 +355,14 @@ typedef struct rbtdb_version {
        rbtdb_changedlist_t             changed_list;
        rdatasetheaderlist_t            resigned_list;
        ISC_LINK(struct rbtdb_version)  link;
+       isc_boolean_t                   secure;
+       isc_boolean_t                   havensec3;
+       /* NSEC3 parameters */
+       dns_hash_t                      hash;
+       isc_uint8_t                     flags;
+       isc_uint16_t                    iterations;
+       isc_uint8_t                     salt_length;
+       unsigned char                   salt[NSEC3_MAX_HASH_LENGTH];
 } rbtdb_version_t;
 
 typedef ISC_LIST(rbtdb_version_t)       rbtdb_versionlist_t;
@@ -404,7 +421,7 @@ typedef struct {
 
        /* Locked by tree_lock. */
        dns_rbt_t *                     tree;
-       dns_db_secure_t                 secure;
+       dns_rbt_t *                     nsec3;
 
        /* Unlocked */
        unsigned int                    quantum;
@@ -448,8 +465,12 @@ static void rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target);
 static unsigned int rdataset_count(dns_rdataset_t *rdataset);
 static isc_result_t rdataset_getnoqname(dns_rdataset_t *rdataset,
                                        dns_name_t *name,
-                                       dns_rdataset_t *nsec,
-                                       dns_rdataset_t *nsecsig);
+                                       dns_rdataset_t *neg,
+                                       dns_rdataset_t *negsig);
+static isc_result_t rdataset_getclosest(dns_rdataset_t *rdataset,
+                                       dns_name_t *name,
+                                       dns_rdataset_t *neg,
+                                       dns_rdataset_t *negsig);
 static isc_result_t rdataset_getadditional(dns_rdataset_t *rdataset,
                                           dns_rdatasetadditional_t type,
                                           dns_rdatatype_t qtype,
@@ -492,6 +513,8 @@ static dns_rdatasetmethods_t rdataset_methods = {
        rdataset_count,
        NULL,
        rdataset_getnoqname,
+       NULL,
+       rdataset_getclosest,
        rdataset_getadditional,
        rdataset_setadditional,
        rdataset_putadditional
@@ -555,9 +578,13 @@ typedef struct rbtdb_dbiterator {
        dns_fixedname_t                 name;
        dns_fixedname_t                 origin;
        dns_rbtnodechain_t              chain;
+       dns_rbtnodechain_t              nsec3chain;
+       dns_rbtnodechain_t              *current;
        dns_rbtnode_t                   *node;
        dns_rbtnode_t                   *deletions[DELETION_BATCH_MAX];
        int                             delete;
+       isc_boolean_t                   nsec3only;
+       isc_boolean_t                   nonsec3;
 } rbtdb_dbiterator_t;
 
 
@@ -567,6 +594,8 @@ typedef struct rbtdb_dbiterator {
 static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log,
                       isc_event_t *event);
 static void overmem(dns_db_t *db, isc_boolean_t overmem);
+static void setnsec3parameters(dns_db_t *db, rbtdb_version_t *version,
+                              isc_boolean_t *nsec3createflag);
 
 /*%
  * 'init_count' is used to initialize 'newheader->count' which inturn
@@ -831,6 +860,30 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
                }
                INSIST(result == ISC_R_SUCCESS && rbtdb->tree == NULL);
        }
+
+       if (rbtdb->nsec3 != NULL) {
+               isc_time_now(&start);
+               result = dns_rbt_destroy2(&rbtdb->nsec3, rbtdb->quantum);
+               if (result == ISC_R_QUOTA) {
+                       INSIST(rbtdb->task != NULL);
+                       if (rbtdb->quantum != 0)
+                               rbtdb->quantum = adjust_quantum(rbtdb->quantum,
+                                                               &start);
+                       if (event == NULL)
+                               event = isc_event_allocate(rbtdb->common.mctx,
+                                                          NULL,
+                                                        DNS_EVENT_FREESTORAGE,
+                                                          free_rbtdb_callback,
+                                                          rbtdb,
+                                                          sizeof(isc_event_t));
+                       if (event == NULL)
+                               goto again;
+                       isc_task_send(rbtdb->task, &event);
+                       return;
+               }
+               INSIST(result == ISC_R_SUCCESS && rbtdb->nsec3 == NULL);
+       }
+
        if (event != NULL)
                isc_event_free(&event);
        if (log) {
@@ -1017,6 +1070,24 @@ newversion(dns_db_t *db, dns_dbversion_t **versionp) {
                                   ISC_TRUE);
        if (version != NULL) {
                version->commit_ok = ISC_TRUE;
+               version->secure = rbtdb->current_version->secure;
+               version->havensec3 = rbtdb->current_version->havensec3;
+               if (version->havensec3) {
+                       version->flags = rbtdb->current_version->flags;
+                       version->iterations =
+                               rbtdb->current_version->iterations;
+                       version->hash = rbtdb->current_version->hash;
+                       version->salt_length =
+                               rbtdb->current_version->salt_length;
+                       memcpy(version->salt, rbtdb->current_version->salt,
+                              version->salt_length);
+               } else {
+                       version->flags = 0;
+                       version->iterations = 0;
+                       version->hash = 0;
+                       version->salt_length = 0;
+                       memset(version->salt, 0, sizeof(version->salt));
+               }
                rbtdb->next_serial++;
                rbtdb->future_version = version;
        }
@@ -1112,12 +1183,12 @@ free_noqname(isc_mem_t *mctx, struct noqname **noqname) {
 
        if (dns_name_dynamic(&(*noqname)->name))
                dns_name_free(&(*noqname)->name, mctx);
-       if ((*noqname)->nsec != NULL)
-               isc_mem_put(mctx, (*noqname)->nsec,
-                           dns_rdataslab_size((*noqname)->nsec, 0));
-       if ((*noqname)->nsecsig != NULL)
-               isc_mem_put(mctx, (*noqname)->nsecsig,
-                           dns_rdataslab_size((*noqname)->nsecsig, 0));
+       if ((*noqname)->neg != NULL)
+               isc_mem_put(mctx, (*noqname)->neg,
+                           dns_rdataslab_size((*noqname)->neg, 0));
+       if ((*noqname)->negsig != NULL)
+               isc_mem_put(mctx, (*noqname)->negsig,
+                           dns_rdataslab_size((*noqname)->negsig, 0));
        isc_mem_put(mctx, *noqname, sizeof(**noqname));
        *noqname = NULL;
 }
@@ -1173,6 +1244,8 @@ free_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *rdataset)
 
        if (rdataset->noqname != NULL)
                free_noqname(mctx, &rdataset->noqname);
+       if (rdataset->closest != NULL)
+               free_noqname(mctx, &rdataset->closest);
 
        free_acachearray(mctx, rdataset, rdataset->additional_auth);
        free_acachearray(mctx, rdataset, rdataset->additional_glue);
@@ -1409,7 +1482,13 @@ cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum) {
                INSIST(dns_rbtnode_refcurrent(node) == 0 &&
                       node->data == NULL);
 
-               result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
+               INSIST(!ISC_LINK_LINKED(node, deadlink));
+               if (node->nsec3)
+                       result = dns_rbt_deletenode(rbtdb->nsec3, node,
+                                                   ISC_FALSE);
+               else
+                       result = dns_rbt_deletenode(rbtdb->tree, node,
+                                                   ISC_FALSE);
                if (result != ISC_R_SUCCESS)
                        isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
                                      DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
@@ -1605,7 +1684,12 @@ decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
                }
 
                INSIST(!ISC_LINK_LINKED(node, deadlink));
-               result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
+               if (node->nsec3)
+                       result = dns_rbt_deletenode(rbtdb->nsec3, node,
+                                                   ISC_FALSE);
+               else
+                       result = dns_rbt_deletenode(rbtdb->tree, node,
+                                                   ISC_FALSE);
                if (result != ISC_R_SUCCESS)
                        isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
                                      DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
@@ -1679,19 +1763,20 @@ cleanup_nondirty(rbtdb_version_t *version, rbtdb_changedlist_t *cleanup_list) {
        }
 }
 
-static dns_db_secure_t
-iszonesecure(dns_db_t *db, dns_dbnode_t *origin) {
+static void
+iszonesecure(dns_db_t *db, rbtdb_version_t *version, dns_dbnode_t *origin) {
        dns_rdataset_t keyset;
        dns_rdataset_t nsecset, signsecset;
        dns_rdata_t rdata = DNS_RDATA_INIT;
        isc_boolean_t haszonekey = ISC_FALSE;
        isc_boolean_t hasnsec = ISC_FALSE;
        isc_boolean_t hasoptbit = ISC_FALSE;
+       isc_boolean_t nsec3createflag = ISC_FALSE;
        isc_result_t result;
 
        dns_rdataset_init(&keyset);
-       result = dns_db_findrdataset(db, origin, NULL, dns_rdatatype_dnskey, 0,
-                                    0, &keyset, NULL);
+       result = dns_db_findrdataset(db, origin, version, dns_rdatatype_dnskey,
+                                    0, 0, &keyset, NULL);
        if (result == ISC_R_SUCCESS) {
                dns_rdata_t keyrdata = DNS_RDATA_INIT;
                result = dns_rdataset_first(&keyset);
@@ -1705,13 +1790,16 @@ iszonesecure(dns_db_t *db, dns_dbnode_t *origin) {
                }
                dns_rdataset_disassociate(&keyset);
        }
-       if (!haszonekey)
-               return (dns_db_insecure);
+       if (!haszonekey) {
+               version->secure = dns_db_insecure;
+               version->havensec3 = ISC_FALSE;
+               return;
+       }
 
        dns_rdataset_init(&nsecset);
        dns_rdataset_init(&signsecset);
-       result = dns_db_findrdataset(db, origin, NULL, dns_rdatatype_nsec, 0,
-                                    0, &nsecset, &signsecset);
+       result = dns_db_findrdataset(db, origin, version, dns_rdatatype_nsec,
+                                    0, 0, &nsecset, &signsecset);
        if (result == ISC_R_SUCCESS) {
                if (dns_rdataset_isassociated(&signsecset)) {
                        hasnsec = ISC_TRUE;
@@ -1726,9 +1814,129 @@ iszonesecure(dns_db_t *db, dns_dbnode_t *origin) {
                dns_rdataset_disassociate(&nsecset);
        }
 
-       if (hasnsec && hasoptbit)
-               return (dns_db_partial);
-       return (hasnsec ? dns_db_secure : dns_db_insecure);
+       setnsec3parameters(db, version, &nsec3createflag);
+
+       /*
+        * Do we have a valid NSEC/NSEC3 chain?
+        */
+       if (version->havensec3 || (hasnsec && !hasoptbit))
+               version->secure = dns_db_secure;
+       /*
+        * Do we have a NSEC/NSEC3 chain under creation?
+        */
+       else if (hasoptbit || nsec3createflag)
+               version->secure = dns_db_partial;
+       else
+               version->secure = dns_db_insecure;
+}
+
+/*%<
+ * Walk the origin node looking for NSEC3PARAM records.
+ * Cache the nsec3 parameters.
+ */
+static void
+setnsec3parameters(dns_db_t *db, rbtdb_version_t *version,
+                  isc_boolean_t *nsec3createflag)
+{
+       dns_rbtnode_t *node;
+       dns_rdata_nsec3param_t nsec3param;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       isc_region_t region;
+       isc_result_t result;
+       rdatasetheader_t *header, *header_next;
+       unsigned char *raw;             /* RDATASLAB */
+       unsigned int count, length;
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       version->havensec3 = ISC_FALSE;
+       node = rbtdb->origin_node;
+       NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
+                 isc_rwlocktype_read);
+       for (header = node->data;
+            header != NULL;
+            header = header_next) {
+               header_next = header->next;
+               do {
+                       if (header->serial <= version->serial &&
+                           !IGNORE(header)) {
+                               if (NONEXISTENT(header))
+                                       header = NULL;
+                               break;
+                       } else
+                               header = header->down;
+               } while (header != NULL);
+
+               if (header != NULL &&
+                   header->type == dns_rdatatype_nsec3param) {
+                       /*
+                        * Find A NSEC3PARAM with a supported algorithm.
+                        */
+                       raw = (unsigned char *)header + sizeof(*header);
+                       count = raw[0] * 256 + raw[1]; /* count */
+#if DNS_RDATASET_FIXED
+                       raw += count * 4 + 2;
+#else
+                       raw += 2;
+#endif
+                       while (count-- > 0U) {
+                               length = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+                               raw += 4;
+#else
+                               raw += 2;
+#endif
+                               region.base = raw;
+                               region.length = length;
+                               raw += length;
+                               dns_rdata_fromregion(&rdata,
+                                                    rbtdb->common.rdclass,
+                                                    dns_rdatatype_nsec3param,
+                                                    &region);
+                               result = dns_rdata_tostruct(&rdata,
+                                                           &nsec3param,
+                                                           NULL);
+                               INSIST(result == ISC_R_SUCCESS);
+                               dns_rdata_reset(&rdata);
+
+                               if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG &&
+                                   !dns_nsec3_supportedhash(nsec3param.hash))
+                                       continue;
+
+#ifdef RFC5155_STRICT
+                               if (nsec3param.flags != 0)
+                                       continue;
+#else
+                               if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE)
+                                   != 0)
+                                       *nsec3createflag = ISC_TRUE;
+                               if ((nsec3param.flags & ~DNS_NSEC3FLAG_OPTOUT)
+                                   != 0)
+                                       continue;
+#endif 
+
+                               INSIST(nsec3param.salt_length <=
+                                      sizeof(version->salt));
+                               memcpy(version->salt, nsec3param.salt,
+                                      nsec3param.salt_length);
+                               version->hash = nsec3param.hash;
+                               version->salt_length = nsec3param.salt_length;
+                               version->iterations = nsec3param.iterations;
+                               version->flags = nsec3param.flags;
+                               version->havensec3 = ISC_TRUE;
+                               /*
+                                * Look for a better algorithm than the
+                                * unknown test algorithm.
+                                */
+                               if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG)
+                                       goto unlock;
+                       }
+               }
+       }
+ unlock:
+       NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
+                   isc_rwlocktype_read);
+       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
 }
 
 static void
@@ -1902,7 +2110,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
         * Update the zone's secure status.
         */
        if (writer && commit && !IS_CACHE(rbtdb))
-               rbtdb->secure = iszonesecure(db, rbtdb->origin_node);
+               iszonesecure(db, version, rbtdb->origin_node);
 
        if (cleanup_version != NULL) {
                INSIST(EMPTY(cleanup_version->changed_list));
@@ -1969,7 +2177,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
                RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
        }
 
 end:
+ end:
        *versionp = NULL;
 }
 
@@ -2002,6 +2210,7 @@ add_wildcard_magic(dns_rbtdb_t *rbtdb, dns_name_t *name) {
        result = dns_rbt_addnode(rbtdb->tree, &foundname, &node);
        if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
                return (result);
+       node->nsec3 = 0;
        node->find_callback = 1;
        node->wild = 1;
        return (ISC_R_SUCCESS);
@@ -2029,6 +2238,7 @@ add_empty_wildcards(dns_rbtdb_t *rbtdb, dns_name_t *name) {
                                                 &node);
                        if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
                                return (result);
+                       node->nsec3 = 0;
                }
                i++;
        }
@@ -2074,6 +2284,7 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
                        node->locknum = dns_name_hash(&nodename, ISC_TRUE) %
                                rbtdb->node_lock_count;
 #endif
+                       node->nsec3 = 0;
                        add_empty_wildcards(rbtdb, name);
 
                        if (dns_name_iswildcard(name)) {
@@ -2096,6 +2307,62 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
        return (ISC_R_SUCCESS);
 }
 
+static isc_result_t
+findnsec3node(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+             dns_dbnode_t **nodep)
+{
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+       dns_rbtnode_t *node = NULL;
+       dns_name_t nodename;
+       isc_result_t result;
+       isc_rwlocktype_t locktype = isc_rwlocktype_read;
+
+       REQUIRE(VALID_RBTDB(rbtdb));
+
+       dns_name_init(&nodename, NULL);
+       RWLOCK(&rbtdb->tree_lock, locktype);
+       result = dns_rbt_findnode(rbtdb->nsec3, name, NULL, &node, NULL,
+                                 DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+       if (result != ISC_R_SUCCESS) {
+               RWUNLOCK(&rbtdb->tree_lock, locktype);
+               if (!create) {
+                       if (result == DNS_R_PARTIALMATCH)
+                               result = ISC_R_NOTFOUND;
+                       return (result);
+               }
+               /*
+                * It would be nice to try to upgrade the lock instead of
+                * unlocking then relocking.
+                */
+               locktype = isc_rwlocktype_write;
+               RWLOCK(&rbtdb->tree_lock, locktype);
+               node = NULL;
+               result = dns_rbt_addnode(rbtdb->nsec3, name, &node);
+               if (result == ISC_R_SUCCESS) {
+                       dns_rbt_namefromnode(node, &nodename);
+#ifdef DNS_RBT_USEHASH
+                       node->locknum = node->hashval % rbtdb->node_lock_count;
+#else
+                       node->locknum = dns_name_hash(&nodename, ISC_TRUE) %
+                               rbtdb->node_lock_count;
+#endif
+                       node->nsec3 = 1U;
+               } else if (result != ISC_R_EXISTS) {
+                       RWUNLOCK(&rbtdb->tree_lock, locktype);
+                       return (result);
+               }
+       } else
+               INSIST(node->nsec3);
+       NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock);
+       new_reference(rbtdb, node);
+       NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock);
+       RWUNLOCK(&rbtdb->tree_lock, locktype);
+
+       *nodep = (dns_dbnode_t *)node;
+
+       return (ISC_R_SUCCESS);
+}
+
 static isc_result_t
 zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
        rbtdb_search_t *search = arg;
@@ -2265,6 +2532,8 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
        rdataset->trust = header->trust;
        if (NXDOMAIN(header))
                rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN;
+       if (OPTOUT(header))
+               rdataset->attributes |= DNS_RDATASETATTR_OPTOUT;
        rdataset->private1 = rbtdb;
        rdataset->private2 = node;
        raw = (unsigned char *)header + sizeof(*header);
@@ -2285,6 +2554,10 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
        rdataset->private6 = header->noqname;
        if (rdataset->private6 != NULL)
                rdataset->attributes |=  DNS_RDATASETATTR_NOQNAME;
+       rdataset->private7 = header->closest;
+       if (rdataset->private7 != NULL)
+               rdataset->attributes |=  DNS_RDATASETATTR_CLOSEST;
+
        /*
         * Copy out re-signing information.
         */
@@ -2740,10 +3013,50 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep,
        return (result);
 }
 
+static isc_boolean_t
+matchparams(rdatasetheader_t *header, rbtdb_search_t *search)
+{
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdata_nsec3_t nsec3;
+       unsigned char *raw;                     /* RDATASLAB */
+       unsigned int rdlen, count;
+       isc_region_t region;
+       isc_result_t result;
+
+       REQUIRE(header->type == dns_rdatatype_nsec3);
+
+       raw = (unsigned char *)header + sizeof(*header);
+       count = raw[0] * 256 + raw[1]; /* count */
+#if DNS_RDATASET_FIXED
+       raw += count * 4 + 2;
+#else
+       raw += 2;
+#endif
+       rdlen = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+       raw += 4;
+#else
+       raw += 2;
+#endif
+       region.base = raw;
+       region.length = rdlen;
+       dns_rdata_fromregion(&rdata, search->rbtdb->common.rdclass,
+                            dns_rdatatype_nsec3, &region);
+       result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
+       INSIST(result == ISC_R_SUCCESS);
+       if (nsec3.hash == search->rbtversion->hash &&
+           nsec3.iterations == search->rbtversion->iterations &&
+           nsec3.salt_length == search->rbtversion->salt_length &&
+           memcmp(nsec3.salt, search->rbtversion->salt, nsec3.salt_length) == 0)
+               return (ISC_TRUE);
+       return (ISC_FALSE);
+}
+
 static inline isc_result_t
 find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
                  dns_name_t *foundname, dns_rdataset_t *rdataset,
-                 dns_rdataset_t *sigrdataset, isc_boolean_t need_sig)
+                 dns_rdataset_t *sigrdataset, dns_rbt_t *tree,
+                 isc_boolean_t need_sig)
 {
        dns_rbtnode_t *node;
        rdatasetheader_t *header, *header_next, *found, *foundsig;
@@ -2751,7 +3064,21 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
        isc_result_t result;
        dns_fixedname_t fname, forigin;
        dns_name_t *name, *origin;
+       dns_rdatatype_t type;
+       rbtdb_rdatatype_t sigtype;
+       isc_boolean_t wraps;
 
+       if (tree == search->rbtdb->nsec3) {
+               type = dns_rdatatype_nsec3;
+               sigtype = RBTDB_RDATATYPE_SIGNSEC3;
+               wraps = ISC_TRUE;
+       } else {
+               type = dns_rdatatype_nsec;
+               sigtype = RBTDB_RDATATYPE_SIGNSEC;
+               wraps = ISC_FALSE;
+       }
+
+ again:
        do {
                node = NULL;
                dns_fixedname_init(&fname);
@@ -2793,12 +3120,11 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
                                 * active rdataset at this node.
                                 */
                                empty_node = ISC_FALSE;
-                               if (header->type == dns_rdatatype_nsec) {
+                               if (header->type == type) {
                                        found = header;
                                        if (foundsig != NULL)
                                                break;
-                               } else if (header->type ==
-                                          RBTDB_RDATATYPE_SIGNSEC) {
+                               } else if (header->type == sigtype) {
                                        foundsig = header;
                                        if (found != NULL)
                                                break;
@@ -2806,11 +3132,19 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
                        }
                }
                if (!empty_node) {
-                       if (found != NULL &&
-                           (foundsig != NULL || !need_sig))
+                       if (found != NULL && search->rbtversion->havensec3 &&
+                           found->type == dns_rdatatype_nsec3 &&
+                           !matchparams(found, search)) {
+                               empty_node = ISC_TRUE;
+                               found = NULL;
+                               foundsig = NULL;
+                               result = dns_rbtnodechain_prev(&search->chain,
+                                                              NULL, NULL);
+                       } else if (found != NULL &&
+                                  (foundsig != NULL || !need_sig))
                        {
                                /*
-                                * We've found the right NSEC record.
+                                * We've found the right NSEC/NSEC3 record.
                                 *
                                 * Note: for this to really be the right
                                 * NSEC record, it's essential that the NSEC
@@ -2867,6 +3201,15 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
                            isc_rwlocktype_read);
        } while (empty_node && result == ISC_R_SUCCESS);
 
+       if (result == ISC_R_NOMORE && wraps) {
+               result = dns_rbtnodechain_last(&search->chain, tree,
+                                              NULL, NULL);
+               if (result == ISC_R_SUCCESS) {
+                       wraps = ISC_FALSE;
+                       goto again;
+               }
+       }
+
        /*
         * If the result is ISC_R_NOMORE, then we got to the beginning of
         * the database and didn't find a NSEC record.  This shouldn't
@@ -2899,7 +3242,7 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
        isc_boolean_t active;
        dns_rbtnodechain_t chain;
        nodelock_t *lock;
-
+       dns_rbt_t *tree;
 
        search.rbtdb = (dns_rbtdb_t *)db;
 
@@ -2942,7 +3285,9 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
         * encounter a callback node, zone_zonecut_callback() will search the
         * rdatasets at the zone cut for active DNAME or NS rdatasets.
         */
-       result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node,
+       tree =  (options & DNS_DBFIND_FORCENSEC3) != 0 ? search.rbtdb->nsec3 :
+                                                        search.rbtdb->tree;
+       result = dns_rbt_findnode(tree, name, foundname, &node,
                                  &search.chain, DNS_RBTFIND_EMPTYDATA,
                                  zone_zonecut_callback, &search);
 
@@ -2980,12 +3325,14 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                 * If we're here, then the name does not exist, is not
                 * beneath a zonecut, and there's no matching wildcard.
                 */
-               if (search.rbtdb->secure == dns_db_secure ||
-                   (search.options & DNS_DBFIND_FORCENSEC) != 0)
+               if ((search.rbtversion->secure &&
+                    !search.rbtversion->havensec3) ||
+                   (search.options & DNS_DBFIND_FORCENSEC) != 0 ||
+                   (search.options & DNS_DBFIND_FORCENSEC3) != 0)
                {
                        result = find_closest_nsec(&search, nodep, foundname,
-                                                  rdataset, sigrdataset,
-                                                  ISC_TRUE);
+                                                  rdataset, sigrdataset, tree,
+                                                  search.rbtversion->secure);
                        if (result == ISC_R_SUCCESS)
                                result = active ? DNS_R_EMPTYNAME :
                                                  DNS_R_NXDOMAIN;
@@ -3106,6 +3453,14 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                                        break;
                        }
 
+
+                       /*
+                        * If the NSEC3 record doesn't match the chain
+                        * we are using behave as if it isn't here.
+                        */
+                       if (header->type == dns_rdatatype_nsec3 &&
+                           !matchparams(header, &search))
+                               goto partial_match;
                        /*
                         * If we found a type we were looking for,
                         * remember it.
@@ -3150,14 +3505,16 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                                 */
                                if (!maybe_zonecut && found != NULL)
                                        break;
-                       } else if (header->type == dns_rdatatype_nsec) {
+                       } else if (header->type == dns_rdatatype_nsec &&
+                                  !search.rbtversion->havensec3) {
                                /*
                                 * Remember a NSEC rdataset even if we're
                                 * not specifically looking for it, because
                                 * we might need it later.
                                 */
                                nsecheader = header;
-                       } else if (header->type == RBTDB_RDATATYPE_SIGNSEC) {
+                       } else if (header->type == RBTDB_RDATATYPE_SIGNSEC &&
+                                  !search.rbtversion->havensec3) {
                                /*
                                 * If we need the NSEC rdataset, we'll also
                                 * need its signature.
@@ -3209,7 +3566,8 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                 * The desired type doesn't exist.
                 */
                result = DNS_R_NXRRSET;
-               if (search.rbtdb->secure == dns_db_secure &&
+               if (search.rbtversion->secure &&
+                   !search.rbtversion->havensec3 &&
                    (nsecheader == NULL || nsecsig == NULL)) {
                        /*
                         * The zone is secure but there's no NSEC,
@@ -3224,8 +3582,8 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                        NODE_UNLOCK(lock, isc_rwlocktype_read);
                        result = find_closest_nsec(&search, nodep, foundname,
                                                   rdataset, sigrdataset,
-                                                  search.rbtdb->secure ==
-                                                  dns_db_secure);
+                                                  search.rbtdb->tree,
+                                                  search.rbtversion->secure);
                        if (result == ISC_R_SUCCESS)
                                result = DNS_R_EMPTYWILD;
                        goto tree_exit;
@@ -3244,7 +3602,8 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                        new_reference(search.rbtdb, node);
                        *nodep = node;
                }
-               if (search.rbtdb->secure == dns_db_secure ||
+               if ((search.rbtversion->secure &&
+                    !search.rbtversion->havensec3) ||
                    (search.options & DNS_DBFIND_FORCENSEC) != 0)
                {
                        bind_rdataset(search.rbtdb, node, nsecheader,
@@ -3285,6 +3644,7 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                         * validated updates.
                         */
                        if (type == dns_rdatatype_nsec ||
+                           type == dns_rdatatype_nsec3 ||
                            type == dns_rdatatype_key)
                                result = ISC_R_SUCCESS;
                        else if (type == dns_rdatatype_any)
@@ -4523,8 +4883,7 @@ printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
 }
 
 static isc_result_t
-createiterator(dns_db_t *db, isc_boolean_t relative_names,
-              dns_dbiterator_t **iteratorp)
+createiterator(dns_db_t *db, unsigned int options, dns_dbiterator_t **iteratorp)
 {
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
        rbtdb_dbiterator_t *rbtdbiter;
@@ -4538,7 +4897,8 @@ createiterator(dns_db_t *db, isc_boolean_t relative_names,
        rbtdbiter->common.methods = &dbiterator_methods;
        rbtdbiter->common.db = NULL;
        dns_db_attach(db, &rbtdbiter->common.db);
-       rbtdbiter->common.relative_names = relative_names;
+       rbtdbiter->common.relative_names =
+                       ISC_TF((options & DNS_DB_RELATIVENAMES) != 0);
        rbtdbiter->common.magic = DNS_DBITERATOR_MAGIC;
        rbtdbiter->common.cleaning = ISC_FALSE;
        rbtdbiter->paused = ISC_TRUE;
@@ -4548,8 +4908,15 @@ createiterator(dns_db_t *db, isc_boolean_t relative_names,
        dns_fixedname_init(&rbtdbiter->origin);
        rbtdbiter->node = NULL;
        rbtdbiter->delete = 0;
+       rbtdbiter->nsec3only = ISC_TF((options & DNS_DB_NSEC3ONLY) != 0);
+       rbtdbiter->nonsec3 = ISC_TF((options & DNS_DB_NONSEC3) != 0);
        memset(rbtdbiter->deletions, 0, sizeof(rbtdbiter->deletions));
        dns_rbtnodechain_init(&rbtdbiter->chain, db->mctx);
+       dns_rbtnodechain_init(&rbtdbiter->nsec3chain, db->mctx);
+       if (rbtdbiter->nsec3only)
+               rbtdbiter->current = &rbtdbiter->nsec3chain;
+       else
+               rbtdbiter->current = &rbtdbiter->chain;
 
        *iteratorp = (dns_dbiterator_t *)rbtdbiter;
 
@@ -5132,6 +5499,11 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                                header->noqname = newheader->noqname;
                                newheader->noqname = NULL;
                        }
+                       if (header->closest == NULL &&
+                           newheader->closest != NULL) {
+                               header->closest = newheader->closest;
+                               newheader->closest = NULL;
+                       }
                        free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
                        if (addedrdataset != NULL)
                                bind_rdataset(rbtdb, rbtnode, header, now,
@@ -5157,6 +5529,11 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                                header->noqname = newheader->noqname;
                                newheader->noqname = NULL;
                        }
+                       if (header->closest == NULL &&
+                           newheader->closest != NULL) {
+                               header->closest = newheader->closest;
+                               newheader->closest = NULL;
+                       }
                        free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
                        if (addedrdataset != NULL)
                                bind_rdataset(rbtdb, rbtnode, header, now,
@@ -5294,15 +5671,15 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
        struct noqname *noqname;
        isc_mem_t *mctx = rbtdb->common.mctx;
        dns_name_t name;
-       dns_rdataset_t nsec, nsecsig;
+       dns_rdataset_t neg, negsig;
        isc_result_t result;
        isc_region_t r;
 
        dns_name_init(&name, NULL);
-       dns_rdataset_init(&nsec);
-       dns_rdataset_init(&nsecsig);
+       dns_rdataset_init(&neg);
+       dns_rdataset_init(&negsig);
 
-       result = dns_rdataset_getnoqname(rdataset, &name, &nsec, &nsecsig);
+       result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
        noqname = isc_mem_get(mctx, sizeof(*noqname));
@@ -5311,31 +5688,84 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
                goto cleanup;
        }
        dns_name_init(&noqname->name, NULL);
-       noqname->nsec = NULL;
-       noqname->nsecsig = NULL;
+       noqname->neg = NULL;
+       noqname->negsig = NULL;
+       noqname->type = neg.type;
        result = dns_name_dup(&name, mctx, &noqname->name);
        if (result != ISC_R_SUCCESS)
                goto cleanup;
-       result = dns_rdataslab_fromrdataset(&nsec, mctx, &r, 0);
+       result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0);
        if (result != ISC_R_SUCCESS)
                goto cleanup;
-       noqname->nsec = r.base;
-       result = dns_rdataslab_fromrdataset(&nsecsig, mctx, &r, 0);
+       noqname->neg = r.base;
+       result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0);
        if (result != ISC_R_SUCCESS)
                goto cleanup;
-       noqname->nsecsig = r.base;
-       dns_rdataset_disassociate(&nsec);
-       dns_rdataset_disassociate(&nsecsig);
+       noqname->negsig = r.base;
+       dns_rdataset_disassociate(&neg);
+       dns_rdataset_disassociate(&negsig);
        newheader->noqname = noqname;
        return (ISC_R_SUCCESS);
 
 cleanup:
-       dns_rdataset_disassociate(&nsec);
-       dns_rdataset_disassociate(&nsecsig);
+       dns_rdataset_disassociate(&neg);
+       dns_rdataset_disassociate(&negsig);
        free_noqname(mctx, &noqname);
        return(result);
 }
 
+static inline isc_result_t
+addclosest(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
+          dns_rdataset_t *rdataset)
+{
+       struct noqname *closest;
+       isc_mem_t *mctx = rbtdb->common.mctx;
+       dns_name_t name;
+       dns_rdataset_t neg, negsig;
+       isc_result_t result;
+       isc_region_t r;
+
+       dns_name_init(&name, NULL);
+       dns_rdataset_init(&neg);
+       dns_rdataset_init(&negsig);
+
+       result = dns_rdataset_getclosest(rdataset, &name, &neg, &negsig);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+       closest = isc_mem_get(mctx, sizeof(*closest));
+       if (closest == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+       dns_name_init(&closest->name, NULL);
+       closest->neg = NULL;
+       closest->negsig = NULL;
+       closest->type = neg.type;
+       result = dns_name_dup(&name, mctx, &closest->name);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       closest->neg = r.base;
+       result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       closest->negsig = r.base;
+       dns_rdataset_disassociate(&neg);
+       dns_rdataset_disassociate(&negsig);
+       newheader->closest = closest;
+       return (ISC_R_SUCCESS);
+
+ cleanup:
+       dns_rdataset_disassociate(&neg);
+       dns_rdataset_disassociate(&negsig);
+       free_noqname(mctx, &closest);
+       return(result);
+}
+
+static dns_dbmethods_t zone_methods;
+
 static isc_result_t
 addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
            isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
@@ -5352,6 +5782,14 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
+       if (rbtdb->common.methods == &zone_methods)
+               REQUIRE(((rbtnode->nsec3 &&
+                         (rdataset->type == dns_rdatatype_nsec3 ||
+                          rdataset->covers == dns_rdatatype_nsec3)) ||
+                        (!rbtnode->nsec3 &&
+                          rdataset->type != dns_rdatatype_nsec3 &&
+                          rdataset->covers != dns_rdatatype_nsec3)));
+
        if (rbtversion == NULL) {
                if (now == 0)
                        isc_stdtime_get(&now);
@@ -5371,6 +5809,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                                                rdataset->covers);
        newheader->attributes = 0;
        newheader->noqname = NULL;
+       newheader->closest = NULL;
        newheader->count = init_count++;
        newheader->trust = rdataset->trust;
        newheader->additional_auth = NULL;
@@ -5380,6 +5819,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        if (rbtversion != NULL) {
                newheader->serial = rbtversion->serial;
                now = 0;
+
                if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
                        newheader->attributes |= RDATASET_ATTR_RESIGN;
                        newheader->resign = rdataset->resign;
@@ -5390,6 +5830,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                newheader->resign = 0;
                if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
                        newheader->attributes |= RDATASET_ATTR_NXDOMAIN;
+               if ((rdataset->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
+                       newheader->attributes |= RDATASET_ATTR_OPTOUT;
                if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) {
                        result = addnoqname(rbtdb, newheader, rdataset);
                        if (result != ISC_R_SUCCESS) {
@@ -5398,6 +5840,14 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                                return (result);
                        }
                }
+               if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) != 0) {
+                       result = addclosest(rbtdb, newheader, rdataset);
+                       if (result != ISC_R_SUCCESS) {
+                               free_rdataset(rbtdb, rbtdb->common.mctx,
+                                             newheader);
+                               return (result);
+                       }
+               }
        }
 
        /*
@@ -5461,7 +5911,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
         * this is defered until closeversion() is called.
         */
        if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
-               rbtdb->secure = iszonesecure(db, rbtdb->origin_node);
+               iszonesecure(db, version, rbtdb->origin_node);
 
        return (result);
 }
@@ -5482,6 +5932,14 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
+       if (rbtdb->common.methods == &zone_methods)
+               REQUIRE(((rbtnode->nsec3 &&
+                         (rdataset->type == dns_rdatatype_nsec3 ||
+                          rdataset->covers == dns_rdatatype_nsec3)) ||
+                        (!rbtnode->nsec3 &&
+                          rdataset->type != dns_rdatatype_nsec3 &&
+                          rdataset->covers != dns_rdatatype_nsec3)));
+
        result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx,
                                            &region,
                                            sizeof(rdatasetheader_t));
@@ -5496,6 +5954,7 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        newheader->serial = rbtversion->serial;
        newheader->trust = 0;
        newheader->noqname = NULL;
+       newheader->closest = NULL;
        newheader->count = init_count++;
        newheader->additional_auth = NULL;
        newheader->additional_glue = NULL;
@@ -5586,6 +6045,7 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                        newheader->trust = 0;
                        newheader->serial = rbtversion->serial;
                        newheader->noqname = NULL;
+                       newheader->closest = NULL;
                        newheader->count = 0;
                        newheader->additional_auth = NULL;
                        newheader->additional_glue = NULL;
@@ -5635,7 +6095,7 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
         * this is defered until closeversion() is called.
         */
        if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
-               rbtdb->secure = iszonesecure(db, rbtdb->origin_node);
+               iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
 
        return (result);
 }
@@ -5665,6 +6125,7 @@ deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        newheader->attributes = RDATASET_ATTR_NONEXISTENT;
        newheader->trust = 0;
        newheader->noqname = NULL;
+       newheader->closest = NULL;
        newheader->additional_auth = NULL;
        newheader->additional_glue = NULL;
        if (rbtversion != NULL)
@@ -5689,7 +6150,7 @@ deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
         * this is defered until closeversion() is called.
         */
        if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
-               rbtdb->secure = iszonesecure(db, rbtdb->origin_node);
+               iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
 
        return (result);
 }
@@ -5717,7 +6178,9 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) {
            !IS_CACHE(rbtdb) && !dns_name_equal(name, &rbtdb->common.origin))
                return (DNS_R_NOTZONETOP);
 
-       add_empty_wildcards(rbtdb, name);
+       if (rdataset->type != dns_rdatatype_nsec3 &&
+           rdataset->covers != dns_rdatatype_nsec3)
+               add_empty_wildcards(rbtdb, name);
 
        if (dns_name_iswildcard(name)) {
                /*
@@ -5725,13 +6188,27 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) {
                 */
                if (rdataset->type == dns_rdatatype_ns)
                        return (DNS_R_INVALIDNS);
+               /*
+                * NSEC3 record owners cannot legally be wild cards.
+                */
+               if (rdataset->type == dns_rdatatype_nsec3)
+                       return (DNS_R_INVALIDNSEC3);
                result = add_wildcard_magic(rbtdb, name);
                if (result != ISC_R_SUCCESS)
                        return (result);
        }
 
        node = NULL;
-       result = dns_rbt_addnode(rbtdb->tree, name, &node);
+       if (rdataset->type == dns_rdatatype_nsec3 ||
+           rdataset->covers == dns_rdatatype_nsec3) {
+               result = dns_rbt_addnode(rbtdb->nsec3, name, &node);
+               if (result == ISC_R_SUCCESS)
+                       node->nsec3 = 1;
+       } else {
+               result = dns_rbt_addnode(rbtdb->tree, name, &node);
+               if (result == ISC_R_SUCCESS)
+                       node->nsec3 = 0;
+       }
        if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
                return (result);
        if (result != ISC_R_EXISTS) {
@@ -5761,6 +6238,7 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) {
        newheader->trust = rdataset->trust;
        newheader->serial = 1;
        newheader->noqname = NULL;
+       newheader->closest = NULL;
        newheader->count = init_count++;
        newheader->additional_auth = NULL;
        newheader->additional_glue = NULL;
@@ -5841,7 +6319,7 @@ endload(dns_db_t *db, dns_dbload_t **dbloadp) {
         * zone key, we consider the zone secure.
         */
        if (! IS_CACHE(rbtdb))
-               rbtdb->secure = iszonesecure(db, rbtdb->origin_node);
+               iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
 
        *dbloadp = NULL;
 
@@ -5885,7 +6363,7 @@ issecure(dns_db_t *db) {
        REQUIRE(VALID_RBTDB(rbtdb));
 
        RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-       secure = ISC_TF(rbtdb->secure == dns_db_secure);
+       secure = ISC_TF(rbtdb->current_version->secure == dns_db_secure);
        RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
 
        return (secure);
@@ -5901,7 +6379,7 @@ isdnssec(dns_db_t *db) {
        REQUIRE(VALID_RBTDB(rbtdb));
 
        RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-       dnssec = ISC_TF(rbtdb->secure != dns_db_insecure);
+       dnssec = ISC_TF(rbtdb->current_version->secure != dns_db_insecure);
        RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
 
        return (dnssec);
@@ -5963,13 +6441,51 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
 
                *nodep = rbtdb->origin_node;
        } else {
-               INSIST(!IS_CACHE(rbtdb));
+               INSIST(IS_CACHE(rbtdb));
                result = ISC_R_NOTFOUND;
        }
 
        return (result);
 }
 
+static isc_result_t
+getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
+                  isc_uint8_t *flags, isc_uint16_t *iterations,
+                  unsigned char *salt, size_t *salt_length)
+{
+       dns_rbtdb_t *rbtdb;
+       isc_result_t result = ISC_R_NOTFOUND;
+       rbtdb_version_t *rbtversion = version;
+
+       rbtdb = (dns_rbtdb_t *)db;
+
+       REQUIRE(VALID_RBTDB(rbtdb));
+
+       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+       if (rbtversion == NULL)
+               rbtversion = rbtdb->current_version;
+
+       if (rbtversion->havensec3) {
+               if (hash != NULL)
+                       *hash = rbtversion->hash;
+               if (salt != NULL && salt_length != 0) {
+                       REQUIRE(*salt_length > rbtversion->salt_length);
+                       memcpy(salt, rbtversion->salt, rbtversion->salt_length);
+               }
+               if (salt_length != NULL)
+                       *salt_length = rbtversion->salt_length;
+               if (iterations != NULL)
+                       *iterations = rbtversion->iterations;
+               if (flags != NULL)
+                       *flags = rbtversion->flags;
+               result = ISC_R_SUCCESS;
+       }
+       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+       return (result);
+}
+
 static isc_result_t
 setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
@@ -6129,6 +6645,8 @@ static dns_dbmethods_t zone_methods = {
        settask,
        getoriginnode,
        NULL,
+       getnsec3parameters,
+       findnsec3node,
        setsigningtime,
        getsigningtime,
        resigned,
@@ -6169,6 +6687,8 @@ static dns_dbmethods_t cache_methods = {
        NULL,
        NULL,
        NULL,
+       NULL,
+       NULL,
        isdnssec,
        getrrsetstats
 };
@@ -6323,13 +6843,20 @@ dns_rbtdb_create
        }
 
        /*
-        * Make the Red-Black Tree.
+        * Make the Red-Black Trees.
         */
        result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->tree);
        if (result != ISC_R_SUCCESS) {
                free_rbtdb(rbtdb, ISC_FALSE, NULL);
                return (result);
        }
+
+       result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec3);
+       if (result != ISC_R_SUCCESS) {
+               free_rbtdb(rbtdb, ISC_FALSE, NULL);
+               return (result);
+       }
+
        /*
         * In order to set the node callback bit correctly in zone databases,
         * we need to know if the node has the origin name of the zone.
@@ -6352,6 +6879,7 @@ dns_rbtdb_create
                        free_rbtdb(rbtdb, ISC_FALSE, NULL);
                        return (result);
                }
+               rbtdb->origin_node->nsec3 = 0;
                /*
                 * We need to give the origin node the right locknum.
                 */
@@ -6377,7 +6905,6 @@ dns_rbtdb_create
                return (result);
        }
        rbtdb->attributes = 0;
-       rbtdb->secure = dns_db_insecure;
        rbtdb->overmem = ISC_FALSE;
        rbtdb->task = NULL;
 
@@ -6394,6 +6921,14 @@ dns_rbtdb_create
                free_rbtdb(rbtdb, ISC_FALSE, NULL);
                return (ISC_R_NOMEMORY);
        }
+       rbtdb->current_version->secure = ISC_FALSE;
+       rbtdb->current_version->havensec3 = ISC_FALSE;
+       rbtdb->current_version->flags = 0;
+       rbtdb->current_version->iterations = 0;
+       rbtdb->current_version->hash = 0;
+       rbtdb->current_version->salt_length = 0;
+       memset(rbtdb->current_version->salt, 0,
+              sizeof(rbtdb->current_version->salt));
        rbtdb->future_version = NULL;
        ISC_LIST_INIT(rbtdb->open_versions);
        /*
@@ -6604,37 +7139,85 @@ rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
        attachnode(db, node, &cloned_node);
        nsec->methods = &rdataset_methods;
        nsec->rdclass = db->rdclass;
-       nsec->type = dns_rdatatype_nsec;
+       nsec->type = noqname->type;
        nsec->covers = 0;
        nsec->ttl = rdataset->ttl;
        nsec->trust = rdataset->trust;
        nsec->private1 = rdataset->private1;
        nsec->private2 = rdataset->private2;
-       nsec->private3 = noqname->nsec;
+       nsec->private3 = noqname->neg;
        nsec->privateuint4 = 0;
        nsec->private5 = NULL;
        nsec->private6 = NULL;
+       nsec->private7 = NULL;
 
        cloned_node = NULL;
        attachnode(db, node, &cloned_node);
        nsecsig->methods = &rdataset_methods;
        nsecsig->rdclass = db->rdclass;
        nsecsig->type = dns_rdatatype_rrsig;
-       nsecsig->covers = dns_rdatatype_nsec;
+       nsecsig->covers = noqname->type;
        nsecsig->ttl = rdataset->ttl;
        nsecsig->trust = rdataset->trust;
        nsecsig->private1 = rdataset->private1;
        nsecsig->private2 = rdataset->private2;
-       nsecsig->private3 = noqname->nsecsig;
+       nsecsig->private3 = noqname->negsig;
        nsecsig->privateuint4 = 0;
        nsecsig->private5 = NULL;
        nsec->private6 = NULL;
+       nsec->private7 = NULL;
 
        dns_name_clone(&noqname->name, name);
 
        return (ISC_R_SUCCESS);
 }
 
+static isc_result_t
+rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+                   dns_rdataset_t *nsec, dns_rdataset_t *nsecsig)
+{
+       dns_db_t *db = rdataset->private1;
+       dns_dbnode_t *node = rdataset->private2;
+       dns_dbnode_t *cloned_node;
+       struct noqname *closest = rdataset->private7;
+
+       cloned_node = NULL;
+       attachnode(db, node, &cloned_node);
+       nsec->methods = &rdataset_methods;
+       nsec->rdclass = db->rdclass;
+       nsec->type = closest->type;
+       nsec->covers = 0;
+       nsec->ttl = rdataset->ttl;
+       nsec->trust = rdataset->trust;
+       nsec->private1 = rdataset->private1;
+       nsec->private2 = rdataset->private2;
+       nsec->private3 = closest->neg;
+       nsec->privateuint4 = 0;
+       nsec->private5 = NULL;
+       nsec->private6 = NULL;
+       nsec->private7 = NULL;
+
+       cloned_node = NULL;
+       attachnode(db, node, &cloned_node);
+       nsecsig->methods = &rdataset_methods;
+       nsecsig->rdclass = db->rdclass;
+       nsecsig->type = dns_rdatatype_rrsig;
+       nsecsig->covers = closest->type;
+       nsecsig->ttl = rdataset->ttl;
+       nsecsig->trust = rdataset->trust;
+       nsecsig->private1 = rdataset->private1;
+       nsecsig->private2 = rdataset->private2;
+       nsecsig->private3 = closest->negsig;
+       nsecsig->privateuint4 = 0;
+       nsecsig->private5 = NULL;
+       nsec->private6 = NULL;
+       nsec->private7 = NULL;
+
+       dns_name_clone(&closest->name, name);
+
+       return (ISC_R_SUCCESS);
+}
+
 /*
  * Rdataset Iterator Methods
  */
@@ -6685,8 +7268,8 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) {
                                 * record?  Or is it too old in the cache?
                                 *
                                 * Note: unlike everywhere else, we
-                                * check for now > header->ttl instead
-                                * of now >= header->ttl.  This allows
+                                * check for now > header->rdh_ttl instead
+                                * of now >= header->rdh_ttl.  This allows
                                 * ANY and RRSIG queries for 0 TTL
                                 * rdatasets to work.
                                 */
@@ -6929,6 +7512,7 @@ dbiterator_destroy(dns_dbiterator_t **iteratorp) {
        dns_db_detach(&rbtdbiter->common.db);
 
        dns_rbtnodechain_reset(&rbtdbiter->chain);
+       dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
        isc_mem_put(db->mctx, rbtdbiter, sizeof(*rbtdbiter));
        dns_db_detach(&db);
 
@@ -6954,12 +7538,25 @@ dbiterator_first(dns_dbiterator_t *iterator) {
        name = dns_fixedname_name(&rbtdbiter->name);
        origin = dns_fixedname_name(&rbtdbiter->origin);
        dns_rbtnodechain_reset(&rbtdbiter->chain);
+       dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
 
-       result = dns_rbtnodechain_first(&rbtdbiter->chain, rbtdb->tree, name,
-                                       origin);
-
+       if (rbtdbiter->nsec3only) {
+               rbtdbiter->current = &rbtdbiter->nsec3chain;
+               result = dns_rbtnodechain_first(rbtdbiter->current,
+                                               rbtdb->nsec3, name, origin);
+       } else {
+               rbtdbiter->current = &rbtdbiter->chain;
+               result = dns_rbtnodechain_first(rbtdbiter->current,
+                                               rbtdb->tree, name, origin);
+               if (!rbtdbiter->nonsec3 && result == ISC_R_NOTFOUND) {
+                       rbtdbiter->current = &rbtdbiter->nsec3chain;
+                       result = dns_rbtnodechain_first(rbtdbiter->current,
+                                                       rbtdb->nsec3, name,
+                                                       origin);
+               }
+       }
        if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
-               result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL,
+               result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
                                                  NULL, &rbtdbiter->node);
                if (result == ISC_R_SUCCESS) {
                        rbtdbiter->new_origin = ISC_TRUE;
@@ -6994,11 +7591,21 @@ dbiterator_last(dns_dbiterator_t *iterator) {
        name = dns_fixedname_name(&rbtdbiter->name);
        origin = dns_fixedname_name(&rbtdbiter->origin);
        dns_rbtnodechain_reset(&rbtdbiter->chain);
+       dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
 
-       result = dns_rbtnodechain_last(&rbtdbiter->chain, rbtdb->tree, name,
-                                      origin);
+       result = ISC_R_NOTFOUND;
+       if (rbtdbiter->nsec3only && !rbtdbiter->nonsec3) {
+               rbtdbiter->current = &rbtdbiter->nsec3chain;
+               result = dns_rbtnodechain_last(rbtdbiter->current,
+                                              rbtdb->nsec3, name, origin);
+       }
+       if (!rbtdbiter->nsec3only && result == ISC_R_NOTFOUND) {
+               rbtdbiter->current = &rbtdbiter->chain;
+               result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->tree,
+                                              name, origin);
+       }
        if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
-               result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL,
+               result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
                                                  NULL, &rbtdbiter->node);
                if (result == ISC_R_SUCCESS) {
                        rbtdbiter->new_origin = ISC_TRUE;
@@ -7022,6 +7629,7 @@ dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) {
        dns_name_t *iname, *origin;
 
        if (rbtdbiter->result != ISC_R_SUCCESS &&
+           rbtdbiter->result != ISC_R_NOTFOUND &&
            rbtdbiter->result != ISC_R_NOMORE)
                return (rbtdbiter->result);
 
@@ -7033,22 +7641,74 @@ dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) {
        iname = dns_fixedname_name(&rbtdbiter->name);
        origin = dns_fixedname_name(&rbtdbiter->origin);
        dns_rbtnodechain_reset(&rbtdbiter->chain);
+       dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
+
+       if (rbtdbiter->nsec3only) {
+               rbtdbiter->current = &rbtdbiter->nsec3chain;
+               result = dns_rbt_findnode(rbtdb->nsec3, name, NULL,
+                                         &rbtdbiter->node,
+                                         rbtdbiter->current,
+                                         DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+       } else if (rbtdbiter->nonsec3) {
+               rbtdbiter->current = &rbtdbiter->chain;
+               result = dns_rbt_findnode(rbtdb->tree, name, NULL,
+                                         &rbtdbiter->node,
+                                         rbtdbiter->current,
+                                         DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+       } else {
+               /*
+                * Stay on main chain if not found on either chain.
+                */
+               rbtdbiter->current = &rbtdbiter->chain;
+               result = dns_rbt_findnode(rbtdb->tree, name, NULL,
+                                         &rbtdbiter->node,
+                                         rbtdbiter->current,
+                                         DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+               if (result == DNS_R_PARTIALMATCH) {
+                       dns_rbtnode_t *node = NULL;
+                       result = dns_rbt_findnode(rbtdb->nsec3, name, NULL,
+                                                 &node, &rbtdbiter->nsec3chain,
+                                                 DNS_RBTFIND_EMPTYDATA,
+                                                 NULL, NULL);
+                       if (result == ISC_R_SUCCESS) {
+                               rbtdbiter->node = node;
+                               rbtdbiter->current = &rbtdbiter->nsec3chain;
+                       }
+               }
+       }
 
-       result = dns_rbt_findnode(rbtdb->tree, name, NULL, &rbtdbiter->node,
-                                 &rbtdbiter->chain, DNS_RBTFIND_EMPTYDATA,
-                                 NULL, NULL);
+#if 1
        if (result == ISC_R_SUCCESS) {
-               result = dns_rbtnodechain_current(&rbtdbiter->chain, iname,
+               result = dns_rbtnodechain_current(rbtdbiter->current, iname,
                                                  origin, NULL);
                if (result == ISC_R_SUCCESS) {
                        rbtdbiter->new_origin = ISC_TRUE;
                        reference_iter_node(rbtdbiter);
                }
-
-       } else if (result == DNS_R_PARTIALMATCH)
+       } else if (result == DNS_R_PARTIALMATCH) {
                result = ISC_R_NOTFOUND;
+               rbtdbiter->node = NULL; 
+       }
 
        rbtdbiter->result = result;
+#else
+       if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+               isc_result_t tresult;
+               tresult = dns_rbtnodechain_current(rbtdbiter->current, iname,
+                                                  origin, NULL);
+               if (tresult == ISC_R_SUCCESS) {
+                       rbtdbiter->new_origin = ISC_TRUE;
+                       reference_iter_node(rbtdbiter);
+               } else {
+                       result = tresult;
+                       rbtdbiter->node = NULL;
+               }
+       } else
+               rbtdbiter->node = NULL;
+
+       rbtdbiter->result = (result == DNS_R_PARTIALMATCH) ?
+                           ISC_R_SUCCESS : result;
+#endif
 
        return (result);
 }
@@ -7058,6 +7718,7 @@ dbiterator_prev(dns_dbiterator_t *iterator) {
        isc_result_t result;
        rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
        dns_name_t *name, *origin;
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
 
        REQUIRE(rbtdbiter->node != NULL);
 
@@ -7069,13 +7730,23 @@ dbiterator_prev(dns_dbiterator_t *iterator) {
 
        name = dns_fixedname_name(&rbtdbiter->name);
        origin = dns_fixedname_name(&rbtdbiter->origin);
-       result = dns_rbtnodechain_prev(&rbtdbiter->chain, name, origin);
+       result = dns_rbtnodechain_prev(rbtdbiter->current, name, origin);
+       if (result == ISC_R_NOMORE && !rbtdbiter->nsec3only &&
+           !rbtdbiter->nonsec3 &&
+           &rbtdbiter->nsec3chain == rbtdbiter->current) {
+               rbtdbiter->current = &rbtdbiter->chain;
+               dns_rbtnodechain_reset(rbtdbiter->current);
+               result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->tree,
+                                              name, origin);
+               if (result == ISC_R_NOTFOUND)
+                       result = ISC_R_NOMORE;
+       }
 
        dereference_iter_node(rbtdbiter);
 
        if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
                rbtdbiter->new_origin = ISC_TF(result == DNS_R_NEWORIGIN);
-               result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL,
+               result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
                                                  NULL, &rbtdbiter->node);
        }
 
@@ -7092,6 +7763,7 @@ dbiterator_next(dns_dbiterator_t *iterator) {
        isc_result_t result;
        rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
        dns_name_t *name, *origin;
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
 
        REQUIRE(rbtdbiter->node != NULL);
 
@@ -7103,13 +7775,22 @@ dbiterator_next(dns_dbiterator_t *iterator) {
 
        name = dns_fixedname_name(&rbtdbiter->name);
        origin = dns_fixedname_name(&rbtdbiter->origin);
-       result = dns_rbtnodechain_next(&rbtdbiter->chain, name, origin);
+       result = dns_rbtnodechain_next(rbtdbiter->current, name, origin);
+       if (result == ISC_R_NOMORE && !rbtdbiter->nsec3only &&
+           !rbtdbiter->nonsec3 && &rbtdbiter->chain == rbtdbiter->current) {
+               rbtdbiter->current = &rbtdbiter->nsec3chain;
+               dns_rbtnodechain_reset(rbtdbiter->current);
+               result = dns_rbtnodechain_first(rbtdbiter->current,
+                                               rbtdb->nsec3, name, origin);
+               if (result == ISC_R_NOTFOUND)
+                       result = ISC_R_NOMORE;
+       }
 
        dereference_iter_node(rbtdbiter);
 
        if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
                rbtdbiter->new_origin = ISC_TF(result == DNS_R_NEWORIGIN);
-               result = dns_rbtnodechain_current(&rbtdbiter->chain, NULL,
+               result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
                                                  NULL, &rbtdbiter->node);
        }
        if (result == ISC_R_SUCCESS)
@@ -7485,7 +8166,7 @@ rdataset_setadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type,
 
        return (ISC_R_SUCCESS);
 
 fail:
+ fail:
        if (newcbarg != NULL) {
                if (newentry != NULL) {
                        acache_cancelentry(rbtdb->common.mctx, newentry,
index 135c3eec06545cffa8dbfb4941a8da1ce7ebcf66..1e61ff89eb0eb6a840f69f86d663db5cc1a1aeb4 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rcode.c,v 1.6 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: rcode.c,v 1.7 2008/09/24 02:46:22 marka Exp $ */
 
 #include <config.h>
 #include <ctype.h>
@@ -66,7 +66,7 @@
 #define ERCODENAMES \
        /* extended rcodes */ \
        { dns_rcode_badvers, "BADVERS", 0}, \
-       { 0, NULL, 0 } 
+       { 0, NULL, 0 }
 
 #define TSIGRCODENAMES \
        /* extended rcodes */ \
        { DNS_KEYALG_RSAMD5, "RSA", 0 }, \
        { DNS_KEYALG_DH, "DH", 0 }, \
        { DNS_KEYALG_DSA, "DSA", 0 }, \
+       { DNS_KEYALG_NSEC3DSA, "NSEC3DSA", 0 }, \
        { DNS_KEYALG_ECC, "ECC", 0 }, \
        { DNS_KEYALG_RSASHA1, "RSASHA1", 0 }, \
+       { DNS_KEYALG_NSEC3RSASHA1, "NSEC3RSASHA1", 0 }, \
        { DNS_KEYALG_INDIRECT, "INDIRECT", 0 }, \
        { DNS_KEYALG_PRIVATEDNS, "PRIVATEDNS", 0 }, \
        { DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, \
        { 255,    "ALL", 0 }, \
        { 0, NULL, 0}
 
+#define HASHALGNAMES \
+       { 1, "SHA-1", 0 }, \
+       { 0, NULL, 0 }
+
 struct tbl {
        unsigned int    value;
        const char      *name;
@@ -125,6 +131,7 @@ static struct tbl tsigrcodes[] = { RCODENAMES TSIGRCODENAMES };
 static struct tbl certs[] = { CERTNAMES };
 static struct tbl secalgs[] = { SECALGNAMES };
 static struct tbl secprotos[] = { SECPROTONAMES };
+static struct tbl hashalgs[] = { HASHALGNAMES };
 
 static struct keyflag {
        const char *name;
@@ -238,7 +245,7 @@ dns_mnemonic_fromtext(unsigned int *valuep, isc_textregion_t *source,
 
 static isc_result_t
 dns_mnemonic_totext(unsigned int value, isc_buffer_t *target,
-                   struct tbl *table) 
+                   struct tbl *table)
 {
        int i = 0;
        char buf[sizeof("4294967296")];
@@ -271,7 +278,7 @@ dns_tsigrcode_fromtext(dns_rcode_t *rcodep, isc_textregion_t *source) {
        RETERR(dns_mnemonic_fromtext(&value, source, tsigrcodes, 0xffff));
        *rcodep = value;
        return (ISC_R_SUCCESS);
-} 
+}
 
 isc_result_t
 dns_tsigrcode_totext(dns_rcode_t rcode, isc_buffer_t *target) {
@@ -317,6 +324,14 @@ dns_secproto_totext(dns_secproto_t secproto, isc_buffer_t *target) {
        return (dns_mnemonic_totext(secproto, target, secprotos));
 }
 
+isc_result_t
+dns_hashalg_fromtext(unsigned char *hashalg, isc_textregion_t *source) {
+       unsigned int value;
+       RETERR(dns_mnemonic_fromtext(&value, source, hashalgs, 0xff));
+       *hashalg = value;
+       return (ISC_R_SUCCESS);
+}
+
 isc_result_t
 dns_keyflags_fromtext(dns_keyflags_t *flagsp, isc_textregion_t *source)
 {
index 3366dd607312eabcec7c23d96dfdfe3e62a905bb..d35900b4f9ff40df2a01afdc0ea1262395158cf2 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdata.c,v 1.198 2008/04/01 23:47:10 tbox Exp $ */
+/* $Id: rdata.c,v 1.199 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -162,6 +162,9 @@ uint16_fromregion(isc_region_t *region);
 static isc_uint8_t
 uint8_fromregion(isc_region_t *region);
 
+static isc_uint8_t
+uint8_consume_fromregion(isc_region_t *region);
+
 static isc_result_t
 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
 
@@ -201,6 +204,9 @@ static void
 warn_badmx(isc_token_t *token, isc_lex_t *lexer,
           dns_rdatacallbacks_t *callbacks);
 
+static isc_uint16_t
+uint16_consume_fromregion(isc_region_t *region);
+
 static inline int
 getquad(const void *src, struct in_addr *dst,
        isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks)
@@ -1234,6 +1240,14 @@ uint32_fromregion(isc_region_t *region) {
        return(value);
 }
 
+static isc_uint16_t
+uint16_consume_fromregion(isc_region_t *region) {
+       isc_uint16_t r = uint16_fromregion(region);
+
+       isc_region_consume(region, 2);
+       return r;
+}
+
 static isc_uint16_t
 uint16_fromregion(isc_region_t *region) {
 
@@ -1250,6 +1264,14 @@ uint8_fromregion(isc_region_t *region) {
        return (region->base[0]);
 }
 
+static isc_uint8_t
+uint8_consume_fromregion(isc_region_t *region) {
+       isc_uint8_t r = uint8_fromregion(region);
+
+       isc_region_consume(region, 1);
+       return r;
+}
+
 static isc_result_t
 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
        isc_region_t tr;
diff --git a/lib/dns/rdata/generic/nsec3_50.c b/lib/dns/rdata/generic/nsec3_50.c
new file mode 100644 (file)
index 0000000..89d34f9
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+/*
+ * Copyright (C) 2004  Nominet, Ltd.
+ *
+ * Permission to use, copy, modify, and 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 NOMINET 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.
+ */
+
+/* RFC 5155 */
+
+#ifndef RDATA_GENERIC_NSEC3_50_C
+#define RDATA_GENERIC_NSEC3_50_C
+
+#include <isc/iterated_hash.h>
+#include <isc/base32.h>
+
+#define RRTYPE_NSEC3_ATTRIBUTES DNS_RDATATYPEATTR_DNSSEC
+
+static inline isc_result_t
+fromtext_nsec3(ARGS_FROMTEXT) {
+       isc_token_t token;
+       unsigned char bm[8*1024]; /* 64k bits */
+       dns_rdatatype_t covered;
+       int octet;
+       int window;
+       unsigned int flags;
+       unsigned char hashalg;
+       isc_buffer_t b;
+
+       REQUIRE(type == 50);
+
+       UNUSED(type);
+       UNUSED(rdclass);
+       UNUSED(callbacks);
+       UNUSED(origin);
+       UNUSED(options);
+
+       /* Hash. */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+                                     ISC_FALSE));
+       RETTOK(dns_hashalg_fromtext(&hashalg, &token.value.as_textregion));
+       RETERR(uint8_tobuffer(hashalg, target));
+
+       /* Flags. */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+                                     ISC_FALSE));
+       flags = token.value.as_ulong;
+       if (flags > 255U)
+               RETTOK(ISC_R_RANGE);
+       RETERR(uint8_tobuffer(flags, target));
+
+       /* Iterations. */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+                                     ISC_FALSE));
+       if (token.value.as_ulong > 0xffffU)
+               RETTOK(ISC_R_RANGE);
+       RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+       /* salt */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+                                     ISC_FALSE));
+       if (token.value.as_textregion.length > (255*2))
+               RETTOK(DNS_R_TEXTTOOLONG);
+       if (strcmp(DNS_AS_STR(token), "-") == 0) {
+               RETERR(uint8_tobuffer(0, target));
+       } else {
+               RETERR(uint8_tobuffer(strlen(DNS_AS_STR(token)) / 2, target));
+               RETERR(isc_hex_decodestring(DNS_AS_STR(token), target));
+       }
+
+       /*
+        * Next hash a single base32hex word.
+        */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+                                     ISC_FALSE));
+       isc_buffer_init(&b, bm, sizeof(bm));
+       RETTOK(isc_base32hex_decodestring(DNS_AS_STR(token), &b));
+       if (isc_buffer_usedlength(&b) > 0xffU)
+               RETTOK(ISC_R_RANGE);
+       RETERR(uint8_tobuffer(isc_buffer_usedlength(&b), target));
+       RETERR(mem_tobuffer(target, &bm, isc_buffer_usedlength(&b)));
+
+       memset(bm, 0, sizeof(bm));
+       do {
+               RETERR(isc_lex_getmastertoken(lexer, &token,
+                                             isc_tokentype_string, ISC_TRUE));
+               if (token.type != isc_tokentype_string)
+                       break;
+               RETTOK(dns_rdatatype_fromtext(&covered,
+                                             &token.value.as_textregion));
+               bm[covered/8] |= (0x80>>(covered%8));
+       } while (1);
+       isc_lex_ungettoken(lexer, &token);
+       for (window = 0; window < 256 ; window++) {
+               /*
+                * Find if we have a type in this window.
+                */
+               for (octet = 31; octet >= 0; octet--)
+                       if (bm[window * 32 + octet] != 0)
+                               break;
+               if (octet < 0)
+                       continue;
+               RETERR(uint8_tobuffer(window, target));
+               RETERR(uint8_tobuffer(octet + 1, target));
+               RETERR(mem_tobuffer(target, &bm[window * 32], octet + 1));
+       }
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_nsec3(ARGS_TOTEXT) {
+       isc_region_t sr;
+       unsigned int i, j, k;
+       unsigned int window, len;
+       unsigned char hash;
+       unsigned char flags;
+       char buf[sizeof("65535 ")];
+       isc_uint32_t iterations;
+
+       REQUIRE(rdata->type == 50);
+       REQUIRE(rdata->length != 0);
+
+       UNUSED(tctx);
+
+       dns_rdata_toregion(rdata, &sr);
+
+       hash = uint8_fromregion(&sr);
+       isc_region_consume(&sr, 1);
+
+       flags = uint8_fromregion(&sr);
+       isc_region_consume(&sr, 1);
+
+       iterations = uint16_fromregion(&sr);
+       isc_region_consume(&sr, 2);
+
+       sprintf(buf, "%u ", hash);
+       RETERR(str_totext(buf, target));
+
+       sprintf(buf, "%u ", flags);
+       RETERR(str_totext(buf, target));
+
+       sprintf(buf, "%u ", iterations);
+       RETERR(str_totext(buf, target));
+
+       j = uint8_fromregion(&sr);
+       isc_region_consume(&sr, 1);
+       INSIST(j <= sr.length);
+       
+       if (j != 0) {
+               i = sr.length;
+               sr.length = j;
+               RETERR(isc_hex_totext(&sr, 1, "", target));
+               sr.length = i - j;
+               RETERR(str_totext(" ", target));
+       } else
+               RETERR(str_totext("- ", target));
+
+       j = uint8_fromregion(&sr);
+       isc_region_consume(&sr, 1);
+       INSIST(j <= sr.length);
+
+       i = sr.length;
+       sr.length = j;
+       RETERR(isc_base32hex_totext(&sr, 1, "", target));
+       sr.length = i - j;
+
+       for (i = 0; i < sr.length; i += len) {
+               INSIST(i + 2 <= sr.length);
+               window = sr.base[i];
+               len = sr.base[i + 1];
+               INSIST(len > 0 && len <= 32);
+               i += 2;
+               INSIST(i + len <= sr.length);
+               for (j = 0; j < len; j++) {
+                       dns_rdatatype_t t;
+                       if (sr.base[i + j] == 0)
+                               continue;
+                       for (k = 0; k < 8; k++) {
+                               if ((sr.base[i + j] & (0x80 >> k)) == 0)
+                                       continue;
+                               t = window * 256 + j * 8 + k;
+                               RETERR(str_totext(" ", target));
+                               if (dns_rdatatype_isknown(t)) {
+                                       RETERR(dns_rdatatype_totext(t, target));
+                               } else {
+                                       char buf[sizeof("TYPE65535")];
+                                       sprintf(buf, "TYPE%u", t);
+                                       RETERR(str_totext(buf, target));
+                               }
+                       }
+               }
+       }
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_nsec3(ARGS_FROMWIRE) {
+       isc_region_t sr, rr;
+       unsigned int window, lastwindow = 0;
+       unsigned int len;
+       unsigned int saltlen, hashlen;
+       isc_boolean_t first = ISC_TRUE;
+       unsigned int i;
+
+       REQUIRE(type == 50);
+
+       UNUSED(type);
+       UNUSED(rdclass);
+       UNUSED(options);
+       UNUSED(dctx);
+
+       isc_buffer_activeregion(source, &sr);
+       rr = sr;
+
+       /* hash(1), flags(1), interation(2), saltlen(1) */
+       if (sr.length < 5U)
+               RETERR(DNS_R_FORMERR);
+       saltlen = sr.base[4];
+       isc_region_consume(&sr, 5);
+
+       if (sr.length < saltlen)
+               RETERR(DNS_R_FORMERR);
+       isc_region_consume(&sr, saltlen);
+
+       if (sr.length < 1U)
+               RETERR(DNS_R_FORMERR);
+       hashlen = sr.base[0];
+       isc_region_consume(&sr, 1);
+
+       if (sr.length < hashlen)
+               RETERR(DNS_R_FORMERR);
+       isc_region_consume(&sr, hashlen);
+
+       for (i = 0; i < sr.length; i += len) {
+               /*
+                * Check for overflow.
+                */
+               if (i + 2 > sr.length)
+                       RETERR(DNS_R_FORMERR);
+               window = sr.base[i];
+               len = sr.base[i + 1];
+               i += 2;
+               /*
+                * Check that bitmap windows are in the correct order.
+                */
+               if (!first && window <= lastwindow)
+                       RETERR(DNS_R_FORMERR);
+               /*
+                * Check for legal lengths.
+                */
+               if (len < 1 || len > 32)
+                       RETERR(DNS_R_FORMERR);
+               /*
+                * Check for overflow.
+                */
+               if (i + len > sr.length)
+                       RETERR(DNS_R_FORMERR);
+               /*
+                * The last octet of the bitmap must be non zero.
+                */
+               if (sr.base[i + len - 1] == 0)
+                       RETERR(DNS_R_FORMERR);
+               lastwindow = window;
+               first = ISC_FALSE;
+       }
+       if (i != sr.length)
+               return (DNS_R_EXTRADATA);
+       RETERR(mem_tobuffer(target, rr.base, rr.length));
+       isc_buffer_forward(source, rr.length);
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_nsec3(ARGS_TOWIRE) {
+       isc_region_t sr;
+
+       REQUIRE(rdata->type == 50);
+       REQUIRE(rdata->length != 0);
+
+       UNUSED(cctx);
+
+       dns_rdata_toregion(rdata, &sr);
+       return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_nsec3(ARGS_COMPARE) {
+       isc_region_t r1;
+       isc_region_t r2;
+
+       REQUIRE(rdata1->type == rdata2->type);
+       REQUIRE(rdata1->rdclass == rdata2->rdclass);
+       REQUIRE(rdata1->type == 50);
+       REQUIRE(rdata1->length != 0);
+       REQUIRE(rdata2->length != 0);
+
+       dns_rdata_toregion(rdata1, &r1);
+       dns_rdata_toregion(rdata2, &r2);
+       return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_nsec3(ARGS_FROMSTRUCT) {
+       dns_rdata_nsec3_t *nsec3 = source;
+       unsigned int i, len, window, lastwindow = 0;
+       isc_boolean_t first = ISC_TRUE;
+
+       REQUIRE(type == 50);
+       REQUIRE(source != NULL);
+       REQUIRE(nsec3->common.rdtype == type);
+       REQUIRE(nsec3->common.rdclass == rdclass);
+       REQUIRE(nsec3->typebits != NULL || nsec3->len == 0);
+       REQUIRE(nsec3->hash == dns_hash_sha1);
+
+       UNUSED(type);
+       UNUSED(rdclass);
+
+       RETERR(uint8_tobuffer(nsec3->hash, target));
+       RETERR(uint8_tobuffer(nsec3->flags, target));
+       RETERR(uint16_tobuffer(nsec3->iterations, target));
+       RETERR(uint8_tobuffer(nsec3->salt_length, target));
+       RETERR(mem_tobuffer(target, nsec3->salt, nsec3->salt_length));
+       RETERR(uint8_tobuffer(nsec3->next_length, target));
+       RETERR(mem_tobuffer(target, nsec3->next, nsec3->next_length));
+
+       /*
+        * Perform sanity check.
+        */
+       for (i = 0; i < nsec3->len ; i += len) {
+               INSIST(i + 2 <= nsec3->len);
+               window = nsec3->typebits[i];
+               len = nsec3->typebits[i+1];
+               i += 2;
+               INSIST(first || window > lastwindow); 
+               INSIST(len > 0 && len <= 32);
+               INSIST(i + len <= nsec3->len);
+               INSIST(nsec3->typebits[i + len - 1] != 0);
+               lastwindow = window;
+               first = ISC_FALSE;
+       }
+       return (mem_tobuffer(target, nsec3->typebits, nsec3->len));
+}
+
+static inline isc_result_t
+tostruct_nsec3(ARGS_TOSTRUCT) {
+       isc_region_t region;
+       dns_rdata_nsec3_t *nsec3 = target;
+
+       REQUIRE(rdata->type == 50);
+       REQUIRE(target != NULL);
+       REQUIRE(rdata->length != 0);
+
+       nsec3->common.rdclass = rdata->rdclass;
+       nsec3->common.rdtype = rdata->type;
+       ISC_LINK_INIT(&nsec3->common, link);
+
+       region.base = rdata->data;
+       region.length = rdata->length;
+       nsec3->hash = uint8_consume_fromregion(&region);
+       nsec3->flags = uint8_consume_fromregion(&region);
+       nsec3->iterations = uint16_consume_fromregion(&region);
+
+       nsec3->salt_length = uint8_consume_fromregion(&region);
+       nsec3->salt = mem_maybedup(mctx, region.base, nsec3->salt_length);
+       if (nsec3->salt == NULL)
+               return (ISC_R_NOMEMORY);
+       isc_region_consume(&region, nsec3->salt_length);
+
+       nsec3->next_length = uint8_consume_fromregion(&region);
+       nsec3->next = mem_maybedup(mctx, region.base, nsec3->next_length);
+       if (nsec3->next == NULL)
+               goto cleanup;
+       isc_region_consume(&region, nsec3->next_length);
+
+       nsec3->len = region.length;
+       nsec3->typebits = mem_maybedup(mctx, region.base, region.length);
+       if (nsec3->typebits == NULL)
+               goto cleanup;
+
+       nsec3->mctx = mctx;
+       return (ISC_R_SUCCESS);
+
+  cleanup:
+       if (nsec3->next != NULL)
+               isc_mem_free(mctx, nsec3->next);
+       isc_mem_free(mctx, nsec3->salt);
+       return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_nsec3(ARGS_FREESTRUCT) {
+       dns_rdata_nsec3_t *nsec3 = source;
+
+       REQUIRE(source != NULL);
+       REQUIRE(nsec3->common.rdtype == 50);
+
+       if (nsec3->mctx == NULL)
+               return;
+
+       if (nsec3->salt != NULL)
+               isc_mem_free(nsec3->mctx, nsec3->salt);
+       if (nsec3->next != NULL)
+               isc_mem_free(nsec3->mctx, nsec3->next);
+       if (nsec3->typebits != NULL)
+               isc_mem_free(nsec3->mctx, nsec3->typebits);
+       nsec3->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_nsec3(ARGS_ADDLDATA) {
+       REQUIRE(rdata->type == 50);
+
+       UNUSED(rdata);
+       UNUSED(add);
+       UNUSED(arg);
+
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_nsec3(ARGS_DIGEST) {
+       isc_region_t r;
+
+       REQUIRE(rdata->type == 50);
+
+       dns_rdata_toregion(rdata, &r);
+       return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_nsec3(ARGS_CHECKOWNER) {
+
+       REQUIRE(type == 50);
+
+       UNUSED(name);
+       UNUSED(type);
+       UNUSED(rdclass);
+       UNUSED(wildcard);
+
+       return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_nsec3(ARGS_CHECKNAMES) {
+
+       REQUIRE(rdata->type == 50);
+
+       UNUSED(rdata);
+       UNUSED(owner);
+       UNUSED(bad);
+
+       return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_NSEC3_50_C */
diff --git a/lib/dns/rdata/generic/nsec3_50.h b/lib/dns/rdata/generic/nsec3_50.h
new file mode 100644 (file)
index 0000000..1140f36
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2004  Nominet, Ltd.
+ *
+ * Permission to use, copy, modify, and 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 NOMINET 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.
+ */
+
+
+#ifndef GENERIC_NSEC3_50_H
+#define GENERIC_NSEC3_50_H 1
+
+/* $Id: nsec3_50.h,v 1.3 2008/09/24 02:46:23 marka Exp $ */
+
+/*!
+ * \brief Per RFC 5155 */
+
+#include <isc/iterated_hash.h>
+
+typedef struct dns_rdata_nsec3 {
+       dns_rdatacommon_t       common;
+       isc_mem_t               *mctx;
+       dns_hash_t              hash;
+       unsigned char           flags;
+       dns_iterations_t        iterations;
+       unsigned char           salt_length;
+       unsigned char           next_length;
+       isc_uint16_t            len;
+       unsigned char           *salt;
+       unsigned char           *next;
+       unsigned char           *typebits;
+} dns_rdata_nsec3_t;
+
+/*
+ * The corresponding NSEC3 interval is OPTOUT indicating possible
+ * insecure delegations.
+ */
+#define DNS_NSEC3FLAG_OPTOUT 0x01U
+
+/*%
+ * Non-standard, NSEC3PARAM only.
+ *
+ * Create a corresponding NSEC3 chain.
+ * Once the NSEC3 chain is complete this flag will be removed to signal
+ * that there is a complete chain.
+ *
+ * This flag is automatically set when a NSEC3PARAM record is added to
+ * the zone via UPDATE.
+ *
+ * NSEC3PARAM records with this flag set are supposed to be ignored by
+ * RFC 5155 compliant nameservers.
+ */
+#define DNS_NSEC3FLAG_CREATE 0x80U
+
+/*%
+ * Non-standard, NSEC3PARAM only.
+ *
+ * The corresponding NSEC3 set is to be removed once the NSEC chain
+ * has been generated.
+ *
+ * This flag is automatically set when the last active NSEC3PARAM record
+ * is removed from the zone via UPDATE.
+ *
+ * NSEC3PARAM records with this flag set are supposed to be ignored by
+ * RFC 5155 compliant nameservers.
+ */
+#define DNS_NSEC3FLAG_REMOVE 0x40U
+
+/*%
+ * Non-standard, NSEC3PARAM only.
+ *
+ * Used to identify NSEC3PARAM records added in this UPDATE request. 
+ */
+#define DNS_NSEC3FLAG_UPDATE 0x20U
+
+/*%
+ * Non-standard, NSEC3PARAM only.
+ *
+ * Prevent the creation of a NSEC chain before the last NSEC3 chain
+ * is removed.  This will normally only be set when the zone is
+ * transitioning from secure with NSEC3 chains to insecure.
+ */
+#define DNS_NSEC3FLAG_NONSEC 0x10U
+
+#endif /* GENERIC_NSEC3_50_H */
diff --git a/lib/dns/rdata/generic/nsec3param_51.c b/lib/dns/rdata/generic/nsec3param_51.c
new file mode 100644 (file)
index 0000000..5ab3b22
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+/*
+ * Copyright (C) 2004  Nominet, Ltd.
+ *
+ * Permission to use, copy, modify, and 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 NOMINET 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.
+ */
+
+/* RFC 5155 */
+
+#ifndef RDATA_GENERIC_NSEC3PARAM_51_C
+#define RDATA_GENERIC_NSEC3PARAM_51_C
+
+#include <isc/iterated_hash.h>
+#include <isc/base32.h>
+
+#define RRTYPE_NSEC3PARAM_ATTRIBUTES (DNS_RDATATYPEATTR_DNSSEC)
+
+static inline isc_result_t
+fromtext_nsec3param(ARGS_FROMTEXT) {
+       isc_token_t token;
+       unsigned int flags = 0;
+       unsigned char hashalg;
+
+       REQUIRE(type == 51);
+
+       UNUSED(type);
+       UNUSED(rdclass);
+       UNUSED(callbacks);
+       UNUSED(origin);
+       UNUSED(options);
+
+       /* Hash. */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+                                     ISC_FALSE));
+       RETTOK(dns_hashalg_fromtext(&hashalg, &token.value.as_textregion));
+       RETERR(uint8_tobuffer(hashalg, target));
+
+       /* Flags. */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+                                     ISC_FALSE));
+       flags = token.value.as_ulong;
+       if (flags > 255U)
+               RETTOK(ISC_R_RANGE);
+       RETERR(uint8_tobuffer(flags, target));
+
+       /* Iterations. */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+                                     ISC_FALSE));
+       if (token.value.as_ulong > 0xffffU)
+               RETTOK(ISC_R_RANGE);
+       RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+       /* Salt. */
+       RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+                                     ISC_FALSE));
+       if (token.value.as_textregion.length > (255*2))
+               RETTOK(DNS_R_TEXTTOOLONG);
+       if (strcmp(DNS_AS_STR(token), "-") == 0) {
+               RETERR(uint8_tobuffer(0, target));
+       } else {
+               RETERR(uint8_tobuffer(strlen(DNS_AS_STR(token)) / 2, target));
+               RETERR(isc_hex_decodestring(DNS_AS_STR(token), target));
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_nsec3param(ARGS_TOTEXT) {
+       isc_region_t sr;
+       unsigned int i, j;
+       unsigned char hash;
+       unsigned char flags;
+       char buf[sizeof("65535 ")];
+       isc_uint32_t iterations;
+
+       REQUIRE(rdata->type == 51);
+       REQUIRE(rdata->length != 0);
+
+       UNUSED(tctx);
+
+       dns_rdata_toregion(rdata, &sr);
+
+       hash = uint8_fromregion(&sr);
+       isc_region_consume(&sr, 1);
+
+       flags = uint8_fromregion(&sr);
+       isc_region_consume(&sr, 1);
+
+       iterations = uint16_fromregion(&sr);
+       isc_region_consume(&sr, 2);
+
+       sprintf(buf, "%u ", hash);
+       RETERR(str_totext(buf, target));
+
+       sprintf(buf, "%u ", flags);
+       RETERR(str_totext(buf, target));
+
+       sprintf(buf, "%u ", iterations);
+       RETERR(str_totext(buf, target));
+
+       j = uint8_fromregion(&sr);
+       isc_region_consume(&sr, 1);
+       INSIST(j <= sr.length);
+       
+       if (j != 0) {
+               i = sr.length;
+               sr.length = j;
+               RETERR(isc_hex_totext(&sr, 1, "", target));
+               sr.length = i - j;
+       } else
+               RETERR(str_totext("-", target));
+
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_nsec3param(ARGS_FROMWIRE) {
+       isc_region_t sr, rr;
+       unsigned int saltlen;
+
+       REQUIRE(type == 51);
+
+       UNUSED(type);
+       UNUSED(rdclass);
+       UNUSED(options);
+       UNUSED(dctx);
+
+       isc_buffer_activeregion(source, &sr);
+       rr = sr;
+
+       /* hash(1), flags(1), interations(2), saltlen(1) */
+       if (sr.length < 5U)
+               RETERR(DNS_R_FORMERR);
+       saltlen = sr.base[4];
+       isc_region_consume(&sr, 5);
+
+       if (sr.length < saltlen)
+               RETERR(DNS_R_FORMERR);
+       isc_region_consume(&sr, saltlen);
+       RETERR(mem_tobuffer(target, rr.base, rr.length));
+       isc_buffer_forward(source, rr.length);
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_nsec3param(ARGS_TOWIRE) {
+       isc_region_t sr;
+
+       REQUIRE(rdata->type == 51);
+       REQUIRE(rdata->length != 0);
+
+       UNUSED(cctx);
+
+       dns_rdata_toregion(rdata, &sr);
+       return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_nsec3param(ARGS_COMPARE) {
+       isc_region_t r1;
+       isc_region_t r2;
+
+       REQUIRE(rdata1->type == rdata2->type);
+       REQUIRE(rdata1->rdclass == rdata2->rdclass);
+       REQUIRE(rdata1->type == 51);
+       REQUIRE(rdata1->length != 0);
+       REQUIRE(rdata2->length != 0);
+
+       dns_rdata_toregion(rdata1, &r1);
+       dns_rdata_toregion(rdata2, &r2);
+       return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_nsec3param(ARGS_FROMSTRUCT) {
+       dns_rdata_nsec3param_t *nsec3param = source;
+
+       REQUIRE(type == 51);
+       REQUIRE(source != NULL);
+       REQUIRE(nsec3param->common.rdtype == type);
+       REQUIRE(nsec3param->common.rdclass == rdclass);
+
+       UNUSED(type);
+       UNUSED(rdclass);
+
+       RETERR(uint8_tobuffer(nsec3param->hash, target));
+       RETERR(uint8_tobuffer(nsec3param->flags, target));
+       RETERR(uint16_tobuffer(nsec3param->iterations, target));
+       RETERR(uint8_tobuffer(nsec3param->salt_length, target));
+       RETERR(mem_tobuffer(target, nsec3param->salt,
+                           nsec3param->salt_length));
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+tostruct_nsec3param(ARGS_TOSTRUCT) {
+       isc_region_t region;
+       dns_rdata_nsec3param_t *nsec3param = target;
+
+       REQUIRE(rdata->type == 51);
+       REQUIRE(target != NULL);
+       REQUIRE(rdata->length != 0);
+
+       nsec3param->common.rdclass = rdata->rdclass;
+       nsec3param->common.rdtype = rdata->type;
+       ISC_LINK_INIT(&nsec3param->common, link);
+
+       region.base = rdata->data;
+       region.length = rdata->length;
+       nsec3param->hash = uint8_consume_fromregion(&region);
+       nsec3param->flags = uint8_consume_fromregion(&region);
+       nsec3param->iterations = uint16_consume_fromregion(&region);
+
+       nsec3param->salt_length = uint8_consume_fromregion(&region);
+       nsec3param->salt = mem_maybedup(mctx, region.base,
+                                       nsec3param->salt_length);
+       if (nsec3param->salt == NULL)
+               return (ISC_R_NOMEMORY);
+       isc_region_consume(&region, nsec3param->salt_length);
+
+       nsec3param->mctx = mctx;
+       return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_nsec3param(ARGS_FREESTRUCT) {
+       dns_rdata_nsec3param_t *nsec3param = source;
+
+       REQUIRE(source != NULL);
+       REQUIRE(nsec3param->common.rdtype == 51);
+
+       if (nsec3param->mctx == NULL)
+               return;
+
+       if (nsec3param->salt != NULL)
+               isc_mem_free(nsec3param->mctx, nsec3param->salt);
+       nsec3param->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_nsec3param(ARGS_ADDLDATA) {
+       REQUIRE(rdata->type == 51);
+
+       UNUSED(rdata);
+       UNUSED(add);
+       UNUSED(arg);
+
+       return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_nsec3param(ARGS_DIGEST) {
+       isc_region_t r;
+
+       REQUIRE(rdata->type == 51);
+
+       dns_rdata_toregion(rdata, &r);
+       return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_nsec3param(ARGS_CHECKOWNER) {
+
+       REQUIRE(type == 51);
+
+       UNUSED(name);
+       UNUSED(type);
+       UNUSED(rdclass);
+       UNUSED(wildcard);
+
+       return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_nsec3param(ARGS_CHECKNAMES) {
+
+       REQUIRE(rdata->type == 51);
+
+       UNUSED(rdata);
+       UNUSED(owner);
+       UNUSED(bad);
+
+       return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_NSEC3PARAM_51_C */
diff --git a/lib/dns/rdata/generic/nsec3param_51.h b/lib/dns/rdata/generic/nsec3param_51.h
new file mode 100644 (file)
index 0000000..2ae6a9c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2004  Nominet, Ltd.
+ *
+ * Permission to use, copy, modify, and 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 NOMINET 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.
+ */
+
+
+#ifndef GENERIC_NSEC3PARAM_51_H
+#define GENERIC_NSEC3PARAM_51_H 1
+
+/* $Id: nsec3param_51.h,v 1.3 2008/09/24 02:46:23 marka Exp $ */
+
+/*!
+ * \brief Per RFC 5155 */
+
+#include <isc/iterated_hash.h>
+
+typedef struct dns_rdata_nsec3param {
+       dns_rdatacommon_t       common;
+       isc_mem_t               *mctx;
+       dns_hash_t              hash;
+       unsigned char           flags;          /* DNS_NSEC3FLAG_* */
+       dns_iterations_t        iterations;
+       unsigned char           salt_length;
+       unsigned char           *salt;
+} dns_rdata_nsec3param_t;
+
+#endif /* GENERIC_NSEC3PARAM_51_H */
index 06b3513c0f1d1327b1531c14e59055d218f80230..d6f11ae64d3007d85593e2d55dcb005446c19de3 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdatalist.c,v 1.35 2008/04/03 06:09:04 tbox Exp $ */
+/* $Id: rdatalist.c,v 1.36 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -26,6 +26,7 @@
 #include <isc/util.h>
 
 #include <dns/name.h>
+#include <dns/nsec3.h>
 #include <dns/rdata.h>
 #include <dns/rdatalist.h>
 #include <dns/rdataset.h>
@@ -41,6 +42,8 @@ static dns_rdatasetmethods_t methods = {
        isc__rdatalist_count,
        isc__rdatalist_addnoqname,
        isc__rdatalist_getnoqname,
+       isc__rdatalist_addclosest,
+       isc__rdatalist_getclosest,
        NULL,
        NULL,
        NULL
@@ -63,8 +66,8 @@ dns_rdatalist_init(dns_rdatalist_t *rdatalist) {
 
 isc_result_t
 dns_rdatalist_tordataset(dns_rdatalist_t *rdatalist,
-                        dns_rdataset_t *rdataset) {
-
+                        dns_rdataset_t *rdataset)
+{
        /*
         * Make 'rdataset' refer to the rdata in 'rdatalist'.
         */
@@ -171,8 +174,8 @@ isc__rdatalist_count(dns_rdataset_t *rdataset) {
 
 isc_result_t
 isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) {
-       dns_rdataset_t *nsec = NULL;
-       dns_rdataset_t *nsecsig = NULL;
+       dns_rdataset_t *neg = NULL;
+       dns_rdataset_t *negsig = NULL;
        dns_rdataset_t *rdset;
        dns_ttl_t ttl;
 
@@ -182,24 +185,33 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) {
        {
                if (rdset->rdclass != rdataset->rdclass)
                        continue;
-               if (rdset->type == dns_rdatatype_nsec)
-                       nsec = rdset;
+               if (rdset->type == dns_rdatatype_nsec ||
+                   rdset->type == dns_rdatatype_nsec3)
+                       neg = rdset;
+       }
+       if (neg == NULL)
+               return (ISC_R_NOTFOUND);
+
+       for (rdset = ISC_LIST_HEAD(name->list);
+            rdset != NULL;
+            rdset = ISC_LIST_NEXT(rdset, link))
+       {
                if (rdset->type == dns_rdatatype_rrsig &&
-                   rdset->covers == dns_rdatatype_nsec)
-                       nsecsig = rdset;
+                   rdset->covers == neg->type)
+                       negsig = rdset;
        }
 
-       if (nsec == NULL || nsecsig == NULL)
+       if (negsig == NULL)
                return (ISC_R_NOTFOUND);
        /*
         * Minimise ttl.
         */
        ttl = rdataset->ttl;
-       if (nsec->ttl < ttl)
-               ttl = nsec->ttl;
-       if (nsecsig->ttl < ttl)
-               ttl = nsecsig->ttl;
-       rdataset->ttl = nsec->ttl = nsecsig->ttl = ttl;
+       if (neg->ttl < ttl)
+               ttl = neg->ttl;
+       if (negsig->ttl < ttl)
+               ttl = negsig->ttl;
+       rdataset->ttl = neg->ttl = negsig->ttl = ttl;
        rdataset->attributes |= DNS_RDATASETATTR_NOQNAME;
        rdataset->private6 = name;
        return (ISC_R_SUCCESS);
@@ -207,11 +219,11 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) {
 
 isc_result_t
 isc__rdatalist_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
-                        dns_rdataset_t *nsec, dns_rdataset_t *nsecsig)
+                         dns_rdataset_t *neg, dns_rdataset_t *negsig)
 {
        dns_rdataclass_t rdclass = rdataset->rdclass;
-       dns_rdataset_t *tnsec = NULL;
-       dns_rdataset_t *tnsecsig = NULL;
+       dns_rdataset_t *tneg = NULL;
+       dns_rdataset_t *tnegsig = NULL;
        dns_name_t *noqname = rdataset->private6;
 
        REQUIRE((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0);
@@ -223,17 +235,113 @@ isc__rdatalist_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
        {
                if (rdataset->rdclass != rdclass)
                        continue;
-               if (rdataset->type == dns_rdatatype_nsec)
-                       tnsec = rdataset;
+               if (rdataset->type == dns_rdatatype_nsec ||
+                   rdataset->type == dns_rdatatype_nsec3)
+                       tneg = rdataset;
+       }
+       if (tneg == NULL)
+               return (ISC_R_NOTFOUND);
+
+       for (rdataset = ISC_LIST_HEAD(noqname->list);
+            rdataset != NULL;
+            rdataset = ISC_LIST_NEXT(rdataset, link))
+       {
                if (rdataset->type == dns_rdatatype_rrsig &&
-                   rdataset->covers == dns_rdatatype_nsec)
-                       tnsecsig = rdataset;
+                   rdataset->covers == tneg->type)
+                       tnegsig = rdataset;
        }
-       if (tnsec == NULL || tnsecsig == NULL)
+       if (tnegsig == NULL)
                return (ISC_R_NOTFOUND);
 
        dns_name_clone(noqname, name);
-       dns_rdataset_clone(tnsec, nsec);
-       dns_rdataset_clone(tnsecsig, nsecsig);
+       dns_rdataset_clone(tneg, neg);
+       dns_rdataset_clone(tnegsig, negsig);
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc__rdatalist_addclosest(dns_rdataset_t *rdataset, dns_name_t *name) {
+       dns_rdataset_t *neg = NULL;
+       dns_rdataset_t *negsig = NULL;
+       dns_rdataset_t *rdset;
+       dns_ttl_t ttl;
+
+       for (rdset = ISC_LIST_HEAD(name->list);
+            rdset != NULL;
+            rdset = ISC_LIST_NEXT(rdset, link))
+       {
+               if (rdset->rdclass != rdataset->rdclass)
+                       continue;
+               if (rdset->type == dns_rdatatype_nsec ||
+                   rdset->type == dns_rdatatype_nsec3)
+                       neg = rdset;
+       }
+       if (neg == NULL)
+               return (ISC_R_NOTFOUND);
+
+       for (rdset = ISC_LIST_HEAD(name->list);
+            rdset != NULL;
+            rdset = ISC_LIST_NEXT(rdset, link))
+       {
+               if (rdset->type == dns_rdatatype_rrsig &&
+                   rdset->covers == neg->type)
+                       negsig = rdset;
+       }
+
+       if (negsig == NULL)
+               return (ISC_R_NOTFOUND);
+       /*
+        * Minimise ttl.
+        */
+       ttl = rdataset->ttl;
+       if (neg->ttl < ttl)
+               ttl = neg->ttl;
+       if (negsig->ttl < ttl)
+               ttl = negsig->ttl;
+       rdataset->ttl = neg->ttl = negsig->ttl = ttl;
+       rdataset->attributes |= DNS_RDATASETATTR_CLOSEST;
+       rdataset->private7 = name;
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc__rdatalist_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+                         dns_rdataset_t *neg, dns_rdataset_t *negsig)
+{
+       dns_rdataclass_t rdclass = rdataset->rdclass;
+       dns_rdataset_t *tneg = NULL;
+       dns_rdataset_t *tnegsig = NULL;
+       dns_name_t *closest = rdataset->private7;
+
+       REQUIRE((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) != 0);
+       (void)dns_name_dynamic(closest);        /* Sanity Check. */
+
+       for (rdataset = ISC_LIST_HEAD(closest->list);
+            rdataset != NULL;
+            rdataset = ISC_LIST_NEXT(rdataset, link))
+       {
+               if (rdataset->rdclass != rdclass)
+                       continue;
+               if (rdataset->type == dns_rdatatype_nsec ||
+                   rdataset->type == dns_rdatatype_nsec3)
+                       tneg = rdataset;
+       }
+       if (tneg == NULL)
+               return (ISC_R_NOTFOUND);
+
+       for (rdataset = ISC_LIST_HEAD(closest->list);
+            rdataset != NULL;
+            rdataset = ISC_LIST_NEXT(rdataset, link))
+       {
+               if (rdataset->type == dns_rdatatype_rrsig &&
+                   rdataset->covers == tneg->type)
+                       tnegsig = rdataset;
+       }
+       if (tnegsig == NULL)
+               return (ISC_R_NOTFOUND);
+
+       dns_name_clone(closest, name);
+       dns_rdataset_clone(tneg, neg);
+       dns_rdataset_clone(tnegsig, negsig);
        return (ISC_R_SUCCESS);
 }
index 4c9d08688e37d811c0a343c0c2d77991abf88732..65d9235e131d644ae06fc5a54bd00b2c1288e2ff 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdatalist_p.h,v 1.9 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: rdatalist_p.h,v 1.10 2008/09/24 02:46:22 marka Exp $ */
 
 #ifndef DNS_RDATALIST_P_H
 #define DNS_RDATALIST_P_H
@@ -50,7 +50,14 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name);
 
 isc_result_t
 isc__rdatalist_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
-                         dns_rdataset_t *nsec, dns_rdataset_t *nsecsig);
+                         dns_rdataset_t *neg, dns_rdataset_t *negsig);
+
+isc_result_t
+isc__rdatalist_addclosest(dns_rdataset_t *rdataset, dns_name_t *name);
+
+isc_result_t
+isc__rdatalist_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+                         dns_rdataset_t *neg, dns_rdataset_t *negsig);
 
 ISC_LANG_ENDDECLS
 
index 4b8a4bfee62a8b6c4843cc02a8b11e8421a109f2..451c6b841b70b89388e3397c9c2a0b3931684aa7 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdataset.c,v 1.81 2008/04/01 23:47:10 tbox Exp $ */
+/* $Id: rdataset.c,v 1.82 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -180,6 +180,8 @@ static dns_rdatasetmethods_t question_methods = {
        NULL,
        NULL,
        NULL,
+       NULL,
+       NULL,
        NULL
 };
 
@@ -621,14 +623,36 @@ dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) {
 
 isc_result_t
 dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
-                       dns_rdataset_t *nsec, dns_rdataset_t *nsecsig)
+                       dns_rdataset_t *neg, dns_rdataset_t *negsig)
 {
        REQUIRE(DNS_RDATASET_VALID(rdataset));
        REQUIRE(rdataset->methods != NULL);
 
        if (rdataset->methods->getnoqname == NULL)
                return (ISC_R_NOTIMPLEMENTED);
-       return((rdataset->methods->getnoqname)(rdataset, name, nsec, nsecsig));
+       return((rdataset->methods->getnoqname)(rdataset, name, neg, negsig));
+}
+
+isc_result_t
+dns_rdataset_addclosest(dns_rdataset_t *rdataset, dns_name_t *name) {
+
+       REQUIRE(DNS_RDATASET_VALID(rdataset));
+       REQUIRE(rdataset->methods != NULL);
+       if (rdataset->methods->addclosest == NULL)
+               return (ISC_R_NOTIMPLEMENTED);
+       return((rdataset->methods->addclosest)(rdataset, name));
+}
+
+isc_result_t
+dns_rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+                       dns_rdataset_t *neg, dns_rdataset_t *negsig)
+{
+       REQUIRE(DNS_RDATASET_VALID(rdataset));
+       REQUIRE(rdataset->methods != NULL);
+
+       if (rdataset->methods->getclosest == NULL)
+               return (ISC_R_NOTIMPLEMENTED);
+       return((rdataset->methods->getclosest)(rdataset, name, neg, negsig));
 }
 
 /*
index f84bed0393e0a16b689e71a440990749cb45c988..610d44fb1a0c3804a0c209a134980ba6f64f968a 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdataslab.c,v 1.47 2008/04/23 21:32:01 each Exp $ */
+/* $Id: rdataslab.c,v 1.48 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -434,6 +434,8 @@ static dns_rdatasetmethods_t rdataset_methods = {
        NULL,
        NULL,
        NULL,
+       NULL,
+       NULL,
        NULL
 };
 
index 08bb64591f190a31ecbd745ca9588047a371e6e4..e195ac173c81bde6b62d0556acc615a9122b564f 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: resolver.c,v 1.380 2008/09/04 04:23:43 marka Exp $ */
+/* $Id: resolver.c,v 1.381 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -389,6 +389,7 @@ static isc_result_t ncache_adderesult(dns_message_t *message,
                                      dns_db_t *cache, dns_dbnode_t *node,
                                      dns_rdatatype_t covers,
                                      isc_stdtime_t now, dns_ttl_t maxttl,
+                                     isc_boolean_t optout,
                                      dns_rdataset_t *ardataset,
                                      isc_result_t *eresultp);
 static void validated(isc_task_t *task, isc_event_t *event);
@@ -1157,13 +1158,18 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
        resquery_t *query;
        isc_sockaddr_t addr;
        isc_boolean_t have_addr = ISC_FALSE;
+       unsigned int srtt;
 
        FCTXTRACE("query");
 
        res = fctx->res;
        task = res->buckets[fctx->bucketnum].task;
 
-       fctx_setretryinterval(fctx, addrinfo->srtt);
+       srtt = addrinfo->srtt;
+       if (ISFORWARDER(addrinfo) && srtt < 1000000)
+               srtt = 1000000;
+
+       fctx_setretryinterval(fctx, srtt);
        result = fctx_startidletimer(fctx);
        if (result != ISC_R_SUCCESS)
                return (result);
@@ -3748,7 +3754,7 @@ validated(isc_task_t *task, isc_event_t *event) {
                        ttl = 0;
 
                result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
-                                          covers, now, ttl,
+                                          covers, now, ttl, vevent->optout,
                                           ardataset, &eresult);
                if (result != ISC_R_SUCCESS)
                        goto noanswer_response;
@@ -3765,6 +3771,11 @@ validated(isc_task_t *task, isc_event_t *event) {
                RUNTIME_CHECK(result == ISC_R_SUCCESS);
                INSIST(vevent->sigrdataset != NULL);
                vevent->sigrdataset->ttl = vevent->rdataset->ttl;
+               if (vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER] != NULL) {
+                       result = dns_rdataset_addclosest(vevent->rdataset,
+                                vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER]);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+               }
        }
 
        /*
@@ -4305,12 +4316,12 @@ cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now)
 }
 
 /*
- * Do what dns_ncache_add() does, and then compute an appropriate eresult.
+ * Do what dns_ncache_addoptout() does, and then compute an appropriate eresult.
  */
 static isc_result_t
 ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                  dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
-                 dns_rdataset_t *ardataset,
+                 isc_boolean_t optout, dns_rdataset_t *ardataset,
                  isc_result_t *eresultp)
 {
        isc_result_t result;
@@ -4320,8 +4331,8 @@ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                dns_rdataset_init(&rdataset);
                ardataset = &rdataset;
        }
-       result = dns_ncache_add(message, cache, node, covers, now,
-                               maxttl, ardataset);
+       result = dns_ncache_addoptout(message, cache, node, covers, now,
+                                    maxttl, optout, ardataset);
        if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) {
                /*
                 * If the cache now contains a negative entry and we
@@ -4485,7 +4496,8 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
                ttl = 0;
 
        result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
-                                  covers, now, ttl, ardataset, &eresult);
+                                  covers, now, ttl, ISC_FALSE,
+                                  ardataset, &eresult);
        if (result != ISC_R_SUCCESS)
                goto unlock;
 
@@ -4860,7 +4872,8 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
                                type = rdataset->type;
                                if (type == dns_rdatatype_rrsig)
                                        type = rdataset->covers;
-                               if (type == dns_rdatatype_nsec) {
+                               if (type == dns_rdatatype_nsec ||
+                                   type == dns_rdatatype_nsec3) {
                                        /*
                                         * NSEC or RRSIG NSEC.
                                         */
@@ -4869,7 +4882,7 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
                                                        DNS_NAMEATTR_NCACHE;
                                                rdataset->attributes |=
                                                        DNS_RDATASETATTR_NCACHE;
-                                       } else {
+                                       } else if (type == dns_rdatatype_nsec) {
                                                name->attributes |=
                                                        DNS_NAMEATTR_CACHE;
                                                rdataset->attributes |=
@@ -5081,6 +5094,13 @@ answer_response(fetchctx_t *fctx) {
                                found = ISC_FALSE;
                                want_chaining = ISC_FALSE;
                                aflag = 0;
+                               if (rdataset->type == dns_rdatatype_nsec3) {
+                                       /*
+                                        * NSEC3 records are not allowed to
+                                        * appear in the answer section.
+                                        */
+                                       return (DNS_R_FORMERR);
+                               }
                                if (rdataset->type == type && !found_cname) {
                                        /*
                                         * We've found an ordinary answer.
index 2e060542faf216e4353c63f54c2005aaa26999b0..1f940e54a7d85a5a31678768de3d7b043c694c55 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: result.c,v 1.123 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: result.c,v 1.124 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -155,7 +155,8 @@ static const char *text[DNS_R_NRESULTS] = {
        "must-be-secure",                      /*%< 100 DNS_R_MUSTBESECURE */
        "covering NSEC record returned",       /*%< 101 DNS_R_COVERINGNSEC */
        "MX is an address",                    /*%< 102 DNS_R_MXISADDRESS */
-       "duplicate query"                      /*%< 103 DNS_R_DUPLICATE */
+       "duplicate query",                     /*%< 103 DNS_R_DUPLICATE */
+       "invalid NSEC3 owner name (wildcard)", /*%< 104 DNS_R_INVALIDNSEC3 */
 };
 
 static const char *rcode_text[DNS_R_NRCODERESULTS] = {
index 01d23b5d4b195292314f3becf57d947d94cc6328..3c50a1823afe9e72f2246d5b9f48d6a8112b9f18 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rootns.c,v 1.35 2008/04/01 01:37:25 marka Exp $ */
+/* $Id: rootns.c,v 1.36 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -159,7 +159,7 @@ check_hints(dns_db_t *db) {
        dns_rdataset_init(&rootns);
        (void)dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0,
                          now, NULL, name, &rootns, NULL);
-       result = dns_db_createiterator(db, ISC_FALSE, &dbiter);
+       result = dns_db_createiterator(db, 0, &dbiter);
        if (result != ISC_R_SUCCESS)
                goto cleanup;
        result = dns_dbiterator_first(dbiter);
index a0db9957042226ec94f395e0f7def3e0d927dbdb..8ef7c63fa1986b1b4e769d34cd473e193e20dc0c 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: sdb.c,v 1.64 2008/04/03 05:55:52 marka Exp $ */
+/* $Id: sdb.c,v 1.65 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -1035,8 +1035,7 @@ printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
 }
 
 static isc_result_t
-createiterator(dns_db_t *db, isc_boolean_t relative_names,
-              dns_dbiterator_t **iteratorp)
+createiterator(dns_db_t *db, unsigned int options, dns_dbiterator_t **iteratorp)
 {
        dns_sdb_t *sdb = (dns_sdb_t *)db;
        sdb_dbiterator_t *sdbiter;
@@ -1048,6 +1047,10 @@ createiterator(dns_db_t *db, isc_boolean_t relative_names,
        if (imp->methods->allnodes == NULL)
                return (ISC_R_NOTIMPLEMENTED);
 
+       if ((options & DNS_DB_NSEC3ONLY) != 0 ||
+           (options & DNS_DB_NONSEC3) != 0)
+                return (ISC_R_NOTIMPLEMENTED);
+
        sdbiter = isc_mem_get(sdb->common.mctx, sizeof(sdb_dbiterator_t));
        if (sdbiter == NULL)
                return (ISC_R_NOMEMORY);
@@ -1055,7 +1058,7 @@ createiterator(dns_db_t *db, isc_boolean_t relative_names,
        sdbiter->common.methods = &dbiterator_methods;
        sdbiter->common.db = NULL;
        dns_db_attach(db, &sdbiter->common.db);
-       sdbiter->common.relative_names = relative_names;
+       sdbiter->common.relative_names = ISC_TF(options & DNS_DB_RELATIVENAMES);
        sdbiter->common.magic = DNS_DBITERATOR_MAGIC;
        ISC_LIST_INIT(sdbiter->nodelist);
        sdbiter->current = NULL;
@@ -1252,6 +1255,8 @@ static dns_dbmethods_t sdb_methods = {
        NULL,
        NULL,
        NULL,
+       NULL,
+       NULL,
        NULL
 };
 
@@ -1377,6 +1382,8 @@ static dns_rdatasetmethods_t methods = {
        isc__rdatalist_getnoqname,
        NULL,
        NULL,
+       NULL,
+       NULL,
        NULL
 };
 
index 8fbb5971bce917ffea56714071b157f58f96535b..ec82c3e51c7124e92667d6a19119c8aa3a34da32 100644 (file)
@@ -50,7 +50,7 @@
  * USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: sdlz.c,v 1.17 2008/04/03 05:55:52 marka Exp $ */
+/* $Id: sdlz.c,v 1.18 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -667,8 +667,7 @@ printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
 }
 
 static isc_result_t
-createiterator(dns_db_t *db, isc_boolean_t relative_names,
-              dns_dbiterator_t **iteratorp)
+createiterator(dns_db_t *db, unsigned int options, dns_dbiterator_t **iteratorp)
 {
        dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
        sdlz_dbiterator_t *sdlziter;
@@ -681,6 +680,10 @@ createiterator(dns_db_t *db, isc_boolean_t relative_names,
        if (sdlz->dlzimp->methods->allnodes == NULL)
                return (ISC_R_NOTIMPLEMENTED);
 
+       if ((options & DNS_DB_NSEC3ONLY) != 0 ||
+           (options & DNS_DB_NONSEC3) != 0)
+                return (ISC_R_NOTIMPLEMENTED);
+
        isc_buffer_init(&b, zonestr, sizeof(zonestr));
        result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b);
        if (result != ISC_R_SUCCESS)
@@ -694,7 +697,7 @@ createiterator(dns_db_t *db, isc_boolean_t relative_names,
        sdlziter->common.methods = &dbiterator_methods;
        sdlziter->common.db = NULL;
        dns_db_attach(db, &sdlziter->common.db);
-       sdlziter->common.relative_names = relative_names;
+       sdlziter->common.relative_names = ISC_TF(options & DNS_DB_RELATIVENAMES);
        sdlziter->common.magic = DNS_DBITERATOR_MAGIC;
        ISC_LIST_INIT(sdlziter->nodelist);
        sdlziter->current = NULL;
@@ -1056,6 +1059,8 @@ static dns_dbmethods_t sdlzdb_methods = {
        NULL,
        NULL,
        NULL,
+       NULL,
+       NULL,
        NULL
 };
 
@@ -1199,6 +1204,8 @@ static dns_rdatasetmethods_t rdataset_methods = {
        isc__rdatalist_getnoqname,
        NULL,
        NULL,
+       NULL,
+       NULL,
        NULL
 };
 
index 87717401bba1dfb9791b1046f36d2b90f3ad038f..cd6de2d3fdf8f409e4aa2970a09d0a3938690090 100644 (file)
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: validator.c,v 1.161 2008/08/21 04:43:49 marka Exp $ */
+/* $Id: validator.c,v 1.162 2008/09/24 02:46:22 marka Exp $ */
 
 #include <config.h>
 
+#include <isc/base32.h>
 #include <isc/mem.h>
 #include <isc/print.h>
+#include <isc/sha2.h>
 #include <isc/string.h>
 #include <isc/task.h>
 #include <isc/util.h>
-#include <isc/sha2.h>
 
 #include <dns/db.h>
 #include <dns/ds.h>
@@ -35,6 +36,7 @@
 #include <dns/message.h>
 #include <dns/ncache.h>
 #include <dns/nsec.h>
+#include <dns/nsec3.h>
 #include <dns/rdata.h>
 #include <dns/rdatastruct.h>
 #include <dns/rdataset.h>
 /*!
  * NSEC proofs to be looked for.
  */
-#define VALATTR_NEEDNOQNAME            0x0100
-#define VALATTR_NEEDNOWILDCARD         0x0200
-#define VALATTR_NEEDNODATA             0x0400
+#define VALATTR_NEEDNOQNAME            0x00000100
+#define VALATTR_NEEDNOWILDCARD         0x00000200
+#define VALATTR_NEEDNODATA             0x00000400
 
 /*!
  * NSEC proofs that have been found.
  */
-#define VALATTR_FOUNDNOQNAME           0x1000
-#define VALATTR_FOUNDNOWILDCARD                0x2000
-#define VALATTR_FOUNDNODATA            0x4000
+#define VALATTR_FOUNDNOQNAME           0x00001000
+#define VALATTR_FOUNDNOWILDCARD                0x00002000
+#define VALATTR_FOUNDNODATA            0x00004000
+#define VALATTR_FOUNDCLOSEST           0x00008000
+
+/*
+ *
+ */
+#define VALATTR_FOUNDOPTOUT            0x00010000
+#define VALATTR_FOUNDUNKNOWN           0x00020000
 
 #define NEEDNODATA(val) ((val->attributes & VALATTR_NEEDNODATA) != 0)
 #define NEEDNOQNAME(val) ((val->attributes & VALATTR_NEEDNOQNAME) != 0)
@@ -217,10 +226,19 @@ static isc_boolean_t
 isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
             isc_result_t dbresult)
 {
-       dns_rdataset_t set;
+       dns_label_t hashlabel;
+       dns_name_t nsec3name;
+       dns_rdata_nsec3_t nsec3;
        dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdataset_t set;
+       int order;
+       int scope;
        isc_boolean_t found;
+       isc_buffer_t buffer;
        isc_result_t result;
+       unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+       unsigned char owner[NSEC3_MAX_HASH_LENGTH];
+       unsigned int length;
 
        REQUIRE(dbresult == DNS_R_NXRRSET || dbresult == DNS_R_NCACHENXRRSET);
 
@@ -230,6 +248,8 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
        else {
                result = dns_ncache_getrdataset(rdataset, name,
                                                dns_rdatatype_nsec, &set);
+               if (result == ISC_R_NOTFOUND)
+                       goto trynsec3;
                if (result != ISC_R_SUCCESS)
                        return (ISC_FALSE);
        }
@@ -241,9 +261,75 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
        if (result == ISC_R_SUCCESS) {
                dns_rdataset_current(&set, &rdata);
                found = dns_nsec_typepresent(&rdata, dns_rdatatype_ns);
+               dns_rdata_reset(&rdata);
        }
        dns_rdataset_disassociate(&set);
        return (found);
+
+ trynsec3:
+       /*
+        * Iterate over the ncache entry.
+        */
+       found = ISC_FALSE;
+       dns_name_init(&nsec3name, NULL);
+       result = dns_rdataset_first(rdataset);
+       for (result = dns_rdataset_first(rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(rdataset))
+       {
+               dns_ncache_current(rdataset, &nsec3name, &set);
+               if (set.type != dns_rdatatype_nsec3) {
+                       dns_rdataset_disassociate(&set);
+                       continue;
+               }
+               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) {
+                       dns_rdataset_disassociate(&set);
+                       continue;
+               }
+               for (result = dns_rdataset_first(&set);
+                    result == ISC_R_SUCCESS;
+                    result = dns_rdataset_next(&set))
+               {
+                       dns_rdata_reset(&rdata);
+                       dns_rdataset_current(&set, &rdata);
+                       (void)dns_rdata_tostruct(&rdata, &nsec3, NULL);
+                       if (nsec3.hash != 1)
+                               continue;
+                       length = isc_iterated_hash(hash, nsec3.hash,
+                                                  nsec3.iterations, nsec3.salt,
+                                                  nsec3.salt_length,
+                                                  name->ndata, name->length);
+                       if (length != isc_buffer_usedlength(&buffer))
+                               continue;
+                       order = memcmp(hash, owner, length);
+                       if (order == 0) {
+                               found = dns_nsec3_typepresent(&rdata,
+                                                             dns_rdatatype_ns);
+                               dns_rdataset_disassociate(&set);
+                               return (found);
+                       }
+                       if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) == 0)
+                               continue;
+                       /*
+                        * Does this optout span cover the name?
+                        */
+                       scope = memcmp(owner, nsec3.next, nsec3.next_length);
+                       if ((scope < 0 && order > 0 &&
+                            memcmp(hash, nsec3.next, length) < 0) ||
+                           (scope >= 0 && (order > 0 ||
+                                       memcmp(hash, nsec3.next, length) < 0)))
+                       {
+                               dns_rdataset_disassociate(&set);
+                               return (ISC_TRUE);
+                       }
+               }
+               dns_rdataset_disassociate(&set);
+       }
+       return (found);
 }
 
 /*%
@@ -734,10 +820,317 @@ nsecnoexistnodata(dns_validator_t *val, dns_name_t* name, dns_name_t *nsecname,
        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 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 NSEC3");
+                                       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 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-existance 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;
+                       }
+#if 0
+                       /*
+                        * The closest encloser may be the zone name.
+                        */
+                       if (closest != NULL &&
+                           dns_name_countlabels(closest) == 0 &&
+                           !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)))
+                       {
+                               char namebuf[DNS_NAME_FORMATSIZE];
+
+                               dns_name_format(zone, namebuf,
+                                               sizeof(namebuf));
+                               validator_log(val, ISC_LOG_DEBUG(3),
+                                             "NSEC3 potential closest "
+                                              "encloser from zone name: '%s'",
+                                              namebuf);
+                               dns_name_copy(zone, closest, NULL);
+                               *setclosest = ISC_TRUE;
+                       }
+#endif
+                       *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.
  *
- * Looks for NOQNAME and NODATA proofs.
+ * Looks for NOQNAME, NODATA and OPTOUT proofs.
  *
  * Resumes nsecvalidate.
  */
@@ -746,6 +1139,7 @@ authvalidated(isc_task_t *task, isc_event_t *event) {
        dns_validatorevent_t *devent;
        dns_validator_t *val;
        dns_rdataset_t *rdataset;
+       dns_rdataset_t *sigrdataset;
        isc_boolean_t want_destroy;
        isc_result_t result;
        isc_boolean_t exists, data;
@@ -755,6 +1149,7 @@ authvalidated(isc_task_t *task, isc_event_t *event) {
 
        devent = (dns_validatorevent_t *)event;
        rdataset = devent->rdataset;
+       sigrdataset = devent->sigrdataset;
        val = devent->ev_arg;
        result = devent->result;
        dns_validator_destroy(&val->subvalidator);
@@ -801,11 +1196,18 @@ authvalidated(isc_task_t *task, isc_event_t *event) {
                        }
                        if (!exists) {
                                val->attributes |= VALATTR_FOUNDNOQNAME;
+                               val->attributes |= VALATTR_FOUNDCLOSEST;
+                               /*
+                                * The NSEC noqname proof also contains
+                                * the closest encloser.
+
+                                */
                                if (NEEDNOQNAME(val))
                                        proofs[DNS_VALIDATOR_NOQNAMEPROOF] =
                                                devent->name;
                        }
                }
+
                result = nsecvalidate(val, ISC_TRUE);
                if (result != DNS_R_WAIT)
                        validator_done(val, result);
@@ -959,13 +1361,25 @@ view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
  * the validation process will stall if looping was to occur.
  */
 static inline isc_boolean_t
-check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
+check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
+              dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
        dns_validator_t *parent;
 
        for (parent = val; parent != NULL; parent = parent->parent) {
                if (parent->event != NULL &&
                    parent->event->type == type &&
-                   dns_name_equal(parent->event->name, name))
+                   dns_name_equal(parent->event->name, name) &&
+                   /*
+                    * As NSEC3 records are meta data you sometimes
+                    * need to prove a NSEC3 record which says that
+                    * itself doesn't exist.
+                    */
+                   (parent->event->type != dns_rdatatype_nsec3 ||
+                    rdataset == NULL || sigrdataset == NULL ||
+                    parent->event->message == NULL ||
+                    parent->event->rdataset != NULL ||
+                    parent->event->sigrdataset != NULL))
                {
                        validator_log(val, ISC_LOG_DEBUG(3),
                                      "continuing validation would lead to "
@@ -988,7 +1402,7 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
        if (dns_rdataset_isassociated(&val->fsigrdataset))
                dns_rdataset_disassociate(&val->fsigrdataset);
 
-       if (check_deadlock(val, name, type))
+       if (check_deadlock(val, name, type, NULL, NULL))
                return (DNS_R_NOVALIDSIG);
 
        validator_logcreate(val, name, type, caller, "fetch");
@@ -1011,7 +1425,7 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
 {
        isc_result_t result;
 
-       if (check_deadlock(val, name, type))
+       if (check_deadlock(val, name, type, rdataset, sigrdataset))
                return (DNS_R_NOVALIDSIG);
 
        validator_logcreate(val, name, type, caller, "validator");
@@ -2011,7 +2425,8 @@ start_positive_validation(dns_validator_t *val) {
  * \li ISC_R_SUCCESS
  */
 static isc_result_t
-checkwildcard(dns_validator_t *val) {
+checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename)
+{
        dns_name_t *name, *wild;
        dns_message_t *message = val->event->message;
        isc_result_t result;
@@ -2019,6 +2434,13 @@ checkwildcard(dns_validator_t *val) {
        char namebuf[DNS_NAME_FORMATSIZE];
 
        wild = dns_fixedname_name(&val->wild);
+
+       if (dns_name_countlabels(wild) == 0) {
+               validator_log(val, ISC_LOG_DEBUG(3),
+                             "in checkwildcard: no wildcard to check");
+               return (ISC_R_SUCCESS);
+       }
+
        dns_name_format(wild, namebuf, sizeof(namebuf));
        validator_log(val, ISC_LOG_DEBUG(3), "in checkwildcard: %s", namebuf);
 
@@ -2035,9 +2457,8 @@ checkwildcard(dns_validator_t *val) {
                     rdataset != NULL;
                     rdataset = ISC_LIST_NEXT(rdataset, link))
                {
-                       if (rdataset->type != dns_rdatatype_nsec)
+                       if (rdataset->type != type)
                                continue;
-                       val->nsecset = rdataset;
 
                        for (sigrdataset = ISC_LIST_HEAD(name->list);
                             sigrdataset != NULL;
@@ -2053,7 +2474,8 @@ checkwildcard(dns_validator_t *val) {
                        if (rdataset->trust != dns_trust_secure)
                                continue;
 
-                       if (((val->attributes & VALATTR_NEEDNODATA) != 0 ||
+                       if (rdataset->type == dns_rdatatype_nsec &&
+                           ((val->attributes & VALATTR_NEEDNODATA) != 0 ||
                             (val->attributes & VALATTR_NEEDNOWILDCARD) != 0) &&
                            (val->attributes & VALATTR_FOUNDNODATA) == 0 &&
                            (val->attributes & VALATTR_FOUNDNOWILDCARD) == 0 &&
@@ -2075,6 +2497,31 @@ checkwildcard(dns_validator_t *val) {
                                                         name;
                                return (ISC_R_SUCCESS);
                        }
+
+                       if (rdataset->type == dns_rdatatype_nsec3 &&
+                           ((val->attributes & VALATTR_NEEDNODATA) != 0 ||
+                            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0) &&
+                           (val->attributes & VALATTR_FOUNDNODATA) == 0 &&
+                           (val->attributes & VALATTR_FOUNDNOWILDCARD) == 0 &&
+                           nsec3noexistnodata(val, wild, name, rdataset,
+                                              zonename, &exists, &data,
+                                              NULL, NULL, NULL, NULL, NULL,
+                                              NULL) == ISC_R_SUCCESS)
+                       {
+                               dns_name_t **proofs = val->event->proofs;
+                               if (exists && !data)
+                                       val->attributes |= VALATTR_FOUNDNODATA;
+                               if (exists && !data && NEEDNODATA(val))
+                                       proofs[DNS_VALIDATOR_NODATAPROOF] =
+                                                        name;
+                               if (!exists)
+                                       val->attributes |=
+                                                VALATTR_FOUNDNOWILDCARD;
+                               if (!exists && NEEDNOQNAME(val))
+                                       proofs[DNS_VALIDATOR_NOWILDCARDPROOF] =
+                                                        name;
+                               return (ISC_R_SUCCESS);
+                       }
                }
        }
        if (result == ISC_R_NOMORE)
@@ -2082,6 +2529,170 @@ checkwildcard(dns_validator_t *val) {
        return (result);
 }
 
+
+static isc_result_t
+findnsec3proofs(dns_validator_t *val) {
+       dns_name_t *name;
+       dns_message_t *message = val->event->message;
+       isc_result_t result;
+       isc_boolean_t exists, data, optout, unknown;
+       isc_boolean_t setclosest, setnearest;
+       dns_fixedname_t fclosest, fnearest, fzonename;
+       dns_name_t *closest, *nearest, *zonename;
+       dns_name_t **proofs = val->event->proofs;
+
+       dns_fixedname_init(&fclosest);
+       dns_fixedname_init(&fnearest);
+       dns_fixedname_init(&fzonename);
+       closest = dns_fixedname_name(&fclosest);
+       nearest = dns_fixedname_name(&fnearest);
+       zonename = dns_fixedname_name(&fzonename);
+
+       for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+            result == ISC_R_SUCCESS;
+            result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
+       {
+               dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+
+               name = NULL;
+               dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+
+               for (rdataset = ISC_LIST_HEAD(name->list);
+                    rdataset != NULL;
+                    rdataset = ISC_LIST_NEXT(rdataset, link))
+               {
+                       if (rdataset->type != dns_rdatatype_nsec3)
+                               continue;
+
+                       for (sigrdataset = ISC_LIST_HEAD(name->list);
+                            sigrdataset != NULL;
+                            sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
+                       {
+                               if (sigrdataset->type == dns_rdatatype_rrsig &&
+                                   sigrdataset->covers == dns_rdatatype_nsec3)
+                                       break;
+                       }
+                       if (sigrdataset == NULL)
+                               continue;
+
+                       if (rdataset->trust != dns_trust_secure)
+                               continue;
+
+                       result = nsec3noexistnodata(val, val->event->name,
+                                                   name, rdataset,
+                                                   zonename, NULL, NULL, NULL,
+                                                   NULL, NULL, NULL, NULL,
+                                                   NULL);
+                       if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS)
+                               return (result);
+               }
+       }
+       if (result != ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+
+       if (dns_name_countlabels(zonename) == 0)
+               return (ISC_R_SUCCESS);
+
+       for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+            result == ISC_R_SUCCESS;
+            result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
+       {
+               dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+
+               name = NULL;
+               dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+
+               for (rdataset = ISC_LIST_HEAD(name->list);
+                    rdataset != NULL;
+                    rdataset = ISC_LIST_NEXT(rdataset, link))
+               {
+                       if (rdataset->type != dns_rdatatype_nsec3)
+                               continue;
+
+                       for (sigrdataset = ISC_LIST_HEAD(name->list);
+                            sigrdataset != NULL;
+                            sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
+                       {
+                               if (sigrdataset->type == dns_rdatatype_rrsig &&
+                                   sigrdataset->covers == dns_rdatatype_nsec3)
+                                       break;
+                       }
+                       if (sigrdataset == NULL)
+                               continue;
+
+                       if (rdataset->trust != dns_trust_secure)
+                               continue;
+
+                       /*
+                        * We process all NSEC3 records to find the closest
+                        * encloser and nearest name to the closest encloser.
+                        */
+                       setclosest = setnearest = ISC_FALSE;
+                       optout = ISC_FALSE;
+                       unknown = ISC_FALSE;
+                       result = nsec3noexistnodata(val, val->event->name,
+                                                   name, rdataset,
+                                                   zonename, &exists,
+                                                   &data, &optout, &unknown,
+                                                   &setclosest, &setnearest,
+                                                   closest, nearest);
+                       if (setclosest)
+                               proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name;
+                       if (unknown)
+                               val->attributes |= VALATTR_FOUNDUNKNOWN;
+                       if (result != ISC_R_SUCCESS)
+                               continue;
+                       if (exists && !data && NEEDNODATA(val)) {
+                               val->attributes |= VALATTR_FOUNDNODATA;
+                               proofs[DNS_VALIDATOR_NODATAPROOF] = name;
+                       }
+                       if (!exists && setnearest) {
+                               val->attributes |= VALATTR_FOUNDNOQNAME;
+                               proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name;
+                               if (optout)
+                                       val->attributes |= VALATTR_FOUNDOPTOUT;
+                       }
+               }
+       }
+       if (result != ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+
+       /*
+        * To know we have a valid noqname and optout proofs we need to also
+        * have a valid closest encloser.  Otherwise we could still be looking
+        * at proofs from the parent zone.
+        */
+       if (dns_name_countlabels(closest) > 0 &&
+           dns_name_countlabels(nearest) ==
+           dns_name_countlabels(closest) + 1 &&
+           dns_name_issubdomain(nearest, closest))
+       {
+               val->attributes |= VALATTR_FOUNDCLOSEST;
+               result = dns_name_concatenate(dns_wildcardname, closest,
+                                             dns_fixedname_name(&val->wild),
+                                             NULL);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       } else {
+               val->attributes &= ~VALATTR_FOUNDNOQNAME;
+               val->attributes &= ~VALATTR_FOUNDOPTOUT;
+               proofs[DNS_VALIDATOR_NOQNAMEPROOF] = NULL;
+       }
+
+       /*
+        * Do we need to check for the wildcard?
+        */
+       if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
+           (val->attributes & VALATTR_FOUNDCLOSEST) != 0 &&
+           (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
+             (val->attributes & VALATTR_FOUNDNODATA) == 0) ||
+            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0)) {
+               result = checkwildcard(val, dns_rdatatype_nsec3, zonename);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       }
+       return (result);
+}
+
 /*%
  * Prove a negative answer is good or that there is a NOQNAME when the
  * answer is from a wildcard.
@@ -2187,7 +2798,10 @@ nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
        if ((val->attributes & VALATTR_NEEDNODATA) == 0 &&
            (val->attributes & VALATTR_NEEDNOWILDCARD) == 0 &&
            (val->attributes & VALATTR_NEEDNOQNAME) != 0) {
-               if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0) {
+               if ((val->attributes & VALATTR_FOUNDNOQNAME) == 0)
+                       findnsec3proofs(val);
+               if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
+                   (val->attributes & VALATTR_FOUNDCLOSEST) != 0) {
                        validator_log(val, ISC_LOG_DEBUG(3),
                                      "noqname proof found");
                        validator_log(val, ISC_LOG_DEBUG(3),
@@ -2195,34 +2809,57 @@ nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
                        val->event->rdataset->trust = dns_trust_secure;
                        val->event->sigrdataset->trust = dns_trust_secure;
                        return (ISC_R_SUCCESS);
+               } else if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0 &&
+                          dns_name_countlabels(dns_fixedname_name(&val->wild))
+                                        != 0) {
+                       validator_log(val, ISC_LOG_DEBUG(3),
+                                     "optout proof found");
+                       val->event->optout = ISC_TRUE;
+                       markanswer(val);
+                       return (ISC_R_SUCCESS);
+               } else if ((val->attributes & VALATTR_FOUNDUNKNOWN) != 0) {
+                       validator_log(val, ISC_LOG_DEBUG(3),
+                                     "unknown NSEC3 hash algorithm found");
+                       markanswer(val);
+                       return (ISC_R_SUCCESS);
                }
                validator_log(val, ISC_LOG_DEBUG(3),
                              "noqname proof not found");
                return (DNS_R_NOVALIDNSEC);
        }
 
+       if ((val->attributes & VALATTR_FOUNDNOQNAME) == 0 &&
+           (val->attributes & VALATTR_FOUNDNODATA) == 0)
+               findnsec3proofs(val);
+
        /*
         * Do we need to check for the wildcard?
         */
        if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
+           (val->attributes & VALATTR_FOUNDCLOSEST) != 0 &&
            (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
              (val->attributes & VALATTR_FOUNDNODATA) == 0) ||
             (val->attributes & VALATTR_NEEDNOWILDCARD) != 0)) {
-               result = checkwildcard(val);
+               result = checkwildcard(val, dns_rdatatype_nsec, NULL);
                if (result != ISC_R_SUCCESS)
                        return (result);
        }
 
        if (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
-            (val->attributes & VALATTR_FOUNDNODATA) != 0) ||
+            ((val->attributes & VALATTR_FOUNDNODATA) != 0 ||
+             (val->attributes & VALATTR_FOUNDOPTOUT) != 0)) ||
            ((val->attributes & VALATTR_NEEDNOQNAME) != 0 &&
             (val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
             (val->attributes & VALATTR_NEEDNOWILDCARD) != 0 &&
-            (val->attributes & VALATTR_FOUNDNOWILDCARD) != 0)) {
+            (val->attributes & VALATTR_FOUNDNOWILDCARD) != 0 &&
+            (val->attributes & VALATTR_FOUNDCLOSEST) != 0)) {
+               if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0)
+                       val->event->optout = ISC_TRUE;
                validator_log(val, ISC_LOG_DEBUG(3),
                              "nonexistence proof(s) found");
                return (ISC_R_SUCCESS);
        }
+               findnsec3proofs(val);
 
        validator_log(val, ISC_LOG_DEBUG(3),
                      "nonexistence proof(s) not found");
@@ -2718,6 +3355,15 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                        return (DNS_R_WAIT);
                }
        }
+
+/*
+       if ((val->attributes & VALATTR_NEEDOPTOUT) == 0 &&
+           val->event->message != NULL) {
+               val->attributes |= VALATTR_NEEDOPTOUT;
+               return (nsecvalidate(val, ISC_FALSE));
+       }
+*/
+
        validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed");
        return (DNS_R_NOTINSECURE); /* Couldn't complete insecurity proof */
 
@@ -2898,6 +3544,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
        event->sigrdataset = sigrdataset;
        event->message = message;
        memset(event->proofs, 0, sizeof(event->proofs));
+       event->optout = ISC_FALSE;
        result = isc_mutex_init(&val->lock);
        if (result != ISC_R_SUCCESS)
                goto cleanup_event;
@@ -2927,6 +3574,8 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
        dns_rdataset_init(&val->frdataset);
        dns_rdataset_init(&val->fsigrdataset);
        dns_fixedname_init(&val->wild);
+       dns_fixedname_init(&val->nearest);
+       dns_fixedname_init(&val->closest);
        ISC_LINK_INIT(val, link);
        val->magic = VALIDATOR_MAGIC;
 
index 55761b9d7d1ace80555a051891fa76b2e93f127a..48a56c7f07147bbfb7ea68f76f9f27d9eaa23533 100644 (file)
@@ -97,8 +97,10 @@ dns_db_endload
 dns_db_expirenode
 dns_db_find
 dns_db_findnode
+dns_db_findnsec3node
 dns_db_findrdataset
 dns_db_findzonecut
+dns_db_getnsec3parameters
 dns_db_getoriginnode
 dns_db_getrrsetstats
 dns_db_getsoaserial
@@ -351,8 +353,20 @@ dns_name_towire
 dns_ncache_add
 dns_ncache_getrdataset
 dns_ncache_towire
+dns_nsec3_active
+dns_nsec3_addnsec3
+dns_nsec3_addnsec3s
+dns_nsec3_buildrdata
+dns_nsec3_delnsec3
+dns_nsec3_delnsec3s
+dns_nsec3_hashlength
+dns_nsec3_hashname
+dns_nsec3_maxiterations
+dns_nsec3_supportedhash
+dns_nsec3_typepresent
 dns_nsec_build
 dns_nsec_buildrdata
+dns_nsec_nseconly
 dns_nsec_typepresent
 dns_opcode_totext
 dns_opcodestats_create
@@ -454,6 +468,7 @@ dns_rdataset_current
 dns_rdataset_disassociate
 dns_rdataset_first
 dns_rdataset_getadditional
+dns_rdataset_getclosest
 dns_rdataset_getnoqname
 dns_rdataset_init
 dns_rdataset_invalidate
index 46252da6c9c26ab64688ae9614b9cb0f7f767e47..ce283b2fe90d28f79d4f08ccc6a36454f3b35915 100644 (file)
@@ -246,6 +246,10 @@ SOURCE=..\include\dns\nsec.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\include\dns\nsec3.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\include\dns\order.h
 # End Source File
 # Begin Source File
@@ -530,6 +534,10 @@ SOURCE=..\nsec.c
 # End Source File
 # Begin Source File
 
+SOURCE=..\nsec3.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\order.c
 # End Source File
 # Begin Source File
index 0a324da07ce7b5639d08038d44c83b72f5089511..2296708c774f57fa7cb34202f163477cea588656 100644 (file)
@@ -157,6 +157,7 @@ CLEAN :
        -@erase "$(INTDIR)\name.obj"
        -@erase "$(INTDIR)\ncache.obj"
        -@erase "$(INTDIR)\nsec.obj"
+       -@erase "$(INTDIR)\nsec3.obj"
        -@erase "$(INTDIR)\openssl_link.obj"
        -@erase "$(INTDIR)\openssldh_link.obj"
        -@erase "$(INTDIR)\openssldsa_link.obj"
@@ -277,6 +278,7 @@ LINK32_OBJS= \
        "$(INTDIR)\name.obj" \
        "$(INTDIR)\ncache.obj" \
        "$(INTDIR)\nsec.obj" \
+       "$(INTDIR)\nsec3.obj" \
        "$(INTDIR)\order.obj" \
        "$(INTDIR)\peer.obj" \
        "$(INTDIR)\portlist.obj" \
@@ -429,6 +431,8 @@ CLEAN :
        -@erase "$(INTDIR)\ncache.sbr"
        -@erase "$(INTDIR)\nsec.obj"
        -@erase "$(INTDIR)\nsec.sbr"
+       -@erase "$(INTDIR)\nsec3.obj"
+       -@erase "$(INTDIR)\nsec3.sbr"
        -@erase "$(INTDIR)\openssl_link.obj"
        -@erase "$(INTDIR)\openssl_link.sbr"
        -@erase "$(INTDIR)\openssldh_link.obj"
@@ -586,6 +590,7 @@ BSC32_SBRS= \
        "$(INTDIR)\name.sbr" \
        "$(INTDIR)\ncache.sbr" \
        "$(INTDIR)\nsec.sbr" \
+       "$(INTDIR)\nsec3.sbr" \
        "$(INTDIR)\order.sbr" \
        "$(INTDIR)\peer.sbr" \
        "$(INTDIR)\portlist.sbr" \
@@ -672,6 +677,7 @@ LINK32_OBJS= \
        "$(INTDIR)\name.obj" \
        "$(INTDIR)\ncache.obj" \
        "$(INTDIR)\nsec.obj" \
+       "$(INTDIR)\nsec3.obj" \
        "$(INTDIR)\order.obj" \
        "$(INTDIR)\peer.obj" \
        "$(INTDIR)\portlist.obj" \
@@ -1262,6 +1268,24 @@ SOURCE=..\nsec.c
        $(CPP) $(CPP_PROJ) $(SOURCE)
 
 
+!ENDIF 
+
+SOURCE=..\nsec3.c
+
+!IF  "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\nsec3.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\nsec3.obj"  "$(INTDIR)\nsec3.sbr" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
 !ENDIF 
 
 SOURCE=..\order.c
index e8d89c112a35d9d09fb70d6924892cc25adc3904..221a76335df73d3df46327919aa66bc433a55c79 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone.c,v 1.480 2008/04/09 04:29:16 marka Exp $ */
+/* $Id: zone.c,v 1.481 2008/09/24 02:46:22 marka Exp $ */
 
 /*! \file */
 
@@ -51,6 +51,7 @@
 #include <dns/message.h>
 #include <dns/name.h>
 #include <dns/nsec.h>
+#include <dns/nsec3.h>
 #include <dns/peer.h>
 #include <dns/rcode.h>
 #include <dns/rdataclass.h>
@@ -98,6 +99,8 @@
 #define RANGE(a, min, max) \
                (((a) < (min)) ? (min) : ((a) < (max) ? (a) : (max)))
 
+#define NSEC3REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0)
+
 /*
  * Default values.
  */
@@ -121,6 +124,8 @@ typedef struct dns_io dns_io_t;
 typedef ISC_LIST(dns_io_t) dns_iolist_t;
 typedef struct dns_signing dns_signing_t;
 typedef ISC_LIST(dns_signing_t) dns_signinglist_t;
+typedef struct dns_nsec3chain dns_nsec3chain_t;
+typedef ISC_LIST(dns_nsec3chain_t) dns_nsec3chainlist_t;
 
 #define DNS_ZONE_CHECKLOCK
 #ifdef DNS_ZONE_CHECKLOCK
@@ -191,6 +196,7 @@ struct dns_zone {
        isc_time_t              resigntime;
        isc_time_t              keywarntime;
        isc_time_t              signingtime;
+       isc_time_t              nsec3chaintime;
        isc_uint32_t            serial;
        isc_uint32_t            refresh;
        isc_uint32_t            retry;
@@ -288,7 +294,8 @@ struct dns_zone {
        /*%
         * Keys that are signing the zone for the first time.
         */
-       ISC_LIST(dns_signing_t) signing;
+       dns_signinglist_t       signing;
+       dns_nsec3chainlist_t    nsec3chain;
        /*%
         * Signing / re-signing quantum stopping parameters.
         */
@@ -476,9 +483,46 @@ struct dns_signing {
        dns_dbiterator_t        *dbiterator;
        dns_secalg_t            algorithm;
        isc_uint16_t            keyid;
+       isc_boolean_t           delete;
+       isc_boolean_t           done;
        ISC_LINK(dns_signing_t) link;
 };
 
+struct dns_nsec3chain {
+       unsigned int                    magic;
+       dns_db_t                        *db;
+       dns_dbiterator_t                *dbiterator;
+       dns_rdata_nsec3param_t          nsec3param;
+       unsigned char                   salt[255];
+       isc_boolean_t                   done;
+       isc_boolean_t                   seen_nsec;
+       isc_boolean_t                   delete_nsec;
+       isc_boolean_t                   save_delete_nsec;
+       ISC_LINK(dns_nsec3chain_t)      link;
+};
+/*%<
+ * 'dbiterator' contains a iterator for the database.  If we are creating
+ * a NSEC3 chain only the non-NSEC3 nodes will be iterated.  If we are
+ * removing a NSEC3 chain then both NSEC3 and non-NSEC3 nodes will be
+ * iterated.
+ *
+ * 'nsec3param' contains the parameters of the NSEC3 chain being created
+ * or removed.
+ *
+ * 'salt' is buffer space and is referenced via 'nsec3param.salt'.
+ *
+ * 'seen_nsec' will be set to true if, while iterating the zone to create a
+ * NSEC3 chain, a NSEC record is seen.
+ *
+ * 'delete_nsec' will be set to true if, at the completion of the creation
+ * of a NSEC3 chain, 'seen_nsec' is true.  If 'delete_nsec' is true then we
+ * are in the process of deleting the NSEC chain.
+ *
+ * 'save_delete_nsec' is used to store the initial state of 'delete_nsec'
+ * so it can be recovered in the event of a error.
+ */
+
+
 #define SEND_BUFFER_SIZE 2048
 
 static void zone_settimer(dns_zone_t *, isc_time_t *);
@@ -562,7 +606,7 @@ static isc_boolean_t dns_zonemgr_unreachable(dns_zonemgr_t *zmgr,
                                             isc_sockaddr_t *local,
                                             isc_time_t *now);
 static isc_result_t zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
-                                    isc_uint16_t keyid);
+                                    isc_uint16_t keyid, isc_boolean_t delete);
 
 #define ENTER zone_debuglog(zone, me, 1, "enter")
 
@@ -670,6 +714,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
        isc_time_settoepoch(&zone->resigntime);
        isc_time_settoepoch(&zone->keywarntime);
        isc_time_settoepoch(&zone->signingtime);
+       isc_time_settoepoch(&zone->nsec3chaintime);
        zone->serial = 0;
        zone->refresh = DNS_ZONE_DEFAULTREFRESH;
        zone->retry = DNS_ZONE_DEFAULTRETRY;
@@ -733,6 +778,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
        zone->isself = NULL;
        zone->isselfarg = NULL;
        ISC_LIST_INIT(zone->signing);
+       ISC_LIST_INIT(zone->nsec3chain);
        zone->signatures = 10;
        zone->nodes = 100;
        zone->privatetype = (dns_rdatatype_t)0xffffU;
@@ -773,6 +819,7 @@ static void
 zone_free(dns_zone_t *zone) {
        isc_mem_t *mctx = NULL;
        dns_signing_t *signing;
+       dns_nsec3chain_t *nsec3chain;
 
        REQUIRE(DNS_ZONE_VALID(zone));
        REQUIRE(isc_refcount_current(&zone->erefs) == 0);
@@ -803,6 +850,14 @@ zone_free(dns_zone_t *zone) {
                dns_dbiterator_destroy(&signing->dbiterator);
                isc_mem_put(zone->mctx, signing, sizeof *signing);
        }
+       for (nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+            nsec3chain != NULL;
+            nsec3chain = ISC_LIST_HEAD(zone->nsec3chain)) {
+               ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain, link);
+               dns_db_detach(&nsec3chain->db);
+               dns_dbiterator_destroy(&nsec3chain->dbiterator);
+               isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+       }
        if (zone->masterfile != NULL)
                isc_mem_free(zone->mctx, zone->masterfile);
        zone->masterfile = NULL;
@@ -1893,7 +1948,7 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
        dns_rdataset_init(&rdataset);
        dns_rdata_init(&rdata);
 
-       result = dns_db_createiterator(db, ISC_FALSE, &dbiterator);
+       result = dns_db_createiterator(db, 0, &dbiterator);
        if (result != ISC_R_SUCCESS)
                return (ISC_TRUE);
 
@@ -2057,7 +2112,7 @@ zone_check_dnskeys(dns_zone_t *zone, dns_db_t *db) {
 }
 
 static void
-set_signingtime(dns_zone_t *zone) {
+resume_signingwithkey(dns_zone_t *zone) {
        dns_dbnode_t *node = NULL;
        dns_dbversion_t *version = NULL;
        dns_rdata_t rdata = DNS_RDATA_INIT;
@@ -2082,13 +2137,13 @@ set_signingtime(dns_zone_t *zone) {
             result = dns_rdataset_next(&rdataset))
        {
                dns_rdataset_current(&rdataset, &rdata);
-               if (rdata.length != 4 || rdata.data[3] != 0) {
+               if (rdata.length != 5 || rdata.data[4] != 0) {
                        dns_rdata_reset(&rdata);
                        continue;
                }
 
                result = zone_signwithkey(zone, rdata.data[0],
-                                         (rdata.data[1] << 8) | rdata.data[2]);
+                                         (rdata.data[1] << 8) | rdata.data[2],                                           ISC_TF(rdata.data[3]));
                if (result != ISC_R_SUCCESS) {
                        dns_zone_log(zone, ISC_LOG_ERROR,
                                     "zone_signwithkey failed: %s",
@@ -2106,6 +2161,127 @@ set_signingtime(dns_zone_t *zone) {
 
 }
 
+static isc_result_t
+zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) {
+       dns_nsec3chain_t *nsec3chain, *current;
+       isc_result_t result;
+       isc_time_t now;
+       unsigned int options = 0;
+
+       nsec3chain = isc_mem_get(zone->mctx, sizeof *nsec3chain);
+       if (nsec3chain == NULL)
+               return (ISC_R_NOMEMORY);
+
+       nsec3chain->magic = 0;
+       nsec3chain->done = ISC_FALSE;
+       nsec3chain->db = NULL;
+       nsec3chain->dbiterator = NULL;
+       nsec3chain->nsec3param.common.rdclass = nsec3param->common.rdclass;
+       nsec3chain->nsec3param.common.rdtype = nsec3param->common.rdtype;
+       nsec3chain->nsec3param.hash = nsec3param->hash;
+       nsec3chain->nsec3param.iterations = nsec3param->iterations;
+       nsec3chain->nsec3param.flags = nsec3param->flags;
+       nsec3chain->nsec3param.salt_length = nsec3param->salt_length;
+       memcpy(nsec3chain->salt, nsec3param->salt, nsec3param->salt_length);
+       nsec3chain->nsec3param.salt = nsec3chain->salt;
+       nsec3chain->seen_nsec = ISC_FALSE;
+       nsec3chain->delete_nsec = ISC_FALSE;
+       nsec3chain->save_delete_nsec = ISC_FALSE;
+
+       for (current = ISC_LIST_HEAD(zone->nsec3chain);
+            current != NULL;
+            current = ISC_LIST_NEXT(current, link)) {
+               if (current->db == zone->db &&
+                   current->nsec3param.hash == nsec3param->hash &&
+                   current->nsec3param.iterations == nsec3param->iterations &&
+                   current->nsec3param.salt_length == nsec3param->salt_length
+                   && !memcmp(current->nsec3param.salt, nsec3param->salt,
+                              nsec3param->salt_length))
+                       current->done = ISC_TRUE;
+       }
+
+       if (zone->db != NULL) {
+               dns_db_attach(zone->db, &nsec3chain->db);
+               if ((nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0)
+                       options = DNS_DB_NONSEC3;
+               result = dns_db_createiterator(nsec3chain->db, options,
+                                               &nsec3chain->dbiterator);
+               if (result == ISC_R_SUCCESS)
+                       dns_dbiterator_first(nsec3chain->dbiterator);
+               if (result == ISC_R_SUCCESS) {
+                       dns_dbiterator_pause(nsec3chain->dbiterator);
+                       ISC_LIST_INITANDAPPEND(zone->nsec3chain,
+                                              nsec3chain, link);
+                       nsec3chain = NULL;
+                       if (isc_time_isepoch(&zone->nsec3chaintime)) {
+                               TIME_NOW(&now);
+                               zone->nsec3chaintime = now;
+                               if (zone->task != NULL)
+                                       zone_settimer(zone, &now);
+                       }
+               }
+       } else
+               result = ISC_R_NOTFOUND;
+
+       if (nsec3chain != NULL) {
+               if (nsec3chain->db != NULL)
+                       dns_db_detach(&nsec3chain->db);
+               if (nsec3chain->dbiterator != NULL)
+                       dns_dbiterator_destroy(&nsec3chain->dbiterator);
+               isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+       }
+       return (result);
+}
+
+static void
+resume_addnsec3chain(dns_zone_t *zone) {
+       dns_dbnode_t *node = NULL;
+       dns_dbversion_t *version = NULL;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+       dns_rdata_nsec3param_t nsec3param;
+
+       result = dns_db_findnode(zone->db, &zone->origin, ISC_FALSE, &node);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       dns_db_currentversion(zone->db, &version);
+       dns_rdataset_init(&rdataset);
+       result = dns_db_findrdataset(zone->db, node, version,
+                                    dns_rdatatype_nsec3param,
+                                    dns_rdatatype_none, 0,
+                                    &rdataset, NULL);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset))
+       {
+               dns_rdataset_current(&rdataset, &rdata);
+               result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+               if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 ||
+                   (nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) {
+                       result = zone_addnsec3chain(zone, &nsec3param);
+                       if (result != ISC_R_SUCCESS) {
+                               dns_zone_log(zone, ISC_LOG_ERROR,
+                                            "zone_addnsec3chain failed: %s",
+                                            dns_result_totext(result));
+                       }
+               }
+               dns_rdata_reset(&rdata);
+       }
+       dns_rdataset_disassociate(&rdataset);
+
+ cleanup:
+       if (node != NULL)
+               dns_db_detachnode(zone->db, &node);
+       if (version != NULL)
+               dns_db_closeversion(zone->db, &version, ISC_FALSE);
+}
+
 static void
 set_resigntime(dns_zone_t *zone) {
        dns_rdataset_t rdataset;
@@ -2131,6 +2307,96 @@ set_resigntime(dns_zone_t *zone) {
        isc_time_set(&zone->resigntime, resign, nanosecs);
 }
 
+static isc_result_t
+check_nsec3param(dns_zone_t *zone, dns_db_t *db) {
+       dns_dbnode_t *node = NULL;
+       dns_rdataset_t rdataset;
+       dns_dbversion_t *version = NULL;
+       dns_rdata_nsec3param_t nsec3param;
+       isc_boolean_t ok = ISC_FALSE;
+       isc_result_t result;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       isc_boolean_t dynamic = (zone->type == dns_zone_master) ?
+                               zone_isdynamic(zone) : ISC_FALSE;
+
+       dns_rdataset_init(&rdataset);
+       result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node);
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR,
+                            "nsec3param lookup failure: %s",
+                            dns_result_totext(result));
+               return (result);
+       }
+       dns_db_currentversion(db, &version);
+
+       result = dns_db_findrdataset(db, node, version,
+                                    dns_rdatatype_nsec3param,
+                                    dns_rdatatype_none, 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND) {
+               result = ISC_R_SUCCESS;
+               goto cleanup;
+       }
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR,
+                            "nsec3param lookup failure: %s",
+                            dns_result_totext(result));
+               goto cleanup;
+       }
+
+       /*
+        * For dynamic zones we must support every algorithm so we can
+        * regenerate all the NSEC3 chains.
+        * For non-dynamic zones we only need to find a supported algorithm.
+        */
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset))
+       {
+               dns_rdataset_current(&rdataset, &rdata);
+               result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+               dns_rdata_reset(&rdata);
+               INSIST(result == ISC_R_SUCCESS);
+               if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NSEC3TESTZONE) &&
+                   nsec3param.hash == DNS_NSEC3_UNKNOWNALG && !dynamic)
+               {
+                       dns_zone_log(zone, ISC_LOG_WARNING,
+                            "nsec3 test \"unknown\" hash algorithm found: %u",
+                                    nsec3param.hash);
+                       ok = ISC_TRUE;
+               } else if (!dns_nsec3_supportedhash(nsec3param.hash)) {
+                       if (dynamic) {
+                               dns_zone_log(zone, ISC_LOG_ERROR,
+                                            "unsupported nsec3 hash algorithm"
+                                            " in dynamic zone: %u",
+                                            nsec3param.hash);
+                               result = DNS_R_BADZONE;
+                               /* Stop second error message. */
+                               ok = ISC_TRUE;
+                               break;
+                       } else
+                               dns_zone_log(zone, ISC_LOG_WARNING,
+                                    "unsupported nsec3 hash algorithm: %u",
+                                            nsec3param.hash);
+               } else
+                       ok = ISC_TRUE;
+       }
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+
+       if (!ok) {
+               result = DNS_R_BADZONE;
+               dns_zone_log(zone, ISC_LOG_ERROR,
+                            "no supported nsec3 hash algorithm");
+       }
+
+ cleanup:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       dns_db_closeversion(db, &version, ISC_FALSE);
+       dns_db_detachnode(db, &node);
+       return (result);
+}
+
 static isc_result_t
 zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
              isc_result_t result)
@@ -2256,6 +2522,11 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
                        result = DNS_R_BADZONE;
                        goto cleanup;
                }
+               if (zone->type != dns_zone_stub) {
+                       result = check_nsec3param(zone, db);
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+               }
                if (zone->type == dns_zone_master &&
                    DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKINTEGRITY) &&
                    !integrity_checks(zone, db)) {
@@ -2381,7 +2652,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
        if (zone->task != NULL) {
                if (zone->type == dns_zone_master) {
                        set_resigntime(zone);
-                       set_signingtime(zone);
+                       resume_signingwithkey(zone);
+                       resume_addnsec3chain(zone);
                }
                zone_settimer(zone, &now);
        }
@@ -3404,7 +3676,10 @@ del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
 
        dns_rdataset_init(&rdataset);
 
-       result = dns_db_findnode(db, name, ISC_FALSE, &node);
+       if (type == dns_rdatatype_nsec3) 
+               result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
+       else
+               result = dns_db_findnode(db, name, ISC_FALSE, &node);
        if (result == ISC_R_NOTFOUND)
                return (ISC_R_SUCCESS);
        if (result != ISC_R_SUCCESS)
@@ -3514,7 +3789,10 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
        dns_rdataset_init(&rdataset);
        isc_buffer_init(&buffer, data, sizeof(data));
 
-       result = dns_db_findnode(db, name, ISC_FALSE, &node);
+       if (type == dns_rdatatype_nsec3) 
+               result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
+       else
+               result = dns_db_findnode(db, name, ISC_FALSE, &node);
        if (result == ISC_R_NOTFOUND)
                return (ISC_R_SUCCESS);
        if (result != ISC_R_SUCCESS)
@@ -3777,7 +4055,7 @@ next_active(dns_db_t *db, dns_dbversion_t *version, dns_name_t *oldname,
        dns_rdatasetiter_t *rdsit = NULL;
        dns_dbnode_t *node = NULL;
 
-       CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
+       CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit));
        CHECK(dns_dbiterator_seek(dbit, oldname));
        do {
                result = dns_dbiterator_next(dbit);
@@ -3848,13 +4126,52 @@ signed_with_key(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        return (ISC_FALSE);
 }
 
+static isc_result_t
+add_nsec(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+        dns_dbnode_t *node, dns_ttl_t ttl, isc_boolean_t bottom,
+        dns_diff_t *diff)
+{
+       dns_fixedname_t fixed;
+       dns_name_t *next;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       isc_result_t result;
+       unsigned char nsecbuffer[DNS_NSEC_BUFFERSIZE];
+
+       dns_fixedname_init(&fixed);
+       next = dns_fixedname_name(&fixed);
+
+       CHECK(next_active(db, version, name, next, bottom));
+       CHECK(dns_nsec_buildrdata(db, version, node, next, nsecbuffer,
+                                 &rdata));
+       if (dns_name_equal(dns_db_origin(db), name)) {
+               /*
+                * Set the OPT bit to indicate that this is a
+                * partially secure zone.
+                */
+               isc_region_t region;
+
+               dns_rdata_toregion(&rdata, &region);
+               dns_name_fromregion(next, &region);
+               isc_region_consume(&region, next->length);
+               INSIST(region.length > (2 + dns_rdatatype_opt / 8) &&
+                      region.base[0] == 0 &&
+                      region.base[1] > dns_rdatatype_opt / 8);
+               set_bit(region.base + 2, dns_rdatatype_opt);
+       }
+       CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, ttl,
+                           &rdata));
+ failure:
+       return (result);
+}
+
 static isc_result_t
 sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node,
-           dns_dbversion_t *version, dst_key_t *key,
+           dns_dbversion_t *version, isc_boolean_t build_nsec3,
+           isc_boolean_t build_nsec, dst_key_t *key,
            isc_stdtime_t inception, isc_stdtime_t expire,
-           unsigned int minimum,
-           isc_boolean_t is_ksk, isc_boolean_t *delegation,
-           dns_diff_t *diff, isc_int32_t *signatures, isc_mem_t *mctx)
+           unsigned int minimum, isc_boolean_t is_ksk,
+           isc_boolean_t *delegation, dns_diff_t *diff,
+           isc_int32_t *signatures, isc_mem_t *mctx)
 {
        isc_result_t result;
        dns_rdatasetiter_t *iterator = NULL;
@@ -3862,11 +4179,9 @@ sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node,
        dns_rdata_t rdata = DNS_RDATA_INIT;
        isc_buffer_t buffer;
        unsigned char data[1024];
-       isc_boolean_t seen_soa, seen_ns, seen_rr, seen_dname, seen_nsec;
-       unsigned char nsecbuffer[DNS_NSEC_BUFFERSIZE];
+       isc_boolean_t seen_soa, seen_ns, seen_rr, seen_dname, seen_nsec,
+                     seen_nsec3, seen_ds;
        isc_boolean_t bottom;
-       dns_name_t *next;
-       dns_fixedname_t fixed;
 
        result = dns_db_allrdatasets(db, node, version, 0, &iterator);
        if (result != ISC_R_SUCCESS) {
@@ -3876,7 +4191,8 @@ sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node,
        }
        dns_rdataset_init(&rdataset);
        isc_buffer_init(&buffer, data, sizeof(data));
-       seen_rr = seen_soa = seen_ns = seen_dname = seen_nsec = ISC_FALSE;
+       seen_rr = seen_soa = seen_ns = seen_dname = seen_nsec =
+       seen_nsec3 = seen_ds = ISC_FALSE;
        for (result = dns_rdatasetiter_first(iterator);
             result == ISC_R_SUCCESS;
             result = dns_rdatasetiter_next(iterator)) {
@@ -3885,10 +4201,14 @@ sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node,
                        seen_soa = ISC_TRUE;
                else if (rdataset.type == dns_rdatatype_ns)
                        seen_ns = ISC_TRUE;
+               else if (rdataset.type == dns_rdatatype_ds)
+                       seen_ds = ISC_TRUE;
                else if (rdataset.type == dns_rdatatype_dname)
                        seen_dname = ISC_TRUE;
                else if (rdataset.type == dns_rdatatype_nsec)
                        seen_nsec = ISC_TRUE;
+               else if (rdataset.type == dns_rdatatype_nsec3)
+                       seen_nsec3 = ISC_TRUE;
                seen_rr = ISC_TRUE;
                dns_rdataset_disassociate(&rdataset);
        }
@@ -3896,32 +4216,24 @@ sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node,
                goto failure;
        if (seen_ns && !seen_soa)
                *delegation = ISC_TRUE;
-       if (!seen_nsec && seen_rr) {
+       /*
+        * Going from insecure to NSEC3.
+        * Don't generate NSEC3 records for NSEC3 records.
+        */
+       if (build_nsec3 && !seen_nsec3 && seen_rr) {
+               isc_boolean_t unsecure = !seen_ds && seen_ns && !seen_soa;
+               CHECK(dns_nsec3_addnsec3s(db, version, name, minimum,
+                                         unsecure, diff));
+               (*signatures)--;
+       } 
+       /*
+        * Going from insecure to NSEC.
+        * Don't generate NSEC records for NSEC3 records.
+        */
+       if (build_nsec && !seen_nsec3 && !seen_nsec && seen_rr) {
                /* Build and add NSEC. */
-               dns_fixedname_init(&fixed);
-               next = dns_fixedname_name(&fixed);
                bottom = (seen_ns && !seen_soa) || seen_dname;
-               CHECK(next_active(db, version, name, next, bottom));
-               CHECK(dns_nsec_buildrdata(db, version, node, next, nsecbuffer,
-                                         &rdata));
-               if (dns_name_equal(dns_db_origin(db), name)) {
-                       /*
-                        * Set the OPT bit to indicate that this is a
-                        * partially secure zone.
-                        */
-                       isc_region_t region;
-
-                       dns_rdata_toregion(&rdata, &region);
-                       dns_name_fromregion(next, &region);
-                       isc_region_consume(&region, next->length);
-                       INSIST(region.length > (2 + dns_rdatatype_opt / 8) &&
-                              region.base[0] == 0 &&
-                              region.base[1] > dns_rdatatype_opt / 8);
-                       set_bit(region.base + 2, dns_rdatatype_opt);
-               }
-               CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name,
-                                   minimum, &rdata));
-               dns_rdata_reset(&rdata);
+               CHECK(add_nsec(db, version, name, node, minimum, bottom, diff));
                /* Count a NSEC generation as a signature generation. */
                (*signatures)--;
        }
@@ -3974,106 +4286,1154 @@ updatesecure(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
        dns_dbnode_t *node = NULL;
 
        /*
-        * Check to see if the OPT bit has already been cleared.
+        * Check to see if the OPT bit has already been cleared.
+        */
+       CHECK(dns_db_getoriginnode(db, &node));
+       dns_rdataset_init(&rdataset);
+       CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec,
+                                 dns_rdatatype_none, 0, &rdataset, NULL));
+       CHECK(dns_rdataset_first(&rdataset));
+       dns_rdataset_current(&rdataset, &rdata);
+
+       /*
+        * Find the NEXT name for building the new record.
+        */
+       CHECK(dns_rdata_tostruct(&rdata, &nsec, NULL));
+
+       /*
+        * Delete the old NSEC record.
+        */
+       CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_DEL, name, minimum,
+                           &rdata));
+       dns_rdata_reset(&rdata);
+
+       /*
+        * Add the new NSEC record.
+        */
+       CHECK(dns_nsec_buildrdata(db, version, node, &nsec.next, nsecbuffer,
+                                 &rdata));
+       CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, minimum,
+                           &rdata));
+       dns_rdata_reset(&rdata);
+
+       if (secureupdated != NULL)
+               *secureupdated = ISC_TRUE;
+
+ failure:
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       return (result);
+}
+
+static isc_result_t
+updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version,
+                 dns_name_t *name, dns_rdatatype_t privatetype,
+                 dns_diff_t *diff)
+{
+       isc_result_t result;
+       dns_dbnode_t *node = NULL;
+       dns_rdataset_t rdataset;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       unsigned char data[5];
+       isc_boolean_t seen_done = ISC_FALSE;
+
+       dns_rdataset_init(&rdataset);
+       result = dns_db_getoriginnode(signing->db, &node);
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       result = dns_db_findrdataset(signing->db, node, version, privatetype,
+                                    dns_rdatatype_none, 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND) {
+               result = ISC_R_SUCCESS;
+               goto failure;
+       }
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdataset_current(&rdataset, &rdata);
+               if (rdata.length != 5 ||
+                   rdata.data[0] != signing->algorithm ||
+                   rdata.data[1] != ((signing->keyid >> 8) & 0xff) ||
+                   rdata.data[2] != (signing->keyid & 0xff)) {
+                       dns_rdata_reset(&rdata);
+                       continue;
+               }
+               if (!signing->delete && rdata.data[4] != 0)
+                       seen_done = ISC_TRUE;
+               else
+                       CHECK(update_one_rr(signing->db, version, diff,
+                                           DNS_DIFFOP_DEL, name,                                                           rdataset.ttl, &rdata));
+               dns_rdata_reset(&rdata);
+       }
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+       if (!signing->delete && !seen_done) {
+
+               data[0] = signing->algorithm;
+               data[1] = (signing->keyid >> 8) & 0xff;
+               data[2] = signing->keyid & 0xff;
+               data[3] = 0;
+               data[4] = 1;
+               rdata.length = sizeof(data);
+               rdata.data = data;
+               rdata.type = privatetype;
+                rdata.rdclass = dns_db_class(signing->db);
+               CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_ADD,
+                                   name, rdataset.ttl, &rdata));
+       }
+ failure:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       if (node != NULL)
+               dns_db_detachnode(signing->db, &node);
+       return (result);
+}
+
+static isc_result_t
+fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain,
+                isc_boolean_t active, dns_diff_t *diff)
+{
+       dns_dbnode_t *node = NULL;
+       dns_name_t *name = dns_db_origin(db);
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdataset_t rdataset;
+       dns_rdata_nsec3param_t nsec3param;
+       isc_result_t result;
+       isc_buffer_t buffer;
+       unsigned char parambuf[DNS_NSEC3PARAM_BUFFERSIZE];
+       dns_ttl_t ttl = 0;
+
+       dns_rdataset_init(&rdataset);
+
+       result = dns_db_getoriginnode(db, &node);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+                                    0, 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND)
+               goto add;
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       /*
+        * Preserve the existing ttl.
+        */
+       ttl = rdataset.ttl;
+
+       /*
+        * Delete all NSEC3PARAM records which match that in nsec3chain.
+        */
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+
+               dns_rdataset_current(&rdataset, &rdata);
+               CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+
+               if (nsec3param.hash != chain->nsec3param.hash ||
+                   (active && nsec3param.flags != 0) ||
+                   nsec3param.iterations != chain->nsec3param.iterations ||
+                   nsec3param.salt_length != chain->nsec3param.salt_length ||
+                   memcmp(nsec3param.salt, chain->nsec3param.salt,
+                          nsec3param.salt_length)) {
+                       dns_rdata_reset(&rdata);
+                       continue;
+               }
+
+               CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
+                                   name, rdataset.ttl, &rdata));
+               dns_rdata_reset(&rdata);
+       }
+       if (result != ISC_R_NOMORE)
+               goto failure;
+
+  add:
+       if ((chain->nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) {
+               result = ISC_R_SUCCESS;
+               goto failure;
+       }
+
+       /*
+        * Add a NSEC3PARAM record which matches that in nsec3chain but
+        * with all flags bits cleared.
+        *
+        * Note: we do not clear chain->nsec3param.flags as this change
+        * may be reversed.
+        */
+       isc_buffer_init(&buffer, &parambuf, sizeof(parambuf));
+       CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db),
+                                  dns_rdatatype_nsec3param,
+                                  &chain->nsec3param, &buffer));
+       rdata.data[1] = 0;      /* Clear flag bits. */
+       CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name, ttl, &rdata));
+
+  failure:
+       dns_db_detachnode(db, &node);
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       return (result);
+}
+
+static isc_result_t
+delete_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node,
+           dns_name_t *name, dns_diff_t *diff)
+{
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+
+       dns_rdataset_init(&rdataset);
+
+       result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
+                                    0, 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND)
+               return (ISC_R_SUCCESS);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+
+               dns_rdataset_current(&rdataset, &rdata);
+               CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, name,
+                                   rdataset.ttl, &rdata));
+       }
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+ failure:
+       dns_rdataset_disassociate(&rdataset);
+       return (result);
+}
+
+static isc_result_t
+deletematchingnsec3(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node,
+                   dns_name_t *name, const dns_rdata_nsec3param_t *param,
+                   dns_diff_t *diff)
+{
+       dns_rdataset_t rdataset;
+       dns_rdata_nsec3_t nsec3;
+       isc_result_t result;
+       
+       dns_rdataset_init(&rdataset);
+       result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
+                                    0, 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND)
+               return (ISC_R_SUCCESS);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+
+               dns_rdataset_current(&rdataset, &rdata);
+               CHECK(dns_rdata_tostruct(&rdata, &nsec3, NULL));
+               if (nsec3.hash != param->hash ||
+                   nsec3.iterations != param->iterations ||
+                   nsec3.salt_length != param->salt_length ||
+                   memcmp(nsec3.salt, param->salt, nsec3.salt_length))
+                       continue;
+               CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, name,
+                                   rdataset.ttl, &rdata));
+       }
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+ failure:
+       dns_rdataset_disassociate(&rdataset);
+       return (result);
+} 
+
+static isc_result_t
+need_nsec_chain(dns_db_t *db, dns_dbversion_t *ver,
+               const dns_rdata_nsec3param_t *param,
+               isc_boolean_t *answer, isc_boolean_t *updatensec)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdata_nsec3param_t myparam;
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+       
+       *answer = ISC_FALSE;
+
+       result = dns_db_getoriginnode(db, &node);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+       dns_rdataset_init(&rdataset);
+       result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
+                                    0, 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND)
+               goto check_nsec3param;
+
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       CHECK(dns_rdataset_first(&rdataset));
+       dns_rdataset_current(&rdataset, &rdata);
+
+       if (!dns_nsec_typepresent(&rdata, dns_rdatatype_opt)) {
+               /*
+                * We have a complete NSEC chain.  Signal to update
+                * the apex NSEC record.
+                */
+               *updatensec = ISC_TRUE;
+               goto failure;
+       }
+       dns_rdataset_disassociate(&rdataset);
+       dns_rdata_reset(&rdata);
+
+ check_nsec3param:
+       result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+                                    0, 0, &rdataset, NULL);
+       if (result == ISC_R_NOTFOUND) { 
+               *answer = ISC_TRUE;
+               dns_db_detachnode(db, &node);
+               return (ISC_R_SUCCESS);
+       }
+       if (result != ISC_R_SUCCESS) {
+               dns_db_detachnode(db, &node);
+               return (result);
+       }
+
+       for (result = dns_rdataset_first(&rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&rdataset)) {
+               dns_rdataset_current(&rdataset, &rdata);
+               CHECK(dns_rdata_tostruct(&rdata, &myparam, NULL));
+               dns_rdata_reset(&rdata);
+               /*
+                * Ignore any NSEC3PARAM removals.
+                */
+               if (NSEC3REMOVE(myparam.flags))
+                       continue;
+               /*
+                * Ignore the chain that we are in the process of deleting.
+                */
+               if (myparam.hash == param->hash &&
+                   myparam.iterations == param->iterations &&
+                   myparam.salt_length == param->salt_length &&
+                   !memcmp(myparam.salt, param->salt, myparam.salt_length))
+                       continue;
+               /*
+                * Found an active NSEC3 chain.
+                */
+               break;
+       }
+       if (result == ISC_R_NOMORE) {
+               *answer = ISC_TRUE;
+               result = ISC_R_SUCCESS;
+       }
+
+ failure:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       dns_db_detachnode(db, &node);
+       return (result);
+}
+
+/*
+ * Incrementally build and sign a new NSEC3 chain using the parameters
+ * requested.
+ */
+static void
+zone_nsec3chain(dns_zone_t *zone) {
+       const char *journalfile;
+       dns_db_t *db = NULL;
+       dns_dbnode_t *node = NULL;
+       dns_dbversion_t *version = NULL;
+       dns_diff_t sig_diff;
+       dns_diff_t nsec_diff;
+       dns_diff_t nsec3_diff;
+       dns_diff_t param_diff;
+       dns_fixedname_t fixed;
+       dns_fixedname_t nextfixed;
+       dns_name_t *name, *nextname;
+       dns_rdataset_t rdataset;
+       dns_nsec3chain_t *nsec3chain = NULL, *nextnsec3chain;
+       dns_nsec3chainlist_t cleanup;
+       dst_key_t *zone_keys[MAXZONEKEYS];
+       isc_int32_t signatures;
+       isc_boolean_t check_ksk, is_ksk;
+       isc_boolean_t delegation;
+       isc_boolean_t first;
+       isc_result_t result;
+       isc_stdtime_t now, inception, soaexpire, expire, stop;
+       isc_uint32_t jitter;
+       unsigned int i;
+       unsigned int nkeys = 0;
+       isc_uint32_t nodes;
+       isc_boolean_t unsecure = ISC_FALSE;
+       isc_boolean_t seen_soa, seen_ns, seen_dname, seen_ds;
+       isc_boolean_t seen_nsec, seen_nsec3, seen_rr;
+       dns_rdatasetiter_t *iterator = NULL;
+       dns_difftuple_t *tuple;
+       isc_boolean_t buildnsecchain;
+       isc_boolean_t updatensec = ISC_FALSE;
+
+       dns_rdataset_init(&rdataset);
+       dns_fixedname_init(&fixed);
+       name = dns_fixedname_name(&fixed);
+       dns_fixedname_init(&nextfixed);
+       nextname = dns_fixedname_name(&nextfixed);
+       dns_diff_init(zone->mctx, &param_diff);
+       dns_diff_init(zone->mctx, &nsec3_diff);
+       dns_diff_init(zone->mctx, &nsec_diff);
+       dns_diff_init(zone->mctx, &sig_diff);
+       sig_diff.resign = zone->sigresigninginterval;
+       ISC_LIST_INIT(cleanup);
+
+       /*
+        * Updates are disabled.  Pause for 5 minutes.
+        */
+       if (zone->update_disabled) {
+               result = ISC_R_FAILURE;
+               goto failure;
+       }
+
+       ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+       dns_db_attach(zone->db, &db);
+       ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+       result = dns_db_newversion(db, &version);
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR,
+                            "zone_nsec3chain:dns_db_newversion -> %s\n",
+                            dns_result_totext(result));
+               goto failure;
+       }
+
+       result = find_zone_keys(zone, db, version, zone->mctx,
+                               MAXZONEKEYS, zone_keys, &nkeys);
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR,
+                            "zone_nsec3chain:find_zone_keys -> %s\n",
+                            dns_result_totext(result));
+               goto failure;
+       }
+
+       isc_stdtime_get(&now);
+       inception = now - 3600; /* Allow for clock skew. */
+       soaexpire = now + dns_zone_getsigvalidityinterval(zone);
+
+       /*
+        * Spread out signatures over time if they happen to be
+        * clumped.  We don't do this for each add_sigs() call as
+        * we still want some clustering to occur.
+        */
+       isc_random_get(&jitter);
+       expire = soaexpire - jitter % 3600;
+       stop = now + 5;
+
+       check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+       if (check_ksk)
+               check_ksk = ksk_sanity(db, version);
+
+       /*
+        * We keep pulling nodes off each interator in turn until
+        * we have no more nodes to pull off or we reach the limits
+        * for this quantum.
+        */
+       nodes = zone->nodes;
+       signatures = zone->signatures;
+       LOCK_ZONE(zone);
+       nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+       UNLOCK_ZONE(zone);
+       first = ISC_TRUE;
+
+       if (nsec3chain != NULL)
+               nsec3chain->save_delete_nsec = nsec3chain->delete_nsec;
+       /*
+        * Generate new NSEC3 chains first.
+        */
+       while (nsec3chain != NULL && nodes-- > 0 && signatures > 0) {
+               LOCK_ZONE(zone);
+               nextnsec3chain = ISC_LIST_NEXT(nsec3chain, link);
+
+               ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+               if (nsec3chain->done || nsec3chain->db != zone->db) {
+                       ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain, link);
+                       ISC_LIST_APPEND(cleanup, nsec3chain, link);
+               }
+               ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+               UNLOCK_ZONE(zone);
+               if (ISC_LIST_TAIL(cleanup) == nsec3chain)
+                       goto next_addchain;
+
+               /*
+                * Possible future db.
+                */
+               if (nsec3chain->db != db) {
+                       goto next_addchain;
+               }
+
+               if (NSEC3REMOVE(nsec3chain->nsec3param.flags))
+                       goto next_addchain;
+
+               is_ksk = ISC_FALSE;
+               delegation = ISC_FALSE;
+               dns_dbiterator_current(nsec3chain->dbiterator, &node, name);
+
+               if (nsec3chain->delete_nsec) {
+                       delegation = ISC_FALSE;
+                       dns_dbiterator_pause(nsec3chain->dbiterator);
+                       CHECK(delete_nsec(db, version, node, name, &nsec_diff));
+                       goto next_addnode;
+               }
+               /*
+                * On the first pass we need to check if the current node
+                * has not been obscured.
+                */
+               delegation = ISC_FALSE;
+               unsecure = ISC_FALSE;
+               if (first) {
+                       dns_fixedname_t ffound;
+                       dns_name_t *found;
+                       dns_fixedname_init(&ffound);
+                       found = dns_fixedname_name(&ffound);
+                       result = dns_db_find(db, name, version,
+                                            dns_rdatatype_soa,
+                                            DNS_DBFIND_NOWILD, 0, NULL, found,
+                                            NULL, NULL);
+                       if ((result == DNS_R_DELEGATION ||
+                           result == DNS_R_DNAME) &&
+                           !dns_name_equal(name, found)) {
+                               /*
+                                * Remember the obscuring name so that
+                                * we skip all obscured names.
+                                */
+                               dns_name_copy(found, name, NULL);
+                               delegation = ISC_TRUE;
+                               goto next_addnode;
+                       }
+               }
+
+               /*
+                * Check to see if this is a bottom of zone node.
+                */
+               result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+               if (result == ISC_R_NOTFOUND)   /* Empty node? */
+                       goto next_addnode;
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+                       
+               seen_soa = seen_ns = seen_dname = seen_ds = seen_nsec =
+                       ISC_FALSE;
+               for (result = dns_rdatasetiter_first(iterator);
+                    result == ISC_R_SUCCESS;
+                    result = dns_rdatasetiter_next(iterator)) {
+                       dns_rdatasetiter_current(iterator, &rdataset);
+                       INSIST(rdataset.type != dns_rdatatype_nsec3);
+                       if (rdataset.type == dns_rdatatype_soa)
+                               seen_soa = ISC_TRUE;
+                       else if (rdataset.type == dns_rdatatype_ns)
+                               seen_ns = ISC_TRUE;
+                       else if (rdataset.type == dns_rdatatype_dname)
+                               seen_dname = ISC_TRUE;
+                       else if (rdataset.type == dns_rdatatype_ds)
+                               seen_ds = ISC_TRUE;
+                       else if (rdataset.type == dns_rdatatype_nsec)
+                               seen_nsec = ISC_TRUE;
+                       dns_rdataset_disassociate(&rdataset);
+               }
+               dns_rdatasetiter_destroy(&iterator);
+               /*
+                * Is there a NSEC chain than needs to be cleaned up?
+                */
+               if (seen_nsec)
+                       nsec3chain->seen_nsec = ISC_TRUE;
+               if (seen_ns && !seen_soa && !seen_ds)
+                       unsecure = ISC_TRUE;
+               if ((seen_ns && !seen_soa) || seen_dname)
+                       delegation = ISC_TRUE;
+
+               /*
+                * Process one node.
+                */
+               dns_dbiterator_pause(nsec3chain->dbiterator);
+               CHECK(dns_nsec3_addnsec3(db, version, name,
+                                        &nsec3chain->nsec3param,
+                                        zone->minimum, unsecure, &nsec3_diff));
+               /*
+                * Treat each call to dns_nsec3_addnsec3() as if it's cost is
+                * two signatures.  Additionally there will, in general, be
+                * two signature generated below. 
+                *
+                * If we are only changing the optout flag the cost is half
+                * that of the cost of generating a completely new chain.
+                */
+               signatures -= 4;
+                       
+               /*
+                * Go onto next node.
+                */
+ next_addnode:
+               first = ISC_FALSE;
+               dns_db_detachnode(db, &node);
+               do {
+                       result = dns_dbiterator_next(nsec3chain->dbiterator);
+                       
+                       if (result == ISC_R_NOMORE && nsec3chain->delete_nsec) {
+                               CHECK(fixup_nsec3param(db, version, nsec3chain,
+                                                      ISC_FALSE, &param_diff));
+                               LOCK_ZONE(zone);
+                               ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain,
+                                               link);
+                               UNLOCK_ZONE(zone);
+                               ISC_LIST_APPEND(cleanup, nsec3chain, link);
+                               goto next_addchain;
+                       }
+                       if (result == ISC_R_NOMORE) {
+                               dns_dbiterator_pause(nsec3chain->dbiterator);
+                               if (nsec3chain->seen_nsec) {
+                                       CHECK(fixup_nsec3param(db, version,
+                                                              nsec3chain,
+                                                              ISC_TRUE,
+                                                              &param_diff));
+                                       nsec3chain->delete_nsec = ISC_TRUE;
+                                       goto same_addchain;
+                               }
+                               CHECK(fixup_nsec3param(db, version, nsec3chain,
+                                                      ISC_FALSE, &param_diff));
+                               LOCK_ZONE(zone);
+                               ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain,
+                                               link);
+                               UNLOCK_ZONE(zone);
+                               ISC_LIST_APPEND(cleanup, nsec3chain, link);
+                               goto next_addchain;
+                       } else if (result != ISC_R_SUCCESS) {
+                               dns_zone_log(zone, ISC_LOG_ERROR,
+                                            "zone_nsec3chain:"
+                                            "dns_dbiterator_next -> %s\n",
+                                            dns_result_totext(result));
+                               goto failure;
+                       } else if (delegation) {
+                               dns_dbiterator_current(nsec3chain->dbiterator,
+                                                      &node, nextname);
+                               dns_db_detachnode(db, &node);
+                               if (!dns_name_issubdomain(nextname, name))
+                                       break;
+                       } else
+                               break;
+               } while (1);
+               continue;
+
+ same_addchain:
+               CHECK(dns_dbiterator_first(nsec3chain->dbiterator));
+               first = ISC_TRUE;
+               continue;
+
+ next_addchain:
+               dns_dbiterator_pause(nsec3chain->dbiterator);
+               nsec3chain = nextnsec3chain;
+               first = ISC_TRUE;
+               if (nsec3chain != NULL)
+                       nsec3chain->save_delete_nsec = nsec3chain->delete_nsec;
+       }
+
+       /*
+        * Process removals.
+        */
+       LOCK_ZONE(zone);
+       nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+       UNLOCK_ZONE(zone);
+       first = ISC_TRUE;
+       buildnsecchain = ISC_FALSE;
+       while (nsec3chain != NULL && nodes-- > 0 && signatures > 0) {
+               LOCK_ZONE(zone);
+               nextnsec3chain = ISC_LIST_NEXT(nsec3chain, link);
+               UNLOCK_ZONE(zone);
+
+               if (nsec3chain->db != db)
+                       goto next_removechain;
+
+               if (!NSEC3REMOVE(nsec3chain->nsec3param.flags))
+                       goto next_removechain;
+
+               /*
+                * Work out if we need to build a NSEC chain as a consequence
+                * of removing this NSEC3 chain.
+                */
+               if (first && !updatensec &&
+                   (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0)
+                       CHECK(need_nsec_chain(db, version,
+                                             &nsec3chain->nsec3param,
+                                             &buildnsecchain, &updatensec));
+
+               dns_dbiterator_current(nsec3chain->dbiterator, &node, name);
+               delegation = ISC_FALSE;
+
+               if (!buildnsecchain) {
+                       /*
+                        * Delete the NSECPARAM record that matches this chain.
+                        */
+                       if (first)
+                               CHECK(fixup_nsec3param(db, version, nsec3chain,
+                                                      ISC_TRUE, &param_diff));
+
+                       /*
+                        *  Delete the NSEC3 records.
+                        */
+                       CHECK(deletematchingnsec3(db, version, node, name,
+                                                 &nsec3chain->nsec3param,
+                                                 &nsec3_diff)); 
+                       goto next_removenode;
+               }
+
+               if (first) {
+                       dns_fixedname_t ffound;
+                       dns_name_t *found;
+                       dns_fixedname_init(&ffound);
+                       found = dns_fixedname_name(&ffound);
+                       result = dns_db_find(db, name, version,
+                                            dns_rdatatype_soa,
+                                            DNS_DBFIND_NOWILD, 0, NULL, found,
+                                            NULL, NULL);
+                       if ((result == DNS_R_DELEGATION ||
+                            result == DNS_R_DNAME) &&
+                           !dns_name_equal(name, found)) {
+                               /*
+                                * Remember the obscuring name so that
+                                * we skip all obscured names.
+                                */
+                               dns_name_copy(found, name, NULL);
+                               delegation = ISC_TRUE;
+                               goto next_removenode;
+                       }
+               }
+
+               /*
+                * Check to see if this is a bottom of zone node.
+                */
+               result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+               if (result == ISC_R_NOTFOUND)   /* Empty node? */
+                       goto next_removenode;
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+                       
+               seen_soa = seen_ns = seen_dname = seen_nsec3 = seen_nsec =
+                       seen_rr = ISC_FALSE;
+               for (result = dns_rdatasetiter_first(iterator);
+                    result == ISC_R_SUCCESS;
+                    result = dns_rdatasetiter_next(iterator)) {
+                       dns_rdatasetiter_current(iterator, &rdataset);
+                       if (rdataset.type == dns_rdatatype_soa)
+                               seen_soa = ISC_TRUE;
+                       else if (rdataset.type == dns_rdatatype_ns)
+                               seen_ns = ISC_TRUE;
+                       else if (rdataset.type == dns_rdatatype_dname)
+                               seen_dname = ISC_TRUE;
+                       else if (rdataset.type == dns_rdatatype_nsec)
+                               seen_nsec = ISC_TRUE;
+                       else if (rdataset.type == dns_rdatatype_nsec3)
+                               seen_nsec3 = ISC_TRUE;
+                       seen_rr = ISC_TRUE;
+                       dns_rdataset_disassociate(&rdataset);
+               }
+               dns_rdatasetiter_destroy(&iterator);
+
+               if (!seen_rr || seen_nsec3 || seen_nsec)
+                       goto next_removenode;
+               if ((seen_ns && !seen_soa) || seen_dname)
+                       delegation = ISC_TRUE;
+
+               CHECK(add_nsec(db, version, name, node, zone->minimum,
+                              delegation, &nsec_diff));
+
+ next_removenode:
+               first = ISC_FALSE;
+               dns_db_detachnode(db, &node);
+               do {
+                       result = dns_dbiterator_next(nsec3chain->dbiterator);
+                       if (result == ISC_R_NOMORE && buildnsecchain) {
+                               /*
+                                * The NSEC chain should now be built.
+                                * We can now remove the NSEC3 chain.
+                                */
+                               updatensec = ISC_TRUE;
+                               goto same_removechain;
+                       }
+                       if (result == ISC_R_NOMORE) {
+                               LOCK_ZONE(zone);
+                               ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain,
+                                               link);
+                               UNLOCK_ZONE(zone);
+                               ISC_LIST_APPEND(cleanup, nsec3chain, link);
+                               dns_dbiterator_pause(nsec3chain->dbiterator);
+                               CHECK(fixup_nsec3param(db, version, nsec3chain,
+                                                      ISC_FALSE, &param_diff));
+                               goto next_removechain;
+                       } else if (result != ISC_R_SUCCESS) {
+                               dns_zone_log(zone, ISC_LOG_ERROR,
+                                            "zone_nsec3chain:"
+                                            "dns_dbiterator_next -> %s\n",
+                                            dns_result_totext(result));
+                               goto failure;
+                       } else if (delegation) {
+                               dns_dbiterator_current(nsec3chain->dbiterator,
+                                                      &node, nextname);
+                               dns_db_detachnode(db, &node);
+                               if (!dns_name_issubdomain(nextname, name))
+                                       break;
+                       } else
+                               break;
+               } while (1);
+               continue;
+
+ same_removechain:
+               CHECK(dns_dbiterator_first(nsec3chain->dbiterator));
+               buildnsecchain = ISC_FALSE;
+               first = ISC_TRUE;
+               continue;
+
+ next_removechain:
+               dns_dbiterator_pause(nsec3chain->dbiterator);
+               nsec3chain = nextnsec3chain;
+               first = ISC_TRUE;
+       }
+
+       /*
+        * Add / update signatures for the NSEC3 records.
+        */
+       for (tuple = ISC_LIST_HEAD(nsec3_diff.tuples);
+            tuple != NULL;
+            tuple = ISC_LIST_HEAD(nsec3_diff.tuples)) {
+               /*
+                * We have changed the NSEC3 RRset above so we need to update
+                * the signatures.
+                */
+               result = del_sigs(zone, db, version, &tuple->name,
+                                 dns_rdatatype_nsec3, &sig_diff,
+                                 zone_keys, nkeys, now);
+               if (result != ISC_R_SUCCESS) {
+                       dns_zone_log(zone, ISC_LOG_ERROR,
+                                    "zone_nsec3chain:del_sigs -> %s\n",
+                                    dns_result_totext(result));
+                       goto failure;
+               }
+               result = add_sigs(db, version, &tuple->name,
+                                 dns_rdatatype_nsec3, &sig_diff, zone_keys,
+                                 nkeys, zone->mctx, inception, expire,
+                                 check_ksk);
+               if (result != ISC_R_SUCCESS) {
+                       dns_zone_log(zone, ISC_LOG_ERROR,
+                                    "zone_nsec3chain:add_sigs -> %s\n",
+                                    dns_result_totext(result));
+                       goto failure;
+               }
+               
+               do {
+                       dns_difftuple_t *next = ISC_LIST_NEXT(tuple, link);
+                       while (next != NULL &&
+                              !dns_name_equal(&tuple->name, &next->name))
+                               next = ISC_LIST_NEXT(next, link);
+                       ISC_LIST_UNLINK(nsec3_diff.tuples, tuple, link);
+                       dns_diff_appendminimal(&sig_diff, &tuple);
+                       INSIST(tuple == NULL);
+                       tuple = next;
+               } while (tuple != NULL);
+       }
+
+       for (tuple = ISC_LIST_HEAD(param_diff.tuples);
+            tuple != NULL;
+            tuple = ISC_LIST_HEAD(param_diff.tuples)) {
+               /*
+                * We have changed the NSEC3PARAM RRset above so we need to
+                * update the signatures.
+                */
+               result = del_sigs(zone, db, version, &tuple->name,
+                                 dns_rdatatype_nsec3param, &sig_diff,
+                                 zone_keys, nkeys, now);
+               if (result != ISC_R_SUCCESS) {
+                       dns_zone_log(zone, ISC_LOG_ERROR,
+                                    "zone_nsec3chain:del_sigs -> %s\n",
+                                    dns_result_totext(result));
+                       goto failure;
+               }
+               result = add_sigs(db, version, &tuple->name,
+                                 dns_rdatatype_nsec3param, &sig_diff,
+                                 zone_keys, nkeys, zone->mctx, inception,
+                                 expire, check_ksk);
+               if (result != ISC_R_SUCCESS) {
+                       dns_zone_log(zone, ISC_LOG_ERROR,
+                                    "zone_nsec3chain:add_sigs -> %s\n",
+                                    dns_result_totext(result));
+                       goto failure;
+               }
+               ISC_LIST_UNLINK(param_diff.tuples, tuple, link);
+               dns_diff_appendminimal(&sig_diff, &tuple);
+               INSIST(tuple == NULL);
+       }
+
+       if (updatensec)
+               CHECK(updatesecure(db, version, &zone->origin, zone->minimum,
+                                  NULL, &nsec_diff));
+
+       for (tuple = ISC_LIST_HEAD(nsec_diff.tuples);
+            tuple != NULL;
+            tuple = ISC_LIST_HEAD(nsec_diff.tuples)) {
+               result = del_sigs(zone, db, version, &tuple->name,
+                                 dns_rdatatype_nsec, &sig_diff,
+                                 zone_keys, nkeys, now);
+               if (result != ISC_R_SUCCESS) {
+                       dns_zone_log(zone, ISC_LOG_ERROR,
+                                    "zone_nsec3chain:del_sigs -> %s\n",
+                                    dns_result_totext(result));
+                       goto failure;
+               }
+               result = add_sigs(db, version, &tuple->name,
+                                 dns_rdatatype_nsec, &sig_diff,
+                                 zone_keys, nkeys, zone->mctx, inception,
+                                 expire, check_ksk);
+               if (result != ISC_R_SUCCESS) {
+                       dns_zone_log(zone, ISC_LOG_ERROR,
+                                    "zone_nsec3chain:add_sigs -> %s\n",
+                                    dns_result_totext(result));
+                       goto failure;
+               }
+               ISC_LIST_UNLINK(nsec_diff.tuples, tuple, link);
+               dns_diff_appendminimal(&sig_diff, &tuple);
+               INSIST(tuple == NULL);
+       }
+
+       /*
+        * If we made no effective changes to the zone then we can just
+        * cleanup otherwise we need to increment the serial.
         */
-       CHECK(dns_db_getoriginnode(db, &node));
-       dns_rdataset_init(&rdataset);
-       CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec,
-                                 dns_rdatatype_none, 0, &rdataset, NULL));
-       CHECK(dns_rdataset_first(&rdataset));
-       dns_rdataset_current(&rdataset, &rdata);
-       if (!dns_nsec_typepresent(&rdata, dns_rdatatype_opt))
+       if (ISC_LIST_HEAD(sig_diff.tuples) == NULL)
+               goto done;
+
+       result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa,
+                         &sig_diff, zone_keys, nkeys, now);
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+                            "del_sigs -> %s\n", dns_result_totext(result));
+               goto failure;
+       }
+
+       result = increment_soa_serial(db, version, &sig_diff, zone->mctx);
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+                            "increment_soa_serial -> %s\n",
+                            dns_result_totext(result));
+               goto failure;
+       }
+
+       result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
+                         &sig_diff, zone_keys, nkeys, zone->mctx, inception,
+                         soaexpire, check_ksk);
+       if (result != ISC_R_SUCCESS) {
+               dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+                            "add_sigs -> %s\n", dns_result_totext(result));
                goto failure;
+       }
+
+       journalfile = dns_zone_getjournal(zone);
+       if (journalfile != NULL) {
+               dns_journal_t *journal = NULL;
+               result = dns_journal_open(zone->mctx, journalfile,
+                                         ISC_TRUE, &journal);
+               if (result != ISC_R_SUCCESS) {
+                       dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+                                    "dns_journal_open -> %s\n",
+                                    dns_result_totext(result));
+                       goto failure;
+               }
+
+               result = dns_journal_write_transaction(journal, &sig_diff);
+               dns_journal_destroy(&journal);
+               if (result != ISC_R_SUCCESS) {
+                       dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+                                    "dns_journal_write_transaction -> %s\n",
+                                    dns_result_totext(result));
+                       goto failure;
+               }
+       }
+       
+       LOCK_ZONE(zone);
+       zone_needdump(zone, DNS_DUMP_DELAY);
+       UNLOCK_ZONE(zone);
 
+ done:
        /*
-        * Find the NEXT name for building the new record.
+        * Pause all iterators so that dns_db_closeversion() can succeed.
         */
-       CHECK(dns_rdata_tostruct(&rdata, &nsec, NULL));
+       LOCK_ZONE(zone);
+       for (nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+            nsec3chain != NULL;
+            nsec3chain = ISC_LIST_NEXT(nsec3chain, link))
+               dns_dbiterator_pause(nsec3chain->dbiterator);
+       UNLOCK_ZONE(zone);
 
        /*
-        * Delete the old NSEC record.
+        * Everything has succeeded. Commit the changes.
         */
-       CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_DEL, name, minimum,
-                           &rdata));
-       dns_rdata_reset(&rdata);
+       dns_db_closeversion(db, &version, ISC_TRUE);
 
        /*
-        * Add the new NSEC record.
+        * Everything succeeded so we can clean these up now.
         */
-       CHECK(dns_nsec_buildrdata(db, version, node, &nsec.next, nsecbuffer,
-                                 &rdata));
-       CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, minimum,
-                           &rdata));
-       dns_rdata_reset(&rdata);
+       nsec3chain = ISC_LIST_HEAD(cleanup);
+       while (nsec3chain != NULL) {
+               ISC_LIST_UNLINK(cleanup, nsec3chain, link);
+               dns_db_detach(&nsec3chain->db);
+               dns_dbiterator_destroy(&nsec3chain->dbiterator);
+               isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+               nsec3chain = ISC_LIST_HEAD(cleanup);
+       }
 
-       *secureupdated = ISC_TRUE;
+       set_resigntime(zone);
 
  failure:
-       if (node != NULL)
-               dns_db_detachnode(db, &node);
-       if (dns_rdataset_isassociated(&rdataset))
-               dns_rdataset_disassociate(&rdataset);
-       return (result);
+       /*
+        * On error roll back the current nsec3chain.
+        */
+       if (result != ISC_R_SUCCESS && nsec3chain != NULL) {
+               if (nsec3chain->done) {
+                       dns_db_detach(&nsec3chain->db);
+                       dns_dbiterator_destroy(&nsec3chain->dbiterator);
+                       isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+               } else {
+                       result = dns_dbiterator_first(nsec3chain->dbiterator);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+                       dns_dbiterator_pause(nsec3chain->dbiterator);
+                       nsec3chain->delete_nsec = nsec3chain->save_delete_nsec;
+               }
+       }
+               
+       /*
+        * Rollback the cleanup list.
+        */
+       nsec3chain = ISC_LIST_TAIL(cleanup);
+       while (nsec3chain != NULL) {
+               ISC_LIST_UNLINK(cleanup, nsec3chain, link);
+               if (nsec3chain->done) {
+                       dns_db_detach(&nsec3chain->db);
+                       dns_dbiterator_destroy(&nsec3chain->dbiterator);
+                       isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+               } else {
+                       LOCK_ZONE(zone);
+                       ISC_LIST_PREPEND(zone->nsec3chain, nsec3chain, link);
+                       UNLOCK_ZONE(zone);
+                       result = dns_dbiterator_first(nsec3chain->dbiterator);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+                       dns_dbiterator_pause(nsec3chain->dbiterator);
+                       nsec3chain->delete_nsec = nsec3chain->save_delete_nsec;
+               }
+               nsec3chain = ISC_LIST_TAIL(cleanup);
+       }
+
+       LOCK_ZONE(zone);
+       for (nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+            nsec3chain != NULL;
+            nsec3chain = ISC_LIST_NEXT(nsec3chain, link))
+               dns_dbiterator_pause(nsec3chain->dbiterator);
+       UNLOCK_ZONE(zone);
+
+       dns_diff_clear(&param_diff);
+       dns_diff_clear(&nsec3_diff);
+       dns_diff_clear(&nsec_diff);
+       dns_diff_clear(&sig_diff);
+
+       if (iterator != NULL)
+               dns_rdatasetiter_destroy(&iterator);
+
+       for (i = 0; i < nkeys; i++)
+               dst_key_free(&zone_keys[i]);
+
+       if (version != NULL) {
+               dns_db_closeversion(db, &version, ISC_FALSE);
+               dns_db_detach(&db);
+       } else if (db != NULL)
+               dns_db_detach(&db);
+
+       LOCK_ZONE(zone);
+       if (ISC_LIST_HEAD(zone->nsec3chain) != NULL) {
+               isc_interval_t i;
+               if (zone->update_disabled || result != ISC_R_SUCCESS)
+                       isc_interval_set(&i, 60, 0);            /* 1 minute */
+               else
+                       isc_interval_set(&i, 0, 10000000);      /* 10 ms */
+               isc_time_nowplusinterval(&zone->nsec3chaintime, &i);
+       } else
+               isc_time_settoepoch(&zone->nsec3chaintime);
+       UNLOCK_ZONE(zone);
 }
 
 static isc_result_t
-updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version,
-                 dns_name_t *name, dns_rdatatype_t privatetype,
-                 dns_diff_t *diff)
+del_sig(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+       dns_dbnode_t *node, unsigned int nkeys, dns_secalg_t algorithm,
+       isc_uint16_t keyid, dns_diff_t *diff)
 {
-       isc_result_t result;
-       dns_dbnode_t *node = NULL;
+       dns_rdata_rrsig_t rrsig;
        dns_rdataset_t rdataset;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
-       unsigned char data[4];
-
-       dns_rdataset_init(&rdataset);
-       result = dns_db_getoriginnode(signing->db, &node);
-       if (result != ISC_R_SUCCESS)
-               goto failure;
+       dns_rdatasetiter_t *iterator = NULL;
+       isc_result_t result;
 
-       result = dns_db_findrdataset(signing->db, node, version, privatetype,
-                                    dns_rdatatype_none, 0, &rdataset, NULL);
-       if (result == ISC_R_NOTFOUND) {
-               result = ISC_R_SUCCESS;
-               goto failure;
+       result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+       if (result != ISC_R_SUCCESS) {
+               if (result == ISC_R_NOTFOUND)
+                       result = ISC_R_SUCCESS;
+               return (result);
        }
-       if (result != ISC_R_SUCCESS)
-               goto failure;
-       for (result = dns_rdataset_first(&rdataset);
+
+       dns_rdataset_init(&rdataset);
+       for (result = dns_rdatasetiter_first(iterator);
             result == ISC_R_SUCCESS;
-            result = dns_rdataset_next(&rdataset)) {
-               dns_rdataset_current(&rdataset, &rdata);
-               if (rdata.length != 4 ||
-                   rdata.data[0] != signing->algorithm ||
-                   rdata.data[1] != ((signing->keyid >> 8) & 0xff) ||
-                   rdata.data[2] != (signing->keyid & 0xff) ||
-                   rdata.data[3] != 0) {
-                       dns_rdata_reset(&rdata);
+            result = dns_rdatasetiter_next(iterator)) {
+               dns_rdatasetiter_current(iterator, &rdataset);
+               if (nkeys == 0 && rdataset.type == dns_rdatatype_nsec) {
+                       for (result = dns_rdataset_first(&rdataset);
+                            result == ISC_R_SUCCESS;
+                            result = dns_rdataset_next(&rdataset)) {
+                               dns_rdata_t rdata = DNS_RDATA_INIT;
+                               dns_rdataset_current(&rdataset, &rdata);
+                               CHECK(update_one_rr(db, version, diff,
+                                                   DNS_DIFFOP_DEL, name,
+                                                   rdataset.ttl, &rdata));
+                       }
+                       if (result != ISC_R_NOMORE)
+                               goto failure;
+                       dns_rdataset_disassociate(&rdataset);
                        continue;
                }
-               CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_DEL,
-                                   name, rdataset.ttl, &rdata));
-               memcpy(data, rdata.data, 4);
-               data[3] = 1;                    /* mark as done */
-               rdata.data = data;
-               CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_ADD,
-                                   name, rdataset.ttl, &rdata));
-               break;
+               if (rdataset.type != dns_rdatatype_rrsig) {
+                       dns_rdataset_disassociate(&rdataset);
+                       continue;
+               }
+               for (result = dns_rdataset_first(&rdataset);
+                    result == ISC_R_SUCCESS;
+                    result = dns_rdataset_next(&rdataset)) {
+                       dns_rdata_t rdata = DNS_RDATA_INIT;
+                       dns_rdataset_current(&rdataset, &rdata);
+                       CHECK(dns_rdata_tostruct(&rdata, &rrsig, NULL));
+                       if (rrsig.algorithm != algorithm ||
+                           rrsig.keyid != keyid)
+                               continue;
+                       CHECK(update_one_rr(db, version, diff,
+                                           DNS_DIFFOP_DEL, name,
+                                           rdataset.ttl, &rdata));
+               }
+               dns_rdataset_disassociate(&rdataset);
+               if (result != ISC_R_NOMORE)
+                       break;
        }
        if (result == ISC_R_NOMORE)
                result = ISC_R_SUCCESS;
  failure:
        if (dns_rdataset_isassociated(&rdataset))
                dns_rdataset_disassociate(&rdataset);
-       if (node != NULL)
-               dns_db_detachnode(signing->db, &node);
+       dns_rdatasetiter_destroy(&iterator);
        return (result);
 }
 
 /*
  * Incrementally sign the zone using the keys requested.
- * Builds the NSEC chain is required.
+ * Builds the NSEC chain if required.
  */
 static void
 zone_sign(dns_zone_t *zone) {
@@ -4094,6 +5454,7 @@ zone_sign(dns_zone_t *zone) {
        isc_boolean_t delegation;
        isc_boolean_t finishedakey = ISC_FALSE;
        isc_boolean_t secureupdated = ISC_FALSE;
+       isc_boolean_t build_nsec3 = ISC_FALSE, build_nsec = ISC_FALSE;
        isc_boolean_t first;
        isc_result_t result;
        isc_stdtime_t now, inception, soaexpire, expire, stop;
@@ -4143,6 +5504,7 @@ zone_sign(dns_zone_t *zone) {
        isc_stdtime_get(&now);
        inception = now - 3600; /* Allow for clock skew. */
        soaexpire = now + dns_zone_getsigvalidityinterval(zone);
+
        /*
         * Spread out signatures over time if they happen to be
         * clumped.  We don't do this for each add_sigs() call as
@@ -4158,27 +5520,59 @@ zone_sign(dns_zone_t *zone) {
 
        /*
         * We keep pulling nodes off each interator in turn until
-        * we have no more noded to pull off or we reach the limits
+        * we have no more nodes to pull off or we reach the limits
         * for this quantum.
         */
        nodes = zone->nodes;
        signatures = zone->signatures;
        signing = ISC_LIST_HEAD(zone->signing);
        first = ISC_TRUE;
+       /*
+        * See if we have a NSEC chain.
+        */
+       result = dns_db_getoriginnode(db, &node);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec,
+                                    dns_rdatatype_none, 0, &rdataset, NULL);
+       dns_db_detachnode(db, &node);
+       if (result == ISC_R_SUCCESS) {
+               build_nsec = ISC_TRUE;
+               dns_rdataset_disassociate(&rdataset);
+       } else if (result != ISC_R_NOTFOUND) {
+               goto failure;
+       } else {
+               /*
+                * No NSEC chain present.
+                * See if we need to build a NSEC3 chain?
+                */
+               result = dns_nsec3_active(db, version, ISC_TRUE, &build_nsec3);
+               if (result == ISC_R_SUCCESS) {
+                       if (build_nsec3) 
+                               build_nsec3 = ISC_FALSE;
+                       else  {
+                               result = dns_nsec3_active(db, version,
+                                                         ISC_FALSE,
+                                                         &build_nsec3);
+                               if (build_nsec3)
+                                       secureupdated = ISC_TRUE;
+                               else
+                                       build_nsec = ISC_TRUE;
+                       }
+               }
+       }
+
        while (signing != NULL && nodes-- > 0 && signatures > 0) {
                nextsigning = ISC_LIST_NEXT(signing, link);
 
                ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
-               if (signing->db != zone->db) {
+               if (signing->done || signing->db != zone->db) {
                        /*
                         * The zone has been reloaded.  We will have
                         * created new signings as part of the reload
                         * process so we can destroy this one.
                         */
                        ISC_LIST_UNLINK(zone->signing, signing, link);
-                       dns_db_detach(&signing->db);
-                       dns_dbiterator_destroy(&signing->dbiterator);
-                       isc_mem_put(zone->mctx, signing, sizeof *signing);
+                       ISC_LIST_APPEND(cleanup, signing, link);
                        ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
                        goto next_signing;
                }
@@ -4188,12 +5582,21 @@ zone_sign(dns_zone_t *zone) {
                        goto next_signing;
 
                is_ksk = ISC_FALSE;
+               delegation = ISC_FALSE;
+
                dns_dbiterator_current(signing->dbiterator, &node, name);
+
+               if (signing->delete) {
+                       dns_dbiterator_pause(signing->dbiterator);
+                       CHECK(del_sig(db, version, name, node, nkeys,
+                                     signing->algorithm, signing->keyid,
+                                     &sig_diff));
+                       goto next_node;
+               }
                /*
                 * On the first pass we need to check if the current node
                 * has not been obscured.
                 */
-               delegation = ISC_FALSE;
                if (first) {
                        dns_fixedname_t ffound;
                        dns_name_t *found;
@@ -4219,6 +5622,7 @@ zone_sign(dns_zone_t *zone) {
                /*
                 * Process one node.
                 */
+               dns_dbiterator_pause(signing->dbiterator);
                for (i = 0; i < nkeys; i++) {
                        /*
                         * Find the key we want to sign with.
@@ -4233,24 +5637,28 @@ zone_sign(dns_zone_t *zone) {
                        if (check_ksk &&
                            (dst_key_flags(zone_keys[i]) & DNS_KEYFLAG_KSK) != 0)
                                is_ksk = ISC_TRUE;
-                       CHECK(sign_a_node(db, name, node, version,
-                                         zone_keys[i], inception, expire,
-                                         zone->minimum, is_ksk, &delegation,
-                                         &sig_diff, &signatures, zone->mctx));
+                       CHECK(sign_a_node(db, name, node, version, build_nsec3,
+                                         build_nsec, zone_keys[i], inception,
+                                         expire, zone->minimum, is_ksk,
+                                         &delegation, &sig_diff, &signatures,
+                                         zone->mctx));
                        break;
                }
                /*
                 * Go onto next node.
                 */
  next_node:
+               first = ISC_FALSE;
                dns_db_detachnode(db, &node);
                do {
                        result = dns_dbiterator_next(signing->dbiterator);
                        if (result == ISC_R_NOMORE) {
                                ISC_LIST_UNLINK(zone->signing, signing, link);
                                ISC_LIST_APPEND(cleanup, signing, link);
+                               dns_dbiterator_pause(signing->dbiterator);
                                finishedakey = ISC_TRUE;
-                               if (!is_ksk && !secureupdated) {
+                               if (!is_ksk && !secureupdated && nkeys != 0 &&
+                                   build_nsec) {
                                        /*
                                         * We have finished regenerating the
                                         * zone with a zone signing key.
@@ -4282,7 +5690,7 @@ zone_sign(dns_zone_t *zone) {
                                                     dns_result_totext(result));
                                        goto failure;
                                }
-                               break;
+                               goto next_signing;
                        } else if (result != ISC_R_SUCCESS) {
                                dns_zone_log(zone, ISC_LOG_ERROR,
                                        "zone_sign:dns_dbiterator_next -> %s\n",
@@ -4296,13 +5704,13 @@ zone_sign(dns_zone_t *zone) {
                                        break;
                        } else
                                break;
-                       } while (1);
+               } while (1);
+               continue;
+
  next_signing:
+               dns_dbiterator_pause(signing->dbiterator);
                signing = nextsigning;
-               if (signing == NULL) {
-                       first = ISC_FALSE;
-                       signing = ISC_LIST_HEAD(zone->signing);
-               }
+               first = ISC_TRUE;
        }
 
        if (secureupdated) {
@@ -4408,6 +5816,20 @@ zone_sign(dns_zone_t *zone) {
                }
        }
 
+
+       /*
+        * Pause all iterators so that dns_db_closeversion() can succeed.
+        */
+       for (signing = ISC_LIST_HEAD(zone->signing);
+            signing != NULL;
+            signing = ISC_LIST_NEXT(signing, link))
+               dns_dbiterator_pause(signing->dbiterator);
+
+       for (signing = ISC_LIST_HEAD(cleanup);
+            signing != NULL;
+            signing = ISC_LIST_NEXT(signing, link))
+               dns_dbiterator_pause(signing->dbiterator);
+
        /*
         * Everything has succeeded. Commit the changes.
         */
@@ -4427,6 +5849,10 @@ zone_sign(dns_zone_t *zone) {
 
        set_resigntime(zone);
 
+       LOCK_ZONE(zone);
+       zone_needdump(zone, DNS_DUMP_DELAY);
+       UNLOCK_ZONE(zone);
+
  failure:
        /*
         * Rollback the cleanup list.
@@ -4436,23 +5862,29 @@ zone_sign(dns_zone_t *zone) {
                ISC_LIST_UNLINK(cleanup, signing, link);
                ISC_LIST_APPEND(zone->signing, signing, link);
                dns_dbiterator_first(signing->dbiterator);
+               dns_dbiterator_pause(signing->dbiterator);
                signing = ISC_LIST_HEAD(cleanup);
        }
+
        for (signing = ISC_LIST_HEAD(zone->signing);
             signing != NULL;
             signing = ISC_LIST_NEXT(signing, link))
                dns_dbiterator_pause(signing->dbiterator);
+
        dns_diff_clear(&sig_diff);
+
        for (i = 0; i < nkeys; i++)
                dst_key_free(&zone_keys[i]);
+
        if (version != NULL) {
                dns_db_closeversion(db, &version, ISC_FALSE);
                dns_db_detach(&db);
        } else if (db != NULL)
                dns_db_detach(&db);
+
        if (ISC_LIST_HEAD(zone->signing) != NULL) {
                isc_interval_t i;
-               if (zone->update_disabled)
+               if (zone->update_disabled || result != ISC_R_SUCCESS)
                        isc_interval_set(&i, 60, 0);            /* 1 minute */
                else
                        isc_interval_set(&i, 0, 10000000);      /* 10 ms */
@@ -4560,6 +5992,9 @@ zone_maintenance(dns_zone_t *zone) {
                else if (!isc_time_isepoch(&zone->resigntime) &&
                    isc_time_compare(&now, &zone->resigntime) >= 0)
                        zone_resigninc(zone);
+               else if (!isc_time_isepoch(&zone->nsec3chaintime) &&
+                       isc_time_compare(&now, &zone->nsec3chaintime) >= 0)
+                       zone_nsec3chain(zone);
                /*
                 * Do we need to issue a key expiry warning.
                 */
@@ -6994,6 +8429,11 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) {
                            isc_time_compare(&zone->signingtime, &next) < 0)
                                next = zone->signingtime;
                }
+               if (!isc_time_isepoch(&zone->nsec3chaintime)) {
+                       if (isc_time_isepoch(&next) ||
+                           isc_time_compare(&zone->nsec3chaintime, &next) < 0)
+                               next = zone->nsec3chaintime;
+               }
                break;
 
        case dns_zone_slave:
@@ -8011,6 +9451,10 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) {
                return (result);
        }
 
+       result = check_nsec3param(zone, db);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
        ver = NULL;
        dns_db_currentversion(db, &ver);
 
@@ -9908,7 +11352,7 @@ dns_zone_getnotifydelay(dns_zone_t *zone) {
 
 isc_result_t
 dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
-                    isc_uint16_t keyid)
+                    isc_uint16_t keyid, isc_boolean_t delete)
 {
        isc_result_t result;
        REQUIRE(DNS_ZONE_VALID(zone));
@@ -9917,12 +11361,42 @@ dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
                     "dns_zone_signwithkey(algorithm=%u, keyid=%u)",
                     algorithm, keyid);
        LOCK_ZONE(zone);
-       result = zone_signwithkey(zone, algorithm, keyid);
+       result = zone_signwithkey(zone, algorithm, keyid, delete);
        UNLOCK_ZONE(zone);
 
        return (result);
 }
 
+static const char *hex = "0123456789ABCDEF";
+
+isc_result_t
+dns_zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) {
+        isc_result_t result;
+       char salt[255*2+1];
+       unsigned int i, j;
+       
+        REQUIRE(DNS_ZONE_VALID(zone));
+                         
+       if (nsec3param->salt_length != 0) {
+               INSIST((nsec3param->salt_length * 2U) < sizeof(salt));
+               for (i = 0, j = 0; i < nsec3param->salt_length; i++) {
+                       salt[j++] = hex[(nsec3param->salt[i] >> 4) & 0xf];
+                       salt[j++] = hex[nsec3param->salt[i] & 0xf];
+               }
+               salt[j] = '\0';
+       } else 
+               strcpy(salt, "-");
+        dns_zone_log(zone, ISC_LOG_NOTICE,
+                     "dns_zone_addnsec3chain(hash=%u, iterations=%u, salt=%s)",
+                     nsec3param->hash, nsec3param->iterations,
+                    salt);
+        LOCK_ZONE(zone);
+       result = zone_addnsec3chain(zone, nsec3param);
+        UNLOCK_ZONE(zone);
+                                          
+        return (result);
+}
+
 void
 dns_zone_setnodes(dns_zone_t *zone, isc_uint32_t nodes) {
        REQUIRE(DNS_ZONE_VALID(zone));
@@ -9960,9 +11434,12 @@ dns_zone_getprivatetype(dns_zone_t *zone) {
 }
 
 static isc_result_t
-zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, isc_uint16_t keyid) {
+zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, isc_uint16_t keyid,
+                isc_boolean_t delete)
+{
        dns_signing_t *signing;
-       isc_result_t result;
+       dns_signing_t *current;
+       isc_result_t result = ISC_R_SUCCESS;
        isc_time_t now;
 
        signing = isc_mem_get(zone->mctx, sizeof *signing);
@@ -9974,12 +11451,27 @@ zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, isc_uint16_t keyid) {
        signing->dbiterator = NULL;
        signing->algorithm = algorithm;
        signing->keyid = keyid;
+       signing->delete = delete;
+       signing->done = ISC_FALSE;
 
        TIME_NOW(&now);
 
+       for (current = ISC_LIST_HEAD(zone->signing);
+            current != NULL;
+            current = ISC_LIST_NEXT(current, link)) {
+               if (current->db == zone->db &&
+                   current->algorithm == signing->algorithm &&
+                   current->keyid == signing->keyid) {
+                       if (current->delete != signing->delete)
+                               current->done = ISC_TRUE;
+                       else
+                               goto cleanup;
+               }
+       }
+
        if (zone->db != NULL) {
                dns_db_attach(zone->db, &signing->db);
-               result = dns_db_createiterator(signing->db, ISC_FALSE,
+               result = dns_db_createiterator(signing->db, 0,
                                               &signing->dbiterator);
 
                if (result == ISC_R_SUCCESS)
@@ -9997,6 +11489,7 @@ zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, isc_uint16_t keyid) {
        } else
                result = ISC_R_NOTFOUND;
 
+ cleanup:
        if (signing != NULL) {
                dns_db_detach(&signing->db);
                if (signing->dbiterator != NULL)
index f695715fa64ac1a484389ca9dc98dc30ea34a918..c3ece8435331f46721e63cd1da58bcb2f589c1aa 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.95 2008/06/23 23:47:11 tbox Exp $
+# $Id: Makefile.in,v 1.96 2008/09/24 02:46:23 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -51,10 +51,10 @@ WIN32OBJS =         win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \
 
 # Alphabetically
 OBJS =         @ISC_EXTRA_OBJS@ \
-               assertions.@O@ base64.@O@ bitstring.@O@ buffer.@O@ \
+               assertions.@O@ base32.@O@ base64.@O@ bitstring.@O@ buffer.@O@ \
                bufferlist.@O@ commandline.@O@ error.@O@ event.@O@ \
                hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \
-               httpd.@O@ \
+               httpd.@O@ iterated_hash.@O@ \
                lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \
                md5.@O@ mem.@O@ mutexblock.@O@ \
                netaddr.@O@ netscope.@O@ ondestroy.@O@ \
@@ -66,10 +66,10 @@ OBJS =              @ISC_EXTRA_OBJS@ \
 
 # Alphabetically
 SRCS =         @ISC_EXTRA_SRCS@ \
-               assertions.c base64.c bitstring.c buffer.c \
+               assertions.c base32.c base64.c bitstring.c buffer.c \
                bufferlist.c commandline.c error.c event.c \
                heap.c hex.c hmacmd5.c hmacsha.c \
-               httpd.c \
+               httpd.c iterated_hash.c \
                lex.c lfsr.c lib.c log.c \
                md5.c mem.c mutexblock.c \
                netaddr.c netscope.c ondestroy.c \
index ccf17b3c2066e43d7d92faff2dd10b42f851f521..25fde0c2240e824ee5c93eb88685ae11e8763f59 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: buffer.c,v 1.47 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: buffer.c,v 1.48 2008/09/24 02:46:23 marka Exp $ */
 
 /*! \file */
 
@@ -316,6 +316,14 @@ isc__buffer_putuint16(isc_buffer_t *b, isc_uint16_t val) {
        ISC__BUFFER_PUTUINT16(b, val);
 }
 
+void
+isc__buffer_putuint24(isc_buffer_t *b, isc_uint32_t val) {
+       REQUIRE(ISC_BUFFER_VALID(b));
+       REQUIRE(b->used + 3 <= b->length);
+
+       ISC__BUFFER_PUTUINT24(b, val);
+}
+
 isc_uint32_t
 isc_buffer_getuint32(isc_buffer_t *b) {
        unsigned char *cp;
@@ -429,7 +437,7 @@ isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r) {
         */
        base = isc_buffer_used(b);
        available = isc_buffer_availablelength(b);
-        if (r->length > available)
+       if (r->length > available)
                return (ISC_R_NOSPACE);
        memcpy(base, r->base, r->length);
        b->used += r->length;
index 8735d35e271269b02008fa3b9d9781723caeb2d7..af7ca184e889027b30d89b9d043b60b058a3073b 100644 (file)
@@ -48,7 +48,7 @@
  * SUCH DAMAGE.
  */
 
-/* $Id: commandline.c,v 1.20 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: commandline.c,v 1.21 2008/09/24 02:46:23 marka Exp $ */
 
 /*! \file
  * This file was adapted from the NetBSD project's source tree, RCS ID:
@@ -107,7 +107,10 @@ isc_commandline_parse(int argc, char * const *argv, const char *options) {
         * the previous argv was finished.
         */
        if (isc_commandline_reset || *place == '\0') {
-               isc_commandline_reset = ISC_FALSE;
+               if (isc_commandline_reset) {
+                       isc_commandline_index = 1;
+                       isc_commandline_reset = ISC_FALSE;
+               }
 
                if (isc_commandline_progname == NULL)
                        isc_commandline_progname = argv[0];
index 4983a77ea9cbcded947cfe1eff293331f27b3f80..94c2b56cbdf37a8d6b10f463d1bf1d645b762c31 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: hex.c,v 1.18 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: hex.c,v 1.19 2008/09/24 02:46:23 marka Exp $ */
 
 /*! \file */
 
@@ -156,7 +156,7 @@ isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
 }
 
 isc_result_t
-isc_hex_decodestring(char *cstr, isc_buffer_t *target) {
+isc_hex_decodestring(const char *cstr, isc_buffer_t *target) {
        hex_decode_ctx_t ctx;
 
        hex_decode_init(&ctx, -1, target);
@@ -168,7 +168,7 @@ isc_hex_decodestring(char *cstr, isc_buffer_t *target) {
                        continue;
                RETERR(hex_decode_char(&ctx, c));
        }
-       RETERR(hex_decode_finish(&ctx));        
+       RETERR(hex_decode_finish(&ctx));
        return (ISC_R_SUCCESS);
 }
 
diff --git a/lib/isc/include/isc/base32.h b/lib/isc/include/isc/base32.h
new file mode 100644 (file)
index 0000000..2d9779d
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and 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: base32.h,v 1.2 2008/09/24 02:46:23 marka Exp $ */
+
+#ifndef ISC_BASE32_H
+#define ISC_BASE32_H 1
+
+/*! \file */
+
+/*
+ * Routines for manipulating base 32 and base 32 hex encoded data.
+ * Based on RFC 4648.
+ *
+ * Base 32 hex preserves the sort order of data when it is encoded /
+ * decoded.
+ */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+isc_base32_totext(isc_region_t *source, int wordlength,
+                 const char *wordbreak, isc_buffer_t *target);
+isc_result_t
+isc_base32hex_totext(isc_region_t *source, int wordlength,
+                    const char *wordbreak, isc_buffer_t *target);
+/*!<
+ * \brief Convert data into base32 encoded text.
+ *
+ * Notes:
+ *\li  The base32 encoded text in 'target' will be divided into
+ *     words of at most 'wordlength' characters, separated by
+ *     the 'wordbreak' string.  No parentheses will surround
+ *     the text.
+ *
+ * Requires:
+ *\li  'source' is a region containing binary data
+ *\li  'target' is a text buffer containing available space
+ *\li  'wordbreak' points to a null-terminated string of
+ *             zero or more whitespace characters
+ *
+ * Ensures:
+ *\li  target will contain the base32 encoded version of the data
+ *     in source.  The 'used' pointer in target will be advanced as
+ *     necessary.
+ */
+
+isc_result_t
+isc_base32_decodestring(const char *cstr, isc_buffer_t *target);
+isc_result_t
+isc_base32hex_decodestring(const char *cstr, isc_buffer_t *target);
+/*!<
+ * \brief Decode a null-terminated base32 string.
+ *
+ * Requires:
+ *\li  'cstr' is non-null.
+ *\li  'target' is a valid buffer.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS  -- the entire decoded representation of 'cstring'
+ *                        fit in 'target'.
+ *\li  #ISC_R_BADBASE32 -- 'cstr' is not a valid base32 encoding.
+ *
+ *     Other error returns are any possible error code from:
+ *\li          isc_lex_create(),
+ *\li          isc_lex_openbuffer(),
+ *\li          isc_base32_tobuffer().
+ */
+
+isc_result_t
+isc_base32_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length);
+isc_result_t
+isc_base32hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length);
+/*!<
+ * \brief Convert base32 encoded text from a lexer context into data.
+ *
+ * Requires:
+ *\li  'lex' is a valid lexer context
+ *\li  'target' is a buffer containing binary data
+ *\li  'length' is an integer
+ *
+ * Ensures:
+ *\li  target will contain the data represented by the base32 encoded
+ *     string parsed by the lexer.  No more than length bytes will be read,
+ *     if length is positive.  The 'used' pointer in target will be
+ *     advanced as necessary.
+ */
+
+isc_result_t
+isc_base32_decoderegion(isc_region_t *source, isc_buffer_t *target);
+isc_result_t
+isc_base32hex_decoderegion(isc_region_t *source, isc_buffer_t *target);
+/*!<
+ * \brief Decode a packed (no white space permitted) base32 region.
+ *
+ * Requires:
+ *\li   'source' is a valid region.
+ *\li   'target' is a valid buffer.
+ *
+ * Returns:
+ *\li   #ISC_R_SUCCESS  -- the entire decoded representation of 'cstring'
+ *                         fit in 'target'.
+ *\li   #ISC_R_BADBASE32 -- 'source' is not a valid base32 encoding.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISC_BASE32_H */
index 124239a2d43f6265cf436f9ce489ab753143be28..c531fa0277295bedfc2501e80018249ff804ad70 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: buffer.h,v 1.51 2007/06/19 23:47:18 tbox Exp $ */
+/* $Id: buffer.h,v 1.52 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef ISC_BUFFER_H
 #define ISC_BUFFER_H 1
@@ -168,13 +168,13 @@ ISC_LANG_BEGINDECLS
 struct isc_buffer {
        unsigned int            magic;
        void                   *base;
-        /*@{*/
+       /*@{*/
        /*! The following integers are byte offsets from 'base'. */
        unsigned int            length;
        unsigned int            used;
        unsigned int            current;
        unsigned int            active;
-        /*@}*/
+       /*@}*/
        /*! linkable */
        ISC_LINK(isc_buffer_t)  link;
        /*! private internal elements */
@@ -595,6 +595,21 @@ isc__buffer_putuint48(isc_buffer_t *b, isc_uint64_t val);
  *\li  The used pointer in 'b' is advanced by 6.
  */
 
+void
+isc__buffer_putuint24(isc_buffer_t *b, isc_uint32_t val);
+/*!<
+ * Store an unsigned 24-bit integer in host byte order from 'val'
+ * into 'b' in network byte order.
+ *
+ * Requires:
+ *\li  'b' is a valid buffer.
+ *
+ *     The length of the unused region of 'b' is at least 3.
+ *
+ * Ensures:
+ *\li  The used pointer in 'b' is advanced by 3.
+ */
+
 void
 isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base,
                   unsigned int length);
@@ -810,6 +825,17 @@ ISC_LANG_ENDDECLS
                _cp[1] = (unsigned char)(_val2 & 0x00ffU); \
        } while (0)
 
+#define ISC__BUFFER_PUTUINT24(_b, _val) \
+       do { \
+               unsigned char *_cp; \
+               isc_uint32_t _val2 = (_val); \
+               _cp = isc_buffer_used(_b); \
+               (_b)->used += 3; \
+               _cp[0] = (unsigned char)((_val2 & 0xff0000U) >> 16); \
+               _cp[1] = (unsigned char)((_val2 & 0xff00U) >> 8); \
+               _cp[2] = (unsigned char)(_val2 & 0x00ffU); \
+       } while (0)
+
 #define ISC__BUFFER_PUTUINT32(_b, _val) \
        do { \
                unsigned char *_cp; \
@@ -843,6 +869,7 @@ ISC_LANG_ENDDECLS
 #define isc_buffer_putstr              ISC__BUFFER_PUTSTR
 #define isc_buffer_putuint8            ISC__BUFFER_PUTUINT8
 #define isc_buffer_putuint16           ISC__BUFFER_PUTUINT16
+#define isc_buffer_putuint24           ISC__BUFFER_PUTUINT24
 #define isc_buffer_putuint32           ISC__BUFFER_PUTUINT32
 #else
 #define isc_buffer_init                        isc__buffer_init
@@ -865,6 +892,7 @@ ISC_LANG_ENDDECLS
 #define isc_buffer_putstr              isc__buffer_putstr
 #define isc_buffer_putuint8            isc__buffer_putuint8
 #define isc_buffer_putuint16           isc__buffer_putuint16
+#define isc_buffer_putuint24           isc__buffer_putuint24
 #define isc_buffer_putuint32           isc__buffer_putuint32
 #endif
 
index c865f072b57cacca20532252f57566b66424d5fd..da8bd07901348758dda2fd524b6c81faa7069b50 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: hex.h,v 1.11 2007/06/19 23:47:18 tbox Exp $ */
+/* $Id: hex.h,v 1.12 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef ISC_HEX_H
 #define ISC_HEX_H 1
@@ -56,7 +56,7 @@ isc_hex_totext(isc_region_t *source, int wordlength,
  */
 
 isc_result_t
-isc_hex_decodestring(char *cstr, isc_buffer_t *target);
+isc_hex_decodestring(const char *cstr, isc_buffer_t *target);
 /*!<
  * \brief Decode a null-terminated hex string.
  *
diff --git a/lib/isc/include/isc/iterated_hash.h b/lib/isc/include/isc/iterated_hash.h
new file mode 100644 (file)
index 0000000..01ecc2e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2004  Nominet, Ltd.
+ *
+ * Permission to use, copy, modify, and 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 NOMINET DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL NOMINET 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: iterated_hash.h,v 1.2 2008/09/24 02:46:23 marka Exp $ */
+
+#ifndef ISC_ITERATED_HASH_H
+#define ISC_ITERATED_HASH_H 1
+
+#include <isc/lang.h>
+#include <isc/sha1.h>
+
+/*
+ * The maximal hash length that can be encoded it a name
+ * using base32hex.  floor(255/8)*5
+ */
+#define NSEC3_MAX_HASH_LENGTH 155
+
+/*
+ * The maximum has that can be encoded in a single label using
+ * base32hex.  floor(63/8)*5
+ */
+#define NSEC3_MAX_LABEL_HASH 35
+
+ISC_LANG_BEGINDECLS
+
+int isc_iterated_hash(unsigned char out[NSEC3_MAX_HASH_LENGTH],
+                     unsigned int hashalg, int iterations,
+                     const unsigned char *salt, int saltlength,
+                     const unsigned char *in, int inlength);
+
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISC_ITERATED_HASH_H */
index 5b650851e4a480bb802ea1e08828dcf800d3493f..9ddd0cf41564f478d60ce04bb1dfae361b321513 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: result.h,v 1.69 2007/06/19 23:47:18 tbox Exp $ */
+/* $Id: result.h,v 1.70 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef ISC_RESULT_H
 #define ISC_RESULT_H 1
 #define ISC_R_DISABLED                 57      /*%< disabled */
 #define ISC_R_MAXSIZE                  58      /*%< max size */
 #define ISC_R_BADADDRESSFORM           59      /*%< invalid address format */
+#define ISC_R_BADBASE32                        60      /*%< bad base32 encoding */
 
 /*% Not a result code: the number of results. */
-#define ISC_R_NRESULTS                         60
+#define ISC_R_NRESULTS                         61
 
 ISC_LANG_BEGINDECLS
 
index 39a777978fc630ae0e334d0b294cd8171a97e755..331c56e33ab622272b38d5085b5762d16ffdd7af 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: result.c,v 1.69 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: result.c,v 1.70 2008/09/24 02:46:23 marka Exp $ */
 
 /*! \file */
 
@@ -100,7 +100,8 @@ static const char *text[ISC_R_NRESULTS] = {
        "not a valid number",                   /*%< 56 */
        "disabled",                             /*%< 57 */
        "max size",                             /*%< 58 */
-       "invalid address format"                /*%< 59 */
+       "invalid address format",               /*%< 59 */
+       "bad base32 encoding",                  /*%< 60 */
 };
 
 #define ISC_RESULT_RESULTSET                   2
index 936bcd5cb9422bb261cdb8f44d4e9292d6ca074e..065a6024f66c1086f0a23e308af6d8f3c8db3b23 100644 (file)
@@ -46,6 +46,14 @@ isc_app_start
 isc_app_unblock
 isc_assertion_setcallback
 isc_assertion_typetotext
+isc_base32_decoderegion
+isc_base32_decodestring
+isc_base32_tobuffer
+isc_base32_totext
+isc_base32hex_decoderegion
+isc_base32hex_decodestring
+isc_base32hex_tobuffer
+isc_base32hex_totext
 isc_base64_decodestring
 isc_base64_tobuffer
 isc_base64_totext
@@ -179,6 +187,7 @@ isc_interfaceiter_first
 isc_interfaceiter_next
 isc_interval_iszero
 isc_interval_set
+isc_iterated_hash
 isc_keyboard_canceled
 isc_keyboard_close
 isc_keyboard_getchar
index 39af0df74bca8cdab6ca7a1863cb73c19893a35f..d8094cdae3e3ea9880c84097cded6a38701663a7 100644 (file)
@@ -133,6 +133,10 @@ SOURCE=.\ipv6.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=..\iterated_hash.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\keyboard.c\r
 # End Source File\r
 # Begin Source File\r
@@ -201,6 +205,10 @@ SOURCE=..\include\isc\assertions.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=..\include\isc\base32.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=..\include\isc\base64.h\r
 # End Source File\r
 # Begin Source File\r
@@ -313,6 +321,10 @@ SOURCE=..\include\isc\ipv6.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=..\include\isc\iterated_hash.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\include\isc\keyboard.h\r
 # End Source File\r
 # Begin Source File\r
@@ -565,6 +577,10 @@ SOURCE=..\assertions.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=..\base32.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=..\base64.c\r
 # End Source File\r
 # Begin Source File\r
index 3b5fc556f95d07b3ad2c0223242ba6ceb624fdc1..9e8e06ba2b21610d4a907443a470a20a7f84e168 100644 (file)
@@ -115,6 +115,7 @@ ALL : "..\..\..\Build\Release\libisc.dll"
 CLEAN :
        -@erase "$(INTDIR)\app.obj"
        -@erase "$(INTDIR)\assertions.obj"
+       -@erase "$(INTDIR)\base32.obj"
        -@erase "$(INTDIR)\base64.obj"
        -@erase "$(INTDIR)\bitstring.obj"
        -@erase "$(INTDIR)\buffer.obj"
@@ -140,6 +141,7 @@ CLEAN :
        -@erase "$(INTDIR)\inet_pton.obj"
        -@erase "$(INTDIR)\interfaceiter.obj"
        -@erase "$(INTDIR)\ipv6.obj"
+       -@erase "$(INTDIR)\iterated_hash.obj"
        -@erase "$(INTDIR)\keyboard.obj"
        -@erase "$(INTDIR)\lex.obj"
        -@erase "$(INTDIR)\lfsr.obj"
@@ -214,6 +216,7 @@ LINK32_OBJS= \
        "$(INTDIR)\fsaccess.obj" \
        "$(INTDIR)\interfaceiter.obj" \
        "$(INTDIR)\ipv6.obj" \
+       "$(INTDIR)\iterated_hash.obj" \
        "$(INTDIR)\keyboard.obj" \
        "$(INTDIR)\net.obj" \
        "$(INTDIR)\ntpaths.obj" \
@@ -230,6 +233,7 @@ LINK32_OBJS= \
        "$(INTDIR)\version.obj" \
        "$(INTDIR)\win32os.obj" \
        "$(INTDIR)\assertions.obj" \
+       "$(INTDIR)\base32.obj" \
        "$(INTDIR)\base64.obj" \
        "$(INTDIR)\bitstring.obj" \
        "$(INTDIR)\buffer.obj" \
@@ -298,6 +302,8 @@ CLEAN :
        -@erase "$(INTDIR)\app.sbr"
        -@erase "$(INTDIR)\assertions.obj"
        -@erase "$(INTDIR)\assertions.sbr"
+       -@erase "$(INTDIR)\base32.obj"
+       -@erase "$(INTDIR)\base32.sbr"
        -@erase "$(INTDIR)\base64.obj"
        -@erase "$(INTDIR)\base64.sbr"
        -@erase "$(INTDIR)\bitstring.obj"
@@ -348,6 +354,8 @@ CLEAN :
        -@erase "$(INTDIR)\interfaceiter.sbr"
        -@erase "$(INTDIR)\ipv6.obj"
        -@erase "$(INTDIR)\ipv6.sbr"
+       -@erase "$(INTDIR)\iterated_hash.obj"
+       -@erase "$(INTDIR)\iterated_hash.sbr"
        -@erase "$(INTDIR)\keyboard.obj"
        -@erase "$(INTDIR)\keyboard.sbr"
        -@erase "$(INTDIR)\lex.obj"
@@ -465,6 +473,7 @@ BSC32_SBRS= \
        "$(INTDIR)\fsaccess.sbr" \
        "$(INTDIR)\interfaceiter.sbr" \
        "$(INTDIR)\ipv6.sbr" \
+       "$(INTDIR)\iterated_hash.sbr" \
        "$(INTDIR)\keyboard.sbr" \
        "$(INTDIR)\net.sbr" \
        "$(INTDIR)\ntpaths.sbr" \
@@ -481,6 +490,7 @@ BSC32_SBRS= \
        "$(INTDIR)\version.sbr" \
        "$(INTDIR)\win32os.sbr" \
        "$(INTDIR)\assertions.sbr" \
+       "$(INTDIR)\base32.sbr" \
        "$(INTDIR)\base64.sbr" \
        "$(INTDIR)\bitstring.sbr" \
        "$(INTDIR)\buffer.sbr" \
@@ -547,6 +557,7 @@ LINK32_OBJS= \
        "$(INTDIR)\fsaccess.obj" \
        "$(INTDIR)\interfaceiter.obj" \
        "$(INTDIR)\ipv6.obj" \
+       "$(INTDIR)\iterated_hash.obj" \
        "$(INTDIR)\keyboard.obj" \
        "$(INTDIR)\net.obj" \
        "$(INTDIR)\ntpaths.obj" \
@@ -563,6 +574,7 @@ LINK32_OBJS= \
        "$(INTDIR)\version.obj" \
        "$(INTDIR)\win32os.obj" \
        "$(INTDIR)\assertions.obj" \
+       "$(INTDIR)\base32.obj" \
        "$(INTDIR)\base64.obj" \
        "$(INTDIR)\bitstring.obj" \
        "$(INTDIR)\buffer.obj" \
@@ -816,6 +828,23 @@ SOURCE=.\ipv6.c
 "$(INTDIR)\ipv6.obj"   "$(INTDIR)\ipv6.sbr" : $(SOURCE) "$(INTDIR)"
 
 
+!ENDIF 
+
+
+SOURCE=..\iterated_hash.c
+
+!IF  "$(CFG)" == "libisc - Win32 Release"
+
+
+"$(INTDIR)\iterated_hash.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "libisc - Win32 Debug"
+
+
+"$(INTDIR)\iterated_hash.obj"  "$(INTDIR)\iterated_hash.sbr" : $(SOURCE) "$(INTDIR)"
+
+
 !ENDIF 
 
 SOURCE=.\keyboard.c
@@ -1074,6 +1103,24 @@ SOURCE=..\assertions.c
        $(CPP) $(CPP_PROJ) $(SOURCE)
 
 
+!ENDIF 
+
+SOURCE=..\base32.c
+
+!IF  "$(CFG)" == "libisc - Win32 Release"
+
+
+"$(INTDIR)\base32.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "libisc - Win32 Debug"
+
+
+"$(INTDIR)\base32.obj" "$(INTDIR)\base32.sbr" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
 !ENDIF 
 
 SOURCE=..\base64.c
index be65d4874812f29efbbb52bea2b33ea1e3aaf9da..aa372e99233524367221979c3e9c4633531ff279 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: grammar.h,v 1.15 2007/06/19 23:47:22 tbox Exp $ */
+/* $Id: grammar.h,v 1.16 2008/09/24 02:46:23 marka Exp $ */
 
 #ifndef ISCCFG_GRAMMAR_H
 #define ISCCFG_GRAMMAR_H 1
@@ -51,6 +51,8 @@
  * "directory" option.
  */
 #define CFG_CLAUSEFLAG_CALLBACK                0x00000020
+/*% A option that is only used in testing. */
+#define CFG_CLAUSEFLAG_TESTONLY                0x00000040
 
 typedef struct cfg_clausedef cfg_clausedef_t;
 typedef struct cfg_tuplefielddef cfg_tuplefielddef_t;
index 33aa668f336aeb5cc7db982578e0cbffd8d673f2..e96258ce01150572440fa3cc32766e777773a42d 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: namedconf.c,v 1.90 2008/09/04 05:56:43 marka Exp $ */
+/* $Id: namedconf.c,v 1.91 2008/09/24 02:46:23 marka Exp $ */
 
 /*! \file */
 
@@ -948,6 +948,7 @@ zone_clauses[] = {
        { "notify-source", &cfg_type_sockaddr4wild, 0 },
        { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
        { "notify-to-soa", &cfg_type_boolean, 0 },
+       { "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY },
        { "sig-signing-nodes", &cfg_type_uint32, 0 },
        { "sig-signing-signatures", &cfg_type_uint32, 0 },
        { "sig-signing-type", &cfg_type_uint32, 0 },
index 79c7c48914baa80ff7a612a59de729817f4cd53b..21f044b0523ccdac80546b3e4950232609fb5c2e 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: parser.c,v 1.127 2007/10/12 04:17:18 each Exp $ */
+/* $Id: parser.c,v 1.128 2008/09/24 02:46:23 marka Exp $ */
 
 /*! \file */
 
@@ -1477,6 +1477,7 @@ static struct flagtext {
        { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
        { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
        { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
+       { CFG_CLAUSEFLAG_TESTONLY, "test only" },
        { 0, NULL }
 };
 
index a1d12f5ca749395670dba1fcc337c34d05d9b002..8cf3ca658502146d4cfcf904284098a77c26961b 100644 (file)
 ./bin/tests/net/netaddr_multicast.c            C       2000,2001,2004,2007
 ./bin/tests/net/sockaddr_multicast.c           C       2000,2001,2004,2007
 ./bin/tests/net/testsuite.h                    C       2000,2001,2004,2007
+./bin/tests/nsec3hash.c                                C       2006
 ./bin/tests/nsecify.c                          C       1999,2000,2001,2003,2004,2007
 ./bin/tests/printmsg.c                         C       1998,1999,2000,2001,2004,2007
 ./bin/tests/printmsg.h                         C       1998,1999,2000,2001,2004,2007
 ./bin/tests/system/dnssec/ns1/root.db.in       ZONE    2000,2001,2004,2007
 ./bin/tests/system/dnssec/ns1/sign.sh          SH      2000,2001,2002,2003,2004,2006,2007
 ./bin/tests/system/dnssec/ns2/.cvsignore       X       2000,2001
+./bin/tests/system/dnssec/ns2/child.nsec3.example.db   ZONE    2006
+./bin/tests/system/dnssec/ns2/child.optout.example.db  ZONE    2006
 ./bin/tests/system/dnssec/ns2/dlv.db.in                ZONE    2004,2007
 ./bin/tests/system/dnssec/ns2/dst.example.db.in        ZONE    2004,2007
 ./bin/tests/system/dnssec/ns2/example.db.in    ZONE    2000,2001,2002,2004,2007
 ./bin/tests/system/dnssec/ns3/dynamic.example.db.in    ZONE    2002,2004,2007
 ./bin/tests/system/dnssec/ns3/insecure.example.db      ZONE    2000,2001,2004,2007
 ./bin/tests/system/dnssec/ns3/keyless.example.db.in    ZONE    2001,2002,2004,2007
+./bin/tests/system/dnssec/ns3/multiple.example.db.in   ZONE    2006
 ./bin/tests/system/dnssec/ns3/named.conf       CONF-C  2000,2001,2002,2004,2006,2007
+./bin/tests/system/dnssec/ns3/nsec3-unknown.example.db.in      ZONE    2006
+./bin/tests/system/dnssec/ns3/nsec3.example.db.in      ZONE    2006
+./bin/tests/system/dnssec/ns3/optout-unknown.example.db.in     ZONE    2006
+./bin/tests/system/dnssec/ns3/optout.example.db.in     ZONE    2006
 ./bin/tests/system/dnssec/ns3/secure.example.db.in     ZONE    2000,2001,2004,2007
 ./bin/tests/system/dnssec/ns3/sign.sh          SH      2000,2001,2002,2004,2006,2007
 ./bin/tests/system/dnssec/ns4/.cvsignore       X       2000,2001
 ./bin/tests/system/dnssec/ns5/named.conf       CONF-C  2000,2001,2004,2006,2007
 ./bin/tests/system/dnssec/ns5/trusted.conf.bad CONF-C  2000,2001,2004,2007
 ./bin/tests/system/dnssec/ns6/named.conf       CONF-C  2004,2006,2007
+./bin/tests/system/dnssec/ns7/named.conf       CONF-C  2006
 ./bin/tests/system/dnssec/prereq.sh            SH      2000,2001,2002,2004,2006,2007
 ./bin/tests/system/dnssec/setup.sh             SH      2000,2001,2004,2007
 ./bin/tests/system/dnssec/tests.sh             SH      2000,2001,2002,2004,2005,2006,2007
 ./lib/bind/isc/Makefile.in                     MAKE    2001,2004,2007,2008
 ./lib/bind/isc/assertions.c                    X       2001,2004,2005
 ./lib/bind/isc/assertions.mdoc                 X       2001,2004
+./lib/bind/isc/base32.c                                X       2006
 ./lib/bind/isc/base64.c                                X       2001,2004,2005
 ./lib/bind/isc/bitncmp.c                       X       2001,2004,2005,2008
 ./lib/bind/isc/bitncmp.mdoc                    X       2001,2004
 ./lib/dns/name.c                               C       1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008
 ./lib/dns/ncache.c                             C       1999,2000,2001,2002,2003,2004,2005,2007
 ./lib/dns/nsec.c                               C       1999,2000,2001,2003,2004,2005,2007
+./lib/dns/nsec3.c                              C       2006
 ./lib/dns/openssl_link.c                       C.NAI   1999,2000,2001,2002,2003,2004,2005,2006,2007,2008
 ./lib/dns/openssldh_link.c                     C.NAI   1999,2000,2001,2002,2004,2005,2006,2007,2008
 ./lib/dns/openssldsa_link.c                    C.NAI   1999,2000,2001,2002,2004,2005,2006,2007,2008
 ./lib/isc/inet_aton.c                          C.PORTION       1996,1997,1998,1999,2000,2001,2004,2005,2007
 ./lib/isc/inet_ntop.c                          C       1996,1997,1998,1999,2000,2001,2004,2005,2007
 ./lib/isc/inet_pton.c                          C       1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2007
-./lib/isc/iterated_hash.c                      C       2008
+./lib/isc/iterated_hash.c                      C       2006,2008
 ./lib/isc/lex.c                                        C       1998,1999,2000,2001,2002,2003,2004,2005,2007
 ./lib/isc/lfsr.c                               C       1999,2000,2001,2002,2004,2005,2007
 ./lib/isc/lib.c                                        C       1999,2000,2001,2004,2005,2007