Michal 'vorner' Vaner 13 years ago
parent
commit
2080e0316a

+ 10 - 0
src/bin/bind10/bind10_src.py.in

@@ -87,6 +87,10 @@ DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
 CREATOR_SOCKET_OK = b"1\n"
 CREATOR_SOCKET_UNAVAILABLE = b"0\n"
 
+# RCodes of known exceptions for the get_token command
+CREATOR_SOCKET_ERROR = 2
+CREATOR_SHARE_ERROR = 3
+
 # Assign this process some longer name
 isc.util.process.rename(sys.argv[0])
 
@@ -805,6 +809,12 @@ class BoB:
                 'token': token,
                 'path': self._socket_path
             })
+        except isc.bind10.socket_cache.SocketError as e:
+            return isc.config.ccsession.create_answer(CREATOR_SOCKET_ERROR,
+                                                      str(e))
+        except isc.bind10.socket_cache.ShareError as e:
+            return isc.config.ccsession.create_answer(CREATOR_SHARE_ERROR,
+                                                      str(e))
         except Exception as e:
             return isc.config.ccsession.create_answer(1, str(e))
 

+ 9 - 1
src/bin/bind10/tests/bind10_test.py.in

@@ -263,10 +263,11 @@ class TestCacheCommands(unittest.TestCase):
             """
             [rcode, ranswer] = self.__boss._get_socket(args)['result']
             self.assertEqual(code, rcode)
-            if code == 1:
+            if code != 0:
                 # This should be an error message. The exact formatting
                 # is unknown, but we check it is string at least
                 self.assertTrue(isinstance(ranswer, str))
+
         def mod_args(name, value):
             """
             Override a parameter in the args.
@@ -296,6 +297,13 @@ class TestCacheCommands(unittest.TestCase):
         # to an error, not propagated
         self.__raise_exception = Exception("Test exception")
         check_code(1, self.__socket_args)
+        # The special "expected" exceptions
+        self.__raise_exception = \
+            isc.bind10.socket_cache.ShareError("Not shared")
+        check_code(3, self.__socket_args)
+        self.__raise_exception = \
+            isc.bind10.socket_cache.SocketError("Not shared", 13)
+        check_code(2, self.__socket_args)
 
     def drop_socket(self, token):
         """

+ 12 - 0
src/lib/server_common/socket_request.cc

@@ -64,6 +64,10 @@ const std::string& RELEASE_SOCKET_COMMAND() {
     return (str);
 }
 
+// RCode constants for the get_token command
+const size_t SOCKET_ERROR_CODE = 2;
+const size_t SHARE_ERROR_CODE = 3;
+
 // A helper converter from numeric protocol ID to the corresponding string.
 // used both for generating a message for the boss process and for logging.
 inline const char*
@@ -133,6 +137,14 @@ readRequestSocketAnswer(isc::data::ConstElementPtr recv_msg,
     int rcode;
     isc::data::ConstElementPtr answer = isc::config::parseAnswer(rcode,
                                                                  recv_msg);
+    // Translate known rcodes to the corresponding exceptions
+    if (rcode == SOCKET_ERROR_CODE) {
+        isc_throw(SocketRequestor::SocketAllocateError, answer->str());
+    }
+    if (rcode == SHARE_ERROR_CODE) {
+        isc_throw(SocketRequestor::ShareError, answer->str());
+    }
+    // The unknown exceptions
     if (rcode != 0) {
         isc_throw(isc::config::CCSessionError,
                   "Error response when requesting socket: " << answer->str());

+ 53 - 5
src/lib/server_common/socket_request.h

@@ -104,6 +104,52 @@ public:
         { }
     };
 
+    /// \brief Exception when we can't return a requested socket, but we're
+    ///     sure we could return others
+    ///
+    /// This is thrown if the requested socket can't be granted, but it is only
+    /// that one socket, not that the system would be broken or anything. This
+    /// exception is a common base class for the concrete exceptions actually
+    /// thrown. You can safely keep using the SocketRequestor after this
+    /// exception (or anything derived from it) is thrown.
+    ///
+    /// \see ShareError
+    /// \see SocketAllocateError
+    class NonFatalSocketError : public SocketError {
+    public:
+        NonFatalSocketError(const char* file, size_t line, const char* what) :
+            SocketError(file, line, what)
+        { }
+    };
+
+    /// \brief Exception when the socket is allocated by other bind10 module
+    ///    and it doesn't want to share it.
+    ///
+    /// This is thrown if a socket is requested and the socket is already
+    /// allocated by bind10, but other bind10 module(s) is using it and
+    /// the sharing parameters are incompatible (the socket can't be shared
+    /// between the module and our module).
+    class ShareError : public NonFatalSocketError {
+    public:
+        ShareError(const char* file, size_t line, const char* what) :
+            NonFatalSocketError(file, line, what)
+        { }
+    };
+
+    /// \brief Exception when the operating system doesn't allow us to create
+    ///    the requested socket.
+    ///
+    /// This happens when the socket() or bind() call fails in the socket
+    /// creator. This can happen when the address/port pair is already taken
+    /// by a different application, the socket creator doesn't have enough
+    /// privileges, or for some kind of similar reason.
+    class SocketAllocateError : public NonFatalSocketError {
+    public:
+        SocketAllocateError(const char* file, size_t line, const char* what) :
+            NonFatalSocketError(file, line, what)
+        { }
+    };
+
     /// \brief Ask for a socket
     ///
     /// Asks the socket creator to give us a socket. The socket will be bound
@@ -124,11 +170,13 @@ public:
     /// \throw InvalidParameter protocol or share_mode is invalid
     /// \throw CCSessionError when we have a problem talking over the CC
     ///     session.
-    /// \throw SocketError in case the other side doesn't want to give us
-    ///     the socket for some reason (common cases are when the socket
-    ///     can't be allocated or bound, or when the socket is claimed by
-    ///     some other application and the sharing parameters don't allow
-    ///     sharing it).
+    /// \throw SocketError in case we have some other problems receiving the
+    ///     socket (eg. inconsistency in the protocol, the socket got stuck
+    ///     in the transport, etc). If the exception is not of the following
+    ///     derived ones, it usualy means something serious happened.
+    /// \throw SocketAllocateError if the other side can't create the socket.
+    /// \throw ShareError if the socket is used by other bind10 module and
+    ///     that one doesn't want to share it with us.
     virtual SocketID requestSocket(Protocol protocol,
                                    const std::string& address,
                                    uint16_t port, ShareMode share_mode,

+ 6 - 0
src/lib/server_common/tests/socket_requestor_test.cc

@@ -250,8 +250,14 @@ TEST_F(SocketRequestorTest, testBadRequestAnswers) {
     }
 
     // Send back an error response
+    // A generic one first
     session.getMessages()->add(createAnswer(1, "error"));
     ASSERT_THROW(doRequest(), CCSessionError);
+    // Now some with specific exceptions
+    session.getMessages()->add(createAnswer(2, "error"));
+    ASSERT_THROW(doRequest(), SocketRequestor::SocketAllocateError);
+    session.getMessages()->add(createAnswer(3, "error"));
+    ASSERT_THROW(doRequest(), SocketRequestor::ShareError);
 }
 
 // Helper function to create the release commands as we expect