symbian-qemu-0.9.1-12/python-2.6.1/Demo/rpc/rpc.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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)