Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

look for updated DNS when connection to server is lost #455

Open
wants to merge 7 commits into from

5 participants

maltek Anders Kaseorg Igor Bukanov m0yellow Jashank Jeremy
maltek

I implemented issue 212:
Instead of resolving the host name in the mosh wrapper script and giving the mosh-client the resolved IPv4 address, I modified the script to pass down the host name, then changed the client to constantly re-resolve that host name when the connection is lost.

To do so, I modified Connection::hop_port() to make an asynchronous DNS request. The DNS requests are IPv4 only right now, but I used getaddrinfo, so changing it to support IPv6 should not be too hard.

Only GNU libc has an asynchronus getaddrinfo_a, so I had to use pthreads to make one.

Note also that I changed AC_CHECK_FUNCS to spit out an error message when a function isn't found, otherwise the missing getaddrinfo would just be ignored. I'm by far not an automake expert, so I'm not 100% sure this doesn't break anything.

I tested this patch on Linux, OS X and FreeBSD x86_64.

Anders Kaseorg
Collaborator

As explained in #81, there’s a tricky issue here that will require a bit more work. Some server hostnames (such as athena.dialup.mit.edu) are load-balanced by a round-robin DNS record where different entries point to different servers. If the hostname is resolved twice, once in the wrapper script and once inside mosh-client, then there’s no guarantee that the results will be the same, so mosh-client may try to connect to a different server than mosh-server is actually running on.

We really do need to resolve the IP address once and pass it along. It may work to also pass the hostname along for future re-resolution, as long as the client does not rely on it by throwing away the working IP address.

Malte Kraus added some commits
maltek

Alright, I changed things to pass down IP and host name. When re-resolving, in case of a DNS response containing several IPs at once the code now only tries to switch IPs if none of those IPs match the old one. And instead of completely throwing away the previously working IP after a re-resolution, the code now flips between the new IP and the last one that got a server response.

Igor Bukanov

The latest patch compiles fine on Ubuntu 12.04 both on x64 and Arm. As far as I can see it does not break anything, I will see tomorrow if it can reconnect from inside firewall.

Igor Bukanov

The patch works nicely with server reachable from different ip inside/outside firewall.

Matthew Abbott payco referenced this pull request
Open

Missing IPv6-support #81

m0yellow

Coming here from #81 but find the idea fascinating:

Could mosh-server add all local IPs to the list on the client, which does pollute the client list with rfc1918-IPs, but makes roaming between internal and external IPs possible, without adding them to DNS (which would break nearly all protocols other than mosh). If accepted, this could open the door to finally solve #81 by adding v6 addresses to the list in the future.

Igor Bukanov

@m0yellow - that does not work in general as mosh client may hit another server from a different local private net with the same address. Compared with that DNS names are supposed to be unique.

m0yellow

I know, but this is the crux with IPs, they were intended to be unique, too.
Reading about the AS112 project, I would suggest we trust in the strength of the key and let the client hit another server, it would be one udp packet, and one RST, and then the election goes on, combined with a numbered priority based on clients connected on the server, it could still stay on the list as last resort.

BTW: I already have the problem, when the client dies, the mosh-server process runs indefinitely, and the client happily opening another session. So on servers with long uptime, I have dozen of mosh-server processes, all for the same user. So from the server side, it might even reduce the load, as more sessions could be recovered, instead of a new one created.

Igor Bukanov

I filed #469 which should allow to implement whatever fancy server reconnection algorithm in a few lines of, say, Perl script. I may also allow to support IPv6

Jashank Jeremy

The latest version of this doesn't compile with GCC or Clang on OS X 10.9. With GCC:

> gmake V=1
g++-apple-4.2 -DHAVE_CONFIG_H -I. -I../..  -I./../util -I./../crypto -I../protobufs -D_THREAD_SAFE -I/opt/local/include   -Wall  -fno-strict-overflow -D_FORTIFY_SOURCE=2 -fstack-protector-all -Wstack-protector --param ssp-buffer-size=1 -fPIE -fno-default-inline -pipe -g -O2 -MT network.o -MD -MP -MF .deps/network.Tpo -c -o network.o network.cc
network.cc: In member function 'Network::Connection::DNSResolverAsync::Status     Network::Connection::DNSResolverAsync::try_start_stop(Network::AddrLen&)':
network.cc:271: error: invalid use of nonstatic data member 'Network::Connection::remote_addr'
gmake: *** [network.o] Error 1

With clang:

> gmake V=1 CXX=clang++
clang++ -DHAVE_CONFIG_H -I. -I../..  -I./../util -I./../crypto -I../protobufs -D_THREAD_SAFE -I/opt/local/include   -Wall  -fno-strict-overflow -D_FORTIFY_SOURCE=2 -fstack-protector-all -Wstack-protector --param ssp-buffer-size=1 -fPIE -fno-default-inline -pipe -g -O2 -MT network.o -MD -MP -MF .deps/network.Tpo -c -o network.o network.cc
clang: warning: argument unused during compilation: '-fno-default-inline'
network.cc:271:53: error: use of non-static data member 'remote_addr' of 'Connection' from nested type 'DNSResolverAsync'
        fatal_assert( result->ai_addrlen <= sizeof( remote_addr.addr ) );
                                                    ^~~~~~~~~~~
./../util/fatal_assert.h:47:5: note: expanded from macro 'fatal_assert'
  ((expr)                                                               \
    ^
1 error generated.
gmake: *** [network.o] Error 1

I couldn't come up with a quick fix to this. 66673fd still compiles, and I'm just about to check it still works. If it does, I'll bisect 66673fd..ba1fb85 and play whack-a-bug.

Update: it works excellently! Took ~40 seconds to fail over, but compared to not failing over at all, that's very acceptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 23, 2013
  1. look for updated DNS when connection to server is lost

    Malte Kraus authored
Commits on Aug 26, 2013
  1. pass down hostname *and* IP from mosh wrapper to mosh-client, in orde…

    Malte Kraus authored
    …r to handle round-robin DNS
  2. only hop to another IP when the old one isn't among ALL the addresses…

    Malte Kraus authored
    … returned by getaddrinfo (handle round robin DNS gracefully)
Commits on Dec 10, 2013
Commits on Jan 5, 2014
  1. merge with upstream (IPv6 support)

    Malte Kraus authored
Commits on Jan 24, 2014
This page is out of date. Refresh to see the latest.
2  configure.ac
View
@@ -191,6 +191,8 @@ AC_CHECK_HEADERS([m4_normalize([
unistd.h
wchar.h
wctype.h
+ pthread.h
+ netdb.h
])], [], [AC_MSG_ERROR([Missing required header file.])])
AC_CHECK_HEADERS([pty.h util.h libutil.h paths.h])
14 scripts/mosh
View
@@ -189,7 +189,7 @@ if ( defined $fake_proxy ) {
Proto => "tcp" )
or die "$0: Could not connect to $host: $@\n";
- print STDERR "MOSH IP ", $sock->peerhost, "\n";
+ print STDERR "MOSH IP_HOSTNAME ", $sock->peerhost, " ", $host, "\n";
# Act like netcat
binmode($sock);
@@ -267,15 +267,15 @@ if ( $pid == 0 ) { # child
exec "$ssh " . shell_quote( '-S', 'none', '-o', "ProxyCommand=$quoted_self --fake-proxy -- %h %p", '-n', '-tt', $userhost, '--', "$server " . shell_quote( @server ) );
die "Cannot exec ssh: $!\n";
} else { # parent
- my ( $ip, $port, $key );
+ my ( $ip, $hostname, $port, $key );
my $bad_udp_port_warning = 0;
LINE: while ( <$pipe> ) {
chomp;
- if ( m{^MOSH IP } ) {
+ if ( m{^MOSH IP_HOSTNAME } ) {
if ( defined $ip ) {
- die "$0 error: detected attempt to redefine MOSH IP.\n";
+ die "$0 error: detected attempt to redefine MOSH IP_HOSTNAME.\n";
}
- ( $ip ) = m{^MOSH IP (\S+)\s*$} or die "Bad MOSH IP string: $_\n";
+ ( $ip, $hostname ) = m{^MOSH IP_HOSTNAME (\S+) (\S+)\s*$} or die "Bad MOSH IP_HOSTNAME string: $_\n";
} elsif ( m{^MOSH CONNECT } ) {
if ( ( $port, $key ) = m{^MOSH CONNECT (\d+?) ([A-Za-z0-9/+]{22})\s*$} ) {
last LINE;
@@ -293,7 +293,7 @@ if ( $pid == 0 ) { # child
close $pipe;
if ( not defined $ip ) {
- die "$0: Did not find remote IP address (is SSH ProxyCommand disabled?).\n";
+ die "$0: Did not find remote IP address and host name (is SSH ProxyCommand disabled?).\n";
}
if ( not defined $key or not defined $port ) {
@@ -307,7 +307,7 @@ if ( $pid == 0 ) { # child
$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 |", $ip, $hostname, $port);
}
sub shell_quote { join ' ', map {(my $a = $_) =~ s/'/'\\''/g; "'$a'"} @_ }
12 src/frontend/mosh-client.cc
View
@@ -75,7 +75,8 @@ void usage( const char *argv0 ) {
fprintf( stderr, "Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n" );
fprintf( stderr, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.\n\n" );
- fprintf( stderr, "Usage: %s IP PORT\n %s -c\n", argv0, argv0 );
+ fprintf( stderr, "Usage: %s HOSTNAME PORT\n %s -c\n", argv0, argv0 );
+ fprintf( stderr, "Usage: %s IP HOSTNAME PORT\n %s -c\n", argv0, argv0 );
}
void print_colorcount( void )
@@ -116,15 +117,16 @@ int main( int argc, char *argv[] )
}
}
- char *ip, *desired_port;
+ char *ip, *hostname, *desired_port;
- if ( argc - optind != 2 ) {
+ if ( argc - optind != 3 ) {
usage( argv[ 0 ] );
exit( 1 );
}
ip = argv[ optind ];
- desired_port = argv[ optind + 1 ];
+ hostname = argv[ optind + 1 ];
+ desired_port = argv[ optind + 2 ];
/* Sanity-check arguments */
if ( desired_port
@@ -160,7 +162,7 @@ int main( int argc, char *argv[] )
set_native_locale();
try {
- STMClient client( ip, desired_port, key, predict_mode );
+ STMClient client( ip, hostname, desired_port, key, predict_mode );
client.init();
try {
12 src/frontend/mosh-server.cc
View
@@ -526,8 +526,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network
#ifdef HAVE_UTEMPTER
bool connected_utmp = false;
- Addr saved_addr;
- socklen_t saved_addr_len = 0;
+ AddrLen saved_addr;
#endif
while ( 1 ) {
@@ -610,16 +609,15 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network
#ifdef HAVE_UTEMPTER
/* update utmp entry if we have become "connected" */
if ( (!connected_utmp)
- || saved_addr_len != network.get_remote_addr_len()
- || memcmp( &saved_addr, &network.get_remote_addr(),
- saved_addr_len ) != 0 ) {
+ || saved_addr.len != network.get_remote_addr().len
+ || memcmp( &saved_addr.addr, &network.get_remote_addr().addr,
+ saved_addr.len ) != 0 ) {
utempter_remove_record( host_fd );
saved_addr = network.get_remote_addr();
- saved_addr_len = network.get_remote_addr_len();
char host[ NI_MAXHOST ];
- int errcode = getnameinfo( &saved_addr.sa, saved_addr_len,
+ int errcode = getnameinfo( &saved_addr.addr.sa, saved_addr.len,
host, sizeof( host ), NULL, 0,
NI_NUMERICHOST );
if ( errcode != 0 ) {
2  src/frontend/stmclient.cc
View
@@ -247,7 +247,7 @@ void STMClient::main_init( void )
Network::UserStream blank;
Terminal::Complete local_terminal( window_size.ws_col, window_size.ws_row );
network = new Network::Transport< Network::UserStream, Terminal::Complete >( blank, local_terminal,
- key.c_str(), ip.c_str(), port.c_str() );
+ key.c_str(), ip.c_str(), hostname.c_str(), port.c_str() );
network->set_send_delay( 1 ); /* minimal delay on outgoing keystrokes */
5 src/frontend/stmclient.h
View
@@ -45,6 +45,7 @@
class STMClient {
private:
std::string ip;
+ std::string hostname;
std::string port;
std::string key;
@@ -83,8 +84,8 @@ class STMClient {
void resume( void ); /* restore state after SIGCONT */
public:
- STMClient( const char *s_ip, const char *s_port, const char *s_key, const char *predict_mode )
- : ip( s_ip ), port( s_port ), key( s_key ),
+ STMClient( const char *s_ip, const char *s_hostname, const char *s_port, const char *s_key, const char *predict_mode )
+ : ip ( s_ip ), hostname( s_hostname ), port( s_port ), key( s_key ),
escape_key( 0x1E ), escape_pass_key( '^' ), escape_pass_key2( '^' ),
escape_requires_lf( false ), escape_key_help( L"?" ),
saved_termios(), raw_termios(),
187 src/network/network.cc
View
@@ -34,6 +34,7 @@
#include <sys/types.h>
#include <sys/socket.h>
+#include <netdb.h>
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
@@ -42,6 +43,7 @@
#include <assert.h>
#include <errno.h>
#include <unistd.h>
+#include <pthread.h>
#include "dos_assert.h"
#include "fatal_assert.h"
@@ -97,6 +99,44 @@ string Packet::tostring( Session *session )
return session->encrypt( Message( Nonce( direction_seq ), timestamps + payload ) );
}
+uint16_t & AddrLen::Addr::port() {
+ switch( sa.sa_family ) {
+ case AF_INET:
+ return sin.sin_port;
+ case AF_INET6:
+ return sin6.sin6_port;
+ default:
+ throw NetworkException( "Unknown address family", 0 );
+ }
+}
+bool AddrLen::Addr::same_addr( const struct addrinfo* other ) {
+ if( sa.sa_family == other->ai_family ) {
+ size_t size = 0;
+ const void *addr1 = NULL;
+ const void *addr2 = NULL;
+
+ switch( sa.sa_family ) {
+ case AF_INET: {
+ const struct sockaddr_in *other4 = (const struct sockaddr_in*)other->ai_addr;
+ size = sizeof( sin.sin_addr );
+ addr1 = &sin.sin_addr;
+ addr2 = &other4->sin_addr;
+ }
+ case AF_INET6: {
+ const struct sockaddr_in6 *other6 = (const struct sockaddr_in6*)other->ai_addr;
+ size = sizeof( sin6.sin6_addr );
+ addr1 = &sin6.sin6_addr;
+ addr2 = &other6->sin6_addr;
+ }
+ default:
+ throw NetworkException( "Unknown address family", 0 );
+ }
+
+ return memcmp( addr1, addr2, size ) == 0;
+ }
+ return false;
+}
+
Packet Connection::new_packet( string &s_payload )
{
uint16_t outgoing_timestamp_reply = -1;
@@ -119,9 +159,21 @@ void Connection::hop_port( void )
{
assert( !server );
+ switch ( remote_addr_resolver.try_start_stop( remote_addr ) ) {
+ case DNSResolverAsync::STARTED:
+ case DNSResolverAsync::ERROR: {
+ // because of round-robin DNS servers that only return one record, we also want to retry the last working IP sometimes
+ remote_addr = remote_addr_last_working;
+ break;
+ }
+ case DNSResolverAsync::RESULT_RETURNED:
+ case DNSResolverAsync::STILL_RUNNING:
+ // do nothing
+ break;
+ }
setup();
- assert( remote_addr_len != 0 );
- socks.push_back( Socket( remote_addr.sa.sa_family ) );
+ assert( remote_addr.len != 0 );
+ socks.push_back( Socket( remote_addr.addr.sa.sa_family ) );
prune_sockets();
}
@@ -186,6 +238,68 @@ void Connection::setup( void )
last_port_choice = timestamp();
}
+Connection::DNSResolverAsync::DNSResolverAsync( const std::string &hostname )
+ : thread(),
+ resolving( false ),
+ result_waiting( false ),
+ hostname( hostname )
+{
+}
+
+Connection::DNSResolverAsync::Status Connection::DNSResolverAsync::try_start_stop( AddrLen &addr )
+{
+ if( !resolving ) {
+ resolving = true;
+ int ok = pthread_create( &thread, NULL, resolve_thread, this );
+ return ok == 0 ? STARTED : ERROR;
+ } else if( result_waiting ) {
+ result_waiting = false;
+ resolving = false;
+
+ void *res;
+ int ok = pthread_join( thread, &res );
+ struct addrinfo *result = (struct addrinfo*)res, *rp;
+ if( ok == 0 && result != NULL ) {
+ bool old_record_found = false;
+ for( rp = result; rp != NULL && !old_record_found; rp = rp->ai_next ) {
+ old_record_found = addr.addr.same_addr( rp );
+ }
+
+ if( !old_record_found ) {
+ uint16_t old_port = addr.addr.port();
+
+ fatal_assert( result->ai_addrlen <= sizeof( addr.addr ) );
+ addr.len = result->ai_addrlen;
+ memcpy( &addr.addr.sa, result->ai_addr, addr.len );
+ addr.addr.port() = old_port;
+ }
+ freeaddrinfo( result );
+ return RESULT_RETURNED;
+ }
+ return ERROR;
+ }
+ return STILL_RUNNING;
+}
+
+void *Connection::DNSResolverAsync::resolve_thread(void *param)
+{
+ DNSResolverAsync *resolver = (DNSResolverAsync*)param;
+
+ struct addrinfo hints = addrinfo();
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ struct addrinfo *result;
+ if( getaddrinfo( resolver->hostname.c_str(), NULL, &hints, &result ) != 0 ) {
+ result = NULL;
+ }
+ resolver->result_waiting = true;
+ return (void*)result;
+}
+
+
+
const std::vector< int > Connection::fds( void ) const
{
std::vector< int > ret;
@@ -220,7 +334,8 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se
: socks(),
has_remote_addr( false ),
remote_addr(),
- remote_addr_len( 0 ),
+ remote_addr_last_working(),
+ remote_addr_resolver( "" ),
server( true ),
MTU( DEFAULT_SEND_MTU ),
key(),
@@ -288,9 +403,9 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;
AddrInfo ai( addr, "0", &hints );
- Addr local_addr;
- socklen_t local_addr_len = ai.res->ai_addrlen;
- memcpy( &local_addr.sa, ai.res->ai_addr, local_addr_len );
+ AddrLen local_addr;
+ local_addr.len = ai.res->ai_addrlen;
+ memcpy( &local_addr.addr.sa, ai.res->ai_addr, local_addr.len );
int search_low = PORT_RANGE_LOW, search_high = PORT_RANGE_HIGH;
@@ -301,26 +416,17 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
search_high = port_high;
}
- socks.push_back( Socket( local_addr.sa.sa_family ) );
+ socks.push_back( Socket( local_addr.addr.sa.sa_family ) );
for ( int i = search_low; i <= search_high; i++ ) {
- switch (local_addr.sa.sa_family) {
- case AF_INET:
- local_addr.sin.sin_port = htons( i );
- break;
- case AF_INET6:
- local_addr.sin6.sin6_port = htons( i );
- break;
- default:
- throw NetworkException( "Unknown address family", 0 );
- }
+ local_addr.addr.port() = htons( i );
- if ( bind( sock(), &local_addr.sa, local_addr_len ) == 0 ) {
+ if ( bind( sock(), &local_addr.addr.sa, local_addr.len ) == 0 ) {
return true;
} else if ( i == search_high ) { /* last port to search */
int saved_errno = errno;
socks.pop_back();
char host[ NI_MAXHOST ], serv[ NI_MAXSERV ];
- int errcode = getnameinfo( &local_addr.sa, local_addr_len,
+ int errcode = getnameinfo( &local_addr.addr.sa, local_addr.len,
host, sizeof( host ), serv, sizeof( serv ),
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
if ( errcode != 0 ) {
@@ -336,11 +442,12 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
return false;
}
-Connection::Connection( const char *key_str, const char *ip, const char *port ) /* client */
+Connection::Connection( const char *key_str, const char *ip, const char *hostname, const char *port ) /* client */
: socks(),
has_remote_addr( false ),
remote_addr(),
- remote_addr_len( 0 ),
+ remote_addr_last_working(),
+ remote_addr_resolver( hostname ),
server( false ),
MTU( DEFAULT_SEND_MTU ),
key( key_str ),
@@ -368,13 +475,14 @@ Connection::Connection( const char *key_str, const char *ip, const char *port )
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
AddrInfo ai( ip, port, &hints );
- fatal_assert( ai.res->ai_addrlen <= sizeof( remote_addr ) );
- remote_addr_len = ai.res->ai_addrlen;
- memcpy( &remote_addr.sa, ai.res->ai_addr, remote_addr_len );
+ fatal_assert( ai.res->ai_addrlen <= sizeof( remote_addr.addr ) );
+ remote_addr.len = ai.res->ai_addrlen;
+ memcpy( &remote_addr.addr.sa, ai.res->ai_addr, remote_addr.len );
+ remote_addr_last_working = remote_addr;
has_remote_addr = true;
- socks.push_back( Socket( remote_addr.sa.sa_family ) );
+ socks.push_back( Socket( remote_addr.addr.sa.sa_family ) );
}
void Connection::send( string s )
@@ -388,7 +496,7 @@ void Connection::send( string s )
string p = px.tostring( &session );
ssize_t bytes_sent = sendto( sock(), p.data(), p.size(), MSG_DONTWAIT,
- &remote_addr.sa, remote_addr_len );
+ &remote_addr.addr.sa, remote_addr.len );
if ( bytes_sent == static_cast<ssize_t>( p.size() ) ) {
have_send_exception = false;
@@ -449,7 +557,7 @@ string Connection::recv( void )
string Connection::recv_one( int sock_to_recv, bool nonblocking )
{
/* receive source address, ECN, and payload in msghdr structure */
- Addr packet_remote_addr;
+ AddrLen packet_remote;
struct msghdr header;
struct iovec msg_iovec;
@@ -457,8 +565,8 @@ string Connection::recv_one( int sock_to_recv, bool nonblocking )
char msg_control[ Session::RECEIVE_MTU ];
/* receive source address */
- header.msg_name = &packet_remote_addr.sa;
- header.msg_namelen = sizeof( packet_remote_addr );
+ header.msg_name = &packet_remote.addr.sa;
+ header.msg_namelen = sizeof( packet_remote.addr );
/* receive payload */
msg_iovec.iov_base = msg_payload;
@@ -545,12 +653,12 @@ string Connection::recv_one( int sock_to_recv, bool nonblocking )
last_heard = timestamp();
if ( server ) { /* only client can roam */
- if ( remote_addr_len != header.msg_namelen ||
- memcmp( &remote_addr, &packet_remote_addr, remote_addr_len ) != 0 ) {
- remote_addr = packet_remote_addr;
- remote_addr_len = header.msg_namelen;
+ if ( remote_addr.len != header.msg_namelen ||
+ memcmp( &remote_addr.addr, &packet_remote.addr, remote_addr.len ) != 0 ) {
+ remote_addr.addr = packet_remote.addr;
+ remote_addr.len = header.msg_namelen;
char host[ NI_MAXHOST ], serv[ NI_MAXSERV ];
- int errcode = getnameinfo( &remote_addr.sa, remote_addr_len,
+ int errcode = getnameinfo( &remote_addr.addr.sa, remote_addr.len,
host, sizeof( host ), serv, sizeof( serv ),
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
if ( errcode != 0 ) {
@@ -559,6 +667,9 @@ string Connection::recv_one( int sock_to_recv, bool nonblocking )
fprintf( stderr, "Server now attached to client at %s:%s\n",
host, serv );
}
+ } else {
+ remote_addr_last_working.addr = packet_remote.addr;
+ remote_addr_last_working.len = header.msg_namelen;
}
}
@@ -567,15 +678,15 @@ string Connection::recv_one( int sock_to_recv, bool nonblocking )
std::string Connection::port( void ) const
{
- Addr local_addr;
- socklen_t addrlen = sizeof( local_addr );
+ AddrLen local_addr;
+ local_addr.len = sizeof( local_addr.addr );
- if ( getsockname( sock(), &local_addr.sa, &addrlen ) < 0 ) {
+ if ( getsockname( sock(), &local_addr.addr.sa, &local_addr.len ) < 0 ) {
throw NetworkException( "getsockname", errno );
}
char serv[ NI_MAXSERV ];
- int errcode = getnameinfo( &local_addr.sa, addrlen,
+ int errcode = getnameinfo( &local_addr.addr.sa, local_addr.len,
NULL, 0, serv, sizeof( serv ),
NI_DGRAM | NI_NUMERICSERV );
if ( errcode != 0 ) {
51 src/network/network.h
View
@@ -36,6 +36,7 @@
#include <stdint.h>
#include <deque>
#include <sys/socket.h>
+#include <netdb.h>
#include <netinet/in.h>
#include <string>
#include <math.h>
@@ -84,11 +85,17 @@ namespace Network {
string tostring( Session *session );
};
- union Addr {
- struct sockaddr sa;
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
- struct sockaddr_storage ss;
+ struct AddrLen {
+ union Addr {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_storage ss;
+
+ uint16_t & port();
+ bool same_addr( const struct addrinfo* other );
+ } addr;
+ socklen_t len;
};
class Connection {
@@ -124,10 +131,35 @@ namespace Network {
Socket & operator=( const Socket & other );
};
+ class DNSResolverAsync
+ {
+ private:
+ pthread_t thread;
+ bool resolving; /* thread started, still running */
+ volatile bool result_waiting; /* thread finished, not yet joined */
+ const std::string hostname;
+
+ static void *resolve_thread( void * );
+
+
+ public:
+ enum Status {
+ STARTED,
+ STILL_RUNNING,
+ RESULT_RETURNED,
+ ERROR
+ };
+
+ DNSResolverAsync( const std::string & );
+ Status try_start_stop( AddrLen & );
+
+ };
+
std::deque< Socket > socks;
bool has_remote_addr;
- Addr remote_addr;
- socklen_t remote_addr_len;
+ AddrLen remote_addr;
+ AddrLen remote_addr_last_working;
+ DNSResolverAsync remote_addr_resolver;
bool server;
@@ -169,7 +201,7 @@ namespace Network {
public:
Connection( const char *desired_ip, const char *desired_port ); /* server */
- Connection( const char *key_str, const char *ip, const char *port ); /* client */
+ Connection( const char *key_str, const char *ip, const char *hostname, const char *port ); /* client */
void send( string s );
string recv( void );
@@ -183,8 +215,7 @@ namespace Network {
uint64_t timeout( void ) const;
double get_SRTT( void ) const { return SRTT; }
- const Addr &get_remote_addr( void ) const { return remote_addr; }
- socklen_t get_remote_addr_len( void ) const { return remote_addr_len; }
+ const AddrLen &get_remote_addr( void ) const { return remote_addr; }
const NetworkException *get_send_exception( void ) const
{
4 src/network/networktransport.cc
View
@@ -55,8 +55,8 @@ Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState
template <class MyState, class RemoteState>
Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState &initial_remote,
- const char *key_str, const char *ip, const char *port )
- : connection( key_str, ip, port ),
+ const char *key_str, const char *ip, const char *hostname, const char *port )
+ : connection( key_str, ip, hostname, port ),
sender( &connection, initial_state ),
received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ),
receiver_quench_timer( 0 ),
5 src/network/networktransport.h
View
@@ -69,7 +69,7 @@ namespace Network {
Transport( MyState &initial_state, RemoteState &initial_remote,
const char *desired_ip, const char *desired_port );
Transport( MyState &initial_state, RemoteState &initial_remote,
- const char *key_str, const char *ip, const char *port );
+ const char *key_str, const char *ip, const char *desired_hostname, const char *port );
/* Send data or an ack if necessary. */
void tick( void ) { sender.tick(); }
@@ -116,8 +116,7 @@ namespace Network {
unsigned int send_interval( void ) const { return sender.send_interval(); }
- 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(); }
+ const AddrLen &get_remote_addr( void ) const { return connection.get_remote_addr(); }
const NetworkException *get_send_exception( void ) const { return connection.get_send_exception(); }
};
Something went wrong with that request. Please try again.