@ -28,6 +28,7 @@
# endif
# endif
enum {
enum {
SCK_RESOLVING ,
SCK_CONNECTING ,
SCK_CONNECTING ,
# ifdef HAVE_LIBSSL
# ifdef HAVE_LIBSSL
SCK_STARTTLS ,
SCK_STARTTLS ,
@ -415,6 +416,7 @@ static void socket_fd_cb( int, void * );
static void socket_fake_cb ( void * ) ;
static void socket_fake_cb ( void * ) ;
static void socket_timeout_cb ( void * ) ;
static void socket_timeout_cb ( void * ) ;
static void socket_resolve ( conn_t * ) ;
static void socket_connect_one ( conn_t * ) ;
static void socket_connect_one ( conn_t * ) ;
static void socket_connect_next ( conn_t * ) ;
static void socket_connect_next ( conn_t * ) ;
static void socket_connect_failed ( conn_t * ) ;
static void socket_connect_failed ( conn_t * ) ;
@ -422,15 +424,21 @@ static void socket_connected( conn_t * );
static void socket_connect_bail ( conn_t * ) ;
static void socket_connect_bail ( conn_t * ) ;
static void
static void
socket_open _internal ( conn_t * sock , int fd )
socket_register _internal ( conn_t * sock , int fd )
{
{
sock - > fd = fd ;
sock - > fd = fd ;
fcntl ( fd , F_SETFL , O_NONBLOCK ) ;
init_notifier ( & sock - > notify , fd , socket_fd_cb , sock ) ;
init_notifier ( & sock - > notify , fd , socket_fd_cb , sock ) ;
init_wakeup ( & sock - > fd_fake , socket_fake_cb , sock ) ;
init_wakeup ( & sock - > fd_fake , socket_fake_cb , sock ) ;
init_wakeup ( & sock - > fd_timeout , socket_timeout_cb , sock ) ;
init_wakeup ( & sock - > fd_timeout , socket_timeout_cb , sock ) ;
}
}
static void
socket_open_internal ( conn_t * sock , int fd )
{
fcntl ( fd , F_SETFL , O_NONBLOCK ) ;
socket_register_internal ( sock , fd ) ;
}
static void
static void
socket_close_internal ( conn_t * sock )
socket_close_internal ( conn_t * sock )
{
{
@ -441,32 +449,6 @@ socket_close_internal( conn_t *sock )
sock - > fd = - 1 ;
sock - > fd = - 1 ;
}
}
# ifndef HAVE_IPV6
struct addr_info {
struct addr_info * ai_next ;
struct sockaddr_in ai_addr [ 1 ] ;
} ;
# define freeaddrinfo(ai) free( ai )
static struct addr_info *
init_addrinfo ( struct hostent * he )
{
uint naddr = 0 ;
for ( char * * addr = he - > h_addr_list ; * addr ; addr + + )
naddr + + ;
struct addr_info * caddr = nfzalloc ( naddr * sizeof ( struct addrinfo ) ) ;
struct addr_info * ret , * * caddrp = & ret ;
for ( char * * addr = he - > h_addr_list ; * addr ; addr + + , caddr + + ) {
caddr - > ai_addr - > sin_family = AF_INET ;
memcpy ( & caddr - > ai_addr - > sin_addr . s_addr , * addr , sizeof ( struct in_addr ) ) ;
* caddrp = caddr ;
caddrp = & caddr - > ai_next ;
}
return ret ;
}
# endif
void
void
socket_connect ( conn_t * sock , void ( * cb ) ( int ok , void * aux ) )
socket_connect ( conn_t * sock , void ( * cb ) ( int ok , void * aux ) )
{
{
@ -501,77 +483,202 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
info ( " \v ok \n " ) ;
info ( " \v ok \n " ) ;
socket_connected ( sock ) ;
socket_connected ( sock ) ;
} else {
} else {
# ifdef HAVE_IPV6
socket_resolve ( sock ) ;
int gaierr ;
}
struct addrinfo hints ;
}
static void
pipe_write ( int fd , void * buf , int len )
{
do {
int wrote = write ( fd , buf , len ) ;
if ( wrote < 0 ) {
perror ( " write " ) ;
_exit ( 1 ) ;
}
buf = ( ( char * ) buf ) + wrote ;
len - = wrote ;
} while ( len ) ;
}
memset ( & hints , 0 , sizeof ( hints ) ) ;
static void
socket_resolve ( conn_t * sock )
{
info ( " Resolving %s... \n " , sock - > conf - > host ) ;
int pfd [ 2 ] ;
if ( pipe ( pfd ) ) {
perror ( " pipe " ) ;
exit ( 1 ) ;
}
switch ( fork ( ) ) {
case - 1 :
perror ( " fork " ) ;
exit ( 1 ) ;
case 0 :
break ;
default :
close ( pfd [ 1 ] ) ;
socket_register_internal ( sock , pfd [ 0 ] ) ;
sock - > state = SCK_RESOLVING ;
conf_notifier ( & sock - > notify , 0 , POLLIN ) ;
socket_expect_activity ( sock , 1 ) ;
return ;
}
# ifdef HAVE_IPV6
struct addrinfo * res , hints = { 0 } ;
hints . ai_family = AF_UNSPEC ;
hints . ai_family = AF_UNSPEC ;
hints . ai_socktype = SOCK_STREAM ;
hints . ai_socktype = SOCK_STREAM ;
hints . ai_flags = AI_ADDRCONFIG ;
hints . ai_flags = AI_ADDRCONFIG ;
infon ( " Resolving %s... " , conf - > host ) ;
int gaierr = getaddrinfo ( sock - > conf - > host , NULL , & hints , & res ) ;
if ( ( gaierr = getaddrinfo ( conf - > host , NULL , & hints , & sock - > addrs ) ) ) {
pipe_write ( pfd [ 1 ] , & gaierr , sizeof ( gaierr ) ) ;
error ( " Error: Cannot resolve server '%s': %s \n " , conf - > host , gai_strerror ( gaierr ) ) ;
if ( gaierr )
socket_connect_bail ( sock ) ;
_exit ( 1 ) ;
return ;
static_assert ( sizeof ( ( ( struct addrinfo ) { 0 } ) . ai_family ) = = sizeof ( int ) , " unexpected size of ai_family " ) ;
static_assert ( sizeof ( struct in_addr ) % sizeof ( int ) = = 0 , " unexpected size of struct in_addr " ) ;
static_assert ( sizeof ( struct in6_addr ) % sizeof ( int ) = = 0 , " unexpected size of struct in6_addr " ) ;
int nbytes = 0 ;
for ( struct addrinfo * cres = res ; cres ; cres = cres - > ai_next ) {
if ( cres - > ai_family = = AF_INET ) {
nbytes + = sizeof ( int ) + sizeof ( struct in_addr ) ;
} else {
assert ( cres - > ai_family = = AF_INET6 ) ;
nbytes + = sizeof ( int ) + sizeof ( struct in6_addr ) ;
}
}
pipe_write ( pfd [ 1 ] , & nbytes , sizeof ( nbytes ) ) ;
for ( struct addrinfo * cres = res ; cres ; cres = cres - > ai_next ) {
pipe_write ( pfd [ 1 ] , & cres - > ai_family , sizeof ( int ) ) ;
if ( cres - > ai_family = = AF_INET )
pipe_write ( pfd [ 1 ] , & ( ( struct sockaddr_in * ) cres - > ai_addr ) - > sin_addr , sizeof ( struct in_addr ) ) ;
else
pipe_write ( pfd [ 1 ] , & ( ( struct sockaddr_in6 * ) cres - > ai_addr ) - > sin6_addr , sizeof ( struct in6_addr ) ) ;
}
}
info ( " \v ok \n " ) ;
# else
# else
struct hostent * he ;
struct hostent * he = gethostbyname ( sock - > conf - > host ) ;
int herrno = he ? 0 : h_errno ;
pipe_write ( pfd [ 1 ] , & herrno , sizeof ( herrno ) ) ;
if ( ! he )
_exit ( 1 ) ;
static_assert ( sizeof ( struct in_addr ) % sizeof ( int ) = = 0 , " unexpected size of struct in_addr " ) ;
int nbytes = 0 ;
for ( char * * addr = he - > h_addr_list ; * addr ; addr + + )
nbytes + = sizeof ( struct in_addr ) ;
pipe_write ( pfd [ 1 ] , & nbytes , sizeof ( nbytes ) ) ;
for ( char * * addr = he - > h_addr_list ; * addr ; addr + + )
pipe_write ( pfd [ 1 ] , * addr , sizeof ( struct in_addr ) ) ;
# endif
_exit ( 0 ) ;
}
infon ( " Resolving %s... " , conf - > host ) ;
static void
he = gethostbyname ( conf - > host ) ;
pipe_read ( int fd , void * buf , int len )
if ( ! he ) {
{
error ( " Error: Cannot resolve server '%s': %s \n " , conf - > host , hstrerror ( h_errno ) ) ;
do {
int didrd = read ( fd , buf , len ) ;
if ( didrd < 0 ) {
sys_error ( " read " ) ;
exit ( 1 ) ;
}
if ( ! didrd ) {
error ( " read: unexpected EOF \n " ) ;
exit ( 1 ) ;
}
buf = ( ( char * ) buf ) + didrd ;
len - = didrd ;
} while ( len ) ;
}
static void
socket_resolve_finalize ( conn_t * sock )
{
int errcode ;
pipe_read ( sock - > fd , & errcode , sizeof ( errcode ) ) ;
if ( errcode ) {
# ifdef HAVE_IPV6
const char * err = gai_strerror ( errcode ) ;
# else
const char * err = hstrerror ( errcode ) ;
# endif
error ( " Error: Cannot resolve server '%s': %s \n " , sock - > conf - > host , err ) ;
socket_close_internal ( sock ) ;
socket_connect_bail ( sock ) ;
socket_connect_bail ( sock ) ;
return ;
return ;
}
}
info ( " \v ok \n " ) ;
sock - > addrs = init_addrinfo ( he ) ;
int nbytes ;
# endif
pipe_read ( sock - > fd , & nbytes , sizeof ( nbytes ) ) ;
sock - > curr_addr = sock - > addrs ;
char * addrs = nfmalloc ( nbytes ) ;
pipe_read ( sock - > fd , addrs , nbytes ) ;
sock - > curr_addr = sock - > addrs = addrs ;
sock - > addrs_end = addrs + nbytes ;
socket_close_internal ( sock ) ; // Get rid of the pipe
socket_connect_one ( sock ) ;
socket_connect_one ( sock ) ;
}
}
static void
socket_resolve_timeout ( conn_t * sock )
{
error ( " Error: Cannot resolve server '%s': timeout. \n " , sock - > conf - > host ) ;
socket_close_internal ( sock ) ;
socket_connect_bail ( sock ) ;
}
}
static void
static void
socket_connect_one ( conn_t * sock )
socket_connect_one ( conn_t * sock )
{
{
int s ;
char * ai = sock - > curr_addr ;
# ifdef HAVE_IPV6
if ( ai = = sock - > addrs_end ) {
struct addrinfo * ai ;
# else
struct addr_info * ai ;
# endif
if ( ! ( ai = sock - > curr_addr ) ) {
error ( " No working address found for %s \n " , sock - > conf - > host ) ;
error ( " No working address found for %s \n " , sock - > conf - > host ) ;
socket_connect_bail ( sock ) ;
socket_connect_bail ( sock ) ;
return ;
return ;
}
}
union {
struct sockaddr any ;
struct sockaddr_in ip4 ;
# ifdef HAVE_IPV6
struct sockaddr_in6 ip6 ;
# endif
} addr ;
# ifdef HAVE_IPV6
int fam = * ( int * ) ai ;
ai + = sizeof ( int ) ;
int addr_len ;
if ( fam = = AF_INET6 ) {
addr_len = sizeof ( addr . ip6 ) ;
addr . ip6 . sin6_addr = * ( struct in6_addr * ) ai ;
addr . ip6 . sin6_flowinfo = 0 ;
addr . ip6 . sin6_scope_id = 0 ;
ai + = sizeof ( struct in6_addr ) ;
} else {
addr_len = sizeof ( addr . ip4 ) ;
# else
const int fam = AF_INET ;
const int addr_len = sizeof ( addr . ip4 ) ;
{
# endif
addr . ip4 . sin_addr = * ( struct in_addr * ) ai ;
ai + = sizeof ( struct in_addr ) ;
}
sock - > curr_addr = ai ;
# ifdef HAVE_IPV6
# ifdef HAVE_IPV6
if ( ai - > ai_family = = AF_INET6 ) {
if ( fam = = AF_INET6 ) {
struct sockaddr_in6 * in6 = ( ( struct sockaddr_in6 * ) ai - > ai_addr ) ;
char sockname [ 64 ] ;
char sockname [ 64 ] ;
in6 - > sin6_port = htons ( sock - > conf - > port ) ;
inet_ntop ( fam , & addr . ip6 . sin6_addr , sockname , sizeof ( sockname ) ) ;
nfasprintf ( & sock - > name , " %s ([%s]:%hu) " ,
nfasprintf ( & sock - > name , " %s ([%s]:%hu) " ,
sock - > conf - > host , inet_ntop ( AF_INET6 , & in6 - > sin6_addr , sockname , sizeof ( sockname ) ) , sock - > conf - > port ) ;
sock - > conf - > host , sockname , sock - > conf - > port ) ;
} else
} else
# endif
# endif
{
{
struct sockaddr_in * in = ( ( struct sockaddr_in * ) ai - > ai_addr ) ;
in - > sin_port = htons ( sock - > conf - > port ) ;
nfasprintf ( & sock - > name , " %s (%s:%hu) " ,
nfasprintf ( & sock - > name , " %s (%s:%hu) " ,
sock - > conf - > host , inet_ntoa ( in - > sin_addr ) , sock - > conf - > port ) ;
sock - > conf - > host , inet_ntoa ( addr . ip4 . sin_addr ) , sock - > conf - > port ) ;
}
}
# ifdef HAVE_IPV6
int s = socket ( fam , SOCK_STREAM , 0 ) ;
s = socket ( ai - > ai_family , SOCK_STREAM , 0 ) ;
# else
s = socket ( PF_INET , SOCK_STREAM , 0 ) ;
# endif
if ( s < 0 ) {
if ( s < 0 ) {
socket_connect_next ( sock ) ;
socket_connect_next ( sock ) ;
return ;
return ;
@ -579,11 +686,9 @@ socket_connect_one( conn_t *sock )
socket_open_internal ( sock , s ) ;
socket_open_internal ( sock , s ) ;
infon ( " Connecting to %s... " , sock - > name ) ;
infon ( " Connecting to %s... " , sock - > name ) ;
# ifdef HAVE_IPV6
addr . any . sa_family = fam ;
if ( connect ( s , ai - > ai_addr , ai - > ai_addrlen ) ) {
addr . ip4 . sin_port = htons ( sock - > conf - > port ) ; // Aliased for ip6
# else
if ( connect ( s , & addr . any , addr_len ) ) {
if ( connect ( s , ai - > ai_addr , sizeof ( * ai - > ai_addr ) ) ) {
# endif
if ( errno ! = EINPROGRESS ) {
if ( errno ! = EINPROGRESS ) {
socket_connect_failed ( sock ) ;
socket_connect_failed ( sock ) ;
return ;
return ;
@ -604,7 +709,6 @@ socket_connect_next( conn_t *conn )
sys_error ( " Cannot connect to %s " , conn - > name ) ;
sys_error ( " Cannot connect to %s " , conn - > name ) ;
free ( conn - > name ) ;
free ( conn - > name ) ;
conn - > name = NULL ;
conn - > name = NULL ;
conn - > curr_addr = conn - > curr_addr - > ai_next ;
socket_connect_one ( conn ) ;
socket_connect_one ( conn ) ;
}
}
@ -618,10 +722,8 @@ socket_connect_failed( conn_t *conn )
static void
static void
socket_connected ( conn_t * conn )
socket_connected ( conn_t * conn )
{
{
if ( conn - > addrs ) {
free ( conn - > addrs ) ;
freeaddrinfo ( conn - > addrs ) ;
conn - > addrs = NULL ;
conn - > addrs = NULL ;
}
conf_notifier ( & conn - > notify , 0 , POLLIN ) ;
conf_notifier ( & conn - > notify , 0 , POLLIN ) ;
socket_expect_activity ( conn , 0 ) ;
socket_expect_activity ( conn , 0 ) ;
conn - > state = SCK_READY ;
conn - > state = SCK_READY ;
@ -631,10 +733,8 @@ socket_connected( conn_t *conn )
static void
static void
socket_cleanup_names ( conn_t * conn )
socket_cleanup_names ( conn_t * conn )
{
{
if ( conn - > addrs ) {
free ( conn - > addrs ) ;
freeaddrinfo ( conn - > addrs ) ;
conn - > addrs = NULL ;
conn - > addrs = NULL ;
}
free ( conn - > name ) ;
free ( conn - > name ) ;
conn - > name = NULL ;
conn - > name = NULL ;
}
}
@ -1106,6 +1206,11 @@ socket_fd_cb( int events, void *aux )
{
{
conn_t * conn = ( conn_t * ) aux ;
conn_t * conn = ( conn_t * ) aux ;
if ( conn - > state = = SCK_RESOLVING ) {
socket_resolve_finalize ( conn ) ;
return ;
}
if ( ( events & POLLERR ) | | conn - > state = = SCK_CONNECTING ) {
if ( ( events & POLLERR ) | | conn - > state = = SCK_CONNECTING ) {
int soerr ;
int soerr ;
socklen_t selen = sizeof ( soerr ) ;
socklen_t selen = sizeof ( soerr ) ;
@ -1168,7 +1273,9 @@ socket_timeout_cb( void *aux )
{
{
conn_t * conn = ( conn_t * ) aux ;
conn_t * conn = ( conn_t * ) aux ;
if ( conn - > state = = SCK_CONNECTING ) {
if ( conn - > state = = SCK_RESOLVING ) {
socket_resolve_timeout ( conn ) ;
} else if ( conn - > state = = SCK_CONNECTING ) {
errno = ETIMEDOUT ;
errno = ETIMEDOUT ;
socket_connect_failed ( conn ) ;
socket_connect_failed ( conn ) ;
} else {
} else {