From aa3c9bad3e7c98b11ab910d3e3e5c107f76f24cf Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Sun, 7 Jan 2007 19:20:46 +0000 Subject: [PATCH] First cut to truly async connect: - after connect(2), if async the connection is in "connecting" state - the first time a request is sent, "connecting" conns are polled - in case of success, the request is sent - in case still connecting, LDAP_X_CONNECTING is returned; clients are expected to retry later - the "async" behavior must be explicitly enabled by setting the LDAP_OPT_CONNECT_ASYNC option "local" connections need work --- include/ldap.h | 2 + libraries/libldap/ldap-int.h | 3 + libraries/libldap/options.c | 12 +++ libraries/libldap/os-ip.c | 152 +++++++++++++++++++++++++++++++++-- libraries/libldap/request.c | 40 +++++++-- 5 files changed, 196 insertions(+), 13 deletions(-) diff --git a/include/ldap.h b/include/ldap.h index 35187aeb6d..16f7eaa603 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -122,6 +122,7 @@ LDAP_BEGIN_DECL #define LDAP_OPT_REFERRAL_URLS 0x5007 /* Referral URLs */ #define LDAP_OPT_SOCKBUF 0x5008 /* sockbuf */ #define LDAP_OPT_DEFBASE 0x5009 /* searchbase */ +#define LDAP_OPT_CONNECT_ASYNC 0x5010 /* create connections asynchronously */ /* OpenLDAP TLS options */ #define LDAP_OPT_X_TLS 0x6000 @@ -653,6 +654,7 @@ typedef struct ldapcontrol { #define LDAP_MORE_RESULTS_TO_RETURN (-15) /* Obsolete */ #define LDAP_CLIENT_LOOP (-16) #define LDAP_REFERRAL_LIMIT_EXCEEDED (-17) +#define LDAP_X_CONNECTING (-18) /* diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h index c8a7ba5ca9..e37a7b81c3 100644 --- a/libraries/libldap/ldap-int.h +++ b/libraries/libldap/ldap-int.h @@ -117,6 +117,7 @@ LDAP_BEGIN_DECL #define LDAP_BOOL_REFERRALS 0 #define LDAP_BOOL_RESTART 1 #define LDAP_BOOL_TLS 3 +#define LDAP_BOOL_CONNECT_ASYNC 4 #define LDAP_BOOLEANS unsigned long #define LDAP_BOOL(n) ((LDAP_BOOLEANS)1 << (n)) @@ -510,6 +511,8 @@ LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest, const struct timeval *tm ); LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb, int proto, const char *host, int port, int async ); +LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s, + struct timeval *tvp ); #if defined(LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND) || \ defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) diff --git a/libraries/libldap/options.c b/libraries/libldap/options.c index 9b100447e4..029a0a1a9d 100644 --- a/libraries/libldap/options.c +++ b/libraries/libldap/options.c @@ -242,6 +242,10 @@ ldap_get_option( return LDAP_OPT_SUCCESS; + case LDAP_OPT_CONNECT_ASYNC: + * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC); + return LDAP_OPT_SUCCESS; + case LDAP_OPT_RESULT_CODE: if(ld == NULL) { /* bad param */ @@ -392,6 +396,14 @@ ldap_set_option( LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART); } return LDAP_OPT_SUCCESS; + + case LDAP_OPT_CONNECT_ASYNC: + if(invalue == LDAP_OPT_OFF) { + LDAP_BOOL_CLR(lo, LDAP_BOOL_CONNECT_ASYNC); + } else { + LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC); + } + return LDAP_OPT_SUCCESS; } /* options which can withstand invalue == NULL */ diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c index cfd3b20190..7194c827fb 100644 --- a/libraries/libldap/os-ip.c +++ b/libraries/libldap/os-ip.c @@ -214,6 +214,137 @@ ldap_pvt_is_socket_ready(LDAP *ld, int s) #endif /* HAVE_WINSOCK */ +/* NOTE: this is identical to analogous code in os-local.c */ +int +ldap_int_poll( + LDAP *ld, + ber_socket_t s, + struct timeval *tvp ) +{ + int timeout = INFTIM; + struct timeval tv = { 0 }; + int rc; + + if ( tvp != NULL ) { + tv = *tvp; + timeout = TV2MILLISEC( tvp ); + } + + osip_debug(ld, "ldap_int_poll: fd: %d tm: %ld\n", + s, tvp ? tvp->tv_sec : -1L, 0); + +#ifdef HAVE_POLL + { + struct pollfd fd; + + fd.fd = s; + fd.events = POLL_WRITE; + + do { + fd.revents = 0; + rc = poll( &fd, 1, timeout ); + + } while ( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) ); + + if ( rc == AC_SOCKET_ERROR ) { + return rc; + } + + if ( timeout == 0 && rc == 0 ) { + return -2; + } + + if ( fd.revents & POLL_WRITE ) { + if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) { + return -1; + } + + if ( ldap_pvt_ndelay_off( ld, s ) == -1 ) { + return -1; + } + return 0; + } + } +#else + { + fd_set wfds, *z = NULL; +#ifdef HAVE_WINSOCK + fd_set efds; +#endif + +#if defined( FD_SETSIZE ) && !defined( HAVE_WINSOCK ) + if ( s >= FD_SETSIZE ) { + rc = AC_SOCKET_ERROR; + tcp_close( s ); + ldap_pvt_set_errno( EMFILE ); + return rc; + } +#endif + + do { + FD_ZERO(&wfds); + FD_SET(s, &wfds ); + +#ifdef HAVE_WINSOCK + FD_ZERO(&efds); + FD_SET(s, &efds ); +#endif + + rc = select( ldap_int_tblsize, z, &wfds, +#ifdef HAVE_WINSOCK + &efds, +#else + z, +#endif + tvp ? &tv : NULL ); + } while ( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) ); + + if ( rc == AC_SOCKET_ERROR ) { + return rc; + } + + if ( timeout == 0 && rc == 0 ) { + return -2; + } + +#ifdef HAVE_WINSOCK + /* This means the connection failed */ + if ( FD_ISSET(s, &efds) ) { + int so_errno; + int dummy = sizeof(so_errno); + if ( getsockopt( s, SOL_SOCKET, SO_ERROR, + (char *) &so_errno, &dummy ) == AC_SOCKET_ERROR || !so_errno ) + { + /* impossible */ + so_errno = WSAGetLastError(); + } + ldap_pvt_set_errno( so_errno ); + osip_debug(ld, "ldap_int_poll: error on socket %d: " + "errno: %d (%s)\n", s, errno, sock_errstr( errno )); + return -1; + } +#endif + if ( FD_ISSET(s, &wfds) ) { +#ifndef HAVE_WINSOCK + if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) { + return -1; + } +#endif + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) { + return -1; + } + return 0; + } + } +#endif + + osip_debug(ld, "ldap_int_poll: timed out\n",0,0,0); + ldap_pvt_set_errno( ETIMEDOUT ); + return -1; +} + static int ldap_pvt_connect(LDAP *ld, ber_socket_t s, struct sockaddr *sin, socklen_t addrlen, @@ -240,7 +371,7 @@ ldap_pvt_connect(LDAP *ld, ber_socket_t s, tv = *opt_tv; } - osip_debug(ld, "ldap_connect_timeout: fd: %d tm: %ld async: %d\n", + osip_debug(ld, "ldap_pvt_connect: fd: %d tm: %ld async: %d\n", s, opt_tv ? tv.tv_sec : -1L, async); if ( opt_tv && ldap_pvt_ndelay_on(ld, s) == -1 ) @@ -257,10 +388,14 @@ ldap_pvt_connect(LDAP *ld, ber_socket_t s, return ( -1 ); } -#ifdef notyet - if ( async ) return ( -2 ); -#endif + if ( async ) { + /* caller will call ldap_int_poll() as appropriate? */ + return ( -2 ); + } + + rc = ldap_int_poll( ld, s, opt_tv ); +#if 0 #ifdef HAVE_POLL { struct pollfd fd; @@ -349,9 +484,10 @@ ldap_pvt_connect(LDAP *ld, ber_socket_t s, } #endif - osip_debug(ld, "ldap_connect_timeout: timed out\n",0,0,0); - ldap_pvt_set_errno( ETIMEDOUT ); - return -1; +#endif + + osip_debug(ld, "ldap_pvt_connect: %d\n", rc, 0, 0); + return rc; } #ifndef HAVE_INET_ATON @@ -482,7 +618,7 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, rc = ldap_pvt_connect( ld, s, sai->ai_addr, sai->ai_addrlen, async ); - if ( (rc == 0) || (rc == -2) ) { + if ( rc == 0 || rc == -2 ) { ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s ); break; } diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c index daab508c8a..bf81bd6f44 100644 --- a/libraries/libldap/request.c +++ b/libraries/libldap/request.c @@ -209,11 +209,37 @@ ldap_send_server_request( } } + /* async connect... */ + if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) { + ber_socket_t sd = AC_SOCKET_ERROR; + struct timeval tv = { 0 }; + + ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd ); + + /* poll ... */ + switch ( ldap_int_poll( ld, sd, &tv ) ) { + case 0: + /* go on! */ + lc->lconn_status = LDAP_CONNST_CONNECTED; + break; + + case -2: + /* caller will have to call again */ + ld->ld_errno = LDAP_X_CONNECTING; + /* fallthru */ + + default: + /* error */ + break; + } + } + if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) { - ber_free( ber, 1 ); if ( ld->ld_errno == LDAP_SUCCESS ) { ld->ld_errno = LDAP_SERVER_DOWN; } + + ber_free( ber, 1 ); if ( incparent ) { /* Forget about the bind */ --parentreq->lr_outrefcnt; @@ -312,6 +338,7 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, int connect, LDAPreqinfo *bind ) { LDAPConn *lc; + int async = 0; Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n", use_ldsb, connect, (bind != NULL) ); @@ -341,8 +368,10 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, if ( connect ) { LDAPURLDesc **srvp, *srv = NULL; + async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC ); + for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) { - if ( ldap_int_open_connection( ld, lc, *srvp, 0 ) != -1 ) + if ( ldap_int_open_connection( ld, lc, *srvp, async) != -1 ) { srv = *srvp; @@ -366,7 +395,7 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, lc->lconn_server = ldap_url_dup( srv ); } - lc->lconn_status = LDAP_CONNST_CONNECTED; + lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED; #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); #endif @@ -663,8 +692,9 @@ ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) } Debug( LDAP_DEBUG_TRACE, " refcnt: %d status: %s\n", lc->lconn_refcnt, ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) - ? "NeedSocket" : ( lc->lconn_status == LDAP_CONNST_CONNECTING ) - ? "Connecting" : "Connected", 0 ); + ? "NeedSocket" : + ( lc->lconn_status == LDAP_CONNST_CONNECTING ) + ? "Connecting" : "Connected", 0 ); Debug( LDAP_DEBUG_TRACE, " last used: %s%s\n", ldap_pvt_ctime( &lc->lconn_lastused, timebuf ), lc->lconn_rebind_inprogress ? " rebind in progress" : "", 0 ); -- 2.39.5