Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: request.c,v 1.4 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2024 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
     18  * All rights reserved.
     19  */
     20 /* This notice applies to changes, created by or for Novell, Inc.,
     21  * to preexisting works for which notices appear elsewhere in this file.
     22  *
     23  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
     24  *
     25  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
     26  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
     27  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
     28  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
     29  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
     30  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
     31  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
     32  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
     33  *---
     34  * Modification to OpenLDAP source by Novell, Inc.
     35  * April 2000 sfs  Added code to chase V3 referrals
     36  *  request.c - sending of ldap requests; handling of referrals
     37  *---
     38  * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
     39  * can be found in the file "build/LICENSE-2.0.1" in this distribution
     40  * of OpenLDAP Software.
     41  */
     42 
     43 #include <sys/cdefs.h>
     44 __RCSID("$NetBSD: request.c,v 1.4 2025/09/05 21:16:21 christos Exp $");
     45 
     46 #include "portable.h"
     47 
     48 #include <stdio.h>
     49 
     50 #include <ac/stdlib.h>
     51 
     52 #include <ac/errno.h>
     53 #include <ac/param.h>
     54 #include <ac/socket.h>
     55 #include <ac/string.h>
     56 #include <ac/time.h>
     57 #include <ac/unistd.h>
     58 
     59 #include "ldap-int.h"
     60 #include "lber.h"
     61 
     62 /* used by ldap_send_server_request and ldap_new_connection */
     63 #ifdef LDAP_R_COMPILE
     64 #define LDAP_CONN_LOCK_IF(nolock) \
     65 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); }
     66 #define LDAP_CONN_UNLOCK_IF(nolock) \
     67 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); }
     68 #define LDAP_REQ_LOCK_IF(nolock) \
     69 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); }
     70 #define LDAP_REQ_UNLOCK_IF(nolock) \
     71 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); }
     72 #define LDAP_RES_LOCK_IF(nolock) \
     73 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); }
     74 #define LDAP_RES_UNLOCK_IF(nolock) \
     75 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); }
     76 #else
     77 #define LDAP_CONN_LOCK_IF(nolock)
     78 #define LDAP_CONN_UNLOCK_IF(nolock)
     79 #define LDAP_REQ_LOCK_IF(nolock)
     80 #define LDAP_REQ_UNLOCK_IF(nolock)
     81 #define LDAP_RES_LOCK_IF(nolock)
     82 #define LDAP_RES_UNLOCK_IF(nolock)
     83 #endif
     84 
     85 static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
     86 static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
     87 static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr ));
     88 
     89 static BerElement *
     90 re_encode_request( LDAP *ld,
     91 	BerElement *origber,
     92 	ber_int_t msgid,
     93 	int sref,
     94 	LDAPURLDesc *srv,
     95 	int *type );
     96 
     97 BerElement *
     98 ldap_alloc_ber_with_options( LDAP *ld )
     99 {
    100 	BerElement	*ber;
    101 
    102 	ber = ber_alloc_t( ld->ld_lberoptions );
    103 	if ( ber == NULL ) {
    104 		ld->ld_errno = LDAP_NO_MEMORY;
    105 	}
    106 
    107 	return( ber );
    108 }
    109 
    110 
    111 void
    112 ldap_set_ber_options( LDAP *ld, BerElement *ber )
    113 {
    114 	/* ld_lberoptions is constant, hence no lock */
    115 	ber->ber_options = ld->ld_lberoptions;
    116 }
    117 
    118 
    119 /* sets needed mutexes - no mutexes set to this point */
    120 ber_int_t
    121 ldap_send_initial_request(
    122 	LDAP *ld,
    123 	ber_tag_t msgtype,
    124 	const char *dn,
    125 	BerElement *ber,
    126 	ber_int_t msgid)
    127 {
    128 	int rc = 1;
    129 	ber_socket_t sd = AC_SOCKET_INVALID;
    130 
    131 	Debug0( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n" );
    132 
    133 	LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
    134 	if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) {
    135 		/* not connected yet */
    136 		rc = ldap_open_defconn( ld );
    137 		if ( rc == 0 ) {
    138 			ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
    139 				LBER_SB_OPT_GET_FD, &sd );
    140 		}
    141 	}
    142 	if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING )
    143 		rc = ldap_int_check_async_open( ld, sd );
    144 	if( rc < 0 ) {
    145 		ber_free( ber, 1 );
    146 		LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    147 		return( -1 );
    148 	} else if ( rc == 0 ) {
    149 		Debug0( LDAP_DEBUG_TRACE,
    150 			"ldap_open_defconn: successful\n" );
    151 	}
    152 
    153 #ifdef LDAP_CONNECTIONLESS
    154 	if (LDAP_IS_UDP(ld)) {
    155 		if (msgtype == LDAP_REQ_BIND) {
    156 			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
    157 			if (ld->ld_options.ldo_cldapdn)
    158 				ldap_memfree(ld->ld_options.ldo_cldapdn);
    159 			ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
    160 			ber_free( ber, 1 );
    161 			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
    162 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    163 			return 0;
    164 		}
    165 		if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
    166 		{
    167 			ber_free( ber, 1 );
    168 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    169 			return LDAP_PARAM_ERROR;
    170 		}
    171 	}
    172 #endif
    173 	LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
    174 	rc = ldap_send_server_request( ld, ber, msgid, NULL,
    175 		NULL, NULL, NULL, 0, 0 );
    176 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
    177 	LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    178 	return(rc);
    179 }
    180 
    181 
    182 /* protected by conn_mutex */
    183 int
    184 ldap_int_flush_request(
    185 	LDAP *ld,
    186 	LDAPRequest *lr )
    187 {
    188 	LDAPConn *lc = lr->lr_conn;
    189 
    190 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
    191 	if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
    192 		if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) {
    193 			/* ENOTCONN is returned in Solaris 10 */
    194 			/* need to continue write later */
    195 			lr->lr_status = LDAP_REQST_WRITING;
    196 			ldap_mark_select_write( ld, lc->lconn_sb );
    197 			ld->ld_errno = LDAP_BUSY;
    198 			return -2;
    199 		} else {
    200 			ld->ld_errno = LDAP_SERVER_DOWN;
    201 			ldap_free_request( ld, lr );
    202 			ldap_free_connection( ld, lc, 0, 0 );
    203 			return( -1 );
    204 		}
    205 	} else {
    206 		if ( lr->lr_parent == NULL ) {
    207 			lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
    208 			lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
    209 		}
    210 		lr->lr_status = LDAP_REQST_INPROGRESS;
    211 
    212 		/* sent -- waiting for a response */
    213 		ldap_mark_select_read( ld, lc->lconn_sb );
    214 		ldap_clear_select_write( ld, lc->lconn_sb );
    215 	}
    216 	return 0;
    217 }
    218 
    219 /*
    220  * protected by req_mutex
    221  * if m_noconn then protect using conn_lock
    222  * else already protected with conn_lock
    223  * if m_res then also protected by res_mutex
    224  */
    225 
    226 int
    227 ldap_send_server_request(
    228 	LDAP *ld,
    229 	BerElement *ber,
    230 	ber_int_t msgid,
    231 	LDAPRequest *parentreq,
    232 	LDAPURLDesc **srvlist,
    233 	LDAPConn *lc,
    234 	LDAPreqinfo *bind,
    235 	int m_noconn,
    236 	int m_res )
    237 {
    238 	LDAPRequest	*lr;
    239 	int		incparent, rc;
    240 
    241 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
    242 	Debug0( LDAP_DEBUG_TRACE, "ldap_send_server_request\n" );
    243 
    244 	incparent = 0;
    245 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
    246 
    247 	LDAP_CONN_LOCK_IF(m_noconn);
    248 	if ( lc == NULL ) {
    249 		if ( srvlist == NULL ) {
    250 			lc = ld->ld_defconn;
    251 		} else {
    252 			lc = find_connection( ld, *srvlist, 1 );
    253 			if ( lc == NULL ) {
    254 				if ( (bind != NULL) && (parentreq != NULL) ) {
    255 					/* Remember the bind in the parent */
    256 					incparent = 1;
    257 					++parentreq->lr_outrefcnt;
    258 				}
    259 				lc = ldap_new_connection( ld, srvlist, 0,
    260 					1, bind, 1, m_res );
    261 			}
    262 		}
    263 	}
    264 
    265 	/* async connect... */
    266 	if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
    267 		ber_socket_t	sd = AC_SOCKET_ERROR;
    268 		struct timeval	tv = { 0 };
    269 
    270 		ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
    271 
    272 		/* poll ... */
    273 		switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) {
    274 		case 0:
    275 			/* go on! */
    276 			lc->lconn_status = LDAP_CONNST_CONNECTED;
    277 			break;
    278 
    279 		case -2:
    280 			/* async only occurs if a network timeout is set */
    281 
    282 			/* honor network timeout */
    283 			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
    284 			if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
    285 			{
    286 				/* caller will have to call again */
    287 				ld->ld_errno = LDAP_X_CONNECTING;
    288 			}
    289 			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
    290 			/* fallthru */
    291 
    292 		default:
    293 			/* error */
    294 			break;
    295 		}
    296 	}
    297 
    298 	if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
    299 		if ( ld->ld_errno == LDAP_SUCCESS ) {
    300 			ld->ld_errno = LDAP_SERVER_DOWN;
    301 		}
    302 
    303 		ber_free( ber, 1 );
    304 		if ( incparent ) {
    305 			/* Forget about the bind */
    306 			--parentreq->lr_outrefcnt;
    307 		}
    308 		LDAP_CONN_UNLOCK_IF(m_noconn);
    309 		return( -1 );
    310 	}
    311 
    312 	use_connection( ld, lc );
    313 
    314 #ifdef LDAP_CONNECTIONLESS
    315 	if ( LDAP_IS_UDP( ld )) {
    316 		BerElement tmpber = *ber;
    317 		ber_rewind( &tmpber );
    318 		LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
    319 		rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
    320 			sizeof( struct sockaddr_storage ), 0 );
    321 		LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
    322 		if ( rc == -1 ) {
    323 			ld->ld_errno = LDAP_ENCODING_ERROR;
    324 			ber_free( ber, 1 );
    325 			LDAP_CONN_UNLOCK_IF(m_noconn);
    326 			return rc;
    327 		}
    328 	}
    329 #endif
    330 
    331 	/* If we still have an incomplete write, try to finish it before
    332 	 * dealing with the new request. If we don't finish here, return
    333 	 * LDAP_BUSY and let the caller retry later. We only allow a single
    334 	 * request to be in WRITING state.
    335 	 */
    336 	rc = 0;
    337 	if ( ld->ld_requests != NULL ) {
    338 		TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT );
    339 		LDAPRequest *lr;
    340 
    341 		assert( node != NULL );
    342 		lr = node->avl_data;
    343 		if ( lr->lr_status == LDAP_REQST_WRITING &&
    344 				ldap_int_flush_request( ld, lr ) < 0 ) {
    345 			rc = -1;
    346 		}
    347 	}
    348 	if ( rc ) {
    349 		ber_free( ber, 1 );
    350 		LDAP_CONN_UNLOCK_IF(m_noconn);
    351 		return rc;
    352 	}
    353 
    354 	lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
    355 	if ( lr == NULL ) {
    356 		ld->ld_errno = LDAP_NO_MEMORY;
    357 		ldap_free_connection( ld, lc, 0, 0 );
    358 		ber_free( ber, 1 );
    359 		if ( incparent ) {
    360 			/* Forget about the bind */
    361 			--parentreq->lr_outrefcnt;
    362 		}
    363 		LDAP_CONN_UNLOCK_IF(m_noconn);
    364 		return( -1 );
    365 	}
    366 	lr->lr_msgid = msgid;
    367 	lr->lr_status = LDAP_REQST_INPROGRESS;
    368 	lr->lr_res_errno = LDAP_SUCCESS;	/* optimistic */
    369 	lr->lr_ber = ber;
    370 	lr->lr_conn = lc;
    371 	if ( parentreq != NULL ) {	/* sub-request */
    372 		if ( !incparent ) {
    373 			/* Increment if we didn't do it before the bind */
    374 			++parentreq->lr_outrefcnt;
    375 		}
    376 		lr->lr_origid = parentreq->lr_origid;
    377 		lr->lr_parentcnt = ++parentreq->lr_parentcnt;
    378 		lr->lr_parent = parentreq;
    379 		lr->lr_refnext = parentreq->lr_child;
    380 		parentreq->lr_child = lr;
    381 	} else {			/* original request */
    382 		lr->lr_origid = lr->lr_msgid;
    383 	}
    384 
    385 	/* Extract requestDN for future reference */
    386 #ifdef LDAP_CONNECTIONLESS
    387 	if ( !LDAP_IS_UDP(ld) )
    388 #endif
    389 	{
    390 		BerElement tmpber = *ber;
    391 		ber_int_t	bint;
    392 		ber_tag_t	tag, rtag;
    393 
    394 		ber_reset( &tmpber, 1 );
    395 		rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
    396 		switch ( tag ) {
    397 		case LDAP_REQ_BIND:
    398 			rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
    399 			break;
    400 		case LDAP_REQ_DELETE:
    401 			break;
    402 		default:
    403 			rtag = ber_scanf( &tmpber, "{" /*}*/ );
    404 		case LDAP_REQ_ABANDON:
    405 			break;
    406 		}
    407 		if ( tag != LDAP_REQ_ABANDON ) {
    408 			ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
    409 			lr->lr_dn.bv_val = tmpber.ber_ptr;
    410 		}
    411 	}
    412 
    413 	rc = ldap_tavl_insert( &ld->ld_requests, lr, ldap_req_cmp, ldap_avl_dup_error );
    414 	assert( rc == LDAP_SUCCESS );
    415 
    416 	ld->ld_errno = LDAP_SUCCESS;
    417 	if ( ldap_int_flush_request( ld, lr ) == -1 ) {
    418 		msgid = -1;
    419 	}
    420 
    421 	LDAP_CONN_UNLOCK_IF(m_noconn);
    422 	return( msgid );
    423 }
    424 
    425 /* return 0 if no StartTLS ext, 1 if present, 2 if critical */
    426 static int
    427 find_tls_ext( LDAPURLDesc *srv )
    428 {
    429 	int i, crit;
    430 	char *ext;
    431 
    432 	if ( !srv->lud_exts )
    433 		return 0;
    434 
    435 	for (i=0; srv->lud_exts[i]; i++) {
    436 		crit = 0;
    437 		ext = srv->lud_exts[i];
    438 		if ( ext[0] == '!') {
    439 			ext++;
    440 			crit = 1;
    441 		}
    442 		if ( !strcasecmp( ext, "StartTLS" ) ||
    443 			!strcasecmp( ext, "X-StartTLS" ) ||
    444 			!strcmp( ext, LDAP_EXOP_START_TLS )) {
    445 			return crit + 1;
    446 		}
    447 	}
    448 	return 0;
    449 }
    450 
    451 /*
    452  * always protected by conn_mutex
    453  * optionally protected by req_mutex and res_mutex
    454  */
    455 LDAPConn *
    456 ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
    457 	int connect, LDAPreqinfo *bind, int m_req, int m_res )
    458 {
    459 	LDAPConn	*lc;
    460 	int		async = 0;
    461 
    462 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
    463 	Debug3( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
    464 		use_ldsb, connect, (bind != NULL) );
    465 	/*
    466 	 * make a new LDAP server connection
    467 	 * XXX open connection synchronously for now
    468 	 */
    469 	lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) );
    470 	if ( lc == NULL ) {
    471 		ld->ld_errno = LDAP_NO_MEMORY;
    472 		return( NULL );
    473 	}
    474 
    475 	if ( use_ldsb ) {
    476 		assert( ld->ld_sb != NULL );
    477 		lc->lconn_sb = ld->ld_sb;
    478 
    479 	} else {
    480 		lc->lconn_sb = ber_sockbuf_alloc();
    481 		if ( lc->lconn_sb == NULL ) {
    482 			LDAP_FREE( (char *)lc );
    483 			ld->ld_errno = LDAP_NO_MEMORY;
    484 			return( NULL );
    485 		}
    486 	}
    487 
    488 	if ( connect ) {
    489 		LDAPURLDesc	**srvp, *srv = NULL;
    490 
    491 		async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
    492 
    493 		for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
    494 			int		rc;
    495 
    496 			rc = ldap_int_open_connection( ld, lc, *srvp, async );
    497 			if ( rc != -1 ) {
    498 				srv = *srvp;
    499 
    500 				/* If we fully connected, async is moot */
    501 				if ( rc == 0 )
    502 					async = 0;
    503 
    504 				if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) {
    505 					ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params );
    506 				}
    507 
    508 				break;
    509 			}
    510 		}
    511 
    512 		if ( srv == NULL ) {
    513 			if ( !use_ldsb ) {
    514 				ber_sockbuf_free( lc->lconn_sb );
    515 			}
    516 			LDAP_FREE( (char *)lc );
    517 			ld->ld_errno = LDAP_SERVER_DOWN;
    518 			return( NULL );
    519 		}
    520 
    521 		lc->lconn_server = ldap_url_dup( srv );
    522 		if ( !lc->lconn_server ) {
    523 			if ( !use_ldsb )
    524 				ber_sockbuf_free( lc->lconn_sb );
    525 			LDAP_FREE( (char *)lc );
    526 			ld->ld_errno = LDAP_NO_MEMORY;
    527 			return( NULL );
    528 		}
    529 	}
    530 
    531 	lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
    532 	lc->lconn_next = ld->ld_conns;
    533 	ld->ld_conns = lc;
    534 
    535 	if ( connect ) {
    536 #ifdef HAVE_TLS
    537 		if ( lc->lconn_server->lud_exts ) {
    538 			int rc, ext = find_tls_ext( lc->lconn_server );
    539 			if ( ext ) {
    540 				LDAPConn	*savedefconn;
    541 
    542 				savedefconn = ld->ld_defconn;
    543 				++lc->lconn_refcnt;	/* avoid premature free */
    544 				ld->ld_defconn = lc;
    545 
    546 				LDAP_REQ_UNLOCK_IF(m_req);
    547 				LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    548 				LDAP_RES_UNLOCK_IF(m_res);
    549 				rc = ldap_start_tls_s( ld, NULL, NULL );
    550 				LDAP_RES_LOCK_IF(m_res);
    551 				LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
    552 				LDAP_REQ_LOCK_IF(m_req);
    553 				ld->ld_defconn = savedefconn;
    554 				--lc->lconn_refcnt;
    555 
    556 				if ( rc != LDAP_SUCCESS && ext == 2 ) {
    557 					ldap_free_connection( ld, lc, 1, 0 );
    558 					return NULL;
    559 				}
    560 			}
    561 		}
    562 #endif
    563 	}
    564 
    565 	if ( bind != NULL ) {
    566 		int		err = 0;
    567 		LDAPConn	*savedefconn;
    568 
    569 		/* Set flag to prevent additional referrals
    570 		 * from being processed on this
    571 		 * connection until the bind has completed
    572 		 */
    573 		lc->lconn_rebind_inprogress = 1;
    574 		/* V3 rebind function */
    575 		if ( ld->ld_rebind_proc != NULL) {
    576 			LDAPURLDesc	*srvfunc;
    577 
    578 			srvfunc = ldap_url_dup( *srvlist );
    579 			if ( srvfunc == NULL ) {
    580 				ld->ld_errno = LDAP_NO_MEMORY;
    581 				err = -1;
    582 			} else {
    583 				savedefconn = ld->ld_defconn;
    584 				++lc->lconn_refcnt;	/* avoid premature free */
    585 				ld->ld_defconn = lc;
    586 
    587 				Debug0( LDAP_DEBUG_TRACE, "Call application rebind_proc\n" );
    588 				LDAP_REQ_UNLOCK_IF(m_req);
    589 				LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    590 				LDAP_RES_UNLOCK_IF(m_res);
    591 				err = (*ld->ld_rebind_proc)( ld,
    592 					bind->ri_url, bind->ri_request, bind->ri_msgid,
    593 					ld->ld_rebind_params );
    594 				LDAP_RES_LOCK_IF(m_res);
    595 				LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
    596 				LDAP_REQ_LOCK_IF(m_req);
    597 
    598 				ld->ld_defconn = savedefconn;
    599 				--lc->lconn_refcnt;
    600 
    601 				if ( err != 0 ) {
    602 					err = -1;
    603 					ldap_free_connection( ld, lc, 1, 0 );
    604 					lc = NULL;
    605 				}
    606 				ldap_free_urldesc( srvfunc );
    607 			}
    608 
    609 		} else {
    610 			int		msgid, rc;
    611 			struct berval	passwd = BER_BVNULL;
    612 
    613 			savedefconn = ld->ld_defconn;
    614 			++lc->lconn_refcnt;	/* avoid premature free */
    615 			ld->ld_defconn = lc;
    616 
    617 			Debug0( LDAP_DEBUG_TRACE,
    618 				"anonymous rebind via ldap_sasl_bind(\"\")\n" );
    619 
    620 			LDAP_REQ_UNLOCK_IF(m_req);
    621 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    622 			LDAP_RES_UNLOCK_IF(m_res);
    623 			rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
    624 				NULL, NULL, &msgid );
    625 			if ( rc != LDAP_SUCCESS ) {
    626 				err = -1;
    627 
    628 			} else {
    629 				for ( err = 1; err > 0; ) {
    630 					struct timeval	tv = { 0, 100000 };
    631 					LDAPMessage	*res = NULL;
    632 
    633 					switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
    634 					case -1:
    635 						err = -1;
    636 						break;
    637 
    638 					case 0:
    639 #ifdef LDAP_R_COMPILE
    640 						ldap_pvt_thread_yield();
    641 #endif
    642 						break;
    643 
    644 					case LDAP_RES_BIND:
    645 						rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
    646 						if ( rc != LDAP_SUCCESS ) {
    647 							err = -1;
    648 
    649 						} else if ( err != LDAP_SUCCESS ) {
    650 							err = -1;
    651 						}
    652 						/* else err == LDAP_SUCCESS == 0 */
    653 						break;
    654 
    655 					default:
    656 						Debug3( LDAP_DEBUG_TRACE,
    657 							"ldap_new_connection %p: "
    658 							"unexpected response %d "
    659 							"from BIND request id=%d\n",
    660 							(void *) ld, ldap_msgtype( res ), msgid );
    661 						err = -1;
    662 						break;
    663 					}
    664 				}
    665 			}
    666 			LDAP_RES_LOCK_IF(m_res);
    667 			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
    668 			LDAP_REQ_LOCK_IF(m_req);
    669 			ld->ld_defconn = savedefconn;
    670 			--lc->lconn_refcnt;
    671 
    672 			if ( err != 0 ) {
    673 				ldap_free_connection( ld, lc, 1, 0 );
    674 				lc = NULL;
    675 			}
    676 		}
    677 		if ( lc != NULL )
    678 			lc->lconn_rebind_inprogress = 0;
    679 	}
    680 	return( lc );
    681 }
    682 
    683 
    684 /* protected by ld_conn_mutex */
    685 static LDAPConn *
    686 find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
    687 /*
    688  * return an existing connection (if any) to the server srv
    689  * if "any" is non-zero, check for any server in the "srv" chain
    690  */
    691 {
    692 	LDAPConn	*lc;
    693 	LDAPURLDesc	*lcu, *lsu;
    694 	int lcu_port, lsu_port;
    695 	int found = 0;
    696 
    697 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
    698 	for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
    699 		lcu = lc->lconn_server;
    700 		lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
    701 			lcu->lud_port );
    702 
    703 		for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
    704 			lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
    705 				lsu->lud_port );
    706 
    707 			if ( lsu_port == lcu_port
    708 				&& strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
    709 				&& lcu->lud_host != NULL && lsu->lud_host != NULL
    710 				&& strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
    711 			{
    712 				found = 1;
    713 				break;
    714 			}
    715 
    716 			if ( !any ) break;
    717 		}
    718 		if ( found )
    719 			break;
    720 	}
    721 	return lc;
    722 }
    723 
    724 
    725 
    726 /* protected by ld_conn_mutex */
    727 static void
    728 use_connection( LDAP *ld, LDAPConn *lc )
    729 {
    730 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
    731 	++lc->lconn_refcnt;
    732 	lc->lconn_lastused = time( NULL );
    733 }
    734 
    735 
    736 /* protected by ld_conn_mutex */
    737 void
    738 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
    739 {
    740 	LDAPConn	*tmplc, *prevlc;
    741 
    742 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
    743 	Debug2( LDAP_DEBUG_TRACE,
    744 		"ldap_free_connection %d %d\n",
    745 		force, unbind );
    746 
    747 	if ( force || --lc->lconn_refcnt <= 0 ) {
    748 		/* remove from connections list first */
    749 
    750 		for ( prevlc = NULL, tmplc = ld->ld_conns;
    751 			tmplc != NULL;
    752 			tmplc = tmplc->lconn_next )
    753 		{
    754 			if ( tmplc == lc ) {
    755 				if ( prevlc == NULL ) {
    756 				    ld->ld_conns = tmplc->lconn_next;
    757 				} else {
    758 				    prevlc->lconn_next = tmplc->lconn_next;
    759 				}
    760 				if ( ld->ld_defconn == lc ) {
    761 					ld->ld_defconn = NULL;
    762 				}
    763 				break;
    764 			}
    765 			prevlc = tmplc;
    766 		}
    767 
    768 		/* process connection callbacks */
    769 		{
    770 			struct ldapoptions *lo;
    771 			ldaplist *ll;
    772 			ldap_conncb *cb;
    773 
    774 			lo = &ld->ld_options;
    775 			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
    776 			if ( lo->ldo_conn_cbs ) {
    777 				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
    778 					cb = ll->ll_data;
    779 					cb->lc_del( ld, lc->lconn_sb, cb );
    780 				}
    781 			}
    782 			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
    783 			lo = LDAP_INT_GLOBAL_OPT();
    784 			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
    785 			if ( lo->ldo_conn_cbs ) {
    786 				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
    787 					cb = ll->ll_data;
    788 					cb->lc_del( ld, lc->lconn_sb, cb );
    789 				}
    790 			}
    791 			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
    792 		}
    793 
    794 		if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
    795 			ldap_mark_select_clear( ld, lc->lconn_sb );
    796 			if ( unbind ) {
    797 				ldap_send_unbind( ld, lc->lconn_sb,
    798 						NULL, NULL );
    799 			}
    800 		}
    801 
    802 		if ( lc->lconn_ber != NULL ) {
    803 			ber_free( lc->lconn_ber, 1 );
    804 		}
    805 
    806 		ldap_int_sasl_close( ld, lc );
    807 #ifdef HAVE_GSSAPI
    808 		ldap_int_gssapi_close( ld, lc );
    809 #endif
    810 
    811 		ldap_free_urllist( lc->lconn_server );
    812 
    813 		/* FIXME: is this at all possible?
    814 		 * ldap_ld_free() in unbind.c calls ldap_free_connection()
    815 		 * with force == 1 __after__ explicitly calling
    816 		 * ldap_tavl_free on ld->ld_requests */
    817 		if ( force ) {
    818 			ldap_tavl_free( ld->ld_requests, ldap_do_free_request );
    819 			ld->ld_requests = NULL;
    820 		}
    821 
    822 		if ( lc->lconn_sb != ld->ld_sb ) {
    823 			ber_sockbuf_free( lc->lconn_sb );
    824 		} else {
    825 			ber_int_sb_close( lc->lconn_sb );
    826 		}
    827 
    828 		if ( lc->lconn_rebind_queue != NULL) {
    829 			int i;
    830 			for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
    831 				LDAP_VFREE( lc->lconn_rebind_queue[i] );
    832 			}
    833 			LDAP_FREE( lc->lconn_rebind_queue );
    834 		}
    835 
    836 		LDAP_FREE( lc );
    837 
    838 		Debug0( LDAP_DEBUG_TRACE,
    839 			"ldap_free_connection: actually freed\n" );
    840 
    841 	} else {
    842 		lc->lconn_lastused = time( NULL );
    843 		Debug1( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
    844 				lc->lconn_refcnt );
    845 	}
    846 }
    847 
    848 
    849 /* Protects self with ld_conn_mutex */
    850 #ifdef LDAP_DEBUG
    851 void
    852 ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
    853 {
    854 	LDAPConn	*lc;
    855    	char		timebuf[32];
    856 
    857 	Debug2( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "" );
    858 	LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
    859 	for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
    860 		if ( lc->lconn_server != NULL ) {
    861 			Debug3( LDAP_DEBUG_TRACE, "* host: %s  port: %d%s\n",
    862 				( lc->lconn_server->lud_host == NULL ) ? "(null)"
    863 				: lc->lconn_server->lud_host,
    864 				lc->lconn_server->lud_port, ( lc->lconn_sb ==
    865 				ld->ld_sb ) ? "  (default)" : "" );
    866 		}
    867 		if ( lc->lconn_sb != NULL ) {
    868 			char 			from[LDAP_IPADDRLEN];
    869 			struct berval 	frombv = BER_BVC(from);
    870 			ber_socket_t 	sb;
    871 			if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sb ) == 1 ) {
    872 				Sockaddr sin;
    873 				socklen_t len = sizeof( sin );
    874 				if ( getsockname( sb, (struct sockaddr *)&sin, &len ) == 0 ) {
    875 					ldap_pvt_sockaddrstr( &sin, &frombv );
    876 					Debug1( LDAP_DEBUG_TRACE, "* from: %s\n",
    877 						( from == NULL ) ? "(null)" : from );
    878 				}
    879 			}
    880 		}
    881 		Debug2( LDAP_DEBUG_TRACE, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
    882 			( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
    883 				? "NeedSocket" :
    884 				( lc->lconn_status == LDAP_CONNST_CONNECTING )
    885 					? "Connecting" : "Connected" );
    886 		Debug2( LDAP_DEBUG_TRACE, "  last used: %s%s\n",
    887 			ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
    888 			lc->lconn_rebind_inprogress ? "  rebind in progress" : "" );
    889 		if ( lc->lconn_rebind_inprogress ) {
    890 			if ( lc->lconn_rebind_queue != NULL) {
    891 				int	i;
    892 
    893 				for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
    894 					int	j;
    895 					for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) {
    896 						Debug3( LDAP_DEBUG_TRACE, "    queue %d entry %d - %s\n",
    897 							i, j, lc->lconn_rebind_queue[i][j] );
    898 					}
    899 				}
    900 			} else {
    901 				Debug0( LDAP_DEBUG_TRACE, "    queue is empty\n" );
    902 			}
    903 		}
    904 		Debug0( LDAP_DEBUG_TRACE, "\n" );
    905 		if ( !all ) {
    906 			break;
    907 		}
    908 	}
    909 	LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
    910 }
    911 
    912 
    913 /* protected by req_mutex and res_mutex */
    914 void
    915 ldap_dump_requests_and_responses( LDAP *ld )
    916 {
    917 	LDAPMessage	*lm, *l;
    918 	TAvlnode *node;
    919 	int		i;
    920 
    921 	Debug1( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n",
    922 		(void *)ld );
    923 	node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_LEFT );
    924 	if ( node == NULL ) {
    925 		Debug0( LDAP_DEBUG_TRACE, "   Empty\n" );
    926 	}
    927 	for ( i = 0 ; node != NULL; i++, node = ldap_tavl_next( node, TAVL_DIR_RIGHT ) ) {
    928 		LDAPRequest	*lr = node->avl_data;
    929 
    930 		Debug3( LDAP_DEBUG_TRACE, " * msgid %d,  origid %d, status %s\n",
    931 			lr->lr_msgid, lr->lr_origid,
    932 			( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
    933 			( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
    934 			( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
    935 			( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" :
    936 			( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted"
    937 				: "InvalidStatus" );
    938 		Debug2( LDAP_DEBUG_TRACE, "   outstanding referrals %d, parent count %d\n",
    939 			lr->lr_outrefcnt, lr->lr_parentcnt );
    940 	}
    941 	Debug3( LDAP_DEBUG_TRACE, "  ld %p request count %d (abandoned %lu)\n",
    942 		(void *)ld, i, ld->ld_nabandoned );
    943 	Debug1( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld );
    944 	if ( ( lm = ld->ld_responses ) == NULL ) {
    945 		Debug0( LDAP_DEBUG_TRACE, "   Empty\n" );
    946 	}
    947 	for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) {
    948 		Debug2( LDAP_DEBUG_TRACE, " * msgid %d,  type %lu\n",
    949 		    lm->lm_msgid, (unsigned long)lm->lm_msgtype );
    950 		if ( lm->lm_chain != NULL ) {
    951 			Debug0( LDAP_DEBUG_TRACE, "   chained responses:\n" );
    952 			for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) {
    953 				Debug2( LDAP_DEBUG_TRACE,
    954 					"  * msgid %d,  type %lu\n",
    955 					l->lm_msgid,
    956 					(unsigned long)l->lm_msgtype );
    957 			}
    958 		}
    959 	}
    960 	Debug2( LDAP_DEBUG_TRACE, "  ld %p response count %d\n", (void *)ld, i );
    961 }
    962 #endif /* LDAP_DEBUG */
    963 
    964 /* protected by req_mutex */
    965 void
    966 ldap_do_free_request( void *arg )
    967 {
    968 	LDAPRequest *lr = arg;
    969 
    970 	Debug3( LDAP_DEBUG_TRACE, "ldap_do_free_request: "
    971 			"asked to free lr %p msgid %d refcnt %d\n",
    972 			(void *) lr, lr->lr_msgid, lr->lr_refcnt );
    973 	/* if lr_refcnt > 0, the request has been looked up
    974 	 * by ldap_find_request_by_msgid(); if in the meanwhile
    975 	 * the request is free()'d by someone else, just decrease
    976 	 * the reference count; later on, it will be freed. */
    977 	if ( lr->lr_refcnt > 0 ) {
    978 		assert( lr->lr_refcnt == 1 );
    979 		lr->lr_refcnt = -lr->lr_refcnt;
    980 		return;
    981 	}
    982 
    983 	if ( lr->lr_ber != NULL ) {
    984 		ber_free( lr->lr_ber, 1 );
    985 		lr->lr_ber = NULL;
    986 	}
    987 
    988 	if ( lr->lr_res_error != NULL ) {
    989 		LDAP_FREE( lr->lr_res_error );
    990 		lr->lr_res_error = NULL;
    991 	}
    992 
    993 	if ( lr->lr_res_matched != NULL ) {
    994 		LDAP_FREE( lr->lr_res_matched );
    995 		lr->lr_res_matched = NULL;
    996 	}
    997 
    998 	LDAP_FREE( lr );
    999 }
   1000 
   1001 int
   1002 ldap_req_cmp( const void *l, const void *r )
   1003 {
   1004 	const LDAPRequest *left = l, *right = r;
   1005 	return left->lr_msgid - right->lr_msgid;
   1006 }
   1007 
   1008 /* protected by req_mutex */
   1009 static void
   1010 ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
   1011 {
   1012 	LDAPRequest *removed;
   1013 
   1014 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
   1015 	removed = ldap_tavl_delete( &ld->ld_requests, lr, ldap_req_cmp );
   1016 	assert( !removed || removed == lr );
   1017 	Debug3( LDAP_DEBUG_TRACE, "ldap_free_request_int: "
   1018 			"lr %p msgid %d%s removed\n",
   1019 			(void *) lr, lr->lr_msgid, removed ? "" : " not" );
   1020 
   1021 	ldap_do_free_request( lr );
   1022 }
   1023 
   1024 /* protected by req_mutex */
   1025 void
   1026 ldap_free_request( LDAP *ld, LDAPRequest *lr )
   1027 {
   1028 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
   1029 	Debug2( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
   1030 		lr->lr_origid, lr->lr_msgid );
   1031 
   1032 	/* free all referrals (child requests) */
   1033 	while ( lr->lr_child ) {
   1034 		ldap_free_request( ld, lr->lr_child );
   1035 	}
   1036 
   1037 	if ( lr->lr_parent != NULL ) {
   1038 		LDAPRequest     **lrp;
   1039 
   1040 		--lr->lr_parent->lr_outrefcnt;
   1041 		for ( lrp = &lr->lr_parent->lr_child;
   1042 			*lrp && *lrp != lr;
   1043 			lrp = &(*lrp)->lr_refnext );
   1044 
   1045 		if ( *lrp == lr ) {
   1046 			*lrp = lr->lr_refnext;
   1047 		}
   1048 	}
   1049 	ldap_free_request_int( ld, lr );
   1050 }
   1051 
   1052 /*
   1053  * call first time with *cntp = -1
   1054  * when returns *cntp == -1, no referrals are left
   1055  *
   1056  * NOTE: may replace *refsp, or shuffle the contents
   1057  * of the original array.
   1058  */
   1059 static int ldap_int_nextref(
   1060 	LDAP			*ld,
   1061 	char			***refsp,
   1062 	int			*cntp,
   1063 	void			*params )
   1064 {
   1065 	assert( refsp != NULL );
   1066 	assert( *refsp != NULL );
   1067 	assert( cntp != NULL );
   1068 
   1069 	if ( *cntp < -1 ) {
   1070 		*cntp = -1;
   1071 		return -1;
   1072 	}
   1073 
   1074 	(*cntp)++;
   1075 
   1076 	if ( (*refsp)[ *cntp ] == NULL ) {
   1077 		*cntp = -1;
   1078 	}
   1079 
   1080 	return 0;
   1081 }
   1082 
   1083 /*
   1084  * Chase v3 referrals
   1085  *
   1086  * Parameters:
   1087  *  (IN) ld = LDAP connection handle
   1088  *  (IN) lr = LDAP Request structure
   1089  *  (IN) refs = array of pointers to referral strings that we will chase
   1090  *              The array will be free'd by this function when no longer needed
   1091  *  (IN) sref != 0 if following search reference
   1092  *  (OUT) errstrp = Place to return a string of referrals which could not be followed
   1093  *  (OUT) hadrefp = 1 if successfully followed referral
   1094  *
   1095  * Return value - number of referrals followed
   1096  *
   1097  * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
   1098  */
   1099 int
   1100 ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
   1101 {
   1102 	char		*unfollowed;
   1103 	int		 unfollowedcnt = 0;
   1104 	LDAPRequest	*origreq;
   1105 	LDAPURLDesc	*srv = NULL;
   1106 	BerElement	*ber;
   1107 	char		**refarray = NULL;
   1108 	LDAPConn	*lc;
   1109 	int			 rc, count, i, j, id;
   1110 	LDAPreqinfo  rinfo;
   1111 	LDAP_NEXTREF_PROC	*nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;
   1112 
   1113 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
   1114 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
   1115 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
   1116 	Debug0( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n" );
   1117 
   1118 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
   1119 	*hadrefp = 0;
   1120 
   1121 	unfollowed = NULL;
   1122 	rc = count = 0;
   1123 
   1124 	/* If no referrals in array, return */
   1125 	if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
   1126 		rc = 0;
   1127 		goto done;
   1128 	}
   1129 
   1130 	/* Check for hop limit exceeded */
   1131 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
   1132 		Debug1( LDAP_DEBUG_ANY,
   1133 		    "more than %d referral hops (dropping)\n", ld->ld_refhoplimit );
   1134 		ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
   1135 		rc = -1;
   1136 		goto done;
   1137 	}
   1138 
   1139 	/* find original request */
   1140 	for ( origreq = lr;
   1141 		origreq->lr_parent != NULL;
   1142 		origreq = origreq->lr_parent )
   1143 	{
   1144 		/* empty */ ;
   1145 	}
   1146 
   1147 	refarray = refs;
   1148 	refs = NULL;
   1149 
   1150 	/* parse out & follow referrals */
   1151 	/* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
   1152 	i = -1;
   1153 	for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
   1154 			i != -1;
   1155 			nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
   1156 	{
   1157 
   1158 		/* Parse the referral URL */
   1159 		rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
   1160 		if ( rc != LDAP_URL_SUCCESS ) {
   1161 			/* ldap_url_parse_ext() returns LDAP_URL_* errors
   1162 			 * which do not map on API errors */
   1163 			ld->ld_errno = LDAP_PARAM_ERROR;
   1164 			rc = -1;
   1165 			goto done;
   1166 		}
   1167 
   1168 		if( srv->lud_crit_exts ) {
   1169 			int ok = 0;
   1170 #ifdef HAVE_TLS
   1171 			/* If StartTLS is the only critical ext, OK. */
   1172 			if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
   1173 				ok = 1;
   1174 #endif
   1175 			if ( !ok ) {
   1176 				/* we do not support any other extensions */
   1177 				ld->ld_errno = LDAP_NOT_SUPPORTED;
   1178 				rc = -1;
   1179 				goto done;
   1180 			}
   1181 		}
   1182 
   1183 		/* check connection for re-bind in progress */
   1184 		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
   1185 			/* See if we've already requested this DN with this conn */
   1186 			LDAPRequest *lp;
   1187 			int looped = 0;
   1188 			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
   1189 			for ( lp = origreq; lp; ) {
   1190 				if ( lp->lr_conn == lc
   1191 					&& len == lp->lr_dn.bv_len
   1192 					&& len
   1193 					&& strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
   1194 				{
   1195 					looped = 1;
   1196 					break;
   1197 				}
   1198 				if ( lp == origreq ) {
   1199 					lp = lp->lr_child;
   1200 				} else {
   1201 					lp = lp->lr_refnext;
   1202 				}
   1203 			}
   1204 			if ( looped ) {
   1205 				ldap_free_urllist( srv );
   1206 				srv = NULL;
   1207 				ld->ld_errno = LDAP_CLIENT_LOOP;
   1208 				rc = -1;
   1209 				continue;
   1210 			}
   1211 
   1212 			if ( lc->lconn_rebind_inprogress ) {
   1213 				/* We are already chasing a referral or search reference and a
   1214 				 * bind on that connection is in progress.  We must queue
   1215 				 * referrals on that connection, so we don't get a request
   1216 				 * going out before the bind operation completes. This happens
   1217 				 * if two search references come in one behind the other
   1218 				 * for the same server with different contexts.
   1219 				 */
   1220 				Debug1( LDAP_DEBUG_TRACE,
   1221 					"ldap_chase_v3referrals: queue referral \"%s\"\n",
   1222 					refarray[i] );
   1223 				if( lc->lconn_rebind_queue == NULL ) {
   1224 					/* Create a referral list */
   1225 					lc->lconn_rebind_queue =
   1226 						(char ***) LDAP_MALLOC( sizeof(void *) * 2);
   1227 
   1228 					if( lc->lconn_rebind_queue == NULL) {
   1229 						ld->ld_errno = LDAP_NO_MEMORY;
   1230 						rc = -1;
   1231 						goto done;
   1232 					}
   1233 
   1234 					lc->lconn_rebind_queue[0] = refarray;
   1235 					lc->lconn_rebind_queue[1] = NULL;
   1236 					refarray = NULL;
   1237 
   1238 				} else {
   1239 					/* Count how many referral arrays we already have */
   1240 					for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
   1241 						/* empty */;
   1242 					}
   1243 
   1244 					/* Add the new referral to the list */
   1245 					lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
   1246 						lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
   1247 
   1248 					if( lc->lconn_rebind_queue == NULL ) {
   1249 						ld->ld_errno = LDAP_NO_MEMORY;
   1250 						rc = -1;
   1251 						goto done;
   1252 					}
   1253 					lc->lconn_rebind_queue[j] = refarray;
   1254 					lc->lconn_rebind_queue[j+1] = NULL;
   1255 					refarray = NULL;
   1256 				}
   1257 
   1258 				/* We have queued the referral/reference, now just return */
   1259 				rc = 0;
   1260 				*hadrefp = 1;
   1261 				count = 1; /* Pretend we already followed referral */
   1262 				goto done;
   1263 			}
   1264 		}
   1265 		/* Re-encode the request with the new starting point of the search.
   1266 		 * Note: In the future we also need to replace the filter if one
   1267 		 * was provided with the search reference
   1268 		 */
   1269 
   1270 		/* For references we don't want old dn if new dn empty */
   1271 		if ( sref && srv->lud_dn == NULL ) {
   1272 			srv->lud_dn = LDAP_STRDUP( "" );
   1273 		}
   1274 
   1275 		LDAP_NEXT_MSGID( ld, id );
   1276 		ber = re_encode_request( ld, origreq->lr_ber, id,
   1277 			sref, srv, &rinfo.ri_request );
   1278 
   1279 		if( ber == NULL ) {
   1280 			ld->ld_errno = LDAP_ENCODING_ERROR;
   1281 			rc = -1;
   1282 			goto done;
   1283 		}
   1284 
   1285 		Debug2( LDAP_DEBUG_TRACE,
   1286 			"ldap_chase_v3referral: msgid %d, url \"%s\"\n",
   1287 			lr->lr_msgid, refarray[i] );
   1288 
   1289 		/* Send the new request to the server - may require a bind */
   1290 		rinfo.ri_msgid = origreq->lr_origid;
   1291 		rinfo.ri_url = refarray[i];
   1292 		rc = ldap_send_server_request( ld, ber, id,
   1293 			origreq, &srv, NULL, &rinfo, 0, 1 );
   1294 		if ( rc < 0 ) {
   1295 			/* Failure, try next referral in the list */
   1296 			Debug3( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n",
   1297 				refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
   1298 			unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
   1299 			ldap_free_urllist( srv );
   1300 			srv = NULL;
   1301 			ld->ld_errno = LDAP_REFERRAL;
   1302 		} else {
   1303 			/* Success, no need to try this referral list further */
   1304 			rc = 0;
   1305 			++count;
   1306 			*hadrefp = 1;
   1307 
   1308 			/* check if there is a queue of referrals that came in during bind */
   1309 			if ( lc == NULL) {
   1310 				lc = find_connection( ld, srv, 1 );
   1311 				if ( lc == NULL ) {
   1312 					ld->ld_errno = LDAP_OPERATIONS_ERROR;
   1313 					rc = -1;
   1314 					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
   1315 					goto done;
   1316 				}
   1317 			}
   1318 
   1319 			if ( lc->lconn_rebind_queue != NULL ) {
   1320 				/* Release resources of previous list */
   1321 				LDAP_VFREE( refarray );
   1322 				refarray = NULL;
   1323 				ldap_free_urllist( srv );
   1324 				srv = NULL;
   1325 
   1326 				/* Pull entries off end of queue so list always null terminated */
   1327 				for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
   1328 					;
   1329 				refarray = lc->lconn_rebind_queue[j - 1];
   1330 				lc->lconn_rebind_queue[j-1] = NULL;
   1331 				/* we pulled off last entry from queue, free queue */
   1332 				if ( j == 1 ) {
   1333 					LDAP_FREE( lc->lconn_rebind_queue );
   1334 					lc->lconn_rebind_queue = NULL;
   1335 				}
   1336 				/* restart the loop the with new referral list */
   1337 				i = -1;
   1338 				continue;
   1339 			}
   1340 			break; /* referral followed, break out of for loop */
   1341 		}
   1342 	} /* end for loop */
   1343 done:
   1344 	LDAP_VFREE( refarray );
   1345 	ldap_free_urllist( srv );
   1346 	LDAP_FREE( *errstrp );
   1347 
   1348 	if( rc == 0 ) {
   1349 		*errstrp = NULL;
   1350 		LDAP_FREE( unfollowed );
   1351 		return count;
   1352 	} else {
   1353 		*errstrp = unfollowed;
   1354 		return rc;
   1355 	}
   1356 }
   1357 
   1358 /*
   1359  * XXX merging of errors in this routine needs to be improved
   1360  * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
   1361  */
   1362 int
   1363 ldap_chase_referrals( LDAP *ld,
   1364 	LDAPRequest *lr,
   1365 	char **errstrp,
   1366 	int sref,
   1367 	int *hadrefp )
   1368 {
   1369 	int		rc, count, id;
   1370 	unsigned	len;
   1371 	char		*p, *ref, *unfollowed;
   1372 	LDAPRequest	*origreq;
   1373 	LDAPURLDesc	*srv;
   1374 	BerElement	*ber;
   1375 	LDAPreqinfo  rinfo;
   1376 	LDAPConn	*lc;
   1377 
   1378 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
   1379 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
   1380 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
   1381 	Debug0( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n" );
   1382 
   1383 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
   1384 	*hadrefp = 0;
   1385 
   1386 	if ( *errstrp == NULL ) {
   1387 		return( 0 );
   1388 	}
   1389 
   1390 	len = strlen( *errstrp );
   1391 	for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
   1392 		if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
   1393 			*p = '\0';
   1394 			p += LDAP_REF_STR_LEN;
   1395 			break;
   1396 		}
   1397 	}
   1398 
   1399 	if ( len < LDAP_REF_STR_LEN ) {
   1400 		return( 0 );
   1401 	}
   1402 
   1403 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
   1404 		Debug1( LDAP_DEBUG_ANY,
   1405 		    "more than %d referral hops (dropping)\n",
   1406 		    ld->ld_refhoplimit );
   1407 		    /* XXX report as error in ld->ld_errno? */
   1408 		    return( 0 );
   1409 	}
   1410 
   1411 	/* find original request */
   1412 	for ( origreq = lr; origreq->lr_parent != NULL;
   1413 	     origreq = origreq->lr_parent ) {
   1414 		/* empty */;
   1415 	}
   1416 
   1417 	unfollowed = NULL;
   1418 	rc = count = 0;
   1419 
   1420 	/* parse out & follow referrals */
   1421 	for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
   1422 		p = strchr( ref, '\n' );
   1423 		if ( p != NULL ) {
   1424 			*p++ = '\0';
   1425 		}
   1426 
   1427 		rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
   1428 		if ( rc != LDAP_URL_SUCCESS ) {
   1429 			Debug2( LDAP_DEBUG_TRACE,
   1430 				"ignoring %s referral <%s>\n",
   1431 				ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect" );
   1432 			rc = ldap_append_referral( ld, &unfollowed, ref );
   1433 			*hadrefp = 1;
   1434 			continue;
   1435 		}
   1436 
   1437 		Debug1( LDAP_DEBUG_TRACE,
   1438 		    "chasing LDAP referral: <%s>\n", ref );
   1439 
   1440 		*hadrefp = 1;
   1441 
   1442 		/* See if we've already been here */
   1443 		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
   1444 			LDAPRequest *lp;
   1445 			int looped = 0;
   1446 			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
   1447 			for ( lp = lr; lp; lp = lp->lr_parent ) {
   1448 				if ( lp->lr_conn == lc
   1449 					&& len == lp->lr_dn.bv_len )
   1450 				{
   1451 					if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
   1452 							continue;
   1453 					looped = 1;
   1454 					break;
   1455 				}
   1456 			}
   1457 			if ( looped ) {
   1458 				ldap_free_urllist( srv );
   1459 				ld->ld_errno = LDAP_CLIENT_LOOP;
   1460 				rc = -1;
   1461 				continue;
   1462 			}
   1463 		}
   1464 
   1465 		LDAP_NEXT_MSGID( ld, id );
   1466 		ber = re_encode_request( ld, origreq->lr_ber,
   1467 		    id, sref, srv, &rinfo.ri_request );
   1468 
   1469 		if ( ber == NULL ) {
   1470 			ldap_free_urllist( srv );
   1471 			return -1 ;
   1472 		}
   1473 
   1474 		/* copy the complete referral for rebind process */
   1475 		rinfo.ri_url = LDAP_STRDUP( ref );
   1476 
   1477 		rinfo.ri_msgid = origreq->lr_origid;
   1478 
   1479 		rc = ldap_send_server_request( ld, ber, id,
   1480 			lr, &srv, NULL, &rinfo, 0, 1 );
   1481 		LDAP_FREE( rinfo.ri_url );
   1482 
   1483 		if( rc >= 0 ) {
   1484 			++count;
   1485 		} else {
   1486 			Debug3( LDAP_DEBUG_ANY,
   1487 				"Unable to chase referral \"%s\" (%d: %s)\n",
   1488 				ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
   1489 			rc = ldap_append_referral( ld, &unfollowed, ref );
   1490 		}
   1491 
   1492 		ldap_free_urllist(srv);
   1493 	}
   1494 
   1495 	LDAP_FREE( *errstrp );
   1496 	*errstrp = unfollowed;
   1497 
   1498 	return(( rc == 0 ) ? count : rc );
   1499 }
   1500 
   1501 
   1502 int
   1503 ldap_append_referral( LDAP *ld, char **referralsp, char *s )
   1504 {
   1505 	int	first;
   1506 
   1507 	if ( *referralsp == NULL ) {
   1508 		first = 1;
   1509 		*referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
   1510 		    + 1 );
   1511 	} else {
   1512 		first = 0;
   1513 		*referralsp = (char *)LDAP_REALLOC( *referralsp,
   1514 		    strlen( *referralsp ) + strlen( s ) + 2 );
   1515 	}
   1516 
   1517 	if ( *referralsp == NULL ) {
   1518 		ld->ld_errno = LDAP_NO_MEMORY;
   1519 		return( -1 );
   1520 	}
   1521 
   1522 	if ( first ) {
   1523 		strcpy( *referralsp, LDAP_REF_STR );
   1524 	} else {
   1525 		strcat( *referralsp, "\n" );
   1526 	}
   1527 	strcat( *referralsp, s );
   1528 
   1529 	return( 0 );
   1530 }
   1531 
   1532 
   1533 
   1534 static BerElement *
   1535 re_encode_request( LDAP *ld,
   1536 	BerElement *origber,
   1537 	ber_int_t msgid,
   1538 	int sref,
   1539 	LDAPURLDesc *srv,
   1540 	int *type )
   1541 {
   1542 	/*
   1543 	 * XXX this routine knows way too much about how the lber library works!
   1544 	 */
   1545 	ber_int_t	along;
   1546 	ber_tag_t	tag;
   1547 	ber_tag_t	rtag;
   1548 	ber_int_t	ver;
   1549 	ber_int_t	scope;
   1550 	int		rc;
   1551 	BerElement	tmpber, *ber;
   1552 	struct berval		dn;
   1553 
   1554 	Debug2( LDAP_DEBUG_TRACE,
   1555 	    "re_encode_request: new msgid %ld, new dn <%s>\n",
   1556 	    (long) msgid,
   1557 		( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn );
   1558 
   1559 	tmpber = *origber;
   1560 
   1561 	/*
   1562 	 * all LDAP requests are sequences that start with a message id.
   1563 	 * For all except delete, this is followed by a sequence that is
   1564 	 * tagged with the operation code.  For delete, the provided DN
   1565 	 * is not wrapped by a sequence.
   1566 	 */
   1567 	rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
   1568 
   1569 	if ( rtag == LBER_ERROR ) {
   1570 		ld->ld_errno = LDAP_DECODING_ERROR;
   1571 		return( NULL );
   1572 	}
   1573 
   1574 	assert( tag != 0);
   1575 	if ( tag == LDAP_REQ_BIND ) {
   1576 		/* bind requests have a version number before the DN & other stuff */
   1577 		rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn );
   1578 
   1579 	} else if ( tag == LDAP_REQ_DELETE ) {
   1580 		/* delete requests don't have a DN wrapping sequence */
   1581 		rtag = ber_scanf( &tmpber, "m", &dn );
   1582 
   1583 	} else if ( tag == LDAP_REQ_SEARCH ) {
   1584 		/* search requests need to be re-scope-ed */
   1585 		rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope );
   1586 
   1587 		if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
   1588 			/* use the scope provided in reference */
   1589 			scope = srv->lud_scope;
   1590 
   1591 		} else if ( sref ) {
   1592 			/* use scope implied by previous operation
   1593 			 *   base -> base
   1594 			 *   one -> base
   1595 			 *   subtree -> subtree
   1596 			 *   subordinate -> subtree
   1597 			 */
   1598 			switch( scope ) {
   1599 			default:
   1600 			case LDAP_SCOPE_BASE:
   1601 			case LDAP_SCOPE_ONELEVEL:
   1602 				scope = LDAP_SCOPE_BASE;
   1603 				break;
   1604 			case LDAP_SCOPE_SUBTREE:
   1605 			case LDAP_SCOPE_SUBORDINATE:
   1606 				scope = LDAP_SCOPE_SUBTREE;
   1607 				break;
   1608 			}
   1609 		}
   1610 
   1611 	} else {
   1612 		rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn );
   1613 	}
   1614 
   1615 	if( rtag == LBER_ERROR ) {
   1616 		ld->ld_errno = LDAP_DECODING_ERROR;
   1617 		return NULL;
   1618 	}
   1619 
   1620 	/* restore character zero'd out by ber_scanf*/
   1621 	dn.bv_val[dn.bv_len] = tmpber.ber_tag;
   1622 
   1623 	if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
   1624 		return NULL;
   1625 	}
   1626 
   1627 	if ( srv->lud_dn ) {
   1628 		ber_str2bv( srv->lud_dn, 0, 0, &dn );
   1629 	}
   1630 
   1631 	if ( tag == LDAP_REQ_BIND ) {
   1632 		rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn );
   1633 	} else if ( tag == LDAP_REQ_DELETE ) {
   1634 		rc = ber_printf( ber, "{itON}", msgid, tag, &dn );
   1635 	} else if ( tag == LDAP_REQ_SEARCH ) {
   1636 		rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope );
   1637 	} else {
   1638 		rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn );
   1639 	}
   1640 
   1641 	if ( rc == -1 ) {
   1642 		ld->ld_errno = LDAP_ENCODING_ERROR;
   1643 		ber_free( ber, 1 );
   1644 		return NULL;
   1645 	}
   1646 
   1647 	if ( tag != LDAP_REQ_DELETE && (
   1648 		ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
   1649 		!= ( tmpber.ber_end - tmpber.ber_ptr ) ||
   1650 	    ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
   1651 	{
   1652 		ld->ld_errno = LDAP_ENCODING_ERROR;
   1653 		ber_free( ber, 1 );
   1654 		return NULL;
   1655 	}
   1656 
   1657 #ifdef LDAP_DEBUG
   1658 	if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
   1659 		Debug0( LDAP_DEBUG_ANY, "re_encode_request new request is:\n" );
   1660 		ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
   1661 	}
   1662 #endif /* LDAP_DEBUG */
   1663 
   1664 	*type = tag;	/* return request type */
   1665 	return ber;
   1666 }
   1667 
   1668 
   1669 /* protected by req_mutex */
   1670 LDAPRequest *
   1671 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
   1672 {
   1673 	LDAPRequest	*lr, needle = {0};
   1674 	needle.lr_msgid = msgid;
   1675 
   1676 	lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp );
   1677 	if ( lr != NULL && lr->lr_status != LDAP_REQST_COMPLETED ) {
   1678 		/* lr_refcnt is only negative when we removed it from ld_requests
   1679 		 * already, it is positive if we have sub-requests (referrals) */
   1680 		assert( lr->lr_refcnt >= 0 );
   1681 		lr->lr_refcnt++;
   1682 		Debug3( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: "
   1683 				"msgid %d, lr %p lr->lr_refcnt = %d\n",
   1684 				msgid, (void *) lr, lr->lr_refcnt );
   1685 		return lr;
   1686 	}
   1687 
   1688 	Debug2( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: "
   1689 			"msgid %d, lr %p\n", msgid, (void *) lr );
   1690 	return NULL;
   1691 }
   1692 
   1693 /* protected by req_mutex */
   1694 void
   1695 ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
   1696 {
   1697 	LDAPRequest	*lr;
   1698 
   1699 	lr = ldap_tavl_find( ld->ld_requests, lrx, ldap_req_cmp );
   1700 	Debug2( LDAP_DEBUG_TRACE, "ldap_return_request: "
   1701 			"lrx %p, lr %p\n", (void *) lrx, (void *) lr );
   1702 	if ( lr ) {
   1703 		assert( lr == lrx );
   1704 		if ( lr->lr_refcnt > 0 ) {
   1705 			lr->lr_refcnt--;
   1706 		} else if ( lr->lr_refcnt < 0 ) {
   1707 			lr->lr_refcnt++;
   1708 			if ( lr->lr_refcnt == 0 ) {
   1709 				lr = NULL;
   1710 			}
   1711 		}
   1712 	}
   1713 	Debug3( LDAP_DEBUG_TRACE, "ldap_return_request: "
   1714 			"lrx->lr_msgid %d, lrx->lr_refcnt is now %d, lr is %s present\n",
   1715 			lrx->lr_msgid, lrx->lr_refcnt, lr ? "still" : "not" );
   1716 	/* The request is not tracked anymore */
   1717 	if ( lr == NULL ) {
   1718 		ldap_free_request_int( ld, lrx );
   1719 	} else if ( freeit ) {
   1720 		ldap_free_request( ld, lrx );
   1721 	}
   1722 }
   1723