BIND 10 master, updated. 79af4f917ed615538c88ef4df43f8884a0d3e5bc Merge #1760

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Mar 20 09:40:07 UTC 2012


The branch, master has been updated
       via  79af4f917ed615538c88ef4df43f8884a0d3e5bc (commit)
       via  abd717cfc9b0b01bfe6a5d651a2d34ffaf61d055 (commit)
       via  c2b8d8433c9a4ec5fb6817a104b6a6309d413f7e (commit)
       via  37960ee83cb33be5cd683fb0d458343752caa01a (commit)
       via  8f36290ceaae7abb6ee5b5affdf0c7bb31e5bea5 (commit)
       via  e80de6f13a404687f4ed896e548fc614dc953e6e (commit)
      from  da4d1f0f461a2ffd46fd6d09b78d4bce47df6816 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 79af4f917ed615538c88ef4df43f8884a0d3e5bc
Merge: da4d1f0f461a2ffd46fd6d09b78d4bce47df6816 abd717cfc9b0b01bfe6a5d651a2d34ffaf61d055
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Mar 20 10:21:38 2012 +0100

    Merge #1760

-----------------------------------------------------------------------

Summary of changes:
 src/lib/datasrc/sqlite3_accessor.cc                |  148 +++++++++++++++++--
 src/lib/datasrc/tests/sqlite3_accessor_unittest.cc |  102 ++++++++++++++
 2 files changed, 234 insertions(+), 16 deletions(-)

-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 5fe2b67..308df60 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -54,7 +54,10 @@ enum StatementID {
     LOW_DIFF_ID = 13,
     HIGH_DIFF_ID = 14,
     DIFF_RECS = 15,
-    NUM_STATEMENTS = 16
+    NSEC3 = 16,
+    NSEC3_PREVIOUS = 17,
+    NSEC3_LAST = 18,
+    NUM_STATEMENTS = 19
 };
 
 const char* const text_statements[NUM_STATEMENTS] = {
@@ -83,7 +86,7 @@ const char* const text_statements[NUM_STATEMENTS] = {
      */
     "SELECT name FROM records " // FIND_PREVIOUS
         "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
-        "rname < $2 ORDER BY rname DESC LIMIT 1",
+        "rname < ?2 ORDER BY rname DESC LIMIT 1",
     "INSERT INTO diffs "        // ADD_RECORD_DIFF
         "(zone_id, version, operation, name, rrtype, ttl, rdata) "
         "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
@@ -103,7 +106,21 @@ const char* const text_statements[NUM_STATEMENTS] = {
     // that the columns match the column IDs passed to the iterator
     "SELECT rrtype, ttl, id, rdata, name FROM diffs "   // DIFF_RECS
         "WHERE zone_id=?1 AND id>=?2 and id<=?3 "
-        "ORDER BY id ASC"
+        "ORDER BY id ASC",
+
+    // Query to get the NSEC3 records
+    //
+    // The "1" in SELECT is for positioning the rdata column to the
+    // expected position, so we can reuse the same code as for other
+    // lookups.
+    "SELECT rdtype, ttl, 1, rdata FROM nsec3 WHERE zone_id=?1 AND "
+        "hash=?2",
+    // For getting the previous NSEC3 hash
+    "SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 AND hash < ?2 "
+        "ORDER BY hash DESC LIMIT 1",
+    // And for wrap-around
+    "SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 "
+        "ORDER BY hash DESC LIMIT 1",
 };
 
 struct SQLite3Parameters {
@@ -482,19 +499,46 @@ public:
         bindZoneId(id);
     }
 
+    // What kind of query it is - selection of the statement for DB
+    enum QueryType {
+        QT_ANY, // Directly for a domain
+        QT_SUBDOMAINS, // Subdomains of a given domain
+        QT_NSEC3 // Domain in the NSEC3 namespace (the name is is the hash,
+                 // not the whole name)
+    };
+
     // Construct an iterator for records with a specific name. When constructed
     // this way, the getNext() call will copy all fields except name
     Context(const boost::shared_ptr<const SQLite3Accessor>& accessor, int id,
-            const std::string& name, bool subdomains) :
-        iterator_type_(ITT_NAME),
+            const std::string& name, QueryType qtype) :
+        iterator_type_(qtype == QT_NSEC3 ? ITT_NSEC3 : ITT_NAME),
         accessor_(accessor),
         statement_(NULL),
         name_(name)
     {
+        // Choose the statement text depending on the query type
+        const char* statement(NULL);
+        switch (qtype) {
+            case QT_ANY:
+                statement = text_statements[ANY];
+                break;
+            case QT_SUBDOMAINS:
+                statement = text_statements[ANY_SUB];
+                break;
+            case QT_NSEC3:
+                statement = text_statements[NSEC3];
+                break;
+            default:
+                // Can Not Happen - there isn't any other type of query
+                // and all the calls to the constructor are from this
+                // file. Therefore no way to test it throws :-(.
+                isc_throw(Unexpected,
+                          "Invalid qtype passed - unreachable code branch "
+                          "reached");
+        }
+
         // We create the statement now and then just keep getting data from it
-        statement_ = prepare(accessor->dbparameters_->db_,
-                             subdomains ? text_statements[ANY_SUB] :
-                             text_statements[ANY]);
+        statement_ = prepare(accessor->dbparameters_->db_, statement);
         bindZoneId(id);
         bindName(name_);
     }
@@ -511,7 +555,11 @@ public:
             // For both types, we copy the first four columns
             copyColumn(data, TYPE_COLUMN);
             copyColumn(data, TTL_COLUMN);
-            copyColumn(data, SIGTYPE_COLUMN);
+            // The NSEC3 lookup does not provide the SIGTYPE, it is not
+            // necessary and not contained in the table.
+            if (iterator_type_ != ITT_NSEC3) {
+                copyColumn(data, SIGTYPE_COLUMN);
+            }
             copyColumn(data, RDATA_COLUMN);
             // Only copy Name if we are iterating over every record
             if (iterator_type_ == ITT_ALL) {
@@ -537,7 +585,8 @@ private:
     // See description of getNext() and the constructors
     enum IteratorType {
         ITT_ALL,
-        ITT_NAME
+        ITT_NAME,
+        ITT_NSEC3
     };
 
     void copyColumn(std::string (&data)[COLUMN_COUNT], int column) {
@@ -584,13 +633,15 @@ SQLite3Accessor::getRecords(const std::string& name, int id,
                             bool subdomains) const
 {
     return (IteratorContextPtr(new Context(shared_from_this(), id, name,
-                                           subdomains)));
+                                           subdomains ?
+                                           Context::QT_SUBDOMAINS :
+                                           Context::QT_ANY)));
 }
 
 DatabaseAccessor::IteratorContextPtr
-SQLite3Accessor::getNSEC3Records(const std::string&, int) const {
-    // TODO: Implement
-    isc_throw(NotImplemented, "Not implemented yet, see ticket #1760");
+SQLite3Accessor::getNSEC3Records(const std::string& hash, int id) const {
+    return (IteratorContextPtr(new Context(shared_from_this(), id, hash,
+                                           Context::QT_NSEC3)));
 }
 
 DatabaseAccessor::IteratorContextPtr
@@ -1099,8 +1150,73 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
 }
 
 std::string
-SQLite3Accessor::findPreviousNSEC3Hash(int, const std::string&) const {
-    isc_throw(NotImplemented, "Not implemented yet, see #1760");
+SQLite3Accessor::findPreviousNSEC3Hash(int zone_id, const std::string& hash)
+    const
+{
+    sqlite3_stmt* const stmt = dbparameters_->getStatement(NSEC3_PREVIOUS);
+    sqlite3_reset(stmt);
+    sqlite3_clear_bindings(stmt);
+
+    if (sqlite3_bind_int(stmt, 1, zone_id) != SQLITE_OK) {
+        isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
+                  " to SQL statement (find previous NSEC3): " <<
+                  sqlite3_errmsg(dbparameters_->db_));
+    }
+    if (sqlite3_bind_text(stmt, 2, hash.c_str(), -1, SQLITE_STATIC) !=
+        SQLITE_OK) {
+        isc_throw(SQLite3Error, "Could not bind hash " << hash <<
+                  " to SQL statement (find previous NSEC3): " <<
+                  sqlite3_errmsg(dbparameters_->db_));
+    }
+
+    std::string result;
+    const int rc = sqlite3_step(stmt);
+    if (rc == SQLITE_ROW) {
+        // We found it
+        result = convertToPlainChar(sqlite3_column_text(stmt, 0),
+                                    dbparameters_->db_);
+    }
+    sqlite3_reset(stmt);
+
+    if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+        // Some kind of error
+        isc_throw(SQLite3Error, "Could not get data for previous hash");
+    }
+
+    if (rc == SQLITE_DONE) {
+        // No NSEC3 records before this hash. This means we should wrap
+        // around and take the last one.
+        sqlite3_stmt* const stmt = dbparameters_->getStatement(NSEC3_LAST);
+        sqlite3_reset(stmt);
+        sqlite3_clear_bindings(stmt);
+
+        if (sqlite3_bind_int(stmt, 1, zone_id) != SQLITE_OK) {
+            isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
+                      " to SQL statement (find last NSEC3): " <<
+                      sqlite3_errmsg(dbparameters_->db_));
+        }
+
+        const int rc = sqlite3_step(stmt);
+        if (rc == SQLITE_ROW) {
+            // We found it
+            result = convertToPlainChar(sqlite3_column_text(stmt, 0),
+                                        dbparameters_->db_);
+        }
+        sqlite3_reset(stmt);
+
+        if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+            // Some kind of error
+            isc_throw(SQLite3Error, "Could not get data for last hash");
+        }
+
+        if (rc == SQLITE_DONE) {
+            // No NSEC3 at all in the zone. Well, bad luck, but you should not
+            // have asked in the first place.
+            isc_throw(DataSourceError, "No NSEC3 in this zone");
+        }
+    }
+
+    return (result);
 }
 
 } // end of namespace datasrc
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 5122136..4f7a5d8 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -173,6 +173,108 @@ TEST_F(SQLite3AccessorTest, iterator) {
     EXPECT_FALSE(context->getNext(data));
 }
 
+// This tests getting NSEC3 records
+TEST_F(SQLite3AccessorTest, nsec3) {
+    const std::pair<bool, int>
+        zone_info(accessor->getZone("sql2.example.com."));
+    ASSERT_TRUE(zone_info.first);
+
+    DatabaseAccessor::IteratorContextPtr
+        context(accessor->getNSEC3Records("1BB7SO0452U1QHL98UISNDD9218GELR5",
+                                          zone_info.second));
+    // This relies on specific ordering in the DB. Is it OK?
+    // The name field is empty, as well as the sigtype one. This is OK, as
+    // both are not needed and the interface allows it.
+    checkRR(context, "", "7200", "NSEC3",
+            "1 0 10 FEEDABEE 4KLSVDE8KH8G95VU68R7AHBE1CPQN38J");
+    checkRR(context, "", "7200", "RRSIG",
+            "NSEC3 5 4 7200 20100410172647 20100311172647 63192 "
+            "sql2.example.com. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK "
+            "mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/"
+            "QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o "
+            "8gHSY5vYTtothcZQa4BMKhmGQEk=");
+
+    // And that's all
+    std::string data[DatabaseAccessor::COLUMN_COUNT];
+    EXPECT_FALSE(context->getNext(data));
+
+    // Calling again won't hurt
+    EXPECT_FALSE(context->getNext(data));
+
+    // This one should be empty ‒ no data here
+    context = accessor->getNSEC3Records("NO_SUCH_HASH", zone_info.second);
+    EXPECT_FALSE(context->getNext(data));
+    // Still nothing? ;-)
+    EXPECT_FALSE(context->getNext(data));
+}
+
+// This tests getting a previoeus hash in the NSEC3 namespace of a zone,
+// including a wrap-around and asking for a hash that does not exist in the.
+// zone at all.
+TEST_F(SQLite3AccessorTest, nsec3Previous) {
+    // Get the zone
+    const std::pair<bool, int>
+        zone_info(accessor->getZone("sql2.example.com."));
+    ASSERT_TRUE(zone_info.first);
+
+    std::string data[DatabaseAccessor::COLUMN_COUNT];
+
+    // Test a previous hash for something that is in the zone
+    // (ensuring it is really there)
+    DatabaseAccessor::IteratorContextPtr
+        context(accessor->getNSEC3Records("703OOGCKF8VEV1N7U64D1JG19URETN8N",
+                                          zone_info.second));
+    EXPECT_TRUE(context->getNext(data));
+    EXPECT_EQ("56IEQ664LHDAKVPE2FL179MSM3QAOFVC", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "703OOGCKF8VEV1N7U64D1JG19URETN8N"));
+
+    // Test a previous hash for something that is not in the
+    // zone
+    context = accessor->getNSEC3Records("702OOGCKF8VEV1N7U64D1JG19URETN8N",
+                                        zone_info.second);
+    EXPECT_FALSE(context->getNext(data));
+    EXPECT_EQ("56IEQ664LHDAKVPE2FL179MSM3QAOFVC", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "702OOGCKF8VEV1N7U64D1JG19URETN8N"));
+
+    // Search at the first item, should wrap around
+    context = accessor->getNSEC3Records("1BB7SO0452U1QHL98UISNDD9218GELR5",
+                                        zone_info.second);
+    EXPECT_TRUE(context->getNext(data));
+    EXPECT_EQ("RKBUCQT8T78GV6QBCGBHCHC019LG73SJ", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "1BB7SO0452U1QHL98UISNDD9218GELR5"));
+
+    // Search before the first item, should wrap around
+    context = accessor->getNSEC3Records("0BB7SO0452U1QHL98UISNDD9218GELR5",
+                                        zone_info.second);
+    EXPECT_FALSE(context->getNext(data));
+    EXPECT_EQ("RKBUCQT8T78GV6QBCGBHCHC019LG73SJ", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "0BB7SO0452U1QHL98UISNDD9218GELR5"));
+
+    // Search after the last item (should return the last one)
+    context = accessor->getNSEC3Records("RRBUCQT8T78GV6QBCGBHCHC019LG73SJ",
+                                        zone_info.second);
+    EXPECT_FALSE(context->getNext(data));
+    EXPECT_EQ("RKBUCQT8T78GV6QBCGBHCHC019LG73SJ", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "RRBUCQT8T78GV6QBCGBHCHC019LG73SJ"));
+}
+
+// Check it throws when we want a previous NSEC3 hash in an unsigned zone
+TEST_F(SQLite3AccessorTest, nsec3PreviousUnsigned) {
+    // This zone did not look signed in the test file.
+    const std::pair<bool, int>
+        unsigned_zone_info(accessor->getZone("example.com."));
+
+    EXPECT_THROW(accessor->
+                 findPreviousNSEC3Hash(unsigned_zone_info.second,
+                                       "0BB7SO0452U1QHL98UISNDD9218GELR5"),
+                 DataSourceError);
+}
+
 // This tests the difference iterator context
 
 // Test that at attempt to create a difference iterator for a serial number



More information about the bind10-changes mailing list