Add --experimental-remote-ip option.
This commit is contained in:
+31
@@ -82,6 +82,10 @@ apply the locale-related environment variables from the client and try
|
|||||||
again.
|
again.
|
||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
|
Options named \fB \-\-experimental-*\fP are subject to change or
|
||||||
|
removal in future versions of Mosh; their design or function is not
|
||||||
|
yet final.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \fIcommand\fP
|
.B \fIcommand\fP
|
||||||
Command to run on remote host. By default, \fBmosh\fP executes a login shell.
|
Command to run on remote host. By default, \fBmosh\fP executes a login shell.
|
||||||
@@ -202,6 +206,33 @@ Invoke \fBmosh-server\fP locally, without using \fBssh\fP. This
|
|||||||
option requires the \fBhost\fP argument to be a local, numeric
|
option requires the \fBhost\fP argument to be a local, numeric
|
||||||
IPv4/IPv6 address. This option is useful for testing.
|
IPv4/IPv6 address. This option is useful for testing.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-\-experimental\-remote\-ip={proxy|local|remote}
|
||||||
|
Select the method used to discover the IP address that the
|
||||||
|
\fBmosh-client\fP connects to.
|
||||||
|
|
||||||
|
The default is \fBproxy\fP, which uses SSH's
|
||||||
|
.B \-\-ssh\-proxy\-command
|
||||||
|
option to generate and report the exact address that \fBssh\fP uses to
|
||||||
|
connect to the remote host. This option is generally the most
|
||||||
|
compatible with hosts and other options configured in \fBssh\fP
|
||||||
|
configuration files. However, this may not work for some
|
||||||
|
configurations, or for environments where a \fBssh\fP bastion host
|
||||||
|
forwards to a remote machine. It only works with \fBOpenSSH\fP.
|
||||||
|
|
||||||
|
With \fBremote\fP, the server's
|
||||||
|
.B SSH_CONNECTION
|
||||||
|
environment variable will be used. This is useful for environments
|
||||||
|
where \fBssh\fP forwarding is used, or the
|
||||||
|
.B \-\-ssh\-proxy\-command
|
||||||
|
option is used for other purposes.
|
||||||
|
|
||||||
|
With \fBlocal\fP, Mosh resolves the hostname given on its command
|
||||||
|
line, and uses that address for both \fBssh\fP and Mosh connections.
|
||||||
|
This option ignores any configuration in
|
||||||
|
.B ssh_config
|
||||||
|
for the same hostname.
|
||||||
|
|
||||||
.SH ESCAPE SEQUENCES
|
.SH ESCAPE SEQUENCES
|
||||||
|
|
||||||
The default escape character used by Mosh is ASCII RS (decimal 30).
|
The default escape character used by Mosh is ASCII RS (decimal 30).
|
||||||
|
|||||||
+122
-65
@@ -58,6 +58,8 @@ my $predict = undef;
|
|||||||
|
|
||||||
my $bind_ip = undef;
|
my $bind_ip = undef;
|
||||||
|
|
||||||
|
my $use_remote_ip = 'proxy';
|
||||||
|
|
||||||
my $family = 'all';
|
my $family = 'all';
|
||||||
my $port_request = undef;
|
my $port_request = undef;
|
||||||
|
|
||||||
@@ -101,6 +103,10 @@ qq{Usage: $0 [options] [--] [user@]host [command...]
|
|||||||
|
|
||||||
--local run mosh-server locally without using ssh
|
--local run mosh-server locally without using ssh
|
||||||
|
|
||||||
|
--experimental-remote-ip=(local|remote|proxy) select the method for
|
||||||
|
discovering the remote IP address to use for mosh
|
||||||
|
(default: "proxy")
|
||||||
|
|
||||||
--help this message
|
--help this message
|
||||||
--version version and copyright information
|
--version version and copyright information
|
||||||
|
|
||||||
@@ -141,7 +147,8 @@ GetOptions( 'client=s' => \$client,
|
|||||||
'help' => \$help,
|
'help' => \$help,
|
||||||
'version' => \$version,
|
'version' => \$version,
|
||||||
'fake-proxy!' => \my $fake_proxy,
|
'fake-proxy!' => \my $fake_proxy,
|
||||||
'bind-server=s' => \$bind_ip) or die $usage;
|
'bind-server=s' => \$bind_ip,
|
||||||
|
'experimental-remote-ip=s' => \$use_remote_ip) or die $usage;
|
||||||
|
|
||||||
die $usage if ( defined $help );
|
die $usage if ( defined $help );
|
||||||
die $version_message if ( defined $version );
|
die $version_message if ( defined $version );
|
||||||
@@ -156,6 +163,21 @@ if ( defined $predict ) {
|
|||||||
predict_check( $predict, 0 );
|
predict_check( $predict, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( not grep { $_ eq $use_remote_ip } qw { local remote proxy } ) {
|
||||||
|
die "Unknown parameter $use_remote_ip";
|
||||||
|
}
|
||||||
|
|
||||||
|
$family = lc( $family );
|
||||||
|
# Handle IPv4-only Perl installs.
|
||||||
|
if (!$have_ipv6) {
|
||||||
|
# Report failure if IPv6 needed and not available.
|
||||||
|
if (defined($family) && $family eq "inet6") {
|
||||||
|
die "$0: IPv6 sockets not available in this Perl install\n";
|
||||||
|
}
|
||||||
|
# Force IPv4.
|
||||||
|
$family = "inet";
|
||||||
|
}
|
||||||
|
|
||||||
if ( defined $port_request ) {
|
if ( defined $port_request ) {
|
||||||
if ( $port_request =~ m{^(\d+)(:(\d+))?$} ) {
|
if ( $port_request =~ m{^(\d+)(:(\d+))?$} ) {
|
||||||
my ( $low, $clause, $high ) = ( $1, $2, $3 );
|
my ( $low, $clause, $high ) = ( $1, $2, $3 );
|
||||||
@@ -204,65 +226,11 @@ if ( ! defined $fake_proxy ) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
my ( $host, $port ) = @ARGV;
|
my ( $host, $port ) = @ARGV;
|
||||||
my $err;
|
|
||||||
my @res;
|
|
||||||
my $af;
|
|
||||||
|
|
||||||
$family = lc( $family );
|
my @res = resolvename( $host, $port, $family );
|
||||||
# Handle IPv4-only Perl installs.
|
|
||||||
if (!$have_ipv6) {
|
|
||||||
# Report failure if IPv6 needed and not available.
|
|
||||||
if (defined($family) && $family eq "inet6") {
|
|
||||||
die "$0: IPv6 sockets not available in this Perl install\n";
|
|
||||||
}
|
|
||||||
# Force IPv4.
|
|
||||||
$family = "inet";
|
|
||||||
}
|
|
||||||
|
|
||||||
# If the user selected a specific family, parse it.
|
|
||||||
if ( defined( $family ) && ( $family ne "auto" && $family ne "all" )) {
|
|
||||||
# Choose an address family, or cause pain.
|
|
||||||
my $afstr = 'AF_' . uc( $family );
|
|
||||||
$af = eval { IO::Socket->$afstr } or die "$0: Invalid family $family\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
# First try the address as a numeric.
|
|
||||||
my %hints = ( flags => AI_NUMERICHOST,
|
|
||||||
socktype => SOCK_STREAM,
|
|
||||||
protocol => IPPROTO_TCP );
|
|
||||||
if ( defined( $af )) {
|
|
||||||
$hints{family} = $af;
|
|
||||||
}
|
|
||||||
( $err, @res ) = getaddrinfo( $host, $port, \%hints );
|
|
||||||
if ( $err ) {
|
|
||||||
# Get canonical name for this host.
|
|
||||||
$hints{flags} = AI_CANONNAME;
|
|
||||||
( $err, @res ) = getaddrinfo( $host, $port, \%hints );
|
|
||||||
die "$0: could not get canonical name for $host: ${err}\n" if $err;
|
|
||||||
# Then get final resolution of the canonical name.
|
|
||||||
$hints{flags} = undef;
|
|
||||||
my @newres;
|
|
||||||
( $err, @newres ) = getaddrinfo( $res[0]{canonname}, $port, \%hints );
|
|
||||||
die "$0: could not resolve canonical name ${res[0]{canonname}} for ${host}: ${err}\n" if $err;
|
|
||||||
@res = @newres;
|
|
||||||
}
|
|
||||||
|
|
||||||
# If v4 or v6 was specified, reduce the host list.
|
|
||||||
if ( defined( $af )) {
|
|
||||||
@res = grep {$_->{family} == $af} @res;
|
|
||||||
} elsif ( $family ne 'all' ) {
|
|
||||||
# If v4/v6/all were not specified, verify that this host only has one address family available.
|
|
||||||
for my $ai ( @res ) {
|
|
||||||
if ( !defined( $af )) {
|
|
||||||
$af = $ai->{family};
|
|
||||||
} else {
|
|
||||||
die "$0: host has both IPv4 and IPv6 addresses, use --family to specify behavior\n"
|
|
||||||
if $af != $ai->{family};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Now try and connect to something.
|
# Now try and connect to something.
|
||||||
|
my $err;
|
||||||
my $sock;
|
my $sock;
|
||||||
my $addr_string;
|
my $addr_string;
|
||||||
my $service;
|
my $service;
|
||||||
@@ -329,17 +297,52 @@ if ( (not defined $colors)
|
|||||||
|
|
||||||
$ENV{ 'MOSH_CLIENT_PID' } = $$; # We don't support this, but it's useful for test and debug.
|
$ENV{ 'MOSH_CLIENT_PID' } = $$; # We don't support this, but it's useful for test and debug.
|
||||||
|
|
||||||
|
# If we are using a locally-resolved address, we have to get it before we fork,
|
||||||
|
# so both parent and child get it.
|
||||||
|
my $ip;
|
||||||
|
if ( $use_remote_ip eq 'local' ) {
|
||||||
|
# "parse" the host from what the user gave us
|
||||||
|
my $host = $userhost;
|
||||||
|
$host =~ s/.*@//;
|
||||||
|
if ( !defined $host ) {
|
||||||
|
die( "could not find hostname in $userhost" );
|
||||||
|
}
|
||||||
|
# get list of addresses
|
||||||
|
my @res = resolvename( $host, 22, $family );
|
||||||
|
# Use only the first address as the Mosh IP
|
||||||
|
my $hostaddr = $res[0];
|
||||||
|
if ( !defined $hostaddr ) {
|
||||||
|
die( "could not find address for $host" );
|
||||||
|
}
|
||||||
|
my ( $err, $addr_string, $service ) = getnameinfo( $hostaddr->{addr}, NI_NUMERICHOST );
|
||||||
|
if ( $err ) {
|
||||||
|
die( "could not use address for $host" );
|
||||||
|
}
|
||||||
|
$ip = $addr_string;
|
||||||
|
$userhost =~ s/${host}/${ip}/;
|
||||||
|
}
|
||||||
|
|
||||||
my $pid = open(my $pipe, "-|");
|
my $pid = open(my $pipe, "-|");
|
||||||
die "$0: fork: $!\n" unless ( defined $pid );
|
die "$0: fork: $!\n" unless ( defined $pid );
|
||||||
if ( $pid == 0 ) { # child
|
if ( $pid == 0 ) { # child
|
||||||
open(STDERR, ">&STDOUT") or die;
|
open(STDERR, ">&STDOUT") or die;
|
||||||
|
|
||||||
# Ask the server for its IP. The user's shell may not be
|
my @sshopts = ( '-n', '-tt' );
|
||||||
# Posix-compatible so invoke sh explicitly.
|
|
||||||
my $ssh_connection = "sh -c " .
|
|
||||||
shell_quote ( '[ -n "$SSH_CONNECTION" ] && printf "\nMOSH SSH_CONNECTION %s\n" "$SSH_CONNECTION"' ) .
|
|
||||||
";";
|
|
||||||
|
|
||||||
|
my $ssh_connection = "";
|
||||||
|
if ( $use_remote_ip eq 'remote' ) {
|
||||||
|
# Ask the server for its IP. The user's shell may not be
|
||||||
|
# Posix-compatible so invoke sh explicitly.
|
||||||
|
$ssh_connection = "sh -c " .
|
||||||
|
shell_quote ( '[ -n "$SSH_CONNECTION" ] && printf "\nMOSH SSH_CONNECTION %s\n" "$SSH_CONNECTION"' ) .
|
||||||
|
" ; ";
|
||||||
|
# Only with 'remote', we may need to tell SSH which protocol to use.
|
||||||
|
if ( $family eq 'inet' ) {
|
||||||
|
push @sshopts, '-4';
|
||||||
|
} elsif ( $family eq 'inet6' ) {
|
||||||
|
push @sshopts, '-6';
|
||||||
|
}
|
||||||
|
}
|
||||||
my @server = ( 'new' );
|
my @server = ( 'new' );
|
||||||
|
|
||||||
push @server, ( '-c', $colors );
|
push @server, ( '-c', $colors );
|
||||||
@@ -365,12 +368,15 @@ if ( $pid == 0 ) { # child
|
|||||||
exec( $server, @server );
|
exec( $server, @server );
|
||||||
die "Cannot exec $server: $!\n";
|
die "Cannot exec $server: $!\n";
|
||||||
}
|
}
|
||||||
my $quoted_proxy_command = shell_quote( $0, "--family=$family" );
|
if ( $use_remote_ip eq 'proxy' ) {
|
||||||
my @exec_argv = ( @ssh, '-S', 'none', '-o', "ProxyCommand=$quoted_proxy_command --fake-proxy -- %h %p", '-n', '-tt', $userhost, '--', $ssh_connection . " $server " . shell_quote( @server ) );
|
my $quoted_proxy_command = shell_quote( $0, "--family=$family" );
|
||||||
|
push @sshopts, ( '-S', 'none', '-o', "ProxyCommand=$quoted_proxy_command --fake-proxy -- %h %p" );
|
||||||
|
}
|
||||||
|
my @exec_argv = ( @ssh, @sshopts, $userhost, '--', $ssh_connection . "$server " . shell_quote( @server ) );
|
||||||
exec @exec_argv;
|
exec @exec_argv;
|
||||||
die "Cannot exec ssh: $!\n";
|
die "Cannot exec ssh: $!\n";
|
||||||
} else { # parent
|
} else { # parent
|
||||||
my ( $ip, $sship, $port, $key );
|
my ( $sship, $port, $key );
|
||||||
my $bad_udp_port_warning = 0;
|
my $bad_udp_port_warning = 0;
|
||||||
LINE: while ( <$pipe> ) {
|
LINE: while ( <$pipe> ) {
|
||||||
chomp;
|
chomp;
|
||||||
@@ -440,3 +446,54 @@ sub locale_vars {
|
|||||||
|
|
||||||
return @assignments;
|
return @assignments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub resolvename {
|
||||||
|
my ( $host, $port, $family ) = @_;
|
||||||
|
my $err;
|
||||||
|
my @res;
|
||||||
|
my $af;
|
||||||
|
|
||||||
|
# If the user selected a specific family, parse it.
|
||||||
|
if ( defined( $family ) && ( $family ne "auto" && $family ne "all" )) {
|
||||||
|
# Choose an address family, or cause pain.
|
||||||
|
my $afstr = 'AF_' . uc( $family );
|
||||||
|
$af = eval { IO::Socket->$afstr } or die "$0: Invalid family $family\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# First try the address as a numeric.
|
||||||
|
my %hints = ( flags => AI_NUMERICHOST,
|
||||||
|
socktype => SOCK_STREAM,
|
||||||
|
protocol => IPPROTO_TCP );
|
||||||
|
if ( defined( $af )) {
|
||||||
|
$hints{family} = $af;
|
||||||
|
}
|
||||||
|
( $err, @res ) = getaddrinfo( $host, $port, \%hints );
|
||||||
|
if ( $err ) {
|
||||||
|
# Get canonical name for this host.
|
||||||
|
$hints{flags} = AI_CANONNAME;
|
||||||
|
( $err, @res ) = getaddrinfo( $host, $port, \%hints );
|
||||||
|
die "$0: could not get canonical name for $host: ${err}\n" if $err;
|
||||||
|
# Then get final resolution of the canonical name.
|
||||||
|
$hints{flags} = undef;
|
||||||
|
my @newres;
|
||||||
|
( $err, @newres ) = getaddrinfo( $res[0]{canonname}, $port, \%hints );
|
||||||
|
die "$0: could not resolve canonical name ${res[0]{canonname}} for ${host}: ${err}\n" if $err;
|
||||||
|
@res = @newres;
|
||||||
|
}
|
||||||
|
|
||||||
|
# If v4 or v6 was specified, reduce the host list.
|
||||||
|
if ( defined( $af )) {
|
||||||
|
@res = grep {$_->{family} == $af} @res;
|
||||||
|
} elsif ( $family ne 'all' ) {
|
||||||
|
# If v4/v6/all were not specified, verify that this host only has one address family available.
|
||||||
|
for my $ai ( @res ) {
|
||||||
|
if ( !defined( $af )) {
|
||||||
|
$af = $ai->{family};
|
||||||
|
} else {
|
||||||
|
die "$0: host has both IPv4 and IPv6 addresses, use --family to specify behavior\n"
|
||||||
|
if $af != $ai->{family};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return @res;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user