diff options
Diffstat (limited to 'Demo/rpc')
-rw-r--r-- | Demo/rpc/MANIFEST | 10 | ||||
-rw-r--r-- | Demo/rpc/README | 31 | ||||
-rw-r--r-- | Demo/rpc/T.py | 22 | ||||
-rw-r--r-- | Demo/rpc/mountclient.py | 202 | ||||
-rw-r--r-- | Demo/rpc/nfsclient.py | 201 | ||||
-rw-r--r-- | Demo/rpc/rnusersclient.py | 98 | ||||
-rw-r--r-- | Demo/rpc/rpc.py | 890 | ||||
-rwxr-xr-x | Demo/rpc/test | 24 | ||||
-rw-r--r-- | Demo/rpc/xdr.py | 200 |
9 files changed, 0 insertions, 1678 deletions
diff --git a/Demo/rpc/MANIFEST b/Demo/rpc/MANIFEST deleted file mode 100644 index e65f3eb..0000000 --- a/Demo/rpc/MANIFEST +++ /dev/null @@ -1,10 +0,0 @@ - File Name Archive # Description ------------------------------------------------------------ - MANIFEST 1 This shipping list - README 1 - T.py 1 - mountclient.py 1 - nfsclient.py 1 - rpc.py 1 - test 1 - xdr.py 1 diff --git a/Demo/rpc/README b/Demo/rpc/README deleted file mode 100644 index 97948a3..0000000 --- a/Demo/rpc/README +++ /dev/null @@ -1,31 +0,0 @@ -This is a Python interface to Sun RPC, designed and implemented mostly -by reading the Internet RFCs about the subject. - -*** NOTE: xdr.py has evolved into the standard module xdrlib.py *** - -There are two library modules, xdr.py and rpc.py, and several example -clients: mountclient.py, nfsclient.py, and rnusersclient.py, -implementing the NFS Mount protocol, (part of) the NFS protocol, and -the "rnusers" protocol (used by rusers(1)), respectively. The latter -demonstrates the use of broadcast via the Port mapper's CALLIT -procedure. - -There is also a way to create servers in Python. - -To test the nfs client, run it from the shell with something like this: - - python -c 'import nfsclient; nfsclient.test()' [hostname [filesystemname]] - -When called without a filesystemname, it lists the filesystems at the -host; default host is the local machine. - -Other clients are tested similarly. - -For hostname, use e.g. wuarchive.wustl.edu or gatekeeper.dec.com (two -hosts that are known to export NFS filesystems with little restrictions). - -There are now two different RPC compilers: - -1) Wim Lewis rpcgen.py found on http://www.omnigroup.com/~wiml/soft/stale-index.html#python. - -2) Peter Åstrands rpcgen.py, which is part of "pynfs" (http://www.cendio.se/~peter/pynfs/). diff --git a/Demo/rpc/T.py b/Demo/rpc/T.py deleted file mode 100644 index 3325507..0000000 --- a/Demo/rpc/T.py +++ /dev/null @@ -1,22 +0,0 @@ -# Simple interface to report execution times of program fragments. -# Call TSTART() to reset the timer, TSTOP(...) to report times. - -import sys, os, time - -def TSTART(): - global t0, t1 - u, s, cu, cs = os.times() - t0 = u+cu, s+cs, time.time() - -def TSTOP(*label): - global t0, t1 - u, s, cu, cs = os.times() - t1 = u+cu, s+cs, time.time() - 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 + '%r user, %r sys, %r real\n' % (u, s, r) - sys.stderr.write(msg) diff --git a/Demo/rpc/mountclient.py b/Demo/rpc/mountclient.py deleted file mode 100644 index 81797df..0000000 --- a/Demo/rpc/mountclient.py +++ /dev/null @@ -1,202 +0,0 @@ -# Mount RPC client -- RFC 1094 (NFS), Appendix A - -# This module demonstrates how to write your own RPC client in Python. -# When this example was written, there was no RPC compiler for -# Python. Without such a compiler, 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. - - -import rpc -from rpc import Packer, Unpacker, TCPClient, UDPClient - - -# Program number and version for the mount protocol -MOUNTPROG = 100005 -MOUNTVERS = 1 - -# Size of the 'fhandle' opaque structure -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() - self.unpacker = MountUnpacker('') - - # This method is called by Client.__init__ to bind the socket - # to a particular network interface and port. We use the - # default network interface, but if we're running as root, - # we want to bind to a reserved port - def bindsocket(self): - import os - try: - uid = os.getuid() - except AttributeError: - uid = 1 - if uid == 0: - port = rpc.bindresvport(self.sock, '') - # 'port' is not used - else: - self.sock.bind(('', 0)) - - # This function is called to cough up a suitable - # authentication object for a call to procedure 'proc'. - def mkcred(self): - if self.cred is None: - self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default() - return self.cred - - # The methods Mnt, Dump etc. each implement one Remote - # Procedure Call. This is done by calling self.make_call() - # with as arguments: - # - # - the procedure number - # - the arguments (or None) - # - the "packer" function for the arguments (or None) - # - the "unpacker" function for the return value (or None) - # - # The packer and unpacker function, if not None, *must* be - # methods of self.packer and self.unpacker, respectively. - # A value of None means that there are no arguments or is no - # return value, respectively. - # - # The return value from make_call() is the return value from - # the remote procedure call, as unpacked by the "unpacker" - # function, or None if the unpacker function is None. - # - # (Even if you expect a result of None, you should still - # return the return value from make_call(), since this may be - # needed by a broadcasting version of the class.) - # - # If the call fails, make_call() raises an exception - # (this includes time-outs and invalid results). - # - # Note that (at least with the UDP protocol) there is no - # guarantee that a call is executed at most once. When you do - # get a reply, you know it has been executed at least once; - # when you don't get a reply, you know nothing. - - def Mnt(self, directory): - return self.make_call(1, directory, \ - self.packer.pack_string, \ - self.unpacker.unpack_fhstatus) - - def Dump(self): - return self.make_call(2, None, \ - None, self.unpacker.unpack_mountlist) - - def Umnt(self, directory): - return self.make_call(3, directory, \ - self.packer.pack_string, None) - - def Umntall(self): - return self.make_call(4, None, None, None) - - def Export(self): - return self.make_call(5, None, \ - None, self.unpacker.unpack_exportlist) - - -# 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): - TCPClient.__init__(self, host, MOUNTPROG, MOUNTVERS) - - -class UDPMountClient(PartialMountClient, UDPClient): - - def __init__(self, host): - 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 attempts to mount and unmount each exported files system. -# An optional first argument of -t or -u specifies the protocol to use -# (TCP or UDP), default is UDP. - -def test(): - import sys - if sys.argv[1:] and sys.argv[1] == '-t': - C = TCPMountClient - del sys.argv[1] - elif sys.argv[1:] and sys.argv[1] == '-u': - C = UDPMountClient - del sys.argv[1] - else: - C = UDPMountClient - if sys.argv[1:]: host = sys.argv[1] - else: host = '' - mcl = C(host) - list = mcl.Export() - for item in list: - print(item) - try: - mcl.Mnt(item[0]) - except: - print('Sorry') - continue - mcl.Umnt(item[0]) diff --git a/Demo/rpc/nfsclient.py b/Demo/rpc/nfsclient.py deleted file mode 100644 index a291ce0..0000000 --- a/Demo/rpc/nfsclient.py +++ /dev/null @@ -1,201 +0,0 @@ -# NFS RPC client -- RFC 1094 - -# XXX This is not yet complete. -# XXX Only GETATTR, SETTTR, LOOKUP and READDIR are supported. - -# (See mountclient.py for some hints on how to write RPC clients in -# Python in general) - -import rpc -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): - UDPClient.__init__(self, host, NFS_PROGRAM, NFS_VERSION) - - def addpackers(self): - self.packer = NFSPacker() - self.unpacker = NFSUnpacker('') - - def mkcred(self): - if self.cred is None: - self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default() - return self.cred - - def Getattr(self, fh): - return self.make_call(1, fh, \ - self.packer.pack_fhandle, \ - self.unpacker.unpack_attrstat) - - def Setattr(self, sa): - return self.make_call(2, sa, \ - self.packer.pack_sattrargs, \ - self.unpacker.unpack_attrstat) - - # Root() is obsolete - - def Lookup(self, da): - return self.make_call(4, da, \ - self.packer.pack_diropargs, \ - self.unpacker.unpack_diropres) - - # ... - - def Readdir(self, ra): - return self.make_call(16, ra, \ - self.packer.pack_readdirargs, \ - self.unpacker.unpack_readdirres) - - # Shorthand to get the entire contents of a directory - def Listdir(self, dir): - list = [] - ra = (dir, 0, 2000) - while 1: - (status, rest) = self.Readdir(ra) - if status != NFS_OK: - break - entries, eof = rest - last_cookie = None - for fileid, name, cookie in entries: - list.append((fileid, name)) - last_cookie = cookie - if eof or last_cookie is None: - 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(host) - if filesys is None: - list = mcl.Export() - for item in list: - print(item) - return - sf = mcl.Mnt(filesys) - print(sf) - fh = sf[1] - if fh: - ncl = NFSClient(host) - attrstat = ncl.Getattr(fh) - print(attrstat) - list = ncl.Listdir(fh) - for item in list: print(item) - mcl.Umnt(filesys) diff --git a/Demo/rpc/rnusersclient.py b/Demo/rpc/rnusersclient.py deleted file mode 100644 index eaabefe..0000000 --- a/Demo/rpc/rnusersclient.py +++ /dev/null @@ -1,98 +0,0 @@ -# Remote nusers client interface - -import rpc -from rpc import Packer, Unpacker, UDPClient, BroadcastUDPClient - - -class RnusersPacker(Packer): - def pack_utmp(self, ui): - ut_line, ut_name, ut_host, ut_time = utmp - self.pack_string(ut_line) - self.pack_string(ut_name) - self.pack_string(ut_host) - self.pack_int(ut_time) - def pack_utmpidle(self, ui): - ui_itmp, ui_idle = ui - self.pack_utmp(ui_utmp) - self.pack_uint(ui_idle) - def pack_utmpidlearr(self, list): - self.pack_array(list, self.pack_itmpidle) - - -class RnusersUnpacker(Unpacker): - def unpack_utmp(self): - ut_line = self.unpack_string() - ut_name = self.unpack_string() - ut_host = self.unpack_string() - ut_time = self.unpack_int() - return ut_line, ut_name, ut_host, ut_time - def unpack_utmpidle(self): - ui_utmp = self.unpack_utmp() - ui_idle = self.unpack_uint() - return ui_utmp, ui_idle - def unpack_utmpidlearr(self): - return self.unpack_array(self.unpack_utmpidle) - - -class PartialRnusersClient: - - def addpackers(self): - self.packer = RnusersPacker() - self.unpacker = RnusersUnpacker('') - - def Num(self): - return self.make_call(1, None, None, self.unpacker.unpack_int) - - def Names(self): - return self.make_call(2, None, \ - None, self.unpacker.unpack_utmpidlearr) - - def Allnames(self): - return self.make_call(3, None, \ - None, self.unpacker.unpack_utmpidlearr) - - -class RnusersClient(PartialRnusersClient, UDPClient): - - def __init__(self, host): - UDPClient.__init__(self, host, 100002, 2) - - -class BroadcastRnusersClient(PartialRnusersClient, BroadcastUDPClient): - - def __init__(self, bcastaddr): - BroadcastUDPClient.__init__(self, bcastaddr, 100002, 2) - - -def test(): - import sys - if not sys.argv[1:]: - testbcast() - return - else: - host = sys.argv[1] - c = RnusersClient(host) - list = c.Names() - for (line, name, host, time), idle in list: - line = strip0(line) - name = strip0(name) - host = strip0(host) - print("%r %r %r %s %s" % (name, host, line, time, idle)) - -def testbcast(): - c = BroadcastRnusersClient('<broadcast>') - def listit(list, fromaddr): - host, port = fromaddr - print(host + '\t:', end=' ') - for (line, name, host, time), idle in list: - print(strip0(name), end=' ') - print() - c.set_reply_handler(listit) - all = c.Names() - print('Total Count:', len(all)) - -def strip0(s): - while s and s[-1] == '\0': s = s[:-1] - return s - -test() diff --git a/Demo/rpc/rpc.py b/Demo/rpc/rpc.py deleted file mode 100644 index 30b3017..0000000 --- a/Demo/rpc/rpc.py +++ /dev/null @@ -1,890 +0,0 @@ -# Sun RPC version 2 -- RFC1057. - -# XXX There should be separate exceptions for the various reasons why -# XXX an RPC can fail, rather than using RuntimeError for everything - -# XXX Need to use class based exceptions rather than string exceptions - -# XXX The UDP version of the protocol resends requests when it does -# XXX not receive a timely reply -- use only for idempotent calls! - -# XXX There is no provision for call timeout on TCP connections - -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 - - -# Exceptions -class BadRPCFormat(Exception): pass -class BadRPCVersion(Exception): pass -class GarbageArgs(Exception): pass - -class Unpacker(xdr.Unpacker): - - def unpack_auth(self): - flavor = self.unpack_enum() - stuff = self.unpack_opaque() - return (flavor, stuff) - - def unpack_callheader(self): - xid = self.unpack_uint() - temp = self.unpack_enum() - if temp != CALL: - raise BadRPCFormat('no CALL but %r' % (temp,)) - temp = self.unpack_uint() - if temp != RPCVERSION: - raise BadRPCVersion('bad RPC version %r' % (temp,)) - prog = self.unpack_uint() - vers = self.unpack_uint() - proc = self.unpack_uint() - cred = self.unpack_auth() - verf = self.unpack_auth() - return xid, prog, vers, proc, cred, verf - # Caller must add procedure-specific part of call - - def unpack_replyheader(self): - xid = self.unpack_uint() - mtype = self.unpack_enum() - if mtype != REPLY: - raise RuntimeError('no REPLY but %r' % (mtype,)) - stat = self.unpack_enum() - if stat == MSG_DENIED: - stat = self.unpack_enum() - if stat == RPC_MISMATCH: - low = self.unpack_uint() - high = self.unpack_uint() - raise RuntimeError('MSG_DENIED: RPC_MISMATCH: %r' % ((low, high),)) - if stat == AUTH_ERROR: - stat = self.unpack_uint() - raise RuntimeError('MSG_DENIED: AUTH_ERROR: %r' % (stat,)) - raise RuntimeError('MSG_DENIED: %r' % (stat,)) - if stat != MSG_ACCEPTED: - raise RuntimeError('Neither MSG_DENIED nor MSG_ACCEPTED: %r' % (stat,)) - verf = self.unpack_auth() - stat = self.unpack_enum() - if stat == PROG_UNAVAIL: - raise RuntimeError('call failed: PROG_UNAVAIL') - if stat == PROG_MISMATCH: - low = self.unpack_uint() - high = self.unpack_uint() - raise RuntimeError('call failed: PROG_MISMATCH: %r' % ((low, high),)) - if stat == PROC_UNAVAIL: - raise RuntimeError('call failed: PROC_UNAVAIL') - if stat == GARBAGE_ARGS: - raise RuntimeError('call failed: GARBAGE_ARGS') - if stat != SUCCESS: - raise RuntimeError('call failed: %r' % (stat,)) - return xid, verf - # Caller must get procedure-specific part of reply - - -# Subroutines to create opaque authentication objects - -def make_auth_null(): - return '' - -def make_auth_unix(seed, host, uid, gid, groups): - p = Packer() - p.pack_auth_unix(seed, host, uid, gid, groups) - return p.get_buf() - -def make_auth_unix_default(): - try: - from os import getuid, getgid - uid = getuid() - gid = getgid() - except ImportError: - uid = gid = 0 - import time - return make_auth_unix(int(time.time()-unix_epoch()), \ - socket.gethostname(), uid, gid, []) - -_unix_epoch = -1 -def unix_epoch(): - """Very painful calculation of when the Unix Epoch is. - - This is defined as the return value of time.time() on Jan 1st, - 1970, 00:00:00 GMT. - - On a Unix system, this should always return 0.0. On a Mac, the - calculations are needed -- and hard because of integer overflow - and other limitations. - - """ - global _unix_epoch - if _unix_epoch >= 0: return _unix_epoch - import time - now = time.time() - localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...) - gmt = time.gmtime(now) - offset = time.mktime(localt) - time.mktime(gmt) - y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0 - offset, ss = divmod(ss + offset, 60) - offset, mm = divmod(mm + offset, 60) - offset, hh = divmod(hh + offset, 24) - d = d + offset - _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0)) - print("Unix epoch:", time.ctime(_unix_epoch)) - return _unix_epoch - - -# Common base class for clients - -class Client: - - def __init__(self, host, prog, vers, port): - self.host = host - self.prog = prog - self.vers = vers - self.port = port - self.makesocket() # Assigns to self.sock - self.bindsocket() - self.connsocket() - self.lastxid = 0 # XXX should be more random? - self.addpackers() - self.cred = None - self.verf = None - - def close(self): - self.sock.close() - - def makesocket(self): - # This MUST be overridden - raise RuntimeError('makesocket not defined') - - def connsocket(self): - # Override this if you don't want/need a connection - self.sock.connect((self.host, self.port)) - - def bindsocket(self): - # Override this to bind to a different port (e.g. reserved) - self.sock.bind(('', 0)) - - def addpackers(self): - # Override this to use derived classes from Packer/Unpacker - self.packer = Packer() - self.unpacker = Unpacker('') - - def make_call(self, proc, args, pack_func, unpack_func): - # Don't normally override this (but see Broadcast) - if pack_func is None and args is not None: - raise TypeError('non-null args with null pack_func') - self.start_call(proc) - if pack_func: - pack_func(args) - self.do_call() - if unpack_func: - result = unpack_func() - else: - result = None - self.unpacker.done() - return result - - def start_call(self, proc): - # Don't override this - self.lastxid = xid = self.lastxid + 1 - cred = self.mkcred() - verf = self.mkverf() - p = self.packer - p.reset() - p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) - - def do_call(self): - # This MUST be overridden - raise RuntimeError('do_call not defined') - - def mkcred(self): - # Override this to use more powerful credentials - if self.cred is None: - self.cred = (AUTH_NULL, make_auth_null()) - return self.cred - - def mkverf(self): - # Override this to use a more powerful verifier - if self.verf is None: - self.verf = (AUTH_NULL, make_auth_null()) - return self.verf - - def call_0(self): # Procedure 0 is always like this - return self.make_call(0, None, None, None) - - -# Record-Marking standard support - -def sendfrag(sock, last, frag): - x = len(frag) - if last: x = x | 0x80000000 - 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) - if len(header) < 4: - raise EOFError - x = int(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 - - -# Try to bind to a reserved port (must be root) - -last_resv_port_tried = None -def bindresvport(sock, host): - global last_resv_port_tried - FIRST, LAST = 600, 1024 # Range of ports to try - if last_resv_port_tried is None: - import os - last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST) - for i in range(last_resv_port_tried, LAST) + \ - range(FIRST, last_resv_port_tried): - last_resv_port_tried = i - try: - sock.bind((host, i)) - return last_resv_port_tried - except socket.error as e: - (errno, msg) = e - if errno != 114: - raise socket.error(errno, msg) - raise RuntimeError('can\'t assign reserved port') - - -# Client using TCP to a specific port - -class RawTCPClient(Client): - - def makesocket(self): - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - def do_call(self): - call = self.packer.get_buf() - sendrecord(self.sock, call) - 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 %r instead of %r' % ( - xid, self.lastxid)) - - -# Client using UDP to a specific port - -class RawUDPClient(Client): - - def makesocket(self): - self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - - def do_call(self): - call = self.packer.get_buf() - self.sock.send(call) - try: - from select import select - except ImportError: - print('WARNING: select not found, RPC may hang') - select = None - BUFSIZE = 8192 # Max UDP buffer size - timeout = 1 - count = 5 - while 1: - r, w, x = [self.sock], [], [] - if select: - r, w, x = select(r, w, x, timeout) - if self.sock not in r: - count = count - 1 - if count < 0: raise RuntimeError('timeout') - if timeout < 25: timeout = timeout *2 -## print 'RESEND', timeout, count - self.sock.send(call) - continue - reply = self.sock.recv(BUFSIZE) - u = self.unpacker - u.reset(reply) - xid, verf = u.unpack_replyheader() - if xid != self.lastxid: -## print 'BAD xid' - continue - break - - -# Client using UDP broadcast to a specific port - -class RawBroadcastUDPClient(RawUDPClient): - - def __init__(self, bcastaddr, prog, vers, port): - RawUDPClient.__init__(self, bcastaddr, prog, vers, port) - self.reply_handler = None - self.timeout = 30 - - def connsocket(self): - # Don't connect -- use sendto - self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - - def set_reply_handler(self, reply_handler): - self.reply_handler = reply_handler - - def set_timeout(self, timeout): - self.timeout = timeout # Use None for infinite timeout - - def make_call(self, proc, args, pack_func, unpack_func): - if pack_func is None and args is not None: - raise TypeError('non-null args with null pack_func') - self.start_call(proc) - if pack_func: - pack_func(args) - call = self.packer.get_buf() - self.sock.sendto(call, (self.host, self.port)) - try: - from select import select - except ImportError: - print('WARNING: select not found, broadcast will hang') - select = None - BUFSIZE = 8192 # Max UDP buffer size (for reply) - replies = [] - if unpack_func is None: - def dummy(): pass - unpack_func = dummy - while 1: - r, w, x = [self.sock], [], [] - if select: - if self.timeout is None: - r, w, x = select(r, w, x) - else: - r, w, x = select(r, w, x, self.timeout) - if self.sock not in r: - break - reply, fromaddr = self.sock.recvfrom(BUFSIZE) - u = self.unpacker - u.reset(reply) - xid, verf = u.unpack_replyheader() - if xid != self.lastxid: -## print 'BAD xid' - continue - reply = unpack_func() - self.unpacker.done() - replies.append((reply, fromaddr)) - if self.reply_handler: - self.reply_handler(reply, fromaddr) - return replies - - -# Port mapper interface - -# Program number, version and (fixed!) port number -PMAP_PROG = 100000 -PMAP_VERS = 2 -PMAP_PORT = 111 - -# Procedure numbers -PMAPPROC_NULL = 0 # (void) -> void -PMAPPROC_SET = 1 # (mapping) -> bool -PMAPPROC_UNSET = 2 # (mapping) -> bool -PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int -PMAPPROC_DUMP = 4 # (void) -> pmaplist -PMAPPROC_CALLIT = 5 # (call_args) -> call_result - -# A mapping is (prog, vers, prot, port) and prot is one of: - -IPPROTO_TCP = 6 -IPPROTO_UDP = 17 - -# A pmaplist is a variable-length list of mappings, as follows: -# either (1, mapping, pmaplist) or (0). - -# A call_args is (prog, vers, proc, args) where args is opaque; -# a call_result is (port, res) where res is opaque. - - -class PortMapperPacker(Packer): - - def pack_mapping(self, mapping): - prog, vers, prot, port = mapping - self.pack_uint(prog) - self.pack_uint(vers) - self.pack_uint(prot) - self.pack_uint(port) - - def pack_pmaplist(self, list): - self.pack_list(list, self.pack_mapping) - - def pack_call_args(self, ca): - prog, vers, proc, args = ca - self.pack_uint(prog) - self.pack_uint(vers) - self.pack_uint(proc) - self.pack_opaque(args) - - -class PortMapperUnpacker(Unpacker): - - def unpack_mapping(self): - prog = self.unpack_uint() - vers = self.unpack_uint() - prot = self.unpack_uint() - port = self.unpack_uint() - return prog, vers, prot, port - - def unpack_pmaplist(self): - return self.unpack_list(self.unpack_mapping) - - def unpack_call_result(self): - port = self.unpack_uint() - res = self.unpack_opaque() - return port, res - - -class PartialPortMapperClient: - - def addpackers(self): - self.packer = PortMapperPacker() - self.unpacker = PortMapperUnpacker('') - - def Set(self, mapping): - return self.make_call(PMAPPROC_SET, mapping, \ - self.packer.pack_mapping, \ - self.unpacker.unpack_uint) - - def Unset(self, mapping): - return self.make_call(PMAPPROC_UNSET, mapping, \ - self.packer.pack_mapping, \ - self.unpacker.unpack_uint) - - def Getport(self, mapping): - return self.make_call(PMAPPROC_GETPORT, mapping, \ - self.packer.pack_mapping, \ - self.unpacker.unpack_uint) - - def Dump(self): - return self.make_call(PMAPPROC_DUMP, None, \ - None, \ - self.unpacker.unpack_pmaplist) - - def Callit(self, ca): - return self.make_call(PMAPPROC_CALLIT, ca, \ - self.packer.pack_call_args, \ - self.unpacker.unpack_call_result) - - -class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): - - def __init__(self, host): - RawTCPClient.__init__(self, \ - host, PMAP_PROG, PMAP_VERS, PMAP_PORT) - - -class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient): - - def __init__(self, host): - RawUDPClient.__init__(self, \ - host, PMAP_PROG, PMAP_VERS, PMAP_PORT) - - -class BroadcastUDPPortMapperClient(PartialPortMapperClient, \ - RawBroadcastUDPClient): - - def __init__(self, bcastaddr): - RawBroadcastUDPClient.__init__(self, \ - bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT) - - -# Generic clients that find their server through the Port mapper - -class TCPClient(RawTCPClient): - - def __init__(self, host, prog, vers): - pmap = TCPPortMapperClient(host) - port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) - pmap.close() - if port == 0: - raise RuntimeError('program not registered') - RawTCPClient.__init__(self, host, prog, vers, port) - - -class UDPClient(RawUDPClient): - - def __init__(self, host, prog, vers): - pmap = UDPPortMapperClient(host) - port = pmap.Getport((prog, vers, IPPROTO_UDP, 0)) - pmap.close() - if port == 0: - raise RuntimeError('program not registered') - RawUDPClient.__init__(self, host, prog, vers, port) - - -class BroadcastUDPClient(Client): - - def __init__(self, bcastaddr, prog, vers): - self.pmap = BroadcastUDPPortMapperClient(bcastaddr) - self.pmap.set_reply_handler(self.my_reply_handler) - self.prog = prog - self.vers = vers - self.user_reply_handler = None - self.addpackers() - - def close(self): - self.pmap.close() - - def set_reply_handler(self, reply_handler): - self.user_reply_handler = reply_handler - - def set_timeout(self, timeout): - self.pmap.set_timeout(timeout) - - def my_reply_handler(self, reply, fromaddr): - port, res = reply - self.unpacker.reset(res) - result = self.unpack_func() - self.unpacker.done() - self.replies.append((result, fromaddr)) - if self.user_reply_handler is not None: - self.user_reply_handler(result, fromaddr) - - def make_call(self, proc, args, pack_func, unpack_func): - self.packer.reset() - if pack_func: - pack_func(args) - if unpack_func is None: - def dummy(): pass - self.unpack_func = dummy - else: - self.unpack_func = unpack_func - self.replies = [] - packed_args = self.packer.get_buf() - dummy_replies = self.pmap.Callit( \ - (self.prog, self.vers, proc, packed_args)) - return self.replies - - -# Server classes - -# These are not symmetric to the Client classes -# XXX No attempt is made to provide authorization hooks yet - -class Server: - - def __init__(self, host, prog, vers, port): - self.host = host # Should normally be '' for default interface - self.prog = prog - self.vers = vers - self.port = port # Should normally be 0 for random port - self.makesocket() # Assigns to self.sock and self.prot - self.bindsocket() - self.host, self.port = self.sock.getsockname() - self.addpackers() - - def register(self): - mapping = self.prog, self.vers, self.prot, self.port - p = TCPPortMapperClient(self.host) - if not p.Set(mapping): - raise RuntimeError('register failed') - - def unregister(self): - mapping = self.prog, self.vers, self.prot, self.port - p = TCPPortMapperClient(self.host) - if not p.Unset(mapping): - raise RuntimeError('unregister failed') - - def handle(self, call): - # Don't use unpack_header but parse the header piecewise - # XXX I have no idea if I am using the right error responses! - self.unpacker.reset(call) - self.packer.reset() - xid = self.unpacker.unpack_uint() - self.packer.pack_uint(xid) - temp = self.unpacker.unpack_enum() - if temp != CALL: - return None # Not worthy of a reply - self.packer.pack_uint(REPLY) - temp = self.unpacker.unpack_uint() - if temp != RPCVERSION: - self.packer.pack_uint(MSG_DENIED) - self.packer.pack_uint(RPC_MISMATCH) - self.packer.pack_uint(RPCVERSION) - self.packer.pack_uint(RPCVERSION) - return self.packer.get_buf() - self.packer.pack_uint(MSG_ACCEPTED) - self.packer.pack_auth((AUTH_NULL, make_auth_null())) - prog = self.unpacker.unpack_uint() - if prog != self.prog: - self.packer.pack_uint(PROG_UNAVAIL) - return self.packer.get_buf() - vers = self.unpacker.unpack_uint() - if vers != self.vers: - self.packer.pack_uint(PROG_MISMATCH) - self.packer.pack_uint(self.vers) - self.packer.pack_uint(self.vers) - return self.packer.get_buf() - proc = self.unpacker.unpack_uint() - methname = 'handle_' + repr(proc) - try: - meth = getattr(self, methname) - except AttributeError: - self.packer.pack_uint(PROC_UNAVAIL) - return self.packer.get_buf() - cred = self.unpacker.unpack_auth() - verf = self.unpacker.unpack_auth() - try: - meth() # Unpack args, call turn_around(), pack reply - except (EOFError, GarbageArgs): - # Too few or too many arguments - self.packer.reset() - self.packer.pack_uint(xid) - self.packer.pack_uint(REPLY) - self.packer.pack_uint(MSG_ACCEPTED) - self.packer.pack_auth((AUTH_NULL, make_auth_null())) - self.packer.pack_uint(GARBAGE_ARGS) - return self.packer.get_buf() - - def turn_around(self): - try: - self.unpacker.done() - except RuntimeError: - raise GarbageArgs - self.packer.pack_uint(SUCCESS) - - def handle_0(self): # Handle NULL message - self.turn_around() - - def makesocket(self): - # This MUST be overridden - raise RuntimeError('makesocket not defined') - - def bindsocket(self): - # Override this to bind to a different port (e.g. reserved) - self.sock.bind((self.host, self.port)) - - def addpackers(self): - # Override this to use derived classes from Packer/Unpacker - self.packer = Packer() - self.unpacker = Unpacker('') - - -class TCPServer(Server): - - def makesocket(self): - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.prot = IPPROTO_TCP - - def loop(self): - self.sock.listen(0) - while 1: - self.session(self.sock.accept()) - - def session(self, connection): - sock, (host, port) = connection - while 1: - try: - call = recvrecord(sock) - except EOFError: - break - except socket.error as msg: - print('socket error:', msg) - break - reply = self.handle(call) - if reply is not None: - sendrecord(sock, reply) - - def forkingloop(self): - # Like loop but uses forksession() - self.sock.listen(0) - while 1: - self.forksession(self.sock.accept()) - - def forksession(self, connection): - # Like session but forks off a subprocess - import os - # Wait for deceased children - try: - while 1: - pid, sts = os.waitpid(0, 1) - except os.error: - pass - pid = None - try: - pid = os.fork() - if pid: # Parent - connection[0].close() - return - # Child - self.session(connection) - finally: - # Make sure we don't fall through in the parent - if pid == 0: - os._exit(0) - - -class UDPServer(Server): - - def makesocket(self): - self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.prot = IPPROTO_UDP - - def loop(self): - while 1: - self.session() - - def session(self): - call, host_port = self.sock.recvfrom(8192) - reply = self.handle(call) - if reply is not None: - self.sock.sendto(reply, host_port) - - -# Simple test program -- dump local portmapper status - -def test(): - pmap = UDPPortMapperClient('') - list = pmap.Dump() - list.sort() - for prog, vers, prot, port in list: - print(prog, vers, end=' ') - if prot == IPPROTO_TCP: print('tcp', end=' ') - elif prot == IPPROTO_UDP: print('udp', end=' ') - else: print(prot, end=' ') - print(port) - - -# Test program for broadcast operation -- dump everybody's portmapper status - -def testbcast(): - import sys - if sys.argv[1:]: - bcastaddr = sys.argv[1] - else: - bcastaddr = '<broadcast>' - def rh(reply, fromaddr): - host, port = fromaddr - print(host + '\t' + repr(reply)) - pmap = BroadcastUDPPortMapperClient(bcastaddr) - pmap.set_reply_handler(rh) - pmap.set_timeout(5) - replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0)) - - -# Test program for server, with corresponding client -# On machine A: python -c 'import rpc; rpc.testsvr()' -# On machine B: python -c 'import rpc; rpc.testclt()' A -# (A may be == B) - -def testsvr(): - # Simple test class -- proc 1 doubles its string argument as reply - class S(UDPServer): - def handle_1(self): - arg = self.unpacker.unpack_string() - self.turn_around() - print('RPC function 1 called, arg', repr(arg)) - self.packer.pack_string(arg + arg) - # - s = S('', 0x20000000, 1, 0) - try: - s.unregister() - except RuntimeError as msg: - print('RuntimeError:', msg, '(ignored)') - s.register() - print('Service started...') - try: - s.loop() - finally: - s.unregister() - print('Service interrupted.') - - -def testclt(): - import sys - if sys.argv[1:]: host = sys.argv[1] - else: host = '' - # Client for above server - class C(UDPClient): - def call_1(self, arg): - return self.make_call(1, arg, \ - self.packer.pack_string, \ - self.unpacker.unpack_string) - c = C(host, 0x20000000, 1) - print('making call...') - reply = c.call_1('hello, world, ') - print('call returned', repr(reply)) diff --git a/Demo/rpc/test b/Demo/rpc/test deleted file mode 100755 index ba220f2..0000000 --- a/Demo/rpc/test +++ /dev/null @@ -1,24 +0,0 @@ -: ${PYTHON=python} -: ${SERVER=charon.cwi.nl} - -set -xe - -$PYTHON -c 'from rpc import test; test()' -$PYTHON -c 'from rpc import test; test()' ${SERVER} - -$PYTHON -c 'from rpc import testsvr; testsvr()' & -PID=$! -sleep 2 -$PYTHON -c 'from rpc import testclt; testclt()' -kill -2 $PID - -$PYTHON -c 'from mountclient import test; test()' -$PYTHON -c 'from mountclient import test; test()' gatekeeper.dec.com - -$PYTHON -c 'from nfsclient import test; test()' -$PYTHON -c 'from nfsclient import test; test()' gatekeeper.dec.com -$PYTHON -c 'from nfsclient import test; test()' gatekeeper.dec.com /archive - -$PYTHON -c 'from rnusersclient import test; test()' '' - -$PYTHON -c 'from rpc import testbcast; testbcast()' diff --git a/Demo/rpc/xdr.py b/Demo/rpc/xdr.py deleted file mode 100644 index 2d5f9c3..0000000 --- a/Demo/rpc/xdr.py +++ /dev/null @@ -1,200 +0,0 @@ -# Implement (a subset of) Sun XDR -- RFC1014. - - -try: - import struct -except ImportError: - struct = None - - -Long = type(0) - - -class Packer: - - def __init__(self): - self.reset() - - 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 and struct.pack('l', 1) == '\0\0\0\1': - def pack_uint(self, x): - if type(x) == Long: - x = int((x + 0x80000000) % 0x100000000 \ - - 0x80000000) - self.buf = self.buf + struct.pack('l', 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(int(x>>32 & 0xffffffff)) - self.pack_uint(int(x & 0xffffffff)) - - pack_hyper = pack_uhyper - - def pack_float(self, x): - # XXX - self.buf = self.buf + struct.pack('f', x) - - def pack_double(self, x): - # XXX - self.buf = self.buf + struct.pack('d', x) - - 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(item) - self.pack_uint(0) - - def pack_farray(self, n, list, pack_item): - if len(list) != n: - raise ValueError('wrong array size') - for item in list: - pack_item(item) - - def pack_array(self, list, pack_item): - n = len(list) - self.pack_uint(n) - self.pack_farray(n, list, pack_item) - - -class Unpacker: - - def __init__(self, data): - self.reset(data) - - 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] - if len(data) < 4: - raise EOFError - x = int(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 < 0x80000000: x = int(x) - return x - if struct and struct.unpack('l', '\0\0\0\1') == 1: - def unpack_uint(self): - i = self.pos - self.pos = j = i+4 - data = self.buf[i:j] - if len(data) < 4: - raise EOFError - return struct.unpack('l', data) - - def unpack_int(self): - x = self.unpack_uint() - if x >= 0x80000000: x = x - 0x100000000 - return int(x) - - unpack_enum = unpack_int - - unpack_bool = unpack_int - - def unpack_uhyper(self): - hi = self.unpack_uint() - lo = self.unpack_uint() - return int(hi)<<32 | lo - - def unpack_hyper(self): - x = self.unpack_uhyper() - if x >= 0x8000000000000000: x = x - 0x10000000000000000 - return x - - def unpack_float(self): - # XXX - i = self.pos - self.pos = j = i+4 - data = self.buf[i:j] - if len(data) < 4: - raise EOFError - return struct.unpack('f', data)[0] - - def unpack_double(self): - # XXX - i = self.pos - self.pos = j = i+8 - data = self.buf[i:j] - if len(data) < 8: - raise EOFError - return struct.unpack('d', data)[0] - - 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 EOFError - 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 x == 0: break - if x != 1: - raise RuntimeError('0 or 1 expected, got %r' % (x, )) - item = unpack_item() - list.append(item) - return list - - def unpack_farray(self, n, unpack_item): - list = [] - for i in range(n): - list.append(unpack_item()) - return list - - def unpack_array(self, unpack_item): - n = self.unpack_uint() - return self.unpack_farray(n, unpack_item) |