diff options
-rw-r--r-- | Demo/rpc/rpc.py | 160 |
1 files changed, 106 insertions, 54 deletions
diff --git a/Demo/rpc/rpc.py b/Demo/rpc/rpc.py index 4c790f0..d1c2c5e 100644 --- a/Demo/rpc/rpc.py +++ b/Demo/rpc/rpc.py @@ -6,6 +6,8 @@ # XXX The UDP version of the protocol resends requests when it does # XXX not receive a timely reply -- use only for idempotent calls! +# XXX There is no provision for call timeout on TCP connections + import xdr import socket import os @@ -160,20 +162,21 @@ def make_auth_unix_default(): gid = getgid() except ImportError: uid = gid = 0 - return make_auth_unix(0, socket.gethostname(), uid, gid, []) + import time + return make_auth_unix(time.time(), socket.gethostname(), uid, gid, []) # Common base class for clients class Client: - def init(self, host, prog, vers, port, type): + def init(self, host, prog, vers, port): self.host = host self.prog = prog self.vers = vers self.port = port - self.type = type - self.sock = socket.socket(socket.AF_INET, type) + self.makesocket() # Assigns to self.sock + self.bindsocket() self.sock.connect((host, port)) self.lastxid = 0 self.addpackers() @@ -181,30 +184,56 @@ class Client: self.verf = None return self - def Null(self): # Procedure 0 is always like this - self.start_call(0) - self.do_call(0) - self.end_call() - def close(self): self.sock.close() - # Functions that may be overridden by specific derived classes + def makesocket(self): + # This MUST be overridden + raise RuntimeError, 'makesocket not defined' + + def bindsocket(self): + # Override this to bind to a different port (e.g. reserved) + self.sock.bind(('', 0)) def addpackers(self): + # Override this to use derived classes from Packer/Unpacker self.packer = Packer().init() self.unpacker = Unpacker().init('') - def mkcred(self, proc): + def start_call(self, proc): + # Don't override this + self.lastxid = xid = self.lastxid + 1 + cred = self.mkcred() + verf = self.mkverf() + p = self.packer + p.reset() + p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) + + def do_call(self, *rest): + # This MUST be overridden + raise RuntimeError, 'do_call not defined' + + def end_call(self): + # Don't override this + self.unpacker.done() + + def mkcred(self): + # Override this to use more powerful credentials if self.cred == None: self.cred = (AUTH_NULL, make_auth_null()) return self.cred - def mkverf(self, proc): + def mkverf(self): + # Override this to use a more powerful verifier if self.verf == None: self.verf = (AUTH_NULL, make_auth_null()) return self.verf + def Null(self): # Procedure 0 is always like this + self.start_call(0) + self.do_call(0) + self.end_call() + # Record-Marking standard support @@ -243,18 +272,38 @@ def recvrecord(sock): return record +# Try to bind to a reserved port (must be root) + +last_resv_port_tried = None +def bindresvport(sock, host): + global last_resv_port_tried + FIRST, LAST = 600, 1024 # Range of ports to try + if last_resv_port_tried == None: + import os + last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST) + for i in range(last_resv_port_tried, LAST) + \ + range(FIRST, last_resv_port_tried): + last_resv_port_tried = i + try: + sock.bind((host, i)) + return last_resv_port_tried + except socket.error, (errno, msg): + if errno <> 114: + raise socket.error, (errno, msg) + raise RuntimeError, 'can\'t assign reserved port' + + # Raw TCP-based client class RawTCPClient(Client): - def init(self, host, prog, vers, port): - return Client.init(self, host, prog, vers, port, \ - socket.SOCK_STREAM) + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def start_call(self, proc): self.lastxid = xid = self.lastxid + 1 - cred = self.mkcred(proc) - verf = self.mkverf(proc) + cred = self.mkcred() + verf = self.mkverf() p = self.packer p.reset() p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) @@ -280,14 +329,13 @@ class RawTCPClient(Client): class RawUDPClient(Client): - def init(self, host, prog, vers, port): - return Client.init(self, host, prog, vers, port, \ - socket.SOCK_DGRAM) + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def start_call(self, proc): self.lastxid = xid = self.lastxid + 1 - cred = self.mkcred(proc) - verf = self.mkverf(proc) + cred = self.mkcred() + verf = self.mkverf() p = self.packer p.reset() p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) @@ -316,7 +364,7 @@ class RawUDPClient(Client): count = count - 1 if count < 0: raise RuntimeError, 'timeout' if timeout < 25: timeout = timeout *2 - print 'RESEND', timeout, count +## print 'RESEND', timeout, count self.sock.send(call) continue reply = self.sock.recv(bufsize) @@ -324,7 +372,7 @@ class RawUDPClient(Client): u.reset(reply) xid, verf = u.unpack_replyheader() if xid <> self.lastxid: - print 'BAD xid' +## print 'BAD xid' continue break @@ -334,9 +382,14 @@ class RawUDPClient(Client): # Port mapper interface -PMAP_PORT = 111 +# XXX CALLIT is not implemented + +# Program number, version and (fixed!) port number PMAP_PROG = 100000 PMAP_VERS = 2 +PMAP_PORT = 111 + +# Procedure numbers PMAPPROC_NULL = 0 # (void) -> void PMAPPROC_SET = 1 # (mapping) -> bool PMAPPROC_UNSET = 2 # (mapping) -> bool @@ -439,9 +492,9 @@ class TCPClient(RawTCPClient): def init(self, host, prog, vers): pmap = TCPPortMapperClient().init(host) port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) + pmap.close() if port == 0: raise RuntimeError, 'program not registered' - pmap.close() return RawTCPClient.init(self, host, prog, vers, port) @@ -451,45 +504,37 @@ class UDPClient(RawUDPClient): pmap = UDPPortMapperClient().init(host) port = pmap.Getport((prog, vers, IPPROTO_UDP, 0)) pmap.close() + if port == 0: + raise RuntimeError, 'program not registered' return RawUDPClient.init(self, host, prog, vers, port) # Server classes +# These are not symmetric to the Client classes +# XXX No attempt is made to provide authorization hooks yet + class Server: - def init(self, host, prog, vers, port, type): + def init(self, host, prog, vers, port): self.host = host # Should normally be '' for default interface self.prog = prog self.vers = vers self.port = port # Should normally be 0 for random port - self.type = type # SOCK_STREAM or SOCK_DGRAM - self.sock = socket.socket(socket.AF_INET, type) - self.sock.bind((host, port)) + self.makesocket() # Assigns to self.sock and self.prot + self.bindsocket() self.host, self.port = self.sock.getsockname() self.addpackers() return self def register(self): - if self.type == socket.SOCK_STREAM: - type = IPPROTO_TCP - elif self.type == socket.SOCK_DGRAM: - type = IPPROTO_UDP - else: - raise ValueError, 'unknown protocol type' - mapping = self.prog, self.vers, type, self.port + mapping = self.prog, self.vers, self.prot, self.port p = TCPPortMapperClient().init(self.host) if not p.Set(mapping): raise RuntimeError, 'register failed' def unregister(self): - if self.type == socket.SOCK_STREAM: - type = IPPROTO_TCP - elif self.type == socket.SOCK_DGRAM: - type = IPPROTO_UDP - else: - raise ValueError, 'unknown protocol type' - mapping = self.prog, self.vers, type, self.port + mapping = self.prog, self.vers, self.prot, self.port p = TCPPortMapperClient().init(self.host) if not p.Unset(mapping): raise RuntimeError, 'unregister failed' @@ -555,18 +600,25 @@ class Server: def handle_0(self): # Handle NULL message self.turn_around() - # Functions that may be overridden by specific derived classes + def makesocket(self): + # This MUST be overridden + raise RuntimeError, 'makesocket not defined' + + def bindsocket(self): + # Override this to bind to a different port (e.g. reserved) + self.sock.bind((self.host, self.port)) def addpackers(self): + # Override this to use derived classes from Packer/Unpacker self.packer = Packer().init() self.unpacker = Unpacker().init('') class TCPServer(Server): - def init(self, host, prog, vers, port): - return Server.init(self, host, prog, vers, port, \ - socket.SOCK_STREAM) + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.prot = IPPROTO_TCP def loop(self): self.sock.listen(0) @@ -587,13 +639,13 @@ class TCPServer(Server): class UDPServer(Server): - def init(self, host, prog, vers, port): - return Server.init(self, host, prog, vers, port, \ - socket.SOCK_DGRAM) + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.prot = IPPROTO_UDP def loop(self): while 1: - session() + self.session() def session(self): call, host_port = self.sock.recvfrom(8192) @@ -629,7 +681,7 @@ def test(): def testsvr(): # Simple test class -- proc 1 doubles its string argument as reply - class S(TCPServer): + class S(UDPServer): def handle_1(self): arg = self.unpacker.unpack_string() self.turn_around() @@ -655,7 +707,7 @@ def testclt(): if sys.argv[1:]: host = sys.argv[1] else: host = '' # Client for above server - class C(TCPClient): + class C(UDPClient): def call_1(self, arg): self.start_call(1) self.packer.pack_string(arg) |