From: Michal 'vorner' Vaner Date: Tue, 13 Dec 2011 13:07:00 +0000 (+0100) Subject: [1483] Implement findAll on database X-Git-Tag: perftcpdns_before_epoll~5^2~7^2~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b2da8d97a3f24f2f80af7f408f6b8d461f3dccd4;p=thirdparty%2Fkea.git [1483] Implement findAll on database --- diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 25dc66ef0f..33aa2b9eb4 100644 --- a/src/lib/datasrc/database.cc +++ b/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,17 +398,27 @@ DatabaseClient::Finder::findNSECCover(const Name& name) { } ZoneFinder::FindResult -DatabaseClient::Finder::findAll(const isc::dns::Name&, - std::vector&, - const FindOptions) +DatabaseClient::Finder::findAll(const isc::dns::Name& name, + std::vector& target, + const FindOptions options) { - isc_throw(isc::NotImplemented, "Not implemented"); + return (findInternal(name, RRType::ANY(), &target, options)); } ZoneFinder::FindResult 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* + target, + const FindOptions options) { // This variable is used to determine the difference between // NXDOMAIN and NXRRSET @@ -409,6 +426,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, 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; diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index f0c4575c9a..f291064213 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -822,12 +822,26 @@ public: boost::shared_ptr accessor_; const int zone_id_; const isc::dns::Name origin_; - // /// \brief Shortcut name for the result of getRRsets typedef std::pair > FoundRRsets; /// \brief Just shortcut for set of types typedef std::set 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* target, + const FindOptions options = FIND_DEFAULT); /** * \brief Searches database for RRsets of one domain. * @@ -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. * diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 8491b7a1a2..4f4af27340 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/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 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 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 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 finder(this->getFinder()); + + // It should act the same on the "failures" + std::vector 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")));