]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Initial revision
authorGuido van Rossum <guido@python.org>
Mon, 14 Dec 1992 23:25:04 +0000 (23:25 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 14 Dec 1992 23:25:04 +0000 (23:25 +0000)
Demo/rpc/T.py [new file with mode: 0644]
Demo/rpc/mountclient.py [new file with mode: 0644]
Demo/rpc/nfsclient.py [new file with mode: 0644]
Demo/rpc/rpc.py [new file with mode: 0644]
Demo/rpc/xdr.py [new file with mode: 0644]

diff --git a/Demo/rpc/T.py b/Demo/rpc/T.py
new file mode 100644 (file)
index 0000000..24d5e8a
--- /dev/null
@@ -0,0 +1,19 @@
+import sys, os, time
+
+def TSTART():
+       global t0, t1
+       u, s, cu, cs = os.times()
+       t0 = u+cu, s+cs, time.millitimer()
+
+def TSTOP(*label):
+       global t0, t1
+       u, s, cu, cs = os.times()
+       t1 = u+cu, s+cs, time.millitimer()
+       tt = []
+       for i in range(3):
+               tt.append(t1[i] - t0[i])
+       [u, s, r] = tt
+       msg = ''
+       for x in label: msg = msg + (x + ' ')
+       msg = msg + `u` + ' user, ' + `s` + ' sys, ' + `r*0.001` + ' real\n'
+       sys.stderr.write(msg)
diff --git a/Demo/rpc/mountclient.py b/Demo/rpc/mountclient.py
new file mode 100644 (file)
index 0000000..021de8c
--- /dev/null
@@ -0,0 +1,166 @@
+# Mount RPC client -- RFC 1094 (NFS), Appendix A
+
+# This module demonstrates how to write your own RPC client in Python.
+# Since there is no RPC compiler for Python (yet), you must first
+# create classes derived from Packer and Unpacker to handle the data
+# types for the server you want to interface to.  You then write the
+# client class.  If you want to support both the TCP and the UDP
+# version of a protocol, use multiple inheritance as shown below.
+
+
+from rpc import Packer, Unpacker, TCPClient, UDPClient
+
+MOUNTPROG = 100005
+MOUNTVERS = 1
+
+FHSIZE = 32
+
+
+# Packer derived class for Mount protocol clients.
+# The only thing we need to pack beyond basic types is an 'fhandle'
+
+class MountPacker(Packer):
+
+       def pack_fhandle(self, fhandle):
+               self.pack_fopaque(FHSIZE, fhandle)
+
+
+# Unpacker derived class for Mount protocol clients.
+# The important types we need to unpack are fhandle, fhstatus,
+# mountlist and exportlist; mountstruct, exportstruct and groups are
+# used to unpack components of mountlist and exportlist and the
+# corresponding functions are passed as function argument to the
+# generic unpack_list function.
+
+class MountUnpacker(Unpacker):
+
+       def unpack_fhandle(self):
+               return self.unpack_fopaque(FHSIZE)
+
+       def unpack_fhstatus(self):
+               status = self.unpack_uint()
+               if status == 0:
+                       fh = self.unpack_fhandle()
+               else:
+                       fh = None
+               return status, fh
+
+       def unpack_mountlist(self):
+               return self.unpack_list(self.unpack_mountstruct)
+
+       def unpack_mountstruct(self):
+               hostname = self.unpack_string()
+               directory = self.unpack_string()
+               return (hostname, directory)
+
+       def unpack_exportlist(self):
+               return self.unpack_list(self.unpack_exportstruct)
+
+       def unpack_exportstruct(self):
+               filesys = self.unpack_string()
+               groups = self.unpack_groups()
+               return (filesys, groups)
+
+       def unpack_groups(self):
+               return self.unpack_list(self.unpack_string)
+
+
+# These are the procedures specific to the Mount client class.
+# Think of this as a derived class of either TCPClient or UDPClient.
+
+class PartialMountClient:
+
+       # This method is called by Client.init to initialize
+       # self.packer and self.unpacker
+       def addpackers(self):
+               self.packer = MountPacker().init()
+               self.unpacker = MountUnpacker().init('')
+
+       # The methods Mnt, Dump etc. each implement one Remote
+       # Procedure Call.  Their general structure is
+       #  self.start_call(<procedure-number>)
+       #  <pack arguments using self.packer>
+       #  self.do_call()       # This does the actual message exchange
+       #  <unpack reply using self.unpacker>
+       #  self.end_call()
+       #  return <reply>
+       # If the call fails, an exception is raised by do_call().
+       # If the reply does not match what you unpack, an exception is
+       # raised either during unpacking (if you overrun the buffer)
+       # or by end_call() (if you leave values in the buffer).
+       # Calling packer methods with invalid arguments (e.g. if
+       # invalid arguments were passed from outside) will also result
+       # in exceptions during packing.
+
+       def Mnt(self, directory):
+               self.start_call(1)
+               self.packer.pack_string(directory)
+               self.do_call()
+               stat = self.unpacker.unpack_fhstatus()
+               self.end_call()
+               return stat
+
+       def Dump(self):
+               self.start_call(2)
+               self.do_call()
+               list = self.unpacker.unpack_mountlist()
+               self.end_call()
+               return list
+
+       def Umnt(self, directory):
+               self.start_call(3)
+               self.packer.pack_string(directory)
+               self.do_call()
+               self.end_call()
+
+       def Umntall(self):
+               self.start_call(4)
+               self.do_call()
+               self.end_call()
+
+       def Export(self):
+               self.start_call(5)
+               self.do_call()
+               list = self.unpacker.unpack_exportlist()
+               self.end_call()
+               return list
+
+
+# We turn the partial Mount client into a full one for either protocol
+# by use of multiple inheritance.  (In general, when class C has base
+# classes B1...Bn, if x is an instance of class C, methods of x are
+# searched first in C, then in B1, then in B2, ..., finally in Bn.)
+
+class TCPMountClient(PartialMountClient, TCPClient):
+
+       def init(self, host):
+               return TCPClient.init(self, host, MOUNTPROG, MOUNTVERS)
+
+
+class UDPMountClient(PartialMountClient, UDPClient):
+
+       def init(self, host):
+               return UDPClient.init(self, host, MOUNTPROG, MOUNTVERS)
+
+
+# A little test program for the Mount client.  This takes a host as
+# command line argument (default the local machine), prints its export
+# list, and attempt to mount and unmount each exported files system.
+
+def test():
+       import sys
+       if sys.argv[1:]: host = sys.argv[1]
+       else: host = ''
+       mcl = UDPMountClient().init(host)
+       list = mcl.Export()
+       for item in list:
+               print item
+               try:
+                       mcl.Mnt(item[0])
+               except:
+                       print 'Sorry'
+                       continue
+               mcl.Umnt(item[0])
+       return
+
+#test()
diff --git a/Demo/rpc/nfsclient.py b/Demo/rpc/nfsclient.py
new file mode 100644 (file)
index 0000000..2260139
--- /dev/null
@@ -0,0 +1,207 @@
+# NFS RPC client -- RFC 1094
+
+# (See mountclient.py for some hints on how to write RPC clients in
+# Python in general)
+
+from rpc import UDPClient, TCPClient
+from mountclient import FHSIZE, MountPacker, MountUnpacker
+
+NFS_PROGRAM = 100003
+NFS_VERSION = 2
+
+# enum stat
+NFS_OK = 0
+# (...many error values...)
+
+# enum ftype
+NFNON = 0
+NFREG = 1
+NFDIR = 2
+NFBLK = 3
+NFCHR = 4
+NFLNK = 5
+
+
+class NFSPacker(MountPacker):
+
+       def pack_sattrargs(self, sa):
+               file, attributes = sa
+               self.pack_fhandle(file)
+               self.pack_sattr(attributes)
+
+       def pack_sattr(self, sa):
+               mode, uid, gid, size, atime, mtime = sa
+               self.pack_uint(mode)
+               self.pack_uint(uid)
+               self.pack_uint(gid)
+               self.pack_uint(size)
+               self.pack_timeval(atime)
+               self.pack_timeval(mtime)
+
+       def pack_diropargs(self, da):
+               dir, name = da
+               self.pack_fhandle(dir)
+               self.pack_string(name)
+
+       def pack_readdirargs(self, ra):
+               dir, cookie, count = ra
+               self.pack_fhandle(dir)
+               self.pack_uint(cookie)
+               self.pack_uint(count)
+
+       def pack_timeval(self, tv):
+               secs, usecs = tv
+               self.pack_uint(secs)
+               self.pack_uint(usecs)
+
+
+class NFSUnpacker(MountUnpacker):
+
+       def unpack_readdirres(self):
+               status = self.unpack_enum()
+               if status == NFS_OK:
+                       entries = self.unpack_list(self.unpack_entry)
+                       eof = self.unpack_bool()
+                       rest = (entries, eof)
+               else:
+                       rest = None
+               return (status, rest)
+
+       def unpack_entry(self):
+               fileid = self.unpack_uint()
+               name = self.unpack_string()
+               cookie = self.unpack_uint()
+               return (fileid, name, cookie)
+
+       def unpack_diropres(self):
+               status = self.unpack_enum()
+               if status == NFS_OK:
+                       fh = self.unpack_fhandle()
+                       fa = self.unpack_fattr()
+                       rest = (fh, fa)
+               else:
+                       rest = None
+               return (status, rest)
+
+       def unpack_attrstat(self):
+               status = self.unpack_enum()
+               if status == NFS_OK:
+                       attributes = self.unpack_fattr()
+               else:
+                       attributes = None
+               return status, attributes
+
+       def unpack_fattr(self):
+              type = self.unpack_enum()
+              mode = self.unpack_uint()
+              nlink = self.unpack_uint()
+              uid = self.unpack_uint()
+              gid = self.unpack_uint()
+              size = self.unpack_uint()
+              blocksize = self.unpack_uint()
+              rdev = self.unpack_uint()
+              blocks = self.unpack_uint()
+              fsid = self.unpack_uint()
+              fileid = self.unpack_uint()
+              atime = self.unpack_timeval()
+              mtime = self.unpack_timeval()
+              ctime = self.unpack_timeval()
+             return (type, mode, nlink, uid, gid, size, blocksize, \
+                     rdev, blocks, fsid, fileid, atime, mtime, ctime)
+
+       def unpack_timeval(self):
+               secs = self.unpack_uint()
+               usecs = self.unpack_uint()
+               return (secs, usecs)
+
+
+class NFSClient(UDPClient):
+
+       def init(self, host):
+               return UDPClient.init(self, host, NFS_PROGRAM, NFS_VERSION)
+
+       def addpackers(self):
+               self.packer = NFSPacker().init()
+               self.unpacker = NFSUnpacker().init('')
+
+       def Getattr(self, fh):
+               self.start_call(1)
+               self.packer.pack_fhandle(fh)
+               self.do_call()
+               as = self.unpacker.unpack_attrstat()
+               self.end_call()
+               return as
+
+       def Setattr(self, sa):
+               self.start_call(2)
+               self.packer.pack_sattrargs(sa)
+               self.do_call()
+               as = self.unpacker.unpack_attrstat()
+               self.end_call()
+               return as
+
+       # Root() is obsolete
+
+       def Lookup(self, da):
+               self.start_call(4)
+               self.packer.pack_diropargs(da)
+               self.do_call()
+               dr = self.unpacker.unpack_diropres()
+               self.end_call()
+               return dr
+
+       # ...
+
+       def Readdir(self, ra):
+               self.start_call(16)
+               self.packer.pack_readdirargs(ra)
+               self.do_call()
+               rr = self.unpacker.unpack_readdirres()
+               self.end_call()
+               return rr
+
+       # Shorthand to get the entire contents of a directory
+       def Listdir(self, dir):
+               list = []
+               ra = (dir, 0, 16)
+               while 1:
+                       (status, rest) = self.Readdir(ra)
+                       if status <> NFS_OK:
+                               break
+                       entries, eof = rest
+                       last_cookie = None
+                       for fileid, name, cookie in entries:
+                               print (fileid, name, cookie) # XXX
+                               list.append(fileid, name)
+                               last_cookie = cookie
+                       if eof or not last_cookie:
+                               break
+                       ra = (ra[0], last_cookie, ra[2])
+               return list
+
+       
+def test():
+       import sys
+       if sys.argv[1:]: host = sys.argv[1]
+       else: host = ''
+       if sys.argv[2:]: filesys = sys.argv[2]
+       else: filesys = None
+       from mountclient import UDPMountClient, TCPMountClient
+       mcl = TCPMountClient().init(host)
+       if filesys == None:
+               list = mcl.Export()
+               for item in list:
+                       print item
+               return
+       sf = mcl.Mnt(filesys)
+       print sf
+       fh = sf[1]
+       if fh:
+               ncl = NFSClient().init(host)
+               as = ncl.Getattr(fh)
+               print as
+               list = ncl.Listdir(fh)
+               for item in list: print item
+               mcl.Unmnt(filesys)
+       
+test()
diff --git a/Demo/rpc/rpc.py b/Demo/rpc/rpc.py
new file mode 100644 (file)
index 0000000..ba3bd54
--- /dev/null
@@ -0,0 +1,376 @@
+# Implement (a subset of) Sun RPC, version 2 -- RFC1057.
+
+import xdr
+import socket
+import os
+
+RPCVERSION = 2
+
+CALL = 0
+REPLY = 1
+
+AUTH_NULL = 0
+AUTH_UNIX = 1
+AUTH_SHORT = 2
+AUTH_DES = 3
+
+MSG_ACCEPTED = 0
+MSG_DENIED = 1
+
+SUCCESS = 0                            # RPC executed successfully
+PROG_UNAVAIL  = 1                      # remote hasn't exported program
+PROG_MISMATCH = 2                      # remote can't support version #
+PROC_UNAVAIL  = 3                      # program can't support procedure
+GARBAGE_ARGS  = 4                      # procedure can't decode params
+
+RPC_MISMATCH = 0                       # RPC version number != 2
+AUTH_ERROR = 1                         # remote can't authenticate caller
+
+AUTH_BADCRED      = 1                  # bad credentials (seal broken)
+AUTH_REJECTEDCRED = 2                  # client must begin new session
+AUTH_BADVERF      = 3                  # bad verifier (seal broken)
+AUTH_REJECTEDVERF = 4                  # verifier expired or replayed
+AUTH_TOOWEAK      = 5                  # rejected for security reasons
+
+
+class Packer(xdr.Packer):
+
+       def pack_auth(self, auth):
+               flavor, stuff = auth
+               self.pack_enum(flavor)
+               self.pack_opaque(stuff)
+
+       def pack_auth_unix(self, stamp, machinename, uid, gid, gids):
+               self.pack_uint(stamp)
+               self.pack_string(machinename)
+               self.pack_uint(uid)
+               self.pack_uint(gid)
+               self.pack_uint(len(gids))
+               for i in gids:
+                       self.pack_uint(i)
+
+       def pack_callheader(self, xid, prog, vers, proc, cred, verf):
+               self.pack_uint(xid)
+               self.pack_enum(CALL)
+               self.pack_uint(RPCVERSION)
+               self.pack_uint(prog)
+               self.pack_uint(vers)
+               self.pack_uint(proc)
+               self.pack_auth(cred)
+               self.pack_auth(verf)
+               # Caller must add procedure-specific part of call
+
+       def pack_replyheader(self, xid, verf):
+               self.pack_uint(xid)
+               self.pack_enum(REPLY)
+               self.pack_uint(MSG_ACCEPTED)
+               self.pack_auth(verf)
+               self.pack_enum(SUCCESS)
+               # Caller must add procedure-specific part of reply
+
+
+class Unpacker(xdr.Unpacker):
+
+       def unpack_auth(self):
+               flavor = self.unpack_enum()
+               stuff = self.unpack_opaque()
+               return (flavor, stuff)
+
+       def unpack_replyheader(self):
+               xid = self.unpack_uint()
+               mtype = self.unpack_enum()
+               if mtype <> REPLY:
+                       raise RuntimeError, 'no REPLY but ' + str(mtype)
+               stat = self.unpack_enum()
+               if stat <> MSG_ACCEPTED:
+                       if stat == MSG_DENIED:
+                               stat = self.unpack_enum()
+                               if stat == RPC_MISMATCH:
+                                       low = self.unpack_uint()
+                                       high = self.unpack_uint()
+                                       raise 'RPC_MISMATCH', (low, high)
+                               if stat == AUTH_ERROR:
+                                       stat = self.unpack_uint()
+                                       raise 'AUTH_ERROR', str(stat)
+                               raise 'MSG_REJECTED', str(stat)
+                       raise RuntimeError, 'no MSG_ACCEPTED but ' + str(stat)
+               verf = self.unpack_auth()
+               stat = self.unpack_enum()
+               if stat <> SUCCESS:
+                       raise RuntimeError, 'no SUCCESS but ' + str(stat)
+               return xid, verf
+               # Caller must get procedure-specific part of reply
+
+
+# Common base class for clients
+
+class Client:
+
+       def init(self, host, prog, vers, port, type):
+               self.host = host
+               self.prog = prog
+               self.vers = vers
+               self.port = port
+               self.type = type
+               self.sock = socket.socket(socket.AF_INET, type)
+               self.sock.connect((host, port))
+               self.lastxid = 0
+               self.addpackers()
+               self.cred = None
+               self.verf = None
+               return self
+
+       def Null(self):                 # Procedure 0 is always like this
+               self.start_call(0)
+               self.do_call(0)
+               self.end_call()
+
+       def close(self):
+               self.sock.close()
+
+       # Functions that may be overridden by specific derived classes
+
+       def addpackers(self):
+               self.packer = Packer().init()
+               self.unpacker = Unpacker().init('')
+
+       def mkcred(self, proc):
+               if self.cred == None:
+                       p = Packer().init()
+                       p.pack_auth_unix(0, socket.gethostname(), \
+                               os.getuid(), os.getgid(), [])
+                       self.cred = p.get_buf()
+               return (AUTH_UNIX, self.cred)
+
+       def mkverf(self, proc):
+               return (AUTH_NULL, '')
+
+
+# Record-Marking standard support
+
+def sendfrag(sock, last, frag):
+       x = len(frag)
+       if last: x = x | 0x80000000L
+       header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
+                 chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
+       sock.send(header + frag)
+
+def sendrecord(sock, record):
+       sendfrag(sock, 1, record)
+
+def recvfrag(sock):
+       header = sock.recv(4)
+       x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
+           ord(header[2])<<8 | ord(header[3])
+       last = ((x & 0x80000000) != 0)
+       n = int(x & 0x7fffffff)
+       frag = ''
+       while n > 0:
+               buf = sock.recv(n)
+               if not buf: raise EOFError
+               n = n - len(buf)
+               frag = frag + buf
+       return last, frag
+
+def recvrecord(sock):
+       record = ''
+       last = 0
+       while not last:
+               last, frag = recvfrag(sock)
+               record = record + frag
+       return record
+
+
+# Raw TCP-based client
+
+class RawTCPClient(Client):
+
+       def init(self, host, prog, vers, port):
+               return Client.init(self, host, prog, vers, port, \
+                       socket.SOCK_STREAM)
+
+       def start_call(self, proc):
+               self.lastxid = xid = self.lastxid + 1
+               cred = self.mkcred(proc)
+               verf = self.mkverf(proc)
+               p = self.packer
+               p.reset()
+               p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
+
+       def do_call(self, *rest):
+               # rest is used for UDP buffer size; ignored for TCP
+               call = self.packer.get_buf()
+               sendrecord(self.sock, call)
+               reply = recvrecord(self.sock)
+               u = self.unpacker
+               u.reset(reply)
+               xid, verf = u.unpack_replyheader()
+               if xid <> self.lastxid:
+                       # Can't really happen since this is TCP...
+                       raise RuntimeError, 'wrong xid in reply ' + `xid` + \
+                               ' instead of ' + `self.lastxid`
+
+       def end_call(self):
+               self.unpacker.done()
+
+
+# Raw UDP-based client
+# XXX This class does not recover from missed/duplicated packets!
+
+class RawUDPClient(Client):
+
+       def init(self, host, prog, vers, port):
+               return Client.init(self, host, prog, vers, port, \
+                       socket.SOCK_DGRAM)
+
+       def start_call(self, proc):
+               self.lastxid = xid = self.lastxid + 1
+               cred = self.mkcred(proc)
+               verf = self.mkverf(proc)
+               p = self.packer
+               p.reset()
+               p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
+
+       def do_call(self, *rest):
+               if len(rest) == 0:
+                       bufsize = 8192
+               elif len(rest) > 1:
+                       raise TypeError, 'too many args'
+               else:
+                       bufsize = rest[0] + 512
+               call = self.packer.get_buf()
+               self.sock.send(call)
+               # XXX What about time-out and retry?
+               reply = self.sock.recv(bufsize)
+               u = self.unpacker
+               u.reset(reply)
+               xid, verf = u.unpack_replyheader()
+               if xid <> self.lastxid:
+                       # XXX Should assume it's an old reply
+                       raise RuntimeError, 'wrong xid in reply ' + `xid` + \
+                               ' instead of ' + `self.lastxid`
+
+       def end_call(self):
+               self.unpacker.done()
+
+
+# Port mapper interface
+
+PMAP_PORT = 111
+PMAP_PROG = 100000
+PMAP_VERS = 2
+PMAPPROC_NULL = 0                      # (void) -> void
+PMAPPROC_SET = 1                       # (mapping) -> bool
+PMAPPROC_UNSET = 2                     # (mapping) -> bool
+PMAPPROC_GETPORT = 3                   # (mapping) -> unsigned int
+PMAPPROC_DUMP = 4                      # (void) -> pmaplist
+PMAPPROC_CALLIT = 5                    # (call_args) -> call_result
+
+# A mapping is (prog, vers, prot, port) and prot is one of:
+
+IPPROTO_TCP = 6
+IPPROTO_UDP = 17
+
+# A pmaplist is a variable-length list of mappings, as follows:
+# either (1, mapping, pmaplist) or (0).
+
+# A call_args is (prog, vers, proc, args) where args is opaque;
+# a call_result is (port, res) where res is opaque.
+
+
+class PortMapperPacker(Packer):
+
+       def pack_mapping(self, mapping):
+               prog, vers, prot, port = mapping
+               self.pack_uint(prog)
+               self.pack_uint(vers)
+               self.pack_uint(prot)
+               self.pack_uint(port)
+
+       def pack_pmaplist(self, list):
+               self.pack_list(list, self.pack_mapping)
+
+
+class PortMapperUnpacker(Unpacker):
+
+       def unpack_mapping(self):
+               prog = self.unpack_uint()
+               vers = self.unpack_uint()
+               prot = self.unpack_uint()
+               port = self.unpack_uint()
+               return prog, vers, prot, port
+
+       def unpack_pmaplist(self):
+               return self.unpack_list(self.unpack_mapping)
+
+
+class PartialPortMapperClient:
+
+       def addpackers(self):
+               self.packer = PortMapperPacker().init()
+               self.unpacker = PortMapperUnpacker().init('')
+
+       def Getport(self, mapping):
+               self.start_call(PMAPPROC_GETPORT)
+               self.packer.pack_mapping(mapping)
+               self.do_call(4)
+               port = self.unpacker.unpack_uint()
+               self.end_call()
+               return port
+
+       def Dump(self):
+               self.start_call(PMAPPROC_DUMP)
+               self.do_call(8192-512)
+               list = self.unpacker.unpack_pmaplist()
+               self.end_call()
+               return list
+
+
+class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
+
+       def init(self, host):
+               return RawTCPClient.init(self, \
+                       host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+
+
+class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
+
+       def init(self, host):
+               return RawUDPClient.init(self, \
+                       host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+
+
+class TCPClient(RawTCPClient):
+
+       def init(self, host, prog, vers):
+               pmap = TCPPortMapperClient().init(host)
+               port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
+               pmap.close()
+               return RawTCPClient.init(self, host, prog, vers, port)
+
+
+class UDPClient(RawUDPClient):
+
+       def init(self, host, prog, vers):
+               pmap = UDPPortMapperClient().init(host)
+               port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
+               pmap.close()
+               return RawUDPClient.init(self, host, prog, vers, port)
+
+
+def test():
+       import T
+       T.TSTART()
+       pmap = UDPPortMapperClient().init('')
+       T.TSTOP()
+       pmap.Null()
+       T.TSTOP()
+       list = pmap.Dump()
+       T.TSTOP()
+       list.sort()
+       for prog, vers, prot, port in list:
+               print prog, vers,
+               if prot == IPPROTO_TCP: print 'tcp',
+               elif prot == IPPROTO_UDP: print 'udp',
+               else: print prot,
+               print port
diff --git a/Demo/rpc/xdr.py b/Demo/rpc/xdr.py
new file mode 100644 (file)
index 0000000..b189b45
--- /dev/null
@@ -0,0 +1,141 @@
+# Implement (a subset of) Sun XDR -- RFC1014.
+
+
+import struct
+
+
+class Packer:
+
+       def init(self):
+               self.reset()
+               return self
+
+       def reset(self):
+               self.buf = ''
+
+       def get_buf(self):
+               return self.buf
+
+       def pack_uint(self, x):
+               self.buf = self.buf + \
+                       (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
+                        chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
+       if struct.pack('i', 1) == '\0\0\0\1':
+               def pack_uint(self, x):
+                       self.buf = self.buf + struct.pack('i', x)
+
+       pack_int = pack_uint
+
+       pack_enum = pack_int
+
+       def pack_bool(self, x):
+               if x: self.buf = self.buf + '\0\0\0\1'
+               else: self.buf = self.buf + '\0\0\0\0'
+
+       def pack_uhyper(self, x):
+               self.pack_uint(x>>32 & 0xffffffff)
+               self.pack_uint(x & 0xffffffff)
+
+       pack_hyper = pack_uhyper
+
+       def pack_fstring(self, n, s):
+               if n < 0:
+                       raise ValueError, 'fstring size must be nonnegative'
+               n = ((n+3)/4)*4
+               data = s[:n]
+               data = data + (n - len(data)) * '\0'
+               self.buf = self.buf + data
+
+       pack_fopaque = pack_fstring
+
+       def pack_string(self, s):
+               n = len(s)
+               self.pack_uint(n)
+               self.pack_fstring(n, s)
+
+       pack_opaque = pack_string
+
+       def pack_list(self, list, pack_item):
+               for item in list:
+                       self.pack_uint(1)
+                       pack_item(list)
+               self.pack_uint(0)
+
+
+class Unpacker:
+
+       def init(self, data):
+               self.reset(data)
+               return self
+
+       def reset(self, data):
+               self.buf = data
+               self.pos = 0
+
+       def done(self):
+               if self.pos < len(self.buf):
+                       raise RuntimeError, 'unextracted data remains'
+
+       def unpack_uint(self):
+               i = self.pos
+               self.pos = j = i+4
+               data = self.buf[i:j]
+               x = long(ord(data[0]))<<24 | ord(data[1])<<16 | \
+                       ord(data[2])<<8 | ord(data[3])
+               # Return a Python long only if the value is not representable
+               # as a nonnegative Python int
+               if x < 0x80000000L: x = int(x)
+               return x
+       if struct.unpack('i', '\0\0\0\1') == 1:
+               def unpack_uint(self):
+                       i = self.pos
+                       self.pos = j = i+4
+                       return struct.unpack('i', self.buf[i:j])
+
+       def unpack_int(self):
+               x = self.unpack_uint()
+               if x >= 0x80000000L: x = x - 0x100000000L
+               return int(x)
+
+       unpack_enum = unpack_int
+
+       unpack_bool = unpack_int
+
+       def unpack_uhyper(self):
+               hi = self.unpack_uint()
+               lo = self.unpack_uint()
+               return long(hi)<<32 | lo
+
+       def unpack_hyper(self):
+               x = self.unpack_uhyper()
+               if x >= 0x8000000000000000L: x = x - 0x10000000000000000L
+               return x
+
+       def unpack_fstring(self, n):
+               if n < 0:
+                       raise ValueError, 'fstring size must be nonnegative'
+               i = self.pos
+               j = i + (n+3)/4*4
+               if j > len(self.buf):
+                       raise RuntimeError, 'buffer overrun'
+               self.pos = j
+               return self.buf[i:i+n]
+
+       unpack_fopaque = unpack_fstring
+
+       def unpack_string(self):
+               n = self.unpack_uint()
+               return self.unpack_fstring(n)
+
+       unpack_opaque = unpack_string
+
+       def unpack_list(self, unpack_item):
+               list = []
+               while 1:
+                       x = self.unpack_uint()
+                       if not x: break
+                       if x <> 1:
+                               raise RuntimeError, \
+                                       '0 or 1 expected, got ' + `x`
+                       list.append(unpack_item())
+               return list