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) 894