Commit 4065c720 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[1784] cleanups, tests, and doc for addServerUDPFromFD.

"UDPVersion" is now more generic options, and its default is the previous
behavior, which should be more suitable default.  added test cases to
confirm the effect of the options.  added detailed documentation.
parent af8054f4
......@@ -207,17 +207,13 @@ void DNSService::addServerTCPFromFD(int fd, int af) {
impl_->addServerFromFD<DNSServiceImpl::TCPServerPtr, TCPServer>(fd, af);
}
void DNSService::addServerUDPFromFD(int fd, int af,
const UDPVersion param_flags)
{
if (SYNC_ == param_flags) {
void DNSService::addServerUDPFromFD(int fd, int af, ServerFlag options) {
if ((options & SERVER_SYNC_OK) != 0) {
impl_->addServerFromFD<DNSServiceImpl::SyncUDPServerPtr,
SyncUDPServer>(fd, af);
} else if (ASYNC_ == param_flags) {
} else {
impl_->addServerFromFD<DNSServiceImpl::UDPServerPtr, UDPServer>(
fd, af);
} else {
isc_throw(IOError, "Bad UDPServer Version!");
}
}
......
......@@ -27,15 +27,6 @@ class DNSLookup;
class DNSAnswer;
class DNSServiceImpl;
/// Codes for UDPServers used in addServer()method.
///
/// Note: the codes only used in how to create the UDPServers.
enum UDPVersion {
SYNC_ = 1, ///< used synchronous UDPServer
ASYNC_ = 2 ///< used asynchronous UDPServer
};
/// \brief Handle DNS Queries
///
/// DNSService is the service that handles DNS queries and answers with
......@@ -44,6 +35,31 @@ enum UDPVersion {
/// server implementations. As such, it handles asio, including config
/// updates (through the 'Checkinprovider'), and listening sockets.
class DNSService {
public:
/// \brief Flags for optional server properties.
///
/// The values of this enumerable type are intended to be used to specify
/// a particular property of the server created via the \c addServer
/// variants. As we see need for more such properties, a compound
/// form of flags (i.e., a single value generated by bitwise OR'ed
/// multiple flag values) will be allowed.
enum ServerFlag {
SERVER_DEFAULT = 0, ///< The default flag (no particular property)
SERVER_SYNC_OK = 1 ///< The server can act in the "synchronous" mode.
///< In this mode, the client ensures that the
///< lookup provider always completes the query
///< process and it immediately releases the
///< ownership of the given buffer. This allows
///< the server implementation to introduce some
///< optimization such as omitting unnecessary
///< operation or reusing internal resources.
///< Note that in functionality the non
///< "synchronous" mode is compatible with the
///< synchronous mode; it's up to the server
///< implementation whether it exploits the
///< information given by the client.
};
///
/// \name Constructors and Destructor
///
......@@ -99,7 +115,7 @@ 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);
void addServer(const char& port, const std::string& address);
/// \brief Add another TCP server/listener to the service from already
/// opened file descriptor
......@@ -111,6 +127,10 @@ public:
/// specific address) and is ready for listening to new connection
/// requests but has not actually started listening.
///
/// At the moment, TCP servers don't support any optional properties;
/// so unlike the UDP version of the method it doesn't have an \c options
/// argument.
///
/// \param fd the file descriptor to be used.
/// \param af the address family of the file descriptor. Must be either
/// AF_INET or AF_INET6.
......@@ -131,10 +151,13 @@ public:
/// \param fd the file descriptor to be used.
/// \param af the address family of the file descriptor. Must be either
/// AF_INET or AF_INET6.
/// \param options Optional properties of the server (see ServerFlag).
///
/// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6.
/// \throw isc::asiolink::IOError when a low-level error happens, like the
/// fd is not a valid descriptor or it can't be listened on.
void addServerUDPFromFD(int fd, int af,const UDPVersion param_flags = SYNC_);
void addServerUDPFromFD(int fd, int af,
ServerFlag options = SERVER_DEFAULT);
/// \brief Remove all servers from the service
void clearServers();
......@@ -160,3 +183,7 @@ private:
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_SERVICE_H
// Local Variables:
// mode: c++
// End:
......@@ -18,7 +18,7 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += io_service_unittest.cc
run_unittests_SOURCES += dns_service_unittest.cc
run_unittests_SOURCES += dns_server_unittest.cc
run_unittests_SOURCES += io_fetch_unittest.cc
......
......@@ -20,11 +20,22 @@
#include <asiodns/asiodns.h>
#include <boost/scoped_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <csignal>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
using namespace isc::asiolink;
using namespace isc::asiodns;
using boost::scoped_ptr;
using boost::lexical_cast;
namespace {
const char* const TEST_SERVER_PORT = "53535";
const char* const TEST_CLIENT_PORT = "53536";
const char* const TEST_IPV6_ADDR = "::1";
......@@ -119,10 +130,174 @@ TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
delete dns_service;
}
TEST(IOServiceTest, BadUdpServerVersion) {
// A simple lookup callback for DNS services. It records the pointer value of
// to given output buffer each time the callback is called (up to two times)
// for the main tests. At the end of the second callback it stops the server.
// The sender of the data doesn't expect to get a response, so it simply
// discards any received data.
class TestLookup : public DNSLookup {
public:
TestLookup(isc::util::OutputBuffer** b1, isc::util::OutputBuffer** b2,
IOService& io_service) :
first_buffer_(b1), second_buffer_(b2), io_service_(io_service)
{}
void operator()(const IOMessage&, isc::dns::MessagePtr,
isc::dns::MessagePtr, isc::util::OutputBufferPtr buffer,
DNSServer* server) const
{
server->resume(false);
if (*first_buffer_ == NULL) {
*first_buffer_ = buffer.get();
} else {
assert(*second_buffer_ == NULL);
*second_buffer_ = buffer.get();
server->stop();
io_service_.stop();
}
}
isc::util::OutputBuffer** first_buffer_;
isc::util::OutputBuffer** second_buffer_;
IOService& io_service_;
};
// A test fixture to check creation of UDP servers from a socket FD, changing
// options.
class UDPDNSServiceTest : public::testing::Test {
private:
static const unsigned int IO_SERVICE_TIME_OUT = 5;
protected:
UDPDNSServiceTest() :
first_buffer_(NULL), second_buffer_(NULL),
lookup(&first_buffer_, &second_buffer_, io_service),
dns_service(io_service, NULL, &lookup, NULL),
client_socket(io_service.get_io_service(), asio::ip::udp::v6()),
server_ep(asio::ip::address::from_string(TEST_IPV6_ADDR),
lexical_cast<uint16_t>(TEST_SERVER_PORT)),
asio_service(io_service.get_io_service())
{
current_service = &io_service;
}
~UDPDNSServiceTest() {
current_service = NULL;
}
void runService() {
io_service_is_time_out = false;
// Send two UDP packets, which will be passed to the TestLookup
// callback. They are not expected to be responded, so it simply
// closes the socket right after that.
client_socket.send_to(asio::buffer(data, sizeof(data)), server_ep);
client_socket.send_to(asio::buffer(data, sizeof(data)), server_ep);
client_socket.close();
// set a signal-based alarm to prevent the test from hanging up
// due to a bug.
void (*prev_handler)(int) =
std::signal(SIGALRM, UDPDNSServiceTest::stopIOService);
current_service = &io_service;
alarm(IO_SERVICE_TIME_OUT);
io_service.run();
io_service.get_io_service().reset();
//cancel scheduled alarm
alarm(0);
std::signal(SIGALRM, prev_handler);
}
// last resort service stopper by signal
static void stopIOService(int) {
io_service_is_time_out = true;
if (current_service != NULL) {
current_service->stop();
}
}
bool serverStopSucceed() const {
return (!io_service_is_time_out);
}
isc::util::OutputBuffer* first_buffer_;
isc::util::OutputBuffer* second_buffer_;
IOService io_service;
scoped_ptr<DNSService> dns_service(new DNSService(io_service, NULL, NULL,
NULL));
EXPECT_THROW(dns_service->addServerUDPFromFD(42, AF_INET6,
UDPVersion(3)), IOError);
TestLookup lookup;
DNSService dns_service;
private:
asio::ip::udp::socket client_socket;
const asio::ip::udp::endpoint server_ep;
char data[4]; // the content doesn't matter for the test
// To access them in signal handle function, the following
// variables have to be static.
static IOService* current_service;
static bool io_service_is_time_out;
asio::io_service& asio_service;
};
// A helper socket FD creator for given address and port. It's generally
// expected to succeed; on failure it simply throws an exception to make
// the test fail.
int
getSocketFD(int family, const char* const address, const char* const port) {
struct addrinfo hints, *res = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
int s = -1;
int error = getaddrinfo(address, port, &hints, &res);
if (error == 0) {
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s >= 0) {
error = bind(s, res->ai_addr, res->ai_addrlen);
}
}
if (res != NULL) {
freeaddrinfo(res);
}
if (error != 0) {
if (s >= 0) {
close(s);
}
isc_throw(isc::Unexpected, "failed to open test socket");
}
return (s);
}
TEST_F(UDPDNSServiceTest, defaultUDPServerFromFD) {
// If no option is explicitly specified, an asynchronous server should be
// created. So the two output buffers should be different.
dns_service.addServerUDPFromFD(getSocketFD(AF_INET6, TEST_IPV6_ADDR,
TEST_SERVER_PORT), AF_INET6);
runService();
EXPECT_TRUE(serverStopSucceed());
EXPECT_NE(first_buffer_, second_buffer_);
}
TEST_F(UDPDNSServiceTest, explicitDefaultUDPServerFromFD) {
// If "default" option is explicitly specified, the effect should be the
// same as the previous case.
dns_service.addServerUDPFromFD(getSocketFD(AF_INET6, TEST_IPV6_ADDR,
TEST_SERVER_PORT),
AF_INET6, DNSService::SERVER_DEFAULT);
runService();
EXPECT_TRUE(serverStopSucceed());
EXPECT_NE(first_buffer_, second_buffer_);
}
TEST_F(UDPDNSServiceTest, syncUDPServerFromFD) {
// If "SYNC_OK" option is specified, a synchronous server should be
// created. It will reuse the output buffer, so the recorded two pointer
// should be identical.
dns_service.addServerUDPFromFD(getSocketFD(AF_INET6, TEST_IPV6_ADDR,
TEST_SERVER_PORT),
AF_INET6, DNSService::SERVER_SYNC_OK);
runService();
EXPECT_TRUE(serverStopSucceed());
EXPECT_EQ(first_buffer_, second_buffer_);
}
} // unnamed namespace
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