Browse Source

[2109] ZoneFinder for the improved memory datasource

Moved out the ZoneFinder-specific classes and code from memory_datasrc.[h|cc] into memory/zonefinder.[h|cc]

The new code is mostly similar to the original, but using the new data structures instead of the old. It is also no longer templated (not necessary anymore) and no longer uses the pimpl (also no longer relevant), so it should be somewhat easier to read and modify.
Jelte Jansen 12 years ago
parent
commit
32f6a87363

+ 2 - 2
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/dns -I$(top_builddir)/src/lib/dns
@@ -64,7 +64,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/log/libb10-log.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
-libb10_datasrc_la_LIBADD += memory/libdatasrc_memory.la # convenience library
+#libb10_datasrc_la_LIBADD += memory/libdatasrc_memory.la # convenience library
 libb10_datasrc_la_LIBADD += $(SQLITE_LIBS)
 
 BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc

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

@@ -16,4 +16,5 @@ libdatasrc_memory_la_SOURCES += treenode_rrset.h treenode_rrset.cc
 libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc
 libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc
 libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
+libdatasrc_memory_la_SOURCES += zone_finder.h zone_finder.cc
 EXTRA_DIST  = rdata_serialization_priv.cc

+ 3 - 0
src/lib/datasrc/memory/benchmarks/Makefile.am

@@ -13,8 +13,11 @@ noinst_PROGRAMS = rdata_reader_bench rrset_render_bench
 
 rdata_reader_bench_SOURCES = rdata_reader_bench.cc
 rdata_reader_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la
+rdata_reader_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 rdata_reader_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 
 rrset_render_bench_SOURCES = rrset_render_bench.cc
 rrset_render_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la
+rrset_render_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+rrset_render_bench_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 rrset_render_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la

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

@@ -24,6 +24,8 @@ run_unittests_SOURCES += domaintree_unittest.cc
 run_unittests_SOURCES += treenode_rrset_unittest.cc
 run_unittests_SOURCES += zone_table_unittest.cc
 run_unittests_SOURCES += zone_data_unittest.cc
+run_unittests_SOURCES += zone_finder_unittest.cc
+run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc
 run_unittests_SOURCES += memory_segment_test.h
 run_unittests_SOURCES += segment_object_holder_unittest.cc
 
@@ -31,6 +33,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 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/dns/libb10-dns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la

+ 3 - 0
src/lib/datasrc/memory/tests/run_unittests.cc

@@ -14,10 +14,13 @@
 
 #include <gtest/gtest.h>
 #include <util/unittests/run_all.h>
+#include <log/logger_support.h>
 
 int
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
 
+    isc::log::initLogger();
+
     return (isc::util::unittests::run_all());
 }

+ 972 - 0
src/lib/datasrc/memory/tests/zone_finder_unittest.cc

@@ -0,0 +1,972 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "memory_segment_test.h"
+
+#include "../../tests/faked_nsec3.h"
+
+#include <exceptions/exceptions.h>
+
+#include <dns/masterload.h>
+#include <dns/name.h>
+#include <dns/nsec3hash.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrsetlist.h>
+#include <dns/rrttl.h>
+#include <dns/masterload.h>
+
+#include <datasrc/client.h>
+#include <datasrc/memory_datasrc.h>
+#include <datasrc/data_source.h>
+#include <datasrc/iterator.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <sstream>
+#include <vector>
+
+#include <datasrc/memory/zone_finder.h>
+#include <datasrc/memory/rdata_serialization.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc;
+using namespace isc::testutils;
+using boost::shared_ptr;
+using namespace isc::datasrc::test;
+using namespace isc::datasrc::memory::test;
+using namespace isc::datasrc::memory;
+
+namespace {
+// Commonly used result codes (Who should write the prefix all the time)
+using result::SUCCESS;
+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.
+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
+///
+/// converts any specialized rrset (which may not have implemented some
+/// methods for efficiency) into a 'full' RRsetPtr, for easy use in test
+/// checks.
+///
+/// Done very inefficiently through text representation, speed should not
+/// be a concern here.
+ConstRRsetPtr
+convertRRset(ConstRRsetPtr src) {
+    return (textToRRset(src->toText()));
+}
+
+/// \brief Test fixture for the InMemoryZoneFinder class
+class InMemoryZoneFinderTest : public ::testing::Test {
+    // A straightforward pair of textual RR(set) and a RRsetPtr variable
+    // to store the RRset.  Used to build test data below.
+    struct RRsetData {
+        const char* const text; // textual representation of an RRset
+        RRsetPtr* rrset;
+    };
+protected:
+    // The following sub tests are shared by multiple test cases, changing
+    // the zone's DNSSEC status (unsigned, NSEC-signed or NSEC3-signed).
+    // expected_flags is set to either RESULT_NSEC_SIGNED or
+    // RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and
+    // find() is expected to set the corresponding flags.
+    // find_options should be set to FIND_DNSSEC for NSEC-signed case when
+    // NSEC is expected to be returned.
+    void findCheck(ZoneFinder::FindResultFlags expected_flags =
+                   ZoneFinder::RESULT_DEFAULT,
+                   ZoneFinder::FindOptions find_options =
+                   ZoneFinder::FIND_DEFAULT);
+    void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags =
+                        ZoneFinder::RESULT_DEFAULT);
+    void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                               ZoneFinder::RESULT_DEFAULT,
+                               ZoneFinder::FindOptions find_options =
+                               ZoneFinder::FIND_DEFAULT);
+    void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                          ZoneFinder::RESULT_DEFAULT);
+    void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                            ZoneFinder::RESULT_DEFAULT);
+    void findNSECENTCheck(const Name& ent_name,
+                          ConstRRsetPtr expected_nsec,
+                          ZoneFinder::FindResultFlags expected_flags =
+                          ZoneFinder::RESULT_DEFAULT);
+
+public:
+    InMemoryZoneFinderTest() :
+        class_(RRClass::IN()),
+        origin_("example.org"),
+        zone_data_(ZoneData::create(mem_sgmt_, origin_)),
+        zone_finder_(*zone_data_)
+    {
+        // Build test RRsets.  Below, we construct an RRset for
+        // each textual RR(s) of zone_data, and assign it to the corresponding
+        // rr_xxx.
+        // Note that this contains an out-of-zone RR, and due to the
+        // validation check of masterLoad() used below, we cannot add SOA.
+        const RRsetData zone_data[] = {
+            {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
+            {"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 AAAA 2001:db8::2", &rr_ns_aaaa_},
+            {"cname.example.org. 300 IN CNAME canonical.example.org",
+             &rr_cname_},
+            {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
+            {"dname.example.org. 300 IN DNAME target.example.org.",
+             &rr_dname_},
+            {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
+            {"dname.example.org. 300 IN NS ns.dname.example.org.",
+             &rr_dname_ns_},
+            {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
+            {"child.example.org. 300 IN NS ns.child.example.org.",
+             &rr_child_ns_},
+            {"child.example.org. 300 IN DS 12345 5 1 DEADBEEF",
+             &rr_child_ds_},
+            {"ns.child.example.org. 300 IN A 192.0.2.153",
+             &rr_child_glue_},
+            {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
+             &rr_grandchild_ns_},
+            {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
+             &rr_grandchild_glue_},
+            {"dname.child.example.org. 300 IN DNAME example.com.",
+             &rr_child_dname_},
+            {"example.com. 300 IN A 192.0.2.10", &rr_out_},
+            {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
+            {"*.cnamewild.example.org. 300 IN CNAME canonial.example.org.",
+             &rr_cnamewild_},
+            {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
+            {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
+            {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
+             &rr_nested_emptywild_},
+            {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
+            {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
+             &rr_dnamewild_},
+            {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_},
+            {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
+            {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
+             &rr_not_wild_another_},
+            {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
+             "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
+             &rr_nsec3_},
+            {"example.org. 300 IN NSEC wild.*.foo.example.org. "
+             "NS SOA RRSIG NSEC DNSKEY", &rr_nsec_},
+            // Together with the apex NSEC, these next NSECs make a complete
+            // chain in the case of the zone for the emptyNonterminal tests
+            // (We may want to clean up this generator code and/or masterLoad
+            // so that we can prepare conflicting datasets better)
+            {"wild.*.foo.example.org. 3600 IN NSEC ns.example.org. "
+             "A RRSIG NSEC", &rr_ent_nsec2_},
+            {"ns.example.org. 3600 IN NSEC foo.wild.example.org. A RRSIG NSEC",
+             &rr_ent_nsec3_},
+            {"foo.wild.example.org. 3600 IN NSEC example.org. A RRSIG NSEC",
+             &rr_ent_nsec4_},
+            // And these are NSECs used in different tests
+            {"ns.example.org. 300 IN NSEC *.nswild.example.org. A AAAA NSEC",
+             &rr_ns_nsec_},
+            {"*.wild.example.org. 300 IN NSEC foo.wild.example.org. A NSEC",
+             &rr_wild_nsec_},
+            {NULL, NULL}
+        };
+
+        for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
+            *zone_data[i].rrset = textToRRset(zone_data[i].text);
+        }
+
+    }
+
+    ~InMemoryZoneFinderTest() {
+        // Make sure we reset the hash creator to the default
+        setNSEC3HashCreator(NULL);
+        ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+    }
+
+    // NSEC3-specific call for 'loading' data
+    void addZoneDataNSEC3(const ConstRRsetPtr rrset) {
+        // we're only adding one anyway so this is a simplified version
+        // base nsec3 chain of rrset rdata
+        // TODO: add if already exists?
+        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());
+        }
+    }
+
+    // simplified version of 'loading' data
+    void addZoneData(const ConstRRsetPtr rrset) {
+        ZoneNode* node = NULL;
+
+        if (rrset->getType() == RRType::NSEC3()) {
+            return (addZoneDataNSEC3(rrset));
+        }
+        zone_data_->insertName(mem_sgmt_, rrset->getName(), &node);
+        RdataSet* next_rds = node->getData();
+
+        if (rrset->getType() == RRType::NS() &&
+            rrset->getName() != zone_data_->getOriginNode()->getName()) {
+            node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
+        } else if (rrset->getType() == RRType::DNAME()) {
+            node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
+        }
+
+        RdataSet* rdataset =
+            RdataSet::create(mem_sgmt_, encoder_, rrset, ConstRRsetPtr());
+        rdataset->next = next_rds;
+        node->setData(rdataset);
+    }
+
+    // Some data to test with
+    const RRClass class_;
+    const Name origin_;
+    // The zone finder to torture by tests
+    MemorySegmentTest mem_sgmt_;
+    memory::ZoneData* zone_data_;
+    memory::InMemoryZoneFinder zone_finder_;
+    isc::datasrc::memory::RdataEncoder encoder_;
+
+    // Placeholder for storing RRsets to be checked with rrsetsCheck()
+    vector<ConstRRsetPtr> actual_rrsets_;
+
+    /*
+     * Some RRsets to put inside the zone.
+     */
+    RRsetPtr
+        // Out of zone RRset
+        rr_out_,
+        // NS of example.org
+        rr_ns_,
+        // A of ns.example.org
+        rr_ns_a_,
+        // AAAA of ns.example.org
+        rr_ns_aaaa_,
+        // A of example.org
+        rr_a_;
+    RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
+    RRsetPtr rr_cname_a_; // for mixed CNAME + A case
+    RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
+    RRsetPtr rr_dname_a_; // for mixed DNAME + A case
+    RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
+    RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
+    RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
+    RRsetPtr rr_child_ds_; // DS of a child domain (for delegation, auth data)
+    RRsetPtr rr_child_glue_; // glue RR of the child domain
+    RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
+    RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
+    RRsetPtr rr_child_dname_; // A DNAME under NS
+    RRsetPtr rr_wild_;        // Wildcard record
+    RRsetPtr rr_cnamewild_;     // CNAME at a wildcard
+    RRsetPtr rr_emptywild_;
+    RRsetPtr rr_nested_emptywild_;
+    RRsetPtr rr_nswild_, rr_dnamewild_;
+    RRsetPtr rr_child_wild_;
+    RRsetPtr rr_under_wild_;
+    RRsetPtr rr_not_wild_;
+    RRsetPtr rr_not_wild_another_;
+    RRsetPtr rr_nsec3_;
+    RRsetPtr rr_nsec_;
+    RRsetPtr rr_ent_nsec2_;
+    RRsetPtr rr_ent_nsec3_;
+    RRsetPtr rr_ent_nsec4_;
+    RRsetPtr rr_ns_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.
+     *
+     * Asks a query to the zone finder and checks it does not throw and returns
+     * expected results. It returns nothing, it just signals failures
+     * to GTEST.
+     *
+     * \param name The name to ask for.
+     * \param rrtype The RRType to ask of.
+     * \param result The expected code of the result.
+     * \param check_answer Should a check against equality of the answer be
+     *     done?
+     * \param answer The expected rrset, if any should be returned.
+     * \param expected_flags The expected result flags returned via find().
+     *     These can be tested using isWildcard() etc.
+     * \param zone_finder Check different InMemoryZoneFinder object than
+     *     zone_finder_ (if NULL, uses zone_finder_)
+     * \param check_wild_answer Checks that the answer has the same RRs, type
+     *     class and TTL as the eqxpected answer and that the name corresponds
+     *     to the one searched. It is meant for checking answers for wildcard
+     *     queries.
+     */
+    void findTest(const Name& name, const RRType& rrtype,
+                  ZoneFinder::Result result,
+                  bool check_answer = true,
+                  const ConstRRsetPtr& answer = ConstRRsetPtr(),
+                  ZoneFinder::FindResultFlags expected_flags =
+                  ZoneFinder::RESULT_DEFAULT,
+                  memory::InMemoryZoneFinder* zone_finder = NULL,
+                  ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
+                  bool check_wild_answer = false)
+    {
+        SCOPED_TRACE("findTest for " + name.toText() + "/" + rrtype.toText());
+
+        if (zone_finder == NULL) {
+            zone_finder = &zone_finder_;
+        }
+        const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
+            RRsetPtr(); // note we use the same type as of retval of getRRsig()
+        // The whole block is inside, because we need to check the result and
+        // we can't assign to FindResult
+        EXPECT_NO_THROW({
+                ZoneFinderContextPtr find_result(zone_finder->find(
+                                                     name, rrtype, options));
+                // Check it returns correct answers
+                EXPECT_EQ(result, find_result->code);
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+                          find_result->isWildcard());
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
+                          != 0, find_result->isNSECSigned());
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
+                          != 0, find_result->isNSEC3Signed());
+                if (check_answer) {
+                    if (!answer) {
+                        ASSERT_FALSE(find_result->rrset);
+                    } else {
+                        ASSERT_TRUE(find_result->rrset);
+                        rrsetCheck(answer, convertRRset(find_result->rrset));
+                        if (answer_sig) {
+                            ASSERT_TRUE(find_result->rrset->getRRsig());
+                            rrsetCheck(answer_sig,
+                                       find_result->rrset->getRRsig());
+                        }
+                    }
+                } else if (check_wild_answer) {
+                    ASSERT_NE(ConstRRsetPtr(), answer) <<
+                        "Wrong test, don't check for wild names if you expect "
+                        "empty answer";
+                    ASSERT_NE(ConstRRsetPtr(), find_result->rrset) <<
+                        "No answer found";
+                    // Build the expected answer using the given name and
+                    // other parameter of the base wildcard RRset.
+                    RRsetPtr wildanswer(new RRset(name, answer->getClass(),
+                                                  answer->getType(),
+                                                  answer->getTTL()));
+                    RdataIteratorPtr expectedIt(answer->getRdataIterator());
+                    for (; !expectedIt->isLast(); expectedIt->next()) {
+                        wildanswer->addRdata(expectedIt->getCurrent());
+                    }
+                    rrsetCheck(wildanswer, find_result->rrset);
+
+                    // Same for the RRSIG, if any.
+                    if (answer_sig) {
+                        ASSERT_TRUE(find_result->rrset->getRRsig());
+
+                        RRsetPtr wildsig(new RRset(name,
+                                                   answer_sig->getClass(),
+                                                   RRType::RRSIG(),
+                                                   answer_sig->getTTL()));
+                        RdataIteratorPtr expectedIt(
+                            answer_sig->getRdataIterator());
+                        for (; !expectedIt->isLast(); expectedIt->next()) {
+                            wildsig->addRdata(expectedIt->getCurrent());
+                        }
+                        rrsetCheck(wildsig, find_result->rrset->getRRsig());
+                    }
+                }
+            });
+    }
+    /**
+     * \brief Calls the findAll on the finder and checks the result.
+     */
+    void findAllTest(const Name& name, ZoneFinder::Result result,
+                     const vector<ConstRRsetPtr>& expected_rrsets,
+                     ZoneFinder::FindResultFlags expected_flags =
+                     ZoneFinder::RESULT_DEFAULT,
+                     memory::InMemoryZoneFinder* finder = NULL,
+                     const ConstRRsetPtr &rrset_result = ConstRRsetPtr(),
+                     ZoneFinder::FindOptions options =
+                     ZoneFinder::FIND_DEFAULT)
+    {
+        if (finder == NULL) {
+            finder = &zone_finder_;
+        }
+        std::vector<ConstRRsetPtr> target;
+        ZoneFinderContextPtr find_result(finder->findAll(name, target,
+                                                         options));
+        EXPECT_EQ(result, find_result->code);
+        if (!rrset_result) {
+            EXPECT_FALSE(find_result->rrset);
+        } else {
+            ASSERT_TRUE(find_result->rrset);
+            rrsetCheck(rrset_result, convertRRset(find_result->rrset));
+        }
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+                  find_result->isWildcard());
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
+                  != 0, find_result->isNSECSigned());
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
+                  != 0, find_result->isNSEC3Signed());
+        // Convert all rrsets to 'full' ones before checking
+        std::vector<ConstRRsetPtr> converted_rrsets;
+        BOOST_FOREACH(ConstRRsetPtr cur_rrset, target) {
+            converted_rrsets.push_back(convertRRset(cur_rrset));
+        }
+        rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+                    converted_rrsets.begin(), converted_rrsets.end());
+    }
+};
+
+/**
+ * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
+ *
+ * Takes the created zone finder and checks its properties they are the same
+ * as passed parameters.
+ */
+TEST_F(InMemoryZoneFinderTest, constructor) {
+    ASSERT_EQ(origin_, zone_finder_.getOrigin());
+}
+
+TEST_F(InMemoryZoneFinderTest, findCNAME) {
+    // install CNAME RR
+    addZoneData(rr_cname_);
+
+    // Find A RR of the same.  Should match the CNAME
+    findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true,
+             rr_cname_);
+
+    // Find the CNAME itself.  Should result in normal SUCCESS
+    findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true,
+             rr_cname_);
+}
+
+TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
+    // There's nothing special when we find a CNAME under a zone cut
+    // (with FIND_GLUE_OK).  The behavior is different from BIND 9,
+    // so we test this case explicitly.
+    addZoneData(rr_child_ns_);
+    ConstRRsetPtr rr_cname_under_cut_ = textToRRset(
+        "cname.child.example.org. 300 IN CNAME target.child.example.org.");
+    addZoneData(rr_cname_under_cut_);
+    findTest(Name("cname.child.example.org"), RRType::AAAA(),
+             ZoneFinder::CNAME, true, rr_cname_under_cut_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+// Search under a DNAME record. It should return the DNAME
+TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
+    EXPECT_NO_THROW(addZoneData(rr_dname_));
+    findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME,
+             true, rr_dname_);
+}
+
+// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
+// influences only the data below (see RFC 2672, section 3)
+TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
+    EXPECT_NO_THROW(addZoneData(rr_dname_));
+    EXPECT_NO_THROW(addZoneData(rr_dname_a_));
+
+    const Name dname_name(rr_dname_->getName());
+    findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_);
+    findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true,
+             rr_dname_);
+    findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true);
+}
+
+// Try searching something that is both under NS and DNAME, without and with
+// GLUE_OK mode (it should stop at the NS and DNAME respectively).
+TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
+    addZoneData(rr_child_ns_);
+    addZoneData(rr_child_dname_);
+
+    Name lowName("below.dname.child.example.org.");
+
+    findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
+    findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+// Test adding child zones and zone cut handling
+TEST_F(InMemoryZoneFinderTest, delegationNS) {
+    // add in-zone data
+    EXPECT_NO_THROW(addZoneData(rr_ns_));
+
+    // install a zone cut
+    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+
+    // below the zone cut
+    findTest(Name("www.child.example.org"), RRType::A(),
+             ZoneFinder::DELEGATION, true, rr_child_ns_);
+
+    // at the zone cut
+    findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
+             true, rr_child_ns_);
+    findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION,
+             true, rr_child_ns_);
+
+    // finding NS for the apex (origin) node.  This must not be confused
+    // with delegation due to the existence of an NS RR.
+    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
+
+    // unusual case of "nested delegation": the highest cut should be used.
+    EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
+    findTest(Name("www.grand.child.example.org"), RRType::A(),
+             // note: !rr_grandchild_ns_
+             ZoneFinder::DELEGATION, true, rr_child_ns_);
+}
+
+TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
+    // Similar setup to the previous one, but with DS RR at the delegation
+    // point.
+    addZoneData(rr_ns_);
+    addZoneData(rr_child_ns_);
+    addZoneData(rr_child_ds_);
+
+    // Normal types of query should result in delegation, but DS query
+    // should be considered in-zone (but only exactly at the delegation point).
+    findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
+             true, rr_child_ns_);
+    findTest(Name("child.example.org"), RRType::DS(), ZoneFinder::SUCCESS,
+             true, rr_child_ds_);
+    findTest(Name("grand.child.example.org"), RRType::DS(),
+             ZoneFinder::DELEGATION, true, rr_child_ns_);
+
+    // There's nothing special for DS query at the zone apex.  It would
+    // normally result in NXRRSET.
+    findTest(Name("example.org"), RRType::DS(), ZoneFinder::NXRRSET,
+             true, ConstRRsetPtr());
+}
+
+TEST_F(InMemoryZoneFinderTest, findAny) {
+    EXPECT_NO_THROW(addZoneData(rr_a_));
+    EXPECT_NO_THROW(addZoneData(rr_ns_));
+    EXPECT_NO_THROW(addZoneData(rr_child_glue_));
+
+    vector<ConstRRsetPtr> expected_sets;
+
+    // origin
+    expected_sets.push_back(rr_a_);
+    expected_sets.push_back(rr_ns_);
+    findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets);
+
+    // out zone name
+    EXPECT_THROW(findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
+                             vector<ConstRRsetPtr>()),
+                 OutOfZone);
+
+    expected_sets.clear();
+    expected_sets.push_back(rr_child_glue_);
+    findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets);
+
+    // add zone cut
+    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+
+    // zone cut
+    findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION,
+                vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
+                NULL, rr_child_ns_);
+
+    // glue for this zone cut
+    findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION,
+                vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
+                NULL, rr_child_ns_);
+}
+
+TEST_F(InMemoryZoneFinderTest, glue) {
+    // install zone data:
+    // a zone cut
+    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+    // glue for this cut
+    EXPECT_NO_THROW(addZoneData(rr_child_glue_));
+    // a nested zone cut (unusual)
+    EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
+    // glue under the deeper zone cut
+    EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_));
+
+    // by default glue is hidden due to the zone cut
+    findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
+             true, rr_child_ns_);
+
+
+    // If we do it in the "glue OK" mode, we should find the exact match.
+    findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+             rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
+
+    // glue OK + NXRRSET case
+    findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
+             true, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
+
+    // glue OK + NXDOMAIN case
+    findTest(Name("www.child.example.org"), RRType::A(),
+             ZoneFinder::DELEGATION, true, rr_child_ns_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+
+    // nested cut case.  The glue should be found.
+    findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
+             ZoneFinder::SUCCESS,
+             true, rr_grandchild_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
+
+    // A non-existent name in nested cut.  This should result in delegation
+    // at the highest zone cut.
+    findTest(Name("www.grand.child.example.org"), RRType::TXT(),
+             ZoneFinder::DELEGATION, true, rr_child_ns_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+/**
+ * \brief Test searching.
+ *
+ * Check it finds or does not find correctly and does not throw exceptions.
+ * \todo This doesn't do any kind of CNAME and so on. If it isn't
+ *     directly there, it just tells it doesn't exist.
+ */
+void
+InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
+                                  ZoneFinder::FindOptions find_options)
+{
+    // Fill some data inside
+    // Now put all the data we have there. It should throw nothing
+    EXPECT_NO_THROW(addZoneData(rr_ns_));
+    EXPECT_NO_THROW(addZoneData(rr_ns_a_));
+    EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_));
+    EXPECT_NO_THROW(addZoneData(rr_a_));
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        addZoneData(rr_nsec3_);
+        zone_data_->setSigned(true);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_nsec_);
+        zone_data_->setSigned(true);
+    }
+
+    // These two should be successful
+    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
+    findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+             rr_ns_a_);
+
+    // 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
+    // covering NSEC for the query name (the actual NSEC in the test data may
+    // not really "cover" it, but for the purpose of this test it's okay).
+    ConstRRsetPtr expected_nsec; // by default it's NULL
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+        (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+        expected_nsec = rr_nsec_;
+    }
+
+    // There's no other name between this one and the origin, so when NSEC
+    // is to be returned it should be the origin NSEC.
+    findTest(Name("nothere.example.org"), RRType::A(),
+             ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+             NULL, find_options);
+
+    // The previous name in the zone is "ns.example.org", but it doesn't
+    // have an NSEC.  It should be skipped and the origin NSEC will be
+    // returned as the "closest NSEC".
+    findTest(Name("nxdomain.example.org"), RRType::A(),
+             ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+             NULL, find_options);
+    EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
+                 OutOfZone);
+
+    // These domain exist but don't have the provided RRType.  For the latter
+    // one we now add its NSEC (which was delayed so that it wouldn't break
+    // other cases above).
+    findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
+             expected_nsec, expected_flags, NULL, find_options);
+
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_ns_nsec_);
+        if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+            expected_nsec = rr_ns_nsec_;
+        }
+    }
+    findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
+             expected_nsec, expected_flags, NULL, find_options);
+}
+
+TEST_F(InMemoryZoneFinderTest, find) {
+    findCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
+    findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3SignedWithDNSSEC) {
+    // For NSEC3-signed zones, specifying the DNSSEC option shouldn't change
+    // anything (the NSEC3_SIGNED flag is always set, and no records are
+    // returned for negative cases regardless).
+    findCheck(ZoneFinder::RESULT_NSEC3_SIGNED, ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECSigned) {
+    // NSEC-signed zone, without requesting DNSSEC (no NSEC should be provided)
+    findCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// Generalized test for Empty Nonterminal (ENT) cases with NSEC
+void
+InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
+    ConstRRsetPtr expected_nsec,
+    ZoneFinder::FindResultFlags expected_flags)
+{
+    addZoneData(rr_emptywild_);
+    addZoneData(rr_under_wild_);
+
+    // Sanity check: Should result in NXRRSET
+    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
+    // Sanity check: No NSEC added yet
+    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags,
+             NULL, ZoneFinder::FIND_DNSSEC);
+
+    // Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC,
+    // there are no sigs)
+    addZoneData(rr_nsec_);
+    addZoneData(rr_ent_nsec2_);
+    addZoneData(rr_ent_nsec3_);
+    addZoneData(rr_ent_nsec4_);
+    zone_data_->setSigned(true);
+
+    // Should result in NXRRSET, and RESULT_NSEC_SIGNED
+    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(),
+             expected_flags | ZoneFinder::RESULT_NSEC_SIGNED);
+
+    // And check for the NSEC if DNSSEC_OK
+    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+             expected_nsec, expected_flags | ZoneFinder::RESULT_NSEC_SIGNED,
+             NULL, ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) {
+    // Non-wildcard case
+    findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_);
+}
+
+TEST_F(InMemoryZoneFinderTest, DISABLED_findNSECEmptyNonterminalWildcard) {
+    // Wildcard case, above actual wildcard
+    findNSECENTCheck(Name("foo.example.org"), rr_nsec_);
+}
+
+TEST_F(InMemoryZoneFinderTest,DISABLED_findNSECEmptyNonterminalAtWildcard) {
+    // Wildcard case, at actual wildcard
+    findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_,
+                     ZoneFinder::RESULT_WILDCARD);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECSignedWithDNSSEC) {
+    // NSEC-signed zone, requesting DNSSEC (NSEC should be provided)
+    findCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::FIND_DNSSEC);
+}
+
+void
+InMemoryZoneFinderTest::emptyNodeCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
+    /*
+     * The backend RBTree for this test should look like as follows:
+     *          example.org
+     *               |
+     *              baz (empty; easy case)
+     *            /  |  \
+     *          bar  |  x.foo ('foo' part is empty; a bit trickier)
+     *              bbb
+     *             /
+     *           aaa
+     */
+
+    // Construct the test zone
+    const char* const names[] = {
+        "bar.example.org.", "x.foo.example.org.", "aaa.baz.example.org.",
+        "bbb.baz.example.org.", NULL};
+    for (int i = 0; names[i] != NULL; ++i) {
+        ConstRRsetPtr rrset = textToRRset(string(names[i]) +
+                                          " 300 IN A 192.0.2.1");
+        addZoneData(rrset);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        addZoneData(rr_nsec3_);
+        zone_data_->setSigned(true);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_nsec_);
+        zone_data_->setSigned(true);
+    }
+
+    // empty node matching, easy case: the node for 'baz' exists with
+    // no data.
+    findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
+
+    // empty node matching, a trickier case: the node for 'foo' is part of
+    // "x.foo", which should be considered an empty node.
+    findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
+
+    // "org" is contained in "example.org", but it shouldn't be treated as
+    // NXRRSET because it's out of zone.
+    // Note: basically we don't expect such a query to be performed (the common
+    // operation is to identify the best matching zone first then perform
+    // search it), but we shouldn't be confused even in the unexpected case.
+    EXPECT_THROW(zone_finder_.find(Name("org"), RRType::A()), OutOfZone);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNode) {
+    emptyNodeCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
+    emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) {
+    emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// DISABLED: nsec3 will be re-added in #2118
+TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3) {
+    // 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_);
+
+    // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
+    // findNSEC3() should be rejected.
+    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+                 DataSourceError);
+
+    // Only having NSEC3PARAM isn't enough
+    addZoneData(textToRRset("example.org. 300 IN NSEC3PARAM "
+                            "1 0 12 aabbccdd"));
+    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+                 DataSourceError);
+
+    // Unless NSEC3 for apex is added the result in the recursive mode
+    // is guaranteed.
+    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+        string(nsec3_common);
+    addZoneData(textToRRset(ns1_nsec3_text));
+    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+                 DataSourceError);
+}
+
+}

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

@@ -252,6 +252,8 @@ private:
     const dns::Name* const realname_;
 };
 
+typedef boost::shared_ptr<TreeNodeRRset> TreeNodeRRsetPtr;
+
 } // namespace memory
 } // namespace datasrc
 } // namespace isc

+ 1 - 0
src/lib/datasrc/memory/zone_data.h

@@ -43,6 +43,7 @@ namespace memory {
 
 typedef DomainTree<RdataSet> ZoneTree;
 typedef DomainTreeNode<RdataSet> ZoneNode;
+typedef DomainTreeNodeChain<RdataSet> ZoneNodeChain;
 
 /// \brief NSEC3 data for a DNS zone.
 ///

+ 412 - 0
src/lib/datasrc/memory/zone_finder.cc

@@ -0,0 +1,412 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_finder.h>
+#include <datasrc/memory/domaintree.h>
+#include <datasrc/memory/treenode_rrset.h>
+
+#include <datasrc/zone.h>
+#include <datasrc/data_source.h>
+#include <dns/labelsequence.h>
+#include <dns/name.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+
+#include <datasrc/logger.h>
+
+using namespace isc::dns;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc;
+
+namespace {
+
+// (temporary?) convenience function to create a treenoderrset
+// given an rdataset; we should probably have some pool so these
+// do not need to be allocated dynamically
+// For now they are still dynamically allocated, and have a fixed rrclass (...)
+TreeNodeRRsetPtr
+createTreeNodeRRset(const ZoneNode* node,
+                    const RdataSet* rdataset)
+{
+    return TreeNodeRRsetPtr(new TreeNodeRRset(RRClass::IN(), node,
+                                              rdataset, true));
+}
+
+struct FindState {
+    FindState(bool glue_ok) :
+        zonecut_node_(NULL),
+        dname_node_(NULL),
+        rrset_(TreeNodeRRsetPtr()),
+        glue_ok_(glue_ok)
+    {}
+
+    // These will be set to a domain node of the highest delegation point,
+    // if any.  In fact, we could use a single variable instead of both.
+    // But then we would need to distinquish these two cases by something
+    // else and it seemed little more confusing when this was written.
+    const ZoneNode* zonecut_node_;
+    const ZoneNode* dname_node_;
+
+    // Delegation RRset (NS or DNAME), if found.
+    TreeNodeRRsetPtr rrset_;
+
+    // Whether to continue search below a delegation point.
+    // Set at construction time.
+    const bool glue_ok_;
+};
+
+// A callback called from possible zone cut nodes and nodes with DNAME.
+// This will be passed from findNode() to \c RBTree::find().
+bool cutCallback(const ZoneNode& node, FindState* state) {
+    // We need to look for DNAME first, there's allowed case where
+    // DNAME and NS coexist in the apex. DNAME is the one to notice,
+    // the NS is authoritative, not delegation (corner case explicitly
+    // allowed by section 3 of 2672)
+    const RdataSet* found_dname = RdataSet::find(node.getData(), RRType::DNAME());
+
+    if (found_dname != NULL) {
+        LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED);
+        state->dname_node_ = &node;
+        state->rrset_ = createTreeNodeRRset(&node, found_dname);
+        return (true);
+    }
+
+    // Look for NS
+    const RdataSet* found_ns = RdataSet::find(node.getData(), RRType::NS());
+    if (found_ns != NULL) {
+        // We perform callback check only for the highest zone cut in the
+        // rare case of nested zone cuts.
+        if (state->zonecut_node_ != NULL) {
+            return (false);
+        }
+
+        LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
+
+        // BIND 9 checks if this node is not the origin.  That's probably
+        // because it can support multiple versions for dynamic updates
+        // and IXFR, and it's possible that the callback is called at
+        // the apex and the DNAME doesn't exist for a particular version.
+        // It cannot happen for us (at least for now), so we don't do
+        // that check.
+        state->zonecut_node_ = &node;
+        state->rrset_ = createTreeNodeRRset(&node, found_ns);
+
+        // Unless glue is allowed the search stops here, so we return
+        // false; otherwise return true to continue the search.
+        return (!state->glue_ok_);
+    }
+
+    // This case should not happen because we enable callback only
+    // when we add an RR searched for above.
+    assert(0);
+    // This is here to avoid warning (therefore compilation error)
+    // in case assert is turned off. Otherwise we could get "Control
+    // reached end of non-void function".
+    return (false);
+}
+
+} // unnamed namespace
+
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+namespace {
+    // convenience function to fill in the final details
+    isc::datasrc::memory::ZoneFinderResultContext
+    createFindResult(const ZoneData& zone_data,
+                     ZoneFinder::Result code,
+                     TreeNodeRRsetPtr rrset,
+                     const ZoneNode* node,
+                     bool wild = false) {
+        ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
+
+        if (wild) {
+            flags = flags | ZoneFinder::RESULT_WILDCARD;
+        }
+        if (code == ZoneFinder::NXRRSET || code == ZoneFinder::NXDOMAIN || wild) {
+            if (zone_data.isNSEC3Signed()) {
+                flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED;
+            } else if (zone_data.isSigned()) {
+                flags = flags | ZoneFinder::RESULT_NSEC_SIGNED;
+            }
+        }
+
+        return (ZoneFinderResultContext(code, rrset, flags, node));
+    }
+} // end anonymous namespace
+
+class InMemoryZoneFinder::Context : public ZoneFinder::Context {
+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,
+            ZoneFinderResultContext result) :
+        ZoneFinder::Context(finder, options,
+                            ResultContext(result.code, result.rrset,
+                                          result.flags)),
+        rrset_(result.rrset), found_node_(result.found_node)
+    {}
+
+private:
+    const TreeNodeRRsetPtr rrset_;
+    const ZoneNode* const found_node_;
+};
+
+boost::shared_ptr<ZoneFinder::Context>
+InMemoryZoneFinder::find(const isc::dns::Name& name,
+                const isc::dns::RRType& type,
+                const FindOptions options)
+{
+    // call find_internal, and wrap the result in a ContextPtr
+    return ZoneFinderContextPtr(new Context(*this, options,
+                                            find_internal(name,
+                                                          type,
+                                                          NULL,
+                                                          options)));
+}
+
+boost::shared_ptr<ZoneFinder::Context>
+InMemoryZoneFinder::findAll(const isc::dns::Name& name,
+        std::vector<isc::dns::ConstRRsetPtr>& target,
+        const FindOptions options)
+{
+    // call find_internal, and wrap the result in a ContextPtr
+    return ZoneFinderContextPtr(new Context(*this, options,
+                                            find_internal(name,
+                                                          RRType::ANY(),
+                                                          &target,
+                                                          options)));
+}
+
+namespace {
+
+TreeNodeRRsetPtr
+getClosestNSEC(const ZoneData& zone_data,
+               ZoneNodeChain& chain,
+               ZoneFinder::FindOptions options)
+{
+    if (!zone_data.isSigned() || (options & ZoneFinder::FIND_DNSSEC) == 0 || zone_data.isNSEC3Signed()) {
+        return (TreeNodeRRsetPtr());
+    }
+
+    const ZoneNode* prev_node;
+    while ((prev_node = zone_data.getZoneTree().previousNode(chain)) != NULL) {
+        if (!prev_node->isEmpty()) {
+            const RdataSet* found = RdataSet::find(prev_node->getData(), RRType::NSEC());
+            if (found != NULL) {
+                return (createTreeNodeRRset(prev_node, found));
+            }
+        }
+    }
+    // This must be impossible and should be an internal bug.
+    // See the description at the method declaration.
+    assert(false);
+    // Even though there is an assert here, strict compilers
+    // will still need some return value.
+    return (TreeNodeRRsetPtr());
+}
+
+// A helper function for the NXRRSET case in find().  If the zone is
+// NSEC-signed and DNSSEC records are requested, try to find NSEC
+// on the given node, and return it if found; return NULL for all other
+// cases.
+TreeNodeRRsetPtr
+getNSECForNXRRSET(const ZoneData& zone_data,
+                  ZoneFinder::FindOptions options,
+                  const ZoneNode* node)
+{
+    if (zone_data.isSigned() &&
+        !zone_data.isNSEC3Signed() &&
+        (options & ZoneFinder::FIND_DNSSEC) != 0) {
+        const RdataSet* found = RdataSet::find(node->getData(), RRType::NSEC());
+        if (found != NULL) {
+            return (createTreeNodeRRset(node, found));
+        }
+    }
+    return (TreeNodeRRsetPtr());
+}
+
+
+class FindNodeResult {
+public:
+    // Bitwise flags to represent supplemental information of the
+    // search result:
+    // Search resulted in a wildcard match.
+    static const unsigned int FIND_WILDCARD = 1;
+    // Search encountered a zone cut due to NS but continued to look for
+    // a glue.
+    static const unsigned int FIND_ZONECUT = 2;
+
+    FindNodeResult(ZoneFinder::Result code_param,
+                   ZoneNode* node_param,
+                   TreeNodeRRsetPtr rrset_param,
+                   unsigned int flags_param = 0) :
+        code(code_param),
+        node(node_param),
+        rrset(rrset_param),
+        flags(flags_param)
+    {}
+    const ZoneFinder::Result code;
+    const ZoneNode* node;
+    TreeNodeRRsetPtr rrset;
+    const unsigned int flags;
+};
+
+FindNodeResult findNode(const ZoneData& zone_data,
+                        const Name& name,
+                        ZoneNodeChain& chain,
+                        ZoneFinder::FindOptions options)
+{
+    ZoneNode* node = NULL;
+    FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
+
+    const ZoneTree& tree(zone_data.getZoneTree());
+    ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name),
+                                        &node, chain, cutCallback, &state);
+    const unsigned int zonecut_flag =
+        (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
+    if (result == ZoneTree::EXACTMATCH) {
+        return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_,
+                               zonecut_flag));
+    } else if (result == ZoneTree::PARTIALMATCH) {
+        assert(node != NULL);
+        if (state.dname_node_ != NULL) { // DNAME
+            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
+                arg(state.rrset_->getName());
+            return (FindNodeResult(ZoneFinder::DNAME, NULL, state.rrset_));
+        }
+        if (state.zonecut_node_ != NULL) { // DELEGATION due to NS
+            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
+                arg(state.rrset_->getName());
+            return (FindNodeResult(ZoneFinder::DELEGATION, NULL, state.rrset_));
+        }
+        if (chain.getLastComparisonResult().getRelation() ==
+            NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
+            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name);
+            return (FindNodeResult(ZoneFinder::NXRRSET, node,
+                                   getClosestNSEC(zone_data, chain, options)));
+        }
+        // TODO: wildcard (see memory_datasrc.cc:480)
+        // Nothing really matched.
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
+        return (FindNodeResult(ZoneFinder::NXDOMAIN, node,
+                               getClosestNSEC(zone_data, chain, options)));
+    } else {
+        // 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 " <<
+                             zone_data.getOriginNode()->getName());
+    }
+}
+
+} // end anonymous namespace
+
+ZoneFinderResultContext
+InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
+                                  const isc::dns::RRType& type,
+                                  std::vector<ConstRRsetPtr>* target,
+                                  const FindOptions options)
+{
+    // Get the node.  All other cases than an exact match are handled
+    // in findNode().  We simply construct a result structure and return.
+    ZoneNodeChain chain;
+    const FindNodeResult node_result =
+        findNode(zone_data_, name, chain, options);
+    if (node_result.code != SUCCESS) {
+        return (createFindResult(zone_data_, node_result.code, node_result.rrset, NULL));
+    }
+
+    const ZoneNode* node = node_result.node;
+    assert(node != NULL);
+
+    // We've found an exact match, may or may not be a result of wildcard.
+    // TODO, ticket #2110
+    // If there is an exact match but the node is empty, it's equivalent
+    // to NXRRSET.
+    if (node->isEmpty()) {
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
+            arg(name);
+        return (createFindResult(zone_data_, NXRRSET,
+                                 getClosestNSEC(zone_data_, chain, options),
+                                 node));
+    }
+
+    const RdataSet* found;
+
+    // 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.
+    // There is one exception: the case for DS query, which should always
+    // be considered in-zone lookup.
+    if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
+            node != zone_data_.getOriginNode() && type != RRType::DS()) {
+        found = RdataSet::find(node->getData(), RRType::NS());
+        if (found != NULL) {
+            LOG_DEBUG(logger, DBG_TRACE_DATA,
+                      DATASRC_MEM_EXACT_DELEGATION).arg(name);
+            // TODO: rename argument (wildcards, see #2110)
+            return (createFindResult(zone_data_, DELEGATION, createTreeNodeRRset(node, found), node));
+        }
+    }
+
+    // Handle type any query
+    if (target != NULL && node->getData() != NULL) {
+        // Empty domain will be handled as NXRRSET by normal processing
+        const RdataSet* cur_rds = node->getData();
+        while (cur_rds != NULL) {
+            target->push_back(createTreeNodeRRset(node, cur_rds));
+            cur_rds = cur_rds->getNext();
+        }
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
+            arg(name);
+        return (createFindResult(zone_data_, SUCCESS, TreeNodeRRsetPtr(), node));
+    }
+
+    const RdataSet* currds = node->getData();
+    while (currds != NULL) {
+        currds = currds->getNext();
+    }
+    found = RdataSet::find(node->getData(), type);
+    if (found != NULL) {
+        // Good, it is here
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
+            arg(type);
+        return (createFindResult(zone_data_, SUCCESS, createTreeNodeRRset(node, found), node));
+    } else {
+        // Next, try CNAME.
+        found = RdataSet::find(node->getData(), RRType::CNAME());
+        if (found != NULL) {
+            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
+            return (createFindResult(zone_data_, CNAME, createTreeNodeRRset(node, found), node));
+        }
+    }
+    // No exact match or CNAME.  Get NSEC if necessary and return NXRRSET.
+    return (createFindResult(zone_data_, NXRRSET, getNSECForNXRRSET(zone_data_, options, node), node));
+}
+
+isc::datasrc::ZoneFinder::FindNSEC3Result
+InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
+    (void)name;
+    (void)recursive;
+    isc_throw(isc::NotImplemented, "not completed yet! please implement me");
+}
+
+}
+}
+}

+ 93 - 0
src/lib/datasrc/memory/zone_finder.h

@@ -0,0 +1,93 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_ZONE_FINDER_H
+#define DATASRC_MEMORY_ZONE_FINDER_H 1
+
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/treenode_rrset.h>
+
+#include <datasrc/zone.h>
+#include <dns/name.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+
+namespace isc {
+namespace datasrc {
+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;
+};
+
+class InMemoryZoneFinder : boost::noncopyable, public ZoneFinder {
+public:
+    InMemoryZoneFinder(const ZoneData& zone_data) : zone_data_(zone_data) {}
+
+    virtual boost::shared_ptr<ZoneFinder::Context> find(
+        const isc::dns::Name& name,
+        const isc::dns::RRType& type,
+        const FindOptions options = FIND_DEFAULT);
+
+    virtual boost::shared_ptr<ZoneFinder::Context> findAll(
+        const isc::dns::Name& name,
+        std::vector<isc::dns::ConstRRsetPtr>& target,
+        const FindOptions options = FIND_DEFAULT);
+
+    virtual FindNSEC3Result
+    findNSEC3(const isc::dns::Name& name, bool recursive);
+
+    virtual isc::dns::Name getOrigin() const {
+        return zone_data_.getOriginNode()->getName();
+    }
+
+    virtual isc::dns::RRClass getClass() const {
+        isc_throw(isc::NotImplemented, "this method is not relevant and should "
+                                       "probably be removed from the API");
+    }
+
+    class Context;
+
+private:
+    ZoneFinderResultContext find_internal(const isc::dns::Name& name,
+                                          const isc::dns::RRType& type,
+                                          std::vector<isc::dns::ConstRRsetPtr>* target,
+                                          const FindOptions options =
+                                          FIND_DEFAULT);
+
+    const ZoneData& zone_data_;
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_ZONE_FINDER_H

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

@@ -156,6 +156,7 @@ performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist) {
                        finder.findNSEC3(origin, false));
     }
 
+/*
     // Recursive mode doesn't change the result in this case.
     {
         SCOPED_TRACE("apex, recursive mode");
@@ -211,6 +212,7 @@ performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist) {
                        zzz_nsec3_text, "",
                        finder.findNSEC3(largest_name, false));
     }
+*/
 }
 
 }