Browse Source

[1429] Error handling in _get_socket

Michal 'vorner' Vaner 13 years ago
parent
commit
1485c897a9
2 changed files with 81 additions and 19 deletions
  1. 26 11
      src/bin/bind10/bind10_src.py.in
  2. 55 8
      src/bin/bind10/tests/bind10_test.py.in

+ 26 - 11
src/bin/bind10/bind10_src.py.in

@@ -824,17 +824,32 @@ class BoB:
         Implementation of the get_socket CC command. It asks the cache
         to provide the token and sends the information back.
         """
-        addr = isc.net.parse.addr_parse(args['address'])
-        port = isc.net.parse.port_parse(args['port'])
-        protocol = args['protocol']
-        share_mode = args['share_mode']
-        share_name = args['share_name']
-        token = self._socket_cache.get_token(protocol, addr, port, share_mode,
-                                             share_name)
-        return isc.config.ccsession.create_answer(0, {
-            'token': token,
-            'path': self._socket_path
-        })
+        try:
+            try:
+                addr = isc.net.parse.addr_parse(args['address'])
+                port = isc.net.parse.port_parse(args['port'])
+                protocol = args['protocol']
+                if protocol not in ['UDP', 'TCP']:
+                    raise ValueError("Protocol must be either UDP or TCP")
+                share_mode = args['share_mode']
+                if share_mode not in ['ANY', 'SAMEAPP', 'NO']:
+                    raise ValueError("Share mode must be one of ANY, SAMEAPP" +
+                                     "or NO")
+                share_name = args['share_name']
+            except KeyError as ke:
+                return \
+                    isc.config.ccsession.create_answer(1,
+                                                       "Missing parameter " +
+                                                       str(ke))
+
+            token = self._socket_cache.get_token(protocol, addr, port,
+                                                 share_mode, share_name)
+            return isc.config.ccsession.create_answer(0, {
+                'token': token,
+                'path': self._socket_path
+            })
+        except Exception as e:
+            return isc.config.ccsession.create_answer(1, str(e))
 
     def socket_request_handler(self, token, unix_socket):
         """

+ 55 - 8
src/bin/bind10/tests/bind10_test.py.in

@@ -115,6 +115,13 @@ class TestCacheCommands(unittest.TestCase):
         self.__boss._socket_cache = self
         self.__boss._socket_path = '/socket/path'
         self.__raise_exception = None
+        self.__socket_args = {
+            "port": 53,
+            "address": "0.0.0.0",
+            "protocol": "UDP",
+            "share_mode": "ANY",
+            "share_name": "app"
+        }
         # What was and wasn't called.
         self.__drop_app_called = None
         self.__get_socket_called = None
@@ -209,14 +216,7 @@ class TestCacheCommands(unittest.TestCase):
         """
         Test the successful scenario of getting a socket.
         """
-        args = {
-            "port": 53,
-            "address": "0.0.0.0",
-            "protocol": "UDP",
-            "share_mode": "ANY",
-            "share_name": "app"
-        }
-        result = self.__boss._get_socket(args)
+        result = self.__boss._get_socket(self.__socket_args)
         [code, answer] = result['result']
         self.assertEqual(0, code)
         self.assertEqual({
@@ -229,6 +229,53 @@ class TestCacheCommands(unittest.TestCase):
         self.assertEqual(("UDP", addr, 53, "ANY", "app"),
                          self.__get_token_called)
 
+    def test_get_socket_error(self):
+        """
+        Test that bad inputs are handled correctly, etc.
+        """
+        def check_code(code, args):
+            """
+            Pass the args there and check if it returs success or not.
+
+            The rest is not tested, as it is already checked in the
+            test_get_socket_ok.
+            """
+            [rcode, ranswer] = self.__boss._get_socket(args)['result']
+            self.assertEqual(code, rcode)
+            if code == 1:
+                # This should be an error message. The exact formatting
+                # is unknown, but we check it is string at last
+                self.assertIsInstance(ranswer, str)
+        def mod_args(name, value):
+            """
+            Override a parameter in the args.
+            """
+            result = dict(self.__socket_args)
+            result[name] = value
+            return result
+
+        # Port too large
+        check_code(1, mod_args('port', 65536))
+        # Not numeric address
+        check_code(1, mod_args('address', 'example.org.'))
+        # Some bad values of enum-like params
+        check_code(1, mod_args('protocol', 'BAD PROTO'))
+        check_code(1, mod_args('share_mode', 'BAD SHARE'))
+        # Check missing parameters
+        for param in self.__socket_args.keys():
+            args = dict(self.__socket_args)
+            del args[param]
+            check_code(1, args)
+        # These are OK values for the enum-like parameters
+        # The ones from test_get_socket_ok are not tested here
+        check_code(0, mod_args('protocol', 'TCP'))
+        check_code(0, mod_args('share_mode', 'SAMEAPP'))
+        check_code(0, mod_args('share_mode', 'NO'))
+        # If an exception is raised from within the cache, it is converted
+        # to an error, not propagated
+        self.__raise_exception = Exception("Test exception")
+        check_code(1, self.__socket_args)
+
 class TestBoB(unittest.TestCase):
     def test_init(self):
         bob = BoB()