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