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))
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))
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)
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))
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)
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)
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)
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)
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
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
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
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.