Example #1
0
 def testHmacMethod(self):
     data = Message(Pyro4.message.MSG_RESULT, b"test", 42, 0, 1, hmac_key=b"test key")
     digest = data.hmac()
     self.assertTrue(len(digest) > 10)
     data = Message(Pyro4.message.MSG_RESULT, b"test", 42, 0, 1)
     with self.assertRaises(TypeError):
         data.hmac()
Example #2
0
 def _sendExceptionResponse(self, connection, seq, serializer_id, exc_value,
                            tbinfo):
     """send an exception back including the local traceback info"""
     exc_value._pyroTraceback = tbinfo
     if sys.platform == "cli":
         util.fixIronPythonExceptionForPickle(exc_value,
                                              True)  # piggyback attributes
     serializer = util.get_serializer_by_id(serializer_id)
     try:
         data, compressed = serializer.serializeData(exc_value)
     except:
         # the exception object couldn't be serialized, use a generic PyroError instead
         xt, xv, tb = sys.exc_info()
         msg = "Error serializing exception: %s. Original exception: %s: %s" % (
             str(xv), type(exc_value), str(exc_value))
         exc_value = errors.PyroError(msg)
         exc_value._pyroTraceback = tbinfo
         if sys.platform == "cli":
             util.fixIronPythonExceptionForPickle(
                 exc_value, True)  # piggyback attributes
         data, compressed = serializer.serializeData(exc_value)
     flags = Pyro4.message.FLAGS_EXCEPTION
     if compressed:
         flags |= Pyro4.message.FLAGS_COMPRESSED
     if Pyro4.config.LOGWIRE:
         log.debug(
             "daemon wiredata sending (error response): msgtype=%d flags=0x%x ser=%d seq=%d data=%r"
             % (Pyro4.message.MSG_RESULT, flags, serializer.serializer_id,
                seq, data))
     msg = Message(Pyro4.message.MSG_RESULT, data, serializer.serializer_id,
                   flags, seq)
     connection.send(msg.to_bytes())
Example #3
0
 def testRecvNoAnnotations(self):
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello", 42, 0, 0)
     c = ConnectionMock()
     c.send(msg.to_bytes())
     msg = Message.recv(c)
     self.assertEqual(0, len(c.received))
     self.assertEqual(5, msg.data_size)
     self.assertEqual(b"hello", msg.data)
     self.assertEqual(0, msg.annotations_size)
     self.assertEqual(0, len(msg.annotations))
Example #4
0
 def testMessageHeaderDatasize(self):
     msg = Message(Pyro4.message.MSG_RESULT, b"hello", 12345, 60006, 30003, hmac_key=b"secret")
     msg.data_size = 0x12345678  # hack it to a large value to see if it comes back ok
     hdr = msg.to_bytes()[:24]
     msg = Message.from_header(hdr)
     self.assertEqual(Pyro4.message.MSG_RESULT, msg.type)
     self.assertEqual(60006, msg.flags)
     self.assertEqual(0x12345678, msg.data_size)
     self.assertEqual(12345, msg.serializer_id)
     self.assertEqual(30003, msg.seq)
Example #5
0
 def testRecvNoAnnotations(self):
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello", 42, 0, 0)
     c = ConnectionMock()
     c.send(msg.to_bytes())
     msg = Message.recv(c)
     self.assertEqual(0, len(c.received))
     self.assertEqual(5, msg.data_size)
     self.assertEqual(b"hello", msg.data)
     self.assertEqual(0, msg.annotations_size)
     self.assertEqual(0, len(msg.annotations))
Example #6
0
 def testMessageHeaderDatasize(self):
     msg = Message(Pyro4.message.MSG_RESULT, b"hello", 12345, 60006, 30003)
     msg.data_size = 0x12345678  # hack it to a large value to see if it comes back ok
     hdr = msg.to_bytes()[:24]
     msg = Message.from_header(hdr)
     self.assertEqual(Pyro4.message.MSG_RESULT, msg.type)
     self.assertEqual(60006, msg.flags)
     self.assertEqual(0x12345678, msg.data_size)
     self.assertEqual(12345, msg.serializer_id)
     self.assertEqual(30003, msg.seq)
Example #7
0
 def testRecvAnnotations(self):
     annotations = {"TEST": b"abcde"}
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello", self.ser.serializer_id, 0, 0, annotations, b"secret")
     c = ConnectionMock()
     c.send(msg.to_bytes())
     msg = Message.recv(c, hmac_key=b"secret")
     self.assertEqual(0, len(c.received))
     self.assertEqual(5, msg.data_size)
     self.assertEqual(b"hello", msg.data)
     self.assertEqual(b"abcde", msg.annotations["TEST"])
     self.assertIn("HMAC", msg.annotations)
Example #8
0
 def testAnnotations(self):
     annotations = {"TEST": b"abcde"}
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello", self.ser.serializer_id, 0, 0, annotations, b"secret")
     data = msg.to_bytes()
     annotations_size = 4 + 2 + 20 + 4 + 2 + 5
     self.assertEqual(msg.header_size + 5 + annotations_size, len(data))
     self.assertEqual(annotations_size, msg.annotations_size)
     self.assertEqual(2, len(msg.annotations))
     self.assertEqual(b"abcde", msg.annotations["TEST"])
     mac = pyrohmac(b"hello", b"secret", annotations)
     self.assertEqual(mac, msg.annotations["HMAC"])
Example #9
0
 def testRecvAnnotations(self):
     annotations = { b"TEST": b"abcde" }
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello", self.ser.serializer_id, 0, 0, annotations)
     c = ConnectionMock()
     c.send(msg.to_bytes())
     msg = Message.recv(c)
     self.assertEquals(0, len(c.received))
     self.assertEquals(5, msg.data_size)
     self.assertEquals(b"hello", msg.data)
     self.assertEquals(b"abcde", msg.annotations[b"TEST"])
     self.assertTrue(b"HMAC" in msg.annotations)
Example #10
0
 def testMaxDataSize(self):
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello", 42, 0, 0)
     msg.data_size = 0x7fffffff  # still within 32 bits signed limits
     msg.to_bytes()
     msg.data_size = 0x80000000  # overflow, Pyro has a 2 gigabyte message size limitation
     with self.assertRaises(ValueError) as ex:
         msg.to_bytes()
     self.assertEqual("invalid message size (outside range 0..2Gb)", str(ex.exception))
     msg.data_size = -42
     with self.assertRaises(ValueError) as ex:
         msg.to_bytes()
     self.assertEqual("invalid message size (outside range 0..2Gb)", str(ex.exception))
Example #11
0
 def testAnnotations(self):
     annotations = {"TEST": b"abcde"}
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello",
                   self.ser.serializer_id, 0, 0, annotations)
     data = msg.to_bytes()
     annotations_size = 4 + 2 + 20 + 4 + 2 + 5
     self.assertEqual(msg.header_size + 5 + annotations_size, len(data))
     self.assertEqual(annotations_size, msg.annotations_size)
     self.assertEqual(2, len(msg.annotations))
     self.assertEqual(b"abcde", msg.annotations["TEST"])
     mac = pyrohmac(b"hello", annotations)
     self.assertEqual(mac, msg.annotations["HMAC"])
Example #12
0
 def _handshake(self, conn):
     """Perform connection handshake with new clients"""
     # For now, client is not sending anything. Just respond with a CONNECT_OK.
     # We need a minimal amount of data or the socket will remain blocked
     # on some systems... (messages smaller than 40 bytes)
     # Return True for successful handshake, False if something was wrong.
     # We default to the marshal serializer to send message payload of "ok"
     ser = util.get_serializer("marshal")
     data = ser.dumps("ok")
     msg = Message(Pyro4.message.MSG_CONNECTOK, data, ser.serializer_id, 0, 1)
     conn.send(msg.to_bytes())
     return True
Example #13
0
 def testRecvAnnotations(self):
     annotations = {"TEST": b"abcde"}
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello",
                   self.ser.serializer_id, 0, 0, annotations)
     c = ConnectionMock()
     c.send(msg.to_bytes())
     msg = Message.recv(c)
     self.assertEqual(0, len(c.received))
     self.assertEqual(5, msg.data_size)
     self.assertEqual(b"hello", msg.data)
     self.assertEqual(b"abcde", msg.annotations["TEST"])
     self.assertTrue("HMAC" in msg.annotations)
Example #14
0
 def testCompression(self):
     data = b"The quick brown fox jumps over the lazy dog."*10
     compressed_data = zlib.compress(data)
     flags = Pyro4.message.FLAGS_COMPRESSED
     msg = Message(Pyro4.message.MSG_INVOKE, compressed_data, 42, flags, 1, hmac_key=b"secret")
     self.assertNotEqual(data, msg.data)
     data_size = msg.data_size
     self.assertLess(data_size, len(data))
     msg.decompress_if_needed()
     self.assertEqual(data, msg.data)
     self.assertEqual(0, msg.flags)
     self.assertGreater(msg.data_size, data_size)
Example #15
0
 def testChecksum(self):
     msg = Message(Pyro4.message.MSG_RESULT, b"test", 42, 0, 1, hmac_key=b"secret")
     c = ConnectionMock()
     c.send(msg.to_bytes())
     # corrupt the checksum bytes
     data = c.received
     data = data[:msg.header_size - 2] + b'\x00\x00' + data[msg.header_size:]
     c = ConnectionMock(data)
     try:
         Message.recv(c)
         self.fail("crash expected")
     except Pyro4.errors.ProtocolError as x:
         self.assertIn("checksum", str(x))
Example #16
0
 def connectionMade(self):
     """
     Handshake - should replicate Pyro4.Daemon._handshake()
     """
     log = logger.debug
     if self.state == "server":
         log("Connection made with Pyro4Protocol")
         log("... attempting handshake")
         ser = util.get_serializer("marshal")
         data = ser.dumps("ok")
         msg = Message(Pyro4.message.MSG_CONNECTOK, data, ser.serializer_id, 0, 1)
         self.transport.write(msg.to_bytes())
         self.state = "header"
Example #17
0
 def testAnnotationsIdLength4(self):
     try:
         msg = Message(Pyro4.message.MSG_CONNECT, b"hello", self.ser.serializer_id, 0, 0, {"TOOLONG": b"abcde"}, b"secret")
         _ = msg.to_bytes()
         self.fail("should fail, too long")
     except Pyro4.errors.ProtocolError:
         pass
     try:
         msg = Message(Pyro4.message.MSG_CONNECT, b"hello", self.ser.serializer_id, 0, 0, {"QQ": b"abcde"}, b"secret")
         _ = msg.to_bytes()
         self.fail("should fail, too short")
     except Pyro4.errors.ProtocolError:
         pass
Example #18
0
 def testChecksum(self):
     msg = Message(Pyro4.message.MSG_RESULT, b"test", 42, 0, 1)
     c = ConnectionMock()
     c.send(msg.to_bytes())
     # corrupt the checksum bytes
     data = c.received
     data = data[:msg.header_size -
                 2] + b'\x00\x00' + data[msg.header_size:]
     c = ConnectionMock(data)
     try:
         Message.recv(c)
         self.fail("crash expected")
     except Pyro4.errors.ProtocolError as x:
         self.assertTrue("checksum" in str(x))
Example #19
0
 def _pyroInvoke(self, methodname, vargs, kwargs, flags=0):
     """perform the remote method call communication"""
     if self._pyroConnection is None:
         # rebind here, don't do it from inside the invoke because deadlock will occur
         self.__pyroCreateConnection()
     serializer = util.get_serializer(Pyro4.config.SERIALIZER)
     data, compressed = serializer.serializeCall(
         self._pyroConnection.objectId, methodname, vargs, kwargs,
         compress=Pyro4.config.COMPRESSION)
     if compressed:
         flags |= Pyro4.message.FLAGS_COMPRESSED
     if methodname in self._pyroOneway:
         flags |= Pyro4.message.FLAGS_ONEWAY
     with self.__pyroLock:
         self._pyroSeq=(self._pyroSeq+1)&0xffff
         if Pyro4.config.LOGWIRE:
             log.debug("proxy wiredata sending: msgtype=%d flags=0x%x ser=%d seq=%d data=%r" % (Pyro4.message.MSG_INVOKE, flags, serializer.serializer_id, self._pyroSeq, data))
         msg = Message(Pyro4.message.MSG_INVOKE, data, serializer.serializer_id, flags, self._pyroSeq)
         try:
             self._pyroConnection.send(msg.to_bytes())
             del msg  # invite GC to collect the object, don't wait for out-of-scope
             if flags & Pyro4.message.FLAGS_ONEWAY:
                 return None    # oneway call, no response data
             else:
                 msg = Message.recv(self._pyroConnection, [Pyro4.message.MSG_RESULT])
                 if Pyro4.config.LOGWIRE:
                     log.debug("proxy wiredata received: msgtype=%d flags=0x%x ser=%d seq=%d data=%r" % (msg.type, msg.flags, msg.serializer_id, msg.seq, msg.data))
                 self.__pyroCheckSequence(msg.seq)
                 if msg.serializer_id != serializer.serializer_id:
                     error = "invalid serializer in response: %d" % msg.serializer_id
                     log.error(error)
                     raise errors.ProtocolError(error)
                 data = serializer.deserializeData(msg.data, compressed=msg.flags & Pyro4.message.FLAGS_COMPRESSED)
                 if msg.flags & Pyro4.message.FLAGS_EXCEPTION:
                     if sys.platform=="cli":
                         util.fixIronPythonExceptionForPickle(data, False)
                     raise data
                 else:
                     return data
         except (errors.CommunicationError, KeyboardInterrupt):
             # Communication error during read. To avoid corrupt transfers, we close the connection.
             # Otherwise we might receive the previous reply as a result of a new methodcall!
             # Special case for keyboardinterrupt: people pressing ^C to abort the client
             # may be catching the keyboardinterrupt in their code. We should probably be on the
             # safe side and release the proxy connection in this case too, because they might
             # be reusing the proxy object after catching the exception...
             self._pyroRelease()
             raise
Example #20
0
 def testProtocolVersion(self):
     version = Pyro4.constants.PROTOCOL_VERSION
     Pyro4.constants.PROTOCOL_VERSION = 0  # fake invalid protocol version number
     msg = Message(Pyro4.message.MSG_RESULT, b"", self.ser.serializer_id, 0,
                   1).to_bytes()
     Pyro4.constants.PROTOCOL_VERSION = version
     self.assertRaises(Pyro4.errors.ProtocolError, Message.from_header, msg)
Example #21
0
 def testCompression(self):
     data = b"The quick brown fox jumps over the lazy dog." * 10
     compressed_data = zlib.compress(data)
     flags = Pyro4.message.FLAGS_COMPRESSED
     msg = Message(Pyro4.message.MSG_INVOKE,
                   compressed_data,
                   42,
                   flags,
                   1,
                   hmac_key=b"secret")
     self.assertNotEqual(data, msg.data)
     data_size = msg.data_size
     self.assertLess(data_size, len(data))
     msg.decompress_if_needed()
     self.assertEqual(data, msg.data)
     self.assertEqual(0, msg.flags)
     self.assertGreater(msg.data_size, data_size)
Example #22
0
    def testMessage(self):
        Message(99, b"", self.ser.serializer_id, 0, 0, hmac_key=b"secret")  # doesn't check msg type here
        self.assertRaises(Pyro4.errors.ProtocolError, Message.from_header, "FOOBAR")
        msg = Message(Pyro4.message.MSG_CONNECT, b"hello", self.ser.serializer_id, 0, 0, hmac_key=b"secret")
        self.assertEqual(Pyro4.message.MSG_CONNECT, msg.type)
        self.assertEqual(5, msg.data_size)
        self.assertEqual(b"hello", msg.data)
        self.assertEqual(4 + 2 + 20, msg.annotations_size)
        mac = pyrohmac(b"hello", b"secret", msg.annotations)
        self.assertDictEqual({"HMAC": mac}, msg.annotations)

        hdr = msg.to_bytes()[:24]
        msg = Message.from_header(hdr)
        self.assertEqual(Pyro4.message.MSG_CONNECT, msg.type)
        self.assertEqual(4 + 2 + 20, msg.annotations_size)
        self.assertEqual(5, msg.data_size)

        hdr = Message(Pyro4.message.MSG_RESULT, b"", self.ser.serializer_id, 0, 0, hmac_key=b"secret").to_bytes()[:24]
        msg = Message.from_header(hdr)
        self.assertEqual(Pyro4.message.MSG_RESULT, msg.type)
        self.assertEqual(4 + 2 + 20, msg.annotations_size)
        self.assertEqual(0, msg.data_size)

        hdr = Message(Pyro4.message.MSG_RESULT, b"hello", 12345, 60006, 30003, hmac_key=b"secret").to_bytes()[:24]
        msg = Message.from_header(hdr)
        self.assertEqual(Pyro4.message.MSG_RESULT, msg.type)
        self.assertEqual(60006, msg.flags)
        self.assertEqual(5, msg.data_size)
        self.assertEqual(12345, msg.serializer_id)
        self.assertEqual(30003, msg.seq)

        msg = Message(255, b"", self.ser.serializer_id, 0, 255, hmac_key=b"secret").to_bytes()
        self.assertEqual(50, len(msg))
        msg = Message(1, b"", self.ser.serializer_id, 0, 255, hmac_key=b"secret").to_bytes()
        self.assertEqual(50, len(msg))
        msg = Message(1, b"", self.ser.serializer_id, flags=253, seq=254, hmac_key=b"secret").to_bytes()
        self.assertEqual(50, len(msg))

        # compression is a job of the code supplying the data, so the messagefactory should leave it untouched
        data = b"x" * 1000
        msg = Message(Pyro4.message.MSG_INVOKE, data, self.ser.serializer_id, 0, 0, hmac_key=b"secret").to_bytes()
        msg2 = Message(
            Pyro4.message.MSG_INVOKE,
            data,
            self.ser.serializer_id,
            Pyro4.message.FLAGS_COMPRESSED,
            0,
            hmac_key=b"secret",
        ).to_bytes()
        self.assertEqual(len(msg), len(msg2))
Example #23
0
 def testHmac(self):
     try:
         hk = Pyro4.config.HMAC_KEY
         Pyro4.config.HMAC_KEY = b"test key"
         data = Message(Pyro4.message.MSG_RESULT, b"test", 42, 0,
                        1).to_bytes()
         c = ConnectionMock(data)
     finally:
         Pyro4.config.HMAC_KEY = hk
     # test checking of different hmacs
     try:
         Message.recv(c)
         self.fail("crash expected")
     except Pyro4.errors.SecurityError as x:
         self.assertTrue("hmac" in str(x))
     c = ConnectionMock(data)
     # test that it works again when resetting the key
     try:
         hk = Pyro4.config.HMAC_KEY
         Pyro4.config.HMAC_KEY = b"test key"
         Message.recv(c)
     finally:
         Pyro4.config.HMAC_KEY = hk
     c = ConnectionMock(data)
     # test that it doesn't work when no key is set
     try:
         hk = Pyro4.config.HMAC_KEY
         Pyro4.config.HMAC_KEY = b""
         Message.recv(c)
         self.fail("crash expected")
     except Pyro4.errors.SecurityError as x:
         self.assertTrue("hmac key config" in str(x))
     finally:
         Pyro4.config.HMAC_KEY = hk
Example #24
0
 def __pyroCreateConnection(self, replaceUri=False):
     """
     Connects this proxy to the remote Pyro daemon. Does connection handshake.
     Returns true if a new connection was made, false if an existing one was already present.
     """
     with self.__pyroConnLock:
         if self._pyroConnection is not None:
             return False     # already connected
         from Pyro4.naming import resolve  # don't import this globally because of cyclic dependancy
         uri=resolve(self._pyroUri)
         # socket connection (normal or Unix domain socket)
         conn=None
         log.debug("connecting to %s", uri)
         connect_location=uri.sockname if uri.sockname else (uri.host, uri.port)
         with self.__pyroLock:
             try:
                 if self._pyroConnection is not None:
                     return False    # already connected
                 sock=socketutil.createSocket(connect=connect_location, reuseaddr=Pyro4.config.SOCK_REUSE, timeout=self.__pyroTimeout)
                 conn=socketutil.SocketConnection(sock, uri.object)
                 # Do handshake. For now, no need to send anything. (message type CONNECT is not yet used)
                 msg = Message.recv(conn, None)
                 # any trailing data (dataLen>0) is an error message, if any
             except Exception:
                 x=sys.exc_info()[1]
                 if conn:
                     conn.close()
                 err="cannot connect: %s" % x
                 log.error(err)
                 if isinstance(x, errors.CommunicationError):
                     raise
                 else:
                     ce = errors.CommunicationError(err)
                     ce.__cause__ = x
                     raise ce
             else:
                 if msg.type==Pyro4.message.MSG_CONNECTFAIL:
                     error="connection rejected"
                     if msg.data:
                         serializer = util.get_serializer_by_id(msg.serializer_id)
                         data = serializer.deserializeData(msg.data, compressed=msg.flags & Pyro4.message.FLAGS_COMPRESSED)
                         error += ", reason: " + data
                     conn.close()
                     log.error(error)
                     raise errors.CommunicationError(error)
                 elif msg.type==Pyro4.message.MSG_CONNECTOK:
                     self._pyroConnection=conn
                     if replaceUri:
                         self._pyroUri=uri
                     log.debug("connected to %s", self._pyroUri)
                     return True
                 else:
                     conn.close()
                     err="connect: invalid msg type %d received" % msg.type
                     log.error(err)
                     raise errors.ProtocolError(err)
Example #25
0
 def testHmac(self):
     data = Message(Pyro4.message.MSG_RESULT, b"test", 42, 0, 1, hmac_key=b"test key").to_bytes()
     c = ConnectionMock(data)
     # test checking of different hmacs
     try:
         Message.recv(c, hmac_key=None)
         self.fail("crash expected")
     except Pyro4.errors.SecurityError as x:
         self.assertIn("hmac key config", str(x))
     c = ConnectionMock(data)
     try:
         Message.recv(c, hmac_key=b"T3ST-K3Y")
         self.fail("crash expected")
     except Pyro4.errors.SecurityError as x:
         self.assertIn("hmac", str(x))
     # test that it works again when providing the correct key
     c = ConnectionMock(data)
     msg = Message.recv(c, hmac_key=b"test key")
     self.assertEqual(b"test key", msg.hmac_key)
Example #26
0
 def testHmac(self):
     try:
         hk = Pyro4.config.HMAC_KEY
         Pyro4.config.HMAC_KEY = b"test key"
         data = Message(Pyro4.message.MSG_RESULT, b"test", 42, 0, 1).to_bytes()
         c = ConnectionMock(data)
     finally:
         Pyro4.config.HMAC_KEY = hk
     # test checking of different hmacs
     try:
         Message.recv(c)
         self.fail("crash expected")
     except Pyro4.errors.SecurityError as x:
         self.assertIn("hmac", str(x))
     c = ConnectionMock(data)
     # test that it works again when resetting the key
     try:
         hk = Pyro4.config.HMAC_KEY
         Pyro4.config.HMAC_KEY = b"test key"
         Message.recv(c)
     finally:
         Pyro4.config.HMAC_KEY = hk
     c = ConnectionMock(data)
     # test that it doesn't work when no key is set
     try:
         hk = Pyro4.config.HMAC_KEY
         Pyro4.config.HMAC_KEY = b""
         Message.recv(c)
         self.fail("crash expected")
     except Pyro4.errors.SecurityError as x:
         self.assertIn("hmac key config", str(x))
     finally:
         Pyro4.config.HMAC_KEY = hk
Example #27
0
 def testHmacMethod(self):
     data = Message(Pyro4.message.MSG_RESULT,
                    b"test",
                    42,
                    0,
                    1,
                    hmac_key=b"test key")
     digest = data.hmac()
     self.assertTrue(len(digest) > 10)
     data = Message(Pyro4.message.MSG_RESULT, b"test", 42, 0, 1)
     with self.assertRaises(TypeError):
         data.hmac()
Example #28
0
 def testAnnotationsIdLength4(self):
     try:
         msg = Message(Pyro4.message.MSG_CONNECT, b"hello",
                       self.ser.serializer_id, 0, 0, {"TOOLONG": b"abcde"})
         data = msg.to_bytes()
         self.fail("should fail, too long")
     except Pyro4.errors.ProtocolError:
         pass
     try:
         msg = Message(Pyro4.message.MSG_CONNECT, b"hello",
                       self.ser.serializer_id, 0, 0, {"QQ": b"abcde"})
         data = msg.to_bytes()
         self.fail("should fail, too short")
     except Pyro4.errors.ProtocolError:
         pass
Example #29
0
 def testHmac(self):
     data = Message(Pyro4.message.MSG_RESULT,
                    b"test",
                    42,
                    0,
                    1,
                    hmac_key=b"test key").to_bytes()
     c = ConnectionMock(data)
     # test checking of different hmacs
     try:
         Message.recv(c, hmac_key=None)
         self.fail("crash expected")
     except Pyro4.errors.SecurityError as x:
         self.assertIn("hmac key config", str(x))
     c = ConnectionMock(data)
     try:
         Message.recv(c, hmac_key=b"T3ST-K3Y")
         self.fail("crash expected")
     except Pyro4.errors.SecurityError as x:
         self.assertIn("hmac", str(x))
     # test that it works again when providing the correct key
     c = ConnectionMock(data)
     msg = Message.recv(c, hmac_key=b"test key")
     self.assertEqual(b"test key", msg.hmac_key)
Example #30
0
    def testMessage(self):
        Message(99, b"", self.ser.serializer_id, 0,
                0)  # doesn't check msg type here
        self.assertRaises(Pyro4.errors.ProtocolError, Message.from_header,
                          "FOOBAR")
        msg = Message(Pyro4.message.MSG_CONNECT, b"hello",
                      self.ser.serializer_id, 0, 0)
        self.assertEqual(Pyro4.message.MSG_CONNECT, msg.type)
        self.assertEqual(5, msg.data_size)
        self.assertEqual(b"hello", msg.data)
        self.assertEqual(4 + 2 + 20, msg.annotations_size)
        mac = pyrohmac(b"hello", msg.annotations)
        self.assertDictEqual({"HMAC": mac}, msg.annotations)

        hdr = msg.to_bytes()[:24]
        msg = Message.from_header(hdr)
        self.assertEqual(Pyro4.message.MSG_CONNECT, msg.type)
        self.assertEqual(4 + 2 + 20, msg.annotations_size)
        self.assertEqual(5, msg.data_size)

        hdr = Message(Pyro4.message.MSG_RESULT, b"", self.ser.serializer_id, 0,
                      0).to_bytes()[:24]
        msg = Message.from_header(hdr)
        self.assertEqual(Pyro4.message.MSG_RESULT, msg.type)
        self.assertEqual(4 + 2 + 20, msg.annotations_size)
        self.assertEqual(0, msg.data_size)

        hdr = Message(Pyro4.message.MSG_RESULT, b"hello", 12345, 60006,
                      30003).to_bytes()[:24]
        msg = Message.from_header(hdr)
        self.assertEqual(Pyro4.message.MSG_RESULT, msg.type)
        self.assertEqual(60006, msg.flags)
        self.assertEqual(5, msg.data_size)
        self.assertEqual(12345, msg.serializer_id)
        self.assertEqual(30003, msg.seq)

        msg = Message(255, b"", self.ser.serializer_id, 0, 255).to_bytes()
        self.assertEqual(50, len(msg))
        msg = Message(1, b"", self.ser.serializer_id, 0, 255).to_bytes()
        self.assertEqual(50, len(msg))
        msg = Message(1, b"", self.ser.serializer_id, flags=253,
                      seq=254).to_bytes()
        self.assertEqual(50, len(msg))

        # compression is a job of the code supplying the data, so the messagefactory should leave it untouched
        data = b"x" * 1000
        msg = Message(Pyro4.message.MSG_INVOKE, data, self.ser.serializer_id,
                      0, 0).to_bytes()
        msg2 = Message(Pyro4.message.MSG_INVOKE, data, self.ser.serializer_id,
                       Pyro4.message.FLAGS_COMPRESSED, 0).to_bytes()
        self.assertEqual(len(msg), len(msg2))
Example #31
0
    def dataReceived(self, data):
        """
        This function must aggregate and standardize the I/O behavior of several Pyro4 functions:

         - Daemon.handleRequest
         - Message.recv
         - Protocol._pyroInvoke

        Due to differences between synchronous and asynchronous approaches, twisted-pyro uses states to determine how
        to route received data.  Because state data need not persist across connections (unlike state information in
        many applications), it is attached to the Protocol.  These states are:

         - server:  indicates that a handshake will be required upon connection
         - header:  waiting on enough data to parse a message header and respond accordingly (idle state for servers)
         - annotations:  header parsed, waiting on amount of annotation data requested in header
         - data:  header parsed, waiting on amount of data requested in header
         - response:  the other end is waiting for data from us (idle state for clients)
        """
        log = logger.debug 
        log("Handling %d bytes of data" % len(data))
        self.data += data
        if self.state == "header" and len(self.data) >= Message.header_size:
            log("... enough data received to process header.")
            # Have enough data to process headers
            self.request = Message.from_header(self.data[:Message.header_size])
            if Pyro4.config.LOGWIRE:
                log("wiredata received: msgtype=%d flags=0x%x ser=%d seq=%d data=%r" %
                    (self.request.type, self.request.flags, self.request.serializer_id, self.request.seq, self.request.data))
            if self.required_message_types and self.request.type not in self.required_message_types:
                err = "invalid msg type %d received" % self.request.type
                logger.error(err)
                self._return_error(errors.ProtocolError(err))
            if self.request.serializer_id not in \
                    set([util.get_serializer(ser_name).serializer_id
                         for ser_name in Pyro4.config.SERIALIZERS_ACCEPTED]):
                self._return_error(errors.ProtocolError("message used serializer that is not accepted: %d" % self.request.serializer_id))
            self.data = self.data[Message.header_size:]
            if self.request.annotations_size:
                self.state = "annotations"
            else:
                self.state = "data"

        if self.state == "annotations" and len(self.data) >= self.request.annotations_size:
            log("... enough data received to process annotation.")
            self.request.annotations = {}
            annotations_data = self.data[:self.request.annotations_size]
            self.data = self.data[self.request.annotations_size:]
            i = 0
            while i < self.request.annotations_size:
                anno, length = struct.unpack("!4sH", annotations_data[i:i+6])
                self.request.annotations[anno] = annotations_data[i+6:i+6+length]
                i += 6+length
            if b"HMAC" in self.request.annotations and Pyro4.config.HMAC_KEY:
                if self.request.annotations[b"HMAC"] != self.request.hmac():
                    self._return_error(errors.SecurityError("message hmac mismatch"))
            elif (b"HMAC" in self.request.annotations) != bool(Pyro4.config.HMAC_KEY):
                # Message contains hmac and local HMAC_KEY not set, or vice versa. This is not allowed.
                err = "hmac key config not symmetric"
                logger.warning(err)
                self._return_error(errors.SecurityError(err))
            self.state = "data"

        if self.state == "data" and len(self.data) >= self.request.data_size:
            log("... enough data received to process data.")
            # A oneway call can be immediately followed by another call.  Otherwise, we should not receive any
            # additional data until we have sent a response
            if self.request.flags & Pyro4.message.FLAGS_ONEWAY:
                if self.request.type == message.MSG_PING:
                    error_msg = "ONEWAY ping doesn't make sense"
                    self._return_error(errors.ProtocolError(error_msg))
            else:
                if len(self.data) > self.request.data_size:
                    self.transport.loseConnection()
                    error_msg = "max message size exceeded (%d where max=%d)" % \
                                (self.request.data_size + self.request.annotations_size, Pyro4.config.MAX_MESSAGE_SIZE)
                    self._return_error(errors.ProtocolError(error_msg))

            # Transfer data to message
            self.request.data = self.data[:self.request.data_size]
            self.data = self.data[self.request.data_size:]

            # Execute message
            d = Deferred()

            if self.request.type == message.MSG_CONNECT:
                raise NotImplementedError("No action provided for MSG_CONNECT")

            elif self.request.type == message.MSG_CONNECTOK:
                # We only reach this spot if it is a valid message type so we're in client handshake mode.  Update
                # protocol to support client __call__ execution (i.e. invocation)
                self.required_message_types = [message.MSG_CONNECTOK]
                raise NotImplementedError("No action provided for MSG_CONNECTOK")

            elif self.request.type == message.MSG_CONNECTFAIL:
                raise NotImplementedError("No action provided for MSG_CONNECTFAIL")

            elif self.request.type == message.MSG_INVOKE:
                log("Responding to invoke request.")
                # Must be a static method so the Protocol can reset after oneway messages
                d.addCallback(self._pyro_remote_call)
                reactor.callLater(0, d.callback, self.request)

            elif self.request.type == message.MSG_PING:
                log("Responding to ping request.")
                reactor.callLater(0, d.callback, b"pong")

            elif self.request.type == message.MSG_RESULT:
                # Trigger callback with data or raise exception
                raise NotImplementedError("No action provided for MSG_RESULT")

            if self.request.flags & Pyro4.message.FLAGS_ONEWAY:
                log("... ONEWAY request, not building response.")

                def reraise(response):
                    if isinstance(response, Exception):
                        log.exception("ONEWAY call resulted in an exception: %s" % str(response))
                        return Failure(response)
                d.addCallback(reraise)

                # In Pyro4 core, the ONEWAY_THREADED option determines whether or not the execution order of ONEWAY
                # calls is guaranteed. To replicate this behavior, we reference the same flag.
                if Pyro4.config.ONEWAY_THREADED:
                    self.reset()
                else:
                    self.state = "blocked"

                    def process_next_message(response):
                        self.reset()
                        return response
                    d.addBoth(process_next_message)
            else:
                log("... appending response callbacks.")
                # If the previous call was not oneway, we maintain state on the protocol
                log("... setting state to 'response'")
                self.state = "response"
                log("... adding build/send callbacks")
                d.addCallback(self._build_response)
                d.addCallback(self._send_response)

        if self.state == "response" and len(self.data) > 0:
            error_msg = "data received while in response state" % \
                        (self.request.data_size + self.request.annotations_size, Pyro4.config.MAX_MESSAGE_SIZE)
            self._return_error(errors.ProtocolError(error_msg))

        if self.state == "blocked":
            # Waiting on a ONEWAY call (with guaranteed execution order) to complete.  When it completes, the system
            # will reset itself to accept the next call.
            pass
Example #32
0
 def testMaxDataSize(self):
     msg = Message(Pyro4.message.MSG_CONNECT, b"hello", 42, 0, 0)
     msg.data_size = 0x7fffffff  # still within 32 bits signed limits
     msg.to_bytes()
     msg.data_size = 0x80000000  # overflow, Pyro has a 2 gigabyte message size limitation
     with self.assertRaises(ValueError) as ex:
         msg.to_bytes()
     self.assertEqual("invalid message size (outside range 0..2Gb)",
                      str(ex.exception))
     msg.data_size = -42
     with self.assertRaises(ValueError) as ex:
         msg.to_bytes()
     self.assertEqual("invalid message size (outside range 0..2Gb)",
                      str(ex.exception))
Example #33
0
 def handleRequest(self, conn):
     """
     Handle incoming Pyro request. Catches any exception that may occur and
     wraps it in a reply to the calling side, as to not make this server side loop
     terminate due to exceptions caused by remote invocations.
     """
     request_flags = 0
     request_seq = 0
     request_serializer_id = util.MarshalSerializer.serializer_id
     wasBatched = False
     isCallback = False
     try:
         msg = Message.recv(
             conn, [Pyro4.message.MSG_INVOKE, Pyro4.message.MSG_PING])
         request_flags = msg.flags
         request_seq = msg.seq
         request_serializer_id = msg.serializer_id
         if Pyro4.config.LOGWIRE:
             log.debug(
                 "daemon wiredata received: msgtype=%d flags=0x%x ser=%d seq=%d data=%r"
                 % (msg.type, msg.flags, msg.serializer_id, msg.seq,
                    msg.data))
         if msg.type == Pyro4.message.MSG_PING:
             # return same seq, but ignore any data (it's a ping, not an echo). Nothing is deserialized.
             msg = Message(Pyro4.message.MSG_PING, b"pong",
                           msg.serializer_id, 0, msg.seq)
             if Pyro4.config.LOGWIRE:
                 log.debug(
                     "daemon wiredata sending: msgtype=%d flags=0x%x ser=%d seq=%d data=%r"
                     % (msg.type, msg.flags, msg.serializer_id, msg.seq,
                        msg.data))
             conn.send(msg.to_bytes())
             return
         if msg.serializer_id not in self.__serializer_ids:
             raise errors.ProtocolError(
                 "message used serializer that is not accepted: %d" %
                 msg.serializer_id)
         serializer = util.get_serializer_by_id(msg.serializer_id)
         objId, method, vargs, kwargs = serializer.deserializeCall(
             msg.data,
             compressed=msg.flags & Pyro4.message.FLAGS_COMPRESSED)
         del msg  # invite GC to collect the object, don't wait for out-of-scope
         obj = self.objectsById.get(objId)
         if obj is not None:
             if kwargs and sys.version_info < (2, 6,
                                               5) and os.name != "java":
                 # Python before 2.6.5 doesn't accept unicode keyword arguments
                 kwargs = dict((str(k), kwargs[k]) for k in kwargs)
             if request_flags & Pyro4.message.FLAGS_BATCH:
                 # batched method calls, loop over them all and collect all results
                 data = []
                 for method, vargs, kwargs in vargs:
                     method = util.resolveDottedAttribute(
                         obj, method, Pyro4.config.DOTTEDNAMES)
                     try:
                         result = method(
                             *vargs, **kwargs
                         )  # this is the actual method call to the Pyro object
                     except Exception:
                         xt, xv = sys.exc_info()[0:2]
                         log.debug(
                             "Exception occurred while handling batched request: %s",
                             xv)
                         xv._pyroTraceback = util.formatTraceback(
                             detailed=Pyro4.config.DETAILED_TRACEBACK)
                         if sys.platform == "cli":
                             util.fixIronPythonExceptionForPickle(
                                 xv, True)  # piggyback attributes
                         data.append(futures._ExceptionWrapper(xv))
                         break  # stop processing the rest of the batch
                     else:
                         data.append(result)
                 wasBatched = True
             else:
                 # normal single method call
                 method = util.resolveDottedAttribute(
                     obj, method, Pyro4.config.DOTTEDNAMES)
                 if request_flags & Pyro4.message.FLAGS_ONEWAY and Pyro4.config.ONEWAY_THREADED:
                     # oneway call to be run inside its own thread
                     thread = threadutil.Thread(target=method,
                                                args=vargs,
                                                kwargs=kwargs)
                     thread.setDaemon(True)
                     thread.start()
                 else:
                     isCallback = getattr(method, "_pyroCallback", False)
                     data = method(
                         *vargs, **kwargs
                     )  # this is the actual method call to the Pyro object
         else:
             log.debug("unknown object requested: %s", objId)
             raise errors.DaemonError("unknown object")
         if request_flags & Pyro4.message.FLAGS_ONEWAY:
             return  # oneway call, don't send a response
         else:
             data, compressed = serializer.serializeData(
                 data, compress=Pyro4.config.COMPRESSION)
             response_flags = 0
             if compressed:
                 response_flags |= Pyro4.message.FLAGS_COMPRESSED
             if wasBatched:
                 response_flags |= Pyro4.message.FLAGS_BATCH
             if Pyro4.config.LOGWIRE:
                 log.debug(
                     "daemon wiredata sending: msgtype=%d flags=0x%x ser=%d seq=%d data=%r"
                     % (Pyro4.message.MSG_RESULT, response_flags,
                        serializer.serializer_id, request_seq, data))
             msg = Message(Pyro4.message.MSG_RESULT, data,
                           serializer.serializer_id, response_flags,
                           request_seq)
             conn.send(msg.to_bytes())
     except Exception:
         xt, xv = sys.exc_info()[0:2]
         if xt is not errors.ConnectionClosedError:
             log.debug("Exception occurred while handling request: %r", xv)
             if not request_flags & Pyro4.message.FLAGS_ONEWAY:
                 # only return the error to the client if it wasn't a oneway call
                 tblines = util.formatTraceback(
                     detailed=Pyro4.config.DETAILED_TRACEBACK)
                 self._sendExceptionResponse(conn, request_seq,
                                             request_serializer_id, xv,
                                             tblines)
         if isCallback or isinstance(
                 xv, (errors.CommunicationError, errors.SecurityError)):
             raise  # re-raise if flagged as callback, communication or security error.