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