source: python/vendor/Python-2.7.6/Demo/rpc/rpc.py

Last change on this file was 2, checked in by Yuri Dario, 15 years ago

Initial import for vendor code.

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