Back to index

openldap  2.4.31
conn.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1999-2012 The OpenLDAP Foundation.
00005  * Portions Copyright 2001-2003 Pierangelo Masarati.
00006  * Portions Copyright 1999-2003 Howard Chu.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by the Howard Chu for inclusion
00019  * in OpenLDAP Software and subsequently enhanced by Pierangelo
00020  * Masarati.
00021  */
00022 
00023 #include "portable.h"
00024 
00025 #include <stdio.h>
00026 
00027 #include <ac/errno.h>
00028 #include <ac/socket.h>
00029 #include <ac/string.h>
00030 
00031 
00032 #define AVL_INTERNAL
00033 #include "slap.h"
00034 #include "../back-ldap/back-ldap.h"
00035 #include "back-meta.h"
00036 
00037 /*
00038  * meta_back_conndn_cmp
00039  *
00040  * compares two struct metaconn based on the value of the conn pointer
00041  * and of the local DN; used by avl stuff
00042  */
00043 int
00044 meta_back_conndn_cmp(
00045        const void *c1,
00046        const void *c2 )
00047 {
00048        metaconn_t    *mc1 = ( metaconn_t * )c1;
00049         metaconn_t   *mc2 = ( metaconn_t * )c2;
00050        int           rc;
00051        
00052        /* If local DNs don't match, it is definitely not a match */
00053        /* For shared sessions, conn is NULL. Only explicitly
00054         * bound sessions will have non-NULL conn.
00055         */
00056        rc = SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
00057        if ( rc == 0 ) {
00058               rc = ber_bvcmp( &mc1->mc_local_ndn, &mc2->mc_local_ndn );
00059        }
00060 
00061        return rc;
00062 }
00063 
00064 /*
00065  * meta_back_conndnmc_cmp
00066  *
00067  * compares two struct metaconn based on the value of the conn pointer,
00068  * the local DN and the struct pointer; used by avl stuff
00069  */
00070 static int
00071 meta_back_conndnmc_cmp(
00072        const void *c1,
00073        const void *c2 )
00074 {
00075        metaconn_t    *mc1 = ( metaconn_t * )c1;
00076         metaconn_t   *mc2 = ( metaconn_t * )c2;
00077        int           rc;
00078        
00079        /* If local DNs don't match, it is definitely not a match */
00080        /* For shared sessions, conn is NULL. Only explicitly
00081         * bound sessions will have non-NULL conn.
00082         */
00083        rc = SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
00084        if ( rc == 0 ) {
00085               rc = ber_bvcmp( &mc1->mc_local_ndn, &mc2->mc_local_ndn );
00086               if ( rc == 0 ) {
00087                      rc = SLAP_PTRCMP( mc1, mc2 );
00088               }
00089        }
00090 
00091        return rc;
00092 }
00093 
00094 /*
00095  * meta_back_conn_cmp
00096  *
00097  * compares two struct metaconn based on the value of the conn pointer;
00098  * used by avl stuff
00099  */
00100 int
00101 meta_back_conn_cmp(
00102        const void *c1,
00103        const void *c2 )
00104 {
00105        metaconn_t    *mc1 = ( metaconn_t * )c1;
00106         metaconn_t   *mc2 = ( metaconn_t * )c2;
00107        
00108        /* For shared sessions, conn is NULL. Only explicitly
00109         * bound sessions will have non-NULL conn.
00110         */
00111        return SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
00112 }
00113 
00114 /*
00115  * meta_back_conndn_dup
00116  *
00117  * returns -1 in case a duplicate struct metaconn has been inserted;
00118  * used by avl stuff
00119  */
00120 int
00121 meta_back_conndn_dup(
00122        void *c1,
00123        void *c2 )
00124 {
00125        metaconn_t    *mc1 = ( metaconn_t * )c1;
00126        metaconn_t    *mc2 = ( metaconn_t * )c2;
00127 
00128        /* Cannot have more than one shared session with same DN */
00129        if ( mc1->mc_conn == mc2->mc_conn &&
00130               dn_match( &mc1->mc_local_ndn, &mc2->mc_local_ndn ) )
00131        {
00132               return -1;
00133        }
00134               
00135        return 0;
00136 }
00137 
00138 /*
00139  * Debug stuff (got it from libavl)
00140  */
00141 #if META_BACK_PRINT_CONNTREE > 0
00142 static void
00143 meta_back_print( metaconn_t *mc, char *avlstr )
00144 {
00145        int    i;
00146 
00147        fputs( "targets=[", stderr );
00148        for ( i = 0; i < mc->mc_info->mi_ntargets; i++ ) {
00149               fputc( mc->mc_conns[ i ].msc_ld ? '*' : 'o', stderr);
00150        }
00151        fputc( ']', stderr );
00152 
00153        fprintf( stderr, " mc=%p local=\"%s\" conn=%p refcnt=%d%s %s\n",
00154               (void *)mc,
00155               mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "",
00156               (void *)mc->mc_conn,
00157               mc->mc_refcnt,
00158               LDAP_BACK_CONN_TAINTED( mc ) ? " tainted" : "",
00159               avlstr );
00160 }
00161 
00162 static void
00163 meta_back_ravl_print( Avlnode *root, int depth )
00164 {
00165        int           i;
00166 
00167        if ( root == 0 ) {
00168               return;
00169        }
00170        
00171        meta_back_ravl_print( root->avl_right, depth + 1 );
00172        
00173        for ( i = 0; i < depth; i++ ) {
00174               fprintf( stderr, "-" );
00175        }
00176        fputc( ' ', stderr );
00177 
00178        meta_back_print( (metaconn_t *)root->avl_data,
00179               avl_bf2str( root->avl_bf ) );
00180 
00181        meta_back_ravl_print( root->avl_left, depth + 1 );
00182 }
00183 
00184 /* NOTE: duplicate from back-ldap/bind.c */
00185 static char* priv2str[] = {
00186        "privileged",
00187        "privileged/TLS",
00188        "anonymous",
00189        "anonymous/TLS",
00190        "bind",
00191        "bind/TLS",
00192        NULL
00193 };
00194 
00195 void
00196 meta_back_print_conntree( metainfo_t *mi, char *msg )
00197 {
00198        int    c;
00199 
00200        fprintf( stderr, "========> %s\n", msg );
00201        
00202        for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
00203               int           i = 0;
00204               metaconn_t    *mc;
00205 
00206               fprintf( stderr, "  %s[%d]\n", priv2str[ c ], mi->mi_conn_priv[ c ].mic_num );
00207 
00208               LDAP_TAILQ_FOREACH( mc, &mi->mi_conn_priv[ c ].mic_priv, mc_q )
00209               {
00210                      fprintf( stderr, "    [%d] ", i );
00211                      meta_back_print( mc, "" );
00212                      i++;
00213               }
00214        }
00215        
00216        if ( mi->mi_conninfo.lai_tree == NULL ) {
00217               fprintf( stderr, "\t(empty)\n" );
00218 
00219        } else {
00220               meta_back_ravl_print( mi->mi_conninfo.lai_tree, 0 );
00221        }
00222        
00223        fprintf( stderr, "<======== %s\n", msg );
00224 }
00225 #endif /* META_BACK_PRINT_CONNTREE */
00226 /*
00227  * End of debug stuff
00228  */
00229 
00230 /*
00231  * metaconn_alloc
00232  * 
00233  * Allocates a connection structure, making room for all the referenced targets
00234  */
00235 static metaconn_t *
00236 metaconn_alloc(
00237               Operation            *op )
00238 {
00239        metainfo_t    *mi = ( metainfo_t * )op->o_bd->be_private;
00240        metaconn_t    *mc;
00241        int           ntargets = mi->mi_ntargets;
00242 
00243        assert( ntargets > 0 );
00244 
00245        /* malloc all in one */
00246        mc = ( metaconn_t * )ch_calloc( 1, sizeof( metaconn_t )
00247               + sizeof( metasingleconn_t ) * ( ntargets - 1 ) );
00248        if ( mc == NULL ) {
00249               return NULL;
00250        }
00251 
00252        mc->mc_info = mi;
00253 
00254        mc->mc_authz_target = META_BOUND_NONE;
00255        mc->mc_refcnt = 1;
00256 
00257        return mc;
00258 }
00259 
00260 /*
00261  * meta_back_init_one_conn
00262  * 
00263  * Initializes one connection
00264  */
00265 int
00266 meta_back_init_one_conn(
00267        Operation            *op,
00268        SlapReply            *rs,
00269        metaconn_t           *mc,
00270        int                  candidate,
00271        int                  ispriv,
00272        ldap_back_send_t     sendok,
00273        int                  dolock )
00274 {
00275        metainfo_t           *mi = ( metainfo_t * )op->o_bd->be_private;
00276        metatarget_t         *mt = mi->mi_targets[ candidate ];
00277        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
00278        int                  version;
00279        dncookie             dc;
00280        int                  isauthz = ( candidate == mc->mc_authz_target );
00281        int                  do_return = 0;
00282 #ifdef HAVE_TLS
00283        int                  is_ldaps = 0;
00284        int                  do_start_tls = 0;
00285 #endif /* HAVE_TLS */
00286 
00287        /* if the server is quarantined, and
00288         * - the current interval did not expire yet, or
00289         * - no more retries should occur,
00290         * don't return the connection */
00291        if ( mt->mt_isquarantined ) {
00292               slap_retry_info_t    *ri = &mt->mt_quarantine;
00293               int                  dont_retry = 0;
00294 
00295               if ( mt->mt_quarantine.ri_interval ) {
00296                      ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
00297                      dont_retry = ( mt->mt_isquarantined > LDAP_BACK_FQ_NO );
00298                      if ( dont_retry ) {
00299                             dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
00300                                    || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
00301                             if ( !dont_retry ) {
00302                                    if ( LogTest( LDAP_DEBUG_ANY ) ) {
00303                                           char   buf[ SLAP_TEXT_BUFLEN ];
00304 
00305                                           snprintf( buf, sizeof( buf ),
00306                                                  "meta_back_init_one_conn[%d]: quarantine "
00307                                                  "retry block #%d try #%d",
00308                                                  candidate, ri->ri_idx, ri->ri_count );
00309                                           Debug( LDAP_DEBUG_ANY, "%s %s.\n",
00310                                                  op->o_log_prefix, buf, 0 );
00311                                    }
00312 
00313                                    mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
00314                             }
00315 
00316                      }
00317                      ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
00318               }
00319 
00320               if ( dont_retry ) {
00321                      rs->sr_err = LDAP_UNAVAILABLE;
00322                      if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
00323                             rs->sr_text = "Target is quarantined";
00324                             send_ldap_result( op, rs );
00325                      }
00326                      return rs->sr_err;
00327               }
00328        }
00329 
00330 retry_lock:;
00331        if ( dolock ) {
00332               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00333        }
00334 
00335        /*
00336         * Already init'ed
00337         */
00338        if ( LDAP_BACK_CONN_ISBOUND( msc )
00339               || LDAP_BACK_CONN_ISANON( msc ) )
00340        {
00341               assert( msc->msc_ld != NULL );
00342               rs->sr_err = LDAP_SUCCESS;
00343               do_return = 1;
00344 
00345        } else if ( META_BACK_CONN_CREATING( msc )
00346               || LDAP_BACK_CONN_BINDING( msc ) )
00347        {
00348               if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
00349                      if ( dolock ) {
00350                             ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00351                      }
00352 
00353                      ldap_pvt_thread_yield();
00354                      goto retry_lock;
00355               }
00356 
00357               /* sounds more appropriate */
00358               rs->sr_err = LDAP_BUSY;
00359               rs->sr_text = "No connections to target are available";
00360               do_return = 1;
00361 
00362        } else if ( META_BACK_CONN_INITED( msc ) ) {
00363               assert( msc->msc_ld != NULL );
00364               rs->sr_err = LDAP_SUCCESS;
00365               do_return = 1;
00366 
00367        } else {
00368               /*
00369                * creating...
00370                */
00371               META_BACK_CONN_CREATING_SET( msc );
00372        }
00373 
00374        if ( dolock ) {
00375               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00376        }
00377 
00378        if ( do_return ) {
00379               if ( rs->sr_err != LDAP_SUCCESS
00380                      && op->o_conn
00381                      && ( sendok & LDAP_BACK_SENDERR ) )
00382               {
00383                      send_ldap_result( op, rs );
00384               }
00385 
00386               return rs->sr_err;
00387        }
00388 
00389        assert( msc->msc_ld == NULL );
00390        
00391        /*
00392         * Attempts to initialize the connection to the target ds
00393         */
00394        ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
00395        rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
00396 #ifdef HAVE_TLS
00397        is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
00398 #endif /* HAVE_TLS */
00399        ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
00400        if ( rs->sr_err != LDAP_SUCCESS ) {
00401               goto error_return;
00402        }
00403 
00404        /*
00405         * Set LDAP version. This will always succeed: If the client
00406         * bound with a particular version, then so can we.
00407         */
00408        if ( mt->mt_version != 0 ) {
00409               version = mt->mt_version;
00410 
00411        } else if ( op->o_conn->c_protocol != 0 ) {
00412               version = op->o_conn->c_protocol;
00413 
00414        } else {
00415               version = LDAP_VERSION3;
00416        }
00417        ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &version );
00418        ldap_set_urllist_proc( msc->msc_ld, mt->mt_urllist_f, mt->mt_urllist_p );
00419 
00420        /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
00421        ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS,
00422               META_BACK_TGT_CHASE_REFERRALS( mt ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
00423 
00424 #ifdef HAVE_TLS
00425        if ( !is_ldaps ) {
00426               slap_bindconf *sb = NULL;
00427 
00428               if ( ispriv ) {
00429                      sb = &mt->mt_idassert.si_bc;
00430               } else {
00431                      sb = &mt->mt_tls;
00432               }
00433 
00434               if ( sb->sb_tls_do_init ) {
00435                      bindconf_tls_set( sb, msc->msc_ld );
00436               } else if ( sb->sb_tls_ctx ) {
00437                      ldap_set_option( msc->msc_ld, LDAP_OPT_X_TLS_CTX, sb->sb_tls_ctx );
00438               }
00439 
00440               if ( sb == &mt->mt_idassert.si_bc && sb->sb_tls_ctx ) {
00441                      do_start_tls = 1;
00442 
00443               } else if ( META_BACK_TGT_USE_TLS( mt )
00444                      || ( op->o_conn->c_is_tls && META_BACK_TGT_PROPAGATE_TLS( mt ) ) )
00445               {
00446                      do_start_tls = 1;
00447               }
00448        }
00449 
00450        /* start TLS ("tls [try-]{start|propagate}" statement) */
00451        if ( do_start_tls ) {
00452 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
00453               /*
00454                * use asynchronous StartTLS; in case, chase referral
00455                * FIXME: OpenLDAP does not return referral on StartTLS yet
00456                */
00457               int           msgid;
00458 
00459               rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
00460               if ( rs->sr_err == LDAP_SUCCESS ) {
00461                      LDAPMessage   *res = NULL;
00462                      int           rc, nretries = mt->mt_nretries;
00463                      struct timeval       tv;
00464 
00465                      LDAP_BACK_TV_SET( &tv );
00466 
00467 retry:;
00468                      rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
00469                      switch ( rc ) {
00470                      case -1:
00471                             rs->sr_err = LDAP_OTHER;
00472                             break;
00473 
00474                      case 0:
00475                             if ( nretries != 0 ) {
00476                                    if ( nretries > 0 ) {
00477                                           nretries--;
00478                                    }
00479                                    LDAP_BACK_TV_SET( &tv );
00480                                    goto retry;
00481                             }
00482                             rs->sr_err = LDAP_OTHER;
00483                             break;
00484 
00485                      default:
00486                             /* only touch when activity actually took place... */
00487                             if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
00488                                    msc->msc_time = op->o_time;
00489                             }
00490                             break;
00491                      }
00492 
00493                      if ( rc == LDAP_RES_EXTENDED ) {
00494                             struct berval *data = NULL;
00495 
00496                             /* NOTE: right now, data is unused, so don't get it */
00497                             rs->sr_err = ldap_parse_extended_result( msc->msc_ld,
00498                                    res, NULL, NULL /* &data */ , 0 );
00499                             if ( rs->sr_err == LDAP_SUCCESS ) {
00500                                    int           err;
00501 
00502                                    /* FIXME: matched? referrals? response controls? */
00503                                    rs->sr_err = ldap_parse_result( msc->msc_ld,
00504                                           res, &err, NULL, NULL, NULL, NULL, 1 );
00505                                    res = NULL;
00506 
00507                                    if ( rs->sr_err == LDAP_SUCCESS ) {
00508                                           rs->sr_err = err;
00509                                    }
00510                                    rs->sr_err = slap_map_api2result( rs );
00511                                    
00512                                    /* FIXME: in case a referral 
00513                                     * is returned, should we try
00514                                     * using it instead of the 
00515                                     * configured URI? */
00516                                    if ( rs->sr_err == LDAP_SUCCESS ) {
00517                                           ldap_install_tls( msc->msc_ld );
00518 
00519                                    } else if ( rs->sr_err == LDAP_REFERRAL ) {
00520                                           /* FIXME: LDAP_OPERATIONS_ERROR? */
00521                                           rs->sr_err = LDAP_OTHER;
00522                                           rs->sr_text = "Unwilling to chase referral "
00523                                                  "returned by Start TLS exop";
00524                                    }
00525 
00526                                    if ( data ) {
00527                                           ber_bvfree( data );
00528                                    }
00529                             }
00530 
00531                      } else {
00532                             rs->sr_err = LDAP_OTHER;
00533                      }
00534 
00535                      if ( res != NULL ) {
00536                             ldap_msgfree( res );
00537                      }
00538               }
00539 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
00540               /*
00541                * use synchronous StartTLS
00542                */
00543               rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
00544 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
00545 
00546               /* if StartTLS is requested, only attempt it if the URL
00547                * is not "ldaps://"; this may occur not only in case
00548                * of misconfiguration, but also when used in the chain 
00549                * overlay, where the "uri" can be parsed out of a referral */
00550               if ( rs->sr_err == LDAP_SERVER_DOWN
00551                      || ( rs->sr_err != LDAP_SUCCESS
00552                             && META_BACK_TGT_TLS_CRITICAL( mt ) ) )
00553               {
00554 
00555 #ifdef DEBUG_205
00556                      Debug( LDAP_DEBUG_ANY,
00557                             "### %s meta_back_init_one_conn(TLS) "
00558                             "ldap_unbind_ext[%d] ld=%p\n",
00559                             op->o_log_prefix, candidate,
00560                             (void *)msc->msc_ld );
00561 #endif /* DEBUG_205 */
00562 
00563                      /* need to trash a failed Start TLS */
00564                      meta_clear_one_candidate( op, mc, candidate );
00565                      goto error_return;
00566               }
00567        }
00568 #endif /* HAVE_TLS */
00569 
00570        /*
00571         * Set the network timeout if set
00572         */
00573        if ( mt->mt_network_timeout != 0 ) {
00574               struct timeval       network_timeout;
00575 
00576               network_timeout.tv_usec = 0;
00577               network_timeout.tv_sec = mt->mt_network_timeout;
00578 
00579               ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
00580                             (void *)&network_timeout );
00581        }
00582 
00583        /*
00584         * If the connection DN is not null, an attempt to rewrite it is made
00585         */
00586 
00587        if ( ispriv ) {
00588               if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
00589                      ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
00590                      if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
00591                             if ( !BER_BVISNULL( &msc->msc_cred ) ) {
00592                                    memset( msc->msc_cred.bv_val, 0,
00593                                           msc->msc_cred.bv_len );
00594                             }
00595                             ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
00596                      }
00597                      LDAP_BACK_CONN_ISIDASSERT_SET( msc );
00598 
00599               } else {
00600                      ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
00601               }
00602 
00603        } else {
00604               if ( !BER_BVISNULL( &msc->msc_cred ) ) {
00605                      memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
00606                      ber_memfree_x( msc->msc_cred.bv_val, NULL );
00607                      BER_BVZERO( &msc->msc_cred );
00608               }
00609               if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
00610                      ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
00611                      BER_BVZERO( &msc->msc_bound_ndn );
00612               }
00613               if ( !BER_BVISEMPTY( &op->o_ndn )
00614                      && SLAP_IS_AUTHZ_BACKEND( op )
00615                      && isauthz )
00616               {
00617                      dc.target = mt;
00618                      dc.conn = op->o_conn;
00619                      dc.rs = rs;
00620                      dc.ctx = "bindDN";
00621               
00622                      /*
00623                       * Rewrite the bind dn if needed
00624                       */
00625                      if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
00626                                           &msc->msc_bound_ndn ) )
00627                      {
00628 
00629 #ifdef DEBUG_205
00630                             Debug( LDAP_DEBUG_ANY,
00631                                    "### %s meta_back_init_one_conn(rewrite) "
00632                                    "ldap_unbind_ext[%d] ld=%p\n",
00633                                    op->o_log_prefix, candidate,
00634                                    (void *)msc->msc_ld );
00635 #endif /* DEBUG_205 */
00636 
00637                             /* need to trash a connection not fully established */
00638                             meta_clear_one_candidate( op, mc, candidate );
00639                             goto error_return;
00640                      }
00641                      
00642                      /* copy the DN if needed */
00643                      if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
00644                             ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
00645                      }
00646 
00647                      assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
00648 
00649               } else {
00650                      ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
00651               }
00652        }
00653 
00654        assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
00655 
00656 error_return:;
00657        if ( dolock ) {
00658               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00659        }
00660        META_BACK_CONN_CREATING_CLEAR( msc );
00661        if ( rs->sr_err == LDAP_SUCCESS ) {
00662               /*
00663                * Sets a cookie for the rewrite session
00664                */
00665               ( void )rewrite_session_init( mt->mt_rwmap.rwm_rw, op->o_conn );
00666               META_BACK_CONN_INITED_SET( msc );
00667        }
00668        if ( dolock ) {
00669               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00670        }
00671 
00672        if ( rs->sr_err != LDAP_SUCCESS ) {
00673               rs->sr_err = slap_map_api2result( rs );
00674               if ( sendok & LDAP_BACK_SENDERR ) {
00675                      send_ldap_result( op, rs );
00676               }
00677        }
00678 
00679        return rs->sr_err;
00680 }
00681 
00682 /*
00683  * meta_back_retry
00684  * 
00685  * Retries one connection
00686  */
00687 int
00688 meta_back_retry(
00689        Operation            *op,
00690        SlapReply            *rs,
00691        metaconn_t           **mcp,
00692        int                  candidate,
00693        ldap_back_send_t     sendok )
00694 {
00695        metainfo_t           *mi = ( metainfo_t * )op->o_bd->be_private;
00696        metatarget_t         *mt = mi->mi_targets[ candidate ];
00697        metaconn_t           *mc = *mcp;
00698        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
00699        int                  rc = LDAP_UNAVAILABLE,
00700                             binding,
00701                             quarantine = 1;
00702 
00703        ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00704 
00705        assert( !META_BACK_CONN_CREATING( msc ) );
00706        binding = LDAP_BACK_CONN_BINDING( msc );
00707        LDAP_BACK_CONN_BINDING_CLEAR( msc );
00708 
00709        assert( mc->mc_refcnt > 0 );
00710        if ( mc->mc_refcnt == 1 ) {
00711               struct berval save_cred;
00712 
00713               if ( LogTest( LDAP_DEBUG_ANY ) ) {
00714                      char   buf[ SLAP_TEXT_BUFLEN ];
00715 
00716                      /* this lock is required; however,
00717                       * it's invoked only when logging is on */
00718                      ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
00719                      snprintf( buf, sizeof( buf ),
00720                             "retrying URI=\"%s\" DN=\"%s\"",
00721                             mt->mt_uri,
00722                             BER_BVISNULL( &msc->msc_bound_ndn ) ?
00723                                    "" : msc->msc_bound_ndn.bv_val );
00724                      ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
00725 
00726                      Debug( LDAP_DEBUG_ANY,
00727                             "%s meta_back_retry[%d]: %s.\n",
00728                             op->o_log_prefix, candidate, buf );
00729               }
00730 
00731               /* save credentials, if any, for later use;
00732                * meta_clear_one_candidate() would free them */
00733               save_cred = msc->msc_cred;
00734               BER_BVZERO( &msc->msc_cred );
00735 
00736               meta_clear_one_candidate( op, mc, candidate );
00737               LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
00738 
00739               ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
00740 
00741               /* mc here must be the regular mc, reset and ready for init */
00742               rc = meta_back_init_one_conn( op, rs, mc, candidate,
00743                      LDAP_BACK_CONN_ISPRIV( mc ), sendok, 0 );
00744 
00745               /* restore credentials, if any and if needed;
00746                * meta_back_init_one_conn() restores msc_bound_ndn, if any;
00747                * if no msc_bound_ndn is restored, destroy credentials */
00748               if ( !BER_BVISNULL( &msc->msc_bound_ndn )
00749                      && BER_BVISNULL( &msc->msc_cred ) )
00750               {
00751                      msc->msc_cred = save_cred;
00752 
00753               } else if ( !BER_BVISNULL( &save_cred ) ) {
00754                      memset( save_cred.bv_val, 0, save_cred.bv_len );
00755                      ber_memfree_x( save_cred.bv_val, NULL );
00756               }
00757 
00758               /* restore the "binding" flag, in case */
00759               if ( binding ) {
00760                      LDAP_BACK_CONN_BINDING_SET( msc );
00761               }
00762 
00763               if ( rc == LDAP_SUCCESS ) {
00764                      quarantine = 0;
00765                      rc = meta_back_single_dobind( op, rs, mcp, candidate,
00766                             sendok, mt->mt_nretries, 0 );
00767 
00768                      Debug( LDAP_DEBUG_ANY,
00769                             "%s meta_back_retry[%d]: "
00770                             "meta_back_single_dobind=%d\n",
00771                             op->o_log_prefix, candidate, rc );
00772                      if ( rc == LDAP_SUCCESS ) {
00773                             if ( !BER_BVISNULL( &msc->msc_bound_ndn ) &&
00774                                    !BER_BVISEMPTY( &msc->msc_bound_ndn ) )
00775                             {
00776                                    LDAP_BACK_CONN_ISBOUND_SET( msc );
00777 
00778                             } else {
00779                                    LDAP_BACK_CONN_ISANON_SET( msc );
00780                             }
00781 
00782                             /* when bound, dispose of the "binding" flag */
00783                             if ( binding ) {
00784                                    LDAP_BACK_CONN_BINDING_CLEAR( msc );
00785                             }
00786                      }
00787               }
00788 
00789               /* don't send twice */
00790               sendok &= ~LDAP_BACK_SENDERR;
00791        }
00792 
00793        if ( rc != LDAP_SUCCESS ) {
00794               SlapReply            *candidates = meta_back_candidates_get( op );
00795 
00796               candidates[ candidate ].sr_err = rc;
00797 
00798               if ( *mcp != NULL ) {
00799                      if ( mc->mc_refcnt == 1 ) {
00800                             if ( binding ) {
00801                                    LDAP_BACK_CONN_BINDING_CLEAR( msc );
00802                             }
00803                             (void)meta_clear_one_candidate( op, mc, candidate );
00804                      }
00805 
00806                      LDAP_BACK_CONN_TAINTED_SET( mc );
00807                      /* only release if mandatory; otherwise
00808                       * let the caller do what's best before
00809                       * releasing */
00810                      if ( META_BACK_ONERR_STOP( mi ) ) {
00811                             meta_back_release_conn_lock( mi, mc, 0 );
00812                             *mcp = NULL;
00813 
00814                      } else {
00815 #if META_BACK_PRINT_CONNTREE > 0
00816                             meta_back_print_conntree( mi, ">>> meta_back_retry" );
00817 #endif /* META_BACK_PRINT_CONNTREE */
00818 
00819                             /* FIXME: could be done better, reworking meta_back_release_conn_lock() */
00820                             if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
00821                                    if ( mc->mc_q.tqe_prev != NULL ) {
00822                                           assert( LDAP_BACK_CONN_CACHED( mc ) );
00823                                           assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
00824                                           LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
00825                                                  mc, mc_q );
00826                                           mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
00827                                           LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
00828 
00829                                    } else {
00830                                           assert( !LDAP_BACK_CONN_CACHED( mc ) );
00831                                    }
00832 
00833                             } else {
00834                                    /* FIXME: check if in tree, for consistency? */
00835                                    (void)avl_delete( &mi->mi_conninfo.lai_tree,
00836                                           ( caddr_t )mc, meta_back_conndnmc_cmp );
00837                             }
00838                             LDAP_BACK_CONN_CACHED_CLEAR( mc );
00839 
00840 #if META_BACK_PRINT_CONNTREE > 0
00841                             meta_back_print_conntree( mi, "<<< meta_back_retry" );
00842 #endif /* META_BACK_PRINT_CONNTREE */
00843                      }
00844               }
00845 
00846               if ( sendok & LDAP_BACK_SENDERR ) {
00847                      rs->sr_err = rc;
00848                      rs->sr_text = "Unable to retry";
00849                      send_ldap_result( op, rs );
00850               }
00851        }
00852 
00853        if ( quarantine && META_BACK_TGT_QUARANTINE( mt ) ) {
00854               meta_back_quarantine( op, rs, candidate );
00855        }
00856 
00857        ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00858 
00859        return rc == LDAP_SUCCESS ? 1 : 0;
00860 }
00861 
00862 /*
00863  * callback for unique candidate selection
00864  */
00865 static int
00866 meta_back_conn_cb( Operation *op, SlapReply *rs )
00867 {
00868        assert( op->o_tag == LDAP_REQ_SEARCH );
00869 
00870        switch ( rs->sr_type ) {
00871        case REP_SEARCH:
00872               ((long *)op->o_callback->sc_private)[0] = (long)op->o_private;
00873               break;
00874 
00875        case REP_SEARCHREF:
00876        case REP_RESULT:
00877               break;
00878 
00879        default:
00880               return rs->sr_err;
00881        }
00882 
00883        return 0;
00884 }
00885 
00886 
00887 static int
00888 meta_back_get_candidate(
00889        Operation     *op,
00890        SlapReply     *rs,
00891        struct berval *ndn )
00892 {
00893        metainfo_t    *mi = ( metainfo_t * )op->o_bd->be_private;
00894        long          candidate;
00895 
00896        /*
00897         * tries to get a unique candidate
00898         * (takes care of default target)
00899         */
00900        candidate = meta_back_select_unique_candidate( mi, ndn );
00901 
00902        /*
00903         * if any is found, inits the connection
00904         */
00905        if ( candidate == META_TARGET_NONE ) {
00906               rs->sr_err = LDAP_NO_SUCH_OBJECT;
00907               rs->sr_text = "No suitable candidate target found";
00908 
00909        } else if ( candidate == META_TARGET_MULTIPLE ) {
00910               Operation     op2 = *op;
00911               SlapReply     rs2 = { REP_RESULT };
00912               slap_callback cb2 = { 0 };
00913               int           rc;
00914 
00915               /* try to get a unique match for the request ndn
00916                * among the multiple candidates available */
00917               op2.o_tag = LDAP_REQ_SEARCH;
00918               op2.o_req_dn = *ndn;
00919               op2.o_req_ndn = *ndn;
00920               op2.ors_scope = LDAP_SCOPE_BASE;
00921               op2.ors_deref = LDAP_DEREF_NEVER;
00922               op2.ors_attrs = slap_anlist_no_attrs;
00923               op2.ors_attrsonly = 0;
00924               op2.ors_limit = NULL;
00925               op2.ors_slimit = 1;
00926               op2.ors_tlimit = SLAP_NO_LIMIT;
00927 
00928               op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
00929               op2.ors_filterstr = *slap_filterstr_objectClass_pres;
00930 
00931               op2.o_callback = &cb2;
00932               cb2.sc_response = meta_back_conn_cb;
00933               cb2.sc_private = (void *)&candidate;
00934 
00935               rc = op->o_bd->be_search( &op2, &rs2 );
00936 
00937               switch ( rs2.sr_err ) {
00938               case LDAP_SUCCESS:
00939               default:
00940                      rs->sr_err = rs2.sr_err;
00941                      break;
00942 
00943               case LDAP_SIZELIMIT_EXCEEDED:
00944                      /* if multiple candidates can serve the operation,
00945                       * and a default target is defined, and it is
00946                       * a candidate, try using it (FIXME: YMMV) */
00947                      if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
00948                             && meta_back_is_candidate( mi->mi_targets[ mi->mi_defaulttarget ],
00949                                           ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
00950                      {
00951                             candidate = mi->mi_defaulttarget;
00952                             rs->sr_err = LDAP_SUCCESS;
00953                             rs->sr_text = NULL;
00954 
00955                      } else {
00956                             rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
00957                             rs->sr_text = "Unable to select unique candidate target";
00958                      }
00959                      break;
00960               }
00961 
00962        } else {
00963               rs->sr_err = LDAP_SUCCESS;
00964        }
00965 
00966        return candidate;
00967 }
00968 
00969 static void   *meta_back_candidates_dummy;
00970 
00971 static void
00972 meta_back_candidates_keyfree(
00973        void          *key,
00974        void          *data )
00975 {
00976        metacandidates_t     *mc = (metacandidates_t *)data;
00977 
00978        ber_memfree_x( mc->mc_candidates, NULL );
00979        ber_memfree_x( data, NULL );
00980 }
00981 
00982 SlapReply *
00983 meta_back_candidates_get( Operation *op )
00984 {
00985        metainfo_t           *mi = ( metainfo_t * )op->o_bd->be_private;
00986        metacandidates_t     *mc;
00987 
00988        if ( op->o_threadctx ) {
00989               void          *data = NULL;
00990 
00991               ldap_pvt_thread_pool_getkey( op->o_threadctx,
00992                             &meta_back_candidates_dummy, &data, NULL );
00993               mc = (metacandidates_t *)data;
00994 
00995        } else {
00996               mc = mi->mi_candidates;
00997        }
00998 
00999        if ( mc == NULL ) {
01000               mc = ch_calloc( sizeof( metacandidates_t ), 1 );
01001               mc->mc_ntargets = mi->mi_ntargets;
01002               mc->mc_candidates = ch_calloc( sizeof( SlapReply ), mc->mc_ntargets );
01003               if ( op->o_threadctx ) {
01004                      void          *data = NULL;
01005 
01006                      data = (void *)mc;
01007                      ldap_pvt_thread_pool_setkey( op->o_threadctx,
01008                                    &meta_back_candidates_dummy, data,
01009                                    meta_back_candidates_keyfree,
01010                                    NULL, NULL );
01011 
01012               } else {
01013                      mi->mi_candidates = mc;
01014               }
01015 
01016        } else if ( mc->mc_ntargets < mi->mi_ntargets ) {
01017               /* NOTE: in the future, may want to allow back-config
01018                * to add/remove targets from back-meta... */
01019               mc->mc_candidates = ch_realloc( mc->mc_candidates,
01020                             sizeof( SlapReply ) * mi->mi_ntargets );
01021               memset( &mc->mc_candidates[ mc->mc_ntargets ], 0,
01022                      sizeof( SlapReply ) * ( mi->mi_ntargets - mc->mc_ntargets ) );
01023               mc->mc_ntargets = mi->mi_ntargets;
01024        }
01025 
01026        return mc->mc_candidates;
01027 }
01028 
01029 /*
01030  * meta_back_getconn
01031  * 
01032  * Prepares the connection structure
01033  * 
01034  * RATIONALE:
01035  *
01036  * - determine what DN is being requested:
01037  *
01038  *     op     requires candidate   checks
01039  *
01040  *     add    unique               parent of o_req_ndn
01041  *     bind   unique^*[/all]              o_req_ndn [no check]
01042  *     compare       unique^+             o_req_ndn
01043  *     delete unique               o_req_ndn
01044  *     modify unique               o_req_ndn
01045  *     search any                  o_req_ndn
01046  *     modrdn unique[, unique]     o_req_ndn[, orr_nnewSup]
01047  *
01048  * - for ops that require the candidate to be unique, in case of multiple
01049  *   occurrences an internal search with sizeLimit=1 is performed
01050  *   if a unique candidate can actually be determined.  If none is found,
01051  *   the operation aborts; if multiple are found, the default target
01052  *   is used if defined and candidate; otherwise the operation aborts.
01053  *
01054  * *^note: actually, the bind operation is handled much like a search;
01055  *   i.e. the bind is broadcast to all candidate targets.
01056  *
01057  * +^note: actually, the compare operation is handled much like a search;
01058  *   i.e. the compare is broadcast to all candidate targets, while checking
01059  *   that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
01060  *   returned.
01061  */
01062 metaconn_t *
01063 meta_back_getconn(
01064               Operation            *op,
01065        SlapReply            *rs,
01066        int                  *candidate,
01067        ldap_back_send_t     sendok )
01068 {
01069        metainfo_t    *mi = ( metainfo_t * )op->o_bd->be_private;
01070        metaconn_t    *mc = NULL,
01071                      mc_curr = {{ 0 }};
01072        int           cached = META_TARGET_NONE,
01073                      i = META_TARGET_NONE,
01074                      err = LDAP_SUCCESS,
01075                      new_conn = 0,
01076                      ncandidates = 0;
01077 
01078 
01079        meta_op_type  op_type = META_OP_REQUIRE_SINGLE;
01080        enum          {
01081               META_DNTYPE_ENTRY,
01082               META_DNTYPE_PARENT,
01083               META_DNTYPE_NEWPARENT
01084        }             dn_type = META_DNTYPE_ENTRY;
01085        struct berval ndn = op->o_req_ndn,
01086                      pndn;
01087 
01088        SlapReply     *candidates = meta_back_candidates_get( op );
01089 
01090        /* Internal searches are privileged and shared. So is root. */
01091        if ( ( !BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ALWAYS( mi ) )
01092               || ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ANON( mi ) )
01093               || op->o_do_not_cache || be_isroot( op ) )
01094        {
01095               LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
01096               mc_curr.mc_local_ndn = op->o_bd->be_rootndn;
01097               LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op );
01098 
01099        } else if ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_NOANON( mi ) )
01100        {
01101               LDAP_BACK_CONN_ISANON_SET( &mc_curr );
01102               BER_BVSTR( &mc_curr.mc_local_ndn, "" );
01103               LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
01104 
01105        } else {
01106               mc_curr.mc_local_ndn = op->o_ndn;
01107 
01108               /* Explicit binds must not be shared */
01109               if ( !BER_BVISEMPTY( &op->o_ndn )
01110                      || op->o_tag == LDAP_REQ_BIND
01111                      || SLAP_IS_AUTHZ_BACKEND( op ) )
01112               {
01113                      mc_curr.mc_conn = op->o_conn;
01114        
01115               } else {
01116                      LDAP_BACK_CONN_ISANON_SET( &mc_curr );
01117                      LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
01118               }
01119        }
01120 
01121        /* Explicit Bind requests always get their own conn */
01122        if ( sendok & LDAP_BACK_BINDING ) {
01123               mc_curr.mc_conn = op->o_conn;
01124 
01125        } else {
01126               /* Searches for a metaconn in the avl tree */
01127 retry_lock:;
01128               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
01129               if ( LDAP_BACK_PCONN_ISPRIV( &mc_curr ) ) {
01130                      /* lookup a conn that's not binding */
01131                      LDAP_TAILQ_FOREACH( mc,
01132                             &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv,
01133                             mc_q )
01134                      {
01135                             if ( !LDAP_BACK_CONN_BINDING( mc ) && mc->mc_refcnt == 0 ) {
01136                                    break;
01137                             }
01138                      }
01139 
01140                      if ( mc != NULL ) {
01141                             /* move to tail of queue */
01142                             if ( mc != LDAP_TAILQ_LAST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
01143                                    metaconn_t, mc_q ) )
01144                             {
01145                                    LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
01146                                           mc, mc_q );
01147                                    LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
01148                                    LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
01149                                           mc, mc_q );
01150                             }
01151 
01152                      } else if ( !LDAP_BACK_USE_TEMPORARIES( mi )
01153                             && mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_num == mi->mi_conn_priv_max )
01154                      {
01155                             mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv );
01156                      }
01157                      
01158 
01159               } else {
01160                      mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, 
01161                             (caddr_t)&mc_curr, meta_back_conndn_cmp );
01162               }
01163 
01164               if ( mc ) {
01165                      /* catch taint errors */
01166                      assert( !LDAP_BACK_CONN_TAINTED( mc ) );
01167 
01168                      /* Don't reuse connections while they're still binding
01169                       * NOTE: only makes sense for binds */
01170                      if ( LDAP_BACK_CONN_BINDING( mc ) ) {
01171                             if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
01172                                    ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
01173 
01174                                    ldap_pvt_thread_yield();
01175                                    goto retry_lock;
01176                             }
01177 
01178                             /* release conn, and create a temporary */
01179                             mc = NULL;
01180 
01181                      } else {
01182                             if ( ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl )
01183                                    || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) )
01184                             {
01185 #if META_BACK_PRINT_CONNTREE > 0
01186                                    meta_back_print_conntree( mi,
01187                                           ">>> meta_back_getconn(expired)" );
01188 #endif /* META_BACK_PRINT_CONNTREE */
01189 
01190                                    /* don't let anyone else use this expired connection */
01191                                    if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
01192                                           if ( mc->mc_q.tqe_prev != NULL ) {
01193                                                  assert( LDAP_BACK_CONN_CACHED( mc ) );
01194                                                  assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
01195                                                  LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
01196                                                         mc, mc_q );
01197                                                  mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
01198                                                  LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
01199 
01200                                           } else {
01201                                                  assert( !LDAP_BACK_CONN_CACHED( mc ) );
01202                                           }
01203 
01204                                    } else {
01205                                           (void)avl_delete( &mi->mi_conninfo.lai_tree,
01206                                                  (caddr_t)mc, meta_back_conndnmc_cmp );
01207                                    }
01208 
01209 #if META_BACK_PRINT_CONNTREE > 0
01210                                    meta_back_print_conntree( mi,
01211                                           "<<< meta_back_getconn(expired)" );
01212 #endif /* META_BACK_PRINT_CONNTREE */
01213                                    LDAP_BACK_CONN_TAINTED_SET( mc );
01214                                    LDAP_BACK_CONN_CACHED_CLEAR( mc );
01215 
01216                                    if ( LogTest( LDAP_DEBUG_TRACE ) ) {
01217                                           char buf[STRLENOF("4294967295U") + 1] = { 0 };
01218                                           mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
01219 
01220                                           Debug( LDAP_DEBUG_TRACE,
01221                                                  "%s meta_back_getconn: mc=%p conn=%s expired (tainted).\n",
01222                                                  op->o_log_prefix, (void *)mc, buf );
01223                                    }
01224                             }
01225 
01226                             mc->mc_refcnt++;
01227                      }
01228               }
01229               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
01230        }
01231 
01232        switch ( op->o_tag ) {
01233        case LDAP_REQ_ADD:
01234               /* if we go to selection, the entry must not exist,
01235                * and we must be able to resolve the parent */
01236               dn_type = META_DNTYPE_PARENT;
01237               dnParent( &ndn, &pndn );
01238               break;
01239 
01240        case LDAP_REQ_MODRDN:
01241               /* if nnewSuperior is not NULL, it must resolve
01242                * to the same candidate as the req_ndn */
01243               if ( op->orr_nnewSup ) {
01244                      dn_type = META_DNTYPE_NEWPARENT;
01245               }
01246               break;
01247 
01248        case LDAP_REQ_BIND:
01249               /* if bound as rootdn, the backend must bind to all targets
01250                * with the administrative identity
01251                * (unless pseoudoroot-bind-defer is TRUE) */
01252               if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
01253                      op_type = META_OP_REQUIRE_ALL;
01254               }
01255               break;
01256 
01257        case LDAP_REQ_COMPARE:
01258        case LDAP_REQ_DELETE:
01259        case LDAP_REQ_MODIFY:
01260               /* just a unique candidate */
01261               break;
01262 
01263        case LDAP_REQ_SEARCH:
01264               /* allow multiple candidates for the searchBase */
01265               op_type = META_OP_ALLOW_MULTIPLE;
01266               break;
01267 
01268        default:
01269               /* right now, just break (exop?) */
01270               break;
01271        }
01272 
01273        /*
01274         * require all connections ...
01275         */
01276        if ( op_type == META_OP_REQUIRE_ALL ) {
01277 
01278               /* Looks like we didn't get a bind. Open a new session... */
01279               if ( mc == NULL ) {
01280                      assert( new_conn == 0 );
01281                      mc = metaconn_alloc( op );
01282                      mc->mc_conn = mc_curr.mc_conn;
01283                      ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
01284                      new_conn = 1;
01285                      if ( sendok & LDAP_BACK_BINDING ) {
01286                             LDAP_BACK_CONN_BINDING_SET( mc );
01287                      }
01288                      if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
01289                             LDAP_BACK_CONN_ISPRIV_SET( mc );
01290 
01291                      } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
01292                             LDAP_BACK_CONN_ISANON_SET( mc );
01293                      }
01294 
01295               } else if ( 0 ) {
01296                      /* TODO: if any of the connections is binding,
01297                       * release mc and create a new one */
01298               }
01299 
01300               for ( i = 0; i < mi->mi_ntargets; i++ ) {
01301                      /*
01302                       * The target is activated; if needed, it is
01303                       * also init'd
01304                       */
01305                      candidates[ i ].sr_err = meta_back_init_one_conn( op,
01306                             rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
01307                             LDAP_BACK_DONTSEND, !new_conn );
01308                      if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
01309                             if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
01310                                    LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
01311                             }
01312                             META_CANDIDATE_SET( &candidates[ i ] );
01313                             ncandidates++;
01314        
01315                      } else {
01316                             
01317                             /*
01318                              * FIXME: in case one target cannot
01319                              * be init'd, should the other ones
01320                              * be tried?
01321                              */
01322                             META_CANDIDATE_RESET( &candidates[ i ] );
01323                             err = candidates[ i ].sr_err;
01324                             continue;
01325                      }
01326               }
01327 
01328               if ( ncandidates == 0 ) {
01329                      if ( new_conn ) {
01330                             mc->mc_refcnt = 0;
01331                             meta_back_conn_free( mc );
01332 
01333                      } else {
01334                             meta_back_release_conn( mi, mc );
01335                      }
01336 
01337                      rs->sr_err = LDAP_NO_SUCH_OBJECT;
01338                      rs->sr_text = "Unable to select valid candidates";
01339 
01340                      if ( sendok & LDAP_BACK_SENDERR ) {
01341                             if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
01342                                    rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
01343                             }
01344                             send_ldap_result( op, rs );
01345                             rs->sr_matched = NULL;
01346                      }
01347 
01348                      return NULL;
01349               }
01350 
01351               goto done;
01352        }
01353        
01354        /*
01355         * looks in cache, if any
01356         */
01357        if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
01358               cached = i = meta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
01359        }
01360 
01361        if ( op_type == META_OP_REQUIRE_SINGLE ) {
01362               metatarget_t         *mt = NULL;
01363               metasingleconn_t     *msc = NULL;
01364 
01365               int                  j;
01366 
01367               for ( j = 0; j < mi->mi_ntargets; j++ ) {
01368                      META_CANDIDATE_RESET( &candidates[ j ] );
01369               }
01370 
01371               /*
01372                * tries to get a unique candidate
01373                * (takes care of default target)
01374                */
01375               if ( i == META_TARGET_NONE ) {
01376                      i = meta_back_get_candidate( op, rs, &ndn );
01377 
01378                      if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && dn_type == META_DNTYPE_PARENT ) {
01379                             i = meta_back_get_candidate( op, rs, &pndn );
01380                      }
01381        
01382                      if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
01383                             if ( mc != NULL ) {
01384                                    meta_back_release_conn( mi, mc );
01385                             }
01386 
01387                             if ( sendok & LDAP_BACK_SENDERR ) {
01388                                    if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
01389                                           rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
01390                                    }
01391                                    send_ldap_result( op, rs );
01392                                    rs->sr_matched = NULL;
01393                             }
01394                      
01395                             return NULL;
01396                      }
01397               }
01398 
01399               if ( dn_type == META_DNTYPE_NEWPARENT && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
01400               {
01401                      if ( mc != NULL ) {
01402                             meta_back_release_conn( mi, mc );
01403                      }
01404 
01405                      rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
01406                      rs->sr_text = "Cross-target rename not supported";
01407                      if ( sendok & LDAP_BACK_SENDERR ) {
01408                             send_ldap_result( op, rs );
01409                      }
01410 
01411                      return NULL;
01412               }
01413 
01414               Debug( LDAP_DEBUG_TRACE,
01415        "==>meta_back_getconn: got target=%d for ndn=\"%s\" from cache\n",
01416                             i, op->o_req_ndn.bv_val, 0 );
01417 
01418               if ( mc == NULL ) {
01419                      /* Retries searching for a metaconn in the avl tree
01420                       * the reason is that the connection might have been
01421                       * created by meta_back_get_candidate() */
01422                      if ( !( sendok & LDAP_BACK_BINDING ) ) {
01423 retry_lock2:;
01424                             ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
01425                             mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, 
01426                                    (caddr_t)&mc_curr, meta_back_conndn_cmp );
01427                             if ( mc != NULL ) {
01428                                    /* catch taint errors */
01429                                    assert( !LDAP_BACK_CONN_TAINTED( mc ) );
01430 
01431                                    /* Don't reuse connections while they're still binding */
01432                                    if ( META_BACK_CONN_CREATING( &mc->mc_conns[ i ] )
01433                                           || LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) )
01434                                    {
01435                                           if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
01436                                                  ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
01437                                                  ldap_pvt_thread_yield();
01438                                                  goto retry_lock2;
01439                                           }
01440 
01441                                           mc = NULL;
01442 
01443                                    } else {
01444                                           mc->mc_refcnt++;
01445                                    }
01446                             }
01447                             ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
01448                      }
01449 
01450                      /* Looks like we didn't get a bind. Open a new session... */
01451                      if ( mc == NULL ) {
01452                             assert( new_conn == 0 );
01453                             mc = metaconn_alloc( op );
01454                             mc->mc_conn = mc_curr.mc_conn;
01455                             ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
01456                             new_conn = 1;
01457                             if ( sendok & LDAP_BACK_BINDING ) {
01458                                    LDAP_BACK_CONN_BINDING_SET( mc );
01459                             }
01460                             if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
01461                                    LDAP_BACK_CONN_ISPRIV_SET( mc );
01462 
01463                             } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
01464                                    LDAP_BACK_CONN_ISANON_SET( mc );
01465                             }
01466                      }
01467               }
01468 
01469               /*
01470                * Clear all other candidates
01471                */
01472               ( void )meta_clear_unused_candidates( op, i );
01473 
01474               mt = mi->mi_targets[ i ];
01475               msc = &mc->mc_conns[ i ];
01476 
01477               /*
01478                * The target is activated; if needed, it is
01479                * also init'd. In case of error, meta_back_init_one_conn
01480                * sends the appropriate result.
01481                */
01482               err = meta_back_init_one_conn( op, rs, mc, i,
01483                      LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn );
01484               if ( err != LDAP_SUCCESS ) {
01485                      /*
01486                       * FIXME: in case one target cannot
01487                       * be init'd, should the other ones
01488                       * be tried?
01489                       */
01490                      META_CANDIDATE_RESET( &candidates[ i ] );
01491                      if ( new_conn ) {
01492                             mc->mc_refcnt = 0;
01493                             meta_back_conn_free( mc );
01494 
01495                      } else {
01496                             meta_back_release_conn( mi, mc );
01497                      }
01498                      return NULL;
01499               }
01500 
01501               if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
01502                      LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
01503               }
01504 
01505               candidates[ i ].sr_err = LDAP_SUCCESS;
01506               META_CANDIDATE_SET( &candidates[ i ] );
01507               ncandidates++;
01508 
01509               if ( candidate ) {
01510                      *candidate = i;
01511               }
01512 
01513        /*
01514         * if no unique candidate ...
01515         */
01516        } else {
01517 
01518               /* Looks like we didn't get a bind. Open a new session... */
01519               if ( mc == NULL ) {
01520                      assert( new_conn == 0 );
01521                      mc = metaconn_alloc( op );
01522                      mc->mc_conn = mc_curr.mc_conn;
01523                      ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
01524                      new_conn = 1;
01525                      if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
01526                             LDAP_BACK_CONN_ISPRIV_SET( mc );
01527 
01528                      } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
01529                             LDAP_BACK_CONN_ISANON_SET( mc );
01530                      }
01531               }
01532 
01533               for ( i = 0; i < mi->mi_ntargets; i++ ) {
01534                      metatarget_t         *mt = mi->mi_targets[ i ];
01535 
01536                      META_CANDIDATE_RESET( &candidates[ i ] );
01537 
01538                      if ( i == cached 
01539                             || meta_back_is_candidate( mt, &op->o_req_ndn,
01540                                    op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_SUBTREE ) )
01541                      {
01542 
01543                             /*
01544                              * The target is activated; if needed, it is
01545                              * also init'd
01546                              */
01547                             int lerr = meta_back_init_one_conn( op, rs, mc, i,
01548                                    LDAP_BACK_CONN_ISPRIV( &mc_curr ),
01549                                    LDAP_BACK_DONTSEND, !new_conn );
01550                             candidates[ i ].sr_err = lerr;
01551                             if ( lerr == LDAP_SUCCESS ) {
01552                                    META_CANDIDATE_SET( &candidates[ i ] );
01553                                    ncandidates++;
01554 
01555                                    Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d]\n",
01556                                           op->o_log_prefix, i, 0 );
01557 
01558                             } else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
01559                                    META_CANDIDATE_SET( &candidates[ i ] );
01560 
01561                                    Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] %s\n",
01562                                           op->o_log_prefix, i,
01563                                           mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
01564 
01565                             } else {
01566                             
01567                                    /*
01568                                     * FIXME: in case one target cannot
01569                                     * be init'd, should the other ones
01570                                     * be tried?
01571                                     */
01572                                    if ( new_conn ) {
01573                                           ( void )meta_clear_one_candidate( op, mc, i );
01574                                    }
01575                                    /* leave the target candidate, but record the error for later use */
01576                                    err = lerr;
01577 
01578                                    if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
01579                                           Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] quarantined err=%d\n",
01580                                                  op->o_log_prefix, i, lerr );
01581 
01582                                    } else {
01583                                           Debug( LDAP_DEBUG_ANY, "%s: meta_back_getconn[%d] failed err=%d\n",
01584                                                  op->o_log_prefix, i, lerr );
01585                                    }
01586 
01587                                    if ( META_BACK_ONERR_STOP( mi ) ) {
01588                                           if ( sendok & LDAP_BACK_SENDERR ) {
01589                                                  send_ldap_result( op, rs );
01590                                           }
01591                                           if ( new_conn ) {
01592                                                  mc->mc_refcnt = 0;
01593                                                  meta_back_conn_free( mc );
01594 
01595                                           } else {
01596                                                  meta_back_release_conn( mi, mc );
01597                                           }
01598 
01599                                           return NULL;
01600                                    }
01601 
01602                                    continue;
01603                             }
01604 
01605                      } else {
01606                             if ( new_conn ) {
01607                                    ( void )meta_clear_one_candidate( op, mc, i );
01608                             }
01609                      }
01610               }
01611 
01612               if ( ncandidates == 0 ) {
01613                      if ( new_conn ) {
01614                             mc->mc_refcnt = 0;
01615                             meta_back_conn_free( mc );
01616 
01617                      } else {
01618                             meta_back_release_conn( mi, mc );
01619                      }
01620 
01621                      if ( rs->sr_err == LDAP_SUCCESS ) {
01622                             rs->sr_err = LDAP_NO_SUCH_OBJECT;
01623                             rs->sr_text = "Unable to select valid candidates";
01624                      }
01625 
01626                      if ( sendok & LDAP_BACK_SENDERR ) {
01627                             if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
01628                                    rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
01629                             }
01630                             send_ldap_result( op, rs );
01631                             rs->sr_matched = NULL;
01632                      }
01633 
01634                      return NULL;
01635               }
01636        }
01637 
01638 done:;
01639        /* clear out meta_back_init_one_conn non-fatal errors */
01640        rs->sr_err = LDAP_SUCCESS;
01641        rs->sr_text = NULL;
01642 
01643        /* touch the timestamp */
01644        if ( mi->mi_idle_timeout != 0 ) {
01645               mc->mc_time = op->o_time;
01646        }
01647 
01648        if ( new_conn ) {
01649               if ( mi->mi_conn_ttl ) {
01650                      mc->mc_create_time = op->o_time;
01651               }
01652 
01653               /*
01654                * Inserts the newly created metaconn in the avl tree
01655                */
01656               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
01657 #if META_BACK_PRINT_CONNTREE > 0
01658               meta_back_print_conntree( mi, ">>> meta_back_getconn" );
01659 #endif /* META_BACK_PRINT_CONNTREE */
01660 
01661               err = 0;
01662               if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
01663                      if ( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num < mi->mi_conn_priv_max ) {
01664                             LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
01665                             mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num++;
01666                             LDAP_BACK_CONN_CACHED_SET( mc );
01667 
01668                      } else {
01669                             LDAP_BACK_CONN_TAINTED_SET( mc );
01670                      }
01671                      rs->sr_err = 0;
01672 
01673               } else if ( !( sendok & LDAP_BACK_BINDING ) ) {
01674                      err = avl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc,
01675                                    meta_back_conndn_cmp, meta_back_conndn_dup );
01676                      LDAP_BACK_CONN_CACHED_SET( mc );
01677               }
01678 
01679 #if META_BACK_PRINT_CONNTREE > 0
01680               meta_back_print_conntree( mi, "<<< meta_back_getconn" );
01681 #endif /* META_BACK_PRINT_CONNTREE */
01682               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
01683 
01684               if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) {
01685                      /*
01686                       * Err could be -1 in case a duplicate metaconn is inserted
01687                       */
01688                      switch ( err ) {
01689                      case 0:
01690                             break;
01691 
01692                      case -1:
01693                             LDAP_BACK_CONN_CACHED_CLEAR( mc );
01694                             /* duplicate: free and try to get the newly created one */
01695                             if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
01696                                    mc->mc_refcnt = 0;   
01697                                    meta_back_conn_free( mc );
01698        
01699                                    new_conn = 0;
01700                                    goto retry_lock;
01701                             }
01702 
01703                             LDAP_BACK_CONN_TAINTED_SET( mc );
01704                             break;
01705 
01706                      default:
01707                             LDAP_BACK_CONN_CACHED_CLEAR( mc );
01708                             if ( LogTest( LDAP_DEBUG_ANY ) ) {
01709                                    char buf[STRLENOF("4294967295U") + 1] = { 0 };
01710                                    mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
01711 
01712                                    Debug( LDAP_DEBUG_ANY,
01713                                           "%s meta_back_getconn: candidates=%d conn=%s insert failed\n",
01714                                           op->o_log_prefix, ncandidates, buf );
01715                             }
01716        
01717                             mc->mc_refcnt = 0;   
01718                             meta_back_conn_free( mc );
01719 
01720                             rs->sr_err = LDAP_OTHER;
01721                             rs->sr_text = "Proxy bind collision";
01722                             if ( sendok & LDAP_BACK_SENDERR ) {
01723                                    send_ldap_result( op, rs );
01724                             }
01725                             return NULL;
01726                      }
01727               }
01728 
01729               if ( LogTest( LDAP_DEBUG_TRACE ) ) {
01730                      char buf[STRLENOF("4294967295U") + 1] = { 0 };
01731                      mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
01732 
01733                      Debug( LDAP_DEBUG_TRACE,
01734                             "%s meta_back_getconn: candidates=%d conn=%s inserted\n",
01735                             op->o_log_prefix, ncandidates, buf );
01736               }
01737 
01738        } else {
01739               if ( LogTest( LDAP_DEBUG_TRACE ) ) {
01740                      char buf[STRLENOF("4294967295U") + 1] = { 0 };
01741                      mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
01742 
01743                      Debug( LDAP_DEBUG_TRACE,
01744                             "%s meta_back_getconn: candidates=%d conn=%s fetched\n",
01745                             op->o_log_prefix, ncandidates, buf );
01746               }
01747        }
01748 
01749        return mc;
01750 }
01751 
01752 void
01753 meta_back_release_conn_lock(
01754               metainfo_t           *mi,
01755        metaconn_t           *mc,
01756        int                  dolock )
01757 {
01758        assert( mc != NULL );
01759 
01760        if ( dolock ) {
01761               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
01762        }
01763        assert( mc->mc_refcnt > 0 );
01764        mc->mc_refcnt--;
01765        /* NOTE: the connection is removed if either it is tainted
01766         * or if it is shared and no one else is using it.  This needs
01767         * to occur because for intrinsic reasons cached connections
01768         * that are not privileged would live forever and pollute
01769         * the connection space (and eat up resources).  Maybe this
01770         * should be configurable... */
01771        if ( LDAP_BACK_CONN_TAINTED( mc ) || !LDAP_BACK_CONN_CACHED( mc ) ) {
01772 #if META_BACK_PRINT_CONNTREE > 0
01773               meta_back_print_conntree( mi, ">>> meta_back_release_conn" );
01774 #endif /* META_BACK_PRINT_CONNTREE */
01775 
01776               if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
01777                      if ( mc->mc_q.tqe_prev != NULL ) {
01778                             assert( LDAP_BACK_CONN_CACHED( mc ) );
01779                             assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
01780                             LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
01781                             mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
01782                             LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
01783 
01784                      } else {
01785                             assert( !LDAP_BACK_CONN_CACHED( mc ) );
01786                      }
01787 
01788               } else if ( LDAP_BACK_CONN_CACHED( mc ) ) {
01789                      metaconn_t    *tmpmc;
01790 
01791                      tmpmc = avl_delete( &mi->mi_conninfo.lai_tree,
01792                             ( caddr_t )mc, meta_back_conndnmc_cmp );
01793 
01794                      /* Overparanoid, but useful... */
01795                      assert( tmpmc == NULL || tmpmc == mc );
01796               }
01797 
01798               LDAP_BACK_CONN_CACHED_CLEAR( mc );
01799 
01800 #if META_BACK_PRINT_CONNTREE > 0
01801               meta_back_print_conntree( mi, "<<< meta_back_release_conn" );
01802 #endif /* META_BACK_PRINT_CONNTREE */
01803 
01804               if ( mc->mc_refcnt == 0 ) {
01805                      meta_back_conn_free( mc );
01806                      mc = NULL;
01807               }
01808        }
01809 
01810        if ( mc != NULL && LDAP_BACK_CONN_BINDING( mc ) ) {
01811               LDAP_BACK_CONN_BINDING_CLEAR( mc );
01812        }
01813 
01814        if ( dolock ) {
01815               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
01816        }
01817 }
01818 
01819 void
01820 meta_back_quarantine(
01821        Operation     *op,
01822        SlapReply     *rs,
01823        int           candidate )
01824 {
01825        metainfo_t           *mi = (metainfo_t *)op->o_bd->be_private;
01826        metatarget_t         *mt = mi->mi_targets[ candidate ];
01827 
01828        slap_retry_info_t    *ri = &mt->mt_quarantine;
01829 
01830        ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
01831 
01832        if ( rs->sr_err == LDAP_UNAVAILABLE ) {
01833               time_t new_last = slap_get_time();
01834 
01835               switch ( mt->mt_isquarantined ) {
01836               case LDAP_BACK_FQ_NO:
01837                      if ( ri->ri_last == new_last ) {
01838                             goto done;
01839                      }
01840 
01841                      Debug( LDAP_DEBUG_ANY,
01842                             "%s meta_back_quarantine[%d]: enter.\n",
01843                             op->o_log_prefix, candidate, 0 );
01844 
01845                      ri->ri_idx = 0;
01846                      ri->ri_count = 0;
01847                      break;
01848 
01849               case LDAP_BACK_FQ_RETRYING:
01850                      if ( LogTest( LDAP_DEBUG_ANY ) ) {
01851                             char   buf[ SLAP_TEXT_BUFLEN ];
01852 
01853                             snprintf( buf, sizeof( buf ),
01854                                    "meta_back_quarantine[%d]: block #%d try #%d failed",
01855                                    candidate, ri->ri_idx, ri->ri_count );
01856                             Debug( LDAP_DEBUG_ANY, "%s %s.\n",
01857                                    op->o_log_prefix, buf, 0 );
01858                      }
01859 
01860                      ++ri->ri_count;
01861                      if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
01862                             && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
01863                      {
01864                             ri->ri_count = 0;
01865                             ++ri->ri_idx;
01866                      }
01867                      break;
01868 
01869               default:
01870                      break;
01871               }
01872 
01873               mt->mt_isquarantined = LDAP_BACK_FQ_YES;
01874               ri->ri_last = new_last;
01875 
01876        } else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
01877               Debug( LDAP_DEBUG_ANY,
01878                      "%s meta_back_quarantine[%d]: exit.\n",
01879                      op->o_log_prefix, candidate, 0 );
01880 
01881               if ( mi->mi_quarantine_f ) {
01882                      (void)mi->mi_quarantine_f( mi, candidate,
01883                             mi->mi_quarantine_p );
01884               }
01885 
01886               ri->ri_count = 0;
01887               ri->ri_idx = 0;
01888               mt->mt_isquarantined = LDAP_BACK_FQ_NO;
01889        }
01890 
01891 done:;
01892        ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
01893 }