OpenSSH
[Top] [All Lists]

Re: tunneling through stdin/stdout, source routing

To: openssh-unix-dev@mindrot.org
Subject: Re: tunneling through stdin/stdout, source routing
From: Alan Barrett <apb@cequrux.com>
Date: Tue, 21 Nov 2006 17:54:45 +0200
Delivered-to: sp-com-lists@consult.net
Delivered-to: openssh-unix-dev-list1@securepoint.com
Delivered-to: openssh-unix-dev-tmda@mindrot.org
Delivered-to: openssh-unix-dev@mindrot.org
In-reply-to: <4555C24C.7000007@hogyros.de>
List-archive: <http://lists.mindrot.org/pipermail/openssh-unix-dev>
List-help: <mailto:openssh-unix-dev-request@mindrot.org?subject=help>
List-id: Development of portable OpenSSH <openssh-unix-dev.mindrot.org>
List-post: <mailto:openssh-unix-dev@mindrot.org>
List-subscribe: <http://lists.mindrot.org/mailman/listinfo/openssh-unix-dev>, <mailto:openssh-unix-dev-request@mindrot.org?subject=subscribe>
List-unsubscribe: <http://lists.mindrot.org/mailman/listinfo/openssh-unix-dev>, <mailto:openssh-unix-dev-request@mindrot.org?subject=unsubscribe>
References: <4555C24C.7000007@hogyros.de>
Sender: openssh-unix-dev-bounces+openssh-unix-dev-list1=securepoint.com@mindrot.org
On Sat, 11 Nov 2006, Simon Richter wrote:
> quite often I find myself using commands like
> 
> $ ssh foo nc bar 12345
  [...]
> I wonder whether it would make sense to have an option in the ssh
> client that told it to connect to the server, then open a tunneled
> connection and connect that to stdin/stdout.

That would be nice.  Syntax wise, I suggest an extension of the "-L"
or "LocalForward" option to let it interpret "-" in the local port number
position as a request to forward stdin/stdout instead of a TCP port.
So this:

    ssh -N -L -:bar:12345 foo

would make an ssh connection to foo, ask foo to forward a channel to
bar:12345, amd connect the local stdin/stdout to the forwarded channel.

I append a proof of concept wrapper script that can be used as

    sshconnect foo bar 12345

to do much the same thing, through a combination of "ssh -N -L
localhost:localport:bar:12345 foo" and "connect localhost localport",
where localport is a dynamically allocated port.  It works without any
support from the server, but requires perl and connect on the client.

> As an extension, there could also be a source routing option in
> the config file that would take care of setting up a chain of ssh
> connections if I need multiple hops.

I'd suggest using "ProxyCommand some_clever_script %h %p".  Let the
clever script have knowledge of which hosts can see which other hosts,
which firewall you are behind today, etc.

--apb (Alan Barrett)

#!/usr/bin/perl -w
#
# sshconnect - connect stdin/stdout to a distant host and port,
#              via ssh port forwarding through a nearby host.
#
# Placed in the public domain by A P Barrett, November 2006.
#
# usage: sshconnect [-sshoptions] [user@]nearby distanthost distantport
#
# usage in .ssh/config file:
#       ProxyCommand sshconnect [-sshoptions] [user@]nearby %h %p
#
# What it does:
#  1.  Find an available local port number;
#  2.  Use ssh to connect to the nearby host, and set up
#      port forwarding from the local port allocated above to the
#      desired distant host and port.
#  3.  Use the "connect" program to connect stdin/stdout to the
#      local port allocated above.  This will cause the local
#      ssh client to ask the nearby ssh server to forward the connection
#      to the distant destination.
#

# returns a local port number that is not currently in use.
# XXX: There is a race between choosing the port here,
# and using the port elsewhere.
sub choose_port
{
    my (@netstat_lines) = `netstat -an -f inet`;
    foreach my $port (2048..3000) {
        if (! grep /\.\Q${port}\E\s/, @netstat_lines) {
            #print STDERR "Chose port $port\n";
            return $port;
        }
    }
    return undef;
}

# sleep for a specified (possibly fractional) number of seconds
sub fracsleep
{
    my ($delay) = @_;
    select(undef, undef, undef, $delay);
}

# really kill a process, by sending SIGTERM immediately,
# and SIGKILL after a short delay if the process doesn't exit by itelf.
sub really_kill
{
    my ($pid) = @_;
    kill TERM, $pid;
    foreach my $delay (((0.1) x 5, (0.5) x 4)) {
        return unless kill 0, $pid;
        fracsleep $delay;
    }
    kill KILL, $pid;
}

sub main
{
    my (@ARGV) = @_;
    my $localhost = "127.0.0.1";
    my $localport = choose_port();
    my (@delays) = ((0.2) x 5, (0.5) x 6, (1) x 10, (2) x 60);
    my $connect_command = "connect";
    my $connect_status = undef;
    my $childpid = undef;
    my $sighandler = sub {
                    #print STDERR "CAUGHT SIGNAL\n";
                    really_kill($childpid) if defined $childpid;
                    exit(1);
                };
    local $SIG{INT} = $sighandler;

    die "Can't assign local port\n" unless defined $localport;

    $childpid = fork();

    die "Can't fork\n" unless defined $childpid;

    if ($childpid == 0) {
        # This is the child.
        # Run ssh, asking it to set up port forwarding.

        my ($distant_port) = pop(@ARGV);
        my ($distant_host) = pop(@ARGV);
        my ($user_at_nearby_host) = pop(@ARGV);
        my (@ssh_options) = @ARGV;
        push @ssh_options, (
                # no login or command
                "-N",
                # port forwarding does not play nicely with
                # session multiplexing, so disable session multiplexing.
                "-o", "ControlPath=none",
                "-o", "ControlMaster=no",
                # forward the local port to the distant port
                "-o", "ExitOnForwardFailure=yes",
                "-L", "localhost:${localport}:${distant_host}:${distant_port}",
                );

        exec ("ssh", @ssh_options, $user_at_nearby_host);

        exit(0);

    } else {
        # This is the parent.
        # Connect sdin/stdout to the TCP port that ssh will forward.

        # Wait until netstat shows that $localport is listening,
        # or the child dies.
        $ok = 0;
        foreach my $delay (@delays) {
            @netstat_lines = `netstat -an -f inet`;
            $ok = 1 if (grep /\s\Q${localhost}.${localport}\E\s.*LISTEN/,
                    @netstat_lines);

            if ($ok) {
                #print STDERR "OK\n";
                last;
            }

            if (kill(0, $childpid) != 1) {
                #print STDERR "ssh died\n";
                last;
            }

            #print STDERR "SLEEP $delay\n";
            fracsleep $delay;
        }

        if (! $ok) {
            #print STDERR "Could not forward port\n";
        }
        
        # Connect.  This will run until EOF or network error.
        if ($ok) {
            #print STDERR "CONNECTING to port $localport\n";
            $connect_status = system($connect_command, $localhost, $localport);
            #print STDERR "CONNECT STATUS $connect_status\n";
        }

        # Kill the child and exit.
        #print STDERR "KILL $childpid\n";
        really_kill($childpid);

        if (defined($connect_status)) {
            exit($connect_status);
        } else {
            exit(1);
        }
    }
}

main @ARGV;
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
http://lists.mindrot.org/mailman/listinfo/openssh-unix-dev

<Prev in Thread] Current Thread [Next in Thread>