Browse Source

IOService can add and remove servers at runtime

However, it does not seem to close the sockets. It needs to be addressed
somehow and fixed (there's one disabled test because of that).

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/vorner-recursor-config@3337 e5f2f494-b856-4b98-b285-d166d9295462
Michal Vaner 14 years ago
parent
commit
73d5beccc1

+ 93 - 60
src/lib/asiolink/asiolink.cc

@@ -22,6 +22,7 @@
 #include <sys/socket.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 
 
+#include <vector>
 #include <asio.hpp>
 #include <asio.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/bind.hpp>
 #include <boost/bind.hpp>
@@ -54,10 +55,50 @@ public:
 
 
     typedef boost::shared_ptr<UDPServer> UDPServerPtr;
     typedef boost::shared_ptr<UDPServer> UDPServerPtr;
     typedef boost::shared_ptr<TCPServer> TCPServerPtr;
     typedef boost::shared_ptr<TCPServer> TCPServerPtr;
-    UDPServerPtr udp4_server_;
-    UDPServerPtr udp6_server_;
-    TCPServerPtr tcp4_server_;
-    TCPServerPtr tcp6_server_;
+    typedef boost::shared_ptr<DNSServer> DNSServerPtr;
+    vector<DNSServerPtr> servers_;
+    SimpleCallback *checkin_;
+    DNSLookup *lookup_;
+    DNSAnswer *answer_;
+
+    void addServer(uint16_t port, const ip::address& address) {
+        try {
+            TCPServerPtr tcpServer(new TCPServer(io_service_, address, port,
+                checkin_, lookup_, answer_));
+            (*tcpServer)();
+            servers_.push_back(tcpServer);
+
+            UDPServerPtr udpServer(new UDPServer(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 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);
+    }
 };
 };
 
 
 IOServiceImpl::IOServiceImpl(const char& port,
 IOServiceImpl::IOServiceImpl(const char& port,
@@ -66,52 +107,16 @@ IOServiceImpl::IOServiceImpl(const char& port,
                              SimpleCallback* checkin,
                              SimpleCallback* checkin,
                              DNSLookup* lookup,
                              DNSLookup* lookup,
                              DNSAnswer* answer) :
                              DNSAnswer* answer) :
-    udp4_server_(UDPServerPtr()), udp6_server_(UDPServerPtr()),
-    tcp4_server_(TCPServerPtr()), tcp6_server_(TCPServerPtr())
+    checkin_(checkin),
+    lookup_(lookup),
+    answer_(answer)
 {
 {
-    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());
-    }
 
 
-    try {
-        if (v4addr != NULL) {
-            udp4_server_ = UDPServerPtr(new UDPServer(io_service_,
-                                                      *v4addr, portnum,
-                                                      checkin, lookup, answer));
-            (*udp4_server_)();
-            tcp4_server_ = TCPServerPtr(new TCPServer(io_service_,
-                                                      *v4addr, portnum,
-                                                      checkin, lookup, answer));
-            (*tcp4_server_)();
-        }
-        if (v6addr != NULL) {
-            udp6_server_ = UDPServerPtr(new UDPServer(io_service_,
-                                                      *v6addr, portnum,
-                                                      checkin, lookup, answer));
-            (*udp6_server_)();
-            tcp6_server_ = TCPServerPtr(new TCPServer(io_service_,
-                                                      *v6addr, portnum,
-                                                      checkin, lookup, answer));
-            (*tcp6_server_)();
-        }
-    } 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());
+    if (v4addr) {
+        addServer(port, *v4addr);
+    }
+    if (v6addr) {
+        addServer(port, *v6addr);
     }
     }
 }
 }
 
 
@@ -119,19 +124,9 @@ IOService::IOService(const char& port, const char& address,
                      SimpleCallback* checkin,
                      SimpleCallback* checkin,
                      DNSLookup* lookup,
                      DNSLookup* lookup,
                      DNSAnswer* answer) :
                      DNSAnswer* answer) :
-    impl_(NULL)
+    impl_(new IOServiceImpl(port, NULL, NULL, checkin, lookup, answer))
 {
 {
-    error_code err;
-    const ip::address addr = ip::address::from_string(&address, err);
-    if (err) {
-        isc_throw(IOError, "Invalid IP address '" << &address << "': "
-                  << err.message());
-    }
-
-    impl_ = new IOServiceImpl(port,
-                              addr.is_v4() ? &addr : NULL,
-                              addr.is_v6() ? &addr : NULL,
-                              checkin, lookup, answer);
+    addServer(port, &address);
 }
 }
 
 
 IOService::IOService(const char& port,
 IOService::IOService(const char& port,
@@ -148,10 +143,48 @@ IOService::IOService(const char& port,
     impl_ = new IOServiceImpl(port, v4addrp, v6addrp, checkin, lookup, answer);
     impl_ = new IOServiceImpl(port, v4addrp, v6addrp, checkin, lookup, answer);
 }
 }
 
 
+IOService::IOService(SimpleCallback* checkin, DNSLookup* lookup,
+    DNSAnswer *answer) :
+    impl_(new IOServiceImpl(*"", NULL, NULL, checkin, lookup, answer))
+{
+}
+
 IOService::~IOService() {
 IOService::~IOService() {
     delete impl_;
     delete impl_;
 }
 }
 
 
+namespace {
+
+ip::address
+convertAddr(const string& address) {
+    error_code err;
+    ip::address addr = ip::address::from_string(address, err);
+    if (err) {
+        isc_throw(IOError, "Invalid IP address '" << &address << "': "
+            << err.message());
+    }
+    return addr;
+}
+
+}
+
+void
+IOService::addServer(const char& port, const string& address) {
+    impl_->addServer(port, convertAddr(address));
+}
+
+void
+IOService::addServer(uint16_t port, const string &address) {
+    impl_->addServer(port, convertAddr(address));
+}
+
+void
+IOService::clearServers() {
+    // FIXME: This does not work, it does not close the socket.
+    // How is it done?
+    impl_->servers_.clear();
+}
+
 void
 void
 IOService::run() {
 IOService::run() {
     impl_->io_service_.run();
     impl_->io_service_.run();

+ 10 - 0
src/lib/asiolink/asiolink.h

@@ -149,10 +149,20 @@ public:
               SimpleCallback* checkin,
               SimpleCallback* checkin,
               DNSLookup* lookup,
               DNSLookup* lookup,
               DNSAnswer* answer);
               DNSAnswer* answer);
+    /// \brief The constructor without any servers.
+    ///
+    /// Use addServer() to add some servers.
+    IOService(SimpleCallback *checkin, DNSLookup* lookup, DNSAnswer *answer);
     /// \brief The destructor.
     /// \brief The destructor.
     ~IOService();
     ~IOService();
     //@}
     //@}
 
 
+    /// \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 Remove all servers from the service
+    void clearServers();
+
     /// \brief Start the underlying event loop.
     /// \brief Start the underlying event loop.
     ///
     ///
     /// This method does not return control to the caller until
     /// This method does not return control to the caller until

+ 33 - 1
src/lib/asiolink/tests/asiolink_unittest.cc

@@ -341,10 +341,18 @@ protected:
                                     NULL, NULL);
                                     NULL, NULL);
     }
     }
 
 
+    // Set up an IO Service queue without any addresses
+    void setIOService() {
+        delete io_service_;
+        io_service_ = NULL;
+        callback_ = new ASIOCallBack(this);
+        io_service_ = new IOService(callback_, NULL, NULL);
+    }
+
     // Run a simple server test, on either IPv4 or IPv6, and over either
     // Run a simple server test, on either IPv4 or IPv6, and over either
     // UDP or TCP.  Calls the sendUDP() or sendTCP() methods, which will
     // UDP or TCP.  Calls the sendUDP() or sendTCP() methods, which will
     // start the IO Service queue.  The UDPServer or TCPServer that was
     // start the IO Service queue.  The UDPServer or TCPServer that was
-    // created by setIOSerice() will receive the test packet and issue a
+    // created by setIOService() will receive the test packet and issue a
     // callback, which enables us to check that the data it received
     // callback, which enables us to check that the data it received
     // matches what we sent.
     // matches what we sent.
     void doTest(const int family, const int protocol) {
     void doTest(const int family, const int protocol) {
@@ -515,6 +523,30 @@ TEST_F(ASIOLinkTest, v4TCPSendSpecific) {
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
 }
 }
 
 
+TEST_F(ASIOLinkTest, v6AddServer) {
+    setIOService();
+    io_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
+    doTest(AF_INET6, IPPROTO_TCP);
+
+    EXPECT_THROW(sendTCP(AF_INET), IOError);
+}
+
+TEST_F(ASIOLinkTest, v4AddServer) {
+    setIOService();
+    io_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
+    doTest(AF_INET, IPPROTO_TCP);
+
+    EXPECT_THROW(sendTCP(AF_INET6), IOError);
+}
+
+TEST_F(ASIOLinkTest, DISABLED_clearServers) {
+    // FIXME: Enable when clearServers actually close the sockets
+    io_service_->clearServers();
+
+    EXPECT_THROW(sendTCP(AF_INET), IOError);
+    EXPECT_THROW(sendTCP(AF_INET6), IOError);
+}
+
 TEST_F(ASIOLinkTest, v6TCPOnly) {
 TEST_F(ASIOLinkTest, v6TCPOnly) {
     // Open only IPv6 TCP socket.  A subsequent attempt of establishing an
     // Open only IPv6 TCP socket.  A subsequent attempt of establishing an
     // IPv4/TCP connection should fail.  See above for why we only test this
     // IPv4/TCP connection should fail.  See above for why we only test this