socket: use next addrinfo if opening socket fails

The `socket_connect_one` function previously did an `exit(1)` when
encountering any errors with opening the socket. This would break
connecting to a host where multiple possible addrinfos are returned,
where the leading addrinfos are in fact impossible to connect to. E.g.
with a kernel configured without support for IPv6, the `getaddrinfo`
call may still return a hint containing an IPv6 address alongside
another hint with an IPv4 address. Creating the socket with the IPv6
address, which will cause an error, lead us to exiting early without
even trying remaining hints.

While one can argue that the user should have compiled without HAVE_IPV6
or used an appropriate DNS configuration, we can do better by simply
skipping over the current addrinfo causing an error. To do so, we split
out a new function `socket_connect_next`, which selects the next
available address info and subsequently calls `socket_connect_one`
again. When no hints remain, `sock_connect_one` will error out at that
point.
This commit is contained in:
Patrick Steinhardt 2017-06-21 09:33:20 +02:00 committed by Oswald Buddenhagen
parent 62808c9003
commit 56515abe94

View File

@ -329,6 +329,7 @@ static void socket_fd_cb( int, void * );
static void socket_fake_cb( void * );
static void socket_connect_one( conn_t * );
static void socket_connect_next( conn_t * );
static void socket_connect_failed( conn_t * );
static void socket_connected( conn_t * );
static void socket_connect_bail( conn_t * );
@ -469,8 +470,8 @@ socket_connect_one( conn_t *sock )
s = socket( PF_INET, SOCK_STREAM, 0 );
#endif
if (s < 0) {
perror( "socket" );
exit( 1 );
socket_connect_next( sock );
return;
}
socket_open_internal( sock, s );
@ -494,10 +495,9 @@ socket_connect_one( conn_t *sock )
}
static void
socket_connect_failed( conn_t *conn )
socket_connect_next( conn_t *conn )
{
sys_error( "Cannot connect to %s", conn->name );
socket_close_internal( conn );
free( conn->name );
conn->name = 0;
#ifdef HAVE_IPV6
@ -508,6 +508,13 @@ socket_connect_failed( conn_t *conn )
socket_connect_one( conn );
}
static void
socket_connect_failed( conn_t *conn )
{
socket_close_internal( conn );
socket_connect_next( conn );
}
static void
socket_connected( conn_t *conn )
{