BIND 10 master, updated. 966c129cc3c538841421f1e554167d33ef9bdf25 [master] Merge branch 'trac1638'

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Feb 16 21:13:15 UTC 2012


The branch, master has been updated
       via  966c129cc3c538841421f1e554167d33ef9bdf25 (commit)
       via  326b046810b3d77997fe95f2eab21b591a709ac7 (commit)
       via  d3877a19035f09caa0b457cbe01fe28edacdeddd (commit)
       via  b1a417fa43cc2d467fd67d79ee7749fddaf92011 (commit)
       via  001717c6baf2ed1929f9a585408ae66f26eed014 (commit)
       via  bf67156c7798685263abd1fc8c60669a78bcec2d (commit)
       via  6292462f7e27fe5970d7f46b0cb3b360d21d830c (commit)
       via  cfb3f053d87cd3d406c84acd548290b3ca77492d (commit)
       via  602204a5a15d7e602381a885f366acf9ba10b0d4 (commit)
       via  75e6307ae2766d7eb46875d95fb13b5ef2b11f10 (commit)
       via  c91c89ac62e179ac0a1568cd835f55990de3fd19 (commit)
       via  c356ad2e53fe54c417b00ab7b6d806a31fa3ac5f (commit)
       via  a5fec871b80f8ef0bddc2e708235581bde00533f (commit)
       via  95b8b13fa13f48b524aa46691e96bc21062559d6 (commit)
       via  a2bc5862342ad6063a80809c284fe395cf3e6531 (commit)
      from  2a349b194cf4f946cbd60d2295a278a6e0f3c0fa (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 966c129cc3c538841421f1e554167d33ef9bdf25
Merge: 2a349b1 326b046
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Thu Feb 16 13:08:43 2012 -0800

    [master] Merge branch 'trac1638'

-----------------------------------------------------------------------

Summary of changes:
 src/lib/dns/Makefile.am                            |    4 +
 .../dns/rdata/generic/detail/nsec3param_common.cc  |  130 ++++++++++
 .../dns/rdata/generic/detail/nsec3param_common.h   |  134 ++++++++++
 src/lib/dns/rdata/generic/detail/nsec_bitmap.h     |    5 +
 src/lib/dns/rdata/generic/nsec3_50.cc              |   81 ++-----
 src/lib/dns/rdata/generic/nsec3param_51.cc         |  119 ++++------
 src/lib/dns/tests/Makefile.am                      |    1 +
 src/lib/dns/tests/rdata_nsec3_unittest.cc          |  114 +--------
 .../dns/tests/rdata_nsec3param_like_unittest.cc    |  260 ++++++++++++++++++++
 src/lib/dns/tests/rdata_nsec3param_unittest.cc     |   42 +++-
 src/lib/dns/tests/testdata/Makefile.am             |    6 +
 .../dns/tests/testdata/rdata_nsec3param_fromWire1  |    4 +-
 .../testdata/rdata_nsec3param_fromWire11.spec      |    8 +
 .../testdata/rdata_nsec3param_fromWire13.spec      |    9 +
 .../tests/testdata/rdata_nsec3param_fromWire2.spec |    9 +
 src/lib/util/python/gen_wiredata.py.in             |   48 +++-
 16 files changed, 712 insertions(+), 262 deletions(-)
 create mode 100644 src/lib/dns/rdata/generic/detail/nsec3param_common.cc
 create mode 100644 src/lib/dns/rdata/generic/detail/nsec3param_common.h
 create mode 100644 src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec

-----------------------------------------------------------------------
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 5e2a48d..9ed70ba 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -23,6 +23,8 @@ EXTRA_DIST += rdata/generic/cname_5.cc
 EXTRA_DIST += rdata/generic/cname_5.h
 EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc
 EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h
+EXTRA_DIST += rdata/generic/detail/nsec3param_common.cc
+EXTRA_DIST += rdata/generic/detail/nsec3param_common.h
 EXTRA_DIST += rdata/generic/detail/txt_like.h
 EXTRA_DIST += rdata/generic/detail/ds_like.h
 EXTRA_DIST += rdata/generic/dlv_32769.cc
@@ -113,6 +115,8 @@ libdns___la_SOURCES += tsigrecord.h tsigrecord.cc
 libdns___la_SOURCES += character_string.h character_string.cc
 libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
 libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
+libdns___la_SOURCES += rdata/generic/detail/nsec3param_common.cc
+libdns___la_SOURCES += rdata/generic/detail/nsec3param_common.h
 libdns___la_SOURCES += rdata/generic/detail/txt_like.h
 libdns___la_SOURCES += rdata/generic/detail/ds_like.h
 
diff --git a/src/lib/dns/rdata/generic/detail/nsec3param_common.cc b/src/lib/dns/rdata/generic/detail/nsec3param_common.cc
new file mode 100644
index 0000000..a7a0bb4
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/nsec3param_common.cc
@@ -0,0 +1,130 @@
+// Copyright (C) 2012  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.
+
+#include <exceptions/exceptions.h>
+
+#include <util/encode/hex.h>
+#include <util/buffer.h>
+
+#include <dns/exceptions.h>
+#include <dns/rdata.h>
+#include <dns/rdata/generic/detail/nsec3param_common.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <sstream>
+#include <vector>
+#include <stdint.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::util::encode;
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+namespace nsec3 {
+
+ParseNSEC3ParamResult
+parseNSEC3ParamText(const char* const rrtype_name,
+                    const string& rdata_str, istringstream& iss,
+                    vector<uint8_t>& salt)
+{
+    unsigned int hashalg, flags, iterations;
+    string iterations_str, salthex;
+
+    iss >> hashalg >> flags >> iterations_str >> salthex;
+    if (iss.bad() || iss.fail()) {
+        isc_throw(InvalidRdataText, "Invalid " << rrtype_name <<
+                  " text: " << rdata_str);
+    }
+    if (hashalg > 0xff) {
+        isc_throw(InvalidRdataText, rrtype_name <<
+                  " hash algorithm out of range: " << hashalg);
+    }
+    if (flags > 0xff) {
+        isc_throw(InvalidRdataText, rrtype_name << " flags out of range: " <<
+                  flags);
+    }
+    // Convert iteration.  To reject an invalid case where there's no space
+    // between iteration and salt, we extract this field as string and convert
+    // to integer.
+    try {
+        iterations = boost::lexical_cast<unsigned int>(iterations_str);
+    } catch (const boost::bad_lexical_cast&) {
+        isc_throw(InvalidRdataText, "Bad " << rrtype_name <<
+                  " iteration: " << iterations_str);
+    }
+    if (iterations > 0xffff) {
+        isc_throw(InvalidRdataText, rrtype_name <<
+                  " iterations out of range: " <<
+            iterations);
+    }
+
+    // Salt is up to 255 bytes, and space is not allowed in the HEX encoding,
+    // so the encoded string cannot be longer than the double of max length
+    // of the actual salt.
+    if (salthex.size() > 255 * 2) {
+        isc_throw(InvalidRdataText, rrtype_name << " salt is too long: "
+                  << salthex.size() << " (encoded) bytes");
+    }
+    if (salthex != "-") {       // "-" means a 0-length salt
+        decodeHex(salthex, salt);
+    }
+
+    return (ParseNSEC3ParamResult(hashalg, flags, iterations));
+}
+
+ParseNSEC3ParamResult
+parseNSEC3ParamWire(const char* const rrtype_name,
+                    InputBuffer& buffer,
+                    size_t& rdata_len, std::vector<uint8_t>& salt)
+{
+    // NSEC3/NSEC3PARAM RR must have at least 5 octets:
+    // hash algorithm(1), flags(1), iteration(2), saltlen(1)
+    if (rdata_len < 5) {
+        isc_throw(DNSMessageFORMERR, rrtype_name << " too short, length: "
+                  << rdata_len);
+    }
+
+    const uint8_t hashalg = buffer.readUint8();
+    const uint8_t flags = buffer.readUint8();
+    const uint16_t iterations = buffer.readUint16();
+
+    const uint8_t saltlen = buffer.readUint8();
+    rdata_len -= 5;
+    if (rdata_len < saltlen) {
+        isc_throw(DNSMessageFORMERR, rrtype_name <<
+                  " salt length is too large: " <<
+                  static_cast<unsigned int>(saltlen));
+    }
+
+    salt.resize(saltlen);
+    if (saltlen > 0) {
+        buffer.readData(&salt[0], saltlen);
+        rdata_len -= saltlen;
+    }
+
+    return (ParseNSEC3ParamResult(hashalg, flags, iterations));
+}
+
+} // end of nsec3
+} // end of detail
+} // end of generic
+} // end of rdata
+} // end of dns
+} // end of isc
+
diff --git a/src/lib/dns/rdata/generic/detail/nsec3param_common.h b/src/lib/dns/rdata/generic/detail/nsec3param_common.h
new file mode 100644
index 0000000..515777b
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/nsec3param_common.h
@@ -0,0 +1,134 @@
+// Copyright (C) 2012  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.
+
+#ifndef __NSEC3PARAM_COMMON_H
+#define __NSEC3PARAM_COMMON_H 1
+
+#include <util/buffer.h>
+
+#include <stdint.h>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+namespace nsec3 {
+
+/// \file
+///
+/// This helper module provides some utilities that handle NSEC3 and
+/// NSEC3PARAM RDATA.  They share the first few fields, and some operations
+/// on these fields are sufficiently complicated, so it would make sense to
+/// consolidate the processing logic into a single implementation module.
+///
+/// The functions defined here are essentially private and are only expected
+/// to be called from the \c NSEC3 and \c NSEC3PARAM class implementations.
+
+/// \brief Result values of the utilities.
+///
+/// This structure encapsulates a tuple of NSEC3/NSEC3PARAM algorithm,
+/// flags and iterations field values.  This is used as the return value
+/// of the utility functions defined in this module so the caller can
+/// use it for constructing the corresponding RDATA.
+struct ParseNSEC3ParamResult {
+    ParseNSEC3ParamResult(uint8_t param_algorithm, uint8_t param_flags,
+                          uint16_t param_iterations) :
+        algorithm(param_algorithm), flags(param_flags),
+        iterations(param_iterations)
+    {}
+    const uint8_t algorithm;
+    const uint8_t flags;
+    const uint16_t iterations;
+};
+
+/// \brief Convert textual representation of NSEC3 parameters.
+///
+/// This function takes an input string stream that consists of a complete
+/// textual representation of an NSEC3 or NSEC3PARAM RDATA and parses it
+/// extracting the hash algorithm, flags, iterations, and salt fields.
+///
+/// The first three fields are returned as the return value of this function.
+/// The salt will be stored in the given vector.  The vector is expected
+/// to be empty, but if not, the existing content will be overridden.
+///
+/// On successful return the given input stream will reach the end of the
+/// salt field.
+///
+/// \exception isc::BadValue The salt is not a valid hex string.
+/// \exception InvalidRdataText The given string is otherwise invalid for
+/// NSEC3 or NSEC3PARAM fields.
+///
+/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of
+/// exception messages.
+/// \param rdata_str A complete textual string of the RDATA; used as part of
+/// exception messages.
+/// \param iss Input stream that consists of a complete textual string of
+/// the RDATA.
+/// \param salt A placeholder for the salt field value of the RDATA.
+/// Expected to be empty, but it's not checked (and will be overridden).
+///
+/// \return The hash algorithm, flags, iterations in the form of
+/// ParseNSEC3ParamResult.
+ParseNSEC3ParamResult parseNSEC3ParamText(const char* const rrtype_name,
+                                          const std::string& rdata_str,
+                                          std::istringstream& iss,
+                                          std::vector<uint8_t>& salt);
+
+/// \brief Extract NSEC3 parameters from wire-format data.
+///
+/// This function takes an input buffer that stores wire-format NSEC3 or
+/// NSEC3PARAM RDATA and parses it extracting the hash algorithm, flags,
+/// iterations, and salt fields.
+///
+/// The first three fields are returned as the return value of this function.
+/// The salt will be stored in the given vector.  The vector is expected
+/// to be empty, but if not, the existing content will be overridden.
+///
+/// On successful return the input buffer will point to the end of the
+/// salt field; rdata_len will be the length of the rest of RDATA
+/// (in the case of a valid NSEC3PARAM, it should be 0).
+///
+/// \exception DNSMessageFORMERR The wire data is invalid.
+///
+/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of
+/// exception messages.
+/// \param buffer An input buffer that stores wire-format RDATA.  It must
+/// point to the beginning of the data.
+/// \param rdata_len The total length of the RDATA.
+/// \param salt A placeholder for the salt field value of the RDATA.
+/// Expected to be empty, but it's not checked (and will be overridden).
+///
+/// \return The hash algorithm, flags, iterations in the form of
+/// ParseNSEC3ParamResult.
+ParseNSEC3ParamResult parseNSEC3ParamWire(const char* const rrtype_name,
+                                          isc::util::InputBuffer& buffer,
+                                          size_t& rdata_len,
+                                          std::vector<uint8_t>& salt);
+}
+}
+}
+}
+}
+}
+
+#endif  // __NSEC3PARAM_COMMON_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.h b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h
index 27a5166..85cae2e 100644
--- a/src/lib/dns/rdata/generic/detail/nsec_bitmap.h
+++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h
@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __NSECBITMAP_H
+#define __NSECBITMAP_H 1
+
 #include <stdint.h>
 
 #include <sstream>
@@ -97,6 +100,8 @@ void bitmapsToText(const std::vector<uint8_t>& typebits,
 }
 }
 
+#endif  // __NSECBITMAP_H
+
 // Local Variables:
 // mode: c++
 // End:
diff --git a/src/lib/dns/rdata/generic/nsec3_50.cc b/src/lib/dns/rdata/generic/nsec3_50.cc
index 3817941..b569d91 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.cc
+++ b/src/lib/dns/rdata/generic/nsec3_50.cc
@@ -17,6 +17,7 @@
 #include <string>
 #include <sstream>
 #include <vector>
+#include <cassert>
 
 #include <boost/lexical_cast.hpp>
 
@@ -32,12 +33,14 @@
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 #include <dns/rdata/generic/detail/nsec_bitmap.h>
+#include <dns/rdata/generic/detail/nsec3param_common.h>
 
 #include <stdio.h>
 #include <time.h>
 
 using namespace std;
 using namespace isc::dns::rdata::generic::detail::nsec;
+using namespace isc::dns::rdata::generic::detail::nsec3;
 using namespace isc::util::encode;
 using namespace isc::util;
 
@@ -65,42 +68,20 @@ NSEC3::NSEC3(const string& nsec3_str) :
     impl_(NULL)
 {
     istringstream iss(nsec3_str);
-    unsigned int hashalg, flags, iterations;
-    string iterations_str, salthex, nexthash;
+    vector<uint8_t> salt;
+    const ParseNSEC3ParamResult params =
+        parseNSEC3ParamText("NSEC3", nsec3_str, iss, salt);
 
-    iss >> hashalg >> flags >> iterations_str >> salthex >> nexthash;
+    // Extract Next hash.  It must be an unpadded base32hex string.
+    string nexthash;
+    iss >> nexthash;
     if (iss.bad() || iss.fail()) {
         isc_throw(InvalidRdataText, "Invalid NSEC3 text: " << nsec3_str);
     }
-    if (hashalg > 0xff) {
-        isc_throw(InvalidRdataText,
-                  "NSEC3 hash algorithm out of range: " << hashalg);
-    }
-    if (flags > 0xff) {
-        isc_throw(InvalidRdataText, "NSEC3 flags out of range: " << flags);
-    }
-    // Convert iteration.  To reject an invalid case where there's no space
-    // between iteration and salt, we extract this field as string and convert
-    // to integer.
-    try {
-        iterations = boost::lexical_cast<unsigned int>(iterations_str);
-    } catch (const boost::bad_lexical_cast&) {
-        isc_throw(InvalidRdataText, "Bad NSEC3 iteration: " << iterations_str);
+    assert(!nexthash.empty());
+    if (*nexthash.rbegin() == '=') {
+        isc_throw(InvalidRdataText, "NSEC3 hash has padding: " << nsec3_str);
     }
-    if (iterations > 0xffff) {
-        isc_throw(InvalidRdataText, "NSEC3 iterations out of range: " <<
-            iterations);
-    }
-
-    vector<uint8_t> salt;
-    if (salthex != "-") {       // "-" means a 0-length salt
-        decodeHex(salthex, salt);
-    }
-    if (salt.size() > 255) {
-        isc_throw(InvalidRdataText, "NSEC3 salt is too long: "
-                  << salt.size() << " bytes");
-    }
-
     vector<uint8_t> next;
     decodeBase32Hex(nexthash, next);
     if (next.size() > 255) {
@@ -110,7 +91,8 @@ NSEC3::NSEC3(const string& nsec3_str) :
 
     // For NSEC3 empty bitmap is possible and allowed.
     if (iss.eof()) {
-        impl_ = new NSEC3Impl(hashalg, flags, iterations, salt, next,
+        impl_ = new NSEC3Impl(params.algorithm, params.flags,
+                              params.iterations, salt, next,
                               vector<uint8_t>());
         return;
     }
@@ -118,36 +100,18 @@ NSEC3::NSEC3(const string& nsec3_str) :
     vector<uint8_t> typebits;
     buildBitmapsFromText("NSEC3", iss, typebits);
 
-    impl_ = new NSEC3Impl(hashalg, flags, iterations, salt, next, typebits);
+    impl_ = new NSEC3Impl(params.algorithm, params.flags, params.iterations,
+                          salt, next, typebits);
 }
 
 NSEC3::NSEC3(InputBuffer& buffer, size_t rdata_len) {
-    // NSEC3 RR must have at least 5 octets:
-    // hash algorithm(1), flags(1), iteration(2), saltlen(1)
-    if (rdata_len < 5) {
-        isc_throw(DNSMessageFORMERR, "NSEC3 too short, length: " << rdata_len);
-    }
-
-    const uint8_t hashalg = buffer.readUint8();
-    const uint8_t flags = buffer.readUint8();
-    const uint16_t iterations = buffer.readUint16();
-
-    const uint8_t saltlen = buffer.readUint8();
-    rdata_len -= 5;
-    if (rdata_len < saltlen) {
-        isc_throw(DNSMessageFORMERR, "NSEC3 salt length is too large: " <<
-                  static_cast<unsigned int>(saltlen));
-    }
-
-    vector<uint8_t> salt(saltlen);
-    if (saltlen > 0) {
-        buffer.readData(&salt[0], saltlen);
-        rdata_len -= saltlen;
-    }
+    vector<uint8_t> salt;
+    const ParseNSEC3ParamResult params =
+        parseNSEC3ParamWire("NSEC3", buffer, rdata_len, salt);
 
     if (rdata_len < 1) {
         isc_throw(DNSMessageFORMERR, "NSEC3 too short to contain hash length, "
-                  "length: " << rdata_len + saltlen + 5);
+                  "length: " << rdata_len + salt.size() + 5);
     }
     const uint8_t nextlen = buffer.readUint8();
     --rdata_len;
@@ -168,7 +132,8 @@ NSEC3::NSEC3(InputBuffer& buffer, size_t rdata_len) {
         checkRRTypeBitmaps("NSEC3", typebits);
     }
 
-    impl_ = new NSEC3Impl(hashalg, flags, iterations, salt, next, typebits);
+    impl_ = new NSEC3Impl(params.algorithm, params.flags, params.iterations,
+                          salt, next, typebits);
 }
 
 NSEC3::NSEC3(const NSEC3& source) :
@@ -256,10 +221,10 @@ compareVectors(const vector<uint8_t>& v1, const vector<uint8_t>& v2,
 {
     const size_t len1 = v1.size();
     const size_t len2 = v2.size();
-    const size_t cmplen = min(len1, len2);
     if (check_length_first && len1 != len2) {
         return (len1 - len2);
     }
+    const size_t cmplen = min(len1, len2);
     const int cmp = cmplen == 0 ? 0 : memcmp(&v1.at(0), &v2.at(0), cmplen);
     if (cmp != 0) {
         return (cmp);
diff --git a/src/lib/dns/rdata/generic/nsec3param_51.cc b/src/lib/dns/rdata/generic/nsec3param_51.cc
index 850be14..ac09b57 100644
--- a/src/lib/dns/rdata/generic/nsec3param_51.cc
+++ b/src/lib/dns/rdata/generic/nsec3param_51.cc
@@ -12,22 +12,19 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <iostream>
-#include <string>
-#include <sstream>
-#include <vector>
-
-#include <boost/lexical_cast.hpp>
-
 #include <util/buffer.h>
 #include <util/encode/hex.h>
+
 #include <dns/messagerenderer.h>
-#include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rdata/generic/detail/nsec3param_common.h>
+
+#include <boost/lexical_cast.hpp>
 
-#include <stdio.h>
-#include <time.h>
+#include <string>
+#include <sstream>
+#include <vector>
 
 using namespace std;
 using namespace isc::util;
@@ -43,9 +40,9 @@ struct NSEC3PARAMImpl {
         hashalg_(hashalg), flags_(flags), iterations_(iterations), salt_(salt)
     {}
 
-    uint8_t hashalg_;
-    uint8_t flags_;
-    uint16_t iterations_;
+    const uint8_t hashalg_;
+    const uint8_t flags_;
+    const uint16_t iterations_;
     const vector<uint8_t> salt_;
 };
 
@@ -53,50 +50,26 @@ NSEC3PARAM::NSEC3PARAM(const string& nsec3param_str) :
     impl_(NULL)
 {
     istringstream iss(nsec3param_str);
-    uint16_t hashalg, flags, iterations;
-    stringbuf saltbuf;
-
-    iss >> hashalg >> flags >> iterations >> &saltbuf;
-    if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid NSEC3PARAM text");
-    }
-    if (hashalg > 0xf) {
-        isc_throw(InvalidRdataText, "NSEC3PARAM hash algorithm out of range");
-    }
-    if (flags > 0xff) {
-        isc_throw(InvalidRdataText, "NSEC3PARAM flags out of range");
-    }
-
-    const string salt_str = saltbuf.str();
     vector<uint8_t> salt;
-    if (salt_str != "-") { // "-" means an empty salt, no need to touch vector
-        decodeHex(saltbuf.str(), salt);
+    const ParseNSEC3ParamResult params =
+        parseNSEC3ParamText("NSEC3PARAM", nsec3param_str, iss, salt);
+
+    if (!iss.eof()) {
+        isc_throw(InvalidRdataText, "Invalid NSEC3PARAM (redundant text): "
+                  << nsec3param_str);
     }
 
-    impl_ = new NSEC3PARAMImpl(hashalg, flags, iterations, salt);
+    impl_ = new NSEC3PARAMImpl(params.algorithm, params.flags,
+                               params.iterations, salt);
 }
 
 NSEC3PARAM::NSEC3PARAM(InputBuffer& buffer, size_t rdata_len) {
-    if (rdata_len < 4) {
-        isc_throw(InvalidRdataLength, "NSEC3PARAM too short");
-    }
-
-    uint8_t hashalg = buffer.readUint8();
-    uint8_t flags = buffer.readUint8();
-    uint16_t iterations = buffer.readUint16();
-    rdata_len -= 4;
-
-    uint8_t saltlen = buffer.readUint8();
-    --rdata_len;
-
-    if (rdata_len < saltlen) {
-        isc_throw(InvalidRdataLength, "NSEC3PARAM salt too short");
-    }
-
-    vector<uint8_t> salt(saltlen);
-    buffer.readData(&salt[0], saltlen);
+    vector<uint8_t> salt;
+    const ParseNSEC3ParamResult params =
+        parseNSEC3ParamWire("NSEC3PARAM", buffer, rdata_len, salt);
 
-    impl_ = new NSEC3PARAMImpl(hashalg, flags, iterations, salt);
+    impl_ = new NSEC3PARAMImpl(params.algorithm, params.flags,
+                               params.iterations, salt);
 }
 
 NSEC3PARAM::NSEC3PARAM(const NSEC3PARAM& source) :
@@ -124,27 +97,31 @@ string
 NSEC3PARAM::toText() const {
     using namespace boost;
     return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) +
-        " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) +
-        " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) +
-        " " + encodeHex(impl_->salt_));
+            " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) +
+            " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) +
+            " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_)));
+}
+
+template <typename OUTPUT_TYPE>
+void
+toWireHelper(const NSEC3PARAMImpl& impl, OUTPUT_TYPE& output) {
+    output.writeUint8(impl.hashalg_);
+    output.writeUint8(impl.flags_);
+    output.writeUint16(impl.iterations_);
+    output.writeUint8(impl.salt_.size());
+    if (!impl.salt_.empty()) {
+        output.writeData(&impl.salt_[0], impl.salt_.size());
+    }
 }
 
 void
 NSEC3PARAM::toWire(OutputBuffer& buffer) const {
-    buffer.writeUint8(impl_->hashalg_);
-    buffer.writeUint8(impl_->flags_);
-    buffer.writeUint16(impl_->iterations_);
-    buffer.writeUint8(impl_->salt_.size());
-    buffer.writeData(&impl_->salt_[0], impl_->salt_.size());
+    toWireHelper(*impl_, buffer);
 }
 
 void
 NSEC3PARAM::toWire(AbstractMessageRenderer& renderer) const {
-    renderer.writeUint8(impl_->hashalg_);
-    renderer.writeUint8(impl_->flags_);
-    renderer.writeUint16(impl_->iterations_);
-    renderer.writeUint8(impl_->salt_.size());
-    renderer.writeData(&impl_->salt_[0], impl_->salt_.size());
+    toWireHelper(*impl_, renderer);
 }
 
 int
@@ -161,15 +138,18 @@ NSEC3PARAM::compare(const Rdata& other) const {
         return (impl_->iterations_ < other_param.impl_->iterations_ ? -1 : 1);
     }
 
-    size_t this_len = impl_->salt_.size();
-    size_t other_len = other_param.impl_->salt_.size();
-    size_t cmplen = min(this_len, other_len);
-    int cmp = memcmp(&impl_->salt_[0], &other_param.impl_->salt_[0],
-                     cmplen);
+    const size_t this_len = impl_->salt_.size();
+    const size_t other_len = other_param.impl_->salt_.size();
+    if (this_len != other_len) {
+        return (this_len - other_len);
+    }
+    const size_t cmplen = min(this_len, other_len);
+    const int cmp = (cmplen == 0) ? 0 :
+        memcmp(&impl_->salt_.at(0), &other_param.impl_->salt_.at(0), cmplen);
     if (cmp != 0) {
         return (cmp);
     } else {
-        return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1);
+        return (this_len - other_len);
     }
 }
 
@@ -193,6 +173,5 @@ NSEC3PARAM::getSalt() const {
     return (impl_->salt_);
 }
 
-
 // END_RDATA_NAMESPACE
 // END_ISC_NAMESPACE
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 7f7ab59..6e6f7f4 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -43,6 +43,7 @@ run_unittests_SOURCES += rdata_nsec_unittest.cc
 run_unittests_SOURCES += rdata_nsec3_unittest.cc
 run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc
 run_unittests_SOURCES += rdata_nsec3param_unittest.cc
+run_unittests_SOURCES += rdata_nsec3param_like_unittest.cc
 run_unittests_SOURCES += rdata_rrsig_unittest.cc
 run_unittests_SOURCES += rdata_rp_unittest.cc
 run_unittests_SOURCES += rdata_srv_unittest.cc
diff --git a/src/lib/dns/tests/rdata_nsec3_unittest.cc b/src/lib/dns/tests/rdata_nsec3_unittest.cc
index c8d21cf..edd2d4b 100644
--- a/src/lib/dns/tests/rdata_nsec3_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3_unittest.cc
@@ -39,19 +39,19 @@ using namespace isc::util::encode;
 using namespace isc::dns::rdata;
 
 namespace {
+
+// Note: some tests can be shared with NSEC3PARAM.  They are unified as
+// typed tests defined in nsec3param_like_unittest.
 class Rdata_NSEC3_Test : public RdataTest {
     // there's nothing to specialize
 public:
     Rdata_NSEC3_Test() :
         nsec3_txt("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
                   "NS SOA RRSIG DNSKEY NSEC3PARAM"),
-        nsec3_nosalt_txt("1 1 1 - H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A" ),
-        obuffer(0), renderer(obuffer)
+        nsec3_nosalt_txt("1 1 1 - H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A" )
     {}
     const string nsec3_txt;
     const string nsec3_nosalt_txt;
-    OutputBuffer obuffer;
-    MessageRenderer renderer;
 };
 
 TEST_F(Rdata_NSEC3_Test, fromText) {
@@ -59,20 +59,6 @@ TEST_F(Rdata_NSEC3_Test, fromText) {
     // text and construct nsec3_txt.  It will be tested against the wire format
     // representation in the createFromWire test.
 
-    // Numeric parameters have possible maximum values.  Unusual, but must
-    // be accepted.
-    EXPECT_NO_THROW(generic::NSEC3("255 255 65535 D399EAAB "
-                                   "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
-                                   "NS SOA RRSIG DNSKEY NSEC3PARAM"));
-
-    // 0-length salt
-    EXPECT_EQ(0, generic::NSEC3(nsec3_nosalt_txt).getSalt().size());
-
-    // salt that has the possible max length
-    EXPECT_EQ(255, generic::NSEC3("1 1 1 " + string(255 * 2, '0') +
-                                  " H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
-                                  "NS").getSalt().size());
-
     // hash that has the possible max length (see badText about the magic
     // numbers)
     EXPECT_EQ(255, generic::NSEC3("1 1 1 D399EAAB " +
@@ -84,47 +70,20 @@ TEST_F(Rdata_NSEC3_Test, fromText) {
                         "1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6"));
 }
 
-TEST_F(Rdata_NSEC3_Test, toText) {
-    // normal case
-    const generic::NSEC3 rdata_nsec3(nsec3_txt);
-    EXPECT_EQ(nsec3_txt, rdata_nsec3.toText());
-
-    // empty salt case
-    EXPECT_EQ(nsec3_nosalt_txt, generic::NSEC3(nsec3_nosalt_txt).toText());
-}
-
 TEST_F(Rdata_NSEC3_Test, badText) {
     EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEEE "
                                 "0123456789ABCDEFGHIJKLMNOPQRSTUV "
                                 "BIFF POW SPOON"),
                  InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEE "
-                                "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW A NS SOA"),
-                 BadValue);     // bad hex
-    EXPECT_THROW(generic::NSEC3("1 1 1 -- H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
-                                "A"),
-                 BadValue); // this shouldn't be confused a valid empty salt
     EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEEE "
                                 "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW A NS SOA"),
                  BadValue);     // bad base32hex
-    EXPECT_THROW(generic::NSEC3("1000000 1 1 ADDAFEEE "
-                                "0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
-                 InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3("1 1000000 1 ADDAFEEE "
-                                "0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
-                 InvalidRdataText);
     EXPECT_THROW(generic::NSEC3("1 1 1000000 ADDAFEEE "
                                 "0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
                  InvalidRdataText);
 
-    // There should be a space between "1" and "D399EAAB" (salt)
-    EXPECT_THROW(generic::NSEC3(
-                     "1 1 1D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
-                     "NS SOA RRSIG DNSKEY NSEC3PARAM"), InvalidRdataText);
-
-    // Salt is too long (possible max + 1 bytes)
-    EXPECT_THROW(generic::NSEC3("1 1 1 " + string(256 * 2, '0') +
-                                " H9RSFB7FPF2L8HG35CMPC765TDK23RP6 NS"),
+    // Next hash shouldn't be padded
+    EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEEE CPNMU=== A NS SOA"),
                  InvalidRdataText);
 
     // Hash is too long.  Max = 255 bytes, base32-hex converts each 5 bytes
@@ -136,34 +95,12 @@ TEST_F(Rdata_NSEC3_Test, badText) {
 }
 
 TEST_F(Rdata_NSEC3_Test, createFromWire) {
-    // Normal case
-    const generic::NSEC3 rdata_nsec3(nsec3_txt);
-    EXPECT_EQ(0, rdata_nsec3.compare(
-                  *rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
-                                        "rdata_nsec3_fromWire1")));
-
     // A valid NSEC3 RR with empty type bitmap.
     EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
                                          "rdata_nsec3_fromWire15.wire"));
 
-    // Too short RDLENGTH: it doesn't even contain the first 5 octets.
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
-                                      "rdata_nsec3_fromWire2.wire"),
-                 DNSMessageFORMERR);
-
     // Invalid bitmap cases are tested in Rdata_NSECBITMAP_Test.
 
-    // salt length is too large
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
-                                      "rdata_nsec3_fromWire11.wire"),
-                 DNSMessageFORMERR);
-
-    // empty salt.  unusual, but valid.
-    ConstRdataPtr rdata =
-        rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
-                             "rdata_nsec3_fromWire13.wire");
-    EXPECT_EQ(0, dynamic_cast<const generic::NSEC3&>(*rdata).getSalt().size());
-
     // hash length is too large
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
                                       "rdata_nsec3_fromWire12.wire"),
@@ -193,37 +130,6 @@ TEST_F(Rdata_NSEC3_Test, createFromWire) {
     }
 }
 
-template <typename OUTPUT_TYPE>
-void
-toWireCheck(OUTPUT_TYPE& output, const char* const data_file) {
-    vector<uint8_t> data;
-    UnitTestUtil::readWireData(data_file, data);
-    InputBuffer buffer(&data[0], data.size());
-    const uint16_t rdlen = buffer.readUint16();
-    const generic::NSEC3 nsec3 =
-        dynamic_cast<const generic::NSEC3&>(*createRdata(
-                                                RRType::NSEC3(), RRClass::IN(),
-                                                buffer, rdlen));
-
-    output.clear();
-    output.writeUint16(rdlen);
-    nsec3.toWire(output);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, output.getData(),
-                        output.getLength(), &data[0], data.size());
-}
-
-TEST_F(Rdata_NSEC3_Test, toWire) {
-    // normal case
-    toWireCheck(renderer, "rdata_nsec3_fromWire1");
-    toWireCheck(obuffer, "rdata_nsec3_fromWire1");
-
-    // empty salt
-    toWireCheck(renderer, "rdata_nsec3_fromWire13.wire");
-    toWireCheck(obuffer, "rdata_nsec3_fromWire13.wire");
-
-    // empty bitmap case is handled in the bitmap tests
-}
-
 TEST_F(Rdata_NSEC3_Test, assign) {
     generic::NSEC3 rdata_nsec3(nsec3_txt);
     generic::NSEC3 other_nsec3 = rdata_nsec3;
@@ -240,14 +146,8 @@ TEST_F(Rdata_NSEC3_Test, compare) {
 
     // test RDATAs, sorted in the ascendent order.  We only check comparison
     // on NSEC3-specific fields.  Bitmap comparison is tested in the bitmap
-    // tests.
+    // tests.  Common cases for NSEC3 and NSECPARAM3 are in their shared tests.
     vector<generic::NSEC3> compare_set;
-    compare_set.push_back(generic::NSEC3("0 0 0 D399EAAB D1K6GQ38"));
-    compare_set.push_back(generic::NSEC3("1 0 0 D399EAAB D1K6GQ38"));
-    compare_set.push_back(generic::NSEC3("1 1 0 D399EAAB D1K6GQ38"));
-    compare_set.push_back(generic::NSEC3("1 1 1 - D1K6GQ38"));
-    compare_set.push_back(generic::NSEC3("1 1 1 D399EAAB D1K6GQ38"));
-    compare_set.push_back(generic::NSEC3("1 1 1 FF99EAAB D1K6GQ38"));
     compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ38"));
     compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ0000000000"));
     compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ00UUUUUUUU"));
diff --git a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
new file mode 100644
index 0000000..30110df
--- /dev/null
+++ b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
@@ -0,0 +1,260 @@
+// Copyright (C) 2012  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.
+
+#include <dns/exceptions.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+
+#include <string>
+#include <vector>
+
+using namespace std;
+using isc::UnitTestUtil;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+
+// Template for shared tests for NSEC3 and NSEC3PARAM
+template <typename RDATA_TYPE>
+class NSEC3PARAMLikeTest : public RdataTest {
+protected:
+    NSEC3PARAMLikeTest() :
+        salt_txt("1 1 1 D399EAAB" + getCommonText()),
+        nosalt_txt("1 1 1 -" + getCommonText()),
+        obuffer(0), renderer(obuffer)
+    {}
+
+    RDATA_TYPE fromText(const string& rdata_text) {
+        return (RDATA_TYPE(rdata_text));
+    }
+
+    void compareCheck() const {
+        typename vector<RDATA_TYPE>::const_iterator it;
+        typename vector<RDATA_TYPE>::const_iterator const it_end =
+            compare_set.end();
+        for (it = compare_set.begin(); it != it_end - 1; ++it) {
+            SCOPED_TRACE("compare " + it->toText() + " to " +
+                         (it + 1)->toText());
+            EXPECT_GT(0, (*it).compare(*(it + 1)));
+            EXPECT_LT(0, (*(it + 1)).compare(*it));
+        }
+    }
+
+    const string salt_txt;      // RDATA text with salt
+    const string nosalt_txt;    // RDATA text without salt
+    OutputBuffer obuffer;       // used in toWire() tests
+    MessageRenderer renderer;   // ditto
+    vector<RDATA_TYPE> compare_set; // used in compare() tests
+
+    // Convert generic Rdata to the corresponding derived Rdata class object.
+    // Defined here because it depends on the template parameter.
+    static const RDATA_TYPE& convert(const Rdata& rdata) {
+        return (dynamic_cast<const RDATA_TYPE&>(rdata));
+    }
+
+    // These depend on the specific RR type.  We use specialized methods
+    // for them.
+    static RRType getType(); // return either RRType::NSEC3() or NSEC3PARAM()
+    static string getWireFilePrefix();
+    static string getCommonText(); // commonly used part of textual form
+};
+
+// Instantiate specific typed tests
+typedef ::testing::Types<generic::NSEC3, generic::NSEC3PARAM> TestRdataTypes;
+TYPED_TEST_CASE(NSEC3PARAMLikeTest, TestRdataTypes);
+
+template <>
+RRType
+NSEC3PARAMLikeTest<generic::NSEC3>::getType() {
+    return (RRType::NSEC3());
+}
+
+template <>
+RRType
+NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getType() {
+    return (RRType::NSEC3PARAM());
+}
+
+template <>
+string
+NSEC3PARAMLikeTest<generic::NSEC3>::getWireFilePrefix() {
+    return ("rdata_nsec3_");
+}
+
+template <>
+string
+NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getWireFilePrefix() {
+    return ("rdata_nsec3param_");
+}
+
+template <>
+string
+NSEC3PARAMLikeTest<generic::NSEC3>::getCommonText() {
+    // next hash + RR type bitmap
+    return (" H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
+            "NS SOA RRSIG DNSKEY NSEC3PARAM");
+}
+
+template <>
+string
+NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getCommonText() {
+    // there's no more text for NSEC3PARAM
+    return ("");
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, fromText) {
+    // Numeric parameters have possible maximum values.  Unusual, but must
+    // be accepted.
+    EXPECT_NO_THROW(this->fromText("255 255 65535 D399EAAB" +
+                                   this->getCommonText()));
+
+    // 0-length salt
+    EXPECT_EQ(0, this->fromText(this->nosalt_txt).getSalt().size());
+
+    // salt that has the possible max length
+    EXPECT_EQ(255, this->fromText("1 1 1 " + string(255 * 2, '0') +
+                                  this->getCommonText()).getSalt().size());
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, badText) {
+    // Bad salt hex
+    EXPECT_THROW(this->fromText("1 1 1 SPORK0" + this->getCommonText()),
+                 isc::BadValue);
+    EXPECT_THROW(this->fromText("1 1 1 ADDAFEE" + this->getCommonText()),
+                 isc::BadValue);
+
+    // Space within salt
+    EXPECT_THROW(this->fromText("1 1 1 ADDAFE ADDAFEEE" +
+                                this->getCommonText()),
+                 InvalidRdataText);
+
+    // Similar to empty salt, but not really.  This shouldn't cause confusion.
+    EXPECT_THROW(this->fromText("1 1 1 --" + this->getCommonText()),
+                 isc::BadValue);
+
+    // Too large algorithm
+    EXPECT_THROW(this->fromText("1000000 1 1 ADDAFEEE" + this->getCommonText()),
+                 InvalidRdataText);
+
+    // Too large flags
+    EXPECT_THROW(this->fromText("1 1000000 1 ADDAFEEE" + this->getCommonText()),
+                 InvalidRdataText);
+
+    // Too large iterations
+    EXPECT_THROW(this->fromText("1 1 65536 ADDAFEEE" + this->getCommonText()),
+                 InvalidRdataText);
+
+    // There should be a space between "1" and "D399EAAB" (salt)
+    EXPECT_THROW(this->fromText("1 1 1D399EAAB" + this->getCommonText()),
+                 InvalidRdataText);
+
+    // Salt is too long (possible max + 1 bytes)
+    EXPECT_THROW(this->fromText("1 1 1 " + string(256 * 2, '0') +
+                                this->getCommonText()),
+                 InvalidRdataText);
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, toText) {
+    // normal case
+    EXPECT_EQ(this->salt_txt, this->fromText(this->salt_txt).toText());
+
+    // empty salt case
+    EXPECT_EQ(this->nosalt_txt, this->fromText(this->nosalt_txt).toText());
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, createFromWire) {
+    // Normal case
+    EXPECT_EQ(0, this->fromText(this->salt_txt).compare(
+                  *this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+                                              (this->getWireFilePrefix() +
+                                               "fromWire1").c_str())));
+
+    // Too short RDLENGTH: it doesn't even contain the first 5 octets.
+    EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+                                            (this->getWireFilePrefix() +
+                                             "fromWire2.wire").c_str()),
+                 DNSMessageFORMERR);
+
+    // salt length is too large
+    EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+                                            (this->getWireFilePrefix() +
+                                             "fromWire11.wire").c_str()),
+                 DNSMessageFORMERR);
+
+    // empty salt.  not so usual, but valid.
+    ConstRdataPtr rdata =
+        this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+                                   (this->getWireFilePrefix() +
+                                    "fromWire13.wire").c_str());
+    EXPECT_EQ(0, this->convert(*rdata).getSalt().size());
+}
+
+template <typename OUTPUT_TYPE>
+void
+toWireCheck(RRType rrtype, OUTPUT_TYPE& output, const string& data_file) {
+    vector<uint8_t> data;
+    UnitTestUtil::readWireData(data_file.c_str(), data);
+    InputBuffer buffer(&data[0], data.size());
+    const uint16_t rdlen = buffer.readUint16();
+
+    output.clear();
+    output.writeUint16(rdlen);
+    createRdata(rrtype, RRClass::IN(), buffer, rdlen)->toWire(output);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, output.getData(),
+                        output.getLength(), &data[0], data.size());
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, toWire) {
+    // normal case
+    toWireCheck(this->getType(), this->renderer,
+                this->getWireFilePrefix() + "fromWire1");
+    toWireCheck(this->getType(), this->obuffer,
+                this->getWireFilePrefix() + "fromWire1");
+
+    // empty salt
+    toWireCheck(this->getType(), this->renderer,
+                this->getWireFilePrefix() + "fromWire13.wire");
+    toWireCheck(this->getType(), this->obuffer,
+                this->getWireFilePrefix() + "fromWire13.wire");
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, compare) {
+    // test RDATAs, sorted in the ascendent order.
+    this->compare_set.push_back(this->fromText("0 0 0 D399EAAB" +
+                                               this->getCommonText()));
+    this->compare_set.push_back(this->fromText("1 0 0 D399EAAB" +
+                                               this->getCommonText()));
+    this->compare_set.push_back(this->fromText("1 1 0 D399EAAB" +
+                                               this->getCommonText()));
+    this->compare_set.push_back(this->fromText("1 1 1 -" +
+                                               this->getCommonText()));
+    this->compare_set.push_back(this->fromText("1 1 1 D399EAAB" +
+                                               this->getCommonText()));
+    this->compare_set.push_back(this->fromText("1 1 1 FF99EAAB" +
+                                               this->getCommonText()));
+    this->compare_set.push_back(this->fromText("1 1 1 FF99EA0000" +
+                                               this->getCommonText()));
+
+    this->compareCheck();
+}
+
+}
diff --git a/src/lib/dns/tests/rdata_nsec3param_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
index bcbc0d7..90313d0 100644
--- a/src/lib/dns/tests/rdata_nsec3param_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
@@ -41,14 +41,14 @@ using namespace isc::dns::rdata;
 namespace {
 class Rdata_NSEC3PARAM_Test : public RdataTest {
 public:
-    Rdata_NSEC3PARAM_Test() : nsec3param_txt("1 0 1 D399EAAB") {}
+    Rdata_NSEC3PARAM_Test() : nsec3param_txt("1 1 1 D399EAAB") {}
     const string nsec3param_txt;
 };
 
 TEST_F(Rdata_NSEC3PARAM_Test, fromText) {
     // With a salt
     EXPECT_EQ(1, generic::NSEC3PARAM(nsec3param_txt).getHashalg());
-    EXPECT_EQ(0, generic::NSEC3PARAM(nsec3param_txt).getFlags());
+    EXPECT_EQ(1, generic::NSEC3PARAM(nsec3param_txt).getFlags());
     // (salt is checked in the toText test)
 
     // With an empty salt
@@ -61,16 +61,9 @@ TEST_F(Rdata_NSEC3PARAM_Test, toText) {
 }
 
 TEST_F(Rdata_NSEC3PARAM_Test, badText) {
-    EXPECT_THROW(generic::NSEC3PARAM("1 1 1 SPORK"), BadValue); // bad hex
-    EXPECT_THROW(generic::NSEC3PARAM("100000 1 1 ADDAFEE"), InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3PARAM("1 100000 1 ADDAFEE"), InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3PARAM("1 1 100000 ADDAFEE"), InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3PARAM("1"), InvalidRdataText);
-}
-
-TEST_F(Rdata_NSEC3PARAM_Test, DISABLED_badText) {
-    // this currently fails
-    EXPECT_THROW(generic::NSEC3PARAM("1 0 1D399EAAB"), InvalidRdataText);
+    // garbage space at the end
+    EXPECT_THROW(generic::NSEC3PARAM("1 1 1 D399EAAB "),
+                 InvalidRdataText);
 }
 
 TEST_F(Rdata_NSEC3PARAM_Test, createFromWire) {
@@ -78,6 +71,19 @@ TEST_F(Rdata_NSEC3PARAM_Test, createFromWire) {
     EXPECT_EQ(0, rdata_nsec3param.compare(
                   *rdataFactoryFromFile(RRType::NSEC3PARAM(), RRClass::IN(),
                                        "rdata_nsec3param_fromWire1")));
+
+    // Short buffer cases.  The data is valid NSEC3PARAM RDATA, but the buffer
+    // is trimmed at the end.  All cases should result in an exception from
+    // the buffer class.
+    vector<uint8_t> data;
+    UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data);
+    const uint16_t rdlen = (data.at(0) << 8) + data.at(1);
+    for (int i = 0; i < rdlen; ++i) {
+        // intentionally construct a short buffer
+        InputBuffer b(&data[0] + 2, i);
+        EXPECT_THROW(createRdata(RRType::NSEC3PARAM(), RRClass::IN(), b, 9),
+                     InvalidBufferPosition);
+    }
 }
 
 TEST_F(Rdata_NSEC3PARAM_Test, toWireRenderer) {
@@ -103,4 +109,16 @@ TEST_F(Rdata_NSEC3PARAM_Test, assign) {
     EXPECT_EQ(0, rdata_nsec3param.compare(other_nsec3param));
 }
 
+TEST_F(Rdata_NSEC3PARAM_Test, compare) {
+    // trivial case: self equivalence
+    EXPECT_EQ(0, generic::NSEC3PARAM(nsec3param_txt).
+              compare(generic::NSEC3PARAM(nsec3param_txt)));
+    EXPECT_EQ(0, generic::NSEC3PARAM("1 1 1 -").
+              compare(generic::NSEC3PARAM("1 1 1 -")));
+
+    // comparison attempt between incompatible RR types should be rejected
+    EXPECT_THROW(generic::NSEC3PARAM(nsec3param_txt).compare(*rdata_nomatch),
+                 bad_cast);
+}
+
 }
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index ae1bcc9..fdf1025 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -30,6 +30,9 @@ BUILT_SOURCES += rdata_nsec3_fromWire10.wire rdata_nsec3_fromWire11.wire
 BUILT_SOURCES += rdata_nsec3_fromWire12.wire rdata_nsec3_fromWire13.wire
 BUILT_SOURCES += rdata_nsec3_fromWire14.wire rdata_nsec3_fromWire15.wire
 BUILT_SOURCES += rdata_nsec3_fromWire16.wire rdata_nsec3_fromWire17.wire
+BUILT_SOURCES += rdata_nsec3param_fromWire2.wire
+BUILT_SOURCES += rdata_nsec3param_fromWire11.wire
+BUILT_SOURCES += rdata_nsec3param_fromWire13.wire
 BUILT_SOURCES += rdata_rrsig_fromWire2.wire
 BUILT_SOURCES += rdata_minfo_fromWire1.wire rdata_minfo_fromWire2.wire
 BUILT_SOURCES += rdata_minfo_fromWire3.wire rdata_minfo_fromWire4.wire
@@ -104,6 +107,9 @@ EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec
 EXTRA_DIST += rdata_nsec_fromWire10.spec
 EXTRA_DIST += rdata_nsec_fromWire16.spec
 EXTRA_DIST += rdata_nsec3param_fromWire1
+EXTRA_DIST += rdata_nsec3param_fromWire2.spec
+EXTRA_DIST += rdata_nsec3param_fromWire11.spec
+EXTRA_DIST += rdata_nsec3param_fromWire13.spec
 EXTRA_DIST += rdata_nsec3_fromWire1
 EXTRA_DIST += rdata_nsec3_fromWire2.spec rdata_nsec3_fromWire3
 EXTRA_DIST += rdata_nsec3_fromWire4.spec rdata_nsec3_fromWire5.spec
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1
index 3f99f9d..1b8697f 100644
--- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1
+++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1
@@ -1,5 +1,5 @@
 # RDLENGTH, 9 bytes
 00 09
 # NSEC3PARAM record
-# 1 0 1 D399EAAB
-01 00 00 01 04 d3 99 ea ab
+# 1 1 1 D399EAAB
+01 01 00 01 04 d3 99 ea ab
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec
new file mode 100644
index 0000000..41e1784
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec
@@ -0,0 +1,8 @@
+#
+# An invalid NSEC3PARAM RDATA: Saltlen is too large
+#
+
+[custom]
+sections: nsec3param
+[nsec3param]
+rdlen: 7
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec
new file mode 100644
index 0000000..311b2dd
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec
@@ -0,0 +1,9 @@
+#
+# A valid (but unusual) NSEC3PARAM RDATA: salt is empty.
+#
+
+[custom]
+sections: nsec3param
+[nsec3param]
+saltlen: 0
+salt: ''
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec
new file mode 100644
index 0000000..bce65ff
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec
@@ -0,0 +1,9 @@
+#
+# A malformed NSEC3PARAM RDATA: RDLEN indicates it doesn't even contain the
+# fixed 5 octects
+#
+
+[custom]
+sections: nsec3param
+[nsec3param]
+rdlen: 4
diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in
index 8bd2b3c..8b3eac0 100755
--- a/src/lib/util/python/gen_wiredata.py.in
+++ b/src/lib/util/python/gen_wiredata.py.in
@@ -949,12 +949,11 @@ class NSEC(NSECBASE):
                                                  int(len(name_wire) / 2)))
         f.write('%s\n' % name_wire)
 
-class NSEC3(NSECBASE):
-    '''Implements rendering NSEC3 RDATA in the test data format.
+class NSEC3PARAM(RR):
+    '''Implements rendering NSEC3PARAM RDATA in the test data format.
 
     Configurable parameters are as follows (see the description of the
     same name of attribute for the default value):
-    - Type bitmap related parameters: see class NSECBASE
     - hashalg (8-bit int): The Hash Algorithm field.  Note that
       currently the only defined algorithm is SHA-1, for which a value
       of 1 will be used, and it's the default.  So this implementation
@@ -967,9 +966,6 @@ class NSEC3(NSECBASE):
     - saltlen (int): The Salt Length field.
     - salt (string): The Salt field.  It is converted to a sequence of
       ascii codes and its hexadecimal representation will be used.
-    - hashlen (int): The Hash Length field.
-    - hash (string): The Next Hashed Owner Name field.  This parameter
-      is interpreted as "salt".
     '''
 
     hashalg = 1                 # SHA-1
@@ -978,15 +974,18 @@ class NSEC3(NSECBASE):
     iterations = 1
     saltlen = 5
     salt = 's' * saltlen
-    hashlen = 20
-    hash = 'h' * hashlen
-    def dump_fixedpart(self, f, bitmap_totallen):
+
+    def dump(self, f):
         if self.rdlen is None:
-            # if rdlen needs to be calculated, it must be based on the bitmap
-            # length, because the configured maplen can be fake.
-            self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
-                + bitmap_totallen
+            self.rdlen = 4 + 1 + len(self.salt)
         self.dump_header(f, self.rdlen)
+        self._dump_params(f)
+
+    def _dump_params(self, f):
+        '''This method is intended to be shared with NSEC3 class.
+
+        '''
+
         optout_val = 1 if self.optout else 0
         f.write('# Hash Alg=%s, Opt-Out=%d, Other Flags=%0x, Iterations=%d\n' %
                 (code_totext(self.hashalg, rdict_nsec3_algorithm),
@@ -997,6 +996,29 @@ class NSEC3(NSECBASE):
         f.write('%02x%s%s\n' % (self.saltlen,
                                 ' ' if len(self.salt) > 0 else '',
                                 encode_string(self.salt)))
+
+class NSEC3(NSECBASE, NSEC3PARAM):
+    '''Implements rendering NSEC3 RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - Type bitmap related parameters: see class NSECBASE
+    - Hash parameter related parameters: see class NSEC3PARAM
+    - hashlen (int): The Hash Length field.
+    - hash (string): The Next Hashed Owner Name field.  This parameter
+      is interpreted as "salt".
+    '''
+
+    hashlen = 20
+    hash = 'h' * hashlen
+    def dump_fixedpart(self, f, bitmap_totallen):
+        if self.rdlen is None:
+            # if rdlen needs to be calculated, it must be based on the bitmap
+            # length, because the configured maplen can be fake.
+            self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
+                + bitmap_totallen
+        self.dump_header(f, self.rdlen)
+        self._dump_params(f)
         f.write("# Hash Len=%d, Hash='%s'\n" % (self.hashlen, self.hash))
         f.write('%02x%s%s\n' % (self.hashlen,
                                 ' ' if len(self.hash) > 0 else '',




More information about the bind10-changes mailing list