Browse Source

[master] Merge branch 'trac1784' with fixing conflicts.

JINMEI Tatuya 13 years ago
parent
commit
4de96e9a32

+ 11 - 0
ChangeLog

@@ -1,3 +1,14 @@
+405.?	[bug]		jinmei
+	Make sure disabling Boost threads if the default configuration is
+	to disable it for the system.  This fixes a crash and hang up
+	problem on OpenBSD, where the use of Boost thread could be
+	different in different program files depending on the order of
+	including various header files, and could introduce inconsistent
+	states between a library and a program.  Explicitly forcing the
+	original default throughout the BIND 10 build environment will
+	prevent this from happening.
+	(Trac #1727, git TBD)
+
 404.	[bug]		naokikambe
 	The statistic counters are now properly accumulated across multiple
 	instances of b10-auth (if there are multiple instances), instead of

+ 7 - 2
src/bin/auth/auth_srv.cc

@@ -47,6 +47,8 @@
 #include <dns/message.h>
 #include <dns/tsig.h>
 
+#include <asiodns/dns_service.h>
+
 #include <datasrc/query.h>
 #include <datasrc/data_source.h>
 #include <datasrc/memory_datasrc.h>
@@ -866,11 +868,14 @@ AuthSrv::getListenAddresses() const {
 
 void
 AuthSrv::setListenAddresses(const AddressList& addresses) {
-    installListenAddresses(addresses, impl_->listen_addresses_, *dnss_);
+    // For UDP servers we specify the "SYNC_OK" option because in our usage
+    // it can act in the synchronous mode.
+    installListenAddresses(addresses, impl_->listen_addresses_, *dnss_,
+                           DNSService::SERVER_SYNC_OK);
 }
 
 void
-AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
+AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
     dnss_ = &dnss;
 }
 

+ 3 - 2
src/bin/auth/auth_srv.h

@@ -28,6 +28,7 @@
 #include <util/buffer.h>
 
 #include <asiodns/dns_server.h>
+#include <asiodns/dns_service.h>
 #include <asiodns/dns_lookup.h>
 #include <asiodns/dns_answer.h>
 #include <asiolink/io_message.h>
@@ -384,7 +385,7 @@ public:
         const;
 
     /// \brief Assign an ASIO DNS Service queue to this Auth object
-    void setDNSService(isc::asiodns::DNSService& dnss);
+    void setDNSService(isc::asiodns::DNSServiceBase& dnss);
 
     /// \brief Sets the keyring used for verifying and signing
     ///
@@ -400,7 +401,7 @@ private:
     isc::asiolink::SimpleCallback* checkin_;
     isc::asiodns::DNSLookup* dns_lookup_;
     isc::asiodns::DNSAnswer* dns_answer_;
-    isc::asiodns::DNSService* dnss_;
+    isc::asiodns::DNSServiceBase* dnss_;
 };
 
 #endif // __AUTH_SRV_H

+ 11 - 8
src/bin/auth/tests/auth_srv_unittest.cc

@@ -41,6 +41,7 @@
 #include <dns/tests/unittest_util.h>
 #include <testutils/dnsmessage_test.h>
 #include <testutils/srv_test.h>
+#include <testutils/mockups.h>
 #include <testutils/portconfig.h>
 #include <testutils/socket_request.h>
 
@@ -75,7 +76,7 @@ const char* const CONFIG_INMEMORY_EXAMPLE =
 class AuthSrvTest : public SrvTestBase {
 protected:
     AuthSrvTest() :
-        dnss_(ios_, NULL, NULL, NULL),
+        dnss_(),
         server(true, xfrout),
         rrclass(RRClass::IN()),
         // The empty string is expected value of the parameter of
@@ -135,8 +136,7 @@ protected:
                     opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
     }
 
-    IOService ios_;
-    DNSService dnss_;
+    MockDNSService dnss_;
     MockSession statistics_session;
     MockXfroutClient xfrout;
     AuthSrv server;
@@ -1079,10 +1079,11 @@ TEST_F(AuthSrvTest, processNormalQuery_reuseRenderer1) {
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
                                        RRClass::IN(), RRType::NS());
-    
+
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, *parse_message, *response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_NE(request_message.getRcode(), parse_message->getRcode());
 }
 
@@ -1090,12 +1091,14 @@ TEST_F(AuthSrvTest, processNormalQuery_reuseRenderer2) {
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
                                        RRClass::IN(), RRType::SOA());
-    
+
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, *parse_message, *response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     ConstQuestionPtr question = *parse_message->beginQuestion();
-    EXPECT_STRNE(question->getType().toText().c_str(),RRType::NS().toText().c_str());
+    EXPECT_STRNE(question->getType().toText().c_str(),
+                 RRType::NS().toText().c_str());
 }
 //
 // Tests for catching exceptions in various stages of the query processing

+ 1 - 0
src/bin/auth/tests/command_unittest.cc

@@ -49,6 +49,7 @@ using namespace isc::dns;
 using namespace isc::data;
 using namespace isc::datasrc;
 using namespace isc::config;
+using namespace isc::testutils;
 
 namespace {
 class AuthCommandTest : public ::testing::Test {

+ 11 - 4
src/bin/auth/tests/config_unittest.cc

@@ -37,13 +37,13 @@ using namespace isc::dns;
 using namespace isc::data;
 using namespace isc::datasrc;
 using namespace isc::asiodns;
-using namespace isc::asiolink;
+using namespace isc::testutils;
 
 namespace {
 class AuthConfigTest : public ::testing::Test {
 protected:
     AuthConfigTest() :
-        dnss_(ios_, NULL, NULL, NULL),
+        dnss_(),
         rrclass(RRClass::IN()),
         server(true, xfrout),
         // The empty string is expected value of the parameter of
@@ -53,8 +53,7 @@ protected:
     {
         server.setDNSService(dnss_);
     }
-    IOService ios_;
-    DNSService dnss_;
+    MockDNSService dnss_;
     const RRClass rrclass;
     MockXfroutClient xfrout;
     AuthSrv server;
@@ -146,6 +145,14 @@ TEST_F(AuthConfigTest, invalidListenAddressConfig) {
 // Try setting addresses trough config
 TEST_F(AuthConfigTest, listenAddressConfig) {
     isc::testutils::portconfig::listenAddressConfig(server);
+
+    // listenAddressConfig should have attempted to create 4 DNS server
+    // objects: two IP addresses, TCP and UDP for each.  For UDP, the "SYNC_OK"
+    // option should have been specified.
+    EXPECT_EQ(2, dnss_.getTCPFdParams().size());
+    EXPECT_EQ(2, dnss_.getUDPFdParams().size());
+    EXPECT_EQ(DNSService::SERVER_SYNC_OK, dnss_.getUDPFdParams().at(0).options);
+    EXPECT_EQ(DNSService::SERVER_SYNC_OK, dnss_.getUDPFdParams().at(1).options);
 }
 
 class MemoryDatasrcConfigTest : public AuthConfigTest {

+ 6 - 6
src/bin/resolver/resolver.cc

@@ -92,7 +92,7 @@ public:
         queryShutdown();
     }
 
-    void querySetup(DNSService& dnss,
+    void querySetup(DNSServiceBase& dnss,
                     isc::nsas::NameserverAddressStore& nsas,
                     isc::cache::ResolverCache& cache)
     {
@@ -121,10 +121,10 @@ public:
     }
 
     void setForwardAddresses(const AddressList& upstream,
-        DNSService *dnss)
+                             DNSServiceBase* dnss)
     {
         upstream_ = upstream;
-        if (dnss) {
+        if (dnss != NULL) {
             if (!upstream_.empty()) {
                 BOOST_FOREACH(const AddressPair& address, upstream) {
                     LOG_INFO(resolver_logger, RESOLVER_FORWARD_ADDRESS)
@@ -137,10 +137,10 @@ public:
     }
 
     void setRootAddresses(const AddressList& upstream_root,
-                          DNSService *dnss)
+                          DNSServiceBase* dnss)
     {
         upstream_root_ = upstream_root;
-        if (dnss) {
+        if (dnss != NULL) {
             if (!upstream_root_.empty()) {
                 BOOST_FOREACH(const AddressPair& address, upstream_root) {
                     LOG_INFO(resolver_logger, RESOLVER_SET_ROOT_ADDRESS)
@@ -377,7 +377,7 @@ Resolver::~Resolver() {
 }
 
 void
-Resolver::setDNSService(isc::asiodns::DNSService& dnss) {
+Resolver::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
     dnss_ = &dnss;
 }
 

+ 3 - 3
src/bin/resolver/resolver.h

@@ -104,7 +104,7 @@ public:
                                             bool startup = false);
 
     /// \brief Assign an ASIO IO Service queue to this Resolver object
-    void setDNSService(isc::asiodns::DNSService& dnss);
+    void setDNSService(isc::asiodns::DNSServiceBase& dnss);
 
     /// \brief Assign a NameserverAddressStore to this Resolver object
     void setNameserverAddressStore(isc::nsas::NameserverAddressStore &nsas);
@@ -113,7 +113,7 @@ public:
     void setCache(isc::cache::ResolverCache& cache);
 
     /// \brief Return this object's ASIO IO Service queue
-    isc::asiodns::DNSService& getDNSService() const { return (*dnss_); }
+    isc::asiodns::DNSServiceBase& getDNSService() const { return (*dnss_); }
 
     /// \brief Returns this object's NSAS
     isc::nsas::NameserverAddressStore& getNameserverAddressStore() const {
@@ -258,7 +258,7 @@ public:
 
 private:
     ResolverImpl* impl_;
-    isc::asiodns::DNSService* dnss_;
+    isc::asiodns::DNSServiceBase* dnss_;
     isc::asiolink::SimpleCallback* checkin_;
     isc::asiodns::DNSLookup* dns_lookup_;
     isc::asiodns::DNSAnswer* dns_answer_;

+ 11 - 3
src/bin/resolver/tests/resolver_config_unittest.cc

@@ -48,6 +48,7 @@
 
 #include <dns/tests/unittest_util.h>
 #include <testutils/srv_test.h>
+#include <testutils/mockups.h>
 #include <testutils/portconfig.h>
 #include <testutils/socket_request.h>
 
@@ -76,15 +77,13 @@ public:
 
 class ResolverConfig : public ::testing::Test {
 protected:
-    IOService ios;
-    DNSService dnss;
+    MockDNSService dnss;
     Resolver server;
     scoped_ptr<const IOEndpoint> endpoint;
     scoped_ptr<const IOMessage> query_message;
     scoped_ptr<const Client> client;
     scoped_ptr<const RequestContext> request;
     ResolverConfig() :
-        dnss(ios, NULL, NULL, NULL),
         // The empty string is expected value of the parameter of
         // requestSocket, not the app_name (there's no fallback, it checks
         // the empty string is passed).
@@ -320,6 +319,15 @@ TEST_F(ResolverConfig, invalidForwardAddresses) {
 // Try setting the addresses directly
 TEST_F(ResolverConfig, listenAddresses) {
     isc::testutils::portconfig::listenAddresses(server);
+
+    // listenAddressConfig should have attempted to create 4 DNS server
+    // objects: two IP addresses, TCP and UDP for each.  For UDP, the "SYNC_OK"
+    // option (or anything else) should have NOT been specified.
+    EXPECT_EQ(2, dnss.getTCPFdParams().size());
+    EXPECT_EQ(2, dnss.getUDPFdParams().size());
+    EXPECT_EQ(DNSService::SERVER_DEFAULT, dnss.getUDPFdParams().at(0).options);
+    EXPECT_EQ(DNSService::SERVER_DEFAULT, dnss.getUDPFdParams().at(1).options);
+
     // Check it requests the correct addresses
     const char* tokens[] = {
         "TCP:127.0.0.1:53210:1",

+ 49 - 58
src/lib/asiodns/dns_service.cc

@@ -22,6 +22,8 @@
 
 #include <log/dummylog.h>
 
+#include <exceptions/exceptions.h>
+
 #include <asio.hpp>
 #include <dns_service.h>
 #include <asiolink/io_service.h>
@@ -63,11 +65,10 @@ convertAddr(const std::string& address) {
 class DNSServiceImpl {
 public:
     DNSServiceImpl(IOService& io_service, const char& port,
-                  const asio::ip::address* v4addr,
-                  const asio::ip::address* v6addr,
-                  SimpleCallback* checkin, DNSLookup* lookup,
-                  DNSAnswer* answer,
-                  const UDPVersion param_flags);
+                   const asio::ip::address* v4addr,
+                   const asio::ip::address* v6addr,
+                   SimpleCallback* checkin, DNSLookup* lookup,
+                   DNSAnswer* answe);
 
     IOService& io_service_;
 
@@ -87,38 +88,24 @@ public:
         servers_.push_back(server);
     }
 
-    void addServer(uint16_t port, const asio::ip::address& address,
-                   const UDPVersion param_flags) {
+    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));
+            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_));
+                                                 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));
-            // Use param_flags to generate diff UDPServers.    
-            switch(param_flags) {
-                case SYNC_: {
-                    SyncUDPServerPtr syncUdpServer(new SyncUDPServer(io_service_.get_io_service(),
-                                                   address, port, checkin_, lookup_, answer_));
-                    (*syncUdpServer)();
-                    servers_.push_back(syncUdpServer);
-                    break;
-                }
-                case ASYNC_: {
-                    UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
-                                           address, port, checkin_, lookup_, answer_));
-                    (*udpServer)();
-                    servers_.push_back(udpServer);
-                    break;
-                }
-                default:
-                    // If nerther asyn UDPServer nor sync UDNServer, it throws.
-                    isc_throw(IOError, "Bad UDPServer Version!");
-                    break;
-            }
-        }
-        catch (const asio::system_error& err) {
+            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.
@@ -126,8 +113,7 @@ public:
                       err.what());
         }
     }
-    void addServer(const char& port, const asio::ip::address& address,
-                   const UDPVersion param_flags) {
+    void addServer(const char& port, const asio::ip::address& address) {
         uint16_t portnum;
         try {
             // XXX: SunStudio with stlport4 doesn't reject some invalid
@@ -143,7 +129,7 @@ public:
             isc_throw(IOError, "Invalid port number '" << &port << "': " <<
                       ex.what());
         }
-        addServer(portnum, address,param_flags);
+        addServer(portnum, address);
     }
 };
 
@@ -153,8 +139,7 @@ DNSServiceImpl::DNSServiceImpl(IOService& io_service,
                                const asio::ip::address* const v6addr,
                                SimpleCallback* checkin,
                                DNSLookup* lookup,
-                               DNSAnswer* answer,
-                               const UDPVersion param_flags):
+                               DNSAnswer* answer) :
     io_service_(io_service),
     checkin_(checkin),
     lookup_(lookup),
@@ -162,10 +147,10 @@ DNSServiceImpl::DNSServiceImpl(IOService& io_service,
 {
 
     if (v4addr) {
-        addServer(port, *v4addr,param_flags);
+        addServer(port, *v4addr);
     }
     if (v6addr) {
-        addServer(port, *v6addr,param_flags);
+        addServer(port, *v6addr);
     }
 }
 
@@ -173,12 +158,12 @@ DNSService::DNSService(IOService& io_service,
                        const char& port, const char& address,
                        SimpleCallback* checkin,
                        DNSLookup* lookup,
-                       DNSAnswer* answer,
-                       const UDPVersion param_flags) :
+                       DNSAnswer* answer) :
     impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
-        answer,param_flags)), io_service_(io_service)
+                             answer)),
+    io_service_(io_service)
 {
-    addServer(port, &address,param_flags);
+    addServer(port, &address);
 }
 
 DNSService::DNSService(IOService& io_service,
@@ -186,8 +171,7 @@ DNSService::DNSService(IOService& io_service,
                        const bool use_ipv4, const bool use_ipv6,
                        SimpleCallback* checkin,
                        DNSLookup* lookup,
-                       DNSAnswer* answer,
-                       const UDPVersion param_flags) :
+                       DNSAnswer* answer) :
     impl_(NULL), io_service_(io_service)
 {
     const asio::ip::address v4addr_any =
@@ -196,13 +180,14 @@ DNSService::DNSService(IOService& io_service,
     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,param_flags);
+    impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin,
+                               lookup, answer);
 }
 
 DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
-    DNSLookup* lookup, DNSAnswer *answer,const UDPVersion param_flags) :
+    DNSLookup* lookup, DNSAnswer *answer) :
     impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
-        answer,param_flags)), io_service_(io_service)
+                             answer)), io_service_(io_service)
 {
 }
 
@@ -211,24 +196,30 @@ DNSService::~DNSService() {
 }
 
 void
-DNSService::addServer(const char& port, const std::string& address,UDPVersion param_flags) {
-    impl_->addServer(port, convertAddr(address),param_flags);
+DNSService::addServer(const char& port, const std::string& address) {
+    impl_->addServer(port, convertAddr(address));
 }
 
 void
-DNSService::addServer(uint16_t port, const std::string& address,UDPVersion param_flags) {
-    impl_->addServer(port, convertAddr(address),param_flags);
+DNSService::addServer(uint16_t port, const std::string& address) {
+    impl_->addServer(port, convertAddr(address));
 }
 
 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) { 
-        impl_->addServerFromFD<DNSServiceImpl::SyncUDPServerPtr, SyncUDPServer>(fd, af);
-    } else if(ASYNC_ == param_flags) {
-        impl_->addServerFromFD<DNSServiceImpl::UDPServerPtr, UDPServer>(fd, af);
+void DNSService::addServerUDPFromFD(int fd, int af, ServerFlag options) {
+    if ((~SERVER_DEFINED_FLAGS & static_cast<unsigned int>(options)) != 0) {
+        isc_throw(isc::InvalidParameter, "Invalid DNS/UDP server option: "
+                  << options);
+    }
+    if ((options & SERVER_SYNC_OK) != 0) {
+        impl_->addServerFromFD<DNSServiceImpl::SyncUDPServerPtr,
+            SyncUDPServer>(fd, af);
+    } else {
+        impl_->addServerFromFD<DNSServiceImpl::UDPServerPtr, UDPServer>(
+            fd, af);
     }
 }
 

+ 90 - 23
src/lib/asiodns/dns_service.h

@@ -27,13 +27,64 @@ class DNSLookup;
 class DNSAnswer;
 class DNSServiceImpl;
 
-
-/// Codes for UDPServers used in addServer()method.
+/// \brief A base class for common \c DNSService interfaces.
+///
+/// This class is defined mainly for test code so it can use a faked/mock
+/// version of a derived class and test scenarios that would involve
+/// \c DNSService without actually instantiating the real service class.
 ///
-/// Note: the codes only used in how to create the UDPServers.
-enum UDPVersion {
-      SYNC_  = 1,     ///< used synchronous UDPServer
-      ASYNC_ = 2     ///< used asynchronous UDPServer
+/// It doesn't intend to be a customization for other purposes - we generally
+/// expect non test code only use \c DNSService directly.
+/// For this reason most of the detailed description are given in the
+/// \c DNSService class.  See that for further details of specific methods
+/// and class behaviors.
+class DNSServiceBase {
+protected:
+    /// \brief Default constructor.
+    ///
+    /// This is protected so this class couldn't be accidentally instantiated
+    /// directly, even if there were no pure virtual functions.
+    DNSServiceBase() {}
+
+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.
+    ///
+    /// Note: the description is given here because it's used in the method
+    /// signature.  It essentially belongs to the derived \c DNSService
+    /// class.
+    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.
+    };
+
+public:
+    /// \brief The destructor.
+    virtual ~DNSServiceBase() {}
+
+    virtual void addServerTCPFromFD(int fd, int af) = 0;
+    virtual void addServerUDPFromFD(int fd, int af,
+                                    ServerFlag options = SERVER_DEFAULT) = 0;
+    virtual void clearServers() = 0;
+
+    virtual asiolink::IOService& getIOService() = 0;
 };
 
 /// \brief Handle DNS Queries
@@ -43,7 +94,7 @@ enum UDPVersion {
 /// logic that is shared between the authoritative and the recursive
 /// server implementations. As such, it handles asio, including config
 /// updates (through the 'Checkinprovider'), and listening sockets.
-class DNSService {
+class DNSService : public DNSServiceBase {
     ///
     /// \name Constructors and Destructor
     ///
@@ -54,6 +105,12 @@ private:
     DNSService(const DNSService& source);
     DNSService& operator=(const DNSService& source);
 
+private:
+    // Bit or'ed all defined \c ServerFlag values.  Used internally for
+    // compatibility check.  Note that this doesn't have to be used by
+    // applications, and doesn't have to be defined in the "base" class.
+    static const unsigned int SERVER_DEFINED_FLAGS = 1;
+
 public:
     /// \brief The constructor with a specific IP address and port on which
     /// the services listen on.
@@ -66,8 +123,8 @@ public:
     /// \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,
-               const UDPVersion param_flags = SYNC_);
+               DNSLookup* lookup, DNSAnswer* answer);
+
     /// \brief The constructor with a specific port on which the services
     /// listen on.
     ///
@@ -85,23 +142,21 @@ public:
     DNSService(asiolink::IOService& io_service, const char& port,
                const bool use_ipv4, const bool use_ipv6,
                isc::asiolink::SimpleCallback* checkin, DNSLookup* lookup,
-               DNSAnswer* answer,
-               const UDPVersion param_flags = SYNC_);
+               DNSAnswer* answer);
     /// \brief The constructor without any servers.
     ///
     /// Use addServer() to add some servers.
-    DNSService(asiolink::IOService& io_service, isc::asiolink::SimpleCallback* checkin,
-               DNSLookup* lookup, DNSAnswer* answer,
-               const UDPVersion param_flags = SYNC_);
+    DNSService(asiolink::IOService& io_service,
+               isc::asiolink::SimpleCallback* checkin,
+               DNSLookup* lookup, DNSAnswer* answer);
+
     /// \brief The destructor.
-    ~DNSService();
+    virtual ~DNSService();
     //@}
 
     /// \brief Add another server to the service
-    void addServer(uint16_t port, const std::string &address,
-                   const UDPVersion param_flags = SYNC_);
-    void addServer(const char &port, const std::string &address,
-                   const UDPVersion param_flags = SYNC_);
+    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
     /// opened file descriptor
@@ -113,13 +168,17 @@ 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.
     /// \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 addServerTCPFromFD(int fd, int af);
+    virtual void addServerTCPFromFD(int fd, int af);
 
     /// \brief Add another UDP server to the service from already opened
     ///    file descriptor
@@ -133,10 +192,14 @@ 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.
-    /// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6.
+    /// \param options Optional properties of the server (see ServerFlag).
+    ///
+    /// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6,
+    ///     or the given \c options include an unsupported or invalid value.
     /// \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 = ASYNC_);
+    virtual void addServerUDPFromFD(int fd, int af,
+                                    ServerFlag options = SERVER_DEFAULT);
 
     /// \brief Remove all servers from the service
     void clearServers();
@@ -152,7 +215,7 @@ public:
     /// \brief Return the IO Service Object
     ///
     /// \return IOService object for this DNS service.
-    asiolink::IOService& getIOService() { return (io_service_);}
+    virtual asiolink::IOService& getIOService() { return (io_service_);}
 
 private:
     DNSServiceImpl* impl_;
@@ -162,3 +225,7 @@ private:
 } // namespace asiodns
 } // namespace isc
 #endif // __ASIOLINK_DNS_SERVICE_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 1 - 1
src/lib/asiodns/tests/Makefile.am

@@ -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
 

+ 323 - 0
src/lib/asiodns/tests/dns_service_unittest.cc

@@ -0,0 +1,323 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <asio.hpp>
+#include <asiolink/asiolink.h>
+#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";
+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
+// 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;
+        // Content shouldn't matter for the tests, but initialize anyway
+        memset(data, 1, sizeof(data));
+    }
+
+    ~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;
+    TestLookup lookup;
+    DNSService dns_service;
+private:
+    asio::ip::udp::socket client_socket;
+    const asio::ip::udp::endpoint server_ep;
+    char data[4];
+
+    // 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;
+};
+
+// Need to define the non-const static members outside of the class
+// declaration
+IOService* UDPDNSServiceTest::current_service;
+bool UDPDNSServiceTest::io_service_is_time_out;
+
+// 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) {
+        // If getaddrinfo returns 0, res should be set to a non NULL valid
+        // pointer, but some variants of cppcheck reportedly complains about
+        // it, so we satisfy them here.
+        if (res != NULL) {
+            s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+            if (s >= 0) {
+                error = bind(s, res->ai_addr, res->ai_addrlen);
+            }
+            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_);
+}
+
+TEST_F(UDPDNSServiceTest, addUDPServerFromFDWithUnknownOption) {
+    // Use of undefined/incompatible options should result in an exception.
+    EXPECT_THROW(dns_service.addServerUDPFromFD(
+                     getSocketFD(AF_INET6, TEST_IPV6_ADDR, TEST_SERVER_PORT),
+                     AF_INET6, static_cast<DNSService::ServerFlag>(2)),
+                 isc::InvalidParameter);
+}
+
+} // unnamed namespace

+ 0 - 123
src/lib/asiodns/tests/io_service_unittest.cc

@@ -1,123 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-#include <gtest/gtest.h>
-
-#include <asio.hpp>
-#include <asiolink/asiolink.h>
-#include <asiodns/asiodns.h>
-
-using namespace isc::asiolink;
-using namespace isc::asiodns;
-
-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_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;
-}
-
-TEST(IOServiceTest, BadUdpServerVersion) {
-    IOService io_service;
-    DNSService* dns_service = new DNSService(io_service, NULL, NULL, NULL);
-    EXPECT_THROW(dns_service->addServer(*TEST_SERVER_PORT, "127.0.0.1", UDPVersion(3)), IOError);
-}

+ 1 - 0
src/lib/resolve/Makefile.am

@@ -34,6 +34,7 @@ nodist_libresolve_la_SOURCES = resolve_messages.h resolve_messages.cc
 libresolve_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
 libresolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 libresolve_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+libresolve_la_LIBADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
 
 # The message file should be in the distribution.
 EXTRA_DIST = resolve_messages.mes

+ 1 - 1
src/lib/resolve/recursive_query.cc

@@ -128,7 +128,7 @@ typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
 // mishandles this in its name mangling, and wouldn't compile.
 // We can probably use a typedef, but need to move it to a central
 // location and use it consistently.
-RecursiveQuery::RecursiveQuery(DNSService& dns_service,
+RecursiveQuery::RecursiveQuery(DNSServiceBase& dns_service,
     isc::nsas::NameserverAddressStore& nsas,
     isc::cache::ResolverCache& cache,
     const std::vector<std::pair<std::string, uint16_t> >& upstream,

+ 2 - 2
src/lib/resolve/recursive_query.h

@@ -87,7 +87,7 @@ public:
     /// \param lookup_timeout Timeout value for when we give up, in ms
     /// \param retries how many times we try again (0 means just send and
     ///     and return if it returs).
-    RecursiveQuery(DNSService& dns_service,
+    RecursiveQuery(DNSServiceBase& dns_service,
                    isc::nsas::NameserverAddressStore& nsas,
                    isc::cache::ResolverCache& cache,
                    const std::vector<std::pair<std::string, uint16_t> >&
@@ -178,7 +178,7 @@ public:
     void setTestServer(const std::string& address, uint16_t port);
 
 private:
-    DNSService& dns_service_;
+    DNSServiceBase& dns_service_;
     isc::nsas::NameserverAddressStore& nsas_;
     isc::cache::ResolverCache& cache_;
     boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >

+ 15 - 21
src/lib/server_common/portconfig.cc

@@ -31,11 +31,6 @@ namespace isc {
 namespace server_common {
 namespace portconfig {
 
-// This flags disables pushing the sockets to the DNSService. It prevents
-// the clearServers() method to close the file descriptors we made up.
-// It is not presented in any header, but we use it from the tests anyway.
-bool test_mode(false);
-
 AddressList
 parseAddresses(isc::data::ConstElementPtr addresses,
                const std::string& elemName)
@@ -84,7 +79,9 @@ namespace {
 vector<string> current_sockets;
 
 void
-setAddresses(DNSService& service, const AddressList& addresses) {
+setAddresses(DNSServiceBase& service, const AddressList& addresses,
+             DNSService::ServerFlag server_options)
+{
     service.clearServers();
     BOOST_FOREACH(const string& token, current_sockets) {
         socketRequestor().releaseSocket(token);
@@ -99,35 +96,32 @@ setAddresses(DNSService& service, const AddressList& addresses) {
                                                 address.first, address.second,
                                                 SocketRequestor::SHARE_SAME));
         current_sockets.push_back(tcp.second);
-        if (!test_mode) {
-            service.addServerTCPFromFD(tcp.first, af);
-        }
+        service.addServerTCPFromFD(tcp.first, af);
         const SocketRequestor::SocketID
             udp(socketRequestor().requestSocket(SocketRequestor::UDP,
                                                 address.first, address.second,
                                                 SocketRequestor::SHARE_SAME));
         current_sockets.push_back(udp.second);
-        if (!test_mode) {
-            service.addServerUDPFromFD(udp.first, af);
-        }
+        service.addServerUDPFromFD(udp.first, af, server_options);
     }
 }
 
 }
 
 void
-installListenAddresses(const AddressList& newAddresses,
-                       AddressList& addressStore,
-                       isc::asiodns::DNSService& service)
+installListenAddresses(const AddressList& new_addresses,
+                       AddressList& address_store,
+                       DNSServiceBase& service,
+                       DNSService::ServerFlag server_options)
 {
     try {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_SET_LISTEN);
-        BOOST_FOREACH(const AddressPair& addr, newAddresses) {
+        BOOST_FOREACH(const AddressPair& addr, new_addresses) {
             LOG_DEBUG(logger, DBG_TRACE_VALUES, SRVCOMM_ADDRESS_VALUE).
                 arg(addr.first).arg(addr.second);
         }
-        setAddresses(service, newAddresses);
-        addressStore = newAddresses;
+        setAddresses(service, new_addresses, server_options);
+        address_store = new_addresses;
     } catch (const SocketRequestor::NonFatalSocketError& e) {
         /*
          * If one of the addresses isn't set successfully, we will restore
@@ -144,14 +138,14 @@ installListenAddresses(const AddressList& newAddresses,
          */
         LOG_ERROR(logger, SRVCOMM_ADDRESS_FAIL).arg(e.what());
         try {
-            setAddresses(service, addressStore);
+            setAddresses(service, address_store, server_options);
         } catch (const SocketRequestor::NonFatalSocketError& e2) {
             LOG_FATAL(logger, SRVCOMM_ADDRESS_UNRECOVERABLE).arg(e2.what());
             // If we can't set the new ones, nor the old ones, at least
             // releasing everything should work. If it doesn't, there isn't
             // anything else we could do.
-            setAddresses(service, AddressList());
-            addressStore.clear();
+            setAddresses(service, AddressList(), server_options);
+            address_store.clear();
         }
         //Anyway the new configure has problem, we need to notify configure
         //manager the new configure doesn't work

+ 43 - 43
src/lib/server_common/portconfig.h

@@ -15,22 +15,15 @@
 #ifndef ISC_SERVER_COMMON_PORTCONFIG_H
 #define ISC_SERVER_COMMON_PORTCONFIG_H
 
+#include <cc/data.h>
+
+#include <asiodns/dns_service.h>
+
 #include <utility>
 #include <string>
 #include <stdint.h>
 #include <vector>
 
-#include <cc/data.h>
-
-/*
- * Some forward declarations.
- */
-namespace isc {
-namespace asiodns {
-class DNSService;
-}
-}
-
 namespace isc {
 namespace server_common {
 /**
@@ -88,42 +81,49 @@ AddressList
 parseAddresses(isc::data::ConstElementPtr addresses,
                const std::string& elemName);
 
-/**
- * \brief Changes current listening addresses and ports.
- *
- * Removes all sockets we currently listen on and starts listening on the
- * addresses and ports requested in newAddresses.
- *
- * If it fails to set up the new addresses, it attempts to roll back to the
- * previous addresses (but it still propagates the exception). If the rollback
- * fails as well, it doesn't abort the application (to allow reconfiguration),
- * but removes all the sockets it listened on. One of the exceptions is
- * propagated.
- *
- * The ports are requested from the socket creator through boss. Therefore
- * you need to initialize the SocketRequestor before using this function.
- *
- * \param newAddresses are the addresses you want to listen on.
- * \param addressStore is the place you store your current addresses. It is
- *     used when there's a need for rollback. The newAddresses are copied here
- *     when the change is successful.
- * \param dnsService is the DNSService object we use now. The requests from
- *     the new sockets are handled using this dnsService (and all current
- *     sockets on the service are closed first).
- * \throw asiolink::IOError when initialization or closing of socket fails.
- * \throw isc::server_common::SocketRequestor::Socket error when the
- *     boss/socket creator doesn't want to give us the socket.
- * \throw std::bad_alloc when allocation fails.
- * \throw isc::InvalidOperation when the function is called and the
- *     SocketRequestor isn't initialized yet.
- */
+/// \brief Changes current listening addresses and ports.
+///
+/// Removes all sockets we currently listen on and starts listening on the
+/// addresses and ports requested in new_addresses.
+///
+/// If it fails to set up the new addresses, it attempts to roll back to the
+/// previous addresses (but it still propagates the exception). If the rollback
+/// fails as well, it doesn't abort the application (to allow reconfiguration),
+/// but removes all the sockets it listened on. One of the exceptions is
+/// propagated.
+///
+/// The ports are requested from the socket creator through boss. Therefore
+/// you need to initialize the SocketRequestor before using this function.
+///
+/// \param new_addresses are the addresses you want to listen on.
+/// \param address_store is the place you store your current addresses. It is
+///     used when there's a need for rollback. The new_addresses are copied
+///     here when the change is successful.
+/// \param dns_service is the DNSService object we use now. The requests from
+///     the new sockets are handled using this dns_service (and all current
+///     sockets on the service are closed first).
+/// \param server_options specifies optional properties for the servers
+///        created via \c dns_service.
+///
+/// \throw asiolink::IOError when initialization or closing of socket fails.
+/// \throw isc::server_common::SocketRequestor::Socket error when the
+///     boss/socket creator doesn't want to give us the socket.
+/// \throw std::bad_alloc when allocation fails.
+/// \throw isc::InvalidOperation when the function is called and the
+///     SocketRequestor isn't initialized yet.
 void
-installListenAddresses(const AddressList& newAddresses,
-                       AddressList& addressStore,
-                       asiodns::DNSService& dnsService);
+installListenAddresses(const AddressList& new_addresses,
+                       AddressList& address_store,
+                       asiodns::DNSServiceBase& dns_service,
+                       asiodns::DNSService::ServerFlag server_options =
+                       asiodns::DNSService::SERVER_DEFAULT);
 
 }
 }
 }
 
 #endif
+
+// Local Variables:
+// mode: c++
+// End:

+ 3 - 3
src/lib/server_common/tests/portconfig_unittest.cc

@@ -14,6 +14,7 @@
 
 #include <server_common/portconfig.h>
 #include <testutils/socket_request.h>
+#include <testutils/mockups.h>
 
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
@@ -30,6 +31,7 @@ using namespace isc;
 using namespace std;
 using namespace isc::asiolink;
 using namespace isc::asiodns;
+using namespace isc::testutils;
 using boost::lexical_cast;
 
 namespace {
@@ -132,7 +134,6 @@ TEST_F(ParseAddresses, invalid) {
 // Test fixture for installListenAddresses
 struct InstallListenAddresses : public ::testing::Test {
     InstallListenAddresses() :
-        dnss_(ios_, NULL, NULL, NULL),
         // The empty string is expected parameter of requestSocket,
         // not app_name - the request does not fall back to this, it
         // is checked to be the same.
@@ -143,8 +144,7 @@ struct InstallListenAddresses : public ::testing::Test {
         invalid_.push_back(AddressPair("127.0.0.1", 5288));
         invalid_.push_back(AddressPair("192.0.2.2", 1));
     }
-    IOService ios_;
-    DNSService dnss_;
+    MockDNSService dnss_;
     AddressList store_;
     isc::testutils::TestSocketRequestor sock_requestor_;
     // We should be able to bind to these addresses

+ 4 - 0
src/lib/testutils/dnsmessage_test.h

@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __ISC_TESTUTILS_DNSMESSAGETEST_H
+#define __ISC_TESTUTILS_DNSMESSAGETEST_H 1
+
 #include <algorithm>
 #include <functional>
 #include <iosfwd>
@@ -339,6 +342,7 @@ rrsetsCheck(const std::string& expected,
 
 } // end of namespace testutils
 } // end of namespace isc
+#endif  // __ISC_TESTUTILS_DNSMESSAGETEST_H
 
 // Local Variables:
 // mode: c++

+ 57 - 0
src/lib/testutils/mockups.h

@@ -12,8 +12,13 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __ISC_TESTUTILS_MOCKUPS_H
+#define __ISC_TESTUTILS_MOCKUPS_H 1
+
 #include <config.h>
 
+#include <exceptions/exceptions.h>
+
 #include <cc/data.h>
 #include <cc/session.h>
 
@@ -21,6 +26,12 @@
 
 #include <asiodns/asiodns.h>
 
+#include <utility>
+#include <vector>
+
+namespace isc {
+namespace testutils {
+
 // A minimal mock configuration session.  Most the methods are
 // stubbed out, except for a very basic group_sendmsg() and
 // group_recvmsg().  hasQueuedMessages() always returns false.
@@ -93,6 +104,45 @@ private:
     bool receive_ok_;
 };
 
+// This mock object does nothing except for recording passed parameters
+// to addServerXXX methods so the test code subsequently checks the parameters.
+class MockDNSService : public isc::asiodns::DNSServiceBase {
+public:
+    // A helper tuple of parameters passed to addServerUDPFromFD().
+    struct UDPFdParams {
+        int fd;
+        int af;
+        ServerFlag options;
+    };
+
+    virtual void addServerTCPFromFD(int fd, int af) {
+        tcp_fd_params_.push_back(std::pair<int, int>(fd, af));
+    }
+    virtual void addServerUDPFromFD(int fd, int af, ServerFlag options) {
+        UDPFdParams params = { fd, af, options };
+        udp_fd_params_.push_back(params);
+    }
+    virtual void clearServers() {}
+
+    virtual asiolink::IOService& getIOService() {
+        isc_throw(isc::Unexpected,
+                  "MockDNSService::getIOService() shouldn't be called");
+    }
+
+    // These two allow the tests to check how the servers have been created
+    // through this object.
+    const std::vector<std::pair<int, int> >& getTCPFdParams() const {
+        return (tcp_fd_params_);
+    }
+    const std::vector<UDPFdParams>& getUDPFdParams() const {
+        return (udp_fd_params_);
+    }
+
+private:
+    std::vector<std::pair<int, int> > tcp_fd_params_;
+    std::vector<UDPFdParams> udp_fd_params_;
+};
+
 // A nonoperative DNSServer object to be used in calls to processMessage().
 class MockServer : public isc::asiodns::DNSServer {
 public:
@@ -149,3 +199,10 @@ private:
     bool disconnect_ok_;
 };
 
+} // end of testutils
+} // end of isc
+#endif  // __ISC_TESTUTILS_MOCKUPS_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 3 - 3
src/lib/testutils/portconfig.h

@@ -12,8 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#ifndef TESTUTILS_PORTCONFIG_H
-#define TESTUTILS_PORTCONFIG_H
+#ifndef __ISC_TESTUTILS_PORTCONFIG_H
+#define __ISC_TESTUTILS_PORTCONFIG_H
 
 #include <gtest/gtest.h>
 #include <cc/data.h>
@@ -186,4 +186,4 @@ invalidListenAddressConfig(Server& server) {
 }
 }
 
-#endif
+#endif  // __ISC_TESTUTILS_PORTCONFIG_H

+ 6 - 13
src/lib/testutils/socket_request.h

@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __ISC_TESTUTILS_SOCKETREQUEST_H
+#define __ISC_TESTUTILS_SOCKETREQUEST_H 1
+
 #include <server_common/socket_request.h>
 #include <server_common/portconfig.h>
 
@@ -24,13 +27,6 @@
 #include <string>
 
 namespace isc {
-namespace server_common {
-namespace portconfig {
-// Access the private hidden flag
-extern bool test_mode;
-}
-}
-
 namespace testutils {
 
 /// \brief A testcase part for faking the SocketRequestor in tests
@@ -64,7 +60,7 @@ public:
     ///     not fall back to this value if its share_name is left empty, if
     ///     you want to check the code relies on the requestor to use the
     ///     app name, you set this to empty string.
-    TestSocketRequestor(asiodns::DNSService& dnss,
+    TestSocketRequestor(asiodns::DNSServiceBase& dnss,
                         server_common::portconfig::AddressList& store,
                         uint16_t expect_port,
                         const std::string& expected_app) :
@@ -74,8 +70,6 @@ public:
     {
         // Prepare the requestor (us) for the test
         server_common::initTestSocketRequestor(this);
-        // Don't manipulate the real sockets
-        server_common::portconfig::test_mode = true;
     }
 
     /// \brief Destructor
@@ -90,8 +84,6 @@ public:
         server_common::portconfig::installListenAddresses(list, store_, dnss_);
         // Don't leave invalid pointers here
         server_common::initTestSocketRequestor(NULL);
-        // And return the mode
-        server_common::portconfig::test_mode = false;
     }
 
     /// \brief Tokens released by releaseSocket
@@ -216,7 +208,7 @@ public:
     }
 
 private:
-    asiodns::DNSService& dnss_;
+    asiodns::DNSServiceBase& dnss_;
     server_common::portconfig::AddressList& store_;
     const uint16_t expect_port_;
     const std::string expected_app_;
@@ -224,3 +216,4 @@ private:
 
 }
 }
+#endif  // __ISC_TESTUTILS_SOCKETREQUEST_H

+ 4 - 0
src/lib/testutils/srv_test.h

@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __ISC_TESTUTILS_SRVTEST_H
+#define __ISC_TESTUTILS_SRVTEST_H 1
+
 #include <util/buffer.h>
 #include <dns/name.h>
 #include <dns/message.h>
@@ -106,6 +109,7 @@ protected:
 };
 } // end of namespace testutils
 } // end of namespace isc
+#endif  // __ISC_TESTUTILS_SRVTEST_H
 
 // Local Variables: 
 // mode: c++