|
@@ -87,7 +87,11 @@ protected:
|
|
|
server.setXfrinSession(¬ify_session);
|
|
|
server.setStatisticsSession(&statistics_session);
|
|
|
}
|
|
|
+
|
|
|
virtual void processMessage() {
|
|
|
+ // If processMessage has been called before, parse_message needs
|
|
|
+ // to be reset. If it hasn't, there's no harm in doing so
|
|
|
+ parse_message->clear(Message::PARSE);
|
|
|
server.processMessage(*io_message, parse_message, response_obuffer,
|
|
|
&dnsserv);
|
|
|
}
|
|
@@ -120,6 +124,17 @@ protected:
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // 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();
|
|
|
+ EXPECT_TRUE(dnsserv.hasAnswer());
|
|
|
+ headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
|
|
|
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
IOService ios_;
|
|
|
DNSService dnss_;
|
|
|
MockSession statistics_session;
|
|
@@ -479,17 +494,17 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
|
|
|
}
|
|
|
|
|
|
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.
|
|
@@ -537,17 +552,16 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
|
|
|
}
|
|
|
|
|
|
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.
|
|
@@ -747,7 +761,8 @@ updateConfig(AuthSrv* server, const char* const config_data,
|
|
|
|
|
|
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.
|
|
@@ -987,11 +1002,10 @@ getDummyUnknownSocket() {
|
|
|
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(),
|
|
@@ -1007,9 +1021,7 @@ TEST_F(AuthSrvTest, queryCounterUnexpected) {
|
|
|
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) {
|
|
@@ -1038,4 +1050,231 @@ TEST_F(AuthSrvTest, listenAddresses) {
|
|
|
"Released tokens");
|
|
|
}
|
|
|
|
|
|
+//
|
|
|
+// 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.
|
|
|
+//
|
|
|
+namespace {
|
|
|
+
|
|
|
+/// A the possible methods to throw in, either in FakeInMemoryClient or
|
|
|
+/// FakeZoneFinder
|
|
|
+enum ThrowWhen {
|
|
|
+ THROW_NEVER,
|
|
|
+ THROW_AT_FIND_ZONE,
|
|
|
+ THROW_AT_GET_ORIGIN,
|
|
|
+ THROW_AT_GET_CLASS,
|
|
|
+ THROW_AT_FIND,
|
|
|
+ THROW_AT_FIND_ALL,
|
|
|
+ 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) {
|
|
|
+ if (isc_exception) {
|
|
|
+ isc_throw(isc::Exception, "foo");
|
|
|
+ } else {
|
|
|
+ throw std::exception();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// \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,
|
|
|
+ ThrowWhen throw_when,
|
|
|
+ bool isc_exception) :
|
|
|
+ real_zone_finder_(zone_finder),
|
|
|
+ throw_when_(throw_when),
|
|
|
+ isc_exception_(isc_exception)
|
|
|
+ {}
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ checkThrow(THROW_AT_GET_CLASS, throw_when_, isc_exception_);
|
|
|
+ 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)
|
|
|
+ {
|
|
|
+ checkThrow(THROW_AT_FIND, throw_when_, isc_exception_);
|
|
|
+ return (real_zone_finder_->find(name, type, options));
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual FindResult
|
|
|
+ findAll(const isc::dns::Name& name,
|
|
|
+ std::vector<isc::dns::ConstRRsetPtr> &target,
|
|
|
+ const FindOptions options = FIND_DEFAULT)
|
|
|
+ {
|
|
|
+ checkThrow(THROW_AT_FIND_ALL, throw_when_, isc_exception_);
|
|
|
+ return (real_zone_finder_->findAll(name, target, options));
|
|
|
+ };
|
|
|
+
|
|
|
+ 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));
|
|
|
+ };
|
|
|
+
|
|
|
+ virtual isc::dns::Name
|
|
|
+ findPreviousName(const isc::dns::Name& query) const {
|
|
|
+ return (real_zone_finder_->findPreviousName(query));
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ isc::datasrc::ZoneFinderPtr real_zone_finder_;
|
|
|
+ ThrowWhen throw_when_;
|
|
|
+ bool isc_exception_;
|
|
|
+};
|
|
|
+
|
|
|
+/// \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) :
|
|
|
+ real_client_(real_client),
|
|
|
+ throw_when_(throw_when),
|
|
|
+ isc_exception_(isc_exception)
|
|
|
+ {}
|
|
|
+
|
|
|
+ /// \brief proxy call for findZone
|
|
|
+ ///
|
|
|
+ /// if this instance was constructed with throw_when 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_))));
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ AuthSrv::InMemoryClientPtr real_client_;
|
|
|
+ ThrowWhen throw_when_;
|
|
|
+ bool isc_exception_;
|
|
|
+};
|
|
|
+
|
|
|
+} // 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);
|
|
|
+
|
|
|
+ AuthSrv::InMemoryClientPtr fake_client(
|
|
|
+ new FakeInMemoryClient(server.getInMemoryClient(rrclass),
|
|
|
+ THROW_NEVER,
|
|
|
+ false));
|
|
|
+
|
|
|
+ ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
|
|
|
+ server.setInMemoryClient(rrclass, fake_client);
|
|
|
+
|
|
|
+ createDataFromFile("nsec3query_nodnssec_fromWire.wire");
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
+// 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
|
|
|
+setupThrow(AuthSrv* server, const char *config, ThrowWhen throw_when,
|
|
|
+ bool isc_exception)
|
|
|
+{
|
|
|
+ // Set real inmem client to proxy
|
|
|
+ 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));
|
|
|
+
|
|
|
+ ASSERT_NE(AuthSrv::InMemoryClientPtr(),
|
|
|
+ server->getInMemoryClient(isc::dns::RRClass::IN()));
|
|
|
+ server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(AuthSrvTest, queryWithThrowingProxyServfails) {
|
|
|
+ // Test the common cases, all of which should simply return SERVFAIL
|
|
|
+ // Use THROW_NEVER as end marker
|
|
|
+ ThrowWhen throws[] = { THROW_AT_FIND_ZONE,
|
|
|
+ THROW_AT_GET_ORIGIN,
|
|
|
+ THROW_AT_FIND,
|
|
|
+ THROW_AT_FIND_NSEC3,
|
|
|
+ THROW_NEVER };
|
|
|
+ UnitTestUtil::createDNSSECRequestMessage(request_message, opcode,
|
|
|
+ default_qid, Name("foo.example."),
|
|
|
+ RRClass::IN(), RRType::TXT());
|
|
|
+ for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) {
|
|
|
+ createRequestPacket(request_message, IPPROTO_UDP);
|
|
|
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, true);
|
|
|
+ processAndCheckSERVFAIL();
|
|
|
+ // To be sure, check same for non-isc-exceptions
|
|
|
+ createRequestPacket(request_message, IPPROTO_UDP);
|
|
|
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, false);
|
|
|
+ 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");
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
}
|