]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/conn.c
b1c4cc5724bb0d9d64b2f979202d70150ce5a2b0
[openldap] / servers / slapd / back-meta / conn.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2006 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 ravl_print( Avlnode *root, int depth )
144 {
145         int             i;
146         metaconn_t      *mc;
147         
148         if ( root == 0 ) {
149                 return;
150         }
151         
152         ravl_print( root->avl_right, depth + 1 );
153         
154         for ( i = 0; i < depth; i++ ) {
155                 fprintf( stderr, "-" );
156         }
157
158         mc = (metaconn_t *)root->avl_data;
159         fprintf( stderr, "mc=%p local=\"%s\" conn=%p %s refcnt=%d%s\n",
160                 (void *)mc,
161                 mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "",
162                 (void *)mc->mc_conn,
163                 avl_bf2str( root->avl_bf ), mc->mc_refcnt,
164                 LDAP_BACK_CONN_TAINTED( mc ) ? " tainted" : "" );
165         
166         ravl_print( root->avl_left, depth + 1 );
167 }
168
169 void
170 meta_back_print_conntree( Avlnode *root, char *msg )
171 {
172         fprintf( stderr, "========> %s\n", msg );
173         
174         if ( root == 0 ) {
175                 fprintf( stderr, "\t(empty)\n" );
176
177         } else {
178                 ravl_print( root, 0 );
179         }
180         
181         fprintf( stderr, "<======== %s\n", msg );
182 }
183 #endif /* META_BACK_PRINT_CONNTREE */
184 /*
185  * End of debug stuff
186  */
187
188 /*
189  * metaconn_alloc
190  * 
191  * Allocates a connection structure, making room for all the referenced targets
192  */
193 static metaconn_t *
194 metaconn_alloc(
195         Operation               *op )
196 {
197         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
198         metaconn_t      *mc;
199         int             ntargets = mi->mi_ntargets;
200
201         assert( ntargets > 0 );
202
203         /* malloc all in one */
204         mc = ( metaconn_t * )ch_calloc( 1, sizeof( metaconn_t )
205                         + sizeof( metasingleconn_t ) * ntargets );
206         if ( mc == NULL ) {
207                 return NULL;
208         }
209
210         mc->mc_info = mi;
211
212         mc->mc_authz_target = META_BOUND_NONE;
213         mc->mc_refcnt = 1;
214
215         return mc;
216 }
217
218 /*
219  * meta_back_init_one_conn
220  * 
221  * Initializes one connection
222  */
223 int
224 meta_back_init_one_conn(
225         Operation               *op,
226         SlapReply               *rs,
227         metaconn_t              *mc,
228         int                     candidate,
229         int                     ispriv,
230         ldap_back_send_t        sendok )
231 {
232         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
233         metatarget_t            *mt = mi->mi_targets[ candidate ];
234         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
235         int                     version;
236         dncookie                dc;
237         int                     isauthz = ( candidate == mc->mc_authz_target );
238 #ifdef HAVE_TLS
239         int                     is_ldaps = 0;
240 #endif /* HAVE_TLS */
241
242         /* if the server is quarantined, and
243          * - the current interval did not expire yet, or
244          * - no more retries should occur,
245          * don't return the connection */
246         if ( mt->mt_isquarantined ) {
247                 slap_retry_info_t       *ri = &mt->mt_quarantine;
248                 int                     dont_retry = 1;
249
250                 if ( mt->mt_isquarantined == LDAP_BACK_FQ_YES ) {
251                         dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
252                                 || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
253                         if ( !dont_retry ) {
254                                 if ( LogTest( LDAP_DEBUG_ANY ) ) {
255                                         char    buf[ SLAP_TEXT_BUFLEN ];
256
257                                         snprintf( buf, sizeof( buf ),
258                                                 "meta_back_init_one_conn[%d]: quarantine "
259                                                 "retry block #%d try #%d",
260                                                 candidate, ri->ri_idx, ri->ri_count );
261                                         Debug( LDAP_DEBUG_ANY, "%s %s.\n",
262                                                 op->o_log_prefix, buf, 0 );
263                                 }
264
265                                 mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
266                         }
267                 }
268
269                 if ( dont_retry ) {
270                         rs->sr_err = LDAP_UNAVAILABLE;
271                         if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
272                                 send_ldap_result( op, rs );
273                         }
274                         return rs->sr_err;
275                 }
276         }
277
278         /*
279          * Already init'ed
280          */
281         if ( msc->msc_ld != NULL ) {
282                 return rs->sr_err = LDAP_SUCCESS;
283         }
284
285         msc->msc_mscflags = 0;
286        
287         /*
288          * Attempts to initialize the connection to the target ds
289          */
290         ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
291         rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
292 #ifdef HAVE_TLS
293         is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
294 #endif /* HAVE_TLS */
295         ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
296         if ( rs->sr_err != LDAP_SUCCESS ) {
297                 goto error_return;
298         }
299
300         /*
301          * Set LDAP version. This will always succeed: If the client
302          * bound with a particular version, then so can we.
303          */
304         if ( mt->mt_version != 0 ) {
305                 version = mt->mt_version;
306
307         } else if ( op->o_conn->c_protocol != 0 ) {
308                 version = op->o_conn->c_protocol;
309
310         } else {
311                 version = LDAP_VERSION3;
312         }
313         ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &version );
314         ldap_set_urllist_proc( msc->msc_ld, mt->mt_urllist_f, mt->mt_urllist_p );
315
316         /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
317         ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS,
318                 LDAP_BACK_CHASE_REFERRALS( mi ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
319
320 #ifdef HAVE_TLS
321         /* start TLS ("tls [try-]{start|propagate}" statement) */
322         if ( ( LDAP_BACK_USE_TLS( mi ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( mi ) ) )
323                         && !is_ldaps )
324         {
325 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
326                 /*
327                  * use asynchronous StartTLS; in case, chase referral
328                  * FIXME: OpenLDAP does not return referral on StartTLS yet
329                  */
330                 int             msgid;
331
332                 rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
333                 if ( rs->sr_err == LDAP_SUCCESS ) {
334                         LDAPMessage     *res = NULL;
335                         int             rc, nretries = mt->mt_nretries;
336                         struct timeval  tv;
337
338                         LDAP_BACK_TV_SET( &tv );
339
340 retry:;
341                         rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
342                         switch ( rc ) {
343                         case -1:
344                                 rs->sr_err = LDAP_OTHER;
345                                 break;
346
347                         case 0:
348                                 if ( nretries != 0 ) {
349                                         if ( nretries > 0 ) {
350                                                 nretries--;
351                                         }
352                                         LDAP_BACK_TV_SET( &tv );
353                                         goto retry;
354                                 }
355                                 rs->sr_err = LDAP_OTHER;
356                                 break;
357
358                         default:
359                                 /* only touch when activity actually took place... */
360                                 if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
361                                         msc->msc_time = op->o_time;
362                                 }
363                                 break;
364                         }
365
366                         if ( rc == LDAP_RES_EXTENDED ) {
367                                 struct berval   *data = NULL;
368
369                                 /* NOTE: right now, data is unused, so don't get it */
370                                 rs->sr_err = ldap_parse_extended_result( msc->msc_ld, res,
371                                                 NULL, NULL /* &data */ , 0 );
372                                 if ( rs->sr_err == LDAP_SUCCESS ) {
373                                         int             err;
374
375                                         /* FIXME: matched? referrals? response controls? */
376                                         rs->sr_err = ldap_parse_result( msc->msc_ld, res,
377                                                 &err, NULL, NULL, NULL, NULL, 1 );
378                                         res = NULL;
379
380                                         if ( rs->sr_err == LDAP_SUCCESS ) {
381                                                 rs->sr_err = err;
382                                         }
383                                         
384                                         /* FIXME: in case a referral 
385                                          * is returned, should we try
386                                          * using it instead of the 
387                                          * configured URI? */
388                                         if ( rs->sr_err == LDAP_SUCCESS ) {
389                                                 ldap_install_tls( msc->msc_ld );
390
391                                         } else if ( rs->sr_err == LDAP_REFERRAL ) {
392                                                 /* FIXME: LDAP_OPERATIONS_ERROR? */
393                                                 rs->sr_err = LDAP_OTHER;
394                                                 rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
395                                         }
396
397                                         if ( data ) {
398                                                 if ( data->bv_val ) {
399                                                         ber_memfree( data->bv_val );
400                                                 }
401                                                 ber_memfree( data );
402                                         }
403                                 }
404
405                         } else {
406                                 rs->sr_err = LDAP_OTHER;
407                         }
408
409                         if ( res != NULL ) {
410                                 ldap_msgfree( res );
411                         }
412                 }
413 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
414                 /*
415                  * use synchronous StartTLS
416                  */
417                 rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
418 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
419
420                 /* if StartTLS is requested, only attempt it if the URL
421                  * is not "ldaps://"; this may occur not only in case
422                  * of misconfiguration, but also when used in the chain 
423                  * overlay, where the "uri" can be parsed out of a referral */
424                 if ( rs->sr_err == LDAP_SERVER_DOWN
425                                 || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( mi ) ) )
426                 {
427
428 #ifdef DEBUG_205
429                         Debug( LDAP_DEBUG_ANY, "### %s meta_back_init_one_conn(TLS) ldap_unbind_ext[%d] ld=%p\n",
430                                 op->o_log_prefix, candidate, (void *)msc->msc_ld );
431 #endif /* DEBUG_205 */
432
433                         ldap_unbind_ext( msc->msc_ld, NULL, NULL );
434                         msc->msc_ld = NULL;
435                         goto error_return;
436                 }
437         }
438 #endif /* HAVE_TLS */
439
440         /*
441          * Set the network timeout if set
442          */
443         if ( mt->mt_network_timeout != 0 ) {
444                 struct timeval  network_timeout;
445
446                 network_timeout.tv_usec = 0;
447                 network_timeout.tv_sec = mt->mt_network_timeout;
448
449                 ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
450                                 (void *)&network_timeout );
451         }
452
453         /*
454          * If the connection DN is not null, an attempt to rewrite it is made
455          */
456
457         if ( ispriv ) {
458                 if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
459                         ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
460                         if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
461                                 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
462                                         memset( msc->msc_cred.bv_val, 0,
463                                                 msc->msc_cred.bv_len );
464                                 }
465                                 ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
466                         }
467
468                 } else {
469                         ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
470                 }
471
472         } else {
473                 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
474                         memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
475                         ber_memfree_x( msc->msc_cred.bv_val, NULL );
476                         BER_BVZERO( &msc->msc_cred );
477                 }
478                 if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
479                         ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
480                         BER_BVZERO( &msc->msc_bound_ndn );
481                 }
482                 if ( !BER_BVISEMPTY( &op->o_ndn )
483                         && SLAP_IS_AUTHZ_BACKEND( op )
484                         && isauthz )
485                 {
486                         dc.target = mt;
487                         dc.conn = op->o_conn;
488                         dc.rs = rs;
489                         dc.ctx = "bindDN";
490                 
491                         /*
492                          * Rewrite the bind dn if needed
493                          */
494                         if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
495                                                 &msc->msc_bound_ndn ) )
496                         {
497
498 #ifdef DEBUG_205
499                                 Debug( LDAP_DEBUG_ANY, "### %s meta_back_init_one_conn(rewrite) ldap_unbind_ext[%d] ld=%p\n",
500                                         op->o_log_prefix, candidate, (void *)msc->msc_ld );
501 #endif /* DEBUG_205 */
502
503                                 ldap_unbind_ext( msc->msc_ld, NULL, NULL );
504                                 msc->msc_ld = NULL;
505                                 goto error_return;
506                         }
507                         
508                         /* copy the DN if needed */
509                         if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
510                                 ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
511                         }
512
513                 } else {
514                         ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
515                 }
516         }
517
518         assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
519
520 error_return:;
521         if ( rs->sr_err == LDAP_SUCCESS ) {
522                 /*
523                  * Sets a cookie for the rewrite session
524                  */
525                 ( void )rewrite_session_init( mt->mt_rwmap.rwm_rw, op->o_conn );
526
527         } else {
528                 rs->sr_err = slap_map_api2result( rs );
529                 if ( sendok & LDAP_BACK_SENDERR ) {
530                         send_ldap_result( op, rs );
531                         rs->sr_text = NULL;
532                 }
533         }
534
535         return rs->sr_err;
536 }
537
538 /*
539  * meta_back_retry
540  * 
541  * Retries one connection
542  */
543 int
544 meta_back_retry(
545         Operation               *op,
546         SlapReply               *rs,
547         metaconn_t              **mcp,
548         int                     candidate,
549         ldap_back_send_t        sendok )
550 {
551         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
552         metatarget_t            *mt = mi->mi_targets[ candidate ];
553         metaconn_t              *mc = *mcp;
554         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
555         int                     rc = LDAP_UNAVAILABLE,
556                                 binding;
557
558         ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
559         binding = LDAP_BACK_CONN_BINDING( msc );
560
561         assert( mc->mc_refcnt > 0 );
562         if ( mc->mc_refcnt == 1 ) {
563                 if ( LogTest( LDAP_DEBUG_ANY ) ) {
564                         char    buf[ SLAP_TEXT_BUFLEN ];
565
566                         ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
567                         snprintf( buf, sizeof( buf ),
568                                 "retrying URI=\"%s\" DN=\"%s\"",
569                                 mt->mt_uri,
570                                 BER_BVISNULL( &msc->msc_bound_ndn ) ?
571                                         "" : msc->msc_bound_ndn.bv_val );
572                         ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
573
574                         Debug( LDAP_DEBUG_ANY,
575                                 "%s meta_back_retry[%d]: %s.\n",
576                                 op->o_log_prefix, candidate, buf );
577                 }
578
579                 meta_clear_one_candidate( op, mc, candidate );
580                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
581
582                 ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
583
584                 /* mc here must be the regular mc, reset and ready for init */
585                 rc = meta_back_init_one_conn( op, rs, mc, candidate,
586                         LDAP_BACK_CONN_ISPRIV( mc ), sendok );
587
588                 /* restore the "binding" flag, in case */
589                 if ( binding ) {
590                         LDAP_BACK_CONN_BINDING_SET( msc );
591                 }
592
593                 if ( rc == LDAP_SUCCESS ) {
594                         rc = meta_back_single_dobind( op, rs, mcp, candidate,
595                                 sendok, mt->mt_nretries, 0 );
596
597                         Debug( LDAP_DEBUG_ANY,
598                                 "%s meta_back_retry[%d]: "
599                                 "meta_back_single_dobind=%d\n",
600                                 op->o_log_prefix, candidate, rc );
601                         if ( rc == LDAP_SUCCESS ) {
602                                 if ( !BER_BVISNULL( &msc->msc_bound_ndn ) &&
603                                         !BER_BVISEMPTY( &msc->msc_bound_ndn ) )
604                                 {
605                                         LDAP_BACK_CONN_ISBOUND_SET( msc );
606
607                                 } else {
608                                         LDAP_BACK_CONN_ISANON_SET( msc );
609                                 }
610
611                                 /* when bound, dispose of the "binding" flag */
612                                 if ( binding ) {
613                                         LDAP_BACK_CONN_BINDING_CLEAR( msc );
614                                 }
615                         }
616                 }
617         }
618
619         if ( rc != LDAP_SUCCESS ) {
620                 SlapReply               *candidates = meta_back_candidates_get( op );
621
622                 candidates[ candidate ].sr_err = rc;
623
624                 if ( *mcp != NULL ) {
625                         if ( binding ) {
626                                 LDAP_BACK_CONN_BINDING_CLEAR( msc );
627                         }
628                         LDAP_BACK_CONN_TAINTED_SET( mc );
629                         /* only release if mandatory; otherwise
630                          * let the caller do what's best before
631                          * releasing */
632                         if ( META_BACK_ONERR_STOP( mi ) ) {
633                                 meta_back_release_conn_lock( op, mc, 0 );
634                                 *mcp = NULL;
635                         }
636                 }
637
638                 if ( sendok ) {
639                         rs->sr_err = rc;
640                         rs->sr_text = NULL;
641                         send_ldap_result( op, rs );
642                 }
643         }
644
645         if ( META_BACK_TGT_QUARANTINE( mt ) ) {
646                 meta_back_quarantine( op, rs, candidate );
647         }
648
649         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
650
651         return rc == LDAP_SUCCESS ? 1 : 0;
652 }
653
654 /*
655  * callback for unique candidate selection
656  */
657 static int
658 meta_back_conn_cb( Operation *op, SlapReply *rs )
659 {
660         assert( op->o_tag == LDAP_REQ_SEARCH );
661
662         switch ( rs->sr_type ) {
663         case REP_SEARCH:
664                 ((long *)op->o_callback->sc_private)[0] = (long)op->o_private;
665                 break;
666
667         case REP_SEARCHREF:
668         case REP_RESULT:
669                 break;
670
671         default:
672                 return rs->sr_err;
673         }
674
675         return 0;
676 }
677
678
679 static int
680 meta_back_get_candidate(
681         Operation       *op,
682         SlapReply       *rs,
683         struct berval   *ndn )
684 {
685         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
686         long            candidate;
687
688         /*
689          * tries to get a unique candidate
690          * (takes care of default target)
691          */
692         candidate = meta_back_select_unique_candidate( mi, ndn );
693
694         /*
695          * if any is found, inits the connection
696          */
697         if ( candidate == META_TARGET_NONE ) {
698                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
699                 rs->sr_text = "no suitable candidate target found";
700
701         } else if ( candidate == META_TARGET_MULTIPLE ) {
702                 Filter          f = { 0 };
703                 Operation       op2 = *op;
704                 SlapReply       rs2 = { 0 };
705                 slap_callback   cb2 = { 0 };
706                 int             rc;
707
708                 /* try to get a unique match for the request ndn
709                  * among the multiple candidates available */
710                 op2.o_tag = LDAP_REQ_SEARCH;
711                 op2.o_req_dn = *ndn;
712                 op2.o_req_ndn = *ndn;
713                 op2.ors_scope = LDAP_SCOPE_BASE;
714                 op2.ors_deref = LDAP_DEREF_NEVER;
715                 op2.ors_attrs = slap_anlist_no_attrs;
716                 op2.ors_attrsonly = 0;
717                 op2.ors_limit = NULL;
718                 op2.ors_slimit = 1;
719                 op2.ors_tlimit = SLAP_NO_LIMIT;
720
721                 f.f_choice = LDAP_FILTER_PRESENT;
722                 f.f_desc = slap_schema.si_ad_objectClass;
723                 op2.ors_filter = &f;
724                 BER_BVSTR( &op2.ors_filterstr, "(objectClass=*)" );
725
726                 op2.o_callback = &cb2;
727                 cb2.sc_response = meta_back_conn_cb;
728                 cb2.sc_private = (void *)&candidate;
729
730                 rc = op->o_bd->be_search( &op2, &rs2 );
731
732                 switch ( rs2.sr_err ) {
733                 case LDAP_SUCCESS:
734                 default:
735                         rs->sr_err = rs2.sr_err;
736                         break;
737
738                 case LDAP_SIZELIMIT_EXCEEDED:
739                         /* if multiple candidates can serve the operation,
740                          * and a default target is defined, and it is
741                          * a candidate, try using it (FIXME: YMMV) */
742                         if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
743                                 && meta_back_is_candidate( mi->mi_targets[ mi->mi_defaulttarget ],
744                                                 ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
745                         {
746                                 candidate = mi->mi_defaulttarget;
747                                 rs->sr_err = LDAP_SUCCESS;
748                                 rs->sr_text = NULL;
749
750                         } else {
751                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
752                                 rs->sr_text = "cannot select unique candidate target";
753                         }
754                         break;
755                 }
756
757         } else {
758                 rs->sr_err = LDAP_SUCCESS;
759         }
760
761         return candidate;
762 }
763
764 static void     *meta_back_candidates_dummy;
765
766 static void
767 meta_back_candidates_keyfree(
768         void            *key,
769         void            *data )
770 {
771         metacandidates_t        *mc = (metacandidates_t *)data;
772
773         ber_memfree_x( mc->mc_candidates, NULL );
774         ber_memfree_x( data, NULL );
775 }
776
777 SlapReply *
778 meta_back_candidates_get( Operation *op )
779 {
780         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
781         metacandidates_t        *mc;
782
783         if ( op->o_threadctx ) {
784                 void            *data = NULL;
785
786                 ldap_pvt_thread_pool_getkey( op->o_threadctx,
787                                 &meta_back_candidates_dummy, &data, NULL );
788                 mc = (metacandidates_t *)data;
789
790         } else {
791                 mc = mi->mi_candidates;
792         }
793
794         if ( mc == NULL ) {
795                 mc = ch_calloc( sizeof( metacandidates_t ), 1 );
796                 mc->mc_ntargets = mi->mi_ntargets;
797                 mc->mc_candidates = ch_calloc( sizeof( SlapReply ), mc->mc_ntargets );
798                 if ( op->o_threadctx ) {
799                         void            *data = NULL;
800
801                         data = (void *)mc;
802                         ldap_pvt_thread_pool_setkey( op->o_threadctx,
803                                         &meta_back_candidates_dummy, data,
804                                         meta_back_candidates_keyfree );
805
806                 } else {
807                         mi->mi_candidates = mc;
808                 }
809
810         } else if ( mc->mc_ntargets < mi->mi_ntargets ) {
811                 /* NOTE: in the future, may want to allow back-config
812                  * to add/remove targets from back-meta... */
813                 mc->mc_candidates = ch_realloc( mc->mc_candidates,
814                                 sizeof( SlapReply ) * mi->mi_ntargets );
815                 memset( &mc->mc_candidates[ mc->mc_ntargets ], 0,
816                         sizeof( SlapReply ) * ( mi->mi_ntargets - mc->mc_ntargets ) );
817                 mc->mc_ntargets = mi->mi_ntargets;
818         }
819
820         return mc->mc_candidates;
821 }
822
823 /*
824  * meta_back_getconn
825  * 
826  * Prepares the connection structure
827  * 
828  * RATIONALE:
829  *
830  * - determine what DN is being requested:
831  *
832  *      op      requires candidate      checks
833  *
834  *      add     unique                  parent of o_req_ndn
835  *      bind    unique^*[/all]          o_req_ndn [no check]
836  *      compare unique^+                o_req_ndn
837  *      delete  unique                  o_req_ndn
838  *      modify  unique                  o_req_ndn
839  *      search  any                     o_req_ndn
840  *      modrdn  unique[, unique]        o_req_ndn[, orr_nnewSup]
841  *
842  * - for ops that require the candidate to be unique, in case of multiple
843  *   occurrences an internal search with sizeLimit=1 is performed
844  *   if a unique candidate can actually be determined.  If none is found,
845  *   the operation aborts; if multiple are found, the default target
846  *   is used if defined and candidate; otherwise the operation aborts.
847  *
848  * *^note: actually, the bind operation is handled much like a search;
849  *   i.e. the bind is broadcast to all candidate targets.
850  *
851  * +^note: actually, the compare operation is handled much like a search;
852  *   i.e. the compare is broadcast to all candidate targets, while checking
853  *   that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
854  *   returned.
855  */
856 metaconn_t *
857 meta_back_getconn(
858         Operation               *op,
859         SlapReply               *rs,
860         int                     *candidate,
861         ldap_back_send_t        sendok )
862 {
863         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
864         metaconn_t      *mc = NULL,
865                         mc_curr = { 0 };
866         int             cached = META_TARGET_NONE,
867                         i = META_TARGET_NONE,
868                         err = LDAP_SUCCESS,
869                         new_conn = 0,
870                         ncandidates = 0;
871
872
873         meta_op_type    op_type = META_OP_REQUIRE_SINGLE;
874         enum            {
875                 META_DNTYPE_ENTRY,
876                 META_DNTYPE_PARENT,
877                 META_DNTYPE_NEWPARENT
878         }               dn_type = META_DNTYPE_ENTRY;
879         struct berval   ndn = op->o_req_ndn,
880                         pndn;
881
882         SlapReply       *candidates = meta_back_candidates_get( op );
883
884         /* Internal searches are privileged and shared. So is root. */
885         /* FIXME: there seem to be concurrency issues */
886         if ( op->o_do_not_cache || be_isroot( op ) ) {
887                 mc_curr.mc_local_ndn = op->o_bd->be_rootndn;
888                 LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
889                 mc_curr.mc_conn = LDAP_BACK_PCONN_SET( op );
890
891         } else {
892                 mc_curr.mc_local_ndn = op->o_ndn;
893
894                 /* Explicit binds must not be shared */
895                 if ( op->o_tag == LDAP_REQ_BIND || SLAP_IS_AUTHZ_BACKEND( op ) ) {
896                         mc_curr.mc_conn = op->o_conn;
897         
898                 } else {
899                         mc_curr.mc_conn = LDAP_BACK_PCONN_SET( op );
900                 }
901         }
902
903         /* Explicit Bind requests always get their own conn */
904         if ( !( sendok & LDAP_BACK_BINDING ) ) {
905                 /* Searches for a metaconn in the avl tree */
906 retry_lock:;
907                 ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
908                 mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, 
909                         (caddr_t)&mc_curr, meta_back_conndn_cmp );
910                 if ( mc ) {
911                         if ( ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl )
912                                 || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) )
913                         {
914 #if META_BACK_PRINT_CONNTREE > 0
915                                 meta_back_print_conntree( mi->mi_conninfo.lai_tree, ">>> meta_back_getconn" );
916 #endif /* META_BACK_PRINT_CONNTREE */
917                                 /* don't let anyone else use this expired connection */
918                                 (void)avl_delete( &mi->mi_conninfo.lai_tree,
919                                         (caddr_t)mc, meta_back_conndnmc_cmp );
920 #if META_BACK_PRINT_CONNTREE > 0
921                                 meta_back_print_conntree( mi->mi_conninfo.lai_tree, "<<< meta_back_getconn" );
922 #endif /* META_BACK_PRINT_CONNTREE */
923                                 LDAP_BACK_CONN_TAINTED_SET( mc );
924
925                                 Debug( LDAP_DEBUG_TRACE, "%s meta_back_getconn: mc=%p conn=%ld expired.\n",
926                                         op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc ) );
927                         }
928
929                         /* Don't reuse connections while they're still binding
930                          * NOTE: only makes sense for binds */
931                         if ( LDAP_BACK_CONN_BINDING( mc ) ) {
932                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
933                                 ldap_pvt_thread_yield();
934                                 goto retry_lock;
935                         }
936
937                         mc->mc_refcnt++;
938                 }
939                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
940         }
941
942         switch ( op->o_tag ) {
943         case LDAP_REQ_ADD:
944                 /* if we go to selection, the entry must not exist,
945                  * and we must be able to resolve the parent */
946                 dn_type = META_DNTYPE_PARENT;
947                 dnParent( &ndn, &pndn );
948                 break;
949
950         case LDAP_REQ_MODRDN:
951                 /* if nnewSuperior is not NULL, it must resolve
952                  * to the same candidate as the req_ndn */
953                 if ( op->orr_nnewSup ) {
954                         dn_type = META_DNTYPE_NEWPARENT;
955                 }
956                 break;
957
958         case LDAP_REQ_BIND:
959                 /* if bound as rootdn, the backend must bind to all targets
960                  * with the administrative identity */
961                 if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
962                         op_type = META_OP_REQUIRE_ALL;
963                 }
964                 break;
965
966         case LDAP_REQ_COMPARE:
967         case LDAP_REQ_DELETE:
968         case LDAP_REQ_MODIFY:
969                 /* just a unique candidate */
970                 break;
971
972         case LDAP_REQ_SEARCH:
973                 /* allow multiple candidates for the searchBase */
974                 op_type = META_OP_ALLOW_MULTIPLE;
975                 break;
976
977         default:
978                 /* right now, just break (exop?) */
979                 break;
980         }
981
982         /*
983          * require all connections ...
984          */
985         if ( op_type == META_OP_REQUIRE_ALL ) {
986
987                 /* Looks like we didn't get a bind. Open a new session... */
988                 if ( mc == NULL ) {
989                         assert( new_conn == 0 );
990                         mc = metaconn_alloc( op );
991                         mc->mc_conn = mc_curr.mc_conn;
992                         ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
993                         new_conn = 1;
994                         if ( sendok & LDAP_BACK_BINDING ) {
995                                 LDAP_BACK_CONN_BINDING_SET( mc );
996                         }
997                         if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
998                                 LDAP_BACK_CONN_ISPRIV_SET( mc );
999                         }
1000
1001                 } else if ( 0 ) {
1002                         /* TODO: if any of the connections is binding,
1003                          * release mc and create a new one */
1004                 }
1005
1006                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
1007                         /*
1008                          * The target is activated; if needed, it is
1009                          * also init'd
1010                          */
1011                         candidates[ i ].sr_err = meta_back_init_one_conn( op,
1012                                 rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
1013                                 sendok );
1014                         if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
1015                                 META_CANDIDATE_SET( &candidates[ i ] );
1016                                 ncandidates++;
1017         
1018                         } else {
1019                                 
1020                                 /*
1021                                  * FIXME: in case one target cannot
1022                                  * be init'd, should the other ones
1023                                  * be tried?
1024                                  */
1025                                 META_CANDIDATE_RESET( &candidates[ i ] );
1026                                 err = candidates[ i ].sr_err;
1027                                 continue;
1028                         }
1029                 }
1030
1031                 if ( ncandidates == 0 ) {
1032                         if ( new_conn ) {
1033                                 mc->mc_refcnt = 0;
1034                                 meta_back_conn_free( mc );
1035
1036                         } else {
1037                                 meta_back_release_conn( op, mc );
1038                         }
1039
1040                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
1041                         rs->sr_text = "Unable to select valid candidates";
1042
1043                         if ( sendok & LDAP_BACK_SENDERR ) {
1044                                 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1045                                         rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1046                                 }
1047                                 send_ldap_result( op, rs );
1048                                 rs->sr_text = NULL;
1049                                 rs->sr_matched = NULL;
1050                         }
1051
1052                         return NULL;
1053                 }
1054
1055                 goto done;
1056         }
1057         
1058         /*
1059          * looks in cache, if any
1060          */
1061         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
1062                 cached = i = meta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
1063         }
1064
1065         if ( op_type == META_OP_REQUIRE_SINGLE ) {
1066                 metatarget_t            *mt = NULL;
1067                 metasingleconn_t        *msc = NULL;
1068
1069                 int                     j;
1070
1071                 for ( j = 0; j < mi->mi_ntargets; j++ ) {
1072                         META_CANDIDATE_RESET( &candidates[ j ] );
1073                 }
1074
1075                 /*
1076                  * tries to get a unique candidate
1077                  * (takes care of default target)
1078                  */
1079                 if ( i == META_TARGET_NONE ) {
1080                         i = meta_back_get_candidate( op, rs, &ndn );
1081
1082                         if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && dn_type == META_DNTYPE_PARENT ) {
1083                                 i = meta_back_get_candidate( op, rs, &pndn );
1084                         }
1085         
1086                         if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
1087                                 if ( mc != NULL ) {
1088                                         meta_back_release_conn( op, mc );
1089                                 }
1090
1091                                 if ( sendok & LDAP_BACK_SENDERR ) {
1092                                         if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1093                                                 rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1094                                         }
1095                                         send_ldap_result( op, rs );
1096                                         rs->sr_text = NULL;
1097                                         rs->sr_matched = NULL;
1098                                 }
1099                         
1100                                 return NULL;
1101                         }
1102                 }
1103
1104                 if ( dn_type == META_DNTYPE_NEWPARENT && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
1105                 {
1106                         if ( mc != NULL ) {
1107                                 meta_back_release_conn( op, mc );
1108                         }
1109
1110                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1111                         rs->sr_text = "cross-target rename not supported";
1112                         if ( sendok & LDAP_BACK_SENDERR ) {
1113                                 send_ldap_result( op, rs );
1114                                 rs->sr_text = NULL;
1115                         }
1116
1117                         return NULL;
1118                 }
1119
1120                 Debug( LDAP_DEBUG_TRACE,
1121         "==>meta_back_getconn: got target=%d for ndn=\"%s\" from cache\n",
1122                                 i, op->o_req_ndn.bv_val, 0 );
1123
1124                 if ( mc == NULL ) {
1125                         /* Retries searching for a metaconn in the avl tree
1126                          * the reason is that the connection might have been
1127                          * created by meta_back_get_candidate() */
1128                         if ( !( sendok & LDAP_BACK_BINDING ) ) {
1129 retry_lock2:;
1130                                 ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1131                                 mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, 
1132                                         (caddr_t)&mc_curr, meta_back_conndn_cmp );
1133                                 if ( mc != NULL ) {
1134                                         /* Don't reuse connections while they're still binding */
1135                                         if ( LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) ) {
1136                                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1137                                                 ldap_pvt_thread_yield();
1138                                                 goto retry_lock2;
1139                                         }
1140                                         mc->mc_refcnt++;
1141                                 }
1142                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1143                         }
1144
1145                         /* Looks like we didn't get a bind. Open a new session... */
1146                         if ( mc == NULL ) {
1147                                 assert( new_conn == 0 );
1148                                 mc = metaconn_alloc( op );
1149                                 mc->mc_conn = mc_curr.mc_conn;
1150                                 ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
1151                                 new_conn = 1;
1152                                 if ( sendok & LDAP_BACK_BINDING ) {
1153                                         LDAP_BACK_CONN_BINDING_SET( mc );
1154                                 }
1155                                 if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
1156                                         LDAP_BACK_CONN_ISPRIV_SET( mc );
1157                                 }
1158                         }
1159                 }
1160
1161                 /*
1162                  * Clear all other candidates
1163                  */
1164                 ( void )meta_clear_unused_candidates( op, i );
1165
1166                 mt = mi->mi_targets[ i ];
1167                 msc = &mc->mc_conns[ i ];
1168
1169                 /*
1170                  * The target is activated; if needed, it is
1171                  * also init'd. In case of error, meta_back_init_one_conn
1172                  * sends the appropriate result.
1173                  */
1174                 err = meta_back_init_one_conn( op, rs, mc, i,
1175                         LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok );
1176                 if ( err != LDAP_SUCCESS ) {
1177                         /*
1178                          * FIXME: in case one target cannot
1179                          * be init'd, should the other ones
1180                          * be tried?
1181                          */
1182                         META_CANDIDATE_RESET( &candidates[ i ] );
1183                         if ( new_conn ) {
1184                                 mc->mc_refcnt = 0;
1185                                 meta_back_conn_free( mc );
1186
1187                         } else {
1188                                 meta_back_release_conn( op, mc );
1189                         }
1190                         return NULL;
1191                 }
1192
1193                 candidates[ i ].sr_err = LDAP_SUCCESS;
1194                 META_CANDIDATE_SET( &candidates[ i ] );
1195                 ncandidates++;
1196
1197                 if ( candidate ) {
1198                         *candidate = i;
1199                 }
1200
1201         /*
1202          * if no unique candidate ...
1203          */
1204         } else {
1205
1206                 /* Looks like we didn't get a bind. Open a new session... */
1207                 if ( mc == NULL ) {
1208                         assert( new_conn == 0 );
1209                         mc = metaconn_alloc( op );
1210                         mc->mc_conn = mc_curr.mc_conn;
1211                         ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
1212                         new_conn = 1;
1213                         if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
1214                                 LDAP_BACK_CONN_ISPRIV_SET( mc );
1215                         }
1216                 }
1217
1218                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
1219                         metatarget_t            *mt = mi->mi_targets[ i ];
1220
1221                         META_CANDIDATE_RESET( &candidates[ i ] );
1222
1223                         if ( i == cached 
1224                                 || meta_back_is_candidate( mt, &op->o_req_ndn,
1225                                         LDAP_SCOPE_SUBTREE ) )
1226                         {
1227
1228                                 /*
1229                                  * The target is activated; if needed, it is
1230                                  * also init'd
1231                                  */
1232                                 int lerr = meta_back_init_one_conn( op, rs, mc, i,
1233                                         LDAP_BACK_CONN_ISPRIV( &mc_curr ), LDAP_BACK_DONTSEND );
1234                                 if ( lerr == LDAP_SUCCESS ) {
1235                                         META_CANDIDATE_SET( &candidates[ i ] );
1236                                         candidates[ i ].sr_err = LDAP_SUCCESS;
1237                                         ncandidates++;
1238
1239                                         Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d]\n",
1240                                                 op->o_log_prefix, i, 0 );
1241
1242                                 } else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
1243                                         META_CANDIDATE_SET( &candidates[ i ] );
1244                                         candidates[ i ].sr_err = LDAP_UNAVAILABLE;
1245
1246                                         Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] %s\n",
1247                                                 op->o_log_prefix, i,
1248                                                 mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
1249
1250                                 } else {
1251                                 
1252                                         /*
1253                                          * FIXME: in case one target cannot
1254                                          * be init'd, should the other ones
1255                                          * be tried?
1256                                          */
1257                                         if ( new_conn ) {
1258                                                 ( void )meta_clear_one_candidate( op, mc, i );
1259                                         }
1260                                         /* leave the target candidate, but record the error for later use */
1261                                         candidates[ i ].sr_err = lerr;
1262                                         err = lerr;
1263
1264                                         if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
1265                                                 Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] quarantined: %d\n",
1266                                                         op->o_log_prefix, i, lerr );
1267
1268                                         } else {
1269                                                 Debug( LDAP_DEBUG_ANY, "%s: meta_back_getconn[%d] failed: %d\n",
1270                                                         op->o_log_prefix, i, lerr );
1271                                         }
1272
1273                                         if ( META_BACK_ONERR_STOP( mi ) ) {
1274                                                 if ( sendok & LDAP_BACK_SENDERR ) {
1275                                                         send_ldap_result( op, rs );
1276                                                         rs->sr_text = NULL;
1277                                                 }
1278                                                 if ( new_conn ) {
1279                                                         mc->mc_refcnt = 0;
1280                                                         meta_back_conn_free( mc );
1281
1282                                                 } else {
1283                                                         meta_back_release_conn( op, mc );
1284                                                 }
1285
1286                                                 return NULL;
1287                                         }
1288
1289                                         rs->sr_text = NULL;
1290                                         continue;
1291                                 }
1292
1293                         } else {
1294                                 if ( new_conn ) {
1295                                         ( void )meta_clear_one_candidate( op, mc, i );
1296                                 }
1297                         }
1298                 }
1299
1300                 if ( ncandidates == 0 ) {
1301                         if ( new_conn ) {
1302                                 mc->mc_refcnt = 0;
1303                                 meta_back_conn_free( mc );
1304
1305                         } else {
1306                                 meta_back_release_conn( op, mc );
1307                         }
1308
1309                         if ( rs->sr_err == LDAP_SUCCESS ) {
1310                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1311                                 rs->sr_text = "Unable to select valid candidates";
1312                         }
1313
1314                         if ( sendok & LDAP_BACK_SENDERR ) {
1315                                 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1316                                         rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1317                                 }
1318                                 send_ldap_result( op, rs );
1319                                 rs->sr_text = NULL;
1320                                 rs->sr_matched = NULL;
1321                         }
1322
1323                         return NULL;
1324                 }
1325         }
1326
1327 done:;
1328         /* clear out meta_back_init_one_conn non-fatal errors */
1329         rs->sr_err = LDAP_SUCCESS;
1330         rs->sr_text = NULL;
1331
1332         /* touch the timestamp */
1333         if ( mi->mi_idle_timeout != 0 ) {
1334                 mc->mc_time = op->o_time;
1335         }
1336
1337         if ( new_conn ) {
1338                 if ( mi->mi_conn_ttl ) {
1339                         mc->mc_create_time = op->o_time;
1340                 }
1341
1342                 /*
1343                  * Inserts the newly created metaconn in the avl tree
1344                  */
1345                 ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1346 #if META_BACK_PRINT_CONNTREE > 0
1347                 meta_back_print_conntree( mi->mi_conninfo.lai_tree, ">>> meta_back_getconn" );
1348 #endif /* META_BACK_PRINT_CONNTREE */
1349                 err = avl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc,
1350                                 meta_back_conndn_cmp, meta_back_conndn_dup );
1351 #if META_BACK_PRINT_CONNTREE > 0
1352                 meta_back_print_conntree( mi->mi_conninfo.lai_tree, ">>> meta_back_getconn" );
1353 #endif /* META_BACK_PRINT_CONNTREE */
1354                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1355
1356                 /*
1357                  * Err could be -1 in case a duplicate metaconn is inserted
1358                  */
1359                 switch ( err ) {
1360                 case 0:
1361                         break;
1362
1363                 case -1:
1364                         /* duplicate: free and try to get the newly created one */
1365                         if ( !( sendok & LDAP_BACK_BINDING ) ) {
1366                                 new_conn = 0;
1367                                 goto retry_lock;
1368                         }
1369                         LDAP_BACK_CONN_TAINTED_SET( mc );
1370                         break;
1371
1372                 default:
1373                         Debug( LDAP_DEBUG_ANY,
1374                                 "%s meta_back_getconn: candidates=%d conn=%ld insert failed\n",
1375                                 op->o_log_prefix, ncandidates,
1376                                 LDAP_BACK_PCONN_ID( mc ) );
1377         
1378                         mc->mc_refcnt = 0;      
1379                         meta_back_conn_free( mc );
1380
1381                         rs->sr_err = LDAP_OTHER;
1382                         rs->sr_text = "proxy bind collision";
1383                         if ( sendok & LDAP_BACK_SENDERR ) {
1384                                 send_ldap_result( op, rs );
1385                                 rs->sr_text = NULL;
1386                         }
1387                         return NULL;
1388                 }
1389
1390                 Debug( LDAP_DEBUG_TRACE,
1391                         "%s meta_back_getconn: candidates=%d conn=%ld inserted\n",
1392                         op->o_log_prefix, ncandidates,
1393                         LDAP_BACK_PCONN_ID( mc ) );
1394
1395         } else {
1396                 Debug( LDAP_DEBUG_TRACE,
1397                         "%s meta_back_getconn: candidates=%d conn=%ld fetched\n",
1398                         op->o_log_prefix, ncandidates,
1399                         LDAP_BACK_PCONN_ID( mc ) );
1400         }
1401         
1402         return mc;
1403 }
1404
1405 void
1406 meta_back_release_conn_lock(
1407         Operation               *op,
1408         metaconn_t              *mc,
1409         int                     dolock )
1410 {
1411         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
1412
1413         assert( mc != NULL );
1414
1415         if ( dolock ) {
1416                 ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1417         }
1418         assert( mc->mc_refcnt > 0 );
1419         mc->mc_refcnt--;
1420         /* NOTE: the connection is removed if either it is tainted
1421          * or if it is shared and no one else is using it.  This needs
1422          * to occur because for intrinsic reasons cached connections
1423          * that are not privileged would live forever and pollute
1424          * the connection space (and eat up resources).  Maybe this
1425          * should be configurable... */
1426         if ( LDAP_BACK_CONN_TAINTED( mc ) || 
1427                 ( !LDAP_BACK_CONN_ISPRIV( mc ) &&
1428                         LDAP_BACK_PCONN_ISPRIV( mc ) && 
1429                         mc->mc_refcnt == 0 ) )
1430         {
1431                 Debug( LDAP_DEBUG_TRACE, "%s meta_back_release_conn: mc=%p conn=%ld tainted.\n",
1432                         op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc ) );
1433 #if META_BACK_PRINT_CONNTREE > 0
1434                 meta_back_print_conntree( mi->mi_conninfo.lai_tree, ">>> meta_back_release_conn" );
1435 #endif /* META_BACK_PRINT_CONNTREE */
1436                 (void)avl_delete( &mi->mi_conninfo.lai_tree,
1437                         ( caddr_t )mc, meta_back_conndnmc_cmp );
1438 #if META_BACK_PRINT_CONNTREE > 0
1439                 meta_back_print_conntree( mi->mi_conninfo.lai_tree, "<<< meta_back_release_conn" );
1440 #endif /* META_BACK_PRINT_CONNTREE */
1441                 if ( mc->mc_refcnt == 0 ) {
1442                         meta_back_conn_free( mc );
1443                         mc = NULL;
1444                 }
1445         }
1446
1447         if ( mc != NULL ) {
1448                 LDAP_BACK_CONN_BINDING_CLEAR( mc );
1449         }
1450
1451         if ( dolock ) {
1452                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1453         }
1454 }
1455
1456 void
1457 meta_back_quarantine(
1458         Operation       *op,
1459         SlapReply       *rs,
1460         int             candidate )
1461 {
1462         metainfo_t              *mi = (metainfo_t *)op->o_bd->be_private;
1463         metatarget_t            *mt = mi->mi_targets[ candidate ];
1464
1465         slap_retry_info_t       *ri = &mt->mt_quarantine;
1466
1467         ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
1468
1469         if ( rs->sr_err == LDAP_UNAVAILABLE ) {
1470                 time_t  new_last = slap_get_time();
1471
1472                 switch ( mt->mt_isquarantined ) {
1473                 case LDAP_BACK_FQ_NO:
1474                         if ( ri->ri_last == new_last ) {
1475                                 goto done;
1476                         }
1477
1478                         Debug( LDAP_DEBUG_ANY,
1479                                 "%s meta_back_quarantine[%d]: enter.\n",
1480                                 op->o_log_prefix, candidate, 0 );
1481
1482                         ri->ri_idx = 0;
1483                         ri->ri_count = 0;
1484                         break;
1485
1486                 case LDAP_BACK_FQ_RETRYING:
1487                         if ( LogTest( LDAP_DEBUG_ANY ) ) {
1488                                 char    buf[ SLAP_TEXT_BUFLEN ];
1489
1490                                 snprintf( buf, sizeof( buf ),
1491                                         "meta_back_quarantine[%d]: block #%d try #%d failed",
1492                                         candidate, ri->ri_idx, ri->ri_count );
1493                                 Debug( LDAP_DEBUG_ANY, "%s %s.\n",
1494                                         op->o_log_prefix, buf, 0 );
1495                         }
1496
1497                         ++ri->ri_count;
1498                         if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
1499                                 && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
1500                         {
1501                                 ri->ri_count = 0;
1502                                 ++ri->ri_idx;
1503                         }
1504                         break;
1505
1506                 default:
1507                         break;
1508                 }
1509
1510                 mt->mt_isquarantined = LDAP_BACK_FQ_YES;
1511                 ri->ri_last = new_last;
1512
1513         } else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
1514                 Debug( LDAP_DEBUG_ANY,
1515                         "%s meta_back_quarantine[%d]: exit.\n",
1516                         op->o_log_prefix, candidate, 0 );
1517
1518                 if ( mi->mi_quarantine_f ) {
1519                         (void)mi->mi_quarantine_f( mi, candidate,
1520                                 mi->mi_quarantine_p );
1521                 }
1522
1523                 ri->ri_count = 0;
1524                 ri->ri_idx = 0;
1525                 mt->mt_isquarantined = LDAP_BACK_FQ_NO;
1526         }
1527
1528 done:;
1529         ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
1530 }
1531