Commit ddc4ec56 authored by Michal Vaner's avatar Michal Vaner
Browse files

Merge #447 (MemoryZone::find)

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@4031 e5f2f494-b856-4b98-b285-d166d9295462
parents 064529e8 dc38532a
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <map>
#include <cassert>
#include <boost/shared_ptr.hpp>
#include <dns/name.h>
......@@ -51,12 +52,98 @@ struct MemoryZone::MemoryZoneImpl {
* that.
*/
typedef map<RRType, ConstRRsetPtr> Domain;
typedef Domain::value_type DomainPair;
typedef boost::shared_ptr<Domain> DomainPtr;
// The tree stores domains
typedef RBTree<Domain> DomainTree;
typedef RBNode<Domain> DomainNode;
// The actual zone data
DomainTree domains_;
/*
* Implementation of longer methods. We put them here, because the
* access is without the impl_-> and it will get inlined anyway.
*/
// Implementation of MemoryZone::add
result::Result add(const ConstRRsetPtr& rrset) {
// Sanitize input
if (!rrset) {
isc_throw(NullRRset, "The rrset provided is NULL");
}
Name name(rrset->getName());
NameComparisonResult compare(origin_.compare(name));
if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
compare.getRelation() != NameComparisonResult::EQUAL)
{
isc_throw(OutOfZone, "The name " << name <<
" is not contained in zone " << origin_);
}
// Get the node
DomainNode* node;
switch (domains_.insert(name, &node)) {
// Just check it returns reasonable results
case DomainTree::SUCCEED:
case DomainTree::ALREADYEXIST:
break;
// Something odd got out
default:
assert(0);
}
assert(node);
// Now get the domain
DomainPtr domain;
// It didn't exist yet, create it
if (node->isEmpty()) {
domain.reset(new Domain);
node->setData(domain);
} else { // Get existing one
domain = node->getData();
}
// Try inserting the rrset there
if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
// Ok, we just put it in
return (result::SUCCESS);
} else {
// The RRSet of given type was already there
return (result::EXIST);
}
}
// Implementation of MemoryZone::find
FindResult find(const Name& name, RRType type) const {
// Get the node
DomainNode* node;
switch (domains_.find(name, &node)) {
case DomainTree::PARTIALMATCH:
// Pretend it was not found for now
// TODO: Implement real delegation. Currently, not having
// the the domain can cause a partialmatch as well, so
// better check.
case DomainTree::NOTFOUND:
return (FindResult(NXDOMAIN, ConstRRsetPtr()));
case DomainTree::EXACTMATCH: // This one is OK, handle it
break;
default:
assert(0);
}
assert(node);
assert(!node->isEmpty());
Domain::const_iterator found(node->getData()->find(type));
if (found != node->getData()->end()) {
// Good, it is here
return (FindResult(SUCCESS, found->second));
} else {
/*
* TODO Look for CNAME and DNAME (it should be OK to do so when
* the value is not found, as CNAME/DNAME domain should be
* empty otherwise.)
*/
return (FindResult(NXRRSET, ConstRRsetPtr()));
}
}
};
MemoryZone::MemoryZone(const RRClass& zone_class, const Name& origin) :
......@@ -79,9 +166,13 @@ MemoryZone::getClass() const {
}
Zone::FindResult
MemoryZone::find(const Name&, const RRType&) const {
// This is a tentative implementation that always returns NXDOMAIN.
return (FindResult(NXDOMAIN, RRsetPtr()));
MemoryZone::find(const Name& name, const RRType& type) const {
return (impl_->find(name, type));
}
result::Result
MemoryZone::add(const ConstRRsetPtr& rrset) {
return (impl_->add(rrset));
}
/// Implementation details for \c MemoryDataSrc hidden from the public
......
......@@ -58,9 +58,45 @@ public:
/// \brief Looks up an RRset in the zone.
///
/// See documentation in \c Zone.
///
/// It returns NULL pointer in case of NXDOMAIN and NXRRSET
/// (the base class documentation does not seem to require that).
virtual FindResult find(const isc::dns::Name& name,
const isc::dns::RRType& type) const;
/// \brief Inserts an rrset into the zone.
///
/// It puts another RRset into the zone.
///
/// It throws NullRRset or OutOfZone if the provided rrset is invalid. It
/// might throw standard allocation exceptions, in which case this function
/// does not guarantee strong exception safety (it is currently not needed,
/// if it is needed in future, it should be implemented).
///
/// \param rrset The set to add.
/// \return SUCCESS or EXIST (if an rrset for given name and type already
/// exists).
result::Result add(const isc::dns::ConstRRsetPtr& rrset);
/// \brief RRSet out of zone exception.
///
/// This is thrown if addition of an RRset that doesn't belong under the
/// zone's origin is requested.
struct OutOfZone : public InvalidParameter {
OutOfZone(const char* file, size_t line, const char* what) :
InvalidParameter(file, line, what)
{ }
};
/// \brief RRset is NULL exception.
///
/// This is thrown if the provided RRset parameter is NULL.
struct NullRRset : public InvalidParameter {
NullRRset(const char* file, size_t line, const char* what) :
InvalidParameter(file, line, what)
{ }
};
private:
/// \name Hidden private data
//@{
......
......@@ -16,6 +16,7 @@
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrttl.h>
#include <datasrc/memory_datasrc.h>
......@@ -136,13 +137,64 @@ public:
MemoryZoneTest() :
class_(RRClass::IN()),
origin_("example.org"),
zone_(class_, origin_)
{ }
ns_name_("ns.example.org"),
zone_(class_, origin_),
rr_out_(new RRset(Name("example.com"), class_, RRType::A(),
RRTTL(300))),
rr_ns_(new RRset(origin_, class_, RRType::NS(), RRTTL(300))),
rr_ns_a_(new RRset(ns_name_, class_, RRType::A(), RRTTL(300))),
rr_ns_aaaa_(new RRset(ns_name_, class_, RRType::AAAA(), RRTTL(300))),
rr_a_(new RRset(origin_, class_, RRType::A(), RRTTL(300)))
{
}
// Some data to test with
RRClass class_;
Name origin_;
Name origin_, ns_name_;
// The zone to torture by tests
MemoryZone zone_;
/*
* Some RRsets to put inside the zone.
* They are empty, but the MemoryZone does not have a reason to look
* inside anyway. We will check it finds them and does not change
* the pointer.
*/
RRsetPtr
// Out of zone RRset
rr_out_,
// NS of example.org
rr_ns_,
// A of ns.example.org
rr_ns_a_,
// AAAA of ns.example.org
rr_ns_aaaa_,
// A of example.org
rr_a_;
/**
* \brief Test one find query to the zone.
*
* Asks a query to the zone and checks it does not throw and returns
* expected results. It returns nothing, it just signals failures
* to GTEST.
*
* \param name The name to ask for.
* \param rrtype The RRType to ask of.
* \param result The expected code of the result.
* \param answer The expected rrset, if any should be returned.
*/
void findTest(const Name& name, const RRType& rrtype, Zone::Result result,
const ConstRRsetPtr& answer = ConstRRsetPtr())
{
// The whole block is inside, because we need to check the result and
// we can't assign to FindResult
EXPECT_NO_THROW({
Zone::FindResult find_result(zone_.find(name, rrtype));
// Check it returns correct answers
EXPECT_EQ(result, find_result.code);
EXPECT_EQ(answer, find_result.rrset);
});
}
};
/**
......@@ -151,8 +203,61 @@ public:
* Takes the created zone and checks its properties they are the same
* as passed parameters.
*/
TEST_F(MemoryZoneTest, Constructor) {
TEST_F(MemoryZoneTest, constructor) {
ASSERT_EQ(class_, zone_.getClass());
ASSERT_EQ(origin_, zone_.getOrigin());
}
/**
* \brief Test adding.
*
* We test that it throws at the correct moments and the correct exceptions.
* And we test the return value.
*/
TEST_F(MemoryZoneTest, add) {
// This one does not belong to this zone
EXPECT_THROW(zone_.add(rr_out_), MemoryZone::OutOfZone);
// Test null pointer
EXPECT_THROW(zone_.add(ConstRRsetPtr()), MemoryZone::NullRRset);
using namespace result; // Who should write the prefix all the time
// Now put all the data we have there. It should throw nothing
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_)));
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_aaaa_)));
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_)));
// Try putting there something twice, it should be rejected
EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_)));
EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_a_)));
}
/**
* \brief Test searching.
*
* Check it finds or does not find correctly and does not throw exceptions.
* \todo This doesn't do any kind of CNAME and so on. If it isn't
* directly there, it just tells it doesn't exist.
*/
TEST_F(MemoryZoneTest, find) {
// Fill some data inside
using namespace result; // Who should write the prefix all the time
// Now put all the data we have there. It should throw nothing
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_)));
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_aaaa_)));
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_)));
// These two should be successful
findTest(origin_, RRType::NS(), Zone::SUCCESS, rr_ns_);
findTest(ns_name_, RRType::A(), Zone::SUCCESS, rr_ns_a_);
// These domain exist but don't have the provided RRType
findTest(origin_, RRType::AAAA(), Zone::NXRRSET);
findTest(ns_name_, RRType::NS(), Zone::NXRRSET);
// These domains don't exist (and one is out of the zone)
findTest(Name("nothere.example.org"), RRType::A(), Zone::NXDOMAIN);
findTest(Name("example.net"), RRType::A(), Zone::NXDOMAIN);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment