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