Commit 63e6e8db authored by Jelte Jansen's avatar Jelte Jansen
Browse files

Made IOService realloy only encapsulate asio::io_service

moved all dns specific functionality to the new DNSService


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac383@3305 e5f2f494-b856-4b98-b285-d166d9295462
parent 125ea02a
......@@ -43,13 +43,82 @@ using namespace isc::dns;
namespace asiolink {
class IOServiceImpl {
private:
IOServiceImpl(const IOService& source);
IOServiceImpl& operator=(const IOService& source);
public:
IOServiceImpl(const char& port,
/// \brief The constructor
IOServiceImpl() : io_service_() {};
/// \brief The destructor.
~IOServiceImpl() {};
//@}
/// \brief Start the underlying event loop.
///
/// This method does not return control to the caller until
/// the \c stop() method is called via some handler.
void run() { io_service_.run(); };
/// \brief Run the underlying event loop for a single event.
///
/// This method return control to the caller as soon as the
/// first handler has completed. (If no handlers are ready when
/// it is run, it will block until one is.)
void run_one() { io_service_.run_one();} ;
/// \brief Stop the underlying event loop.
///
/// This will return the control to the caller of the \c run() method.
void stop() { io_service_.stop();} ;
/// \brief Return the native \c io_service object used in this wrapper.
///
/// This is a short term work around to support other BIND 10 modules
/// that share the same \c io_service with the authoritative server.
/// It will eventually be removed once the wrapper interface is
/// generalized.
asio::io_service& get_io_service() { return io_service_; };
private:
asio::io_service io_service_;
};
IOService::IOService() {
io_impl_ = new IOServiceImpl();
}
IOService::~IOService() {
delete io_impl_;
}
void
IOService::run() {
io_impl_->run();
}
void
IOService::run_one() {
io_impl_->run_one();
}
void
IOService::stop() {
io_impl_->stop();
}
asio::io_service&
IOService::get_io_service() {
return io_impl_->get_io_service();
}
class DNSServiceImpl {
public:
DNSServiceImpl(IOService& io_service, const char& port,
const ip::address* v4addr, const ip::address* v6addr,
SimpleCallback* checkin, DNSLookup* lookup,
DNSAnswer* answer);
asio::io_service io_service_;
//asio::io_service io_service_;
void stop();
typedef boost::shared_ptr<UDPServer> UDPServerPtr;
typedef boost::shared_ptr<TCPServer> TCPServerPtr;
UDPServerPtr udp4_server_;
......@@ -58,12 +127,13 @@ public:
TCPServerPtr tcp6_server_;
};
IOServiceImpl::IOServiceImpl(const char& port,
const ip::address* const v4addr,
const ip::address* const v6addr,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer) :
DNSServiceImpl::DNSServiceImpl(IOService& io_service_,
const char& port,
const ip::address* const v4addr,
const ip::address* const v6addr,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer) :
udp4_server_(UDPServerPtr()), udp6_server_(UDPServerPtr()),
tcp4_server_(TCPServerPtr()), tcp6_server_(TCPServerPtr())
{
......@@ -85,21 +155,21 @@ IOServiceImpl::IOServiceImpl(const char& port,
try {
if (v4addr != NULL) {
udp4_server_ = UDPServerPtr(new UDPServer(io_service_,
udp4_server_ = UDPServerPtr(new UDPServer(io_service_.get_io_service(),
*v4addr, portnum,
checkin, lookup, answer));
(*udp4_server_)();
tcp4_server_ = TCPServerPtr(new TCPServer(io_service_,
tcp4_server_ = TCPServerPtr(new TCPServer(io_service_.get_io_service(),
*v4addr, portnum,
checkin, lookup, answer));
(*tcp4_server_)();
}
if (v6addr != NULL) {
udp6_server_ = UDPServerPtr(new UDPServer(io_service_,
udp6_server_ = UDPServerPtr(new UDPServer(io_service_.get_io_service(),
*v6addr, portnum,
checkin, lookup, answer));
(*udp6_server_)();
tcp6_server_ = TCPServerPtr(new TCPServer(io_service_,
tcp6_server_ = TCPServerPtr(new TCPServer(io_service_.get_io_service(),
*v6addr, portnum,
checkin, lookup, answer));
(*tcp6_server_)();
......@@ -113,11 +183,12 @@ IOServiceImpl::IOServiceImpl(const char& port,
}
}
IOService::IOService(const char& port, const char& address,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer) :
impl_(NULL)
DNSService::DNSService(IOService& io_service,
const char& port, const char& address,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer) :
impl_(NULL), io_service_(io_service)
{
error_code err;
const ip::address addr = ip::address::from_string(&address, err);
......@@ -126,53 +197,34 @@ IOService::IOService(const char& port, const char& address,
<< err.message());
}
impl_ = new IOServiceImpl(port,
impl_ = new DNSServiceImpl(io_service, port,
addr.is_v4() ? &addr : NULL,
addr.is_v6() ? &addr : NULL,
checkin, lookup, answer);
}
IOService::IOService(const char& port,
const bool use_ipv4, const bool use_ipv6,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer) :
impl_(NULL)
DNSService::DNSService(IOService& io_service,
const char& port,
const bool use_ipv4, const bool use_ipv6,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer) :
impl_(NULL), io_service_(io_service)
{
const ip::address v4addr_any = ip::address(ip::address_v4::any());
const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL;
const ip::address v6addr_any = ip::address(ip::address_v6::any());
const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
impl_ = new IOServiceImpl(port, v4addrp, v6addrp, checkin, lookup, answer);
impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
}
IOService::~IOService() {
DNSService::~DNSService() {
delete impl_;
}
void
IOService::run() {
impl_->io_service_.run();
}
void
IOService::run_one() {
impl_->io_service_.run_one();
}
void
IOService::stop() {
impl_->io_service_.stop();
}
asio::io_service&
IOService::get_io_service() {
return (impl_->io_service_);
}
RecursiveQuery::RecursiveQuery(IOService& io_service, const char& forward,
RecursiveQuery::RecursiveQuery(DNSService& dns_service, const char& forward,
uint16_t port) :
io_service_(io_service), ns_addr_(&forward), port_(port)
dns_service_(dns_service), ns_addr_(&forward), port_(port)
{}
void
......@@ -184,7 +236,7 @@ RecursiveQuery::sendQuery(const Question& question, OutputBufferPtr buffer,
// the message should be sent via TCP or UDP, or sent initially via
// UDP and then fall back to TCP on failure, but for the moment
// we're only going to handle UDP.
asio::io_service& io = io_service_.get_io_service();
asio::io_service& io = dns_service_.get_io_service();
UDPQuery q(io, question, ns_addr_, port_, buffer, server);
io.post(q);
}
......
......@@ -36,9 +36,11 @@
#include <asiolink/ioendpoint.h>
#include <asiolink/iomessage.h>
#include <asiolink/iosocket.h>
//#include <asio/io_service.hpp>
namespace asio {
// forward declaration for IOService::get_io_service() below
class DNSService;
class io_service;
}
......@@ -93,6 +95,7 @@ class io_service;
/// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html
namespace asiolink {
struct DNSServiceImpl;
struct IOServiceImpl;
/// \brief An exception that is thrown if an error occurs within the IO
......@@ -130,44 +133,94 @@ class IOService {
private:
IOService(const IOService& source);
IOService& operator=(const IOService& source);
public:
/// \brief The constructor
IOService();
/// \brief The destructor.
~IOService();
//@}
/// \brief Start the underlying event loop.
///
/// This method does not return control to the caller until
/// the \c stop() method is called via some handler.
void run();
/// \brief Run the underlying event loop for a single event.
///
/// This method return control to the caller as soon as the
/// first handler has completed. (If no handlers are ready when
/// it is run, it will block until one is.)
void run_one();
/// \brief Stop the underlying event loop.
///
/// This will return the control to the caller of the \c run() method.
void stop();
/// \brief Return the native \c io_service object used in this wrapper.
///
/// This is a short term work around to support other BIND 10 modules
/// that share the same \c io_service with the authoritative server.
/// It will eventually be removed once the wrapper interface is
/// generalized.
asio::io_service& get_io_service();
private:
IOServiceImpl* io_impl_;
};
class DNSService {
///
/// \name Constructors and Destructor
///
/// These are currently very specific to the authoritative server
/// implementation.
///
/// Note: The copy constructor and the assignment operator are
/// intentionally defined as private, making this class non-copyable.
//@{
private:
DNSService(const DNSService& source);
DNSService& operator=(const DNSService& source);
public:
/// \brief The constructor with a specific IP address and port on which
/// the services listen on.
IOService(const char& port, const char& address,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer);
DNSService(IOService& io_service, const char& port, const char& address,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer);
/// \brief The constructor with a specific port on which the services
/// listen on.
///
/// It effectively listens on "any" IPv4 and/or IPv6 addresses.
/// IPv4/IPv6 services will be available if and only if \c use_ipv4
/// or \c use_ipv6 is \c true, respectively.
IOService(const char& port, const bool use_ipv4, const bool use_ipv6,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer);
DNSService(IOService& io_service, const char& port, const bool use_ipv4, const bool use_ipv6,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer);
/// \brief The destructor.
~IOService();
~DNSService();
//@}
/// \brief Start the underlying event loop.
///
/// This method does not return control to the caller until
/// the \c stop() method is called via some handler.
void run();
// void run();
/// \brief Run the underlying event loop for a single event.
///
/// This method return control to the caller as soon as the
/// first handler has completed. (If no handlers are ready when
/// it is run, it will block until one is.)
void run_one();
// void run_one();
/// \brief Stop the underlying event loop.
///
/// This will return the control to the caller of the \c run() method.
void stop();
// void stop();
/// \brief Return the native \c io_service object used in this wrapper.
///
......@@ -175,9 +228,10 @@ public:
/// that share the same \c io_service with the authoritative server.
/// It will eventually be removed once the wrapper interface is
/// generalized.
asio::io_service& get_io_service();
asio::io_service& get_io_service() { return io_service_.get_io_service(); };
private:
IOServiceImpl* impl_;
DNSServiceImpl* impl_;
IOService& io_service_;
};
/// \brief The \c DNSServer class is a wrapper (and base class) for
......@@ -447,7 +501,7 @@ public:
/// This is currently the only way to construct \c RecursiveQuery
/// object. The address of the forward nameserver is specified,
/// and all upstream queries will be sent to that one address.
RecursiveQuery(IOService& io_service, const char& forward,
RecursiveQuery(DNSService& dns_service, const char& forward,
uint16_t port = 53);
//@}
......@@ -465,7 +519,7 @@ public:
isc::dns::OutputBufferPtr buffer,
DNSServer* server);
private:
IOService& io_service_;
DNSService& dns_service_;
IOAddress ns_addr_;
uint16_t port_;
};
......
......@@ -135,69 +135,92 @@ TEST(IOSocketTest, dummySockets) {
}
TEST(IOServiceTest, badPort) {
EXPECT_THROW(IOService(*"65536", true, false, NULL, NULL, NULL), IOError);
EXPECT_THROW(IOService(*"5300.0", true, false, NULL, NULL, NULL), IOError);
EXPECT_THROW(IOService(*"-1", true, false, NULL, NULL, NULL), IOError);
EXPECT_THROW(IOService(*"domain", true, false, NULL, NULL, NULL), IOError);
IOService io_service;
EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
EXPECT_THROW(DNSService(io_service, *"5300.0", true, false, NULL, NULL, NULL), IOError);
EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
}
TEST(IOServiceTest, badAddress) {
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
IOService io_service;
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
}
TEST(IOServiceTest, unavailableAddress) {
IOService io_service;
// These addresses should generally be unavailable as a valid local
// address, although there's no guarantee in theory.
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *"255.255.0.0", NULL, NULL, NULL), IOError);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"255.255.0.0", NULL, NULL, NULL), IOError);
// Some OSes would simply reject binding attempt for an AF_INET6 socket
// to an IPv4-mapped IPv6 address. Even if those that allow it, since
// the corresponding IPv4 address is the same as the one used in the
// AF_INET socket case above, it should at least show the same result
// as the previous one.
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *"::ffff:255.255.0.0", NULL, NULL, NULL), IOError);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:255.255.0.0", NULL, NULL, NULL), IOError);
}
TEST(IOServiceTest, duplicateBind) {
TEST(IOServiceTest, duplicateBind_v6) {
// In each sub test case, second attempt should fail due to duplicate bind
IOService io_service;
// IPv6, "any" address
IOService* io_service = new IOService(*TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
EXPECT_THROW(IOService(*TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
delete io_service;
DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
delete dns_service;
}
TEST(IOServiceTest, duplicateBind_v6_address) {
// In each sub test case, second attempt should fail due to duplicate bind
IOService io_service;
// IPv6, specific address
io_service = new IOService(*TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
delete io_service;
DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
delete dns_service;
}
TEST(IOServiceTest, duplicateBind_v4) {
// In each sub test case, second attempt should fail due to duplicate bind
IOService io_service;
// IPv4, "any" address
io_service = new IOService(*TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
EXPECT_THROW(IOService(*TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
delete io_service;
DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
delete dns_service;
}
TEST(IOServiceTest, duplicateBind_v4_address) {
// In each sub test case, second attempt should fail due to duplicate bind
IOService io_service;
// IPv4, specific address
io_service = new IOService(*TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
delete io_service;
DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
delete dns_service;
}
// Disabled because IPv4-mapped addresses don't seem to be working with
// the IOService constructor
TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
IOService io_service;
// Duplicate bind on IPv4-mapped IPv6 address
IOService* io_service = new IOService(*TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
delete io_service;
DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
delete dns_service;
// XXX:
// Currently, this throws an "invalid argument" exception. I have
// not been able to get IPv4-mapped addresses to work.
io_service = new IOService(*TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
EXPECT_THROW(IOService(*TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
delete io_service;
dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
delete dns_service;
}
// This function returns an addrinfo structure for use by tests, using
......@@ -234,7 +257,7 @@ resolveAddress(const int family, const int protocol, const bool client) {
// received on the server side. It then checks the received data matches
// expected parameters.
// If initialization parameters of the IOService should be modified, the test
// case can do it using the setIOService() method.
// case can do it using the setDNSService() method.
// Note: the set of tests in ASIOLinkTest use actual network services and may
// involve undesirable side effects such as blocking.
class ASIOLinkTest : public ::testing::Test {
......@@ -247,12 +270,15 @@ protected:
if (sock_ != -1) {
close(sock_);
}
if (io_service_ != NULL) {
delete io_service_;
if (dns_service_ != NULL) {
delete dns_service_;
}
if (callback_ != NULL) {
delete callback_;
}
if (io_service_) {
delete io_service_;
}
}
// Send a test UDP packet to a mock server
......@@ -324,21 +350,25 @@ protected:
// Set up an IO Service queue using the specified address
void setIOService(const char& address) {
void setDNSService(const char& address) {
delete dns_service_;
dns_service_ = NULL;
delete io_service_;
io_service_ = NULL;
io_service_ = new IOService();
callback_ = new ASIOCallBack(this);
io_service_ = new IOService(*TEST_SERVER_PORT, address, callback_, NULL, NULL);
dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, address, callback_, NULL, NULL);
}
// Set up an IO Service queue using the "any" address, on IPv4 if
// 'use_ipv4' is true and on IPv6 if 'use_ipv6' is true.
void setIOService(const bool use_ipv4, const bool use_ipv6) {
void setDNSService(const bool use_ipv4, const bool use_ipv6) {
delete dns_service_;
dns_service_ = NULL;
delete io_service_;
io_service_ = NULL;
io_service_ = new IOService();
callback_ = new ASIOCallBack(this);
io_service_ = new IOService(*TEST_SERVER_PORT, use_ipv4, use_ipv6, callback_,
NULL, NULL);
dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, use_ipv4, use_ipv6, callback_,
NULL, NULL);
}
// Run a simple server test, on either IPv4 or IPv6, and over either
......@@ -447,7 +477,10 @@ private:
io_service_->stop();
}
protected:
// We use a pointer for io_service_, because for some tests we
// need to recreate a new one within one onstance of this class
IOService* io_service_;
DNSService* dns_service_;
ASIOCallBack* callback_;
int callback_protocol_;
int callback_native_;
......@@ -459,9 +492,10 @@ private:
};
ASIOLinkTest::ASIOLinkTest() :
io_service_(NULL), callback_(NULL), sock_(-1), res_(NULL)
dns_service_(NULL), callback_(NULL), sock_(-1), res_(NULL)
{
setIOService(true, true);
io_service_ = new IOService();
setDNSService(true, true);
}
TEST_F(ASIOLinkTest, v6UDPSend) {
......@@ -492,24 +526,24 @@ TEST_F(ASIOLinkTest, v6UDPSendSpecific) {
// an error on a subsequent read operation. We could do it, but for
// simplicity we only tests the easier cases for now.
setIOService(*TEST_IPV6_ADDR);
setDNSService(*TEST_IPV6_ADDR);
doTest(AF_INET6, IPPROTO_UDP);
}
TEST_F(ASIOLinkTest, v6TCPSendSpecific) {
setIOService(*TEST_IPV6_ADDR);
setDNSService(*TEST_IPV6_ADDR);
doTest(AF_INET6, IPPROTO_TCP);
EXPECT_THROW(sendTCP(AF_INET), IOError);
}
TEST_F(ASIOLinkTest, v4UDPSendSpecific) {
setIOService(*TEST_IPV4_ADDR);
setDNSService(*TEST_IPV4_ADDR);
doTest(AF_INET, IPPROTO_UDP);
}
TEST_F(ASIOLinkTest, v4TCPSendSpecific) {
setIOService(*TEST_IPV4_ADDR);
setDNSService(*TEST_IPV4_ADDR);
doTest(AF_INET, IPPROTO_TCP);
EXPECT_THROW(sendTCP(AF_INET6), IOError);
......@@ -519,25 +553,25 @@ TEST_F(ASIOLinkTest, v6TCPOnly) {