Skip to content
Snippets Groups Projects
result.c 35.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* result.c - wait for an ldap result */
    
    /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
     *
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     * Copyright 1998-2010 The OpenLDAP Foundation.
    
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted only as authorized by the OpenLDAP
     * Public License.
     *
     * A copy of this license is available in the file LICENSE in the
     * top-level directory of the distribution or, alternatively, at
     * <http://www.OpenLDAP.org/license.html>.
    
    /* Portions Copyright (c) 1990 Regents of the University of Michigan.
     * All rights reserved.
    
    /* This notice applies to changes, created by or for Novell, Inc.,
    
     * to preexisting works for which notices appear elsewhere in this file.
     *
     * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
     *
     * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
     * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
     * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
     * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
     * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
     * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
     * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
     * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
    
     * Modification to OpenLDAP source by Novell, Inc.
     * April 2000 sfs Add code to process V3 referrals and search results
    
     *---
     * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 
    
     * can be found in the file "build/LICENSE-2.0.1" in this distribution
     * of OpenLDAP Software.
     */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
     * LDAPv3 (RFC 4511)
    
     *	LDAPResult ::= SEQUENCE {
    
     *		resultCode			ENUMERATED { ... },
     *		matchedDN			LDAPDN,
    
     *		referral			[3] Referral OPTIONAL
    
     *	}
     *	Referral ::= SEQUENCE OF LDAPURL	(one or more)
    
    #include "portable.h"
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    #include <stdio.h>
    
    
    #include <ac/stdlib.h>
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    #include <ac/errno.h>
    #include <ac/socket.h>
    #include <ac/string.h>
    #include <ac/time.h>
    #include <ac/unistd.h>
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    #include "ldap-int.h"
    
    Julius Enarusai's avatar
     
    Julius Enarusai committed
    #include "ldap_log.h"
    
    #include "lutil.h"
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int *idx ));
    static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int idx ));
    
    static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
    
    	LDAPMessage **result ));
    
    static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	int all, LDAPConn *lc, LDAPMessage **result ));
    
    static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
    
    static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
    
    static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    #define LDAP_MSG_X_KEEP_LOOKING		(-2)
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    /*
     * ldap_result - wait for an ldap result response to a message from the
    
     * ldap server.  If msgid is LDAP_RES_ANY (-1), any message will be
     * accepted.  If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
     * message is accepted.  Otherwise ldap_result will wait for a response
     * with msgid.  If all is LDAP_MSG_ONE (0) the first message with id
     * msgid will be accepted, otherwise, ldap_result will wait for all
     * responses with id msgid and then return a pointer to the entire list
    
     * of messages.  In general, this is only useful for search responses,
     * which can be of three message types (zero or more entries, zero or
     * search references, followed by an ldap result).  An extension to
     * LDAPv3 allows partial extended responses to be returned in response
     * to any request.  The type of the first message received is returned.
    
     * When waiting, any messages that have been abandoned/discarded are 
     * discarded.
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     *
     * Example:
     *	ldap_result( s, msgid, all, timeout, result )
     */
    int
    
    ldap_result(
    	LDAP *ld,
    	int msgid,
    	int all,
    	struct timeval *timeout,
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	LDAPMessage **result )
    {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	assert( ld != NULL );
    	assert( result != NULL );
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    #ifdef LDAP_R_COMPILE
    	ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
    #endif
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	rc = wait4msg( ld, msgid, all, timeout, result );
    
    #ifdef LDAP_R_COMPILE
    	ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
    #endif
    
    }
    
    static LDAPMessage *
    
    chkResponseList(
    	LDAP *ld,
    	int msgid,
    	int all)
    
    	LDAPMessage	*lm, **lastlm, *nextlm;
    
    	 * Look through the list of responses we have received on
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	 * this association and see if the response we're interested in
    	 * is there.  If it is, return it.  If not, call wait4msg() to
    	 * wait until it arrives or timeout occurs.
    	 */
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    #ifdef LDAP_R_COMPILE
    	LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
    #endif
    
    
    	Debug( LDAP_DEBUG_TRACE,
    
    		"ldap_chkResponseList ld %p msgid %d all %d\n",
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		(void *)ld, msgid, all );
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    
    
    	lastlm = &ld->ld_responses;
    
    	for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		nextlm = lm->lm_next;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    		if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
    			Debug( LDAP_DEBUG_ANY,
    				"response list msg abandoned, "
    				"msgid %d message type %s\n",
    				lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
    
    			switch ( lm->lm_msgtype ) {
    			case LDAP_RES_SEARCH_ENTRY:
    			case LDAP_RES_SEARCH_REFERENCE:
    			case LDAP_RES_INTERMEDIATE:
    				break;
    
    			default:
    				/* there's no need to keep the id
    				 * in the abandoned list any longer */
    				ldap_mark_abandoned( ld, lm->lm_msgid, idx );
    				break;
    			}
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    			/* Remove this entry from list */
    			*lastlm = nextlm;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    			ldap_msgfree( lm );
    
    			continue;
    		}
    
    		if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
    			LDAPMessage	*tmp;
    
    
    			if ( all == LDAP_MSG_ONE ||
    				all == LDAP_MSG_RECEIVED ||
    
    				msgid == LDAP_RES_UNSOLICITED )
    			{
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				break;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    			tmp = lm->lm_chain_tail;
    
    			if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
    				tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
    				tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			}
    
    
    			if ( tmp == NULL ) {
    
    				lm = NULL;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			}
    
    			break;
    		}
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	}
    
    		/* Found an entry, remove it from the list */
    
    		if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
    
    			*lastlm = lm->lm_chain;
    
    Jong Hyuk Choi's avatar
    Jong Hyuk Choi committed
    			lm->lm_chain->lm_next = lm->lm_next;
    			lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
    			lm->lm_chain = NULL;
    			lm->lm_chain_tail = NULL;
    
    		Debug( LDAP_DEBUG_TRACE,
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			"ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
    
    		Debug( LDAP_DEBUG_TRACE,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			"ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
    
    			(void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    }
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    static int
    
    wait4msg(
    	LDAP *ld,
    	ber_int_t msgid,
    	int all,
    	struct timeval *timeout,
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	LDAPMessage **result )
    {
    	int		rc;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	struct timeval	tv = { 0 },
    			tv0 = { 0 },
    
    			start_time_tv = { 0 },
    			*tvp = NULL;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	assert( ld != NULL );
    	assert( result != NULL );
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    #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;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    #ifdef LDAP_DEBUG
    	if ( timeout == NULL ) {
    
    		Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			(void *)ld, msgid, 0 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	} else {
    
    		Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			(void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	}
    #endif /* LDAP_DEBUG */
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	if ( timeout != NULL && timeout->tv_sec != -1 ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		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 */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	}
    		    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	rc = LDAP_MSG_X_KEEP_LOOKING;
    	while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    #ifdef LDAP_DEBUG
    		if ( ldap_debug & LDAP_DEBUG_TRACE ) {
    
    			Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				(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
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		}
    #endif /* LDAP_DEBUG */
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		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 ) )
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				{
    
    					lc_ready = 1;
    
    			}
    #ifdef LDAP_R_COMPILE
    			ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
    #endif
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( !lc_ready ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				int err;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				rc = ldap_int_select( ld, tvp );
    				if ( rc == -1 ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					err = sock_errno();
    #ifdef LDAP_DEBUG
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    					Debug( LDAP_DEBUG_TRACE,
    						"ldap_int_select returned -1: errno %d\n",
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						err, 0, 0 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    #endif
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				}
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				if ( rc == 0 || ( rc == -1 && (
    					!LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						|| err != EINTR ) ) )
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				{
    					ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
    						LDAP_TIMEOUT);
    					return( rc );
    				}
    
    				if ( rc == -1 ) {
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    					rc = LDAP_MSG_X_KEEP_LOOKING;	/* select interrupted: loop */
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				} else {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					lc_ready = 1;
    				}
    			}
    			if ( lc_ready ) {
    				LDAPConn *lnext;
    				rc = LDAP_MSG_X_KEEP_LOOKING;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				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 );
    				}
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
    				ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				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 ) )
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    					{
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						/* Don't let it get freed out from under us */
    						++lc->lconn_refcnt;
    #ifdef LDAP_R_COMPILE
    						ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
    #endif
    						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 ) {
    
    #ifdef LDAP_R_COMPILE
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    							ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    							ldap_free_connection( ld, lc, 0, 1 );
    
    #ifdef LDAP_R_COMPILE
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    							ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						} else {
    							--lc->lconn_refcnt;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    						}
    
    #ifdef LDAP_R_COMPILE
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					} else {
    						lnext = lc->lconn_next;
    					}
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				}
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    #ifdef LDAP_R_COMPILE
    				ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
    #endif
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			}
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		}
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		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 */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				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 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    			start_time_tv.tv_sec = curr_time_tv.tv_sec;
    			start_time_tv.tv_usec = curr_time_tv.tv_usec;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	return( rc );
    
    static ber_tag_t
    try_read1msg(
    	LDAP *ld,
    	ber_int_t msgid,
    	int all,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	LDAPConn *lc,
    
    	LDAPMessage **result )
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    {
    
    	int		idx;
    
    	ber_tag_t	tag;
    	ber_len_t	len;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	int		foundit = 0;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	BerElement	tmpber;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	int		rc, refer_cnt, hadref, simple_request, err;
    
    	LDAPMessage	*tmp = NULL, *chain_head = NULL;
    
    	int		moremsgs = 0, isv2 = 0;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	assert( ld != NULL );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	assert( lc != NULL );
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    #ifdef LDAP_R_COMPILE
    	LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
    #endif
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
    		(void *)ld, msgid, all );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		lc->lconn_ber = ldap_alloc_ber_with_options( ld );
    
    		if ( lc->lconn_ber == NULL ) {
    
    	assert( LBER_VALID (ber) );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	/* get the next message */
    
    #ifdef LDAP_CONNECTIONLESS
    	if ( LDAP_IS_UDP(ld) ) {
    		struct sockaddr from;
    
    		ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
    
    		if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
    
    	tag = ber_get_next( lc->lconn_sb, &len, ber );
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	switch ( tag ) {
    	case LDAP_TAG_MESSAGE:
    
    		/*
    	 	 * We read a complete message.
    	 	 * The connection should no longer need this ber.
    	 	 */
    		lc->lconn_ber = NULL;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		break;
    
    	case LBER_DEFAULT:
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		err = sock_errno();
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		Debug( LDAP_DEBUG_CONNS,
    			"ber_get_next failed.\n", 0, 0, 0 );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		ld->ld_errno = LDAP_SERVER_DOWN;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		--lc->lconn_refcnt;
    		lc->lconn_status = 0;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		return -1;
    
    	default:
    
    		ld->ld_errno = LDAP_LOCAL_ERROR;
    		return -1;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	}
    
    	/* message id */
    
    	if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		ld->ld_errno = LDAP_DECODING_ERROR;
    		return( -1 );
    	}
    
    
    	/* id == 0 iff unsolicited notification message (RFC 4511) */
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	/* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
    	if ( id < 0 ) {
    		goto retry_ber;
    	}
    	
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	/* if it's been abandoned, toss it */
    
    	if ( id > 0 ) {
    		if ( ldap_abandoned( ld, id, &idx ) ) {
    			/* the message type */
    			tag = ber_peek_tag( ber, &len );
    			switch ( tag ) {
    			case LDAP_RES_SEARCH_ENTRY:
    			case LDAP_RES_SEARCH_REFERENCE:
    			case LDAP_RES_INTERMEDIATE:
    			case LBER_ERROR:
    				break;
    
    			default:
    				/* there's no need to keep the id
    				 * in the abandoned list any longer */
    				ldap_mark_abandoned( ld, id, idx );
    				break;
    			}
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				"abandoned/discarded ld %p msgid %d message type %s\n",
    				(void *)ld, id, ldap_int_msgtype2str( tag ) );
    
    			ber_free( ber, 1 );
    			if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
    				goto retry;
    			}
    			return( LDAP_MSG_X_KEEP_LOOKING );	/* continue looking */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    		lr = ldap_find_request_by_msgid( ld, id );
    		if ( lr == NULL ) {
    			const char	*msg = "unknown";
    
    			/* the message type */
    			tag = ber_peek_tag( ber, &len );
    			switch ( tag ) {
    			case LBER_ERROR:
    				break;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				"no request for response on ld %p msgid %d message type %s (tossing)\n",
    				(void *)ld, id, msg );
    
    #ifdef LDAP_CONNECTIONLESS
    
    Howard Chu's avatar
    Howard Chu committed
    nextresp2:
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	/* the message type */
    
    	tag = ber_peek_tag( ber, &len );
    	if ( tag == LBER_ERROR ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		ld->ld_errno = LDAP_DECODING_ERROR;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		return( -1 );
    	}
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		"read1msg: ld %p msgid %d message type %s\n",
    		(void *)ld, id, ldap_int_msgtype2str( tag ) );
    
    	if ( id == 0 ) {
    		/* unsolicited notification message (RFC 4511) */
    		if ( tag != LDAP_RES_EXTENDED ) {
    			/* toss it */
    			goto retry_ber;
    
    			/* strictly speaking, it's an error; from RFC 4511:
    
    4.4.  Unsolicited Notification
    
       An unsolicited notification is an LDAPMessage sent from the server to
       the client that is not in response to any LDAPMessage received by the
       server.  It is used to signal an extraordinary condition in the
       server or in the LDAP session between the client and the server.  The
       notification is of an advisory nature, and the server will not expect
       any response to be returned from the client.
    
       The unsolicited notification is structured as an LDAPMessage in which
       the messageID is zero and protocolOp is set to the extendedResp
       choice using the ExtendedResponse type (See Section 4.12).  The
       responseName field of the ExtendedResponse always contains an LDAPOID
       that is unique for this notification.
    
    			 * however, since unsolicited responses
    			 * are of advisory nature, better
    			 * toss it, right now
    			 */
    
    #if 0
    			ld->ld_errno = LDAP_DECODING_ERROR;
    			ber_free( ber, 1 );
    			return( -1 );
    #endif
    		}
    
    		lr = &dummy_lr;
    	}
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	refer_cnt = 0;
    	hadref = simple_request = 0;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	rc = LDAP_MSG_X_KEEP_LOOKING;	/* default is to keep looking (no response found) */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	lr->lr_res_msgtype = tag;
    
    
    	 * Check for V3 search reference
    
    	if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
    		if ( ld->ld_version > LDAP_VERSION2 ) {
    
    			/* This is a V3 search reference */
    
    			if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
    					lr->lr_parent != NULL )
    
    				char **refs = NULL;
    				tmpber = *ber;
    
    
    				/* Get the referral list */
    				if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
    					rc = LDAP_DECODING_ERROR;
    
    					/* Note: refs array is freed by ldap_chase_v3referrals */
    
    					refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
    
    						1, &lr->lr_res_error, &hadref );
    
    					if ( refer_cnt > 0 ) {
    
    						/* successfully chased reference */
    
    						/* If haven't got end search, set chasing referrals */
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    						if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
    
    							lr->lr_status = LDAP_REQST_CHASINGREFS;
    							Debug( LDAP_DEBUG_TRACE,
    
    								"read1msg:  search ref chased, "
    								"mark request chasing refs, "
    								"id = %d\n",
    
    								lr->lr_msgid, 0, 0 );
    
    
    	} else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
    		/* All results that just return a status, i.e. don't return data
    		 * go through the following code.  This code also chases V2 referrals
    		 * and checks if all referrals have been chased.
    		 */
    
    		char		*lr_res_error = NULL;
    
    		tmpber = *ber; 	/* struct copy */
    		if ( ber_scanf( &tmpber, "{eAA", &lderr,
    				&lr->lr_res_matched, &lr_res_error )
    				!= LBER_ERROR )
    		{
    			if ( lr_res_error != NULL ) {
    				if ( lr->lr_res_error != NULL ) {
    					(void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
    					LDAP_FREE( (char *)lr_res_error );
    
    				} else {
    					lr->lr_res_error = lr_res_error;
    
    				lr_res_error = NULL;
    			}
    
    			/* Do we need to check for referrals? */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( tag != LDAP_RES_BIND &&
    				( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
    					lr->lr_parent != NULL ))
    
    			{
    				char		**refs = NULL;
    				ber_len_t	len;
    
    				/* Check if V3 referral */
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
    
    					if ( ld->ld_version > LDAP_VERSION2 ) {
    
    						/* Get the referral list */
    
    						if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
    
    							rc = LDAP_DECODING_ERROR;
    							lr->lr_status = LDAP_REQST_COMPLETED;
    							Debug( LDAP_DEBUG_TRACE,
    
    								"read1msg: referral decode error, "
    								"mark request completed, ld %p msgid %d\n",
    								(void *)ld, lr->lr_msgid, 0 );
    
    						} else {
    							/* Chase the referral 
    
    							 * refs array is freed by ldap_chase_v3referrals
    
    							 */
    							refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    								0, &lr->lr_res_error, &hadref );
    
    							lr->lr_status = LDAP_REQST_COMPLETED;
    							Debug( LDAP_DEBUG_TRACE,
    
    								"read1msg: referral %s chased, "
    
    								"mark request completed, ld %p msgid %d\n",
    
    								refer_cnt > 0 ? "" : "not",
    
    								(void *)ld, lr->lr_msgid);
    
    							if ( refer_cnt < 0 ) {
    
    				} else {
    					switch ( lderr ) {
    					case LDAP_SUCCESS:
    					case LDAP_COMPARE_TRUE:
    					case LDAP_COMPARE_FALSE:
    						break;
    
    						/* pedantic, should never happen */
    						if ( lr->lr_res_error[ 0 ] == '\0' ) {
    							LDAP_FREE( lr->lr_res_error );
    							lr->lr_res_error = NULL;
    							break;	
    						}
    
    
    						/* V2 referrals are in error string */
    						refer_cnt = ldap_chase_referrals( ld, lr,
    							&lr->lr_res_error, -1, &hadref );
    						lr->lr_status = LDAP_REQST_COMPLETED;
    						Debug( LDAP_DEBUG_TRACE,
    							"read1msg:  V2 referral chased, "
    							"mark request completed, id = %d\n",
    							lr->lr_msgid, 0, 0 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				}
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    			/* save errno, message, and matched string */
    			if ( !hadref || lr->lr_res_error == NULL ) {
    				lr->lr_res_errno =
    					lderr == LDAP_PARTIAL_RESULTS
    					? LDAP_SUCCESS : lderr;
    
    			} else if ( ld->ld_errno != LDAP_SUCCESS ) {
    				lr->lr_res_errno = ld->ld_errno;
    
    			} else {
    				lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
    
    		/* in any case, don't leave any lr_res_error 'round */
    		if ( lr_res_error ) {
    			LDAP_FREE( lr_res_error );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		}
    
    		Debug( LDAP_DEBUG_TRACE,
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			"read1msg: ld %p %d new referrals\n",
    			(void *)ld, refer_cnt, 0 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    		if ( refer_cnt != 0 ) {	/* chasing referrals */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			if ( refer_cnt < 0 ) {
    
    				ldap_return_request( ld, lr, 0 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				return( -1 );	/* fatal error */
    			}
    
    			lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( lr->lr_res_matched ) {
    				LDAP_FREE( lr->lr_res_matched );
    				lr->lr_res_matched = NULL;
    			}
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		} else {
    			if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
    				/* request without any referrals */
    				simple_request = ( hadref ? 0 : 1 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			} else {
    				/* request with referrals or child request */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			}
    
    
    			lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
    			Debug( LDAP_DEBUG_TRACE,
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				"read1msg:  mark request completed, ld %p msgid %d\n",
    				(void *)ld, lr->lr_msgid, 0);
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			while ( lr->lr_parent != NULL ) {
    				merge_error_info( ld, lr->lr_parent, lr );
    
    				lr = lr->lr_parent;
    				if ( --lr->lr_outrefcnt > 0 ) {
    					break;	/* not completely done yet */
    				}
    			}
    
    
    			/* Check if all requests are finished, lr is now parent */
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
    
    				for ( tmplr = lr->lr_child;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    					tmplr != NULL;
    
    					tmplr = tmplr->lr_refnext )
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				{
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    					if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
    
    				}
    			}
    
    			/* This is the parent request if the request has referrals */
    
    			if ( lr->lr_outrefcnt <= 0 &&
    				lr->lr_parent == NULL &&
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				tmplr == NULL )
    			{
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				id = lr->lr_msgid;
    				tag = lr->lr_res_msgtype;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
    					(void *)ld, id, 0 );
    
    				Debug( LDAP_DEBUG_TRACE,
    					"res_errno: %d, res_error: <%s>, "
    					"res_matched: <%s>\n",
    					lr->lr_res_errno,
    					lr->lr_res_error ? lr->lr_res_error : "",
    					lr->lr_res_matched ? lr->lr_res_matched : "" );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				if ( !simple_request ) {
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    					    == LBER_ERROR )
    					{
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    						rc = -1; /* fatal error */
    					}
    				}
    
    
    				if ( lr != &dummy_lr ) {
    					ldap_return_request( ld, lr, 1 );
    				}
    
    				lr = NULL;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			}
    
    
    			/*
    			 * RF 4511 unsolicited (id == 0) responses
    			 * shouldn't necessarily end the connection
    			 */
    			if ( lc != NULL && id != 0 ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				--lc->lconn_refcnt;
    				lc = NULL;
    
    	if ( lr != NULL ) {
    
    		if ( lr != &dummy_lr ) {
    			ldap_return_request( ld, lr, 0 );
    		}
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		return( rc );
    	}
    
    
    	/* try to handle unsolicited responses as appropriate */
    
    	if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    		tag = ber_peek_tag( &tmpber, &len );
    
    
    		/* we have a res oid */
    		if ( tag == LDAP_TAG_EXOP_RES_OID ) {
    
    			static struct berval	bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
    			struct berval		resoid = BER_BVNULL;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
    
    			assert( !BER_BVISEMPTY( &resoid ) );
    
    			is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			tag = ber_peek_tag( &tmpber, &len );
    
    #if 0 /* don't need right now */
    		/* we have res data */
    
    			struct berval resdata;
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
    
    				ld->ld_errno = LDAP_DECODING_ERROR;
    				ber_free( ber, 0 );
    				return ld->ld_errno;
    			}
    
    			/* use it... */
    
    
    		/* handle RFC 4511 "Notice of Disconnection" locally */
    
    		if ( is_nod ) {
    			if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
    				ld->ld_errno = LDAP_DECODING_ERROR;
    				ber_free( ber, 1 );
    				return -1;
    			}
    
    			/* get rid of the connection... */
    			if ( lc != NULL ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				--lc->lconn_refcnt;
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			/* need to return -1, because otherwise
    			 * a valid result is expected */
    			return -1;