Browse Source

added tests for specific addresses and "-4/-6" cases.
the tests identified bugs in my previous refactoring, which were fixed.


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac221b@2474 e5f2f494-b856-4b98-b285-d166d9295462

JINMEI Tatuya 15 years ago
parent
commit
4813426241
3 changed files with 145 additions and 65 deletions
  1. 16 16
      src/bin/auth/asio_link.cc
  2. 12 3
      src/bin/auth/asio_link.h
  3. 117 46
      src/bin/auth/tests/asio_link_unittest.cc

+ 16 - 16
src/bin/auth/asio_link.cc

@@ -453,7 +453,7 @@ private:
 class IOServiceImpl {
 public:
     IOServiceImpl(AuthSrv* auth_server, const char& port,
-                  const ip::address& v4addr, const ip::address& v6addr);
+                  const ip::address* v4addr, const ip::address* v6addr);
     asio::io_service io_service_;
     AuthSrv* auth_server_;
 
@@ -469,8 +469,8 @@ public:
 };
 
 IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, const char& port,
-                             const ip::address& v4addr,
-                             const ip::address& v6addr) :
+                             const ip::address* const v4addr,
+                             const ip::address* const v6addr) :
     auth_server_(auth_server),
     udp4_server_(UDPServerPtr()), udp6_server_(UDPServerPtr()),
     tcp4_server_(TCPServerPtr()), tcp6_server_(TCPServerPtr())
@@ -485,17 +485,17 @@ IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, const char& port,
     }
 
     try {
-        if (v4addr.is_v4()) {
+        if (v4addr != NULL) {
             udp4_server_ = UDPServerPtr(new UDPServer(auth_server, io_service_,
-                                                      v4addr, portnum));
+                                                      *v4addr, portnum));
             tcp4_server_ = TCPServerPtr(new TCPServer(auth_server, io_service_,
-                                                      v4addr, portnum));
+                                                      *v4addr, portnum));
         }
-        if (v6addr.is_v6()) {
+        if (v6addr != NULL) {
             udp6_server_ = UDPServerPtr(new UDPServer(auth_server, io_service_,
-                                                      v6addr, portnum));
+                                                      *v6addr, portnum));
             tcp6_server_ = TCPServerPtr(new TCPServer(auth_server, io_service_,
-                                                      v6addr, portnum));
+                                                      *v6addr, portnum));
         }
     } catch (const asio::system_error& err) {
         // We need to catch and convert any ASIO level exceptions.
@@ -518,19 +518,19 @@ IOService::IOService(AuthSrv* auth_server, const char& port,
     }
 
     impl_ = new IOServiceImpl(auth_server, port,
-                              addr.is_v4() ? addr : ip::address::address(),
-                              addr.is_v6() ? addr : ip::address::address());
+                              addr.is_v4() ? &addr : NULL,
+                              addr.is_v6() ? &addr : NULL);
 }
 
 IOService::IOService(AuthSrv* auth_server, const char& port,
                      const bool use_ipv4, const bool use_ipv6) :
     impl_(NULL)
 {
-    const ip::address v4addr = use_ipv4 ? ip::address(ip::address_v4::any()) :
-        ip::address::address();
-    const ip::address v6addr = use_ipv6 ? ip::address(ip::address_v6::any()) :
-        ip::address::address();
-    impl_ = new IOServiceImpl(auth_server, port, v4addr, v6addr);
+    const ip::address v4addr_any = ip::address(ip::address_v4::any());
+    const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL; 
+    const ip::address v6addr_any = ip::address(ip::address_v6::any());
+    const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
+    impl_ = new IOServiceImpl(auth_server, port, v4addrp, v6addrp);
 }
 
 IOService::~IOService() {

+ 12 - 3
src/bin/auth/asio_link.h

@@ -380,6 +380,9 @@ class IOService {
     ///
     /// \name Constructors and Destructor
     ///
+    /// These are currently very specific to the authoritative server
+    /// implementation.
+    ///
     /// Note: The copy constructor and the assignment operator are
     /// intentionally defined as private, making this class non-copyable.
     //@{
@@ -387,9 +390,15 @@ private:
     IOService(const IOService& source);
     IOService& operator=(const IOService& source);
 public:
-    /// \brief The constructor.  Currently very specific to the authoritative
-    /// server implementation.
-    IOService(AuthSrv* auth_server, const char& address, const char& port);
+    /// \brief The constructor with a specific IP address and port on which
+    /// the services listen on.
+    IOService(AuthSrv* auth_server, const char& port, const char& address);
+    /// \brief The constructor with a specific port on which the services
+    /// listen on.
+    ///
+    /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
+    /// IPv4/IPv6 services will be available if and only if \c use_ipv4
+    /// or \c use_ipv6 is \c true, respectively.
     IOService(AuthSrv* auth_server, const char& port,
               const bool use_ipv4, const bool use_ipv6);
     /// \brief The destructor.

+ 117 - 46
src/bin/auth/tests/asio_link_unittest.cc

@@ -156,6 +156,18 @@ resolveAddress(const int family, const int sock_type, const int protocol) {
     return (res);
 }
 
+// This fixture is a framework for various types of network operations
+// using the ASIO interfaces.  Each test case creates an IOService object,
+// opens a local "client" socket for testing, sends data via the local socket
+// to the service that would run in the IOService object.
+// A mock callback function (an ASIOCallBack object) is registered with the
+// IOService object, so the test code should be able to examine the data
+// receives on the server side.  It then checks the received data matches
+// expected parameters.
+// If initialization parameters of the IOService should be modified, the test
+// case can do it using the setIOService() method.
+// Note: the set of tests in ASIOLinkTest use actual network services and may
+// involve undesirable side effect such as blocking.
 class ASIOLinkTest : public ::testing::Test {
 protected:
     ASIOLinkTest();
@@ -166,6 +178,7 @@ protected:
         if (sock_ != -1) {
             close(sock_);
         }
+        delete io_service_;
     }
     void sendUDP(const int family) {
         res_ = resolveAddress(family, SOCK_DGRAM, IPPROTO_UDP);
@@ -179,7 +192,7 @@ protected:
         if (cc != sizeof(test_data)) {
             isc_throw(IOError, "unexpected sendto result: " << cc);
         }
-        io_service_.run();
+        io_service_->run();
     }
     void sendTCP(const int family) {
         res_ = resolveAddress(family, SOCK_STREAM, IPPROTO_TCP);
@@ -195,9 +208,54 @@ protected:
         if (cc != sizeof(test_data)) {
             isc_throw(IOError, "unexpected sendto result: " << cc);
         }
-        io_service_.run();
+        io_service_->run();
     }
-public:
+    void setIOService(const char& address) {
+        delete io_service_;
+        io_service_ = NULL;
+        io_service_ = new IOService(NULL, *TEST_PORT, address);
+        io_service_->setCallBack(ASIOCallBack(this));
+    }
+    void setIOService(const bool use_ipv4, const bool use_ipv6) {
+        delete io_service_;
+        io_service_ = NULL;
+        io_service_ = new IOService(NULL, *TEST_PORT, use_ipv4, use_ipv6);
+        io_service_->setCallBack(ASIOCallBack(this));
+    }
+    void doTest(const int family, const int protocol) {
+        if (protocol == IPPROTO_UDP) {
+            sendUDP(family);
+        } else {
+            sendTCP(family);
+        }
+
+        // There doesn't seem to be an effective test for the validity of
+        // 'native'.
+        // One thing we are sure is it must be different from our local socket.
+        EXPECT_NE(sock_, callback_native_);
+        EXPECT_EQ(protocol, callback_protocol_);
+        EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
+                  callback_address_);
+
+        const uint8_t* expected_data =
+            protocol == IPPROTO_UDP ? test_data : test_data + 2;
+        const size_t expected_datasize =
+            protocol == IPPROTO_UDP ? sizeof(test_data) :
+            sizeof(test_data) - 2;
+        EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
+                            callback_data_.size(),
+                            expected_data, expected_datasize);
+    }
+private:
+    class ASIOCallBack : public std::unary_function<IOMessage, void> {
+    public:
+        ASIOCallBack(ASIOLinkTest* test_obj) : test_obj_(test_obj) {}
+        void operator()(const IOMessage& io_message) const {
+            test_obj_->callBack(io_message);
+        }
+    private:
+        ASIOLinkTest* test_obj_;
+    };
     void callBack(const IOMessage& io_message) {
         callback_protocol_ = io_message.getSocket().getProtocol();
         callback_native_ = io_message.getSocket().getNative();
@@ -207,10 +265,10 @@ public:
             static_cast<const uint8_t*>(io_message.getData()),
             static_cast<const uint8_t*>(io_message.getData()) +
             io_message.getDataSize());
-        io_service_.stop();
+        io_service_->stop();
     }
 protected:
-    IOService io_service_;
+    IOService* io_service_;
     int callback_protocol_;
     int callback_native_;
     string callback_address_;
@@ -220,61 +278,74 @@ private:
     struct addrinfo* res_;
 };
 
-class ASIOCallBack : public std::unary_function<IOMessage, void> {
-public:
-    ASIOCallBack(ASIOLinkTest* test_obj) : test_obj_(test_obj) {}
-    void operator()(const IOMessage& io_message) const {
-        test_obj_->callBack(io_message);
-    }
-private:
-    ASIOLinkTest* test_obj_;
-};
-
 ASIOLinkTest::ASIOLinkTest() :
-    io_service_(NULL, *TEST_PORT, true, true),
-    sock_(-1), res_(NULL)
+    io_service_(NULL), sock_(-1), res_(NULL)
 {
-    io_service_.setCallBack(ASIOCallBack(this));
+    setIOService(true, true);
 }
 
 TEST_F(ASIOLinkTest, v6UDPSend) {
-    sendUDP(AF_INET6);
-    // There doesn't seem to be an effective test for the validity of 'native'.
-    // One thing we are sure is it must be different from our local socket.
-    EXPECT_NE(callback_native_, sock_);
-    EXPECT_EQ(IPPROTO_UDP, callback_protocol_);
-    EXPECT_EQ(TEST_IPV6_ADDR, callback_address_);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
-                        callback_data_.size(), test_data, sizeof(test_data));
+    doTest(AF_INET6, IPPROTO_UDP);
 }
 
 TEST_F(ASIOLinkTest, v6TCPSend) {
-    sendTCP(AF_INET6);
-    EXPECT_NE(callback_native_, sock_);
-    EXPECT_EQ(IPPROTO_TCP, callback_protocol_);
-    EXPECT_EQ(TEST_IPV6_ADDR, callback_address_);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
-                        callback_data_.size(),
-                        test_data + 2, sizeof(test_data) - 2);
+    doTest(AF_INET6, IPPROTO_TCP);
 }
 
 TEST_F(ASIOLinkTest, v4UDPSend) {
-    sendUDP(AF_INET);
-    EXPECT_NE(callback_native_, sock_);
-    EXPECT_EQ(IPPROTO_UDP, callback_protocol_);
-    EXPECT_EQ(TEST_IPV4_ADDR, callback_address_);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
-                        callback_data_.size(), test_data, sizeof(test_data));
+    doTest(AF_INET, IPPROTO_UDP);
 }
 
 TEST_F(ASIOLinkTest, v4TCPSend) {
-    sendTCP(AF_INET);
-    EXPECT_NE(callback_native_, sock_);
-    EXPECT_EQ(IPPROTO_TCP, callback_protocol_);
-    EXPECT_EQ(TEST_IPV4_ADDR, callback_address_);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
-                        callback_data_.size(),
-                        test_data + 2, sizeof(test_data) - 2);
+    doTest(AF_INET, IPPROTO_TCP);
+}
+
+TEST_F(ASIOLinkTest, v6UDPSendSpecific) {
+    // Explicitly set a specific address to be bound to the socket.
+    // The subsequent test does not directly ensures the underlying socket
+    // is bound to the expected address, but the success of the tests should
+    // reasonably suggest it works as intended.
+    // Specifying an address also implicitly means the service runs in a
+    // single address-family mode.  In tests using TCP we can confirm that
+    // by trying to make a connection and seeing a failure.  In UDP, it'd be
+    // more complicated because we need to use a connected socket and catch
+    // an error on a subsequent read operation.  We could do it, but for
+    // simplicity we only tests the easier cases for now.
+
+    setIOService(*TEST_IPV6_ADDR);
+    doTest(AF_INET6, IPPROTO_UDP);
+}
+
+TEST_F(ASIOLinkTest, v6TCPSendSpecific) {
+    setIOService(*TEST_IPV6_ADDR);
+    doTest(AF_INET6, IPPROTO_TCP);
+
+    EXPECT_THROW(sendTCP(AF_INET), IOError);
+}
+
+TEST_F(ASIOLinkTest, v4UDPSendSpecific) {
+    setIOService(*TEST_IPV4_ADDR);
+    doTest(AF_INET, IPPROTO_UDP);
+}
+
+TEST_F(ASIOLinkTest, v4TCPSendSpecific) {
+    setIOService(*TEST_IPV4_ADDR);
+    doTest(AF_INET, IPPROTO_TCP);
+
+    EXPECT_THROW(sendTCP(AF_INET6), IOError);
+}
+
+TEST_F(ASIOLinkTest, v6TCPOnly) {
+    // Open only IPv6 TCP socket.  A subsequent attempt of establishing an
+    // IPv4/TCP connection should fail.  See above for why we only test this
+    // for TCP.
+    setIOService(false, true);
+    EXPECT_THROW(sendTCP(AF_INET), IOError);
+}
+
+TEST_F(ASIOLinkTest, v4TCPOnly) {
+    setIOService(true, false);
+    EXPECT_THROW(sendTCP(AF_INET6), IOError);
 }
 
 }