blob: d1c2c5e599ec4898f6852c0a72a10221d27af199 [file] [log] [blame]
Guido van Rossume3cafbe1992-12-14 23:25:04 +00001# Implement (a subset of) Sun RPC, version 2 -- RFC1057.
2
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):
154 p = Packer().init()
155 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
166 return make_auth_unix(time.time(), socket.gethostname(), uid, gid, [])
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000167
168
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000169# Common base class for clients
170
171class Client:
172
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000173 def init(self, host, prog, vers, port):
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000174 self.host = host
175 self.prog = prog
176 self.vers = vers
177 self.port = port
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000178 self.makesocket() # Assigns to self.sock
179 self.bindsocket()
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000180 self.sock.connect((host, port))
181 self.lastxid = 0
182 self.addpackers()
183 self.cred = None
184 self.verf = None
185 return self
186
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
194 def bindsocket(self):
195 # Override this to bind to a different port (e.g. reserved)
196 self.sock.bind(('', 0))
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000197
198 def addpackers(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000199 # Override this to use derived classes from Packer/Unpacker
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000200 self.packer = Packer().init()
201 self.unpacker = Unpacker().init('')
202
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000203 def start_call(self, proc):
204 # Don't override this
205 self.lastxid = xid = self.lastxid + 1
206 cred = self.mkcred()
207 verf = self.mkverf()
208 p = self.packer
209 p.reset()
210 p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
211
212 def do_call(self, *rest):
213 # This MUST be overridden
214 raise RuntimeError, 'do_call not defined'
215
216 def end_call(self):
217 # Don't override this
218 self.unpacker.done()
219
220 def mkcred(self):
221 # Override this to use more powerful credentials
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000222 if self.cred == None:
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000223 self.cred = (AUTH_NULL, make_auth_null())
224 return self.cred
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000225
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000226 def mkverf(self):
227 # Override this to use a more powerful verifier
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000228 if self.verf == None:
229 self.verf = (AUTH_NULL, make_auth_null())
230 return self.verf
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000231
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000232 def Null(self): # Procedure 0 is always like this
233 self.start_call(0)
234 self.do_call(0)
235 self.end_call()
236
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000237
238# Record-Marking standard support
239
240def sendfrag(sock, last, frag):
241 x = len(frag)
242 if last: x = x | 0x80000000L
243 header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
244 chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
245 sock.send(header + frag)
246
247def sendrecord(sock, record):
248 sendfrag(sock, 1, record)
249
250def recvfrag(sock):
251 header = sock.recv(4)
Guido van Rossum424c6731992-12-19 00:05:55 +0000252 if len(header) < 4:
253 raise EOFError
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000254 x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
255 ord(header[2])<<8 | ord(header[3])
256 last = ((x & 0x80000000) != 0)
257 n = int(x & 0x7fffffff)
258 frag = ''
259 while n > 0:
260 buf = sock.recv(n)
261 if not buf: raise EOFError
262 n = n - len(buf)
263 frag = frag + buf
264 return last, frag
265
266def recvrecord(sock):
267 record = ''
268 last = 0
269 while not last:
270 last, frag = recvfrag(sock)
271 record = record + frag
272 return record
273
274
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000275# Try to bind to a reserved port (must be root)
276
277last_resv_port_tried = None
278def bindresvport(sock, host):
279 global last_resv_port_tried
280 FIRST, LAST = 600, 1024 # Range of ports to try
281 if last_resv_port_tried == None:
282 import os
283 last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST)
284 for i in range(last_resv_port_tried, LAST) + \
285 range(FIRST, last_resv_port_tried):
286 last_resv_port_tried = i
287 try:
288 sock.bind((host, i))
289 return last_resv_port_tried
290 except socket.error, (errno, msg):
291 if errno <> 114:
292 raise socket.error, (errno, msg)
293 raise RuntimeError, 'can\'t assign reserved port'
294
295
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000296# Raw TCP-based client
297
298class RawTCPClient(Client):
299
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000300 def makesocket(self):
301 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000302
303 def start_call(self, proc):
304 self.lastxid = xid = self.lastxid + 1
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000305 cred = self.mkcred()
306 verf = self.mkverf()
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000307 p = self.packer
308 p.reset()
309 p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
310
311 def do_call(self, *rest):
312 # rest is used for UDP buffer size; ignored for TCP
313 call = self.packer.get_buf()
314 sendrecord(self.sock, call)
315 reply = recvrecord(self.sock)
316 u = self.unpacker
317 u.reset(reply)
318 xid, verf = u.unpack_replyheader()
319 if xid <> self.lastxid:
320 # Can't really happen since this is TCP...
321 raise RuntimeError, 'wrong xid in reply ' + `xid` + \
322 ' instead of ' + `self.lastxid`
323
324 def end_call(self):
325 self.unpacker.done()
326
327
328# Raw UDP-based client
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000329
330class RawUDPClient(Client):
331
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000332 def makesocket(self):
333 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000334
335 def start_call(self, proc):
336 self.lastxid = xid = self.lastxid + 1
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000337 cred = self.mkcred()
338 verf = self.mkverf()
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000339 p = self.packer
340 p.reset()
341 p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
342
343 def do_call(self, *rest):
Guido van Rossuma5854441992-12-17 17:31:58 +0000344 try:
345 from select import select
346 except ImportError:
347 print 'WARNING: select not found, RPC may hang'
348 select = None
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000349 if len(rest) == 0:
350 bufsize = 8192
351 elif len(rest) > 1:
352 raise TypeError, 'too many args'
353 else:
354 bufsize = rest[0] + 512
355 call = self.packer.get_buf()
Guido van Rossum16b22191992-12-15 21:44:31 +0000356 timeout = 1
357 count = 5
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000358 self.sock.send(call)
Guido van Rossum16b22191992-12-15 21:44:31 +0000359 while 1:
Guido van Rossuma5854441992-12-17 17:31:58 +0000360 r, w, x = [self.sock], [], []
361 if select:
362 r, w, x = select(r, w, x, timeout)
Guido van Rossum16b22191992-12-15 21:44:31 +0000363 if self.sock not in r:
364 count = count - 1
365 if count < 0: raise RuntimeError, 'timeout'
366 if timeout < 25: timeout = timeout *2
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000367## print 'RESEND', timeout, count
Guido van Rossum16b22191992-12-15 21:44:31 +0000368 self.sock.send(call)
369 continue
370 reply = self.sock.recv(bufsize)
371 u = self.unpacker
372 u.reset(reply)
373 xid, verf = u.unpack_replyheader()
374 if xid <> self.lastxid:
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000375## print 'BAD xid'
Guido van Rossum16b22191992-12-15 21:44:31 +0000376 continue
377 break
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000378
379 def end_call(self):
380 self.unpacker.done()
381
382
383# Port mapper interface
384
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000385# XXX CALLIT is not implemented
386
387# Program number, version and (fixed!) port number
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000388PMAP_PROG = 100000
389PMAP_VERS = 2
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000390PMAP_PORT = 111
391
392# Procedure numbers
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000393PMAPPROC_NULL = 0 # (void) -> void
394PMAPPROC_SET = 1 # (mapping) -> bool
395PMAPPROC_UNSET = 2 # (mapping) -> bool
396PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int
397PMAPPROC_DUMP = 4 # (void) -> pmaplist
398PMAPPROC_CALLIT = 5 # (call_args) -> call_result
399
400# A mapping is (prog, vers, prot, port) and prot is one of:
401
402IPPROTO_TCP = 6
403IPPROTO_UDP = 17
404
405# A pmaplist is a variable-length list of mappings, as follows:
406# either (1, mapping, pmaplist) or (0).
407
408# A call_args is (prog, vers, proc, args) where args is opaque;
409# a call_result is (port, res) where res is opaque.
410
411
412class PortMapperPacker(Packer):
413
414 def pack_mapping(self, mapping):
415 prog, vers, prot, port = mapping
416 self.pack_uint(prog)
417 self.pack_uint(vers)
418 self.pack_uint(prot)
419 self.pack_uint(port)
420
421 def pack_pmaplist(self, list):
422 self.pack_list(list, self.pack_mapping)
423
424
425class PortMapperUnpacker(Unpacker):
426
427 def unpack_mapping(self):
428 prog = self.unpack_uint()
429 vers = self.unpack_uint()
430 prot = self.unpack_uint()
431 port = self.unpack_uint()
432 return prog, vers, prot, port
433
434 def unpack_pmaplist(self):
435 return self.unpack_list(self.unpack_mapping)
436
437
438class PartialPortMapperClient:
439
440 def addpackers(self):
441 self.packer = PortMapperPacker().init()
442 self.unpacker = PortMapperUnpacker().init('')
443
Guido van Rossum424c6731992-12-19 00:05:55 +0000444 def Set(self, mapping):
445 self.start_call(PMAPPROC_SET)
446 self.packer.pack_mapping(mapping)
447 self.do_call()
448 res = self.unpacker.unpack_uint()
449 self.end_call()
450 return res
451
452 def Unset(self, mapping):
453 self.start_call(PMAPPROC_UNSET)
454 self.packer.pack_mapping(mapping)
455 self.do_call()
456 res = self.unpacker.unpack_uint()
457 self.end_call()
458 return res
459
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000460 def Getport(self, mapping):
461 self.start_call(PMAPPROC_GETPORT)
462 self.packer.pack_mapping(mapping)
463 self.do_call(4)
464 port = self.unpacker.unpack_uint()
465 self.end_call()
466 return port
467
468 def Dump(self):
469 self.start_call(PMAPPROC_DUMP)
470 self.do_call(8192-512)
471 list = self.unpacker.unpack_pmaplist()
472 self.end_call()
473 return list
474
475
476class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
477
478 def init(self, host):
479 return RawTCPClient.init(self, \
480 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
481
482
483class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
484
485 def init(self, host):
486 return RawUDPClient.init(self, \
487 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
488
489
490class TCPClient(RawTCPClient):
491
492 def init(self, host, prog, vers):
493 pmap = TCPPortMapperClient().init(host)
494 port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000495 pmap.close()
Guido van Rossum424c6731992-12-19 00:05:55 +0000496 if port == 0:
497 raise RuntimeError, 'program not registered'
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000498 return RawTCPClient.init(self, host, prog, vers, port)
499
500
501class UDPClient(RawUDPClient):
502
503 def init(self, host, prog, vers):
504 pmap = UDPPortMapperClient().init(host)
505 port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
506 pmap.close()
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000507 if port == 0:
508 raise RuntimeError, 'program not registered'
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000509 return RawUDPClient.init(self, host, prog, vers, port)
510
511
Guido van Rossum424c6731992-12-19 00:05:55 +0000512# Server classes
513
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000514# These are not symmetric to the Client classes
515# XXX No attempt is made to provide authorization hooks yet
516
Guido van Rossum424c6731992-12-19 00:05:55 +0000517class Server:
518
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000519 def init(self, host, prog, vers, port):
Guido van Rossum424c6731992-12-19 00:05:55 +0000520 self.host = host # Should normally be '' for default interface
521 self.prog = prog
522 self.vers = vers
523 self.port = port # Should normally be 0 for random port
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000524 self.makesocket() # Assigns to self.sock and self.prot
525 self.bindsocket()
Guido van Rossum424c6731992-12-19 00:05:55 +0000526 self.host, self.port = self.sock.getsockname()
527 self.addpackers()
528 return self
529
530 def register(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000531 mapping = self.prog, self.vers, self.prot, self.port
Guido van Rossum424c6731992-12-19 00:05:55 +0000532 p = TCPPortMapperClient().init(self.host)
533 if not p.Set(mapping):
534 raise RuntimeError, 'register failed'
535
536 def unregister(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000537 mapping = self.prog, self.vers, self.prot, self.port
Guido van Rossum424c6731992-12-19 00:05:55 +0000538 p = TCPPortMapperClient().init(self.host)
539 if not p.Unset(mapping):
540 raise RuntimeError, 'unregister failed'
541
542 def handle(self, call):
543 # Don't use unpack_header but parse the header piecewise
544 # XXX I have no idea if I am using the right error responses!
545 self.unpacker.reset(call)
546 self.packer.reset()
547 xid = self.unpacker.unpack_uint()
548 self.packer.pack_uint(xid)
549 temp = self.unpacker.unpack_enum()
550 if temp <> CALL:
551 return None # Not worthy of a reply
552 self.packer.pack_uint(REPLY)
553 temp = self.unpacker.unpack_uint()
554 if temp <> RPCVERSION:
555 self.packer.pack_uint(MSG_DENIED)
556 self.packer.pack_uint(RPC_MISMATCH)
557 self.packer.pack_uint(RPCVERSION)
558 self.packer.pack_uint(RPCVERSION)
559 return self.packer.get_buf()
560 self.packer.pack_uint(MSG_ACCEPTED)
561 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
562 prog = self.unpacker.unpack_uint()
563 if prog <> self.prog:
564 self.packer.pack_uint(PROG_UNAVAIL)
565 return self.packer.get_buf()
566 vers = self.unpacker.unpack_uint()
567 if vers <> self.vers:
568 self.packer.pack_uint(PROG_MISMATCH)
569 self.packer.pack_uint(self.vers)
570 self.packer.pack_uint(self.vers)
571 return self.packer.get_buf()
572 proc = self.unpacker.unpack_uint()
573 methname = 'handle_' + `proc`
574 try:
575 meth = getattr(self, methname)
576 except AttributeError:
577 self.packer.pack_uint(PROC_UNAVAIL)
578 return self.packer.get_buf()
579 cred = self.unpacker.unpack_auth()
580 verf = self.unpacker.unpack_auth()
581 try:
582 meth() # Unpack args, call turn_around(), pack reply
583 except (EOFError, GarbageArgs):
584 # Too few or too many arguments
585 self.packer.reset()
586 self.packer.pack_uint(xid)
587 self.packer.pack_uint(REPLY)
588 self.packer.pack_uint(MSG_ACCEPTED)
589 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
590 self.packer.pack_uint(GARBAGE_ARGS)
591 return self.packer.get_buf()
592
593 def turn_around(self):
594 try:
595 self.unpacker.done()
596 except RuntimeError:
597 raise GarbageArgs
598 self.packer.pack_uint(SUCCESS)
599
600 def handle_0(self): # Handle NULL message
601 self.turn_around()
602
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000603 def makesocket(self):
604 # This MUST be overridden
605 raise RuntimeError, 'makesocket not defined'
606
607 def bindsocket(self):
608 # Override this to bind to a different port (e.g. reserved)
609 self.sock.bind((self.host, self.port))
Guido van Rossum424c6731992-12-19 00:05:55 +0000610
611 def addpackers(self):
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000612 # Override this to use derived classes from Packer/Unpacker
Guido van Rossum424c6731992-12-19 00:05:55 +0000613 self.packer = Packer().init()
614 self.unpacker = Unpacker().init('')
615
616
617class TCPServer(Server):
618
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000619 def makesocket(self):
620 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
621 self.prot = IPPROTO_TCP
Guido van Rossum424c6731992-12-19 00:05:55 +0000622
623 def loop(self):
624 self.sock.listen(0)
625 while 1:
626 self.session(self.sock.accept())
627
628 def session(self, connection):
629 sock, (host, port) = connection
630 while 1:
631 try:
632 call = recvrecord(sock)
633 except EOFError:
634 break
635 reply = self.handle(call)
636 if reply <> None:
637 sendrecord(sock, reply)
638
639
640class UDPServer(Server):
641
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000642 def makesocket(self):
643 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
644 self.prot = IPPROTO_UDP
Guido van Rossum424c6731992-12-19 00:05:55 +0000645
646 def loop(self):
647 while 1:
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000648 self.session()
Guido van Rossum424c6731992-12-19 00:05:55 +0000649
650 def session(self):
651 call, host_port = self.sock.recvfrom(8192)
652 reply = self.handle(call)
653 if reply <> None:
654 self.sock.sendto(reply, host_port)
655
656
657# Simple test program -- dump local portmapper status
658
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000659def test():
660 import T
661 T.TSTART()
662 pmap = UDPPortMapperClient().init('')
663 T.TSTOP()
664 pmap.Null()
665 T.TSTOP()
666 list = pmap.Dump()
667 T.TSTOP()
668 list.sort()
669 for prog, vers, prot, port in list:
670 print prog, vers,
671 if prot == IPPROTO_TCP: print 'tcp',
672 elif prot == IPPROTO_UDP: print 'udp',
673 else: print prot,
674 print port
Guido van Rossum424c6731992-12-19 00:05:55 +0000675
676
677# Server and client test program.
678# On machine A: python -c 'import rpc; rpc.testsvr()'
679# On machine B: python -c 'import rpc; rpc.testclt()' A
680# (A may be == B)
681
682def testsvr():
683 # Simple test class -- proc 1 doubles its string argument as reply
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000684 class S(UDPServer):
Guido van Rossum424c6731992-12-19 00:05:55 +0000685 def handle_1(self):
686 arg = self.unpacker.unpack_string()
687 self.turn_around()
688 print 'RPC function 1 called, arg', `arg`
689 self.packer.pack_string(arg + arg)
690 #
691 s = S().init('', 0x20000000, 1, 0)
692 try:
693 s.unregister()
694 except RuntimeError, msg:
695 print 'RuntimeError:', msg, '(ignored)'
696 s.register()
697 print 'Service started...'
698 try:
699 s.loop()
700 finally:
701 s.unregister()
702 print 'Service interrupted.'
703
704
705def testclt():
706 import sys
707 if sys.argv[1:]: host = sys.argv[1]
708 else: host = ''
709 # Client for above server
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000710 class C(UDPClient):
Guido van Rossum424c6731992-12-19 00:05:55 +0000711 def call_1(self, arg):
712 self.start_call(1)
713 self.packer.pack_string(arg)
714 self.do_call()
715 reply = self.unpacker.unpack_string()
716 self.end_call()
717 return reply
718 c = C().init(host, 0x20000000, 1)
719 print 'making call...'
720 reply = c.call_1('hello, world, ')
721 print 'call returned', `reply`