blob: 99115d343692dded09de5b1e4f93d05c82289367 [file] [log] [blame]
Guido van Rossum8f1506b1992-12-21 14:32:06 +00001# Sun RPC version 2 -- RFC1057.
Guido van Rossume3cafbe1992-12-14 23:25:04 +00002
Guido van Rossumc4698fb1992-12-17 17:12:17 +00003# XXX There should be separate exceptions for the various reasons why
4# XXX an RPC can fail, rather than using RuntimeError for everything
5
Raymond Hettingerc672f8c2005-04-10 16:21:07 +00006# XXX Need to use class based exceptions rather than string exceptions
7
Guido van Rossumc4698fb1992-12-17 17:12:17 +00008# XXX The UDP version of the protocol resends requests when it does
9# XXX not receive a timely reply -- use only for idempotent calls!
10
Guido van Rossum79f85ee1992-12-20 14:56:32 +000011# XXX There is no provision for call timeout on TCP connections
12
Guido van Rossume3cafbe1992-12-14 23:25:04 +000013import xdr
14import socket
15import os
16
17RPCVERSION = 2
18
19CALL = 0
20REPLY = 1
21
22AUTH_NULL = 0
23AUTH_UNIX = 1
24AUTH_SHORT = 2
25AUTH_DES = 3
26
27MSG_ACCEPTED = 0
28MSG_DENIED = 1
29
Tim Peterse6ddc8b2004-07-18 05:56:09 +000030SUCCESS = 0 # RPC executed successfully
31PROG_UNAVAIL = 1 # remote hasn't exported program
32PROG_MISMATCH = 2 # remote can't support version #
33PROC_UNAVAIL = 3 # program can't support procedure
34GARBAGE_ARGS = 4 # procedure can't decode params
Guido van Rossume3cafbe1992-12-14 23:25:04 +000035
Tim Peterse6ddc8b2004-07-18 05:56:09 +000036RPC_MISMATCH = 0 # RPC version number != 2
37AUTH_ERROR = 1 # remote can't authenticate caller
Guido van Rossume3cafbe1992-12-14 23:25:04 +000038
Tim Peterse6ddc8b2004-07-18 05:56:09 +000039AUTH_BADCRED = 1 # bad credentials (seal broken)
40AUTH_REJECTEDCRED = 2 # client must begin new session
41AUTH_BADVERF = 3 # bad verifier (seal broken)
42AUTH_REJECTEDVERF = 4 # verifier expired or replayed
43AUTH_TOOWEAK = 5 # rejected for security reasons
Guido van Rossume3cafbe1992-12-14 23:25:04 +000044
45
46class Packer(xdr.Packer):
47
Tim Peterse6ddc8b2004-07-18 05:56:09 +000048 def pack_auth(self, auth):
49 flavor, stuff = auth
50 self.pack_enum(flavor)
51 self.pack_opaque(stuff)
Guido van Rossume3cafbe1992-12-14 23:25:04 +000052
Tim Peterse6ddc8b2004-07-18 05:56:09 +000053 def pack_auth_unix(self, stamp, machinename, uid, gid, gids):
54 self.pack_uint(stamp)
55 self.pack_string(machinename)
56 self.pack_uint(uid)
57 self.pack_uint(gid)
58 self.pack_uint(len(gids))
59 for i in gids:
60 self.pack_uint(i)
Guido van Rossume3cafbe1992-12-14 23:25:04 +000061
Tim Peterse6ddc8b2004-07-18 05:56:09 +000062 def pack_callheader(self, xid, prog, vers, proc, cred, verf):
63 self.pack_uint(xid)
64 self.pack_enum(CALL)
65 self.pack_uint(RPCVERSION)
66 self.pack_uint(prog)
67 self.pack_uint(vers)
68 self.pack_uint(proc)
69 self.pack_auth(cred)
70 self.pack_auth(verf)
71 # Caller must add procedure-specific part of call
Guido van Rossume3cafbe1992-12-14 23:25:04 +000072
Tim Peterse6ddc8b2004-07-18 05:56:09 +000073 def pack_replyheader(self, xid, verf):
74 self.pack_uint(xid)
75 self.pack_enum(REPLY)
76 self.pack_uint(MSG_ACCEPTED)
77 self.pack_auth(verf)
78 self.pack_enum(SUCCESS)
79 # Caller must add procedure-specific part of reply
Guido van Rossume3cafbe1992-12-14 23:25:04 +000080
81
Guido van Rossum424c6731992-12-19 00:05:55 +000082# Exceptions
83BadRPCFormat = 'rpc.BadRPCFormat'
84BadRPCVersion = 'rpc.BadRPCVersion'
85GarbageArgs = 'rpc.GarbageArgs'
86
Guido van Rossume3cafbe1992-12-14 23:25:04 +000087class Unpacker(xdr.Unpacker):
88
Tim Peterse6ddc8b2004-07-18 05:56:09 +000089 def unpack_auth(self):
90 flavor = self.unpack_enum()
91 stuff = self.unpack_opaque()
92 return (flavor, stuff)
Guido van Rossume3cafbe1992-12-14 23:25:04 +000093
Tim Peterse6ddc8b2004-07-18 05:56:09 +000094 def unpack_callheader(self):
Raymond Hettingerc672f8c2005-04-10 16:21:07 +000095 xid = self.unpack_uint()
Tim Peterse6ddc8b2004-07-18 05:56:09 +000096 temp = self.unpack_enum()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +000097 if temp != CALL:
Collin Winter6f2df4d2007-07-17 20:59:35 +000098 raise BadRPCFormat('no CALL but %r' % (temp,))
Tim Peterse6ddc8b2004-07-18 05:56:09 +000099 temp = self.unpack_uint()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000100 if temp != RPCVERSION:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000101 raise BadRPCVersion('bad RPC version %r' % (temp,))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000102 prog = self.unpack_uint()
103 vers = self.unpack_uint()
104 proc = self.unpack_uint()
105 cred = self.unpack_auth()
106 verf = self.unpack_auth()
107 return xid, prog, vers, proc, cred, verf
108 # Caller must add procedure-specific part of call
Guido van Rossum424c6731992-12-19 00:05:55 +0000109
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000110 def unpack_replyheader(self):
111 xid = self.unpack_uint()
112 mtype = self.unpack_enum()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000113 if mtype != REPLY:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000114 raise RuntimeError('no REPLY but %r' % (mtype,))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000115 stat = self.unpack_enum()
116 if stat == MSG_DENIED:
117 stat = self.unpack_enum()
118 if stat == RPC_MISMATCH:
119 low = self.unpack_uint()
120 high = self.unpack_uint()
Collin Winter6f2df4d2007-07-17 20:59:35 +0000121 raise RuntimeError('MSG_DENIED: RPC_MISMATCH: %r' % ((low, high),))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000122 if stat == AUTH_ERROR:
123 stat = self.unpack_uint()
Collin Winter6f2df4d2007-07-17 20:59:35 +0000124 raise RuntimeError('MSG_DENIED: AUTH_ERROR: %r' % (stat,))
125 raise RuntimeError('MSG_DENIED: %r' % (stat,))
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000126 if stat != MSG_ACCEPTED:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000127 raise RuntimeError('Neither MSG_DENIED nor MSG_ACCEPTED: %r' % (stat,))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000128 verf = self.unpack_auth()
129 stat = self.unpack_enum()
130 if stat == PROG_UNAVAIL:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000131 raise RuntimeError('call failed: PROG_UNAVAIL')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000132 if stat == PROG_MISMATCH:
133 low = self.unpack_uint()
134 high = self.unpack_uint()
Collin Winter6f2df4d2007-07-17 20:59:35 +0000135 raise RuntimeError('call failed: PROG_MISMATCH: %r' % ((low, high),))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000136 if stat == PROC_UNAVAIL:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000137 raise RuntimeError('call failed: PROC_UNAVAIL')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000138 if stat == GARBAGE_ARGS:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000139 raise RuntimeError('call failed: GARBAGE_ARGS')
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000140 if stat != SUCCESS:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000141 raise RuntimeError('call failed: %r' % (stat,))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000142 return xid, verf
143 # Caller must get procedure-specific part of reply
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000144
145
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000146# Subroutines to create opaque authentication objects
147
148def make_auth_null():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000149 return ''
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000150
151def make_auth_unix(seed, host, uid, gid, groups):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000152 p = Packer()
153 p.pack_auth_unix(seed, host, uid, gid, groups)
154 return p.get_buf()
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000155
156def make_auth_unix_default():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000157 try:
158 from os import getuid, getgid
159 uid = getuid()
160 gid = getgid()
161 except ImportError:
162 uid = gid = 0
163 import time
164 return make_auth_unix(int(time.time()-unix_epoch()), \
165 socket.gethostname(), uid, gid, [])
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000166
Guido van Rossum7a1c7911996-07-21 02:09:54 +0000167_unix_epoch = -1
168def unix_epoch():
169 """Very painful calculation of when the Unix Epoch is.
170
171 This is defined as the return value of time.time() on Jan 1st,
172 1970, 00:00:00 GMT.
173
174 On a Unix system, this should always return 0.0. On a Mac, the
175 calculations are needed -- and hard because of integer overflow
176 and other limitations.
177
178 """
179 global _unix_epoch
180 if _unix_epoch >= 0: return _unix_epoch
181 import time
182 now = time.time()
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000183 localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...)
Guido van Rossum7a1c7911996-07-21 02:09:54 +0000184 gmt = time.gmtime(now)
185 offset = time.mktime(localt) - time.mktime(gmt)
186 y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0
187 offset, ss = divmod(ss + offset, 60)
188 offset, mm = divmod(mm + offset, 60)
189 offset, hh = divmod(hh + offset, 24)
190 d = d + offset
191 _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0))
Collin Winter6f2df4d2007-07-17 20:59:35 +0000192 print("Unix epoch:", time.ctime(_unix_epoch))
Guido van Rossum7a1c7911996-07-21 02:09:54 +0000193 return _unix_epoch
194
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000195
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000196# Common base class for clients
197
198class Client:
199
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000200 def __init__(self, host, prog, vers, port):
201 self.host = host
202 self.prog = prog
203 self.vers = vers
204 self.port = port
205 self.makesocket() # Assigns to self.sock
206 self.bindsocket()
207 self.connsocket()
208 self.lastxid = 0 # XXX should be more random?
209 self.addpackers()
210 self.cred = None
211 self.verf = None
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000212
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000213 def close(self):
214 self.sock.close()
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000215
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000216 def makesocket(self):
217 # This MUST be overridden
Collin Winter6f2df4d2007-07-17 20:59:35 +0000218 raise RuntimeError('makesocket not defined')
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000219
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000220 def connsocket(self):
221 # Override this if you don't want/need a connection
222 self.sock.connect((self.host, self.port))
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000223
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000224 def bindsocket(self):
225 # Override this to bind to a different port (e.g. reserved)
226 self.sock.bind(('', 0))
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000227
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000228 def addpackers(self):
229 # Override this to use derived classes from Packer/Unpacker
230 self.packer = Packer()
231 self.unpacker = Unpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000232
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000233 def make_call(self, proc, args, pack_func, unpack_func):
234 # Don't normally override this (but see Broadcast)
235 if pack_func is None and args is not None:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000236 raise TypeError('non-null args with null pack_func')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000237 self.start_call(proc)
238 if pack_func:
239 pack_func(args)
240 self.do_call()
241 if unpack_func:
242 result = unpack_func()
243 else:
244 result = None
245 self.unpacker.done()
246 return result
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000247
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000248 def start_call(self, proc):
249 # Don't override this
250 self.lastxid = xid = self.lastxid + 1
251 cred = self.mkcred()
252 verf = self.mkverf()
253 p = self.packer
254 p.reset()
255 p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000256
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000257 def do_call(self):
258 # This MUST be overridden
Collin Winter6f2df4d2007-07-17 20:59:35 +0000259 raise RuntimeError('do_call not defined')
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000260
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000261 def mkcred(self):
262 # Override this to use more powerful credentials
263 if self.cred == None:
264 self.cred = (AUTH_NULL, make_auth_null())
265 return self.cred
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000266
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000267 def mkverf(self):
268 # Override this to use a more powerful verifier
269 if self.verf == None:
270 self.verf = (AUTH_NULL, make_auth_null())
271 return self.verf
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000272
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000273 def call_0(self): # Procedure 0 is always like this
274 return self.make_call(0, None, None, None)
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000275
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000276
277# Record-Marking standard support
278
279def sendfrag(sock, last, frag):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000280 x = len(frag)
Collin Winter6f2df4d2007-07-17 20:59:35 +0000281 if last: x = x | 0x80000000
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000282 header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
283 chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
284 sock.send(header + frag)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000285
286def sendrecord(sock, record):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000287 sendfrag(sock, 1, record)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000288
289def recvfrag(sock):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000290 header = sock.recv(4)
291 if len(header) < 4:
292 raise EOFError
Collin Winter6f2df4d2007-07-17 20:59:35 +0000293 x = int(ord(header[0]))<<24 | ord(header[1])<<16 | \
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000294 ord(header[2])<<8 | ord(header[3])
295 last = ((x & 0x80000000) != 0)
296 n = int(x & 0x7fffffff)
297 frag = ''
298 while n > 0:
299 buf = sock.recv(n)
300 if not buf: raise EOFError
301 n = n - len(buf)
302 frag = frag + buf
303 return last, frag
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000304
305def recvrecord(sock):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000306 record = ''
307 last = 0
308 while not last:
309 last, frag = recvfrag(sock)
310 record = record + frag
311 return record
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000312
313
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000314# Try to bind to a reserved port (must be root)
315
316last_resv_port_tried = None
317def bindresvport(sock, host):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000318 global last_resv_port_tried
319 FIRST, LAST = 600, 1024 # Range of ports to try
320 if last_resv_port_tried == None:
321 import os
322 last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST)
323 for i in range(last_resv_port_tried, LAST) + \
324 range(FIRST, last_resv_port_tried):
325 last_resv_port_tried = i
326 try:
327 sock.bind((host, i))
328 return last_resv_port_tried
Guido van Rossumb940e112007-01-10 16:19:56 +0000329 except socket.error as e:
330 (errno, msg) = e
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000331 if errno != 114:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000332 raise socket.error(errno, msg)
333 raise RuntimeError('can\'t assign reserved port')
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000334
335
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000336# Client using TCP to a specific port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000337
338class RawTCPClient(Client):
339
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000340 def makesocket(self):
341 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000342
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000343 def do_call(self):
344 call = self.packer.get_buf()
345 sendrecord(self.sock, call)
346 reply = recvrecord(self.sock)
347 u = self.unpacker
348 u.reset(reply)
349 xid, verf = u.unpack_replyheader()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000350 if xid != self.lastxid:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000351 # Can't really happen since this is TCP...
Collin Winter6f2df4d2007-07-17 20:59:35 +0000352 raise RuntimeError('wrong xid in reply %r instead of %r' % (
353 xid, self.lastxid))
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000354
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000355
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000356# Client using UDP to a specific port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000357
358class RawUDPClient(Client):
359
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000360 def makesocket(self):
361 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000362
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000363 def do_call(self):
364 call = self.packer.get_buf()
365 self.sock.send(call)
366 try:
367 from select import select
368 except ImportError:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000369 print('WARNING: select not found, RPC may hang')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000370 select = None
371 BUFSIZE = 8192 # Max UDP buffer size
372 timeout = 1
373 count = 5
374 while 1:
375 r, w, x = [self.sock], [], []
376 if select:
377 r, w, x = select(r, w, x, timeout)
378 if self.sock not in r:
379 count = count - 1
Collin Winter6f2df4d2007-07-17 20:59:35 +0000380 if count < 0: raise RuntimeError('timeout')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000381 if timeout < 25: timeout = timeout *2
382## print 'RESEND', timeout, count
383 self.sock.send(call)
384 continue
385 reply = self.sock.recv(BUFSIZE)
386 u = self.unpacker
387 u.reset(reply)
388 xid, verf = u.unpack_replyheader()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000389 if xid != self.lastxid:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000390## print 'BAD xid'
391 continue
392 break
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000393
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000394
395# Client using UDP broadcast to a specific port
396
397class RawBroadcastUDPClient(RawUDPClient):
398
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000399 def __init__(self, bcastaddr, prog, vers, port):
400 RawUDPClient.__init__(self, bcastaddr, prog, vers, port)
401 self.reply_handler = None
402 self.timeout = 30
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000403
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000404 def connsocket(self):
405 # Don't connect -- use sendto
406 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000407
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000408 def set_reply_handler(self, reply_handler):
409 self.reply_handler = reply_handler
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000410
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000411 def set_timeout(self, timeout):
412 self.timeout = timeout # Use None for infinite timeout
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000413
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000414 def make_call(self, proc, args, pack_func, unpack_func):
415 if pack_func is None and args is not None:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000416 raise TypeError('non-null args with null pack_func')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000417 self.start_call(proc)
418 if pack_func:
419 pack_func(args)
420 call = self.packer.get_buf()
421 self.sock.sendto(call, (self.host, self.port))
422 try:
423 from select import select
424 except ImportError:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000425 print('WARNING: select not found, broadcast will hang')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000426 select = None
427 BUFSIZE = 8192 # Max UDP buffer size (for reply)
428 replies = []
429 if unpack_func is None:
430 def dummy(): pass
431 unpack_func = dummy
432 while 1:
433 r, w, x = [self.sock], [], []
434 if select:
435 if self.timeout is None:
436 r, w, x = select(r, w, x)
437 else:
438 r, w, x = select(r, w, x, self.timeout)
439 if self.sock not in r:
440 break
441 reply, fromaddr = self.sock.recvfrom(BUFSIZE)
442 u = self.unpacker
443 u.reset(reply)
444 xid, verf = u.unpack_replyheader()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000445 if xid != self.lastxid:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000446## print 'BAD xid'
447 continue
448 reply = unpack_func()
449 self.unpacker.done()
450 replies.append((reply, fromaddr))
451 if self.reply_handler:
452 self.reply_handler(reply, fromaddr)
453 return replies
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000454
455
456# Port mapper interface
457
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000458# Program number, version and (fixed!) port number
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000459PMAP_PROG = 100000
460PMAP_VERS = 2
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000461PMAP_PORT = 111
462
463# Procedure numbers
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000464PMAPPROC_NULL = 0 # (void) -> void
465PMAPPROC_SET = 1 # (mapping) -> bool
466PMAPPROC_UNSET = 2 # (mapping) -> bool
467PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int
468PMAPPROC_DUMP = 4 # (void) -> pmaplist
469PMAPPROC_CALLIT = 5 # (call_args) -> call_result
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000470
471# A mapping is (prog, vers, prot, port) and prot is one of:
472
473IPPROTO_TCP = 6
474IPPROTO_UDP = 17
475
476# A pmaplist is a variable-length list of mappings, as follows:
477# either (1, mapping, pmaplist) or (0).
478
479# A call_args is (prog, vers, proc, args) where args is opaque;
480# a call_result is (port, res) where res is opaque.
481
482
483class PortMapperPacker(Packer):
484
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000485 def pack_mapping(self, mapping):
486 prog, vers, prot, port = mapping
487 self.pack_uint(prog)
488 self.pack_uint(vers)
489 self.pack_uint(prot)
490 self.pack_uint(port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000491
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000492 def pack_pmaplist(self, list):
493 self.pack_list(list, self.pack_mapping)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000494
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000495 def pack_call_args(self, ca):
496 prog, vers, proc, args = ca
497 self.pack_uint(prog)
498 self.pack_uint(vers)
499 self.pack_uint(proc)
500 self.pack_opaque(args)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000501
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000502
503class PortMapperUnpacker(Unpacker):
504
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000505 def unpack_mapping(self):
506 prog = self.unpack_uint()
507 vers = self.unpack_uint()
508 prot = self.unpack_uint()
509 port = self.unpack_uint()
510 return prog, vers, prot, port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000511
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000512 def unpack_pmaplist(self):
513 return self.unpack_list(self.unpack_mapping)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000514
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000515 def unpack_call_result(self):
516 port = self.unpack_uint()
517 res = self.unpack_opaque()
518 return port, res
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000519
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000520
521class PartialPortMapperClient:
522
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000523 def addpackers(self):
524 self.packer = PortMapperPacker()
525 self.unpacker = PortMapperUnpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000526
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000527 def Set(self, mapping):
528 return self.make_call(PMAPPROC_SET, mapping, \
529 self.packer.pack_mapping, \
530 self.unpacker.unpack_uint)
Guido van Rossum424c6731992-12-19 00:05:55 +0000531
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000532 def Unset(self, mapping):
533 return self.make_call(PMAPPROC_UNSET, mapping, \
534 self.packer.pack_mapping, \
535 self.unpacker.unpack_uint)
Guido van Rossum424c6731992-12-19 00:05:55 +0000536
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000537 def Getport(self, mapping):
538 return self.make_call(PMAPPROC_GETPORT, mapping, \
539 self.packer.pack_mapping, \
540 self.unpacker.unpack_uint)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000541
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000542 def Dump(self):
543 return self.make_call(PMAPPROC_DUMP, None, \
544 None, \
545 self.unpacker.unpack_pmaplist)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000546
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000547 def Callit(self, ca):
548 return self.make_call(PMAPPROC_CALLIT, ca, \
549 self.packer.pack_call_args, \
550 self.unpacker.unpack_call_result)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000551
552
553class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
554
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000555 def __init__(self, host):
556 RawTCPClient.__init__(self, \
557 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000558
559
560class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
561
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000562 def __init__(self, host):
563 RawUDPClient.__init__(self, \
564 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000565
566
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000567class BroadcastUDPPortMapperClient(PartialPortMapperClient, \
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000568 RawBroadcastUDPClient):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000569
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000570 def __init__(self, bcastaddr):
571 RawBroadcastUDPClient.__init__(self, \
572 bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000573
574
575# Generic clients that find their server through the Port mapper
576
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000577class TCPClient(RawTCPClient):
578
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000579 def __init__(self, host, prog, vers):
580 pmap = TCPPortMapperClient(host)
581 port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
582 pmap.close()
583 if port == 0:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000584 raise RuntimeError('program not registered')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000585 RawTCPClient.__init__(self, host, prog, vers, port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000586
587
588class UDPClient(RawUDPClient):
589
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000590 def __init__(self, host, prog, vers):
591 pmap = UDPPortMapperClient(host)
592 port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
593 pmap.close()
594 if port == 0:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000595 raise RuntimeError('program not registered')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000596 RawUDPClient.__init__(self, host, prog, vers, port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000597
598
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000599class BroadcastUDPClient(Client):
600
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000601 def __init__(self, bcastaddr, prog, vers):
602 self.pmap = BroadcastUDPPortMapperClient(bcastaddr)
603 self.pmap.set_reply_handler(self.my_reply_handler)
604 self.prog = prog
605 self.vers = vers
606 self.user_reply_handler = None
607 self.addpackers()
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000608
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000609 def close(self):
610 self.pmap.close()
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000611
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000612 def set_reply_handler(self, reply_handler):
613 self.user_reply_handler = reply_handler
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000614
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000615 def set_timeout(self, timeout):
616 self.pmap.set_timeout(timeout)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000617
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000618 def my_reply_handler(self, reply, fromaddr):
619 port, res = reply
620 self.unpacker.reset(res)
621 result = self.unpack_func()
622 self.unpacker.done()
623 self.replies.append((result, fromaddr))
624 if self.user_reply_handler is not None:
625 self.user_reply_handler(result, fromaddr)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000626
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000627 def make_call(self, proc, args, pack_func, unpack_func):
628 self.packer.reset()
629 if pack_func:
630 pack_func(args)
631 if unpack_func is None:
632 def dummy(): pass
633 self.unpack_func = dummy
634 else:
635 self.unpack_func = unpack_func
636 self.replies = []
637 packed_args = self.packer.get_buf()
638 dummy_replies = self.pmap.Callit( \
639 (self.prog, self.vers, proc, packed_args))
640 return self.replies
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000641
642
Guido van Rossum424c6731992-12-19 00:05:55 +0000643# Server classes
644
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000645# These are not symmetric to the Client classes
646# XXX No attempt is made to provide authorization hooks yet
647
Guido van Rossum424c6731992-12-19 00:05:55 +0000648class Server:
649
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000650 def __init__(self, host, prog, vers, port):
651 self.host = host # Should normally be '' for default interface
652 self.prog = prog
653 self.vers = vers
654 self.port = port # Should normally be 0 for random port
655 self.makesocket() # Assigns to self.sock and self.prot
656 self.bindsocket()
657 self.host, self.port = self.sock.getsockname()
658 self.addpackers()
Guido van Rossum424c6731992-12-19 00:05:55 +0000659
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000660 def register(self):
661 mapping = self.prog, self.vers, self.prot, self.port
662 p = TCPPortMapperClient(self.host)
663 if not p.Set(mapping):
Collin Winter6f2df4d2007-07-17 20:59:35 +0000664 raise RuntimeError('register failed')
Guido van Rossum424c6731992-12-19 00:05:55 +0000665
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000666 def unregister(self):
667 mapping = self.prog, self.vers, self.prot, self.port
668 p = TCPPortMapperClient(self.host)
669 if not p.Unset(mapping):
Collin Winter6f2df4d2007-07-17 20:59:35 +0000670 raise RuntimeError('unregister failed')
Guido van Rossum424c6731992-12-19 00:05:55 +0000671
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000672 def handle(self, call):
673 # Don't use unpack_header but parse the header piecewise
674 # XXX I have no idea if I am using the right error responses!
675 self.unpacker.reset(call)
676 self.packer.reset()
677 xid = self.unpacker.unpack_uint()
678 self.packer.pack_uint(xid)
679 temp = self.unpacker.unpack_enum()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000680 if temp != CALL:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000681 return None # Not worthy of a reply
682 self.packer.pack_uint(REPLY)
683 temp = self.unpacker.unpack_uint()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000684 if temp != RPCVERSION:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000685 self.packer.pack_uint(MSG_DENIED)
686 self.packer.pack_uint(RPC_MISMATCH)
687 self.packer.pack_uint(RPCVERSION)
688 self.packer.pack_uint(RPCVERSION)
689 return self.packer.get_buf()
690 self.packer.pack_uint(MSG_ACCEPTED)
691 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
692 prog = self.unpacker.unpack_uint()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000693 if prog != self.prog:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000694 self.packer.pack_uint(PROG_UNAVAIL)
695 return self.packer.get_buf()
696 vers = self.unpacker.unpack_uint()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000697 if vers != self.vers:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000698 self.packer.pack_uint(PROG_MISMATCH)
699 self.packer.pack_uint(self.vers)
700 self.packer.pack_uint(self.vers)
701 return self.packer.get_buf()
702 proc = self.unpacker.unpack_uint()
703 methname = 'handle_' + repr(proc)
704 try:
705 meth = getattr(self, methname)
706 except AttributeError:
707 self.packer.pack_uint(PROC_UNAVAIL)
708 return self.packer.get_buf()
709 cred = self.unpacker.unpack_auth()
710 verf = self.unpacker.unpack_auth()
711 try:
712 meth() # Unpack args, call turn_around(), pack reply
713 except (EOFError, GarbageArgs):
714 # Too few or too many arguments
715 self.packer.reset()
716 self.packer.pack_uint(xid)
717 self.packer.pack_uint(REPLY)
718 self.packer.pack_uint(MSG_ACCEPTED)
719 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
720 self.packer.pack_uint(GARBAGE_ARGS)
721 return self.packer.get_buf()
Guido van Rossum424c6731992-12-19 00:05:55 +0000722
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000723 def turn_around(self):
724 try:
725 self.unpacker.done()
726 except RuntimeError:
727 raise GarbageArgs
728 self.packer.pack_uint(SUCCESS)
Guido van Rossum424c6731992-12-19 00:05:55 +0000729
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000730 def handle_0(self): # Handle NULL message
731 self.turn_around()
Guido van Rossum424c6731992-12-19 00:05:55 +0000732
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000733 def makesocket(self):
734 # This MUST be overridden
Collin Winter6f2df4d2007-07-17 20:59:35 +0000735 raise RuntimeError('makesocket not defined')
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000736
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000737 def bindsocket(self):
738 # Override this to bind to a different port (e.g. reserved)
739 self.sock.bind((self.host, self.port))
Guido van Rossum424c6731992-12-19 00:05:55 +0000740
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000741 def addpackers(self):
742 # Override this to use derived classes from Packer/Unpacker
743 self.packer = Packer()
744 self.unpacker = Unpacker('')
Guido van Rossum424c6731992-12-19 00:05:55 +0000745
746
747class TCPServer(Server):
748
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000749 def makesocket(self):
750 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
751 self.prot = IPPROTO_TCP
Guido van Rossum424c6731992-12-19 00:05:55 +0000752
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000753 def loop(self):
754 self.sock.listen(0)
755 while 1:
756 self.session(self.sock.accept())
Guido van Rossum424c6731992-12-19 00:05:55 +0000757
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000758 def session(self, connection):
759 sock, (host, port) = connection
760 while 1:
761 try:
762 call = recvrecord(sock)
763 except EOFError:
764 break
Guido van Rossumb940e112007-01-10 16:19:56 +0000765 except socket.error as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000766 print('socket error:', msg)
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000767 break
768 reply = self.handle(call)
769 if reply is not None:
770 sendrecord(sock, reply)
Guido van Rossum424c6731992-12-19 00:05:55 +0000771
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000772 def forkingloop(self):
773 # Like loop but uses forksession()
774 self.sock.listen(0)
775 while 1:
776 self.forksession(self.sock.accept())
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000777
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000778 def forksession(self, connection):
779 # Like session but forks off a subprocess
780 import os
781 # Wait for deceased children
782 try:
783 while 1:
784 pid, sts = os.waitpid(0, 1)
785 except os.error:
786 pass
787 pid = None
788 try:
789 pid = os.fork()
790 if pid: # Parent
791 connection[0].close()
792 return
793 # Child
794 self.session(connection)
795 finally:
796 # Make sure we don't fall through in the parent
797 if pid == 0:
798 os._exit(0)
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000799
Guido van Rossum424c6731992-12-19 00:05:55 +0000800
801class UDPServer(Server):
802
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000803 def makesocket(self):
804 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
805 self.prot = IPPROTO_UDP
Guido van Rossum424c6731992-12-19 00:05:55 +0000806
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000807 def loop(self):
808 while 1:
809 self.session()
Guido van Rossum424c6731992-12-19 00:05:55 +0000810
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000811 def session(self):
812 call, host_port = self.sock.recvfrom(8192)
813 reply = self.handle(call)
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000814 if reply != None:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000815 self.sock.sendto(reply, host_port)
Guido van Rossum424c6731992-12-19 00:05:55 +0000816
817
818# Simple test program -- dump local portmapper status
819
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000820def test():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000821 pmap = UDPPortMapperClient('')
822 list = pmap.Dump()
823 list.sort()
824 for prog, vers, prot, port in list:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000825 print(prog, vers, end=' ')
826 if prot == IPPROTO_TCP: print('tcp', end=' ')
827 elif prot == IPPROTO_UDP: print('udp', end=' ')
828 else: print(prot, end=' ')
829 print(port)
Guido van Rossum424c6731992-12-19 00:05:55 +0000830
831
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000832# Test program for broadcast operation -- dump everybody's portmapper status
833
834def testbcast():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000835 import sys
836 if sys.argv[1:]:
837 bcastaddr = sys.argv[1]
838 else:
839 bcastaddr = '<broadcast>'
840 def rh(reply, fromaddr):
841 host, port = fromaddr
Collin Winter6f2df4d2007-07-17 20:59:35 +0000842 print(host + '\t' + repr(reply))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000843 pmap = BroadcastUDPPortMapperClient(bcastaddr)
844 pmap.set_reply_handler(rh)
845 pmap.set_timeout(5)
846 replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0))
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000847
848
849# Test program for server, with corresponding client
Guido van Rossum424c6731992-12-19 00:05:55 +0000850# On machine A: python -c 'import rpc; rpc.testsvr()'
851# On machine B: python -c 'import rpc; rpc.testclt()' A
852# (A may be == B)
853
854def testsvr():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000855 # Simple test class -- proc 1 doubles its string argument as reply
856 class S(UDPServer):
857 def handle_1(self):
858 arg = self.unpacker.unpack_string()
859 self.turn_around()
Collin Winter6f2df4d2007-07-17 20:59:35 +0000860 print('RPC function 1 called, arg', repr(arg))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000861 self.packer.pack_string(arg + arg)
862 #
863 s = S('', 0x20000000, 1, 0)
864 try:
865 s.unregister()
Guido van Rossumb940e112007-01-10 16:19:56 +0000866 except RuntimeError as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000867 print('RuntimeError:', msg, '(ignored)')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000868 s.register()
Collin Winter6f2df4d2007-07-17 20:59:35 +0000869 print('Service started...')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000870 try:
871 s.loop()
872 finally:
873 s.unregister()
Collin Winter6f2df4d2007-07-17 20:59:35 +0000874 print('Service interrupted.')
Guido van Rossum424c6731992-12-19 00:05:55 +0000875
876
877def testclt():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000878 import sys
879 if sys.argv[1:]: host = sys.argv[1]
880 else: host = ''
881 # Client for above server
882 class C(UDPClient):
883 def call_1(self, arg):
884 return self.make_call(1, arg, \
885 self.packer.pack_string, \
886 self.unpacker.unpack_string)
887 c = C(host, 0x20000000, 1)
Collin Winter6f2df4d2007-07-17 20:59:35 +0000888 print('making call...')
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000889 reply = c.call_1('hello, world, ')
Collin Winter6f2df4d2007-07-17 20:59:35 +0000890 print('call returned', repr(reply))