From: Michal 'vorner' Vaner Date: Mon, 5 Dec 2011 16:15:59 +0000 (+0100) Subject: [805] Asiodns can accept fd X-Git-Tag: trac2351_base~304^2~15^2~25 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d02b99f79bec594fc795936b18954543c87edc67;p=thirdparty%2Fkea.git [805] Asiodns can accept fd The dns_service is not tested, so the tests are not added. It is simple anyway. --- diff --git a/src/lib/asiodns/dns_service.cc b/src/lib/asiodns/dns_service.cc index 94510fe520..964b77e706 100644 --- a/src/lib/asiodns/dns_service.cc +++ b/src/lib/asiodns/dns_service.cc @@ -78,6 +78,13 @@ public: DNSLookup *lookup_; DNSAnswer *answer_; + template 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(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(fd, v6); +} + +void DNSService::addServerUDP(int fd, bool v6) { + impl_->addServerFromFD(fd, v6); +} + void DNSService::clearServers() { BOOST_FOREACH(const DNSServiceImpl::DNSServerPtr& s, impl_->servers_) { diff --git a/src/lib/asiodns/dns_service.h b/src/lib/asiodns/dns_service.h index 6b6a6c07f4..75b688eb1f 100644 --- a/src/lib/asiodns/dns_service.h +++ b/src/lib/asiodns/dns_service.h @@ -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(); diff --git a/src/lib/asiodns/tcp_server.cc b/src/lib/asiodns/tcp_server.cc index f91eb3293b..05cf14041f 100644 --- a/src/lib/asiodns/tcp_server.cc +++ b/src/lib/asiodns/tcp_server.cc @@ -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 diff --git a/src/lib/asiodns/tcp_server.h b/src/lib/asiodns/tcp_server.h index 22a2f691f2..71cd5ba981 100644 --- a/src/lib/asiodns/tcp_server.h +++ b/src/lib/asiodns/tcp_server.h @@ -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); diff --git a/src/lib/asiodns/tests/dns_server_unittest.cc b/src/lib/asiodns/tests/dns_server_unittest.cc index 1ef3192f35..cd80e2ade2 100644 --- a/src/lib/asiodns/tests/dns_server_unittest.cc +++ b/src/lib/asiodns/tests/dns_server_unittest.cc @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include //for alarm @@ -30,6 +32,8 @@ #include #include +#include +#include /// 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(&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 DNSServerTest : public Parent { }; + +typedef ::testing::Types 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 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 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()); } } diff --git a/src/lib/asiodns/udp_server.cc b/src/lib/asiodns/udp_server.cc index f103e5a94c..69dcade4c8 100644 --- a/src/lib/asiodns/udp_server.cc +++ b/src/lib/asiodns/udp_server.cc @@ -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 diff --git a/src/lib/asiodns/udp_server.h b/src/lib/asiodns/udp_server.h index 4c19544365..2d564fc228 100644 --- a/src/lib/asiodns/udp_server.h +++ b/src/lib/asiodns/udp_server.h @@ -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(),