Browse Source

[2187] Throw exception in case of error when opening socket.

Marcin Siodelski 12 years ago
parent
commit
1f486ccd69
3 changed files with 116 additions and 75 deletions
  1. 51 66
      src/lib/dhcp/iface_mgr.cc
  2. 34 0
      src/lib/dhcp/iface_mgr.h
  3. 31 9
      src/lib/dhcp/tests/iface_mgr_unittest.cc

+ 51 - 66
src/lib/dhcp/iface_mgr.cc

@@ -132,13 +132,7 @@ IfaceMgr::IfaceMgr()
         detectIfaces();
 
     } catch (const std::exception& ex) {
-        cout << "IfaceMgr creation failed:" << ex.what() << endl;
-
-        // TODO Uncomment this (or call LOG_FATAL) once
-        // interface detection is implemented. Otherwise
-        // it is not possible to run tests in a portable
-        // way (see detectIfaces() method).
-        throw;
+        isc_throw(IfaceDetectError, ex.what());
     }
 }
 
@@ -164,45 +158,34 @@ void IfaceMgr::stubDetectIfaces() {
     // is faked by detecting loopback interface (lo or lo0). It will eventually
     // be removed once we have actual implementations for all supported systems.
 
-    try {
-        if (if_nametoindex("lo") > 0) {
-            ifaceName = "lo";
-            // this is Linux-like OS
-        } else if (if_nametoindex("lo0") > 0) {
-            ifaceName = "lo0";
-            // this is BSD-like OS
-        } else {
-            // we give up. What OS is this, anyway? Solaris? Hurd?
-            isc_throw(NotImplemented,
-                      "Interface detection on this OS is not supported.");
-        }
-
-        Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
-        iface.flag_up_ = true;
-        iface.flag_running_ = true;
-
-        // Note that we claim that this is not a loopback. iface_mgr tries to open a
-        // socket on all interaces that are up, running and not loopback. As this is
-        // the only interface we were able to detect, let's pretend this is a normal
-        // interface.
-        iface.flag_loopback_ = false;
-        iface.flag_multicast_ = true;
-        iface.flag_broadcast_ = true;
-        iface.setHWType(HWTYPE_ETHERNET);
-
-        iface.addAddress(IOAddress(v4addr));
-        iface.addAddress(IOAddress(v6addr));
-        addInterface(iface);
-    } catch (const std::exception& ex) {
-        // TODO: deallocate whatever memory we used
-        // not that important, since this function is going to be
-        // thrown away as soon as we get proper interface detection
-        // implemented
-
-        // TODO Do LOG_FATAL here
-        std::cerr << "Interface detection failed." << std::endl;
-        throw;
+    if (if_nametoindex("lo") > 0) {
+        ifaceName = "lo";
+        // this is Linux-like OS
+    } else if (if_nametoindex("lo0") > 0) {
+        ifaceName = "lo0";
+        // this is BSD-like OS
+    } else {
+        // we give up. What OS is this, anyway? Solaris? Hurd?
+        isc_throw(NotImplemented,
+                  "Interface detection on this OS is not supported.");
     }
+
+    Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
+    iface.flag_up_ = true;
+    iface.flag_running_ = true;
+
+    // Note that we claim that this is not a loopback. iface_mgr tries to open a
+    // socket on all interaces that are up, running and not loopback. As this is
+    // the only interface we were able to detect, let's pretend this is a normal
+    // interface.
+    iface.flag_loopback_ = false;
+    iface.flag_multicast_ = true;
+    iface.flag_broadcast_ = true;
+    iface.setHWType(HWTYPE_ETHERNET);
+
+    iface.addAddress(IOAddress(v4addr));
+    iface.addAddress(IOAddress(v6addr));
+    addInterface(iface);
 }
 
 bool IfaceMgr::openSockets4(const uint16_t port) {
@@ -231,15 +214,13 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
 
             sock = openSocket(iface->getName(), *addr, port);
             if (sock < 0) {
-                cout << "Failed to open unicast socket." << endl;
-                return (false);
+                isc_throw(SocketConfigError, "failed to open unicast socket");
             }
 
             count++;
         }
     }
     return (count > 0);
-
 }
 
 bool IfaceMgr::openSockets6(const uint16_t port) {
@@ -268,8 +249,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
 
             sock = openSocket(iface->getName(), *addr, port);
             if (sock < 0) {
-                cout << "Failed to open unicast socket." << endl;
-                return (false);
+                isc_throw(SocketConfigError, "failed to open unicast socket");
             }
 
             // Binding socket to unicast address and then joining multicast group
@@ -278,7 +258,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
             if ( !joinMulticast(sock, iface->getName(),
                                 string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
                 close(sock);
-                isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+                isc_throw(SocketConfigError, "Failed to join "
+                          << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                           << " multicast group.");
             }
 
@@ -293,7 +274,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
                                    IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
                                    port);
             if (sock2 < 0) {
-                isc_throw(Unexpected, "Failed to open multicast socket on "
+                isc_throw(SocketConfigError, "Failed to open multicast socket on "
                           << " interface " << iface->getFullName());
                 iface->delSocket(sock); // delete previously opened socket
             }
@@ -406,7 +387,7 @@ int IfaceMgr::openSocketFromIface(const std::string& ifname,
                 family_name = "AF_INET6";
             }
             // We did not find address on the interface.
-            isc_throw(BadValue, "There is no address for interface: "
+            isc_throw(SocketConfigError, "There is no address for interface: "
                       << ifname << ", port: " << port << ", address "
                       " family: " << family_name);
         }
@@ -448,9 +429,13 @@ int IfaceMgr::openSocketFromAddress(const IOAddress& addr,
 
 int IfaceMgr::openSocketFromRemoteAddress(const IOAddress& remote_addr,
                                           const uint16_t port) {
-    // Get local address to be used to connect to remote location.
-    IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
-    return openSocketFromAddress(local_address, port);
+    try {
+        // Get local address to be used to connect to remote location.
+        IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
+        return openSocketFromAddress(local_address, port);
+    } catch (const Exception& e) {
+        isc_throw(SocketConfigError, e.what());
+    }
 }
 
 isc::asiolink::IOAddress
@@ -494,7 +479,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
     sock.connect(remote_endpoint->getASIOEndpoint(), err_code);
     if (err_code) {
         sock.close();
-        isc_throw(Unexpected,"failed to connect to remote endpoint.");
+        isc_throw(Unexpected, "failed to connect to remote endpoint.");
     }
 
     // Once we are connected socket object holds local endpoint.
@@ -522,12 +507,12 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
 
     int sock = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock < 0) {
-        isc_throw(Unexpected, "Failed to create UDP6 socket.");
+        isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
     }
 
     if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
         close(sock);
-        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+        isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
                   << "/port=" << port);
     }
 
@@ -537,7 +522,7 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
     int flag = 1;
     if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
         close(sock);
-        isc_throw(Unexpected, "setsockopt: IP_PKTINFO: failed.");
+        isc_throw(SocketConfigError, "setsockopt: IP_PKTINFO: failed.");
     }
 #endif
 
@@ -569,7 +554,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
     // make a socket
     int sock = socket(AF_INET6, SOCK_DGRAM, 0);
     if (sock < 0) {
-        isc_throw(Unexpected, "Failed to create UDP6 socket.");
+        isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
     }
 
     // Set the REUSEADDR option so that we don't fail to start if
@@ -578,12 +563,12 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                    (char *)&flag, sizeof(flag)) < 0) {
         close(sock);
-        isc_throw(Unexpected, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
+        isc_throw(SocketConfigError, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
     }
 
     if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
         close(sock);
-        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+        isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
                   << "/port=" << port);
     }
 #ifdef IPV6_RECVPKTINFO
@@ -591,14 +576,14 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
                    &flag, sizeof(flag)) != 0) {
         close(sock);
-        isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
+        isc_throw(SocketConfigError, "setsockopt: IPV6_RECVPKTINFO failed.");
     }
 #else
     // RFC2292 - an old way
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
                    &flag, sizeof(flag)) != 0) {
         close(sock);
-        isc_throw(Unexpected, "setsockopt: IPV6_PKTINFO: failed.");
+        isc_throw(SocketConfigError, "setsockopt: IPV6_PKTINFO: failed.");
     }
 #endif
 
@@ -611,7 +596,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
         if ( !joinMulticast( sock, iface.getName(),
                          string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
             close(sock);
-            isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+            isc_throw(SocketConfigError, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                       << " multicast group.");
         }
     }

+ 34 - 0
src/lib/dhcp/iface_mgr.h

@@ -28,6 +28,38 @@
 namespace isc {
 
 namespace dhcp {
+
+/// @brief IfaceMgr exception thrown thrown when interface detection fails.
+class IfaceDetectError : public Exception {
+public:
+    IfaceDetectError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when socket opening
+/// or configuration failed.
+class SocketConfigError : public Exception {
+public:
+    SocketConfigError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when error occured during
+/// reading data from socket.
+class SocketReadError : public Exception {
+public:
+    SocketReadError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when error occured during
+/// sedning data through socket.
+class SocketWriteError : public Exception {
+public:
+    SocketWriteError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief handles network interfaces, transmission and reception
 ///
 /// IfaceMgr is an interface manager class that detects available network
@@ -460,6 +492,7 @@ public:
     ///
     /// @param port specifies port number (usually DHCP6_SERVER_PORT)
     ///
+    /// @throw SocketOpenFailure if tried and failed to open socket.
     /// @return true if any sockets were open
     bool openSockets6(const uint16_t port = DHCP6_SERVER_PORT);
 
@@ -472,6 +505,7 @@ public:
     ///
     /// @param port specifies port number (usually DHCP4_SERVER_PORT)
     ///
+    /// @throw SocketOpenFailure if tried and failed to open socket.
     /// @return true if any sockets were open
     bool openSockets4(const uint16_t port = DHCP4_SERVER_PORT);
 

+ 31 - 9
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -419,7 +419,7 @@ TEST_F(IfaceMgrTest, sockets6) {
     // testing socket operation in a portable way is tricky
     // without interface detection implemented
 
-    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
 
     IOAddress loAddr("::1");
 
@@ -439,10 +439,20 @@ TEST_F(IfaceMgrTest, sockets6) {
     // removed code for binding socket twice to the same address/port
     // as it caused problems on some platforms (e.g. Mac OS X)
 
-    close(socket1);
-    close(socket2);
+    ifacemgr->closeSockets();
 
-    delete ifacemgr;
+    // Use address that is not assigned to LOOPBACK iface.
+    IOAddress invalidAddr("::2");
+    EXPECT_THROW(
+        ifacemgr->openSocket(LOOPBACK, invalidAddr, 10547),
+        SocketConfigError
+    );
+
+    // Use non-existing interface name.
+    EXPECT_THROW(
+        ifacemgr->openSocket("non_existing_interface", loAddr, 10548),
+        BadValue
+    );
 }
 
 TEST_F(IfaceMgrTest, socketsFromIface) {
@@ -466,6 +476,13 @@ TEST_F(IfaceMgrTest, socketsFromIface) {
     EXPECT_GT(socket2, 0);
     close(socket2);
 
+    ifacemgr->closeSockets();
+
+    // Use invalid interface name.
+    EXPECT_THROW(
+        ifacemgr->openSocketFromIface("non_existing_interface", PORT1, AF_INET),
+        BadValue
+    );
 }
 
 
@@ -480,7 +497,6 @@ TEST_F(IfaceMgrTest, socketsFromAddress) {
     );
     // socket descriptor must be positive integer
     EXPECT_GT(socket1, 0);
-    close(socket1);
 
     // Open v4 socket on loopback interface and bind to different port
     int socket2 = 0;
@@ -490,7 +506,14 @@ TEST_F(IfaceMgrTest, socketsFromAddress) {
     );
     // socket descriptor must be positive integer
     EXPECT_GT(socket2, 0);
-    close(socket2);
+
+    ifacemgr->closeSockets();
+
+    // Use non-existing address.
+    IOAddress invalidAddr("1.2.3.4");
+    EXPECT_THROW(
+        ifacemgr->openSocketFromAddress(invalidAddr, PORT1), BadValue
+    );
 }
 
 TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
@@ -505,7 +528,6 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
         socket1 = ifacemgr->openSocketFromRemoteAddress(loAddr6, PORT1);
     );
     EXPECT_GT(socket1, 0);
-    close(socket1);
 
     // Open v4 socket to connect to remote address.
     int socket2 = 0;
@@ -514,7 +536,8 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
         socket2 = ifacemgr->openSocketFromRemoteAddress(loAddr, PORT2);
     );
     EXPECT_GT(socket2, 0);
-    close(socket2);
+
+    ifacemgr->closeSockets();
 
     // The following test is currently disabled for OSes other than
     // Linux because interface detection is not implemented on them.
@@ -528,7 +551,6 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
         socket3 = ifacemgr->openSocketFromRemoteAddress(bcastAddr, PORT2);
     );
     EXPECT_GT(socket3, 0);
-    close(socket3);
 #endif
 }