BIND 10 master, updated. 2e940ea65d5b9f371c26352afd9e66719c38a6b9 [master] Merge branch 'trac1607' with fixing conflicts: src/lib/datasrc/tests/Makefile.am auth_srv_unittest.cc needed an adjustment, but I believe it's trivial, so I'm commiting and pushing it.

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Mar 7 22:07:40 UTC 2012


The branch, master has been updated
       via  2e940ea65d5b9f371c26352afd9e66719c38a6b9 (commit)
       via  516e15b88a729bc28ec04ee2713aa6b352f38875 (commit)
       via  510292e639de813a6f8b02ee3e36726fae1da5d8 (commit)
       via  d08e8c4fca2bdcbe3fd573c1872c8e9347a71fb0 (commit)
       via  64611b969652b074976c72867bc78ca43f8e51be (commit)
       via  6f31530ff7e74757980eb2d01b82852636991672 (commit)
       via  29fab3811a8bbae6bc28198f7aec4378b338a03c (commit)
       via  1633572f050616e4fc41502fa2bcbfd70ea594ef (commit)
       via  b2e7f7e47b9ac2dc93ca57c63635255a9a50fbe7 (commit)
       via  de4b10ee8d53f5c9537ba98ad401f84d008efd69 (commit)
       via  9a5e82f81a296fb59dfe75c7f47fe91471fcb13c (commit)
       via  1d3d7c590668be4503e70c2d3f2ee1da955874ac (commit)
       via  3e88cb09890b3d05c82c56be350b1b76325dff15 (commit)
       via  ac299353640a32e77ad0e3f630d1a6bdbfdbbb06 (commit)
       via  118f1f9d41fed38bb12d46f41d50dd2fd7367a80 (commit)
       via  ff863e03a8cbdb0c971a101af01b604dc13d1457 (commit)
       via  6b3dabe34a4fafac4a91fd0b953b49dfb846b713 (commit)
       via  4bcec5ffb6b84ccc0e3b6598567d1e0b67b95976 (commit)
       via  a74802e8424747421511511f78a344de22105c1d (commit)
       via  bb4cc9928f968cd9e72503116a83c1fbe8f07361 (commit)
      from  4a82ece4c4e353ab8e3ceb01fd8e0f4824ac6bbf (commit)

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

- Log -----------------------------------------------------------------
commit 2e940ea65d5b9f371c26352afd9e66719c38a6b9
Merge: 4a82ece 516e15b
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Mar 7 14:06:55 2012 -0800

    [master] Merge branch 'trac1607' with fixing conflicts:
    	src/lib/datasrc/tests/Makefile.am
    auth_srv_unittest.cc needed an adjustment, but I believe it's trivial,
    so I'm commiting and pushing it.

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

Summary of changes:
 src/bin/auth/query.cc                              |  284 +++++++++--------
 src/bin/auth/query.h                               |   48 +---
 src/bin/auth/tests/auth_srv_unittest.cc            |    8 +-
 src/bin/auth/tests/command_unittest.cc             |   16 +-
 src/bin/auth/tests/config_unittest.cc              |    2 +-
 src/bin/auth/tests/query_unittest.cc               |  177 ++++++-----
 src/lib/datasrc/Makefile.am                        |    4 +-
 src/lib/datasrc/database.cc                        |   59 ++--
 src/lib/datasrc/database.h                         |   77 +++--
 src/lib/datasrc/memory_datasrc.cc                  |  199 ++----------
 src/lib/datasrc/memory_datasrc.h                   |   14 +-
 src/lib/datasrc/memory_datasrc_link.cc             |  173 +++++++++++
 src/lib/datasrc/sqlite3_accessor.cc                |   74 -----
 src/lib/datasrc/sqlite3_accessor_link.cc           |  105 +++++++
 src/lib/datasrc/tests/Makefile.am                  |   51 +--
 src/lib/datasrc/tests/database_unittest.cc         |   74 +++---
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |   88 +++---
 src/lib/datasrc/tests/testdata/contexttest.zone    |   35 +++
 .../datasrc/tests/zone_finder_context_unittest.cc  |  322 ++++++++++++++++++++
 src/lib/datasrc/zone.h                             |  258 ++++++++++++----
 src/lib/datasrc/zone_finder_context.cc             |  102 ++++++
 src/lib/python/isc/datasrc/finder_python.cc        |   24 +-
 src/lib/testutils/dnsmessage_test.cc               |    3 +
 src/lib/testutils/dnsmessage_test.h                |   31 ++-
 24 files changed, 1453 insertions(+), 775 deletions(-)
 create mode 100644 src/lib/datasrc/memory_datasrc_link.cc
 create mode 100644 src/lib/datasrc/sqlite3_accessor_link.cc
 create mode 100644 src/lib/datasrc/tests/testdata/contexttest.zone
 create mode 100644 src/lib/datasrc/tests/zone_finder_context_unittest.cc
 create mode 100644 src/lib/datasrc/zone_finder_context.cc

-----------------------------------------------------------------------
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 3474430..7d50393 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -12,89 +12,89 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <algorithm>            // for std::max
-#include <vector>
-#include <boost/foreach.hpp>
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
-
 #include <dns/message.h>
 #include <dns/rcode.h>
+#include <dns/rrtype.h>
 #include <dns/rdataclass.h>
 
 #include <datasrc/client.h>
 
 #include <auth/query.h>
 
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+#include <algorithm>            // for std::max
+#include <vector>
+
+using namespace std;
 using namespace isc::dns;
 using namespace isc::datasrc;
 using namespace isc::dns::rdata;
 
-namespace isc {
-namespace auth {
+// Commonly used helper callback object for vector<ConstRRsetPtr> to
+// insert them to (the specified section of) a message.
+namespace {
+class RRsetInserter {
+public:
+    RRsetInserter(Message& msg, Message::Section section, bool dnssec) :
+        msg_(msg), section_(section), dnssec_(dnssec)
+    {}
+    void operator()(const ConstRRsetPtr& rrset) {
+        msg_.addRRset(section_,
+                      boost::const_pointer_cast<AbstractRRset>(rrset),
+                      dnssec_);
+    }
 
-void
-Query::addAdditional(ZoneFinder& zone, const AbstractRRset& rrset) {
-    RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
-    for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
-        const Rdata& rdata(rdata_iterator->getCurrent());
-        if (rrset.getType() == RRType::NS()) {
-            // Need to perform the search in the "GLUE OK" mode.
-            const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
-            addAdditionalAddrs(zone, ns.getNSName(), ZoneFinder::FIND_GLUE_OK);
-        } else if (rrset.getType() == RRType::MX()) {
-            const generic::MX& mx(dynamic_cast<const generic::MX&>(rdata));
-            addAdditionalAddrs(zone, mx.getMXName());
-        }
+private:
+    Message& msg_;
+    const Message::Section section_;
+    const bool dnssec_;
+};
+
+// This is a "constant" vector storing desired RR types for the additional
+// section.  The vector is filled first time it's used.
+const vector<RRType>&
+A_AND_AAAA() {
+    static vector<RRType> needed_types;
+    if (needed_types.empty()) {
+        needed_types.push_back(RRType::A());
+        needed_types.push_back(RRType::AAAA());
     }
+    return (needed_types);
 }
 
+// A wrapper for ZoneFinder::Context::getAdditional() so we don't include
+// duplicate RRs.  This is not efficient, and we should actually unify
+// this at the end of the process() method.  See also #1688.
 void
-Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
-                          const ZoneFinder::FindOptions options)
+getAdditional(const Name& qname, RRType qtype,
+              ZoneFinder::Context& ctx, vector<ConstRRsetPtr>& results)
 {
-    // Out of zone name
-    NameComparisonResult result = zone.getOrigin().compare(qname);
-    if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
-        (result.getRelation() != NameComparisonResult::EQUAL))
-        return;
-
-    // Omit additional data which has already been provided in the answer
-    // section from the additional.
-    //
-    // All the address rrset with the owner name of qname have been inserted
-    // into ANSWER section.
-    if (qname_ == qname && qtype_ == RRType::ANY())
-        return;
-
-    // Find A rrset
-    if (qname_ != qname || qtype_ != RRType::A()) {
-        ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
-                                                    options | dnssec_opt_);
-        if (a_result.code == ZoneFinder::SUCCESS) {
-            response_.addRRset(Message::SECTION_ADDITIONAL,
-                    boost::const_pointer_cast<AbstractRRset>(a_result.rrset), dnssec_);
-        }
-    }
-
-    // Find AAAA rrset
-    if (qname_ != qname || qtype_ != RRType::AAAA()) {
-        ZoneFinder::FindResult aaaa_result = zone.find(qname, RRType::AAAA(),
-                                                       options | dnssec_opt_);
-        if (aaaa_result.code == ZoneFinder::SUCCESS) {
-            response_.addRRset(Message::SECTION_ADDITIONAL,
-                    boost::const_pointer_cast<AbstractRRset>(aaaa_result.rrset),
-                    dnssec_);
+    vector<ConstRRsetPtr> additionals;
+    ctx.getAdditional(A_AND_AAAA(), additionals);
+
+    vector<ConstRRsetPtr>::const_iterator it = additionals.begin();
+    vector<ConstRRsetPtr>::const_iterator it_end = additionals.end();
+    for (; it != it_end; ++it) {
+        if ((qtype == (*it)->getType() || qtype == RRType::ANY()) &&
+            qname == (*it)->getName()) {
+            continue;
         }
+        results.push_back(*it);
     }
 }
+}
+
+namespace isc {
+namespace auth {
 
 void
 Query::addSOA(ZoneFinder& finder) {
-    ZoneFinder::FindResult soa_result = finder.find(finder.getOrigin(),
-                                                    RRType::SOA(),
-                                                    dnssec_opt_);
-    if (soa_result.code != ZoneFinder::SUCCESS) {
+    ZoneFinderContextPtr soa_ctx = finder.find(finder.getOrigin(),
+                                               RRType::SOA(), dnssec_opt_);
+    if (soa_ctx->code != ZoneFinder::SUCCESS) {
         isc_throw(NoSOA, "There's no SOA record in zone " <<
             finder.getOrigin().toText());
     } else {
@@ -104,7 +104,7 @@ Query::addSOA(ZoneFinder& finder) {
          * to insist.
          */
         response_.addRRset(Message::SECTION_AUTHORITY,
-            boost::const_pointer_cast<AbstractRRset>(soa_result.rrset), dnssec_);
+            boost::const_pointer_cast<AbstractRRset>(soa_ctx->rrset), dnssec_);
     }
 }
 
@@ -148,10 +148,10 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
     // Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
     // otherwise we shouldn't have got NXDOMAIN for the original query in
     // the first place).
-    const ZoneFinder::FindResult fresult =
+    ConstZoneFinderContextPtr fcontext =
         finder.find(wildname, RRType::NSEC(), dnssec_opt_);
-    if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
-        fresult.rrset->getRdataCount() == 0) {
+    if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+        fcontext->rrset->getRdataCount() == 0) {
         isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
     }
 
@@ -160,9 +160,9 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
     // Note: name comparison is relatively expensive.  When we are at the
     // stage of performance optimization, we should consider optimizing this
     // for some optimized data source implementations.
-    if (nsec->getName() != fresult.rrset->getName()) {
+    if (nsec->getName() != fcontext->rrset->getName()) {
         response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
+                           boost::const_pointer_cast<AbstractRRset>(fcontext->rrset),
                            dnssec_);
     }
 }
@@ -230,27 +230,27 @@ Query::addNXDOMAINProofByNSEC3(ZoneFinder& finder) {
 
 void
 Query::addWildcardProof(ZoneFinder& finder,
-                        const ZoneFinder::FindResult& db_result)
+                        const ZoneFinder::Context& db_context)
 {
-    if (db_result.isNSECSigned()) {
+    if (db_context.isNSECSigned()) {
         // Case for RFC4035 Section 3.1.3.3.
         //
         // The query name shouldn't exist in the zone if there were no wildcard
         // substitution.  Confirm that by specifying NO_WILDCARD.  It should
         // result in NXDOMAIN and an NSEC RR that proves it should be returned.
-        const ZoneFinder::FindResult fresult =
+        ConstZoneFinderContextPtr fcontext =
             finder.find(qname_, RRType::NSEC(),
                         dnssec_opt_ | ZoneFinder::NO_WILDCARD);
-        if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
-            fresult.rrset->getRdataCount() == 0) {
+        if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+            fcontext->rrset->getRdataCount() == 0) {
             isc_throw(BadNSEC,
                       "Unexpected NSEC result for wildcard proof");
         }
         response_.addRRset(Message::SECTION_AUTHORITY,
                            boost::const_pointer_cast<AbstractRRset>(
-                               fresult.rrset),
+                               fcontext->rrset),
                            dnssec_);
-    } else if (db_result.isNSEC3Signed()) {
+    } else if (db_context.isNSEC3Signed()) {
         // Case for RFC 5155 Section 7.2.6.
         //
         // Note that the closest encloser must be the immediate ancestor
@@ -269,36 +269,36 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
         isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
     }
     
-    const ZoneFinder::FindResult fresult =
+    ConstZoneFinderContextPtr fcontext =
         finder.find(qname_, RRType::NSEC(),
                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
-    if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
-        fresult.rrset->getRdataCount() == 0) {
+    if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+        fcontext->rrset->getRdataCount() == 0) {
         isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
     }
    
-    if (nsec->getName() != fresult.rrset->getName()) {
+    if (nsec->getName() != fcontext->rrset->getName()) {
         // one NSEC RR proves wildcard_nxrrset that no matched QNAME.
         response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
+                           boost::const_pointer_cast<AbstractRRset>(fcontext->rrset),
                            dnssec_);
     }
 }
 
 void
 Query::addDS(ZoneFinder& finder, const Name& dname) {
-    ZoneFinder::FindResult ds_result =
+    ConstZoneFinderContextPtr ds_context =
         finder.find(dname, RRType::DS(), dnssec_opt_);
-    if (ds_result.code == ZoneFinder::SUCCESS) {
+    if (ds_context->code == ZoneFinder::SUCCESS) {
         response_.addRRset(Message::SECTION_AUTHORITY,
                            boost::const_pointer_cast<AbstractRRset>(
-                               ds_result.rrset),
+                               ds_context->rrset),
                            dnssec_);
-    } else if (ds_result.code == ZoneFinder::NXRRSET &&
-               ds_result.isNSECSigned()) {
-        addNXRRsetProof(finder, ds_result);
-    } else if (ds_result.code == ZoneFinder::NXRRSET &&
-               ds_result.isNSEC3Signed()) {
+    } else if (ds_context->code == ZoneFinder::NXRRSET &&
+               ds_context->isNSECSigned()) {
+        addNXRRsetProof(finder, *ds_context);
+    } else if (ds_context->code == ZoneFinder::NXRRSET &&
+               ds_context->isNSEC3Signed()) {
         // Add no DS proof with NSEC3 as specified in RFC 5155 Section 7.2.7.
         addClosestEncloserProof(finder, dname, true);
     } else {
@@ -309,17 +309,17 @@ Query::addDS(ZoneFinder& finder, const Name& dname) {
 
 void
 Query::addNXRRsetProof(ZoneFinder& finder,
-                       const ZoneFinder::FindResult& db_result)
+                       const ZoneFinder::Context& db_context)
 {
-    if (db_result.isNSECSigned() && db_result.rrset) {
+    if (db_context.isNSECSigned() && db_context.rrset) {
         response_.addRRset(Message::SECTION_AUTHORITY,
                            boost::const_pointer_cast<AbstractRRset>(
-                               db_result.rrset),
+                               db_context.rrset),
                            dnssec_);
-        if (db_result.isWildcard()) {
-            addWildcardNXRRSETProof(finder, db_result.rrset);
+        if (db_context.isWildcard()) {
+            addWildcardNXRRSETProof(finder, db_context.rrset);
         }
-    } else if (db_result.isNSEC3Signed() && !db_result.isWildcard()) {
+    } else if (db_context.isNSEC3Signed() && !db_context.isWildcard()) {
         if (qtype_ == RRType::DS()) {
             // RFC 5155, Section 7.2.4.  Add either NSEC3 for the qname or
             // closest (provable) encloser proof in case of optout.
@@ -328,7 +328,7 @@ Query::addNXRRsetProof(ZoneFinder& finder,
             // RFC 5155, Section 7.2.3.  Just add NSEC3 for the qname.
             addNSEC3ForName(finder, qname_, true);
         }
-    } else if (db_result.isNSEC3Signed() && db_result.isWildcard()) {
+    } else if (db_context.isNSEC3Signed() && db_context.isWildcard()) {
         // Case for RFC 5155 Section 7.2.5: add closest encloser proof for the
         // qname, construct the matched wildcard name and add NSEC3 for it.
         const uint8_t closest_labels =
@@ -340,21 +340,24 @@ Query::addNXRRsetProof(ZoneFinder& finder,
 }
 
 void
-Query::addAuthAdditional(ZoneFinder& finder) {
+Query::addAuthAdditional(ZoneFinder& finder,
+                         vector<ConstRRsetPtr>& additionals)
+{
+    const Name& origin = finder.getOrigin();
+
     // Fill in authority and addtional sections.
-    ZoneFinder::FindResult ns_result =
-        finder.find(finder.getOrigin(), RRType::NS(), dnssec_opt_);
+    ConstZoneFinderContextPtr ns_context = finder.find(origin, RRType::NS(),
+                                                       dnssec_opt_);
 
     // zone origin name should have NS records
-    if (ns_result.code != ZoneFinder::SUCCESS) {
+    if (ns_context->code != ZoneFinder::SUCCESS) {
         isc_throw(NoApexNS, "There's no apex NS records in zone " <<
-                finder.getOrigin().toText());
-    } else {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-            boost::const_pointer_cast<AbstractRRset>(ns_result.rrset), dnssec_);
-        // Handle additional for authority section
-        addAdditional(finder, *ns_result.rrset);
+                  finder.getOrigin().toText());
     }
+    response_.addRRset(Message::SECTION_AUTHORITY,
+                       boost::const_pointer_cast<AbstractRRset>(
+                           ns_context->rrset), dnssec_);
+    getAdditional(qname_, qtype_, *ns_context, additionals);
 }
 
 namespace {
@@ -403,8 +406,9 @@ Query::process() {
     // indirectly via delegation).  Look into the zone.
     response_.setHeaderFlag(Message::HEADERFLAG_AA);
     response_.setRcode(Rcode::NOERROR());
-    std::vector<ConstRRsetPtr> target;
-    boost::function0<ZoneFinder::FindResult> find;
+    vector<ConstRRsetPtr> target;
+    vector<ConstRRsetPtr> additionals;
+    boost::function0<ZoneFinderContextPtr> find;
     const bool qtype_is_any = (qtype_ == RRType::ANY());
     if (qtype_is_any) {
         find = boost::bind(&ZoneFinder::findAll, &zfinder, qname_,
@@ -413,12 +417,12 @@ Query::process() {
         find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
                            dnssec_opt_);
     }
-    ZoneFinder::FindResult db_result(find());
-    switch (db_result.code) {
+    ZoneFinderContextPtr db_context(find());
+    switch (db_context->code) {
         case ZoneFinder::DNAME: {
             // First, put the dname into the answer
             response_.addRRset(Message::SECTION_ANSWER,
-                boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
+                boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
                 dnssec_);
             /*
              * Empty DNAME should never get in, as it is impossible to
@@ -426,14 +430,14 @@ Query::process() {
              *
              * FIXME: Other way to prevent this should be done
              */
-            assert(db_result.rrset->getRdataCount() > 0);
+            assert(db_context->rrset->getRdataCount() > 0);
             // Get the data of DNAME
             const rdata::generic::DNAME& dname(
                 dynamic_cast<const rdata::generic::DNAME&>(
-                db_result.rrset->getRdataIterator()->getCurrent()));
+                db_context->rrset->getRdataIterator()->getCurrent()));
             // The yet unmatched prefix dname
             const Name prefix(qname_.split(0, qname_.getLabelCount() -
-                db_result.rrset->getName().getLabelCount()));
+                db_context->rrset->getName().getLabelCount()));
             // If we put it together, will it be too long?
             // (The prefix contains trailing ., which will be removed
             if (prefix.getLength() - Name::ROOT_NAME().getLength() +
@@ -448,12 +452,12 @@ Query::process() {
             // The new CNAME we are creating (it will be unsigned even
             // with DNSSEC, the DNAME is signed and it can be validated
             // by that)
-            RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
-                RRType::CNAME(), db_result.rrset->getTTL()));
+            RRsetPtr cname(new RRset(qname_, db_context->rrset->getClass(),
+                RRType::CNAME(), db_context->rrset->getTTL()));
             // Construct the new target by replacing the end
             cname->addRdata(rdata::generic::CNAME(qname_.split(0,
                 qname_.getLabelCount() -
-                db_result.rrset->getName().getLabelCount()).
+                db_context->rrset->getName().getLabelCount()).
                 concatenate(dname.getDname())));
             response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
             break;
@@ -469,13 +473,13 @@ Query::process() {
              * So, just put it there.
              */
             response_.addRRset(Message::SECTION_ANSWER,
-                boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
+                boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
                 dnssec_);
 
             // If the answer is a result of wildcard substitution,
             // add a proof that there's no closer name.
-            if (dnssec_ && db_result.isWildcard()) {
-                addWildcardProof(*result.zone_finder,db_result);
+            if (dnssec_ && db_context->isWildcard()) {
+                addWildcardProof(*result.zone_finder, *db_context);
             }
             break;
         case ZoneFinder::SUCCESS:
@@ -485,31 +489,33 @@ Query::process() {
                 BOOST_FOREACH(ConstRRsetPtr rrset, target) {
                     response_.addRRset(Message::SECTION_ANSWER,
                         boost::const_pointer_cast<AbstractRRset>(rrset), dnssec_);
-                    // Handle additional for answer section
-                    addAdditional(*result.zone_finder, *rrset.get());
                 }
             } else {
                 response_.addRRset(Message::SECTION_ANSWER,
-                    boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
+                    boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
                     dnssec_);
-                // Handle additional for answer section
-                addAdditional(*result.zone_finder, *db_result.rrset);
             }
+
+            // Retrieve additional records for the answer
+            getAdditional(qname_, qtype_, *db_context, additionals);
+
             // If apex NS records haven't been provided in the answer
             // section, insert apex NS records into the authority section
             // and AAAA/A RRS of each of the NS RDATA into the additional
             // section.
-            if (qname_ != result.zone_finder->getOrigin() ||
-                db_result.code != ZoneFinder::SUCCESS ||
+            // Checking the findZone() is a lightweight check to see if
+            // qname is the zone origin.
+            if (result.code != result::SUCCESS ||
+                db_context->code != ZoneFinder::SUCCESS ||
                 (qtype_ != RRType::NS() && !qtype_is_any))
             {
-                addAuthAdditional(*result.zone_finder);
+                addAuthAdditional(*result.zone_finder, additionals);
             }
 
             // If the answer is a result of wildcard substitution,
             // add a proof that there's no closer name.
-            if (dnssec_ && db_result.isWildcard()) {
-                addWildcardProof(*result.zone_finder,db_result);
+            if (dnssec_ && db_context->isWildcard()) {
+                addWildcardProof(*result.zone_finder, *db_context);
             }
             break;
         case ZoneFinder::DELEGATION:
@@ -523,22 +529,24 @@ Query::process() {
 
             response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
             response_.addRRset(Message::SECTION_AUTHORITY,
-                boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
+                boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
                 dnssec_);
+            // Retrieve additional records for the name servers
+            db_context->getAdditional(A_AND_AAAA(), additionals);
+
             // If DNSSEC is requested, see whether there is a DS
             // record for this delegation.
             if (dnssec_) {
-                addDS(*result.zone_finder, db_result.rrset->getName());
+                addDS(*result.zone_finder, db_context->rrset->getName());
             }
-            addAdditional(*result.zone_finder, *db_result.rrset);
             break;
         case ZoneFinder::NXDOMAIN:
             response_.setRcode(Rcode::NXDOMAIN());
             addSOA(*result.zone_finder);
             if (dnssec_) {
-                if (db_result.isNSECSigned() && db_result.rrset) {
-                    addNXDOMAINProofByNSEC(zfinder, db_result.rrset);
-                } else if (db_result.isNSEC3Signed()) {
+                if (db_context->isNSECSigned() && db_context->rrset) {
+                    addNXDOMAINProofByNSEC(zfinder, db_context->rrset);
+                } else if (db_context->isNSEC3Signed()) {
                     addNXDOMAINProofByNSEC3(zfinder);
                 }
             }
@@ -546,7 +554,7 @@ Query::process() {
         case ZoneFinder::NXRRSET:
             addSOA(*result.zone_finder);
             if (dnssec_) {
-                addNXRRsetProof(zfinder, db_result);
+                addNXRRsetProof(zfinder, *db_context);
             }
             break;
         default:
@@ -556,6 +564,10 @@ Query::process() {
             isc_throw(isc::NotImplemented, "Unknown result code");
             break;
     }
+
+    for_each(additionals.begin(), additionals.end(),
+             RRsetInserter(response_, Message::SECTION_ADDITIONAL,
+                           dnssec_));
 }
 
 bool
@@ -579,11 +591,11 @@ Query::processDSAtChild() {
     response_.setHeaderFlag(Message::HEADERFLAG_AA);
     response_.setRcode(Rcode::NOERROR());
     addSOA(*zresult.zone_finder);
-    const ZoneFinder::FindResult ds_result =
+    ConstZoneFinderContextPtr ds_context =
         zresult.zone_finder->find(qname_, RRType::DS(), dnssec_opt_);
-    if (ds_result.code == ZoneFinder::NXRRSET) {
+    if (ds_context->code == ZoneFinder::NXRRSET) {
         if (dnssec_) {
-            addNXRRsetProof(*zresult.zone_finder, ds_result);
+            addNXRRsetProof(*zresult.zone_finder, *ds_context);
         }
     }
 
diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h
index e8d3ba8..f8a8f02 100644
--- a/src/bin/auth/query.h
+++ b/src/bin/auth/query.h
@@ -15,8 +15,11 @@
  */
 
 #include <exceptions/exceptions.h>
+#include <dns/rrset.h>
 #include <datasrc/zone.h>
 
+#include <vector>
+
 namespace isc {
 namespace dns {
 class Message;
@@ -96,7 +99,7 @@ private:
     ///               data
     /// \param db_result The ZoneFinder::FindResult returned by find()
     void addNXRRsetProof(isc::datasrc::ZoneFinder& finder,
-        const isc::datasrc::ZoneFinder::FindResult& db_result);
+                         const isc::datasrc::ZoneFinder::Context& db_context);
 
     /// Add NSEC RRs that prove an NXDOMAIN result.
     ///
@@ -115,7 +118,7 @@ private:
     /// of RFC5155.
     void addWildcardProof(
         isc::datasrc::ZoneFinder& finder,
-        const isc::datasrc::ZoneFinder::FindResult& dbResult);
+        const isc::datasrc::ZoneFinder::Context& db_context);
 
     /// \brief Adds one NSEC RR proved no matched QNAME,one NSEC RR proved no
     /// matched <QNAME,QTYPE> through wildcard extension.
@@ -129,44 +132,6 @@ private:
     void addWildcardNXRRSETProof(isc::datasrc::ZoneFinder& finder,
                                  isc::dns::ConstRRsetPtr nsec);
 
-    /// \brief Look up additional data (i.e., address records for the names
-    /// included in NS or MX records) and add them to the additional section.
-    ///
-    /// Note: Any additional data which has already been provided in the
-    /// answer section (i.e., if the original query happend to be for the
-    /// address of the DNS server), it should be omitted from the additional.
-    ///
-    /// This method may throw a exception because its underlying methods may
-    /// throw exceptions.
-    ///
-    /// \param zone The ZoneFinder through which the additional data for the
-    /// query is to be found.
-    /// \param rrset The RRset (i.e., NS or MX rrset) which require additional
-    /// processing.
-    void addAdditional(isc::datasrc::ZoneFinder& zone,
-                       const isc::dns::AbstractRRset& rrset);
-
-    /// \brief Find address records for a specified name.
-    ///
-    /// Search the specified zone for AAAA/A RRs of each of the NS/MX RDATA
-    /// (domain name), and insert the found ones into the additional section
-    /// if address records are available. By default the search will stop
-    /// once it encounters a zone cut.
-    ///
-    /// Note: we need to perform the search in the "GLUE OK" mode for NS RDATA,
-    /// which means that we should include A/AAAA RRs under a zone cut.
-    /// The glue records must exactly match the name in the NS RDATA, without
-    /// CNAME or wildcard processing.
-    ///
-    /// \param zone The \c ZoneFinder through which the address records is to
-    /// be found.
-    /// \param qname The name in rrset RDATA.
-    /// \param options The search options.
-    void addAdditionalAddrs(isc::datasrc::ZoneFinder& zone,
-                            const isc::dns::Name& qname,
-                            const isc::datasrc::ZoneFinder::FindOptions options
-                            = isc::datasrc::ZoneFinder::FIND_DEFAULT);
-
     /// \brief Look up a zone's NS RRset and their address records for an
     /// authoritative answer, and add them to the additional section.
     ///
@@ -185,7 +150,8 @@ private:
     ///
     /// \param finder The \c ZoneFinder through which the NS and additional
     /// data for the query are to be found.
-    void addAuthAdditional(isc::datasrc::ZoneFinder& finder);
+    void addAuthAdditional(isc::datasrc::ZoneFinder& finder,
+                           std::vector<isc::dns::ConstRRsetPtr>& additionals);
 
     /// \brief Process a DS query possible at the child side of zone cut.
     ///
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 688ce62..4823ad7 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -1135,7 +1135,7 @@ public:
         return (real_zone_finder_->getClass());
     }
 
-    virtual isc::datasrc::ZoneFinder::FindResult
+    virtual isc::datasrc::ZoneFinderContextPtr
     find(const isc::dns::Name& name,
          const isc::dns::RRType& type,
          isc::datasrc::ZoneFinder::FindOptions options)
@@ -1144,20 +1144,20 @@ public:
         return (real_zone_finder_->find(name, type, options));
     }
 
-    virtual FindResult
+    virtual isc::datasrc::ZoneFinderContextPtr
     findAll(const isc::dns::Name& name,
             std::vector<isc::dns::ConstRRsetPtr> &target,
             const FindOptions options = FIND_DEFAULT)
     {
         checkThrow(THROW_AT_FIND_ALL, throw_when_, isc_exception_);
         return (real_zone_finder_->findAll(name, target, options));
-    };
+    }
 
     virtual FindNSEC3Result
     findNSEC3(const isc::dns::Name& name, bool recursive) {
         checkThrow(THROW_AT_FIND_NSEC3, throw_when_, isc_exception_);
         return (real_zone_finder_->findNSEC3(name, recursive));
-    };
+    }
 
     virtual isc::dns::Name
     findPreviousName(const isc::dns::Name& query) const {
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index 087ce81..093afda 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -176,16 +176,16 @@ zoneChecks(AuthSrv& server) {
     EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test1.example")).zone_finder->
-              find(Name("ns.test1.example"), RRType::A()).code);
+              find(Name("ns.test1.example"), RRType::A())->code);
     EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test1.example")).zone_finder->
-              find(Name("ns.test1.example"), RRType::AAAA()).code);
+              find(Name("ns.test1.example"), RRType::AAAA())->code);
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test2.example")).zone_finder->
-              find(Name("ns.test2.example"), RRType::A()).code);
+              find(Name("ns.test2.example"), RRType::A())->code);
     EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test2.example")).zone_finder->
-              find(Name("ns.test2.example"), RRType::AAAA()).code);
+              find(Name("ns.test2.example"), RRType::AAAA())->code);
 }
 
 void
@@ -213,19 +213,19 @@ newZoneChecks(AuthSrv& server) {
     EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test1.example")).zone_finder->
-              find(Name("ns.test1.example"), RRType::A()).code);
+              find(Name("ns.test1.example"), RRType::A())->code);
     // now test1.example should have ns/AAAA
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test1.example")).zone_finder->
-              find(Name("ns.test1.example"), RRType::AAAA()).code);
+              find(Name("ns.test1.example"), RRType::AAAA())->code);
 
     // test2.example shouldn't change
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test2.example")).zone_finder->
-              find(Name("ns.test2.example"), RRType::A()).code);
+              find(Name("ns.test2.example"), RRType::A())->code);
     EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test2.example")).zone_finder->
-              find(Name("ns.test2.example"), RRType::AAAA()).code);
+              find(Name("ns.test2.example"), RRType::AAAA())->code);
 }
 
 TEST_F(AuthCommandTest, loadZone) {
diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc
index 973ab31..fcf88b1 100644
--- a/src/bin/auth/tests/config_unittest.cc
+++ b/src/bin/auth/tests/config_unittest.cc
@@ -191,7 +191,7 @@ TEST_F(MemoryDatasrcConfigTest, addOneZone) {
     // Check it actually loaded something
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(rrclass)->findZone(
         Name("ns.example.com.")).zone_finder->find(Name("ns.example.com."),
-        RRType::A()).code);
+        RRType::A())->code);
 }
 
 TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 0c413a1..3c901aa 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -370,12 +370,14 @@ public:
     }
     virtual isc::dns::Name getOrigin() const { return (origin_); }
     virtual isc::dns::RRClass getClass() const { return (rrclass_); }
-    virtual FindResult find(const isc::dns::Name& name,
-                            const isc::dns::RRType& type,
-                            const FindOptions options = FIND_DEFAULT);
-    virtual FindResult findAll(const isc::dns::Name& name,
-                               std::vector<ConstRRsetPtr>& target,
-                               const FindOptions options = FIND_DEFAULT);
+    virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+                                      const isc::dns::RRType& type,
+                                      const FindOptions options =
+                                      FIND_DEFAULT);
+    virtual ZoneFinderContextPtr findAll(const isc::dns::Name& name,
+                                         std::vector<ConstRRsetPtr>& target,
+                                         const FindOptions options =
+                                         FIND_DEFAULT);
 
     virtual ZoneFinder::FindNSEC3Result
     findNSEC3(const Name& name, bool recursive);
@@ -397,8 +399,10 @@ public:
                        ConstRRsetPtr rrset)
     {
         nsec_name_ = nsec_name;
-        nsec_result_.reset(new ZoneFinder::FindResult(code, rrset,
-                                                      RESULT_NSEC_SIGNED));
+        nsec_context_.reset(new Context(*this,
+                                        FIND_DEFAULT, // a fake value
+                                        ResultContext(code, rrset,
+                                                      RESULT_NSEC_SIGNED)));
     }
 
     // Once called, the findNSEC3 will return the provided result for the next
@@ -435,6 +439,18 @@ public:
     ConstRRsetPtr dname_rrset_; // could be used as an arbitrary bogus RRset
     ConstRRsetPtr empty_nsec_rrset_;
 
+protected:
+    // A convenient shortcut.  Will also be used by further derived mocks.
+    ZoneFinderContextPtr createContext(FindOptions options,
+                                       Result code,
+                                       isc::dns::ConstRRsetPtr rrset,
+                                       FindResultFlags flags = RESULT_DEFAULT)
+    {
+        return (ZoneFinderContextPtr(
+                    new Context(*this, options,
+                                ResultContext(code, rrset, flags))));
+    }
+
 private:
     typedef map<RRType, ConstRRsetPtr> RRsetStore;
     typedef map<Name, RRsetStore> Domains;
@@ -505,7 +521,7 @@ private:
     bool use_nsec3_;
     // The following two will be used for faked NSEC cases
     Name nsec_name_;
-    boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
+    ZoneFinderContextPtr nsec_context_;
     // The following two are for faking bad NSEC3 responses
     // Enabled when not NULL
     const FindNSEC3Result* nsec3_fake_;
@@ -533,12 +549,12 @@ substituteWild(const AbstractRRset& wild_rrset, const Name& real_name) {
     return (rrset);
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
                         const FindOptions options)
 {
-    ZoneFinder::FindResult result(find(name, RRType::ANY(), options));
-    if (result.code == NXRRSET) {
+    ZoneFinderContextPtr result(find(name, RRType::ANY(), options));
+    if (result->code == NXRRSET) {
         const Domains::const_iterator found_domain = domains_.find(name);
         if (!found_domain->second.empty()) {
             for (RRsetStore::const_iterator found_rrset =
@@ -547,7 +563,10 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
                 // Insert RRs under the domain name into target
                 target.push_back(found_rrset->second);
             }
-            return (FindResult(SUCCESS, RRsetPtr()));
+            return (ZoneFinderContextPtr(
+                        new Context(*this, options,
+                                    ResultContext(SUCCESS, RRsetPtr()),
+                                    target)));
         }
     }
 
@@ -610,16 +629,16 @@ MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
     isc_throw(isc::Unexpected, "findNSEC3() isn't expected to fail");
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 MockZoneFinder::find(const Name& name, const RRType& type,
                      const FindOptions options)
 {
     // Emulating a broken zone: mandatory apex RRs are missing if specifically
     // configured so (which are rare cases).
     if (name == origin_ && type == RRType::SOA() && !has_SOA_) {
-        return (FindResult(NXDOMAIN, RRsetPtr()));
+        return (createContext(options, NXDOMAIN, RRsetPtr()));
     } else if (name == origin_ && type == RRType::NS() && !has_apex_NS_) {
-        return (FindResult(NXDOMAIN, RRsetPtr()));
+        return (createContext(options, NXDOMAIN, RRsetPtr()));
     }
 
     // Special case for names on or under a zone cut and under DNAME
@@ -634,11 +653,11 @@ MockZoneFinder::find(const Name& name, const RRType& type,
         // handled just like an in-zone case below.  Others result in
         // DELEGATION.
         if (type != RRType::DS() || it->first != name) {
-            return (FindResult(DELEGATION, delegation_ns));
+            return (createContext(options, DELEGATION, delegation_ns));
         }
     } else if (name.compare(dname_name_).getRelation() ==
                NameComparisonResult::SUBDOMAIN) {
-        return (FindResult(DNAME, dname_rrset_));
+        return (createContext(options, DNAME, dname_rrset_));
     }
 
     // normal cases.  names are searched for only per exact-match basis
@@ -668,33 +687,35 @@ MockZoneFinder::find(const Name& name, const RRType& type,
                 }
                 rrset = noconst;
             }
-            return (FindResult(SUCCESS, rrset));
+            return (createContext(options, SUCCESS, rrset));
         }
 
         // Otherwise, if this domain name has CNAME, return it.
         found_rrset = found_domain->second.find(RRType::CNAME());
         if (found_rrset != found_domain->second.end()) {
-            return (FindResult(CNAME, found_rrset->second));
+            return (createContext(options, CNAME, found_rrset->second));
         }
 
         // Otherwise it's NXRRSET case...
         // ...but a special pathological case first:
         if (found_domain->first == bad_signed_delegation_name_ &&
             type == RRType::DS()) {
-            return (FindResult(NXDOMAIN, RRsetPtr()));
+            return (createContext(options, NXDOMAIN, RRsetPtr()));
         }
         // normal cases follow.
         if ((options & FIND_DNSSEC) != 0) {
             if (use_nsec3_) {
-                return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+                return (createContext(options, NXRRSET, RRsetPtr(),
+                                      RESULT_NSEC3_SIGNED));
             }
             found_rrset = found_domain->second.find(RRType::NSEC());
             if (found_rrset != found_domain->second.end()) {
-                return (FindResult(NXRRSET, found_rrset->second,
-                                   RESULT_NSEC_SIGNED));
+                return (createContext(options, NXRRSET, found_rrset->second,
+                                      RESULT_NSEC_SIGNED));
             }
         }
-        return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC_SIGNED));
+        return (createContext(options, NXRRSET, RRsetPtr(),
+                              RESULT_NSEC_SIGNED));
     }
 
     // query name isn't found in our domains.
@@ -714,16 +735,17 @@ MockZoneFinder::find(const Name& name, const RRType& type,
         --domain;               // reset domain to the "previous name"
         if ((options & FIND_DNSSEC) != 0) {
             if (use_nsec3_) {
-                return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+                return (createContext(options, NXRRSET, RRsetPtr(),
+                                      RESULT_NSEC3_SIGNED));
             }
             RRsetStore::const_iterator found_rrset =
                 (*domain).second.find(RRType::NSEC());
             if (found_rrset != (*domain).second.end()) {
-                return (FindResult(NXRRSET, found_rrset->second,
-                                   RESULT_NSEC_SIGNED));
+                return (createContext(options, NXRRSET, found_rrset->second,
+                                      RESULT_NSEC_SIGNED));
             }
         }
-        return (FindResult(NXRRSET, RRsetPtr()));
+        return (createContext(options, NXRRSET, RRsetPtr()));
     }
 
     // Another possibility is wildcard.  For simplicity we only check
@@ -746,39 +768,37 @@ MockZoneFinder::find(const Name& name, const RRType& type,
                         domain->second.find(type);
                     // Matched the QTYPE
                     if(found_rrset != domain->second.end()) {
-                        return (FindResult(SUCCESS,
-                                           substituteWild(
-                                               *found_rrset->second, name),
-                                           RESULT_WILDCARD |
-                                           (use_nsec3_ ?
-                                            RESULT_NSEC3_SIGNED :
-                                            RESULT_NSEC_SIGNED)));
+                        return (createContext(options,SUCCESS, substituteWild(
+                                                  *found_rrset->second, name),
+                                              RESULT_WILDCARD |
+                                              (use_nsec3_ ?
+                                               RESULT_NSEC3_SIGNED :
+                                               RESULT_NSEC_SIGNED)));
                     } else {
                         // No matched QTYPE, this case is for NXRRSET with
                         // WILDCARD
                         if (use_nsec3_) {
-                            return (FindResult(NXRRSET, RRsetPtr(),
-                                               RESULT_WILDCARD |
-                                               RESULT_NSEC3_SIGNED));
+                            return (createContext(options, NXRRSET, RRsetPtr(),
+                                                  RESULT_WILDCARD |
+                                                  RESULT_NSEC3_SIGNED));
                         }
                         const Name new_name =
                             Name("*").concatenate(wild_suffix);
                         found_rrset = domain->second.find(RRType::NSEC());
                         assert(found_rrset != domain->second.end());
-                        return (FindResult(NXRRSET,
-                                           substituteWild(
-                                               *found_rrset->second,
-                                               new_name),
-                                           RESULT_WILDCARD |
-                                           RESULT_NSEC_SIGNED));
+                        return (createContext(options, NXRRSET, substituteWild(
+                                                  *found_rrset->second,
+                                                  new_name),
+                                              RESULT_WILDCARD |
+                                              RESULT_NSEC_SIGNED));
                     }
                 } else {
                     // This is empty non terminal name case on wildcard.
                     const Name empty_name = Name("*").concatenate(wild_suffix);
                     if (use_nsec3_) {
-                        return (FindResult(NXRRSET, RRsetPtr(),
-                                           RESULT_WILDCARD |
-                                           RESULT_NSEC3_SIGNED));
+                        return (createContext(options, NXRRSET, RRsetPtr(),
+                                              RESULT_WILDCARD |
+                                              RESULT_NSEC3_SIGNED));
                     }
                     for (Domains::reverse_iterator it = domains_.rbegin();
                          it != domains_.rend();
@@ -787,13 +807,15 @@ MockZoneFinder::find(const Name& name, const RRType& type,
                         if ((*it).first < empty_name &&
                             (nsec_it = (*it).second.find(RRType::NSEC()))
                             != (*it).second.end()) {
-                            return (FindResult(NXRRSET, (*nsec_it).second,
-                                               RESULT_WILDCARD |
-                                               RESULT_NSEC_SIGNED));
+                            return (createContext(options, NXRRSET,
+                                                  (*nsec_it).second,
+                                                  RESULT_WILDCARD |
+                                                  RESULT_NSEC_SIGNED));
                         }
                     }
                 }
-                return (FindResult(NXRRSET, RRsetPtr(), RESULT_WILDCARD));
+                return (createContext(options, NXRRSET, RRsetPtr(),
+                                      RESULT_WILDCARD));
              }
         }
         const Name cnamewild_suffix("cnamewild.example.com");
@@ -804,11 +826,11 @@ MockZoneFinder::find(const Name& name, const RRType& type,
             RRsetStore::const_iterator found_rrset =
                 domain->second.find(RRType::CNAME());
             assert(found_rrset != domain->second.end());
-            return (FindResult(CNAME,
-                               substituteWild(*found_rrset->second, name),
-                               RESULT_WILDCARD |
-                               (use_nsec3_ ? RESULT_NSEC3_SIGNED :
-                                RESULT_NSEC_SIGNED)));
+            return (createContext(options, CNAME,
+                                  substituteWild(*found_rrset->second, name),
+                                  RESULT_WILDCARD |
+                                  (use_nsec3_ ? RESULT_NSEC3_SIGNED :
+                                   RESULT_NSEC_SIGNED)));
         }
     }
 
@@ -821,12 +843,13 @@ MockZoneFinder::find(const Name& name, const RRType& type,
     // than the origin)
     if ((options & FIND_DNSSEC) != 0) {
         if (use_nsec3_) {
-            return (FindResult(NXDOMAIN, RRsetPtr(), RESULT_NSEC3_SIGNED));
+            return (createContext(options, NXDOMAIN, RRsetPtr(),
+                                  RESULT_NSEC3_SIGNED));
         }
 
         // Emulate a broken DataSourceClient for some special names.
-        if (nsec_result_ && nsec_name_ == name) {
-            return (*nsec_result_);
+        if (nsec_context_ && nsec_name_ == name) {
+            return (nsec_context_);
         }
 
         // Normal case
@@ -839,12 +862,12 @@ MockZoneFinder::find(const Name& name, const RRType& type,
             if ((*it).first < name &&
                 (nsec_it = (*it).second.find(RRType::NSEC()))
                 != (*it).second.end()) {
-                return (FindResult(NXDOMAIN, (*nsec_it).second,
-                                   RESULT_NSEC_SIGNED));
+                return (createContext(options, NXDOMAIN, (*nsec_it).second,
+                                      RESULT_NSEC_SIGNED));
             }
         }
     }
-    return (FindResult(NXDOMAIN, RRsetPtr()));
+    return (createContext(options,NXDOMAIN, RRsetPtr()));
 }
 
 class QueryTest : public ::testing::Test {
@@ -1117,7 +1140,6 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
     // proof (and their RRSIGs).  The closest encloser is the apex (origin),
     // and with our faked hash the covering NSEC3 for the next closer
     // (= child zone name) is that for www.example.com.
-    cout << response << endl;
     responseCheck(response, Rcode::NOERROR(), 0, 0, 5, 0,
                   NULL,
                   (string(unsigned_delegation_txt) +
@@ -1962,23 +1984,23 @@ public:
         MockZoneFinder(), origin_(origin), have_ds_(have_ds)
     {}
     virtual isc::dns::Name getOrigin() const { return (origin_); }
-    virtual FindResult find(const isc::dns::Name&,
-                            const isc::dns::RRType& type,
-                            const FindOptions)
+    virtual ZoneFinderContextPtr find(const isc::dns::Name&,
+                                      const isc::dns::RRType& type,
+                                      const FindOptions options)
     {
         if (type == RRType::SOA()) {
             RRsetPtr soa = textToRRset(origin_.toText() + " 3600 IN SOA . . "
                                        "0 0 0 0 0\n", origin_);
             soa->addRRsig(RdataPtr(new generic::RRSIG(
                                        getCommonRRSIGText("SOA"))));
-            return (FindResult(SUCCESS, soa));
+            return (createContext(options, SUCCESS, soa));
         }
         if (type == RRType::NS()) {
             RRsetPtr ns = textToRRset(origin_.toText() + " 3600 IN NS " +
                                       Name("ns").concatenate(origin_).toText());
             ns->addRRsig(RdataPtr(new generic::RRSIG(
                                       getCommonRRSIGText("NS"))));
-            return (FindResult(SUCCESS, ns));
+            return (createContext(options, SUCCESS, ns));
         }
         if (type == RRType::DS()) {
             if (have_ds_) {
@@ -1988,7 +2010,7 @@ public:
                                           "3CD34AC1AFE51DE");
                 ds->addRRsig(RdataPtr(new generic::RRSIG(
                                           getCommonRRSIGText("DS"))));
-                return (FindResult(SUCCESS, ds));
+                return (createContext(options, SUCCESS, ds));
             } else {
                 RRsetPtr nsec = textToRRset(origin_.toText() +
                                             " 3600 IN NSEC " +
@@ -1996,12 +2018,13 @@ public:
                                             " SOA NSEC RRSIG");
                 nsec->addRRsig(RdataPtr(new generic::RRSIG(
                                             getCommonRRSIGText("NSEC"))));
-                return (FindResult(NXRRSET, nsec, RESULT_NSEC_SIGNED));
+                return (createContext(options, NXRRSET, nsec,
+                                      RESULT_NSEC_SIGNED));
             }
         }
 
         // Returning NXDOMAIN is not correct, but doesn't matter for our tests.
-        return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+        return (createContext(options, NXDOMAIN, ConstRRsetPtr()));
     }
 private:
     const Name origin_;
@@ -2311,11 +2334,11 @@ TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
 // clean them up.
 TEST_F(QueryTest, emptyNameWithNSEC3) {
     mock_finder->setNSEC3Flag(true);
-    ZoneFinder::FindResult result = mock_finder->find(
+    ZoneFinderContextPtr result = mock_finder->find(
         Name("no.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
-    EXPECT_FALSE(result.rrset);
-    EXPECT_TRUE(result.isNSEC3Signed());
-    EXPECT_FALSE(result.isWildcard());
+    EXPECT_EQ(ZoneFinder::NXRRSET, result->code);
+    EXPECT_FALSE(result->rrset);
+    EXPECT_TRUE(result->isNSEC3Signed());
+    EXPECT_FALSE(result->isWildcard());
 }
 }
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index a058b33..dde3a82 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -24,7 +24,7 @@ libdatasrc_la_SOURCES += cache.h cache.cc
 libdatasrc_la_SOURCES += rbnode_rrset.h
 libdatasrc_la_SOURCES += rbtree.h
 libdatasrc_la_SOURCES += zonetable.h zonetable.cc
-libdatasrc_la_SOURCES += zone.h
+libdatasrc_la_SOURCES += zone.h zone_finder_context.cc
 libdatasrc_la_SOURCES += result.h
 libdatasrc_la_SOURCES += logger.h logger.cc
 libdatasrc_la_SOURCES += client.h iterator.h
@@ -35,6 +35,7 @@ nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
 pkglib_LTLIBRARIES =  sqlite3_ds.la memory_ds.la
 
 sqlite3_ds_la_SOURCES = sqlite3_accessor.h sqlite3_accessor.cc
+sqlite3_ds_la_SOURCES += sqlite3_accessor_link.cc
 sqlite3_ds_la_LDFLAGS = -module
 sqlite3_ds_la_LDFLAGS += -no-undefined -version-info 1:0:0
 sqlite3_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
@@ -42,6 +43,7 @@ sqlite3_ds_la_LIBADD += libdatasrc.la
 sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
 
 memory_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc
+memory_ds_la_SOURCES += memory_datasrc_link.cc
 memory_ds_la_LDFLAGS = -module
 memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 memory_ds_la_LIBADD += libdatasrc.la
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 139576a..4e2fb15 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -396,15 +396,18 @@ DatabaseClient::Finder::findNSECCover(const Name& name) {
     return (ConstRRsetPtr());
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 DatabaseClient::Finder::findAll(const isc::dns::Name& name,
                                 std::vector<isc::dns::ConstRRsetPtr>& target,
                                 const FindOptions options)
 {
-    return (findInternal(name, RRType::ANY(), &target, options));
+    return (ZoneFinderContextPtr(new Context(*this, options,
+                                             findInternal(name, RRType::ANY(),
+                                                          &target, options),
+                                             target)));
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 DatabaseClient::Finder::find(const isc::dns::Name& name,
                              const isc::dns::RRType& type,
                              const FindOptions options)
@@ -412,7 +415,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     if (type == RRType::ANY()) {
         isc_throw(isc::Unexpected, "Use findAll to answer ANY");
     }
-    return (findInternal(name, type, NULL, options));
+    return (ZoneFinderContextPtr(new Context(*this, options,
+                                             findInternal(name, type,
+                                                          NULL, options))));
 }
 
 DatabaseClient::Finder::DelegationSearchResult
@@ -573,7 +578,7 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
 // covering NSEC record.
 //
 // If none of the above applies in any level, the search fails with NXDOMAIN.
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::findWildcardMatch(
     const isc::dns::Name& name, const isc::dns::RRType& type,
     const FindOptions options, const DelegationSearchResult& dresult,
@@ -616,8 +621,7 @@ DatabaseClient::Finder::findWildcardMatch(
                           DATASRC_DATABASE_WILDCARD_CANCEL_NS).
                     arg(accessor_->getDBName()).arg(wildcard).
                     arg(dresult.first_ns->getName());
-                return (FindResult(DELEGATION, dresult.first_ns));
-
+                return (ResultContext(DELEGATION, dresult.first_ns));
             } else if (!hasSubdomains(name.split(i - 1).toText())) {
                 // The wildcard match is the best one, find the final result
                 // at it.  Note that wildcard should never be the zone origin.
@@ -630,7 +634,7 @@ DatabaseClient::Finder::findWildcardMatch(
                           DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
                     arg(accessor_->getDBName()).arg(wildcard).
                     arg(name).arg(superdomain);
-                return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+                return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
             }
 
         } else if (hasSubdomains(wildcard)) {
@@ -641,19 +645,20 @@ DatabaseClient::Finder::findWildcardMatch(
             if ((options & FIND_DNSSEC) != 0) {
                 ConstRRsetPtr nsec = findNSECCover(Name(wildcard));
                 if (nsec) {
-                    return (FindResult(NXRRSET, nsec,
-                                       RESULT_WILDCARD | RESULT_NSEC_SIGNED));
+                    return (ResultContext(NXRRSET, nsec,
+                                          RESULT_WILDCARD |
+                                          RESULT_NSEC_SIGNED));
                 }
             }
-            return (FindResult(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
+            return (ResultContext(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
         }
     }
 
     // Nothing found at any level.
-    return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+    return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
 }
 
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::logAndCreateResult(
     const Name& name, const string* wildname, const RRType& type,
     ZoneFinder::Result code, ConstRRsetPtr rrset,
@@ -680,10 +685,10 @@ DatabaseClient::Finder::logAndCreateResult(
                 arg(getClass()).arg(*wildname);
         }
     }
-    return (ZoneFinder::FindResult(code, rrset, flags));
+    return (ResultContext(code, rrset, flags));
 }
 
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::findOnNameResult(const Name& name,
                                          const RRType& type,
                                          const FindOptions options,
@@ -799,7 +804,7 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                                DATASRC_DATABASE_FOUND_NXRRSET, flags));
 }
 
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
                                          FindOptions options,
                                          const DelegationSearchResult& dresult,
@@ -821,17 +826,17 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
             arg(accessor_->getDBName()).arg(name);
         const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
             ConstRRsetPtr();
-        return (FindResult(NXRRSET, nsec,
-                           nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+        return (ResultContext(NXRRSET, nsec,
+                              nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
     } else if ((options & NO_WILDCARD) == 0) {
         // It's not an empty non-terminal and wildcard matching is not
         // disabled, so check for wildcards. If there is a wildcard match
         // (i.e. all results except NXDOMAIN) return it; otherwise fall
         // through to the NXDOMAIN case below.
-        const ZoneFinder::FindResult wresult =
+        const ResultContext wcontext =
             findWildcardMatch(name, type, options, dresult, target);
-        if (wresult.code != NXDOMAIN) {
-            return (wresult);
+        if (wcontext.code != NXDOMAIN) {
+            return (wcontext);
         }
     }
 
@@ -841,11 +846,11 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
               arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
     const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
         ConstRRsetPtr();
-    return (FindResult(NXDOMAIN, nsec,
-                       nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+    return (ResultContext(NXDOMAIN, nsec,
+                          nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
 }
 
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
                                      std::vector<ConstRRsetPtr>* target,
                                      const FindOptions options)
@@ -860,7 +865,7 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
         name.compare(getOrigin()).getRelation();
     if (reln != NameComparisonResult::SUBDOMAIN &&
         reln != NameComparisonResult::EQUAL) {
-        return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+        return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
     }
 
     // First, go through all superdomains from the origin down, searching for
@@ -877,7 +882,7 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
     const DelegationSearchResult dresult = findDelegationPoint(name, options);
     if (dresult.rrset) {
         // In this case no special flags are needed.
-        return (FindResult(dresult.code, dresult.rrset));
+        return (ResultContext(dresult.code, dresult.rrset));
     }
 
     // If there is no delegation, look for the exact match to the request
@@ -975,7 +980,7 @@ public:
         // Find the SOA of the zone (may or may not succeed).  Note that
         // this must be done before starting the iteration context.
         soa_ = DatabaseClient::Finder(accessor_, zone.second, zone_name).
-            find(zone_name, RRType::SOA()).rrset;
+            find(zone_name, RRType::SOA())->rrset;
 
         // Request the context
         context_ = accessor_->getAllRecords(zone.second);
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 4d58401..afd3efb 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -26,7 +26,7 @@
 
 #include <datasrc/data_source.h>
 #include <datasrc/client.h>
-#include <datasrc/client.h>
+#include <datasrc/zone.h>
 #include <datasrc/logger.h>
 
 #include <dns/name.h>
@@ -738,17 +738,19 @@ public:
         /// \param type The RRType to find
         /// \param options Options about how to search.
         ///     See ZoneFinder::FindOptions.
-        virtual FindResult find(const isc::dns::Name& name,
-                                const isc::dns::RRType& type,
-                                const FindOptions options = FIND_DEFAULT);
+        virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+                                          const isc::dns::RRType& type,
+                                          const FindOptions options =
+                                          FIND_DEFAULT);
         /// \brief Implementation of the ZoneFinder::findAll method.
         ///
         /// In short, it is mostly the same thing as find, but it returns all
         /// RRsets in the named node through the target parameter in successful
         /// case. It acts the same in the unsuccessful one.
-        virtual FindResult findAll(const isc::dns::Name& name,
-                                   std::vector<isc::dns::ConstRRsetPtr>& target,
-                                   const FindOptions options = FIND_DEFAULT);
+        virtual ZoneFinderContextPtr findAll(
+            const isc::dns::Name& name,
+            std::vector<isc::dns::ConstRRsetPtr>& target,
+            const FindOptions options = FIND_DEFAULT);
 
         /// \brief Implementation of ZoneFinder::findPreviousName method.
         virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
@@ -785,6 +787,7 @@ public:
             FoundRRsets;
         /// \brief Just shortcut for set of types
         typedef std::set<dns::RRType> WantedTypes;
+
         /// \brief Internal logit of find and findAll methods.
         ///
         /// Most of their handling is in the "error" cases and delegations
@@ -794,10 +797,12 @@ public:
         /// Parameters and behaviour is like of those combined together.
         /// Unexpected parameters, like type != ANY and having the target, are
         /// just that - unexpected and not checked.
-        FindResult findInternal(const isc::dns::Name& name,
-                                const isc::dns::RRType& type,
-                                std::vector<isc::dns::ConstRRsetPtr>* target,
-                                const FindOptions options = FIND_DEFAULT);
+        ResultContext findInternal(const isc::dns::Name& name,
+                                   const isc::dns::RRType& type,
+                                   std::vector<isc::dns::ConstRRsetPtr>*
+                                   target,
+                                   const FindOptions options = FIND_DEFAULT);
+
         /// \brief Searches database for RRsets of one domain.
         ///
         /// This method scans RRs of single domain specified by name and
@@ -942,9 +947,10 @@ public:
         ///         success due to an exact match).  Also returned if there
         ///         is no match is an indication as to whether there was an
         ///         NXDOMAIN or an NXRRSET.
-        FindResult findWildcardMatch(
+        ResultContext findWildcardMatch(
             const isc::dns::Name& name,
-            const isc::dns::RRType& type, const FindOptions options,
+            const isc::dns::RRType& type,
+            const FindOptions options,
             const DelegationSearchResult& dresult,
             std::vector<isc::dns::ConstRRsetPtr>* target);
 
@@ -986,13 +992,14 @@ public:
         ///         the above 4 cases).  The return value is intended to be
         ///         usable as a return value of the caller of this helper
         ///         method.
-        FindResult findOnNameResult(const isc::dns::Name& name,
-				    const isc::dns::RRType& type,
-				    const FindOptions options,
-				    const bool is_origin,
-				    const FoundRRsets& found,
-				    const std::string* wildname,
-                    std::vector<isc::dns::ConstRRsetPtr>* target);
+        ResultContext findOnNameResult(const isc::dns::Name& name,
+                                       const isc::dns::RRType& type,
+                                       const FindOptions options,
+                                       const bool is_origin,
+                                       const FoundRRsets& found,
+                                       const std::string* wildname,
+                                       std::vector<isc::dns::ConstRRsetPtr>*
+                                       target);
 
         /// \brief Handle no match for name
         ///
@@ -1023,12 +1030,12 @@ public:
         ///         indicating the match type (e.g. CNAME at the wildcard
         ///         match, no RRs of the requested type at the wildcard,
         ///         success due to an exact match).
-        FindResult findNoNameResult(const isc::dns::Name& name,
-                                    const isc::dns::RRType& type,
-                                    FindOptions options,
-                                    const DelegationSearchResult& dresult,
-                                    std::vector<isc::dns::ConstRRsetPtr>*
-                                    target);
+        ResultContext findNoNameResult(const isc::dns::Name& name,
+                                       const isc::dns::RRType& type,
+                                       FindOptions options,
+                                       const DelegationSearchResult& dresult,
+                                       std::vector<isc::dns::ConstRRsetPtr>*
+                                       target);
 
         /// Logs condition and creates result
         ///
@@ -1051,13 +1058,13 @@ public:
         ///
         /// \return FindResult object constructed from the code and rrset
         ///         arguments.
-        FindResult logAndCreateResult(const isc::dns::Name& name,
-				      const std::string* wildname,
-                                      const isc::dns::RRType& type,
-                                      ZoneFinder::Result code,
-                                      isc::dns::ConstRRsetPtr rrset,
-                                      const isc::log::MessageID& log_id,
-                                      FindResultFlags flags) const;
+        ResultContext logAndCreateResult(const isc::dns::Name& name,
+                                         const std::string* wildname,
+                                         const isc::dns::RRType& type,
+                                         ZoneFinder::Result code,
+                                         isc::dns::ConstRRsetPtr rrset,
+                                         const isc::log::MessageID& log_id,
+                                         FindResultFlags flags) const;
 
         /// \brief Checks if something lives below this domain.
         ///
@@ -1150,3 +1157,7 @@ private:
 }
 
 #endif  // __DATABASE_DATASRC_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index e4700c2..33ab82f 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -21,7 +21,6 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/bind.hpp>
-#include <boost/foreach.hpp>
 
 #include <exceptions/exceptions.h>
 
@@ -40,12 +39,9 @@
 #include <datasrc/data_source.h>
 #include <datasrc/factory.h>
 
-#include <cc/data.h>
-
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
-using namespace isc::data;
 using boost::scoped_ptr;
 
 namespace isc {
@@ -651,12 +647,12 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         }
     }
 
-    // Set up FindResult object as a return value of find(), taking into
+    // Set up FindContext object as a return value of find(), taking into
     // account wildcard matches and DNSSEC information.  We set the NSEC/NSEC3
     // flag when applicable regardless of the find option; the caller would
     // simply ignore these when they didn't request DNSSEC related results.
-    FindResult createFindResult(Result code, ConstRRsetPtr rrset,
-                                bool wild) const
+    ResultContext createFindResult(Result code, ConstRRsetPtr rrset,
+                                   bool wild = false) const
     {
         FindResultFlags flags = RESULT_DEFAULT;
         if (wild) {
@@ -666,13 +662,13 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             zone_data_->nsec3_data_) {
             flags = flags | RESULT_NSEC3_SIGNED;
         }
-        return (FindResult(code, rrset, flags));
+        return (ZoneFinder::ResultContext(code, rrset, flags));
     }
 
     // Implementation of InMemoryZoneFinder::find
-    FindResult find(const Name& name, RRType type,
-                    std::vector<ConstRRsetPtr>* target,
-                    const FindOptions options) const
+    ZoneFinder::ResultContext find(const Name& name, RRType type,
+                                   std::vector<ConstRRsetPtr>* target,
+                                   const FindOptions options) const
     {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
             arg(type);
@@ -707,15 +703,16 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                         arg(state.rrset_->getName());
                     // We were traversing a DNAME node (and wanted to go
                     // lower below it), so return the DNAME
-                    return (FindResult(DNAME, prepareRRset(name, state.rrset_,
-                                                           false, options)));
+                    return (createFindResult(DNAME,
+                                             prepareRRset(name, state.rrset_,
+                                                          false, options)));
                 }
                 if (state.zonecut_node_ != NULL) {
                     LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
                         arg(state.rrset_->getName());
-                    return (FindResult(DELEGATION,
-                                       prepareRRset(name, state.rrset_,
-                                                    false, options)));
+                    return (createFindResult(DELEGATION,
+                                             prepareRRset(name, state.rrset_,
+                                                          false, options)));
                 }
 
                 // If the RBTree search stopped at a node for a super domain
@@ -725,7 +722,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                     NameComparisonResult::SUPERDOMAIN) {
                     LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).
                         arg(name);
-                    return (createFindResult(NXRRSET, ConstRRsetPtr(), false));
+                    return (createFindResult(NXRRSET, ConstRRsetPtr()));
                 }
 
                 /*
@@ -818,9 +815,9 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             if (found != node->getData()->end()) {
                 LOG_DEBUG(logger, DBG_TRACE_DATA,
                           DATASRC_MEM_EXACT_DELEGATION).arg(name);
-                return (FindResult(DELEGATION,
-                                   prepareRRset(name, found->second, rename,
-                                                options)));
+                return (createFindResult(DELEGATION,
+                                         prepareRRset(name, found->second,
+                                                      rename, options)));
             }
         }
 
@@ -853,9 +850,9 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             if (found != node->getData()->end()) {
                 LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
                 return (createFindResult(CNAME,
-                                         prepareRRset(name, found->second,
-                                                      rename, options),
-                                         rename));
+                                          prepareRRset(name, found->second,
+                                                       rename, options),
+                                          rename));
             }
         }
         // No exact match or CNAME.  Return NXRRSET.
@@ -888,19 +885,24 @@ InMemoryZoneFinder::getClass() const {
     return (impl_->zone_class_);
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 InMemoryZoneFinder::find(const Name& name, const RRType& type,
-                 const FindOptions options)
+                         const FindOptions options)
 {
-    return (impl_->find(name, type, NULL, options));
+    return (ZoneFinderContextPtr(
+                new Context(*this, options,
+                            impl_->find(name, type, NULL, options))));
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 InMemoryZoneFinder::findAll(const Name& name,
                             std::vector<ConstRRsetPtr>& target,
                             const FindOptions options)
 {
-    return (impl_->find(name, RRType::ANY(), &target, options));
+    return (ZoneFinderContextPtr(
+                new Context(*this, options, impl_->find(name, RRType::ANY(),
+                                                        &target, options),
+                            target)));
 }
 
 ZoneFinder::FindNSEC3Result
@@ -1221,146 +1223,5 @@ InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
               "in memory data source");
 }
 
-namespace {
-// convencience function to add an error message to a list of those
-// (TODO: move functions like these to some util lib?)
-void
-addError(ElementPtr errors, const std::string& error) {
-    if (errors != ElementPtr() && errors->getType() == Element::list) {
-        errors->add(Element::create(error));
-    }
-}
-
-/// Check if the given element exists in the map, and if it is a string
-bool
-checkConfigElementString(ConstElementPtr config, const std::string& name,
-                         ElementPtr errors)
-{
-    if (!config->contains(name)) {
-        addError(errors,
-                 "Config for memory backend does not contain a '"
-                 +name+
-                 "' value");
-        return false;
-    } else if (!config->get(name) ||
-               config->get(name)->getType() != Element::string) {
-        addError(errors, "value of " + name +
-                 " in memory backend config is not a string");
-        return false;
-    } else {
-        return true;
-    }
-}
-
-bool
-checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
-    bool result = true;
-    if (!config || config->getType() != Element::map) {
-        addError(errors, "Elements in memory backend's zone list must be maps");
-        result = false;
-    } else {
-        if (!checkConfigElementString(config, "origin", errors)) {
-            result = false;
-        }
-        if (!checkConfigElementString(config, "file", errors)) {
-            result = false;
-        }
-        // we could add some existence/readabilty/parsability checks here
-        // if we want
-    }
-    return result;
-}
-
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
-    /* Specific configuration is under discussion, right now this accepts
-     * the 'old' configuration, see [TODO]
-     * So for memory datasource, we get a structure like this:
-     * { "type": string ("memory"),
-     *   "class": string ("IN"/"CH"/etc),
-     *   "zones": list
-     * }
-     * Zones list is a list of maps:
-     * { "origin": string,
-     *     "file": string
-     * }
-     *
-     * At this moment we cannot be completely sure of the contents of the
-     * structure, so we have to do some more extensive tests than should
-     * strictly be necessary (e.g. existence and type of elements)
-     */
-    bool result = true;
-
-    if (!config || config->getType() != Element::map) {
-        addError(errors, "Base config for memory backend must be a map");
-        result = false;
-    } else {
-        if (!checkConfigElementString(config, "type", errors)) {
-            result = false;
-        } else {
-            if (config->get("type")->stringValue() != "memory") {
-                addError(errors,
-                         "Config for memory backend is not of type \"memory\"");
-                result = false;
-            }
-        }
-        if (!checkConfigElementString(config, "class", errors)) {
-            result = false;
-        } else {
-            try {
-                RRClass rrc(config->get("class")->stringValue());
-            } catch (const isc::Exception& rrce) {
-                addError(errors,
-                         "Error parsing class config for memory backend: " +
-                         std::string(rrce.what()));
-                result = false;
-            }
-        }
-        if (!config->contains("zones")) {
-            addError(errors, "No 'zones' element in memory backend config");
-            result = false;
-        } else if (!config->get("zones") ||
-                   config->get("zones")->getType() != Element::list) {
-            addError(errors, "'zones' element in memory backend config is not a list");
-            result = false;
-        } else {
-            BOOST_FOREACH(ConstElementPtr zone_config,
-                          config->get("zones")->listValue()) {
-                if (!checkZoneConfig(zone_config, errors)) {
-                    result = false;
-                }
-            }
-        }
-    }
-
-    return (result);
-}
-
-} // end anonymous namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
-    ElementPtr errors(Element::createList());
-    if (!checkConfig(config, errors)) {
-        error = "Configuration error: " + errors->str();
-        return (NULL);
-    }
-    try {
-        return (new InMemoryClient());
-    } catch (const std::exception& exc) {
-        error = std::string("Error creating memory datasource: ") + exc.what();
-        return (NULL);
-    } catch (...) {
-        error = std::string("Error creating memory datasource, "
-                            "unknown exception");
-        return (NULL);
-    }
-}
-
-void destroyInstance(DataSourceClient* instance) {
-    delete instance;
-}
-
-
 } // end of namespace datasrc
 } // end of namespace isc
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index b960ab9..9ee6747 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -70,18 +70,20 @@ public:
     /// See documentation in \c Zone.
     ///
     /// It returns NULL pointer in case of NXDOMAIN and NXRRSET.
-    virtual FindResult find(const isc::dns::Name& name,
-                            const isc::dns::RRType& type,
-                            const FindOptions options = FIND_DEFAULT);
+    virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+                                      const isc::dns::RRType& type,
+                                      const FindOptions options =
+                                      FIND_DEFAULT);
 
     /// \brief Version of find that returns all types at once
     ///
     /// It acts the same as find, just that when the correct node is found,
     /// all the RRsets are filled into the target parameter instead of being
     /// returned by the result.
-    virtual FindResult findAll(const isc::dns::Name& name,
-                               std::vector<isc::dns::ConstRRsetPtr>& target,
-                               const FindOptions options = FIND_DEFAULT);
+    virtual ZoneFinderContextPtr findAll(
+        const isc::dns::Name& name,
+        std::vector<isc::dns::ConstRRsetPtr>& target,
+        const FindOptions options = FIND_DEFAULT);
 
     /// Look for NSEC3 for proving (non)existence of given name.
     ///
diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc
new file mode 100644
index 0000000..a0b4bf6
--- /dev/null
+++ b/src/lib/datasrc/memory_datasrc_link.cc
@@ -0,0 +1,173 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+
+#include <dns/rrclass.h>
+
+#include <datasrc/client.h>
+#include <datasrc/memory_datasrc.h>
+
+#include <boost/foreach.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+// convencience function to add an error message to a list of those
+// (TODO: move functions like these to some util lib?)
+void
+addError(ElementPtr errors, const std::string& error) {
+    if (errors != ElementPtr() && errors->getType() == Element::list) {
+        errors->add(Element::create(error));
+    }
+}
+
+/// Check if the given element exists in the map, and if it is a string
+bool
+checkConfigElementString(ConstElementPtr config, const std::string& name,
+                         ElementPtr errors)
+{
+    if (!config->contains(name)) {
+        addError(errors,
+                 "Config for memory backend does not contain a '"
+                 +name+
+                 "' value");
+        return false;
+    } else if (!config->get(name) ||
+               config->get(name)->getType() != Element::string) {
+        addError(errors, "value of " + name +
+                 " in memory backend config is not a string");
+        return false;
+    } else {
+        return true;
+    }
+}
+
+bool
+checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
+    bool result = true;
+    if (!config || config->getType() != Element::map) {
+        addError(errors, "Elements in memory backend's zone list must be maps");
+        result = false;
+    } else {
+        if (!checkConfigElementString(config, "origin", errors)) {
+            result = false;
+        }
+        if (!checkConfigElementString(config, "file", errors)) {
+            result = false;
+        }
+        // we could add some existence/readabilty/parsability checks here
+        // if we want
+    }
+    return result;
+}
+
+bool
+checkConfig(ConstElementPtr config, ElementPtr errors) {
+    /* Specific configuration is under discussion, right now this accepts
+     * the 'old' configuration, see [TODO]
+     * So for memory datasource, we get a structure like this:
+     * { "type": string ("memory"),
+     *   "class": string ("IN"/"CH"/etc),
+     *   "zones": list
+     * }
+     * Zones list is a list of maps:
+     * { "origin": string,
+     *     "file": string
+     * }
+     *
+     * At this moment we cannot be completely sure of the contents of the
+     * structure, so we have to do some more extensive tests than should
+     * strictly be necessary (e.g. existence and type of elements)
+     */
+    bool result = true;
+
+    if (!config || config->getType() != Element::map) {
+        addError(errors, "Base config for memory backend must be a map");
+        result = false;
+    } else {
+        if (!checkConfigElementString(config, "type", errors)) {
+            result = false;
+        } else {
+            if (config->get("type")->stringValue() != "memory") {
+                addError(errors,
+                         "Config for memory backend is not of type \"memory\"");
+                result = false;
+            }
+        }
+        if (!checkConfigElementString(config, "class", errors)) {
+            result = false;
+        } else {
+            try {
+                RRClass rrc(config->get("class")->stringValue());
+            } catch (const isc::Exception& rrce) {
+                addError(errors,
+                         "Error parsing class config for memory backend: " +
+                         std::string(rrce.what()));
+                result = false;
+            }
+        }
+        if (!config->contains("zones")) {
+            addError(errors, "No 'zones' element in memory backend config");
+            result = false;
+        } else if (!config->get("zones") ||
+                   config->get("zones")->getType() != Element::list) {
+            addError(errors, "'zones' element in memory backend config is not a list");
+            result = false;
+        } else {
+            BOOST_FOREACH(ConstElementPtr zone_config,
+                          config->get("zones")->listValue()) {
+                if (!checkZoneConfig(zone_config, errors)) {
+                    result = false;
+                }
+            }
+        }
+    }
+
+    return (result);
+}
+
+} // end unnamed namespace
+
+DataSourceClient *
+createInstance(isc::data::ConstElementPtr config, std::string& error) {
+    ElementPtr errors(Element::createList());
+    if (!checkConfig(config, errors)) {
+        error = "Configuration error: " + errors->str();
+        return (NULL);
+    }
+    try {
+        return (new isc::datasrc::InMemoryClient());
+    } catch (const std::exception& exc) {
+        error = std::string("Error creating memory datasource: ") + exc.what();
+        return (NULL);
+    } catch (...) {
+        error = std::string("Error creating memory datasource, "
+                            "unknown exception");
+        return (NULL);
+    }
+}
+
+void destroyInstance(DataSourceClient* instance) {
+    delete instance;
+}
+
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index fb2ffef..a0afb7f 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -17,8 +17,6 @@
 #include <string>
 #include <vector>
 
-#include <boost/foreach.hpp>
-
 #include <datasrc/sqlite3_accessor.h>
 #include <datasrc/logger.h>
 #include <datasrc/data_source.h>
@@ -31,8 +29,6 @@ using namespace isc::data;
 
 #define SQLITE_SCHEMA_VERSION 1
 
-#define CONFIG_ITEM_DATABASE_FILE "database_file"
-
 namespace isc {
 namespace datasrc {
 
@@ -1096,75 +1092,5 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
     return (result);
 }
 
-namespace {
-void
-addError(ElementPtr errors, const std::string& error) {
-    if (errors != ElementPtr() && errors->getType() == Element::list) {
-        errors->add(Element::create(error));
-    }
-}
-
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
-    /* Specific configuration is under discussion, right now this accepts
-     * the 'old' configuration, see header file
-     */
-    bool result = true;
-
-    if (!config || config->getType() != Element::map) {
-        addError(errors, "Base config for SQlite3 backend must be a map");
-        result = false;
-    } else {
-        if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) {
-            addError(errors,
-                     "Config for SQlite3 backend does not contain a '"
-                     CONFIG_ITEM_DATABASE_FILE
-                     "' value");
-            result = false;
-        } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) ||
-                   config->get(CONFIG_ITEM_DATABASE_FILE)->getType() !=
-                   Element::string) {
-            addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE
-                     " in SQLite3 backend is not a string");
-            result = false;
-        } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() ==
-                   "") {
-            addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE
-                     " in SQLite3 backend is empty");
-            result = false;
-        }
-    }
-
-    return (result);
-}
-
-} // end anonymous namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
-    ElementPtr errors(Element::createList());
-    if (!checkConfig(config, errors)) {
-        error = "Configuration error: " + errors->str();
-        return (NULL);
-    }
-    std::string dbfile = config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue();
-    try {
-        boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
-            new SQLite3Accessor(dbfile, "IN")); // XXX: avoid hardcode RR class
-        return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor));
-    } catch (const std::exception& exc) {
-        error = std::string("Error creating sqlite3 datasource: ") + exc.what();
-        return (NULL);
-    } catch (...) {
-        error = std::string("Error creating sqlite3 datasource, "
-                            "unknown exception");
-        return (NULL);
-    }
-}
-
-void destroyInstance(DataSourceClient* instance) {
-    delete instance;
-}
-
 } // end of namespace datasrc
 } // end of namespace isc
diff --git a/src/lib/datasrc/sqlite3_accessor_link.cc b/src/lib/datasrc/sqlite3_accessor_link.cc
new file mode 100644
index 0000000..81ac6b5
--- /dev/null
+++ b/src/lib/datasrc/sqlite3_accessor_link.cc
@@ -0,0 +1,105 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+
+#include <dns/rrclass.h>
+
+#include <datasrc/sqlite3_accessor.h>
+#include <datasrc/database.h>
+
+#include <string>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+
+const char* const CONFIG_ITEM_DATABASE_FILE = "database_file";
+
+void
+addError(ElementPtr errors, const std::string& error) {
+    if (errors != ElementPtr() && errors->getType() == Element::list) {
+        errors->add(Element::create(error));
+    }
+}
+
+bool
+checkConfig(ConstElementPtr config, ElementPtr errors) {
+    /* Specific configuration is under discussion, right now this accepts
+     * the 'old' configuration, see header file
+     */
+    bool result = true;
+
+    if (!config || config->getType() != Element::map) {
+        addError(errors, "Base config for SQlite3 backend must be a map");
+        result = false;
+    } else {
+        if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) {
+            addError(errors,
+                     "Config for SQlite3 backend does not contain a '" +
+                     string(CONFIG_ITEM_DATABASE_FILE) +
+                     "' value");
+            result = false;
+        } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) ||
+                   config->get(CONFIG_ITEM_DATABASE_FILE)->getType() !=
+                   Element::string) {
+            addError(errors, "value of " + string(CONFIG_ITEM_DATABASE_FILE) +
+                     " in SQLite3 backend is not a string");
+            result = false;
+        } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() ==
+                   "") {
+            addError(errors, "value of " + string(CONFIG_ITEM_DATABASE_FILE) +
+                     " in SQLite3 backend is empty");
+            result = false;
+        }
+    }
+
+    return (result);
+}
+
+} // end unnamed namespace
+
+DataSourceClient *
+createInstance(isc::data::ConstElementPtr config, std::string& error) {
+    ElementPtr errors(Element::createList());
+    if (!checkConfig(config, errors)) {
+        error = "Configuration error: " + errors->str();
+        return (NULL);
+    }
+    std::string dbfile = config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue();
+    try {
+        boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
+            new SQLite3Accessor(dbfile, "IN")); // XXX: avoid hardcode RR class
+        return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor));
+    } catch (const std::exception& exc) {
+        error = std::string("Error creating sqlite3 datasource: ") + exc.what();
+        return (NULL);
+    } catch (...) {
+        error = std::string("Error creating sqlite3 datasource, "
+                            "unknown exception");
+        return (NULL);
+    }
+}
+
+void destroyInstance(DataSourceClient* instance) {
+    delete instance;
+}
+
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index 8eaf9ab..c8ffa58 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -20,17 +20,13 @@ CLEANFILES = *.gcno *.gcda
 TESTS =
 noinst_PROGRAMS =
 if HAVE_GTEST
-TESTS += run_unittests run_unittests_sqlite3 run_unittests_memory
+TESTS += run_unittests
 
-#
-# For each specific datasource, there is a separate binary that includes
-# the code itself (we can't unittest through the public API). These need
-# to be separate because the included code, by design, contains conflicting
-# symbols.
-# We also have a 'general' run_unittests with non-datasource-specific tests
-#
+# We have two sets of tests: the general tests and factory tests (see below
+# for the latter).  They are separate binary files sharing some program files
+# and libraries.
 
-# First define the parts shared by all
+# First define the parts shared by both
 common_sources = run_unittests.cc
 common_sources += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 common_sources += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
@@ -46,7 +42,6 @@ common_ldadd += $(top_builddir)/src/lib/cc/libcc.la
 common_ldadd += $(top_builddir)/src/lib/testutils/libtestutils.la
 common_ldadd += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 
-
 # The general tests
 run_unittests_SOURCES = $(common_sources)
 run_unittests_SOURCES += datasrc_unittest.cc
@@ -57,36 +52,23 @@ run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
 run_unittests_SOURCES += rbtree_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
 run_unittests_SOURCES += client_unittest.cc
+run_unittests_SOURCES += database_unittest.cc
+run_unittests_SOURCES += sqlite3_unittest.cc
+run_unittests_SOURCES += sqlite3_accessor_unittest.cc
+run_unittests_SOURCES += memory_datasrc_unittest.cc
+run_unittests_SOURCES += rbnode_rrset_unittest.cc
+run_unittests_SOURCES += zone_finder_context_unittest.cc
+
+# We need the actual module implementation in the tests (they are not part
+# of libdatasrc)
+run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
+run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
 run_unittests_LDADD = $(common_ldadd)
 
-
-# SQlite3 datasource tests
-run_unittests_sqlite3_SOURCES = $(common_sources)
-run_unittests_sqlite3_SOURCES += database_unittest.cc
-run_unittests_sqlite3_SOURCES += sqlite3_unittest.cc
-run_unittests_sqlite3_SOURCES += sqlite3_accessor_unittest.cc
-run_unittests_sqlite3_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
-
-run_unittests_sqlite3_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_sqlite3_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
-run_unittests_sqlite3_LDADD = $(common_ldadd)
-
-# In-memory datasource tests
-run_unittests_memory_SOURCES = $(common_sources)
-run_unittests_memory_SOURCES += memory_datasrc_unittest.cc
-run_unittests_memory_SOURCES += rbnode_rrset_unittest.cc
-run_unittests_memory_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
-
-run_unittests_memory_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_memory_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
-run_unittests_memory_LDADD = $(common_ldadd)
-
 noinst_PROGRAMS+= $(TESTS)
 
 # For the factory unit tests, we need to specify that we want
@@ -110,6 +92,7 @@ endif
 endif
 
 EXTRA_DIST =  testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/contexttest.zone
 EXTRA_DIST += testdata/diffs.sqlite3
 EXTRA_DIST += testdata/example2.com
 EXTRA_DIST += testdata/example2.com.sqlite3
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 408913a..758095b 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -1364,7 +1364,7 @@ TYPED_TEST(DatabaseClientTest, iterateThenUpdate) {
 
         // Confirm at least it doesn't contain any SOA
         EXPECT_EQ(ZoneFinder::NXDOMAIN,
-                  this->getFinder()->find(this->zname_, RRType::SOA()).code);
+                  this->getFinder()->find(this->zname_, RRType::SOA())->code);
     } catch (const DataSourceError&) {}
 
     ConstRRsetPtr rrset;
@@ -1422,31 +1422,31 @@ doFindTest(ZoneFinder& finder,
            const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
 {
     SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
-    const ZoneFinder::FindResult result = finder.find(name, type, options);
-    ASSERT_EQ(expected_result, result.code) << name << " " << type;
+    ConstZoneFinderContextPtr result = finder.find(name, type, options);
+    ASSERT_EQ(expected_result, result->code) << name << " " << type;
     EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-              result.isWildcard());
+              result->isWildcard());
     EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
-              result.isNSECSigned());
+              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 :
+              result->isNSEC3Signed());
+    if (!expected_rdatas.empty() && result->rrset) {
+        checkRRset(result->rrset, expected_name != Name(".") ? expected_name :
                    name, finder.getClass(), expected_type, expected_ttl,
                    expected_rdatas);
 
-        if (!expected_sig_rdatas.empty() && result.rrset->getRRsig()) {
-            checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ?
+        if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
+            checkRRset(result->rrset->getRRsig(), expected_name != 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());
+            EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig());
         } else {
             ADD_FAILURE() << "Missing RRSIG";
         }
     } else if (expected_rdatas.empty()) {
-        EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset);
+        EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset);
     } else {
         ADD_FAILURE() << "Missing result";
     }
@@ -1464,11 +1464,11 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
 {
     SCOPED_TRACE("All test for " + name.toText());
     std::vector<ConstRRsetPtr> target;
-    ZoneFinder::FindResult result(finder.findAll(name, target, options));
+    ConstZoneFinderContextPtr result(finder.findAll(name, target, options));
     EXPECT_TRUE(target.empty());
-    EXPECT_EQ(expected_result, result.code);
-    EXPECT_EQ(expected_type, result.rrset->getType());
-    RdataIteratorPtr it(result.rrset->getRdataIterator());
+    EXPECT_EQ(expected_result, result->code);
+    EXPECT_EQ(expected_type, result->rrset->getType());
+    RdataIteratorPtr it(result->rrset->getRdataIterator());
     std::vector<std::string> rdata;
     while (!it->isLast()) {
         rdata.push_back(it->getCurrent().toText());
@@ -1482,7 +1482,7 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
     }
     EXPECT_TRUE(expected_rdata == rdata);
     EXPECT_EQ(expected_name == isc::dns::Name::ROOT_NAME() ? name :
-              expected_name, result.rrset->getName());
+              expected_name, result->rrset->getName());
 }
 
 // When asking for an RRset where RRs somehow have different TTLs, it should 
@@ -1787,30 +1787,30 @@ TYPED_TEST(DatabaseClientTest, findOutOfZone) {
     doFindTest(*finder, Name("org"), this->qtype_, this->qtype_,
                this->rrttl_, ZoneFinder::NXDOMAIN,
                this->empty_rdatas_, this->empty_rdatas_);
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("org"), target).code);
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("org"), target)->code);
     // sharing a common ancestor
     doFindTest(*finder, Name("noexample.org"), this->qtype_, this->qtype_,
                this->rrttl_, ZoneFinder::NXDOMAIN,
                this->empty_rdatas_, this->empty_rdatas_);
     EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("noexample.org"),
-                                                    target).code);
+                                                    target)->code);
     // totally unrelated domain, smaller number of labels
     doFindTest(*finder, Name("com"), this->qtype_, this->qtype_,
                this->rrttl_, ZoneFinder::NXDOMAIN,
                this->empty_rdatas_, this->empty_rdatas_);
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("com"), target).code);
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("com"), target)->code);
     // totally unrelated domain, same number of labels
     doFindTest(*finder, Name("example.com"), this->qtype_, this->qtype_,
                this->rrttl_, ZoneFinder::NXDOMAIN,
                this->empty_rdatas_, this->empty_rdatas_);
     EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("example.com"),
-                                                    target).code);
+                                                    target)->code);
     // totally unrelated domain, larger number of labels
     doFindTest(*finder, Name("more.example.com"), this->qtype_, this->qtype_,
                this->rrttl_, ZoneFinder::NXDOMAIN,
                this->empty_rdatas_, this->empty_rdatas_);
     EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("more.example.com"),
-                                                    target).code);
+                                                    target)->code);
 }
 
 TYPED_TEST(DatabaseClientTest, findDelegation) {
@@ -2363,11 +2363,11 @@ TYPED_TEST(DatabaseClientTest, getAll) {
     std::vector<ConstRRsetPtr> target;
     EXPECT_EQ(ZoneFinder::NXDOMAIN,
               finder->findAll(isc::dns::Name("nothere.example.org."),
-                              target).code);
+                              target)->code);
     EXPECT_TRUE(target.empty());
     EXPECT_EQ(ZoneFinder::NXRRSET,
               finder->findAll(isc::dns::Name("here.wild.example.org."),
-                              target).code);
+                              target)->code);
     this->expected_rdatas_.push_back("ns.delegation.example.org.");
     this->expected_rdatas_.push_back("ns.example.com.");
     doFindAllTestResult(*finder, isc::dns::Name("xx.delegation.example.org."),
@@ -2388,7 +2388,7 @@ TYPED_TEST(DatabaseClientTest, getAll) {
     // It should get the data on success
     EXPECT_EQ(ZoneFinder::SUCCESS,
               finder->findAll(isc::dns::Name("www2.example.org."),
-                              target).code);
+                              target)->code);
     ASSERT_EQ(2, target.size());
     size_t a_idx(target[1]->getType() == RRType::A());
     EXPECT_EQ(RRType::A(), target[a_idx]->getType());
@@ -2412,13 +2412,13 @@ TYPED_TEST(DatabaseClientTest, getAll) {
 
     // And on wildcard. Check the signatures as well.
     target.clear();
-    const ZoneFinder::FindResult result =
+    ConstZoneFinderContextPtr result =
         finder->findAll(Name("a.wild.example.org"), target,
                         ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
-    EXPECT_TRUE(result.isWildcard());
-    EXPECT_TRUE(result.isNSECSigned());
-    EXPECT_FALSE(result.isNSEC3Signed());
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+    EXPECT_TRUE(result->isWildcard());
+    EXPECT_TRUE(result->isNSECSigned());
+    EXPECT_FALSE(result->isNSEC3Signed());
     ASSERT_EQ(2, target.size());
     a_idx = target[1]->getType() == RRType::A();
     EXPECT_EQ(RRType::A(), target[a_idx]->getType());
@@ -2496,22 +2496,22 @@ TYPED_TEST(DatabaseClientTest, flushZone) {
 
     // Before update, the name exists.
     EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
-                                                this->qtype_).code);
+                                                this->qtype_)->code);
 
     // start update in the replace mode.  the normal finder should still
     // be able to see the record, but the updater's finder shouldn't.
     this->updater_ = this->client_->getUpdater(this->zname_, true);
     this->setUpdateAccessor();
     EXPECT_EQ(ZoneFinder::SUCCESS,
-              finder->find(this->qname_, this->qtype_).code);
+              finder->find(this->qname_, this->qtype_)->code);
     EXPECT_EQ(ZoneFinder::NXDOMAIN,
               this->updater_->getFinder().find(this->qname_,
-                                               this->qtype_).code);
+                                               this->qtype_)->code);
 
     // commit the update.  now the normal finder shouldn't see it.
     this->updater_->commit();
     EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(this->qname_,
-                                                 this->qtype_).code);
+                                                 this->qtype_)->code);
 
     // Check rollback wasn't accidentally performed.
     EXPECT_FALSE(this->isRollbacked());
@@ -2522,13 +2522,13 @@ TYPED_TEST(DatabaseClientTest, updateCancel) {
 
     ZoneFinderPtr finder = this->client_->findZone(this->zname_).zone_finder;
     EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
-                                                this->qtype_).code);
+                                                this->qtype_)->code);
 
     this->updater_ = this->client_->getUpdater(this->zname_, true);
     this->setUpdateAccessor();
     EXPECT_EQ(ZoneFinder::NXDOMAIN,
               this->updater_->getFinder().find(this->qname_,
-                                               this->qtype_).code);
+                                               this->qtype_)->code);
     // DB should not have been rolled back yet.
     EXPECT_FALSE(this->isRollbacked());
     this->updater_.reset();            // destruct without commit
@@ -2538,7 +2538,7 @@ TYPED_TEST(DatabaseClientTest, updateCancel) {
     // isRollbacked())
     EXPECT_TRUE(this->isRollbacked(true));
     EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
-                                                this->qtype_).code);
+                                                this->qtype_)->code);
 }
 
 TYPED_TEST(DatabaseClientTest, exceptionFromRollback) {
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index 92d3a9a..9096a9e 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -553,33 +553,33 @@ public:
         // The whole block is inside, because we need to check the result and
         // we can't assign to FindResult
         EXPECT_NO_THROW({
-                ZoneFinder::FindResult find_result(zone_finder->find(
-                                                       name, rrtype, options));
+                ZoneFinderContextPtr find_result(zone_finder->find(
+                                                     name, rrtype, options));
                 // Check it returns correct answers
-                EXPECT_EQ(result, find_result.code);
+                EXPECT_EQ(result, find_result->code);
                 EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-                          find_result.isWildcard());
+                          find_result->isWildcard());
                 EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
-                          != 0, find_result.isNSECSigned());
+                          != 0, find_result->isNSECSigned());
                 EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
-                          != 0, find_result.isNSEC3Signed());
+                          != 0, find_result->isNSEC3Signed());
                 if (check_answer) {
                     if (!answer) {
-                        ASSERT_FALSE(find_result.rrset);
+                        ASSERT_FALSE(find_result->rrset);
                     } else {
-                        ASSERT_TRUE(find_result.rrset);
-                        rrsetCheck(answer, find_result.rrset);
+                        ASSERT_TRUE(find_result->rrset);
+                        rrsetCheck(answer, find_result->rrset);
                         if (answer_sig) {
-                            ASSERT_TRUE(find_result.rrset->getRRsig());
+                            ASSERT_TRUE(find_result->rrset->getRRsig());
                             rrsetCheck(answer_sig,
-                                       find_result.rrset->getRRsig());
+                                       find_result->rrset->getRRsig());
                         }
                     }
                 } else if (check_wild_answer) {
                     ASSERT_NE(ConstRRsetPtr(), answer) <<
                         "Wrong test, don't check for wild names if you expect "
                         "empty answer";
-                    ASSERT_NE(ConstRRsetPtr(), find_result.rrset) <<
+                    ASSERT_NE(ConstRRsetPtr(), find_result->rrset) <<
                         "No answer found";
                     // Build the expected answer using the given name and
                     // other parameter of the base wildcard RRset.
@@ -590,11 +590,11 @@ public:
                     for (; !expectedIt->isLast(); expectedIt->next()) {
                         wildanswer->addRdata(expectedIt->getCurrent());
                     }
-                    rrsetCheck(wildanswer, find_result.rrset);
+                    rrsetCheck(wildanswer, find_result->rrset);
 
                     // Same for the RRSIG, if any.
                     if (answer_sig) {
-                        ASSERT_TRUE(find_result.rrset->getRRsig());
+                        ASSERT_TRUE(find_result->rrset->getRRsig());
 
                         RRsetPtr wildsig(new RRset(name,
                                                    answer_sig->getClass(),
@@ -605,7 +605,7 @@ public:
                         for (; !expectedIt->isLast(); expectedIt->next()) {
                             wildsig->addRdata(expectedIt->getCurrent());
                         }
-                        rrsetCheck(wildsig, find_result.rrset->getRRsig());
+                        rrsetCheck(wildsig, find_result->rrset->getRRsig());
                     }
                 }
             });
@@ -626,21 +626,21 @@ public:
             finder = &zone_finder_;
         }
         std::vector<ConstRRsetPtr> target;
-        ZoneFinder::FindResult find_result(finder->findAll(name, target,
-                                                           options));
-        EXPECT_EQ(result, find_result.code);
+        ZoneFinderContextPtr find_result(finder->findAll(name, target,
+                                                         options));
+        EXPECT_EQ(result, find_result->code);
         if (!rrset_result) {
-            EXPECT_FALSE(find_result.rrset);
+            EXPECT_FALSE(find_result->rrset);
         } else {
-            ASSERT_TRUE(find_result.rrset);
-            rrsetCheck(rrset_result, find_result.rrset);
+            ASSERT_TRUE(find_result->rrset);
+            rrsetCheck(rrset_result, find_result->rrset);
         }
         EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-                  find_result.isWildcard());
+                  find_result->isWildcard());
         EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
-                  != 0, find_result.isNSECSigned());
+                  != 0, find_result->isNSECSigned());
         EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
-                  != 0, find_result.isNSEC3Signed());
+                  != 0, find_result->isNSEC3Signed());
         rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
                     target.begin(), target.end());
     }
@@ -1552,37 +1552,35 @@ TEST_F(InMemoryZoneFinderTest, addRRsig) {
     // that covers the first RRset
     zone_finder_.add(rr_a_);
     zone_finder_.add(textToRRset(rrsig_a_txt));
-    ZoneFinder::FindResult result = zone_finder_.find(origin_, RRType::A(),
-                                                      ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
-    ASSERT_TRUE(result.rrset);
-    ASSERT_TRUE(result.rrset->getRRsig());
-    actual_rrsets_.push_back(result.rrset->getRRsig());
+    ZoneFinderContextPtr result = zone_finder_.find(origin_, RRType::A(),
+                                                    ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+    ASSERT_TRUE(result->rrset);
+    ASSERT_TRUE(result->rrset->getRRsig());
+    actual_rrsets_.push_back(result->rrset->getRRsig());
     rrsetsCheck(rrsig_a_txt, actual_rrsets_.begin(), actual_rrsets_.end());
 
     // Confirm a separate RRISG for a different type can be added
     actual_rrsets_.clear();
     zone_finder_.add(rr_ns_);
     zone_finder_.add(textToRRset(rrsig_ns_txt));
-    ZoneFinder::FindResult result2 =
-        zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::SUCCESS, result2.code);
-    ASSERT_TRUE(result2.rrset);
-    ASSERT_TRUE(result2.rrset->getRRsig());
-    actual_rrsets_.push_back(result2.rrset->getRRsig());
+    result = zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+    ASSERT_TRUE(result->rrset);
+    ASSERT_TRUE(result->rrset->getRRsig());
+    actual_rrsets_.push_back(result->rrset->getRRsig());
     rrsetsCheck(rrsig_ns_txt, actual_rrsets_.begin(), actual_rrsets_.end());
 
     // Check a case with multiple RRSIGs
     actual_rrsets_.clear();
     zone_finder_.add(rr_ns_aaaa_);
     zone_finder_.add(textToRRset(rrsig_aaaa_txt));
-    ZoneFinder::FindResult result3 =
-        zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
-                          ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::SUCCESS, result3.code);
-    ASSERT_TRUE(result3.rrset);
-    ASSERT_TRUE(result3.rrset->getRRsig());
-    actual_rrsets_.push_back(result3.rrset->getRRsig());
+    result = zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
+                               ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+    ASSERT_TRUE(result->rrset);
+    ASSERT_TRUE(result->rrset->getRRsig());
+    actual_rrsets_.push_back(result->rrset->getRRsig());
     rrsetsCheck(rrsig_aaaa_txt, actual_rrsets_.begin(), actual_rrsets_.end());
 }
 
@@ -1694,7 +1692,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3) {
     EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
     EXPECT_EQ(ZoneFinder::NXDOMAIN,
               zone_finder_.find(Name(string(apex_hash) + ".example.org"),
-                                RRType::NSEC3()).code);
+                                RRType::NSEC3())->code);
     // Dedicated NSEC3 find should be able to find it.
     findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
                    zone_finder_.findNSEC3(Name("example.org"), false));
@@ -1714,7 +1712,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3) {
     EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nonsec3_text)));
     EXPECT_EQ(ZoneFinder::SUCCESS,
               zone_finder_.find(Name(string(apex_hash) + ".example.org"),
-                                RRType::A()).code);
+                                RRType::A())->code);
 }
 
 TEST_F(InMemoryZoneFinderTest, addNSEC3Lower) {
diff --git a/src/lib/datasrc/tests/testdata/contexttest.zone b/src/lib/datasrc/tests/testdata/contexttest.zone
new file mode 100644
index 0000000..7227ced
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/contexttest.zone
@@ -0,0 +1,35 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 22 3600 300 3600000 3600
+example.org.			      3600 IN NS	ns1.example.org.
+example.org.			      3600 IN NS	ns2.example.org.
+example.org.			      3600 IN MX	1 mx1.example.org.
+example.org.			      3600 IN MX	2 mx2.example.org.
+example.org.			      3600 IN MX	3 mx.a.example.org.
+
+ns1.example.org.		      3600 IN A		192.0.2.1
+ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org.		      3600 IN AAAA	2001:db8::1
+ns1.example.org.		      3600 IN RRSIG	AAAA 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
+ns2.example.org.		      3600 IN A		192.0.2.2
+ns2.example.org.		      3600 IN TXT	"text data"
+
+mx1.example.org.		      3600 IN A		192.0.2.10
+mx2.example.org.		      3600 IN AAAA	2001:db8::10
+
+;; delegation
+a.example.org.			      3600 IN NS	ns1.a.example.org.
+a.example.org.			      3600 IN NS	ns2.a.example.org.
+a.example.org.			      3600 IN NS	ns.example.com.
+
+ns1.a.example.org.		      3600 IN A		192.0.2.5
+ns2.a.example.org.		      3600 IN A		192.0.2.6
+ns2.a.example.org.		      3600 IN AAAA	2001:db8::6
+mx.a.example.org.		      3600 IN A		192.0.2.7
+
+;; CNAME
+alias.example.org. 3600 IN CNAME cname.example.org.
+
+;; DNAME
+dname.example.org. 3600 IN DNAME dname.example.com.
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
new file mode 100644
index 0000000..5639f26
--- /dev/null
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -0,0 +1,322 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/masterload.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/zone.h>
+#include <datasrc/memory_datasrc.h>
+#include <datasrc/database.h>
+#include <datasrc/sqlite3_accessor.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <cstdlib>
+#include <vector>
+
+using namespace std;
+using boost::shared_ptr;
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+using namespace isc::testutils;
+
+namespace {
+
+// Commonly used test zone file.
+const char* const TEST_ZONE_FILE = TEST_DATA_DIR "/contexttest.zone";
+
+// Convenient shortcut
+typedef shared_ptr<DataSourceClient> DataSourceClientPtr;
+
+// This is the type used as the test parameter.  Note that this is
+// intentionally a plain old type (i.e. a function pointer), not a class;
+// otherwise it could cause initialization fiasco at the instantiation time.
+typedef DataSourceClientPtr (*ClientCreator)(RRClass, const Name&);
+
+// Creator for the in-memory client to be tested
+DataSourceClientPtr
+createInMemoryClient(RRClass zclass, const Name& zname) {
+    shared_ptr<InMemoryClient> client(new InMemoryClient);
+
+    shared_ptr<InMemoryZoneFinder> finder(
+        new InMemoryZoneFinder(zclass, zname));
+    finder->load(TEST_ZONE_FILE);
+
+    client->addZone(finder);
+
+    return (client);
+}
+
+// Creator for the SQLite3 client to be tested.  addRRset() is a helper
+// subroutine.
+void
+addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
+    updater->addRRset(*rrset);
+}
+
+DataSourceClientPtr
+createSQLite3Client(RRClass zclass, const Name& zname) {
+    // We always begin with an empty template SQLite3 DB file and install
+    // the zone data from the zone file to ensure both cases have the
+    // same test data.
+
+    const char* const install_cmd = INSTALL_PROG " " TEST_DATA_DIR
+        "/rwtest.sqlite3 " TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied";
+    if (system(install_cmd) != 0) {
+        isc_throw(isc::Unexpected,
+                  "Error setting up; command failed: " << install_cmd);
+    }
+
+    shared_ptr<SQLite3Accessor> accessor(
+        new SQLite3Accessor(TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied",
+                            zclass.toText()));
+    shared_ptr<DatabaseClient> client(new DatabaseClient(zclass, accessor));
+
+    ZoneUpdaterPtr updater = client->getUpdater(zname, true);
+    masterLoad(TEST_ZONE_FILE, zname, zclass, boost::bind(addRRset, updater,
+                                                          _1));
+    // Insert an out-of-zone name to test if it's incorrectly returned.
+    // Note that neither updater nor SQLite3 accessor checks this condition,
+    // so this should succeed.
+    stringstream ss("ns.example.com. 3600 IN A 192.0.2.7");
+    masterLoad(ss, Name::ROOT_NAME(), zclass,
+               boost::bind(addRRset, updater, _1));
+    updater->commit();
+
+    return (client);
+}
+
+// The test class.  Its parameterized so we can share the test scnearios
+// for any concrete data source implementaitons.
+class ZoneFinderContextTest :
+        public ::testing::TestWithParam<ClientCreator>
+{
+protected:
+    ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") {
+        client_ = (*GetParam())(qclass_, qzone_);
+        REQUESTED_A.push_back(RRType::A());
+        REQUESTED_AAAA.push_back(RRType::AAAA());
+        REQUESTED_BOTH.push_back(RRType::A());
+        REQUESTED_BOTH.push_back(RRType::AAAA());
+    }
+    void SetUp() {
+        finder_ = client_->findZone(qzone_).zone_finder;
+        ASSERT_TRUE(finder_);
+    }
+
+    const RRClass qclass_;
+    const Name qzone_;
+    DataSourceClientPtr client_;
+    ZoneFinderPtr finder_;
+
+    vector<RRType> requested_types_;
+    vector<RRType> REQUESTED_A;
+    vector<RRType> REQUESTED_AAAA;
+    vector<RRType> REQUESTED_BOTH;
+    vector<ConstRRsetPtr> result_sets_;
+};
+
+// We test the in-memory and SQLite3 data source implementations.
+INSTANTIATE_TEST_CASE_P(, ZoneFinderContextTest,
+                        ::testing::Values(createInMemoryClient,
+                                          createSQLite3Client));
+
+TEST_P(ZoneFinderContextTest, getAdditionalAuthNS) {
+    ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    // Getting both A and AAAA NS addresses
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting only A
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_A, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting only AAAA
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting A again, without clearing the result sets.  This confirms
+    // getAdditional() doesn't change the existing vector content.
+    ctx->getAdditional(REQUESTED_A, result_sets_);
+    // The first element should be the existing AAAA RR, followed by the A's.
+    EXPECT_EQ(RRType::AAAA(), result_sets_[0]->getType());
+    rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Normally expected type set contain only A and/or AAAA, but others aren't
+    // excluded.
+    result_sets_.clear();
+    requested_types_.push_back(RRType::TXT());
+    ctx->getAdditional(requested_types_, result_sets_);
+    rrsetsCheck("ns2.example.org. 3600 IN TXT \"text data\"",
+                result_sets_.begin(), result_sets_.end());
+
+    // Even empty set is okay.  The result should also be empty.
+    result_sets_.clear();
+    ctx->getAdditional(vector<RRType>(), result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegation) {
+    // Basically similar to the AuthNS case, but NS names are glues.
+    // It contains an out-of-zone NS name.  Its address (even if it's somehow
+    // inserted to the zone data) shouldn't be returned.
+    const Name qname("www.a.example.org");
+    ZoneFinderContextPtr ctx = finder_->find(qname, RRType::AAAA());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+                "ns2.a.example.org. 3600 IN A 192.0.2.6\n"
+                "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+                result_sets_.begin(), result_sets_.end());
+
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_A, result_sets_);
+    rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+                "ns2.a.example.org. 3600 IN A 192.0.2.6\n",
+                result_sets_.begin(), result_sets_.end());
+
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+    rrsetsCheck("ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalMX) {
+    // Similar to the previous cases, but for MX addresses.  The test zone
+    // contains MX name under a zone cut.  Its address shouldn't be returned.
+    ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::MX());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    // Getting both A and AAAA NS addresses
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n"
+                "mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting only A
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_A, result_sets_);
+    rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting only AAAA
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+    rrsetsCheck("mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalWithSIG) {
+    // Similar to the AuthNS test, but the original find() requested DNSSEC
+    // RRSIGs.  Then additional records will also have RRSIGs.
+    ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS(),
+                                             ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    vector<ConstRRsetPtr> sigresult_sets;
+    BOOST_FOREACH(ConstRRsetPtr rrset, result_sets_) {
+        ConstRRsetPtr sig_rrset = rrset->getRRsig();
+        if (sig_rrset) {
+            sigresult_sets.push_back(sig_rrset);
+        }
+    }
+    rrsetsCheck("ns1.example.org. 3600 IN RRSIG	A 7 3 3600 20150420235959 "
+                "20051021000000 40430 example.org. FAKEFAKE\n"
+                "ns1.example.org. 3600 IN RRSIG	AAAA 7 3 3600 20150420235959 "
+                "20051021000000 40430 example.org. FAKEFAKEFAKE\n",
+                sigresult_sets.begin(), sigresult_sets.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalNoOP) {
+    // getAdditional() is only meaningful after SUCCESS or DELEGATION.
+
+    ZoneFinderContextPtr ctx = finder_->find(Name("nxdomain.example.org"),
+                                             RRType::NS());
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+
+    ctx = finder_->find(qzone_, RRType::TXT());
+    EXPECT_EQ(ZoneFinder::NXRRSET, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+
+    ctx = finder_->find(Name("alias.example.org."), RRType::A());
+    EXPECT_EQ(ZoneFinder::CNAME, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+
+    ctx = finder_->find(Name("www.dname.example.org."), RRType::A());
+    EXPECT_EQ(ZoneFinder::DNAME, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalForAny) {
+    // getAdditional() after successful type ANY query should return
+    // the additional records of all returned RRsets.
+    vector<ConstRRsetPtr> all_rrsets;
+    ZoneFinderContextPtr ctx = finder_->findAll(qzone_, all_rrsets);
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n"
+                "mx1.example.org. 3600 IN A 192.0.2.10\n"
+                "mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // If the type ANY query results in DELEGATION, the result should be the
+    // same as normal query.
+    all_rrsets.clear();
+    result_sets_.clear();
+    ctx = finder_->findAll(Name("www.a.example.org"), all_rrsets);
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+                "ns2.a.example.org. 3600 IN A 192.0.2.6\n"
+                "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+}
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index ff88746..0a58312 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -15,14 +15,15 @@
 #ifndef __ZONE_H
 #define __ZONE_H 1
 
-#include <utility>
-#include <vector>
-
+#include <dns/name.h>
 #include <dns/rrset.h>
-#include <dns/rrsetlist.h>
+#include <dns/rrtype.h>
 
 #include <datasrc/result.h>
 
+#include <utility>
+#include <vector>
+
 namespace isc {
 namespace datasrc {
 
@@ -79,7 +80,7 @@ public:
     /// proof of the result.
     ///
     /// The caller is generally expected to get access to the information
-    /// via read-only getter methods of \c FindResult so that it won't rely
+    /// 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.
@@ -90,39 +91,117 @@ public:
         RESULT_NSEC3_SIGNED = 4   ///< The zone is signed with NSEC3 RRs
     };
 
-    /// A helper structure to represent the search result of \c find().
-    ///
-    /// This is a straightforward tuple of the result code and a pointer
-    /// (and optionally special flags) to the found RRset to represent the
-    /// result of \c find() (there will be more members in the future -
-    /// see the class description).
-    /// We use this in order to avoid overloading the return value for both
-    /// the result code ("success" or "not found") and the found object,
-    /// i.e., avoid using \c NULL to mean "not found", etc.
-    ///
-    /// This is a simple value class whose internal state never changes,
-    /// so for convenience we allow the applications to refer to some of the
-    /// members directly.  For others we provide read-only accessor methods
-    /// to hide specific representation.
-    ///
-    /// Note: we should eventually include a notion of "zone node", which
-    /// corresponds to a particular domain name of the zone, so that we can
-    /// find RRsets of a different RR type for that name (e.g. for type ANY
-    /// query or to include DS RRs with delegation).
-    ///
-    /// Note: we may also want to include the closest enclosure "node" to
-    /// optimize including the NSEC for no-wildcard proof (FWIW NSD does that).
-    struct FindResult {
-        FindResult(Result param_code,
-                   const isc::dns::ConstRRsetPtr param_rrset,
-                   FindResultFlags param_flags = RESULT_DEFAULT) :
-            code(param_code), rrset(param_rrset), flags(param_flags)
+    /// 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 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 reprsent 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 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 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 The find options specified for the find() call.
+        /// \param result The result of the find() call.
+        Context(ZoneFinder& finder, FindOptions options,
+                const ResultContext& result) :
+            code(result.code), rrset(result.rrset),
+            finder_(finder), flags_(result.flags), options_(options)
+        {}
+
+        /// \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 The find options specified for the findAll() call.
+        /// \param result The result of the findAll() call (whose rrset is
+        ///        expected to be NULL).
+        /// \param all_set Reference to the vector given by the caller of
+        ///       \c findAll(), storing the RRsets to be returned.
+        Context(ZoneFinder& finder, FindOptions options,
+                const ResultContext& result,
+                const std::vector<isc::dns::ConstRRsetPtr> &all_set) :
+            code(result.code), rrset(result.rrset),
+            finder_(finder), flags_(result.flags), options_(options),
+            all_set_(all_set)
         {}
+
         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); }
+        bool isWildcard() const { return ((flags_ & RESULT_WILDCARD) != 0); }
 
         /// Return true when the underlying zone is signed with NSEC.
         ///
@@ -134,7 +213,7 @@ public:
         /// that \c rrset be a valid NSEC RRset as described in \c find()
         /// documentation.
         bool isNSECSigned() const {
-            return ((flags & RESULT_NSEC_SIGNED) != 0);
+            return ((flags_ & RESULT_NSEC_SIGNED) != 0);
         }
 
         /// Return true when the underlying zone is signed with NSEC3.
@@ -143,25 +222,65 @@ public:
         /// \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);
+            return ((flags_ & RESULT_NSEC3_SIGNED) != 0);
         }
-    private:
-        FindResultFlags flags;
-    };
 
-    /// 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.
+        /// \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.
+        ///
+        /// \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 the default
+            // or specialized implementation.
+            if (code != SUCCESS && code != DELEGATION) {
+                return;
+            }
+
+            getAdditionalImpl(requested_types, result);
+        }
+
+    protected:
+        /// \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.
+        virtual void getAdditionalImpl(
+            const std::vector<isc::dns::RRType>& requested_types,
+            std::vector<isc::dns::ConstRRsetPtr>& result);
+
+    private:
+        ZoneFinder& finder_;
+        const FindResultFlags flags_;
+        const FindOptions options_;
+        std::vector<isc::dns::ConstRRsetPtr> all_set_;
     };
 
     ///
@@ -217,10 +336,10 @@ public:
     ///   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 FindResult will be NULL), unless DNSSEC data
+    /// (\c rrset member of \c FindContext will be NULL), unless DNSSEC data
     /// are required.  See below for the cases with DNSSEC.
     ///
-    /// The returned \c FindResult object can also provide supplemental
+    /// 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
@@ -276,7 +395,7 @@ public:
     /// 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 FindResult.
+    /// 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:
     ///
@@ -333,7 +452,7 @@ public:
     /// \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 FindResult object will return true.
+    /// \c FindContext object will return true.
     ///
     /// \exception std::bad_alloc Memory allocation such as for constructing
     ///  the resulting RRset fails
@@ -348,11 +467,12 @@ public:
     /// \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 FindResult object enclosing the search result (see above).
-    virtual FindResult find(const isc::dns::Name& name,
-                            const isc::dns::RRType& type,
-                            const FindOptions options
-                            = FIND_DEFAULT) = 0;
+    /// \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.
@@ -370,13 +490,14 @@ public:
     /// \param target the successfull result is returned through this
     /// \param options \see find, parameter options
     /// \return \see find and it's result
-    virtual FindResult findAll(const isc::dns::Name& name,
-                               std::vector<isc::dns::ConstRRsetPtr> &target,
-                               const FindOptions options = FIND_DEFAULT) = 0;
+    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 FindResult, but \c findNSEC3() has
+    /// 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 {
@@ -530,9 +651,16 @@ inline ZoneFinder::FindResultFlags operator |(
 /// \brief A pointer-like type pointing to a \c ZoneFinder object.
 typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
 
-/// \brief A pointer-like type pointing to a \c ZoneFinder object.
+/// \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;
+
 /// The base class to make updates to a single zone.
 ///
 /// On construction, each derived class object will start a "transaction"
diff --git a/src/lib/datasrc/zone_finder_context.cc b/src/lib/datasrc/zone_finder_context.cc
new file mode 100644
index 0000000..7913d71
--- /dev/null
+++ b/src/lib/datasrc/zone_finder_context.cc
@@ -0,0 +1,102 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rdataclass.h>
+
+#include <datasrc/zone.h>
+
+#include <boost/foreach.hpp>
+
+#include <vector>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+void
+getAdditionalAddrs(ZoneFinder& finder, const Name& name,
+                   const vector<RRType>& requested_types,
+                   vector<ConstRRsetPtr>& result_rrsets,
+                   ZoneFinder::FindOptions options)
+{
+    // Ignore out-of-zone names
+    const NameComparisonResult cmp = finder.getOrigin().compare(name);
+    if ((cmp.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
+        (cmp.getRelation() != NameComparisonResult::EQUAL)) {
+        return;
+    }
+
+    BOOST_FOREACH(RRType rrtype, requested_types) {
+        ConstZoneFinderContextPtr ctx = finder.find(name, rrtype, options);
+        if (ctx->code == ZoneFinder::SUCCESS) {
+            result_rrsets.push_back(ctx->rrset);
+        }
+    }
+}
+
+void
+getAdditionalForRRset(ZoneFinder& finder, const AbstractRRset& rrset,
+                      const vector<RRType>& requested_types,
+                      vector<ConstRRsetPtr>& result,
+                      ZoneFinder::FindOptions orig_options)
+{
+    RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
+    ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
+    if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
+        options = options | ZoneFinder::FIND_DNSSEC;
+    }
+
+    for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+        const Rdata& rdata(rdata_iterator->getCurrent());
+
+        if (rrset.getType() == RRType::NS()) {
+            // Need to perform the search in the "GLUE OK" mode.
+            const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
+            getAdditionalAddrs(finder, ns.getNSName(), requested_types,
+                               result, options | ZoneFinder::FIND_GLUE_OK);
+        } else if (rrset.getType() == RRType::MX()) {
+            const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
+            getAdditionalAddrs(finder, mx.getMXName(), requested_types,
+                               result, options);
+        }
+    }
+}
+}
+
+void
+ZoneFinder::Context::getAdditionalImpl(const vector<RRType>& requested_types,
+                                       vector<ConstRRsetPtr>& result)
+{
+    // If rrset is non NULL, it should have been SUCCESS/DELEGATION; otherwise
+    // we should have responded to type ANY query.
+    if (rrset) {
+        getAdditionalForRRset(finder_, *rrset, requested_types, result,
+                              options_);
+        return;
+    }
+    BOOST_FOREACH(ConstRRsetPtr rrset_in_set, all_set_) {
+        getAdditionalForRRset(finder_, *rrset_in_set, requested_types, result,
+                              options_);
+    }
+}
+
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc
index f4b4ceb..33a503f 100644
--- a/src/lib/python/isc/datasrc/finder_python.cc
+++ b/src/lib/python/isc/datasrc/finder_python.cc
@@ -47,15 +47,15 @@ using namespace isc::datasrc::python;
 
 namespace  {
 ZoneFinder::FindResultFlags
-getFindResultFlags(const ZoneFinder::FindResult& result) {
+getFindResultFlags(const ZoneFinder::Context& context) {
     ZoneFinder::FindResultFlags result_flags = ZoneFinder::RESULT_DEFAULT;
-    if (result.isWildcard()) {
+    if (context.isWildcard()) {
         result_flags = result_flags | ZoneFinder::RESULT_WILDCARD;
     }
-    if (result.isNSECSigned()) {
+    if (context.isNSECSigned()) {
         result_flags = result_flags | ZoneFinder::RESULT_NSEC_SIGNED;
     }
-    if (result.isNSEC3Signed()) {
+    if (context.isNSEC3Signed()) {
         result_flags = result_flags | ZoneFinder::RESULT_NSEC3_SIGNED;
     }
     return (result_flags);
@@ -83,13 +83,13 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
         try {
             ZoneFinder::FindOptions options =
                 static_cast<ZoneFinder::FindOptions>(options_int);
-            const ZoneFinder::FindResult find_result(
+            ConstZoneFinderContextPtr find_ctx(
                 finder->find(PyName_ToName(name), PyRRType_ToRRType(rrtype),
                              options));
-            const ZoneFinder::Result r = find_result.code;
-            isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+            const ZoneFinder::Result r = find_ctx->code;
+            isc::dns::ConstRRsetPtr rrsp = find_ctx->rrset;
             ZoneFinder::FindResultFlags result_flags =
-                getFindResultFlags(find_result);
+                getFindResultFlags(*find_ctx);
             if (rrsp) {
                 // Use N instead of O so the refcount isn't increased twice
                 return (Py_BuildValue("INI", r, createRRsetObject(*rrsp),
@@ -127,12 +127,12 @@ PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args) {
             ZoneFinder::FindOptions options =
                 static_cast<ZoneFinder::FindOptions>(options_int);
             std::vector<isc::dns::ConstRRsetPtr> target;
-            const ZoneFinder::FindResult find_result(
+            ConstZoneFinderContextPtr find_ctx(
                 finder->findAll(PyName_ToName(name), target, options));
-            const ZoneFinder::Result r = find_result.code;
-            isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+            const ZoneFinder::Result r = find_ctx->code;
+            isc::dns::ConstRRsetPtr rrsp = find_ctx->rrset;
             ZoneFinder::FindResultFlags result_flags =
-                getFindResultFlags(find_result);
+                getFindResultFlags(*find_ctx);
             if (r == ZoneFinder::SUCCESS) {
                 // Copy all the RRsets to the result list
                 PyObjectContainer list_container(PyList_New(target.size()));
diff --git a/src/lib/testutils/dnsmessage_test.cc b/src/lib/testutils/dnsmessage_test.cc
index af354d5..3d653c0 100644
--- a/src/lib/testutils/dnsmessage_test.cc
+++ b/src/lib/testutils/dnsmessage_test.cc
@@ -86,6 +86,9 @@ void
 rrsetCheck(isc::dns::ConstRRsetPtr expected_rrset,
            isc::dns::ConstRRsetPtr actual_rrset)
 {
+    SCOPED_TRACE("Comparing RRsets\n"
+                 "  Actual: " + actual_rrset->toText() +
+                 "  Expected: " + expected_rrset->toText());
     EXPECT_EQ(expected_rrset->getName(), actual_rrset->getName());
     EXPECT_EQ(expected_rrset->getClass(), actual_rrset->getClass());
     EXPECT_EQ(expected_rrset->getType(), actual_rrset->getType());
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index 1aba526..8a46e0e 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -157,6 +157,18 @@ public:
 private:
     std::vector<isc::dns::ConstRRsetPtr>& rrsets_;
 };
+
+class RRsetDumper {
+public:
+    RRsetDumper(std::string& output) :
+        output_(output)
+    {}
+    void operator()(isc::dns::ConstRRsetPtr rrset) {
+        output_ += "  " + rrset->toText();
+    }
+private:
+    std::string& output_;
+};
 }
 
 /// Set of unit tests to check if two sets of RRsets are identical.
@@ -195,6 +207,10 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
             ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end)
 {
     std::vector<isc::dns::ConstRRsetPtr> checked_rrsets; // for duplicate check
+    std::string expected_text, actual_text;
+    std::for_each(expected_begin, expected_end,
+                  detail::RRsetDumper(expected_text));
+    std::for_each(actual_begin, actual_end, detail::RRsetDumper(actual_text));
     unsigned int rrset_matched = 0;
     ACTUAL_ITERATOR it;
     for (it = actual_begin; it != actual_end; ++it) {
@@ -217,11 +233,16 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
         }
     }
 
-    // make sure all expected RRsets are in actual sets
-    EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
-    // make sure rrsets only contains expected RRsets
-    EXPECT_EQ(std::distance(expected_begin, expected_end),
-              std::distance(actual_begin, actual_end));
+    {
+        SCOPED_TRACE(std::string("Comparing two RRsets:\n") +
+                     "Actual:\n" + actual_text +
+                     "Expected:\n" + expected_text);
+        // make sure all expected RRsets are in actual sets
+        EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
+        // make sure rrsets only contains expected RRsets
+        EXPECT_EQ(std::distance(expected_begin, expected_end),
+                  std::distance(actual_begin, actual_end));
+    }
 }
 
 /// Set of unit tests to check if two sets of RRsets are identical using



More information about the bind10-changes mailing list