%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% module #include "dk4sock.h" #include #if !DK4_ON_WINDOWS #if DK4_HAVE_FCNTL_H #ifndef FCNTL_H_INCLUDED #include #define FCNTL_H_INCLUDED 1 #endif #endif #endif #if DK4_HAVE_ERRNO_H #ifndef ERRNO_H_INCLUDED #include #define ERRNO_H_INCLUDED 1 #endif #endif #if DK4_HAVE_UNISTD_H #ifndef UNISTD_H_INCLUDED #include #define UNISTD_H_INCLUDED 1 #endif #endif #if DK4_HAVE_IO_H #ifndef IO_H_INCLUDED #include #define IO_H_INCLUDED 1 #endif #endif #if DK4_HAVE_STRING_H #ifndef STRING_H_INCLUDED #include #define STRING_H_INCLUDED 1 #endif #endif $!trace-include static dk4_socket_t dk4socket_tcp_client_cleanup_socket( dk4_socket_t sock, int level, struct sockaddr *pAddr, size_t szAddr ) { #if 0 /* Setting SO_REUSEADDR or SO_REUSEPORT must follow socket() immediately. */ if ((0 < level) || ((NULL != pAddr) && (0 < szAddr))) { (void)dk4socket_option_set_reuse(sock, 1, NULL); } #endif #if DK4_ON_WINDOWS closesocket(sock); #else close(sock); #endif return (INVALID_SOCKET); } static dk4_socket_t dk4socket_tcp_client_connect_attempt( dk4_socket_t back, struct sockaddr *pAddr, size_t szAddr, struct sockaddr *pLoAddr, size_t szLoAddr, long secs, long usecs, dk4_er_t *erp ) { fd_set wfds; struct timeval to; #if (!DK4_ON_WINDOWS) && (DK4_HAVE_SETSOCKOPT) \ && defined(SOL_SOCKET) && defined(SO_ERROR) #if DK4_HAVE_SOCKLEN_T socklen_t slo; #else int slo; #endif int err; #endif int res; $? "+ dk4socket_tcp_client_connect_attempt" $? ". remote = PTR=%d %lu", TR_IPTR(pAddr), (unsigned long)szAddr res = dk4socket_connect(back, pAddr, szAddr, erp); switch (res) { case DK4_SOCKET_RESULT_SUCCESS : { $? ". connected" /* do nothing */ } break; case DK4_SOCKET_RESULT_IN_PROGRESS : { $? ". in progress" FD_ZERO(&wfds); FD_SET(back, &wfds); to.tv_sec = secs; to.tv_usec = usecs; dk4socket_error_reset(); res = select((((int)back) + 1), NULL, &wfds, NULL, &to); if (0 < res) { $? ". select" if (FD_ISSET(back, &wfds)) { $? ". in write set" #if (!DK4_ON_WINDOWS) && (DK4_HAVE_SETSOCKOPT) \ && defined(SOL_SOCKET) && defined(SO_ERROR) err = 0; slo = sizeof(err); dk4socket_error_reset(); res = getsockopt(back, SOL_SOCKET, SO_ERROR, &err, &slo); if (0 == res) { $? ". getsockopt ok" if (0 != dk4socket_non_null_getsockopt(err, slo)) { $? "! non-null from getsockopt" dk4error_set_idetails(erp, DK4_E_SOCKET_CONNECT, err); back = dk4socket_tcp_client_cleanup_socket( back, 1, pLoAddr, szLoAddr ); } #if VERSION_BEFORE_20160415 if (sizeof(int) != slo) { if (0 != err) { $? "! error from getsockopt" dk4error_set_idetails(erp, DK4_E_SOCKET_CONNECT, err); back = dk4socket_tcp_client_cleanup_socket( back, 1, pLoAddr, szLoAddr ); } } else { $? "! getsockopt (size)" dk4socket_error_report(erp, DK4_E_SOCKET_GETSOCKOPT); back = dk4socket_tcp_client_cleanup_socket( back, 1, pLoAddr, szLoAddr ); } #endif } else { $? "! getsockopt" dk4socket_error_report(erp, DK4_E_SOCKET_GETSOCKOPT); back = dk4socket_tcp_client_cleanup_socket(back,1,pLoAddr,szLoAddr); } #endif } else { $? "! timeout (not in list)" dk4error_set_simple_error_code(erp, DK4_E_SOCKET_TIMEOUT); back = dk4socket_tcp_client_cleanup_socket(back,0,pLoAddr,szLoAddr); } } else { if (0 > res) { $? "! select" dk4socket_error_report(erp, DK4_E_SOCKET_SELECT); } else { $? "! timeout (from select)" dk4error_set_simple_error_code(erp, DK4_E_SOCKET_TIMEOUT); } back = dk4socket_tcp_client_cleanup_socket(back,0,pLoAddr,szLoAddr); } } break; default : { $? "! failed" back = dk4socket_tcp_client_cleanup_socket(back, 0, pLoAddr, szLoAddr); } break; } $? "- dk4socket_tcp_client_connect_attempt %d", (int)back return back; } dk4_socket_t dk4socket_tcp_client_for_addr( int protfam, struct sockaddr *pRemoteAddr, size_t szRemoteAddr, struct sockaddr *pLocalAddr, size_t szLocalAddr, long seconds, long microSeconds, dk4_er_t *erp ) { dk4_socket_t back = INVALID_SOCKET; int res; #if DK4_HAVE_SETSOCKOPT && (defined(SO_REUSEPORT) || defined(SO_REUSEADDR)) int opt = 0; #endif $? "+ dk4socket_tcp_client_for_addr" if ((NULL != pRemoteAddr) && (0 < szRemoteAddr)) { $? ". args ok" $? ". remote = PTR=%d %lu",TR_IPTR(pRemoteAddr),(unsigned long)szRemoteAddr back = dk4socket_open(protfam, SOCK_STREAM, 0, erp); if (INVALID_SOCKET != back) { $? ". socket" if ((NULL != pLocalAddr) && (0 < szLocalAddr)) { $? ". must bind" #if DK4_HAVE_SETSOCKOPT && defined(SO_REUSEPORT) $? ". SO_REUSEPORT" dk4socket_set_one_bytes(&opt, sizeof(opt)); res = setsockopt( back, SOL_SOCKET, SO_REUSEPORT, (void *)(&opt), sizeof(opt) ); if (0 != res) { $? "! failed to reuse port number" } #else #if DK4_HAVE_SETSOCKOPT && defined(SO_REUSEADDR) $? ". SO_REUSEADDR" dk4socket_set_one_bytes(&opt, sizeof(opt)); res = setsockopt( back, SOL_SOCKET, SO_REUSEADDR, (void *)(&opt), sizeof(opt) ); if (0 != res) { $? "! failed to reuse address" } #else $? ". neither SO_REUSEPORT nor SO_REUSEADDR" #endif #endif res = dk4socket_bind(back, pLocalAddr, szLocalAddr, erp); if (DK4_SOCKET_RESULT_FAILED == res) { $? "! bind" back = dk4socket_tcp_client_cleanup_socket( back, 0, pLocalAddr, szLocalAddr ); } } if (INVALID_SOCKET != back) { if ((0L != seconds) || (0L != microSeconds)) { $? ". use timeout" res = dk4socket_option_set_nonblock(back, 1, erp); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". non-blocking" back = dk4socket_tcp_client_connect_attempt( back, pRemoteAddr, szRemoteAddr, pLocalAddr, szLocalAddr, seconds, microSeconds, erp ); if (INVALID_SOCKET != back) { res = dk4socket_option_set_nonblock(back, 0, erp); if (DK4_SOCKET_RESULT_SUCCESS != res) { $? "! back blocking" back = dk4socket_tcp_client_cleanup_socket( back, 1, pLocalAddr, szLocalAddr ); } } } else { $? "! non-blocking" back = dk4socket_tcp_client_cleanup_socket( back, 0, pLocalAddr, szLocalAddr ); } } else { $? ". no timeout" back = dk4socket_tcp_client_connect_attempt( back, pRemoteAddr, szRemoteAddr, pLocalAddr, szLocalAddr, 0L, 0L, erp ); } } } else { $? "! socket" $? "! errno=%d", errno } } else { $? "! args" dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4socket_tcp_client_for_addr %d", (int)back return back; }