BIND 10 trac800, updated. 517c31a58af1f7b97f308e77caeb8cbe9ef99cf1 [trac800] The parser

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Jul 25 10:45:12 UTC 2011


The branch, trac800 has been updated
       via  517c31a58af1f7b97f308e77caeb8cbe9ef99cf1 (commit)
      from  4c485d0b112721d3a2b2939ab61db14b7608c98c (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 517c31a58af1f7b97f308e77caeb8cbe9ef99cf1
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Jul 25 12:44:09 2011 +0200

    [trac800] The parser

-----------------------------------------------------------------------

Summary of changes:
 src/bin/bind10/sockcreator.py            |   81 +++++++++++++++++++++++++++-
 src/bin/bind10/tests/sockcreator_test.py |   84 ++++++++++++++++++++++++++++-
 src/bin/sockcreator/README               |    2 +-
 3 files changed, 160 insertions(+), 7 deletions(-)

-----------------------------------------------------------------------
diff --git a/src/bin/bind10/sockcreator.py b/src/bin/bind10/sockcreator.py
index 667c22f..44e1d8e 100644
--- a/src/bin/bind10/sockcreator.py
+++ b/src/bin/bind10/sockcreator.py
@@ -13,6 +13,9 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+import socket
+import struct
+
 """
 Module that comunicates with the priviledget socket creator (b10-sockcreator).
 """
@@ -55,14 +58,26 @@ class Parser:
         have a read_fd() method to read the file descriptor. This slightly
         unusual modification of socket object is used to easy up testing.
         """
-        pass # TODO Implement
+        self.__socket = creator_socket
 
     def terminate(self):
         """
         Asks the creator process to terminate and waits for it to close the
         socket. Does not return anything.
         """
-        pass # TODO Implement
+        if self.__socket is None:
+            raise CreatorError('Terminated already', True)
+        try:
+            self.__socket.sendall(b'T')
+            # Wait for an EOF - it will return empty data
+            eof = self.__socket.recv(1)
+            if len(eof) != 0:
+                raise CreatorError('Protocol error - data after terminated',
+                                   True)
+            self.__socket = None
+        except socket.error as se:
+            self.__socket = None
+            raise CreatorError(str(se), True)
 
     def get_socket(self, address, port, socktype):
         """
@@ -75,4 +90,64 @@ class Parser:
         should be fast, as it is on localhost) and returns the file descriptor
         number. It raises a CreatorError exception if the creation fails.
         """
-        pass # TODO Implement
+        if self.__socket is None:
+            raise CreatorError('Socket requested on terminated creator', True)
+        # First, assemble the request from parts
+        data = b'S'
+        if socktype == 'UDP' or socktype == socket.SOCK_DGRAM:
+            data += b'U'
+        elif socktype == 'TCP' or socktype == socket.SOCK_STREAM:
+            data += b'T'
+        else:
+            raise ValueError('Unknown socket type: ' + str(socktype))
+        if address.family == socket.AF_INET:
+            data += b'4'
+        elif address.family == socket.AF_INET6:
+            data += b'6'
+        else:
+            raise ValueError('Unknown address family in address')
+        data += struct.pack('!H', port)
+        data += address.addr
+        try:
+            # Send the request
+            self.__socket.sendall(data)
+            answer = self.__socket.recv(1)
+            if answer == b'S':
+                # Success!
+                return self.__socket.read_fd()
+            elif answer == b'E':
+                # There was an error, read the error as well
+                error = self.__socket.recv(1)
+                errno = struct.unpack('i',
+                                      self.__read_all(len(struct.pack('i',
+                                                                      0))))
+                if error == b'S':
+                    cause = 'socket'
+                elif error == b'B':
+                    cause = 'bind'
+                else:
+                    self.__socket = None
+                    raise CreatorError('Unknown error cause' + str(answer), True)
+                raise CreatorError('Error creating socket on ' + cause, False,
+                                   errno[0])
+            else:
+                self.__socket = None
+                raise CreatorError('Unknown response ' + str(answer), True)
+        except socket.error as se:
+            self.__socket = None
+            raise CreatorError(str(se), True)
+
+    def __read_all(self, length):
+        """
+        Keeps reading until length data is read or EOF or error happens.
+
+        EOF is considered error as well and throws.
+        """
+        result = b''
+        while len(result) < length:
+            data = self.__socket.recv(length - len(result))
+            if len(data) == 0:
+                self.__socket = None
+                raise CreatorError('Unexpected EOF', True)
+            result += data
+        return result
diff --git a/src/bin/bind10/tests/sockcreator_test.py b/src/bin/bind10/tests/sockcreator_test.py
index 4e93903..fee691f 100644
--- a/src/bin/bind10/tests/sockcreator_test.py
+++ b/src/bin/bind10/tests/sockcreator_test.py
@@ -61,6 +61,7 @@ class FakeCreator:
             raise InvalidPlan('Nothing more planned')
         (kind, data) = self.__plan[0]
         if kind == 'e':
+            self.__plan.pop(0)
             raise socket.error('False socket error')
         if kind != expected:
             raise InvalidPlan('Planned ' + kind + ', but ' + expected +
@@ -108,6 +109,7 @@ class FakeCreator:
             self.__plan[0] = ('s', rest)
         else:
             self.__plan.pop(0)
+
     def all_used(self):
         """
         Returns if the whole plan was consumed.
@@ -118,15 +120,65 @@ class ParserTests(unittest.TestCase):
     """
     Testcases for the Parser class.
     """
+    def __terminate(self):
+        creator = FakeCreator([('s', b'T'), ('r', b'')])
+        parser = Parser(creator)
+        self.assertEqual(None, parser.terminate())
+        self.assertTrue(creator.all_used())
+        return parser
+
     def test_terminate(self):
         """
         Test if the command to terminate is correct and it waits for reading the
         EOF.
         """
-        creator = FakeCreator([('s', b'T'), ('r', b'')])
+        self.__terminate()
+
+    def test_terminate_error1(self):
+        """
+        Test it reports an exception when there's error terminating the creator.
+        This one raises an error when receiving the EOF.
+        """
+        creator = FakeCreator([('s', b'T'), ('e', None)])
         parser = Parser(creator)
-        self.assertEqual(None, parser.terminate())
-        self.assertTrue(creator.all_used())
+        with self.assertRaises(CreatorError) as cm:
+            parser.terminate()
+        self.assertTrue(cm.exception.fatal)
+        self.assertEqual(None, cm.exception.errno)
+
+    def test_terminate_error2(self):
+        """
+        Test it reports an exception when there's error terminating the creator.
+        This one raises an error when sending data.
+        """
+        creator = FakeCreator([('e', None)])
+        parser = Parser(creator)
+        with self.assertRaises(CreatorError) as cm:
+            parser.terminate()
+        self.assertTrue(cm.exception.fatal)
+        self.assertEqual(None, cm.exception.errno)
+
+    def test_terminate_twice(self):
+        """
+        Test we can't terminate twice.
+        """
+        parser = self.__terminate()
+        with self.assertRaises(CreatorError) as cm:
+            parser.terminate()
+        self.assertTrue(cm.exception.fatal)
+        self.assertEqual(None, cm.exception.errno)
+
+    def test_terminate_error3(self):
+        """
+        Test it reports an exception when there's error terminating the creator.
+        This one sends data when it should have terminated.
+        """
+        creator = FakeCreator([('s', b'T'), ('r', b'Extra data')])
+        parser = Parser(creator)
+        with self.assertRaises(CreatorError) as cm:
+            parser.terminate()
+        self.assertTrue(cm.exception.fatal)
+        self.assertEqual(None, cm.exception.errno)
 
     def test_crash(self):
         """
@@ -189,5 +241,31 @@ class ParserTests(unittest.TestCase):
         self.__create('2001:db8::', socket.SOCK_STREAM,
             b'T6\0\x2A\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\0\0')
 
+    def test_create_terminated(self):
+        """
+        Test we can't request sockets after it was terminated.
+        """
+        parser = self.__terminate()
+        with self.assertRaises(CreatorError) as cm:
+            parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
+        self.assertTrue(cm.exception.fatal)
+        self.assertEqual(None, cm.exception.errno)
+
+    def test_invalid_socktype(self):
+        """
+        Test invalid socket type is rejected
+        """
+        self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket,
+                          IPAddr('0.0.0.0'), 42, 'RAW')
+
+    def test_invalid_family(self):
+        """
+        Test it rejects invalid address family.
+        """
+        addr = IPAddr('0.0.0.0')
+        addr.family = 'Nonsense'
+        self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket,
+                          addr, 42, socket.SOCK_DGRAM)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/bin/sockcreator/README b/src/bin/sockcreator/README
index 4dbbee7..e142d19 100644
--- a/src/bin/sockcreator/README
+++ b/src/bin/sockcreator/README
@@ -3,7 +3,7 @@ The socket creator
 
 The only thing we need higher rights than standard user is binding sockets to
 ports lower than 1024. So we will have a separate process that keeps the
-rights, while the rests drop them for security reasons.
+rights, while the rest drops them for security reasons.
 
 This process is the socket creator. Its goal is to be as simple as possible
 and to contain as little code as possible to minimise the amount of code




More information about the bind10-changes mailing list