blob: f7231c1b547bf41c8ecfcf1ac7a3336223849a30 [file] [log] [blame]
Guido van Rossum8f1506b1992-12-21 14:32:06 +00001# Sun RPC version 2 -- RFC1057.
Guido van Rossume3cafbe1992-12-14 23:25:04 +00002
Guido van Rossumc4698fb1992-12-17 17:12:17 +00003# XXX There should be separate exceptions for the various reasons why
4# XXX an RPC can fail, rather than using RuntimeError for everything
5
Raymond Hettingerc672f8c2005-04-10 16:21:07 +00006# XXX Need to use class based exceptions rather than string exceptions
7
Guido van Rossumc4698fb1992-12-17 17:12:17 +00008# 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
Guido van Rossum79f85ee1992-12-20 14:56:32 +000011# XXX There is no provision for call timeout on TCP connections
12
Guido van Rossume3cafbe1992-12-14 23:25:04 +000013import 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
Tim Peterse6ddc8b2004-07-18 05:56:09 +000030SUCCESS = 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
Guido van Rossume3cafbe1992-12-14 23:25:04 +000035
Tim Peterse6ddc8b2004-07-18 05:56:09 +000036RPC_MISMATCH = 0 # RPC version number != 2
37AUTH_ERROR = 1 # remote can't authenticate caller
Guido van Rossume3cafbe1992-12-14 23:25:04 +000038
Tim Peterse6ddc8b2004-07-18 05:56:09 +000039AUTH_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
Guido van Rossume3cafbe1992-12-14 23:25:04 +000044
45
46class Packer(xdr.Packer):
47
Tim Peterse6ddc8b2004-07-18 05:56:09 +000048 def pack_auth(self, auth):
49 flavor, stuff = auth
50 self.pack_enum(flavor)
51 self.pack_opaque(stuff)
Guido van Rossume3cafbe1992-12-14 23:25:04 +000052
Tim Peterse6ddc8b2004-07-18 05:56:09 +000053 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)
Guido van Rossume3cafbe1992-12-14 23:25:04 +000061
Tim Peterse6ddc8b2004-07-18 05:56:09 +000062 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
Guido van Rossume3cafbe1992-12-14 23:25:04 +000072
Tim Peterse6ddc8b2004-07-18 05:56:09 +000073 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
Guido van Rossume3cafbe1992-12-14 23:25:04 +000080
81
Guido van Rossum424c6731992-12-19 00:05:55 +000082# Exceptions
83BadRPCFormat = 'rpc.BadRPCFormat'
84BadRPCVersion = 'rpc.BadRPCVersion'
85GarbageArgs = 'rpc.GarbageArgs'
86
Guido van Rossume3cafbe1992-12-14 23:25:04 +000087class Unpacker(xdr.Unpacker):
88
Tim Peterse6ddc8b2004-07-18 05:56:09 +000089 def unpack_auth(self):
90 flavor = self.unpack_enum()
91 stuff = self.unpack_opaque()
92 return (flavor, stuff)
Guido van Rossume3cafbe1992-12-14 23:25:04 +000093
Tim Peterse6ddc8b2004-07-18 05:56:09 +000094 def unpack_callheader(self):
Raymond Hettingerc672f8c2005-04-10 16:21:07 +000095 xid = self.unpack_uint()
Tim Peterse6ddc8b2004-07-18 05:56:09 +000096 temp = self.unpack_enum()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +000097 if temp != CALL:
Tim Peterse6ddc8b2004-07-18 05:56:09 +000098 raise BadRPCFormat, 'no CALL but %r' % (temp,)
99 temp = self.unpack_uint()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000100 if temp != RPCVERSION:
101 raise BadRPCVersion, 'bad RPC version %r' % (temp,)
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000102 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
Guido van Rossum424c6731992-12-19 00:05:55 +0000109
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000110 def unpack_replyheader(self):
111 xid = self.unpack_uint()
112 mtype = self.unpack_enum()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000113 if mtype != REPLY:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000114 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,)
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000128 if stat != MSG_ACCEPTED:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000129 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'
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000144 if stat != SUCCESS:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000145 raise RuntimeError, 'call failed: %r' % (stat,)
146 return xid, verf
147 # Caller must get procedure-specific part of reply
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000148
149
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000150# Subroutines to create opaque authentication objects
151
152def make_auth_null():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000153 return ''
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000154
155def make_auth_unix(seed, host, uid, gid, groups):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000156 p = Packer()
157 p.pack_auth_unix(seed, host, uid, gid, groups)
158 return p.get_buf()
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000159
160def make_auth_unix_default():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000161 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, [])
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000170
Guido van Rossum7a1c7911996-07-21 02:09:54 +0000171_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()
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000187 localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...)
Guido van Rossum7a1c7911996-07-21 02:09:54 +0000188 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
Guido van Rossum749d0bb1992-12-15 20:52:53 +0000199
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000200# Common base class for clients
201
202class Client:
203
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000204 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
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000216
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000217 def close(self):
218 self.sock.close()
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000219
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000220 def makesocket(self):
221 # This MUST be overridden
222 raise RuntimeError, 'makesocket not defined'
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000223
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000224 def connsocket(self):
225 # Override this if you don't want/need a connection
226 self.sock.connect((self.host, self.port))
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000227
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000228 def bindsocket(self):
229 # Override this to bind to a different port (e.g. reserved)
230 self.sock.bind(('', 0))
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000231
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000232 def addpackers(self):
233 # Override this to use derived classes from Packer/Unpacker
234 self.packer = Packer()
235 self.unpacker = Unpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000236
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000237 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
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000251
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000252 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)
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000260
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000261 def do_call(self):
262 # This MUST be overridden
263 raise RuntimeError, 'do_call not defined'
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000264
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000265 def mkcred(self):
266 # Override this to use more powerful credentials
267 if self.cred == None:
268 self.cred = (AUTH_NULL, make_auth_null())
269 return self.cred
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000270
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000271 def mkverf(self):
272 # Override this to use a more powerful verifier
273 if self.verf == None:
274 self.verf = (AUTH_NULL, make_auth_null())
275 return self.verf
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000276
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000277 def call_0(self): # Procedure 0 is always like this
278 return self.make_call(0, None, None, None)
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000279
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000280
281# Record-Marking standard support
282
283def sendfrag(sock, last, frag):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000284 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)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000289
290def sendrecord(sock, record):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000291 sendfrag(sock, 1, record)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000292
293def recvfrag(sock):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000294 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
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000308
309def recvrecord(sock):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000310 record = ''
311 last = 0
312 while not last:
313 last, frag = recvfrag(sock)
314 record = record + frag
315 return record
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000316
317
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000318# Try to bind to a reserved port (must be root)
319
320last_resv_port_tried = None
321def bindresvport(sock, host):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000322 global last_resv_port_tried
323 FIRST, LAST = 600, 1024 # Range of ports to try
324 if last_resv_port_tried == 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
Guido van Rossumb940e112007-01-10 16:19:56 +0000333 except socket.error as e:
334 (errno, msg) = e
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000335 if errno != 114:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000336 raise socket.error, (errno, msg)
337 raise RuntimeError, 'can\'t assign reserved port'
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000338
339
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000340# Client using TCP to a specific port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000341
342class RawTCPClient(Client):
343
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000344 def makesocket(self):
345 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000346
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000347 def do_call(self):
348 call = self.packer.get_buf()
349 sendrecord(self.sock, call)
350 reply = recvrecord(self.sock)
351 u = self.unpacker
352 u.reset(reply)
353 xid, verf = u.unpack_replyheader()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000354 if xid != self.lastxid:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000355 # Can't really happen since this is TCP...
356 raise RuntimeError, 'wrong xid in reply %r instead of %r' % (
357 xid, self.lastxid)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000358
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000359
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000360# Client using UDP to a specific port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000361
362class RawUDPClient(Client):
363
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000364 def makesocket(self):
365 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000366
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000367 def do_call(self):
368 call = self.packer.get_buf()
369 self.sock.send(call)
370 try:
371 from select import select
372 except ImportError:
373 print 'WARNING: select not found, RPC may hang'
374 select = None
375 BUFSIZE = 8192 # Max UDP buffer size
376 timeout = 1
377 count = 5
378 while 1:
379 r, w, x = [self.sock], [], []
380 if select:
381 r, w, x = select(r, w, x, timeout)
382 if self.sock not in r:
383 count = count - 1
384 if count < 0: raise RuntimeError, 'timeout'
385 if timeout < 25: timeout = timeout *2
386## print 'RESEND', timeout, count
387 self.sock.send(call)
388 continue
389 reply = self.sock.recv(BUFSIZE)
390 u = self.unpacker
391 u.reset(reply)
392 xid, verf = u.unpack_replyheader()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000393 if xid != self.lastxid:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000394## print 'BAD xid'
395 continue
396 break
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000397
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000398
399# Client using UDP broadcast to a specific port
400
401class RawBroadcastUDPClient(RawUDPClient):
402
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000403 def __init__(self, bcastaddr, prog, vers, port):
404 RawUDPClient.__init__(self, bcastaddr, prog, vers, port)
405 self.reply_handler = None
406 self.timeout = 30
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000407
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000408 def connsocket(self):
409 # Don't connect -- use sendto
410 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000411
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000412 def set_reply_handler(self, reply_handler):
413 self.reply_handler = reply_handler
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000414
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000415 def set_timeout(self, timeout):
416 self.timeout = timeout # Use None for infinite timeout
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000417
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000418 def make_call(self, proc, args, pack_func, unpack_func):
419 if pack_func is None and args is not None:
420 raise TypeError, 'non-null args with null pack_func'
421 self.start_call(proc)
422 if pack_func:
423 pack_func(args)
424 call = self.packer.get_buf()
425 self.sock.sendto(call, (self.host, self.port))
426 try:
427 from select import select
428 except ImportError:
429 print 'WARNING: select not found, broadcast will hang'
430 select = None
431 BUFSIZE = 8192 # Max UDP buffer size (for reply)
432 replies = []
433 if unpack_func is None:
434 def dummy(): pass
435 unpack_func = dummy
436 while 1:
437 r, w, x = [self.sock], [], []
438 if select:
439 if self.timeout is None:
440 r, w, x = select(r, w, x)
441 else:
442 r, w, x = select(r, w, x, self.timeout)
443 if self.sock not in r:
444 break
445 reply, fromaddr = self.sock.recvfrom(BUFSIZE)
446 u = self.unpacker
447 u.reset(reply)
448 xid, verf = u.unpack_replyheader()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000449 if xid != self.lastxid:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000450## print 'BAD xid'
451 continue
452 reply = unpack_func()
453 self.unpacker.done()
454 replies.append((reply, fromaddr))
455 if self.reply_handler:
456 self.reply_handler(reply, fromaddr)
457 return replies
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000458
459
460# Port mapper interface
461
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000462# Program number, version and (fixed!) port number
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000463PMAP_PROG = 100000
464PMAP_VERS = 2
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000465PMAP_PORT = 111
466
467# Procedure numbers
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000468PMAPPROC_NULL = 0 # (void) -> void
469PMAPPROC_SET = 1 # (mapping) -> bool
470PMAPPROC_UNSET = 2 # (mapping) -> bool
471PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int
472PMAPPROC_DUMP = 4 # (void) -> pmaplist
473PMAPPROC_CALLIT = 5 # (call_args) -> call_result
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000474
475# A mapping is (prog, vers, prot, port) and prot is one of:
476
477IPPROTO_TCP = 6
478IPPROTO_UDP = 17
479
480# A pmaplist is a variable-length list of mappings, as follows:
481# either (1, mapping, pmaplist) or (0).
482
483# A call_args is (prog, vers, proc, args) where args is opaque;
484# a call_result is (port, res) where res is opaque.
485
486
487class PortMapperPacker(Packer):
488
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000489 def pack_mapping(self, mapping):
490 prog, vers, prot, port = mapping
491 self.pack_uint(prog)
492 self.pack_uint(vers)
493 self.pack_uint(prot)
494 self.pack_uint(port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000495
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000496 def pack_pmaplist(self, list):
497 self.pack_list(list, self.pack_mapping)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000498
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000499 def pack_call_args(self, ca):
500 prog, vers, proc, args = ca
501 self.pack_uint(prog)
502 self.pack_uint(vers)
503 self.pack_uint(proc)
504 self.pack_opaque(args)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000505
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000506
507class PortMapperUnpacker(Unpacker):
508
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000509 def unpack_mapping(self):
510 prog = self.unpack_uint()
511 vers = self.unpack_uint()
512 prot = self.unpack_uint()
513 port = self.unpack_uint()
514 return prog, vers, prot, port
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000515
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000516 def unpack_pmaplist(self):
517 return self.unpack_list(self.unpack_mapping)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000518
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000519 def unpack_call_result(self):
520 port = self.unpack_uint()
521 res = self.unpack_opaque()
522 return port, res
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000523
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000524
525class PartialPortMapperClient:
526
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000527 def addpackers(self):
528 self.packer = PortMapperPacker()
529 self.unpacker = PortMapperUnpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000530
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000531 def Set(self, mapping):
532 return self.make_call(PMAPPROC_SET, mapping, \
533 self.packer.pack_mapping, \
534 self.unpacker.unpack_uint)
Guido van Rossum424c6731992-12-19 00:05:55 +0000535
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000536 def Unset(self, mapping):
537 return self.make_call(PMAPPROC_UNSET, mapping, \
538 self.packer.pack_mapping, \
539 self.unpacker.unpack_uint)
Guido van Rossum424c6731992-12-19 00:05:55 +0000540
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000541 def Getport(self, mapping):
542 return self.make_call(PMAPPROC_GETPORT, mapping, \
543 self.packer.pack_mapping, \
544 self.unpacker.unpack_uint)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000545
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000546 def Dump(self):
547 return self.make_call(PMAPPROC_DUMP, None, \
548 None, \
549 self.unpacker.unpack_pmaplist)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000550
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000551 def Callit(self, ca):
552 return self.make_call(PMAPPROC_CALLIT, ca, \
553 self.packer.pack_call_args, \
554 self.unpacker.unpack_call_result)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000555
556
557class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
558
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000559 def __init__(self, host):
560 RawTCPClient.__init__(self, \
561 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000562
563
564class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
565
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000566 def __init__(self, host):
567 RawUDPClient.__init__(self, \
568 host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000569
570
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000571class BroadcastUDPPortMapperClient(PartialPortMapperClient, \
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000572 RawBroadcastUDPClient):
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000573
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000574 def __init__(self, bcastaddr):
575 RawBroadcastUDPClient.__init__(self, \
576 bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000577
578
579# Generic clients that find their server through the Port mapper
580
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000581class TCPClient(RawTCPClient):
582
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000583 def __init__(self, host, prog, vers):
584 pmap = TCPPortMapperClient(host)
585 port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
586 pmap.close()
587 if port == 0:
588 raise RuntimeError, 'program not registered'
589 RawTCPClient.__init__(self, host, prog, vers, port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000590
591
592class UDPClient(RawUDPClient):
593
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000594 def __init__(self, host, prog, vers):
595 pmap = UDPPortMapperClient(host)
596 port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
597 pmap.close()
598 if port == 0:
599 raise RuntimeError, 'program not registered'
600 RawUDPClient.__init__(self, host, prog, vers, port)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000601
602
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000603class BroadcastUDPClient(Client):
604
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000605 def __init__(self, bcastaddr, prog, vers):
606 self.pmap = BroadcastUDPPortMapperClient(bcastaddr)
607 self.pmap.set_reply_handler(self.my_reply_handler)
608 self.prog = prog
609 self.vers = vers
610 self.user_reply_handler = None
611 self.addpackers()
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000612
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000613 def close(self):
614 self.pmap.close()
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000615
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000616 def set_reply_handler(self, reply_handler):
617 self.user_reply_handler = reply_handler
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000618
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000619 def set_timeout(self, timeout):
620 self.pmap.set_timeout(timeout)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000621
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000622 def my_reply_handler(self, reply, fromaddr):
623 port, res = reply
624 self.unpacker.reset(res)
625 result = self.unpack_func()
626 self.unpacker.done()
627 self.replies.append((result, fromaddr))
628 if self.user_reply_handler is not None:
629 self.user_reply_handler(result, fromaddr)
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000630
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000631 def make_call(self, proc, args, pack_func, unpack_func):
632 self.packer.reset()
633 if pack_func:
634 pack_func(args)
635 if unpack_func is None:
636 def dummy(): pass
637 self.unpack_func = dummy
638 else:
639 self.unpack_func = unpack_func
640 self.replies = []
641 packed_args = self.packer.get_buf()
642 dummy_replies = self.pmap.Callit( \
643 (self.prog, self.vers, proc, packed_args))
644 return self.replies
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000645
646
Guido van Rossum424c6731992-12-19 00:05:55 +0000647# Server classes
648
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000649# These are not symmetric to the Client classes
650# XXX No attempt is made to provide authorization hooks yet
651
Guido van Rossum424c6731992-12-19 00:05:55 +0000652class Server:
653
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000654 def __init__(self, host, prog, vers, port):
655 self.host = host # Should normally be '' for default interface
656 self.prog = prog
657 self.vers = vers
658 self.port = port # Should normally be 0 for random port
659 self.makesocket() # Assigns to self.sock and self.prot
660 self.bindsocket()
661 self.host, self.port = self.sock.getsockname()
662 self.addpackers()
Guido van Rossum424c6731992-12-19 00:05:55 +0000663
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000664 def register(self):
665 mapping = self.prog, self.vers, self.prot, self.port
666 p = TCPPortMapperClient(self.host)
667 if not p.Set(mapping):
668 raise RuntimeError, 'register failed'
Guido van Rossum424c6731992-12-19 00:05:55 +0000669
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000670 def unregister(self):
671 mapping = self.prog, self.vers, self.prot, self.port
672 p = TCPPortMapperClient(self.host)
673 if not p.Unset(mapping):
674 raise RuntimeError, 'unregister failed'
Guido van Rossum424c6731992-12-19 00:05:55 +0000675
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000676 def handle(self, call):
677 # Don't use unpack_header but parse the header piecewise
678 # XXX I have no idea if I am using the right error responses!
679 self.unpacker.reset(call)
680 self.packer.reset()
681 xid = self.unpacker.unpack_uint()
682 self.packer.pack_uint(xid)
683 temp = self.unpacker.unpack_enum()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000684 if temp != CALL:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000685 return None # Not worthy of a reply
686 self.packer.pack_uint(REPLY)
687 temp = self.unpacker.unpack_uint()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000688 if temp != RPCVERSION:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000689 self.packer.pack_uint(MSG_DENIED)
690 self.packer.pack_uint(RPC_MISMATCH)
691 self.packer.pack_uint(RPCVERSION)
692 self.packer.pack_uint(RPCVERSION)
693 return self.packer.get_buf()
694 self.packer.pack_uint(MSG_ACCEPTED)
695 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
696 prog = self.unpacker.unpack_uint()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000697 if prog != self.prog:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000698 self.packer.pack_uint(PROG_UNAVAIL)
699 return self.packer.get_buf()
700 vers = self.unpacker.unpack_uint()
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000701 if vers != self.vers:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000702 self.packer.pack_uint(PROG_MISMATCH)
703 self.packer.pack_uint(self.vers)
704 self.packer.pack_uint(self.vers)
705 return self.packer.get_buf()
706 proc = self.unpacker.unpack_uint()
707 methname = 'handle_' + repr(proc)
708 try:
709 meth = getattr(self, methname)
710 except AttributeError:
711 self.packer.pack_uint(PROC_UNAVAIL)
712 return self.packer.get_buf()
713 cred = self.unpacker.unpack_auth()
714 verf = self.unpacker.unpack_auth()
715 try:
716 meth() # Unpack args, call turn_around(), pack reply
717 except (EOFError, GarbageArgs):
718 # Too few or too many arguments
719 self.packer.reset()
720 self.packer.pack_uint(xid)
721 self.packer.pack_uint(REPLY)
722 self.packer.pack_uint(MSG_ACCEPTED)
723 self.packer.pack_auth((AUTH_NULL, make_auth_null()))
724 self.packer.pack_uint(GARBAGE_ARGS)
725 return self.packer.get_buf()
Guido van Rossum424c6731992-12-19 00:05:55 +0000726
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000727 def turn_around(self):
728 try:
729 self.unpacker.done()
730 except RuntimeError:
731 raise GarbageArgs
732 self.packer.pack_uint(SUCCESS)
Guido van Rossum424c6731992-12-19 00:05:55 +0000733
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000734 def handle_0(self): # Handle NULL message
735 self.turn_around()
Guido van Rossum424c6731992-12-19 00:05:55 +0000736
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000737 def makesocket(self):
738 # This MUST be overridden
739 raise RuntimeError, 'makesocket not defined'
Guido van Rossum79f85ee1992-12-20 14:56:32 +0000740
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000741 def bindsocket(self):
742 # Override this to bind to a different port (e.g. reserved)
743 self.sock.bind((self.host, self.port))
Guido van Rossum424c6731992-12-19 00:05:55 +0000744
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000745 def addpackers(self):
746 # Override this to use derived classes from Packer/Unpacker
747 self.packer = Packer()
748 self.unpacker = Unpacker('')
Guido van Rossum424c6731992-12-19 00:05:55 +0000749
750
751class TCPServer(Server):
752
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000753 def makesocket(self):
754 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
755 self.prot = IPPROTO_TCP
Guido van Rossum424c6731992-12-19 00:05:55 +0000756
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000757 def loop(self):
758 self.sock.listen(0)
759 while 1:
760 self.session(self.sock.accept())
Guido van Rossum424c6731992-12-19 00:05:55 +0000761
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000762 def session(self, connection):
763 sock, (host, port) = connection
764 while 1:
765 try:
766 call = recvrecord(sock)
767 except EOFError:
768 break
Guido van Rossumb940e112007-01-10 16:19:56 +0000769 except socket.error as msg:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000770 print 'socket error:', msg
771 break
772 reply = self.handle(call)
773 if reply is not None:
774 sendrecord(sock, reply)
Guido van Rossum424c6731992-12-19 00:05:55 +0000775
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000776 def forkingloop(self):
777 # Like loop but uses forksession()
778 self.sock.listen(0)
779 while 1:
780 self.forksession(self.sock.accept())
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000781
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000782 def forksession(self, connection):
783 # Like session but forks off a subprocess
784 import os
785 # Wait for deceased children
786 try:
787 while 1:
788 pid, sts = os.waitpid(0, 1)
789 except os.error:
790 pass
791 pid = None
792 try:
793 pid = os.fork()
794 if pid: # Parent
795 connection[0].close()
796 return
797 # Child
798 self.session(connection)
799 finally:
800 # Make sure we don't fall through in the parent
801 if pid == 0:
802 os._exit(0)
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000803
Guido van Rossum424c6731992-12-19 00:05:55 +0000804
805class UDPServer(Server):
806
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000807 def makesocket(self):
808 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
809 self.prot = IPPROTO_UDP
Guido van Rossum424c6731992-12-19 00:05:55 +0000810
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000811 def loop(self):
812 while 1:
813 self.session()
Guido van Rossum424c6731992-12-19 00:05:55 +0000814
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000815 def session(self):
816 call, host_port = self.sock.recvfrom(8192)
817 reply = self.handle(call)
Raymond Hettingerc672f8c2005-04-10 16:21:07 +0000818 if reply != None:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000819 self.sock.sendto(reply, host_port)
Guido van Rossum424c6731992-12-19 00:05:55 +0000820
821
822# Simple test program -- dump local portmapper status
823
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000824def test():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000825 pmap = UDPPortMapperClient('')
826 list = pmap.Dump()
827 list.sort()
828 for prog, vers, prot, port in list:
829 print prog, vers,
830 if prot == IPPROTO_TCP: print 'tcp',
831 elif prot == IPPROTO_UDP: print 'udp',
832 else: print prot,
833 print port
Guido van Rossum424c6731992-12-19 00:05:55 +0000834
835
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000836# Test program for broadcast operation -- dump everybody's portmapper status
837
838def testbcast():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000839 import sys
840 if sys.argv[1:]:
841 bcastaddr = sys.argv[1]
842 else:
843 bcastaddr = '<broadcast>'
844 def rh(reply, fromaddr):
845 host, port = fromaddr
846 print host + '\t' + repr(reply)
847 pmap = BroadcastUDPPortMapperClient(bcastaddr)
848 pmap.set_reply_handler(rh)
849 pmap.set_timeout(5)
850 replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0))
Guido van Rossum8f1506b1992-12-21 14:32:06 +0000851
852
853# Test program for server, with corresponding client
Guido van Rossum424c6731992-12-19 00:05:55 +0000854# On machine A: python -c 'import rpc; rpc.testsvr()'
855# On machine B: python -c 'import rpc; rpc.testclt()' A
856# (A may be == B)
857
858def testsvr():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000859 # Simple test class -- proc 1 doubles its string argument as reply
860 class S(UDPServer):
861 def handle_1(self):
862 arg = self.unpacker.unpack_string()
863 self.turn_around()
864 print 'RPC function 1 called, arg', repr(arg)
865 self.packer.pack_string(arg + arg)
866 #
867 s = S('', 0x20000000, 1, 0)
868 try:
869 s.unregister()
Guido van Rossumb940e112007-01-10 16:19:56 +0000870 except RuntimeError as msg:
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000871 print 'RuntimeError:', msg, '(ignored)'
872 s.register()
873 print 'Service started...'
874 try:
875 s.loop()
876 finally:
877 s.unregister()
878 print 'Service interrupted.'
Guido van Rossum424c6731992-12-19 00:05:55 +0000879
880
881def testclt():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000882 import sys
883 if sys.argv[1:]: host = sys.argv[1]
884 else: host = ''
885 # Client for above server
886 class C(UDPClient):
887 def call_1(self, arg):
888 return self.make_call(1, arg, \
889 self.packer.pack_string, \
890 self.unpacker.unpack_string)
891 c = C(host, 0x20000000, 1)
892 print 'making call...'
893 reply = c.call_1('hello, world, ')
894 print 'call returned', repr(reply)