Browse Source

Merge #447 (MemoryZone::find)

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@4031 e5f2f494-b856-4b98-b285-d166d9295462
Michal Vaner 14 years ago
parent
commit
ddc4ec56ef

+ 94 - 3
src/lib/datasrc/memory_datasrc.cc

@@ -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

+ 36 - 0
src/lib/datasrc/memory_datasrc.h

@@ -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
     //@{

+ 109 - 4
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -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);
+}
+
 }