blob: 83cc954b3d976ae361c88748670fa3e76ecd7d8c [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 Rossum9e80d6f1993-12-17 14:32:26 +0000166 return make_auth_unix(int(time.time()), \
167 socket.gethostname(), uid, gid, [])
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000168
169
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000170# Common base class for clients
171
172class Client:
173
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000174 def __init__(self, host, prog, vers, port):
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000175 self.host = host
176 self.prog = prog
177 self.vers = vers
178 self.port = port
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000179 self.makesocket() # Assigns to self.sock
180 self.bindsocket()
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000181 self.connsocket()
182 self.lastxid = 0 # XXX should be more random?
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000183 self.addpackers()
184 self.cred = None
185 self.verf = None
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000186
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000187 def close(self):
188 self.sock.close()
189
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000190 def makesocket(self):
191 # This MUST be overridden
192 raise RuntimeError, 'makesocket not defined'
193
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000194 def connsocket(self):
195 # Override this if you don't want/need a connection
196 self.sock.connect((self.host, self.port))
197
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000198 def bindsocket(self):
199 # Override this to bind to a different port (e.g. reserved)
200 self.sock.bind(('', 0))
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000201
202 def addpackers(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000203 # Override this to use derived classes from Packer/Unpacker
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000204 self.packer = Packer()
205 self.unpacker = Unpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000206
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000207 def make_call(self, proc, args, pack_func, unpack_func):
208 # Don't normally override this (but see Broadcast)
209 if pack_func is None and args is not None:
210 raise TypeError, 'non-null args with null pack_func'
211 self.start_call(proc)
212 if pack_func:
213 pack_func(args)
214 self.do_call()
215 if unpack_func:
216 result = unpack_func()
217 else:
218 result = None
219 self.unpacker.done()
220 return result
221
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000222 def start_call(self, proc):
223 # Don't override this
224 self.lastxid = xid = self.lastxid + 1
225 cred = self.mkcred()
226 verf = self.mkverf()
227 p = self.packer
228 p.reset()
229 p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
230
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000231 def do_call(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000232 # This MUST be overridden
233 raise RuntimeError, 'do_call not defined'
234
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000235 def mkcred(self):
236 # Override this to use more powerful credentials
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000237 if self.cred == None:
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000238 self.cred = (AUTH_NULL, make_auth_null())
239 return self.cred
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000240
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000241 def mkverf(self):
242 # Override this to use a more powerful verifier
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000243 if self.verf == None:
244 self.verf = (AUTH_NULL, make_auth_null())
245 return self.verf
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000246
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000247 def call_0(self): # Procedure 0 is always like this
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000248 return self.make_call(0, None, None, None)
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000249
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000250
251# Record-Marking standard support
252
253def sendfrag(sock, last, frag):
254 x = len(frag)
255 if last: x = x | 0x80000000L
256 header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
257 chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
258 sock.send(header + frag)
259
260def sendrecord(sock, record):
261 sendfrag(sock, 1, record)
262
263def recvfrag(sock):
264 header = sock.recv(4)
Guido van Rossum424c6731992-12-19 00:05:55 +0000265 if len(header) < 4:
266 raise EOFError
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000267 x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
268 ord(header[2])<<8 | ord(header[3])
269 last = ((x & 0x80000000) != 0)
270 n = int(x & 0x7fffffff)
271 frag = ''
272 while n > 0:
273 buf = sock.recv(n)
274 if not buf: raise EOFError
275 n = n - len(buf)
276 frag = frag + buf
277 return last, frag
278
279def recvrecord(sock):
280 record = ''
281 last = 0
282 while not last:
283 last, frag = recvfrag(sock)
284 record = record + frag
285 return record
286
287
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000288# Try to bind to a reserved port (must be root)
289
290last_resv_port_tried = None
291def bindresvport(sock, host):
292 global last_resv_port_tried
293 FIRST, LAST = 600, 1024 # Range of ports to try
294 if last_resv_port_tried == None:
295 import os
296 last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST)
297 for i in range(last_resv_port_tried, LAST) + \
298 range(FIRST, last_resv_port_tried):
299 last_resv_port_tried = i
300 try:
301 sock.bind((host, i))
302 return last_resv_port_tried
303 except socket.error, (errno, msg):
304 if errno <> 114:
305 raise socket.error, (errno, msg)
306 raise RuntimeError, 'can\'t assign reserved port'
307
308
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000309# Client using TCP to a specific port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000310
311class RawTCPClient(Client):
312
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000313 def makesocket(self):
314 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000315
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000316 def do_call(self):
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000317 call = self.packer.get_buf()
318 sendrecord(self.sock, call)
319 reply = recvrecord(self.sock)
320 u = self.unpacker
321 u.reset(reply)
322 xid, verf = u.unpack_replyheader()
323 if xid <> self.lastxid:
324 # Can't really happen since this is TCP...
325 raise RuntimeError, 'wrong xid in reply ' + `xid` + \
326 ' instead of ' + `self.lastxid`
327
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000328
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000329# Client using UDP to a specific port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000330
331class RawUDPClient(Client):
332
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000333 def makesocket(self):
334 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000335
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000336 def do_call(self):
337 call = self.packer.get_buf()
338 self.sock.send(call)
Guido van Rossuma5854441992-12-17 17:31:58 +0000339 try:
340 from select import select
341 except ImportError:
342 print 'WARNING: select not found, RPC may hang'
343 select = None
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000344 BUFSIZE = 8192 # Max UDP buffer size
Guido van Rossum16b22191992-12-15 21:44:31 +0000345 timeout = 1
346 count = 5
Guido van Rossum16b22191992-12-15 21:44:31 +0000347 while 1:
Guido van Rossuma5854441992-12-17 17:31:58 +0000348 r, w, x = [self.sock], [], []
349 if select:
350 r, w, x = select(r, w, x, timeout)
Guido van Rossum16b22191992-12-15 21:44:31 +0000351 if self.sock not in r:
352 count = count - 1
353 if count < 0: raise RuntimeError, 'timeout'
354 if timeout < 25: timeout = timeout *2
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000355## print 'RESEND', timeout, count
Guido van Rossum16b22191992-12-15 21:44:31 +0000356 self.sock.send(call)
357 continue
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000358 reply = self.sock.recv(BUFSIZE)
Guido van Rossum16b22191992-12-15 21:44:31 +0000359 u = self.unpacker
360 u.reset(reply)
361 xid, verf = u.unpack_replyheader()
362 if xid <> self.lastxid:
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000363## print 'BAD xid'
Guido van Rossum16b22191992-12-15 21:44:31 +0000364 continue
365 break
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000366
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000367
368# Client using UDP broadcast to a specific port
369
370class RawBroadcastUDPClient(RawUDPClient):
371
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000372 def __init__(self, bcastaddr, prog, vers, port):
373 RawUDPClient.__init__(self, bcastaddr, prog, vers, port)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000374 self.reply_handler = None
375 self.timeout = 30
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000376
377 def connsocket(self):
378 # Don't connect -- use sendto
Guido van Rossumee3de2a1995-10-04 18:39:03 +0000379 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000380
381 def set_reply_handler(self, reply_handler):
382 self.reply_handler = reply_handler
383
384 def set_timeout(self, timeout):
385 self.timeout = timeout # Use None for infinite timeout
386
387 def make_call(self, proc, args, pack_func, unpack_func):
388 if pack_func is None and args is not None:
389 raise TypeError, 'non-null args with null pack_func'
390 self.start_call(proc)
391 if pack_func:
392 pack_func(args)
393 call = self.packer.get_buf()
394 self.sock.sendto(call, (self.host, self.port))
395 try:
396 from select import select
397 except ImportError:
398 print 'WARNING: select not found, broadcast will hang'
399 select = None
400 BUFSIZE = 8192 # Max UDP buffer size (for reply)
401 replies = []
402 if unpack_func is None:
403 def dummy(): pass
404 unpack_func = dummy
405 while 1:
406 r, w, x = [self.sock], [], []
407 if select:
408 if self.timeout is None:
409 r, w, x = select(r, w, x)
410 else:
411 r, w, x = select(r, w, x, self.timeout)
412 if self.sock not in r:
413 break
414 reply, fromaddr = self.sock.recvfrom(BUFSIZE)
415 u = self.unpacker
416 u.reset(reply)
417 xid, verf = u.unpack_replyheader()
418 if xid <> self.lastxid:
419## print 'BAD xid'
420 continue
421 reply = unpack_func()
422 self.unpacker.done()
423 replies.append((reply, fromaddr))
424 if self.reply_handler:
425 self.reply_handler(reply, fromaddr)
426 return replies
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000427
428
429# Port mapper interface
430
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000431# Program number, version and (fixed!) port number
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000432PMAP_PROG = 100000
433PMAP_VERS = 2
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000434PMAP_PORT = 111
435
436# Procedure numbers
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000437PMAPPROC_NULL = 0 # (void) -> void
438PMAPPROC_SET = 1 # (mapping) -> bool
439PMAPPROC_UNSET = 2 # (mapping) -> bool
440PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int
441PMAPPROC_DUMP = 4 # (void) -> pmaplist
442PMAPPROC_CALLIT = 5 # (call_args) -> call_result
443
444# A mapping is (prog, vers, prot, port) and prot is one of:
445
446IPPROTO_TCP = 6
447IPPROTO_UDP = 17
448
449# A pmaplist is a variable-length list of mappings, as follows:
450# either (1, mapping, pmaplist) or (0).
451
452# A call_args is (prog, vers, proc, args) where args is opaque;
453# a call_result is (port, res) where res is opaque.
454
455
456class PortMapperPacker(Packer):
457
458 def pack_mapping(self, mapping):
459 prog, vers, prot, port = mapping
460 self.pack_uint(prog)
461 self.pack_uint(vers)
462 self.pack_uint(prot)
463 self.pack_uint(port)
464
465 def pack_pmaplist(self, list):
466 self.pack_list(list, self.pack_mapping)
467
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000468 def pack_call_args(self, ca):
469 prog, vers, proc, args = ca
470 self.pack_uint(prog)
471 self.pack_uint(vers)
472 self.pack_uint(proc)
473 self.pack_opaque(args)
474
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000475
476class PortMapperUnpacker(Unpacker):
477
478 def unpack_mapping(self):
479 prog = self.unpack_uint()
480 vers = self.unpack_uint()
481 prot = self.unpack_uint()
482 port = self.unpack_uint()
483 return prog, vers, prot, port
484
485 def unpack_pmaplist(self):
486 return self.unpack_list(self.unpack_mapping)
487
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000488 def unpack_call_result(self):
489 port = self.unpack_uint()
490 res = self.unpack_opaque()
491 return port, res
492
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000493
494class PartialPortMapperClient:
495
496 def addpackers(self):
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000497 self.packer = PortMapperPacker()
498 self.unpacker = PortMapperUnpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000499
Guido van Rossum424c6731992-12-19 00:05:55 +0000500 def Set(self, mapping):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000501 return self.make_call(PMAPPROC_SET, mapping, \
502 self.packer.pack_mapping, \
503 self.unpacker.unpack_uint)
Guido van Rossum424c6731992-12-19 00:05:55 +0000504
505 def Unset(self, mapping):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000506 return self.make_call(PMAPPROC_UNSET, mapping, \
507 self.packer.pack_mapping, \
508 self.unpacker.unpack_uint)
Guido van Rossum424c6731992-12-19 00:05:55 +0000509
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000510 def Getport(self, mapping):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000511 return self.make_call(PMAPPROC_GETPORT, mapping, \
512 self.packer.pack_mapping, \
513 self.unpacker.unpack_uint)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000514
515 def Dump(self):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000516 return self.make_call(PMAPPROC_DUMP, None, \
517 None, \
518 self.unpacker.unpack_pmaplist)
519
520 def Callit(self, ca):
521 return self.make_call(PMAPPROC_CALLIT, ca, \
522 self.packer.pack_call_args, \
523 self.unpacker.unpack_call_result)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000524
525
526class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
527
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000528 def __init__(self, host):
529 RawTCPClient.__init__(self, \
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000530 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
531
532
533class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
534
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000535 def __init__(self, host):
536 RawUDPClient.__init__(self, \
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000537 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
538
539
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000540class BroadcastUDPPortMapperClient(PartialPortMapperClient, \
541 RawBroadcastUDPClient):
542
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000543 def __init__(self, bcastaddr):
544 RawBroadcastUDPClient.__init__(self, \
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000545 bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
546
547
548# Generic clients that find their server through the Port mapper
549
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000550class TCPClient(RawTCPClient):
551
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000552 def __init__(self, host, prog, vers):
553 pmap = TCPPortMapperClient(host)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000554 port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000555 pmap.close()
Guido van Rossum424c6731992-12-19 00:05:55 +0000556 if port == 0:
557 raise RuntimeError, 'program not registered'
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000558 RawTCPClient.__init__(self, host, prog, vers, port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000559
560
561class UDPClient(RawUDPClient):
562
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000563 def __init__(self, host, prog, vers):
564 pmap = UDPPortMapperClient(host)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000565 port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
566 pmap.close()
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000567 if port == 0:
568 raise RuntimeError, 'program not registered'
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000569 RawUDPClient.__init__(self, host, prog, vers, port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000570
571
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000572class BroadcastUDPClient(Client):
573
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000574 def __init__(self, bcastaddr, prog, vers):
575 self.pmap = BroadcastUDPPortMapperClient(bcastaddr)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000576 self.pmap.set_reply_handler(self.my_reply_handler)
577 self.prog = prog
578 self.vers = vers
579 self.user_reply_handler = None
580 self.addpackers()
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000581
582 def close(self):
583 self.pmap.close()
584
585 def set_reply_handler(self, reply_handler):
586 self.user_reply_handler = reply_handler
587
588 def set_timeout(self, timeout):
589 self.pmap.set_timeout(timeout)
590
591 def my_reply_handler(self, reply, fromaddr):
592 port, res = reply
593 self.unpacker.reset(res)
594 result = self.unpack_func()
595 self.unpacker.done()
596 self.replies.append((result, fromaddr))
597 if self.user_reply_handler is not None:
598 self.user_reply_handler(result, fromaddr)
599
600 def make_call(self, proc, args, pack_func, unpack_func):
601 self.packer.reset()
602 if pack_func:
603 pack_func(args)
604 if unpack_func is None:
605 def dummy(): pass
606 self.unpack_func = dummy
607 else:
608 self.unpack_func = unpack_func
609 self.replies = []
610 packed_args = self.packer.get_buf()
611 dummy_replies = self.pmap.Callit( \
612 (self.prog, self.vers, proc, packed_args))
613 return self.replies
614
615
Guido van Rossum424c6731992-12-19 00:05:55 +0000616# Server classes
617
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000618# These are not symmetric to the Client classes
619# XXX No attempt is made to provide authorization hooks yet
620
Guido van Rossum424c6731992-12-19 00:05:55 +0000621class Server:
622
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000623 def __init__(self, host, prog, vers, port):
Guido van Rossum424c6731992-12-19 00:05:55 +0000624 self.host = host # Should normally be '' for default interface
625 self.prog = prog
626 self.vers = vers
627 self.port = port # Should normally be 0 for random port
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000628 self.makesocket() # Assigns to self.sock and self.prot
629 self.bindsocket()
Guido van Rossum424c6731992-12-19 00:05:55 +0000630 self.host, self.port = self.sock.getsockname()
631 self.addpackers()
Guido van Rossum424c6731992-12-19 00:05:55 +0000632
633 def register(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000634 mapping = self.prog, self.vers, self.prot, self.port
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000635 p = TCPPortMapperClient(self.host)
Guido van Rossum424c6731992-12-19 00:05:55 +0000636 if not p.Set(mapping):
637 raise RuntimeError, 'register failed'
638
639 def unregister(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000640 mapping = self.prog, self.vers, self.prot, self.port
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000641 p = TCPPortMapperClient(self.host)
Guido van Rossum424c6731992-12-19 00:05:55 +0000642 if not p.Unset(mapping):
643 raise RuntimeError, 'unregister failed'
644
645 def handle(self, call):
646 # Don't use unpack_header but parse the header piecewise
647 # XXX I have no idea if I am using the right error responses!
648 self.unpacker.reset(call)
649 self.packer.reset()
650 xid = self.unpacker.unpack_uint()
651 self.packer.pack_uint(xid)
652 temp = self.unpacker.unpack_enum()
653 if temp <> CALL:
654 return None # Not worthy of a reply
655 self.packer.pack_uint(REPLY)
656 temp = self.unpacker.unpack_uint()
657 if temp <> RPCVERSION:
658 self.packer.pack_uint(MSG_DENIED)
659 self.packer.pack_uint(RPC_MISMATCH)
660 self.packer.pack_uint(RPCVERSION)
661 self.packer.pack_uint(RPCVERSION)
662 return self.packer.get_buf()
663 self.packer.pack_uint(MSG_ACCEPTED)
664 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
665 prog = self.unpacker.unpack_uint()
666 if prog <> self.prog:
667 self.packer.pack_uint(PROG_UNAVAIL)
668 return self.packer.get_buf()
669 vers = self.unpacker.unpack_uint()
670 if vers <> self.vers:
671 self.packer.pack_uint(PROG_MISMATCH)
672 self.packer.pack_uint(self.vers)
673 self.packer.pack_uint(self.vers)
674 return self.packer.get_buf()
675 proc = self.unpacker.unpack_uint()
676 methname = 'handle_' + `proc`
677 try:
678 meth = getattr(self, methname)
679 except AttributeError:
680 self.packer.pack_uint(PROC_UNAVAIL)
681 return self.packer.get_buf()
682 cred = self.unpacker.unpack_auth()
683 verf = self.unpacker.unpack_auth()
684 try:
685 meth() # Unpack args, call turn_around(), pack reply
686 except (EOFError, GarbageArgs):
687 # Too few or too many arguments
688 self.packer.reset()
689 self.packer.pack_uint(xid)
690 self.packer.pack_uint(REPLY)
691 self.packer.pack_uint(MSG_ACCEPTED)
692 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
693 self.packer.pack_uint(GARBAGE_ARGS)
694 return self.packer.get_buf()
695
696 def turn_around(self):
697 try:
698 self.unpacker.done()
699 except RuntimeError:
700 raise GarbageArgs
701 self.packer.pack_uint(SUCCESS)
702
703 def handle_0(self): # Handle NULL message
704 self.turn_around()
705
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000706 def makesocket(self):
707 # This MUST be overridden
708 raise RuntimeError, 'makesocket not defined'
709
710 def bindsocket(self):
711 # Override this to bind to a different port (e.g. reserved)
712 self.sock.bind((self.host, self.port))
Guido van Rossum424c6731992-12-19 00:05:55 +0000713
714 def addpackers(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000715 # Override this to use derived classes from Packer/Unpacker
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000716 self.packer = Packer()
717 self.unpacker = Unpacker('')
Guido van Rossum424c6731992-12-19 00:05:55 +0000718
719
720class TCPServer(Server):
721
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000722 def makesocket(self):
723 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
724 self.prot = IPPROTO_TCP
Guido van Rossum424c6731992-12-19 00:05:55 +0000725
726 def loop(self):
727 self.sock.listen(0)
728 while 1:
729 self.session(self.sock.accept())
730
731 def session(self, connection):
732 sock, (host, port) = connection
733 while 1:
734 try:
735 call = recvrecord(sock)
736 except EOFError:
737 break
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000738 except socket.error, msg:
739 print 'socket error:', msg
740 break
Guido van Rossum424c6731992-12-19 00:05:55 +0000741 reply = self.handle(call)
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000742 if reply is not None:
Guido van Rossum424c6731992-12-19 00:05:55 +0000743 sendrecord(sock, reply)
744
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000745 def forkingloop(self):
746 # Like loop but uses forksession()
747 self.sock.listen(0)
748 while 1:
749 self.forksession(self.sock.accept())
750
751 def forksession(self, connection):
752 # Like session but forks off a subprocess
753 import os
754 # Wait for deceased children
755 try:
756 while 1:
757 pid, sts = os.waitpid(0, 1)
758 except os.error:
759 pass
760 pid = None
761 try:
762 pid = os.fork()
763 if pid: # Parent
764 connection[0].close()
765 return
766 # Child
767 self.session(connection)
768 finally:
769 # Make sure we don't fall through in the parent
770 if pid == 0:
771 os._exit(0)
772
Guido van Rossum424c6731992-12-19 00:05:55 +0000773
774class UDPServer(Server):
775
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000776 def makesocket(self):
777 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
778 self.prot = IPPROTO_UDP
Guido van Rossum424c6731992-12-19 00:05:55 +0000779
780 def loop(self):
781 while 1:
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000782 self.session()
Guido van Rossum424c6731992-12-19 00:05:55 +0000783
784 def session(self):
785 call, host_port = self.sock.recvfrom(8192)
786 reply = self.handle(call)
787 if reply <> None:
788 self.sock.sendto(reply, host_port)
789
790
791# Simple test program -- dump local portmapper status
792
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000793def test():
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000794 pmap = UDPPortMapperClient('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000795 list = pmap.Dump()
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000796 list.sort()
797 for prog, vers, prot, port in list:
798 print prog, vers,
799 if prot == IPPROTO_TCP: print 'tcp',
800 elif prot == IPPROTO_UDP: print 'udp',
801 else: print prot,
802 print port
Guido van Rossum424c6731992-12-19 00:05:55 +0000803
804
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000805# Test program for broadcast operation -- dump everybody's portmapper status
806
807def testbcast():
808 import sys
809 if sys.argv[1:]:
810 bcastaddr = sys.argv[1]
811 else:
812 bcastaddr = '<broadcast>'
813 def rh(reply, fromaddr):
814 host, port = fromaddr
815 print host + '\t' + `reply`
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000816 pmap = BroadcastUDPPortMapperClient(bcastaddr)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000817 pmap.set_reply_handler(rh)
818 pmap.set_timeout(5)
819 replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0))
820
821
822# Test program for server, with corresponding client
Guido van Rossum424c6731992-12-19 00:05:55 +0000823# On machine A: python -c 'import rpc; rpc.testsvr()'
824# On machine B: python -c 'import rpc; rpc.testclt()' A
825# (A may be == B)
826
827def testsvr():
828 # Simple test class -- proc 1 doubles its string argument as reply
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000829 class S(UDPServer):
Guido van Rossum424c6731992-12-19 00:05:55 +0000830 def handle_1(self):
831 arg = self.unpacker.unpack_string()
832 self.turn_around()
833 print 'RPC function 1 called, arg', `arg`
834 self.packer.pack_string(arg + arg)
835 #
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000836 s = S('', 0x20000000, 1, 0)
Guido van Rossum424c6731992-12-19 00:05:55 +0000837 try:
838 s.unregister()
839 except RuntimeError, msg:
840 print 'RuntimeError:', msg, '(ignored)'
841 s.register()
842 print 'Service started...'
843 try:
844 s.loop()
845 finally:
846 s.unregister()
847 print 'Service interrupted.'
848
849
850def testclt():
851 import sys
852 if sys.argv[1:]: host = sys.argv[1]
853 else: host = ''
854 # Client for above server
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000855 class C(UDPClient):
Guido van Rossum424c6731992-12-19 00:05:55 +0000856 def call_1(self, arg):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000857 return self.make_call(1, arg, \
858 self.packer.pack_string, \
859 self.unpacker.unpack_string)
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000860 c = C(host, 0x20000000, 1)
Guido van Rossum424c6731992-12-19 00:05:55 +0000861 print 'making call...'
862 reply = c.call_1('hello, world, ')
863 print 'call returned', `reply`