Browse Source

[master] Merge branch 'trac2309'

JINMEI Tatuya 12 years ago
parent
commit
ee17e979fc

+ 7 - 6
src/bin/auth/query.cc

@@ -101,8 +101,11 @@ Query::ResponseCreator::create(Message& response,
 
 void
 Query::addSOA(ZoneFinder& finder) {
-    ZoneFinderContextPtr soa_ctx = finder.find(finder.getOrigin(),
-                                               RRType::SOA(), dnssec_opt_);
+    // This method is always called in finding SOA for a negative response,
+    // so we specify the use of min(RRTTL, SOA MINTTL) as specified in
+    // Section 3 of RFC2308.
+    ZoneFinderContextPtr soa_ctx = finder.findAtOrigin(RRType::SOA(), true,
+                                                       dnssec_opt_);
     if (soa_ctx->code != ZoneFinder::SUCCESS) {
         isc_throw(NoSOA, "There's no SOA record in zone " <<
             finder.getOrigin().toText());
@@ -318,11 +321,9 @@ void
 Query::addAuthAdditional(ZoneFinder& finder,
                          vector<ConstRRsetPtr>& additionals)
 {
-    const Name& origin = finder.getOrigin();
-
     // Fill in authority and addtional sections.
-    ConstZoneFinderContextPtr ns_context = finder.find(origin, RRType::NS(),
-                                                       dnssec_opt_);
+    ConstZoneFinderContextPtr ns_context =
+        finder.findAtOrigin(RRType::NS(), false, dnssec_opt_);
 
     // zone origin name should have NS records
     if (ns_context->code != ZoneFinder::SUCCESS) {

+ 50 - 32
src/bin/auth/tests/query_unittest.cc

@@ -90,6 +90,10 @@ private:
 #include <auth/tests/example_base_inc.cc>
 #include <auth/tests/example_nsec3_inc.cc>
 
+// This SOA is used in negative responses; its RRTTL is set to SOA's MINTTL
+const char* const soa_minttl_txt =
+    "example.com. 0 IN SOA . . 1 0 0 0 0\n";
+
 // This is used only in one pathological test case.
 const char* const zone_ds_txt =
     "example.com. 3600 IN DS 57855 5 1 "
@@ -1207,7 +1211,7 @@ TEST_P(QueryTest, nodomainANY) {
     EXPECT_NO_THROW(query.process(*list_, Name("nxdomain.example.com"),
                                   RRType::ANY(), response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
-                  NULL, soa_txt, NULL, mock_finder->getOrigin());
+                  NULL, soa_minttl_txt, NULL, mock_finder->getOrigin());
 }
 
 // This tests that when we need to look up Zone's apex NS records for
@@ -1345,7 +1349,7 @@ TEST_P(QueryTest, nxdomain) {
                                   Name("nxdomain.example.com"), qtype,
                                   response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
-                  NULL, soa_txt, NULL, mock_finder->getOrigin());
+                  NULL, soa_minttl_txt, NULL, mock_finder->getOrigin());
 }
 
 TEST_P(QueryTest, nxdomainWithNSEC) {
@@ -1356,8 +1360,8 @@ TEST_P(QueryTest, nxdomainWithNSEC) {
                                   Name("nxdomain.example.com"), qtype,
                                   response, true));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
-                  NULL, (string(soa_txt) +
-                         string("example.com. 3600 IN RRSIG ") +
+                  NULL, (string(soa_minttl_txt) +
+                         string("example.com. 0 IN RRSIG ") +
                          getCommonRRSIGText("SOA") + "\n" +
                          string(nsec_nxdomain_txt) + "\n" +
                          string("noglue.example.com. 3600 IN RRSIG ") +
@@ -1382,8 +1386,8 @@ TEST_P(QueryTest, nxdomainWithNSEC2) {
     query.process(*list_, Name("(.no.example.com"), qtype, response,
                   true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
-                  NULL, (string(soa_txt) +
-                         string("example.com. 3600 IN RRSIG ") +
+                  NULL, (string(soa_minttl_txt) +
+                         string("example.com. 0 IN RRSIG ") +
                          getCommonRRSIGText("SOA") + "\n" +
                          string(nsec_mx_txt) + "\n" +
                          string("mx.example.com. 3600 IN RRSIG ") +
@@ -1407,8 +1411,8 @@ TEST_P(QueryTest, nxdomainWithNSECDuplicate) {
     query.process(*list_, Name("nx.no.example.com"), qtype, response,
                   true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 4, 0,
-                  NULL, (string(soa_txt) +
-                         string("example.com. 3600 IN RRSIG ") +
+                  NULL, (string(soa_minttl_txt) +
+                         string("example.com. 0 IN RRSIG ") +
                          getCommonRRSIGText("SOA") + "\n" +
                          string(nsec_no_txt) + "\n" +
                          string(").no.example.com. 3600 IN RRSIG ") +
@@ -1474,8 +1478,8 @@ TEST_F(QueryTestForMockOnly, nxdomainBadNSEC5) {
     query.process(*list_, Name("nxdomain.example.com"), qtype,
                   response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
-                  NULL, (string(soa_txt) +
-                         string("example.com. 3600 IN RRSIG ") +
+                  NULL, (string(soa_minttl_txt) +
+                         string("example.com. 0 IN RRSIG ") +
                          getCommonRRSIGText("SOA") + "\n" +
                          string(nsec_nxdomain_txt) + "\n" +
                          string("noglue.example.com. 3600 IN RRSIG ") +
@@ -1503,7 +1507,7 @@ TEST_P(QueryTest, nxrrset) {
                                   RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
-                  NULL, soa_txt, NULL, mock_finder->getOrigin());
+                  NULL, soa_minttl_txt, NULL, mock_finder->getOrigin());
 }
 
 TEST_P(QueryTest, nxrrsetWithNSEC) {
@@ -1513,7 +1517,8 @@ TEST_P(QueryTest, nxrrsetWithNSEC) {
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec_www_txt) + "\n" +
                    string("www.example.com. 3600 IN RRSIG ") +
@@ -1534,7 +1539,8 @@ TEST_P(QueryTest, emptyNameWithNSEC) {
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec_mx_txt) + "\n" +
                    string("mx.example.com. 3600 IN RRSIG ") +
@@ -1550,7 +1556,8 @@ TEST_P(QueryTest, nxrrsetWithoutNSEC) {
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n").c_str(),
                   NULL, mock_finder->getOrigin());
 }
@@ -1706,7 +1713,8 @@ TEST_P(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec_wild_txt) +
                    string("*.wild.example.com. 3600 IN RRSIG ") +
@@ -1729,7 +1737,8 @@ TEST_P(QueryTest, wildcardNxrrsetWithNSEC) {
                   RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec_wild_txt_nxrrset) +
                    string("*.uwild.example.com. 3600 IN RRSIG ") +
@@ -1753,7 +1762,8 @@ TEST_P(QueryTest, wildcardNxrrsetWithNSEC3) {
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 8, 0, NULL,
                   // SOA + its RRSIG
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    // NSEC3 for the closest encloser + its RRSIG
                    string(nsec3_uwild_txt) +
@@ -1816,7 +1826,8 @@ TEST_P(QueryTest, wildcardEmptyWithNSEC) {
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec_empty_prev_txt) +
                    string("t.example.com. 3600 IN RRSIG ") +
@@ -2043,7 +2054,7 @@ TEST_P(QueryTest, DNAME_NX_RRSET) {
                     RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
-        NULL, soa_txt, NULL, mock_finder->getOrigin());
+        NULL, soa_minttl_txt, NULL, mock_finder->getOrigin());
 }
 
 /*
@@ -2307,8 +2318,8 @@ TEST_P(QueryTest, dsAboveDelegationNoData) {
                                   RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (string(soa_txt) +
-                   string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(unsigned_delegation_nsec_txt) +
                    "unsigned-delegation.example.com. 3600 IN RRSIG " +
@@ -2324,7 +2335,8 @@ TEST_P(QueryTest, dsBelowDelegation) {
                                   RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec_apex_txt) + "\n" +
                    string("example.com. 3600 IN RRSIG ") +
@@ -2342,7 +2354,8 @@ TEST_P(QueryTest, dsBelowDelegationWithDS) {
                                   RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA")).c_str(), NULL,
                   mock_finder->getOrigin());
 }
@@ -2382,9 +2395,10 @@ TEST_F(QueryTestForMockOnly, dsAtGrandParentAndChild) {
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(childname)));
     query.process(*list_, childname, RRType::DS(), response, true);
+    // Note that RR TTL of SOA and its RRSIG are set to SOA MINTTL, 0
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (childname.toText() + " 3600 IN SOA . . 0 0 0 0 0\n" +
-                   childname.toText() + " 3600 IN RRSIG " +
+                  (childname.toText() + " 0 IN SOA . . 0 0 0 0 0\n" +
+                   childname.toText() + " 0 IN RRSIG " +
                    getCommonRRSIGText("SOA") + "\n" +
                    childname.toText() + " 3600 IN NSEC " +
                    childname.toText() + " SOA NSEC RRSIG\n" +
@@ -2404,9 +2418,10 @@ TEST_F(QueryTestForMockOnly, dsAtRoot) {
                               new AlternateZoneFinder(Name::ROOT_NAME())));
     query.process(*list_, Name::ROOT_NAME(), RRType::DS(), response,
                   true);
+    // Note that RR TTL of SOA and its RRSIG are set to SOA MINTTL, 0
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (string(". 3600 IN SOA . . 0 0 0 0 0\n") +
-                   ". 3600 IN RRSIG " + getCommonRRSIGText("SOA") + "\n" +
+                  (string(". 0 IN SOA . . 0 0 0 0 0\n") +
+                   ". 0 IN RRSIG " + getCommonRRSIGText("SOA") + "\n" +
                    ". 3600 IN NSEC " + ". SOA NSEC RRSIG\n" +
                    ". 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC")).c_str(), NULL);
@@ -2443,7 +2458,8 @@ TEST_P(QueryTest, nxrrsetWithNSEC3) {
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec3_www_txt) + "\n" +
                    nsec3_hash_.calculate(Name("www.example.com.")) +
@@ -2478,7 +2494,8 @@ TEST_P(QueryTest, nxrrsetWithNSEC3_ds_exact) {
     query.process(*list_, Name("unsigned-delegation.example.com."),
                   RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(unsigned_delegation_nsec3_txt) + "\n" +
                    nsec3_hash_.calculate(
@@ -2500,7 +2517,8 @@ TEST_P(QueryTest, nxrrsetWithNSEC3_ds_no_exact) {
     query.process(*list_, Name("unsigned-delegation-optout.example.com."),
                   RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
-                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec3_apex_txt) + "\n" +
                    nsec3_hash_.calculate(Name("example.com.")) +
@@ -2528,8 +2546,8 @@ TEST_P(QueryTest, nxdomainWithNSEC3Proof) {
                   response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 8, 0, NULL,
                   // SOA + its RRSIG
-                  (string(soa_txt) +
-                   string("example.com. 3600 IN RRSIG ") +
+                  (string(soa_minttl_txt) +
+                   string("example.com. 0 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    // NSEC3 for the closest encloser + its RRSIG
                    string(nsec3_apex_txt) + "\n" +

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

@@ -28,10 +28,12 @@ libb10_datasrc_la_SOURCES += rbnode_rrset.h
 libb10_datasrc_la_SOURCES += rbtree.h
 libb10_datasrc_la_SOURCES += exceptions.h
 libb10_datasrc_la_SOURCES += zonetable.h zonetable.cc
-libb10_datasrc_la_SOURCES += zone.h zone_finder.cc zone_finder_context.cc
+libb10_datasrc_la_SOURCES += zone.h zone_finder.h zone_finder.cc
+libb10_datasrc_la_SOURCES += zone_finder_context.cc
+libb10_datasrc_la_SOURCES += zone_iterator.h
 libb10_datasrc_la_SOURCES += result.h
 libb10_datasrc_la_SOURCES += logger.h logger.cc
-libb10_datasrc_la_SOURCES += client.h client.cc iterator.h
+libb10_datasrc_la_SOURCES += client.h client.cc
 libb10_datasrc_la_SOURCES += database.h database.cc
 libb10_datasrc_la_SOURCES += factory.h factory.cc
 libb10_datasrc_la_SOURCES += client_list.h client_list.cc

+ 2 - 1
src/lib/datasrc/client.h

@@ -21,6 +21,7 @@
 #include <boost/shared_ptr.hpp>
 
 #include <datasrc/zone.h>
+#include <datasrc/zone_finder.h>
 
 /// \file
 /// Datasource clients
@@ -66,7 +67,7 @@
 namespace isc {
 namespace datasrc {
 
-// The iterator.h is not included on purpose, most application won't need it
+// zone_iterator.h is not included on purpose, most application won't need it
 class ZoneIterator;
 typedef boost::shared_ptr<ZoneIterator> ZoneIteratorPtr;
 

+ 1 - 1
src/lib/datasrc/database.cc

@@ -18,7 +18,7 @@
 
 #include <datasrc/database.h>
 #include <datasrc/data_source.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <datasrc/rrset_collection_base.h>
 
 #include <exceptions/exceptions.h>

+ 1 - 1
src/lib/datasrc/memory/memory_client.h

@@ -17,7 +17,7 @@
 
 #include <util/memory_segment.h>
 
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <datasrc/client.h>
 #include <datasrc/memory/zone_table.h>
 #include <datasrc/memory/zone_data.h>

+ 1 - 1
src/lib/datasrc/memory/zone_data_loader.h

@@ -17,7 +17,7 @@
 
 #include <datasrc/exceptions.h>
 #include <datasrc/memory/zone_data.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <dns/name.h>
 #include <dns/rrclass.h>
 #include <util/memory_segment.h>

+ 1 - 1
src/lib/datasrc/memory/zone_finder.cc

@@ -17,7 +17,7 @@
 #include <datasrc/memory/treenode_rrset.h>
 #include <datasrc/memory/rdata_serialization.h>
 
-#include <datasrc/zone.h>
+#include <datasrc/zone_finder.h>
 #include <datasrc/data_source.h>
 #include <dns/labelsequence.h>
 #include <dns/name.h>

+ 1 - 1
src/lib/datasrc/memory/zone_finder.h

@@ -18,7 +18,7 @@
 #include <datasrc/memory/zone_data.h>
 #include <datasrc/memory/treenode_rrset.h>
 
-#include <datasrc/zone.h>
+#include <datasrc/zone_finder.h>
 #include <dns/name.h>
 #include <dns/rrset.h>
 #include <dns/rrtype.h>

+ 2 - 1
src/lib/datasrc/memory_datasrc.cc

@@ -26,9 +26,10 @@
 #include <datasrc/rbtree.h>
 #include <datasrc/rbnode_rrset.h>
 #include <datasrc/logger.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <datasrc/data_source.h>
 #include <datasrc/factory.h>
+#include <datasrc/zone_finder.h>
 
 #include <boost/function.hpp>
 #include <boost/shared_ptr.hpp>

+ 1 - 0
src/lib/datasrc/rrset_collection_base.cc

@@ -14,6 +14,7 @@
 
 #include <datasrc/rrset_collection_base.h>
 #include <datasrc/zone_loader.h>
+#include <datasrc/zone_finder.h>
 #include <exceptions/exceptions.h>
 
 using namespace isc;

+ 1 - 1
src/lib/datasrc/tests/client_list_unittest.cc

@@ -14,7 +14,7 @@
 
 #include <datasrc/client_list.h>
 #include <datasrc/client.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <datasrc/data_source.h>
 #include <datasrc/memory/memory_client.h>
 #include <datasrc/memory/zone_table_segment.h>

+ 234 - 31
src/lib/datasrc/tests/database_unittest.cc

@@ -25,8 +25,9 @@
 
 #include <datasrc/database.h>
 #include <datasrc/zone.h>
+#include <datasrc/zone_finder.h>
 #include <datasrc/data_source.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <datasrc/sqlite3_accessor.h>
 
 #include <testutils/dnsmessage_test.h>
@@ -1728,58 +1729,107 @@ TYPED_TEST(DatabaseClientTest, updateAfterDeleteIterator) {
 }
 
 void
-doFindTest(ZoneFinder& finder,
-           const isc::dns::Name& name,
-           const isc::dns::RRType& type,
-           const isc::dns::RRType& expected_type,
-           const isc::dns::RRTTL expected_ttl,
-           ZoneFinder::Result expected_result,
-           const std::vector<std::string>& expected_rdatas,
-           const std::vector<std::string>& expected_sig_rdatas,
-           ZoneFinder::FindResultFlags expected_flags =
-           ZoneFinder::RESULT_DEFAULT,
-           const isc::dns::Name& expected_name = isc::dns::Name::ROOT_NAME(),
-           const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
+findTestCommon(ZoneFinder& finder, const isc::dns::Name& name,
+               const isc::dns::RRType& type,
+               ConstZoneFinderContextPtr actual_result,
+               const isc::dns::RRType& expected_type,
+               const isc::dns::RRTTL expected_ttl,
+               ZoneFinder::Result expected_result,
+               const std::vector<string>& expected_rdatas,
+               const std::vector<string>& expected_sig_rdatas,
+               ZoneFinder::FindResultFlags expected_flags,
+               const isc::dns::Name& expected_name,
+               const ZoneFinder::FindOptions options)
 {
-    SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
-    ConstZoneFinderContextPtr result = finder.find(name, type, options);
-    ASSERT_EQ(expected_result, result->code) << name << " " << type;
+    ASSERT_EQ(expected_result, actual_result->code) << name << " " << type;
     EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-              result->isWildcard());
+              actual_result->isWildcard());
     EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
-              result->isNSECSigned());
+              actual_result->isNSECSigned());
     EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
-              result->isNSEC3Signed());
-    if (!expected_rdatas.empty() && result->rrset) {
-        checkRRset(result->rrset, expected_name != Name(".") ? expected_name :
+              actual_result->isNSEC3Signed());
+    if (!expected_rdatas.empty() && actual_result->rrset) {
+        checkRRset(actual_result->rrset,
+                   expected_name != Name::ROOT_NAME() ? expected_name :
                    name, finder.getClass(), expected_type, expected_ttl,
                    expected_rdatas);
         if ((options & ZoneFinder::FIND_DNSSEC) == ZoneFinder::FIND_DNSSEC) {
-            if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
-                checkRRset(result->rrset->getRRsig(),
-                           expected_name != Name(".") ? expected_name : name,
+            if (!expected_sig_rdatas.empty() &&
+                actual_result->rrset->getRRsig()) {
+                checkRRset(actual_result->rrset->getRRsig(),
+                           expected_name != Name::ROOT_NAME() ?
+                           expected_name : name,
                            finder.getClass(),
                            isc::dns::RRType::RRSIG(), expected_ttl,
                            expected_sig_rdatas);
             } else if (expected_sig_rdatas.empty()) {
-                EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig()) <<
-                    "Unexpected RRSIG: " << result->rrset->getRRsig()->toText();
+                EXPECT_EQ(isc::dns::RRsetPtr(),
+                          actual_result->rrset->getRRsig()) <<
+                    "Unexpected RRSIG: " <<
+                    actual_result->rrset->getRRsig()->toText();
             } else {
                 ADD_FAILURE() << "Missing RRSIG";
             }
-        } else if (result->rrset->getRRsig()) {
-            EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig()) <<
-                "Unexpected RRSIG: " << result->rrset->getRRsig()->toText();
+        } else if (actual_result->rrset->getRRsig()) {
+            EXPECT_EQ(isc::dns::RRsetPtr(), actual_result->rrset->getRRsig())
+                << "Unexpected RRSIG: "
+                << actual_result->rrset->getRRsig()->toText();
         }
     } else if (expected_rdatas.empty()) {
-        EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset) <<
-            "Unexpected RRset: " << result->rrset->toText();
+        EXPECT_EQ(isc::dns::RRsetPtr(), actual_result->rrset) <<
+            "Unexpected RRset: " << actual_result->rrset->toText();
     } else {
         ADD_FAILURE() << "Missing result";
     }
 }
 
 void
+doFindTest(ZoneFinder& finder,
+           const isc::dns::Name& name,
+           const isc::dns::RRType& type,
+           const isc::dns::RRType& expected_type,
+           const isc::dns::RRTTL expected_ttl,
+           ZoneFinder::Result expected_result,
+           const std::vector<std::string>& expected_rdatas,
+           const std::vector<std::string>& expected_sig_rdatas,
+           ZoneFinder::FindResultFlags expected_flags =
+           ZoneFinder::RESULT_DEFAULT,
+           const isc::dns::Name& expected_name = isc::dns::Name::ROOT_NAME(),
+           const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
+{
+    SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
+    ConstZoneFinderContextPtr result = finder.find(name, type, options);
+    findTestCommon(finder, name, type, result, expected_type, expected_ttl,
+                   expected_result, expected_rdatas, expected_sig_rdatas,
+                   expected_flags, expected_name, options);
+}
+
+void
+doFindAtOriginTest(ZoneFinder& finder,
+                   const isc::dns::Name& origin,
+                   const isc::dns::RRType& type,
+                   const isc::dns::RRType& expected_type,
+                   const isc::dns::RRTTL expected_ttl,
+                   ZoneFinder::Result expected_result,
+                   const std::vector<std::string>& expected_rdatas,
+                   const std::vector<std::string>& expected_sig_rdatas,
+                   bool use_minttl = false,
+                   ZoneFinder::FindResultFlags expected_flags =
+                   ZoneFinder::RESULT_DEFAULT,
+                   const isc::dns::Name& expected_name =
+                   isc::dns::Name::ROOT_NAME(),
+                   const ZoneFinder::FindOptions options =
+                   ZoneFinder::FIND_DEFAULT)
+{
+    SCOPED_TRACE("doFindOriginTest " + origin.toText() + " " + type.toText());
+    ConstZoneFinderContextPtr result =
+        finder.findAtOrigin(type, use_minttl, options);
+    findTestCommon(finder, origin, type, result, expected_type, expected_ttl,
+                   expected_result, expected_rdatas, expected_sig_rdatas,
+                   expected_flags, expected_name, options);
+}
+
+void
 doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
                     ZoneFinder::Result expected_result,
                     const isc::dns::RRType expected_type,
@@ -2116,6 +2166,159 @@ TYPED_TEST(DatabaseClientTest, find) {
                this->expected_rdatas_, this->expected_sig_rdatas_);
 }
 
+TYPED_TEST(DatabaseClientTest, findAtOrigin) {
+    ZoneFinderPtr finder(this->getFinder());
+
+    // Specified type of RR exists, no DNSSEC
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("ns.example.com.");
+    doFindAtOriginTest(*finder, this->zname_, RRType::NS(), RRType::NS(),
+                       this->rrttl_, ZoneFinder::SUCCESS,
+                       this->expected_rdatas_, this->expected_sig_rdatas_);
+
+    // Specified type of RR exists, with DNSSEC
+    this->expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 "
+                                         "20000201000000 12345 example.org. "
+                                         "FAKEFAKEFAKE");
+    doFindAtOriginTest(*finder, this->zname_, RRType::NS(), RRType::NS(),
+                       this->rrttl_, ZoneFinder::SUCCESS,
+                       this->expected_rdatas_, this->expected_sig_rdatas_,
+                       false, ZoneFinder::RESULT_DEFAULT, this->zname_,
+                       ZoneFinder::FIND_DNSSEC);
+
+    // Specified type of RR doesn't exist, no DNSSEC
+    this->expected_rdatas_.clear();
+    this->expected_sig_rdatas_.clear();
+    doFindAtOriginTest(*finder, this->zname_, RRType::TXT(), this->qtype_,
+                       this->rrttl_, ZoneFinder::NXRRSET,
+                       this->expected_rdatas_, this->expected_sig_rdatas_);
+
+    // Specified type of RR doesn't exist, with DNSSEC
+    this->expected_rdatas_.clear();
+    this->expected_sig_rdatas_.clear();
+    this->expected_rdatas_.push_back(
+        "acnamesig1.example.org. A NS RRSIG NSEC");
+    this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+                                         "20000201000000 12345 example.org. "
+                                         "FAKEFAKEFAKE");
+    doFindAtOriginTest(*finder, this->zname_, RRType::TXT(), RRType::NSEC(),
+                       this->rrttl_, ZoneFinder::NXRRSET,
+                       this->expected_rdatas_, this->expected_sig_rdatas_,
+                       false, ZoneFinder::RESULT_NSEC_SIGNED,
+                       this->zname_, ZoneFinder::FIND_DNSSEC);
+
+    // Specified type of RR doesn't exist, with DNSSEC, enabling NSEC3
+    this->current_accessor_->enableNSEC3();
+    this->expected_rdatas_.clear();
+    this->expected_sig_rdatas_.clear();
+    doFindAtOriginTest(*finder, this->zname_, RRType::TXT(), RRType::TXT(),
+                       this->rrttl_, ZoneFinder::NXRRSET,
+                       this->expected_rdatas_, this->expected_sig_rdatas_,
+                       false, ZoneFinder::RESULT_NSEC3_SIGNED,
+                       this->zname_, ZoneFinder::FIND_DNSSEC);
+}
+
+TYPED_TEST(DatabaseClientTest, findAtOriginWithMinTTL) {
+    // First, replace the SOA of the test zone so that its RR TTL is larger
+    // than MINTTL (the original data are used in many places, so replacing
+    // them just for this doesn't make sense).
+    RRsetPtr old_soa(new RRset(this->zname_, this->qclass_, RRType::SOA(),
+                               this->rrttl_));
+    old_soa->addRdata(rdata::createRdata(RRType::SOA(), this->qclass_,
+                                         "ns1.example.org. admin.example.org. "
+                                         "1234 3600 1800 2419200 7200"));
+
+    const string new_soa_rdata = "ns1.example.org. admin.example.org. "
+        "1234 3600 1800 2419200 1200";
+    RRsetPtr new_soa(new RRset(this->zname_, this->qclass_, RRType::SOA(),
+                               this->rrttl_));
+    new_soa->addRdata(rdata::createRdata(RRType::SOA(), this->qclass_,
+                                         new_soa_rdata));
+
+    this->updater_ = this->client_->getUpdater(this->zname_, false);
+    this->updater_->deleteRRset(*old_soa);
+    this->updater_->addRRset(*new_soa);
+    this->updater_->commit();
+
+    ZoneFinderPtr finder = this->getFinder();
+
+    // Specify the use of min TTL, then the resulting TTL should be derived
+    // from the SOA MINTTL (which is smaller).
+    this->expected_rdatas_.push_back(new_soa_rdata);
+    doFindAtOriginTest(*finder, this->zname_, RRType::SOA(), RRType::SOA(),
+                       RRTTL(1200), ZoneFinder::SUCCESS,
+                       this->expected_rdatas_, this->expected_sig_rdatas_,
+                       true);
+
+    // If DNSSEC is requested, TTL of the RRSIG should also be the min.
+    this->expected_sig_rdatas_.push_back(
+        "SOA 5 3 3600 20000101000000 "
+        "20000201000000 12345 example.org. FAKEFAKEFAKE");
+    doFindAtOriginTest(*finder, this->zname_, RRType::SOA(), RRType::SOA(),
+                       RRTTL(1200), ZoneFinder::SUCCESS,
+                       this->expected_rdatas_, this->expected_sig_rdatas_,
+                       true, ZoneFinder::RESULT_DEFAULT, this->zname_,
+                       ZoneFinder::FIND_DNSSEC);
+
+    // Not really intended usage, but specify the use of min TTL for non SOA.
+    // It should still work as specified.
+    this->expected_rdatas_.clear();
+    this->expected_sig_rdatas_.clear();
+    this->expected_rdatas_.push_back("ns.example.com.");
+    doFindAtOriginTest(*finder, this->zname_, RRType::NS(), RRType::NS(),
+                       RRTTL(1200), ZoneFinder::SUCCESS,
+                       this->expected_rdatas_, this->expected_sig_rdatas_,
+                       true);
+
+    // If we don't request the use of min TTL, the original TTL will be used.
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back(new_soa_rdata);
+    doFindAtOriginTest(*finder, this->zname_, RRType::SOA(), RRType::SOA(),
+                       this->rrttl_, ZoneFinder::SUCCESS,
+                       this->expected_rdatas_, this->expected_sig_rdatas_);
+
+    // If no RRset is returned, use_minttl doesn't matter (it shouldn't cause
+    // disruption)
+    this->expected_rdatas_.clear();
+    doFindAtOriginTest(*finder, this->zname_, RRType::TXT(), this->qtype_,
+                       this->rrttl_, ZoneFinder::NXRRSET,
+                       this->expected_rdatas_, this->expected_sig_rdatas_,
+                       true);
+
+    // If it results in NXRRSET with NSEC, and if we specify the use of min
+    // TTL, the NSEC and RRSIG should have the min TTL (again, though, this
+    // use case is not really the intended one)
+    this->expected_rdatas_.push_back(
+        "acnamesig1.example.org. A NS RRSIG NSEC");
+    this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+                                         "20000201000000 12345 example.org. "
+                                         "FAKEFAKEFAKE");
+    doFindAtOriginTest(*finder, this->zname_, RRType::TXT(), RRType::NSEC(),
+                       RRTTL(1200), ZoneFinder::NXRRSET,
+                       this->expected_rdatas_, this->expected_sig_rdatas_,
+                       true, ZoneFinder::RESULT_NSEC_SIGNED,
+                       this->zname_, ZoneFinder::FIND_DNSSEC);
+}
+
+TYPED_TEST(DatabaseClientTest, findAtOriginWithMinTTLBroken) {
+    // Similar to the previous case, but we intentionally remove the SOA
+    // (assuming the underlying data source doesn't complain about it).
+    // This will cause exception in subsequent findAtOrigin() with use_minttl
+    // being true.
+    RRsetPtr old_soa(new RRset(this->zname_, this->qclass_, RRType::SOA(),
+                               this->rrttl_));
+    old_soa->addRdata(rdata::createRdata(RRType::SOA(), this->qclass_,
+                                         "ns1.example.org. admin.example.org. "
+                                         "1234 3600 1800 2419200 7200"));
+    this->updater_ = this->client_->getUpdater(this->zname_, false);
+    this->updater_->deleteRRset(*old_soa);
+    this->updater_->commit();
+
+    EXPECT_THROW(this->getFinder()->findAtOrigin(RRType::NS(), true,
+                                                 ZoneFinder::FIND_DEFAULT),
+                 DataSourceError);
+}
+
 TYPED_TEST(DatabaseClientTest, findOutOfZone) {
     // If the query name is out-of-zone it should result in an exception
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());

+ 1 - 1
src/lib/datasrc/tests/faked_nsec3.h

@@ -15,7 +15,7 @@
 #ifndef FAKED_NSEC3_H
 #define FAKED_NSEC3_H
 
-#include <datasrc/zone.h>
+#include <datasrc/zone_finder.h>
 
 #include <dns/nsec3hash.h>
 

+ 1 - 1
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -28,7 +28,7 @@
 #include <datasrc/client.h>
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/data_source.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 
 #include "test_client.h"
 

+ 1 - 1
src/lib/datasrc/tests/zone_finder_context_unittest.cc

@@ -18,7 +18,7 @@
 #include <dns/name.h>
 #include <dns/rrclass.h>
 
-#include <datasrc/zone.h>
+#include <datasrc/zone_finder.h>
 #include <datasrc/memory/memory_client.h>
 #include <datasrc/memory/zone_table_segment.h>
 #include <datasrc/database.h>

+ 2 - 713
src/lib/datasrc/zone.h

@@ -24,726 +24,15 @@
 #include <datasrc/rrset_collection_base.h>
 
 #include <utility>
-#include <vector>
 
 namespace isc {
 namespace datasrc {
 
-/// \brief Out of zone exception
-///
-/// This is thrown when a method is called for a name or RRset which
-/// is not in or below the zone.
-class OutOfZone : public ZoneException {
-public:
-    OutOfZone(const char* file, size_t line, const char* what) :
-        ZoneException(file, line, what) {}
-};
-
-/// \brief The base class to search a zone for RRsets
-///
-/// The \c ZoneFinder class is an abstract base class for representing
-/// an object that performs DNS lookups in a specific zone accessible via
-/// a data source.  In general, different types of data sources (in-memory,
-/// database-based, etc) define their own derived classes of \c ZoneFinder,
-/// implementing ways to retrieve the required data through the common
-/// interfaces declared in the base class.  Each concrete \c ZoneFinder
-/// object is therefore (conceptually) associated with a specific zone
-/// of one specific data source instance.
-///
-/// The origin name and the RR class of the associated zone are available
-/// via the \c getOrigin() and \c getClass() methods, respectively.
-///
-/// The most important method of this class is \c find(), which performs
-/// the lookup for a given domain and type.  See the description of the
-/// method for details.
-///
-/// \note It's not clear whether we should request that a zone finder form a
-/// "transaction", that is, whether to ensure the finder is not susceptible
-/// to changes made by someone else than the creator of the finder.  If we
-/// don't request that, for example, two different lookup results for the
-/// same name and type can be different if other threads or programs make
-/// updates to the zone between the lookups.  We should revisit this point
-/// as we gain more experiences.
-class ZoneFinder {
-public:
-    /// Result codes of the \c find() method.
-    ///
-    /// Note: the codes are tentative.  We may need more, or we may find
-    /// some of them unnecessary as we implement more details.
-    ///
-    /// See the description of \c find() for further details of how
-    /// these results should be interpreted.
-    enum Result {
-        SUCCESS,                ///< An exact match is found.
-        DELEGATION,             ///< The search encounters a zone cut.
-        NXDOMAIN, ///< There is no domain name that matches the search name
-        NXRRSET,  ///< There is a matching name but no RRset of the search type
-        CNAME,    ///< The search encounters and returns a CNAME RR
-        DNAME    ///< The search encounters and returns a DNAME RR
-    };
-
-    /// Special attribute flags on the result of the \c find() method
-    ///
-    /// The flag values defined here are intended to signal to the caller
-    /// that it may need special handling on the result.  This is particularly
-    /// of concern when DNSSEC is requested.  For example, for negative
-    /// responses the caller would want to know whether the zone is signed
-    /// with NSEC or NSEC3 so that it can subsequently provide necessary
-    /// proof of the result.
-    ///
-    /// The caller is generally expected to get access to the information
-    /// via read-only getter methods of \c FindContext so that it won't rely
-    /// on specific details of the representation of the flags.  So these
-    /// definitions are basically only meaningful for data source
-    /// implementations.
-    enum FindResultFlags {
-        RESULT_DEFAULT = 0,       ///< The default flags
-        RESULT_WILDCARD = 1,      ///< find() resulted in a wildcard match
-        RESULT_NSEC_SIGNED = 2,   ///< The zone is signed with NSEC RRs
-        RESULT_NSEC3_SIGNED = 4   ///< The zone is signed with NSEC3 RRs
-    };
-
-    /// Find options.
-    ///
-    /// The option values are used as a parameter for \c find().
-    /// These are values of a bitmask type.  Bitwise operations can be
-    /// performed on these values to express compound options.
-    enum FindOptions {
-        FIND_DEFAULT = 0,       ///< The default options
-        FIND_GLUE_OK = 1,       ///< Allow search under a zone cut
-        FIND_DNSSEC = 2,        ///< Require DNSSEC data in the answer
-                                ///< (RRSIG, NSEC, etc.). The implementation
-                                ///< is allowed to include it even if it is
-                                ///< not set.
-        NO_WILDCARD = 4         ///< Do not try wildcard matching.
-    };
-
-protected:
-    /// \brief A convenient tuple representing a set of find() results.
-    ///
-    /// This helper structure is specifically expected to be used as an input
-    /// for the construct of the \c Context class object used by derived
-    /// ZoneFinder implementations.  This is therefore defined as protected.
-    struct ResultContext {
-        ResultContext(Result code_param,
-                      isc::dns::ConstRRsetPtr rrset_param,
-                      FindResultFlags flags_param = RESULT_DEFAULT) :
-            code(code_param), rrset(rrset_param), flags(flags_param)
-        {}
-        const Result code;
-        const isc::dns::ConstRRsetPtr rrset;
-        const FindResultFlags flags;
-    };
-
-public:
-    /// \brief A helper function to strip RRSIGs when FIND_DNSSEC is not
-    /// requested.
-    static isc::dns::ConstRRsetPtr
-    stripRRsigs(isc::dns::ConstRRsetPtr rp, const FindOptions options);
-
-    /// \brief Context of the result of a find() call.
-    ///
-    /// This class encapsulates results and (possibly) associated context
-    /// of a call to the \c find() method.   The public member variables of
-    /// this class represent the result of the call.  They are a
-    /// straightforward tuple of the result code and a pointer (and
-    /// optionally special flags) to the found RRset.
-    ///
-    /// These member variables will be initialized on construction and never
-    /// change, so for convenience we allow the applications to refer to some
-    /// of the members directly.  For some others we provide read-only accessor
-    /// methods to hide specific representation.
-    ///
-    /// Another role of this class is to provide the interface to some common
-    /// processing logic that may be necessary using the result of \c find().
-    /// Specifically, it's expected to be used in the context of DNS query
-    /// handling, where the caller would need to look into the data source
-    /// again based on the \c find() result.  For example, it would need to
-    /// get A and/or AAAA records for some of the answer or authority RRs.
-    ///
-    /// This class defines (a set of) method(s) that can be commonly used
-    /// for such purposes for any type of data source (as long as it conforms
-    /// to the public \c find() interface).  In some cases, a specific data
-    /// source implementation may want to (and can) optimize the processing
-    /// exploiting its internal data structure and the knowledge of the context
-    /// of the precedent \c find() call.  Such a data source implementation
-    /// can define a derived class of the base Context and override the
-    /// specific virtual method.
-    ///
-    /// This base class defines these common protected methods along with
-    /// some helper pure virtual methods that would be necessary for the
-    /// common methods.  If a derived class wants to use the common version
-    /// of the protected method, it needs to provide expected result through
-    /// their implementation of the pure virtual methods.
-    ///
-    /// This class object is generally expected to be associated with the
-    /// ZoneFinder that originally performed the \c find() call, and expects
-    /// the finder is valid throughout the lifetime of this object.  It's
-    /// caller's responsibility to ensure that assumption.
-    class Context {
-    public:
-        /// \brief The constructor.
-        ///
-        /// \param options The find options specified for the find() call.
-        /// \param result The result of the find() call.
-        Context(FindOptions options, const ResultContext& result) :
-            code(result.code), rrset(result.rrset),
-            flags_(result.flags), options_(options)
-        {}
-
-        /// \brief The destructor.
-        virtual ~Context() {}
-
-        const Result code;
-        const isc::dns::ConstRRsetPtr rrset;
-
-        /// Return true iff find() results in a wildcard match.
-        bool isWildcard() const { return ((flags_ & RESULT_WILDCARD) != 0); }
-
-        /// Return true when the underlying zone is signed with NSEC.
-        ///
-        /// The \c find() implementation allows this to return false if
-        /// \c FIND_DNSSEC isn't specified regardless of whether the zone
-        /// is signed or which of NSEC/NSEC3 is used.
-        ///
-        /// When this is returned, the implementation of find() must ensure
-        /// that \c rrset be a valid NSEC RRset as described in \c find()
-        /// documentation.
-        bool isNSECSigned() const {
-            return ((flags_ & RESULT_NSEC_SIGNED) != 0);
-        }
-
-        /// Return true when the underlying zone is signed with NSEC3.
-        ///
-        /// The \c find() implementation allows this to return false if
-        /// \c FIND_DNSSEC isn't specified regardless of whether the zone
-        /// is signed or which of NSEC/NSEC3 is used.
-        bool isNSEC3Signed() const {
-            return ((flags_ & RESULT_NSEC3_SIGNED) != 0);
-        }
-
-        /// \brief Find and return additional RRsets corresponding to the
-        ///        result of \c find().
-        ///
-        /// If this context is based on a normal find() call that resulted
-        /// in SUCCESS or DELEGATION, it examines the returned RRset (in many
-        /// cases NS, sometimes MX or others), searches the data source for
-        /// specified type of additional RRs for each RDATA of the RRset
-        /// (e.g., A or AAAA for the name server addresses), and stores the
-        /// result in the given vector.  The vector may not be empty; this
-        /// method appends any found RRsets to it, without touching existing
-        /// elements.
-        ///
-        /// If this context is based on a findAll() call that resulted in
-        /// SUCCESS, it performs the same process for each RRset returned in
-        /// the \c findAll() call.
-        ///
-        /// The caller specifies desired RR types of the additional RRsets
-        /// in \c requested_types.  Normally it consists of A and/or AAAA
-        /// types, but other types can be specified.
-        ///
-        /// This method is meaningful only when the precedent find()/findAll()
-        /// call resulted in SUCCESS or DELEGATION.  Otherwise this method
-        /// does nothing.
-        ///
-        /// \note The additional RRsets returned via method are limited to
-        /// ones contained in the zone which the corresponding find/findAll
-        /// call searched (possibly including glues under a zone cut where
-        /// they are applicable).  If the caller needs to get out-of-zone
-        /// additional RRsets, it needs to explicitly finds them by
-        /// identifying the corresponding zone and calls \c find() for it.
-        ///
-        /// \param requested_types A vector of RR types for desired additional
-        ///  RRsets.
-        /// \param result A vector to which any found additional RRsets are
-        /// to be inserted.
-        void getAdditional(
-            const std::vector<isc::dns::RRType>& requested_types,
-            std::vector<isc::dns::ConstRRsetPtr>& result)
-        {
-            // Perform common checks, and delegate the process to the default
-            // or specialized implementation.
-            if (code != SUCCESS && code != DELEGATION) {
-                return;
-            }
-
-            getAdditionalImpl(requested_types, result);
-        }
-
-    protected:
-        /// \brief Return the \c ZoneFinder that created this \c Context.
-        ///
-        /// A derived class implementation can return NULL if it defines
-        /// other protected methods that require a non NULL result from
-        /// this method.  Otherwise it must return a valid, non NULL pointer
-        /// to the \c ZoneFinder object.
-        ///
-        /// When returning non NULL, the ownership of the pointed object
-        /// was not transferred to the caller; it cannot be assumed to be
-        /// valid after the originating \c Context object is destroyed.
-        /// Also, the caller must not try to delete the returned object.
-        virtual ZoneFinder* getFinder() = 0;
-
-        /// \brief Return a vector of RRsets corresponding to findAll() result.
-        ///
-        /// This method returns a set of RRsets that correspond to the
-        /// returned RRsets to a prior \c findAll() call.
-        ///
-        /// A derived class implementation can return NULL if it defines
-        /// other protected methods that require a non NULL result from
-        /// this method.  Otherwise it must return a valid, non NULL pointer
-        /// to a vector that correspond to the expected set of RRsets.
-        ///
-        /// When returning non NULL, the ownership of the pointed object
-        /// was not transferred to the caller; it cannot be assumed to be
-        /// valid after the originating \c Context object is destroyed.
-        /// Also, the caller must not try to delete the returned object.
-        virtual const std::vector<isc::dns::ConstRRsetPtr>*
-        getAllRRsets() const = 0;
-
-        /// \brief Actual implementation of getAdditional().
-        ///
-        /// This base class defines a default implementation that can be
-        /// used for any type of data sources.  A data source implementation
-        /// can override it.
-        ///
-        /// The default version of this implementation requires both
-        /// \c getFinder() and \c getAllRRsets() return valid results.
-        virtual void getAdditionalImpl(
-            const std::vector<isc::dns::RRType>& requested_types,
-            std::vector<isc::dns::ConstRRsetPtr>& result);
-
-    private:
-        const FindResultFlags flags_;
-    protected:
-        const FindOptions options_;
-    };
-
-    /// \brief Generic ZoneFinder context that works for all implementations.
-    ///
-    /// This is a concrete derived class of \c ZoneFinder::Context that
-    /// only use the generic (default) versions of the protected methods
-    /// and therefore work for any data source implementation.
-    ///
-    /// A data source implementation can use this class to create a
-    /// \c Context object as a return value of \c find() or \c findAll()
-    /// method if it doesn't have to optimize specific protected methods.
-    class GenericContext : public Context {
-    public:
-        /// \brief The constructor for the normal find call.
-        ///
-        /// This constructor is expected to be called from the \c find()
-        /// method when it constructs the return value.
-        ///
-        /// \param finder The ZoneFinder on which find() is called.
-        /// \param options See the \c Context class.
-        /// \param result See the \c Context class.
-        GenericContext(ZoneFinder& finder, FindOptions options,
-                       const ResultContext& result) :
-            Context(options, result), finder_(finder)
-        {}
-
-        /// \brief The constructor for the normal findAll call.
-        ///
-        /// This constructor is expected to be called from the \c findAll()
-        /// method when it constructs the return value.
-        ///
-        /// It copies the vector that is to be returned to the caller of
-        /// \c findAll() for possible subsequent use.  Note that it cannot
-        /// simply hold a reference to the vector because the caller may
-        /// alter it after the \c findAll() call.
-        ///
-        /// \param finder The ZoneFinder on which findAll() is called.
-        /// \param options See the \c Context class.
-        /// \param result See the \c Context class.
-        /// \param all_set Reference to the vector given by the caller of
-        ///       \c findAll(), storing the RRsets to be returned.
-        GenericContext(ZoneFinder& finder, FindOptions options,
-                       const ResultContext& result,
-                       const std::vector<isc::dns::ConstRRsetPtr>& all_set) :
-            Context(options, result), finder_(finder), all_set_(all_set)
-        {}
-
-    protected:
-        virtual ZoneFinder* getFinder() { return (&finder_); }
-        virtual const std::vector<isc::dns::ConstRRsetPtr>*
-        getAllRRsets() const {
-            return (&all_set_);
-        }
-
-    private:
-        ZoneFinder& finder_;
-        std::vector<isc::dns::ConstRRsetPtr> all_set_;
-    };
-
-    ///
-    /// \name Constructors and Destructor.
-    ///
-    //@{
-protected:
-    /// The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class should
-    /// never be instantiated (except as part of a derived class).
-    ZoneFinder() {}
-public:
-    /// The destructor.
-    virtual ~ZoneFinder() {}
-    //@}
-
-    ///
-    /// \name Getter Methods
-    ///
-    /// These methods should never throw an exception.
-    //@{
-    /// Return the origin name of the zone.
-    virtual isc::dns::Name getOrigin() const = 0;
-
-    /// Return the RR class of the zone.
-    virtual isc::dns::RRClass getClass() const = 0;
-    //@}
-
-    ///
-    /// \name Search Methods
-    ///
-    //@{
-    /// Search the zone for a given pair of domain name and RR type.
-    ///
-    /// Each derived version of this method searches the underlying backend
-    /// for the data that best matches the given name and type.
-    /// This method is expected to be "intelligent", and identifies the
-    /// best possible answer for the search key.  Specifically,
-    ///
-    /// - If the search name belongs under a zone cut, it returns the code
-    ///   of \c DELEGATION and the NS RRset at the zone cut.
-    /// - If there is no matching name, it returns the code of \c NXDOMAIN.
-    /// - If there is a matching name but no RRset of the search type, it
-    ///   returns the code of \c NXRRSET.  This case includes the search name
-    ///   matches an empty node of the zone.
-    /// - If there is a CNAME RR of the searched name but there is no
-    ///   RR of the searched type of the name (so this type is different from
-    ///   CNAME), it returns the code of \c CNAME and that CNAME RR.
-    ///   Note that if the searched RR type is CNAME, it is considered
-    ///   a successful match, and the code of \c SUCCESS will be returned.
-    /// - If the search name matches a delegation point of DNAME, it returns
-    ///   the code of \c DNAME and that DNAME RR.
-    ///
-    /// No RRset will be returned in the \c NXDOMAIN and \c NXRRSET cases
-    /// (\c rrset member of \c FindContext will be NULL), unless DNSSEC data
-    /// are required.  See below for the cases with DNSSEC.
-    ///
-    /// The returned \c FindContext object can also provide supplemental
-    /// information about the search result via its methods returning a
-    /// boolean value.  Such information may be useful for the caller if
-    /// the caller wants to collect additional DNSSEC proofs based on the
-    /// search result.
-    ///
-    /// The \c options parameter specifies customized behavior of the search.
-    /// Their semantics is as follows (they are or bit-field):
-    ///
-    /// - \c FIND_GLUE_OK Allow search under a zone cut.  By default the search
-    ///   will stop once it encounters a zone cut.  If this option is specified
-    ///   it remembers information about the highest zone cut and continues
-    ///   the search until it finds an exact match for the given name or it
-    ///   detects there is no exact match.  If an exact match is found,
-    ///   RRsets for that name are searched just like the normal case;
-    ///   otherwise, if the search has encountered a zone cut, \c DELEGATION
-    ///   with the information of the highest zone cut will be returned.
-    ///   Note: the term "glue" in the DNS protocol standard may sometimes
-    ///   cause confusion: some people use this term strictly for an address
-    ///   record (type AAAA or A) for the name used in the RDATA of an NS RR;
-    ///   some others seem to give it broader flexibility.  Nevertheless,
-    ///   in this API the "GLUE OK" simply means the search by find() can
-    ///   continue beyond a zone cut; the derived class implementation does
-    ///   not have to, and should not, check whether the type is an address
-    ///   record or whether the query name is pointed by some NS RR.
-    ///   It's up to the caller with which definition of "glue" the search
-    ///   result with this option should be used.
-    /// - \c FIND_DNSSEC Request that DNSSEC data (like NSEC, RRSIGs) are
-    ///   returned with the answer. It is allowed for the data source to
-    ///   include them even when not requested.
-    /// - \c NO_WILDCARD Do not try wildcard matching.  This option is of no
-    ///   use for normal lookups; it's intended to be used to get a DNSSEC
-    ///   proof of the non existence of any matching wildcard or non existence
-    ///   of an exact match when a wildcard match is found.
-    ///
-    /// In general, \c name is expected to be included in the zone, that is,
-    /// it should be equal to or a subdomain of the zone origin.  Otherwise
-    /// this method will return \c NXDOMAIN with an empty RRset.  But such a
-    /// case should rather be considered a caller's bug.
-    ///
-    /// \note For this reason it's probably better to throw an exception
-    /// than returning \c NXDOMAIN.  This point should be revisited in a near
-    /// future version.  In any case applications shouldn't call this method
-    /// for an out-of-zone name.
-    ///
-    /// <b>DNSSEC considerations:</b>
-    /// The result when DNSSEC data are required can be very complicated,
-    /// especially if it involves negative result or wildcard match.
-    /// Specifically, if an application calls this method for DNS query
-    /// processing with DNSSEC data, and if the search result code is
-    /// either \c NXDOMAIN or \c NXRRRSET, and/or \c isWildcard() returns
-    /// true, then the application will need to find additional NSEC or
-    /// NSEC3 records for supplemental proofs.  This method helps the
-    /// application for such post search processing.
-    ///
-    /// First, it tells the application whether the zone is signed with
-    /// NSEC or NSEC3 via the \c isNSEC(3)Signed() method.  Any sanely signed
-    /// zone should be signed with either (and only one) of these two types
-    /// of RRs; however, the application should expect that the zone could
-    /// be broken and these methods could both return false.  But this method
-    /// should ensure that not both of these methods return true.
-    ///
-    /// In case it's signed with NSEC3, there is no further information
-    /// returned from this method.
-    ///
-    /// In case it's signed with NSEC, this method will possibly return
-    /// a related NSEC RRset in the \c rrset member of \c FindContext.
-    /// What kind of NSEC is returned depends on the result code
-    /// (\c NXDOMAIN or \c NXRRSET) and on whether it's a wildcard match:
-    ///
-    /// - In case of NXDOMAIN, the returned NSEC covers the queried domain
-    ///   that proves that the query name does not exist in the zone.  Note
-    ///   that this does not necessarily prove it doesn't even match a
-    ///   wildcard (even if the result of NXDOMAIN can only happen when
-    ///   there's no matching wildcard either).  It is caller's
-    ///   responsibility to provide a proof that there is no matching
-    ///   wildcard if that proof is necessary.
-    /// - In case of NXRRSET, we need to consider the following cases
-    ///   referring to Section 3.1.3 of RFC4035:
-    ///
-    /// -# (Normal) no data: there is a matching non-wildcard name with a
-    ///    different RR type.  This is the "No Data" case of the RFC.
-    /// -# (Normal) empty non terminal: there is no matching (exact or
-    ///    wildcard) name, but there is a subdomain with an RR of the query
-    ///    name.  This is one case of "Name Error" of the RFC.
-    /// -# Wildcard empty non terminal: similar to 2a, but the empty name
-    ///    is a wildcard, and matches the query name by wildcard expansion.
-    ///    This is a special case of "Name Error" of the RFC.
-    /// -# Wildcard no data: there is no exact match name, but there is a
-    ///    wildcard name that matches the query name with a different type
-    ///    of RR.  This is the "Wildcard No Data" case of the RFC.
-    ///
-    /// In case 1, \c find() returns NSEC of the matching name.
-    ///
-    /// In case 2, \c find() will return NSEC for the interval where the
-    /// empty nonterminal lives. The end of the interval is the subdomain
-    /// causing existence of the empty nonterminal (if there's
-    /// sub.x.example.com, and no record in x.example.com, then
-    /// x.example.com exists implicitly - is the empty nonterminal and
-    /// sub.x.example.com is the subdomain causing it).  Note that this NSEC
-    /// proves not only the existence of empty non terminal name but also
-    /// the non existence of possibly matching wildcard name, because
-    /// there can be no better wildcard match than the exact matching empty
-    /// name.
-    ///
-    /// In case 3, \c find() will return NSEC for the interval where the
-    /// wildcard empty nonterminal lives.   Cases 2 and 3 are especially
-    /// complicated and confusing.  See the examples below.
-    ///
-    /// In case 4, \c find() will return NSEC of the matching wildcard name.
-    ///
-    /// Examples: if zone "example.com" has the following record:
-    /// \code
-    /// a.example.com. NSEC a.b.example.com.
-    /// \endcode
-    /// a call to \c find() for "b.example.com." with the FIND_DNSSEC option
-    /// will result in NXRRSET, and this NSEC will be returned.
-    /// Likewise, if zone "example.org" has the following record,
-    /// \code
-    /// a.example.org. NSEC x.*.b.example.org.
-    /// \endcode
-    /// a call to \c find() for "y.b.example.org" with FIND_DNSSEC will
-    /// result in NXRRSET and this NSEC; \c isWildcard() on the returned
-    /// \c FindContext object will return true.
-    ///
-    /// \exception std::bad_alloc Memory allocation such as for constructing
-    ///  the resulting RRset fails
-    /// \throw OutOfZone The Name \c name is outside of the origin of the
-    /// zone of this ZoneFinder.
-    /// \exception DataSourceError Derived class specific exception, e.g.
-    /// when encountering a bad zone configuration or database connection
-    /// failure.  Although these are considered rare, exceptional events,
-    /// it can happen under relatively usual conditions (unlike memory
-    /// allocation failure).  So, in general, the application is expected
-    /// to catch this exception, either specifically or as a result of
-    /// catching a base exception class, and handle it gracefully.
-    ///
-    /// \param name The domain name to be searched for.
-    /// \param type The RR type to be searched for.
-    /// \param options The search options.
-    /// \return A \c FindContext object enclosing the search result
-    ///         (see above).
-    virtual boost::shared_ptr<Context> find(const isc::dns::Name& name,
-                                            const isc::dns::RRType& type,
-                                            const FindOptions options
-                                            = FIND_DEFAULT) = 0;
-
-    ///
-    /// \brief Finds all RRsets in the given name.
-    ///
-    /// This function works almost exactly in the same way as the find one. The
-    /// only difference is, when the lookup is successful (eg. the code is
-    /// SUCCESS), all the RRsets residing in the named node are
-    /// copied into the \c target parameter and the rrset member of the result
-    /// is NULL. All the other (unsuccessful) cases are handled the same,
-    /// including returning delegations, NSEC/NSEC3 availability and NSEC
-    /// proofs, wildcard information etc. The options parameter works the
-    /// same way and it should conform to the same exception restrictions.
-    ///
-    /// \param name \see find, parameter name
-    /// \param target the successfull result is returned through this
-    /// \param options \see find, parameter options
-    /// \return \see find and it's result
-    virtual boost::shared_ptr<Context> findAll(
-        const isc::dns::Name& name,
-        std::vector<isc::dns::ConstRRsetPtr> &target,
-        const FindOptions options = FIND_DEFAULT) = 0;
-
-    /// A helper structure to represent the search result of \c findNSEC3().
-    ///
-    /// The idea is similar to that of \c FindContext, but \c findNSEC3() has
-    /// special interface and semantics, we use a different structure to
-    /// represent the result.
-    struct FindNSEC3Result {
-        FindNSEC3Result(bool param_matched, uint8_t param_closest_labels,
-                        isc::dns::ConstRRsetPtr param_closest_proof,
-                        isc::dns::ConstRRsetPtr param_next_proof) :
-            matched(param_matched), closest_labels(param_closest_labels),
-            closest_proof(param_closest_proof),
-            next_proof(param_next_proof)
-        {}
-
-        /// true iff closest_proof is a matching NSEC3
-        const bool matched;
-
-        /// The number of labels of the identified closest encloser.
-        const uint8_t closest_labels;
-
-        /// Either the NSEC3 for the closest provable encloser of the given
-        /// name or NSEC3 that covers the name
-        const isc::dns::ConstRRsetPtr closest_proof;
-
-        /// When non NULL, NSEC3 for the next closer name.
-        const isc::dns::ConstRRsetPtr next_proof;
-    };
-
-    /// Search the zone for the NSEC3 RR(s) that prove existence or non
-    /// existence of a give name.
-    ///
-    /// It searches the NSEC3 namespace of the zone (how that namespace is
-    /// implemented can vary in specific data source implementation) for NSEC3
-    /// RRs that match or cover the NSEC3 hash value for the given name.
-    ///
-    /// If \c recursive is false, it will first look for the NSEC3 that has
-    /// a matching hash.  If it doesn't exist, it identifies the covering NSEC3
-    /// for the hash.  In either case the search stops at that point and the
-    /// found NSEC3 RR(set) will be returned in the closest_proof member of
-    /// \c FindNSEC3Result.  \c matched is true or false depending on
-    /// the found NSEC3 is a matched one or covering one.  \c next_proof
-    /// is always NULL.  closest_labels must be equal to the number of
-    /// labels of \c name (and therefore meaningless).
-    ///
-    /// If \c recursive is true, it will continue the search toward the zone
-    /// apex (origin name) until it finds a provable encloser, that is,
-    /// an ancestor of \c name that has a matching NSEC3.  This is the closest
-    /// provable encloser of \c name as defined in RFC5155.  In this case,
-    /// if the found encloser is not equal to \c name, the search should
-    /// have seen a covering NSEC3 for the immediate child of the found
-    /// encloser.  That child name is the next closer name as defined in
-    /// RFC5155.  In this case, this method returns the NSEC3 for the
-    /// closest encloser in \c closest_proof, and the NSEC3 for the next
-    /// closer name in \c next_proof of \c FindNSEC3Result.  This set of
-    /// NSEC3 RRs provide the closest encloser proof as defined in RFC5155.
-    /// closest_labels will be set to the number of labels of the identified
-    /// closest encloser.  This will be useful when the caller needs to
-    /// construct the closest encloser name from the original \c name.
-    /// If, on the other hand, the found closest name is equal to \c name,
-    /// this method simply returns it in \c closest_proof.  \c next_proof
-    /// is set to NULL.  In all cases \c matched is set to true.
-    /// closest_labels will be set to the number of labels of \c name.
-    ///
-    /// When looking for NSEC3, this method retrieves NSEC3 parameters from
-    /// the corresponding zone to calculate hash values.  Actual implementation
-    /// of how to do this will differ in different data sources.  If the
-    /// NSEC3 parameters are not available \c DataSourceError exception
-    /// will be thrown.
-    ///
-    /// \note This implicitly means this method assumes the zone does not
-    /// have more than one set of parameters.  This assumption should be
-    /// reasonable in actual deployment and will help simplify the interface
-    /// and implementation.  But if there's a real need for supporting
-    /// multiple sets of parameters in a single zone, we will have to
-    /// extend this method so that, e.g., the caller can specify the parameter
-    /// set.
-    ///
-    /// In general, this method expects the zone is properly signed with NSEC3
-    /// RRs.  Specifically, it assumes at least the apex node has a matching
-    /// NSEC3 RR (so the search in the recursive mode must always succeed);
-    /// it also assumes that it can retrieve NSEC parameters (iterations,
-    /// algorithm, and salt) from the zone as noted above.  If these
-    /// assumptions aren't met, \c DataSourceError exception will be thrown.
-    ///
-    /// \exception OutOfZone name is not a subdomain of the zone origin
-    /// \exception DataSourceError Low-level or internal datasource errors
-    /// happened, or the zone isn't properly signed with NSEC3
-    /// (NSEC3 parameters cannot be found, no NSEC3s are available, etc).
-    /// \exception std::bad_alloc The underlying implementation involves
-    /// memory allocation and it fails
-    ///
-    /// \param name The name for which NSEC3 RRs are to be found.  It must
-    /// be a subdomain of the zone.
-    /// \param recursive Whether or not search should continue until it finds
-    /// a provable encloser (see above).
-    ///
-    /// \return The search result and whether or not the closest_proof is
-    /// a matching NSEC3, in the form of \c FindNSEC3Result object.
-    virtual FindNSEC3Result
-    findNSEC3(const isc::dns::Name& name, bool recursive) = 0;
-    //@}
-};
-
-/// \brief Operator to combine FindOptions
-///
-/// We would need to manually static-cast the options if we put or
-/// between them, which is undesired with bit-flag options. Therefore
-/// we hide the cast here, which is the simplest solution and it still
-/// provides reasonable level of type safety.
-inline ZoneFinder::FindOptions operator |(ZoneFinder::FindOptions a,
-                                          ZoneFinder::FindOptions b)
-{
-    return (static_cast<ZoneFinder::FindOptions>(static_cast<unsigned>(a) |
-                                                 static_cast<unsigned>(b)));
-}
-
-/// \brief Operator to combine FindResultFlags
-///
-/// Similar to the same operator for \c FindOptions.  Refer to the description
-/// of that function.
-inline ZoneFinder::FindResultFlags operator |(
-    ZoneFinder::FindResultFlags a,
-    ZoneFinder::FindResultFlags b)
-{
-    return (static_cast<ZoneFinder::FindResultFlags>(
-                static_cast<unsigned>(a) | static_cast<unsigned>(b)));
-}
-
-/// \brief A pointer-like type pointing to a \c ZoneFinder object.
-typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
-
-/// \brief A pointer-like type pointing to an immutable \c ZoneFinder object.
-typedef boost::shared_ptr<const ZoneFinder> ConstZoneFinderPtr;
-
-/// \brief A pointer-like type pointing to a \c ZoneFinder::Context object.
-typedef boost::shared_ptr<ZoneFinder::Context> ZoneFinderContextPtr;
-
-/// \brief A pointer-like type pointing to an immutable
-/// \c ZoneFinder::Context object.
-typedef boost::shared_ptr<ZoneFinder::Context> ConstZoneFinderContextPtr;
-
 /// \brief A forward declaration
 class RRsetCollectionBase;
 
+class ZoneFinder;
+
 /// The base class to make updates to a single zone.
 ///
 /// On construction, each derived class object will start a "transaction"

+ 93 - 2
src/lib/datasrc/zone_finder.cc

@@ -12,13 +12,14 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <datasrc/zone_finder.h>
+#include <datasrc/data_source.h>
+
 #include <dns/rdata.h>
 #include <dns/rrset.h>
 #include <dns/rrtype.h>
 #include <dns/rdataclass.h>
 
-#include <datasrc/zone.h>
-
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
@@ -26,6 +27,96 @@ using namespace isc::dns::rdata;
 namespace isc {
 namespace datasrc {
 
+namespace {
+// Identify zone's SOA and return its MINTTL in the form of RRTTL.
+RRTTL
+getMinTTL(ZoneFinder& finder, ConstRRsetPtr rrset) {
+    ConstRRsetPtr soa_rrset;
+    if (rrset->getType() == RRType::SOA()) {
+        // Shortcut: if we are looking at SOA itself (which should be the
+        // case in the expected scenario), we can simply use its RDATA.
+        soa_rrset = rrset;
+    } else {
+        soa_rrset =
+            finder.findAtOrigin(RRType::SOA(), false,
+                                ZoneFinder::FIND_DEFAULT)->rrset;
+    }
+
+    // In a valid zone there is one and only one SOA RR at the origin.
+    // Otherwise either zone data or the data source implementation is broken.
+    if (!soa_rrset || soa_rrset->getRdataCount() != 1) {
+        isc_throw(DataSourceError, "Zone " << rrset->getName().toText(true)
+                  << "/" << rrset->getClass().toText() << " is broken: "
+                  << (!soa_rrset ? "no SOA" : "empty SOA"));
+    }
+
+    return (RRTTL(dynamic_cast<const generic::SOA&>(
+                      soa_rrset->getRdataIterator()->getCurrent()).
+                  getMinimum()));
+}
+
+// Make a fresh copy of given RRset, just replacing RRTTL with the given one.
+RRsetPtr
+copyRRset(const AbstractRRset& rrset, const RRTTL& ttl) {
+    RRsetPtr rrset_copy(new RRset(rrset.getName(), rrset.getClass(),
+                                  rrset.getType(), ttl));
+    for (RdataIteratorPtr rit = rrset.getRdataIterator();
+         !rit->isLast();
+         rit->next()) {
+        rrset_copy->addRdata(rit->getCurrent());
+    }
+
+    ConstRRsetPtr rrsig = rrset.getRRsig();
+    if (rrsig) {
+        RRsetPtr rrsig_copy(new RRset(rrset.getName(), rrset.getClass(),
+                                      RRType::RRSIG(), ttl));
+        for (RdataIteratorPtr rit = rrsig->getRdataIterator();
+             !rit->isLast();
+             rit->next()) {
+            rrsig_copy->addRdata(rit->getCurrent());
+        }
+        rrset_copy->addRRsig(rrsig_copy);
+    }
+
+    return (rrset_copy);
+}
+}
+
+ZoneFinderContextPtr
+ZoneFinder::findAtOrigin(const dns::RRType& type, bool use_minttl,
+                         FindOptions options)
+{
+    ZoneFinderContextPtr context = find(getOrigin(), type, options);
+
+    // If we are requested to use the min TTL and the RRset's RR TTL is larger
+    // than that, we need to make a copy of the RRset, replacing the TTL,
+    // and return a newly created context copying other parameters.
+    if (use_minttl && context->rrset) {
+        const AbstractRRset& rrset = *context->rrset;
+        const RRTTL min_ttl = getMinTTL(*this, context->rrset);
+        if (min_ttl < rrset.getTTL()) {
+            FindResultFlags flags_copy = RESULT_DEFAULT;
+            if (context->isWildcard()) {
+                flags_copy = flags_copy | RESULT_WILDCARD;
+            }
+            if (context->isNSECSigned()) {
+                flags_copy = flags_copy | RESULT_NSEC_SIGNED;
+            } else if (context->isNSEC3Signed()) {
+                flags_copy = flags_copy | RESULT_NSEC3_SIGNED;
+            }
+
+            return (ZoneFinderContextPtr(
+                        new GenericContext(*this, options,
+                                           ResultContext(context->code,
+                                                         copyRRset(rrset,
+                                                                   min_ttl),
+                                                         flags_copy))));
+        }
+    }
+
+    return (context);
+}
+
 isc::dns::ConstRRsetPtr
 ZoneFinder::stripRRsigs(isc::dns::ConstRRsetPtr rp,
                         const FindOptions options) {

+ 809 - 0
src/lib/datasrc/zone_finder.h

@@ -0,0 +1,809 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_ZONE_FINDER_H
+#define DATASRC_ZONE_FINDER_H 1
+
+#include <dns/name.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+
+#include <datasrc/exceptions.h>
+#include <datasrc/result.h>
+#include <datasrc/rrset_collection_base.h>
+
+#include <utility>
+#include <vector>
+
+namespace isc {
+namespace datasrc {
+
+/// \brief Out of zone exception
+///
+/// This is thrown when a method is called for a name or RRset which
+/// is not in or below the zone.
+class OutOfZone : public ZoneException {
+public:
+    OutOfZone(const char* file, size_t line, const char* what) :
+        ZoneException(file, line, what) {}
+};
+
+/// \brief The base class to search a zone for RRsets
+///
+/// The \c ZoneFinder class is an abstract base class for representing
+/// an object that performs DNS lookups in a specific zone accessible via
+/// a data source.  In general, different types of data sources (in-memory,
+/// database-based, etc) define their own derived classes of \c ZoneFinder,
+/// implementing ways to retrieve the required data through the common
+/// interfaces declared in the base class.  Each concrete \c ZoneFinder
+/// object is therefore (conceptually) associated with a specific zone
+/// of one specific data source instance.
+///
+/// The origin name and the RR class of the associated zone are available
+/// via the \c getOrigin() and \c getClass() methods, respectively.
+///
+/// The most important method of this class is \c find(), which performs
+/// the lookup for a given domain and type.  See the description of the
+/// method for details.
+///
+/// \note It's not clear whether we should request that a zone finder form a
+/// "transaction", that is, whether to ensure the finder is not susceptible
+/// to changes made by someone else than the creator of the finder.  If we
+/// don't request that, for example, two different lookup results for the
+/// same name and type can be different if other threads or programs make
+/// updates to the zone between the lookups.  We should revisit this point
+/// as we gain more experiences.
+class ZoneFinder {
+public:
+    /// Result codes of the \c find() method.
+    ///
+    /// Note: the codes are tentative.  We may need more, or we may find
+    /// some of them unnecessary as we implement more details.
+    ///
+    /// See the description of \c find() for further details of how
+    /// these results should be interpreted.
+    enum Result {
+        SUCCESS,                ///< An exact match is found.
+        DELEGATION,             ///< The search encounters a zone cut.
+        NXDOMAIN, ///< There is no domain name that matches the search name
+        NXRRSET,  ///< There is a matching name but no RRset of the search type
+        CNAME,    ///< The search encounters and returns a CNAME RR
+        DNAME    ///< The search encounters and returns a DNAME RR
+    };
+
+    /// Special attribute flags on the result of the \c find() method
+    ///
+    /// The flag values defined here are intended to signal to the caller
+    /// that it may need special handling on the result.  This is particularly
+    /// of concern when DNSSEC is requested.  For example, for negative
+    /// responses the caller would want to know whether the zone is signed
+    /// with NSEC or NSEC3 so that it can subsequently provide necessary
+    /// proof of the result.
+    ///
+    /// The caller is generally expected to get access to the information
+    /// via read-only getter methods of \c FindContext so that it won't rely
+    /// on specific details of the representation of the flags.  So these
+    /// definitions are basically only meaningful for data source
+    /// implementations.
+    enum FindResultFlags {
+        RESULT_DEFAULT = 0,       ///< The default flags
+        RESULT_WILDCARD = 1,      ///< find() resulted in a wildcard match
+        RESULT_NSEC_SIGNED = 2,   ///< The zone is signed with NSEC RRs
+        RESULT_NSEC3_SIGNED = 4   ///< The zone is signed with NSEC3 RRs
+    };
+
+    /// Find options.
+    ///
+    /// The option values are used as a parameter for \c find().
+    /// These are values of a bitmask type.  Bitwise operations can be
+    /// performed on these values to express compound options.
+    enum FindOptions {
+        FIND_DEFAULT = 0,       ///< The default options
+        FIND_GLUE_OK = 1,       ///< Allow search under a zone cut
+        FIND_DNSSEC = 2,        ///< Require DNSSEC data in the answer
+                                ///< (RRSIG, NSEC, etc.). The implementation
+                                ///< is allowed to include it even if it is
+                                ///< not set.
+        NO_WILDCARD = 4         ///< Do not try wildcard matching.
+    };
+
+protected:
+    /// \brief A convenient tuple representing a set of find() results.
+    ///
+    /// This helper structure is specifically expected to be used as an input
+    /// for the construct of the \c Context class object used by derived
+    /// ZoneFinder implementations.  This is therefore defined as protected.
+    struct ResultContext {
+        ResultContext(Result code_param,
+                      isc::dns::ConstRRsetPtr rrset_param,
+                      FindResultFlags flags_param = RESULT_DEFAULT) :
+            code(code_param), rrset(rrset_param), flags(flags_param)
+        {}
+        const Result code;
+        const isc::dns::ConstRRsetPtr rrset;
+        const FindResultFlags flags;
+    };
+
+public:
+    /// \brief A helper function to strip RRSIGs when FIND_DNSSEC is not
+    /// requested.
+    static isc::dns::ConstRRsetPtr
+    stripRRsigs(isc::dns::ConstRRsetPtr rp, const FindOptions options);
+
+    /// \brief Context of the result of a find() call.
+    ///
+    /// This class encapsulates results and (possibly) associated context
+    /// of a call to the \c find() method.   The public member variables of
+    /// this class represent the result of the call.  They are a
+    /// straightforward tuple of the result code and a pointer (and
+    /// optionally special flags) to the found RRset.
+    ///
+    /// These member variables will be initialized on construction and never
+    /// change, so for convenience we allow the applications to refer to some
+    /// of the members directly.  For some others we provide read-only accessor
+    /// methods to hide specific representation.
+    ///
+    /// Another role of this class is to provide the interface to some common
+    /// processing logic that may be necessary using the result of \c find().
+    /// Specifically, it's expected to be used in the context of DNS query
+    /// handling, where the caller would need to look into the data source
+    /// again based on the \c find() result.  For example, it would need to
+    /// get A and/or AAAA records for some of the answer or authority RRs.
+    ///
+    /// This class defines (a set of) method(s) that can be commonly used
+    /// for such purposes for any type of data source (as long as it conforms
+    /// to the public \c find() interface).  In some cases, a specific data
+    /// source implementation may want to (and can) optimize the processing
+    /// exploiting its internal data structure and the knowledge of the context
+    /// of the precedent \c find() call.  Such a data source implementation
+    /// can define a derived class of the base Context and override the
+    /// specific virtual method.
+    ///
+    /// This base class defines these common protected methods along with
+    /// some helper pure virtual methods that would be necessary for the
+    /// common methods.  If a derived class wants to use the common version
+    /// of the protected method, it needs to provide expected result through
+    /// their implementation of the pure virtual methods.
+    ///
+    /// This class object is generally expected to be associated with the
+    /// ZoneFinder that originally performed the \c find() call, and expects
+    /// the finder is valid throughout the lifetime of this object.  It's
+    /// caller's responsibility to ensure that assumption.
+    class Context {
+    public:
+        /// \brief The constructor.
+        ///
+        /// \param options The find options specified for the find() call.
+        /// \param result The result of the find() call.
+        Context(FindOptions options, const ResultContext& result) :
+            code(result.code), rrset(result.rrset),
+            flags_(result.flags), options_(options)
+        {}
+
+        /// \brief The destructor.
+        virtual ~Context() {}
+
+        const Result code;
+        const isc::dns::ConstRRsetPtr rrset;
+
+        /// Return true iff find() results in a wildcard match.
+        bool isWildcard() const { return ((flags_ & RESULT_WILDCARD) != 0); }
+
+        /// Return true when the underlying zone is signed with NSEC.
+        ///
+        /// The \c find() implementation allows this to return false if
+        /// \c FIND_DNSSEC isn't specified regardless of whether the zone
+        /// is signed or which of NSEC/NSEC3 is used.
+        ///
+        /// When this is returned, the implementation of find() must ensure
+        /// that \c rrset be a valid NSEC RRset as described in \c find()
+        /// documentation.
+        bool isNSECSigned() const {
+            return ((flags_ & RESULT_NSEC_SIGNED) != 0);
+        }
+
+        /// Return true when the underlying zone is signed with NSEC3.
+        ///
+        /// The \c find() implementation allows this to return false if
+        /// \c FIND_DNSSEC isn't specified regardless of whether the zone
+        /// is signed or which of NSEC/NSEC3 is used.
+        bool isNSEC3Signed() const {
+            return ((flags_ & RESULT_NSEC3_SIGNED) != 0);
+        }
+
+        /// \brief Find and return additional RRsets corresponding to the
+        ///        result of \c find().
+        ///
+        /// If this context is based on a normal find() call that resulted
+        /// in SUCCESS or DELEGATION, it examines the returned RRset (in many
+        /// cases NS, sometimes MX or others), searches the data source for
+        /// specified type of additional RRs for each RDATA of the RRset
+        /// (e.g., A or AAAA for the name server addresses), and stores the
+        /// result in the given vector.  The vector may not be empty; this
+        /// method appends any found RRsets to it, without touching existing
+        /// elements.
+        ///
+        /// If this context is based on a findAll() call that resulted in
+        /// SUCCESS, it performs the same process for each RRset returned in
+        /// the \c findAll() call.
+        ///
+        /// The caller specifies desired RR types of the additional RRsets
+        /// in \c requested_types.  Normally it consists of A and/or AAAA
+        /// types, but other types can be specified.
+        ///
+        /// This method is meaningful only when the precedent find()/findAll()
+        /// call resulted in SUCCESS or DELEGATION.  Otherwise this method
+        /// does nothing.
+        ///
+        /// \note The additional RRsets returned via method are limited to
+        /// ones contained in the zone which the corresponding find/findAll
+        /// call searched (possibly including glues under a zone cut where
+        /// they are applicable).  If the caller needs to get out-of-zone
+        /// additional RRsets, it needs to explicitly finds them by
+        /// identifying the corresponding zone and calls \c find() for it.
+        ///
+        /// \param requested_types A vector of RR types for desired additional
+        ///  RRsets.
+        /// \param result A vector to which any found additional RRsets are
+        /// to be inserted.
+        void getAdditional(
+            const std::vector<isc::dns::RRType>& requested_types,
+            std::vector<isc::dns::ConstRRsetPtr>& result)
+        {
+            // Perform common checks, and delegate the process to the default
+            // or specialized implementation.
+            if (code != SUCCESS && code != DELEGATION) {
+                return;
+            }
+
+            getAdditionalImpl(requested_types, result);
+        }
+
+    protected:
+        /// \brief Return the \c ZoneFinder that created this \c Context.
+        ///
+        /// A derived class implementation can return NULL if it defines
+        /// other protected methods that require a non NULL result from
+        /// this method.  Otherwise it must return a valid, non NULL pointer
+        /// to the \c ZoneFinder object.
+        ///
+        /// When returning non NULL, the ownership of the pointed object
+        /// was not transferred to the caller; it cannot be assumed to be
+        /// valid after the originating \c Context object is destroyed.
+        /// Also, the caller must not try to delete the returned object.
+        virtual ZoneFinder* getFinder() = 0;
+
+        /// \brief Return a vector of RRsets corresponding to findAll() result.
+        ///
+        /// This method returns a set of RRsets that correspond to the
+        /// returned RRsets to a prior \c findAll() call.
+        ///
+        /// A derived class implementation can return NULL if it defines
+        /// other protected methods that require a non NULL result from
+        /// this method.  Otherwise it must return a valid, non NULL pointer
+        /// to a vector that correspond to the expected set of RRsets.
+        ///
+        /// When returning non NULL, the ownership of the pointed object
+        /// was not transferred to the caller; it cannot be assumed to be
+        /// valid after the originating \c Context object is destroyed.
+        /// Also, the caller must not try to delete the returned object.
+        virtual const std::vector<isc::dns::ConstRRsetPtr>*
+        getAllRRsets() const = 0;
+
+        /// \brief Actual implementation of getAdditional().
+        ///
+        /// This base class defines a default implementation that can be
+        /// used for any type of data sources.  A data source implementation
+        /// can override it.
+        ///
+        /// The default version of this implementation requires both
+        /// \c getFinder() and \c getAllRRsets() return valid results.
+        virtual void getAdditionalImpl(
+            const std::vector<isc::dns::RRType>& requested_types,
+            std::vector<isc::dns::ConstRRsetPtr>& result);
+
+    private:
+        const FindResultFlags flags_;
+    protected:
+        const FindOptions options_;
+    };
+
+    /// \brief Generic ZoneFinder context that works for all implementations.
+    ///
+    /// This is a concrete derived class of \c ZoneFinder::Context that
+    /// only use the generic (default) versions of the protected methods
+    /// and therefore work for any data source implementation.
+    ///
+    /// A data source implementation can use this class to create a
+    /// \c Context object as a return value of \c find() or \c findAll()
+    /// method if it doesn't have to optimize specific protected methods.
+    class GenericContext : public Context {
+    public:
+        /// \brief The constructor for the normal find call.
+        ///
+        /// This constructor is expected to be called from the \c find()
+        /// method when it constructs the return value.
+        ///
+        /// \param finder The ZoneFinder on which find() is called.
+        /// \param options See the \c Context class.
+        /// \param result See the \c Context class.
+        GenericContext(ZoneFinder& finder, FindOptions options,
+                       const ResultContext& result) :
+            Context(options, result), finder_(finder)
+        {}
+
+        /// \brief The constructor for the normal findAll call.
+        ///
+        /// This constructor is expected to be called from the \c findAll()
+        /// method when it constructs the return value.
+        ///
+        /// It copies the vector that is to be returned to the caller of
+        /// \c findAll() for possible subsequent use.  Note that it cannot
+        /// simply hold a reference to the vector because the caller may
+        /// alter it after the \c findAll() call.
+        ///
+        /// \param finder The ZoneFinder on which findAll() is called.
+        /// \param options See the \c Context class.
+        /// \param result See the \c Context class.
+        /// \param all_set Reference to the vector given by the caller of
+        ///       \c findAll(), storing the RRsets to be returned.
+        GenericContext(ZoneFinder& finder, FindOptions options,
+                       const ResultContext& result,
+                       const std::vector<isc::dns::ConstRRsetPtr>& all_set) :
+            Context(options, result), finder_(finder), all_set_(all_set)
+        {}
+
+    protected:
+        virtual ZoneFinder* getFinder() { return (&finder_); }
+        virtual const std::vector<isc::dns::ConstRRsetPtr>*
+        getAllRRsets() const {
+            return (&all_set_);
+        }
+
+    private:
+        ZoneFinder& finder_;
+        std::vector<isc::dns::ConstRRsetPtr> all_set_;
+    };
+
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    //@{
+protected:
+    /// The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class should
+    /// never be instantiated (except as part of a derived class).
+    ZoneFinder() {}
+public:
+    /// The destructor.
+    virtual ~ZoneFinder() {}
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    /// These methods should never throw an exception.
+    //@{
+    /// Return the origin name of the zone.
+    virtual isc::dns::Name getOrigin() const = 0;
+
+    /// Return the RR class of the zone.
+    virtual isc::dns::RRClass getClass() const = 0;
+    //@}
+
+    ///
+    /// \name Search Methods
+    ///
+    //@{
+    /// \brief Search the zone for a given pair of domain name and RR type.
+    ///
+    /// Each derived version of this method searches the underlying backend
+    /// for the data that best matches the given name and type.
+    /// This method is expected to be "intelligent", and identifies the
+    /// best possible answer for the search key.  Specifically,
+    ///
+    /// - If the search name belongs under a zone cut, it returns the code
+    ///   of \c DELEGATION and the NS RRset at the zone cut.
+    /// - If there is no matching name, it returns the code of \c NXDOMAIN.
+    /// - If there is a matching name but no RRset of the search type, it
+    ///   returns the code of \c NXRRSET.  This case includes the search name
+    ///   matches an empty node of the zone.
+    /// - If there is a CNAME RR of the searched name but there is no
+    ///   RR of the searched type of the name (so this type is different from
+    ///   CNAME), it returns the code of \c CNAME and that CNAME RR.
+    ///   Note that if the searched RR type is CNAME, it is considered
+    ///   a successful match, and the code of \c SUCCESS will be returned.
+    /// - If the search name matches a delegation point of DNAME, it returns
+    ///   the code of \c DNAME and that DNAME RR.
+    ///
+    /// No RRset will be returned in the \c NXDOMAIN and \c NXRRSET cases
+    /// (\c rrset member of \c FindContext will be NULL), unless DNSSEC data
+    /// are required.  See below for the cases with DNSSEC.
+    ///
+    /// The returned \c FindContext object can also provide supplemental
+    /// information about the search result via its methods returning a
+    /// boolean value.  Such information may be useful for the caller if
+    /// the caller wants to collect additional DNSSEC proofs based on the
+    /// search result.
+    ///
+    /// The \c options parameter specifies customized behavior of the search.
+    /// Their semantics is as follows (they are or bit-field):
+    ///
+    /// - \c FIND_GLUE_OK Allow search under a zone cut.  By default the search
+    ///   will stop once it encounters a zone cut.  If this option is specified
+    ///   it remembers information about the highest zone cut and continues
+    ///   the search until it finds an exact match for the given name or it
+    ///   detects there is no exact match.  If an exact match is found,
+    ///   RRsets for that name are searched just like the normal case;
+    ///   otherwise, if the search has encountered a zone cut, \c DELEGATION
+    ///   with the information of the highest zone cut will be returned.
+    ///   Note: the term "glue" in the DNS protocol standard may sometimes
+    ///   cause confusion: some people use this term strictly for an address
+    ///   record (type AAAA or A) for the name used in the RDATA of an NS RR;
+    ///   some others seem to give it broader flexibility.  Nevertheless,
+    ///   in this API the "GLUE OK" simply means the search by find() can
+    ///   continue beyond a zone cut; the derived class implementation does
+    ///   not have to, and should not, check whether the type is an address
+    ///   record or whether the query name is pointed by some NS RR.
+    ///   It's up to the caller with which definition of "glue" the search
+    ///   result with this option should be used.
+    /// - \c FIND_DNSSEC Request that DNSSEC data (like NSEC, RRSIGs) are
+    ///   returned with the answer. It is allowed for the data source to
+    ///   include them even when not requested.
+    /// - \c NO_WILDCARD Do not try wildcard matching.  This option is of no
+    ///   use for normal lookups; it's intended to be used to get a DNSSEC
+    ///   proof of the non existence of any matching wildcard or non existence
+    ///   of an exact match when a wildcard match is found.
+    ///
+    /// In general, \c name is expected to be included in the zone, that is,
+    /// it should be equal to or a subdomain of the zone origin.  Otherwise
+    /// this method will return \c NXDOMAIN with an empty RRset.  But such a
+    /// case should rather be considered a caller's bug.
+    ///
+    /// \note For this reason it's probably better to throw an exception
+    /// than returning \c NXDOMAIN.  This point should be revisited in a near
+    /// future version.  In any case applications shouldn't call this method
+    /// for an out-of-zone name.
+    ///
+    /// <b>DNSSEC considerations:</b>
+    /// The result when DNSSEC data are required can be very complicated,
+    /// especially if it involves negative result or wildcard match.
+    /// Specifically, if an application calls this method for DNS query
+    /// processing with DNSSEC data, and if the search result code is
+    /// either \c NXDOMAIN or \c NXRRRSET, and/or \c isWildcard() returns
+    /// true, then the application will need to find additional NSEC or
+    /// NSEC3 records for supplemental proofs.  This method helps the
+    /// application for such post search processing.
+    ///
+    /// First, it tells the application whether the zone is signed with
+    /// NSEC or NSEC3 via the \c isNSEC(3)Signed() method.  Any sanely signed
+    /// zone should be signed with either (and only one) of these two types
+    /// of RRs; however, the application should expect that the zone could
+    /// be broken and these methods could both return false.  But this method
+    /// should ensure that not both of these methods return true.
+    ///
+    /// In case it's signed with NSEC3, there is no further information
+    /// returned from this method.
+    ///
+    /// In case it's signed with NSEC, this method will possibly return
+    /// a related NSEC RRset in the \c rrset member of \c FindContext.
+    /// What kind of NSEC is returned depends on the result code
+    /// (\c NXDOMAIN or \c NXRRSET) and on whether it's a wildcard match:
+    ///
+    /// - In case of NXDOMAIN, the returned NSEC covers the queried domain
+    ///   that proves that the query name does not exist in the zone.  Note
+    ///   that this does not necessarily prove it doesn't even match a
+    ///   wildcard (even if the result of NXDOMAIN can only happen when
+    ///   there's no matching wildcard either).  It is caller's
+    ///   responsibility to provide a proof that there is no matching
+    ///   wildcard if that proof is necessary.
+    /// - In case of NXRRSET, we need to consider the following cases
+    ///   referring to Section 3.1.3 of RFC4035:
+    ///
+    /// -# (Normal) no data: there is a matching non-wildcard name with a
+    ///    different RR type.  This is the "No Data" case of the RFC.
+    /// -# (Normal) empty non terminal: there is no matching (exact or
+    ///    wildcard) name, but there is a subdomain with an RR of the query
+    ///    name.  This is one case of "Name Error" of the RFC.
+    /// -# Wildcard empty non terminal: similar to 2a, but the empty name
+    ///    is a wildcard, and matches the query name by wildcard expansion.
+    ///    This is a special case of "Name Error" of the RFC.
+    /// -# Wildcard no data: there is no exact match name, but there is a
+    ///    wildcard name that matches the query name with a different type
+    ///    of RR.  This is the "Wildcard No Data" case of the RFC.
+    ///
+    /// In case 1, \c find() returns NSEC of the matching name.
+    ///
+    /// In case 2, \c find() will return NSEC for the interval where the
+    /// empty nonterminal lives. The end of the interval is the subdomain
+    /// causing existence of the empty nonterminal (if there's
+    /// sub.x.example.com, and no record in x.example.com, then
+    /// x.example.com exists implicitly - is the empty nonterminal and
+    /// sub.x.example.com is the subdomain causing it).  Note that this NSEC
+    /// proves not only the existence of empty non terminal name but also
+    /// the non existence of possibly matching wildcard name, because
+    /// there can be no better wildcard match than the exact matching empty
+    /// name.
+    ///
+    /// In case 3, \c find() will return NSEC for the interval where the
+    /// wildcard empty nonterminal lives.   Cases 2 and 3 are especially
+    /// complicated and confusing.  See the examples below.
+    ///
+    /// In case 4, \c find() will return NSEC of the matching wildcard name.
+    ///
+    /// Examples: if zone "example.com" has the following record:
+    /// \code
+    /// a.example.com. NSEC a.b.example.com.
+    /// \endcode
+    /// a call to \c find() for "b.example.com." with the FIND_DNSSEC option
+    /// will result in NXRRSET, and this NSEC will be returned.
+    /// Likewise, if zone "example.org" has the following record,
+    /// \code
+    /// a.example.org. NSEC x.*.b.example.org.
+    /// \endcode
+    /// a call to \c find() for "y.b.example.org" with FIND_DNSSEC will
+    /// result in NXRRSET and this NSEC; \c isWildcard() on the returned
+    /// \c FindContext object will return true.
+    ///
+    /// \exception std::bad_alloc Memory allocation such as for constructing
+    ///  the resulting RRset fails
+    /// \throw OutOfZone The Name \c name is outside of the origin of the
+    /// zone of this ZoneFinder.
+    /// \exception DataSourceError Derived class specific exception, e.g.
+    /// when encountering a bad zone configuration or database connection
+    /// failure.  Although these are considered rare, exceptional events,
+    /// it can happen under relatively usual conditions (unlike memory
+    /// allocation failure).  So, in general, the application is expected
+    /// to catch this exception, either specifically or as a result of
+    /// catching a base exception class, and handle it gracefully.
+    ///
+    /// \param name The domain name to be searched for.
+    /// \param type The RR type to be searched for.
+    /// \param options The search options.
+    /// \return A \c FindContext object enclosing the search result
+    ///         (see above).
+    virtual boost::shared_ptr<Context> find(const isc::dns::Name& name,
+                                            const isc::dns::RRType& type,
+                                            const FindOptions options
+                                            = FIND_DEFAULT) = 0;
+
+    /// \brief Search for an RRset of given RR type at the zone origin.
+    ///
+    /// In terms of API this method is equivalent to a call to \c find() where
+    /// the \c name parameter is the zone origin (return value of
+    /// \c getOrigin()) and is redundant.  This method is provided as an
+    /// optimization point for some kind of finder implementations that can
+    /// exploit the fact that the query name is the zone origin and for
+    /// applications that want to possibly benefit from such implementations.
+    ///
+    /// If \c use_minttl is set to \c true and the returned context would
+    /// contain a non NULL RRset, its RR TTL is (possibly) adjusted so that
+    /// it's set to the minimum of its own TTL and the minimum TTL field value
+    /// of the zone's SOA record.  If the RRset contains an RRSIG, its TTL
+    /// is also adjusted in the same way.
+    ///
+    /// The origin of a zone is special in some points: for any valid zone
+    /// there should always be an SOA and at least one NS RR there, which
+    /// also means the origin name is never empty.  Also, the SOA record can
+    /// be used in a DNS response for negative answers, in which case the
+    /// RR TTL must be set to minimum of its own RRTTL and the value of the
+    /// minimum TTL field.  Although these operations can be performed
+    /// through other public interfaces, they can be sometimes suboptimal
+    /// in performance or could be more efficient in a specialized
+    /// implementation.  For example, a specific implementation of
+    /// \c getOrigin() could involve a dynamic creation of a \c Name object,
+    /// which is less efficient; on the other hand, the underlying finder
+    /// implementation may have an efficient way to access RRs of the origin
+    /// in implementation specific way; and, while reconstructing an RRset
+    /// with replacing the TTL is relatively expensive, this can be done
+    /// much faster if the need for it is known beforehand.
+    ///
+    /// If the underlying finder implementation wants to optimize these cases,
+    /// it can do so by specializing the method.  It has the default
+    /// implementation for any other implementations, which should work for
+    /// any finder implementation as long as it conforms to other public
+    /// interfaces.
+    ///
+    /// So, an implementation of a finder does not have to care about this
+    /// method unless it sees the need for optimizing the behavior.
+    /// Also, applications normally do not have to use this interface;
+    /// using the generic \c find() method (with some post call processing)
+    /// can do everything this method can provide.  The default implementation
+    /// may even be slower than such straightforward usage due to the
+    /// internal overhead.  This method should be used if and only if the
+    /// application needs to achieve the possible best performance with an
+    /// optimized finder implementation.
+    ///
+    /// \param type The RR type to be searched for.
+    /// \param use_minttl Whether to adjust the TTL (see the description).
+    /// \param options The search options.  Same for \c find().
+    ///
+    /// \return A \c FindContext object enclosing the search result.
+    ///         See \c find().
+    virtual boost::shared_ptr<Context> findAtOrigin(
+        const isc::dns::RRType& type, bool use_minttl,
+        FindOptions options);
+
+public:
+    ///
+    /// \brief Finds all RRsets in the given name.
+    ///
+    /// This function works almost exactly in the same way as the find one. The
+    /// only difference is, when the lookup is successful (eg. the code is
+    /// SUCCESS), all the RRsets residing in the named node are
+    /// copied into the \c target parameter and the rrset member of the result
+    /// is NULL. All the other (unsuccessful) cases are handled the same,
+    /// including returning delegations, NSEC/NSEC3 availability and NSEC
+    /// proofs, wildcard information etc. The options parameter works the
+    /// same way and it should conform to the same exception restrictions.
+    ///
+    /// \param name \see find, parameter name
+    /// \param target the successfull result is returned through this
+    /// \param options \see find, parameter options
+    /// \return \see find and it's result
+    virtual boost::shared_ptr<Context> findAll(
+        const isc::dns::Name& name,
+        std::vector<isc::dns::ConstRRsetPtr> &target,
+        const FindOptions options = FIND_DEFAULT) = 0;
+
+    /// A helper structure to represent the search result of \c findNSEC3().
+    ///
+    /// The idea is similar to that of \c FindContext, but \c findNSEC3() has
+    /// special interface and semantics, we use a different structure to
+    /// represent the result.
+    struct FindNSEC3Result {
+        FindNSEC3Result(bool param_matched, uint8_t param_closest_labels,
+                        isc::dns::ConstRRsetPtr param_closest_proof,
+                        isc::dns::ConstRRsetPtr param_next_proof) :
+            matched(param_matched), closest_labels(param_closest_labels),
+            closest_proof(param_closest_proof),
+            next_proof(param_next_proof)
+        {}
+
+        /// true iff closest_proof is a matching NSEC3
+        const bool matched;
+
+        /// The number of labels of the identified closest encloser.
+        const uint8_t closest_labels;
+
+        /// Either the NSEC3 for the closest provable encloser of the given
+        /// name or NSEC3 that covers the name
+        const isc::dns::ConstRRsetPtr closest_proof;
+
+        /// When non NULL, NSEC3 for the next closer name.
+        const isc::dns::ConstRRsetPtr next_proof;
+    };
+
+    /// Search the zone for the NSEC3 RR(s) that prove existence or non
+    /// existence of a give name.
+    ///
+    /// It searches the NSEC3 namespace of the zone (how that namespace is
+    /// implemented can vary in specific data source implementation) for NSEC3
+    /// RRs that match or cover the NSEC3 hash value for the given name.
+    ///
+    /// If \c recursive is false, it will first look for the NSEC3 that has
+    /// a matching hash.  If it doesn't exist, it identifies the covering NSEC3
+    /// for the hash.  In either case the search stops at that point and the
+    /// found NSEC3 RR(set) will be returned in the closest_proof member of
+    /// \c FindNSEC3Result.  \c matched is true or false depending on
+    /// the found NSEC3 is a matched one or covering one.  \c next_proof
+    /// is always NULL.  closest_labels must be equal to the number of
+    /// labels of \c name (and therefore meaningless).
+    ///
+    /// If \c recursive is true, it will continue the search toward the zone
+    /// apex (origin name) until it finds a provable encloser, that is,
+    /// an ancestor of \c name that has a matching NSEC3.  This is the closest
+    /// provable encloser of \c name as defined in RFC5155.  In this case,
+    /// if the found encloser is not equal to \c name, the search should
+    /// have seen a covering NSEC3 for the immediate child of the found
+    /// encloser.  That child name is the next closer name as defined in
+    /// RFC5155.  In this case, this method returns the NSEC3 for the
+    /// closest encloser in \c closest_proof, and the NSEC3 for the next
+    /// closer name in \c next_proof of \c FindNSEC3Result.  This set of
+    /// NSEC3 RRs provide the closest encloser proof as defined in RFC5155.
+    /// closest_labels will be set to the number of labels of the identified
+    /// closest encloser.  This will be useful when the caller needs to
+    /// construct the closest encloser name from the original \c name.
+    /// If, on the other hand, the found closest name is equal to \c name,
+    /// this method simply returns it in \c closest_proof.  \c next_proof
+    /// is set to NULL.  In all cases \c matched is set to true.
+    /// closest_labels will be set to the number of labels of \c name.
+    ///
+    /// When looking for NSEC3, this method retrieves NSEC3 parameters from
+    /// the corresponding zone to calculate hash values.  Actual implementation
+    /// of how to do this will differ in different data sources.  If the
+    /// NSEC3 parameters are not available \c DataSourceError exception
+    /// will be thrown.
+    ///
+    /// \note This implicitly means this method assumes the zone does not
+    /// have more than one set of parameters.  This assumption should be
+    /// reasonable in actual deployment and will help simplify the interface
+    /// and implementation.  But if there's a real need for supporting
+    /// multiple sets of parameters in a single zone, we will have to
+    /// extend this method so that, e.g., the caller can specify the parameter
+    /// set.
+    ///
+    /// In general, this method expects the zone is properly signed with NSEC3
+    /// RRs.  Specifically, it assumes at least the apex node has a matching
+    /// NSEC3 RR (so the search in the recursive mode must always succeed);
+    /// it also assumes that it can retrieve NSEC parameters (iterations,
+    /// algorithm, and salt) from the zone as noted above.  If these
+    /// assumptions aren't met, \c DataSourceError exception will be thrown.
+    ///
+    /// \exception OutOfZone name is not a subdomain of the zone origin
+    /// \exception DataSourceError Low-level or internal datasource errors
+    /// happened, or the zone isn't properly signed with NSEC3
+    /// (NSEC3 parameters cannot be found, no NSEC3s are available, etc).
+    /// \exception std::bad_alloc The underlying implementation involves
+    /// memory allocation and it fails
+    ///
+    /// \param name The name for which NSEC3 RRs are to be found.  It must
+    /// be a subdomain of the zone.
+    /// \param recursive Whether or not search should continue until it finds
+    /// a provable encloser (see above).
+    ///
+    /// \return The search result and whether or not the closest_proof is
+    /// a matching NSEC3, in the form of \c FindNSEC3Result object.
+    virtual FindNSEC3Result
+    findNSEC3(const isc::dns::Name& name, bool recursive) = 0;
+    //@}
+};
+
+/// \brief Operator to combine FindOptions
+///
+/// We would need to manually static-cast the options if we put or
+/// between them, which is undesired with bit-flag options. Therefore
+/// we hide the cast here, which is the simplest solution and it still
+/// provides reasonable level of type safety.
+inline ZoneFinder::FindOptions operator |(ZoneFinder::FindOptions a,
+                                          ZoneFinder::FindOptions b)
+{
+    return (static_cast<ZoneFinder::FindOptions>(static_cast<unsigned>(a) |
+                                                 static_cast<unsigned>(b)));
+}
+
+/// \brief Operator to combine FindResultFlags
+///
+/// Similar to the same operator for \c FindOptions.  Refer to the description
+/// of that function.
+inline ZoneFinder::FindResultFlags operator |(
+    ZoneFinder::FindResultFlags a,
+    ZoneFinder::FindResultFlags b)
+{
+    return (static_cast<ZoneFinder::FindResultFlags>(
+                static_cast<unsigned>(a) | static_cast<unsigned>(b)));
+}
+
+/// \brief A pointer-like type pointing to a \c ZoneFinder object.
+typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
+
+/// \brief A pointer-like type pointing to an immutable \c ZoneFinder object.
+typedef boost::shared_ptr<const ZoneFinder> ConstZoneFinderPtr;
+
+/// \brief A pointer-like type pointing to a \c ZoneFinder::Context object.
+typedef boost::shared_ptr<ZoneFinder::Context> ZoneFinderContextPtr;
+
+/// \brief A pointer-like type pointing to an immutable
+/// \c ZoneFinder::Context object.
+typedef boost::shared_ptr<ZoneFinder::Context> ConstZoneFinderContextPtr;
+
+} // end of datasrc
+} // end of isc
+
+#endif  // DATASRC_ZONE_FINDER_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 1 - 1
src/lib/datasrc/zone_finder_context.cc

@@ -19,7 +19,7 @@
 #include <dns/rrtype.h>
 #include <dns/rdataclass.h>
 
-#include <datasrc/zone.h>
+#include <datasrc/zone_finder.h>
 
 #include <boost/foreach.hpp>
 

src/lib/datasrc/iterator.h → src/lib/datasrc/zone_iterator.h


+ 1 - 1
src/lib/datasrc/zone_loader.cc

@@ -17,7 +17,7 @@
 
 #include <datasrc/client.h>
 #include <datasrc/data_source.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <datasrc/zone.h>
 #include <datasrc/logger.h>
 #include <datasrc/rrset_collection_base.h>

+ 1 - 1
src/lib/datasrc/zonetable.h

@@ -19,7 +19,7 @@
 
 #include <dns/rrset.h>
 
-#include <datasrc/zone.h>
+#include <datasrc/zone_finder.h>
 
 #include <boost/shared_ptr.hpp>
 

+ 1 - 1
src/lib/python/isc/datasrc/client_python.cc

@@ -27,7 +27,7 @@
 #include <datasrc/database.h>
 #include <datasrc/data_source.h>
 #include <datasrc/sqlite3_accessor.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <datasrc/client_list.h>
 
 #include <dns/python/name_python.h>

+ 1 - 1
src/lib/python/isc/datasrc/finder_python.cc

@@ -26,7 +26,7 @@
 #include <datasrc/database.h>
 #include <datasrc/data_source.h>
 #include <datasrc/sqlite3_accessor.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 #include <datasrc/zone.h>
 
 #include <dns/python/name_python.h>

+ 1 - 1
src/lib/python/isc/datasrc/iterator_python.cc

@@ -25,7 +25,7 @@
 #include <datasrc/client.h>
 #include <datasrc/database.h>
 #include <datasrc/sqlite3_accessor.h>
-#include <datasrc/iterator.h>
+#include <datasrc/zone_iterator.h>
 
 #include <dns/python/name_python.h>
 #include <dns/python/rrset_python.h>