Michal 'vorner' Vaner 13 years ago
parent
commit
823f30307b

+ 63 - 0
src/lib/datasrc/database.h

@@ -234,6 +234,41 @@ public:
                                           int id,
                                           bool subdomains = false) const = 0;
 
+    /// \brief Creates an iterator context for the records of NSEC3 namespace
+    ///     for the given hash
+    ///
+    /// Returns an Iteratorcontextptr that contains all the records of the given
+    /// hash in the NSEC3 namespace of the given zone.
+    ///
+    /// The implementation of the iterator that is returned may leave the
+    /// NAME_COLUMN column of the array passed to getNext() untouched,
+    /// as that name is easy to construct on the caller side (both the
+    /// hash and the name of the zone is known). The SIGTYPE_COLUMN can
+    /// be omitted as well, as it would be always empty for NSEC3 RRs or
+    /// contained "NSEC3" in case of RRSIG RRs.
+    ///
+    /// The iterator will contain both the NSEC3 records and the corresponding
+    /// RRSIGs, in arbitrary order.
+    ///
+    /// The iterator might be empty (containing no RRs) in case the zone is not
+    /// signed by NSEC3.
+    ///
+    /// \note In case there are multiple NSEC3 chains and they collide
+    ///     (unlikely, but it can happen), this can return multiple NSEC3
+    ///     records.
+    /// \exception any Since any implementaion can be used, the caller should
+    ///     expect any exception to be thrown.
+    /// \exception isc::NotImplemented in case the database does not support
+    ///     NSEC3
+    ///
+    /// \param hash The hash part of the NSEC3 name (eg. for a name of NSEC3
+    ///     RKBUCQT8T78GV6QBCGBHCHC019LG73SJ.example.com., we the hash would be
+    ///     RKBUCQT8T78GV6QBCGBHCHC019LG73SJ).
+    /// \param id The id of te zone, as returned from getZone().
+    /// \return Newly created iterator context. Must not be NULL.
+    virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
+                                               int id) const = 0;
+
     /// \brief Creates an iterator context for the whole zone.
     ///
     /// Returns an IteratorContextPtr that contains all records of the
@@ -643,6 +678,34 @@ public:
     ///     apex of the zone).
     virtual std::string findPreviousName(int zone_id,
                                          const std::string& rname) const = 0;
+
+    /// \brief It returns the previous hash in the NSEC3 chain.
+    ///
+    /// This is used to find previous NSEC3 hashes, to find covering NSEC3 in
+    /// case none match exactly.
+    ///
+    /// In case a hash before before the lowest or the lowest is provided,
+    /// this should return the largest one in the zone (NSEC3 needs a
+    /// wrap-around semantics).
+    ///
+    /// \param zone_id Specifies the zone to look into, as returned by getZone.
+    /// \param hash The hash to look before.
+    /// \return The nearest smaller hash than the provided one, or the largest
+    ///     hash in the zone if something smaller or equal to the lowest one
+    ///     is provided.
+    /// \note If the zone contains multiple NSEC3 chains, you should check that
+    ///     the returned result contains the NSEC3 for correct parameters. If
+    ///     not, query again and get something smaller - this will eventually
+    ///     get to the correct one. This interface and semantics might change
+    ///     in future.
+    ///
+    /// \throw DataSourceError if there's a problem with the database or if
+    ///     this zone is not signed with NSEC3.
+    /// \throw NotImplemented if this database doesn't support NSEC3.
+    /// \throw anything else, as this might be any implementation.
+    virtual std::string findPreviousNSEC3Hash(int zone_id,
+                                              const std::string& hash)
+        const = 0;
 };
 
 /// \brief Concrete data source client oriented at database backends.

+ 11 - 0
src/lib/datasrc/sqlite3_accessor.cc

@@ -588,6 +588,12 @@ SQLite3Accessor::getRecords(const std::string& name, int id,
 }
 
 DatabaseAccessor::IteratorContextPtr
+SQLite3Accessor::getNSEC3Records(const std::string&, int) const {
+    // TODO: Implement
+    isc_throw(NotImplemented, "Not implemented yet, see ticket #1760");
+}
+
+DatabaseAccessor::IteratorContextPtr
 SQLite3Accessor::getAllRecords(int id) const {
     return (IteratorContextPtr(new Context(shared_from_this(), id)));
 }
@@ -1092,5 +1098,10 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
     return (result);
 }
 
+std::string
+SQLite3Accessor::findPreviousNSEC3Hash(int, const std::string&) const {
+    isc_throw(NotImplemented, "Not implemented yet, see #1760");
+}
+
 } // end of namespace datasrc
 } // end of namespace isc

+ 13 - 0
src/lib/datasrc/sqlite3_accessor.h

@@ -141,6 +141,14 @@ public:
                                           int id,
                                           bool subdomains = false) const;
 
+    /// \brief Look up NSEC3 records for the given hash
+    ///
+    /// This implements the getNSEC3Records of DatabaseAccessor.
+    ///
+    /// \todo Actually implement, currently throws NotImplemented.
+    virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
+                                               int id) const;
+
     /** \brief Look up all resource records for a zone
      *
      * This implements the getRecords() method from DatabaseAccessor
@@ -231,6 +239,11 @@ public:
     virtual std::string findPreviousName(int zone_id, const std::string& rname)
         const;
 
+    /// \brief Conrete implemantion of the pure virtual method of
+    /// DatabaseAccessor
+    virtual std::string findPreviousNSEC3Hash(int zone_id,
+                                              const std::string& hash) const;
+
 private:
     /// \brief Private database data
     boost::scoped_ptr<SQLite3Parameters> dbparameters_;

+ 94 - 1
src/lib/datasrc/tests/database_unittest.cc

@@ -205,6 +205,13 @@ const char* const TEST_RECORDS[][5] = {
     {NULL, NULL, NULL, NULL, NULL},
 };
 
+// FIXME: Taken from a different test. Fill with proper data when creating a test.
+const char* TEST_NSEC3_RECORDS[][5] = {
+    {"1BB7SO0452U1QHL98UISNDD9218GELR5", "NSEC3", "3600", "", "1 0 10 FEEDABEE 4KLSVDE8KH8G95VU68R7AHBE1CPQN38J"},
+    {"1BB7SO0452U1QHL98UISNDD9218GELR5", "RRSIG", "3600", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {NULL, NULL, NULL, NULL, NULL}
+};
+
 /*
  * An accessor with minimum implementation, keeping the original
  * "NotImplemented" methods.
@@ -251,11 +258,16 @@ public:
 
     virtual IteratorContextPtr getRecords(const std::string&, int, bool)
         const
-        {
+    {
         isc_throw(isc::NotImplemented,
                   "This database datasource can't be iterated");
     }
 
+    virtual IteratorContextPtr getNSEC3Records(const std::string&, int) const {
+        isc_throw(isc::NotImplemented, "This test database datasource won't "
+                  "give you any NSEC3. Ever. Ask someone else.");
+    }
+
     virtual IteratorContextPtr getAllRecords(int) const {
         isc_throw(isc::NotImplemented,
                   "This database datasource can't be iterated");
@@ -270,6 +282,11 @@ public:
         isc_throw(isc::NotImplemented,
                   "This data source doesn't support DNSSEC");
     }
+
+    virtual std::string findPreviousNSEC3Hash(int, const std::string&) const {
+        isc_throw(isc::NotImplemented,
+                  "This test database knows nothing about NSEC3 nor order");
+    }
 private:
     const std::string database_name_;
 
@@ -378,6 +395,28 @@ public:
     }
 
 private:
+    class DomainIterator : public IteratorContext {
+    public:
+        DomainIterator(const std::vector<std::vector<std::string> >& domain) :
+            domain_(domain),
+            position_(domain_.begin())
+        {}
+        virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) {
+            if (position_ == domain_.end()) {
+                return (false);
+            } else {
+                for (size_t i(0); i < COLUMN_COUNT; ++ i) {
+                    columns[i] = (*position_)[i];
+                }
+                ++ position_;
+                return (true);
+            }
+        }
+    private:
+        const std::vector<std::vector<std::string> > domain_;
+        std::vector<std::vector<std::string> >::const_iterator position_;
+    };
+
     class MockNameIteratorContext : public IteratorContext {
     public:
         MockNameIteratorContext(const MockAccessor& mock_accessor, int zone_id,
@@ -610,6 +649,17 @@ public:
         }
     }
 
+    virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
+                                               int) const
+    {
+        Domains::const_iterator it(nsec3_namespace_.find(hash));
+        if (it == nsec3_namespace_.end()) {
+            return (IteratorContextPtr(new EmptyIteratorContext()));
+        } else {
+            return (IteratorContextPtr(new DomainIterator(it->second)));
+        }
+    }
+
     virtual pair<bool, int> startUpdateZone(const std::string& zone_name,
                                             bool replace)
     {
@@ -747,6 +797,21 @@ public:
             isc_throw(isc::Unexpected, "Unknown zone ID");
         }
     }
+    virtual std::string findPreviousNSEC3Hash(int,
+                                              const std::string& hash) const
+    {
+        // TODO: Provide some broken data, but it is not known yet how broken
+        // they'll have to be.
+        Domains::const_iterator it(nsec3_namespace_.lower_bound(hash));
+        // We got just after the one we want
+        if (it == nsec3_namespace_.begin()) {
+            // Hmm, we got something really small. So we wrap around.
+            // This is one after the last, so after decreasing it we'll get
+            // the biggest.
+            it = nsec3_namespace_.end();
+        }
+        return ((--it)->first);
+    }
     virtual void addRecordDiff(int id, uint32_t serial,
                                DiffOperation operation,
                                const std::string (&data)[DIFF_PARAM_COUNT])
@@ -830,6 +895,9 @@ private:
     const Domains empty_records_master_;
     const Domains* empty_records_;
 
+    // The NSEC3 namespace. The above trick will be added once it is needed.
+    Domains nsec3_namespace_;
+
     // The journal data
     std::vector<JournalEntry> journal_entries_master_;
     std::vector<JournalEntry>* journal_entries_;
@@ -890,6 +958,20 @@ private:
         cur_name_.clear();
     }
 
+    // Works in a similar way to addCurName, but it is added to
+    // the NSEC3 namespace. You don't provide the full name, only
+    // the hash part.
+    void addCurHash(const std::string& hash) {
+        ASSERT_EQ(0, nsec3_namespace_.count(hash));
+        // Append the name to all of them
+        for (std::vector<std::vector<std::string> >::iterator
+             i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
+            i->push_back(hash);
+        }
+        (*readonly_records_)[hash] = cur_name_;
+        cur_name_.clear();
+    }
+
     // Fills the database with zone data.
     // This method constructs a number of resource records (with addRecord),
     // which will all be added for one domain name to the fake database
@@ -912,6 +994,17 @@ private:
                       TEST_RECORDS[i][3], TEST_RECORDS[i][4]);
         }
         addCurName(prev_name);
+        prev_name = NULL;
+        for (int i = 0; TEST_NSEC3_RECORDS[i][0] != NULL; ++i) {
+            if (prev_name != NULL &&
+                strcmp(prev_name, TEST_NSEC3_RECORDS[i][0]) != 0) {
+                addCurHash(prev_name);
+            }
+            prev_name = TEST_NSEC3_RECORDS[i][0];
+            addRecord(TEST_NSEC3_RECORDS[i][1], TEST_NSEC3_RECORDS[i][2],
+                      TEST_NSEC3_RECORDS[i][3], TEST_NSEC3_RECORDS[i][4]);
+        }
+        addCurHash(prev_name);
     }
 };