home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyth_os2.zip / python-1.0.2 / Demo / rpc / rpc.py < prev    next >
Text File  |  1993-12-17  |  22KB  |  864 lines

  1. # Sun RPC version 2 -- RFC1057.
  2.  
  3. # XXX There should be separate exceptions for the various reasons why
  4. # XXX an RPC can fail, rather than using RuntimeError for everything
  5.  
  6. # XXX The UDP version of the protocol resends requests when it does
  7. # XXX not receive a timely reply -- use only for idempotent calls!
  8.  
  9. # XXX There is no provision for call timeout on TCP connections
  10.  
  11. import xdr
  12. import socket
  13. import os
  14.  
  15. RPCVERSION = 2
  16.  
  17. CALL = 0
  18. REPLY = 1
  19.  
  20. AUTH_NULL = 0
  21. AUTH_UNIX = 1
  22. AUTH_SHORT = 2
  23. AUTH_DES = 3
  24.  
  25. MSG_ACCEPTED = 0
  26. MSG_DENIED = 1
  27.  
  28. SUCCESS = 0                # RPC executed successfully
  29. PROG_UNAVAIL  = 1            # remote hasn't exported program
  30. PROG_MISMATCH = 2            # remote can't support version #
  31. PROC_UNAVAIL  = 3            # program can't support procedure
  32. GARBAGE_ARGS  = 4            # procedure can't decode params
  33.  
  34. RPC_MISMATCH = 0            # RPC version number != 2
  35. AUTH_ERROR = 1                # remote can't authenticate caller
  36.  
  37. AUTH_BADCRED      = 1            # bad credentials (seal broken)
  38. AUTH_REJECTEDCRED = 2            # client must begin new session
  39. AUTH_BADVERF      = 3            # bad verifier (seal broken)
  40. AUTH_REJECTEDVERF = 4            # verifier expired or replayed
  41. AUTH_TOOWEAK      = 5            # rejected for security reasons
  42.  
  43.  
  44. class Packer(xdr.Packer):
  45.  
  46.     def pack_auth(self, auth):
  47.         flavor, stuff = auth
  48.         self.pack_enum(flavor)
  49.         self.pack_opaque(stuff)
  50.  
  51.     def pack_auth_unix(self, stamp, machinename, uid, gid, gids):
  52.         self.pack_uint(stamp)
  53.         self.pack_string(machinename)
  54.         self.pack_uint(uid)
  55.         self.pack_uint(gid)
  56.         self.pack_uint(len(gids))
  57.         for i in gids:
  58.             self.pack_uint(i)
  59.  
  60.     def pack_callheader(self, xid, prog, vers, proc, cred, verf):
  61.         self.pack_uint(xid)
  62.         self.pack_enum(CALL)
  63.         self.pack_uint(RPCVERSION)
  64.         self.pack_uint(prog)
  65.         self.pack_uint(vers)
  66.         self.pack_uint(proc)
  67.         self.pack_auth(cred)
  68.         self.pack_auth(verf)
  69.         # Caller must add procedure-specific part of call
  70.  
  71.     def pack_replyheader(self, xid, verf):
  72.         self.pack_uint(xid)
  73.         self.pack_enum(REPLY)
  74.         self.pack_uint(MSG_ACCEPTED)
  75.         self.pack_auth(verf)
  76.         self.pack_enum(SUCCESS)
  77.         # Caller must add procedure-specific part of reply
  78.  
  79.  
  80. # Exceptions
  81. BadRPCFormat = 'rpc.BadRPCFormat'
  82. BadRPCVersion = 'rpc.BadRPCVersion'
  83. GarbageArgs = 'rpc.GarbageArgs'
  84.  
  85. class Unpacker(xdr.Unpacker):
  86.  
  87.     def unpack_auth(self):
  88.         flavor = self.unpack_enum()
  89.         stuff = self.unpack_opaque()
  90.         return (flavor, stuff)
  91.  
  92.     def unpack_callheader(self):
  93.         xid = self.unpack_uint(xid)
  94.         temp = self.unpack_enum()
  95.         if temp <> CALL:
  96.             raise BadRPCFormat, 'no CALL but ' + `temp`
  97.         temp = self.unpack_uint()
  98.         if temp <> RPCVERSION:
  99.             raise BadRPCVerspion, 'bad RPC version ' + `temp`
  100.         prog = self.unpack_uint()
  101.         vers = self.unpack_uint()
  102.         proc = self.unpack_uint()
  103.         cred = self.unpack_auth()
  104.         verf = self.unpack_auth()
  105.         return xid, prog, vers, proc, cred, verf
  106.         # Caller must add procedure-specific part of call
  107.  
  108.     def unpack_replyheader(self):
  109.         xid = self.unpack_uint()
  110.         mtype = self.unpack_enum()
  111.         if mtype <> REPLY:
  112.             raise RuntimeError, 'no REPLY but ' + `mtype`
  113.         stat = self.unpack_enum()
  114.         if stat == MSG_DENIED:
  115.             stat = self.unpack_enum()
  116.             if stat == RPC_MISMATCH:
  117.                 low = self.unpack_uint()
  118.                 high = self.unpack_uint()
  119.                 raise RuntimeError, \
  120.                   'MSG_DENIED: RPC_MISMATCH: ' + `low, high`
  121.             if stat == AUTH_ERROR:
  122.                 stat = self.unpack_uint()
  123.                 raise RuntimeError, \
  124.                     'MSG_DENIED: AUTH_ERROR: ' + `stat`
  125.             raise RuntimeError, 'MSG_DENIED: ' + `stat`
  126.         if stat <> MSG_ACCEPTED:
  127.             raise RuntimeError, \
  128.               'Neither MSG_DENIED nor MSG_ACCEPTED: ' + `stat`
  129.         verf = self.unpack_auth()
  130.         stat = self.unpack_enum()
  131.         if stat == PROG_UNAVAIL:
  132.             raise RuntimeError, 'call failed: PROG_UNAVAIL'
  133.         if stat == PROG_MISMATCH:
  134.             low = self.unpack_uint()
  135.             high = self.unpack_uint()
  136.             raise RuntimeError, \
  137.                 'call failed: PROG_MISMATCH: ' + `low, high`
  138.         if stat == PROC_UNAVAIL:
  139.             raise RuntimeError, 'call failed: PROC_UNAVAIL'
  140.         if stat == GARBAGE_ARGS:
  141.             raise RuntimeError, 'call failed: GARBAGE_ARGS'
  142.         if stat <> SUCCESS:
  143.             raise RuntimeError, 'call failed: ' + `stat`
  144.         return xid, verf
  145.         # Caller must get procedure-specific part of reply
  146.  
  147.  
  148. # Subroutines to create opaque authentication objects
  149.  
  150. def make_auth_null():
  151.     return ''
  152.  
  153. def make_auth_unix(seed, host, uid, gid, groups):
  154.     p = Packer()
  155.     p.pack_auth_unix(seed, host, uid, gid, groups)
  156.     return p.get_buf()
  157.  
  158. def make_auth_unix_default():
  159.     try:
  160.         from os import getuid, getgid
  161.         uid = getuid()
  162.         gid = getgid()
  163.     except ImportError:
  164.         uid = gid = 0
  165.     import time
  166.     return make_auth_unix(int(time.time()), \
  167.           socket.gethostname(), uid, gid, [])
  168.  
  169.  
  170. # Common base class for clients
  171.  
  172. class Client:
  173.  
  174.     def __init__(self, host, prog, vers, port):
  175.         self.host = host
  176.         self.prog = prog
  177.         self.vers = vers
  178.         self.port = port
  179.         self.makesocket() # Assigns to self.sock
  180.         self.bindsocket()
  181.         self.connsocket()
  182.         self.lastxid = 0 # XXX should be more random?
  183.         self.addpackers()
  184.         self.cred = None
  185.         self.verf = None
  186.  
  187.     def close(self):
  188.         self.sock.close()
  189.  
  190.     def makesocket(self):
  191.         # This MUST be overridden
  192.         raise RuntimeError, 'makesocket not defined'
  193.  
  194.     def connsocket(self):
  195.         # Override this if you don't want/need a connection
  196.         self.sock.connect((self.host, self.port))
  197.  
  198.     def bindsocket(self):
  199.         # Override this to bind to a different port (e.g. reserved)
  200.         self.sock.bind(('', 0))
  201.  
  202.     def addpackers(self):
  203.         # Override this to use derived classes from Packer/Unpacker
  204.         self.packer = Packer()
  205.         self.unpacker = Unpacker('')
  206.  
  207.     def make_call(self, proc, args, pack_func, unpack_func):
  208.         # Don't normally override this (but see Broadcast)
  209.         if pack_func is None and args is not None:
  210.             raise TypeError, 'non-null args with null pack_func'
  211.         self.start_call(proc)
  212.         if pack_func:
  213.             pack_func(args)
  214.         self.do_call()
  215.         if unpack_func:
  216.             result = unpack_func()
  217.         else:
  218.             result = None
  219.         self.unpacker.done()
  220.         return result
  221.  
  222.     def start_call(self, proc):
  223.         # Don't override this
  224.         self.lastxid = xid = self.lastxid + 1
  225.         cred = self.mkcred()
  226.         verf = self.mkverf()
  227.         p = self.packer
  228.         p.reset()
  229.         p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
  230.  
  231.     def do_call(self):
  232.         # This MUST be overridden
  233.         raise RuntimeError, 'do_call not defined'
  234.  
  235.     def mkcred(self):
  236.         # Override this to use more powerful credentials
  237.         if self.cred == None:
  238.             self.cred = (AUTH_NULL, make_auth_null())
  239.         return self.cred
  240.  
  241.     def mkverf(self):
  242.         # Override this to use a more powerful verifier
  243.         if self.verf == None:
  244.             self.verf = (AUTH_NULL, make_auth_null())
  245.         return self.verf
  246.  
  247.     def call_0(self):        # Procedure 0 is always like this
  248.         return self.make_call(0, None, None, None)
  249.  
  250.  
  251. # Record-Marking standard support
  252.  
  253. def sendfrag(sock, last, frag):
  254.     x = len(frag)
  255.     if last: x = x | 0x80000000L
  256.     header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
  257.           chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
  258.     sock.send(header + frag)
  259.  
  260. def sendrecord(sock, record):
  261.     sendfrag(sock, 1, record)
  262.  
  263. def recvfrag(sock):
  264.     header = sock.recv(4)
  265.     if len(header) < 4:
  266.         raise EOFError
  267.     x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
  268.         ord(header[2])<<8 | ord(header[3])
  269.     last = ((x & 0x80000000) != 0)
  270.     n = int(x & 0x7fffffff)
  271.     frag = ''
  272.     while n > 0:
  273.         buf = sock.recv(n)
  274.         if not buf: raise EOFError
  275.         n = n - len(buf)
  276.         frag = frag + buf
  277.     return last, frag
  278.  
  279. def recvrecord(sock):
  280.     record = ''
  281.     last = 0
  282.     while not last:
  283.         last, frag = recvfrag(sock)
  284.         record = record + frag
  285.     return record
  286.  
  287.  
  288. # Try to bind to a reserved port (must be root)
  289.  
  290. last_resv_port_tried = None
  291. def bindresvport(sock, host):
  292.     global last_resv_port_tried
  293.     FIRST, LAST = 600, 1024 # Range of ports to try
  294.     if last_resv_port_tried == None:
  295.         import os
  296.         last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST)
  297.     for i in range(last_resv_port_tried, LAST) + \
  298.           range(FIRST, last_resv_port_tried):
  299.         last_resv_port_tried = i
  300.         try:
  301.             sock.bind((host, i))
  302.             return last_resv_port_tried
  303.         except socket.error, (errno, msg):
  304.             if errno <> 114:
  305.                 raise socket.error, (errno, msg)
  306.     raise RuntimeError, 'can\'t assign reserved port'
  307.  
  308.  
  309. # Client using TCP to a specific port
  310.  
  311. class RawTCPClient(Client):
  312.  
  313.     def makesocket(self):
  314.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  315.  
  316.     def do_call(self):
  317.         call = self.packer.get_buf()
  318.         sendrecord(self.sock, call)
  319.         reply = recvrecord(self.sock)
  320.         u = self.unpacker
  321.         u.reset(reply)
  322.         xid, verf = u.unpack_replyheader()
  323.         if xid <> self.lastxid:
  324.             # Can't really happen since this is TCP...
  325.             raise RuntimeError, 'wrong xid in reply ' + `xid` + \
  326.                 ' instead of ' + `self.lastxid`
  327.  
  328.  
  329. # Client using UDP to a specific port
  330.  
  331. class RawUDPClient(Client):
  332.  
  333.     def makesocket(self):
  334.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  335.  
  336.     def do_call(self):
  337.         call = self.packer.get_buf()
  338.         self.sock.send(call)
  339.         try:
  340.             from select import select
  341.         except ImportError:
  342.             print 'WARNING: select not found, RPC may hang'
  343.             select = None
  344.         BUFSIZE = 8192 # Max UDP buffer size
  345.         timeout = 1
  346.         count = 5
  347.         while 1:
  348.             r, w, x = [self.sock], [], []
  349.             if select:
  350.                 r, w, x = select(r, w, x, timeout)
  351.             if self.sock not in r:
  352.                 count = count - 1
  353.                 if count < 0: raise RuntimeError, 'timeout'
  354.                 if timeout < 25: timeout = timeout *2
  355. ##                print 'RESEND', timeout, count
  356.                 self.sock.send(call)
  357.                 continue
  358.             reply = self.sock.recv(BUFSIZE)
  359.             u = self.unpacker
  360.             u.reset(reply)
  361.             xid, verf = u.unpack_replyheader()
  362.             if xid <> self.lastxid:
  363. ##                print 'BAD xid'
  364.                 continue
  365.             break
  366.  
  367.  
  368. # Client using UDP broadcast to a specific port
  369.  
  370. class RawBroadcastUDPClient(RawUDPClient):
  371.  
  372.     def __init__(self, bcastaddr, prog, vers, port):
  373.         RawUDPClient.__init__(self, bcastaddr, prog, vers, port)
  374.         self.reply_handler = None
  375.         self.timeout = 30
  376.  
  377.     def connsocket(self):
  378.         # Don't connect -- use sendto
  379.         self.sock.allowbroadcast(1)
  380.  
  381.     def set_reply_handler(self, reply_handler):
  382.         self.reply_handler = reply_handler
  383.  
  384.     def set_timeout(self, timeout):
  385.         self.timeout = timeout # Use None for infinite timeout
  386.  
  387.     def make_call(self, proc, args, pack_func, unpack_func):
  388.         if pack_func is None and args is not None:
  389.             raise TypeError, 'non-null args with null pack_func'
  390.         self.start_call(proc)
  391.         if pack_func:
  392.             pack_func(args)
  393.         call = self.packer.get_buf()
  394.         self.sock.sendto(call, (self.host, self.port))
  395.         try:
  396.             from select import select
  397.         except ImportError:
  398.             print 'WARNING: select not found, broadcast will hang'
  399.             select = None
  400.         BUFSIZE = 8192 # Max UDP buffer size (for reply)
  401.         replies = []
  402.         if unpack_func is None:
  403.             def dummy(): pass
  404.             unpack_func = dummy
  405.         while 1:
  406.             r, w, x = [self.sock], [], []
  407.             if select:
  408.                 if self.timeout is None:
  409.                     r, w, x = select(r, w, x)
  410.                 else:
  411.                     r, w, x = select(r, w, x, self.timeout)
  412.             if self.sock not in r:
  413.                 break
  414.             reply, fromaddr = self.sock.recvfrom(BUFSIZE)
  415.             u = self.unpacker
  416.             u.reset(reply)
  417.             xid, verf = u.unpack_replyheader()
  418.             if xid <> self.lastxid:
  419. ##                print 'BAD xid'
  420.                 continue
  421.             reply = unpack_func()
  422.             self.unpacker.done()
  423.             replies.append((reply, fromaddr))
  424.             if self.reply_handler:
  425.                 self.reply_handler(reply, fromaddr)
  426.         return replies
  427.  
  428.  
  429. # Port mapper interface
  430.  
  431. # Program number, version and (fixed!) port number
  432. PMAP_PROG = 100000
  433. PMAP_VERS = 2
  434. PMAP_PORT = 111
  435.  
  436. # Procedure numbers
  437. PMAPPROC_NULL = 0            # (void) -> void
  438. PMAPPROC_SET = 1            # (mapping) -> bool
  439. PMAPPROC_UNSET = 2            # (mapping) -> bool
  440. PMAPPROC_GETPORT = 3            # (mapping) -> unsigned int
  441. PMAPPROC_DUMP = 4            # (void) -> pmaplist
  442. PMAPPROC_CALLIT = 5            # (call_args) -> call_result
  443.  
  444. # A mapping is (prog, vers, prot, port) and prot is one of:
  445.  
  446. IPPROTO_TCP = 6
  447. IPPROTO_UDP = 17
  448.  
  449. # A pmaplist is a variable-length list of mappings, as follows:
  450. # either (1, mapping, pmaplist) or (0).
  451.  
  452. # A call_args is (prog, vers, proc, args) where args is opaque;
  453. # a call_result is (port, res) where res is opaque.
  454.  
  455.  
  456. class PortMapperPacker(Packer):
  457.  
  458.     def pack_mapping(self, mapping):
  459.         prog, vers, prot, port = mapping
  460.         self.pack_uint(prog)
  461.         self.pack_uint(vers)
  462.         self.pack_uint(prot)
  463.         self.pack_uint(port)
  464.  
  465.     def pack_pmaplist(self, list):
  466.         self.pack_list(list, self.pack_mapping)
  467.  
  468.     def pack_call_args(self, ca):
  469.         prog, vers, proc, args = ca
  470.         self.pack_uint(prog)
  471.         self.pack_uint(vers)
  472.         self.pack_uint(proc)
  473.         self.pack_opaque(args)
  474.  
  475.  
  476. class PortMapperUnpacker(Unpacker):
  477.  
  478.     def unpack_mapping(self):
  479.         prog = self.unpack_uint()
  480.         vers = self.unpack_uint()
  481.         prot = self.unpack_uint()
  482.         port = self.unpack_uint()
  483.         return prog, vers, prot, port
  484.  
  485.     def unpack_pmaplist(self):
  486.         return self.unpack_list(self.unpack_mapping)
  487.  
  488.     def unpack_call_result(self):
  489.         port = self.unpack_uint()
  490.         res = self.unpack_opaque()
  491.         return port, res
  492.  
  493.  
  494. class PartialPortMapperClient:
  495.  
  496.     def addpackers(self):
  497.         self.packer = PortMapperPacker()
  498.         self.unpacker = PortMapperUnpacker('')
  499.  
  500.     def Set(self, mapping):
  501.         return self.make_call(PMAPPROC_SET, mapping, \
  502.             self.packer.pack_mapping, \
  503.             self.unpacker.unpack_uint)
  504.  
  505.     def Unset(self, mapping):
  506.         return self.make_call(PMAPPROC_UNSET, mapping, \
  507.             self.packer.pack_mapping, \
  508.             self.unpacker.unpack_uint)
  509.  
  510.     def Getport(self, mapping):
  511.         return self.make_call(PMAPPROC_GETPORT, mapping, \
  512.             self.packer.pack_mapping, \
  513.             self.unpacker.unpack_uint)
  514.  
  515.     def Dump(self):
  516.         return self.make_call(PMAPPROC_DUMP, None, \
  517.             None, \
  518.             self.unpacker.unpack_pmaplist)
  519.  
  520.     def Callit(self, ca):
  521.         return self.make_call(PMAPPROC_CALLIT, ca, \
  522.             self.packer.pack_call_args, \
  523.             self.unpacker.unpack_call_result)
  524.  
  525.  
  526. class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
  527.  
  528.     def __init__(self, host):
  529.         RawTCPClient.__init__(self, \
  530.             host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
  531.  
  532.  
  533. class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
  534.  
  535.     def __init__(self, host):
  536.         RawUDPClient.__init__(self, \
  537.             host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
  538.  
  539.  
  540. class BroadcastUDPPortMapperClient(PartialPortMapperClient, \
  541.                    RawBroadcastUDPClient):
  542.  
  543.     def __init__(self, bcastaddr):
  544.         RawBroadcastUDPClient.__init__(self, \
  545.             bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
  546.  
  547.  
  548. # Generic clients that find their server through the Port mapper
  549.  
  550. class TCPClient(RawTCPClient):
  551.  
  552.     def __init__(self, host, prog, vers):
  553.         pmap = TCPPortMapperClient(host)
  554.         port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
  555.         pmap.close()
  556.         if port == 0:
  557.             raise RuntimeError, 'program not registered'
  558.         RawTCPClient.__init__(self, host, prog, vers, port)
  559.  
  560.  
  561. class UDPClient(RawUDPClient):
  562.  
  563.     def __init__(self, host, prog, vers):
  564.         pmap = UDPPortMapperClient(host)
  565.         port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
  566.         pmap.close()
  567.         if port == 0:
  568.             raise RuntimeError, 'program not registered'
  569.         RawUDPClient.__init__(self, host, prog, vers, port)
  570.  
  571.  
  572. class BroadcastUDPClient(Client):
  573.  
  574.     def __init__(self, bcastaddr, prog, vers):
  575.         self.pmap = BroadcastUDPPortMapperClient(bcastaddr)
  576.         self.pmap.set_reply_handler(self.my_reply_handler)
  577.         self.prog = prog
  578.         self.vers = vers
  579.         self.user_reply_handler = None
  580.         self.addpackers()
  581.  
  582.     def close(self):
  583.         self.pmap.close()
  584.  
  585.     def set_reply_handler(self, reply_handler):
  586.         self.user_reply_handler = reply_handler
  587.  
  588.     def set_timeout(self, timeout):
  589.         self.pmap.set_timeout(timeout)
  590.  
  591.     def my_reply_handler(self, reply, fromaddr):
  592.         port, res = reply
  593.         self.unpacker.reset(res)
  594.         result = self.unpack_func()
  595.         self.unpacker.done()
  596.         self.replies.append((result, fromaddr))
  597.         if self.user_reply_handler is not None:
  598.             self.user_reply_handler(result, fromaddr)
  599.  
  600.     def make_call(self, proc, args, pack_func, unpack_func):
  601.         self.packer.reset()
  602.         if pack_func:
  603.             pack_func(args)
  604.         if unpack_func is None:
  605.             def dummy(): pass
  606.             self.unpack_func = dummy
  607.         else:
  608.             self.unpack_func = unpack_func
  609.         self.replies = []
  610.         packed_args = self.packer.get_buf()
  611.         dummy_replies = self.pmap.Callit( \
  612.             (self.prog, self.vers, proc, packed_args))
  613.         return self.replies
  614.  
  615.  
  616. # Server classes
  617.  
  618. # These are not symmetric to the Client classes
  619. # XXX No attempt is made to provide authorization hooks yet
  620.  
  621. class Server:
  622.  
  623.     def __init__(self, host, prog, vers, port):
  624.         self.host = host # Should normally be '' for default interface
  625.         self.prog = prog
  626.         self.vers = vers
  627.         self.port = port # Should normally be 0 for random port
  628.         self.makesocket() # Assigns to self.sock and self.prot
  629.         self.bindsocket()
  630.         self.host, self.port = self.sock.getsockname()
  631.         self.addpackers()
  632.  
  633.     def register(self):
  634.         mapping = self.prog, self.vers, self.prot, self.port
  635.         p = TCPPortMapperClient(self.host)
  636.         if not p.Set(mapping):
  637.             raise RuntimeError, 'register failed'
  638.  
  639.     def unregister(self):
  640.         mapping = self.prog, self.vers, self.prot, self.port
  641.         p = TCPPortMapperClient(self.host)
  642.         if not p.Unset(mapping):
  643.             raise RuntimeError, 'unregister failed'
  644.  
  645.     def handle(self, call):
  646.         # Don't use unpack_header but parse the header piecewise
  647.         # XXX I have no idea if I am using the right error responses!
  648.         self.unpacker.reset(call)
  649.         self.packer.reset()
  650.         xid = self.unpacker.unpack_uint()
  651.         self.packer.pack_uint(xid)
  652.         temp = self.unpacker.unpack_enum()
  653.         if temp <> CALL:
  654.             return None # Not worthy of a reply
  655.         self.packer.pack_uint(REPLY)
  656.         temp = self.unpacker.unpack_uint()
  657.         if temp <> RPCVERSION:
  658.             self.packer.pack_uint(MSG_DENIED)
  659.             self.packer.pack_uint(RPC_MISMATCH)
  660.             self.packer.pack_uint(RPCVERSION)
  661.             self.packer.pack_uint(RPCVERSION)
  662.             return self.packer.get_buf()
  663.         self.packer.pack_uint(MSG_ACCEPTED)
  664.         self.packer.pack_auth((AUTH_NULL, make_auth_null()))
  665.         prog = self.unpacker.unpack_uint()
  666.         if prog <> self.prog:
  667.             self.packer.pack_uint(PROG_UNAVAIL)
  668.             return self.packer.get_buf()
  669.         vers = self.unpacker.unpack_uint()
  670.         if vers <> self.vers:
  671.             self.packer.pack_uint(PROG_MISMATCH)
  672.             self.packer.pack_uint(self.vers)
  673.             self.packer.pack_uint(self.vers)
  674.             return self.packer.get_buf()
  675.         proc = self.unpacker.unpack_uint()
  676.         methname = 'handle_' + `proc`
  677.         try:
  678.             meth = getattr(self, methname)
  679.         except AttributeError:
  680.             self.packer.pack_uint(PROC_UNAVAIL)
  681.             return self.packer.get_buf()
  682.         cred = self.unpacker.unpack_auth()
  683.         verf = self.unpacker.unpack_auth()
  684.         try:
  685.             meth() # Unpack args, call turn_around(), pack reply
  686.         except (EOFError, GarbageArgs):
  687.             # Too few or too many arguments
  688.             self.packer.reset()
  689.             self.packer.pack_uint(xid)
  690.             self.packer.pack_uint(REPLY)
  691.             self.packer.pack_uint(MSG_ACCEPTED)
  692.             self.packer.pack_auth((AUTH_NULL, make_auth_null()))
  693.             self.packer.pack_uint(GARBAGE_ARGS)
  694.         return self.packer.get_buf()
  695.  
  696.     def turn_around(self):
  697.         try:
  698.             self.unpacker.done()
  699.         except RuntimeError:
  700.             raise GarbageArgs
  701.         self.packer.pack_uint(SUCCESS)
  702.  
  703.     def handle_0(self): # Handle NULL message
  704.         self.turn_around()
  705.  
  706.     def makesocket(self):
  707.         # This MUST be overridden
  708.         raise RuntimeError, 'makesocket not defined'
  709.  
  710.     def bindsocket(self):
  711.         # Override this to bind to a different port (e.g. reserved)
  712.         self.sock.bind((self.host, self.port))
  713.  
  714.     def addpackers(self):
  715.         # Override this to use derived classes from Packer/Unpacker
  716.         self.packer = Packer()
  717.         self.unpacker = Unpacker('')
  718.  
  719.  
  720. class TCPServer(Server):
  721.  
  722.     def makesocket(self):
  723.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  724.         self.prot = IPPROTO_TCP
  725.  
  726.     def loop(self):
  727.         self.sock.listen(0)
  728.         while 1:
  729.             self.session(self.sock.accept())
  730.  
  731.     def session(self, connection):
  732.         sock, (host, port) = connection
  733.         while 1:
  734.             try:
  735.                 call = recvrecord(sock)
  736.             except EOFError:
  737.                 break
  738.             except socket.error, msg:
  739.                 print 'socket error:', msg
  740.                 break
  741.             reply = self.handle(call)
  742.             if reply is not None:
  743.                 sendrecord(sock, reply)
  744.  
  745.     def forkingloop(self):
  746.         # Like loop but uses forksession()
  747.         self.sock.listen(0)
  748.         while 1:
  749.             self.forksession(self.sock.accept())
  750.  
  751.     def forksession(self, connection):
  752.         # Like session but forks off a subprocess
  753.         import os
  754.         # Wait for deceased children
  755.         try:
  756.             while 1:
  757.                 pid, sts = os.waitpid(0, 1)
  758.         except os.error:
  759.             pass
  760.         pid = None
  761.         try:
  762.             pid = os.fork()
  763.             if pid: # Parent
  764.                 connection[0].close()
  765.                 return
  766.             # Child
  767.             self.session(connection)
  768.         finally:
  769.             # Make sure we don't fall through in the parent
  770.             if pid == 0:
  771.                 os._exit(0)
  772.  
  773.  
  774. class UDPServer(Server):
  775.  
  776.     def makesocket(self):
  777.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  778.         self.prot = IPPROTO_UDP
  779.  
  780.     def loop(self):
  781.         while 1:
  782.             self.session()
  783.  
  784.     def session(self):
  785.         call, host_port = self.sock.recvfrom(8192)
  786.         reply = self.handle(call)
  787.         if reply <> None:
  788.             self.sock.sendto(reply, host_port)
  789.  
  790.  
  791. # Simple test program -- dump local portmapper status
  792.  
  793. def test():
  794.     pmap = UDPPortMapperClient('')
  795.     list = pmap.Dump()
  796.     list.sort()
  797.     for prog, vers, prot, port in list:
  798.         print prog, vers,
  799.         if prot == IPPROTO_TCP: print 'tcp',
  800.         elif prot == IPPROTO_UDP: print 'udp',
  801.         else: print prot,
  802.         print port
  803.  
  804.  
  805. # Test program for broadcast operation -- dump everybody's portmapper status
  806.  
  807. def testbcast():
  808.     import sys
  809.     if sys.argv[1:]:
  810.         bcastaddr = sys.argv[1]
  811.     else:
  812.         bcastaddr = '<broadcast>'
  813.     def rh(reply, fromaddr):
  814.         host, port = fromaddr
  815.         print host + '\t' + `reply`
  816.     pmap = BroadcastUDPPortMapperClient(bcastaddr)
  817.     pmap.set_reply_handler(rh)
  818.     pmap.set_timeout(5)
  819.     replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0))
  820.  
  821.  
  822. # Test program for server, with corresponding client
  823. # On machine A: python -c 'import rpc; rpc.testsvr()'
  824. # On machine B: python -c 'import rpc; rpc.testclt()' A
  825. # (A may be == B)
  826.  
  827. def testsvr():
  828.     # Simple test class -- proc 1 doubles its string argument as reply
  829.     class S(UDPServer):
  830.         def handle_1(self):
  831.             arg = self.unpacker.unpack_string()
  832.             self.turn_around()
  833.             print 'RPC function 1 called, arg', `arg`
  834.             self.packer.pack_string(arg + arg)
  835.     #
  836.     s = S('', 0x20000000, 1, 0)
  837.     try:
  838.         s.unregister()
  839.     except RuntimeError, msg:
  840.         print 'RuntimeError:', msg, '(ignored)'
  841.     s.register()
  842.     print 'Service started...'
  843.     try:
  844.         s.loop()
  845.     finally:
  846.         s.unregister()
  847.         print 'Service interrupted.'
  848.  
  849.  
  850. def testclt():
  851.     import sys
  852.     if sys.argv[1:]: host = sys.argv[1]
  853.     else: host = ''
  854.     # Client for above server
  855.     class C(UDPClient):
  856.         def call_1(self, arg):
  857.             return self.make_call(1, arg, \
  858.                 self.packer.pack_string, \
  859.                 self.unpacker.unpack_string)
  860.     c = C(host, 0x20000000, 1)
  861.     print 'making call...'
  862.     reply = c.call_1('hello, world, ')
  863.     print 'call returned', `reply`
  864.