2016-12-09 4 views
0

Ich implementiert Pickle-RPC in Bezug auf Python xmlrpc-Modul.Zugriff auf Eigenschaften über RPC

Clientseitig kann ich Methoden der Instanz aufrufen, die auf dem RPC-Server registriert sind.

s = ServerProxy(('127.0.0.1', 49152), DatagramRequestSender) 
s.foo('bar') 

Ich möchte auch auf Eigenschaften zugreifen.

s = ServerProxy(('127.0.0.1', 49152), DatagramRequestSender) 
s.foobar 

Wie soll ich implementieren?

client.py

import pickle 
import socket 
from io import BytesIO 


class StreamRequestSender: 
    max_packet_size = 8192 
    address_family = socket.AF_INET 
    socket_type = socket.SOCK_STREAM 

    def send_request(self, address, request): 
     with socket.socket(self.address_family, self.socket_type) as client_socket: 
      with client_socket.makefile('wb') as wfile: 
       pickle.dump(request, wfile) 
      with client_socket.makefile('rb') as rfile: 
       response = pickle.load(rfile) 
       return response 


class DatagramRequestSender(StreamRequestSender): 
    socket_type = socket.SOCK_DGRAM 

    def send_request(self, address, request): 
     with socket.socket(self.address_family, self.socket_type) as client_socket: 
      with BytesIO() as wfile: 
       pickle.dump(request, wfile) 
       client_socket.sendto(wfile.getvalue(), address) 
      data = client_socket.recv(self.max_packet_size) 
      with BytesIO(data) as rfile: 
       response = pickle.load(rfile) 
       return response 


class ServerProxy: 
    def __init__(self, address, RequestSenderClass): 
     self.__address = address 
     self.__request_sender = RequestSenderClass() 

    def __send(self, method, args): 
     request = (method, args) 
     response = self.__request_sender.send_request(self.__address, request) 
     return response 

    def __getattr__(self, name): 
     return _Method(self.__send, name) 


class _Method: 
    def __init__(self, send, name): 
     self.__send = send 
     self.__name = name 

    def __getattr__(self, name): 
     return _Method(self.__send, "{}.{}".format(self.__name, name)) 

    def __call__(self, *args): 
     return self.__send(self.__name, args) 


if __name__ == '__main__': 
    s = ServerProxy(('127.0.0.1', 49152), DatagramRequestSender) 
    print(s.pow(2, 160)) 

server.py

import pickle 
import sys 
from socketserver import StreamRequestHandler, DatagramRequestHandler, ThreadingTCPServer, ThreadingUDPServer 


class RPCRequestHandler: 
    def handle(self): 
     method, args = pickle.load(self.rfile) 
     response = self.server.dispatch(method, args) 
     pickle.dump(response, self.wfile) 


class StreamRPCRequestHandler(RPCRequestHandler, StreamRequestHandler): 
    pass 


class DatagramRPCRequestHandler(RPCRequestHandler, DatagramRequestHandler): 
    pass 


class RPCDispatcher: 
    def __init__(self, instance=None): 
     self.__instance = instance 

    def register_instance(self, instance): 
     self.__instance = instance 

    def dispatch(self, method, args): 
     _method = None 
     if self.__instance is not None: 
      try: 
       _method = self._resolve_dotted_attribute(self.__instance, method) 
      except AttributeError: 
       pass 
     if _method is not None: 
      return _method(*args) 
     else: 
      raise Exception('method "{}" is not supported'.format(method)) 

    @staticmethod 
    def _resolve_dotted_attribute(obj, dotted_attribute): 
     attributes = dotted_attribute.split('.') 
     for attribute in attributes: 
      if attribute.startswith('_'): 
       raise AttributeError('attempt to access private attribute "{}"'.format(attribute)) 
      else: 
       obj = getattr(obj, attribute) 
     return obj 


class RPCServer(ThreadingUDPServer, RPCDispatcher): 
    def __init__(self, server_address, RPCRequestHandlerClass, instance=None): 
     ThreadingUDPServer.__init__(self, server_address, RPCRequestHandlerClass) 
     RPCDispatcher.__init__(self, instance) 


if __name__ == '__main__': 
    class ExampleService: 
     def pow(self, base, exp): 
      return base ** exp 


    s = RPCServer(('127.0.0.1', 49152), DatagramRPCRequestHandler, ExampleService()) 
    print('Serving Pickle-RPC on localhost port 49152') 
    print('It is advisable to run this example server within a secure, closed network.') 
    try: 
     s.serve_forever() 
    except KeyboardInterrupt: 
     print("\nKeyboard interrupt received, exiting.") 
     s.server_close() 
     sys.exit(0) 

Antwort

1

Sie finden das Attribut Zugriff auf das Objekt, das er über seine zwingende __getattr__ Methode, herauszufinden, müssen abfangen ist nur ein Attributzugriff anstelle eines Methodenaufrufs und sendet eine bestimmte Nachricht an Ihren Server, der die Methode nicht aufruft, sondern stattdessen den Wert des referenzierten attr zurückgibt direkt. Dies ist genau what Pyro4 does. Code: https://github.com/irmen/Pyro4/blob/master/src/Pyro4/core.py#L255

Auch: Pickle als Serialisierungsformat ist ein großes Sicherheitsproblem. Es ermöglicht eine beliebige Remotecodeausführung. Also, für die Liebe zu allem, was heilig ist, stellen Sie sicher, dass Sie den Kunden absolut vertrauen und keine zufälligen Verbindungen über das Internet akzeptieren.

+0

Die 'Proxy' Klasse von Pyro4 empfängt Remote-Attribute, indem sie dynamisch gemanagte' _pyroAttrs' verwendet, wenn ich es richtig verstehe. Wir können keine Methoden/Eigenschaften dynamisch ohne irgendwelche Kommunikation bestimmen, oder? P.S. Pyro4 ist eine sehr schöne Bibliothek. Ich würde gerne etwas fragen. Einer von ihnen, Wie absorbiert Pyro 4 den Unterschied zwischen TCP und UDP? Vielen Dank. – sira

+0

Ich verstehe deine Frage nicht. Pyro4 verwendet ausschließlich TCP/IP-Sockets, es gibt keine Möglichkeit, verlässliche RPCs in der Art und Weise durchzuführen, wie Pyro es tut, über UDP (ein unzuverlässiges Protokoll, das nicht einmal die Reihenfolge Ihrer Pakete garantiert) - aber das ist es nicht der Ort, um dies zu diskutieren :) –

+0

Oh, ich verstehe. Vielen Dank. – sira

Verwandte Themen