@ -79,6 +79,94 @@
# endif
/* Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno
* ( or equivalent ) on this platform to hide platform details to code using it .
*/
# ifdef USE_WINSOCK
# define SOCKERRNO ((int)WSAGetLastError())
# define SET_SOCKERRNO(x) (WSASetLastError((int)(x)))
# else
# define SOCKERRNO (errno)
# define SET_SOCKERRNO(x) (errno = (x))
# endif
/* Portable error number symbolic names defined to Winsock error codes. */
# ifdef USE_WINSOCK
# undef EBADF /* override definition in errno.h */
# define EBADF WSAEBADF
# undef EINTR /* override definition in errno.h */
# define EINTR WSAEINTR
# undef EINVAL /* override definition in errno.h */
# define EINVAL WSAEINVAL
# undef EWOULDBLOCK /* override definition in errno.h */
# define EWOULDBLOCK WSAEWOULDBLOCK
# undef EINPROGRESS /* override definition in errno.h */
# define EINPROGRESS WSAEINPROGRESS
# undef EALREADY /* override definition in errno.h */
# define EALREADY WSAEALREADY
# undef ENOTSOCK /* override definition in errno.h */
# define ENOTSOCK WSAENOTSOCK
# undef EDESTADDRREQ /* override definition in errno.h */
# define EDESTADDRREQ WSAEDESTADDRREQ
# undef EMSGSIZE /* override definition in errno.h */
# define EMSGSIZE WSAEMSGSIZE
# undef EPROTOTYPE /* override definition in errno.h */
# define EPROTOTYPE WSAEPROTOTYPE
# undef ENOPROTOOPT /* override definition in errno.h */
# define ENOPROTOOPT WSAENOPROTOOPT
# undef EPROTONOSUPPORT /* override definition in errno.h */
# define EPROTONOSUPPORT WSAEPROTONOSUPPORT
# define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
# undef EOPNOTSUPP /* override definition in errno.h */
# define EOPNOTSUPP WSAEOPNOTSUPP
# define EPFNOSUPPORT WSAEPFNOSUPPORT
# undef EAFNOSUPPORT /* override definition in errno.h */
# define EAFNOSUPPORT WSAEAFNOSUPPORT
# undef EADDRINUSE /* override definition in errno.h */
# define EADDRINUSE WSAEADDRINUSE
# undef EADDRNOTAVAIL /* override definition in errno.h */
# define EADDRNOTAVAIL WSAEADDRNOTAVAIL
# undef ENETDOWN /* override definition in errno.h */
# define ENETDOWN WSAENETDOWN
# undef ENETUNREACH /* override definition in errno.h */
# define ENETUNREACH WSAENETUNREACH
# undef ENETRESET /* override definition in errno.h */
# define ENETRESET WSAENETRESET
# undef ECONNABORTED /* override definition in errno.h */
# define ECONNABORTED WSAECONNABORTED
# undef ECONNRESET /* override definition in errno.h */
# define ECONNRESET WSAECONNRESET
# undef ENOBUFS /* override definition in errno.h */
# define ENOBUFS WSAENOBUFS
# undef EISCONN /* override definition in errno.h */
# define EISCONN WSAEISCONN
# undef ENOTCONN /* override definition in errno.h */
# define ENOTCONN WSAENOTCONN
# define ESHUTDOWN WSAESHUTDOWN
# define ETOOMANYREFS WSAETOOMANYREFS
# undef ETIMEDOUT /* override definition in errno.h */
# define ETIMEDOUT WSAETIMEDOUT
# undef ECONNREFUSED /* override definition in errno.h */
# define ECONNREFUSED WSAECONNREFUSED
# undef ELOOP /* override definition in errno.h */
# define ELOOP WSAELOOP
# ifndef ENAMETOOLONG /* possible previous definition in errno.h */
# define ENAMETOOLONG WSAENAMETOOLONG
# endif
# define EHOSTDOWN WSAEHOSTDOWN
# undef EHOSTUNREACH /* override definition in errno.h */
# define EHOSTUNREACH WSAEHOSTUNREACH
# ifndef ENOTEMPTY /* possible previous definition in errno.h */
# define ENOTEMPTY WSAENOTEMPTY
# endif
# define EPROCLIM WSAEPROCLIM
# define EUSERS WSAEUSERS
# define EDQUOT WSAEDQUOT
# define ESTALE WSAESTALE
# define EREMOTE WSAEREMOTE
# endif
# ifndef HAVE_WRITEV
/* Structure for scatter/gather I/O. */
struct iovec {
@ -87,65 +175,202 @@ struct iovec {
} ;
# endif
/* Return 1 if the specified error number describes a readiness error, or 0
* otherwise . This is mostly for HP - UX , which could return EAGAIN or
* EWOULDBLOCK . See this man page
*
* http : //devrsrc1.external.hp.com/STKS/cgi-bin/man2html?
* manpage = / usr / share / man / man2 . Z / send .2
*/
ares_bool_t ares__socket_try_again ( int errnum )
static ares_conn_err_t ares__socket_deref_error ( int err )
{
# if !defined EWOULDBLOCK && !defined EAGAIN
# error "Neither EWOULDBLOCK nor EAGAIN defined"
switch ( err ) {
# if defined(EWOULDBLOCK)
case EWOULDBLOCK :
return ARES_CONN_ERR_WOULDBLOCK ;
# endif
# if defined(EAGAIN) && (!defined(EWOULDBLOCK) || EAGAIN != EWOULDBLOCK)
case EAGAIN :
return ARES_CONN_ERR_WOULDBLOCK ;
# endif
case EINPROGRESS :
return ARES_CONN_ERR_WOULDBLOCK ;
case ENETDOWN :
return ARES_CONN_ERR_NETDOWN ;
case ENETUNREACH :
return ARES_CONN_ERR_NETUNREACH ;
case ECONNABORTED :
return ARES_CONN_ERR_CONNABORTED ;
case ECONNRESET :
return ARES_CONN_ERR_CONNRESET ;
case ECONNREFUSED :
return ARES_CONN_ERR_CONNREFUSED ;
case ETIMEDOUT :
return ARES_CONN_ERR_CONNTIMEDOUT ;
case EHOSTDOWN :
return ARES_CONN_ERR_HOSTDOWN ;
case EHOSTUNREACH :
return ARES_CONN_ERR_HOSTUNREACH ;
case EINTR :
return ARES_CONN_ERR_INTERRUPT ;
case EAFNOSUPPORT :
return ARES_CONN_ERR_AFNOSUPPORT ;
case EADDRNOTAVAIL :
return ARES_CONN_ERR_BADADDR ;
default :
break ;
}
# ifdef EWOULDBLOCK
if ( errnum = = EWOULDBLOCK ) {
return ARES_TRUE ;
return ARES_CONN_ERR_FAILURE ;
}
# endif
# if defined EAGAIN && EAGAIN != EWOULDBLOCK
if ( errnum = = EAGAIN ) {
return ARES_TRUE ;
static ares_bool_t same_address ( const struct sockaddr * sa ,
const struct ares_addr * aa )
{
const void * addr1 ;
const void * addr2 ;
if ( sa - > sa_family = = aa - > family ) {
switch ( aa - > family ) {
case AF_INET :
addr1 = & aa - > addr . addr4 ;
addr2 = & ( CARES_INADDR_CAST ( const struct sockaddr_in * , sa ) ) - > sin_addr ;
if ( memcmp ( addr1 , addr2 , sizeof ( aa - > addr . addr4 ) ) = = 0 ) {
return ARES_TRUE ; /* match */
}
break ;
case AF_INET6 :
addr1 = & aa - > addr . addr6 ;
addr2 =
& ( CARES_INADDR_CAST ( const struct sockaddr_in6 * , sa ) ) - > sin6_addr ;
if ( memcmp ( addr1 , addr2 , sizeof ( aa - > addr . addr6 ) ) = = 0 ) {
return ARES_TRUE ; /* match */
}
break ;
default :
break ; /* LCOV_EXCL_LINE */
}
}
return ARES_FALSE ; /* different */
}
# endif
return ARES_FALSE ;
void ares__conn_sock_state_cb_update ( ares_conn_t * conn ,
ares_conn_state_flags_t flags )
{
ares_channel_t * channel = conn - > server - > channel ;
if ( ( conn - > state_flags & ARES_CONN_STATE_CBFLAGS ) ! = flags & &
channel - > sock_state_cb ) {
channel - > sock_state_cb ( channel - > sock_state_cb_data , conn - > fd ,
flags & ARES_CONN_STATE_READ ? 1 : 0 ,
flags & ARES_CONN_STATE_WRITE ? 1 : 0 ) ;
}
conn - > state_flags & = ~ ( ( unsigned int ) ARES_CONN_STATE_CBFLAGS ) ;
conn - > state_flags | = flags ;
}
ares_ssize_t ares__socket_recv ( ares_channel_t * channel , ares_socket_t s ,
void * data , size_t data_len )
ares_conn_err_t ares__socket_recv ( ares_channel_t * channel , ares_socket_t s ,
ares_bool_t is_tcp , void * data ,
size_t data_len , size_t * read_bytes )
{
ares_ssize_t rv ;
* read_bytes = 0 ;
if ( channel - > sock_funcs & & channel - > sock_funcs - > arecvfrom ) {
return channel - > sock_funcs - > arecvfrom ( s , data , data_len , 0 , 0 , 0 ,
rv = channel - > sock_funcs - > arecvfrom ( s , data , data_len , 0 , 0 , 0 ,
channel - > sock_func_cb_data ) ;
} else {
rv = ( ares_ssize_t ) recv ( ( RECV_TYPE_ARG1 ) s , ( RECV_TYPE_ARG2 ) data ,
( RECV_TYPE_ARG3 ) data_len , ( RECV_TYPE_ARG4 ) ( 0 ) ) ;
}
return ( ares_ssize_t ) recv ( ( RECV_TYPE_ARG1 ) s , ( RECV_TYPE_ARG2 ) data ,
( RECV_TYPE_ARG3 ) data_len , ( RECV_TYPE_ARG4 ) ( 0 ) ) ;
if ( rv > 0 ) {
* read_bytes = ( size_t ) rv ;
return ARES_CONN_ERR_SUCCESS ;
}
if ( rv = = 0 ) {
/* UDP allows 0-byte packets and is connectionless, so this is success */
if ( ! is_tcp ) {
return ARES_CONN_ERR_SUCCESS ;
} else {
return ARES_CONN_ERR_CONNCLOSED ;
}
}
ares_ssize_t ares__socket_recvfrom ( ares_channel_t * channel , ares_socket_t s ,
void * data , size_t data_len , int flags ,
/* If we're here, rv<0 */
return ares__socket_deref_error ( SOCKERRNO ) ;
}
ares_conn_err_t ares__socket_recvfrom ( ares_channel_t * channel , ares_socket_t s ,
ares_bool_t is_tcp , void * data ,
size_t data_len , int flags ,
struct sockaddr * from ,
ares_socklen_t * from_len )
ares_socklen_t * from_len ,
size_t * read_bytes )
{
ares_ssize_t rv ;
if ( channel - > sock_funcs & & channel - > sock_funcs - > arecvfrom ) {
return channel - > sock_funcs - > arecvfrom ( s , data , data_len , flags , from ,
rv = channel - > sock_funcs - > arecvfrom ( s , data , data_len , flags , from ,
from_len , channel - > sock_func_cb_data ) ;
}
} else {
# ifdef HAVE_RECVFROM
return ( ares_ssize_t ) recvfrom ( s , data , ( RECVFROM_TYPE_ARG3 ) data_len , flags ,
rv = ( ares_ssize_t ) recvfrom ( s , data , ( RECVFROM_TYPE_ARG3 ) data_len , flags ,
from , from_len ) ;
# else
return ares__socket_recv ( channel , s , data , data_len ) ;
return ares__socket_recv ( channel , s , is_udp , data , data_len ) ;
# endif
}
if ( rv > 0 ) {
* read_bytes = ( size_t ) rv ;
return ARES_CONN_ERR_SUCCESS ;
}
if ( rv = = 0 ) {
/* UDP allows 0-byte packets and is connectionless, so this is success */
if ( ! is_tcp ) {
return ARES_CONN_ERR_SUCCESS ;
} else {
return ARES_CONN_ERR_CONNCLOSED ;
}
}
/* If we're here, rv<0 */
return ares__socket_deref_error ( SOCKERRNO ) ;
}
ares_conn_err_t ares__conn_read ( ares_conn_t * conn , void * data , size_t len ,
size_t * read_bytes )
{
ares_channel_t * channel = conn - > server - > channel ;
ares_conn_err_t err ;
if ( ! ( conn - > flags & ARES_CONN_FLAG_TCP ) ) {
struct sockaddr_storage sa_storage ;
ares_socklen_t salen = sizeof ( sa_storage ) ;
memset ( & sa_storage , 0 , sizeof ( sa_storage ) ) ;
err =
ares__socket_recvfrom ( channel , conn - > fd , ARES_FALSE , data , len , 0 ,
( struct sockaddr * ) & sa_storage , & salen , read_bytes ) ;
# ifdef HAVE_RECVFROM
if ( err = = ARES_CONN_ERR_SUCCESS & &
! same_address ( ( struct sockaddr * ) & sa_storage , & conn - > server - > addr ) ) {
err = ARES_CONN_ERR_WOULDBLOCK ;
}
# endif
} else {
err =
ares__socket_recv ( channel , conn - > fd , ARES_TRUE , data , len , read_bytes ) ;
}
/* Toggle connected state if needed */
if ( err = = ARES_CONN_ERR_SUCCESS ) {
conn - > state_flags | = ARES_CONN_STATE_CONNECTED ;
}
return err ;
}
/* Use like:
* struct sockaddr_storage sa_storage ;
* ares_socklen_t salen = sizeof ( sa_storage ) ;
@ -230,10 +455,23 @@ static ares_status_t ares_conn_set_self_ip(ares_conn_t *conn, ares_bool_t early)
return ARES_SUCCESS ;
}
ares_ssize_t ares__conn_write ( ares_conn_t * conn , const void * data , size_t len )
ares_conn_err_t ares__conn_write ( ares_conn_t * conn , const void * data ,
size_t len , size_t * written )
{
ares_channel_t * channel = conn - > server - > channel ;
int flags = 0 ;
ares_ssize_t rv ;
ares_bool_t is_tfo = ARES_FALSE ;
ares_conn_err_t err = ARES_CONN_ERR_SUCCESS ;
* written = 0 ;
/* Don't try to write if not doing initial TFO and not connected */
if ( conn - > flags & ARES_CONN_FLAG_TCP & &
! ( conn - > state_flags & ARES_CONN_STATE_CONNECTED ) & &
! ( conn - > flags & ARES_CONN_FLAG_TFO_INITIAL ) ) {
return ARES_CONN_ERR_WOULDBLOCK ;
}
# ifdef HAVE_MSG_NOSIGNAL
flags | = MSG_NOSIGNAL ;
@ -243,41 +481,162 @@ ares_ssize_t ares__conn_write(ares_conn_t *conn, const void *data, size_t len)
struct iovec vec ;
vec . iov_base = ( void * ) ( ( size_t ) data ) ; /* Cast off const */
vec . iov_len = len ;
return channel - > sock_funcs - > asendv ( conn - > fd , & vec , 1 ,
rv = channel - > sock_funcs - > asendv ( conn - > fd , & vec , 1 ,
channel - > sock_func_cb_data ) ;
if ( rv < = 0 ) {
err = ares__socket_deref_error ( SOCKERRNO ) ;
} else {
* written = ( size_t ) rv ;
}
goto done ;
}
if ( conn - > flags & ARES_CONN_FLAG_TFO_INITIAL ) {
conn - > flags & = ~ ( ( unsigned int ) ARES_CONN_FLAG_TFO_INITIAL ) ;
is_tfo = ARES_TRUE ;
# if defined(TFO_USE_SENDTO) && TFO_USE_SENDTO
{
struct sockaddr_storage sa_storage ;
ares_socklen_t salen = sizeof ( sa_storage ) ;
struct sockaddr * sa = ( struct sockaddr * ) & sa_storage ;
ares_status_t status ;
ares_ssize_t rv ;
status = ares__conn_set_sockaddr ( conn , sa , & salen ) ;
if ( status ! = ARES_SUCCESS ) {
return status ;
if ( ares__conn_set_sockaddr ( conn , sa , & salen ) ! = ARES_SUCCESS ) {
return ARES_CONN_ERR_FAILURE ;
}
rv = ( ares_ssize_t ) sendto ( ( SEND_TYPE_ARG1 ) conn - > fd , ( SEND_TYPE_ARG2 ) data ,
( SEND_TYPE_ARG3 ) len , ( SEND_TYPE_ARG4 ) flags , sa ,
salen ) ;
if ( rv < = 0 ) {
err = ares__socket_deref_error ( SOCKERRNO ) ;
} else {
* written = ( size_t ) rv ;
}
/* If using TFO, we might not have been able to get an IP earlier, since
* we hadn ' t informed the OS of the destination . When using sendto ( )
* now we have so we should be able to fetch it */
ares_conn_set_self_ip ( conn , ARES_TRUE ) ;
return rv ;
ares_conn_set_self_ip ( conn , ARES_FALS E ) ;
goto done ;
}
# endif
}
return ( ares_ssize_t ) send ( ( SEND_TYPE_ARG1 ) conn - > fd , ( SEND_TYPE_ARG2 ) data ,
rv = ( ares_ssize_t ) send ( ( SEND_TYPE_ARG1 ) conn - > fd , ( SEND_TYPE_ARG2 ) data ,
( SEND_TYPE_ARG3 ) len , ( SEND_TYPE_ARG4 ) flags ) ;
if ( rv < = 0 ) {
err = ares__socket_deref_error ( SOCKERRNO ) ;
} else {
* written = ( size_t ) rv ;
}
goto done ;
done :
if ( err = = ARES_CONN_ERR_SUCCESS & & len = = * written ) {
/* Wrote all data, make sure we're not listening for write events unless
* using TFO , in which case we ' ll need a write event to know when
* we ' re connected . */
ares__conn_sock_state_cb_update (
conn , ARES_CONN_STATE_READ |
( is_tfo ? ARES_CONN_STATE_WRITE : ARES_CONN_STATE_NONE ) ) ;
} else if ( err = = ARES_CONN_ERR_WOULDBLOCK ) {
/* Need to wait on more buffer space to write */
ares__conn_sock_state_cb_update ( conn , ARES_CONN_STATE_READ |
ARES_CONN_STATE_WRITE ) ;
}
return err ;
}
ares_status_t ares__conn_flush ( ares_conn_t * conn )
{
const unsigned char * data ;
size_t data_len ;
size_t count ;
ares_conn_err_t err ;
ares_status_t status ;
ares_bool_t tfo = ARES_FALSE ;
if ( conn = = NULL ) {
return ARES_EFORMERR ;
}
if ( conn - > flags & ARES_CONN_FLAG_TFO_INITIAL ) {
tfo = ARES_TRUE ;
}
do {
if ( ares__buf_len ( conn - > out_buf ) = = 0 ) {
status = ARES_SUCCESS ;
goto done ;
}
if ( conn - > flags & ARES_CONN_FLAG_TCP ) {
data = ares__buf_peek ( conn - > out_buf , & data_len ) ;
} else {
unsigned short msg_len ;
/* Read length, then provide buffer without length */
ares__buf_tag ( conn - > out_buf ) ;
status = ares__buf_fetch_be16 ( conn - > out_buf , & msg_len ) ;
if ( status ! = ARES_SUCCESS ) {
return status ;
}
ares__buf_tag_rollback ( conn - > out_buf ) ;
data = ares__buf_peek ( conn - > out_buf , & data_len ) ;
if ( data_len < msg_len + 2 ) {
status = ARES_EFORMERR ;
goto done ;
}
data + = 2 ;
data_len = msg_len ;
}
err = ares__conn_write ( conn , data , data_len , & count ) ;
if ( err ! = ARES_CONN_ERR_SUCCESS ) {
if ( err ! = ARES_CONN_ERR_WOULDBLOCK ) {
status = ARES_ECONNREFUSED ;
goto done ;
}
status = ARES_SUCCESS ;
goto done ;
}
/* UDP didn't send the length prefix so augment that here */
if ( ! ( conn - > flags & ARES_CONN_FLAG_TCP ) ) {
count + = 2 ;
}
/* Strip data written from the buffer */
ares__buf_consume ( conn - > out_buf , ( size_t ) count ) ;
status = ARES_SUCCESS ;
/* Loop only for UDP since we have to send per-packet. We already
* sent everything we could if using tcp */
} while ( ! ( conn - > flags & ARES_CONN_FLAG_TCP ) ) ;
done :
if ( status = = ARES_SUCCESS ) {
ares_conn_state_flags_t flags = ARES_CONN_STATE_READ ;
/* When using TFO, the we need to enabling waiting on a write event to
* be notified of when a connection is actually established */
if ( tfo ) {
flags | = ARES_CONN_STATE_WRITE ;
}
/* If using TCP and not all data was written (partial write), that means
* we need to also wait on a write event */
if ( conn - > flags & ARES_CONN_FLAG_TCP & & ares__buf_len ( conn - > out_buf ) ) {
flags | = ARES_CONN_STATE_WRITE ;
}
ares__conn_sock_state_cb_update ( conn , flags ) ;
}
return status ;
}
/*
@ -516,10 +875,11 @@ static ares_status_t ares__conn_connect(ares_conn_t *conn, struct sockaddr *sa,
# elif defined(TFO_USE_CONNECTX) && TFO_USE_CONNECTX
{
int rv ;
in t err ;
ares_conn_err_ t err ;
do {
sa_endpoints_t endpoints ;
memset ( & endpoints , 0 , sizeof ( endpoints ) ) ;
endpoints . sae_dstaddr = sa ;
endpoints . sae_dstaddrlen = salen ;
@ -528,12 +888,16 @@ static ares_status_t ares__conn_connect(ares_conn_t *conn, struct sockaddr *sa,
CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE ,
NULL , 0 , NULL , NULL ) ;
err = SOCKERRNO ;
if ( rv = = - 1 & & err ! = EINPROGRESS & & err ! = EWOULDBLOCK ) {
return ARES_ECONNREFUSED ;
if ( rv < 0 ) {
err = ares__socket_deref_error ( SOCKERRNO ) ;
} else {
break ;
}
} while ( rv = = - 1 & & err = = EINTR ) ;
if ( err ! = ARES_CONN_ERR_WOULDBLOCK & & err ! = ARES_CONN_ERR_INTERRUPT ) {
return ARES_ECONNREFUSED ;
}
} while ( err = = ARES_CONN_ERR_INTERRUPT ) ;
}
return ARES_SUCCESS ;
# elif defined(TFO_SUPPORTED) && TFO_SUPPORTED
@ -555,6 +919,7 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
ares_conn_t * conn ;
ares__llist_node_t * node = NULL ;
int stype = is_tcp ? SOCK_STREAM : SOCK_DGRAM ;
ares_conn_state_flags_t state_flags ;
* conn_out = NULL ;
@ -568,6 +933,16 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
conn - > server = server ;
conn - > queries_to_conn = ares__llist_create ( NULL ) ;
conn - > flags = is_tcp ? ARES_CONN_FLAG_TCP : ARES_CONN_FLAG_NONE ;
conn - > out_buf = ares__buf_create ( ) ;
conn - > in_buf = ares__buf_create ( ) ;
if ( conn - > queries_to_conn = = NULL | | conn - > out_buf = = NULL | |
conn - > in_buf = = NULL ) {
/* LCOV_EXCL_START: OutOfMemory */
status = ARES_ENOMEM ;
goto done ;
/* LCOV_EXCL_STOP */
}
/* Enable TFO if the OS supports it and we were passed in data to send during
* the connect . It might be disabled later if an error is encountered . Make
@ -577,13 +952,6 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
conn - > flags | = ARES_CONN_FLAG_TFO ;
}
if ( conn - > queries_to_conn = = NULL ) {
/* LCOV_EXCL_START: OutOfMemory */
status = ARES_ENOMEM ;
goto done ;
/* LCOV_EXCL_STOP */
}
/* Convert into the struct sockaddr structure needed by the OS */
status = ares__conn_set_sockaddr ( conn , sa , & salen ) ;
if ( status ! = ARES_SUCCESS ) {
@ -591,8 +959,8 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
}
/* Acquire a socket. */
conn - > fd = ares__open_socket ( channel , server - > addr . family , stype , 0 ) ;
if ( conn - > fd = = ARES_SOCKET_BAD ) {
if ( ares__open_socket ( & conn - > fd , channel , server - > addr . family , stype , 0 ) ! =
ARES_CONN_ERR_SUCCESS ) {
status = ARES_ECONNREFUSED ;
goto done ;
}
@ -633,7 +1001,7 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
}
/* Need to store our own ip for DNS cookie support */
status = ares_conn_set_self_ip ( conn , ARES_FALS E ) ;
status = ares_conn_set_self_ip ( conn , ARES_TRU E ) ;
if ( status ! = ARES_SUCCESS ) {
goto done ; /* LCOV_EXCL_LINE: UntestablePath */
}
@ -662,7 +1030,20 @@ ares_status_t ares__open_connection(ares_conn_t **conn_out,
/* LCOV_EXCL_STOP */
}
SOCK_STATE_CALLBACK ( channel , conn - > fd , 1 , is_tcp ? 1 : 0 ) ;
state_flags = ARES_CONN_STATE_READ ;
/* Get notified on connect if using TCP */
if ( conn - > flags & ARES_CONN_FLAG_TCP ) {
state_flags | = ARES_CONN_STATE_WRITE ;
}
/* Dot no attempt to update sock state callbacks on TFO until *after* the
* initial write is performed . Due to the notification event , its possible
* an erroneous read can come in before the attempt to write the data which
* might be used to set the ip address */
if ( ! ( conn - > flags & ARES_CONN_FLAG_TFO_INITIAL ) ) {
ares__conn_sock_state_cb_update ( conn , state_flags ) ;
}
if ( is_tcp ) {
server - > tcp_conn = conn ;
@ -673,6 +1054,8 @@ done:
ares__llist_node_claim ( node ) ;
ares__llist_destroy ( conn - > queries_to_conn ) ;
ares__close_socket ( channel , conn - > fd ) ;
ares__buf_destroy ( conn - > out_buf ) ;
ares__buf_destroy ( conn - > in_buf ) ;
ares_free ( conn ) ;
} else {
* conn_out = conn ;
@ -680,15 +1063,27 @@ done:
return status ;
}
ares_socket_t ares__open_socket ( ares_channel_t * channel , int af , int type ,
int protocol )
ares_conn_err_t ares__open_socket ( ares_socket_t * sock , ares_channel_t * channel ,
int af , int type , int protocol )
{
ares_socket_t s ;
* sock = ARES_SOCKET_BAD ;
if ( channel - > sock_funcs & & channel - > sock_funcs - > asocket ) {
return channel - > sock_funcs - > asocket ( af , type , protocol ,
s = channel - > sock_funcs - > asocket ( af , type , protocol ,
channel - > sock_func_cb_data ) ;
} else {
s = socket ( af , type , protocol ) ;
}
return socket ( af , type , protocol ) ;
if ( s = = ARES_SOCKET_BAD ) {
return ares__socket_deref_error ( SOCKERRNO ) ;
}
* sock = s ;
return ARES_CONN_ERR_SUCCESS ;
}
ares_status_t ares__connect_socket ( ares_channel_t * channel ,
@ -697,7 +1092,7 @@ ares_status_t ares__connect_socket(ares_channel_t *channel,
ares_socklen_t addrlen )
{
int rv ;
in t err ;
ares_conn_err_ t err ;
do {
if ( channel - > sock_funcs & & channel - > sock_funcs - > aconnect ) {
@ -707,13 +1102,16 @@ ares_status_t ares__connect_socket(ares_channel_t *channel,
rv = connect ( sockfd , addr , addrlen ) ;
}
err = SOCKERRNO ;
if ( rv < 0 ) {
err = ares__socket_deref_error ( SOCKERRNO ) ;
} else {
break ;
}
if ( rv = = - 1 & & err ! = EINPROGRESS & & err ! = EWOULDBLOCK ) {
if ( err ! = ARES_CONN_ERR_WOULDBLOCK & & err ! = ARES_CONN_ERR_INTERRUPT ) {
return ARES_ECONNREFUSED ;
}
} while ( rv = = - 1 & & err = = EINTR ) ;
} while ( err = = ARES_CONN_ERR_INTERRUPT ) ;
return ARES_SUCCESS ;
}
@ -762,3 +1160,14 @@ void ares_set_socket_functions(ares_channel_t *channel,
channel - > sock_funcs = funcs ;
channel - > sock_func_cb_data = data ;
}
void ares_set_notify_pending_write_callback (
ares_channel_t * channel , ares_notify_pending_write_callback callback ,
void * user_data )
{
if ( channel = = NULL | | channel - > optmask & ARES_OPT_EVENT_THREAD ) {
return ;
}
channel - > notify_pending_write_cb = callback ;
channel - > notify_pending_write_cb_data = user_data ;
}