Mercurial > ~darius > hgwebdir.cgi > pyinst
diff rpc.py @ 19:cba1c44060f5
Copied from
http://sourceforge.net/projects/pythonlabtools/
I modified them slightly to work as something is causing pack_uint to
be called instead of pack_int...
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Thu, 11 Aug 2011 17:11:58 +0930 |
parents | |
children | 91b476ebc0f2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rpc.py Thu Aug 11 17:11:58 2011 +0930 @@ -0,0 +1,965 @@ +"A an RPC module, with optimizations for VXI-11 rpc calls" +_rcsid="$Id: rpc.py 323 2011-04-06 19:10:03Z marcus $" + +#this module is 99% from the python distribution, Demo/rpc/rpc.py, with a few modifications +#by Marcus Mendenhall to improve timeout handling, etc. It probably should follow any licensing +#intent of whoever originated it. + +# Sun RPC version 2 -- RFC1057. + +# XXX There should be separate exceptions for the various reasons why +# XXX an RPC can fail, rather than using RuntimeError for everything + +# 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 +# Now there is, on receives. 2003.02.20 Marcus Mendenhall, Vanderbilt University marcus.h.mendenhall@vanderbilt.edu + +import xdrlib as xdr +import socket +import os +from exceptions import * + +RPCVERSION = 2 + +CALL = 0 +REPLY = 1 + +AUTH_NULL = 0 +AUTH_UNIX = 1 +AUTH_SHORT = 2 +AUTH_DES = 3 + +MSG_ACCEPTED = 0 +MSG_DENIED = 1 + +SUCCESS = 0 # RPC executed successfully +PROG_UNAVAIL = 1 # remote hasn't exported program +PROG_MISMATCH = 2 # remote can't support version # +PROC_UNAVAIL = 3 # program can't support procedure +GARBAGE_ARGS = 4 # procedure can't decode params + +RPC_MISMATCH = 0 # RPC version number != 2 +AUTH_ERROR = 1 # remote can't authenticate caller + +AUTH_BADCRED = 1 # bad credentials (seal broken) +AUTH_REJECTEDCRED = 2 # client must begin new session +AUTH_BADVERF = 3 # bad verifier (seal broken) +AUTH_REJECTEDVERF = 4 # verifier expired or replayed +AUTH_TOOWEAK = 5 # rejected for security reasons + + +class Packer(xdr.Packer): + + def pack_auth(self, auth): + flavor, stuff = auth + self.pack_enum(flavor) + self.pack_opaque(stuff) + + def pack_auth_unix(self, stamp, machinename, uid, gid, gids): + self.pack_uint(stamp) + self.pack_string(machinename) + self.pack_uint(uid) + self.pack_uint(gid) + self.pack_uint(len(gids)) + for i in gids: + self.pack_uint(i) + + def pack_callheader(self, xid, prog, vers, proc, cred, verf): + self.pack_uint(xid) + self.pack_enum(CALL) + self.pack_uint(RPCVERSION) + self.pack_uint(prog) + self.pack_uint(vers) + self.pack_uint(proc) + self.pack_auth(cred) + self.pack_auth(verf) + # Caller must add procedure-specific part of call + + def pack_replyheader(self, xid, verf): + self.pack_uint(xid) + self.pack_enum(REPLY) + self.pack_uint(MSG_ACCEPTED) + self.pack_auth(verf) + self.pack_enum(SUCCESS) + # Caller must add procedure-specific part of reply + + +# Exceptions +BadRPCFormat = 'rpc.BadRPCFormat' +BadRPCVersion = 'rpc.BadRPCVersion' +GarbageArgs = 'rpc.GarbageArgs' + +class Unpacker(xdr.Unpacker): + + def unpack_auth(self): + flavor = self.unpack_enum() + stuff = self.unpack_opaque() + return (flavor, stuff) + + def unpack_callheader(self): + xid = self.unpack_uint(xid) + temp = self.unpack_enum() + if temp <> CALL: + raise BadRPCFormat, 'no CALL but ' + `temp` + temp = self.unpack_uint() + if temp <> RPCVERSION: + raise BadRPCVerspion, 'bad RPC version ' + `temp` + prog = self.unpack_uint() + vers = self.unpack_uint() + proc = self.unpack_uint() + cred = self.unpack_auth() + verf = self.unpack_auth() + return xid, prog, vers, proc, cred, verf + # Caller must add procedure-specific part of call + + def unpack_replyheader(self): + xid = self.unpack_uint() + mtype = self.unpack_enum() + if mtype <> REPLY: + raise RuntimeError, 'no REPLY but ' + `mtype` + stat = self.unpack_enum() + if stat == MSG_DENIED: + stat = self.unpack_enum() + if stat == RPC_MISMATCH: + low = self.unpack_uint() + high = self.unpack_uint() + raise RuntimeError, \ + 'MSG_DENIED: RPC_MISMATCH: ' + `low, high` + if stat == AUTH_ERROR: + stat = self.unpack_uint() + raise RuntimeError, \ + 'MSG_DENIED: AUTH_ERROR: ' + `stat` + raise RuntimeError, 'MSG_DENIED: ' + `stat` + if stat <> MSG_ACCEPTED: + raise RuntimeError, \ + 'Neither MSG_DENIED nor MSG_ACCEPTED: ' + `stat` + verf = self.unpack_auth() + stat = self.unpack_enum() + if stat == PROG_UNAVAIL: + raise RuntimeError, 'call failed: PROG_UNAVAIL' + if stat == PROG_MISMATCH: + low = self.unpack_uint() + high = self.unpack_uint() + raise RuntimeError, \ + 'call failed: PROG_MISMATCH: ' + `low, high` + if stat == PROC_UNAVAIL: + raise RuntimeError, 'call failed: PROC_UNAVAIL' + if stat == GARBAGE_ARGS: + raise RuntimeError, 'call failed: GARBAGE_ARGS' + if stat <> SUCCESS: + raise RuntimeError, 'call failed: ' + `stat` + return xid, verf + # Caller must get procedure-specific part of reply + + +# Subroutines to create opaque authentication objects + +def make_auth_null(): + return '' + +def make_auth_unix(seed, host, uid, gid, groups): + p = Packer() + p.pack_auth_unix(seed, host, uid, gid, groups) + return p.get_buf() + +def make_auth_unix_default(): + try: + from os import getuid, getgid + uid = getuid() + gid = getgid() + except ImportError: + uid = gid = 0 + import time + return make_auth_unix(int(time.time()-unix_epoch()), \ + socket.gethostname(), uid, gid, []) + +_unix_epoch = -1 +def unix_epoch(): + """Very painful calculation of when the Unix Epoch is. + + This is defined as the return value of time.time() on Jan 1st, + 1970, 00:00:00 GMT. + + On a Unix system, this should always return 0.0. On a Mac, the + calculations are needed -- and hard because of integer overflow + and other limitations. + + """ + global _unix_epoch + if _unix_epoch >= 0: return _unix_epoch + import time + now = time.time() + localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...) + gmt = time.gmtime(now) + offset = time.mktime(localt) - time.mktime(gmt) + y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0 + offset, ss = divmod(ss + offset, 60) + offset, mm = divmod(mm + offset, 60) + offset, hh = divmod(hh + offset, 24) + d = d + offset + _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0)) + print "Unix epoch:", time.ctime(_unix_epoch) + return _unix_epoch + + +# Common base class for clients + +class Client: + + def __init__(self, host, prog, vers, port): + self.host = host + self.prog = prog + self.vers = vers + self.port = port + self.makesocket() # Assigns to self.sock + self.bindsocket() + self.connsocket() + self.lastxid = 0 # XXX should be more random? + self.addpackers() + self.cred = None + self.verf = None + + def close(self): + self.sock.close() + + def makesocket(self): + # This MUST be overridden + raise RuntimeError, 'makesocket not defined' + + def connsocket(self): + # Override this if you don't want/need a connection + self.sock.connect((self.host, self.port)) + + 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() + self.unpacker = Unpacker('') + + def make_call(self, proc, args, pack_func, unpack_func): + #print "make_call() args = " + str(args) + # Don't normally override this (but see Broadcast) + if pack_func is None and args is not None: + raise TypeError, 'non-null args with null pack_func' + #print "packed args" + self.start_call(proc) + if pack_func: + pack_func(args) + self.do_call() + if unpack_func: + result = unpack_func() + else: + result = None + #print "result = " + str(result) + self.unpacker.done() + return result + + 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): + # This MUST be overridden + raise RuntimeError, 'do_call not defined' + + 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): + # Override this to use a more powerful verifier + if self.verf == None: + self.verf = (AUTH_NULL, make_auth_null()) + return self.verf + + def call_0(self): # Procedure 0 is always like this + return self.make_call(0, None, None, None) + + +# Record-Marking standard support + +try: + from select import select as _select +except: + _select=None + +def sendfrag_with_timeout(sock, last, frag, timeout_seconds=None): + x = len(frag) + if last: x = x | 0x80000000L + header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \ + chr(int(x>>8 & 0xff)) + chr(int(x & 0xff))) + block=header+frag + n=len(block) + nsent=0 + while(nsent<n): + if _select and timeout_seconds: + rlist, wlist, xlist=_select([],[sock],[], timeout_seconds) + if not wlist: + raise EOFError, "Blocked write in sendfrag()" + nsent+=sock.send(block[nsent:]) + +def recvfrag_with_timeout(sock, timeout_seconds=None): + #print "receiving from ", sock + if _select and timeout_seconds: + #print "Selecting with timeout...", timeout_seconds + rlist, wlist, xlist=_select([sock],[],[], timeout_seconds) + if not rlist: + raise EOFError, "No header at all in recvfrag()" + + header = sock.recv(4) + if len(header) < 4: + raise EOFError + x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \ + ord(header[2])<<8 | ord(header[3]) + last = ((x & 0x80000000L) != 0) + n = int(x & 0x7fffffff) + + frag='' + + while(len(frag) < n): + if _select and timeout_seconds: + #print "Selecting with timeout...", timeout_seconds + rlist, wlist, xlist=_select([sock],[],[], timeout_seconds) + if not rlist: + raise EOFError, "No data after header in recvfrag()" + frag += sock.recv(n-len(frag)) + + return last, frag + + +def recvrecord(sock, timeout_seconds=None): + record = '' + last = 0 + while not last: + last, frag = recvfrag_with_timeout(sock, timeout_seconds) + record = record + frag + return record + +def sendrecord(sock, record, timeout_seconds=None): + sendfrag_with_timeout(sock, 1, record, timeout_seconds) + + + +# 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' + + +# Client using TCP to a specific port + +class RawTCPClient(Client): + + select_timeout_seconds=None + + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + def do_call(self): + call = self.packer.get_buf() + sendrecord(self.sock, call, self.select_timeout_seconds) + reply = recvrecord(self.sock, self.select_timeout_seconds) + u = self.unpacker + u.reset(reply) + xid, verf = u.unpack_replyheader() + if xid <> self.lastxid: + # Can't really happen since this is TCP... + raise RuntimeError, 'wrong xid in reply ' + `xid` + \ + ' instead of ' + `self.lastxid` + + +# Client using UDP to a specific port + +class RawUDPClient(Client): + + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + def do_call(self): + call = self.packer.get_buf() + self.sock.send(call) + try: + from select import select + except ImportError: + print 'WARNING: select not found, RPC may hang' + select = None + BUFSIZE = 8192 # Max UDP buffer size + timeout = 1 + count = 5 + while 1: + r, w, x = [self.sock], [], [] + if select: + r, w, x = select(r, w, x, timeout) + if self.sock not in r: + count = count - 1 + if count < 0: raise RuntimeError, 'timeout' + if timeout < 25: timeout = timeout *2 +## print 'RESEND', timeout, count + self.sock.send(call) + continue + reply = self.sock.recv(BUFSIZE) + u = self.unpacker + u.reset(reply) + xid, verf = u.unpack_replyheader() + if xid <> self.lastxid: +## print 'BAD xid' + continue + break + + +# Client using UDP broadcast to a specific port + +class RawBroadcastUDPClient(RawUDPClient): + + def __init__(self, bcastaddr, prog, vers, port): + RawUDPClient.__init__(self, bcastaddr, prog, vers, port) + self.reply_handler = None + self.timeout = 30 + + def connsocket(self): + # Don't connect -- use sendto + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + + def set_reply_handler(self, reply_handler): + self.reply_handler = reply_handler + + def set_timeout(self, timeout): + self.timeout = timeout # Use None for infinite timeout + + def make_call(self, proc, args, pack_func, unpack_func): + if pack_func is None and args is not None: + raise TypeError, 'non-null args with null pack_func' + self.start_call(proc) + if pack_func: + pack_func(args) + call = self.packer.get_buf() + self.sock.sendto(call, (self.host, self.port)) + try: + from select import select + except ImportError: + print 'WARNING: select not found, broadcast will hang' + select = None + BUFSIZE = 8192 # Max UDP buffer size (for reply) + replies = [] + if unpack_func is None: + def dummy(): pass + unpack_func = dummy + while 1: + r, w, x = [self.sock], [], [] + if select: + if self.timeout is None: + r, w, x = select(r, w, x) + else: + r, w, x = select(r, w, x, self.timeout) + if self.sock not in r: + break + reply, fromaddr = self.sock.recvfrom(BUFSIZE) + u = self.unpacker + u.reset(reply) + xid, verf = u.unpack_replyheader() + if xid <> self.lastxid: +## print 'BAD xid' + continue + reply = unpack_func() + self.unpacker.done() + replies.append((reply, fromaddr)) + if self.reply_handler: + self.reply_handler(reply, fromaddr) + return replies + + +# Port mapper interface + +# 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 +PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int +PMAPPROC_DUMP = 4 # (void) -> pmaplist +PMAPPROC_CALLIT = 5 # (call_args) -> call_result + +# A mapping is (prog, vers, prot, port) and prot is one of: + +IPPROTO_TCP = 6 +IPPROTO_UDP = 17 + +# A pmaplist is a variable-length list of mappings, as follows: +# either (1, mapping, pmaplist) or (0). + +# A call_args is (prog, vers, proc, args) where args is opaque; +# a call_result is (port, res) where res is opaque. + + +class PortMapperPacker(Packer): + + def pack_mapping(self, mapping): + prog, vers, prot, port = mapping + self.pack_uint(prog) + self.pack_uint(vers) + self.pack_uint(prot) + self.pack_uint(port) + + def pack_pmaplist(self, list): + self.pack_list(list, self.pack_mapping) + + def pack_call_args(self, ca): + prog, vers, proc, args = ca + self.pack_uint(prog) + self.pack_uint(vers) + self.pack_uint(proc) + self.pack_opaque(args) + + +class PortMapperUnpacker(Unpacker): + + def unpack_mapping(self): + prog = self.unpack_uint() + vers = self.unpack_uint() + prot = self.unpack_uint() + port = self.unpack_uint() + return prog, vers, prot, port + + def unpack_pmaplist(self): + return self.unpack_list(self.unpack_mapping) + + def unpack_call_result(self): + port = self.unpack_uint() + res = self.unpack_opaque() + return port, res + + +class PartialPortMapperClient: + + def addpackers(self): + self.packer = PortMapperPacker() + self.unpacker = PortMapperUnpacker('') + + def Set(self, mapping): + return self.make_call(PMAPPROC_SET, mapping, \ + self.packer.pack_mapping, \ + self.unpacker.unpack_uint) + + def Unset(self, mapping): + return self.make_call(PMAPPROC_UNSET, mapping, \ + self.packer.pack_mapping, \ + self.unpacker.unpack_uint) + + def Getport(self, mapping): + return self.make_call(PMAPPROC_GETPORT, mapping, \ + self.packer.pack_mapping, \ + self.unpacker.unpack_uint) + + def Dump(self): + return self.make_call(PMAPPROC_DUMP, None, \ + None, \ + self.unpacker.unpack_pmaplist) + + def Callit(self, ca): + return self.make_call(PMAPPROC_CALLIT, ca, \ + self.packer.pack_call_args, \ + self.unpacker.unpack_call_result) + + +class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): + + def __init__(self, host, port=PMAP_PORT, timeout_seconds=None): + RawTCPClient.__init__(self, \ + host, PMAP_PROG, PMAP_VERS, port) + self.select_timeout_seconds=timeout_seconds + + +class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient): + + def __init__(self, host, port=PMAP_PORT): + RawUDPClient.__init__(self, \ + host, PMAP_PROG, PMAP_VERS, port) + +class BroadcastUDPPortMapperClient(PartialPortMapperClient, \ + RawBroadcastUDPClient): + + def __init__(self, bcastaddr): + RawBroadcastUDPClient.__init__(self, \ + bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT) + + +# Generic clients that find their server through the Port mapper + +class TCPClient(RawTCPClient): + + def __init__(self, host, prog, vers, portmap_proxy_host=None, portmap_proxy_port=PMAP_PORT, timeout_seconds=None): + + self.select_timeout_seconds=timeout_seconds + if portmap_proxy_host is None: + portmap_proxy_host=host #use a proxy to get around firewalled portmappers + pmap = TCPPortMapperClient(portmap_proxy_host, portmap_proxy_port,timeout_seconds) + port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) + pmap.close() + if port == 0: + raise RuntimeError, 'program not registered' + RawTCPClient.__init__(self, host, prog, vers, port) + + +class UDPClient(RawUDPClient): + + def __init__(self, host, prog, vers, portmap_proxy_host=None, portmap_proxy_port=PMAP_PORT): + if portmap_proxy_host is None: + portmap_proxy_host=host #use a proxy to get around firewalled portmappers + pmap = UDPPortMapperClient(portmap_proxy_host, portmap_proxy_port) + port = pmap.Getport((prog, vers, IPPROTO_UDP, 0)) + pmap.close() + if port == 0: + raise RuntimeError, 'program not registered' + RawUDPClient.__init__(self, host, prog, vers, port) + + +class BroadcastUDPClient(Client): + + def __init__(self, bcastaddr, prog, vers): + self.pmap = BroadcastUDPPortMapperClient(bcastaddr) + self.pmap.set_reply_handler(self.my_reply_handler) + self.prog = prog + self.vers = vers + self.user_reply_handler = None + self.addpackers() + + def close(self): + self.pmap.close() + + def set_reply_handler(self, reply_handler): + self.user_reply_handler = reply_handler + + def set_timeout(self, timeout): + self.pmap.set_timeout(timeout) + + def my_reply_handler(self, reply, fromaddr): + port, res = reply + self.unpacker.reset(res) + result = self.unpack_func() + self.unpacker.done() + self.replies.append((result, fromaddr)) + if self.user_reply_handler is not None: + self.user_reply_handler(result, fromaddr) + + def make_call(self, proc, args, pack_func, unpack_func): + self.packer.reset() + if pack_func: + pack_func(args) + if unpack_func is None: + def dummy(): pass + self.unpack_func = dummy + else: + self.unpack_func = unpack_func + self.replies = [] + packed_args = self.packer.get_buf() + dummy_replies = self.pmap.Callit( \ + (self.prog, self.vers, proc, packed_args)) + return self.replies + + +# 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): + 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.makesocket() # Assigns to self.sock and self.prot + self.bindsocket() + self.host, self.port = self.sock.getsockname() + self.addpackers() + + def handle(self, call): + # Don't use unpack_header but parse the header piecewise + # XXX I have no idea if I am using the right error responses! + self.unpacker.reset(call) + self.packer.reset() + xid = self.unpacker.unpack_uint() + self.packer.pack_uint(xid) + temp = self.unpacker.unpack_enum() + if temp <> CALL: + return None # Not worthy of a reply + self.packer.pack_uint(REPLY) + temp = self.unpacker.unpack_uint() + if temp <> RPCVERSION: + self.packer.pack_uint(MSG_DENIED) + self.packer.pack_uint(RPC_MISMATCH) + self.packer.pack_uint(RPCVERSION) + self.packer.pack_uint(RPCVERSION) + return self.packer.get_buf() + self.packer.pack_uint(MSG_ACCEPTED) + self.packer.pack_auth((AUTH_NULL, make_auth_null())) + prog = self.unpacker.unpack_uint() + if prog <> self.prog: + self.packer.pack_uint(PROG_UNAVAIL) + return self.packer.get_buf() + vers = self.unpacker.unpack_uint() + if vers <> self.vers: + self.packer.pack_uint(PROG_MISMATCH) + self.packer.pack_uint(self.vers) + self.packer.pack_uint(self.vers) + return self.packer.get_buf() + proc = self.unpacker.unpack_uint() + methname = 'handle_' + `proc` + try: + meth = getattr(self, methname) + except AttributeError: + self.packer.pack_uint(PROC_UNAVAIL) + return self.packer.get_buf() + cred = self.unpacker.unpack_auth() + verf = self.unpacker.unpack_auth() + try: + meth() # Unpack args, call turn_around(), pack reply + except (EOFError, GarbageArgs): + # Too few or too many arguments + self.packer.reset() + self.packer.pack_uint(xid) + self.packer.pack_uint(REPLY) + self.packer.pack_uint(MSG_ACCEPTED) + self.packer.pack_auth((AUTH_NULL, make_auth_null())) + self.packer.pack_uint(GARBAGE_ARGS) + return self.packer.get_buf() + + def turn_around(self): + try: + self.unpacker.done() + except RuntimeError: + raise GarbageArgs + self.packer.pack_uint(SUCCESS) + + def handle_0(self): # Handle NULL message + self.turn_around() + + 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() + self.unpacker = Unpacker('') + + +class TCPServer(Server): + + def register(self): + mapping = self.prog, self.vers, self.prot, self.port + p = TCPPortMapperClient(self.host) + if not p.Set(mapping): + raise RuntimeError, 'register failed' + + def unregister(self): + mapping = self.prog, self.vers, self.prot, self.port + p = TCPPortMapperClient(self.host) + if not p.Unset(mapping): + raise RuntimeError, 'unregister failed' + + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.prot = IPPROTO_TCP + + def loop(self): + self.sock.listen(0) + while 1: + self.session(self.sock.accept()) + + def session(self, connection): + sock, (host, port) = connection + while 1: + try: + call = recvrecord(sock) + except EOFError: + break + except socket.error, msg: + print 'socket error:', msg + break + reply = self.handle(call) + if reply is not None: + sendrecord(sock, reply) + + def forkingloop(self): + # Like loop but uses forksession() + self.sock.listen(0) + while 1: + self.forksession(self.sock.accept()) + + def forksession(self, connection): + # Like session but forks off a subprocess + import os + # Wait for deceased children + try: + while 1: + pid, sts = os.waitpid(0, 1) + except os.error: + pass + pid = None + try: + pid = os.fork() + if pid: # Parent + connection[0].close() + return + # Child + self.session(connection) + finally: + # Make sure we don't fall through in the parent + if pid == 0: + os._exit(0) + + +class UDPServer(Server): + + def register(self): + mapping = self.prog, self.vers, self.prot, self.port + p = UDPPortMapperClient(self.host) + if not p.Set(mapping): + raise RuntimeError, 'register failed' + + def unregister(self): + mapping = self.prog, self.vers, self.prot, self.port + p = UDPPortMapperClient(self.host) + if not p.Unset(mapping): + raise RuntimeError, 'unregister failed' + + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.prot = IPPROTO_UDP + + def loop(self): + while 1: + self.session() + + def session(self): + call, host_port = self.sock.recvfrom(8192) + reply = self.handle(call) + if reply <> None: + self.sock.sendto(reply, host_port) + + +# Simple test program -- dump local portmapper status + +def test(): + pmap = UDPPortMapperClient('') + list = pmap.Dump() + list.sort() + for prog, vers, prot, port in list: + print prog, vers, + if prot == IPPROTO_TCP: print 'tcp', + elif prot == IPPROTO_UDP: print 'udp', + else: print prot, + print port + + +# Test program for broadcast operation -- dump everybody's portmapper status + +def testbcast(): + import sys + if sys.argv[1:]: + bcastaddr = sys.argv[1] + else: + bcastaddr = '<broadcast>' + def rh(reply, fromaddr): + host, port = fromaddr + print host + '\t' + `reply` + pmap = BroadcastUDPPortMapperClient(bcastaddr) + pmap.set_reply_handler(rh) + pmap.set_timeout(5) + replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0)) + + +# Test program for server, with corresponding client +# On machine A: python -c 'import rpc; rpc.testsvr()' +# On machine B: python -c 'import rpc; rpc.testclt()' A +# (A may be == B) + +def testsvr(): + # Simple test class -- proc 1 doubles its string argument as reply + class S(UDPServer): + def handle_1(self): + arg = self.unpacker.unpack_string() + self.turn_around() + print 'RPC function 1 called, arg', `arg` + self.packer.pack_string(arg + arg) + # + s = S('', 0x20000000, 1, 0) + try: + s.unregister() + except RuntimeError, msg: + print 'RuntimeError:', msg, '(ignored)' + s.register() + print 'Service started...' + try: + s.loop() + finally: + s.unregister() + print 'Service interrupted.' + + +def testclt(): + import sys + if sys.argv[1:]: host = sys.argv[1] + else: host = '' + # Client for above server + class C(UDPClient): + def call_1(self, arg): + return self.make_call(1, arg, \ + self.packer.pack_string, \ + self.unpacker.unpack_string) + c = C(host, 0x20000000, 1) + print 'making call...' + reply = c.call_1('hello, world, ') + print 'call returned', `reply` + +def testclt2(): + import sys + host = '127.0.0.1' + # Client for above server + class C(UDPClient): + def call_1(self, arg): + return self.make_call(1, arg, \ + self.packer.pack_string, \ + self.unpacker.unpack_string) + c = C(host, 0x20000000, 1) + print 'making call...' + reply = c.call_1('hello, world, ') + print 'call returned', `reply` +