xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-meta/conn.c (revision 96230fab84e26a6435963032070e916a951a8b2e)
1 /* $OpenLDAP: pkg/ldap/servers/slapd/back-meta/conn.c,v 1.86.2.15 2008/04/14 21:19:57 quanah Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2008 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
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 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22 
23 #include "portable.h"
24 
25 #include <stdio.h>
26 
27 #include <ac/errno.h>
28 #include <ac/socket.h>
29 #include <ac/string.h>
30 
31 
32 #define AVL_INTERNAL
33 #include "slap.h"
34 #include "../back-ldap/back-ldap.h"
35 #include "back-meta.h"
36 
37 /*
38  * meta_back_conndn_cmp
39  *
40  * compares two struct metaconn based on the value of the conn pointer
41  * and of the local DN; used by avl stuff
42  */
43 int
44 meta_back_conndn_cmp(
45 	const void *c1,
46 	const void *c2 )
47 {
48 	metaconn_t	*mc1 = ( metaconn_t * )c1;
49         metaconn_t	*mc2 = ( metaconn_t * )c2;
50 	int		rc;
51 
52 	/* If local DNs don't match, it is definitely not a match */
53 	/* For shared sessions, conn is NULL. Only explicitly
54 	 * bound sessions will have non-NULL conn.
55 	 */
56 	rc = SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
57 	if ( rc == 0 ) {
58 		rc = ber_bvcmp( &mc1->mc_local_ndn, &mc2->mc_local_ndn );
59 	}
60 
61 	return rc;
62 }
63 
64 /*
65  * meta_back_conndnmc_cmp
66  *
67  * compares two struct metaconn based on the value of the conn pointer,
68  * the local DN and the struct pointer; used by avl stuff
69  */
70 static int
71 meta_back_conndnmc_cmp(
72 	const void *c1,
73 	const void *c2 )
74 {
75 	metaconn_t	*mc1 = ( metaconn_t * )c1;
76         metaconn_t	*mc2 = ( metaconn_t * )c2;
77 	int		rc;
78 
79 	/* If local DNs don't match, it is definitely not a match */
80 	/* For shared sessions, conn is NULL. Only explicitly
81 	 * bound sessions will have non-NULL conn.
82 	 */
83 	rc = SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
84 	if ( rc == 0 ) {
85 		rc = ber_bvcmp( &mc1->mc_local_ndn, &mc2->mc_local_ndn );
86 		if ( rc == 0 ) {
87 			rc = SLAP_PTRCMP( mc1, mc2 );
88 		}
89 	}
90 
91 	return rc;
92 }
93 
94 /*
95  * meta_back_conn_cmp
96  *
97  * compares two struct metaconn based on the value of the conn pointer;
98  * used by avl stuff
99  */
100 int
101 meta_back_conn_cmp(
102 	const void *c1,
103 	const void *c2 )
104 {
105 	metaconn_t	*mc1 = ( metaconn_t * )c1;
106         metaconn_t	*mc2 = ( metaconn_t * )c2;
107 
108 	/* For shared sessions, conn is NULL. Only explicitly
109 	 * bound sessions will have non-NULL conn.
110 	 */
111 	return SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
112 }
113 
114 /*
115  * meta_back_conndn_dup
116  *
117  * returns -1 in case a duplicate struct metaconn has been inserted;
118  * used by avl stuff
119  */
120 int
121 meta_back_conndn_dup(
122 	void *c1,
123 	void *c2 )
124 {
125 	metaconn_t	*mc1 = ( metaconn_t * )c1;
126 	metaconn_t	*mc2 = ( metaconn_t * )c2;
127 
128 	/* Cannot have more than one shared session with same DN */
129 	if ( mc1->mc_conn == mc2->mc_conn &&
130 		dn_match( &mc1->mc_local_ndn, &mc2->mc_local_ndn ) )
131 	{
132 		return -1;
133 	}
134 
135 	return 0;
136 }
137 
138 /*
139  * Debug stuff (got it from libavl)
140  */
141 #if META_BACK_PRINT_CONNTREE > 0
142 static void
143 meta_back_print( metaconn_t *mc, char *avlstr )
144 {
145 	int	i;
146 
147 	fputs( "targets=[", stderr );
148 	for ( i = 0; i < mc->mc_info->mi_ntargets; i++ ) {
149 		fputc( mc->mc_conns[ i ].msc_ld ? '*' : 'o', stderr);
150 	}
151 	fputc( ']', stderr );
152 
153 	fprintf( stderr, " mc=%p local=\"%s\" conn=%p refcnt=%d%s %s\n",
154 		(void *)mc,
155 		mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "",
156 		(void *)mc->mc_conn,
157 		mc->mc_refcnt,
158 		LDAP_BACK_CONN_TAINTED( mc ) ? " tainted" : "",
159 		avlstr );
160 }
161 
162 static void
163 meta_back_ravl_print( Avlnode *root, int depth )
164 {
165 	int     	i;
166 
167 	if ( root == 0 ) {
168 		return;
169 	}
170 
171 	meta_back_ravl_print( root->avl_right, depth + 1 );
172 
173 	for ( i = 0; i < depth; i++ ) {
174 		fprintf( stderr, "-" );
175 	}
176 	fputc( ' ', stderr );
177 
178 	meta_back_print( (metaconn_t *)root->avl_data,
179 		avl_bf2str( root->avl_bf ) );
180 
181 	meta_back_ravl_print( root->avl_left, depth + 1 );
182 }
183 
184 /* NOTE: duplicate from back-ldap/bind.c */
185 static char* priv2str[] = {
186 	"privileged",
187 	"privileged/TLS",
188 	"anonymous",
189 	"anonymous/TLS",
190 	"bind",
191 	"bind/TLS",
192 	NULL
193 };
194 
195 void
196 meta_back_print_conntree( metainfo_t *mi, char *msg )
197 {
198 	int	c;
199 
200 	fprintf( stderr, "========> %s\n", msg );
201 
202 	for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
203 		int		i = 0;
204 		metaconn_t	*mc;
205 
206 		fprintf( stderr, "  %s[%d]\n", priv2str[ c ], mi->mi_conn_priv[ c ].mic_num );
207 
208 		LDAP_TAILQ_FOREACH( mc, &mi->mi_conn_priv[ c ].mic_priv, mc_q )
209 		{
210 			fprintf( stderr, "    [%d] ", i );
211 			meta_back_print( mc, "" );
212 			i++;
213 		}
214 	}
215 
216 	if ( mi->mi_conninfo.lai_tree == NULL ) {
217 		fprintf( stderr, "\t(empty)\n" );
218 
219 	} else {
220 		meta_back_ravl_print( mi->mi_conninfo.lai_tree, 0 );
221 	}
222 
223 	fprintf( stderr, "<======== %s\n", msg );
224 }
225 #endif /* META_BACK_PRINT_CONNTREE */
226 /*
227  * End of debug stuff
228  */
229 
230 /*
231  * metaconn_alloc
232  *
233  * Allocates a connection structure, making room for all the referenced targets
234  */
235 static metaconn_t *
236 metaconn_alloc(
237        	Operation 		*op )
238 {
239 	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;
240 	metaconn_t	*mc;
241 	int		ntargets = mi->mi_ntargets;
242 
243 	assert( ntargets > 0 );
244 
245 	/* malloc all in one */
246 	mc = ( metaconn_t * )ch_calloc( 1, sizeof( metaconn_t )
247 		+ sizeof( metasingleconn_t ) * ( ntargets - 1 ) );
248 	if ( mc == NULL ) {
249 		return NULL;
250 	}
251 
252 	mc->mc_info = mi;
253 
254 	mc->mc_authz_target = META_BOUND_NONE;
255 	mc->mc_refcnt = 1;
256 
257 	return mc;
258 }
259 
260 /*
261  * meta_back_init_one_conn
262  *
263  * Initializes one connection
264  */
265 int
266 meta_back_init_one_conn(
267 	Operation		*op,
268 	SlapReply		*rs,
269 	metaconn_t		*mc,
270 	int			candidate,
271 	int			ispriv,
272 	ldap_back_send_t	sendok,
273 	int			dolock )
274 {
275 	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
276 	metatarget_t		*mt = mi->mi_targets[ candidate ];
277 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
278 	int			version;
279 	dncookie		dc;
280 	int			isauthz = ( candidate == mc->mc_authz_target );
281 	int			do_return = 0;
282 #ifdef HAVE_TLS
283 	int			is_ldaps = 0;
284 #endif /* HAVE_TLS */
285 
286 	/* if the server is quarantined, and
287 	 * - the current interval did not expire yet, or
288 	 * - no more retries should occur,
289 	 * don't return the connection */
290 	if ( mt->mt_isquarantined ) {
291 		slap_retry_info_t	*ri = &mt->mt_quarantine;
292 		int			dont_retry = 1;
293 
294 		if ( mt->mt_quarantine.ri_interval ) {
295 			ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
296 			if ( mt->mt_isquarantined == LDAP_BACK_FQ_YES ) {
297 				dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
298 					|| slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
299 				if ( !dont_retry ) {
300 					if ( LogTest( LDAP_DEBUG_ANY ) ) {
301 						char	buf[ SLAP_TEXT_BUFLEN ];
302 
303 						snprintf( buf, sizeof( buf ),
304 							"meta_back_init_one_conn[%d]: quarantine "
305 							"retry block #%d try #%d",
306 							candidate, ri->ri_idx, ri->ri_count );
307 						Debug( LDAP_DEBUG_ANY, "%s %s.\n",
308 							op->o_log_prefix, buf, 0 );
309 					}
310 				}
311 
312 				mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
313 			}
314 			ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
315 		}
316 
317 		if ( dont_retry ) {
318 			rs->sr_err = LDAP_UNAVAILABLE;
319 			if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
320 				rs->sr_text = "Target is quarantined";
321 				send_ldap_result( op, rs );
322 			}
323 			return rs->sr_err;
324 		}
325 	}
326 
327 retry_lock:;
328 	if ( dolock ) {
329 		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
330 	}
331 
332 	/*
333 	 * Already init'ed
334 	 */
335 	if ( LDAP_BACK_CONN_ISBOUND( msc )
336 		|| LDAP_BACK_CONN_ISANON( msc ) )
337 	{
338 		assert( msc->msc_ld != NULL );
339 		rs->sr_err = LDAP_SUCCESS;
340 		do_return = 1;
341 
342 	} else if ( META_BACK_CONN_CREATING( msc )
343 		|| LDAP_BACK_CONN_BINDING( msc ) )
344 	{
345 		if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
346 			if ( dolock ) {
347 				ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
348 			}
349 
350 			ldap_pvt_thread_yield();
351 			goto retry_lock;
352 		}
353 
354 		/* sounds more appropriate */
355 		rs->sr_err = LDAP_BUSY;
356 		rs->sr_text = "No connections to target are available";
357 		do_return = 1;
358 
359 	} else if ( META_BACK_CONN_INITED( msc ) ) {
360 		assert( msc->msc_ld != NULL );
361 		rs->sr_err = LDAP_SUCCESS;
362 		do_return = 1;
363 
364 	} else {
365 		/*
366 		 * creating...
367 		 */
368 		META_BACK_CONN_CREATING_SET( msc );
369 	}
370 
371 	if ( dolock ) {
372 		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
373 	}
374 
375 	if ( do_return ) {
376 		if ( rs->sr_err != LDAP_SUCCESS
377 			&& op->o_conn
378 			&& ( sendok & LDAP_BACK_SENDERR ) )
379 		{
380 			send_ldap_result( op, rs );
381 		}
382 
383 		return rs->sr_err;
384 	}
385 
386 	assert( msc->msc_ld == NULL );
387 
388 	/*
389 	 * Attempts to initialize the connection to the target ds
390 	 */
391 	ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
392 	rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
393 #ifdef HAVE_TLS
394 	is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
395 #endif /* HAVE_TLS */
396 	ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
397 	if ( rs->sr_err != LDAP_SUCCESS ) {
398 		goto error_return;
399 	}
400 
401 	/*
402 	 * Set LDAP version. This will always succeed: If the client
403 	 * bound with a particular version, then so can we.
404 	 */
405 	if ( mt->mt_version != 0 ) {
406 		version = mt->mt_version;
407 
408 	} else if ( op->o_conn->c_protocol != 0 ) {
409 		version = op->o_conn->c_protocol;
410 
411 	} else {
412 		version = LDAP_VERSION3;
413 	}
414 	ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &version );
415 	ldap_set_urllist_proc( msc->msc_ld, mt->mt_urllist_f, mt->mt_urllist_p );
416 
417 	/* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
418 	ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS,
419 		LDAP_BACK_CHASE_REFERRALS( mi ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
420 
421 #ifdef HAVE_TLS
422 	/* start TLS ("tls [try-]{start|propagate}" statement) */
423 	if ( ( LDAP_BACK_USE_TLS( mi )
424 		|| ( op->o_conn->c_is_tls
425 			&& LDAP_BACK_PROPAGATE_TLS( mi ) ) )
426 		&& !is_ldaps )
427 	{
428 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
429 		/*
430 		 * use asynchronous StartTLS; in case, chase referral
431 		 * FIXME: OpenLDAP does not return referral on StartTLS yet
432 		 */
433 		int		msgid;
434 
435 		rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
436 		if ( rs->sr_err == LDAP_SUCCESS ) {
437 			LDAPMessage	*res = NULL;
438 			int		rc, nretries = mt->mt_nretries;
439 			struct timeval	tv;
440 
441 			LDAP_BACK_TV_SET( &tv );
442 
443 retry:;
444 			rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
445 			switch ( rc ) {
446 			case -1:
447 				rs->sr_err = LDAP_OTHER;
448 				break;
449 
450 			case 0:
451 				if ( nretries != 0 ) {
452 					if ( nretries > 0 ) {
453 						nretries--;
454 					}
455 					LDAP_BACK_TV_SET( &tv );
456 					goto retry;
457 				}
458 				rs->sr_err = LDAP_OTHER;
459 				break;
460 
461 			default:
462 				/* only touch when activity actually took place... */
463 				if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
464 					msc->msc_time = op->o_time;
465 				}
466 				break;
467 			}
468 
469 			if ( rc == LDAP_RES_EXTENDED ) {
470 				struct berval	*data = NULL;
471 
472 				/* NOTE: right now, data is unused, so don't get it */
473 				rs->sr_err = ldap_parse_extended_result( msc->msc_ld,
474 					res, NULL, NULL /* &data */ , 0 );
475 				if ( rs->sr_err == LDAP_SUCCESS ) {
476 					int		err;
477 
478 					/* FIXME: matched? referrals? response controls? */
479 					rs->sr_err = ldap_parse_result( msc->msc_ld,
480 						res, &err, NULL, NULL, NULL, NULL, 1 );
481 					res = NULL;
482 
483 					if ( rs->sr_err == LDAP_SUCCESS ) {
484 						rs->sr_err = err;
485 					}
486 
487 					/* FIXME: in case a referral
488 					 * is returned, should we try
489 					 * using it instead of the
490 					 * configured URI? */
491 					if ( rs->sr_err == LDAP_SUCCESS ) {
492 						ldap_install_tls( msc->msc_ld );
493 
494 					} else if ( rs->sr_err == LDAP_REFERRAL ) {
495 						/* FIXME: LDAP_OPERATIONS_ERROR? */
496 						rs->sr_err = LDAP_OTHER;
497 						rs->sr_text = "Unwilling to chase referral "
498 							"returned by Start TLS exop";
499 					}
500 
501 					if ( data ) {
502 						ber_bvfree( data );
503 					}
504 				}
505 
506 			} else {
507 				rs->sr_err = LDAP_OTHER;
508 			}
509 
510 			if ( res != NULL ) {
511 				ldap_msgfree( res );
512 			}
513 		}
514 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
515 		/*
516 		 * use synchronous StartTLS
517 		 */
518 		rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
519 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
520 
521 		/* if StartTLS is requested, only attempt it if the URL
522 		 * is not "ldaps://"; this may occur not only in case
523 		 * of misconfiguration, but also when used in the chain
524 		 * overlay, where the "uri" can be parsed out of a referral */
525 		if ( rs->sr_err == LDAP_SERVER_DOWN
526 			|| ( rs->sr_err != LDAP_SUCCESS
527 				&& LDAP_BACK_TLS_CRITICAL( mi ) ) )
528 		{
529 
530 #ifdef DEBUG_205
531 			Debug( LDAP_DEBUG_ANY,
532 				"### %s meta_back_init_one_conn(TLS) "
533 				"ldap_unbind_ext[%d] ld=%p\n",
534 				op->o_log_prefix, candidate,
535 				(void *)msc->msc_ld );
536 #endif /* DEBUG_205 */
537 
538 			/* need to trash a failed Start TLS */
539 			meta_clear_one_candidate( op, mc, candidate );
540 			goto error_return;
541 		}
542 	}
543 #endif /* HAVE_TLS */
544 
545 	/*
546 	 * Set the network timeout if set
547 	 */
548 	if ( mt->mt_network_timeout != 0 ) {
549 		struct timeval	network_timeout;
550 
551 		network_timeout.tv_usec = 0;
552 		network_timeout.tv_sec = mt->mt_network_timeout;
553 
554 		ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
555 				(void *)&network_timeout );
556 	}
557 
558 	/*
559 	 * If the connection DN is not null, an attempt to rewrite it is made
560 	 */
561 
562 	if ( ispriv ) {
563 		if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
564 			ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
565 			if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
566 				if ( !BER_BVISNULL( &msc->msc_cred ) ) {
567 					memset( msc->msc_cred.bv_val, 0,
568 						msc->msc_cred.bv_len );
569 				}
570 				ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
571 			}
572 
573 		} else {
574 			ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
575 		}
576 
577 	} else {
578 		if ( !BER_BVISNULL( &msc->msc_cred ) ) {
579 			memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
580 			ber_memfree_x( msc->msc_cred.bv_val, NULL );
581 			BER_BVZERO( &msc->msc_cred );
582 		}
583 		if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
584 			ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
585 			BER_BVZERO( &msc->msc_bound_ndn );
586 		}
587 		if ( !BER_BVISEMPTY( &op->o_ndn )
588 			&& SLAP_IS_AUTHZ_BACKEND( op )
589 			&& isauthz )
590 		{
591 			dc.target = mt;
592 			dc.conn = op->o_conn;
593 			dc.rs = rs;
594 			dc.ctx = "bindDN";
595 
596 			/*
597 			 * Rewrite the bind dn if needed
598 			 */
599 			if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
600 						&msc->msc_bound_ndn ) )
601 			{
602 
603 #ifdef DEBUG_205
604 				Debug( LDAP_DEBUG_ANY,
605 					"### %s meta_back_init_one_conn(rewrite) "
606 					"ldap_unbind_ext[%d] ld=%p\n",
607 					op->o_log_prefix, candidate,
608 					(void *)msc->msc_ld );
609 #endif /* DEBUG_205 */
610 
611 				/* need to trash a connection not fully established */
612 				meta_clear_one_candidate( op, mc, candidate );
613 				goto error_return;
614 			}
615 
616 			/* copy the DN if needed */
617 			if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
618 				ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
619 			}
620 
621 			assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
622 
623 		} else {
624 			ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
625 		}
626 	}
627 
628 	assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
629 
630 error_return:;
631 	if ( dolock ) {
632 		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
633 	}
634 	META_BACK_CONN_CREATING_CLEAR( msc );
635 	if ( rs->sr_err == LDAP_SUCCESS ) {
636 		/*
637 		 * Sets a cookie for the rewrite session
638 		 */
639 		( void )rewrite_session_init( mt->mt_rwmap.rwm_rw, op->o_conn );
640 		META_BACK_CONN_INITED_SET( msc );
641 	}
642 	if ( dolock ) {
643 		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
644 	}
645 
646 	if ( rs->sr_err != LDAP_SUCCESS ) {
647 		rs->sr_err = slap_map_api2result( rs );
648 		if ( sendok & LDAP_BACK_SENDERR ) {
649 			send_ldap_result( op, rs );
650 		}
651 	}
652 
653 	return rs->sr_err;
654 }
655 
656 /*
657  * meta_back_retry
658  *
659  * Retries one connection
660  */
661 int
662 meta_back_retry(
663 	Operation		*op,
664 	SlapReply		*rs,
665 	metaconn_t		**mcp,
666 	int			candidate,
667 	ldap_back_send_t	sendok )
668 {
669 	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
670 	metatarget_t		*mt = mi->mi_targets[ candidate ];
671 	metaconn_t		*mc = *mcp;
672 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
673 	int			rc = LDAP_UNAVAILABLE,
674 				binding,
675 				quarantine = 1;
676 
677 	ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
678 
679 	assert( !META_BACK_CONN_CREATING( msc ) );
680 	binding = LDAP_BACK_CONN_BINDING( msc );
681 	LDAP_BACK_CONN_BINDING_CLEAR( msc );
682 
683 	assert( mc->mc_refcnt > 0 );
684 	if ( mc->mc_refcnt == 1 ) {
685 		if ( LogTest( LDAP_DEBUG_ANY ) ) {
686 			char	buf[ SLAP_TEXT_BUFLEN ];
687 
688 			/* this lock is required; however,
689 			 * it's invoked only when logging is on */
690 			ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
691 			snprintf( buf, sizeof( buf ),
692 				"retrying URI=\"%s\" DN=\"%s\"",
693 				mt->mt_uri,
694 				BER_BVISNULL( &msc->msc_bound_ndn ) ?
695 					"" : msc->msc_bound_ndn.bv_val );
696 			ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
697 
698 			Debug( LDAP_DEBUG_ANY,
699 				"%s meta_back_retry[%d]: %s.\n",
700 				op->o_log_prefix, candidate, buf );
701 		}
702 
703 		meta_clear_one_candidate( op, mc, candidate );
704 		LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
705 
706 		( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
707 
708 		/* mc here must be the regular mc, reset and ready for init */
709 		rc = meta_back_init_one_conn( op, rs, mc, candidate,
710 			LDAP_BACK_CONN_ISPRIV( mc ), sendok, 0 );
711 
712 		/* restore the "binding" flag, in case */
713 		if ( binding ) {
714 			LDAP_BACK_CONN_BINDING_SET( msc );
715 		}
716 
717 		if ( rc == LDAP_SUCCESS ) {
718 			quarantine = 0;
719 			rc = meta_back_single_dobind( op, rs, mcp, candidate,
720 				sendok, mt->mt_nretries, 0 );
721 
722 			Debug( LDAP_DEBUG_ANY,
723 				"%s meta_back_retry[%d]: "
724 				"meta_back_single_dobind=%d\n",
725 				op->o_log_prefix, candidate, rc );
726 			if ( rc == LDAP_SUCCESS ) {
727 				if ( !BER_BVISNULL( &msc->msc_bound_ndn ) &&
728 					!BER_BVISEMPTY( &msc->msc_bound_ndn ) )
729 				{
730 					LDAP_BACK_CONN_ISBOUND_SET( msc );
731 
732 				} else {
733 					LDAP_BACK_CONN_ISANON_SET( msc );
734 				}
735 
736 				/* when bound, dispose of the "binding" flag */
737 				if ( binding ) {
738 					LDAP_BACK_CONN_BINDING_CLEAR( msc );
739 				}
740 			}
741         	}
742 
743 		/* don't send twice */
744 		sendok &= ~LDAP_BACK_SENDERR;
745 	}
746 
747 	if ( rc != LDAP_SUCCESS ) {
748 		SlapReply		*candidates = meta_back_candidates_get( op );
749 
750 		candidates[ candidate ].sr_err = rc;
751 
752 		if ( *mcp != NULL ) {
753 			if ( mc->mc_refcnt == 1 ) {
754 				if ( binding ) {
755 					LDAP_BACK_CONN_BINDING_CLEAR( msc );
756 				}
757 				(void)meta_clear_one_candidate( op, mc, candidate );
758 			}
759 
760 			LDAP_BACK_CONN_TAINTED_SET( mc );
761 			/* only release if mandatory; otherwise
762 			 * let the caller do what's best before
763 			 * releasing */
764 			if ( META_BACK_ONERR_STOP( mi ) ) {
765 				meta_back_release_conn_lock( mi, mc, 0 );
766 				*mcp = NULL;
767 
768 			} else {
769 #if META_BACK_PRINT_CONNTREE > 0
770 				meta_back_print_conntree( mi, ">>> meta_back_retry" );
771 #endif /* META_BACK_PRINT_CONNTREE */
772 
773 				/* FIXME: could be done better, reworking meta_back_release_conn_lock() */
774 				if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
775 					if ( mc->mc_q.tqe_prev != NULL ) {
776 						assert( LDAP_BACK_CONN_CACHED( mc ) );
777 						assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
778 						LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
779 							mc, mc_q );
780 						mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
781 						LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
782 
783 					} else {
784 						assert( !LDAP_BACK_CONN_CACHED( mc ) );
785 					}
786 
787 				} else {
788 					/* FIXME: check if in tree, for consistency? */
789 					(void)avl_delete( &mi->mi_conninfo.lai_tree,
790 						( caddr_t )mc, meta_back_conndnmc_cmp );
791 				}
792 				LDAP_BACK_CONN_CACHED_CLEAR( mc );
793 
794 #if META_BACK_PRINT_CONNTREE > 0
795 				meta_back_print_conntree( mi, "<<< meta_back_retry" );
796 #endif /* META_BACK_PRINT_CONNTREE */
797 			}
798 		}
799 
800 		if ( sendok & LDAP_BACK_SENDERR ) {
801 			rs->sr_err = rc;
802 			rs->sr_text = "Unable to retry";
803 			send_ldap_result( op, rs );
804 		}
805 	}
806 
807 	if ( quarantine && META_BACK_TGT_QUARANTINE( mt ) ) {
808 		meta_back_quarantine( op, rs, candidate );
809 	}
810 
811 	ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
812 
813 	return rc == LDAP_SUCCESS ? 1 : 0;
814 }
815 
816 /*
817  * callback for unique candidate selection
818  */
819 static int
820 meta_back_conn_cb( Operation *op, SlapReply *rs )
821 {
822 	assert( op->o_tag == LDAP_REQ_SEARCH );
823 
824 	switch ( rs->sr_type ) {
825 	case REP_SEARCH:
826 		((long *)op->o_callback->sc_private)[0] = (long)op->o_private;
827 		break;
828 
829 	case REP_SEARCHREF:
830 	case REP_RESULT:
831 		break;
832 
833 	default:
834 		return rs->sr_err;
835 	}
836 
837 	return 0;
838 }
839 
840 
841 static int
842 meta_back_get_candidate(
843 	Operation	*op,
844 	SlapReply	*rs,
845 	struct berval	*ndn )
846 {
847 	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;
848 	long		candidate;
849 
850 	/*
851 	 * tries to get a unique candidate
852 	 * (takes care of default target)
853 	 */
854 	candidate = meta_back_select_unique_candidate( mi, ndn );
855 
856 	/*
857 	 * if any is found, inits the connection
858 	 */
859 	if ( candidate == META_TARGET_NONE ) {
860 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
861 		rs->sr_text = "No suitable candidate target found";
862 
863 	} else if ( candidate == META_TARGET_MULTIPLE ) {
864 		Operation	op2 = *op;
865 		SlapReply	rs2 = { 0 };
866 		slap_callback	cb2 = { 0 };
867 		int		rc;
868 
869 		/* try to get a unique match for the request ndn
870 		 * among the multiple candidates available */
871 		op2.o_tag = LDAP_REQ_SEARCH;
872 		op2.o_req_dn = *ndn;
873 		op2.o_req_ndn = *ndn;
874 		op2.ors_scope = LDAP_SCOPE_BASE;
875 		op2.ors_deref = LDAP_DEREF_NEVER;
876 		op2.ors_attrs = slap_anlist_no_attrs;
877 		op2.ors_attrsonly = 0;
878 		op2.ors_limit = NULL;
879 		op2.ors_slimit = 1;
880 		op2.ors_tlimit = SLAP_NO_LIMIT;
881 
882 		op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
883 		op2.ors_filterstr = *slap_filterstr_objectClass_pres;
884 
885 		op2.o_callback = &cb2;
886 		cb2.sc_response = meta_back_conn_cb;
887 		cb2.sc_private = (void *)&candidate;
888 
889 		rc = op->o_bd->be_search( &op2, &rs2 );
890 
891 		switch ( rs2.sr_err ) {
892 		case LDAP_SUCCESS:
893 		default:
894 			rs->sr_err = rs2.sr_err;
895 			break;
896 
897 		case LDAP_SIZELIMIT_EXCEEDED:
898 			/* if multiple candidates can serve the operation,
899 			 * and a default target is defined, and it is
900 			 * a candidate, try using it (FIXME: YMMV) */
901 			if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
902 				&& meta_back_is_candidate( mi->mi_targets[ mi->mi_defaulttarget ],
903 						ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
904 			{
905 				candidate = mi->mi_defaulttarget;
906 				rs->sr_err = LDAP_SUCCESS;
907 				rs->sr_text = NULL;
908 
909 			} else {
910 				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
911 				rs->sr_text = "Unable to select unique candidate target";
912 			}
913 			break;
914 		}
915 
916 	} else {
917 		rs->sr_err = LDAP_SUCCESS;
918 	}
919 
920 	return candidate;
921 }
922 
923 static void	*meta_back_candidates_dummy;
924 
925 static void
926 meta_back_candidates_keyfree(
927 	void		*key,
928 	void		*data )
929 {
930 	metacandidates_t	*mc = (metacandidates_t *)data;
931 
932 	ber_memfree_x( mc->mc_candidates, NULL );
933 	ber_memfree_x( data, NULL );
934 }
935 
936 SlapReply *
937 meta_back_candidates_get( Operation *op )
938 {
939 	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
940 	metacandidates_t	*mc;
941 
942 	if ( op->o_threadctx ) {
943 		void		*data = NULL;
944 
945 		ldap_pvt_thread_pool_getkey( op->o_threadctx,
946 				&meta_back_candidates_dummy, &data, NULL );
947 		mc = (metacandidates_t *)data;
948 
949 	} else {
950 		mc = mi->mi_candidates;
951 	}
952 
953 	if ( mc == NULL ) {
954 		mc = ch_calloc( sizeof( metacandidates_t ), 1 );
955 		mc->mc_ntargets = mi->mi_ntargets;
956 		mc->mc_candidates = ch_calloc( sizeof( SlapReply ), mc->mc_ntargets );
957 		if ( op->o_threadctx ) {
958 			void		*data = NULL;
959 
960 			data = (void *)mc;
961 			ldap_pvt_thread_pool_setkey( op->o_threadctx,
962 					&meta_back_candidates_dummy, data,
963 					meta_back_candidates_keyfree,
964 					NULL, NULL );
965 
966 		} else {
967 			mi->mi_candidates = mc;
968 		}
969 
970 	} else if ( mc->mc_ntargets < mi->mi_ntargets ) {
971 		/* NOTE: in the future, may want to allow back-config
972 		 * to add/remove targets from back-meta... */
973 		mc->mc_candidates = ch_realloc( mc->mc_candidates,
974 				sizeof( SlapReply ) * mi->mi_ntargets );
975 		memset( &mc->mc_candidates[ mc->mc_ntargets ], 0,
976 			sizeof( SlapReply ) * ( mi->mi_ntargets - mc->mc_ntargets ) );
977 		mc->mc_ntargets = mi->mi_ntargets;
978 	}
979 
980 	return mc->mc_candidates;
981 }
982 
983 /*
984  * meta_back_getconn
985  *
986  * Prepares the connection structure
987  *
988  * RATIONALE:
989  *
990  * - determine what DN is being requested:
991  *
992  *	op	requires candidate	checks
993  *
994  *	add	unique			parent of o_req_ndn
995  *	bind	unique^*[/all]		o_req_ndn [no check]
996  *	compare	unique^+		o_req_ndn
997  *	delete	unique			o_req_ndn
998  *	modify	unique			o_req_ndn
999  *	search	any			o_req_ndn
1000  *	modrdn	unique[, unique]	o_req_ndn[, orr_nnewSup]
1001  *
1002  * - for ops that require the candidate to be unique, in case of multiple
1003  *   occurrences an internal search with sizeLimit=1 is performed
1004  *   if a unique candidate can actually be determined.  If none is found,
1005  *   the operation aborts; if multiple are found, the default target
1006  *   is used if defined and candidate; otherwise the operation aborts.
1007  *
1008  * *^note: actually, the bind operation is handled much like a search;
1009  *   i.e. the bind is broadcast to all candidate targets.
1010  *
1011  * +^note: actually, the compare operation is handled much like a search;
1012  *   i.e. the compare is broadcast to all candidate targets, while checking
1013  *   that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
1014  *   returned.
1015  */
1016 metaconn_t *
1017 meta_back_getconn(
1018        	Operation 		*op,
1019 	SlapReply		*rs,
1020 	int 			*candidate,
1021 	ldap_back_send_t	sendok )
1022 {
1023 	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;
1024 	metaconn_t	*mc = NULL,
1025 			mc_curr = { 0 };
1026 	int		cached = META_TARGET_NONE,
1027 			i = META_TARGET_NONE,
1028 			err = LDAP_SUCCESS,
1029 			new_conn = 0,
1030 			ncandidates = 0;
1031 
1032 
1033 	meta_op_type	op_type = META_OP_REQUIRE_SINGLE;
1034 	enum		{
1035 		META_DNTYPE_ENTRY,
1036 		META_DNTYPE_PARENT,
1037 		META_DNTYPE_NEWPARENT
1038 	}		dn_type = META_DNTYPE_ENTRY;
1039 	struct berval	ndn = op->o_req_ndn,
1040 			pndn;
1041 
1042 	SlapReply	*candidates = meta_back_candidates_get( op );
1043 
1044 	/* Internal searches are privileged and shared. So is root. */
1045 	if ( ( !BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ALWAYS( mi ) )
1046 		|| ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ANON( mi ) )
1047 		|| op->o_do_not_cache || be_isroot( op ) )
1048 	{
1049 		LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
1050 		mc_curr.mc_local_ndn = op->o_bd->be_rootndn;
1051 		LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op );
1052 
1053 	} else if ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_NOANON( mi ) )
1054 	{
1055 		LDAP_BACK_CONN_ISANON_SET( &mc_curr );
1056 		BER_BVSTR( &mc_curr.mc_local_ndn, "" );
1057 		LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
1058 
1059 	} else {
1060 		mc_curr.mc_local_ndn = op->o_ndn;
1061 
1062 		/* Explicit binds must not be shared */
1063 		if ( !BER_BVISEMPTY( &op->o_ndn )
1064 			|| op->o_tag == LDAP_REQ_BIND
1065 			|| SLAP_IS_AUTHZ_BACKEND( op ) )
1066 		{
1067 			mc_curr.mc_conn = op->o_conn;
1068 
1069 		} else {
1070 			LDAP_BACK_CONN_ISANON_SET( &mc_curr );
1071 			LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
1072 		}
1073 	}
1074 
1075 	/* Explicit Bind requests always get their own conn */
1076 	if ( sendok & LDAP_BACK_BINDING ) {
1077 		mc_curr.mc_conn = op->o_conn;
1078 
1079 	} else {
1080 		/* Searches for a metaconn in the avl tree */
1081 retry_lock:;
1082 		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1083 		if ( LDAP_BACK_PCONN_ISPRIV( &mc_curr ) ) {
1084 			/* lookup a conn that's not binding */
1085 			LDAP_TAILQ_FOREACH( mc,
1086 				&mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv,
1087 				mc_q )
1088 			{
1089 				if ( !LDAP_BACK_CONN_BINDING( mc ) && mc->mc_refcnt == 0 ) {
1090 					break;
1091 				}
1092 			}
1093 
1094 			if ( mc != NULL ) {
1095 				if ( mc != LDAP_TAILQ_LAST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
1096 					metaconn_t, mc_q ) )
1097 				{
1098 					LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
1099 						mc, mc_q );
1100 					LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
1101 					LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
1102 						mc, mc_q );
1103 				}
1104 
1105 			} else if ( !LDAP_BACK_USE_TEMPORARIES( mi )
1106 				&& mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_num == mi->mi_conn_priv_max )
1107 			{
1108 				mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv );
1109 			}
1110 
1111 
1112 		} else {
1113 			mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree,
1114 				(caddr_t)&mc_curr, meta_back_conndn_cmp );
1115 		}
1116 
1117 		if ( mc ) {
1118 			/* catch taint errors */
1119 			assert( !LDAP_BACK_CONN_TAINTED( mc ) );
1120 
1121 			/* Don't reuse connections while they're still binding
1122 			 * NOTE: only makes sense for binds */
1123 			if ( LDAP_BACK_CONN_BINDING( mc ) ) {
1124 				if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
1125 					ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1126 
1127 					ldap_pvt_thread_yield();
1128 					goto retry_lock;
1129 				}
1130 
1131 				/* release conn, and create a temporary */
1132 				mc = NULL;
1133 
1134 			} else {
1135 				if ( ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl )
1136 					|| ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) )
1137 				{
1138 #if META_BACK_PRINT_CONNTREE > 0
1139 					meta_back_print_conntree( mi,
1140 						">>> meta_back_getconn(expired)" );
1141 #endif /* META_BACK_PRINT_CONNTREE */
1142 
1143 					/* don't let anyone else use this expired connection */
1144 					if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
1145 						if ( mc->mc_q.tqe_prev != NULL ) {
1146 							assert( LDAP_BACK_CONN_CACHED( mc ) );
1147 							assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
1148 							LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
1149 								mc, mc_q );
1150 							mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
1151 							LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
1152 
1153 						} else {
1154 							assert( !LDAP_BACK_CONN_CACHED( mc ) );
1155 						}
1156 
1157 					} else {
1158 						(void)avl_delete( &mi->mi_conninfo.lai_tree,
1159 							(caddr_t)mc, meta_back_conndnmc_cmp );
1160 					}
1161 
1162 #if META_BACK_PRINT_CONNTREE > 0
1163 					meta_back_print_conntree( mi,
1164 						"<<< meta_back_getconn(expired)" );
1165 #endif /* META_BACK_PRINT_CONNTREE */
1166 					LDAP_BACK_CONN_TAINTED_SET( mc );
1167 					LDAP_BACK_CONN_CACHED_CLEAR( mc );
1168 
1169 					Debug( LDAP_DEBUG_TRACE, "%s meta_back_getconn: mc=%p conn=%ld expired (tainted).\n",
1170 						op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc ) );
1171 				}
1172 
1173 				mc->mc_refcnt++;
1174 			}
1175 		}
1176 		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1177 	}
1178 
1179 	switch ( op->o_tag ) {
1180 	case LDAP_REQ_ADD:
1181 		/* if we go to selection, the entry must not exist,
1182 		 * and we must be able to resolve the parent */
1183 		dn_type = META_DNTYPE_PARENT;
1184 		dnParent( &ndn, &pndn );
1185 		break;
1186 
1187 	case LDAP_REQ_MODRDN:
1188 		/* if nnewSuperior is not NULL, it must resolve
1189 		 * to the same candidate as the req_ndn */
1190 		if ( op->orr_nnewSup ) {
1191 			dn_type = META_DNTYPE_NEWPARENT;
1192 		}
1193 		break;
1194 
1195 	case LDAP_REQ_BIND:
1196 		/* if bound as rootdn, the backend must bind to all targets
1197 		 * with the administrative identity
1198 		 * (unless pseoudoroot-bind-defer is TRUE) */
1199 		if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
1200 			op_type = META_OP_REQUIRE_ALL;
1201 		}
1202 		break;
1203 
1204 	case LDAP_REQ_COMPARE:
1205 	case LDAP_REQ_DELETE:
1206 	case LDAP_REQ_MODIFY:
1207 		/* just a unique candidate */
1208 		break;
1209 
1210 	case LDAP_REQ_SEARCH:
1211 		/* allow multiple candidates for the searchBase */
1212 		op_type = META_OP_ALLOW_MULTIPLE;
1213 		break;
1214 
1215 	default:
1216 		/* right now, just break (exop?) */
1217 		break;
1218 	}
1219 
1220 	/*
1221 	 * require all connections ...
1222 	 */
1223 	if ( op_type == META_OP_REQUIRE_ALL ) {
1224 
1225 		/* Looks like we didn't get a bind. Open a new session... */
1226 		if ( mc == NULL ) {
1227 			assert( new_conn == 0 );
1228 			mc = metaconn_alloc( op );
1229 			mc->mc_conn = mc_curr.mc_conn;
1230 			ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
1231 			new_conn = 1;
1232 			if ( sendok & LDAP_BACK_BINDING ) {
1233 				LDAP_BACK_CONN_BINDING_SET( mc );
1234 			}
1235 			if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
1236 				LDAP_BACK_CONN_ISPRIV_SET( mc );
1237 
1238 			} else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
1239 				LDAP_BACK_CONN_ISANON_SET( mc );
1240 			}
1241 
1242 		} else if ( 0 ) {
1243 			/* TODO: if any of the connections is binding,
1244 			 * release mc and create a new one */
1245 		}
1246 
1247 		for ( i = 0; i < mi->mi_ntargets; i++ ) {
1248 			/*
1249 			 * The target is activated; if needed, it is
1250 			 * also init'd
1251 			 */
1252 			candidates[ i ].sr_err = meta_back_init_one_conn( op,
1253 				rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
1254 				LDAP_BACK_DONTSEND, !new_conn );
1255 			if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
1256 				if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
1257 					LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
1258 				}
1259 				META_CANDIDATE_SET( &candidates[ i ] );
1260 				ncandidates++;
1261 
1262 			} else {
1263 
1264 				/*
1265 				 * FIXME: in case one target cannot
1266 				 * be init'd, should the other ones
1267 				 * be tried?
1268 				 */
1269 				META_CANDIDATE_RESET( &candidates[ i ] );
1270 				err = candidates[ i ].sr_err;
1271 				continue;
1272 			}
1273 		}
1274 
1275 		if ( ncandidates == 0 ) {
1276 			if ( new_conn ) {
1277 				mc->mc_refcnt = 0;
1278 				meta_back_conn_free( mc );
1279 
1280 			} else {
1281 				meta_back_release_conn( mi, mc );
1282 			}
1283 
1284 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
1285 			rs->sr_text = "Unable to select valid candidates";
1286 
1287 			if ( sendok & LDAP_BACK_SENDERR ) {
1288 				if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1289 					rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1290 				}
1291 				send_ldap_result( op, rs );
1292 				rs->sr_matched = NULL;
1293 			}
1294 
1295 			return NULL;
1296 		}
1297 
1298 		goto done;
1299 	}
1300 
1301 	/*
1302 	 * looks in cache, if any
1303 	 */
1304 	if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
1305 		cached = i = meta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
1306 	}
1307 
1308 	if ( op_type == META_OP_REQUIRE_SINGLE ) {
1309 		metatarget_t		*mt = NULL;
1310 		metasingleconn_t	*msc = NULL;
1311 
1312 		int			j;
1313 
1314 		for ( j = 0; j < mi->mi_ntargets; j++ ) {
1315 			META_CANDIDATE_RESET( &candidates[ j ] );
1316 		}
1317 
1318 		/*
1319 		 * tries to get a unique candidate
1320 		 * (takes care of default target)
1321 		 */
1322 		if ( i == META_TARGET_NONE ) {
1323 			i = meta_back_get_candidate( op, rs, &ndn );
1324 
1325 			if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && dn_type == META_DNTYPE_PARENT ) {
1326 				i = meta_back_get_candidate( op, rs, &pndn );
1327 			}
1328 
1329 			if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
1330 				if ( mc != NULL ) {
1331 					meta_back_release_conn( mi, mc );
1332 				}
1333 
1334 				if ( sendok & LDAP_BACK_SENDERR ) {
1335 					if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1336 						rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1337 					}
1338 					send_ldap_result( op, rs );
1339 					rs->sr_matched = NULL;
1340 				}
1341 
1342 				return NULL;
1343 			}
1344 		}
1345 
1346 		if ( dn_type == META_DNTYPE_NEWPARENT && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
1347 		{
1348 			if ( mc != NULL ) {
1349 				meta_back_release_conn( mi, mc );
1350 			}
1351 
1352 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1353 			rs->sr_text = "Cross-target rename not supported";
1354 			if ( sendok & LDAP_BACK_SENDERR ) {
1355 				send_ldap_result( op, rs );
1356 			}
1357 
1358 			return NULL;
1359 		}
1360 
1361 		Debug( LDAP_DEBUG_TRACE,
1362 	"==>meta_back_getconn: got target=%d for ndn=\"%s\" from cache\n",
1363 				i, op->o_req_ndn.bv_val, 0 );
1364 
1365 		if ( mc == NULL ) {
1366 			/* Retries searching for a metaconn in the avl tree
1367 			 * the reason is that the connection might have been
1368 			 * created by meta_back_get_candidate() */
1369 			if ( !( sendok & LDAP_BACK_BINDING ) ) {
1370 retry_lock2:;
1371 				ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1372 				mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree,
1373 					(caddr_t)&mc_curr, meta_back_conndn_cmp );
1374 				if ( mc != NULL ) {
1375 					/* catch taint errors */
1376 					assert( !LDAP_BACK_CONN_TAINTED( mc ) );
1377 
1378 					/* Don't reuse connections while they're still binding */
1379 					if ( META_BACK_CONN_CREATING( &mc->mc_conns[ i ] )
1380 						|| LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) )
1381 					{
1382 						if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
1383 							ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1384 							ldap_pvt_thread_yield();
1385 							goto retry_lock2;
1386 						}
1387 
1388 						mc = NULL;
1389 
1390 					} else {
1391 						mc->mc_refcnt++;
1392 					}
1393 				}
1394 				ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1395 			}
1396 
1397 			/* Looks like we didn't get a bind. Open a new session... */
1398 			if ( mc == NULL ) {
1399 				assert( new_conn == 0 );
1400 				mc = metaconn_alloc( op );
1401 				mc->mc_conn = mc_curr.mc_conn;
1402 				ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
1403 				new_conn = 1;
1404 				if ( sendok & LDAP_BACK_BINDING ) {
1405 					LDAP_BACK_CONN_BINDING_SET( mc );
1406 				}
1407 				if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
1408 					LDAP_BACK_CONN_ISPRIV_SET( mc );
1409 
1410 				} else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
1411 					LDAP_BACK_CONN_ISANON_SET( mc );
1412 				}
1413 			}
1414 		}
1415 
1416 		/*
1417 		 * Clear all other candidates
1418 		 */
1419 		( void )meta_clear_unused_candidates( op, i );
1420 
1421 		mt = mi->mi_targets[ i ];
1422 		msc = &mc->mc_conns[ i ];
1423 
1424 		/*
1425 		 * The target is activated; if needed, it is
1426 		 * also init'd. In case of error, meta_back_init_one_conn
1427 		 * sends the appropriate result.
1428 		 */
1429 		err = meta_back_init_one_conn( op, rs, mc, i,
1430 			LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn );
1431 		if ( err != LDAP_SUCCESS ) {
1432 			/*
1433 			 * FIXME: in case one target cannot
1434 			 * be init'd, should the other ones
1435 			 * be tried?
1436 			 */
1437 			META_CANDIDATE_RESET( &candidates[ i ] );
1438  			if ( new_conn ) {
1439 				mc->mc_refcnt = 0;
1440 				meta_back_conn_free( mc );
1441 
1442 			} else {
1443 				meta_back_release_conn( mi, mc );
1444 			}
1445 			return NULL;
1446 		}
1447 
1448 		if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
1449 			LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
1450 		}
1451 
1452 		candidates[ i ].sr_err = LDAP_SUCCESS;
1453 		META_CANDIDATE_SET( &candidates[ i ] );
1454 		ncandidates++;
1455 
1456 		if ( candidate ) {
1457 			*candidate = i;
1458 		}
1459 
1460 	/*
1461 	 * if no unique candidate ...
1462 	 */
1463 	} else {
1464 
1465 		/* Looks like we didn't get a bind. Open a new session... */
1466 		if ( mc == NULL ) {
1467 			assert( new_conn == 0 );
1468 			mc = metaconn_alloc( op );
1469 			mc->mc_conn = mc_curr.mc_conn;
1470 			ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
1471 			new_conn = 1;
1472 			if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
1473 				LDAP_BACK_CONN_ISPRIV_SET( mc );
1474 
1475 			} else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
1476 				LDAP_BACK_CONN_ISANON_SET( mc );
1477 			}
1478 		}
1479 
1480 		for ( i = 0; i < mi->mi_ntargets; i++ ) {
1481 			metatarget_t		*mt = mi->mi_targets[ i ];
1482 
1483 			META_CANDIDATE_RESET( &candidates[ i ] );
1484 
1485 			if ( i == cached
1486 				|| meta_back_is_candidate( mt, &op->o_req_ndn,
1487 					LDAP_SCOPE_SUBTREE ) )
1488 			{
1489 
1490 				/*
1491 				 * The target is activated; if needed, it is
1492 				 * also init'd
1493 				 */
1494 				int lerr = meta_back_init_one_conn( op, rs, mc, i,
1495 					LDAP_BACK_CONN_ISPRIV( &mc_curr ),
1496 					LDAP_BACK_DONTSEND, !new_conn );
1497 				candidates[ i ].sr_err = lerr;
1498 				if ( lerr == LDAP_SUCCESS ) {
1499 					META_CANDIDATE_SET( &candidates[ i ] );
1500 					ncandidates++;
1501 
1502 					Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d]\n",
1503 						op->o_log_prefix, i, 0 );
1504 
1505 				} else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
1506 					META_CANDIDATE_SET( &candidates[ i ] );
1507 
1508 					Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] %s\n",
1509 						op->o_log_prefix, i,
1510 						mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
1511 
1512 				} else {
1513 
1514 					/*
1515 					 * FIXME: in case one target cannot
1516 					 * be init'd, should the other ones
1517 					 * be tried?
1518 					 */
1519 					if ( new_conn ) {
1520 						( void )meta_clear_one_candidate( op, mc, i );
1521 					}
1522 					/* leave the target candidate, but record the error for later use */
1523 					err = lerr;
1524 
1525 					if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
1526 						Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] quarantined err=%d\n",
1527 							op->o_log_prefix, i, lerr );
1528 
1529 					} else {
1530 						Debug( LDAP_DEBUG_ANY, "%s: meta_back_getconn[%d] failed err=%d\n",
1531 							op->o_log_prefix, i, lerr );
1532 					}
1533 
1534 					if ( META_BACK_ONERR_STOP( mi ) ) {
1535 						if ( sendok & LDAP_BACK_SENDERR ) {
1536 							send_ldap_result( op, rs );
1537 						}
1538 						if ( new_conn ) {
1539 							mc->mc_refcnt = 0;
1540 							meta_back_conn_free( mc );
1541 
1542 						} else {
1543 							meta_back_release_conn( mi, mc );
1544 						}
1545 
1546 						return NULL;
1547 					}
1548 
1549 					continue;
1550 				}
1551 
1552 			} else {
1553 				if ( new_conn ) {
1554 					( void )meta_clear_one_candidate( op, mc, i );
1555 				}
1556 			}
1557 		}
1558 
1559 		if ( ncandidates == 0 ) {
1560 			if ( new_conn ) {
1561 				mc->mc_refcnt = 0;
1562 				meta_back_conn_free( mc );
1563 
1564 			} else {
1565 				meta_back_release_conn( mi, mc );
1566 			}
1567 
1568 			if ( rs->sr_err == LDAP_SUCCESS ) {
1569 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
1570 				rs->sr_text = "Unable to select valid candidates";
1571 			}
1572 
1573 			if ( sendok & LDAP_BACK_SENDERR ) {
1574 				if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1575 					rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1576 				}
1577 				send_ldap_result( op, rs );
1578 				rs->sr_matched = NULL;
1579 			}
1580 
1581 			return NULL;
1582 		}
1583 	}
1584 
1585 done:;
1586 	/* clear out meta_back_init_one_conn non-fatal errors */
1587 	rs->sr_err = LDAP_SUCCESS;
1588 	rs->sr_text = NULL;
1589 
1590 	/* touch the timestamp */
1591 	if ( mi->mi_idle_timeout != 0 ) {
1592 		mc->mc_time = op->o_time;
1593 	}
1594 
1595 	if ( new_conn ) {
1596 		if ( mi->mi_conn_ttl ) {
1597 			mc->mc_create_time = op->o_time;
1598 		}
1599 
1600 		/*
1601 		 * Inserts the newly created metaconn in the avl tree
1602 		 */
1603 		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1604 #if META_BACK_PRINT_CONNTREE > 0
1605 		meta_back_print_conntree( mi, ">>> meta_back_getconn" );
1606 #endif /* META_BACK_PRINT_CONNTREE */
1607 
1608 		err = 0;
1609 		if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
1610 			if ( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num < mi->mi_conn_priv_max ) {
1611 				LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
1612 				mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num++;
1613 				LDAP_BACK_CONN_CACHED_SET( mc );
1614 
1615 			} else {
1616 				LDAP_BACK_CONN_TAINTED_SET( mc );
1617 			}
1618 			rs->sr_err = 0;
1619 
1620 		} else if ( !( sendok & LDAP_BACK_BINDING ) ) {
1621 			err = avl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc,
1622 			       	meta_back_conndn_cmp, meta_back_conndn_dup );
1623 			LDAP_BACK_CONN_CACHED_SET( mc );
1624 		}
1625 
1626 #if META_BACK_PRINT_CONNTREE > 0
1627 		meta_back_print_conntree( mi, "<<< meta_back_getconn" );
1628 #endif /* META_BACK_PRINT_CONNTREE */
1629 		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1630 
1631 		if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) {
1632 			/*
1633 			 * Err could be -1 in case a duplicate metaconn is inserted
1634 			 */
1635 			switch ( err ) {
1636 			case 0:
1637 				break;
1638 
1639 			case -1:
1640 				LDAP_BACK_CONN_CACHED_CLEAR( mc );
1641 				/* duplicate: free and try to get the newly created one */
1642 				if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
1643 					mc->mc_refcnt = 0;
1644 					meta_back_conn_free( mc );
1645 
1646 					new_conn = 0;
1647 					goto retry_lock;
1648 				}
1649 
1650 				LDAP_BACK_CONN_TAINTED_SET( mc );
1651 				break;
1652 
1653 			default:
1654 				LDAP_BACK_CONN_CACHED_CLEAR( mc );
1655 				Debug( LDAP_DEBUG_ANY,
1656 					"%s meta_back_getconn: candidates=%d conn=%ld insert failed\n",
1657 					op->o_log_prefix, ncandidates,
1658 					LDAP_BACK_PCONN_ID( mc ) );
1659 
1660 				mc->mc_refcnt = 0;
1661 				meta_back_conn_free( mc );
1662 
1663 				rs->sr_err = LDAP_OTHER;
1664 				rs->sr_text = "Proxy bind collision";
1665 				if ( sendok & LDAP_BACK_SENDERR ) {
1666 					send_ldap_result( op, rs );
1667 				}
1668 				return NULL;
1669 			}
1670 		}
1671 
1672 		Debug( LDAP_DEBUG_TRACE,
1673 			"%s meta_back_getconn: candidates=%d conn=%ld inserted\n",
1674 			op->o_log_prefix, ncandidates,
1675 			LDAP_BACK_PCONN_ID( mc ) );
1676 
1677 	} else {
1678 		Debug( LDAP_DEBUG_TRACE,
1679 			"%s meta_back_getconn: candidates=%d conn=%ld fetched\n",
1680 			op->o_log_prefix, ncandidates,
1681 			LDAP_BACK_PCONN_ID( mc ) );
1682 	}
1683 
1684 	return mc;
1685 }
1686 
1687 void
1688 meta_back_release_conn_lock(
1689        	metainfo_t		*mi,
1690 	metaconn_t		*mc,
1691 	int			dolock )
1692 {
1693 	assert( mc != NULL );
1694 
1695 	if ( dolock ) {
1696 		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1697 	}
1698 	assert( mc->mc_refcnt > 0 );
1699 	mc->mc_refcnt--;
1700 	/* NOTE: the connection is removed if either it is tainted
1701 	 * or if it is shared and no one else is using it.  This needs
1702 	 * to occur because for intrinsic reasons cached connections
1703 	 * that are not privileged would live forever and pollute
1704 	 * the connection space (and eat up resources).  Maybe this
1705 	 * should be configurable... */
1706 	if ( LDAP_BACK_CONN_TAINTED( mc ) || !LDAP_BACK_CONN_CACHED( mc ) ) {
1707 #if META_BACK_PRINT_CONNTREE > 0
1708 		meta_back_print_conntree( mi, ">>> meta_back_release_conn" );
1709 #endif /* META_BACK_PRINT_CONNTREE */
1710 
1711 		if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
1712 			if ( mc->mc_q.tqe_prev != NULL ) {
1713 				assert( LDAP_BACK_CONN_CACHED( mc ) );
1714 				assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
1715 				LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
1716 				mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
1717 				LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
1718 
1719 			} else {
1720 				assert( !LDAP_BACK_CONN_CACHED( mc ) );
1721 			}
1722 
1723 		} else if ( LDAP_BACK_CONN_CACHED( mc ) ) {
1724 			metaconn_t	*tmpmc;
1725 
1726 			tmpmc = avl_delete( &mi->mi_conninfo.lai_tree,
1727 				( caddr_t )mc, meta_back_conndnmc_cmp );
1728 
1729 			/* Overparanoid, but useful... */
1730 			assert( tmpmc == NULL || tmpmc == mc );
1731 		}
1732 
1733 		LDAP_BACK_CONN_CACHED_CLEAR( mc );
1734 
1735 #if META_BACK_PRINT_CONNTREE > 0
1736 		meta_back_print_conntree( mi, "<<< meta_back_release_conn" );
1737 #endif /* META_BACK_PRINT_CONNTREE */
1738 
1739 		if ( mc->mc_refcnt == 0 ) {
1740 			meta_back_conn_free( mc );
1741 			mc = NULL;
1742 		}
1743 	}
1744 
1745 	if ( mc != NULL && LDAP_BACK_CONN_BINDING( mc ) ) {
1746 		LDAP_BACK_CONN_BINDING_CLEAR( mc );
1747 	}
1748 
1749 	if ( dolock ) {
1750 		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1751 	}
1752 }
1753 
1754 void
1755 meta_back_quarantine(
1756 	Operation	*op,
1757 	SlapReply	*rs,
1758 	int		candidate )
1759 {
1760 	metainfo_t		*mi = (metainfo_t *)op->o_bd->be_private;
1761 	metatarget_t		*mt = mi->mi_targets[ candidate ];
1762 
1763 	slap_retry_info_t	*ri = &mt->mt_quarantine;
1764 
1765 	ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
1766 
1767 	if ( rs->sr_err == LDAP_UNAVAILABLE ) {
1768 		time_t	new_last = slap_get_time();
1769 
1770 		switch ( mt->mt_isquarantined ) {
1771 		case LDAP_BACK_FQ_NO:
1772 			if ( ri->ri_last == new_last ) {
1773 				goto done;
1774 			}
1775 
1776 			Debug( LDAP_DEBUG_ANY,
1777 				"%s meta_back_quarantine[%d]: enter.\n",
1778 				op->o_log_prefix, candidate, 0 );
1779 
1780 			ri->ri_idx = 0;
1781 			ri->ri_count = 0;
1782 			break;
1783 
1784 		case LDAP_BACK_FQ_RETRYING:
1785 			if ( LogTest( LDAP_DEBUG_ANY ) ) {
1786 				char	buf[ SLAP_TEXT_BUFLEN ];
1787 
1788 				snprintf( buf, sizeof( buf ),
1789 					"meta_back_quarantine[%d]: block #%d try #%d failed",
1790 					candidate, ri->ri_idx, ri->ri_count );
1791 				Debug( LDAP_DEBUG_ANY, "%s %s.\n",
1792 					op->o_log_prefix, buf, 0 );
1793 			}
1794 
1795 			++ri->ri_count;
1796 			if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
1797 				&& ri->ri_count == ri->ri_num[ ri->ri_idx ] )
1798 			{
1799 				ri->ri_count = 0;
1800 				++ri->ri_idx;
1801 			}
1802 			break;
1803 
1804 		default:
1805 			break;
1806 		}
1807 
1808 		mt->mt_isquarantined = LDAP_BACK_FQ_YES;
1809 		ri->ri_last = new_last;
1810 
1811 	} else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
1812 		Debug( LDAP_DEBUG_ANY,
1813 			"%s meta_back_quarantine[%d]: exit.\n",
1814 			op->o_log_prefix, candidate, 0 );
1815 
1816 		if ( mi->mi_quarantine_f ) {
1817 			(void)mi->mi_quarantine_f( mi, candidate,
1818 				mi->mi_quarantine_p );
1819 		}
1820 
1821 		ri->ri_count = 0;
1822 		ri->ri_idx = 0;
1823 		mt->mt_isquarantined = LDAP_BACK_FQ_NO;
1824 	}
1825 
1826 done:;
1827 	ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
1828 }
1829 
1830