return;
}
- // update per opcode statistics counter. This can only be reliable after
- // TSIG check succeeds.
- impl_->counters_.inc(message->getOpcode());
-
- bool send_answer = true;
- if (message->getOpcode() == Opcode::NOTIFY()) {
- send_answer = impl_->processNotify(io_message, message, buffer,
- tsig_context);
- } else if (message->getOpcode() != Opcode::QUERY()) {
- LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
- .arg(message->getOpcode().toText());
- makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
- } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
- makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
- } else {
- ConstQuestionPtr question = *message->beginQuestion();
- const RRType &qtype = question->getType();
- if (qtype == RRType::AXFR()) {
- send_answer = impl_->processXfrQuery(io_message, message, buffer,
- tsig_context);
- } else if (qtype == RRType::IXFR()) {
- send_answer = impl_->processXfrQuery(io_message, message, buffer,
- tsig_context);
+ try {
+ // update per opcode statistics counter. This can only be reliable
+ // after TSIG check succeeds.
+ impl_->counters_.inc(message->getOpcode());
+
+ bool send_answer = true;
+ if (message->getOpcode() == Opcode::NOTIFY()) {
+ send_answer = impl_->processNotify(io_message, message, buffer,
+ tsig_context);
+ } else if (message->getOpcode() != Opcode::QUERY()) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
+ .arg(message->getOpcode().toText());
+ makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
+ } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
} else {
- send_answer = impl_->processNormalQuery(io_message, message,
- buffer, tsig_context);
+ ConstQuestionPtr question = *message->beginQuestion();
+ const RRType &qtype = question->getType();
+ if (qtype == RRType::AXFR()) {
+ send_answer = impl_->processXfrQuery(io_message, message,
+ buffer, tsig_context);
+ } else if (qtype == RRType::IXFR()) {
+ send_answer = impl_->processXfrQuery(io_message, message,
+ buffer, tsig_context);
+ } else {
+ send_answer = impl_->processNormalQuery(io_message, message,
+ buffer, tsig_context);
+ }
}
- }
- impl_->resumeServer(server, message, send_answer);
+ impl_->resumeServer(server, message, send_answer);
+ } catch (const isc::Unexpected&) {
+ // If the error was unexpected protocol, don't even bother responding
+ // (If we see other Unexpected's here, we should probably change to
+ // a specific exception for unknown protocol)
+ impl_->resumeServer(server, message, false);
+ } catch (const isc::Exception&) {
+ // For ISC Exceptions, respond with servfail
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL());
+ impl_->resumeServer(server, message, true);
+ } catch (...) {
+ // Drop the query on any other exceptions (do we want servfail here
+ // too?)
+ impl_->resumeServer(server, message, false);
+ }
}
bool
}
}
- void processAndCheckSERVFAIL(bool expect_answer) {
+ // Convenience method for tests that expect to return SERVFAIL
+ // It calls processMessage, checks if there is an answer, and
+ // check the header for default SERVFAIL data
+ void processAndCheckSERVFAIL() {
processMessage();
- if (expect_answer) {
- EXPECT_TRUE(dnsserv.hasAnswer());
- headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
- opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
- } else {
- EXPECT_FALSE(dnsserv.hasAnswer());
- }
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
}
IOService ios_;
}
TEST_F(AuthSrvTest, AXFRDisconnectFail) {
- // In our usage disconnect() shouldn't fail. So we'll see the exception
- // should it be thrown.
+ // In our usage disconnect() shouldn't fail. But even if it does,
+ // it should not disrupt service (so processMessage should have caught it)
xfrout.disableSend();
xfrout.disableDisconnect();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- XfroutError);
+ EXPECT_NO_THROW(server.processMessage(*io_message, parse_message,
+ response_obuffer, &dnsserv));
+ // Since the disconnect failed, we should still be 'connected'
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
// thrown via the destructor of the server.
}
TEST_F(AuthSrvTest, IXFRDisconnectFail) {
- // In our usage disconnect() shouldn't fail. So we'll see the exception
- // should it be thrown.
+ // In our usage disconnect() shouldn't fail, but even if it does,
+ // procesMessage() should catch it.
xfrout.disableSend();
xfrout.disableDisconnect();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- XfroutError);
+ EXPECT_NO_THROW(server.processMessage(*io_message, parse_message,
+ response_obuffer, &dnsserv));
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
// thrown via the destructor of the server.
ConstElementPtr result = config_answer->get("result");
EXPECT_EQ(Element::list, result->getType());
- EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
+ EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue()) <<
+ "Bad result from updateConfig: " << result->str();
}
// Install a Sqlite3 data source with testing data.
return (socket);
}
-// Submit unexpected type of query and check it throws isc::Unexpected
+// Submit unexpected type of query and check it is ignored
TEST_F(AuthSrvTest, queryCounterUnexpected) {
// This code isn't exception safe, but we'd rather keep the code
- // simpler and more readable as this is only for tests and if it throws
- // the program would immediately terminate anyway.
+ // simpler and more readable as this is only for tests
// Create UDP query packet.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
request_renderer.getLength(),
getDummyUnknownSocket(), *endpoint);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- isc::Unexpected);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
TEST_F(AuthSrvTest, stop) {
// Tests for catching exceptions in various stages of the query processing
//
// These tests work by defining two proxy classes, that act as an in-memory
-// client by default, but can throw exceptions at various points
+// client by default, but can throw exceptions at various points.
//
namespace {
+/// A the possible methods to throw in, either in FakeInMemoryClient or
+/// FakeZoneFinder
enum ThrowWhen {
throw_never,
throw_at_find_zone,
throw_at_get_class,
throw_at_find,
throw_at_find_all,
- throw_at_find_nsec3,
- throw_at_find_previous_name
+ throw_at_find_nsec3
};
+/// convenience function to check whether and what to throw
void
checkThrow(ThrowWhen method, ThrowWhen throw_at, bool isc_exception) {
if (method == throw_at) {
}
}
+/// \brief proxy class for the ZoneFinder returned by the InMemoryClient
+/// proxied by FakeInMemoryClient
+///
+/// See the documentation for FakeInMemoryClient for more information,
+/// all methods simply check whether they should throw, and if not, call
+/// their proxied equivalent.
class FakeZoneFinder : public isc::datasrc::ZoneFinder {
public:
FakeZoneFinder(isc::datasrc::ZoneFinderPtr zone_finder,
throw_when_(throw_when),
isc_exception_(isc_exception)
{}
- virtual isc::dns::Name getOrigin() const {
- checkThrow(throw_at_find_zone, throw_when_, isc_exception_);
- return real_zone_finder_->getOrigin();
+
+ virtual isc::dns::Name
+ getOrigin() const {
+ checkThrow(throw_at_get_origin, throw_when_, isc_exception_);
+ return (real_zone_finder_->getOrigin());
}
- virtual isc::dns::RRClass getClass() const {
+ virtual isc::dns::RRClass
+ getClass() const {
+ assert(false);
checkThrow(throw_at_get_class, throw_when_, isc_exception_);
- return real_zone_finder_->getClass();
+ return (real_zone_finder_->getClass());
}
- virtual isc::datasrc::ZoneFinder::FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- isc::datasrc::ZoneFinder::FindOptions options) {
+ virtual isc::datasrc::ZoneFinder::FindResult
+ find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ isc::datasrc::ZoneFinder::FindOptions options)
+ {
checkThrow(throw_at_find, throw_when_, isc_exception_);
- return real_zone_finder_->find(name, type, options);
+ return (real_zone_finder_->find(name, type, options));
}
- virtual FindResult findAll(const isc::dns::Name& name,
+ virtual FindResult
+ findAll(const isc::dns::Name& name,
std::vector<isc::dns::ConstRRsetPtr> &target,
- const FindOptions options = FIND_DEFAULT) {
+ const FindOptions options = FIND_DEFAULT)
+ {
checkThrow(throw_at_find_all, throw_when_, isc_exception_);
- return real_zone_finder_->findAll(name, target, options);
+ return (real_zone_finder_->findAll(name, target, options));
};
- virtual FindNSEC3Result findNSEC3(const isc::dns::Name& name, bool recursive) {
+ virtual FindNSEC3Result
+ findNSEC3(const isc::dns::Name& name,bool recursive) {
checkThrow(throw_at_find_nsec3, throw_when_, isc_exception_);
- return real_zone_finder_->findNSEC3(name, recursive);
+ return (real_zone_finder_->findNSEC3(name, recursive));
};
- virtual isc::dns::Name findPreviousName(const isc::dns::Name& query) const {
- checkThrow(throw_at_find_previous_name, throw_when_, isc_exception_);
- return real_zone_finder_->findPreviousName(query);
+ virtual isc::dns::Name
+ findPreviousName(const isc::dns::Name& query) const {
+ return (real_zone_finder_->findPreviousName(query));
}
private:
bool isc_exception_;
};
-// Mock Auth Server that can throw exceptions at specified times
+/// \brief Proxy InMemoryClient that can throw exceptions at specified times
+///
+/// It is based on the memory client since that one is easy to override
+/// (with setInMemoryClient) with the current design of AuthSrv.
class FakeInMemoryClient : public isc::datasrc::InMemoryClient {
public:
+ /// \brief Create a proxy memory client
+ ///
+ /// \param real_client The real in-memory client to proxy
+ /// \param throw_when if set to any value other than never, that is
+ /// the method that will throw an exception (either in this
+ /// class or the related FakeZoneFinder)
+ /// \param isc_exception if true, throw isc::Exception, otherwise,
+ /// throw std::exception
FakeInMemoryClient(AuthSrv::InMemoryClientPtr real_client,
ThrowWhen throw_when,
bool isc_exception) :
isc_exception_(isc_exception)
{}
- virtual FindResult findZone(const isc::dns::Name& name) const {
+ /// \brief proxy call for findZone
+ ///
+ /// if this instance was constructed with throw_shen set to find_zone,
+ /// this method will throw. Otherwise, it will return a FakeZoneFinder
+ /// instance which will throw at the method specified at the
+ /// construction of this instance.
+ virtual FindResult
+ findZone(const isc::dns::Name& name) const {
checkThrow(throw_at_find_zone, throw_when_, isc_exception_);
const FindResult result = real_client_->findZone(name);
- return FindResult(result.code, isc::datasrc::ZoneFinderPtr(new FakeZoneFinder(result.zone_finder, throw_when_, isc_exception_)));
+ return (FindResult(result.code, isc::datasrc::ZoneFinderPtr(
+ new FakeZoneFinder(result.zone_finder,
+ throw_when_,
+ isc_exception_))));
}
private:
bool isc_exception_;
};
-} // end namespace for throwing proxy classes
+} // end anonymous namespace for throwing proxy classes
+// Test for the tests
+//
+// Set the proxies to never throw, this should have the same result as
+// queryWithInMemoryClientNoDNSSEC, and serves to test the two proxy classes
TEST_F(AuthSrvTest, queryWithInMemoryClientProxy) {
// Set real inmem client to proxy
updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
- // Set it to never throw, this should have the same result as
- // queryWithInMemoryClientNoDNSSEC, and serves to test the
- // two proxy classes
AuthSrv::InMemoryClientPtr fake_client(
new FakeInMemoryClient(server.getInMemoryClient(rrclass),
throw_never,
opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
}
-// convenience function for setup
+// Convenience function for the rest of the tests, set up a proxy
+// to throw in the given method
+// If isc_exception is true, it will throw isc::Exception, otherwise
+// it will throw std::exception
void
-setupThrowServfail(AuthSrv* server, ThrowWhen throw_when, bool isc_exception)
+setupThrow(AuthSrv* server, const char *config, ThrowWhen throw_when,
+ bool isc_exception)
{
// Set real inmem client to proxy
- updateConfig(server, CONFIG_INMEMORY_EXAMPLE, true);
+ updateConfig(server, config, true);
// Set it to throw on findZone(), this should result in
// SERVFAIL on any exception
AuthSrv::InMemoryClientPtr fake_client(
- new FakeInMemoryClient(server->getInMemoryClient(isc::dns::RRClass::IN()),
- throw_when,
- isc_exception));
+ new FakeInMemoryClient(
+ server->getInMemoryClient(isc::dns::RRClass::IN()),
+ throw_when,
+ isc_exception));
ASSERT_NE(AuthSrv::InMemoryClientPtr(),
server->getInMemoryClient(isc::dns::RRClass::IN()));
server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client);
}
+// Throw isc::Exception at findZone(), should result in SERVFAIL
TEST_F(AuthSrvTest, queryWithInMemoryClientProxyFindZone) {
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- setupThrowServfail(&server, throw_at_find_zone, true);
- processAndCheckSERVFAIL(true);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_find_zone,
+ true);
+ processAndCheckSERVFAIL();
}
-/*
+// Throw std::exception at findZone(), should result in no answer
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxyFindZoneStdException) {
+ createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_find_zone,
+ false);
+ EXPECT_FALSE(dnsserv.hasAnswer());
+}
+
+// Throw isc::Exception at getOrigin(), should result in SERVFAIL
TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetOrigin) {
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- setupThrowServfail(&server, throw_at_get_origin, true);
- processAndCheckSERVFAIL(true);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_get_origin,
+ true);
+ processAndCheckSERVFAIL();
+}
+
+// Throw std::exception at findZone(), should result in SERVFAIL
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetOriginStdException) {
+ createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_get_origin,
+ true);
+ processAndCheckSERVFAIL();
}
+// Throw isc::Exception in getClass(). (Currently?) getClass is not called
+// in the processMessage path, so this should result in a normal answer
TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetClass) {
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- setupThrowServfail(&server, throw_at_get_class, true);
- processAndCheckSERVFAIL(true);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_get_class,
+ true);
+
+ // getClass is not called so it should just answer
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
}
-*/
+// Throw isc::Exception in find(), should result in SERVFAIL
TEST_F(AuthSrvTest, queryWithInMemoryClientProxyFind) {
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- setupThrowServfail(&server, throw_at_find, true);
- processAndCheckSERVFAIL(true);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_find,
+ true);
+ processAndCheckSERVFAIL();
}
-/*
+// Throw std::exception at find(), should result in no answer
TEST_F(AuthSrvTest, queryWithInMemoryClientProxyFindStdException) {
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- setupThrowServfail(&server, throw_at_find, false);
- processAndCheckSERVFAIL(true);
-}
-*/
-/*
-TEST_F(AuthSrvTest, queryWithInMemoryClientProxyFindAll) {
- createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- setupThrowServfail(&server, throw_at_find_all, true);
- processAndCheckSERVFAIL(true);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_find,
+ false);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
+// Throw isc::Exception in findNSEC3(), should result in SERVFAIL
TEST_F(AuthSrvTest, queryWithInMemoryClientProxyFindNSEC3) {
- createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- setupThrowServfail(&server, throw_at_find_nsec3, true);
- processAndCheckSERVFAIL(true);
+ UnitTestUtil::createDNSSECRequestMessage(request_message, opcode,
+ default_qid, Name("foo.example."),
+ RRClass::IN(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_find_nsec3,
+ true);
+ processAndCheckSERVFAIL();
}
-TEST_F(AuthSrvTest, queryWithInMemoryClientProxyFindPreviousName) {
- createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- setupThrowServfail(&server, throw_at_find_previous_name, true);
- processAndCheckSERVFAIL(true);
+// Throw std::exception at findNSEC3(), should result in no answer
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxyFindNSEC3StdException) {
+ UnitTestUtil::createDNSSECRequestMessage(request_message, opcode,
+ default_qid, Name("foo.example."),
+ RRClass::IN(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, throw_at_find_nsec3,
+ false);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
-*/
}