#include <cc/data.h>
-#include <datasrc/memory_datasrc.h>
-#include <datasrc/zonetable.h>
#include <datasrc/factory.h>
#include <auth/auth_srv.h>
run_unittests_SOURCES += config_syntax_unittest.cc
run_unittests_SOURCES += command_unittest.cc
run_unittests_SOURCES += common_unittest.cc
-run_unittests_SOURCES += query_unittest.cc
+# Disabled by #2281
+#run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
run_unittests_SOURCES += test_datasrc_clients_mgr.h test_datasrc_clients_mgr.cc
run_unittests_SOURCES += datasrc_clients_builder_unittest.cc
#include <server_common/portconfig.h>
#include <server_common/keyring.h>
-#include <datasrc/memory_datasrc.h>
#include <datasrc/client_list.h>
#include <auth/auth_srv.h>
#include <auth/command.h>
#include <config/ccsession.h>
-#include <datasrc/memory_datasrc.h>
-
#include <asiolink/asiolink.h>
#include <util/unittests/mock_socketsession.h>
#include <cc/data.h>
#include <datasrc/data_source.h>
-#include <datasrc/memory_datasrc.h>
#include <xfr/xfrout_client.h>
#include <dns/rrtype.h>
#include <dns/rdataclass.h>
-#include <datasrc/memory_datasrc.h>
+#include <datasrc/client.h>
#include <datasrc/client_list.h>
#include <auth/query.h>
lib_LTLIBRARIES = libb10-datasrc.la
libb10_datasrc_la_SOURCES = data_source.h
-libb10_datasrc_la_SOURCES += rbnode_rrset.h
-libb10_datasrc_la_SOURCES += rbtree.h
libb10_datasrc_la_SOURCES += exceptions.h
-libb10_datasrc_la_SOURCES += zonetable.h zonetable.cc
libb10_datasrc_la_SOURCES += zone.h zone_finder.h zone_finder.cc
libb10_datasrc_la_SOURCES += zone_finder_context.cc
libb10_datasrc_la_SOURCES += zone_iterator.h
libb10_datasrc_la_SOURCES += database.h database.cc
libb10_datasrc_la_SOURCES += factory.h factory.cc
libb10_datasrc_la_SOURCES += client_list.h client_list.cc
-libb10_datasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc
libb10_datasrc_la_SOURCES += master_loader_callbacks.h
libb10_datasrc_la_SOURCES += master_loader_callbacks.cc
libb10_datasrc_la_SOURCES += rrset_collection_base.h rrset_collection_base.cc
nodist_libb10_datasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
libb10_datasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
-pkglib_LTLIBRARIES = sqlite3_ds.la memory_ds.la static_ds.la
+pkglib_LTLIBRARIES = sqlite3_ds.la static_ds.la
sqlite3_ds_la_SOURCES = sqlite3_accessor.h sqlite3_accessor.cc
sqlite3_ds_la_SOURCES += sqlite3_accessor_link.cc
sqlite3_ds_la_LIBADD += libb10-datasrc.la
sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
-memory_ds_la_SOURCES = memory_datasrc_link.cc
-memory_ds_la_LDFLAGS = -module -avoid-version
-memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-memory_ds_la_LIBADD += libb10-datasrc.la
-
static_ds_la_SOURCES = static_datasrc_link.cc
static_ds_la_LDFLAGS = -module -avoid-version
static_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
-libb10_datasrc_la_LIBADD += $(builddir)/memory/libdatasrc_memory.la
+libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la
libb10_datasrc_la_LIBADD += $(SQLITE_LIBS)
BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc
#include "data_source.h"
#include "database.h"
#include "sqlite3_accessor.h"
-#include "memory_datasrc.h"
#include "datasrc_config.h"
--- /dev/null
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+#ifndef DATASRC_FACTORY_LINK_H
+#define DATASRC_FACTORY_LINK_H
+
+#include <datasrc/database.h>
+#include <cc/data.h>
+
+#include <string>
+
+namespace isc {
+namespace datasrc {
+
+extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr config,
+ std::string& error);
+
+extern "C" void destroyInstance(DataSourceClient* instance);
+
+}
+}
+
+#endif // DATASRC_FACTORY_LINK_H
+
+// Local Variables:
+// mode: c++
+// End:
+++ /dev/null
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/nsec3hash.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/masterload.h>
-
-#include <datasrc/memory_datasrc.h>
-#include <datasrc/rbtree.h>
-#include <datasrc/rbnode_rrset.h>
-#include <datasrc/logger.h>
-#include <datasrc/zone_iterator.h>
-#include <datasrc/data_source.h>
-#include <datasrc/factory.h>
-#include <datasrc/zone_finder.h>
-
-#include <boost/function.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-
-#include <algorithm>
-#include <map>
-#include <utility>
-#include <cctype>
-#include <cassert>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using boost::scoped_ptr;
-
-namespace isc {
-namespace datasrc {
-
-using namespace internal;
-
-namespace {
-// Some type aliases
-
-// A functor type used for loading.
-typedef boost::function<void(ConstRRsetPtr)> LoadCallback;
-
-// RRset specified for this implementation
-typedef boost::shared_ptr<internal::RBNodeRRset> RBNodeRRsetPtr;
-typedef boost::shared_ptr<const internal::RBNodeRRset> ConstRBNodeRRsetPtr;
-
-/*
- * Each domain consists of some RRsets. They will be looked up by the
- * RRType.
- *
- * The use of map is questionable with regard to performance - there'll
- * be usually only few RRsets in the domain, so the log n benefit isn't
- * much and a vector/array might be faster due to its simplicity and
- * continuous memory location. But this is unlikely to be a performance
- * critical place and map has better interface for the lookups, so we use
- * that.
- */
-typedef map<RRType, ConstRBNodeRRsetPtr> Domain;
-typedef Domain::value_type DomainPair;
-typedef boost::shared_ptr<Domain> DomainPtr;
-// The tree stores domains
-typedef RBTree<Domain> DomainTree;
-typedef RBNode<Domain> DomainNode;
-
-// In the following dedicated namespace we define a few application-specific
-// RBNode flags. We use a separate namespace so we can consolidate the
-// definition in a single place, which would hopefully reduce the risk of
-// collisions.
-// (Note: it's within an unnamed namespace, so effectively private.)
-namespace domain_flag {
-// This flag indicates the node is at a "wildcard level" (in short, it means
-// one of the node's immediate child is a wildcard). See addWildcards()
-// for more details.
-const DomainNode::Flags WILD = DomainNode::FLAG_USER1;
-
-// This flag is used for additional record shortcut. If a node has this
-// flag, it's under a zone cut for a delegation to a child zone.
-// Note: for a statically built zone this information is stable, but if we
-// change the implementation to be dynamically modifiable, it may not be
-// realistic to keep this flag update for all affected nodes, and we may
-// have to reconsider the mechanism.
-const DomainNode::Flags GLUE = DomainNode::FLAG_USER2;
-
-// This flag indicates the node is generated as a result of wildcard
-// expansion. In this implementation, this flag can be set only in
-// the separate auxiliary tree of ZoneData (see the structure description).
-const DomainNode::Flags WILD_EXPANDED = DomainNode::FLAG_USER3;
-};
-
-// Separate storage for NSEC3 RRs (and their RRSIGs). It's an STL map
-// from string to the NSEC3 RRset. The map key is the first label
-// (upper cased) of the owner name of the corresponding NSEC3 (i.e., map
-// value). We can use the standard string comparison (if the comparison
-// target is also upper cased) due to the nature of NSEC3 owner names.
-//
-// Note: We maintain the RRsets in the form of RBNodeRRset even if they are
-// not stored in the RB tree. The reason is because comparison can be
-// more efficient if we make sure all RRsets returned from this module are
-// of the same type.
-typedef map<string, ConstRBNodeRRsetPtr> NSEC3Map;
-typedef NSEC3Map::value_type NSEC3Pair;
-
-// Actual zone data: Essentially a set of zone's RRs. This is defined as
-// a separate structure so that it'll be replaceable on reload.
-struct ZoneData {
- // Note: this code is not entirely exception safe; domains_storage_ could
- // leak if the constructor throws. But since it's an intermediate version
- // toward a full revision and the actual risk of leak should be very small
- // in practice, we leave it open for now.
- ZoneData(const Name& origin) :
- domains_storage_(DomainTree::create(local_mem_sgmt_, true)),
- domains_(*domains_storage_),
- aux_wild_domains_(NULL),
- origin_data_(NULL),
- nsec_signed_(false)
- {
- // We create the node for origin (it needs to exist anyway in future)
- domains_.insert(local_mem_sgmt_, origin, &origin_data_);
- DomainPtr origin_domain(new Domain);
- origin_data_->setData(origin_domain);
- }
-
- ~ZoneData() {
- DomainTree::destroy(local_mem_sgmt_, domains_storage_);
- if (aux_wild_domains_ != NULL) {
- DomainTree::destroy(local_mem_sgmt_, aux_wild_domains_);
- }
-
- // The assert may be too harsh, but we assume we'll discard (rewrite)
- // this code soon enough. Until then this would be a good way to
- // detect any memory leak. Also, at that point we shouldn't use
- // a single separate memory segment for each zone tree; normally
- // zone data for multiple zones will be managed in a single segment.
- assert(local_mem_sgmt_.allMemoryDeallocated());
- }
-
- // Memory segment to allocate/deallocate memory for the tree and the nodes.
- // (This will eventually have to be abstract; for now we hardcode the
- // specific derived segment class).
- util::MemorySegmentLocal local_mem_sgmt_;
-
- // The main data (name + RRsets). We use domains_ as a reference to
- // domains_storage_ so we don't have to update the rest of the code;
- // it will eventually have to be revised substantially, at which point
- // we should clean this up, too.
- DomainTree* domains_storage_;
- DomainTree& domains_;
-
- // An auxiliary tree for wildcard expanded data used in additional data
- // processing. It contains names like "ns.wild.example" in the following
- // example:
- // child.wild.example. NS ns.wild.example.
- // *.wild.example IN AAAA 2001:db8::1234
- // (and there's no exact ns.wild.example. in the zone). This tree contains
- // such names with a copy of the RRsets of the matching wildcard name
- // with its owner name expanded, e.g.:
- // ns.wild.example. IN AAAA 2001:db8::1234
- // In theory, this tree could have many such wildcard-expandable names,
- // each of which has a copy of the original list of RRsets. In practice,
- // however, it should be very rare that names for additional section
- // processing are subject to wildcard expansion, so in most cases this tree
- // should be even empty, and even if it has content it should be very
- // small.
-private:
- DomainTree* aux_wild_domains_;
-public:
- DomainTree& getAuxWildDomains() {
- if (aux_wild_domains_ == NULL) {
- aux_wild_domains_ = DomainTree::create(local_mem_sgmt_);
- }
- return (*aux_wild_domains_);
- }
-
- // Shortcut to the origin node, which should always exist
- DomainNode* origin_data_;
-
- // The optional NSEC3 related data
- struct NSEC3Data {
- NSEC3Data(const generic::NSEC3PARAM& nsec3param) :
- hash_(NSEC3Hash::create(nsec3param))
- {}
- NSEC3Data(const generic::NSEC3& nsec3) :
- hash_(NSEC3Hash::create(nsec3))
- {}
- NSEC3Map map_; // Actual NSEC3 RRs
- const scoped_ptr<NSEC3Hash> hash_; // hash parameter/calculator
- };
- scoped_ptr<NSEC3Data> nsec3_data_; // non NULL only when it's NSEC3 signed
- bool nsec_signed_; // True if there's at least one NSEC record
-
- // This templated structure encapsulates the find result of findNode()
- // method (also templated) below.
- // The template parameter is expected to be either 'const DomainNode' or
- // 'DomainNode' (to avoid misuse the template definition itself is kept
- // private - we only expose expected typedefs). The former is expected
- // to be used for lookups, and the latter is expected to be used for
- // constructing the zone.
-private:
- template <typename NodeType>
- struct FindNodeResultBase {
- // Bitwise flags to represent supplemental information of the
- // search result:
- // Search resulted in a wildcard match.
- static const unsigned int FIND_WILDCARD = 1;
- // Search encountered a zone cut due to NS but continued to look for
- // a glue.
- static const unsigned int FIND_ZONECUT = 2;
-
- FindNodeResultBase(ZoneFinder::Result code_param,
- NodeType* node_param,
- ConstRBNodeRRsetPtr rrset_param,
- unsigned int flags_param = 0) :
- code(code_param), node(node_param), rrset(rrset_param),
- flags(flags_param)
- {}
- const ZoneFinder::Result code;
- NodeType* const node;
- ConstRBNodeRRsetPtr const rrset;
- const unsigned int flags;
- };
-public:
- typedef FindNodeResultBase<const DomainNode> FindNodeResult;
- typedef FindNodeResultBase<DomainNode> FindMutableNodeResult;
-
- // Identify the RBTree node that best matches the given name.
- // See implementation notes below.
- //
- // The caller should pass an empty node_path, and it will contain the
- // search context (all ancestor nodes that the underlying RBTree search
- // traverses, and how the search stops) for possible later use at the
- // caller side.
- template <typename ResultType>
- ResultType findNode(const Name& name,
- RBTreeNodeChain<Domain>& node_path,
- ZoneFinder::FindOptions options) const;
-
- // A helper method for NSEC-signed zones. It searches the zone for
- // the "closest" NSEC corresponding to the search context stored in
- // node_path (it should contain sufficient information to identify the
- // previous name of the query name in the zone). In some cases the
- // immediate closest name may not have NSEC (when it's under a zone cut
- // for glue records, or even when the zone is partly broken), so this
- // method continues the search until it finds a name that has NSEC,
- // and returns the one found first. Due to the prerequisite (see below),
- // it should always succeed.
- //
- // node_path must store valid search context (in practice, it's expected
- // to be set by findNode()); otherwise the underlying RBTree implementation
- // throws.
- //
- // If the zone is not considered NSEC-signed or DNSSEC records were not
- // required in the original search context (specified in options), this
- // method doesn't bother to find NSEC, and simply returns NULL. So, by
- // definition of "NSEC-signed", when it really tries to find an NSEC it
- // should succeed; there should be one at least at the zone origin.
- ConstRBNodeRRsetPtr
- getClosestNSEC(RBTreeNodeChain<Domain>& node_path,
- ZoneFinder::FindOptions options) const;
-};
-
-ConstRBNodeRRsetPtr
-ZoneData::getClosestNSEC(RBTreeNodeChain<Domain>& node_path,
- ZoneFinder::FindOptions options) const
-{
- if (!nsec_signed_ || (options & ZoneFinder::FIND_DNSSEC) == 0) {
- return (ConstRBNodeRRsetPtr());
- }
-
- const DomainNode* prev_node;
- while ((prev_node = domains_.previousNode(node_path)) != NULL) {
- if (!prev_node->isEmpty()) {
- const Domain::const_iterator found =
- prev_node->getData()->find(RRType::NSEC());
- if (found != prev_node->getData()->end()) {
- return (found->second);
- }
- }
- }
- // This must be impossible and should be an internal bug.
- // See the description at the method declaration.
- assert(false);
- // Even though there is an assert here, strict compilers
- // will still need some return value.
- return (ConstRBNodeRRsetPtr());
-}
-
-/// Maintain intermediate data specific to the search context used in
-/// \c find().
-///
-/// It will be passed to \c cutCallback() (see below) and record a possible
-/// zone cut node and related RRset (normally NS or DNAME).
-struct FindState {
- FindState(bool glue_ok) :
- zonecut_node_(NULL),
- dname_node_(NULL),
- glue_ok_(glue_ok)
- {}
-
- // These will be set to a domain node of the highest delegation point,
- // if any. In fact, we could use a single variable instead of both.
- // But then we would need to distinquish these two cases by something
- // else and it seemed little more confusing when this was written.
- const DomainNode* zonecut_node_;
- const DomainNode* dname_node_;
-
- // Delegation RRset (NS or DNAME), if found.
- ConstRBNodeRRsetPtr rrset_;
-
- // Whether to continue search below a delegation point.
- // Set at construction time.
- const bool glue_ok_;
-};
-
-// A callback called from possible zone cut nodes and nodes with DNAME.
-// This will be passed from findNode() to \c RBTree::find().
-bool cutCallback(const DomainNode& node, FindState* state) {
- // We need to look for DNAME first, there's allowed case where
- // DNAME and NS coexist in the apex. DNAME is the one to notice,
- // the NS is authoritative, not delegation (corner case explicitly
- // allowed by section 3 of 2672)
- const Domain::const_iterator found_dname(node.getData()->find(
- RRType::DNAME()));
- if (found_dname != node.getData()->end()) {
- LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED);
- state->dname_node_ = &node;
- state->rrset_ = found_dname->second;
- // No more processing below the DNAME (RFC 2672, section 3
- // forbids anything to exist below it, so there's no need
- // to actually search for it). This is strictly speaking
- // a different way than described in 4.1 of that RFC,
- // but because of the assumption in section 3, it has the
- // same behaviour.
- return (true);
- }
-
- // Look for NS
- const Domain::const_iterator found_ns(node.getData()->find(RRType::NS()));
- if (found_ns != node.getData()->end()) {
- // We perform callback check only for the highest zone cut in the
- // rare case of nested zone cuts.
- if (state->zonecut_node_ != NULL) {
- return (false);
- }
-
- LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
-
- // BIND 9 checks if this node is not the origin. That's probably
- // because it can support multiple versions for dynamic updates
- // and IXFR, and it's possible that the callback is called at
- // the apex and the DNAME doesn't exist for a particular version.
- // It cannot happen for us (at least for now), so we don't do
- // that check.
- state->zonecut_node_ = &node;
- state->rrset_ = found_ns->second;
-
- // Unless glue is allowed the search stops here, so we return
- // false; otherwise return true to continue the search.
- return (!state->glue_ok_);
- }
-
- // This case should not happen because we enable callback only
- // when we add an RR searched for above.
- assert(0);
- // This is here to avoid warning (therefore compilation error)
- // in case assert is turned off. Otherwise we could get "Control
- // reached end of non-void function".
- return (false);
-}
-
-// Implementation notes: this method identifies an RBT node that best matches
-// the give name in terms of DNS query handling. In many cases,
-// DomainTree::find() will result in EXACTMATCH or PARTIALMATCH (note that
-// the given name is generally expected to be contained in the zone, so
-// even if it doesn't exist, it should at least match the zone origin).
-// If it finds an exact match, that's obviously the best one. The partial
-// match case is more complicated.
-//
-// We first need to consider the case where search hits a delegation point,
-// either due to NS or DNAME. They are indicated as either dname_node_ or
-// zonecut_node_ being non NULL. Usually at most one of them will be
-// something else than NULL (it might happen both are NULL, in which case we
-// consider it NOT FOUND). There's one corner case when both might be
-// something else than NULL and it is in case there's a DNAME under a zone
-// cut and we search in glue OK mode ‒ in that case we don't stop on the
-// domain with NS and ignore it for the answer, but it gets set anyway. Then
-// we find the DNAME and we need to act by it, therefore we first check for
-// DNAME and then for NS. In all other cases it doesn't matter, as at least
-// one of them is NULL.
-//
-// Next, we need to check if the RBTree search stopped at a node for a
-// subdomain of the search name (so the comparison result that stopped the
-// search is "SUPERDOMAIN"), it means the stopping node is an empty
-// non-terminal node. In this case the search name is considered to exist
-// but no data should be found there.
-//
-// If none of above is the case, we then consider whether there's a matching
-// wildcard. DomainTree::find() records the node if it encounters a
-// "wildcarding" node, i.e., the immediate ancestor of a wildcard name
-// (e.g., wild.example.com for *.wild.example.com), and returns it if it
-// doesn't find any node that better matches the query name. In this case
-// we'll check if there's indeed a wildcard below the wildcarding node.
-//
-// Note, first, that the wildcard is checked after the empty
-// non-terminal domain case above, because if that one triggers, it
-// means we should not match according to 4.3.3 of RFC 1034 (the query
-// name is known to exist).
-//
-// Before we try to find a wildcard, we should check whether there's
-// an existing node that would cancel the wildcard match. If
-// DomainTree::find() stopped at a node which has a common ancestor
-// with the query name, it might mean we are comparing with a
-// non-wildcard node. In that case, we check which part is common. If
-// we have something in common that lives below the node we got (the
-// one above *), then we should cancel the match according to section
-// 4.3.3 of RFC 1034 (as the name between the wildcard domain and the
-// query name is known to exist).
-//
-// If there's no node below the wildcarding node that shares a common ancestor
-// of the query name, we can conclude the wildcard is the best match.
-// We'll then identify the wildcard node via an incremental search. Note that
-// there's no possibility that the query name is at an empty non terminal
-// node below the wildcarding node at this stage; that case should have been
-// caught above.
-//
-// If none of the above succeeds, we conclude the name doesn't exist in
-// the zone.
-template <typename ResultType>
-ResultType
-ZoneData::findNode(const Name& name, RBTreeNodeChain<Domain>& node_path,
- ZoneFinder::FindOptions options) const
-{
- DomainNode* node = NULL;
- FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
-
- const DomainTree::Result result =
- domains_.find(LabelSequence(name), &node, node_path,
- cutCallback, &state);
- const unsigned int zonecut_flag =
- (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
- if (result == DomainTree::EXACTMATCH) {
- return (ResultType(ZoneFinder::SUCCESS, node, state.rrset_,
- zonecut_flag));
- } else if (result == DomainTree::PARTIALMATCH) {
- assert(node != NULL);
- if (state.dname_node_ != NULL) { // DNAME
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
- arg(state.rrset_->getName());
- return (ResultType(ZoneFinder::DNAME, NULL, state.rrset_));
- }
- if (state.zonecut_node_ != NULL) { // DELEGATION due to NS
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
- arg(state.rrset_->getName());
- return (ResultType(ZoneFinder::DELEGATION, NULL, state.rrset_));
- }
- if (node_path.getLastComparisonResult().getRelation() ==
- NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name);
- return (ResultType(ZoneFinder::NXRRSET, node,
- getClosestNSEC(node_path, options)));
- }
- if (node->getFlag(domain_flag::WILD) && // maybe a wildcard, check only
- (options & ZoneFinder::NO_WILDCARD) == 0) { // if not disabled.
- if (node_path.getLastComparisonResult().getRelation() ==
- NameComparisonResult::COMMONANCESTOR) {
- // This means, e.g., we have *.wild.example and
- // bar.foo.wild.example and are looking for
- // 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);
- return (ResultType(ZoneFinder::NXDOMAIN, NULL,
- getClosestNSEC(node_path, options)));
- }
- // Now the wildcard should be the best match.
- const Name wildcard(Name("*").concatenate(
- node_path.getAbsoluteName()));
-
- // Clear the node_path so that we don't keep incorrect (NSEC)
- // context
- node_path.clear();
- DomainTree::Result result(domains_.find(wildcard, &node,
- node_path));
- // Otherwise, why would the domain_flag::WILD be there if
- // there was no wildcard under it?
- assert(result == DomainTree::EXACTMATCH);
- return (ResultType(ZoneFinder::SUCCESS, node, state.rrset_,
- FindNodeResult::FIND_WILDCARD |
- zonecut_flag));
- }
- // Nothing really matched.
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
- return (ResultType(ZoneFinder::NXDOMAIN, node,
- getClosestNSEC(node_path, options)));
- } 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 " <<
- origin_data_->getName());
- }
-}
-} // unnamed namespace
-
-namespace internal {
-
-/// \brief An encapsulation type for a pointer of an additional node
-/// associated with an \c RBNodeRRset object.
-///
-/// Currently this is defined as a structure only so that it can declared
-/// in rbnode_rrset.h; this is essentially a pointer to \c DomainNode.
-/// In future, however, this structure may have other attributes.
-struct AdditionalNodeInfo {
- explicit AdditionalNodeInfo(DomainNode* node) : node_(node) {}
- DomainNode* node_;
-};
-
-//
-// RBNodeRRset details
-//
-struct RBNodeRRsetImpl {
-public:
- RBNodeRRsetImpl(const ConstRRsetPtr& rrset) : rrset_(rrset)
- {}
-
- ConstRRsetPtr rrset_; ///< Underlying RRset
- scoped_ptr<vector<AdditionalNodeInfo> > additionals_;
-};
-
-RBNodeRRset::RBNodeRRset(const ConstRRsetPtr& rrset) :
- impl_(new RBNodeRRsetImpl(rrset))
-{
-}
-
-RBNodeRRset::~RBNodeRRset() {
- delete impl_;
-}
-
-unsigned int
-RBNodeRRset::getRdataCount() const {
- return (impl_->rrset_->getRdataCount());
-}
-
-const Name&
-RBNodeRRset::getName() const {
- return (impl_->rrset_->getName());
-}
-
-const RRClass&
-RBNodeRRset::getClass() const {
- return (impl_->rrset_->getClass());
-}
-
-const RRType&
-RBNodeRRset::getType() const {
- return (impl_->rrset_->getType());
-}
-
-const RRTTL&
-RBNodeRRset::getTTL() const {
- return (impl_->rrset_->getTTL());
-}
-
-void
-RBNodeRRset::setName(const Name&) {
- isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
-}
-
-void
-RBNodeRRset::setTTL(const RRTTL&) {
- isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
-}
-
-string
-RBNodeRRset::toText() const {
- return (impl_->rrset_->toText());
-}
-
-unsigned int
-RBNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
- return (impl_->rrset_->toWire(renderer));
-}
-
-unsigned int
-RBNodeRRset::toWire(isc::util::OutputBuffer& buffer) const {
- return (impl_->rrset_->toWire(buffer));
-}
-
-void
-RBNodeRRset::addRdata(ConstRdataPtr) {
- isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
-}
-
-void
-RBNodeRRset::addRdata(const Rdata&) {
- isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
-}
-
-RdataIteratorPtr
-RBNodeRRset::getRdataIterator() const {
- return (impl_->rrset_->getRdataIterator());
-}
-
-RRsetPtr
-RBNodeRRset::getRRsig() const {
- return (impl_->rrset_->getRRsig());
-}
-
-unsigned int
-RBNodeRRset::getRRsigDataCount() const {
- return (impl_->rrset_->getRRsigDataCount());
-}
-
-void
-RBNodeRRset::addRRsig(const ConstRdataPtr& rdata) {
- AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
- p->addRRsig(rdata);
-}
-
-void
-RBNodeRRset::addRRsig(const RdataPtr& rdata) {
- AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
- p->addRRsig(rdata);
-}
-
-void
-RBNodeRRset::addRRsig(const AbstractRRset& sigs) {
- AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
- p->addRRsig(sigs);
-}
-
-void
-RBNodeRRset::addRRsig(const ConstRRsetPtr& sigs) {
- AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
- p->addRRsig(sigs);
-}
-
-void
-RBNodeRRset::addRRsig(const RRsetPtr& sigs) {
- AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
- p->addRRsig(sigs);
-}
-
-void
-RBNodeRRset::removeRRsig() {
- AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
- p->removeRRsig();
-}
-
-ConstRRsetPtr
-RBNodeRRset::getUnderlyingRRset() const {
- return (impl_->rrset_);
-}
-
-void
-RBNodeRRset::addAdditionalNode(const AdditionalNodeInfo& additional) {
- // Lazy initialization
- if (!impl_->additionals_) {
- impl_->additionals_.reset(new vector<AdditionalNodeInfo>);
- }
- impl_->additionals_->push_back(additional);
-}
-
-const vector<AdditionalNodeInfo>*
-RBNodeRRset::getAdditionalNodes() const {
- return (impl_->additionals_.get());
-}
-
-void
-RBNodeRRset::copyAdditionalNodes(RBNodeRRset& dst) const {
- if (impl_->additionals_) {
- dst.impl_->additionals_.reset(
- new vector<AdditionalNodeInfo>(impl_->additionals_->begin(),
- impl_->additionals_->end()));
- }
-}
-
-} // end of internal
-
-namespace {
-/*
- * Prepares a rrset to be return as a result.
- *
- * If rename is false, it returns the one provided. If it is true, it
- * creates a new rrset with the same data but with provided name.
- * In addition, if DNSSEC records are required by the original caller of
- * find(), it also creates expanded RRSIG based on the RRSIG of the
- * wildcard RRset.
- * It is designed for wildcard case, where we create the rrsets
- * dynamically.
- */
-ConstRBNodeRRsetPtr
-prepareRRset(const Name& name, const ConstRBNodeRRsetPtr& rrset, bool rename,
- ZoneFinder::FindOptions options)
-{
- if (rename) {
- LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
- arg(rrset->getName()).arg(name);
- RRsetPtr result_base(new RRset(name, rrset->getClass(),
- rrset->getType(), rrset->getTTL()));
- for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
- i->next()) {
- result_base->addRdata(i->getCurrent());
- }
- if ((options & ZoneFinder::FIND_DNSSEC) != 0) {
- ConstRRsetPtr sig_rrset = rrset->getRRsig();
- if (sig_rrset) {
- RRsetPtr result_sig(new RRset(name, sig_rrset->getClass(),
- RRType::RRSIG(),
- sig_rrset->getTTL()));
- for (RdataIteratorPtr i(sig_rrset->getRdataIterator());
- !i->isLast();
- i->next())
- {
- result_sig->addRdata(i->getCurrent());
- }
- result_base->addRRsig(result_sig);
- }
- }
- RBNodeRRsetPtr result(new RBNodeRRset(result_base));
- rrset->copyAdditionalNodes(*result);
- return (result);
- } else {
- ConstRRsetPtr sig_rrset = rrset->getRRsig();
- if (sig_rrset &&
- ((options & ZoneFinder::FIND_DNSSEC) == 0)) {
- RRsetPtr result_base(new RRset(name, rrset->getClass(),
- rrset->getType(),
- rrset->getTTL()));
- for (RdataIteratorPtr i(rrset->getRdataIterator());
- !i->isLast();
- i->next()) {
- result_base->addRdata(i->getCurrent());
- }
-
- RBNodeRRsetPtr result(new RBNodeRRset(result_base));
- rrset->copyAdditionalNodes(*result);
- return (result);
- } else {
- return (rrset);
- }
- }
-}
-
-// Specialized version of ZoneFinder::ResultContext, which specifically
-// holds rrset in the form of RBNodeRRset.
-struct RBNodeResultContext {
- /// \brief Constructor
- ///
- /// The first three parameters correspond to those of
- /// ZoneFinder::ResultContext. If node is non NULL, it specifies the
- /// found RBNode in the search.
- RBNodeResultContext(ZoneFinder::Result code_param,
- ConstRBNodeRRsetPtr rrset_param,
- ZoneFinder::FindResultFlags flags_param,
- const DomainNode* node) :
- code(code_param), rrset(rrset_param), flags(flags_param),
- found_node(node)
- {}
-
- const ZoneFinder::Result code;
- const ConstRBNodeRRsetPtr rrset;
- const ZoneFinder::FindResultFlags flags;
- const DomainNode* const found_node;
-};
-}
-
-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,
- const RBNodeResultContext& result) :
- ZoneFinder::Context(options,
- ResultContext(result.code, result.rrset,
- result.flags)),
- finder_(finder), rrset_(result.rrset), found_node_(result.found_node)
- {}
-
-protected:
- virtual ZoneFinder* getFinder() { return (&finder_); }
-
- virtual const std::vector<isc::dns::ConstRRsetPtr>* getAllRRsets() const {
- return (NULL);
- }
-
- virtual void getAdditionalImpl(const vector<RRType>& requested_types,
- vector<ConstRRsetPtr>& result)
- {
- if (!rrset_) {
- // In this case this context should encapsulate the result of
- // findAll() and found_node_ should point to a valid answer node.
- if (found_node_ == NULL || found_node_->isEmpty()) {
- isc_throw(isc::Unexpected,
- "Invalid call to in-memory getAdditional: caller's "
- "bug or broken zone");
- }
- BOOST_FOREACH(const DomainPair& dom_it, *found_node_->getData()) {
- getAdditionalForRRset(*dom_it.second, requested_types,
- result, options_);
- }
- } else {
- getAdditionalForRRset(*rrset_, requested_types, result, options_);
- }
- }
-
-private:
- // Retrieve additional RRsets for a given RRset associated in the context.
- // The process is straightforward: it examines the link to
- // AdditionalNodeInfo vector (if set), and find RRsets of the requested
- // type for each node.
- static void getAdditionalForRRset(const RBNodeRRset& rrset,
- const vector<RRType>& requested_types,
- vector<ConstRRsetPtr>& result,
- ZoneFinder::FindOptions options)
- {
- const vector<AdditionalNodeInfo>* additionals_ =
- rrset.getAdditionalNodes();
- if (additionals_ == NULL) {
- return;
- }
- const bool glue_ok = (rrset.getType() == RRType::NS());
- BOOST_FOREACH(const AdditionalNodeInfo& additional, *additionals_) {
- assert(additional.node_ != NULL);
- if (additional.node_->isEmpty()) {
- continue;
- }
- if (!glue_ok && additional.node_->getFlag(domain_flag::GLUE)) {
- continue;
- }
- const bool wild_expanded =
- additional.node_->getFlag(domain_flag::WILD_EXPANDED);
- BOOST_FOREACH(const RRType& rrtype, requested_types) {
- Domain::const_iterator found =
- additional.node_->getData()->find(rrtype);
- if (found != additional.node_->getData()->end()) {
- // If the additional node was generated as a result of
- // wildcard expansion, we return the underlying RRset,
- // in case the caller has the same RRset but as a result
- // of normal find() and needs to know they are of the same
- // kind; otherwise we simply use the stored RBNodeRRset.
- ConstRRsetPtr rp;
- if (wild_expanded) {
- rp = found->second->getUnderlyingRRset();
- } else {
- rp = found->second;
- }
- result.push_back(ZoneFinder::stripRRsigs(rp, options));
- }
- }
- }
- }
-
- ZoneFinder& finder_;
- const ConstRBNodeRRsetPtr rrset_;
- const DomainNode* const found_node_;
-};
-
-// Private data and hidden methods of InMemoryZoneFinder
-struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
- // Constructor
- InMemoryZoneFinderImpl(const RRClass& zone_class, const Name& origin) :
- zone_class_(zone_class), origin_(origin),
- zone_data_(new ZoneData(origin_))
- {}
-
- // Information about the zone
- RRClass zone_class_;
- Name origin_;
- string file_name_;
-
- // The actual zone data
- scoped_ptr<ZoneData> zone_data_;
-
- // Common process for zone load.
- // rrset_installer is a functor that takes another functor as an argument,
- // and expected to call the latter for each RRset of the zone. How the
- // sequence of the RRsets is generated depends on the internal
- // details of the loader: either from a textual master file or from
- // another data source.
- // filename is the file name of the master file or empty if the zone is
- // loaded from another data source.
- void load(const string& filename,
- boost::function<void(LoadCallback)> rrset_installer);
-
- // Add the necessary magic for any wildcard contained in 'name'
- // (including itself) to be found in the zone.
- //
- // In order for wildcard matching to work correctly in find(),
- // we must ensure that a node for the wildcarding level exists in the
- // backend RBTree.
- // E.g. if the wildcard name is "*.sub.example." then we must ensure
- // that "sub.example." exists and is marked as a wildcard level.
- // Note: the "wildcarding level" is for the parent name of the wildcard
- // name (such as "sub.example.").
- //
- // We also perform the same trick for empty wild card names possibly
- // contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
- void addWildcards(util::MemorySegment& mem_sgmt, DomainTree& domains,
- const Name& name)
- {
- Name wname(name);
- const unsigned int labels(wname.getLabelCount());
- const unsigned int origin_labels(origin_.getLabelCount());
- for (unsigned int l = labels;
- l > origin_labels;
- --l, wname = wname.split(1)) {
- if (wname.isWildcard()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_WILDCARD).
- arg(name);
- // Ensure a separate level exists for the "wildcarding" name,
- // and mark the node as "wild".
- DomainNode* node;
- DomainTree::Result result(domains.insert(mem_sgmt,
- wname.split(1),
- &node));
- assert(result == DomainTree::SUCCESS ||
- result == DomainTree::ALREADYEXISTS);
- node->setFlag(domain_flag::WILD);
-
- // Ensure a separate level exists for the wildcard name.
- // Note: for 'name' itself we do this later anyway, but the
- // overhead should be marginal because wildcard names should
- // be rare.
- result = domains.insert(mem_sgmt, wname, &node);
- assert(result == DomainTree::SUCCESS ||
- result == DomainTree::ALREADYEXISTS);
- }
- }
- }
-
- // A helper predicate used in contextCheck() to check if a given domain
- // name has a RRset of type different than NSEC.
- static bool isNotNSEC(const DomainPair& element) {
- return (element.second->getType() != RRType::NSEC());
- }
-
- /*
- * Does some checks in context of the data that are already in the zone.
- * Currently checks for forbidden combinations of RRsets in the same
- * domain (CNAME+anything, DNAME+NS).
- *
- * If such condition is found, it throws AddError.
- */
- void contextCheck(const AbstractRRset& rrset, const Domain& domain) const {
- // Ensure CNAME and other type of RR don't coexist for the same
- // owner name except with NSEC, which is the only RR that can coexist
- // with CNAME (and also RRSIG, which is handled separately)
- if (rrset.getType() == RRType::CNAME()) {
- if (find_if(domain.begin(), domain.end(), isNotNSEC)
- != domain.end()) {
- LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY).
- arg(rrset.getName());
- isc_throw(AddError, "CNAME can't be added with other data for "
- << rrset.getName());
- }
- } else if (rrset.getType() != RRType::NSEC() &&
- domain.find(RRType::CNAME()) != domain.end()) {
- LOG_ERROR(logger, DATASRC_MEM_CNAME_COEXIST).arg(rrset.getName());
- isc_throw(AddError, "CNAME and " << rrset.getType() <<
- " can't coexist for " << rrset.getName());
- }
-
- /*
- * Similar with DNAME, but it must not coexist only with NS and only in
- * non-apex domains.
- * RFC 2672 section 3 mentions that it is implied from it and RFC 2181
- */
- if (rrset.getName() != origin_ &&
- // Adding DNAME, NS already there
- ((rrset.getType() == RRType::DNAME() &&
- domain.find(RRType::NS()) != domain.end()) ||
- // Adding NS, DNAME already there
- (rrset.getType() == RRType::NS() &&
- domain.find(RRType::DNAME()) != domain.end())))
- {
- LOG_ERROR(logger, DATASRC_MEM_DNAME_NS).arg(rrset.getName());
- isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
- "domain " << rrset.getName());
- }
- }
-
- // Validate rrset before adding it to the zone. If something is wrong
- // it throws an exception. It doesn't modify the zone, and provides
- // the strong exception guarantee.
- void addValidation(const ConstRRsetPtr rrset) {
- if (!rrset) {
- isc_throw(NullRRset, "The rrset provided is NULL");
- }
- if (rrset->getRdataCount() == 0) {
- isc_throw(AddError, "The rrset provided is empty: " <<
- rrset->getName() << "/" << rrset->getType());
- }
- // Check for singleton RRs. It should probably handled at a different
- // layer in future.
- if ((rrset->getType() == RRType::CNAME() ||
- rrset->getType() == RRType::DNAME()) &&
- rrset->getRdataCount() > 1)
- {
- // XXX: this is not only for CNAME or DNAME. We should generalize
- // this code for all other "singleton RR types" (such as SOA) in a
- // separate task.
- LOG_ERROR(logger, DATASRC_MEM_SINGLETON).arg(rrset->getName()).
- arg(rrset->getType());
- isc_throw(AddError, "multiple RRs of singleton type for "
- << rrset->getName());
- }
- // NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
- // implementation requests it be so at the moment.
- if ((rrset->getType() == RRType::NSEC3() ||
- rrset->getType() == RRType::NSEC3PARAM()) &&
- rrset->getRdataCount() > 1) {
- isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
- << rrset->getName() << " which isn't supported");
- }
-
- NameComparisonResult compare(origin_.compare(rrset->getName()));
- if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
- compare.getRelation() != NameComparisonResult::EQUAL)
- {
- LOG_ERROR(logger, DATASRC_MEM_OUT_OF_ZONE).arg(rrset->getName()).
- arg(origin_);
- isc_throw(OutOfZone, "The name " << rrset->getName() <<
- " is not contained in zone " << origin_);
- }
-
- // Some RR types do not really work well with a wildcard.
- // Even though the protocol specifically doesn't completely ban such
- // usage, we refuse to load a zone containing such RR in order to
- // keep the lookup logic simpler and more predictable.
- // See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
- // for more technical background. Note also that BIND 9 refuses
- // NS at a wildcard, so in that sense we simply provide compatible
- // behavior.
- if (rrset->getName().isWildcard()) {
- if (rrset->getType() == RRType::NS()) {
- LOG_ERROR(logger, DATASRC_MEM_WILDCARD_NS).
- arg(rrset->getName());
- isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
- rrset->getName());
- }
- if (rrset->getType() == RRType::DNAME()) {
- LOG_ERROR(logger, DATASRC_MEM_WILDCARD_DNAME).
- arg(rrset->getName());
- isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
- rrset->getName());
- }
- }
-
- // Owner names of NSEC3 have special format as defined in RFC5155,
- // and cannot be a wildcard name or must be one label longer than
- // the zone origin. While the RFC doesn't prohibit other forms of
- // names, no sane zone would have such names for NSEC3.
- // BIND 9 also refuses NSEC3 at wildcard.
- if (rrset->getType() == RRType::NSEC3() &&
- (rrset->getName().isWildcard() ||
- rrset->getName().getLabelCount() !=
- origin_.getLabelCount() + 1)) {
- LOG_ERROR(logger, DATASRC_BAD_NSEC3_NAME).
- arg(rrset->getName());
- isc_throw(AddError, "Invalid NSEC3 owner name: " <<
- rrset->getName());
- }
- }
-
- result::Result addRRsig(const ConstRRsetPtr sig_rrset, ZoneData& zone_data)
- {
- // Check consistency of the type covered.
- // We know the RRset isn't empty, so the following check is safe.
- RdataIteratorPtr rit = sig_rrset->getRdataIterator();
- const RRType covered = dynamic_cast<const generic::RRSIG&>(
- rit->getCurrent()).typeCovered();
- for (rit->next(); !rit->isLast(); rit->next()) {
- if (dynamic_cast<const generic::RRSIG&>(
- rit->getCurrent()).typeCovered() != covered) {
- isc_throw(AddError, "RRSIG contains mixed covered types: "
- << sig_rrset->toText());
- }
- }
-
- // Find the RRset to be covered; if not found, treat it as an error
- // for now.
- ConstRRsetPtr covered_rrset;
- if (covered != RRType::NSEC3()) {
- DomainNode* node = NULL;
- if (zone_data.domains_.find(sig_rrset->getName(), &node) !=
- DomainTree::EXACTMATCH || node == NULL || !node->getData()) {
- isc_throw(AddError,
- "RRSIG is being added, but no RR to be covered: "
- << sig_rrset->getName());
- }
- const Domain::const_iterator it = node->getData()->find(covered);
- if (it != node->getData()->end()) {
- covered_rrset = it->second;
- }
- } else {
- // In case of NSEC3 if something is found it must be NSEC3 RRset
- // under the assumption of our current implementation.
- if (zone_data.nsec3_data_) {
- // Convert the first label to upper-cased text. Note that
- // for a valid NSEC3 RR the label should only consist of
- // positive 8-bit char values, so using toupper(int) should be
- // safe (if it's a bogus label for NSEC3 the zone won't work
- // anyway). Also note the '::' below: g++'s STL implementation
- // seems to require it to toupper to make this compile.
- string fst_label =
- sig_rrset->getName().split(0, 1).toText(true);
- transform(fst_label.begin(), fst_label.end(),
- fst_label.begin(), ::toupper);
-
- NSEC3Map::const_iterator found =
- zone_data.nsec3_data_->map_.find(fst_label);
- if (found != zone_data.nsec3_data_->map_.end()) {
- covered_rrset = found->second;
- assert(covered_rrset->getType() == covered);
- }
- }
- }
- if (!covered_rrset) {
- isc_throw(AddError, "RRSIG is being added, but no RR of "
- "covered type found: " << sig_rrset->toText());
- }
-
- // The current implementation doesn't allow an existing RRSIG to be
- // overridden (or updated with additional ones).
- if (covered_rrset->getRRsig()) {
- isc_throw(AddError,
- "RRSIG is being added to override an existing one: "
- << sig_rrset->toText());
- }
-
- // All okay, setting the RRSIG.
- // XXX: we break const-ness of the covered RRsets. In practice the
- // ownership of these RRsets would have been given to us so it should
- // be safe, but it's still a very bad practice.
- // We'll fix this problem anyway when we update the underlying
- // representation so that it's more space efficient.
- // Note: there's a slight chance of getting an exception.
- // As noted in add(), we give up strong exception guarantee in such
- // cases.
- boost::const_pointer_cast<AbstractRRset>(covered_rrset)->addRRsig(sig_rrset);
-
- return (result::SUCCESS);
- }
-
- result::Result addNSEC3(const ConstRRsetPtr rrset, ZoneData& zone_data) {
- // We know rrset has exactly one RDATA
- const generic::NSEC3& nsec3_rdata =
- dynamic_cast<const generic::NSEC3&>(
- rrset->getRdataIterator()->getCurrent());
-
- // If we've not done any NSEC3 setup for the zone, do it now;
- // otherwise check parameter consistency.
- if (!zone_data.nsec3_data_) {
- zone_data.nsec3_data_.reset(new ZoneData::NSEC3Data(nsec3_rdata));
- } else if (!zone_data.nsec3_data_->hash_->match(nsec3_rdata)) {
- isc_throw(AddError, "NSEC3 with inconsistent parameters: " <<
- rrset->toText());
- }
-
- string fst_label = rrset->getName().split(0, 1).toText(true);
- transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
- ::toupper);
-
- // Our current implementation doesn't allow an existing NSEC3 to be
- // updated/overridden.
- if (zone_data.nsec3_data_->map_.find(fst_label) !=
- zone_data.nsec3_data_->map_.end()) {
- return (result::EXIST);
- }
-
- zone_data.nsec3_data_->map_.insert(
- NSEC3Pair(fst_label, ConstRBNodeRRsetPtr(new RBNodeRRset(rrset))));
- return (result::SUCCESS);
- }
-
- /*
- * Implementation of longer methods. We put them here, because the
- * access is without the impl_-> and it will get inlined anyway.
- */
- // Implementation of InMemoryZoneFinder::add
- result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data,
- vector<RBNodeRRset*>* need_additionals)
- {
- // Sanitize input. This will cause an exception to be thrown
- // if the input RRset is empty.
- addValidation(rawrrset);
-
- // OK, can add the RRset.
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
- arg(rawrrset->getName()).arg(rawrrset->getType()).arg(origin_);
-
- // ... although instead of loading the RRset directly, we encapsulate
- // it within an RBNodeRRset. This contains additional information that
- // speeds up queries.
- RBNodeRRsetPtr rrset(new RBNodeRRset(rawrrset));
-
- if (rrset->getType() == RRType::NSEC3()) {
- return (addNSEC3(rrset, zone_data));
- }
-
- // RRSIGs are special in various points, so we handle it in a
- // separate dedicated method.
- if (rrset->getType() == RRType::RRSIG()) {
- return (addRRsig(rrset, zone_data));
- }
-
- // Add wildcards possibly contained in the owner name to the domain
- // tree.
- // Note: this can throw an exception, breaking strong exception
- // guarantee. (see also the note for contextCheck() below).
- addWildcards(zone_data.local_mem_sgmt_, zone_data.domains_,
- rrset->getName());
-
- // Get the node
- DomainNode* node;
- DomainTree::Result result =
- zone_data.domains_.insert(zone_data.local_mem_sgmt_,
- rrset->getName(), &node);
- // Just check it returns reasonable results
- assert((result == DomainTree::SUCCESS ||
- result == DomainTree::ALREADYEXISTS) && node!= NULL);
-
- // Now get the domain
- DomainPtr domain;
- // It didn't exist yet, create it
- if (node->isEmpty()) {
- domain.reset(new Domain);
- node->setData(domain);
- } else { // Get existing one
- domain = node->getData();
- }
-
- // Checks related to the surrounding data.
- // Note: when the check fails and the exception is thrown, it may
- // break strong exception guarantee. At the moment we prefer
- // code simplicity and don't bother to introduce complicated
- // recovery code.
- contextCheck(*rrset, *domain);
-
- // Try inserting the rrset there
- if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
- // Ok, we just put it in
-
- // If this RRset creates a zone cut at this node, mark the node
- // indicating the need for callback in find().
- if (rrset->getType() == RRType::NS() &&
- rrset->getName() != origin_) {
- node->setFlag(DomainNode::FLAG_CALLBACK);
- // If it is DNAME, we have a callback as well here
- } else if (rrset->getType() == RRType::DNAME()) {
- node->setFlag(DomainNode::FLAG_CALLBACK);
- }
-
- if (need_additionals != NULL &&
- (rrset->getType() == RRType::NS() ||
- rrset->getType() == RRType::MX())) {
- need_additionals->push_back(rrset.get());
- }
-
- // If we've added NSEC3PARAM at zone origin, set up NSEC3 specific
- // data or check consistency with already set up parameters.
- if (rrset->getType() == RRType::NSEC3PARAM() &&
- rrset->getName() == origin_) {
- // We know rrset has exactly one RDATA
- const generic::NSEC3PARAM& param =
- dynamic_cast<const generic::NSEC3PARAM&>(
- rrset->getRdataIterator()->getCurrent());
-
- if (!zone_data.nsec3_data_) {
- zone_data.nsec3_data_.reset(
- new ZoneData::NSEC3Data(param));
- } else if (!zone_data.nsec3_data_->hash_->match(param)) {
- isc_throw(AddError, "NSEC3PARAM with inconsistent "
- "parameters: " << rrset->toText());
- }
- } else if (rrset->getType() == RRType::NSEC()) {
- // If it is NSEC signed zone, so we put a flag there
- // (flag is enough)
- zone_data.nsec_signed_ = true;
- }
- return (result::SUCCESS);
- } else {
- // The RRSet of given type was already there
- return (result::EXIST);
- }
- }
-
- /*
- * Same as above, but it checks the return value and if it already exists,
- * it throws.
- */
- void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data,
- vector<RBNodeRRset*>* need_additionals)
- {
- switch (add(set, *zone_data, need_additionals)) {
- case result::EXIST:
- LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
- arg(set->getName()).arg(set->getType());
- isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
- set->toText());
- case result::SUCCESS:
- return;
- default:
- assert(0);
- }
- }
-
- // A helper function for the NXRRSET case in find(). If the zone is
- // NSEC-signed and DNSSEC records are requested, try to find NSEC
- // on the given node, and return it if found; return NULL for all other
- // cases.
- ConstRBNodeRRsetPtr getNSECForNXRRSET(FindOptions options,
- const DomainNode& node) const
- {
- if (zone_data_->nsec_signed_ &&
- (options & ZoneFinder::FIND_DNSSEC) != 0) {
- const Domain::const_iterator found =
- node.getData()->find(RRType::NSEC());
- if (found != node.getData()->end()) {
- return (found->second);
- }
- }
- return (ConstRBNodeRRsetPtr());
- }
-
- // Set up FindContext object as a return value of find(), taking into
- // account wildcard matches and DNSSEC information. We set the NSEC/NSEC3
- // flag when applicable regardless of the find option; the caller would
- // simply ignore these when they didn't request DNSSEC related results.
- // When the optional parameter 'node' is given (in which case it should be
- // non NULL), it means it's a result of ANY query and the context should
- // remember the matched node.
- RBNodeResultContext createFindResult(Result code,
- ConstRBNodeRRsetPtr rrset,
- bool wild = false,
- const DomainNode* node = NULL) const
- {
- FindResultFlags flags = RESULT_DEFAULT;
- if (wild) {
- flags = flags | RESULT_WILDCARD;
- }
- if (code == NXRRSET || code == NXDOMAIN || wild) {
- if (zone_data_->nsec3_data_) {
- flags = flags | RESULT_NSEC3_SIGNED;
- }
- if (zone_data_->nsec_signed_) {
- flags = flags | RESULT_NSEC_SIGNED;
- }
- }
- return (RBNodeResultContext(code, rrset, flags, node));
- }
-
- // Implementation of InMemoryZoneFinder::find
- RBNodeResultContext find(const Name& name, RRType type,
- std::vector<ConstRRsetPtr>* target,
- const FindOptions options) const
- {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
- arg(type);
-
- // Get the node. All other cases than an exact match are handled
- // in findNode(). We simply construct a result structure and return.
- RBTreeNodeChain<Domain> node_path; // findNode will fill in this
- const ZoneData::FindNodeResult node_result =
- zone_data_->findNode<ZoneData::FindNodeResult>(name, node_path,
- options);
- if (node_result.code != SUCCESS) {
- return (createFindResult(node_result.code, node_result.rrset));
- }
-
- // We've found an exact match, may or may not be a result of wildcard.
- const DomainNode* node = node_result.node;
- assert(node != NULL);
- const bool rename = ((node_result.flags &
- ZoneData::FindNodeResult::FIND_WILDCARD) != 0);
-
- // If there is an exact match but the node is empty, it's equivalent
- // to NXRRSET.
- if (node->isEmpty()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
- arg(name);
- return (createFindResult(NXRRSET,
- zone_data_->getClosestNSEC(node_path,
- options),
- rename));
- }
-
- Domain::const_iterator 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.
- if (node->getFlag(DomainNode::FLAG_CALLBACK) &&
- node != zone_data_->origin_data_ && type != RRType::DS()) {
- found = node->getData()->find(RRType::NS());
- if (found != node->getData()->end()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA,
- DATASRC_MEM_EXACT_DELEGATION).arg(name);
- return (createFindResult(DELEGATION,
- prepareRRset(name, found->second,
- rename, options)));
- }
- }
-
- // handle type any query
- if (target != NULL && !node->getData()->empty()) {
- // Empty domain will be handled as NXRRSET by normal processing
- for (found = node->getData()->begin();
- found != node->getData()->end(); ++found)
- {
- target->push_back(prepareRRset(name, found->second, rename,
- options));
- }
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
- arg(name);
- return (createFindResult(SUCCESS, ConstRBNodeRRsetPtr(), rename,
- node));
- }
-
- found = node->getData()->find(type);
- if (found != node->getData()->end()) {
- // Good, it is here
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
- arg(type);
- return (createFindResult(SUCCESS, prepareRRset(name,
- found->second,
- rename, options),
- rename));
- } else {
- // Next, try CNAME.
- found = node->getData()->find(RRType::CNAME());
- if (found != node->getData()->end()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
- return (createFindResult(CNAME,
- prepareRRset(name, found->second,
- rename, options),
- rename));
- }
- }
- // No exact match or CNAME. Get NSEC if necessary and return NXRRSET.
- return (createFindResult(NXRRSET, getNSECForNXRRSET(options, *node),
- rename));
- }
-};
-
-InMemoryZoneFinder::InMemoryZoneFinder(const RRClass& zone_class,
- const Name& origin) :
- impl_(new InMemoryZoneFinderImpl(zone_class, origin))
-{
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin).
- arg(zone_class);
-}
-
-InMemoryZoneFinder::~InMemoryZoneFinder() {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_DESTROY).arg(getOrigin()).
- arg(getClass());
- delete impl_;
-}
-
-Name
-InMemoryZoneFinder::getOrigin() const {
- return (impl_->origin_);
-}
-
-RRClass
-InMemoryZoneFinder::getClass() const {
- return (impl_->zone_class_);
-}
-
-ZoneFinderContextPtr
-InMemoryZoneFinder::find(const Name& name, const RRType& type,
- const FindOptions options)
-{
- return (ZoneFinderContextPtr(
- new Context(*this, options, impl_->find(name, type, NULL,
- options))));
-}
-
-ZoneFinderContextPtr
-InMemoryZoneFinder::findAll(const Name& name,
- std::vector<ConstRRsetPtr>& target,
- const FindOptions options)
-{
- return (ZoneFinderContextPtr(
- new Context(*this, options, impl_->find(name, RRType::ANY(),
- &target, options))));
-}
-
-ZoneFinder::FindNSEC3Result
-InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
- arg(recursive ? "recursive" : "non-recursive");
-
- if (!impl_->zone_data_->nsec3_data_) {
- isc_throw(DataSourceError,
- "findNSEC3 attempt for non NSEC3 signed zone: " <<
- impl_->origin_ << "/" << impl_->zone_class_);
- }
- const NSEC3Map& map = impl_->zone_data_->nsec3_data_->map_;
- if (map.empty()) {
- isc_throw(DataSourceError,
- "findNSEC3 attempt but zone has no NSEC3 RR: " <<
- impl_->origin_ << "/" << impl_->zone_class_);
- }
- const NameComparisonResult cmp_result = name.compare(impl_->origin_);
- if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
- cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
- isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
- << name << ", zone: " << impl_->origin_ << "/"
- << impl_->zone_class_);
- }
-
- // Convenient shortcuts
- const NSEC3Hash& nsec3hash = *impl_->zone_data_->nsec3_data_->hash_;
- const unsigned int olabels = impl_->origin_.getLabelCount();
- const unsigned int qlabels = name.getLabelCount();
-
- ConstRBNodeRRsetPtr covering_proof; // 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 string hlabel = nsec3hash.calculate(
- labels == qlabels ? name : name.split(qlabels - labels, labels));
- NSEC3Map::const_iterator found = map.lower_bound(hlabel);
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
- arg(name).arg(labels).arg(hlabel);
-
- // 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 (found == map.end() || found->first != hlabel) {
- // If the given hash is larger or smaller than everything,
- // the covering proof is the NSEC3 that has the largest hash.
- // Note that we know the map isn't empty, so rbegin() is
- // safe.
- if (found == map.end() || found == map.begin()) {
- covering_proof = map.rbegin()->second;
- } 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_proof = (--found)->second;
- }
- if (!recursive) { // in non recursive mode, we are done.
- LOG_DEBUG(logger, DBG_TRACE_BASIC,
- DATASRC_MEM_FINDNSEC3_COVER).
- arg(name).arg(*covering_proof);
- return (FindNSEC3Result(false, labels, covering_proof,
- ConstRRsetPtr()));
- }
- } else { // found an exact match.
- LOG_DEBUG(logger, DBG_TRACE_BASIC,
- DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
- arg(*found->second);
- return (FindNSEC3Result(true, labels, found->second,
- covering_proof));
- }
- }
-
- isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
- "a broken NSEC3 zone: " << impl_->origin_ << "/"
- << impl_->zone_class_);
-}
-
-result::Result
-InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
- return (impl_->add(rrset, *impl_->zone_data_, NULL));
-}
-
-namespace {
-// This should eventually be more generalized.
-const Name
-getAdditionalName(RRType rrtype, const rdata::Rdata& rdata) {
- if (rrtype == RRType::NS()) {
- const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
- return (ns.getNSName());
- } else {
- // In our usage the only other possible case is MX.
- assert(rrtype == RRType::MX());
- const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
- return (mx.getMXName());
- }
-}
-
-void
-convertAndInsert(const DomainPair& rrset_item, DomainPtr dst_domain,
- const Name* dstname)
-{
- // We copy RRSIGs, too, if they are attached in case we need it in
- // getAdditional().
- dst_domain->insert(DomainPair(rrset_item.first,
- prepareRRset(*dstname, rrset_item.second,
- true,
- ZoneFinder::FIND_DNSSEC)));
-}
-
-void
-addAdditional(RBNodeRRset* rrset, ZoneData* zone_data,
- vector<RBNodeRRset*>* wild_rrsets)
-{
- RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
- bool match_wild = false; // will be true if wildcard match is found
- RBTreeNodeChain<Domain> node_path; // placeholder for findNode()
- for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
- // For each domain name that requires additional section processing
- // in each RDATA, search the tree for the name and remember it if
- // found. If the name is under a zone cut (for a delegation to a
- // child zone), mark the node as "GLUE", so we can selectively
- // include/exclude them when we use it.
-
- const Name& name = getAdditionalName(rrset->getType(),
- rdata_iterator->getCurrent());
- // if the name is not in or below this zone, skip it
- const NameComparisonResult::NameRelation reln =
- name.compare(zone_data->origin_data_->getName()).getRelation();
- if (reln != NameComparisonResult::SUBDOMAIN &&
- reln != NameComparisonResult::EQUAL) {
- continue;
- }
- node_path.clear();
- const ZoneData::FindMutableNodeResult result =
- zone_data->findNode<ZoneData::FindMutableNodeResult>(
- name, node_path, ZoneFinder::FIND_GLUE_OK);
- if (result.code != ZoneFinder::SUCCESS) {
- // We are not interested in anything but a successful match.
- continue;
- }
- DomainNode* node = result.node;
- assert(node != NULL);
- if ((result.flags & ZoneData::FindNodeResult::FIND_ZONECUT) != 0 ||
- (node->getFlag(DomainNode::FLAG_CALLBACK) &&
- node->getData()->find(RRType::NS()) != node->getData()->end())) {
- // The node is under or at a zone cut; mark it as a glue.
- node->setFlag(domain_flag::GLUE);
- }
-
- // A rare case: the additional name may have to be expanded with a
- // wildcard. We'll store the name in a separate auxiliary tree,
- // copying all RRsets of the original wildcard node with expanding
- // the owner name. This is costly in terms of memory, but this case
- // should be pretty rare. On the other hand we won't have to worry
- // about wildcard expansion in getAdditional, which is quite
- // performance sensitive.
- DomainNode* wildnode = NULL;
- if ((result.flags & ZoneData::FindNodeResult::FIND_WILDCARD) != 0) {
- // Wildcard and glue shouldn't coexist. Make it sure here.
- assert(!node->getFlag(domain_flag::GLUE));
-
- if (zone_data->getAuxWildDomains().insert(
- zone_data->local_mem_sgmt_, name, &wildnode)
- == DomainTree::SUCCESS) {
- // If we first insert the node, copy the RRsets. If the
- // original node was empty, we add empty data so
- // addWildAdditional() can get an exactmatch for this name.
- DomainPtr dst_domain(new Domain);
- if (!node->isEmpty()) {
- for_each(node->getData()->begin(), node->getData()->end(),
- boost::bind(convertAndInsert, _1, dst_domain,
- &name));
- }
- wildnode->setData(dst_domain);
- // Mark the node as "wildcard expanded" so it can be
- // distinguished at lookup time.
- wildnode->setFlag(domain_flag::WILD_EXPANDED);
- }
- match_wild = true;
- node = wildnode;
- }
-
- // If this name wasn't subject to wildcard substitution, we can add
- // the additional information to the RRset now; otherwise I'll defer
- // it until the entire auxiliary tree is built (pointers may be
- // invalidated as we build it).
- if (wildnode == NULL) {
- // Note that node may be empty. We should keep it in the list
- // in case we dynamically update the tree and it becomes non empty
- // (which is not supported yet)
- rrset->addAdditionalNode(AdditionalNodeInfo(node));
- }
- }
-
- if (match_wild) {
- wild_rrsets->push_back(rrset);
- }
-}
-
-void
-addWildAdditional(RBNodeRRset* rrset, ZoneData* zone_data) {
- // Similar to addAdditional(), but due to the first stage we know that
- // the rrset should contain a name stored in the auxiliary trees, and
- // that it should be found as an exact match. The RRset may have other
- // names that didn't require wildcard expansion, but we can simply ignore
- // them in this context. (Note that if we find an exact match in the
- // auxiliary tree, it shouldn't be in the original zone; otherwise it
- // shouldn't have resulted in wildcard in the first place).
-
- RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
- for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
- const Name& name = getAdditionalName(rrset->getType(),
- rdata_iterator->getCurrent());
- DomainNode* wildnode = NULL;
- if (zone_data->getAuxWildDomains().find(name, &wildnode) ==
- DomainTree::EXACTMATCH) {
- rrset->addAdditionalNode(AdditionalNodeInfo(wildnode));
- }
- }
-}
-}
-
-void
-InMemoryZoneFinder::InMemoryZoneFinderImpl::load(
- const string& filename,
- boost::function<void(LoadCallback)> rrset_installer)
-{
- vector<RBNodeRRset*> need_additionals;
- scoped_ptr<ZoneData> tmp(new ZoneData(origin_));
-
- rrset_installer(boost::bind(&InMemoryZoneFinderImpl::addFromLoad, this,
- _1, tmp.get(), &need_additionals));
-
- vector<RBNodeRRset*> wild_additionals;
- for_each(need_additionals.begin(), need_additionals.end(),
- boost::bind(addAdditional, _1, tmp.get(), &wild_additionals));
- for_each(wild_additionals.begin(), wild_additionals.end(),
- boost::bind(addWildAdditional, _1, tmp.get()));
-
- // If the zone is NSEC3-signed, check if it has NSEC3PARAM
- if (tmp->nsec3_data_) {
- // Note: origin_data_ is set on creation of ZoneData, and the load
- // process only adds new nodes (and their data), so this assertion
- // should hold.
- assert(tmp->origin_data_ != NULL && !tmp->origin_data_->isEmpty());
- if (tmp->origin_data_->getData()->find(RRType::NSEC3PARAM()) ==
- tmp->origin_data_->getData()->end()) {
- LOG_WARN(logger, DATASRC_MEM_NO_NSEC3PARAM).
- arg(origin_).arg(zone_class_);
- }
- }
-
- // If it went well, put it inside
- file_name_ = filename;
- tmp.swap(zone_data_);
- // And let the old data die with tmp
-}
-
-namespace {
-// A wrapper for dns::masterLoad used by load() below. Essentially it
-// converts the two callback types. Note the mostly redundant wrapper of
-// boost::bind. It converts function<void(ConstRRsetPtr)> to
-// function<void(RRsetPtr)> (masterLoad() expects the latter). SunStudio
-// doesn't seem to do this conversion if we just pass 'callback'.
-void
-masterLoadWrapper(const char* const filename, const Name& origin,
- const RRClass& zone_class, LoadCallback callback)
-{
- masterLoad(filename, origin, zone_class, boost::bind(callback, _1));
-}
-
-// The installer called from Impl::load() for the iterator version of load().
-void
-generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
- ConstRRsetPtr rrset;
- vector<ConstRRsetPtr> rrsigs; // placeholder for RRSIGs until "commitable".
-
- // The current internal implementation assumes an RRSIG is always added
- // after the RRset they cover. So we store any RRSIGs in 'rrsigs' until
- // it's safe to add them; based on our assumption if the owner name
- // changes, all covered RRsets of the previous name should have been
- // installed and any pending RRSIGs can be added at that point. RRSIGs
- // of the last name from the iterator must be added separately.
- while ((rrset = iterator->getNextRRset()) != NULL) {
- if (!rrsigs.empty() && rrset->getName() != rrsigs[0]->getName()) {
- BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
- callback(sig_rrset);
- }
- rrsigs.clear();
- }
- if (rrset->getType() == RRType::RRSIG()) {
- rrsigs.push_back(rrset);
- } else {
- callback(rrset);
- }
- }
-
- BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
- callback(sig_rrset);
- }
-}
-}
-
-void
-InMemoryZoneFinder::load(const std::string& filename) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
- arg(filename);
-
- impl_->load(filename,
- boost::bind(masterLoadWrapper, filename.c_str(), getOrigin(),
- getClass(), _1));
-}
-
-void
-InMemoryZoneFinder::load(ZoneIterator& iterator) {
- impl_->load(string(),
- boost::bind(generateRRsetFromIterator, &iterator, _1));
-}
-
-void
-InMemoryZoneFinder::swap(InMemoryZoneFinder& zone_finder) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()).
- arg(zone_finder.getOrigin());
- std::swap(impl_, zone_finder.impl_);
-}
-
-const string
-InMemoryZoneFinder::getFileName() const {
- return (impl_->file_name_);
-}
-
-/// Implementation details for \c InMemoryClient hidden from the public
-/// interface.
-///
-/// For now, \c InMemoryClient only contains a \c ZoneTable object, which
-/// consists of (pointers to) \c InMemoryZoneFinder objects, we may add more
-/// member variables later for new features.
-class InMemoryClient::InMemoryClientImpl {
-public:
- InMemoryClientImpl() : zone_count(0),
- zone_table(ZoneTable::create(local_mem_sgmt))
- {}
- ~InMemoryClientImpl() {
- ZoneTable::destroy(local_mem_sgmt, zone_table);
-
- // see above for the assert().
- assert(local_mem_sgmt.allMemoryDeallocated());
- }
-
- // Memory segment to allocate/deallocate memory for the zone table.
- // (This will eventually have to be abstract; for now we hardcode the
- // specific derived segment class).
- util::MemorySegmentLocal local_mem_sgmt;
- unsigned int zone_count;
- ZoneTable* zone_table;
-};
-
-InMemoryClient::InMemoryClient() : impl_(new InMemoryClientImpl)
-{}
-
-InMemoryClient::~InMemoryClient() {
- delete impl_;
-}
-
-unsigned int
-InMemoryClient::getZoneCount() const {
- return (impl_->zone_count);
-}
-
-result::Result
-InMemoryClient::addZone(ZoneFinderPtr zone_finder) {
- if (!zone_finder) {
- isc_throw(InvalidParameter,
- "Null pointer is passed to InMemoryClient::addZone()");
- }
-
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE).
- arg(zone_finder->getOrigin()).arg(zone_finder->getClass().toText());
-
- const result::Result result =
- impl_->zone_table->addZone(impl_->local_mem_sgmt, zone_finder);
- if (result == result::SUCCESS) {
- ++impl_->zone_count;
- }
- return (result);
-}
-
-InMemoryClient::FindResult
-InMemoryClient::findZone(const isc::dns::Name& name) const {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_ZONE).arg(name);
- ZoneTable::FindResult result(impl_->zone_table->findZone(name));
- return (FindResult(result.code, result.zone));
-}
-
-namespace {
-
-class MemoryIterator : public ZoneIterator {
-private:
- RBTreeNodeChain<Domain> chain_;
- Domain::const_iterator dom_iterator_;
- const DomainTree& tree_;
- const DomainNode* node_;
- // Only used when separate_rrs_ is true
- RdataIteratorPtr rdata_iterator_;
- bool separate_rrs_;
- bool ready_;
-public:
- MemoryIterator(const DomainTree& tree, const Name& origin, bool separate_rrs) :
- tree_(tree),
- separate_rrs_(separate_rrs),
- ready_(true)
- {
- // Find the first node (origin) and preserve the node chain for future
- // searches
- DomainTree::Result result(tree_.find(origin, &node_, chain_));
- // It can't happen that the origin is not in there
- if (result != DomainTree::EXACTMATCH) {
- isc_throw(Unexpected,
- "In-memory zone corrupted, missing origin node");
- }
- // Initialize the iterator if there's somewhere to point to
- if (node_ != NULL && node_->getData() != DomainPtr()) {
- dom_iterator_ = node_->getData()->begin();
- if (separate_rrs_ && dom_iterator_ != node_->getData()->end()) {
- rdata_iterator_ = dom_iterator_->second->getRdataIterator();
- }
- }
- }
-
- virtual ConstRRsetPtr getNextRRset() {
- if (!ready_) {
- isc_throw(Unexpected, "Iterating past the zone end");
- }
- /*
- * This cycle finds the first nonempty node with yet unused RRset.
- * If it is NULL, we run out of nodes. If it is empty, it doesn't
- * contain any RRsets. If we are at the end, just get to next one.
- */
- while (node_ != NULL && (node_->getData() == DomainPtr() ||
- dom_iterator_ == node_->getData()->end())) {
- node_ = tree_.nextNode(chain_);
- // If there's a node, initialize the iterator and check next time
- // if the map is empty or not
- if (node_ != NULL && node_->getData() != NULL) {
- dom_iterator_ = node_->getData()->begin();
- // New RRset, so get a new rdata iterator
- if (separate_rrs_) {
- rdata_iterator_ = dom_iterator_->second->getRdataIterator();
- }
- }
- }
- if (node_ == NULL) {
- // That's all, folks
- ready_ = false;
- return (ConstRRsetPtr());
- }
-
- if (separate_rrs_) {
- // For separate rrs, reconstruct a new RRset with just the
- // 'current' rdata
- RRsetPtr result(new RRset(dom_iterator_->second->getName(),
- dom_iterator_->second->getClass(),
- dom_iterator_->second->getType(),
- dom_iterator_->second->getTTL()));
- result->addRdata(rdata_iterator_->getCurrent());
- rdata_iterator_->next();
- if (rdata_iterator_->isLast()) {
- // all used up, next.
- ++dom_iterator_;
- // New RRset, so get a new rdata iterator, but only if this
- // was not the final RRset in the chain
- if (dom_iterator_ != node_->getData()->end()) {
- rdata_iterator_ = dom_iterator_->second->getRdataIterator();
- }
- }
- return (result);
- } else {
- // The iterator points to the next yet unused RRset now
- ConstRRsetPtr result(dom_iterator_->second);
-
- // This one is used, move it to the next time for next call
- ++dom_iterator_;
-
- return (result);
- }
- }
-
- virtual ConstRRsetPtr getSOA() const {
- isc_throw(NotImplemented, "Not imelemented");
- }
-};
-
-} // End of anonymous namespace
-
-ZoneIteratorPtr
-InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
- ZoneTable::FindResult result(impl_->zone_table->findZone(name));
- if (result.code != result::SUCCESS) {
- isc_throw(DataSourceError, "No such zone: " + name.toText());
- }
-
- const InMemoryZoneFinder*
- zone(dynamic_cast<const InMemoryZoneFinder*>(result.zone.get()));
- if (zone == NULL) {
- /*
- * TODO: This can happen only during some of the tests and only as
- * a temporary solution. This should be fixed by #1159 and then
- * this cast and check shouldn't be necessary. We don't have
- * test for handling a "can not happen" condition.
- */
- isc_throw(Unexpected, "The zone at " + name.toText() +
- " is not InMemoryZoneFinder");
- }
- return (ZoneIteratorPtr(new MemoryIterator(
- zone->impl_->zone_data_->domains_, name,
- separate_rrs)));
-}
-
-ZoneUpdaterPtr
-InMemoryClient::getUpdater(const isc::dns::Name&, bool, bool) const {
- isc_throw(isc::NotImplemented, "Update attempt on in memory data source");
-}
-
-pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
-InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
- uint32_t) const
-{
- isc_throw(isc::NotImplemented, "Journaling isn't supported for "
- "in memory data source");
-}
-
-} // end of namespace datasrc
-} // end of namespace isc
+++ /dev/null
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef MEMORY_DATA_SOURCE_H
-#define MEMORY_DATA_SOURCE_H 1
-
-#include <string>
-
-#include <boost/noncopyable.hpp>
-
-#include <datasrc/zonetable.h>
-#include <datasrc/client.h>
-
-#include <cc/data.h>
-
-namespace isc {
-namespace dns {
-class Name;
-class RRsetList;
-};
-
-namespace datasrc {
-
-/// A derived zone finder class intended to be used with the memory data source.
-///
-/// Conceptually this "finder" maintains a local in-memory copy of all RRs
-/// of a single zone from some kind of source (right now it's a textual
-/// master file, but it could also be another data source with a database
-/// backend). This is why the class has methods like \c load() or \c add().
-///
-/// This class is non copyable.
-class InMemoryZoneFinder : boost::noncopyable, public ZoneFinder {
- ///
- /// \name Constructors and Destructor.
-public:
- /// \brief Constructor from zone parameters.
- ///
- /// This constructor internally involves resource allocation, and if
- /// it fails, a corresponding standard exception will be thrown.
- /// It never throws an exception otherwise.
- ///
- /// \param rrclass The RR class of the zone.
- /// \param origin The origin name of the zone.
- InMemoryZoneFinder(const isc::dns::RRClass& rrclass,
- const isc::dns::Name& origin);
-
- /// The destructor.
- virtual ~InMemoryZoneFinder();
- //@}
-
- /// \brief Returns the origin of the zone.
- virtual isc::dns::Name getOrigin() const;
-
- /// \brief Returns the class of the zone.
- virtual isc::dns::RRClass getClass() const;
-
- /// \brief Find an RRset in the datasource
- virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options =
- FIND_DEFAULT);
-
- /// \brief Version of find that returns all types at once
- ///
- /// It acts the same as find, just that when the correct node is found,
- /// all the RRsets are filled into the target parameter instead of being
- /// returned by the result.
- virtual ZoneFinderContextPtr findAll(
- const isc::dns::Name& name,
- std::vector<isc::dns::ConstRRsetPtr>& target,
- const FindOptions options = FIND_DEFAULT);
-
- /// Look for NSEC3 for proving (non)existence of given name.
- ///
- /// See documentation in \c Zone.
- virtual FindNSEC3Result
- findNSEC3(const isc::dns::Name& name, bool recursive);
-
- /// \brief Inserts an rrset into the zone.
- ///
- /// It puts another RRset into the zone.
- ///
- /// In the current implementation, this method doesn't allow an existing
- /// RRset to be updated or overridden. So the caller must make sure that
- /// all RRs of the same type and name must be given in the form of a
- /// single RRset. The current implementation will also require that
- /// when an RRSIG is added the RRset to be covered has already been
- /// added. These restrictions are probably too strict when this data
- /// source accepts various forms of input, so they should be revisited
- /// later.
- ///
- /// Except for NullRRset and OutOfZone, this method does not guarantee
- /// strong exception safety (it is currently not needed, if it is needed
- /// in future, it should be implemented).
- ///
- /// \throw NullRRset \c rrset is a NULL pointer.
- /// \throw OutOfZone The owner name of \c rrset is outside of the
- /// origin of the zone.
- /// \throw AddError Other general errors.
- /// \throw Others This method might throw standard allocation exceptions.
- ///
- /// \param rrset The set to add.
- /// \return SUCCESS or EXIST (if an rrset for given name and type already
- /// exists).
- result::Result add(const isc::dns::ConstRRsetPtr& rrset);
-
- /// \brief RRset is NULL exception.
- ///
- /// This is thrown if the provided RRset parameter is NULL.
- struct NullRRset : public InvalidParameter {
- NullRRset(const char* file, size_t line, const char* what) :
- InvalidParameter(file, line, what)
- { }
- };
-
- /// \brief General failure exception for \c add().
- ///
- /// This is thrown against general error cases in adding an RRset
- /// to the zone.
- ///
- /// Note: this exception would cover cases for \c OutOfZone or
- /// \c NullRRset. We'll need to clarify and unify the granularity
- /// of exceptions eventually. For now, exceptions are added as
- /// developers see the need for it.
- struct AddError : public InvalidParameter {
- AddError(const char* file, size_t line, const char* what) :
- InvalidParameter(file, line, what)
- { }
- };
-
- /// Return the master file name of the zone
- ///
- /// This method returns the name of the zone's master file to be loaded.
- /// The returned string will be an empty unless the zone finder has
- /// successfully loaded a zone.
- ///
- /// This method should normally not throw an exception. But the creation
- /// of the return string may involve a resource allocation, and if it
- /// fails, the corresponding standard exception will be thrown.
- ///
- /// \return The name of the zone file loaded in the zone finder, or an empty
- /// string if the zone hasn't loaded any file.
- const std::string getFileName() const;
-
- /// \brief Load zone from masterfile.
- ///
- /// This loads data from masterfile specified by filename. It replaces
- /// current content. The masterfile parsing ability is kind of limited,
- /// see isc::dns::masterLoad.
- ///
- /// This throws isc::dns::MasterLoadError if there is problem with loading
- /// (missing file, malformed, it contains different zone, etc - see
- /// isc::dns::masterLoad for details).
- ///
- /// In case of internal problems, OutOfZone, NullRRset or AssertError could
- /// be thrown, but they should not be expected. Exceptions caused by
- /// allocation may be thrown as well.
- ///
- /// If anything is thrown, the previous content is preserved (so it can
- /// be used to update the data, but if user makes a typo, the old one
- /// is kept).
- ///
- /// \param filename The master file to load.
- ///
- /// \todo We may need to split it to some kind of build and commit/abort.
- /// This will probably be needed when a better implementation of
- /// configuration reloading is written.
- void load(const std::string& filename);
-
- /// \brief Load zone from another data source.
- ///
- /// This is similar to the other version, but zone's RRsets are provided
- /// by an iterator of another data source. On successful load, the
- /// internal filename will be cleared.
- ///
- /// This implementation assumes the iterator produces combined RRsets,
- /// that is, there should exactly one RRset for the same owner name and
- /// RR type. This means the caller is expected to create the iterator
- /// with \c separate_rrs being \c false. This implementation also assumes
- /// RRsets of different names are not mixed; so if the iterator produces
- /// an RRset of a different name than that of the previous RRset, that
- /// previous name must never appear in the subsequent sequence of RRsets.
- /// Note that the iterator API does not ensure this. If the underlying
- /// implementation does not follow it, load() will fail. Note, however,
- /// that this whole interface is tentative. in-memory zone loading will
- /// have to be revisited fundamentally, and at that point this restriction
- /// probably won't matter.
- void load(ZoneIterator& iterator);
-
- /// Exchanges the content of \c this zone finder with that of the given
- /// \c zone_finder.
- ///
- /// This method never throws an exception.
- ///
- /// \param zone_finder Another \c InMemoryZone object which is to
- /// be swapped with \c this zone finder.
- void swap(InMemoryZoneFinder& zone_finder);
-
-private:
- /// \name Hidden private data
- //@{
- struct InMemoryZoneFinderImpl;
- InMemoryZoneFinderImpl* impl_;
- //@}
- // The friend here is for InMemoryClient::getIterator. The iterator
- // needs to access the data inside the zone, so the InMemoryClient
- // extracts the pointer to data and puts it into the iterator.
- // The access is read only.
- friend class InMemoryClient;
-
- /// \brief In-memory version of finder context.
- ///
- /// The implementation (and any specialized interface) is completely local
- /// to the InMemoryZoneFinder class, so it's defined as private
- class Context;
-};
-
-/// \brief A data source client that holds all necessary data in memory.
-///
-/// The \c InMemoryClient class provides an access to a conceptual data
-/// source that maintains all necessary data in a memory image, thereby
-/// allowing much faster lookups. The in memory data is a copy of some
-/// real physical source - in the current implementation a list of zones
-/// are populated as a result of \c addZone() calls; zone data is given
-/// in a standard master file (but there's a plan to use database backends
-/// as a source of the in memory data).
-///
-/// Although every data source client is assumed to be of the same RR class,
-/// the \c InMemoryClient class does not enforce the assumption through
-/// its interface.
-/// For example, the \c addZone() method does not check if the new zone is of
-/// the same RR class as that of the others already in memory.
-/// It is caller's responsibility to ensure this assumption.
-///
-/// <b>Notes to developer:</b>
-///
-/// The addZone() method takes a (Boost) shared pointer because it would be
-/// inconvenient to require the caller to maintain the ownership of zones,
-/// while it wouldn't be safe to delete unnecessary zones inside the dedicated
-/// backend.
-///
-/// The findZone() method takes a domain name and returns the best matching
-/// \c InMemoryZoneFinder in the form of (Boost) shared pointer, so that it can
-/// provide the general interface for all data sources.
-class InMemoryClient : public DataSourceClient {
-public:
- ///
- /// \name Constructors and Destructor.
- ///
- //@{
-
- /// Default constructor.
- ///
- /// This constructor internally involves resource allocation, and if
- /// it fails, a corresponding standard exception will be thrown.
- /// It never throws an exception otherwise.
- InMemoryClient();
-
- /// The destructor.
- ~InMemoryClient();
- //@}
-
- /// Return the number of zones stored in the client.
- ///
- /// This method never throws an exception.
- ///
- /// \return The number of zones stored in the client.
- virtual unsigned int getZoneCount() const;
-
- /// Add a zone (in the form of \c ZoneFinder) to the \c InMemoryClient.
- ///
- /// \c zone_finder must not be associated with a NULL pointer; otherwise
- /// an exception of class \c InvalidParameter will be thrown.
- /// If internal resource allocation fails, a corresponding standard
- /// exception will be thrown.
- /// This method never throws an exception otherwise.
- ///
- /// \param zone_finder A \c ZoneFinder object to be added.
- /// \return \c result::SUCCESS If the zone_finder is successfully
- /// added to the client.
- /// \return \c result::EXIST The memory data source already
- /// stores a zone that has the same origin.
- result::Result addZone(ZoneFinderPtr zone_finder);
-
- /// Returns a \c ZoneFinder for a zone_finder that best matches the given
- /// name.
- ///
- /// This derived version of the method never throws an exception.
- /// For other details see \c DataSourceClient::findZone().
- virtual FindResult findZone(const isc::dns::Name& name) const;
-
- /// \brief Implementation of the getIterator method
- virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name,
- bool separate_rrs = false) const;
-
- /// In-memory data source is read-only, so this derived method will
- /// result in a NotImplemented exception.
- ///
- /// \note We plan to use a database-based data source as a backend
- /// persistent storage for an in-memory data source. When it's
- /// implemented we may also want to allow the user of the in-memory client
- /// to update via its updater (this may or may not be a good idea and
- /// is subject to further discussions).
- virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name,
- bool replace, bool journaling = false)
- const;
-
- virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
- getJournalReader(const isc::dns::Name& zone, uint32_t begin_serial,
- uint32_t end_serial) const;
-
-private:
- // TODO: Do we still need the PImpl if nobody should manipulate this class
- // directly any more (it should be handled through DataSourceClient)?
- class InMemoryClientImpl;
- InMemoryClientImpl* impl_;
-};
-
-/// \brief Creates an instance of the Memory datasource client
-///
-/// Currently the configuration passed here must be a MapElement, formed as
-/// follows:
-/// \code
-/// { "type": string ("memory"),
-/// "class": string ("IN"/"CH"/etc),
-/// "zones": list
-/// }
-/// Zones list is a list of maps:
-/// { "origin": string,
-/// "file": string
-/// }
-/// \endcode
-/// (i.e. the configuration that was used prior to the datasource refactor)
-///
-/// This configuration setup is currently under discussion and will change in
-/// the near future.
-///
-/// \param config The configuration for the datasource instance
-/// \param error This string will be set to an error message if an error occurs
-/// during initialization
-/// \return An instance of the memory datasource client, or NULL if there was
-/// an error
-extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr config,
- std::string& error);
-
-/// \brief Destroy the instance created by createInstance()
-extern "C" void destroyInstance(DataSourceClient* instance);
-
-
-}
-}
-#endif // MEMORY_DATA_SOURCE_H
-// Local Variables:
-// mode: c++
-// End:
+++ /dev/null
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <cc/data.h>
-
-#include <dns/rrclass.h>
-
-#include <datasrc/client.h>
-#include <datasrc/factory.h>
-#include <datasrc/memory_datasrc.h>
-
-#include <exceptions/exceptions.h>
-
-#include <boost/foreach.hpp>
-#include <boost/scoped_ptr.hpp>
-
-#include <string>
-
-using namespace isc::dns;
-using namespace isc::data;
-
-namespace isc {
-namespace datasrc {
-
-/// This exception is raised if there is an error in the configuration
-/// that has been passed; missing information, duplicate values, etc.
-class InMemoryConfigError : public isc::Exception {
-public:
- InMemoryConfigError(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-namespace {
-// convencience function to add an error message to a list of those
-// (TODO: move functions like these to some util lib?)
-void
-addError(ElementPtr errors, const std::string& error) {
- if (errors != ElementPtr() && errors->getType() == Element::list) {
- errors->add(Element::create(error));
- }
-}
-
-/// Check if the given element exists in the map, and if it is a string
-bool
-checkConfigElementString(ConstElementPtr config, const std::string& name,
- ElementPtr errors)
-{
- if (!config->contains(name)) {
- addError(errors,
- "Config for memory backend does not contain a '"
- +name+
- "' value");
- return (false);
- } else if (!config->get(name) ||
- config->get(name)->getType() != Element::string) {
- addError(errors, "value of " + name +
- " in memory backend config is not a string");
- return (false);
- } else {
- return (true);
- }
-}
-
-bool
-checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
- bool result = true;
- if (!config || config->getType() != Element::map) {
- addError(errors, "Elements in memory backend's zone list must be maps");
- result = false;
- } else {
- if (!checkConfigElementString(config, "origin", errors)) {
- result = false;
- }
- if (!checkConfigElementString(config, "file", errors)) {
- result = false;
- }
- // we could add some existence/readabilty/parsability checks here
- // if we want
- }
- return result;
-}
-
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
- /* Specific configuration is under discussion, right now this accepts
- * the 'old' configuration, see [TODO]
- * So for memory datasource, we get a structure like this:
- * { "type": string ("memory"),
- * "class": string ("IN"/"CH"/etc),
- * "zones": list
- * }
- * Zones list is a list of maps:
- * { "origin": string,
- * "file": string
- * }
- *
- * At this moment we cannot be completely sure of the contents of the
- * structure, so we have to do some more extensive tests than should
- * strictly be necessary (e.g. existence and type of elements)
- */
- bool result = true;
-
- if (!config || config->getType() != Element::map) {
- addError(errors, "Base config for memory backend must be a map");
- result = false;
- } else {
- if (!checkConfigElementString(config, "type", errors)) {
- result = false;
- } else {
- if (config->get("type")->stringValue() != "memory") {
- addError(errors,
- "Config for memory backend is not of type \"memory\"");
- result = false;
- }
- }
- if (config->contains("class")) {
- if (!checkConfigElementString(config, "class", errors)) {
- result = false;
- } else {
- try {
- RRClass(config->get("class")->stringValue());
- } catch (const isc::Exception& rrce) {
- addError(errors,
- "Error parsing class config for memory backend: " +
- std::string(rrce.what()));
- result = false;
- }
- }
- }
- if (!config->contains("zones")) {
- // Assume empty list of zones
- } else if (!config->get("zones") ||
- config->get("zones")->getType() != Element::list) {
- addError(errors,
- "'zones' element in memory backend config is not a list");
- result = false;
- } else {
- BOOST_FOREACH(ConstElementPtr zone_config,
- config->get("zones")->listValue()) {
- if (!checkZoneConfig(zone_config, errors)) {
- result = false;
- }
- }
- }
- }
-
- return (result);
-}
-
-// Apply the given config to the just-initialized client
-// client must be freshly allocated, and config_value should have been
-// checked by the caller
-void
-applyConfig(isc::datasrc::InMemoryClient& client,
- isc::data::ConstElementPtr config_value)
-{
- // XXX: We have lost the context to get to the default values here,
- // as a temporary workaround we hardcode the IN class here.
- isc::dns::RRClass rrclass = RRClass::IN();
- if (config_value->contains("class")) {
- rrclass = RRClass(config_value->get("class")->stringValue());
- }
- ConstElementPtr zones_config = config_value->get("zones");
- if (!zones_config) {
- // XXX: Like the RR class, we cannot retrieve the default value here,
- // so we assume an empty zone list in this case.
- return;
- }
-
- BOOST_FOREACH(ConstElementPtr zone_config, zones_config->listValue()) {
- ConstElementPtr origin = zone_config->get("origin");
- const std::string origin_txt = origin ? origin->stringValue() : "";
- if (origin_txt.empty()) {
- isc_throw(InMemoryConfigError, "Missing zone origin");
- }
- ConstElementPtr file = zone_config->get("file");
- const std::string file_txt = file ? file->stringValue() : "";
- if (file_txt.empty()) {
- isc_throw(InMemoryConfigError, "Missing zone file for zone: "
- << origin_txt);
- }
-
- // We support the traditional text type and SQLite3 backend. For the
- // latter we create a client for the underlying SQLite3 data source,
- // and build the in-memory zone using an iterator of the underlying
- // zone.
- ConstElementPtr filetype = zone_config->get("filetype");
- const std::string filetype_txt = filetype ? filetype->stringValue() :
- "text";
- boost::scoped_ptr<DataSourceClientContainer> container;
- if (filetype_txt == "sqlite3") {
- container.reset(new DataSourceClientContainer(
- "sqlite3",
- Element::fromJSON("{\"database_file\": \"" +
- file_txt + "\"}")));
- } else if (filetype_txt != "text") {
- isc_throw(InMemoryConfigError, "Invalid filetype for zone "
- << origin_txt << ": " << filetype_txt);
- }
-
- // Note: we don't want to have such small try-catch blocks for each
- // specific error. We may eventually want to introduce some unified
- // error handling framework as we have more configuration parameters.
- // See bug #1627 for the relevant discussion.
- InMemoryZoneFinder* imzf = NULL;
- try {
- imzf = new InMemoryZoneFinder(rrclass, Name(origin_txt));
- } catch (const isc::dns::NameParserException& ex) {
- isc_throw(InMemoryConfigError, "unable to parse zone's origin: " <<
- ex.what());
- }
-
- boost::shared_ptr<InMemoryZoneFinder> zone_finder(imzf);
- const result::Result result = client.addZone(zone_finder);
- if (result == result::EXIST) {
- isc_throw(InMemoryConfigError, "zone "<< origin->str()
- << " already exists");
- }
-
- /*
- * TODO: Once we have better reloading of configuration (something
- * else than throwing everything away and loading it again), we will
- * need the load method to be split into some kind of build and
- * commit/abort parts.
- */
- if (filetype_txt == "text") {
- zone_finder->load(file_txt);
- } else {
- zone_finder->load(*container->getInstance().getIterator(
- Name(origin_txt)));
- }
- }
-}
-
-} // end unnamed namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
- ElementPtr errors(Element::createList());
- if (!checkConfig(config, errors)) {
- error = "Configuration error: " + errors->str();
- return (NULL);
- }
- try {
- std::auto_ptr<InMemoryClient> client(new isc::datasrc::InMemoryClient());
- applyConfig(*client, config);
- return (client.release());
- } catch (const isc::Exception& isce) {
- error = isce.what();
- return (NULL);
- } catch (const std::exception& exc) {
- error = std::string("Error creating memory datasource: ") + exc.what();
- return (NULL);
- } catch (...) {
- error = std::string("Error creating memory datasource, "
- "unknown exception");
- return (NULL);
- }
-}
-
-void destroyInstance(DataSourceClient* instance) {
- delete instance;
-}
-
-} // end of namespace datasrc
-} // end of namespace isc
+++ /dev/null
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef RBNODE_RRSET_H
-#define RBNODE_RRSET_H
-
-#include <dns/messagerenderer.h>
-#include <dns/name.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrttl.h>
-#include <dns/rrtype.h>
-#include <util/buffer.h>
-
-#include <string>
-#include <vector>
-
-namespace isc {
-namespace datasrc {
-namespace internal {
-
-/// \brief The actual content of \c RBNodeRRset
-///
-/// This is defined in the namespace-scope (not hidden in the main class)
-/// so that the In-memory data source implementation can refer to it.
-struct RBNodeRRsetImpl;
-
-// Forward declaration of an opaque data type defined and used within the
-// implementation. This is public only because it needs to be used within
-// the in-memory data source implementation, but conceptually this is a
-// private type for the in-memory data source implementation.
-// Note that the definition of the structure is still hidden within the
-// implementation, so, basically, a normal application should never be able
-// to use it directly even if it peeks into the "internal" namespace.
-struct AdditionalNodeInfo;
-
-/// \brief Special RRset for optimizing memory datasource requirement
-///
-/// To speed up the performance of the in-memory data source, at load time
-/// associate relevant "additional section" data with each RRset in the
-/// data source.
-///
-/// This class, derived from AbstractRRset, holds a "const" pointer to the
-/// underlying RRset object. All calls to methods on the class are passed to
-/// the underlying object. However, there are some restrictions:
-///
-/// - Calls to methods that change attributes of the underlying RRset (such as
-/// TTL or Name) cause an exception to be thrown. The in-memory data source
-/// does not allow modification of these attributes. In theory, it is a bad
-/// practice in that it doesn't preserve the assumed behavior of the base
-/// class. In practice, however, it should be acceptable because this
-/// class is effectively hidden from applications and will only be given
-/// to them as a const pointer to the base class via find() variants.
-/// So the application cannot call non const methods anyway unless it
-/// intentionally breaks the constness.
-///
-/// - Calls that add the pointer to the associated RRSIG to the RRset are
-/// allowed (even though the pointer is to a "const" RRset). The reason here
-/// is that RRSIGs are added to the in-memory data source after the
-/// RBNodeRRset objects have been created. Thus there has to be the
-/// capability of modifying this information.
-///
-/// The class is not derived from RRset itself to simplify coding: part of the
-/// loading of the memory data source is handled in the BIND 10 "libdns++"
-/// code, which creates RRsets and passes them to the data source code. This
-/// does not have to be altered if encapsulation, rather than inheritance, is
-/// used.
-///
-/// \note This class is exposed in this separate header file so that test code
-/// can refer to its definition, and only for that purpose. Otherwise this is
-/// essentially a private class of the in-memory data source implementation,
-/// and an application shouldn't directly refer to this class.
-///
-// Note: non-Doxygen-documented methods are documented in the base class.
-
-class RBNodeRRset : public isc::dns::AbstractRRset {
-
-private:
- // Note: The copy constructor and the assignment operator are intentionally
- // defined as private as we would normally not duplicate a RBNodeRRset.
- // (We use the "private" method instead of inheriting from
- // boost::noncopyable so as to avoid multiple inheritance.)
- RBNodeRRset(const RBNodeRRset& source);
- RBNodeRRset& operator=(const RBNodeRRset& source);
-
-public:
- /// \brief Usual Constructor
- ///
- /// Creates an RBNodeRRset from the pointer to the RRset passed to it.
- ///
- /// \param rrset Pointer to underlying RRset encapsulated by this object.
- explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset);
-
- /// \brief Destructor
- virtual ~RBNodeRRset();
-
- // Getter and Setter Methods
- //
- // The getter methods pass the call through to the underlying RRset. The
- // setter methods thrown an exception - this specialisation of the RRset
- // object does not expect the underlying RRset to be modified.
-
- virtual unsigned int getRdataCount() const;
-
- virtual const isc::dns::Name& getName() const;
-
- virtual const isc::dns::RRClass& getClass() const;
-
- virtual const isc::dns::RRType& getType() const;
-
- virtual const isc::dns::RRTTL& getTTL() const;
-
- virtual void setName(const isc::dns::Name&);
-
- virtual void setTTL(const isc::dns::RRTTL&);
-
- virtual std::string toText() const;
-
- virtual bool isSameKind(const AbstractRRset& other) const {
- // This code is an optimisation for comparing
- // RBNodeRRsets. However, in doing this optimisation,
- // semantically the code is not "is same kind" but is instead
- // "is identical object" in the case where RBNodeRRsets are compared.
-
- const RBNodeRRset* rb = dynamic_cast<const RBNodeRRset*>(&other);
- if (rb != NULL) {
- return (this == rb);
- } else {
- return (AbstractRRset::isSameKind(other));
- }
- }
-
- virtual unsigned int toWire(
- isc::dns::AbstractMessageRenderer& renderer) const;
-
- virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const;
-
- virtual void addRdata(isc::dns::rdata::ConstRdataPtr);
-
- virtual void addRdata(const isc::dns::rdata::Rdata&);
-
- virtual isc::dns::RdataIteratorPtr getRdataIterator() const;
-
- virtual isc::dns::RRsetPtr getRRsig() const;
-
- virtual unsigned int getRRsigDataCount() const;
-
- // With all the RRsig methods, we have the problem that we store the
- // underlying RRset using a ConstRRsetPtr - a pointer to a "const" RRset -
- // but we need to modify it by adding or removing an RRSIG. We overcome
- // this by temporarily violating the "const" nature of the RRset to add the
- // data.
-
- virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata);
-
- virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata);
-
- virtual void addRRsig(const AbstractRRset& sigs);
-
- virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs);
-
- virtual void addRRsig(const isc::dns::RRsetPtr& sigs);
-
- virtual void removeRRsig();
-
- /// \brief Associate a link to an RB node of the additional record.
- ///
- /// This method adds a given opaque object that holds a link to an RB node
- /// of the underlying in-memory data source that is corresponding to an
- /// RDATA of this RRset.
- ///
- /// This method is exposed as public so it can be used within the in-memory
- /// data source implementation, and only for that purpose.
- ///
- /// \param additional An opaque \c AdditionalNodeInfo object to be
- /// associated with this RRset.
- void addAdditionalNode(const AdditionalNodeInfo& additional);
-
- /// \brief Return a pointer to the list (vector) of additional RB nodes.
- ///
- /// This method returns a pointer to a vector storing the opaque
- /// \c AdditionalNodeInfo object that may be possibly set in this RRset.
- /// Not all RRsets are associated with additional nodes; if no
- /// such node is stored, this method returns NULL.
- ///
- /// Like \c addAdditionalNode(), this method is exposed as public only for
- /// the in-memory data source implementation.
- ///
- /// \return A pointer to the associated vector of \c AdditionalNodeInfo;
- /// NULL if no additional nodes are associated to this RRset.
- const std::vector<AdditionalNodeInfo>* getAdditionalNodes() const;
-
- /// \brief Copy the list of additional RB nodes to another RRset.
- ///
- /// This method copies the internal list (an STL vector in the actual
- /// implementation) of additional RB nodes for this RRset to another
- /// \c RBNodeRRset object. The copy destination is generally expected to
- /// be newly created and have an empty list, but this method does not
- /// check the condition. If the destination already has a non empty list,
- /// the existing entries will be lost.
- ///
- /// \param dst The \c RBNodeRRset object to which the additional
- /// RB node list is to be copied.
- void copyAdditionalNodes(RBNodeRRset& dst) const;
-
- /// \brief Return underlying RRset pointer
- ///
- /// ... mainly for testing.
- isc::dns::ConstRRsetPtr getUnderlyingRRset() const;
-
-private:
- RBNodeRRsetImpl* impl_;
-};
-
-} // namespace internal
-} // namespace datasrc
-} // namespace isc
-
-#endif // RBNODE_RRSET_H
+++ /dev/null
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef RBTREE_H
-#define RBTREE_H 1
-
-//! \file datasrc/rbtree.h
-///
-/// \note The purpose of the RBTree is to provide a generic map with
-/// domain names as the key that can be used by various BIND 10 modules or
-/// even by other applications. However, because of some unresolved design
-/// issue, the design and interface are not fixed, and RBTree isn't ready
-/// to be used as a base data structure by other modules.
-
-#include <exceptions/exceptions.h>
-#include <util/memory_segment.h>
-#include <dns/name.h>
-#include <dns/labelsequence.h>
-
-#include <boost/utility.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/interprocess/offset_ptr.hpp>
-#include <boost/static_assert.hpp>
-
-#include <ostream>
-#include <algorithm>
-#include <cassert>
-
-namespace isc {
-namespace datasrc {
-
-/// Forward declare RBTree class here is convinent for following friend
-/// class declare inside RBNode and RBTreeNodeChain
-template <typename T>
-class RBTree;
-
-/// \brief \c RBNode is used by RBTree to store any data related to one domain
-/// name.
-///
-/// This is meant to be used only from RBTree. It is meaningless to inherit it
-/// or create instances of it from elsewhere. For that reason, the constructor
-/// (and the allocator, see below) is private.
-///
-/// It serves three roles. One is to keep structure of the \c RBTree as a
-/// red-black tree. For that purpose, it has left, right and parent pointers
-/// and color member. These are private and accessed only from within the tree.
-///
-/// The second one is to store data for one domain name. The data related
-/// functions can be used to access and set the data.
-///
-/// The third role is to keep the hierarchy of domains. The down pointer
-/// points to a subtree of subdomains. The parent pointer of a subtree's
-/// root node points to the parent leaf of the upper tree.
-///
-/// One special kind of node is non-terminal node. It has subdomains with
-/// RRsets, but doesn't have any RRsets itself.
-///
-/// In order to keep memory footprint as small as possible, the node data
-/// are heavily packed. Specifically, some internal node properties (such as
-/// the node color) are encoded as part of "flags", some of the flag bits
-/// can also be set by the user application. Each node is associated with
-/// a sequence of domain name labels, which is essentially the search/insert
-/// key for the node (see also the description of RBTree). This is encoded
-/// as opaque binary immediately following the main node object. The size
-/// of the allocated space for the labels data is encoded by borrowing some
-/// bits of the "flags" field.
-template <typename T>
-class RBNode : public boost::noncopyable {
-private:
- /// The RBNode is meant for use from within RBTree, so it has access to
- /// it.
- friend class RBTree<T>;
-
- /// \brief Just a type alias
- ///
- /// We are going to use a lot of these offset pointers here and they
- /// have a long name.
- typedef boost::interprocess::offset_ptr<RBNode<T> > RBNodePtr;
-
- /// \name Constructors
- ///
- /// \note The existence of a RBNode without a RBTree is meaningless.
- /// Therefore the constructors are private.
- //@{
-
- /// \brief Constructor from normal nodes.
- RBNode(size_t labels_capacity);
-
- /// \brief Destructor
- ~RBNode();
-
- //@}
-
- /// \brief Accessor to the memory region for node labels.
- ///
- /// The only valid usage of the returned pointer is to pass it to the
- /// corresponding constructor of \c dns::LabelSequence.
- const void* getLabelsData() const { return (this + 1); }
-
- /// \brief Accessor to the memory region for node labels, mutable version.
- ///
- /// The only valid usage of the returned pointer is to pass it to
- /// \c LabelSequence::serialize() with the node's labels_capacity_ member
- /// (which should be sufficiently large for the \c LabelSequence in that
- /// context).
- void* getLabelsData() { return (this + 1); }
-
- /// \brief Allocate and construct \c RBNode
- ///
- /// This static method allocates memory for a new \c RBNode object
- /// from the given memory segment, constructs the object, and returns
- /// a pointer to it.
- ///
- /// \throw std::bad_alloc Memory allocation fails.
- ///
- /// \param mem_sgmt A \c MemorySegment from which memory for the new
- /// \c RBNode is allocated.
- static RBNode<T>* create(util::MemorySegment& mem_sgmt,
- const dns::LabelSequence& labels)
- {
- const size_t labels_len = labels.getSerializedLength();
- void* p = mem_sgmt.allocate(sizeof(RBNode<T>) + labels_len);
- RBNode<T>* node = new(p) RBNode<T>(labels_len);
- labels.serialize(node->getLabelsData(), labels_len);
- return (node);
- }
-
- /// \brief Destruct and deallocate \c RBNode
- ///
- /// \throw none
- ///
- /// \param mem_sgmt The \c MemorySegment that allocated memory for
- /// \c rbnode.
- /// \param rbnode A non NULL pointer to a valid \c RBNode object
- /// that was originally created by the \c create() method (the behavior
- /// is undefined if this condition isn't met).
- static void destroy(util::MemorySegment& mem_sgmt, RBNode<T>* rbnode) {
- const size_t labels_capacity = rbnode->labels_capacity_;
- rbnode->~RBNode<T>();
- mem_sgmt.deallocate(rbnode, sizeof(RBNode<T>) + labels_capacity);
- }
-
- /// \brief Reset node's label sequence to a new one.
- ///
- /// The new labels must be a sub sequence of the current label sequence;
- /// otherwise the serialize() method will throw an exception.
- void resetLabels(const dns::LabelSequence& labels) {
- labels.serialize(getLabelsData(), labels_capacity_);
- }
-
-public:
- /// \brief Alias for shared pointer to the data.
- typedef boost::shared_ptr<T> NodeDataPtr;
-
- /// Node flags.
- ///
- /// Each flag value defines a non default property for a specific node.
- /// These are defined as bitmask type values for the convenience of
- /// internal implementation, but applications are expected to use
- /// each flag separately via the enum definitions.
- ///
- /// All (settable) flags are off by default; they must be explicitly
- /// set to on by the \c setFlag() method.
- enum Flags {
- FLAG_CALLBACK = 1, ///< Callback enabled. See \ref callback
- FLAG_RED = 2, ///< Node color; 1 if node is red, 0 if node is black.
- FLAG_SUBTREE_ROOT = 4, ///< Set if the node is the root of a subtree
- FLAG_USER1 = 0x400000U, ///< Application specific flag
- FLAG_USER2 = 0x200000U, ///< Application specific flag
- FLAG_USER3 = 0x100000U, ///< Application specific flag
- FLAG_MAX = 0x400000U // for integrity check
- };
-private:
- // Some flag values are expected to be used for internal purposes
- // (e.g., representing the node color) in future versions, so we
- // limit the settable flags via the \c setFlag() method to those
- // explicitly defined in \c Flags. This constant represents all
- // such flags.
- static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1 |
- FLAG_USER2 | FLAG_USER3);
-
-public:
-
- /// \name Getter functions.
- //@{
- /// \brief Return the name of current node.
- ///
- /// It's relative to its containing node.
- ///
- /// To get the absolute name of one node, the node path from the top node
- /// to current node has to be recorded.
- ///
- /// \note We should eventually deprecate this method and revise all its
- /// usage with \c getLabels(). At this point the only user of this method
- /// is getAbsoluteName()::getAbsoluteName(), which would have to be revised
- /// using \c LabelSequence. Until then we keep this interface as a
- /// simplest form of wrapper; it's not efficient, but should be replaced
- /// before we need to worry about that.
- const isc::dns::Name getName() const {
- return (dns::Name(dns::LabelSequence(getLabelsData()).toText()));
- }
-
- /// \brief Return the label sequence of the node.
- ///
- /// This method returns the label sequence corresponding to this node
- /// in the form of \c dns::LabelSequence object. Any modification to
- /// the tree can invalidate the returned \c LabelSequence object or copy
- /// of it; in general, it's expected to be used in a very limited scope.
- dns::LabelSequence getLabels() const {
- return (dns::LabelSequence(getLabelsData()));
- }
-
- /// \brief Return the absolute label sequence of the node.
- ///
- /// This method returns the label sequence corresponding to the full
- /// name of the node; i.e. the entire name as it appears in the zone.
- ///
- /// It takes the (partial) name of the node itself, and extends it
- /// with all upper nodes.
- ///
- /// \note Care must be taken with the buffer that is used here; this
- /// method overwrites its data, so it should not be associated with
- /// any other LabelSequence during the lifetime of the LabelSequence
- /// returned by this method. See LabelSequence::extend(), which is used
- /// by this method.
- ///
- /// \param buf A data buffer where the label sequence will be built.
- /// The data in this buffer will be overwritten by this call.
- /// \return A LabelSequence with the absolute name of this node.
- isc::dns::LabelSequence getAbsoluteLabels(
- uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const;
-
- /// \brief Return the data stored in this node.
- ///
- /// You should not delete the data, it is handled by shared pointers.
- NodeDataPtr& getData() { return (data_); }
- /// \brief Return the data stored in this node.
- const NodeDataPtr& getData() const { return (data_); }
-
- /// \brief return whether the node has related data.
- ///
- /// There can be empty nodes inside the RBTree. They are usually the
- /// non-terminal domains, but it is possible (yet probably meaningless)
- /// empty nodes anywhere.
- bool isEmpty() const { return (data_.get() == NULL); }
- //@}
-
- /// \name Setter functions.
- //@{
- /// \brief Set the data stored in the node.
- void setData(const NodeDataPtr& data) { data_ = data; }
- //@}
-
- /// \name Node flag manipulation methods
- //@{
- /// Get the status of a node flag.
- ///
- /// This method returns whether the given node flag is set (enabled)
- /// on the node. The \c flag parameter is expected to be one of the
- /// defined \c Flags constants. For simplicity, the method interface
- /// does not prohibit passing an undefined flag or combined flags, but
- /// the return value in such a case will be meaningless for the caller
- /// (an application would have to use an ugly cast for such an unintended
- /// form of call, which will hopefully avoid accidental misuse).
- ///
- /// \exception None
- /// \param flag The flag to be tested.
- /// \return \c true if the \c flag is set; \c false otherwise.
- bool getFlag(Flags flag) const {
- return ((flags_ & flag) != 0);
- }
-
- /// Set or clear a node flag.
- ///
- /// This method changes the status of the specified node flag to either
- /// "on" (enabled) or "off" (disabled). The new status is specified by
- /// the \c on parameter.
- /// Like the \c getFlag() method, \c flag is expected to be one of the
- /// defined \c Flags constants. If an undefined or unsettable flag is
- /// specified, \c isc::InvalidParameter exception will be thrown.
- ///
- /// \exception isc::InvalidParameter Unsettable flag is specified
- /// \exception None otherwise
- /// \param flag The node flag to be changed.
- /// \param on If \c true, set the flag to on; otherwise set it to off.
- void setFlag(Flags flag, bool on = true) {
- if ((flag & ~SETTABLE_FLAGS) != 0) {
- isc_throw(isc::InvalidParameter,
- "Unsettable RBTree flag is being set");
- }
- if (on) {
- flags_ |= flag;
- } else {
- flags_ &= ~flag;
- }
- }
- //@}
-
-private:
- /// \name Callback related methods
- ///
- /// See the description of \c RBTree<T>::find() at \ref callback
- /// about callbacks.
- ///
- /// These methods never throw an exception.
- //@{
- /// Return if callback is enabled at the node.
- //@}
-
-
- /// \brief Define rbnode color
- enum RBNodeColor {BLACK, RED};
-
- /// \brief Returns the color of this node
- RBNodeColor getColor() const {
- if ((flags_ & FLAG_RED) != 0) {
- return (RED);
- } else {
- return (BLACK);
- }
- }
-
- /// \brief Sets the color of this node
- void setColor(const RBNodeColor color) {
- if (color == RED) {
- flags_ |= FLAG_RED;
- } else {
- flags_ &= ~FLAG_RED;
- }
- }
-
- void setSubTreeRoot(bool root) {
- if (root) {
- flags_ |= FLAG_SUBTREE_ROOT;
- } else {
- flags_ &= ~FLAG_SUBTREE_ROOT;
- }
- }
-
- bool isSubTreeRoot() const {
- return ((flags_ & FLAG_SUBTREE_ROOT) != 0);
- }
-
-public:
- /// \brief returns the parent of the root of its subtree
- ///
- /// This method takes a node and returns the parent of the root of
- /// its subtree (i.e, it returns the node's immediate ancestor in
- /// the tree-of-tree hierarchy). If the node is at the top level
- /// (which should be absolute), it will return \c NULL.
- ///
- /// This method never throws an exception.
- const RBNode<T>* getUpperNode() const;
-
-private:
- /// \brief return the next node which is bigger than current node
- /// in the same subtree
- ///
- /// The next successor for this node is the next bigger node in terms of
- /// the DNSSEC order relation within the same single subtree.
- /// Note that it may NOT be the next bigger node in the entire RBTree;
- /// RBTree is a tree in tree, and the real next node may reside in
- /// an upper or lower subtree of the subtree where this node belongs.
- /// For example, if this node has a sub domain, the real next node is
- /// the smallest node in the sub domain tree.
- ///
- /// If this node is the biggest node within the subtree, this method
- /// returns \c NULL.
- ///
- /// This method never throws an exception.
- const RBNode<T>* successor() const;
-
- /// \brief return the next node which is smaller than current node
- /// in the same subtree
- ///
- /// The predecessor for this node is the next smaller node in terms of
- /// the DNSSEC order relation within the same single subtree.
- /// Note that it may NOT be the next smaller node in the entire RBTree;
- /// RBTree is a tree in tree, and the real next node may reside in
- /// an upper or lower subtree of the subtree where this node belongs.
- /// For example, if the predecessor node has a sub domain, the real next
- /// node is the largest node in the sub domain tree.
- ///
- /// If this node is the smallest node within the subtree, this method
- /// returns \c NULL.
- ///
- /// This method never throws an exception.
- const RBNode<T>* predecessor() const;
-
- /// \brief private shared implementation of successor and predecessor
- ///
- /// As the two mentioned functions are merely mirror images of each other,
- /// it makes little sense to keep both versions. So this is the body of the
- /// functions and we call it with the correct pointers.
- ///
- /// Not to be called directly, not even by friends.
- ///
- /// The overhead of the member pointers should be optimised out, as this
- /// will probably get completely inlined into predecessor and successor
- /// methods.
- const RBNode<T>*
- abstractSuccessor(typename RBNode<T>::RBNodePtr RBNode<T>::*left,
- typename RBNode<T>::RBNodePtr RBNode<T>::*right)
- const;
-
- /// \name Data to maintain the rbtree structure.
- ///
- /// We keep them as offset pointers. This is part of a future plan, when we
- /// want to share the image of the tree between multiple processes.
- /// However, whenever we have a chance, we switch to bare pointers during
- /// the processing. The pointers on stack are never shared and the offset
- /// pointers have non-trivial performance impact.
- //@{
- RBNodePtr parent_;
- /// \brief Access the parent_ as bare pointer.
- RBNode<T>* getParent() {
- return (parent_.get());
- }
- /// \brief Access the parent_ as bare pointer, const.
- const RBNode<T>* getParent() const {
- return (parent_.get());
- }
- RBNodePtr left_;
- /// \brief Access the left_ as bare pointer.
- RBNode<T>* getLeft() {
- return (left_.get());
- }
- /// \brief Access the left_ as bare pointer, const.
- const RBNode<T>* getLeft() const {
- return (left_.get());
- }
- RBNodePtr right_;
- /// \brief Access the right_ as bare pointer.
- RBNode<T>* getRight() {
- return (right_.get());
- }
- /// \brief Access the right_ as bare pointer, const.
- const RBNode<T>* getRight() const {
- return (right_.get());
- }
- //@}
-
- /// \brief Data stored here.
- NodeDataPtr data_;
-
- /// \brief The subdomain tree.
- ///
- /// This points to the root node of trees of subdomains of this domain.
- ///
- /// \par Adding down pointer to \c RBNode has two purposes:
- /// \li Accelerate the search process, with sub domain tree, it splits the
- /// big flat tree into several hierarchy trees.
- /// \li It saves memory usage as it allows storing only relative names,
- /// avoiding storage of the same domain labels multiple times.
- RBNodePtr down_;
- /// \brief Access the down_ as bare pointer.
- RBNode<T>* getDown() {
- return (down_.get());
- }
- /// \brief Access the down_ as bare pointer, const.
- const RBNode<T>* getDown() const {
- return (down_.get());
- }
-
- /// \brief Internal or user-configurable flags of node's properties.
- ///
- /// See the \c Flags enum for available flags.
- ///
- /// For memory efficiency reasons, we only use a subset of the 32-bit
- /// space, and use the rest to store the allocated size for the node's
- /// label sequence data.
- uint32_t flags_ : 23; // largest flag being 0x400000
- BOOST_STATIC_ASSERT((1 << 23) > FLAG_MAX); // assumption check
-
- const uint32_t labels_capacity_ : 9; // size for labelseq; range is 0..511
- // Make sure the reserved space for labels_capacity_ is sufficiently
- // large. In effect, we use the knowledge of the implementation of the
- // serialization, but we still only use its public interface, and the
- // public interface of this class doesn't rely on this assumption.
- // So we can change this implementation without affecting its users if
- // a future change to LabelSequence breaks this assumption.
- BOOST_STATIC_ASSERT((1 << 9) > dns::LabelSequence::MAX_SERIALIZED_LENGTH);
-};
-
-template <typename T>
-RBNode<T>::RBNode(size_t labels_capacity) :
- parent_(NULL),
- left_(NULL),
- right_(NULL),
- down_(NULL),
- flags_(FLAG_RED | FLAG_SUBTREE_ROOT),
- labels_capacity_(labels_capacity)
-{
-}
-
-template <typename T>
-RBNode<T>::~RBNode() {
-}
-
-template <typename T>
-const RBNode<T>*
-RBNode<T>::getUpperNode() const {
- const RBNode<T>* current = this;
-
- // current would never be equal to NULL here (in a correct tree
- // implementation)
- while (!current->isSubTreeRoot()) {
- current = current->getParent();
- }
-
- return (current->getParent());
-}
-
-template <typename T>
-isc::dns::LabelSequence
-RBNode<T>::getAbsoluteLabels(
- uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
-{
- isc::dns::LabelSequence result(getLabels(), buf);
- const RBNode<T>* upper = getUpperNode();
- while (upper != NULL) {
- result.extend(upper->getLabels(), buf);
- upper = upper->getUpperNode();
- }
-
- return (result);
-}
-
-template <typename T>
-const RBNode<T>*
-RBNode<T>::abstractSuccessor(typename RBNode<T>::RBNodePtr RBNode<T>::*left,
- typename RBNode<T>::RBNodePtr RBNode<T>::*right)
- const
-{
- // This function is written as a successor. It becomes predecessor if
- // the left and right pointers are swapped. So in case of predecessor,
- // the left pointer points to right and vice versa. Don't get confused
- // by the idea, just imagine the pointers look into a mirror.
-
- const RBNode<T>* current = this;
- // If it has right node, the successor is the left-most node of the right
- // subtree.
- if ((current->*right).get() != NULL) {
- current = (current->*right).get();
- const RBNode<T>* left_n;
- while ((left_n = (current->*left).get()) != NULL) {
- current = left_n;
- }
- return (current);
- }
-
- // Otherwise go up until we find the first left branch on our path to
- // root. If found, the parent of the branch is the successor.
- // Otherwise, we return the null node
- const RBNode<T>* parent = current->getParent();
- while ((!current->isSubTreeRoot()) &&
- (current == (parent->*right).get())) {
- current = parent;
- parent = parent->getParent();
- }
-
- if (!current->isSubTreeRoot()) {
- return (parent);
- } else {
- return (NULL);
- }
-}
-
-template <typename T>
-const RBNode<T>*
-RBNode<T>::successor() const {
- return (abstractSuccessor(&RBNode<T>::left_, &RBNode<T>::right_));
-}
-
-template <typename T>
-const RBNode<T>*
-RBNode<T>::predecessor() const {
- // Swap the left and right pointers for the abstractSuccessor
- return (abstractSuccessor(&RBNode<T>::right_, &RBNode<T>::left_));
-}
-
-/// \brief RBTreeNodeChain stores detailed information of \c RBTree::find()
-/// result.
-///
-/// - The \c RBNode that was last compared with the search name, and
-/// the comparison result at that point in the form of
-/// \c isc::dns::NameComparisonResult.
-/// - A sequence of nodes that forms a path to the found node.
-///
-/// The comparison result can be used to handle some rare cases such as
-/// empty node processing.
-/// The node sequence keeps track of the nodes to reach any given node from
-/// the root of RBTree.
-///
-/// Currently, RBNode does not have "up" pointers in them (i.e., back pointers
-/// from the root of one level of tree of trees to the node in the parent
-/// tree whose down pointer points to that root node) for memory usage
-/// reasons, so there is no other way to find the path back to the root from
-/// any given RBNode.
-///
-/// \note This design may change in future versions. In particular, it's
-/// quite likely we want to have that pointer if we want to optimize name
-/// compression by exploiting the structure of the zone. If and when that
-/// happens we should also revisit the need for the chaining.
-/// Also, the class name may not be appropriate now that it contains other
-/// information than a node "chain", and the chain itself may even be
-/// deprecated. Something like "RBTreeFindContext" may be a better name.
-/// This point should be revisited later.
-///
-/// RBTreeNodeChain is constructed and manipulated only inside the \c RBTree
-/// class.
-/// \c RBTree uses it as an inner data structure to iterate over the whole
-/// RBTree.
-/// This is the reason why manipulation methods such as \c push() and \c pop()
-/// are private (and not shown in the doxygen document).
-template <typename T>
-class RBTreeNodeChain {
- /// RBTreeNodeChain is initialized by RBTree, only RBTree has
- /// knowledge to manipulate it.
- friend class RBTree<T>;
-public:
- /// \name Constructors and Assignment Operator.
- ///
- /// \note The copy constructor and the assignment operator are
- /// intentionally defined as private, making this class non copyable.
- /// This may have to be changed in a future version with newer need.
- /// For now we explicitly disable copy to avoid accidental copy happens
- /// unintentionally.
- //{@
- /// The default constructor.
- ///
- /// \exception None
- RBTreeNodeChain() : node_count_(0), last_compared_(NULL),
- // XXX: meaningless initial values:
- last_comparison_(0, 0,
- isc::dns::NameComparisonResult::EQUAL)
- {}
-
-private:
- RBTreeNodeChain(const RBTreeNodeChain<T>&);
- RBTreeNodeChain<T>& operator=(const RBTreeNodeChain<T>&);
- //@}
-
-public:
- /// Clear the state of the chain.
- ///
- /// This method re-initializes the internal state of the chain so that
- /// it can be reused for subsequent operations.
- ///
- /// \exception None
- void clear() {
- node_count_ = 0;
- last_compared_ = NULL;
- }
-
- /// Return the \c RBNode that was last compared in \c RBTree::find().
- ///
- /// If this chain has been passed to \c RBTree::find() and there has
- /// been name comparison against the search name, the last compared
- /// \c RBNode is recorded within the chain. This method returns that
- /// node.
- /// If \c RBTree::find() hasn't been called with this chain or name
- /// comparison hasn't taken place (which is possible if the tree is empty),
- /// this method returns \c NULL.
- ///
- /// \exception None
- const RBNode<T>* getLastComparedNode() const {
- return (last_compared_);
- }
-
- /// Return the result of last name comparison in \c RBTree::find().
- ///
- /// Like \c getLastComparedNode(), \c RBTree::find() records the result
- /// of the last name comparison in the chain. This method returns the
- /// result.
- /// The return value of this method is only meaningful when comparison
- /// has taken place, i.e, when \c getLastComparedNode() would return a
- /// non \c NULL value.
- ///
- /// \exception None
- const isc::dns::NameComparisonResult& getLastComparisonResult() const {
- return (last_comparison_);
- }
-
- /// \brief Return the number of levels stored in the chain.
- ///
- /// It's equal to the number of nodes in the chain; for an empty
- /// chain, 0 will be returned.
- ///
- /// \exception None
- unsigned int getLevelCount() const { return (node_count_); }
-
- /// \brief return the absolute name for the node which this
- /// \c RBTreeNodeChain currently refers to.
- ///
- /// The chain must not be empty.
- ///
- /// \exception isc::BadValue the chain is empty.
- /// \exception std::bad_alloc memory allocation for the new name fails.
- isc::dns::Name getAbsoluteName() const {
- if (isEmpty()) {
- isc_throw(isc::BadValue,
- "RBTreeNodeChain::getAbsoluteName is called on an empty "
- "chain");
- }
-
- const RBNode<T>* top_node = top();
- isc::dns::Name absolute_name = top_node->getName();
- int node_count = node_count_ - 1;
- while (node_count > 0) {
- top_node = nodes_[node_count - 1];
- absolute_name = absolute_name.concatenate(top_node->getName());
- --node_count;
- }
- return (absolute_name);
- }
-
-private:
- // the following private functions check invariants about the internal
- // state using assert() instead of exception. The state of a chain
- // can only be modified by operations within this file, so if any of the
- // assumptions fails it means an internal bug.
-
- /// \brief return whether node chain has node in it.
- ///
- /// \exception None
- bool isEmpty() const { return (node_count_ == 0); }
-
- /// \brief return the top node for the node chain
- ///
- /// RBTreeNodeChain store all the nodes along top node to
- /// root node of RBTree
- ///
- /// \exception None
- const RBNode<T>* top() const {
- assert(!isEmpty());
- return (nodes_[node_count_ - 1]);
- }
-
- /// \brief pop the top node from the node chain
- ///
- /// After pop, up/super node of original top node will be
- /// the top node
- ///
- /// \exception None
- void pop() {
- assert(!isEmpty());
- --node_count_;
- }
-
- /// \brief add the node into the node chain
- ///
- /// If the node chain isn't empty, the node should be
- /// the sub domain of the original top node in node chain
- /// otherwise the node should be the root node of RBTree.
- ///
- /// \exception None
- void push(const RBNode<T>* node) {
- assert(node_count_ < RBT_MAX_LEVEL);
- nodes_[node_count_++] = node;
- }
-
-private:
- // The max label count for one domain name is Name::MAX_LABELS (128).
- // Since each node in rbtree stores at least one label, it's also equal
- // to the possible maximum level.
- const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
-
- int node_count_;
- const RBNode<T>* nodes_[RBT_MAX_LEVEL];
- const RBNode<T>* last_compared_;
- isc::dns::NameComparisonResult last_comparison_;
-};
-
-
-// note: the following class description is documented using multiline comments
-// because the verbatim diagram contain a backslash, which could be interpreted
-// as escape of newline in singleline comment.
-/**
- * \brief \c RBTree class represents all the domains with the same suffix.
- * It can be used to store the domains in one zone, for example.
- *
- * RBTree is a generic map from domain names to any kind of data. Internally,
- * it uses a red-black tree. However, it isn't one tree containing everything.
- * Subdomains are trees, so this structure is recursive - trees inside trees.
- * But, from the interface point of view, it is opaque data structure.
- *
- * \c RBTree splits the domain space into hierarchy red black trees; nodes
- * in one tree has the same base name. The benefit of this struct is that:
- * - Enhances the query performace compared with one big flat red black tree.
- * - Decreases the memory footprint, as it doesn't store the suffix labels
- * multiple times.
- *
- * Depending on different usage, rbtree will support different search policies.
- * Whether to return an empty node to end user is one policy among them.
- * The default policy is to NOT return an empty node to end user;
- * to change the behavior, specify \c true for the constructor parameter
- * \c returnEmptyNode.
- * \note The search policy only affects the \c find() behavior of RBTree.
- * When inserting one name into RBTree, if the node with the name already
- * exists in the RBTree and it's an empty node which doesn't have any data,
- * the \c insert() method will still return \c ALREADYEXISTS regardless of
- * the search policy.
- *
- * \anchor diagram
- *
- * with the following names:
- * - a
- * - b
- * - c
- * - x.d.e.f
- * - z.d.e.f
- * - g.h
- * - o.w.y.d.e.f
- * - p.w.y.d.e.f
- * - q.w.y.d.e.f
- *
- * the tree will look like:
- * \verbatim
- .
- |
- b
- / \
- a d.e.f
- /|\
- c | g.h
- |
- w.y
- /|\
- x | z
- |
- p
- / \
- o q
- \endverbatim
- * \todo
- * - add remove interface
- */
-template <typename T>
-class RBTree : public boost::noncopyable {
- friend class RBNode<T>;
-public:
- /// \brief The return value for the \c find() and insert() methods
- enum Result {
- SUCCESS, ///< Insert was successful
- /// \brief The node returned from find mathes exactly the name given
- EXACTMATCH,
- PARTIALMATCH, ///< A superdomain node was found
- NOTFOUND, ///< Not even any superdomain was found
- /// \brief Returned by insert() if a node of the name already exists
- ALREADYEXISTS,
- };
-
- /// \brief Allocate and construct \c RBTree
- ///
- /// This static method allocates memory for a new \c RBTree object
- /// from the given memory segment, constructs the object, and returns
- /// a pointer to it.
- ///
- /// \throw std::bad_alloc Memory allocation fails.
- ///
- /// \param mem_sgmt A \c MemorySegment from which memory for the new
- /// \c RBTree is allocated.
- static RBTree* create(util::MemorySegment& mem_sgmt,
- bool return_empty_node = false)
- {
- void* p = mem_sgmt.allocate(sizeof(RBTree<T>));
- return (new(p) RBTree<T>(return_empty_node));
- }
-
- /// \brief Destruct and deallocate \c RBTree
- ///
- /// This method also destroys and deallocates all nodes inserted to the
- /// tree.
- ///
- /// \note The memory segment (\c mem_sgmt) must be the same one that
- /// was originally used to allocate memory for the tree (and for all
- /// nodes inserted to the tree, due to the requirement of \c insert()),
- /// since the tree itself doesn't maintain a reference to the segment.
- /// This is not a robust interface, but since we plan to share the tree
- /// structure by multiple processes via shared memory or possibly allow
- /// the memory image to be dumped to a file for later reload, there
- /// doesn't seem to be an easy way to store such reference in the data
- /// itself. We should probably consider a wrapper interface that
- /// encapsulates the corresponding segment and always use it for any
- /// allocation/deallocation of tree related data (the tree itself, their
- /// nodes, and node data) to keep the usage as safe as possible.
- ///
- /// \throw none
- ///
- /// \param mem_sgmt The \c MemorySegment that allocated memory for
- /// \c rbtree and for all nodes inserted to the tree.
- /// \param rbtree A non NULL pointer to a valid \c RBTree object
- /// that was originally created by the \c create() method (the behavior
- /// is undefined if this condition isn't met).
- static void destroy(util::MemorySegment& mem_sgmt, RBTree<T>* rbtree) {
- rbtree->deleteAllNodes(mem_sgmt);
- rbtree->~RBTree<T>();
- mem_sgmt.deallocate(rbtree, sizeof(RBTree<T>));
- }
-
-private:
- /// \name Constructor and Destructor
- //@{
- /// \brief The constructor.
- ///
- /// An object of this class is always expected to be created by the
- /// allocator (\c create()), so the constructor is hidden as private.
- ///
- /// It never throws an exception.
- explicit RBTree(bool returnEmptyNode = false);
-
- /// \brief The destructor.
- ///
- /// An object of this class is always expected to be destroyed explicitly
- /// by \c destroy(), so the constructor is hidden as private.
- ///
- /// \note RBTree is not intended to be inherited so the destructor
- /// is not virtual
- ~RBTree();
- //@}
-
-public:
-
- /// \name Find methods
- ///
- /// \brief Find the node that gives a longest match against the given name.
- ///
- /// \anchor find
- ///
- /// These methods search the RBTree for a node whose name is longest
- /// against name. The found node, if any, is returned via the node pointer.
- ///
- /// By default, nodes that don't have data (see RBNode::isEmpty) are
- /// ignored and the result can be NOTFOUND even if there's a node whose
- /// name matches. If the \c RBTree is constructed with its
- /// \c returnEmptyNode parameter being \c true, empty nodes will also
- /// be match candidates.
- ///
- /// \note Even when \c returnEmptyNode is \c true, not all empty nodes
- /// in terms of the DNS protocol may necessarily be found by this method.
- /// For example, in the \ref diagram shown in the class description,
- /// the name y.d.e.f is logically contained in the tree as part of the
- /// node w.y, but the \c find() variants cannot find the former for
- /// the search key of y.d.e.f, no matter how the \c RBTree is constructed.
- /// The caller of this method must use a different way to identify the
- /// hidden match when necessary.
- ///
- /// These methods involve operations on names that can throw an exception.
- /// If that happens the exception will be propagated to the caller.
- /// The callback function should generally not throw an exception, but
- /// if it throws, the exception will be propagated to the caller.
- ///
- /// The \c name parameter says what should be found. The node parameter
- /// is output-only, and in case of EXACTMATCH or PARTIALMATCH, it is set
- /// to a pointer to the found node.
- ///
- /// They return:
- /// - EXACTMATCH when a node with the same name as requested exists.
- /// - PARTIALMATCH when a node with the same name does not exist (or is
- /// empty), but there's a (nonempty) superdomain of the requested one.
- /// The superdomain with longest name is returned through the node
- /// parameter. Beware that if you store a zone in the tree, you may get
- /// PARTIALMATCH with zone apex when the given domain name is not there.
- /// You should not try to delegate into another zone in that case.
- /// - NOTFOUND if there's no node with the same name nor any superdomain
- /// of it. In that case, node parameter is left intact.
- //@{
-
- /// \brief Simple find.
- ///
- /// Acts as described in the \ref find section.
- Result find(const isc::dns::Name& name, RBNode<T>** node) const {
- RBTreeNodeChain<T> node_path;
- const isc::dns::LabelSequence ls(name);
- return (find<void*>(ls, node, node_path, NULL, NULL));
- }
-
- /// \brief Simple find returning immutable node.
- ///
- /// Acts as described in the \ref find section, but returns immutable node
- /// pointer.
- Result find(const isc::dns::Name& name, const RBNode<T>** node) const {
- RBTreeNodeChain<T> node_path;
- RBNode<T> *target_node = NULL;
- const isc::dns::LabelSequence ls(name);
- Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
- if (ret != NOTFOUND) {
- *node = target_node;
- }
- return (ret);
- }
-
- /// \brief Simple find, with node_path tracking
- ///
- /// Acts as described in the \ref find section.
- Result find(const isc::dns::Name& name, RBNode<T>** node,
- RBTreeNodeChain<T>& node_path) const
- {
- const isc::dns::LabelSequence ls(name);
- return (find<void*>(ls, node, node_path, NULL, NULL));
- }
-
- /// \brief Simple find returning immutable node, with node_path tracking
- ///
- /// Acts as described in the \ref find section, but returns immutable node
- /// pointer.
- Result find(const isc::dns::Name& name, const RBNode<T>** node,
- RBTreeNodeChain<T>& node_path) const
- {
- RBNode<T> *target_node = NULL;
- const isc::dns::LabelSequence ls(name);
- Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
- if (ret != NOTFOUND) {
- *node = target_node;
- }
- return (ret);
- }
-
- /// \brief Simple find returning immutable node.
- ///
- /// Acts as described in the \ref find section, but returns immutable
- /// node pointer.
- template <typename CBARG>
- Result find(const isc::dns::Name& name,
- const RBNode<T>** node,
- RBTreeNodeChain<T>& node_path,
- bool (*callback)(const RBNode<T>&, CBARG),
- CBARG callback_arg) const
- {
- RBNode<T>* target_node = NULL;
- const isc::dns::LabelSequence ls(name);
- Result ret = find(ls, &target_node, node_path, callback,
- callback_arg);
- if (ret != NOTFOUND) {
- *node = target_node;
- }
- return (ret);
- }
-
- /// \brief Find with callback and node chain
- /// \anchor callback
- ///
- /// This version of \c find() is specifically designed for the backend
- /// of the \c InMemoryZoneFinder class, and implements all necessary
- /// features for that purpose. Other applications shouldn't need these
- /// additional features, and should normally use the simpler versions.
- ///
- /// This version of \c find() calls the callback whenever traversing (on
- /// the way from root down the tree) a marked node on the way down through
- /// the domain namespace (see \c RBNode::FLAG_CALLBACK).
- ///
- /// Also, this version takes a \c LabelSequence object, not a \c Name
- /// object to be as efficient as possible; operations on the former
- /// needed for the search are generally much more efficient than those
- /// for the latter. Since \c Name objects are more commonly used
- /// in other parts of the implementation, other versions take a \c Name
- /// and convert it to \c LabelSequence. This conversion is cheap,
- /// while the other direction isn't, and since there would be cases
- /// where an implementation primarily handles \c LabelSequence objects
- /// as an efficient representation of names, it would make most sense
- /// to provide the interface that takes \c LabelSequence.
- ///
- /// If you return true from the callback, the search is stopped and a
- /// PARTIALMATCH is returned with the given node. Note that this node
- /// doesn't really need to be the one with longest possible match.
- ///
- /// The callback is not called for the node which matches exactly
- /// (EXACTMATCH is returned). This is typically the last node in the
- /// traversal during a successful search.
- ///
- /// This callback mechanism was designed with zone cut (delegation)
- /// processing in mind. The marked nodes would be the ones at delegation
- /// points. It is not expected that any other applications would need
- /// callbacks; they should use the versions of find without callbacks.
- /// The callbacks are not general functors for the same reason - we don't
- /// expect it to be needed.
- ///
- /// Another special feature of this version is the ability to record
- /// more detailed information regarding the search result.
- ///
- /// This information will be returned via the \c node_path parameter,
- /// which is an object of class \c RBTreeNodeChain.
- /// The passed parameter must be empty.
- ///
- /// On success, the node sequence stored in \c node_path will contain all
- /// the ancestor nodes from the found node towards the root.
- /// For example, if we look for o.w.y.d.e.f in the example \ref diagram,
- /// \c node_path will contain w.y and d.e.f; the \c top() node of the
- /// chain will be o, w.y and d.e.f will be stored below it.
- ///
- /// This feature can be used to get the absolute name for a node;
- /// to do so, we need to travel upside from the node toward the root,
- /// concatenating all ancestor labels. A node chain can also be used to
- /// find the next and previous nodes of a given node in the entire RBTree;
- /// the \c nextNode() and \c previousNode() methods take a node
- /// chain as a parameter.
- ///
- /// \exception isc::BadValue node_path is not empty.
- ///
- /// \param target_labels_orig Target to be found
- /// \param node On success (either \c EXACTMATCH or \c PARTIALMATCH)
- /// it will store a pointer to the matching node
- /// \param node_path Other search details will be stored (see the
- /// description)
- /// \param callback If non- \c NULL, a call back function to be called
- /// at marked nodes (see the description).
- /// \param callback_arg A caller supplied argument to be passed to
- /// \c callback.
- ///
- /// \return As in the description, but in case of callback returning
- /// \c true, it returns immediately with the current node.
- template <typename CBARG>
- Result find(const isc::dns::LabelSequence& target_labels_orig,
- RBNode<T>** node,
- RBTreeNodeChain<T>& node_path,
- bool (*callback)(const RBNode<T>&, CBARG),
- CBARG callback_arg) const;
-
- /// \brief Simple find returning immutable node.
- ///
- /// Acts as described in the \ref find section, but returns immutable
- /// node pointer.
- template <typename CBARG>
- Result find(const isc::dns::LabelSequence& target_labels,
- const RBNode<T>** node,
- RBTreeNodeChain<T>& node_path,
- bool (*callback)(const RBNode<T>&, CBARG),
- CBARG callback_arg) const
- {
- RBNode<T>* target_node = NULL;
- Result ret = find(target_labels, &target_node, node_path,
- callback, callback_arg);
- if (ret != NOTFOUND) {
- *node = target_node;
- }
- return (ret);
- }
- //@}
-
- /// \brief return the next bigger node in DNSSEC order from a given node
- /// chain.
- ///
- /// This method identifies the next bigger node of the node currently
- /// referenced in \c node_path and returns it.
- /// This method also updates the passed \c node_path so that it will store
- /// the path for the returned next node.
- /// It will be convenient when we want to iterate over the all nodes
- /// of \c RBTree; we can do this by calling this method repeatedly
- /// starting from the root node.
- ///
- /// \note \c nextNode() will iterate over all the nodes in RBTree including
- /// empty nodes. If empty node isn't desired, it's easy to add logic to
- /// check return node and keep invoking \c nextNode() until the non-empty
- /// node is retrieved.
- ///
- /// \exception isc::BadValue node_path is empty.
- ///
- /// \param node_path A node chain that stores all the nodes along the path
- /// from root to node.
- ///
- /// \return An \c RBNode that is next bigger than \c node; if \c node is
- /// the largest, \c NULL will be returned.
- const RBNode<T>* nextNode(RBTreeNodeChain<T>& node_path) const;
-
- /// \brief return the next smaller node in DNSSEC order from a node
- /// searched by RBTree::find().
- ///
- /// This acts similarly to \c nextNode(), but it walks in the other
- /// direction. But unlike \c nextNode(), this can start even if the
- /// node requested by \c find() was not found. In that case, it will
- /// identify the node that is previous to the queried name.
- ///
- /// \note \c previousNode() will iterate over all the nodes in RBTree
- /// including empty nodes. If empty node isn't desired, it's easy to add
- /// logic to check return node and keep invoking \c previousNode() until the
- /// non-empty node is retrieved.
- ///
- /// \exception isc::BadValue node_path is empty.
- ///
- /// \param node_path A node chain that stores all the nodes along the path
- /// from root to node and the result of \c find(). This will get modified.
- /// You should not use the node_path again except for repetitive calls
- /// of this method.
- ///
- /// \return An \c RBNode that is next smaller than \c node; if \c node is
- /// the smallest, \c NULL will be returned.
- const RBNode<T>* previousNode(RBTreeNodeChain<T>& node_path) const;
-
- /// \brief Get the total number of nodes in the tree
- ///
- /// It includes nodes internally created as a result of adding a domain
- /// name that is a subdomain of an existing node of the tree.
- /// This function is mainly intended to be used for debugging.
- int getNodeCount() const { return (node_count_); }
-
- /// \name Debug function
- //@{
- /// \brief Print the nodes in the trees.
- ///
- /// \param os A \c std::ostream object to which the tree is printed.
- /// \param depth A factor of the initial indentation. Each line
- /// will begin with space character repeating <code>5 * depth</code>
- /// times.
- void dumpTree(std::ostream& os, unsigned int depth = 0) const;
-
- /// \brief Print the nodes in the trees for processing with
- /// Graphviz's dot.
- ///
- /// \param os A \c std::ostream object to which the tree is printed.
- /// \param show_pointers Show node and parent pointers in the node
- void dumpDot(std::ostream& os, bool show_pointers = false) const;
- //@}
-
- /// \name Modify functions
- //@{
- /// \brief Insert the domain name into the tree.
- ///
- /// It either finds an already existing node of the given name, or inserts
- /// a new one if none exists yet. In any case, the \c inserted_node parameter
- /// is set to point to that node. You can fill data into it or modify it.
- /// So, if you don't know if a node exists or not and you need to modify
- /// it, just call insert and act by the result.
- ///
- /// Please note that the tree can add some empty nodes by itself, so don't
- /// assume that if you didn't insert a node of that name it doesn't exist.
- ///
- /// This method normally involves resource allocation. If it fails
- /// the corresponding standard exception will be thrown.
- ///
- /// This method does not provide the strong exception guarantee in its
- /// strict sense; if an exception is thrown in the middle of this
- /// method, the internal structure may change. However, it should
- /// still retain the same property as a mapping container before this
- /// method is called. For example, the result of \c find() should be
- /// the same. This method provides the weak exception guarantee in its
- /// normal sense.
- ///
- /// \param mem_sgmt A \c MemorySegment object for allocating memory of
- /// a new node to be inserted. Must be the same segment as that used
- /// for creating the tree itself.
- /// \param name The name to be inserted into the tree.
- /// \param inserted_node This is an output parameter and is set to the
- /// node.
- ///
- /// \return
- /// - SUCCESS The node was added.
- /// - ALREADYEXISTS There was already a node of that name, so it was not
- /// added.
- Result insert(util::MemorySegment& mem_sgmt, const isc::dns::Name& name,
- RBNode<T>** inserted_node);
-
- /// \brief Delete all tree nodes.
- ///
- /// \throw none.
- ///
- /// \param mem_sgmt The \c MemorySegment object used to insert the nodes
- /// (which was also used for creating the tree due to the requirement of
- /// \c inert()).
- void deleteAllNodes(util::MemorySegment& mem_sgmt);
-
- /// \brief Swaps two tree's contents.
- ///
- /// This and \c other trees must have been created with the same
- /// memory segment (see the discussion in \c create()); otherwise the
- /// behavior is undefined.
- ///
- /// This acts the same as many std::*.swap functions, exchanges the
- /// contents. This doesn't throw anything.
- void swap(RBTree<T>& other) {
- std::swap(root_, other.root_);
- std::swap(node_count_, other.node_count_);
- }
- //@}
-
-private:
- /// \name RBTree balance functions
- //@{
- void insertRebalance(typename RBNode<T>::RBNodePtr* root, RBNode<T>* node);
- RBNode<T>* rightRotate(typename RBNode<T>::RBNodePtr* root,
- RBNode<T>* node);
- RBNode<T>* leftRotate(typename RBNode<T>::RBNodePtr* root,
- RBNode<T>* node);
- //@}
-
- /// \name Helper functions
- //@{
- /// \brief delete tree whose root is equal to node
- void deleteHelper(util::MemorySegment& mem_sgmt, RBNode<T> *node);
-
- /// \brief Print the information of given RBNode.
- void dumpTreeHelper(std::ostream& os, const RBNode<T>* node,
- unsigned int depth) const;
-
- /// \brief Print the information of given RBNode for dot.
- int dumpDotHelper(std::ostream& os, const RBNode<T>* node,
- int* nodecount, bool show_pointers) const;
-
- /// \brief Indentation helper function for dumpTree
- static void indent(std::ostream& os, unsigned int depth);
-
- /// Split one node into two nodes for "prefix" and "suffix" parts of
- /// the labels of the original node, respectively. The given node
- /// will hold the prefix, while a newly created node will hold the prefix.
- /// Note that the original node still represents the same domain name in
- /// the entire tree. This ensures that a pointer to a node keeps its
- /// semantics even if the tree structure is changed (as long as the node
- /// itself remains valid).
- void nodeFission(util::MemorySegment& mem_sgmt, RBNode<T>& node,
- const isc::dns::LabelSequence& new_prefix,
- const isc::dns::LabelSequence& new_suffix);
- //@}
-
- typename RBNode<T>::RBNodePtr root_;
- /// the node count of current tree
- unsigned int node_count_;
- /// search policy for rbtree
- const bool needsReturnEmptyNode_;
-};
-
-template <typename T>
-RBTree<T>::RBTree(bool returnEmptyNode) :
- root_(NULL),
- node_count_(0),
- needsReturnEmptyNode_(returnEmptyNode)
-{
-}
-
-template <typename T>
-RBTree<T>::~RBTree() {
- assert(node_count_ == 0);
-}
-
-template <typename T>
-void
-RBTree<T>::deleteHelper(util::MemorySegment& mem_sgmt, RBNode<T>* root) {
- while (root != NULL) {
- // If there is a left, right or down node, walk into it and
- // iterate.
- if (root->getLeft() != NULL) {
- RBNode<T>* node = root;
- root = root->getLeft();
- node->left_ = NULL;
- } else if (root->getRight() != NULL) {
- RBNode<T>* node = root;
- root = root->getRight();
- node->right_ = NULL;
- } else if (root->getDown() != NULL) {
- RBNode<T>* node = root;
- root = root->getDown();
- node->down_ = NULL;
- } else {
- // There are no left, right or down nodes, so we can
- // free this one and go back to its parent.
- RBNode<T>* node = root;
- root = root->getParent();
- RBNode<T>::destroy(mem_sgmt, node);
- --node_count_;
- }
- }
-}
-
-template <typename T>
-template <typename CBARG>
-typename RBTree<T>::Result
-RBTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
- RBNode<T>** target,
- RBTreeNodeChain<T>& node_path,
- bool (*callback)(const RBNode<T>&, CBARG),
- CBARG callback_arg) const
-{
- if (!node_path.isEmpty()) {
- isc_throw(isc::BadValue, "RBTree::find is given a non empty chain");
- }
-
- RBNode<T>* node = root_.get();
- Result ret = NOTFOUND;
- dns::LabelSequence target_labels(target_labels_orig);
-
- while (node != NULL) {
- node_path.last_compared_ = node;
- node_path.last_comparison_ = target_labels.compare(node->getLabels());
- const isc::dns::NameComparisonResult::NameRelation relation =
- node_path.last_comparison_.getRelation();
-
- if (relation == isc::dns::NameComparisonResult::EQUAL) {
- if (needsReturnEmptyNode_ || !node->isEmpty()) {
- node_path.push(node);
- *target = node;
- ret = EXACTMATCH;
- }
- break;
- } else if (relation == isc::dns::NameComparisonResult::NONE) {
- // If the two labels have no hierarchical relationship in terms
- // of matching, we should continue the binary search.
- node = (node_path.last_comparison_.getOrder() < 0) ?
- node->getLeft() : node->getRight();
- } else {
- if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
- if (needsReturnEmptyNode_ || !node->isEmpty()) {
- ret = PARTIALMATCH;
- *target = node;
- if (callback != NULL &&
- node->getFlag(RBNode<T>::FLAG_CALLBACK)) {
- if ((callback)(*node, callback_arg)) {
- break;
- }
- }
- }
- node_path.push(node);
- target_labels.stripRight(
- node_path.last_comparison_.getCommonLabels());
- node = node->getDown();
- } else {
- break;
- }
- }
- }
-
- return (ret);
-}
-
-template <typename T>
-const RBNode<T>*
-RBTree<T>::nextNode(RBTreeNodeChain<T>& node_path) const {
- if (node_path.isEmpty()) {
- isc_throw(isc::BadValue, "RBTree::nextNode is given an empty chain");
- }
-
- const RBNode<T>* node = node_path.top();
- // if node has sub domain, the next domain is the smallest
- // domain in sub domain tree
- const RBNode<T>* down = node->getDown();
- if (down != NULL) {
- const RBNode<T>* left_most = down;
- while (left_most->getLeft() != NULL) {
- left_most = left_most->getLeft();
- }
- node_path.push(left_most);
- return (left_most);
- }
-
- // try to find a successor.
- // if no successor found move to up level, the next successor
- // is the successor of up node in the up level tree, if
- // up node doesn't have successor we gonna keep moving to up
- // level
- while (!node_path.isEmpty()) {
- const RBNode<T>* up_node_successor = node_path.top()->successor();
- node_path.pop();
- if (up_node_successor != NULL) {
- node_path.push(up_node_successor);
- return (up_node_successor);
- }
- }
-
- return (NULL);
-}
-
-template <typename T>
-const RBNode<T>*
-RBTree<T>::previousNode(RBTreeNodeChain<T>& node_path) const {
- if (getNodeCount() == 0) {
- // Special case for empty trees. It would look every time like
- // we didn't search, because the last compared is empty. This is
- // a slight hack and not perfect, but this is better than throwing
- // on empty tree. And we probably won't meet an empty tree in practice
- // anyway.
- return (NULL);
- }
- if (node_path.last_compared_ == NULL) {
- isc_throw(isc::BadValue,
- "RBTree::previousNode() called before find()");
- }
-
- // If the relation isn't EQUAL, it means the find was called previously
- // and didn't find the exact node. Therefore we need to locate the place
- // to start iterating the chain of domains.
- //
- // The logic here is not too complex, we just need to take care to handle
- // all the cases and decide where to go from there.
- switch (node_path.last_comparison_.getRelation()) {
- case dns::NameComparisonResult::COMMONANCESTOR:
- case dns::NameComparisonResult::NONE:
- // We compared with a leaf in the tree and wanted to go to one of
- // the children. But the child was not there. It now depends on the
- // direction in which we wanted to go.
- if (node_path.last_comparison_.getOrder() < 0) {
- // We wanted to go left. So the one we compared with is
- // the one higher than we wanted. If we just put it into
- // the node_path, then the following algorithm below will find
- // the smaller one.
- //
- // This is exactly the same as with superdomain below.
- // Therefore, we just fall through to the next case.
- } else {
- // We wanted to go right. That means we want to output the
- // one which is the largest in the tree defined by the
- // compared one (it is either the compared one, or some
- // subdomain of it). There probably is not an easy trick
- // for this, so we just find the correct place.
- const RBNode<T>* current(node_path.last_compared_);
- while (current != NULL) {
- node_path.push(current);
- // Go a level down and as much right there as possible
- current = current->getDown();
- if (current != NULL) {
- const RBNode<T>* right;
- while ((right = current->getRight()) != NULL) {
- current = right;
- }
- }
- }
- // Now, the one on top of the path is the one we want. We
- // return it now and leave it there, so we can search for
- // previous of it the next time we'are called.
- node_path.last_comparison_ =
- dns::NameComparisonResult(0, 0,
- dns::NameComparisonResult::EQUAL);
- return (node_path.top());
- }
- // No break; here - we want to fall through. See above.
- case dns::NameComparisonResult::SUPERDOMAIN:
- // This is the case there's a "compressed" node and we looked for
- // only part of it. The node itself is larger than we wanted, but
- // if we put it to the node_path and then go one step left from it,
- // we get the correct result.
- node_path.push(node_path.last_compared_);
- // Correct the comparison result, so we won't trigger this case
- // next time previousNode is called. We already located the correct
- // place to start. The value is partly nonsense, but that doesn't
- // matter any more.
- node_path.last_comparison_ =
- dns::NameComparisonResult(0, 0,
- dns::NameComparisonResult::EQUAL);
- break;
- case dns::NameComparisonResult::SUBDOMAIN:
- // A subdomain means we returned the one above the searched one
- // already and it is on top of the stack. This is was smaller
- // than the one already, but we want to return yet smaller one.
- // So we act as if it was EQUAL.
- break;
- case dns::NameComparisonResult::EQUAL:
- // The find gave us an exact match or the previousNode was called
- // already, which located the exact node. The rest of the function
- // goes one domain left and returns it for us.
- break;
- }
-
- // So, the node_path now contains the path to a node we want previous for.
- // We just need to go one step left.
-
- if (node_path.isEmpty()) {
- // We got past the first one. So, we're returning NULL from
- // now on.
- return (NULL);
- }
-
- const RBNode<T>* node(node_path.top());
-
- // Try going left in this tree
- node = node->predecessor();
- if (node == NULL) {
- // We are the smallest ones in this tree. We go one level
- // up. That one is the smaller one than us.
-
- node_path.pop();
- if (node_path.isEmpty()) {
- // We're past the first one
- return (NULL);
- } else {
- return (node_path.top());
- }
- }
-
- // Exchange the node at the top of the path, as we move horizontaly
- // through the domain tree
- node_path.pop();
- node_path.push(node);
-
- // Try going as deep as possible, keeping on the right side of the trees
- const RBNode<T>* down;
- while ((down = node->getDown()) != NULL) {
- // Move to the tree below
- node = down;
- if (node != NULL) {
- // And get as much to the right of the tree as possible
- const RBNode<T>* right;
- while ((right = node->getRight()) != NULL) {
- node = right;
- }
- }
- // Now, we found the right-most node in the sub-tree, we need to
- // include it in the path
- node_path.push(node);
- }
-
- // Now, if the current node has no down_ pointer any more, it's the
- // correct one.
- return (node);
-}
-
-template <typename T>
-typename RBTree<T>::Result
-RBTree<T>::insert(util::MemorySegment& mem_sgmt,
- const isc::dns::Name& target_name, RBNode<T>** new_node)
-{
- RBNode<T>* parent = NULL;
- RBNode<T>* current = root_.get();
- RBNode<T>* up_node = NULL;
- isc::dns::LabelSequence target_labels(target_name);
-
- int order = -1;
- // For possible LabelSequence serialization we always store labels data
- // in the separate local buffer.
- uint8_t labels_buf[dns::LabelSequence::MAX_SERIALIZED_LENGTH];
- while (current != NULL) {
- const dns::LabelSequence current_labels(
- dns::LabelSequence(current->getLabels(), labels_buf));
- const isc::dns::NameComparisonResult compare_result =
- target_labels.compare(current_labels);
- const isc::dns::NameComparisonResult::NameRelation relation =
- compare_result.getRelation();
- if (relation == isc::dns::NameComparisonResult::EQUAL) {
- if (new_node != NULL) {
- *new_node = current;
- }
- return (ALREADYEXISTS);
- } else if (relation == isc::dns::NameComparisonResult::NONE) {
- parent = current;
- order = compare_result.getOrder();
- current = order < 0 ? current->getLeft() : current->getRight();
- } else if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
- // insert sub domain to sub tree
- parent = NULL;
- up_node = current;
- target_labels.stripRight(compare_result.getCommonLabels());
- current = current->getDown();
- } else {
- // The number of labels in common is fewer than the number of
- // labels at the current node, so the current node must be
- // adjusted to have just the common suffix, and a down pointer
- // made to a new tree.
- dns::LabelSequence common_ancestor = target_labels;
- common_ancestor.stripLeft(target_labels.getLabelCount() -
- compare_result.getCommonLabels());
- dns::LabelSequence new_prefix = current_labels;
- new_prefix.stripRight(compare_result.getCommonLabels());
- nodeFission(mem_sgmt, *current, new_prefix, common_ancestor);
- current = current->getParent();
- }
- }
-
- typename RBNode<T>::RBNodePtr* current_root = (up_node != NULL) ?
- &(up_node->down_) : &root_;
- // Once a new node is created, no exception will be thrown until the end
- // of the function, so we can simply create and hold a new node pointer.
- RBNode<T>* node = RBNode<T>::create(mem_sgmt, target_labels);
- node->parent_ = parent;
- if (parent == NULL) {
- *current_root = node;
- // node is the new root of sub tree, so its init color is BLACK
- node->setColor(RBNode<T>::BLACK);
- node->setSubTreeRoot(true);
- node->parent_ = up_node;
- } else if (order < 0) {
- node->setSubTreeRoot(false);
- parent->left_ = node;
- } else {
- node->setSubTreeRoot(false);
- parent->right_ = node;
- }
- insertRebalance(current_root, node);
- if (new_node != NULL) {
- *new_node = node;
- }
-
- ++node_count_;
- return (SUCCESS);
-}
-
-template <typename T>
-void
-RBTree<T>::deleteAllNodes(util::MemorySegment& mem_sgmt) {
- deleteHelper(mem_sgmt, root_.get());
- root_ = NULL;
-}
-
-template <typename T>
-void
-RBTree<T>::nodeFission(util::MemorySegment& mem_sgmt, RBNode<T>& node,
- const isc::dns::LabelSequence& new_prefix,
- const isc::dns::LabelSequence& new_suffix)
-{
- // Create and reset the labels.
- // Once a new node is created, no exception will be thrown until
- // the end of the function, and it will keep consistent behavior
- // (i.e., a weak form of strong exception guarantee) even if code
- // after the call to this function throws an exception.
- RBNode<T>* up_node = RBNode<T>::create(mem_sgmt, new_suffix);
- node.resetLabels(new_prefix);
-
- up_node->parent_ = node.getParent();
- if (node.getParent() != NULL) {
- if (node.getParent()->getLeft() == &node) {
- node.getParent()->left_ = up_node;
- } else if (node.getParent()->getRight() == &node) {
- node.getParent()->right_ = up_node;
- } else {
- node.getParent()->down_ = up_node;
- }
- } else {
- this->root_ = up_node;
- }
-
- up_node->down_ = &node;
- node.parent_ = up_node;
-
- // inherit the left/right pointers from the original node, and set
- // the original node's left/right pointers to NULL.
- up_node->left_ = node.getLeft();
- if (node.getLeft() != NULL) {
- node.getLeft()->parent_ = up_node;
- }
- up_node->right_ = node.getRight();
- if (node.getRight() != NULL) {
- node.getRight()->parent_ = up_node;
- }
- node.left_ = NULL;
- node.right_ = NULL;
-
- // set color of both nodes; the initial subtree node color is BLACK
- up_node->setColor(node.getColor());
- node.setColor(RBNode<T>::BLACK);
-
- // set the subtree root flag of both nodes
- up_node->setSubTreeRoot(node.isSubTreeRoot());
- node.setSubTreeRoot(true);
-
- ++node_count_;
-}
-
-
-template <typename T>
-void
-RBTree<T>::insertRebalance(typename RBNode<T>::RBNodePtr* root,
- RBNode<T>* node)
-{
- RBNode<T>* uncle;
- RBNode<T>* parent;
- while (node != (*root).get() &&
- (parent = node->getParent())->getColor() == RBNode<T>::RED) {
- // Here, node->parent_ is not NULL and it is also red, so
- // node->parent_->parent_ is also not NULL.
- if (parent == parent->getParent()->getLeft()) {
- uncle = parent->getParent()->getRight();
-
- if (uncle != NULL && uncle->getColor() == RBNode<T>::RED) {
- parent->setColor(RBNode<T>::BLACK);
- uncle->setColor(RBNode<T>::BLACK);
- parent->getParent()->setColor(RBNode<T>::RED);
- node = parent->getParent();
- } else {
- if (node == parent->getRight()) {
- node = parent;
- leftRotate(root, node);
- parent = node->getParent();
- }
- parent->setColor(RBNode<T>::BLACK);
- parent->getParent()->setColor(RBNode<T>::RED);
- rightRotate(root, parent->getParent());
- }
- } else {
- uncle = parent->getParent()->getLeft();
- if (uncle != NULL && uncle->getColor() == RBNode<T>::RED) {
- parent->setColor(RBNode<T>::BLACK);
- uncle->setColor(RBNode<T>::BLACK);
- parent->getParent()->setColor(RBNode<T>::RED);
- node = parent->getParent();
- } else {
- if (node == parent->getLeft()) {
- node = parent;
- rightRotate(root, node);
- parent = node->getParent();
- }
- parent->setColor(RBNode<T>::BLACK);
- parent->getParent()->setColor(RBNode<T>::RED);
- leftRotate(root, parent->getParent());
- }
- }
- }
-
- (*root)->setColor(RBNode<T>::BLACK);
-}
-
-
-template <typename T>
-RBNode<T>*
-RBTree<T>::leftRotate(typename RBNode<T>::RBNodePtr* root, RBNode<T>* node) {
- RBNode<T>* const right = node->getRight();
- RBNode<T>* const rleft = right->getLeft();
- node->right_ = rleft;
- if (rleft != NULL) {
- rleft->parent_ = node;
- }
-
- RBNode<T>* const parent = node->getParent();
- right->parent_ = parent;
-
- if (!node->isSubTreeRoot()) {
- right->setSubTreeRoot(false);
- if (node == parent->getLeft()) {
- parent->left_ = right;
- } else {
- parent->right_ = right;
- }
- } else {
- right->setSubTreeRoot(true);
- *root = right;
- }
-
- right->left_ = node;
- node->parent_ = right;
- node->setSubTreeRoot(false);
- return (node);
-}
-
-template <typename T>
-RBNode<T>*
-RBTree<T>::rightRotate(typename RBNode<T>::RBNodePtr* root, RBNode<T>* node) {
- RBNode<T>* const left = node->getLeft();
- RBNode<T>* const lright = left->getRight();
- node->left_ = lright;
- if (lright != NULL) {
- lright->parent_ = node;
- }
-
- RBNode<T>* const parent = node->getParent();
- left->parent_ = parent;
-
- if (!node->isSubTreeRoot()) {
- left->setSubTreeRoot(false);
- if (node == parent->getRight()) {
- parent->right_ = left;
- } else {
- parent->left_ = left;
- }
- } else {
- left->setSubTreeRoot(true);
- *root = left;
- }
- left->right_ = node;
- node->parent_ = left;
- node->setSubTreeRoot(false);
-
- return (node);
-}
-
-
-template <typename T>
-void
-RBTree<T>::dumpTree(std::ostream& os, unsigned int depth) const {
- indent(os, depth);
- os << "tree has " << node_count_ << " node(s)\n";
- dumpTreeHelper(os, root_.get(), depth);
-}
-
-template <typename T>
-void
-RBTree<T>::dumpTreeHelper(std::ostream& os, const RBNode<T>* node,
- unsigned int depth) const
-{
- if (node == NULL) {
- indent(os, depth);
- os << "NULL\n";
- return;
- }
-
- indent(os, depth);
- os << node->getLabels() << " ("
- << ((node->getColor() == RBNode<T>::BLACK) ? "black" : "red")
- << ")";
- if (node->isEmpty()) {
- os << " [invisible]";
- }
- if (node->isSubTreeRoot()) {
- os << " [subtreeroot]";
- }
- os << "\n";
-
- const RBNode<T>* down = node->getDown();
- if (down != NULL) {
- indent(os, depth + 1);
- os << "begin down from " << node->getLabels() << "\n";
- dumpTreeHelper(os, down, depth + 1);
- indent(os, depth + 1);
- os << "end down from " << node->getLabels() << "\n";
- }
- dumpTreeHelper(os, node->getLeft(), depth + 1);
- dumpTreeHelper(os, node->getRight(), depth + 1);
-}
-
-template <typename T>
-void
-RBTree<T>::indent(std::ostream& os, unsigned int depth) {
- static const unsigned int INDENT_FOR_EACH_DEPTH = 5;
- os << std::string(depth * INDENT_FOR_EACH_DEPTH, ' ');
-}
-
-template <typename T>
-void
-RBTree<T>::dumpDot(std::ostream& os, bool show_pointers) const {
- int nodecount = 0;
-
- os << "digraph g {\n";
- os << "node [shape = record,height=.1];\n";
- dumpDotHelper(os, root_.get(), &nodecount, show_pointers);
- os << "}\n";
-}
-
-template <typename T>
-int
-RBTree<T>::dumpDotHelper(std::ostream& os, const RBNode<T>* node,
- int* nodecount, bool show_pointers) const
-{
- if (node == NULL) {
- return 0;
- }
-
- int l = dumpDotHelper(os, node->getLeft(), nodecount, show_pointers);
- int r = dumpDotHelper(os, node->getRight(), nodecount, show_pointers);
- int d = dumpDotHelper(os, node->getDown(), nodecount, show_pointers);
-
- *nodecount += 1;
-
- os << "node" << *nodecount <<
- "[label = \"<f0> |<f1> " << node->getLabels() <<
- "|<f2>";
- if (show_pointers) {
- os << "|<f3> n=" << node << "|<f4> p=" << node->getParent();
- }
- os << "\"] [";
-
- if (node->getColor() == RBNode<T>::RED) {
- os << "color=red";
- } else {
- os << "color=black";
- }
-
- if (node->isSubTreeRoot()) {
- os << ",penwidth=3";
- }
-
- if (node->isEmpty()) {
- os << ",style=filled,fillcolor=lightgrey";
- }
-
- os << "];\n";
-
- if (node->getLeft() != NULL) {
- os << "\"node" << *nodecount << "\":f0 -> \"node" << l << "\":f1;\n";
- }
-
- if (node->getDown() != NULL) {
- os << "\"node" << *nodecount << "\":f1 -> \"node" << d << "\":f1 [penwidth=5];\n";
- }
-
- if (node->getRight() != NULL) {
- os << "\"node" << *nodecount << "\":f2 -> \"node" << r << "\":f1;\n";
- }
-
- return (*nodecount);
-}
-
-}
-}
-
-#endif // RBTREE_H
-
-// Local Variables:
-// mode: c++
-// End:
// PERFORMANCE OF THIS SOFTWARE.
#include "client.h"
-#include "memory_datasrc.h"
+#include "factory_link.h"
#include <datasrc/memory/memory_client.h>
#include <datasrc/memory/zone_table_segment.h>
run_unittests_SOURCES = $(common_sources)
run_unittests_SOURCES += test_client.h test_client.cc
-run_unittests_SOURCES += rbtree_unittest.cc
run_unittests_SOURCES += logger_unittest.cc
run_unittests_SOURCES += client_unittest.cc
run_unittests_SOURCES += database_unittest.cc
run_unittests_SOURCES += sqlite3_accessor_unittest.cc
-run_unittests_SOURCES += memory_datasrc_unittest.cc
-run_unittests_SOURCES += rbnode_rrset_unittest.cc
-run_unittests_SOURCES += zonetable_unittest.cc
run_unittests_SOURCES += zone_finder_context_unittest.cc
run_unittests_SOURCES += faked_nsec3.h faked_nsec3.cc
run_unittests_SOURCES += client_list_unittest.cc
isc::dns::Name("example.org."), false));
}
-TEST(FactoryTest, memoryClient) {
- // We start out by building the configuration data bit by bit,
- // testing each form of 'bad config', until we have a good one.
- // Then we do some very basic operation on the client (detailed
- // tests are left to the implementation-specific backends)
- ElementPtr config;
- ASSERT_THROW(DataSourceClientContainer client("memory", config),
- DataSourceError);
-
- config = Element::create("asdf");
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config = Element::createMap();
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("type", ElementPtr());
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("type", Element::create(1));
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("type", Element::create("FOO"));
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("type", Element::create("memory"));
- // no config at all should result in a default empty memory client
- ASSERT_NO_THROW(DataSourceClientContainer("memory", config));
-
- config->set("class", ElementPtr());
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("class", Element::create(1));
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("class", Element::create("FOO"));
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("class", Element::create("IN"));
- ASSERT_NO_THROW(DataSourceClientContainer("memory", config));
-
- config->set("zones", ElementPtr());
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("zones", Element::create(1));
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
-
- config->set("zones", Element::createList());
- DataSourceClientContainer dsc("memory", config);
-
- // Once it is able to load some zones, we should add a few tests
- // here to see that it does.
- DataSourceClient::FindResult result(
- dsc.getInstance().findZone(isc::dns::Name("no.such.zone.")));
- ASSERT_EQ(result::NOTFOUND, result.code);
-
- ASSERT_THROW(dsc.getInstance().getIterator(isc::dns::Name("example.org.")),
- DataSourceError);
-
- ASSERT_THROW(dsc.getInstance().getUpdater(isc::dns::Name("no.such.zone."),
- false), isc::NotImplemented);
-}
-
TEST(FactoryTest, badType) {
ASSERT_THROW(DataSourceClientContainer("foo", ElementPtr()),
DataSourceError);
+++ /dev/null
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include "faked_nsec3.h"
-
-#include <exceptions/exceptions.h>
-
-#include <dns/masterload.h>
-#include <dns/name.h>
-#include <dns/nsec3hash.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrttl.h>
-#include <dns/masterload.h>
-
-#include <datasrc/client.h>
-#include <datasrc/memory_datasrc.h>
-#include <datasrc/data_source.h>
-#include <datasrc/zone_iterator.h>
-
-#include "test_client.h"
-
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <sstream>
-#include <vector>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::testutils;
-using boost::shared_ptr;
-using namespace isc::datasrc::test;
-
-namespace {
-// Commonly used result codes (Who should write the prefix all the time)
-using result::SUCCESS;
-using result::EXIST;
-
-class InMemoryClientTest : public ::testing::Test {
-protected:
- InMemoryClientTest() : rrclass(RRClass::IN())
- {}
- RRClass rrclass;
- InMemoryClient memory_client;
-};
-
-TEST_F(InMemoryClientTest, add_find_Zone) {
- // test add zone
- // Bogus zone (NULL)
- EXPECT_THROW(memory_client.addZone(ZoneFinderPtr()),
- isc::InvalidParameter);
-
- // add zones with different names one by one
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("a")))));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
- Name("b")))));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("c")))));
- // add zones with the same name suffix
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
- Name("x.d.e.f")))));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
- Name("o.w.y.d.e.f")))));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
- Name("p.w.y.d.e.f")))));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("q.w.y.d.e.f")))));
- // add super zone and its subzone
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
- Name("g.h")))));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("i.g.h")))));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("z.d.e.f")))));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("j.z.d.e.f")))));
-
- // different zone class isn't allowed.
- EXPECT_EQ(result::EXIST, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
- Name("q.w.y.d.e.f")))));
-
- // names are compared in a case insensitive manner.
- EXPECT_EQ(result::EXIST, memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("Q.W.Y.d.E.f")))));
-
- // test find zone
- EXPECT_EQ(result::SUCCESS, memory_client.findZone(Name("a")).code);
- EXPECT_EQ(Name("a"),
- memory_client.findZone(Name("a")).zone_finder->getOrigin());
-
- EXPECT_EQ(result::SUCCESS,
- memory_client.findZone(Name("j.z.d.e.f")).code);
- EXPECT_EQ(Name("j.z.d.e.f"),
- memory_client.findZone(Name("j.z.d.e.f")).zone_finder->
- getOrigin());
-
- // NOTFOUND
- EXPECT_EQ(result::NOTFOUND, memory_client.findZone(Name("d.e.f")).code);
- EXPECT_EQ(ConstZoneFinderPtr(),
- memory_client.findZone(Name("d.e.f")).zone_finder);
-
- EXPECT_EQ(result::NOTFOUND,
- memory_client.findZone(Name("w.y.d.e.f")).code);
- EXPECT_EQ(ConstZoneFinderPtr(),
- memory_client.findZone(Name("w.y.d.e.f")).zone_finder);
-
- // there's no exact match. the result should be the longest match,
- // and the code should be PARTIALMATCH.
- EXPECT_EQ(result::PARTIALMATCH,
- memory_client.findZone(Name("j.g.h")).code);
- EXPECT_EQ(Name("g.h"),
- memory_client.findZone(Name("g.h")).zone_finder->getOrigin());
-
- EXPECT_EQ(result::PARTIALMATCH,
- memory_client.findZone(Name("z.i.g.h")).code);
- EXPECT_EQ(Name("i.g.h"),
- memory_client.findZone(Name("z.i.g.h")).zone_finder->
- getOrigin());
-}
-
-TEST_F(InMemoryClientTest, iterator) {
- // Just some preparations of data
- boost::shared_ptr<InMemoryZoneFinder>
- zone(new InMemoryZoneFinder(RRClass::IN(), Name("a")));
- RRsetPtr aRRsetA(new RRset(Name("a"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- aRRsetA->addRdata(rdata::in::A("192.0.2.1"));
- RRsetPtr aRRsetAAAA(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(),
- RRTTL(300)));
- aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::1"));
- aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::2"));
- RRsetPtr subRRsetA(new RRset(Name("sub.x.a"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- subRRsetA->addRdata(rdata::in::A("192.0.2.2"));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(zone));
- // First, the zone is not there, so it should throw
- EXPECT_THROW(memory_client.getIterator(Name("b")), DataSourceError);
- // This zone is not there either, even when there's a zone containing this
- EXPECT_THROW(memory_client.getIterator(Name("x.a")), DataSourceError);
- // Now, an empty zone
- ZoneIteratorPtr iterator(memory_client.getIterator(Name("a")));
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
- // It throws Unexpected when we are past the end
- EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
- EXPECT_EQ(result::SUCCESS, zone->add(aRRsetA));
- EXPECT_EQ(result::SUCCESS, zone->add(aRRsetAAAA));
- EXPECT_EQ(result::SUCCESS, zone->add(subRRsetA));
-
- // Check it with full zone.
- vector<ConstRRsetPtr> expected_rrsets;
- expected_rrsets.push_back(aRRsetA);
- expected_rrsets.push_back(aRRsetAAAA);
- expected_rrsets.push_back(subRRsetA);
-
- iterator = memory_client.getIterator(Name("a"));
- vector<ConstRRsetPtr> actual_rrsets;
- ConstRRsetPtr actual;
- while ((actual = iterator->getNextRRset()) != NULL) {
- actual_rrsets.push_back(actual);
- }
-
- rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
- actual_rrsets.begin(), actual_rrsets.end());
-
-}
-
-TEST_F(InMemoryClientTest, iterator_separate_rrs) {
- // Exactly the same tests as for iterator, but now with separate_rrs = true
- // For the one that returns actual data, the AAAA should now be split up
- boost::shared_ptr<InMemoryZoneFinder>
- zone(new InMemoryZoneFinder(RRClass::IN(), Name("a")));
- RRsetPtr aRRsetA(new RRset(Name("a"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- aRRsetA->addRdata(rdata::in::A("192.0.2.1"));
- RRsetPtr aRRsetAAAA(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(),
- RRTTL(300)));
- aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::1"));
- aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::2"));
- RRsetPtr aRRsetAAAA_r1(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(),
- RRTTL(300)));
- aRRsetAAAA_r1->addRdata(rdata::in::AAAA("2001:db8::1"));
- RRsetPtr aRRsetAAAA_r2(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(),
- RRTTL(300)));
- aRRsetAAAA_r2->addRdata(rdata::in::AAAA("2001:db8::2"));
-
- RRsetPtr subRRsetA(new RRset(Name("sub.x.a"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- subRRsetA->addRdata(rdata::in::A("192.0.2.2"));
- EXPECT_EQ(result::SUCCESS, memory_client.addZone(zone));
-
- // First, the zone is not there, so it should throw
- EXPECT_THROW(memory_client.getIterator(Name("b"), true), DataSourceError);
- // This zone is not there either, even when there's a zone containing this
- EXPECT_THROW(memory_client.getIterator(Name("x.a")), DataSourceError);
- // Now, an empty zone
- ZoneIteratorPtr iterator(memory_client.getIterator(Name("a"), true));
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
- // It throws Unexpected when we are past the end
- EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
-
- ASSERT_EQ(result::SUCCESS, zone->add(aRRsetA));
- ASSERT_EQ(result::SUCCESS, zone->add(aRRsetAAAA));
- ASSERT_EQ(result::SUCCESS, zone->add(subRRsetA));
- // Check it with full zone, one by one.
- // It should be in ascending order in case of InMemory data source
- // (isn't guaranteed in general)
- iterator = memory_client.getIterator(Name("a"), true);
- EXPECT_EQ(aRRsetA->toText(), iterator->getNextRRset()->toText());
- EXPECT_EQ(aRRsetAAAA_r1->toText(), iterator->getNextRRset()->toText());
- EXPECT_EQ(aRRsetAAAA_r2->toText(), iterator->getNextRRset()->toText());
- EXPECT_EQ(subRRsetA->toText(), iterator->getNextRRset()->toText());
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-}
-
-TEST_F(InMemoryClientTest, getZoneCount) {
- EXPECT_EQ(0, memory_client.getZoneCount());
- memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(rrclass,
- Name("example.com"))));
- EXPECT_EQ(1, memory_client.getZoneCount());
-
- // duplicate add. counter shouldn't change
- memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(rrclass,
- Name("example.com"))));
- EXPECT_EQ(1, memory_client.getZoneCount());
-
- // add one more
- memory_client.addZone(
- ZoneFinderPtr(new InMemoryZoneFinder(rrclass,
- Name("example.org"))));
- EXPECT_EQ(2, memory_client.getZoneCount());
-}
-
-TEST_F(InMemoryClientTest, startUpdateZone) {
- EXPECT_THROW(memory_client.getUpdater(Name("example.org"), false),
- isc::NotImplemented);
-}
-
-// Commonly used RRSIG data
-const char* const rrsig_a_txt =
- "example.org. 300 IN RRSIG A 5 3 3600 20000101000000 20000201000000 12345 "
- "example.org. FAKEFAKEFAKE\n";
-const char* const rrsig_ns_txt =
- "example.org. 300 IN RRSIG NS 5 3 3600 20000101000000 20000201000000 "
- "54321 example.org. FAKEFAKEFAKEFAKE\n";
-// This RRset has two RRSIGs
-const char* const rrsig_aaaa_txt =
- "ns.example.org. 300 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 "
- "12345 example.org. FAKEFAKEFAKE\n"
- "ns.example.org. 300 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 "
- "54321 example.org. FAKEFAKEFAKEFAKE\n";
-
-// A helper callback of masterLoad() used in InMemoryZoneFinderTest.
-void
-setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
- *(*it) = rrset;
- ++it;
-}
-
-/// \brief Test fixture for the InMemoryZoneFinder class
-class InMemoryZoneFinderTest : public ::testing::Test {
- // A straightforward pair of textual RR(set) and a RRsetPtr variable
- // to store the RRset. Used to build test data below.
- struct RRsetData {
- const char* const text; // textual representation of an RRset
- RRsetPtr* rrset;
- };
-protected:
- // The following sub tests are shared by multiple test cases, changing
- // the zone's DNSSEC status (unsigned, NSEC-signed or NSEC3-signed).
- // expected_flags is set to either RESULT_NSEC_SIGNED or
- // RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and
- // find() is expected to set the corresponding flags.
- // find_options should be set to FIND_DNSSEC for NSEC-signed case when
- // NSEC is expected to be returned.
- void findCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- ZoneFinder::FindOptions find_options =
- ZoneFinder::FIND_DEFAULT);
- void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
- void wildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- ZoneFinder::FindOptions find_options =
- ZoneFinder::FIND_DEFAULT);
- void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- ZoneFinder::FindOptions find_options =
- ZoneFinder::FIND_DEFAULT);
- void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
- void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
- void findNSECENTCheck(const Name& ent_name,
- ConstRRsetPtr expected_nsec,
- ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
-
-public:
- InMemoryZoneFinderTest() :
- class_(RRClass::IN()),
- origin_("example.org"),
- zone_finder_(class_, origin_)
- {
- // Build test RRsets. Below, we construct an RRset for
- // each textual RR(s) of zone_data, and assign it to the corresponding
- // rr_xxx.
- // Note that this contains an out-of-zone RR, and due to the
- // validation check of masterLoad() used below, we cannot add SOA.
- const RRsetData zone_data[] = {
- {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
- {"example.org. 300 IN A 192.0.2.1", &rr_a_},
- {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
- {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
- {"cname.example.org. 300 IN CNAME canonical.example.org.",
- &rr_cname_},
- {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
- {"dname.example.org. 300 IN DNAME target.example.org.",
- &rr_dname_},
- {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
- {"dname.example.org. 300 IN NS ns.dname.example.org.",
- &rr_dname_ns_},
- {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
- {"child.example.org. 300 IN NS ns.child.example.org.",
- &rr_child_ns_},
- {"child.example.org. 300 IN DS 12345 5 1 DEADBEEF",
- &rr_child_ds_},
- {"ns.child.example.org. 300 IN A 192.0.2.153",
- &rr_child_glue_},
- {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
- &rr_grandchild_ns_},
- {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
- &rr_grandchild_glue_},
- {"dname.child.example.org. 300 IN DNAME example.com.",
- &rr_child_dname_},
- {"example.com. 300 IN A 192.0.2.10", &rr_out_},
- {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
- {"*.cnamewild.example.org. 300 IN CNAME canonial.example.org.",
- &rr_cnamewild_},
- {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
- {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
- {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
- &rr_nested_emptywild_},
- {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
- {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
- &rr_dnamewild_},
- {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_},
- {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
- {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
- &rr_not_wild_another_},
- {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
- "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
- &rr_nsec3_},
- {"example.org. 300 IN NSEC wild.*.foo.example.org. "
- "NS SOA RRSIG NSEC DNSKEY", &rr_nsec_},
- // Together with the apex NSEC, these next NSECs make a complete
- // chain in the case of the zone for the emptyNonterminal tests
- // (We may want to clean up this generator code and/or masterLoad
- // so that we can prepare conflicting datasets better)
- {"wild.*.foo.example.org. 3600 IN NSEC ns.example.org. "
- "A RRSIG NSEC", &rr_ent_nsec2_},
- {"ns.example.org. 3600 IN NSEC foo.wild.example.org. A RRSIG NSEC",
- &rr_ent_nsec3_},
- {"foo.wild.example.org. 3600 IN NSEC example.org. A RRSIG NSEC",
- &rr_ent_nsec4_},
- // And these are NSECs used in different tests
- {"ns.example.org. 300 IN NSEC *.nswild.example.org. A AAAA NSEC",
- &rr_ns_nsec_},
- {"*.wild.example.org. 300 IN NSEC foo.wild.example.org. A NSEC",
- &rr_wild_nsec_},
- {NULL, NULL}
- };
-
- stringstream zone_data_stream;
- vector<RRsetPtr*> rrsets;
- for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
- zone_data_stream << zone_data[i].text << "\n";
- rrsets.push_back(zone_data[i].rrset);
- }
-
- masterLoad(zone_data_stream, Name::ROOT_NAME(), class_,
- boost::bind(setRRset, _1, rrsets.begin()));
- }
-
- ~InMemoryZoneFinderTest() {
- // Make sure we reset the hash creator to the default
- setNSEC3HashCreator(NULL);
- }
-
- // Some data to test with
- const RRClass class_;
- const Name origin_;
- // The zone finder to torture by tests
- InMemoryZoneFinder zone_finder_;
-
- // Placeholder for storing RRsets to be checked with rrsetsCheck()
- vector<ConstRRsetPtr> actual_rrsets_;
-
- /*
- * Some RRsets to put inside the zone.
- */
- RRsetPtr
- // Out of zone RRset
- rr_out_,
- // NS of example.org
- rr_ns_,
- // A of ns.example.org
- rr_ns_a_,
- // AAAA of ns.example.org
- rr_ns_aaaa_,
- // A of example.org
- rr_a_;
- RRsetPtr rr_cname_; // CNAME in example.org (RDATA will be added)
- RRsetPtr rr_cname_a_; // for mixed CNAME + A case
- RRsetPtr rr_dname_; // DNAME in example.org (RDATA will be added)
- RRsetPtr rr_dname_a_; // for mixed DNAME + A case
- RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
- RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
- RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
- RRsetPtr rr_child_ds_; // DS of a child domain (for delegation, auth data)
- RRsetPtr rr_child_glue_; // glue RR of the child domain
- RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
- RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
- RRsetPtr rr_child_dname_; // A DNAME under NS
- RRsetPtr rr_wild_; // Wildcard record
- RRsetPtr rr_cnamewild_; // CNAME at a wildcard
- RRsetPtr rr_emptywild_;
- RRsetPtr rr_nested_emptywild_;
- RRsetPtr rr_nswild_, rr_dnamewild_;
- RRsetPtr rr_child_wild_;
- RRsetPtr rr_under_wild_;
- RRsetPtr rr_not_wild_;
- RRsetPtr rr_not_wild_another_;
- RRsetPtr rr_nsec3_;
- RRsetPtr rr_nsec_;
- RRsetPtr rr_ent_nsec2_;
- RRsetPtr rr_ent_nsec3_;
- RRsetPtr rr_ent_nsec4_;
- RRsetPtr rr_ns_nsec_;
- RRsetPtr rr_wild_nsec_;
-
- // A faked NSEC3 hash calculator for convenience.
- // Tests that need to use the faked hashed values should call
- // setNSEC3HashCreator() with a pointer to this variable at the beginning
- // of the test (at least before adding any NSEC3/NSEC3PARAM RR).
- TestNSEC3HashCreator nsec3_hash_creator_;
-
- /**
- * \brief Test one find query to the zone finder.
- *
- * Asks a query to the zone finder and checks it does not throw and returns
- * expected results. It returns nothing, it just signals failures
- * to GTEST.
- *
- * \param name The name to ask for.
- * \param rrtype The RRType to ask of.
- * \param result The expected code of the result.
- * \param check_answer Should a check against equality of the answer be
- * done?
- * \param answer The expected rrset, if any should be returned.
- * \param expected_flags The expected result flags returned via find().
- * These can be tested using isWildcard() etc.
- * \param zone_finder Check different InMemoryZoneFinder object than
- * zone_finder_ (if NULL, uses zone_finder_)
- * \param check_wild_answer Checks that the answer has the same RRs, type
- * class and TTL as the eqxpected answer and that the name corresponds
- * to the one searched. It is meant for checking answers for wildcard
- * queries.
- */
- void findTest(const Name& name, const RRType& rrtype,
- ZoneFinder::Result result,
- bool check_answer = true,
- const ConstRRsetPtr& answer = ConstRRsetPtr(),
- ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- InMemoryZoneFinder* zone_finder = NULL,
- ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
- bool check_wild_answer = false)
- {
- SCOPED_TRACE("findTest for " + name.toText() + "/" + rrtype.toText());
-
- if (zone_finder == NULL) {
- zone_finder = &zone_finder_;
- }
- const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
- RRsetPtr(); // note we use the same type as of retval of getRRsig()
- // The whole block is inside, because we need to check the result and
- // we can't assign to FindResult
- EXPECT_NO_THROW({
- ZoneFinderContextPtr find_result(zone_finder->find(
- name, rrtype, options));
- // Check it returns correct answers
- EXPECT_EQ(result, find_result->code);
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- find_result->isWildcard());
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
- != 0, find_result->isNSECSigned());
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
- != 0, find_result->isNSEC3Signed());
- if (check_answer) {
- if (!answer) {
- ASSERT_FALSE(find_result->rrset);
- } else {
- ASSERT_TRUE(find_result->rrset);
- rrsetCheck(answer, find_result->rrset);
- if (answer_sig) {
- ASSERT_TRUE(find_result->rrset->getRRsig());
- rrsetCheck(answer_sig,
- find_result->rrset->getRRsig());
- }
- }
- } else if (check_wild_answer) {
- ASSERT_NE(ConstRRsetPtr(), answer) <<
- "Wrong test, don't check for wild names if you expect "
- "empty answer";
- ASSERT_NE(ConstRRsetPtr(), find_result->rrset) <<
- "No answer found";
- // Build the expected answer using the given name and
- // other parameter of the base wildcard RRset.
- RRsetPtr wildanswer(new RRset(name, answer->getClass(),
- answer->getType(),
- answer->getTTL()));
- RdataIteratorPtr expectedIt(answer->getRdataIterator());
- for (; !expectedIt->isLast(); expectedIt->next()) {
- wildanswer->addRdata(expectedIt->getCurrent());
- }
- rrsetCheck(wildanswer, find_result->rrset);
-
- // Same for the RRSIG, if any.
- if (answer_sig) {
- ASSERT_TRUE(find_result->rrset->getRRsig());
-
- RRsetPtr wildsig(new RRset(name,
- answer_sig->getClass(),
- RRType::RRSIG(),
- answer_sig->getTTL()));
- RdataIteratorPtr expectedIt(
- answer_sig->getRdataIterator());
- for (; !expectedIt->isLast(); expectedIt->next()) {
- wildsig->addRdata(expectedIt->getCurrent());
- }
- rrsetCheck(wildsig, find_result->rrset->getRRsig());
- }
- }
- });
- }
- /**
- * \brief Calls the findAll on the finder and checks the result.
- */
- void findAllTest(const Name& name, ZoneFinder::Result result,
- const vector<ConstRRsetPtr>& expected_rrsets,
- ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- InMemoryZoneFinder* finder = NULL,
- const ConstRRsetPtr &rrset_result = ConstRRsetPtr(),
- ZoneFinder::FindOptions options =
- ZoneFinder::FIND_DEFAULT)
- {
- if (finder == NULL) {
- finder = &zone_finder_;
- }
- std::vector<ConstRRsetPtr> target;
- ZoneFinderContextPtr find_result(finder->findAll(name, target,
- options));
- EXPECT_EQ(result, find_result->code);
- if (!rrset_result) {
- EXPECT_FALSE(find_result->rrset);
- } else {
- ASSERT_TRUE(find_result->rrset);
- rrsetCheck(rrset_result, find_result->rrset);
- }
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- find_result->isWildcard());
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
- != 0, find_result->isNSECSigned());
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
- != 0, find_result->isNSEC3Signed());
- rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
- target.begin(), target.end());
- }
-};
-
-/**
- * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
- *
- * Takes the created zone finder and checks its properties they are the same
- * as passed parameters.
- */
-TEST_F(InMemoryZoneFinderTest, constructor) {
- ASSERT_EQ(class_, zone_finder_.getClass());
- ASSERT_EQ(origin_, zone_finder_.getOrigin());
-}
-/**
- * \brief Test adding.
- *
- * We test that it throws at the correct moments and the correct exceptions.
- * And we test the return value.
- */
-TEST_F(InMemoryZoneFinderTest, add) {
- // This one does not belong to this zone
- EXPECT_THROW(zone_finder_.add(rr_out_), OutOfZone);
- // Test null pointer
- EXPECT_THROW(zone_finder_.add(ConstRRsetPtr()),
- InMemoryZoneFinder::NullRRset);
-
- // Now put all the data we have there. It should throw nothing
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_a_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_aaaa_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_)));
-
- // Try putting there something twice, it should be rejected
- EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_finder_.add(rr_ns_)));
- EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_finder_.add(rr_ns_a_)));
-}
-
-TEST_F(InMemoryZoneFinderTest, addMultipleCNAMEs) {
- rr_cname_->addRdata(generic::CNAME("canonical2.example.org."));
- EXPECT_THROW(zone_finder_.add(rr_cname_), InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, addCNAMEThenOther) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_));
- EXPECT_THROW(zone_finder_.add(rr_cname_a_), InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, addOtherThenCNAME) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_a_));
- EXPECT_THROW(zone_finder_.add(rr_cname_), InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, addCNAMEAndDNSSECRecords) {
- // CNAME and RRSIG can coexist
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(
- textToRRset("cname.example.org. 300 IN RRSIG CNAME 5 3 "
- "3600 20000101000000 20000201000000 12345 "
- "example.org. FAKEFAKEFAKE")));
-
- // Same for NSEC
- EXPECT_EQ(SUCCESS, zone_finder_.add(
- textToRRset("cname.example.org. 300 IN NSEC "
- "dname.example.org. CNAME RRSIG NSEC")));
-
- // Same as above, but adding NSEC first.
- EXPECT_EQ(SUCCESS, zone_finder_.add(
- textToRRset("cname2.example.org. 300 IN NSEC "
- "dname.example.org. CNAME RRSIG NSEC")));
- EXPECT_EQ(SUCCESS, zone_finder_.add(
- textToRRset("cname2.example.org. 300 IN CNAME c.example.")));
-
- // If there's another type of RRset with NSEC, it should still fail.
- EXPECT_EQ(SUCCESS, zone_finder_.add(
- textToRRset("cname3.example.org. 300 IN A 192.0.2.1")));
- EXPECT_EQ(SUCCESS, zone_finder_.add(
- textToRRset("cname3.example.org. 300 IN NSEC "
- "dname.example.org. CNAME RRSIG NSEC")));
- EXPECT_THROW(zone_finder_.add(textToRRset("cname3.example.org. 300 "
- "IN CNAME c.example.")),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, findCNAME) {
- // install CNAME RR
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_));
-
- // Find A RR of the same. Should match the CNAME
- findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true,
- rr_cname_);
-
- // Find the CNAME itself. Should result in normal SUCCESS
- findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true,
- rr_cname_);
-}
-
-TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
- // There's nothing special when we find a CNAME under a zone cut
- // (with FIND_GLUE_OK). The behavior is different from BIND 9,
- // so we test this case explicitly.
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_));
- ConstRRsetPtr rr_cname_under_cut_ = textToRRset(
- "cname.child.example.org. 300 IN CNAME target.child.example.org.");
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_under_cut_));
- findTest(Name("cname.child.example.org"), RRType::AAAA(),
- ZoneFinder::CNAME, true, rr_cname_under_cut_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-// Two DNAMEs at single domain are disallowed by RFC 2672, section 3)
-// Having a CNAME there is disallowed too, but it is tested by
-// addOtherThenCNAME and addCNAMEThenOther.
-TEST_F(InMemoryZoneFinderTest, addMultipleDNAMEs) {
- rr_dname_->addRdata(generic::DNAME("target2.example.org."));
- EXPECT_THROW(zone_finder_.add(rr_dname_), InMemoryZoneFinder::AddError);
-}
-
-/*
- * These two tests ensure that we can't have DNAME and NS at the same
- * node with the exception of the apex of zone (forbidden by RFC 2672)
- */
-TEST_F(InMemoryZoneFinderTest, addDNAMEThenNS) {
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_)));
- EXPECT_THROW(zone_finder_.add(rr_dname_ns_), InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, addNSThenDNAME) {
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_ns_)));
- EXPECT_THROW(zone_finder_.add(rr_dname_), InMemoryZoneFinder::AddError);
-}
-
-// It is allowed to have NS and DNAME at apex
-TEST_F(InMemoryZoneFinderTest, DNAMEAndNSAtApex) {
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_apex_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
-
- // The NS should be possible to be found, below should be DNAME, not
- // delegation
- findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
- findTest(rr_child_ns_->getName(), RRType::A(), ZoneFinder::DNAME, true,
- rr_dname_apex_);
-}
-
-TEST_F(InMemoryZoneFinderTest, NSAndDNAMEAtApex) {
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_apex_)));
-}
-
-// TODO: Test (and implement) adding data under DNAME. That is forbidden by
-// 2672 as well.
-
-// Search under a DNAME record. It should return the DNAME
-TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_)));
- findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME,
- true, rr_dname_);
-}
-
-// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
-// influences only the data below (see RFC 2672, section 3)
-TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_a_)));
-
- const Name dname_name(rr_dname_->getName());
- findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_);
- findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true,
- rr_dname_);
- findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true);
-}
-
-// Try searching something that is both under NS and DNAME, without and with
-// GLUE_OK mode (it should stop at the NS and DNAME respectively).
-TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
- zone_finder_.add(rr_child_ns_);
- zone_finder_.add(rr_child_dname_);
-
- Name lowName("below.dname.child.example.org.");
-
- findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
- findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-// Test adding child zones and zone cut handling
-TEST_F(InMemoryZoneFinderTest, delegationNS) {
- // add in-zone data
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
-
- // install a zone cut
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
-
- // below the zone cut
- findTest(Name("www.child.example.org"), RRType::A(),
- ZoneFinder::DELEGATION, true, rr_child_ns_);
-
- // at the zone cut
- findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
- true, rr_child_ns_);
- findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION,
- true, rr_child_ns_);
-
- // finding NS for the apex (origin) node. This must not be confused
- // with delegation due to the existence of an NS RR.
- findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
-
- // unusual case of "nested delegation": the highest cut should be used.
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_ns_)));
- findTest(Name("www.grand.child.example.org"), RRType::A(),
- // note: !rr_grandchild_ns_
- ZoneFinder::DELEGATION, true, rr_child_ns_);
-}
-
-TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
- // Similar setup to the previous one, but with DS RR at the delegation
- // point.
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ds_));
-
- // Normal types of query should result in delegation, but DS query
- // should be considered in-zone (but only exactly at the delegation point).
- findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
- true, rr_child_ns_);
- findTest(Name("child.example.org"), RRType::DS(), ZoneFinder::SUCCESS,
- true, rr_child_ds_);
- findTest(Name("grand.child.example.org"), RRType::DS(),
- ZoneFinder::DELEGATION, true, rr_child_ns_);
-
- // There's nothing special for DS query at the zone apex. It would
- // normally result in NXRRSET.
- findTest(Name("example.org"), RRType::DS(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr());
-}
-
-TEST_F(InMemoryZoneFinderTest, findAny) {
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_)));
-
- vector<ConstRRsetPtr> expected_sets;
-
- // origin
- expected_sets.push_back(rr_a_);
- expected_sets.push_back(rr_ns_);
- findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets);
-
- // out zone name
- EXPECT_THROW(findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
- vector<ConstRRsetPtr>()),
- OutOfZone);
-
- expected_sets.clear();
- expected_sets.push_back(rr_child_glue_);
- findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets);
-
- // add zone cut
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
-
- // zone cut
- findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION,
- vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
- NULL, rr_child_ns_);
-
- // glue for this zone cut
- findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION,
- vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
- NULL, rr_child_ns_);
-}
-
-TEST_F(InMemoryZoneFinderTest, glue) {
- // install zone data:
- // a zone cut
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
- // glue for this cut
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_)));
- // a nested zone cut (unusual)
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_ns_)));
- // glue under the deeper zone cut
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_glue_)));
-
- // by default glue is hidden due to the zone cut
- findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
- true, rr_child_ns_);
-
-
- // If we do it in the "glue OK" mode, we should find the exact match.
- findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
- rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
- ZoneFinder::FIND_GLUE_OK);
-
- // glue OK + NXRRSET case
- findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, NULL,
- ZoneFinder::FIND_GLUE_OK);
-
- // glue OK + NXDOMAIN case
- findTest(Name("www.child.example.org"), RRType::A(),
- ZoneFinder::DELEGATION, true, rr_child_ns_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-
- // nested cut case. The glue should be found.
- findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
- ZoneFinder::SUCCESS,
- true, rr_grandchild_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
- ZoneFinder::FIND_GLUE_OK);
-
- // A non-existent name in nested cut. This should result in delegation
- // at the highest zone cut.
- findTest(Name("www.grand.child.example.org"), RRType::TXT(),
- ZoneFinder::DELEGATION, true, rr_child_ns_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-/**
- * \brief Test searching.
- *
- * Check it finds or does not find correctly and does not throw exceptions.
- * \todo This doesn't do any kind of CNAME and so on. If it isn't
- * directly there, it just tells it doesn't exist.
- */
-void
-InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
- ZoneFinder::FindOptions find_options)
-{
- // Fill some data inside
- // Now put all the data we have there. It should throw nothing
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_a_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_aaaa_)));
- EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_)));
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
- }
-
- // These two should be successful
- findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
- findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
- rr_ns_a_);
-
- // These domains don't exist. (and one is out of the zone). In an
- // NSEC-signed zone with DNSSEC records requested, it should return the
- // covering NSEC for the query name (the actual NSEC in the test data may
- // not really "cover" it, but for the purpose of this test it's okay).
- ConstRRsetPtr expected_nsec; // by default it's NULL
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
- (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
- expected_nsec = rr_nsec_;
- }
-
- // There's no other name between this one and the origin, so when NSEC
- // is to be returned it should be the origin NSEC.
- findTest(Name("nothere.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
-
- // The previous name in the zone is "ns.example.org", but it doesn't
- // have an NSEC. It should be skipped and the origin NSEC will be
- // returned as the "closest NSEC".
- findTest(Name("nxdomain.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
- EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
- OutOfZone);
-
- // These domain exist but don't have the provided RRType. For the latter
- // one we now add its NSEC (which was delayed so that it wouldn't break
- // other cases above).
- findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
- expected_nsec, expected_flags, NULL, find_options);
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_nsec_));
- if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) {
- expected_nsec = rr_ns_nsec_;
- }
- }
- findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
- expected_nsec, expected_flags, NULL, find_options);
-}
-
-TEST_F(InMemoryZoneFinderTest, find) {
- findCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
- findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSEC3SignedWithDNSSEC) {
- // For NSEC3-signed zones, specifying the DNSSEC option shouldn't change
- // anything (the NSEC3_SIGNED flag is always set, and no records are
- // returned for negative cases regardless).
- findCheck(ZoneFinder::RESULT_NSEC3_SIGNED, ZoneFinder::FIND_DNSSEC);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECSigned) {
- // NSEC-signed zone, without requesting DNSSEC (no NSEC should be provided)
- findCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Generalized test for Empty Nonterminal (ENT) cases with NSEC
-void
-InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
- ConstRRsetPtr expected_nsec,
- ZoneFinder::FindResultFlags expected_flags)
-{
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_emptywild_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_under_wild_));
-
- // Sanity check: Should result in NXRRSET
- findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags);
- // Sanity check: No NSEC added yet
- findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags,
- NULL, ZoneFinder::FIND_DNSSEC);
-
- // Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC,
- // there are no sigs)
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ent_nsec2_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ent_nsec3_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ent_nsec4_));
-
- // Should result in NXRRSET, and RESULT_NSEC_SIGNED
- findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(),
- expected_flags | ZoneFinder::RESULT_NSEC_SIGNED);
-
- // And check for the NSEC if DNSSEC_OK
- findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
- expected_nsec, expected_flags | ZoneFinder::RESULT_NSEC_SIGNED,
- NULL, ZoneFinder::FIND_DNSSEC);
-}
-
-TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) {
- // Non-wildcard case
- findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_);
-}
-
-TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminalWildcard) {
- // Wildcard case, above actual wildcard
- findNSECENTCheck(Name("foo.example.org"), rr_nsec_);
-}
-
-TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminalAtWildcard) {
- // Wildcard case, at actual wildcard
- findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_,
- ZoneFinder::RESULT_WILDCARD);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECSignedWithDNSSEC) {
- // NSEC-signed zone, requesting DNSSEC (NSEC should be provided)
- findCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::FIND_DNSSEC);
-}
-
-void
-InMemoryZoneFinderTest::emptyNodeCheck(
- ZoneFinder::FindResultFlags expected_flags)
-{
- /*
- * The backend RBTree for this test should look like as follows:
- * example.org
- * |
- * baz (empty; easy case)
- * / | \
- * bar | x.foo ('foo' part is empty; a bit trickier)
- * bbb
- * /
- * aaa
- */
-
- // Construct the test zone
- const char* const names[] = {
- "bar.example.org.", "x.foo.example.org.", "aaa.baz.example.org.",
- "bbb.baz.example.org.", NULL};
- for (int i = 0; names[i] != NULL; ++i) {
- ConstRRsetPtr rrset = textToRRset(string(names[i]) +
- " 300 IN A 192.0.2.1");
- EXPECT_EQ(SUCCESS, zone_finder_.add(rrset));
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
- }
-
- // empty node matching, easy case: the node for 'baz' exists with
- // no data.
- findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags);
-
- // empty node matching, a trickier case: the node for 'foo' is part of
- // "x.foo", which should be considered an empty node.
- findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags);
-
- // "org" is contained in "example.org", but it shouldn't be treated as
- // NXRRSET because it's out of zone.
- // Note: basically we don't expect such a query to be performed (the common
- // operation is to identify the best matching zone first then perform
- // search it), but we shouldn't be confused even in the unexpected case.
- EXPECT_THROW(zone_finder_.find(Name("org"), RRType::A()), OutOfZone);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNode) {
- emptyNodeCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
- emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) {
- emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, load) {
- // Put some data inside the zone
- EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_finder_.add(rr_ns_)));
- // Loading with different origin should fail
- EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/root.zone"),
- MasterLoadError);
- // See the original data is still there, survived the exception
- findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
- // Create correct zone
- InMemoryZoneFinder rootzone(class_, Name("."));
- // Try putting something inside
- EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, rootzone.add(rr_ns_aaaa_)));
- // Load the zone. It should overwrite/remove the above RRset
- EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone"));
-
- // Now see there are some rrsets (we don't look inside, though)
- findTest(Name("."), RRType::SOA(), ZoneFinder::SUCCESS, false,
- ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &rootzone);
- findTest(Name("."), RRType::NS(), ZoneFinder::SUCCESS, false,
- ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &rootzone);
- findTest(Name("a.root-servers.net."), RRType::A(), ZoneFinder::SUCCESS,
- false, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &rootzone);
- // But this should no longer be here
- findTest(rr_ns_a_->getName(), RRType::AAAA(), ZoneFinder::NXDOMAIN, true,
- ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &rootzone);
-
- // Try loading zone that is wrong in a different way
- EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
- MasterLoadError);
-}
-
-TEST_F(InMemoryZoneFinderTest, loadFromIterator) {
- // The initial test set doesn't have SOA at the apex.
- findTest(origin_, RRType::SOA(), ZoneFinder::NXRRSET, false,
- ConstRRsetPtr());
-
- // The content of the new version of zone to be first installed to
- // the SQLite3 data source, then to in-memory via SQLite3. The data are
- // chosen to cover major check points of the implementation:
- // - the previously non-existent record is added (SOA)
- // - An RRSIG is given from the iterator before the RRset it covers
- // (RRSIG for SOA, because they are sorted by name then rrtype as text)
- // - An RRset containing multiple RRs (ns1/A)
- // - RRSIGs for different owner names
- stringstream ss;
- const char* const soa_txt = "example.org. 300 IN SOA . . 0 0 0 0 0\n";
- const char* const soa_sig_txt = "example.org. 300 IN RRSIG SOA 5 3 300 "
- "20000101000000 20000201000000 12345 example.org. FAKEFAKE\n";
- const char* const a_txt =
- "ns1.example.org. 300 IN A 192.0.2.1\n"
- "ns1.example.org. 300 IN A 192.0.2.2\n";
- const char* const a_sig_txt = "ns1.example.org. 300 IN RRSIG A 5 3 300 "
- "20000101000000 20000201000000 12345 example.org. FAKEFAKE\n";
- ss << soa_txt << soa_sig_txt << a_txt << a_sig_txt;
- shared_ptr<DataSourceClient> db_client = unittest::createSQLite3Client(
- class_, origin_, TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied", ss);
- zone_finder_.load(*db_client->getIterator(origin_));
-
- // The new content should be visible, including the previously-nonexistent
- // SOA.
- RRsetPtr expected_answer = textToRRset(soa_txt, RRClass::IN(), origin_);
- expected_answer->addRRsig(textToRRset(soa_sig_txt));
- findTest(origin_, RRType::SOA(), ZoneFinder::SUCCESS, true,
- expected_answer, ZoneFinder::RESULT_DEFAULT, NULL,
- ZoneFinder::FIND_DNSSEC);
-
- expected_answer = textToRRset(a_txt);
- expected_answer->addRRsig(textToRRset(a_sig_txt));
- findTest(Name("ns1.example.org"), RRType::A(), ZoneFinder::SUCCESS, true,
- expected_answer, ZoneFinder::RESULT_DEFAULT, NULL,
- ZoneFinder::FIND_DNSSEC);
-
- // File name should be (re)set to empty.
- EXPECT_TRUE(zone_finder_.getFileName().empty());
-
- // Loading the zone with an iterator separating RRs of the same RRset
- // will fail because the resulting sequence doesn't meet assumptions of
- // the (current) in-memory implementation.
- EXPECT_THROW(zone_finder_.load(*db_client->getIterator(origin_, true)),
- MasterLoadError);
-
- // Load the zone from a file that contains more realistic data (borrowed
- // from a different test). There's nothing special in this case for the
- // purpose of this test, so it should just succeed.
- db_client = unittest::createSQLite3Client(
- class_, origin_, TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied",
- TEST_DATA_DIR "/contexttest-almost-obsolete.zone");
- zone_finder_.load(*db_client->getIterator(origin_));
-
- // just checking a couple of RRs in the new version of zone.
- findTest(Name("mx1.example.org"), RRType::A(), ZoneFinder::SUCCESS, true,
- textToRRset("mx1.example.org. 3600 IN A 192.0.2.10"));
- findTest(Name("ns1.example.org"), RRType::AAAA(), ZoneFinder::SUCCESS,
- true, textToRRset("ns1.example.org. 3600 IN AAAA 2001:db8::1"));
-}
-
-/*
- * Test that puts a (simple) wildcard into the zone and checks we can
- * correctly find the data.
- */
-void
-InMemoryZoneFinderTest::wildcardCheck(
- ZoneFinder::FindResultFlags expected_flags,
- ZoneFinder::FindOptions find_options)
-{
- /*
- * example.org.
- * |
- * [cname]wild (not *.wild, should have wild mark)
- * |
- * *
- */
-
- // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags),
- // add RRSIGs to the records.
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 ||
- (expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- // Convenience shortcut. The RDATA is not really validatable, but
- // it doesn't matter for our tests.
- const char* const rrsig_common = "5 3 3600 "
- "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
-
- find_options = find_options | ZoneFinder::FIND_DNSSEC;
- rr_wild_->addRRsig(textToRRset("*.wild.example.org. 300 IN RRSIG A " +
- string(rrsig_common)));
- rr_cnamewild_->addRRsig(textToRRset("*.cnamewild.example.org. 300 IN "
- "RRSIG CNAME " +
- string(rrsig_common)));
- }
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cnamewild_));
- // If the zone is expected to be "signed" with NSEC3, add an NSEC3.
- // (the content of the NSEC3 shouldn't matter)
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
- }
-
- // Search at the parent. The parent will not have the A, but it will
- // be in the wildcard (so check the wildcard isn't matched at the parent)
- {
- SCOPED_TRACE("Search at parent");
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- findTest(Name("wild.example.org"), RRType::A(),
- ZoneFinder::NXRRSET, true, rr_nsec_, expected_flags,
- NULL, find_options);
- } else {
- findTest(Name("wild.example.org"), RRType::A(),
- ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
- expected_flags, NULL, find_options);
- }
- }
-
- // For the test setup of "NSEC-signed" zone, we might expect it will
- // be returned with a negative result, either because wildcard match is
- // disabled by the search option or because wildcard match is canceled
- // per protocol.
- ConstRRsetPtr expected_nsec; // by default it's NULL
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
- (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
- expected_nsec = rr_nsec_;
- }
- // Explicitly converting the following to const pointers; some compilers
- // would complain about mixed use of const and non const in ?: below.
- const ConstRRsetPtr rr_wild = rr_wild_;
- const ConstRRsetPtr rr_cnamewild = rr_cnamewild_;
-
- // Search the original name of wildcard
- {
- SCOPED_TRACE("Search directly at *");
- findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
- true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
- find_options);
- }
-
- // Below some of the test cases will normally result in a wildcard match;
- // if NO_WILDCARD is specified, it should result in NXDOMAIN instead,
- // and, when available and requested, the covering NSEC will be returned.
- // The following are shortcut parameters to unify these cases.
- const bool wild_ok = ((find_options & ZoneFinder::NO_WILDCARD) == 0);
- const ZoneFinder::FindResultFlags wild_expected_flags =
- wild_ok ? (ZoneFinder::RESULT_WILDCARD | expected_flags) :
- expected_flags;
-
- // Search "created" name.
- {
- SCOPED_TRACE("Search at created child");
- findTest(Name("a.wild.example.org"), RRType::A(),
- wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
- wild_ok ? rr_wild : expected_nsec,
- wild_expected_flags, NULL, find_options, wild_ok);
- }
-
- // Search name that has CNAME.
- {
- SCOPED_TRACE("Matching CNAME");
- findTest(Name("a.cnamewild.example.org"), RRType::A(),
- wild_ok ? ZoneFinder::CNAME : ZoneFinder::NXDOMAIN, false,
- wild_ok ? rr_cnamewild : expected_nsec,
- wild_expected_flags, NULL, find_options, wild_ok);
- }
-
- // Search another created name, this time little bit lower
- {
- SCOPED_TRACE("Search at created grand-child");
- findTest(Name("a.b.wild.example.org"), RRType::A(),
- wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
- wild_ok ? rr_wild : expected_nsec,
- wild_expected_flags, NULL, find_options, wild_ok);
- }
-
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_under_wild_));
- {
- SCOPED_TRACE("Search under non-wildcard");
- findTest(Name("bar.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
- }
-
- // Wildcard match, but no data. We add the additional NSEC at the wildcard
- // at this point so that it wouldn't break other tests above. Note also
- // that in the NO_WILDCARD case the resulting NSEC is the same. Ideally
- // we could use a more tricky setup so we can distinguish these cases,
- // but for this purpose it's not bad; what we'd like to test here is that
- // wildcard substitution doesn't happen for either case, and the
- // NO_WILDCARD effect itself can be checked by the result code (NXDOMAIN).
- ConstRRsetPtr expected_wild_nsec; // by default it's NULL
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_nsec_));
- expected_wild_nsec = rr_wild_nsec_;
- }
- {
- SCOPED_TRACE("Search at wildcard, no data");
- findTest(Name("a.wild.example.org"), RRType::AAAA(),
- wild_ok ? ZoneFinder::NXRRSET : ZoneFinder::NXDOMAIN, true,
- wild_ok ? expected_wild_nsec : expected_wild_nsec,
- wild_expected_flags, NULL, find_options);
- }
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcard) {
- // Normal case
- wildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcardNSEC3) {
- // Similar to the previous one, but the zone signed with NSEC3
- wildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcardNSEC) {
- // Similar to the previous one, but the zone is signed with NSEC
- wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithNSEC) {
- // Wildcard is disabled. In practice, this is used as part of query
- // processing for an NSEC-signed zone, so we test that case specifically.
- wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::NO_WILDCARD);
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithoutNSEC) {
- // Similar to the previous once, but check the behavior for a non signed
- // zone just in case.
- wildcardCheck(ZoneFinder::RESULT_DEFAULT, ZoneFinder::NO_WILDCARD);
-}
-
-/*
- * Test that we don't match a wildcard if we get under delegation.
- * By 4.3.3 of RFC1034:
- * "Wildcard RRs do not apply:
- * - When the query is in another zone. That is, delegation cancels
- * the wildcard defaults."
- */
-TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_wild_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_));
-
- {
- SCOPED_TRACE("Looking under delegation point");
- findTest(Name("a.child.example.org"), RRType::A(),
- ZoneFinder::DELEGATION, true, rr_child_ns_);
- }
-
- {
- SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
- findTest(Name("a.child.example.org"), RRType::A(),
- ZoneFinder::DELEGATION, true, rr_child_ns_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
- }
-}
-
-// Tests combination of wildcard and ANY.
-void
-InMemoryZoneFinderTest::anyWildcardCheck(
- ZoneFinder::FindResultFlags expected_flags)
-{
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
- }
-
- vector<ConstRRsetPtr> expected_sets;
-
- // First try directly the name (normal match)
- {
- SCOPED_TRACE("Asking directly for *");
- expected_sets.push_back(rr_wild_);
- findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
- expected_sets);
- }
-
- // Then a wildcard match
- {
- SCOPED_TRACE("Asking in the wild way");
- expected_sets.clear();
- RRsetPtr expected(new RRset(Name("a.wild.example.org"),
- rr_wild_->getClass(), rr_wild_->getType(),
- rr_wild_->getTTL()));
- expected->addRdata(rr_wild_->getRdataIterator()->getCurrent());
- expected_sets.push_back(expected);
- findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
- expected_sets,
- ZoneFinder::RESULT_WILDCARD | expected_flags);
- }
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcard) {
- anyWildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC3) {
- anyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC) {
- anyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Test there's nothing in the wildcard in the middle if we load
-// wild.*.foo.example.org.
-void
-InMemoryZoneFinderTest::emptyWildcardCheck(
- ZoneFinder::FindResultFlags expected_flags)
-{
- /*
- * example.org.
- * foo
- * *
- * wild
- */
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_emptywild_));
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
- }
-
- {
- SCOPED_TRACE("Asking for the original record under wildcard");
- findTest(Name("wild.*.foo.example.org"), RRType::A(),
- ZoneFinder::SUCCESS, true, rr_emptywild_);
- }
-
- {
- SCOPED_TRACE("Asking for A record");
- findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(),
- ZoneFinder::RESULT_WILDCARD | expected_flags);
- findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(), expected_flags);
- findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(), expected_flags);
- }
-
- {
- SCOPED_TRACE("Asking for ANY record");
- findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET,
- vector<ConstRRsetPtr>(), expected_flags);
-
- findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET,
- vector<ConstRRsetPtr>(),
- ZoneFinder::RESULT_WILDCARD | expected_flags);
- }
-
- {
- SCOPED_TRACE("Asking on the non-terminal");
- findTest(Name("wild.bar.foo.example.org"), RRType::A(),
- ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
- ZoneFinder::RESULT_WILDCARD | expected_flags);
- }
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
- emptyWildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC3) {
- emptyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC) {
- emptyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Same as emptyWildcard, but with multiple * in the path.
-TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nested_emptywild_));
-
- {
- SCOPED_TRACE("Asking for the original record under wildcards");
- findTest(Name("wild.*.foo.*.bar.example.org"), RRType::A(),
- ZoneFinder::SUCCESS, true, rr_nested_emptywild_);
- }
-
- {
- SCOPED_TRACE("Matching wildcard against empty nonterminal");
-
- const char* names[] = {
- "baz.foo.*.bar.example.org",
- "baz.foo.baz.bar.example.org",
- "*.foo.baz.bar.example.org",
- NULL
- };
-
- for (const char** name = names; *name != NULL; ++ name) {
- SCOPED_TRACE(string("Node ") + *name);
- findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), ZoneFinder::RESULT_WILDCARD);
- }
- }
-
- // Domains to test
- const char* names[] = {
- "*.foo.*.bar.example.org",
- "foo.*.bar.example.org",
- "*.bar.example.org",
- "bar.example.org",
- NULL
- };
-
- {
- SCOPED_TRACE("Asking directly for A on parent nodes");
-
- for (const char** name = names; *name != NULL; ++ name) {
- SCOPED_TRACE(string("Node ") + *name);
- findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET);
- }
- }
-
- {
- SCOPED_TRACE("Asking for ANY on parent nodes");
-
- for (const char** name = names; *name != NULL; ++ name) {
- SCOPED_TRACE(string("Node ") + *name);
-
- findAllTest(Name(*name), ZoneFinder::NXRRSET,
- vector<ConstRRsetPtr>());
- }
- }
-}
-
-// We run this part twice from the below test, in two slightly different
-// situations
-void
-InMemoryZoneFinderTest::doCancelWildcardCheck(
- ZoneFinder::FindResultFlags expected_flags,
- ZoneFinder::FindOptions find_options)
-{
- // These should be canceled
- {
- SCOPED_TRACE("Canceled under foo.wild.example.org");
-
- // For an NSEC-signed zone with DNSSEC requested, the covering NSEC
- // should be returned. The expected NSEC is actually just the only
- // NSEC in the test data, but in this context it doesn't matter;
- // it's sufficient just to check any NSEC is returned (or not).
- ConstRRsetPtr expected_nsec; // by default it's NULL
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
- (find_options & ZoneFinder::FIND_DNSSEC)) {
- expected_nsec = rr_nsec_;
- }
-
- findTest(Name("aaa.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
- findTest(Name("zzz.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
- }
-
- // This is existing, non-wildcard domain, shouldn't wildcard at all
- {
- SCOPED_TRACE("Existing domain under foo.wild.example.org");
- findTest(Name("bar.foo.wild.example.org"), RRType::A(),
- ZoneFinder::SUCCESS, true, rr_not_wild_);
- }
-
- // These should be caught by the wildcard
- {
- SCOPED_TRACE("Neighbor wildcards to foo.wild.example.org");
-
- const char* names[] = {
- "aaa.bbb.wild.example.org",
- "aaa.zzz.wild.example.org",
- "zzz.wild.example.org",
- NULL
- };
-
- for (const char** name = names; *name != NULL; ++ name) {
- SCOPED_TRACE(string("Node ") + *name);
-
- findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false,
- rr_wild_,
- ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
- ZoneFinder::FIND_DEFAULT, true);
- }
- }
-
- // This shouldn't be wildcarded, it's an existing domain
- {
- SCOPED_TRACE("The foo.wild.example.org itself");
- findTest(Name("foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXRRSET, true, ConstRRsetPtr(), expected_flags);
- }
-}
-
-/*
- * This tests that if there's a name between the wildcard domain and the
- * searched one, it will not trigger wildcard, for example, if we have
- * *.wild.example.org and bar.foo.wild.example.org, then we know
- * foo.wild.example.org exists and is not wildcard. Therefore, search for
- * aaa.foo.wild.example.org should return NXDOMAIN.
- *
- * Tests few cases "around" the canceled wildcard match, to see something that
- * shouldn't be canceled isn't.
- */
-TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_));
-
- {
- SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
- doCancelWildcardCheck();
- }
-
- // Try putting another one under foo.wild....
- // The result should be the same but it will be done in another way in the
- // code, because the foo.wild.example.org will exist in the tree.
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_another_));
- {
- SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
- doCancelWildcardCheck();
- }
-}
-
-// Same tests as cancelWildcard for NSEC3-signed zone
-TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
-
- {
- SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
- }
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_another_));
- {
- SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
- }
-}
-
-// Same tests as cancelWildcard for NSEC-signed zone. Check both cases with
-// or without FIND_DNSSEC option. NSEC should be returned only when the option
-// is given.
-TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_));
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
-
- {
- SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
- ZoneFinder::FIND_DNSSEC);
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
- }
- EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_another_));
- {
- SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
- ZoneFinder::FIND_DNSSEC);
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
- }
-}
-
-TEST_F(InMemoryZoneFinderTest, loadBadWildcard) {
- // We reject loading the zone if it contains a wildcard name for
- // NS or DNAME.
- EXPECT_THROW(zone_finder_.add(rr_nswild_), InMemoryZoneFinder::AddError);
- EXPECT_THROW(zone_finder_.add(rr_dnamewild_),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, swap) {
- // build one zone finder with some data
- InMemoryZoneFinder finder1(class_, origin_);
- EXPECT_EQ(result::SUCCESS, finder1.add(rr_ns_));
- EXPECT_EQ(result::SUCCESS, finder1.add(rr_ns_aaaa_));
-
- // build another zone finder of a different RR class with some other data
- const Name other_origin("version.bind");
- ASSERT_NE(origin_, other_origin); // make sure these two are different
- InMemoryZoneFinder finder2(RRClass::CH(), other_origin);
- EXPECT_EQ(result::SUCCESS,
- finder2.add(textToRRset("version.bind. 0 CH TXT \"test\"",
- RRClass::CH())));
-
- finder1.swap(finder2);
- EXPECT_EQ(other_origin, finder1.getOrigin());
- EXPECT_EQ(origin_, finder2.getOrigin());
- EXPECT_EQ(RRClass::CH(), finder1.getClass());
- EXPECT_EQ(RRClass::IN(), finder2.getClass());
- // make sure the zone data is swapped, too
- EXPECT_THROW(finder1.find(origin_, RRType::NS()), OutOfZone);
- findTest(other_origin, RRType::TXT(), ZoneFinder::SUCCESS, false,
- ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder1);
- findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, false,
- ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder2);
- EXPECT_THROW(finder2.find(other_origin, RRType::TXT()), OutOfZone);
-}
-
-TEST_F(InMemoryZoneFinderTest, getFileName) {
- // for an empty zone the file name should also be empty.
- EXPECT_TRUE(zone_finder_.getFileName().empty());
-
- // if loading a zone fails the file name shouldn't be set.
- EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/root.zone"),
- MasterLoadError);
- EXPECT_TRUE(zone_finder_.getFileName().empty());
-
- // after a successful load, the specified file name should be set
- InMemoryZoneFinder rootzone(class_, Name("."));
- EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone"));
- EXPECT_EQ(TEST_DATA_DIR "/root.zone", rootzone.getFileName());
- // overriding load, which will fail
- EXPECT_THROW(rootzone.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
- MasterLoadError);
- // the file name should be intact.
- EXPECT_EQ(TEST_DATA_DIR "/root.zone", rootzone.getFileName());
-
- // After swap, file names should also be swapped.
- zone_finder_.swap(rootzone);
- EXPECT_EQ(TEST_DATA_DIR "/root.zone", zone_finder_.getFileName());
- EXPECT_TRUE(rootzone.getFileName().empty());
-}
-
-TEST_F(InMemoryZoneFinderTest, addRRsig) {
- // A simple valid case: adding an RRset to be signed followed by an RRSIG
- // that covers the first RRset
- zone_finder_.add(rr_a_);
- zone_finder_.add(textToRRset(rrsig_a_txt));
- ZoneFinderContextPtr result = zone_finder_.find(origin_, RRType::A(),
- ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
- ASSERT_TRUE(result->rrset);
- ASSERT_TRUE(result->rrset->getRRsig());
- actual_rrsets_.push_back(result->rrset->getRRsig());
- rrsetsCheck(rrsig_a_txt, actual_rrsets_.begin(), actual_rrsets_.end());
-
- // Confirm a separate RRISG for a different type can be added
- actual_rrsets_.clear();
- zone_finder_.add(rr_ns_);
- zone_finder_.add(textToRRset(rrsig_ns_txt));
- result = zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
- ASSERT_TRUE(result->rrset);
- ASSERT_TRUE(result->rrset->getRRsig());
- actual_rrsets_.push_back(result->rrset->getRRsig());
- rrsetsCheck(rrsig_ns_txt, actual_rrsets_.begin(), actual_rrsets_.end());
-
- // Check a case with multiple RRSIGs
- actual_rrsets_.clear();
- zone_finder_.add(rr_ns_aaaa_);
- zone_finder_.add(textToRRset(rrsig_aaaa_txt));
- result = zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
- ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
- ASSERT_TRUE(result->rrset);
- ASSERT_TRUE(result->rrset->getRRsig());
- actual_rrsets_.push_back(result->rrset->getRRsig());
- rrsetsCheck(rrsig_aaaa_txt, actual_rrsets_.begin(), actual_rrsets_.end());
-}
-
-TEST_F(InMemoryZoneFinderTest, addRRsigWithoutCovered) {
- // The current implementation rejects attempts of adding RRSIG without
- // covered RRsets already in the zone.
-
- // Name doesn't exist
- EXPECT_THROW(zone_finder_.add(
- textToRRset("notexist.example.org. 300 IN RRSIG A 5 3 "
- "3600 20000101000000 20000201000000 12345 "
- "example.org. FAKEFAKEFAKE\n")),
- InMemoryZoneFinder::AddError);
-
- // Name exists, but is empty.
- zone_finder_.add(rr_emptywild_);
- EXPECT_THROW(zone_finder_.add(
- textToRRset("foo.example.org. 300 IN RRSIG A 5 3 "
- "3600 20000101000000 20000201000000 12345 "
- "example.org. FAKEFAKEFAKE\n")),
- InMemoryZoneFinder::AddError);
-
- // Add RRSIG RRset without covered RR
- zone_finder_.add(rr_a_);
- EXPECT_THROW(zone_finder_.add(textToRRset(rrsig_ns_txt)),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, addbadRRsig) {
- // Tests with other types of bogus input
-
- // Empty RRSIG RRset.
- EXPECT_THROW(zone_finder_.add(RRsetPtr(new RRset(origin_, class_,
- RRType::RRSIG(),
- RRTTL(300)))),
- InMemoryZoneFinder::AddError);
-
- // RRSIG with mixed covered types
- zone_finder_.add(rr_a_); // make sure the covered name exists
- // textToRRset() doesn't work as intended for this pathological case,
- // so we need to construct the RRset by hand.
- RRsetPtr rrset(new RRset(origin_, class_, RRType::RRSIG(), RRTTL(300)));
- rrset->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
- "12345 example.org. FAKEFAKEFAKE"));
- rrset->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
- "54321 example.org. FAKEFAKEFAKEFAKE"));
- EXPECT_THROW(zone_finder_.add(rrset), InMemoryZoneFinder::AddError);
-
- // An attempt of overriding an existing RRSIG. The current implementation
- // prohibits that.
- zone_finder_.add(textToRRset(rrsig_a_txt));
- EXPECT_THROW(zone_finder_.add(textToRRset(rrsig_a_txt)),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, addNSEC3) {
- // Set up the faked hash calculator.
- setNSEC3HashCreator(&nsec3_hash_creator_);
-
- const string nsec3_text = string(apex_hash) + ".example.org." +
- string(nsec3_common);
- // This name shouldn't be found in the normal domain tree.
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
- EXPECT_EQ(ZoneFinder::NXDOMAIN,
- zone_finder_.find(Name(string(apex_hash) + ".example.org"),
- RRType::NSEC3())->code);
- // Dedicated NSEC3 find should be able to find it.
- findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
- zone_finder_.findNSEC3(Name("example.org"), false));
-
- // This implementation rejects duplicate/update add of the same hash name
- EXPECT_EQ(result::EXIST,
- zone_finder_.add(textToRRset(
- string(apex_hash) + ".example.org." +
- string(nsec3_common) + " AAAA")));
- // The original NSEC3 should be intact
- findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
- zone_finder_.findNSEC3(Name("example.org"), false));
-
- // NSEC3-like name but of ordinary RR type should go to normal tree.
- const string nonsec3_text = string(apex_hash) + ".example.org. " +
- "300 IN A 192.0.2.1";
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nonsec3_text)));
- EXPECT_EQ(ZoneFinder::SUCCESS,
- zone_finder_.find(Name(string(apex_hash) + ".example.org"),
- RRType::A())->code);
-}
-
-TEST_F(InMemoryZoneFinderTest, addNSEC3Lower) {
- // Set up the faked hash calculator.
- setNSEC3HashCreator(&nsec3_hash_creator_);
-
- // Similar to the previous case, but NSEC3 owner name is lower-cased.
- const string nsec3_text = string(apex_hash_lower) + ".example.org." +
- string(nsec3_common);
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
- findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
- zone_finder_.findNSEC3(Name("example.org"), false));
-}
-
-TEST_F(InMemoryZoneFinderTest, addNSEC3Ordering) {
- // Set up the faked hash calculator.
- setNSEC3HashCreator(&nsec3_hash_creator_);
-
- // Check that the internal storage ensures comparison based on the NSEC3
- // semantics, regardless of the add order or the letter-case of hash.
-
- // Adding "0P..", "2v..", then "2T..".
- const string smallest = string(apex_hash) + ".example.org." +
- string(nsec3_common);
- const string middle = string(ns1_hash) + ".example.org." +
- string(nsec3_common);
- const string largest = string(xyw_hash) + ".example.org." +
- string(nsec3_common);
- zone_finder_.add(textToRRset(smallest));
- zone_finder_.add(textToRRset(largest));
- zone_finder_.add(textToRRset(middle));
-
- // Then look for NSEC3 that covers a name whose hash is "2S.."
- // The covering NSEC3 should be "0P.."
- findNSEC3Check(false, 4, smallest, "",
- zone_finder_.findNSEC3(Name("www.example.org"), false));
-
- // Look for NSEC3 that covers names whose hash are "Q0.." and "0A.."
- // The covering NSEC3 should be "2v.." in both cases
- findNSEC3Check(false, 4, largest, "",
- zone_finder_.findNSEC3(Name("xxx.example.org"), false));
- findNSEC3Check(false, 4, largest, "",
- zone_finder_.findNSEC3(Name("yyy.example.org"), false));
-}
-
-TEST_F(InMemoryZoneFinderTest, badNSEC3Name) {
- // Our implementation refuses to load NSEC3 at a wildcard name
- EXPECT_THROW(zone_finder_.add(textToRRset("*.example.org." +
- string(nsec3_common))),
- InMemoryZoneFinder::AddError);
-
- // Likewise, if the owner name of NSEC3 has too many labels, it's refused.
- EXPECT_THROW(zone_finder_.add(textToRRset("a." + string(apex_hash) +
- ".example.org." +
- string(nsec3_common))),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, addMultiNSEC3) {
- // In this current implementation multiple NSEC3 RDATA isn't supported.
- RRsetPtr nsec3(new RRset(Name(string(apex_hash) + ".example.org"),
- RRClass::IN(), RRType::NSEC3(), RRTTL(300)));
- nsec3->addRdata(
- generic::NSEC3("1 0 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A"));
- nsec3->addRdata(
- generic::NSEC3("1 1 1 ddccbbaa 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A"));
- EXPECT_THROW(zone_finder_.add(nsec3), InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, addNSEC3WithRRSIG) {
- // Set up the faked hash calculator.
- setNSEC3HashCreator(&nsec3_hash_creator_);
-
- // Adding NSEC3 and its RRSIG
- const string nsec3_text = string(apex_hash) + ".example.org." +
- string(nsec3_common);
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
- const string nsec3_rrsig_text = string(apex_hash) + ".example.org." +
- string(nsec3_rrsig_common);
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_rrsig_text)));
-
- // Then look for it. The NSEC3 should have the RRSIG that was just added.
- findNSEC3Check(true, origin_.getLabelCount(),
- nsec3_text + "\n" + nsec3_rrsig_text, "",
- zone_finder_.findNSEC3(Name("example.org"), false));
-
- // Duplicate add of RRSIG for the same NSEC3 is prohibited.
- EXPECT_THROW(zone_finder_.add(textToRRset(nsec3_rrsig_text)),
- InMemoryZoneFinder::AddError);
-
- // Same check using the lower-cased name. This also confirms matching
- // is case-insensitive.
- EXPECT_THROW(zone_finder_.add(textToRRset(string(apex_hash_lower) +
- ".example.org."
- + string(nsec3_rrsig_common))),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, badRRsigForNSEC3) {
- // adding RRSIG for NSEC3 even before adding any NSEC3 (internally,
- // a space for NSEC3 namespace isn't yet allocated)
- EXPECT_THROW(zone_finder_.add(textToRRset(string(apex_hash) +
- ".example.org." +
- string(nsec3_rrsig_common))),
- InMemoryZoneFinder::AddError);
-
- // Add an NSEC3
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(
- textToRRset(string(apex_hash) + ".example.org." +
- string(nsec3_common))));
-
- // Then add an NSEC3 for a non existent NSEC3. It should fail in the
- // current implementation.
- EXPECT_THROW(zone_finder_.add(textToRRset(string(ns1_hash) +
- ".example.org." +
- string(nsec3_rrsig_common))),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, paramConsistencyWithNSEC3PARAM) {
- // First, add an NSEC3PARAM RR
- EXPECT_EQ(result::SUCCESS,
- zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
- "1 0 12 aabbccdd")));
- // Adding an NSEC3 that has matching parameters is okay.
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(
- textToRRset(string(apex_hash) + ".example.org." +
- string(nsec3_common))));
- // NSEC3 with inconsistent parameter will be rejected
- EXPECT_THROW(zone_finder_.add(
- textToRRset("a.example.org. 300 IN NSEC3 1 0 1 aabbccdd "
- "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG")),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, paramConsistencyWithNSEC3) {
- // Add an NSEC3 without adding NSEC3PARAM
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(
- textToRRset(string(apex_hash) + ".example.org." +
- string(nsec3_common))));
- // Adding an NSEC3 with inconsistent parameter will be rejected at this pt.
- EXPECT_THROW(zone_finder_.add(
- textToRRset("a.example.org. 300 IN NSEC3 1 0 1 aabbccdd "
- "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG")),
- InMemoryZoneFinder::AddError);
-
- // Likewise, NSEC3PARAM with inconsistent parameter will be rejected.
- EXPECT_THROW(zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
- "1 0 1 aabbccdd")),
- InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, multiNSEC3PARAM) {
- // In this current implementation multiple NSEC3PARAM isn't supported.
- RRsetPtr nsec3param(new RRset(Name("example.org"), RRClass::IN(),
- RRType::NSEC3PARAM(), RRTTL(300)));
- nsec3param->addRdata(generic::NSEC3PARAM("1 0 12 aabbccdd"));
- nsec3param->addRdata(generic::NSEC3PARAM("1 1 1 ddccbbaa"));
- EXPECT_THROW(zone_finder_.add(nsec3param), InMemoryZoneFinder::AddError);
-}
-
-TEST_F(InMemoryZoneFinderTest, nonOriginNSEC3PARAM) {
- // This is a normal NSEC3PARAM at the zone origin
- EXPECT_EQ(result::SUCCESS,
- zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
- "1 0 12 aabbccdd")));
- // Add another (with different param) at a non origin node. This is
- // awkward, but the implementation accepts it as an ordinary RR.
- EXPECT_EQ(result::SUCCESS,
- zone_finder_.add(textToRRset("a.example.org. 300 IN NSEC3PARAM "
- "1 1 1 aabbccdd")));
-}
-
-TEST_F(InMemoryZoneFinderTest, loadNSEC3Zone) {
- // Check if it can load validly NSEC3-signed zone. At this moment
- // it's sufficient to see it doesn't crash
- zone_finder_.load(TEST_DATA_DIR "/example.org.nsec3-signed");
-
- // Reload the zone with a version that doesn't have NSEC3PARAM.
- // This is an abnormal case, but the implementation accepts it.
- zone_finder_.load(TEST_DATA_DIR "/example.org.nsec3-signed-noparam");
-}
-
-// This test checks that the NSEC3 names don't really exist in the real
-// namespace.
-TEST_F(InMemoryZoneFinderTest, queryToNSEC3Name) {
- // Add the NSEC3 and NSEC3PARAM there.
- EXPECT_EQ(result::SUCCESS,
- zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
- "1 0 12 aabbccdd")));
- const Name nsec3domain(string(apex_hash) + ".example.org.");
- // Adding an NSEC3 that has matching parameters is okay.
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(
- textToRRset(string(apex_hash) + ".example.org." +
- string(nsec3_common))));
- // Now, the domain should not exist
- findTest(nsec3domain, RRType::AAAA(), ZoneFinder::NXDOMAIN, false,
- ConstRRsetPtr(), ZoneFinder::RESULT_NSEC3_SIGNED, &zone_finder_,
- ZoneFinder::FIND_DNSSEC);
- // If we add an A record, the domain should exist
- ConstRRsetPtr rrset(textToRRset(string(apex_hash) +
- ".example.org. 300 IN A 192.0.2.1"));
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(rrset));
- // Searching for a different RRType will tell us this RRset doesn't exist
- findTest(nsec3domain, RRType::AAAA(), ZoneFinder::NXRRSET, false,
- ConstRRsetPtr(), ZoneFinder::RESULT_NSEC3_SIGNED, &zone_finder_,
- ZoneFinder::FIND_DNSSEC);
- // Searching for the A record would find it
- findTest(nsec3domain, RRType::A(), ZoneFinder::SUCCESS, true,
- rrset, ZoneFinder::RESULT_DEFAULT, &zone_finder_,
- ZoneFinder::FIND_DNSSEC);
-}
-
-// Continuation of the previous test (queryToNSEC3Name), we check we don't break
-// the empty nonterminal case by existence of NSEC3 record with that name.
-TEST_F(InMemoryZoneFinderTest, queryToNSEC3NameNonterminal) {
- // Add the NSEC3 and NSEC3PARAM there.
- EXPECT_EQ(result::SUCCESS,
- zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
- "1 0 12 aabbccdd")));
- const Name nsec3domain(string(apex_hash) + ".example.org.");
- // Adding an NSEC3 that has matching parameters is okay.
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(
- textToRRset(string(apex_hash) + ".example.org." +
- string(nsec3_common))));
- // Something below the name
- ConstRRsetPtr rrset(textToRRset("below." + string(apex_hash) +
- ".example.org. 300 IN A 192.0.2.1"));
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(rrset));
- // Now, the node is empty non-terminal.
- findTest(nsec3domain, RRType::AAAA(), ZoneFinder::NXRRSET, false,
- ConstRRsetPtr(), ZoneFinder::RESULT_NSEC3_SIGNED, &zone_finder_,
- ZoneFinder::FIND_DNSSEC);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSEC3) {
- // Set up the faked hash calculator.
- setNSEC3HashCreator(&nsec3_hash_creator_);
-
- // Add a few NSEC3 records:
- // apex (example.org.): hash=0P..
- // ns1.example.org: hash=2T..
- // w.example.org: hash=01..
- // zzz.example.org: hash=R5..
- const string apex_nsec3_text = string(apex_hash) + ".example.org." +
- string(nsec3_common);
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(apex_nsec3_text)));
- const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
- string(nsec3_common);
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(ns1_nsec3_text)));
- const string w_nsec3_text = string(w_hash) + ".example.org." +
- string(nsec3_common);
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(w_nsec3_text)));
- const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
- string(nsec3_common);
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(zzz_nsec3_text)));
-
- performNSEC3Test(zone_finder_);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {
- // Set up the faked hash calculator.
- setNSEC3HashCreator(&nsec3_hash_creator_);
-
- // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
- // findNSEC3() should be rejected.
- EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
- DataSourceError);
-
- // Only having NSEC3PARAM isn't enough
- EXPECT_EQ(result::SUCCESS,
- zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
- "1 0 12 aabbccdd")));
- EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
- DataSourceError);
-
- // Unless NSEC3 for apex is added the result in the recursive mode
- // is guaranteed.
- const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
- string(nsec3_common);
- EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(ns1_nsec3_text)));
- EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
- DataSourceError);
-}
-
-TEST_F(InMemoryZoneFinderTest, loadAndFindNSEC3) {
- // Using more realistic example, borrowed from RFC5155, with compliant
- // hash calculator. We only confirm the data source can load it
- // successfully and find correct NSEC3 RRs for some selected cases
- // (detailed tests have been done above).
-
- InMemoryZoneFinder finder(class_, Name("example"));
- finder.load(TEST_DATA_COMMONDIR "/rfc5155-example.zone.signed");
-
- // See RFC5155 B.1
- ZoneFinder::FindNSEC3Result result1 =
- finder.findNSEC3(Name("c.x.w.example"), true);
- ASSERT_TRUE(result1.closest_proof);
- // We compare closest_labels as int so the error report will be more
- // readable in case it fails.
- EXPECT_EQ(4, static_cast<int>(result1.closest_labels));
- EXPECT_EQ(Name("b4um86eghhds6nea196smvmlo4ors995.example"),
- result1.closest_proof->getName());
- ASSERT_TRUE(result1.next_proof);
- EXPECT_EQ(Name("0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example"),
- result1.next_proof->getName());
-
- // See RFC5155 B.2.
- ZoneFinder::FindNSEC3Result result2 =
- finder.findNSEC3(Name("ns1.example"), true);
- ASSERT_TRUE(result2.closest_proof);
- EXPECT_EQ(3, static_cast<int>(result2.closest_labels));
- EXPECT_EQ(Name("2t7b4g4vsa5smi47k61mv5bv1a22bojr.example"),
- result2.closest_proof->getName());
- ASSERT_FALSE(result2.next_proof);
-
- // See RFC5155 B.5.
- ZoneFinder::FindNSEC3Result result3 =
- finder.findNSEC3(Name("a.z.w.example"), true);
- ASSERT_TRUE(result3.closest_proof);
- EXPECT_EQ(3, static_cast<int>(result3.closest_labels));
- EXPECT_EQ(Name("k8udemvp1j2f7eg6jebps17vp3n8i58h.example"),
- result3.closest_proof->getName());
- ASSERT_TRUE(result3.next_proof);
- EXPECT_EQ(Name("q04jkcevqvmu85r014c7dkba38o0ji5r.example"),
- result3.next_proof->getName());
-}
-}
+++ /dev/null
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-#include <dns/rdataclass.h>
-#include <datasrc/rbnode_rrset.h>
-#include <testutils/dnsmessage_test.h>
-
-#include <dns/tests/unittest_util.h>
-
-#include <gtest/gtest.h>
-
-#include <sstream>
-#include <stdexcept>
-
-using isc::UnitTestUtil;
-
-using namespace isc;
-using namespace isc::datasrc;
-using namespace isc::datasrc::internal;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::testutils;
-using namespace isc::util;
-using namespace std;
-
-// These tests are very similar to those for RRset - indeed, this file was
-// created from those tests. However, the significant difference in behaviour
-// between RRset and RBNodeRRset - that the "set" methods in the latter mostly
-// result in exceptions being thrown - preclude use of full type
-// parameterisation of the tests.
-
-namespace {
-const char* const RRSIG_TXT =
- "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
- "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
- "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
- "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
- "f49t+sXKPzbipN9g+s1ZPiIyofc=";
-
-class RBNodeRRsetTest : public ::testing::Test {
-protected:
- RBNodeRRsetTest() :
- test_name("test.example.com"),
- test_domain("example.com"),
- test_nsname("ns.example.com"),
- rrset_a(ConstRRsetPtr(new RRset(
- test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
- rrset_a_empty(ConstRRsetPtr(new RRset(
- test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
- rrset_ns(ConstRRsetPtr(new RRset(
- test_domain, RRClass::IN(), RRType::NS(), RRTTL(86400)))),
- rrset_ch_txt(ConstRRsetPtr(new RRset(
- test_domain, RRClass::CH(), RRType::TXT(), RRTTL(0)))),
- rrset_siga(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
- RRTTL(3600)))
-
- {
- // Add a couple of Rdata elements to the A RRset. The easiest way to
- // do this is to override the "const" restrictions. As this is a test,
- // we don't feel too bad about doing so.
- AbstractRRset* a_rrset =
- const_cast<AbstractRRset*>(rrset_a.getUnderlyingRRset().get());
- a_rrset->addRdata(in::A("192.0.2.1"));
- a_rrset->addRdata(in::A("192.0.2.2"));
-
- // Create the RRSIG corresponding to the rrset_a record. The RDATA
- // won't match the A record it covers, although it is internally
- // self-consistent.
- AbstractRRset* sig_rrset =
- const_cast<AbstractRRset*>(rrset_siga.get());
- sig_rrset->addRdata(generic::RRSIG(RRSIG_TXT));
- }
-
- const Name test_name;
- const Name test_domain;
- const Name test_nsname;
-
- RBNodeRRset rrset_a;
- RBNodeRRset rrset_a_empty;
- const RBNodeRRset rrset_ns;
- const RBNodeRRset rrset_ch_txt;
-
- ConstRRsetPtr rrset_siga;
-};
-
-TEST_F(RBNodeRRsetTest, getRdataCount) {
- EXPECT_EQ(0, rrset_a_empty.getRdataCount());
- EXPECT_EQ(2, rrset_a.getRdataCount());
-}
-
-TEST_F(RBNodeRRsetTest, getName) {
- EXPECT_EQ(test_name, rrset_a.getName());
- EXPECT_EQ(test_domain, rrset_ns.getName());
-}
-
-TEST_F(RBNodeRRsetTest, getClass) {
- EXPECT_EQ(RRClass("IN"), rrset_a.getClass());
- EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass());
-}
-
-TEST_F(RBNodeRRsetTest, getType) {
- EXPECT_EQ(RRType("A"), rrset_a.getType());
- EXPECT_EQ(RRType("NS"), rrset_ns.getType());
- EXPECT_EQ(RRType("TXT"), rrset_ch_txt.getType());
-}
-
-TEST_F(RBNodeRRsetTest, getTTL) {
- EXPECT_EQ(RRTTL(3600), rrset_a.getTTL());
- EXPECT_EQ(RRTTL(86400), rrset_ns.getTTL());
- EXPECT_EQ(RRTTL(0), rrset_ch_txt.getTTL());
-}
-
-TEST_F(RBNodeRRsetTest, setName) {
- EXPECT_THROW(rrset_a.setName(test_nsname), NotImplemented);
-}
-
-TEST_F(RBNodeRRsetTest, setTTL) {
- EXPECT_THROW(rrset_a.setTTL(RRTTL(86400)), NotImplemented);
-}
-
-TEST_F(RBNodeRRsetTest, toText) {
- EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
- "test.example.com. 3600 IN A 192.0.2.2\n",
- rrset_a.toText());
-
- // toText() cannot be performed for an empty RRset.
- EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
-}
-
-TEST_F(RBNodeRRsetTest, isSameKind) {
- RBNodeRRset rrset_p(ConstRRsetPtr(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(3600))));
- RBNodeRRset rrset_q(ConstRRsetPtr(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(3600))));
- RRset rrset_w(test_name, RRClass::IN(), RRType::A(), RRTTL(3600));
- RRset rrset_x(test_nsname, RRClass::IN(), RRType::A(), RRTTL(3600));
- RRset rrset_y(test_name, RRClass::IN(), RRType::NS(), RRTTL(3600));
- RRset rrset_z(test_name, RRClass::CH(), RRType::A(), RRTTL(3600));
-
- EXPECT_TRUE(rrset_p.isSameKind(rrset_p));
- EXPECT_FALSE(rrset_p.isSameKind(rrset_q));
-
- EXPECT_TRUE(rrset_p.isSameKind(rrset_w));
- EXPECT_FALSE(rrset_p.isSameKind(rrset_x));
- EXPECT_FALSE(rrset_p.isSameKind(rrset_y));
- EXPECT_FALSE(rrset_p.isSameKind(rrset_z));
-}
-
-// Note: although the next two tests are essentially the same and used common
-// test code, they use different test data: the MessageRenderer produces
-// compressed wire data whereas the OutputBuffer does not.
-
-template <typename T>
-void
-performToWireTest(T& dataHolder, const RBNodeRRset& rrset,
- const RBNodeRRset& rrset_empty, const char* testdata)
-{
- rrset.toWire(dataHolder);
-
- std::vector<unsigned char> wiredata;
- UnitTestUtil::readWireData(testdata, wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, dataHolder.getData(),
- dataHolder.getLength(), &wiredata[0], wiredata.size());
-
- // toWire() cannot be performed for an empty RRset.
- dataHolder.clear();
- EXPECT_THROW(rrset_empty.toWire(dataHolder), EmptyRRset);
-}
-
-TEST_F(RBNodeRRsetTest, toWireRenderer) {
- MessageRenderer renderer;
- performToWireTest(renderer, rrset_a, rrset_a_empty, "rrset_toWire2");
-}
-
-TEST_F(RBNodeRRsetTest, toWireBuffer) {
- OutputBuffer buffer(0);
- performToWireTest(buffer, rrset_a, rrset_a_empty, "rrset_toWire1");
-}
-
-TEST_F(RBNodeRRsetTest, addRdata) {
- EXPECT_THROW(rrset_a.addRdata(in::A("192.0.2.3")), NotImplemented);
-
- // Check the same goes for trying to add the wrong type of data
- EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), NotImplemented);
-}
-
-TEST_F(RBNodeRRsetTest, addRdataPtr) {
- EXPECT_THROW(rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(),
- rrset_a_empty.getClass(),
- "192.0.2.1")),
- NotImplemented);
-}
-
-TEST_F(RBNodeRRsetTest, getRDataIterator) {
- RdataIteratorPtr it = rrset_a.getRdataIterator();
- for (int i = 0; i < 2; ++i) {
- ASSERT_FALSE(it->isLast());
- ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1")));
-
- it->next();
- ASSERT_FALSE(it->isLast());
- ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2")));
-
- it->next();
- ASSERT_TRUE(it->isLast());
-
- // Should be able repeat the iteration by calling first().
- it->first();
- }
-}
-
-// test operator<<. We simply confirm it appends the result of toText().
-TEST_F(RBNodeRRsetTest, LeftShiftOperator) {
- ostringstream oss;
- oss << rrset_a;
- EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
- "test.example.com. 3600 IN A 192.0.2.2\n", oss.str());
-}
-
-// addRRSIG tests.
-TEST_F(RBNodeRRsetTest, addRRsigConstRdataPointer) {
- EXPECT_FALSE(rrset_a.getRRsig());
- ConstRdataPtr data = createRdata(rrset_siga->getType(),
- rrset_siga->getClass(), RRSIG_TXT);
- rrset_a.addRRsig(data);
- rrsetCheck(rrset_siga, rrset_a.getRRsig());
-}
-
-TEST_F(RBNodeRRsetTest, addRRsigRdataPointer) {
- EXPECT_FALSE(rrset_a.getRRsig());
- RdataPtr data = createRdata(rrset_siga->getType(), rrset_siga->getClass(),
- RRSIG_TXT);
- rrset_a.addRRsig(data);
- rrsetCheck(rrset_siga, rrset_a.getRRsig());
-}
-
-TEST_F(RBNodeRRsetTest, addRRsigAbstractRRset) {
- EXPECT_FALSE(rrset_a.getRRsig());
- rrset_a.addRRsig(*(rrset_siga.get()));
- rrsetCheck(rrset_siga, rrset_a.getRRsig());
-}
-
-TEST_F(RBNodeRRsetTest, addRRsigConstantRRsetPointer) {
- EXPECT_FALSE(rrset_a.getRRsig());
- rrset_a.addRRsig(rrset_siga);
- rrsetCheck(rrset_siga, rrset_a.getRRsig());
-}
-
-TEST_F(RBNodeRRsetTest, addRRsigRRsetPointer) {
- EXPECT_FALSE(rrset_a.getRRsig());
- RRsetPtr rrsig(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
- RRTTL(3600)));
- rrsig->addRdata(generic::RRSIG(RRSIG_TXT));
- rrset_a.addRRsig(rrsig);
- rrsetCheck(rrset_siga, rrset_a.getRRsig());
-}
-
-TEST_F(RBNodeRRsetTest, removeRRsig) {
- EXPECT_FALSE(rrset_a.getRRsig());
- rrset_a.addRRsig(*(rrset_siga.get()));
- EXPECT_TRUE(rrset_a.getRRsig());
- rrset_a.removeRRsig();
- EXPECT_FALSE(rrset_a.getRRsig());
-}
-
-} // Anonymous namespace
+++ /dev/null
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <gtest/gtest.h>
-
-#include <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <datasrc/rbtree.h>
-
-#include <dns/tests/unittest_util.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dns;
-using isc::UnitTestUtil;
-using namespace isc::datasrc;
-
-// XXX: some compilers cannot find class static constants used in
-// EXPECT_xxx macros, for which we need an explicit empty definition.
-const size_t Name::MAX_LABELS;
-
-/* The initial structure of rbtree
- *
- * .
- * |
- * b
- * / \
- * a d.e.f
- * / | \
- * c | g.h
- * | |
- * w.y i
- * / | \ \
- * x | z k
- * | |
- * p j
- * / \
- * o q
- */
-
-namespace {
-class TreeHolder {
-public:
- TreeHolder(util::MemorySegment& mem_sgmt, RBTree<int>* tree) :
- mem_sgmt_(mem_sgmt), tree_(tree)
- {}
- ~TreeHolder() {
- RBTree<int>::destroy(mem_sgmt_, tree_);
- }
- RBTree<int>* get() { return (tree_); }
-private:
- util::MemorySegment& mem_sgmt_;
- RBTree<int>* tree_;
-};
-
-class RBTreeTest : public::testing::Test {
-protected:
- RBTreeTest() :
- rbtree_holder_(mem_sgmt_, RBTree<int>::create(mem_sgmt_)),
- rbtree_expose_empty_node_holder_(mem_sgmt_,
- RBTree<int>::create(mem_sgmt_, true)),
- rbtree(*rbtree_holder_.get()),
- rbtree_expose_empty_node(*rbtree_expose_empty_node_holder_.get()),
- crbtnode(NULL)
- {
- const char* const domain_names[] = {
- "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
- "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
- int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
- for (int i = 0; i < name_count; ++i) {
- rbtree.insert(mem_sgmt_, Name(domain_names[i]), &rbtnode);
- rbtnode->setData(RBNode<int>::NodeDataPtr(new int(i + 1)));
-
- rbtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
- &rbtnode);
- rbtnode->setData(RBNode<int>::NodeDataPtr(new int(i + 1)));
-
- }
- }
-
- util::MemorySegmentLocal mem_sgmt_;
- TreeHolder rbtree_holder_;
- TreeHolder rbtree_expose_empty_node_holder_;
- RBTree<int>& rbtree;
- RBTree<int>& rbtree_expose_empty_node;
- RBNode<int>* rbtnode;
- const RBNode<int>* crbtnode;
- uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
-};
-
-TEST_F(RBTreeTest, nodeCount) {
- EXPECT_EQ(15, rbtree.getNodeCount());
-
- // Delete all nodes, then the count should be set to 0. This also tests
- // the behavior of deleteAllNodes().
- rbtree.deleteAllNodes(mem_sgmt_);
- EXPECT_EQ(0, rbtree.getNodeCount());
-}
-
-TEST_F(RBTreeTest, setGetData) {
- rbtnode->setData(RBNode<int>::NodeDataPtr(new int(11)));
- EXPECT_EQ(11, *(rbtnode->getData()));
-}
-
-TEST_F(RBTreeTest, insertNames) {
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt_,
- Name("d.e.f"),
- &rbtnode));
- EXPECT_EQ(Name("d.e.f"), rbtnode->getName());
- EXPECT_EQ(15, rbtree.getNodeCount());
-
- // insert not exist node
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("0"),
- &rbtnode));
- EXPECT_EQ(Name("0"), rbtnode->getName());
- EXPECT_EQ(16, rbtree.getNodeCount());
-
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
- Name("example.com"),
- &rbtnode));
- EXPECT_EQ(17, rbtree.getNodeCount());
- rbtnode->setData(RBNode<int>::NodeDataPtr(new int(12)));
-
- // return ALREADYEXISTS, since node "example.com" already has
- // been explicitly inserted
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt_,
- Name("example.com"),
- &rbtnode));
- EXPECT_EQ(17, rbtree.getNodeCount());
-
- // split the node "d.e.f"
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("k.e.f"),
- &rbtnode));
- EXPECT_EQ(Name("k"), rbtnode->getName());
- EXPECT_EQ(19, rbtree.getNodeCount());
-
- // split the node "g.h"
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt_, Name("h"),
- &rbtnode));
- EXPECT_EQ(Name("h"), rbtnode->getName());
- EXPECT_EQ(20, rbtree.getNodeCount());
-
- // add child domain
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
- Name("m.p.w.y.d.e.f"),
- &rbtnode));
- EXPECT_EQ(Name("m"), rbtnode->getName());
- EXPECT_EQ(21, rbtree.getNodeCount());
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
- Name("n.p.w.y.d.e.f"),
- &rbtnode));
- EXPECT_EQ(Name("n"), rbtnode->getName());
- EXPECT_EQ(22, rbtree.getNodeCount());
-
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("l.a"),
- &rbtnode));
- EXPECT_EQ(Name("l"), rbtnode->getName());
- EXPECT_EQ(23, rbtree.getNodeCount());
-
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("r.d.e.f"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("s.d.e.f"),
- &rbtnode));
- EXPECT_EQ(25, rbtree.getNodeCount());
-
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
- Name("h.w.y.d.e.f"),
- &rbtnode));
-
- // add more nodes one by one to cover leftRotate and rightRotate
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt_, Name("f"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("m"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("nm"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("om"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("k"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("l"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("fe"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("ge"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("i"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("ae"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("n"),
- &rbtnode));
-}
-
-TEST_F(RBTreeTest, subTreeRoot) {
- // This is a testcase for a particular issue that went unchecked in
- // #2089's implementation, but was fixed in #2092. The issue was
- // that when a node was fissioned, FLAG_SUBTREE_ROOT was not being
- // copied correctly.
-
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS,
- rbtree_expose_empty_node.insert(mem_sgmt_, Name("d.e.f"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS,
- rbtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS,
- rbtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS,
- rbtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
- &rbtnode));
- EXPECT_EQ(RBTree<int>::SUCCESS,
- rbtree_expose_empty_node.insert(mem_sgmt_, Name("k.e.f"),
- &rbtnode));
-
- // "g.h" is not a subtree root
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree_expose_empty_node.find(Name("g.h"), &rbtnode));
- EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_SUBTREE_ROOT));
-
- // fission the node "g.h"
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS,
- rbtree_expose_empty_node.insert(mem_sgmt_, Name("h"),
- &rbtnode));
-
- // the node "h" (h.down_ -> "g") should not be a subtree root. "g"
- // should be a subtree root.
- EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_SUBTREE_ROOT));
-
- // "g.h" should be a subtree root now.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree_expose_empty_node.find(Name("g.h"), &rbtnode));
- EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_SUBTREE_ROOT));
-}
-
-TEST_F(RBTreeTest, additionalNodeFission) {
- // These are additional nodeFission tests added by #2054's rewrite
- // of RBTree::nodeFission(). These test specific corner cases that
- // are not covered by other tests.
-
- // Insert "t.0" (which becomes the left child of its parent)
- EXPECT_EQ(RBTree<int>::SUCCESS,
- rbtree_expose_empty_node.insert(mem_sgmt_, Name("t.0"),
- &rbtnode));
-
- // "t.0" is not a subtree root
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree_expose_empty_node.find(Name("t.0"), &rbtnode));
- EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_SUBTREE_ROOT));
-
- // fission the node "t.0"
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS,
- rbtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
- &rbtnode));
-
- // the node "0" ("0".down_ -> "t") should not be a subtree root. "t"
- // should be a subtree root.
- EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_SUBTREE_ROOT));
-
- // "t.0" should be a subtree root now.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree_expose_empty_node.find(Name("t.0"), &rbtnode));
- EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_SUBTREE_ROOT));
-}
-
-TEST_F(RBTreeTest, findName) {
- // find const rbtnode
- // exact match
- EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("a"), &crbtnode));
- EXPECT_EQ(Name("a"), crbtnode->getName());
-
- // not found
- EXPECT_EQ(RBTree<int>::NOTFOUND, rbtree.find(Name("d.e.f"), &crbtnode));
- EXPECT_EQ(RBTree<int>::NOTFOUND, rbtree.find(Name("y.d.e.f"), &crbtnode));
- EXPECT_EQ(RBTree<int>::NOTFOUND, rbtree.find(Name("x"), &crbtnode));
- EXPECT_EQ(RBTree<int>::NOTFOUND, rbtree.find(Name("m.n"), &crbtnode));
-
- // if we expose empty node, we can get the empty node created during insert
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree_expose_empty_node.find(Name("d.e.f"), &crbtnode));
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree_expose_empty_node.find(Name("w.y.d.e.f"), &crbtnode));
-
- // partial match
- EXPECT_EQ(RBTree<int>::PARTIALMATCH, rbtree.find(Name("m.b"), &crbtnode));
- EXPECT_EQ(Name("b"), crbtnode->getName());
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- rbtree_expose_empty_node.find(Name("m.d.e.f"), &crbtnode));
-
- // find rbtnode
- EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("q.w.y.d.e.f"),
- &rbtnode));
- EXPECT_EQ(Name("q"), rbtnode->getName());
-}
-
-TEST_F(RBTreeTest, findError) {
- // For the version that takes a node chain, the chain must be empty.
- RBTreeNodeChain<int> chain;
- EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("a"), &crbtnode,
- chain));
- // trying to reuse the same chain. it should result in an exception.
- EXPECT_THROW(rbtree.find(Name("a"), &crbtnode, chain),
- BadValue);
-}
-
-TEST_F(RBTreeTest, flags) {
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
- Name("flags.example"),
- &rbtnode));
-
- // by default, flags are all off
- EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
-
- // set operation, by default it enables the flag
- rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
- EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
-
- // try disable the flag explicitly
- rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, false);
- EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
-
- // try enable the flag explicitly
- rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, true);
- EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
-
- // setting an unknown flag will trigger an exception
- EXPECT_THROW(rbtnode->setFlag(static_cast<RBNode<int>::Flags>(2), true),
- isc::InvalidParameter);
-}
-
-bool
-testCallback(const RBNode<int>&, bool* callback_checker) {
- *callback_checker = true;
- return (false);
-}
-
-template <typename T>
-void
-performCallbackTest(RBTree<int>& rbtree,
- util::MemorySegmentLocal& mem_sgmt,
- const T& name_called,
- const T& name_not_called)
-{
- RBNode<int>* rbtnode;
- const RBNode<int>* crbtnode;
-
- // by default callback isn't enabled
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt,
- Name("callback.example"),
- &rbtnode));
- rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
- EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
-
- // enable/re-disable callback
- rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
- EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
- rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, false);
- EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
-
- // enable again for subsequent tests
- rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
- // add more levels below and above the callback node for partial match.
- RBNode<int>* subrbtnode;
- EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(mem_sgmt,
- Name("sub.callback.example"),
- &subrbtnode));
- subrbtnode->setData(RBNode<int>::NodeDataPtr(new int(2)));
- RBNode<int>* parentrbtnode;
- EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt,
- Name("example"),
- &parentrbtnode));
- // the child/parent nodes shouldn't "inherit" the callback flag.
- // "rbtnode" may be invalid due to the insertion, so we need to re-find
- // it.
- EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("callback.example"),
- &rbtnode));
- EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
- EXPECT_FALSE(subrbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
- EXPECT_FALSE(parentrbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
-
- // check if the callback is called from find()
- RBTreeNodeChain<int> node_path1;
- bool callback_called = false;
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree.find(name_called, &crbtnode, node_path1,
- testCallback, &callback_called));
- EXPECT_TRUE(callback_called);
-
- // enable callback at the parent node, but it doesn't have data so
- // the callback shouldn't be called.
- RBTreeNodeChain<int> node_path2;
- parentrbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
- callback_called = false;
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree.find(name_not_called, &crbtnode, node_path2,
- testCallback, &callback_called));
- EXPECT_FALSE(callback_called);
-}
-
-TEST_F(RBTreeTest, callbackName) {
- const Name n1("sub.callback.example");
- const Name n2("callback.example");
-
- performCallbackTest(rbtree, mem_sgmt_, n1, n2);
-}
-
-TEST_F(RBTreeTest, callbackLabelSequence) {
- const Name n1("sub.callback.example");
- const Name n2("callback.example");
- const LabelSequence ls1(n1);
- const LabelSequence ls2(n2);
-
- performCallbackTest(rbtree, mem_sgmt_, ls1, ls2);
-}
-
-TEST_F(RBTreeTest, chainLevel) {
- RBTreeNodeChain<int> chain;
-
- // by default there should be no level in the chain.
- EXPECT_EQ(0, chain.getLevelCount());
-
- // insert one node to the tree and find it. there should be exactly
- // one level in the chain.
- TreeHolder tree_holder(mem_sgmt_, RBTree<int>::create(mem_sgmt_, true));
- RBTree<int>& tree(*tree_holder.get());
- Name node_name(Name::ROOT_NAME());
- EXPECT_EQ(RBTree<int>::SUCCESS, tree.insert(mem_sgmt_, node_name,
- &rbtnode));
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find(node_name, &crbtnode, chain));
- EXPECT_EQ(1, chain.getLevelCount());
-
- // Check the name of the found node (should have '.' as both non-absolute
- // and absolute name
- EXPECT_EQ(".", crbtnode->getLabels().toText());
- EXPECT_EQ(".", crbtnode->getAbsoluteLabels(buf).toText());
-
- /*
- * Now creating a possibly deepest tree with MAX_LABELS levels.
- * it should look like:
- * (.)
- * |
- * a
- * |
- * a
- * : (MAX_LABELS - 1) "a"'s
- *
- * then confirm that find() for the deepest name succeeds without any
- * disruption, and the resulting chain has the expected level.
- * Note that the root name (".") solely belongs to a single level,
- * so the levels begin with 2.
- */
- for (unsigned int i = 2; i <= Name::MAX_LABELS; ++i) {
- node_name = Name("a.").concatenate(node_name);
- EXPECT_EQ(RBTree<int>::SUCCESS, tree.insert(mem_sgmt_, node_name,
- &rbtnode));
- RBTreeNodeChain<int> found_chain;
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find(node_name, &crbtnode, found_chain));
- EXPECT_EQ(i, found_chain.getLevelCount());
-
- // The non-absolute name should only have the first label
- EXPECT_EQ("a", crbtnode->getLabels().toText());
- // But the absolute name should have all labels
- EXPECT_EQ(node_name.toText(),
- crbtnode->getAbsoluteLabels(buf).toText());
- }
-
- // Confirm the last inserted name has the possible maximum length with
- // maximum label count. This confirms the rbtree and chain level cannot
- // be larger.
- EXPECT_EQ(Name::MAX_LABELS, node_name.getLabelCount());
- EXPECT_THROW(node_name.concatenate(Name("a.")), TooLongName);
-}
-
-TEST_F(RBTreeTest, getAbsoluteNameError) {
- // an empty chain isn't allowed.
- RBTreeNodeChain<int> chain;
- EXPECT_THROW(chain.getAbsoluteName(), BadValue);
-}
-
-/*
- * The domain order should be:
- * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
- * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
- * . (no data, can't be found)
- * |
- * b
- * / \
- * a d.e.f
- * / | \
- * c | g.h
- * | |
- * w.y i
- * / | \ \
- * x | z k
- * | |
- * p j
- * / \
- * o q
- */
-const char* const names[] = {
- "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
- "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
- "g.h", "i.g.h", "k.g.h"};
-const size_t name_count(sizeof(names) / sizeof(*names));
-
-const char* const upper_node_names[] = {
- ".", ".", ".", ".", "d.e.f", "d.e.f", "w.y.d.e.f",
- "w.y.d.e.f", "w.y.d.e.f", "d.e.f", "z.d.e.f",
- ".", "g.h", "g.h"};
-
-TEST_F(RBTreeTest, getUpperNode) {
- RBTreeNodeChain<int> node_path;
- const RBNode<int>* node = NULL;
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree_expose_empty_node.find(Name(names[0]),
- &node,
- node_path));
- for (int i = 0; i < name_count; ++i) {
- EXPECT_NE(static_cast<void*>(NULL), node);
-
- const RBNode<int>* upper_node = node->getUpperNode();
- if (upper_node_names[i] != NULL) {
- const RBNode<int>* upper_node2 = NULL;
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree_expose_empty_node.find(Name(upper_node_names[i]),
- &upper_node2));
- EXPECT_NE(static_cast<void*>(NULL), upper_node2);
- EXPECT_EQ(upper_node, upper_node2);
- } else {
- EXPECT_EQ(static_cast<void*>(NULL), upper_node);
- }
-
- node = rbtree_expose_empty_node.nextNode(node_path);
- }
-
- // We should have reached the end of the tree.
- EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-TEST_F(RBTreeTest, nextNode) {
- RBTreeNodeChain<int> node_path;
- const RBNode<int>* node = NULL;
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree.find(Name(names[0]), &node, node_path));
- for (int i = 0; i < name_count; ++i) {
- EXPECT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
- node = rbtree.nextNode(node_path);
- }
-
- // We should have reached the end of the tree.
- EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-// Just walk using previousNode until the beginning of the tree and check it is
-// OK
-//
-// rbtree - the tree to walk
-// node - result of previous call to find(), starting position of the walk
-// node_path - the path from the previous call to find(), will be modified
-// chain_length - the number of names that should be in the chain to be walked
-// (0 means it should be empty, 3 means 'a', 'b' and 'c' should be there -
-// this is always from the beginning of the names[] list).
-// skip_first - if this is false, the node should already contain the node with
-// the first name of the chain. If it is true, the node should be NULL
-// (true is for finds that return no match, false for the ones that return
-// match)
-void
-previousWalk(RBTree<int>& rbtree, const RBNode<int>* node,
- RBTreeNodeChain<int>& node_path, size_t chain_length,
- bool skip_first)
-{
- if (skip_first) {
- // If the first is not found, this is supposed to be NULL and we skip
- // it in our checks.
- EXPECT_EQ(static_cast<void*>(NULL), node);
- node = rbtree.previousNode(node_path);
- }
- for (size_t i(chain_length); i > 0; --i) {
- EXPECT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
- // Find the node at the path and check the value is the same
- // (that it really returns the correct corresponding node)
- //
- // The "empty" nodes can not be found
- if (node->getData()) {
- const RBNode<int>* node2(NULL);
- RBTreeNodeChain<int> node_path2;
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree.find(Name(names[i - 1]), &node2, node_path2));
- EXPECT_EQ(node, node2);
- }
- node = rbtree.previousNode(node_path);
- }
-
- // We should have reached the start of the tree.
- ASSERT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(".", node->getLabels().toText());
-
- // With one more call it results in NULL
- node = rbtree.previousNode(node_path);
- EXPECT_EQ(static_cast<void*>(NULL), node);
-
- // Calling previousNode() yet again should still return NULL without
- // fail.
- node = rbtree.previousNode(node_path);
- EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-// Check the previousNode
-TEST_F(RBTreeTest, previousNode) {
- // First, iterate the whole tree from the end to the beginning.
- RBTreeNodeChain<int> node_path;
- EXPECT_THROW(rbtree.previousNode(node_path), isc::BadValue) <<
- "Throw before a search was done on the path";
- const RBNode<int>* node(NULL);
- {
- SCOPED_TRACE("Iterate through");
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree.find(Name(names[name_count - 1]), &node, node_path));
- previousWalk(rbtree, node, node_path, name_count, false);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Iterate from the middle");
- // Now, start somewhere in the middle, but within the real node.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree.find(Name(names[4]), &node, node_path));
- previousWalk(rbtree, node, node_path, 5, false);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start at the first");
- // If we start at the lowest (which is "a"), we get to the beginning
- // right away.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree.find(Name(names[0]), &node, node_path));
- EXPECT_NE(static_cast<void*>(NULL), node);
- node = rbtree.previousNode(node_path);
- ASSERT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(".", node->getLabels().toText());
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start before the first");
- // If we start before the lowest (. < 0. < a.), we should not get a
- // node. Its previous node should be the root.
- EXPECT_EQ(RBTree<int>::NOTFOUND,
- rbtree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
- EXPECT_EQ(static_cast<void*>(NULL), node);
- node = rbtree.previousNode(node_path);
- ASSERT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(".", node->getLabels().toText());
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start after the last");
- EXPECT_EQ(RBTree<int>::NOTFOUND,
- rbtree.find(Name("z"), &node, node_path));
- previousWalk(rbtree, node, node_path, name_count, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start below a leaf");
- // We exit a leaf by going down. We should start by the one
- // we exited - 'c' (actually, we should get it by the find, as partial
- // match).
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- rbtree.find(Name("b.c"), &node, node_path));
- previousWalk(rbtree, node, node_path, 3, false);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start to the right of a leaf");
- // When searching for this, we exit the 'x' node to the right side,
- // so we should go x afterwards.
-
- // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
- // and not PARTIALMATCH.
- EXPECT_EQ(RBTree<int>::NOTFOUND,
- rbtree.find(Name("xy.d.e.f"), &node, node_path));
- previousWalk(rbtree, node, node_path, 5, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start to the left of a leaf");
- // This is similar to the previous, but we exit the 'z' leaf to the
- // left side, so should not visit z at all then.
-
- // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
- // and not PARTIALMATCH.
- EXPECT_EQ(RBTree<int>::NOTFOUND,
- rbtree.find(Name("yz.d.e.f"), &node, node_path));
- previousWalk(rbtree, node, node_path, 9, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start to the right of a parent");
- // When searching for this, we exit the 'g.h' node to the right
- // side, so we should go to g.h's children afterwards.
-
- // 'g.h' is an empty node, so we get a NOTFOUND and not
- // PARTIALMATCH.
- EXPECT_EQ(RBTree<int>::NOTFOUND,
- rbtree.find(Name("x.h"), &node, node_path));
- // 'g.h' is the COMMONANCESTOR.
- EXPECT_EQ(node_path.getLastComparedNode()->getName(), Name("g.h"));
- EXPECT_EQ(NameComparisonResult::COMMONANCESTOR,
- node_path.getLastComparisonResult().getRelation());
- // find() exits to the right of 'g.h'
- EXPECT_GT(node_path.getLastComparisonResult().getOrder(), 0);
- // We then descend into 'i.g.h' and walk all the nodes in the
- // tree.
- previousWalk(rbtree, node, node_path, name_count, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start inside a wrong node");
- // The d.e.f is a single node, but we want only part of it. We
- // should start iterating before it.
- EXPECT_EQ(RBTree<int>::NOTFOUND,
- rbtree.find(Name("e.f"), &node, node_path));
- previousWalk(rbtree, node, node_path, 3, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Lookup in empty tree");
- // Just check it doesn't crash, etc.
- TreeHolder tree_holder(mem_sgmt_, RBTree<int>::create(mem_sgmt_));
- RBTree<int>& empty_tree(*tree_holder.get());
- EXPECT_EQ(RBTree<int>::NOTFOUND,
- empty_tree.find(Name("x"), &node, node_path));
- EXPECT_EQ(static_cast<void*>(NULL), node);
- EXPECT_EQ(static_cast<void*>(NULL),
- empty_tree.previousNode(node_path));
- node = NULL;
- node_path.clear();
- }
-}
-
-TEST_F(RBTreeTest, nextNodeError) {
- // Empty chain for nextNode() is invalid.
- RBTreeNodeChain<int> chain;
- EXPECT_THROW(rbtree.nextNode(chain), BadValue);
-}
-
-// A helper function for getLastComparedNode() below.
-void
-comparisonChecks(const RBTreeNodeChain<int>& chain,
- int expected_order, int expected_common_labels,
- NameComparisonResult::NameRelation expected_reln)
-{
- if (expected_order > 0) {
- EXPECT_LT(0, chain.getLastComparisonResult().getOrder());
- } else if (expected_order < 0) {
- EXPECT_GT(0, chain.getLastComparisonResult().getOrder());
- } else {
- EXPECT_EQ(0, chain.getLastComparisonResult().getOrder());
- }
- EXPECT_EQ(expected_common_labels,
- chain.getLastComparisonResult().getCommonLabels());
- EXPECT_EQ(expected_reln,
- chain.getLastComparisonResult().getRelation());
-}
-
-TEST_F(RBTreeTest, getLastComparedNode) {
- RBTree<int>& tree = rbtree_expose_empty_node; // use the "empty OK" mode
- RBTreeNodeChain<int> chain;
-
- // initially there should be no 'last compared'.
- EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
-
- // A search for an empty tree should result in no 'last compared', too.
- TreeHolder tree_holder(mem_sgmt_, RBTree<int>::create(mem_sgmt_));
- RBTree<int>& empty_tree(*tree_holder.get());
- EXPECT_EQ(RBTree<int>::NOTFOUND,
- empty_tree.find(Name("a"), &crbtnode, chain));
- EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
- chain.clear();
-
- const RBNode<int>* expected_node = NULL;
-
- // Exact match case. The returned node should be last compared.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find(Name("x.d.e.f"), &expected_node, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // 1 = # labels of "x" (note: excluding ".")
- comparisonChecks(chain, 0, 1, NameComparisonResult::EQUAL);
- chain.clear();
-
- // Partial match, search stopped at the matching node, which should be
- // the last compared node.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find(Name("k.g.h"), &expected_node));
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find(Name("x.k.g.h"), &crbtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // k.g.h < x.k.g.h, 1 = # labels of "k"
- comparisonChecks(chain, 1, 1, NameComparisonResult::SUBDOMAIN);
- chain.clear();
-
- // Partial match, search stopped in the subtree below the matching node
- // after following a left branch.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find(Name("x.d.e.f"), &expected_node));
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find(Name("a.d.e.f"), &crbtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // a < x, no common labels
- comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
- chain.clear();
-
- // Partial match, search stopped in the subtree below the matching node
- // after following a right branch.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find(Name("z.d.e.f"), &expected_node));
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find(Name("zz.d.e.f"), &crbtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // zz > z, no common label
- comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
- chain.clear();
-
- // Partial match, search stopped at a node for a super domain of the
- // search name in the subtree below the matching node.
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find(Name("w.y.d.e.f"), &expected_node));
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find(Name("y.d.e.f"), &crbtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // y < w.y, 1 = # labels of "y"
- comparisonChecks(chain, -1, 1, NameComparisonResult::SUPERDOMAIN);
- chain.clear();
-
- // Partial match, search stopped at a node that share a common ancestor
- // with the search name in the subtree below the matching node.
- // (the expected node is the same as the previous case)
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find(Name("z.y.d.e.f"), &crbtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // z.y > w.y, 1 = # labels of "y"
- comparisonChecks(chain, 1, 1, NameComparisonResult::COMMONANCESTOR);
- chain.clear();
-
- // Search stops in the highest level (under ".") after following a left
- // branch. (find() still returns PARTIALMATCH due to the top level ".")
- EXPECT_EQ(RBTree<int>::EXACTMATCH, tree.find(Name("c"), &expected_node));
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find(Name("bb"), &crbtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // bb < c, no common label
- comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
- chain.clear();
-
- // Search stops in the highest level (under ".") after following a right
- // branch. (the expected node is the same as the previous case)
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find(Name("d"), &crbtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // d > c, no common label
- comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
- chain.clear();
-}
-
-TEST_F(RBTreeTest, dumpTree) {
- std::ostringstream str;
- std::ostringstream str2;
- rbtree.dumpTree(str);
- str2 << "tree has 15 node(s)\n"
- ". (black) [invisible] [subtreeroot]\n"
- " begin down from .\n"
- " b (black) [subtreeroot]\n"
- " a (black)\n"
- " NULL\n"
- " NULL\n"
- " d.e.f (black) [invisible]\n"
- " begin down from d.e.f\n"
- " w.y (black) [invisible] [subtreeroot]\n"
- " begin down from w.y\n"
- " p (black) [subtreeroot]\n"
- " o (red)\n"
- " NULL\n"
- " NULL\n"
- " q (red)\n"
- " NULL\n"
- " NULL\n"
- " end down from w.y\n"
- " x (red)\n"
- " NULL\n"
- " NULL\n"
- " z (red)\n"
- " begin down from z\n"
- " j (black) [subtreeroot]\n"
- " NULL\n"
- " NULL\n"
- " end down from z\n"
- " NULL\n"
- " NULL\n"
- " end down from d.e.f\n"
- " c (red)\n"
- " NULL\n"
- " NULL\n"
- " g.h (red)\n"
- " begin down from g.h\n"
- " i (black) [subtreeroot]\n"
- " NULL\n"
- " k (red)\n"
- " NULL\n"
- " NULL\n"
- " end down from g.h\n"
- " NULL\n"
- " NULL\n"
- " end down from .\n"
- " NULL\n"
- " NULL\n";
- EXPECT_EQ(str2.str(), str.str());
-}
-
-TEST_F(RBTreeTest, swap) {
- // Store info about the first tree
- std::ostringstream str1;
- rbtree.dumpTree(str1);
- size_t count1(rbtree.getNodeCount());
-
- // Create second one and store state
- TreeHolder tree_holder(mem_sgmt_, RBTree<int>::create(mem_sgmt_));
- RBTree<int>& tree2(*tree_holder.get());
- RBNode<int>* node;
- tree2.insert(mem_sgmt_, Name("second"), &node);
- std::ostringstream str2;
- tree2.dumpTree(str2);
-
- // Swap them
- ASSERT_NO_THROW(tree2.swap(rbtree));
-
- // Check their sizes
- ASSERT_EQ(1, rbtree.getNodeCount());
- ASSERT_EQ(count1, tree2.getNodeCount());
-
- // And content
- std::ostringstream out;
- rbtree.dumpTree(out);
- ASSERT_EQ(str2.str(), out.str());
- out.str("");
- tree2.dumpTree(out);
- ASSERT_EQ(str1.str(), out.str());
-}
-
-// Matching in the "root zone" may be special (e.g. there's no parent,
-// any domain names should be considered a subdomain of it), so it makes
-// sense to test cases with the root zone explicitly.
-TEST_F(RBTreeTest, root) {
- TreeHolder tree_holder(mem_sgmt_, RBTree<int>::create(mem_sgmt_));
- RBTree<int>& root(*tree_holder.get());
- root.insert(mem_sgmt_, Name::ROOT_NAME(), &rbtnode);
- rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
-
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- root.find(Name::ROOT_NAME(), &crbtnode));
- EXPECT_EQ(rbtnode, crbtnode);
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- root.find(Name("example.com"), &crbtnode));
- EXPECT_EQ(rbtnode, crbtnode);
-
- // Insert a new name that better matches the query name. find() should
- // find the better one.
- root.insert(mem_sgmt_, Name("com"), &rbtnode);
- rbtnode->setData(RBNode<int>::NodeDataPtr(new int(2)));
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- root.find(Name("example.com"), &crbtnode));
- EXPECT_EQ(rbtnode, crbtnode);
-
- // Perform the same tests for the tree that allows matching against empty
- // nodes.
- TreeHolder tree_holder_emptyok(mem_sgmt_,
- RBTree<int>::create(mem_sgmt_, true));
- RBTree<int>& root_emptyok(*tree_holder_emptyok.get());
- root_emptyok.insert(mem_sgmt_, Name::ROOT_NAME(), &rbtnode);
- EXPECT_EQ(RBTree<int>::EXACTMATCH,
- root_emptyok.find(Name::ROOT_NAME(), &crbtnode));
- EXPECT_EQ(rbtnode, crbtnode);
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- root_emptyok.find(Name("example.com"), &crbtnode));
- EXPECT_EQ(rbtnode, crbtnode);
-
- root.insert(mem_sgmt_, Name("com"), &rbtnode);
- EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- root.find(Name("example.com"), &crbtnode));
- EXPECT_EQ(rbtnode, crbtnode);
-}
-
-TEST_F(RBTreeTest, getAbsoluteLabels) {
- // The full absolute names of the nodes in the tree
- // with the addition of the explicit root node
- const char* const domain_names[] = {
- "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
- "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
- // The names of the nodes themselves, as they end up in the tree
- const char* const first_labels[] = {
- "c", "b", "a", "x", "z", "g.h", "i", "o",
- "j", "p", "q", "k"};
-
- const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
- for (int i = 0; i < name_count; ++i) {
- EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name(domain_names[i]),
- &crbtnode));
-
- // First make sure the names themselves are not absolute
- const LabelSequence ls(crbtnode->getLabels());
- EXPECT_EQ(first_labels[i], ls.toText());
- EXPECT_FALSE(ls.isAbsolute());
-
- // Now check the absolute names
- const LabelSequence abs_ls(crbtnode->getAbsoluteLabels(buf));
- EXPECT_EQ(Name(domain_names[i]).toText(), abs_ls.toText());
- EXPECT_TRUE(abs_ls.isAbsolute());
- }
-
- // Explicitly add and find a root node, to see that getAbsoluteLabels
- // also works when getLabels() already returns an absolute LabelSequence
- rbtree.insert(mem_sgmt_, Name("."), &rbtnode);
- rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
-
- EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("."), &crbtnode));
-
- EXPECT_TRUE(crbtnode->getLabels().isAbsolute());
- EXPECT_EQ(".", crbtnode->getLabels().toText());
- EXPECT_TRUE(crbtnode->getAbsoluteLabels(buf).isAbsolute());
- EXPECT_EQ(".", crbtnode->getAbsoluteLabels(buf).toText());
-}
-
-}
+++ /dev/null
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-
-#include <datasrc/zonetable.h>
-// We use InMemoryZone to put something into the table
-#include <datasrc/memory_datasrc.h>
-
-#include <gtest/gtest.h>
-
-using namespace isc::dns;
-using namespace isc::datasrc;
-
-namespace {
-TEST(ZoneTest, init) {
- InMemoryZoneFinder zone(RRClass::IN(), Name("example.com"));
- EXPECT_EQ(Name("example.com"), zone.getOrigin());
- EXPECT_EQ(RRClass::IN(), zone.getClass());
-
- InMemoryZoneFinder ch_zone(RRClass::CH(), Name("example"));
- EXPECT_EQ(Name("example"), ch_zone.getOrigin());
- EXPECT_EQ(RRClass::CH(), ch_zone.getClass());
-}
-
-TEST(ZoneTest, find) {
- InMemoryZoneFinder zone(RRClass::IN(), Name("example.com"));
- EXPECT_EQ(ZoneFinder::NXDOMAIN,
- zone.find(Name("www.example.com"), RRType::A())->code);
-}
-
-class ZoneTableTest : public ::testing::Test {
-protected:
- ZoneTableTest() : zone1(new InMemoryZoneFinder(RRClass::IN(),
- Name("example.com"))),
- zone2(new InMemoryZoneFinder(RRClass::IN(),
- Name("example.net"))),
- zone3(new InMemoryZoneFinder(RRClass::IN(),
- Name("example"))),
- zone_table(ZoneTable::create(mem_sgmt_))
- {}
-
- ~ZoneTableTest() {
- ZoneTable::destroy(mem_sgmt_, zone_table);
- }
- ZoneFinderPtr zone1, zone2, zone3;
- isc::util::MemorySegmentLocal mem_sgmt_;
- ZoneTable* zone_table;
-};
-
-TEST_F(ZoneTableTest, addZone) {
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone1));
- EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zone1));
- // names are compared in a case insensitive manner.
- EXPECT_EQ(result::EXIST, zone_table->addZone(
- mem_sgmt_,
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("EXAMPLE.COM")))));
-
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone2));
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone3));
-
- // Zone table is indexed only by name. Duplicate origin name with
- // different zone class isn't allowed.
- EXPECT_EQ(result::EXIST, zone_table->addZone(
- mem_sgmt_,
- ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
- Name("example.com")))));
-
- /// Bogus zone (NULL)
- EXPECT_THROW(zone_table->addZone(mem_sgmt_, ZoneFinderPtr()),
- isc::InvalidParameter);
-}
-
-TEST_F(ZoneTableTest, DISABLED_removeZone) {
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone1));
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone2));
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone3));
-
- EXPECT_EQ(result::SUCCESS, zone_table->removeZone(Name("example.net")));
- EXPECT_EQ(result::NOTFOUND, zone_table->removeZone(Name("example.net")));
-}
-
-TEST_F(ZoneTableTest, findZone) {
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone1));
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone2));
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone3));
-
- EXPECT_EQ(result::SUCCESS, zone_table->findZone(Name("example.com")).code);
- EXPECT_EQ(Name("example.com"),
- zone_table->findZone(Name("example.com")).zone->getOrigin());
-
- EXPECT_EQ(result::NOTFOUND,
- zone_table->findZone(Name("example.org")).code);
- EXPECT_EQ(ConstZoneFinderPtr(),
- zone_table->findZone(Name("example.org")).zone);
-
- // there's no exact match. the result should be the longest match,
- // and the code should be PARTIALMATCH.
- EXPECT_EQ(result::PARTIALMATCH,
- zone_table->findZone(Name("www.example.com")).code);
- EXPECT_EQ(Name("example.com"),
- zone_table->findZone(Name("www.example.com")).zone->getOrigin());
-
- // make sure the partial match is indeed the longest match by adding
- // a zone with a shorter origin and query again.
- ZoneFinderPtr zone_com(new InMemoryZoneFinder(RRClass::IN(), Name("com")));
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zone_com));
- EXPECT_EQ(Name("example.com"),
- zone_table->findZone(Name("www.example.com")).zone->getOrigin());
-}
-}
+++ /dev/null
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/memory_segment.h>
-
-#include <dns/name.h>
-
-#include <datasrc/zonetable.h>
-#include <datasrc/rbtree.h>
-
-#include <cassert>
-
-using namespace std;
-using namespace isc::dns;
-
-namespace isc {
-namespace datasrc {
-
-/// \short Private data and implementation of ZoneTable
-struct ZoneTable::ZoneTableImpl {
- // Type aliases to make it shorter
- typedef RBTree<ZoneFinder> ZoneTree;
- typedef RBNode<ZoneFinder> ZoneNode;
-
- // The actual storage
- ZoneTree* zones_;
-
- // Constructor
- ZoneTableImpl(util::MemorySegment& mem_sgmt) :
- zones_(ZoneTree::create(mem_sgmt))
- {}
-
- /*
- * The implementation methods are here and just wrap-called in the
- * ZoneTable. We have variables locally (without impl_->), have
- * type aliases, etc. And they will get inlined anyway.
- */
-
- // Implementation of ZoneTable::addZone
- result::Result addZone(util::MemorySegment& mem_sgmt, ZoneFinderPtr zone) {
- // Sanity check
- if (!zone) {
- isc_throw(InvalidParameter,
- "Null pointer is passed to ZoneTable::addZone()");
- }
-
- // Get the node where we put the zone
- ZoneNode* node(NULL);
- switch (zones_->insert(mem_sgmt, zone->getOrigin(), &node)) {
- // This is OK
- case ZoneTree::SUCCESS:
- case ZoneTree::ALREADYEXISTS:
- break;
- // Can Not Happen
- default:
- assert(0);
- }
- // Can Not Happen
- assert(node);
-
- // Is it empty? We either just created it or it might be nonterminal
- if (node->isEmpty()) {
- node->setData(zone);
- return (result::SUCCESS);
- } else { // There's something there already
- return (result::EXIST);
- }
- }
-
- // Implementation of ZoneTable::findZone
- ZoneTable::FindResult findZone(const Name& name) const {
- ZoneNode *node(NULL);
- result::Result my_result;
-
- // Translate the return codes
- switch (zones_->find(name, &node)) {
- case ZoneTree::EXACTMATCH:
- my_result = result::SUCCESS;
- break;
- case ZoneTree::PARTIALMATCH:
- my_result = result::PARTIALMATCH;
- break;
- // We have no data there, so translate the pointer to NULL as well
- case ZoneTree::NOTFOUND:
- return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
- // Can Not Happen
- default:
- assert(0);
- // Because of warning
- return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
- }
-
- // Can Not Happen (remember, NOTFOUND is handled)
- assert(node);
-
- return (FindResult(my_result, node->getData()));
- }
-};
-
-ZoneTable::ZoneTable(util::MemorySegment& mem_sgmt) :
- impl_(new ZoneTableImpl(mem_sgmt))
-{}
-
-ZoneTable::~ZoneTable() {
- delete impl_;
-}
-
-ZoneTable*
-ZoneTable::create(util::MemorySegment& mem_sgmt) {
- // The ZoneTable constructor can throw, so we need to prevent memory leak.
- // This is ugly, but for now this seems to be the only place we need
- // this, and since we'll substantially revise this code soon, so we don't
- // work around it by this hack at the moment.
- void* p = mem_sgmt.allocate(sizeof(ZoneTable));
- try {
- return (new(p) ZoneTable(mem_sgmt));
- } catch (...) {
- mem_sgmt.deallocate(p, sizeof(ZoneTable));
- throw;
- }
-}
-
-void
-ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable) {
- ZoneTableImpl::ZoneTree::destroy(mem_sgmt, ztable->impl_->zones_);
- ztable->~ZoneTable();
- mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
-}
-
-result::Result
-ZoneTable::addZone(util::MemorySegment& mem_sgmt, ZoneFinderPtr zone) {
- return (impl_->addZone(mem_sgmt, zone));
-}
-
-result::Result
-ZoneTable::removeZone(const Name&) {
- // TODO Implement
- assert(0);
- // This should not ever be returned, the assert should kill us by now
- return (result::SUCCESS);
-}
-
-ZoneTable::FindResult
-ZoneTable::findZone(const Name& name) const {
- return (impl_->findZone(name));
-}
-
-} // end of namespace datasrc
-} // end of namespace isc
+++ /dev/null
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef ZONETABLE_H
-#define ZONETABLE_H 1
-
-#include <util/memory_segment.h>
-
-#include <dns/rrset.h>
-
-#include <datasrc/zone_finder.h>
-
-#include <boost/shared_ptr.hpp>
-
-namespace isc {
-namespace dns {
-class Name;
-class RRClass;
-}
-
-namespace datasrc {
-
-/// \brief A set of authoritative zones.
-///
-/// \c ZoneTable class is primarily intended to be used as a backend for the
-/// \c MemoryDataSrc class, but is exposed as a separate class in case some
-/// application wants to use it directly (e.g. for a customized data source
-/// implementation).
-///
-/// For more descriptions about its struct and interfaces, please refer to the
-/// corresponding struct and interfaces of \c MemoryDataSrc.
-class ZoneTable {
-public:
- struct FindResult {
- FindResult(result::Result param_code, const ZoneFinderPtr param_zone) :
- code(param_code), zone(param_zone)
- {}
- const result::Result code;
- const ZoneFinderPtr zone;
- };
- ///
- /// \name Constructors and Destructor.
- ///
- /// \b Note:
- /// The copy constructor and the assignment operator are intentionally
- /// defined as private, making this class non copyable.
- //@{
-private:
- ZoneTable(const ZoneTable& source);
- ZoneTable& operator=(const ZoneTable& source);
-
- /// Constructor.
- ///
- /// An object of this class is always expected to be created by the
- /// allocator (\c create()), so the constructor is hidden as private.
- ///
- /// This constructor internally involves resource allocation, and if
- /// it fails, a corresponding standard exception will be thrown.
- /// It never throws an exception otherwise.
- ZoneTable(util::MemorySegment& mem_sgmt);
-
- /// The destructor.
- ///
- /// An object of this class is always expected to be destroyed explicitly
- /// by \c destroy(), so the constructor is hidden as private.
- ~ZoneTable();
- //@}
-
-public:
- /// \brief Allocate and construct \c ZoneTable
- ///
- /// This static method allocates memory for a new \c ZoneTable object
- /// from the given memory segment, constructs the object, and returns
- /// a pointer to it.
- ///
- /// \throw std::bad_alloc Memory allocation fails.
- ///
- /// \param mem_sgmt A \c MemorySegment from which memory for the new
- /// \c ZoneTable is allocated.
- static ZoneTable* create(util::MemorySegment& mem_sgmt);
-
- /// \brief Destruct and deallocate \c ZoneTable
- ///
- /// \throw none
- ///
- /// \param mem_sgmt The \c MemorySegment that allocated memory for
- /// \c ztable.
- /// \param ztable A non NULL pointer to a valid \c ZoneTable object
- /// that was originally created by the \c create() method (the behavior
- /// is undefined if this condition isn't met).
- static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable);
-
- /// Add a \c Zone to the \c ZoneTable.
- ///
- /// \c Zone must not be associated with a NULL pointer; otherwise
- /// an exception of class \c InvalidParameter will be thrown.
- /// If internal resource allocation fails, a corresponding standard
- /// exception will be thrown.
- /// This method never throws an exception otherwise.
- ///
- /// \param zone A \c Zone object to be added.
- /// \return \c result::SUCCESS If the zone is successfully
- /// added to the zone table.
- /// \return \c result::EXIST The zone table already contains
- /// zone of the same origin.
- result::Result addZone(util::MemorySegment& mem_sgmt, ZoneFinderPtr zone);
-
- /// Remove a \c Zone of the given origin name from the \c ZoneTable.
- ///
- /// This method never throws an exception.
- ///
- /// \param origin The origin name of the zone to be removed.
- /// \return \c result::SUCCESS If the zone is successfully
- /// removed from the zone table.
- /// \return \c result::NOTFOUND The zone table does not
- /// store the zone that matches \c origin.
- result::Result removeZone(const isc::dns::Name& origin);
-
- /// Find a \c Zone that best matches the given name in the \c ZoneTable.
- ///
- /// It searches the internal storage for a \c Zone that gives the
- /// longest match against \c name, and returns the result in the
- /// form of a \c FindResult object as follows:
- /// - \c code: The result code of the operation.
- /// - \c result::SUCCESS: A zone that gives an exact match
- /// is found
- /// - \c result::PARTIALMATCH: A zone whose origin is a
- /// super domain of \c name is found (but there is no exact match)
- /// - \c result::NOTFOUND: For all other cases.
- /// - \c zone: A "Boost" shared pointer to the found \c Zone object if one
- /// is found; otherwise \c NULL.
- ///
- /// This method never throws an exception.
- ///
- /// \param name A domain name for which the search is performed.
- /// \return A \c FindResult object enclosing the search result (see above).
- FindResult findZone(const isc::dns::Name& name) const;
-
-private:
- struct ZoneTableImpl;
- ZoneTableImpl* impl_;
-};
-}
-}
-#endif // ZONETABLE_H
-
-// Local Variables:
-// mode: c++
-// End:
EXTRA_DIST += testdata/new_minor_schema.sqlite3
EXTRA_DIST += testdata/example.com
EXTRA_DIST += testdata/example.com.ch
+EXTRA_DIST += testdata/static.zone
CLEANFILES = $(abs_builddir)/rwtest.sqlite3.copied
CLEANFILES += $(abs_builddir)/zoneloadertest.sqlite3
READ_ZONE_DB_CONFIG = "{ \"database_file\": \"" + READ_ZONE_DB_FILE + "\" }"
WRITE_ZONE_DB_CONFIG = "{ \"database_file\": \"" + WRITE_ZONE_DB_FILE + "\"}"
+STATIC_ZONE_CONFIG = '"' + TESTDATA_PATH + "static.zone" + '"'
+
def add_rrset(rrset_list, name, rrclass, rrtype, ttl, rdatas):
rrset_to_add = isc.dns.RRset(name, rrclass, rrtype, ttl)
if rdatas is not None:
self.assertRaises(isc.datasrc.Error,
isc.datasrc.DataSourceClient, "sqlite3",
"{ \"foo\": 1 }")
- self.assertRaises(isc.datasrc.Error,
- isc.datasrc.DataSourceClient, "memory",
- "{ \"foo\": 1 }")
def test_iterate(self):
dsc = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
def test_two_modules(self):
# load two modules, and check if they don't interfere
- mem_cfg = { "type": "memory", "class": "IN", "zones": [] };
- dsc_mem = isc.datasrc.DataSourceClient("memory", json.dumps(mem_cfg))
+ dsc_static = isc.datasrc.DataSourceClient("static", STATIC_ZONE_CONFIG)
dsc_sql = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
# check if exceptions are working
self.assertRaises(isc.datasrc.Error, isc.datasrc.DataSourceClient,
- "memory", "{}")
+ "static", "\"\"")
self.assertRaises(isc.datasrc.Error, isc.datasrc.DataSourceClient,
"sqlite3", "{}")
self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
rrset.to_text())
- # see if a lookup fails in mem ds
- result, finder = dsc_mem.find_zone(isc.dns.Name("example.com"))
+ # see if a lookup fails in static ds
+ result, finder = dsc_static.find_zone(isc.dns.Name("example"))
self.assertEqual(finder.NXDOMAIN, result)
-
def test_update_delete_abort(self):
# we don't do enything with this one, just making sure loading two
# datasources
self.assertTrue(dsc.delete_zone(zone_name))
def test_create_zone_not_implemented(self):
- mem_cfg = '{ "type": "memory", "class": "IN", "zones": [] }';
- dsc = isc.datasrc.DataSourceClient("memory", mem_cfg)
+ dsc = isc.datasrc.DataSourceClient("static", STATIC_ZONE_CONFIG)
self.assertRaises(isc.datasrc.NotImplemented, dsc.create_zone,
isc.dns.Name("example.com"))
--- /dev/null
+BIND. 3600 CH SOA BIND. BIND. 3 3600 300 36000 3600
+BIND. 3600 CH NS BIND.
+VERSION.BIND. 3600 CH TXT "10"