Import Upstream version 2.7.18
This commit is contained in:
10
Demo/rpc/MANIFEST
Normal file
10
Demo/rpc/MANIFEST
Normal file
@@ -0,0 +1,10 @@
|
||||
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
|
||||
31
Demo/rpc/README
Normal file
31
Demo/rpc/README
Normal file
@@ -0,0 +1,31 @@
|
||||
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 <20>strands rpcgen.py, which is part of "pynfs" (http://www.cendio.se/~peter/pynfs/).
|
||||
22
Demo/rpc/T.py
Normal file
22
Demo/rpc/T.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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)
|
||||
202
Demo/rpc/mountclient.py
Normal file
202
Demo/rpc/mountclient.py
Normal file
@@ -0,0 +1,202 @@
|
||||
# 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])
|
||||
201
Demo/rpc/nfsclient.py
Normal file
201
Demo/rpc/nfsclient.py
Normal file
@@ -0,0 +1,201 @@
|
||||
# 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)
|
||||
98
Demo/rpc/rnusersclient.py
Normal file
98
Demo/rpc/rnusersclient.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# 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:',
|
||||
for (line, name, host, time), idle in list:
|
||||
print strip0(name),
|
||||
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()
|
||||
893
Demo/rpc/rpc.py
Normal file
893
Demo/rpc/rpc.py
Normal file
@@ -0,0 +1,893 @@
|
||||
# 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 | 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)
|
||||
if len(header) < 4:
|
||||
raise EOFError
|
||||
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
|
||||
|
||||
|
||||
# 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, (errno, msg):
|
||||
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, 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,
|
||||
if prot == IPPROTO_TCP: print 'tcp',
|
||||
elif prot == IPPROTO_UDP: print 'udp',
|
||||
else: print prot,
|
||||
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, 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)
|
||||
24
Demo/rpc/test
Executable file
24
Demo/rpc/test
Executable file
@@ -0,0 +1,24 @@
|
||||
: ${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()'
|
||||
200
Demo/rpc/xdr.py
Normal file
200
Demo/rpc/xdr.py
Normal file
@@ -0,0 +1,200 @@
|
||||
# Implement (a subset of) Sun XDR -- RFC1014.
|
||||
|
||||
|
||||
try:
|
||||
import struct
|
||||
except ImportError:
|
||||
struct = None
|
||||
|
||||
|
||||
Long = type(0L)
|
||||
|
||||
|
||||
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 + 0x80000000L) % 0x100000000L \
|
||||
- 0x80000000L)
|
||||
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 = 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 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 >= 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_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)
|
||||
Reference in New Issue
Block a user