Browse Source

[1483] Implement findAll on database

Michal 'vorner' Vaner 13 years ago
parent
commit
b2da8d97a3
3 changed files with 193 additions and 10 deletions
  1. 51 8
      src/lib/datasrc/database.cc
  2. 21 2
      src/lib/datasrc/database.h
  3. 121 0
      src/lib/datasrc/tests/database_unittest.cc

+ 51 - 8
src/lib/datasrc/database.cc

@@ -177,7 +177,8 @@ private:
 
 DatabaseClient::Finder::FoundRRsets
 DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
-                                  bool check_ns, const string* construct_name)
+                                  bool check_ns, const string* construct_name,
+                                  bool any)
 {
     RRsigStore sig_store;
     bool records_found = false;
@@ -222,7 +223,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
                      columns[DatabaseAccessor::RDATA_COLUMN]));
             }
 
-            if (types.find(cur_type) != types.end()) {
+            if (types.find(cur_type) != types.end() || any) {
                 // This type is requested, so put it into result
                 const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
                 // Ths sigtype column was an optimization for finding the
@@ -287,6 +288,12 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
         sig_store.appendSignatures(i->second);
     }
 
+    if (records_found && any) {
+        result[RRType::ANY()] = RRsetPtr();
+        // These will be sitting on the other RRsets.
+        result.erase(RRType::RRSIG());
+    }
+
     return (FoundRRsets(records_found, result));
 }
 
@@ -391,11 +398,11 @@ DatabaseClient::Finder::findNSECCover(const Name& name) {
 }
 
 ZoneFinder::FindResult
-DatabaseClient::Finder::findAll(const isc::dns::Name&,
-                                std::vector<isc::dns::ConstRRsetPtr>&,
-                                const FindOptions)
+DatabaseClient::Finder::findAll(const isc::dns::Name& name,
+                                std::vector<isc::dns::ConstRRsetPtr>& target,
+                                const FindOptions options)
 {
-    isc_throw(isc::NotImplemented, "Not implemented");
+    return (findInternal(name, RRType::ANY(), &target, options));
 }
 
 ZoneFinder::FindResult
@@ -403,12 +410,23 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                              const isc::dns::RRType& type,
                              const FindOptions options)
 {
+    return (findInternal(name, type, NULL, options));
+}
+
+ZoneFinder::FindResult
+DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
+                                     const isc::dns::RRType& type,
+                                     std::vector<isc::dns::ConstRRsetPtr>*
+                                     target,
+                                     const FindOptions options)
+{
     // This variable is used to determine the difference between
     // NXDOMAIN and NXRRSET
     bool records_found = false;
     bool glue_ok((options & FIND_GLUE_OK) != 0);
     const bool dnssec_data((options & FIND_DNSSEC) != 0);
     bool get_cover(false);
+    bool any(type == RRType::ANY());
     isc::dns::RRsetPtr result_rrset;
     ZoneFinder::Result result_status = SUCCESS;
     FoundRRsets found;
@@ -479,7 +497,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
 
         WantedTypes final_types(FINAL_TYPES());
         final_types.insert(type);
-        found = getRRsets(name.toText(), final_types, name != origin);
+        found = getRRsets(name.toText(), final_types, name != origin, NULL,
+                          any);
         records_found = found.first;
 
         // NS records, CNAME record and Wanted Type records
@@ -505,7 +524,18 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
             }
         } else if (wti != found.second.end()) {
             // Just get the answer
+            // TODO update here
             result_rrset = wti->second;
+            if (any) {
+                for (FoundIterator it(found.second.begin());
+                     it != found.second.end(); ++it) {
+                    if (it->second) {
+                        // Skip over the empty ANY
+                        target->push_back(it->second);
+                    }
+                }
+                return (FindResult(result_status, result_rrset));
+            }
         } else if (!records_found) {
             // Nothing lives here.
             // But check if something lives below this
@@ -535,7 +565,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                     // TODO What do we do about DNAME here?
                     // The types are the same as with original query
                     found = getRRsets(wildcard, final_types, true,
-                                      &construct_name);
+                                      &construct_name, any);
                     if (found.first) {
                         if (first_ns) {
                             // In case we are under NS, we don't
@@ -572,7 +602,20 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                                 result_status = DELEGATION;
                             } else if (wti != found.second.end()) {
                                 result_rrset = wti->second;
+                                // TODO Update here
                                 result_status = WILDCARD;
+                                if (any) {
+                                    for (FoundIterator
+                                         it(found.second.begin());
+                                         it != found.second.end(); ++it) {
+                                        if (it->second) {
+                                            // Skip over the empty ANY
+                                            target->push_back(it->second);
+                                        }
+                                    }
+                                    return (FindResult(result_status,
+                                                       result_rrset));
+                                }
                             } else {
                                 // NXRRSET case in the wildcard
                                 result_status = WILDCARD_NXRRSET;

+ 21 - 2
src/lib/datasrc/database.h

@@ -822,13 +822,27 @@ public:
         boost::shared_ptr<DatabaseAccessor> accessor_;
         const int zone_id_;
         const isc::dns::Name origin_;
-        //
         /// \brief Shortcut name for the result of getRRsets
         typedef std::pair<bool, std::map<dns::RRType, dns::RRsetPtr> >
             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
+         * and so on. So they share the logic here and find and findAll provide
+         * just an interface for it.
+         *
+         * 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);
+        /**
          * \brief Searches database for RRsets of one domain.
          *
          * This method scans RRs of single domain specified by name and
@@ -846,6 +860,10 @@ public:
          *     their name set to name. If it is not NULL, it overrides the name
          *     and uses this one (this can be used for wildcard synthesized
          *     records).
+         * \param any If this is true, it records all the types, not only the
+         *     ones requested by types. It also puts a NULL pointer under the
+         *     ANY type into the result, if it finds any RRs at all, to easy the
+         *     identification of success.
          * \return A pair, where the first element indicates if the domain
          *     contains any RRs at all (not only the requested, it may happen
          *     this is set to true, but the second part is empty). The second
@@ -857,7 +875,8 @@ public:
          */
         FoundRRsets getRRsets(const std::string& name,
                               const WantedTypes& types, bool check_ns,
-                              const std::string* construct_name = NULL);
+                              const std::string* construct_name = NULL,
+                              bool any = false);
         /**
          * \brief Checks if something lives below this domain.
          *

+ 121 - 0
src/lib/datasrc/tests/database_unittest.cc

@@ -1443,6 +1443,39 @@ doFindTest(ZoneFinder& finder,
     }
 }
 
+void
+doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
+                    ZoneFinder::Result expected_result,
+                    const isc::dns::RRType expected_type,
+                    std::vector<std::string> expected_rdata,
+                    const isc::dns::Name& expected_name =
+                    isc::dns::Name::ROOT_NAME(),
+                    const ZoneFinder::FindOptions options =
+                    ZoneFinder::FIND_DEFAULT)
+{
+    SCOPED_TRACE("All test for " + name.toText());
+    std::vector<ConstRRsetPtr> target;
+    ZoneFinder::FindResult 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());
+    std::vector<std::string> rdata;
+    while (!it->isLast()) {
+        rdata.push_back(it->getCurrent().toText());
+        it->next();
+    }
+    std::sort(rdata.begin(), rdata.end());
+    std::sort(expected_rdata.begin(), expected_rdata.end());
+    ASSERT_EQ(expected_rdata.size(), rdata.size());
+    for (size_t i(0); i < expected_rdata.size(); ++ i) {
+        EXPECT_EQ(expected_rdata[i], rdata[i]);
+    }
+    EXPECT_TRUE(expected_rdata == rdata);
+    EXPECT_EQ(expected_name == isc::dns::Name::ROOT_NAME() ? name :
+              expected_name, result.rrset->getName());
+}
+
 // When asking for an RRset where RRs somehow have different TTLs, it should 
 // convert to the lowest one.
 TEST_F(MockDatabaseClientTest, ttldiff) {
@@ -2254,6 +2287,94 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
                                Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
 }
 
+// Test the findAll method.
+TYPED_TEST(DatabaseClientTest, getAll) {
+    // The domain doesn't exist, so we must get the right NSEC
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    // It should act the same on the "failures"
+    std::vector<ConstRRsetPtr> target;
+    EXPECT_EQ(ZoneFinder::NXDOMAIN,
+              finder->findAll(isc::dns::Name("nothere.example.org."),
+                              target).code);
+    EXPECT_TRUE(target.empty());
+    EXPECT_EQ(ZoneFinder::NXRRSET,
+              finder->findAll(isc::dns::Name("here.wild.example.org."),
+                              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."),
+                        ZoneFinder::DELEGATION, RRType::NS(),
+                        this->expected_rdatas_,
+                        isc::dns::Name("delegation.example.org."));
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("www.example.org.");
+    doFindAllTestResult(*finder, isc::dns::Name("cname.example.org"),
+                        ZoneFinder::CNAME, RRType::CNAME(),
+                        this->expected_rdatas_);
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("dname.example.com.");
+    doFindAllTestResult(*finder, isc::dns::Name("a.dname.example.org"),
+                        ZoneFinder::DNAME, RRType::DNAME(),
+                        this->expected_rdatas_,
+                        isc::dns::Name("dname.example.org."));
+    // It should get the data on success
+    EXPECT_EQ(ZoneFinder::SUCCESS,
+              finder->findAll(isc::dns::Name("www2.example.org."),
+                              target).code);
+    ASSERT_EQ(2, target.size());
+    size_t a_idx(target[1]->getType() == RRType::A());
+    EXPECT_EQ(RRType::A(), target[a_idx]->getType());
+    std::string previous;
+    size_t count(0);
+    for (RdataIteratorPtr it(target[a_idx]->getRdataIterator());
+         !it->isLast(); it->next()) {
+        count ++;
+        EXPECT_NE(previous, it->getCurrent().toText());
+        EXPECT_TRUE(it->getCurrent().toText() == "192.0.2.1" ||
+                    it->getCurrent().toText() == "192.0.2.2");
+        previous = it->getCurrent().toText();
+    }
+    EXPECT_EQ(2, count);
+    EXPECT_EQ(RRType::AAAA(), target[1 - a_idx]->getType());
+    RdataIteratorPtr it(target[1 - a_idx]->getRdataIterator());
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("2001:db8::1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    // And on wildcard. Check the signatures as well.
+    target.clear();
+    EXPECT_EQ(ZoneFinder::WILDCARD,
+              finder->findAll(isc::dns::Name("a.wild.example.org"),
+                              target, ZoneFinder::FIND_DNSSEC).code);
+    ASSERT_EQ(2, target.size());
+    a_idx = target[1]->getType() == RRType::A();
+    EXPECT_EQ(RRType::A(), target[a_idx]->getType());
+    it = target[a_idx]->getRdataIterator();
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("192.0.2.5", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+    ConstRRsetPtr sig(target[a_idx]->getRRsig());
+    ASSERT_TRUE(sig);
+    EXPECT_EQ(RRType::RRSIG(), sig->getType());
+    EXPECT_EQ("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE",
+              sig->getRdataIterator()->getCurrent().toText());
+    EXPECT_EQ(RRType::NSEC(), target[1 - a_idx]->getType());
+    it = target[1 - a_idx]->getRdataIterator();
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("cancel.here.wild.example.org. A RRSIG NSEC",
+              it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+    sig = target[1 - a_idx]->getRRsig();
+    ASSERT_TRUE(sig);
+    EXPECT_EQ(RRType::RRSIG(), sig->getType());
+    EXPECT_EQ("NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE",
+              sig->getRdataIterator()->getCurrent().toText());
+}
+
 TYPED_TEST(DatabaseClientTest, getOrigin) {
     DataSourceClient::FindResult
         zone(this->client_->findZone(Name("example.org")));