blob: f44b3e442603f191be8357b5192870f8f3b7a29a [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
6# XXX The UDP version of the protocol resends requests when it does
7# XXX not receive a timely reply -- use only for idempotent calls!
8
Guido van Rossum79f85ee1992-12-20 14:56:32 +00009# XXX There is no provision for call timeout on TCP connections
10
Guido van Rossume3cafbe1992-12-14 23:25:04 +000011import xdr
12import socket
13import os
14
15RPCVERSION = 2
16
17CALL = 0
18REPLY = 1
19
20AUTH_NULL = 0
21AUTH_UNIX = 1
22AUTH_SHORT = 2
23AUTH_DES = 3
24
25MSG_ACCEPTED = 0
26MSG_DENIED = 1
27
28SUCCESS = 0 # RPC executed successfully
29PROG_UNAVAIL = 1 # remote hasn't exported program
30PROG_MISMATCH = 2 # remote can't support version #
31PROC_UNAVAIL = 3 # program can't support procedure
32GARBAGE_ARGS = 4 # procedure can't decode params
33
34RPC_MISMATCH = 0 # RPC version number != 2
35AUTH_ERROR = 1 # remote can't authenticate caller
36
37AUTH_BADCRED = 1 # bad credentials (seal broken)
38AUTH_REJECTEDCRED = 2 # client must begin new session
39AUTH_BADVERF = 3 # bad verifier (seal broken)
40AUTH_REJECTEDVERF = 4 # verifier expired or replayed
41AUTH_TOOWEAK = 5 # rejected for security reasons
42
43
44class Packer(xdr.Packer):
45
46 def pack_auth(self, auth):
47 flavor, stuff = auth
48 self.pack_enum(flavor)
49 self.pack_opaque(stuff)
50
51 def pack_auth_unix(self, stamp, machinename, uid, gid, gids):
52 self.pack_uint(stamp)
53 self.pack_string(machinename)
54 self.pack_uint(uid)
55 self.pack_uint(gid)
56 self.pack_uint(len(gids))
57 for i in gids:
58 self.pack_uint(i)
59
60 def pack_callheader(self, xid, prog, vers, proc, cred, verf):
61 self.pack_uint(xid)
62 self.pack_enum(CALL)
63 self.pack_uint(RPCVERSION)
64 self.pack_uint(prog)
65 self.pack_uint(vers)
66 self.pack_uint(proc)
67 self.pack_auth(cred)
68 self.pack_auth(verf)
69 # Caller must add procedure-specific part of call
70
71 def pack_replyheader(self, xid, verf):
72 self.pack_uint(xid)
73 self.pack_enum(REPLY)
74 self.pack_uint(MSG_ACCEPTED)
75 self.pack_auth(verf)
76 self.pack_enum(SUCCESS)
77 # Caller must add procedure-specific part of reply
78
79
Guido van Rossum424c6731992-12-19 00:05:55 +000080# Exceptions
81BadRPCFormat = 'rpc.BadRPCFormat'
82BadRPCVersion = 'rpc.BadRPCVersion'
83GarbageArgs = 'rpc.GarbageArgs'
84
Guido van Rossume3cafbe1992-12-14 23:25:04 +000085class Unpacker(xdr.Unpacker):
86
87 def unpack_auth(self):
88 flavor = self.unpack_enum()
89 stuff = self.unpack_opaque()
90 return (flavor, stuff)
91
Guido van Rossum424c6731992-12-19 00:05:55 +000092 def unpack_callheader(self):
93 xid = self.unpack_uint(xid)
94 temp = self.unpack_enum()
95 if temp <> CALL:
96 raise BadRPCFormat, 'no CALL but ' + `temp`
97 temp = self.unpack_uint()
98 if temp <> RPCVERSION:
99 raise BadRPCVerspion, 'bad RPC version ' + `temp`
100 prog = self.unpack_uint()
101 vers = self.unpack_uint()
102 proc = self.unpack_uint()
103 cred = self.unpack_auth()
104 verf = self.unpack_auth()
105 return xid, prog, vers, proc, cred, verf
106 # Caller must add procedure-specific part of call
107
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000108 def unpack_replyheader(self):
109 xid = self.unpack_uint()
110 mtype = self.unpack_enum()
111 if mtype <> REPLY:
Guido van Rossumc4698fb1992-12-17 17:12:17 +0000112 raise RuntimeError, 'no REPLY but ' + `mtype`
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000113 stat = self.unpack_enum()
Guido van Rossumc4698fb1992-12-17 17:12:17 +0000114 if stat == MSG_DENIED:
115 stat = self.unpack_enum()
116 if stat == RPC_MISMATCH:
117 low = self.unpack_uint()
118 high = self.unpack_uint()
119 raise RuntimeError, \
120 'MSG_DENIED: RPC_MISMATCH: ' + `low, high`
121 if stat == AUTH_ERROR:
122 stat = self.unpack_uint()
123 raise RuntimeError, \
124 'MSG_DENIED: AUTH_ERROR: ' + `stat`
125 raise RuntimeError, 'MSG_DENIED: ' + `stat`
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000126 if stat <> MSG_ACCEPTED:
Guido van Rossumc4698fb1992-12-17 17:12:17 +0000127 raise RuntimeError, \
128 'Neither MSG_DENIED nor MSG_ACCEPTED: ' + `stat`
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000129 verf = self.unpack_auth()
130 stat = self.unpack_enum()
Guido van Rossum424c6731992-12-19 00:05:55 +0000131 if stat == PROG_UNAVAIL:
132 raise RuntimeError, 'call failed: PROG_UNAVAIL'
Guido van Rossumc4698fb1992-12-17 17:12:17 +0000133 if stat == PROG_MISMATCH:
134 low = self.unpack_uint()
135 high = self.unpack_uint()
136 raise RuntimeError, \
137 'call failed: PROG_MISMATCH: ' + `low, high`
Guido van Rossum424c6731992-12-19 00:05:55 +0000138 if stat == PROC_UNAVAIL:
139 raise RuntimeError, 'call failed: PROC_UNAVAIL'
140 if stat == GARBAGE_ARGS:
141 raise RuntimeError, 'call failed: GARBAGE_ARGS'
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000142 if stat <> SUCCESS:
Guido van Rossumc4698fb1992-12-17 17:12:17 +0000143 raise RuntimeError, 'call failed: ' + `stat`
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000144 return xid, verf
145 # Caller must get procedure-specific part of reply
146
147
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000148# Subroutines to create opaque authentication objects
149
150def make_auth_null():
151 return ''
152
153def make_auth_unix(seed, host, uid, gid, groups):
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000154 p = Packer()
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000155 p.pack_auth_unix(seed, host, uid, gid, groups)
156 return p.get_buf()
157
158def make_auth_unix_default():
Guido van Rossuma5854441992-12-17 17:31:58 +0000159 try:
160 from os import getuid, getgid
161 uid = getuid()
162 gid = getgid()
163 except ImportError:
164 uid = gid = 0
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000165 import time
Guido van Rossum7a1c7911996-07-21 02:09:54 +0000166 return make_auth_unix(int(time.time()-unix_epoch()), \
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000167 socket.gethostname(), uid, gid, [])
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000168
Guido van Rossum7a1c7911996-07-21 02:09:54 +0000169_unix_epoch = -1
170def unix_epoch():
171 """Very painful calculation of when the Unix Epoch is.
172
173 This is defined as the return value of time.time() on Jan 1st,
174 1970, 00:00:00 GMT.
175
176 On a Unix system, this should always return 0.0. On a Mac, the
177 calculations are needed -- and hard because of integer overflow
178 and other limitations.
179
180 """
181 global _unix_epoch
182 if _unix_epoch >= 0: return _unix_epoch
183 import time
184 now = time.time()
185 localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...)
186 gmt = time.gmtime(now)
187 offset = time.mktime(localt) - time.mktime(gmt)
188 y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0
189 offset, ss = divmod(ss + offset, 60)
190 offset, mm = divmod(mm + offset, 60)
191 offset, hh = divmod(hh + offset, 24)
192 d = d + offset
193 _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0))
194 print "Unix epoch:", time.ctime(_unix_epoch)
195 return _unix_epoch
196
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000197
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000198# Common base class for clients
199
200class Client:
201
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000202 def __init__(self, host, prog, vers, port):
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000203 self.host = host
204 self.prog = prog
205 self.vers = vers
206 self.port = port
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000207 self.makesocket() # Assigns to self.sock
208 self.bindsocket()
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000209 self.connsocket()
210 self.lastxid = 0 # XXX should be more random?
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000211 self.addpackers()
212 self.cred = None
213 self.verf = None
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000214
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000215 def close(self):
216 self.sock.close()
217
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000218 def makesocket(self):
219 # This MUST be overridden
220 raise RuntimeError, 'makesocket not defined'
221
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000222 def connsocket(self):
223 # Override this if you don't want/need a connection
224 self.sock.connect((self.host, self.port))
225
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000226 def bindsocket(self):
227 # Override this to bind to a different port (e.g. reserved)
228 self.sock.bind(('', 0))
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000229
230 def addpackers(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000231 # Override this to use derived classes from Packer/Unpacker
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000232 self.packer = Packer()
233 self.unpacker = Unpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000234
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000235 def make_call(self, proc, args, pack_func, unpack_func):
236 # Don't normally override this (but see Broadcast)
237 if pack_func is None and args is not None:
238 raise TypeError, 'non-null args with null pack_func'
239 self.start_call(proc)
240 if pack_func:
241 pack_func(args)
242 self.do_call()
243 if unpack_func:
244 result = unpack_func()
245 else:
246 result = None
247 self.unpacker.done()
248 return result
249
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000250 def start_call(self, proc):
251 # Don't override this
252 self.lastxid = xid = self.lastxid + 1
253 cred = self.mkcred()
254 verf = self.mkverf()
255 p = self.packer
256 p.reset()
257 p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
258
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000259 def do_call(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000260 # This MUST be overridden
261 raise RuntimeError, 'do_call not defined'
262
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000263 def mkcred(self):
264 # Override this to use more powerful credentials
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000265 if self.cred == None:
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000266 self.cred = (AUTH_NULL, make_auth_null())
267 return self.cred
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000268
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000269 def mkverf(self):
270 # Override this to use a more powerful verifier
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000271 if self.verf == None:
272 self.verf = (AUTH_NULL, make_auth_null())
273 return self.verf
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000274
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000275 def call_0(self): # Procedure 0 is always like this
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000276 return self.make_call(0, None, None, None)
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000277
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000278
279# Record-Marking standard support
280
281def sendfrag(sock, last, frag):
282 x = len(frag)
283 if last: x = x | 0x80000000L
284 header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
285 chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
286 sock.send(header + frag)
287
288def sendrecord(sock, record):
289 sendfrag(sock, 1, record)
290
291def recvfrag(sock):
292 header = sock.recv(4)
Guido van Rossum424c6731992-12-19 00:05:55 +0000293 if len(header) < 4:
294 raise EOFError
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000295 x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
296 ord(header[2])<<8 | ord(header[3])
297 last = ((x & 0x80000000) != 0)
298 n = int(x & 0x7fffffff)
299 frag = ''
300 while n > 0:
301 buf = sock.recv(n)
302 if not buf: raise EOFError
303 n = n - len(buf)
304 frag = frag + buf
305 return last, frag
306
307def recvrecord(sock):
308 record = ''
309 last = 0
310 while not last:
311 last, frag = recvfrag(sock)
312 record = record + frag
313 return record
314
315
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000316# Try to bind to a reserved port (must be root)
317
318last_resv_port_tried = None
319def bindresvport(sock, host):
320 global last_resv_port_tried
321 FIRST, LAST = 600, 1024 # Range of ports to try
322 if last_resv_port_tried == None:
323 import os
324 last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST)
325 for i in range(last_resv_port_tried, LAST) + \
326 range(FIRST, last_resv_port_tried):
327 last_resv_port_tried = i
328 try:
329 sock.bind((host, i))
330 return last_resv_port_tried
331 except socket.error, (errno, msg):
332 if errno <> 114:
333 raise socket.error, (errno, msg)
334 raise RuntimeError, 'can\'t assign reserved port'
335
336
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000337# Client using TCP to a specific port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000338
339class RawTCPClient(Client):
340
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000341 def makesocket(self):
342 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000343
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000344 def do_call(self):
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000345 call = self.packer.get_buf()
346 sendrecord(self.sock, call)
347 reply = recvrecord(self.sock)
348 u = self.unpacker
349 u.reset(reply)
350 xid, verf = u.unpack_replyheader()
351 if xid <> self.lastxid:
352 # Can't really happen since this is TCP...
353 raise RuntimeError, 'wrong xid in reply ' + `xid` + \
354 ' instead of ' + `self.lastxid`
355
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000356
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000357# Client using UDP to a specific port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000358
359class RawUDPClient(Client):
360
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000361 def makesocket(self):
362 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000363
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000364 def do_call(self):
365 call = self.packer.get_buf()
366 self.sock.send(call)
Guido van Rossuma5854441992-12-17 17:31:58 +0000367 try:
368 from select import select
369 except ImportError:
370 print 'WARNING: select not found, RPC may hang'
371 select = None
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000372 BUFSIZE = 8192 # Max UDP buffer size
Guido van Rossum16b22191992-12-15 21:44:31 +0000373 timeout = 1
374 count = 5
Guido van Rossum16b22191992-12-15 21:44:31 +0000375 while 1:
Guido van Rossuma5854441992-12-17 17:31:58 +0000376 r, w, x = [self.sock], [], []
377 if select:
378 r, w, x = select(r, w, x, timeout)
Guido van Rossum16b22191992-12-15 21:44:31 +0000379 if self.sock not in r:
380 count = count - 1
381 if count < 0: raise RuntimeError, 'timeout'
382 if timeout < 25: timeout = timeout *2
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000383## print 'RESEND', timeout, count
Guido van Rossum16b22191992-12-15 21:44:31 +0000384 self.sock.send(call)
385 continue
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000386 reply = self.sock.recv(BUFSIZE)
Guido van Rossum16b22191992-12-15 21:44:31 +0000387 u = self.unpacker
388 u.reset(reply)
389 xid, verf = u.unpack_replyheader()
390 if xid <> self.lastxid:
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000391## print 'BAD xid'
Guido van Rossum16b22191992-12-15 21:44:31 +0000392 continue
393 break
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000394
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000395
396# Client using UDP broadcast to a specific port
397
398class RawBroadcastUDPClient(RawUDPClient):
399
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000400 def __init__(self, bcastaddr, prog, vers, port):
401 RawUDPClient.__init__(self, bcastaddr, prog, vers, port)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000402 self.reply_handler = None
403 self.timeout = 30
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000404
405 def connsocket(self):
406 # Don't connect -- use sendto
Guido van Rossumee3de2a1995-10-04 18:39:03 +0000407 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000408
409 def set_reply_handler(self, reply_handler):
410 self.reply_handler = reply_handler
411
412 def set_timeout(self, timeout):
413 self.timeout = timeout # Use None for infinite timeout
414
415 def make_call(self, proc, args, pack_func, unpack_func):
416 if pack_func is None and args is not None:
417 raise TypeError, 'non-null args with null pack_func'
418 self.start_call(proc)
419 if pack_func:
420 pack_func(args)
421 call = self.packer.get_buf()
422 self.sock.sendto(call, (self.host, self.port))
423 try:
424 from select import select
425 except ImportError:
426 print 'WARNING: select not found, broadcast will hang'
427 select = None
428 BUFSIZE = 8192 # Max UDP buffer size (for reply)
429 replies = []
430 if unpack_func is None:
431 def dummy(): pass
432 unpack_func = dummy
433 while 1:
434 r, w, x = [self.sock], [], []
435 if select:
436 if self.timeout is None:
437 r, w, x = select(r, w, x)
438 else:
439 r, w, x = select(r, w, x, self.timeout)
440 if self.sock not in r:
441 break
442 reply, fromaddr = self.sock.recvfrom(BUFSIZE)
443 u = self.unpacker
444 u.reset(reply)
445 xid, verf = u.unpack_replyheader()
446 if xid <> self.lastxid:
447## print 'BAD xid'
448 continue
449 reply = unpack_func()
450 self.unpacker.done()
451 replies.append((reply, fromaddr))
452 if self.reply_handler:
453 self.reply_handler(reply, fromaddr)
454 return replies
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000455
456
457# Port mapper interface
458
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000459# Program number, version and (fixed!) port number
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000460PMAP_PROG = 100000
461PMAP_VERS = 2
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000462PMAP_PORT = 111
463
464# Procedure numbers
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000465PMAPPROC_NULL = 0 # (void) -> void
466PMAPPROC_SET = 1 # (mapping) -> bool
467PMAPPROC_UNSET = 2 # (mapping) -> bool
468PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int
469PMAPPROC_DUMP = 4 # (void) -> pmaplist
470PMAPPROC_CALLIT = 5 # (call_args) -> call_result
471
472# A mapping is (prog, vers, prot, port) and prot is one of:
473
474IPPROTO_TCP = 6
475IPPROTO_UDP = 17
476
477# A pmaplist is a variable-length list of mappings, as follows:
478# either (1, mapping, pmaplist) or (0).
479
480# A call_args is (prog, vers, proc, args) where args is opaque;
481# a call_result is (port, res) where res is opaque.
482
483
484class PortMapperPacker(Packer):
485
486 def pack_mapping(self, mapping):
487 prog, vers, prot, port = mapping
488 self.pack_uint(prog)
489 self.pack_uint(vers)
490 self.pack_uint(prot)
491 self.pack_uint(port)
492
493 def pack_pmaplist(self, list):
494 self.pack_list(list, self.pack_mapping)
495
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000496 def pack_call_args(self, ca):
497 prog, vers, proc, args = ca
498 self.pack_uint(prog)
499 self.pack_uint(vers)
500 self.pack_uint(proc)
501 self.pack_opaque(args)
502
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000503
504class PortMapperUnpacker(Unpacker):
505
506 def unpack_mapping(self):
507 prog = self.unpack_uint()
508 vers = self.unpack_uint()
509 prot = self.unpack_uint()
510 port = self.unpack_uint()
511 return prog, vers, prot, port
512
513 def unpack_pmaplist(self):
514 return self.unpack_list(self.unpack_mapping)
515
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000516 def unpack_call_result(self):
517 port = self.unpack_uint()
518 res = self.unpack_opaque()
519 return port, res
520
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000521
522class PartialPortMapperClient:
523
524 def addpackers(self):
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000525 self.packer = PortMapperPacker()
526 self.unpacker = PortMapperUnpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000527
Guido van Rossum424c6731992-12-19 00:05:55 +0000528 def Set(self, mapping):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000529 return self.make_call(PMAPPROC_SET, mapping, \
530 self.packer.pack_mapping, \
531 self.unpacker.unpack_uint)
Guido van Rossum424c6731992-12-19 00:05:55 +0000532
533 def Unset(self, mapping):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000534 return self.make_call(PMAPPROC_UNSET, mapping, \
535 self.packer.pack_mapping, \
536 self.unpacker.unpack_uint)
Guido van Rossum424c6731992-12-19 00:05:55 +0000537
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000538 def Getport(self, mapping):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000539 return self.make_call(PMAPPROC_GETPORT, mapping, \
540 self.packer.pack_mapping, \
541 self.unpacker.unpack_uint)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000542
543 def Dump(self):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000544 return self.make_call(PMAPPROC_DUMP, None, \
545 None, \
546 self.unpacker.unpack_pmaplist)
547
548 def Callit(self, ca):
549 return self.make_call(PMAPPROC_CALLIT, ca, \
550 self.packer.pack_call_args, \
551 self.unpacker.unpack_call_result)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000552
553
554class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
555
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000556 def __init__(self, host):
557 RawTCPClient.__init__(self, \
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000558 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
559
560
561class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
562
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000563 def __init__(self, host):
564 RawUDPClient.__init__(self, \
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000565 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
566
567
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000568class BroadcastUDPPortMapperClient(PartialPortMapperClient, \
569 RawBroadcastUDPClient):
570
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000571 def __init__(self, bcastaddr):
572 RawBroadcastUDPClient.__init__(self, \
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000573 bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
574
575
576# Generic clients that find their server through the Port mapper
577
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000578class TCPClient(RawTCPClient):
579
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000580 def __init__(self, host, prog, vers):
581 pmap = TCPPortMapperClient(host)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000582 port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000583 pmap.close()
Guido van Rossum424c6731992-12-19 00:05:55 +0000584 if port == 0:
585 raise RuntimeError, 'program not registered'
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000586 RawTCPClient.__init__(self, host, prog, vers, port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000587
588
589class UDPClient(RawUDPClient):
590
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000591 def __init__(self, host, prog, vers):
592 pmap = UDPPortMapperClient(host)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000593 port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
594 pmap.close()
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000595 if port == 0:
596 raise RuntimeError, 'program not registered'
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000597 RawUDPClient.__init__(self, host, prog, vers, port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000598
599
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000600class BroadcastUDPClient(Client):
601
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000602 def __init__(self, bcastaddr, prog, vers):
603 self.pmap = BroadcastUDPPortMapperClient(bcastaddr)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000604 self.pmap.set_reply_handler(self.my_reply_handler)
605 self.prog = prog
606 self.vers = vers
607 self.user_reply_handler = None
608 self.addpackers()
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000609
610 def close(self):
611 self.pmap.close()
612
613 def set_reply_handler(self, reply_handler):
614 self.user_reply_handler = reply_handler
615
616 def set_timeout(self, timeout):
617 self.pmap.set_timeout(timeout)
618
619 def my_reply_handler(self, reply, fromaddr):
620 port, res = reply
621 self.unpacker.reset(res)
622 result = self.unpack_func()
623 self.unpacker.done()
624 self.replies.append((result, fromaddr))
625 if self.user_reply_handler is not None:
626 self.user_reply_handler(result, fromaddr)
627
628 def make_call(self, proc, args, pack_func, unpack_func):
629 self.packer.reset()
630 if pack_func:
631 pack_func(args)
632 if unpack_func is None:
633 def dummy(): pass
634 self.unpack_func = dummy
635 else:
636 self.unpack_func = unpack_func
637 self.replies = []
638 packed_args = self.packer.get_buf()
639 dummy_replies = self.pmap.Callit( \
640 (self.prog, self.vers, proc, packed_args))
641 return self.replies
642
643
Guido van Rossum424c6731992-12-19 00:05:55 +0000644# Server classes
645
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000646# These are not symmetric to the Client classes
647# XXX No attempt is made to provide authorization hooks yet
648
Guido van Rossum424c6731992-12-19 00:05:55 +0000649class Server:
650
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000651 def __init__(self, host, prog, vers, port):
Guido van Rossum424c6731992-12-19 00:05:55 +0000652 self.host = host # Should normally be '' for default interface
653 self.prog = prog
654 self.vers = vers
655 self.port = port # Should normally be 0 for random port
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000656 self.makesocket() # Assigns to self.sock and self.prot
657 self.bindsocket()
Guido van Rossum424c6731992-12-19 00:05:55 +0000658 self.host, self.port = self.sock.getsockname()
659 self.addpackers()
Guido van Rossum424c6731992-12-19 00:05:55 +0000660
661 def register(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000662 mapping = self.prog, self.vers, self.prot, self.port
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000663 p = TCPPortMapperClient(self.host)
Guido van Rossum424c6731992-12-19 00:05:55 +0000664 if not p.Set(mapping):
665 raise RuntimeError, 'register failed'
666
667 def unregister(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000668 mapping = self.prog, self.vers, self.prot, self.port
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000669 p = TCPPortMapperClient(self.host)
Guido van Rossum424c6731992-12-19 00:05:55 +0000670 if not p.Unset(mapping):
671 raise RuntimeError, 'unregister failed'
672
673 def handle(self, call):
674 # Don't use unpack_header but parse the header piecewise
675 # XXX I have no idea if I am using the right error responses!
676 self.unpacker.reset(call)
677 self.packer.reset()
678 xid = self.unpacker.unpack_uint()
679 self.packer.pack_uint(xid)
680 temp = self.unpacker.unpack_enum()
681 if temp <> CALL:
682 return None # Not worthy of a reply
683 self.packer.pack_uint(REPLY)
684 temp = self.unpacker.unpack_uint()
685 if temp <> RPCVERSION:
686 self.packer.pack_uint(MSG_DENIED)
687 self.packer.pack_uint(RPC_MISMATCH)
688 self.packer.pack_uint(RPCVERSION)
689 self.packer.pack_uint(RPCVERSION)
690 return self.packer.get_buf()
691 self.packer.pack_uint(MSG_ACCEPTED)
692 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
693 prog = self.unpacker.unpack_uint()
694 if prog <> self.prog:
695 self.packer.pack_uint(PROG_UNAVAIL)
696 return self.packer.get_buf()
697 vers = self.unpacker.unpack_uint()
698 if vers <> self.vers:
699 self.packer.pack_uint(PROG_MISMATCH)
700 self.packer.pack_uint(self.vers)
701 self.packer.pack_uint(self.vers)
702 return self.packer.get_buf()
703 proc = self.unpacker.unpack_uint()
704 methname = 'handle_' + `proc`
705 try:
706 meth = getattr(self, methname)
707 except AttributeError:
708 self.packer.pack_uint(PROC_UNAVAIL)
709 return self.packer.get_buf()
710 cred = self.unpacker.unpack_auth()
711 verf = self.unpacker.unpack_auth()
712 try:
713 meth() # Unpack args, call turn_around(), pack reply
714 except (EOFError, GarbageArgs):
715 # Too few or too many arguments
716 self.packer.reset()
717 self.packer.pack_uint(xid)
718 self.packer.pack_uint(REPLY)
719 self.packer.pack_uint(MSG_ACCEPTED)
720 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
721 self.packer.pack_uint(GARBAGE_ARGS)
722 return self.packer.get_buf()
723
724 def turn_around(self):
725 try:
726 self.unpacker.done()
727 except RuntimeError:
728 raise GarbageArgs
729 self.packer.pack_uint(SUCCESS)
730
731 def handle_0(self): # Handle NULL message
732 self.turn_around()
733
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000734 def makesocket(self):
735 # This MUST be overridden
736 raise RuntimeError, 'makesocket not defined'
737
738 def bindsocket(self):
739 # Override this to bind to a different port (e.g. reserved)
740 self.sock.bind((self.host, self.port))
Guido van Rossum424c6731992-12-19 00:05:55 +0000741
742 def addpackers(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000743 # Override this to use derived classes from Packer/Unpacker
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000744 self.packer = Packer()
745 self.unpacker = Unpacker('')
Guido van Rossum424c6731992-12-19 00:05:55 +0000746
747
748class TCPServer(Server):
749
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000750 def makesocket(self):
751 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
752 self.prot = IPPROTO_TCP
Guido van Rossum424c6731992-12-19 00:05:55 +0000753
754 def loop(self):
755 self.sock.listen(0)
756 while 1:
757 self.session(self.sock.accept())
758
759 def session(self, connection):
760 sock, (host, port) = connection
761 while 1:
762 try:
763 call = recvrecord(sock)
764 except EOFError:
765 break
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000766 except socket.error, msg:
767 print 'socket error:', msg
768 break
Guido van Rossum424c6731992-12-19 00:05:55 +0000769 reply = self.handle(call)
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000770 if reply is not None:
Guido van Rossum424c6731992-12-19 00:05:55 +0000771 sendrecord(sock, reply)
772
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000773 def forkingloop(self):
774 # Like loop but uses forksession()
775 self.sock.listen(0)
776 while 1:
777 self.forksession(self.sock.accept())
778
779 def forksession(self, connection):
780 # Like session but forks off a subprocess
781 import os
782 # Wait for deceased children
783 try:
784 while 1:
785 pid, sts = os.waitpid(0, 1)
786 except os.error:
787 pass
788 pid = None
789 try:
790 pid = os.fork()
791 if pid: # Parent
792 connection[0].close()
793 return
794 # Child
795 self.session(connection)
796 finally:
797 # Make sure we don't fall through in the parent
798 if pid == 0:
799 os._exit(0)
800
Guido van Rossum424c6731992-12-19 00:05:55 +0000801
802class UDPServer(Server):
803
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000804 def makesocket(self):
805 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
806 self.prot = IPPROTO_UDP
Guido van Rossum424c6731992-12-19 00:05:55 +0000807
808 def loop(self):
809 while 1:
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000810 self.session()
Guido van Rossum424c6731992-12-19 00:05:55 +0000811
812 def session(self):
813 call, host_port = self.sock.recvfrom(8192)
814 reply = self.handle(call)
815 if reply <> None:
816 self.sock.sendto(reply, host_port)
817
818
819# Simple test program -- dump local portmapper status
820
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000821def test():
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000822 pmap = UDPPortMapperClient('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000823 list = pmap.Dump()
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000824 list.sort()
825 for prog, vers, prot, port in list:
826 print prog, vers,
827 if prot == IPPROTO_TCP: print 'tcp',
828 elif prot == IPPROTO_UDP: print 'udp',
829 else: print prot,
830 print port
Guido van Rossum424c6731992-12-19 00:05:55 +0000831
832
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000833# Test program for broadcast operation -- dump everybody's portmapper status
834
835def testbcast():
836 import sys
837 if sys.argv[1:]:
838 bcastaddr = sys.argv[1]
839 else:
840 bcastaddr = '<broadcast>'
841 def rh(reply, fromaddr):
842 host, port = fromaddr
843 print host + '\t' + `reply`
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000844 pmap = BroadcastUDPPortMapperClient(bcastaddr)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000845 pmap.set_reply_handler(rh)
846 pmap.set_timeout(5)
847 replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0))
848
849
850# Test program for server, with corresponding client
Guido van Rossum424c6731992-12-19 00:05:55 +0000851# On machine A: python -c 'import rpc; rpc.testsvr()'
852# On machine B: python -c 'import rpc; rpc.testclt()' A
853# (A may be == B)
854
855def testsvr():
856 # Simple test class -- proc 1 doubles its string argument as reply
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000857 class S(UDPServer):
Guido van Rossum424c6731992-12-19 00:05:55 +0000858 def handle_1(self):
859 arg = self.unpacker.unpack_string()
860 self.turn_around()
861 print 'RPC function 1 called, arg', `arg`
862 self.packer.pack_string(arg + arg)
863 #
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000864 s = S('', 0x20000000, 1, 0)
Guido van Rossum424c6731992-12-19 00:05:55 +0000865 try:
866 s.unregister()
867 except RuntimeError, msg:
868 print 'RuntimeError:', msg, '(ignored)'
869 s.register()
870 print 'Service started...'
871 try:
872 s.loop()
873 finally:
874 s.unregister()
875 print 'Service interrupted.'
876
877
878def testclt():
879 import sys
880 if sys.argv[1:]: host = sys.argv[1]
881 else: host = ''
882 # Client for above server
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000883 class C(UDPClient):
Guido van Rossum424c6731992-12-19 00:05:55 +0000884 def call_1(self, arg):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000885 return self.make_call(1, arg, \
886 self.packer.pack_string, \
887 self.unpacker.unpack_string)
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000888 c = C(host, 0x20000000, 1)
Guido van Rossum424c6731992-12-19 00:05:55 +0000889 print 'making call...'
890 reply = c.call_1('hello, world, ')
891 print 'call returned', `reply`