@@ -898,65 +898,139 @@ nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
898898 return NULL ;
899899}
900900
901- int
902- nc_sock_connect (const char * host , uint16_t port )
901+ /*
902+ Helper for a non-blocking connect (which is required because of the locking
903+ concept for e.g. call home settings). For more details see nc_sock_connect().
904+ */
905+ static int
906+ _non_blocking_connect (int timeout , int * sock_pending , struct addrinfo * res )
903907{
904- int i , sock = -1 , flags ;
905- struct addrinfo hints , * res_list , * res ;
906- char port_s [6 ]; /* length of string representation of short int */
907-
908- snprintf (port_s , 6 , "%u" , port );
909-
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- }
920-
921- for (res = res_list ; res != NULL ; res = res -> ai_next ) {
908+ int flags , ret = 0 ;
909+ int sock = -1 ;
910+ fd_set wset ;
911+ struct timeval ts ;
912+ int error = 0 ;
913+ socklen_t len = sizeof (int );
914+
915+ if (sock_pending && * sock_pending != -1 ) {
916+ VRB ("Trying to connect the pending socket=%d." , * sock_pending );
917+ sock = * sock_pending ;
918+ } else {
919+ assert (res );
920+ VRB ("Trying to connect via %s." , (res -> ai_family == AF_INET6 ) ? "IPv6" : "IPv4" );
921+ /* Connect to a server */
922922 sock = socket (res -> ai_family , res -> ai_socktype , res -> ai_protocol );
923923 if (sock == -1 ) {
924- /* socket was not created, try another resource */
925- continue ;
924+ ERR ( " socket couldn't be created." , strerror ( errno ));
925+ return -1 ;
926926 }
927-
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 ;
933- }
934-
935927 /* make the socket non-blocking */
936928 if (((flags = fcntl (sock , F_GETFL )) == -1 ) || (fcntl (sock , F_SETFL , flags | O_NONBLOCK ) == -1 )) {
937929 ERR ("Fcntl failed (%s)." , strerror (errno ));
938- goto error ;
930+ goto cleanup ;
931+ }
932+ /* non-blocking connect! */
933+ if (connect (sock , res -> ai_addr , res -> ai_addrlen ) < 0 ) {
934+ if (errno != EINPROGRESS ) {
935+ /* network connection failed, try another resource */
936+ ERR ("connect failed: (%s)." , strerror (errno ));
937+ goto cleanup ;
938+ }
939+ }
940+ /* check the usability of the socket */
941+ if (getsockopt (sock , SOL_SOCKET , SO_ERROR , & error , & len ) < 0 ) {
942+ ERR ("getsockopt failed: (%s)." , strerror (errno ));
943+ goto cleanup ;
944+ }
945+ if (error == ECONNREFUSED ) {
946+ /* network connection failed, try another resource */
947+ VRB ("getsockopt error: (%s)." , strerror (error ));
948+ goto cleanup ;
939949 }
940-
941- /* we're done, network connection established */
942- break ;
943950 }
951+ ts .tv_sec = timeout ;
952+ ts .tv_usec = 0 ;
944953
945- if (sock != -1 ) {
946- VRB ("Successfully connected to %s:%s over %s." , host , port_s , (res -> ai_family == AF_INET6 ) ? "IPv6" : "IPv4" );
954+ FD_ZERO (& wset );
955+ FD_SET (sock , & wset );
956+
957+ if ((ret = select (sock + 1 , NULL , & wset , NULL , (timeout != -1 ) ? & ts : NULL )) < 0 ) {
958+ ERR ("select failed: (%s)." , strerror (errno ));
959+ goto cleanup ;
947960 }
948- freeaddrinfo (res_list );
949961
962+ if (ret == 0 ) { //we had a timeout
963+ VRB ("timed out after %ds (%s)." , timeout , strerror (errno ));
964+ if (sock_pending ) {
965+ /* no sock-close, we'll try it again */
966+ * sock_pending = sock ;
967+ } else {
968+ close (sock );
969+ }
970+ return -1 ;
971+ }
950972 return sock ;
951973
952- error :
953- if (sock > -1 ) {
954- close ( sock ) ;
974+ cleanup :
975+ if (sock_pending ) {
976+ * sock_pending = -1 ;
955977 }
956- freeaddrinfo ( res_list );
978+ close ( sock );
957979 return -1 ;
958980}
959981
982+ /* A given timeout value limits the time how long the function blocks. If it has to block
983+ only for some seconds, a socket connection might not yet have been fully established.
984+ Therefore the active (pending) socket will be stored in *sock_pending, but the return
985+ value will be -1. In such a case a subsequent invokation is required, by providing the
986+ stored sock_pending, again.
987+ In general, if this function returns -1, when a timeout has been given, this function
988+ has to be invoked, until it returns a valid socket.
989+ */
990+ int
991+ nc_sock_connect (const char * host , uint16_t port , int timeout , int * sock_pending )
992+ {
993+ int i ;
994+ int sock = sock_pending ?* sock_pending :-1 ;
995+ struct addrinfo hints , * res_list , * res ;
996+ char port_s [6 ]; /* length of string representation of short int */
997+
998+ VRB ("nc_sock_connect(%s, %u, %d, %d)" , host , port , timeout , sock );
999+
1000+ /* no pending socket */
1001+ if (sock == -1 ) {
1002+ /* Connect to a server */
1003+ snprintf (port_s , 6 , "%u" , port );
1004+ memset (& hints , 0 , sizeof hints );
1005+ hints .ai_family = AF_UNSPEC ;
1006+ hints .ai_socktype = SOCK_STREAM ;
1007+ hints .ai_protocol = IPPROTO_TCP ;
1008+ i = getaddrinfo (host , port_s , & hints , & res_list );
1009+ if (i != 0 ) {
1010+ ERR ("Unable to translate the host address (%s)." , gai_strerror (i ));
1011+ return -1 ;
1012+ }
1013+
1014+ for (res = res_list ; res != NULL ; res = res -> ai_next ) {
1015+ sock = _non_blocking_connect (timeout , sock_pending , res );
1016+ if (sock == -1 && (!sock_pending || * sock_pending == -1 )) {
1017+ /* try the next resource */
1018+ continue ;
1019+ }
1020+ VRB ("Successfully connected to %s:%s over %s." , host , port_s , (res -> ai_family == AF_INET6 ) ? "IPv6" : "IPv4" );
1021+ break ;
1022+ }
1023+ freeaddrinfo (res_list );
1024+
1025+ } else {
1026+ /* try to get a connection with the pending socket */
1027+ assert (sock_pending );
1028+ sock = _non_blocking_connect (timeout , sock_pending , NULL );
1029+ }
1030+
1031+ return sock ;
1032+ }
1033+
9601034static NC_MSG_TYPE
9611035get_msg (struct nc_session * session , int timeout , uint64_t msgid , struct lyxml_elem * * msg )
9621036{
0 commit comments