forward.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #
  2. # This file is adapted from a paramiko demo, and thus licensed under LGPL 2.1.
  3. # Original Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
  4. # Edits Copyright (C) 2010 The IPython Team
  5. #
  6. # Paramiko is free software; you can redistribute it and/or modify it under the
  7. # terms of the GNU Lesser General Public License as published by the Free
  8. # Software Foundation; either version 2.1 of the License, or (at your option)
  9. # any later version.
  10. #
  11. # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
  12. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  13. # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  14. # details.
  15. #
  16. # You should have received a copy of the GNU Lesser General Public License
  17. # along with Paramiko; if not, see <https://www.gnu.org/licenses/>.
  18. """
  19. Sample script showing how to do local port forwarding over paramiko.
  20. This script connects to the requested SSH server and sets up local port
  21. forwarding (the openssh -L option) from a local port through a tunneled
  22. connection to a destination reachable from the SSH server machine.
  23. """
  24. import logging
  25. import select
  26. import socketserver
  27. logger = logging.getLogger('ssh')
  28. class ForwardServer(socketserver.ThreadingTCPServer):
  29. daemon_threads = True
  30. allow_reuse_address = True
  31. class Handler(socketserver.BaseRequestHandler):
  32. def handle(self):
  33. try:
  34. chan = self.ssh_transport.open_channel(
  35. 'direct-tcpip',
  36. (self.chain_host, self.chain_port),
  37. self.request.getpeername(),
  38. )
  39. except Exception as e:
  40. logger.debug(
  41. 'Incoming request to %s:%d failed: %r',
  42. self.chain_host,
  43. self.chain_port,
  44. e,
  45. )
  46. return
  47. if chan is None:
  48. logger.debug(
  49. 'Incoming request to %s:%d was rejected by the SSH server.',
  50. self.chain_host,
  51. self.chain_port,
  52. )
  53. return
  54. logger.debug(
  55. f'Connected! Tunnel open {self.request.getpeername()!r} -> {chan.getpeername()!r} -> {(self.chain_host, self.chain_port)!r}'
  56. )
  57. while True:
  58. r, w, x = select.select([self.request, chan], [], [])
  59. if self.request in r:
  60. data = self.request.recv(1024)
  61. if len(data) == 0:
  62. break
  63. chan.send(data)
  64. if chan in r:
  65. data = chan.recv(1024)
  66. if len(data) == 0:
  67. break
  68. self.request.send(data)
  69. chan.close()
  70. self.request.close()
  71. logger.debug('Tunnel closed ')
  72. def forward_tunnel(local_port, remote_host, remote_port, transport):
  73. # this is a little convoluted, but lets me configure things for the Handler
  74. # object. (SocketServer doesn't give Handlers any way to access the outer
  75. # server normally.)
  76. class SubHander(Handler):
  77. chain_host = remote_host
  78. chain_port = remote_port
  79. ssh_transport = transport
  80. ForwardServer(('127.0.0.1', local_port), SubHander).serve_forever()
  81. __all__ = ['forward_tunnel']