From: Mukund Sivaraman Date: Wed, 12 Sep 2012 17:44:04 +0000 (+0530) Subject: [2218] Add an initial InMemoryZoneFinder::findNSEC3() implementation X-Git-Tag: trac2351_base~37^2~1^2~13^2~22^2~16 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f8b99e43fcc928eccd9b30fd187d91e92782ee02;p=thirdparty%2Fkea.git [2218] Add an initial InMemoryZoneFinder::findNSEC3() implementation --- diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc index 75d3187551..b7c4903573 100644 --- a/src/lib/datasrc/memory/zone_finder.cc +++ b/src/lib/datasrc/memory/zone_finder.cc @@ -23,11 +23,18 @@ #include #include +#include +#include +#include + #include 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 digest(SHA1_HASHSIZE); + uint8_t* const digest_buf = &digest[0]; + + SHA1Context sha1_ctx; + iterateSHA1(&sha1_ctx, static_cast(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