Browse Source

[2218] Add an initial InMemoryZoneFinder::findNSEC3() implementation

Mukund Sivaraman 12 years ago
parent
commit
f8b99e43fc
1 changed files with 145 additions and 3 deletions
  1. 145 3
      src/lib/datasrc/memory/zone_finder.cc

+ 145 - 3
src/lib/datasrc/memory/zone_finder.cc

@@ -23,11 +23,18 @@
 #include <dns/rrset.h>
 #include <dns/rrtype.h>
 
+#include <util/buffer.h>
+#include <util/encode/base32hex.h>
+#include <util/hash/sha1.h>
+
 #include <datasrc/logger.h>
 
 using namespace isc::dns;
 using namespace isc::datasrc::memory;
 using namespace isc::datasrc;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::util::hash;
 
 namespace isc {
 namespace datasrc {
@@ -259,6 +266,45 @@ getNSECForNXRRSET(const ZoneData& zone_data,
     return (NULL);
 }
 
+inline void
+iterateSHA1(SHA1Context* ctx, const uint8_t* input, size_t inlength,
+            const uint8_t* salt, size_t saltlen,
+            uint8_t output[SHA1_HASHSIZE])
+{
+    SHA1Reset(ctx);
+    SHA1Input(ctx, input, inlength);
+    SHA1Input(ctx, salt, saltlen); // this works whether saltlen == or > 0
+    SHA1Result(ctx, output);
+}
+
+std::string
+NSEC3Calculate(const Name& name,
+               const uint16_t iterations,
+               const uint8_t* salt,
+               size_t salt_len) {
+    // We first need to normalize the name by converting all upper case
+    // characters in the labels to lower ones.
+    OutputBuffer obuf(Name::MAX_WIRE);
+    Name name_copy(name);
+    name_copy.downcase();
+    name_copy.toWire(obuf);
+
+    const uint8_t* const salt_buf = (salt_len > 0) ? salt : NULL;
+    std::vector<uint8_t> digest(SHA1_HASHSIZE);
+    uint8_t* const digest_buf = &digest[0];
+
+    SHA1Context sha1_ctx;
+    iterateSHA1(&sha1_ctx, static_cast<const uint8_t*>(obuf.getData()),
+                obuf.getLength(), salt_buf, salt_len, digest_buf);
+    for (unsigned int n = 0; n < iterations; ++n) {
+        iterateSHA1(&sha1_ctx, digest_buf, SHA1_HASHSIZE,
+                    salt_buf, salt_len,
+                    digest_buf);
+    }
+
+    return (encodeBase32Hex(digest));
+}
+
 // Structure to hold result data of the findNode() call
 class FindNodeResult {
 public:
@@ -590,9 +636,105 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
 
 isc::datasrc::ZoneFinder::FindNSEC3Result
 InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
-    (void)name;
-    (void)recursive;
-    isc_throw(isc::NotImplemented, "not completed yet! please implement me");
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
+        arg(recursive ? "recursive" : "non-recursive");
+
+    if (!zone_data_.isNSEC3Signed()) {
+        isc_throw(DataSourceError,
+                  "findNSEC3 attempt for non NSEC3 signed zone: " <<
+                  getOrigin() << "/" << getClass());
+    }
+
+    const NameComparisonResult cmp_result = name.compare(getOrigin());
+    if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
+        cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
+        isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
+                  << name << ", zone: " << getOrigin() << "/"
+                  << getClass());
+    }
+
+    // Convenient shortcuts
+    const unsigned int olabels = getOrigin().getLabelCount();
+    const unsigned int qlabels = name.getLabelCount();
+    const NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
+
+    const ZoneNode* covering_node(NULL); // placeholder of the next closer proof
+    // Examine all names from the query name to the origin name, stripping
+    // the deepest label one by one, until we find a name that has a matching
+    // NSEC3 hash.
+    for (unsigned int labels = qlabels; labels >= olabels; --labels) {
+        const std::string hlabel =
+             NSEC3Calculate((labels == qlabels ?
+                             name : name.split(qlabels - labels, labels)),
+                            nsec3_data->iterations,
+                            nsec3_data->getSaltData(),
+                            nsec3_data->getSaltLen());
+
+        LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
+            arg(name).arg(labels).arg(hlabel);
+
+        const ZoneTree& tree = nsec3_data->getNSEC3Tree();
+
+        ZoneNode* node(NULL);
+        ZoneNodeChain chain;
+
+        ZoneTree::Result result = tree.find(Name(hlabel), &node, chain);
+
+        if (result == ZoneTree::EXACTMATCH) {
+            // We found an exact match.
+            RdataSet* set = node->getData();
+            ConstRRsetPtr closest = createTreeNodeRRset(node,
+                                                        set,
+                                                        getClass());
+            ConstRRsetPtr next = createTreeNodeRRset(covering_node,
+                                                     covering_node->getData(),
+                                                     getClass());
+
+            LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                      DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
+                arg(*closest);
+
+            return (FindNSEC3Result(true, labels, closest, next));
+        } else {
+            const NameComparisonResult& cmp = chain.getLastComparisonResult();
+            assert(cmp.getOrder() != 0);
+
+            // find() finished in between these two:
+            const ZoneNode* previous_node = tree.previousNode(chain);
+            const ZoneNode* next_node = tree.nextNode(chain);
+
+            // If the given hash is larger than the largest stored hash or
+            // the first label doesn't match the target, identify the "previous"
+            // hash value and remember it as the candidate next closer proof.
+            if (((cmp.getOrder() < 0) && (previous_node == NULL)) ||
+                ((cmp.getOrder() > 0) && (next_node == NULL))) {
+                covering_node = tree.getLargestNode();
+            } else {
+                // Otherwise, H(found_entry-1) < given_hash < H(found_entry).
+                // The covering proof is the first one (and it's valid
+                // because found is neither begin nor end)
+                covering_node = previous_node;
+            }
+
+            if (!recursive) {   // in non recursive mode, we are done.
+                ConstRRsetPtr closest =
+                    createTreeNodeRRset(covering_node,
+                                        covering_node->getData(),
+                                        getClass());
+
+                LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                          DATASRC_MEM_FINDNSEC3_COVER).
+                    arg(name).arg(*closest);
+
+                return (FindNSEC3Result(false, labels,
+                                        closest, ConstRRsetPtr()));
+            }
+        }
+    }
+
+    isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
+              "a broken NSEC3 zone: " << getOrigin() << "/"
+              << getClass());
 }
 
 } // namespace memory