Commit d02b99f7 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

[805] Asiodns can accept fd

The dns_service is not tested, so the tests are not added. It is simple
anyway.
parent f9cbe6fb
......@@ -78,6 +78,13 @@ public:
DNSLookup *lookup_;
DNSAnswer *answer_;
template<class Ptr, class Server> void addServerFromFD(int fd, bool v6) {
Ptr server(new Server(io_service_.get_io_service(), fd, v6, checkin_,
lookup_, answer_));
(*server)();
servers_.push_back(server);
}
void addServer(uint16_t port, const asio::ip::address& address) {
try {
dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
......@@ -189,6 +196,14 @@ DNSService::addServer(uint16_t port, const std::string& address) {
impl_->addServer(port, convertAddr(address));
}
void DNSService::addServerTCP(int fd, bool v6) {
impl_->addServerFromFD<DNSServiceImpl::TCPServerPtr, TCPServer>(fd, v6);
}
void DNSService::addServerUDP(int fd, bool v6) {
impl_->addServerFromFD<DNSServiceImpl::UDPServerPtr, UDPServer>(fd, v6);
}
void
DNSService::clearServers() {
BOOST_FOREACH(const DNSServiceImpl::DNSServerPtr& s, impl_->servers_) {
......
......@@ -88,6 +88,10 @@ public:
/// \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 Add another server to the service from already opened file
/// descriptor
void addServerTCP(int fd, bool v6);
void addServerUDP(int fd, bool v6);
/// \brief Remove all servers from the service
void clearServers();
......
......@@ -69,6 +69,19 @@ TCPServer::TCPServer(io_service& io_service,
acceptor_->listen();
}
TCPServer::TCPServer(io_service& io_service, int fd, bool v6,
const SimpleCallback* checkin,
const DNSLookup* lookup,
const DNSAnswer* answer) :
io_(io_service), done_(false),
checkin_callback_(checkin), lookup_callback_(lookup),
answer_callback_(answer)
{
acceptor_.reset(new tcp::acceptor(io_service));
acceptor_->assign(v6 ? tcp::v6() : tcp::v4(), fd);
acceptor_->listen();
}
void
TCPServer::operator()(error_code ec, size_t length) {
/// Because the coroutine reentry block is implemented as
......
......@@ -42,6 +42,16 @@ public:
const isc::asiolink::SimpleCallback* checkin = NULL,
const DNSLookup* lookup = NULL,
const DNSAnswer* answer = NULL);
/// \brief Constructor
/// \param io_service the asio::io_service to work with
/// \param fd the file descriptor of opened UDP socket
/// \param v6 the socket in fd is ipv6 one (if false, it is ipv4)
/// \param checkin the callbackprovider for non-DNS events
/// \param lookup the callbackprovider for DNS lookup events
/// \param answer the callbackprovider for DNS answer events
TCPServer(asio::io_service& io_service, int fd, bool v6,
const isc::asiolink::SimpleCallback* checkin = NULL,
const DNSLookup* lookup = NULL, const DNSAnswer* answer = NULL);
void operator()(asio::error_code ec = asio::error_code(),
size_t length = 0);
......
......@@ -23,6 +23,8 @@
#include <asiodns/dns_answer.h>
#include <asiodns/dns_lookup.h>
#include <string>
#include <cstring>
#include <cerrno>
#include <csignal>
#include <unistd.h> //for alarm
......@@ -30,6 +32,8 @@
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <sys/types.h>
#include <sys/socket.h>
/// The following tests focus on stop interface for udp and
/// tcp server, there are lots of things can be shared to test
......@@ -305,30 +309,14 @@ class TCPClient : public SimpleClient {
uint16_t data_to_send_len_;
};
// \brief provide the context which including two client and
// two server, udp client will only communicate with udp server, same for tcp client
class DNSServerTest : public::testing::Test {
//
// This is only the active part of the test. We run the test case twice, once for each
// type of initialization (once when giving it the address and port, once when giving
// the file descriptor), to ensure it works both ways exactly the same.
class DNSServerTestBase : public::testing::Test {
protected:
void SetUp() {
ip::address server_address = ip::address::from_string(server_ip);
checker_ = new DummyChecker();
lookup_ = new DummyLookup();
answer_ = new SimpleAnswer();
udp_server_ = new UDPServer(service, server_address, server_port,
checker_, lookup_, answer_);
udp_client_ = new UDPClient(service,
ip::udp::endpoint(server_address,
server_port));
tcp_server_ = new TCPServer(service, server_address, server_port,
checker_, lookup_, answer_);
tcp_client_ = new TCPClient(service,
ip::tcp::endpoint(server_address,
server_port));
}
void TearDown() {
udp_server_->stop();
tcp_server_->stop();
......@@ -341,7 +329,6 @@ class DNSServerTest : public::testing::Test {
delete tcp_client_;
}
void testStopServerByStopper(DNSServer* server, SimpleClient* client,
ServerStopper* stopper)
{
......@@ -353,7 +340,8 @@ class DNSServerTest : public::testing::Test {
// Since thread hasn't been introduced into the tool box, using signal
// to make sure run function will eventually return even server stop
// failed
void (*prev_handler)(int) = std::signal(SIGALRM, DNSServerTest::stopIOService);
void (*prev_handler)(int) =
std::signal(SIGALRM, DNSServerTestBase::stopIOService);
alarm(io_service_time_out);
service.run();
service.reset();
......@@ -362,6 +350,18 @@ class DNSServerTest : public::testing::Test {
std::signal(SIGALRM, prev_handler);
}
void commonSetup() {
server_address_ = ip::address::from_string(server_ip);
checker_ = new DummyChecker();
lookup_ = new DummyLookup();
answer_ = new SimpleAnswer();
udp_client_ = new UDPClient(service,
ip::udp::endpoint(server_address_,
server_port));
tcp_client_ = new TCPClient(service,
ip::tcp::endpoint(server_address_,
server_port));
}
static void stopIOService(int _no_use_parameter) {
io_service_is_time_out = true;
......@@ -379,6 +379,7 @@ class DNSServerTest : public::testing::Test {
UDPClient* udp_client_;
TCPClient* tcp_client_;
TCPServer* tcp_server_;
ip::address server_address_;
// To access them in signal handle function, the following
// variables have to be static.
......@@ -386,47 +387,114 @@ class DNSServerTest : public::testing::Test {
static bool io_service_is_time_out;
};
bool DNSServerTest::io_service_is_time_out = false;
asio::io_service DNSServerTest::service;
// Initialization with name and port
class AddrPortInit : public DNSServerTestBase {
protected:
void SetUp() {
commonSetup();
udp_server_ = new UDPServer(service, server_address_, server_port,
checker_, lookup_, answer_);
tcp_server_ = new TCPServer(service, server_address_, server_port,
checker_, lookup_, answer_);
}
};
// Initialization by the file descriptor
class FdInit : public DNSServerTestBase {
private:
// Opens the file descriptor for us
// It uses the low-level C api, as it seems to be the easiest way to get
// a raw file descriptor. It also is what the socket creator does and this
// API is aimed to it.
int getFd(int type) {
int result(socket(AF_INET, type, 0));
if (result == -1) {
return (-1);
}
int on(1);
if (setsockopt(result, SOL_SOCKET, SO_REUSEADDR, &on,
sizeof on) == -1) {
return (-1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(server_port);
if (inet_pton(AF_INET, server_ip.c_str(), &addr.sin_addr.s_addr) != 1) {
return (-1);
}
if (bind(result, reinterpret_cast<const sockaddr*>(&addr),
sizeof addr) == -1) {
return (-1);
}
return (result);
}
protected:
void SetUp() {
commonSetup();
int fdUDP(getFd(SOCK_DGRAM));
ASSERT_NE(-1, fdUDP) << strerror(errno);
udp_server_ = new UDPServer(service, fdUDP, false, checker_, lookup_,
answer_);
int fdTCP(getFd(SOCK_STREAM));
ASSERT_NE(-1, fdTCP) << strerror(errno);
tcp_server_ = new TCPServer(service, fdTCP, false, checker_, lookup_,
answer_);
}
};
// This makes it the template as gtest wants it.
template<class Parent>
class DNSServerTest : public Parent { };
typedef ::testing::Types<AddrPortInit, FdInit> ServerTypes;
TYPED_TEST_CASE(DNSServerTest, ServerTypes);
bool DNSServerTestBase::io_service_is_time_out = false;
asio::io_service DNSServerTestBase::service;
// Test whether server stopped successfully after client get response
// client will send query and start to wait for response, once client
// get response, udp server will be stopped, the io service won't quit
// if udp server doesn't stop successfully.
TEST_F(DNSServerTest, stopUDPServerAfterOneQuery) {
testStopServerByStopper(udp_server_, udp_client_, udp_client_);
EXPECT_EQ(query_message, udp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopUDPServerAfterOneQuery) {
this->testStopServerByStopper(this->udp_server_, this->udp_client_,
this->udp_client_);
EXPECT_EQ(query_message, this->udp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether udp server stopped successfully before server start to serve
TEST_F(DNSServerTest, stopUDPServerBeforeItStartServing) {
udp_server_->stop();
testStopServerByStopper(udp_server_, udp_client_, udp_client_);
EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopUDPServerBeforeItStartServing) {
this->udp_server_->stop();
testStopServerByStopper(this->udp_server_, this->udp_client_,
this->udp_client_);
EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether udp server stopped successfully during message check
TEST_F(DNSServerTest, stopUDPServerDuringMessageCheck) {
testStopServerByStopper(udp_server_, udp_client_, checker_);
EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopUDPServerDuringMessageCheck) {
this->testStopServerByStopper(this->udp_server_, this->udp_client_,
this->checker_);
EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether udp server stopped successfully during query lookup
TEST_F(DNSServerTest, stopUDPServerDuringQueryLookup) {
testStopServerByStopper(udp_server_, udp_client_, lookup_);
EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopUDPServerDuringQueryLookup) {
this->testStopServerByStopper(this->udp_server_, this->udp_client_,
this->lookup_);
EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether udp server stopped successfully during composing answer
TEST_F(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
testStopServerByStopper(udp_server_, udp_client_, answer_);
EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
this->testStopServerByStopper(this->udp_server_, this->udp_client_,
this->answer_);
EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
static void stopServerManyTimes(DNSServer *server, unsigned int times) {
......@@ -437,67 +505,74 @@ static void stopServerManyTimes(DNSServer *server, unsigned int times) {
// Test whether udp server stop interface can be invoked several times without
// throw any exception
TEST_F(DNSServerTest, stopUDPServeMoreThanOnce) {
TYPED_TEST(DNSServerTest, stopUDPServeMoreThanOnce) {
ASSERT_NO_THROW({
boost::function<void()> stop_server_3_times
= boost::bind(stopServerManyTimes, udp_server_, 3);
udp_client_->setGetFeedbackCallback(stop_server_3_times);
testStopServerByStopper(udp_server_, udp_client_, udp_client_);
EXPECT_EQ(query_message, udp_client_->getReceivedData());
= boost::bind(stopServerManyTimes, this->udp_server_, 3);
this->udp_client_->setGetFeedbackCallback(stop_server_3_times);
this->testStopServerByStopper(this->udp_server_,
this->udp_client_, this->udp_client_);
EXPECT_EQ(query_message, this->udp_client_->getReceivedData());
});
EXPECT_TRUE(serverStopSucceed());
EXPECT_TRUE(this->serverStopSucceed());
}
TEST_F(DNSServerTest, stopTCPServerAfterOneQuery) {
testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
EXPECT_EQ(query_message, tcp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopTCPServerAfterOneQuery) {
this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
this->tcp_client_);
EXPECT_EQ(query_message, this->tcp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether tcp server stopped successfully before server start to serve
TEST_F(DNSServerTest, stopTCPServerBeforeItStartServing) {
tcp_server_->stop();
testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopTCPServerBeforeItStartServing) {
this->tcp_server_->stop();
this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
this->tcp_client_);
EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether tcp server stopped successfully during message check
TEST_F(DNSServerTest, stopTCPServerDuringMessageCheck) {
testStopServerByStopper(tcp_server_, tcp_client_, checker_);
EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopTCPServerDuringMessageCheck) {
this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
this->checker_);
EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether tcp server stopped successfully during query lookup
TEST_F(DNSServerTest, stopTCPServerDuringQueryLookup) {
testStopServerByStopper(tcp_server_, tcp_client_, lookup_);
EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopTCPServerDuringQueryLookup) {
testStopServerByStopper(this->tcp_server_, this->tcp_client_,
this->lookup_);
EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether tcp server stopped successfully during composing answer
TEST_F(DNSServerTest, stopTCPServerDuringPrepareAnswer) {
testStopServerByStopper(tcp_server_, tcp_client_, answer_);
EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
EXPECT_TRUE(serverStopSucceed());
TYPED_TEST(DNSServerTest, stopTCPServerDuringPrepareAnswer) {
testStopServerByStopper(this->tcp_server_, this->tcp_client_,
this->answer_);
EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
EXPECT_TRUE(this->serverStopSucceed());
}
// Test whether tcp server stop interface can be invoked several times without
// throw any exception
TEST_F(DNSServerTest, stopTCPServeMoreThanOnce) {
TYPED_TEST(DNSServerTest, stopTCPServeMoreThanOnce) {
ASSERT_NO_THROW({
boost::function<void()> stop_server_3_times
= boost::bind(stopServerManyTimes, tcp_server_, 3);
tcp_client_->setGetFeedbackCallback(stop_server_3_times);
testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
EXPECT_EQ(query_message, tcp_client_->getReceivedData());
= boost::bind(stopServerManyTimes, this->tcp_server_, 3);
this->tcp_client_->setGetFeedbackCallback(stop_server_3_times);
this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
this->tcp_client_);
EXPECT_EQ(query_message, this->tcp_client_->getReceivedData());
});
EXPECT_TRUE(serverStopSucceed());
EXPECT_TRUE(this->serverStopSucceed());
}
}
......@@ -53,7 +53,7 @@ namespace asiodns {
*/
struct UDPServer::Data {
/*
* Constructor from parameters passed to UDPServer constructor.
* Constructors from parameters passed to UDPServer constructor.
* This instance will not be used to retrieve and answer the actual
* query, it will only hold parameters until we wait for the
* first packet. But we do initialize the socket in here.
......@@ -74,6 +74,20 @@ struct UDPServer::Data {
}
socket_->bind(udp::endpoint(addr, port));
}
Data(io_service& io_service, int fd, bool v6,
SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
io_(io_service), done_(false),
checkin_callback_(checkin),lookup_callback_(lookup),
answer_callback_(answer)
{
// We must use different instantiations for v4 and v6;
// otherwise ASIO will bind to both
udp proto = v6 ? udp::v6() : udp::v4();
socket_.reset(new udp::socket(io_service, proto));
// For some strange reason, without this, the assign throws an exception.
socket_->close();
socket_->assign(proto, fd);
}
/*
* Copy constructor. Default one would probably do, but it is unnecessary
......@@ -167,6 +181,11 @@ UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
data_(new Data(io_service, addr, port, checkin, lookup, answer))
{ }
UDPServer::UDPServer(io_service& io_service, int fd, bool v6,
SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
data_(new Data(io_service, fd, v6, checkin, lookup, answer))
{ }
/// The function operator is implemented with the "stackless coroutine"
/// pattern; see internal/coroutine.h for details.
void
......
......@@ -51,6 +51,16 @@ public:
isc::asiolink::SimpleCallback* checkin = NULL,
DNSLookup* lookup = NULL,
DNSAnswer* answer = NULL);
/// \brief Constructor
/// \param io_service the asio::io_service to work with
/// \param fd the file descriptor of opened UDP socket
/// \param v6 the socket in fd is ipv6 one (if false, it is ipv4)
/// \param checkin the callbackprovider for non-DNS events
/// \param lookup the callbackprovider for DNS lookup events
/// \param answer the callbackprovider for DNS answer events
UDPServer(asio::io_service& io_service, int fd, bool v6,
isc::asiolink::SimpleCallback* checkin = NULL,
DNSLookup* lookup = NULL, DNSAnswer* answer = NULL);
/// \brief The function operator
void operator()(asio::error_code ec = asio::error_code(),
......
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