@@ -898,65 +898,107 @@ nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
898898 return NULL ;
899899}
900900
901+ /* A given timeout value limits the time how long the function blocks. If it has to block
902+ only for some seconds, a socket connection might not yet have been fully established.
903+ Therefore the active (pending) socket will be stored in *sock_pending, but the return
904+ value will be -1. In such a case a subsequent invokation is required, by providing the
905+ stored sock_pending, again.
906+ In general, if this function returns -1, when a timeout has been given, this function
907+ has to be invoked, until it returns a valid socket.
908+ */
901909int
902- nc_sock_connect (const char * host , uint16_t port )
910+ nc_sock_connect (const char * host , uint16_t port , int timeout , int * sock_pending )
903911{
904- int i , sock = -1 , flags ;
912+ int i , flags , ret = 0 ;
913+ int sock = sock_pending ?* sock_pending :-1 ;
914+ fd_set wset ;
905915 struct addrinfo hints , * res_list , * res ;
906916 char port_s [6 ]; /* length of string representation of short int */
917+ struct timeval ts ;
907918
908- snprintf (port_s , 6 , "%u" , port );
919+ ts .tv_sec = timeout ;
920+ ts .tv_usec = 0 ;
909921
910- /* Connect to a server */
911- memset (& hints , 0 , sizeof hints );
912- hints .ai_family = AF_UNSPEC ;
913- hints .ai_socktype = SOCK_STREAM ;
914- hints .ai_protocol = IPPROTO_TCP ;
915- i = getaddrinfo (host , port_s , & hints , & res_list );
916- if (i != 0 ) {
917- ERR ("Unable to translate the host address (%s)." , gai_strerror (i ));
918- return -1 ;
919- }
922+ VRB ("nc_sock_connect(%s, %u, %d, %d)" , host , port , timeout , sock );
920923
921- for (res = res_list ; res != NULL ; res = res -> ai_next ) {
922- sock = socket (res -> ai_family , res -> ai_socktype , res -> ai_protocol );
923- if (sock == -1 ) {
924- /* socket was not created, try another resource */
925- continue ;
924+ /* no pending socket */
925+ if (sock == -1 ) {
926+ /* Connect to a server */
927+ snprintf (port_s , 6 , "%u" , port );
928+ memset (& hints , 0 , sizeof hints );
929+ hints .ai_family = AF_UNSPEC ;
930+ hints .ai_socktype = SOCK_STREAM ;
931+ hints .ai_protocol = IPPROTO_TCP ;
932+ i = getaddrinfo (host , port_s , & hints , & res_list );
933+ if (i != 0 ) {
934+ ERR ("Unable to translate the host address (%s)." , gai_strerror (i ));
935+ return -1 ;
926936 }
927937
928- if (connect (sock , res -> ai_addr , res -> ai_addrlen ) == -1 ) {
929- /* network connection failed, try another resource */
930- close (sock );
931- sock = -1 ;
932- continue ;
938+ for (res = res_list ; res != NULL ; res = res -> ai_next ) {
939+ sock = socket (res -> ai_family , res -> ai_socktype , res -> ai_protocol );
940+ if (sock == -1 ) {
941+ /* socket was not created, try another resource */
942+ continue ;
943+ }
944+ /* make the socket non-blocking */
945+ if (((flags = fcntl (sock , F_GETFL )) == -1 ) || (fcntl (sock , F_SETFL , flags | O_NONBLOCK ) == -1 )) {
946+ ERR ("Fcntl failed (%s)." , strerror (errno ));
947+ close (sock );
948+ freeaddrinfo (res_list );
949+ return -1 ;
950+ }
951+ /* enable keep-alive */
952+ i = 1 ;
953+ if (setsockopt (sock , SOL_SOCKET , SO_KEEPALIVE , & i , sizeof i ) == -1 ) {
954+ ERR ("Setsockopt failed (%s)." , strerror (errno ));
955+ close (sock );
956+ freeaddrinfo (res_list );
957+ return -1 ;
958+ }
959+ /* non-blocking connect! */
960+ if (connect (sock , res -> ai_addr , res -> ai_addrlen ) < 0 ) {
961+ if (errno != EINPROGRESS ) {
962+ /* network connection failed, try another resource */
963+ VRB ("connect failed: (%s)." , strerror (errno ));
964+ close (sock );
965+ sock = -1 ;
966+ continue ;
967+ }
968+ }
933969 }
970+ freeaddrinfo (res_list );
971+ }
972+ /* new socket or pending socket */
973+ if (sock != -1 ) {
934974
935- /* make the socket non-blocking */
936- if (((flags = fcntl (sock , F_GETFL )) == -1 ) || (fcntl (sock , F_SETFL , flags | O_NONBLOCK ) == -1 )) {
937- ERR ("Fcntl failed (%s)." , strerror (errno ));
975+ FD_ZERO (& wset );
976+ FD_SET (sock , & wset );
977+
978+ if ((ret = select (sock + 1 , NULL , & wset , NULL , (timeout != -1 ) ? & ts : NULL )) < 0 ) {
979+ ERR ("select failed: (%s)." , strerror (errno ));
938980 close (sock );
939- freeaddrinfo (res_list );
940981 return -1 ;
941982 }
942983
943- /* enable keep-alive */
944- i = 1 ;
945- if (setsockopt (sock , SOL_SOCKET , SO_KEEPALIVE , & i , sizeof i ) == -1 ) {
946- ERR ("Setsockopt failed (%s)." , strerror (errno ));
947- close (sock );
948- freeaddrinfo (res_list );
984+ if (ret == 0 ) { //we had a timeout
985+ VRB ("timed out after %ds (%s)." , timeout , strerror (errno ));
986+ /* in that case we need to store it as pending for another attempt */
987+ if (sock_pending ) {
988+ * sock_pending = sock ;
989+ } else {
990+ close (sock );
991+ }
949992 return -1 ;
950993 }
951994
952- /* we're done, network connection established */
953- break ;
954- }
955-
956- if ( sock != -1 ) {
957- VRB ("Successfully connected to %s:%s over %s ." , host , port_s , ( res -> ai_family == AF_INET6 ) ? "IPv6" : "IPv4" );
995+ if (! FD_ISSET ( sock , & wset )) {
996+ ERR ( "FD_ISSET failed: (%s)." , strerror ( errno )) ;
997+ close ( sock );
998+ return -1 ;
999+ }
1000+ VRB ("Successfully connected to %s:%s." , host , port_s );
9581001 }
959- freeaddrinfo (res_list );
9601002
9611003 return sock ;
9621004}
0 commit comments