Commit bbb92779 authored by Evan Hunt's avatar Evan Hunt

Substantial further (but still very incomplete) work on ASIO structure.

DNS lookup calls can now be asynchronous, calling back into the UDPServer
or TCPServer coroutine that originated them via io_service::post().

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac327@3084 e5f2f494-b856-4b98-b285-d166d9295462
parent 0089203f
......@@ -14,6 +14,8 @@
// $Id$
#include <config.h>
#include <netinet/in.h>
#include <algorithm>
......@@ -123,28 +125,58 @@ AuthSrvImpl::~AuthSrvImpl() {
}
}
// This is a derived class of \c DNSProvider, to serve as a
// This is a derived class of \c DNSLookup, to serve as a
// callback in the asiolink module. It calls
// AuthSrv::processMessage() on a single DNS message.
class MessageProcessor : public DNSProvider {
class MessageLookup : public DNSLookup {
public:
MessageProcessor(AuthSrv* srv) : server_(srv) {}
virtual bool operator()(const IOMessage& io_message,
MessageLookup(AuthSrv* srv) : server_(srv) {}
virtual void operator()(const IOMessage& io_message,
isc::dns::Message& dns_message,
isc::dns::MessageRenderer& renderer) const {
return (server_->processMessage(io_message, dns_message, renderer));
isc::dns::MessageRenderer& renderer,
BasicServer* server, bool& complete) const
{
server_->processMessage(io_message, dns_message, renderer,
server, complete);
}
private:
AuthSrv* server_;
};
// This is a derived class of \c DNSAnswer, to serve as a
// callback in the asiolink module. It takes a completed
// set of answer data from the DNS lookup and assembles it
// into a wire-format response.
class MessageAnswer : public DNSAnswer {
public:
MessageAnswer(AuthSrv* srv) : server_(srv) {}
virtual void operator()(const IOMessage& io_message,
isc::dns::Message& message,
isc::dns::MessageRenderer& renderer) const
{
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
renderer.setLengthLimit(message.getUDPSize());
} else {
renderer.setLengthLimit(65535);
}
message.toWire(renderer);
if (server_->getVerbose()) {
cerr << "[b10-recurse] sending a response (" << renderer.getLength()
<< " bytes):\n" << message.toText() << endl;
}
}
private:
AuthSrv* server_;
};
// This is a derived class of \c CheckinProvider, to serve
// This is a derived class of \c IOCallback, to serve
// as a callback in the asiolink module. It checks for queued
// configuration messages, and executes them if found.
class ConfigChecker : public CheckinProvider {
class ConfigChecker : public IOCallback {
public:
ConfigChecker(AuthSrv* srv) : server_(srv) {}
virtual void operator()(void) const {
virtual void operator()(const IOMessage& io_message UNUSED_PARAM) const {
if (server_->configSession()->hasQueuedMsgs()) {
server_->configSession()->checkCommand();
}
......@@ -156,13 +188,15 @@ private:
AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client) :
impl_(new AuthSrvImpl(use_cache, xfrout_client)),
checkin_provider_(new ConfigChecker(this)),
dns_provider_(new MessageProcessor(this))
dns_lookup_(new MessageLookup(this)),
dns_answer_(new MessageAnswer(this))
{}
AuthSrv::~AuthSrv() {
delete impl_;
delete checkin_provider_;
delete dns_provider_;
delete dns_lookup_;
delete dns_answer_;
}
namespace {
......@@ -241,9 +275,10 @@ AuthSrv::configSession() const {
return (impl_->config_session_);
}
bool
void
AuthSrv::processMessage(const IOMessage& io_message, Message& message,
MessageRenderer& response_renderer)
MessageRenderer& response_renderer,
BasicServer* server, bool& complete)
{
InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
......@@ -258,14 +293,21 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
cerr << "[b10-auth] received unexpected response, ignoring"
<< endl;
}
return (false);
complete = false;
server->resume();
return;
}
} catch (const Exception& ex) {
return (false);
if (impl_->verbose_mode_) {
cerr << "[b10-auth] DNS packet exception: " << ex.what() << endl;
}
complete = false;
server->resume();
return;
}
// Parse the message. On failure, return an appropriate error.
try {
// Parse the message.
message.fromWire(request_buffer);
} catch (const DNSProtocolError& error) {
if (impl_->verbose_mode_) {
......@@ -274,14 +316,18 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
}
makeErrorMessage(message, response_renderer, error.getRcode(),
impl_->verbose_mode_);
return (true);
complete = true;
server->resume();
return;
} catch (const Exception& ex) {
if (impl_->verbose_mode_) {
cerr << "[b10-auth] returning SERVFAIL: " << ex.what() << endl;
}
makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
impl_->verbose_mode_);
return (true);
complete = true;
server->resume();
return;
} // other exceptions will be handled at a higher layer.
if (impl_->verbose_mode_) {
......@@ -291,35 +337,36 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
// Perform further protocol-level validation.
if (message.getOpcode() == Opcode::NOTIFY()) {
return (impl_->processNotify(io_message, message, response_renderer));
complete = impl_->processNotify(io_message, message,
response_renderer);
} else if (message.getOpcode() != Opcode::QUERY()) {
if (impl_->verbose_mode_) {
cerr << "[b10-auth] unsupported opcode" << endl;
}
makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
impl_->verbose_mode_);
return (true);
}
if (message.getRRCount(Section::QUESTION()) != 1) {
complete = true;
} else if (message.getRRCount(Section::QUESTION()) != 1) {
makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
impl_->verbose_mode_);
return (true);
}
ConstQuestionPtr question = *message.beginQuestion();
const RRType &qtype = question->getType();
if (qtype == RRType::AXFR()) {
return (impl_->processAxfrQuery(io_message, message,
response_renderer));
} else if (qtype == RRType::IXFR()) {
makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
impl_->verbose_mode_);
return (true);
complete = true;
} else {
return (impl_->processNormalQuery(io_message, message,
response_renderer));
ConstQuestionPtr question = *message.beginQuestion();
const RRType &qtype = question->getType();
if (qtype == RRType::AXFR()) {
complete = impl_->processAxfrQuery(io_message, message,
response_renderer);
} else if (qtype == RRType::IXFR()) {
makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
impl_->verbose_mode_);
complete = true;
} else {
complete = impl_->processNormalQuery(io_message, message,
response_renderer);
}
}
server->resume();
}
bool
......
......@@ -66,19 +66,26 @@ public:
//@}
/// \return \c true if the \message contains a response to be returned;
/// otherwise \c false.
bool processMessage(const asiolink::IOMessage& io_message,
void processMessage(const asiolink::IOMessage& io_message,
isc::dns::Message& message,
isc::dns::MessageRenderer& response_renderer);
isc::dns::MessageRenderer& response_renderer,
asiolink::BasicServer* server, bool& complete);
void setVerbose(bool on);
bool getVerbose() const;
isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
isc::config::ModuleCCSession* configSession() const;
void setConfigSession(isc::config::ModuleCCSession* config_session);
asiolink::DNSProvider* getDNSProvider() {
return (dns_provider_);
void setIOService(asiolink::IOService& ios) { io_service_ = &ios; }
asiolink::IOService& getIOService() const { return (*io_service_); }
asiolink::DNSLookup* getDNSLookupProvider() const {
return (dns_lookup_);
}
asiolink::DNSAnswer* getDNSAnswerProvider() const {
return (dns_answer_);
}
asiolink::CheckinProvider* getCheckinProvider() {
asiolink::IOCallback* getCheckinProvider() const {
return (checkin_provider_);
}
......@@ -98,8 +105,10 @@ public:
void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
private:
AuthSrvImpl* impl_;
asiolink::CheckinProvider* checkin_provider_;
asiolink::DNSProvider* dns_provider_;
asiolink::IOService* io_service_;
asiolink::IOCallback* checkin_provider_;
asiolink::DNSLookup* dns_lookup_;
asiolink::DNSAnswer* dns_answer_;
};
#endif // __AUTH_SRV_H
......
......@@ -177,8 +177,9 @@ main(int argc, char* argv[]) {
auth_server->setVerbose(verbose_mode);
cout << "[b10-auth] Server created." << endl;
CheckinProvider* checkin = auth_server->getCheckinProvider();
DNSProvider* process = auth_server->getDNSProvider();
IOCallback* checkin = auth_server->getCheckinProvider();
DNSLookup* lookup = auth_server->getDNSLookupProvider();
DNSAnswer* answer = auth_server->getDNSAnswerProvider();
if (address != NULL) {
// XXX: we can only specify at most one explicit address.
......@@ -188,11 +189,12 @@ main(int argc, char* argv[]) {
// is a short term workaround until we support dynamic listening
// port allocation.
io_service = new IOService(*port, *address,
checkin, process);
checkin, lookup, answer);
} else {
io_service = new IOService(*port, use_ipv4, use_ipv6,
checkin, process);
checkin, lookup, answer);
}
auth_server->setIOService(*io_service);
cout << "[b10-auth] IOService created." << endl;
cc_session = new Session(io_service->get_io_service());
......
......@@ -122,6 +122,13 @@ private:
bool receive_ok_;
};
// A nonoperative task object to be used in calls to processMessage()
class MockTask : public BasicServer {
void operator()(asio::error_code ec UNUSED_PARAM,
size_t length UNUSED_PARAM)
{}
};
protected:
AuthSrvTest() : server(true, xfrout),
request_message(Message::RENDER),
......@@ -140,6 +147,7 @@ protected:
}
MockSession notify_session;
MockXfroutClient xfrout;
MockTask noOp;
AuthSrv server;
Message request_message;
Message parse_message;
......@@ -273,7 +281,6 @@ AuthSrvTest::createDataFromFile(const char* const datafile,
const int protocol = IPPROTO_UDP)
{
delete io_message;
delete io_sock;
data.clear();
delete endpoint;
......@@ -359,8 +366,10 @@ TEST_F(AuthSrvTest, unsupportedRequest) {
data[2] = ((i << 3) & 0xff);
parse_message.clear(Message::PARSE);
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
0, 0, 0, 0);
}
......@@ -378,8 +387,10 @@ TEST_F(AuthSrvTest, verbose) {
// Multiple questions. Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
createDataFromFile("multiquestion_fromWire");
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
QR_FLAG, 2, 0, 0, 0);
......@@ -399,8 +410,10 @@ TEST_F(AuthSrvTest, multiQuestion) {
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
createDataFromFile("shortmessage_fromWire");
EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(done);
}
// Response messages. Must be silently dropped, whether it's a valid response
......@@ -408,26 +421,32 @@ TEST_F(AuthSrvTest, shortMessage) {
TEST_F(AuthSrvTest, response) {
// A valid (although unusual) response
createDataFromFile("simpleresponse_fromWire");
EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(done);
// A response with a broken question section. must be dropped rather than
// returning FORMERR.
createDataFromFile("shortresponse_fromWire");
EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
response_renderer));
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(done);
// A response to iquery. must be dropped rather than returning NOTIMP.
createDataFromFile("iqueryresponse_fromWire");
EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
response_renderer));
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(done);
}
// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
createDataFromFile("shortquestion_fromWire");
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
// Since the query's question is broken, the question section of the
// response should be empty.
headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
......@@ -437,8 +456,10 @@ TEST_F(AuthSrvTest, shortQuestion) {
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
createDataFromFile("shortanswer_fromWire");
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
// This is a bogus query, but question section is valid. So the response
// should copy the question section.
......@@ -456,8 +477,10 @@ TEST_F(AuthSrvTest, shortAnswer) {
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
createDataFromFile("queryBadEDNS_fromWire");
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
// The response must have an EDNS OPT RR in the additional section.
// Note that the DNSSEC DO bit is cleared even if this bit in the query
......@@ -472,8 +495,10 @@ TEST_F(AuthSrvTest, AXFROverUDP) {
// AXFR over UDP is invalid and should result in FORMERR.
createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
RRType::AXFR(), IPPROTO_UDP);
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
QR_FLAG, 1, 0, 0, 0);
}
......@@ -484,8 +509,10 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
RRType::AXFR(), IPPROTO_TCP);
// On success, the AXFR query has been passed to a separate process,
// so we shouldn't have to respond.
EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(done);
EXPECT_FALSE(xfrout.isConnected());
}
......@@ -494,8 +521,10 @@ TEST_F(AuthSrvTest, AXFRConnectFail) {
xfrout.disableConnect();
createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
RRType::AXFR(), IPPROTO_TCP);
EXPECT_TRUE(server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
// For a shot term workaround with xfrout we currently close the connection
......@@ -508,7 +537,9 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
// open.
createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
RRType::AXFR(), IPPROTO_TCP);
server.processMessage(*io_message, parse_message, response_renderer);
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(xfrout.isConnected()); // see above
xfrout.disableSend();
......@@ -516,8 +547,9 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
response_renderer.clear();
createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
RRType::AXFR(), IPPROTO_TCP);
EXPECT_TRUE(server.processMessage(*io_message, parse_message,
response_renderer));
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
......@@ -532,8 +564,9 @@ TEST_F(AuthSrvTest, AXFRDisconnectFail) {
xfrout.disableDisconnect();
createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
RRType::AXFR(), IPPROTO_TCP);
bool done;
EXPECT_THROW(server.processMessage(*io_message, parse_message,
response_renderer),
response_renderer, &noOp, done),
XfroutError);
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
......@@ -546,8 +579,10 @@ TEST_F(AuthSrvTest, notify) {
RRType::SOA());
request_message.setHeaderFlag(MessageFlag::AA());
createRequestPacket(IPPROTO_UDP);
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
// An internal command message should have been created and sent to an
// external module. Check them.
......@@ -578,8 +613,10 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
RRType::SOA());
request_message.setHeaderFlag(MessageFlag::AA());
createRequestPacket(IPPROTO_UDP);
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
// Other conditions should be the same, so simply confirm the RR class is
// set correctly.
......@@ -595,8 +632,10 @@ TEST_F(AuthSrvTest, notifyEmptyQuestion) {
request_message.setQid(default_qid);
request_message.toWire(request_renderer);
createRequestPacket(IPPROTO_UDP);
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}
......@@ -609,8 +648,10 @@ TEST_F(AuthSrvTest, notifyMultiQuestions) {
RRType::SOA()));
request_message.setHeaderFlag(MessageFlag::AA());
createRequestPacket(IPPROTO_UDP);
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
}
......@@ -620,8 +661,10 @@ TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
RRType::NS());
request_message.setHeaderFlag(MessageFlag::AA());
createRequestPacket(IPPROTO_UDP);
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
}
......@@ -630,8 +673,10 @@ TEST_F(AuthSrvTest, notifyWithoutAA) {
// implicitly leave the AA bit off. our implementation will accept it.
createRequestPacket(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
RRType::SOA());
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}
......@@ -642,8 +687,10 @@ TEST_F(AuthSrvTest, notifyWithErrorRcode) {
request_message.setHeaderFlag(MessageFlag::AA());
request_message.setRcode(Rcode::SERVFAIL());
createRequestPacket(IPPROTO_UDP);
EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_TRUE(done);
headerCheck(parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}
......@@ -658,8 +705,10 @@ TEST_F(AuthSrvTest, notifyWithoutSession) {
// we simply ignore the notify and let it be resent if an internal error
// happens.
EXPECT_FALSE(server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(done);
}
TEST_F(AuthSrvTest, notifySendFail) {
......@@ -670,8 +719,10 @@ TEST_F(AuthSrvTest, notifySendFail) {
request_message.setHeaderFlag(MessageFlag::AA());
createRequestPacket(IPPROTO_UDP);
EXPECT_FALSE(server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(done);
}
TEST_F(AuthSrvTest, notifyReceiveFail) {
......@@ -681,8 +732,10 @@ TEST_F(AuthSrvTest, notifyReceiveFail) {
RRType::SOA());
request_message.setHeaderFlag(MessageFlag::AA());
createRequestPacket(IPPROTO_UDP);
EXPECT_FALSE(server.processMessage(*io_message, parse_message,
response_renderer));
bool done;
server.processMessage(*io_message, parse_message, response_renderer,
&noOp, done);
EXPECT_FALSE(done);
}
TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
......@@ -692,8 +745,10 @@ TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
RRType::SOA());
request_message.setHeaderFlag(MessageFlag::AA());