""" Address Revealing XML-RPC Server. This is a modified version of SimpleXMLRPCServer. AddressRevealingXMLRPCServer is a subclass of SimpleXMLRPCServer. It works mostly like SimpleXMLRPCServer, except that the client's address is passed to the _dispatch method and the default _dispatch methods is also modified. Example: # Server: server=AddressRevealingXMLRPCServer(('',8000)) def whoami(client_address): ''' Note that the registered function takes one parameter even if the client doesn't supply any parameters. ''' return "%s:%d"%client_address server.register_function(whoami) server.serve_forever() # Client: s = xmlrpclib.Server("http://localhost:8000") s.whoami() ## This returns '127.0.0.1:xxxxx' ## where xxxxx is the port connected to the server. """ import SimpleXMLRPCServer import xmlrpclib from xmlrpclib import Fault import SocketServer import BaseHTTPServer import sys import os try: import fcntl except ImportError: fcntl = None from SimpleXMLRPCServer import resolve_dotted_attribute, list_public_methods, remove_duplicates class AddressRevealingXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): def do_POST(self): """Handles the HTTP POST request. Modified so that it passes the client_address to _dispatch() """ if not self.is_rpc_path_valid(): self.report_404() return try: max_chunk_size = 10*1024*1024 size_remaining = int(self.headers["content-length"]) L = [] while size_remaining: chunk_size = min(size_remaining, max_chunk_size) L.append(self.rfile.read(chunk_size)) size_remaining -= len(L[-1]) data = ''.join(L) ## MODIFICATION: self.client_address is simply passed to ## the _marshaled_dispatch method for further processing. response = self.server._marshaled_dispatch( data, getattr(self, '_dispatch', None), client_address=self.client_address ## MODIFICATION ) except Exception,e: print e self.send_response(500) self.end_headers() else: self.send_response(200) self.send_header("Content-type", "text/xml") self.send_header("Content-length", str(len(response))) self.end_headers() self.wfile.write(response) self.wfile.flush() self.connection.shutdown(1) class AddressRevealingXMLRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): def _marshaled_dispatch(self, data, dispatch_method = None, client_address = None): """Dispatches an XML-RPC method from marshalled (XML) data. Modified so that it passes client_address to _dispatch. """ try: params, method = xmlrpclib.loads(data) ## MODIFICATION: Also pass client_address to dispatch_method if dispatch_method is not None: response = dispatch_method(method, params, client_address=client_address) ## MODIFICATION else: response = self._dispatch(method, params, client_address=client_address) ## MODIFICATION response = (response,) response = xmlrpclib.dumps(response, methodresponse=1, allow_none=self.allow_none, encoding=self.encoding) except Fault, fault: response = xmlrpclib.dumps(fault, allow_none=self.allow_none, encoding=self.encoding) except Exception, e: print 'response exception:',e response = xmlrpclib.dumps( xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)), encoding=self.encoding, allow_none=self.allow_none, ) return response def system_multicall(self, call_list, client_address): """ Modified so that it passes client_address to _dispatch. """ results = [] for call in call_list: method_name = call['methodName'] params = call['params'] try: ## MODIFICATION: Passes client_address to _dispatch. results.append([self._dispatch(method_name, params, client_address=client_address)]) ## MODIFICATION except Fault, fault: results.append( {'faultCode' : fault.faultCode, 'faultString' : fault.faultString} ) except: results.append( {'faultCode' : 1, 'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)} ) return results def _dispatch(self, method, params, client_address): """Dispatches the XML-RPC method. Modified so that it passes client_address to _dispatch. NOTE: By modifying this method means that the default _dispatch API has been changed. _dispatch now takes 3 arguments: original method and params plus the client_address. """ func = None try: func = self.funcs[method] except KeyError: if self.instance is not None: if hasattr(self.instance, '_dispatch'): ## MODIFICATION: Pass client_address to _dispatch return self.instance._dispatch(method, params, client_address=client_address) else: # call instance method directly try: func = resolve_dotted_attribute( self.instance, method, self.allow_dotted_names ) except AttributeError: pass if func is not None: return func(client_address, *params) else: raise Exception('method "%s" is not supported' % method) class AddressRevealingXMLRPCServer(AddressRevealingXMLRPCDispatcher, SimpleXMLRPCServer.SimpleXMLRPCServer): """ An XML-RPC server that passes the client address to called function. """ def __init__(self, addr, requestHandler=AddressRevealingXMLRPCRequestHandler, logRequests=True, allow_none=False, encoding=None): SimpleXMLRPCServer.SimpleXMLRPCServer.__init__( self,addr,requestHandler,logRequests,allow_none,encoding)