Initial revision
This commit is contained in:
parent
5f59d6018e
commit
e3cafbe7b8
|
@ -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)
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
|
@ -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…
Reference in New Issue