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