mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-03 23:45:27 +00:00
[master] Merge branch 'trac1820'
This commit is contained in:
@@ -14,30 +14,19 @@
|
|||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <unistd.h> // for some IPC/network system calls
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
|
|
||||||
#include <log/dummylog.h>
|
|
||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
#include <asio.hpp>
|
|
||||||
#include <dns_service.h>
|
#include <dns_service.h>
|
||||||
|
|
||||||
#include <asiolink/io_service.h>
|
#include <asiolink/io_service.h>
|
||||||
|
|
||||||
|
#include <asio.hpp> // xxx_server.h requires this to be included first
|
||||||
#include <tcp_server.h>
|
#include <tcp_server.h>
|
||||||
#include <udp_server.h>
|
#include <udp_server.h>
|
||||||
#include <sync_udp_server.h>
|
#include <sync_udp_server.h>
|
||||||
|
|
||||||
#include <log/dummylog.h>
|
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
using isc::log::dlog;
|
|
||||||
|
|
||||||
using namespace isc::asiolink;
|
using namespace isc::asiolink;
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
@@ -46,29 +35,13 @@ namespace asiodns {
|
|||||||
class DNSLookup;
|
class DNSLookup;
|
||||||
class DNSAnswer;
|
class DNSAnswer;
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
asio::ip::address
|
|
||||||
convertAddr(const std::string& address) {
|
|
||||||
asio::error_code err;
|
|
||||||
asio::ip::address addr = asio::ip::address::from_string(address, err);
|
|
||||||
if (err) {
|
|
||||||
isc_throw(IOError, "Invalid IP address '" << &address << "': "
|
|
||||||
<< err.message());
|
|
||||||
}
|
|
||||||
return (addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DNSServiceImpl {
|
class DNSServiceImpl {
|
||||||
public:
|
public:
|
||||||
DNSServiceImpl(IOService& io_service, const char& port,
|
DNSServiceImpl(IOService& io_service, SimpleCallback* checkin,
|
||||||
const asio::ip::address* v4addr,
|
DNSLookup* lookup, DNSAnswer* answer) :
|
||||||
const asio::ip::address* v6addr,
|
io_service_(io_service), checkin_(checkin), lookup_(lookup),
|
||||||
SimpleCallback* checkin, DNSLookup* lookup,
|
answer_(answer)
|
||||||
DNSAnswer* answe);
|
{}
|
||||||
|
|
||||||
IOService& io_service_;
|
IOService& io_service_;
|
||||||
|
|
||||||
@@ -77,9 +50,9 @@ public:
|
|||||||
typedef boost::shared_ptr<TCPServer> TCPServerPtr;
|
typedef boost::shared_ptr<TCPServer> TCPServerPtr;
|
||||||
typedef boost::shared_ptr<DNSServer> DNSServerPtr;
|
typedef boost::shared_ptr<DNSServer> DNSServerPtr;
|
||||||
std::vector<DNSServerPtr> servers_;
|
std::vector<DNSServerPtr> servers_;
|
||||||
SimpleCallback *checkin_;
|
SimpleCallback* checkin_;
|
||||||
DNSLookup *lookup_;
|
DNSLookup* lookup_;
|
||||||
DNSAnswer *answer_;
|
DNSAnswer* answer_;
|
||||||
|
|
||||||
template<class Ptr, class Server> void addServerFromFD(int fd, int af) {
|
template<class Ptr, class Server> void addServerFromFD(int fd, int af) {
|
||||||
Ptr server(new Server(io_service_.get_io_service(), fd, af, checkin_,
|
Ptr server(new Server(io_service_.get_io_service(), fd, af, checkin_,
|
||||||
@@ -87,107 +60,12 @@ public:
|
|||||||
(*server)();
|
(*server)();
|
||||||
servers_.push_back(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));
|
|
||||||
TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
|
|
||||||
address, port, checkin_,
|
|
||||||
lookup_, answer_));
|
|
||||||
(*tcpServer)();
|
|
||||||
servers_.push_back(tcpServer);
|
|
||||||
dlog(std::string("Initialize UDP server at ") +
|
|
||||||
address.to_string() + ":" +
|
|
||||||
boost::lexical_cast<std::string>(port));
|
|
||||||
UDPServerPtr udpServer(new UDPServer(io_service_.get_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 asio::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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DNSServiceImpl::DNSServiceImpl(IOService& io_service,
|
|
||||||
const char& port,
|
|
||||||
const asio::ip::address* const v4addr,
|
|
||||||
const asio::ip::address* const v6addr,
|
|
||||||
SimpleCallback* checkin,
|
|
||||||
DNSLookup* lookup,
|
|
||||||
DNSAnswer* answer) :
|
|
||||||
io_service_(io_service),
|
|
||||||
checkin_(checkin),
|
|
||||||
lookup_(lookup),
|
|
||||||
answer_(answer)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (v4addr) {
|
|
||||||
addServer(port, *v4addr);
|
|
||||||
}
|
|
||||||
if (v6addr) {
|
|
||||||
addServer(port, *v6addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DNSService::DNSService(IOService& io_service,
|
|
||||||
const char& port, const char& address,
|
|
||||||
SimpleCallback* checkin,
|
|
||||||
DNSLookup* lookup,
|
|
||||||
DNSAnswer* answer) :
|
|
||||||
impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
|
|
||||||
answer)),
|
|
||||||
io_service_(io_service)
|
|
||||||
{
|
|
||||||
addServer(port, &address);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 asio::ip::address v4addr_any =
|
|
||||||
asio::ip::address(asio::ip::address_v4::any());
|
|
||||||
const asio::ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL;
|
|
||||||
const asio::ip::address v6addr_any =
|
|
||||||
asio::ip::address(asio::ip::address_v6::any());
|
|
||||||
const asio::ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
|
|
||||||
impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin,
|
|
||||||
lookup, answer);
|
|
||||||
}
|
|
||||||
|
|
||||||
DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
|
DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
|
||||||
DNSLookup* lookup, DNSAnswer *answer) :
|
DNSLookup* lookup, DNSAnswer *answer) :
|
||||||
impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
|
impl_(new DNSServiceImpl(io_service, checkin, lookup, answer)),
|
||||||
answer)), io_service_(io_service)
|
io_service_(io_service)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,16 +73,6 @@ DNSService::~DNSService() {
|
|||||||
delete impl_;
|
delete impl_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
DNSService::addServer(const char& port, const std::string& address) {
|
|
||||||
impl_->addServer(port, convertAddr(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
DNSService::addServer(uint16_t port, const std::string& address) {
|
|
||||||
impl_->addServer(port, convertAddr(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSService::addServerTCPFromFD(int fd, int af) {
|
void DNSService::addServerTCPFromFD(int fd, int af) {
|
||||||
impl_->addServerFromFD<DNSServiceImpl::TCPServerPtr, TCPServer>(fd, af);
|
impl_->addServerFromFD<DNSServiceImpl::TCPServerPtr, TCPServer>(fd, af);
|
||||||
}
|
}
|
||||||
|
@@ -112,40 +112,14 @@ private:
|
|||||||
static const unsigned int SERVER_DEFINED_FLAGS = 1;
|
static const unsigned int SERVER_DEFINED_FLAGS = 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// \brief The constructor with a specific IP address and port on which
|
|
||||||
/// the services listen on.
|
|
||||||
///
|
|
||||||
/// \param io_service The IOService to work with
|
|
||||||
/// \param port the port to listen on
|
|
||||||
/// \param address the IP address to listen on
|
|
||||||
/// \param checkin Provider for cc-channel events (see \c SimpleCallback)
|
|
||||||
/// \param lookup The lookup provider (see \c DNSLookup)
|
|
||||||
/// \param answer The answer provider (see \c DNSAnswer)
|
|
||||||
DNSService(asiolink::IOService& io_service, const char& port,
|
|
||||||
const char& address, isc::asiolink::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.
|
|
||||||
///
|
|
||||||
/// \param io_service The IOService to work with
|
|
||||||
/// \param port the port to listen on
|
|
||||||
/// \param use_ipv4 If true, listen on ipv4 'any'
|
|
||||||
/// \param use_ipv6 If true, listen on ipv6 'any'
|
|
||||||
/// \param checkin Provider for cc-channel events (see \c SimpleCallback)
|
|
||||||
/// \param lookup The lookup provider (see \c DNSLookup)
|
|
||||||
/// \param answer The answer provider (see \c DNSAnswer)
|
|
||||||
DNSService(asiolink::IOService& io_service, const char& port,
|
|
||||||
const bool use_ipv4, const bool use_ipv6,
|
|
||||||
isc::asiolink::SimpleCallback* checkin, DNSLookup* lookup,
|
|
||||||
DNSAnswer* answer);
|
|
||||||
/// \brief The constructor without any servers.
|
/// \brief The constructor without any servers.
|
||||||
///
|
///
|
||||||
/// Use addServer() to add some servers.
|
/// Use addServerTCPFromFD() or addServerUDPFromFD() to add some servers.
|
||||||
|
///
|
||||||
|
/// \param io_service The IOService to work with
|
||||||
|
/// \param checkin Provider for cc-channel events (see \c SimpleCallback)
|
||||||
|
/// \param lookup The lookup provider (see \c DNSLookup)
|
||||||
|
/// \param answer The answer provider (see \c DNSAnswer)
|
||||||
DNSService(asiolink::IOService& io_service,
|
DNSService(asiolink::IOService& io_service,
|
||||||
isc::asiolink::SimpleCallback* checkin,
|
isc::asiolink::SimpleCallback* checkin,
|
||||||
DNSLookup* lookup, DNSAnswer* answer);
|
DNSLookup* lookup, DNSAnswer* answer);
|
||||||
@@ -154,10 +128,6 @@ public:
|
|||||||
virtual ~DNSService();
|
virtual ~DNSService();
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// \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 TCP server/listener to the service from already
|
/// \brief Add another TCP server/listener to the service from already
|
||||||
/// opened file descriptor
|
/// opened file descriptor
|
||||||
///
|
///
|
||||||
|
@@ -38,29 +38,6 @@ using namespace isc::asiolink;
|
|||||||
namespace isc {
|
namespace isc {
|
||||||
namespace asiodns {
|
namespace asiodns {
|
||||||
|
|
||||||
SyncUDPServer::SyncUDPServer(asio::io_service& io_service,
|
|
||||||
const asio::ip::address& addr,
|
|
||||||
const uint16_t port,
|
|
||||||
asiolink::SimpleCallback* checkin,
|
|
||||||
DNSLookup* lookup, DNSAnswer* answer) :
|
|
||||||
output_buffer_(new isc::util::OutputBuffer(0)),
|
|
||||||
query_(new isc::dns::Message(isc::dns::Message::PARSE)),
|
|
||||||
answer_(new isc::dns::Message(isc::dns::Message::RENDER)),
|
|
||||||
io_(io_service), checkin_callback_(checkin), lookup_callback_(lookup),
|
|
||||||
answer_callback_(answer), stopped_(false)
|
|
||||||
{
|
|
||||||
// We must use different instantiations for v4 and v6;
|
|
||||||
// otherwise ASIO will bind to both
|
|
||||||
asio::ip::udp proto = addr.is_v4() ? asio::ip::udp::v4() :
|
|
||||||
asio::ip::udp::v6();
|
|
||||||
socket_.reset(new asio::ip::udp::socket(io_service, proto));
|
|
||||||
socket_->set_option(asio::socket_base::reuse_address(true));
|
|
||||||
if (addr.is_v6()) {
|
|
||||||
socket_->set_option(asio::ip::v6_only(true));
|
|
||||||
}
|
|
||||||
socket_->bind(asio::ip::udp::endpoint(addr, port));
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncUDPServer::SyncUDPServer(asio::io_service& io_service, const int fd,
|
SyncUDPServer::SyncUDPServer(asio::io_service& io_service, const int fd,
|
||||||
const int af, asiolink::SimpleCallback* checkin,
|
const int af, asiolink::SimpleCallback* checkin,
|
||||||
DNSLookup* lookup, DNSAnswer* answer) :
|
DNSLookup* lookup, DNSAnswer* answer) :
|
||||||
|
@@ -42,19 +42,6 @@ namespace asiodns {
|
|||||||
/// the UDPClass.
|
/// the UDPClass.
|
||||||
class SyncUDPServer : public DNSServer, public boost::noncopyable {
|
class SyncUDPServer : public DNSServer, public boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
/// \brief Constructor
|
|
||||||
/// \param io_service the asio::io_service to work with
|
|
||||||
/// \param addr the IP address to listen for queries on
|
|
||||||
/// \param port the port to listen for queries on
|
|
||||||
/// \param checkin the callbackprovider for non-DNS events
|
|
||||||
/// \param lookup the callbackprovider for DNS lookup events
|
|
||||||
/// \param answer the callbackprovider for DNS answer events
|
|
||||||
explicit SyncUDPServer(asio::io_service& io_service,
|
|
||||||
const asio::ip::address& addr, const uint16_t port,
|
|
||||||
isc::asiolink::SimpleCallback* checkin = NULL,
|
|
||||||
DNSLookup* lookup = NULL,
|
|
||||||
DNSAnswer* answer = NULL);
|
|
||||||
|
|
||||||
/// \brief Constructor
|
/// \brief Constructor
|
||||||
/// \param io_service the asio::io_service to work with
|
/// \param io_service the asio::io_service to work with
|
||||||
/// \param fd the file descriptor of opened UDP socket
|
/// \param fd the file descriptor of opened UDP socket
|
||||||
|
@@ -47,28 +47,6 @@ namespace asiodns {
|
|||||||
/// The following functions implement the \c TCPServer class.
|
/// The following functions implement the \c TCPServer class.
|
||||||
///
|
///
|
||||||
/// The constructor
|
/// The constructor
|
||||||
TCPServer::TCPServer(io_service& io_service,
|
|
||||||
const ip::address& addr, const uint16_t port,
|
|
||||||
const SimpleCallback* checkin,
|
|
||||||
const DNSLookup* lookup,
|
|
||||||
const DNSAnswer* answer) :
|
|
||||||
io_(io_service), done_(false),
|
|
||||||
checkin_callback_(checkin), lookup_callback_(lookup),
|
|
||||||
answer_callback_(answer)
|
|
||||||
{
|
|
||||||
tcp::endpoint endpoint(addr, port);
|
|
||||||
acceptor_.reset(new tcp::acceptor(io_service));
|
|
||||||
acceptor_->open(endpoint.protocol());
|
|
||||||
// Set v6-only (we use a separate instantiation for v4,
|
|
||||||
// otherwise asio will bind to both v4 and v6
|
|
||||||
if (addr.is_v6()) {
|
|
||||||
acceptor_->set_option(ip::v6_only(true));
|
|
||||||
}
|
|
||||||
acceptor_->set_option(tcp::acceptor::reuse_address(true));
|
|
||||||
acceptor_->bind(endpoint);
|
|
||||||
acceptor_->listen();
|
|
||||||
}
|
|
||||||
|
|
||||||
TCPServer::TCPServer(io_service& io_service, int fd, int af,
|
TCPServer::TCPServer(io_service& io_service, int fd, int af,
|
||||||
const SimpleCallback* checkin,
|
const SimpleCallback* checkin,
|
||||||
const DNSLookup* lookup,
|
const DNSLookup* lookup,
|
||||||
|
@@ -37,12 +37,6 @@ namespace asiodns {
|
|||||||
/// defined in coroutine.h.
|
/// defined in coroutine.h.
|
||||||
class TCPServer : public virtual DNSServer, public virtual coroutine {
|
class TCPServer : public virtual DNSServer, public virtual coroutine {
|
||||||
public:
|
public:
|
||||||
explicit TCPServer(asio::io_service& io_service,
|
|
||||||
const asio::ip::address& addr, const uint16_t port,
|
|
||||||
const isc::asiolink::SimpleCallback* checkin = NULL,
|
|
||||||
const DNSLookup* lookup = NULL,
|
|
||||||
const DNSAnswer* answer = NULL);
|
|
||||||
|
|
||||||
/// \brief Constructor
|
/// \brief Constructor
|
||||||
/// \param io_service the asio::io_service to work with
|
/// \param io_service the asio::io_service to work with
|
||||||
/// \param fd the file descriptor of opened TCP socket
|
/// \param fd the file descriptor of opened TCP socket
|
||||||
|
@@ -414,22 +414,7 @@ class DNSServerTestBase : public::testing::Test {
|
|||||||
static bool io_service_is_time_out;
|
static bool io_service_is_time_out;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialization with name and port
|
// Initialization (by the file descriptor)
|
||||||
template<class UDPServerClass>
|
|
||||||
class AddrPortInit : public DNSServerTestBase<UDPServerClass> {
|
|
||||||
protected:
|
|
||||||
AddrPortInit() {
|
|
||||||
this->udp_server_ = new UDPServerClass(this->service,
|
|
||||||
this->server_address_,
|
|
||||||
server_port, this->checker_,
|
|
||||||
this->lookup_, this->answer_);
|
|
||||||
this->tcp_server_ = new TCPServer(this->service, this->server_address_,
|
|
||||||
server_port, this->checker_,
|
|
||||||
this->lookup_, this->answer_);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialization by the file descriptor
|
|
||||||
template<class UDPServerClass>
|
template<class UDPServerClass>
|
||||||
class FdInit : public DNSServerTestBase<UDPServerClass> {
|
class FdInit : public DNSServerTestBase<UDPServerClass> {
|
||||||
private:
|
private:
|
||||||
@@ -494,8 +479,7 @@ protected:
|
|||||||
template<class Parent>
|
template<class Parent>
|
||||||
class DNSServerTest : public Parent { };
|
class DNSServerTest : public Parent { };
|
||||||
|
|
||||||
typedef ::testing::Types<AddrPortInit<UDPServer>, AddrPortInit<SyncUDPServer>,
|
typedef ::testing::Types<FdInit<UDPServer>, FdInit<SyncUDPServer> >
|
||||||
FdInit<UDPServer>, FdInit<SyncUDPServer> >
|
|
||||||
ServerTypes;
|
ServerTypes;
|
||||||
TYPED_TEST_CASE(DNSServerTest, ServerTypes);
|
TYPED_TEST_CASE(DNSServerTest, ServerTypes);
|
||||||
|
|
||||||
@@ -507,12 +491,6 @@ bool DNSServerTestBase<UDPServerClass>::io_service_is_time_out = false;
|
|||||||
template<class UDPServerClass>
|
template<class UDPServerClass>
|
||||||
asio::io_service* DNSServerTestBase<UDPServerClass>::current_service(NULL);
|
asio::io_service* DNSServerTestBase<UDPServerClass>::current_service(NULL);
|
||||||
|
|
||||||
typedef ::testing::Types<AddrPortInit<SyncUDPServer>, FdInit<SyncUDPServer> >
|
|
||||||
SyncTypes;
|
|
||||||
template<class Parent>
|
|
||||||
class SyncServerTest : public Parent { };
|
|
||||||
TYPED_TEST_CASE(SyncServerTest, SyncTypes);
|
|
||||||
|
|
||||||
// Test whether server stopped successfully after client get response
|
// Test whether server stopped successfully after client get response
|
||||||
// client will send query and start to wait for response, once client
|
// 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
|
// get response, udp server will be stopped, the io service won't quit
|
||||||
@@ -558,7 +536,8 @@ TYPED_TEST(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
|
|||||||
EXPECT_TRUE(this->serverStopSucceed());
|
EXPECT_TRUE(this->serverStopSucceed());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stopServerManyTimes(DNSServer *server, unsigned int times) {
|
void
|
||||||
|
stopServerManyTimes(DNSServer *server, unsigned int times) {
|
||||||
for (unsigned int i = 0; i < times; ++i) {
|
for (unsigned int i = 0; i < times; ++i) {
|
||||||
server->stop();
|
server->stop();
|
||||||
}
|
}
|
||||||
@@ -680,18 +659,19 @@ TYPED_TEST(DNSServerTestBase, DISABLED_invalidUDPFD) {
|
|||||||
isc::asiolink::IOError);
|
isc::asiolink::IOError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check it rejects some of the unsupported operatirons
|
// A specialized test type for SyncUDPServer.
|
||||||
TYPED_TEST(SyncServerTest, unsupportedOps) {
|
typedef FdInit<SyncUDPServer> SyncServerTest;
|
||||||
EXPECT_THROW(this->udp_server_->clone(), isc::Unexpected);
|
|
||||||
EXPECT_THROW(this->udp_server_->asyncLookup(), isc::Unexpected);
|
// Check it rejects some of the unsupported operations
|
||||||
|
TEST_F(SyncServerTest, unsupportedOps) {
|
||||||
|
EXPECT_THROW(udp_server_->clone(), isc::Unexpected);
|
||||||
|
EXPECT_THROW(udp_server_->asyncLookup(), isc::Unexpected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check it rejects forgotten resume (eg. insists that it is synchronous)
|
// Check it rejects forgotten resume (eg. insists that it is synchronous)
|
||||||
TYPED_TEST(SyncServerTest, mustResume) {
|
TEST_F(SyncServerTest, mustResume) {
|
||||||
this->lookup_->allow_resume_ = false;
|
lookup_->allow_resume_ = false;
|
||||||
ASSERT_THROW(this->testStopServerByStopper(this->udp_server_,
|
ASSERT_THROW(testStopServerByStopper(udp_server_, udp_client_, lookup_),
|
||||||
this->udp_client_,
|
|
||||||
this->lookup_),
|
|
||||||
isc::Unexpected);
|
isc::Unexpected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,98 +39,7 @@ using boost::lexical_cast;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const char* const TEST_SERVER_PORT = "53535";
|
const char* const TEST_SERVER_PORT = "53535";
|
||||||
const char* const TEST_CLIENT_PORT = "53536";
|
|
||||||
const char* const TEST_IPV6_ADDR = "::1";
|
const char* const TEST_IPV6_ADDR = "::1";
|
||||||
const char* const TEST_IPV4_ADDR = "127.0.0.1";
|
|
||||||
|
|
||||||
TEST(IOServiceTest, badPort) {
|
|
||||||
IOService io_service;
|
|
||||||
EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
|
|
||||||
EXPECT_THROW(DNSService(io_service, *"53210.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) {
|
|
||||||
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(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.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(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(IOServiceTest, duplicateBind_v6) {
|
|
||||||
// In each sub test case, second attempt should fail due to duplicate bind
|
|
||||||
IOService io_service;
|
|
||||||
|
|
||||||
// IPv6, "any" address
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A simple lookup callback for DNS services. It records the pointer value of
|
// 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)
|
// to given output buffer each time the callback is called (up to two times)
|
||||||
|
@@ -182,12 +182,6 @@ struct UDPServer::Data {
|
|||||||
///
|
///
|
||||||
/// The constructor. It just creates new internal state object
|
/// The constructor. It just creates new internal state object
|
||||||
/// and lets it handle the initialization.
|
/// and lets it handle the initialization.
|
||||||
UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
|
|
||||||
const uint16_t port, SimpleCallback* checkin,
|
|
||||||
DNSLookup* lookup, DNSAnswer* answer) :
|
|
||||||
data_(new Data(io_service, addr, port, checkin, lookup, answer))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
UDPServer::UDPServer(io_service& io_service, int fd, int af,
|
UDPServer::UDPServer(io_service& io_service, int fd, int af,
|
||||||
SimpleCallback* checkin, DNSLookup* lookup,
|
SimpleCallback* checkin, DNSLookup* lookup,
|
||||||
DNSAnswer* answer) :
|
DNSAnswer* answer) :
|
||||||
|
@@ -39,19 +39,6 @@ namespace asiodns {
|
|||||||
///
|
///
|
||||||
class UDPServer : public virtual DNSServer, public virtual coroutine {
|
class UDPServer : public virtual DNSServer, public virtual coroutine {
|
||||||
public:
|
public:
|
||||||
/// \brief Constructor
|
|
||||||
/// \param io_service the asio::io_service to work with
|
|
||||||
/// \param addr the IP address to listen for queries on
|
|
||||||
/// \param port the port to listen for queries on
|
|
||||||
/// \param checkin the callbackprovider for non-DNS events
|
|
||||||
/// \param lookup the callbackprovider for DNS lookup events
|
|
||||||
/// \param answer the callbackprovider for DNS answer events
|
|
||||||
explicit UDPServer(asio::io_service& io_service,
|
|
||||||
const asio::ip::address& addr, const uint16_t port,
|
|
||||||
isc::asiolink::SimpleCallback* checkin = NULL,
|
|
||||||
DNSLookup* lookup = NULL,
|
|
||||||
DNSAnswer* answer = NULL);
|
|
||||||
|
|
||||||
/// \brief Constructor
|
/// \brief Constructor
|
||||||
/// \param io_service the asio::io_service to work with
|
/// \param io_service the asio::io_service to work with
|
||||||
/// \param fd the file descriptor of opened UDP socket
|
/// \param fd the file descriptor of opened UDP socket
|
||||||
|
@@ -18,10 +18,12 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
@@ -62,6 +64,7 @@ using namespace isc::asiodns;
|
|||||||
using namespace isc::asiolink;
|
using namespace isc::asiolink;
|
||||||
using namespace isc::dns;
|
using namespace isc::dns;
|
||||||
using namespace isc::util;
|
using namespace isc::util;
|
||||||
|
using boost::scoped_ptr;
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
namespace asiodns {
|
namespace asiodns {
|
||||||
@@ -85,18 +88,14 @@ const char* const TEST_IPV4_ADDR = "127.0.0.1";
|
|||||||
// for the tests below.
|
// for the tests below.
|
||||||
const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
|
const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
|
||||||
|
|
||||||
// This function returns an addrinfo structure for use by tests, using
|
// This function returns an addrinfo structure for use by tests.
|
||||||
// different addresses and ports depending on whether we're testing
|
|
||||||
// IPv4 or v6, TCP or UDP, and client or server operation.
|
|
||||||
struct addrinfo*
|
struct addrinfo*
|
||||||
resolveAddress(const int family, const int protocol, const bool client) {
|
resolveAddress(const int protocol, const char* const addr,
|
||||||
const char* const addr = (family == AF_INET6) ?
|
const char* const port)
|
||||||
TEST_IPV6_ADDR : TEST_IPV4_ADDR;
|
{
|
||||||
const char* const port = client ? TEST_CLIENT_PORT : TEST_SERVER_PORT;
|
|
||||||
|
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_family = family;
|
hints.ai_family = AF_UNSPEC; // let the address decide it.
|
||||||
hints.ai_socktype = (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
|
hints.ai_socktype = (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
|
||||||
hints.ai_protocol = protocol;
|
hints.ai_protocol = protocol;
|
||||||
hints.ai_flags = AI_NUMERICSERV;
|
hints.ai_flags = AI_NUMERICSERV;
|
||||||
@@ -110,6 +109,51 @@ resolveAddress(const int family, const int protocol, const bool client) {
|
|||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convenience shortcut of the other version using different addresses and
|
||||||
|
// ports depending on whether we're testing IPv4 or v6, TCP or UDP, and
|
||||||
|
// client or server operation.
|
||||||
|
struct addrinfo*
|
||||||
|
resolveAddress(const int family, const int protocol, const bool client) {
|
||||||
|
return (resolveAddress(protocol,
|
||||||
|
(family == AF_INET6) ? TEST_IPV6_ADDR :
|
||||||
|
TEST_IPV4_ADDR,
|
||||||
|
client ? TEST_CLIENT_PORT : TEST_SERVER_PORT));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper holder of addrinfo so we can safely release the resource
|
||||||
|
// either when leaving the defined scope either normally or due to exception.
|
||||||
|
struct ScopedAddrInfo {
|
||||||
|
ScopedAddrInfo(struct addrinfo* res) : res_(res) {}
|
||||||
|
~ScopedAddrInfo() { freeaddrinfo(res_); }
|
||||||
|
struct addrinfo* res_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Similar to ScopedAddrInfo but for socket FD. It also supports the "release"
|
||||||
|
// operation so it can release the ownership of the FD.
|
||||||
|
// This is made non copyable to avoid making an accidental copy, which could
|
||||||
|
// result in duplicate close.
|
||||||
|
struct ScopedSocket : private boost::noncopyable {
|
||||||
|
ScopedSocket() : s_(-1) {}
|
||||||
|
ScopedSocket(int s) : s_(s) {}
|
||||||
|
~ScopedSocket() {
|
||||||
|
if (s_ >= 0) {
|
||||||
|
close(s_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void reset(int new_s) {
|
||||||
|
if (s_ >= 0) {
|
||||||
|
close(s_);
|
||||||
|
}
|
||||||
|
s_ = new_s;
|
||||||
|
}
|
||||||
|
int release() {
|
||||||
|
int s = s_;
|
||||||
|
s_ = -1;
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
int s_;
|
||||||
|
};
|
||||||
|
|
||||||
// This fixture is a framework for various types of network operations
|
// This fixture is a framework for various types of network operations
|
||||||
// using the ASIO interfaces. Each test case creates an IOService object,
|
// using the ASIO interfaces. Each test case creates an IOService object,
|
||||||
// opens a local "client" socket for testing, sends data via the local socket
|
// opens a local "client" socket for testing, sends data via the local socket
|
||||||
@@ -129,27 +173,20 @@ protected:
|
|||||||
// It would delete itself, but after the io_service_, which could
|
// It would delete itself, but after the io_service_, which could
|
||||||
// segfailt in case there were unhandled requests
|
// segfailt in case there were unhandled requests
|
||||||
resolver_.reset();
|
resolver_.reset();
|
||||||
if (res_ != NULL) {
|
|
||||||
freeaddrinfo(res_);
|
|
||||||
}
|
|
||||||
if (sock_ != -1) {
|
|
||||||
close(sock_);
|
|
||||||
}
|
|
||||||
delete dns_service_;
|
|
||||||
delete callback_;
|
|
||||||
delete io_service_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a test UDP packet to a mock server
|
// Send a test UDP packet to a mock server
|
||||||
void sendUDP(const int family) {
|
void sendUDP(const int family) {
|
||||||
res_ = resolveAddress(family, IPPROTO_UDP, false);
|
ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, false));
|
||||||
|
struct addrinfo* res = sai.res_;
|
||||||
|
|
||||||
sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
|
sock_.reset(socket(res->ai_family, res->ai_socktype,
|
||||||
if (sock_ < 0) {
|
res->ai_protocol));
|
||||||
|
if (sock_.s_ < 0) {
|
||||||
isc_throw(IOError, "failed to open test socket");
|
isc_throw(IOError, "failed to open test socket");
|
||||||
}
|
}
|
||||||
const int cc = sendto(sock_, test_data, sizeof(test_data), 0,
|
const int cc = sendto(sock_.s_, test_data, sizeof(test_data), 0,
|
||||||
res_->ai_addr, res_->ai_addrlen);
|
res->ai_addr, res->ai_addrlen);
|
||||||
if (cc != sizeof(test_data)) {
|
if (cc != sizeof(test_data)) {
|
||||||
isc_throw(IOError, "unexpected sendto result: " << cc);
|
isc_throw(IOError, "unexpected sendto result: " << cc);
|
||||||
}
|
}
|
||||||
@@ -158,16 +195,18 @@ protected:
|
|||||||
|
|
||||||
// Send a test TCP packet to a mock server
|
// Send a test TCP packet to a mock server
|
||||||
void sendTCP(const int family) {
|
void sendTCP(const int family) {
|
||||||
res_ = resolveAddress(family, IPPROTO_TCP, false);
|
ScopedAddrInfo sai(resolveAddress(family, IPPROTO_TCP, false));
|
||||||
|
struct addrinfo* res = sai.res_;
|
||||||
|
|
||||||
sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
|
sock_.reset(socket(res->ai_family, res->ai_socktype,
|
||||||
if (sock_ < 0) {
|
res->ai_protocol));
|
||||||
|
if (sock_.s_ < 0) {
|
||||||
isc_throw(IOError, "failed to open test socket");
|
isc_throw(IOError, "failed to open test socket");
|
||||||
}
|
}
|
||||||
if (connect(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
|
if (connect(sock_.s_, res->ai_addr, res->ai_addrlen) < 0) {
|
||||||
isc_throw(IOError, "failed to connect to the test server");
|
isc_throw(IOError, "failed to connect to the test server");
|
||||||
}
|
}
|
||||||
const int cc = send(sock_, test_data, sizeof(test_data), 0);
|
const int cc = send(sock_.s_, test_data, sizeof(test_data), 0);
|
||||||
if (cc != sizeof(test_data)) {
|
if (cc != sizeof(test_data)) {
|
||||||
isc_throw(IOError, "unexpected send result: " << cc);
|
isc_throw(IOError, "unexpected send result: " << cc);
|
||||||
}
|
}
|
||||||
@@ -178,14 +217,16 @@ protected:
|
|||||||
// recursive lookup. The caller must place a RecursiveQuery
|
// recursive lookup. The caller must place a RecursiveQuery
|
||||||
// on the IO Service queue before running this routine.
|
// on the IO Service queue before running this routine.
|
||||||
void recvUDP(const int family, void* buffer, size_t& size) {
|
void recvUDP(const int family, void* buffer, size_t& size) {
|
||||||
res_ = resolveAddress(family, IPPROTO_UDP, true);
|
ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, true));
|
||||||
|
struct addrinfo* res = sai.res_;
|
||||||
|
|
||||||
sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
|
sock_.reset(socket(res->ai_family, res->ai_socktype,
|
||||||
if (sock_ < 0) {
|
res->ai_protocol));
|
||||||
|
if (sock_.s_ < 0) {
|
||||||
isc_throw(IOError, "failed to open test socket");
|
isc_throw(IOError, "failed to open test socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bind(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
|
if (bind(sock_.s_, res->ai_addr, res->ai_addrlen) < 0) {
|
||||||
isc_throw(IOError, "bind failed: " << strerror(errno));
|
isc_throw(IOError, "bind failed: " << strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +246,7 @@ protected:
|
|||||||
// we add an ad hoc timeout.
|
// we add an ad hoc timeout.
|
||||||
const struct timeval timeo = { 10, 0 };
|
const struct timeval timeo = { 10, 0 };
|
||||||
int recv_options = 0;
|
int recv_options = 0;
|
||||||
if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
|
if (setsockopt(sock_.s_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
|
||||||
sizeof(timeo))) {
|
sizeof(timeo))) {
|
||||||
if (errno == ENOPROTOOPT) {
|
if (errno == ENOPROTOOPT) {
|
||||||
// Workaround for Solaris: it doesn't accept SO_RCVTIMEO
|
// Workaround for Solaris: it doesn't accept SO_RCVTIMEO
|
||||||
@@ -218,7 +259,7 @@ protected:
|
|||||||
isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
|
isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const int ret = recv(sock_, buffer, size, recv_options);
|
const int ret = recv(sock_.s_, buffer, size, recv_options);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
isc_throw(IOError, "recvfrom failed: " << strerror(errno));
|
isc_throw(IOError, "recvfrom failed: " << strerror(errno));
|
||||||
}
|
}
|
||||||
@@ -227,38 +268,68 @@ protected:
|
|||||||
size = ret;
|
size = ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
addServer(const string& address, const char* const port, int protocol) {
|
||||||
|
ScopedAddrInfo sai(resolveAddress(protocol, address.c_str(), port));
|
||||||
|
struct addrinfo* res = sai.res_;
|
||||||
|
const int family = res->ai_family;
|
||||||
|
|
||||||
|
ScopedSocket sock(socket(res->ai_family, res->ai_socktype,
|
||||||
|
res->ai_protocol));
|
||||||
|
const int s = sock.s_;
|
||||||
|
if (s < 0) {
|
||||||
|
isc_throw(isc::Unexpected, "failed to open a test socket");
|
||||||
|
}
|
||||||
|
const int on = 1;
|
||||||
|
if (family == AF_INET6) {
|
||||||
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) ==
|
||||||
|
-1) {
|
||||||
|
isc_throw(isc::Unexpected,
|
||||||
|
"failed to set socket option(IPV6_V6ONLY)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||||
|
isc_throw(isc::Unexpected,
|
||||||
|
"failed to set socket option(SO_REUSEADDR)");
|
||||||
|
}
|
||||||
|
if (bind(s, res->ai_addr, res->ai_addrlen) != 0) {
|
||||||
|
isc_throw(isc::Unexpected, "failed to bind a test socket");
|
||||||
|
}
|
||||||
|
if (protocol == IPPROTO_TCP) {
|
||||||
|
dns_service_->addServerTCPFromFD(sock.release(), family);
|
||||||
|
} else {
|
||||||
|
dns_service_->addServerUDPFromFD(sock.release(), family);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set up an IO Service queue using the specified address
|
// Set up an IO Service queue using the specified address
|
||||||
void setDNSService(const char& address) {
|
void setDNSService(const string& address) {
|
||||||
delete dns_service_;
|
setDNSService();
|
||||||
dns_service_ = NULL;
|
addServer(address, TEST_SERVER_PORT, IPPROTO_TCP);
|
||||||
delete io_service_;
|
addServer(address, TEST_SERVER_PORT, IPPROTO_UDP);
|
||||||
io_service_ = new IOService();
|
|
||||||
callback_ = new ASIOCallBack(this);
|
|
||||||
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
|
// 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.
|
// 'use_ipv4' is true and on IPv6 if 'use_ipv6' is true.
|
||||||
void setDNSService(const bool use_ipv4, const bool use_ipv6) {
|
void setDNSService(const bool use_ipv4, const bool use_ipv6) {
|
||||||
delete dns_service_;
|
setDNSService();
|
||||||
dns_service_ = NULL;
|
if (use_ipv6) {
|
||||||
delete io_service_;
|
addServer("::", TEST_SERVER_PORT, IPPROTO_TCP);
|
||||||
io_service_ = new IOService();
|
addServer("::", TEST_SERVER_PORT, IPPROTO_UDP);
|
||||||
callback_ = new ASIOCallBack(this);
|
}
|
||||||
dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, use_ipv4, use_ipv6, callback_,
|
if (use_ipv4) {
|
||||||
NULL, NULL);
|
addServer("0.0.0.0", TEST_SERVER_PORT, IPPROTO_TCP);
|
||||||
|
addServer("0.0.0.0", TEST_SERVER_PORT, IPPROTO_UDP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up empty DNS Service
|
// Set up empty DNS Service
|
||||||
// Set up an IO Service queue without any addresses
|
// Set up an IO Service queue without any addresses
|
||||||
void setDNSService() {
|
void setDNSService() {
|
||||||
delete dns_service_;
|
io_service_.reset(new IOService());
|
||||||
dns_service_ = NULL;
|
callback_.reset(new ASIOCallBack(this));
|
||||||
delete io_service_;
|
dns_service_.reset(new DNSService(*io_service_, callback_.get(), NULL,
|
||||||
io_service_ = new IOService();
|
NULL));
|
||||||
callback_ = new ASIOCallBack(this);
|
|
||||||
dns_service_ = new DNSService(*io_service_, callback_, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run a simple server test, on either IPv4 or IPv6, and over either
|
// Run a simple server test, on either IPv4 or IPv6, and over either
|
||||||
@@ -277,7 +348,7 @@ protected:
|
|||||||
// There doesn't seem to be an effective test for the validity of
|
// There doesn't seem to be an effective test for the validity of
|
||||||
// 'native'.
|
// 'native'.
|
||||||
// One thing we are sure is it must be different from our local socket.
|
// One thing we are sure is it must be different from our local socket.
|
||||||
EXPECT_NE(sock_, callback_native_);
|
EXPECT_NE(sock_.s_, callback_native_);
|
||||||
EXPECT_EQ(protocol, callback_protocol_);
|
EXPECT_EQ(protocol, callback_protocol_);
|
||||||
EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
|
EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
|
||||||
callback_address_);
|
callback_address_);
|
||||||
@@ -425,28 +496,26 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
// We use a pointer for io_service_, because for some tests we
|
// We use a pointer for io_service_, because for some tests we
|
||||||
// need to recreate a new one within one onstance of this class
|
// need to recreate a new one within one onstance of this class
|
||||||
IOService* io_service_;
|
scoped_ptr<IOService> io_service_;
|
||||||
DNSService* dns_service_;
|
scoped_ptr<DNSService> dns_service_;
|
||||||
isc::nsas::NameserverAddressStore* nsas_;
|
scoped_ptr<isc::nsas::NameserverAddressStore> nsas_;
|
||||||
isc::cache::ResolverCache cache_;
|
isc::cache::ResolverCache cache_;
|
||||||
ASIOCallBack* callback_;
|
scoped_ptr<ASIOCallBack> callback_;
|
||||||
int callback_protocol_;
|
int callback_protocol_;
|
||||||
int callback_native_;
|
int callback_native_;
|
||||||
string callback_address_;
|
string callback_address_;
|
||||||
vector<uint8_t> callback_data_;
|
vector<uint8_t> callback_data_;
|
||||||
int sock_;
|
ScopedSocket sock_;
|
||||||
struct addrinfo* res_;
|
|
||||||
boost::shared_ptr<isc::util::unittests::TestResolver> resolver_;
|
boost::shared_ptr<isc::util::unittests::TestResolver> resolver_;
|
||||||
};
|
};
|
||||||
|
|
||||||
RecursiveQueryTest::RecursiveQueryTest() :
|
RecursiveQueryTest::RecursiveQueryTest() :
|
||||||
dns_service_(NULL), callback_(NULL), callback_protocol_(0),
|
dns_service_(NULL), callback_(NULL), callback_protocol_(0),
|
||||||
callback_native_(-1), sock_(-1), res_(NULL),
|
callback_native_(-1), resolver_(new isc::util::unittests::TestResolver())
|
||||||
resolver_(new isc::util::unittests::TestResolver())
|
|
||||||
{
|
{
|
||||||
io_service_ = new IOService();
|
io_service_.reset(new IOService());
|
||||||
setDNSService(true, true);
|
setDNSService(true, true);
|
||||||
nsas_ = new isc::nsas::NameserverAddressStore(resolver_);
|
nsas_.reset(new isc::nsas::NameserverAddressStore(resolver_));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RecursiveQueryTest, v6UDPSend) {
|
TEST_F(RecursiveQueryTest, v6UDPSend) {
|
||||||
@@ -477,24 +546,24 @@ TEST_F(RecursiveQueryTest, v6UDPSendSpecific) {
|
|||||||
// an error on a subsequent read operation. We could do it, but for
|
// an error on a subsequent read operation. We could do it, but for
|
||||||
// simplicity we only tests the easier cases for now.
|
// simplicity we only tests the easier cases for now.
|
||||||
|
|
||||||
setDNSService(*TEST_IPV6_ADDR);
|
setDNSService(TEST_IPV6_ADDR);
|
||||||
doTest(AF_INET6, IPPROTO_UDP);
|
doTest(AF_INET6, IPPROTO_UDP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RecursiveQueryTest, v6TCPSendSpecific) {
|
TEST_F(RecursiveQueryTest, v6TCPSendSpecific) {
|
||||||
setDNSService(*TEST_IPV6_ADDR);
|
setDNSService(TEST_IPV6_ADDR);
|
||||||
doTest(AF_INET6, IPPROTO_TCP);
|
doTest(AF_INET6, IPPROTO_TCP);
|
||||||
|
|
||||||
EXPECT_THROW(sendTCP(AF_INET), IOError);
|
EXPECT_THROW(sendTCP(AF_INET), IOError);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RecursiveQueryTest, v4UDPSendSpecific) {
|
TEST_F(RecursiveQueryTest, v4UDPSendSpecific) {
|
||||||
setDNSService(*TEST_IPV4_ADDR);
|
setDNSService(TEST_IPV4_ADDR);
|
||||||
doTest(AF_INET, IPPROTO_UDP);
|
doTest(AF_INET, IPPROTO_UDP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
|
TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
|
||||||
setDNSService(*TEST_IPV4_ADDR);
|
setDNSService(TEST_IPV4_ADDR);
|
||||||
doTest(AF_INET, IPPROTO_TCP);
|
doTest(AF_INET, IPPROTO_TCP);
|
||||||
|
|
||||||
EXPECT_THROW(sendTCP(AF_INET6), IOError);
|
EXPECT_THROW(sendTCP(AF_INET6), IOError);
|
||||||
@@ -502,7 +571,7 @@ TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
|
|||||||
|
|
||||||
TEST_F(RecursiveQueryTest, v6AddServer) {
|
TEST_F(RecursiveQueryTest, v6AddServer) {
|
||||||
setDNSService();
|
setDNSService();
|
||||||
dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
|
addServer(TEST_IPV6_ADDR, TEST_SERVER_PORT, IPPROTO_TCP);
|
||||||
doTest(AF_INET6, IPPROTO_TCP);
|
doTest(AF_INET6, IPPROTO_TCP);
|
||||||
|
|
||||||
EXPECT_THROW(sendTCP(AF_INET), IOError);
|
EXPECT_THROW(sendTCP(AF_INET), IOError);
|
||||||
@@ -510,7 +579,7 @@ TEST_F(RecursiveQueryTest, v6AddServer) {
|
|||||||
|
|
||||||
TEST_F(RecursiveQueryTest, v4AddServer) {
|
TEST_F(RecursiveQueryTest, v4AddServer) {
|
||||||
setDNSService();
|
setDNSService();
|
||||||
dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
|
addServer(TEST_IPV4_ADDR, TEST_SERVER_PORT, IPPROTO_TCP);
|
||||||
doTest(AF_INET, IPPROTO_TCP);
|
doTest(AF_INET, IPPROTO_TCP);
|
||||||
|
|
||||||
EXPECT_THROW(sendTCP(AF_INET6), IOError);
|
EXPECT_THROW(sendTCP(AF_INET6), IOError);
|
||||||
@@ -607,41 +676,43 @@ TEST_F(RecursiveQueryTest, forwarderSend) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
createTestSocket()
|
createTestSocket() {
|
||||||
{
|
ScopedAddrInfo sai(resolveAddress(AF_INET, IPPROTO_UDP, true));
|
||||||
struct addrinfo* res_ = resolveAddress(AF_INET, IPPROTO_UDP, true);
|
struct addrinfo* res = sai.res_;
|
||||||
int sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
|
|
||||||
if (sock_ < 0) {
|
ScopedSocket sock(socket(res->ai_family, res->ai_socktype,
|
||||||
|
res->ai_protocol));
|
||||||
|
if (sock.s_ < 0) {
|
||||||
isc_throw(IOError, "failed to open test socket");
|
isc_throw(IOError, "failed to open test socket");
|
||||||
}
|
}
|
||||||
if (bind(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
|
if (bind(sock.s_, res->ai_addr, res->ai_addrlen) < 0) {
|
||||||
isc_throw(IOError, "failed to bind test socket");
|
isc_throw(IOError, "failed to bind test socket");
|
||||||
}
|
}
|
||||||
return sock_;
|
return (sock.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
setSocketTimeout(int sock_, size_t tv_sec, size_t tv_usec) {
|
setSocketTimeout(int sock, size_t tv_sec, size_t tv_usec) {
|
||||||
const struct timeval timeo = { tv_sec, tv_usec };
|
const struct timeval timeo = { tv_sec, tv_usec };
|
||||||
int recv_options = 0;
|
int recv_options = 0;
|
||||||
if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
|
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
|
||||||
if (errno == ENOPROTOOPT) { // see RecursiveQueryTest::recvUDP()
|
if (errno == ENOPROTOOPT) { // see RecursiveQueryTest::recvUDP()
|
||||||
recv_options = MSG_DONTWAIT;
|
recv_options = MSG_DONTWAIT;
|
||||||
} else {
|
} else {
|
||||||
isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
|
isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return recv_options;
|
return (recv_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to read from the socket max time
|
// try to read from the socket max time
|
||||||
// *num is incremented for every succesfull read
|
// *num is incremented for every succesfull read
|
||||||
// returns true if it can read max times, false otherwise
|
// returns true if it can read max times, false otherwise
|
||||||
bool tryRead(int sock_, int recv_options, size_t max, int* num) {
|
bool tryRead(int sock, int recv_options, size_t max, int* num) {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
do {
|
do {
|
||||||
char inbuff[512];
|
char inbuff[512];
|
||||||
if (recv(sock_, inbuff, sizeof(inbuff), recv_options) < 0) {
|
if (recv(sock, inbuff, sizeof(inbuff), recv_options) < 0) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
++i;
|
++i;
|
||||||
@@ -691,7 +762,7 @@ TEST_F(RecursiveQueryTest, forwardQueryTimeout) {
|
|||||||
setDNSService();
|
setDNSService();
|
||||||
|
|
||||||
// Prepare the socket
|
// Prepare the socket
|
||||||
sock_ = createTestSocket();
|
sock_.reset(createTestSocket());
|
||||||
|
|
||||||
// Prepare the server
|
// Prepare the server
|
||||||
bool done(true);
|
bool done(true);
|
||||||
@@ -725,7 +796,7 @@ TEST_F(RecursiveQueryTest, forwardClientTimeout) {
|
|||||||
// Prepare the service (we do not use the common setup, we do not answer
|
// Prepare the service (we do not use the common setup, we do not answer
|
||||||
setDNSService();
|
setDNSService();
|
||||||
|
|
||||||
sock_ = createTestSocket();
|
sock_.reset(createTestSocket());
|
||||||
|
|
||||||
// Prepare the server
|
// Prepare the server
|
||||||
bool done1(true);
|
bool done1(true);
|
||||||
@@ -759,7 +830,7 @@ TEST_F(RecursiveQueryTest, forwardLookupTimeout) {
|
|||||||
setDNSService();
|
setDNSService();
|
||||||
|
|
||||||
// Prepare the socket
|
// Prepare the socket
|
||||||
sock_ = createTestSocket();
|
sock_.reset(createTestSocket());
|
||||||
|
|
||||||
// Prepare the server
|
// Prepare the server
|
||||||
bool done(true);
|
bool done(true);
|
||||||
@@ -794,7 +865,7 @@ TEST_F(RecursiveQueryTest, lowtimeouts) {
|
|||||||
setDNSService();
|
setDNSService();
|
||||||
|
|
||||||
// Prepare the socket
|
// Prepare the socket
|
||||||
sock_ = createTestSocket();
|
sock_.reset(createTestSocket());
|
||||||
|
|
||||||
// Prepare the server
|
// Prepare the server
|
||||||
bool done(true);
|
bool done(true);
|
||||||
|
Reference in New Issue
Block a user