You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.8KB

  1. from __future__ import print_function
  2. from code import InteractiveConsole
  3. import errno
  4. import socket
  5. import sys
  6. import errno
  7. import traceback
  8. import eventlet
  9. from eventlet import hubs
  10. from eventlet.support import greenlets, get_errno
  11. try:
  12. sys.ps1
  13. except AttributeError:
  14. sys.ps1 = '>>> '
  15. try:
  16. sys.ps2
  17. except AttributeError:
  18. sys.ps2 = '... '
  19. class FileProxy(object):
  20. def __init__(self, f):
  21. self.f = f
  22. def isatty(self):
  23. return True
  24. def flush(self):
  25. pass
  26. def write(self, data, *a, **kw):
  27. self.f.write(data, *a, **kw)
  28. self.f.flush()
  29. def readline(self, *a):
  30. return self.f.readline(*a).replace('\r\n', '\n')
  31. def __getattr__(self, attr):
  32. return getattr(self.f, attr)
  33. # @@tavis: the `locals` args below mask the built-in function. Should
  34. # be renamed.
  35. class SocketConsole(greenlets.greenlet):
  36. def __init__(self, desc, hostport, locals):
  37. self.hostport = hostport
  38. self.locals = locals
  39. # mangle the socket
  40. self.desc = FileProxy(desc)
  41. greenlets.greenlet.__init__(self)
  42. def run(self):
  43. try:
  44. console = InteractiveConsole(self.locals)
  45. console.interact()
  46. finally:
  47. self.switch_out()
  48. self.finalize()
  49. def switch(self, *args, **kw):
  50. self.saved = sys.stdin, sys.stderr, sys.stdout
  51. sys.stdin = sys.stdout = sys.stderr = self.desc
  52. greenlets.greenlet.switch(self, *args, **kw)
  53. def switch_out(self):
  54. sys.stdin, sys.stderr, sys.stdout = self.saved
  55. def finalize(self):
  56. # restore the state of the socket
  57. self.desc = None
  58. if len(self.hostport) >= 2:
  59. host = self.hostport[0]
  60. port = self.hostport[1]
  61. print("backdoor closed to %s:%s" % (host, port,))
  62. else:
  63. print('backdoor closed')
  64. def backdoor_server(sock, locals=None):
  65. """ Blocking function that runs a backdoor server on the socket *sock*,
  66. accepting connections and running backdoor consoles for each client that
  67. connects.
  68. The *locals* argument is a dictionary that will be included in the locals()
  69. of the interpreters. It can be convenient to stick important application
  70. variables in here.
  71. """
  72. listening_on = sock.getsockname()
  73. if sock.family == socket.AF_INET:
  74. # Expand result to IP + port
  75. listening_on = '%s:%s' % listening_on
  76. elif sock.family == socket.AF_INET6:
  77. ip, port, _, _ = listening_on
  78. listening_on = '%s:%s' % (ip, port,)
  79. # No action needed if sock.family == socket.AF_UNIX
  80. print("backdoor server listening on %s" % (listening_on,))
  81. try:
  82. try:
  83. while True:
  84. socketpair = sock.accept()
  85. backdoor(socketpair, locals)
  86. except socket.error as e:
  87. # Broken pipe means it was shutdown
  88. if get_errno(e) != errno.EPIPE:
  89. raise
  90. finally:
  91. sock.close()
  92. def backdoor(conn_info, locals=None):
  93. """Sets up an interactive console on a socket with a single connected
  94. client. This does not block the caller, as it spawns a new greenlet to
  95. handle the console. This is meant to be called from within an accept loop
  96. (such as backdoor_server).
  97. """
  98. conn, addr = conn_info
  99. if conn.family == socket.AF_INET:
  100. host, port = addr
  101. print("backdoor to %s:%s" % (host, port))
  102. elif conn.family == socket.AF_INET6:
  103. host, port, _, _ = addr
  104. print("backdoor to %s:%s" % (host, port))
  105. else:
  106. print('backdoor opened')
  107. fl = conn.makefile("rw")
  108. console = SocketConsole(fl, addr, locals)
  109. hub = hubs.get_hub()
  110. hub.schedule_call_global(0, console.switch)
  111. if __name__ == '__main__':
  112. backdoor_server(eventlet.listen(('127.0.0.1', 9000)), {})