static int wait4msg( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout, LDAPMessage **result ) { int rc; struct timeval tv = { 0 }, tv0 = { 0 }, start_time_tv = { 0 }, *tvp = NULL; LDAPConn *lc; assert( ld != NULL ); assert( result != NULL ); #ifdef LDAP_R_COMPILE LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); #endif if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) { tv = ld->ld_options.ldo_tm_api; timeout = &tv; } #ifdef LDAP_DEBUG if ( timeout == NULL ) { Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n", (void *)ld, msgid, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n", (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec ); } #endif /* LDAP_DEBUG */ if ( timeout != NULL ) { tv0 = *timeout; tv = *timeout; tvp = &tv; #ifdef HAVE_GETTIMEOFDAY gettimeofday( &start_time_tv, NULL ); #else /* ! HAVE_GETTIMEOFDAY */ time( &start_time_tv.tv_sec ); start_time_tv.tv_usec = 0; #endif /* ! HAVE_GETTIMEOFDAY */ } rc = LDAP_MSG_X_KEEP_LOOKING; while ( rc == LDAP_MSG_X_KEEP_LOOKING ) { #ifdef LDAP_DEBUG if ( ldap_debug & LDAP_DEBUG_TRACE ) { Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n", (void *)ld, msgid, all ); #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); #endif ldap_dump_connection( ld, ld->ld_conns, 1 ); #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex ); ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); #endif ldap_dump_requests_and_responses( ld ); #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); #endif } #endif /* LDAP_DEBUG */ if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) { rc = (*result)->lm_msgtype; } else { int lc_ready = 0; #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); #endif for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex ); #endif rc = try_read1msg( ld, msgid, all, &lc, result ); #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); #endif lc_ready = 1; break; } } #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex ); #endif if ( !lc_ready ) { rc = ldap_int_select( ld, tvp ); #ifdef LDAP_DEBUG if ( rc == -1 ) { Debug( LDAP_DEBUG_TRACE, "ldap_int_select returned -1: errno %d\n", sock_errno(), 0, 0 ); } #endif if ( rc == 0 || ( rc == -1 && ( !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART) || sock_errno() != EINTR ) ) ) { ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN : LDAP_TIMEOUT); return( rc ); } if ( rc == -1 ) { rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */ } else { rc = LDAP_MSG_X_KEEP_LOOKING; #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); #endif if ( ld->ld_requests && ld->ld_requests->lr_status == LDAP_REQST_WRITING && ldap_is_write_ready( ld, ld->ld_requests->lr_conn->lconn_sb ) ) { ldap_int_flush_request( ld, ld->ld_requests ); } #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); #endif for ( lc = ld->ld_conns; rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; ) { if ( lc->lconn_status == LDAP_CONNST_CONNECTED && ldap_is_read_ready( ld, lc->lconn_sb ) ) { #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex ); #endif rc = try_read1msg( ld, msgid, all, &lc, result ); #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex ); #endif if ( lc == NULL ) { /* if lc gets free()'d, * there's no guarantee * lc->lconn_next is still * sane; better restart * (ITS#4405) */ lc = ld->ld_conns; /* don't get to next conn! */ break; } } /* next conn */ lc = lc->lconn_next; } #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex ); #endif } } } if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) { struct timeval curr_time_tv = { 0 }, delta_time_tv = { 0 }; #ifdef HAVE_GETTIMEOFDAY gettimeofday( &curr_time_tv, NULL ); #else /* ! HAVE_GETTIMEOFDAY */ time( &curr_time_tv.tv_sec ); curr_time_tv.tv_usec = 0; #endif /* ! HAVE_GETTIMEOFDAY */ /* delta_time = tmp_time - start_time */ delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec; delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec; if ( delta_time_tv.tv_usec < 0 ) { delta_time_tv.tv_sec--; delta_time_tv.tv_usec += 1000000; } /* tv0 < delta_time ? */ if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) || ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) ) { rc = 0; /* timed out */ ld->ld_errno = LDAP_TIMEOUT; break; } /* tv0 -= delta_time */ tv0.tv_sec -= delta_time_tv.tv_sec; tv0.tv_usec -= delta_time_tv.tv_usec; if ( tv0.tv_usec < 0 ) { tv0.tv_sec--; tv0.tv_usec += 1000000; } tv.tv_sec = tv0.tv_sec; tv.tv_usec = tv0.tv_usec; Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n", (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec ); start_time_tv.tv_sec = curr_time_tv.tv_sec; start_time_tv.tv_usec = curr_time_tv.tv_usec; } } return( rc ); }
int ldap_send_server_request( LDAP *ld, BerElement *ber, ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist, LDAPConn *lc, LDAPreqinfo *bind, int m_noconn, int m_res ) { LDAPRequest *lr; int incparent, rc; LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 ); incparent = 0; ld->ld_errno = LDAP_SUCCESS; /* optimistic */ LDAP_CONN_LOCK_IF(m_noconn); if ( lc == NULL ) { if ( srvlist == NULL ) { lc = ld->ld_defconn; } else { lc = find_connection( ld, *srvlist, 1 ); if ( lc == NULL ) { if ( (bind != NULL) && (parentreq != NULL) ) { /* Remember the bind in the parent */ incparent = 1; ++parentreq->lr_outrefcnt; } lc = ldap_new_connection( ld, srvlist, 0, 1, bind, 1, m_res ); } } } /* 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, 1 ) ) { case 0: /* go on! */ lc->lconn_status = LDAP_CONNST_CONNECTED; break; case -2: /* async only occurs if a network timeout is set */ /* honor network timeout */ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec ) { /* caller will have to call again */ ld->ld_errno = LDAP_X_CONNECTING; } LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); /* fallthru */ default: /* error */ break; } } if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) { 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; } LDAP_CONN_UNLOCK_IF(m_noconn); return( -1 ); } use_connection( ld, lc ); #ifdef LDAP_CONNECTIONLESS if ( LDAP_IS_UDP( ld )) { BerElement tmpber = *ber; ber_rewind( &tmpber ); LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); rc = ber_write( &tmpber, ld->ld_options.ldo_peer, sizeof( struct sockaddr_storage ), 0 ); LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); if ( rc == -1 ) { ld->ld_errno = LDAP_ENCODING_ERROR; LDAP_CONN_UNLOCK_IF(m_noconn); return rc; } } #endif /* If we still have an incomplete write, try to finish it before * dealing with the new request. If we don't finish here, return * LDAP_BUSY and let the caller retry later. We only allow a single * request to be in WRITING state. */ rc = 0; if ( ld->ld_requests && ld->ld_requests->lr_status == LDAP_REQST_WRITING && ldap_int_flush_request( ld, ld->ld_requests ) < 0 ) { rc = -1; } if ( rc ) { LDAP_CONN_UNLOCK_IF(m_noconn); return rc; } lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) ); if ( lr == NULL ) { ld->ld_errno = LDAP_NO_MEMORY; ldap_free_connection( ld, lc, 0, 0 ); ber_free( ber, 1 ); if ( incparent ) { /* Forget about the bind */ --parentreq->lr_outrefcnt; } LDAP_CONN_UNLOCK_IF(m_noconn); return( -1 ); } lr->lr_msgid = msgid; lr->lr_status = LDAP_REQST_INPROGRESS; lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */ lr->lr_ber = ber; lr->lr_conn = lc; if ( parentreq != NULL ) { /* sub-request */ if ( !incparent ) { /* Increment if we didn't do it before the bind */ ++parentreq->lr_outrefcnt; } lr->lr_origid = parentreq->lr_origid; lr->lr_parentcnt = ++parentreq->lr_parentcnt; lr->lr_parent = parentreq; lr->lr_refnext = parentreq->lr_child; parentreq->lr_child = lr; } else { /* original request */ lr->lr_origid = lr->lr_msgid; } /* Extract requestDN for future reference */ #ifdef LDAP_CONNECTIONLESS if ( !LDAP_IS_UDP(ld) ) #endif { BerElement tmpber = *ber; ber_int_t bint; ber_tag_t tag, rtag; ber_reset( &tmpber, 1 ); rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag ); switch ( tag ) { case LDAP_REQ_BIND: rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint ); break; case LDAP_REQ_DELETE: break; default: rtag = ber_scanf( &tmpber, "{" /*}*/ ); case LDAP_REQ_ABANDON: break; } if ( tag != LDAP_REQ_ABANDON ) { ber_skip_tag( &tmpber, &lr->lr_dn.bv_len ); lr->lr_dn.bv_val = tmpber.ber_ptr; } } lr->lr_prev = NULL; lr->lr_next = ld->ld_requests; if ( lr->lr_next != NULL ) { lr->lr_next->lr_prev = lr; } ld->ld_requests = lr; ld->ld_errno = LDAP_SUCCESS; if ( ldap_int_flush_request( ld, lr ) == -1 ) { msgid = -1; } LDAP_CONN_UNLOCK_IF(m_noconn); return( msgid ); }
/* protected by res_mutex */ static int wait4msg( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout, LDAPMessage **result ) { int rc; struct timeval tv = { 0 }, tv0 = { 0 }, start_time_tv = { 0 }, *tvp = NULL; LDAPConn *lc; assert( ld != NULL ); assert( result != NULL ); LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) { tv = ld->ld_options.ldo_tm_api; timeout = &tv; } #ifdef LDAP_DEBUG if ( timeout == NULL ) { Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n", (void *)ld, msgid, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n", (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec ); } #endif /* LDAP_DEBUG */ if ( timeout != NULL && timeout->tv_sec != -1 ) { tv0 = *timeout; tv = *timeout; tvp = &tv; #ifdef HAVE_GETTIMEOFDAY gettimeofday( &start_time_tv, NULL ); #else /* ! HAVE_GETTIMEOFDAY */ start_time_tv.tv_sec = time( NULL ); start_time_tv.tv_usec = 0; #endif /* ! HAVE_GETTIMEOFDAY */ } rc = LDAP_MSG_X_KEEP_LOOKING; while ( rc == LDAP_MSG_X_KEEP_LOOKING ) { #ifdef LDAP_DEBUG if ( ldap_debug & LDAP_DEBUG_TRACE ) { Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n", (void *)ld, msgid, all ); ldap_dump_connection( ld, ld->ld_conns, 1 ); LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); ldap_dump_requests_and_responses( ld ); LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); } #endif /* LDAP_DEBUG */ if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) { rc = (*result)->lm_msgtype; } else { int lc_ready = 0; LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { lc_ready = 2; /* ready at ber level, not socket level */ break; } } if ( !lc_ready ) { int err; rc = ldap_int_select( ld, tvp ); if ( rc == -1 ) { err = sock_errno(); #ifdef LDAP_DEBUG Debug( LDAP_DEBUG_TRACE, "ldap_int_select returned -1: errno %d\n", err, 0, 0 ); #endif } if ( rc == 0 || ( rc == -1 && ( !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART) || err != EINTR ) ) ) { ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN : LDAP_TIMEOUT); LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); return( rc ); } if ( rc == -1 ) { rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */ } else { lc_ready = 1; } } if ( lc_ready ) { LDAPConn *lnext; int serviced = 0; rc = LDAP_MSG_X_KEEP_LOOKING; LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); if ( ld->ld_requests && ld->ld_requests->lr_status == LDAP_REQST_WRITING && ldap_is_write_ready( ld, ld->ld_requests->lr_conn->lconn_sb ) ) { serviced = 1; ldap_int_flush_request( ld, ld->ld_requests ); } for ( lc = ld->ld_conns; rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; lc = lnext ) { if ( lc->lconn_status == LDAP_CONNST_CONNECTED && ldap_is_read_ready( ld, lc->lconn_sb ) ) { serviced = 1; /* Don't let it get freed out from under us */ ++lc->lconn_refcnt; rc = try_read1msg( ld, msgid, all, lc, result ); lnext = lc->lconn_next; /* Only take locks if we're really freeing */ if ( lc->lconn_refcnt <= 1 ) { ldap_free_connection( ld, lc, 0, 1 ); } else { --lc->lconn_refcnt; } } else { lnext = lc->lconn_next; } } LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); /* Quit looping if no one handled any socket events */ if (!serviced && lc_ready == 1) rc = -1; } LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); } if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) { struct timeval curr_time_tv = { 0 }, delta_time_tv = { 0 }; #ifdef HAVE_GETTIMEOFDAY gettimeofday( &curr_time_tv, NULL ); #else /* ! HAVE_GETTIMEOFDAY */ curr_time_tv.tv_sec = time( NULL ); curr_time_tv.tv_usec = 0; #endif /* ! HAVE_GETTIMEOFDAY */ /* delta_time = tmp_time - start_time */ delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec; delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec; if ( delta_time_tv.tv_usec < 0 ) { delta_time_tv.tv_sec--; delta_time_tv.tv_usec += 1000000; } /* tv0 < delta_time ? */ if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) || ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) ) { rc = 0; /* timed out */ ld->ld_errno = LDAP_TIMEOUT; break; } /* tv0 -= delta_time */ tv0.tv_sec -= delta_time_tv.tv_sec; tv0.tv_usec -= delta_time_tv.tv_usec; if ( tv0.tv_usec < 0 ) { tv0.tv_sec--; tv0.tv_usec += 1000000; } tv.tv_sec = tv0.tv_sec; tv.tv_usec = tv0.tv_usec; Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n", (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec ); start_time_tv.tv_sec = curr_time_tv.tv_sec; start_time_tv.tv_usec = curr_time_tv.tv_usec; } } return( rc ); }