BIND 10 master, updated. acb299784ddbf280aac6ee5a78977c9acbf1fd32 Merge #1177
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Sep 29 07:47:33 UTC 2011
The branch, master has been updated
via acb299784ddbf280aac6ee5a78977c9acbf1fd32 (commit)
via 000164d51a974acf3846a6b0a7795f484e915161 (commit)
via bc03b37015ab6ea23cbec70dbd299c74fb001aba (commit)
via e56e0f7d1ad206f1ebc26e285d82a8e7ff6390e1 (commit)
via 688867daa34ade5075443c77535f80e1d2d76743 (commit)
via d36ded7d95a695f0412f6ccdb59bf55fc600e9d3 (commit)
via b8e90124c19177e0b6b33bd624e244860e2424b3 (commit)
via 5cf1b7ab58c42675c1396fbbd5b1aaf037eb8d19 (commit)
via 17d9827aa40e363650d1698fddba9204f27b5171 (commit)
via 27f447c8b054b17d96abfba431568c1ffe017f0a (commit)
via 70bba1b3f811261fcef30694568245e83cd64bc5 (commit)
via 06a24c688282b61dd2ce5b6c00608bee34ae3563 (commit)
via b902e70583a9dfb1ee410e297e2da4c8b944ba8d (commit)
via 09349cf206ee9e68618713b97e621b7ef2a6c0a9 (commit)
via ff1bd2a00278bc753a7d035fd5020ff936df1882 (commit)
via c89f3a2f43fd7fe70bcb199fad0ccf94364b1ebe (commit)
via 4c86025464db4603ec07490169aaf4b77868057b (commit)
via 7a061c2e82d62e2b275cb5a8d7460dce7d36f050 (commit)
via a6cbb14cc9c986d109983087313225829f1c91fe (commit)
via 7cc32b7915532354ed7e2fd15f7ca5a9b9b64610 (commit)
via dd340b32df88083fdc17f682094b451f7dcdf6d6 (commit)
via 30c277567f64d09c11cadcb173eef066efdaea07 (commit)
via ec2793914d1090db8c8d94a2f9b92ed97b1a6cba (commit)
via a59c7f28a458842b4edce2d6639639b17a85eb9f (commit)
via 2a5c5383e3df0e625367bf85b740f62bf777b211 (commit)
via f16de89251e4607eb413df666a64022c50478a4c (commit)
via 4e93ba217318854742144bf0b8e30f4c3614db92 (commit)
via 38d80ef7186ac2b18ed234a825894f5f78fc90b1 (commit)
via 88bee2515653d3b5481608bc92a1956c7ea7cf48 (commit)
via e9286ce511be095f2b16b1b7bc503b1e4377593d (commit)
via 723a6d1f333f1d513d5e4fe26a8ee7611767c9fc (commit)
via 88fe1bafce118f40d256097c2bfbdf9e53553784 (commit)
via 51c4b53945599a72d550d7380c7107e11b467d5c (commit)
via 84d7ae48d44e055cb16e3900cf2c4b2262f6a6da (commit)
via 03e9f45f8a6584a373f1bd15f01f56d9296c842a (commit)
via cb4d8443645a5c3e973b4e2477198686d8d8c507 (commit)
via f847a5e079ceae0346b84fb320ed06ce9b443a63 (commit)
via 05512e090c6c3cb852cebdb85ae7c12e8001603b (commit)
via c35f6b15bb6b703154e05399266dd2051ef9cfa9 (commit)
via 3f2864bf1271ca525858cf3e1fa641e3496eec59 (commit)
via f8720ba467d8e107c512160a5502caf9be58a425 (commit)
via 38af8a4225e8c82564758e8a5629da438220bc87 (commit)
via c5e0db2b7d8fbdb13548e01310f623f131ea0e9c (commit)
via 26c7bfe851f00422beb442a77d25cc0887557b79 (commit)
via f5239632a06383f2b4f6825cb6a006ceb8bea417 (commit)
via 680f05c35753bf1f70392d25b1e6310cf46476ce (commit)
via b12351c21ee92a13536aa89331cc73bd166dbe5f (commit)
via 2e1dceedf6a4f661a8d7e57757b28f9f6cb1a9b3 (commit)
via df69ad0d0231218610f68ecb2b1953ae7f28fa68 (commit)
via 5b713ea8e5fd35fdb1ab7ff953e010ef9b60f98c (commit)
from 250ce2abb3d6b48fce778b5e0c651d57582aff7c (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 acb299784ddbf280aac6ee5a78977c9acbf1fd32
Merge: 250ce2abb3d6b48fce778b5e0c651d57582aff7c 000164d51a974acf3846a6b0a7795f484e915161
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Thu Sep 29 09:41:47 2011 +0200
Merge #1177
-----------------------------------------------------------------------
Summary of changes:
src/bin/auth/query.cc | 7 +
src/bin/auth/tests/query_unittest.cc | 4 +
src/lib/datasrc/database.cc | 469 ++++++++++++++------
src/lib/datasrc/database.h | 126 ++++--
src/lib/datasrc/datasrc_messages.mes | 5 +
src/lib/datasrc/memory_datasrc.cc | 6 +
src/lib/datasrc/memory_datasrc.h | 6 +
src/lib/datasrc/sqlite3_accessor.cc | 105 ++++--
src/lib/datasrc/sqlite3_accessor.h | 4 +
src/lib/datasrc/tests/database_unittest.cc | 279 +++++++++++-
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 8 +
src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 39 ++
src/lib/datasrc/zone.h | 73 +++-
13 files changed, 904 insertions(+), 227 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 898fff7..ab6404e 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -253,6 +253,13 @@ Query::process() const {
// Just empty answer with SOA in authority section
putSOA(*result.zone_finder);
break;
+ default:
+ // These are new result codes (WILDCARD and WILDCARD_NXRRSET)
+ // They should not happen from the in-memory and the database
+ // backend isn't used yet.
+ // TODO: Implement before letting the database backends in
+ isc_throw(isc::NotImplemented, "Unknown result code");
+ break;
}
}
}
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 4b8f013..b2d1094 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -141,6 +141,10 @@ public:
// Turn this on if you want it to return RRSIGs regardless of FIND_GLUE_OK
void setIncludeRRSIGAnyway(bool on) { include_rrsig_anyway_ = on; }
+ Name findPreviousName(const Name&) const {
+ isc_throw(isc::NotImplemented, "Mock doesn't support previous name");
+ }
+
private:
typedef map<RRType, ConstRRsetPtr> RRsetStore;
typedef map<Name, RRsetStore> Domains;
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 2c5aaeb..e476297 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -174,105 +174,42 @@ private:
};
}
-std::pair<bool, isc::dns::RRsetPtr>
-DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
- const isc::dns::RRType* type,
- bool want_cname, bool want_dname,
- bool want_ns,
- const isc::dns::Name* construct_name)
+DatabaseClient::Finder::FoundRRsets
+DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
+ bool check_ns, const string* construct_name)
{
RRsigStore sig_store;
bool records_found = false;
- isc::dns::RRsetPtr result_rrset;
+ std::map<RRType, RRsetPtr> result;
// Request the context
DatabaseAccessor::IteratorContextPtr
- context(accessor_->getRecords(name.toText(), zone_id_));
+ context(accessor_->getRecords(name, zone_id_));
// It must not return NULL, that's a bug of the implementation
if (!context) {
- isc_throw(isc::Unexpected, "Iterator context null at " +
- name.toText());
+ isc_throw(isc::Unexpected, "Iterator context null at " + name);
}
std::string columns[DatabaseAccessor::COLUMN_COUNT];
if (construct_name == NULL) {
construct_name = &name;
}
+
+ const Name construct_name_object(*construct_name);
+
+ bool seen_cname(false);
+ bool seen_ds(false);
+ bool seen_other(false);
+ bool seen_ns(false);
+
while (context->getNext(columns)) {
- if (!records_found) {
- records_found = true;
- }
+ // The domain is not empty
+ records_found = true;
try {
- const isc::dns::RRType cur_type(columns[DatabaseAccessor::
- TYPE_COLUMN]);
- const isc::dns::RRTTL cur_ttl(columns[DatabaseAccessor::
- TTL_COLUMN]);
- // Ths sigtype column was an optimization for finding the
- // relevant RRSIG RRs for a lookup. Currently this column is
- // not used in this revised datasource implementation. We
- // should either start using it again, or remove it from use
- // completely (i.e. also remove it from the schema and the
- // backend implementation).
- // Note that because we don't use it now, we also won't notice
- // it if the value is wrong (i.e. if the sigtype column
- // contains an rrtype that is different from the actual value
- // of the 'type covered' field in the RRSIG Rdata).
- //cur_sigtype(columns[SIGTYPE_COLUMN]);
-
- // Check for delegations before checking for the right type.
- // This is needed to properly delegate request for the NS
- // record itself.
- //
- // This happens with NS only, CNAME must be alone and DNAME
- // is not checked in the exact queried domain.
- if (want_ns && cur_type == isc::dns::RRType::NS()) {
- if (result_rrset &&
- result_rrset->getType() != isc::dns::RRType::NS()) {
- isc_throw(DataSourceError, "NS found together with data"
- " in non-apex domain " + name.toText());
- }
- addOrCreate(result_rrset, *construct_name, getClass(),
- cur_type, cur_ttl,
- columns[DatabaseAccessor::RDATA_COLUMN],
- *accessor_);
- } else if (type != NULL && cur_type == *type) {
- if (result_rrset &&
- result_rrset->getType() == isc::dns::RRType::CNAME()) {
- isc_throw(DataSourceError, "CNAME found but it is not "
- "the only record for " + name.toText());
- } else if (result_rrset && want_ns &&
- result_rrset->getType() == isc::dns::RRType::NS()) {
- isc_throw(DataSourceError, "NS found together with data"
- " in non-apex domain " + name.toText());
- }
- addOrCreate(result_rrset, *construct_name, getClass(),
- cur_type, cur_ttl,
- columns[DatabaseAccessor::RDATA_COLUMN],
- *accessor_);
- } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) {
- // There should be no other data, so result_rrset should
- // be empty.
- if (result_rrset) {
- isc_throw(DataSourceError, "CNAME found but it is not "
- "the only record for " + name.toText());
- }
- addOrCreate(result_rrset, *construct_name, getClass(),
- cur_type, cur_ttl,
- columns[DatabaseAccessor::RDATA_COLUMN],
- *accessor_);
- } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) {
- // There should be max one RR of DNAME present
- if (result_rrset &&
- result_rrset->getType() == isc::dns::RRType::DNAME()) {
- isc_throw(DataSourceError, "DNAME with multiple RRs in " +
- name.toText());
- }
- addOrCreate(result_rrset, *construct_name, getClass(),
- cur_type, cur_ttl,
- columns[DatabaseAccessor::RDATA_COLUMN],
- *accessor_);
- } else if (cur_type == isc::dns::RRType::RRSIG()) {
+ const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]);
+
+ if (cur_type == RRType::RRSIG()) {
// If we get signatures before we get the actual data, we
// can't know which ones to keep and which to drop...
// So we keep a separate store of any signature that may be
@@ -280,27 +217,76 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
// done.
// A possible optimization here is to not store them for
// types we are certain we don't need
- sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
- getClass(), columns[DatabaseAccessor::RDATA_COLUMN]));
+ sig_store.addSig(rdata::createRdata(cur_type, getClass(),
+ columns[DatabaseAccessor::RDATA_COLUMN]));
+ }
+
+ if (types.find(cur_type) != types.end()) {
+ // This type is requested, so put it into result
+ const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
+ // Ths sigtype column was an optimization for finding the
+ // relevant RRSIG RRs for a lookup. Currently this column is
+ // not used in this revised datasource implementation. We
+ // should either start using it again, or remove it from use
+ // completely (i.e. also remove it from the schema and the
+ // backend implementation).
+ // Note that because we don't use it now, we also won't notice
+ // it if the value is wrong (i.e. if the sigtype column
+ // contains an rrtype that is different from the actual value
+ // of the 'type covered' field in the RRSIG Rdata).
+ //cur_sigtype(columns[SIGTYPE_COLUMN]);
+ addOrCreate(result[cur_type], construct_name_object,
+ getClass(), cur_type, cur_ttl,
+ columns[DatabaseAccessor::RDATA_COLUMN],
+ *accessor_);
}
- } catch (const isc::dns::InvalidRRType& irt) {
+
+ if (cur_type == RRType::CNAME()) {
+ seen_cname = true;
+ } else if (cur_type == RRType::NS()) {
+ seen_ns = true;
+ } else if (cur_type == RRType::DS()) {
+ seen_ds = true;
+ } else if (cur_type != RRType::RRSIG() &&
+ cur_type != RRType::NSEC3() &&
+ cur_type != RRType::NSEC()) {
+ // NSEC and RRSIG can coexist with anything, otherwise
+ // we've seen something that can't live together with potential
+ // CNAME or NS
+ //
+ // NSEC3 lives in separate namespace from everything, therefore
+ // we just ignore it here for these checks as well.
+ seen_other = true;
+ }
+ } catch (const InvalidRRType&) {
isc_throw(DataSourceError, "Invalid RRType in database for " <<
name << ": " << columns[DatabaseAccessor::
TYPE_COLUMN]);
- } catch (const isc::dns::InvalidRRTTL& irttl) {
+ } catch (const InvalidRRTTL&) {
isc_throw(DataSourceError, "Invalid TTL in database for " <<
name << ": " << columns[DatabaseAccessor::
TTL_COLUMN]);
- } catch (const isc::dns::rdata::InvalidRdataText& ird) {
+ } catch (const rdata::InvalidRdataText&) {
isc_throw(DataSourceError, "Invalid rdata in database for " <<
name << ": " << columns[DatabaseAccessor::
RDATA_COLUMN]);
}
}
- if (result_rrset) {
- sig_store.appendSignatures(result_rrset);
+ if (seen_cname && (seen_other || seen_ns || seen_ds)) {
+ isc_throw(DataSourceError, "CNAME shares domain " << name <<
+ " with something else");
+ }
+ if (check_ns && seen_ns && seen_other) {
+ isc_throw(DataSourceError, "NS shares domain " << name <<
+ " with something else");
}
- return (std::pair<bool, isc::dns::RRsetPtr>(records_found, result_rrset));
+ // Add signatures to all found RRsets
+ for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
+ i != result.end(); ++ i) {
+ sig_store.appendSignatures(i->second);
+ }
+
+ return (FoundRRsets(records_found, result));
}
bool
@@ -317,6 +303,92 @@ DatabaseClient::Finder::hasSubdomains(const std::string& name) {
return (context->getNext(columns));
}
+// Some manipulation with RRType sets
+namespace {
+
+// Bunch of functions to construct specific sets of RRTypes we will
+// ask from it.
+typedef std::set<RRType> WantedTypes;
+
+const WantedTypes&
+NSEC_TYPES() {
+ static bool initialized(false);
+ static WantedTypes result;
+
+ if (!initialized) {
+ result.insert(RRType::NSEC());
+ initialized = true;
+ }
+ return (result);
+}
+
+const WantedTypes&
+DELEGATION_TYPES() {
+ static bool initialized(false);
+ static WantedTypes result;
+
+ if (!initialized) {
+ result.insert(RRType::DNAME());
+ result.insert(RRType::NS());
+ initialized = true;
+ }
+ return (result);
+}
+
+const WantedTypes&
+FINAL_TYPES() {
+ static bool initialized(false);
+ static WantedTypes result;
+
+ if (!initialized) {
+ result.insert(RRType::CNAME());
+ result.insert(RRType::NS());
+ result.insert(RRType::NSEC());
+ initialized = true;
+ }
+ return (result);
+}
+
+}
+
+RRsetPtr
+DatabaseClient::Finder::findNSECCover(const Name& name) {
+ try {
+ // Which one should contain the NSEC record?
+ const Name coverName(findPreviousName(name));
+ // Get the record and copy it out
+ const FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(),
+ coverName != getOrigin());
+ const FoundIterator
+ nci(found.second.find(RRType::NSEC()));
+ if (nci != found.second.end()) {
+ return (nci->second);
+ } else {
+ // The previous doesn't contain NSEC.
+ // Badly signed zone or a bug?
+
+ // FIXME: Currently, if the zone is not signed, we could get
+ // here. In that case we can't really throw, but for now, we can't
+ // recognize it. So we don't throw at all, enable it once
+ // we have a is_signed flag or something.
+#if 0
+ isc_throw(DataSourceError, "No NSEC in " +
+ coverName.toText() + ", but it was "
+ "returned as previous - "
+ "accessor error? Badly signed zone?");
+#endif
+ }
+ }
+ catch (const isc::NotImplemented&) {
+ // Well, they want DNSSEC, but there is no available.
+ // So we don't provide anything.
+ LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
+ arg(accessor_->getDBName()).arg(name);
+ }
+ // We didn't find it, return nothing
+ return (RRsetPtr());
+}
+
ZoneFinder::FindResult
DatabaseClient::Finder::find(const isc::dns::Name& name,
const isc::dns::RRType& type,
@@ -326,10 +398,12 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
// This variable is used to determine the difference between
// NXDOMAIN and NXRRSET
bool records_found = false;
- bool glue_ok(options & FIND_GLUE_OK);
+ bool glue_ok((options & FIND_GLUE_OK) != 0);
+ const bool dnssec_data((options & FIND_DNSSEC) != 0);
+ bool get_cover(false);
isc::dns::RRsetPtr result_rrset;
ZoneFinder::Result result_status = SUCCESS;
- std::pair<bool, isc::dns::RRsetPtr> found;
+ FoundRRsets found;
logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
.arg(accessor_->getDBName()).arg(name).arg(type);
// In case we are in GLUE_OK mode and start matching wildcards,
@@ -337,11 +411,11 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
isc::dns::RRsetPtr first_ns;
// First, do we have any kind of delegation (NS/DNAME) here?
- Name origin(getOrigin());
- size_t origin_label_count(origin.getLabelCount());
+ const Name origin(getOrigin());
+ const size_t origin_label_count(origin.getLabelCount());
// Number of labels in the last known non-empty domain
size_t last_known(origin_label_count);
- size_t current_label_count(name.getLabelCount());
+ const size_t current_label_count(name.getLabelCount());
// This is how many labels we remove to get origin
size_t remove_labels(current_label_count - origin_label_count);
@@ -349,35 +423,44 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
for (int i(remove_labels); i > 0; --i) {
Name superdomain(name.split(i));
// Look if there's NS or DNAME (but ignore the NS in origin)
- found = getRRset(superdomain, NULL, false, true,
- i != remove_labels && !glue_ok);
+ found = getRRsets(superdomain.toText(), DELEGATION_TYPES(),
+ i != remove_labels);
if (found.first) {
// It contains some RRs, so it exists.
last_known = superdomain.getLabelCount();
- // In case we are in GLUE_OK, we want to store the highest
- // encountered RRset.
- if (glue_ok && !first_ns && i != remove_labels) {
- first_ns = getRRset(superdomain, NULL, false, false,
- true).second;
- }
- }
- if (found.second) {
- // We found something redirecting somewhere else
- // (it can be only NS or DNAME here)
- result_rrset = found.second;
- if (result_rrset->getType() == isc::dns::RRType::NS()) {
+
+ const FoundIterator nsi(found.second.find(RRType::NS()));
+ const FoundIterator dni(found.second.find(RRType::DNAME()));
+ // In case we are in GLUE_OK mode, we want to store the
+ // highest encountered NS (but not apex)
+ if (glue_ok && !first_ns && i != remove_labels &&
+ nsi != found.second.end()) {
+ first_ns = nsi->second;
+ } else if (!glue_ok && i != remove_labels &&
+ nsi != found.second.end()) {
+ // Do a NS delegation, but ignore NS in glue_ok mode. Ignore
+ // delegation in apex
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DELEGATION).
arg(accessor_->getDBName()).arg(superdomain);
+ result_rrset = nsi->second;
result_status = DELEGATION;
- } else {
+ // No need to go lower, found
+ break;
+ } else if (dni != found.second.end()) {
+ // Very similar with DNAME
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DNAME).
arg(accessor_->getDBName()).arg(superdomain);
+ result_rrset = dni->second;
result_status = DNAME;
+ if (result_rrset->getRdataCount() != 1) {
+ isc_throw(DataSourceError, "DNAME at " << superdomain <<
+ " has " << result_rrset->getRdataCount() <<
+ " rdata, 1 expected");
+ }
+ break;
}
- // Don't search more
- break;
}
}
@@ -385,21 +468,37 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
// Try getting the final result and extract it
// It is special if there's a CNAME or NS, DNAME is ignored here
// And we don't consider the NS in origin
- found = getRRset(name, &type, true, false, name != origin && !glue_ok);
+
+ WantedTypes final_types(FINAL_TYPES());
+ final_types.insert(type);
+ found = getRRsets(name.toText(), final_types, name != origin);
records_found = found.first;
- result_rrset = found.second;
- if (result_rrset && name != origin && !glue_ok &&
- result_rrset->getType() == isc::dns::RRType::NS()) {
+
+ // NS records, CNAME record and Wanted Type records
+ const FoundIterator nsi(found.second.find(RRType::NS()));
+ const FoundIterator cni(found.second.find(RRType::CNAME()));
+ const FoundIterator wti(found.second.find(type));
+ if (name != origin && !glue_ok && nsi != found.second.end()) {
+ // There's a delegation at the exact node.
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DELEGATION_EXACT).
arg(accessor_->getDBName()).arg(name);
result_status = DELEGATION;
- } else if (result_rrset && type != isc::dns::RRType::CNAME() &&
- result_rrset->getType() == isc::dns::RRType::CNAME()) {
+ result_rrset = nsi->second;
+ } else if (type != isc::dns::RRType::CNAME() &&
+ cni != found.second.end()) {
+ // A CNAME here
result_status = CNAME;
- }
-
- if (!result_rrset && !records_found) {
+ result_rrset = cni->second;
+ if (result_rrset->getRdataCount() != 1) {
+ isc_throw(DataSourceError, "CNAME with " <<
+ result_rrset->getRdataCount() <<
+ " rdata at " << name << ", expected 1");
+ }
+ } else if (wti != found.second.end()) {
+ // Just get the answer
+ result_rrset = wti->second;
+ } else if (!records_found) {
// Nothing lives here.
// But check if something lives below this
// domain and if so, pretend something is here as well.
@@ -408,23 +507,22 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
arg(accessor_->getDBName()).arg(name);
records_found = true;
+ get_cover = dnssec_data;
} else {
// It's not empty non-terminal. So check for wildcards.
// We remove labels one by one and look for the wildcard there.
// Go up to first non-empty domain.
remove_labels = current_label_count - last_known;
- Name star("*");
for (size_t i(1); i <= remove_labels; ++ i) {
// Construct the name with *
- // TODO: Once the underlying DatabaseAccessor takes
- // string, do the concatenation on strings, not
- // Names
- Name superdomain(name.split(i));
- Name wildcard(star.concatenate(superdomain));
+ const Name superdomain(name.split(i));
+ const string wildcard("*." + superdomain.toText());
+ const string construct_name(name.toText());
// TODO What do we do about DNAME here?
- found = getRRset(wildcard, &type, true, false, true,
- &name);
+ // The types are the same as with original query
+ found = getRRsets(wildcard, final_types, true,
+ &construct_name);
if (found.first) {
if (first_ns) {
// In case we are under NS, we don't
@@ -445,7 +543,42 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
// domain, but it could be empty non-terminal. In
// that case, we need to cancel the match.
records_found = true;
- result_rrset = found.second;
+ const FoundIterator
+ cni(found.second.find(RRType::CNAME()));
+ const FoundIterator
+ nsi(found.second.find(RRType::NS()));
+ const FoundIterator
+ nci(found.second.find(RRType::NSEC()));
+ const FoundIterator wti(found.second.find(type));
+ if (cni != found.second.end() &&
+ type != RRType::CNAME()) {
+ result_rrset = cni->second;
+ result_status = CNAME;
+ } else if (nsi != found.second.end()) {
+ result_rrset = nsi->second;
+ result_status = DELEGATION;
+ } else if (wti != found.second.end()) {
+ result_rrset = wti->second;
+ result_status = WILDCARD;
+ } else {
+ // NXRRSET case in the wildcard
+ result_status = WILDCARD_NXRRSET;
+ if (dnssec_data &&
+ nci != found.second.end()) {
+ // User wants a proof the wildcard doesn't
+ // contain it
+ //
+ // However, we need to get the RRset in the
+ // name of the wildcard, not the constructed
+ // one, so we walk it again
+ found = getRRsets(wildcard, NSEC_TYPES(),
+ true);
+ result_rrset =
+ found.second.find(RRType::NSEC())->
+ second;
+ }
+ }
+
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD).
arg(accessor_->getDBName()).arg(wildcard).
@@ -457,33 +590,63 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
arg(name).arg(superdomain);
}
break;
- } else if (hasSubdomains(wildcard.toText())) {
+ } else if (hasSubdomains(wildcard)) {
// Empty non-terminal asterisk
records_found = true;
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD_EMPTY).
arg(accessor_->getDBName()).arg(wildcard).
arg(name);
+ if (dnssec_data) {
+ result_rrset = findNSECCover(Name(wildcard));
+ if (result_rrset) {
+ result_status = WILDCARD_NXRRSET;
+ }
+ }
break;
}
}
+ // This is the NXDOMAIN case (nothing found anywhere). If
+ // they want DNSSEC data, try getting the NSEC record
+ if (dnssec_data && !records_found) {
+ get_cover = true;
+ }
+ }
+ } else if (dnssec_data) {
+ // This is the "usual" NXRRSET case
+ // So in case they want DNSSEC, provide the NSEC
+ // (which should be available already here)
+ result_status = NXRRSET;
+ const FoundIterator nci(found.second.find(RRType::NSEC()));
+ if (nci != found.second.end()) {
+ result_rrset = nci->second;
}
}
}
if (!result_rrset) {
- if (records_found) {
- logger.debug(DBG_TRACE_DETAILED,
- DATASRC_DATABASE_FOUND_NXRRSET)
- .arg(accessor_->getDBName()).arg(name)
- .arg(getClass()).arg(type);
- result_status = NXRRSET;
- } else {
- logger.debug(DBG_TRACE_DETAILED,
- DATASRC_DATABASE_FOUND_NXDOMAIN)
- .arg(accessor_->getDBName()).arg(name)
- .arg(getClass()).arg(type);
- result_status = NXDOMAIN;
+ if (result_status == SUCCESS) {
+ // Should we look for NSEC covering the name?
+ if (get_cover) {
+ result_rrset = findNSECCover(name);
+ if (result_rrset) {
+ result_status = NXDOMAIN;
+ }
+ }
+ // Something is not here and we didn't decide yet what
+ if (records_found) {
+ logger.debug(DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_FOUND_NXRRSET)
+ .arg(accessor_->getDBName()).arg(name)
+ .arg(getClass()).arg(type);
+ result_status = NXRRSET;
+ } else {
+ logger.debug(DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_FOUND_NXDOMAIN)
+ .arg(accessor_->getDBName()).arg(name)
+ .arg(getClass()).arg(type);
+ result_status = NXDOMAIN;
+ }
}
} else {
logger.debug(DBG_TRACE_DETAILED,
@@ -494,6 +657,26 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
}
Name
+DatabaseClient::Finder::findPreviousName(const Name& name) const {
+ const string str(accessor_->findPreviousName(zone_id_,
+ name.reverse().toText()));
+ try {
+ return (Name(str));
+ }
+ /*
+ * To avoid having the same code many times, we just catch all the
+ * exceptions and handle them in a common code below
+ */
+ catch (const isc::dns::EmptyLabel&) {}
+ catch (const isc::dns::TooLongLabel&) {}
+ catch (const isc::dns::BadLabelType&) {}
+ catch (const isc::dns::BadEscape&) {}
+ catch (const isc::dns::TooLongName&) {}
+ catch (const isc::dns::IncompleteName&) {}
+ isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName");
+}
+
+Name
DatabaseClient::Finder::getOrigin() const {
return (origin_);
}
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 82918ac..8295779 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -28,6 +28,9 @@
#include <dns/name.h>
#include <exceptions/exceptions.h>
+#include <map>
+#include <set>
+
namespace isc {
namespace datasrc {
@@ -471,6 +474,34 @@ public:
* \return the name of the database
*/
virtual const std::string& getDBName() const = 0;
+
+ /**
+ * \brief It returns the previous name in DNSSEC order.
+ *
+ * This is used in DatabaseClient::findPreviousName and does more
+ * or less the real work, except for working on strings.
+ *
+ * \param rname The name to ask for previous of, in reversed form.
+ * We use the reversed form (see isc::dns::Name::reverse),
+ * because then the case insensitive order of string representation
+ * and the DNSSEC order correspond (eg. org.example.a is followed
+ * by org.example.a.b which is followed by org.example.b, etc).
+ * \param zone_id The zone to look through.
+ * \return The previous name.
+ * \note This function must return previous name even in case
+ * the queried rname does not exist in the zone.
+ * \note This method must skip under-the-zone-cut data (glue data).
+ * This might be implemented by looking for NSEC records (as glue
+ * data don't have them) in the zone or in some other way.
+ *
+ * \throw DataSourceError if there's a problem with the database.
+ * \throw NotImplemented if this database doesn't support DNSSEC
+ * or there's no previous name for the queried one (the NSECs
+ * might be missing or the queried name is less or equal the
+ * apex of the zone).
+ */
+ virtual std::string findPreviousName(int zone_id,
+ const std::string& rname) const = 0;
};
/**
@@ -587,6 +618,12 @@ public:
const FindOptions options = FIND_DEFAULT);
/**
+ * \brief Implementation of ZoneFinder::findPreviousName method.
+ */
+ virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
+ const;
+
+ /**
* \brief The zone ID
*
* This function provides the stored zone ID as passed to the
@@ -609,54 +646,42 @@ public:
boost::shared_ptr<DatabaseAccessor> accessor_;
const int zone_id_;
const isc::dns::Name origin_;
-
+ //
+ /// \brief Shortcut name for the result of getRRsets
+ typedef std::pair<bool, std::map<dns::RRType, dns::RRsetPtr> >
+ FoundRRsets;
+ /// \brief Just shortcut for set of types
+ typedef std::set<dns::RRType> WantedTypes;
/**
- * \brief Searches database for an RRset
+ * \brief Searches database for RRsets of one domain.
*
- * This method scans RRs of single domain specified by name and finds
- * RRset with given type or any of redirection RRsets that are
- * requested.
+ * This method scans RRs of single domain specified by name and
+ * extracts any RRsets found and requested by parameters.
*
- * This function is used internally by find(), because this part is
- * called multiple times with slightly different parameters.
+ * It is used internally by find(), because it is called multiple
+ * times (usually with different domains).
*
* \param name Which domain name should be scanned.
- * \param type The RRType which is requested. This can be NULL, in
- * which case the method will look for the redirections only.
- * \param want_cname If this is true, CNAME redirection may be returned
- * instead of the RRset with given type. If there's CNAME and
- * something else or the CNAME has multiple RRs, it throws
- * DataSourceError.
- * \param want_dname If this is true, DNAME redirection may be returned
- * instead. This is with type = NULL only and is not checked in
- * other circumstances. If the DNAME has multiple RRs, it throws
- * DataSourceError.
- * \param want_ns This allows redirection by NS to be returned. If
- * any other data is met as well, DataSourceError is thrown.
- * \param construct_name If set to non-NULL, the resulting RRset will
- * be constructed for this name instead of the queried one. This
- * is useful for wildcards.
- * \note It may happen that some of the above error conditions are not
- * detected in some circumstances. The goal here is not to validate
- * the domain in DB, but to avoid bad behaviour resulting from
- * broken data.
- * \return First part of the result tells if the domain contains any
- * RRs. This can be used to decide between NXDOMAIN and NXRRSET.
- * The second part is the RRset found (if any) with any relevant
- * signatures attached to it.
- * \todo This interface doesn't look very elegant. Any better idea
- * would be nice.
+ * \param types List of types the caller is interested in.
+ * \param check_ns If this is set to true, it checks nothing lives
+ * together with NS record (with few little exceptions, like RRSIG
+ * or NSEC). This check is meant for non-apex NS records.
+ * \param construct_name If this is NULL, the resulting RRsets have
+ * their name set to name. If it is not NULL, it overrides the name
+ * and uses this one (this can be used for wildcard synthesized
+ * records).
+ * \return A pair, where the first element indicates if the domain
+ * contains any RRs at all (not only the requested, it may happen
+ * this is set to true, but the second part is empty). The second
+ * part is map from RRtypes to RRsets of the corresponding types.
+ * If the RRset is not present in DB, the RRtype is not there at
+ * all (so you'll not find NULL pointer in the result).
+ * \throw DataSourceError If there's a low-level error with the
+ * database or the database contains bad data.
*/
- std::pair<bool, isc::dns::RRsetPtr> getRRset(const isc::dns::Name&
- name,
- const isc::dns::RRType*
- type,
- bool want_cname,
- bool want_dname,
- bool want_ns, const
- isc::dns::Name*
- construct_name = NULL);
-
+ FoundRRsets getRRsets(const std::string& name,
+ const WantedTypes& types, bool check_ns,
+ const std::string* construct_name = NULL);
/**
* \brief Checks if something lives below this domain.
*
@@ -666,6 +691,23 @@ public:
* \param name The domain to check.
*/
bool hasSubdomains(const std::string& name);
+
+ /**
+ * \brief Get the NSEC covering a name.
+ *
+ * This one calls findPreviousName on the given name and extracts an NSEC
+ * record on the result. It handles various error cases. The method exists
+ * to share code present at more than one location.
+ */
+ dns::RRsetPtr findNSECCover(const dns::Name& name);
+
+ /**
+ * \brief Convenience type shortcut.
+ *
+ * To find stuff in the result of getRRsets.
+ */
+ typedef std::map<dns::RRType, dns::RRsetPtr>::const_iterator
+ FoundIterator;
};
/**
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index efb88fd..04ad610 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -63,6 +63,11 @@ The maximum allowed number of items of the hotspot cache is set to the given
number. If there are too many, some of them will be dropped. The size of 0
means no limit.
+% DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED %1 doesn't support DNSSEC when asked for NSEC data covering %2
+The datasource tried to provide an NSEC proof that the named domain does not
+exist, but the database backend doesn't support DNSSEC. No proof is included
+in the answer as a result.
+
% DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3
Debug information. The database data source is looking up records with the given
name and type in the database.
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index 630b1c0..2e94b67 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -661,6 +661,12 @@ InMemoryZoneFinder::getFileName() const {
return (impl_->file_name_);
}
+isc::dns::Name
+InMemoryZoneFinder::findPreviousName(const isc::dns::Name&) const {
+ isc_throw(NotImplemented, "InMemory data source doesn't support DNSSEC "
+ "yet, can't find previous name");
+}
+
/// Implementation details for \c InMemoryClient hidden from the public
/// interface.
///
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index c569548..95f589a 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -75,6 +75,12 @@ public:
isc::dns::RRsetList* target = NULL,
const FindOptions options = FIND_DEFAULT);
+ /// \brief Imelementation of the ZoneFinder::findPreviousName method
+ ///
+ /// This one throws NotImplemented exception, as InMemory doesn't
+ /// support DNSSEC currently.
+ virtual isc::dns::Name findPreviousName(const isc::dns::Name& query) const;
+
/// \brief Inserts an rrset into the zone.
///
/// It puts another RRset into the zone.
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 956f447..69d5649 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -47,7 +47,8 @@ enum StatementID {
ADD_RECORD = 7,
DEL_RECORD = 8,
ITERATE = 9,
- NUM_STATEMENTS = 10
+ FIND_PREVIOUS = 10,
+ NUM_STATEMENTS = 11
};
const char* const text_statements[NUM_STATEMENTS] = {
@@ -68,7 +69,15 @@ const char* const text_statements[NUM_STATEMENTS] = {
"DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
"AND rdtype=?3 AND rdata=?4",
"SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE
- "WHERE zone_id = ?1 ORDER BY name, rdtype"
+ "WHERE zone_id = ?1 ORDER BY name, rdtype",
+ /*
+ * This one looks for previous name with NSEC record. It is done by
+ * using the reversed name. The NSEC is checked because we need to
+ * skip glue data, which don't have the NSEC.
+ */
+ "SELECT name FROM records " // FIND_PREVIOUS
+ "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
+ "rname < $2 ORDER BY rname DESC LIMIT 1"
};
struct SQLite3Parameters {
@@ -391,6 +400,28 @@ SQLite3Accessor::getZone(const std::string& name) const {
return (std::pair<bool, int>(false, 0));
}
+namespace {
+
+// Conversion to plain char
+const char*
+convertToPlainChar(const unsigned char* ucp, sqlite3 *db) {
+ if (ucp == NULL) {
+ // The field can really be NULL, in which case we return an
+ // empty string, or sqlite may have run out of memory, in
+ // which case we raise an error
+ if (sqlite3_errcode(db) == SQLITE_NOMEM) {
+ isc_throw(DataSourceError,
+ "Sqlite3 backend encountered a memory allocation "
+ "error in sqlite3_column_text()");
+ } else {
+ return ("");
+ }
+ }
+ const void* p = ucp;
+ return (static_cast<const char*>(p));
+}
+
+}
class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext {
public:
// Construct an iterator for all records. When constructed this
@@ -468,7 +499,8 @@ private:
void copyColumn(std::string (&data)[COLUMN_COUNT], int column) {
data[column] = convertToPlainChar(sqlite3_column_text(statement_,
- column));
+ column),
+ accessor_->dbparameters_->db_);
}
void bindZoneId(const int zone_id) {
@@ -495,29 +527,6 @@ private:
statement_ = NULL;
}
- // This helper method converts from the unsigned char* type (used by
- // sqlite3) to char* (wanted by std::string). Technically these types
- // might not be directly convertable
- // In case sqlite3_column_text() returns NULL, we just make it an
- // empty string, unless it was caused by a memory error
- const char* convertToPlainChar(const unsigned char* ucp) {
- if (ucp == NULL) {
- // The field can really be NULL, in which case we return an
- // empty string, or sqlite may have run out of memory, in
- // which case we raise an error
- if (sqlite3_errcode(accessor_->dbparameters_->db_)
- == SQLITE_NOMEM) {
- isc_throw(DataSourceError,
- "Sqlite3 backend encountered a memory allocation "
- "error in sqlite3_column_text()");
- } else {
- return ("");
- }
- }
- const void* p = ucp;
- return (static_cast<const char*>(p));
- }
-
const IteratorType iterator_type_;
boost::shared_ptr<const SQLite3Accessor> accessor_;
sqlite3_stmt *statement_;
@@ -658,5 +667,49 @@ SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
*dbparameters_, DEL_RECORD, params, "delete record from zone");
}
+std::string
+SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
+ const
+{
+ sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
+ sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS]);
+
+ if (sqlite3_bind_int(dbparameters_->statements_[FIND_PREVIOUS], 1,
+ zone_id) != SQLITE_OK) {
+ isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
+ " to SQL statement (find previous): " <<
+ sqlite3_errmsg(dbparameters_->db_));
+ }
+ if (sqlite3_bind_text(dbparameters_->statements_[FIND_PREVIOUS], 2,
+ rname.c_str(), -1, SQLITE_STATIC) != SQLITE_OK) {
+ isc_throw(SQLite3Error, "Could not bind name " << rname <<
+ " to SQL statement (find previous): " <<
+ sqlite3_errmsg(dbparameters_->db_));
+ }
+
+ std::string result;
+ const int rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]);
+ if (rc == SQLITE_ROW) {
+ // We found it
+ result = convertToPlainChar(sqlite3_column_text(dbparameters_->
+ statements_[FIND_PREVIOUS], 0), dbparameters_->db_);
+ }
+ sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
+
+ if (rc == SQLITE_DONE) {
+ // No NSEC records here, this DB doesn't support DNSSEC or
+ // we asked before the apex
+ isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC or "
+ "query before apex");
+ }
+
+ if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+ // Some kind of error
+ isc_throw(SQLite3Error, "Could not get data for previous name");
+ }
+
+ return (result);
+}
+
}
}
diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h
index fae249b..c4bacad 100644
--- a/src/lib/datasrc/sqlite3_accessor.h
+++ b/src/lib/datasrc/sqlite3_accessor.h
@@ -170,6 +170,10 @@ public:
/// "sqlite3_bind10.sqlite3".
virtual const std::string& getDBName() const { return (database_name_); }
+ /// \brief Concrete implementation of the pure virtual method
+ virtual std::string findPreviousName(int zone_id, const std::string& rname)
+ const;
+
private:
/// \brief Private database data
boost::scoped_ptr<SQLite3Parameters> dbparameters_;
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 4ed9f12..94ae022 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -48,9 +48,11 @@ const char* const TEST_RECORDS[][5] = {
{"www.example.org.", "A", "3600", "", "192.0.2.1"},
{"www.example.org.", "AAAA", "3600", "", "2001:db8::1"},
{"www.example.org.", "AAAA", "3600", "", "2001:db8::2"},
+ {"www.example.org.", "NSEC", "3600", "", "www2.example.org. A AAAA NSEC RRSIG"},
+ {"www.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"www2.example.org.", "A", "3600", "", "192.0.2.1"},
- {"www2.example.org.","AAAA", "3600", "", "2001:db8::1"},
+ {"www2.example.org.", "AAAA", "3600", "", "2001:db8::1"},
{"www2.example.org.", "A", "3600", "", "192.0.2.2"},
{"cname.example.org.", "CNAME", "3600", "", "www.example.org."},
@@ -125,6 +127,7 @@ const char* const TEST_RECORDS[][5] = {
{"delegation.example.org.", "NS", "3600", "", "ns.example.com."},
{"delegation.example.org.", "NS", "3600", "",
"ns.delegation.example.org."},
+ {"delegation.example.org.", "DS", "3600", "", "1 RSAMD5 2 abcd"},
{"delegation.example.org.", "RRSIG", "3600", "", "NS 5 3 3600 "
"20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"ns.delegation.example.org.", "A", "3600", "", "192.0.2.1"},
@@ -153,6 +156,9 @@ const char* const TEST_RECORDS[][5] = {
// doesn't break anything
{"example.org.", "NS", "3600", "", "ns.example.com."},
{"example.org.", "A", "3600", "", "192.0.2.1"},
+ {"example.org.", "NSEC", "3600", "", "acnamesig1.example.org. NS A NSEC RRSIG"},
+ {"example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"example.org.", "RRSIG", "3600", "", "NS 5 3 3600 20000101000000 "
"20000201000000 12345 example.org. FAKEFAKEFAKE"},
@@ -162,11 +168,23 @@ const char* const TEST_RECORDS[][5] = {
// Something for wildcards
{"*.wild.example.org.", "A", "3600", "", "192.0.2.5"},
{"*.wild.example.org.", "RRSIG", "3600", "A", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
+ {"*.wild.example.org.", "NSEC", "3600", "", "cancel.here.wild.example.org. A NSEC RRSIG"},
+ {"*.wild.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"cancel.here.wild.example.org.", "AAAA", "3600", "", "2001:db8::5"},
{"delegatedwild.example.org.", "NS", "3600", "", "ns.example.com."},
{"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"},
{"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"},
{"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"},
+ {"bao.example.org.", "NSEC", "3600", "", "wild.*.foo.*.bar.example.org. NSEC"},
+ {"*.cnamewild.example.org.", "CNAME", "3600", "", "www.example.org."},
+ {"*.nswild.example.org.", "NS", "3600", "", "ns.example.com."},
+ // For NSEC empty non-terminal
+ {"l.example.org.", "NSEC", "3600", "", "empty.nonterminal.example.org. NSEC"},
+ {"empty.nonterminal.example.org.", "A", "3600", "", "192.0.2.1"},
+ // Invalid rdata
+ {"invalidrdata.example.org.", "A", "3600", "", "Bunch of nonsense"},
+ {"invalidrdata2.example.org.", "A", "3600", "", "192.0.2.1"},
+ {"invalidrdata2.example.org.", "RRSIG", "3600", "", "Nonsense"},
{NULL, NULL, NULL, NULL, NULL},
};
@@ -223,6 +241,10 @@ public:
"This database datasource can't be iterated");
}
+ virtual std::string findPreviousName(int, const std::string&) const {
+ isc_throw(isc::NotImplemented,
+ "This data source doesn't support DNSSEC");
+ }
private:
const std::string database_name_;
@@ -529,6 +551,38 @@ public:
return (latest_clone_);
}
+ virtual std::string findPreviousName(int id, const std::string& rname)
+ const
+ {
+ // Hardcoded for now, but we could compute it from the data
+ // Maybe do it when it is needed some time in future?
+ if (id == -1) {
+ isc_throw(isc::NotImplemented, "Test not implemented behaviour");
+ } else if (id == 42) {
+ if (rname == "org.example.nonterminal.") {
+ return ("l.example.org.");
+ } else if (rname == "org.example.aa.") {
+ return ("example.org.");
+ } else if (rname == "org.example.www2." ||
+ rname == "org.example.www1.") {
+ return ("www.example.org.");
+ } else if (rname == "org.example.badnsec2.") {
+ return ("badnsec1.example.org.");
+ } else if (rname == "org.example.brokenname.") {
+ return ("brokenname...example.org.");
+ } else if (rname == "org.example.bar.*.") {
+ return ("bao.example.org.");
+ } else if (rname == "org.example.notimplnsec." ||
+ rname == "org.example.wild.here.") {
+ isc_throw(isc::NotImplemented, "Not implemented in this test");
+ } else {
+ isc_throw(isc::Unexpected, "Unexpected name");
+ }
+ } else {
+ isc_throw(isc::Unexpected, "Unknown zone ID");
+ }
+ }
+
private:
// The following member variables are storage and/or update work space
// of the test zone. The "master"s are the real objects that contain
@@ -967,21 +1021,25 @@ doFindTest(ZoneFinder& finder,
ZoneFinder::FindResult result =
finder.find(name, type, NULL, options);
ASSERT_EQ(expected_result, result.code) << name << " " << type;
- if (!expected_rdatas.empty()) {
+ if (!expected_rdatas.empty() && result.rrset) {
checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
name, finder.getClass(), expected_type, expected_ttl,
expected_rdatas);
- if (!expected_sig_rdatas.empty()) {
+ if (!expected_sig_rdatas.empty() && result.rrset->getRRsig()) {
checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ?
expected_name : name, finder.getClass(),
isc::dns::RRType::RRSIG(), expected_ttl,
expected_sig_rdatas);
- } else {
+ } else if (expected_sig_rdatas.empty()) {
EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
+ } else {
+ ADD_FAILURE() << "Missing RRSIG";
}
- } else {
+ } else if (expected_rdatas.empty()) {
EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset);
+ } else {
+ ADD_FAILURE() << "Missing result";
}
}
@@ -1422,21 +1480,21 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
"FAKEFAKEFAKE");
doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
this->qtype_, this->qtype_, this->rrttl_,
- ZoneFinder::SUCCESS, this->expected_rdatas_,
+ ZoneFinder::WILDCARD, this->expected_rdatas_,
this->expected_sig_rdatas_);
doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"),
- this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS,
+ this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::WILDCARD,
this->expected_rdatas_, this->expected_sig_rdatas_);
this->expected_rdatas_.clear();
this->expected_sig_rdatas_.clear();
doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
- this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_,
- this->expected_sig_rdatas_);
+ this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
- this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_,
- this->expected_sig_rdatas_);
+ this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
// Direct request for this wildcard
this->expected_rdatas_.push_back("192.0.2.5");
@@ -1532,11 +1590,146 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
this->qtype_, this->rrttl_, ZoneFinder::NXRRSET,
this->expected_rdatas_, this->expected_sig_rdatas_);
+ // FIXME: What should be returned in this case? How does the
+ // DNSSEC logic handle it?
+ }
+
+ const char* negative_dnssec_names[] = {
+ "a.bar.example.org.",
+ "foo.baz.bar.example.org.",
+ "a.foo.bar.example.org.",
+ NULL
+ };
+
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("wild.*.foo.*.bar.example.org. NSEC");
+ this->expected_sig_rdatas_.clear();
+ for (const char** name(negative_dnssec_names); *name != NULL; ++ name) {
+ doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
+ RRType::NSEC(), this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("bao.example.org."), ZoneFinder::FIND_DNSSEC);
+ }
+
+ // Some strange things in the wild node
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("www.example.org.");
+ this->expected_sig_rdatas_.clear();
+ doFindTest(*finder, isc::dns::Name("a.cnamewild.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::CNAME(),
+ this->rrttl_, ZoneFinder::CNAME,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
+
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("ns.example.com.");
+ doFindTest(*finder, isc::dns::Name("a.nswild.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NS(),
+ this->rrttl_, ZoneFinder::DELEGATION,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
+}
+
+TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) {
+ // The domain exists, but doesn't have this RRType
+ // So we should get its NSEC
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
+ this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ doFindTest(*finder, isc::dns::Name("www.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC);
+}
+
+TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
+ // The domain exists, but doesn't have this RRType
+ // So we should get its NSEC
+ //
+ // The user will have to query us again to get the correct
+ // answer (eg. prove there's not an exact match)
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ this->expected_rdatas_.push_back("cancel.here.wild.example.org. A NSEC "
+ "RRSIG");
+ this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ // Note that the NSEC name should NOT be synthesized.
+ doFindTest(*finder, isc::dns::Name("a.wild.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC);
+}
+
+TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
+ // The domain doesn't exist, so we must get the right NSEC
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
+ this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ doFindTest(*finder, isc::dns::Name("www1.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::NXDOMAIN,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("www.example.org."), ZoneFinder::FIND_DNSSEC);
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("acnamesig1.example.org. NS A NSEC RRSIG");
+ // This tests it works correctly in apex (there was a bug, where a check
+ // for NS-alone was there and it would throw).
+ doFindTest(*finder, isc::dns::Name("aa.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::NXDOMAIN,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("example.org."), ZoneFinder::FIND_DNSSEC);
+
+ // Check that if the DB doesn't support it, the exception from there
+ // is not propagated and it only does not include the NSEC
+ if (!this->is_mock_) {
+ return; // We don't make the real DB to throw
+ }
+ EXPECT_NO_THROW(doFindTest(*finder,
+ isc::dns::Name("notimplnsec.example.org."),
+ isc::dns::RRType::TXT(),
+ isc::dns::RRType::NSEC(), this->rrttl_,
+ ZoneFinder::NXDOMAIN, this->empty_rdatas_,
+ this->empty_rdatas_, Name::ROOT_NAME(),
+ ZoneFinder::FIND_DNSSEC));
+}
+
+TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
+ // Same as NXDOMAIN_NSEC, but with empty non-terminal
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ this->expected_rdatas_.push_back("empty.nonterminal.example.org. NSEC");
+ doFindTest(*finder, isc::dns::Name("nonterminal.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), this->rrttl_,
+ ZoneFinder::NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("l.example.org."), ZoneFinder::FIND_DNSSEC);
+
+ // Check that if the DB doesn't support it, the exception from there
+ // is not propagated and it only does not include the NSEC
+ if (!this->is_mock_) {
+ return; // We don't make the real DB to throw
}
+ EXPECT_NO_THROW(doFindTest(*finder,
+ isc::dns::Name("here.wild.example.org."),
+ isc::dns::RRType::TXT(),
+ isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::NXRRSET,
+ this->empty_rdatas_, this->empty_rdatas_,
+ Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
}
TYPED_TEST(DatabaseClientTest, getOrigin) {
- DataSourceClient::FindResult zone(this->client_->findZone(this->zname_));
+ DataSourceClient::FindResult
+ zone(this->client_->findZone(Name("example.org")));
ASSERT_EQ(result::SUCCESS, zone.code);
shared_ptr<DatabaseClient::Finder> finder(
dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
@@ -2142,4 +2335,66 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
ZoneFinder::SUCCESS, this->expected_rdatas_,
this->empty_rdatas_);
}
+
+TYPED_TEST(DatabaseClientTest, previous) {
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ EXPECT_EQ(Name("www.example.org."),
+ finder->findPreviousName(Name("www2.example.org.")));
+ // Check a name that doesn't exist there
+ EXPECT_EQ(Name("www.example.org."),
+ finder->findPreviousName(Name("www1.example.org.")));
+ if (this->is_mock_) { // We can't really force the DB to throw
+ // Check it doesn't crash or anything if the underlying DB throws
+ DataSourceClient::FindResult
+ zone(this->client_->findZone(Name("bad.example.org")));
+ finder =
+ dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder);
+
+ EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")),
+ isc::NotImplemented);
+ } else {
+ // No need to test this on mock one, because we test only that
+ // the exception gets through
+
+ // A name before the origin
+ EXPECT_THROW(finder->findPreviousName(Name("example.com")),
+ isc::NotImplemented);
+ }
+}
+
+TYPED_TEST(DatabaseClientTest, invalidRdata) {
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ EXPECT_THROW(finder->find(Name("invalidrdata.example.org."), RRType::A()),
+ DataSourceError);
+ EXPECT_THROW(finder->find(Name("invalidrdata2.example.org."), RRType::A()),
+ DataSourceError);
+}
+
+TEST_F(MockDatabaseClientTest, missingNSEC) {
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ /*
+ * FIXME: For now, we can't really distinguish this bogus input
+ * from not-signed zone so we can't throw. But once we can,
+ * enable the original test.
+ */
+#if 0
+ EXPECT_THROW(finder->find(Name("badnsec2.example.org."), RRType::A(), NULL,
+ ZoneFinder::FIND_DNSSEC),
+ DataSourceError);
+#endif
+ doFindTest(*finder, Name("badnsec2.example.org."), RRType::A(),
+ RRType::A(), this->rrttl_, ZoneFinder::NXDOMAIN,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
+}
+
+TEST_F(MockDatabaseClientTest, badName) {
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ EXPECT_THROW(finder->findPreviousName(Name("brokenname.example.org.")),
+ DataSourceError);
+}
+
}
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index a926935..2b854db 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -395,6 +395,14 @@ public:
};
/**
+ * \brief Check that findPreviousName throws as it should now.
+ */
+TEST_F(InMemoryZoneFinderTest, findPreviousName) {
+ EXPECT_THROW(zone_finder_.findPreviousName(Name("www.example.org")),
+ isc::NotImplemented);
+}
+
+/**
* \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
*
* Takes the created zone finder and checks its properties they are the same
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 8b423f8..3974977 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -351,6 +351,45 @@ TEST_F(SQLite3AccessorTest, getRecords) {
EXPECT_FALSE(context->getNext(columns));
}
+TEST_F(SQLite3AccessorTest, findPrevious) {
+ EXPECT_EQ("dns01.example.com.",
+ accessor->findPreviousName(1, "com.example.dns02."));
+ // A name that doesn't exist
+ EXPECT_EQ("dns01.example.com.",
+ accessor->findPreviousName(1, "com.example.dns01x."));
+ // Largest name
+ EXPECT_EQ("www.example.com.",
+ accessor->findPreviousName(1, "com.example.wwww"));
+ // Out of zone after the last name
+ EXPECT_EQ("www.example.com.",
+ accessor->findPreviousName(1, "org.example."));
+ // Case insensitive?
+ EXPECT_EQ("dns01.example.com.",
+ accessor->findPreviousName(1, "com.exaMple.DNS02."));
+ // A name that doesn't exist
+ EXPECT_EQ("dns01.example.com.",
+ accessor->findPreviousName(1, "com.exaMple.DNS01X."));
+ // The DB contains foo.bar.example.com., which would be in between
+ // these two names. However, that one does not have an NSEC record,
+ // which is how this database recognizes glue data, so it should
+ // be skipped.
+ EXPECT_EQ("example.com.",
+ accessor->findPreviousName(1, "com.example.cname-ext."));
+ // Throw when we are before the origin
+ EXPECT_THROW(accessor->findPreviousName(1, "com.example."),
+ isc::NotImplemented);
+ EXPECT_THROW(accessor->findPreviousName(1, "a.example."),
+ isc::NotImplemented);
+}
+
+TEST_F(SQLite3AccessorTest, findPreviousNoData) {
+ // This one doesn't hold any NSEC records, so it shouldn't work
+ // The underlying DB/data don't support DNSSEC, so it's not implemented
+ // (does it make sense? Or different exception here?)
+ EXPECT_THROW(accessor->findPreviousName(3, "com.example.sql2.www."),
+ isc::NotImplemented);
+}
+
// Test fixture for creating a db that automatically deletes it before start,
// and when done
class SQLite3Create : public ::testing::Test {
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index bb4f435..c83b14b 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -54,13 +54,50 @@ public:
///
/// Note: the codes are tentative. We may need more, or we may find
/// some of them unnecessary as we implement more details.
+ ///
+ /// Some are synonyms of others in terms of RCODE returned to user.
+ /// But they help the logic to decide if it should ask for a NSEC
+ /// that covers something or not (for example, in case of NXRRSET,
+ /// the directly returned NSEC is sufficient, but with wildcard one,
+ /// we need to add one proving there's no exact match and this is
+ /// actually the best wildcard we have). Data sources that don't
+ /// support DNSSEC don't need to distinguish them.
+ ///
+ /// In case of NXRRSET related results, the returned NSEC record
+ /// belongs to the domain which would provide the result if it
+ /// contained the correct type (in case of NXRRSET, it is the queried
+ /// domain, in case of WILDCARD_NXRRSET, it is the wildcard domain
+ /// that matched the query name). In case of an empty nonterminal,
+ /// an NSEC is provided for the interval where the empty nonterminal
+ /// lives. The end of the interval is the subdomain causing existence
+ /// of the empty nonterminal (if there's sub.x.example.com, and no record
+ /// in x.example.com, then x.example.com exists implicitly - is the empty
+ /// nonterminal and sub.x.example.com is the subdomain causing it).
+ ///
+ /// Examples: if zone "example.com" has the following record:
+ /// \code
+ /// a.b.example.com. NSEC c.example.com.
+ /// \endcode
+ /// a call to \c find() for "b.example.com." will result in NXRRSET,
+ /// and if the FIND_DNSSEC option is set this NSEC will be returned.
+ /// Likewise, if zone "example.org" has the following record,
+ /// \code
+ /// x.*.example.org. NSEC a.example.org.
+ /// \endcode
+ /// a call to \c find() for "y.example.org" will result in
+ /// WILDCARD_NXRRSET (*.example.org is an empty nonterminal wildcard node),
+ /// and if the FIND_DNSSEC option is set this NSEC will be returned.
+ ///
+ /// In case of NXDOMAIN, the returned NSEC covers the queried domain.
enum Result {
SUCCESS, ///< An exact match is found.
DELEGATION, ///< The search encounters a zone cut.
NXDOMAIN, ///< There is no domain name that matches the search name
NXRRSET, ///< There is a matching name but no RRset of the search type
CNAME, ///< The search encounters and returns a CNAME RR
- DNAME ///< The search encounters and returns a DNAME RR
+ DNAME, ///< The search encounters and returns a DNAME RR
+ WILDCARD, ///< Succes by wildcard match, for DNSSEC
+ WILDCARD_NXRRSET ///< NXRRSET on wildcard, for DNSSEC
};
/// A helper structure to represent the search result of \c find().
@@ -135,7 +172,7 @@ public:
//@}
///
- /// \name Search Method
+ /// \name Search Methods
///
//@{
/// Search the zone for a given pair of domain name and RR type.
@@ -167,8 +204,8 @@ public:
/// We should revisit the interface before we heavily rely on it.
///
/// The \c options parameter specifies customized behavior of the search.
- /// Their semantics is as follows:
- /// - \c GLUE_OK Allow search under a zone cut. By default the search
+ /// Their semantics is as follows (they are or bit-field):
+ /// - \c FIND_GLUE_OK Allow search under a zone cut. By default the search
/// will stop once it encounters a zone cut. If this option is specified
/// it remembers information about the highest zone cut and continues
/// the search until it finds an exact match for the given name or it
@@ -176,6 +213,9 @@ public:
/// RRsets for that name are searched just like the normal case;
/// otherwise, if the search has encountered a zone cut, \c DELEGATION
/// with the information of the highest zone cut will be returned.
+ /// - \c FIND_DNSSEC Request that DNSSEC data (like NSEC, RRSIGs) are
+ /// returned with the answer. It is allowed for the data source to
+ /// include them even when not requested.
///
/// A derived version of this method may involve internal resource
/// allocation, especially for constructing the resulting RRset, and may
@@ -195,6 +235,31 @@ public:
isc::dns::RRsetList* target = NULL,
const FindOptions options
= FIND_DEFAULT) = 0;
+
+ /// \brief Get previous name in the zone
+ ///
+ /// Gets the previous name in the DNSSEC order. This can be used
+ /// to find the correct NSEC records for proving nonexistence
+ /// of domains.
+ ///
+ /// The concrete implementation might throw anything it thinks appropriate,
+ /// however it is recommended to stick to the ones listed here. The user
+ /// of this method should be able to handle any exceptions.
+ ///
+ /// This method does not include under-zone-cut data (glue data).
+ ///
+ /// \param query The name for which one we look for a previous one. The
+ /// queried name doesn't have to exist in the zone.
+ /// \return The preceding name
+ ///
+ /// \throw NotImplemented in case the data source backend doesn't support
+ /// DNSSEC or there is no previous in the zone (NSEC records might be
+ /// missing in the DB, the queried name is less or equal to the apex).
+ /// \throw DataSourceError for low-level or internal datasource errors
+ /// (like broken connection to database, wrong data living there).
+ /// \throw std::bad_alloc For allocation errors.
+ virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
+ const = 0;
//@}
};
More information about the bind10-changes
mailing list