Exemplo n.º 1
0
def execute_cmd(options):
    channel = default_channel()
    timeout = options.command_timeout

    with char_buffered(sys.stdin):
        # Combine stdout and stderr to get around oddball mixing issues
        channel.set_combine_stderr(False)

        # Assume pty use, and allow overriding of this either via kwarg or env
        # var.  (invoke_shell always wants a pty no matter what.)
        using_pty = True
        # Request pty with size params (default to 80x24, obtain real
        # parameters if on POSIX platform)
        channel.get_pty(width=80, height=24)

        channel.invoke_shell()
        while not channel.recv_ready():
            time.sleep(0.01)

        workers = (
            ThreadHandler('out', output_loop, channel, "recv",
                capture=None, stream=sys.stdout, timeout=timeout, cmd=options.command),
            ThreadHandler('err', output_loop, channel, "recv_stderr",
                capture=None, stream=sys.stderr, timeout=timeout),
            ThreadHandler('in', input_loop, channel, using_pty)
        )

        while True:
            if channel.exit_status_ready():
                break
            else:
                # Check for thread exceptions here so we can raise ASAP
                # (without chance of getting blocked by, or hidden by an
                # exception within, recv_exit_status())
                for worker in workers:
                    worker.raise_if_needed()
            try:
                time.sleep(ssh.io_sleep)
            except KeyboardInterrupt:
                channel.send('\x03')

        # Obtain exit code of remote program now that we're done.
        status = channel.recv_exit_status()

        # Wait for threads to exit so we aren't left with stale threads
        for worker in workers:
            worker.thread.join()
            worker.raise_if_needed()

        # Close channel
        channel.close()

        return status
Exemplo n.º 2
0
    def interact(self):
        """
        Invoke shell interactive mode
        """
        with char_buffered(sys.stdin):
            # Init stdout, stderr capturing. Must use lists instead of strings as
            # strings are immutable and we're using these as pass-by-reference
            stdout_buf, stderr_buf = [], []
            workers = (ThreadHandler('out',
                                     output_loop,
                                     self.channel,
                                     "recv",
                                     capture=stdout_buf,
                                     stream=self.stdout,
                                     timeout=self.timeout),
                       ThreadHandler('err',
                                     output_loop,
                                     self.channel,
                                     "recv_stderr",
                                     capture=stderr_buf,
                                     stream=self.stderr,
                                     timeout=self.timeout),
                       ThreadHandler('in', input_loop, self.channel, True))

            while True:
                if self.channel.exit_status_ready():
                    break
                else:
                    # Check for thread exceptions here so we can raise ASAP
                    # (without chance of getting blocked by, or hidden by an
                    # exception within, recv_exit_status())
                    for worker in workers:
                        worker.raise_if_needed()
                try:
                    time.sleep(ssh.io_sleep)
                except KeyboardInterrupt, e:
                    raise e

            # Obtain exit code of remote program now that we're done.
            status = self.channel.recv_exit_status()

            # Wait for threads to exit so we aren't left with stale threads
            for worker in workers:
                worker.thread.join()
                worker.raise_if_needed()

            # Close channel
            self.channel.close()

            return stdout_buf, stderr_buf, status
Exemplo n.º 3
0
def remote(local_port,
           remote_port=0,
           local_host="localhost",
           remote_bind_address="127.0.0.1"):
    """
    Create a tunnel forwarding a locally-visible port to the remote target.
    """
    sockets = []
    channels = []
    threads = []

    def accept(channel, (src_addr, src_port), (dest_addr, dest_port)):
        channels.append(channel)
        sock = socket.socket()
        sockets.append(sock)

        try:
            sock.connect((local_host, local_port))
        except Exception as e:
            try:
                channel.close()
            except Exception as e2:
                close_error = ' (While trying to close channel: {0})'.format(
                    e2)
            else:
                close_error = ''
            raise NonRecoverableError(
                '[{0}] rtunnel: cannot connect to {1}:{2} ({3}){4}'.format(
                    fabric_api.env.host_string, local_host, local_port, e,
                    close_error))

        th = ThreadHandler('fwd', _forwarder, channel, sock)
        threads.append(th)
Exemplo n.º 4
0
def remote(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.
    """
    if local_port is None:
        local_port = remote_port

    sockets = []
    channels = []
    threads = []

    def accept(channel, (src_addr, src_port), (dest_addr, dest_port)):
        channels.append(channel)
        sock = socket.socket()
        sockets.append(sock)

        try:
            sock.connect((local_host, local_port))
        except Exception as e:
            raise NonRecoverableError(
                '[{0}] rtunnel: cannot connect to {1}:{2} ({3})'.format(
                    fabric_api.env.host_string, local_host,
                    local_port, e.message))
            channel.close()
            return

        th = ThreadHandler('fwd', _forwarder, channel, sock)
        threads.append(th)
Exemplo n.º 5
0
        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)
Exemplo n.º 6
0
    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))
Exemplo n.º 7
0
def _execute_local(command, shell=True, combine_stderr=None):
    '''
    Local implementation of fabric.operations._execute using subprocess.
    '''
    if combine_stderr is None:
        combine_stderr = env.combine_stderr
    stderr = subprocess.STDOUT if combine_stderr else subprocess.PIPE

    process = subprocess.Popen(command,
                               shell=shell,
                               stdout=subprocess.PIPE,
                               stderr=stderr)

    # Create handlers to buffer and store output with fabric's output_loop()
    capture_out, capture_err = [], []
    channel = MockChannel(process.stdout, process.stderr)
    workers = (
        ThreadHandler('out', output_loop, channel, "recv", capture_out),
        ThreadHandler('err', output_loop, channel, "recv_stderr", capture_err),
    )

    # Wait for process to finish, raising on any errors
    while process.poll() is None:
        for worker in workers:
            e = worker.exception
            if e:
                raise e[0], e[1], e[2]
        time.sleep(ssh.io_sleep)

    # Join threads to make sure all output was read
    for worker in workers:
        worker.thread.join()

    out = ''.join(capture_out).rstrip('\n')
    err = ''.join(capture_err).rstrip('\n')
    return out, err, process.returncode
Exemplo n.º 8
0
def remote(local_port, remote_port=0, local_host="localhost",
           remote_bind_address="127.0.0.1"):
    """
    Create a tunnel forwarding a locally-visible port to the remote target.
    """
    sockets = []
    channels = []
    threads = []

    def accept(channel, (src_addr, src_port), (dest_addr, dest_port)):
        # This seemingly innocent statement seems to be doing nothing
        # but the truth is far from it!
        # calling fileno() on a paramiko channel the first time, creates
        # the required plumbing to make the channel valid for select.
        # While this would generally happen implicitly inside the _forwarder
        # function when select is called, it may already be too late and may
        # cause the select loop to hang.
        # Specifically, when new data arrives to the channel, a flag is set
        # on an "event" object which is what makes the select call work.
        # problem is this will only happen if the event object is not None
        # and it will be not-None only after channel.fileno() has been called
        # for the first time. If we wait until _forwarder calls select for the
        # first time it may be after initial data has reached the channel.
        # calling it explicitly here in the paramiko transport main event loop
        # guarantees this will not happen.
        channel.fileno()

        channels.append(channel)
        sock = socket.socket()
        sockets.append(sock)

        try:
            sock.connect((local_host, local_port))
        except Exception as e:
            try:
                channel.close()
            except Exception as e2:
                close_error = ' (While trying to close channel: {0})'.format(
                    e2)
            else:
                close_error = ''
            raise NonRecoverableError(
                '[{0}] rtunnel: cannot connect to {1}:{2} ({3}){4}'.format(
                    fabric_api.env.host_string, local_host,
                    local_port, e, close_error))

        th = ThreadHandler('fwd', _forwarder, channel, sock)
        threads.append(th)
Exemplo n.º 9
0
    def accept(channel, src_address, dest_address):
        (src_addr, src_port) = src_address
        (dest_addr, dest_port) = dest_address
        channels.append(channel)
        sock = socket.socket()
        sockets.append(sock)

        try:
            sock.connect((local_host, local_port))
        except Exception as e:
            print("[%s] rtunnel: cannot connect to %s:%d (from local)" % (env.host_string, local_host, local_port))
            chan.close()
            return

        print("[%s] rtunnel: opened reverse tunnel: %r -> %r -> %r"\
              % (env.host_string, channel.origin_addr,
                 channel.getpeername(), (local_host, local_port)))

        th = ThreadHandler('fwd', _forwarder, channel, sock)
        threads.append(th)
Exemplo n.º 10
0
    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))
Exemplo n.º 11
0
 def inner(*args, **kwargs):
     # Start server
     _server = serve_responses(responses, files, passwords, home,
                               pubkeys, port)
     _server.all_done = threading.Event()
     worker = ThreadHandler('server', _server.serve_forever)
     # Execute function
     try:
         return func(*args, **kwargs)
     finally:
         # Clean up client side connections
         with hide('status'):
             disconnect_all()
         # Stop server
         _server.all_done.set()
         _server.shutdown()
         # Why this is not called in shutdown() is beyond me.
         _server.server_close()
         worker.thread.join()
         # Handle subthread exceptions
         e = worker.exception
         if e:
             raise e[0], e[1], e[2]
Exemplo n.º 12
0
def test_patch():
    from fabric.thread_handling import ThreadHandler
    from fabric.api import env, output

    env.host_string = 'myhost'
    output['test'] = 'ok'

    # test dict proxy
    assert env['host_string'] == 'myhost'
    assert output['test'] == 'ok'

    assert len(output.expand_aliases(['everything'])) > 0

    # test attribute dict proxy
    assert env.host_string == 'myhost'

    # test state transfer
    def test_state_transfer(x, y):
        assert x == 1
        assert y == 2
        assert env.host_string == 'myhost'

    # test fresh state
    def test_default(x, y):
        assert x == 1
        assert y == 2
        assert env.host_string == None

    th = ThreadHandler('footest', test_state_transfer, [1], {'y': 2})
    th.thread.join()

    t = threading.Thread(None, test_default, args=[1], kwargs={'y': 2})
    t.start()
    t.join()

    print 'OK'
Exemplo n.º 13
0
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")

    The database might be installed on a client only reachable from the client
    host (as opposed to *on* the client itself)::

        # Map localhost:6379 on the server to redis.internal:6379 on the client
        with remote_tunnel(6379, local_host="redis.internal")
            run("redis-cli -i")

    ``remote_tunnel`` accepts up to four arguments:

    * ``remote_port`` (mandatory) is the remote port to listen to.
    * ``local_port`` (optional) is the local port to connect to; the default is
      the same port as the remote one.
    * ``local_host`` (optional) is the locally-reachable computer (DNS name or
      IP address) to connect to; the default is ``localhost`` (that is, the
      same computer Fabric is running on).
    * ``remote_bind_address`` (optional) is the remote IP address to bind to
      for listening, on the current target. It should be an IP address assigned
      to an interface on the target (or a DNS name that resolves to such IP).
      You can use "0.0.0.0" to bind to all interfaces.

    .. note::
        By default, most SSH servers only allow remote tunnels to listen to the
        localhost interface (127.0.0.1). In these cases, `remote_bind_address`
        is ignored by the server, and the tunnel will listen only to 127.0.0.1.

    .. versionadded: 1.6
    """
    if local_port is None:
        local_port = remote_port

    sockets = []
    channels = []
    threads = []

    def accept(channel, (src_addr, src_port), (dest_addr, dest_port)):
        channels.append(channel)
        sock = socket.socket()
        sockets.append(sock)

        try:
            sock.connect((local_host, local_port))
        except Exception:
            print "[%s] rtunnel: cannot connect to %s:%d (from local)" % (
                env.host_string, local_host, local_port)
            channel.close()
            return

        print "[%s] rtunnel: opened reverse tunnel: %r -> %r -> %r"\
              % (env.host_string, channel.origin_addr,
                 channel.getpeername(), (local_host, local_port))

        th = ThreadHandler('fwd', _forwarder, channel, sock)
        threads.append(th)
Exemplo n.º 14
0
        channels.append(channel)
        sock = socket.socket()
        sockets.append(sock)

        try:
            sock.connect((local_host, local_port))
        except Exception, e:
            print "[%s] rtunnel: cannot connect to %s:%d (from local)" % (env.host_string, local_host, local_port)
            chan.close()
            return

        print "[%s] rtunnel: opened reverse tunnel: %r -> %r -> %r"\
              % (env.host_string, channel.origin_addr,
                 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)
Exemplo n.º 15
0
def _execute(channel, command, pty=True, combine_stderr=None,
    invoke_shell=False, stdout=None, stderr=None, timeout=None):
    """
    Execute ``command`` over ``channel``.

    ``pty`` controls whether a pseudo-terminal is created.

    ``combine_stderr`` controls whether we call ``channel.set_combine_stderr``.
    By default, the global setting for this behavior (:ref:`env.combine_stderr
    <combine-stderr>`) is consulted, but you may specify ``True`` or ``False``
    here to override it.

    ``invoke_shell`` controls whether we use ``exec_command`` or
    ``invoke_shell`` (plus a handful of other things, such as always forcing a
    pty.)

    Returns a three-tuple of (``stdout``, ``stderr``, ``status``), where
    ``stdout``/``stderr`` are captured output strings and ``status`` is the
    program's return code, if applicable.
    """
    # stdout/stderr redirection
    stdout = stdout or sys.stdout
    stderr = stderr or sys.stderr

    # Timeout setting control
    timeout = env.command_timeout if (timeout is None) else timeout

    # What to do with CTRl-C?
    remote_interrupt = env.remote_interrupt

    with char_buffered(sys.stdin):
        # Combine stdout and stderr to get around oddball mixing issues
        if combine_stderr is None:
            combine_stderr = env.combine_stderr
        channel.set_combine_stderr(combine_stderr)

        # Assume pty use, and allow overriding of this either via kwarg or env
        # var.  (invoke_shell always wants a pty no matter what.)
        using_pty = True
        if not invoke_shell and (not pty or not env.always_use_pty):
            using_pty = False
        # Request pty with size params (default to 80x24, obtain real
        # parameters if on POSIX platform)
        if using_pty:
            rows, cols = _pty_size()
            channel.get_pty(width=cols, height=rows)

        # Use SSH agent forwarding from 'ssh' if enabled by user
        config_agent = ssh_config().get('forwardagent', 'no').lower() == 'yes'
        forward = None
        if env.forward_agent or config_agent:
            forward = ssh.agent.AgentRequestHandler(channel)

        # Kick off remote command
        if invoke_shell:
            channel.invoke_shell()
            if command:
                channel.sendall(command + "\n")
        else:
            channel.exec_command(command=command)

        # Init stdout, stderr capturing. Must use lists instead of strings as
        # strings are immutable and we're using these as pass-by-reference
        stdout_buf, stderr_buf = [], []
        if invoke_shell:
            stdout_buf = stderr_buf = None

        workers = (
            ThreadHandler('out', output_loop, channel, "recv",
                capture=stdout_buf, stream=stdout, timeout=timeout),
            ThreadHandler('err', output_loop, channel, "recv_stderr",
                capture=stderr_buf, stream=stderr, timeout=timeout),
            ThreadHandler('in', input_loop, channel, using_pty)
        )

        if remote_interrupt is None:
            remote_interrupt = invoke_shell
        if remote_interrupt and not using_pty:
            remote_interrupt = False

        while True:
            if channel.exit_status_ready():
                break
            else:
                # Check for thread exceptions here so we can raise ASAP
                # (without chance of getting blocked by, or hidden by an
                # exception within, recv_exit_status())
                for worker in workers:
                    worker.raise_if_needed()
            try:
                time.sleep(ssh.io_sleep)
            except KeyboardInterrupt:
                if not remote_interrupt:
                    raise
                channel.send('\x03')

        # Obtain exit code of remote program now that we're done.
        status = channel.recv_exit_status()

        # Wait for threads to exit so we aren't left with stale threads
        for worker in workers:
            worker.thread.join()
            worker.raise_if_needed()

        # Close channel
        channel.close()
        # Close any agent forward proxies
        if forward is not None:
            forward.close()

        # Update stdout/stderr with captured values if applicable
        if not invoke_shell:
            stdout_buf = ''.join(stdout_buf).strip()
            stderr_buf = ''.join(stderr_buf).strip()

        # Tie off "loose" output by printing a newline. Helps to ensure any
        # following print()s aren't on the same line as a trailing line prefix
        # or similar. However, don't add an extra newline if we've already
        # ended up with one, as that adds a entire blank line instead.
        if output.running \
            and (output.stdout and stdout_buf and not stdout_buf.endswith("\n")) \
            or (output.stderr and stderr_buf and not stderr_buf.endswith("\n")):
            print("")

        return stdout_buf, stderr_buf, status
Exemplo n.º 16
0
def _execute(channel,
             command,
             pty=True,
             combine_stderr=True,
             invoke_shell=False):
    """
    Execute ``command`` over ``channel``.

    ``pty`` controls whether a pseudo-terminal is created.

    ``combine_stderr`` controls whether we call ``channel.set_combine_stderr``.

    ``invoke_shell`` controls whether we use ``exec_command`` or
    ``invoke_shell`` (plus a handful of other things, such as always forcing a
    pty.)

    Returns a three-tuple of (``stdout``, ``stderr``, ``status``), where
    ``stdout``/``stderr`` are captured output strings and ``status`` is the
    program's return code, if applicable.
    """
    with char_buffered(sys.stdin):
        # Combine stdout and stderr to get around oddball mixing issues
        if combine_stderr or env.combine_stderr:
            channel.set_combine_stderr(True)

        # Assume pty use, and allow overriding of this either via kwarg or env
        # var.  (invoke_shell always wants a pty no matter what.)
        using_pty = True
        if not invoke_shell and (not pty or not env.always_use_pty):
            using_pty = False
        # Request pty with size params (default to 80x24, obtain real
        # parameters if on POSIX platform)
        if using_pty:
            rows, cols = _pty_size()
            channel.get_pty(width=cols, height=rows)

        # Kick off remote command
        if invoke_shell:
            channel.invoke_shell()
            if command:
                channel.sendall(command + "\n")
        else:
            channel.exec_command(command)

        # Init stdout, stderr capturing. Must use lists instead of strings as
        # strings are immutable and we're using these as pass-by-reference
        stdout, stderr = [], []
        if invoke_shell:
            stdout = stderr = None

        workers = (ThreadHandler('out', output_loop, channel, "recv", stdout),
                   ThreadHandler('err', output_loop, channel, "recv_stderr",
                                 stderr),
                   ThreadHandler('in', input_loop, channel, using_pty))

        while True:
            if channel.exit_status_ready():
                break
            else:
                for worker in workers:
                    e = worker.exception
                    if e:
                        raise e[0], e[1], e[2]

        # Obtain exit code of remote program now that we're done.
        status = channel.recv_exit_status()

        # Wait for threads to exit so we aren't left with stale threads
        for worker in workers:
            worker.thread.join()

        # Close channel
        channel.close()

        # Update stdout/stderr with captured values if applicable
        if not invoke_shell:
            stdout = ''.join(stdout).strip()
            stderr = ''.join(stderr).strip()

        # Tie off "loose" output by printing a newline. Helps to ensure any
        # following print()s aren't on the same line as a trailing line prefix
        # or similar. However, don't add an extra newline if we've already
        # ended up with one, as that adds a entire blank line instead.
        if output.running \
            and (output.stdout and stdout and not stdout.endswith("\n")) \
            or (output.stderr and stderr and not stderr.endswith("\n")):
            print("")

        return stdout, stderr, status
Exemplo n.º 17
0
        channels.append(channel)
        sock = socket.socket()
        sockets.append(sock)

        try:
            sock.connect((local_host, local_port))
        except Exception, e:
            print "[%s] rtunnel: cannot connect to %s:%d (from local)" % (env.host_string, local_host, local_port)
            chan.close()
            return

        print "[%s] rtunnel: opened reverse tunnel: %r -> %r -> %r"\
              % (env.host_string, channel.origin_addr,
                 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)
Exemplo n.º 18
0
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()
Exemplo n.º 19
0
        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.' %
                    self.remote_addr)
        
        th = ThreadHandler('fwd', _forwarder, channel, sock)
        
        sockets.append(sock)
        channels.append(channel)
        threads.append(th)
    
    listening_thread = ThreadHandler('local_bind', listener_thread_main,
                                     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()
Exemplo n.º 20
0
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()