mirror of
https://github.com/python/cpython.git
synced 2025-08-02 16:13:13 +00:00
Initial revision
This commit is contained in:
parent
5f59d6018e
commit
e3cafbe7b8
5 changed files with 909 additions and 0 deletions
19
Demo/rpc/T.py
Normal file
19
Demo/rpc/T.py
Normal file
|
@ -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)
|
166
Demo/rpc/mountclient.py
Normal file
166
Demo/rpc/mountclient.py
Normal file
|
@ -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()
|
207
Demo/rpc/nfsclient.py
Normal file
207
Demo/rpc/nfsclient.py
Normal file
|
@ -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()
|
376
Demo/rpc/rpc.py
Normal file
376
Demo/rpc/rpc.py
Normal file
|
@ -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
|
141
Demo/rpc/xdr.py
Normal file
141
Demo/rpc/xdr.py
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue