codekingpro/portable-devtools
114k
1from __future__ import print_function2 3from code import InteractiveConsole4import errno5import socket6import sys7import errno8import traceback9 10import eventlet11from eventlet import hubs12from eventlet.support import greenlets, get_errno13 14try:15 sys.ps116except AttributeError:17 sys.ps1 = '>>> '18try:19 sys.ps220except AttributeError:21 sys.ps2 = '... '22 23 24class FileProxy(object):25 def __init__(self, f):26 self.f = f27 28 def isatty(self):29 return True30 31 def flush(self):32 pass33 34 def write(self, data, *a, **kw):35 try:36 self.f.write(data, *a, **kw)37 self.f.flush()38 except socket.error as e:39 if get_errno(e) != errno.EPIPE:40 raise41 42 def readline(self, *a):43 return self.f.readline(*a).replace('\r\n', '\n')44 45 def __getattr__(self, attr):46 return getattr(self.f, attr)47 48 49# @@tavis: the `locals` args below mask the built-in function. Should50# be renamed.51class SocketConsole(greenlets.greenlet):52 def __init__(self, desc, hostport, locals):53 self.hostport = hostport54 self.locals = locals55 # mangle the socket56 self.desc = FileProxy(desc)57 greenlets.greenlet.__init__(self)58 59 def run(self):60 try:61 console = InteractiveConsole(self.locals)62 console.interact()63 finally:64 self.switch_out()65 self.finalize()66 67 def switch(self, *args, **kw):68 self.saved = sys.stdin, sys.stderr, sys.stdout69 sys.stdin = sys.stdout = sys.stderr = self.desc70 greenlets.greenlet.switch(self, *args, **kw)71 72 def switch_out(self):73 sys.stdin, sys.stderr, sys.stdout = self.saved74 75 def finalize(self):76 # restore the state of the socket77 self.desc = None78 if len(self.hostport) >= 2:79 host = self.hostport[0]80 port = self.hostport[1]81 print("backdoor closed to %s:%s" % (host, port,))82 else:83 print('backdoor closed')84 85 86def backdoor_server(sock, locals=None):87 """ Blocking function that runs a backdoor server on the socket *sock*,88 accepting connections and running backdoor consoles for each client that89 connects.90 91 The *locals* argument is a dictionary that will be included in the locals()92 of the interpreters. It can be convenient to stick important application93 variables in here.94 """95 listening_on = sock.getsockname()96 if sock.family == socket.AF_INET:97 # Expand result to IP + port98 listening_on = '%s:%s' % listening_on99 elif sock.family == socket.AF_INET6:100 ip, port, _, _ = listening_on101 listening_on = '%s:%s' % (ip, port,)102 # No action needed if sock.family == socket.AF_UNIX103 104 print("backdoor server listening on %s" % (listening_on,))105 try:106 while True:107 socketpair = None108 try:109 socketpair = sock.accept()110 backdoor(socketpair, locals)111 except socket.error as e:112 # Broken pipe means it was shutdown113 if get_errno(e) != errno.EPIPE:114 raise115 finally:116 if socketpair:117 socketpair[0].close()118 finally:119 sock.close()120 121 122def backdoor(conn_info, locals=None):123 """Sets up an interactive console on a socket with a single connected124 client. This does not block the caller, as it spawns a new greenlet to125 handle the console. This is meant to be called from within an accept loop126 (such as backdoor_server).127 """128 conn, addr = conn_info129 if conn.family == socket.AF_INET:130 host, port = addr131 print("backdoor to %s:%s" % (host, port))132 elif conn.family == socket.AF_INET6:133 host, port, _, _ = addr134 print("backdoor to %s:%s" % (host, port))135 else:136 print('backdoor opened')137 fl = conn.makefile("rw")138 console = SocketConsole(fl, addr, locals)139 hub = hubs.get_hub()140 hub.schedule_call_global(0, console.switch)141 142 143if __name__ == '__main__':144 backdoor_server(eventlet.listen(('127.0.0.1', 9000)), {})145 