#!/usr/bin/perl -w
use strict;
use English;
use IPC::Open3;
my $FORCE_NETCAT = $ENV{'FORCE_NETCAT'} || 0;
my $SSH_BIN = $ENV{'SSH_BIN'} || 'ssh';
my $DEBUG = $ENV{'DEBUG'} || 0;
my $SSH_OPTS = ssh_cli_opts();
################
### ###
### Main ###
### ###
################
# TODO: robust error handling
# Attempt to detect if we were called by SSH
my $PPID = getppid; # Get parent process id
if ($PPID > 0 && open(my $fh, '<', "/proc/$PPID/cmdline")) {
my @cmdline = split(/\0/, join("\n", <$fh>));
close($fh);
# If not invoked by ssh, operate as a wrapper
if ($cmdline[0] !~ /(?:\A|\/)ssh\z/) {
unless (grep { /HostKeyAlias=/i } @cmdline) {
my @SSH_ARGS = @ARGV;
# Remove all options leaving the hostname
while (scalar(@SSH_ARGS) && $SSH_ARGS[0] =~ /\A-[^-]*([^-])\Z/) {
shift(@SSH_ARGS);
# If an argument was required for the option, remove that too
shift(@SSH_ARGS) if ($SSH_OPTS->{args}->{$1});
}
my $hostname = $SSH_ARGS[0]; # hostname should be left
@SSH_ARGS = @ARGV; # Repopulate argument
if ($hostname) {
$hostname =~ s/.*[\@\+]//; # Remove username
# Since we're overriding the hostname, the Host *^* in the ssh config
# file will no longer match, so we need to set the ProxyCommand here
unshift(@SSH_ARGS, '-o', "ProxyCommand=$0 $hostname \%p");
$hostname =~ s/\^.*//; # Remove caretpath
$hostname =~ s/_.*//; # Remove port spec
# Prevent an unknown host key warning for the final host
unshift(@SSH_ARGS, '-o', "HostKeyAlias=$hostname");
}
print STDERR join(' ', $SSH_BIN, @SSH_ARGS) . "\n" if ($DEBUG);
exec($SSH_BIN, @SSH_ARGS); # Script stops here
}
}
}
# If we get to here, we're acting as a ProxyCommand
my $host_arg = $ARGV[0] || die "No host passed!";
my $port_arg = $ARGV[1] || die "No port passed!";
my ($dest_host, $bounce_host);
if ($host_arg =~ /\A([^\^]+)\^([^\^].*)/) {
($dest_host, $bounce_host) = ($1, $2);
} else {
die "Invalid arguments!";
}
# Deal with usernames
$bounce_host =~ s/\A([^\^\+]+)\+/$1\@/;
# Strip username+ syntax from the dest host
$dest_host =~ s/\A.*\+//;
if ($dest_host =~ /\A([^\^]+)_(\d+)\z/) {
$dest_host = $1;
$port_arg = $2;
}
print STDERR "Connecting to $dest_host:$port_arg via $bounce_host\n" if ($DEBUG);
# See if the local version of ssh supports -W ('netcat mode')
if (!$FORCE_NETCAT && $SSH_OPTS->{args}->{W}) {
ssh_bounce_native($dest_host, $port_arg, $bounce_host);
} else {
ssh_bounce_netcat($dest_host, $port_arg, $bounce_host);
}
# Shouldn't make it this far
exit 1;
#####################
### ###
### Functions ###
### ###
#####################
# Parse supported SSH command line options
sub ssh_cli_opts {
my $data = {};
my @tmp;
#
my $pid = open3(undef, undef, \*FH, $SSH_BIN);
while(my $line =