Browse Source

[2433] Merge remote-tracking branch 'origin/trac2432' into trac2433

JINMEI Tatuya 12 years ago
parent
commit
0e90f47824

+ 2 - 0
src/lib/dns/Makefile.am

@@ -127,6 +127,8 @@ libb10_dns___la_SOURCES += tsigrecord.h tsigrecord.cc
 libb10_dns___la_SOURCES += character_string.h character_string.cc
 libb10_dns___la_SOURCES += master_loader_callbacks.h master_loader_callbacks.cc
 libb10_dns___la_SOURCES += master_loader.h
+libb10_dns___la_SOURCES += rrset_collection_base.h
+libb10_dns___la_SOURCES += rrset_collection.h rrset_collection.cc
 libb10_dns___la_SOURCES += rdata/generic/detail/char_string.h
 libb10_dns___la_SOURCES += rdata/generic/detail/char_string.cc
 libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h

+ 126 - 0
src/lib/dns/rrset_collection.cc

@@ -0,0 +1,126 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/rrset_collection.h>
+#include <dns/master_loader_callbacks.h>
+#include <dns/master_loader.h>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/bind.hpp>
+
+using namespace isc;
+
+namespace isc {
+namespace dns {
+
+void
+RRsetCollection::loaderCallback(const std::string&, size_t, const std::string&)
+{
+     // We just ignore callbacks for errors and warnings.
+}
+
+void
+RRsetCollection::addRRset(const Name& name, const RRClass& rrclass,
+                          const RRType& rrtype, const RRTTL& rrttl,
+                          const rdata::RdataPtr& data)
+{
+    RRsetPtr rrset(new BasicRRset(name, rrclass, rrtype, rrttl));
+    rrset->addRdata(data);
+    addRRset(rrset);
+}
+
+void
+RRsetCollection::addRRset(RRsetPtr rrset) {
+    const CollectionKey key(rrset->getClass(), rrset->getType(),
+                            rrset->getName());
+    CollectionMap::const_iterator it = rrsets_.find(key);
+    if (it != rrsets_.end()) {
+        isc_throw(InvalidParameter,
+                  "RRset for " << rrset->getName() << "/" << rrset->getClass()
+                  << " with type " << rrset->getType() << " already exists");
+    }
+
+    rrsets_.insert(std::pair<CollectionKey, RRsetPtr>(key, rrset));
+}
+
+RRsetCollection::RRsetCollection(const char* filename, const Name& origin,
+                                 const RRClass& rrclass)
+{
+    MasterLoaderCallbacks callbacks
+        (boost::bind(&RRsetCollection::loaderCallback, this, _1, _2, _3),
+         boost::bind(&RRsetCollection::loaderCallback, this, _1, _2, _3));
+    MasterLoader loader(filename, origin, rrclass, callbacks,
+                        boost::bind(&RRsetCollection::addRRset,
+                                    this, _1, _2, _3, _4, _5),
+                        MasterLoader::DEFAULT);
+    loader.load();
+}
+
+const AbstractRRset*
+RRsetCollection::find(const Name& name, const RRType& rrtype,
+                      const RRClass& rrclass) const {
+    const CollectionKey key(rrclass, rrtype, name);
+    CollectionMap::const_iterator it = rrsets_.find(key);
+    if (it != rrsets_.end()) {
+        return (&(*it->second));
+    }
+    return (NULL);
+}
+
+RRsetPtr
+RRsetCollection::find(const Name& name, const RRClass& rrclass,
+                      const RRType& rrtype) {
+    const CollectionKey key(rrclass, rrtype, name);
+    CollectionMap::iterator it = rrsets_.find(key);
+    if (it != rrsets_.end()) {
+        return (it->second);
+    }
+    return (RRsetPtr());
+}
+
+ConstRRsetPtr
+RRsetCollection::find(const Name& name, const RRClass& rrclass,
+                      const RRType& rrtype) const
+{
+    const CollectionKey key(rrclass, rrtype, name);
+    CollectionMap::const_iterator it = rrsets_.find(key);
+    if (it != rrsets_.end()) {
+        return (it->second);
+    }
+    return (ConstRRsetPtr());
+}
+
+void
+RRsetCollection::removeRRset(const Name& name, const RRClass& rrclass,
+                             const RRType& rrtype)
+{
+    const CollectionKey key(rrclass, rrtype, name);
+    rrsets_.erase(key);
+}
+
+RRsetCollectionBase::IterPtr
+RRsetCollection::getBeginning() {
+    CollectionMap::iterator it = rrsets_.begin();
+    return (RRsetCollectionBase::IterPtr(new DnsIter(it)));
+}
+
+RRsetCollectionBase::IterPtr
+RRsetCollection::getEnd() {
+    CollectionMap::iterator it = rrsets_.end();
+    return (RRsetCollectionBase::IterPtr(new DnsIter(it)));
+}
+
+} // end of namespace dns
+} // end of namespace isc

+ 142 - 0
src/lib/dns/rrset_collection.h

@@ -0,0 +1,142 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef RRSET_COLLECTION_H
+#define RRSET_COLLECTION_H 1
+
+#include <dns/rrset_collection_base.h>
+#include <dns/rrclass.h>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+
+#include <map>
+
+namespace isc {
+namespace dns {
+
+/// \brief libdns++ implementation of RRsetCollectionBase using an STL
+/// container.
+class RRsetCollection : public RRsetCollectionBase {
+public:
+    /// \brief Constructor.
+    RRsetCollection()
+    {}
+
+    /// \brief Constructor.
+    ///
+    /// The \c origin and \c rrclass arguments are required for the zone
+    /// loading, but \c RRsetCollection itself does not do any
+    /// validation, and the collection of RRsets does not have to form a
+    /// valid zone. The constructor throws MasterLoaderError if there is
+    /// an error during loading.
+    ///
+    /// \param filename Name of a file containing a collection of RRs in
+    /// the master file format (which may or may not form a valid zone).
+    RRsetCollection(const char* filename, const isc::dns::Name& origin,
+                    const isc::dns::RRClass& rrclass);
+
+    /// \brief Add an RRset to the collection.
+    ///
+    /// Does not do any validation whether \c rrset belongs to a
+    /// particular zone or not. It throws an \c isc::InvalidParameter
+    /// exception if an rrset with the same class, type and name already
+    /// exists.
+    void addRRset(isc::dns::RRsetPtr rrset);
+
+    /// \brief Remove an RRset from the collection.
+    ///
+    /// RRset(s) matching the \c name, \c rrclass and \c rrtype are
+    /// removed from the collection.
+    void removeRRset(const isc::dns::Name& name,
+                     const isc::dns::RRClass& rrclass,
+                     const isc::dns::RRType& rrtype);
+
+    /// \brief Find a matching RRset in the collection.
+    ///
+    /// Returns the RRset in the collection that exactly matches the
+    /// given \c name and \c rrtype.  If no matching RRset is found,
+    /// \c NULL is returned.
+    ///
+    /// \param name The name of the RRset to search for.
+    /// \param rrtype The type of the RRset to search for.
+    /// \param rrclass The class of the RRset to search for.
+    /// \returns A pointer to the RRset if found, \c NULL otherwise.
+    virtual const isc::dns::AbstractRRset* find
+        (const isc::dns::Name& name, const isc::dns::RRType& rrtype,
+         const isc::dns::RRClass& rrclass)
+        const;
+
+    isc::dns::RRsetPtr find(const isc::dns::Name& name,
+                            const isc::dns::RRClass& rrclass,
+                            const isc::dns::RRType& rrtype);
+
+    isc::dns::ConstRRsetPtr find(const isc::dns::Name& name,
+                                 const isc::dns::RRClass& rrclass,
+                                 const isc::dns::RRType& rrtype) const;
+
+private:
+    void addRRset(const isc::dns::Name& name, const isc::dns::RRClass& rrclass,
+                  const isc::dns::RRType& rrtype, const isc::dns::RRTTL& rrttl,
+                  const isc::dns::rdata::RdataPtr& data);
+    void loaderCallback(const std::string&, size_t, const std::string&);
+
+    typedef boost::tuple<isc::dns::RRClass, isc::dns::RRType, isc::dns::Name>
+        CollectionKey;
+    typedef std::map<CollectionKey, isc::dns::RRsetPtr> CollectionMap;
+
+    CollectionMap rrsets_;
+
+protected:
+    class DnsIter : public RRsetCollectionBase::Iter {
+    public:
+        DnsIter(CollectionMap::iterator& iter) :
+            iter_(iter)
+        {}
+
+        virtual const isc::dns::AbstractRRset& getValue() {
+            isc::dns::RRsetPtr& rrset = iter_->second;
+            return (*rrset);
+        }
+
+        virtual IterPtr getNext() {
+            CollectionMap::iterator it = iter_;
+            it++;
+            return (RRsetCollectionBase::IterPtr(new DnsIter(it)));
+        }
+
+        virtual bool equals(Iter& other) {
+            const DnsIter* other_real = dynamic_cast<DnsIter*>(&other);
+            if (other_real == NULL) {
+                return (false);
+            }
+            return (iter_ == other_real->iter_);
+        }
+
+    private:
+        CollectionMap::iterator iter_;
+    };
+
+    virtual RRsetCollectionBase::IterPtr getBeginning();
+    virtual RRsetCollectionBase::IterPtr getEnd();
+};
+
+} // end of namespace dns
+} // end of namespace isc
+
+#endif  // RRSET_COLLECTION_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 122 - 0
src/lib/dns/rrset_collection_base.h

@@ -0,0 +1,122 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef RRSET_COLLECTION_BASE_H
+#define RRSET_COLLECTION_BASE_H 1
+
+#include <dns/rrset.h>
+#include <dns/name.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <iterator>
+
+namespace isc {
+namespace dns {
+
+/// \brief Generic class to represent a set of RRsets.
+///
+/// This is a generic container and the stored set of RRsets does not
+/// necessarily form a valid zone (e.g. there doesn't necessarily have
+/// to be an SOA at the "origin"). Instead, it will be used to represent
+/// a single zone for the purpose of zone loading/checking. It provides
+/// a simple find() method to find an RRset for the given name and type
+/// (and maybe class) and a way to iterate over all RRsets.
+///
+/// See \c RRsetCollection for a simple libdns++ implementation using an
+/// STL container. libdatasrc will have another implementation.
+class RRsetCollectionBase {
+public:
+    /// \brief Find a matching RRset in the collection.
+    ///
+    /// Returns the RRset in the collection that exactly matches the
+    /// given \c name and \c rrtype.  If no matching RRset is found,
+    /// \c NULL is returned.
+    ///
+    /// \param name The name of the RRset to search for.
+    /// \param rrtype The type of the RRset to search for.
+    /// \param rrclass The class of the RRset to search for.
+    /// \returns A pointer to the RRset if found, \c NULL otherwise.
+    virtual const isc::dns::AbstractRRset* find
+        (const isc::dns::Name& name, const isc::dns::RRType& rrtype,
+	 const isc::dns::RRClass& rrclass)
+        const = 0;
+
+protected:
+    class Iter; // forward declaration
+    typedef boost::shared_ptr<Iter> IterPtr;
+
+    class Iter {
+    public:
+        virtual const isc::dns::AbstractRRset& getValue() = 0;
+        virtual IterPtr getNext() = 0;
+        virtual bool equals(Iter& other) = 0;
+    };
+
+    virtual IterPtr getBeginning() = 0;
+    virtual IterPtr getEnd() = 0;
+
+public:
+    class iterator : std::iterator<std::forward_iterator_tag,
+                                   const isc::dns::AbstractRRset>
+    {
+    public:
+        explicit iterator(IterPtr iter) :
+            iter_(iter)
+        {}
+
+        reference operator*() {
+            return (iter_->getValue());
+        }
+
+        iterator& operator++() {
+            iter_ = iter_->getNext();
+            return (*this);
+        }
+
+        iterator operator++(int) {
+            iterator tmp(iter_);
+            ++*this;
+            return (tmp);
+        }
+
+        bool operator==(const iterator& other) const {
+            return (iter_->equals(*other.iter_));
+        }
+
+        bool operator!=(const iterator& other) const {
+            return (!iter_->equals(*other.iter_));
+        }
+
+    private:
+        IterPtr iter_;
+    };
+
+    iterator begin() {
+      return iterator(getBeginning());
+    }
+
+    iterator end() {
+      return iterator(getEnd());
+    }
+};
+
+} // end of namespace dns
+} // end of namespace isc
+
+#endif  // RRSET_COLLECTION_BASE_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 1 - 0
src/lib/dns/tests/Makefile.am

@@ -75,6 +75,7 @@ run_unittests_SOURCES += tsigkey_unittest.cc
 run_unittests_SOURCES += tsigrecord_unittest.cc
 run_unittests_SOURCES += character_string_unittest.cc
 run_unittests_SOURCES += master_loader_callbacks_test.cc
+run_unittests_SOURCES += rrset_collection_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 # We shouldn't need to include BOTAN_LIBS here, but there

+ 256 - 0
src/lib/dns/tests/rrset_collection_unittest.cc

@@ -0,0 +1,256 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/rrset_collection.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
+
+#include <gtest/gtest.h>
+
+#include <list>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace std;
+
+namespace {
+
+class RRsetCollectionTest : public ::testing::Test {
+public:
+    RRsetCollectionTest() :
+        rrclass("IN"),
+        origin("example.org"),
+        collection(TEST_DATA_SRCDIR "/example.org", origin, rrclass)
+    {}
+
+    const RRClass rrclass;
+    const Name origin;
+    RRsetCollection collection;
+};
+
+TEST_F(RRsetCollectionTest, findBase) {
+    // Test the find() that returns isc::dns::AbstractRRset*
+    const AbstractRRset* rrset = collection.find(Name("www.example.org"),
+                                                 RRType::A(), rrclass);
+    EXPECT_NE(static_cast<AbstractRRset*>(NULL), rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRTTL(3600), rrset->getTTL());
+    EXPECT_EQ(RRClass("IN"), rrset->getClass());
+    EXPECT_EQ(Name("www.example.org"), rrset->getName());
+
+    // foo.example.org doesn't exist
+    rrset = collection.find(Name("foo.example.org"), RRType::A(), rrclass);
+    EXPECT_EQ(static_cast<AbstractRRset*>(NULL), rrset);
+
+    // www.example.org exists, but not with MX
+    rrset = collection.find(Name("www.example.org"), RRType::MX(), rrclass);
+    EXPECT_EQ(static_cast<AbstractRRset*>(NULL), rrset);
+
+    // www.example.org exists, with AAAA
+    rrset = collection.find(Name("www.example.org"), RRType::AAAA(), rrclass);
+    EXPECT_NE(static_cast<AbstractRRset*>(NULL), rrset);
+
+    // www.example.org with AAAA does not exist in RRClass::CH()
+    rrset = collection.find(Name("www.example.org"), RRType::AAAA(),
+                            RRClass::CH());
+    EXPECT_EQ(static_cast<AbstractRRset*>(NULL), rrset);
+}
+
+template <typename T, typename TP>
+void doFind(T& collection, const RRClass& rrclass) {
+    // Test the find() that returns ConstRRsetPtr
+    TP rrset = collection.find(Name("www.example.org"), rrclass, RRType::A());
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRTTL(3600), rrset->getTTL());
+    EXPECT_EQ(RRClass("IN"), rrset->getClass());
+    EXPECT_EQ(Name("www.example.org"), rrset->getName());
+
+    // foo.example.org doesn't exist
+    rrset = collection.find(Name("foo.example.org"), rrclass, RRType::A());
+    EXPECT_FALSE(rrset);
+
+    // www.example.org exists, but not with MX
+    rrset = collection.find(Name("www.example.org"), rrclass, RRType::MX());
+    EXPECT_FALSE(rrset);
+
+    // www.example.org exists, with AAAA
+    rrset = collection.find(Name("www.example.org"), rrclass, RRType::AAAA());
+    EXPECT_TRUE(rrset);
+
+    // www.example.org with AAAA does not exist in RRClass::CH()
+    rrset = collection.find(Name("www.example.org"), RRClass::CH(),
+                            RRType::AAAA());
+    EXPECT_FALSE(rrset);
+}
+
+TEST_F(RRsetCollectionTest, findConst) {
+    // Test the find() that returns ConstRRsetPtr
+    const RRsetCollection& ccln = collection;
+    doFind<const RRsetCollection, ConstRRsetPtr>(ccln, rrclass);
+}
+
+TEST_F(RRsetCollectionTest, find) {
+    // Test the find() that returns RRsetPtr
+    doFind<RRsetCollection, RRsetPtr>(collection, rrclass);
+}
+
+void
+doAddAndRemove(RRsetCollection& collection, const RRClass& rrclass) {
+    // foo.example.org/A doesn't exist
+    RRsetPtr rrset_found = collection.find(Name("foo.example.org"), rrclass,
+                                           RRType::A());
+    EXPECT_FALSE(rrset_found);
+
+    // Add foo.example.org/A
+    RRsetPtr rrset(new BasicRRset(Name("foo.example.org"), rrclass, RRType::A(),
+                                  RRTTL(7200)));
+    rrset->addRdata(in::A("192.0.2.1"));
+    collection.addRRset(rrset);
+
+    // foo.example.org/A should now exist
+    rrset_found = collection.find(Name("foo.example.org"), rrclass,
+                                  RRType::A());
+    EXPECT_TRUE(rrset_found);
+    EXPECT_EQ(RRType::A(), rrset_found->getType());
+    EXPECT_EQ(RRTTL(7200), rrset_found->getTTL());
+    EXPECT_EQ(RRClass("IN"), rrset_found->getClass());
+    EXPECT_EQ(Name("foo.example.org"), rrset_found->getName());
+
+    // The collection must not be empty.
+    EXPECT_NE(collection.end(), collection.begin());
+
+    // Adding a duplicate RRset must throw.
+    EXPECT_THROW({
+        collection.addRRset(rrset);
+    }, isc::InvalidParameter);
+
+    // Remove foo.example.org/A
+    collection.removeRRset(Name("foo.example.org"), rrclass, RRType::A());
+
+    // foo.example.org/A should not exist now
+    rrset_found = collection.find(Name("foo.example.org"), rrclass,
+                                  RRType::A());
+    EXPECT_FALSE(rrset_found);
+}
+
+TEST_F(RRsetCollectionTest, addAndRemove) {
+    doAddAndRemove(collection, rrclass);
+}
+
+TEST_F(RRsetCollectionTest, empty) {
+    RRsetCollection cln;
+
+    // Here, cln is empty.
+    EXPECT_EQ(cln.end(), cln.begin());
+
+    doAddAndRemove(cln, rrclass);
+
+    // cln should be empty again here, after the add and remove
+    // operations.
+    EXPECT_EQ(cln.end(), cln.begin());
+}
+
+TEST_F(RRsetCollectionTest, iteratorTest) {
+    // The collection must not be empty.
+    EXPECT_NE(collection.end(), collection.begin());
+
+    // Here, we just count the records and do some basic tests on them.
+    size_t count = 0;
+    for (RRsetCollection::iterator it = collection.begin();
+         it != collection.end(); ++it) {
+         ++count;
+         const AbstractRRset& rrset = *it;
+         EXPECT_EQ(rrclass, rrset.getClass());
+         EXPECT_EQ(RRTTL(3600), rrset.getTTL());
+    }
+
+    // example.org master file has SOA, NS, A, AAAA
+    EXPECT_EQ(4, count);
+}
+
+// This is a dummy class which is used in iteratorCompareDifferent test
+// to compare iterators from different RRsetCollectionBase
+// implementations.
+class MyRRsetCollection : public RRsetCollectionBase {
+public:
+    MyRRsetCollection()
+    {}
+
+    virtual const isc::dns::AbstractRRset* find
+        (const isc::dns::Name&, const isc::dns::RRType&,
+         const isc::dns::RRClass&)
+        const
+    {
+        return (NULL);
+    }
+
+    typedef std::list<isc::dns::RRset> MyCollection;
+
+protected:
+    class MyIter : public RRsetCollectionBase::Iter {
+    public:
+        MyIter(MyCollection::iterator& iter) :
+            iter_(iter)
+        {}
+
+        virtual const isc::dns::AbstractRRset& getValue() {
+            return (*iter_);
+        }
+
+        virtual IterPtr getNext() {
+            MyCollection::iterator it = iter_;
+            it++;
+            return (RRsetCollectionBase::IterPtr(new MyIter(it)));
+        }
+
+        virtual bool equals(Iter& other) {
+            const MyIter* other_real = dynamic_cast<MyIter*>(&other);
+            if (other_real == NULL) {
+                return (false);
+            }
+            return (iter_ == other_real->iter_);
+        }
+
+    private:
+        MyCollection::iterator iter_;
+    };
+
+    virtual RRsetCollectionBase::IterPtr getBeginning() {
+        MyCollection::iterator it = dummy_list_.begin();
+        return (RRsetCollectionBase::IterPtr(new MyIter(it)));
+    }
+
+    virtual RRsetCollectionBase::IterPtr getEnd() {
+        MyCollection::iterator it = dummy_list_.end();
+        return (RRsetCollectionBase::IterPtr(new MyIter(it)));
+    }
+
+private:
+    MyCollection dummy_list_;
+};
+
+TEST_F(RRsetCollectionTest, iteratorCompareDifferent) {
+    // Create objects of two different RRsetCollectionBase
+    // implementations.
+    RRsetCollection cln1;
+    MyRRsetCollection cln2;
+
+    // Comparing two iterators from different RRsetCollectionBase
+    // implementations must not throw.
+    EXPECT_NE(cln2.begin(), cln1.begin());
+    EXPECT_NE(cln1.end(), cln2.end());
+}
+
+} // namespace