/Demo/rpc/rpc.py

http://unladen-swallow.googlecode.com/ · Python · 893 lines · 660 code · 158 blank · 75 comment · 101 complexity · dbffc2631a5938e4f06b903cb547934a MD5 · raw file

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