BIND 10 trac2108, updated. 95f50715f18416e85f831f1e078c2e2cc9061d97 tmp
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Aug 31 00:18:02 UTC 2012
The branch, trac2108 has been updated
via 95f50715f18416e85f831f1e078c2e2cc9061d97 (commit)
via 19453b493a9bb0410e38157e3042fd5e97a0c1bf (commit)
from eb04fd11c962185ffa86a1fd1fb9ccefded0d67b (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 95f50715f18416e85f831f1e078c2e2cc9061d97
Author: Mukund Sivaraman <muks at isc.org>
Date: Thu Aug 30 10:31:17 2012 +0530
tmp
commit 19453b493a9bb0410e38157e3042fd5e97a0c1bf
Author: Mukund Sivaraman <muks at isc.org>
Date: Thu Aug 30 09:54:08 2012 +0530
[2108] Pass InMemoryClient when creating an InMemoryZoneFinder
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/client_list.cc | 16 +-
src/lib/datasrc/memory_datasrc.cc | 1652 ++++++++++++++++----------------
src/lib/datasrc/memory_datasrc.h | 212 ++--
src/lib/datasrc/memory_datasrc_link.cc | 32 +-
src/lib/datasrc/static_datasrc_link.cc | 7 +-
5 files changed, 943 insertions(+), 976 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/client_list.cc b/src/lib/datasrc/client_list.cc
index 103d53a..e862c39 100644
--- a/src/lib/datasrc/client_list.cc
+++ b/src/lib/datasrc/client_list.cc
@@ -141,13 +141,9 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
for (vector<string>::const_iterator it(zones_origins.begin());
it != zones_origins.end(); ++it) {
const Name origin(*it);
- shared_ptr<InMemoryZoneFinder>
- finder(new
- InMemoryZoneFinder(rrclass_, origin));
if (type == "MasterFiles") {
try {
- finder->load(paramConf->get(*it)->stringValue());
- cache->addZone(finder);
+ cache->load(origin, paramConf->get(*it)->stringValue());
} catch (const isc::dns::MasterLoadError& mle) {
LOG_ERROR(logger, DATASRC_MASTERLOAD_ERROR)
.arg(mle.what());
@@ -165,8 +161,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
isc_throw(isc::Unexpected, "Got NULL iterator "
"for zone " << origin);
}
- finder->load(*iterator);
- cache->addZone(finder);
+ cache->load(origin, *iterator);
}
}
}
@@ -331,7 +326,8 @@ ConfigurableClientList::reload(const Name& name) {
if (!info->cache_ || !finder) {
return (ZONE_NOT_CACHED);
}
- DataSourceClient* client(info->data_src_client_);
+ InMemoryClient* client =
+ dynamic_cast<InMemoryClient*>(info->data_src_client_);
if (client) {
// Now do the final reload. If it does not exist in client,
// DataSourceError is thrown, which is exactly the result what we
@@ -340,7 +336,7 @@ ConfigurableClientList::reload(const Name& name) {
if (!iterator) {
isc_throw(isc::Unexpected, "Null iterator from " << name);
}
- finder->load(*iterator);
+ client->load(name, *iterator);
} else {
// The MasterFiles special case
const string filename(finder->getFileName());
@@ -348,7 +344,7 @@ ConfigurableClientList::reload(const Name& name) {
isc_throw(isc::Unexpected, "Confused about missing both filename "
"and data source");
}
- finder->load(filename);
+ client->load(name, filename);
}
return (ZONE_RELOADED);
}
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index e5d6c9a..288ada0 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -855,866 +855,903 @@ private:
// 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),
+ InMemoryZoneFinderImpl(const InMemoryClient& client, const Name& origin) :
+ client_(client), origin_(origin),
zone_data_(new ZoneData(origin_))
{}
// Information about the zone
- RRClass zone_class_;
+ const InMemoryClient& client_;
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)
+ // 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
{
- 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);
+ 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());
}
- // 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());
+ // 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;
}
-
- /*
- * 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());
+ 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));
}
- // 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");
+ // 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));
}
- 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_);
+ // 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));
}
- // 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());
+ 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)));
}
- 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());
+ }
+
+ // 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));
}
- // 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());
+ 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));
}
+};
- 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());
- }
- }
+InMemoryZoneFinder::InMemoryZoneFinder(const InMemoryClient& client,
+ const Name& origin) :
+ impl_(new InMemoryZoneFinderImpl(client, origin))
+{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin).
+ arg(client.getClass());
+}
- // 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);
+InMemoryZoneFinder::~InMemoryZoneFinder() {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_DESTROY).arg(getOrigin()).
+ arg(getClass());
+ delete impl_;
+}
- 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());
- }
+Name
+InMemoryZoneFinder::getOrigin() const {
+ return (impl_->origin_);
+}
- // 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());
+RRClass
+InMemoryZoneFinder::getClass() const {
+ return (impl_->client_.getClass());
+}
+
+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_ << "/" << getClass());
+ }
+ 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_ << "/" << getClass());
+ }
+ 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_ << "/"
+ << getClass());
+ }
+
+ // 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));
}
+ }
- // 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);
+ isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
+ "a broken NSEC3 zone: " << impl_->origin_ << "/"
+ << getClass());
+}
- return (result::SUCCESS);
+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());
}
+}
- 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());
+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 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());
+ 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;
}
- 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);
+ // 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));
}
-
- 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 (match_wild) {
+ wild_rrsets->push_back(rrset);
+ }
+}
- if (rrset->getType() == RRType::NSEC3()) {
- return (addNSEC3(rrset, zone_data));
- }
+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).
- // 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));
+ 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));
}
+ }
+}
+}
- // 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);
+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_);
+}
- // 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();
- }
+const string
+InMemoryZoneFinder::getFileName() const {
+ return (impl_->file_name_);
+}
- // 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);
+/// 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(RRClass rrclass) :
+ rrclass_(rrclass),
+ zone_count(0),
+ zone_table_(ZoneTable::create(local_mem_sgmt, rrclass))
+ {}
+ ~InMemoryClientImpl() {
+ ZoneTable::destroy(local_mem_sgmt, zone_table_, rrclass_);
- // Try inserting the rrset there
- if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
- // Ok, we just put it in
+ // see above for the assert().
+ assert(local_mem_sgmt.allMemoryDeallocated());
+ }
- // 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);
- }
+ // 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;
+ RRClass rrclass_;
+ unsigned int zone_count;
+ ZoneTable* zone_table_;
- if (need_additionals != NULL &&
- (rrset->getType() == RRType::NS() ||
- rrset->getType() == RRType::MX())) {
- need_additionals->push_back(rrset.get());
- }
+ // 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.
+ result::Result load(const Name& zone_name, const string& filename,
+ boost::function<void(LoadCallback)> rrset_installer);
- // 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());
+ // 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);
- 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;
+ // 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);
}
- return (result::SUCCESS);
- } else {
- // The RRSet of given type was already there
- return (result::EXIST);
}
}
+ // 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());
+ }
+
/*
- * Same as above, but it checks the return value and if it already exists,
- * it throws.
+ * 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 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);
+ 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());
}
- }
- // 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);
- }
+ /*
+ * 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());
}
- 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;
+ // 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 (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;
- }
+ if (rrset->getRdataCount() == 0) {
+ isc_throw(AddError, "The rrset provided is empty: " <<
+ rrset->getName() << "/" << rrset->getType());
}
- 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));
+ // 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());
}
-
- // 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));
+ // 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");
}
- Domain::const_iterator found;
+ 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_);
+ }
- // 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)));
+ // 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());
}
}
- // 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));
+ // 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());
}
+ }
- 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));
+ 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());
}
}
- // 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_);
-}
+ // 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);
-ZoneFinderContextPtr
-InMemoryZoneFinder::find(const Name& name, const RRType& type,
- const FindOptions options)
-{
- return (ZoneFinderContextPtr(
- new Context(*this, options, impl_->find(name, type, NULL,
- options))));
-}
+ 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());
+ }
-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))));
-}
+ // 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());
+ }
-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");
+ // 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);
- 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_);
+ return (result::SUCCESS);
}
- // 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);
+ 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 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));
+ // 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());
}
- }
- isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
- "a broken NSEC3 zone: " << impl_->origin_ << "/"
- << impl_->zone_class_);
-}
+ string fst_label = rrset->getName().split(0, 1).toText(true);
+ transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
+ ::toupper);
-result::Result
-InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
- return (impl_->add(rrset, *impl_->zone_data_, NULL));
-}
+ // 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);
+ }
-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());
+ zone_data.nsec3_data_->map_.insert(
+ NSEC3Pair(fst_label, ConstRBNodeRRsetPtr(new RBNodeRRset(rrset))));
+ return (result::SUCCESS);
}
-}
-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)));
-}
+ /*
+ * 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);
-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.
+ // OK, can add the RRset.
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
+ arg(rawrrset->getName()).arg(rawrrset->getType()).arg(origin_);
- 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;
+ // ... 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));
}
- 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;
+
+ // 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));
}
- 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);
+
+ // 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();
}
- // 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));
+ // 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);
- 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);
+ // 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);
}
- 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 (need_additionals != NULL &&
+ (rrset->getType() == RRType::NS() ||
+ rrset->getType() == RRType::MX())) {
+ need_additionals->push_back(rrset.get());
+ }
- if (match_wild) {
- wild_rrsets->push_back(rrset);
- }
-}
+ // 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());
-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).
+ 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);
+ }
+ }
- 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));
+ /*
+ * 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);
}
}
-}
-}
+};
-void
-InMemoryZoneFinder::InMemoryZoneFinderImpl::load(
+result::Result
+InMemoryClient::InMemoryClientImpl::load(
+ const Name& zone_name,
const string& filename,
boost::function<void(LoadCallback)> rrset_installer)
{
vector<RBNodeRRset*> need_additionals;
- scoped_ptr<ZoneData> tmp(new ZoneData(origin_));
+ scoped_ptr<ZoneData> tmp(new ZoneData(zone_name));
- rrset_installer(boost::bind(&InMemoryZoneFinderImpl::addFromLoad, this,
+ rrset_installer(boost::bind(&InMemoryClientImpl::addFromLoad, this,
_1, tmp.get(), &need_additionals));
vector<RBNodeRRset*> wild_additionals;
@@ -1732,7 +1769,7 @@ InMemoryZoneFinder::InMemoryZoneFinderImpl::load(
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_);
+ arg(origin_).arg(client_.getClass());
}
}
@@ -1740,6 +1777,23 @@ InMemoryZoneFinder::InMemoryZoneFinderImpl::load(
file_name_ = filename;
tmp.swap(zone_data_);
// And let the old data die with tmp
+
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE).
+ arg(zone_name).arg(getClass().toText());
+
+ const ZoneTable::AddResult result =
+ impl_->zone_table_->addZone(impl_->local_mem_sgmt,
+ getClass(), zone_name);
+ if (result.code == result::SUCCESS) {
+ ++impl_->zone_count;
+ }
+
+ if (result.code == result::SUCCESS ||
+ result.code == result::EXIST) {
+ // Add the ZoneData here when it's ready.
+ }
+
+ return (result.code);
}
namespace {
@@ -1787,63 +1841,6 @@ generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
}
}
-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(RRClass rrclass) :
- rrclass_(rrclass),
- zone_count(0),
- zone_table(ZoneTable::create(local_mem_sgmt, rrclass))
- {}
- ~InMemoryClientImpl() {
- ZoneTable::destroy(local_mem_sgmt, zone_table, rrclass_);
-
- // 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;
- RRClass rrclass_;
- unsigned int zone_count;
- ZoneTable* zone_table;
-};
-
InMemoryClient::InMemoryClient(RRClass rrclass) :
impl_(new InMemoryClientImpl(rrclass))
{}
@@ -1862,29 +1859,36 @@ InMemoryClient::getZoneCount() const {
return (impl_->zone_count);
}
+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));
+}
+
result::Result
-InMemoryClient::addZone(ZoneFinderPtr zone_finder) {
- if (!zone_finder) {
- isc_throw(InvalidParameter,
- "Null pointer is passed to InMemoryClient::addZone()");
- }
+InMemoryClient::load(const isc::dns::Name& zone_name,
+ const std::string& filename) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
+ arg(filename);
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE).
- arg(zone_finder->getOrigin()).arg(zone_finder->getClass().toText());
+ return (impl_->load(zone_name, filename,
+ boost::bind(masterLoadWrapper, filename.c_str(),
+ getOrigin(), getClass(), _1)));
+}
- const result::Result result =
- impl_->zone_table->addZone(impl_->local_mem_sgmt, zone_finder);
- if (result == result::SUCCESS) {
- ++impl_->zone_count;
- }
- return (result);
+result::Result
+InMemoryClient::load(const isc::dns::Name& zone_name,
+ ZoneIterator& iterator) {
+ return (impl_->load(zone_name, string(),
+ boost::bind(generateRRsetFromIterator,
+ &iterator, _1)));
}
-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));
+result::Result
+InMemoryClient::add(const isc::dns::Name& zone_name,
+ const ConstRRsetPtr& rrset) {
+ return (impl_->add(rrset, *impl_->zone_data_, NULL));
}
namespace {
@@ -1989,7 +1993,7 @@ public:
ZoneIteratorPtr
InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
- ZoneTable::FindResult result(impl_->zone_table->findZone(name));
+ ZoneTable::FindResult result(impl_->zone_table_->findZone(name));
if (result.code != result::SUCCESS) {
isc_throw(DataSourceError, "No such zone: " + name.toText());
}
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index 375b386..3fe1194 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -28,14 +28,10 @@ class RRsetList;
};
namespace datasrc {
+class InMemoryClient;
/// 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 {
///
@@ -49,7 +45,7 @@ public:
///
/// \param rrclass The RR class of the zone.
/// \param origin The origin name of the zone.
- InMemoryZoneFinder(const isc::dns::RRClass& rrclass,
+ InMemoryZoneFinder(const InMemoryClient& client,
const isc::dns::Name& origin);
/// The destructor.
@@ -84,58 +80,6 @@ public:
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.
@@ -150,51 +94,6 @@ public:
/// 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.
///
@@ -278,20 +177,103 @@ public:
/// \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.
+ /// \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.
///
- /// \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.
+ /// \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.
+ result::Result load(const isc::dns::Name& zone_name, 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.
+ result::Result load(const isc::dns::Name& zone_name, ZoneIterator& iterator);
+
+ /// \brief Inserts an rrset into the zone.
+ ///
+ /// It puts another RRset into the zone.
///
- /// \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);
+ /// 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::Name& zone_name,
+ 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)
+ { }
+ };
/// Returns a \c ZoneFinder for a zone_finder that best matches the given
/// name.
diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc
index bf0812a..37e0f39 100644
--- a/src/lib/datasrc/memory_datasrc_link.cc
+++ b/src/lib/datasrc/memory_datasrc_link.cc
@@ -163,7 +163,6 @@ checkConfig(ConstElementPtr config, ElementPtr errors) {
// checked by the caller
void
applyConfig(isc::datasrc::InMemoryClient& client,
- isc::dns::RRClass rrclass,
isc::data::ConstElementPtr config_value)
{
ConstElementPtr zones_config = config_value->get("zones");
@@ -208,20 +207,6 @@ applyConfig(isc::datasrc::InMemoryClient& client,
// 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
@@ -229,11 +214,16 @@ applyConfig(isc::datasrc::InMemoryClient& client,
* 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)));
+ try {
+ if (filetype_txt == "text") {
+ client.load(Name(origin_txt), file_txt);
+ } else {
+ client.load(Name(origin_txt),
+ *container->getInstance().getIterator(Name(origin_txt)));
+ }
+ } catch (const isc::dns::NameParserException& ex) {
+ isc_throw(InMemoryConfigError, "unable to parse zone's origin: " <<
+ ex.what());
}
}
}
@@ -255,7 +245,7 @@ createInstance(isc::data::ConstElementPtr config, std::string& error) {
rrclass = RRClass(config->get("class")->stringValue());
}
std::auto_ptr<InMemoryClient> client(new isc::datasrc::InMemoryClient(rrclass));
- applyConfig(*client, rrclass, config);
+ applyConfig(*client, config);
return (client.release());
} catch (const isc::Exception& isce) {
error = isce.what();
diff --git a/src/lib/datasrc/static_datasrc_link.cc b/src/lib/datasrc/static_datasrc_link.cc
index ac8b80c..a8a5876 100644
--- a/src/lib/datasrc/static_datasrc_link.cc
+++ b/src/lib/datasrc/static_datasrc_link.cc
@@ -34,14 +34,9 @@ createInstance(ConstElementPtr config, string& error) {
try {
// Create the data source
auto_ptr<InMemoryClient> client(new InMemoryClient(RRClass::CH()));
- // Hardcode the origin and class
- shared_ptr<InMemoryZoneFinder>
- finder(new InMemoryZoneFinder(RRClass::CH(), Name("BIND")));
// Fill it with data
const string path(config->stringValue());
- finder->load(path);
- // And put the zone inside
- client->addZone(finder);
+ client->load(Name("BIND"), path);
return (client.release());
}
catch (const std::exception& e) {
More information about the bind10-changes
mailing list