Commit 0fd48fc7 authored by JINMEI Tatuya's avatar JINMEI Tatuya

[2433] added a zone check function, just checking the number of NS and SOA

parent df8823fd
......@@ -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
......
......@@ -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 <vector>
using isc::Unexpected;
using namespace isc::dns;
using namespace isc::dns::rdata;
namespace {
class ZoneCheckerTest : public ::testing::Test {
protected:
ZoneCheckerTest() :
zname_("example.com"), zclass_(RRClass::IN()),
callbacks_(boost::bind(&ZoneCheckerTest::callback, this, _1, true),
boost::bind(&ZoneCheckerTest::callback, this, _1, false))
{
soa_ = RRsetPtr(new RRset(zname_, zclass_, RRType::SOA(), RRTTL(60)));
soa_->addRdata(generic::SOA(
"ns.example.com. root.example.com. 0 0 0 0 0"));
ns_ = RRsetPtr(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60)));
ns_->addRdata(generic::NS("ns.example.com"));
ns_->addRdata(generic::NS("ns2.example.com"));
rrsets_.reset(new RRsetCollection);
}
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) {
EXPECT_EQ(expected_errors_[i], errors_[i]);
}
EXPECT_EQ(expected_warns_.size(), warns_.size());
for (int i = 0; i < std::min(expected_warns_.size(), warns_.size());
++i) {
EXPECT_EQ(expected_warns_[i], warns_[i]);
}
errors_.clear();
expected_errors_.clear();
warns_.clear();
expected_warns_.clear();
}
const Name zname_;
const RRClass zclass_;
RRsetPtr soa_;
RRsetPtr ns_;
boost::scoped_ptr<RRsetCollection> rrsets_;
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.
rrsets_->addRRset(soa_);
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_->addRRset(ns_);
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("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_->addRRset(soa_);
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
}
}
// 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");
}
}
void
checkNS(const Name& zone_name, const RRClass& zone_class,
const RRsetCollectionBase& zone_rrsets, CallbackWrapper& callback) {
const AbstractRRset* rrset =
zone_rrsets.find(zone_name, RRType::NS(), zone_class);
if (rrset == NULL) {
callback.error("zone " + zoneText(zone_name, zone_class) +
" has no NS records");
return;
}
if (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 = rrset->getRdataIterator();
!rit->isLast();
rit->next()) {
if (dynamic_cast<const rdata::generic::NS*>(&rit->getCurrent()) ==
NULL) {
isc_throw(Unexpected, "Zone checker found bad RDATA in NS");
}
}
}
}
bool
checkZone(const Name& zone_name, const RRClass& zone_class,
const RRsetCollectionBase& zone_rrsets,
const ZoneCheckerCallbacks& callbacks) {
CallbackWrapper my_callback(callbacks);
checkSOA(zone_name, zone_class, zone_rrsets, my_callback);
checkNS(zone_name, zone_class, zone_rrsets, my_callback);
return (!my_callback.hasError());
}
} // end namespace dns
} // end namespace isc
// 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 {
class ZoneCheckerCallbacks {
public:
typedef boost::function<void(const std::string& reason)> IssueCallback;
ZoneCheckerCallbacks() :
error_callback_(nullCallback), warn_callback_(nullCallback)
{}
ZoneCheckerCallbacks(const IssueCallback& error_callback,
const IssueCallback& warn_callback) :
error_callback_(error_callback), warn_callback_(warn_callback)
{}
void error(const std::string& reason) { error_callback_(reason); }
void warn(const std::string& reason) { warn_callback_(reason); }
private:
static void nullCallback(const std::string&) {}
IssueCallback error_callback_;
IssueCallback warn_callback_;
};
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:
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment