diff --git a/scripts/mosh.pl b/scripts/mosh.pl index f224ef348..ec32466a3 100755 --- a/scripts/mosh.pl +++ b/scripts/mosh.pl @@ -69,6 +69,7 @@ BEGIN my $overwrite = 0; my $bind_ip = undef; +my $host = undef; my $use_remote_ip = 'proxy'; @@ -245,6 +246,7 @@ sub predict_check { } $userhost = shift; @command = @ARGV; + (my $user, $host) = $userhost =~ /^((?:.*@)?)(.*)$/; if ( not defined $bind_ip or $bind_ip =~ m{^ssh$}i ) { if ( not defined $localhost ) { push @bind_arguments, '-s'; @@ -334,7 +336,7 @@ sub predict_check { my $ip; if ( $use_remote_ip eq 'local' ) { # "parse" the host from what the user gave us - my ($user, $host) = $userhost =~ /^((?:.*@)?)(.*)$/; + (my $user, $host) = $userhost =~ /^((?:.*@)?)(.*)$/; # get list of addresses my @res = resolvename( $host, 22, $family ); # Use only the first address as the Mosh IP @@ -462,7 +464,7 @@ sub predict_check { $ENV{ 'MOSH_KEY' } = $key; $ENV{ 'MOSH_PREDICTION_DISPLAY' } = $predict; $ENV{ 'MOSH_NO_TERM_INIT' } = '1' if !$term_init; - exec {$client} ("$client", "-# @cmdline |", $ip, $port); + exec {$client} ("$client", "-# @cmdline |", "-n", "$host", $ip, $port); } sub shell_quote { join ' ', map {(my $a = $_) =~ s/'/'\\''/g; "'$a'"} @_ } diff --git a/src/frontend/mosh-client.cc b/src/frontend/mosh-client.cc index 1803dc6db..667d15257 100644 --- a/src/frontend/mosh-client.cc +++ b/src/frontend/mosh-client.cc @@ -85,7 +85,7 @@ static void print_usage( FILE* file, const char* argv0 ) { print_version( file ); fprintf( file, - "\nUsage: %s [-# 'ARGS'] IP PORT\n" + "\nUsage: %s [-# 'ARGS'] [-n hostname] IP PORT\n" " %s -c\n", argv0, argv0 ); @@ -130,8 +130,10 @@ int main( int argc, char* argv[] ) } } + char *hostname = NULL; + int opt; - while ( ( opt = getopt( argc, argv, "#:cv" ) ) != -1 ) { + while ( ( opt = getopt( argc, argv, "#:cvn:" ) ) != -1 ) { switch ( opt ) { case '#': // Ignore the original arguments to mosh wrapper @@ -143,6 +145,9 @@ int main( int argc, char* argv[] ) case 'v': verbose++; break; + case 'n': + hostname = optarg; + break; default: print_usage( stderr, argv[0] ); exit( 1 ); @@ -194,7 +199,7 @@ int main( int argc, char* argv[] ) bool success = false; try { - STMClient client( ip, desired_port, key.c_str(), predict_mode, verbose, predict_overwrite ); + STMClient client( hostname, ip, desired_port, key.c_str(), predict_mode, verbose, predict_overwrite ); client.init(); try { diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc index bccab7d94..58cea9f7e 100644 --- a/src/frontend/stmclient.cc +++ b/src/frontend/stmclient.cc @@ -45,6 +45,8 @@ #include #include #include +#include +#include #if HAVE_PTY_H #include @@ -193,7 +195,7 @@ void STMClient::init( void ) tmp = std::string( escape_key_name_buf ); std::wstring escape_key_name = std::wstring( tmp.begin(), tmp.end() ); escape_key_help - = L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name; + = L"Commands: Ctrl-Z suspends, \".\" quits, \"r\" forces a re-resolve, " + escape_pass_name + L" gives literal " + escape_key_name; overlays.get_notification_engine().set_escape_key_string( tmp ); } wchar_t tmp[128]; @@ -367,6 +369,41 @@ bool STMClient::process_user_input( int fd ) kill( 0, SIGSTOP ); resume(); + } else if ( the_byte == 'r' || the_byte == 'R' ) { + if ( !hostname.empty() ) { + overlays.get_notification_engine().set_notification_string( std::wstring( L"Re-resolving..." ), true ); + output_new_frame(); + struct addrinfo hints, *res; + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + int err = getaddrinfo( hostname.c_str(), port.c_str(), &hints, &res ); + if ( err == 0 ) { + struct addrinfo *p; + struct addrinfo *match = NULL; + int current_family = net.get_remote_addr().sa.sa_family; + + for ( p = res; p != NULL; p = p->ai_next ) { + if ( p->ai_family == current_family ) { + match = p; + break; + } + } + + if ( match ) { + net.set_remote_addr( match->ai_addr, match->ai_addrlen ); + } else { + net.set_remote_addr( res->ai_addr, res->ai_addrlen ); + } + + freeaddrinfo( res ); + overlays.get_notification_engine().set_notification_string( std::wstring( L"Re-resolved hostname." ), false ); + } else { + std::string msg = "DNS failure: "; + msg += gai_strerror( err ); + overlays.get_notification_engine().set_notification_string( std::wstring( msg.begin(), msg.end() ), false ); + } + } } else if ( ( the_byte == escape_pass_key ) || ( the_byte == escape_pass_key2 ) ) { /* Emulation sequence to type escape_key is escape_key + escape_pass_key (that is escape key without Ctrl) */ diff --git a/src/frontend/stmclient.h b/src/frontend/stmclient.h index 4d19225f3..ae5bb4eb9 100644 --- a/src/frontend/stmclient.h +++ b/src/frontend/stmclient.h @@ -47,6 +47,7 @@ class STMClient { private: + std::string hostname; std::string ip; std::string port; std::string key; @@ -89,16 +90,18 @@ class STMClient void resume( void ); /* restore state after SIGCONT */ public: - STMClient( const char* s_ip, + STMClient( const char* s_hostname, + const char* s_ip, const char* s_port, const char* s_key, const char* predict_mode, unsigned int s_verbose, const char* predict_overwrite ) - : ip( s_ip ? s_ip : "" ), port( s_port ? s_port : "" ), key( s_key ? s_key : "" ), escape_key( 0x1E ), - escape_pass_key( '^' ), escape_pass_key2( '^' ), escape_requires_lf( false ), escape_key_help( L"?" ), - saved_termios(), raw_termios(), window_size(), local_framebuffer( 1, 1 ), new_state( 1, 1 ), overlays(), - network(), display( true ) /* use TERM environment var to initialize display */, connecting_notification(), + : hostname( s_hostname ? s_hostname : "" ), ip( s_ip ? s_ip : "" ), port( s_port ? s_port : "" ), + key( s_key ? s_key : "" ), escape_key( 0x1E ), escape_pass_key( '^' ), escape_pass_key2( '^' ), + escape_requires_lf( false ), escape_key_help( L"?" ), saved_termios(), raw_termios(), window_size(), + local_framebuffer( 1, 1 ), new_state( 1, 1 ), overlays(), network(), + display( true ) /* use TERM environment var to initialize display */, connecting_notification(), repaint_requested( false ), lf_entered( false ), quit_sequence_started( false ), clean_shutdown( false ), verbose( s_verbose ) { diff --git a/src/network/network.cc b/src/network/network.cc index 8a6118eae..51c1717ce 100644 --- a/src/network/network.cc +++ b/src/network/network.cc @@ -364,6 +364,24 @@ Connection::Connection( const char* key_str, const char* ip, const char* port ) set_MTU( remote_addr.sa.sa_family ); } +void Connection::set_remote_addr( const struct sockaddr* addr, socklen_t len ) +{ + fatal_assert( len <= sizeof( remote_addr ) ); + + bool family_changed = ( remote_addr.sa.sa_family != addr->sa_family ); + + remote_addr_len = len; + memcpy( &remote_addr.sa, addr, remote_addr_len ); + + has_remote_addr = true; + + if ( family_changed ) { + socks.push_back( Socket( remote_addr.sa.sa_family ) ); + } + + set_MTU( remote_addr.sa.sa_family ); +} + void Connection::send( const std::string& s ) { if ( !has_remote_addr ) { diff --git a/src/network/network.h b/src/network/network.h index 00125f57e..532b11e69 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -233,6 +233,7 @@ class Connection const Addr& get_remote_addr( void ) const { return remote_addr; } socklen_t get_remote_addr_len( void ) const { return remote_addr_len; } + void set_remote_addr( const struct sockaddr* addr, socklen_t len ); std::string& get_send_error( void ) { return send_error; } diff --git a/src/network/networktransport.h b/src/network/networktransport.h index 1b09a6300..34a67d7ea 100644 --- a/src/network/networktransport.h +++ b/src/network/networktransport.h @@ -126,6 +126,7 @@ class Transport const Addr& get_remote_addr( void ) const { return connection.get_remote_addr(); } socklen_t get_remote_addr_len( void ) const { return connection.get_remote_addr_len(); } + void set_remote_addr( const struct sockaddr* addr, socklen_t len ) { connection.set_remote_addr( addr, len ); } std::string& get_send_error( void ) { return connection.get_send_error(); } };