|
@@ -15,6 +15,7 @@
|
|
|
#include <datasrc/memory/zone_finder.h>
|
|
|
#include <datasrc/memory/domaintree.h>
|
|
|
#include <datasrc/memory/treenode_rrset.h>
|
|
|
+#include <datasrc/memory/rdata_serialization.h>
|
|
|
|
|
|
#include <datasrc/zone.h>
|
|
|
#include <datasrc/data_source.h>
|
|
@@ -22,9 +23,16 @@
|
|
|
#include <dns/name.h>
|
|
|
#include <dns/rrset.h>
|
|
|
#include <dns/rrtype.h>
|
|
|
+#include <dns/nsec3hash.h>
|
|
|
|
|
|
#include <datasrc/logger.h>
|
|
|
|
|
|
+#include <boost/scoped_ptr.hpp>
|
|
|
+#include <boost/bind.hpp>
|
|
|
+
|
|
|
+#include <algorithm>
|
|
|
+#include <vector>
|
|
|
+
|
|
|
using namespace isc::dns;
|
|
|
using namespace isc::datasrc::memory;
|
|
|
using namespace isc::datasrc;
|
|
@@ -33,6 +41,37 @@ namespace isc {
|
|
|
namespace datasrc {
|
|
|
namespace memory {
|
|
|
|
|
|
+namespace internal {
|
|
|
+
|
|
|
+// Specialized version of ZoneFinder::ResultContext, which holds objects
|
|
|
+// related to find() results using internal representations of the in-memory
|
|
|
+// data source implementation.
|
|
|
+class ZoneFinderResultContext {
|
|
|
+public:
|
|
|
+ /// \brief Constructor
|
|
|
+ ///
|
|
|
+ /// The first three parameters correspond to those of
|
|
|
+ /// ZoneFinder::ResultContext. If node is non NULL, it specifies the
|
|
|
+ /// found ZoneNode in the search.
|
|
|
+ ZoneFinderResultContext(ZoneFinder::Result code_param,
|
|
|
+ TreeNodeRRsetPtr rrset_param,
|
|
|
+ ZoneFinder::FindResultFlags flags_param,
|
|
|
+ const ZoneData& zone_data_param,
|
|
|
+ const ZoneNode* node, const RdataSet* rdset) :
|
|
|
+ code(code_param), rrset(rrset_param), flags(flags_param),
|
|
|
+ zone_data(&zone_data_param), found_node(node), found_rdset(rdset)
|
|
|
+ {}
|
|
|
+
|
|
|
+ const ZoneFinder::Result code;
|
|
|
+ const TreeNodeRRsetPtr rrset;
|
|
|
+ const ZoneFinder::FindResultFlags flags;
|
|
|
+ const ZoneData* const zone_data;
|
|
|
+ const ZoneNode* const found_node;
|
|
|
+ const RdataSet* const found_rdset;
|
|
|
+};
|
|
|
+}
|
|
|
+using internal::ZoneFinderResultContext;
|
|
|
+
|
|
|
namespace {
|
|
|
/// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for
|
|
|
/// the given RRClass
|
|
@@ -51,18 +90,21 @@ TreeNodeRRsetPtr
|
|
|
createTreeNodeRRset(const ZoneNode* node,
|
|
|
const RdataSet* rdataset,
|
|
|
const RRClass& rrclass,
|
|
|
+ ZoneFinder::FindOptions options,
|
|
|
const Name* realname = NULL)
|
|
|
{
|
|
|
+ const bool dnssec = ((options & ZoneFinder::FIND_DNSSEC) != 0);
|
|
|
if (node != NULL && rdataset != NULL) {
|
|
|
if (realname != NULL) {
|
|
|
- return TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass, node,
|
|
|
- rdataset, true));
|
|
|
+ return (TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass,
|
|
|
+ node, rdataset,
|
|
|
+ dnssec)));
|
|
|
} else {
|
|
|
- return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node,
|
|
|
- rdataset, true));
|
|
|
+ return (TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset,
|
|
|
+ dnssec)));
|
|
|
}
|
|
|
} else {
|
|
|
- return TreeNodeRRsetPtr();
|
|
|
+ return (TreeNodeRRsetPtr());
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -145,6 +187,27 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
|
|
|
return (false);
|
|
|
}
|
|
|
|
|
|
+/// Creates a NSEC3 ConstRRsetPtr for the given ZoneNode inside the
|
|
|
+/// NSEC3 tree, for the given RRClass.
|
|
|
+///
|
|
|
+/// It asserts that the node contains data (RdataSet) and is of type
|
|
|
+/// NSEC3.
|
|
|
+///
|
|
|
+/// \param node The ZoneNode inside the NSEC3 tree
|
|
|
+/// \param rrclass The RRClass as passed by the client
|
|
|
+ConstRRsetPtr
|
|
|
+createNSEC3RRset(const ZoneNode* node, const RRClass& rrclass) {
|
|
|
+ const RdataSet* rdataset = node->getData();
|
|
|
+ // Only NSEC3 ZoneNodes are allowed to be passed to this method. We
|
|
|
+ // assert that these have data, and also are of type NSEC3.
|
|
|
+ assert(rdataset != NULL);
|
|
|
+ assert(rdataset->type == RRType::NSEC3());
|
|
|
+
|
|
|
+ // Create the RRset. Note the DNSSEC flag: NSEC3 implies DNSSEC.
|
|
|
+ return (createTreeNodeRRset(node, rdataset, rrclass,
|
|
|
+ ZoneFinder::FIND_DNSSEC));
|
|
|
+}
|
|
|
+
|
|
|
// convenience function to fill in the final details
|
|
|
//
|
|
|
// Set up ZoneFinderResultContext object as a return value of find(),
|
|
@@ -158,14 +221,16 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
|
|
|
// if wild is true, the RESULT_WILDCARD flag will be set.
|
|
|
// If qname is not NULL, this is the query name, to be used in wildcard
|
|
|
// substitution instead of the Node's name).
|
|
|
-isc::datasrc::memory::ZoneFinderResultContext
|
|
|
+ZoneFinderResultContext
|
|
|
createFindResult(const RRClass& rrclass,
|
|
|
const ZoneData& zone_data,
|
|
|
ZoneFinder::Result code,
|
|
|
- const RdataSet* rrset,
|
|
|
+ const RdataSet* rdset,
|
|
|
const ZoneNode* node,
|
|
|
+ ZoneFinder::FindOptions options,
|
|
|
bool wild = false,
|
|
|
- const Name* qname = NULL) {
|
|
|
+ const Name* qname = NULL)
|
|
|
+{
|
|
|
ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
|
|
|
const Name* rename = NULL;
|
|
|
|
|
@@ -182,9 +247,10 @@ createFindResult(const RRClass& rrclass,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset,
|
|
|
- rrclass, rename),
|
|
|
- flags, node));
|
|
|
+ return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rdset,
|
|
|
+ rrclass, options,
|
|
|
+ rename),
|
|
|
+ flags, zone_data, node, rdset));
|
|
|
}
|
|
|
|
|
|
// A helper function for NSEC-signed zones. It searches the zone for
|
|
@@ -341,19 +407,22 @@ public:
|
|
|
// caught above.
|
|
|
//
|
|
|
// If none of the above succeeds, we conclude the name doesn't exist in
|
|
|
-// the zone, and throw an OutOfZone exception.
|
|
|
+// the zone, and throw an OutOfZone exception by default. If the optional
|
|
|
+// out_of_zone_ok is true, it returns an NXDOMAIN result with NULL data so
|
|
|
+// the caller can take an action to it (technically it's not "NXDOMAIN",
|
|
|
+// but the caller is assumed not to rely on the difference.)
|
|
|
FindNodeResult findNode(const ZoneData& zone_data,
|
|
|
- const Name& name,
|
|
|
+ const LabelSequence& name_labels,
|
|
|
ZoneChain& node_path,
|
|
|
- ZoneFinder::FindOptions options)
|
|
|
+ ZoneFinder::FindOptions options,
|
|
|
+ bool out_of_zone_ok = false)
|
|
|
{
|
|
|
ZoneNode* node = NULL;
|
|
|
FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
|
|
|
|
|
|
const ZoneTree& tree(zone_data.getZoneTree());
|
|
|
- ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name),
|
|
|
- &node, node_path, cutCallback,
|
|
|
- &state);
|
|
|
+ const ZoneTree::Result result = tree.find(name_labels, &node, node_path,
|
|
|
+ cutCallback, &state);
|
|
|
const unsigned int zonecut_flag =
|
|
|
(state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
|
|
|
if (result == ZoneTree::EXACTMATCH) {
|
|
@@ -377,7 +446,7 @@ FindNodeResult findNode(const ZoneData& zone_data,
|
|
|
if (node_path.getLastComparisonResult().getRelation() ==
|
|
|
NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
|
|
|
LOG_DEBUG(logger, DBG_TRACE_DATA,
|
|
|
- DATASRC_MEM_SUPER_STOP).arg(name);
|
|
|
+ DATASRC_MEM_SUPER_STOP).arg(name_labels);
|
|
|
const ZoneNode* nsec_node;
|
|
|
const RdataSet* nsec_rds = getClosestNSEC(zone_data,
|
|
|
node_path,
|
|
@@ -398,7 +467,7 @@ FindNodeResult findNode(const ZoneData& zone_data,
|
|
|
// baz.foo.wild.example. The common ancestor, foo.wild.example,
|
|
|
// should cancel wildcard. Treat it as NXDOMAIN.
|
|
|
LOG_DEBUG(logger, DBG_TRACE_DATA,
|
|
|
- DATASRC_MEM_WILDCARD_CANCEL).arg(name);
|
|
|
+ DATASRC_MEM_WILDCARD_CANCEL).arg(name_labels);
|
|
|
const ZoneNode* nsec_node;
|
|
|
const RdataSet* nsec_rds = getClosestNSEC(zone_data,
|
|
|
node_path,
|
|
@@ -431,53 +500,198 @@ FindNodeResult findNode(const ZoneData& zone_data,
|
|
|
FindNodeResult::FIND_WILDCARD | zonecut_flag));
|
|
|
}
|
|
|
|
|
|
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
|
|
|
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
|
|
|
+ arg(name_labels);
|
|
|
const ZoneNode* nsec_node;
|
|
|
const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path,
|
|
|
&nsec_node, options);
|
|
|
return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds));
|
|
|
} else {
|
|
|
// If the name is neither an exact or partial match, it is
|
|
|
- // out of bailiwick, which is considered an error.
|
|
|
- isc_throw(OutOfZone, name.toText() << " not in " <<
|
|
|
+ // out of bailiwick, which is considered an error, unless the caller
|
|
|
+ // is willing to accept it.
|
|
|
+ if (out_of_zone_ok) {
|
|
|
+ return (FindNodeResult(ZoneFinder::NXDOMAIN, NULL, NULL));
|
|
|
+ }
|
|
|
+ isc_throw(OutOfZone, name_labels << " not in " <<
|
|
|
zone_data.getOriginNode()->getName());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
-// Specialization of the ZoneFinder::Context for the in-memory finder.
|
|
|
+
|
|
|
+/// \brief Specialization of the ZoneFinder::Context for the in-memory finder.
|
|
|
+///
|
|
|
+/// Note that we don't have a specific constructor for the findAll() case.
|
|
|
+/// For (successful) type ANY query, found_node points to the
|
|
|
+/// corresponding zone node, which is recorded within this specialized
|
|
|
+/// context.
|
|
|
class InMemoryZoneFinder::Context : public ZoneFinder::Context {
|
|
|
public:
|
|
|
- /// \brief Constructor.
|
|
|
- ///
|
|
|
- /// Note that we don't have a specific constructor for the findAll() case.
|
|
|
- /// For (successful) type ANY query, found_node points to the
|
|
|
- /// corresponding RB node, which is recorded within this specialized
|
|
|
- /// context.
|
|
|
Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
|
|
|
- ZoneFinderResultContext result) :
|
|
|
+ const RRClass& rrclass, const ZoneFinderResultContext& result) :
|
|
|
ZoneFinder::Context(finder, options,
|
|
|
ResultContext(result.code, result.rrset,
|
|
|
result.flags)),
|
|
|
- rrset_(result.rrset), found_node_(result.found_node)
|
|
|
+ rrclass_(rrclass), zone_data_(result.zone_data),
|
|
|
+ found_node_(result.found_node),
|
|
|
+ found_rdset_(result.found_rdset)
|
|
|
{}
|
|
|
|
|
|
+protected:
|
|
|
+ virtual void getAdditionalImpl(const std::vector<RRType>& requested_types,
|
|
|
+ std::vector<ConstRRsetPtr>& result)
|
|
|
+ {
|
|
|
+ if (found_rdset_ != NULL) {
|
|
|
+ // Normal query with successful result.
|
|
|
+ getAdditionalForRdataset(found_rdset_, requested_types, result,
|
|
|
+ options_);
|
|
|
+ } else if (found_node_ != NULL) {
|
|
|
+ // Successful type ANY query result. Call
|
|
|
+ // getAdditionalForRdataset for each RdataSet of the node.
|
|
|
+ for (const RdataSet* rdset = found_node_->getData();
|
|
|
+ rdset != NULL;
|
|
|
+ rdset = rdset->getNext())
|
|
|
+ {
|
|
|
+ getAdditionalForRdataset(rdset, requested_types, result,
|
|
|
+ options_);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private:
|
|
|
- const TreeNodeRRsetPtr rrset_;
|
|
|
+ // Main subroutine of getAdditionalImpl, iterate over Rdata fields
|
|
|
+ // find, create, and insert necessary additional RRsets.
|
|
|
+ void
|
|
|
+ getAdditionalForRdataset(const RdataSet* rdset,
|
|
|
+ const std::vector<RRType>& requested_types,
|
|
|
+ std::vector<ConstRRsetPtr>& result,
|
|
|
+ ZoneFinder::FindOptions orig_options) const
|
|
|
+ {
|
|
|
+ ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
|
|
|
+ if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
|
|
|
+ options = options | ZoneFinder::FIND_DNSSEC;
|
|
|
+ }
|
|
|
+ if (rdset->type == RRType::NS()) {
|
|
|
+ options = options | ZoneFinder::FIND_GLUE_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ RdataReader(rrclass_, rdset->type, rdset->getDataBuf(),
|
|
|
+ rdset->getRdataCount(), rdset->getSigRdataCount(),
|
|
|
+ boost::bind(&Context::findAdditional, this,
|
|
|
+ &requested_types, &result, options, _1, _2),
|
|
|
+ &RdataReader::emptyDataAction).iterate();
|
|
|
+ }
|
|
|
+
|
|
|
+ // RdataReader callback for additional section processing.
|
|
|
+ void
|
|
|
+ findAdditional(const std::vector<RRType>* requested_types,
|
|
|
+ std::vector<ConstRRsetPtr>* result,
|
|
|
+ ZoneFinder::FindOptions options,
|
|
|
+ const LabelSequence& name_labels,
|
|
|
+ RdataNameAttributes attr) const;
|
|
|
+
|
|
|
+ // Subroutine for findAdditional() to unify the normal and wildcard match
|
|
|
+ // cases.
|
|
|
+ void
|
|
|
+ findAdditionalHelper(const std::vector<RRType>* requested_types,
|
|
|
+ std::vector<ConstRRsetPtr>* result,
|
|
|
+ const ZoneNode* node,
|
|
|
+ ZoneFinder::FindOptions options,
|
|
|
+ const Name* real_name) const
|
|
|
+ {
|
|
|
+ const std::vector<RRType>::const_iterator type_beg =
|
|
|
+ requested_types->begin();
|
|
|
+ const std::vector<RRType>::const_iterator type_end =
|
|
|
+ requested_types->end();
|
|
|
+ for (const RdataSet* rdset = node->getData();
|
|
|
+ rdset != NULL;
|
|
|
+ rdset = rdset->getNext())
|
|
|
+ {
|
|
|
+ // Checking all types for all RdataSets could be suboptimal.
|
|
|
+ // This can be a bit more optimized, but unless we have many
|
|
|
+ // requested types the effect is probably marginal. For now we
|
|
|
+ // keep it simple.
|
|
|
+ if (std::find(type_beg, type_end, rdset->type) != type_end) {
|
|
|
+ result->push_back(createTreeNodeRRset(node, rdset, rrclass_,
|
|
|
+ options, real_name));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ const RRClass rrclass_;
|
|
|
+ const ZoneData* const zone_data_;
|
|
|
const ZoneNode* const found_node_;
|
|
|
+ const RdataSet* const found_rdset_;
|
|
|
};
|
|
|
|
|
|
+void
|
|
|
+InMemoryZoneFinder::Context::findAdditional(
|
|
|
+ const std::vector<RRType>* requested_types,
|
|
|
+ std::vector<ConstRRsetPtr>* result,
|
|
|
+ ZoneFinder::FindOptions options,
|
|
|
+ const LabelSequence& name_labels,
|
|
|
+ RdataNameAttributes attr) const
|
|
|
+{
|
|
|
+ // Ignore name data that don't need additional processing.
|
|
|
+ if ((attr & NAMEATTR_ADDITIONAL) == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Find the zone node for the additional name. By passing true as the
|
|
|
+ // last parameter of findNode() we ignore out-of-zone names.
|
|
|
+ ZoneChain node_path;
|
|
|
+ const FindNodeResult node_result =
|
|
|
+ findNode(*zone_data_, name_labels, node_path, options, true);
|
|
|
+ // we only need non-empty exact match
|
|
|
+ if (node_result.code != SUCCESS) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ignore data at a zone cut (due to subdomain delegation) unless glue is
|
|
|
+ // allowed. Checking the node callback flag is a cheap way to detect
|
|
|
+ // zone cuts, but it includes DNAME delegation, in which case we should
|
|
|
+ // keep finding the additional records regardless of the 'GLUE_OK' flag.
|
|
|
+ // The last two conditions limit the case to delegation NS, i.e, the node
|
|
|
+ // has an NS and it's not the zone origin.
|
|
|
+ const ZoneNode* node = node_result.node;
|
|
|
+ if ((options & ZoneFinder::FIND_GLUE_OK) == 0 &&
|
|
|
+ node->getFlag(ZoneNode::FLAG_CALLBACK) &&
|
|
|
+ node != zone_data_->getOriginNode() &&
|
|
|
+ RdataSet::find(node->getData(), RRType::NS()) != NULL) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Examine RdataSets of the node, and create and insert requested types
|
|
|
+ // of RRsets as we find them.
|
|
|
+ if ((node_result.flags & FindNodeResult::FIND_WILDCARD) == 0) {
|
|
|
+ // normal case
|
|
|
+ findAdditionalHelper(requested_types, result, node, options, NULL);
|
|
|
+ } else {
|
|
|
+ // if the additional name is subject to wildcard substitution, we need
|
|
|
+ // to create a name object for the "real" (after substitution) name.
|
|
|
+ // This is expensive, but in the additional processing this should be
|
|
|
+ // very rare cases and acceptable.
|
|
|
+ size_t data_len;
|
|
|
+ const uint8_t* data;
|
|
|
+ data = name_labels.getData(&data_len);
|
|
|
+ util::InputBuffer buffer(data, data_len);
|
|
|
+ const Name real_name(buffer);
|
|
|
+ findAdditionalHelper(requested_types, result, node, options,
|
|
|
+ &real_name);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
boost::shared_ptr<ZoneFinder::Context>
|
|
|
InMemoryZoneFinder::find(const isc::dns::Name& name,
|
|
|
const isc::dns::RRType& type,
|
|
|
const FindOptions options)
|
|
|
{
|
|
|
- return ZoneFinderContextPtr(new Context(*this, options,
|
|
|
- find_internal(name,
|
|
|
- type,
|
|
|
- NULL,
|
|
|
- options)));
|
|
|
+ return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
|
|
|
+ find_internal(name, type,
|
|
|
+ NULL, options))));
|
|
|
}
|
|
|
|
|
|
boost::shared_ptr<ZoneFinder::Context>
|
|
@@ -485,11 +699,11 @@ InMemoryZoneFinder::findAll(const isc::dns::Name& name,
|
|
|
std::vector<isc::dns::ConstRRsetPtr>& target,
|
|
|
const FindOptions options)
|
|
|
{
|
|
|
- return ZoneFinderContextPtr(new Context(*this, options,
|
|
|
- find_internal(name,
|
|
|
- RRType::ANY(),
|
|
|
- &target,
|
|
|
- options)));
|
|
|
+ return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
|
|
|
+ find_internal(name,
|
|
|
+ RRType::ANY(),
|
|
|
+ &target,
|
|
|
+ options))));
|
|
|
}
|
|
|
|
|
|
ZoneFinderResultContext
|
|
@@ -502,10 +716,11 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
|
|
|
// in findNode(). We simply construct a result structure and return.
|
|
|
ZoneChain node_path;
|
|
|
const FindNodeResult node_result =
|
|
|
- findNode(zone_data_, name, node_path, options);
|
|
|
+ findNode(zone_data_, LabelSequence(name), node_path, options);
|
|
|
if (node_result.code != SUCCESS) {
|
|
|
return (createFindResult(rrclass_, zone_data_, node_result.code,
|
|
|
- node_result.rrset, node_result.node));
|
|
|
+ node_result.rrset, node_result.node,
|
|
|
+ options));
|
|
|
}
|
|
|
|
|
|
const ZoneNode* node = node_result.node;
|
|
@@ -524,25 +739,26 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
|
|
|
const RdataSet* nsec_rds = getClosestNSEC(zone_data_, node_path,
|
|
|
&nsec_node, options);
|
|
|
return (createFindResult(rrclass_, zone_data_, NXRRSET,
|
|
|
- nsec_rds,
|
|
|
- nsec_node,
|
|
|
- wild));
|
|
|
+ nsec_rds, nsec_node, options, wild));
|
|
|
}
|
|
|
|
|
|
const RdataSet* found;
|
|
|
|
|
|
// If the node callback is enabled, this may be a zone cut. If it
|
|
|
// has a NS RR, we should return a delegation, but not in the apex.
|
|
|
- // There is one exception: the case for DS query, which should always
|
|
|
- // be considered in-zone lookup.
|
|
|
+ // There are two exceptions:
|
|
|
+ // - the case for DS query, which should always be considered in-zone
|
|
|
+ // lookup.
|
|
|
+ // - when we are looking for glue records (FIND_GLUE_OK)
|
|
|
if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
|
|
|
- node != zone_data_.getOriginNode() && type != RRType::DS()) {
|
|
|
+ (options & FIND_GLUE_OK) == 0 &&
|
|
|
+ node != zone_data_.getOriginNode() && type != RRType::DS()) {
|
|
|
found = RdataSet::find(node->getData(), RRType::NS());
|
|
|
if (found != NULL) {
|
|
|
LOG_DEBUG(logger, DBG_TRACE_DATA,
|
|
|
DATASRC_MEM_EXACT_DELEGATION).arg(name);
|
|
|
return (createFindResult(rrclass_, zone_data_, DELEGATION,
|
|
|
- found, node, wild, &name));
|
|
|
+ found, node, options, wild, &name));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -552,13 +768,13 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
|
|
|
const RdataSet* cur_rds = node->getData();
|
|
|
while (cur_rds != NULL) {
|
|
|
target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_,
|
|
|
- &name));
|
|
|
+ options, &name));
|
|
|
cur_rds = cur_rds->getNext();
|
|
|
}
|
|
|
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
|
|
|
arg(name);
|
|
|
return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node,
|
|
|
- wild, &name));
|
|
|
+ options, wild, &name));
|
|
|
}
|
|
|
|
|
|
found = RdataSet::find(node->getData(), type);
|
|
@@ -567,7 +783,7 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
|
|
|
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
|
|
|
arg(type);
|
|
|
return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node,
|
|
|
- wild, &name));
|
|
|
+ options, wild, &name));
|
|
|
} else {
|
|
|
// Next, try CNAME.
|
|
|
found = RdataSet::find(node->getData(), RRType::CNAME());
|
|
@@ -575,20 +791,166 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
|
|
|
|
|
|
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
|
|
|
return (createFindResult(rrclass_, zone_data_, CNAME, found, node,
|
|
|
- wild, &name));
|
|
|
+ options, wild, &name));
|
|
|
}
|
|
|
}
|
|
|
// No exact match or CNAME. Get NSEC if necessary and return NXRRSET.
|
|
|
return (createFindResult(rrclass_, zone_data_, NXRRSET,
|
|
|
getNSECForNXRRSET(zone_data_, options, node),
|
|
|
- node, wild, &name));
|
|
|
+ node, options, wild, &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");
|
|
|
+
|
|
|
+ uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
|
|
|
+ const LabelSequence origin_ls(zone_data_.getOriginNode()->
|
|
|
+ getAbsoluteLabels(labels_buf));
|
|
|
+ const LabelSequence name_ls(name);
|
|
|
+
|
|
|
+ if (!zone_data_.isNSEC3Signed()) {
|
|
|
+ isc_throw(DataSourceError,
|
|
|
+ "findNSEC3 attempt for non NSEC3 signed zone: " <<
|
|
|
+ origin_ls << "/" << getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ const NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
|
|
|
+ // This would be a programming mistake, as ZoneData::isNSEC3Signed()
|
|
|
+ // should check this.
|
|
|
+ assert(nsec3_data != NULL);
|
|
|
+
|
|
|
+ const ZoneTree& tree = nsec3_data->getNSEC3Tree();
|
|
|
+ if (tree.getNodeCount() == 0) {
|
|
|
+ isc_throw(DataSourceError,
|
|
|
+ "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
|
|
|
+ origin_ls << "/" << getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ const NameComparisonResult cmp_result = name_ls.compare(origin_ls);
|
|
|
+ if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
|
|
|
+ cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
|
|
|
+ isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
|
|
|
+ << name_ls << ", zone: " << origin_ls << "/"
|
|
|
+ << getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Convenient shortcuts
|
|
|
+ const unsigned int olabels = origin_ls.getLabelCount();
|
|
|
+ const unsigned int qlabels = name.getLabelCount();
|
|
|
+ // placeholder of the next closer proof
|
|
|
+ const ZoneNode* covering_node(NULL);
|
|
|
+
|
|
|
+ // Now we'll first look up the origin node and initialize orig_chain
|
|
|
+ // with it.
|
|
|
+ ZoneChain orig_chain;
|
|
|
+ const ZoneNode* node(NULL);
|
|
|
+ ZoneTree::Result result =
|
|
|
+ tree.find<void*>(origin_ls, &node, orig_chain, NULL, NULL);
|
|
|
+ if (result != ZoneTree::EXACTMATCH) {
|
|
|
+ // If the origin node doesn't exist, simply fail.
|
|
|
+ isc_throw(DataSourceError,
|
|
|
+ "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
|
|
|
+ origin_ls << "/" << getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ const boost::scoped_ptr<NSEC3Hash> hash
|
|
|
+ (NSEC3Hash::create(nsec3_data->hashalg,
|
|
|
+ nsec3_data->iterations,
|
|
|
+ nsec3_data->getSaltData(),
|
|
|
+ nsec3_data->getSaltLen()));
|
|
|
+
|
|
|
+ // 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 Name& hname = (labels == qlabels ?
|
|
|
+ name : name.split(qlabels - labels, labels));
|
|
|
+ const std::string hlabel = hash->calculate(hname);
|
|
|
+
|
|
|
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
|
|
|
+ arg(name).arg(labels).arg(hlabel);
|
|
|
+
|
|
|
+ node = NULL;
|
|
|
+ ZoneChain chain(orig_chain);
|
|
|
+
|
|
|
+ // Now, make a label sequence relative to the origin.
|
|
|
+ const Name hlabel_name(hlabel);
|
|
|
+ LabelSequence hlabel_ls(hlabel_name);
|
|
|
+ // Remove trailing '.' making it relative
|
|
|
+ hlabel_ls.stripRight(1);
|
|
|
+
|
|
|
+ // Find hlabel relative to the orig_chain.
|
|
|
+ result = tree.find<void*>(hlabel_ls, &node, chain, NULL, NULL);
|
|
|
+ if (result == ZoneTree::EXACTMATCH) {
|
|
|
+ // We found an exact match.
|
|
|
+ ConstRRsetPtr closest = createNSEC3RRset(node, getClass());
|
|
|
+ ConstRRsetPtr next;
|
|
|
+ if (covering_node != NULL) {
|
|
|
+ next = createNSEC3RRset(covering_node, getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ LOG_DEBUG(logger, DBG_TRACE_BASIC,
|
|
|
+ DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
|
|
|
+ arg(*closest);
|
|
|
+
|
|
|
+ return (FindNSEC3Result(true, labels, closest, next));
|
|
|
+ } else {
|
|
|
+ while ((covering_node = tree.previousNode(chain)) != NULL &&
|
|
|
+ covering_node->isEmpty()) {
|
|
|
+ ;
|
|
|
+ }
|
|
|
+ if (covering_node == NULL) {
|
|
|
+ covering_node = tree.largestNode();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!recursive) { // in non recursive mode, we are done.
|
|
|
+ ConstRRsetPtr closest;
|
|
|
+ if (covering_node != NULL) {
|
|
|
+ closest = createNSEC3RRset(covering_node, 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());
|
|
|
+}
|
|
|
+
|
|
|
+Name
|
|
|
+InMemoryZoneFinder::getOrigin() const {
|
|
|
+ size_t data_len;
|
|
|
+ const uint8_t* data;
|
|
|
+
|
|
|
+ // Normally the label sequence of the origin node should be absolute,
|
|
|
+ // in which case we can simply generate the origin name from the labels.
|
|
|
+ const LabelSequence node_labels = zone_data_.getOriginNode()->getLabels();
|
|
|
+ if (node_labels.isAbsolute()) {
|
|
|
+ data = node_labels.getData(&data_len);
|
|
|
+ } else {
|
|
|
+ // In future we may allow adding out-of-zone names in the zone tree.
|
|
|
+ // For example, to hold out-of-zone NS names so we can establish a
|
|
|
+ // shortcut link to them as an optimization. If and when that happens
|
|
|
+ // the origin node may not have an absolute label (consider the zone
|
|
|
+ // is example.org and we add ns.noexample.org). In that case
|
|
|
+ // we first need to construct the absolute label sequence and then
|
|
|
+ // construct the name.
|
|
|
+ uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
|
|
|
+ const LabelSequence name_labels =
|
|
|
+ zone_data_.getOriginNode()->getAbsoluteLabels(labels_buf);
|
|
|
+ data = name_labels.getData(&data_len);
|
|
|
+ }
|
|
|
+ util::InputBuffer buffer(data, data_len);
|
|
|
+ return (Name(buffer));
|
|
|
}
|
|
|
|
|
|
} // namespace memory
|