BIND 10 trac826, updated. 98743e8215518210ae8e6efb5348b14c4fe4cd28 libasiodns first attempt

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Jun 28 22:02:15 UTC 2012


The branch, trac826 has been updated
       via  98743e8215518210ae8e6efb5348b14c4fe4cd28 (commit)
      from  539b22d0e9306dfb49b7a54fdde3bb34827ad5be (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 98743e8215518210ae8e6efb5348b14c4fe4cd28
Author: Francis Dupont <fdupont at isc.org>
Date:   Fri Jun 29 00:01:49 2012 +0200

    libasiodns first attempt

-----------------------------------------------------------------------

Summary of changes:
 src/lib/asiodns/.gitignore                         |    2 +
 src/lib/asiodns/Makefile.am                        |    2 +
 src/lib/asiodns/asiodns_messages.mes               |    8 +
 src/lib/asiodns/dns_lookup.h                       |    4 +-
 src/lib/asiodns/dns_server.h                       |   20 +-
 src/lib/asiodns/dns_service.cc                     |  194 +++------
 src/lib/asiodns/dns_service.h                      |  170 ++++++--
 src/lib/asiodns/io_fetch.cc                        |   50 +--
 src/lib/asiodns/io_fetch.h                         |    8 +-
 src/lib/{cache => asiodns}/logger.cc               |   10 +-
 .../{server_common/logger.cc => asiodns/logger.h}  |   11 +-
 src/lib/asiodns/sync_udp_server.cc                 |  202 +++++++++
 src/lib/asiodns/sync_udp_server.h                  |  154 +++++++
 src/lib/asiodns/tcp_server.cc                      |   39 +-
 src/lib/asiodns/tcp_server.h                       |   29 +-
 src/lib/{acl => asiodns}/tests/.gitignore          |    0
 src/lib/asiodns/tests/Makefile.am                  |    5 +-
 src/lib/asiodns/tests/dns_server_unittest.cc       |  438 ++++++++++++++------
 src/lib/asiodns/tests/dns_service_unittest.cc      |  273 ++++++++++++
 src/lib/asiodns/tests/io_fetch_unittest.cc         |   41 +-
 src/lib/asiodns/tests/io_service_unittest.cc       |  118 ------
 src/lib/asiodns/udp_server.cc                      |   52 ++-
 src/lib/asiodns/udp_server.h                       |   33 +-
 23 files changed, 1320 insertions(+), 543 deletions(-)
 create mode 100644 src/lib/asiodns/.gitignore
 copy src/lib/{cache => asiodns}/logger.cc (81%)
 copy src/lib/{server_common/logger.cc => asiodns/logger.h} (76%)
 create mode 100644 src/lib/asiodns/sync_udp_server.cc
 create mode 100644 src/lib/asiodns/sync_udp_server.h
 copy src/lib/{acl => asiodns}/tests/.gitignore (100%)
 create mode 100644 src/lib/asiodns/tests/dns_service_unittest.cc
 delete mode 100644 src/lib/asiodns/tests/io_service_unittest.cc

-----------------------------------------------------------------------
diff --git a/src/lib/asiodns/.gitignore b/src/lib/asiodns/.gitignore
new file mode 100644
index 0000000..dedf17e
--- /dev/null
+++ b/src/lib/asiodns/.gitignore
@@ -0,0 +1,2 @@
+/asiodns_messages.cc
+/asiodns_messages.h
diff --git a/src/lib/asiodns/Makefile.am b/src/lib/asiodns/Makefile.am
index 2d246ef..a03f147 100644
--- a/src/lib/asiodns/Makefile.am
+++ b/src/lib/asiodns/Makefile.am
@@ -24,7 +24,9 @@ libasiodns_la_SOURCES += dns_server.h
 libasiodns_la_SOURCES += dns_service.cc dns_service.h
 libasiodns_la_SOURCES += tcp_server.cc tcp_server.h
 libasiodns_la_SOURCES += udp_server.cc udp_server.h
+libasiodns_la_SOURCES += sync_udp_server.cc sync_udp_server.h
 libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
+libasiodns_la_SOURCES += logger.h logger.cc
 
 nodist_libasiodns_la_SOURCES = asiodns_messages.cc asiodns_messages.h
 
diff --git a/src/lib/asiodns/asiodns_messages.mes b/src/lib/asiodns/asiodns_messages.mes
index feb75d4..8fbafdd 100644
--- a/src/lib/asiodns/asiodns_messages.mes
+++ b/src/lib/asiodns/asiodns_messages.mes
@@ -14,6 +14,14 @@
 
 $NAMESPACE isc::asiodns
 
+% ASIODNS_FD_ADD_TCP adding a new TCP server by opened fd %1
+A debug message informing about installing a file descriptor as a server.
+The file descriptor number is noted.
+
+% ASIODNS_FD_ADD_UDP adding a new UDP server by opened fd %1
+A debug message informing about installing a file descriptor as a server.
+The file descriptor number is noted.
+
 % ASIODNS_FETCH_COMPLETED upstream fetch to %1(%2) has now completed
 A debug message, this records that the upstream fetch (a query made by the
 resolver on behalf of its client) to the specified address has completed.
diff --git a/src/lib/asiodns/dns_lookup.h b/src/lib/asiodns/dns_lookup.h
index f79c507..5dc84ac 100644
--- a/src/lib/asiodns/dns_lookup.h
+++ b/src/lib/asiodns/dns_lookup.h
@@ -51,7 +51,9 @@ protected:
     ///
     /// This is intentionally defined as \c protected as this base class
     /// should never be instantiated (except as part of a derived class).
-    DNSLookup() { self_ = this; }
+    DNSLookup() {
+        self_ = this;
+    }
 public:
     /// \brief The destructor
     virtual ~DNSLookup() {}
diff --git a/src/lib/asiodns/dns_server.h b/src/lib/asiodns/dns_server.h
index 3361e3b..119aa66 100644
--- a/src/lib/asiodns/dns_server.h
+++ b/src/lib/asiodns/dns_server.h
@@ -53,7 +53,9 @@ protected:
     /// This is intentionally defined as \c protected, as this base class
     /// should never be instantiated except as part of a derived class.
     //@{
-    DNSServer() { self_ = this; }
+    DNSServer() {
+        self_ = this;
+    }
 public:
     /// \brief The destructor
     virtual ~DNSServer() {}
@@ -86,22 +88,6 @@ public:
     ///             to return.
     virtual void resume(const bool done) { self_->resume(done); }
 
-    /// \brief Indicate whether the server is able to send an answer
-    /// to a query.
-    ///
-    /// This is presently used only for testing purposes.
-    virtual bool hasAnswer() { return (self_->hasAnswer()); }
-
-    /// \brief Returns the current value of the 'coroutine' object
-    ///
-    /// This is a temporary method, intended to be used for debugging
-    /// purposes during development and removed later.  It allows
-    /// callers from outside the coroutine object to retrieve information
-    /// about its current state.
-    ///
-    /// \return The value of the 'coroutine' object
-    virtual int value() { return (self_->value()); }
-
     /// \brief Returns a pointer to a clone of this DNSServer object.
     ///
     /// When a \c DNSServer object is copied or assigned, the result will
diff --git a/src/lib/asiodns/dns_service.cc b/src/lib/asiodns/dns_service.cc
index 94f98e2..175e82b 100644
--- a/src/lib/asiodns/dns_service.cc
+++ b/src/lib/asiodns/dns_service.cc
@@ -14,32 +14,19 @@
 
 #include <config.h>
 
-#ifdef _WIN32
-#include <ws2tcpip.h>
-#else
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
-#endif
+#include <exceptions/exceptions.h>
 
-#include <boost/lexical_cast.hpp>
-
-#include <log/dummylog.h>
-
-#include <asio.hpp>
 #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 <udp_server.h>
+#include <sync_udp_server.h>
 
-#include <log/dummylog.h>
-
-#include <boost/lexical_cast.hpp>
 #include <boost/foreach.hpp>
 
-using isc::log::dlog;
-
 using namespace isc::asiolink;
 
 namespace isc {
@@ -48,137 +35,42 @@ namespace asiodns {
 class DNSLookup;
 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 {
 public:
-    DNSServiceImpl(IOService& io_service, const char& port,
-                  const asio::ip::address* v4addr,
-                  const asio::ip::address* v6addr,
-                  SimpleCallback* checkin, DNSLookup* lookup,
-                  DNSAnswer* answer);
+    DNSServiceImpl(IOService& io_service, SimpleCallback* checkin,
+                   DNSLookup* lookup, DNSAnswer* answer) :
+            io_service_(io_service), checkin_(checkin), lookup_(lookup),
+            answer_(answer)
+    {}
 
     IOService& io_service_;
 
     typedef boost::shared_ptr<UDPServer> UDPServerPtr;
+    typedef boost::shared_ptr<SyncUDPServer> SyncUDPServerPtr;
     typedef boost::shared_ptr<TCPServer> TCPServerPtr;
     typedef boost::shared_ptr<DNSServer> DNSServerPtr;
     std::vector<DNSServerPtr> servers_;
-    SimpleCallback *checkin_;
-    DNSLookup *lookup_;
-    DNSAnswer *answer_;
-
-    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);
-    }
-private:
-    // silence MSVC warning C4512: assignment operator could not be generated
-    DNSServiceImpl& operator=(DNSServiceImpl const&);
-};
+    SimpleCallback* checkin_;
+    DNSLookup* lookup_;
+    DNSAnswer* answer_;
 
-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);
+#ifdef _WIN32
+    template<class Ptr, class Server> void addServerFromFD(SOCKET fd, int af)
+#else
+    template<class Ptr, class Server> void addServerFromFD(int fd, int af)
+#endif
+    {
+        Ptr server(new Server(io_service_.get_io_service(), fd, af, checkin_,
+                              lookup_, answer_));
+        (*server)();
+        servers_.push_back(server);
     }
-}
-
-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,
-    DNSLookup* lookup, DNSAnswer *answer) :
-    impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
-        answer)), io_service_(io_service)
+                       DNSLookup* lookup, DNSAnswer *answer) :
+    impl_(new DNSServiceImpl(io_service, checkin, lookup, answer)),
+    io_service_(io_service)
 {
 }
 
@@ -186,14 +78,32 @@ DNSService::~DNSService() {
     delete impl_;
 }
 
-void
-DNSService::addServer(const char& port, const std::string& address) {
-    impl_->addServer(port, convertAddr(address));
+#ifdef _WIN32
+void DNSService::addServerTCPFromFD(SOCKET fd, int af)
+#else
+void DNSService::addServerTCPFromFD(int fd, int af)
+#endif
+{
+    impl_->addServerFromFD<DNSServiceImpl::TCPServerPtr, TCPServer>(fd, af);
 }
 
-void
-DNSService::addServer(uint16_t port, const std::string& address) {
-    impl_->addServer(port, convertAddr(address));
+#ifdef _WIN32
+void DNSService::addServerUDPFromFD(SOCKET fd, int af, ServerFlag options)
+#else
+void DNSService::addServerUDPFromFD(int fd, int af, ServerFlag options)
+#endif
+{
+    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);
+    }
 }
 
 void
diff --git a/src/lib/asiodns/dns_service.h b/src/lib/asiodns/dns_service.h
index 6b6a6c0..0520fa0 100644
--- a/src/lib/asiodns/dns_service.h
+++ b/src/lib/asiodns/dns_service.h
@@ -27,6 +27,72 @@ class DNSLookup;
 class DNSAnswer;
 class DNSServiceImpl;
 
+/// \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.
+///
+/// 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() {}
+
+#ifdef _WIN32
+    virtual void addServerTCPFromFD(SOCKET fd, int af) = 0;
+    virtual void addServerUDPFromFD(SOCKET fd, int af,
+                                    ServerFlag options = SERVER_DEFAULT) = 0;
+#else
+    virtual void addServerTCPFromFD(int fd, int af) = 0;
+    virtual void addServerUDPFromFD(int fd, int af,
+                                    ServerFlag options = SERVER_DEFAULT) = 0;
+#endif
+    virtual void clearServers() = 0;
+
+    virtual asiolink::IOService& getIOService() = 0;
+};
+
 /// \brief Handle DNS Queries
 ///
 /// DNSService is the service that handles DNS queries and answers with
@@ -34,7 +100,7 @@ class DNSServiceImpl;
 /// 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
     ///
@@ -45,49 +111,81 @@ 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.
-    ///
-    /// \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.
+    /// \brief The constructor without any servers.
     ///
-    /// 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.
+    /// Use addServerTCPFromFD() or addServerUDPFromFD() to add some servers.
     ///
     /// \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.
-    ///
-    /// Use addServer() to add some servers.
-    DNSService(asiolink::IOService& io_service, isc::asiolink::SimpleCallback* checkin,
+    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);
-    void addServer(const char &port, const std::string &address);
+    /// \brief Add another TCP server/listener to the service from already
+    /// opened file descriptor
+    ///
+    /// Adds a new TCP server using an already opened file descriptor (eg. it
+    /// only wraps it so the file descriptor is usable within the event loop).
+    /// The file descriptor must be associated with a TCP socket of the given
+    /// address family that is bound to an appropriate port (and possibly a
+    /// 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.
+#ifdef _WIN32
+    virtual void addServerTCPFromFD(SOCKET fd, int af);
+#else
+    virtual void addServerTCPFromFD(int fd, int af);
+#endif
+
+    /// \brief Add another UDP server to the service from already opened
+    ///    file descriptor
+    ///
+    /// Adds a new UDP server using an already opened file descriptor (eg. it
+    /// only wraps it so the file descriptor is usable within the event loop).
+    /// The file descriptor must be associated with a UDP socket of the given
+    /// address family that is bound to an appropriate port (and possibly a
+    /// specific address).
+    ///
+    /// \param fd the file descriptor to be used.
+    /// \param af the address family of the file descriptor. Must be either
+    ///     AF_INET or AF_INET6.
+    /// \param options Optional properties of the server (see ServerFlag).
+    ///
+    /// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6,
+    ///     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.
+#ifdef _WIN32
+    virtual void addServerUDPFromFD(SOCKET fd, int af,
+                                    ServerFlag options = SERVER_DEFAULT);
+#else
+    virtual void addServerUDPFromFD(int fd, int af,
+                                    ServerFlag options = SERVER_DEFAULT);
+#endif
+
     /// \brief Remove all servers from the service
     void clearServers();
 
@@ -102,7 +200,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_;
@@ -112,3 +210,7 @@ private:
 } // namespace asiodns
 } // namespace isc
 #endif // __ASIOLINK_DNS_SERVICE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
index db14b00..e6c931a 100644
--- a/src/lib/asiodns/io_fetch.cc
+++ b/src/lib/asiodns/io_fetch.cc
@@ -19,9 +19,9 @@
 #include <ws2tcpip.h>
 #include <mswsock.h>
 #else
+#include <unistd.h>             // for some IPC/network system calls
 #include <netinet/in.h>
 #include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
 #endif
 
 #include <boost/bind.hpp>
@@ -43,15 +43,14 @@
 #include <dns/messagerenderer.h>
 #include <dns/opcode.h>
 #include <dns/rcode.h>
-#include <log/logger.h>
-#include <log/macros.h>
 
-#include <asiodns/asiodns_messages.h>
 #include <asiodns/io_fetch.h>
 
 #include <util/buffer.h>
 #include <util/random/qid_gen.h>
 
+#include <asiodns/logger.h>
+
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable: 4351)
@@ -68,19 +67,11 @@ using namespace std;
 namespace isc {
 namespace asiodns {
 
-/// Use the ASIO logger
-
-namespace {
-
-isc::log::Logger logger("asiolink");
 // Log debug verbosity
-enum {
-    DBG_IMPORTANT = 1,
-    DBG_COMMON = 20,
-    DBG_ALL = 50
-};
 
-}
+const int DBG_IMPORTANT = DBGLVL_TRACE_BASIC;
+const int DBG_COMMON = DBGLVL_TRACE_DETAIL;
+const int DBG_ALL = DBGLVL_TRACE_DETAIL + 20;
 
 /// \brief IOFetch Data
 ///
@@ -188,12 +179,12 @@ struct IOFetchData {
 /// IOFetch Constructor - just initialize the private data
 
 IOFetch::IOFetch(Protocol protocol, IOService& service,
-    const isc::dns::Question& question, const IOAddress& address, uint16_t port,
-    OutputBufferPtr& buff, Callback* cb, int wait)
+    const isc::dns::Question& question, const IOAddress& address,
+    uint16_t port, OutputBufferPtr& buff, Callback* cb, int wait, bool edns)
 {
     MessagePtr query_msg(new Message(Message::RENDER));
     initIOFetch(query_msg, protocol, service, question, address, port, buff,
-                cb, wait);
+                cb, wait, edns);
 }
 
 IOFetch::IOFetch(Protocol protocol, IOService& service,
@@ -224,10 +215,11 @@ IOFetch::IOFetch(Protocol protocol, IOService& service,
 }
 
 void
-IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& service,
+IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol,
+                     IOService& service,
                      const isc::dns::Question& question,
                      const IOAddress& address, uint16_t port,
-                     OutputBufferPtr& buff, Callback* cb, int wait)
+                     OutputBufferPtr& buff, Callback* cb, int wait, bool edns)
 {
     data_ = boost::shared_ptr<IOFetchData>(new IOFetchData(
         protocol, service, address, port, buff, cb, wait));
@@ -237,11 +229,17 @@ IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& servic
     query_msg->setRcode(Rcode::NOERROR());
     query_msg->setHeaderFlag(Message::HEADERFLAG_RD);
     query_msg->addQuestion(question);
-    EDNSPtr edns_query(new EDNS());
-    edns_query->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
-    query_msg->setEDNS(edns_query);
-    MessageRenderer renderer(*data_->msgbuf);
+
+    if (edns) {
+        EDNSPtr edns_query(new EDNS());
+        edns_query->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+        query_msg->setEDNS(edns_query);
+    }
+
+    MessageRenderer renderer;
+    renderer.setBuffer(data_->msgbuf.get());
     query_msg->toWire(renderer);
+    renderer.setBuffer(NULL);
 }
 
 // Return protocol in use.
@@ -368,10 +366,6 @@ IOFetch::stop(Result result) {
         // variable should be done inside a mutex (and the stopped_ variable
         // declared as "volatile").
         //
-        // The numeric arguments indicate the debug level, with the lower
-        // numbers indicating the most important information.  The relative
-        // values are somewhat arbitrary.
-        //
         // TODO: Update testing of stopped_ if threads are used.
         data_->stopped = true;
         switch (result) {
diff --git a/src/lib/asiodns/io_fetch.h b/src/lib/asiodns/io_fetch.h
index ee0aacd..78c2da9 100644
--- a/src/lib/asiodns/io_fetch.h
+++ b/src/lib/asiodns/io_fetch.h
@@ -131,11 +131,14 @@ public:
     ///        and deleting it if necessary.
     /// \param wait Timeout for the fetch (in ms).  The default value of
     ///        -1 indicates no timeout.
+    /// \param edns true if the request should be EDNS. The default value is
+    ///        true.
     IOFetch(Protocol protocol, isc::asiolink::IOService& service,
         const isc::dns::Question& question,
         const isc::asiolink::IOAddress& address,
         uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
-        int wait = -1);
+        int wait = -1,
+        bool edns = true);
 
     /// \brief Constructor
     ///  This constructor has one parameter "query_message", which
@@ -206,7 +209,8 @@ private:
     void initIOFetch(isc::dns::MessagePtr& query_message, Protocol protocol,
             isc::asiolink::IOService& service, const isc::dns::Question& question,
             const isc::asiolink::IOAddress& address, uint16_t port,
-            isc::util::OutputBufferPtr& buff, Callback* cb, int wait);
+            isc::util::OutputBufferPtr& buff, Callback* cb, int wait,
+            bool edns = true);
 
     /// \brief Log I/O Failure
     ///
diff --git a/src/lib/asiodns/logger.cc b/src/lib/asiodns/logger.cc
new file mode 100644
index 0000000..5801a0b
--- /dev/null
+++ b/src/lib/asiodns/logger.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2012  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 <asiodns/logger.h>
+
+namespace isc {
+namespace asiodns {
+
+/// Use the ASIO logger
+
+isc::log::Logger logger("asiodns");
+
+}
+}
diff --git a/src/lib/asiodns/logger.h b/src/lib/asiodns/logger.h
new file mode 100644
index 0000000..306d463
--- /dev/null
+++ b/src/lib/asiodns/logger.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2012  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 <log/logger.h>
+#include <log/macros.h>
+#include <log/log_dbglevels.h>
+#include <asiodns/asiodns_messages.h>
+
+namespace isc {
+namespace asiodns {
+
+extern isc::log::Logger logger;
+
+}
+}
diff --git a/src/lib/asiodns/sync_udp_server.cc b/src/lib/asiodns/sync_udp_server.cc
new file mode 100644
index 0000000..e6b497b
--- /dev/null
+++ b/src/lib/asiodns/sync_udp_server.cc
@@ -0,0 +1,202 @@
+// Copyright (C) 2012  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 <asio.hpp>
+#include <asio/error.hpp>
+
+#include "sync_udp_server.h"
+#include "logger.h"
+
+#include <asiolink/dummy_io_cb.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+
+#include <boost/bind.hpp>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+#endif
+#include <errno.h>
+
+using namespace std;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace asiodns {
+
+SyncUDPServer::SyncUDPServer(asio::io_service& io_service,
+#ifdef _WIN32
+                             const SOCKET fd,
+#else
+                             const int fd,
+#endif
+                             const int af, 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)
+{
+    if (af != AF_INET && af != AF_INET6) {
+        isc_throw(InvalidParameter, "Address family must be either AF_INET "
+                  "or AF_INET6, not " << af);
+    }
+    LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_UDP).arg(fd);
+    try {
+        socket_.reset(new asio::ip::udp::socket(io_service));
+        socket_->assign(af == AF_INET6 ? asio::ip::udp::v6() :
+                        asio::ip::udp::v4(), fd);
+    } catch (const std::exception& exception) {
+        // Whatever the thing throws, it is something from ASIO and we
+        // convert it
+        isc_throw(IOError, exception.what());
+    }
+}
+
+void
+SyncUDPServer::scheduleRead() {
+    socket_->async_receive_from(asio::buffer(data_, MAX_LENGTH), sender_,
+                                boost::bind(&SyncUDPServer::handleRead, this,
+                                            _1, _2));
+}
+
+void
+SyncUDPServer::handleRead(const asio::error_code& ec, const size_t length) {
+    // Abort on fatal errors
+    if (ec) {
+        using namespace asio::error;
+        if (ec.value() != would_block && ec.value() != try_again &&
+            ec.value() != interrupted) {
+            return;
+        }
+    }
+    // Some kind of interrupt, spurious wakeup, or like that. Just try reading
+    // again.
+    if (ec || length == 0) {
+        scheduleRead();
+        return;
+    }
+    // OK, we have a real packet of data. Let's dig into it!
+
+    // XXX: This is taken (and ported) from UDPSocket class. What the hell does
+    // it really mean?
+
+    // The UDP socket class has been extended with asynchronous functions
+    // and takes as a template parameter a completion callback class.  As
+    // UDPServer does not use these extended functions (only those defined
+    // in the IOSocket base class) - but needs a UDPSocket to get hold of
+    // the underlying Boost UDP socket - DummyIOCallback is used.  This
+    // provides the appropriate operator() but is otherwise functionless.
+    UDPSocket<DummyIOCallback> socket(*socket_);
+    UDPEndpoint endpoint(sender_);
+    IOMessage message(data_, length, socket, endpoint);
+    if (checkin_callback_ != NULL) {
+        (*checkin_callback_)(message);
+        if (stopped_) {
+            return;
+        }
+    }
+
+    // If we don't have a DNS Lookup provider, there's no point in
+    // continuing; we exit the coroutine permanently.
+    if (lookup_callback_ == NULL) {
+        scheduleRead();
+        return;
+    }
+
+    // Make sure the buffers are fresh
+    output_buffer_->clear();
+    query_->clear(isc::dns::Message::PARSE);
+    answer_->clear(isc::dns::Message::RENDER);
+
+    // Mark that we don't have an answer yet.
+    done_ = false;
+    resume_called_ = false;
+
+    // Call the actual lookup
+    (*lookup_callback_)(message, query_, answer_, output_buffer_, this);
+
+    if (!resume_called_) {
+        isc_throw(isc::Unexpected,
+                  "No resume called from the lookup callback");
+    }
+
+    if (stopped_) {
+        return;
+    }
+
+    if (done_) {
+        // Good, there's an answer.
+        // Call the answer callback to render it.
+        (*answer_callback_)(message, query_, answer_, output_buffer_);
+
+        if (stopped_) {
+            return;
+        }
+
+        socket_->send_to(asio::buffer(output_buffer_->getData(),
+                                      output_buffer_->getLength()),
+                         sender_);
+    }
+
+    // And schedule handling another socket.
+    scheduleRead();
+}
+
+void
+SyncUDPServer::operator()(asio::error_code, size_t) {
+    // To start the server, we just schedule reading of data when they
+    // arrive.
+    scheduleRead();
+}
+
+/// Stop the UDPServer
+void
+SyncUDPServer::stop() {
+    /// Using close instead of cancel, because cancel
+    /// will only cancel the asynchornized event already submitted
+    /// to io service, the events post to io service after
+    /// cancel still can be scheduled by io service, if
+    /// the socket is cloesed, all the asynchronized event
+    /// for it won't be scheduled by io service not matter it is
+    /// submit to io serice before or after close call. And we will
+    //. get bad_descriptor error
+    socket_->close();
+    stopped_ = true;
+}
+
+/// Post this coroutine on the ASIO service queue so that it will
+/// resume processing where it left off.  The 'done' parameter indicates
+/// whether there is an answer to return to the client.
+void
+SyncUDPServer::resume(const bool done) {
+    resume_called_ = true;
+    done_ = done;
+}
+
+bool
+SyncUDPServer::hasAnswer() {
+    return (done_);
+}
+
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/asiodns/sync_udp_server.h b/src/lib/asiodns/sync_udp_server.h
new file mode 100644
index 0000000..8358d28
--- /dev/null
+++ b/src/lib/asiodns/sync_udp_server.h
@@ -0,0 +1,154 @@
+// Copyright (C) 2012  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.
+
+#ifndef __SYNC_UDP_SERVER_H
+#define __SYNC_UDP_SERVER_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include "dns_answer.h"
+#include "dns_lookup.h"
+#include "dns_server.h"
+
+#include <dns/message.h>
+#include <asiolink/simple_callback.h>
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace asiodns {
+
+/// \brief An UDP server that doesn't asynchronous lookup handlers.
+///
+/// That means, the lookup handler must provide the answer right away.
+/// This allows for implementation with less overhead, compared with
+/// the UDPClass.
+class SyncUDPServer : public DNSServer, public boost::noncopyable {
+public:
+    /// \brief Constructor
+    /// \param io_service the asio::io_service to work with
+    /// \param fd the file descriptor of opened UDP socket
+    /// \param af address family, either AF_INET or AF_INET6
+    /// \param checkin the callbackprovider for non-DNS events
+    /// \param lookup the callbackprovider for DNS lookup events
+    /// \param answer the callbackprovider for DNS answer events
+    /// \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.
+    SyncUDPServer(asio::io_service& io_service,
+#ifdef _WIN32
+                  const SOCKET fd,
+#else
+                  const int fd,
+#endif
+                  const int af,
+                  isc::asiolink::SimpleCallback* checkin = NULL,
+                  DNSLookup* lookup = NULL, DNSAnswer* answer = NULL);
+
+    /// \brief Start the SyncUDPServer.
+    ///
+    /// This is the function operator to keep interface with other server
+    /// classes. They need that because they're coroutines.
+    virtual void operator()(asio::error_code ec = asio::error_code(),
+                    size_t length = 0);
+
+    /// \brief Calls the lookup callback
+    virtual void asyncLookup() {
+        isc_throw(Unexpected,
+                  "SyncUDPServer doesn't support asyncLookup by design, use "
+                  "UDPServer if you need it.");
+    }
+
+    /// \brief Stop the running server
+    /// \note once the server stopped, it can't restart
+    virtual void stop();
+
+    /// \brief Resume operation
+    ///
+    /// Note that unlike other servers, this one expects it to be called
+    /// directly from the lookup callback. If it isn't, the server will
+    /// throw an Unexpected exception (probably to the event loop, which
+    /// would usually lead to termination of the program, but that's OK,
+    /// as it would be serious programmer error).
+    ///
+    /// \param done Set this to true if the lookup action is done and
+    ///        we have an answer
+    virtual void resume(const bool done);
+
+    /// \brief Check if we have an answer
+    ///
+    /// \return true if we have an answer
+    virtual bool hasAnswer();
+
+    /// \brief Clones the object
+    ///
+    /// Since cloning is for the use of coroutines, the synchronous UDP server
+    /// does not need to be cloned. Therefore supporting it would be needless
+    /// work, and trying to clone it would be a programmer error anyway, this
+    /// throws Unexpected.
+    ///
+    /// \return a newly allocated copy of this object
+    virtual DNSServer* clone() {
+        isc_throw(Unexpected, "SyncUDPServer can't be cloned.");
+    }
+private:
+    // Internal state & buffers. We don't use the PIMPL idiom, as this class
+    // isn't usually used directly anyway.
+
+    // Maximum size of incoming UDP packet
+    static const size_t MAX_LENGTH = 4096;
+    // Buffer for incoming data
+    uint8_t data_[MAX_LENGTH];
+    // The buffer to render the output to and send it.
+    // If it was OK to have just a buffer, not the wrapper class,
+    // we could reuse the data_
+    isc::util::OutputBufferPtr output_buffer_;
+    // Objects to hold the query message and the answer
+    isc::dns::MessagePtr query_, answer_;
+    // The socket used for the communication
+    std::auto_ptr<asio::ip::udp::socket> socket_;
+    // The event loop we use
+    asio::io_service& io_;
+    // Place the socket puts the sender of a packet when it is received
+    asio::ip::udp::endpoint sender_;
+    // Callbacks
+    const asiolink::SimpleCallback* checkin_callback_;
+    const DNSLookup* lookup_callback_;
+    const DNSAnswer* answer_callback_;
+    // Answers from the lookup callback (not sent directly, but signalled
+    // through resume()
+    bool resume_called_, done_;
+    // This turns true when the server stops. Allows for not sending the
+    // answer after we closed the socket.
+    bool stopped_;
+
+    // Auxiliary functions
+
+    // Schedule next read on the socket. Just a wrapper around
+    // socket_->async_read_from with the correct parameters.
+    void scheduleRead();
+    // Callback from the socket's read call (called when there's an error or
+    // when a new packet comes).
+    void handleRead(const asio::error_code& ec, const size_t length);
+};
+
+} // namespace asiodns
+} // namespace isc
+#endif // __SYNC_UDP_SERVER_H
diff --git a/src/lib/asiodns/tcp_server.cc b/src/lib/asiodns/tcp_server.cc
index 0c53f21..334306f 100644
--- a/src/lib/asiodns/tcp_server.cc
+++ b/src/lib/asiodns/tcp_server.cc
@@ -18,9 +18,9 @@
 #ifdef _WIN32
 #include <ws2tcpip.h>
 #else
+#include <unistd.h>             // for some IPC/network system calls
 #include <netinet/in.h>
 #include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
 #endif
 #include <errno.h>
 
@@ -34,8 +34,8 @@
 #include <asiolink/dummy_io_cb.h>
 #include <asiolink/tcp_endpoint.h>
 #include <asiolink/tcp_socket.h>
-#include <tcp_server.h>
-
+#include <asiodns/tcp_server.h>
+#include <asiodns/logger.h>
 
 using namespace asio;
 using asio::ip::udp;
@@ -53,7 +53,12 @@ namespace asiodns {
 ///
 /// The constructor
 TCPServer::TCPServer(io_service& io_service,
-                     const ip::address& addr, const uint16_t port, 
+#ifdef _WIN32
+                     SOCKET fd,
+#else
+                     int fd,
+#endif
+                     int af,
                      const SimpleCallback* checkin,
                      const DNSLookup* lookup,
                      const DNSAnswer* answer) :
@@ -61,19 +66,21 @@ TCPServer::TCPServer(io_service& io_service,
     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));
+    if (af != AF_INET && af != AF_INET6) {
+        isc_throw(InvalidParameter, "Address family must be either AF_INET "
+                  "or AF_INET6, not " << af);
+    }
+    LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_TCP).arg(fd);
+
+    try {
+        acceptor_.reset(new tcp::acceptor(io_service));
+        acceptor_->assign(af == AF_INET6 ? tcp::v6() : tcp::v4(), fd);
+        acceptor_->listen();
+    } catch (const std::exception& exception) {
+        // Whatever the thing throws, it is something from ASIO and we convert
+        // it
+        isc_throw(IOError, exception.what());
     }
-#ifndef _WIN32
-    acceptor_->set_option(tcp::acceptor::reuse_address(true));
-#endif
-    acceptor_->bind(endpoint);
-    acceptor_->listen();
 }
 
 void
diff --git a/src/lib/asiodns/tcp_server.h b/src/lib/asiodns/tcp_server.h
index f87a6bb..66935a0 100644
--- a/src/lib/asiodns/tcp_server.h
+++ b/src/lib/asiodns/tcp_server.h
@@ -37,20 +37,31 @@ namespace asiodns {
 /// defined in coroutine.h. 
 class TCPServer : public virtual DNSServer, public virtual coroutine {
 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
+    /// \param io_service the asio::io_service to work with
+    /// \param fd the file descriptor of opened TCP socket
+    /// \param af address family of the socket, either AF_INET or AF_INET6
+    /// \param checkin the callbackprovider for non-DNS events
+    /// \param lookup the callbackprovider for DNS lookup events
+    /// \param answer the callbackprovider for DNS answer events
+    /// \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.
+    TCPServer(asio::io_service& io_service,
+#ifdef _WIN32
+              SOCKET fd,
+#else
+              int fd,
+#endif
+              int af,
+              const isc::asiolink::SimpleCallback* checkin = NULL,
+              const DNSLookup* lookup = NULL, const DNSAnswer* answer = NULL);
 
     void operator()(asio::error_code ec = asio::error_code(),
                     size_t length = 0);
     void asyncLookup();
     void stop();
     void resume(const bool done);
-    bool hasAnswer() { return (done_); }
-    int value() { return (get_value()); }
-
     DNSServer* clone() {
         TCPServer* s = new TCPServer(*this);
         return (s);
@@ -117,8 +128,6 @@ private:
 
     boost::shared_ptr<isc::asiolink::IOEndpoint> peer_;
     boost::shared_ptr<isc::asiolink::IOSocket> iosock_;
-    // silence MSVC warning C4512: assignment operator could not be generated
-    TCPServer& operator=(TCPServer const&);
 };
 
 } // namespace asiodns
diff --git a/src/lib/asiodns/tests/.gitignore b/src/lib/asiodns/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/asiodns/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/asiodns/tests/Makefile.am b/src/lib/asiodns/tests/Makefile.am
index f49d485..1e18645 100644
--- a/src/lib/asiodns/tests/Makefile.am
+++ b/src/lib/asiodns/tests/Makefile.am
@@ -12,13 +12,16 @@ endif
 
 CLEANFILES = *.gcno *.gcda
 
+TESTS_ENVIRONMENT = \
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
 TESTS =
 if HAVE_GTEST
 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
 
diff --git a/src/lib/asiodns/tests/dns_server_unittest.cc b/src/lib/asiodns/tests/dns_server_unittest.cc
index 445b0d1..39c6a53 100644
--- a/src/lib/asiodns/tests/dns_server_unittest.cc
+++ b/src/lib/asiodns/tests/dns_server_unittest.cc
@@ -20,10 +20,13 @@
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_error.h>
 #include <asiodns/udp_server.h>
+#include <asiodns/sync_udp_server.h>
 #include <asiodns/tcp_server.h>
 #include <asiodns/dns_answer.h>
 #include <asiodns/dns_lookup.h>
 #include <string>
+#include <cstring>
+#include <cerrno>
 #include <csignal>
 #ifndef _WIN32
 #include <unistd.h> //for alarm
@@ -33,6 +36,8 @@
 #include <boost/bind.hpp>
 #include <boost/function.hpp>
 
+#include <sys/types.h>
+#include <sys/socket.h>
 
 /// The following tests focus on stop interface for udp and
 /// tcp server, there are lots of things can be shared to test
@@ -73,11 +78,12 @@ using namespace isc::asiodns;
 using namespace asio;
 
 namespace {
-static const std::string server_ip = "127.0.0.1";
+const char* const server_ip = "::1";
 const int server_port = 5553;
+const char* const server_port_str = "5553";
 //message client send to udp server, which isn't dns package
 //just for simple testing
-static const std::string query_message("BIND10 is awesome");
+const char* const query_message = "BIND10 is awesome";
 
 // \brief provide capacity to derived class the ability
 // to stop DNSServer at certern point
@@ -110,15 +116,22 @@ class DummyChecker : public SimpleCallback, public ServerStopper {
 
 // \brief no lookup logic at all,just provide a checkpoint to stop the server
 class DummyLookup : public DNSLookup, public ServerStopper {
-    public:
-        void operator()(const IOMessage& io_message,
-                isc::dns::MessagePtr message,
-                isc::dns::MessagePtr answer_message,
-                isc::util::OutputBufferPtr buffer,
-                DNSServer* server) const {
-            stopServer();
+public:
+    DummyLookup() :
+        allow_resume_(true)
+    { }
+    void operator()(const IOMessage& io_message,
+            isc::dns::MessagePtr message,
+            isc::dns::MessagePtr answer_message,
+            isc::util::OutputBufferPtr buffer,
+            DNSServer* server) const {
+        stopServer();
+        if (allow_resume_) {
             server->resume(true);
         }
+    }
+    // If you want it not to call resume, set this to false
+    bool allow_resume_;
 };
 
 // \brief copy the data received from user to the answer part
@@ -203,15 +216,15 @@ class SimpleClient : public ServerStopper {
 
 class UDPClient : public SimpleClient {
     public:
-    //After 1 seconds without feedback client will stop wait
-    static const unsigned int server_time_out = 1;
+    //After 1 second without feedback client will stop wait
+    static const unsigned int SERVER_TIME_OUT = 1;
 
     UDPClient(asio::io_service& service, const ip::udp::endpoint& server) :
-        SimpleClient(service, server_time_out)
+        SimpleClient(service, SERVER_TIME_OUT)
     {
         server_ = server;
         socket_.reset(new ip::udp::socket(service));
-        socket_->open(ip::udp::v4());
+        socket_->open(ip::udp::v6());
     }
 
 
@@ -246,13 +259,13 @@ class TCPClient : public SimpleClient {
     public:
     // after 2 seconds without feedback client will stop wait,
     // this includes connect, send message and recevice message
-    static const unsigned int server_time_out = 2;
+    static const unsigned int SERVER_TIME_OUT = 2;
     TCPClient(asio::io_service& service, const ip::tcp::endpoint& server)
-        : SimpleClient(service, server_time_out)
+        : SimpleClient(service, SERVER_TIME_OUT)
     {
         server_ = server;
         socket_.reset(new ip::tcp::socket(service));
-        socket_->open(ip::tcp::v4());
+        socket_->open(ip::tcp::v6());
     }
 
 
@@ -308,33 +321,41 @@ class TCPClient : public SimpleClient {
     uint16_t data_to_send_len_;
 };
 
-
-
-// \brief provide the context which including two client and
-// two server, udp client will only communicate with udp server, same for tcp client
-class DNSServerTest : public::testing::Test {
+// \brief provide the context which including two clients and
+// two servers, UDP client will only communicate with UDP server, same for TCP
+// client
+//
+// This is only the active part of the test. We run the test case four times, once
+// for each type of initialization (once when giving it the address and port,
+// once when giving the file descriptor) multiplied by once for each type of UDP
+// server (UDPServer and SyncUDPServer), to ensure it works exactly the same.
+template<class UDPServerClass>
+class DNSServerTestBase : public::testing::Test {
     protected:
-        void SetUp() {
-            ip::address server_address = ip::address::from_string(server_ip);
-            checker_ = new DummyChecker();
-            lookup_ = new DummyLookup();
-            answer_ = new SimpleAnswer();
-            udp_server_ = new UDPServer(service, server_address, server_port,
-                    checker_, lookup_, answer_);
-            udp_client_ = new UDPClient(service,
-                    ip::udp::endpoint(server_address,
-                        server_port));
-            tcp_server_ = new TCPServer(service, server_address, server_port,
-                    checker_, lookup_, answer_);
-            tcp_client_ = new TCPClient(service,
-                    ip::tcp::endpoint(server_address,
-                        server_port));
+        DNSServerTestBase() :
+            server_address_(ip::address::from_string(server_ip)),
+            checker_(new DummyChecker()),
+            lookup_(new DummyLookup()),
+            answer_(new SimpleAnswer()),
+            udp_client_(new UDPClient(service,
+                                      ip::udp::endpoint(server_address_,
+                                                         server_port))),
+            tcp_client_(new TCPClient(service,
+                                      ip::tcp::endpoint(server_address_,
+                                                        server_port))),
+            udp_server_(NULL),
+            tcp_server_(NULL)
+        {
+            current_service = &service;
         }
 
-
-        void TearDown() {
-            udp_server_->stop();
-            tcp_server_->stop();
+        ~ DNSServerTestBase() {
+            if (udp_server_ != NULL) {
+                udp_server_->stop();
+            }
+            if (tcp_server_ != NULL) {
+                tcp_server_->stop();
+            }
             delete checker_;
             delete lookup_;
             delete answer_;
@@ -342,31 +363,36 @@ class DNSServerTest : public::testing::Test {
             delete udp_client_;
             delete tcp_server_;
             delete tcp_client_;
+            // No delete here. The service is not allocated by new, but as our
+            // member. This only references it, so just cleaning the pointer.
+            current_service = NULL;
         }
 
-
         void testStopServerByStopper(DNSServer* server, SimpleClient* client,
                 ServerStopper* stopper)
         {
-            static const unsigned int io_service_time_out = 5;
+            static const unsigned int IO_SERVICE_TIME_OUT = 5;
             io_service_is_time_out = false;
             stopper->setServerToStop(server);
             (*server)();
             client->sendDataThenWaitForFeedback(query_message);
-            // Since thread hasn't been introduced into the tool box, using signal
-            // to make sure run function will eventually return even server stop
-            // failed
+            // Since thread hasn't been introduced into the tool box, using
+            // signal to make sure run function will eventually return even
+            // server stop failed
 #ifdef _WIN32
             UINT_PTR id = 1;
-            SetTimer(NULL, id, io_service_time_out * 1000,
+            SetTimer(NULL, id, IO_SERVICE_TIME_OUT * 1000,
                      DNSServerTest::stopIOService);
+            current_service = &service;
             service.run();
             service.reset();
             //cancel scheduled alarm
             KillTimer(NULL, id);
 #else
-            void (*prev_handler)(int) = std::signal(SIGALRM, DNSServerTest::stopIOService);
-            alarm(io_service_time_out);
+            void (*prev_handler)(int) =
+                std::signal(SIGALRM, DNSServerTestBase::stopIOService);
+            current_service = &service;
+            alarm(IO_SERVICE_TIME_OUT);
             service.run();
             service.reset();
             //cancel scheduled alarm
@@ -375,7 +401,6 @@ class DNSServerTest : public::testing::Test {
 #endif
         }
 
-
 #ifdef _WIN32
         static void CALLBACK stopIOService(
             HWND _no_use_hwnd,
@@ -383,75 +408,189 @@ class DNSServerTest : public::testing::Test {
             UINT_PTR _no_use_idevent,
             DWORD _no_use_dwtime)
 #else
-        static void stopIOService(int _no_use_parameter)
+        static void stopIOService(int _no_use_parameter) {
 #endif
-        {
             io_service_is_time_out = true;
-            service.stop();
+            if (current_service != NULL) {
+                current_service->stop();
+            }
         }
 
         bool serverStopSucceed() const {
             return (!io_service_is_time_out);
         }
 
-        DummyChecker* checker_;
-        DummyLookup*  lookup_;
-        SimpleAnswer* answer_;
-        UDPServer*    udp_server_;
-        UDPClient*    udp_client_;
-        TCPClient*    tcp_client_;
+        asio::io_service service;
+        const ip::address server_address_;
+        DummyChecker* const checker_;
+        DummyLookup*  const lookup_;
+        SimpleAnswer* const answer_;
+        UDPClient*    const udp_client_;
+        TCPClient*    const tcp_client_;
+        UDPServerClass* udp_server_;
         TCPServer*    tcp_server_;
 
         // To access them in signal handle function, the following
         // variables have to be static.
-        static asio::io_service service;
+        static asio::io_service* current_service;
         static bool io_service_is_time_out;
 };
 
-bool DNSServerTest::io_service_is_time_out = false;
-asio::io_service DNSServerTest::service;
+// Initialization (by the file descriptor)
+template<class UDPServerClass>
+class FdInit : public DNSServerTestBase<UDPServerClass> {
+private:
+    // Opens the file descriptor for us
+    // It uses the low-level C api, as it seems to be the easiest way to get
+    // a raw file descriptor. It also is what the socket creator does and this
+    // API is aimed to it.
+#ifdef _WIN32
+    SOCKET getFd(int type)
+#else
+    int getFd(int type)
+#endif
+        {
+        struct addrinfo hints;
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_family = AF_UNSPEC;
+        hints.ai_socktype = type;
+        hints.ai_protocol = (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
+        hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
+
+        struct addrinfo* res;
+        const int error = getaddrinfo(server_ip, server_port_str,
+                                      &hints, &res);
+        if (error != 0) {
+            isc_throw(IOError, "getaddrinfo failed: " << gai_strerror(error));
+        }
+
+#ifdef _WIN32
+        SOCKET sock;
+#else
+        int sock;
+#endif
+        // Go as far as you can and stop on failure
+        // Create the socket
+        // set the options
+        // and bind it
+#ifdef _WIN32
+        const bool failed((sock = socket(res->ai_family, res->ai_socktype,
+                                         res->ai_protocol)) == INVALID_SOCKET ||
+                          bind(sock, res->ai_addr, res->ai_addrlen) == -1);
+#else
+        const int on(1);
+        const bool failed((sock = socket(res->ai_family, res->ai_socktype,
+                                         res->ai_protocol)) == -1 ||
+                          setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
+                                     sizeof(on)) == -1 ||
+                          bind(sock, res->ai_addr, res->ai_addrlen) == -1);
+#endif
+        // No matter if it succeeded or not, free the address info
+        freeaddrinfo(res);
+        if (failed) {
+#ifdef _WIN32
+            if (sock != INVALID_SOCKET) {
+                closesocket(sock);
+            }
+#else
+            if (sock != -1) {
+                close(sock);
+            }
+#endif
+            return (-1);
+        } else {
+            return (sock);
+        }
+    }
+protected:
+    // Using SetUp here so we can ASSERT_*
+    void SetUp() {
+#ifdef _WIN32
+        const SOCKET fdUDP(getFd(SOCK_DGRAM));
+        ASSERT_NE(INVALID_SOCKET, fdUDP) << strerror(errno);
+#else
+        const int fdUDP(getFd(SOCK_DGRAM));
+        ASSERT_NE(-1, fdUDP) << strerror(errno);
+#endif
+        this->udp_server_ = new UDPServerClass(this->service, fdUDP, AF_INET6,
+                                               this->checker_, this->lookup_,
+                                               this->answer_);
+#ifdef _WIN32
+        const int fdTCP(getFd(SOCK_STREAM));
+        ASSERT_NE(-1, fdTCP) << strerror(errno);
+#else
+        const int fdTCP(getFd(SOCK_STREAM));
+        ASSERT_NE(-1, fdTCP) << strerror(errno);
+#endif
+        this->tcp_server_ = new TCPServer(this->service, fdTCP, AF_INET6,
+                                          this->checker_, this->lookup_,
+                                          this->answer_);
+    }
+};
+
+// This makes it the template as gtest wants it.
+template<class Parent>
+class DNSServerTest : public Parent { };
+
+typedef ::testing::Types<FdInit<UDPServer>, FdInit<SyncUDPServer> >
+    ServerTypes;
+TYPED_TEST_CASE(DNSServerTest, ServerTypes);
+
+typedef ::testing::Types<UDPServer, SyncUDPServer> UDPServerTypes;
+TYPED_TEST_CASE(DNSServerTestBase, UDPServerTypes);
+
+template<class UDPServerClass>
+bool DNSServerTestBase<UDPServerClass>::io_service_is_time_out = false;
+template<class UDPServerClass>
+asio::io_service* DNSServerTestBase<UDPServerClass>::current_service(NULL);
 
 // Test whether server stopped successfully after client get response
 // client will send query and start to wait for response, once client
 // get response, udp server will be stopped, the io service won't quit
 // if udp server doesn't stop successfully.
-TEST_F(DNSServerTest, stopUDPServerAfterOneQuery) {
-    testStopServerByStopper(udp_server_, udp_client_, udp_client_);
-    EXPECT_EQ(query_message, udp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopUDPServerAfterOneQuery) {
+    this->testStopServerByStopper(this->udp_server_, this->udp_client_,
+                                  this->udp_client_);
+    EXPECT_EQ(query_message, this->udp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 // Test whether udp server stopped successfully before server start to serve
-TEST_F(DNSServerTest, stopUDPServerBeforeItStartServing) {
-    udp_server_->stop();
-    testStopServerByStopper(udp_server_, udp_client_, udp_client_);
-    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopUDPServerBeforeItStartServing) {
+    this->udp_server_->stop();
+    this->testStopServerByStopper(this->udp_server_, this->udp_client_,
+                                  this->udp_client_);
+    EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 
 // Test whether udp server stopped successfully during message check
-TEST_F(DNSServerTest, stopUDPServerDuringMessageCheck) {
-    testStopServerByStopper(udp_server_, udp_client_, checker_);
-    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopUDPServerDuringMessageCheck) {
+    this->testStopServerByStopper(this->udp_server_, this->udp_client_,
+                                  this->checker_);
+    EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 // Test whether udp server stopped successfully during query lookup
-TEST_F(DNSServerTest, stopUDPServerDuringQueryLookup) {
-    testStopServerByStopper(udp_server_, udp_client_, lookup_);
-    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopUDPServerDuringQueryLookup) {
+    this->testStopServerByStopper(this->udp_server_, this->udp_client_,
+                                  this->lookup_);
+    EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 // Test whether udp server stopped successfully during composing answer
-TEST_F(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
-    testStopServerByStopper(udp_server_, udp_client_, answer_);
-    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
+    this->testStopServerByStopper(this->udp_server_, this->udp_client_,
+                                  this->answer_);
+    EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
-static void stopServerManyTimes(DNSServer *server, unsigned int times) {
+void
+stopServerManyTimes(DNSServer *server, unsigned int times) {
     for (unsigned int i = 0; i < times; ++i) {
         server->stop();
     }
@@ -459,67 +598,134 @@ static void stopServerManyTimes(DNSServer *server, unsigned int times) {
 
 // Test whether udp server stop interface can be invoked several times without
 // throw any exception
-TEST_F(DNSServerTest, stopUDPServeMoreThanOnce) {
+TYPED_TEST(DNSServerTest, stopUDPServeMoreThanOnce) {
     ASSERT_NO_THROW({
         boost::function<void()> stop_server_3_times
-            = boost::bind(stopServerManyTimes, udp_server_, 3);
-        udp_client_->setGetFeedbackCallback(stop_server_3_times);
-        testStopServerByStopper(udp_server_, udp_client_, udp_client_);
-        EXPECT_EQ(query_message, udp_client_->getReceivedData());
+            = boost::bind(stopServerManyTimes, this->udp_server_, 3);
+        this->udp_client_->setGetFeedbackCallback(stop_server_3_times);
+        this->testStopServerByStopper(this->udp_server_,
+                                      this->udp_client_, this->udp_client_);
+        EXPECT_EQ(query_message, this->udp_client_->getReceivedData());
     });
-    EXPECT_TRUE(serverStopSucceed());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 
-TEST_F(DNSServerTest, stopTCPServerAfterOneQuery) {
-    testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
-    EXPECT_EQ(query_message, tcp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopTCPServerAfterOneQuery) {
+    this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
+                                  this->tcp_client_);
+    EXPECT_EQ(query_message, this->tcp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 
 // Test whether tcp server stopped successfully before server start to serve
-TEST_F(DNSServerTest, stopTCPServerBeforeItStartServing) {
-    tcp_server_->stop();
-    testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
-    EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopTCPServerBeforeItStartServing) {
+    this->tcp_server_->stop();
+    this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
+                                  this->tcp_client_);
+    EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 
 // Test whether tcp server stopped successfully during message check
-TEST_F(DNSServerTest, stopTCPServerDuringMessageCheck) {
-    testStopServerByStopper(tcp_server_, tcp_client_, checker_);
-    EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopTCPServerDuringMessageCheck) {
+    this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
+                                  this->checker_);
+    EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 // Test whether tcp server stopped successfully during query lookup
-TEST_F(DNSServerTest, stopTCPServerDuringQueryLookup) {
-    testStopServerByStopper(tcp_server_, tcp_client_, lookup_);
-    EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopTCPServerDuringQueryLookup) {
+    this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
+                                  this->lookup_);
+    EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 // Test whether tcp server stopped successfully during composing answer
-TEST_F(DNSServerTest, stopTCPServerDuringPrepareAnswer) {
-    testStopServerByStopper(tcp_server_, tcp_client_, answer_);
-    EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
-    EXPECT_TRUE(serverStopSucceed());
+TYPED_TEST(DNSServerTest, stopTCPServerDuringPrepareAnswer) {
+    this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
+                                  this->answer_);
+    EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
+    EXPECT_TRUE(this->serverStopSucceed());
 }
 
 
 // Test whether tcp server stop interface can be invoked several times without
 // throw any exception
-TEST_F(DNSServerTest, stopTCPServeMoreThanOnce) {
+TYPED_TEST(DNSServerTest, stopTCPServeMoreThanOnce) {
     ASSERT_NO_THROW({
         boost::function<void()> stop_server_3_times
-            = boost::bind(stopServerManyTimes, tcp_server_, 3);
-        tcp_client_->setGetFeedbackCallback(stop_server_3_times);
-        testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
-        EXPECT_EQ(query_message, tcp_client_->getReceivedData());
+            = boost::bind(stopServerManyTimes, this->tcp_server_, 3);
+        this->tcp_client_->setGetFeedbackCallback(stop_server_3_times);
+        this->testStopServerByStopper(this->tcp_server_, this->tcp_client_,
+                                      this->tcp_client_);
+        EXPECT_EQ(query_message, this->tcp_client_->getReceivedData());
     });
-    EXPECT_TRUE(serverStopSucceed());
+    EXPECT_TRUE(this->serverStopSucceed());
+}
+
+// It raises an exception when invalid address family is passed
+// The parameter here doesn't mean anything
+TYPED_TEST(DNSServerTestBase, invalidFamily) {
+    // We abuse DNSServerTestBase for this test, as we don't need the
+    // initialization.
+    EXPECT_THROW(TypeParam(this->service, 0, AF_UNIX, this->checker_,
+                           this->lookup_, this->answer_),
+                 isc::InvalidParameter);
+    EXPECT_THROW(TCPServer(this->service, 0, AF_UNIX, this->checker_,
+                           this->lookup_, this->answer_),
+                 isc::InvalidParameter);
+}
+
+// It raises an exception when invalid address family is passed
+TYPED_TEST(DNSServerTestBase, invalidTCPFD) {
+    // We abuse DNSServerTestBase for this test, as we don't need the
+    // initialization.
+    /*
+     FIXME: The UDP server doesn't fail reliably with an invalid FD.
+     We need to find a way to trigger it reliably (it seems epoll
+     asio backend does fail as it tries to insert it right away, but
+     not the others, maybe we could make it run this at last on epoll-based
+     systems).
+    EXPECT_THROW(UDPServer(service, -1, AF_INET, checker_, lookup_,
+                           answer_), isc::asiolink::IOError);
+    */
+    EXPECT_THROW(TCPServer(this->service, -1, AF_INET, this->checker_,
+                           this->lookup_, this->answer_),
+                 isc::asiolink::IOError);
+}
+
+TYPED_TEST(DNSServerTestBase, DISABLED_invalidUDPFD) {
+    /*
+     FIXME: The UDP server doesn't fail reliably with an invalid FD.
+     We need to find a way to trigger it reliably (it seems epoll
+     asio backend does fail as it tries to insert it right away, but
+     not the others, maybe we could make it run this at least on epoll-based
+     systems).
+    */
+    EXPECT_THROW(TypeParam(this->service, -1, AF_INET, this->checker_,
+                           this->lookup_, this->answer_),
+                 isc::asiolink::IOError);
+}
+
+// A specialized test type for SyncUDPServer.
+typedef FdInit<SyncUDPServer> SyncServerTest;
+
+// 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)
+TEST_F(SyncServerTest, mustResume) {
+    lookup_->allow_resume_ = false;
+    ASSERT_THROW(testStopServerByStopper(udp_server_, udp_client_, lookup_),
+                 isc::Unexpected);
 }
 
 }
diff --git a/src/lib/asiodns/tests/dns_service_unittest.cc b/src/lib/asiodns/tests/dns_service_unittest.cc
new file mode 100644
index 0000000..4450053
--- /dev/null
+++ b/src/lib/asiodns/tests/dns_service_unittest.cc
@@ -0,0 +1,273 @@
+// 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>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <netdb.h>
+#endif
+
+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_IPV6_ADDR = "::1";
+
+// 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.
+#ifdef _WIN32
+        UINT_PTR id = 1;
+        SetTimer(NULL, id, IO_SERVICE_TIME_OUT * 1000,
+                 UDPDNSServiceTest::stopIOService);
+        current_service = &io_service;
+        io_service.run();
+        io_service.get_io_service().reset();
+        //cancel scheduled alarm
+        KillTimer(NULL, id);
+#else
+        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);
+#endif
+    }
+
+    // last resort service stopper by signal
+#ifdef _WIN32
+        static void CALLBACK stopIOService(
+            HWND _no_use_hwnd,
+            UINT _no_use_umsg,
+            UINT_PTR _no_use_idevent,
+            DWORD _no_use_dwtime)
+#else
+    static void stopIOService(int) {
+#endif
+        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.
+#ifdef _WIN32
+SOCKET
+#else
+int
+#endif
+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;
+#ifdef _WIN32
+    SOCKET s = INVALID_SOCKET;
+#else
+    int s = -1;
+#endif
+    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);
+#ifdef _WIN32
+            if (s != INVALID_SOCKET) {
+                error = bind(s, res->ai_addr, res->ai_addrlen);
+            }
+#else
+            if (s >= 0) {
+                error = bind(s, res->ai_addr, res->ai_addrlen);
+            }
+#endif
+            freeaddrinfo(res);
+        }
+    }
+    if (error != 0) {
+#ifdef _WIN32
+        if (s != INVALID_SOCKET) {
+            closesocket(s);
+        }
+#else
+        if (s >= 0) {
+            close(s);
+        }
+#endif
+        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
diff --git a/src/lib/asiodns/tests/io_fetch_unittest.cc b/src/lib/asiodns/tests/io_fetch_unittest.cc
index 47ff0a6..1d48b45 100644
--- a/src/lib/asiodns/tests/io_fetch_unittest.cc
+++ b/src/lib/asiodns/tests/io_fetch_unittest.cc
@@ -140,10 +140,15 @@ public:
         EDNSPtr msg_edns(new EDNS());
         msg_edns->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
         msg.setEDNS(msg_edns);
-        MessageRenderer renderer(*msgbuf_);
+
+        MessageRenderer renderer;
+        renderer.setBuffer(msgbuf_.get());
+        msg.toWire(renderer);
+        renderer.setBuffer(NULL);
+
+        renderer.setBuffer(expected_buffer_.get());
         msg.toWire(renderer);
-        MessageRenderer renderer2(*expected_buffer_);
-        msg.toWire(renderer2);
+        renderer.setBuffer(NULL);
 
         // Initialize the test data to be returned: tests will return a
         // substring of this data. (It's convenient to have this as a member of
@@ -175,12 +180,11 @@ public:
     ///        mangled qid response has been sent, a second packet will be
     ///        sent with the correct QID.
     /// \param length Amount of data received.
-    void udpReceiveHandler(udp::endpoint* remote,
-                    udp::socket* socket,
-                    asio::error_code ec = asio::error_code(),
-                    size_t length = 0,
-                    bool bad_qid = false,
-                    bool second_send = false) {
+    void udpReceiveHandler(udp::endpoint* remote, udp::socket* socket,
+                           asio::error_code ec = asio::error_code(),
+                           size_t length = 0, bool bad_qid = false,
+                           bool second_send = false)
+    {
         if (debug_) {
             cout << "udpReceiveHandler(): error = " << ec.value() <<
                     ", length = " << length << endl;
@@ -397,8 +401,7 @@ public:
     ///        pass back to the send method.
     /// \param ec Boost error code, value should be zero.
     /// \param length Number of bytes sent.
-    void tcpSendHandler(size_t expected,
-                        tcp::socket* socket,
+    void tcpSendHandler(size_t expected, tcp::socket* socket,
                         asio::error_code ec = asio::error_code(),
                         size_t length = 0)
     {
@@ -590,20 +593,22 @@ public:
         return_data_ = "Message returned to the client";
 
         udp::endpoint remote;
-        socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
-            remote,
-            boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
-                        _1, _2, bad_qid, second_send));
+        socket.async_receive_from(asio::buffer(receive_buffer_,
+                                               sizeof(receive_buffer_)),
+                                  remote,
+                                  boost::bind(&IOFetchTest::udpReceiveHandler,
+                                              this, &remote, &socket,
+                                              _1, _2, bad_qid, second_send));
         service_.get_io_service().post(udp_fetch_);
         if (debug_) {
-            cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
-                    endl;
+            cout << "udpSendReceive: async_receive_from posted,"
+                "waiting for callback" << endl;
         }
         service_.run();
 
         socket.close();
 
-        EXPECT_TRUE(run_);;
+        EXPECT_TRUE(run_);
     }
 };
 
diff --git a/src/lib/asiodns/tests/io_service_unittest.cc b/src/lib/asiodns/tests/io_service_unittest.cc
deleted file mode 100644
index cc64022..0000000
--- a/src/lib/asiodns/tests/io_service_unittest.cc
+++ /dev/null
@@ -1,118 +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;
-}
-
diff --git a/src/lib/asiodns/udp_server.cc b/src/lib/asiodns/udp_server.cc
index 8a37948..1c79dba 100644
--- a/src/lib/asiodns/udp_server.cc
+++ b/src/lib/asiodns/udp_server.cc
@@ -18,9 +18,9 @@
 #ifdef _WIN32
 #include <ws2tcpip.h>
 #else
+#include <unistd.h>             // for some IPC/network system calls
 #include <netinet/in.h>
 #include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
 #endif
 #include <errno.h>
 
@@ -34,6 +34,7 @@
 #include <asiolink/udp_endpoint.h>
 #include <asiolink/udp_socket.h>
 #include "udp_server.h"
+#include "logger.h"
 
 #include <dns/opcode.h>
 
@@ -58,7 +59,7 @@ namespace asiodns {
  */
 struct UDPServer::Data {
     /*
-     * Constructor from parameters passed to UDPServer constructor.
+     * Constructors from parameters passed to UDPServer constructor.
      * This instance will not be used to retrieve and answer the actual
      * query, it will only hold parameters until we wait for the
      * first packet. But we do initialize the socket in here.
@@ -81,6 +82,32 @@ struct UDPServer::Data {
         }
         socket_->bind(udp::endpoint(addr, port));
     }
+    Data(io_service& io_service,
+#ifdef _WIN32
+         SOCKET fd,
+#else
+         int fd,
+#endif
+         int af, SimpleCallback* checkin,
+         DNSLookup* lookup, DNSAnswer* answer) :
+         io_(io_service), done_(false),
+         checkin_callback_(checkin),lookup_callback_(lookup),
+         answer_callback_(answer)
+    {
+        if (af != AF_INET && af != AF_INET6) {
+            isc_throw(InvalidParameter, "Address family must be either AF_INET "
+                      "or AF_INET6, not " << af);
+        }
+        LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_UDP).arg(fd);
+        try {
+            socket_.reset(new udp::socket(io_service));
+            socket_->assign(af == AF_INET6 ? udp::v6() : udp::v4(), fd);
+        } catch (const std::exception& exception) {
+            // Whatever the thing throws, it is something from ASIO and we
+            // convert it
+            isc_throw(IOError, exception.what());
+        }
+    }
 
     /*
      * Copy constructor. Default one would probably do, but it is unnecessary
@@ -162,19 +189,21 @@ struct UDPServer::Data {
 
     std::auto_ptr<IOEndpoint> peer_;
     std::auto_ptr<IOSocket> iosock_;
-private:
-    // silence MSVC warning C4512: assignment operator could not be generated
-    Data& operator=(Data const&);
 };
 
 /// The following functions implement the \c UDPServer class.
 ///
 /// The constructor. It just creates new internal state object
 /// 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,
+#ifdef _WIN32
+                     SOCKET fd,
+#else
+                     int fd,
+#endif
+                     int af, SimpleCallback* checkin, DNSLookup* lookup,
+                     DNSAnswer* answer) :
+    data_(new Data(io_service, fd, af, checkin, lookup, answer))
 { }
 
 /// The function operator is implemented with the "stackless coroutine"
@@ -326,10 +355,5 @@ UDPServer::resume(const bool done) {
     data_->io_.post(*this);
 }
 
-bool
-UDPServer::hasAnswer() {
-    return (data_->done_);
-}
-
 } // namespace asiodns
 } // namespace isc
diff --git a/src/lib/asiodns/udp_server.h b/src/lib/asiodns/udp_server.h
index 5d32300..1a05227 100644
--- a/src/lib/asiodns/udp_server.h
+++ b/src/lib/asiodns/udp_server.h
@@ -41,16 +41,23 @@ class UDPServer : public virtual DNSServer, public virtual coroutine {
 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 fd the file descriptor of opened UDP socket
+    /// \param af address family, either AF_INET or AF_INET6
     /// \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);
+    /// \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.
+    UDPServer(asio::io_service& io_service,
+#ifdef _WIN32
+              SOCKET fd,
+#else
+              int fd,
+#endif
+              int af,
+              isc::asiolink::SimpleCallback* checkin = NULL,
+              DNSLookup* lookup = NULL, DNSAnswer* answer = NULL);
 
     /// \brief The function operator
     void operator()(asio::error_code ec = asio::error_code(),
@@ -69,16 +76,6 @@ public:
     ///        we have an answer
     void resume(const bool done);
 
-    /// \brief Check if we have an answer
-    ///
-    /// \return true if we have an answer
-    bool hasAnswer();
-
-    /// \brief Returns the coroutine state value
-    ///
-    /// \return the coroutine state value
-    int value() { return (get_value()); }
-
     /// \brief Clones the object
     ///
     /// \return a newly allocated copy of this object
@@ -94,7 +91,7 @@ private:
      * \brief Internal state and data.
      *
      * We use the pimple design pattern, but not because we need to hide
-     * internal data. This struct and whole header is for private use anyway.
+     * internal data. This class and whole header is for private use anyway.
      * It turned out that UDPServer is copied a lot, because it is a coroutine.
      * This way the overhead of copying is lower, we copy only one shared
      * pointer instead of about 10 of them.



More information about the bind10-changes mailing list