Parcourir la source

merged trac #418: a second (or 1.5th) step of the query logic for in memory data source.

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@3749 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya il y a 14 ans
Parent
commit
ebd39d1145

+ 2 - 2
src/bin/auth/tests/query_unittest.cc

@@ -55,7 +55,7 @@ TEST_F(QueryTest, noZone) {
 TEST_F(QueryTest, matchZone) {
 TEST_F(QueryTest, matchZone) {
     // add a matching zone.  since the zone is empty right now, the response
     // add a matching zone.  since the zone is empty right now, the response
     // should have NXDOMAIN.
     // should have NXDOMAIN.
-    zone_table.add(ZonePtr(new Zone(qclass, Name("example.com"))));
+    zone_table.add(ZonePtr(new MemoryZone(qclass, Name("example.com"))));
     query.process();
     query.process();
     EXPECT_EQ(Rcode::NXDOMAIN(), response.getRcode());
     EXPECT_EQ(Rcode::NXDOMAIN(), response.getRcode());
 }
 }
@@ -63,7 +63,7 @@ TEST_F(QueryTest, matchZone) {
 TEST_F(QueryTest, noMatchZone) {
 TEST_F(QueryTest, noMatchZone) {
     // there's a zone in the table but it doesn't match the qname.  should
     // there's a zone in the table but it doesn't match the qname.  should
     // result in SERVFAIL.
     // result in SERVFAIL.
-    zone_table.add(ZonePtr(new Zone(qclass, Name("example.org"))));
+    zone_table.add(ZonePtr(new MemoryZone(qclass, Name("example.org"))));
     query.process();
     query.process();
     EXPECT_EQ(Rcode::SERVFAIL(), response.getRcode());
     EXPECT_EQ(Rcode::SERVFAIL(), response.getRcode());
 }
 }

+ 17 - 8
src/lib/datasrc/tests/zonetable_unittest.cc

@@ -26,20 +26,28 @@ using namespace isc::datasrc;
 
 
 namespace {
 namespace {
 TEST(ZoneTest, init) {
 TEST(ZoneTest, init) {
-    Zone zone(RRClass::IN(), Name("example.com"));
+    MemoryZone zone(RRClass::IN(), Name("example.com"));
     EXPECT_EQ(Name("example.com"), zone.getOrigin());
     EXPECT_EQ(Name("example.com"), zone.getOrigin());
     EXPECT_EQ(RRClass::IN(), zone.getClass());
     EXPECT_EQ(RRClass::IN(), zone.getClass());
 
 
-    Zone ch_zone(RRClass::CH(), Name("example"));
+    MemoryZone ch_zone(RRClass::CH(), Name("example"));
     EXPECT_EQ(Name("example"), ch_zone.getOrigin());
     EXPECT_EQ(Name("example"), ch_zone.getOrigin());
     EXPECT_EQ(RRClass::CH(), ch_zone.getClass());
     EXPECT_EQ(RRClass::CH(), ch_zone.getClass());
 }
 }
 
 
+TEST(ZoneTest, find) {
+    MemoryZone zone(RRClass::IN(), Name("example.com"));
+    EXPECT_EQ(Zone::NXDOMAIN,
+              zone.find(Name("www.example.com"), RRType::A()).code);
+}
+
 class ZoneTableTest : public ::testing::Test {
 class ZoneTableTest : public ::testing::Test {
 protected:
 protected:
-    ZoneTableTest() : zone1(new Zone(RRClass::IN(), Name("example.com"))),
-                      zone2(new Zone(RRClass::IN(), Name("example.net"))),
-                      zone3(new Zone(RRClass::IN(), Name("example")))
+    ZoneTableTest() : zone1(new MemoryZone(RRClass::IN(),
+                                           Name("example.com"))),
+                      zone2(new MemoryZone(RRClass::IN(),
+                                           Name("example.net"))),
+                      zone3(new MemoryZone(RRClass::IN(), Name("example")))
     {}
     {}
     ZoneTable zone_table;
     ZoneTable zone_table;
     ZonePtr zone1, zone2, zone3;
     ZonePtr zone1, zone2, zone3;
@@ -50,7 +58,7 @@ TEST_F(ZoneTableTest, add) {
     EXPECT_EQ(ZoneTable::EXIST, zone_table.add(zone1));
     EXPECT_EQ(ZoneTable::EXIST, zone_table.add(zone1));
     // names are compared in a case insensitive manner.
     // names are compared in a case insensitive manner.
     EXPECT_EQ(ZoneTable::EXIST, zone_table.add(
     EXPECT_EQ(ZoneTable::EXIST, zone_table.add(
-                  ZonePtr(new Zone(RRClass::IN(), Name("EXAMPLE.COM")))));
+                  ZonePtr(new MemoryZone(RRClass::IN(), Name("EXAMPLE.COM")))));
 
 
     EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone2));
     EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone2));
     EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone3));
     EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone3));
@@ -58,7 +66,8 @@ TEST_F(ZoneTableTest, add) {
     // Zone table is indexed only by name.  Duplicate origin name with
     // Zone table is indexed only by name.  Duplicate origin name with
     // different zone class isn't allowed.
     // different zone class isn't allowed.
     EXPECT_EQ(ZoneTable::EXIST, zone_table.add(
     EXPECT_EQ(ZoneTable::EXIST, zone_table.add(
-                  ZonePtr(new Zone(RRClass::CH(), Name("example.com")))));
+                  ZonePtr(new MemoryZone(RRClass::CH(),
+                                         Name("example.com")))));
 
 
     /// Bogus zone (NULL)
     /// Bogus zone (NULL)
     EXPECT_THROW(zone_table.add(ZonePtr()), isc::InvalidParameter);
     EXPECT_THROW(zone_table.add(ZonePtr()), isc::InvalidParameter);
@@ -96,7 +105,7 @@ TEST_F(ZoneTableTest, find) {
 
 
     // make sure the partial match is indeed the longest match by adding
     // make sure the partial match is indeed the longest match by adding
     // a zone with a shorter origin and query again.
     // a zone with a shorter origin and query again.
-    ZonePtr zone_com(new Zone(RRClass::IN(), Name("com")));
+    ZonePtr zone_com(new MemoryZone(RRClass::IN(), Name("com")));
     EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone_com));
     EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone_com));
     EXPECT_EQ(Name("example.com"),
     EXPECT_EQ(Name("example.com"),
               zone_table.find(Name("www.example.com")).zone->getOrigin());
               zone_table.find(Name("www.example.com")).zone->getOrigin());

+ 14 - 7
src/lib/datasrc/zonetable.cc

@@ -28,32 +28,39 @@ using namespace isc::dns;
 namespace isc {
 namespace isc {
 namespace datasrc {
 namespace datasrc {
 
 
-struct Zone::ZoneImpl {
-    ZoneImpl(const RRClass& zone_class, const Name& origin) :
+struct MemoryZone::MemoryZoneImpl {
+    MemoryZoneImpl(const RRClass& zone_class, const Name& origin) :
         zone_class_(zone_class), origin_(origin)
         zone_class_(zone_class), origin_(origin)
     {}
     {}
     RRClass zone_class_;
     RRClass zone_class_;
     Name origin_;
     Name origin_;
 };
 };
 
 
-Zone::Zone(const RRClass& zone_class, const Name& origin) : impl_(NULL) {
-    impl_ = new ZoneImpl(zone_class, origin);
+MemoryZone::MemoryZone(const RRClass& zone_class, const Name& origin) :
+    impl_(new MemoryZoneImpl(zone_class, origin))
+{
 }
 }
 
 
-Zone::~Zone() {
+MemoryZone::~MemoryZone() {
     delete impl_;
     delete impl_;
 }
 }
 
 
 const Name&
 const Name&
-Zone::getOrigin() const {
+MemoryZone::getOrigin() const {
     return (impl_->origin_);
     return (impl_->origin_);
 }
 }
 
 
 const RRClass&
 const RRClass&
-Zone::getClass() const {
+MemoryZone::getClass() const {
     return (impl_->zone_class_);
     return (impl_->zone_class_);
 }
 }
 
 
+Zone::FindResult
+MemoryZone::find(const Name&, const RRType&) const {
+    // This is a tentative implementation that always returns NXDOMAIN.
+    return (FindResult(NXDOMAIN, RRsetPtr()));
+}
+
 // This is a temporary, inefficient implementation using std::map and handmade
 // This is a temporary, inefficient implementation using std::map and handmade
 // iteration to realize longest match.
 // iteration to realize longest match.
 
 

+ 162 - 27
src/lib/datasrc/zonetable.h

@@ -17,6 +17,8 @@
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
+#include <dns/rrset.h>
+
 namespace isc {
 namespace isc {
 namespace dns {
 namespace dns {
 class Name;
 class Name;
@@ -25,19 +27,165 @@ class RRClass;
 
 
 namespace datasrc {
 namespace datasrc {
 
 
-/// \brief A single authoritative zone
+/// \brief The base class for a single authoritative zone
 ///
 ///
-/// The \c Zone class represents a DNS zone as part of %data source.
+/// The \c Zone class is an abstract base class for representing
+/// a DNS zone as part of data source.
 ///
 ///
 /// At the moment this is provided mainly for making the \c ZoneTable class
 /// At the moment this is provided mainly for making the \c ZoneTable class
-/// testable, and only provides a minimal set of features.
+/// and the authoritative query logic  testable, and only provides a minimal
+/// set of features.
 /// This is why this class is defined in the same header file, but it may
 /// This is why this class is defined in the same header file, but it may
 /// have to move to a separate header file when we understand what is
 /// have to move to a separate header file when we understand what is
 /// necessary for this class for actual operation.
 /// necessary for this class for actual operation.
-/// Likewise, it will have more features.  For example, it will maintain
+///
+/// The idea is to provide a specific derived zone class for each data
+/// source, beginning with in memory one.  At that point the derived classes
+/// will have more specific features.  For example, they will maintain
 /// information about the location of a zone file, whether it's loaded in
 /// information about the location of a zone file, whether it's loaded in
 /// memory, etc.
 /// memory, etc.
+///
+/// It's not yet clear how the derived zone classes work with various other
+/// data sources when we integrate these components, but one possibility is
+/// something like this:
+/// - If the underlying database such as some variant of SQL doesn't have an
+///   explicit representation of zones (as part of public interface), we can
+///   probably use a "default" zone class that simply encapsulates the
+///   corresponding data source and calls a common "find" like method.
+/// - Some data source may want to specialize it by inheritance as an
+///   optimization.  For example, in the current schema design of the sqlite3
+///   data source, its (derived) zone class would contain the information of
+///   the "zone ID".
+///
+/// <b>Note:</b> Unlike some other abstract base classes we don't name the
+/// class beginning with "Abstract".  This is because we want to have
+/// commonly used definitions such as \c Result and \c ZonePtr, and we want
+/// to make them look more intuitive.
 class Zone {
 class Zone {
+public:
+    /// Result codes of the \c find() method.
+    ///
+    /// Note: the codes are tentative.  We may need more, or we may find
+    /// some of them unnecessary as we implement more details.
+    enum Result {
+        SUCCESS,                ///< An exact match is found.
+        DELEGATION,             ///< The search encounters a zone cut.
+        NXDOMAIN, ///< There is no domain name that matches the search name
+        NXRRSET,  ///< There is a matching name but no RRset of the search type
+        CNAME,    ///< The search encounters and returns a CNAME RR
+        DNAME     ///< The search encounters and returns a DNAME RR
+    };
+
+    /// A helper structure to represent the search result of \c find().
+    ///
+    /// This is a straightforward tuple of the result code and a pointer
+    /// to the found RRset to represent the result of \c find()
+    /// (there will be more members in the future - see the class
+    /// description).
+    /// We use this in order to avoid overloading the return value for both
+    /// the result code ("success" or "not found") and the found object,
+    /// i.e., avoid using \c NULL to mean "not found", etc.
+    ///
+    /// This is a simple value class whose internal state never changes,
+    /// so for convenience we allow the applications to refer to the members
+    /// directly.
+    ///
+    /// Note: we should eventually include a notion of "zone node", which
+    /// corresponds to a particular domain name of the zone, so that we can
+    /// find RRsets of a different RR type for that name (e.g. for type ANY
+    /// query or to include DS RRs with delegation).
+    ///
+    /// Note: we may also want to include the closest enclosure "node" to
+    /// optimize including the NSEC for no-wildcard proof (FWIW NSD does that).
+    struct FindResult {
+        FindResult(Result param_code,
+                   const isc::dns::ConstRRsetPtr param_rrset) :
+            code(param_code), rrset(param_rrset)
+        {}
+        const Result code;
+        const isc::dns::ConstRRsetPtr rrset;
+    };
+
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    //@{
+protected:
+    /// The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class should
+    /// never be instantiated (except as part of a derived class).
+    Zone() {}
+public:
+    /// The destructor.
+    virtual ~Zone() {}
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    /// These methods should never throw an exception.
+    //@{
+    /// Return the origin name of the zone.
+    virtual const isc::dns::Name& getOrigin() const = 0;
+
+    /// Return the RR class of the zone.
+    virtual const isc::dns::RRClass& getClass() const = 0;
+    //@}
+
+    ///
+    /// \name Search Method
+    ///
+    //@{
+    /// Search the zone for a given pair of domain name and RR type.
+    ///
+    /// Each derived version of this method searches the underlying backend
+    /// for the data that best matches the given name and type.
+    /// This method is expected to be "intelligent", and identifies the
+    /// best possible answer for the search key.  Specifically,
+    /// - If the search name belongs under a zone cut, it returns the code
+    ///   of \c DELEGATION and the NS RRset at the zone cut.
+    /// - If there is no matching name, it returns the code of \c NXDOMAIN,
+    ///   and, if DNSSEC is requested, the NSEC RRset that proves the
+    ///   non-existence.
+    /// - If there is a matching name but no RRset of the search type, it
+    ///   returns the code of \c NXRRSET, and, if DNSSEC is required,
+    ///   the NSEC RRset for that name.
+    /// - If there is a matching name with CNAME, it returns the code of
+    ///   \c CNAME and that CNAME RR.
+    /// - If the search name matches a delegation point of DNAME, it returns
+    ///   the code of \c DNAME and that DNAME RR.
+    ///
+    /// A derived version of this method may involve internal resource
+    /// allocation, especially for constructing the resulting RRset, and may
+    /// throw an exception if it fails.
+    /// It should not throw other types of exceptions.
+    ///
+    /// Note: It's quite likely that we'll need to specify search options.
+    /// For example, we should be able to specify whether to allow returning
+    /// glue records at or under a zone cut.  We leave this interface open
+    /// at this moment.
+    ///
+    /// \param name The domain name to be searched for.
+    /// \param type The RR type to be searched for.
+    /// \return A \c FindResult object enclosing the search result (see above).
+    virtual FindResult find(const isc::dns::Name& name,
+                            const isc::dns::RRType& type) const = 0;
+    //@}
+};
+
+/// \brief A pointer-like type pointing to a \c Zone object.
+typedef boost::shared_ptr<Zone> ZonePtr;
+
+/// \brief A pointer-like type pointing to a \c Zone object.
+typedef boost::shared_ptr<const Zone> ConstZonePtr;
+
+/// A derived zone class intended to be used with the memory data source.
+///
+/// Currently this is almost empty and is only used for testing the
+/// \c ZoneTable class.  It will be substantially expanded, and will probably
+/// moved to a separate header file.
+class MemoryZone : public Zone {
     ///
     ///
     /// \name Constructors and Destructor.
     /// \name Constructors and Destructor.
     ///
     ///
@@ -46,8 +194,8 @@ class Zone {
     /// defined as private, making this class non copyable.
     /// defined as private, making this class non copyable.
     //@{
     //@{
 private:
 private:
-    Zone(const Zone& source);
-    Zone& operator=(const Zone& source);
+    MemoryZone(const MemoryZone& source);
+    MemoryZone& operator=(const MemoryZone& source);
 public:
 public:
     /// \brief Constructor from zone parameters.
     /// \brief Constructor from zone parameters.
     ///
     ///
@@ -57,35 +205,22 @@ public:
     ///
     ///
     /// \param rrclass The RR class of the zone.
     /// \param rrclass The RR class of the zone.
     /// \param origin The origin name of the zone.
     /// \param origin The origin name of the zone.
-    Zone(const isc::dns::RRClass& rrclass, const isc::dns::Name& origin);
+    MemoryZone(const isc::dns::RRClass& rrclass, const isc::dns::Name& origin);
 
 
     /// The destructor.
     /// The destructor.
-    ~Zone();
+    virtual ~MemoryZone();
     //@}
     //@}
 
 
-    ///
-    /// \name Getter Methods
-    ///
-    /// These methods never throw an exception.
-    //@{
-    /// \brief Return the origin name of the zone.
-    const isc::dns::Name& getOrigin() const;
-
-    /// \brief Return the RR class of the zone.
-    const isc::dns::RRClass& getClass() const;
-    //@}
+    virtual const isc::dns::Name& getOrigin() const;
+    virtual const isc::dns::RRClass& getClass() const;
+    virtual FindResult find(const isc::dns::Name& name,
+                            const isc::dns::RRType& type) const;
 
 
 private:
 private:
-    struct ZoneImpl;
-    ZoneImpl* impl_;
+    struct MemoryZoneImpl;
+    MemoryZoneImpl* impl_;
 };
 };
 
 
-/// \brief A pointer-like type pointing to a \c Zone object.
-typedef boost::shared_ptr<Zone> ZonePtr;
-
-/// \brief A pointer-like type pointing to a \c Zone object.
-typedef boost::shared_ptr<const Zone> ConstZonePtr;
-
 /// \brief A set of authoritative zones.
 /// \brief A set of authoritative zones.
 ///
 ///
 /// The \c ZoneTable class represents a set of zones of the same RR class
 /// The \c ZoneTable class represents a set of zones of the same RR class