xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/request.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1*549b59edSchristos /*	$NetBSD: request.c,v 1.3 2021/08/14 16:14:56 christos Exp $	*/
24e6df137Slukem 
3d11b170bStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem  *
6*549b59edSchristos  * Copyright 1998-2021 The OpenLDAP Foundation.
72de962bdSlukem  * All rights reserved.
82de962bdSlukem  *
92de962bdSlukem  * Redistribution and use in source and binary forms, with or without
102de962bdSlukem  * modification, are permitted only as authorized by the OpenLDAP
112de962bdSlukem  * Public License.
122de962bdSlukem  *
132de962bdSlukem  * A copy of this license is available in the file LICENSE in the
142de962bdSlukem  * top-level directory of the distribution or, alternatively, at
152de962bdSlukem  * <http://www.OpenLDAP.org/license.html>.
162de962bdSlukem  */
172de962bdSlukem /* Portions Copyright (c) 1995 Regents of the University of Michigan.
182de962bdSlukem  * All rights reserved.
192de962bdSlukem  */
202de962bdSlukem /* This notice applies to changes, created by or for Novell, Inc.,
212de962bdSlukem  * to preexisting works for which notices appear elsewhere in this file.
222de962bdSlukem  *
232de962bdSlukem  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
242de962bdSlukem  *
252de962bdSlukem  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
262de962bdSlukem  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
272de962bdSlukem  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
282de962bdSlukem  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
292de962bdSlukem  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
302de962bdSlukem  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
312de962bdSlukem  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
322de962bdSlukem  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
332de962bdSlukem  *---
342de962bdSlukem  * Modification to OpenLDAP source by Novell, Inc.
352de962bdSlukem  * April 2000 sfs  Added code to chase V3 referrals
362de962bdSlukem  *  request.c - sending of ldap requests; handling of referrals
372de962bdSlukem  *---
382de962bdSlukem  * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
392de962bdSlukem  * can be found in the file "build/LICENSE-2.0.1" in this distribution
402de962bdSlukem  * of OpenLDAP Software.
412de962bdSlukem  */
422de962bdSlukem 
43376af7d7Schristos #include <sys/cdefs.h>
44*549b59edSchristos __RCSID("$NetBSD: request.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
45376af7d7Schristos 
462de962bdSlukem #include "portable.h"
472de962bdSlukem 
482de962bdSlukem #include <stdio.h>
492de962bdSlukem 
502de962bdSlukem #include <ac/stdlib.h>
512de962bdSlukem 
522de962bdSlukem #include <ac/errno.h>
532de962bdSlukem #include <ac/socket.h>
542de962bdSlukem #include <ac/string.h>
552de962bdSlukem #include <ac/time.h>
562de962bdSlukem #include <ac/unistd.h>
572de962bdSlukem 
582de962bdSlukem #include "ldap-int.h"
592de962bdSlukem #include "lber.h"
602de962bdSlukem 
61d11b170bStron /* used by ldap_send_server_request and ldap_new_connection */
62d11b170bStron #ifdef LDAP_R_COMPILE
63d11b170bStron #define LDAP_CONN_LOCK_IF(nolock) \
64d11b170bStron 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); }
65d11b170bStron #define LDAP_CONN_UNLOCK_IF(nolock) \
66d11b170bStron 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); }
67d11b170bStron #define LDAP_REQ_LOCK_IF(nolock) \
68d11b170bStron 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); }
69d11b170bStron #define LDAP_REQ_UNLOCK_IF(nolock) \
70d11b170bStron 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); }
71d11b170bStron #define LDAP_RES_LOCK_IF(nolock) \
72d11b170bStron 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); }
73d11b170bStron #define LDAP_RES_UNLOCK_IF(nolock) \
74d11b170bStron 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); }
75d11b170bStron #else
76d11b170bStron #define LDAP_CONN_LOCK_IF(nolock)
77d11b170bStron #define LDAP_CONN_UNLOCK_IF(nolock)
78d11b170bStron #define LDAP_REQ_LOCK_IF(nolock)
79d11b170bStron #define LDAP_REQ_UNLOCK_IF(nolock)
80d11b170bStron #define LDAP_RES_LOCK_IF(nolock)
81d11b170bStron #define LDAP_RES_UNLOCK_IF(nolock)
82d11b170bStron #endif
83d11b170bStron 
842de962bdSlukem static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
852de962bdSlukem static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
862de962bdSlukem static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr ));
872de962bdSlukem 
882de962bdSlukem static BerElement *
892de962bdSlukem re_encode_request( LDAP *ld,
902de962bdSlukem 	BerElement *origber,
912de962bdSlukem 	ber_int_t msgid,
922de962bdSlukem 	int sref,
932de962bdSlukem 	LDAPURLDesc *srv,
942de962bdSlukem 	int *type );
952de962bdSlukem 
962de962bdSlukem BerElement *
ldap_alloc_ber_with_options(LDAP * ld)972de962bdSlukem ldap_alloc_ber_with_options( LDAP *ld )
982de962bdSlukem {
992de962bdSlukem 	BerElement	*ber;
1002de962bdSlukem 
1012de962bdSlukem 	ber = ber_alloc_t( ld->ld_lberoptions );
1022de962bdSlukem 	if ( ber == NULL ) {
1032de962bdSlukem 		ld->ld_errno = LDAP_NO_MEMORY;
1042de962bdSlukem 	}
1052de962bdSlukem 
1062de962bdSlukem 	return( ber );
1072de962bdSlukem }
1082de962bdSlukem 
1092de962bdSlukem 
1102de962bdSlukem void
ldap_set_ber_options(LDAP * ld,BerElement * ber)1112de962bdSlukem ldap_set_ber_options( LDAP *ld, BerElement *ber )
1122de962bdSlukem {
113d11b170bStron 	/* ld_lberoptions is constant, hence no lock */
1142de962bdSlukem 	ber->ber_options = ld->ld_lberoptions;
1152de962bdSlukem }
1162de962bdSlukem 
1172de962bdSlukem 
118d11b170bStron /* sets needed mutexes - no mutexes set to this point */
1192de962bdSlukem ber_int_t
ldap_send_initial_request(LDAP * ld,ber_tag_t msgtype,const char * dn,BerElement * ber,ber_int_t msgid)1202de962bdSlukem ldap_send_initial_request(
1212de962bdSlukem 	LDAP *ld,
1222de962bdSlukem 	ber_tag_t msgtype,
1232de962bdSlukem 	const char *dn,
1242de962bdSlukem 	BerElement *ber,
1252de962bdSlukem 	ber_int_t msgid)
1262de962bdSlukem {
1272de962bdSlukem 	int rc = 1;
128d11b170bStron 	ber_socket_t sd = AC_SOCKET_INVALID;
1292de962bdSlukem 
130*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n" );
1312de962bdSlukem 
132d11b170bStron 	LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
133d11b170bStron 	if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) {
1342de962bdSlukem 		/* not connected yet */
1352de962bdSlukem 		rc = ldap_open_defconn( ld );
136376af7d7Schristos 		if ( rc == 0 ) {
137376af7d7Schristos 			ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
138376af7d7Schristos 				LBER_SB_OPT_GET_FD, &sd );
139376af7d7Schristos 		}
1402de962bdSlukem 	}
141d11b170bStron 	if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING )
142d11b170bStron 		rc = ldap_int_check_async_open( ld, sd );
1432de962bdSlukem 	if( rc < 0 ) {
1442de962bdSlukem 		ber_free( ber, 1 );
145d11b170bStron 		LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
1462de962bdSlukem 		return( -1 );
1472de962bdSlukem 	} else if ( rc == 0 ) {
148*549b59edSchristos 		Debug0( LDAP_DEBUG_TRACE,
149*549b59edSchristos 			"ldap_open_defconn: successful\n" );
1502de962bdSlukem 	}
1512de962bdSlukem 
1522de962bdSlukem #ifdef LDAP_CONNECTIONLESS
1532de962bdSlukem 	if (LDAP_IS_UDP(ld)) {
1542de962bdSlukem 		if (msgtype == LDAP_REQ_BIND) {
155d11b170bStron 			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
1562de962bdSlukem 			if (ld->ld_options.ldo_cldapdn)
1572de962bdSlukem 				ldap_memfree(ld->ld_options.ldo_cldapdn);
1582de962bdSlukem 			ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
159ef2f90d3Sadam 			ber_free( ber, 1 );
160d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
161d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
1622de962bdSlukem 			return 0;
1632de962bdSlukem 		}
1642de962bdSlukem 		if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
165ef2f90d3Sadam 		{
166ef2f90d3Sadam 			ber_free( ber, 1 );
167d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
1682de962bdSlukem 			return LDAP_PARAM_ERROR;
1692de962bdSlukem 		}
170ef2f90d3Sadam 	}
1712de962bdSlukem #endif
172d11b170bStron 	LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
1732de962bdSlukem 	rc = ldap_send_server_request( ld, ber, msgid, NULL,
174d11b170bStron 		NULL, NULL, NULL, 0, 0 );
175d11b170bStron 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
176d11b170bStron 	LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
1772de962bdSlukem 	return(rc);
1782de962bdSlukem }
1792de962bdSlukem 
1802de962bdSlukem 
181d11b170bStron /* protected by conn_mutex */
1822de962bdSlukem int
ldap_int_flush_request(LDAP * ld,LDAPRequest * lr)1832de962bdSlukem ldap_int_flush_request(
1842de962bdSlukem 	LDAP *ld,
1852de962bdSlukem 	LDAPRequest *lr )
1862de962bdSlukem {
1872de962bdSlukem 	LDAPConn *lc = lr->lr_conn;
1882de962bdSlukem 
189d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
1902de962bdSlukem 	if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
1914e27b3e8Schristos 		if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) {
1924e27b3e8Schristos 			/* ENOTCONN is returned in Solaris 10 */
1932de962bdSlukem 			/* need to continue write later */
1942de962bdSlukem 			lr->lr_status = LDAP_REQST_WRITING;
1952de962bdSlukem 			ldap_mark_select_write( ld, lc->lconn_sb );
1962de962bdSlukem 			ld->ld_errno = LDAP_BUSY;
1972de962bdSlukem 			return -2;
1982de962bdSlukem 		} else {
1992de962bdSlukem 			ld->ld_errno = LDAP_SERVER_DOWN;
2002de962bdSlukem 			ldap_free_request( ld, lr );
2012de962bdSlukem 			ldap_free_connection( ld, lc, 0, 0 );
2022de962bdSlukem 			return( -1 );
2032de962bdSlukem 		}
2042de962bdSlukem 	} else {
2052de962bdSlukem 		if ( lr->lr_parent == NULL ) {
2062de962bdSlukem 			lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
2072de962bdSlukem 			lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
2082de962bdSlukem 		}
2092de962bdSlukem 		lr->lr_status = LDAP_REQST_INPROGRESS;
2102de962bdSlukem 
2112de962bdSlukem 		/* sent -- waiting for a response */
2122de962bdSlukem 		ldap_mark_select_read( ld, lc->lconn_sb );
213d11b170bStron 		ldap_clear_select_write( ld, lc->lconn_sb );
2142de962bdSlukem 	}
2152de962bdSlukem 	return 0;
2162de962bdSlukem }
2172de962bdSlukem 
218d11b170bStron /*
219d11b170bStron  * protected by req_mutex
220d11b170bStron  * if m_noconn then protect using conn_lock
221d11b170bStron  * else already protected with conn_lock
222d11b170bStron  * if m_res then also protected by res_mutex
223d11b170bStron  */
224d11b170bStron 
2252de962bdSlukem int
ldap_send_server_request(LDAP * ld,BerElement * ber,ber_int_t msgid,LDAPRequest * parentreq,LDAPURLDesc ** srvlist,LDAPConn * lc,LDAPreqinfo * bind,int m_noconn,int m_res)2262de962bdSlukem ldap_send_server_request(
2272de962bdSlukem 	LDAP *ld,
2282de962bdSlukem 	BerElement *ber,
2292de962bdSlukem 	ber_int_t msgid,
2302de962bdSlukem 	LDAPRequest *parentreq,
2312de962bdSlukem 	LDAPURLDesc **srvlist,
2322de962bdSlukem 	LDAPConn *lc,
233d11b170bStron 	LDAPreqinfo *bind,
234d11b170bStron 	int m_noconn,
235d11b170bStron 	int m_res )
2362de962bdSlukem {
2372de962bdSlukem 	LDAPRequest	*lr;
2382de962bdSlukem 	int		incparent, rc;
2392de962bdSlukem 
240d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
241*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_send_server_request\n" );
2422de962bdSlukem 
2432de962bdSlukem 	incparent = 0;
2442de962bdSlukem 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
2452de962bdSlukem 
246d11b170bStron 	LDAP_CONN_LOCK_IF(m_noconn);
2472de962bdSlukem 	if ( lc == NULL ) {
2482de962bdSlukem 		if ( srvlist == NULL ) {
2492de962bdSlukem 			lc = ld->ld_defconn;
2502de962bdSlukem 		} else {
2512de962bdSlukem 			lc = find_connection( ld, *srvlist, 1 );
2522de962bdSlukem 			if ( lc == NULL ) {
2532de962bdSlukem 				if ( (bind != NULL) && (parentreq != NULL) ) {
2542de962bdSlukem 					/* Remember the bind in the parent */
2552de962bdSlukem 					incparent = 1;
2562de962bdSlukem 					++parentreq->lr_outrefcnt;
2572de962bdSlukem 				}
258d11b170bStron 				lc = ldap_new_connection( ld, srvlist, 0,
259d11b170bStron 					1, bind, 1, m_res );
2602de962bdSlukem 			}
2612de962bdSlukem 		}
2622de962bdSlukem 	}
2632de962bdSlukem 
2642de962bdSlukem 	/* async connect... */
2652de962bdSlukem 	if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
2662de962bdSlukem 		ber_socket_t	sd = AC_SOCKET_ERROR;
2672de962bdSlukem 		struct timeval	tv = { 0 };
2682de962bdSlukem 
2692de962bdSlukem 		ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
2702de962bdSlukem 
2712de962bdSlukem 		/* poll ... */
272d11b170bStron 		switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) {
2732de962bdSlukem 		case 0:
2742de962bdSlukem 			/* go on! */
2752de962bdSlukem 			lc->lconn_status = LDAP_CONNST_CONNECTED;
2762de962bdSlukem 			break;
2772de962bdSlukem 
2782de962bdSlukem 		case -2:
2792de962bdSlukem 			/* async only occurs if a network timeout is set */
2802de962bdSlukem 
2812de962bdSlukem 			/* honor network timeout */
282d11b170bStron 			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
2832de962bdSlukem 			if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
2842de962bdSlukem 			{
2852de962bdSlukem 				/* caller will have to call again */
2862de962bdSlukem 				ld->ld_errno = LDAP_X_CONNECTING;
2872de962bdSlukem 			}
288d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
2892de962bdSlukem 			/* fallthru */
2902de962bdSlukem 
2912de962bdSlukem 		default:
2922de962bdSlukem 			/* error */
2932de962bdSlukem 			break;
2942de962bdSlukem 		}
2952de962bdSlukem 	}
2962de962bdSlukem 
2972de962bdSlukem 	if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
2982de962bdSlukem 		if ( ld->ld_errno == LDAP_SUCCESS ) {
2992de962bdSlukem 			ld->ld_errno = LDAP_SERVER_DOWN;
3002de962bdSlukem 		}
3012de962bdSlukem 
3022de962bdSlukem 		ber_free( ber, 1 );
3032de962bdSlukem 		if ( incparent ) {
3042de962bdSlukem 			/* Forget about the bind */
3052de962bdSlukem 			--parentreq->lr_outrefcnt;
3062de962bdSlukem 		}
307d11b170bStron 		LDAP_CONN_UNLOCK_IF(m_noconn);
3082de962bdSlukem 		return( -1 );
3092de962bdSlukem 	}
3102de962bdSlukem 
3112de962bdSlukem 	use_connection( ld, lc );
3122de962bdSlukem 
3132de962bdSlukem #ifdef LDAP_CONNECTIONLESS
3142de962bdSlukem 	if ( LDAP_IS_UDP( ld )) {
3152de962bdSlukem 		BerElement tmpber = *ber;
3162de962bdSlukem 		ber_rewind( &tmpber );
317d11b170bStron 		LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
3182de962bdSlukem 		rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
319d11b170bStron 			sizeof( struct sockaddr_storage ), 0 );
320d11b170bStron 		LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
3212de962bdSlukem 		if ( rc == -1 ) {
3222de962bdSlukem 			ld->ld_errno = LDAP_ENCODING_ERROR;
3234e27b3e8Schristos 			ber_free( ber, 1 );
324d11b170bStron 			LDAP_CONN_UNLOCK_IF(m_noconn);
3252de962bdSlukem 			return rc;
3262de962bdSlukem 		}
3272de962bdSlukem 	}
3282de962bdSlukem #endif
3292de962bdSlukem 
3302de962bdSlukem 	/* If we still have an incomplete write, try to finish it before
3312de962bdSlukem 	 * dealing with the new request. If we don't finish here, return
3322de962bdSlukem 	 * LDAP_BUSY and let the caller retry later. We only allow a single
3332de962bdSlukem 	 * request to be in WRITING state.
3342de962bdSlukem 	 */
3352de962bdSlukem 	rc = 0;
336*549b59edSchristos 	if ( ld->ld_requests != NULL ) {
337*549b59edSchristos 		TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT );
338*549b59edSchristos 		LDAPRequest *lr;
339*549b59edSchristos 
340*549b59edSchristos 		assert( node != NULL );
341*549b59edSchristos 		lr = node->avl_data;
342*549b59edSchristos 		if ( lr->lr_status == LDAP_REQST_WRITING &&
343*549b59edSchristos 				ldap_int_flush_request( ld, lr ) < 0 ) {
3442de962bdSlukem 			rc = -1;
3452de962bdSlukem 		}
346*549b59edSchristos 	}
347d11b170bStron 	if ( rc ) {
3484e27b3e8Schristos 		ber_free( ber, 1 );
349d11b170bStron 		LDAP_CONN_UNLOCK_IF(m_noconn);
350d11b170bStron 		return rc;
351d11b170bStron 	}
3522de962bdSlukem 
3532de962bdSlukem 	lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
3542de962bdSlukem 	if ( lr == NULL ) {
3552de962bdSlukem 		ld->ld_errno = LDAP_NO_MEMORY;
3562de962bdSlukem 		ldap_free_connection( ld, lc, 0, 0 );
3572de962bdSlukem 		ber_free( ber, 1 );
3582de962bdSlukem 		if ( incparent ) {
3592de962bdSlukem 			/* Forget about the bind */
3602de962bdSlukem 			--parentreq->lr_outrefcnt;
3612de962bdSlukem 		}
362d11b170bStron 		LDAP_CONN_UNLOCK_IF(m_noconn);
3632de962bdSlukem 		return( -1 );
3642de962bdSlukem 	}
3652de962bdSlukem 	lr->lr_msgid = msgid;
3662de962bdSlukem 	lr->lr_status = LDAP_REQST_INPROGRESS;
3672de962bdSlukem 	lr->lr_res_errno = LDAP_SUCCESS;	/* optimistic */
3682de962bdSlukem 	lr->lr_ber = ber;
3692de962bdSlukem 	lr->lr_conn = lc;
3702de962bdSlukem 	if ( parentreq != NULL ) {	/* sub-request */
3712de962bdSlukem 		if ( !incparent ) {
3722de962bdSlukem 			/* Increment if we didn't do it before the bind */
3732de962bdSlukem 			++parentreq->lr_outrefcnt;
3742de962bdSlukem 		}
3752de962bdSlukem 		lr->lr_origid = parentreq->lr_origid;
3762de962bdSlukem 		lr->lr_parentcnt = ++parentreq->lr_parentcnt;
3772de962bdSlukem 		lr->lr_parent = parentreq;
3782de962bdSlukem 		lr->lr_refnext = parentreq->lr_child;
3792de962bdSlukem 		parentreq->lr_child = lr;
3802de962bdSlukem 	} else {			/* original request */
3812de962bdSlukem 		lr->lr_origid = lr->lr_msgid;
3822de962bdSlukem 	}
3832de962bdSlukem 
3842de962bdSlukem 	/* Extract requestDN for future reference */
385d11b170bStron #ifdef LDAP_CONNECTIONLESS
386d11b170bStron 	if ( !LDAP_IS_UDP(ld) )
387d11b170bStron #endif
3882de962bdSlukem 	{
3892de962bdSlukem 		BerElement tmpber = *ber;
3902de962bdSlukem 		ber_int_t	bint;
3912de962bdSlukem 		ber_tag_t	tag, rtag;
3922de962bdSlukem 
3932de962bdSlukem 		ber_reset( &tmpber, 1 );
3942de962bdSlukem 		rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
3952de962bdSlukem 		switch ( tag ) {
3962de962bdSlukem 		case LDAP_REQ_BIND:
3972de962bdSlukem 			rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
3982de962bdSlukem 			break;
3992de962bdSlukem 		case LDAP_REQ_DELETE:
4002de962bdSlukem 			break;
4012de962bdSlukem 		default:
4022de962bdSlukem 			rtag = ber_scanf( &tmpber, "{" /*}*/ );
4032de962bdSlukem 		case LDAP_REQ_ABANDON:
4042de962bdSlukem 			break;
4052de962bdSlukem 		}
4062de962bdSlukem 		if ( tag != LDAP_REQ_ABANDON ) {
4072de962bdSlukem 			ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
4082de962bdSlukem 			lr->lr_dn.bv_val = tmpber.ber_ptr;
4092de962bdSlukem 		}
4102de962bdSlukem 	}
4112de962bdSlukem 
412*549b59edSchristos 	rc = ldap_tavl_insert( &ld->ld_requests, lr, ldap_req_cmp, ldap_avl_dup_error );
413*549b59edSchristos 	assert( rc == LDAP_SUCCESS );
4142de962bdSlukem 
4152de962bdSlukem 	ld->ld_errno = LDAP_SUCCESS;
4162de962bdSlukem 	if ( ldap_int_flush_request( ld, lr ) == -1 ) {
4172de962bdSlukem 		msgid = -1;
4182de962bdSlukem 	}
4192de962bdSlukem 
420d11b170bStron 	LDAP_CONN_UNLOCK_IF(m_noconn);
4212de962bdSlukem 	return( msgid );
4222de962bdSlukem }
4232de962bdSlukem 
4244e6df137Slukem /* return 0 if no StartTLS ext, 1 if present, 2 if critical */
4254e6df137Slukem static int
find_tls_ext(LDAPURLDesc * srv)4264e6df137Slukem find_tls_ext( LDAPURLDesc *srv )
4274e6df137Slukem {
4284e6df137Slukem 	int i, crit;
4294e6df137Slukem 	char *ext;
4304e6df137Slukem 
4314e6df137Slukem 	if ( !srv->lud_exts )
4324e6df137Slukem 		return 0;
4334e6df137Slukem 
4344e6df137Slukem 	for (i=0; srv->lud_exts[i]; i++) {
4354e6df137Slukem 		crit = 0;
4364e6df137Slukem 		ext = srv->lud_exts[i];
4374e6df137Slukem 		if ( ext[0] == '!') {
4384e6df137Slukem 			ext++;
4394e6df137Slukem 			crit = 1;
4404e6df137Slukem 		}
4414e6df137Slukem 		if ( !strcasecmp( ext, "StartTLS" ) ||
4424e6df137Slukem 			!strcasecmp( ext, "X-StartTLS" ) ||
4434e6df137Slukem 			!strcmp( ext, LDAP_EXOP_START_TLS )) {
4444e6df137Slukem 			return crit + 1;
4454e6df137Slukem 		}
4464e6df137Slukem 	}
4474e6df137Slukem 	return 0;
4484e6df137Slukem }
4494e6df137Slukem 
450d11b170bStron /*
451d11b170bStron  * always protected by conn_mutex
452d11b170bStron  * optionally protected by req_mutex and res_mutex
453d11b170bStron  */
4542de962bdSlukem LDAPConn *
ldap_new_connection(LDAP * ld,LDAPURLDesc ** srvlist,int use_ldsb,int connect,LDAPreqinfo * bind,int m_req,int m_res)4552de962bdSlukem ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
456d11b170bStron 	int connect, LDAPreqinfo *bind, int m_req, int m_res )
4572de962bdSlukem {
4582de962bdSlukem 	LDAPConn	*lc;
4592de962bdSlukem 	int		async = 0;
4602de962bdSlukem 
461d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
462*549b59edSchristos 	Debug3( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
4632de962bdSlukem 		use_ldsb, connect, (bind != NULL) );
4642de962bdSlukem 	/*
4652de962bdSlukem 	 * make a new LDAP server connection
4662de962bdSlukem 	 * XXX open connection synchronously for now
4672de962bdSlukem 	 */
4682de962bdSlukem 	lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) );
4692de962bdSlukem 	if ( lc == NULL ) {
4702de962bdSlukem 		ld->ld_errno = LDAP_NO_MEMORY;
4712de962bdSlukem 		return( NULL );
4722de962bdSlukem 	}
4732de962bdSlukem 
4742de962bdSlukem 	if ( use_ldsb ) {
4752de962bdSlukem 		assert( ld->ld_sb != NULL );
4762de962bdSlukem 		lc->lconn_sb = ld->ld_sb;
4772de962bdSlukem 
4782de962bdSlukem 	} else {
4792de962bdSlukem 		lc->lconn_sb = ber_sockbuf_alloc();
4802de962bdSlukem 		if ( lc->lconn_sb == NULL ) {
4812de962bdSlukem 			LDAP_FREE( (char *)lc );
4822de962bdSlukem 			ld->ld_errno = LDAP_NO_MEMORY;
4832de962bdSlukem 			return( NULL );
4842de962bdSlukem 		}
4852de962bdSlukem 	}
4862de962bdSlukem 
4872de962bdSlukem 	if ( connect ) {
4882de962bdSlukem 		LDAPURLDesc	**srvp, *srv = NULL;
4892de962bdSlukem 
4902de962bdSlukem 		async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
4912de962bdSlukem 
4922de962bdSlukem 		for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
4932de962bdSlukem 			int		rc;
4942de962bdSlukem 
4952de962bdSlukem 			rc = ldap_int_open_connection( ld, lc, *srvp, async );
4962de962bdSlukem 			if ( rc != -1 ) {
4972de962bdSlukem 				srv = *srvp;
4982de962bdSlukem 
499376af7d7Schristos 				/* If we fully connected, async is moot */
500376af7d7Schristos 				if ( rc == 0 )
501376af7d7Schristos 					async = 0;
502376af7d7Schristos 
5032de962bdSlukem 				if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) {
5042de962bdSlukem 					ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params );
5052de962bdSlukem 				}
5062de962bdSlukem 
5072de962bdSlukem 				break;
5082de962bdSlukem 			}
5092de962bdSlukem 		}
5102de962bdSlukem 
5112de962bdSlukem 		if ( srv == NULL ) {
5122de962bdSlukem 			if ( !use_ldsb ) {
5132de962bdSlukem 				ber_sockbuf_free( lc->lconn_sb );
5142de962bdSlukem 			}
5152de962bdSlukem 			LDAP_FREE( (char *)lc );
5162de962bdSlukem 			ld->ld_errno = LDAP_SERVER_DOWN;
5172de962bdSlukem 			return( NULL );
5182de962bdSlukem 		}
5192de962bdSlukem 
5202de962bdSlukem 		lc->lconn_server = ldap_url_dup( srv );
521376af7d7Schristos 		if ( !lc->lconn_server ) {
522376af7d7Schristos 			if ( !use_ldsb )
523376af7d7Schristos 				ber_sockbuf_free( lc->lconn_sb );
524376af7d7Schristos 			LDAP_FREE( (char *)lc );
525376af7d7Schristos 			ld->ld_errno = LDAP_NO_MEMORY;
526376af7d7Schristos 			return( NULL );
527376af7d7Schristos 		}
5282de962bdSlukem 	}
5292de962bdSlukem 
5302de962bdSlukem 	lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
5312de962bdSlukem 	lc->lconn_next = ld->ld_conns;
5322de962bdSlukem 	ld->ld_conns = lc;
5332de962bdSlukem 
5344e6df137Slukem 	if ( connect ) {
5354e6df137Slukem #ifdef HAVE_TLS
5364e6df137Slukem 		if ( lc->lconn_server->lud_exts ) {
5374e6df137Slukem 			int rc, ext = find_tls_ext( lc->lconn_server );
5384e6df137Slukem 			if ( ext ) {
5394e6df137Slukem 				LDAPConn	*savedefconn;
5404e6df137Slukem 
5414e6df137Slukem 				savedefconn = ld->ld_defconn;
5424e6df137Slukem 				++lc->lconn_refcnt;	/* avoid premature free */
5434e6df137Slukem 				ld->ld_defconn = lc;
5444e6df137Slukem 
545d11b170bStron 				LDAP_REQ_UNLOCK_IF(m_req);
546d11b170bStron 				LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
547d11b170bStron 				LDAP_RES_UNLOCK_IF(m_res);
5484e6df137Slukem 				rc = ldap_start_tls_s( ld, NULL, NULL );
549d11b170bStron 				LDAP_RES_LOCK_IF(m_res);
550d11b170bStron 				LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
551d11b170bStron 				LDAP_REQ_LOCK_IF(m_req);
5524e6df137Slukem 				ld->ld_defconn = savedefconn;
5534e6df137Slukem 				--lc->lconn_refcnt;
5544e6df137Slukem 
5554e6df137Slukem 				if ( rc != LDAP_SUCCESS && ext == 2 ) {
5564e6df137Slukem 					ldap_free_connection( ld, lc, 1, 0 );
5574e6df137Slukem 					return NULL;
5584e6df137Slukem 				}
5594e6df137Slukem 			}
5604e6df137Slukem 		}
5614e6df137Slukem #endif
5624e6df137Slukem 	}
5634e6df137Slukem 
5642de962bdSlukem 	if ( bind != NULL ) {
5652de962bdSlukem 		int		err = 0;
5662de962bdSlukem 		LDAPConn	*savedefconn;
5672de962bdSlukem 
5682de962bdSlukem 		/* Set flag to prevent additional referrals
5692de962bdSlukem 		 * from being processed on this
5702de962bdSlukem 		 * connection until the bind has completed
5712de962bdSlukem 		 */
5722de962bdSlukem 		lc->lconn_rebind_inprogress = 1;
5732de962bdSlukem 		/* V3 rebind function */
5742de962bdSlukem 		if ( ld->ld_rebind_proc != NULL) {
5752de962bdSlukem 			LDAPURLDesc	*srvfunc;
5762de962bdSlukem 
5772de962bdSlukem 			srvfunc = ldap_url_dup( *srvlist );
5782de962bdSlukem 			if ( srvfunc == NULL ) {
5792de962bdSlukem 				ld->ld_errno = LDAP_NO_MEMORY;
5802de962bdSlukem 				err = -1;
5812de962bdSlukem 			} else {
5822de962bdSlukem 				savedefconn = ld->ld_defconn;
5832de962bdSlukem 				++lc->lconn_refcnt;	/* avoid premature free */
5842de962bdSlukem 				ld->ld_defconn = lc;
5852de962bdSlukem 
586*549b59edSchristos 				Debug0( LDAP_DEBUG_TRACE, "Call application rebind_proc\n" );
587d11b170bStron 				LDAP_REQ_UNLOCK_IF(m_req);
588d11b170bStron 				LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
589d11b170bStron 				LDAP_RES_UNLOCK_IF(m_res);
5902de962bdSlukem 				err = (*ld->ld_rebind_proc)( ld,
5912de962bdSlukem 					bind->ri_url, bind->ri_request, bind->ri_msgid,
5922de962bdSlukem 					ld->ld_rebind_params );
593d11b170bStron 				LDAP_RES_LOCK_IF(m_res);
594d11b170bStron 				LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
595d11b170bStron 				LDAP_REQ_LOCK_IF(m_req);
5962de962bdSlukem 
5972de962bdSlukem 				ld->ld_defconn = savedefconn;
5982de962bdSlukem 				--lc->lconn_refcnt;
5992de962bdSlukem 
6002de962bdSlukem 				if ( err != 0 ) {
6012de962bdSlukem 					err = -1;
6022de962bdSlukem 					ldap_free_connection( ld, lc, 1, 0 );
6032de962bdSlukem 					lc = NULL;
6042de962bdSlukem 				}
6052de962bdSlukem 				ldap_free_urldesc( srvfunc );
6062de962bdSlukem 			}
6072de962bdSlukem 
6082de962bdSlukem 		} else {
6092de962bdSlukem 			int		msgid, rc;
6102de962bdSlukem 			struct berval	passwd = BER_BVNULL;
6112de962bdSlukem 
6122de962bdSlukem 			savedefconn = ld->ld_defconn;
6132de962bdSlukem 			++lc->lconn_refcnt;	/* avoid premature free */
6142de962bdSlukem 			ld->ld_defconn = lc;
6152de962bdSlukem 
616*549b59edSchristos 			Debug0( LDAP_DEBUG_TRACE,
617*549b59edSchristos 				"anonymous rebind via ldap_sasl_bind(\"\")\n" );
6182de962bdSlukem 
619d11b170bStron 			LDAP_REQ_UNLOCK_IF(m_req);
620d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
621d11b170bStron 			LDAP_RES_UNLOCK_IF(m_res);
6222de962bdSlukem 			rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
6232de962bdSlukem 				NULL, NULL, &msgid );
6242de962bdSlukem 			if ( rc != LDAP_SUCCESS ) {
6252de962bdSlukem 				err = -1;
6262de962bdSlukem 
6272de962bdSlukem 			} else {
6282de962bdSlukem 				for ( err = 1; err > 0; ) {
6292de962bdSlukem 					struct timeval	tv = { 0, 100000 };
6302de962bdSlukem 					LDAPMessage	*res = NULL;
6312de962bdSlukem 
6322de962bdSlukem 					switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
6332de962bdSlukem 					case -1:
6342de962bdSlukem 						err = -1;
6352de962bdSlukem 						break;
6362de962bdSlukem 
6372de962bdSlukem 					case 0:
6382de962bdSlukem #ifdef LDAP_R_COMPILE
6392de962bdSlukem 						ldap_pvt_thread_yield();
6402de962bdSlukem #endif
6412de962bdSlukem 						break;
6422de962bdSlukem 
6432de962bdSlukem 					case LDAP_RES_BIND:
6442de962bdSlukem 						rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
6452de962bdSlukem 						if ( rc != LDAP_SUCCESS ) {
6462de962bdSlukem 							err = -1;
6472de962bdSlukem 
6482de962bdSlukem 						} else if ( err != LDAP_SUCCESS ) {
6492de962bdSlukem 							err = -1;
6502de962bdSlukem 						}
6512de962bdSlukem 						/* else err == LDAP_SUCCESS == 0 */
6522de962bdSlukem 						break;
6532de962bdSlukem 
6542de962bdSlukem 					default:
655*549b59edSchristos 						Debug3( LDAP_DEBUG_TRACE,
6562de962bdSlukem 							"ldap_new_connection %p: "
6572de962bdSlukem 							"unexpected response %d "
6582de962bdSlukem 							"from BIND request id=%d\n",
6592de962bdSlukem 							(void *) ld, ldap_msgtype( res ), msgid );
6602de962bdSlukem 						err = -1;
6612de962bdSlukem 						break;
6622de962bdSlukem 					}
6632de962bdSlukem 				}
6642de962bdSlukem 			}
665d11b170bStron 			LDAP_RES_LOCK_IF(m_res);
666d11b170bStron 			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
667d11b170bStron 			LDAP_REQ_LOCK_IF(m_req);
6682de962bdSlukem 			ld->ld_defconn = savedefconn;
6692de962bdSlukem 			--lc->lconn_refcnt;
6702de962bdSlukem 
6712de962bdSlukem 			if ( err != 0 ) {
6722de962bdSlukem 				ldap_free_connection( ld, lc, 1, 0 );
6732de962bdSlukem 				lc = NULL;
6742de962bdSlukem 			}
6752de962bdSlukem 		}
6762de962bdSlukem 		if ( lc != NULL )
6772de962bdSlukem 			lc->lconn_rebind_inprogress = 0;
6782de962bdSlukem 	}
6792de962bdSlukem 	return( lc );
6802de962bdSlukem }
6812de962bdSlukem 
6822de962bdSlukem 
683d11b170bStron /* protected by ld_conn_mutex */
6842de962bdSlukem static LDAPConn *
find_connection(LDAP * ld,LDAPURLDesc * srv,int any)6852de962bdSlukem find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
6862de962bdSlukem /*
6872de962bdSlukem  * return an existing connection (if any) to the server srv
6882de962bdSlukem  * if "any" is non-zero, check for any server in the "srv" chain
6892de962bdSlukem  */
6902de962bdSlukem {
6912de962bdSlukem 	LDAPConn	*lc;
6922de962bdSlukem 	LDAPURLDesc	*lcu, *lsu;
6932de962bdSlukem 	int lcu_port, lsu_port;
6942de962bdSlukem 	int found = 0;
6952de962bdSlukem 
696d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
6972de962bdSlukem 	for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
6982de962bdSlukem 		lcu = lc->lconn_server;
6992de962bdSlukem 		lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
7002de962bdSlukem 			lcu->lud_port );
7012de962bdSlukem 
7022de962bdSlukem 		for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
7032de962bdSlukem 			lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
7042de962bdSlukem 				lsu->lud_port );
7052de962bdSlukem 
7062de962bdSlukem 			if ( lsu_port == lcu_port
7072de962bdSlukem 				&& strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
7084e6df137Slukem 				&& lcu->lud_host != NULL && lsu->lud_host != NULL
7092de962bdSlukem 				&& strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
7102de962bdSlukem 			{
7112de962bdSlukem 				found = 1;
7122de962bdSlukem 				break;
7132de962bdSlukem 			}
7142de962bdSlukem 
7152de962bdSlukem 			if ( !any ) break;
7162de962bdSlukem 		}
7172de962bdSlukem 		if ( found )
7182de962bdSlukem 			break;
7192de962bdSlukem 	}
7202de962bdSlukem 	return lc;
7212de962bdSlukem }
7222de962bdSlukem 
7232de962bdSlukem 
7242de962bdSlukem 
725d11b170bStron /* protected by ld_conn_mutex */
7262de962bdSlukem static void
use_connection(LDAP * ld,LDAPConn * lc)7272de962bdSlukem use_connection( LDAP *ld, LDAPConn *lc )
7282de962bdSlukem {
729d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
7302de962bdSlukem 	++lc->lconn_refcnt;
7312de962bdSlukem 	lc->lconn_lastused = time( NULL );
7322de962bdSlukem }
7332de962bdSlukem 
7342de962bdSlukem 
735d11b170bStron /* protected by ld_conn_mutex */
7362de962bdSlukem void
ldap_free_connection(LDAP * ld,LDAPConn * lc,int force,int unbind)7372de962bdSlukem ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
7382de962bdSlukem {
7392de962bdSlukem 	LDAPConn	*tmplc, *prevlc;
7402de962bdSlukem 
741d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
742*549b59edSchristos 	Debug2( LDAP_DEBUG_TRACE,
7432de962bdSlukem 		"ldap_free_connection %d %d\n",
744*549b59edSchristos 		force, unbind );
7452de962bdSlukem 
7462de962bdSlukem 	if ( force || --lc->lconn_refcnt <= 0 ) {
7472de962bdSlukem 		/* remove from connections list first */
7482de962bdSlukem 
7492de962bdSlukem 		for ( prevlc = NULL, tmplc = ld->ld_conns;
7502de962bdSlukem 			tmplc != NULL;
7512de962bdSlukem 			tmplc = tmplc->lconn_next )
7522de962bdSlukem 		{
7532de962bdSlukem 			if ( tmplc == lc ) {
7542de962bdSlukem 				if ( prevlc == NULL ) {
7552de962bdSlukem 				    ld->ld_conns = tmplc->lconn_next;
7562de962bdSlukem 				} else {
7572de962bdSlukem 				    prevlc->lconn_next = tmplc->lconn_next;
7582de962bdSlukem 				}
759bb30016cSlukem 				if ( ld->ld_defconn == lc ) {
760bb30016cSlukem 					ld->ld_defconn = NULL;
761bb30016cSlukem 				}
7622de962bdSlukem 				break;
7632de962bdSlukem 			}
7642de962bdSlukem 			prevlc = tmplc;
7652de962bdSlukem 		}
7662de962bdSlukem 
7674e6df137Slukem 		/* process connection callbacks */
7684e6df137Slukem 		{
7694e6df137Slukem 			struct ldapoptions *lo;
7704e6df137Slukem 			ldaplist *ll;
7714e6df137Slukem 			ldap_conncb *cb;
7724e6df137Slukem 
7734e6df137Slukem 			lo = &ld->ld_options;
774d11b170bStron 			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
7754e6df137Slukem 			if ( lo->ldo_conn_cbs ) {
7764e6df137Slukem 				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
7774e6df137Slukem 					cb = ll->ll_data;
7784e6df137Slukem 					cb->lc_del( ld, lc->lconn_sb, cb );
7794e6df137Slukem 				}
7804e6df137Slukem 			}
781d11b170bStron 			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
7824e6df137Slukem 			lo = LDAP_INT_GLOBAL_OPT();
783d11b170bStron 			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
7844e6df137Slukem 			if ( lo->ldo_conn_cbs ) {
7854e6df137Slukem 				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
7864e6df137Slukem 					cb = ll->ll_data;
7874e6df137Slukem 					cb->lc_del( ld, lc->lconn_sb, cb );
7884e6df137Slukem 				}
7894e6df137Slukem 			}
790d11b170bStron 			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
7914e6df137Slukem 		}
7924e6df137Slukem 
7932de962bdSlukem 		if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
7942de962bdSlukem 			ldap_mark_select_clear( ld, lc->lconn_sb );
7952de962bdSlukem 			if ( unbind ) {
7962de962bdSlukem 				ldap_send_unbind( ld, lc->lconn_sb,
7972de962bdSlukem 						NULL, NULL );
7982de962bdSlukem 			}
7992de962bdSlukem 		}
8002de962bdSlukem 
8012de962bdSlukem 		if ( lc->lconn_ber != NULL ) {
8022de962bdSlukem 			ber_free( lc->lconn_ber, 1 );
8032de962bdSlukem 		}
8042de962bdSlukem 
8052de962bdSlukem 		ldap_int_sasl_close( ld, lc );
8064e6df137Slukem #ifdef HAVE_GSSAPI
8074e6df137Slukem 		ldap_int_gssapi_close( ld, lc );
8084e6df137Slukem #endif
8092de962bdSlukem 
8102de962bdSlukem 		ldap_free_urllist( lc->lconn_server );
8112de962bdSlukem 
8122de962bdSlukem 		/* FIXME: is this at all possible?
8132de962bdSlukem 		 * ldap_ld_free() in unbind.c calls ldap_free_connection()
8142de962bdSlukem 		 * with force == 1 __after__ explicitly calling
815*549b59edSchristos 		 * ldap_tavl_free on ld->ld_requests */
8162de962bdSlukem 		if ( force ) {
817*549b59edSchristos 			ldap_tavl_free( ld->ld_requests, ldap_do_free_request );
818*549b59edSchristos 			ld->ld_requests = NULL;
8192de962bdSlukem 		}
8202de962bdSlukem 
8212de962bdSlukem 		if ( lc->lconn_sb != ld->ld_sb ) {
8222de962bdSlukem 			ber_sockbuf_free( lc->lconn_sb );
823bb30016cSlukem 		} else {
824bb30016cSlukem 			ber_int_sb_close( lc->lconn_sb );
8252de962bdSlukem 		}
8262de962bdSlukem 
8272de962bdSlukem 		if ( lc->lconn_rebind_queue != NULL) {
8282de962bdSlukem 			int i;
8292de962bdSlukem 			for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
8302de962bdSlukem 				LDAP_VFREE( lc->lconn_rebind_queue[i] );
8312de962bdSlukem 			}
8322de962bdSlukem 			LDAP_FREE( lc->lconn_rebind_queue );
8332de962bdSlukem 		}
8342de962bdSlukem 
8352de962bdSlukem 		LDAP_FREE( lc );
8362de962bdSlukem 
837*549b59edSchristos 		Debug0( LDAP_DEBUG_TRACE,
838*549b59edSchristos 			"ldap_free_connection: actually freed\n" );
8392de962bdSlukem 
8402de962bdSlukem 	} else {
8412de962bdSlukem 		lc->lconn_lastused = time( NULL );
842*549b59edSchristos 		Debug1( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
843*549b59edSchristos 				lc->lconn_refcnt );
8442de962bdSlukem 	}
8452de962bdSlukem }
8462de962bdSlukem 
8472de962bdSlukem 
848d11b170bStron /* Protects self with ld_conn_mutex */
8492de962bdSlukem #ifdef LDAP_DEBUG
8502de962bdSlukem void
ldap_dump_connection(LDAP * ld,LDAPConn * lconns,int all)8512de962bdSlukem ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
8522de962bdSlukem {
8532de962bdSlukem 	LDAPConn	*lc;
8542de962bdSlukem    	char		timebuf[32];
8552de962bdSlukem 
856*549b59edSchristos 	Debug2( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "" );
857d11b170bStron 	LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
8582de962bdSlukem 	for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
8592de962bdSlukem 		if ( lc->lconn_server != NULL ) {
860*549b59edSchristos 			Debug3( LDAP_DEBUG_TRACE, "* host: %s  port: %d%s\n",
8612de962bdSlukem 				( lc->lconn_server->lud_host == NULL ) ? "(null)"
8622de962bdSlukem 				: lc->lconn_server->lud_host,
8632de962bdSlukem 				lc->lconn_server->lud_port, ( lc->lconn_sb ==
8642de962bdSlukem 				ld->ld_sb ) ? "  (default)" : "" );
8652de962bdSlukem 		}
866*549b59edSchristos 		if ( lc->lconn_sb != NULL ) {
867*549b59edSchristos 			char 			from[LDAP_IPADDRLEN];
868*549b59edSchristos 			struct berval 	frombv = BER_BVC(from);
869*549b59edSchristos 			ber_socket_t 	sb;
870*549b59edSchristos 			if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sb ) == 1 ) {
871*549b59edSchristos 				Sockaddr sin;
872*549b59edSchristos 				socklen_t len = sizeof( sin );
873*549b59edSchristos 				if ( getsockname( sb, (struct sockaddr *)&sin, &len ) == 0 ) {
874*549b59edSchristos 					ldap_pvt_sockaddrstr( &sin, &frombv );
875*549b59edSchristos 					Debug1( LDAP_DEBUG_TRACE, "* from: %s\n",
876*549b59edSchristos 						( from == NULL ) ? "(null)" : from );
877*549b59edSchristos 				}
878*549b59edSchristos 			}
879*549b59edSchristos 		}
880*549b59edSchristos 		Debug2( LDAP_DEBUG_TRACE, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
8812de962bdSlukem 			( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
8822de962bdSlukem 				? "NeedSocket" :
8832de962bdSlukem 				( lc->lconn_status == LDAP_CONNST_CONNECTING )
884*549b59edSchristos 					? "Connecting" : "Connected" );
885*549b59edSchristos 		Debug2( LDAP_DEBUG_TRACE, "  last used: %s%s\n",
8862de962bdSlukem 			ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
887*549b59edSchristos 			lc->lconn_rebind_inprogress ? "  rebind in progress" : "" );
8882de962bdSlukem 		if ( lc->lconn_rebind_inprogress ) {
8892de962bdSlukem 			if ( lc->lconn_rebind_queue != NULL) {
8902de962bdSlukem 				int	i;
8912de962bdSlukem 
8922de962bdSlukem 				for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
8932de962bdSlukem 					int	j;
8942de962bdSlukem 					for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) {
895*549b59edSchristos 						Debug3( LDAP_DEBUG_TRACE, "    queue %d entry %d - %s\n",
8962de962bdSlukem 							i, j, lc->lconn_rebind_queue[i][j] );
8972de962bdSlukem 					}
8982de962bdSlukem 				}
8992de962bdSlukem 			} else {
900*549b59edSchristos 				Debug0( LDAP_DEBUG_TRACE, "    queue is empty\n" );
9012de962bdSlukem 			}
9022de962bdSlukem 		}
903*549b59edSchristos 		Debug0( LDAP_DEBUG_TRACE, "\n" );
9042de962bdSlukem 		if ( !all ) {
9052de962bdSlukem 			break;
9062de962bdSlukem 		}
9072de962bdSlukem 	}
908d11b170bStron 	LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
9092de962bdSlukem }
9102de962bdSlukem 
9112de962bdSlukem 
912d11b170bStron /* protected by req_mutex and res_mutex */
9132de962bdSlukem void
ldap_dump_requests_and_responses(LDAP * ld)9142de962bdSlukem ldap_dump_requests_and_responses( LDAP *ld )
9152de962bdSlukem {
9162de962bdSlukem 	LDAPMessage	*lm, *l;
917*549b59edSchristos 	TAvlnode *node;
9182de962bdSlukem 	int		i;
9192de962bdSlukem 
920*549b59edSchristos 	Debug1( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n",
921*549b59edSchristos 		(void *)ld );
922*549b59edSchristos 	node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_LEFT );
923*549b59edSchristos 	if ( node == NULL ) {
924*549b59edSchristos 		Debug0( LDAP_DEBUG_TRACE, "   Empty\n" );
9252de962bdSlukem 	}
926*549b59edSchristos 	for ( i = 0 ; node != NULL; i++, node = ldap_tavl_next( node, TAVL_DIR_RIGHT ) ) {
927*549b59edSchristos 		LDAPRequest	*lr = node->avl_data;
928*549b59edSchristos 
929*549b59edSchristos 		Debug3( LDAP_DEBUG_TRACE, " * msgid %d,  origid %d, status %s\n",
9302de962bdSlukem 			lr->lr_msgid, lr->lr_origid,
9312de962bdSlukem 			( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
9322de962bdSlukem 			( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
9332de962bdSlukem 			( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
9342de962bdSlukem 			( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" :
9352de962bdSlukem 			( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted"
9362de962bdSlukem 				: "InvalidStatus" );
937*549b59edSchristos 		Debug2( LDAP_DEBUG_TRACE, "   outstanding referrals %d, parent count %d\n",
938*549b59edSchristos 			lr->lr_outrefcnt, lr->lr_parentcnt );
9392de962bdSlukem 	}
940*549b59edSchristos 	Debug3( LDAP_DEBUG_TRACE, "  ld %p request count %d (abandoned %lu)\n",
9412de962bdSlukem 		(void *)ld, i, ld->ld_nabandoned );
942*549b59edSchristos 	Debug1( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld );
9432de962bdSlukem 	if ( ( lm = ld->ld_responses ) == NULL ) {
944*549b59edSchristos 		Debug0( LDAP_DEBUG_TRACE, "   Empty\n" );
9452de962bdSlukem 	}
9462de962bdSlukem 	for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) {
947*549b59edSchristos 		Debug2( LDAP_DEBUG_TRACE, " * msgid %d,  type %lu\n",
948*549b59edSchristos 		    lm->lm_msgid, (unsigned long)lm->lm_msgtype );
9492de962bdSlukem 		if ( lm->lm_chain != NULL ) {
950*549b59edSchristos 			Debug0( LDAP_DEBUG_TRACE, "   chained responses:\n" );
9512de962bdSlukem 			for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) {
952*549b59edSchristos 				Debug2( LDAP_DEBUG_TRACE,
9532de962bdSlukem 					"  * msgid %d,  type %lu\n",
9542de962bdSlukem 					l->lm_msgid,
955*549b59edSchristos 					(unsigned long)l->lm_msgtype );
9562de962bdSlukem 			}
9572de962bdSlukem 		}
9582de962bdSlukem 	}
959*549b59edSchristos 	Debug2( LDAP_DEBUG_TRACE, "  ld %p response count %d\n", (void *)ld, i );
9602de962bdSlukem }
9612de962bdSlukem #endif /* LDAP_DEBUG */
9622de962bdSlukem 
963d11b170bStron /* protected by req_mutex */
964*549b59edSchristos void
ldap_do_free_request(void * arg)965*549b59edSchristos ldap_do_free_request( void *arg )
9662de962bdSlukem {
967*549b59edSchristos 	LDAPRequest *lr = arg;
968*549b59edSchristos 
969*549b59edSchristos 	Debug3( LDAP_DEBUG_TRACE, "ldap_do_free_request: "
970*549b59edSchristos 			"asked to free lr %p msgid %d refcnt %d\n",
971*549b59edSchristos 			lr, lr->lr_msgid, lr->lr_refcnt );
9722de962bdSlukem 	/* if lr_refcnt > 0, the request has been looked up
9732de962bdSlukem 	 * by ldap_find_request_by_msgid(); if in the meanwhile
9742de962bdSlukem 	 * the request is free()'d by someone else, just decrease
975*549b59edSchristos 	 * the reference count; later on, it will be freed. */
9762de962bdSlukem 	if ( lr->lr_refcnt > 0 ) {
977*549b59edSchristos 		assert( lr->lr_refcnt == 1 );
9782de962bdSlukem 		lr->lr_refcnt = -lr->lr_refcnt;
9792de962bdSlukem 		return;
9802de962bdSlukem 	}
9812de962bdSlukem 
9822de962bdSlukem 	if ( lr->lr_ber != NULL ) {
9832de962bdSlukem 		ber_free( lr->lr_ber, 1 );
9842de962bdSlukem 		lr->lr_ber = NULL;
9852de962bdSlukem 	}
9862de962bdSlukem 
9872de962bdSlukem 	if ( lr->lr_res_error != NULL ) {
9882de962bdSlukem 		LDAP_FREE( lr->lr_res_error );
9892de962bdSlukem 		lr->lr_res_error = NULL;
9902de962bdSlukem 	}
9912de962bdSlukem 
9922de962bdSlukem 	if ( lr->lr_res_matched != NULL ) {
9932de962bdSlukem 		LDAP_FREE( lr->lr_res_matched );
9942de962bdSlukem 		lr->lr_res_matched = NULL;
9952de962bdSlukem 	}
9962de962bdSlukem 
9972de962bdSlukem 	LDAP_FREE( lr );
9982de962bdSlukem }
9992de962bdSlukem 
1000*549b59edSchristos int
ldap_req_cmp(const void * l,const void * r)1001*549b59edSchristos ldap_req_cmp( const void *l, const void *r )
1002*549b59edSchristos {
1003*549b59edSchristos 	const LDAPRequest *left = l, *right = r;
1004*549b59edSchristos 	return left->lr_msgid - right->lr_msgid;
1005*549b59edSchristos }
1006*549b59edSchristos 
1007*549b59edSchristos /* protected by req_mutex */
1008*549b59edSchristos static void
ldap_free_request_int(LDAP * ld,LDAPRequest * lr)1009*549b59edSchristos ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
1010*549b59edSchristos {
1011*549b59edSchristos 	LDAPRequest *removed;
1012*549b59edSchristos 
1013*549b59edSchristos 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1014*549b59edSchristos 	removed = ldap_tavl_delete( &ld->ld_requests, lr, ldap_req_cmp );
1015*549b59edSchristos 	assert( !removed || removed == lr );
1016*549b59edSchristos 	Debug3( LDAP_DEBUG_TRACE, "ldap_free_request_int: "
1017*549b59edSchristos 			"lr %p msgid %d%s removed\n",
1018*549b59edSchristos 			lr, lr->lr_msgid, removed ? "" : " not" );
1019*549b59edSchristos 
1020*549b59edSchristos 	ldap_do_free_request( lr );
1021*549b59edSchristos }
1022*549b59edSchristos 
1023d11b170bStron /* protected by req_mutex */
10242de962bdSlukem void
ldap_free_request(LDAP * ld,LDAPRequest * lr)10252de962bdSlukem ldap_free_request( LDAP *ld, LDAPRequest *lr )
10262de962bdSlukem {
1027d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1028*549b59edSchristos 	Debug2( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
1029*549b59edSchristos 		lr->lr_origid, lr->lr_msgid );
10302de962bdSlukem 
10312de962bdSlukem 	/* free all referrals (child requests) */
10322de962bdSlukem 	while ( lr->lr_child ) {
10332de962bdSlukem 		ldap_free_request( ld, lr->lr_child );
10342de962bdSlukem 	}
10352de962bdSlukem 
10362de962bdSlukem 	if ( lr->lr_parent != NULL ) {
10372de962bdSlukem 		LDAPRequest     **lrp;
10382de962bdSlukem 
10392de962bdSlukem 		--lr->lr_parent->lr_outrefcnt;
10402de962bdSlukem 		for ( lrp = &lr->lr_parent->lr_child;
10412de962bdSlukem 			*lrp && *lrp != lr;
10422de962bdSlukem 			lrp = &(*lrp)->lr_refnext );
10432de962bdSlukem 
10442de962bdSlukem 		if ( *lrp == lr ) {
10452de962bdSlukem 			*lrp = lr->lr_refnext;
10462de962bdSlukem 		}
10472de962bdSlukem 	}
10482de962bdSlukem 	ldap_free_request_int( ld, lr );
10492de962bdSlukem }
10502de962bdSlukem 
10512de962bdSlukem /*
10522de962bdSlukem  * call first time with *cntp = -1
10532de962bdSlukem  * when returns *cntp == -1, no referrals are left
10542de962bdSlukem  *
10552de962bdSlukem  * NOTE: may replace *refsp, or shuffle the contents
10562de962bdSlukem  * of the original array.
10572de962bdSlukem  */
ldap_int_nextref(LDAP * ld,char *** refsp,int * cntp,void * params)10582de962bdSlukem static int ldap_int_nextref(
10592de962bdSlukem 	LDAP			*ld,
10602de962bdSlukem 	char			***refsp,
10612de962bdSlukem 	int			*cntp,
10622de962bdSlukem 	void			*params )
10632de962bdSlukem {
10642de962bdSlukem 	assert( refsp != NULL );
10652de962bdSlukem 	assert( *refsp != NULL );
10662de962bdSlukem 	assert( cntp != NULL );
10672de962bdSlukem 
10682de962bdSlukem 	if ( *cntp < -1 ) {
10692de962bdSlukem 		*cntp = -1;
10702de962bdSlukem 		return -1;
10712de962bdSlukem 	}
10722de962bdSlukem 
10732de962bdSlukem 	(*cntp)++;
10742de962bdSlukem 
10752de962bdSlukem 	if ( (*refsp)[ *cntp ] == NULL ) {
10762de962bdSlukem 		*cntp = -1;
10772de962bdSlukem 	}
10782de962bdSlukem 
10792de962bdSlukem 	return 0;
10802de962bdSlukem }
10812de962bdSlukem 
10822de962bdSlukem /*
10832de962bdSlukem  * Chase v3 referrals
10842de962bdSlukem  *
10852de962bdSlukem  * Parameters:
10862de962bdSlukem  *  (IN) ld = LDAP connection handle
10872de962bdSlukem  *  (IN) lr = LDAP Request structure
10882de962bdSlukem  *  (IN) refs = array of pointers to referral strings that we will chase
10892de962bdSlukem  *              The array will be free'd by this function when no longer needed
10902de962bdSlukem  *  (IN) sref != 0 if following search reference
10912de962bdSlukem  *  (OUT) errstrp = Place to return a string of referrals which could not be followed
1092*549b59edSchristos  *  (OUT) hadrefp = 1 if successfully followed referral
10932de962bdSlukem  *
10942de962bdSlukem  * Return value - number of referrals followed
1095d11b170bStron  *
1096d11b170bStron  * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
10972de962bdSlukem  */
10982de962bdSlukem int
ldap_chase_v3referrals(LDAP * ld,LDAPRequest * lr,char ** refs,int sref,char ** errstrp,int * hadrefp)10992de962bdSlukem ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
11002de962bdSlukem {
11012de962bdSlukem 	char		*unfollowed;
11022de962bdSlukem 	int		 unfollowedcnt = 0;
11032de962bdSlukem 	LDAPRequest	*origreq;
11042de962bdSlukem 	LDAPURLDesc	*srv = NULL;
11052de962bdSlukem 	BerElement	*ber;
11062de962bdSlukem 	char		**refarray = NULL;
11072de962bdSlukem 	LDAPConn	*lc;
11082de962bdSlukem 	int			 rc, count, i, j, id;
11092de962bdSlukem 	LDAPreqinfo  rinfo;
1110d11b170bStron 	LDAP_NEXTREF_PROC	*nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;
1111d11b170bStron 
1112d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1113d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
1114d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1115*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n" );
11162de962bdSlukem 
11172de962bdSlukem 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
11182de962bdSlukem 	*hadrefp = 0;
11192de962bdSlukem 
11202de962bdSlukem 	unfollowed = NULL;
11212de962bdSlukem 	rc = count = 0;
11222de962bdSlukem 
11232de962bdSlukem 	/* If no referrals in array, return */
11242de962bdSlukem 	if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
11252de962bdSlukem 		rc = 0;
11262de962bdSlukem 		goto done;
11272de962bdSlukem 	}
11282de962bdSlukem 
11292de962bdSlukem 	/* Check for hop limit exceeded */
11302de962bdSlukem 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
1131*549b59edSchristos 		Debug1( LDAP_DEBUG_ANY,
1132*549b59edSchristos 		    "more than %d referral hops (dropping)\n", ld->ld_refhoplimit );
11332de962bdSlukem 		ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
11342de962bdSlukem 		rc = -1;
11352de962bdSlukem 		goto done;
11362de962bdSlukem 	}
11372de962bdSlukem 
11382de962bdSlukem 	/* find original request */
11392de962bdSlukem 	for ( origreq = lr;
11402de962bdSlukem 		origreq->lr_parent != NULL;
11412de962bdSlukem 		origreq = origreq->lr_parent )
11422de962bdSlukem 	{
11432de962bdSlukem 		/* empty */ ;
11442de962bdSlukem 	}
11452de962bdSlukem 
11462de962bdSlukem 	refarray = refs;
11472de962bdSlukem 	refs = NULL;
11482de962bdSlukem 
11492de962bdSlukem 	/* parse out & follow referrals */
1150d11b170bStron 	/* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
11512de962bdSlukem 	i = -1;
1152d11b170bStron 	for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
11532de962bdSlukem 			i != -1;
1154d11b170bStron 			nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
11552de962bdSlukem 	{
11562de962bdSlukem 
11572de962bdSlukem 		/* Parse the referral URL */
11582de962bdSlukem 		rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
11592de962bdSlukem 		if ( rc != LDAP_URL_SUCCESS ) {
11602de962bdSlukem 			/* ldap_url_parse_ext() returns LDAP_URL_* errors
11612de962bdSlukem 			 * which do not map on API errors */
11622de962bdSlukem 			ld->ld_errno = LDAP_PARAM_ERROR;
11632de962bdSlukem 			rc = -1;
11642de962bdSlukem 			goto done;
11652de962bdSlukem 		}
11662de962bdSlukem 
11672de962bdSlukem 		if( srv->lud_crit_exts ) {
11684e6df137Slukem 			int ok = 0;
11694e6df137Slukem #ifdef HAVE_TLS
11704e6df137Slukem 			/* If StartTLS is the only critical ext, OK. */
11714e6df137Slukem 			if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
11724e6df137Slukem 				ok = 1;
11734e6df137Slukem #endif
11744e6df137Slukem 			if ( !ok ) {
11754e6df137Slukem 				/* we do not support any other extensions */
11762de962bdSlukem 				ld->ld_errno = LDAP_NOT_SUPPORTED;
11772de962bdSlukem 				rc = -1;
11782de962bdSlukem 				goto done;
11792de962bdSlukem 			}
11804e6df137Slukem 		}
11812de962bdSlukem 
11822de962bdSlukem 		/* check connection for re-bind in progress */
11832de962bdSlukem 		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
11842de962bdSlukem 			/* See if we've already requested this DN with this conn */
11852de962bdSlukem 			LDAPRequest *lp;
11862de962bdSlukem 			int looped = 0;
11874e6df137Slukem 			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
11882de962bdSlukem 			for ( lp = origreq; lp; ) {
11892de962bdSlukem 				if ( lp->lr_conn == lc
11902de962bdSlukem 					&& len == lp->lr_dn.bv_len
11912de962bdSlukem 					&& len
11922de962bdSlukem 					&& strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
11932de962bdSlukem 				{
11942de962bdSlukem 					looped = 1;
11952de962bdSlukem 					break;
11962de962bdSlukem 				}
11972de962bdSlukem 				if ( lp == origreq ) {
11982de962bdSlukem 					lp = lp->lr_child;
11992de962bdSlukem 				} else {
12002de962bdSlukem 					lp = lp->lr_refnext;
12012de962bdSlukem 				}
12022de962bdSlukem 			}
12032de962bdSlukem 			if ( looped ) {
12042de962bdSlukem 				ldap_free_urllist( srv );
12052de962bdSlukem 				srv = NULL;
12062de962bdSlukem 				ld->ld_errno = LDAP_CLIENT_LOOP;
12072de962bdSlukem 				rc = -1;
12082de962bdSlukem 				continue;
12092de962bdSlukem 			}
12102de962bdSlukem 
12112de962bdSlukem 			if ( lc->lconn_rebind_inprogress ) {
12122de962bdSlukem 				/* We are already chasing a referral or search reference and a
12132de962bdSlukem 				 * bind on that connection is in progress.  We must queue
12142de962bdSlukem 				 * referrals on that connection, so we don't get a request
12152de962bdSlukem 				 * going out before the bind operation completes. This happens
12162de962bdSlukem 				 * if two search references come in one behind the other
12172de962bdSlukem 				 * for the same server with different contexts.
12182de962bdSlukem 				 */
1219*549b59edSchristos 				Debug1( LDAP_DEBUG_TRACE,
12202de962bdSlukem 					"ldap_chase_v3referrals: queue referral \"%s\"\n",
1221*549b59edSchristos 					refarray[i] );
12222de962bdSlukem 				if( lc->lconn_rebind_queue == NULL ) {
12232de962bdSlukem 					/* Create a referral list */
12242de962bdSlukem 					lc->lconn_rebind_queue =
12252de962bdSlukem 						(char ***) LDAP_MALLOC( sizeof(void *) * 2);
12262de962bdSlukem 
12272de962bdSlukem 					if( lc->lconn_rebind_queue == NULL) {
12282de962bdSlukem 						ld->ld_errno = LDAP_NO_MEMORY;
12292de962bdSlukem 						rc = -1;
12302de962bdSlukem 						goto done;
12312de962bdSlukem 					}
12322de962bdSlukem 
12332de962bdSlukem 					lc->lconn_rebind_queue[0] = refarray;
12342de962bdSlukem 					lc->lconn_rebind_queue[1] = NULL;
12352de962bdSlukem 					refarray = NULL;
12362de962bdSlukem 
12372de962bdSlukem 				} else {
12382de962bdSlukem 					/* Count how many referral arrays we already have */
12392de962bdSlukem 					for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
12402de962bdSlukem 						/* empty */;
12412de962bdSlukem 					}
12422de962bdSlukem 
12432de962bdSlukem 					/* Add the new referral to the list */
12442de962bdSlukem 					lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
12452de962bdSlukem 						lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
12462de962bdSlukem 
12472de962bdSlukem 					if( lc->lconn_rebind_queue == NULL ) {
12482de962bdSlukem 						ld->ld_errno = LDAP_NO_MEMORY;
12492de962bdSlukem 						rc = -1;
12502de962bdSlukem 						goto done;
12512de962bdSlukem 					}
12522de962bdSlukem 					lc->lconn_rebind_queue[j] = refarray;
12532de962bdSlukem 					lc->lconn_rebind_queue[j+1] = NULL;
12542de962bdSlukem 					refarray = NULL;
12552de962bdSlukem 				}
12562de962bdSlukem 
12572de962bdSlukem 				/* We have queued the referral/reference, now just return */
12582de962bdSlukem 				rc = 0;
12592de962bdSlukem 				*hadrefp = 1;
12602de962bdSlukem 				count = 1; /* Pretend we already followed referral */
12612de962bdSlukem 				goto done;
12622de962bdSlukem 			}
12632de962bdSlukem 		}
12642de962bdSlukem 		/* Re-encode the request with the new starting point of the search.
12652de962bdSlukem 		 * Note: In the future we also need to replace the filter if one
12662de962bdSlukem 		 * was provided with the search reference
12672de962bdSlukem 		 */
12682de962bdSlukem 
12692de962bdSlukem 		/* For references we don't want old dn if new dn empty */
12702de962bdSlukem 		if ( sref && srv->lud_dn == NULL ) {
12712de962bdSlukem 			srv->lud_dn = LDAP_STRDUP( "" );
12722de962bdSlukem 		}
12732de962bdSlukem 
12742de962bdSlukem 		LDAP_NEXT_MSGID( ld, id );
12752de962bdSlukem 		ber = re_encode_request( ld, origreq->lr_ber, id,
12762de962bdSlukem 			sref, srv, &rinfo.ri_request );
12772de962bdSlukem 
12782de962bdSlukem 		if( ber == NULL ) {
12792de962bdSlukem 			ld->ld_errno = LDAP_ENCODING_ERROR;
12802de962bdSlukem 			rc = -1;
12812de962bdSlukem 			goto done;
12822de962bdSlukem 		}
12832de962bdSlukem 
1284*549b59edSchristos 		Debug2( LDAP_DEBUG_TRACE,
12852de962bdSlukem 			"ldap_chase_v3referral: msgid %d, url \"%s\"\n",
1286*549b59edSchristos 			lr->lr_msgid, refarray[i] );
12872de962bdSlukem 
12882de962bdSlukem 		/* Send the new request to the server - may require a bind */
12892de962bdSlukem 		rinfo.ri_msgid = origreq->lr_origid;
12902de962bdSlukem 		rinfo.ri_url = refarray[i];
12912de962bdSlukem 		rc = ldap_send_server_request( ld, ber, id,
1292d11b170bStron 			origreq, &srv, NULL, &rinfo, 0, 1 );
12932de962bdSlukem 		if ( rc < 0 ) {
12942de962bdSlukem 			/* Failure, try next referral in the list */
1295*549b59edSchristos 			Debug3( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n",
12962de962bdSlukem 				refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
12972de962bdSlukem 			unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
12982de962bdSlukem 			ldap_free_urllist( srv );
12992de962bdSlukem 			srv = NULL;
13002de962bdSlukem 			ld->ld_errno = LDAP_REFERRAL;
13012de962bdSlukem 		} else {
13022de962bdSlukem 			/* Success, no need to try this referral list further */
13032de962bdSlukem 			rc = 0;
13042de962bdSlukem 			++count;
13052de962bdSlukem 			*hadrefp = 1;
13062de962bdSlukem 
13072de962bdSlukem 			/* check if there is a queue of referrals that came in during bind */
13082de962bdSlukem 			if ( lc == NULL) {
13092de962bdSlukem 				lc = find_connection( ld, srv, 1 );
13102de962bdSlukem 				if ( lc == NULL ) {
13112de962bdSlukem 					ld->ld_errno = LDAP_OPERATIONS_ERROR;
13122de962bdSlukem 					rc = -1;
1313d11b170bStron 					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
13142de962bdSlukem 					goto done;
13152de962bdSlukem 				}
13162de962bdSlukem 			}
13172de962bdSlukem 
13182de962bdSlukem 			if ( lc->lconn_rebind_queue != NULL ) {
13192de962bdSlukem 				/* Release resources of previous list */
13202de962bdSlukem 				LDAP_VFREE( refarray );
13212de962bdSlukem 				refarray = NULL;
13222de962bdSlukem 				ldap_free_urllist( srv );
13232de962bdSlukem 				srv = NULL;
13242de962bdSlukem 
13252de962bdSlukem 				/* Pull entries off end of queue so list always null terminated */
13262de962bdSlukem 				for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
13272de962bdSlukem 					;
13282de962bdSlukem 				refarray = lc->lconn_rebind_queue[j - 1];
13292de962bdSlukem 				lc->lconn_rebind_queue[j-1] = NULL;
13302de962bdSlukem 				/* we pulled off last entry from queue, free queue */
13312de962bdSlukem 				if ( j == 1 ) {
13322de962bdSlukem 					LDAP_FREE( lc->lconn_rebind_queue );
13332de962bdSlukem 					lc->lconn_rebind_queue = NULL;
13342de962bdSlukem 				}
13352de962bdSlukem 				/* restart the loop the with new referral list */
13362de962bdSlukem 				i = -1;
13372de962bdSlukem 				continue;
13382de962bdSlukem 			}
13392de962bdSlukem 			break; /* referral followed, break out of for loop */
13402de962bdSlukem 		}
13412de962bdSlukem 	} /* end for loop */
13422de962bdSlukem done:
13432de962bdSlukem 	LDAP_VFREE( refarray );
13442de962bdSlukem 	ldap_free_urllist( srv );
13452de962bdSlukem 	LDAP_FREE( *errstrp );
13462de962bdSlukem 
13472de962bdSlukem 	if( rc == 0 ) {
13482de962bdSlukem 		*errstrp = NULL;
13492de962bdSlukem 		LDAP_FREE( unfollowed );
13502de962bdSlukem 		return count;
13512de962bdSlukem 	} else {
13522de962bdSlukem 		*errstrp = unfollowed;
13532de962bdSlukem 		return rc;
13542de962bdSlukem 	}
13552de962bdSlukem }
13562de962bdSlukem 
13572de962bdSlukem /*
13582de962bdSlukem  * XXX merging of errors in this routine needs to be improved
1359d11b170bStron  * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
13602de962bdSlukem  */
13612de962bdSlukem int
ldap_chase_referrals(LDAP * ld,LDAPRequest * lr,char ** errstrp,int sref,int * hadrefp)13622de962bdSlukem ldap_chase_referrals( LDAP *ld,
13632de962bdSlukem 	LDAPRequest *lr,
13642de962bdSlukem 	char **errstrp,
13652de962bdSlukem 	int sref,
13662de962bdSlukem 	int *hadrefp )
13672de962bdSlukem {
13682de962bdSlukem 	int		rc, count, id;
13692de962bdSlukem 	unsigned	len;
13702de962bdSlukem 	char		*p, *ref, *unfollowed;
13712de962bdSlukem 	LDAPRequest	*origreq;
13722de962bdSlukem 	LDAPURLDesc	*srv;
13732de962bdSlukem 	BerElement	*ber;
13742de962bdSlukem 	LDAPreqinfo  rinfo;
13752de962bdSlukem 	LDAPConn	*lc;
13762de962bdSlukem 
1377d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1378d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
1379d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1380*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n" );
13812de962bdSlukem 
13822de962bdSlukem 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
13832de962bdSlukem 	*hadrefp = 0;
13842de962bdSlukem 
13852de962bdSlukem 	if ( *errstrp == NULL ) {
13862de962bdSlukem 		return( 0 );
13872de962bdSlukem 	}
13882de962bdSlukem 
13892de962bdSlukem 	len = strlen( *errstrp );
13902de962bdSlukem 	for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
13912de962bdSlukem 		if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
13922de962bdSlukem 			*p = '\0';
13932de962bdSlukem 			p += LDAP_REF_STR_LEN;
13942de962bdSlukem 			break;
13952de962bdSlukem 		}
13962de962bdSlukem 	}
13972de962bdSlukem 
13982de962bdSlukem 	if ( len < LDAP_REF_STR_LEN ) {
13992de962bdSlukem 		return( 0 );
14002de962bdSlukem 	}
14012de962bdSlukem 
14022de962bdSlukem 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
1403*549b59edSchristos 		Debug1( LDAP_DEBUG_ANY,
14042de962bdSlukem 		    "more than %d referral hops (dropping)\n",
1405*549b59edSchristos 		    ld->ld_refhoplimit );
14062de962bdSlukem 		    /* XXX report as error in ld->ld_errno? */
14072de962bdSlukem 		    return( 0 );
14082de962bdSlukem 	}
14092de962bdSlukem 
14102de962bdSlukem 	/* find original request */
14112de962bdSlukem 	for ( origreq = lr; origreq->lr_parent != NULL;
14122de962bdSlukem 	     origreq = origreq->lr_parent ) {
14132de962bdSlukem 		/* empty */;
14142de962bdSlukem 	}
14152de962bdSlukem 
14162de962bdSlukem 	unfollowed = NULL;
14172de962bdSlukem 	rc = count = 0;
14182de962bdSlukem 
14192de962bdSlukem 	/* parse out & follow referrals */
14202de962bdSlukem 	for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
14212de962bdSlukem 		p = strchr( ref, '\n' );
14222de962bdSlukem 		if ( p != NULL ) {
14232de962bdSlukem 			*p++ = '\0';
14242de962bdSlukem 		}
14252de962bdSlukem 
14262de962bdSlukem 		rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
14272de962bdSlukem 		if ( rc != LDAP_URL_SUCCESS ) {
1428*549b59edSchristos 			Debug2( LDAP_DEBUG_TRACE,
14292de962bdSlukem 				"ignoring %s referral <%s>\n",
1430*549b59edSchristos 				ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect" );
14312de962bdSlukem 			rc = ldap_append_referral( ld, &unfollowed, ref );
14322de962bdSlukem 			*hadrefp = 1;
14332de962bdSlukem 			continue;
14342de962bdSlukem 		}
14352de962bdSlukem 
1436*549b59edSchristos 		Debug1( LDAP_DEBUG_TRACE,
1437*549b59edSchristos 		    "chasing LDAP referral: <%s>\n", ref );
14382de962bdSlukem 
14392de962bdSlukem 		*hadrefp = 1;
14402de962bdSlukem 
14412de962bdSlukem 		/* See if we've already been here */
14422de962bdSlukem 		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
14432de962bdSlukem 			LDAPRequest *lp;
14442de962bdSlukem 			int looped = 0;
14454e6df137Slukem 			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
14462de962bdSlukem 			for ( lp = lr; lp; lp = lp->lr_parent ) {
14472de962bdSlukem 				if ( lp->lr_conn == lc
14482de962bdSlukem 					&& len == lp->lr_dn.bv_len )
14492de962bdSlukem 				{
14502de962bdSlukem 					if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
14512de962bdSlukem 							continue;
14522de962bdSlukem 					looped = 1;
14532de962bdSlukem 					break;
14542de962bdSlukem 				}
14552de962bdSlukem 			}
14562de962bdSlukem 			if ( looped ) {
14572de962bdSlukem 				ldap_free_urllist( srv );
14582de962bdSlukem 				ld->ld_errno = LDAP_CLIENT_LOOP;
14592de962bdSlukem 				rc = -1;
14602de962bdSlukem 				continue;
14612de962bdSlukem 			}
14622de962bdSlukem 		}
14632de962bdSlukem 
14642de962bdSlukem 		LDAP_NEXT_MSGID( ld, id );
14652de962bdSlukem 		ber = re_encode_request( ld, origreq->lr_ber,
14662de962bdSlukem 		    id, sref, srv, &rinfo.ri_request );
14672de962bdSlukem 
14682de962bdSlukem 		if ( ber == NULL ) {
1469376af7d7Schristos 			ldap_free_urllist( srv );
14702de962bdSlukem 			return -1 ;
14712de962bdSlukem 		}
14722de962bdSlukem 
14732de962bdSlukem 		/* copy the complete referral for rebind process */
14742de962bdSlukem 		rinfo.ri_url = LDAP_STRDUP( ref );
14752de962bdSlukem 
14762de962bdSlukem 		rinfo.ri_msgid = origreq->lr_origid;
14772de962bdSlukem 
14782de962bdSlukem 		rc = ldap_send_server_request( ld, ber, id,
1479d11b170bStron 			lr, &srv, NULL, &rinfo, 0, 1 );
14802de962bdSlukem 		LDAP_FREE( rinfo.ri_url );
14812de962bdSlukem 
14822de962bdSlukem 		if( rc >= 0 ) {
14832de962bdSlukem 			++count;
14842de962bdSlukem 		} else {
1485*549b59edSchristos 			Debug3( LDAP_DEBUG_ANY,
14862de962bdSlukem 				"Unable to chase referral \"%s\" (%d: %s)\n",
14872de962bdSlukem 				ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
14882de962bdSlukem 			rc = ldap_append_referral( ld, &unfollowed, ref );
14892de962bdSlukem 		}
14902de962bdSlukem 
14912de962bdSlukem 		ldap_free_urllist(srv);
14922de962bdSlukem 	}
14932de962bdSlukem 
14942de962bdSlukem 	LDAP_FREE( *errstrp );
14952de962bdSlukem 	*errstrp = unfollowed;
14962de962bdSlukem 
14972de962bdSlukem 	return(( rc == 0 ) ? count : rc );
14982de962bdSlukem }
14992de962bdSlukem 
15002de962bdSlukem 
15012de962bdSlukem int
ldap_append_referral(LDAP * ld,char ** referralsp,char * s)15022de962bdSlukem ldap_append_referral( LDAP *ld, char **referralsp, char *s )
15032de962bdSlukem {
15042de962bdSlukem 	int	first;
15052de962bdSlukem 
15062de962bdSlukem 	if ( *referralsp == NULL ) {
15072de962bdSlukem 		first = 1;
15082de962bdSlukem 		*referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
15092de962bdSlukem 		    + 1 );
15102de962bdSlukem 	} else {
15112de962bdSlukem 		first = 0;
15122de962bdSlukem 		*referralsp = (char *)LDAP_REALLOC( *referralsp,
15132de962bdSlukem 		    strlen( *referralsp ) + strlen( s ) + 2 );
15142de962bdSlukem 	}
15152de962bdSlukem 
15162de962bdSlukem 	if ( *referralsp == NULL ) {
15172de962bdSlukem 		ld->ld_errno = LDAP_NO_MEMORY;
15182de962bdSlukem 		return( -1 );
15192de962bdSlukem 	}
15202de962bdSlukem 
15212de962bdSlukem 	if ( first ) {
15222de962bdSlukem 		strcpy( *referralsp, LDAP_REF_STR );
15232de962bdSlukem 	} else {
15242de962bdSlukem 		strcat( *referralsp, "\n" );
15252de962bdSlukem 	}
15262de962bdSlukem 	strcat( *referralsp, s );
15272de962bdSlukem 
15282de962bdSlukem 	return( 0 );
15292de962bdSlukem }
15302de962bdSlukem 
15312de962bdSlukem 
15322de962bdSlukem 
15332de962bdSlukem static BerElement *
re_encode_request(LDAP * ld,BerElement * origber,ber_int_t msgid,int sref,LDAPURLDesc * srv,int * type)15342de962bdSlukem re_encode_request( LDAP *ld,
15352de962bdSlukem 	BerElement *origber,
15362de962bdSlukem 	ber_int_t msgid,
15372de962bdSlukem 	int sref,
15382de962bdSlukem 	LDAPURLDesc *srv,
15392de962bdSlukem 	int *type )
15402de962bdSlukem {
15412de962bdSlukem 	/*
15422de962bdSlukem 	 * XXX this routine knows way too much about how the lber library works!
15432de962bdSlukem 	 */
15442de962bdSlukem 	ber_int_t	along;
15452de962bdSlukem 	ber_tag_t	tag;
15462de962bdSlukem 	ber_tag_t	rtag;
15472de962bdSlukem 	ber_int_t	ver;
15482de962bdSlukem 	ber_int_t	scope;
15492de962bdSlukem 	int		rc;
15502de962bdSlukem 	BerElement	tmpber, *ber;
15512de962bdSlukem 	struct berval		dn;
15522de962bdSlukem 
1553*549b59edSchristos 	Debug2( LDAP_DEBUG_TRACE,
15542de962bdSlukem 	    "re_encode_request: new msgid %ld, new dn <%s>\n",
15552de962bdSlukem 	    (long) msgid,
1556*549b59edSchristos 		( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn );
15572de962bdSlukem 
15582de962bdSlukem 	tmpber = *origber;
15592de962bdSlukem 
15602de962bdSlukem 	/*
15612de962bdSlukem 	 * all LDAP requests are sequences that start with a message id.
15622de962bdSlukem 	 * For all except delete, this is followed by a sequence that is
15632de962bdSlukem 	 * tagged with the operation code.  For delete, the provided DN
15642de962bdSlukem 	 * is not wrapped by a sequence.
15652de962bdSlukem 	 */
15662de962bdSlukem 	rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
15672de962bdSlukem 
15682de962bdSlukem 	if ( rtag == LBER_ERROR ) {
15692de962bdSlukem 		ld->ld_errno = LDAP_DECODING_ERROR;
15702de962bdSlukem 		return( NULL );
15712de962bdSlukem 	}
15722de962bdSlukem 
15732de962bdSlukem 	assert( tag != 0);
15742de962bdSlukem 	if ( tag == LDAP_REQ_BIND ) {
15752de962bdSlukem 		/* bind requests have a version number before the DN & other stuff */
15762de962bdSlukem 		rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn );
15772de962bdSlukem 
15782de962bdSlukem 	} else if ( tag == LDAP_REQ_DELETE ) {
15792de962bdSlukem 		/* delete requests don't have a DN wrapping sequence */
15802de962bdSlukem 		rtag = ber_scanf( &tmpber, "m", &dn );
15812de962bdSlukem 
15822de962bdSlukem 	} else if ( tag == LDAP_REQ_SEARCH ) {
15832de962bdSlukem 		/* search requests need to be re-scope-ed */
15842de962bdSlukem 		rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope );
15852de962bdSlukem 
15862de962bdSlukem 		if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
15872de962bdSlukem 			/* use the scope provided in reference */
15882de962bdSlukem 			scope = srv->lud_scope;
15892de962bdSlukem 
15902de962bdSlukem 		} else if ( sref ) {
15912de962bdSlukem 			/* use scope implied by previous operation
15922de962bdSlukem 			 *   base -> base
15932de962bdSlukem 			 *   one -> base
15942de962bdSlukem 			 *   subtree -> subtree
15952de962bdSlukem 			 *   subordinate -> subtree
15962de962bdSlukem 			 */
15972de962bdSlukem 			switch( scope ) {
15982de962bdSlukem 			default:
15992de962bdSlukem 			case LDAP_SCOPE_BASE:
16002de962bdSlukem 			case LDAP_SCOPE_ONELEVEL:
16012de962bdSlukem 				scope = LDAP_SCOPE_BASE;
16022de962bdSlukem 				break;
16032de962bdSlukem 			case LDAP_SCOPE_SUBTREE:
16042de962bdSlukem 			case LDAP_SCOPE_SUBORDINATE:
16052de962bdSlukem 				scope = LDAP_SCOPE_SUBTREE;
16062de962bdSlukem 				break;
16072de962bdSlukem 			}
16082de962bdSlukem 		}
16092de962bdSlukem 
16102de962bdSlukem 	} else {
16112de962bdSlukem 		rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn );
16122de962bdSlukem 	}
16132de962bdSlukem 
16142de962bdSlukem 	if( rtag == LBER_ERROR ) {
16152de962bdSlukem 		ld->ld_errno = LDAP_DECODING_ERROR;
16162de962bdSlukem 		return NULL;
16172de962bdSlukem 	}
16182de962bdSlukem 
16192de962bdSlukem 	/* restore character zero'd out by ber_scanf*/
16202de962bdSlukem 	dn.bv_val[dn.bv_len] = tmpber.ber_tag;
16212de962bdSlukem 
16222de962bdSlukem 	if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
16232de962bdSlukem 		return NULL;
16242de962bdSlukem 	}
16252de962bdSlukem 
16262de962bdSlukem 	if ( srv->lud_dn ) {
16272de962bdSlukem 		ber_str2bv( srv->lud_dn, 0, 0, &dn );
16282de962bdSlukem 	}
16292de962bdSlukem 
16302de962bdSlukem 	if ( tag == LDAP_REQ_BIND ) {
16312de962bdSlukem 		rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn );
16322de962bdSlukem 	} else if ( tag == LDAP_REQ_DELETE ) {
16332de962bdSlukem 		rc = ber_printf( ber, "{itON}", msgid, tag, &dn );
16342de962bdSlukem 	} else if ( tag == LDAP_REQ_SEARCH ) {
16352de962bdSlukem 		rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope );
16362de962bdSlukem 	} else {
16372de962bdSlukem 		rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn );
16382de962bdSlukem 	}
16392de962bdSlukem 
16402de962bdSlukem 	if ( rc == -1 ) {
16412de962bdSlukem 		ld->ld_errno = LDAP_ENCODING_ERROR;
16422de962bdSlukem 		ber_free( ber, 1 );
16432de962bdSlukem 		return NULL;
16442de962bdSlukem 	}
16452de962bdSlukem 
16462de962bdSlukem 	if ( tag != LDAP_REQ_DELETE && (
16472de962bdSlukem 		ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
16482de962bdSlukem 		!= ( tmpber.ber_end - tmpber.ber_ptr ) ||
16492de962bdSlukem 	    ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
16502de962bdSlukem 	{
16512de962bdSlukem 		ld->ld_errno = LDAP_ENCODING_ERROR;
16522de962bdSlukem 		ber_free( ber, 1 );
16532de962bdSlukem 		return NULL;
16542de962bdSlukem 	}
16552de962bdSlukem 
16562de962bdSlukem #ifdef LDAP_DEBUG
16572de962bdSlukem 	if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
1658*549b59edSchristos 		Debug0( LDAP_DEBUG_ANY, "re_encode_request new request is:\n" );
16592de962bdSlukem 		ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
16602de962bdSlukem 	}
16612de962bdSlukem #endif /* LDAP_DEBUG */
16622de962bdSlukem 
16632de962bdSlukem 	*type = tag;	/* return request type */
16642de962bdSlukem 	return ber;
16652de962bdSlukem }
16662de962bdSlukem 
16672de962bdSlukem 
1668d11b170bStron /* protected by req_mutex */
16692de962bdSlukem LDAPRequest *
ldap_find_request_by_msgid(LDAP * ld,ber_int_t msgid)16702de962bdSlukem ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
16712de962bdSlukem {
1672*549b59edSchristos 	LDAPRequest	*lr, needle = {0};
1673*549b59edSchristos 	needle.lr_msgid = msgid;
16742de962bdSlukem 
1675*549b59edSchristos 	lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp );
1676*549b59edSchristos 	if ( lr != NULL && lr->lr_status != LDAP_REQST_COMPLETED ) {
1677*549b59edSchristos 		/* try_read1msg is the only user at the moment and we would free it
1678*549b59edSchristos 		 * multiple times if retrieving the request again */
1679*549b59edSchristos 		assert( lr->lr_refcnt == 0 );
16802de962bdSlukem 		lr->lr_refcnt++;
1681*549b59edSchristos 		Debug3( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: "
1682*549b59edSchristos 				"msgid %d, lr %p lr->lr_refcnt = %d\n",
1683*549b59edSchristos 				msgid, lr, lr->lr_refcnt );
1684*549b59edSchristos 		return lr;
16852de962bdSlukem 	}
16862de962bdSlukem 
1687*549b59edSchristos 	Debug2( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: "
1688*549b59edSchristos 			"msgid %d, lr %p\n", msgid, lr );
1689*549b59edSchristos 	return NULL;
16902de962bdSlukem }
16912de962bdSlukem 
1692d11b170bStron /* protected by req_mutex */
16932de962bdSlukem void
ldap_return_request(LDAP * ld,LDAPRequest * lrx,int freeit)16942de962bdSlukem ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
16952de962bdSlukem {
16962de962bdSlukem 	LDAPRequest	*lr;
16972de962bdSlukem 
1698*549b59edSchristos 	lr = ldap_tavl_find( ld->ld_requests, lrx, ldap_req_cmp );
1699*549b59edSchristos 	Debug2( LDAP_DEBUG_TRACE, "ldap_return_request: "
1700*549b59edSchristos 			"lrx %p, lr %p\n", lrx, lr );
1701*549b59edSchristos 	if ( lr ) {
1702*549b59edSchristos 		assert( lr == lrx );
17032de962bdSlukem 		if ( lr->lr_refcnt > 0 ) {
17042de962bdSlukem 			lr->lr_refcnt--;
17052de962bdSlukem 		} else if ( lr->lr_refcnt < 0 ) {
17062de962bdSlukem 			lr->lr_refcnt++;
17072de962bdSlukem 			if ( lr->lr_refcnt == 0 ) {
17082de962bdSlukem 				lr = NULL;
17092de962bdSlukem 			}
17102de962bdSlukem 		}
17112de962bdSlukem 	}
1712*549b59edSchristos 	Debug3( LDAP_DEBUG_TRACE, "ldap_return_request: "
1713*549b59edSchristos 			"lrx->lr_msgid %d, lrx->lr_refcnt is now %d, lr is %s present\n",
1714*549b59edSchristos 			lrx->lr_msgid, lrx->lr_refcnt, lr ? "still" : "not" );
1715*549b59edSchristos 	/* The request is not tracked anymore */
17162de962bdSlukem 	if ( lr == NULL ) {
17172de962bdSlukem 		ldap_free_request_int( ld, lrx );
17182de962bdSlukem 	} else if ( freeit ) {
17192de962bdSlukem 		ldap_free_request( ld, lrx );
17202de962bdSlukem 	}
17212de962bdSlukem }
1722