BIND 10 trac2433, updated. c466b2fb0a4888db16ff0888dc0f8c9875d6f43c [2433] add some more doc
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Jan 3 06:05:00 UTC 2013
The branch, trac2433 has been updated
via c466b2fb0a4888db16ff0888dc0f8c9875d6f43c (commit)
via e2f99db35b161d951e60aee4728118cf41c3b3d4 (commit)
via f47a693087d2583eee59f97e9b43c40535443688 (commit)
via 9faa2925c3934e3526b5b24694ec0986a6303d42 (commit)
via 469a7104b7231ef26e9f08e85e9f4acf035398c4 (commit)
via 4d7307519b21a0364350cbac4560f6693430cd6b (commit)
via 86f542bb1fecca6e8d4b93a3462649b9dc17805f (commit)
via a12ebb8fdcf246c1a096d62c624185c96f88b186 (commit)
via 0fd48fc7f5406e8cbd8b459a7fc5231053519d3b (commit)
via df8823fdb350670a1e5333b335b59a56e8b09b4c (commit)
via 3965fb5302e46eb4e7763a328add0a83687a8309 (commit)
via 98d515d5e653a75762b5da424555ac301e7f9af5 (commit)
via 4a15ebc6f38bf2b2fe66a069a58f22f89e8ec6fc (commit)
via 8f149bd04986ba2542e86c7ce77e5d0e92df16ea (commit)
via 201672389bfa78c8eb59e122f6f117d1eb0644ed (commit)
via fc882313210b117eeec371b33796a54a4094c1d3 (commit)
via 82d0b7f2ab2fcca6daafdcd5aae06cc0efcd2ffd (commit)
via 2205f81d523ba1175614c6a7ee54904b74ea1a43 (commit)
via a9c8b8f3cd7ae57265efd4e01c51cbb4526c038f (commit)
from 823f41b44713d161cbba7456914387e323595220 (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 c466b2fb0a4888db16ff0888dc0f8c9875d6f43c
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 22:04:53 2013 -0800
[2433] add some more doc
commit e2f99db35b161d951e60aee4728118cf41c3b3d4
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 21:24:35 2013 -0800
[2433] added new stuff to include_HEADERS
commit f47a693087d2583eee59f97e9b43c40535443688
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 21:23:24 2013 -0800
[2433] added a few more remaining test cases.
commit 9faa2925c3934e3526b5b24694ec0986a6303d42
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 21:17:58 2013 -0800
[2433] added documentation
commit 469a7104b7231ef26e9f08e85e9f4acf035398c4
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 20:19:45 2013 -0800
[2433] catch an invalid case where NS name is CNAME.
commit 4d7307519b21a0364350cbac4560f6693430cd6b
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 20:09:07 2013 -0800
[2433] handled cases DNAME exists above an NS name
commit 86f542bb1fecca6e8d4b93a3462649b9dc17805f
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 18:53:34 2013 -0800
[2433] check some cases where NS doesn't have address records.
commit a12ebb8fdcf246c1a096d62c624185c96f88b186
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 14:57:01 2013 -0800
[2433] refactored a test to simplify initialization.
commit 0fd48fc7f5406e8cbd8b459a7fc5231053519d3b
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 14:19:32 2013 -0800
[2433] added a zone check function, just checking the number of NS and SOA
commit df8823fdb350670a1e5333b335b59a56e8b09b4c
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 14:16:50 2013 -0800
[2433] introduced dns_fwd.h, a set of forward declarations for libdns++.
totally independent from the subject of the task, but we'll need this
type of thing sooner or later (and sooner is better).
commit 3965fb5302e46eb4e7763a328add0a83687a8309
Merge: 823f41b 98d515d
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Jan 2 08:20:21 2013 -0800
[2433] Merge remote-tracking branch 'origin/trac2432' into trac2433
-----------------------------------------------------------------------
Summary of changes:
src/lib/dns/Makefile.am | 8 +-
.../dns/{python/nsec3hash_python.h => dns_fwd.h} | 59 ++--
src/lib/dns/rrset_collection.cc | 13 +
src/lib/dns/rrset_collection.h | 30 +-
src/lib/dns/rrset_collection_base.h | 43 ++-
src/lib/dns/tests/Makefile.am | 1 +
src/lib/dns/tests/rrset_collection_unittest.cc | 16 +
src/lib/dns/tests/zone_checker_unittest.cc | 338 ++++++++++++++++++++
src/lib/dns/zone_checker.cc | 205 ++++++++++++
src/lib/dns/zone_checker.h | 161 ++++++++++
10 files changed, 846 insertions(+), 28 deletions(-)
copy src/lib/dns/{python/nsec3hash_python.h => dns_fwd.h} (52%)
create mode 100644 src/lib/dns/tests/zone_checker_unittest.cc
create mode 100644 src/lib/dns/zone_checker.cc
create mode 100644 src/lib/dns/zone_checker.h
-----------------------------------------------------------------------
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 5a44edd..3f6ae63 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -95,6 +95,7 @@ lib_LTLIBRARIES = libb10-dns++.la
libb10_dns___la_LDFLAGS = -no-undefined -version-info 2:0:0
libb10_dns___la_SOURCES =
+libb10_dns___la_SOURCES += dns_fwd.h
libb10_dns___la_SOURCES += edns.h edns.cc
libb10_dns___la_SOURCES += exceptions.h exceptions.cc
libb10_dns___la_SOURCES += master_lexer_inputsource.h master_lexer_inputsource.cc
@@ -129,6 +130,7 @@ libb10_dns___la_SOURCES += master_loader_callbacks.h master_loader_callbacks.cc
libb10_dns___la_SOURCES += master_loader.h
libb10_dns___la_SOURCES += rrset_collection_base.h
libb10_dns___la_SOURCES += rrset_collection.h rrset_collection.cc
+libb10_dns___la_SOURCES += zone_checker.h zone_checker.cc
libb10_dns___la_SOURCES += rdata/generic/detail/char_string.h
libb10_dns___la_SOURCES += rdata/generic/detail/char_string.cc
libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
@@ -158,6 +160,7 @@ libdns___includedir = $(includedir)/$(PACKAGE_NAME)/dns
libdns___include_HEADERS = \
edns.h \
exceptions.h \
+ dns_fwd.h \
labelsequence.h \
message.h \
masterload.h \
@@ -172,8 +175,11 @@ libdns___include_HEADERS = \
rdata.h \
rrparamregistry.h \
rrset.h \
+ rrset_collection_base.h \
+ rrset_collection.h \
rrttl.h \
- tsigkey.h
+ tsigkey.h \
+ zone_checker.h
# Purposely not installing these headers:
# name_internal.h: used only internally, and not actually DNS specific
# rdata/*/detail/*.h: these are internal use only
diff --git a/src/lib/dns/dns_fwd.h b/src/lib/dns/dns_fwd.h
new file mode 100644
index 0000000..df71388
--- /dev/null
+++ b/src/lib/dns/dns_fwd.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DNS_FWD_H
+#define DNS_FWD_H 1
+
+/// \file dns_fwd.h
+/// \brief Forward declarations for definitions of libdns++
+///
+/// This file provides a set of forward declarations for definitions commonly
+/// used in libdns++ to help minimize dependency when actual the definition
+/// is not necessary.
+
+namespace isc {
+namespace dns {
+
+class EDNS;
+class Name;
+class MasterLoader;
+class MasterLoaderCallbacks;
+class Message;
+class AbstractMessageRenderer;
+class MessageRenderer;
+class NSEC3Hash;
+class NSEC3HashCreator;
+class Opcode;
+class Question;
+class Rcode;
+namespace rdata {
+class Rdata;
+}
+class RRCollator;
+class RRClass;
+class RRType;
+class RRTTL;
+class AbstractRRset;
+class RdataIterator;
+class RRsetCollectionBase;
+class RRsetCollection;
+class Serial;
+class TSIGContext;
+class TSIGError;
+class TSIGKey;
+class TSIGKeyRing;
+class TSIGRecord;
+
+} // namespace dns
+} // namespace isc
+#endif // DNS_FWD_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rrset_collection.cc b/src/lib/dns/rrset_collection.cc
index 69e1cf4..53f9702 100644
--- a/src/lib/dns/rrset_collection.cc
+++ b/src/lib/dns/rrset_collection.cc
@@ -68,6 +68,19 @@ RRsetCollection::RRsetCollection(const char* filename, const Name& origin,
loader.load();
}
+RRsetCollection::RRsetCollection(std::istream& input_stream, const Name& origin,
+ const RRClass& rrclass)
+{
+ MasterLoaderCallbacks callbacks
+ (boost::bind(&RRsetCollection::loaderCallback, this, _1, _2, _3),
+ boost::bind(&RRsetCollection::loaderCallback, this, _1, _2, _3));
+ MasterLoader loader(input_stream, origin, rrclass, callbacks,
+ boost::bind(&RRsetCollection::addRRset,
+ this, _1, _2, _3, _4, _5),
+ MasterLoader::DEFAULT);
+ loader.load();
+}
+
const AbstractRRset*
RRsetCollection::find(const Name& name, const RRType& rrtype,
const RRClass& rrclass) const {
diff --git a/src/lib/dns/rrset_collection.h b/src/lib/dns/rrset_collection.h
index 9cbf98c..dfa4dd7 100644
--- a/src/lib/dns/rrset_collection.h
+++ b/src/lib/dns/rrset_collection.h
@@ -31,8 +31,7 @@ namespace dns {
class RRsetCollection : public RRsetCollectionBase {
public:
/// \brief Constructor.
- RRsetCollection()
- {}
+ RRsetCollection() {}
/// \brief Constructor.
///
@@ -44,15 +43,36 @@ public:
///
/// \param filename Name of a file containing a collection of RRs in
/// the master file format (which may or may not form a valid zone).
+ /// \param origin The zone origin.
+ /// \param rrclass The zone class.
RRsetCollection(const char* filename, const isc::dns::Name& origin,
const isc::dns::RRClass& rrclass);
+ /// \brief Constructor.
+ ///
+ /// This constructor is similar to the previous one, but instead of
+ /// taking a filename to load a zone from, it takes an input
+ /// stream. The constructor throws MasterLoaderError if there is an
+ /// error during loading.
+ ///
+ /// \param input_stream The input stream to load from.
+ /// \param origin The zone origin.
+ /// \param rrclass The zone class.
+ RRsetCollection(std::istream& input_stream, const isc::dns::Name& origin,
+ const isc::dns::RRClass& rrclass);
+
+ /// \brief Destructor
+ virtual ~RRsetCollection() {}
+
/// \brief Add an RRset to the collection.
///
/// Does not do any validation whether \c rrset belongs to a
- /// particular zone or not. It throws an \c isc::InvalidParameter
- /// exception if an rrset with the same class, type and name already
- /// exists.
+ /// particular zone or not. A reference to \c rrset is taken in an
+ /// internally managed \c shared_ptr, so even if the caller's
+ /// \c RRsetPtr is destroyed, the RRset it wrapped is still alive
+ /// and managed by the \c RRsetCollection. It throws an
+ /// \c isc::InvalidParameter exception if an rrset with the same
+ /// class, type and name already exists.
void addRRset(isc::dns::RRsetPtr rrset);
/// \brief Remove an RRset from the collection.
diff --git a/src/lib/dns/rrset_collection_base.h b/src/lib/dns/rrset_collection_base.h
index 5c4314c..eae7f6d 100644
--- a/src/lib/dns/rrset_collection_base.h
+++ b/src/lib/dns/rrset_collection_base.h
@@ -50,24 +50,61 @@ public:
/// \returns A pointer to the RRset if found, \c NULL otherwise.
virtual const isc::dns::AbstractRRset* find
(const isc::dns::Name& name, const isc::dns::RRType& rrtype,
- const isc::dns::RRClass& rrclass)
+ const isc::dns::RRClass& rrclass)
const = 0;
+ /// \brief Destructor
+ virtual ~RRsetCollectionBase() {}
+
protected:
class Iter; // forward declaration
+
+ /// \brief Wraps Iter with a reference count.
typedef boost::shared_ptr<Iter> IterPtr;
+ /// \brief A helper iterator interface for \c RRsetCollectionBase.
+ ///
+ /// This is a protected iterator class that is a helper interface
+ /// used by the public iterator. Derived classes of
+ /// \c RRsetCollectionBase are supposed to implement this class and
+ /// the \c getBeginning() and \c getEnd() methods, so that the
+ /// public interator interface can be provided. This is a forward
+ /// iterator only.
class Iter {
public:
+ /// \brief Returns the \c AbstractRRset currently pointed to by
+ /// the iterator.
virtual const isc::dns::AbstractRRset& getValue() = 0;
+
+ /// \brief Returns an \c IterPtr wrapping an Iter pointing to
+ /// the next \c AbstractRRset in sequence in the collection.
virtual IterPtr getNext() = 0;
+
+ /// \brief Check if another iterator is equal to this one.
+ ///
+ /// Returns \c true if this iterator is equal to \c other,
+ /// \c false otherwise. Note that if \c other is not the same
+ /// type as \c this, or cannot be compared meaningfully, the
+ /// method must return \c false.
+ ///
+ /// \param other The other iterator to compare against.
+ /// \returns \c true if equal, \c false otherwise.
virtual bool equals(Iter& other) = 0;
};
+ /// \brief Returns an \c IterPtr wrapping an Iter pointing to the
+ /// beginning of the collection.
virtual IterPtr getBeginning() = 0;
+
+ /// \brief Returns an \c IterPtr wrapping an Iter pointing past the
+ /// end of the collection.
virtual IterPtr getEnd() = 0;
public:
+ /// \brief A forward \c std::iterator for \c RRsetCollectionBase.
+ ///
+ /// It behaves like a \c std::iterator forward iterator, so please
+ /// see its documentation for usage.
class iterator : std::iterator<std::forward_iterator_tag,
const isc::dns::AbstractRRset>
{
@@ -103,10 +140,14 @@ public:
IterPtr iter_;
};
+ /// \brief Returns an iterator pointing to the beginning of the
+ /// collection.
iterator begin() {
return iterator(getBeginning());
}
+ /// \brief Returns an iterator pointing past the end of the
+ /// collection.
iterator end() {
return iterator(getEnd());
}
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index d71ec13..8d32b42 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -76,6 +76,7 @@ run_unittests_SOURCES += tsigrecord_unittest.cc
run_unittests_SOURCES += character_string_unittest.cc
run_unittests_SOURCES += master_loader_callbacks_test.cc
run_unittests_SOURCES += rrset_collection_unittest.cc
+run_unittests_SOURCES += zone_checker_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
# We shouldn't need to include BOTAN_LIBS here, but there
diff --git a/src/lib/dns/tests/rrset_collection_unittest.cc b/src/lib/dns/tests/rrset_collection_unittest.cc
index 5452189..7d1b6f6 100644
--- a/src/lib/dns/tests/rrset_collection_unittest.cc
+++ b/src/lib/dns/tests/rrset_collection_unittest.cc
@@ -19,6 +19,7 @@
#include <gtest/gtest.h>
#include <list>
+#include <fstream>
using namespace isc::dns;
using namespace isc::dns::rdata;
@@ -39,6 +40,21 @@ public:
RRsetCollection collection;
};
+TEST_F(RRsetCollectionTest, istreamConstructor) {
+ std::ifstream fs(TEST_DATA_SRCDIR "/example.org");
+ RRsetCollection collection2(fs, origin, rrclass);
+
+ RRsetCollectionBase::iterator iter = collection.begin();
+ RRsetCollectionBase::iterator iter2 = collection2.begin();
+ while (iter != collection.end()) {
+ EXPECT_TRUE(iter2 != collection2.end());
+ EXPECT_EQ((*iter).toText(), (*iter2).toText());
+ ++iter;
+ ++iter2;
+ }
+ EXPECT_TRUE(iter2 == collection2.end());
+}
+
TEST_F(RRsetCollectionTest, findBase) {
// Test the find() that returns isc::dns::AbstractRRset*
const AbstractRRset* rrset = collection.find(Name("www.example.org"),
diff --git a/src/lib/dns/tests/zone_checker_unittest.cc b/src/lib/dns/tests/zone_checker_unittest.cc
new file mode 100644
index 0000000..9ded0e5
--- /dev/null
+++ b/src/lib/dns/tests/zone_checker_unittest.cc
@@ -0,0 +1,338 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/zone_checker.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
+#include <dns/rrset_collection.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <algorithm>
+#include <string>
+#include <sstream>
+#include <vector>
+
+using isc::Unexpected;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+
+const char* const soa_txt = "ns.example.com. root.example.com. 0 0 0 0 0";
+const char* const ns_txt1 = "ns.example.com.";
+const char* const ns_a_txt1 = "192.0.2.1";
+const char* const ns_txt2 = "ns2.example.com.";
+const char* const ns_a_txt2 = "192.0.2.2";
+
+class ZoneCheckerTest : public ::testing::Test {
+protected:
+ ZoneCheckerTest() :
+ zname_("example.com"), zclass_(RRClass::IN()),
+ soa_(new RRset(zname_, zclass_, RRType::SOA(), RRTTL(60))),
+ ns_(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60))),
+ callbacks_(boost::bind(&ZoneCheckerTest::callback, this, _1, true),
+ boost::bind(&ZoneCheckerTest::callback, this, _1, false))
+ {
+ std::stringstream ss;
+ ss << "example.com. 60 IN SOA " << soa_txt << "\n";
+ ss << "example.com. 60 IN NS " << ns_txt1 << "\n";
+ ss << "ns.example.com. 60 IN A " << ns_a_txt1 << "\n";
+ ss << "ns2.example.com. 60 IN A " << ns_a_txt2 << "\n";
+ rrsets_.reset(new RRsetCollection(ss, zname_, zclass_));
+ }
+
+ void callback(const std::string& reason, bool is_error) {
+ if (is_error) {
+ errors_.push_back(reason);
+ } else {
+ warns_.push_back(reason);
+ }
+ }
+
+ // Check stored issue messages with expected ones. Clear vectors so
+ // the caller can check other cases.
+ void checkIssues() {
+ EXPECT_EQ(expected_errors_.size(), errors_.size());
+ for (int i = 0; i < std::min(expected_errors_.size(), errors_.size());
+ ++i) {
+ // The actual message should begin with the expected message.
+ EXPECT_EQ(0, errors_[0].find(expected_errors_[0]))
+ << "actual message: " << errors_[0] << " expected: " <<
+ expected_errors_[0];
+ }
+ EXPECT_EQ(expected_warns_.size(), warns_.size());
+ for (int i = 0; i < std::min(expected_warns_.size(), warns_.size());
+ ++i) {
+ EXPECT_EQ(0, warns_[0].find(expected_warns_[0]))
+ << "actual message: " << warns_[0] << " expected: " <<
+ expected_warns_[0];
+ }
+
+ errors_.clear();
+ expected_errors_.clear();
+ warns_.clear();
+ expected_warns_.clear();
+ }
+
+ const Name zname_;
+ const RRClass zclass_;
+ boost::scoped_ptr<RRsetCollection> rrsets_;
+ RRsetPtr soa_;
+ RRsetPtr ns_;
+ std::vector<std::string> errors_;
+ std::vector<std::string> warns_;
+ std::vector<std::string> expected_errors_;
+ std::vector<std::string> expected_warns_;
+ ZoneCheckerCallbacks callbacks_;
+};
+
+TEST_F(ZoneCheckerTest, checkGood) {
+ // Checking a valid case. No errors or warnings should be reported.
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ checkIssues();
+
+ // We can omit callbacks, in which case the default constructor for
+ // the callbacks is used, meaning callbacks are no-op.
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_));
+
+ // Multiple NS RRs are okay.
+ rrsets_->removeRRset(zname_, zclass_, RRType::NS());
+ ns_->addRdata(generic::NS(ns_txt1));
+ ns_->addRdata(generic::NS(ns_txt2));
+ rrsets_->addRRset(ns_);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ checkIssues();
+}
+
+TEST_F(ZoneCheckerTest, checkSOA) {
+ // If the zone has no SOA it triggers an error.
+ rrsets_->removeRRset(zname_, zclass_, RRType::SOA());
+ EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_errors_.push_back("zone example.com/IN: has 0 SOA records");
+ checkIssues();
+
+ // If there are more than 1 SOA RR, it's also an error.
+ errors_.clear();
+ soa_->addRdata(generic::SOA(soa_txt));
+ soa_->addRdata(generic::SOA("ns2.example.com. . 0 0 0 0 0"));
+ rrsets_->addRRset(soa_);
+ EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_errors_.push_back("zone example.com/IN: has 2 SOA records");
+ checkIssues();
+
+ // If the SOA RRset is "empty", it's treated as an implementation
+ // (rather than operational) error and results in an exception.
+ rrsets_->removeRRset(zname_, zclass_, RRType::SOA());
+ soa_.reset(new RRset(zname_, zclass_, RRType::SOA(), RRTTL(60)));
+ rrsets_->addRRset(soa_);
+ EXPECT_THROW(checkZone(zname_, zclass_, *rrsets_, callbacks_), Unexpected);
+ checkIssues(); // no error/warning should be reported
+
+ // Likewise, if the SOA RRset contains non SOA Rdata, it should be a bug.
+ rrsets_->removeRRset(zname_, zclass_, RRType::SOA());
+ soa_.reset(new RRset(zname_, zclass_, RRType::SOA(), RRTTL(60)));
+ soa_->addRdata(createRdata(RRType::NS(), zclass_, "ns.example.com"));
+ rrsets_->addRRset(soa_);
+ EXPECT_THROW(checkZone(zname_, zclass_, *rrsets_, callbacks_), Unexpected);
+ checkIssues(); // no error/warning should be reported
+}
+
+TEST_F(ZoneCheckerTest, checkNS) {
+ // If the zone has no NS at origin it triggers an error.
+ rrsets_->removeRRset(zname_, zclass_, RRType::NS());
+ EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_errors_.push_back("zone example.com/IN: has no NS records");
+ checkIssues();
+
+ // Check two buggy cases like the SOA tests
+ ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60)));
+ rrsets_->addRRset(ns_);
+ EXPECT_THROW(checkZone(zname_, zclass_, *rrsets_, callbacks_), Unexpected);
+ checkIssues(); // no error/warning should be reported
+
+ rrsets_->removeRRset(zname_, zclass_, RRType::NS());
+ ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60)));
+ ns_->addRdata(createRdata(RRType::TXT(), zclass_, "ns.example.com"));
+ rrsets_->addRRset(ns_);
+ EXPECT_THROW(checkZone(zname_, zclass_, *rrsets_, callbacks_), Unexpected);
+ checkIssues(); // no error/warning should be reported
+}
+
+TEST_F(ZoneCheckerTest, checkNSData) {
+ const Name ns_name("ns.example.com");
+
+ // If a ("in-bailiwick") NS name doesn't have an address record, it's
+ // reported as a warning.
+ rrsets_->removeRRset(ns_name, zclass_, RRType::A());
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_warns_.push_back("zone example.com/IN: NS has no address");
+ checkIssues();
+
+ // A tricky case: if the name matches a wildcard, it should technically
+ // be considered valid, but this checker doesn't check that far and still
+ // warns.
+ RRsetPtr wild(new RRset(Name("*.example.com"), zclass_, RRType::A(),
+ RRTTL(0)));
+ wild->addRdata(in::A("192.0.2.255"));
+ rrsets_->addRRset(wild);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_warns_.push_back("zone example.com/IN: NS has no address");
+ checkIssues();
+
+ // If there's a CNAME at the name instead, it's an error.
+ rrsets_->removeRRset(Name("*.example.com"), zclass_, RRType::A());
+ RRsetPtr cname(new RRset(ns_name, zclass_, RRType::CNAME(), RRTTL(60)));
+ cname->addRdata(generic::CNAME("cname.example.com"));
+ rrsets_->addRRset(cname);
+ EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_errors_.push_back("zone example.com/IN: NS 'ns.example.com' is "
+ "a CNAME (illegal per RFC2181)");
+ checkIssues();
+
+ // It doesn't have to be A. An AAAA is enough.
+ rrsets_->removeRRset(ns_name, zclass_, RRType::CNAME());
+ RRsetPtr aaaa(new RRset(ns_name, zclass_, RRType::AAAA(), RRTTL(60)));
+ aaaa->addRdata(in::AAAA("2001:db8::1"));
+ rrsets_->addRRset(aaaa);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ checkIssues();
+
+ // Coexisting CNAME makes it error (CNAME with other record is itself
+ // invalid, but it's a different issue in this context)
+ rrsets_->addRRset(cname);
+ EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_errors_.push_back("zone example.com/IN: NS 'ns.example.com' is "
+ "a CNAME (illegal per RFC2181)");
+ checkIssues();
+
+ // It doesn't matter if the NS name is "out of bailiwick".
+ rrsets_->removeRRset(ns_name, zclass_, RRType::CNAME());
+ rrsets_->removeRRset(zname_, zclass_, RRType::NS());
+ ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60)));
+ ns_->addRdata(generic::NS("ns.example.org"));
+ rrsets_->addRRset(ns_);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ checkIssues();
+
+ // Note that if the NS name is the origin name, it should be checked
+ rrsets_->removeRRset(zname_, zclass_, RRType::NS());
+ ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60)));
+ ns_->addRdata(generic::NS(zname_));
+ rrsets_->addRRset(ns_);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_warns_.push_back("zone example.com/IN: NS has no address");
+ checkIssues();
+}
+
+TEST_F(ZoneCheckerTest, checkNSWithDelegation) {
+ // Tests various cases where there's a zone cut due to delegation between
+ // the zone origin and the NS name. In each case the NS name doesn't have
+ // an address record.
+ const Name ns_name("ns.child.example.com");
+
+ // Zone cut due to delegation in the middle; the check for the address
+ // record should be skipped.
+ rrsets_->removeRRset(zname_, zclass_, RRType::NS());
+ ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60)));
+ ns_->addRdata(generic::NS(ns_name));
+ rrsets_->addRRset(ns_);
+ RRsetPtr child_ns(new RRset(Name("child.example.com"), zclass_,
+ RRType::NS(), RRTTL(60)));
+ child_ns->addRdata(generic::NS("ns.example.org"));
+ rrsets_->addRRset(child_ns);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ checkIssues();
+
+ // Zone cut at the NS name. Same result.
+ rrsets_->removeRRset(child_ns->getName(), zclass_, RRType::NS());
+ child_ns.reset(new RRset(ns_name, zclass_, RRType::NS(), RRTTL(60)));
+ child_ns->addRdata(generic::NS("ns.example.org"));
+ rrsets_->addRRset(child_ns);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ checkIssues();
+
+ // Zone cut below the NS name. The check applies.
+ rrsets_->removeRRset(child_ns->getName(), zclass_, RRType::NS());
+ child_ns.reset(new RRset(Name("another.ns.child.example.com"), zclass_,
+ RRType::NS(), RRTTL(60)));
+ child_ns->addRdata(generic::NS("ns.example.org"));
+ rrsets_->addRRset(child_ns);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_warns_.push_back("zone example.com/IN: NS has no address");
+ checkIssues();
+}
+
+TEST_F(ZoneCheckerTest, checkNSWithDNAME) {
+ // Similar to the above case, but the zone cut is due to DNAME. This is
+ // an invalid configuration.
+ const Name ns_name("ns.child.example.com");
+
+ // Zone cut due to DNAME at the zone origin. This is an invalid case.
+ rrsets_->removeRRset(zname_, zclass_, RRType::NS());
+ ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60)));
+ ns_->addRdata(generic::NS(ns_name));
+ rrsets_->addRRset(ns_);
+ RRsetPtr dname(new RRset(zname_, zclass_, RRType::DNAME(), RRTTL(60)));
+ dname->addRdata(generic::DNAME("example.org"));
+ rrsets_->addRRset(dname);
+ EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_errors_.push_back("zone example.com/IN: NS 'ns.child.example.com'"
+ " is below a DNAME 'example.com'");
+ checkIssues();
+
+ // Zone cut due to DNAME in the middle. Same result.
+ rrsets_->removeRRset(zname_, zclass_, RRType::DNAME());
+ dname.reset(new RRset(Name("child.example.com"), zclass_, RRType::DNAME(),
+ RRTTL(60)));
+ dname->addRdata(generic::DNAME("example.org"));
+ rrsets_->addRRset(dname);
+ EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_errors_.push_back("zone example.com/IN: NS 'ns.child.example.com'"
+ " is below a DNAME 'child.example.com'");
+ checkIssues();
+
+ // A tricky case: there's also an NS at the name that has DNAME. It's
+ // prohibited per RFC6672 so we could say it's "undefined". Nevertheless,
+ // this implementation prefers the NS and skips further checks.
+ ns_.reset(new RRset(Name("child.example.com"), zclass_, RRType::NS(),
+ RRTTL(60)));
+ ns_->addRdata(generic::NS("ns.example.org"));
+ rrsets_->addRRset(ns_);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ checkIssues();
+
+ // Zone cut due to DNAME at the NS name. In this case DNAME doesn't
+ // affect the NS name, so it should result in "no address record" warning.
+ rrsets_->removeRRset(dname->getName(), zclass_, RRType::DNAME());
+ rrsets_->removeRRset(ns_->getName(), zclass_, RRType::NS());
+ dname.reset(new RRset(ns_name, zclass_, RRType::DNAME(), RRTTL(60)));
+ dname->addRdata(generic::DNAME("example.org"));
+ rrsets_->addRRset(dname);
+ EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_));
+ expected_warns_.push_back("zone example.com/IN: NS has no address");
+ checkIssues();
+}
+
+}
diff --git a/src/lib/dns/zone_checker.cc b/src/lib/dns/zone_checker.cc
new file mode 100644
index 0000000..3e1c47d
--- /dev/null
+++ b/src/lib/dns/zone_checker.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/zone_checker.h>
+
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+#include <dns/rrset_collection_base.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+
+using boost::lexical_cast;
+using std::string;
+
+namespace isc {
+namespace dns {
+
+namespace {
+// This helper class is a trivial wrapper of ZoneCheckerCallbacks, and
+// remembers it if an error happens at least once.
+class CallbackWrapper {
+public:
+ CallbackWrapper(const ZoneCheckerCallbacks& callbacks) :
+ callbacks_(callbacks), has_error_(false)
+ {}
+
+ void error(const string& reason) {
+ has_error_ = true;
+ callbacks_.error(reason);
+ }
+
+ void warn(const string& reason) {
+ callbacks_.warn(reason);
+ }
+
+ bool hasError() const { return (has_error_); }
+
+private:
+ ZoneCheckerCallbacks callbacks_;
+ bool has_error_;
+};
+
+std::string
+zoneText(const Name& zone_name, const RRClass& zone_class) {
+ return (zone_name.toText(true) + "/" + zone_class.toText());
+}
+
+void
+checkSOA(const Name& zone_name, const RRClass& zone_class,
+ const RRsetCollectionBase& zone_rrsets, CallbackWrapper& callback) {
+ const AbstractRRset* rrset =
+ zone_rrsets.find(zone_name, RRType::SOA(), zone_class);
+ size_t count = 0;
+ if (rrset != NULL) {
+ for (RdataIteratorPtr rit = rrset->getRdataIterator();
+ !rit->isLast();
+ rit->next(), ++count) {
+ if (dynamic_cast<const rdata::generic::SOA*>(&rit->getCurrent()) ==
+ NULL) {
+ isc_throw(Unexpected, "Zone checker found bad RDATA in SOA");
+ }
+ }
+ if (count == 0) {
+ // this should be an implementation bug, not an operational error.
+ isc_throw(Unexpected, "Zone checker found an empty SOA RRset");
+ }
+ }
+ if (count != 1) {
+ callback.error("zone " + zoneText(zone_name, zone_class) + ": has " +
+ lexical_cast<string>(count) + " SOA records");
+ }
+}
+
+// Check if a target name is beyond zone cut, either due to delegation or
+// DNAME. Note that DNAME works on the origin but on the name itself, while
+// delegation works on the name itself (but the NS at the origin is not
+// delegation).
+const AbstractRRset*
+findZoneCut(const Name& zone_name, const RRClass& zone_class,
+ const RRsetCollectionBase& zone_rrsets, const Name& target_name) {
+ const unsigned int origin_count = zone_name.getLabelCount();
+ const unsigned int target_count = target_name.getLabelCount();
+ assert(origin_count <= target_count);
+
+ for (unsigned int l = origin_count; l <= target_count; ++l) {
+ const Name& mid_name = (l == target_count) ? target_name :
+ target_name.split(target_count - l);
+
+ const AbstractRRset* found = NULL;
+ if (l != origin_count &&
+ (found = zone_rrsets.find(mid_name, RRType::NS(), zone_class)) !=
+ NULL) {
+ return (found);
+ }
+ if (l != target_count &&
+ (found = zone_rrsets.find(mid_name, RRType::DNAME(), zone_class))
+ != NULL) {
+ return (found);
+ }
+ }
+ return (NULL);
+}
+
+// Check if each "in-zone" NS name has an address record, identifying some
+// error cases.
+void
+checkNSNames(const Name& zone_name, const RRClass& zone_class,
+ const RRsetCollectionBase& zone_rrsets,
+ const AbstractRRset& ns_rrset, CallbackWrapper& callbacks) {
+ if (ns_rrset.getRdataCount() == 0) {
+ // this should be an implementation bug, not an operational error.
+ isc_throw(Unexpected, "Zone checker found an empty NS RRset");
+ }
+
+ for (RdataIteratorPtr rit = ns_rrset.getRdataIterator();
+ !rit->isLast();
+ rit->next()) {
+ const rdata::generic::NS* ns_data =
+ dynamic_cast<const rdata::generic::NS*>(&rit->getCurrent());
+ if (ns_data == NULL) {
+ isc_throw(Unexpected, "Zone checker found bad RDATA in NS");
+ }
+ const Name& ns_name = ns_data->getNSName();
+ const NameComparisonResult::NameRelation reln =
+ ns_name.compare(zone_name).getRelation();
+ if (reln != NameComparisonResult::EQUAL &&
+ reln != NameComparisonResult::SUBDOMAIN) {
+ continue; // not in the zone. we can ignore it.
+ }
+
+ // Check if there's a zone cut between the origin and the NS name.
+ const AbstractRRset* cut_rrset = findZoneCut(zone_name, zone_class,
+ zone_rrsets, ns_name);
+ if (cut_rrset != NULL) {
+ if (cut_rrset->getType() == RRType::NS()) {
+ continue; // delegation; making the NS name "out of zone".
+ } else if (cut_rrset->getType() == RRType::DNAME()) {
+ callbacks.error("zone " + zoneText(zone_name, zone_class) +
+ ": NS '" + ns_name.toText(true) + "' is " +
+ "below a DNAME '" +
+ cut_rrset->getName().toText(true) +
+ "' (illegal per RFC6672)");
+ continue;
+ } else {
+ assert(false);
+ }
+ }
+ if (zone_rrsets.find(ns_name, RRType::CNAME(), zone_class) != NULL) {
+ callbacks.error("zone " + zoneText(zone_name, zone_class) +
+ ": NS '" + ns_name.toText(true) + "' is a CNAME " +
+ "(illegal per RFC2181)");
+ continue;
+ }
+ if (zone_rrsets.find(ns_name, RRType::A(), zone_class) == NULL &&
+ zone_rrsets.find(ns_name, RRType::AAAA(), zone_class) == NULL) {
+ callbacks.warn("zone " + zoneText(zone_name, zone_class) +
+ ": NS has no address records (A or AAAA)");
+ }
+ }
+}
+
+void
+checkNS(const Name& zone_name, const RRClass& zone_class,
+ const RRsetCollectionBase& zone_rrsets, CallbackWrapper& callbacks) {
+ const AbstractRRset* rrset =
+ zone_rrsets.find(zone_name, RRType::NS(), zone_class);
+ if (rrset == NULL) {
+ callbacks.error("zone " + zoneText(zone_name, zone_class) +
+ ": has no NS records");
+ return;
+ }
+ checkNSNames(zone_name, zone_class, zone_rrsets, *rrset, callbacks);
+}
+}
+
+bool
+checkZone(const Name& zone_name, const RRClass& zone_class,
+ const RRsetCollectionBase& zone_rrsets,
+ const ZoneCheckerCallbacks& callbacks) {
+ CallbackWrapper my_callbacks(callbacks);
+
+ checkSOA(zone_name, zone_class, zone_rrsets, my_callbacks);
+ checkNS(zone_name, zone_class, zone_rrsets, my_callbacks);
+
+ return (!my_callbacks.hasError());
+}
+
+} // end namespace dns
+} // end namespace isc
diff --git a/src/lib/dns/zone_checker.h b/src/lib/dns/zone_checker.h
new file mode 100644
index 0000000..5b166a8
--- /dev/null
+++ b/src/lib/dns/zone_checker.h
@@ -0,0 +1,161 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ZONE_CHECKER_H
+#define ZONE_CHECKER_H 1
+
+#include <dns/dns_fwd.h>
+
+#include <boost/function.hpp>
+
+#include <string>
+
+namespace isc {
+namespace dns {
+
+/// \brief Set of callbacks used in zone checks.
+///
+/// Objects of this class are expected to be passed to \c checkZone().
+class ZoneCheckerCallbacks {
+public:
+ /// \brief Functor type of the callback on some issue (error or warning).
+ ///
+ /// Its parameter indicates the reason for the corresponding issue.
+ typedef boost::function<void(const std::string& reason)> IssueCallback;
+
+ /// \brief Default constructor.
+ ///
+ /// This is a convenient shortcut to specify callbacks that do nothing.
+ /// If, for example, the caller of \c checkZone() is only interested in
+ /// the final result, it can use this constructor.
+ ///
+ /// \throw none
+ ZoneCheckerCallbacks() :
+ error_callback_(nullCallback), warn_callback_(nullCallback)
+ {}
+
+ /// \brief Constructor with callbacks.
+ ///
+ /// \throw none
+ ///
+ /// \param error_callback Callback functor to be called on critical errors.
+ /// \param warn_callback Callback functor to be called on non critical
+ /// issues.
+ ZoneCheckerCallbacks(const IssueCallback& error_callback,
+ const IssueCallback& warn_callback) :
+ error_callback_(error_callback), warn_callback_(warn_callback)
+ {}
+
+ /// \brief Call the callback for a critical error.
+ ///
+ /// This method itself is exception free, but propagates any exception
+ /// thrown from the callback.
+ ///
+ /// \param reason Textual representation of the reason for the error.
+ void error(const std::string& reason) { error_callback_(reason); }
+
+ /// \brief Call the callback for a non critical issue.
+ ///
+ /// This method itself is exception free, but propagates any exception
+ /// thrown from the callback.
+ ///
+ /// \param reason Textual representation of the reason for the issue.
+ void warn(const std::string& reason) { warn_callback_(reason); }
+
+private:
+ static void nullCallback(const std::string&) {}
+
+ IssueCallback error_callback_;
+ IssueCallback warn_callback_;
+};
+
+/// \brief Perform basic integrity checks on zone RRsets.
+///
+/// This function performs some lightweight checks on zone's SOA and (apex)
+/// NS records. Here, lightweight means it doesn't require traversing
+/// the entire zone, and should be expected to complete reasonably quickly
+/// regardless of the size of the zone.
+///
+/// It distinguishes "critical" errors and other undesirable issues:
+/// the former should be interpreted as the resulting zone shouldn't be used
+/// further, e.g, by an authoritative server implementation; the latter means
+/// the issues are better to be addressed but are not necessarily considered
+/// to make the zone invalid. Critical errors are reported via the
+/// \c error() method of \c callbacks, and non critical issues are reported
+/// via its \c warn() method.
+///
+/// Specific checks performed by this function is as follows. Failure of
+/// a check is considered a critical error unless noted otherwise:
+/// - There is exactly one SOA RR at the zone apex.
+/// - There is at least one NS RR at the zone apex.
+/// - For each apex NS record, if the NS name (the RDATA of the record) is
+/// in the zone (i.e., it's a subdomain of the zone origin and above any
+/// zone cut due to delegation), check the following:
+/// - the NS name should have an address record (AAAA or A). Failure of
+/// this check is considered a non critical issue.
+/// - the NS name does not have a CNAME. This is prohibited by Section
+/// 10.3 of RFC 2181.
+/// - the NS name is not subject to DNAME substitution. This is prohibited
+/// by Section 4 of RFC 6672.
+/// .
+///
+/// In addition, when the check is completed without any critical error, this
+/// function guarantees that RRsets for the SOA and (apex) NS stored in the
+/// passed RRset collection have the expected type of Rdata objects,
+/// i.e., generic::SOA and generic::NS, respectively. (This is normally
+/// expected to be the case, but not guaranteed by the API).
+///
+/// As for the check on the existence of AAAA or A records for NS names,
+/// it should be noted that BIND 9 treats this as a critical error.
+/// It's not clear whether it's an implementation dependent behavior or
+/// based on the protocol standard (it looks like the former), but to make
+/// it sure we need to confirm there is even no wildcard match for the names.
+/// This should be a very rare configuration, and more expensive to detect,
+/// so we do not check this condition, and treat this case as a non critical
+/// issue.
+///
+/// This function indicates the result of the checks (whether there is a
+/// critical error) via the return value: It returns \c true if there is no
+/// critical error and returns \c false otherwise. It doesn't throw an
+/// exception on encountering an error so that it can report as many errors
+/// as possible in a single call. If an exception is a better way to signal
+/// the error, the caller can pass a callback object that throws from its
+/// \c error() method.
+///
+/// This function can still throw an exception if it finds a really bogus
+/// condition that is most likely to be an implementation bug of the caller.
+/// Such cases include when an RRset contained in the RRset collection is
+/// empty.
+///
+/// \throw Unexpected Conditions that suggest a caller's bug (see the
+/// description)
+///
+/// \param zone_name The name of the zone to be checked
+/// \param zone_class The RR class of the zone to be checked
+/// \param zone_rrsets The collection of RRsets of the zone
+/// \param callbacks Callback object used to report errors and issues
+///
+/// \return \c true if no critical errors are found; \c false otherwise.
+bool
+checkZone(const Name& zone_name, const RRClass& zone_class,
+ const RRsetCollectionBase& zone_rrsets,
+ const ZoneCheckerCallbacks& callbacks = ZoneCheckerCallbacks());
+
+} // namespace dns
+} // namespace isc
+#endif // ZONE_CHECKER_H
+
+// Local Variables:
+// mode: c++
+// End:
More information about the bind10-changes
mailing list