Browse Source

[master] Merge branch 'trac2282merge'

JINMEI Tatuya 12 years ago
parent
commit
c0672d18dc
72 changed files with 1430 additions and 581 deletions
  1. 2 2
      configure.ac
  2. 0 6
      src/bin/auth/Makefile.am
  3. 0 6
      src/bin/auth/benchmarks/Makefile.am
  4. 0 7
      src/bin/auth/tests/Makefile.am
  5. 7 3
      src/bin/auth/tests/auth_srv_unittest.cc
  6. 2 1
      src/lib/datasrc/Makefile.am
  7. 45 22
      src/lib/datasrc/client_list.cc
  8. 43 15
      src/lib/datasrc/client_list.h
  9. 1 1
      src/lib/datasrc/memory/Makefile.am
  10. 95 21
      src/lib/datasrc/memory/domaintree.h
  11. 32 24
      src/lib/datasrc/memory/memory_client.cc
  12. 11 12
      src/lib/datasrc/memory/memory_client.h
  13. 22 17
      src/lib/datasrc/memory/treenode_rrset.cc
  14. 0 2
      src/lib/datasrc/memory/treenode_rrset.h
  15. 421 59
      src/lib/datasrc/memory/zone_finder.cc
  16. 10 28
      src/lib/datasrc/memory/zone_finder.h
  17. 1 1
      src/lib/datasrc/tests/Makefile.am
  18. 91 60
      src/lib/datasrc/tests/client_list_unittest.cc
  19. 17 0
      src/lib/datasrc/tests/faked_nsec3.cc
  20. 4 0
      src/lib/datasrc/tests/faked_nsec3.h
  21. 0 0
      src/lib/datasrc/tests/memory/.gitignore
  22. 1 2
      src/lib/datasrc/memory/tests/Makefile.am
  23. 62 0
      src/lib/datasrc/memory/tests/domaintree_unittest.cc
  24. 43 33
      src/lib/datasrc/memory/tests/memory_client_unittest.cc
  25. 0 0
      src/lib/datasrc/tests/memory/memory_segment_test.h
  26. 1 1
      src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
  27. 0 0
      src/lib/datasrc/tests/memory/rdataset_unittest.cc
  28. 0 0
      src/lib/datasrc/tests/memory/run_unittests.cc
  29. 0 0
      src/lib/datasrc/tests/memory/segment_object_holder_unittest.cc
  30. 1 0
      src/lib/datasrc/memory/tests/testdata/Makefile.am
  31. 0 0
      src/lib/datasrc/tests/memory/testdata/empty.zone
  32. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-broken1.zone
  33. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-broken2.zone
  34. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-1.zone
  35. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-2.zone
  36. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-1.zone
  37. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-2.zone
  38. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-1.zone
  39. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-2.zone
  40. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type-bad.zone
  41. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type.zone
  42. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-empty.zone
  43. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-multiple-cname.zone
  44. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-multiple-dname.zone
  45. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3.zone
  46. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3param.zone
  47. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-multiple.zone
  48. 14 0
      src/lib/datasrc/tests/memory/testdata/example.org-nsec3-empty-salt.zone
  49. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-nsec3-fewer-labels.zone
  50. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-nsec3-more-labels.zone
  51. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed-no-param.zone
  52. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed.zone
  53. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-out-of-zone.zone
  54. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-rrsig-follows-nothing.zone
  55. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-rrsigs.zone
  56. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-wildcard-dname.zone
  57. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-wildcard-ns.zone
  58. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org-wildcard-nsec3.zone
  59. 0 0
      src/lib/datasrc/tests/memory/testdata/example.org.zone
  60. 49 9
      src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
  61. 0 0
      src/lib/datasrc/tests/memory/zone_data_unittest.cc
  62. 236 113
      src/lib/datasrc/memory/tests/zone_finder_unittest.cc
  63. 0 0
      src/lib/datasrc/tests/memory/zone_table_unittest.cc
  64. 0 66
      src/lib/datasrc/tests/memory_datasrc_unittest.cc
  65. 4 1
      src/lib/datasrc/tests/testdata/contexttest.zone
  66. 27 12
      src/lib/datasrc/tests/zone_finder_context_unittest.cc
  67. 1 2
      src/lib/dns/labelsequence.cc
  68. 47 11
      src/lib/dns/nsec3hash.cc
  69. 32 2
      src/lib/dns/nsec3hash.h
  70. 33 0
      src/lib/dns/tests/labelsequence_unittest.cc
  71. 23 1
      src/lib/dns/tests/nsec3hash_unittest.cc
  72. 52 41
      src/lib/python/isc/datasrc/tests/clientlist_test.py

+ 2 - 2
configure.ac

@@ -1193,11 +1193,11 @@ AC_CONFIG_FILES([Makefile
                  src/lib/exceptions/tests/Makefile
                  src/lib/exceptions/tests/Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/memory/Makefile
                  src/lib/datasrc/memory/Makefile
-                 src/lib/datasrc/memory/tests/Makefile
-                 src/lib/datasrc/memory/tests/testdata/Makefile
                  src/lib/datasrc/memory/benchmarks/Makefile
                  src/lib/datasrc/memory/benchmarks/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/datasrc/tests/testdata/Makefile
                  src/lib/datasrc/tests/testdata/Makefile
+                 src/lib/datasrc/tests/memory/Makefile
+                 src/lib/datasrc/tests/memory/testdata/Makefile
                  src/lib/xfr/Makefile
                  src/lib/xfr/Makefile
                  src/lib/xfr/tests/Makefile
                  src/lib/xfr/tests/Makefile
                  src/lib/log/Makefile
                  src/lib/log/Makefile

+ 0 - 6
src/bin/auth/Makefile.am

@@ -57,12 +57,6 @@ b10_auth_SOURCES += common.h common.cc
 b10_auth_SOURCES += statistics.cc statistics.h
 b10_auth_SOURCES += statistics.cc statistics.h
 b10_auth_SOURCES += datasrc_configurator.h
 b10_auth_SOURCES += datasrc_configurator.h
 b10_auth_SOURCES += main.cc
 b10_auth_SOURCES += main.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. This should've been moot after #1207, but there is still
-# one dependency; the in-memory-specific zone loader call is still in
-# auth.
-b10_auth_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
 
 
 nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
 nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
 EXTRA_DIST += auth_messages.mes
 EXTRA_DIST += auth_messages.mes

+ 0 - 6
src/bin/auth/benchmarks/Makefile.am

@@ -17,12 +17,6 @@ query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
 query_bench_SOURCES += ../auth_config.h ../auth_config.cc
 query_bench_SOURCES += ../auth_config.h ../auth_config.cc
 query_bench_SOURCES += ../statistics.h ../statistics.cc
 query_bench_SOURCES += ../statistics.h ../statistics.cc
 query_bench_SOURCES += ../auth_log.h ../auth_log.cc
 query_bench_SOURCES += ../auth_log.h ../auth_log.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. When #1207 is done this becomes moot anyway, and the
-# specific workaround is not needed anymore, so we can then remove this
-# line again.
-query_bench_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
 
 
 nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc
 nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc
 
 

+ 0 - 7
src/bin/auth/tests/Makefile.am

@@ -52,13 +52,6 @@ run_unittests_SOURCES += query_unittest.cc
 run_unittests_SOURCES += statistics_unittest.cc
 run_unittests_SOURCES += statistics_unittest.cc
 run_unittests_SOURCES += datasrc_configurator_unittest.cc
 run_unittests_SOURCES += datasrc_configurator_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_SOURCES += run_unittests.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. This should've been moot after #1207, but there is still
-# one dependency; the in-memory-specific zone loader call is still in
-# auth.
-run_unittests_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
-
 
 
 nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc
 nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc
 
 

+ 7 - 3
src/bin/auth/tests/auth_srv_unittest.cc

@@ -15,6 +15,7 @@
 #include <config.h>
 #include <config.h>
 
 
 #include <util/io/sockaddr_util.h>
 #include <util/io/sockaddr_util.h>
+#include <util/memory_segment_local.h>
 
 
 #include <dns/message.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
@@ -1393,16 +1394,19 @@ public:
              const isc::datasrc::DataSourceClientPtr
              const isc::datasrc::DataSourceClientPtr
                  client(new FakeClient(info.data_src_client_ != NULL ?
                  client(new FakeClient(info.data_src_client_ != NULL ?
                                        info.data_src_client_ :
                                        info.data_src_client_ :
-                                       info.cache_.get(),
+                                       info.getCacheClient(),
                                        throw_when, isc_exception, fake_rrset));
                                        throw_when, isc_exception, fake_rrset));
              clients_.push_back(client);
              clients_.push_back(client);
-             data_sources_.push_back(DataSourceInfo(client.get(),
-                 isc::datasrc::DataSourceClientContainerPtr(), false));
+             data_sources_.push_back(
+                 DataSourceInfo(client.get(),
+                                isc::datasrc::DataSourceClientContainerPtr(),
+                                false, RRClass::IN(), mem_sgmt_));
         }
         }
     }
     }
 private:
 private:
     const boost::shared_ptr<isc::datasrc::ConfigurableClientList> real_;
     const boost::shared_ptr<isc::datasrc::ConfigurableClientList> real_;
     vector<isc::datasrc::DataSourceClientPtr> clients_;
     vector<isc::datasrc::DataSourceClientPtr> clients_;
+    MemorySegmentLocal mem_sgmt_;
 };
 };
 
 
 } // end anonymous namespace for throwing proxy classes
 } // end anonymous namespace for throwing proxy classes

+ 2 - 1
src/lib/datasrc/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = . memory tests
+SUBDIRS = memory . tests
 
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -60,6 +60,7 @@ libb10_datasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+libb10_datasrc_la_LIBADD += $(builddir)/memory/libdatasrc_memory.la
 libb10_datasrc_la_LIBADD += $(SQLITE_LIBS)
 libb10_datasrc_la_LIBADD += $(SQLITE_LIBS)
 
 
 BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc
 BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc

+ 45 - 22
src/lib/datasrc/client_list.cc

@@ -12,10 +12,12 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+#include <util/memory_segment_local.h>
+
 #include "client_list.h"
 #include "client_list.h"
 #include "client.h"
 #include "client.h"
 #include "factory.h"
 #include "factory.h"
-#include "memory_datasrc.h"
+#include "memory/memory_client.h"
 #include "logger.h"
 #include "logger.h"
 #include <dns/masterload.h>
 #include <dns/masterload.h>
 
 
@@ -25,32 +27,58 @@
 using namespace isc::data;
 using namespace isc::data;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace std;
 using namespace std;
+using isc::util::MemorySegment;
 using boost::lexical_cast;
 using boost::lexical_cast;
 using boost::shared_ptr;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
 using boost::dynamic_pointer_cast;
+using isc::datasrc::memory::InMemoryClient;
 
 
 namespace isc {
 namespace isc {
 namespace datasrc {
 namespace datasrc {
 
 
 ConfigurableClientList::DataSourceInfo::DataSourceInfo(
 ConfigurableClientList::DataSourceInfo::DataSourceInfo(
     DataSourceClient* data_src_client,
     DataSourceClient* data_src_client,
-    const DataSourceClientContainerPtr& container, bool has_cache) :
+    const DataSourceClientContainerPtr& container, bool has_cache,
+    const RRClass& rrclass, MemorySegment& mem_sgmt) :
     data_src_client_(data_src_client),
     data_src_client_(data_src_client),
     container_(container)
     container_(container)
 {
 {
     if (has_cache) {
     if (has_cache) {
-        cache_.reset(new InMemoryClient);
+        cache_.reset(new InMemoryClient(mem_sgmt, rrclass));
     }
     }
 }
 }
 
 
-ConfigurableClientList::DataSourceInfo::DataSourceInfo(bool has_cache) :
+ConfigurableClientList::DataSourceInfo::DataSourceInfo(
+    const RRClass& rrclass, MemorySegment& mem_sgmt, bool has_cache) :
     data_src_client_(NULL)
     data_src_client_(NULL)
 {
 {
     if (has_cache) {
     if (has_cache) {
-        cache_.reset(new InMemoryClient);
+        cache_.reset(new InMemoryClient(mem_sgmt, rrclass));
     }
     }
 }
 }
 
 
+const DataSourceClient*
+ConfigurableClientList::DataSourceInfo::getCacheClient() const {
+    return (cache_.get());
+}
+
+ConfigurableClientList::ConfigurableClientList(const RRClass& rrclass) :
+    rrclass_(rrclass),
+    mem_sgmt_(new util::MemorySegmentLocal),
+    configuration_(new isc::data::ListElement),
+    allow_cache_(false)
+{}
+
+ConfigurableClientList::~ConfigurableClientList() {
+    // Explicitly clear the contained data source clients, and check memory
+    // leak.  assert() (with abort on failure) may be too harsh, but
+    // it's probably better to find more leaks initially.  Once it's stabilized
+    // we should probably revisit it.
+
+    data_sources_.clear();
+    assert(mem_sgmt_->allMemoryDeallocated());
+}
+
 void
 void
 ConfigurableClientList::configure(const ConstElementPtr& config,
 ConfigurableClientList::configure(const ConstElementPtr& config,
                                   bool allow_cache)
                                   bool allow_cache)
@@ -98,14 +126,16 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                     isc_throw(ConfigurationError, "The cache must be enabled "
                     isc_throw(ConfigurationError, "The cache must be enabled "
                               "for the MasterFiles type");
                               "for the MasterFiles type");
                 }
                 }
-                new_data_sources.push_back(DataSourceInfo(true));
+                new_data_sources.push_back(DataSourceInfo(rrclass_, *mem_sgmt_,
+                                                          true));
             } else {
             } else {
                 // Ask the factory to create the data source for us
                 // Ask the factory to create the data source for us
                 const DataSourcePair ds(this->getDataSourceClient(type,
                 const DataSourcePair ds(this->getDataSourceClient(type,
                                                                   paramConf));
                                                                   paramConf));
                 // And put it into the vector
                 // And put it into the vector
                 new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
                 new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
-                                                          want_cache));
+                                                          want_cache, rrclass_,
+                                                          *mem_sgmt_));
             }
             }
 
 
             if (want_cache) {
             if (want_cache) {
@@ -141,13 +171,10 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                 for (vector<string>::const_iterator it(zones_origins.begin());
                 for (vector<string>::const_iterator it(zones_origins.begin());
                      it != zones_origins.end(); ++it) {
                      it != zones_origins.end(); ++it) {
                     const Name origin(*it);
                     const Name origin(*it);
-                    shared_ptr<InMemoryZoneFinder>
-                        finder(new
-                            InMemoryZoneFinder(rrclass_, origin));
                     if (type == "MasterFiles") {
                     if (type == "MasterFiles") {
                         try {
                         try {
-                            finder->load(paramConf->get(*it)->stringValue());
-                            cache->addZone(finder);
+                            cache->load(origin,
+                                        paramConf->get(*it)->stringValue());
                         } catch (const isc::dns::MasterLoadError& mle) {
                         } catch (const isc::dns::MasterLoadError& mle) {
                             LOG_ERROR(logger, DATASRC_MASTERLOAD_ERROR)
                             LOG_ERROR(logger, DATASRC_MASTERLOAD_ERROR)
                                 .arg(mle.what());
                                 .arg(mle.what());
@@ -165,8 +192,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                             isc_throw(isc::Unexpected, "Got NULL iterator "
                             isc_throw(isc::Unexpected, "Got NULL iterator "
                                       "for zone " << origin);
                                       "for zone " << origin);
                         }
                         }
-                        finder->load(*iterator);
-                        cache->addZone(finder);
+                        cache->load(origin, *iterator);
                     }
                     }
                 }
                 }
             }
             }
@@ -324,14 +350,11 @@ ConfigurableClientList::reload(const Name& name) {
     }
     }
     // Try to convert the finder to in-memory one. If it is the cache,
     // Try to convert the finder to in-memory one. If it is the cache,
     // it should work.
     // it should work.
-    shared_ptr<InMemoryZoneFinder>
-        finder(dynamic_pointer_cast<InMemoryZoneFinder>(result.finder));
-    const DataSourceInfo* info(result.info);
     // It is of a different type or there's no cache.
     // It is of a different type or there's no cache.
-    if (!info->cache_ || !finder) {
+    if (!result.info->cache_) {
         return (ZONE_NOT_CACHED);
         return (ZONE_NOT_CACHED);
     }
     }
-    DataSourceClient* client(info->data_src_client_);
+    DataSourceClient* client(result.info->data_src_client_);
     if (client) {
     if (client) {
         // Now do the final reload. If it does not exist in client,
         // Now do the final reload. If it does not exist in client,
         // DataSourceError is thrown, which is exactly the result what we
         // DataSourceError is thrown, which is exactly the result what we
@@ -340,15 +363,15 @@ ConfigurableClientList::reload(const Name& name) {
         if (!iterator) {
         if (!iterator) {
             isc_throw(isc::Unexpected, "Null iterator from " << name);
             isc_throw(isc::Unexpected, "Null iterator from " << name);
         }
         }
-        finder->load(*iterator);
+        result.info->cache_->load(name, *iterator);
     } else {
     } else {
         // The MasterFiles special case
         // The MasterFiles special case
-        const string filename(finder->getFileName());
+        const string filename(result.info->cache_->getFileName(name));
         if (filename.empty()) {
         if (filename.empty()) {
             isc_throw(isc::Unexpected, "Confused about missing both filename "
             isc_throw(isc::Unexpected, "Confused about missing both filename "
                       "and data source");
                       "and data source");
         }
         }
-        finder->load(filename);
+        result.info->cache_->load(name, filename);
     }
     }
     return (ZONE_RELOADED);
     return (ZONE_RELOADED);
 }
 }

+ 43 - 15
src/lib/datasrc/client_list.h

@@ -15,6 +15,8 @@
 #ifndef DATASRC_CONTAINER_H
 #ifndef DATASRC_CONTAINER_H
 #define DATASRC_CONTAINER_H
 #define DATASRC_CONTAINER_H
 
 
+#include <util/memory_segment.h>
+
 #include <dns/name.h>
 #include <dns/name.h>
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
 #include <cc/data.h>
 #include <cc/data.h>
@@ -22,6 +24,7 @@
 
 
 #include <vector>
 #include <vector>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
 
 
 namespace isc {
 namespace isc {
@@ -34,7 +37,13 @@ typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
 class DataSourceClientContainer;
 class DataSourceClientContainer;
 typedef boost::shared_ptr<DataSourceClientContainer>
 typedef boost::shared_ptr<DataSourceClientContainer>
     DataSourceClientContainerPtr;
     DataSourceClientContainerPtr;
+
+// XXX: it's better to even hide the existence of the "memory" namespace.
+// We should probably consider pimpl for details of ConfigurableClientList
+// and hide real definitions except for itself and tests.
+namespace memory {
 class InMemoryClient;
 class InMemoryClient;
+}
 
 
 /// \brief The list of data source clients.
 /// \brief The list of data source clients.
 ///
 ///
@@ -209,11 +218,11 @@ public:
     /// \brief Constructor
     /// \brief Constructor
     ///
     ///
     /// \param rrclass For which class the list should work.
     /// \param rrclass For which class the list should work.
-    ConfigurableClientList(const isc::dns::RRClass &rrclass) :
-        rrclass_(rrclass),
-        configuration_(new isc::data::ListElement),
-        allow_cache_(false)
-    {}
+    ConfigurableClientList(const isc::dns::RRClass& rrclass);
+
+    /// \brief Destructor
+    virtual ~ConfigurableClientList();
+
     /// \brief Exception thrown when there's an error in configuration.
     /// \brief Exception thrown when there's an error in configuration.
     class ConfigurationError : public Exception {
     class ConfigurationError : public Exception {
     public:
     public:
@@ -290,24 +299,27 @@ public:
     /// \todo The content yet to be defined.
     /// \todo The content yet to be defined.
     struct DataSourceInfo {
     struct DataSourceInfo {
         // Plays a role of default constructor too (for vector)
         // Plays a role of default constructor too (for vector)
-        DataSourceInfo(bool has_cache = false);
+        DataSourceInfo(const dns::RRClass& rrclass,
+                       util::MemorySegment& mem_sgmt,
+                       bool has_cache = false);
         DataSourceInfo(DataSourceClient* data_src_client,
         DataSourceInfo(DataSourceClient* data_src_client,
                        const DataSourceClientContainerPtr& container,
                        const DataSourceClientContainerPtr& container,
-                       bool has_cache);
+                       bool has_cache, const dns::RRClass& rrclass,
+                       util::MemorySegment& mem_sgmt);
         DataSourceClient* data_src_client_;
         DataSourceClient* data_src_client_;
         DataSourceClientContainerPtr container_;
         DataSourceClientContainerPtr container_;
-        boost::shared_ptr<InMemoryClient> cache_;
+
+        // Accessor to cache_ in the form of DataSourceClient, hiding
+        // the existence of InMemoryClient as much as possible.  We should
+        // really consider cleaner abstraction, but for now it works.
+        // This is also only intended to be used in auth unit tests right now.
+        // No other applications or tests may use it.
+        const DataSourceClient* getCacheClient() const;
+        boost::shared_ptr<memory::InMemoryClient> cache_;
     };
     };
 
 
     /// \brief The collection of data sources.
     /// \brief The collection of data sources.
     typedef std::vector<DataSourceInfo> DataSources;
     typedef std::vector<DataSourceInfo> DataSources;
-protected:
-    /// \brief The data sources held here.
-    ///
-    /// All our data sources are stored here. It is protected to let the
-    /// tests in. You should consider it private if you ever want to
-    /// derive this class (which is not really recommended anyway).
-    DataSources data_sources_;
 
 
     /// \brief Convenience type alias.
     /// \brief Convenience type alias.
     ///
     ///
@@ -357,10 +369,26 @@ private:
     void findInternal(MutableResult& result, const dns::Name& name,
     void findInternal(MutableResult& result, const dns::Name& name,
                       bool want_exact_match, bool want_finder) const;
                       bool want_exact_match, bool want_finder) const;
     const isc::dns::RRClass rrclass_;
     const isc::dns::RRClass rrclass_;
+
+    /// \brief Memory segment for in-memory cache.
+    ///
+    /// Note that this must be placed before data_sources_ so it won't be
+    /// destroyed before the built objects in the destructor.
+    boost::scoped_ptr<util::MemorySegment> mem_sgmt_;
+
     /// \brief Currently active configuration.
     /// \brief Currently active configuration.
     isc::data::ConstElementPtr configuration_;
     isc::data::ConstElementPtr configuration_;
+
     /// \brief The last set value of allow_cache.
     /// \brief The last set value of allow_cache.
     bool allow_cache_;
     bool allow_cache_;
+
+protected:
+    /// \brief The data sources held here.
+    ///
+    /// All our data sources are stored here. It is protected to let the
+    /// tests in. You should consider it private if you ever want to
+    /// derive this class (which is not really recommended anyway).
+    DataSources data_sources_;
 };
 };
 
 
 } // namespace datasrc
 } // namespace datasrc

+ 1 - 1
src/lib/datasrc/memory/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = . tests benchmarks
+SUBDIRS = . benchmarks
 
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns

+ 95 - 21
src/lib/datasrc/memory/domaintree.h

@@ -373,10 +373,23 @@ private:
         }
         }
     }
     }
 
 
+    /// \brief returns if the node is a subtree's root node
+    ///
+    /// This method takes a node and returns \c true if it is the root
+    /// node of the subtree it belongs to.
+    ///
+    /// This method never throws an exception.
     bool isSubTreeRoot() const {
     bool isSubTreeRoot() const {
         return ((flags_ & FLAG_SUBTREE_ROOT) != 0);
         return ((flags_ & FLAG_SUBTREE_ROOT) != 0);
     }
     }
 
 
+    /// \brief returns the root of its subtree
+    ///
+    /// This method takes a node and returns the root of its subtree.
+    ///
+    /// This method never throws an exception.
+    const DomainTreeNode<T>* getSubTreeRoot() const;
+
 public:
 public:
     /// \brief returns the parent of the root of its subtree
     /// \brief returns the parent of the root of its subtree
     ///
     ///
@@ -388,7 +401,6 @@ public:
     /// This method never throws an exception.
     /// This method never throws an exception.
     const DomainTreeNode<T>* getUpperNode() const;
     const DomainTreeNode<T>* getUpperNode() const;
 
 
-private:
     /// \brief return the next node which is bigger than current node
     /// \brief return the next node which is bigger than current node
     /// in the same subtree
     /// in the same subtree
     ///
     ///
@@ -423,6 +435,7 @@ private:
     /// This method never throws an exception.
     /// This method never throws an exception.
     const DomainTreeNode<T>* predecessor() const;
     const DomainTreeNode<T>* predecessor() const;
 
 
+private:
     /// \brief private shared implementation of successor and predecessor
     /// \brief private shared implementation of successor and predecessor
     ///
     ///
     /// As the two mentioned functions are merely mirror images of each other,
     /// As the two mentioned functions are merely mirror images of each other,
@@ -538,7 +551,7 @@ DomainTreeNode<T>::~DomainTreeNode() {
 
 
 template <typename T>
 template <typename T>
 const DomainTreeNode<T>*
 const DomainTreeNode<T>*
-DomainTreeNode<T>::getUpperNode() const {
+DomainTreeNode<T>::getSubTreeRoot() const {
     const DomainTreeNode<T>* current = this;
     const DomainTreeNode<T>* current = this;
 
 
     // current would never be equal to NULL here (in a correct tree
     // current would never be equal to NULL here (in a correct tree
@@ -547,7 +560,13 @@ DomainTreeNode<T>::getUpperNode() const {
         current = current->getParent();
         current = current->getParent();
     }
     }
 
 
-    return (current->getParent());
+    return (current);
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::getUpperNode() const {
+    return (getSubTreeRoot()->getParent());
 }
 }
 
 
 template <typename T>
 template <typename T>
@@ -555,7 +574,17 @@ isc::dns::LabelSequence
 DomainTreeNode<T>::getAbsoluteLabels(
 DomainTreeNode<T>::getAbsoluteLabels(
     uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
     uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
 {
 {
-    isc::dns::LabelSequence result(getLabels(), buf);
+    // If the current node already has absolute labels, just return it.
+    // This should normally be the case for the origin node if this tree
+    // is used to represent a single DNS zone.
+    const isc::dns::LabelSequence cur_labels(getLabels());
+    if (cur_labels.isAbsolute()) {
+        return (cur_labels);
+    }
+
+    // Otherwise, build the absolute sequence traversing the tree of tree
+    // toward the top root.
+    isc::dns::LabelSequence result(cur_labels, buf);
     const DomainTreeNode<T>* upper = getUpperNode();
     const DomainTreeNode<T>* upper = getUpperNode();
     while (upper != NULL) {
     while (upper != NULL) {
         result.extend(upper->getLabels(), buf);
         result.extend(upper->getLabels(), buf);
@@ -672,14 +701,26 @@ public:
     /// The default constructor.
     /// The default constructor.
     ///
     ///
     /// \exception None
     /// \exception None
-    DomainTreeNodeChain() : node_count_(0), last_compared_(NULL),
+    DomainTreeNodeChain() : level_count_(0), last_compared_(NULL),
                         // XXX: meaningless initial values:
                         // XXX: meaningless initial values:
                         last_comparison_(0, 0,
                         last_comparison_(0, 0,
                                          isc::dns::NameComparisonResult::EQUAL)
                                          isc::dns::NameComparisonResult::EQUAL)
     {}
     {}
 
 
+    /// \brief Copy constructor.
+    ///
+    /// \exception None
+    DomainTreeNodeChain(const DomainTreeNodeChain<T>& other) :
+        level_count_(other.level_count_),
+        last_compared_(other.last_compared_),
+        last_comparison_(other.last_comparison_)
+    {
+        for (size_t i = 0; i < level_count_; i++) {
+            nodes_[i] = other.nodes_[i];
+        }
+    }
+
 private:
 private:
-    DomainTreeNodeChain(const DomainTreeNodeChain<T>&);
     DomainTreeNodeChain<T>& operator=(const DomainTreeNodeChain<T>&);
     DomainTreeNodeChain<T>& operator=(const DomainTreeNodeChain<T>&);
     //@}
     //@}
 
 
@@ -691,7 +732,7 @@ public:
     ///
     ///
     /// \exception None
     /// \exception None
     void clear() {
     void clear() {
-        node_count_ = 0;
+        level_count_ = 0;
         last_compared_ = NULL;
         last_compared_ = NULL;
     }
     }
 
 
@@ -732,7 +773,7 @@ public:
     /// chain, 0 will be returned.
     /// chain, 0 will be returned.
     ///
     ///
     /// \exception None
     /// \exception None
-    unsigned int getLevelCount() const { return (node_count_); }
+    size_t getLevelCount() const { return (level_count_); }
 
 
     /// \brief return the absolute name for the node which this
     /// \brief return the absolute name for the node which this
     /// \c DomainTreeNodeChain currently refers to.
     /// \c DomainTreeNodeChain currently refers to.
@@ -750,11 +791,11 @@ public:
 
 
         const DomainTreeNode<T>* top_node = top();
         const DomainTreeNode<T>* top_node = top();
         isc::dns::Name absolute_name = top_node->getName();
         isc::dns::Name absolute_name = top_node->getName();
-        int node_count = node_count_ - 1;
-        while (node_count > 0) {
-            top_node = nodes_[node_count - 1];
+        size_t level = level_count_ - 1;
+        while (level > 0) {
+            top_node = nodes_[level - 1];
             absolute_name = absolute_name.concatenate(top_node->getName());
             absolute_name = absolute_name.concatenate(top_node->getName());
-            --node_count;
+            --level;
         }
         }
         return (absolute_name);
         return (absolute_name);
     }
     }
@@ -768,7 +809,7 @@ private:
     /// \brief return whether node chain has node in it.
     /// \brief return whether node chain has node in it.
     ///
     ///
     /// \exception None
     /// \exception None
-    bool isEmpty() const { return (node_count_ == 0); }
+    bool isEmpty() const { return (level_count_ == 0); }
 
 
     /// \brief return the top node for the node chain
     /// \brief return the top node for the node chain
     ///
     ///
@@ -778,7 +819,7 @@ private:
     /// \exception None
     /// \exception None
     const DomainTreeNode<T>* top() const {
     const DomainTreeNode<T>* top() const {
         assert(!isEmpty());
         assert(!isEmpty());
-        return (nodes_[node_count_ - 1]);
+        return (nodes_[level_count_ - 1]);
     }
     }
 
 
     /// \brief pop the top node from the node chain
     /// \brief pop the top node from the node chain
@@ -789,7 +830,7 @@ private:
     /// \exception None
     /// \exception None
     void pop() {
     void pop() {
         assert(!isEmpty());
         assert(!isEmpty());
-        --node_count_;
+        --level_count_;
     }
     }
 
 
     /// \brief add the node into the node chain
     /// \brief add the node into the node chain
@@ -800,8 +841,8 @@ private:
     ///
     ///
     /// \exception None
     /// \exception None
     void push(const DomainTreeNode<T>* node) {
     void push(const DomainTreeNode<T>* node) {
-        assert(node_count_ < RBT_MAX_LEVEL);
-        nodes_[node_count_++] = node;
+        assert(level_count_ < RBT_MAX_LEVEL);
+        nodes_[level_count_++] = node;
     }
     }
 
 
 private:
 private:
@@ -810,7 +851,7 @@ private:
     // it's also equal to the possible maximum level.
     // it's also equal to the possible maximum level.
     const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
     const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
 
 
-    int node_count_;
+    size_t level_count_;
     const DomainTreeNode<T>* nodes_[RBT_MAX_LEVEL];
     const DomainTreeNode<T>* nodes_[RBT_MAX_LEVEL];
     const DomainTreeNode<T>* last_compared_;
     const DomainTreeNode<T>* last_compared_;
     isc::dns::NameComparisonResult last_comparison_;
     isc::dns::NameComparisonResult last_comparison_;
@@ -1265,12 +1306,20 @@ public:
     const DomainTreeNode<T>*
     const DomainTreeNode<T>*
     previousNode(DomainTreeNodeChain<T>& node_path) const;
     previousNode(DomainTreeNodeChain<T>& node_path) const;
 
 
+    /// \brief return the largest node in the tree of trees.
+    ///
+    /// \throw none
+    ///
+    /// \return A \c DomainTreeNode that is the largest node in the
+    /// tree. If there are no nodes, then \c NULL is returned.
+    const DomainTreeNode<T>* largestNode() const;
+
     /// \brief Get the total number of nodes in the tree
     /// \brief Get the total number of nodes in the tree
     ///
     ///
     /// It includes nodes internally created as a result of adding a domain
     /// It includes nodes internally created as a result of adding a domain
     /// name that is a subdomain of an existing node of the tree.
     /// name that is a subdomain of an existing node of the tree.
     /// This function is mainly intended to be used for debugging.
     /// This function is mainly intended to be used for debugging.
-    int getNodeCount() const { return (node_count_); }
+    uint32_t getNodeCount() const { return (node_count_); }
 
 
     /// \name Debug function
     /// \name Debug function
     //@{
     //@{
@@ -1402,8 +1451,15 @@ private:
     //@}
     //@}
 
 
     typename DomainTreeNode<T>::DomainTreeNodePtr root_;
     typename DomainTreeNode<T>::DomainTreeNodePtr root_;
-    /// the node count of current tree
-    unsigned int node_count_;
+
+    /// the node count of current tree.
+    ///
+    /// Note: uint32_t may look awkward, but we intentionally choose it so
+    /// that needsReturnEmptyNode_ below won't make cause extra padding
+    /// in 64-bit machines (and we can minimize the total size of this class).
+    /// 2^32 - 1 should be a reasonable max of possible number of nodes.
+    uint32_t node_count_;
+
     /// search policy for domaintree
     /// search policy for domaintree
     const bool needsReturnEmptyNode_;
     const bool needsReturnEmptyNode_;
 };
 };
@@ -1710,6 +1766,24 @@ DomainTree<T>::previousNode(DomainTreeNodeChain<T>& node_path) const {
 }
 }
 
 
 template <typename T>
 template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::largestNode() const {
+    const DomainTreeNode<T>* node = root_.get();
+    while (node != NULL) {
+        // We go right first, then down.
+        if (node->getRight() != NULL) {
+            node = node->getRight();
+        } else if (node->getDown() != NULL) {
+            node = node->getDown();
+        } else {
+            break;
+        }
+    }
+
+    return (node);
+}
+
+template <typename T>
 typename DomainTree<T>::Result
 typename DomainTree<T>::Result
 DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
 DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
                       const isc::dns::Name& target_name,
                       const isc::dns::Name& target_name,

+ 32 - 24
src/lib/datasrc/memory/memory_client.cc

@@ -22,6 +22,7 @@
 #include <datasrc/memory/domaintree.h>
 #include <datasrc/memory/domaintree.h>
 #include <datasrc/memory/segment_object_holder.h>
 #include <datasrc/memory/segment_object_holder.h>
 #include <datasrc/memory/treenode_rrset.h>
 #include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/zone_finder.h>
 
 
 #include <util/memory_segment_local.h>
 #include <util/memory_segment_local.h>
 
 
@@ -100,9 +101,6 @@ public:
         FileNameTree::destroy(mem_sgmt_, file_name_tree_, deleter);
         FileNameTree::destroy(mem_sgmt_, file_name_tree_, deleter);
 
 
         ZoneTable::destroy(mem_sgmt_, zone_table_, rrclass_);
         ZoneTable::destroy(mem_sgmt_, zone_table_, rrclass_);
-
-        // see above for the assert().
-        assert(mem_sgmt_.allMemoryDeallocated());
     }
     }
 
 
     util::MemorySegment& mem_sgmt_;
     util::MemorySegment& mem_sgmt_;
@@ -326,6 +324,7 @@ public:
         if (nsec3_data == NULL) {
         if (nsec3_data == NULL) {
             nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
             nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
             zone_data.setNSEC3Data(nsec3_data);
             zone_data.setNSEC3Data(nsec3_data);
+            zone_data.setSigned(true);
         } else {
         } else {
             size_t salt_len = nsec3_data->getSaltLen();
             size_t salt_len = nsec3_data->getSaltLen();
             const uint8_t* salt_data = nsec3_data->getSaltData();
             const uint8_t* salt_data = nsec3_data->getSaltData();
@@ -333,7 +332,12 @@ public:
 
 
             if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
             if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
                 (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
                 (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
-                (salt_data_2.size() != salt_len) ||
+                (salt_data_2.size() != salt_len)) {
+                isc_throw(AddError,
+                          "NSEC3 with inconsistent parameters: " <<
+                          rrset->toText());
+            }
+            if ((salt_len > 0) &&
                 (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
                 (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
                 isc_throw(AddError,
                 isc_throw(AddError,
                           "NSEC3 with inconsistent parameters: " <<
                           "NSEC3 with inconsistent parameters: " <<
@@ -341,17 +345,10 @@ public:
             }
             }
         }
         }
 
 
-        string fst_label = rrset->getName().split(0, 1).toText(true);
-        transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
-                  ::toupper);
-
         ZoneNode* node;
         ZoneNode* node;
-        nsec3_data->insertName(mem_sgmt_, Name(fst_label), &node);
+        nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
 
 
         RdataEncoder encoder;
         RdataEncoder encoder;
-
-        // We assume that rrsig has already been checked to match rrset
-        // by the caller.
         RdataSet* set = RdataSet::create(mem_sgmt_, encoder, rrset, rrsig);
         RdataSet* set = RdataSet::create(mem_sgmt_, encoder, rrset, rrsig);
         RdataSet* old_set = node->setData(set);
         RdataSet* old_set = node->setData(set);
         if (old_set != NULL) {
         if (old_set != NULL) {
@@ -416,6 +413,7 @@ public:
                 if (nsec3_data == NULL) {
                 if (nsec3_data == NULL) {
                     nsec3_data = NSEC3Data::create(mem_sgmt_, param);
                     nsec3_data = NSEC3Data::create(mem_sgmt_, param);
                     zone_data.setNSEC3Data(nsec3_data);
                     zone_data.setNSEC3Data(nsec3_data);
+                    zone_data.setSigned(true);
                 } else {
                 } else {
                     size_t salt_len = nsec3_data->getSaltLen();
                     size_t salt_len = nsec3_data->getSaltLen();
                     const uint8_t* salt_data = nsec3_data->getSaltData();
                     const uint8_t* salt_data = nsec3_data->getSaltData();
@@ -423,7 +421,13 @@ public:
 
 
                     if ((param.getHashalg() != nsec3_data->hashalg) ||
                     if ((param.getHashalg() != nsec3_data->hashalg) ||
                         (param.getIterations() != nsec3_data->iterations) ||
                         (param.getIterations() != nsec3_data->iterations) ||
-                        (salt_data_2.size() != salt_len) ||
+                        (salt_data_2.size() != salt_len)) {
+                        isc_throw(AddError,
+                                  "NSEC3PARAM with inconsistent parameters: "
+                                  << rrset->toText());
+                    }
+
+                    if ((salt_len > 0) &&
                         (std::memcmp(&salt_data_2[0],
                         (std::memcmp(&salt_data_2[0],
                                      salt_data, salt_len) != 0)) {
                                      salt_data, salt_len) != 0)) {
                         isc_throw(AddError,
                         isc_throw(AddError,
@@ -606,7 +610,7 @@ InMemoryClient::InMemoryClientImpl::load(
     }
     }
 
 
     LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_ZONE).
     LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_ZONE).
-        arg(zone_name).arg(rrclass_.toText());
+        arg(zone_name).arg(rrclass_);
 
 
     // Set the filename in file_name_tree_ now, so that getFileName()
     // Set the filename in file_name_tree_ now, so that getFileName()
     // can use it (during zone reloading).
     // can use it (during zone reloading).
@@ -686,21 +690,25 @@ InMemoryClient::getZoneCount() const {
     return (impl_->zone_count_);
     return (impl_->zone_count_);
 }
 }
 
 
-isc::datasrc::memory::ZoneTable::FindResult
-InMemoryClient::findZone2(const isc::dns::Name& zone_name) const {
+isc::datasrc::DataSourceClient::FindResult
+InMemoryClient::findZone(const isc::dns::Name& zone_name) const {
     LOG_DEBUG(logger, DBG_TRACE_DATA,
     LOG_DEBUG(logger, DBG_TRACE_DATA,
               DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name);
               DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name);
+
     ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
     ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
-    return (result);
+
+    ZoneFinderPtr finder;
+    if (result.code != result::NOTFOUND) {
+        finder.reset(new InMemoryZoneFinder(*result.zone_data, getClass()));
+    }
+
+    return (DataSourceClient::FindResult(result.code, finder));
 }
 }
 
 
-isc::datasrc::DataSourceClient::FindResult
-InMemoryClient::findZone(const isc::dns::Name&) const {
-    // This variant of findZone() is not implemented and should be
-    // removed eventually. It currently throws an exception. It is
-    // required right now to derive from DataSourceClient.
-    isc_throw(isc::NotImplemented,
-              "This variant of findZone() is not implemented.");
+const ZoneData*
+InMemoryClient::findZoneData(const isc::dns::Name& zone_name) {
+    ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+    return (result.zone_data);
 }
 }
 
 
 result::Result
 result::Result

+ 11 - 12
src/lib/datasrc/memory/memory_client.h

@@ -20,11 +20,7 @@
 #include <datasrc/iterator.h>
 #include <datasrc/iterator.h>
 #include <datasrc/client.h>
 #include <datasrc/client.h>
 #include <datasrc/memory/zone_table.h>
 #include <datasrc/memory/zone_table.h>
-
-// for isc::datasrc::ZoneTable::FindResult returned by findZone(). This
-// variant of findZone() is not implemented and should be removed
-// eventually.
-#include <datasrc/zonetable.h>
+#include <datasrc/memory/zone_data.h>
 
 
 #include <string>
 #include <string>
 
 
@@ -206,19 +202,22 @@ public:
         { }
         { }
     };
     };
 
 
-    /// Returns a \c ZoneTable result that best matches the given name.
+    /// Returns a \c ZoneFinder result that best matches the given name.
     ///
     ///
     /// This derived version of the method never throws an exception.
     /// This derived version of the method never throws an exception.
     /// For other details see \c DataSourceClient::findZone().
     /// For other details see \c DataSourceClient::findZone().
-    virtual isc::datasrc::memory::ZoneTable::FindResult
-    findZone2(const isc::dns::Name& name) const;
-
-    // This variant of findZone() is not implemented and should be
-    // removed eventually. It currently throws an exception. It is
-    // required right now to derive from DataSourceClient.
     virtual isc::datasrc::DataSourceClient::FindResult
     virtual isc::datasrc::DataSourceClient::FindResult
     findZone(const isc::dns::Name& name) const;
     findZone(const isc::dns::Name& name) const;
 
 
+    /// Returns a \c ZoneData in the result that best matches the given
+    /// name.
+    ///
+    /// This is mainly intended for use in unit tests and should not be
+    /// used in other code.
+    ///
+    /// \throws none
+    const ZoneData* findZoneData(const isc::dns::Name& name);
+
     /// \brief Implementation of the getIterator method
     /// \brief Implementation of the getIterator method
     virtual isc::datasrc::ZoneIteratorPtr
     virtual isc::datasrc::ZoneIteratorPtr
     getIterator(const isc::dns::Name& name, bool separate_rrs = false) const;
     getIterator(const isc::dns::Name& name, bool separate_rrs = false) const;

+ 22 - 17
src/lib/datasrc/memory/treenode_rrset.cc

@@ -79,10 +79,6 @@ TreeNodeRRset::setTTL(const RRTTL&) {
 
 
 std::string
 std::string
 TreeNodeRRset::toText() const {
 TreeNodeRRset::toText() const {
-    // Create TTL from internal data
-    util::InputBuffer ttl_buffer(rdataset_->getTTLData(), sizeof(uint32_t));
-    const RRTTL ttl(ttl_buffer);
-
     // Dump the main RRset, if not empty
     // Dump the main RRset, if not empty
     std::string ret;
     std::string ret;
     RRsetPtr tmp_rrset;
     RRsetPtr tmp_rrset;
@@ -92,7 +88,7 @@ TreeNodeRRset::toText() const {
     {
     {
         if (!tmp_rrset) {
         if (!tmp_rrset) {
             tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_, getType(),
             tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_, getType(),
-                                           ttl));
+                                           getTTL()));
         }
         }
         tmp_rrset->addRdata(rit->getCurrent());
         tmp_rrset->addRdata(rit->getCurrent());
     }
     }
@@ -101,17 +97,7 @@ TreeNodeRRset::toText() const {
     }
     }
 
 
     // Dump any RRSIGs
     // Dump any RRSIGs
-    tmp_rrset.reset();
-    for (RdataIteratorPtr rit = getSigRdataIterator();
-         !rit->isLast();
-         rit->next())
-    {
-        if (!tmp_rrset) {
-            tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_,
-                                           RRType::RRSIG(), ttl));
-        }
-        tmp_rrset->addRdata(rit->getCurrent());
-    }
+    tmp_rrset = getRRsig();
     if (tmp_rrset) {
     if (tmp_rrset) {
         ret += tmp_rrset->toText();
         ret += tmp_rrset->toText();
     }
     }
@@ -292,7 +278,26 @@ TreeNodeRRset::getSigRdataIterator() const {
 
 
 RRsetPtr
 RRsetPtr
 TreeNodeRRset::getRRsig() const {
 TreeNodeRRset::getRRsig() const {
-    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+    // Shortcut: if DNSSEC is disabled for the RRset, simply return NULL.
+    // The generic code below provides the same behavior, but with much more
+    // overhead.
+    if (!dnssec_ok_) {
+        return (RRsetPtr());
+    }
+
+    RRsetPtr tmp_rrset;
+    for (RdataIteratorPtr rit = getSigRdataIterator();
+         !rit->isLast();
+         rit->next())
+    {
+        if (!tmp_rrset) {
+            tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_,
+                                           RRType::RRSIG(), getTTL()));
+        }
+        tmp_rrset->addRdata(rit->getCurrent());
+    }
+
+    return (tmp_rrset);
 }
 }
 
 
 void
 void

+ 0 - 2
src/lib/datasrc/memory/treenode_rrset.h

@@ -190,8 +190,6 @@ public:
     virtual dns::RdataIteratorPtr getRdataIterator() const;
     virtual dns::RdataIteratorPtr getRdataIterator() const;
 
 
     /// \brief Specialized version of \c getRRsig() for \c TreeNodeRRset.
     /// \brief Specialized version of \c getRRsig() for \c TreeNodeRRset.
-    ///
-    /// It throws \c isc::Unexpected unconditionally.
     virtual dns::RRsetPtr getRRsig() const;
     virtual dns::RRsetPtr getRRsig() const;
 
 
     virtual unsigned int getRRsigDataCount() const {
     virtual unsigned int getRRsigDataCount() const {

+ 421 - 59
src/lib/datasrc/memory/zone_finder.cc

@@ -15,6 +15,7 @@
 #include <datasrc/memory/zone_finder.h>
 #include <datasrc/memory/zone_finder.h>
 #include <datasrc/memory/domaintree.h>
 #include <datasrc/memory/domaintree.h>
 #include <datasrc/memory/treenode_rrset.h>
 #include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/rdata_serialization.h>
 
 
 #include <datasrc/zone.h>
 #include <datasrc/zone.h>
 #include <datasrc/data_source.h>
 #include <datasrc/data_source.h>
@@ -22,9 +23,16 @@
 #include <dns/name.h>
 #include <dns/name.h>
 #include <dns/rrset.h>
 #include <dns/rrset.h>
 #include <dns/rrtype.h>
 #include <dns/rrtype.h>
+#include <dns/nsec3hash.h>
 
 
 #include <datasrc/logger.h>
 #include <datasrc/logger.h>
 
 
+#include <boost/scoped_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include <algorithm>
+#include <vector>
+
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::datasrc::memory;
 using namespace isc::datasrc::memory;
 using namespace isc::datasrc;
 using namespace isc::datasrc;
@@ -33,6 +41,37 @@ namespace isc {
 namespace datasrc {
 namespace datasrc {
 namespace memory {
 namespace memory {
 
 
+namespace internal {
+
+// Specialized version of ZoneFinder::ResultContext, which  holds objects
+// related to find() results using internal representations of the in-memory
+// data source implementation.
+class ZoneFinderResultContext {
+public:
+    /// \brief Constructor
+    ///
+    /// The first three parameters correspond to those of
+    /// ZoneFinder::ResultContext.  If node is non NULL, it specifies the
+    /// found ZoneNode in the search.
+    ZoneFinderResultContext(ZoneFinder::Result code_param,
+                            TreeNodeRRsetPtr rrset_param,
+                            ZoneFinder::FindResultFlags flags_param,
+                            const ZoneData& zone_data_param,
+                            const ZoneNode* node, const RdataSet* rdset) :
+        code(code_param), rrset(rrset_param), flags(flags_param),
+        zone_data(&zone_data_param), found_node(node), found_rdset(rdset)
+    {}
+
+    const ZoneFinder::Result code;
+    const TreeNodeRRsetPtr rrset;
+    const ZoneFinder::FindResultFlags flags;
+    const ZoneData* const zone_data;
+    const ZoneNode* const found_node;
+    const RdataSet* const found_rdset;
+};
+}
+using internal::ZoneFinderResultContext;
+
 namespace {
 namespace {
 /// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for
 /// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for
 /// the given RRClass
 /// the given RRClass
@@ -51,18 +90,21 @@ TreeNodeRRsetPtr
 createTreeNodeRRset(const ZoneNode* node,
 createTreeNodeRRset(const ZoneNode* node,
                     const RdataSet* rdataset,
                     const RdataSet* rdataset,
                     const RRClass& rrclass,
                     const RRClass& rrclass,
+                    ZoneFinder::FindOptions options,
                     const Name* realname = NULL)
                     const Name* realname = NULL)
 {
 {
+    const bool dnssec = ((options & ZoneFinder::FIND_DNSSEC) != 0);
     if (node != NULL && rdataset != NULL) {
     if (node != NULL && rdataset != NULL) {
         if (realname != NULL) {
         if (realname != NULL) {
-            return TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass, node,
-                                                      rdataset, true));
+            return (TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass,
+                                                       node, rdataset,
+                                                       dnssec)));
         } else {
         } else {
-            return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node,
-                                                      rdataset, true));
+            return (TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset,
+                                                       dnssec)));
         }
         }
     } else {
     } else {
-        return TreeNodeRRsetPtr();
+        return (TreeNodeRRsetPtr());
     }
     }
 }
 }
 
 
@@ -145,6 +187,27 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
     return (false);
     return (false);
 }
 }
 
 
+/// Creates a NSEC3 ConstRRsetPtr for the given ZoneNode inside the
+/// NSEC3 tree, for the given RRClass.
+///
+/// It asserts that the node contains data (RdataSet) and is of type
+/// NSEC3.
+///
+/// \param node The ZoneNode inside the NSEC3 tree
+/// \param rrclass The RRClass as passed by the client
+ConstRRsetPtr
+createNSEC3RRset(const ZoneNode* node, const RRClass& rrclass) {
+     const RdataSet* rdataset = node->getData();
+     // Only NSEC3 ZoneNodes are allowed to be passed to this method. We
+     // assert that these have data, and also are of type NSEC3.
+     assert(rdataset != NULL);
+     assert(rdataset->type == RRType::NSEC3());
+
+    // Create the RRset.  Note the DNSSEC flag: NSEC3 implies DNSSEC.
+    return (createTreeNodeRRset(node, rdataset, rrclass,
+                                ZoneFinder::FIND_DNSSEC));
+}
+
 // convenience function to fill in the final details
 // convenience function to fill in the final details
 //
 //
 // Set up ZoneFinderResultContext object as a return value of find(),
 // Set up ZoneFinderResultContext object as a return value of find(),
@@ -158,14 +221,16 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
 // if wild is true, the RESULT_WILDCARD flag will be set.
 // if wild is true, the RESULT_WILDCARD flag will be set.
 // If qname is not NULL, this is the query name, to be used in wildcard
 // If qname is not NULL, this is the query name, to be used in wildcard
 // substitution instead of the Node's name).
 // substitution instead of the Node's name).
-isc::datasrc::memory::ZoneFinderResultContext
+ZoneFinderResultContext
 createFindResult(const RRClass& rrclass,
 createFindResult(const RRClass& rrclass,
                  const ZoneData& zone_data,
                  const ZoneData& zone_data,
                  ZoneFinder::Result code,
                  ZoneFinder::Result code,
-                 const RdataSet* rrset,
+                 const RdataSet* rdset,
                  const ZoneNode* node,
                  const ZoneNode* node,
+                 ZoneFinder::FindOptions options,
                  bool wild = false,
                  bool wild = false,
-                 const Name* qname = NULL) {
+                 const Name* qname = NULL)
+{
     ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
     ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
     const Name* rename = NULL;
     const Name* rename = NULL;
 
 
@@ -182,9 +247,10 @@ createFindResult(const RRClass& rrclass,
         }
         }
     }
     }
 
 
-    return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset,
-                                                              rrclass, rename),
-                                    flags, node));
+    return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rdset,
+                                                              rrclass, options,
+                                                              rename),
+                                    flags, zone_data, node, rdset));
 }
 }
 
 
 // A helper function for NSEC-signed zones.  It searches the zone for
 // A helper function for NSEC-signed zones.  It searches the zone for
@@ -341,19 +407,22 @@ public:
 // caught above.
 // caught above.
 //
 //
 // If none of the above succeeds, we conclude the name doesn't exist in
 // If none of the above succeeds, we conclude the name doesn't exist in
-// the zone, and throw an OutOfZone exception.
+// the zone, and throw an OutOfZone exception by default.  If the optional
+// out_of_zone_ok is true, it returns an NXDOMAIN result with NULL data so
+// the caller can take an action to it (technically it's not "NXDOMAIN",
+// but the caller is assumed not to rely on the difference.)
 FindNodeResult findNode(const ZoneData& zone_data,
 FindNodeResult findNode(const ZoneData& zone_data,
-                        const Name& name,
+                        const LabelSequence& name_labels,
                         ZoneChain& node_path,
                         ZoneChain& node_path,
-                        ZoneFinder::FindOptions options)
+                        ZoneFinder::FindOptions options,
+                        bool out_of_zone_ok = false)
 {
 {
     ZoneNode* node = NULL;
     ZoneNode* node = NULL;
     FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
     FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
 
 
     const ZoneTree& tree(zone_data.getZoneTree());
     const ZoneTree& tree(zone_data.getZoneTree());
-    ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name),
-                                        &node, node_path, cutCallback,
-                                        &state);
+    const ZoneTree::Result result = tree.find(name_labels, &node, node_path,
+                                              cutCallback, &state);
     const unsigned int zonecut_flag =
     const unsigned int zonecut_flag =
         (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
         (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
     if (result == ZoneTree::EXACTMATCH) {
     if (result == ZoneTree::EXACTMATCH) {
@@ -377,7 +446,7 @@ FindNodeResult findNode(const ZoneData& zone_data,
         if (node_path.getLastComparisonResult().getRelation() ==
         if (node_path.getLastComparisonResult().getRelation() ==
             NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
             NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
             LOG_DEBUG(logger, DBG_TRACE_DATA,
             LOG_DEBUG(logger, DBG_TRACE_DATA,
-                      DATASRC_MEM_SUPER_STOP).arg(name);
+                      DATASRC_MEM_SUPER_STOP).arg(name_labels);
             const ZoneNode* nsec_node;
             const ZoneNode* nsec_node;
             const RdataSet* nsec_rds = getClosestNSEC(zone_data,
             const RdataSet* nsec_rds = getClosestNSEC(zone_data,
                                                       node_path,
                                                       node_path,
@@ -398,7 +467,7 @@ FindNodeResult findNode(const ZoneData& zone_data,
                 // baz.foo.wild.example. The common ancestor, foo.wild.example,
                 // baz.foo.wild.example. The common ancestor, foo.wild.example,
                 // should cancel wildcard.  Treat it as NXDOMAIN.
                 // should cancel wildcard.  Treat it as NXDOMAIN.
                 LOG_DEBUG(logger, DBG_TRACE_DATA,
                 LOG_DEBUG(logger, DBG_TRACE_DATA,
-                          DATASRC_MEM_WILDCARD_CANCEL).arg(name);
+                          DATASRC_MEM_WILDCARD_CANCEL).arg(name_labels);
                     const ZoneNode* nsec_node;
                     const ZoneNode* nsec_node;
                     const RdataSet* nsec_rds = getClosestNSEC(zone_data,
                     const RdataSet* nsec_rds = getClosestNSEC(zone_data,
                                                               node_path,
                                                               node_path,
@@ -431,53 +500,198 @@ FindNodeResult findNode(const ZoneData& zone_data,
                         FindNodeResult::FIND_WILDCARD | zonecut_flag));
                         FindNodeResult::FIND_WILDCARD | zonecut_flag));
         }
         }
 
 
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
+            arg(name_labels);
         const ZoneNode* nsec_node;
         const ZoneNode* nsec_node;
         const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path,
         const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path,
                                                   &nsec_node, options);
                                                   &nsec_node, options);
         return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds));
         return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds));
     } else {
     } else {
         // If the name is neither an exact or partial match, it is
         // If the name is neither an exact or partial match, it is
-        // out of bailiwick, which is considered an error.
-        isc_throw(OutOfZone, name.toText() << " not in " <<
+        // out of bailiwick, which is considered an error, unless the caller
+        // is willing to accept it.
+        if (out_of_zone_ok) {
+            return (FindNodeResult(ZoneFinder::NXDOMAIN, NULL, NULL));
+        }
+        isc_throw(OutOfZone, name_labels << " not in " <<
                              zone_data.getOriginNode()->getName());
                              zone_data.getOriginNode()->getName());
     }
     }
 }
 }
 
 
 } // end anonymous namespace
 } // end anonymous namespace
 
 
-// Specialization of the ZoneFinder::Context for the in-memory finder.
+
+/// \brief Specialization of the ZoneFinder::Context for the in-memory finder.
+///
+/// Note that we don't have a specific constructor for the findAll() case.
+/// For (successful) type ANY query, found_node points to the
+/// corresponding zone node, which is recorded within this specialized
+/// context.
 class InMemoryZoneFinder::Context : public ZoneFinder::Context {
 class InMemoryZoneFinder::Context : public ZoneFinder::Context {
 public:
 public:
-    /// \brief Constructor.
-    ///
-    /// Note that we don't have a specific constructor for the findAll() case.
-    /// For (successful) type ANY query, found_node points to the
-    /// corresponding RB node, which is recorded within this specialized
-    /// context.
     Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
     Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
-            ZoneFinderResultContext result) :
+            const RRClass& rrclass, const ZoneFinderResultContext& result) :
         ZoneFinder::Context(finder, options,
         ZoneFinder::Context(finder, options,
                             ResultContext(result.code, result.rrset,
                             ResultContext(result.code, result.rrset,
                                           result.flags)),
                                           result.flags)),
-        rrset_(result.rrset), found_node_(result.found_node)
+        rrclass_(rrclass), zone_data_(result.zone_data),
+        found_node_(result.found_node),
+        found_rdset_(result.found_rdset)
     {}
     {}
 
 
+protected:
+    virtual void getAdditionalImpl(const std::vector<RRType>& requested_types,
+                                   std::vector<ConstRRsetPtr>& result)
+    {
+        if (found_rdset_ != NULL) {
+            // Normal query with successful result.
+            getAdditionalForRdataset(found_rdset_, requested_types, result,
+                                     options_);
+        } else if (found_node_ != NULL) {
+            // Successful type ANY query result.  Call
+            // getAdditionalForRdataset for each RdataSet of the node.
+            for (const RdataSet* rdset = found_node_->getData();
+                 rdset != NULL;
+                 rdset = rdset->getNext())
+            {
+                getAdditionalForRdataset(rdset, requested_types, result,
+                                         options_);
+            }
+        }
+    }
+
 private:
 private:
-    const TreeNodeRRsetPtr rrset_;
+    // Main subroutine of getAdditionalImpl, iterate over Rdata fields
+    // find, create, and insert necessary additional RRsets.
+    void
+    getAdditionalForRdataset(const RdataSet* rdset,
+                             const std::vector<RRType>& requested_types,
+                             std::vector<ConstRRsetPtr>& result,
+                             ZoneFinder::FindOptions orig_options) const
+    {
+        ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
+        if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
+            options = options | ZoneFinder::FIND_DNSSEC;
+        }
+        if (rdset->type == RRType::NS()) {
+            options = options | ZoneFinder::FIND_GLUE_OK;
+        }
+
+        RdataReader(rrclass_, rdset->type, rdset->getDataBuf(),
+                    rdset->getRdataCount(), rdset->getSigRdataCount(),
+                    boost::bind(&Context::findAdditional, this,
+                                &requested_types, &result, options, _1, _2),
+                    &RdataReader::emptyDataAction).iterate();
+    }
+
+    // RdataReader callback for additional section processing.
+    void
+    findAdditional(const std::vector<RRType>* requested_types,
+                   std::vector<ConstRRsetPtr>* result,
+                   ZoneFinder::FindOptions options,
+                   const LabelSequence& name_labels,
+                   RdataNameAttributes attr) const;
+
+    // Subroutine for findAdditional() to unify the normal and wildcard match
+    // cases.
+    void
+    findAdditionalHelper(const std::vector<RRType>* requested_types,
+                         std::vector<ConstRRsetPtr>* result,
+                         const ZoneNode* node,
+                         ZoneFinder::FindOptions options,
+                         const Name* real_name) const
+    {
+        const std::vector<RRType>::const_iterator type_beg =
+            requested_types->begin();
+        const std::vector<RRType>::const_iterator type_end =
+            requested_types->end();
+        for (const RdataSet* rdset = node->getData();
+             rdset != NULL;
+             rdset = rdset->getNext())
+        {
+            // Checking all types for all RdataSets could be suboptimal.
+            // This can be a bit more optimized, but unless we have many
+            // requested types the effect is probably marginal.  For now we
+            // keep it simple.
+            if (std::find(type_beg, type_end, rdset->type) != type_end) {
+                result->push_back(createTreeNodeRRset(node, rdset, rrclass_,
+                                                      options, real_name));
+            }
+        }
+    }
+
+private:
+    const RRClass rrclass_;
+    const ZoneData* const zone_data_;
     const ZoneNode* const found_node_;
     const ZoneNode* const found_node_;
+    const RdataSet* const found_rdset_;
 };
 };
 
 
+void
+InMemoryZoneFinder::Context::findAdditional(
+    const std::vector<RRType>* requested_types,
+    std::vector<ConstRRsetPtr>* result,
+    ZoneFinder::FindOptions options,
+    const LabelSequence& name_labels,
+    RdataNameAttributes attr) const
+{
+    // Ignore name data that don't need additional processing.
+    if ((attr & NAMEATTR_ADDITIONAL) == 0) {
+        return;
+    }
+
+    // Find the zone node for the additional name.  By passing true as the
+    // last parameter of findNode() we ignore out-of-zone names.
+    ZoneChain node_path;
+    const FindNodeResult node_result =
+        findNode(*zone_data_, name_labels, node_path, options, true);
+    // we only need non-empty exact match
+    if (node_result.code != SUCCESS) {
+        return;
+    }
+
+    // Ignore data at a zone cut (due to subdomain delegation) unless glue is
+    // allowed.  Checking the node callback flag is a cheap way to detect
+    // zone cuts, but it includes DNAME delegation, in which case we should
+    // keep finding the additional records regardless of the 'GLUE_OK' flag.
+    // The last two conditions limit the case to delegation NS, i.e, the node
+    // has an NS and it's not the zone origin.
+    const ZoneNode* node = node_result.node;
+    if ((options & ZoneFinder::FIND_GLUE_OK) == 0 &&
+        node->getFlag(ZoneNode::FLAG_CALLBACK) &&
+        node != zone_data_->getOriginNode() &&
+        RdataSet::find(node->getData(), RRType::NS()) != NULL) {
+        return;
+    }
+
+    // Examine RdataSets of the node, and create and insert requested types
+    // of RRsets as we find them.
+    if ((node_result.flags & FindNodeResult::FIND_WILDCARD) == 0) {
+        // normal case
+        findAdditionalHelper(requested_types, result, node, options, NULL);
+    } else {
+        // if the additional name is subject to wildcard substitution, we need
+        // to create a name object for the "real" (after substitution) name.
+        // This is expensive, but in the additional processing this should be
+        // very rare cases and acceptable.
+        size_t data_len;
+        const uint8_t* data;
+        data = name_labels.getData(&data_len);
+        util::InputBuffer buffer(data, data_len);
+        const Name real_name(buffer);
+        findAdditionalHelper(requested_types, result, node, options,
+                             &real_name);
+    }
+}
+
 boost::shared_ptr<ZoneFinder::Context>
 boost::shared_ptr<ZoneFinder::Context>
 InMemoryZoneFinder::find(const isc::dns::Name& name,
 InMemoryZoneFinder::find(const isc::dns::Name& name,
                 const isc::dns::RRType& type,
                 const isc::dns::RRType& type,
                 const FindOptions options)
                 const FindOptions options)
 {
 {
-    return ZoneFinderContextPtr(new Context(*this, options,
-                                            find_internal(name,
-                                                          type,
-                                                          NULL,
-                                                          options)));
+    return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
+                                             find_internal(name, type,
+                                                           NULL, options))));
 }
 }
 
 
 boost::shared_ptr<ZoneFinder::Context>
 boost::shared_ptr<ZoneFinder::Context>
@@ -485,11 +699,11 @@ InMemoryZoneFinder::findAll(const isc::dns::Name& name,
         std::vector<isc::dns::ConstRRsetPtr>& target,
         std::vector<isc::dns::ConstRRsetPtr>& target,
         const FindOptions options)
         const FindOptions options)
 {
 {
-    return ZoneFinderContextPtr(new Context(*this, options,
-                                            find_internal(name,
-                                                          RRType::ANY(),
-                                                          &target,
-                                                          options)));
+    return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
+                                             find_internal(name,
+                                                           RRType::ANY(),
+                                                           &target,
+                                                           options))));
 }
 }
 
 
 ZoneFinderResultContext
 ZoneFinderResultContext
@@ -502,10 +716,11 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
     // in findNode().  We simply construct a result structure and return.
     // in findNode().  We simply construct a result structure and return.
     ZoneChain node_path;
     ZoneChain node_path;
     const FindNodeResult node_result =
     const FindNodeResult node_result =
-        findNode(zone_data_, name, node_path, options);
+        findNode(zone_data_, LabelSequence(name), node_path, options);
     if (node_result.code != SUCCESS) {
     if (node_result.code != SUCCESS) {
         return (createFindResult(rrclass_, zone_data_, node_result.code,
         return (createFindResult(rrclass_, zone_data_, node_result.code,
-                                 node_result.rrset, node_result.node));
+                                 node_result.rrset, node_result.node,
+                                 options));
     }
     }
 
 
     const ZoneNode* node = node_result.node;
     const ZoneNode* node = node_result.node;
@@ -524,25 +739,26 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
         const RdataSet* nsec_rds = getClosestNSEC(zone_data_, node_path,
         const RdataSet* nsec_rds = getClosestNSEC(zone_data_, node_path,
                                                   &nsec_node, options);
                                                   &nsec_node, options);
         return (createFindResult(rrclass_, zone_data_, NXRRSET,
         return (createFindResult(rrclass_, zone_data_, NXRRSET,
-                                 nsec_rds,
-                                 nsec_node,
-                                 wild));
+                                 nsec_rds, nsec_node, options, wild));
     }
     }
 
 
     const RdataSet* found;
     const RdataSet* found;
 
 
     // If the node callback is enabled, this may be a zone cut.  If it
     // If the node callback is enabled, this may be a zone cut.  If it
     // has a NS RR, we should return a delegation, but not in the apex.
     // has a NS RR, we should return a delegation, but not in the apex.
-    // There is one exception: the case for DS query, which should always
-    // be considered in-zone lookup.
+    // There are two exceptions:
+    // - the case for DS query, which should always be considered in-zone
+    //   lookup.
+    // - when we are looking for glue records (FIND_GLUE_OK)
     if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
     if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
-            node != zone_data_.getOriginNode() && type != RRType::DS()) {
+        (options & FIND_GLUE_OK) == 0 &&
+        node != zone_data_.getOriginNode() && type != RRType::DS()) {
         found = RdataSet::find(node->getData(), RRType::NS());
         found = RdataSet::find(node->getData(), RRType::NS());
         if (found != NULL) {
         if (found != NULL) {
             LOG_DEBUG(logger, DBG_TRACE_DATA,
             LOG_DEBUG(logger, DBG_TRACE_DATA,
                       DATASRC_MEM_EXACT_DELEGATION).arg(name);
                       DATASRC_MEM_EXACT_DELEGATION).arg(name);
             return (createFindResult(rrclass_, zone_data_, DELEGATION,
             return (createFindResult(rrclass_, zone_data_, DELEGATION,
-                                     found, node, wild, &name));
+                                     found, node, options, wild, &name));
         }
         }
     }
     }
 
 
@@ -552,13 +768,13 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
         const RdataSet* cur_rds = node->getData();
         const RdataSet* cur_rds = node->getData();
         while (cur_rds != NULL) {
         while (cur_rds != NULL) {
             target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_,
             target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_,
-                                                  &name));
+                                                  options, &name));
             cur_rds = cur_rds->getNext();
             cur_rds = cur_rds->getNext();
         }
         }
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
             arg(name);
             arg(name);
         return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node,
         return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node,
-                                 wild, &name));
+                                 options, wild, &name));
     }
     }
 
 
     found = RdataSet::find(node->getData(), type);
     found = RdataSet::find(node->getData(), type);
@@ -567,7 +783,7 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
             arg(type);
             arg(type);
         return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node,
         return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node,
-                                 wild, &name));
+                                 options, wild, &name));
     } else {
     } else {
         // Next, try CNAME.
         // Next, try CNAME.
         found = RdataSet::find(node->getData(), RRType::CNAME());
         found = RdataSet::find(node->getData(), RRType::CNAME());
@@ -575,20 +791,166 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
 
 
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
             return (createFindResult(rrclass_, zone_data_, CNAME, found, node,
             return (createFindResult(rrclass_, zone_data_, CNAME, found, node,
-                                     wild, &name));
+                                     options, wild, &name));
         }
         }
     }
     }
     // No exact match or CNAME.  Get NSEC if necessary and return NXRRSET.
     // No exact match or CNAME.  Get NSEC if necessary and return NXRRSET.
     return (createFindResult(rrclass_, zone_data_, NXRRSET,
     return (createFindResult(rrclass_, zone_data_, NXRRSET,
                              getNSECForNXRRSET(zone_data_, options, node),
                              getNSECForNXRRSET(zone_data_, options, node),
-                             node, wild, &name));
+                             node, options, wild, &name));
 }
 }
 
 
 isc::datasrc::ZoneFinder::FindNSEC3Result
 isc::datasrc::ZoneFinder::FindNSEC3Result
 InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
 InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
-    (void)name;
-    (void)recursive;
-    isc_throw(isc::NotImplemented, "not completed yet! please implement me");
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
+        arg(recursive ? "recursive" : "non-recursive");
+
+    uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+    const LabelSequence origin_ls(zone_data_.getOriginNode()->
+                                  getAbsoluteLabels(labels_buf));
+    const LabelSequence name_ls(name);
+
+    if (!zone_data_.isNSEC3Signed()) {
+        isc_throw(DataSourceError,
+                  "findNSEC3 attempt for non NSEC3 signed zone: " <<
+                  origin_ls << "/" << getClass());
+    }
+
+    const NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
+    // This would be a programming mistake, as ZoneData::isNSEC3Signed()
+    // should check this.
+    assert(nsec3_data != NULL);
+
+    const ZoneTree& tree = nsec3_data->getNSEC3Tree();
+    if (tree.getNodeCount() == 0) {
+        isc_throw(DataSourceError,
+                  "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
+                  origin_ls << "/" << getClass());
+    }
+
+    const NameComparisonResult cmp_result = name_ls.compare(origin_ls);
+    if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
+        cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
+        isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
+                  << name_ls << ", zone: " << origin_ls << "/"
+                  << getClass());
+    }
+
+    // Convenient shortcuts
+    const unsigned int olabels = origin_ls.getLabelCount();
+    const unsigned int qlabels = name.getLabelCount();
+    // placeholder of the next closer proof
+    const ZoneNode* covering_node(NULL);
+
+    // Now we'll first look up the origin node and initialize orig_chain
+    // with it.
+    ZoneChain orig_chain;
+    const ZoneNode* node(NULL);
+    ZoneTree::Result result =
+         tree.find<void*>(origin_ls, &node, orig_chain, NULL, NULL);
+    if (result != ZoneTree::EXACTMATCH) {
+        // If the origin node doesn't exist, simply fail.
+        isc_throw(DataSourceError,
+                  "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
+                  origin_ls << "/" << getClass());
+    }
+
+    const boost::scoped_ptr<NSEC3Hash> hash
+        (NSEC3Hash::create(nsec3_data->hashalg,
+                           nsec3_data->iterations,
+                           nsec3_data->getSaltData(),
+                           nsec3_data->getSaltLen()));
+
+    // Examine all names from the query name to the origin name, stripping
+    // the deepest label one by one, until we find a name that has a matching
+    // NSEC3 hash.
+    for (unsigned int labels = qlabels; labels >= olabels; --labels) {
+        const Name& hname = (labels == qlabels ?
+                             name : name.split(qlabels - labels, labels));
+        const std::string hlabel = hash->calculate(hname);
+
+        LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
+            arg(name).arg(labels).arg(hlabel);
+
+        node = NULL;
+        ZoneChain chain(orig_chain);
+
+        // Now, make a label sequence relative to the origin.
+        const Name hlabel_name(hlabel);
+        LabelSequence hlabel_ls(hlabel_name);
+        // Remove trailing '.' making it relative
+        hlabel_ls.stripRight(1);
+
+        // Find hlabel relative to the orig_chain.
+        result = tree.find<void*>(hlabel_ls, &node, chain, NULL, NULL);
+        if (result == ZoneTree::EXACTMATCH) {
+            // We found an exact match.
+            ConstRRsetPtr closest = createNSEC3RRset(node, getClass());
+            ConstRRsetPtr next;
+            if (covering_node != NULL) {
+                next = createNSEC3RRset(covering_node, getClass());
+            }
+
+            LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                      DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
+                arg(*closest);
+
+            return (FindNSEC3Result(true, labels, closest, next));
+        } else {
+            while ((covering_node = tree.previousNode(chain)) != NULL &&
+                   covering_node->isEmpty()) {
+                ;
+            }
+            if (covering_node == NULL) {
+                covering_node = tree.largestNode();
+            }
+
+            if (!recursive) {   // in non recursive mode, we are done.
+                ConstRRsetPtr closest;
+                if (covering_node != NULL) {
+                    closest = createNSEC3RRset(covering_node, getClass());
+
+                    LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                              DATASRC_MEM_FINDNSEC3_COVER).
+                        arg(name).arg(*closest);
+                }
+
+                return (FindNSEC3Result(false, labels,
+                                        closest, ConstRRsetPtr()));
+            }
+        }
+    }
+
+    isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
+              "a broken NSEC3 zone: " << getOrigin() << "/"
+              << getClass());
+}
+
+Name
+InMemoryZoneFinder::getOrigin() const {
+    size_t data_len;
+    const uint8_t* data;
+
+    // Normally the label sequence of the origin node should be absolute,
+    // in which case we can simply generate the origin name from the labels.
+    const LabelSequence node_labels = zone_data_.getOriginNode()->getLabels();
+    if (node_labels.isAbsolute()) {
+        data = node_labels.getData(&data_len);
+    } else {
+        // In future we may allow adding out-of-zone names in the zone tree.
+        // For example, to hold out-of-zone NS names so we can establish a
+        // shortcut link to them as an optimization.  If and when that happens
+        // the origin node may not have an absolute label (consider the zone
+        // is example.org and we add ns.noexample.org).  In that case
+        // we first need to construct the absolute label sequence and then
+        // construct the name.
+        uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+        const LabelSequence name_labels =
+            zone_data_.getOriginNode()->getAbsoluteLabels(labels_buf);
+        data = name_labels.getData(&data_len);
+    }
+    util::InputBuffer buffer(data, data_len);
+    return (Name(buffer));
 }
 }
 
 
 } // namespace memory
 } // namespace memory

+ 10 - 28
src/lib/datasrc/memory/zone_finder.h

@@ -23,30 +23,15 @@
 #include <dns/rrset.h>
 #include <dns/rrset.h>
 #include <dns/rrtype.h>
 #include <dns/rrtype.h>
 
 
+#include <string>
+
 namespace isc {
 namespace isc {
 namespace datasrc {
 namespace datasrc {
 namespace memory {
 namespace memory {
-
-class ZoneFinderResultContext {
-public:
-    /// \brief Constructor
-    ///
-    /// The first three parameters correspond to those of
-    /// ZoneFinder::ResultContext.  If node is non NULL, it specifies the
-    /// found RBNode in the search.
-    ZoneFinderResultContext(ZoneFinder::Result code_param,
-                            TreeNodeRRsetPtr rrset_param,
-                            ZoneFinder::FindResultFlags flags_param,
-                            const ZoneNode* node) :
-        code(code_param), rrset(rrset_param), flags(flags_param),
-        found_node(node)
-    {}
-
-    const ZoneFinder::Result code;
-    const TreeNodeRRsetPtr rrset;
-    const ZoneFinder::FindResultFlags flags;
-    const ZoneNode* const found_node;
-};
+namespace internal {
+// intermediate result context, only used in the zone finder implementation.
+class ZoneFinderResultContext;
+}
 
 
 /// A derived zone finder class intended to be used with the memory data
 /// A derived zone finder class intended to be used with the memory data
 /// source, using ZoneData for its contents.
 /// source, using ZoneData for its contents.
@@ -92,16 +77,13 @@ public:
     findNSEC3(const isc::dns::Name& name, bool recursive);
     findNSEC3(const isc::dns::Name& name, bool recursive);
 
 
     /// \brief Returns the origin of the zone.
     /// \brief Returns the origin of the zone.
-    virtual isc::dns::Name getOrigin() const {
-        return zone_data_.getOriginNode()->getName();
-    }
+    virtual isc::dns::Name getOrigin() const;
 
 
     /// \brief Returns the RR class of the zone.
     /// \brief Returns the RR class of the zone.
     virtual isc::dns::RRClass getClass() const {
     virtual isc::dns::RRClass getClass() const {
-        return rrclass_;
+        return (rrclass_);
     }
     }
 
 
-
 private:
 private:
     /// \brief In-memory version of finder context.
     /// \brief In-memory version of finder context.
     ///
     ///
@@ -110,7 +92,7 @@ private:
     class Context;
     class Context;
 
 
     /// Actual implementation for both find() and findAll()
     /// Actual implementation for both find() and findAll()
-    ZoneFinderResultContext find_internal(
+    internal::ZoneFinderResultContext find_internal(
         const isc::dns::Name& name,
         const isc::dns::Name& name,
         const isc::dns::RRType& type,
         const isc::dns::RRType& type,
         std::vector<isc::dns::ConstRRsetPtr>* target,
         std::vector<isc::dns::ConstRRsetPtr>* target,
@@ -118,7 +100,7 @@ private:
         FIND_DEFAULT);
         FIND_DEFAULT);
 
 
     const ZoneData& zone_data_;
     const ZoneData& zone_data_;
-    const isc::dns::RRClass& rrclass_;
+    const isc::dns::RRClass rrclass_;
 };
 };
 
 
 } // namespace memory
 } // namespace memory

+ 1 - 1
src/lib/datasrc/tests/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = testdata
+SUBDIRS = . memory testdata
 
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns

+ 91 - 60
src/lib/datasrc/tests/client_list_unittest.cc

@@ -12,11 +12,14 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+#include <util/memory_segment_local.h>
+
 #include <datasrc/client_list.h>
 #include <datasrc/client_list.h>
 #include <datasrc/client.h>
 #include <datasrc/client.h>
 #include <datasrc/iterator.h>
 #include <datasrc/iterator.h>
 #include <datasrc/data_source.h>
 #include <datasrc/data_source.h>
-#include <datasrc/memory_datasrc.h>
+#include <datasrc/memory/memory_client.h>
+#include <datasrc/memory/zone_finder.h>
 
 
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
 #include <dns/rrttl.h>
 #include <dns/rrttl.h>
@@ -28,6 +31,8 @@
 #include <fstream>
 #include <fstream>
 
 
 using namespace isc::datasrc;
 using namespace isc::datasrc;
+using isc::datasrc::memory::InMemoryClient;
+using isc::datasrc::memory::InMemoryZoneFinder;
 using namespace isc::data;
 using namespace isc::data;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace boost;
 using namespace boost;
@@ -67,35 +72,47 @@ public:
     };
     };
     class Iterator : public ZoneIterator {
     class Iterator : public ZoneIterator {
     public:
     public:
-        Iterator(const Name& origin) :
+        Iterator(const Name& origin, bool include_ns) :
             origin_(origin),
             origin_(origin),
-            finished_(false),
-            soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(), RRTTL(3600)))
+            soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(),
+                           RRTTL(3600)))
         {
         {
             // The RData here is bogus, but it is not used to anything. There
             // The RData here is bogus, but it is not used to anything. There
             // just needs to be some.
             // just needs to be some.
             soa_->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
             soa_->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
                                                Name::ROOT_NAME(),
                                                Name::ROOT_NAME(),
                                                0, 0, 0, 0, 0));
                                                0, 0, 0, 0, 0));
+            rrsets_.push_back(soa_);
+
+            if (include_ns) {
+                ns_.reset(new RRset(origin_, RRClass::IN(), RRType::NS(),
+                                    RRTTL(3600)));
+                ns_->addRdata(rdata::generic::NS(Name::ROOT_NAME()));
+                rrsets_.push_back(ns_);
+            }
+            rrsets_.push_back(ConstRRsetPtr());
+
+            it_ = rrsets_.begin();
         }
         }
         virtual isc::dns::ConstRRsetPtr getNextRRset() {
         virtual isc::dns::ConstRRsetPtr getNextRRset() {
-            if (finished_) {
-                return (ConstRRsetPtr());
-            } else {
-                finished_ = true;
-                return (soa_);
-            }
+            ConstRRsetPtr result = *it_;
+            ++it_;
+            return (result);
         }
         }
         virtual isc::dns::ConstRRsetPtr getSOA() const {
         virtual isc::dns::ConstRRsetPtr getSOA() const {
             return (soa_);
             return (soa_);
         }
         }
     private:
     private:
         const Name origin_;
         const Name origin_;
-        bool finished_;
-        const isc::dns::RRsetPtr soa_;
+        const RRsetPtr soa_;
+        RRsetPtr ns_;
+        std::vector<ConstRRsetPtr> rrsets_;
+        std::vector<ConstRRsetPtr>::const_iterator it_;
     };
     };
     // Constructor from a list of zones.
     // Constructor from a list of zones.
-    MockDataSourceClient(const char* zone_names[]) {
+    MockDataSourceClient(const char* zone_names[]) :
+        have_ns_(true), use_baditerator_(true)
+    {
         for (const char** zone(zone_names); *zone; ++zone) {
         for (const char** zone(zone_names); *zone; ++zone) {
             zones.insert(Name(*zone));
             zones.insert(Name(*zone));
         }
         }
@@ -105,7 +122,8 @@ public:
     MockDataSourceClient(const string& type,
     MockDataSourceClient(const string& type,
                          const ConstElementPtr& configuration) :
                          const ConstElementPtr& configuration) :
         type_(type),
         type_(type),
-        configuration_(configuration)
+        configuration_(configuration),
+        have_ns_(true), use_baditerator_(true)
     {
     {
         EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
         EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
             "and it never should be created as a data source client";
             "and it never should be created as a data source client";
@@ -146,23 +164,27 @@ public:
         isc_throw(isc::NotImplemented, "Not implemented");
         isc_throw(isc::NotImplemented, "Not implemented");
     }
     }
     virtual ZoneIteratorPtr getIterator(const Name& name, bool) const {
     virtual ZoneIteratorPtr getIterator(const Name& name, bool) const {
-        if (name == Name("noiter.org")) {
+        if (use_baditerator_ && name == Name("noiter.org")) {
             isc_throw(isc::NotImplemented, "Asked not to be implemented");
             isc_throw(isc::NotImplemented, "Asked not to be implemented");
-        } else if (name == Name("null.org")) {
+        } else if (use_baditerator_ && name == Name("null.org")) {
             return (ZoneIteratorPtr());
             return (ZoneIteratorPtr());
         } else {
         } else {
             FindResult result(findZone(name));
             FindResult result(findZone(name));
             if (result.code == isc::datasrc::result::SUCCESS) {
             if (result.code == isc::datasrc::result::SUCCESS) {
-                return (ZoneIteratorPtr(new Iterator(name)));
+                return (ZoneIteratorPtr(new Iterator(name, have_ns_)));
             } else {
             } else {
                 isc_throw(DataSourceError, "No such zone");
                 isc_throw(DataSourceError, "No such zone");
             }
             }
         }
         }
     }
     }
+    void disableNS() { have_ns_ = false; }
+    void disableBadIterator() { use_baditerator_ = false; }
     const string type_;
     const string type_;
     const ConstElementPtr configuration_;
     const ConstElementPtr configuration_;
 private:
 private:
     set<Name> zones;
     set<Name> zones;
+    bool have_ns_; // control the iterator behavior wrt whether to include NS
+    bool use_baditerator_; // whether to use bogus zone iterators for tests
 };
 };
 
 
 
 
@@ -220,8 +242,9 @@ const size_t ds_count = (sizeof(ds_zones) / sizeof(*ds_zones));
 class ListTest : public ::testing::Test {
 class ListTest : public ::testing::Test {
 public:
 public:
     ListTest() :
     ListTest() :
+        rrclass_(RRClass::IN()),
         // The empty list corresponds to a list with no elements inside
         // The empty list corresponds to a list with no elements inside
-        list_(new TestedList(RRClass::IN())),
+        list_(new TestedList(rrclass_)),
         config_elem_(Element::fromJSON("["
         config_elem_(Element::fromJSON("["
             "{"
             "{"
             "   \"type\": \"test_type\","
             "   \"type\": \"test_type\","
@@ -238,28 +261,35 @@ public:
             shared_ptr<MockDataSourceClient>
             shared_ptr<MockDataSourceClient>
                 ds(new MockDataSourceClient(ds_zones[i]));
                 ds(new MockDataSourceClient(ds_zones[i]));
             ds_.push_back(ds);
             ds_.push_back(ds);
-            ds_info_.push_back(ConfigurableClientList::DataSourceInfo(ds.get(),
-                DataSourceClientContainerPtr(), false));
+            ds_info_.push_back(ConfigurableClientList::DataSourceInfo(
+                                   ds.get(), DataSourceClientContainerPtr(),
+                                   false, rrclass_, mem_sgmt_));
         }
         }
     }
     }
-    void prepareCache(size_t index, const Name& zone, bool prefill = false) {
-        const shared_ptr<InMemoryClient> cache(new InMemoryClient());
-        const shared_ptr<InMemoryZoneFinder>
-            finder(new InMemoryZoneFinder(RRClass::IN(), zone));
-        if (prefill) {
-            RRsetPtr soa(new RRset(zone, RRClass::IN(), RRType::SOA(),
-                                   RRTTL(3600)));
-            // The RData here is bogus, but it is not used to anything. There
-            // just needs to be some.
-            soa->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
-                                              Name::ROOT_NAME(),
-                                              0, 0, 0, 0, 0));
-            finder->add(soa);
-        }
-        // If we don't do prefill, we leave the zone empty. This way,
-        // we can check when it was reloaded.
-        cache->addZone(finder);
-        list_->getDataSources()[index].cache_ = cache;
+
+    // Install a "fake" cached zone using a temporary underlying data source
+    // client.
+    void prepareCache(size_t index, const Name& zone) {
+        // Prepare the temporary data source client
+        const char* zones[2];
+        const std::string zonename_txt = zone.toText();
+        zones[0] = zonename_txt.c_str();
+        zones[1] = NULL;
+        MockDataSourceClient mock_client(zones);
+        // Disable some default features of the mock to distinguish the
+        // temporary case from normal case.
+        mock_client.disableNS();
+        mock_client.disableBadIterator();
+
+        // Create cache from the temporary data source, and push it to the
+        // client list.
+        const shared_ptr<InMemoryClient> cache(new InMemoryClient(mem_sgmt_,
+                                                                  rrclass_));
+        cache->load(zone, *mock_client.getIterator(zone, false));
+
+        ConfigurableClientList::DataSourceInfo& dsrc_info =
+                list_->getDataSources()[index];
+        dsrc_info.cache_ = cache;
     }
     }
     // Check the positive result is as we expect it.
     // Check the positive result is as we expect it.
     void positiveResult(const ClientList::FindResult& result,
     void positiveResult(const ClientList::FindResult& result,
@@ -331,6 +361,8 @@ public:
         EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
         EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
                   shared_ptr<InMemoryClient>());
                   shared_ptr<InMemoryClient>());
     }
     }
+    const RRClass rrclass_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
     shared_ptr<TestedList> list_;
     shared_ptr<TestedList> list_;
     const ClientList::FindResult negative_result_;
     const ClientList::FindResult negative_result_;
     vector<shared_ptr<MockDataSourceClient> > ds_;
     vector<shared_ptr<MockDataSourceClient> > ds_;
@@ -815,39 +847,38 @@ TEST_F(ListTest, BadMasterFile) {
 // Test we can reload a zone
 // Test we can reload a zone
 TEST_F(ListTest, reloadSuccess) {
 TEST_F(ListTest, reloadSuccess) {
     list_->configure(config_elem_zones_, true);
     list_->configure(config_elem_zones_, true);
-    Name name("example.org");
+    const Name name("example.org");
     prepareCache(0, name);
     prepareCache(0, name);
-    // Not there yet. It would be NXDOMAIN, but it is in apex and
-    // it returns NXRRSET instead.
+    // The cache currently contains a tweaked version of zone, which doesn't
+    // have apex NS.  So the lookup should result in NXRRSET.
     EXPECT_EQ(ZoneFinder::NXRRSET,
     EXPECT_EQ(ZoneFinder::NXRRSET,
-              list_->find(name).finder_->find(name, RRType::SOA())->code);
-    // Now reload. It should be there now.
+              list_->find(name).finder_->find(name, RRType::NS())->code);
+    // Now reload the full zone. It should be there now.
     EXPECT_EQ(ConfigurableClientList::ZONE_RELOADED, list_->reload(name));
     EXPECT_EQ(ConfigurableClientList::ZONE_RELOADED, list_->reload(name));
     EXPECT_EQ(ZoneFinder::SUCCESS,
     EXPECT_EQ(ZoneFinder::SUCCESS,
-              list_->find(name).finder_->find(name, RRType::SOA())->code);
+              list_->find(name).finder_->find(name, RRType::NS())->code);
 }
 }
 
 
 // The cache is not enabled. The load should be rejected.
 // The cache is not enabled. The load should be rejected.
 TEST_F(ListTest, reloadNotEnabled) {
 TEST_F(ListTest, reloadNotEnabled) {
     list_->configure(config_elem_zones_, false);
     list_->configure(config_elem_zones_, false);
-    Name name("example.org");
+    const Name name("example.org");
     // We put the cache in even when not enabled. This won't confuse the thing.
     // We put the cache in even when not enabled. This won't confuse the thing.
     prepareCache(0, name);
     prepareCache(0, name);
-    // Not there yet. It would be NXDOMAIN, but it is in apex and
-    // it returns NXRRSET instead.
+    // See the reloadSuccess test.  This should result in NXRRSET.
     EXPECT_EQ(ZoneFinder::NXRRSET,
     EXPECT_EQ(ZoneFinder::NXRRSET,
-              list_->find(name).finder_->find(name, RRType::SOA())->code);
+              list_->find(name).finder_->find(name, RRType::NS())->code);
     // Now reload. It should reject it.
     // Now reload. It should reject it.
     EXPECT_EQ(ConfigurableClientList::CACHE_DISABLED, list_->reload(name));
     EXPECT_EQ(ConfigurableClientList::CACHE_DISABLED, list_->reload(name));
     // Nothing changed here
     // Nothing changed here
     EXPECT_EQ(ZoneFinder::NXRRSET,
     EXPECT_EQ(ZoneFinder::NXRRSET,
-              list_->find(name).finder_->find(name, RRType::SOA())->code);
+              list_->find(name).finder_->find(name, RRType::NS())->code);
 }
 }
 
 
 // Test several cases when the zone does not exist
 // Test several cases when the zone does not exist
 TEST_F(ListTest, reloadNoSuchZone) {
 TEST_F(ListTest, reloadNoSuchZone) {
     list_->configure(config_elem_zones_, true);
     list_->configure(config_elem_zones_, true);
-    Name name("example.org");
+    const Name name("example.org");
     // We put the cache in even when not enabled. This won't confuse the
     // We put the cache in even when not enabled. This won't confuse the
     // reload method, as that one looks at the real state of things, not
     // reload method, as that one looks at the real state of things, not
     // at the configuration.
     // at the configuration.
@@ -867,27 +898,27 @@ TEST_F(ListTest, reloadNoSuchZone) {
               list_->find(Name("example.cz")).dsrc_client_);
               list_->find(Name("example.cz")).dsrc_client_);
     EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
     EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
               list_->find(Name("sub.example.com"), true).dsrc_client_);
               list_->find(Name("sub.example.com"), true).dsrc_client_);
-    // Not reloaded
+    // Not reloaded, so NS shouldn't be visible yet.
     EXPECT_EQ(ZoneFinder::NXRRSET,
     EXPECT_EQ(ZoneFinder::NXRRSET,
               list_->find(Name("example.com")).finder_->
               list_->find(Name("example.com")).finder_->
-              find(Name("example.com"), RRType::SOA())->code);
+              find(Name("example.com"), RRType::NS())->code);
 }
 }
 
 
 // Check we gracefuly throw an exception when a zone disappeared in
 // Check we gracefuly throw an exception when a zone disappeared in
 // the underlying data source when we want to reload it
 // the underlying data source when we want to reload it
 TEST_F(ListTest, reloadZoneGone) {
 TEST_F(ListTest, reloadZoneGone) {
     list_->configure(config_elem_, true);
     list_->configure(config_elem_, true);
-    Name name("example.org");
+    const Name name("example.org");
     // We put in a cache for non-existant zone. This emulates being loaded
     // We put in a cache for non-existant zone. This emulates being loaded
     // and then the zone disappearing. We prefill the cache, so we can check
     // and then the zone disappearing. We prefill the cache, so we can check
     // it.
     // it.
-    prepareCache(0, name, true);
-    // The zone contains something
+    prepareCache(0, name);
+    // The (cached) zone contains zone's SOA
     EXPECT_EQ(ZoneFinder::SUCCESS,
     EXPECT_EQ(ZoneFinder::SUCCESS,
               list_->find(name).finder_->find(name, RRType::SOA())->code);
               list_->find(name).finder_->find(name, RRType::SOA())->code);
     // The zone is not there, so abort the reload.
     // The zone is not there, so abort the reload.
     EXPECT_THROW(list_->reload(name), DataSourceError);
     EXPECT_THROW(list_->reload(name), DataSourceError);
-    // The zone is not hurt.
+    // The (cached) zone is not hurt.
     EXPECT_EQ(ZoneFinder::SUCCESS,
     EXPECT_EQ(ZoneFinder::SUCCESS,
               list_->find(name).finder_->find(name, RRType::SOA())->code);
               list_->find(name).finder_->find(name, RRType::SOA())->code);
 }
 }
@@ -895,8 +926,8 @@ TEST_F(ListTest, reloadZoneGone) {
 // The underlying data source throws. Check we don't modify the state.
 // The underlying data source throws. Check we don't modify the state.
 TEST_F(ListTest, reloadZoneThrow) {
 TEST_F(ListTest, reloadZoneThrow) {
     list_->configure(config_elem_zones_, true);
     list_->configure(config_elem_zones_, true);
-    Name name("noiter.org");
-    prepareCache(0, name, true);
+    const Name name("noiter.org");
+    prepareCache(0, name);
     // The zone contains stuff now
     // The zone contains stuff now
     EXPECT_EQ(ZoneFinder::SUCCESS,
     EXPECT_EQ(ZoneFinder::SUCCESS,
               list_->find(name).finder_->find(name, RRType::SOA())->code);
               list_->find(name).finder_->find(name, RRType::SOA())->code);
@@ -909,8 +940,8 @@ TEST_F(ListTest, reloadZoneThrow) {
 
 
 TEST_F(ListTest, reloadNullIterator) {
 TEST_F(ListTest, reloadNullIterator) {
     list_->configure(config_elem_zones_, true);
     list_->configure(config_elem_zones_, true);
-    Name name("null.org");
-    prepareCache(0, name, true);
+    const Name name("null.org");
+    prepareCache(0, name);
     // The zone contains stuff now
     // The zone contains stuff now
     EXPECT_EQ(ZoneFinder::SUCCESS,
     EXPECT_EQ(ZoneFinder::SUCCESS,
               list_->find(name).finder_->find(name, RRType::SOA())->code);
               list_->find(name).finder_->find(name, RRType::SOA())->code);

+ 17 - 0
src/lib/datasrc/tests/faked_nsec3.cc

@@ -67,6 +67,17 @@ public:
             "00000000000000000000000000000000";
             "00000000000000000000000000000000";
         map_[Name("largest.example.org")] =
         map_[Name("largest.example.org")] =
             "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
             "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
+
+        // These are used by the findNSEC3Walk test.
+        map_[Name("n0.example.org")] = "00000000000000000000000000000000";
+        map_[Name("n1.example.org")] = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+        map_[Name("n2.example.org")] = "02UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+        map_[Name("n3.example.org")] = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+        map_[Name("n4.example.org")] = "11111111111111111111111111111111";
+        map_[Name("n5.example.org")] = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
+        map_[Name("n6.example.org")] = "44444444444444444444444444444444";
+        map_[Name("n7.example.org")] = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
+        map_[Name("n8.example.org")] = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
     }
     }
     virtual string calculate(const Name& name) const {
     virtual string calculate(const Name& name) const {
         const NSEC3HashMap::const_iterator found = map_.find(name);
         const NSEC3HashMap::const_iterator found = map_.find(name);
@@ -94,6 +105,12 @@ NSEC3Hash* TestNSEC3HashCreator::create(const rdata::generic::NSEC3&) const {
     return (new TestNSEC3Hash);
     return (new TestNSEC3Hash);
 }
 }
 
 
+NSEC3Hash* TestNSEC3HashCreator::create(uint8_t, uint16_t,
+                                        const uint8_t*, size_t) const
+{
+    return (new TestNSEC3Hash);
+}
+
 void
 void
 findNSEC3Check(bool expected_matched, uint8_t expected_labels,
 findNSEC3Check(bool expected_matched, uint8_t expected_labels,
                const string& expected_closest,
                const string& expected_closest,

+ 4 - 0
src/lib/datasrc/tests/faked_nsec3.h

@@ -57,11 +57,15 @@ class TestNSEC3HashCreator : public isc::dns::NSEC3HashCreator {
 private:
 private:
     class TestNSEC3Hash;
     class TestNSEC3Hash;
 public:
 public:
+    TestNSEC3HashCreator() {}
     virtual isc::dns::NSEC3Hash* create(const
     virtual isc::dns::NSEC3Hash* create(const
                                         isc::dns::rdata::generic::NSEC3PARAM&)
                                         isc::dns::rdata::generic::NSEC3PARAM&)
         const;
         const;
     virtual isc::dns::NSEC3Hash* create(const isc::dns::rdata::generic::NSEC3&)
     virtual isc::dns::NSEC3Hash* create(const isc::dns::rdata::generic::NSEC3&)
         const;
         const;
+    virtual isc::dns::NSEC3Hash* create(uint8_t, uint16_t,
+                                        const uint8_t*, size_t)
+        const;
 };
 };
 
 
 // Check the result against expected values. It directly calls EXPECT_ macros
 // Check the result against expected values. It directly calls EXPECT_ macros

src/lib/datasrc/memory/tests/.gitignore → src/lib/datasrc/tests/memory/.gitignore


+ 1 - 2
src/lib/datasrc/memory/tests/Makefile.am

@@ -36,8 +36,7 @@ run_unittests_SOURCES += memory_client_unittest.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
 
-run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la
-run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
+run_unittests_LDADD = $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la

+ 62 - 0
src/lib/datasrc/memory/tests/domaintree_unittest.cc

@@ -596,6 +596,10 @@ TEST_F(DomainTreeTest, chainLevel) {
     // by default there should be no level in the chain.
     // by default there should be no level in the chain.
     EXPECT_EQ(0, chain.getLevelCount());
     EXPECT_EQ(0, chain.getLevelCount());
 
 
+    // Copy should be consistent
+    TestDomainTreeNodeChain chain2(chain);
+    EXPECT_EQ(chain.getLevelCount(), chain2.getLevelCount());
+
     // insert one node to the tree and find it.  there should be exactly
     // insert one node to the tree and find it.  there should be exactly
     // one level in the chain.
     // one level in the chain.
     TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
     TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
@@ -607,6 +611,11 @@ TEST_F(DomainTreeTest, chainLevel) {
               tree.find(node_name, &cdtnode, chain));
               tree.find(node_name, &cdtnode, chain));
     EXPECT_EQ(1, chain.getLevelCount());
     EXPECT_EQ(1, chain.getLevelCount());
 
 
+    // Copy should be consistent
+    TestDomainTreeNodeChain chain3(chain);
+    EXPECT_EQ(chain.getLevelCount(), chain3.getLevelCount());
+    EXPECT_EQ(chain.getAbsoluteName(), chain3.getAbsoluteName());
+
     // Check the name of the found node (should have '.' as both non-absolute
     // Check the name of the found node (should have '.' as both non-absolute
     // and absolute name
     // and absolute name
     EXPECT_EQ(".", cdtnode->getLabels().toText());
     EXPECT_EQ(".", cdtnode->getLabels().toText());
@@ -716,6 +725,48 @@ TEST_F(DomainTreeTest, getUpperNode) {
     EXPECT_EQ(static_cast<void*>(NULL), node);
     EXPECT_EQ(static_cast<void*>(NULL), node);
 }
 }
 
 
+
+#if 0
+// Disabled and kept still, for use in case we make getSubTreeRoot() a
+// public function again.
+
+const char* const subtree_root_node_names[] = {
+    "b", "b", "b", "b", "w.y.d.e.f", "w.y.d.e.f", "p.w.y.d.e.f",
+    "p.w.y.d.e.f", "p.w.y.d.e.f", "w.y.d.e.f", "j.z.d.e.f",
+    "b", "i.g.h", "i.g.h"};
+
+TEST_F(DomainTreeTest, getSubTreeRoot) {
+    TestDomainTreeNodeChain node_path;
+    const TestDomainTreeNode* node = NULL;
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name(names[0]),
+                                            &node,
+                                            node_path));
+    for (int i = 0; i < name_count; ++i) {
+        EXPECT_NE(static_cast<void*>(NULL), node);
+
+        const TestDomainTreeNode* sr_node = node->getSubTreeRoot();
+        if (subtree_root_node_names[i] != NULL) {
+            const TestDomainTreeNode* sr_node2 = NULL;
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                dtree_expose_empty_node.find(Name(subtree_root_node_names[i]),
+                                             &sr_node2));
+            EXPECT_NE(static_cast<void*>(NULL), sr_node2);
+            EXPECT_EQ(sr_node, sr_node2);
+        } else {
+            EXPECT_EQ(static_cast<void*>(NULL), sr_node);
+        }
+
+        node = dtree_expose_empty_node.nextNode(node_path);
+    }
+
+    // We should have reached the end of the tree.
+    EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+#endif // disabled getSubTreeRoot()
+
+
 TEST_F(DomainTreeTest, nextNode) {
 TEST_F(DomainTreeTest, nextNode) {
     TestDomainTreeNodeChain node_path;
     TestDomainTreeNodeChain node_path;
     const TestDomainTreeNode* node = NULL;
     const TestDomainTreeNode* node = NULL;
@@ -937,6 +988,17 @@ TEST_F(DomainTreeTest, previousNode) {
     }
     }
 }
 }
 
 
+TEST_F(DomainTreeTest, largestNode) {
+    cdtnode = dtree.largestNode();
+    EXPECT_EQ(Name("k"), cdtnode->getName());
+
+    // Check for largest node in an empty tree.
+    TreeHolder empty_tree_holder
+        (mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+    TestDomainTree& empty_tree(*empty_tree_holder.get());
+    EXPECT_EQ(static_cast<void*>(NULL), empty_tree.largestNode());
+}
+
 TEST_F(DomainTreeTest, nextNodeError) {
 TEST_F(DomainTreeTest, nextNodeError) {
     // Empty chain for nextNode() is invalid.
     // Empty chain for nextNode() is invalid.
     TestDomainTreeNodeChain chain;
     TestDomainTreeNodeChain chain;

+ 43 - 33
src/lib/datasrc/memory/tests/memory_client_unittest.cc

@@ -200,6 +200,11 @@ TEST_F(MemoryClientTest, load) {
     // should not result in any exceptions.
     // should not result in any exceptions.
     client_->load(Name("example.org"),
     client_->load(Name("example.org"),
                   TEST_DATA_DIR "/example.org.zone");
                   TEST_DATA_DIR "/example.org.zone");
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+    EXPECT_FALSE(zone_data->isSigned());
+    EXPECT_FALSE(zone_data->isNSEC3Signed());
 }
 }
 
 
 TEST_F(MemoryClientTest, loadFromIterator) {
 TEST_F(MemoryClientTest, loadFromIterator) {
@@ -266,11 +271,33 @@ TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
 TEST_F(MemoryClientTest, loadNSEC3Signed) {
 TEST_F(MemoryClientTest, loadNSEC3Signed) {
     client_->load(Name("example.org"),
     client_->load(Name("example.org"),
                   TEST_DATA_DIR "/example.org-nsec3-signed.zone");
                   TEST_DATA_DIR "/example.org-nsec3-signed.zone");
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+    EXPECT_TRUE(zone_data->isSigned());
+    EXPECT_TRUE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadNSEC3EmptySalt) {
+    // Load NSEC3 with empty ("-") salt. This should not throw or crash
+    // or anything.
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-nsec3-empty-salt.zone");
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+    EXPECT_TRUE(zone_data->isSigned());
+    EXPECT_TRUE(zone_data->isNSEC3Signed());
 }
 }
 
 
 TEST_F(MemoryClientTest, loadNSEC3SignedNoParam) {
 TEST_F(MemoryClientTest, loadNSEC3SignedNoParam) {
     client_->load(Name("example.org"),
     client_->load(Name("example.org"),
                   TEST_DATA_DIR "/example.org-nsec3-signed-no-param.zone");
                   TEST_DATA_DIR "/example.org-nsec3-signed-no-param.zone");
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+    EXPECT_TRUE(zone_data->isSigned());
+    EXPECT_TRUE(zone_data->isNSEC3Signed());
 }
 }
 
 
 TEST_F(MemoryClientTest, loadReloadZone) {
 TEST_F(MemoryClientTest, loadReloadZone) {
@@ -288,14 +315,12 @@ TEST_F(MemoryClientTest, loadReloadZone) {
                   client_->getFileName(Name("example.org")));
                   client_->getFileName(Name("example.org")));
     EXPECT_EQ(1, client_->getZoneCount());
     EXPECT_EQ(1, client_->getZoneCount());
 
 
-    isc::datasrc::memory::ZoneTable::FindResult
-        result(client_->findZone2(Name("example.org")));
-    EXPECT_EQ(result::SUCCESS, result.code);
-    EXPECT_NE(static_cast<ZoneData*>(NULL),
-              result.zone_data);
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
 
 
     /* Check SOA */
     /* Check SOA */
-    const ZoneNode* node = result.zone_data->getOriginNode();
+    const ZoneNode* node = zone_data->getOriginNode();
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
 
 
     const RdataSet* set = node->getData();
     const RdataSet* set = node->getData();
@@ -306,7 +331,7 @@ TEST_F(MemoryClientTest, loadReloadZone) {
     EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
     EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
 
 
     /* Check ns1.example.org */
     /* Check ns1.example.org */
-    const ZoneTree& tree = result.zone_data->getZoneTree();
+    const ZoneTree& tree = zone_data->getZoneTree();
     ZoneTree::Result zresult(tree.find(Name("ns1.example.org"), &node));
     ZoneTree::Result zresult(tree.find(Name("ns1.example.org"), &node));
     EXPECT_NE(ZoneTree::EXACTMATCH, zresult);
     EXPECT_NE(ZoneTree::EXACTMATCH, zresult);
 
 
@@ -316,14 +341,11 @@ TEST_F(MemoryClientTest, loadReloadZone) {
                   TEST_DATA_DIR "/example.org-rrsigs.zone");
                   TEST_DATA_DIR "/example.org-rrsigs.zone");
     EXPECT_EQ(1, client_->getZoneCount());
     EXPECT_EQ(1, client_->getZoneCount());
 
 
-    isc::datasrc::memory::ZoneTable::FindResult
-        result2(client_->findZone2(Name("example.org")));
-    EXPECT_EQ(result::SUCCESS, result2.code);
-    EXPECT_NE(static_cast<ZoneData*>(NULL),
-              result2.zone_data);
+    zone_data = client_->findZoneData(Name("example.org"));
+    EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
 
 
     /* Check SOA */
     /* Check SOA */
-    node = result2.zone_data->getOriginNode();
+    node = zone_data->getOriginNode();
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
 
 
     set = node->getData();
     set = node->getData();
@@ -334,7 +356,7 @@ TEST_F(MemoryClientTest, loadReloadZone) {
     EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
     EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
 
 
     /* Check ns1.example.org */
     /* Check ns1.example.org */
-    const ZoneTree& tree2 = result2.zone_data->getZoneTree();
+    const ZoneTree& tree2 = zone_data->getZoneTree();
     ZoneTree::Result zresult2(tree2.find(Name("ns1.example.org"), &node));
     ZoneTree::Result zresult2(tree2.find(Name("ns1.example.org"), &node));
     EXPECT_EQ(ZoneTree::EXACTMATCH, zresult2);
     EXPECT_EQ(ZoneTree::EXACTMATCH, zresult2);
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
@@ -702,30 +724,18 @@ TEST_F(MemoryClientTest, add) {
     EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
     EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
 }
 }
 
 
-TEST_F(MemoryClientTest, findZoneThrowsNotImplemented) {
-    // This method is not implemented.
-    EXPECT_THROW(client_->findZone(Name(".")),
-                 isc::NotImplemented);
-}
-
-TEST_F(MemoryClientTest, findZone2) {
+TEST_F(MemoryClientTest, findZoneData) {
     client_->load(Name("example.org"),
     client_->load(Name("example.org"),
                   TEST_DATA_DIR "/example.org-rrsigs.zone");
                   TEST_DATA_DIR "/example.org-rrsigs.zone");
 
 
-    isc::datasrc::memory::ZoneTable::FindResult
-        result(client_->findZone2(Name("example.com")));
-    EXPECT_EQ(result::NOTFOUND, result.code);
-    EXPECT_EQ(static_cast<ZoneData*>(NULL),
-              result.zone_data);
+    const ZoneData* zone_data = client_->findZoneData(Name("example.com"));
+    EXPECT_EQ(static_cast<const ZoneData*>(NULL), zone_data);
 
 
-    isc::datasrc::memory::ZoneTable::FindResult
-        result2(client_->findZone2(Name("example.org")));
-    EXPECT_EQ(result::SUCCESS, result2.code);
-    EXPECT_NE(static_cast<ZoneData*>(NULL),
-              result2.zone_data);
+    zone_data = client_->findZoneData(Name("example.org"));
+    EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
 
 
     /* Check SOA */
     /* Check SOA */
-    const ZoneNode* node = result2.zone_data->getOriginNode();
+    const ZoneNode* node = zone_data->getOriginNode();
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
 
 
     const RdataSet* set = node->getData();
     const RdataSet* set = node->getData();
@@ -736,7 +746,7 @@ TEST_F(MemoryClientTest, findZone2) {
     EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
     EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
 
 
     /* Check ns1.example.org */
     /* Check ns1.example.org */
-    const ZoneTree& tree = result2.zone_data->getZoneTree();
+    const ZoneTree& tree = zone_data->getZoneTree();
     ZoneTree::Result result3(tree.find(Name("ns1.example.org"), &node));
     ZoneTree::Result result3(tree.find(Name("ns1.example.org"), &node));
     EXPECT_EQ(ZoneTree::EXACTMATCH, result3);
     EXPECT_EQ(ZoneTree::EXACTMATCH, result3);
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
     EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);

src/lib/datasrc/memory/tests/memory_segment_test.h → src/lib/datasrc/tests/memory/memory_segment_test.h


+ 1 - 1
src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc

@@ -52,7 +52,7 @@ namespace isc {
 namespace datasrc{
 namespace datasrc{
 namespace memory {
 namespace memory {
 
 
-#include "../rdata_serialization_priv.cc"
+#include <datasrc/memory/rdata_serialization_priv.cc>
 
 
 }
 }
 }
 }

src/lib/datasrc/memory/tests/rdataset_unittest.cc → src/lib/datasrc/tests/memory/rdataset_unittest.cc


src/lib/datasrc/memory/tests/run_unittests.cc → src/lib/datasrc/tests/memory/run_unittests.cc


src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc → src/lib/datasrc/tests/memory/segment_object_holder_unittest.cc


+ 1 - 0
src/lib/datasrc/memory/tests/testdata/Makefile.am

@@ -19,6 +19,7 @@ EXTRA_DIST += example.org-multiple-dname.zone
 EXTRA_DIST += example.org-multiple-nsec3.zone
 EXTRA_DIST += example.org-multiple-nsec3.zone
 EXTRA_DIST += example.org-multiple-nsec3param.zone
 EXTRA_DIST += example.org-multiple-nsec3param.zone
 EXTRA_DIST += example.org-multiple.zone
 EXTRA_DIST += example.org-multiple.zone
+EXTRA_DIST += example.org-nsec3-empty-salt.zone
 EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone
 EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone
 EXTRA_DIST += example.org-nsec3-signed-no-param.zone
 EXTRA_DIST += example.org-nsec3-signed-no-param.zone
 EXTRA_DIST += example.org-nsec3-signed.zone
 EXTRA_DIST += example.org-nsec3-signed.zone

src/lib/datasrc/memory/tests/testdata/empty.zone → src/lib/datasrc/tests/memory/testdata/empty.zone


src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone → src/lib/datasrc/tests/memory/testdata/example.org-broken1.zone


src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone → src/lib/datasrc/tests/memory/testdata/example.org-broken2.zone


src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone → src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-1.zone


src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone → src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-2.zone


src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone → src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-1.zone


src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone → src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-2.zone


src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone → src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-1.zone


src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone → src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-2.zone


src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone → src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type-bad.zone


src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone → src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type.zone


src/lib/datasrc/memory/tests/testdata/example.org-empty.zone → src/lib/datasrc/tests/memory/testdata/example.org-empty.zone


src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone → src/lib/datasrc/tests/memory/testdata/example.org-multiple-cname.zone


src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone → src/lib/datasrc/tests/memory/testdata/example.org-multiple-dname.zone


src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone → src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3.zone


src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone → src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3param.zone


src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone → src/lib/datasrc/tests/memory/testdata/example.org-multiple.zone


+ 14 - 0
src/lib/datasrc/tests/memory/testdata/example.org-nsec3-empty-salt.zone

@@ -0,0 +1,14 @@
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012092602 7200 3600 2592000 1200
+example.org.				      86400 IN RRSIG	SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org.				      86400 IN NS	ns.example.org.
+example.org.				      86400 IN RRSIG	NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org.				      86400 IN DNSKEY	256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org.				      86400 IN RRSIG	DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+example.org.				      0	IN NSEC3PARAM	1 0 10 -
+example.org.				      0	IN RRSIG	NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org.				      86400 IN A	192.0.2.1
+ns.example.org.				      86400 IN RRSIG	A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 - RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3	1 0 10 - 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=

src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone → src/lib/datasrc/tests/memory/testdata/example.org-nsec3-fewer-labels.zone


src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone → src/lib/datasrc/tests/memory/testdata/example.org-nsec3-more-labels.zone


src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone → src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed-no-param.zone


src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone → src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed.zone


src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone → src/lib/datasrc/tests/memory/testdata/example.org-out-of-zone.zone


src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone → src/lib/datasrc/tests/memory/testdata/example.org-rrsig-follows-nothing.zone


src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone → src/lib/datasrc/tests/memory/testdata/example.org-rrsigs.zone


src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone → src/lib/datasrc/tests/memory/testdata/example.org-wildcard-dname.zone


src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone → src/lib/datasrc/tests/memory/testdata/example.org-wildcard-ns.zone


src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone → src/lib/datasrc/tests/memory/testdata/example.org-wildcard-nsec3.zone


src/lib/datasrc/memory/tests/testdata/example.org.zone → src/lib/datasrc/tests/memory/testdata/example.org.zone


+ 49 - 9
src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc

@@ -25,6 +25,7 @@
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+#include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
 #include <string>
 #include <string>
@@ -137,11 +138,22 @@ protected:
     RdataSet* wildcard_rdataset_; // for wildcard (type doesn't matter much)
     RdataSet* wildcard_rdataset_; // for wildcard (type doesn't matter much)
 };
 };
 
 
+void
+compareRRSIGData(RdataIteratorPtr rit, const void* data, size_t data_len) {
+    ASSERT_FALSE(rit->isLast());
+
+    OutputBuffer buffer(0);
+    rit->getCurrent().toWire(buffer);
+    matchWireData(data, data_len, buffer.getData(), buffer.getLength());
+    rit->next();
+}
+
 // Check some trivial fields of a constructed TreeNodeRRset (passed as
 // Check some trivial fields of a constructed TreeNodeRRset (passed as
 // AbstractRRset as we'd normally use it in polymorphic way).
 // AbstractRRset as we'd normally use it in polymorphic way).
 // Other complicated fields are checked through rendering tests.
 // Other complicated fields are checked through rendering tests.
 void
 void
-checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
+checkBasicFields(const AbstractRRset& actual_rrset, const RdataSet* rdataset,
+                 const Name& expected_name,
                  const RRClass& expected_class, const RRType& expected_type,
                  const RRClass& expected_class, const RRType& expected_type,
                  const uint32_t expected_ttl,
                  const uint32_t expected_ttl,
                  size_t expected_rdatacount, size_t expected_sigcount)
                  size_t expected_rdatacount, size_t expected_sigcount)
@@ -152,6 +164,28 @@ checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
     EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL());
     EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL());
     EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
     EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
     EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
     EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
+
+    // getRRsig() should return non NULL iff the RRset is expected to be signed
+    if (expected_sigcount == 0) {
+        EXPECT_FALSE(actual_rrset.getRRsig());
+    } else {
+        ConstRRsetPtr actual_sigrrset = actual_rrset.getRRsig();
+        ASSERT_TRUE(actual_sigrrset);
+        EXPECT_EQ(expected_name, actual_sigrrset->getName());
+        EXPECT_EQ(expected_class, actual_sigrrset->getClass());
+        EXPECT_EQ(RRType::RRSIG(), actual_sigrrset->getType());
+        EXPECT_EQ(RRTTL(expected_ttl), actual_sigrrset->getTTL());
+        EXPECT_EQ(expected_sigcount, actual_sigrrset->getRdataCount());
+
+        // Compare each RRSIG RDATA
+        RdataIteratorPtr rit = actual_sigrrset->getRdataIterator();
+        RdataReader reader(expected_class, expected_type,
+                           rdataset->getDataBuf(), expected_rdatacount,
+                           expected_sigcount, &RdataReader::emptyNameAction,
+                           boost::bind(compareRRSIGData, rit, _1, _2));
+        while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
+        EXPECT_TRUE(rit->isLast()); // should check all RDATAs
+    }
 }
 }
 
 
 // The following two are trivial wrapper to create a shared pointer
 // The following two are trivial wrapper to create a shared pointer
@@ -178,30 +212,37 @@ createRRset(const Name& realname, const RRClass& rrclass, const ZoneNode* node,
 TEST_F(TreeNodeRRsetTest, create) {
 TEST_F(TreeNodeRRsetTest, create) {
     // Constructed with RRSIG, and it should be visible.
     // Constructed with RRSIG, and it should be visible.
     checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true),
     checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true),
-                     www_name_, rrclass_, RRType::A(), 3600, 2, 1);
+                     a_rdataset_, www_name_, rrclass_, RRType::A(), 3600, 2,
+                     1);
     // Constructed with RRSIG, and it should be invisible.
     // Constructed with RRSIG, and it should be invisible.
     checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, false),
     checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, false),
-                     www_name_, rrclass_, RRType::A(), 3600, 2, 0);
+                     a_rdataset_, www_name_, rrclass_, RRType::A(), 3600, 2,
+                     0);
     // Constructed without RRSIG, and it would be visible (but of course won't)
     // Constructed without RRSIG, and it would be visible (but of course won't)
     checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
     checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
-                     origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
+                     ns_rdataset_, origin_name_, rrclass_, RRType::NS(), 3600,
+                     1, 0);
     // Constructed without RRSIG, and it should be visible
     // Constructed without RRSIG, and it should be visible
     checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
     checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
-                     origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
+                     ns_rdataset_, origin_name_, rrclass_, RRType::NS(), 3600,
+                     1, 0);
     // RRSIG-only case (note the RRset's type is covered type)
     // RRSIG-only case (note the RRset's type is covered type)
     checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
     checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
                                   true),
                                   true),
-                     www_name_, rrclass_, RRType::TXT(), 3600, 0, 1);
+                     rrsig_only_rdataset_, www_name_, rrclass_, RRType::TXT(),
+                     3600, 0, 1);
     // RRSIG-only case (note the RRset's type is covered type), but it's
     // RRSIG-only case (note the RRset's type is covered type), but it's
     // invisible
     // invisible
     checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
     checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
                                   false),
                                   false),
-                     www_name_, rrclass_, RRType::TXT(), 3600, 0, 0);
+                     rrsig_only_rdataset_, www_name_, rrclass_, RRType::TXT(),
+                     3600, 0, 0);
     // Wildcard substitution
     // Wildcard substitution
     checkBasicFields(*createRRset(match_name_, rrclass_,
     checkBasicFields(*createRRset(match_name_, rrclass_,
                                   wildcard_node_, wildcard_rdataset_,
                                   wildcard_node_, wildcard_rdataset_,
                                   true),
                                   true),
-                     match_name_, rrclass_, RRType::A(), 3600, 2, 1);
+                     wildcard_rdataset_, match_name_, rrclass_, RRType::A(),
+                     3600, 2, 1);
 }
 }
 
 
 // The following two templated functions are helper to encapsulate the
 // The following two templated functions are helper to encapsulate the
@@ -578,7 +619,6 @@ TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
     EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
     EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
     EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
     EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
                  isc::Unexpected);
                  isc::Unexpected);
-    EXPECT_THROW(rrset.getRRsig(), isc::Unexpected);
     RdataPtr sig_rdata = createRdata(
     RdataPtr sig_rdata = createRdata(
         RRType::RRSIG(), rrclass_,
         RRType::RRSIG(), rrclass_,
         "A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");
         "A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");

src/lib/datasrc/memory/tests/zone_data_unittest.cc → src/lib/datasrc/tests/memory/zone_data_unittest.cc


+ 236 - 113
src/lib/datasrc/memory/tests/zone_finder_unittest.cc

@@ -31,6 +31,8 @@
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+#include <string>
+
 using namespace std;
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::dns::rdata;
@@ -46,74 +48,6 @@ namespace {
 using result::SUCCESS;
 using result::SUCCESS;
 using result::EXIST;
 using result::EXIST;
 
 
-// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
-// object.
-//
-// For apex (example.org)
-const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
-// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
-// For w.example.org
-const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-// For x.y.w.example.org (lower-cased)
-const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
-// For zzz.example.org.
-const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
-
-// A simple faked NSEC3 hash calculator with a dedicated creator for it.
-//
-// This is used in some NSEC3-related tests below.
-// Also see NOTE at inclusion of "../../tests/faked_nsec3.h"
-class TestNSEC3HashCreator : public NSEC3HashCreator {
-    class TestNSEC3Hash : public NSEC3Hash {
-    private:
-        typedef map<Name, string> NSEC3HashMap;
-        typedef NSEC3HashMap::value_type NSEC3HashPair;
-        NSEC3HashMap map_;
-    public:
-        TestNSEC3Hash() {
-            // Build pre-defined hash
-            map_[Name("example.org")] = apex_hash;
-            map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("x.y.w.example.org")] =
-                "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
-            map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-            map_[Name("w.example.org")] = w_hash;
-            map_[Name("zzz.example.org")] = zzz_hash;
-            map_[Name("smallest.example.org")] =
-                "00000000000000000000000000000000";
-            map_[Name("largest.example.org")] =
-                "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
-        }
-        virtual string calculate(const Name& name) const {
-            const NSEC3HashMap::const_iterator found = map_.find(name);
-            if (found != map_.end()) {
-                return (found->second);
-            }
-            isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
-                      << name);
-        }
-        virtual bool match(const generic::NSEC3PARAM&) const {
-            return (true);
-        }
-        virtual bool match(const generic::NSEC3&) const {
-            return (true);
-        }
-    };
-
-public:
-    virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const {
-        return (new TestNSEC3Hash);
-    }
-    virtual NSEC3Hash* create(const generic::NSEC3&) const {
-        return (new TestNSEC3Hash);
-    }
-};
-
-
 /// \brief expensive rrset converter
 /// \brief expensive rrset converter
 ///
 ///
 /// converts any specialized rrset (which may not have implemented some
 /// converts any specialized rrset (which may not have implemented some
@@ -182,6 +116,8 @@ public:
             {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
             {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
             {"example.org. 300 IN A 192.0.2.1", &rr_a_},
             {"example.org. 300 IN A 192.0.2.1", &rr_a_},
             {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
             {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
+            // This one will place rr_ns_a_ at a zone cut, making it a glue:
+            {"ns.example.org. 300 IN NS 192.0.2.2", &rr_ns_ns_},
             {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
             {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
             {"cname.example.org. 300 IN CNAME canonical.example.org",
             {"cname.example.org. 300 IN CNAME canonical.example.org",
              &rr_cname_},
              &rr_cname_},
@@ -255,18 +191,46 @@ public:
     }
     }
 
 
     // NSEC3-specific call for 'loading' data
     // NSEC3-specific call for 'loading' data
-    // This needs to be updated and checked when implementing #2118
     void addZoneDataNSEC3(const ConstRRsetPtr rrset) {
     void addZoneDataNSEC3(const ConstRRsetPtr rrset) {
         assert(rrset->getType() == RRType::NSEC3());
         assert(rrset->getType() == RRType::NSEC3());
 
 
-        const Rdata* rdata = &rrset->getRdataIterator()->getCurrent();
-        const generic::NSEC3* nsec3_rdata =
-            dynamic_cast<const generic::NSEC3*>(rdata);
-        NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, *nsec3_rdata);
-        // in case we happen to be replacing, destroy old
-        NSEC3Data* old_data = zone_data_->setNSEC3Data(nsec3_data);
-        if (old_data != NULL) {
-            NSEC3Data::destroy(mem_sgmt_, old_data, rrset->getClass());
+        const generic::NSEC3& nsec3_rdata =
+             dynamic_cast<const generic::NSEC3&>(
+                  rrset->getRdataIterator()->getCurrent());
+
+        NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
+        if (nsec3_data == NULL) {
+             nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
+             zone_data_->setNSEC3Data(nsec3_data);
+        } else {
+             const size_t salt_len = nsec3_data->getSaltLen();
+             const uint8_t* salt_data = nsec3_data->getSaltData();
+             const vector<uint8_t>& salt_data_2 = nsec3_rdata.getSalt();
+
+             if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
+                 (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
+                 (salt_data_2.size() != salt_len)) {
+                  isc_throw(isc::Unexpected,
+                            "NSEC3 with inconsistent parameters: " <<
+                            rrset->toText());
+             }
+
+             if ((salt_len > 0) &&
+                 (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
+                  isc_throw(isc::Unexpected,
+                            "NSEC3 with inconsistent parameters: " <<
+                            rrset->toText());
+             }
+        }
+
+        ZoneNode* node;
+        nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
+
+        RdataSet* rdset = RdataSet::create(mem_sgmt_, encoder_,
+                                           rrset, ConstRRsetPtr());
+        RdataSet* old_rdset = node->setData(rdset);
+        if (old_rdset != NULL) {
+             RdataSet::destroy(mem_sgmt_, class_, old_rdset);
         }
         }
         zone_data_->setSigned(true);
         zone_data_->setSigned(true);
     }
     }
@@ -312,6 +276,44 @@ public:
             }
             }
             name = name.split(1);
             name = name.split(1);
         }
         }
+
+        // If we've added NSEC3PARAM at zone origin, set up NSEC3
+        // specific data or check consistency with already set up
+        // parameters.
+        if (rrset->getType() == RRType::NSEC3PARAM() &&
+            rrset->getName() == origin_) {
+            // We know rrset has exactly one RDATA
+            const generic::NSEC3PARAM& param =
+                dynamic_cast<const generic::NSEC3PARAM&>
+                 (rrset->getRdataIterator()->getCurrent());
+
+            NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
+            if (nsec3_data == NULL) {
+                nsec3_data = NSEC3Data::create(mem_sgmt_, param);
+                zone_data_->setNSEC3Data(nsec3_data);
+                zone_data_->setSigned(true);
+            } else {
+                size_t salt_len = nsec3_data->getSaltLen();
+                const uint8_t* salt_data = nsec3_data->getSaltData();
+                const vector<uint8_t>& salt_data_2 = param.getSalt();
+
+                if ((param.getHashalg() != nsec3_data->hashalg) ||
+                    (param.getIterations() != nsec3_data->iterations) ||
+                    (salt_data_2.size() != salt_len)) {
+                     isc_throw(isc::Unexpected,
+                               "NSEC3PARAM with inconsistent parameters: "
+                               << rrset->toText());
+                }
+
+                if ((salt_len > 0) &&
+                    (std::memcmp(&salt_data_2[0],
+                                 salt_data, salt_len) != 0)) {
+                     isc_throw(isc::Unexpected,
+                               "NSEC3PARAM with inconsistent parameters: "
+                               << rrset->toText());
+                }
+            }
+        }
     }
     }
 
 
     // Some data to test with
     // Some data to test with
@@ -340,6 +342,7 @@ public:
         rr_ns_aaaa_,
         rr_ns_aaaa_,
         // A of example.org
         // A of example.org
         rr_a_;
         rr_a_;
+    RRsetPtr rr_ns_ns_;         // used to make rr_ns_a_ a glue.
     RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
     RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
     RRsetPtr rr_cname_a_; // for mixed CNAME + A case
     RRsetPtr rr_cname_a_; // for mixed CNAME + A case
     RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
     RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
@@ -369,12 +372,6 @@ public:
     RRsetPtr rr_ns_nsec_;
     RRsetPtr rr_ns_nsec_;
     RRsetPtr rr_wild_nsec_;
     RRsetPtr rr_wild_nsec_;
 
 
-    // A faked NSEC3 hash calculator for convenience.
-    // Tests that need to use the faked hashed values should call
-    // setNSEC3HashCreator() with a pointer to this variable at the beginning
-    // of the test (at least before adding any NSEC3/NSEC3PARAM RR).
-    TestNSEC3HashCreator nsec3_hash_creator_;
-
     /**
     /**
      * \brief Test one find query to the zone finder.
      * \brief Test one find query to the zone finder.
      *
      *
@@ -435,9 +432,12 @@ public:
                         ConstRRsetPtr result_rrset(
                         ConstRRsetPtr result_rrset(
                             convertRRset(find_result->rrset));
                             convertRRset(find_result->rrset));
                         rrsetCheck(answer, result_rrset);
                         rrsetCheck(answer, result_rrset);
-                        if (answer_sig) {
+                        if (answer_sig &&
+                            (options & ZoneFinder::FIND_DNSSEC) != 0) {
                             ASSERT_TRUE(result_rrset->getRRsig());
                             ASSERT_TRUE(result_rrset->getRRsig());
                             rrsetCheck(answer_sig, result_rrset->getRRsig());
                             rrsetCheck(answer_sig, result_rrset->getRRsig());
+                        } else {
+                            EXPECT_FALSE(result_rrset->getRRsig());
                         }
                         }
                     }
                     }
                 } else if (check_wild_answer) {
                 } else if (check_wild_answer) {
@@ -527,6 +527,13 @@ public:
  */
  */
 TEST_F(InMemoryZoneFinderTest, constructor) {
 TEST_F(InMemoryZoneFinderTest, constructor) {
     ASSERT_EQ(origin_, zone_finder_.getOrigin());
     ASSERT_EQ(origin_, zone_finder_.getOrigin());
+
+    // Some unusual (abnormal case): if we add a super domain name of the
+    // zone somehow, the label of the origin node won't be absolute.
+    // getOrigin() should still be the correct one.
+    ZoneNode *node;
+    zone_data_->insertName(mem_sgmt_, Name("org"), &node);
+    ASSERT_EQ(origin_, zone_finder_.getOrigin());
 }
 }
 
 
 TEST_F(InMemoryZoneFinderTest, findCNAME) {
 TEST_F(InMemoryZoneFinderTest, findCNAME) {
@@ -684,6 +691,9 @@ TEST_F(InMemoryZoneFinderTest, glue) {
     EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
     EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
     // glue under the deeper zone cut
     // glue under the deeper zone cut
     EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_));
     EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_));
+    // glue 'at the' zone cut
+    EXPECT_NO_THROW(addZoneData(rr_ns_a_));
+    EXPECT_NO_THROW(addZoneData(rr_ns_ns_));
 
 
     // by default glue is hidden due to the zone cut
     // by default glue is hidden due to the zone cut
     findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
     findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
@@ -716,6 +726,13 @@ TEST_F(InMemoryZoneFinderTest, glue) {
     findTest(Name("www.grand.child.example.org"), RRType::TXT(),
     findTest(Name("www.grand.child.example.org"), RRType::TXT(),
              ZoneFinder::DELEGATION, true, rr_child_ns_,
              ZoneFinder::DELEGATION, true, rr_child_ns_,
              ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
              ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+
+    // Glue at a zone cut
+    findTest(Name("ns.example.org"), RRType::A(),
+             ZoneFinder::DELEGATION, true, rr_ns_ns_);
+    findTest(Name("ns.example.org"), RRType::A(), ZoneFinder::SUCCESS,
+             true, rr_ns_a_, ZoneFinder::RESULT_DEFAULT,
+             NULL, ZoneFinder::FIND_GLUE_OK);
 }
 }
 
 
 /**
 /**
@@ -729,6 +746,9 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
 {
 {
     // Fill some data inside
     // Fill some data inside
     // Now put all the data we have there. It should throw nothing
     // Now put all the data we have there. It should throw nothing
+    rr_a_->addRRsig(createRdata(RRType::RRSIG(), RRClass::IN(),
+                                "A 5 3 3600 20120814220826 20120715220826 "
+                                "1234 example.com. FAKE"));
     EXPECT_NO_THROW(addZoneData(rr_ns_));
     EXPECT_NO_THROW(addZoneData(rr_ns_));
     EXPECT_NO_THROW(addZoneData(rr_ns_a_));
     EXPECT_NO_THROW(addZoneData(rr_ns_a_));
     EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_));
     EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_));
@@ -747,6 +767,12 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
     findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
     findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
              rr_ns_a_);
              rr_ns_a_);
 
 
+    // Similar test for a signed RRset.  We should see the RRSIG iff
+    // FIND_DNSSEC option is specified.
+    findTest(rr_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true, rr_a_);
+    findTest(rr_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+             rr_a_, ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_DNSSEC);
+
     // These domains don't exist. (and one is out of the zone).  In an
     // These domains don't exist. (and one is out of the zone).  In an
     // NSEC-signed zone with DNSSEC records requested, it should return the
     // NSEC-signed zone with DNSSEC records requested, it should return the
     // covering NSEC for the query name (the actual NSEC in the test data may
     // covering NSEC for the query name (the actual NSEC in the test data may
@@ -1427,36 +1453,10 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
 }
 }
 
 
 
 
-// DISABLED: nsec3 will be re-added in #2118
-TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3) {
+TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {
     // Set up the faked hash calculator.
     // Set up the faked hash calculator.
-    setNSEC3HashCreator(&nsec3_hash_creator_);
-
-    // Add a few NSEC3 records:
-    // apex (example.org.): hash=0P..
-    // ns1.example.org:     hash=2T..
-    // w.example.org:       hash=01..
-    // zzz.example.org:     hash=R5..
-    const string apex_nsec3_text = string(apex_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(apex_nsec3_text));
-    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(ns1_nsec3_text));
-    const string w_nsec3_text = string(w_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(w_nsec3_text));
-    const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(zzz_nsec3_text));
-
-    performNSEC3Test(zone_finder_);
-}
-
-// DISABLED: NSEC3 will be re-added in #2218
-TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3ForBadZone) {
-    // Set up the faked hash calculator.
-    setNSEC3HashCreator(&nsec3_hash_creator_);
+    const TestNSEC3HashCreator creator;
+    setNSEC3HashCreator(&creator);
 
 
     // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
     // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
     // findNSEC3() should be rejected.
     // findNSEC3() should be rejected.
@@ -1478,4 +1478,127 @@ TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3ForBadZone) {
                  DataSourceError);
                  DataSourceError);
 }
 }
 
 
+/// \brief NSEC3 specific tests fixture for the InMemoryZoneFinder class
+class InMemoryZoneFinderNSEC3Test : public InMemoryZoneFinderTest {
+public:
+    InMemoryZoneFinderNSEC3Test() {
+        // Set up the faked hash calculator.
+        setNSEC3HashCreator(&creator_);
+
+        // Add a few NSEC3 records:
+        // apex (example.org.): hash=0P..
+        // ns1.example.org:     hash=2T..
+        // w.example.org:       hash=01..
+        // zzz.example.org:     hash=R5..
+        const string apex_nsec3_text = string(apex_hash) + ".example.org." +
+            string(nsec3_common);
+        addZoneData(textToRRset(apex_nsec3_text));
+        const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+            string(nsec3_common);
+        addZoneData(textToRRset(ns1_nsec3_text));
+        const string w_nsec3_text = string(w_hash) + ".example.org." +
+            string(nsec3_common);
+        addZoneData(textToRRset(w_nsec3_text));
+        const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
+            string(nsec3_common);
+        addZoneData(textToRRset(zzz_nsec3_text));
+    }
+
+private:
+    const TestNSEC3HashCreator creator_;
+};
+
+TEST_F(InMemoryZoneFinderNSEC3Test, findNSEC3) {
+    performNSEC3Test(zone_finder_);
+}
+
+struct TestData {
+     // String for the name passed to findNSEC3() (concatenated with
+     // "example.org.")
+     const char* const name;
+     // Should recursive findNSEC3() be performed?
+     const bool recursive;
+     // The following are members of the FindNSEC3Result returned by
+     // findNSEC3(). The proofs are given as char*, which are converted
+     // to Name objects and checked against getName() on the returned
+     // ConstRRsetPtr. If any of these is NULL, then it's expected that
+     // ConstRRsetPtr() will be returned.
+     const bool matched;
+     const uint8_t closest_labels;
+     const char* const closest_proof;
+     const char* const next_proof;
+};
+
+const TestData nsec3_data[] = {
+     // ==== These are non-recursive tests.
+     {"n0", false, false, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+     {"n1", false,  true, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+     {"n2", false, false, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+     {"n3", false,  true, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+     {"n4", false, false, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+     {"n5", false,  true, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+     {"n6", false, false, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+     {"n7", false,  true, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+     {"n8", false, false, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+
+     // ==== These are recursive tests.
+     {"n0",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"},
+     {"n1",  true,  true, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+     {"n2",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H"},
+     {"n3",  true,  true, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+     {"n4",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"},
+     {"n5",  true,  true, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+     {"n6",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR"},
+     {"n7",  true,  true, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+     {"n8",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"}
+};
+
+const size_t data_count(sizeof(nsec3_data) / sizeof(*nsec3_data));
+
+TEST_F(InMemoryZoneFinderNSEC3Test, findNSEC3Walk) {
+    // This test basically uses nsec3_data[] declared above along with
+    // the fake hash setup to walk the NSEC3 tree. The names and fake
+    // hash calculation is specially setup so that the tree search
+    // terminates at specific locations in the tree. We findNSEC3() on
+    // each of the nsec3_data[], which is setup such that the hash
+    // results in the search terminating on either side of each node of
+    // the NSEC3 tree. This way, we check what result is returned in
+    // every search termination case in the NSEC3 tree.
+
+    const Name origin("example.org");
+    for (size_t i = 0; i < data_count; ++i) {
+        const Name name = Name(nsec3_data[i].name).concatenate(origin);
+
+        SCOPED_TRACE(name.toText() + (nsec3_data[i].recursive ?
+                                      ", recursive" :
+                                      ", non-recursive"));
+
+        const ZoneFinder::FindNSEC3Result result =
+            zone_finder_.findNSEC3(name, nsec3_data[i].recursive);
+
+        EXPECT_EQ(nsec3_data[i].matched, result.matched);
+        EXPECT_EQ(nsec3_data[i].closest_labels, result.closest_labels);
+
+        if (nsec3_data[i].closest_proof != NULL) {
+            ASSERT_TRUE(result.closest_proof);
+            EXPECT_EQ(Name(nsec3_data[i].closest_proof).concatenate(origin),
+                      result.closest_proof->getName());
+        } else {
+            EXPECT_FALSE(result.closest_proof);
+        }
+
+        if (nsec3_data[i].next_proof != NULL) {
+            ASSERT_TRUE(result.next_proof);
+            EXPECT_EQ(Name(nsec3_data[i].next_proof).concatenate(origin),
+                      result.next_proof->getName());
+        } else {
+            EXPECT_FALSE(result.next_proof);
+        }
+    }
+}
 }
 }

src/lib/datasrc/memory/tests/zone_table_unittest.cc → src/lib/datasrc/tests/memory/zone_table_unittest.cc


+ 0 - 66
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -293,72 +293,6 @@ setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
     ++it;
     ++it;
 }
 }
 
 
-// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
-// object.
-//
-// For apex (example.org)
-const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
-// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
-// For w.example.org
-const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-// For x.y.w.example.org (lower-cased)
-const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
-// For zzz.example.org.
-const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
-
-// A simple faked NSEC3 hash calculator with a dedicated creator for it.
-//
-// This is used in some NSEC3-related tests below.
-class TestNSEC3HashCreator : public NSEC3HashCreator {
-    class TestNSEC3Hash : public NSEC3Hash {
-    private:
-        typedef map<Name, string> NSEC3HashMap;
-        typedef NSEC3HashMap::value_type NSEC3HashPair;
-        NSEC3HashMap map_;
-    public:
-        TestNSEC3Hash() {
-            // Build pre-defined hash
-            map_[Name("example.org")] = apex_hash;
-            map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("x.y.w.example.org")] =
-                "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
-            map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-            map_[Name("w.example.org")] = w_hash;
-            map_[Name("zzz.example.org")] = zzz_hash;
-            map_[Name("smallest.example.org")] =
-                "00000000000000000000000000000000";
-            map_[Name("largest.example.org")] =
-                "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
-        }
-        virtual string calculate(const Name& name) const {
-            const NSEC3HashMap::const_iterator found = map_.find(name);
-            if (found != map_.end()) {
-                return (found->second);
-            }
-            isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
-                      << name);
-        }
-        virtual bool match(const generic::NSEC3PARAM&) const {
-            return (true);
-        }
-        virtual bool match(const generic::NSEC3&) const {
-            return (true);
-        }
-    };
-
-public:
-    virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const {
-        return (new TestNSEC3Hash);
-    }
-    virtual NSEC3Hash* create(const generic::NSEC3&) const {
-        return (new TestNSEC3Hash);
-    }
-};
-
 /// \brief Test fixture for the InMemoryZoneFinder class
 /// \brief Test fixture for the InMemoryZoneFinder class
 class InMemoryZoneFinderTest : public ::testing::Test {
 class InMemoryZoneFinderTest : public ::testing::Test {
     // A straightforward pair of textual RR(set) and a RRsetPtr variable
     // A straightforward pair of textual RR(set) and a RRsetPtr variable

+ 4 - 1
src/lib/datasrc/tests/testdata/contexttest.zone

@@ -1,7 +1,7 @@
 ;; test zone file used for ZoneFinderContext tests.
 ;; test zone file used for ZoneFinderContext tests.
 ;; RRSIGs are (obviouslly) faked ones for testing.
 ;; RRSIGs are (obviouslly) faked ones for testing.
 
 
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 67 3600 300 3600000 3600
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 71 3600 300 3600000 3600
 example.org.			      3600 IN NS	ns1.example.org.
 example.org.			      3600 IN NS	ns1.example.org.
 example.org.			      3600 IN NS	ns2.example.org.
 example.org.			      3600 IN NS	ns2.example.org.
 example.org.			      3600 IN MX	1 mx1.example.org.
 example.org.			      3600 IN MX	1 mx1.example.org.
@@ -71,6 +71,9 @@ a.*.emptywild.example.org.    3600 IN AAAA	2001:db8::2
 ;; expansion
 ;; expansion
 *.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
 *.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
 
 
+;; the owner name of additional for an answer RRset (MX) has DNAME
+dnamemx.example.org. 3600 IN MX 1 dname.example.org.
+
 ;; CNAME
 ;; CNAME
 alias.example.org. 3600 IN CNAME cname.example.org.
 alias.example.org. 3600 IN CNAME cname.example.org.
 
 

+ 27 - 12
src/lib/datasrc/tests/zone_finder_context_unittest.cc

@@ -14,12 +14,14 @@
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
+#include <util/memory_segment_local.h>
+
 #include <dns/masterload.h>
 #include <dns/masterload.h>
 #include <dns/name.h>
 #include <dns/name.h>
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
 
 
 #include <datasrc/zone.h>
 #include <datasrc/zone.h>
-#include <datasrc/memory_datasrc.h>
+#include <datasrc/memory/memory_client.h>
 #include <datasrc/database.h>
 #include <datasrc/database.h>
 #include <datasrc/sqlite3_accessor.h>
 #include <datasrc/sqlite3_accessor.h>
 
 
@@ -39,8 +41,10 @@
 using namespace std;
 using namespace std;
 using boost::shared_ptr;
 using boost::shared_ptr;
 
 
+using namespace isc::util;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::datasrc;
 using namespace isc::datasrc;
+using isc::datasrc::memory::InMemoryClient;
 using namespace isc::testutils;
 using namespace isc::testutils;
 
 
 namespace {
 namespace {
@@ -54,18 +58,16 @@ typedef shared_ptr<DataSourceClient> DataSourceClientPtr;
 // This is the type used as the test parameter.  Note that this is
 // This is the type used as the test parameter.  Note that this is
 // intentionally a plain old type (i.e. a function pointer), not a class;
 // intentionally a plain old type (i.e. a function pointer), not a class;
 // otherwise it could cause initialization fiasco at the instantiation time.
 // otherwise it could cause initialization fiasco at the instantiation time.
-typedef DataSourceClientPtr (*ClientCreator)(RRClass, const Name&);
+typedef DataSourceClientPtr (*ClientCreator)(MemorySegment&, RRClass,
+                                             const Name&);
 
 
 // Creator for the in-memory client to be tested
 // Creator for the in-memory client to be tested
 DataSourceClientPtr
 DataSourceClientPtr
-createInMemoryClient(RRClass zclass, const Name& zname) {
-    shared_ptr<InMemoryClient> client(new InMemoryClient);
-
-    shared_ptr<InMemoryZoneFinder> finder(
-        new InMemoryZoneFinder(zclass, zname));
-    finder->load(TEST_ZONE_FILE);
-
-    client->addZone(finder);
+createInMemoryClient(MemorySegment& mem_sgmt, RRClass zclass,
+                     const Name& zname)
+{
+    shared_ptr<InMemoryClient> client(new InMemoryClient(mem_sgmt, zclass));
+    client->load(zname, TEST_ZONE_FILE);
 
 
     return (client);
     return (client);
 }
 }
@@ -76,7 +78,7 @@ addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
 }
 }
 
 
 DataSourceClientPtr
 DataSourceClientPtr
-createSQLite3Client(RRClass zclass, const Name& zname) {
+createSQLite3Client(MemorySegment&, RRClass zclass, const Name& zname) {
     // We always begin with an empty template SQLite3 DB file and install
     // We always begin with an empty template SQLite3 DB file and install
     // the zone data from the zone file to ensure both cases have the
     // the zone data from the zone file to ensure both cases have the
     // same test data.
     // same test data.
@@ -103,7 +105,7 @@ class ZoneFinderContextTest :
 {
 {
 protected:
 protected:
     ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") {
     ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") {
-        client_ = (*GetParam())(qclass_, qzone_);
+        client_ = (*GetParam())(mem_sgmt_, qclass_, qzone_);
         REQUESTED_A.push_back(RRType::A());
         REQUESTED_A.push_back(RRType::A());
         REQUESTED_AAAA.push_back(RRType::AAAA());
         REQUESTED_AAAA.push_back(RRType::AAAA());
         REQUESTED_BOTH.push_back(RRType::A());
         REQUESTED_BOTH.push_back(RRType::A());
@@ -114,6 +116,7 @@ protected:
         ASSERT_TRUE(finder_);
         ASSERT_TRUE(finder_);
     }
     }
 
 
+    MemorySegmentLocal mem_sgmt_;
     const RRClass qclass_;
     const RRClass qclass_;
     const Name qzone_;
     const Name qzone_;
     DataSourceClientPtr client_;
     DataSourceClientPtr client_;
@@ -232,6 +235,18 @@ TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithDname) {
                 result_sets_.begin(), result_sets_.end());
                 result_sets_.begin(), result_sets_.end());
 }
 }
 
 
+TEST_P(ZoneFinderContextTest, getAdditionalOnDname) {
+    // The additional name has a DNAME as well as the additional record.
+    // The existence of DNAME shouldn't hide the additional record.
+    ZoneFinderContextPtr ctx = finder_->find(Name("dnamemx.example.org"),
+                                             RRType::MX());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("dname.example.org. 3600 IN A 192.0.2.12\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
 TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithEmptyName) {
 TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithEmptyName) {
     // One of NS names is at an empty non terminal node.  It shouldn't cause
     // One of NS names is at an empty non terminal node.  It shouldn't cause
     // any disruption.
     // any disruption.

+ 1 - 2
src/lib/dns/labelsequence.cc

@@ -45,8 +45,7 @@ LabelSequence::LabelSequence(const void* buf) {
     // Check the integrity on the offsets and the name data
     // Check the integrity on the offsets and the name data
     const uint8_t* dp = data_;
     const uint8_t* dp = data_;
     for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) {
     for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) {
-        if (offsets_[cur_offset] > Name::MAX_LABELLEN ||
-            dp - data_ != offsets_[cur_offset]) {
+        if (dp - data_ != offsets_[cur_offset] || *dp > Name::MAX_LABELLEN) {
             isc_throw(BadValue,
             isc_throw(BadValue,
                       "Broken offset or name data in serialized "
                       "Broken offset or name data in serialized "
                       "LabelSequence data");
                       "LabelSequence data");

+ 47 - 11
src/lib/dns/nsec3hash.cc

@@ -16,6 +16,7 @@
 
 
 #include <cassert>
 #include <cassert>
 #include <cstring>
 #include <cstring>
+#include <cstdlib>
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
@@ -57,17 +58,31 @@ private:
 
 
 public:
 public:
     NSEC3HashRFC5155(uint8_t algorithm, uint16_t iterations,
     NSEC3HashRFC5155(uint8_t algorithm, uint16_t iterations,
-                     const vector<uint8_t>& salt) :
+                     const uint8_t* salt_data, size_t salt_length) :
         algorithm_(algorithm), iterations_(iterations),
         algorithm_(algorithm), iterations_(iterations),
-        salt_(salt), digest_(SHA1_HASHSIZE), obuf_(Name::MAX_WIRE)
+        salt_data_(NULL), salt_length_(salt_length),
+        digest_(SHA1_HASHSIZE), obuf_(Name::MAX_WIRE)
     {
     {
         if (algorithm_ != NSEC3_HASH_SHA1) {
         if (algorithm_ != NSEC3_HASH_SHA1) {
             isc_throw(UnknownNSEC3HashAlgorithm, "Unknown NSEC3 algorithm: " <<
             isc_throw(UnknownNSEC3HashAlgorithm, "Unknown NSEC3 algorithm: " <<
                       static_cast<unsigned int>(algorithm_));
                       static_cast<unsigned int>(algorithm_));
         }
         }
+
+        if (salt_length > 0) {
+            salt_data_ = static_cast<uint8_t*>(std::malloc(salt_length));
+            if (salt_data_ == NULL) {
+                throw std::bad_alloc();
+            }
+            std::memcpy(salt_data_, salt_data, salt_length);
+        }
+
         SHA1Reset(&sha1_ctx_);
         SHA1Reset(&sha1_ctx_);
     }
     }
 
 
+    virtual ~NSEC3HashRFC5155() {
+        std::free(salt_data_);
+    }
+
     virtual std::string calculate(const Name& name) const;
     virtual std::string calculate(const Name& name) const;
 
 
     virtual bool match(const generic::NSEC3& nsec3) const;
     virtual bool match(const generic::NSEC3& nsec3) const;
@@ -78,7 +93,8 @@ public:
 private:
 private:
     const uint8_t algorithm_;
     const uint8_t algorithm_;
     const uint16_t iterations_;
     const uint16_t iterations_;
-    const vector<uint8_t> salt_;
+    uint8_t* salt_data_;
+    const size_t salt_length_;
 
 
     // The following members are placeholder of work place and don't hold
     // The following members are placeholder of work place and don't hold
     // any state over multiple calls so can be mutable without breaking
     // any state over multiple calls so can be mutable without breaking
@@ -108,15 +124,14 @@ NSEC3HashRFC5155::calculate(const Name& name) const {
     name_copy.downcase();
     name_copy.downcase();
     name_copy.toWire(obuf_);
     name_copy.toWire(obuf_);
 
 
-    const uint8_t saltlen = salt_.size();
-    const uint8_t* const salt = (saltlen > 0) ? &salt_[0] : NULL;
     uint8_t* const digest = &digest_[0];
     uint8_t* const digest = &digest_[0];
     assert(digest_.size() == SHA1_HASHSIZE);
     assert(digest_.size() == SHA1_HASHSIZE);
 
 
     iterateSHA1(&sha1_ctx_, static_cast<const uint8_t*>(obuf_.getData()),
     iterateSHA1(&sha1_ctx_, static_cast<const uint8_t*>(obuf_.getData()),
-                obuf_.getLength(), salt, saltlen, digest);
+                obuf_.getLength(), salt_data_, salt_length_, digest);
     for (unsigned int n = 0; n < iterations_; ++n) {
     for (unsigned int n = 0; n < iterations_; ++n) {
-        iterateSHA1(&sha1_ctx_, digest, SHA1_HASHSIZE, salt, saltlen, digest);
+        iterateSHA1(&sha1_ctx_, digest, SHA1_HASHSIZE,
+                    salt_data_, salt_length_, digest);
     }
     }
 
 
     return (encodeBase32Hex(digest_));
     return (encodeBase32Hex(digest_));
@@ -127,8 +142,9 @@ NSEC3HashRFC5155::match(uint8_t algorithm, uint16_t iterations,
                         const vector<uint8_t>& salt) const
                         const vector<uint8_t>& salt) const
 {
 {
     return (algorithm_ == algorithm && iterations_ == iterations &&
     return (algorithm_ == algorithm && iterations_ == iterations &&
-            salt_.size() == salt.size() &&
-            (salt_.empty() || memcmp(&salt_[0], &salt[0], salt_.size()) == 0));
+            salt_length_ == salt.size() &&
+            ((salt_length_ == 0) ||
+             memcmp(salt_data_, &salt[0], salt_length_) == 0));
 }
 }
 
 
 bool
 bool
@@ -175,15 +191,35 @@ NSEC3Hash::create(const generic::NSEC3& nsec3) {
 }
 }
 
 
 NSEC3Hash*
 NSEC3Hash*
+NSEC3Hash::create(uint8_t algorithm, uint16_t iterations,
+                  const uint8_t* salt_data, size_t salt_length) {
+    return (getNSEC3HashCreator()->create(algorithm, iterations,
+                                          salt_data, salt_length));
+}
+
+NSEC3Hash*
 DefaultNSEC3HashCreator::create(const generic::NSEC3PARAM& param) const {
 DefaultNSEC3HashCreator::create(const generic::NSEC3PARAM& param) const {
+    const vector<uint8_t>& salt = param.getSalt();
     return (new NSEC3HashRFC5155(param.getHashalg(), param.getIterations(),
     return (new NSEC3HashRFC5155(param.getHashalg(), param.getIterations(),
-                                 param.getSalt()));
+                                 salt.empty() ? NULL : &salt[0],
+                                 salt.size()));
 }
 }
 
 
 NSEC3Hash*
 NSEC3Hash*
 DefaultNSEC3HashCreator::create(const generic::NSEC3& nsec3) const {
 DefaultNSEC3HashCreator::create(const generic::NSEC3& nsec3) const {
+    const vector<uint8_t>& salt = nsec3.getSalt();
     return (new NSEC3HashRFC5155(nsec3.getHashalg(), nsec3.getIterations(),
     return (new NSEC3HashRFC5155(nsec3.getHashalg(), nsec3.getIterations(),
-                                 nsec3.getSalt()));
+                                 salt.empty() ? NULL : &salt[0],
+                                 salt.size()));
+}
+
+NSEC3Hash*
+DefaultNSEC3HashCreator::create(uint8_t algorithm, uint16_t iterations,
+                                const uint8_t* salt_data,
+                                size_t salt_length) const
+{
+    return (new NSEC3HashRFC5155(algorithm, iterations,
+                                 salt_data, salt_length));
 }
 }
 
 
 void
 void

+ 32 - 2
src/lib/dns/nsec3hash.h

@@ -16,7 +16,8 @@
 #define __NSEC3HASH_H 1
 #define __NSEC3HASH_H 1
 
 
 #include <string>
 #include <string>
-
+#include <vector>
+#include <stdint.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 namespace isc {
 namespace isc {
@@ -115,6 +116,16 @@ public:
     /// for hash calculation from an NSEC3 RDATA object.
     /// for hash calculation from an NSEC3 RDATA object.
     static NSEC3Hash* create(const rdata::generic::NSEC3& nsec3);
     static NSEC3Hash* create(const rdata::generic::NSEC3& nsec3);
 
 
+    /// \brief Factory method of NSECHash from args.
+    ///
+    /// \param algorithm the NSEC3 algorithm to use; currently only 1
+    ///                  (SHA-1) is supported
+    /// \param iterations the number of iterations
+    /// \param salt_data the salt data as a byte array
+    /// \param salt_data_length the length of the salt data
+    static NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+                             const uint8_t* salt_data, size_t salt_length);
+
     /// \brief The destructor.
     /// \brief The destructor.
     virtual ~NSEC3Hash() {}
     virtual ~NSEC3Hash() {}
 
 
@@ -167,7 +178,7 @@ public:
 /// would be an experimental extension for a newer hash algorithm or
 /// would be an experimental extension for a newer hash algorithm or
 /// implementation.
 /// implementation.
 ///
 ///
-/// The two main methods named \c create() correspond to the static factory
+/// The three main methods named \c create() correspond to the static factory
 /// methods of \c NSEC3Hash of the same name.
 /// methods of \c NSEC3Hash of the same name.
 ///
 ///
 /// By default, the library uses the \c DefaultNSEC3HashCreator creator.
 /// By default, the library uses the \c DefaultNSEC3HashCreator creator.
@@ -210,6 +221,22 @@ public:
     /// <code>NSEC3Hash::create(const rdata::generic::NSEC3& param)</code>
     /// <code>NSEC3Hash::create(const rdata::generic::NSEC3& param)</code>
     virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3)
     virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3)
         const = 0;
         const = 0;
+
+    /// \brief Factory method of NSECHash from args.
+    ///
+    /// See
+    /// <code>NSEC3Hash::create(uint8_t algorithm, uint16_t iterations,
+    ///                         const uint8_t* salt_data,
+    ///                         size_t salt_length)</code>
+    ///
+    /// \param algorithm the NSEC3 algorithm to use; currently only 1
+    ///                  (SHA-1) is supported
+    /// \param iterations the number of iterations
+    /// \param salt_data the salt data as a byte array
+    /// \param salt_data_length the length of the salt data
+    virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+                              const uint8_t* salt_data, size_t salt_length)
+        const = 0;
 };
 };
 
 
 /// \brief The default NSEC3Hash creator.
 /// \brief The default NSEC3Hash creator.
@@ -225,6 +252,9 @@ class DefaultNSEC3HashCreator : public NSEC3HashCreator {
 public:
 public:
     virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& param) const;
     virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& param) const;
     virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3) const;
     virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3) const;
+    virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+                              const uint8_t* salt_data,
+                              size_t salt_length) const;
 };
 };
 
 
 /// \brief The registrar of \c NSEC3HashCreator.
 /// \brief The registrar of \c NSEC3HashCreator.

+ 33 - 0
src/lib/dns/tests/labelsequence_unittest.cc

@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+#include <util/buffer.h>
+
 #include <dns/labelsequence.h>
 #include <dns/labelsequence.h>
 #include <dns/name.h>
 #include <dns/name.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
@@ -772,6 +774,37 @@ TEST_F(LabelSequenceTest, serialize) {
         1, 0, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e' };
         1, 0, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e' };
     expected.push_back(DataPair(sizeof(expected_data5), expected_data5));
     expected.push_back(DataPair(sizeof(expected_data5), expected_data5));
 
 
+    // Labels containing a longest possible label
+    const Name name_longlabel(std::string(63, 'x')); // 63 'x's
+    LabelSequence ls_longlabel(name_longlabel);
+    actual_labelseqs.push_back(ls_longlabel);
+    vector<uint8_t> expected_data6;
+    expected_data6.push_back(2); // 2 labels
+    expected_data6.push_back(0); // 1st offset
+    expected_data6.push_back(64); // 2nd offset
+    expected_data6.push_back(63); // 1st label length
+    expected_data6.insert(expected_data6.end(), 63, 'x'); // 1st label: 63 'x's
+    expected_data6.push_back(0); // 2nd label: trailing 0
+    expected.push_back(DataPair(expected_data6.size(), &expected_data6[0]));
+
+    // Max number of labels and longest possible name
+    EXPECT_EQ(Name::MAX_WIRE, n_maxlabel.getLength());
+    LabelSequence ls_maxlabel(n_maxlabel);
+    actual_labelseqs.push_back(ls_maxlabel);
+    vector<uint8_t> expected_data7;
+    expected_data7.push_back(Name::MAX_LABELS); // number of labels
+    for (size_t i = 0; i < Name::MAX_LABELS; ++i) {
+        expected_data7.push_back(i * 2); // each label has length and 1 byte
+    }
+    // Copy wire data of the name
+    isc::util::OutputBuffer ob(0);
+    n_maxlabel.toWire(ob);
+    expected_data7.insert(expected_data7.end(),
+                          static_cast<const uint8_t*>(ob.getData()),
+                          static_cast<const uint8_t*>(ob.getData()) +
+                          ob.getLength());
+    expected.push_back(DataPair(expected_data7.size(), &expected_data7[0]));
+
     // For each data set, serialize the labels and compare the data to the
     // For each data set, serialize the labels and compare the data to the
     // expected one.
     // expected one.
     vector<DataPair>::const_iterator it = expected.begin();
     vector<DataPair>::const_iterator it = expected.begin();

+ 23 - 1
src/lib/dns/tests/nsec3hash_unittest.cc

@@ -20,11 +20,14 @@
 
 
 #include <dns/nsec3hash.h>
 #include <dns/nsec3hash.h>
 #include <dns/rdataclass.h>
 #include <dns/rdataclass.h>
+#include <util/encode/hex.h>
 
 
 using boost::scoped_ptr;
 using boost::scoped_ptr;
 using namespace std;
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::dns::rdata;
+using namespace isc::util;
+using namespace isc::util::encode;
 
 
 namespace {
 namespace {
 typedef scoped_ptr<NSEC3Hash> NSEC3HashPtr;
 typedef scoped_ptr<NSEC3Hash> NSEC3HashPtr;
@@ -39,7 +42,10 @@ protected:
         test_hash_nsec3(NSEC3Hash::create(generic::NSEC3
         test_hash_nsec3(NSEC3Hash::create(generic::NSEC3
                                           ("1 0 12 aabbccdd " +
                                           ("1 0 12 aabbccdd " +
                                            string(nsec3_common))))
                                            string(nsec3_common))))
-    {}
+    {
+        const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd};
+        test_hash_args.reset(NSEC3Hash::create(1, 12, salt, sizeof(salt)));
+    }
 
 
     ~NSEC3HashTest() {
     ~NSEC3HashTest() {
         // Make sure we reset the hash creator to the default
         // Make sure we reset the hash creator to the default
@@ -53,6 +59,9 @@ protected:
 
 
     // Similar to test_hash, but created from NSEC3 RR.
     // Similar to test_hash, but created from NSEC3 RR.
     NSEC3HashPtr test_hash_nsec3;
     NSEC3HashPtr test_hash_nsec3;
+
+    // Similar to test_hash, but created from passed args.
+    NSEC3HashPtr test_hash_args;
 };
 };
 
 
 TEST_F(NSEC3HashTest, unknownAlgorithm) {
 TEST_F(NSEC3HashTest, unknownAlgorithm) {
@@ -65,6 +74,10 @@ TEST_F(NSEC3HashTest, unknownAlgorithm) {
                          generic::NSEC3("2 0 12 aabbccdd " +
                          generic::NSEC3("2 0 12 aabbccdd " +
                                         string(nsec3_common)))),
                                         string(nsec3_common)))),
                      UnknownNSEC3HashAlgorithm);
                      UnknownNSEC3HashAlgorithm);
+
+    const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd};
+    EXPECT_THROW(NSEC3HashPtr(NSEC3Hash::create(2, 12, salt, sizeof(salt))),
+                 UnknownNSEC3HashAlgorithm);
 }
 }
 
 
 // Common checks for NSEC3 hash calculation
 // Common checks for NSEC3 hash calculation
@@ -90,6 +103,10 @@ TEST_F(NSEC3HashTest, calculate) {
         SCOPED_TRACE("calculate check with NSEC3 based hash");
         SCOPED_TRACE("calculate check with NSEC3 based hash");
         calculateCheck(*test_hash_nsec3);
         calculateCheck(*test_hash_nsec3);
     }
     }
+    {
+        SCOPED_TRACE("calculate check with args based hash");
+        calculateCheck(*test_hash_args);
+    }
 
 
     // Some boundary cases: 0-iteration and empty salt.  Borrowed from the
     // Some boundary cases: 0-iteration and empty salt.  Borrowed from the
     // .com zone data.
     // .com zone data.
@@ -177,6 +194,11 @@ public:
         }
         }
         return (new TestNSEC3Hash);
         return (new TestNSEC3Hash);
     }
     }
+    virtual NSEC3Hash* create(uint8_t, uint16_t,
+                              const uint8_t*, size_t) const {
+        isc_throw(isc::Unexpected,
+                  "This method is not implemented here.");
+    }
 private:
 private:
     DefaultNSEC3HashCreator default_creator_;
     DefaultNSEC3HashCreator default_creator_;
 };
 };

+ 52 - 41
src/lib/python/isc/datasrc/tests/clientlist_test.py

@@ -28,6 +28,16 @@ class ClientListTest(unittest.TestCase):
     contain the ConfigurableClientList only.
     contain the ConfigurableClientList only.
     """
     """
 
 
+    def tearDown(self):
+        # The unit test module could keep internal objects alive longer than
+        # we expect.  But cached zone finder and cache client cannot stay
+        # longer than the originating client list.  So we explicitly clean
+        # them up here.  The ordering is important: clist must be destroyed
+        # last.
+        self.dsrc = None
+        self.finder = None
+        self.clist = None
+
     def test_constructors(self):
     def test_constructors(self):
         """
         """
         Test the constructor. It should accept an RRClass. Check it
         Test the constructor. It should accept an RRClass. Check it
@@ -50,16 +60,16 @@ class ClientListTest(unittest.TestCase):
         ones are acceptend and invalid rejected. We check the changes
         ones are acceptend and invalid rejected. We check the changes
         have effect.
         have effect.
         """
         """
-        clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
+        self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
         # This should be NOP now
         # This should be NOP now
-        clist.configure("[]", True)
+        self.clist.configure("[]", True)
         # Check the zone is not there yet
         # Check the zone is not there yet
-        dsrc, finder, exact = clist.find(isc.dns.Name("example.org"))
+        dsrc, finder, exact = self.clist.find(isc.dns.Name("example.org"))
         self.assertIsNone(dsrc)
         self.assertIsNone(dsrc)
         self.assertIsNone(finder)
         self.assertIsNone(finder)
         self.assertFalse(exact)
         self.assertFalse(exact)
         # We can use this type, as it is not loaded dynamically.
         # We can use this type, as it is not loaded dynamically.
-        clist.configure('''[{
+        self.clist.configure('''[{
             "type": "MasterFiles",
             "type": "MasterFiles",
             "params": {
             "params": {
                 "example.org": "''' + TESTDATA_PATH + '''example.org.zone"
                 "example.org": "''' + TESTDATA_PATH + '''example.org.zone"
@@ -68,38 +78,39 @@ class ClientListTest(unittest.TestCase):
         }]''', True)
         }]''', True)
         # Check the zone is there now. Proper tests of find are in other
         # Check the zone is there now. Proper tests of find are in other
         # test methods.
         # test methods.
-        dsrc, finder, exact = clist.find(isc.dns.Name("example.org"))
-        self.assertIsNotNone(dsrc)
-        self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
-        self.assertIsNotNone(finder)
-        self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("example.org"))
+        self.assertIsNotNone(self.dsrc)
+        self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+        self.assertIsNotNone(self.finder)
+        self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
         self.assertTrue(exact)
         self.assertTrue(exact)
-        self.assertRaises(isc.datasrc.Error, clist.configure, '"bad type"',
-                          True)
-        self.assertRaises(isc.datasrc.Error, clist.configure, '''[{
+        self.assertRaises(isc.datasrc.Error, self.clist.configure,
+                          '"bad type"', True)
+        self.assertRaises(isc.datasrc.Error, self.clist.configure, '''[{
             "type": "bad type"
             "type": "bad type"
         }]''', True)
         }]''', True)
-        self.assertRaises(isc.datasrc.Error, clist.configure, '''[{
+        self.assertRaises(isc.datasrc.Error, self.clist.configure, '''[{
             bad JSON,
             bad JSON,
         }]''', True)
         }]''', True)
-        self.assertRaises(TypeError, clist.configure, [], True)
-        self.assertRaises(TypeError, clist.configure, "[]")
-        self.assertRaises(TypeError, clist.configure, "[]", "true")
+        self.assertRaises(TypeError, self.clist.configure, [], True)
+        self.assertRaises(TypeError, self.clist.configure, "[]")
+        self.assertRaises(TypeError, self.clist.configure, "[]", "true")
 
 
     def test_find(self):
     def test_find(self):
         """
         """
         Test the find accepts the right arguments, some of them can be omitted,
         Test the find accepts the right arguments, some of them can be omitted,
         etc.
         etc.
         """
         """
-        clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
-        clist.configure('''[{
+        self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
+        self.clist.configure('''[{
             "type": "MasterFiles",
             "type": "MasterFiles",
             "params": {
             "params": {
                 "example.org": "''' + TESTDATA_PATH + '''example.org.zone"
                 "example.org": "''' + TESTDATA_PATH + '''example.org.zone"
             },
             },
             "cache-enable": true
             "cache-enable": true
         }]''', True)
         }]''', True)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"))
+        dsrc, finder, exact = self.clist.find(isc.dns.Name("sub.example.org"))
         self.assertIsNotNone(dsrc)
         self.assertIsNotNone(dsrc)
         self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
         self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
         self.assertIsNotNone(finder)
         self.assertIsNotNone(finder)
@@ -112,33 +123,33 @@ class ClientListTest(unittest.TestCase):
         self.assertEqual(2, sys.getrefcount(dsrc))
         self.assertEqual(2, sys.getrefcount(dsrc))
         # We check an exact match in test_configure already
         # We check an exact match in test_configure already
         self.assertFalse(exact)
         self.assertFalse(exact)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
-                                         False)
-        self.assertIsNotNone(dsrc)
-        self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
-        self.assertIsNotNone(finder)
-        self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("sub.example.org"), False)
+        self.assertIsNotNone(self.dsrc)
+        self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+        self.assertIsNotNone(self.finder)
+        self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
         self.assertFalse(exact)
         self.assertFalse(exact)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
-                                         True)
-        self.assertIsNone(dsrc)
-        self.assertIsNone(finder)
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("sub.example.org"), True)
+        self.assertIsNone(self.dsrc)
+        self.assertIsNone(self.finder)
         self.assertFalse(exact)
         self.assertFalse(exact)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
-                                         False, False)
-        self.assertIsNotNone(dsrc)
-        self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
-        self.assertIsNotNone(finder)
-        self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("sub.example.org"), False, False)
+        self.assertIsNotNone(self.dsrc)
+        self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+        self.assertIsNotNone(self.finder)
+        self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
         self.assertFalse(exact)
         self.assertFalse(exact)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
-                                         True, False)
-        self.assertIsNone(dsrc)
-        self.assertIsNone(finder)
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("sub.example.org"), True, False)
+        self.assertIsNone(self.dsrc)
+        self.assertIsNone(self.finder)
         self.assertFalse(exact)
         self.assertFalse(exact)
         # Some invalid inputs
         # Some invalid inputs
-        self.assertRaises(TypeError, clist.find, "example.org")
-        self.assertRaises(TypeError, clist.find)
+        self.assertRaises(TypeError, self.clist.find, "example.org")
+        self.assertRaises(TypeError, self.clist.find)
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     isc.log.init("bind10")
     isc.log.init("bind10")