Commit b417936d authored by Kurt B. Kaiser's avatar Kurt B. Kaiser

Reverse the RPC socket connection: Python execution server connects to

Idle client and localhost origin of connection is verified by client.
M PyShell.py
M rpc.py
M run.py
parent db40afaa
...@@ -193,24 +193,30 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -193,24 +193,30 @@ class ModifiedInterpreter(InteractiveInterpreter):
def spawn_subprocess(self): def spawn_subprocess(self):
port = 8833 port = 8833
addr = ("localhost", port) addr = ("localhost", port)
# Spawn the Python execution "server"
w = ['-W' + s for s in sys.warnoptions] w = ['-W' + s for s in sys.warnoptions]
args = [sys.executable] + w + ["-c", "__import__('run').main()", args = [sys.executable] + w + ["-c", "__import__('run').main()",
str(port)] str(port)]
self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args) self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args)
for i in range(5): # Idle starts listening for connection on localhost, retry since
# Idle may be restarted before port is available for rebinding
# XXX 25 July 2002 KBK Find out what is causing the delayed release!
for i in range(12):
time.sleep(i) time.sleep(i)
try: try:
self.rpcclt = rpc.RPCClient(addr) self.rpcclt = rpc.RPCClient(addr)
break break
except socket.error, err: except socket.error, err:
if i > 3: if i < 5:
print >>sys.__stderr__, "Socket error:", err, "; retry..." print>>sys.__stderr__, ". ",
else:
print>>sys.__stderr__,"\nIdle socket error: " + err[1]\
+ ", retrying..."
else: else:
# XXX Make this a dialog? #GvR
print >>sys.__stderr__, "Can't spawn subprocess!"
# XXX Add Stephen's error msg, resolve the two later... KBK 09Jun02
display_port_binding_error() display_port_binding_error()
return return
# Accept the connection from the Python execution server
self.rpcclt.accept()
self.rpcclt.register("stdin", self.tkconsole) self.rpcclt.register("stdin", self.tkconsole)
self.rpcclt.register("stdout", self.tkconsole.stdout) self.rpcclt.register("stdout", self.tkconsole.stdout)
self.rpcclt.register("stderr", self.tkconsole.stderr) self.rpcclt.register("stderr", self.tkconsole.stderr)
......
# ASCII-art documentation """RPC Implemention, originally written for the Python Idle IDE
#
# +---------------------------------+ +----------+ For security reasons, GvR requested that Idle's Python execution server process
# | SocketServer.BaseRequestHandler | | SocketIO | connect to the Idle process, which listens for the connection. Since Idle has
# +---------------------------------+ +----------+ has only one client per server, this was not a limitation.
# ^ ^ ^
# | | | +---------------------------------+ +-------------+
# | + -------------------+ | | SocketServer.BaseRequestHandler | | SocketIO |
# | | | +---------------------------------+ +-------------+
# +-------------------------+ +-----------------+ ^ | register() |
# | RPCHandler | | RPCClient | | | unregister()|
# |-------------------------| |-----------------| | +-------------+
# | register() | | remotecall() | | ^ ^
# | unregister() | | register() | | | |
# | | | unregister() | | + -------------------+ |
# | | | get_remote_proxy| | | |
# +-------------------------+ +-----------------+ +-------------------------+ +-----------------+
# | RPCHandler | | RPCClient |
import sys | [attribute of RPCServer]| | |
+-------------------------+ +-----------------+
The RPCServer handler class is expected to provide register/unregister methods.
RPCHandler inherits the mix-in class SocketIO, which provides these methods.
See the Idle run.main() docstring for further information on how this was
accomplished in Idle.
"""
import sys
import socket import socket
import select import select
import SocketServer import SocketServer
...@@ -55,40 +66,29 @@ class RPCServer(SocketServer.TCPServer): ...@@ -55,40 +66,29 @@ class RPCServer(SocketServer.TCPServer):
def __init__(self, addr, handlerclass=None): def __init__(self, addr, handlerclass=None):
if handlerclass is None: if handlerclass is None:
handlerclass = RPCHandler handlerclass = RPCHandler
# XXX KBK 25Jun02 Not used in Idlefork, see register/unregister note below. # XXX KBK 25Jun02 Not used in Idlefork.
# self.objtable = objecttable # self.objtable = objecttable
SocketServer.TCPServer.__init__(self, addr, handlerclass) SocketServer.TCPServer.__init__(self, addr, handlerclass)
# XXX KBK 25Jun02 Following method is not used (yet) def server_bind(self):
# def verify_request(self, request, client_address): "Override TCPServer method, no bind() phase for connecting entity"
# host, port = client_address pass
# if host != "127.0.0.1":
# print "Disallowed host:", host def server_activate(self):
# return 0 """Override TCPServer method, connect() instead of listen()
# else:
# return 1 Due to the reversed connection, self.server_address is actually the
address of the Idle Client to which we are connecting.
# XXX KBK 25Jun02 The handlerclass is expected to provide register/unregister
# methods. In Idle, RPCServer is instantiated with """
# handlerclass MyHandler, which in turn inherits the self.socket.connect(self.server_address)
# register/unregister methods from the mix-in class SocketIO.
# It is true that this is asymmetric with the RPCClient's use def get_request(self):
# of register/unregister, but I guess that's how a SocketServer "Override TCPServer method, return already connected socket"
# is supposed to work. return self.socket, self.server_address
# Exactly how this gets set up is convoluted. When the
# TCPServer is instantiated, it creates an instance of # XXX The following two methods are not currently used in Idlefork.
# run.MyHandler and calls its handle() method. handle()
# instantiates a run.Executive, passing it a reference to the
# MyHandler object. That reference is saved as an attribute of
# the Executive instance. The Executive methods have access to
# the reference and can pass it on to entities that they
# command (e.g. RemoteDebugger.Debugger.start_debugger()). The
# latter, in turn, can call MyHandler(SocketIO)
# register/unregister methods via the reference to register and
# unregister themselves. Whew.
# The following two methods are not currently used in Idlefork.
# def register(self, oid, object): # def register(self, oid, object):
# self.objtable[oid] = object # self.objtable[oid] = object
...@@ -364,6 +364,8 @@ class SocketIO: ...@@ -364,6 +364,8 @@ class SocketIO:
cv.notify() cv.notify()
self.statelock.release() self.statelock.release()
continue continue
#----------------- end class SocketIO --------------------
class RemoteObject: class RemoteObject:
# Token mix-in class # Token mix-in class
...@@ -388,15 +390,8 @@ class RPCHandler(SocketServer.BaseRequestHandler, SocketIO): ...@@ -388,15 +390,8 @@ class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):
SocketIO.__init__(self, sock) SocketIO.__init__(self, sock)
SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr) SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr)
def setup(self):
SocketServer.BaseRequestHandler.setup(self)
print >>sys.__stderr__, "Connection from", self.client_address
def finish(self):
print >>sys.__stderr__, "End connection from", self.client_address
SocketServer.BaseRequestHandler.finish(self)
def handle(self): def handle(self):
"handle() method required by SocketServer"
self.mainloop() self.mainloop()
def get_remote_proxy(self, oid): def get_remote_proxy(self, oid):
...@@ -404,12 +399,21 @@ class RPCHandler(SocketServer.BaseRequestHandler, SocketIO): ...@@ -404,12 +399,21 @@ class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):
class RPCClient(SocketIO): class RPCClient(SocketIO):
nextseq = 1 # Requests coming from the client are odd nextseq = 1 # Requests coming from the client are odd numbered
def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM): def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
sock = socket.socket(family, type) self.sock = socket.socket(family, type)
sock.connect(address) self.sock.bind(address)
SocketIO.__init__(self, sock) self.sock.listen(1)
def accept(self):
newsock, address = self.sock.accept()
if address[0] == '127.0.0.1':
print>>sys.__stderr__, "Idle accepted connection from ", address
SocketIO.__init__(self, newsock)
else:
print>>sys.__stderr__, "Invalid host: ", address
raise socket.error
def get_remote_proxy(self, oid): def get_remote_proxy(self, oid):
return RPCProxy(self, oid) return RPCProxy(self, oid)
...@@ -477,6 +481,7 @@ class MethodProxy: ...@@ -477,6 +481,7 @@ class MethodProxy:
# #
def testServer(addr): def testServer(addr):
# XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
class RemotePerson: class RemotePerson:
def __init__(self,name): def __init__(self,name):
self.name = name self.name = name
...@@ -505,10 +510,8 @@ def testServer(addr): ...@@ -505,10 +510,8 @@ def testServer(addr):
svr.handle_request() # process once only svr.handle_request() # process once only
def testClient(addr): def testClient(addr):
"demonstrates RPC Client"
# # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
# demonstrates RPC Client
#
import time import time
clt=RPCClient(addr) clt=RPCClient(addr)
thomas = clt.get_remote_proxy("thomas") thomas = clt.get_remote_proxy("thomas")
...@@ -523,7 +526,6 @@ def testClient(addr): ...@@ -523,7 +526,6 @@ def testClient(addr):
print "Done." print "Done."
print print
time.sleep(2) time.sleep(2)
# demonstrates remote server calling local instance # demonstrates remote server calling local instance
class LocalPerson: class LocalPerson:
def __init__(self,name): def __init__(self,name):
......
import sys import sys
import time
import socket
import rpc import rpc
def main(): def main():
"""Start the Python execution server in a subprocess
In Idle, RPCServer is instantiated with handlerclass MyHandler, which
inherits register/unregister methods from RPCHandler via the mix-in class
SocketIO.
When the RPCServer is instantiated, the TCPServer initialization creates an
instance of run.MyHandler and calls its handle() method. handle()
instantiates a run.Executive, passing it a reference to the MyHandler
object. That reference is saved as an attribute of the Executive instance.
The Executive methods have access to the reference and can pass it on to
entities that they command (e.g. RemoteDebugger.Debugger.start_debugger()).
The latter, in turn, can call MyHandler(SocketIO) register/unregister
methods via the reference to register and unregister themselves.
"""
port = 8833 port = 8833
if sys.argv[1:]: if sys.argv[1:]:
port = int(sys.argv[1]) port = int(sys.argv[1])
sys.argv[:] = [""] sys.argv[:] = [""]
addr = ("localhost", port) addr = ("localhost", port)
svr = rpc.RPCServer(addr, MyHandler) for i in range(12):
time.sleep(i)
try:
svr = rpc.RPCServer(addr, MyHandler)
break
except socket.error, err:
if i < 5:
print>>sys.__stderr__, ".. ",
else:
print>>sys.__stderr__,"\nPython subprocess socket error: "\
+ err[1] + ", retrying...."
else:
print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
sys.exit()
svr.handle_request() # A single request only svr.handle_request() # A single request only
class MyHandler(rpc.RPCHandler): class MyHandler(rpc.RPCHandler):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment