Browse Source

Merge branch #1063

Michal 'vorner' Vaner 13 years ago
parent
commit
68da925f22

+ 171 - 79
src/lib/datasrc/database.cc

@@ -49,16 +49,18 @@ DatabaseClient::findZone(const Name& name) const {
     if (zone.first) {
     if (zone.first) {
         return (FindResult(result::SUCCESS,
         return (FindResult(result::SUCCESS,
                            ZoneFinderPtr(new Finder(database_,
                            ZoneFinderPtr(new Finder(database_,
-                                                    zone.second))));
+                                                    zone.second, name))));
     }
     }
-    // Than super domains
+    // Then super domains
     // Start from 1, as 0 is covered above
     // Start from 1, as 0 is covered above
     for (size_t i(1); i < name.getLabelCount(); ++i) {
     for (size_t i(1); i < name.getLabelCount(); ++i) {
-        zone = database_->getZone(name.split(i));
+        isc::dns::Name superdomain(name.split(i));
+        zone = database_->getZone(superdomain);
         if (zone.first) {
         if (zone.first) {
             return (FindResult(result::PARTIALMATCH,
             return (FindResult(result::PARTIALMATCH,
                                ZoneFinderPtr(new Finder(database_,
                                ZoneFinderPtr(new Finder(database_,
-                                                        zone.second))));
+                                                        zone.second,
+                                                        superdomain))));
         }
         }
     }
     }
     // No, really nothing
     // No, really nothing
@@ -66,9 +68,11 @@ DatabaseClient::findZone(const Name& name) const {
 }
 }
 
 
 DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor>
 DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor>
-                               database, int zone_id) :
+                               database, int zone_id,
+                               const isc::dns::Name& origin) :
     database_(database),
     database_(database),
-    zone_id_(zone_id)
+    zone_id_(zone_id),
+    origin_(origin)
 { }
 { }
 
 
 namespace {
 namespace {
@@ -162,6 +166,119 @@ private:
 };
 };
 }
 }
 
 
+std::pair<bool, isc::dns::RRsetPtr>
+DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
+                                 const isc::dns::RRType* type,
+                                 bool want_cname, bool want_dname,
+                                 bool want_ns)
+{
+    RRsigStore sig_store;
+    database_->searchForRecords(zone_id_, name.toText());
+    bool records_found = false;
+    isc::dns::RRsetPtr result_rrset;
+
+    std::string columns[DatabaseAccessor::COLUMN_COUNT];
+    while (database_->getNextRecord(columns, DatabaseAccessor::COLUMN_COUNT)) {
+        if (!records_found) {
+            records_found = true;
+        }
+
+        try {
+            const isc::dns::RRType cur_type(columns[DatabaseAccessor::
+                                            TYPE_COLUMN]);
+            const isc::dns::RRTTL cur_ttl(columns[DatabaseAccessor::
+                                          TTL_COLUMN]);
+            // Ths sigtype column was an optimization for finding the
+            // relevant RRSIG RRs for a lookup. Currently this column is
+            // not used in this revised datasource implementation. We
+            // should either start using it again, or remove it from use
+            // completely (i.e. also remove it from the schema and the
+            // backend implementation).
+            // Note that because we don't use it now, we also won't notice
+            // it if the value is wrong (i.e. if the sigtype column
+            // contains an rrtype that is different from the actual value
+            // of the 'type covered' field in the RRSIG Rdata).
+            //cur_sigtype(columns[SIGTYPE_COLUMN]);
+
+            // Check for delegations before checking for the right type.
+            // This is needed to properly delegate request for the NS
+            // record itself.
+            //
+            // This happens with NS only, CNAME must be alone and DNAME
+            // is not checked in the exact queried domain.
+            if (want_ns && cur_type == isc::dns::RRType::NS()) {
+                if (result_rrset &&
+                    result_rrset->getType() != isc::dns::RRType::NS()) {
+                    isc_throw(DataSourceError, "NS found together with data"
+                              " in non-apex domain " + name.toText());
+                }
+                addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
+                            columns[DatabaseAccessor::RDATA_COLUMN],
+                            *database_);
+            } else if (type != NULL && cur_type == *type) {
+                if (result_rrset &&
+                    result_rrset->getType() == isc::dns::RRType::CNAME()) {
+                    isc_throw(DataSourceError, "CNAME found but it is not "
+                              "the only record for " + name.toText());
+                } else if (result_rrset && want_ns &&
+                           result_rrset->getType() == isc::dns::RRType::NS()) {
+                    isc_throw(DataSourceError, "NS found together with data"
+                              " in non-apex domain " + name.toText());
+                }
+                addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
+                            columns[DatabaseAccessor::RDATA_COLUMN],
+                            *database_);
+            } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) {
+                // There should be no other data, so result_rrset should
+                // be empty.
+                if (result_rrset) {
+                    isc_throw(DataSourceError, "CNAME found but it is not "
+                              "the only record for " + name.toText());
+                }
+                addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
+                            columns[DatabaseAccessor::RDATA_COLUMN],
+                            *database_);
+            } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) {
+                // There should be max one RR of DNAME present
+                if (result_rrset &&
+                    result_rrset->getType() == isc::dns::RRType::DNAME()) {
+                    isc_throw(DataSourceError, "DNAME with multiple RRs in " +
+                              name.toText());
+                }
+                addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
+                            columns[DatabaseAccessor::RDATA_COLUMN],
+                            *database_);
+            } else if (cur_type == isc::dns::RRType::RRSIG()) {
+                // If we get signatures before we get the actual data, we
+                // can't know which ones to keep and which to drop...
+                // So we keep a separate store of any signature that may be
+                // relevant and add them to the final RRset when we are
+                // done.
+                // A possible optimization here is to not store them for
+                // types we are certain we don't need
+                sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
+                    getClass(), columns[DatabaseAccessor::RDATA_COLUMN]));
+            }
+        } catch (const isc::dns::InvalidRRType& irt) {
+            isc_throw(DataSourceError, "Invalid RRType in database for " <<
+                      name << ": " << columns[DatabaseAccessor::
+                      TYPE_COLUMN]);
+        } catch (const isc::dns::InvalidRRTTL& irttl) {
+            isc_throw(DataSourceError, "Invalid TTL in database for " <<
+                      name << ": " << columns[DatabaseAccessor::
+                      TTL_COLUMN]);
+        } catch (const isc::dns::rdata::InvalidRdataText& ird) {
+            isc_throw(DataSourceError, "Invalid rdata in database for " <<
+                      name << ": " << columns[DatabaseAccessor::
+                      RDATA_COLUMN]);
+        }
+    }
+    if (result_rrset) {
+        sig_store.appendSignatures(result_rrset);
+    }
+    return (std::pair<bool, isc::dns::RRsetPtr>(records_found, result_rrset));
+}
+
 
 
 ZoneFinder::FindResult
 ZoneFinder::FindResult
 DatabaseClient::Finder::find(const isc::dns::Name& name,
 DatabaseClient::Finder::find(const isc::dns::Name& name,
@@ -174,84 +291,61 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     bool records_found = false;
     bool records_found = false;
     isc::dns::RRsetPtr result_rrset;
     isc::dns::RRsetPtr result_rrset;
     ZoneFinder::Result result_status = SUCCESS;
     ZoneFinder::Result result_status = SUCCESS;
-    RRsigStore sig_store;
+    std::pair<bool, isc::dns::RRsetPtr> found;
     logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
     logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
         .arg(database_->getDBName()).arg(name).arg(type);
         .arg(database_->getDBName()).arg(name).arg(type);
 
 
     try {
     try {
-        database_->searchForRecords(zone_id_, name.toText());
+        // First, do we have any kind of delegation (NS/DNAME) here?
+        Name origin(getOrigin());
+        size_t origin_label_count(origin.getLabelCount());
+        size_t current_label_count(name.getLabelCount());
+        // This is how many labels we remove to get origin
+        size_t remove_labels(current_label_count - origin_label_count);
 
 
-        std::string columns[DatabaseAccessor::COLUMN_COUNT];
-        while (database_->getNextRecord(columns,
-                                        DatabaseAccessor::COLUMN_COUNT)) {
-            if (!records_found) {
-                records_found = true;
+        // Now go trough all superdomains from origin down
+        for (int i(remove_labels); i > 0; --i) {
+            Name superdomain(name.split(i));
+            // Look if there's NS or DNAME (but ignore the NS in origin)
+            found = getRRset(superdomain, NULL, false, true,
+                             i != remove_labels);
+            if (found.second) {
+                // We found something redirecting somewhere else
+                // (it can be only NS or DNAME here)
+                result_rrset = found.second;
+                if (result_rrset->getType() == isc::dns::RRType::NS()) {
+                    LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+                              DATASRC_DATABASE_FOUND_DELEGATION).
+                        arg(database_->getDBName()).arg(superdomain);
+                    result_status = DELEGATION;
+                } else {
+                    LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+                              DATASRC_DATABASE_FOUND_DNAME).
+                        arg(database_->getDBName()).arg(superdomain);
+                    result_status = DNAME;
+                }
+                // Don't search more
+                break;
             }
             }
+        }
 
 
-            try {
-                const isc::dns::RRType cur_type(columns[DatabaseAccessor::
-                                                        TYPE_COLUMN]);
-                const isc::dns::RRTTL cur_ttl(columns[DatabaseAccessor::
-                                                      TTL_COLUMN]);
-                // Ths sigtype column was an optimization for finding the
-                // relevant RRSIG RRs for a lookup. Currently this column is
-                // not used in this revised datasource implementation. We
-                // should either start using it again, or remove it from use
-                // completely (i.e. also remove it from the schema and the
-                // backend implementation).
-                // Note that because we don't use it now, we also won't notice
-                // it if the value is wrong (i.e. if the sigtype column
-                // contains an rrtype that is different from the actual value
-                // of the 'type covered' field in the RRSIG Rdata).
-                //cur_sigtype(columns[SIGTYPE_COLUMN]);
+        if (!result_rrset) { // Only if we didn't find a redirect already
+            // Try getting the final result and extract it
+            // It is special if there's a CNAME or NS, DNAME is ignored here
+            // And we don't consider the NS in origin
+            found = getRRset(name, &type, true, false, name != origin);
+            records_found = found.first;
+            result_rrset = found.second;
+            if (result_rrset && name != origin &&
 
 
-                if (cur_type == type) {
-                    if (result_rrset &&
-                        result_rrset->getType() == isc::dns::RRType::CNAME()) {
-                        isc_throw(DataSourceError, "CNAME found but it is not "
-                                  "the only record for " + name.toText());
-                    }
-                    addOrCreate(result_rrset, name, getClass(), cur_type,
-                                cur_ttl, columns[DatabaseAccessor::
-                                                 RDATA_COLUMN],
-                                *database_);
-                } else if (cur_type == isc::dns::RRType::CNAME()) {
-                    // There should be no other data, so result_rrset should
-                    // be empty.
-                    if (result_rrset) {
-                        isc_throw(DataSourceError, "CNAME found but it is not "
-                                  "the only record for " + name.toText());
-                    }
-                    addOrCreate(result_rrset, name, getClass(), cur_type,
-                                cur_ttl, columns[DatabaseAccessor::
-                                                 RDATA_COLUMN],
-                                *database_);
-                    result_status = CNAME;
-                } else if (cur_type == isc::dns::RRType::RRSIG()) {
-                    // If we get signatures before we get the actual data, we
-                    // can't know which ones to keep and which to drop...
-                    // So we keep a separate store of any signature that may be
-                    // relevant and add them to the final RRset when we are
-                    // done.
-                    // A possible optimization here is to not store them for
-                    // types we are certain we don't need
-                    sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
-                                    getClass(),
-                                    columns[DatabaseAccessor::
-                                            RDATA_COLUMN]));
-                }
-            } catch (const isc::dns::InvalidRRType& irt) {
-                isc_throw(DataSourceError, "Invalid RRType in database for " <<
-                        name << ": " << columns[DatabaseAccessor::
-                                                TYPE_COLUMN]);
-            } catch (const isc::dns::InvalidRRTTL& irttl) {
-                isc_throw(DataSourceError, "Invalid TTL in database for " <<
-                        name << ": " << columns[DatabaseAccessor::
-                                                TTL_COLUMN]);
-            } catch (const isc::dns::rdata::InvalidRdataText& ird) {
-                isc_throw(DataSourceError, "Invalid rdata in database for " <<
-                        name << ": " << columns[DatabaseAccessor::
-                                                RDATA_COLUMN]);
+                result_rrset->getType() == isc::dns::RRType::NS()) {
+                LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+                          DATASRC_DATABASE_FOUND_DELEGATION_EXACT).
+                    arg(database_->getDBName()).arg(name);
+                result_status = DELEGATION;
+            } else if (result_rrset && type != isc::dns::RRType::CNAME() &&
+                       result_rrset->getType() == isc::dns::RRType::CNAME()) {
+                result_status = CNAME;
             }
             }
         }
         }
     } catch (const DataSourceError& dse) {
     } catch (const DataSourceError& dse) {
@@ -288,7 +382,6 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
             result_status = NXDOMAIN;
             result_status = NXDOMAIN;
         }
         }
     } else {
     } else {
-        sig_store.appendSignatures(result_rrset);
         logger.debug(DBG_TRACE_DETAILED,
         logger.debug(DBG_TRACE_DETAILED,
                      DATASRC_DATABASE_FOUND_RRSET)
                      DATASRC_DATABASE_FOUND_RRSET)
                     .arg(database_->getDBName()).arg(*result_rrset);
                     .arg(database_->getDBName()).arg(*result_rrset);
@@ -298,8 +391,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
 
 
 Name
 Name
 DatabaseClient::Finder::getOrigin() const {
 DatabaseClient::Finder::getOrigin() const {
-    // TODO Implement
-    return (Name("."));
+    return (origin_);
 }
 }
 
 
 isc::dns::RRClass
 isc::dns::RRClass

+ 49 - 1
src/lib/datasrc/database.h

@@ -17,6 +17,8 @@
 
 
 #include <datasrc/client.h>
 #include <datasrc/client.h>
 
 
+#include <dns/name.h>
+
 namespace isc {
 namespace isc {
 namespace datasrc {
 namespace datasrc {
 
 
@@ -218,8 +220,12 @@ public:
          * \param zone_id The zone ID which was returned from
          * \param zone_id The zone ID which was returned from
          *     DatabaseAccessor::getZone and which will be passed to further
          *     DatabaseAccessor::getZone and which will be passed to further
          *     calls to the database.
          *     calls to the database.
+         * \param origin The name of the origin of this zone. It could query
+         *     it from database, but as the DatabaseClient just searched for
+         *     the zone using the name, it should have it.
          */
          */
-        Finder(boost::shared_ptr<DatabaseAccessor> database, int zone_id);
+        Finder(boost::shared_ptr<DatabaseAccessor> database, int zone_id,
+               const isc::dns::Name& origin);
         // The following three methods are just implementations of inherited
         // The following three methods are just implementations of inherited
         // ZoneFinder's pure virtual methods.
         // ZoneFinder's pure virtual methods.
         virtual isc::dns::Name getOrigin() const;
         virtual isc::dns::Name getOrigin() const;
@@ -290,6 +296,48 @@ public:
     private:
     private:
         boost::shared_ptr<DatabaseAccessor> database_;
         boost::shared_ptr<DatabaseAccessor> database_;
         const int zone_id_;
         const int zone_id_;
+        const isc::dns::Name origin_;
+        /**
+         * \brief Searches database for an RRset
+         *
+         * This method scans RRs of single domain specified by name and finds
+         * RRset with given type or any of redirection RRsets that are
+         * requested.
+         *
+         * This function is used internally by find(), because this part is
+         * called multiple times with slightly different parameters.
+         *
+         * \param name Which domain name should be scanned.
+         * \param type The RRType which is requested. This can be NULL, in
+         *     which case the method will look for the redirections only.
+         * \param want_cname If this is true, CNAME redirection may be returned
+         *     instead of the RRset with given type. If there's CNAME and
+         *     something else or the CNAME has multiple RRs, it throws
+         *     DataSourceError.
+         * \param want_dname If this is true, DNAME redirection may be returned
+         *     instead. This is with type = NULL only and is not checked in
+         *     other circumstances. If the DNAME has multiple RRs, it throws
+         *     DataSourceError.
+         * \param want_ns This allows redirection by NS to be returned. If
+         *     any other data is met as well, DataSourceError is thrown.
+         * \note It may happen that some of the above error conditions are not
+         *     detected in some circumstances. The goal here is not to validate
+         *     the domain in DB, but to avoid bad behaviour resulting from
+         *     broken data.
+         * \return First part of the result tells if the domain contains any
+         *     RRs. This can be used to decide between NXDOMAIN and NXRRSET.
+         *     The second part is the RRset found (if any) with any relevant
+         *     signatures attached to it.
+         * \todo This interface doesn't look very elegant. Any better idea
+         *     would be nice.
+         */
+        std::pair<bool, isc::dns::RRsetPtr> getRRset(const isc::dns::Name&
+                                                     name,
+                                                     const isc::dns::RRType*
+                                                     type,
+                                                     bool want_cname,
+                                                     bool want_dname,
+                                                     bool want_ns);
     };
     };
     /**
     /**
      * \brief Find a zone in the database
      * \brief Find a zone in the database

+ 14 - 0
src/lib/datasrc/datasrc_messages.mes

@@ -90,6 +90,20 @@ most likely points to a logic error in the code, and can be considered a bug.
 The current search is aborted. Specific information about the exception is
 The current search is aborted. Specific information about the exception is
 printed in this error message.
 printed in this error message.
 
 
+% DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %2 in %1
+When searching for a domain, the program met a delegation to a different zone
+at the given domain name. It will return that one instead.
+
+% DATASRC_DATABASE_FOUND_DELEGATION_EXACT Found delegation at %2 (exact match) in %1
+The program found the domain requested, but it is a delegation point to a
+different zone, therefore it is not authoritative for this domain name.
+It will return the NS record instead.
+
+% DATASRC_DATABASE_FOUND_DNAME Found DNAME at %2 in %1
+When searching for a domain, the program met a DNAME redirection to a different
+place in the domain space at the given domain name. It will return that one
+instead.
+
 % DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4
 % DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4
 The data returned by the database backend did not contain any data for the given
 The data returned by the database backend did not contain any data for the given
 domain name, class and type.
 domain name, class and type.

+ 310 - 116
src/lib/datasrc/tests/database_unittest.cc

@@ -263,6 +263,46 @@ private:
         addRecord("A", "3600", "", "192.0.2.1");
         addRecord("A", "3600", "", "192.0.2.1");
         addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
         addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
         addCurName("badsigtype.example.org.");
         addCurName("badsigtype.example.org.");
+
+        // Data for testing delegation (with NS and DNAME)
+        addRecord("NS", "3600", "", "ns.example.com.");
+        addRecord("NS", "3600", "", "ns.delegation.example.org.");
+        addRecord("RRSIG", "3600", "", "NS 5 3 3600 20000101000000 "
+                  "20000201000000 12345 example.org. FAKEFAKEFAKE");
+        addCurName("delegation.example.org.");
+        addRecord("A", "3600", "", "192.0.2.1");
+        addCurName("ns.delegation.example.org.");
+        addRecord("A", "3600", "", "192.0.2.1");
+        addCurName("deep.below.delegation.example.org.");
+
+        addRecord("A", "3600", "", "192.0.2.1");
+        addRecord("DNAME", "3600", "", "dname.example.com.");
+        addRecord("RRSIG", "3600", "", "DNAME 5 3 3600 20000101000000 "
+                  "20000201000000 12345 example.org. FAKEFAKEFAKE");
+        addCurName("dname.example.org.");
+        addRecord("A", "3600", "", "192.0.2.1");
+        addCurName("below.dname.example.org.");
+
+        // Broken NS
+        addRecord("A", "3600", "", "192.0.2.1");
+        addRecord("NS", "3600", "", "ns.example.com.");
+        addCurName("brokenns1.example.org.");
+        addRecord("NS", "3600", "", "ns.example.com.");
+        addRecord("A", "3600", "", "192.0.2.1");
+        addCurName("brokenns2.example.org.");
+
+        // Now double DNAME, to test failure mode
+        addRecord("DNAME", "3600", "", "dname1.example.com.");
+        addRecord("DNAME", "3600", "", "dname2.example.com.");
+        addCurName("baddname.example.org.");
+
+        // Put some data into apex (including NS) so we can check our NS
+        // doesn't break anything
+        addRecord("NS", "3600", "", "ns.example.com.");
+        addRecord("A", "3600", "", "192.0.2.1");
+        addRecord("RRSIG", "3600", "", "NS 5 3 3600 20000101000000 "
+                  "20000201000000 12345 example.org. FAKEFAKEFAKE");
+        addCurName("example.org.");
     }
     }
 };
 };
 
 
@@ -298,6 +338,21 @@ public:
         EXPECT_EQ(42, finder->zone_id());
         EXPECT_EQ(42, finder->zone_id());
         EXPECT_EQ(current_database_, &finder->database());
         EXPECT_EQ(current_database_, &finder->database());
     }
     }
+
+    shared_ptr<DatabaseClient::Finder> getFinder() {
+        DataSourceClient::FindResult zone(
+            client_->findZone(Name("example.org")));
+        EXPECT_EQ(result::SUCCESS, zone.code);
+        shared_ptr<DatabaseClient::Finder> finder(
+            dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
+        EXPECT_EQ(42, finder->zone_id());
+        EXPECT_FALSE(current_database_->searchRunning());
+
+        return (finder);
+    }
+
+    std::vector<std::string> expected_rdatas_;
+    std::vector<std::string> expected_sig_rdatas_;
 };
 };
 
 
 TEST_F(DatabaseClientTest, zoneNotFound) {
 TEST_F(DatabaseClientTest, zoneNotFound) {
@@ -352,20 +407,24 @@ doFindTest(shared_ptr<DatabaseClient::Finder> finder,
            const isc::dns::RRType& expected_type,
            const isc::dns::RRType& expected_type,
            const isc::dns::RRTTL expected_ttl,
            const isc::dns::RRTTL expected_ttl,
            ZoneFinder::Result expected_result,
            ZoneFinder::Result expected_result,
-           const std::vector<std::string>& expected_rdatas,
-           const std::vector<std::string>& expected_sig_rdatas)
+           const std::vector<std::string>& expected_rdatas_,
+           const std::vector<std::string>& expected_sig_rdatas_,
+           const isc::dns::Name& expected_name = isc::dns::Name::ROOT_NAME())
 {
 {
+    SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
     ZoneFinder::FindResult result =
     ZoneFinder::FindResult result =
         finder->find(name, type, NULL, ZoneFinder::FIND_DEFAULT);
         finder->find(name, type, NULL, ZoneFinder::FIND_DEFAULT);
     ASSERT_EQ(expected_result, result.code) << name << " " << type;
     ASSERT_EQ(expected_result, result.code) << name << " " << type;
-    if (expected_rdatas.size() > 0) {
-        checkRRset(result.rrset, name, finder->getClass(),
-                   expected_type, expected_ttl, expected_rdatas);
-
-        if (expected_sig_rdatas.size() > 0) {
-            checkRRset(result.rrset->getRRsig(), name,
-                       finder->getClass(), isc::dns::RRType::RRSIG(),
-                       expected_ttl, expected_sig_rdatas);
+    if (expected_rdatas_.size() > 0) {
+        checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
+                   name, finder->getClass(), expected_type, expected_ttl,
+                   expected_rdatas_);
+
+        if (expected_sig_rdatas_.size() > 0) {
+            checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ?
+                       expected_name : name, finder->getClass(),
+                       isc::dns::RRType::RRSIG(), expected_ttl,
+                       expected_sig_rdatas_);
         } else {
         } else {
             EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
             EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
         }
         }
@@ -376,227 +435,220 @@ doFindTest(shared_ptr<DatabaseClient::Finder> finder,
 } // end anonymous namespace
 } // end anonymous namespace
 
 
 TEST_F(DatabaseClientTest, find) {
 TEST_F(DatabaseClientTest, find) {
-    DataSourceClient::FindResult zone(client_->findZone(Name("example.org")));
-    ASSERT_EQ(result::SUCCESS, zone.code);
-    shared_ptr<DatabaseClient::Finder> finder(
-        dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
-    EXPECT_EQ(42, finder->zone_id());
-    EXPECT_FALSE(current_database_->searchRunning());
-    std::vector<std::string> expected_rdatas;
-    std::vector<std::string> expected_sig_rdatas;
+    shared_ptr<DatabaseClient::Finder> finder(getFinder());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
     doFindTest(finder, isc::dns::Name("www.example.org."),
     doFindTest(finder, isc::dns::Name("www.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_rdatas.push_back("192.0.2.2");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_rdatas_.push_back("192.0.2.2");
     doFindTest(finder, isc::dns::Name("www2.example.org."),
     doFindTest(finder, isc::dns::Name("www2.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("2001:db8::1");
-    expected_rdatas.push_back("2001:db8::2");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("2001:db8::1");
+    expected_rdatas_.push_back("2001:db8::2");
     doFindTest(finder, isc::dns::Name("www.example.org."),
     doFindTest(finder, isc::dns::Name("www.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
     doFindTest(finder, isc::dns::Name("www.example.org."),
     doFindTest(finder, isc::dns::Name("www.example.org."),
                isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
                isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::NXRRSET,
                ZoneFinder::NXRRSET,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("www.example.org.");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("www.example.org.");
     doFindTest(finder, isc::dns::Name("cname.example.org."),
     doFindTest(finder, isc::dns::Name("cname.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
                isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::CNAME,
                ZoneFinder::CNAME,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("www.example.org.");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("www.example.org.");
     doFindTest(finder, isc::dns::Name("cname.example.org."),
     doFindTest(finder, isc::dns::Name("cname.example.org."),
                isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(),
                isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
     doFindTest(finder, isc::dns::Name("doesnotexist.example.org."),
     doFindTest(finder, isc::dns::Name("doesnotexist.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::NXDOMAIN,
                ZoneFinder::NXDOMAIN,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
-    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("signed1.example.org."),
     doFindTest(finder, isc::dns::Name("signed1.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("2001:db8::1");
-    expected_rdatas.push_back("2001:db8::2");
-    expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("2001:db8::1");
+    expected_rdatas_.push_back("2001:db8::2");
+    expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("signed1.example.org."),
     doFindTest(finder, isc::dns::Name("signed1.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
     doFindTest(finder, isc::dns::Name("signed1.example.org."),
     doFindTest(finder, isc::dns::Name("signed1.example.org."),
                isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
                isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::NXRRSET,
                ZoneFinder::NXRRSET,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("www.example.org.");
-    expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("www.example.org.");
+    expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("signedcname1.example.org."),
     doFindTest(finder, isc::dns::Name("signedcname1.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
                isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::CNAME,
                ZoneFinder::CNAME,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
-    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("signed2.example.org."),
     doFindTest(finder, isc::dns::Name("signed2.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("2001:db8::2");
-    expected_rdatas.push_back("2001:db8::1");
-    expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("2001:db8::2");
+    expected_rdatas_.push_back("2001:db8::1");
+    expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("signed2.example.org."),
     doFindTest(finder, isc::dns::Name("signed2.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
     doFindTest(finder, isc::dns::Name("signed2.example.org."),
     doFindTest(finder, isc::dns::Name("signed2.example.org."),
                isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
                isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::NXRRSET,
                ZoneFinder::NXRRSET,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("www.example.org.");
-    expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("www.example.org.");
+    expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("signedcname2.example.org."),
     doFindTest(finder, isc::dns::Name("signedcname2.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
                isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::CNAME,
                ZoneFinder::CNAME,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("acnamesig1.example.org."),
     doFindTest(finder, isc::dns::Name("acnamesig1.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("acnamesig2.example.org."),
     doFindTest(finder, isc::dns::Name("acnamesig2.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("acnamesig3.example.org."),
     doFindTest(finder, isc::dns::Name("acnamesig3.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_rdatas.push_back("192.0.2.2");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_rdatas_.push_back("192.0.2.2");
     doFindTest(finder, isc::dns::Name("ttldiff1.example.org."),
     doFindTest(finder, isc::dns::Name("ttldiff1.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(360),
                isc::dns::RRTTL(360),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_rdatas.push_back("192.0.2.2");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_rdatas_.push_back("192.0.2.2");
     doFindTest(finder, isc::dns::Name("ttldiff2.example.org."),
     doFindTest(finder, isc::dns::Name("ttldiff2.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(360),
                isc::dns::RRTTL(360),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 
 
 
 
@@ -672,16 +724,158 @@ TEST_F(DatabaseClientTest, find) {
     // This RRSIG has the wrong sigtype field, which should be
     // This RRSIG has the wrong sigtype field, which should be
     // an error if we decide to keep using that field
     // an error if we decide to keep using that field
     // Right now the field is ignored, so it does not error
     // Right now the field is ignored, so it does not error
-    expected_rdatas.clear();
-    expected_sig_rdatas.clear();
-    expected_rdatas.push_back("192.0.2.1");
-    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
     doFindTest(finder, isc::dns::Name("badsigtype.example.org."),
     doFindTest(finder, isc::dns::Name("badsigtype.example.org."),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRType::A(), isc::dns::RRType::A(),
                isc::dns::RRTTL(3600),
                isc::dns::RRTTL(3600),
                ZoneFinder::SUCCESS,
                ZoneFinder::SUCCESS,
-               expected_rdatas, expected_sig_rdatas);
+               expected_rdatas_, expected_sig_rdatas_);
     EXPECT_FALSE(current_database_->searchRunning());
     EXPECT_FALSE(current_database_->searchRunning());
 }
 }
 
 
+TEST_F(DatabaseClientTest, findDelegation) {
+    shared_ptr<DatabaseClient::Finder> finder(getFinder());
+
+    // The apex should not be considered delegation point and we can access
+    // data
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    doFindTest(finder, isc::dns::Name("example.org."),
+               isc::dns::RRType::A(), isc::dns::RRType::A(),
+               isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_,
+               expected_sig_rdatas_);
+    EXPECT_FALSE(current_database_->searchRunning());
+
+    expected_rdatas_.clear();
+    expected_rdatas_.push_back("ns.example.com.");
+    expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 "
+                                  "12345 example.org. FAKEFAKEFAKE");
+    doFindTest(finder, isc::dns::Name("example.org."),
+               isc::dns::RRType::NS(), isc::dns::RRType::NS(),
+               isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_,
+               expected_sig_rdatas_);
+    EXPECT_FALSE(current_database_->searchRunning());
+
+    // Check when we ask for something below delegation point, we get the NS
+    // (Both when the RRset there exists and doesn't)
+    expected_rdatas_.clear();
+    expected_sig_rdatas_.clear();
+    expected_rdatas_.push_back("ns.example.com.");
+    expected_rdatas_.push_back("ns.delegation.example.org.");
+    expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 "
+                                  "12345 example.org. FAKEFAKEFAKE");
+    doFindTest(finder, isc::dns::Name("ns.delegation.example.org."),
+               isc::dns::RRType::A(), isc::dns::RRType::NS(),
+               isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_,
+               expected_sig_rdatas_,
+               isc::dns::Name("delegation.example.org."));
+    EXPECT_FALSE(current_database_->searchRunning());
+    doFindTest(finder, isc::dns::Name("ns.delegation.example.org."),
+               isc::dns::RRType::AAAA(), isc::dns::RRType::NS(),
+               isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_,
+               expected_sig_rdatas_,
+               isc::dns::Name("delegation.example.org."));
+    doFindTest(finder, isc::dns::Name("deep.below.delegation.example.org."),
+               isc::dns::RRType::AAAA(), isc::dns::RRType::NS(),
+               isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_,
+               expected_sig_rdatas_,
+               isc::dns::Name("delegation.example.org."));
+    EXPECT_FALSE(current_database_->searchRunning());
+
+    // Even when we check directly at the delegation point, we should get
+    // the NS
+    doFindTest(finder, isc::dns::Name("delegation.example.org."),
+               isc::dns::RRType::AAAA(), isc::dns::RRType::NS(),
+               isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_,
+               expected_sig_rdatas_);
+    EXPECT_FALSE(current_database_->searchRunning());
+
+    // And when we ask direcly for the NS, we should still get delegation
+    doFindTest(finder, isc::dns::Name("delegation.example.org."),
+               isc::dns::RRType::NS(), isc::dns::RRType::NS(),
+               isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_,
+               expected_sig_rdatas_);
+    EXPECT_FALSE(current_database_->searchRunning());
+
+    // Now test delegation. If it is below the delegation point, we should get
+    // the DNAME (the one with data under DNAME is invalid zone, but we test
+    // the behaviour anyway just to make sure)
+    expected_rdatas_.clear();
+    expected_rdatas_.push_back("dname.example.com.");
+    expected_sig_rdatas_.clear();
+    expected_sig_rdatas_.push_back("DNAME 5 3 3600 20000101000000 "
+                                  "20000201000000 12345 example.org. "
+                                  "FAKEFAKEFAKE");
+    doFindTest(finder, isc::dns::Name("below.dname.example.org."),
+               isc::dns::RRType::A(), isc::dns::RRType::DNAME(),
+               isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_,
+               expected_sig_rdatas_, isc::dns::Name("dname.example.org."));
+    EXPECT_FALSE(current_database_->searchRunning());
+    doFindTest(finder, isc::dns::Name("below.dname.example.org."),
+               isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(),
+               isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_,
+               expected_sig_rdatas_, isc::dns::Name("dname.example.org."));
+    EXPECT_FALSE(current_database_->searchRunning());
+    doFindTest(finder, isc::dns::Name("really.deep.below.dname.example.org."),
+               isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(),
+               isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_,
+               expected_sig_rdatas_, isc::dns::Name("dname.example.org."));
+    EXPECT_FALSE(current_database_->searchRunning());
+
+    // Asking direcly for DNAME should give SUCCESS
+    doFindTest(finder, isc::dns::Name("dname.example.org."),
+               isc::dns::RRType::DNAME(), isc::dns::RRType::DNAME(),
+               isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_,
+               expected_sig_rdatas_);
+
+    // But we don't delegate at DNAME point
+    expected_rdatas_.clear();
+    expected_rdatas_.push_back("192.0.2.1");
+    expected_sig_rdatas_.clear();
+    doFindTest(finder, isc::dns::Name("dname.example.org."),
+               isc::dns::RRType::A(), isc::dns::RRType::A(),
+               isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_,
+               expected_sig_rdatas_);
+    EXPECT_FALSE(current_database_->searchRunning());
+    expected_rdatas_.clear();
+    doFindTest(finder, isc::dns::Name("dname.example.org."),
+               isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
+               isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_,
+               expected_sig_rdatas_);
+    EXPECT_FALSE(current_database_->searchRunning());
+
+    // This is broken dname, it contains two targets
+    EXPECT_THROW(finder->find(isc::dns::Name("below.baddname.example.org."),
+                              isc::dns::RRType::A(), NULL,
+                              ZoneFinder::FIND_DEFAULT),
+                 DataSourceError);
+    EXPECT_FALSE(current_database_->searchRunning());
+
+    // Broken NS - it lives together with something else
+    EXPECT_FALSE(current_database_->searchRunning());
+    EXPECT_THROW(finder->find(isc::dns::Name("brokenns1.example.org."),
+                              isc::dns::RRType::A(), NULL,
+                              ZoneFinder::FIND_DEFAULT),
+                 DataSourceError);
+    EXPECT_FALSE(current_database_->searchRunning());
+    EXPECT_THROW(finder->find(isc::dns::Name("brokenns2.example.org."),
+                              isc::dns::RRType::A(), NULL,
+                              ZoneFinder::FIND_DEFAULT),
+                 DataSourceError);
+    EXPECT_FALSE(current_database_->searchRunning());
+}
+
+TEST_F(DatabaseClientTest, getOrigin) {
+    DataSourceClient::FindResult zone(client_->findZone(Name("example.org")));
+    ASSERT_EQ(result::SUCCESS, zone.code);
+    shared_ptr<DatabaseClient::Finder> finder(
+        dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
+    EXPECT_EQ(42, finder->zone_id());
+    EXPECT_EQ(isc::dns::Name("example.org"), finder->getOrigin());
+}
+
 }
 }