Commit 73d5becc authored by Michal Vaner's avatar Michal Vaner
Browse files

IOService can add and remove servers at runtime

However, it does not seem to close the sockets. It needs to be addressed
somehow and fixed (there's one disabled test because of that).

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/vorner-recursor-config@3337 e5f2f494-b856-4b98-b285-d166d9295462
parent b723ec45
......@@ -22,6 +22,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <vector>
#include <asio.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
......@@ -54,10 +55,50 @@ public:
typedef boost::shared_ptr<UDPServer> UDPServerPtr;
typedef boost::shared_ptr<TCPServer> TCPServerPtr;
UDPServerPtr udp4_server_;
UDPServerPtr udp6_server_;
TCPServerPtr tcp4_server_;
TCPServerPtr tcp6_server_;
typedef boost::shared_ptr<DNSServer> DNSServerPtr;
vector<DNSServerPtr> servers_;
SimpleCallback *checkin_;
DNSLookup *lookup_;
DNSAnswer *answer_;
void addServer(uint16_t port, const ip::address& address) {
try {
TCPServerPtr tcpServer(new TCPServer(io_service_, address, port,
checkin_, lookup_, answer_));
(*tcpServer)();
servers_.push_back(tcpServer);
UDPServerPtr udpServer(new UDPServer(io_service_, address, port,
checkin_, lookup_, answer_));
(*udpServer)();
servers_.push_back(udpServer);
}
catch (const asio::system_error& err) {
// We need to catch and convert any ASIO level exceptions.
// This can happen for unavailable address, binding a privilege port
// without the privilege, etc.
isc_throw(IOError, "Failed to initialize network servers: " <<
err.what());
}
}
void addServer(const char& port, const ip::address& address) {
uint16_t portnum;
try {
// XXX: SunStudio with stlport4 doesn't reject some invalid
// representation such as "-1" by lexical_cast<uint16_t>, so
// we convert it into a signed integer of a larger size and perform
// range check ourselves.
const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
if (portnum32 < 0 || portnum32 > 65535) {
isc_throw(IOError, "Invalid port number '" << &port);
}
portnum = portnum32;
} catch (const boost::bad_lexical_cast& ex) {
isc_throw(IOError, "Invalid port number '" << &port << "': " <<
ex.what());
}
addServer(portnum, address);
}
};
IOServiceImpl::IOServiceImpl(const char& port,
......@@ -66,52 +107,16 @@ IOServiceImpl::IOServiceImpl(const char& port,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer) :
udp4_server_(UDPServerPtr()), udp6_server_(UDPServerPtr()),
tcp4_server_(TCPServerPtr()), tcp6_server_(TCPServerPtr())
checkin_(checkin),
lookup_(lookup),
answer_(answer)
{
uint16_t portnum;
try {
// XXX: SunStudio with stlport4 doesn't reject some invalid
// representation such as "-1" by lexical_cast<uint16_t>, so
// we convert it into a signed integer of a larger size and perform
// range check ourselves.
const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
if (portnum32 < 0 || portnum32 > 65535) {
isc_throw(IOError, "Invalid port number '" << &port);
}
portnum = portnum32;
} catch (const boost::bad_lexical_cast& ex) {
isc_throw(IOError, "Invalid port number '" << &port << "': " <<
ex.what());
}
try {
if (v4addr != NULL) {
udp4_server_ = UDPServerPtr(new UDPServer(io_service_,
*v4addr, portnum,
checkin, lookup, answer));
(*udp4_server_)();
tcp4_server_ = TCPServerPtr(new TCPServer(io_service_,
*v4addr, portnum,
checkin, lookup, answer));
(*tcp4_server_)();
}
if (v6addr != NULL) {
udp6_server_ = UDPServerPtr(new UDPServer(io_service_,
*v6addr, portnum,
checkin, lookup, answer));
(*udp6_server_)();
tcp6_server_ = TCPServerPtr(new TCPServer(io_service_,
*v6addr, portnum,
checkin, lookup, answer));
(*tcp6_server_)();
}
} catch (const asio::system_error& err) {
// We need to catch and convert any ASIO level exceptions.
// This can happen for unavailable address, binding a privilege port
// without the privilege, etc.
isc_throw(IOError, "Failed to initialize network servers: " <<
err.what());
if (v4addr) {
addServer(port, *v4addr);
}
if (v6addr) {
addServer(port, *v6addr);
}
}
......@@ -119,19 +124,9 @@ IOService::IOService(const char& port, const char& address,
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer) :
impl_(NULL)
impl_(new IOServiceImpl(port, NULL, NULL, checkin, lookup, answer))
{
error_code err;
const ip::address addr = ip::address::from_string(&address, err);
if (err) {
isc_throw(IOError, "Invalid IP address '" << &address << "': "
<< err.message());
}
impl_ = new IOServiceImpl(port,
addr.is_v4() ? &addr : NULL,
addr.is_v6() ? &addr : NULL,
checkin, lookup, answer);
addServer(port, &address);
}
IOService::IOService(const char& port,
......@@ -148,10 +143,48 @@ IOService::IOService(const char& port,
impl_ = new IOServiceImpl(port, v4addrp, v6addrp, checkin, lookup, answer);
}
IOService::IOService(SimpleCallback* checkin, DNSLookup* lookup,
DNSAnswer *answer) :
impl_(new IOServiceImpl(*"", NULL, NULL, checkin, lookup, answer))
{
}
IOService::~IOService() {
delete impl_;
}
namespace {
ip::address
convertAddr(const string& address) {
error_code err;
ip::address addr = ip::address::from_string(address, err);
if (err) {
isc_throw(IOError, "Invalid IP address '" << &address << "': "
<< err.message());
}
return addr;
}
}
void
IOService::addServer(const char& port, const string& address) {
impl_->addServer(port, convertAddr(address));
}
void
IOService::addServer(uint16_t port, const string &address) {
impl_->addServer(port, convertAddr(address));
}
void
IOService::clearServers() {
// FIXME: This does not work, it does not close the socket.
// How is it done?
impl_->servers_.clear();
}
void
IOService::run() {
impl_->io_service_.run();
......
......@@ -149,10 +149,20 @@ public:
SimpleCallback* checkin,
DNSLookup* lookup,
DNSAnswer* answer);
/// \brief The constructor without any servers.
///
/// Use addServer() to add some servers.
IOService(SimpleCallback *checkin, DNSLookup* lookup, DNSAnswer *answer);
/// \brief The destructor.
~IOService();
//@}
/// \brief Add another server to the service
void addServer(uint16_t port, const std::string &address);
void addServer(const char &port, const std::string &address);
/// \brief Remove all servers from the service
void clearServers();
/// \brief Start the underlying event loop.
///
/// This method does not return control to the caller until
......
......@@ -341,10 +341,18 @@ protected:
NULL, NULL);
}
// Set up an IO Service queue without any addresses
void setIOService() {
delete io_service_;
io_service_ = NULL;
callback_ = new ASIOCallBack(this);
io_service_ = new IOService(callback_, NULL, NULL);
}
// Run a simple server test, on either IPv4 or IPv6, and over either
// UDP or TCP. Calls the sendUDP() or sendTCP() methods, which will
// start the IO Service queue. The UDPServer or TCPServer that was
// created by setIOSerice() will receive the test packet and issue a
// created by setIOService() will receive the test packet and issue a
// callback, which enables us to check that the data it received
// matches what we sent.
void doTest(const int family, const int protocol) {
......@@ -515,6 +523,30 @@ TEST_F(ASIOLinkTest, v4TCPSendSpecific) {
EXPECT_THROW(sendTCP(AF_INET6), IOError);
}
TEST_F(ASIOLinkTest, v6AddServer) {
setIOService();
io_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
doTest(AF_INET6, IPPROTO_TCP);
EXPECT_THROW(sendTCP(AF_INET), IOError);
}
TEST_F(ASIOLinkTest, v4AddServer) {
setIOService();
io_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
doTest(AF_INET, IPPROTO_TCP);
EXPECT_THROW(sendTCP(AF_INET6), IOError);
}
TEST_F(ASIOLinkTest, DISABLED_clearServers) {
// FIXME: Enable when clearServers actually close the sockets
io_service_->clearServers();
EXPECT_THROW(sendTCP(AF_INET), IOError);
EXPECT_THROW(sendTCP(AF_INET6), IOError);
}
TEST_F(ASIOLinkTest, v6TCPOnly) {
// Open only IPv6 TCP socket. A subsequent attempt of establishing an
// IPv4/TCP connection should fail. See above for why we only test this
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment