# rpc.py - RFC1057/RFC1831
#
# Copyright (C) 2001 Cendio Systems AB (http://www.cendio.se)
# All rights reserved.
#
# Copyright (c) 2001 Python Software Foundation.
# All rights reserved.
#
# Copyright (c) 2000 BeOpen.com.
# All rights reserved.
#
# Copyright (c) 1995-2001 Corporation for National Research Initiatives.
# All rights reserved.
#
# Copyright (c) 1991-1995 Stichting Mathematisch Centrum.
# All rights reserved.
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# 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
__pychecker__ = 'no-callinit'
import xdrlib
import socket
import os
import time
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
# All RPC errors are subclasses of RPCException
class RPCException(Exception):
def __str__(self):
return "RPCException"
class BadRPCMsgType(RPCException):
def __init__(self, msg_type):
self.msg_type = msg_type
def __str__(self):
return "BadRPCMsgType %d" % self.msg_type
class BadRPCVersion(RPCException):
def __init__(self, version):
self.version = version
def __str__(self):
return "BadRPCVersion %d" % self.version
class RPCMsgDenied(RPCException):
# MSG_DENIED
def __init__(self, stat):
self.stat = stat
def __str__(self):
return "RPCMsgDenied %d" % self.stat
class RPCMisMatch(RPCException):
# MSG_DENIED: RPC_MISMATCH
def __init__(self, low, high):
self.low = low
self.high = high
def __str__(self):
return "RPCMisMatch %d,%d" % (self.low, self.high)
class RPCAuthError(RPCException):
# MSG_DENIED: AUTH_ERROR
def __init__(self, stat):
self.stat = stat
def __str__(self):
return "RPCAuthError %d" % self.stat
class BadRPCReplyType(RPCException):
# Neither MSG_DENIED nor MSG_ACCEPTED
def __init__(self, msg_type):
self.msg_type = msg_type
def __str__(self):
return "BadRPCReplyType %d" % self.msg_type
class RPCProgUnavail(RPCException):
# PROG_UNAVAIL
def __str__(self):
return "RPCProgUnavail"
class RPCProgMismatch(RPCException):
# PROG_MISMATCH
def __init__(self, low, high):
self.low = low
self.high = high
def __str__(self):
return "RPCProgMismatch %d,%d" % (self.low, self.high)
class RPCProcUnavail(RPCException):
# PROC_UNAVAIL
def __str__(self):
return "RPCProcUnavail"
class RPCGarbageArgs(RPCException):
# GARBAGE_ARGS
def __str__(self):
return "RPCGarbageArgs"
class RPCUnextractedData(RPCException):
# xdrlib raised exception because unextracted data remained
def __str__(self):
return "RPCUnextractedData"
class RPCBadAcceptStats(RPCException):
# Unknown accept_stat
def __init__(self, stat):
self.stat = stat
def __str__(self):
return "RPCBadAcceptStats %d" % self.stat
class XidMismatch(RPCException):
# Got wrong XID in reply, got "xid" instead of "expected"
def __init__(self, xid, expected):
self.xid = xid
self.expected = expected
def __str__(self):
return "XidMismatch %d,%d" % (self.xid, self.expected)
class TimeoutError(RPCException):
pass
class PortMapError(RPCException):
pass
class Packer(xdrlib.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(xdrlib.Unpacker):
def unpack_auth(self):
flavor = self.unpack_enum()
stuff = self.unpack_opaque()
if flavor == AUTH_UNIX:
p = Unpacker(stuff)
stuff = p.unpack_auth_unix()
return (flavor, stuff)
def unpack_auth_unix(self):
stamp=self.unpack_uint()
machinename=self.unpack_string()
print "machinename: %s" % machinename
uid=self.unpack_uint()
gid=self.unpack_uint()
n_gids=self.unpack_uint()
gids = []
print "n_gids: %d" % n_gids
for i in range(n_gids):
gids.append(self.unpack_uint())
return stamp, machinename, uid, gid, gids
def unpack_callheader(self):
xid = self.unpack_uint()
msg_type = self.unpack_enum()
if msg_type <> CALL:
raise BadRPCMsgType(msg_type)
rpcvers = self.unpack_uint()
if rpcvers <> RPCVERSION:
raise BadRPCVersion(rpcvers)
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()
msg_type = self.unpack_enum()
if msg_type <> REPLY:
raise BadRPCMsgType(msg_type)
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 RPCMisMatch(low, high)
if stat == AUTH_ERROR:
stat = self.unpack_uint()
raise RPCAuthError(stat)
raise RPCMsgDenied(stat)
if stat <> MSG_ACCEPTED:
raise BadRPCReplyType(stat)
verf = self.unpack_auth()
stat = self.unpack_enum()
if stat == PROG_UNAVAIL:
raise RPCProgUnavail()
if stat == PROG_MISMATCH:
low = self.unpack_uint()
high = self.unpack_uint()
raise RPCProgMismatch(low, high)
if stat == PROC_UNAVAIL:
raise RPCProcUnavail()
if stat == GARBAGE_ARGS:
raise RPCGarbageArgs()
if stat <> SUCCESS:
raise RPCBadAcceptStats(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_buffer()
def make_auth_unix_default():
try:
uid = os.getuid()
gid = os.getgid()
except AttributeError:
uid = gid = 0
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
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.sock = None
self.makesocket() # Assigns to self.sock
self.bindsocket()
self.connsocket()
# Servers may do XID caching, so try to come up with something
# unique to start with. XIDs are 32 bits. Python integers are always
# at least 32 bits.
self.lastxid = int(long(time.time() * 1E6) & 0xfffffff)
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
try:
self.unpacker.done()
except xdrlib.Error:
raise RPCUnextractedData()
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 == 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 == 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 == None:
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_buffer()
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 XidMismatch(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_buffer()
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 TimeoutError()
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_buffer()
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()
try:
self.unpacker.done()
except xdrlib.Error:
raise RPCUnextractedData()
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:
__pychecker__ = 'no-classattr'
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 PortMapError("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 PortMapError("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()
try:
self.unpacker.done()
except xdrlib.Error:
raise RPCUnextractedData()
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_buffer()
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.sock = None
self.prot = None
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 PortMapError("register failed")
def unregister(self):
mapping = self.prog, self.vers, self.prot, self.port
p = TCPPortMapperClient(self.host)
if not p.Unset(mapping):
raise PortMapError("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_buffer()
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_buffer()
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_buffer()
proc = self.unpacker.unpack_uint()
methname = 'handle_' + `proc`
try:
meth = getattr(self, methname)
except AttributeError:
self.packer.pack_uint(PROC_UNAVAIL)
return self.packer.get_buffer()
self.recv_cred = self.unpacker.unpack_auth()
self.recv_verf = self.unpacker.unpack_auth()
try:
meth() # Unpack args, call turn_around(), pack reply
except (EOFError, RPCGarbageArgs):
# 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_buffer()
def turn_around(self):
try:
self.unpacker.done()
except xdrlib.Error:
raise RPCUnextractedData()
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
# 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)
self.sender_port = host_port
reply = self.handle(call)
if reply <> 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' + `reply`
pmap = BroadcastUDPPortMapperClient(bcastaddr)
pmap.set_reply_handler(rh)
pmap.set_timeout(5)
unused_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', `arg`
self.packer.pack_string(arg + arg)
#
s = S('', 0x20000000, 1, 0)
try:
s.unregister()
except PortMapError, e:
print 'RuntimeError:', e.args, '(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', `reply`
# Local variables:
# py-indent-offset: 4
# tab-width: 8
# End:
syntax highlighted by Code2HTML, v. 0.9.1