14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""RPC Server module."""
24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport sys
44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport socket
54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport pickle
64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmfrom fnmatch import fnmatch
74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmfrom repr import repr
84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Default verbosity (0 = silent, 1 = print connections, 2 = print requests too)
114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmVERBOSE = 1
124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass Server:
154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """RPC Server class.  Derive a class to implement a particular service."""
174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, address, verbose = VERBOSE):
194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if type(address) == type(0):
204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            address = ('', address)
214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._address = address
224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._verbose = verbose
234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._socket = None
244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._socket.bind(address)
264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._socket.listen(1)
274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._listening = 1
284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _setverbose(self, verbose):
304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._verbose = verbose
314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __del__(self):
334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._close()
344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _close(self):
364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._listening = 0
374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self._socket:
384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._socket.close()
394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._socket = None
404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _serverloop(self):
424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while self._listening:
434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._serve()
444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _serve(self):
464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self._verbose: print "Wait for connection ..."
474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn, address = self._socket.accept()
484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self._verbose: print "Accepted connection from %s" % repr(address)
494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not self._verify(conn, address):
504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            print "*** Connection from %s refused" % repr(address)
514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn.close()
524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return
534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        rf = conn.makefile('r')
544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        wf = conn.makefile('w')
554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        ok = 1
564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while ok:
574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            wf.flush()
584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self._verbose > 1: print "Wait for next request ..."
594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            ok = self._dorequest(rf, wf)
604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    _valid = ['192.16.201.*', '192.16.197.*', '132.151.1.*', '129.6.64.*']
624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _verify(self, conn, address):
644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        host, port = address
654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for pat in self._valid:
664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if fnmatch(host, pat): return 1
674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return 0
684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _dorequest(self, rf, wf):
704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        rp = pickle.Unpickler(rf)
714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            request = rp.load()
734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except EOFError:
744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return 0
754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self._verbose > 1: print "Got request: %s" % repr(request)
764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            methodname, args, id = request
784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if '.' in methodname:
794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                reply = (None, self._special(methodname, args), id)
804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            elif methodname[0] == '_':
814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                raise NameError, "illegal method name %s" % repr(methodname)
824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            else:
834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                method = getattr(self, methodname)
844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                reply = (None, apply(method, args), id)
854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except:
864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            reply = (sys.exc_type, sys.exc_value, id)
874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if id < 0 and reply[:2] == (None, None):
884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self._verbose > 1: print "Suppress reply"
894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return 1
904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self._verbose > 1: print "Send reply: %s" % repr(reply)
914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        wp = pickle.Pickler(wf)
924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        wp.dump(reply)
934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return 1
944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _special(self, methodname, args):
964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if methodname == '.methods':
974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not hasattr(self, '_methods'):
984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self._methods = tuple(self._listmethods())
994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return self._methods
1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise NameError, "unrecognized special method name %s" % repr(methodname)
1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _listmethods(self, cl=None):
1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not cl: cl = self.__class__
1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        names = cl.__dict__.keys()
1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        names = filter(lambda x: x[0] != '_', names)
1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        names.sort()
1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for base in cl.__bases__:
1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            basenames = self._listmethods(base)
1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            basenames = filter(lambda x, names=names: x not in names, basenames)
1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            names[len(names):] = basenames
1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return names
1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmfrom security import Security
1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass SecureServer(Server, Security):
1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, *args):
1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        apply(Server.__init__, (self,) + args)
1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Security.__init__(self)
1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _verify(self, conn, address):
1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        import string
1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        challenge = self._generate_challenge()
1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn.send("%d\n" % challenge)
1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        response = ""
1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while "\n" not in response and len(response) < 100:
1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            data = conn.recv(100)
1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not data:
1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                break
1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            response = response + data
1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            response = string.atol(string.strip(response))
1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except string.atol_error:
1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self._verbose > 0:
1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                print "Invalid response syntax", repr(response)
1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return 0
1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not self._compare_challenge_response(challenge, response):
1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self._verbose > 0:
1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                print "Invalid response value", repr(response)
1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return 0
1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self._verbose > 1:
1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            print "Response matches challenge.  Go ahead!"
1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return 1
146