|
@@ -23,11 +23,18 @@
|
|
#include <dns/rrset.h>
|
|
#include <dns/rrset.h>
|
|
#include <dns/rrtype.h>
|
|
#include <dns/rrtype.h>
|
|
|
|
|
|
|
|
+#include <util/buffer.h>
|
|
|
|
+#include <util/encode/base32hex.h>
|
|
|
|
+#include <util/hash/sha1.h>
|
|
|
|
+
|
|
#include <datasrc/logger.h>
|
|
#include <datasrc/logger.h>
|
|
|
|
|
|
using namespace isc::dns;
|
|
using namespace isc::dns;
|
|
using namespace isc::datasrc::memory;
|
|
using namespace isc::datasrc::memory;
|
|
using namespace isc::datasrc;
|
|
using namespace isc::datasrc;
|
|
|
|
+using namespace isc::util;
|
|
|
|
+using namespace isc::util::encode;
|
|
|
|
+using namespace isc::util::hash;
|
|
|
|
|
|
namespace isc {
|
|
namespace isc {
|
|
namespace datasrc {
|
|
namespace datasrc {
|
|
@@ -259,6 +266,45 @@ getNSECForNXRRSET(const ZoneData& zone_data,
|
|
return (NULL);
|
|
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
|
|
// Structure to hold result data of the findNode() call
|
|
class FindNodeResult {
|
|
class FindNodeResult {
|
|
public:
|
|
public:
|
|
@@ -590,9 +636,105 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
|
|
|
|
|
|
isc::datasrc::ZoneFinder::FindNSEC3Result
|
|
isc::datasrc::ZoneFinder::FindNSEC3Result
|
|
InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
|
|
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
|
|
} // namespace memory
|