Commit e98da500 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[master] Merge branch 'trac1613'

Conflicts:
	src/bin/auth/auth.spec.pre.in
(due to removal of spaces in earlier spec descriptions)
parents ea1476c0 5b6f56a3
......@@ -273,6 +273,142 @@
"item_default": 0,
"item_title": "Received requests opcode 15",
"item_description": "The number of total request counts whose opcode is 15 (reserved)"
},
{
"item_name": "rcode.noerror",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent success response",
"item_description": "The number of total responses with rcode 0 (NOERROR)"
},
{
"item_name": "rcode.formerr",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'format error' response",
"item_description": "The number of total responses with rcode 1 (FORMERR)"
},
{
"item_name": "rcode.servfail",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'server failure' response",
"item_description": "The number of total responses with rcode 2 (SERVFAIL)"
},
{
"item_name": "rcode.nxdomain",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'name error' response",
"item_description": "The number of total responses with rcode 3 (NXDOMAIN)"
},
{
"item_name": "rcode.notimp",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'not implemented' response",
"item_description": "The number of total responses with rcode 4 (NOTIMP)"
},
{
"item_name": "rcode.refused",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'refused' response",
"item_description": "The number of total responses with rcode 5 (REFUSED)"
},
{
"item_name": "rcode.yxdomain",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'name unexpectedly exists' response",
"item_description": "The number of total responses with rcode 6 (YXDOMAIN)"
},
{
"item_name": "rcode.yxrrset",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'rrset unexpectedly exists' response",
"item_description": "The number of total responses with rcode 7 (YXRRSET)"
},
{
"item_name": "rcode.nxrrset",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'no such rrset' response",
"item_description": "The number of total responses with rcode 8 (NXRRSET)"
},
{
"item_name": "rcode.notauth",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'not authoritative' response",
"item_description": "The number of total responses with rcode 9 (NOTAUTH)"
},
{
"item_name": "rcode.notzone",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'name not in zone' response",
"item_description": "The number of total responses with rcode 10 (NOTZONE)"
},
{
"item_name": "rcode.reserved11",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent response with rcode 11",
"item_description": "The number of total responses with rcode 11 (reserved)"
},
{
"item_name": "rcode.reserved12",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent response with rcode 12",
"item_description": "The number of total responses with rcode 12 (reserved)"
},
{
"item_name": "rcode.reserved13",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent response with rcode 13",
"item_description": "The number of total responses with rcode 13 (reserved)"
},
{
"item_name": "rcode.reserved14",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent response with rcode 14",
"item_description": "The number of total responses with rcode 14 (reserved)"
},
{
"item_name": "rcode.reserved15",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent response with rcode 15",
"item_description": "The number of total responses with rcode 15 (reserved)"
},
{
"item_name": "rcode.badvers",
"item_type": "integer",
"item_optional": true,
"item_default": 0,
"item_title": "Sent 'EDNS version not implemented' response",
"item_description": "The number of total responses with rcode 16 (BADVERS)"
}
]
}
......
......@@ -128,6 +128,22 @@ public:
/// Bind the ModuleSpec object in config_session_ with
/// isc:config::ModuleSpec::validateStatistics.
void registerStatisticsValidator();
/// \brief Resume the server
///
/// This is a wrapper call for DNSServer::resume(done), if 'done' is true,
/// the Rcode set in the given Message is counted in the statistics
/// counter.
///
/// This method is expected to be called by processMessage()
///
/// \param server The DNSServer as passed to processMessage()
/// \param message The response as constructed by processMessage()
/// \param done If true, the Rcode from the given message is counted,
/// this value is then passed to server->resume(bool)
void resumeServer(isc::asiodns::DNSServer* server,
isc::dns::MessagePtr message,
bool done);
private:
std::string db_file_;
......@@ -409,13 +425,13 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
// Ignore all responses.
if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
server->resume(false);
impl_->resumeServer(server, message, false);
return;
}
} catch (const Exception& ex) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
.arg(ex.what());
server->resume(false);
impl_->resumeServer(server, message, false);
return;
}
......@@ -426,13 +442,13 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
.arg(error.getRcode().toText()).arg(error.what());
makeErrorMessage(message, buffer, error.getRcode());
server->resume(true);
impl_->resumeServer(server, message, true);
return;
} catch (const Exception& ex) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
.arg(ex.what());
makeErrorMessage(message, buffer, Rcode::SERVFAIL());
server->resume(true);
impl_->resumeServer(server, message, true);
return;
} // other exceptions will be handled at a higher layer.
......@@ -459,7 +475,7 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
if (tsig_error != TSIGError::NOERROR()) {
makeErrorMessage(message, buffer, tsig_error.toRcode(), tsig_context);
server->resume(true);
impl_->resumeServer(server, message, true);
return;
}
......@@ -492,7 +508,7 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
}
}
server->resume(send_answer);
impl_->resumeServer(server, message, send_answer);
}
bool
......@@ -755,6 +771,14 @@ AuthSrvImpl::setDbFile(ConstElementPtr config) {
return (answer);
}
void
AuthSrvImpl::resumeServer(DNSServer* server, MessagePtr message, bool done) {
if (done) {
counters_.inc(message->getRcode());
}
server->resume(done);
}
ConstElementPtr
AuthSrv::updateConfig(ConstElementPtr new_config) {
try {
......@@ -784,6 +808,11 @@ AuthSrv::getCounter(const Opcode opcode) const {
return (impl_->counters_.getCounter(opcode));
}
uint64_t
AuthSrv::getCounter(const Rcode rcode) const {
return (impl_->counters_.getCounter(rcode));
}
const AddressList&
AuthSrv::getListenAddresses() const {
return (impl_->listen_addresses_);
......
......@@ -361,6 +361,20 @@ public:
/// \return the value of the counter.
uint64_t getCounter(const isc::dns::Opcode opcode) const;
/// \brief Get the value of per Rcode counter in the Auth Counters.
///
/// This function calls AuthCounters::getCounter(isc::dns::Rcode) and
/// returns its return value.
///
/// \note This is a tentative interface as an attempt of experimentally
/// supporting more statistics counters. This should eventually be more
/// generalized. In any case, this method is mainly for testing.
///
/// \throw None
/// \param rcode The rcode of the counter to get the value of
/// \return the value of the counter.
uint64_t getCounter(const isc::dns::Rcode rcode) const;
/**
* \brief Set and get the addresses we listen on.
*/
......
......@@ -50,6 +50,9 @@ public:
void inc(const Opcode opcode) {
opcode_counter_.inc(opcode.getCode());
}
void inc(const Rcode rcode) {
rcode_counter_.inc(rcode.getCode());
}
void inc(const std::string& zone,
const AuthCounters::PerZoneCounterType type);
bool submitStatistics() const;
......@@ -61,10 +64,15 @@ public:
uint64_t getCounter(const Opcode opcode) const {
return (opcode_counter_.get(opcode.getCode()));
}
uint64_t getCounter(const Rcode rcode) const {
return (rcode_counter_.get(rcode.getCode()));
}
private:
Counter server_counter_;
Counter opcode_counter_;
static const size_t NUM_OPCODES = 16;
Counter rcode_counter_;
static const size_t NUM_RCODES = 17;
CounterDictionary per_zone_counter_;
isc::cc::AbstractSession* statistics_session_;
AuthCounters::validator_type validator_;
......@@ -75,7 +83,7 @@ AuthCountersImpl::AuthCountersImpl() :
// size of server_counter_: AuthCounters::SERVER_COUNTER_TYPES
// size of per_zone_counter_: AuthCounters::PER_ZONE_COUNTER_TYPES
server_counter_(AuthCounters::SERVER_COUNTER_TYPES),
opcode_counter_(NUM_OPCODES),
opcode_counter_(NUM_OPCODES), rcode_counter_(NUM_RCODES),
per_zone_counter_(AuthCounters::PER_ZONE_COUNTER_TYPES),
statistics_session_(NULL)
{
......@@ -125,6 +133,19 @@ AuthCountersImpl::submitStatistics() const {
<< counter;
}
}
// Insert non 0 Rcode counters.
for (int i = 0; i < NUM_RCODES; ++i) {
const Counter::Type counter = rcode_counter_.get(i);
if (counter != 0) {
// The counter item name should be derived lower-cased textual
// representation of the code.
std::string rcode_txt = Rcode(i).toText();
std::transform(rcode_txt.begin(), rcode_txt.end(),
rcode_txt.begin(), ::tolower);
statistics_string << ", \"rcode." << rcode_txt << "\": "
<< counter;
}
}
statistics_string << " }"
<< "}"
<< "]}";
......@@ -194,6 +215,11 @@ AuthCounters::inc(const Opcode opcode) {
impl_->inc(opcode);
}
void
AuthCounters::inc(const Rcode rcode) {
impl_->inc(rcode);
}
bool
AuthCounters::submitStatistics() const {
return (impl_->submitStatistics());
......@@ -216,6 +242,11 @@ AuthCounters::getCounter(const Opcode opcode) const {
return (impl_->getCounter(opcode));
}
uint64_t
AuthCounters::getCounter(const Rcode rcode) const {
return (impl_->getCounter(rcode));
}
void
AuthCounters::registerStatisticsValidator
(AuthCounters::validator_type validator) const
......
......@@ -16,6 +16,7 @@
#define __STATISTICS_H 1
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <cc/session.h>
#include <stdint.h>
......@@ -98,6 +99,15 @@ public:
/// \throw None
void inc(const isc::dns::Opcode opcode);
/// \brief Increment the counter of a per rcode counter.
///
/// \note This is a tentative interface. See \c getCounter().
///
/// \param rcode The rcode of the counter to increment.
///
/// \throw None
void inc(const isc::dns::Rcode rcode);
/// \brief Submit statistics counters to statistics module.
///
/// This method is desinged to be called periodically
......@@ -162,6 +172,21 @@ public:
/// \return the value of the counter.
uint64_t getCounter(const isc::dns::Opcode opcode) const;
/// \brief Get the value of a per rcode counter.
///
/// This method returns the value of the per rcode counter for the
/// specified \c rcode.
///
/// \note As mentioned in getCounter(const isc::dns::Opcode opcode),
/// This is a tentative interface as an attempt of experimentally
/// supporting more statistics counters. This should eventually be more
/// generalized. In any case, this method is mainly for testing.
///
/// \throw None
/// \param rcode The rcode of the counter to get the value of
/// \return the value of the counter.
uint64_t getCounter(const isc::dns::Rcode rcode) const;
/// \brief A type of validation function for the specification in
/// isc::config::ModuleSpec.
///
......
......@@ -91,6 +91,35 @@ protected:
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
}
// Helper for checking Rcode statistic counters;
// Checks for one specific Rcode statistics counter value
void checkRcodeCounter(const Rcode& rcode, int expected_value) const {
EXPECT_EQ(expected_value, server.getCounter(rcode)) <<
"Expected Rcode count for " << rcode.toText() <<
" " << expected_value << ", was: " <<
server.getCounter(rcode);
}
// Checks whether all Rcode counters are set to zero
void checkAllRcodeCountersZero() const {
for (int i = 0; i < 17; i++) {
checkRcodeCounter(Rcode(i), 0);
}
}
// Checks whether all Rcode counters are set to zero except the given
// rcode (it is checked to be set to 'value')
void checkAllRcodeCountersZeroExcept(const Rcode& rcode, int value) const {
for (int i = 0; i < 17; i++) {
const Rcode rc(i);
if (rc == rcode) {
checkRcodeCounter(Rcode(i), value);
} else {
checkRcodeCounter(Rcode(i), 0);
}
}
}
IOService ios_;
DNSService dnss_;
MockSession statistics_session;
......@@ -152,6 +181,7 @@ TEST_F(AuthSrvTest, builtInQuery) {
response_obuffer->getData(),
response_obuffer->getLength(),
&response_data[0], response_data.size());
checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
}
// Same test emulating the UDPServer class behavior (defined in libasiolink).
......@@ -202,38 +232,46 @@ TEST_F(AuthSrvTest, iqueryViaDNSServer) {
// Unsupported requests. Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
unsupportedRequest();
// unsupportedRequest tries 14 different opcodes
checkAllRcodeCountersZeroExcept(Rcode::NOTIMP(), 14);
}
// Multiple questions. Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
multiQuestion();
checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
}
// Incoming data doesn't even contain the complete header. Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
shortMessage();
checkAllRcodeCountersZero();
}
// Response messages. Must be silently dropped, whether it's a valid response
// or malformed or could otherwise cause a protocol error.
TEST_F(AuthSrvTest, response) {
response();
checkAllRcodeCountersZero();
}
// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
shortQuestion();
checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
}
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
shortAnswer();
checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
}
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
ednsBadVers();
checkAllRcodeCountersZeroExcept(Rcode::BADVERS(), 1);
}
TEST_F(AuthSrvTest, AXFROverUDP) {
......@@ -251,6 +289,7 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
EXPECT_TRUE(xfrout.isConnected());
checkAllRcodeCountersZero();
}
// Try giving the server a TSIG signed request and see it can anwer signed as
......@@ -286,6 +325,8 @@ TEST_F(AuthSrvTest, TSIGSigned) {
response_obuffer->getLength()));
EXPECT_EQ(TSIGError::NOERROR(), error) <<
"The server signed the response, but it doesn't seem to be valid";
checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
}
// Give the server a signed request, but don't give it the key. It will
......@@ -318,6 +359,8 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
EXPECT_EQ(TSIGError::BAD_KEY_CODE, tsig->getRdata().getError());
EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
"It should be unsigned with this error";
checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
}
// Give the server a signed request, but signed by a different key
......@@ -351,6 +394,8 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
"It should be unsigned with this error";
checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
}
// Give the server a signed unsupported request with a bad signature.
......@@ -390,6 +435,8 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
// TSIG should have failed, and so the per opcode counter shouldn't be
// incremented.
EXPECT_EQ(0, server.getCounter(Opcode::RESERVED14()));
checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
}
TEST_F(AuthSrvTest, AXFRConnectFail) {
......@@ -538,6 +585,8 @@ TEST_F(AuthSrvTest, notify) {
EXPECT_EQ(Name("example.com"), question->getName());
EXPECT_EQ(RRClass::IN(), question->getClass());
EXPECT_EQ(RRType::SOA(), question->getType());
checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
}
TEST_F(AuthSrvTest, notifyForCHClass) {
......@@ -841,6 +890,10 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
&dnsserv);
// After processing UDP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
// The counter for opcode Query should also be one
EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
// The counter for REFUSED responses should also be one, the rest zero
checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
}
// Submit TCP normal query and check query counter
......@@ -856,6 +909,10 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
&dnsserv);
// After processing TCP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
// The counter for SUCCESS responses should also be one
EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
// The counter for REFUSED responses should also be one, the rest zero
checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
}
// Submit TCP AXFR query and check query counter
......@@ -871,6 +928,8 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP AXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
// No rcodes should be incremented
checkAllRcodeCountersZero();
}
// Submit TCP IXFR query and check query counter
......
......@@ -21,6 +21,7 @@
#include <boost/bind.hpp>
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <cc/data.h>
#include <cc/session.h>
......@@ -184,6 +185,16 @@ TEST_F(AuthCountersTest, incrementOpcodeCounter) {
}
}
TEST_F(AuthCountersTest, incrementRcodeCounter) {
// The counter should be initialized to 0. If we increment it by 1
// the counter should be 1.
for (int i = 0; i < 17; ++i) {
EXPECT_EQ(0, counters.getCounter(Rcode(i)));
counters.inc(Rcode(i));
EXPECT_EQ(1, counters.getCounter(Rcode(i)));
}
}
TEST_F(AuthCountersTest, submitStatisticsWithoutSession) {
// Set statistics_session to NULL and call submitStatistics().
// Expect to return false.
......@@ -225,6 +236,29 @@ opcodeDataCheck(ConstElementPtr data, const int expected[16]) {
ASSERT_EQ(static_cast<const char*>(NULL), item_names[i]);
}
void
rcodeDataCheck(ConstElementPtr data, const int expected[17]) {
const char* item_names[] = {
"noerror", "formerr", "servfail", "nxdomain", "notimp", "refused",
"yxdomain", "yxrrset", "nxrrset", "notauth", "notzone", "reserved11",
"reserved12", "reserved13", "reserved14", "reserved15", "badvers",
NULL
};
int i;
for (i = 0; i < 17; ++i) {
ASSERT_NE(static_cast<const char*>(NULL), item_names[i]);
const string item_name = "rcode." + string(item_names[i]);
if (expected[i] == 0) {
EXPECT_FALSE(data->get(item_name));
} else {
EXPECT_EQ(expected[i], data->get(item_name)->intValue());
}
}
// We should have examined all names
ASSERT_EQ(static_cast<const char*>(NULL), item_names[i]);
}
TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
// Submit statistics data.
// Validate if it submits correct data.
......@@ -258,6 +292,10 @@ TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
const int opcode_results[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
opcodeDataCheck(statistics_data, opcode_results);
// By default rcode counters are all 0 and omitted
const int rcode_results[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
rcodeDataCheck(statistics_data, rcode_results);
}
void
......@@ -269,6 +307,15 @@ updateOpcodeCounters(AuthCounters &counters, const int expected[16]) {
}
}
void
updateRcodeCounters(AuthCounters &counters, const int expected[17]) {
for (int i = 0; i < 17; ++i) {
for (int j = 0; j < expected[i]; ++j