66# XXX The UDP version of the protocol resends requests when it does
77# XXX not receive a timely reply -- use only for idempotent calls!
88
9+ # XXX There is no provision for call timeout on TCP connections
10+
911import xdr
1012import socket
1113import os
@@ -160,51 +162,78 @@ def make_auth_unix_default():
160162 gid = getgid ()
161163 except ImportError :
162164 uid = gid = 0
163- return make_auth_unix (0 , socket .gethostname (), uid , gid , [])
165+ import time
166+ return make_auth_unix (time .time (), socket .gethostname (), uid , gid , [])
164167
165168
166169# Common base class for clients
167170
168171class Client :
169172
170- def init (self , host , prog , vers , port , type ):
173+ def init (self , host , prog , vers , port ):
171174 self .host = host
172175 self .prog = prog
173176 self .vers = vers
174177 self .port = port
175- self .type = type
176- self .sock = socket . socket ( socket . AF_INET , type )
178+ self .makesocket () # Assigns to self.sock
179+ self .bindsocket ( )
177180 self .sock .connect ((host , port ))
178181 self .lastxid = 0
179182 self .addpackers ()
180183 self .cred = None
181184 self .verf = None
182185 return self
183186
184- def Null (self ): # Procedure 0 is always like this
185- self .start_call (0 )
186- self .do_call (0 )
187- self .end_call ()
188-
189187 def close (self ):
190188 self .sock .close ()
191189
192- # Functions that may be overridden by specific derived classes
190+ def makesocket (self ):
191+ # This MUST be overridden
192+ raise RuntimeError , 'makesocket not defined'
193+
194+ def bindsocket (self ):
195+ # Override this to bind to a different port (e.g. reserved)
196+ self .sock .bind (('' , 0 ))
193197
194198 def addpackers (self ):
199+ # Override this to use derived classes from Packer/Unpacker
195200 self .packer = Packer ().init ()
196201 self .unpacker = Unpacker ().init ('' )
197202
198- def mkcred (self , proc ):
203+ def start_call (self , proc ):
204+ # Don't override this
205+ self .lastxid = xid = self .lastxid + 1
206+ cred = self .mkcred ()
207+ verf = self .mkverf ()
208+ p = self .packer
209+ p .reset ()
210+ p .pack_callheader (xid , self .prog , self .vers , proc , cred , verf )
211+
212+ def do_call (self , * rest ):
213+ # This MUST be overridden
214+ raise RuntimeError , 'do_call not defined'
215+
216+ def end_call (self ):
217+ # Don't override this
218+ self .unpacker .done ()
219+
220+ def mkcred (self ):
221+ # Override this to use more powerful credentials
199222 if self .cred == None :
200223 self .cred = (AUTH_NULL , make_auth_null ())
201224 return self .cred
202225
203- def mkverf (self , proc ):
226+ def mkverf (self ):
227+ # Override this to use a more powerful verifier
204228 if self .verf == None :
205229 self .verf = (AUTH_NULL , make_auth_null ())
206230 return self .verf
207231
232+ def Null (self ): # Procedure 0 is always like this
233+ self .start_call (0 )
234+ self .do_call (0 )
235+ self .end_call ()
236+
208237
209238# Record-Marking standard support
210239
@@ -243,18 +272,38 @@ def recvrecord(sock):
243272 return record
244273
245274
275+ # Try to bind to a reserved port (must be root)
276+
277+ last_resv_port_tried = None
278+ def bindresvport (sock , host ):
279+ global last_resv_port_tried
280+ FIRST , LAST = 600 , 1024 # Range of ports to try
281+ if last_resv_port_tried == None :
282+ import os
283+ last_resv_port_tried = FIRST + os .getpid () % (LAST - FIRST )
284+ for i in range (last_resv_port_tried , LAST ) + \
285+ range (FIRST , last_resv_port_tried ):
286+ last_resv_port_tried = i
287+ try :
288+ sock .bind ((host , i ))
289+ return last_resv_port_tried
290+ except socket .error , (errno , msg ):
291+ if errno <> 114 :
292+ raise socket .error , (errno , msg )
293+ raise RuntimeError , 'can\' t assign reserved port'
294+
295+
246296# Raw TCP-based client
247297
248298class RawTCPClient (Client ):
249299
250- def init (self , host , prog , vers , port ):
251- return Client .init (self , host , prog , vers , port , \
252- socket .SOCK_STREAM )
300+ def makesocket (self ):
301+ self .sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
253302
254303 def start_call (self , proc ):
255304 self .lastxid = xid = self .lastxid + 1
256- cred = self .mkcred (proc )
257- verf = self .mkverf (proc )
305+ cred = self .mkcred ()
306+ verf = self .mkverf ()
258307 p = self .packer
259308 p .reset ()
260309 p .pack_callheader (xid , self .prog , self .vers , proc , cred , verf )
@@ -280,14 +329,13 @@ def end_call(self):
280329
281330class RawUDPClient (Client ):
282331
283- def init (self , host , prog , vers , port ):
284- return Client .init (self , host , prog , vers , port , \
285- socket .SOCK_DGRAM )
332+ def makesocket (self ):
333+ self .sock = socket .socket (socket .AF_INET , socket .SOCK_DGRAM )
286334
287335 def start_call (self , proc ):
288336 self .lastxid = xid = self .lastxid + 1
289- cred = self .mkcred (proc )
290- verf = self .mkverf (proc )
337+ cred = self .mkcred ()
338+ verf = self .mkverf ()
291339 p = self .packer
292340 p .reset ()
293341 p .pack_callheader (xid , self .prog , self .vers , proc , cred , verf )
@@ -316,15 +364,15 @@ def do_call(self, *rest):
316364 count = count - 1
317365 if count < 0 : raise RuntimeError , 'timeout'
318366 if timeout < 25 : timeout = timeout * 2
319- print 'RESEND' , timeout , count
367+ ## print 'RESEND', timeout, count
320368 self .sock .send (call )
321369 continue
322370 reply = self .sock .recv (bufsize )
323371 u = self .unpacker
324372 u .reset (reply )
325373 xid , verf = u .unpack_replyheader ()
326374 if xid <> self .lastxid :
327- print 'BAD xid'
375+ ## print 'BAD xid'
328376 continue
329377 break
330378
@@ -334,9 +382,14 @@ def end_call(self):
334382
335383# Port mapper interface
336384
337- PMAP_PORT = 111
385+ # XXX CALLIT is not implemented
386+
387+ # Program number, version and (fixed!) port number
338388PMAP_PROG = 100000
339389PMAP_VERS = 2
390+ PMAP_PORT = 111
391+
392+ # Procedure numbers
340393PMAPPROC_NULL = 0 # (void) -> void
341394PMAPPROC_SET = 1 # (mapping) -> bool
342395PMAPPROC_UNSET = 2 # (mapping) -> bool
@@ -439,9 +492,9 @@ class TCPClient(RawTCPClient):
439492 def init (self , host , prog , vers ):
440493 pmap = TCPPortMapperClient ().init (host )
441494 port = pmap .Getport ((prog , vers , IPPROTO_TCP , 0 ))
495+ pmap .close ()
442496 if port == 0 :
443497 raise RuntimeError , 'program not registered'
444- pmap .close ()
445498 return RawTCPClient .init (self , host , prog , vers , port )
446499
447500
@@ -451,45 +504,37 @@ def init(self, host, prog, vers):
451504 pmap = UDPPortMapperClient ().init (host )
452505 port = pmap .Getport ((prog , vers , IPPROTO_UDP , 0 ))
453506 pmap .close ()
507+ if port == 0 :
508+ raise RuntimeError , 'program not registered'
454509 return RawUDPClient .init (self , host , prog , vers , port )
455510
456511
457512# Server classes
458513
514+ # These are not symmetric to the Client classes
515+ # XXX No attempt is made to provide authorization hooks yet
516+
459517class Server :
460518
461- def init (self , host , prog , vers , port , type ):
519+ def init (self , host , prog , vers , port ):
462520 self .host = host # Should normally be '' for default interface
463521 self .prog = prog
464522 self .vers = vers
465523 self .port = port # Should normally be 0 for random port
466- self .type = type # SOCK_STREAM or SOCK_DGRAM
467- self .sock = socket .socket (socket .AF_INET , type )
468- self .sock .bind ((host , port ))
524+ self .makesocket () # Assigns to self.sock and self.prot
525+ self .bindsocket ()
469526 self .host , self .port = self .sock .getsockname ()
470527 self .addpackers ()
471528 return self
472529
473530 def register (self ):
474- if self .type == socket .SOCK_STREAM :
475- type = IPPROTO_TCP
476- elif self .type == socket .SOCK_DGRAM :
477- type = IPPROTO_UDP
478- else :
479- raise ValueError , 'unknown protocol type'
480- mapping = self .prog , self .vers , type , self .port
531+ mapping = self .prog , self .vers , self .prot , self .port
481532 p = TCPPortMapperClient ().init (self .host )
482533 if not p .Set (mapping ):
483534 raise RuntimeError , 'register failed'
484535
485536 def unregister (self ):
486- if self .type == socket .SOCK_STREAM :
487- type = IPPROTO_TCP
488- elif self .type == socket .SOCK_DGRAM :
489- type = IPPROTO_UDP
490- else :
491- raise ValueError , 'unknown protocol type'
492- mapping = self .prog , self .vers , type , self .port
537+ mapping = self .prog , self .vers , self .prot , self .port
493538 p = TCPPortMapperClient ().init (self .host )
494539 if not p .Unset (mapping ):
495540 raise RuntimeError , 'unregister failed'
@@ -555,18 +600,25 @@ def turn_around(self):
555600 def handle_0 (self ): # Handle NULL message
556601 self .turn_around ()
557602
558- # Functions that may be overridden by specific derived classes
603+ def makesocket (self ):
604+ # This MUST be overridden
605+ raise RuntimeError , 'makesocket not defined'
606+
607+ def bindsocket (self ):
608+ # Override this to bind to a different port (e.g. reserved)
609+ self .sock .bind ((self .host , self .port ))
559610
560611 def addpackers (self ):
612+ # Override this to use derived classes from Packer/Unpacker
561613 self .packer = Packer ().init ()
562614 self .unpacker = Unpacker ().init ('' )
563615
564616
565617class TCPServer (Server ):
566618
567- def init (self , host , prog , vers , port ):
568- return Server . init ( self , host , prog , vers , port , \
569- socket . SOCK_STREAM )
619+ def makesocket (self ):
620+ self . sock = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
621+ self . prot = IPPROTO_TCP
570622
571623 def loop (self ):
572624 self .sock .listen (0 )
@@ -587,13 +639,13 @@ def session(self, connection):
587639
588640class UDPServer (Server ):
589641
590- def init (self , host , prog , vers , port ):
591- return Server . init ( self , host , prog , vers , port , \
592- socket . SOCK_DGRAM )
642+ def makesocket (self ):
643+ self . sock = socket . socket ( socket . AF_INET , socket . SOCK_DGRAM )
644+ self . prot = IPPROTO_UDP
593645
594646 def loop (self ):
595647 while 1 :
596- session ()
648+ self . session ()
597649
598650 def session (self ):
599651 call , host_port = self .sock .recvfrom (8192 )
@@ -629,7 +681,7 @@ def test():
629681
630682def testsvr ():
631683 # Simple test class -- proc 1 doubles its string argument as reply
632- class S (TCPServer ):
684+ class S (UDPServer ):
633685 def handle_1 (self ):
634686 arg = self .unpacker .unpack_string ()
635687 self .turn_around ()
@@ -655,7 +707,7 @@ def testclt():
655707 if sys .argv [1 :]: host = sys .argv [1 ]
656708 else : host = ''
657709 # Client for above server
658- class C (TCPClient ):
710+ class C (UDPClient ):
659711 def call_1 (self , arg ):
660712 self .start_call (1 )
661713 self .packer .pack_string (arg )
0 commit comments