Mercurial > ~darius > hgwebdir.cgi > pyinst
comparison rpc.py @ 19:cba1c44060f5
Copied from
http://sourceforge.net/projects/pythonlabtools/
I modified them slightly to work as something is causing pack_uint to
be called instead of pack_int...
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Thu, 11 Aug 2011 17:11:58 +0930 |
parents | |
children | 91b476ebc0f2 |
comparison
equal
deleted
inserted
replaced
18:9bb8a9f3df6b | 19:cba1c44060f5 |
---|---|
1 "A an RPC module, with optimizations for VXI-11 rpc calls" | |
2 _rcsid="$Id: rpc.py 323 2011-04-06 19:10:03Z marcus $" | |
3 | |
4 #this module is 99% from the python distribution, Demo/rpc/rpc.py, with a few modifications | |
5 #by Marcus Mendenhall to improve timeout handling, etc. It probably should follow any licensing | |
6 #intent of whoever originated it. | |
7 | |
8 # Sun RPC version 2 -- RFC1057. | |
9 | |
10 # XXX There should be separate exceptions for the various reasons why | |
11 # XXX an RPC can fail, rather than using RuntimeError for everything | |
12 | |
13 # XXX The UDP version of the protocol resends requests when it does | |
14 # XXX not receive a timely reply -- use only for idempotent calls! | |
15 | |
16 # XXX There is no provision for call timeout on TCP connections | |
17 # Now there is, on receives. 2003.02.20 Marcus Mendenhall, Vanderbilt University marcus.h.mendenhall@vanderbilt.edu | |
18 | |
19 import xdrlib as xdr | |
20 import socket | |
21 import os | |
22 from exceptions import * | |
23 | |
24 RPCVERSION = 2 | |
25 | |
26 CALL = 0 | |
27 REPLY = 1 | |
28 | |
29 AUTH_NULL = 0 | |
30 AUTH_UNIX = 1 | |
31 AUTH_SHORT = 2 | |
32 AUTH_DES = 3 | |
33 | |
34 MSG_ACCEPTED = 0 | |
35 MSG_DENIED = 1 | |
36 | |
37 SUCCESS = 0 # RPC executed successfully | |
38 PROG_UNAVAIL = 1 # remote hasn't exported program | |
39 PROG_MISMATCH = 2 # remote can't support version # | |
40 PROC_UNAVAIL = 3 # program can't support procedure | |
41 GARBAGE_ARGS = 4 # procedure can't decode params | |
42 | |
43 RPC_MISMATCH = 0 # RPC version number != 2 | |
44 AUTH_ERROR = 1 # remote can't authenticate caller | |
45 | |
46 AUTH_BADCRED = 1 # bad credentials (seal broken) | |
47 AUTH_REJECTEDCRED = 2 # client must begin new session | |
48 AUTH_BADVERF = 3 # bad verifier (seal broken) | |
49 AUTH_REJECTEDVERF = 4 # verifier expired or replayed | |
50 AUTH_TOOWEAK = 5 # rejected for security reasons | |
51 | |
52 | |
53 class Packer(xdr.Packer): | |
54 | |
55 def pack_auth(self, auth): | |
56 flavor, stuff = auth | |
57 self.pack_enum(flavor) | |
58 self.pack_opaque(stuff) | |
59 | |
60 def pack_auth_unix(self, stamp, machinename, uid, gid, gids): | |
61 self.pack_uint(stamp) | |
62 self.pack_string(machinename) | |
63 self.pack_uint(uid) | |
64 self.pack_uint(gid) | |
65 self.pack_uint(len(gids)) | |
66 for i in gids: | |
67 self.pack_uint(i) | |
68 | |
69 def pack_callheader(self, xid, prog, vers, proc, cred, verf): | |
70 self.pack_uint(xid) | |
71 self.pack_enum(CALL) | |
72 self.pack_uint(RPCVERSION) | |
73 self.pack_uint(prog) | |
74 self.pack_uint(vers) | |
75 self.pack_uint(proc) | |
76 self.pack_auth(cred) | |
77 self.pack_auth(verf) | |
78 # Caller must add procedure-specific part of call | |
79 | |
80 def pack_replyheader(self, xid, verf): | |
81 self.pack_uint(xid) | |
82 self.pack_enum(REPLY) | |
83 self.pack_uint(MSG_ACCEPTED) | |
84 self.pack_auth(verf) | |
85 self.pack_enum(SUCCESS) | |
86 # Caller must add procedure-specific part of reply | |
87 | |
88 | |
89 # Exceptions | |
90 BadRPCFormat = 'rpc.BadRPCFormat' | |
91 BadRPCVersion = 'rpc.BadRPCVersion' | |
92 GarbageArgs = 'rpc.GarbageArgs' | |
93 | |
94 class Unpacker(xdr.Unpacker): | |
95 | |
96 def unpack_auth(self): | |
97 flavor = self.unpack_enum() | |
98 stuff = self.unpack_opaque() | |
99 return (flavor, stuff) | |
100 | |
101 def unpack_callheader(self): | |
102 xid = self.unpack_uint(xid) | |
103 temp = self.unpack_enum() | |
104 if temp <> CALL: | |
105 raise BadRPCFormat, 'no CALL but ' + `temp` | |
106 temp = self.unpack_uint() | |
107 if temp <> RPCVERSION: | |
108 raise BadRPCVerspion, 'bad RPC version ' + `temp` | |
109 prog = self.unpack_uint() | |
110 vers = self.unpack_uint() | |
111 proc = self.unpack_uint() | |
112 cred = self.unpack_auth() | |
113 verf = self.unpack_auth() | |
114 return xid, prog, vers, proc, cred, verf | |
115 # Caller must add procedure-specific part of call | |
116 | |
117 def unpack_replyheader(self): | |
118 xid = self.unpack_uint() | |
119 mtype = self.unpack_enum() | |
120 if mtype <> REPLY: | |
121 raise RuntimeError, 'no REPLY but ' + `mtype` | |
122 stat = self.unpack_enum() | |
123 if stat == MSG_DENIED: | |
124 stat = self.unpack_enum() | |
125 if stat == RPC_MISMATCH: | |
126 low = self.unpack_uint() | |
127 high = self.unpack_uint() | |
128 raise RuntimeError, \ | |
129 'MSG_DENIED: RPC_MISMATCH: ' + `low, high` | |
130 if stat == AUTH_ERROR: | |
131 stat = self.unpack_uint() | |
132 raise RuntimeError, \ | |
133 'MSG_DENIED: AUTH_ERROR: ' + `stat` | |
134 raise RuntimeError, 'MSG_DENIED: ' + `stat` | |
135 if stat <> MSG_ACCEPTED: | |
136 raise RuntimeError, \ | |
137 'Neither MSG_DENIED nor MSG_ACCEPTED: ' + `stat` | |
138 verf = self.unpack_auth() | |
139 stat = self.unpack_enum() | |
140 if stat == PROG_UNAVAIL: | |
141 raise RuntimeError, 'call failed: PROG_UNAVAIL' | |
142 if stat == PROG_MISMATCH: | |
143 low = self.unpack_uint() | |
144 high = self.unpack_uint() | |
145 raise RuntimeError, \ | |
146 'call failed: PROG_MISMATCH: ' + `low, high` | |
147 if stat == PROC_UNAVAIL: | |
148 raise RuntimeError, 'call failed: PROC_UNAVAIL' | |
149 if stat == GARBAGE_ARGS: | |
150 raise RuntimeError, 'call failed: GARBAGE_ARGS' | |
151 if stat <> SUCCESS: | |
152 raise RuntimeError, 'call failed: ' + `stat` | |
153 return xid, verf | |
154 # Caller must get procedure-specific part of reply | |
155 | |
156 | |
157 # Subroutines to create opaque authentication objects | |
158 | |
159 def make_auth_null(): | |
160 return '' | |
161 | |
162 def make_auth_unix(seed, host, uid, gid, groups): | |
163 p = Packer() | |
164 p.pack_auth_unix(seed, host, uid, gid, groups) | |
165 return p.get_buf() | |
166 | |
167 def make_auth_unix_default(): | |
168 try: | |
169 from os import getuid, getgid | |
170 uid = getuid() | |
171 gid = getgid() | |
172 except ImportError: | |
173 uid = gid = 0 | |
174 import time | |
175 return make_auth_unix(int(time.time()-unix_epoch()), \ | |
176 socket.gethostname(), uid, gid, []) | |
177 | |
178 _unix_epoch = -1 | |
179 def unix_epoch(): | |
180 """Very painful calculation of when the Unix Epoch is. | |
181 | |
182 This is defined as the return value of time.time() on Jan 1st, | |
183 1970, 00:00:00 GMT. | |
184 | |
185 On a Unix system, this should always return 0.0. On a Mac, the | |
186 calculations are needed -- and hard because of integer overflow | |
187 and other limitations. | |
188 | |
189 """ | |
190 global _unix_epoch | |
191 if _unix_epoch >= 0: return _unix_epoch | |
192 import time | |
193 now = time.time() | |
194 localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...) | |
195 gmt = time.gmtime(now) | |
196 offset = time.mktime(localt) - time.mktime(gmt) | |
197 y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0 | |
198 offset, ss = divmod(ss + offset, 60) | |
199 offset, mm = divmod(mm + offset, 60) | |
200 offset, hh = divmod(hh + offset, 24) | |
201 d = d + offset | |
202 _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0)) | |
203 print "Unix epoch:", time.ctime(_unix_epoch) | |
204 return _unix_epoch | |
205 | |
206 | |
207 # Common base class for clients | |
208 | |
209 class Client: | |
210 | |
211 def __init__(self, host, prog, vers, port): | |
212 self.host = host | |
213 self.prog = prog | |
214 self.vers = vers | |
215 self.port = port | |
216 self.makesocket() # Assigns to self.sock | |
217 self.bindsocket() | |
218 self.connsocket() | |
219 self.lastxid = 0 # XXX should be more random? | |
220 self.addpackers() | |
221 self.cred = None | |
222 self.verf = None | |
223 | |
224 def close(self): | |
225 self.sock.close() | |
226 | |
227 def makesocket(self): | |
228 # This MUST be overridden | |
229 raise RuntimeError, 'makesocket not defined' | |
230 | |
231 def connsocket(self): | |
232 # Override this if you don't want/need a connection | |
233 self.sock.connect((self.host, self.port)) | |
234 | |
235 def bindsocket(self): | |
236 # Override this to bind to a different port (e.g. reserved) | |
237 self.sock.bind(('', 0)) | |
238 | |
239 def addpackers(self): | |
240 # Override this to use derived classes from Packer/Unpacker | |
241 self.packer = Packer() | |
242 self.unpacker = Unpacker('') | |
243 | |
244 def make_call(self, proc, args, pack_func, unpack_func): | |
245 #print "make_call() args = " + str(args) | |
246 # Don't normally override this (but see Broadcast) | |
247 if pack_func is None and args is not None: | |
248 raise TypeError, 'non-null args with null pack_func' | |
249 #print "packed args" | |
250 self.start_call(proc) | |
251 if pack_func: | |
252 pack_func(args) | |
253 self.do_call() | |
254 if unpack_func: | |
255 result = unpack_func() | |
256 else: | |
257 result = None | |
258 #print "result = " + str(result) | |
259 self.unpacker.done() | |
260 return result | |
261 | |
262 def start_call(self, proc): | |
263 # Don't override this | |
264 self.lastxid = xid = self.lastxid + 1 | |
265 cred = self.mkcred() | |
266 verf = self.mkverf() | |
267 p = self.packer | |
268 p.reset() | |
269 p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) | |
270 | |
271 def do_call(self): | |
272 # This MUST be overridden | |
273 raise RuntimeError, 'do_call not defined' | |
274 | |
275 def mkcred(self): | |
276 # Override this to use more powerful credentials | |
277 if self.cred == None: | |
278 self.cred = (AUTH_NULL, make_auth_null()) | |
279 return self.cred | |
280 | |
281 def mkverf(self): | |
282 # Override this to use a more powerful verifier | |
283 if self.verf == None: | |
284 self.verf = (AUTH_NULL, make_auth_null()) | |
285 return self.verf | |
286 | |
287 def call_0(self): # Procedure 0 is always like this | |
288 return self.make_call(0, None, None, None) | |
289 | |
290 | |
291 # Record-Marking standard support | |
292 | |
293 try: | |
294 from select import select as _select | |
295 except: | |
296 _select=None | |
297 | |
298 def sendfrag_with_timeout(sock, last, frag, timeout_seconds=None): | |
299 x = len(frag) | |
300 if last: x = x | 0x80000000L | |
301 header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \ | |
302 chr(int(x>>8 & 0xff)) + chr(int(x & 0xff))) | |
303 block=header+frag | |
304 n=len(block) | |
305 nsent=0 | |
306 while(nsent<n): | |
307 if _select and timeout_seconds: | |
308 rlist, wlist, xlist=_select([],[sock],[], timeout_seconds) | |
309 if not wlist: | |
310 raise EOFError, "Blocked write in sendfrag()" | |
311 nsent+=sock.send(block[nsent:]) | |
312 | |
313 def recvfrag_with_timeout(sock, timeout_seconds=None): | |
314 #print "receiving from ", sock | |
315 if _select and timeout_seconds: | |
316 #print "Selecting with timeout...", timeout_seconds | |
317 rlist, wlist, xlist=_select([sock],[],[], timeout_seconds) | |
318 if not rlist: | |
319 raise EOFError, "No header at all in recvfrag()" | |
320 | |
321 header = sock.recv(4) | |
322 if len(header) < 4: | |
323 raise EOFError | |
324 x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \ | |
325 ord(header[2])<<8 | ord(header[3]) | |
326 last = ((x & 0x80000000L) != 0) | |
327 n = int(x & 0x7fffffff) | |
328 | |
329 frag='' | |
330 | |
331 while(len(frag) < n): | |
332 if _select and timeout_seconds: | |
333 #print "Selecting with timeout...", timeout_seconds | |
334 rlist, wlist, xlist=_select([sock],[],[], timeout_seconds) | |
335 if not rlist: | |
336 raise EOFError, "No data after header in recvfrag()" | |
337 frag += sock.recv(n-len(frag)) | |
338 | |
339 return last, frag | |
340 | |
341 | |
342 def recvrecord(sock, timeout_seconds=None): | |
343 record = '' | |
344 last = 0 | |
345 while not last: | |
346 last, frag = recvfrag_with_timeout(sock, timeout_seconds) | |
347 record = record + frag | |
348 return record | |
349 | |
350 def sendrecord(sock, record, timeout_seconds=None): | |
351 sendfrag_with_timeout(sock, 1, record, timeout_seconds) | |
352 | |
353 | |
354 | |
355 # Try to bind to a reserved port (must be root) | |
356 | |
357 last_resv_port_tried = None | |
358 def bindresvport(sock, host): | |
359 global last_resv_port_tried | |
360 FIRST, LAST = 600, 1024 # Range of ports to try | |
361 if last_resv_port_tried == None: | |
362 import os | |
363 last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST) | |
364 for i in range(last_resv_port_tried, LAST) + \ | |
365 range(FIRST, last_resv_port_tried): | |
366 last_resv_port_tried = i | |
367 try: | |
368 sock.bind((host, i)) | |
369 return last_resv_port_tried | |
370 except socket.error, (errno, msg): | |
371 if errno <> 114: | |
372 raise socket.error, (errno, msg) | |
373 raise RuntimeError, 'can\'t assign reserved port' | |
374 | |
375 | |
376 # Client using TCP to a specific port | |
377 | |
378 class RawTCPClient(Client): | |
379 | |
380 select_timeout_seconds=None | |
381 | |
382 def makesocket(self): | |
383 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
384 | |
385 def do_call(self): | |
386 call = self.packer.get_buf() | |
387 sendrecord(self.sock, call, self.select_timeout_seconds) | |
388 reply = recvrecord(self.sock, self.select_timeout_seconds) | |
389 u = self.unpacker | |
390 u.reset(reply) | |
391 xid, verf = u.unpack_replyheader() | |
392 if xid <> self.lastxid: | |
393 # Can't really happen since this is TCP... | |
394 raise RuntimeError, 'wrong xid in reply ' + `xid` + \ | |
395 ' instead of ' + `self.lastxid` | |
396 | |
397 | |
398 # Client using UDP to a specific port | |
399 | |
400 class RawUDPClient(Client): | |
401 | |
402 def makesocket(self): | |
403 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
404 | |
405 def do_call(self): | |
406 call = self.packer.get_buf() | |
407 self.sock.send(call) | |
408 try: | |
409 from select import select | |
410 except ImportError: | |
411 print 'WARNING: select not found, RPC may hang' | |
412 select = None | |
413 BUFSIZE = 8192 # Max UDP buffer size | |
414 timeout = 1 | |
415 count = 5 | |
416 while 1: | |
417 r, w, x = [self.sock], [], [] | |
418 if select: | |
419 r, w, x = select(r, w, x, timeout) | |
420 if self.sock not in r: | |
421 count = count - 1 | |
422 if count < 0: raise RuntimeError, 'timeout' | |
423 if timeout < 25: timeout = timeout *2 | |
424 ## print 'RESEND', timeout, count | |
425 self.sock.send(call) | |
426 continue | |
427 reply = self.sock.recv(BUFSIZE) | |
428 u = self.unpacker | |
429 u.reset(reply) | |
430 xid, verf = u.unpack_replyheader() | |
431 if xid <> self.lastxid: | |
432 ## print 'BAD xid' | |
433 continue | |
434 break | |
435 | |
436 | |
437 # Client using UDP broadcast to a specific port | |
438 | |
439 class RawBroadcastUDPClient(RawUDPClient): | |
440 | |
441 def __init__(self, bcastaddr, prog, vers, port): | |
442 RawUDPClient.__init__(self, bcastaddr, prog, vers, port) | |
443 self.reply_handler = None | |
444 self.timeout = 30 | |
445 | |
446 def connsocket(self): | |
447 # Don't connect -- use sendto | |
448 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) | |
449 | |
450 def set_reply_handler(self, reply_handler): | |
451 self.reply_handler = reply_handler | |
452 | |
453 def set_timeout(self, timeout): | |
454 self.timeout = timeout # Use None for infinite timeout | |
455 | |
456 def make_call(self, proc, args, pack_func, unpack_func): | |
457 if pack_func is None and args is not None: | |
458 raise TypeError, 'non-null args with null pack_func' | |
459 self.start_call(proc) | |
460 if pack_func: | |
461 pack_func(args) | |
462 call = self.packer.get_buf() | |
463 self.sock.sendto(call, (self.host, self.port)) | |
464 try: | |
465 from select import select | |
466 except ImportError: | |
467 print 'WARNING: select not found, broadcast will hang' | |
468 select = None | |
469 BUFSIZE = 8192 # Max UDP buffer size (for reply) | |
470 replies = [] | |
471 if unpack_func is None: | |
472 def dummy(): pass | |
473 unpack_func = dummy | |
474 while 1: | |
475 r, w, x = [self.sock], [], [] | |
476 if select: | |
477 if self.timeout is None: | |
478 r, w, x = select(r, w, x) | |
479 else: | |
480 r, w, x = select(r, w, x, self.timeout) | |
481 if self.sock not in r: | |
482 break | |
483 reply, fromaddr = self.sock.recvfrom(BUFSIZE) | |
484 u = self.unpacker | |
485 u.reset(reply) | |
486 xid, verf = u.unpack_replyheader() | |
487 if xid <> self.lastxid: | |
488 ## print 'BAD xid' | |
489 continue | |
490 reply = unpack_func() | |
491 self.unpacker.done() | |
492 replies.append((reply, fromaddr)) | |
493 if self.reply_handler: | |
494 self.reply_handler(reply, fromaddr) | |
495 return replies | |
496 | |
497 | |
498 # Port mapper interface | |
499 | |
500 # Program number, version and (fixed!) port number | |
501 PMAP_PROG = 100000 | |
502 PMAP_VERS = 2 | |
503 PMAP_PORT = 111 | |
504 | |
505 # Procedure numbers | |
506 PMAPPROC_NULL = 0 # (void) -> void | |
507 PMAPPROC_SET = 1 # (mapping) -> bool | |
508 PMAPPROC_UNSET = 2 # (mapping) -> bool | |
509 PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int | |
510 PMAPPROC_DUMP = 4 # (void) -> pmaplist | |
511 PMAPPROC_CALLIT = 5 # (call_args) -> call_result | |
512 | |
513 # A mapping is (prog, vers, prot, port) and prot is one of: | |
514 | |
515 IPPROTO_TCP = 6 | |
516 IPPROTO_UDP = 17 | |
517 | |
518 # A pmaplist is a variable-length list of mappings, as follows: | |
519 # either (1, mapping, pmaplist) or (0). | |
520 | |
521 # A call_args is (prog, vers, proc, args) where args is opaque; | |
522 # a call_result is (port, res) where res is opaque. | |
523 | |
524 | |
525 class PortMapperPacker(Packer): | |
526 | |
527 def pack_mapping(self, mapping): | |
528 prog, vers, prot, port = mapping | |
529 self.pack_uint(prog) | |
530 self.pack_uint(vers) | |
531 self.pack_uint(prot) | |
532 self.pack_uint(port) | |
533 | |
534 def pack_pmaplist(self, list): | |
535 self.pack_list(list, self.pack_mapping) | |
536 | |
537 def pack_call_args(self, ca): | |
538 prog, vers, proc, args = ca | |
539 self.pack_uint(prog) | |
540 self.pack_uint(vers) | |
541 self.pack_uint(proc) | |
542 self.pack_opaque(args) | |
543 | |
544 | |
545 class PortMapperUnpacker(Unpacker): | |
546 | |
547 def unpack_mapping(self): | |
548 prog = self.unpack_uint() | |
549 vers = self.unpack_uint() | |
550 prot = self.unpack_uint() | |
551 port = self.unpack_uint() | |
552 return prog, vers, prot, port | |
553 | |
554 def unpack_pmaplist(self): | |
555 return self.unpack_list(self.unpack_mapping) | |
556 | |
557 def unpack_call_result(self): | |
558 port = self.unpack_uint() | |
559 res = self.unpack_opaque() | |
560 return port, res | |
561 | |
562 | |
563 class PartialPortMapperClient: | |
564 | |
565 def addpackers(self): | |
566 self.packer = PortMapperPacker() | |
567 self.unpacker = PortMapperUnpacker('') | |
568 | |
569 def Set(self, mapping): | |
570 return self.make_call(PMAPPROC_SET, mapping, \ | |
571 self.packer.pack_mapping, \ | |
572 self.unpacker.unpack_uint) | |
573 | |
574 def Unset(self, mapping): | |
575 return self.make_call(PMAPPROC_UNSET, mapping, \ | |
576 self.packer.pack_mapping, \ | |
577 self.unpacker.unpack_uint) | |
578 | |
579 def Getport(self, mapping): | |
580 return self.make_call(PMAPPROC_GETPORT, mapping, \ | |
581 self.packer.pack_mapping, \ | |
582 self.unpacker.unpack_uint) | |
583 | |
584 def Dump(self): | |
585 return self.make_call(PMAPPROC_DUMP, None, \ | |
586 None, \ | |
587 self.unpacker.unpack_pmaplist) | |
588 | |
589 def Callit(self, ca): | |
590 return self.make_call(PMAPPROC_CALLIT, ca, \ | |
591 self.packer.pack_call_args, \ | |
592 self.unpacker.unpack_call_result) | |
593 | |
594 | |
595 class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): | |
596 | |
597 def __init__(self, host, port=PMAP_PORT, timeout_seconds=None): | |
598 RawTCPClient.__init__(self, \ | |
599 host, PMAP_PROG, PMAP_VERS, port) | |
600 self.select_timeout_seconds=timeout_seconds | |
601 | |
602 | |
603 class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient): | |
604 | |
605 def __init__(self, host, port=PMAP_PORT): | |
606 RawUDPClient.__init__(self, \ | |
607 host, PMAP_PROG, PMAP_VERS, port) | |
608 | |
609 class BroadcastUDPPortMapperClient(PartialPortMapperClient, \ | |
610 RawBroadcastUDPClient): | |
611 | |
612 def __init__(self, bcastaddr): | |
613 RawBroadcastUDPClient.__init__(self, \ | |
614 bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT) | |
615 | |
616 | |
617 # Generic clients that find their server through the Port mapper | |
618 | |
619 class TCPClient(RawTCPClient): | |
620 | |
621 def __init__(self, host, prog, vers, portmap_proxy_host=None, portmap_proxy_port=PMAP_PORT, timeout_seconds=None): | |
622 | |
623 self.select_timeout_seconds=timeout_seconds | |
624 if portmap_proxy_host is None: | |
625 portmap_proxy_host=host #use a proxy to get around firewalled portmappers | |
626 pmap = TCPPortMapperClient(portmap_proxy_host, portmap_proxy_port,timeout_seconds) | |
627 port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) | |
628 pmap.close() | |
629 if port == 0: | |
630 raise RuntimeError, 'program not registered' | |
631 RawTCPClient.__init__(self, host, prog, vers, port) | |
632 | |
633 | |
634 class UDPClient(RawUDPClient): | |
635 | |
636 def __init__(self, host, prog, vers, portmap_proxy_host=None, portmap_proxy_port=PMAP_PORT): | |
637 if portmap_proxy_host is None: | |
638 portmap_proxy_host=host #use a proxy to get around firewalled portmappers | |
639 pmap = UDPPortMapperClient(portmap_proxy_host, portmap_proxy_port) | |
640 port = pmap.Getport((prog, vers, IPPROTO_UDP, 0)) | |
641 pmap.close() | |
642 if port == 0: | |
643 raise RuntimeError, 'program not registered' | |
644 RawUDPClient.__init__(self, host, prog, vers, port) | |
645 | |
646 | |
647 class BroadcastUDPClient(Client): | |
648 | |
649 def __init__(self, bcastaddr, prog, vers): | |
650 self.pmap = BroadcastUDPPortMapperClient(bcastaddr) | |
651 self.pmap.set_reply_handler(self.my_reply_handler) | |
652 self.prog = prog | |
653 self.vers = vers | |
654 self.user_reply_handler = None | |
655 self.addpackers() | |
656 | |
657 def close(self): | |
658 self.pmap.close() | |
659 | |
660 def set_reply_handler(self, reply_handler): | |
661 self.user_reply_handler = reply_handler | |
662 | |
663 def set_timeout(self, timeout): | |
664 self.pmap.set_timeout(timeout) | |
665 | |
666 def my_reply_handler(self, reply, fromaddr): | |
667 port, res = reply | |
668 self.unpacker.reset(res) | |
669 result = self.unpack_func() | |
670 self.unpacker.done() | |
671 self.replies.append((result, fromaddr)) | |
672 if self.user_reply_handler is not None: | |
673 self.user_reply_handler(result, fromaddr) | |
674 | |
675 def make_call(self, proc, args, pack_func, unpack_func): | |
676 self.packer.reset() | |
677 if pack_func: | |
678 pack_func(args) | |
679 if unpack_func is None: | |
680 def dummy(): pass | |
681 self.unpack_func = dummy | |
682 else: | |
683 self.unpack_func = unpack_func | |
684 self.replies = [] | |
685 packed_args = self.packer.get_buf() | |
686 dummy_replies = self.pmap.Callit( \ | |
687 (self.prog, self.vers, proc, packed_args)) | |
688 return self.replies | |
689 | |
690 | |
691 # Server classes | |
692 | |
693 # These are not symmetric to the Client classes | |
694 # XXX No attempt is made to provide authorization hooks yet | |
695 | |
696 class Server: | |
697 | |
698 def __init__(self, host, prog, vers, port): | |
699 self.host = host # Should normally be '' for default interface | |
700 self.prog = prog | |
701 self.vers = vers | |
702 self.port = port # Should normally be 0 for random port | |
703 self.makesocket() # Assigns to self.sock and self.prot | |
704 self.bindsocket() | |
705 self.host, self.port = self.sock.getsockname() | |
706 self.addpackers() | |
707 | |
708 def handle(self, call): | |
709 # Don't use unpack_header but parse the header piecewise | |
710 # XXX I have no idea if I am using the right error responses! | |
711 self.unpacker.reset(call) | |
712 self.packer.reset() | |
713 xid = self.unpacker.unpack_uint() | |
714 self.packer.pack_uint(xid) | |
715 temp = self.unpacker.unpack_enum() | |
716 if temp <> CALL: | |
717 return None # Not worthy of a reply | |
718 self.packer.pack_uint(REPLY) | |
719 temp = self.unpacker.unpack_uint() | |
720 if temp <> RPCVERSION: | |
721 self.packer.pack_uint(MSG_DENIED) | |
722 self.packer.pack_uint(RPC_MISMATCH) | |
723 self.packer.pack_uint(RPCVERSION) | |
724 self.packer.pack_uint(RPCVERSION) | |
725 return self.packer.get_buf() | |
726 self.packer.pack_uint(MSG_ACCEPTED) | |
727 self.packer.pack_auth((AUTH_NULL, make_auth_null())) | |
728 prog = self.unpacker.unpack_uint() | |
729 if prog <> self.prog: | |
730 self.packer.pack_uint(PROG_UNAVAIL) | |
731 return self.packer.get_buf() | |
732 vers = self.unpacker.unpack_uint() | |
733 if vers <> self.vers: | |
734 self.packer.pack_uint(PROG_MISMATCH) | |
735 self.packer.pack_uint(self.vers) | |
736 self.packer.pack_uint(self.vers) | |
737 return self.packer.get_buf() | |
738 proc = self.unpacker.unpack_uint() | |
739 methname = 'handle_' + `proc` | |
740 try: | |
741 meth = getattr(self, methname) | |
742 except AttributeError: | |
743 self.packer.pack_uint(PROC_UNAVAIL) | |
744 return self.packer.get_buf() | |
745 cred = self.unpacker.unpack_auth() | |
746 verf = self.unpacker.unpack_auth() | |
747 try: | |
748 meth() # Unpack args, call turn_around(), pack reply | |
749 except (EOFError, GarbageArgs): | |
750 # Too few or too many arguments | |
751 self.packer.reset() | |
752 self.packer.pack_uint(xid) | |
753 self.packer.pack_uint(REPLY) | |
754 self.packer.pack_uint(MSG_ACCEPTED) | |
755 self.packer.pack_auth((AUTH_NULL, make_auth_null())) | |
756 self.packer.pack_uint(GARBAGE_ARGS) | |
757 return self.packer.get_buf() | |
758 | |
759 def turn_around(self): | |
760 try: | |
761 self.unpacker.done() | |
762 except RuntimeError: | |
763 raise GarbageArgs | |
764 self.packer.pack_uint(SUCCESS) | |
765 | |
766 def handle_0(self): # Handle NULL message | |
767 self.turn_around() | |
768 | |
769 def makesocket(self): | |
770 # This MUST be overridden | |
771 raise RuntimeError, 'makesocket not defined' | |
772 | |
773 def bindsocket(self): | |
774 # Override this to bind to a different port (e.g. reserved) | |
775 self.sock.bind((self.host, self.port)) | |
776 | |
777 def addpackers(self): | |
778 # Override this to use derived classes from Packer/Unpacker | |
779 self.packer = Packer() | |
780 self.unpacker = Unpacker('') | |
781 | |
782 | |
783 class TCPServer(Server): | |
784 | |
785 def register(self): | |
786 mapping = self.prog, self.vers, self.prot, self.port | |
787 p = TCPPortMapperClient(self.host) | |
788 if not p.Set(mapping): | |
789 raise RuntimeError, 'register failed' | |
790 | |
791 def unregister(self): | |
792 mapping = self.prog, self.vers, self.prot, self.port | |
793 p = TCPPortMapperClient(self.host) | |
794 if not p.Unset(mapping): | |
795 raise RuntimeError, 'unregister failed' | |
796 | |
797 def makesocket(self): | |
798 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
799 self.prot = IPPROTO_TCP | |
800 | |
801 def loop(self): | |
802 self.sock.listen(0) | |
803 while 1: | |
804 self.session(self.sock.accept()) | |
805 | |
806 def session(self, connection): | |
807 sock, (host, port) = connection | |
808 while 1: | |
809 try: | |
810 call = recvrecord(sock) | |
811 except EOFError: | |
812 break | |
813 except socket.error, msg: | |
814 print 'socket error:', msg | |
815 break | |
816 reply = self.handle(call) | |
817 if reply is not None: | |
818 sendrecord(sock, reply) | |
819 | |
820 def forkingloop(self): | |
821 # Like loop but uses forksession() | |
822 self.sock.listen(0) | |
823 while 1: | |
824 self.forksession(self.sock.accept()) | |
825 | |
826 def forksession(self, connection): | |
827 # Like session but forks off a subprocess | |
828 import os | |
829 # Wait for deceased children | |
830 try: | |
831 while 1: | |
832 pid, sts = os.waitpid(0, 1) | |
833 except os.error: | |
834 pass | |
835 pid = None | |
836 try: | |
837 pid = os.fork() | |
838 if pid: # Parent | |
839 connection[0].close() | |
840 return | |
841 # Child | |
842 self.session(connection) | |
843 finally: | |
844 # Make sure we don't fall through in the parent | |
845 if pid == 0: | |
846 os._exit(0) | |
847 | |
848 | |
849 class UDPServer(Server): | |
850 | |
851 def register(self): | |
852 mapping = self.prog, self.vers, self.prot, self.port | |
853 p = UDPPortMapperClient(self.host) | |
854 if not p.Set(mapping): | |
855 raise RuntimeError, 'register failed' | |
856 | |
857 def unregister(self): | |
858 mapping = self.prog, self.vers, self.prot, self.port | |
859 p = UDPPortMapperClient(self.host) | |
860 if not p.Unset(mapping): | |
861 raise RuntimeError, 'unregister failed' | |
862 | |
863 def makesocket(self): | |
864 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
865 self.prot = IPPROTO_UDP | |
866 | |
867 def loop(self): | |
868 while 1: | |
869 self.session() | |
870 | |
871 def session(self): | |
872 call, host_port = self.sock.recvfrom(8192) | |
873 reply = self.handle(call) | |
874 if reply <> None: | |
875 self.sock.sendto(reply, host_port) | |
876 | |
877 | |
878 # Simple test program -- dump local portmapper status | |
879 | |
880 def test(): | |
881 pmap = UDPPortMapperClient('') | |
882 list = pmap.Dump() | |
883 list.sort() | |
884 for prog, vers, prot, port in list: | |
885 print prog, vers, | |
886 if prot == IPPROTO_TCP: print 'tcp', | |
887 elif prot == IPPROTO_UDP: print 'udp', | |
888 else: print prot, | |
889 print port | |
890 | |
891 | |
892 # Test program for broadcast operation -- dump everybody's portmapper status | |
893 | |
894 def testbcast(): | |
895 import sys | |
896 if sys.argv[1:]: | |
897 bcastaddr = sys.argv[1] | |
898 else: | |
899 bcastaddr = '<broadcast>' | |
900 def rh(reply, fromaddr): | |
901 host, port = fromaddr | |
902 print host + '\t' + `reply` | |
903 pmap = BroadcastUDPPortMapperClient(bcastaddr) | |
904 pmap.set_reply_handler(rh) | |
905 pmap.set_timeout(5) | |
906 replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0)) | |
907 | |
908 | |
909 # Test program for server, with corresponding client | |
910 # On machine A: python -c 'import rpc; rpc.testsvr()' | |
911 # On machine B: python -c 'import rpc; rpc.testclt()' A | |
912 # (A may be == B) | |
913 | |
914 def testsvr(): | |
915 # Simple test class -- proc 1 doubles its string argument as reply | |
916 class S(UDPServer): | |
917 def handle_1(self): | |
918 arg = self.unpacker.unpack_string() | |
919 self.turn_around() | |
920 print 'RPC function 1 called, arg', `arg` | |
921 self.packer.pack_string(arg + arg) | |
922 # | |
923 s = S('', 0x20000000, 1, 0) | |
924 try: | |
925 s.unregister() | |
926 except RuntimeError, msg: | |
927 print 'RuntimeError:', msg, '(ignored)' | |
928 s.register() | |
929 print 'Service started...' | |
930 try: | |
931 s.loop() | |
932 finally: | |
933 s.unregister() | |
934 print 'Service interrupted.' | |
935 | |
936 | |
937 def testclt(): | |
938 import sys | |
939 if sys.argv[1:]: host = sys.argv[1] | |
940 else: host = '' | |
941 # Client for above server | |
942 class C(UDPClient): | |
943 def call_1(self, arg): | |
944 return self.make_call(1, arg, \ | |
945 self.packer.pack_string, \ | |
946 self.unpacker.unpack_string) | |
947 c = C(host, 0x20000000, 1) | |
948 print 'making call...' | |
949 reply = c.call_1('hello, world, ') | |
950 print 'call returned', `reply` | |
951 | |
952 def testclt2(): | |
953 import sys | |
954 host = '127.0.0.1' | |
955 # Client for above server | |
956 class C(UDPClient): | |
957 def call_1(self, arg): | |
958 return self.make_call(1, arg, \ | |
959 self.packer.pack_string, \ | |
960 self.unpacker.unpack_string) | |
961 c = C(host, 0x20000000, 1) | |
962 print 'making call...' | |
963 reply = c.call_1('hello, world, ') | |
964 print 'call returned', `reply` | |
965 |