BIND 10 master, updated. 95b7c29f155b8fa21cf85c6d3afbe3f510db83d1 [1198] Merge branch 'master' into trac1198
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Dec 9 13:40:10 UTC 2011
The branch, master has been updated
via 95b7c29f155b8fa21cf85c6d3afbe3f510db83d1 (commit)
via c37c596c844d35dc25eb729a99666948c6af8a6b (commit)
via e30dfe31fbc6b290e63ffe4666dc39ebbd0d23aa (commit)
via 52802deb18632b028815d25f19976d0576d76e1f (commit)
via a60c96464c0b959492a13b10767a7d9352be060e (commit)
via 0c0e8938a3ece603eddd70e3ebba94b03eeeeb92 (commit)
via 614e0ed92f8e6fb5f66277c7fbec8af6149cfa39 (commit)
via afee8bc035223c87c385a6855ab210b4e55cc161 (commit)
via 717946a088b5c3fa287258e1ebc3fa6dd9093702 (commit)
via b8e895092634bc661baf7fa043fffdba511f8256 (commit)
via 1bad76a6ab0ece059d8a587870f1da84510eccc5 (commit)
via b4471621912e7518088b106d829a8431a6c4ea97 (commit)
via c05dc7099e4ed686ad1af573e6795a751d020025 (commit)
via 1beaacf4d924392323bd08a0c7aed65e9324e092 (commit)
via 84d0d090f5452410a58d8f8503c61d81ec85f2f4 (commit)
via 35015af5525965fdb421a856ffb01fb1ab8a7ad4 (commit)
via 25b7595f3f1b158bd6278cea3c4dd0d6eeca8a2f (commit)
via cf8596b58bd57f4ebfff7d83d24294eaed38f7bf (commit)
via b77f5d1f891daf4c24024b44db6a7502e2728d2a (commit)
via 0337c552ff717ee890ae784451668ce3d789650f (commit)
via 63f318aa4405840d77c5e7afcf7c3437c5af241b (commit)
via 2e12dd60da03170462efad07173036f973813bd8 (commit)
from a677ae91c9d7a5bd2ef8574f84e9f33f90c45e44 (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 95b7c29f155b8fa21cf85c6d3afbe3f510db83d1
Merge: c37c596c844d35dc25eb729a99666948c6af8a6b a677ae91c9d7a5bd2ef8574f84e9f33f90c45e44
Author: Stephen Morris <stephen at isc.org>
Date: Fri Dec 9 13:25:17 2011 +0000
[1198] Merge branch 'master' into trac1198
Conflicts:
src/lib/datasrc/database.cc
src/lib/datasrc/datasrc_messages.mes
commit c37c596c844d35dc25eb729a99666948c6af8a6b
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Dec 7 14:57:55 2011 -0800
[1198] added more detailed description in the style of doxygen
for findOnNameResult.
also removed obsolete note from the findNoNameResult description.
commit e30dfe31fbc6b290e63ffe4666dc39ebbd0d23aa
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Dec 7 09:26:48 2011 -0800
[1198] applied proposed further refactoring: extracted the common logic
for the positive case to a separate method and used it directly from find()
and findWildcardMatch(). Moved detailed comments of findWildcardMatch to
the head of its definition so that the function body will be physically
concise.
commit 52802deb18632b028815d25f19976d0576d76e1f
Author: Stephen Morris <stephen at isc.org>
Date: Wed Dec 7 16:26:47 2011 +0000
[1198] Changes as a result of review
commit a60c96464c0b959492a13b10767a7d9352be060e
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Thu Dec 1 13:35:46 2011 -0800
[1198] a trivial cleanups: folded some long lines, and fixed indentation.
commit 0c0e8938a3ece603eddd70e3ebba94b03eeeeb92
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Thu Dec 1 13:35:01 2011 -0800
[1198] a minor style fix: removed unnecessary semicolon.
commit 614e0ed92f8e6fb5f66277c7fbec8af6149cfa39
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Mon Nov 28 22:32:54 2011 -0800
[1198] cleanup: removed white spaces at EOLs
commit afee8bc035223c87c385a6855ab210b4e55cc161
Author: Stephen Morris <stephen at isc.org>
Date: Mon Nov 28 13:10:07 2011 +0000
[1198] Miscellaneous additional debug messages and documentation
commit 717946a088b5c3fa287258e1ebc3fa6dd9093702
Author: Stephen Morris <stephen at isc.org>
Date: Mon Nov 28 10:43:05 2011 +0000
[1198] Convert header documentation to Doxygen triple-slash format
commit b8e895092634bc661baf7fa043fffdba511f8256
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 25 19:23:28 2011 +0000
[1198] Checkpoint after updating internal documentation
commit 1bad76a6ab0ece059d8a587870f1da84510eccc5
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 25 16:43:41 2011 +0000
[1198] Add reordered message file and utility to do this
Added utility to put messages in a message file into alphabetical
order and, for ease of further editing, the data source message file
in alphabetical order.
commit b4471621912e7518088b106d829a8431a6c4ea97
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Nov 22 20:13:03 2011 -0800
[1198] proposed further refactor 7: recovered logs, with defining new
ones for each subcase of find().
commit c05dc7099e4ed686ad1af573e6795a751d020025
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Nov 22 20:03:33 2011 -0800
[1198] proposed further refactor 6: we can even remove 'origin' (and thus
eliminating the need for making a copy of it)
commit 1beaacf4d924392323bd08a0c7aed65e9324e092
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Nov 22 19:20:18 2011 -0800
[1198] proposed further refactor 5: replaced variables used only once with
their definitions. define 'origin' immediately before it's used, which
also makes the method a bit more efficient in the delegation case.
commit 84d0d090f5452410a58d8f8503c61d81ec85f2f4
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Nov 22 19:05:52 2011 -0800
[1198] proposed further refactor 4: moved the "no name"
(NXDOMAIN/empty name/wildcard) case to a separate method to make the main
method even more concise.
commit 35015af5525965fdb421a856ffb01fb1ab8a7ad4
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Nov 22 18:43:11 2011 -0800
[1198] proposed further refactor 3: pass DelegationSearchResult to
findWildcardMatch() instead of first_ns and last_known, thereby eliminating
the need for defining and using these variables.
commit 25b7595f3f1b158bd6278cea3c4dd0d6eeca8a2f
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Nov 22 15:18:20 2011 -0800
[1198] proposed further refactor 2: eliminated the need for 'records_found'
in findWildcardMatch(). as a result we can now use the common FindResult
struct as the return type of findWildcardMatch().
commit cf8596b58bd57f4ebfff7d83d24294eaed38f7bf
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Nov 22 14:46:46 2011 -0800
[1198] proposed further refactor 1: simplified find() further by replacing
code logic with intermediate state variable with direct return.
(as a result of some log points were lost, which should be recovered
later)
commit b77f5d1f891daf4c24024b44db6a7502e2728d2a
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Mon Nov 21 22:48:33 2011 -0800
[1198] a trivial cleanup: folded multiple lines into one when they were
sufficiently short.
commit 0337c552ff717ee890ae784451668ce3d789650f
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Mon Nov 21 13:59:46 2011 -0800
[1198] some trivial cleanups:
- constify things as much as possible
- a bit of style fixes (brace position, etc)
commit 63f318aa4405840d77c5e7afcf7c3437c5af241b
Author: Stephen Morris <stephen at isc.org>
Date: Mon Nov 21 18:36:26 2011 +0000
[1198] Extract out the wildcard matching into a separate method
commit 2e12dd60da03170462efad07173036f973813bd8
Author: Stephen Morris <stephen at isc.org>
Date: Mon Nov 21 11:38:12 2011 +0000
[1198] Extracted search for delegation point into a separate method.
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/database.cc | 653 +++++++++++++--------
src/lib/datasrc/database.h | 1118 +++++++++++++++++++---------------
src/lib/datasrc/datasrc_messages.mes | 109 +++-
src/lib/datasrc/zone.h | 15 +-
tools/reorder_message_file.py | 196 ++++++
5 files changed, 1341 insertions(+), 750 deletions(-)
create mode 100644 tools/reorder_message_file.py
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 45ce0c2..5de303b 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -390,66 +390,96 @@ DatabaseClient::Finder::findNSECCover(const Name& name) {
return (RRsetPtr());
}
-ZoneFinder::FindResult
-DatabaseClient::Finder::find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- isc::dns::RRsetList*,
- const FindOptions options)
+DatabaseClient::Finder::DelegationSearchResult
+DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
+ const FindOptions options)
{
- // This variable is used to determine the difference between
- // NXDOMAIN and NXRRSET
- bool records_found = false;
- 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;
+ // Result of search
+ isc::dns::ConstRRsetPtr result_rrset;
ZoneFinder::Result result_status = SUCCESS;
- 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,
- // we can't do it under NS, so we store it here to check
- isc::dns::RRsetPtr first_ns;
-
- // First, do we have any kind of delegation (NS/DNAME) here?
- 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);
- const size_t current_label_count(name.getLabelCount());
- // This is how many labels we remove to get origin
- const size_t remove_labels(current_label_count - origin_label_count);
-
- // Now go trough all superdomains from origin down
- 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 = getRRsets(superdomain.toText(), DELEGATION_TYPES(),
- i != remove_labels);
- if (found.first) {
- // It contains some RRs, so it exists.
- last_known = superdomain.getLabelCount();
+ // Are we searching for glue?
+ const bool glue_ok = ((options & FIND_GLUE_OK) != 0);
+
+ // This next declaration is an optimisation. When we search the database
+ // for glue records, we generally ignore delegations. (This allows for
+ // the case where e.g. the delegation to zone example.com refers to
+ // nameservers within the zone, e.g. ns1.example.com. When conducting the
+ // search for ns1.example.com, we have to search past the NS records at
+ // example.com.)
+ //
+ // The one case where this is forbidden is when we search past the zone
+ // cut but the match we find for the glue is a wildcard match. In that
+ // case, we return the delegation instead (see RFC 1034, section 4.3.3).
+ // To save a new search, we record the location of the delegation cut when
+ // we encounter it here.
+ isc::dns::ConstRRsetPtr first_ns;
+
+ // We want to search from the apex down. We are given the full domain
+ // name so we have to do some manipulation to ensure that when we start
+ // checking superdomains, we start from the the domain name of the zone
+ // (e.g. if the name is b.a.example.com. and we are in the example.com.
+ // zone, we check example.com., a.example.com. and b.a.example.com. We
+ // don't need to check com. or .).
+ //
+ // Set the number of labels in the origin (i.e. apex of the zone) and in
+ // the last known non-empty domain (which, at this point, is the origin).
+ const size_t origin_label_count = getOrigin().getLabelCount();
+ size_t last_known = origin_label_count;
+
+ // Set how many labels we remove to get origin: this is the number of
+ // labels we have to process in our search.
+ const size_t remove_labels = name.getLabelCount() - origin_label_count;
+
+ // Go through all superdomains from the origin down searching for nodes
+ // that indicate a delegation (.e. NS or DNAME).
+ for (int i = remove_labels; i > 0; --i) {
+ const Name superdomain(name.split(i));
+
+ // Note if this is the origin. (We don't count NS records at the origin
+ // as a delegation so this controls whether NS RRs are included in
+ // the results of some searches.)
+ const bool not_origin = (i != remove_labels);
+
+ // Look if there's NS or DNAME at this point of the tree, but ignore
+ // the NS RRs at the apex of the zone.
+ const FoundRRsets found = getRRsets(superdomain.toText(),
+ DELEGATION_TYPES(), not_origin);
+ if (found.first) {
+ // This node contains either NS or DNAME RRs so it does exist.
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()) {
+
+ // An optimisation. We know that there is an exact match for
+ // something at this point in the tree so remember it. If we have
+ // to do a wildcard search, as we search upwards through the tree
+ // we don't need to pass this point, which is an exact match for
+ // the domain name.
+ last_known = superdomain.getLabelCount();
+
+ if (glue_ok && !first_ns && not_origin &&
+ nsi != found.second.end()) {
+ // If we are searching for glue ("glue OK" mode), store the
+ // highest NS record that we find that is not the apex. This
+ // is another optimisation for later, where we need the
+ // information if the domain we are looking for matches through
+ // a wildcard.
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
+
+ } else if (!glue_ok && not_origin && nsi != found.second.end()) {
+ // Not searching for glue and we have found an NS RRset that is
+ // not at the apex. We have found a delegation - return that
+ // fact, there is no need to search further down the tree.
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DELEGATION).
arg(accessor_->getDBName()).arg(superdomain);
result_rrset = nsi->second;
result_status = DELEGATION;
- // No need to go lower, found
break;
+
} else if (dni != found.second.end()) {
- // Very similar with DNAME
+ // We have found a DNAME so again stop searching down the tree
+ // and return the information.
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DNAME).
arg(accessor_->getDBName()).arg(superdomain);
@@ -464,202 +494,344 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
}
}
}
+ return (DelegationSearchResult(result_status, result_rrset, first_ns,
+ last_known));
+}
- if (!result_rrset) { // Only if we didn't find a redirect already
- // 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
-
- WantedTypes final_types(FINAL_TYPES());
- final_types.insert(type);
- found = getRRsets(name.toText(), final_types, name != origin);
- records_found = found.first;
-
- // 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;
- result_rrset = nsi->second;
- } else if (type != isc::dns::RRType::CNAME() &&
- cni != found.second.end()) {
- // A CNAME here
- result_status = CNAME;
- 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.
- if (hasSubdomains(name.toText())) {
- LOG_DEBUG(logger, DBG_TRACE_DETAILED,
- DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
- arg(accessor_->getDBName()).arg(name);
- records_found = true;
- get_cover = dnssec_data;
- } else if ((options & NO_WILDCARD) != 0) {
- // If wildcard check is disabled, the search will ultimately
- // terminate with NXDOMAIN. If DNSSEC is enabled, flag that
- // we need to get the NSEC records to prove this.
- if (dnssec_data) {
- get_cover = true;
- }
- } 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.
- for (size_t i(1); i + last_known <= current_label_count; ++i) {
- // Construct the name with *
- 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?
- // The types are the same as with original query
- found = getRRsets(wildcard, final_types, true,
+// This method is called when we have not found an exact match and when we
+// know that the name is not an empty non-terminal. So the only way that
+// the name can match something in the zone is through a wildcard match.
+//
+// During an earlier stage in the search for this name, we made a record of
+// the lowest superdomain for which we know an RR exists. (Note the "we
+// know" qualification - there may be lower superdomains (ones with more
+// labels) that hold an RR, but as we weren't searching for them, we don't
+// know about them.)
+//
+// In the search for a wildcard match (which starts at the given domain
+// name and goes up the tree to successive superdomains), this is the level
+// at which we can stop - there can't be a wildcard at or beyond that
+// point.
+//
+// At each level that can stop the search, we should consider several cases:
+//
+// - If we found a wildcard match for a glue record below a
+// delegation point, we don't return the match,
+// instead we return the delegation. (Note that if we didn't
+// a wildcard match at all, we would return NXDOMAIN, not the
+// the delegation.)
+//
+// - If we found a wildcard match and we are sure that the match
+// is not an empty non-terminal, return the result taking into account CNAME,
+// on a zone cut, and NXRRSET.
+// (E.g. searching for a match
+// for c.b.a.example.com, we found that b.a.example.com did
+// not exist but that *.a.example.com. did. Checking
+// b.a.example.com revealed no subdomains, so we can use the
+// wilcard match we found.)
+//
+// - If we found a more specified match, the wildcard search
+// is canceled, resulting in NXDOMAIN. (E.g. searching for a match
+// for c.b.a.example.com, we found that b.a.example.com did
+// not exist but that *.a.example.com. did. Checking
+// b.a.example.com found subdomains. So b.example.com is
+// an empty non-terminal and so should not be returned in
+// the wildcard matching process. In other words,
+// b.example.com does exist in the DNS space, it just doesn't
+// have any RRs associated with it.)
+//
+// - If we found a match, but it is an empty non-terminal asterisk (E.g.#
+// subdomain.*.example.com. is present, but there is nothing at
+// *.example.com.), return an NXRRSET indication;
+// the wildcard exists in the DNS space, there's just nothing
+// associated with it. If DNSSEC data is required, return the
+// covering NSEC record.
+//
+// If none of the above applies in any level, the search fails with NXDOMAIN.
+ZoneFinder::FindResult
+DatabaseClient::Finder::findWildcardMatch(
+ const isc::dns::Name& name, const isc::dns::RRType& type,
+ const FindOptions options, const DelegationSearchResult& dresult)
+{
+ // Note that during the search we are going to search not only for the
+ // requested type, but also for types that indicate a delegation -
+ // NS and DNAME.
+ WantedTypes final_types(FINAL_TYPES());
+ final_types.insert(type);
+
+ for (size_t i = 1; i <= (name.getLabelCount() - dresult.last_known); ++i) {
+
+ // Strip off the left-more label(s) in the name and replace with a "*".
+ const Name superdomain(name.split(i));
+ const string wildcard("*." + superdomain.toText());
+ const string construct_name(name.toText());
+
+ // TODO Add a check for DNAME, as DNAME wildcards are discouraged (see
+ // RFC 4592 section 4.4).
+ // Search for a match. The types are the same as with original query.
+ FoundRRsets found = getRRsets(wildcard, final_types, true,
&construct_name);
- if (found.first) {
- if (first_ns) {
- // In case we are under NS, we don't
- // wildcard-match, but return delegation
- result_rrset = first_ns;
- result_status = DELEGATION;
- records_found = true;
- // We pretend to switch to non-glue_ok mode
- glue_ok = false;
- LOG_DEBUG(logger, DBG_TRACE_DETAILED,
- DATASRC_DATABASE_WILDCARD_CANCEL_NS).
- arg(accessor_->getDBName()).arg(wildcard).
- arg(first_ns->getName());
- } else if (!hasSubdomains(name.split(i - 1).toText()))
- {
- // Nothing we added as part of the * can exist
- // directly, as we go up only to first existing
- // domain, but it could be empty non-terminal. In
- // that case, we need to cancel the match.
- records_found = true;
- 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 = WILDCARD_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).
- arg(name);
- } else {
- LOG_DEBUG(logger, DBG_TRACE_DETAILED,
- DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
- arg(accessor_->getDBName()).arg(wildcard).
- arg(name).arg(superdomain);
- }
- break;
- } 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;
+ if (found.first) {
+ // Found something - but what?
+
+ if (dresult.first_ns) {
+ // About to use first_ns. The only way this can be set is if
+ // we are searching for glue, so do a sanity check.
+ if ((options & FIND_GLUE_OK) == 0) {
+ isc_throw(Unexpected, "Inconsistent conditions during "
+ "cancel of wilcard search for " <<
+ name.toText() << ": find_ns non-null when not "
+ "processing glue request");
}
+
+ // Wildcard match for a glue below a delegation point
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_WILDCARD_CANCEL_NS).
+ arg(accessor_->getDBName()).arg(wildcard).
+ arg(dresult.first_ns->getName());
+ return (ZoneFinder::FindResult(DELEGATION, dresult.first_ns));
+
+ } else if (!hasSubdomains(name.split(i - 1).toText())) {
+ // The wildcard match is the best one, find the final result
+ // at it. Note that wildcard should never be the zone origin.
+ return (findOnNameResult(name, type, options, false,
+ found, &wildcard));
+ } else {
+
+ // more specified match found, cancel wildcard match
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
+ arg(accessor_->getDBName()).arg(wildcard).
+ arg(name).arg(superdomain);
+ return (ZoneFinder::FindResult(NXDOMAIN, ConstRRsetPtr()));
}
- } 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;
+
+ } else if (hasSubdomains(wildcard)) {
+ // an empty non-terminal asterisk
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_WILDCARD_EMPTY).
+ arg(accessor_->getDBName()).arg(wildcard).arg(name);
+ if ((options & FIND_DNSSEC) != 0) {
+ ConstRRsetPtr nsec = findNSECCover(Name(wildcard));
+ if (nsec) {
+ return (ZoneFinder::FindResult(WILDCARD_NXRRSET, nsec));
+ }
}
+ return (ZoneFinder::FindResult(NXRRSET, ConstRRsetPtr()));
}
}
- if (!result_rrset) {
- 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;
- }
+ // Nothing found at any level.
+ return (ZoneFinder::FindResult(NXDOMAIN, ConstRRsetPtr()));
+}
+
+ZoneFinder::FindResult
+DatabaseClient::Finder::logAndCreateResult(
+ const Name& name, const string* wildname, const RRType& type,
+ ZoneFinder::Result code, ConstRRsetPtr rrset,
+ const isc::log::MessageID& log_id) const
+{
+ if (rrset) {
+ if (wildname == NULL) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
+ arg(accessor_->getDBName()).arg(name).arg(type).
+ arg(getClass()).arg(*rrset);
+ } else {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
+ arg(accessor_->getDBName()).arg(name).arg(type).
+ arg(getClass()).arg(*wildname).arg(*rrset);
+ }
+ } else {
+ if (wildname == NULL) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
+ arg(accessor_->getDBName()).arg(name).arg(type).
+ arg(getClass());
+ } else {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
+ arg(accessor_->getDBName()).arg(name).arg(type).
+ arg(getClass()).arg(*wildname);
+ }
+ }
+ return (ZoneFinder::FindResult(code, rrset));
+}
+
+ZoneFinder::FindResult
+DatabaseClient::Finder::findOnNameResult(const Name& name,
+ const RRType& type,
+ const FindOptions options,
+ const bool is_origin,
+ const FoundRRsets& found,
+ const string* wildname)
+{
+ const bool wild = (wildname != NULL);
+
+ // Get iterators for the different types of records we are interested in -
+ // CNAME, NS and Wanted types.
+ const FoundIterator nsi(found.second.find(RRType::NS()));
+ const FoundIterator cni(found.second.find(RRType::CNAME()));
+ const FoundIterator wti(found.second.find(type));
+
+ if (!is_origin && ((options & FIND_GLUE_OK) == 0) &&
+ nsi != found.second.end()) {
+ // A NS RRset was found at the domain we were searching for. As it is
+ // not at the origin of the zone, it is a delegation and indicates that
+ // this zone is not authoritative for the data. Just return the
+ // delegation information.
+ return (logAndCreateResult(name, wildname, type, DELEGATION,
+ nsi->second,
+ wild ? DATASRC_DATABASE_WILDCARD_NS :
+ DATASRC_DATABASE_FOUND_DELEGATION_EXACT));
+
+ } else if (type != RRType::CNAME() && cni != found.second.end()) {
+ // We are not searching for a CNAME but nevertheless we have found one
+ // at the name we are searching so we return it. (The caller may
+ // want to continue the lookup by replacing the query name with the
+ // canonical name and the original RR type.) First though, do a sanity
+ // check to ensure that there is only one RR in the CNAME RRset.
+ if (cni->second->getRdataCount() != 1) {
+ isc_throw(DataSourceError, "CNAME with " <<
+ cni->second->getRdataCount() << " rdata at " << name <<
+ ", expected 1");
+ }
+ return (logAndCreateResult(name, wildname, type,
+ wild ? WILDCARD_CNAME : CNAME, cni->second,
+ wild ? DATASRC_DATABASE_WILDCARD_CNAME :
+ DATASRC_DATABASE_FOUND_CNAME));
+
+ } else if (wti != found.second.end()) {
+ // Found an RR matching the query, so return it. (Note that this
+ // includes the case where we were explicitly querying for a CNAME and
+ // found it. It also includes the case where we were querying for an
+ // NS RRset and found it at the apex of the zone.)
+ return (logAndCreateResult(name, wildname, type,
+ wild ? WILDCARD : SUCCESS, wti->second,
+ wild ? DATASRC_DATABASE_WILDCARD_MATCH :
+ DATASRC_DATABASE_FOUND_RRSET));
+ }
+
+ // If we get here, we have found something at the requested name but not
+ // one of the RR types we were interested in. This is the NXRRSET case so
+ // return the appropriate status. If DNSSEC information was requested,
+ // provide the NSEC records. If it's for wildcard, we need to get the
+ // NSEC records in the name of the wildcard, not the substituted one,
+ // so we need to search the tree again.
+ ConstRRsetPtr nsec_rrset; // possibly used with DNSSEC, otherwise NULL
+ if ((options & FIND_DNSSEC) != 0) {
+ if (wild) {
+ const FoundRRsets wfound = getRRsets(*wildname, NSEC_TYPES(),
+ true);
+ const FoundIterator nci = wfound.second.find(RRType::NSEC());
+ if (nci != wfound.second.end()) {
+ nsec_rrset = nci->second;
}
- // 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 {
+ const FoundIterator nci = found.second.find(RRType::NSEC());
+ if (nci != found.second.end()) {
+ nsec_rrset = nci->second;
}
}
+ }
+ if (nsec_rrset) {
+ // This log message covers both normal and wildcard cases, so we pass
+ // NULL for 'wildname'.
+ return (logAndCreateResult(name, NULL, type,
+ wild ? WILDCARD_NXRRSET : NXRRSET,
+ nsec_rrset,
+ DATASRC_DATABASE_FOUND_NXRRSET_NSEC));
+ }
+ return (logAndCreateResult(name, wildname, type,
+ wild ? WILDCARD_NXRRSET : NXRRSET, nsec_rrset,
+ wild ? DATASRC_DATABASE_WILDCARD_NXRRSET :
+ DATASRC_DATABASE_FOUND_NXRRSET));
+}
+
+ZoneFinder::FindResult
+DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
+ FindOptions options,
+ const DelegationSearchResult& dresult)
+{
+ const bool dnssec_data = ((options & FIND_DNSSEC) != 0);
+
+ // On entry to this method, we know that the database doesn't have any
+ // entry for this name. Before returning NXDOMAIN, we need to check
+ // for special cases.
+
+ if (hasSubdomains(name.toText())) {
+ // Does the domain have a subdomain (i.e. it is an empty non-terminal)?
+ // If so, return NXRRSET instead of NXDOMAIN (as although the name does
+ // not exist in the database, it does exist in the DNS tree).
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
+ arg(accessor_->getDBName()).arg(name);
+ return (FindResult(NXRRSET, dnssec_data ? findNSECCover(name) :
+ ConstRRsetPtr()));
+
+ } else if ((options & NO_WILDCARD) == 0) {
+ // It's not an empty non-terminal and wildcard matching is not
+ // disabled, so check for wildcards. If there is a wildcard match
+ // (i.e. all results except NXDOMAIN) return it; otherwise fall
+ // through to the NXDOMAIN case below.
+ const ZoneFinder::FindResult wresult =
+ findWildcardMatch(name, type, options, dresult);
+ if (wresult.code != NXDOMAIN) {
+ return (FindResult(wresult.code, wresult.rrset));
+ }
+ }
+
+ // All avenues to find a match are now exhausted, return NXDOMAIN (plus
+ // NSEC records if requested).
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_NO_MATCH).
+ arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
+ return (FindResult(NXDOMAIN, dnssec_data ? findNSECCover(name) :
+ ConstRRsetPtr()));
+}
+
+ZoneFinder::FindResult
+DatabaseClient::Finder::find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ isc::dns::RRsetList*,
+ const FindOptions options)
+{
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
+ .arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
+
+ // First, go through all superdomains from the origin down, searching for
+ // nodes that indicate a delegation (i.e. NS or DNAME, ignoring NS records
+ // at the apex). If one is found, the search stops there.
+ //
+ // (In fact there could be RRs in the database corresponding to subdomains
+ // of the delegation. The reason we do the search for the delegations
+ // first is because the delegation means that another zone is authoritative
+ // for the data and so should be consulted to retrieve it. RRs below
+ // this delegation point can be found in a search for glue but not
+ // otherwise; in the latter case they are said to be occluded by the
+ // presence of the delegation.)
+ const DelegationSearchResult dresult = findDelegationPoint(name, options);
+ if (dresult.rrset) {
+ return (FindResult(dresult.code, dresult.rrset));
+ }
+
+ // If there is no delegation, look for the exact match to the request
+ // name/type/class. However, there are special cases:
+ // - Requested name has a singleton CNAME record associated with it
+ // - Requested name is a delegation point (NS only but not at the zone
+ // apex - DNAME is ignored here as it redirects DNS names subordinate to
+ // the owner name - the owner name itself is not redirected.)
+ const bool is_origin = (name == getOrigin());
+ WantedTypes final_types(FINAL_TYPES());
+ final_types.insert(type);
+ const FoundRRsets found = getRRsets(name.toText(), final_types,
+ !is_origin);
+
+ if (found.first) {
+ // Something found at the domain name. Look into it further to get
+ // the final result.
+ return (findOnNameResult(name, type, options, is_origin, found, NULL));
} else {
- logger.debug(DBG_TRACE_DETAILED,
- DATASRC_DATABASE_FOUND_RRSET)
- .arg(accessor_->getDBName()).arg(*result_rrset);
+ // Did not find anything at all at the domain name, so check for
+ // subdomains or wildcards.
+ return (findNoNameResult(name, type, options, dresult));
}
- return (FindResult(result_status, result_rrset));
}
Name
@@ -669,10 +841,9 @@ DatabaseClient::Finder::findPreviousName(const Name& name) const {
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
- */
+
+ // 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&) {}
@@ -695,14 +866,12 @@ DatabaseClient::Finder::getClass() const {
namespace {
-/*
- * This needs, beside of converting all data from textual representation, group
- * together rdata of the same RRsets. To do this, we hold one row of data ahead
- * of iteration. When we get a request to provide data, we create it from this
- * data and load a new one. If it is to be put to the same rrset, we add it.
- * Otherwise we just return what we have and keep the row as the one ahead
- * for next time.
- */
+/// This needs, beside of converting all data from textual representation, group
+/// together rdata of the same RRsets. To do this, we hold one row of data ahead
+/// of iteration. When we get a request to provide data, we create it from this
+/// data and load a new one. If it is to be put to the same rrset, we add it.
+/// Otherwise we just return what we have and keep the row as the one ahead
+/// for next time.
class DatabaseIterator : public ZoneIterator {
public:
DatabaseIterator(shared_ptr<DatabaseAccessor> accessor,
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 81e6241..7b94735 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -18,14 +18,16 @@
#include <string>
#include <boost/scoped_ptr.hpp>
+#include <boost/tuple/tuple.hpp>
#include <dns/rrclass.h>
-#include <dns/rrclass.h>
#include <dns/rrset.h>
+#include <dns/rrtype.h>
#include <datasrc/data_source.h>
#include <datasrc/client.h>
#include <datasrc/client.h>
+#include <datasrc/logger.h>
#include <dns/name.h>
#include <exceptions/exceptions.h>
@@ -36,46 +38,41 @@
namespace isc {
namespace datasrc {
-/**
- * \brief Abstraction of lowlevel database with DNS data
- *
- * This class is defines interface to databases. Each supported database
- * will provide methods for accessing the data stored there in a generic
- * manner. The methods are meant to be low-level, without much or any knowledge
- * about DNS and should be possible to translate directly to queries.
- *
- * On the other hand, how the communication with database is done and in what
- * schema (in case of relational/SQL database) is up to the concrete classes.
- *
- * This class is non-copyable, as copying connections to database makes little
- * sense and will not be needed.
- *
- * \todo Is it true this does not need to be copied? For example the zone
- * iterator might need it's own copy. But a virtual clone() method might
- * be better for that than copy constructor.
- *
- * \note The same application may create multiple connections to the same
- * database, having multiple instances of this class. If the database
- * allows having multiple open queries at one connection, the connection
- * class may share it.
- */
+/// \brief Abstraction of lowlevel database with DNS data
+///
+/// This class is defines interface to databases. Each supported database
+/// will provide methods for accessing the data stored there in a generic
+/// manner. The methods are meant to be low-level, without much or any knowledge
+/// about DNS and should be possible to translate directly to queries.
+///
+/// On the other hand, how the communication with database is done and in what
+/// schema (in case of relational/SQL database) is up to the concrete classes.
+///
+/// This class is non-copyable, as copying connections to database makes little
+/// sense and will not be needed.
+///
+/// \todo Is it true this does not need to be copied? For example the zone
+/// iterator might need it's own copy. But a virtual clone() method might
+/// be better for that than copy constructor.
+///
+/// \note The same application may create multiple connections to the same
+/// database, having multiple instances of this class. If the database
+/// allows having multiple open queries at one connection, the connection
+/// class may share it.
class DatabaseAccessor : boost::noncopyable {
public:
- /**
- * Definitions of the fields as they are required to be filled in
- * by IteratorContext::getNext()
- *
- * When implementing getNext(), the columns array should
- * be filled with the values as described in this enumeration,
- * in this order, i.e. TYPE_COLUMN should be the first element
- * (index 0) of the array, TTL_COLUMN should be the second element
- * (index 1), etc.
- */
+ /// \brief Data columns for by IteratorContext::getNext()
+ ///
+ /// When implementing getNext(), the columns array should be filled with
+ /// the values as described in this enumeration, in this order, i.e.
+ /// - TYPE_COLUMN should be the first element (index 0) of the array,
+ /// - TTL_COLUMN should be the second element (index 1),
+ /// - etc.
enum RecordColumns {
TYPE_COLUMN = 0, ///< The RRType of the record (A/NS/TXT etc.)
TTL_COLUMN = 1, ///< The TTL of the record (a
- SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPE
- ///< the RRSIG covers. In the current implementation,
+ SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPEs
+ ///< the RRSIG cover. In the current implementation,
///< this field is ignored.
RDATA_COLUMN = 3, ///< Full text representation of the record's RDATA
NAME_COLUMN = 4, ///< The domain name of this RR
@@ -83,31 +80,26 @@ public:
///< the largest other element in this enum plus 1.
};
- /**
- * Definitions of the fields to be passed to addRecordToZone().
- *
- * Each derived implementation of addRecordToZone() should expect
- * the "columns" array to be filled with the values as described in this
- * enumeration, in this order.
- */
+ /// \brief Definitions of the fields to be passed to addRecordToZone()
+ ///
+ /// Each derived implementation of addRecordToZone() should expect
+ /// the "columns" array to be filled with the values as described in this
+ /// enumeration, in this order.
enum AddRecordColumns {
- ADD_NAME = 0, ///< The owner name of the record (a domain name)
- ADD_REV_NAME = 1, ///< Reversed name of NAME (used for DNSSEC)
- ADD_TTL = 2, ///< The TTL of the record (in numeric form)
- ADD_TYPE = 3, ///< The RRType of the record (A/NS/TXT etc.)
- ADD_SIGTYPE = 4, ///< For RRSIG records, this contains the RRTYPE
- ///< the RRSIG covers.
- ADD_RDATA = 5, ///< Full text representation of the record's RDATA
+ ADD_NAME = 0, ///< The owner name of the record (a domain name)
+ ADD_REV_NAME = 1, ///< Reversed name of NAME (used for DNSSEC)
+ ADD_TTL = 2, ///< The TTL of the record (in numeric form)
+ ADD_TYPE = 3, ///< The RRType of the record (A/NS/TXT etc.)
+ ADD_SIGTYPE = 4, ///< RRSIGs only: RRTYPEs the RRSIG covers.
+ ADD_RDATA = 5, ///< Full text representation of the record's RDATA
ADD_COLUMN_COUNT = 6 ///< Number of columns
};
- /**
- * Definitions of the fields to be passed to deleteRecordInZone().
- *
- * Each derived implementation of deleteRecordInZone() should expect
- * the "params" array to be filled with the values as described in this
- * enumeration, in this order.
- */
+ /// \brief Definitions of the fields to be passed to deleteRecordInZone()
+ ///
+ /// Each derived implementation of deleteRecordInZone() should expect
+ /// the "params" array to be filled with the values as described in this
+ /// enumeration, in this order.
enum DeleteRecordParams {
DEL_NAME = 0, ///< The owner name of the record (a domain name)
DEL_TYPE = 1, ///< The RRType of the record (A/NS/TXT etc.)
@@ -115,218 +107,199 @@ public:
DEL_PARAM_COUNT = 3 ///< Number of parameters
};
- /**
- * Operation mode when adding a record diff.
- *
- * This is used as the "operation" parameter value of addRecordDiff().
- */
+ /// \brief Operation mode when adding a record diff.
+ ///
+ /// This is used as the "operation" parameter value of addRecordDiff().
enum DiffOperation {
DIFF_ADD = 0, ///< This diff is for adding an RR
DIFF_DELETE = 1 ///< This diff is for deleting an RR
};
- /**
- * Definitions of the fields to be passed to addRecordDiff().
- *
- * Each derived implementation of addRecordDiff() should expect
- * the "params" array to be filled with the values as described in this
- * enumeration, in this order.
- */
+ /// \brief Definitions of the fields to be passed to addRecordDiff().
+ ///
+ /// Each derived implementation of addRecordDiff() should expect
+ /// the "params" array to be filled with the values as described in this
+ /// enumeration, in this order.
enum DiffRecordParams {
- DIFF_NAME = 0, ///< The owner name of the record (a domain name)
- DIFF_TYPE = 1, ///< The RRType of the record (A/NS/TXT etc.)
- DIFF_TTL = 2, ///< The TTL of the record (in numeric form)
- DIFF_RDATA = 3, ///< Full text representation of the record's RDATA
+ DIFF_NAME = 0, ///< Owner name of the record (a domain name)
+ DIFF_TYPE = 1, ///< The RRType of the record (A/NS/TXT etc.)
+ DIFF_TTL = 2, ///< The TTL of the record (in numeric form)
+ DIFF_RDATA = 3, ///< Full text representation of record's RDATA
DIFF_PARAM_COUNT = 4 ///< Number of parameters
};
- /**
- * \brief Destructor
- *
- * It is empty, but needs a virtual one, since we will use the derived
- * classes in polymorphic way.
- */
+ /// \brief Destructor
+ ///
+ /// It is empty, but needs a virtual one, since we will use the derived
+ /// classes in polymorphic way.
virtual ~DatabaseAccessor() { }
- /**
- * \brief Retrieve a zone identifier
- *
- * This method looks up a zone for the given name in the database. It
- * should match only exact zone name (eg. name is equal to the zone's
- * apex), as the DatabaseClient will loop trough the labels itself and
- * find the most suitable zone.
- *
- * It is not specified if and what implementation of this method may throw,
- * so code should expect anything.
- *
- * \param name The (fully qualified) domain name of the zone's apex to be
- * looked up.
- * \return The first part of the result indicates if a matching zone
- * was found. In case it was, the second part is internal zone ID.
- * This one will be passed to methods finding data in the zone.
- * It is not required to keep them, in which case whatever might
- * be returned - the ID is only passed back to the database as
- * an opaque handle.
- */
+ /// \brief Retrieve a zone identifier
+ ///
+ /// This method looks up a zone for the given name in the database. It
+ /// should match only exact zone name (eg. name is equal to the zone's
+ /// apex), as the DatabaseClient will loop trough the labels itself and
+ /// find the most suitable zone.
+ ///
+ /// It is not specified if and what implementation of this method may throw,
+ /// so code should expect anything.
+ ///
+ /// \param name The (fully qualified) domain name of the zone's apex to be
+ /// looked up.
+ /// \return The first part of the result indicates if a matching zone
+ /// was found. In case it was, the second part is internal zone ID.
+ /// This one will be passed to methods finding data in the zone.
+ /// It is not required to keep them, in which case whatever might
+ /// be returned - the ID is only passed back to the database as
+ /// an opaque handle.
virtual std::pair<bool, int> getZone(const std::string& name) const = 0;
- /**
- * \brief This holds the internal context of ZoneIterator for databases
- *
- * While the ZoneIterator implementation from DatabaseClient does all the
- * translation from strings to DNS classes and validation, this class
- * holds the pointer to where the database is at reading the data.
- *
- * It can either hold shared pointer to the connection which created it
- * and have some kind of statement inside (in case single database
- * connection can handle multiple concurrent SQL statements) or it can
- * create a new connection (or, if it is more convenient, the connection
- * itself can inherit both from DatabaseConnection and IteratorContext
- * and just clone itself).
- */
+ /// \brief This holds the internal context of ZoneIterator for databases
+ ///
+ /// While the ZoneIterator implementation from DatabaseClient does all the
+ /// translation from strings to DNS classes and validation, this class
+ /// holds the pointer to where the database is at reading the data.
+ ///
+ /// It can either hold shared pointer to the connection which created it
+ /// and have some kind of statement inside (in case single database
+ /// connection can handle multiple concurrent SQL statements) or it can
+ /// create a new connection (or, if it is more convenient, the connection
+ /// itself can inherit both from DatabaseConnection and IteratorContext
+ /// and just clone itself).
class IteratorContext : public boost::noncopyable {
public:
- /**
- * \brief Destructor
- *
- * Virtual destructor, so any descendand class is destroyed correctly.
- */
+ /// \brief Destructor
+ ///
+ /// Virtual destructor, so any descendand class is destroyed correctly.
virtual ~IteratorContext() { }
- /**
- * \brief Function to provide next resource record
- *
- * This function should provide data about the next resource record
- * from the data that is searched. The data is not converted yet.
- *
- * Depending on how the iterator was constructed, there is a difference
- * in behaviour; for a 'full zone iterator', created with
- * getAllRecords(), all COLUMN_COUNT elements of the array are
- * overwritten.
- * For a 'name iterator', created with getRecords(), the column
- * NAME_COLUMN is untouched, since what would be added here is by
- * definition already known to the caller (it already passes it as
- * an argument to getRecords()).
- *
- * Once this function returns false, any subsequent call to it should
- * result in false. The implementation of a derived class must ensure
- * it doesn't cause any disruption due to that such as a crash or
- * exception.
- *
- * \note The order of RRs is not strictly set, but the RRs for single
- * RRset must not be interleaved with any other RRs (eg. RRsets must be
- * "together").
- *
- * \param columns The data will be returned through here. The order
- * is specified by the RecordColumns enum, and the size must be
- * COLUMN_COUNT
- * \todo Do we consider databases where it is stored in binary blob
- * format?
- * \throw DataSourceError if there's database-related error. If the
- * exception (or any other in case of derived class) is thrown,
- * the iterator can't be safely used any more.
- * \return true if a record was found, and the columns array was
- * updated. false if there was no more data, in which case
- * the columns array is untouched.
- */
+ /// \brief Function to provide next resource record
+ ///
+ /// This function should provide data about the next resource record
+ /// from the data that is searched. The data is not converted yet.
+ ///
+ /// Depending on how the iterator was constructed, there is a difference
+ /// in behaviour; for a 'full zone iterator', created with
+ /// getAllRecords(), all COLUMN_COUNT elements of the array are
+ /// overwritten.
+ /// For a 'name iterator', created with getRecords(), the column
+ /// NAME_COLUMN is untouched, since what would be added here is by
+ /// definition already known to the caller (it already passes it as
+ /// an argument to getRecords()).
+ ///
+ /// Once this function returns false, any subsequent call to it should
+ /// result in false. The implementation of a derived class must ensure
+ /// it doesn't cause any disruption due to that such as a crash or
+ /// exception.
+ ///
+ /// \note The order of RRs is not strictly set, but the RRs for single
+ /// RRset must not be interleaved with any other RRs (eg. RRsets must be
+ /// "together").
+ ///
+ /// \param columns The data will be returned through here. The order
+ /// is specified by the RecordColumns enum, and the size must be
+ /// COLUMN_COUNT
+ /// \todo Do we consider databases where it is stored in binary blob
+ /// format?
+ /// \throw DataSourceError if there's database-related error. If the
+ /// exception (or any other in case of derived class) is thrown,
+ /// the iterator can't be safely used any more.
+ /// \return true if a record was found, and the columns array was
+ /// updated. false if there was no more data, in which case
+ /// the columns array is untouched.
virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) = 0;
};
typedef boost::shared_ptr<IteratorContext> IteratorContextPtr;
- /**
- * \brief Creates an iterator context for a specific name.
- *
- * Returns an IteratorContextPtr that contains all records of the
- * given name from the given zone.
- *
- * The implementation of the iterator that is returned may leave the
- * NAME_COLUMN column of the array passed to getNext() untouched, as that
- * data is already known (it is the same as the name argument here)
- *
- * \exception any Since any implementation can be used, the caller should
- * expect any exception to be thrown.
- *
- * \param name The name to search for. This should be a FQDN.
- * \param id The ID of the zone, returned from getZone().
- * \param subdomains If set to true, match subdomains of name instead
- * of name itself. It is used to find empty domains and match
- * wildcards.
- * \return Newly created iterator context. Must not be NULL.
- */
+ /// \brief Creates an iterator context for a specific name.
+ ///
+ /// Returns an IteratorContextPtr that contains all records of the
+ /// given name from the given zone.
+ ///
+ /// The implementation of the iterator that is returned may leave the
+ /// NAME_COLUMN column of the array passed to getNext() untouched, as that
+ /// data is already known (it is the same as the name argument here)
+ ///
+ /// \exception any Since any implementation can be used, the caller should
+ /// expect any exception to be thrown.
+ ///
+ /// \param name The name to search for. This should be a FQDN.
+ /// \param id The ID of the zone, returned from getZone().
+ /// \param subdomains If set to true, match subdomains of name instead
+ /// of name itself. It is used to find empty domains and match
+ /// wildcards.
+ /// \return Newly created iterator context. Must not be NULL.
virtual IteratorContextPtr getRecords(const std::string& name,
int id,
bool subdomains = false) const = 0;
- /**
- * \brief Creates an iterator context for the whole zone.
- *
- * Returns an IteratorContextPtr that contains all records of the
- * zone with the given zone id.
- *
- * Each call to getNext() on the returned iterator should copy all
- * column fields of the array that is passed, as defined in the
- * RecordColumns enum.
- *
- * \exception any Since any implementation can be used, the caller should
- * expect any exception to be thrown.
- *
- * \param id The ID of the zone, returned from getZone().
- * \return Newly created iterator context. Must not be NULL.
- */
+ /// \brief Creates an iterator context for the whole zone.
+ ///
+ /// Returns an IteratorContextPtr that contains all records of the
+ /// zone with the given zone id.
+ ///
+ /// Each call to getNext() on the returned iterator should copy all
+ /// column fields of the array that is passed, as defined in the
+ /// RecordColumns enum.
+ ///
+ /// \exception any Since any implementation can be used, the caller should
+ /// expect any exception to be thrown.
+ ///
+ /// \param id The ID of the zone, returned from getZone().
+ /// \return Newly created iterator context. Must not be NULL.
virtual IteratorContextPtr getAllRecords(int id) const = 0;
- /**
- * \brief Creates an iterator context for a set of differences.
- *
- * Returns an IteratorContextPtr that contains all difference records for
- * the given zone between two versions of a zone.
- *
- * The difference records are the set of records that would appear in an
- * IXFR serving a request for the difference between two versions of a zone.
- * The records are returned in the same order as they would be in the IXFR.
- * This means that if the the difference between versions of a zone with SOA
- * serial numbers of "start" and "end" is required, and the zone contains
- * the differences between serial number "start" to serial number
- * "intermediate" and from serial number "intermediate" to serial number
- * "end", the returned records will be (in order):
- *
- * \li SOA for serial "start"
- * \li Records removed from the zone between versions "start" and
- * "intermediate" of the zone. The order of these is not guaranteed.
- * \li SOA for serial "intermediate"
- * \li Records added to the zone between versions "start" and
- * "intermediate" of the zone. The order of these is not guaranteed.
- * \li SOA for serial "intermediate"
- * \li Records removed from the zone between versions "intermediate" and
- * "end" of the zone. The order of these is not guaranteed.
- * \li SOA for serial "end"
- * \li Records added to the zone between versions "intermediate" and "end"
- * of the zone. The order of these is not guaranteed.
- *
- * Note that there is no requirement that "start" be less than "end". Owing
- * to serial number arithmetic, it is entirely possible that a later version
- * of a zone will have a smaller SOA serial number than an earlier version.
- *
- * Each call to getNext() on the returned iterator should copy all
- * column fields of the array that is passed, as defined in the
- * RecordColumns enum.
- *
- * \exception any Since any implementation can be used, the caller should
- * expect any exception to be thrown.
- *
- * \param id The ID of the zone, returned from getZone().
- * \param start The SOA serial number of the version of the zone from
- * which the difference sequence should start.
- * \param end The SOA serial number of the version of the zone at which
- * the difference sequence should end.
- *
- * \return Newly created iterator context. Must not be NULL.
- */
+ /// \brief Creates an iterator context for a set of differences.
+ ///
+ /// Returns an IteratorContextPtr that contains all difference records for
+ /// the given zone between two versions of a zone.
+ ///
+ /// The difference records are the set of records that would appear in an
+ /// IXFR serving a request for the difference between two versions of a
+ /// zone. The records are returned in the same order as they would be in
+ /// the IXFR. This means that if the the difference between versions of a
+ /// zone with SOA serial numbers of "start" and "end" is required, and the
+ /// zone contains the differences between serial number "start" to serial
+ /// number "intermediate" and from serial number "intermediate" to serial
+ /// number "end", the returned records will be (in order):
+ ///
+ /// \li SOA for serial "start"
+ /// \li Records removed from the zone between versions "start" and
+ /// "intermediate" of the zone. The order of these is not guaranteed.
+ /// \li SOA for serial "intermediate"
+ /// \li Records added to the zone between versions "start" and
+ /// "intermediate" of the zone. The order of these is not guaranteed.
+ /// \li SOA for serial "intermediate"
+ /// \li Records removed from the zone between versions "intermediate" and
+ /// "end" of the zone. The order of these is not guaranteed.
+ /// \li SOA for serial "end"
+ /// \li Records added to the zone between versions "intermediate" and "end"
+ /// of the zone. The order of these is not guaranteed.
+ ///
+ /// Note that there is no requirement that "start" be less than "end".
+ /// Owing to serial number arithmetic, it is entirely possible that a later
+ /// version of a zone will have a smaller SOA serial number than an earlier
+ /// version.
+ ///
+ /// Each call to getNext() on the returned iterator should copy all column
+ /// fields of the array that is passed, as defined in the RecordColumns
+ /// enum.
+ ///
+ /// \exception any Since any implementation can be used, the caller should
+ /// expect any exception to be thrown.
+ ///
+ /// \param id The ID of the zone, returned from getZone().
+ /// \param start The SOA serial number of the version of the zone from
+ /// which the difference sequence should start.
+ /// \param end The SOA serial number of the version of the zone at which
+ /// the difference sequence should end.
+ ///
+ /// \return Newly created iterator context. Must not be NULL.
virtual IteratorContextPtr
getDiffs(int id, uint32_t start, uint32_t end) const = 0;
- /// Start a transaction for updating a zone.
+ /// \brief Start a transaction for updating a zone.
///
/// Each derived class version of this method starts a database
/// transaction to make updates to the given name of zone (whose class was
@@ -385,7 +358,7 @@ public:
virtual std::pair<bool, int> startUpdateZone(const std::string& zone_name,
bool replace) = 0;
- /// Add a single record to the zone to be updated.
+ /// \brief Add a single record to the zone to be updated.
///
/// This method provides a simple interface to insert a new record
/// (a database "row") to the zone in the update context started by
@@ -424,7 +397,7 @@ public:
virtual void addRecordToZone(
const std::string (&columns)[ADD_COLUMN_COUNT]) = 0;
- /// Delete a single record from the zone to be updated.
+ /// \brief Delete a single record from the zone to be updated.
///
/// This method provides a simple interface to delete a record
/// (a database "row") from the zone in the update context started by
@@ -461,7 +434,7 @@ public:
virtual void deleteRecordInZone(
const std::string (¶ms)[DEL_PARAM_COUNT]) = 0;
- /// Start a general transaction.
+ /// \brief Start a general transaction.
///
/// Each derived class version of this method starts a database
/// transaction in a way specific to the database details. Any subsequent
@@ -481,7 +454,7 @@ public:
/// internal database related error.
virtual void startTransaction() = 0;
- /// Commit a transaction.
+ /// \brief Commit a transaction.
///
/// This method completes a transaction started by \c startTransaction
/// or \c startUpdateZone.
@@ -504,7 +477,7 @@ public:
/// to the method or internal database error.
virtual void commit() = 0;
- /// Rollback any changes in a transaction made so far.
+ /// \brief Rollback any changes in a transaction made so far.
///
/// This method rollbacks a transaction started by \c startTransaction or
/// \c startUpdateZone. When it succeeds (it normally should, but see
@@ -530,7 +503,7 @@ public:
/// to the method or internal database error.
virtual void rollback() = 0;
- /// Install a single RR diff in difference sequences for zone update.
+ /// \brief Install a single RR diff in difference sequences for zone update.
///
/// This method inserts parameters of an update operation for a single RR
/// (either adding or deleting one) in the underlying database.
@@ -604,7 +577,7 @@ public:
int zone_id, uint32_t serial, DiffOperation operation,
const std::string (¶ms)[DIFF_PARAM_COUNT]) = 0;
- /// Clone the accessor with the same configuration.
+ /// \brief Clone the accessor with the same configuration.
///
/// Each derived class implementation of this method will create a new
/// accessor of the same derived class with the same configuration
@@ -633,187 +606,169 @@ public:
/// \return A shared pointer to the cloned accessor.
virtual boost::shared_ptr<DatabaseAccessor> clone() = 0;
- /**
- * \brief Returns a string identifying this dabase backend
- *
- * The returned string is mainly intended to be used for
- * debugging/logging purposes.
- *
- * Any implementation is free to choose the exact string content,
- * but it is advisable to make it a name that is distinguishable
- * from the others.
- *
- * \return the name of the database
- */
+ /// \brief Returns a string identifying this dabase backend
+ ///
+ /// The returned string is mainly intended to be used for
+ /// debugging/logging purposes.
+ ///
+ /// Any implementation is free to choose the exact string content,
+ /// but it is advisable to make it a name that is distinguishable
+ /// from the others.
+ ///
+ /// \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).
- */
+ /// \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;
};
-/**
- * \brief Concrete data source client oriented at database backends.
- *
- * This class (together with corresponding versions of ZoneFinder,
- * ZoneIterator, etc.) translates high-level data source queries to
- * low-level calls on DatabaseAccessor. It calls multiple queries
- * if necessary and validates data from the database, allowing the
- * DatabaseAccessor to be just simple translation to SQL/other
- * queries to database.
- *
- * While it is possible to subclass it for specific database in case
- * of special needs, it is not expected to be needed. This should just
- * work as it is with whatever DatabaseAccessor.
- */
+/// \brief Concrete data source client oriented at database backends.
+///
+/// This class (together with corresponding versions of ZoneFinder,
+/// ZoneIterator, etc.) translates high-level data source queries to
+/// low-level calls on DatabaseAccessor. It calls multiple queries
+/// if necessary and validates data from the database, allowing the
+/// DatabaseAccessor to be just simple translation to SQL/other
+/// queries to database.
+///
+/// While it is possible to subclass it for specific database in case
+/// of special needs, it is not expected to be needed. This should just
+/// work as it is with whatever DatabaseAccessor.
class DatabaseClient : public DataSourceClient {
public:
- /**
- * \brief Constructor
- *
- * It initializes the client with a database via the given accessor.
- *
- * \exception isc::InvalidParameter if accessor is NULL. It might throw
- * standard allocation exception as well, but doesn't throw anything else.
- *
- * \param rrclass The RR class of the zones that this client will handle.
- * \param accessor The accessor to the database to use to get data.
- * As the parameter suggests, the client takes ownership of the accessor
- * and will delete it when itself deleted.
- */
+ /// \brief Constructor
+ ///
+ /// It initializes the client with a database via the given accessor.
+ ///
+ /// \exception isc::InvalidParameter if accessor is NULL. It might throw
+ /// standard allocation exception as well, but doesn't throw anything else.
+ ///
+ /// \param rrclass The RR class of the zones that this client will handle.
+ /// \param accessor The accessor to the database to use to get data.
+ /// As the parameter suggests, the client takes ownership of the accessor
+ /// and will delete it when itself deleted.
DatabaseClient(isc::dns::RRClass rrclass,
boost::shared_ptr<DatabaseAccessor> accessor);
- /**
- * \brief Corresponding ZoneFinder implementation
- *
- * The zone finder implementation for database data sources. Similarly
- * to the DatabaseClient, it translates the queries to methods of the
- * database.
- *
- * Application should not come directly in contact with this class
- * (it should handle it trough generic ZoneFinder pointer), therefore
- * it could be completely hidden in the .cc file. But it is provided
- * to allow testing and for rare cases when a database needs slightly
- * different handling, so it can be subclassed.
- *
- * Methods directly corresponds to the ones in ZoneFinder.
- */
+ /// \brief Corresponding ZoneFinder implementation
+ ///
+ /// The zone finder implementation for database data sources. Similarly
+ /// to the DatabaseClient, it translates the queries to methods of the
+ /// database.
+ ///
+ /// Application should not come directly in contact with this class
+ /// (it should handle it trough generic ZoneFinder pointer), therefore
+ /// it could be completely hidden in the .cc file. But it is provided
+ /// to allow testing and for rare cases when a database needs slightly
+ /// different handling, so it can be subclassed.
+ ///
+ /// Methods directly corresponds to the ones in ZoneFinder.
class Finder : public ZoneFinder {
public:
- /**
- * \brief Constructor
- *
- * \param database The database (shared with DatabaseClient) to
- * be used for queries (the one asked for ID before).
- * \param zone_id The zone ID which was returned from
- * DatabaseAccessor::getZone and which will be passed to further
- * calls to the database.
- * \param origin The name of the origin of this zone. It could query
- * it from database, but as the DatabaseClient just searched for
- * the zone using the name, it should have it.
- */
+ /// \brief Constructor
+ ///
+ /// \param database The database (shared with DatabaseClient) to
+ /// be used for queries (the one asked for ID before).
+ /// \param zone_id The zone ID which was returned from
+ /// DatabaseAccessor::getZone and which will be passed to further
+ /// calls to the database.
+ /// \param origin The name of the origin of this zone. It could query
+ /// it from database, but as the DatabaseClient just searched for
+ /// the zone using the name, it should have it.
Finder(boost::shared_ptr<DatabaseAccessor> database, int zone_id,
const isc::dns::Name& origin);
+
// The following three methods are just implementations of inherited
// ZoneFinder's pure virtual methods.
virtual isc::dns::Name getOrigin() const;
virtual isc::dns::RRClass getClass() const;
- /**
- * \brief Find an RRset in the datasource
- *
- * Searches the datasource for an RRset of the given name and
- * type. If there is a CNAME at the given name, the CNAME rrset
- * is returned.
- * (this implementation is not complete, and currently only
- * does full matches, CNAMES, and the signatures for matches and
- * CNAMEs)
- * \note target was used in the original design to handle ANY
- * queries. This is not implemented yet, and may use
- * target again for that, but it might also use something
- * different. It is left in for compatibility at the moment.
- * \note options are ignored at this moment
- *
- * \note Maybe counter intuitively, this method is not a const member
- * function. This is intentional; some of the underlying implementations
- * are expected to use a database backend, and would internally contain
- * some abstraction of "database connection". In the most strict sense
- * any (even read only) operation might change the internal state of
- * such a connection, and in that sense the operation cannot be considered
- * "const". In order to avoid giving a false sense of safety to the
- * caller, we indicate a call to this method may have a surprising
- * side effect. That said, this view may be too strict and it may
- * make sense to say the internal database connection doesn't affect
- * external behavior in terms of the interface of this method. As
- * we gain more experiences with various kinds of backends we may
- * revisit the constness.
- *
- * \exception DataSourceError when there is a problem reading
- * the data from the dabase backend.
- * This can be a connection, code, or
- * data (parse) error.
- *
- * \param name The name to find
- * \param type The RRType to find
- * \param target Unused at this moment
- * \param options Options about how to search.
- * See ZoneFinder::FindOptions.
- */
+ /// \brief Find an RRset in the datasource
+ ///
+ /// Searches the datasource for an RRset of the given name and
+ /// type. If there is a CNAME at the given name, the CNAME rrset
+ /// is returned.
+ /// (this implementation is not complete, and currently only
+ /// does full matches, CNAMES, and the signatures for matches and
+ /// CNAMEs)
+ /// \note target was used in the original design to handle ANY
+ /// queries. This is not implemented yet, and may use
+ /// target again for that, but it might also use something
+ /// different. It is left in for compatibility at the moment.
+ /// \note options are ignored at this moment
+ ///
+ /// \note Maybe counter intuitively, this method is not a const member
+ /// function. This is intentional; some of the underlying
+ /// implementations are expected to use a database backend, and would
+ /// internally contain some abstraction of "database connection". In
+ /// the most strict sense any (even read only) operation might change
+ /// the internal state of such a connection, and in that sense the
+ /// operation cannot be considered "const". In order to avoid giving a
+ /// false sense of safety to the caller, we indicate a call to this
+ /// method may have a surprising side effect. That said, this view may
+ /// be too strict and it may make sense to say the internal database
+ /// connection doesn't affect external behavior in terms of the
+ /// interface of this method. As we gain more experiences with various
+ /// kinds of backends we may revisit the constness.
+ ///
+ /// \exception DataSourceError when there is a problem reading
+ /// the data from the dabase backend.
+ /// This can be a connection, code, or
+ /// data (parse) error.
+ ///
+ /// \param name The name to find
+ /// \param type The RRType to find
+ /// \param target Unused at this moment
+ /// \param options Options about how to search.
+ /// See ZoneFinder::FindOptions.
virtual FindResult find(const isc::dns::Name& name,
const isc::dns::RRType& type,
isc::dns::RRsetList* target = NULL,
const FindOptions options = FIND_DEFAULT);
- /**
- * \brief Implementation of ZoneFinder::findPreviousName method.
- */
+ /// \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
- * constructor. This is meant for testing purposes and normal
- * applications shouldn't need it.
- */
+ /// \brief The zone ID
+ ///
+ /// This function provides the stored zone ID as passed to the
+ /// constructor. This is meant for testing purposes and normal
+ /// applications shouldn't need it.
int zone_id() const { return (zone_id_); }
- /**
- * \brief The database accessor.
- *
- * This function provides the database accessor stored inside as
- * passed to the constructor. This is meant for testing purposes and
- * normal applications shouldn't need it.
- */
+ /// \brief The database accessor.
+ ///
+ /// This function provides the database accessor stored inside as
+ /// passed to the constructor. This is meant for testing purposes and
+ /// normal applications shouldn't need it.
const DatabaseAccessor& getAccessor() const {
return (*accessor_);
}
+
private:
boost::shared_ptr<DatabaseAccessor> accessor_;
const int zone_id_;
@@ -824,103 +779,308 @@ public:
FoundRRsets;
/// \brief Just shortcut for set of types
typedef std::set<dns::RRType> WantedTypes;
- /**
- * \brief Searches database for RRsets of one domain.
- *
- * This method scans RRs of single domain specified by name and
- * extracts any RRsets found and requested by 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 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.
- */
+
+ /// \brief Search result of \c findDelegationPoint().
+ ///
+ /// This is a tuple combining the result of the search - a status code
+ /// and a pointer to the RRset found - together with additional
+ /// information needed for subsequent processing, an indication of
+ /// the first NS RRset found in the search and the number of labels
+ /// in the last non-empty domain encountered in the search. It is
+ /// used by \c findDelegationPoint().
+ ///
+ /// The last two items are located naturally in the search and although
+ /// not strictly part of the result, they are passed back to avoid
+ /// another (duplicate) search later in the processing.
+ ///
+ /// Note that the code and rrset elements are the same as that in
+ /// the \c ZoneFinder::FindResult struct: this structure could be
+ /// derived from that one, but as it is used just once in the code and
+ /// will never be treated as a \c FindResult, the obscurity involved in
+ /// deriving it from a parent class was deemed not worthwhile.
+ struct DelegationSearchResult {
+ DelegationSearchResult(const ZoneFinder::Result param_code,
+ const isc::dns::ConstRRsetPtr param_rrset,
+ const isc::dns::ConstRRsetPtr param_ns,
+ size_t param_last_known) :
+ code(param_code), rrset(param_rrset),
+ first_ns(param_ns),
+ last_known(param_last_known)
+ {}
+ const ZoneFinder::Result code; ///< Result code
+ const isc::dns::ConstRRsetPtr rrset; ///< RRset found
+ const isc::dns::ConstRRsetPtr first_ns; ///< First NS found
+ const size_t last_known; ///< No. labels in last non-empty domain
+ };
+
+ /// \brief Searches database for RRsets of one domain.
+ ///
+ /// This method scans RRs of single domain specified by name and
+ /// extracts any RRsets found and requested by 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 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.
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.
- *
- * This looks if there's any subdomain of the given name. It can be
- * used to test if domain is empty non-terminal.
- *
- * \param name The domain to check.
- */
+
+ /// \brief Find delegation point
+ ///
+ /// Given a name, searches through the superdomains from the origin
+ /// down, searching for a point that indicates a delegation (i.e. an
+ /// NS record or a DNAME).
+ ///
+ /// The method operates in two modes, non-glue-ok and glue-ok modes:
+ ///
+ /// In non-glue-ok mode, the search is made purely for the NS or DNAME
+ /// RR. The zone is searched from the origin down looking for one
+ /// of these RRTypes (and ignoring the NS records at the zone origin).
+ /// A status is returned indicating what is found: DNAME, DELEGATION
+ /// of SUCCESS, the last indicating that nothing was found, together
+ /// with a pointer to the relevant RR.
+ ///
+ /// In glue-ok mode, the first NS encountered in the search (apart from
+ /// the NS at the zone apex) is remembered but otherwise NS records are
+ /// ignored and the search attempts to find a DNAME. The result is
+ /// returned in the same format, along with a pointer to the first non-
+ /// apex NS (if found).
+ ///
+ /// \param name The name to find
+ /// \param options Options about how to search. See the documentation
+ /// for ZoneFinder::FindOptions.
+ ///
+ /// \return Tuple holding the result of the search - the RRset of the
+ /// delegation point and the type of the point (DELEGATION or
+ /// DNAME) - and associated information. This latter item
+ /// comprises two pieces of data: a pointer to the highest
+ /// encountered NS, and the number of labels in the last known
+ /// non-empty domain. The associated information is found as
+ /// a natural part of the search for the delegation point and
+ /// is used later in the find() processing; it is passed back
+ /// to avoid the need to perform a second search to obtain it.
+ DelegationSearchResult
+ findDelegationPoint(const isc::dns::Name& name,
+ const FindOptions options);
+
+ /// \brief Find wildcard match
+ ///
+ /// Having found that the name is not an empty non-terminal, this
+ /// searches the zone for for wildcards that match the name.
+ ///
+ /// It searches superdomains of the name from the zone origin down
+ /// looking for a wildcard in the zone that matches the name. There
+ /// are several cases to consider:
+ ///
+ /// - If the previous search for a delegation point has found that
+ /// there is an NS at the superdomain of the point at which the
+ /// wildcard is found, the delegation is returned.
+ /// - If there is a match to the name, an appropriate status is
+ /// returned (match on requested type, delegation, cname, or just
+ /// the indication of a match but no RRs relevant to the query).
+ /// - If the match is to an non-empty non-terminal wildcard, a
+ /// wildcard NXRRSET is returned.
+ ///
+ /// Note that if DNSSEC is enabled for the search and the zone uses
+ /// NSEC for authenticated denial of existence, the search may
+ /// return NSEC records.
+ ///
+ /// \param name The name to find
+ /// \param type The RRType to find
+ /// \param options Options about how to search. See the documentation
+ /// for ZoneFinder::FindOptions.
+ /// \param dresult Result of the search through the zone for a
+ /// delegation.
+ ///
+ /// \return Tuple holding the result of the search - the RRset of the
+ /// wildcard records matching the name, together with a status
+ /// indicating the match type (e.g. CNAME at the wildcard
+ /// match, no RRs of the requested type at the wildcard,
+ /// success due to an exact match). Also returned if there
+ /// is no match is an indication as to whether there was an
+ /// NXDOMAIN or an NXRRSET.
+ FindResult findWildcardMatch(
+ const isc::dns::Name& name,
+ const isc::dns::RRType& type, const FindOptions options,
+ const DelegationSearchResult& dresult);
+
+ /// \brief Handle matching results for name
+ ///
+ /// This is called when something is found in the underlying database
+ /// whose domain name is an exact match of the name to be searched for.
+ /// It explores four possible cases to decide the final lookup result:
+ /// - The name is a zone cut due to an NS RR.
+ /// - CNAME is found (while the requested RR type is not CNAME).
+ /// In this case multiple CNAMEs are checked and rejected with
+ /// a \c DataSourceError exception.
+ /// - Requested type is not found at that name.
+ /// - A record of the requested type is found.
+ /// and returns a corresponding find result.
+ ///
+ /// This method is commonly used for normal (non wildcard) and wildcard
+ /// matches.
+ ///
+ /// \param name The name to find
+ /// \param type The RRType to find
+ /// \param options Options about how to search. See the documentation
+ /// for ZoneFinder::FindOptions.
+ /// \param is_origin If name is the zone's origin name.
+ /// \param found A set of found RRsets in the search for the name
+ /// and type. It could contain one or more of the requested
+ /// type, CNAME, NS, and NSEC RRsets of the name.
+ /// \param wildname If non NULL, the method is called on a wildcard
+ /// match, and points to a string object representing
+ /// a textual form of the matched wildcard name;
+ /// it's NULL in the case of non wildcard match.
+ ///
+ /// \return Tuple holding the result of the search - the RRset of the
+ /// wildcard records matching the name, together with a status
+ /// indicating the match type (corresponding to the each of
+ /// the above 4 cases). The return value is intended to be
+ /// usable as a return value of the caller of this helper
+ /// method.
+ FindResult findOnNameResult(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options,
+ const bool is_origin,
+ const FoundRRsets& found,
+ const std::string* wildname);
+
+ /// \brief Handle no match for name
+ ///
+ /// This is called when it is known that there is no delegation and
+ /// there is no exact match for the name (regardless of RR types
+ /// requested). Before returning NXDOMAIN, we need to check two
+ /// cases:
+ /// - Empty non-terminal: if the name has subdomains in the database,
+ /// flag the fact. An NXRRSET will be returned (along with the
+ /// NSEC record covering the requested domain name if DNSSEC data
+ /// is being returned).
+ /// - Wildcard: is there a wildcard record in the zone that matches
+ /// requested name? If so, return it. If not, return the relevant
+ /// NSEC records (if requested).
+ ///
+ /// \param name The name to find
+ /// \param type The RRType to find
+ /// \param options Options about how to search. See the documentation
+ /// for ZoneFinder::FindOptions.
+ /// \param dresult Result of the search through the zone for a
+ /// delegation.
+ ///
+ /// \return Tuple holding the result of the search - the RRset of the
+ /// wildcard records matching the name, together with a status
+ /// indicating the match type (e.g. CNAME at the wildcard
+ /// match, no RRs of the requested type at the wildcard,
+ /// success due to an exact match).
+ FindResult findNoNameResult(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ FindOptions options,
+ const DelegationSearchResult& dresult);
+
+ /// Logs condition and creates result
+ ///
+ /// A convenience function used by findOnNameResult(), it both creates
+ /// the FindResult object that find() will return to its caller as well
+ /// as logging a debug message for the information being returned.
+ ///
+ /// \param name Domain name of the RR that was being sought.
+ /// \param wildname Domain name string of a matched wildcard name or
+ /// NULL for non wildcard match.
+ /// \param type Type of RR being sought.
+ /// \param code Result of the find operation
+ /// \param rrset RRset found as a result of the find (which may be
+ /// null).
+ /// \param log_id ID of the message being logged. Up to five
+ /// parameters are available to the message: data source name,
+ /// requested domain name, requested class, requested type
+ /// and (but only if the search was successful and returned
+ /// an RRset) details of the RRset found.
+ ///
+ /// \return FindResult object constructed from the code and rrset
+ /// arguments.
+ FindResult logAndCreateResult(const isc::dns::Name& name,
+ const std::string* wildname,
+ const isc::dns::RRType& type,
+ ZoneFinder::Result code,
+ isc::dns::ConstRRsetPtr rrset,
+ const isc::log::MessageID& log_id) const;
+
+ /// \brief Checks if something lives below this domain.
+ ///
+ /// This looks if there's any subdomain of the given name. It can be
+ /// used to test if domain is empty non-terminal.
+ ///
+ /// \param name The domain to check.
+ ///
+ /// \return true if the name has subdomains, false if not.
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.
- */
+ /// \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.
- */
+ /// \brief Convenience type shortcut.
+ ///
+ /// To find stuff in the result of getRRsets.
typedef std::map<dns::RRType, dns::RRsetPtr>::const_iterator
FoundIterator;
};
- /**
- * \brief Find a zone in the database
- *
- * This queries database's getZone to find the best matching zone.
- * It will propagate whatever exceptions are thrown from that method
- * (which is not restricted in any way).
- *
- * \param name Name of the zone or data contained there.
- * \return FindResult containing the code and an instance of Finder, if
- * anything is found. However, application should not rely on the
- * ZoneFinder being instance of Finder (possible subclass of this class
- * may return something else and it may change in future versions), it
- * should use it as a ZoneFinder only.
- */
+ /// \brief Find a zone in the database
+ ///
+ /// This queries database's getZone to find the best matching zone.
+ /// It will propagate whatever exceptions are thrown from that method
+ /// (which is not restricted in any way).
+ ///
+ /// \param name Name of the zone or data contained there.
+ /// \return FindResult containing the code and an instance of Finder, if
+ /// anything is found. However, application should not rely on the
+ /// ZoneFinder being instance of Finder (possible subclass of this class
+ /// may return something else and it may change in future versions), it
+ /// should use it as a ZoneFinder only.
virtual FindResult findZone(const isc::dns::Name& name) const;
- /**
- * \brief Get the zone iterator
- *
- * The iterator allows going through the whole zone content. If the
- * underlying DatabaseConnection is implemented correctly, it should
- * be possible to have multiple ZoneIterators at once and query data
- * at the same time.
- *
- * \exception DataSourceError if the zone doesn't exist.
- * \exception isc::NotImplemented if the underlying DatabaseConnection
- * doesn't implement iteration. But in case it is not implemented
- * and the zone doesn't exist, DataSourceError is thrown.
- * \exception Anything else the underlying DatabaseConnection might
- * want to throw.
- * \param name The origin of the zone to iterate.
- * \param separate_rrs If true, the iterator will return each RR as a
- * new RRset object. If false, the iterator will
- * combine consecutive RRs with the name and type
- * into 1 RRset. The capitalization of the RRset will
- * be that of the first RR read, and TTLs will be
- * adjusted to the lowest one found.
- * \return Shared pointer to the iterator (it will never be NULL)
- */
+ /// \brief Get the zone iterator
+ ///
+ /// The iterator allows going through the whole zone content. If the
+ /// underlying DatabaseConnection is implemented correctly, it should
+ /// be possible to have multiple ZoneIterators at once and query data
+ /// at the same time.
+ ///
+ /// \exception DataSourceError if the zone doesn't exist.
+ /// \exception isc::NotImplemented if the underlying DatabaseConnection
+ /// doesn't implement iteration. But in case it is not implemented
+ /// and the zone doesn't exist, DataSourceError is thrown.
+ /// \exception Anything else the underlying DatabaseConnection might
+ /// want to throw.
+ /// \param name The origin of the zone to iterate.
+ /// \param separate_rrs If true, the iterator will return each RR as a
+ /// new RRset object. If false, the iterator will
+ /// combine consecutive RRs with the name and type
+ /// into 1 RRset. The capitalization of the RRset will
+ /// be that of the first RR read, and TTLs will be
+ /// adjusted to the lowest one found.
+ /// \return Shared pointer to the iterator (it will never be NULL)
virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name,
bool separate_rrs = false) const;
@@ -953,7 +1113,3 @@ private:
}
#endif // __DATABASE_DATASRC_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index b4d0df7..01fb082 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -68,7 +68,7 @@ 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
+% DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3/%4
Debug information. The database data source is looking up records with the given
name and type in the database.
@@ -78,11 +78,17 @@ different TTL values. This isn't allowed on the wire and is considered
an error, so we set it to the lowest value we found (but we don't modify the
database). The data in database should be checked and fixed.
+% DATASRC_DATABASE_FOUND_CNAME search in datasource %1 for %2/%3/%4 found CNAME, resulting in %5
+When searching the domain for a name a CNAME was found at that name.
+Even though it was not the RR type being sought, it is returned. (The
+caller may want to continue the lookup by replacing the query name with
+the canonical name and restarting the query with the original RR type.)
+
% DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %2 in %1
When searching for a domain, the program met a delegation to a different zone
at the given domain name. It will return that one instead.
-% DATASRC_DATABASE_FOUND_DELEGATION_EXACT Found delegation at %2 (exact match) in %1
+% DATASRC_DATABASE_FOUND_DELEGATION_EXACT search in datasource %1 for %2/%3/%4 found delegation at %5
The program found the domain requested, but it is a delegation point to a
different zone, therefore it is not authoritative for this domain name.
It will return the NS record instead.
@@ -93,19 +99,25 @@ place in the domain space at the given domain name. It will return that one
instead.
% DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL empty non-terminal %2 in %1
-The domain name doesn't have any RRs, so it doesn't exist in the database.
-However, it has a subdomain, so it exists in the DNS address space. So we
-return NXRRSET instead of NXDOMAIN.
+The domain name does not have any RRs associated with it, so it doesn't
+exist in the database. However, it has a subdomain, so it does exist
+in the DNS address space. This type of domain is known an an "empty
+non-terminal" and so we return NXRRSET instead of NXDOMAIN.
% DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4
The data returned by the database backend did not contain any data for the given
domain name, class and type.
-% DATASRC_DATABASE_FOUND_NXRRSET search in datasource %1 resulted in NXRRSET for %2/%3/%4
+% DATASRC_DATABASE_FOUND_NXRRSET search in datasource %1 for %2/%3/%4 resulted in NXRRSET
The data returned by the database backend contained data for the given domain
name and class, but not for the given type.
-% DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %2
+% DATASRC_DATABASE_FOUND_NXRRSET_NSEC search in datasource %1 for %2/%3/%4 resulted in RRset %5
+A search in the database for RRs for the specified name, type and class has
+located RRs that match the name and class but not the type. DNSSEC information
+has been requested and returned.
+
+% DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %5
The data returned by the database backend contained data for the given domain
name, and it either matches the type or has a relevant type. The RRset that is
returned is printed.
@@ -127,11 +139,46 @@ were found to be different. This isn't allowed on the wire and is considered
an error, so we set it to the lowest value we found (but we don't modify the
database). The data in database should be checked and fixed.
-% DATASRC_DATABASE_WILDCARD constructing RRset %3 from wildcard %2 in %1
-The database doesn't contain directly matching domain, but it does contain a
-wildcard one which is being used to synthesize the answer.
+% DATASRC_DATABASE_NO_MATCH not match for %2/%3/%4 in %1
+No match (not even a wildcard) was found in the named data source for the given
+name/type/class in the data source.
+
+% DATASRC_DATABASE_UPDATER_COMMIT updates committed for '%1/%2' on %3
+Debug information. A set of updates to a zone has been successfully
+committed to the corresponding database backend. The zone name,
+its class and the database name are printed.
+
+% DATASRC_DATABASE_UPDATER_CREATED zone updater created for '%1/%2' on %3
+Debug information. A zone updater object is created to make updates to
+the shown zone on the shown backend database.
+
+% DATASRC_DATABASE_UPDATER_DESTROYED zone updater destroyed for '%1/%2' on %3
+Debug information. A zone updater object is destroyed, either successfully
+or after failure of, making updates to the shown zone on the shown backend
+database.
+
+% DATASRC_DATABASE_UPDATER_ROLLBACK zone updates roll-backed for '%1/%2' on %3
+A zone updater is being destroyed without committing the changes.
+This would typically mean the update attempt was aborted due to some
+error, but may also be a bug of the application that forgets committing
+the changes. The intermediate changes made through the updater won't
+be applied to the underlying database. The zone name, its class, and
+the underlying database name are shown in the log message.
-% DATASRC_DATABASE_WILDCARD_CANCEL_NS canceled wildcard match on %2 because %3 contains NS in %1
+% DATASRC_DATABASE_UPDATER_ROLLBACKFAIL failed to roll back zone updates for '%1/%2' on %3: %4
+A zone updater is being destroyed without committing the changes to
+the database, and attempts to rollback incomplete updates, but it
+unexpectedly fails. The higher level implementation does not expect
+it to fail, so this means either a serious operational error in the
+underlying data source (such as a system failure of a database) or
+software bug in the underlying data source implementation. In either
+case if this message is logged the administrator should carefully
+examine the underlying data source to see what exactly happens and
+whether the data is still valid. The zone name, its class, and the
+underlying database name as well as the error message thrown from the
+database module are shown in the log message.
+
+% DATASRC_DATABASE_WILDCARD_CANCEL_NS canceled wildcard match on %3 because %2 contains NS (data source %1)
The database was queried to provide glue data and it didn't find direct match.
It could create it from given wildcard, but matching wildcards is forbidden
under a zone cut, which was found. Therefore the delegation will be returned
@@ -143,11 +190,31 @@ exists, therefore this name is something like empty non-terminal (actually,
from the protocol point of view, it is empty non-terminal, but the code
discovers it differently).
-% DATASRC_DATABASE_WILDCARD_EMPTY implicit wildcard %2 used to construct %3 in %1
-The given wildcard exists implicitly in the domainspace, as empty nonterminal
-(eg. there's something like subdomain.*.example.org, so *.example.org exists
-implicitly, but is empty). This will produce NXRRSET, because the constructed
-domain is empty as well as the wildcard.
+% DATASRC_DATABASE_WILDCARD_CNAME search in datasource %1 for %2/%3/%4 found wildcard CNAME at %5, resulting in %6
+The database doesn't contain directly matching name. When searching
+for a wildcard match, a CNAME RR was found at a wildcard record
+matching the name. This is returned as the result of the search.
+
+% DATASRC_DATABASE_WILDCARD_EMPTY found subdomains of %2 which is a wildcard match for %3 in %1
+The given wildcard matches the name being sough but it as an empty
+nonterminal (e.g. there's nothing at *.example.com but something like
+subdomain.*.example.org, do exist: so *.example.org exists in the
+namespace but has no RRs assopciated with it). This will produce NXRRSET.
+
+% DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %5 with RRset %6
+The database doesn't contain directly matching name. When searching
+for a wildcard match, a wildcard record matching the name and type of
+the query was found. The data at this point is returned.
+
+% DATASRC_DATABASE_WILDCARD_NS search in datasource %1 for %2/%3/%4 found wildcard delegation at %5, resulting in %6
+The database doesn't contain directly matching name. When searching
+for a wildcard match, an NS RR was found at a wildcard record matching
+the name. This is returned as the result of the search.
+
+% DATASRC_DATABASE_WILDCARD_NXRRSET search in datasource %1 for %2/%3/%4 resulted in wildcard NXRRSET at %5
+The database doesn't contain directly matching name. When searching
+for a wildcard match, a matching wildcard entry was found but it did
+not contain RRs the requested type. AN NXRRSET indication is returned.
% DATASRC_DO_QUERY handling query for '%1/%2'
A debug message indicating that a query for the given name and RR type is being
@@ -259,7 +326,7 @@ Debug information. The requested record was found.
% DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty
Debug information. The search stopped at a superdomain of the requested
-domain. The domain is a empty nonterminal, therefore it is treated as NXRRSET
+domain. The domain is an empty nonterminal, therefore it is treated as NXRRSET
case (eg. the domain exists, but it doesn't have the requested record type).
% DATASRC_MEM_SWAP swapping contents of two zone representations ('%1' and '%2')
@@ -487,12 +554,12 @@ enough information for it. The code is 1 for error, 2 for not implemented.
% DATASRC_SQLITE_CLOSE closing SQLite database
Debug information. The SQLite data source is closing the database file.
-% DATASRC_SQLITE_CONNOPEN Opening sqlite database file '%1'
-The database file is being opened so it can start providing data.
-
% DATASRC_SQLITE_CONNCLOSE Closing sqlite database
The database file is no longer needed and is being closed.
+% DATASRC_SQLITE_CONNOPEN Opening sqlite database file '%1'
+The database file is being opened so it can start providing data.
+
% DATASRC_SQLITE_CREATE SQLite data source created
Debug information. An instance of SQLite data source is being created.
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index 9fcd289..e028bea 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -264,12 +264,15 @@ public:
/// proof of the non existence of any matching wildcard or non existence
/// of an exact match when a wildcard match is found.
///
- /// A derived version of this method may involve internal resource
- /// allocation, especially for constructing the resulting RRset, and may
- /// throw an exception if it fails.
- /// It throws DuplicateRRset exception if there are duplicate rrsets under
- /// the same domain.
- /// It should not throw other types of exceptions.
+ /// \exception std::bad_alloc Memory allocation such as for constructing
+ /// the resulting RRset fails
+ /// \exception DataSourceError Derived class specific exception, e.g.
+ /// when encountering a bad zone configuration or database connection
+ /// failure. Although these are considered rare, exceptional events,
+ /// it can happen under relatively usual conditions (unlike memory
+ /// allocation failure). So, in general, the application is expected
+ /// to catch this exception, either specifically or as a result of
+ /// catching a base exception class, and handle it gracefully.
///
/// \param name The domain name to be searched for.
/// \param type The RR type to be searched for.
diff --git a/tools/reorder_message_file.py b/tools/reorder_message_file.py
new file mode 100644
index 0000000..31f4941
--- /dev/null
+++ b/tools/reorder_message_file.py
@@ -0,0 +1,196 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# Reorder Message File
+#
+# Reads a message file into memory, then outputs it with the messages and
+# associated descriptions in alphabetical order.
+#
+# Invocation:
+# The code is invoked using the command line:
+#
+# python reorder.py message_file
+#
+# Output is written to stdout.
+
+import sys
+
+def remove_empty_leading_trailing(lines):
+ """
+ Removes leading and trailing empty lines.
+
+ A list of strings is passed as argument, some of which may be empty.
+ This function removes from the start and end of the list a contiguous
+ sequence of empty lines and returns the result. Embedded sequences of
+ empty lines are not touched.
+
+ Parameters:
+ lines List of strings to be modified.
+
+ Return:
+ Input list of strings with leading/trailing blank line sequences
+ removed.
+ """
+
+ retlines = []
+
+ # Dispose of degenerate case of empty array
+ if len(lines) == 0:
+ return retlines
+
+ # Search for first non-blank line
+ start = 0
+ while start < len(lines):
+ if len(lines[start]) > 0:
+ break
+ start = start + 1
+
+ # Handle case when entire list is empty
+ if start >= len(lines):
+ return retlines
+
+ # Search for last non-blank line
+ finish = len(lines) - 1
+ while finish >= 0:
+ if len(lines[finish]) > 0:
+ break
+ finish = finish - 1
+
+ retlines = lines[start:finish + 1]
+ return retlines
+
+
+def canonicalise_message_line(line):
+ """
+ Given a line known to start with the '%' character (i.e. a line
+ introducing a message), canonicalise it by ensuring that the result
+ is of the form '%<single-space>MESSAGE_IDENTIFIER<single-space>text'.
+
+ Parameters:
+ line - input line. Known to start with a '%' and to have leading
+ and trailing spaces removed.
+
+ Return:
+ Canonicalised line.
+ """
+ # Cope with degenerate case of a single "%"
+ if len(line) == 1:
+ return line
+
+ # Get the rest of the line
+ line = line[1:].lstrip()
+
+ # Extract the first word (the message ID)
+ words = line.split()
+ message_line = "% " + words[0]
+
+ # ... and now the rest of the line
+ if len(line) > len(words[0]):
+ message_line = message_line + " " + line[len(words[0]):].lstrip()
+
+ return message_line
+
+
+def make_dict(lines):
+ """
+ Split the lines into segments starting with the message definition and
+ place into a dictionary.
+
+ Parameters:
+ lines - list of lines containing the text of the message file (less the
+ header).
+
+ Returns:
+ dictionary - map of the messages, keyed by the line that holds the message
+ ID.
+ """
+
+ dictionary = {}
+
+ message_key = canonicalise_message_line(lines[0])
+ message_lines = [message_key]
+ index = 1;
+ while index < len(lines):
+ if lines[index].startswith("%"):
+ # Start of new message
+ dictionary[message_key] = remove_empty_leading_trailing(message_lines)
+ message_key = canonicalise_message_line(lines[index])
+ message_lines = [message_key]
+ else:
+ message_lines.append(lines[index])
+
+ index = index + 1
+
+ dictionary[message_key] = remove_empty_leading_trailing(message_lines)
+
+ return dictionary
+
+
+def print_dict(dictionary):
+ """
+ Prints the dictionary with a blank line between entries.
+
+ Parameters:
+ dicitionary - Map holding the message dictionary
+ """
+ count = 0
+ for msgid in sorted(dictionary):
+
+ # Blank line before all entries but the first
+ if count > 0:
+ print("")
+ count = count + 1
+
+ # ... and the entry itself.
+ for l in dictionary[msgid]:
+ print(l.strip())
+
+
+def process_file(filename):
+ """
+ Processes a file by reading it and searching for the first line starting
+ with the '%' sign. Everything before that line is treated as the file
+ header and is copied to the output with leading and trailing spaces removed.
+ After that, each message block is read and stored for later sorting.
+
+ Parameters:
+ filename Name of the message file to process
+ """
+ lines = open(filename).read().splitlines()
+
+ # Search for the first line starting with the percent character. Everything
+ # before it is considered the file header and is copied to the output with
+ # leading and trailing spaces removed.
+ index = 0
+ while index < len(lines):
+ if lines[index].startswith("%"):
+ break
+ print(lines[index].strip())
+ index = index + 1
+
+ # Now put the remaining lines into the message dictionary
+ dictionary = make_dict(lines[index:])
+
+ # ...and print it
+ print_dict(dictionary)
+
+
+# Main program
+if __name__ == "__main__":
+
+ # Read the files and load the data
+ if len(sys.argv) != 2:
+ print "Usage: python reorder.py message_file"
+ else:
+ process_file(sys.argv[1])
More information about the bind10-changes
mailing list