Commit 5eb2ff86 authored by JINMEI Tatuya's avatar JINMEI Tatuya

[master] Merge branch 'trac2433'

parents 0de32e7f 96f97334
......@@ -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 \
......@@ -175,7 +178,8 @@ libdns___include_HEADERS = \
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
......
// 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:
......@@ -393,7 +393,7 @@ private:
shared_ptr<Name> last_name_; // Last seen name (for INITAL_WS handling)
const RRClass zone_class_;
MasterLoaderCallbacks callbacks_;
AddRRCallback add_callback_;
const AddRRCallback add_callback_;
boost::scoped_ptr<RRTTL> default_ttl_; // Default TTL of RRs used when
// unspecified. If NULL no default
// is known.
......
......@@ -100,7 +100,7 @@ public:
/// If the caller of the loader wants to abort, it is possible to throw
/// from the callback, which aborts the load.
void error(const std::string& source_name, size_t source_line,
const std::string& reason)
const std::string& reason) const
{
error_(source_name, source_line, reason);
}
......@@ -117,7 +117,7 @@ public:
/// may be false positives), it is possible to throw from inside the
/// callback.
void warning(const std::string& source_name, size_t source_line,
const std::string& reason)
const std::string& reason) const
{
warning_(source_name, source_line, reason);
}
......@@ -133,7 +133,7 @@ public:
static MasterLoaderCallbacks getNullCallbacks();
private:
IssueCallback error_, warning_;
const IssueCallback error_, warning_;
};
}
......
......@@ -42,7 +42,7 @@ public:
const RdataPtr& rdata);
RRsetPtr current_rrset_;
AddRRsetCallback callback_;
const AddRRsetCallback callback_;
};
namespace {
......
......@@ -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
......
// 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_));
}
public:
// This one is passed to boost::bind. Some compilers seem to require
// it be public.
void callback(const std::string& reason, bool is_error) {
if (is_error) {
errors_.push_back(reason);
} else {
warns_.push_back(reason);
}
}
protected:
// 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();
// 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 null callback is specified, checkZone() only returns the final
// result.
ZoneCheckerCallbacks noerror_callbacks(
NULL, boost::bind(&ZoneCheckerTest::callback, this, _1, false));
EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, noerror_callbacks));
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();
// Same check, but disabling warning callback. Same result, but without
// the warning.
ZoneCheckerCallbacks nowarn_callbacks(
boost::bind(&ZoneCheckerTest::callback, this, _1, true), NULL);
EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, nowarn_callbacks));
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();
}
}
// 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/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
using boost::lexical_cast;
using std::string;
namespace isc {
namespace dns {
namespace {
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,
ZoneCheckerCallbacks& callback) {
ConstRRsetPtr rrset =
zone_rrsets.find(zone_name, zone_class, RRType::SOA());
size_t count = 0;
if (rrset) {
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 not on the name itself,
// while delegation works on the name itself (but the NS at the origin is not
// delegation).
ConstRRsetPtr
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);
ConstRRsetPtr found;
if (l != origin_count &&
(found = zone_rrsets.find(mid_name, zone_class, RRType::NS())) !=
NULL) {
return (found);
}
if (l != target_count &&
(found = zone_rrsets.find(mid_name, zone_class, RRType::DNAME()))
!= NULL) {
return (found);
}
}
return (ConstRRsetPtr());
}
// Check if each "in-zone" NS name has an address record, identifying some
// err