xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/request.c (revision 4e27b3e8e5aea58b703a0c9ab7a17f5b10c9f733)
1*4e27b3e8Schristos /*	$NetBSD: request.c,v 1.1.1.8 2019/08/08 13:31:14 christos Exp $	*/
24e6df137Slukem 
3d11b170bStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem  *
6*4e27b3e8Schristos  * Copyright 1998-2019 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*4e27b3e8Schristos __RCSID("$NetBSD: request.c,v 1.1.1.8 2019/08/08 13:31:14 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 *
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
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
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 
1302de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 );
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 ) {
1482de962bdSlukem 		Debug( LDAP_DEBUG_TRACE,
1492de962bdSlukem 			"ldap_open_defconn: successful\n",
1502de962bdSlukem 			0, 0, 0 );
1512de962bdSlukem 	}
1522de962bdSlukem 
1532de962bdSlukem #ifdef LDAP_CONNECTIONLESS
1542de962bdSlukem 	if (LDAP_IS_UDP(ld)) {
1552de962bdSlukem 		if (msgtype == LDAP_REQ_BIND) {
156d11b170bStron 			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
1572de962bdSlukem 			if (ld->ld_options.ldo_cldapdn)
1582de962bdSlukem 				ldap_memfree(ld->ld_options.ldo_cldapdn);
1592de962bdSlukem 			ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
160ef2f90d3Sadam 			ber_free( ber, 1 );
161d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
162d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
1632de962bdSlukem 			return 0;
1642de962bdSlukem 		}
1652de962bdSlukem 		if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
166ef2f90d3Sadam 		{
167ef2f90d3Sadam 			ber_free( ber, 1 );
168d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
1692de962bdSlukem 			return LDAP_PARAM_ERROR;
1702de962bdSlukem 		}
171ef2f90d3Sadam 	}
1722de962bdSlukem #endif
173d11b170bStron 	LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
1742de962bdSlukem 	rc = ldap_send_server_request( ld, ber, msgid, NULL,
175d11b170bStron 		NULL, NULL, NULL, 0, 0 );
176d11b170bStron 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
177d11b170bStron 	LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
1782de962bdSlukem 	return(rc);
1792de962bdSlukem }
1802de962bdSlukem 
1812de962bdSlukem 
182d11b170bStron /* protected by conn_mutex */
1832de962bdSlukem int
1842de962bdSlukem ldap_int_flush_request(
1852de962bdSlukem 	LDAP *ld,
1862de962bdSlukem 	LDAPRequest *lr )
1872de962bdSlukem {
1882de962bdSlukem 	LDAPConn *lc = lr->lr_conn;
1892de962bdSlukem 
190d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
1912de962bdSlukem 	if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
192*4e27b3e8Schristos 		if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) {
193*4e27b3e8Schristos 			/* ENOTCONN is returned in Solaris 10 */
1942de962bdSlukem 			/* need to continue write later */
1952de962bdSlukem 			lr->lr_status = LDAP_REQST_WRITING;
1962de962bdSlukem 			ldap_mark_select_write( ld, lc->lconn_sb );
1972de962bdSlukem 			ld->ld_errno = LDAP_BUSY;
1982de962bdSlukem 			return -2;
1992de962bdSlukem 		} else {
2002de962bdSlukem 			ld->ld_errno = LDAP_SERVER_DOWN;
2012de962bdSlukem 			ldap_free_request( ld, lr );
2022de962bdSlukem 			ldap_free_connection( ld, lc, 0, 0 );
2032de962bdSlukem 			return( -1 );
2042de962bdSlukem 		}
2052de962bdSlukem 	} else {
2062de962bdSlukem 		if ( lr->lr_parent == NULL ) {
2072de962bdSlukem 			lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
2082de962bdSlukem 			lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
2092de962bdSlukem 		}
2102de962bdSlukem 		lr->lr_status = LDAP_REQST_INPROGRESS;
2112de962bdSlukem 
2122de962bdSlukem 		/* sent -- waiting for a response */
2132de962bdSlukem 		ldap_mark_select_read( ld, lc->lconn_sb );
214d11b170bStron 		ldap_clear_select_write( ld, lc->lconn_sb );
2152de962bdSlukem 	}
2162de962bdSlukem 	return 0;
2172de962bdSlukem }
2182de962bdSlukem 
219d11b170bStron /*
220d11b170bStron  * protected by req_mutex
221d11b170bStron  * if m_noconn then protect using conn_lock
222d11b170bStron  * else already protected with conn_lock
223d11b170bStron  * if m_res then also protected by res_mutex
224d11b170bStron  */
225d11b170bStron 
2262de962bdSlukem int
2272de962bdSlukem ldap_send_server_request(
2282de962bdSlukem 	LDAP *ld,
2292de962bdSlukem 	BerElement *ber,
2302de962bdSlukem 	ber_int_t msgid,
2312de962bdSlukem 	LDAPRequest *parentreq,
2322de962bdSlukem 	LDAPURLDesc **srvlist,
2332de962bdSlukem 	LDAPConn *lc,
234d11b170bStron 	LDAPreqinfo *bind,
235d11b170bStron 	int m_noconn,
236d11b170bStron 	int m_res )
2372de962bdSlukem {
2382de962bdSlukem 	LDAPRequest	*lr;
2392de962bdSlukem 	int		incparent, rc;
2402de962bdSlukem 
241d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
2422de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
2432de962bdSlukem 
2442de962bdSlukem 	incparent = 0;
2452de962bdSlukem 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
2462de962bdSlukem 
247d11b170bStron 	LDAP_CONN_LOCK_IF(m_noconn);
2482de962bdSlukem 	if ( lc == NULL ) {
2492de962bdSlukem 		if ( srvlist == NULL ) {
2502de962bdSlukem 			lc = ld->ld_defconn;
2512de962bdSlukem 		} else {
2522de962bdSlukem 			lc = find_connection( ld, *srvlist, 1 );
2532de962bdSlukem 			if ( lc == NULL ) {
2542de962bdSlukem 				if ( (bind != NULL) && (parentreq != NULL) ) {
2552de962bdSlukem 					/* Remember the bind in the parent */
2562de962bdSlukem 					incparent = 1;
2572de962bdSlukem 					++parentreq->lr_outrefcnt;
2582de962bdSlukem 				}
259d11b170bStron 				lc = ldap_new_connection( ld, srvlist, 0,
260d11b170bStron 					1, bind, 1, m_res );
2612de962bdSlukem 			}
2622de962bdSlukem 		}
2632de962bdSlukem 	}
2642de962bdSlukem 
2652de962bdSlukem 	/* async connect... */
2662de962bdSlukem 	if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
2672de962bdSlukem 		ber_socket_t	sd = AC_SOCKET_ERROR;
2682de962bdSlukem 		struct timeval	tv = { 0 };
2692de962bdSlukem 
2702de962bdSlukem 		ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
2712de962bdSlukem 
2722de962bdSlukem 		/* poll ... */
273d11b170bStron 		switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) {
2742de962bdSlukem 		case 0:
2752de962bdSlukem 			/* go on! */
2762de962bdSlukem 			lc->lconn_status = LDAP_CONNST_CONNECTED;
2772de962bdSlukem 			break;
2782de962bdSlukem 
2792de962bdSlukem 		case -2:
2802de962bdSlukem 			/* async only occurs if a network timeout is set */
2812de962bdSlukem 
2822de962bdSlukem 			/* honor network timeout */
283d11b170bStron 			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
2842de962bdSlukem 			if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
2852de962bdSlukem 			{
2862de962bdSlukem 				/* caller will have to call again */
2872de962bdSlukem 				ld->ld_errno = LDAP_X_CONNECTING;
2882de962bdSlukem 			}
289d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
2902de962bdSlukem 			/* fallthru */
2912de962bdSlukem 
2922de962bdSlukem 		default:
2932de962bdSlukem 			/* error */
2942de962bdSlukem 			break;
2952de962bdSlukem 		}
2962de962bdSlukem 	}
2972de962bdSlukem 
2982de962bdSlukem 	if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
2992de962bdSlukem 		if ( ld->ld_errno == LDAP_SUCCESS ) {
3002de962bdSlukem 			ld->ld_errno = LDAP_SERVER_DOWN;
3012de962bdSlukem 		}
3022de962bdSlukem 
3032de962bdSlukem 		ber_free( ber, 1 );
3042de962bdSlukem 		if ( incparent ) {
3052de962bdSlukem 			/* Forget about the bind */
3062de962bdSlukem 			--parentreq->lr_outrefcnt;
3072de962bdSlukem 		}
308d11b170bStron 		LDAP_CONN_UNLOCK_IF(m_noconn);
3092de962bdSlukem 		return( -1 );
3102de962bdSlukem 	}
3112de962bdSlukem 
3122de962bdSlukem 	use_connection( ld, lc );
3132de962bdSlukem 
3142de962bdSlukem #ifdef LDAP_CONNECTIONLESS
3152de962bdSlukem 	if ( LDAP_IS_UDP( ld )) {
3162de962bdSlukem 		BerElement tmpber = *ber;
3172de962bdSlukem 		ber_rewind( &tmpber );
318d11b170bStron 		LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
3192de962bdSlukem 		rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
320d11b170bStron 			sizeof( struct sockaddr_storage ), 0 );
321d11b170bStron 		LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
3222de962bdSlukem 		if ( rc == -1 ) {
3232de962bdSlukem 			ld->ld_errno = LDAP_ENCODING_ERROR;
324*4e27b3e8Schristos 			ber_free( ber, 1 );
325d11b170bStron 			LDAP_CONN_UNLOCK_IF(m_noconn);
3262de962bdSlukem 			return rc;
3272de962bdSlukem 		}
3282de962bdSlukem 	}
3292de962bdSlukem #endif
3302de962bdSlukem 
3312de962bdSlukem 	/* If we still have an incomplete write, try to finish it before
3322de962bdSlukem 	 * dealing with the new request. If we don't finish here, return
3332de962bdSlukem 	 * LDAP_BUSY and let the caller retry later. We only allow a single
3342de962bdSlukem 	 * request to be in WRITING state.
3352de962bdSlukem 	 */
3362de962bdSlukem 	rc = 0;
3372de962bdSlukem 	if ( ld->ld_requests &&
3382de962bdSlukem 		ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
3392de962bdSlukem 		ldap_int_flush_request( ld, ld->ld_requests ) < 0 )
3402de962bdSlukem 	{
3412de962bdSlukem 		rc = -1;
3422de962bdSlukem 	}
343d11b170bStron 	if ( rc ) {
344*4e27b3e8Schristos 		ber_free( ber, 1 );
345d11b170bStron 		LDAP_CONN_UNLOCK_IF(m_noconn);
346d11b170bStron 		return rc;
347d11b170bStron 	}
3482de962bdSlukem 
3492de962bdSlukem 	lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
3502de962bdSlukem 	if ( lr == NULL ) {
3512de962bdSlukem 		ld->ld_errno = LDAP_NO_MEMORY;
3522de962bdSlukem 		ldap_free_connection( ld, lc, 0, 0 );
3532de962bdSlukem 		ber_free( ber, 1 );
3542de962bdSlukem 		if ( incparent ) {
3552de962bdSlukem 			/* Forget about the bind */
3562de962bdSlukem 			--parentreq->lr_outrefcnt;
3572de962bdSlukem 		}
358d11b170bStron 		LDAP_CONN_UNLOCK_IF(m_noconn);
3592de962bdSlukem 		return( -1 );
3602de962bdSlukem 	}
3612de962bdSlukem 	lr->lr_msgid = msgid;
3622de962bdSlukem 	lr->lr_status = LDAP_REQST_INPROGRESS;
3632de962bdSlukem 	lr->lr_res_errno = LDAP_SUCCESS;	/* optimistic */
3642de962bdSlukem 	lr->lr_ber = ber;
3652de962bdSlukem 	lr->lr_conn = lc;
3662de962bdSlukem 	if ( parentreq != NULL ) {	/* sub-request */
3672de962bdSlukem 		if ( !incparent ) {
3682de962bdSlukem 			/* Increment if we didn't do it before the bind */
3692de962bdSlukem 			++parentreq->lr_outrefcnt;
3702de962bdSlukem 		}
3712de962bdSlukem 		lr->lr_origid = parentreq->lr_origid;
3722de962bdSlukem 		lr->lr_parentcnt = ++parentreq->lr_parentcnt;
3732de962bdSlukem 		lr->lr_parent = parentreq;
3742de962bdSlukem 		lr->lr_refnext = parentreq->lr_child;
3752de962bdSlukem 		parentreq->lr_child = lr;
3762de962bdSlukem 	} else {			/* original request */
3772de962bdSlukem 		lr->lr_origid = lr->lr_msgid;
3782de962bdSlukem 	}
3792de962bdSlukem 
3802de962bdSlukem 	/* Extract requestDN for future reference */
381d11b170bStron #ifdef LDAP_CONNECTIONLESS
382d11b170bStron 	if ( !LDAP_IS_UDP(ld) )
383d11b170bStron #endif
3842de962bdSlukem 	{
3852de962bdSlukem 		BerElement tmpber = *ber;
3862de962bdSlukem 		ber_int_t	bint;
3872de962bdSlukem 		ber_tag_t	tag, rtag;
3882de962bdSlukem 
3892de962bdSlukem 		ber_reset( &tmpber, 1 );
3902de962bdSlukem 		rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
3912de962bdSlukem 		switch ( tag ) {
3922de962bdSlukem 		case LDAP_REQ_BIND:
3932de962bdSlukem 			rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
3942de962bdSlukem 			break;
3952de962bdSlukem 		case LDAP_REQ_DELETE:
3962de962bdSlukem 			break;
3972de962bdSlukem 		default:
3982de962bdSlukem 			rtag = ber_scanf( &tmpber, "{" /*}*/ );
3992de962bdSlukem 		case LDAP_REQ_ABANDON:
4002de962bdSlukem 			break;
4012de962bdSlukem 		}
4022de962bdSlukem 		if ( tag != LDAP_REQ_ABANDON ) {
4032de962bdSlukem 			ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
4042de962bdSlukem 			lr->lr_dn.bv_val = tmpber.ber_ptr;
4052de962bdSlukem 		}
4062de962bdSlukem 	}
4072de962bdSlukem 
4082de962bdSlukem 	lr->lr_prev = NULL;
4092de962bdSlukem 	lr->lr_next = ld->ld_requests;
4102de962bdSlukem 	if ( lr->lr_next != NULL ) {
4112de962bdSlukem 		lr->lr_next->lr_prev = lr;
4122de962bdSlukem 	}
4132de962bdSlukem 	ld->ld_requests = lr;
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
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 *
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 );
4622de962bdSlukem 	Debug( 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 
5862de962bdSlukem 				Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0);
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 
6162de962bdSlukem 			Debug( LDAP_DEBUG_TRACE,
6172de962bdSlukem 				"anonymous rebind via ldap_sasl_bind(\"\")\n",
6182de962bdSlukem 				0, 0, 0);
6192de962bdSlukem 
620d11b170bStron 			LDAP_REQ_UNLOCK_IF(m_req);
621d11b170bStron 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
622d11b170bStron 			LDAP_RES_UNLOCK_IF(m_res);
6232de962bdSlukem 			rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
6242de962bdSlukem 				NULL, NULL, &msgid );
6252de962bdSlukem 			if ( rc != LDAP_SUCCESS ) {
6262de962bdSlukem 				err = -1;
6272de962bdSlukem 
6282de962bdSlukem 			} else {
6292de962bdSlukem 				for ( err = 1; err > 0; ) {
6302de962bdSlukem 					struct timeval	tv = { 0, 100000 };
6312de962bdSlukem 					LDAPMessage	*res = NULL;
6322de962bdSlukem 
6332de962bdSlukem 					switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
6342de962bdSlukem 					case -1:
6352de962bdSlukem 						err = -1;
6362de962bdSlukem 						break;
6372de962bdSlukem 
6382de962bdSlukem 					case 0:
6392de962bdSlukem #ifdef LDAP_R_COMPILE
6402de962bdSlukem 						ldap_pvt_thread_yield();
6412de962bdSlukem #endif
6422de962bdSlukem 						break;
6432de962bdSlukem 
6442de962bdSlukem 					case LDAP_RES_BIND:
6452de962bdSlukem 						rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
6462de962bdSlukem 						if ( rc != LDAP_SUCCESS ) {
6472de962bdSlukem 							err = -1;
6482de962bdSlukem 
6492de962bdSlukem 						} else if ( err != LDAP_SUCCESS ) {
6502de962bdSlukem 							err = -1;
6512de962bdSlukem 						}
6522de962bdSlukem 						/* else err == LDAP_SUCCESS == 0 */
6532de962bdSlukem 						break;
6542de962bdSlukem 
6552de962bdSlukem 					default:
6562de962bdSlukem 						Debug( LDAP_DEBUG_TRACE,
6572de962bdSlukem 							"ldap_new_connection %p: "
6582de962bdSlukem 							"unexpected response %d "
6592de962bdSlukem 							"from BIND request id=%d\n",
6602de962bdSlukem 							(void *) ld, ldap_msgtype( res ), msgid );
6612de962bdSlukem 						err = -1;
6622de962bdSlukem 						break;
6632de962bdSlukem 					}
6642de962bdSlukem 				}
6652de962bdSlukem 			}
666d11b170bStron 			LDAP_RES_LOCK_IF(m_res);
667d11b170bStron 			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
668d11b170bStron 			LDAP_REQ_LOCK_IF(m_req);
6692de962bdSlukem 			ld->ld_defconn = savedefconn;
6702de962bdSlukem 			--lc->lconn_refcnt;
6712de962bdSlukem 
6722de962bdSlukem 			if ( err != 0 ) {
6732de962bdSlukem 				ldap_free_connection( ld, lc, 1, 0 );
6742de962bdSlukem 				lc = NULL;
6752de962bdSlukem 			}
6762de962bdSlukem 		}
6772de962bdSlukem 		if ( lc != NULL )
6782de962bdSlukem 			lc->lconn_rebind_inprogress = 0;
6792de962bdSlukem 	}
6802de962bdSlukem 	return( lc );
6812de962bdSlukem }
6822de962bdSlukem 
6832de962bdSlukem 
684d11b170bStron /* protected by ld_conn_mutex */
6852de962bdSlukem static LDAPConn *
6862de962bdSlukem find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
6872de962bdSlukem /*
6882de962bdSlukem  * return an existing connection (if any) to the server srv
6892de962bdSlukem  * if "any" is non-zero, check for any server in the "srv" chain
6902de962bdSlukem  */
6912de962bdSlukem {
6922de962bdSlukem 	LDAPConn	*lc;
6932de962bdSlukem 	LDAPURLDesc	*lcu, *lsu;
6942de962bdSlukem 	int lcu_port, lsu_port;
6952de962bdSlukem 	int found = 0;
6962de962bdSlukem 
697d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
6982de962bdSlukem 	for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
6992de962bdSlukem 		lcu = lc->lconn_server;
7002de962bdSlukem 		lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
7012de962bdSlukem 			lcu->lud_port );
7022de962bdSlukem 
7032de962bdSlukem 		for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
7042de962bdSlukem 			lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
7052de962bdSlukem 				lsu->lud_port );
7062de962bdSlukem 
7072de962bdSlukem 			if ( lsu_port == lcu_port
7082de962bdSlukem 				&& strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
7094e6df137Slukem 				&& lcu->lud_host != NULL && lsu->lud_host != NULL
7102de962bdSlukem 				&& strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
7112de962bdSlukem 			{
7122de962bdSlukem 				found = 1;
7132de962bdSlukem 				break;
7142de962bdSlukem 			}
7152de962bdSlukem 
7162de962bdSlukem 			if ( !any ) break;
7172de962bdSlukem 		}
7182de962bdSlukem 		if ( found )
7192de962bdSlukem 			break;
7202de962bdSlukem 	}
7212de962bdSlukem 	return lc;
7222de962bdSlukem }
7232de962bdSlukem 
7242de962bdSlukem 
7252de962bdSlukem 
726d11b170bStron /* protected by ld_conn_mutex */
7272de962bdSlukem static void
7282de962bdSlukem use_connection( LDAP *ld, LDAPConn *lc )
7292de962bdSlukem {
730d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
7312de962bdSlukem 	++lc->lconn_refcnt;
7322de962bdSlukem 	lc->lconn_lastused = time( NULL );
7332de962bdSlukem }
7342de962bdSlukem 
7352de962bdSlukem 
736d11b170bStron /* protected by ld_conn_mutex */
7372de962bdSlukem void
7382de962bdSlukem ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
7392de962bdSlukem {
7402de962bdSlukem 	LDAPConn	*tmplc, *prevlc;
7412de962bdSlukem 
742d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
7432de962bdSlukem 	Debug( LDAP_DEBUG_TRACE,
7442de962bdSlukem 		"ldap_free_connection %d %d\n",
7452de962bdSlukem 		force, unbind, 0 );
7462de962bdSlukem 
7472de962bdSlukem 	if ( force || --lc->lconn_refcnt <= 0 ) {
7482de962bdSlukem 		/* remove from connections list first */
7492de962bdSlukem 
7502de962bdSlukem 		for ( prevlc = NULL, tmplc = ld->ld_conns;
7512de962bdSlukem 			tmplc != NULL;
7522de962bdSlukem 			tmplc = tmplc->lconn_next )
7532de962bdSlukem 		{
7542de962bdSlukem 			if ( tmplc == lc ) {
7552de962bdSlukem 				if ( prevlc == NULL ) {
7562de962bdSlukem 				    ld->ld_conns = tmplc->lconn_next;
7572de962bdSlukem 				} else {
7582de962bdSlukem 				    prevlc->lconn_next = tmplc->lconn_next;
7592de962bdSlukem 				}
760bb30016cSlukem 				if ( ld->ld_defconn == lc ) {
761bb30016cSlukem 					ld->ld_defconn = NULL;
762bb30016cSlukem 				}
7632de962bdSlukem 				break;
7642de962bdSlukem 			}
7652de962bdSlukem 			prevlc = tmplc;
7662de962bdSlukem 		}
7672de962bdSlukem 
7684e6df137Slukem 		/* process connection callbacks */
7694e6df137Slukem 		{
7704e6df137Slukem 			struct ldapoptions *lo;
7714e6df137Slukem 			ldaplist *ll;
7724e6df137Slukem 			ldap_conncb *cb;
7734e6df137Slukem 
7744e6df137Slukem 			lo = &ld->ld_options;
775d11b170bStron 			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
7764e6df137Slukem 			if ( lo->ldo_conn_cbs ) {
7774e6df137Slukem 				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
7784e6df137Slukem 					cb = ll->ll_data;
7794e6df137Slukem 					cb->lc_del( ld, lc->lconn_sb, cb );
7804e6df137Slukem 				}
7814e6df137Slukem 			}
782d11b170bStron 			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
7834e6df137Slukem 			lo = LDAP_INT_GLOBAL_OPT();
784d11b170bStron 			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
7854e6df137Slukem 			if ( lo->ldo_conn_cbs ) {
7864e6df137Slukem 				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
7874e6df137Slukem 					cb = ll->ll_data;
7884e6df137Slukem 					cb->lc_del( ld, lc->lconn_sb, cb );
7894e6df137Slukem 				}
7904e6df137Slukem 			}
791d11b170bStron 			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
7924e6df137Slukem 		}
7934e6df137Slukem 
7942de962bdSlukem 		if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
7952de962bdSlukem 			ldap_mark_select_clear( ld, lc->lconn_sb );
7962de962bdSlukem 			if ( unbind ) {
7972de962bdSlukem 				ldap_send_unbind( ld, lc->lconn_sb,
7982de962bdSlukem 						NULL, NULL );
7992de962bdSlukem 			}
8002de962bdSlukem 		}
8012de962bdSlukem 
8022de962bdSlukem 		if ( lc->lconn_ber != NULL ) {
8032de962bdSlukem 			ber_free( lc->lconn_ber, 1 );
8042de962bdSlukem 		}
8052de962bdSlukem 
8062de962bdSlukem 		ldap_int_sasl_close( ld, lc );
8074e6df137Slukem #ifdef HAVE_GSSAPI
8084e6df137Slukem 		ldap_int_gssapi_close( ld, lc );
8094e6df137Slukem #endif
8102de962bdSlukem 
8112de962bdSlukem 		ldap_free_urllist( lc->lconn_server );
8122de962bdSlukem 
8132de962bdSlukem 		/* FIXME: is this at all possible?
8142de962bdSlukem 		 * ldap_ld_free() in unbind.c calls ldap_free_connection()
8152de962bdSlukem 		 * with force == 1 __after__ explicitly calling
8162de962bdSlukem 		 * ldap_free_request() on all requests */
8172de962bdSlukem 		if ( force ) {
8182de962bdSlukem 			LDAPRequest	*lr;
8192de962bdSlukem 
8202de962bdSlukem 			for ( lr = ld->ld_requests; lr; ) {
8212de962bdSlukem 				LDAPRequest	*lr_next = lr->lr_next;
8222de962bdSlukem 
8232de962bdSlukem 				if ( lr->lr_conn == lc ) {
8242de962bdSlukem 					ldap_free_request_int( ld, lr );
8252de962bdSlukem 				}
8262de962bdSlukem 
8272de962bdSlukem 				lr = lr_next;
8282de962bdSlukem 			}
8292de962bdSlukem 		}
8302de962bdSlukem 
8312de962bdSlukem 		if ( lc->lconn_sb != ld->ld_sb ) {
8322de962bdSlukem 			ber_sockbuf_free( lc->lconn_sb );
833bb30016cSlukem 		} else {
834bb30016cSlukem 			ber_int_sb_close( lc->lconn_sb );
8352de962bdSlukem 		}
8362de962bdSlukem 
8372de962bdSlukem 		if ( lc->lconn_rebind_queue != NULL) {
8382de962bdSlukem 			int i;
8392de962bdSlukem 			for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
8402de962bdSlukem 				LDAP_VFREE( lc->lconn_rebind_queue[i] );
8412de962bdSlukem 			}
8422de962bdSlukem 			LDAP_FREE( lc->lconn_rebind_queue );
8432de962bdSlukem 		}
8442de962bdSlukem 
8452de962bdSlukem 		LDAP_FREE( lc );
8462de962bdSlukem 
8472de962bdSlukem 		Debug( LDAP_DEBUG_TRACE,
8482de962bdSlukem 			"ldap_free_connection: actually freed\n",
8492de962bdSlukem 			0, 0, 0 );
8502de962bdSlukem 
8512de962bdSlukem 	} else {
8522de962bdSlukem 		lc->lconn_lastused = time( NULL );
8532de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
8542de962bdSlukem 				lc->lconn_refcnt, 0, 0 );
8552de962bdSlukem 	}
8562de962bdSlukem }
8572de962bdSlukem 
8582de962bdSlukem 
859d11b170bStron /* Protects self with ld_conn_mutex */
8602de962bdSlukem #ifdef LDAP_DEBUG
8612de962bdSlukem void
8622de962bdSlukem ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
8632de962bdSlukem {
8642de962bdSlukem 	LDAPConn	*lc;
8652de962bdSlukem    	char		timebuf[32];
8662de962bdSlukem 
8672de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 );
868d11b170bStron 	LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
8692de962bdSlukem 	for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
8702de962bdSlukem 		if ( lc->lconn_server != NULL ) {
8712de962bdSlukem 			Debug( LDAP_DEBUG_TRACE, "* host: %s  port: %d%s\n",
8722de962bdSlukem 				( lc->lconn_server->lud_host == NULL ) ? "(null)"
8732de962bdSlukem 				: lc->lconn_server->lud_host,
8742de962bdSlukem 				lc->lconn_server->lud_port, ( lc->lconn_sb ==
8752de962bdSlukem 				ld->ld_sb ) ? "  (default)" : "" );
8762de962bdSlukem 		}
8772de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
8782de962bdSlukem 			( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
8792de962bdSlukem 				? "NeedSocket" :
8802de962bdSlukem 				( lc->lconn_status == LDAP_CONNST_CONNECTING )
8812de962bdSlukem 					? "Connecting" : "Connected", 0 );
8822de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, "  last used: %s%s\n",
8832de962bdSlukem 			ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
8842de962bdSlukem 			lc->lconn_rebind_inprogress ? "  rebind in progress" : "", 0 );
8852de962bdSlukem 		if ( lc->lconn_rebind_inprogress ) {
8862de962bdSlukem 			if ( lc->lconn_rebind_queue != NULL) {
8872de962bdSlukem 				int	i;
8882de962bdSlukem 
8892de962bdSlukem 				for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
8902de962bdSlukem 					int	j;
8912de962bdSlukem 					for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) {
8922de962bdSlukem 						Debug( LDAP_DEBUG_TRACE, "    queue %d entry %d - %s\n",
8932de962bdSlukem 							i, j, lc->lconn_rebind_queue[i][j] );
8942de962bdSlukem 					}
8952de962bdSlukem 				}
8962de962bdSlukem 			} else {
8972de962bdSlukem 				Debug( LDAP_DEBUG_TRACE, "    queue is empty\n", 0, 0, 0 );
8982de962bdSlukem 			}
8992de962bdSlukem 		}
9002de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, "\n", 0, 0, 0 );
9012de962bdSlukem 		if ( !all ) {
9022de962bdSlukem 			break;
9032de962bdSlukem 		}
9042de962bdSlukem 	}
905d11b170bStron 	LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
9062de962bdSlukem }
9072de962bdSlukem 
9082de962bdSlukem 
909d11b170bStron /* protected by req_mutex and res_mutex */
9102de962bdSlukem void
9112de962bdSlukem ldap_dump_requests_and_responses( LDAP *ld )
9122de962bdSlukem {
9132de962bdSlukem 	LDAPRequest	*lr;
9142de962bdSlukem 	LDAPMessage	*lm, *l;
9152de962bdSlukem 	int		i;
9162de962bdSlukem 
9172de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n",
9182de962bdSlukem 		(void *)ld, 0, 0 );
9192de962bdSlukem 	lr = ld->ld_requests;
9202de962bdSlukem 	if ( lr == NULL ) {
9212de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, "   Empty\n", 0, 0, 0 );
9222de962bdSlukem 	}
9232de962bdSlukem 	for ( i = 0; lr != NULL; lr = lr->lr_next, i++ ) {
9242de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, " * msgid %d,  origid %d, status %s\n",
9252de962bdSlukem 			lr->lr_msgid, lr->lr_origid,
9262de962bdSlukem 			( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
9272de962bdSlukem 			( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
9282de962bdSlukem 			( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
9292de962bdSlukem 			( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" :
9302de962bdSlukem 			( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted"
9312de962bdSlukem 				: "InvalidStatus" );
9322de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, "   outstanding referrals %d, parent count %d\n",
9332de962bdSlukem 			lr->lr_outrefcnt, lr->lr_parentcnt, 0 );
9342de962bdSlukem 	}
9352de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "  ld %p request count %d (abandoned %lu)\n",
9362de962bdSlukem 		(void *)ld, i, ld->ld_nabandoned );
9372de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld, 0, 0 );
9382de962bdSlukem 	if ( ( lm = ld->ld_responses ) == NULL ) {
9392de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, "   Empty\n", 0, 0, 0 );
9402de962bdSlukem 	}
9412de962bdSlukem 	for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) {
9422de962bdSlukem 		Debug( LDAP_DEBUG_TRACE, " * msgid %d,  type %lu\n",
9432de962bdSlukem 		    lm->lm_msgid, (unsigned long)lm->lm_msgtype, 0 );
9442de962bdSlukem 		if ( lm->lm_chain != NULL ) {
9452de962bdSlukem 			Debug( LDAP_DEBUG_TRACE, "   chained responses:\n", 0, 0, 0 );
9462de962bdSlukem 			for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) {
9472de962bdSlukem 				Debug( LDAP_DEBUG_TRACE,
9482de962bdSlukem 					"  * msgid %d,  type %lu\n",
9492de962bdSlukem 					l->lm_msgid,
9502de962bdSlukem 					(unsigned long)l->lm_msgtype, 0 );
9512de962bdSlukem 			}
9522de962bdSlukem 		}
9532de962bdSlukem 	}
9542de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "  ld %p response count %d\n", (void *)ld, i, 0 );
9552de962bdSlukem }
9562de962bdSlukem #endif /* LDAP_DEBUG */
9572de962bdSlukem 
958d11b170bStron /* protected by req_mutex */
9592de962bdSlukem static void
9602de962bdSlukem ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
9612de962bdSlukem {
962d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
9632de962bdSlukem 	/* if lr_refcnt > 0, the request has been looked up
9642de962bdSlukem 	 * by ldap_find_request_by_msgid(); if in the meanwhile
9652de962bdSlukem 	 * the request is free()'d by someone else, just decrease
9662de962bdSlukem 	 * the reference count and extract it from the request
9672de962bdSlukem 	 * list; later on, it will be freed. */
9682de962bdSlukem 	if ( lr->lr_prev == NULL ) {
9692de962bdSlukem 		if ( lr->lr_refcnt == 0 ) {
9702de962bdSlukem 			/* free'ing the first request? */
9712de962bdSlukem 			assert( ld->ld_requests == lr );
9722de962bdSlukem 		}
9732de962bdSlukem 
9742de962bdSlukem 		if ( ld->ld_requests == lr ) {
9752de962bdSlukem 			ld->ld_requests = lr->lr_next;
9762de962bdSlukem 		}
9772de962bdSlukem 
9782de962bdSlukem 	} else {
9792de962bdSlukem 		lr->lr_prev->lr_next = lr->lr_next;
9802de962bdSlukem 	}
9812de962bdSlukem 
9822de962bdSlukem 	if ( lr->lr_next != NULL ) {
9832de962bdSlukem 		lr->lr_next->lr_prev = lr->lr_prev;
9842de962bdSlukem 	}
9852de962bdSlukem 
9862de962bdSlukem 	if ( lr->lr_refcnt > 0 ) {
9872de962bdSlukem 		lr->lr_refcnt = -lr->lr_refcnt;
9882de962bdSlukem 
9892de962bdSlukem 		lr->lr_prev = NULL;
9902de962bdSlukem 		lr->lr_next = NULL;
9912de962bdSlukem 
9922de962bdSlukem 		return;
9932de962bdSlukem 	}
9942de962bdSlukem 
9952de962bdSlukem 	if ( lr->lr_ber != NULL ) {
9962de962bdSlukem 		ber_free( lr->lr_ber, 1 );
9972de962bdSlukem 		lr->lr_ber = NULL;
9982de962bdSlukem 	}
9992de962bdSlukem 
10002de962bdSlukem 	if ( lr->lr_res_error != NULL ) {
10012de962bdSlukem 		LDAP_FREE( lr->lr_res_error );
10022de962bdSlukem 		lr->lr_res_error = NULL;
10032de962bdSlukem 	}
10042de962bdSlukem 
10052de962bdSlukem 	if ( lr->lr_res_matched != NULL ) {
10062de962bdSlukem 		LDAP_FREE( lr->lr_res_matched );
10072de962bdSlukem 		lr->lr_res_matched = NULL;
10082de962bdSlukem 	}
10092de962bdSlukem 
10102de962bdSlukem 	LDAP_FREE( lr );
10112de962bdSlukem }
10122de962bdSlukem 
1013d11b170bStron /* protected by req_mutex */
10142de962bdSlukem void
10152de962bdSlukem ldap_free_request( LDAP *ld, LDAPRequest *lr )
10162de962bdSlukem {
1017d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
10182de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
10192de962bdSlukem 		lr->lr_origid, lr->lr_msgid, 0 );
10202de962bdSlukem 
10212de962bdSlukem 	/* free all referrals (child requests) */
10222de962bdSlukem 	while ( lr->lr_child ) {
10232de962bdSlukem 		ldap_free_request( ld, lr->lr_child );
10242de962bdSlukem 	}
10252de962bdSlukem 
10262de962bdSlukem 	if ( lr->lr_parent != NULL ) {
10272de962bdSlukem 		LDAPRequest     **lrp;
10282de962bdSlukem 
10292de962bdSlukem 		--lr->lr_parent->lr_outrefcnt;
10302de962bdSlukem 		for ( lrp = &lr->lr_parent->lr_child;
10312de962bdSlukem 			*lrp && *lrp != lr;
10322de962bdSlukem 			lrp = &(*lrp)->lr_refnext );
10332de962bdSlukem 
10342de962bdSlukem 		if ( *lrp == lr ) {
10352de962bdSlukem 			*lrp = lr->lr_refnext;
10362de962bdSlukem 		}
10372de962bdSlukem 	}
10382de962bdSlukem 	ldap_free_request_int( ld, lr );
10392de962bdSlukem }
10402de962bdSlukem 
10412de962bdSlukem /*
10422de962bdSlukem  * call first time with *cntp = -1
10432de962bdSlukem  * when returns *cntp == -1, no referrals are left
10442de962bdSlukem  *
10452de962bdSlukem  * NOTE: may replace *refsp, or shuffle the contents
10462de962bdSlukem  * of the original array.
10472de962bdSlukem  */
10482de962bdSlukem static int ldap_int_nextref(
10492de962bdSlukem 	LDAP			*ld,
10502de962bdSlukem 	char			***refsp,
10512de962bdSlukem 	int			*cntp,
10522de962bdSlukem 	void			*params )
10532de962bdSlukem {
10542de962bdSlukem 	assert( refsp != NULL );
10552de962bdSlukem 	assert( *refsp != NULL );
10562de962bdSlukem 	assert( cntp != NULL );
10572de962bdSlukem 
10582de962bdSlukem 	if ( *cntp < -1 ) {
10592de962bdSlukem 		*cntp = -1;
10602de962bdSlukem 		return -1;
10612de962bdSlukem 	}
10622de962bdSlukem 
10632de962bdSlukem 	(*cntp)++;
10642de962bdSlukem 
10652de962bdSlukem 	if ( (*refsp)[ *cntp ] == NULL ) {
10662de962bdSlukem 		*cntp = -1;
10672de962bdSlukem 	}
10682de962bdSlukem 
10692de962bdSlukem 	return 0;
10702de962bdSlukem }
10712de962bdSlukem 
10722de962bdSlukem /*
10732de962bdSlukem  * Chase v3 referrals
10742de962bdSlukem  *
10752de962bdSlukem  * Parameters:
10762de962bdSlukem  *  (IN) ld = LDAP connection handle
10772de962bdSlukem  *  (IN) lr = LDAP Request structure
10782de962bdSlukem  *  (IN) refs = array of pointers to referral strings that we will chase
10792de962bdSlukem  *              The array will be free'd by this function when no longer needed
10802de962bdSlukem  *  (IN) sref != 0 if following search reference
10812de962bdSlukem  *  (OUT) errstrp = Place to return a string of referrals which could not be followed
10822de962bdSlukem  *  (OUT) hadrefp = 1 if sucessfully followed referral
10832de962bdSlukem  *
10842de962bdSlukem  * Return value - number of referrals followed
1085d11b170bStron  *
1086d11b170bStron  * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
10872de962bdSlukem  */
10882de962bdSlukem int
10892de962bdSlukem ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
10902de962bdSlukem {
10912de962bdSlukem 	char		*unfollowed;
10922de962bdSlukem 	int		 unfollowedcnt = 0;
10932de962bdSlukem 	LDAPRequest	*origreq;
10942de962bdSlukem 	LDAPURLDesc	*srv = NULL;
10952de962bdSlukem 	BerElement	*ber;
10962de962bdSlukem 	char		**refarray = NULL;
10972de962bdSlukem 	LDAPConn	*lc;
10982de962bdSlukem 	int			 rc, count, i, j, id;
10992de962bdSlukem 	LDAPreqinfo  rinfo;
1100d11b170bStron 	LDAP_NEXTREF_PROC	*nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;
1101d11b170bStron 
1102d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1103d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
1104d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1105d11b170bStron 	Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );
11062de962bdSlukem 
11072de962bdSlukem 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
11082de962bdSlukem 	*hadrefp = 0;
11092de962bdSlukem 
11102de962bdSlukem 	unfollowed = NULL;
11112de962bdSlukem 	rc = count = 0;
11122de962bdSlukem 
11132de962bdSlukem 	/* If no referrals in array, return */
11142de962bdSlukem 	if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
11152de962bdSlukem 		rc = 0;
11162de962bdSlukem 		goto done;
11172de962bdSlukem 	}
11182de962bdSlukem 
11192de962bdSlukem 	/* Check for hop limit exceeded */
11202de962bdSlukem 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
11212de962bdSlukem 		Debug( LDAP_DEBUG_ANY,
11222de962bdSlukem 		    "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 );
11232de962bdSlukem 		ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
11242de962bdSlukem 		rc = -1;
11252de962bdSlukem 		goto done;
11262de962bdSlukem 	}
11272de962bdSlukem 
11282de962bdSlukem 	/* find original request */
11292de962bdSlukem 	for ( origreq = lr;
11302de962bdSlukem 		origreq->lr_parent != NULL;
11312de962bdSlukem 		origreq = origreq->lr_parent )
11322de962bdSlukem 	{
11332de962bdSlukem 		/* empty */ ;
11342de962bdSlukem 	}
11352de962bdSlukem 
11362de962bdSlukem 	refarray = refs;
11372de962bdSlukem 	refs = NULL;
11382de962bdSlukem 
11392de962bdSlukem 	/* parse out & follow referrals */
1140d11b170bStron 	/* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
11412de962bdSlukem 	i = -1;
1142d11b170bStron 	for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
11432de962bdSlukem 			i != -1;
1144d11b170bStron 			nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
11452de962bdSlukem 	{
11462de962bdSlukem 
11472de962bdSlukem 		/* Parse the referral URL */
11482de962bdSlukem 		rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
11492de962bdSlukem 		if ( rc != LDAP_URL_SUCCESS ) {
11502de962bdSlukem 			/* ldap_url_parse_ext() returns LDAP_URL_* errors
11512de962bdSlukem 			 * which do not map on API errors */
11522de962bdSlukem 			ld->ld_errno = LDAP_PARAM_ERROR;
11532de962bdSlukem 			rc = -1;
11542de962bdSlukem 			goto done;
11552de962bdSlukem 		}
11562de962bdSlukem 
11572de962bdSlukem 		if( srv->lud_crit_exts ) {
11584e6df137Slukem 			int ok = 0;
11594e6df137Slukem #ifdef HAVE_TLS
11604e6df137Slukem 			/* If StartTLS is the only critical ext, OK. */
11614e6df137Slukem 			if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
11624e6df137Slukem 				ok = 1;
11634e6df137Slukem #endif
11644e6df137Slukem 			if ( !ok ) {
11654e6df137Slukem 				/* we do not support any other extensions */
11662de962bdSlukem 				ld->ld_errno = LDAP_NOT_SUPPORTED;
11672de962bdSlukem 				rc = -1;
11682de962bdSlukem 				goto done;
11692de962bdSlukem 			}
11704e6df137Slukem 		}
11712de962bdSlukem 
11722de962bdSlukem 		/* check connection for re-bind in progress */
11732de962bdSlukem 		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
11742de962bdSlukem 			/* See if we've already requested this DN with this conn */
11752de962bdSlukem 			LDAPRequest *lp;
11762de962bdSlukem 			int looped = 0;
11774e6df137Slukem 			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
11782de962bdSlukem 			for ( lp = origreq; lp; ) {
11792de962bdSlukem 				if ( lp->lr_conn == lc
11802de962bdSlukem 					&& len == lp->lr_dn.bv_len
11812de962bdSlukem 					&& len
11822de962bdSlukem 					&& strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
11832de962bdSlukem 				{
11842de962bdSlukem 					looped = 1;
11852de962bdSlukem 					break;
11862de962bdSlukem 				}
11872de962bdSlukem 				if ( lp == origreq ) {
11882de962bdSlukem 					lp = lp->lr_child;
11892de962bdSlukem 				} else {
11902de962bdSlukem 					lp = lp->lr_refnext;
11912de962bdSlukem 				}
11922de962bdSlukem 			}
11932de962bdSlukem 			if ( looped ) {
11942de962bdSlukem 				ldap_free_urllist( srv );
11952de962bdSlukem 				srv = NULL;
11962de962bdSlukem 				ld->ld_errno = LDAP_CLIENT_LOOP;
11972de962bdSlukem 				rc = -1;
11982de962bdSlukem 				continue;
11992de962bdSlukem 			}
12002de962bdSlukem 
12012de962bdSlukem 			if ( lc->lconn_rebind_inprogress ) {
12022de962bdSlukem 				/* We are already chasing a referral or search reference and a
12032de962bdSlukem 				 * bind on that connection is in progress.  We must queue
12042de962bdSlukem 				 * referrals on that connection, so we don't get a request
12052de962bdSlukem 				 * going out before the bind operation completes. This happens
12062de962bdSlukem 				 * if two search references come in one behind the other
12072de962bdSlukem 				 * for the same server with different contexts.
12082de962bdSlukem 				 */
12092de962bdSlukem 				Debug( LDAP_DEBUG_TRACE,
12102de962bdSlukem 					"ldap_chase_v3referrals: queue referral \"%s\"\n",
12112de962bdSlukem 					refarray[i], 0, 0);
12122de962bdSlukem 				if( lc->lconn_rebind_queue == NULL ) {
12132de962bdSlukem 					/* Create a referral list */
12142de962bdSlukem 					lc->lconn_rebind_queue =
12152de962bdSlukem 						(char ***) LDAP_MALLOC( sizeof(void *) * 2);
12162de962bdSlukem 
12172de962bdSlukem 					if( lc->lconn_rebind_queue == NULL) {
12182de962bdSlukem 						ld->ld_errno = LDAP_NO_MEMORY;
12192de962bdSlukem 						rc = -1;
12202de962bdSlukem 						goto done;
12212de962bdSlukem 					}
12222de962bdSlukem 
12232de962bdSlukem 					lc->lconn_rebind_queue[0] = refarray;
12242de962bdSlukem 					lc->lconn_rebind_queue[1] = NULL;
12252de962bdSlukem 					refarray = NULL;
12262de962bdSlukem 
12272de962bdSlukem 				} else {
12282de962bdSlukem 					/* Count how many referral arrays we already have */
12292de962bdSlukem 					for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
12302de962bdSlukem 						/* empty */;
12312de962bdSlukem 					}
12322de962bdSlukem 
12332de962bdSlukem 					/* Add the new referral to the list */
12342de962bdSlukem 					lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
12352de962bdSlukem 						lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
12362de962bdSlukem 
12372de962bdSlukem 					if( lc->lconn_rebind_queue == NULL ) {
12382de962bdSlukem 						ld->ld_errno = LDAP_NO_MEMORY;
12392de962bdSlukem 						rc = -1;
12402de962bdSlukem 						goto done;
12412de962bdSlukem 					}
12422de962bdSlukem 					lc->lconn_rebind_queue[j] = refarray;
12432de962bdSlukem 					lc->lconn_rebind_queue[j+1] = NULL;
12442de962bdSlukem 					refarray = NULL;
12452de962bdSlukem 				}
12462de962bdSlukem 
12472de962bdSlukem 				/* We have queued the referral/reference, now just return */
12482de962bdSlukem 				rc = 0;
12492de962bdSlukem 				*hadrefp = 1;
12502de962bdSlukem 				count = 1; /* Pretend we already followed referral */
12512de962bdSlukem 				goto done;
12522de962bdSlukem 			}
12532de962bdSlukem 		}
12542de962bdSlukem 		/* Re-encode the request with the new starting point of the search.
12552de962bdSlukem 		 * Note: In the future we also need to replace the filter if one
12562de962bdSlukem 		 * was provided with the search reference
12572de962bdSlukem 		 */
12582de962bdSlukem 
12592de962bdSlukem 		/* For references we don't want old dn if new dn empty */
12602de962bdSlukem 		if ( sref && srv->lud_dn == NULL ) {
12612de962bdSlukem 			srv->lud_dn = LDAP_STRDUP( "" );
12622de962bdSlukem 		}
12632de962bdSlukem 
12642de962bdSlukem 		LDAP_NEXT_MSGID( ld, id );
12652de962bdSlukem 		ber = re_encode_request( ld, origreq->lr_ber, id,
12662de962bdSlukem 			sref, srv, &rinfo.ri_request );
12672de962bdSlukem 
12682de962bdSlukem 		if( ber == NULL ) {
12692de962bdSlukem 			ld->ld_errno = LDAP_ENCODING_ERROR;
12702de962bdSlukem 			rc = -1;
12712de962bdSlukem 			goto done;
12722de962bdSlukem 		}
12732de962bdSlukem 
12742de962bdSlukem 		Debug( LDAP_DEBUG_TRACE,
12752de962bdSlukem 			"ldap_chase_v3referral: msgid %d, url \"%s\"\n",
12762de962bdSlukem 			lr->lr_msgid, refarray[i], 0);
12772de962bdSlukem 
12782de962bdSlukem 		/* Send the new request to the server - may require a bind */
12792de962bdSlukem 		rinfo.ri_msgid = origreq->lr_origid;
12802de962bdSlukem 		rinfo.ri_url = refarray[i];
12812de962bdSlukem 		rc = ldap_send_server_request( ld, ber, id,
1282d11b170bStron 			origreq, &srv, NULL, &rinfo, 0, 1 );
12832de962bdSlukem 		if ( rc < 0 ) {
12842de962bdSlukem 			/* Failure, try next referral in the list */
12852de962bdSlukem 			Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n",
12862de962bdSlukem 				refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
12872de962bdSlukem 			unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
12882de962bdSlukem 			ldap_free_urllist( srv );
12892de962bdSlukem 			srv = NULL;
12902de962bdSlukem 			ld->ld_errno = LDAP_REFERRAL;
12912de962bdSlukem 		} else {
12922de962bdSlukem 			/* Success, no need to try this referral list further */
12932de962bdSlukem 			rc = 0;
12942de962bdSlukem 			++count;
12952de962bdSlukem 			*hadrefp = 1;
12962de962bdSlukem 
12972de962bdSlukem 			/* check if there is a queue of referrals that came in during bind */
12982de962bdSlukem 			if ( lc == NULL) {
12992de962bdSlukem 				lc = find_connection( ld, srv, 1 );
13002de962bdSlukem 				if ( lc == NULL ) {
13012de962bdSlukem 					ld->ld_errno = LDAP_OPERATIONS_ERROR;
13022de962bdSlukem 					rc = -1;
1303d11b170bStron 					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
13042de962bdSlukem 					goto done;
13052de962bdSlukem 				}
13062de962bdSlukem 			}
13072de962bdSlukem 
13082de962bdSlukem 			if ( lc->lconn_rebind_queue != NULL ) {
13092de962bdSlukem 				/* Release resources of previous list */
13102de962bdSlukem 				LDAP_VFREE( refarray );
13112de962bdSlukem 				refarray = NULL;
13122de962bdSlukem 				ldap_free_urllist( srv );
13132de962bdSlukem 				srv = NULL;
13142de962bdSlukem 
13152de962bdSlukem 				/* Pull entries off end of queue so list always null terminated */
13162de962bdSlukem 				for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
13172de962bdSlukem 					;
13182de962bdSlukem 				refarray = lc->lconn_rebind_queue[j - 1];
13192de962bdSlukem 				lc->lconn_rebind_queue[j-1] = NULL;
13202de962bdSlukem 				/* we pulled off last entry from queue, free queue */
13212de962bdSlukem 				if ( j == 1 ) {
13222de962bdSlukem 					LDAP_FREE( lc->lconn_rebind_queue );
13232de962bdSlukem 					lc->lconn_rebind_queue = NULL;
13242de962bdSlukem 				}
13252de962bdSlukem 				/* restart the loop the with new referral list */
13262de962bdSlukem 				i = -1;
13272de962bdSlukem 				continue;
13282de962bdSlukem 			}
13292de962bdSlukem 			break; /* referral followed, break out of for loop */
13302de962bdSlukem 		}
13312de962bdSlukem 	} /* end for loop */
13322de962bdSlukem done:
13332de962bdSlukem 	LDAP_VFREE( refarray );
13342de962bdSlukem 	ldap_free_urllist( srv );
13352de962bdSlukem 	LDAP_FREE( *errstrp );
13362de962bdSlukem 
13372de962bdSlukem 	if( rc == 0 ) {
13382de962bdSlukem 		*errstrp = NULL;
13392de962bdSlukem 		LDAP_FREE( unfollowed );
13402de962bdSlukem 		return count;
13412de962bdSlukem 	} else {
13422de962bdSlukem 		*errstrp = unfollowed;
13432de962bdSlukem 		return rc;
13442de962bdSlukem 	}
13452de962bdSlukem }
13462de962bdSlukem 
13472de962bdSlukem /*
13482de962bdSlukem  * XXX merging of errors in this routine needs to be improved
1349d11b170bStron  * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
13502de962bdSlukem  */
13512de962bdSlukem int
13522de962bdSlukem ldap_chase_referrals( LDAP *ld,
13532de962bdSlukem 	LDAPRequest *lr,
13542de962bdSlukem 	char **errstrp,
13552de962bdSlukem 	int sref,
13562de962bdSlukem 	int *hadrefp )
13572de962bdSlukem {
13582de962bdSlukem 	int		rc, count, id;
13592de962bdSlukem 	unsigned	len;
13602de962bdSlukem 	char		*p, *ref, *unfollowed;
13612de962bdSlukem 	LDAPRequest	*origreq;
13622de962bdSlukem 	LDAPURLDesc	*srv;
13632de962bdSlukem 	BerElement	*ber;
13642de962bdSlukem 	LDAPreqinfo  rinfo;
13652de962bdSlukem 	LDAPConn	*lc;
13662de962bdSlukem 
1367d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1368d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
1369d11b170bStron 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
13702de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
13712de962bdSlukem 
13722de962bdSlukem 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
13732de962bdSlukem 	*hadrefp = 0;
13742de962bdSlukem 
13752de962bdSlukem 	if ( *errstrp == NULL ) {
13762de962bdSlukem 		return( 0 );
13772de962bdSlukem 	}
13782de962bdSlukem 
13792de962bdSlukem 	len = strlen( *errstrp );
13802de962bdSlukem 	for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
13812de962bdSlukem 		if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
13822de962bdSlukem 			*p = '\0';
13832de962bdSlukem 			p += LDAP_REF_STR_LEN;
13842de962bdSlukem 			break;
13852de962bdSlukem 		}
13862de962bdSlukem 	}
13872de962bdSlukem 
13882de962bdSlukem 	if ( len < LDAP_REF_STR_LEN ) {
13892de962bdSlukem 		return( 0 );
13902de962bdSlukem 	}
13912de962bdSlukem 
13922de962bdSlukem 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
13932de962bdSlukem 		Debug( LDAP_DEBUG_ANY,
13942de962bdSlukem 		    "more than %d referral hops (dropping)\n",
13952de962bdSlukem 		    ld->ld_refhoplimit, 0, 0 );
13962de962bdSlukem 		    /* XXX report as error in ld->ld_errno? */
13972de962bdSlukem 		    return( 0 );
13982de962bdSlukem 	}
13992de962bdSlukem 
14002de962bdSlukem 	/* find original request */
14012de962bdSlukem 	for ( origreq = lr; origreq->lr_parent != NULL;
14022de962bdSlukem 	     origreq = origreq->lr_parent ) {
14032de962bdSlukem 		/* empty */;
14042de962bdSlukem 	}
14052de962bdSlukem 
14062de962bdSlukem 	unfollowed = NULL;
14072de962bdSlukem 	rc = count = 0;
14082de962bdSlukem 
14092de962bdSlukem 	/* parse out & follow referrals */
14102de962bdSlukem 	for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
14112de962bdSlukem 		p = strchr( ref, '\n' );
14122de962bdSlukem 		if ( p != NULL ) {
14132de962bdSlukem 			*p++ = '\0';
14142de962bdSlukem 		}
14152de962bdSlukem 
14162de962bdSlukem 		rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
14172de962bdSlukem 		if ( rc != LDAP_URL_SUCCESS ) {
14182de962bdSlukem 			Debug( LDAP_DEBUG_TRACE,
14192de962bdSlukem 				"ignoring %s referral <%s>\n",
14202de962bdSlukem 				ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 );
14212de962bdSlukem 			rc = ldap_append_referral( ld, &unfollowed, ref );
14222de962bdSlukem 			*hadrefp = 1;
14232de962bdSlukem 			continue;
14242de962bdSlukem 		}
14252de962bdSlukem 
14262de962bdSlukem 		Debug( LDAP_DEBUG_TRACE,
14272de962bdSlukem 		    "chasing LDAP referral: <%s>\n", ref, 0, 0 );
14282de962bdSlukem 
14292de962bdSlukem 		*hadrefp = 1;
14302de962bdSlukem 
14312de962bdSlukem 		/* See if we've already been here */
14322de962bdSlukem 		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
14332de962bdSlukem 			LDAPRequest *lp;
14342de962bdSlukem 			int looped = 0;
14354e6df137Slukem 			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
14362de962bdSlukem 			for ( lp = lr; lp; lp = lp->lr_parent ) {
14372de962bdSlukem 				if ( lp->lr_conn == lc
14382de962bdSlukem 					&& len == lp->lr_dn.bv_len )
14392de962bdSlukem 				{
14402de962bdSlukem 					if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
14412de962bdSlukem 							continue;
14422de962bdSlukem 					looped = 1;
14432de962bdSlukem 					break;
14442de962bdSlukem 				}
14452de962bdSlukem 			}
14462de962bdSlukem 			if ( looped ) {
14472de962bdSlukem 				ldap_free_urllist( srv );
14482de962bdSlukem 				ld->ld_errno = LDAP_CLIENT_LOOP;
14492de962bdSlukem 				rc = -1;
14502de962bdSlukem 				continue;
14512de962bdSlukem 			}
14522de962bdSlukem 		}
14532de962bdSlukem 
14542de962bdSlukem 		LDAP_NEXT_MSGID( ld, id );
14552de962bdSlukem 		ber = re_encode_request( ld, origreq->lr_ber,
14562de962bdSlukem 		    id, sref, srv, &rinfo.ri_request );
14572de962bdSlukem 
14582de962bdSlukem 		if ( ber == NULL ) {
1459376af7d7Schristos 			ldap_free_urllist( srv );
14602de962bdSlukem 			return -1 ;
14612de962bdSlukem 		}
14622de962bdSlukem 
14632de962bdSlukem 		/* copy the complete referral for rebind process */
14642de962bdSlukem 		rinfo.ri_url = LDAP_STRDUP( ref );
14652de962bdSlukem 
14662de962bdSlukem 		rinfo.ri_msgid = origreq->lr_origid;
14672de962bdSlukem 
14682de962bdSlukem 		rc = ldap_send_server_request( ld, ber, id,
1469d11b170bStron 			lr, &srv, NULL, &rinfo, 0, 1 );
14702de962bdSlukem 		LDAP_FREE( rinfo.ri_url );
14712de962bdSlukem 
14722de962bdSlukem 		if( rc >= 0 ) {
14732de962bdSlukem 			++count;
14742de962bdSlukem 		} else {
14752de962bdSlukem 			Debug( LDAP_DEBUG_ANY,
14762de962bdSlukem 				"Unable to chase referral \"%s\" (%d: %s)\n",
14772de962bdSlukem 				ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
14782de962bdSlukem 			rc = ldap_append_referral( ld, &unfollowed, ref );
14792de962bdSlukem 		}
14802de962bdSlukem 
14812de962bdSlukem 		ldap_free_urllist(srv);
14822de962bdSlukem 	}
14832de962bdSlukem 
14842de962bdSlukem 	LDAP_FREE( *errstrp );
14852de962bdSlukem 	*errstrp = unfollowed;
14862de962bdSlukem 
14872de962bdSlukem 	return(( rc == 0 ) ? count : rc );
14882de962bdSlukem }
14892de962bdSlukem 
14902de962bdSlukem 
14912de962bdSlukem int
14922de962bdSlukem ldap_append_referral( LDAP *ld, char **referralsp, char *s )
14932de962bdSlukem {
14942de962bdSlukem 	int	first;
14952de962bdSlukem 
14962de962bdSlukem 	if ( *referralsp == NULL ) {
14972de962bdSlukem 		first = 1;
14982de962bdSlukem 		*referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
14992de962bdSlukem 		    + 1 );
15002de962bdSlukem 	} else {
15012de962bdSlukem 		first = 0;
15022de962bdSlukem 		*referralsp = (char *)LDAP_REALLOC( *referralsp,
15032de962bdSlukem 		    strlen( *referralsp ) + strlen( s ) + 2 );
15042de962bdSlukem 	}
15052de962bdSlukem 
15062de962bdSlukem 	if ( *referralsp == NULL ) {
15072de962bdSlukem 		ld->ld_errno = LDAP_NO_MEMORY;
15082de962bdSlukem 		return( -1 );
15092de962bdSlukem 	}
15102de962bdSlukem 
15112de962bdSlukem 	if ( first ) {
15122de962bdSlukem 		strcpy( *referralsp, LDAP_REF_STR );
15132de962bdSlukem 	} else {
15142de962bdSlukem 		strcat( *referralsp, "\n" );
15152de962bdSlukem 	}
15162de962bdSlukem 	strcat( *referralsp, s );
15172de962bdSlukem 
15182de962bdSlukem 	return( 0 );
15192de962bdSlukem }
15202de962bdSlukem 
15212de962bdSlukem 
15222de962bdSlukem 
15232de962bdSlukem static BerElement *
15242de962bdSlukem re_encode_request( LDAP *ld,
15252de962bdSlukem 	BerElement *origber,
15262de962bdSlukem 	ber_int_t msgid,
15272de962bdSlukem 	int sref,
15282de962bdSlukem 	LDAPURLDesc *srv,
15292de962bdSlukem 	int *type )
15302de962bdSlukem {
15312de962bdSlukem 	/*
15322de962bdSlukem 	 * XXX this routine knows way too much about how the lber library works!
15332de962bdSlukem 	 */
15342de962bdSlukem 	ber_int_t	along;
15352de962bdSlukem 	ber_tag_t	tag;
15362de962bdSlukem 	ber_tag_t	rtag;
15372de962bdSlukem 	ber_int_t	ver;
15382de962bdSlukem 	ber_int_t	scope;
15392de962bdSlukem 	int		rc;
15402de962bdSlukem 	BerElement	tmpber, *ber;
15412de962bdSlukem 	struct berval		dn;
15422de962bdSlukem 
15432de962bdSlukem 	Debug( LDAP_DEBUG_TRACE,
15442de962bdSlukem 	    "re_encode_request: new msgid %ld, new dn <%s>\n",
15452de962bdSlukem 	    (long) msgid,
15462de962bdSlukem 		( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn, 0 );
15472de962bdSlukem 
15482de962bdSlukem 	tmpber = *origber;
15492de962bdSlukem 
15502de962bdSlukem 	/*
15512de962bdSlukem 	 * all LDAP requests are sequences that start with a message id.
15522de962bdSlukem 	 * For all except delete, this is followed by a sequence that is
15532de962bdSlukem 	 * tagged with the operation code.  For delete, the provided DN
15542de962bdSlukem 	 * is not wrapped by a sequence.
15552de962bdSlukem 	 */
15562de962bdSlukem 	rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
15572de962bdSlukem 
15582de962bdSlukem 	if ( rtag == LBER_ERROR ) {
15592de962bdSlukem 		ld->ld_errno = LDAP_DECODING_ERROR;
15602de962bdSlukem 		return( NULL );
15612de962bdSlukem 	}
15622de962bdSlukem 
15632de962bdSlukem 	assert( tag != 0);
15642de962bdSlukem 	if ( tag == LDAP_REQ_BIND ) {
15652de962bdSlukem 		/* bind requests have a version number before the DN & other stuff */
15662de962bdSlukem 		rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn );
15672de962bdSlukem 
15682de962bdSlukem 	} else if ( tag == LDAP_REQ_DELETE ) {
15692de962bdSlukem 		/* delete requests don't have a DN wrapping sequence */
15702de962bdSlukem 		rtag = ber_scanf( &tmpber, "m", &dn );
15712de962bdSlukem 
15722de962bdSlukem 	} else if ( tag == LDAP_REQ_SEARCH ) {
15732de962bdSlukem 		/* search requests need to be re-scope-ed */
15742de962bdSlukem 		rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope );
15752de962bdSlukem 
15762de962bdSlukem 		if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
15772de962bdSlukem 			/* use the scope provided in reference */
15782de962bdSlukem 			scope = srv->lud_scope;
15792de962bdSlukem 
15802de962bdSlukem 		} else if ( sref ) {
15812de962bdSlukem 			/* use scope implied by previous operation
15822de962bdSlukem 			 *   base -> base
15832de962bdSlukem 			 *   one -> base
15842de962bdSlukem 			 *   subtree -> subtree
15852de962bdSlukem 			 *   subordinate -> subtree
15862de962bdSlukem 			 */
15872de962bdSlukem 			switch( scope ) {
15882de962bdSlukem 			default:
15892de962bdSlukem 			case LDAP_SCOPE_BASE:
15902de962bdSlukem 			case LDAP_SCOPE_ONELEVEL:
15912de962bdSlukem 				scope = LDAP_SCOPE_BASE;
15922de962bdSlukem 				break;
15932de962bdSlukem 			case LDAP_SCOPE_SUBTREE:
15942de962bdSlukem 			case LDAP_SCOPE_SUBORDINATE:
15952de962bdSlukem 				scope = LDAP_SCOPE_SUBTREE;
15962de962bdSlukem 				break;
15972de962bdSlukem 			}
15982de962bdSlukem 		}
15992de962bdSlukem 
16002de962bdSlukem 	} else {
16012de962bdSlukem 		rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn );
16022de962bdSlukem 	}
16032de962bdSlukem 
16042de962bdSlukem 	if( rtag == LBER_ERROR ) {
16052de962bdSlukem 		ld->ld_errno = LDAP_DECODING_ERROR;
16062de962bdSlukem 		return NULL;
16072de962bdSlukem 	}
16082de962bdSlukem 
16092de962bdSlukem 	/* restore character zero'd out by ber_scanf*/
16102de962bdSlukem 	dn.bv_val[dn.bv_len] = tmpber.ber_tag;
16112de962bdSlukem 
16122de962bdSlukem 	if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
16132de962bdSlukem 		return NULL;
16142de962bdSlukem 	}
16152de962bdSlukem 
16162de962bdSlukem 	if ( srv->lud_dn ) {
16172de962bdSlukem 		ber_str2bv( srv->lud_dn, 0, 0, &dn );
16182de962bdSlukem 	}
16192de962bdSlukem 
16202de962bdSlukem 	if ( tag == LDAP_REQ_BIND ) {
16212de962bdSlukem 		rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn );
16222de962bdSlukem 	} else if ( tag == LDAP_REQ_DELETE ) {
16232de962bdSlukem 		rc = ber_printf( ber, "{itON}", msgid, tag, &dn );
16242de962bdSlukem 	} else if ( tag == LDAP_REQ_SEARCH ) {
16252de962bdSlukem 		rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope );
16262de962bdSlukem 	} else {
16272de962bdSlukem 		rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn );
16282de962bdSlukem 	}
16292de962bdSlukem 
16302de962bdSlukem 	if ( rc == -1 ) {
16312de962bdSlukem 		ld->ld_errno = LDAP_ENCODING_ERROR;
16322de962bdSlukem 		ber_free( ber, 1 );
16332de962bdSlukem 		return NULL;
16342de962bdSlukem 	}
16352de962bdSlukem 
16362de962bdSlukem 	if ( tag != LDAP_REQ_DELETE && (
16372de962bdSlukem 		ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
16382de962bdSlukem 		!= ( tmpber.ber_end - tmpber.ber_ptr ) ||
16392de962bdSlukem 	    ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
16402de962bdSlukem 	{
16412de962bdSlukem 		ld->ld_errno = LDAP_ENCODING_ERROR;
16422de962bdSlukem 		ber_free( ber, 1 );
16432de962bdSlukem 		return NULL;
16442de962bdSlukem 	}
16452de962bdSlukem 
16462de962bdSlukem #ifdef LDAP_DEBUG
16472de962bdSlukem 	if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
16482de962bdSlukem 		Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
16492de962bdSlukem 		    0, 0, 0 );
16502de962bdSlukem 		ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
16512de962bdSlukem 	}
16522de962bdSlukem #endif /* LDAP_DEBUG */
16532de962bdSlukem 
16542de962bdSlukem 	*type = tag;	/* return request type */
16552de962bdSlukem 	return ber;
16562de962bdSlukem }
16572de962bdSlukem 
16582de962bdSlukem 
1659d11b170bStron /* protected by req_mutex */
16602de962bdSlukem LDAPRequest *
16612de962bdSlukem ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
16622de962bdSlukem {
16632de962bdSlukem 	LDAPRequest	*lr;
16642de962bdSlukem 
16652de962bdSlukem 	for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
16662de962bdSlukem 		if ( lr->lr_status == LDAP_REQST_COMPLETED ) {
16672de962bdSlukem 			continue;	/* Skip completed requests */
16682de962bdSlukem 		}
16692de962bdSlukem 		if ( msgid == lr->lr_msgid ) {
16702de962bdSlukem 			lr->lr_refcnt++;
16712de962bdSlukem 			break;
16722de962bdSlukem 		}
16732de962bdSlukem 	}
16742de962bdSlukem 
16752de962bdSlukem 	return( lr );
16762de962bdSlukem }
16772de962bdSlukem 
1678d11b170bStron /* protected by req_mutex */
16792de962bdSlukem void
16802de962bdSlukem ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
16812de962bdSlukem {
16822de962bdSlukem 	LDAPRequest	*lr;
16832de962bdSlukem 
16842de962bdSlukem 	for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
16852de962bdSlukem 		if ( lr == lrx ) {
16862de962bdSlukem 			if ( lr->lr_refcnt > 0 ) {
16872de962bdSlukem 				lr->lr_refcnt--;
16882de962bdSlukem 
16892de962bdSlukem 			} else if ( lr->lr_refcnt < 0 ) {
16902de962bdSlukem 				lr->lr_refcnt++;
16912de962bdSlukem 				if ( lr->lr_refcnt == 0 ) {
16922de962bdSlukem 					lr = NULL;
16932de962bdSlukem 				}
16942de962bdSlukem 			}
16952de962bdSlukem 			break;
16962de962bdSlukem 		}
16972de962bdSlukem 	}
16982de962bdSlukem 	if ( lr == NULL ) {
16992de962bdSlukem 		ldap_free_request_int( ld, lrx );
17002de962bdSlukem 
17012de962bdSlukem 	} else if ( freeit ) {
17022de962bdSlukem 		ldap_free_request( ld, lrx );
17032de962bdSlukem 	}
17042de962bdSlukem }
1705