channel.getpeername(), (local_host, local_port)) th = ThreadHandler('fwd', _forwarder, channel, sock) threads.append(th) transport = connections[env.host_string].get_transport() transport.request_port_forward(remote_bind_address, remote_port, handler=accept) try: yield finally: for sock, chan, th in zip(sockets, channels, threads): sock.close() chan.close() th.thread.join() th.raise_if_needed() transport.cancel_port_forward(remote_bind_address, remote_port) quiet = lambda: settings(hide('everything'), warn_only=True) quiet.__doc__ = """ Alias to ``settings(hide('everything'), warn_only=True)``. Useful for wrapping remote interrogative commands which you expect to fail occasionally, and/or which you want to silence. Example:: with quiet(): have_build_dir = run("test -e /tmp/build").succeeded
listening_socket, accept, connections[env.host_string].get_transport(), (remote_host, remote_port)) try: yield finally: for sock, chan, th in zip(sockets, channels, threads): sock.close() chan.close() th.thread.join() th.raise_if_needed() listening_socket.close() listening_thread.thread.join() listening_thread.raise_if_needed() @documented_contextmanager def remote_tunnel(remote_port, local_port=None, local_host="localhost", remote_bind_address="127.0.0.1"): """ Create a tunnel forwarding a locally-visible port to the remote target. For example, you can let the remote host access a database that is installed on the client host:: # Map localhost:6379 on the server to localhost:6379 on the client, # so that the remote 'redis-cli' program ends up speaking to the local # redis-server. with remote_tunnel(6379): run("redis-cli -i")
class LocalTunnel(object): """ Adapted from PR #939 of Fabric: https://github.com/fabric/fabric/pull/939 Forward a local port to a given host and port on the remote side. :param remote_port: Remote port to forward connections to. :type remote_port: int :param remote_host: Host to connect to. Optional, default is ``localhost``. :type remote_host: unicode :param bind_port: Local port to bind to. Optional, default is same as ``remote_port``. :type bind_port: int :param bind_host: Local address to bind to. Optional, default is ``localhost``. """ def __init__(self, remote_port, remote_host=None, bind_port=None, bind_host=None, remote_cmd=None): self.remote_port = remote_port self.remote_host = remote_host or 'localhost' self.bind_port = bind_port or remote_port self.bind_host = bind_host or 'localhost' self.remote_cmd = remote_cmd self.sockets = [] self.channels = [] self.threads = [] self.listening_socket = None self.listening_thread = None def get_channel(self, transport, remote_addr, local_peer): channel = transport.open_channel('direct-tcpip', remote_addr, local_peer) if channel is None: raise Exception('Incoming request to %s:%d was rejected by the SSH server.' % remote_addr) return channel @needs_host def connect(self): def listener_thread_main(thead_sock, callback, *a, **kw): try: while True: selsockets = select.select([thead_sock], [], [], 1) if thead_sock in selsockets[0]: callback(thead_sock, *a, **kw) except socket.error as e: #Sockets return bad file descriptor if closed. #Maybe there is a cleaner way of doing this? if e.errno != socket.EBADF: raise except select.error as e: if e[0] != socket.EBADF: raise listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listening_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listening_socket.bind((self.bind_host, self.bind_port)) listening_socket.listen(1) def accept(listen_sock, transport, remote_addr): accept_sock, local_peer = listen_sock.accept() channel = self.get_channel(transport, remote_addr, local_peer) handler = ThreadHandler('fwd', _forwarder, channel, accept_sock) self.sockets.append(accept_sock) self.channels.append(channel) self.threads.append(handler) self.sockets = [] self.channels = [] self.threads = [] self.listening_socket = listening_socket self.listening_thread = ThreadHandler('local_bind', listener_thread_main, listening_socket, accept, connections[env.host_string].get_transport(), (self.remote_host, self.remote_port)) def close(self): for sock, chan, th in zip(self.sockets, self.channels, self.threads): sock.close() if not chan.closed: chan.close() th.thread.join() th.raise_if_needed() self.listening_socket.close() self.listening_thread.thread.join() self.listening_thread.raise_if_needed()