]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#8734 Fixes for many back-asyncmeta issues
authorNadezhda Ivanova <nivanova@symas.com>
Mon, 18 Feb 2019 15:08:52 +0000 (17:08 +0200)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 28 Feb 2019 16:22:11 +0000 (16:22 +0000)
Includes all the changes necessary to fix back-asyncmeta issues
discovered during on-site testing since the start of 2016.
These include:
Issues with stability - crashes and assetion failures
Incorrect behavior during unstable network conditions, such as inability to reset connections
or process responses, or "hanging" to wait for a response that would never be received.
Memory leaks and memory management fixes - major redesign of the way back-asyncmeta
works with memory contexts.
Rewrite was replaced with suffix-massage in configuration, and the network-timeout value was changed to milliseconds.
Incorrect behavior when SASL is used to bind to a target.
Many problems caused by race conditions
Fixes for compiler warnings, and tests.
Cleanup of unused code.

26 files changed:
doc/man/man5/slapd-asyncmeta.5
servers/slapd/back-asyncmeta/Makefile.in
servers/slapd/back-asyncmeta/abandon.c [deleted file]
servers/slapd/back-asyncmeta/add.c
servers/slapd/back-asyncmeta/back-asyncmeta.h
servers/slapd/back-asyncmeta/bind.c
servers/slapd/back-asyncmeta/candidates.c
servers/slapd/back-asyncmeta/compare.c
servers/slapd/back-asyncmeta/config.c
servers/slapd/back-asyncmeta/conn.c
servers/slapd/back-asyncmeta/delete.c
servers/slapd/back-asyncmeta/init.c
servers/slapd/back-asyncmeta/map.c
servers/slapd/back-asyncmeta/message_queue.c
servers/slapd/back-asyncmeta/meta_result.c
servers/slapd/back-asyncmeta/modify.c
servers/slapd/back-asyncmeta/modrdn.c
servers/slapd/back-asyncmeta/proto-asyncmeta.h
servers/slapd/back-asyncmeta/search.c
servers/slapd/back-asyncmeta/suffixmassage.c [deleted file]
servers/slapd/back-asyncmeta/unbind.c [deleted file]
tests/data/slapd-asyncmeta.conf [new file with mode: 0644]
tests/run.in
tests/scripts/defines.sh
tests/scripts/test073-asyncmeta [new file with mode: 0755]
tests/scripts/test074-asyncmeta-concurrency [new file with mode: 0755]

index 2b88c5b99f3699b096475f5720ee61dc975d9a1f..6ab6f4c19b039315634c80283b5fba3510529418 100644 (file)
@@ -376,16 +376,18 @@ Sets the network timeout value after which
 .BR poll (2)/ select (2)
 following a
 .BR connect (2)
-returns in case of no activity.
-The value is in seconds, and it can be specified as for
+returns in case of no activity while sending an operation to the remote target.
+The value is in milliseconds, and it can be specified as for
 .BR idle\-timeout .
 If set before any target specification, it affects all targets, unless
 overridden by any per-target directive.
 
 .TP
 .B nretries {forever|never|<nretries>}
-This directive defines how many times a bind should be retried
-in case of temporary failure in contacting a target.  If defined
+This directive defines how many times forwarding an operation should be retried
+in case of temporary failure in contacting a target. The number of retries
+is per operation, so if a bind to the target is necessary first, the remaining
+number is decremented. If defined
 before any target specification, it applies to all targets (by default,
 .BR 3
 times);
@@ -409,11 +411,13 @@ by a target. See
 for details.
 
 .TP
-.B suffixmassage "<virtual naming context>" "<real naming context>"
-All the directives starting with "rewrite" refer to the rewrite engine
-that has been added to slapd. See
-.B slapd\-meta(5)
-for details.
+.B suffixmassage "<local suffix>" "<remote suffix>"
+.B slapd\-asyncmeta
+does not support the rewrite engine used by
+the LDAP and META backends.
+.B suffixmassage
+can be used to perform DN suffix rewriting, the same way as the obsoleted suffixmassage directive
+previously used by the LDAP backend.
 
 .TP
 .B t\-f\-support {NO|yes|discover}
index 26a19384bfcb7125159ceffb16333298f0230c24..5d64e6f493d053ad32090d67107cecd0fa5357fa 100644 (file)
 ## based on back-meta module for inclusion in OpenLDAP Software.
 ## This work was sponsored by Ericsson
 
-SRCS   = init.c config.c search.c message_queue.c bind.c unbind.c add.c compare.c \
-               delete.c modify.c modrdn.c suffixmassage.c map.c \
-               conn.c candidates.c dncache.c meta_result.c abandon.c
-OBJS   = init.lo config.lo search.lo message_queue.lo bind.lo unbind.lo add.lo compare.lo \
-               delete.lo modify.lo modrdn.lo suffixmassage.lo map.lo \
-               conn.lo candidates.lo dncache.lo meta_result.lo abandon.lo
+SRCS   = init.c config.c search.c message_queue.c bind.c add.c compare.c \
+               delete.c modify.c modrdn.c map.c \
+               conn.c candidates.c dncache.c meta_result.c
+OBJS   = init.lo config.lo search.lo message_queue.lo bind.lo add.lo compare.lo \
+               delete.lo modify.lo modrdn.lo map.lo \
+               conn.lo candidates.lo dncache.lo meta_result.lo
 
 LDAP_INCDIR= ../../../include
 LDAP_LIBDIR= ../../../libraries
diff --git a/servers/slapd/back-asyncmeta/abandon.c b/servers/slapd/back-asyncmeta/abandon.c
deleted file mode 100644 (file)
index 62001db..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/* abandon.c - abandon request handler for back-asyncmeta */
-/* $OpenLDAP$ */
-/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 2016-2019 The OpenLDAP Foundation.
- * Portions Copyright 2016 Symas Corporation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted only as authorized by the OpenLDAP
- * Public License.
- *
- * A copy of this license is available in the file LICENSE in the
- * top-level directory of the distribution or, alternatively, at
- * <http://www.OpenLDAP.org/license.html>.
- */
-
-/* ACKNOWLEDGEMENTS:
- * This work was developed by Symas Corporation
- * based on back-meta module for inclusion in OpenLDAP Software.
- * This work was sponsored by Ericsson. */
-
-#include "portable.h"
-
-#include <stdio.h>
-
-#include <ac/string.h>
-#include <ac/socket.h>
-
-#include "slap.h"
-#include "../back-ldap/back-ldap.h"
-#include "back-asyncmeta.h"
-#include "ldap_rq.h"
-
-/* function is unused */
-int
-asyncmeta_back_abandon( Operation *op, SlapReply *rs )
-{
-       Operation *t_op;
-
-       /* Find the ops being abandoned */
-       ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
-
-       LDAP_STAILQ_FOREACH( t_op, &op->o_conn->c_ops, o_next ) {
-               if ( t_op->o_msgid == op->orn_msgid ) {
-                       t_op->o_abandon = 1;
-               }
-       }
-       ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
-
-       return LDAP_SUCCESS;
-}
index 600138a0f5c621cb6917398261c14a94784769e2..12290a51bd07078629b9ae2e58cff17f15ed63b9 100644 (file)
 
 #include <ac/string.h>
 #include <ac/socket.h>
-
 #include "slap.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "../../../libraries/libldap/ldap-int.h"
 #include "../back-ldap/back-ldap.h"
 #include "back-asyncmeta.h"
 #include "ldap_rq.h"
-#include "../../../libraries/liblber/lber-int.h"
-#include "../../../libraries/libldap/ldap-int.h"
 
-void
-asyncmeta_sender_error(Operation *op,
-                      SlapReply *rs,
-                      slap_callback *cb)
+
+int
+asyncmeta_error_cleanup(Operation *op,
+                       SlapReply *rs,
+                       bm_context_t *bc,
+                       a_metaconn_t *mc,
+                       int candidate)
 {
-       if (cb != NULL) {
-               op->o_callback = cb;
+       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+       mc->mc_conns[candidate].msc_active--;
+       if (asyncmeta_bc_in_queue(mc,bc) == NULL || bc->bc_active > 1) {
+               bc->bc_active--;
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+               return LDAP_SUCCESS;
        }
+       asyncmeta_drop_bc(mc, bc);
+       slap_sl_mem_setctx(op->o_threadctx, op->o_tmpmemctx);
+       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
        send_ldap_result(op, rs);
+       return LDAP_SUCCESS;
 }
 
 meta_search_candidate_t
@@ -51,195 +61,174 @@ asyncmeta_back_add_start(Operation *op,
                         SlapReply *rs,
                         a_metaconn_t *mc,
                         bm_context_t *bc,
-                        int candidate)
+                        int candidate,
+                        int do_lock)
 {
        int             isupdate;
        Attribute       *a;
        int i;
        LDAPMod         **attrs;
-       struct berval   mapped;
        a_dncookie      dc;
        a_metainfo_t    *mi = mc->mc_info;
        a_metatarget_t  *mt = mi->mi_targets[ candidate ];
-       struct berval   mdn;
+       struct berval   mdn = {0, NULL};
        meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
        BerElement *ber = NULL;
        a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
        SlapReply               *candidates = bc->candidates;
        ber_int_t       msgid;
        LDAPControl             **ctrls = NULL;
-       int rc, nretries = 1;
-
+       int rc;
 
+       dc.op = op;
        dc.target = mt;
-       dc.conn = op->o_conn;
-       dc.rs = rs;
-       dc.ctx = "addDN";
-
-       mdn.bv_len = 0;
-
-       switch (asyncmeta_dn_massage( &dc, &bc->op->o_req_dn, &mdn ) )
-       {
-       case LDAP_SUCCESS:
-               break;
-       case LDAP_UNWILLING_TO_PERFORM:
-               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-               rs->sr_text = "Operation not allowed";
-               retcode = META_SEARCH_ERR;
-               goto doreturn;
-       default:
-               rs->sr_err = LDAP_NO_SUCH_OBJECT;
-               retcode = META_SEARCH_NOT_CANDIDATE;
-               goto doreturn;
-       }
+       dc.memctx = op->o_tmpmemctx;
+       dc.to_from = MASSAGE_REQ;
+       asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
 
        /* Count number of attributes in entry ( +1 ) */
        for ( i = 1, a = op->ora_e->e_attrs; a; i++, a = a->a_next );
 
        /* Create array of LDAPMods for ldap_add() */
-       attrs = ch_malloc( sizeof( LDAPMod * )*i );
+       attrs = op->o_tmpalloc(sizeof( LDAPMod * )*i, op->o_tmpmemctx);
 
-       dc.ctx = "addAttrDN";
        isupdate = be_shadow_update( op );
        for ( i = 0, a = op->ora_e->e_attrs; a; a = a->a_next ) {
-               int j, is_oc = 0;
+               int j;
 
                if ( !isupdate && !get_relax( op ) && a->a_desc->ad_type->sat_no_user_mod  )
                {
                        continue;
                }
 
-               if ( a->a_desc == slap_schema.si_ad_objectClass
-                               || a->a_desc == slap_schema.si_ad_structuralObjectClass )
-               {
-                       is_oc = 1;
-                       mapped = a->a_desc->ad_cname;
-
-               } else {
-                       asyncmeta_map( &mt->mt_rwmap.rwm_at,
-                                       &a->a_desc->ad_cname, &mapped, BACKLDAP_MAP );
-                       if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
-                               continue;
-                       }
-               }
-
-               attrs[ i ] = ch_malloc( sizeof( LDAPMod ) );
+               attrs[ i ] = op->o_tmpalloc( sizeof( LDAPMod ), op->o_tmpmemctx );
                if ( attrs[ i ] == NULL ) {
                        continue;
                }
                attrs[ i ]->mod_op = LDAP_MOD_BVALUES;
-               attrs[ i ]->mod_type = mapped.bv_val;
-
-               if ( is_oc ) {
-                       for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ );
-
-                       attrs[ i ]->mod_bvalues =
-                               (struct berval **)ch_malloc( ( j + 1 ) *
-                               sizeof( struct berval * ) );
-                       for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); ) {
-                               struct ldapmapping      *mapping;
-
-                               asyncmeta_mapping( &mt->mt_rwmap.rwm_oc,
-                                               &a->a_vals[ j ], &mapping, BACKLDAP_MAP );
-
-                               if ( mapping == NULL ) {
-                                       if ( mt->mt_rwmap.rwm_oc.drop_missing ) {
-                                               continue;
-                                       }
-                                       attrs[ i ]->mod_bvalues[ j ] = &a->a_vals[ j ];
-
-                               } else {
-                                       attrs[ i ]->mod_bvalues[ j ] = &mapping->dst;
-                               }
-                               j++;
-                       }
-                       attrs[ i ]->mod_bvalues[ j ] = NULL;
-
-               } else {
-                       /*
-                        * FIXME: dn-valued attrs should be rewritten
-                        * to allow their use in ACLs at the back-ldap
-                        * level.
-                        */
-                       if ( a->a_desc->ad_type->sat_syntax ==
-                               slap_schema.si_syn_distinguishedName )
-                       {
-                               (void)asyncmeta_dnattr_rewrite( &dc, a->a_vals );
-                               if ( a->a_vals == NULL ) {
-                                       continue;
-                               }
-                       }
-
-                       for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ )
-                               ;
-
-                       attrs[ i ]->mod_bvalues = ch_malloc( ( j + 1 ) * sizeof( struct berval * ) );
-                       for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ ) {
-                               attrs[ i ]->mod_bvalues[ j ] = &a->a_vals[ j ];
-                       }
-                       attrs[ i ]->mod_bvalues[ j ] = NULL;
+               attrs[ i ]->mod_type = a->a_desc->ad_cname.bv_val;
+               j = a->a_numvals;
+               attrs[ i ]->mod_bvalues = op->o_tmpalloc( ( j + 1 ) * sizeof( struct berval * ), op->o_tmpmemctx );
+               for (j=0; j<a->a_numvals; j++) {
+                       attrs[ i ]->mod_bvalues[ j ] = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
+                       if ( a->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+                               asyncmeta_dn_massage( &dc, &a->a_vals[ j ], attrs[ i ]->mod_bvalues[ j ] );
+                       else
+                               *attrs[ i ]->mod_bvalues[ j ] = a->a_vals[ j ];
                }
+
+               attrs[ i ]->mod_bvalues[ j ] = NULL;
                i++;
        }
        attrs[ i ] = NULL;
 
-retry:;
+       asyncmeta_set_msc_time(msc);
+
        ctrls = op->o_ctrls;
-       if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
+       if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS )
        {
                candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
                retcode = META_SEARCH_ERR;
                goto done;
        }
+       /* someone might have reset the connection */
+       if (!( LDAP_BACK_CONN_ISBOUND( msc )
+              || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+               Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+               goto error_unavailable;
+       }
 
        ber = ldap_build_add_req( msc->msc_ld, mdn.bv_val, attrs, ctrls, NULL, &msgid);
+       if (!ber) {
+               Debug( asyncmeta_debug, "%s asyncmeta_back_add_start: Operation encoding failed with errno %d\n",
+                      op->o_log_prefix, msc->msc_ld->ld_errno );
+               rs->sr_err = LDAP_OPERATIONS_ERROR;
+               rs->sr_text = "Failed to encode proxied request";
+               retcode = META_SEARCH_ERR;
+               goto done;
+       }
+
        if (ber) {
-               candidates[ candidate ].sr_msgid = msgid;
-               rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_ADD,
-                                               mdn.bv_val, ber, msgid );
-               if (rc == msgid)
-                       rc = LDAP_SUCCESS;
-               else
-                       rc = LDAP_SERVER_DOWN;
+               struct timeval tv = {0, mt->mt_network_timeout*1000};
+               ber_socket_t s;
+               if (!( LDAP_BACK_CONN_ISBOUND( msc )
+                      || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+               ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+               if (s < 0) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+               if (rc < 0) {
+                       Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+                       if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+                               rc = LDAP_SERVER_DOWN;
+                       } else {
+                               goto error_unavailable;
+                       }
+               } else {
+                       candidates[ candidate ].sr_msgid = msgid;
+                       rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_ADD,
+                                                       mdn.bv_val, ber, msgid );
+                       if (rc == msgid)
+                               rc = LDAP_SUCCESS;
+                       else
+                               rc = LDAP_SERVER_DOWN;
+                       ber = NULL;
+               }
 
                switch ( rc ) {
                case LDAP_SUCCESS:
                        retcode = META_SEARCH_CANDIDATE;
                        asyncmeta_set_msc_time(msc);
-                       break;
+                       goto done;
 
                case LDAP_SERVER_DOWN:
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_clear_one_msc(NULL, mc, candidate);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) {
-                               nretries = 0;
-                               /* if the identity changed, there might be need to re-authz */
-                               (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
-                               goto retry;
+                       /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+                       if (do_lock > 0) {
+                               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+                               asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+                               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
                        }
-
+                       /* fall though*/
                default:
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       retcode = META_SEARCH_ERR;
+                       Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
                }
        }
 
-done:
+error_unavailable:
+       if (ber)
+               ber_free(ber, 1);
+       switch (bc->nretries[candidate]) {
+       case -1: /* nretries = forever */
+               ldap_pvt_thread_yield();
+               retcode = META_SEARCH_NEED_BIND;
+               break;
+       case 0: /* no retries left */
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               rs->sr_err = LDAP_UNAVAILABLE;
+               rs->sr_text = "Unable to send add request to target";
+               retcode = META_SEARCH_ERR;
+               break;
+       default: /* more retries left - try to rebind and go again */
+               retcode = META_SEARCH_NEED_BIND;
+               bc->nretries[candidate]--;
+               ldap_pvt_thread_yield();
+               break;
+       }
 
+done:
        (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
 
-       for ( --i; i >= 0; --i ) {
-               free( attrs[ i ]->mod_bvalues );
-               free( attrs[ i ] );
-       }
-       free( attrs );
-       if ( mdn.bv_val != op->ora_e->e_dn ) {
-               free( mdn.bv_val );
-               BER_BVZERO( &mdn );
+       if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+               op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
        }
 
-doreturn:;
        Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_add_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
        return retcode;
 }
@@ -252,26 +241,31 @@ asyncmeta_back_add( Operation *op, SlapReply *rs )
        a_metatarget_t  *mt;
        a_metaconn_t    *mc;
        int             rc, candidate = -1;
-       OperationBuffer opbuf;
+       void *thrctx = op->o_threadctx;
        bm_context_t *bc;
        SlapReply *candidates;
-       slap_callback *cb = op->o_callback;
+       time_t current_time = slap_get_time();
+       int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
 
-       Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_add: %s\n",
+       Debug(LDAP_DEBUG_TRACE, "==> asyncmeta_back_add: %s\n",
              op->o_req_dn.bv_val );
 
-       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
+       if (current_time > op->o_time) {
+               Debug(asyncmeta_debug, "==> asyncmeta_back_add[%s]: o_time:[%ld], current time: [%ld]\n",
+                     op->o_log_prefix, op->o_time, current_time );
+       }
+
+       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
        if (bc == NULL) {
                rs->sr_err = LDAP_OTHER;
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
        candidates = bc->candidates;
        mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
        if ( !mc || rs->sr_err != LDAP_SUCCESS) {
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
@@ -280,16 +274,39 @@ asyncmeta_back_add( Operation *op, SlapReply *rs )
        bc->retrying = LDAP_BACK_RETRYING;
        bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
        bc->stoptime = op->o_time + bc->timeout;
+       bc->bc_active = 1;
+
+       if (mc->pending_ops >= max_pending_ops) {
+               rs->sr_err = LDAP_BUSY;
+               rs->sr_text = "Maximum pending ops limit exceeded";
+               send_ldap_result(op, rs);
+               return rs->sr_err;
+       }
 
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
        rc = asyncmeta_add_message_queue(mc, bc);
+       mc->mc_conns[candidate].msc_active++;
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
 
        if (rc != LDAP_SUCCESS) {
                rs->sr_err = LDAP_BUSY;
                rs->sr_text = "Maximum pending ops limit exceeded";
-               asyncmeta_clear_bm_context(bc);
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+               mc->mc_conns[candidate].msc_active--;
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+               goto finish;
+       }
+
+retry:
+       current_time = slap_get_time();
+       if (bc->timeout && bc->stoptime < current_time) {
+               int             timeout_err;
+               timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+                       LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+               rs->sr_err = timeout_err;
+               rs->sr_text = "Operation timed out before it was sent to target";
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
        }
 
@@ -298,72 +315,49 @@ asyncmeta_back_add( Operation *op, SlapReply *rs )
        {
        case META_SEARCH_CANDIDATE:
                /* target is already bound, just send the request */
-               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add:  "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
+               Debug(LDAP_DEBUG_TRACE , "%s asyncmeta_back_add:  "
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
 
-               rc = asyncmeta_back_add_start( op, rs, mc, bc, candidate);
+               rc = asyncmeta_back_add_start( op, rs, mc, bc, candidate, 1);
                if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
 
+               } else if (rc == META_SEARCH_NEED_BIND) {
+                       goto retry;
                }
-                       break;
+               break;
        case META_SEARCH_NOT_CANDIDATE:
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: NOT_CANDIDATE "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
-               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-               asyncmeta_drop_bc(mc, bc);
-               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
 
        case META_SEARCH_NEED_BIND:
-       case META_SEARCH_CONNECTING:
-               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: NEED_BIND "
-                      "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
-               rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
-               if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
-                       goto finish;
-               }
-               break;
        case META_SEARCH_BINDING:
-                       Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: BINDING "
-                              "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
-                       /* Todo add the context to the message queue but do not send the request
-                          the receiver must send this when we are done binding */
-                       /* question - how would do receiver know to which targets??? */
-                       break;
+               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: BINDING "
+                      "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+               /* add the context to the message queue but do not send the request
+                  the receiver must send this when we are done binding */
+               break;
 
        case META_SEARCH_ERR:
-                       Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: ERR "
-                              "cnd=\"%ldd\"\n", op->o_log_prefix, candidate );
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       candidates[ candidate ].sr_type = REP_RESULT;
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
-                       goto finish;
-               default:
-                       assert( 0 );
-                       break;
-               }
+               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: ERR "
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+               goto finish;
+       default:
+               assert( 0 );
+               break;
+       }
 
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+       mc->mc_conns[candidate].msc_active--;
        asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+       bc->bc_active--;
+       asyncmeta_memctx_toggle(thrctx);
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+       rs->sr_err = SLAPD_ASYNCOP;
 finish:
        return rs->sr_err;
 }
index 53d57f6052680dd13a2e4caa50cb7d54c221bf22..ab1f03619f201abe789f0c6aacb9bbd2b65ae420 100644 (file)
 #ifndef SLAPD_ASYNCMETA_H
 #define SLAPD_ASYNCMETA_H
 
-#ifndef ENABLE_REWRITE
-#error "--enable-rewrite is required!"
-#endif
-
 #ifdef LDAP_DEVEL
 #define SLAPD_META_CLIENT_PR 1
 #endif /* LDAP_DEVEL */
 
 #include "proto-asyncmeta.h"
 
-/* String rewrite library */
-#include "rewrite.h"
 #include "ldap_rq.h"
 
 LDAP_BEGIN_DECL
@@ -50,108 +44,6 @@ LDAP_BEGIN_DECL
 #define META_BACK_PRINT_CONNTREE 0
 #endif /* !META_BACK_PRINT_CONNTREE */
 
-/* from back-ldap.h before rwm removal */
-struct ldapmap {
-       int drop_missing;
-
-       Avlnode *map;
-       Avlnode *remap;
-};
-
-struct ldapmapping {
-       struct berval src;
-       struct berval dst;
-};
-
-struct ldaprwmap {
-       /*
-        * DN rewriting
-        */
-       struct rewrite_info *rwm_rw;
-       BerVarray rwm_bva_rewrite;
-
-       /*
-        * Attribute/objectClass mapping
-        */
-       struct ldapmap rwm_oc;
-       struct ldapmap rwm_at;
-       BerVarray rwm_bva_map;
-};
-
-/* Whatever context asyncmeta_dn_massage needs... */
-typedef struct a_dncookie {
-       struct a_metatarget_t   *target;
-
-       Connection              *conn;
-       char                    *ctx;
-       SlapReply               *rs;
-} a_dncookie;
-
-int asyncmeta_dn_massage(a_dncookie *dc, struct berval *dn,
-       struct berval *res);
-
-extern int asyncmeta_conn_dup( void *c1, void *c2 );
-extern void asyncmeta_conn_free( void *c );
-
-/* attributeType/objectClass mapping */
-int asyncmeta_mapping_cmp (const void *, const void *);
-int asyncmeta_mapping_dup (void *, void *);
-
-void asyncmeta_map_init ( struct ldapmap *lm, struct ldapmapping ** );
-int asyncmeta_mapping ( struct ldapmap *map, struct berval *s,
-       struct ldapmapping **m, int remap );
-void asyncmeta_map ( struct ldapmap *map, struct berval *s, struct berval *m,
-       int remap );
-#define BACKLDAP_MAP   0
-#define BACKLDAP_REMAP 1
-char *
-asyncmeta_map_filter(
-       struct ldapmap *at_map,
-       struct ldapmap *oc_map,
-       struct berval *f,
-       int remap );
-
-int
-asyncmeta_map_attrs(
-       Operation *op,
-       struct ldapmap *at_map,
-       AttributeName *a,
-       int remap,
-       char ***mapped_attrs );
-
-extern int
-asyncmeta_filter_map_rewrite(
-       a_dncookie      *dc,
-       Filter          *f,
-       struct berval   *fstr,
-       int             remap,
-       void            *memctx );
-
-/* suffix massaging by means of librewrite */
-extern int
-asyncmeta_suffix_massage_config( struct rewrite_info *info,
-       struct berval *pvnc,
-       struct berval *nvnc,
-       struct berval *prnc,
-       struct berval *nrnc );
-
-extern int
-asyncmeta_back_referral_result_rewrite(
-       a_dncookie      *dc,
-       BerVarray       a_vals,
-       void            *memctx );
-extern int
-asyncmeta_dnattr_rewrite(
-       a_dncookie      *dc,
-       BerVarray       a_vals );
-extern int
-asyncmeta_dnattr_result_rewrite(
-       a_dncookie      *dc,
-       BerVarray       a_vals );
-
-
-/* (end of) from back-ldap.h before rwm removal */
-
 /*
  * A a_metasingleconn_t can be in the following, mutually exclusive states:
  *
@@ -171,6 +63,7 @@ asyncmeta_dnattr_result_rewrite(
 
 #define META_BACK_FCONN_INITED         (0x00100000U)
 #define META_BACK_FCONN_CREATING       (0x00200000U)
+#define META_BACK_FCONN_INVALID                (0x00400000U)
 
 #define        META_BACK_CONN_INITED(lc)               LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_INITED)
 #define        META_BACK_CONN_INITED_SET(lc)           LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_INITED)
@@ -180,6 +73,9 @@ asyncmeta_dnattr_result_rewrite(
 #define        META_BACK_CONN_CREATING_SET(lc)         LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_CREATING)
 #define        META_BACK_CONN_CREATING_CLEAR(lc)       LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_CREATING)
 #define        META_BACK_CONN_CREATING_CPY(lc, mlc)    LDAP_BACK_CONN_CPY((lc), META_BACK_FCONN_CREATING, (mlc))
+#define        META_BACK_CONN_INVALID(lc)              LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_INVALID)
+#define        META_BACK_CONN_INVALID_SET(lc)          LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_INVALID)
+#define        META_BACK_CONN_INVALID_CLEAR(lc)        LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_INVALID)
 
 struct a_metainfo_t;
 struct a_metaconn_t;
@@ -190,20 +86,25 @@ struct a_metatarget_t;
 #define        META_RETRYING                   ((ber_tag_t)0x4)
 
 typedef struct bm_context_t {
-       LDAP_SLIST_ENTRY(bm_context_t) bc_next;
+       LDAP_STAILQ_ENTRY(bm_context_t) bc_next;
+       struct a_metaconn_t *bc_mc;
        time_t                  timeout;
        time_t                  stoptime;
        ldap_back_send_t        sendok;
        ldap_back_send_t        retrying;
        int candidate_match;
-       int sent;
-       int bc_active;
+       volatile int bc_active;
        int searchtime; /* stoptime is a search timelimit */
        int is_ok;
+       int is_root;
+       volatile sig_atomic_t bc_invalid;
        SlapReply               rs;
        Operation               *op;
+       Operation               copy_op;
        LDAPControl             **ctrls;
        int                     *msgids;
+       int                     *nretries;  /* number of times to retry a failed send on an msc */
+       struct berval           c_peer_name; /* peer name of original op->o_conn*/
        SlapReply               *candidates;
 } bm_context_t;
 
@@ -226,14 +127,16 @@ typedef struct a_metasingleconn_t {
        LDAP                    *msc_ld;
        LDAP                    *msc_ldr;
        time_t                  msc_time;
+       time_t                  msc_binding_time;
+       time_t                  msc_result_time;
        struct berval           msc_bound_ndn;
        struct berval           msc_cred;
        unsigned                msc_mscflags;
+
        /* NOTE: lc_lcflags is redefined to msc_mscflags to reuse the macros
         * defined for back-ldap */
 #define        lc_lcflags              msc_mscflags
-       int msc_pending_ops;
-       int msc_timeout_ops;
+       volatile int msc_active;
                /* Connection for the select */
        Connection *conn;
 } a_metasingleconn_t;
@@ -267,7 +170,7 @@ typedef struct a_metaconn_t {
        int pending_ops;
        ldap_pvt_thread_mutex_t mc_om_mutex;
        /* queue for pending operations */
-       LDAP_SLIST_HEAD(BCList, bm_context_t) mc_om_list;
+       LDAP_STAILQ_HEAD(BCList, bm_context_t) mc_om_list;
        /* supersedes the connection stuff */
        a_metasingleconn_t      *mc_conns;
 } a_metaconn_t;
@@ -309,7 +212,7 @@ typedef struct a_metacommon_t {
 #define META_RETRY_UNDEFINED   (-2)
 #define META_RETRY_FOREVER     (-1)
 #define META_RETRY_NEVER       (0)
-#define META_RETRY_DEFAULT     (10)
+#define META_RETRY_DEFAULT     (2)
 
        unsigned                mc_flags;
 #define        META_BACK_CMN_ISSET(mc,f)               ( ( (mc)->mc_flags & (f) ) == (f) )
@@ -359,6 +262,9 @@ typedef struct a_metatarget_t {
        struct berval           mt_psuffix;             /* pretty suffix */
        struct berval           mt_nsuffix;             /* normalized suffix */
 
+       struct berval           mt_lsuffixm;    /* local suffix for massage */
+       struct berval           mt_rsuffixm;    /* remote suffix for massage */
+
        struct berval           mt_binddn;
        struct berval           mt_bindpw;
 
@@ -379,8 +285,6 @@ typedef struct a_metatarget_t {
 #define        mt_idassert_flags       mt_idassert.si_flags
 #define        mt_idassert_authz       mt_idassert.si_authz
 
-       struct ldaprwmap        mt_rwmap;
-
        sig_atomic_t            mt_isquarantined;
        ldap_pvt_thread_mutex_t mt_quarantine_mutex;
 
@@ -520,6 +424,7 @@ typedef struct a_metainfo_t {
        int                    mi_next_conn;
        a_metaconn_t          *mi_conns;
 
+       struct berval           mi_suffix;
 } a_metainfo_t;
 
 typedef enum meta_op_type {
@@ -528,6 +433,33 @@ typedef enum meta_op_type {
        META_OP_REQUIRE_ALL
 } meta_op_type;
 
+/* Whatever context asyncmeta_dn_massage needs... */
+typedef struct a_dncookie {
+       Operation               *op;
+       struct a_metatarget_t   *target;
+       void    *memctx;
+       int     to_from;
+} a_dncookie;
+
+
+#define MASSAGE_REQ    0
+#define MASSAGE_REP    1
+
+extern void
+asyncmeta_dn_massage(a_dncookie *dc, struct berval *dn,
+       struct berval *res);
+
+extern void
+asyncmeta_filter_map_rewrite(
+       a_dncookie      *dc,
+       Filter          *f,
+       struct berval   *fstr );
+
+extern void
+asyncmeta_back_referral_result_rewrite(
+       a_dncookie      *dc,
+       BerVarray       a_vals );
+
 extern a_metaconn_t *
 asyncmeta_getconn(
        Operation               *op,
@@ -537,17 +469,6 @@ asyncmeta_getconn(
        ldap_back_send_t        sendok,
        int                     alloc_new);
 
-extern int
-asyncmeta_retry(
-       Operation               *op,
-       SlapReply               *rs,
-       a_metaconn_t            **mcp,
-       int                     candidate,
-       ldap_back_send_t        sendok );
-
-extern void
-asyncmeta_conn_free(
-       void                    *v_mc );
 
 extern int
 asyncmeta_init_one_conn(
@@ -566,24 +487,6 @@ asyncmeta_quarantine(
        SlapReply               *rs,
        int                     candidate );
 
-extern int
-asyncmeta_dobind(
-       Operation               *op,
-       SlapReply               *rs,
-       a_metaconn_t            *mc,
-       ldap_back_send_t        sendok,
-       SlapReply               *candidates);
-
-extern int
-asyncmeta_single_dobind(
-       Operation               *op,
-       SlapReply               *rs,
-       a_metaconn_t            **mcp,
-       int                     candidate,
-       ldap_back_send_t        sendok,
-       int                     retries,
-       int                     dolock );
-
 extern int
 asyncmeta_proxy_authz_cred(
        a_metaconn_t            *mc,
@@ -595,31 +498,13 @@ asyncmeta_proxy_authz_cred(
        struct berval           *bindcred,
        int                     *method );
 
-extern int
-asyncmeta_cancel(
-       a_metaconn_t            *mc,
-       Operation               *op,
-       SlapReply               *rs,
-       ber_int_t               msgid,
-       int                     candidate,
-       ldap_back_send_t        sendok );
-
-extern int
-asyncmeta_op_result(
-       a_metaconn_t            *mc,
-       Operation               *op,
-       SlapReply               *rs,
-       int                     candidate,
-       ber_int_t               msgid,
-       time_t                  timeout,
-       ldap_back_send_t        sendok );
-
 extern int
 asyncmeta_controls_add(
        Operation       *op,
        SlapReply       *rs,
        a_metaconn_t    *mc,
        int             candidate,
+       int             isroot,
        LDAPControl     ***pctrls );
 
 extern int
@@ -682,9 +567,6 @@ asyncmeta_dncache_delete_entry(
 extern void
 asyncmeta_dncache_free( void *entry );
 
-extern void
-asyncmeta_back_map_free( struct ldapmap *lm );
-
 extern int
 asyncmeta_subtree_destroy( a_metasubtree_t *ms );
 
@@ -724,12 +606,12 @@ void asyncmeta_clear_bm_context(bm_context_t *bc);
 
 int asyncmeta_add_message_queue(a_metaconn_t *mc, bm_context_t *bc);
 void asyncmeta_drop_bc(a_metaconn_t *mc, bm_context_t *bc);
+void asyncmeta_drop_bc_from_fconn(bm_context_t *bc);
 
 bm_context_t *
 asyncmeta_find_message(ber_int_t msgid, a_metaconn_t *mc, int candidate);
 
-bm_context_t *
-asyncmeta_find_message_by_opmsguid(ber_int_t msgid, a_metaconn_t *mc, int remove);
+void asyncmeta_memctx_toggle(void *thrctx);
 
 void* asyncmeta_op_handle_result(void *ctx, void *arg);
 int asyncmeta_back_cleanup( Operation *op, SlapReply *rs, bm_context_t *bm );
@@ -738,16 +620,19 @@ int
 asyncmeta_clear_one_msc(
        Operation       *op,
        a_metaconn_t    *msc,
-       int             candidate );
+       int             candidate,
+       int             unbind,
+       const char *          caller);
 
 a_metaconn_t *
 asyncmeta_get_next_mc( a_metainfo_t *mi );
 
 void* asyncmeta_timeout_loop(void *ctx, void *arg);
+
 int
 asyncmeta_start_timeout_loop(a_metatarget_t *mt, a_metainfo_t *mi);
+
 void asyncmeta_set_msc_time(a_metasingleconn_t *msc);
-void asyncmeta_clear_message_queue(a_metasingleconn_t *msc);
 
 int asyncmeta_back_cancel(
        a_metaconn_t    *mc,
@@ -755,25 +640,15 @@ int asyncmeta_back_cancel(
        ber_int_t               msgid,
        int                             candidate );
 
-int
-asyncmeta_back_cancel_msc(
-       Operation               *op,
-       SlapReply               *rs,
-       ber_int_t               msgid,
-       a_metasingleconn_t      *msc,
-       int                     candidate,
-       ldap_back_send_t        sendok );
-
-int
-asyncmeta_back_abandon_candidate(
-       a_metaconn_t            *mc,
-       Operation               *op,
-       ber_int_t               msgid,
-       int                     candidate );
 void
 asyncmeta_send_result(bm_context_t* bc, int error, char *text);
 
-int asyncmeta_new_bm_context(Operation *op, SlapReply *rs, bm_context_t **new_bc, int ntargets);
+int asyncmeta_new_bm_context(Operation *op,
+                            SlapReply *rs,
+                            bm_context_t **new_bc,
+                            int ntargets,
+                            a_metainfo_t       *mi);
+
 int asyncmeta_start_listeners(a_metaconn_t *mc, SlapReply *candidates,  bm_context_t *bc);
 int asyncmeta_start_one_listener(a_metaconn_t *mc, SlapReply *candidates,  bm_context_t *bc, int candidate);
 
@@ -785,7 +660,8 @@ asyncmeta_back_search_start(
        bm_context_t          *bc,
        int                   candidate,
        struct berval         *prcookie,
-       ber_int_t             prsize );
+       ber_int_t             prsize,
+       int do_lock);
 
 meta_search_candidate_t
 asyncmeta_dobind_init(
@@ -808,40 +684,97 @@ asyncmeta_back_add_start(Operation *op,
                         SlapReply *rs,
                         a_metaconn_t *mc,
                         bm_context_t *bc,
-                        int candidate);
+                        int candidate,
+                        int do_lock);
 meta_search_candidate_t
 asyncmeta_back_modify_start(Operation *op,
-                        SlapReply *rs,
-                        a_metaconn_t *mc,
-                        bm_context_t *bc,
-                        int candidate);
+                           SlapReply *rs,
+                           a_metaconn_t *mc,
+                           bm_context_t *bc,
+                           int candidate,
+                           int do_lock);
 
 meta_search_candidate_t
 asyncmeta_back_modrdn_start(Operation *op,
-                        SlapReply *rs,
-                        a_metaconn_t *mc,
-                        bm_context_t *bc,
-                        int candidate);
+                           SlapReply *rs,
+                           a_metaconn_t *mc,
+                           bm_context_t *bc,
+                           int candidate,
+                           int do_lock);
 meta_search_candidate_t
 asyncmeta_back_delete_start(Operation *op,
-                        SlapReply *rs,
-                        a_metaconn_t *mc,
-                        bm_context_t *bc,
-                        int candidate);
+                           SlapReply *rs,
+                           a_metaconn_t *mc,
+                           bm_context_t *bc,
+                           int candidate,
+                           int do_lock);
 
 meta_search_candidate_t
 asyncmeta_back_compare_start(Operation *op,
-                        SlapReply *rs,
-                        a_metaconn_t *mc,
-                        bm_context_t *bc,
-                        int candidate);
+                            SlapReply *rs,
+                            a_metaconn_t *mc,
+                            bm_context_t *bc,
+                            int candidate,
+                            int do_lock);
 
+bm_context_t *
+asyncmeta_bc_in_queue(a_metaconn_t *mc,
+                     bm_context_t *bc);
+
+int
+asyncmeta_error_cleanup(Operation *op,
+                       SlapReply *rs,
+                       bm_context_t *bc,
+                       a_metaconn_t *mc,
+                       int candidate);
+
+int
+asyncmeta_reset_msc(Operation  *op,
+                   a_metaconn_t        *mc,
+                   int         candidate,
+                   int             unbind,
+                   const char *caller);
+
+
+void
+asyncmeta_back_conn_free(
+                   void                *v_mc );
+
+void asyncmeta_log_msc(a_metasingleconn_t *msc);
+void asyncmeta_log_conns(a_metainfo_t *mi);
+
+void asyncmeta_get_timestamp(char *buf);
+
+int
+asyncmeta_dncache_update_entry(a_metadncache_t *cache,
+                              struct berval    *ndn,
+                              int              target );
+
+void
+asyncmeta_dnattr_result_rewrite(a_dncookie             *dc,
+                               BerVarray               a_vals);
 
 void
-asyncmeta_sender_error(Operation *op,
-                      SlapReply *rs,
-                      slap_callback *cb);
+asyncmeta_referral_result_rewrite(a_dncookie           *dc,
+                                 BerVarray             a_vals);
+
+meta_search_candidate_t
+asyncmeta_send_all_pending_ops(a_metaconn_t *mc,
+                              int          candidate,
+                              void         *ctx,
+                              int          dolock);
+meta_search_candidate_t
+asyncmeta_return_bind_errors(a_metaconn_t *mc,
+                            int          candidate,
+                            SlapReply    *bind_result,
+                            void         *ctx,
+                            int          dolock);
+
+/* The the maximum time in seconds after a result has been received on a connection,
+ * after which it can be reset if a sender error occurs. Should this be configurable? */
+#define META_BACK_RESULT_INTERVAL (2)
 
+extern int asyncmeta_debug;
 
 LDAP_END_DECL
 
index 6e4aec380c20bc038495bc44b4c55f36562145c2..8b33b8b2940ef80dede4b0a63167e92fd7fec60e 100644 (file)
 #include <ac/errno.h>
 #include <ac/socket.h>
 #include <ac/string.h>
-
+#include "slap.h"
+#include "../../../libraries/libldap/ldap-int.h"
 
 #define AVL_INTERNAL
-#include "slap.h"
 #include "../back-ldap/back-ldap.h"
 #include "back-asyncmeta.h"
-
 #include "lutil_ldap.h"
 
+#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ      "2.16.840.1.113730.3.4.12"
+
 static int
 asyncmeta_proxy_authz_bind(
        a_metaconn_t            *mc,
@@ -250,7 +251,6 @@ asyncmeta_bind_op_result(
        struct timeval          tv;
        int                     rc;
        int                     nretries = mt->mt_nretries;
-       char                    buf[ SLAP_TEXT_BUFLEN ];
 
        Debug( LDAP_DEBUG_TRACE,
                ">>> %s asyncmeta_bind_op_result[%d]\n",
@@ -333,7 +333,7 @@ retry:;
                        ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
                                &rs->sr_err );
 
-                       Debug(LDAP_DEBUG_ANY,
+                       Debug( LDAP_DEBUG_ANY,
                              "### %s asyncmeta_bind_op_result[%d]: err=%d (%s) nretries=%d.\n",
                              op->o_log_prefix, candidate, rs->sr_err,
                              ldap_err2string(rs->sr_err), nretries );
@@ -401,16 +401,12 @@ asyncmeta_single_bind(
        /*
         * Rewrite the bind dn if needed
         */
+       dc.op = op;
        dc.target = mt;
-       dc.conn = op->o_conn;
-       dc.rs = rs;
-       dc.ctx = "bindDN";
+       dc.memctx = op->o_tmpmemctx;
+       dc.to_from = MASSAGE_REQ;
 
-       if ( asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
-               rs->sr_text = "DN rewrite error";
-               rs->sr_err = LDAP_OTHER;
-               return rs->sr_err;
-       }
+       asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
 
        /* don't add proxyAuthz; set the bindDN */
        save_o_dn = op->o_dn;
@@ -419,7 +415,7 @@ asyncmeta_single_bind(
        op->o_dn = op->o_req_dn;
 
        ctrls = op->o_ctrls;
-       rs->sr_err = asyncmeta_controls_add( op, rs, mc, candidate, &ctrls );
+       rs->sr_err = asyncmeta_controls_add( op, rs, mc, candidate, be_isroot(op), &ctrls );
        op->o_dn = save_o_dn;
        op->o_do_not_cache = save_o_do_not_cache;
        if ( rs->sr_err != LDAP_SUCCESS ) {
@@ -484,7 +480,7 @@ cache_refresh:;
 
 return_results:;
        if ( mdn.bv_val != op->o_req_dn.bv_val ) {
-               free( mdn.bv_val );
+               op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
        }
 
        if ( META_BACK_TGT_QUARANTINE( mt ) ) {
@@ -497,81 +493,6 @@ return_results:;
        return rs->sr_err;
 }
 
-/*
- * asyncmeta_back_single_dobind
- */
-int
-asyncmeta_back_single_dobind(
-       Operation               *op,
-       SlapReply               *rs,
-       a_metaconn_t            **mcp,
-       int                     candidate,
-       ldap_back_send_t        sendok,
-       int                     nretries,
-       int                     dolock )
-{
-       a_metaconn_t            *mc = *mcp;
-       a_metainfo_t            *mi = mc->mc_info;
-       a_metatarget_t          *mt = mi->mi_targets[ candidate ];
-       a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
-       int                     msgid;
-
-       assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
-
-       if ( op->o_conn != NULL &&
-               !op->o_do_not_cache &&
-               ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
-                       BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
-                       ( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) ||
-                       ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
-       {
-               (void)asyncmeta_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock );
-
-       } else {
-               char *binddn = "";
-               struct berval cred = BER_BVC( "" );
-
-               /* use credentials if available */
-               if ( !BER_BVISNULL( &msc->msc_bound_ndn )
-                       && !BER_BVISNULL( &msc->msc_cred ) )
-               {
-                       binddn = msc->msc_bound_ndn.bv_val;
-                       cred = msc->msc_cred;
-               }
-
-               for (;;) {
-                       rs->sr_err = ldap_sasl_bind( msc->msc_ld,
-                               binddn, LDAP_SASL_SIMPLE, &cred,
-                               NULL, NULL, &msgid );
-                       if ( rs->sr_err != LDAP_X_CONNECTING ) {
-                               break;
-                       }
-                       ldap_pvt_thread_yield();
-               }
-
-               rs->sr_err = asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
-
-               /* if bind succeeded, but anonymous, clear msc_bound_ndn */
-               if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) {
-                       if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
-                               ber_memfree( msc->msc_bound_ndn.bv_val );
-                               BER_BVZERO( &msc->msc_bound_ndn );
-                       }
-
-                       if ( !BER_BVISNULL( &msc->msc_cred ) ) {
-                               memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
-                               ber_memfree( msc->msc_cred.bv_val );
-                               BER_BVZERO( &msc->msc_cred );
-                       }
-               }
-       }
-
-       if ( META_BACK_TGT_QUARANTINE( mt ) ) {
-               asyncmeta_quarantine( op, mi, rs, candidate );
-       }
-
-       return rs->sr_err;
-}
 
 /*
  * asyncmeta_back_default_rebind
@@ -644,16 +565,28 @@ asyncmeta_back_cancel(
        a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
 
        int                     rc = LDAP_OTHER;
+       struct timeval tv = { 0, 0 };
+       ber_socket_t s;
 
        Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel[%d] msgid=%d\n",
                op->o_log_prefix, candidate, msgid );
 
-       if (msc->msc_ld == NULL) {
+       if (!( LDAP_BACK_CONN_ISBOUND( msc )
+              || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
                Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel[%d] msgid=%d\n already reset",
                op->o_log_prefix, candidate, msgid );
                return LDAP_SUCCESS;
        }
 
+       ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+       if (s < 0) {
+               return rc;
+       }
+       rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+       if (rc < 0) {
+               rc = LDAP_SERVER_DOWN;
+               return rc;
+       }
        /* default behavior */
        if ( META_BACK_TGT_ABANDON( mt ) ) {
                rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
@@ -675,396 +608,9 @@ asyncmeta_back_cancel(
 }
 
 
-int
-asyncmeta_back_abandon_candidate(
-       a_metaconn_t            *mc,
-       Operation               *op,
-       ber_int_t               msgid,
-       int                     candidate )
-{
-
-       a_metainfo_t            *mi = mc->mc_info;
-       a_metatarget_t          *mt = mi->mi_targets[ candidate ];
-       a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
-
-       int                     rc = LDAP_OTHER;
-
-       Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_abandon[%d] msgid=%d\n",
-               op->o_log_prefix, candidate, msgid );
-
-       rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
-
-       Debug( LDAP_DEBUG_TRACE, "<<< %s asyncmeta_back_abandon[%d] err=%d\n",
-               op->o_log_prefix, candidate, rc );
-
-       return rc;
-}
-
-int
-asyncmeta_back_cancel_msc(
-       Operation               *op,
-       SlapReply               *rs,
-       ber_int_t               msgid,
-       a_metasingleconn_t      *msc,
-       int                     candidate,
-       ldap_back_send_t        sendok )
-{
-       a_metainfo_t            *mi = (a_metainfo_t *)op->o_bd->be_private;
-
-       a_metatarget_t          *mt = mi->mi_targets[ candidate ];
-
-       int                     rc = LDAP_OTHER;
-
-       Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel_msc[%d] msgid=%d\n",
-               op->o_log_prefix, candidate, msgid );
-
-       /* default behavior */
-       if ( META_BACK_TGT_ABANDON( mt ) ) {
-               rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
-
-       } else if ( META_BACK_TGT_IGNORE( mt ) ) {
-               rc = ldap_pvt_discard( msc->msc_ld, msgid );
-
-       } else if ( META_BACK_TGT_CANCEL( mt ) ) {
-               rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
-
-       } else {
-               assert( 0 );
-       }
-
-       Debug( LDAP_DEBUG_TRACE, "<<< %s asyncmeta_back_cancel_msc[%d] err=%d\n",
-               op->o_log_prefix, candidate, rc );
-
-       return rc;
-}
 
 /*
- * FIXME: error return must be handled in a cleaner way ...
- */
-int
-asyncmeta_back_op_result(
-       a_metaconn_t            *mc,
-       Operation               *op,
-       SlapReply               *rs,
-       int                     candidate,
-       ber_int_t               msgid,
-       time_t                  timeout,
-       ldap_back_send_t        sendok )
-{
-       a_metainfo_t    *mi = mc->mc_info;
-
-       const char      *save_text = rs->sr_text,
-                       *save_matched = rs->sr_matched;
-       BerVarray       save_ref = rs->sr_ref;
-       LDAPControl     **save_ctrls = rs->sr_ctrls;
-       void            *matched_ctx = NULL;
-
-       char            *matched = NULL;
-       char            *text = NULL;
-       char            **refs = NULL;
-       LDAPControl     **ctrls = NULL;
-
-       assert( mc != NULL );
-
-       rs->sr_text = NULL;
-       rs->sr_matched = NULL;
-       rs->sr_ref = NULL;
-       rs->sr_ctrls = NULL;
-
-       if ( candidate != META_TARGET_NONE ) {
-               a_metatarget_t          *mt = mi->mi_targets[ candidate ];
-               a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
-
-               if ( LDAP_ERR_OK( rs->sr_err ) ) {
-                       int             rc;
-                       struct timeval  tv;
-                       LDAPMessage     *res = NULL;
-                       time_t          stoptime = (time_t)(-1);
-                       int             timeout_err = op->o_protocol >= LDAP_VERSION3 ?
-                                               LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
-                       const char      *timeout_text = "Operation timed out";
-
-                       /* if timeout is not specified, compute and use
-                        * the one specific to the ongoing operation */
-                       if ( timeout == (time_t)(-1) ) {
-                               slap_op_t       opidx = slap_req2op( op->o_tag );
-
-                               if ( opidx == SLAP_OP_SEARCH ) {
-                                       if ( op->ors_tlimit <= 0 ) {
-                                               timeout = 0;
-
-                                       } else {
-                                               timeout = op->ors_tlimit;
-                                               timeout_err = LDAP_TIMELIMIT_EXCEEDED;
-                                               timeout_text = NULL;
-                                       }
-
-                               } else {
-                                       timeout = mt->mt_timeout[ opidx ];
-                               }
-                       }
-
-                       /* better than nothing :) */
-                       if ( timeout == 0 ) {
-                               if ( mi->mi_idle_timeout ) {
-                                       timeout = mi->mi_idle_timeout;
-
-                               }
-                       }
-
-                       if ( timeout ) {
-                               stoptime = op->o_time + timeout;
-                       }
-
-                       LDAP_BACK_TV_SET( &tv );
-
-retry:;
-                       rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
-                       switch ( rc ) {
-                       case 0:
-                               if ( timeout && slap_get_time() > stoptime ) {
-                                       (void)asyncmeta_back_cancel( mc, op, msgid, candidate );
-                                       rs->sr_err = timeout_err;
-                                       rs->sr_text = timeout_text;
-                                       break;
-                               }
-
-                               LDAP_BACK_TV_SET( &tv );
-                               ldap_pvt_thread_yield();
-                               goto retry;
-
-                       case -1:
-                               ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE,
-                                               &rs->sr_err );
-                               break;
-
-
-                       /* otherwise get the result; if it is not
-                        * LDAP_SUCCESS, record it in the reply
-                        * structure (this includes
-                        * LDAP_COMPARE_{TRUE|FALSE}) */
-                       default:
-                               /* only touch when activity actually took place... */
-                               if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
-                                       msc->msc_time = op->o_time;
-                               }
-
-                               rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
-                                               &matched, &text, &refs, &ctrls, 1 );
-                               res = NULL;
-                               if ( rc == LDAP_SUCCESS ) {
-                                       rs->sr_text = text;
-                               } else {
-                                       rs->sr_err = rc;
-                               }
-                               rs->sr_err = slap_map_api2result( rs );
-
-                               /* RFC 4511: referrals can only appear
-                                * if result code is LDAP_REFERRAL */
-                               if ( refs != NULL
-                                       && refs[ 0 ] != NULL
-                                       && refs[ 0 ][ 0 ] != '\0' )
-                               {
-                                       if ( rs->sr_err != LDAP_REFERRAL ) {
-                                               Debug( LDAP_DEBUG_ANY,
-                                                       "%s asyncmeta_back_op_result[%d]: "
-                                                       "got referrals with err=%d\n",
-                                                       op->o_log_prefix,
-                                                       candidate, rs->sr_err );
-
-                                       } else {
-                                               int     i;
-
-                                               for ( i = 0; refs[ i ] != NULL; i++ )
-                                                       /* count */ ;
-                                               rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
-                                                       op->o_tmpmemctx );
-                                               for ( i = 0; refs[ i ] != NULL; i++ ) {
-                                                       ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
-                                               }
-                                               BER_BVZERO( &rs->sr_ref[ i ] );
-                                       }
-
-                               } else if ( rs->sr_err == LDAP_REFERRAL ) {
-                                       Debug( LDAP_DEBUG_ANY,
-                                               "%s asyncmeta_back_op_result[%d]: "
-                                               "got err=%d with null "
-                                               "or empty referrals\n",
-                                               op->o_log_prefix,
-                                               candidate, rs->sr_err );
-
-                                       rs->sr_err = LDAP_NO_SUCH_OBJECT;
-                               }
-
-                               if ( ctrls != NULL ) {
-                                       rs->sr_ctrls = ctrls;
-                               }
-                       }
-
-                       assert( res == NULL );
-               }
-
-               /* if the error in the reply structure is not
-                * LDAP_SUCCESS, try to map it from client
-                * to server error */
-               if ( !LDAP_ERR_OK( rs->sr_err ) ) {
-                       rs->sr_err = slap_map_api2result( rs );
-
-                       /* internal ops ( op->o_conn == NULL )
-                        * must not reply to client */
-                       if ( op->o_conn && !op->o_do_not_cache && matched ) {
-
-                               /* record the (massaged) matched
-                                * DN into the reply structure */
-                               rs->sr_matched = matched;
-                       }
-               }
-
-               if ( META_BACK_TGT_QUARANTINE( mt ) ) {
-                       asyncmeta_quarantine( op, mi, rs, candidate );
-               }
-
-       } else {
-               int     i,
-                       err = rs->sr_err;
-
-               for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                       a_metasingleconn_t      *msc = &mc->mc_conns[ i ];
-                       char                    *xtext = NULL;
-                       char                    *xmatched = NULL;
-
-                       if ( msc->msc_ld == NULL ) {
-                               continue;
-                       }
-
-                       rs->sr_err = LDAP_SUCCESS;
-
-                       ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rs->sr_err );
-                       if ( rs->sr_err != LDAP_SUCCESS ) {
-                               /*
-                                * better check the type of error. In some cases
-                                * (search ?) it might be better to return a
-                                * success if at least one of the targets gave
-                                * positive result ...
-                                */
-                               ldap_get_option( msc->msc_ld,
-                                               LDAP_OPT_DIAGNOSTIC_MESSAGE, &xtext );
-                               if ( xtext != NULL && xtext [ 0 ] == '\0' ) {
-                                       ldap_memfree( xtext );
-                                       xtext = NULL;
-                               }
-
-                               ldap_get_option( msc->msc_ld,
-                                               LDAP_OPT_MATCHED_DN, &xmatched );
-                               if ( xmatched != NULL && xmatched[ 0 ] == '\0' ) {
-                                       ldap_memfree( xmatched );
-                                       xmatched = NULL;
-                               }
-
-                               rs->sr_err = slap_map_api2result( rs );
-
-                               Debug(LDAP_DEBUG_ANY,
-                                     "%s asyncmeta_back_op_result[%d] " "err=%d text=\"%s\" matched=\"%s\".\n",
-                                     op->o_log_prefix, i, rs->sr_err,
-                                     (xtext ? xtext : ""),
-                                     (xmatched ? xmatched : "") );
-
-                               /*
-                                * FIXME: need to rewrite "match" (need rwinfo)
-                                */
-                               switch ( rs->sr_err ) {
-                               default:
-                                       err = rs->sr_err;
-                                       if ( xtext != NULL ) {
-                                               if ( text ) {
-                                                       ldap_memfree( text );
-                                               }
-                                               text = xtext;
-                                               xtext = NULL;
-                                       }
-                                       if ( xmatched != NULL ) {
-                                               if ( matched ) {
-                                                       ldap_memfree( matched );
-                                               }
-                                               matched = xmatched;
-                                               xmatched = NULL;
-                                       }
-                                       break;
-                               }
-
-                               if ( xtext ) {
-                                       ldap_memfree( xtext );
-                               }
-
-                               if ( xmatched ) {
-                                       ldap_memfree( xmatched );
-                               }
-                       }
-
-                       if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
-                               asyncmeta_quarantine( op, mi, rs, i );
-                       }
-               }
-
-               if ( err != LDAP_SUCCESS ) {
-                       rs->sr_err = err;
-               }
-       }
-
-       if ( matched != NULL ) {
-               struct berval   dn, pdn;
-
-               ber_str2bv( matched, 0, 0, &dn );
-               if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
-                       ldap_memfree( matched );
-                       matched_ctx = op->o_tmpmemctx;
-                       matched = pdn.bv_val;
-               }
-               rs->sr_matched = matched;
-       }
-
-       if ( rs->sr_err == LDAP_UNAVAILABLE ) {
-               if ( !( sendok & LDAP_BACK_RETRYING ) ) {
-                       if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
-                               if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
-                               send_ldap_result( op, rs );
-                       }
-               }
-
-       } else if ( op->o_conn &&
-               ( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
-                       || ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
-       {
-               send_ldap_result( op, rs );
-       }
-       if ( matched ) {
-               op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
-       }
-       if ( text ) {
-               ldap_memfree( text );
-       }
-       if ( rs->sr_ref ) {
-               op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
-               rs->sr_ref = NULL;
-       }
-       if ( refs ) {
-               ber_memvfree( (void **)refs );
-       }
-       if ( ctrls ) {
-               assert( rs->sr_ctrls != NULL );
-               ldap_controls_free( ctrls );
-       }
-
-       rs->sr_text = save_text;
-       rs->sr_matched = save_matched;
-       rs->sr_ref = save_ref;
-       rs->sr_ctrls = save_ctrls;
-
-       return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
-}
-
-/*
- * meta_back_proxy_authz_cred()
+ * asyncmeta_back_proxy_authz_cred()
  *
  * prepares credentials & method for meta_back_proxy_authz_bind();
  * or, if method is SASL, performs the SASL bind directly.
@@ -1085,7 +631,8 @@ asyncmeta_back_proxy_authz_cred(
        a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
        struct berval           ndn;
        int                     dobind = 0;
-
+       struct timeval old_tv = {0, 0};
+       struct timeval bind_tv = { mt->mt_timeout[ SLAP_OP_BIND ], 0};
        /* don't proxyAuthz if protocol is not LDAPv3 */
        switch ( mt->mt_version ) {
        case LDAP_VERSION3:
@@ -1260,6 +807,22 @@ asyncmeta_back_proxy_authz_cred(
                        }
                }
 
+               ldap_get_option( msc->msc_ld, LDAP_OPT_TIMEOUT, (void *)&old_tv);
+
+               if (mt->mt_timeout[ SLAP_OP_BIND ] > 0 ) {
+                       rs->sr_err = ldap_set_option( msc->msc_ld,
+                               LDAP_OPT_TIMEOUT,
+                               (void *)&bind_tv );
+
+                       if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
+                               rs->sr_err = LDAP_OTHER;
+                               if ( sendok & LDAP_BACK_SENDERR ) {
+                                       send_ldap_result( op, rs );
+                               }
+                               LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+                               goto done;
+                       }
+               }
                defaults = lutil_sasl_defaults( msc->msc_ld,
                                mt->mt_idassert_sasl_mech.bv_val,
                                mt->mt_idassert_sasl_realm.bv_val,
@@ -1280,8 +843,17 @@ asyncmeta_back_proxy_authz_cred(
                                LDAP_SASL_QUIET, lutil_sasl_interact,
                                defaults );
 
+               /* restore the old timeout just in case */
+               ldap_set_option( msc->msc_ld, LDAP_OPT_TIMEOUT, (void *)&old_tv );
+
                rs->sr_err = slap_map_api2result( rs );
                if ( rs->sr_err != LDAP_SUCCESS ) {
+                       if ( LogTest( asyncmeta_debug ) ) {
+                               char    time_buf[ SLAP_TEXT_BUFLEN ];
+                               asyncmeta_get_timestamp(time_buf);
+                               Debug( asyncmeta_debug, "[%s] asyncmeta_back_proxy_authz_cred failed bind msc: %p\n",
+                                     time_buf, msc );
+                       }
                        LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
                        if ( sendok & LDAP_BACK_SENDERR ) {
                                send_ldap_result( op, rs );
@@ -1391,160 +963,297 @@ asyncmeta_proxy_authz_bind(
        return LDAP_BACK_CONN_ISBOUND( msc );
 }
 
-/*
- * Add controls;
- *
- * if any needs to be added, it is prepended to existing ones,
- * in a newly allocated array.  The companion function
- * mi->mi_ldap_extra->controls_free() must be used to restore the original
- * status of op->o_ctrls.
- */
-int
-asyncmeta_controls_add(
-               Operation       *op,
-               SlapReply       *rs,
-               a_metaconn_t    *mc,
-               int             candidate,
-               LDAPControl     ***pctrls )
+
+static int
+asyncmeta_back_proxy_authz_ctrl(Operation *op,
+                               SlapReply *rs,
+                               struct berval   *bound_ndn,
+                               int             version,
+                               int             isroot,
+                               slap_idassert_t *si,
+                               LDAPControl     *ctrl )
 {
-       a_metainfo_t            *mi = mc->mc_info;
-       a_metatarget_t          *mt = mi->mi_targets[ candidate ];
-       a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
+       slap_idassert_mode_t    mode;
+       struct berval           assertedID,
+                               ndn;
+
+       rs->sr_err = SLAP_CB_CONTINUE;
+
+       /* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
+        * but if it is not set this test fails.  We need a different
+        * means to detect if idassert is enabled */
+       if ( ( BER_BVISNULL( &si->si_bc.sb_authcId ) || BER_BVISEMPTY( &si->si_bc.sb_authcId ) )
+               && ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) )
+               && BER_BVISNULL( &si->si_bc.sb_saslmech ) )
+       {
+               goto done;
+       }
 
-       LDAPControl             **ctrls = NULL;
-       /* set to the maximum number of controls this backend can add */
-       LDAPControl             c[ 2 ] = {{ 0 }};
-       int                     n = 0, i, j1 = 0, j2 = 0;
+       if ( !op->o_conn || op->o_do_not_cache || ( isroot ) ) {
+               goto done;
+       }
 
-       *pctrls = NULL;
+       if ( op->o_tag == LDAP_REQ_BIND ) {
+               ndn = op->o_req_ndn;
 
-       rs->sr_err = LDAP_SUCCESS;
+#if 0
+       } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+               ndn = op->o_conn->c_ndn;
+#endif
+       } else {
+               ndn = op->o_ndn;
+       }
 
-       /* don't add controls if protocol is not LDAPv3 */
-       switch ( mt->mt_version ) {
-       case LDAP_VERSION3:
-               break;
+       if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) {
+               if ( op->o_proxy_authz ) {
+                       /*
+                        * FIXME: we do not want to perform proxyAuthz
+                        * on behalf of the client, because this would
+                        * be performed with "proxyauthzdn" privileges.
+                        *
+                        * This might actually be too strict, since
+                        * the "proxyauthzdn" authzTo, and each entry's
+                        * authzFrom attributes may be crafted
+                        * to avoid unwanted proxyAuthz to take place.
+                        */
+#if 0
+                       rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+                       rs->sr_text = "proxyAuthz not allowed within namingContext";
+#endif
+                       goto done;
+               }
 
-       case 0:
-               if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
-                       break;
+               if ( !BER_BVISNULL( bound_ndn ) ) {
+                       goto done;
                }
-               /* fall thru */
 
-       default:
-               goto done;
+               if ( BER_BVISNULL( &ndn ) ) {
+                       goto done;
+               }
+
+               if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) {
+                       goto done;
+               }
+
+       } else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
+               if ( ( si->si_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+               {
+                       /* already asserted in SASL via native authz */
+                       goto done;
+               }
+
+       } else if ( si->si_authz && !isroot ) {
+               int             rc;
+               struct berval authcDN;
+
+               if ( BER_BVISNULL( &ndn ) ) {
+                       authcDN = slap_empty_bv;
+               } else {
+                       authcDN = ndn;
+               }
+               rc = slap_sasl_matches( op, si->si_authz,
+                               &authcDN, &authcDN );
+               if ( rc != LDAP_SUCCESS ) {
+                       if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+                               /* ndn is not authorized
+                                * to use idassert */
+                               rs->sr_err = rc;
+                       }
+                       goto done;
+               }
        }
 
-       /* put controls that go __before__ existing ones here */
+       if ( op->o_proxy_authz ) {
+               /*
+                * FIXME: we can:
+                * 1) ignore the already set proxyAuthz control
+                * 2) leave it in place, and don't set ours
+                * 3) add both
+                * 4) reject the operation
+                *
+                * option (4) is very drastic
+                * option (3) will make the remote server reject
+                * the operation, thus being equivalent to (4)
+                * option (2) will likely break the idassert
+                * assumptions, so we cannot accept it;
+                * option (1) means that we are contradicting
+                * the client's reques.
+                *
+                * I think (4) is the only correct choice.
+                */
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_text = "proxyAuthz not allowed within namingContext";
+       }
 
-       /* proxyAuthz for identity assertion */
-       switch ( mi->mi_ldap_extra->proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
-               mt->mt_version, &mt->mt_idassert, &c[ j1 ] ) )
-       {
-       case SLAP_CB_CONTINUE:
+       if ( op->o_is_auth_check ) {
+               mode = LDAP_BACK_IDASSERT_NOASSERT;
+
+       } else {
+               mode = si->si_mode;
+       }
+
+       switch ( mode ) {
+       case LDAP_BACK_IDASSERT_LEGACY:
+               /* original behavior:
+                * assert the client's identity */
+       case LDAP_BACK_IDASSERT_SELF:
+               assertedID = ndn;
                break;
 
-       case LDAP_SUCCESS:
-               j1++;
+       case LDAP_BACK_IDASSERT_ANONYMOUS:
+               /* assert "anonymous" */
+               assertedID = slap_empty_bv;
+               break;
+
+       case LDAP_BACK_IDASSERT_NOASSERT:
+               /* don't assert; bind as proxyauthzdn */
+               goto done;
+
+       case LDAP_BACK_IDASSERT_OTHERID:
+       case LDAP_BACK_IDASSERT_OTHERDN:
+               /* assert idassert DN */
+               assertedID = si->si_bc.sb_authzId;
                break;
 
        default:
+               assert( 0 );
+       }
+
+       /* if we got here, "" is allowed to proxyAuthz */
+       if ( BER_BVISNULL( &assertedID ) ) {
+               assertedID = slap_empty_bv;
+       }
+
+       /* don't idassert the bound DN (ITS#4497) */
+       if ( dn_match( &assertedID, bound_ndn ) ) {
                goto done;
        }
 
-       /* put controls that go __after__ existing ones here */
+       ctrl->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+       ctrl->ldctl_iscritical = ( ( si->si_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) == LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL );
 
-#ifdef SLAP_CONTROL_X_SESSION_TRACKING
-       /* session tracking */
-       if ( META_BACK_TGT_ST_REQUEST( mt ) ) {
-               switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
-               case SLAP_CB_CONTINUE:
-                       break;
+       switch ( si->si_mode ) {
+       /* already in u:ID or dn:DN form */
+       case LDAP_BACK_IDASSERT_OTHERID:
+       case LDAP_BACK_IDASSERT_OTHERDN:
+               ber_dupbv_x( &ctrl->ldctl_value, &assertedID, op->o_tmpmemctx );
+               rs->sr_err = LDAP_SUCCESS;
+               break;
 
-               case LDAP_SUCCESS:
-                       j2++;
-                       break;
+       /* needs the dn: prefix */
+       default:
+               ctrl->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
+               ctrl->ldctl_value.bv_val = op->o_tmpalloc( ctrl->ldctl_value.bv_len + 1,
+                               op->o_tmpmemctx );
+               AC_MEMCPY( ctrl->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
+               AC_MEMCPY( &ctrl->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
+                               assertedID.bv_val, assertedID.bv_len + 1 );
+               rs->sr_err = LDAP_SUCCESS;
+               break;
+       }
 
-               default:
-                       goto done;
+       /* Older versions of <draft-weltman-ldapv3-proxy> required
+        * to encode the value of the authzID (and called it proxyDN);
+        * this hack provides compatibility with those DSAs that
+        * implement it this way */
+       if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+               struct berval           authzID = ctrl->ldctl_value;
+               BerElementBuffer        berbuf;
+               BerElement              *ber = (BerElement *)&berbuf;
+               ber_tag_t               tag;
+
+               ber_init2( ber, 0, LBER_USE_DER );
+               ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+               tag = ber_printf( ber, "O", &authzID );
+               if ( tag == LBER_ERROR ) {
+                       rs->sr_err = LDAP_OTHER;
+                       goto free_ber;
+               }
+
+               if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
+                       rs->sr_err = LDAP_OTHER;
+                       goto free_ber;
                }
-       }
-#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
 
-       if ( rs->sr_err == SLAP_CB_CONTINUE ) {
                rs->sr_err = LDAP_SUCCESS;
-       }
 
-       /* if nothing to do, just bail out */
-       if ( j1 == 0 && j2 == 0 ) {
-               goto done;
-       }
+free_ber:;
+               op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+               ber_free_buf( ber );
 
-       assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       goto done;
+               }
 
-       if ( op->o_ctrls ) {
-               for ( n = 0; op->o_ctrls[ n ]; n++ )
-                       /* just count ctrls */ ;
-       }
+       } else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+               struct berval           authzID = ctrl->ldctl_value,
+                                       tmp;
+               BerElementBuffer        berbuf;
+               BerElement              *ber = (BerElement *)&berbuf;
+               ber_tag_t               tag;
 
-       ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
-                       op->o_tmpmemctx );
-       if ( j1 ) {
-               ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
-               *ctrls[ 0 ] = c[ 0 ];
-               for ( i = 1; i < j1; i++ ) {
-                       ctrls[ i ] = &ctrls[ 0 ][ i ];
-                       *ctrls[ i ] = c[ i ];
+               if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) {
+                       rs->sr_err = LDAP_PROTOCOL_ERROR;
+                       goto done;
                }
-       }
 
-       i = 0;
-       if ( op->o_ctrls ) {
-               for ( i = 0; op->o_ctrls[ i ]; i++ ) {
-                       ctrls[ i + j1 ] = op->o_ctrls[ i ];
+               tmp = authzID;
+               tmp.bv_val += STRLENOF( "dn:" );
+               tmp.bv_len -= STRLENOF( "dn:" );
+
+               ber_init2( ber, 0, LBER_USE_DER );
+               ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+               /* apparently, Mozilla API encodes this
+                * as "SEQUENCE { LDAPDN }" */
+               tag = ber_printf( ber, "{O}", &tmp );
+               if ( tag == LBER_ERROR ) {
+                       rs->sr_err = LDAP_OTHER;
+                       goto free_ber2;
                }
-       }
 
-       n += j1;
-       if ( j2 ) {
-               ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
-               *ctrls[ n ] = c[ j1 ];
-               for ( i = 1; i < j2; i++ ) {
-                       ctrls[ n + i ] = &ctrls[ n ][ i ];
-                       *ctrls[ n + i ] = c[ i ];
+               if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
+                       rs->sr_err = LDAP_OTHER;
+                       goto free_ber2;
                }
-       }
 
-       ctrls[ n + j2 ] = NULL;
+               ctrl->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
+               rs->sr_err = LDAP_SUCCESS;
 
-done:;
-       if ( ctrls == NULL ) {
-               ctrls = op->o_ctrls;
+free_ber2:;
+               op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+               ber_free_buf( ber );
+
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       goto done;
+               }
        }
 
-       *pctrls = ctrls;
+done:;
 
        return rs->sr_err;
 }
 
-#if 0
 /*
  * Add controls;
  *
- * same as asyncmeta_controls_add, but creates a new controls array
- * to be used by the operation copy
+ * if any needs to be added, it is prepended to existing ones,
+ * in a newly allocated array.  The companion function
+ * mi->mi_ldap_extra->controls_free() must be used to restore the original
+ * status of op->o_ctrls.
  */
 int
-asyncmeta_controls_add_copy(
-               Operation       *op,
-               SlapReply       *rs,
-               a_metaconn_t    *mc,
-               int             candidate,
-               LDAPControl     ***pctrls )
+asyncmeta_controls_add( Operation *op,
+                       SlapReply *rs,
+                       a_metaconn_t    *mc,
+                       int             candidate,
+                       int             isroot,
+                       LDAPControl     ***pctrls )
 {
-       a_metainfo_t            *mi = (a_metainfo_t *)op->o_bd->be_private;
+       a_metainfo_t            *mi = mc->mc_info;
        a_metatarget_t          *mt = mi->mi_targets[ candidate ];
-       a_metasingleconn_t      *msc = mc->mc_conns[ candidate ];
+       a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
 
        LDAPControl             **ctrls = NULL;
        /* set to the maximum number of controls this backend can add */
@@ -1573,8 +1282,8 @@ asyncmeta_controls_add_copy(
        /* put controls that go __before__ existing ones here */
 
        /* proxyAuthz for identity assertion */
-       switch ( mi->mi_ldap_extra->proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
-               mt->mt_version, &mt->mt_idassert, &c[ j1 ] ) )
+       switch ( asyncmeta_back_proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
+                                            mt->mt_version, isroot, &mt->mt_idassert, &c[ j1 ] ) )
        {
        case SLAP_CB_CONTINUE:
                break;
@@ -1601,7 +1310,7 @@ asyncmeta_controls_add_copy(
                        break;
 
                default:
-                       goto copy;
+                       goto done;
                }
        }
 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
@@ -1610,13 +1319,20 @@ asyncmeta_controls_add_copy(
                rs->sr_err = LDAP_SUCCESS;
        }
 
-copy:
+       /* if nothing to do, just bail out */
+       if ( j1 == 0 && j2 == 0 ) {
+               goto done;
+       }
+
+       assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
+
        if ( op->o_ctrls ) {
                for ( n = 0; op->o_ctrls[ n ]; n++ )
                        /* just count ctrls */ ;
        }
 
-       ctrls = ch_calloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ));
+       ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
+                       op->o_tmpmemctx );
        if ( j1 ) {
                ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
                *ctrls[ 0 ] = c[ 0 ];
@@ -1646,12 +1362,15 @@ copy:
        ctrls[ n + j2 ] = NULL;
 
 done:;
+       if ( ctrls == NULL ) {
+               ctrls = op->o_ctrls;
+       }
 
-       op->o_ctrls = ctrls;
+       *pctrls = ctrls;
 
        return rs->sr_err;
 }
-#endif
+
 
 /*
  * asyncmeta_dobind_init()
@@ -1665,7 +1384,6 @@ asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn
        a_metainfo_t            *mi = ( a_metainfo_t * )mc->mc_info;
        a_metatarget_t          *mt = mi->mi_targets[ candidate ];
        a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
-       ber_socket_t s;
        struct berval           binddn = msc->msc_bound_ndn,
                                cred = msc->msc_cred;
        int                     method;
@@ -1675,13 +1393,18 @@ asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn
 
        meta_search_candidate_t retcode;
 
-       Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_search_dobind_init[%d]\n",
+       Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_dobind_init[%d]\n",
                op->o_log_prefix, candidate );
 
        if ( mc->mc_authz_target == META_BOUND_ALL ) {
                return META_SEARCH_CANDIDATE;
        }
 
+       if ( slapd_shutdown ) {
+               rs->sr_err = LDAP_UNAVAILABLE;
+               return META_SEARCH_ERR;
+       }
+
        retcode = META_SEARCH_BINDING;
        if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
                /* already bound (or anonymous) */
@@ -1694,11 +1417,11 @@ asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn
                        bound = 1;
                }
 
-               Debug(LDAP_DEBUG_ANY,
-                     "### %s asyncmeta_search_dobind_init[%d] mc=%p ld=%p%s DN=\"%s\"\n",
-                     op->o_log_prefix, candidate, (void *)mc,
-                     (void *)msc->msc_ld, bound ? " bound" : " anonymous",
-                     bound == 0 ? "" : msc->msc_bound_ndn.bv_val );
+               Debug( LDAP_DEBUG_ANY,
+                      "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p%s DN=\"%s\"\n",
+                      op->o_log_prefix, candidate, (void *)mc,
+                      (void *)msc->msc_ld, bound ? " bound" : " anonymous",
+                      bound == 0 ? "" : msc->msc_bound_ndn.bv_val );
 #endif /* DEBUG_205 */
 
                retcode = META_SEARCH_CANDIDATE;
@@ -1707,27 +1430,24 @@ asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn
                /* another thread is binding the target for this conn; wait */
 
 #ifdef DEBUG_205
-               char    buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
 
-               Debug(LDAP_DEBUG_ANY,
-                     "### %s asyncmeta_search_dobind_init[%d] mc=%p ld=%p needbind\n",
-                     op->o_log_prefix, candidate, (void *)mc,
-                     (void *)msc->msc_ld );
+               Debug( LDAP_DEBUG_ANY,
+                      "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p needbind\n",
+                      op->o_log_prefix, candidate, (void *)mc,
+                      (void *)msc->msc_ld );
 #endif /* DEBUG_205 */
 
                candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
                retcode = META_SEARCH_NEED_BIND;
-
        } else {
                /* we'll need to bind the target for this conn */
 
 #ifdef DEBUG_205
-               char buf[ SLAP_TEXT_BUFLEN ];
 
-               Debug(LDAP_DEBUG_ANY,
-                     "### %s asyncmeta_search_dobind_init[%d] mc=%p ld=%p binding\n",
-                     op->o_log_prefix, candidate, (void *)mc,
-                     (void *)msc->msc_ld );
+               Debug( LDAP_DEBUG_ANY,
+                      "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p binding\n",
+                      op->o_log_prefix, candidate, (void *)mc,
+                      (void *)msc->msc_ld );
 #endif /* DEBUG_205 */
 
                if ( msc->msc_ld == NULL ) {
@@ -1735,11 +1455,12 @@ asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn
                         * state, with eventual connection expiration or invalidation)
                         * it was not initialized as expected */
 
-                       Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p ld=NULL\n",
+                       Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p ld=NULL\n",
                                op->o_log_prefix, candidate, (void *)mc );
 
                        rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
-                               LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
+                                                     LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
+
                        switch ( rc ) {
                        case LDAP_SUCCESS:
                                assert( msc->msc_ld != NULL );
@@ -1780,8 +1501,6 @@ asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn
                /* NOTE: we copy things here, even if bind didn't succeed yet,
                 * because the connection is not shared until bind is over */
                if ( !BER_BVISNULL( &binddn ) ) {
-                       ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);
-
                        ber_bvreplace( &msc->msc_bound_ndn, &binddn );
                        if ( META_BACK_TGT_SAVECRED( mt ) && !BER_BVISNULL( &cred ) ) {
                                if ( !BER_BVISNULL( &msc->msc_cred ) ) {
@@ -1790,7 +1509,6 @@ asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn
                                }
                                ber_bvreplace( &msc->msc_cred, &cred );
                        }
-                       ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
                }
                if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
                        /* apparently, idassert was configured with SASL bind,
@@ -1816,41 +1534,67 @@ asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn
 
        if ( !BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &cred ) ) {
                /* bind anonymously? */
-               Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p: "
+               Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p: "
                        "non-empty dn with empty cred; binding anonymously\n",
                        op->o_log_prefix, candidate, (void *)mc );
                cred = slap_empty_bv;
 
        } else if ( BER_BVISEMPTY( &binddn ) && !BER_BVISEMPTY( &cred ) ) {
                /* error */
-               Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p: "
+               Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p: "
                        "empty dn with non-empty cred: error\n",
                        op->o_log_prefix, candidate, (void *)mc );
                rc = LDAP_OTHER;
                goto other;
        }
 retry_bind:
+       if ( LogTest( asyncmeta_debug ) ) {
+               char    time_buf[ SLAP_TEXT_BUFLEN ];
+               asyncmeta_get_timestamp(time_buf);
+               Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init sending bind msc: %p\n",
+                     time_buf, msc );
+       }
        rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred,
                        NULL, NULL, &msgid );
        ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rc );
+       if ( LogTest( asyncmeta_debug ) ) {
+               char    time_buf[ SLAP_TEXT_BUFLEN ];
+               asyncmeta_get_timestamp(time_buf);
+               Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init rc=%d msc: %p\n",
+                     time_buf, rc, msc );
+       }
 
        if (rc == LDAP_SERVER_DOWN ) {
                goto down;
+       } else if (rc == LDAP_BUSY) {
+               if (rs->sr_text == NULL) {
+                       rs->sr_text = "Unable to establish LDAP connection to target within the specified network timeout.";
+               }
+               LDAP_BACK_CONN_BINDING_CLEAR( msc );
+               goto other;
        }
-       candidates[ candidate ].sr_msgid = msgid;
+       /* mark as need bind so it gets send when the bind response is received */
+       candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
        asyncmeta_set_msc_time(msc);
 #ifdef DEBUG_205
-       Debug(LDAP_DEBUG_ANY,
-             "### %s asyncmeta_search_dobind_init[%d] mc=%p ld=%p rc=%d\n",
-             op->o_log_prefix, candidate, (void *)mc,
-             (void *)mc->mc_conns[candidate].msc_ld, rc );
+       Debug( LDAP_DEBUG_ANY,
+              "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p rc=%d\n",
+              op->o_log_prefix, candidate, (void *)mc,
+              (void *)mc->mc_conns[candidate].msc_ld, rc );
 #endif /* DEBUG_205 */
 
        switch ( rc ) {
        case LDAP_SUCCESS:
                assert( msgid >= 0 );
+               if ( LogTest( asyncmeta_debug ) ) {
+                       char    time_buf[ SLAP_TEXT_BUFLEN ];
+                       asyncmeta_get_timestamp(time_buf);
+                       Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init sending bind success msc: %p\n",
+                             time_buf, msc );
+               }
                META_BINDING_SET( &candidates[ candidate ] );
                rs->sr_err = LDAP_SUCCESS;
+               msc->msc_binding_time = slap_get_time();
                return META_SEARCH_BINDING;
 
        case LDAP_X_CONNECTING:
@@ -1863,7 +1607,11 @@ retry_bind:
 down:;
                retcode = META_SEARCH_ERR;
                rs->sr_err = LDAP_UNAVAILABLE;
+               if (rs->sr_text == NULL) {
+                       rs->sr_text = "Unable to bind to remote target - target down or unavailable";
+               }
                candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+                LDAP_BACK_CONN_BINDING_CLEAR( msc );
                break;
 
                /* fall thru */
@@ -1880,6 +1628,7 @@ other:;
                        retcode = META_SEARCH_NOT_CANDIDATE;
                }
                candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+                LDAP_BACK_CONN_BINDING_CLEAR( msc );
                break;
        }
 
@@ -1893,57 +1642,63 @@ meta_search_candidate_t
 asyncmeta_dobind_init_with_retry(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate)
 {
 
-       int rc, retries = 1;
+       int rc;
        a_metasingleconn_t *msc = &mc->mc_conns[candidate];
        a_metainfo_t            *mi = mc->mc_info;
        a_metatarget_t          *mt = mi->mi_targets[ candidate ];
-       SlapReply               *candidates = bc->candidates;
+
+       if (META_BACK_CONN_INVALID(msc) || (LDAP_BACK_CONN_BINDING( msc ) && msc->msc_binding_time > 0
+                                           && (msc->msc_binding_time + mt->mt_timeout[ SLAP_OP_BIND ]) < slap_get_time())) {
+               char    buf[ SLAP_TEXT_BUFLEN ];
+               snprintf( buf, sizeof( buf ), "called from %s:%d", __FILE__, __LINE__ );
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+               asyncmeta_reset_msc(NULL, mc, candidate, 0, buf);
+
+               rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
+                                             LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+       }
+
+       if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
+               return META_SEARCH_CANDIDATE;
+       }
 
 retry_dobind:
+       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
        rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
-       if (rs->sr_err != LDAP_UNAVAILABLE) {
+       if (rs->sr_err != LDAP_UNAVAILABLE && rs->sr_err != LDAP_BUSY) {
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
                return rc;
-       } else if (retries <= 0) {
-               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-               if (mc->mc_active < 1) {
-                       asyncmeta_clear_one_msc(NULL, mc, candidate);
-               }
+       } else if (bc->nretries[candidate] == 0) {
+               char    buf[ SLAP_TEXT_BUFLEN ];
+               snprintf( buf, sizeof( buf ), "called from %s:%d", __FILE__, __LINE__ );
+               asyncmeta_reset_msc(NULL, mc, candidate, 0, buf);
                ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
                return rc;
        }
        /* need to retry */
-       retries--;
-       if ( LogTest( LDAP_DEBUG_ANY ) ) {
+       bc->nretries[candidate]--;
+       if ( LogTest( LDAP_DEBUG_TRACE ) ) {
                /* this lock is required; however,
                 * it's invoked only when logging is on */
                ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
-               Debug(LDAP_DEBUG_ANY,
-                     "%s asyncmeta_search_dobind_init_with_retry[%d]: retrying URI=\"%s\" DN=\"%s\".\n",
-                     op->o_log_prefix, candidate, mt->mt_uri,
-                     BER_BVISNULL(&msc->msc_bound_ndn) ? "" : msc->msc_bound_ndn.bv_val );
+               Debug( LDAP_DEBUG_ANY,
+                      "%s asyncmeta_dobind_init_with_retry[%d]: retrying URI=\"%s\" DN=\"%s\".\n",
+                      op->o_log_prefix, candidate, mt->mt_uri,
+                      BER_BVISNULL(&msc->msc_bound_ndn) ? "" : msc->msc_bound_ndn.bv_val );
                ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
        }
 
-       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-       if (mc->mc_active < 1) {
-               asyncmeta_clear_one_msc(NULL, mc, candidate);
-       }
-       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
-
-       ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
-
+       asyncmeta_reset_msc(NULL, mc, candidate, 0,  __FUNCTION__);
        rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
                                      LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
 
        if (rs->sr_err != LDAP_SUCCESS) {
-               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-               if (mc->mc_active < 1) {
-                       asyncmeta_clear_one_msc(NULL, mc, candidate);
-               }
+               asyncmeta_reset_msc(NULL, mc, candidate, 0,  __FUNCTION__);
                ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
                return META_SEARCH_ERR;
        }
-
+       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
        goto retry_dobind;
        return rc;
 }
index b25832a7ac8866dfe1cf97a77a22f0de5b9ba037..1ab0639bd58844c480362cccb43be7fc8be509ee 100644 (file)
@@ -206,8 +206,6 @@ asyncmeta_select_unique_candidate(
                        if ( candidate == META_TARGET_NONE ) {
                                candidate = i;
 
-                       } else {
-                               return META_TARGET_MULTIPLE;
                        }
                }
        }
@@ -239,50 +237,3 @@ asyncmeta_clear_unused_candidates(
 
        return 0;
 }
-
-int
-asyncmeta_clear_one_msc(
-       Operation       *op,
-       a_metaconn_t    *mc,
-       int             candidate )
-{
-       a_metasingleconn_t *msc;
-       if (mc == NULL) {
-               return 0;
-       }
-       msc = &mc->mc_conns[candidate];
-       if (msc->conn) {
-               connection_client_stop( msc->conn );
-               msc->conn = NULL;
-       }
-       if ( msc->msc_ld != NULL ) {
-
-#ifdef DEBUG_205
-               Debug(LDAP_DEBUG_ANY,
-                     "### %s asyncmeta_clear_one_msc ldap_unbind_ext[%d] ld=%p\n",
-                     op ? op->o_log_prefix : "", candidate,
-                     (void *)msc->msc_ld );
-#endif /* DEBUG_205 */
-
-               ldap_unbind_ext( msc->msc_ld, NULL, NULL );
-               msc->msc_ld = NULL;
-               ldap_ld_free( msc->msc_ldr, 0, NULL, NULL );
-               msc->msc_ldr = NULL;
-       }
-
-       if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
-               ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
-               BER_BVZERO( &msc->msc_bound_ndn );
-       }
-
-       if ( !BER_BVISNULL( &msc->msc_cred ) ) {
-               memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
-               ber_memfree_x( msc->msc_cred.bv_val, NULL );
-               BER_BVZERO( &msc->msc_cred );
-       }
-       msc->msc_time = 0;
-       msc->msc_mscflags = 0;
-       msc->msc_timeout_ops = 0;
-       msc->msc_pending_ops = 0;
-       return 0;
-}
index d511103b53deeb9a8253c372d2492a34622fe672..8c77a3d506fa926946f5ea01e3b0f77878dcadbc 100644 (file)
 
 #include <ac/string.h>
 #include <ac/socket.h>
-
 #include "slap.h"
-#include "../back-ldap/back-ldap.h"
-#include "back-asyncmeta.h"
 #include "../../../libraries/liblber/lber-int.h"
 #include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
 
 meta_search_candidate_t
 asyncmeta_back_compare_start(Operation *op,
-                           SlapReply *rs,
-                           a_metaconn_t *mc,
-                           bm_context_t *bc,
-                           int candidate)
+                            SlapReply *rs,
+                            a_metaconn_t *mc,
+                            bm_context_t *bc,
+                            int candidate,
+                            int do_lock)
 {
        a_dncookie      dc;
        a_metainfo_t    *mi = mc->mc_info;
        a_metatarget_t  *mt = mi->mi_targets[ candidate ];
+       struct berval   c_attr = op->orc_ava->aa_desc->ad_cname;
        struct berval   mdn = BER_BVNULL;
-       struct berval   mapped_attr = op->orc_ava->aa_desc->ad_cname;
        struct berval   mapped_value = op->orc_ava->aa_value;
-       int rc = 0, nretries = 1;
+       int rc = 0;
        LDAPControl     **ctrls = NULL;
        meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
        BerElement *ber = NULL;
@@ -54,117 +54,125 @@ asyncmeta_back_compare_start(Operation *op,
        SlapReply               *candidates = bc->candidates;
        ber_int_t       msgid;
 
+       dc.op = op;
        dc.target = mt;
-       dc.conn = op->o_conn;
-       dc.rs = rs;
-       dc.ctx = "compareDN";
+       dc.memctx = op->o_tmpmemctx;
+       dc.to_from = MASSAGE_REQ;
 
-       switch ( asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
-       case LDAP_UNWILLING_TO_PERFORM:
-               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-               retcode = META_SEARCH_ERR;
-               goto doreturn;
-       default:
-               break;
-       }
+       asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
 
-       /*
-        * if attr is objectClass, try to remap the value
-        */
-       if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass ) {
-               asyncmeta_map( &mt->mt_rwmap.rwm_oc,
-                               &op->orc_ava->aa_value,
-                               &mapped_value, BACKLDAP_MAP );
-
-               if ( BER_BVISNULL( &mapped_value ) || BER_BVISEMPTY( &mapped_value ) ) {
-                       rs->sr_err = LDAP_OTHER;
-                       retcode = META_SEARCH_ERR;
-                       goto done;
-               }
-
-       /*
-        * else try to remap the attribute
-        */
-       } else {
-               asyncmeta_map( &mt->mt_rwmap.rwm_at,
-                       &op->orc_ava->aa_desc->ad_cname,
-                       &mapped_attr, BACKLDAP_MAP );
-               if ( BER_BVISNULL( &mapped_attr ) || BER_BVISEMPTY( &mapped_attr ) ) {
-                       rs->sr_err = LDAP_OTHER;
-                       retcode = META_SEARCH_ERR;
-                       goto done;
-               }
+       if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+               asyncmeta_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value );
 
-               if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
-               {
-                       dc.ctx = "compareAttrDN";
-
-                       switch ( asyncmeta_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value ) )
-                       {
-                       case LDAP_UNWILLING_TO_PERFORM:
-                               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-                               retcode = META_SEARCH_ERR;
-                               goto done;
-
-                       default:
-                               break;
-                       }
-               }
-       }
-retry:;
+       asyncmeta_set_msc_time(msc);
        ctrls = op->o_ctrls;
-       if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
+       if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root,&ctrls ) != LDAP_SUCCESS )
        {
                candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
                retcode = META_SEARCH_ERR;
                goto done;
        }
+       /* someone might have reset the connection */
+       if (!( LDAP_BACK_CONN_ISBOUND( msc )
+              || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+               Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+               goto error_unavailable;
+       }
 
-       ber = ldap_build_compare_req( msc->msc_ld, mdn.bv_val, mapped_attr.bv_val, &mapped_value,
+       ber = ldap_build_compare_req( msc->msc_ld, mdn.bv_val, c_attr.bv_val, &mapped_value,
                        ctrls, NULL, &msgid);
+
+       if (!ber) {
+               Debug( asyncmeta_debug, "%s asyncmeta_back_compare_start: Operation encoding failed with errno %d\n",
+                      op->o_log_prefix, msc->msc_ld->ld_errno );
+               rs->sr_err = LDAP_OPERATIONS_ERROR;
+               rs->sr_text = "Failed to encode proxied request";
+               retcode = META_SEARCH_ERR;
+               goto done;
+       }
+
        if (ber) {
-               candidates[ candidate ].sr_msgid = msgid;
-               rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_COMPARE,
-                                               mdn.bv_val, ber, msgid );
-               if (rc == msgid)
-                       rc = LDAP_SUCCESS;
-               else
-                       rc = LDAP_SERVER_DOWN;
+               struct timeval tv = {0, mt->mt_network_timeout*1000};
+               ber_socket_t s;
+               if (!( LDAP_BACK_CONN_ISBOUND( msc )
+                      || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+               if (s < 0) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+               rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+               if (rc < 0) {
+                       Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+                       if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+                               rc = LDAP_SERVER_DOWN;
+                       } else {
+                               goto error_unavailable;
+                       }
+               } else {
+                       candidates[ candidate ].sr_msgid = msgid;
+                       rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_COMPARE,
+                                                       mdn.bv_val, ber, msgid );
+                       if (rc == msgid)
+                               rc = LDAP_SUCCESS;
+                       else
+                               rc = LDAP_SERVER_DOWN;
+                       ber = NULL;
+               }
 
                switch ( rc ) {
                case LDAP_SUCCESS:
                        retcode = META_SEARCH_CANDIDATE;
                        asyncmeta_set_msc_time(msc);
-                       break;
+                       goto done;
 
                case LDAP_SERVER_DOWN:
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_clear_one_msc(NULL, mc, candidate);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) {
-                               nretries = 0;
-                               /* if the identity changed, there might be need to re-authz */
-                               (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
-                               goto retry;
+                       /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+                       if (do_lock > 0) {
+                               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+                               asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+                               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
                        }
-
+                       /* fall though*/
                default:
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       retcode = META_SEARCH_ERR;
+                       Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
                }
        }
+
+error_unavailable:
+       if (ber)
+               ber_free(ber, 1);
+       switch (bc->nretries[candidate]) {
+       case -1: /* nretries = forever */
+               retcode = META_SEARCH_NEED_BIND;
+               break;
+       case 0: /* no retries left */
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               rs->sr_err = LDAP_UNAVAILABLE;
+               rs->sr_text = "Unable to send compare request to target";
+               retcode = META_SEARCH_ERR;
+               break;
+       default: /* more retries left - try to rebind and go again */
+               retcode = META_SEARCH_NEED_BIND;
+               bc->nretries[candidate]--;
+               break;
+       }
 done:
        (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
 
-       if ( mdn.bv_val != op->o_req_dn.bv_val ) {
-               free( mdn.bv_val );
+       if ( op->orc_ava->aa_value.bv_val != mapped_value.bv_val ) {
+               op->o_tmpfree( mapped_value.bv_val, op->o_tmpmemctx );
        }
 
-       if ( op->orc_ava->aa_value.bv_val != mapped_value.bv_val ) {
-               free( mapped_value.bv_val );
+       if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+               op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
        }
 
-doreturn:;
        Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_compare_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
        return retcode;
 }
@@ -176,26 +184,30 @@ asyncmeta_back_compare( Operation *op, SlapReply *rs )
        a_metatarget_t  *mt;
        a_metaconn_t    *mc;
        int             rc, candidate = -1;
-       OperationBuffer opbuf;
+       void *thrctx = op->o_threadctx;
        bm_context_t *bc;
        SlapReply *candidates;
-       slap_callback *cb = op->o_callback;
+       time_t current_time = slap_get_time();
+       int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
 
        Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_compare: %s\n",
              op->o_req_dn.bv_val );
 
-       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
+       if (current_time > op->o_time) {
+               Debug( asyncmeta_debug, "==> asyncmeta_back_compare[%s]: o_time:[%ld], current time: [%ld]\n",
+                      op->o_log_prefix, op->o_time, current_time );
+       }
+       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
        if (bc == NULL) {
                rs->sr_err = LDAP_OTHER;
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
        candidates = bc->candidates;
        mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
        if ( !mc || rs->sr_err != LDAP_SUCCESS) {
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
@@ -204,16 +216,38 @@ asyncmeta_back_compare( Operation *op, SlapReply *rs )
        bc->retrying = LDAP_BACK_RETRYING;
        bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
        bc->stoptime = op->o_time + bc->timeout;
+       bc->bc_active = 1;
+
+       if (mc->pending_ops >= max_pending_ops) {
+               rs->sr_err = LDAP_BUSY;
+               rs->sr_text = "Maximum pending ops limit exceeded";
+               send_ldap_result(op, rs);
+               return rs->sr_err;
+       }
 
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
        rc = asyncmeta_add_message_queue(mc, bc);
+       mc->mc_conns[candidate].msc_active++;
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
 
        if (rc != LDAP_SUCCESS) {
                rs->sr_err = LDAP_BUSY;
                rs->sr_text = "Maximum pending ops limit exceeded";
-               asyncmeta_clear_bm_context(bc);
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+               mc->mc_conns[candidate].msc_active--;
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+               goto finish;
+       }
+
+retry:
+       if (bc->timeout && bc->stoptime < slap_get_time()) {
+               int             timeout_err;
+               timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+                       LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+               rs->sr_err = timeout_err;
+               rs->sr_text = "Operation timed out before it was sent to target";
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
        }
 
@@ -223,47 +257,27 @@ asyncmeta_back_compare( Operation *op, SlapReply *rs )
        case META_SEARCH_CANDIDATE:
                /* target is already bound, just send the request */
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare:  "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
 
-               rc = asyncmeta_back_compare_start( op, rs, mc, bc, candidate);
+               rc = asyncmeta_back_compare_start( op, rs, mc, bc, candidate, 1);
                if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
 
+               } else if (rc == META_SEARCH_NEED_BIND) {
+                       goto retry;
                }
-                       break;
+               break;
        case META_SEARCH_NOT_CANDIDATE:
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: NOT_CANDIDATE "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
-               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-               asyncmeta_drop_bc(mc, bc);
-               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
 
        case META_SEARCH_NEED_BIND:
-       case META_SEARCH_CONNECTING:
-               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: NEED_BIND "
-                      "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
-               rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
-               if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
-                       goto finish;
-               }
-               break;
        case META_SEARCH_BINDING:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: BINDING "
-                              "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+                              "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
                        /* Todo add the context to the message queue but do not send the request
                           the receiver must send this when we are done binding */
                        /* question - how would do receiver know to which targets??? */
@@ -271,23 +285,21 @@ asyncmeta_back_compare( Operation *op, SlapReply *rs )
 
        case META_SEARCH_ERR:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: ERR "
-                              "cnd=\"%ldd\"\n", op->o_log_prefix, candidate );
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       candidates[ candidate ].sr_type = REP_RESULT;
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                              "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
                default:
                        assert( 0 );
                        break;
                }
+
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+       mc->mc_conns[candidate].msc_active--;
        asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+       bc->bc_active--;
+       asyncmeta_memctx_toggle(thrctx);
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+       rs->sr_err = SLAPD_ASYNCOP;
 finish:
        return rs->sr_err;
-
 }
index 9e368d633a5fa3c76070da4a83409728c1482f25..bfa5b7a29f6ac34fe328bed4d61f1c041b58f32a 100644 (file)
@@ -43,11 +43,6 @@ static ConfigDriver asyncmeta_back_cf_gen;
 static ConfigLDAPadd asyncmeta_ldadd;
 static ConfigCfAdd asyncmeta_cfadd;
 
-static int asyncmeta_map_config(
-       ConfigArgs *c,
-       struct ldapmap  *oc_map,
-       struct ldapmap  *at_map );
-
 /* Three sets of enums:
  *     1) attrs that are only valid in the base config
  *     2) attrs that are valid in base or target
@@ -95,9 +90,7 @@ enum {
        LDAP_BACK_CFG_ACL_PASSWD,
        LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
        LDAP_BACK_CFG_IDASSERT_BIND,
-       LDAP_BACK_CFG_REWRITE,
        LDAP_BACK_CFG_SUFFIXM,
-       LDAP_BACK_CFG_MAP,
        LDAP_BACK_CFG_SUBTREE_EX,
        LDAP_BACK_CFG_SUBTREE_IN,
        LDAP_BACK_CFG_KEEPALIVE,
@@ -111,7 +104,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:0.14 "
                        "NAME 'olcDbURI' "
                        "DESC 'URI (list) for remote DSA' "
-                       "EQUALITY caseExactMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -120,7 +112,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.1 "
                        "NAME 'olcDbStartTLS' "
                        "DESC 'StartTLS' "
-                       "EQUALITY caseExactMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -129,7 +120,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.2 "
                        "NAME 'olcDbACLAuthcDn' "
                        "DESC 'Remote ACL administrative identity' "
-                       "EQUALITY distinguishedNameMatch "
                        "OBSOLETE "
                        "SYNTAX OMsDN "
                        "SINGLE-VALUE )",
@@ -156,7 +146,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.7 "
                        "NAME 'olcDbIDAssertBind' "
                        "DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -174,7 +163,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.10 "
                        "NAME 'olcDbRebindAsUser' "
                        "DESC 'Rebind as user' "
-                       "EQUALITY booleanMatch "
                        "SYNTAX OMsBoolean "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -183,7 +171,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.11 "
                        "NAME 'olcDbChaseReferrals' "
                        "DESC 'Chase referrals' "
-                       "EQUALITY booleanMatch "
                        "SYNTAX OMsBoolean "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -192,7 +179,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.12 "
                        "NAME 'olcDbTFSupport' "
                        "DESC 'Absolute filters support' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -201,7 +187,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.14 "
                        "NAME 'olcDbTimeout' "
                        "DESC 'Per-operation timeouts' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -210,7 +195,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.15 "
                        "NAME 'olcDbIdleTimeout' "
                        "DESC 'connection idle timeout' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -219,7 +203,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.17 "
                        "NAME 'olcDbNetworkTimeout' "
                        "DESC 'connection network timeout' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -228,7 +211,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.18 "
                        "NAME 'olcDbProtocolVersion' "
                        "DESC 'protocol version' "
-                       "EQUALITY integerMatch "
                        "SYNTAX OMsInteger "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -238,7 +220,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.20 "
                        "NAME 'olcDbCancel' "
                        "DESC 'abandon/ignore/exop operations when appropriate' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -246,7 +227,6 @@ static ConfigTable a_metacfg[] = {
                ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.21 "
                        "NAME 'olcDbQuarantine' "
-                       "EQUALITY caseIgnoreMatch "
                        "DESC 'Quarantine database if connection fails and retry according to rule' "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
@@ -257,7 +237,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.23 "
                        "NAME 'olcDbConnectionPoolMax' "
                        "DESC 'Max size of privileged connections pool' "
-                       "EQUALITY integerMatch "
                        "SYNTAX OMsInteger "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -267,7 +246,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.24 "
                        "NAME 'olcDbSessionTrackingRequest' "
                        "DESC 'Add session tracking control to proxied requests' "
-                       "EQUALITY booleanMatch "
                        "SYNTAX OMsBoolean "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -277,7 +255,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.25 "
                        "NAME 'olcDbNoRefs' "
                        "DESC 'Do not return search reference responses' "
-                       "EQUALITY booleanMatch "
                        "SYNTAX OMsBoolean "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -286,32 +263,18 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.26 "
                        "NAME 'olcDbNoUndefFilter' "
                        "DESC 'Do not propagate undefined search filters' "
-                       "EQUALITY booleanMatch "
                        "SYNTAX OMsBoolean "
                        "SINGLE-VALUE )",
                NULL, NULL },
 
-       { "rewrite", "arglist", 2, 0, STRLENOF( "rewrite" ),
-               ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
-               asyncmeta_back_cf_gen, "( OLcfgDbAt:3.101 "
-                       "NAME 'olcDbRewrite' "
-                       "DESC 'DN rewriting rules' "
-                       "EQUALITY caseIgnoreMatch "
-                       "SYNTAX OMsDirectoryString "
-                       "X-ORDERED 'VALUES' )",
-               NULL, NULL },
-       { "suffixmassage", "virtual> <real", 2, 3, 0,
+       { "suffixmassage", "local> <remote", 2, 3, 0,
                ARG_MAGIC|LDAP_BACK_CFG_SUFFIXM,
-               asyncmeta_back_cf_gen, NULL, NULL, NULL },
-
-       { "map", "attribute|objectClass> [*|<local>] *|<remote", 3, 4, 0,
-               ARG_MAGIC|LDAP_BACK_CFG_MAP,
-               asyncmeta_back_cf_gen, "( OLcfgDbAt:3.102 "
-                       "NAME 'olcDbMap' "
-                       "DESC 'Map attribute and objectclass names' "
+               asyncmeta_back_cf_gen, "( OLcfgDbAt:3.117 "
+                       "NAME 'olcDbSuffixMassage' "
+                       "DESC 'DN suffix massage' "
                        "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
-                       "X-ORDERED 'VALUES' )",
+                       "SINGLE-VALUE )",
                NULL, NULL },
 
        { "subtree-exclude", "pattern", 2, 2, 0,
@@ -335,7 +298,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.105 "
                        "NAME 'olcDbDefaultTarget' "
                        "DESC 'Specify the default target' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -344,7 +306,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.106 "
                        "NAME 'olcDbDnCacheTtl' "
                        "DESC 'dncache ttl' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -353,8 +314,7 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.107 "
                        "NAME 'olcDbBindTimeout' "
                        "DESC 'bind timeout' "
-                       "EQUALITY integerMatch "
-                       "SYNTAX OMsInteger "
+                       "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
        { "onerr", "CONTINUE|report|stop", 2, 2, 0,
@@ -362,7 +322,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.108 "
                        "NAME 'olcDbOnErr' "
                        "DESC 'error handling' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -371,7 +330,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.109 "
                        "NAME 'olcDbPseudoRootBindDefer' "
                        "DESC 'error handling' "
-                       "EQUALITY booleanMatch "
                        "SYNTAX OMsBoolean "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -383,7 +341,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.110 "
                        "NAME 'olcDbNretries' "
                        "DESC 'retry handling' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -391,7 +348,6 @@ static ConfigTable a_metacfg[] = {
                ARG_MAGIC|LDAP_BACK_CFG_CLIENT_PR,
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.111 "
                        "NAME 'olcDbClientPr' "
-                       "EQUALITY caseIgnoreMatch "
                        "DESC 'PagedResults handling' "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
@@ -409,7 +365,6 @@ static ConfigTable a_metacfg[] = {
                asyncmeta_back_cf_gen, "( OLcfgDbAt:3.29 "
                        "NAME 'olcDbKeepalive' "
                        "DESC 'TCP keepalive' "
-                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
@@ -428,7 +383,6 @@ static ConfigTable a_metacfg[] = {
          asyncmeta_back_cf_gen, "( OLcfgDbAt:3.113 "
          "NAME 'olcDbMaxPendingOps' "
          "DESC 'Maximum number of pending operations' "
-         "EQUALITY integerMatch "
          "SYNTAX OMsInteger "
          "SINGLE-VALUE )",
          NULL, NULL },
@@ -438,7 +392,6 @@ static ConfigTable a_metacfg[] = {
          asyncmeta_back_cf_gen, "( OLcfgDbAt:3.114 "
          "NAME 'olcDbMaxTargetConns' "
          "DESC 'Maximum number of open connections per target' "
-         "EQUALITY integerMatch "
          "SYNTAX OMsInteger "
          "SINGLE-VALUE )",
          NULL, NULL },
@@ -448,7 +401,6 @@ static ConfigTable a_metacfg[] = {
          asyncmeta_back_cf_gen, "( OLcfgDbAt:3.115 "
          "NAME 'olcDbMaxTimeoutOps' "
          "DESC 'Maximum number of consecutive timeout operations after which the connection is reset' "
-         "EQUALITY integerMatch "
          "SYNTAX OMsInteger "
          "SINGLE-VALUE )",
          NULL, NULL },
@@ -485,13 +437,10 @@ static ConfigOCs a_metaocs[] = {
                "NAME 'olcAsyncMetaConfig' "
                "DESC 'Asyncmeta backend configuration' "
                "SUP olcDatabaseConfig "
-               "MAY ( olcDbConnTtl "
-                       "$ olcDbDnCacheTtl "
+               "MAY ( olcDbDnCacheTtl "
                        "$ olcDbIdleTimeout "
                        "$ olcDbOnErr "
                        "$ olcDbPseudoRootBindDefer "
-                       "$ olcDbSingleConn "
-                       "$ olcDbUseTemporaryConn "
                        "$ olcDbConnectionPoolMax "
                        "$ olcDbMaxTimeoutOps"
                        "$ olcDbMaxPendingOps "
@@ -509,8 +458,7 @@ static ConfigOCs a_metaocs[] = {
                        "$ olcDbACLPasswd "
                        "$ olcDbIDAssertAuthzFrom "
                        "$ olcDbIDAssertBind "
-                       "$ olcDbMap "
-                       "$ olcDbRewrite "
+                       "$ olcDbSuffixMassage "
                        "$ olcDbSubtreeExclude "
                        "$ olcDbSubtreeInclude "
                        "$ olcDbTimeout "
@@ -555,33 +503,6 @@ asyncmeta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
        return LDAP_SUCCESS;
 }
 
-static int
-asyncmeta_rwi_init( struct rewrite_info **rwm_rw )
-{
-       char                    *rargv[ 3 ];
-
-       *rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
-       if ( *rwm_rw == NULL ) {
-               return -1;
-       }
-       /*
-        * the filter rewrite as a string must be disabled
-        * by default; it can be re-enabled by adding rules;
-        * this creates an empty rewriteContext
-        */
-       rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "searchFilter";
-       rargv[ 2 ] = NULL;
-       rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
-
-       rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "default";
-       rargv[ 2 ] = NULL;
-       rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
-
-       return 0;
-}
-
 static int
 asyncmeta_back_new_target(
        a_metatarget_t  **mtp )
@@ -592,11 +513,6 @@ asyncmeta_back_new_target(
 
        mt = ch_calloc( sizeof( a_metatarget_t ), 1 );
 
-       if ( asyncmeta_rwi_init( &mt->mt_rwmap.rwm_rw )) {
-               ch_free( mt );
-               return -1;
-       }
-
        ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
 
        mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
@@ -610,7 +526,7 @@ asyncmeta_back_new_target(
        return 0;
 }
 
-/* Validation for suffixmassage_config */
+/* suffixmassage config */
 static int
 asyncmeta_suffixm_config(
        ConfigArgs *c,
@@ -621,16 +537,16 @@ asyncmeta_suffixm_config(
 {
        BackendDB       *tmp_bd;
        struct berval   dn, nvnc, pvnc, nrnc, prnc;
-       int j, rc;
+       int j;
 
        /*
         * syntax:
         *
-        *      suffixmassage <suffix> <massaged suffix>
+        *      suffixmassage <local suffix> <remote suffix>
         *
-        * the <suffix> field must be defined as a valid suffix
+        * the <local suffix> field must be defined as a valid suffix
         * (or suffixAlias?) for the current database;
-        * the <massaged suffix> shouldn't have already been
+        * the <remote suffix> shouldn't have already been
         * defined as a valid suffix or suffixAlias for the
         * current server
         */
@@ -679,59 +595,12 @@ asyncmeta_suffixm_config(
                        c->log, prnc.bv_val );
        }
 
-       /*
-        * The suffix massaging is emulated by means of the
-        * rewrite capabilities
-        */
-       rc = asyncmeta_suffix_massage_config( mt->mt_rwmap.rwm_rw,
-                       &pvnc, &nvnc, &prnc, &nrnc );
+       mt->mt_lsuffixm = pvnc;
+       mt->mt_rsuffixm = prnc;
 
-       free( pvnc.bv_val );
        free( nvnc.bv_val );
-       free( prnc.bv_val );
        free( nrnc.bv_val );
 
-       return rc;
-}
-
-static int
-slap_bv_x_ordered_unparse( BerVarray in, BerVarray *out )
-{
-       int             i;
-       BerVarray       bva = NULL;
-       char            ibuf[32], *ptr;
-       struct berval   idx;
-
-       assert( in != NULL );
-
-       for ( i = 0; !BER_BVISNULL( &in[i] ); i++ )
-               /* count'em */ ;
-
-       if ( i == 0 ) {
-               return 1;
-       }
-
-       idx.bv_val = ibuf;
-
-       bva = ch_malloc( ( i + 1 ) * sizeof(struct berval) );
-       BER_BVZERO( &bva[ 0 ] );
-
-       for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
-               idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), SLAP_X_ORDERED_FMT, i );
-               if ( idx.bv_len >= sizeof( ibuf ) ) {
-                       ber_bvarray_free( bva );
-                       return 1;
-               }
-
-               bva[i].bv_len = idx.bv_len + in[i].bv_len;
-               bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
-               ptr = lutil_strcopy( bva[i].bv_val, ibuf );
-               ptr = lutil_strcopy( ptr, in[i].bv_val );
-               *ptr = '\0';
-               BER_BVZERO( &bva[ i + 1 ] );
-       }
-
-       *out = bva;
        return 0;
 }
 
@@ -1156,8 +1025,8 @@ static int
 asyncmeta_back_cf_gen( ConfigArgs *c )
 {
        a_metainfo_t    *mi = ( a_metainfo_t * )c->be->be_private;
-       a_metatarget_t  *mt;
-       a_metacommon_t  *mc;
+       a_metatarget_t  *mt = NULL;
+       a_metacommon_t  *mc = NULL;
 
        int i, rc = 0;
 
@@ -1282,12 +1151,11 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                case LDAP_BACK_CFG_NETWORK_TIMEOUT:
                        if ( mc->mc_network_timeout == 0 ) {
                                return 1;
-                       } else {
-                               char    buf[ SLAP_TEXT_BUFLEN ];
-                               lutil_unparse_time( buf, sizeof( buf ), mc->mc_network_timeout );
-                               ber_str2bv( buf, 0, 0, &bv );
-                               value_add_one( &c->rvalue_vals, &bv );
                        }
+                       bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%ld",
+                               mc->mc_network_timeout );
+                       bv.bv_val = c->cr_msg;
+                       value_add_one( &c->rvalue_vals, &bv );
                        break;
 
                case LDAP_BACK_CFG_NOREFS:
@@ -1617,21 +1485,23 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                        break;
                }
 
-               case LDAP_BACK_CFG_SUFFIXM:     /* unused */
-               case LDAP_BACK_CFG_REWRITE:
-                       if ( mt->mt_rwmap.rwm_bva_rewrite == NULL ) {
-                               rc = 1;
-                       } else {
-                               rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_rewrite, &c->rvalue_vals );
-                       }
-                       break;
-
-               case LDAP_BACK_CFG_MAP:
-                       if ( mt->mt_rwmap.rwm_bva_map == NULL ) {
+               case LDAP_BACK_CFG_SUFFIXM:
+                       if ( mt->mt_lsuffixm.bv_val ) {
+                               struct berval bv;
+                               char *ptr;
+                               bv.bv_len = mt->mt_lsuffixm.bv_len + 2 + 1 + mt->mt_rsuffixm.bv_len + 2;
+                               bv.bv_val = ch_malloc( bv.bv_len + 1 );
+                               ptr = bv.bv_val;
+                               *ptr++ = '"';
+                               ptr = lutil_strcopy(ptr, mt->mt_lsuffixm.bv_val);
+                               ptr = lutil_strcopy(ptr, "\" \"");
+                               ptr = lutil_strcopy(ptr, mt->mt_rsuffixm.bv_val);
+                               *ptr++ = '"';
+                               *ptr = '\0';
+                               ber_bvarray_add( &c->rvalue_vals, &bv );
+                               rc = 0;
+                       } else
                                rc = 1;
-                       } else {
-                               rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_map, &c->rvalue_vals );
-                       }
                        break;
 
                case LDAP_BACK_CFG_SUBTREE_EX:
@@ -1824,25 +1694,13 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                        memset( &mt->mt_idassert, 0, sizeof( slap_idassert_t ) );
                        break;
 
-               case LDAP_BACK_CFG_SUFFIXM:     /* unused */
-               case LDAP_BACK_CFG_REWRITE:
-                       if ( mt->mt_rwmap.rwm_bva_rewrite ) {
-                               ber_bvarray_free( mt->mt_rwmap.rwm_bva_rewrite );
-                               mt->mt_rwmap.rwm_bva_rewrite = NULL;
-                       }
-                       if ( mt->mt_rwmap.rwm_rw )
-                               rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
-                       break;
-
-               case LDAP_BACK_CFG_MAP:
-                       if ( mt->mt_rwmap.rwm_bva_map ) {
-                               ber_bvarray_free( mt->mt_rwmap.rwm_bva_map );
-                               mt->mt_rwmap.rwm_bva_map = NULL;
+               case LDAP_BACK_CFG_SUFFIXM:
+                       if ( mt->mt_lsuffixm.bv_val ) {
+                               ch_free( mt->mt_lsuffixm.bv_val );
+                               ch_free( mt->mt_rsuffixm.bv_val );
+                               BER_BVZERO( &mt->mt_lsuffixm );
+                               BER_BVZERO( &mt->mt_rsuffixm );
                        }
-                       asyncmeta_back_map_free( &mt->mt_rwmap.rwm_oc );
-                       asyncmeta_back_map_free( &mt->mt_rwmap.rwm_at );
-                       mt->mt_rwmap.rwm_oc.drop_missing = 0;
-                       mt->mt_rwmap.rwm_at.drop_missing = 0;
                        break;
 
                case LDAP_BACK_CFG_SUBTREE_EX:
@@ -2179,7 +2037,6 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                break;
        case LDAP_BACK_CFG_MAX_TARGET_CONNS:
        {
-               int msc_num, i;
                if (c->value_int < 0) {
                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                  "max-target-conns invalid value %d",
@@ -2301,8 +2158,7 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                if ( strcasecmp( c->argv[ 0 ], "binddn" ) == 0 ) {
                        Debug( LDAP_DEBUG_ANY, "%s: "
                                "\"binddn\" statement is deprecated; "
-                               "use \"acl-authcDN\" instead\n",
-                               c->log );
+                               "use \"acl-authcDN\" instead\n", c->log );
                        /* FIXME: some day we'll need to throw an error */
                }
 
@@ -2317,8 +2173,7 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                if ( strcasecmp( c->argv[ 0 ], "bindpw" ) == 0 ) {
                        Debug( LDAP_DEBUG_ANY, "%s "
                                "\"bindpw\" statement is deprecated; "
-                               "use \"acl-passwd\" instead\n",
-                               c->log );
+                               "use \"acl-passwd\" instead\n", c->log );
                        /* FIXME: some day we'll need to throw an error */
                }
 
@@ -2524,226 +2379,10 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                break;
 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
 
-       case LDAP_BACK_CFG_SUFFIXM:     /* FALLTHRU */
-       case LDAP_BACK_CFG_REWRITE: {
-       /* rewrite stuff ... */
-               ConfigArgs ca = { 0 };
-               char *line, **argv;
-               struct rewrite_info *rwi;
-               int cnt = 0, argc, ix = c->valx;
-
-               if ( mt->mt_rwmap.rwm_bva_rewrite ) {
-                       for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ cnt ] ); cnt++ )
-                               /* count */ ;
-               }
-
-               if ( ix >= cnt || ix < 0 ) {
-                       ix = cnt;
-               } else {
-                       rwi = mt->mt_rwmap.rwm_rw;
-
-                       mt->mt_rwmap.rwm_rw = NULL;
-                       rc = asyncmeta_rwi_init( &mt->mt_rwmap.rwm_rw );
-
-                       /* re-parse all rewrite rules, up to the one
-                        * that needs to be added */
-                       ca.be = c->be;
-                       ca.fname = c->fname;
-                       ca.lineno = c->lineno;
-                       for ( i = 0; i < ix; i++ ) {
-                               ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
-                               ca.argc = 0;
-                               config_fp_parse_line( &ca );
-
-                               if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
-                                       rc = asyncmeta_suffixm_config( &ca, ca.argc, ca.argv, mt );
-                               } else {
-                                       rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
-                                               c->fname, c->lineno, ca.argc, ca.argv );
-                               }
-                               assert( rc == 0 );
-                               ch_free( ca.argv );
-                               ch_free( ca.tline );
-                       }
-               }
-               argc = c->argc;
-               argv = c->argv;
-               if ( c->op != SLAP_CONFIG_ADD ) {
-                       argc--;
-                       argv++;
-               }
-               /* add the new rule */
-               if ( !strcasecmp( argv[0], "suffixmassage" )) {
-                       rc = asyncmeta_suffixm_config( c, argc, argv, mt );
-               } else {
-                       rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
-                                               c->fname, c->lineno, argc, argv );
-               }
-               if ( rc ) {
-                       if ( ix < cnt ) {
-                               rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
-                               mt->mt_rwmap.rwm_rw = rwi;
-                       }
-                       return 1;
-               }
-               if ( ix < cnt ) {
-                       for ( ; i < cnt; i++ ) {
-                               ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
-                               ca.argc = 0;
-                               config_fp_parse_line( &ca );
-
-                               if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
-                                       rc = asyncmeta_suffixm_config( &ca, ca.argc, ca.argv, mt );
-                               } else {
-                                       rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
-                                               c->fname, c->lineno, ca.argc, argv );
-                               }
-                               assert( rc == 0 );
-                               ch_free( ca.argv );
-                               ch_free( ca.tline );
-                       }
-               }
-
-               /* save the rule info */
-               line = ldap_charray2str( argv, "\" \"" );
-               if ( line != NULL ) {
-                       struct berval bv;
-                       int len = strlen( argv[ 0 ] );
-
-                       ber_str2bv( line, 0, 0, &bv );
-                       AC_MEMCPY( &bv.bv_val[ len ], &bv.bv_val[ len + 1 ],
-                               bv.bv_len - ( len + 1 ));
-                       bv.bv_val[ bv.bv_len - 1] = '"';
-                       ber_bvarray_add( &mt->mt_rwmap.rwm_bva_rewrite, &bv );
-                       /* move it to the right slot */
-                       if ( ix < cnt ) {
-                               for ( i=cnt; i>ix; i-- )
-                                       mt->mt_rwmap.rwm_bva_rewrite[i+1] = mt->mt_rwmap.rwm_bva_rewrite[i];
-                               mt->mt_rwmap.rwm_bva_rewrite[i] = bv;
-
-                               /* destroy old rules */
-                               rewrite_info_delete( &rwi );
-                       }
-               }
-               } break;
-
-       case LDAP_BACK_CFG_MAP: {
-       /* objectclass/attribute mapping */
-               ConfigArgs ca = { 0 };
-               char *argv[5];
-               struct ldapmap rwm_oc;
-               struct ldapmap rwm_at;
-               int cnt = 0, ix = c->valx;
-
-               if ( mt->mt_rwmap.rwm_bva_map ) {
-                       for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_map[ cnt ] ); cnt++ )
-                               /* count */ ;
-               }
-
-               if ( ix >= cnt || ix < 0 ) {
-                       ix = cnt;
-               } else {
-                       rwm_oc = mt->mt_rwmap.rwm_oc;
-                       rwm_at = mt->mt_rwmap.rwm_at;
-
-                       memset( &mt->mt_rwmap.rwm_oc, 0, sizeof( mt->mt_rwmap.rwm_oc ) );
-                       memset( &mt->mt_rwmap.rwm_at, 0, sizeof( mt->mt_rwmap.rwm_at ) );
-
-                       /* re-parse all mappings, up to the one
-                        * that needs to be added */
-                       argv[0] = c->argv[0];
-                       ca.fname = c->fname;
-                       ca.lineno = c->lineno;
-                       for ( i = 0; i < ix; i++ ) {
-                               ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
-                               ca.argc = 0;
-                               config_fp_parse_line( &ca );
-
-                               argv[1] = ca.argv[0];
-                               argv[2] = ca.argv[1];
-                               argv[3] = ca.argv[2];
-                               argv[4] = ca.argv[3];
-                               ch_free( ca.argv );
-                               ca.argv = argv;
-                               ca.argc++;
-                               rc = asyncmeta_map_config( &ca, &mt->mt_rwmap.rwm_oc,
-                                       &mt->mt_rwmap.rwm_at );
-
-                               ch_free( ca.tline );
-                               ca.tline = NULL;
-                               ca.argv = NULL;
-
-                               /* in case of failure, restore
-                                * the existing mapping */
-                               if ( rc ) {
-                                       goto map_fail;
-                               }
-                       }
-               }
-               /* add the new mapping */
-               rc = asyncmeta_map_config( c, &mt->mt_rwmap.rwm_oc,
-                                       &mt->mt_rwmap.rwm_at );
-               if ( rc ) {
-                       goto map_fail;
-               }
-
-               if ( ix < cnt ) {
-                       for ( ; i<cnt ; cnt++ ) {
-                               ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
-                               ca.argc = 0;
-                               config_fp_parse_line( &ca );
-
-                               argv[1] = ca.argv[0];
-                               argv[2] = ca.argv[1];
-                               argv[3] = ca.argv[2];
-                               argv[4] = ca.argv[3];
-
-                               ch_free( ca.argv );
-                               ca.argv = argv;
-                               ca.argc++;
-                               rc = asyncmeta_map_config( &ca, &mt->mt_rwmap.rwm_oc,
-                                       &mt->mt_rwmap.rwm_at );
-
-                               ch_free( ca.tline );
-                               ca.tline = NULL;
-                               ca.argv = NULL;
-
-                               /* in case of failure, restore
-                                * the existing mapping */
-                               if ( rc ) {
-                                       goto map_fail;
-                               }
-                       }
-               }
-
-               /* save the map info */
-               argv[0] = ldap_charray2str( &c->argv[ 1 ], " " );
-               if ( argv[0] != NULL ) {
-                       struct berval bv;
-                       ber_str2bv( argv[0], 0, 0, &bv );
-                       ber_bvarray_add( &mt->mt_rwmap.rwm_bva_map, &bv );
-                       /* move it to the right slot */
-                       if ( ix < cnt ) {
-                               for ( i=cnt; i>ix; i-- )
-                                       mt->mt_rwmap.rwm_bva_map[i+1] = mt->mt_rwmap.rwm_bva_map[i];
-                               mt->mt_rwmap.rwm_bva_map[i] = bv;
-
-                               /* destroy old mapping */
-                               asyncmeta_back_map_free( &rwm_oc );
-                               asyncmeta_back_map_free( &rwm_at );
-                       }
-               }
+       case LDAP_BACK_CFG_SUFFIXM:
+               rc = asyncmeta_suffixm_config( c, c->argc, c->argv, mt );
                break;
 
-map_fail:;
-               if ( ix < cnt ) {
-                       asyncmeta_back_map_free( &mt->mt_rwmap.rwm_oc );
-                       asyncmeta_back_map_free( &mt->mt_rwmap.rwm_at );
-                       mt->mt_rwmap.rwm_oc = rwm_oc;
-                       mt->mt_rwmap.rwm_at = rwm_at;
-               }
-               } break;
-
        case LDAP_BACK_CFG_NRETRIES: {
                int             nretries = META_RETRY_UNDEFINED;
 
@@ -2850,8 +2489,7 @@ asyncmeta_back_init_cf( BackendInfo *bi )
        if ( rc ) {
                Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
                        "warning, unable to get \"olcDbACLPasswd\" "
-                       "attribute description: %d: %s\n",
-                       rc, text );
+                       "attribute description: %d: %s\n", rc, text );
        } else {
                (void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
                        ad->ad_type->sat_oid );
@@ -2862,8 +2500,7 @@ asyncmeta_back_init_cf( BackendInfo *bi )
        if ( rc ) {
                Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
                        "warning, unable to get \"olcDbIDAssertPasswd\" "
-                       "attribute description: %d: %s\n",
-                       rc, text );
+                       "attribute description: %d: %s\n", rc, text );
        } else {
                (void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
                        ad->ad_type->sat_oid );
@@ -2871,350 +2508,3 @@ asyncmeta_back_init_cf( BackendInfo *bi )
 
        return 0;
 }
-
-static int
-asyncmeta_map_config(
-               ConfigArgs *c,
-               struct ldapmap  *oc_map,
-               struct ldapmap  *at_map )
-{
-       struct ldapmap          *map;
-       struct ldapmapping      *mapping;
-       char                    *src, *dst;
-       int                     is_oc = 0;
-
-       if ( strcasecmp( c->argv[ 1 ], "objectclass" ) == 0 ) {
-               map = oc_map;
-               is_oc = 1;
-
-       } else if ( strcasecmp( c->argv[ 1 ], "attribute" ) == 0 ) {
-               map = at_map;
-
-       } else {
-               snprintf( c->cr_msg, sizeof(c->cr_msg),
-                       "%s unknown argument \"%s\"",
-                       c->argv[0], c->argv[1] );
-               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-               return 1;
-       }
-
-       if ( !is_oc && map->map == NULL ) {
-               /* only init if required */
-               asyncmeta_map_init( map, &mapping );
-       }
-
-       if ( strcmp( c->argv[ 2 ], "*" ) == 0 ) {
-               if ( c->argc < 4 || strcmp( c->argv[ 3 ], "*" ) == 0 ) {
-                       map->drop_missing = ( c->argc < 4 );
-                       goto success_return;
-               }
-               src = dst = c->argv[ 3 ];
-
-       } else if ( c->argc < 4 ) {
-               src = "";
-               dst = c->argv[ 2 ];
-
-       } else {
-               src = c->argv[ 2 ];
-               dst = ( strcmp( c->argv[ 3 ], "*" ) == 0 ? src : c->argv[ 3 ] );
-       }
-
-       if ( ( map == at_map )
-               && ( strcasecmp( src, "objectclass" ) == 0
-                       || strcasecmp( dst, "objectclass" ) == 0 ) )
-       {
-               snprintf( c->cr_msg, sizeof(c->cr_msg),
-                       "objectclass attribute cannot be mapped" );
-               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-               return 1;
-       }
-
-       mapping = (struct ldapmapping *)ch_calloc( 2,
-               sizeof(struct ldapmapping) );
-       if ( mapping == NULL ) {
-               snprintf( c->cr_msg, sizeof(c->cr_msg),
-                       "out of memory" );
-               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-               return 1;
-       }
-       ber_str2bv( src, 0, 1, &mapping[ 0 ].src );
-       ber_str2bv( dst, 0, 1, &mapping[ 0 ].dst );
-       mapping[ 1 ].src = mapping[ 0 ].dst;
-       mapping[ 1 ].dst = mapping[ 0 ].src;
-
-       /*
-        * schema check
-        */
-       if ( is_oc ) {
-               if ( src[ 0 ] != '\0' ) {
-                       if ( oc_bvfind( &mapping[ 0 ].src ) == NULL ) {
-                               Debug( LDAP_DEBUG_ANY,
-       "%s: warning, source objectClass '%s' should be defined in schema\n",
-                                       c->log, src );
-
-                               /*
-                                * FIXME: this should become an err
-                                */
-                               goto error_return;
-                       }
-               }
-
-               if ( oc_bvfind( &mapping[ 0 ].dst ) == NULL ) {
-                       Debug( LDAP_DEBUG_ANY,
-       "%s: warning, destination objectClass '%s' is not defined in schema\n",
-                               c->log, dst );
-               }
-       } else {
-               int                     rc;
-               const char              *text = NULL;
-               AttributeDescription    *ad = NULL;
-
-               if ( src[ 0 ] != '\0' ) {
-                       rc = slap_bv2ad( &mapping[ 0 ].src, &ad, &text );
-                       if ( rc != LDAP_SUCCESS ) {
-                               Debug( LDAP_DEBUG_ANY,
-       "%s: warning, source attributeType '%s' should be defined in schema\n",
-                                       c->log, src );
-
-                               /*
-                                * FIXME: this should become an err
-                                */
-                               /*
-                                * we create a fake "proxied" ad
-                                * and add it here.
-                                */
-
-                               rc = slap_bv2undef_ad( &mapping[ 0 ].src,
-                                               &ad, &text, SLAP_AD_PROXIED );
-                               if ( rc != LDAP_SUCCESS ) {
-                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               "source attributeType \"%s\": %d (%s)",
-                                               src, rc, text ? text : "" );
-                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                       goto error_return;
-                               }
-                       }
-
-                       ad = NULL;
-               }
-
-               rc = slap_bv2ad( &mapping[ 0 ].dst, &ad, &text );
-               if ( rc != LDAP_SUCCESS ) {
-                       Debug( LDAP_DEBUG_ANY,
-       "%s: warning, destination attributeType '%s' is not defined in schema\n",
-                               c->log, dst );
-
-                       /*
-                        * we create a fake "proxied" ad
-                        * and add it here.
-                        */
-
-                       rc = slap_bv2undef_ad( &mapping[ 0 ].dst,
-                                       &ad, &text, SLAP_AD_PROXIED );
-                       if ( rc != LDAP_SUCCESS ) {
-                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                       "destination attributeType \"%s\": %d (%s)\n",
-                                       dst, rc, text ? text : "" );
-                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                               return 1;
-                       }
-               }
-       }
-
-       if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)&mapping[ 0 ], asyncmeta_mapping_cmp ) != NULL)
-                       || avl_find( map->remap, (caddr_t)&mapping[ 1 ], asyncmeta_mapping_cmp ) != NULL)
-       {
-               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                       "duplicate mapping found." );
-               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-               goto error_return;
-       }
-
-       if ( src[ 0 ] != '\0' ) {
-               avl_insert( &map->map, (caddr_t)&mapping[ 0 ],
-                                       asyncmeta_mapping_cmp, asyncmeta_mapping_dup );
-       }
-       avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
-                               asyncmeta_mapping_cmp, asyncmeta_mapping_dup );
-
-success_return:;
-       return 0;
-
-error_return:;
-       if ( mapping ) {
-               ch_free( mapping[ 0 ].src.bv_val );
-               ch_free( mapping[ 0 ].dst.bv_val );
-               ch_free( mapping );
-       }
-
-       return 1;
-}
-
-
-static char *
-suffix_massage_regexize( const char *s )
-{
-       char *res, *ptr;
-       const char *p, *r;
-       int i;
-
-       if ( s[ 0 ] == '\0' ) {
-               return ch_strdup( "^(.+)$" );
-       }
-
-       for ( i = 0, p = s;
-                       ( r = strchr( p, ',' ) ) != NULL;
-                       p = r + 1, i++ )
-               ;
-
-       res = ch_calloc( sizeof( char ),
-                       strlen( s )
-                       + STRLENOF( "((.+),)?" )
-                       + STRLENOF( "[ ]?" ) * i
-                       + STRLENOF( "$" ) + 1 );
-
-       ptr = lutil_strcopy( res, "((.+),)?" );
-       for ( i = 0, p = s;
-                       ( r = strchr( p, ',' ) ) != NULL;
-                       p = r + 1 , i++ ) {
-               ptr = lutil_strncopy( ptr, p, r - p + 1 );
-               ptr = lutil_strcopy( ptr, "[ ]?" );
-
-               if ( r[ 1 ] == ' ' ) {
-                       r++;
-               }
-       }
-       ptr = lutil_strcopy( ptr, p );
-       ptr[ 0 ] = '$';
-       ptr++;
-       ptr[ 0 ] = '\0';
-
-       return res;
-}
-
-static char *
-suffix_massage_patternize( const char *s, const char *p )
-{
-       ber_len_t       len;
-       char            *res, *ptr;
-
-       len = strlen( p );
-
-       if ( s[ 0 ] == '\0' ) {
-               len++;
-       }
-
-       res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
-       if ( res == NULL ) {
-               return NULL;
-       }
-
-       ptr = lutil_strcopy( res, ( p[ 0 ] == '\0' ? "%2" : "%1" ) );
-       if ( s[ 0 ] == '\0' ) {
-               ptr[ 0 ] = ',';
-               ptr++;
-       }
-       lutil_strcopy( ptr, p );
-
-       return res;
-}
-
-int
-asyncmeta_suffix_massage_config(
-               struct rewrite_info *info,
-               struct berval *pvnc,
-               struct berval *nvnc,
-               struct berval *prnc,
-               struct berval *nrnc
-)
-{
-       char *rargv[ 5 ];
-       int line = 0;
-
-       rargv[ 0 ] = "rewriteEngine";
-       rargv[ 1 ] = "on";
-       rargv[ 2 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
-
-       rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "default";
-       rargv[ 2 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
-
-       rargv[ 0 ] = "rewriteRule";
-       rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
-       rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
-       rargv[ 3 ] = ":";
-       rargv[ 4 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
-       ch_free( rargv[ 1 ] );
-       ch_free( rargv[ 2 ] );
-
-       if ( BER_BVISEMPTY( pvnc ) ) {
-               rargv[ 0 ] = "rewriteRule";
-               rargv[ 1 ] = "^$";
-               rargv[ 2 ] = prnc->bv_val;
-               rargv[ 3 ] = ":";
-               rargv[ 4 ] = NULL;
-               rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
-       }
-
-       rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "searchEntryDN";
-       rargv[ 2 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
-
-       rargv[ 0 ] = "rewriteRule";
-       rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
-       rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
-       rargv[ 3 ] = ":";
-       rargv[ 4 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
-       ch_free( rargv[ 1 ] );
-       ch_free( rargv[ 2 ] );
-
-       if ( BER_BVISEMPTY( prnc ) ) {
-               rargv[ 0 ] = "rewriteRule";
-               rargv[ 1 ] = "^$";
-               rargv[ 2 ] = pvnc->bv_val;
-               rargv[ 3 ] = ":";
-               rargv[ 4 ] = NULL;
-               rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
-       }
-
-       /* backward compatibility */
-       rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "searchResult";
-       rargv[ 2 ] = "alias";
-       rargv[ 3 ] = "searchEntryDN";
-       rargv[ 4 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
-
-       rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "matchedDN";
-       rargv[ 2 ] = "alias";
-       rargv[ 3 ] = "searchEntryDN";
-       rargv[ 4 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
-
-       rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "searchAttrDN";
-       rargv[ 2 ] = "alias";
-       rargv[ 3 ] = "searchEntryDN";
-       rargv[ 4 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
-
-       /* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
-        * see servers/slapd/overlays/rwm.h for details */
-        rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "referralAttrDN";
-       rargv[ 2 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
-
-       rargv[ 0 ] = "rewriteContext";
-       rargv[ 1 ] = "referralDN";
-       rargv[ 2 ] = NULL;
-       rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
-
-       return 0;
-}
index eb90afa56881388960696a38e82d815b0af3b3d6..8d9690a49e61142338fdee787ae909a025cee092 100644 (file)
 #include <ac/errno.h>
 #include <ac/socket.h>
 #include <ac/string.h>
-
-
-#define AVL_INTERNAL
 #include "slap.h"
+#include "../../../libraries/libldap/ldap-int.h"
 #include "../back-ldap/back-ldap.h"
 #include "back-asyncmeta.h"
 
-
-/*
- * Debug stuff (got it from libavl)
- */
-#if META_BACK_PRINT_CONNTREE > 0
-
-static void
-asyncmeta_back_ravl_print( Avlnode *root, int depth )
-{
-       int             i;
-
-       if ( root == 0 ) {
-               return;
-       }
-
-       asyncmeta_back_ravl_print( root->avl_right, depth + 1 );
-
-       for ( i = 0; i < depth; i++ ) {
-               fprintf( stderr, "-" );
-       }
-       fputc( ' ', stderr );
-
-       asyncmeta_back_print( (a_metaconn_t *)root->avl_data,
-               avl_bf2str( root->avl_bf ) );
-
-       asyncmeta_back_ravl_print( root->avl_left, depth + 1 );
-}
-
-/* NOTE: duplicate from back-ldap/bind.c */
-static char* priv2str[] = {
-       "privileged",
-       "privileged/TLS",
-       "anonymous",
-       "anonymous/TLS",
-       "bind",
-       "bind/TLS",
-       NULL
-};
-
-#endif /* META_BACK_PRINT_CONNTREE */
-/*
- * End of debug stuff
- */
-
 /*
  * asyncmeta_conn_alloc
  *
@@ -127,8 +81,6 @@ asyncmeta_init_one_conn(
        a_dncookie              dc;
        int                     isauthz = ( candidate == mc->mc_authz_target );
        int                     do_return = 0;
-       int                     nretries = 2;
-
 #ifdef HAVE_TLS
        int                     is_ldaps = 0;
        int                     do_start_tls = 0;
@@ -164,15 +116,16 @@ asyncmeta_init_one_conn(
 
                if ( dont_retry ) {
                        rs->sr_err = LDAP_UNAVAILABLE;
+                       rs->sr_text = "Target is quarantined";
+                       Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: Target is quarantined\n",
+                              op->o_log_prefix );
                        if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
-                               rs->sr_text = "Target is quarantined";
                                        send_ldap_result( op, rs );
                        }
                        return rs->sr_err;
                }
        }
                msc = &mc->mc_conns[candidate];
-retry_lock:;
        /*
         * Already init'ed
         */
@@ -186,13 +139,7 @@ retry_lock:;
        } else if ( META_BACK_CONN_CREATING( msc )
                || LDAP_BACK_CONN_BINDING( msc ) )
        {
-               /* sounds more appropriate */
-               if (nretries >= 0) {
-                       nretries--;
-                       ldap_pvt_thread_yield();
-                       goto retry_lock;
-               }
-               rs->sr_err = LDAP_UNAVAILABLE;
+               rs->sr_err = LDAP_SUCCESS;
                do_return = 1;
 
        } else if ( META_BACK_CONN_INITED( msc ) ) {
@@ -231,6 +178,8 @@ retry_lock:;
 #endif /* HAVE_TLS */
        ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
        if ( rs->sr_err != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: ldap_initialize failed err=%d\n",
+                      op->o_log_prefix, rs->sr_err );
                goto error_return;
        }
        msc->msc_ldr = ldap_dup(msc->msc_ld);
@@ -327,8 +276,8 @@ retry:;
 
                        default:
                                /* only touch when activity actually took place... */
-                               if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
-                                       msc->msc_time = op->o_time;
+                               if ( mi->mi_idle_timeout != 0 ) {
+                                       asyncmeta_set_msc_time(msc);
                                }
                                break;
                        }
@@ -348,6 +297,7 @@ retry:;
                                        res = NULL;
 
                                        if ( rs->sr_err == LDAP_SUCCESS ) {
+
                                                rs->sr_err = err;
                                        }
                                        rs->sr_err = slap_map_api2result( rs );
@@ -385,7 +335,10 @@ retry:;
                 */
                rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
-
+               if (rs->sr_err != LDAP_SUCCESS) {
+                       Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: ldap_start_tls_s failed err=%d\n",
+                              op->o_log_prefix, rs->sr_err );
+               }
                /* if StartTLS is requested, only attempt it if the URL
                 * is not "ldaps://"; this may occur not only in case
                 * of misconfiguration, but also when used in the chain
@@ -407,18 +360,16 @@ retry:;
                }
        }
 #endif /* HAVE_TLS */
-
        /*
         * Set the network timeout if set
         */
        if ( mt->mt_network_timeout != 0 ) {
-               struct timeval  network_timeout;
-
-               network_timeout.tv_usec = 0;
-               network_timeout.tv_sec = mt->mt_network_timeout;
+               struct timeval  network_timeout;
+               network_timeout.tv_sec = 0;
+               network_timeout.tv_usec = mt->mt_network_timeout*1000;
 
                ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
-                               (void *)&network_timeout );
+                                (void *)&network_timeout );
        }
 
        /*
@@ -452,38 +403,22 @@ retry:;
                        BER_BVZERO( &msc->msc_bound_ndn );
                }
                if ( !BER_BVISEMPTY( &op->o_ndn )
-                       && SLAP_IS_AUTHZ_BACKEND( op )
                        && isauthz )
                {
+                       dc.op = op;
                        dc.target = mt;
-                       dc.conn = op->o_conn;
-                       dc.rs = rs;
-                       dc.ctx = "bindDN";
+                       dc.memctx = NULL;
+                       dc.to_from = MASSAGE_REQ;
 
                        /*
                         * Rewrite the bind dn if needed
                         */
-                       if ( asyncmeta_dn_massage( &dc, &op->o_conn->c_dn,
-                                               &msc->msc_bound_ndn ) )
-                       {
-
-#ifdef DEBUG_205
-                               Debug( LDAP_DEBUG_ANY,
-                                       "### %s asyncmeta_init_one_conn(rewrite) "
-                                       "ldap_unbind_ext[%d] ld=%p\n",
-                                       op->o_log_prefix, candidate,
-                                       (void *)msc->msc_ld );
-#endif /* DEBUG_205 */
-                               goto error_return;
-                       }
+                       asyncmeta_dn_massage( &dc, &op->o_conn->c_dn, &msc->msc_bound_ndn );
 
                        /* copy the DN if needed */
                        if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
                                ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
                        }
-
-                       assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
-
                } else {
                        ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
                }
@@ -496,10 +431,6 @@ error_return:;
                META_BACK_CONN_CREATING_CLEAR( msc );
        }
        if ( rs->sr_err == LDAP_SUCCESS && msc != NULL) {
-               /*
-                * Sets a cookie for the rewrite session
-                */
-               ( void )rewrite_session_init( mt->mt_rwmap.rwm_rw, op->o_conn );
                META_BACK_CONN_INITED_SET( msc );
        }
 
@@ -512,125 +443,6 @@ error_return:;
        return rs->sr_err;
 }
 
-/*
- * asyncmeta_retry
- *
- * Retries one connection
- */
-int
-asyncmeta_retry(
-       Operation               *op,
-       SlapReply               *rs,
-       a_metaconn_t            **mcp,
-       int                     candidate,
-       ldap_back_send_t        sendok )
-{
-       a_metaconn_t            *mc = *mcp;
-       a_metainfo_t            *mi = mc->mc_info;
-       a_metatarget_t          *mt = mi->mi_targets[ candidate ];
-       a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
-       int                     rc = LDAP_UNAVAILABLE,
-                               binding,
-                               quarantine = 1;
-
-       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-       struct berval save_cred;
-
-       if ( LogTest( LDAP_DEBUG_ANY ) ) {
-               /* this lock is required; however,
-                * it's invoked only when logging is on */
-                       ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
-                       Debug(LDAP_DEBUG_ANY,
-                             "%s asyncmeta_retry[%d]: retrying URI=\"%s\" DN=\"%s\".\n",
-                             op->o_log_prefix, candidate, mt->mt_uri,
-                             BER_BVISNULL(&msc->msc_bound_ndn) ? "" : msc->msc_bound_ndn.bv_val );
-                       ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
-       }
-
-       ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
-       /* mc here must be the regular mc, reset and ready for init */
-       rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
-                                     LDAP_BACK_CONN_ISPRIV( mc ), sendok, 0 );
-
-       if ( rc != LDAP_SUCCESS ) {
-               if ( sendok & LDAP_BACK_SENDERR ) {
-                       /* init_one_conn has set the result */
-                       send_ldap_result( op, rs );
-               }
-       }
-
-       if ( rc == LDAP_SUCCESS ) {
-               quarantine = 0;
-               LDAP_BACK_CONN_BINDING_SET( msc ); binding = 1;
-               /* todo this must be dobind_init */
-               rc = asyncmeta_back_single_dobind( op, rs, mcp, candidate,
-                                                  sendok, mt->mt_nretries, 0 );
-
-               Debug( LDAP_DEBUG_ANY,
-                      "%s asyncmeta_retry[%d]: "
-                      "asyncmeta_single_dobind=%d\n",
-                      op->o_log_prefix, candidate, rc );
-               if ( rc == LDAP_SUCCESS ) {
-                       if ( !BER_BVISNULL( &msc->msc_bound_ndn ) &&
-                            !BER_BVISEMPTY( &msc->msc_bound_ndn ) )
-                       {
-                               LDAP_BACK_CONN_ISBOUND_SET( msc );
-
-                       } else {
-                               LDAP_BACK_CONN_ISANON_SET( msc );
-                       }
-
-                       /* when bound, dispose of the "binding" flag */
-                       if ( binding ) {
-                               LDAP_BACK_CONN_BINDING_CLEAR( msc );
-                       }
-               }
-       }
-
-
-       if ( rc != LDAP_SUCCESS ) {
-               if (mc->mc_active < 1) {
-                       asyncmeta_clear_one_msc(NULL, mc, candidate);
-               }
-               if ( sendok & LDAP_BACK_SENDERR ) {
-                       rs->sr_err = rc;
-                       rs->sr_text = "Unable to retry";
-                       send_ldap_result( op, rs );
-               }
-       }
-
-       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
-       if ( quarantine && META_BACK_TGT_QUARANTINE( mt ) ) {
-               asyncmeta_quarantine( op, mi, rs, candidate );
-       }
-
-       return rc == LDAP_SUCCESS ? 1 : 0;
-}
-
-/*
- * callback for unique candidate selection
- */
-static int
-asyncmeta_conn_cb( Operation *op, SlapReply *rs )
-{
-       assert( op->o_tag == LDAP_REQ_SEARCH );
-
-       switch ( rs->sr_type ) {
-       case REP_SEARCH:
-               ((long *)op->o_callback->sc_private)[0] = (long)op->o_private;
-               break;
-
-       case REP_SEARCHREF:
-       case REP_RESULT:
-               break;
-
-       default:
-               return rs->sr_err;
-       }
-
-       return 0;
-}
-
 
 static int
 asyncmeta_get_candidate(
@@ -654,59 +466,6 @@ asyncmeta_get_candidate(
                rs->sr_err = LDAP_NO_SUCH_OBJECT;
                rs->sr_text = "No suitable candidate target found";
 
-       } else if ( candidate == META_TARGET_MULTIPLE ) {
-               Operation       op2 = *op;
-               SlapReply       rs2 = { REP_RESULT };
-               slap_callback   cb2 = { 0 };
-               int             rc;
-
-               /* try to get a unique match for the request ndn
-                * among the multiple candidates available */
-               op2.o_tag = LDAP_REQ_SEARCH;
-               op2.o_req_dn = *ndn;
-               op2.o_req_ndn = *ndn;
-               op2.ors_scope = LDAP_SCOPE_BASE;
-               op2.ors_deref = LDAP_DEREF_NEVER;
-               op2.ors_attrs = slap_anlist_no_attrs;
-               op2.ors_attrsonly = 0;
-               op2.ors_limit = NULL;
-               op2.ors_slimit = 1;
-               op2.ors_tlimit = SLAP_NO_LIMIT;
-
-               op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
-               op2.ors_filterstr = *slap_filterstr_objectClass_pres;
-
-               op2.o_callback = &cb2;
-               cb2.sc_response = asyncmeta_conn_cb;
-               cb2.sc_private = (void *)&candidate;
-
-               rc = op->o_bd->be_search( &op2, &rs2 );
-
-               switch ( rs2.sr_err ) {
-               case LDAP_SUCCESS:
-               default:
-                       rs->sr_err = rs2.sr_err;
-                       break;
-
-               case LDAP_SIZELIMIT_EXCEEDED:
-                       /* if multiple candidates can serve the operation,
-                        * and a default target is defined, and it is
-                        * a candidate, try using it (FIXME: YMMV) */
-                       if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
-                               && asyncmeta_is_candidate( mi->mi_targets[ mi->mi_defaulttarget ],
-                                               ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
-                       {
-                               candidate = mi->mi_defaulttarget;
-                               rs->sr_err = LDAP_SUCCESS;
-                               rs->sr_text = NULL;
-
-                       } else {
-                               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-                               rs->sr_text = "Unable to select unique candidate target";
-                       }
-                       break;
-               }
-
        } else {
                rs->sr_err = LDAP_SUCCESS;
        }
@@ -714,19 +473,6 @@ asyncmeta_get_candidate(
        return candidate;
 }
 
-static void    *asyncmeta_candidates_dummy;
-
-static void
-asyncmeta_candidates_keyfree(
-       void            *key,
-       void            *data )
-{
-       a_metacandidates_t      *mc = (a_metacandidates_t *)data;
-
-       ber_memfree_x( mc->mc_candidates, NULL );
-       ber_memfree_x( data, NULL );
-}
-
 
 /*
  * asyncmeta_getconn
@@ -910,7 +656,7 @@ asyncmeta_getconn(
 
                        if ( sendok & LDAP_BACK_SENDERR ) {
                                if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
-                                       rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
+                                       rs->sr_matched = mi->mi_suffix.bv_val;
                                }
                                send_ldap_result( op, rs );
                                rs->sr_matched = NULL;
@@ -933,9 +679,6 @@ asyncmeta_getconn(
        }
 
        if ( op_type == META_OP_REQUIRE_SINGLE ) {
-               a_metatarget_t          *mt = NULL;
-               a_metasingleconn_t      *msc = NULL;
-
                int                     j;
 
                for ( j = 0; j < mi->mi_ntargets; j++ ) {
@@ -956,7 +699,7 @@ asyncmeta_getconn(
                        if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
                                if ( sendok & LDAP_BACK_SENDERR ) {
                                        if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
-                                               rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
+                                               rs->sr_matched = mi->mi_suffix.bv_val;
                                        }
                                        send_ldap_result( op, rs );
                                        rs->sr_matched = NULL;
@@ -998,9 +741,6 @@ asyncmeta_getconn(
                 */
                        ( void )asyncmeta_clear_unused_candidates( op, i , mc, candidates);
 
-               mt = mi->mi_targets[ i ];
-               msc = &mc->mc_conns[ i ];
-
                /*
                 * The target is activated; if needed, it is
                 * also init'd. In case of error, asyncmeta_init_one_conn
@@ -1118,7 +858,7 @@ asyncmeta_getconn(
 
                        if ( sendok & LDAP_BACK_SENDERR ) {
                                if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
-                                       rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
+                                       rs->sr_matched = mi->mi_suffix.bv_val;
                                }
                                send_ldap_result( op, rs );
                                rs->sr_matched = NULL;
@@ -1133,7 +873,6 @@ asyncmeta_getconn(
        }
 
 done:;
-       /* clear out meta_back_init_one_conn non-fatal errors */
        rs->sr_err = LDAP_SUCCESS;
        rs->sr_text = NULL;
 
@@ -1266,6 +1005,7 @@ a_metaconn_t *
 asyncmeta_get_next_mc( a_metainfo_t *mi )
 {
        a_metaconn_t *mc = NULL;
+
        ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
        if (mi->mi_next_conn >= mi->mi_num_conns-1) {
                mi->mi_next_conn = 0;
@@ -1294,14 +1034,13 @@ int asyncmeta_start_one_listener(a_metaconn_t *mc,
 {
        a_metasingleconn_t *msc;
        ber_socket_t s;
-       int i;
 
        msc = &mc->mc_conns[candidate];
-       if (msc->msc_ld == NULL || !META_IS_CANDIDATE( &candidates[ candidate ] )) {
+       if ( slapd_shutdown || !META_BACK_CONN_INITED( msc ) || msc->msc_ld == NULL
+           || !META_IS_CANDIDATE( &candidates[ candidate ] )) {
                return LDAP_SUCCESS;
        }
        bc->msgids[candidate] = candidates[candidate].sr_msgid;
-       msc->msc_pending_ops++;
        if ( msc->conn == NULL) {
                ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
                if (s < 0) {
@@ -1313,3 +1052,130 @@ int asyncmeta_start_one_listener(a_metaconn_t *mc,
        connection_client_enable( msc->conn );
        return LDAP_SUCCESS;
 }
+
+int
+asyncmeta_clear_one_msc(
+       Operation       *op,
+       a_metaconn_t    *mc,
+       int             candidate,
+       int             unbind,
+       const char *caller)
+{
+       a_metasingleconn_t *msc;
+       if (mc == NULL) {
+               return 0;
+       }
+       msc = &mc->mc_conns[candidate];
+       if ( LogTest( asyncmeta_debug ) ) {
+               char    time_buf[ SLAP_TEXT_BUFLEN ];
+               asyncmeta_get_timestamp(time_buf);
+               Debug( asyncmeta_debug, "[%s] Resetting msc: %p, msc_ld: %p, "
+                      "msc_bound_ndn: %s, msc->conn: %p, %s \n",
+                      time_buf, msc, msc->msc_ld, msc->msc_bound_ndn.bv_val,
+                      msc->conn, caller ? caller : "" );
+       }
+       msc->msc_mscflags = 0;
+       if (msc->conn) {
+               connection_client_stop( msc->conn );
+               msc->conn = NULL;
+       }
+
+       if ( msc->msc_ld != NULL ) {
+
+#ifdef DEBUG_205
+               Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_clear_one_msc ldap_unbind_ext[%d] ld=%p\n",
+                      op ? op->o_log_prefix : "", candidate, (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+               ldap_unbind_ext( msc->msc_ld, NULL, NULL );
+               msc->msc_ld = NULL;
+               ldap_ld_free( msc->msc_ldr, 0, NULL, NULL );
+               msc->msc_ldr = NULL;
+       }
+
+       if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+               ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
+               BER_BVZERO( &msc->msc_bound_ndn );
+       }
+
+       if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+               memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+               ber_memfree_x( msc->msc_cred.bv_val, NULL );
+               BER_BVZERO( &msc->msc_cred );
+       }
+       msc->msc_time = 0;
+       msc->msc_binding_time = 0;
+       msc->msc_result_time = 0;
+       return 0;
+}
+
+void asyncmeta_get_timestamp(char *buf)
+{
+       struct timespec tp;
+       struct tm *ttm;
+       clock_gettime(CLOCK_REALTIME, &tp);
+       ttm = gmtime(&tp.tv_sec);
+       sprintf(buf, "%d:%d:%d.%ld", ttm->tm_hour, ttm->tm_min, ttm->tm_sec, tp.tv_nsec/1000);
+}
+
+int
+asyncmeta_reset_msc(
+       Operation       *op,
+       a_metaconn_t    *mc,
+       int             candidate,
+       int             unbind,
+       const char *caller)
+{
+       a_metasingleconn_t *msc = &mc->mc_conns[candidate];
+       if ( LogTest( asyncmeta_debug ) ) {
+               char    time_buf[ SLAP_TEXT_BUFLEN ];
+               asyncmeta_get_timestamp(time_buf);
+               Debug(asyncmeta_debug, "[%x] Will attempt to reset [%s] msc: %p, "
+                     "msc->msc_binding_time: %x, msc->msc_flags:%x %s\n",
+                     (unsigned int)slap_get_time(), time_buf, msc,
+                     (unsigned int)msc->msc_binding_time, msc->msc_mscflags, caller );
+       }
+       if (msc->msc_active <= 1 && mc->mc_active < 1) {
+               bm_context_t *om;
+               asyncmeta_clear_one_msc(NULL, mc, candidate, 0, caller);
+               /* set whatever's in the queue to invalid, so the timeout loop cleans it up,
+                * but do not invalidate the current op*/
+               LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
+                       if (om->candidates[candidate].sr_msgid >= 0 && (om->op != op)) {
+                               om->bc_invalid = 1;
+                       }
+               }
+               return LDAP_SUCCESS;
+       } else {
+               META_BACK_CONN_INVALID_SET(msc);
+               Debug( asyncmeta_debug, "[%x] Failed to reset msc %p, msc_active=%d, mc_active=%d, %s\n",
+                      (unsigned int)slap_get_time(), msc, msc->msc_active, mc->mc_active, caller );
+       }
+       return LDAP_OTHER;
+}
+
+
+void asyncmeta_log_msc(a_metasingleconn_t *msc)
+{
+       ber_socket_t s = 0;
+       if (msc->msc_ld) {
+               ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+       }
+       Debug( asyncmeta_debug, "msc: %p, msc_ld: %p, msc_ld socket: %d, "
+              "msc_bound_ndn: %s, msc->conn: %p\n", msc, msc->msc_ld,
+              (int)s, msc->msc_bound_ndn.bv_val, msc->conn );
+}
+
+void asyncmeta_log_conns(a_metainfo_t *mi)
+{
+       a_metaconn_t *mc;
+       int i, j;
+       for (i = 0; i < mi->mi_num_conns; i++) {
+               mc = &mi->mi_conns[i];
+               Debug(asyncmeta_debug, "mc: %p, mc->pending_ops: %d\n", mc, mc->pending_ops);
+               for (j = 0; j < mi->mi_ntargets; j++ ) {
+                       asyncmeta_log_msc(&mc->mc_conns[j]);
+               }
+
+       }
+}
index 911a0ab27f4f7e8559495aa5d9dda2ead3b9fa83..e4d46da7b11ab6341f787382207083eb8d2ee50c 100644 (file)
 #include "portable.h"
 
 #include <stdio.h>
-
 #include <ac/string.h>
 #include <ac/socket.h>
-
 #include "slap.h"
-#include "../back-ldap/back-ldap.h"
-#include "back-asyncmeta.h"
 #include "../../../libraries/liblber/lber-int.h"
 #include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
 
 meta_search_candidate_t
 asyncmeta_back_delete_start(Operation *op,
                            SlapReply *rs,
                            a_metaconn_t *mc,
                            bm_context_t *bc,
-                           int candidate)
+                           int candidate,
+                           int do_lock)
 {
        a_metainfo_t    *mi = mc->mc_info;
        a_metatarget_t  *mt = mi->mi_targets[ candidate ];
        struct berval   mdn = BER_BVNULL;
        a_dncookie      dc;
-       int rc = 0, nretries = 1;
+       int rc = 0;
        LDAPControl     **ctrls = NULL;
        meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
        BerElement *ber = NULL;
@@ -52,67 +51,119 @@ asyncmeta_back_delete_start(Operation *op,
        SlapReply               *candidates = bc->candidates;
        ber_int_t       msgid;
 
+       dc.op = op;
        dc.target = mt;
-       dc.conn = op->o_conn;
-       dc.rs = rs;
-       dc.ctx = "deleteDN";
+       dc.memctx = op->o_tmpmemctx;
+       dc.to_from = MASSAGE_REQ;
 
-       if ( asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
-               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-               retcode = META_SEARCH_ERR;
-               goto doreturn;
-       }
+       asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
 
-retry:;
+       asyncmeta_set_msc_time(msc);
        ctrls = op->o_ctrls;
-       if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
+       if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS )
        {
                candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
                retcode = META_SEARCH_ERR;
                goto done;
        }
-
+       /* someone might have reset the connection */
+       if (!( LDAP_BACK_CONN_ISBOUND( msc )
+              || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+               Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+               goto error_unavailable;
+       }
        ber = ldap_build_delete_req( msc->msc_ld, mdn.bv_val, ctrls, NULL, &msgid);
+
+       if (!ber) {
+               Debug( asyncmeta_debug, "%s asyncmeta_back_delete_start: Operation encoding failed with errno %d\n",
+                      op->o_log_prefix, msc->msc_ld->ld_errno );
+               rs->sr_err = LDAP_OPERATIONS_ERROR;
+               rs->sr_text = "Failed to encode proxied request";
+               retcode = META_SEARCH_ERR;
+               goto done;
+       }
+
        if (ber) {
-               candidates[ candidate ].sr_msgid = msgid;
-               rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_DELETE,
-                                               mdn.bv_val, ber, msgid );
-               if (rc == msgid)
-                       rc = LDAP_SUCCESS;
-               else
-                       rc = LDAP_SERVER_DOWN;
+               struct timeval tv = {0, mt->mt_network_timeout*1000};
+               ber_socket_t s;
+               if (!( LDAP_BACK_CONN_ISBOUND( msc )
+                      || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+               if (s < 0) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+               if (rc < 0) {
+                       Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+                       if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+                               rc = LDAP_SERVER_DOWN;
+                       } else {
+                               goto error_unavailable;
+                       }
+               } else {
+                       candidates[ candidate ].sr_msgid = msgid;
+                       rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_DELETE,
+                                                       mdn.bv_val, ber, msgid );
+                       if (rc == msgid)
+                               rc = LDAP_SUCCESS;
+                       else
+                               rc = LDAP_SERVER_DOWN;
+                       ber = NULL;
+               }
 
                switch ( rc ) {
                case LDAP_SUCCESS:
                        retcode = META_SEARCH_CANDIDATE;
                        asyncmeta_set_msc_time(msc);
-                       break;
+                       goto done;
 
                case LDAP_SERVER_DOWN:
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_clear_one_msc(NULL, mc, candidate);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) {
-                               nretries = 0;
-                               /* if the identity changed, there might be need to re-authz */
-                               (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
-                               goto retry;
+                       /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+                       if (do_lock > 0) {
+                               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+                               asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+                               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
                        }
-
+                       /* fall though*/
                default:
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       retcode = META_SEARCH_ERR;
+                       Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
                }
        }
+
+error_unavailable:
+       if (ber)
+               ber_free(ber, 1);
+       switch (bc->nretries[candidate]) {
+       case -1: /* nretries = forever */
+               retcode = META_SEARCH_NEED_BIND;
+               ldap_pvt_thread_yield();
+               break;
+       case 0: /* no retries left */
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               rs->sr_err = LDAP_UNAVAILABLE;
+               rs->sr_text = "Unable to send delete request to target";
+               retcode = META_SEARCH_ERR;
+               break;
+       default: /* more retries left - try to rebind and go again */
+               retcode = META_SEARCH_NEED_BIND;
+               bc->nretries[candidate]--;
+               ldap_pvt_thread_yield();
+               break;
+       }
 done:
        (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
 
        if ( mdn.bv_val != op->o_req_dn.bv_val ) {
-               free( mdn.bv_val );
-               BER_BVZERO( &mdn );
+               op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
        }
 
-doreturn:;
        Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_delete_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
        return retcode;
 }
@@ -124,26 +175,32 @@ asyncmeta_back_delete( Operation *op, SlapReply *rs )
        a_metatarget_t  *mt;
        a_metaconn_t    *mc;
        int             rc, candidate = -1;
-       OperationBuffer opbuf;
+       void *thrctx = op->o_threadctx;
        bm_context_t *bc;
        SlapReply *candidates;
-       slap_callback *cb = op->o_callback;
+       time_t current_time = slap_get_time();
+
+       int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
 
-       Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_delete: %s\n",
+       Debug(LDAP_DEBUG_TRACE, "==> asyncmeta_back_delete: %s\n",
              op->o_req_dn.bv_val );
 
-       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
+       if (current_time > op->o_time) {
+               Debug(asyncmeta_debug, "==> asyncmeta_back_delete[%s]: o_time:[%ld], current time: [%ld]\n",
+                     op->o_log_prefix, op->o_time, current_time );
+       }
+
+       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
        if (bc == NULL) {
                rs->sr_err = LDAP_OTHER;
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
        candidates = bc->candidates;
        mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
        if ( !mc || rs->sr_err != LDAP_SUCCESS) {
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
@@ -152,16 +209,38 @@ asyncmeta_back_delete( Operation *op, SlapReply *rs )
        bc->retrying = LDAP_BACK_RETRYING;
        bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
        bc->stoptime = op->o_time + bc->timeout;
+       bc->bc_active = 1;
+
+       if (mc->pending_ops >= max_pending_ops) {
+               rs->sr_err = LDAP_BUSY;
+               rs->sr_text = "Maximum pending ops limit exceeded";
+               send_ldap_result(op, rs);
+               return rs->sr_err;
+       }
 
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
        rc = asyncmeta_add_message_queue(mc, bc);
+       mc->mc_conns[candidate].msc_active++;
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
 
        if (rc != LDAP_SUCCESS) {
                rs->sr_err = LDAP_BUSY;
                rs->sr_text = "Maximum pending ops limit exceeded";
-               asyncmeta_clear_bm_context(bc);
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+               mc->mc_conns[candidate].msc_active--;
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+               goto finish;
+       }
+
+retry:
+       if (bc->timeout && bc->stoptime < slap_get_time()) {
+               int             timeout_err;
+               timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+                       LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+               rs->sr_err = timeout_err;
+               rs->sr_text = "Operation timed out before it was sent to target";
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
        }
 
@@ -171,47 +250,27 @@ asyncmeta_back_delete( Operation *op, SlapReply *rs )
        case META_SEARCH_CANDIDATE:
                /* target is already bound, just send the request */
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete:  "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
 
-               rc = asyncmeta_back_delete_start( op, rs, mc, bc, candidate);
+               rc = asyncmeta_back_delete_start( op, rs, mc, bc, candidate, 1);
                if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
 
+               } else if (rc == META_SEARCH_NEED_BIND) {
+                       goto retry;
                }
-                       break;
+               break;
        case META_SEARCH_NOT_CANDIDATE:
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete: NOT_CANDIDATE "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
-               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-               asyncmeta_drop_bc(mc, bc);
-               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
 
        case META_SEARCH_NEED_BIND:
-       case META_SEARCH_CONNECTING:
-               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete: NEED_BIND "
-                      "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
-               rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
-               if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
-                       goto finish;
-               }
-               break;
        case META_SEARCH_BINDING:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete: BINDING "
-                              "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+                              "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
                        /* Todo add the context to the message queue but do not send the request
                           the receiver must send this when we are done binding */
                        /* question - how would do receiver know to which targets??? */
@@ -219,22 +278,21 @@ asyncmeta_back_delete( Operation *op, SlapReply *rs )
 
        case META_SEARCH_ERR:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete: ERR "
-                              "cnd=\"%ldd\"\n", op->o_log_prefix, candidate );
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       candidates[ candidate ].sr_type = REP_RESULT;
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                              "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
                default:
                        assert( 0 );
                        break;
                }
+
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+       mc->mc_conns[candidate].msc_active--;
        asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+       bc->bc_active--;
+       asyncmeta_memctx_toggle(thrctx);
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+       rs->sr_err = SLAPD_ASYNCOP;
 finish:
        return rs->sr_err;
 }
index f4e4658d9c60af7ad82f59521f3682087dd29372..1b03aad52ff7ada5bfeb52cc34852bd96a78e044 100644 (file)
@@ -32,6 +32,8 @@
 #include "../back-ldap/back-ldap.h"
 #include "back-asyncmeta.h"
 
+int asyncmeta_debug;
+
 int
 asyncmeta_back_open(
        BackendInfo     *bi )
@@ -46,6 +48,14 @@ int
 asyncmeta_back_initialize(
        BackendInfo     *bi )
 {
+       int rc;
+       struct berval debugbv = BER_BVC("asyncmeta");
+
+       rc = slap_loglevel_get( &debugbv, &asyncmeta_debug );
+       if ( rc ) {
+               return rc;
+       }
+
        bi->bi_flags =
 #if 0
        /* this is not (yet) set essentially because back-meta does not
@@ -89,7 +99,7 @@ asyncmeta_back_initialize(
        bi->bi_chk_referrals = 0;
 
        bi->bi_connection_init = 0;
-       bi->bi_connection_destroy = asyncmeta_back_conn_destroy;
+       bi->bi_connection_destroy =  0 /* asyncmeta_back_conn_destroy */;
 
        return asyncmeta_back_init_cf( bi );
 }
@@ -164,9 +174,7 @@ asyncmeta_target_finish(
 )
 {
        slap_bindconf   sb = { BER_BVNULL };
-       struct berval mapped;
        int rc;
-       int msc_num, i;
 
        ber_str2bv( mt->mt_uri, 0, 0, &sb.sb_uri );
        sb.sb_version = mt->mt_version;
@@ -216,21 +224,6 @@ asyncmeta_target_finish(
                mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_NOANON;
        }
 
-       BER_BVZERO( &mapped );
-       asyncmeta_map( &mt->mt_rwmap.rwm_at,
-               &slap_schema.si_ad_entryDN->ad_cname, &mapped,
-               BACKLDAP_REMAP );
-       if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
-               mt->mt_rep_flags |= REP_NO_ENTRYDN;
-       }
-
-       BER_BVZERO( &mapped );
-       asyncmeta_map( &mt->mt_rwmap.rwm_at,
-               &slap_schema.si_ad_subschemaSubentry->ad_cname, &mapped,
-               BACKLDAP_REMAP );
-       if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
-               mt->mt_rep_flags |= REP_NO_SUBSCHEMA;
-       }
        return 0;
 }
 
@@ -240,10 +233,8 @@ asyncmeta_back_db_open(
        ConfigReply     *cr )
 {
        a_metainfo_t    *mi = (a_metainfo_t *)be->be_private;
-
        char msg[SLAP_TEXT_BUFLEN];
-
-       int             i, rc;
+       int             i;
 
        if ( mi->mi_ntargets == 0 ) {
                /* Dynamically added, nothing to check here until
@@ -272,11 +263,12 @@ asyncmeta_back_db_open(
                mc->mc_authz_target = META_BOUND_NONE;
                mc->mc_conns = ch_calloc( mi->mi_ntargets, sizeof( a_metasingleconn_t ));
                mc->mc_info = mi;
+               LDAP_STAILQ_INIT( &mc->mc_om_list );
        }
-
+       mi->mi_suffix = be->be_suffix[0];
        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
        mi->mi_task = ldap_pvt_runqueue_insert( &slapd_rq, 0,
-               asyncmeta_timeout_loop, mi, "asyncmeta_timeout_loop", be->be_suffix[0].bv_val );
+               asyncmeta_timeout_loop, mi, "asyncmeta_timeout_loop", mi->mi_suffix.bv_val );
        ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
        return 0;
 }
@@ -298,36 +290,6 @@ asyncmeta_back_conn_free(
        free( mc );
 }
 
-static void
-mapping_free(
-       void            *v_mapping )
-{
-       struct ldapmapping *mapping = v_mapping;
-       ch_free( mapping->src.bv_val );
-       ch_free( mapping->dst.bv_val );
-       ch_free( mapping );
-}
-
-static void
-mapping_dst_free(
-       void            *v_mapping )
-{
-       struct ldapmapping *mapping = v_mapping;
-
-       if ( BER_BVISEMPTY( &mapping->dst ) ) {
-               mapping_free( &mapping[ -1 ] );
-       }
-}
-
-void
-asyncmeta_back_map_free( struct ldapmap *lm )
-{
-       avl_free( lm->remap, mapping_dst_free );
-       avl_free( lm->map, mapping_free );
-       lm->remap = NULL;
-       lm->map = NULL;
-}
-
 static void
 asyncmeta_back_stop_miconns( a_metainfo_t *mi )
 {
@@ -344,7 +306,7 @@ asyncmeta_back_clear_miconns( a_metainfo_t *mi )
                mc = &mi->mi_conns[i];
                /* todo clear the message queue */
                for (j = 0; j < mi->mi_ntargets; j ++) {
-                       asyncmeta_clear_one_msc(NULL, mc, j);
+                       asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
                }
                free(mc->mc_conns);
                ldap_pvt_thread_mutex_destroy( &mc->mc_om_mutex );
@@ -401,14 +363,12 @@ asyncmeta_target_free(
        if ( mt->mt_idassert_authz != NULL ) {
                ber_bvarray_free( mt->mt_idassert_authz );
        }
-       if ( mt->mt_rwmap.rwm_rw ) {
-               rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
-               if ( mt->mt_rwmap.rwm_bva_rewrite )
-                       ber_bvarray_free( mt->mt_rwmap.rwm_bva_rewrite );
+       if ( !BER_BVISNULL( &mt->mt_lsuffixm )) {
+               ch_free( mt->mt_lsuffixm.bv_val );
+       }
+       if ( !BER_BVISNULL( &mt->mt_rsuffixm )) {
+               ch_free( mt->mt_rsuffixm.bv_val );
        }
-       asyncmeta_back_map_free( &mt->mt_rwmap.rwm_oc );
-       asyncmeta_back_map_free( &mt->mt_rwmap.rwm_at );
-       ber_bvarray_free( mt->mt_rwmap.rwm_bva_map );
        free( mt );
 }
 
@@ -420,8 +380,6 @@ asyncmeta_back_db_close(
        a_metainfo_t    *mi;
 
        if ( be->be_private ) {
-               int i;
-
                mi = ( a_metainfo_t * )be->be_private;
                if ( mi->mi_task != NULL ) {
                        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
@@ -435,6 +393,7 @@ asyncmeta_back_db_close(
                asyncmeta_back_stop_miconns( mi );
                ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
        }
+       return 0;
 }
 
 int
@@ -486,13 +445,14 @@ asyncmeta_back_db_destroy(
                if ( META_BACK_QUARANTINE( mi ) ) {
                        mi->mi_ldap_extra->retry_info_destroy( &mi->mi_quarantine );
                }
-       }
-       ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
-       asyncmeta_back_clear_miconns(mi);
-       ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
-       ldap_pvt_thread_mutex_destroy( &mi->mi_mc_mutex );
 
-       free( be->be_private );
+               ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
+               asyncmeta_back_clear_miconns(mi);
+               ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
+               ldap_pvt_thread_mutex_destroy( &mi->mi_mc_mutex );
+
+               free( be->be_private );
+       }
        return 0;
 }
 
index 83a332f097b38284025de2d1a95e36f16dd2f223..833c50d5ac15e70bbbf3c77c3ed399990911fb55 100644 (file)
 #include "../back-ldap/back-ldap.h"
 #include "back-asyncmeta.h"
 
-int
-asyncmeta_mapping_cmp ( const void *c1, const void *c2 )
-{
-       struct ldapmapping *map1 = (struct ldapmapping *)c1;
-       struct ldapmapping *map2 = (struct ldapmapping *)c2;
-       int rc = map1->src.bv_len - map2->src.bv_len;
-       if (rc) return rc;
-       return ( strcasecmp( map1->src.bv_val, map2->src.bv_val ) );
-}
-
-int
-asyncmeta_mapping_dup ( void *c1, void *c2 )
-{
-       struct ldapmapping *map1 = (struct ldapmapping *)c1;
-       struct ldapmapping *map2 = (struct ldapmapping *)c2;
-
-       return ( ( strcasecmp( map1->src.bv_val, map2->src.bv_val ) == 0 ) ? -1 : 0 );
-}
-
 void
-asyncmeta_map_init ( struct ldapmap *lm, struct ldapmapping **m )
-{
-       struct ldapmapping *mapping;
-
-       assert( m != NULL );
-
-       *m = NULL;
-
-       mapping = (struct ldapmapping *)ch_calloc( 2,
-                       sizeof( struct ldapmapping ) );
-       if ( mapping == NULL ) {
-               return;
-       }
-
-       ber_str2bv( "objectclass", STRLENOF("objectclass"), 1, &mapping[0].src);
-       ber_dupbv( &mapping[0].dst, &mapping[0].src );
-       mapping[1].src = mapping[0].src;
-       mapping[1].dst = mapping[0].dst;
-
-       avl_insert( &lm->map, (caddr_t)&mapping[0],
-                       asyncmeta_mapping_cmp, asyncmeta_mapping_dup );
-       avl_insert( &lm->remap, (caddr_t)&mapping[1],
-                       asyncmeta_mapping_cmp, asyncmeta_mapping_dup );
-       *m = mapping;
-}
-
-int
-asyncmeta_mapping ( struct ldapmap *map, struct berval *s, struct ldapmapping **m,
-       int remap )
-{
-       Avlnode *tree;
-       struct ldapmapping fmapping;
-
-       assert( m != NULL );
-
-       /* let special attrnames slip through (ITS#5760) */
-       if ( bvmatch( s, slap_bv_no_attrs )
-               || bvmatch( s, slap_bv_all_user_attrs )
-               || bvmatch( s, slap_bv_all_operational_attrs ) )
-       {
-               *m = NULL;
-               return 0;
-       }
-
-       if ( remap == BACKLDAP_REMAP ) {
-               tree = map->remap;
-
-       } else {
-               tree = map->map;
-       }
-
-       fmapping.src = *s;
-       *m = (struct ldapmapping *)avl_find( tree, (caddr_t)&fmapping, asyncmeta_mapping_cmp );
-       if ( *m == NULL ) {
-               return map->drop_missing;
-       }
-
-       return 0;
-}
-
-void
-asyncmeta_map ( struct ldapmap *map, struct berval *s, struct berval *bv,
-       int remap )
-{
-       struct ldapmapping *mapping;
-       int drop_missing;
-
-       /* map->map may be NULL when mapping is configured,
-        * but map->remap can't */
-       if ( map->remap == NULL ) {
-               *bv = *s;
-               return;
-       }
-
-       BER_BVZERO( bv );
-       drop_missing = asyncmeta_mapping( map, s, &mapping, remap );
-       if ( mapping != NULL ) {
-               if ( !BER_BVISNULL( &mapping->dst ) ) {
-                       *bv = mapping->dst;
-               }
-               return;
-       }
-
-       if ( !drop_missing ) {
-               *bv = *s;
-       }
-}
-
-int
-asyncmeta_map_attrs(
-               Operation *op,
-               struct ldapmap *at_map,
-               AttributeName *an,
-               int remap,
-               char ***mapped_attrs )
-{
-       int i, x, j;
-       char **na;
-       struct berval mapped;
-
-       if ( an == NULL && op->o_bd->be_extra_anlist == NULL ) {
-               *mapped_attrs = NULL;
-               return LDAP_SUCCESS;
-       }
-
-       i = 0;
-       if ( an != NULL ) {
-               for ( ; !BER_BVISNULL( &an[i].an_name ); i++ )
-                       /*  */ ;
-       }
-
-       x = 0;
-       if ( op->o_bd->be_extra_anlist != NULL ) {
-               for ( ; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ )
-                       /*  */ ;
-       }
-
-       assert( i > 0 || x > 0 );
-
-       na = (char **)ber_memcalloc_x( i + x + 1, sizeof(char *), op->o_tmpmemctx );
-       if ( na == NULL ) {
-               *mapped_attrs = NULL;
-               return LDAP_NO_MEMORY;
-       }
-
-       j = 0;
-       if ( i > 0 ) {
-               for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
-                       asyncmeta_map( at_map, &an[i].an_name, &mapped, remap );
-                       if ( !BER_BVISNULL( &mapped ) && !BER_BVISEMPTY( &mapped ) ) {
-                               na[j++] = mapped.bv_val;
-                       }
-               }
-       }
-
-       if ( x > 0 ) {
-               for ( x = 0; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ ) {
-                       if ( op->o_bd->be_extra_anlist[x].an_desc &&
-                               ad_inlist( op->o_bd->be_extra_anlist[x].an_desc, an ) )
-                       {
-                               continue;
-                       }
-
-                       asyncmeta_map( at_map, &op->o_bd->be_extra_anlist[x].an_name, &mapped, remap );
-                       if ( !BER_BVISNULL( &mapped ) && !BER_BVISEMPTY( &mapped ) ) {
-                               na[j++] = mapped.bv_val;
-                       }
-               }
-       }
-
-       if ( j == 0 && ( i > 0 || x > 0 ) ) {
-               na[j++] = LDAP_NO_ATTRS;
-       }
-       na[j] = NULL;
-
-       *mapped_attrs = na;
-
-       return LDAP_SUCCESS;
-}
-
-static int
-map_attr_value(
-               a_dncookie              *dc,
-               AttributeDescription    *ad,
-               struct berval           *mapped_attr,
-               struct berval           *value,
-               struct berval           *mapped_value,
-               int                     remap,
-               void                    *memctx )
-{
-       struct berval           vtmp;
-       int                     freeval = 0;
-
-       asyncmeta_map( &dc->target->mt_rwmap.rwm_at, &ad->ad_cname, mapped_attr, remap );
-       if ( BER_BVISNULL( mapped_attr ) || BER_BVISEMPTY( mapped_attr ) ) {
-#if 0
-               /*
-                * FIXME: are we sure we need to search oc_map if at_map fails?
-                */
-               asyncmeta_map( &dc->target->mt_rwmap.rwm_oc, &ad->ad_cname, mapped_attr, remap );
-               if ( BER_BVISNULL( mapped_attr ) || BER_BVISEMPTY( mapped_attr ) ) {
-                       *mapped_attr = ad->ad_cname;
-               }
-#endif
-               if ( dc->target->mt_rwmap.rwm_at.drop_missing ) {
-                       return -1;
-               }
-
-               *mapped_attr = ad->ad_cname;
-       }
-
-       if ( value == NULL ) {
-               return 0;
-       }
-
-       if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
-       {
-               a_dncookie fdc = *dc;
-
-               fdc.ctx = "searchFilterAttrDN";
-
-               switch ( asyncmeta_dn_massage( &fdc, value, &vtmp ) ) {
-               case LDAP_SUCCESS:
-                       if ( vtmp.bv_val != value->bv_val ) {
-                               freeval = 1;
-                       }
-                       break;
-
-               case LDAP_UNWILLING_TO_PERFORM:
-                       return -1;
-
-               case LDAP_OTHER:
-                       return -1;
-               }
-
-       } else if ( ad->ad_type->sat_equality &&
-               ad->ad_type->sat_equality->smr_usage & SLAP_MR_MUTATION_NORMALIZER )
-       {
-               if ( ad->ad_type->sat_equality->smr_normalize(
-                       (SLAP_MR_DENORMALIZE|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX),
-                       NULL, NULL, value, &vtmp, memctx ) )
-               {
-                       return -1;
-               }
-               freeval = 2;
-
-       } else if ( ad == slap_schema.si_ad_objectClass || ad == slap_schema.si_ad_structuralObjectClass ) {
-               asyncmeta_map( &dc->target->mt_rwmap.rwm_oc, value, &vtmp, remap );
-               if ( BER_BVISNULL( &vtmp ) || BER_BVISEMPTY( &vtmp ) ) {
-                       vtmp = *value;
-               }
-
-       } else {
-               vtmp = *value;
-       }
-
-       filter_escape_value_x( &vtmp, mapped_value, memctx );
-
-       switch ( freeval ) {
-       case 1:
-               ber_memfree( vtmp.bv_val );
-               break;
-       case 2:
-               ber_memfree_x( vtmp.bv_val, memctx );
-               break;
-       }
-
-       return 0;
-}
-
-static int
-asyncmeta_int_filter_map_rewrite(
-               a_dncookie              *dc,
-               Filter                  *f,
-               struct berval   *fstr,
-               int                             remap,
-               void                    *memctx )
-{
-       int             i;
-       Filter          *p;
-       struct berval   atmp,
-                       vtmp,
-                       *tmp;
-       static struct berval
-                       /* better than nothing... */
-                       ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
-                       ber_bvtf_false = BER_BVC( "(|)" ),
-                       /* better than nothing... */
-                       ber_bvtrue = BER_BVC( "(objectClass=*)" ),
-                       ber_bvtf_true = BER_BVC( "(&)" ),
-#if 0
-                       /* no longer needed; preserved for completeness */
-                       ber_bvundefined = BER_BVC( "(?=undefined)" ),
-#endif
-                       ber_bverror = BER_BVC( "(?=error)" ),
-                       ber_bvunknown = BER_BVC( "(?=unknown)" ),
-                       ber_bvnone = BER_BVC( "(?=none)" );
-       ber_len_t       len;
-
-       assert( fstr != NULL );
-       BER_BVZERO( fstr );
-
-       if ( f == NULL ) {
-               ber_dupbv_x( fstr, &ber_bvnone, memctx );
-               return LDAP_OTHER;
-       }
-
-       switch ( ( f->f_choice & SLAPD_FILTER_MASK ) ) {
-       case LDAP_FILTER_EQUALITY:
-               if ( map_attr_value( dc, f->f_av_desc, &atmp,
-                                       &f->f_av_value, &vtmp, remap, memctx ) )
-               {
-                       goto computed;
-               }
-
-               fstr->bv_len = atmp.bv_len + vtmp.bv_len
-                       + ( sizeof("(=)") - 1 );
-               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
-
-               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
-                       atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
-
-               ber_memfree_x( vtmp.bv_val, memctx );
-               break;
-
-       case LDAP_FILTER_GE:
-               if ( map_attr_value( dc, f->f_av_desc, &atmp,
-                                       &f->f_av_value, &vtmp, remap, memctx ) )
-               {
-                       goto computed;
-               }
-
-               fstr->bv_len = atmp.bv_len + vtmp.bv_len
-                       + ( sizeof("(>=)") - 1 );
-               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
-
-               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
-                       atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
-
-               ber_memfree_x( vtmp.bv_val, memctx );
-               break;
-
-       case LDAP_FILTER_LE:
-               if ( map_attr_value( dc, f->f_av_desc, &atmp,
-                                       &f->f_av_value, &vtmp, remap, memctx ) )
-               {
-                       goto computed;
-               }
-
-               fstr->bv_len = atmp.bv_len + vtmp.bv_len
-                       + ( sizeof("(<=)") - 1 );
-               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
-
-               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
-                       atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
-
-               ber_memfree_x( vtmp.bv_val, memctx );
-               break;
-
-       case LDAP_FILTER_APPROX:
-               if ( map_attr_value( dc, f->f_av_desc, &atmp,
-                                       &f->f_av_value, &vtmp, remap, memctx ) )
-               {
-                       goto computed;
-               }
-
-               fstr->bv_len = atmp.bv_len + vtmp.bv_len
-                       + ( sizeof("(~=)") - 1 );
-               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
-
-               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
-                       atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
-
-               ber_memfree_x( vtmp.bv_val, memctx );
-               break;
-
-       case LDAP_FILTER_SUBSTRINGS:
-               if ( map_attr_value( dc, f->f_sub_desc, &atmp,
-                                       NULL, NULL, remap, memctx ) )
-               {
-                       goto computed;
-               }
-
-               /* cannot be a DN ... */
-
-               fstr->bv_len = atmp.bv_len + ( STRLENOF( "(=*)" ) );
-               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 128, memctx ); /* FIXME: why 128 ? */
-
-               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
-                       atmp.bv_val );
-
-               if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
-                       len = fstr->bv_len;
-
-                       filter_escape_value_x( &f->f_sub_initial, &vtmp, memctx );
-
-                       fstr->bv_len += vtmp.bv_len;
-                       fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
-
-                       snprintf( &fstr->bv_val[len - 2], vtmp.bv_len + 3,
-                               /* "(attr=" */ "%s*)",
-                               vtmp.bv_len ? vtmp.bv_val : "" );
-
-                       ber_memfree_x( vtmp.bv_val, memctx );
-               }
-
-               if ( f->f_sub_any != NULL ) {
-                       for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
-                               len = fstr->bv_len;
-                               filter_escape_value_x( &f->f_sub_any[i], &vtmp, memctx );
-
-                               fstr->bv_len += vtmp.bv_len + 1;
-                               fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
-
-                               snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
-                                       /* "(attr=[init]*[any*]" */ "%s*)",
-                                       vtmp.bv_len ? vtmp.bv_val : "" );
-                               ber_memfree_x( vtmp.bv_val, memctx );
-                       }
-               }
-
-               if ( !BER_BVISNULL( &f->f_sub_final ) ) {
-                       len = fstr->bv_len;
-
-                       filter_escape_value_x( &f->f_sub_final, &vtmp, memctx );
-
-                       fstr->bv_len += vtmp.bv_len;
-                       fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
-
-                       snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
-                               /* "(attr=[init*][any*]" */ "%s)",
-                               vtmp.bv_len ? vtmp.bv_val : "" );
-
-                       ber_memfree_x( vtmp.bv_val, memctx );
-               }
-
-               break;
-
-       case LDAP_FILTER_PRESENT:
-               if ( map_attr_value( dc, f->f_desc, &atmp,
-                                       NULL, NULL, remap, memctx ) )
-               {
-                       goto computed;
-               }
-
-               fstr->bv_len = atmp.bv_len + ( STRLENOF( "(=*)" ) );
-               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
-
-               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
-                       atmp.bv_val );
-               break;
-
-       case LDAP_FILTER_AND:
-       case LDAP_FILTER_OR:
-       case LDAP_FILTER_NOT:
-               fstr->bv_len = STRLENOF( "(%)" );
-               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 128, memctx );    /* FIXME: why 128? */
-
-               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
-                       f->f_choice == LDAP_FILTER_AND ? '&' :
-                       f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
-
-               for ( p = f->f_list; p != NULL; p = p->f_next ) {
-                       int     rc;
-
-                       len = fstr->bv_len;
-
-                       rc = asyncmeta_int_filter_map_rewrite( dc, p, &vtmp, remap, memctx );
-                       if ( rc != LDAP_SUCCESS ) {
-                               return rc;
-                       }
-
-                       fstr->bv_len += vtmp.bv_len;
-                       fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
-
-                       snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
-                               /*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
-
-                       ber_memfree_x( vtmp.bv_val, memctx );
-               }
-
-               break;
-
-       case LDAP_FILTER_EXT:
-               if ( f->f_mr_desc ) {
-                       if ( map_attr_value( dc, f->f_mr_desc, &atmp,
-                                               &f->f_mr_value, &vtmp, remap, memctx ) )
-                       {
-                               goto computed;
-                       }
-
-               } else {
-                       BER_BVSTR( &atmp, "" );
-                       filter_escape_value_x( &f->f_mr_value, &vtmp, memctx );
-               }
-
-               /* FIXME: cleanup (less ?: operators...) */
-               fstr->bv_len = atmp.bv_len +
-                       ( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
-                       ( !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
-                       vtmp.bv_len + ( STRLENOF( "(:=)" ) );
-               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
-
-               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
-                       atmp.bv_val,
-                       f->f_mr_dnattrs ? ":dn" : "",
-                       !BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
-                       !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
-                       vtmp.bv_len ? vtmp.bv_val : "" );
-               ber_memfree_x( vtmp.bv_val, memctx );
-               break;
-
-       case SLAPD_FILTER_COMPUTED:
-               switch ( f->f_result ) {
-               /* FIXME: treat UNDEFINED as FALSE */
-               case SLAPD_COMPARE_UNDEFINED:
-computed:;
-                       if ( META_BACK_TGT_NOUNDEFFILTER( dc->target ) ) {
-                               return LDAP_COMPARE_FALSE;
-                       }
-                       /* fallthru */
-
-               case LDAP_COMPARE_FALSE:
-                       if ( META_BACK_TGT_T_F( dc->target ) ) {
-                               tmp = &ber_bvtf_false;
-                               break;
-                       }
-                       tmp = &ber_bvfalse;
-                       break;
-
-               case LDAP_COMPARE_TRUE:
-                       if ( META_BACK_TGT_T_F( dc->target ) ) {
-                               tmp = &ber_bvtf_true;
-                               break;
-                       }
-
-                       tmp = &ber_bvtrue;
-                       break;
-
-               default:
-                       tmp = &ber_bverror;
-                       break;
-               }
-
-               ber_dupbv_x( fstr, tmp, memctx );
-               break;
-
-       default:
-               ber_dupbv_x( fstr, &ber_bvunknown, memctx );
-               break;
-       }
-
-       return 0;
-}
-
-int
-asyncmeta_filter_map_rewrite(
-               a_dncookie              *dc,
-               Filter                  *f,
-               struct berval   *fstr,
-               int                             remap,
-               void                    *memctx )
-{
-       int             rc;
-       a_dncookie      fdc;
-       struct berval   ftmp;
-       static char     *dmy = "";
-
-       rc = asyncmeta_int_filter_map_rewrite( dc, f, fstr, remap, memctx );
-
-       if ( rc != LDAP_SUCCESS ) {
-               return rc;
-       }
-
-       fdc = *dc;
-       ftmp = *fstr;
-
-       fdc.ctx = "searchFilter";
-
-       switch ( rewrite_session( fdc.target->mt_rwmap.rwm_rw, fdc.ctx,
-                               ( !BER_BVISEMPTY( &ftmp ) ? ftmp.bv_val : dmy ),
-                               fdc.conn, &fstr->bv_val ) )
-       {
-       case REWRITE_REGEXEC_OK:
-               if ( !BER_BVISNULL( fstr ) ) {
-                       fstr->bv_len = strlen( fstr->bv_val );
-
-               } else {
-                       *fstr = ftmp;
-               }
-               Debug( LDAP_DEBUG_ARGS,
-                       "[rw] %s: \"%s\" -> \"%s\"\n",
-                       fdc.ctx, BER_BVISNULL( &ftmp ) ? "" : ftmp.bv_val,
-                       BER_BVISNULL( fstr ) ? "" : fstr->bv_val );
-               rc = LDAP_SUCCESS;
-               break;
-
-       case REWRITE_REGEXEC_UNWILLING:
-               if ( fdc.rs ) {
-                       fdc.rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-                       fdc.rs->sr_text = "Operation not allowed";
-               }
-               rc = LDAP_UNWILLING_TO_PERFORM;
-               break;
-
-       case REWRITE_REGEXEC_ERR:
-               if ( fdc.rs ) {
-                       fdc.rs->sr_err = LDAP_OTHER;
-                       fdc.rs->sr_text = "Rewrite error";
-               }
-               rc = LDAP_OTHER;
-               break;
-       }
-
-       if ( fstr->bv_val == dmy ) {
-               BER_BVZERO( fstr );
-
-       } else if ( fstr->bv_val != ftmp.bv_val ) {
-               /* NOTE: need to realloc mapped filter on slab
-                * and free the original one, until librewrite
-                * becomes slab-aware
-                */
-               ber_dupbv_x( &ftmp, fstr, memctx );
-               ch_free( fstr->bv_val );
-               *fstr = ftmp;
-       }
-
-       return rc;
-}
-
-int
 asyncmeta_referral_result_rewrite(
        a_dncookie              *dc,
-       BerVarray               a_vals,
-       void                    *memctx
+       BerVarray               a_vals
 )
 {
        int             i, last;
@@ -731,144 +100,115 @@ asyncmeta_referral_result_rewrite(
 
                ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
 
-               rc = asyncmeta_dn_massage( dc, &olddn, &dn );
-               switch ( rc ) {
-               case LDAP_UNWILLING_TO_PERFORM:
-                       /*
-                        * FIXME: need to check if it may be considered
-                        * legal to trim values when adding/modifying;
-                        * it should be when searching (e.g. ACLs).
-                        */
-                       ber_memfree( a_vals[ i ].bv_val );
-                       if ( last > i ) {
-                               a_vals[ i ] = a_vals[ last ];
-                       }
-                       BER_BVZERO( &a_vals[ last ] );
-                       last--;
-                       i--;
-                       break;
+               asyncmeta_dn_massage( dc, &olddn, &dn );
+               /* leave attr untouched if massage did nothing */
+               if ( olddn.bv_val != dn.bv_val )
+               {
+                       char    *newurl;
 
-               default:
-                       /* leave attr untouched if massage failed */
-                       if ( !BER_BVISNULL( &dn ) && olddn.bv_val != dn.bv_val )
+                       ludp->lud_dn = dn.bv_val;
+                       newurl = ldap_url_desc2str( ludp );
+                       dc->op->o_tmpfree( dn.bv_val, dc->memctx );
+                       if ( newurl )
                        {
-                               char    *newurl;
-
-                               ludp->lud_dn = dn.bv_val;
-                               newurl = ldap_url_desc2str( ludp );
-                               free( dn.bv_val );
-                               if ( newurl == NULL ) {
-                                       /* FIXME: leave attr untouched
-                                        * even if ldap_url_desc2str failed...
-                                        */
-                                       break;
-                               }
+                               /* FIXME: leave attr untouched
+                                * even if ldap_url_desc2str failed...
+                                */
 
-                               ber_memfree_x( a_vals[ i ].bv_val, memctx );
-                               ber_str2bv_x( newurl, 0, 1, &a_vals[ i ], memctx );
+                               ber_memfree_x( a_vals[ i ].bv_val, dc->op->o_tmpmemctx );
+                               ber_str2bv_x( newurl, 0, 1, &a_vals[ i ], dc->memctx );
                                ber_memfree( newurl );
                                ludp->lud_dn = olddn.bv_val;
                        }
-                       break;
                }
-
                ldap_free_urldesc( ludp );
        }
-
-       return 0;
 }
 
-/*
- * I don't like this much, but we need two different
- * functions because different heap managers may be
- * in use in back-ldap/meta to reduce the amount of
- * calls to malloc routines, and some of the free()
- * routines may be macros with args
- */
-int
-asyncmeta_dnattr_rewrite(
+void
+asyncmeta_dnattr_result_rewrite(
        a_dncookie              *dc,
        BerVarray               a_vals
 )
 {
        struct berval   bv;
-       int             i, last;
+       int             i;
 
        assert( a_vals != NULL );
 
-       for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
-               ;
-       last--;
-
        for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
-               switch ( asyncmeta_dn_massage( dc, &a_vals[i], &bv ) ) {
-               case LDAP_UNWILLING_TO_PERFORM:
-                       /*
-                        * FIXME: need to check if it may be considered
-                        * legal to trim values when adding/modifying;
-                        * it should be when searching (e.g. ACLs).
-                        */
-                       ch_free( a_vals[i].bv_val );
-                       if ( last > i ) {
-                               a_vals[i] = a_vals[last];
-                       }
-                       BER_BVZERO( &a_vals[last] );
-                       last--;
-                       break;
-
-               default:
-                       /* leave attr untouched if massage failed */
-                       if ( !BER_BVISNULL( &bv ) && bv.bv_val != a_vals[i].bv_val ) {
-                               ch_free( a_vals[i].bv_val );
-                               a_vals[i] = bv;
-                       }
-                       break;
+               asyncmeta_dn_massage( dc, &a_vals[i], &bv );
+               if ( bv.bv_val != a_vals[i].bv_val ) {
+                       ber_memfree_x( a_vals[i].bv_val, dc->memctx );
+                       a_vals[i] = bv;
                }
        }
-
-       return 0;
 }
 
-int
-asyncmeta_dnattr_result_rewrite(
-       a_dncookie              *dc,
-       BerVarray               a_vals
+/*
+ * asyncmeta_dn_massage
+ *
+ * Aliases the suffix.
+ */
+void
+asyncmeta_dn_massage(
+       a_dncookie *dc,
+       struct berval *odn,
+       struct berval *res
 )
 {
-       struct berval   bv;
-       int             i, last;
+       struct berval pretty = {0,NULL}, *dn = odn;
+       struct berval *osuff, *nsuff;
+       int diff;
 
-       assert( a_vals != NULL );
+       assert( res );
 
-       for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
-               ;
-       last--;
+       BER_BVZERO(res);
+       if ( dn == NULL )
+               return;
 
-       for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
-               switch ( asyncmeta_dn_massage( dc, &a_vals[i], &bv ) ) {
-               case LDAP_UNWILLING_TO_PERFORM:
-                       /*
-                        * FIXME: need to check if it may be considered
-                        * legal to trim values when adding/modifying;
-                        * it should be when searching (e.g. ACLs).
-                        */
-                       ber_memfree( a_vals[i].bv_val );
-                       if ( last > i ) {
-                               a_vals[i] = a_vals[last];
-                       }
-                       BER_BVZERO( &a_vals[last] );
-                       last--;
-                       break;
+       /* no suffix massage configured */
+       if ( !dc->target->mt_lsuffixm.bv_val ) {
+               *res = *dn;
+               return;
+       }
 
-               default:
-                       /* leave attr untouched if massage failed */
-                       if ( !BER_BVISNULL( &bv ) && a_vals[i].bv_val != bv.bv_val ) {
-                               ber_memfree( a_vals[i].bv_val );
-                               a_vals[i] = bv;
-                       }
-                       break;
-               }
+       if ( dc->to_from == MASSAGE_REQ ) {
+               osuff = &dc->target->mt_lsuffixm;
+               nsuff = &dc->target->mt_rsuffixm;
+       } else {
+               osuff = &dc->target->mt_rsuffixm;
+               nsuff = &dc->target->mt_lsuffixm;
+               /* DN from remote server may be in arbitrary form.
+                * Pretty it so we can parse reliably.
+                */
+               dnPretty( NULL, dn, &pretty, dc->op->o_tmpmemctx );
+               if (pretty.bv_val) dn = &pretty;
+       }
+
+       diff = dn->bv_len - osuff->bv_len;
+       /* DN is shorter than suffix - ignore */
+       if ( diff < 0 ) {
+ignore:
+               *res = *odn;
+               if (pretty.bv_val)
+                       dc->op->o_tmpfree( pretty.bv_val, dc->op->o_tmpmemctx );
+               return;
        }
 
-       return 0;
+       /* DN longer than our suffix and doesn't match */
+       if ( diff > 0 && !DN_SEPARATOR(dn->bv_val[diff-1]))
+               goto ignore;
+
+       /* suffix is same length as ours, but doesn't match */
+       if ( strcasecmp( osuff->bv_val, &dn->bv_val[diff] ))
+               goto ignore;
+
+       res->bv_len = diff + nsuff->bv_len;
+       res->bv_val = dc->op->o_tmpalloc( res->bv_len + 1, dc->memctx );
+       strncpy( res->bv_val, dn->bv_val, diff );
+       strcpy( &res->bv_val[diff], nsuff->bv_val );
+
+       if (pretty.bv_val)
+               dc->op->o_tmpfree( pretty.bv_val, dc->op->o_tmpmemctx );
 }
index 382709e40ac885b6ac7bdbc5d0fb4e70d5d66bc8..f122233e3b67a68f595973bde1f46735cf494404 100644 (file)
 #include "lutil.h"
 
 
-LDAPControl **asyncmeta_copy_controls(Operation *op)
-{
-       LDAPControl **new_controls = NULL;
-       LDAPControl **c;
-       LDAPControl  *tmp_ctl = NULL;
-       int i, length = 1;
-
-
-       if (op->o_ctrls == NULL) {
-               return NULL;
-       }
-       for (c = op->o_ctrls; *c != NULL; c++) {
-               length++;
-       }
-
-       new_controls = op->o_tmpalloc( sizeof(LDAPControl*)*length, op->o_tmpmemctx );
-       for (i = 0; i < length-1; i ++) {
-               new_controls[i] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
-               if (op->o_ctrls[i]->ldctl_value.bv_len > 0) {
-                       ber_dupbv_x( &new_controls[i]->ldctl_value, &op->o_ctrls[i]->ldctl_value, op->o_tmpmemctx);
-               }
-               if (op->o_ctrls[i]->ldctl_oid) {
-                       new_controls[i]->ldctl_oid = ber_strdup_x(op->o_ctrls[i]->ldctl_oid, op->o_tmpmemctx);
-               }
-               new_controls[i]->ldctl_iscritical = op->o_ctrls[i]->ldctl_iscritical;
-       }
-       new_controls[length-1] = NULL;
-       return new_controls;
-}
-
-static
-void asyncmeta_free_op_controls(Operation *op)
-{
-       LDAPControl **c;
-       for (c = op->o_ctrls; *c != NULL; c++) {
-               if ((*c)->ldctl_value.bv_len > 0) {
-                       free((*c)->ldctl_value.bv_val);
-               }
-               if ((*c)->ldctl_oid) {
-                       free((*c)->ldctl_oid);
-               }
-               free(*c);
-       }
-       free(op->o_ctrls);
-}
-
-
-static
-Modifications* asyncmeta_copy_modlist(Operation *op, Modifications *modlist)
-{
-       Modifications *ml;
-       Modifications *new_mods = NULL;
-       for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
-               Modifications *mod = op->o_tmpalloc( sizeof( Modifications ), op->o_tmpmemctx );
-               *mod = *ml;
-               if ( ml->sml_values ) {
-                               ber_bvarray_dup_x( &mod->sml_values, ml->sml_values, op->o_tmpmemctx );
-                               if ( ml->sml_nvalues ) {
-                                       ber_bvarray_dup_x( &mod->sml_nvalues, ml->sml_nvalues, op->o_tmpmemctx );
-                               }
-                       }
-               mod->sml_next = NULL;
-               if (new_mods == NULL) {
-                       new_mods = mod;
-               } else {
-                       new_mods->sml_next = mod;
-               }
-       }
-       return new_mods;
-}
-
-Operation *asyncmeta_copy_op(Operation *op)
-{
-       const char      *text;
-       int rc;
-       char txtbuf[SLAP_TEXT_BUFLEN];
-       size_t textlen = sizeof txtbuf;
-       Entry *e;
-       Operation *new_op = op->o_tmpcalloc( 1, sizeof(OperationBuffer), op->o_tmpmemctx );
-       *new_op = *op;
-       new_op->o_hdr = &((OperationBuffer *) new_op)->ob_hdr;
-       *(new_op->o_hdr) = *(op->o_hdr);
-       new_op->o_controls = ((OperationBuffer *) new_op)->ob_controls;
-       new_op->o_callback = op->o_callback;
-       new_op->o_ber = NULL;
-       new_op->o_bd = op->o_bd->bd_self;
-
-       ber_dupbv_x( &new_op->o_req_dn, &op->o_req_dn, op->o_tmpmemctx );
-       ber_dupbv_x( &new_op->o_req_ndn, &op->o_req_ndn, op->o_tmpmemctx );
-       op->o_callback = NULL;
-
-       if (op->o_ndn.bv_len > 0) {
-               ber_dupbv_x( &new_op->o_ndn, &op->o_ndn, op->o_tmpmemctx );
-       }
-       if (op->o_dn.bv_len > 0) {
-               ber_dupbv_x( &new_op->o_dn, &op->o_dn, op->o_tmpmemctx );
-       }
-
-       new_op->o_ctrls = asyncmeta_copy_controls(op);
-       switch (op->o_tag) {
-       case LDAP_REQ_SEARCH:
-       {
-               AttributeName *at_names;
-               int i;
-               for (i=0; op->ors_attrs && !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++);
-               if (i > 0) {
-                       at_names = op->o_tmpcalloc( i+1, sizeof( AttributeName ), op->o_tmpmemctx );
-                       at_names[i].an_name.bv_len = 0;
-                       i--;
-                       for (i; i >= 0; i--) {
-                               at_names[i] = op->ors_attrs[i];
-                               ber_dupbv_x( &at_names[i].an_name, &op->ors_attrs[i].an_name, op->o_tmpmemctx );
-                       }
-               } else {
-                       at_names = NULL;
-               }
-               ber_dupbv_x( &new_op->ors_filterstr, &op->ors_filterstr, op->o_tmpmemctx );
-               new_op->ors_filter = filter_dup( op->ors_filter, op->o_tmpmemctx );
-               new_op->ors_attrs = at_names;
-       }
-       break;
-       case LDAP_REQ_ADD:
-       {
-               slap_entry2mods(op->ora_e, &new_op->ora_modlist, &text, txtbuf, textlen);
-               e = entry_alloc();
-               new_op->ora_e = e;
-               ber_dupbv_x( &e->e_name, &op->o_req_dn, op->o_tmpmemctx );
-               ber_dupbv_x( &e->e_nname, &op->o_req_ndn, op->o_tmpmemctx );
-               rc = slap_mods2entry( new_op->ora_modlist, &new_op->ora_e, 1, 0, &text, txtbuf, textlen);
-       }
-       break;
-       case LDAP_REQ_MODIFY:
-       {
-               new_op->orm_modlist = asyncmeta_copy_modlist(op, op->orm_modlist);
-       }
-       break;
-       case LDAP_REQ_COMPARE:
-               new_op->orc_ava = (AttributeAssertion *)ch_calloc( 1, sizeof( AttributeAssertion ));
-               *new_op->orc_ava = *op->orc_ava;
-               if ( !BER_BVISNULL( &op->orc_ava->aa_value ) ) {
-                       ber_dupbv_x( &new_op->orc_ava->aa_value,  &op->orc_ava->aa_value, op->o_tmpmemctx);
-               }
-               break;
-       case LDAP_REQ_MODRDN:
-
-               if (op->orr_newrdn.bv_len > 0) {
-                       ber_dupbv_x( &new_op->orr_newrdn, &op->orr_newrdn, op->o_tmpmemctx );
-               }
-               if (op->orr_nnewrdn.bv_len > 0) {
-                       ber_dupbv_x( &new_op->orr_nnewrdn, &op->orr_nnewrdn, op->o_tmpmemctx );
-               }
-               if (op->orr_newSup != NULL) {
-                       new_op->orr_newSup = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
-                       new_op->orr_newSup->bv_len = 0;
-                       if (op->orr_newSup->bv_len > 0) {
-                               ber_dupbv_x( new_op->orr_newSup, op->orr_newSup, op->o_tmpmemctx );
-                       }
-               }
-
-               if (op->orr_nnewSup != NULL) {
-                       new_op->orr_nnewSup = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
-                       new_op->orr_nnewSup->bv_len = 0;
-                       if (op->orr_nnewSup->bv_len > 0) {
-                               ber_dupbv_x( new_op->orr_nnewSup, op->orr_nnewSup, op->o_tmpmemctx );
-                       }
-               }
-               new_op->orr_modlist = asyncmeta_copy_modlist(op, op->orr_modlist);
-               break;
-       case LDAP_REQ_DELETE:
-       default:
-               break;
-       }
-       return new_op;
-}
-
-
 typedef struct listptr {
        void *reserved;
        struct listptr *next;
@@ -223,80 +47,73 @@ typedef struct listhead {
        int cnt;
 } listhead;
 
-static void *asyncmeta_memctx_destroy(void *key, void *data)
-{
-       listhead *lh = data;
-       listptr *lp;
-       while (lp = lh->list) {
-               lh->list = lp->next;
-               slap_sl_mem_destroy((void *)1, lp);
-       }
-       ch_free(lh);
-}
-
 #ifndef LH_MAX
 #define LH_MAX 16
 #endif
 
 static void *asyncmeta_memctx_get(void *threadctx)
 {
-       listhead *lh = NULL;
-       listptr *lp = NULL;
-       ldap_pvt_thread_pool_getkey(threadctx, asyncmeta_memctx_get, &lh, NULL);
-       if (!lh) {
-               lh = ch_malloc(sizeof(listhead));
-               lh->cnt = 0;
-               lh->list = NULL;
-               ldap_pvt_thread_pool_setkey(threadctx, asyncmeta_memctx_get, lh, asyncmeta_memctx_destroy, NULL, NULL);
-       }
-       if (lh->list) {
-               lp = lh->list;
-               lh->list = lp->next;
-               lh->cnt--;
-               slap_sl_mem_setctx(threadctx, lp);
-       }
        return slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, threadctx, 1);
 }
 
 static void asyncmeta_memctx_put(void *threadctx, void *memctx)
 {
-       listhead *lh = NULL;
-       ldap_pvt_thread_pool_getkey(threadctx, asyncmeta_memctx_get, &lh, NULL);
-       if (!lh) {
-               lh = ch_malloc(sizeof(listhead));
-               lh->cnt = 0;
-               lh->list = NULL;
-               ldap_pvt_thread_pool_setkey(threadctx, asyncmeta_memctx_get, lh, asyncmeta_memctx_destroy, NULL, NULL);
-       }
-       if (lh->cnt < LH_MAX) {
-               listptr *lp = memctx;
-               lp->next = lh->list;
-               lh->list = lp;
-               lh->cnt++;
-       } else {
-               slap_sl_mem_destroy((void *)1, memctx);
-       }
+       slap_sl_mem_setctx(threadctx, NULL);
+       slap_sl_mem_destroy((void *)1, memctx);
 }
 
-int asyncmeta_new_bm_context(Operation *op, SlapReply *rs, bm_context_t **new_bc, int ntargets)
+void asyncmeta_memctx_toggle(void *thrctx)
+{
+       asyncmeta_memctx_get(thrctx);
+}
+
+int asyncmeta_new_bm_context(Operation *op,
+                            SlapReply *rs,
+                            bm_context_t **new_bc,
+                            int ntargets,
+                            a_metainfo_t       *mi)
 {
-       void *oldctx = op->o_tmpmemctx;
        int i;
-       /* prevent old memctx from being destroyed */
-       slap_sl_mem_setctx(op->o_threadctx, NULL);
-       /* create new memctx */
-       op->o_tmpmemctx = asyncmeta_memctx_get( op->o_threadctx );
        *new_bc = op->o_tmpcalloc( 1, sizeof( bm_context_t ), op->o_tmpmemctx );
 
-       (*new_bc)->op = asyncmeta_copy_op(op);
+       (*new_bc)->op = op;
+       (*new_bc)->copy_op = *op;
        (*new_bc)->candidates = op->o_tmpcalloc(ntargets, sizeof(SlapReply),op->o_tmpmemctx);
        (*new_bc)->msgids = op->o_tmpcalloc(ntargets, sizeof(int),op->o_tmpmemctx);
+       (*new_bc)->nretries = op->o_tmpcalloc(ntargets, sizeof(int),op->o_tmpmemctx);
+       (*new_bc)->c_peer_name = op->o_conn->c_peer_name;
+       (*new_bc)->is_root = be_isroot( op );
+
+       switch(op->o_tag) {
+       case LDAP_REQ_COMPARE:
+               {
+               AttributeAssertion *ava = op->o_tmpcalloc( 1, sizeof(AttributeAssertion), op->o_tmpmemctx );
+               *ava = *op->orc_ava;
+               op->orc_ava = ava;
+               }
+               break;
+       case LDAP_REQ_MODRDN:
+               if (op->orr_newSup != NULL) {
+                       struct berval *bv = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
+                       *bv = *op->orr_newSup;
+                       op->orr_newSup = bv;
+               }
+
+               if (op->orr_nnewSup != NULL) {
+                       struct berval *bv = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
+                       *bv = *op->orr_nnewSup;
+                       op->orr_nnewSup = bv;
+               }
+               break;
+       default:
+               break;
+       }
        for (i = 0; i < ntargets; i++) {
                (*new_bc)->msgids[i] = META_MSGID_UNDEFINED;
        }
-       /* restore original memctx */
-       slap_sl_mem_setctx(op->o_threadctx, oldctx);
-       op->o_tmpmemctx = oldctx;
+       for (i = 0; i < ntargets; i++) {
+               (*new_bc)->nretries[i] = mi->mi_targets[i]->mt_nretries;
+       }
        return LDAP_SUCCESS;
 }
 
@@ -305,15 +122,6 @@ void asyncmeta_free_op(Operation *op)
        assert (op != NULL);
        switch (op->o_tag) {
        case LDAP_REQ_SEARCH:
-               if (op->ors_filterstr.bv_len != 0) {
-                       free(op->ors_filterstr.bv_val);
-               }
-               if (op->ors_filter) {
-                       filter_free(op->ors_filter);
-               }
-               if (op->ors_attrs) {
-                       free(op->ors_attrs);
-               }
                break;
        case LDAP_REQ_ADD:
                if ( op->ora_modlist != NULL ) {
@@ -331,36 +139,11 @@ void asyncmeta_free_op(Operation *op)
                }
                break;
        case LDAP_REQ_MODRDN:
-               if (op->orr_newrdn.bv_len > 0) {
-                       free(op->orr_newrdn.bv_val);
-               }
-               if (op->orr_nnewrdn.bv_len > 0) {
-                       free(op->orr_nnewrdn.bv_val);
-               }
-
-               if (op->orr_nnewSup != NULL ) {
-                       if (op->orr_nnewSup->bv_len > 0) {
-                               free(op->orr_nnewSup->bv_val);
-                       }
-                       free (op->orr_nnewSup);
-               }
-
-               if (op->orr_newSup != NULL ) {
-                       if (op->orr_newSup->bv_len > 0) {
-                               free(op->orr_newSup->bv_val);
-                       }
-                       free (op->orr_newSup);
-               }
-
                if ( op->orr_modlist != NULL ) {
                        slap_mods_free(op->orr_modlist, 1 );
                }
                break;
        case LDAP_REQ_COMPARE:
-               if ( !BER_BVISNULL( &op->orc_ava->aa_value ) ) {
-                       free(op->orc_ava->aa_value.bv_val);
-               }
-               free(op->orc_ava);
                break;
        case LDAP_REQ_DELETE:
                break;
@@ -368,22 +151,8 @@ void asyncmeta_free_op(Operation *op)
                Debug( LDAP_DEBUG_TRACE, "==> asyncmeta_free_op : other message type" );
        }
 
-       if (op->o_ctrls != NULL) {
-               asyncmeta_free_op_controls(op);
-       }
-       if (op->o_ndn.bv_len > 0) {
-               free(op->o_ndn.bv_val);
-       }
-       if (op->o_dn.bv_len > 0) {
-               free( op->o_dn.bv_val );
-       }
-       if (op->o_req_dn.bv_len > 0) {
-               free(op->o_req_dn.bv_val);
-       }
-       if (op->o_req_dn.bv_len > 0) {
-               free(op->o_req_ndn.bv_val);
-       }
-       free(op);
+       connection_op_finish( op );
+       slap_op_free( op, op->o_threadctx );
 }
 
 
@@ -393,96 +162,26 @@ void asyncmeta_clear_bm_context(bm_context_t *bc)
 {
 
        Operation *op = bc->op;
-#if 0
-       bm_candidates_t  *cl;
-       a_metainfo_t    *mi;
-       int i = 0;
-       if (bmc == NULL) {
-               return;
-       } else if (bmc->cl == NULL) {
-               free(bmc);
-               return;
-       }
-       cl = bmc->cl;
-       op = cl->op;
-       switch (op->o_tag) {
-       case LDAP_REQ_SEARCH:
-               break;
-       case LDAP_REQ_ADD:
-               if ( (bmc->mdn.bv_len != 0) &&
-                    (bmc->mdn.bv_val != op->o_req_dn.bv_val) ) {
-                       free( bmc->mdn.bv_val );
-               }
-
-               if (bmc->data.add_d.attrs != NULL )  {
-                       while (bmc->data.add_d.attrs[i] != NULL) {
-                               free( bmc->data.add_d.attrs[i]->mod_bvalues );
-                               free( bmc->data.add_d.attrs[i] );
-                               i++;
-                       }
-                       free( bmc->data.add_d.attrs );
-                       }
-               break;
-       case LDAP_REQ_MODIFY:
-               if ( bmc->mdn.bv_val != op->o_req_dn.bv_val ) {
-                       free( bmc->mdn.bv_val );
-               }
-               if ( bmc->data.mod_d.modv != NULL ) {
-                       for ( i = 0; bmc->data.mod_d.modv[ i ]; i++ ) {
-                               free( bmc->data.mod_d.modv[ i ]->mod_bvalues );
-                       }
-               }
-               free( bmc->data.mod_d.mods );
-               free( bmc->data.mod_d.modv );
-
-               break;
-       case LDAP_REQ_MODRDN:
-               if ( bmc->mdn.bv_val != op->o_req_dn.bv_val ) {
-                       free( bmc->mdn.bv_val );
-               }
-
-               if ( bmc->data.modrdn_d.newSuperior.bv_len != 0 &&
-                    bmc->data.modrdn_d.newSuperior.bv_val != op->orr_newSup->bv_val )
-               {
-                       free( bmc->data.modrdn_d.newSuperior.bv_val );
-
-               }
-
-               if ( bmc->data.modrdn_d.newrdn.bv_len != 0 &&
-                    bmc->data.modrdn_d.newrdn.bv_val != op->orr_newrdn.bv_val )
-               {
-                       free( bmc->data.modrdn_d.newrdn.bv_val );
+       void *thrctx, *memctx;
+       int i;
 
-               }
-               break;
-       case LDAP_REQ_COMPARE:
-               if ( bmc->mdn.bv_val != op->o_req_dn.bv_val ) {
-                       free( bmc->mdn.bv_val );
-               }
-               if ( op->orc_ava->aa_value.bv_val != bmc->data.comp_d.mapped_value.bv_val ) {
-                       free( bmc->data.comp_d.mapped_value.bv_val );
+       if ( bc->bc_mc && bc->bc_mc->mc_info ) {
+               for (i = 0; i < bc->bc_mc->mc_info->mi_ntargets; i++) {
+                       if (bc->candidates[ i ].sr_text != NULL) {
+                               ch_free( (char *)bc->candidates[ i ].sr_text );
+                               bc->candidates[ i ].sr_text = NULL;
                        }
-               break;
-       case LDAP_REQ_DELETE:
-               if ( bmc->mdn.bv_val != op->o_req_dn.bv_val ) {
-                       free( bmc->mdn.bv_val );
                }
-               break;
-       default:
-               Debug( LDAP_DEBUG_TRACE, "==> asyncmeta_clear_bm_context: other message type" );
        }
-       if (bmc->dc != NULL) {
-               free (bmc->dc);
-       }
-       free(bmc);
 
-       if (clear_cl > 0) {
-               asyncmeta_free_candidate_list(cl, lock);
-               Debug( LDAP_DEBUG_TRACE, "==> asyncmeta_clear_bm_context: free_cl_list\n" );
-       }
-#else
-       asyncmeta_memctx_put(op->o_threadctx, op->o_tmpmemctx);
-#endif
+       if (op->o_conn->c_conn_idx == -1)
+               return;
+       memctx = op->o_tmpmemctx;
+       thrctx = op->o_threadctx;
+       while (op->o_bd == bc->copy_op.o_bd)
+               ldap_pvt_thread_yield();
+       asyncmeta_free_op(op);
+       asyncmeta_memctx_put(thrctx, memctx);
 }
 
 int asyncmeta_add_message_queue(a_metaconn_t *mc, bm_context_t *bc)
@@ -493,61 +192,55 @@ int asyncmeta_add_message_queue(a_metaconn_t *mc, bm_context_t *bc)
        Debug( LDAP_DEBUG_TRACE, "add_message_queue: mc %p, pending_ops %d, max_pending %d\n",
                mc, mc->pending_ops, max_pending_ops );
 
+       assert(bc->bc_mc == NULL);
        if (mc->pending_ops >= max_pending_ops) {
                return LDAP_BUSY;
        }
+       bc->bc_mc = mc;
 
-       LDAP_SLIST_INSERT_HEAD( &mc->mc_om_list, bc, bc_next);
+       slap_sl_mem_setctx(bc->op->o_threadctx, NULL);
+       LDAP_STAILQ_INSERT_TAIL( &mc->mc_om_list, bc, bc_next);
        mc->pending_ops++;
        return LDAP_SUCCESS;
 }
 
+
 void
 asyncmeta_drop_bc(a_metaconn_t *mc, bm_context_t *bc)
 {
        bm_context_t *om;
-       int i;
-       LDAP_SLIST_FOREACH( om, &mc->mc_om_list, bc_next ) {
+       LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
                if (om == bc) {
-                       for (i = 0; i < mc->mc_info->mi_ntargets; i++)
-                       {
-                               if (bc->msgids[i] >= 0) {
-                                       mc->mc_conns[i].msc_pending_ops--;
-                               }
-                       }
-                       LDAP_SLIST_REMOVE(&mc->mc_om_list, om, bm_context_t, bc_next);
+                       LDAP_STAILQ_REMOVE(&mc->mc_om_list, om, bm_context_t, bc_next);
                        mc->pending_ops--;
                        break;
                }
        }
+       assert(om == bc);
+       assert(bc->bc_mc == mc);
 }
 
+
 bm_context_t *
 asyncmeta_find_message(ber_int_t msgid, a_metaconn_t *mc, int candidate)
 {
        bm_context_t *om;
-       LDAP_SLIST_FOREACH( om, &mc->mc_om_list, bc_next ) {
-               if (om->candidates[candidate].sr_msgid == msgid) {
+       LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
+               if (om->candidates[candidate].sr_msgid == msgid && !om->bc_invalid) {
                        break;
                }
        }
        return om;
 }
 
-
-
 bm_context_t *
-asyncmeta_find_message_by_opmsguid (ber_int_t msgid, a_metaconn_t *mc, int remove)
+asyncmeta_bc_in_queue(a_metaconn_t *mc, bm_context_t *bc)
 {
        bm_context_t *om;
-       LDAP_SLIST_FOREACH( om, &mc->mc_om_list, bc_next ) {
-               if (om->op->o_msgid == msgid) {
-                       break;
+       LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
+               if (om == bc) {
+                       return bc;
                }
        }
-       if (remove && om) {
-               LDAP_SLIST_REMOVE(&mc->mc_om_list, om, bm_context_t, bc_next);
-               mc->pending_ops--;
-       }
-       return om;
+       return NULL;
 }
index 47651344de9eada8c9798844905ed8e0090f6d6a..1fa733fa24be43ac1a897047e976d0ef15e90a7a 100644 (file)
 #include "ldap_rq.h"
 #include "../../../libraries/liblber/lber-int.h"
 
+static void
+asyncmeta_send_ldap_result(bm_context_t *bc, Operation *op, SlapReply *rs)
+{
+       if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !bc->op->o_abandon ) {
+               send_ldap_result(&bc->copy_op, rs);
+               bc->op->o_callback = bc->copy_op.o_callback;
+               bc->op->o_extra = bc->copy_op.o_extra;
+               bc->op->o_ctrls = bc->copy_op.o_ctrls;
+       }
+}
+
 static int
 asyncmeta_is_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate)
 {
        a_metainfo_t    *mi = mc->mc_info;
-       a_metatarget_t  *mt = mi->mi_targets[ candidate ];
-       int i,found = 0;
+       int i;
        SlapReply *candidates = bc->candidates;
        for ( i = 0; i < mi->mi_ntargets; i++ ) {
                if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
@@ -54,11 +64,9 @@ asyncmeta_is_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate)
 
 meta_search_candidate_t
 asyncmeta_dobind_result(
-       Operation               *op,
-       SlapReply               *rs,
        a_metaconn_t            *mc,
        int                     candidate,
-       SlapReply               *candidates,
+       SlapReply               *bind_result,
        LDAPMessage             *res )
 {
        a_metainfo_t            *mi = mc->mc_info;
@@ -70,30 +78,53 @@ asyncmeta_dobind_result(
 
        assert( msc->msc_ldr != NULL );
 
+       if ( mi->mi_idle_timeout != 0 ) {
+               asyncmeta_set_msc_time(msc);
+       }
+
+       if ( LogTest( asyncmeta_debug ) ) {
+               char    time_buf[ SLAP_TEXT_BUFLEN ];
+               asyncmeta_get_timestamp(time_buf);
+               Debug( asyncmeta_debug, "[%x] [%s] asyncmeta_dobind_result msc: %p, "
+                      "msc->msc_binding_time: %x, msc->msc_flags:%x\n ",
+                      (unsigned int)slap_get_time(), time_buf, msc,
+                      (unsigned int)msc->msc_binding_time, msc->msc_mscflags );
+       }
        /* FIXME: matched? referrals? response controls? */
        rc = ldap_parse_result( msc->msc_ldr, res,
-               &candidates[ candidate ].sr_err,
-               NULL, NULL, NULL, NULL, 0 );
+                               &(bind_result->sr_err),
+                               (char **)&(bind_result->sr_matched),
+                               (char **)&(bind_result->sr_text),
+                               NULL, NULL, 0 );
+
+       if ( LogTest( asyncmeta_debug ) ) {
+               char    time_buf[ SLAP_TEXT_BUFLEN ];
+               asyncmeta_get_timestamp(time_buf);
+               Debug( asyncmeta_debug,
+                      "[%s] asyncmeta_dobind_result error=%d msc: %p\n",
+                      time_buf,bind_result->sr_err, msc );
+       }
+
        if ( rc != LDAP_SUCCESS ) {
-               candidates[ candidate ].sr_err = rc;
+               bind_result->sr_err = rc;
        }
-       rc = slap_map_api2result( &candidates[ candidate ] );
+       rc = slap_map_api2result( bind_result );
 
        LDAP_BACK_CONN_BINDING_CLEAR( msc );
        if ( rc != LDAP_SUCCESS ) {
-               candidates[ candidate ].sr_err = rc;
-               if ( META_BACK_ONERR_STOP( mi ) || op->o_tag != LDAP_REQ_SEARCH) {
-                       asyncmeta_clear_one_msc(op, mc, candidate);
-                       retcode = META_SEARCH_ERR;
-                       rs->sr_err = rc;
-               }
-
+               bind_result->sr_err = rc;
        } else {
                /* FIXME: check if bound as idassert authcDN! */
                if ( BER_BVISNULL( &msc->msc_bound_ndn )
                        || BER_BVISEMPTY( &msc->msc_bound_ndn ) )
                {
                        LDAP_BACK_CONN_ISANON_SET( msc );
+                       if ( LogTest( asyncmeta_debug ) ) {
+                               char    time_buf[ SLAP_TEXT_BUFLEN ];
+                               asyncmeta_get_timestamp(time_buf);
+                               Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result anonymous msc: %p\n",
+                                     time_buf, msc );
+                       }
 
                } else {
                        if ( META_BACK_TGT_SAVECRED( mt ) &&
@@ -102,17 +133,16 @@ asyncmeta_dobind_result(
                        {
                                ldap_set_rebind_proc( msc->msc_ldr, mt->mt_rebind_f, msc );
                        }
+                       if ( LogTest( asyncmeta_debug ) ) {
+                               char    time_buf[ SLAP_TEXT_BUFLEN ];
+                               asyncmeta_get_timestamp(time_buf);
+                               Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result success msc: %p\n",
+                                     time_buf, msc );
+                       }
                        LDAP_BACK_CONN_ISBOUND_SET( msc );
                }
                retcode = META_SEARCH_CANDIDATE;
-
-               /* connect must be async */
-               //ldap_set_option( msc->msc_ld, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_OFF );
        }
-
-       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-       META_BINDING_CLEAR( &candidates[ candidate ] );
-
        return retcode;
 }
 
@@ -125,8 +155,7 @@ asyncmeta_send_entry(
        LDAPMessage     *e )
 {
        a_metainfo_t            *mi = mc->mc_info;
-       struct berval           a, mapped;
-       int                     check_duplicate_attrs = 0;
+       struct berval           a, mapped = BER_BVNULL;
        int                     check_sorted_attrs = 0;
        Entry                   ent = {0};
        BerElement              ber = *ldap_get_message_ber( e );
@@ -137,6 +166,10 @@ asyncmeta_send_entry(
        a_dncookie              dc;
        ber_len_t               len;
        int                     rc;
+       void    *mem_mark;
+
+       mem_mark = slap_sl_mark( op->o_tmpmemctx );
+       ber_set_option( &ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
 
        if ( ber_scanf( &ber, "l{", &len ) == LBER_ERROR ) {
                return LDAP_DECODING_ERROR;
@@ -153,15 +186,11 @@ asyncmeta_send_entry(
        /*
         * Rewrite the dn of the result, if needed
         */
+       dc.op = op;
        dc.target = mi->mi_targets[ target ];
-       dc.conn = op->o_conn;
-       dc.rs = rs;
-       dc.ctx = "searchResult";
-
-       rs->sr_err = asyncmeta_dn_massage( &dc, &bdn, &dn );
-       if ( rs->sr_err != LDAP_SUCCESS) {
-               return rs->sr_err;
-       }
+       dc.memctx = op->o_tmpmemctx;
+       dc.to_from = MASSAGE_REP;
+       asyncmeta_dn_massage( &dc, &bdn, &dn );
 
        /*
         * Note: this may fail if the target host(s) schema differs
@@ -173,7 +202,7 @@ asyncmeta_send_entry(
        rc = dnPrettyNormal( NULL, &dn, &ent.e_name, &ent.e_nname,
                op->o_tmpmemctx );
        if ( dn.bv_val != bdn.bv_val ) {
-                       free( dn.bv_val );
+                       op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
        }
        BER_BVZERO( &dn );
 
@@ -196,7 +225,6 @@ asyncmeta_send_entry(
 
        attrp = &ent.e_attrs;
 
-       dc.ctx = "searchAttrDN";
        while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
                int                             last = 0;
                slap_syntax_validate_func       *validate;
@@ -216,24 +244,12 @@ asyncmeta_send_entry(
                        break;
                }
 
-               asyncmeta_map( &mi->mi_targets[ target ]->mt_rwmap.rwm_at,
-                               &a, &mapped, BACKLDAP_REMAP );
-               if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
-                       ( void )ber_scanf( &ber, "x" /* [W] */ );
-                       continue;
-               }
-               if ( mapped.bv_val != a.bv_val ) {
-                       /* will need to check for duplicate attrs */
-                       check_duplicate_attrs++;
-               }
                attr = op->o_tmpcalloc( 1, sizeof(Attribute), op->o_tmpmemctx );
-               if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
+               if ( slap_bv2ad( &a, &attr->a_desc, &text )
                                != LDAP_SUCCESS) {
-                       if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text,
+                       if ( slap_bv2undef_ad( &a, &attr->a_desc, &text,
                                SLAP_AD_PROXIED ) != LDAP_SUCCESS )
                        {
-                               char    buf[ SLAP_TEXT_BUFLEN ];
-
                                Debug(LDAP_DEBUG_ANY,
                                      "%s meta_send_entry(\"%s\"): " "slap_bv2undef_ad(%s): %s\n",
                                      op->o_log_prefix, ent.e_name.bv_val,
@@ -283,58 +299,11 @@ asyncmeta_send_entry(
                pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
 
                if ( !validate && !pretty ) {
-                       attr_clean( attr );
+                       ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
                        op->o_tmpfree( attr, op->o_tmpmemctx );
                        goto next_attr;
                }
 
-               if ( attr->a_desc == slap_schema.si_ad_objectClass
-                               || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
-               {
-                       struct berval   *bv;
-
-                       for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
-                               ObjectClass *oc;
-
-                               asyncmeta_map( &mi->mi_targets[ target ]->mt_rwmap.rwm_oc,
-                                               bv, &mapped, BACKLDAP_REMAP );
-                               if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
-remove_oc:;
-                                       free( bv->bv_val );
-                                       BER_BVZERO( bv );
-                                       if ( --last < 0 ) {
-                                               break;
-                                       }
-                                       *bv = attr->a_vals[ last ];
-                                       BER_BVZERO( &attr->a_vals[ last ] );
-                                       bv--;
-
-                               } else if ( mapped.bv_val != bv->bv_val ) {
-                                       int     i;
-
-                                       for ( i = 0; !BER_BVISNULL( &attr->a_vals[ i ] ); i++ ) {
-                                               if ( &attr->a_vals[ i ] == bv ) {
-                                                       continue;
-                                               }
-
-                                               if ( ber_bvstrcasecmp( &mapped, &attr->a_vals[ i ] ) == 0 ) {
-                                                       break;
-                                               }
-                                       }
-
-                                       if ( !BER_BVISNULL( &attr->a_vals[ i ] ) ) {
-                                               goto remove_oc;
-                                       }
-
-                                       ber_bvreplace( bv, &mapped );
-
-                               } else if ( ( oc = oc_bvfind_undef( bv ) ) == NULL ) {
-                                       goto remove_oc;
-
-                               } else {
-                                       ber_bvreplace( bv, &oc->soc_cname );
-                               }
-                       }
                /*
                 * It is necessary to try to rewrite attributes with
                 * dn syntax because they might be used in ACLs as
@@ -346,7 +315,7 @@ remove_oc:;
                 * ACLs to the target directory server, and letting
                 * everything pass thru the ldap backend.
                 */
-               } else {
+               {
                        int     i;
 
                        if ( attr->a_desc->ad_type->sat_syntax ==
@@ -355,7 +324,7 @@ remove_oc:;
                                asyncmeta_dnattr_result_rewrite( &dc, attr->a_vals );
 
                        } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
-                               asyncmeta_referral_result_rewrite( &dc, attr->a_vals, NULL );
+                               asyncmeta_referral_result_rewrite( &dc, attr->a_vals );
 
                        }
 
@@ -365,7 +334,7 @@ remove_oc:;
 
                                if ( pretty ) {
                                        rc = ordered_value_pretty( attr->a_desc,
-                                               &attr->a_vals[i], &pval, NULL );
+                                               &attr->a_vals[i], &pval, op->o_tmpmemctx );
 
                                } else {
                                        rc = ordered_value_validate( attr->a_desc,
@@ -373,7 +342,7 @@ remove_oc:;
                                }
 
                                if ( rc ) {
-                                       ber_memfree( attr->a_vals[i].bv_val );
+                                       ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
                                        if ( --last == i ) {
                                                BER_BVZERO( &attr->a_vals[ i ] );
                                                break;
@@ -385,13 +354,13 @@ remove_oc:;
                                }
 
                                if ( pretty ) {
-                                       ber_memfree( attr->a_vals[i].bv_val );
+                                       ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
                                        attr->a_vals[i] = pval;
                                }
                        }
 
                        if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
-                               attr_clean( attr );
+                               ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
                                op->o_tmpfree( attr, op->o_tmpmemctx );
                                goto next_attr;
                        }
@@ -402,7 +371,7 @@ remove_oc:;
                {
                        int i;
 
-                       attr->a_nvals = ch_malloc( ( last + 1 ) * sizeof( struct berval ) );
+                       attr->a_nvals = op->o_tmpalloc( ( last + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
                        for ( i = 0; i<last; i++ ) {
                                /* if normalizer fails, drop this value */
                                if ( ordered_value_normalize(
@@ -410,8 +379,8 @@ remove_oc:;
                                        attr->a_desc,
                                        attr->a_desc->ad_type->sat_equality,
                                        &attr->a_vals[i], &attr->a_nvals[i],
-                                       NULL )) {
-                                       ber_memfree( attr->a_vals[i].bv_val );
+                                       op->o_tmpmemctx )) {
+                                       ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
                                        if ( --last == i ) {
                                                BER_BVZERO( &attr->a_vals[ i ] );
                                                break;
@@ -423,7 +392,8 @@ remove_oc:;
                        }
                        BER_BVZERO( &attr->a_nvals[i] );
                        if ( last == 0 ) {
-                               attr_clean( attr );
+                               ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
+                               ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx );
                                op->o_tmpfree( attr, op->o_tmpmemctx );
                                goto next_attr;
                        }
@@ -438,51 +408,6 @@ remove_oc:;
 next_attr:;
        }
 
-       /* only check if some mapping occurred */
-       if ( check_duplicate_attrs ) {
-               Attribute       **ap;
-
-               for ( ap = &ent.e_attrs; *ap != NULL; ap = &(*ap)->a_next ) {
-                       Attribute       **tap;
-
-                       for ( tap = &(*ap)->a_next; *tap != NULL; ) {
-                               if ( (*tap)->a_desc == (*ap)->a_desc ) {
-                                       Entry           e = { 0 };
-                                       Modification    mod = { 0 };
-                                       const char      *text = NULL;
-                                       char            textbuf[ SLAP_TEXT_BUFLEN ];
-                                       Attribute       *next = (*tap)->a_next;
-
-                                       BER_BVSTR( &e.e_name, "" );
-                                       BER_BVSTR( &e.e_nname, "" );
-                                       e.e_attrs = *ap;
-                                       mod.sm_op = LDAP_MOD_ADD;
-                                       mod.sm_desc = (*ap)->a_desc;
-                                       mod.sm_type = mod.sm_desc->ad_cname;
-                                       mod.sm_numvals = (*ap)->a_numvals;
-                                       mod.sm_values = (*tap)->a_vals;
-                                       if ( (*tap)->a_nvals != (*tap)->a_vals ) {
-                                               mod.sm_nvalues = (*tap)->a_nvals;
-                                       }
-
-                                       (void)modify_add_values( &e, &mod,
-                                               /* permissive */ 1,
-                                               &text, textbuf, sizeof( textbuf ) );
-
-                                       /* should not insert new attrs! */
-                                       assert( e.e_attrs == *ap );
-
-                                       attr_clean( *tap );
-                                       op->o_tmpfree( *tap, op->o_tmpmemctx );
-                                       *tap = next;
-
-                               } else {
-                                       tap = &(*tap)->a_next;
-                               }
-                       }
-               }
-       }
-
        /* Check for sorted attributes */
        if ( check_sorted_attrs ) {
                for ( attr = ent.e_attrs; attr; attr = attr->a_next ) {
@@ -495,8 +420,8 @@ next_attr:;
 
                                        /* Strip duplicate values */
                                        if ( attr->a_nvals != attr->a_vals )
-                                               ber_memfree( attr->a_nvals[i].bv_val );
-                                       ber_memfree( attr->a_vals[i].bv_val );
+                                               ber_memfree_x( attr->a_nvals[i].bv_val, op->o_tmpmemctx );
+                                       ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
                                        attr->a_numvals--;
                                        if ( (unsigned)i < attr->a_numvals ) {
                                                attr->a_vals[i] = attr->a_vals[attr->a_numvals];
@@ -534,28 +459,39 @@ done:;
                ldap_controls_free( rs->sr_ctrls );
                rs->sr_ctrls = NULL;
        }
+#if 0
        while ( ent.e_attrs ) {
                attr = ent.e_attrs;
                ent.e_attrs = attr->a_next;
-               attr_clean( attr );
+               if ( attr->a_nvals != attr->a_vals )
+                       ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx );
+               ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
                op->o_tmpfree( attr, op->o_tmpmemctx );
        }
+       if (ent.e_name.bv_val != NULL) {
+               op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx );
+       }
+
+       if (ent.e_nname.bv_val != NULL) {
+               op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx );
+       }
        if (rs->sr_entry && rs->sr_entry != &ent) {
                entry_free( rs->sr_entry );
        }
+#endif
+       slap_sl_release( mem_mark, op->o_tmpmemctx );
        rs->sr_entry = NULL;
        rs->sr_attrs = NULL;
        return rc;
 }
 
-int
+static void
 asyncmeta_search_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate, int sres)
 {
        a_metainfo_t    *mi = mc->mc_info;
-       a_metatarget_t  *mt = mi->mi_targets[ candidate ];
        Operation *op = bc->op;
        SlapReply *rs = &bc->rs;
-       int        rc, i;
+       int        i;
        SlapReply *candidates = bc->candidates;
        char *matched = NULL;
 
@@ -614,7 +550,7 @@ asyncmeta_search_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate,
                }
 
        } else if ( sres == LDAP_NO_SUCH_OBJECT ) {
-               matched = op->o_bd->be_suffix[ 0 ].bv_val;
+               matched = mi->mi_suffix.bv_val;
        }
 
        /*
@@ -649,181 +585,147 @@ asyncmeta_search_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate,
                }
        }
        Debug( LDAP_DEBUG_TRACE,
-              "%s asyncmeta_search_last_result(\"%s\"): "
+              "%s asyncmeta_search_last_result(\"%d\"): "
               ".\n",
-              op->o_log_prefix, 0 );
+              op->o_log_prefix, candidate );
        rs->sr_err = sres;
        rs->sr_matched = ( sres == LDAP_SUCCESS ? NULL : matched );
+       rs->sr_text =  ( sres == LDAP_SUCCESS ? NULL : candidates[candidate].sr_text );
        rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
-       send_ldap_result(op, rs);
+       asyncmeta_send_ldap_result(bc, op, rs);
+       rs->sr_text = NULL;
        rs->sr_matched = NULL;
        rs->sr_ref = NULL;
 }
 
+static meta_search_candidate_t
+asyncmeta_send_pending_op(bm_context_t *bc, int candidate)
+{
+       meta_search_candidate_t retcode;
+       switch (bc->op->o_tag) {
+               case LDAP_REQ_SEARCH:
+                       retcode = asyncmeta_back_search_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate,  NULL, 0 , 0);
+                       break;
+               case LDAP_REQ_ADD:
+                       retcode = asyncmeta_back_add_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+                       break;
+               case LDAP_REQ_MODIFY:
+                       retcode = asyncmeta_back_modify_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+                       break;
+               case LDAP_REQ_MODRDN:
+                       retcode = asyncmeta_back_modrdn_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+                       break;
+               case LDAP_REQ_COMPARE:
+                       retcode = asyncmeta_back_compare_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+                       break;
+               case LDAP_REQ_DELETE:
+                       retcode = asyncmeta_back_delete_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+                       break;
+               default:
+                       retcode = META_SEARCH_NOT_CANDIDATE;
+               }
+       return retcode;
+}
 
-int
-asyncmeta_search_finish(a_metaconn_t *mc, bm_context_t *bc, int candidate, char *matched)
+
+meta_search_candidate_t
+asyncmeta_send_all_pending_ops(a_metaconn_t *mc, int candidate, void *ctx, int dolock)
 {
        a_metainfo_t    *mi = mc->mc_info;
-       a_metatarget_t  *mt = mi->mi_targets[ candidate ];
-       Operation *op = bc->op;
-       SlapReply *rs = &bc->rs;
-       SlapReply *candidates = bc->candidates;
-       int i;
-       int do_taint = 0;
-       if ( matched && matched != op->o_bd->be_suffix[ 0 ].bv_val ) {
-               op->o_tmpfree( matched, op->o_tmpmemctx );
-       }
+       bm_context_t *bc, *onext;
+       a_metasingleconn_t *msc = &mc->mc_conns[candidate];
 
-       if ( rs->sr_v2ref ) {
-               ber_bvarray_free_x( rs->sr_v2ref, op->o_tmpmemctx );
-       }
+       if ( dolock )
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
 
-       for ( i = 0; i < mi->mi_ntargets; i++ ) {
-               if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+       msc->msc_active++;
+       for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
+               meta_search_candidate_t ret;
+               onext = LDAP_STAILQ_NEXT(bc, bc_next);
+               if (bc->candidates[candidate].sr_msgid != META_MSGID_NEED_BIND || bc->bc_active > 0 || bc->op->o_abandon > 0) {
                        continue;
                }
-
-                       if ( META_IS_BINDING( &candidates[ i ] )
-                               || candidates[ i ].sr_msgid == META_MSGID_CONNECTING )
-                       {
-                               if ( LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] )
-                                       || candidates[ i ].sr_msgid == META_MSGID_CONNECTING )
-                               {
-                                       assert( candidates[ i ].sr_msgid >= 0
-                                               || candidates[ i ].sr_msgid == META_MSGID_CONNECTING );
-                                       assert( mc->mc_conns[ i ].msc_ldr != NULL );
-
-#ifdef DEBUG_205
-                                       Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_finish  "
-                                               "ldap_unbind_ext[%ld] ld=%p\n",
-                                               op->o_log_prefix, i, (void *)mc->mc_conns[i].msc_ldr );
-#endif /* DEBUG_205 */
-
-                               }
-                               META_BINDING_CLEAR( &candidates[ i ] );
-
-                       } else if ( candidates[ i ].sr_msgid >= 0 ) {
-                               (void)asyncmeta_back_cancel( mc, op,
-                                       candidates[ i ].sr_msgid, i );
+               bc->op->o_threadctx = ctx;
+               bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+               slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+               bc->bc_active++;
+               ret = asyncmeta_send_pending_op(bc, candidate);
+               if (ret != META_SEARCH_CANDIDATE) {
+                       bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+                       bc->candidates[ candidate ].sr_type = REP_RESULT;
+                       bc->candidates[ candidate ].sr_err = bc->rs.sr_err;
+                       if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
+                           (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
+                               LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+                               mc->pending_ops--;
+                               asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
+                               asyncmeta_clear_bm_context(bc);
                        }
-
-               if ( candidates[ i ].sr_matched ) {
-                       free( (char *)candidates[ i ].sr_matched );
-                       candidates[ i ].sr_matched = NULL;
+               } else {
+                       bc->bc_active--;
                }
+       }
+       msc->msc_active--;
 
-               if ( candidates[ i ].sr_text ) {
-                       ldap_memfree( (char *)candidates[ i ].sr_text );
-                       candidates[ i ].sr_text = NULL;
-               }
+       if ( dolock )
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
 
-               if ( candidates[ i ].sr_ref ) {
-                       ber_bvarray_free( candidates[ i ].sr_ref );
-                       candidates[ i ].sr_ref = NULL;
-               }
+       return META_SEARCH_CANDIDATE;
+}
 
-               if ( candidates[ i ].sr_ctrls ) {
-                       ldap_controls_free( candidates[ i ].sr_ctrls );
-                       candidates[ i ].sr_ctrls = NULL;
-               }
+meta_search_candidate_t
+asyncmeta_return_bind_errors(a_metaconn_t *mc, int candidate, SlapReply *bind_result, void *ctx, int dolock)
+{
+       a_metainfo_t    *mi = mc->mc_info;
+       bm_context_t *bc, *onext;
+
+       if ( dolock )
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
 
-               if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
-                       asyncmeta_quarantine( op, mi, &candidates[ i ], i );
+       for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
+               onext = LDAP_STAILQ_NEXT(bc, bc_next);
+               if (bc->candidates[candidate].sr_msgid != META_MSGID_NEED_BIND
+                   || bc->bc_active > 0 || bc->op->o_abandon > 0) {
+                       continue;
                }
-               /* this remains as it is only for explicitly bound connections, for the time being */
-               /* only in case of timelimit exceeded, if the timelimit exceeded because
-                * one contacted target never responded, invalidate the connection
-                * NOTE: should we quarantine the target as well?  right now, the connection
-                * is invalidated; the next time it will be recreated and the target
-                * will be quarantined if it cannot be contacted */
-               if ( mi->mi_idle_timeout != 0
-                       && rs->sr_err == LDAP_TIMELIMIT_EXCEEDED
-                       && op->o_time > mc->mc_conns[ i ].msc_time )
-               {
-                       /* don't let anyone else use this expired connection */
-                       do_taint++;
+               bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               bc->candidates[ candidate ].sr_type = REP_RESULT;
+               bc->candidates[ candidate ].sr_err = bind_result->sr_err;
+               if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
+                   (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
+                       LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+                       bc->op->o_threadctx = ctx;
+                       bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+                       slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+                       bc->rs.sr_err = bind_result->sr_err;
+                       bc->rs.sr_text = bind_result->sr_text;
+                       mc->pending_ops--;
+                       asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
+                       asyncmeta_clear_bm_context(bc);
                }
        }
 
-       if ( mc && do_taint) {
-               LDAP_BACK_CONN_TAINTED_SET( mc );
-       }
+       if ( dolock )
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
 
-       return rs->sr_err;
+       return META_SEARCH_CANDIDATE;
 }
 
-
-int
-asyncmeta_handle_bind_result(LDAPMessage *msg, a_metaconn_t *mc, bm_context_t *bc, int candidate)
+static meta_search_candidate_t
+asyncmeta_handle_bind_result(LDAPMessage *msg, a_metaconn_t *mc, int candidate, void *ctx)
 {
        meta_search_candidate_t retcode;
-       a_metainfo_t    *mi;
-       a_metatarget_t  *mt;
-       Operation *op;
-       SlapReply *rs;
-       int        rc;
-       SlapReply *candidates;
-
-       mi = mc->mc_info;
-       mt = mi->mi_targets[ candidate ];
-       op = bc->op;
-       rs = &bc->rs;
-       candidates = bc->candidates;
-       Debug( LDAP_DEBUG_TRACE,
-              "%s asyncmeta_handle_bind_result[%d]\n",
-              op->o_log_prefix, candidate );
-       retcode = asyncmeta_dobind_result( op, rs, mc, candidate, candidates, msg );
+       SlapReply bind_result = {0};
+       /* could modify the msc, safer to lock it */
+       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+       retcode = asyncmeta_dobind_result( mc, candidate, &bind_result, msg );
+       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
        if ( retcode == META_SEARCH_CANDIDATE ) {
-               switch (op->o_tag) {
-               case LDAP_REQ_SEARCH:
-                       retcode = asyncmeta_back_search_start( op, rs, mc, bc, candidate,  NULL, 0 );
-                       break;
-               case LDAP_REQ_ADD:
-                       retcode = asyncmeta_back_add_start( op, rs, mc, bc, candidate);
-                       break;
-               case LDAP_REQ_MODIFY:
-                       retcode = asyncmeta_back_modify_start( op, rs, mc, bc, candidate);
-                       break;
-               case LDAP_REQ_MODRDN:
-                       retcode = asyncmeta_back_modrdn_start( op, rs, mc, bc, candidate);
-                       break;
-               case LDAP_REQ_COMPARE:
-                       retcode = asyncmeta_back_compare_start( op, rs, mc, bc, candidate);
-                       break;
-               case LDAP_REQ_DELETE:
-                       retcode = asyncmeta_back_delete_start( op, rs, mc, bc, candidate);
-                       break;
-               default:
-                       retcode = META_SEARCH_NOT_CANDIDATE;
-               }
-       }
-
-       switch ( retcode ) {
-       case META_SEARCH_CANDIDATE:
-               break;
-               /* means that failed but onerr == continue */
-       case META_SEARCH_NOT_CANDIDATE:
-       case META_SEARCH_ERR:
-               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-               candidates[ candidate ].sr_type = REP_RESULT;
-               candidates[ candidate ].sr_err = rs->sr_err;
-               if ( META_BACK_ONERR_STOP( mi ) || op->o_tag != LDAP_REQ_SEARCH) {
-                       send_ldap_result(op, rs);
-               }
-               if (op->o_tag != LDAP_REQ_SEARCH) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-                       asyncmeta_drop_bc( mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
-                       asyncmeta_clear_bm_context(bc);
-                       return retcode;
-               }
-               break;
-       default:
-               assert( 0 );
-               break;
+               /* send the remaining pending ops */
+               asyncmeta_send_all_pending_ops(mc, candidate, ctx, 1);
+       } else {
+               asyncmeta_return_bind_errors(mc, candidate, &bind_result, ctx, 1);
        }
-       bc->bc_active = 0;
        return retcode;
 }
 
@@ -833,9 +735,9 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
        a_metainfo_t    *mi;
        a_metatarget_t  *mt;
        a_metasingleconn_t *msc;
-       Operation op;
+       Operation *op = bc->op;
        SlapReply *rs;
-       int        i, j, rc, sres;
+       int        i, rc = LDAP_SUCCESS, sres;
        SlapReply *candidates;
        char            **references = NULL;
        LDAPControl     **ctrls = NULL;
@@ -843,48 +745,55 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
        LDAPMessage *msg;
        ber_int_t id;
 
-       op = *bc->op;
        rs = &bc->rs;
        mi = mc->mc_info;
        mt = mi->mi_targets[ candidate ];
        msc = &mc->mc_conns[ candidate ];
+       dc.op = op;
        dc.target = mt;
-       dc.conn = op.o_conn;
-       dc.rs = rs;
+       dc.to_from = MASSAGE_REP;
        id = ldap_msgid(res);
 
+
        candidates = bc->candidates;
        i = candidate;
 
-       while (res) {
+       while (res && !META_BACK_CONN_INVALID(msc)) {
        for (msg = ldap_first_message(msc->msc_ldr, res); msg; msg = ldap_next_message(msc->msc_ldr, msg)) {
                switch(ldap_msgtype(msg)) {
                case LDAP_RES_SEARCH_ENTRY:
                        Debug( LDAP_DEBUG_TRACE,
                                "%s asyncmeta_handle_search_msg: msc %p entry\n",
-                               op.o_log_prefix, msc );
+                               op->o_log_prefix, msc );
                        if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
                                /* don't retry any more... */
                                candidates[ i ].sr_type = REP_RESULT;
                        }
                        /* count entries returned by target */
                        candidates[ i ].sr_nentries++;
-                       rs->sr_err = asyncmeta_send_entry( &op, rs, mc, i, msg );
+                       if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !op->o_abandon) {
+                               rs->sr_err = asyncmeta_send_entry( &bc->copy_op, rs, mc, i, msg );
+                       } else {
+                               goto err_cleanup;
+                       }
                        switch ( rs->sr_err ) {
                        case LDAP_SIZELIMIT_EXCEEDED:
-                               send_ldap_result(&op, rs);
+                               asyncmeta_send_ldap_result(bc, op, rs);
                                rs->sr_err = LDAP_SUCCESS;
                                goto err_cleanup;
                        case LDAP_UNAVAILABLE:
                                rs->sr_err = LDAP_OTHER;
+                               break;
+                       default:
+                               break;
                        }
                        bc->is_ok++;
                        break;
 
                case LDAP_RES_SEARCH_REFERENCE:
-                       if ( META_BACK_TGT_NOREFS( mi->mi_targets[ i ] ) ) {
+                       if ( META_BACK_TGT_NOREFS( mt ) ) {
                                rs->sr_err = LDAP_OTHER;
-                               send_ldap_result(&op, rs);
+                               asyncmeta_send_ldap_result(bc, op, rs);
                                goto err_cleanup;
                        }
                        if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
@@ -897,7 +806,7 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
 
                        if ( rc != LDAP_SUCCESS || references == NULL ) {
                                rs->sr_err = LDAP_OTHER;
-                               send_ldap_result(&op, rs);
+                               asyncmeta_send_ldap_result(bc, op, rs);
                                goto err_cleanup;
                        }
 
@@ -909,28 +818,27 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                                        ;
 
                                rs->sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ),
-                                                                op.o_tmpmemctx );
+                                                                op->o_tmpmemctx );
 
                                for ( cnt = 0; references[ cnt ]; cnt++ ) {
                                        ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ],
-                                                         op.o_tmpmemctx );
+                                                         op->o_tmpmemctx );
                                }
                                BER_BVZERO( &rs->sr_ref[ cnt ] );
                        }
 
                        {
-                               dc.ctx = "referralDN";
-                               ( void )asyncmeta_referral_result_rewrite( &dc, rs->sr_ref,
-                                                                  op.o_tmpmemctx );
+                               dc.memctx = op->o_tmpmemctx;
+                               ( void )asyncmeta_referral_result_rewrite( &dc, rs->sr_ref );
                        }
 
                        if ( rs->sr_ref != NULL ) {
                                if (!BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
                                        /* ignore return value by now */
-                                       ( void )send_search_reference( &op, rs );
+                                       ( void )send_search_reference( op, rs );
                                }
 
-                               ber_bvarray_free_x( rs->sr_ref, op.o_tmpmemctx );
+                               ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx );
                                rs->sr_ref = NULL;
                        }
 
@@ -963,11 +871,11 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                        if ( rs->sr_err != LDAP_SUCCESS ) {
                                candidates[ i ].sr_type = REP_RESULT;
                                rs->sr_err = LDAP_OTHER;
-                               send_ldap_result(&op, rs);
+                               asyncmeta_send_ldap_result(bc, op, rs);
                                goto err_cleanup;
                        }
 
-                       slap_send_ldap_intermediate( &op, rs );
+                       slap_send_ldap_intermediate( op, rs );
 
                        if ( rs->sr_rspoid != NULL ) {
                                ber_memfree( (char *)rs->sr_rspoid );
@@ -986,9 +894,12 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                        break;
 
                case LDAP_RES_SEARCH_RESULT:
+                       if ( mi->mi_idle_timeout != 0 ) {
+                               asyncmeta_set_msc_time(msc);
+                       }
                        Debug( LDAP_DEBUG_TRACE,
-                               "%s asyncmeta_handle_search_msg: msc %p result\n",
-                               op.o_log_prefix, msc );
+                              "%s asyncmeta_handle_search_msg: msc %p result\n",
+                              op->o_log_prefix, msc );
                        candidates[ i ].sr_type = REP_RESULT;
                        candidates[ i ].sr_msgid = META_MSGID_IGNORE;
                        /* NOTE: ignores response controls
@@ -1025,19 +936,17 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                                                0, 0, &match );
                                candidates[ i ].sr_matched = NULL;
 
-                               dc.ctx = "matchedDN";
-                               dc.target = mi->mi_targets[ i ];
-                               if ( !asyncmeta_dn_massage( &dc, &match, &mmatch ) ) {
-                                       if ( mmatch.bv_val == match.bv_val ) {
-                                               candidates[ i ].sr_matched
-                                                       = ch_strdup( mmatch.bv_val );
-
-                                       } else {
-                                               candidates[ i ].sr_matched = mmatch.bv_val;
-                                       }
+                               dc.memctx = NULL;
+                               asyncmeta_dn_massage( &dc, &match, &mmatch );
+                               if ( mmatch.bv_val == match.bv_val ) {
+                                       candidates[ i ].sr_matched
+                                               = ch_strdup( mmatch.bv_val );
 
-                                       bc->candidate_match++;
+                               } else {
+                                       candidates[ i ].sr_matched = mmatch.bv_val;
                                }
+
+                               bc->candidate_match++;
                                ldap_memfree( match.bv_val );
                        }
 
@@ -1052,7 +961,7 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                                        Debug( LDAP_DEBUG_ANY,
                                                   "%s asncmeta_search_result[%d]: "
                                                   "got referrals with err=%d\n",
-                                                  op.o_log_prefix,
+                                                  op->o_log_prefix,
                                                   i, rs->sr_err );
 
                                } else {
@@ -1063,16 +972,16 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                                                ;
 
                                        sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ),
-                                                                op.o_tmpmemctx );
+                                                                op->o_tmpmemctx );
 
                                        for ( cnt = 0; references[ cnt ]; cnt++ ) {
                                                ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ],
-                                                                 op.o_tmpmemctx );
+                                                                 op->o_tmpmemctx );
                                        }
                                        BER_BVZERO( &sr_ref[ cnt ] );
 
-                                       ( void )asyncmeta_referral_result_rewrite( &dc, sr_ref,
-                                                                                  op.o_tmpmemctx );
+                                       dc.memctx = op->o_tmpmemctx;
+                                       ( void )asyncmeta_referral_result_rewrite( &dc, sr_ref );
 
                                        if ( rs->sr_v2ref == NULL ) {
                                                rs->sr_v2ref = sr_ref;
@@ -1080,18 +989,18 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                                        } else {
                                                for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
                                                        ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ],
-                                                                          op.o_tmpmemctx );
+                                                                          op->o_tmpmemctx );
                                                }
-                                               ber_memfree_x( sr_ref, op.o_tmpmemctx );
+                                               ber_memfree_x( sr_ref, op->o_tmpmemctx );
                                        }
                                }
 
                        } else if ( rs->sr_err == LDAP_REFERRAL ) {
-                               Debug( LDAP_DEBUG_ANY,
+                               Debug( LDAP_DEBUG_TRACE,
                                           "%s asyncmeta_search_result[%d]: "
                                           "got err=%d with null "
                                           "or empty referrals\n",
-                                          op.o_log_prefix,
+                                          op->o_log_prefix,
                                           i, rs->sr_err );
 
                                rs->sr_err = LDAP_NO_SUCH_OBJECT;
@@ -1102,21 +1011,18 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
 
                        sres = slap_map_api2result( rs );
 
-                       if ( LogTest( LDAP_DEBUG_TRACE | LDAP_DEBUG_ANY ) ) {
-                               char buf[ SLAP_TEXT_BUFLEN ];
-                               snprintf( buf, sizeof( buf ),
-                                         "%s asyncmeta_search_result[%ld] "
-                                         "match=\"%s\" err=%d",
-                                         op.o_log_prefix, i,
-                                         candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
-                                         (long) candidates[ i ].sr_err );
-                               if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
-                                       Debug( LDAP_DEBUG_TRACE, "%s.\n", buf );
-
-                               } else {
-                                       Debug( LDAP_DEBUG_ANY, "%s (%s).\n",
-                                                  buf, ldap_err2string( candidates[ i ].sr_err ) );
-                                                               }
+                       if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
+                               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d] "
+                                      "match=\"%s\" err=%ld",
+                                      op->o_log_prefix, i,
+                                      candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
+                                      (long) candidates[ i ].sr_err );
+                       } else {
+                                       Debug( LDAP_DEBUG_ANY,  "%s asyncmeta_search_result[%d] "
+                                      "match=\"%s\" err=%ld (%s)",
+                                      op->o_log_prefix, i,
+                                      candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
+                                              (long) candidates[ i ].sr_err, ldap_err2string( candidates[ i ].sr_err ) );
                        }
 
                        switch ( sres ) {
@@ -1146,7 +1052,7 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                                                struct berval prcookie;
 
                                                /* unsolicited, do not accept */
-                                               if ( mi->mi_targets[i]->mt_ps == 0 ) {
+                                               if ( mt->mt_ps == 0 ) {
                                                        rs->sr_err = LDAP_OTHER;
                                                        goto err_pr;
                                                }
@@ -1161,7 +1067,7 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
 
                                                /* more pages? new search request */
                                                if ( !BER_BVISNULL( &prcookie ) && !BER_BVISEMPTY( &prcookie ) ) {
-                                                       if ( mi->mi_targets[i]->mt_ps > 0 ) {
+                                                       if ( mt->mt_ps > 0 ) {
                                                                /* ignore size if specified */
                                                                prsize = 0;
 
@@ -1178,7 +1084,7 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                                                        assert( candidates[ i ].sr_text == NULL );
                                                        assert( candidates[ i ].sr_ref == NULL );
 
-                                                       switch ( asyncmeta_back_search_start( &op, rs, mc, bc, i, &prcookie, prsize ) )
+                                                       switch ( asyncmeta_back_search_start( &bc->copy_op, rs, mc, bc, i, &prcookie, prsize, 1 ) )
                                                        {
                                                        case META_SEARCH_CANDIDATE:
                                                                assert( candidates[ i ].sr_msgid >= 0 );
@@ -1186,11 +1092,12 @@ asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc
                                                                //      goto free_message;
 
                                                        case META_SEARCH_ERR:
+                                                       case META_SEARCH_NEED_BIND:
 err_pr:;
                                                                candidates[ i ].sr_err = rs->sr_err;
                                                                candidates[ i ].sr_type = REP_RESULT;
                                                                if ( META_BACK_ONERR_STOP( mi ) ) {
-                                                                       send_ldap_result(&op, rs);
+                                                                       asyncmeta_send_ldap_result(bc, op, rs);
                                                                        ldap_controls_free( ctrls );
                                                                        goto err_cleanup;
                                                                }
@@ -1231,16 +1138,18 @@ err_pr:;
                                 * than what requested by the proxy;
                                 * ignore it */
                                candidates[ i ].sr_err = rs->sr_err;
-                               if ( rs->sr_nentries == op.ors_slimit
+                               if ( rs->sr_nentries == op->ors_slimit
                                         || META_BACK_ONERR_STOP( mi ) )
                                {
                                        const char *save_text;
 got_err:
                                        save_text = rs->sr_text;
                                        rs->sr_text = candidates[ i ].sr_text;
-                                       send_ldap_result(&op, rs);
-                                       ch_free( candidates[ i ].sr_text );
-                                       candidates[ i ].sr_text = NULL;
+                                       asyncmeta_send_ldap_result(bc, op, rs);
+                                       if (candidates[ i ].sr_text != NULL) {
+                                               ch_free( (char *)candidates[ i ].sr_text );
+                                               candidates[ i ].sr_text = NULL;
+                                       }
                                        rs->sr_text = save_text;
                                        ldap_controls_free( ctrls );
                                        goto err_cleanup;
@@ -1259,14 +1168,14 @@ got_err:
                        if (asyncmeta_is_last_result(mc, bc, i) == 0) {
                                Debug( LDAP_DEBUG_TRACE,
                                        "%s asyncmeta_handle_search_msg: msc %p last result\n",
-                                       op.o_log_prefix, msc );
+                                       op->o_log_prefix, msc );
                                asyncmeta_search_last_result(mc, bc, i, sres);
 err_cleanup:
                                rc = rs->sr_err;
                                ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
                                asyncmeta_drop_bc( mc, bc);
-                               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
                                asyncmeta_clear_bm_context(bc);
+                               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
                                ldap_msgfree(res);
                                return rc;
                        }
@@ -1282,9 +1191,14 @@ finish:
                if (candidates[ i ].sr_type != REP_RESULT) {
                        struct timeval  tv = {0};
                        rc = ldap_result( msc->msc_ldr, id, LDAP_MSG_RECEIVED, &tv, &res );
+                       if (res != NULL) {
+                               msc->msc_result_time = slap_get_time();
+                       }
                }
        }
-       bc->bc_active = 0;
+       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+       bc->bc_active--;
+       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
 
        return rc;
 }
@@ -1326,8 +1240,8 @@ int asyncmeta_handle_common_result(LDAPMessage *msg, a_metaconn_t *mc, bm_contex
        rs->sr_ctrls = NULL;
 
        /* only touch when activity actually took place... */
-       if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
-               msc->msc_time = op->o_time;
+       if ( mi->mi_idle_timeout != 0 ) {
+               asyncmeta_set_msc_time(msc);
        }
 
        rc = ldap_parse_result( msc->msc_ldr, msg, &rs->sr_err,
@@ -1413,20 +1327,20 @@ int asyncmeta_handle_common_result(LDAPMessage *msg, a_metaconn_t *mc, bm_contex
                rs->sr_matched = matched;
        }
 
-       if ( rs->sr_err == LDAP_UNAVAILABLE ) {
-               if ( !( bc->sendok & LDAP_BACK_RETRYING ) ) {
-                       if ( op->o_conn && ( bc->sendok & LDAP_BACK_SENDERR ) ) {
-                               if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
-                                       send_ldap_result( op, rs );
+       if ( rs->sr_err == LDAP_UNAVAILABLE || rs->sr_err == LDAP_SERVER_DOWN ) {
+               if ( rs->sr_text == NULL ) {
+                               rs->sr_text = "Target is unavailable";
                        }
-               }
+       }
 
-       } else if ( op->o_conn &&
-               ( ( ( bc->sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
-                       || ( ( bc->sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
-       {
-                       send_ldap_result( op, rs );
+       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+       asyncmeta_drop_bc( mc, bc);
+       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+       if ( op->o_conn ) {
+               asyncmeta_send_ldap_result(bc, op, rs);
        }
+
        if ( matched ) {
                op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
        }
@@ -1451,52 +1365,60 @@ int asyncmeta_handle_common_result(LDAPMessage *msg, a_metaconn_t *mc, bm_contex
        rs->sr_ctrls = save_ctrls;
        rc = (LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err);
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-       asyncmeta_drop_bc( mc, bc);
-       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
        asyncmeta_clear_bm_context(bc);
+       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
        return rc;
 }
 
 /* This takes care to clean out the outbound queue in case we have a read error
  * sending back responses to the client */
 int
-asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error)
+asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error, void* ctx)
 {
        bm_context_t *bc, *onext;
-       int rc, cleanup;
+       int cleanup;
        Operation *op;
        SlapReply *rs;
        SlapReply *candidates;
        /* no outstanding ops, nothing to do but log */
-       Debug( LDAP_DEBUG_ANY,
-              "asyncmeta_op_read_error: %x\n",
-              error );
-#if 0
-       if (mc->mc_conns[candidate].conn) {
-               Connection *conn = mc->mc_conns[candidate].conn;
-               mc->mc_conns[candidate].conn = NULL;
-               connection_client_stop(conn);
-       }
-#endif
+       Debug( LDAP_DEBUG_TRACE,
+              "asyncmeta_op_read_error: ldr=%p\n",
+              mc->mc_conns[candidate].msc_ldr );
+
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-       asyncmeta_clear_one_msc(NULL, mc, candidate);
+       /*someone may be trying to write */
+       if (mc->mc_conns[candidate].msc_active <= 1) {
+               asyncmeta_clear_one_msc(NULL, mc, candidate, 0, __FUNCTION__);
+       } else {
+               META_BACK_CONN_INVALID_SET(&mc->mc_conns[candidate]);
+       }
+
        if (mc->pending_ops <= 0) {
                ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
                return LDAP_SUCCESS;
        }
 
-       for (bc = LDAP_SLIST_FIRST(&mc->mc_om_list); bc; bc = onext) {
-               onext = LDAP_SLIST_NEXT(bc, bc_next);
+       for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
+               onext = LDAP_STAILQ_NEXT(bc, bc_next);
                cleanup = 0;
                candidates = bc->candidates;
                /* was this op affected? */
                if ( !META_IS_CANDIDATE( &candidates[ candidate ] ) )
                        continue;
 
-               if (bc->op->o_abandon == 1) {
+               if (bc->op->o_abandon) {
+                       bc->bc_invalid = 1;
                        continue;
                }
 
+               if (bc->bc_active > 0) {
+                       continue;
+               }
+
+               bc->op->o_threadctx = ctx;
+               bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+               slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+
                op = bc->op;
                rs = &bc->rs;
                switch (op->o_tag) {
@@ -1506,18 +1428,20 @@ asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error)
                case LDAP_REQ_COMPARE:
                case LDAP_REQ_DELETE:
                        rs->sr_err = LDAP_UNAVAILABLE;
-                       send_ldap_error( op, rs, rs->sr_err , "Read error on connection to target" );
+                       rs->sr_text = "Read error on connection to target";
+                       asyncmeta_send_ldap_result( bc, op, rs );
                        cleanup = 1;
                        break;
                case LDAP_REQ_SEARCH:
                {
                        a_metainfo_t *mi = mc->mc_info;
                        rs->sr_err = LDAP_UNAVAILABLE;
+                       rs->sr_text = "Read error on connection to target";
                        candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
                        candidates[ candidate ].sr_type = REP_RESULT;
-                       if ( META_BACK_ONERR_STOP( mi ) ||
-                               asyncmeta_is_last_result(mc, bc, candidate) && op->o_conn) {
-                               send_ldap_error(op, rs, rs->sr_err, "Read error on connection to target");
+                       if ( (META_BACK_ONERR_STOP( mi ) ||
+                             asyncmeta_is_last_result(mc, bc, candidate)) && op->o_conn) {
+                               asyncmeta_send_ldap_result( bc, op, rs );
                                cleanup = 1;
                        }
                }
@@ -1532,12 +1456,12 @@ asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error)
                        for (j=0; j<mi->mi_ntargets; j++) {
                                if (j != candidate && bc->candidates[j].sr_msgid >= 0
                                    && mc->mc_conns[j].msc_ld != NULL) {
-                                       asyncmeta_back_abandon_candidate( mc, op,
-                                                                         bc->candidates[ j ].sr_msgid, j );
+                                       asyncmeta_back_cancel( mc, op,
+                                                              bc->candidates[ j ].sr_msgid, j );
                                }
                        }
-                       asyncmeta_drop_bc( mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+                       LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+                       mc->pending_ops--;
                        asyncmeta_clear_bm_context(bc);
                }
        }
@@ -1549,7 +1473,7 @@ void *
 asyncmeta_op_handle_result(void *ctx, void *arg)
 {
        a_metaconn_t *mc = arg;
-       int             i, j, rc, ntargets, processed;
+       int             i, j, rc, ntargets;
        struct timeval  tv = {0};
        LDAPMessage     *msg;
        a_metasingleconn_t *msc;
@@ -1567,30 +1491,71 @@ asyncmeta_op_handle_result(void *ctx, void *arg)
        oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);   /* get existing memctx */
 
 again:
-       processed = 0;
        for (j=0; j<ntargets; j++) {
                i++;
                if (i >= ntargets) i = 0;
-               if (!mc->mc_conns[i].msc_ldr || mc->mc_conns[i].msc_pending_ops <= 0) continue;
-               rc = ldap_result( mc->mc_conns[i].msc_ldr, LDAP_RES_ANY, LDAP_MSG_RECEIVED, &tv, &msg );
                msc = &mc->mc_conns[i];
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+               if (!mc->mc_conns[i].msc_ldr ||
+                   META_BACK_CONN_CREATING( &mc->mc_conns[i] ) ||
+                   META_BACK_CONN_INVALID(&mc->mc_conns[i])) {
+                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+                       continue;
+               }
+
+               msc->msc_active++;
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+               rc = ldap_result( mc->mc_conns[i].msc_ldr, LDAP_RES_ANY, LDAP_MSG_RECEIVED, &tv, &msg );
                if (rc < 1) {
                        if (rc < 0) {
-                               asyncmeta_op_read_error(mc, i, rc);
+                               ldap_get_option( mc->mc_conns[i].msc_ldr, LDAP_OPT_ERROR_NUMBER, &rc);
+                               asyncmeta_op_read_error(mc, i, rc, ctx);
+                       }
+                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+                       msc->msc_active--;
+                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+                       continue;
+               }
+               rc = ldap_msgtype( msg );
+               if (rc == LDAP_RES_BIND) {
+                       if ( LogTest( asyncmeta_debug ) ) {
+                               char    time_buf[ SLAP_TEXT_BUFLEN ];
+                               asyncmeta_get_timestamp(time_buf);
+                               Debug( asyncmeta_debug, "[%s] asyncmeta_op_handle_result received bind msgid=%d msc: %p\n",
+                                     time_buf, ldap_msgid(msg), msc );
                        }
+                       asyncmeta_handle_bind_result(msg, mc, i, ctx);
+                       mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
+                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+                       msc->msc_result_time = slap_get_time();
+                       msc->msc_active--;
+                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+                       if (msg)
+                               ldap_msgfree(msg);
+
                        continue;
                }
-               Debug(LDAP_DEBUG_TRACE, "asyncmeta_op_handle_result: got msgid %d on msc %p\n",
-                       ldap_msgid(msg), msc );
+retry_bc:
                ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
                bc = asyncmeta_find_message(ldap_msgid(msg), mc, i);
+/* The sender might not be yet done with the context. On error it might also remove it
+ * so it's best to try and find it again after a wait */
+               if (bc && bc->bc_active > 0) {
+                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+                       ldap_pvt_thread_yield();
+                       goto retry_bc;
+               }
+               if (bc) {
+                       bc->bc_active++;
+               }
 
-               if (bc)
-                       bc->bc_active = 1;
+               msc->msc_result_time = slap_get_time();
+               msc->msc_active--;
                ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
                if (!bc) {
-                       Debug( LDAP_DEBUG_ANY,
-                               "asyncmeta_op_handle_result: Unable to find bc for msguid %d\n", ldap_msgid(msg) );
+                       Debug( asyncmeta_debug,
+                               "asyncmeta_op_handle_result: Unable to find bc for msguid %d, msc: %p\n", ldap_msgid(msg), msc );
                        ldap_msgfree(msg);
                        continue;
                }
@@ -1599,38 +1564,34 @@ again:
                bc->op->o_threadctx = ctx;
                bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
                slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
-               if (bc->op->o_abandon == 1) {
+               if (bc->op->o_abandon) {
                        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
                        asyncmeta_drop_bc( mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+                       if ( bc->op->o_tag == LDAP_REQ_SEARCH ) {
+                               int j;
+                               for (j=0; j<ntargets; j++) {
+                                       if (bc->candidates[j].sr_msgid >= 0) {
+                                               a_metasingleconn_t *tmp_msc = &mc->mc_conns[j];
+                                               tmp_msc->msc_active++;
+                                               asyncmeta_back_cancel( mc, bc->op,
+                                                                      bc->candidates[ j ].sr_msgid, j );
+                                               tmp_msc->msc_active--;
+                                       }
+                               }
+                       }
                        asyncmeta_clear_bm_context(bc);
+                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
                        if (msg)
                                ldap_msgfree(msg);
                        continue;
                }
 
-               rc = ldap_msgtype( msg );
                switch (rc) {
-               case LDAP_RES_BIND:
-                       asyncmeta_handle_bind_result(msg, mc, bc, i);
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-                       LDAP_SLIST_FOREACH( bc, &mc->mc_om_list, bc_next ) {
-                               if (bc->candidates[i].sr_msgid == META_MSGID_NEED_BIND) {
-                                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
-                                       asyncmeta_handle_bind_result(msg, mc, bc, i);
-                                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-                               }
-                       }
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
-                       msc->msc_timeout_ops = 0;
-                       mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
-                       break;
                case LDAP_RES_SEARCH_ENTRY:
                case LDAP_RES_SEARCH_REFERENCE:
                case LDAP_RES_SEARCH_RESULT:
                case LDAP_RES_INTERMEDIATE:
                        asyncmeta_handle_search_msg(msg, mc, bc, i);
-                       msc->msc_timeout_ops = 0;
                        mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
                        msg = NULL;
                        break;
@@ -1640,14 +1601,16 @@ again:
                case LDAP_RES_COMPARE:
                case LDAP_RES_MODIFY:
                        rc = asyncmeta_handle_common_result(msg, mc, bc, i);
-                       msc->msc_timeout_ops = 0;
                        mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
                        break;
                default:
-                       Debug( LDAP_DEBUG_ANY,
-                             "asyncmeta_op_handle_result: "
-                             "unrecognized response message tag=%d\n",
-                             rc );
+                       {
+                       Debug( asyncmeta_debug,
+                                  "asyncmeta_op_handle_result: "
+                                  "unrecognized response message tag=%d\n",
+                                  rc );
+
+                       }
                }
                if (msg)
                        ldap_msgfree(msg);
@@ -1662,9 +1625,11 @@ again:
        }
        slap_sl_mem_setctx(ctx, oldctx);
        if (mc->mc_conns) {
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
                for (i=0; i<ntargets; i++)
                        if (mc->mc_conns[i].msc_ldr && mc->mc_conns[i].conn)
                                connection_client_enable(mc->mc_conns[i].conn);
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
        }
        return NULL;
 }
@@ -1678,42 +1643,114 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
 {
        struct re_s* rtask = arg;
        a_metainfo_t *mi = rtask->arg;
-       a_metaconn_t *mc;
        bm_context_t *bc, *onext;
        time_t current_time = slap_get_time();
        int i, j;
+       LDAP_STAILQ_HEAD(BCList, bm_context_t) timeout_list;
+       LDAP_STAILQ_INIT( &timeout_list );
 
+       Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time );
+       void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);
        for (i=0; i<mi->mi_num_conns; i++) {
-               mc = &mi->mi_conns[i];
+               a_metaconn_t * mc= &mi->mi_conns[i];
                ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
-               for (bc = LDAP_SLIST_FIRST(&mc->mc_om_list); bc; bc = onext) {
-                       onext = LDAP_SLIST_NEXT(bc, bc_next);
-                       if (!bc->bc_active && bc->timeout && bc->stoptime <= current_time) {
+               for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
+                       onext = LDAP_STAILQ_NEXT(bc, bc_next);
+                       if (bc->bc_active > 0) {
+                               continue;
+                       }
+
+                       if (bc->op->o_abandon ) {
+                                       /* set our memctx */
+                               bc->op->o_threadctx = ctx;
+                               bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+                               slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
                                Operation *op = bc->op;
-                               SlapReply *rs = &bc->rs;
-                               int             timeout_err;
-                               const char *timeout_text;
-                               LDAP_SLIST_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+
+                               LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
                                mc->pending_ops--;
+                               for (j=0; j<mi->mi_ntargets; j++) {
+                                       if (bc->candidates[j].sr_msgid >= 0) {
+                                               a_metasingleconn_t *msc = &mc->mc_conns[j];
+                                               if ( op->o_tag == LDAP_REQ_SEARCH ) {
+                                                       msc->msc_active++;
+                                                       asyncmeta_back_cancel( mc, op,
+                                                                              bc->candidates[ j ].sr_msgid, j );
+                                                       msc->msc_active--;
+                                               }
+                                       }
+                               }
+                               asyncmeta_clear_bm_context(bc);
+                               continue;
+                       }
+                       if (bc->bc_invalid) {
+                               LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+                               mc->pending_ops--;
+                               LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
+                               continue;
+                       }
+
+                       if (bc->timeout && bc->stoptime < current_time) {
+                               Operation *op = bc->op;
+                               LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+                               mc->pending_ops--;
+                               LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
+                               for (j=0; j<mi->mi_ntargets; j++) {
+                                       if (bc->candidates[j].sr_msgid >= 0) {
+                                               a_metasingleconn_t *msc = &mc->mc_conns[j];
+                                               asyncmeta_set_msc_time(msc);
+                                               if ( op->o_tag == LDAP_REQ_SEARCH ) {
+                                                       msc->msc_active++;
+                                                       asyncmeta_back_cancel( mc, op,
+                                                                              bc->candidates[ j ].sr_msgid, j );
+                                                       msc->msc_active--;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+               for (bc = LDAP_STAILQ_FIRST(&timeout_list); bc; bc = onext) {
+                       Operation *op = bc->op;
+                       SlapReply *rs = &bc->rs;
+                       int             timeout_err;
+                       const char *timeout_text;
+
+                       onext = LDAP_STAILQ_NEXT(bc, bc_next);
+                       LDAP_STAILQ_REMOVE(&timeout_list, bc, bm_context_t, bc_next);
+                       /* set our memctx */
+                       bc->op->o_threadctx = ctx;
+                       bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+                       slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+
+                       if (bc->searchtime) {
+                               timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+                       } else {
+                               timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+                                       LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+                       }
+
+                       if ( bc->bc_invalid ) {
+                               timeout_text = "Operation is invalid - target connection has been reset";
+                       } else {
+                               a_metasingleconn_t *log_msc =  &mc->mc_conns[0];
+                               Debug( asyncmeta_debug,
+                                      "asyncmeta_timeout_loop:Timeout op %s loop[%p], "
+                                      "current_time:%ld, op->o_time:%ld msc: %p, "
+                                      "msc->msc_binding_time: %x, msc->msc_flags:%x \n",
+                                      bc->op->o_log_prefix, rtask, current_time, bc->op->o_time,
+                                      log_msc, (unsigned int)log_msc->msc_binding_time, log_msc->msc_mscflags );
 
                                if (bc->searchtime) {
-                                       timeout_err = LDAP_TIMELIMIT_EXCEEDED;
                                        timeout_text = NULL;
                                } else {
-                                       timeout_err = op->o_protocol >= LDAP_VERSION3 ?
-                                               LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
                                        timeout_text = "Operation timed out";
                                }
+
                                for (j=0; j<mi->mi_ntargets; j++) {
                                        if (bc->candidates[j].sr_msgid >= 0) {
-                                               a_metasingleconn_t *msc = &mc->mc_conns[j];
                                                a_metatarget_t     *mt = mi->mi_targets[j];
-                                               msc->msc_timeout_ops++;
-                                               if (bc->msgids[j] >= 0) {
-                                                       msc->msc_pending_ops--;
-                                               }
-                                               asyncmeta_back_cancel( mc, op,
-                                                                      bc->candidates[ j ].sr_msgid, j );
                                                if (!META_BACK_TGT_QUARANTINE( mt ) ||
                                                    bc->candidates[j].sr_type == REP_RESULT) {
                                                        continue;
@@ -1733,25 +1770,37 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
                                                }
                                        }
                                }
-                               send_ldap_error( op, rs, timeout_err, timeout_text );
-                               asyncmeta_clear_bm_context(bc);
                        }
+                       rs->sr_err = timeout_err;
+                       rs->sr_text = timeout_text;
+                       if (!bc->op->o_abandon ) {
+                               asyncmeta_send_ldap_result( bc, bc->op, &bc->rs );
+                       }
+                       asyncmeta_clear_bm_context(bc);
                }
 
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
                if (mi->mi_idle_timeout) {
                        for (j=0; j<mi->mi_ntargets; j++) {
                                a_metasingleconn_t *msc = &mc->mc_conns[j];
-                               if (msc->msc_pending_ops > 0) {
+                               if ( msc->msc_active > 0 ) {
                                        continue;
                                }
-                               if (msc->msc_ld && msc->msc_time > 0 && msc->msc_time + mi->mi_idle_timeout <= current_time) {
-                                       asyncmeta_clear_one_msc(NULL, mc, j);
+                               if (mc->pending_ops > 0) {
+                                       continue;
+                               }
+                               current_time = slap_get_time();
+                               if (msc->msc_ld && msc->msc_time > 0 && msc->msc_time + mi->mi_idle_timeout < current_time) {
+                                       asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
                                }
                        }
                }
                ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
        }
 
+       slap_sl_mem_setctx(ctx, oldctx);
+       current_time = slap_get_time();
+       Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] stop at [%ld] \n", rtask, current_time );
        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
        if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
                ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
@@ -1763,15 +1812,3 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
        return NULL;
 }
 
-#if 0
-int
-asyncmeta_back_cleanup( Operation *op, SlapReply *rs, bm_context_t *bc )
-{
-       if (bc->ctrls != NULL && bc->cl != NULL && bc->cl->mc != NULL) {
-               a_metainfo_t *mi = bc->cl->mc->mc_info;
-               (void)mi->mi_ldap_extra->controls_free(op, rs, &bc->ctrls );
-       }
-       asyncmeta_clear_bm_context(bc, 1, 1);
-       return rs->sr_err;
-}
-#endif
index 362b1544ea33082f3a4e388a50285ec43b3fc900..ae7559ee3a735a42beab4c47783958f42817d844 100644 (file)
 
 #include <ac/string.h>
 #include <ac/socket.h>
-
 #include "slap.h"
-#include "../back-ldap/back-ldap.h"
-#include "back-asyncmeta.h"
 #include "../../../libraries/liblber/lber-int.h"
 #include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
 
 meta_search_candidate_t
 asyncmeta_back_modify_start(Operation *op,
                            SlapReply *rs,
                            a_metaconn_t *mc,
                            bm_context_t *bc,
-                           int candidate)
+                           int candidate,
+                           int do_lock)
 {
-       int             i, isupdate, rc = 0, nretries = 1;
+       int             i, isupdate, rc = 0;
        a_dncookie      dc;
        a_metainfo_t    *mi = mc->mc_info;
        a_metatarget_t  *mt = mi->mi_targets[ candidate ];
@@ -48,7 +48,6 @@ asyncmeta_back_modify_start(Operation *op,
        LDAPMod         *mods = NULL;
        struct berval mdn;
        Modifications   *ml;
-       struct berval   mapped;
        meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
                BerElement *ber = NULL;
        a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
@@ -59,122 +58,55 @@ asyncmeta_back_modify_start(Operation *op,
        /*
         * Rewrite the modify dn, if needed
         */
+       dc.op = op;
        dc.target = mt;
-       dc.conn = op->o_conn;
-       dc.rs = rs;
-       dc.ctx = "modifyDN";
+       dc.memctx = op->o_tmpmemctx;
+       dc.to_from = MASSAGE_REQ;
 
-       switch ( asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn ) )
-       {
-       case LDAP_SUCCESS:
-               break;
-       case LDAP_UNWILLING_TO_PERFORM:
-               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-               rs->sr_text = "Operation not allowed";
-               retcode = META_SEARCH_ERR;
-               goto doreturn;
-       default:
-               rs->sr_err = LDAP_NO_SUCH_OBJECT;
-               retcode = META_SEARCH_NOT_CANDIDATE;
-               goto doreturn;
-       }
+       asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
 
        for ( i = 0, ml = op->orm_modlist; ml; i++ ,ml = ml->sml_next )
                ;
+       if (i > 0) {
+               mods = op->o_tmpalloc( sizeof( LDAPMod )*i, op->o_tmpmemctx );
+       }
 
-       mods = ch_malloc( sizeof( LDAPMod )*i );
        if ( mods == NULL ) {
                rs->sr_err = LDAP_OTHER;
                retcode = META_SEARCH_ERR;
                goto doreturn;
        }
-       modv = ( LDAPMod ** )ch_malloc( ( i + 1 )*sizeof( LDAPMod * ) );
+       modv = ( LDAPMod ** )op->o_tmpalloc( ( i + 1 )*sizeof( LDAPMod * ), op->o_tmpmemctx );
        if ( modv == NULL ) {
                rs->sr_err = LDAP_OTHER;
                retcode = META_SEARCH_ERR;
                goto doreturn;
        }
 
-       dc.ctx = "modifyAttrDN";
        isupdate = be_shadow_update( op );
        for ( i = 0, ml = op->orm_modlist; ml; ml = ml->sml_next ) {
-               int     j, is_oc = 0;
+               int     j;
 
                if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod  )
                {
                        continue;
                }
 
-               if ( ml->sml_desc == slap_schema.si_ad_objectClass
-                               || ml->sml_desc == slap_schema.si_ad_structuralObjectClass )
-               {
-                       is_oc = 1;
-                       mapped = ml->sml_desc->ad_cname;
-
-               } else {
-                       asyncmeta_map( &mt->mt_rwmap.rwm_at,
-                                       &ml->sml_desc->ad_cname, &mapped,
-                                       BACKLDAP_MAP );
-                       if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
-                               continue;
-                       }
-               }
-
                modv[ i ] = &mods[ i ];
                mods[ i ].mod_op = ml->sml_op | LDAP_MOD_BVALUES;
-               mods[ i ].mod_type = mapped.bv_val;
+               mods[ i ].mod_type = ml->sml_desc->ad_cname.bv_val;
 
-               /*
-                * FIXME: dn-valued attrs should be rewritten
-                * to allow their use in ACLs at the back-ldap
-                * level.
-                */
                if ( ml->sml_values != NULL ) {
-                       if ( is_oc ) {
-                               for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ )
-                                       ;
-                               mods[ i ].mod_bvalues =
-                                       (struct berval **)ch_malloc( ( j + 1 ) *
-                                       sizeof( struct berval * ) );
-                               for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); ) {
-                                       struct ldapmapping      *mapping;
-
-                                       asyncmeta_mapping( &mt->mt_rwmap.rwm_oc,
-                                                       &ml->sml_values[ j ], &mapping, BACKLDAP_MAP );
-
-                                       if ( mapping == NULL ) {
-                                               if ( mt->mt_rwmap.rwm_oc.drop_missing ) {
-                                                       continue;
-                                               }
-                                               mods[ i ].mod_bvalues[ j ] = &ml->sml_values[ j ];
-
-                                       } else {
-                                               mods[ i ].mod_bvalues[ j ] = &mapping->dst;
-                                       }
-                                       j++;
-                               }
-                               mods[ i ].mod_bvalues[ j ] = NULL;
-
-                       } else {
-                               if ( ml->sml_desc->ad_type->sat_syntax ==
-                                               slap_schema.si_syn_distinguishedName )
-                               {
-                                       ( void )asyncmeta_dnattr_rewrite( &dc, ml->sml_values );
-                                       if ( ml->sml_values == NULL ) {
-                                               continue;
-                                       }
-                               }
-
-                               for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ )
-                                       ;
-                               mods[ i ].mod_bvalues =
-                                       (struct berval **)ch_malloc( ( j + 1 ) *
-                                       sizeof( struct berval * ) );
-                               for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ ) {
-                                       mods[ i ].mod_bvalues[ j ] = &ml->sml_values[ j ];
-                               }
-                               mods[ i ].mod_bvalues[ j ] = NULL;
+                       j = ml->sml_numvals;
+                       mods[ i ].mod_bvalues =(struct berval **)op->o_tmpalloc( ( j + 1 ) *sizeof( struct berval * ), op->o_tmpmemctx );
+                       for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ ) {
+                               mods[ i ].mod_bvalues[ j ] = op->o_tmpalloc(sizeof( struct berval ), op->o_tmpmemctx );
+                               if ( ml->sml_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+                                       asyncmeta_dn_massage( &dc, &ml->sml_values[ j ], mods[ i ].mod_bvalues[ j ] );
+                               else
+                                       *mods[ i ].mod_bvalues[ j ] = ml->sml_values[ j ];
                        }
+                       mods[ i ].mod_bvalues[ j ] = NULL;
 
                } else {
                        mods[ i ].mod_bvalues = NULL;
@@ -184,62 +116,113 @@ asyncmeta_back_modify_start(Operation *op,
        }
        modv[ i ] = 0;
 
-retry:;
+       asyncmeta_set_msc_time(msc);
        ctrls = op->o_ctrls;
-       if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
+       if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls) != LDAP_SUCCESS )
        {
                candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
                retcode = META_SEARCH_ERR;
                goto done;
        }
 
+       /* someone reset the connection */
+       if (!( LDAP_BACK_CONN_ISBOUND( msc )
+              || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+               Debug( asyncmeta_debug , "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+               goto error_unavailable;
+       }
+
        ber = ldap_build_modify_req( msc->msc_ld, mdn.bv_val, modv, ctrls, NULL, &msgid);
+
+       if (!ber) {
+               Debug( asyncmeta_debug, "%s asyncmeta_back_modify_start: Operation encoding failed with errno %d\n",
+                      op->o_log_prefix, msc->msc_ld->ld_errno );
+               rs->sr_err = LDAP_OPERATIONS_ERROR;
+               rs->sr_text = "Failed to encode proxied request";
+               retcode = META_SEARCH_ERR;
+               goto done;
+       }
+
        if (ber) {
-               candidates[ candidate ].sr_msgid = msgid;
-               rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODIFY,
-                                               mdn.bv_val, ber, msgid );
-               if (rc == msgid)
-                       rc = LDAP_SUCCESS;
-               else
-                       rc = LDAP_SERVER_DOWN;
+               struct timeval tv = {0, mt->mt_network_timeout*1000};
+               ber_socket_t s;
+               if (!( LDAP_BACK_CONN_ISBOUND( msc )
+                      || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+               if (s < 0) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+               if (rc < 0) {
+                       Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+                       if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+                               rc = LDAP_SERVER_DOWN;
+                       } else {
+                               goto error_unavailable;
+                       }
+               } else {
+                       candidates[ candidate ].sr_msgid = msgid;
+                       rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODIFY,
+                                                       mdn.bv_val, ber, msgid );
+                       if (rc == msgid)
+                               rc = LDAP_SUCCESS;
+                       else
+                               rc = LDAP_SERVER_DOWN;
+                       ber = NULL;
+               }
 
                switch ( rc ) {
                case LDAP_SUCCESS:
                        retcode = META_SEARCH_CANDIDATE;
                        asyncmeta_set_msc_time(msc);
-                       break;
+                       goto done;
 
                case LDAP_SERVER_DOWN:
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_clear_one_msc(NULL, mc, candidate);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) {
-                               nretries = 0;
-                               /* if the identity changed, there might be need to re-authz */
-                               (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
-                               goto retry;
+                       /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+                       if (do_lock > 0) {
+                               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+                               asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+                               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
                        }
-
+                       /* fall though*/
                default:
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       retcode = META_SEARCH_ERR;
+                       Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
                }
        }
 
+error_unavailable:
+       if (ber)
+               ber_free(ber, 1);
+       switch (bc->nretries[candidate]) {
+       case -1: /* nretries = forever */
+               ldap_pvt_thread_yield();
+               retcode = META_SEARCH_NEED_BIND;
+               break;
+       case 0: /* no retries left */
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               rs->sr_err = LDAP_UNAVAILABLE;
+               rs->sr_text = "Unable to send modify request to target";
+               retcode = META_SEARCH_ERR;
+               break;
+       default: /* more retries left - try to rebind and go again */
+               retcode = META_SEARCH_NEED_BIND;
+               bc->nretries[candidate]--;
+               ldap_pvt_thread_yield();
+               break;
+       }
 done:
        (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
 
        if ( mdn.bv_val != op->o_req_dn.bv_val ) {
-               free( mdn.bv_val );
-               BER_BVZERO( &mdn );
-       }
-       if ( modv != NULL ) {
-               for ( i = 0; modv[ i ]; i++ ) {
-                       free( modv[ i ]->mod_bvalues );
-               }
+               op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
        }
-       free( mods );
-       free( modv );
 
 doreturn:;
        Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_modify_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
@@ -253,26 +236,31 @@ asyncmeta_back_modify( Operation *op, SlapReply *rs )
        a_metatarget_t  *mt;
        a_metaconn_t    *mc;
        int             rc, candidate = -1;
-       OperationBuffer opbuf;
+       void *thrctx = op->o_threadctx;
        bm_context_t *bc;
        SlapReply *candidates;
-       slap_callback *cb = op->o_callback;
+       time_t current_time = slap_get_time();
+       int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
 
        Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_modify: %s\n",
              op->o_req_dn.bv_val );
 
-       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
+       if (current_time > op->o_time) {
+               Debug(asyncmeta_debug, "==> asyncmeta_back_modify[%s]: o_time:[%ld], current time: [%ld]\n",
+                     op->o_log_prefix, op->o_time, current_time );
+       }
+
+       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
        if (bc == NULL) {
                rs->sr_err = LDAP_OTHER;
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
        candidates = bc->candidates;
        mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
        if ( !mc || rs->sr_err != LDAP_SUCCESS) {
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
@@ -281,16 +269,38 @@ asyncmeta_back_modify( Operation *op, SlapReply *rs )
        bc->retrying = LDAP_BACK_RETRYING;
        bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
        bc->stoptime = op->o_time + bc->timeout;
+       bc->bc_active = 1;
+
+       if (mc->pending_ops >= max_pending_ops) {
+               rs->sr_err = LDAP_BUSY;
+               rs->sr_text = "Maximum pending ops limit exceeded";
+               send_ldap_result(op, rs);
+               return rs->sr_err;
+       }
 
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
        rc = asyncmeta_add_message_queue(mc, bc);
+       mc->mc_conns[candidate].msc_active++;
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
 
        if (rc != LDAP_SUCCESS) {
                rs->sr_err = LDAP_BUSY;
                rs->sr_text = "Maximum pending ops limit exceeded";
-               asyncmeta_clear_bm_context(bc);
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
+               ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);
+               mc->mc_conns[candidate].msc_active--;
+               ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+               goto finish;
+       }
+
+retry:
+       if (bc->timeout && bc->stoptime < slap_get_time()) {
+               int             timeout_err;
+               timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+                       LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+               rs->sr_err = timeout_err;
+               rs->sr_text = "Operation timed out before it was sent to target";
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
        }
 
@@ -300,70 +310,49 @@ asyncmeta_back_modify( Operation *op, SlapReply *rs )
        case META_SEARCH_CANDIDATE:
                /* target is already bound, just send the request */
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify:  "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
 
-               rc = asyncmeta_back_modify_start( op, rs, mc, bc, candidate);
+               rc = asyncmeta_back_modify_start( op, rs, mc, bc, candidate, 1);
                if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
 
+               } else if (rc == META_SEARCH_NEED_BIND) {
+                       goto retry;
                }
-                       break;
+               break;
        case META_SEARCH_NOT_CANDIDATE:
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: NOT_CANDIDATE "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
-               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-               asyncmeta_drop_bc(mc, bc);
-               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
 
        case META_SEARCH_NEED_BIND:
-       case META_SEARCH_CONNECTING:
-               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: NEED_BIND "
-                      "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
-               rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
-               if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
-                       goto finish;
-               }
-               break;
        case META_SEARCH_BINDING:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: BINDING "
-                              "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+                              "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
                        /* Todo add the context to the message queue but do not send the request
                           the receiver must send this when we are done binding */
-                       /* question - how would do receiver know to which targets??? */
                        break;
 
        case META_SEARCH_ERR:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: ERR "
-                              "cnd=\"%ldd\"\n", op->o_log_prefix, candidate );
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       candidates[ candidate ].sr_type = REP_RESULT;
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                              "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
                default:
                        assert( 0 );
                        break;
                }
+
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+       mc->mc_conns[candidate].msc_active--;
        asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+       bc->bc_active--;
+       asyncmeta_memctx_toggle(thrctx);
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+       rs->sr_err = SLAPD_ASYNCOP;
+
 finish:
        return rs->sr_err;
 }
index 2fe2de26886ad3677add7d0dfb06df03bd6ff75a..deaf63c42f9ef43d663b34a4e92f1f140df5dc23 100644 (file)
 
 #include <ac/socket.h>
 #include <ac/string.h>
-
 #include "slap.h"
-#include "../back-ldap/back-ldap.h"
-#include "back-asyncmeta.h"
 #include "../../../libraries/liblber/lber-int.h"
 #include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
 
 meta_search_candidate_t
 asyncmeta_back_modrdn_start(Operation *op,
                            SlapReply *rs,
                            a_metaconn_t *mc,
                            bm_context_t *bc,
-                           int candidate)
+                           int candidate,
+                           int do_lock)
 {
        a_dncookie      dc;
        a_metainfo_t    *mi = mc->mc_info;
@@ -46,7 +46,7 @@ asyncmeta_back_modrdn_start(Operation *op,
        struct berval   mdn = BER_BVNULL,
                mnewSuperior = BER_BVNULL,
                newrdn = BER_BVNULL;
-       int rc = 0, nretries = 1;
+       int rc = 0;
        LDAPControl     **ctrls = NULL;
        meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
        BerElement *ber = NULL;
@@ -54,9 +54,10 @@ asyncmeta_back_modrdn_start(Operation *op,
        SlapReply               *candidates = bc->candidates;
        ber_int_t       msgid;
 
+       dc.op = op;
        dc.target = mt;
-       dc.conn = op->o_conn;
-       dc.rs = rs;
+       dc.memctx = op->o_tmpmemctx;
+       dc.to_from = MASSAGE_REQ;
 
        if ( op->orr_newSup ) {
 
@@ -101,23 +102,13 @@ asyncmeta_back_modrdn_start(Operation *op,
                /*
                 * Rewrite the new superior, if defined and required
                 */
-               dc.ctx = "newSuperiorDN";
-               if ( asyncmeta_dn_massage( &dc, op->orr_newSup, &mnewSuperior ) ) {
-                       rs->sr_err = LDAP_OTHER;
-                       retcode = META_SEARCH_ERR;
-                       goto done;
-               }
+               asyncmeta_dn_massage( &dc, op->orr_newSup, &mnewSuperior );
        }
 
        /*
         * Rewrite the modrdn dn, if required
         */
-       dc.ctx = "modrDN";
-       if ( asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
-               rs->sr_err = LDAP_OTHER;
-               retcode = META_SEARCH_ERR;
-               goto done;
-       }
+       asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
 
        /* NOTE: we need to copy the newRDN in case it was formed
         * from a DN by simply changing the length (ITS#5397) */
@@ -125,69 +116,125 @@ asyncmeta_back_modrdn_start(Operation *op,
        if ( newrdn.bv_val[ newrdn.bv_len ] != '\0' ) {
                ber_dupbv_x( &newrdn, &op->orr_newrdn, op->o_tmpmemctx );
        }
-retry:;
+
+       asyncmeta_set_msc_time(msc);
        ctrls = op->o_ctrls;
-       if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
+       if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS )
        {
                candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
                retcode = META_SEARCH_ERR;
                goto done;
        }
-
+       /* someone might have reset the connection */
+       if (!( LDAP_BACK_CONN_ISBOUND( msc )
+              || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+               Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+               goto error_unavailable;
+       }
        ber = ldap_build_moddn_req( msc->msc_ld, mdn.bv_val, newrdn.bv_val,
                        mnewSuperior.bv_val, op->orr_deleteoldrdn, ctrls, NULL, &msgid);
+
+       if (!ber) {
+               Debug( asyncmeta_debug, "%s asyncmeta_back_modrdn_start: Operation encoding failed with errno %d\n",
+                      op->o_log_prefix, msc->msc_ld->ld_errno );
+               rs->sr_err = LDAP_OPERATIONS_ERROR;
+               rs->sr_text = "Failed to encode proxied request";
+               retcode = META_SEARCH_ERR;
+               goto done;
+       }
+
        if (ber) {
-               candidates[ candidate ].sr_msgid = msgid;
-               rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODRDN,
-                                               mdn.bv_val, ber, msgid );
-               if (rc == msgid)
-                       rc = LDAP_SUCCESS;
-               else
-                       rc = LDAP_SERVER_DOWN;
+               struct timeval tv = {0, mt->mt_network_timeout*1000};
+               ber_socket_t s;
+
+               if (!( LDAP_BACK_CONN_ISBOUND( msc )
+                      || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+               if (s < 0) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+               if (rc < 0) {
+                       Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+                       if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+                               rc = LDAP_SERVER_DOWN;
+                       } else {
+                               goto error_unavailable;
+                       }
+               } else {
+                       candidates[ candidate ].sr_msgid = msgid;
+                       rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODRDN,
+                                                       mdn.bv_val, ber, msgid );
+                       if (rc == msgid)
+                               rc = LDAP_SUCCESS;
+                       else
+                               rc = LDAP_SERVER_DOWN;
+                       ber = NULL;
+               }
 
                switch ( rc ) {
                case LDAP_SUCCESS:
                        retcode = META_SEARCH_CANDIDATE;
                        asyncmeta_set_msc_time(msc);
-                       break;
+                       goto done;
 
                case LDAP_SERVER_DOWN:
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_clear_one_msc(NULL, mc, candidate);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) {
-                               nretries = 0;
-                               /* if the identity changed, there might be need to re-authz */
-                               (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
-                               goto retry;
+                       /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+                       if (do_lock > 0) {
+                               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+                               asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__ );
+                               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
                        }
-
+                       /* fall though*/
                default:
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       retcode = META_SEARCH_ERR;
+                       Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
                }
        }
 
+error_unavailable:
+       if (ber)
+               ber_free(ber, 1);
+       switch (bc->nretries[candidate]) {
+       case -1: /* nretries = forever */
+               retcode = META_SEARCH_NEED_BIND;
+               ldap_pvt_thread_yield();
+               break;
+       case 0: /* no retries left */
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               rs->sr_err = LDAP_UNAVAILABLE;
+               rs->sr_text = "Unable to send modrdn request to target";
+               retcode = META_SEARCH_ERR;
+               break;
+       default: /* more retries left - try to rebind and go again */
+               retcode = META_SEARCH_NEED_BIND;
+               bc->nretries[candidate]--;
+               ldap_pvt_thread_yield();
+               break;
+       }
 done:
        (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
 
        if ( mdn.bv_val != op->o_req_dn.bv_val ) {
-               free( mdn.bv_val );
-               BER_BVZERO( &mdn );
+               op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
        }
 
        if ( !BER_BVISNULL( &mnewSuperior )
                        && mnewSuperior.bv_val != op->orr_newSup->bv_val )
        {
-               free( mnewSuperior.bv_val );
-               BER_BVZERO( &mnewSuperior );
+               op->o_tmpfree( mnewSuperior.bv_val, op->o_tmpmemctx );
        }
 
        if ( newrdn.bv_val != op->orr_newrdn.bv_val ) {
                op->o_tmpfree( newrdn.bv_val, op->o_tmpmemctx );
        }
 
-doreturn:;
        Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_modrdn_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
        return retcode;
 }
@@ -199,26 +246,30 @@ asyncmeta_back_modrdn( Operation *op, SlapReply *rs )
        a_metatarget_t  *mt;
        a_metaconn_t    *mc;
        int             rc, candidate = -1;
-       OperationBuffer opbuf;
+       void *thrctx = op->o_threadctx;
        bm_context_t *bc;
        SlapReply *candidates;
-       slap_callback *cb = op->o_callback;
+       time_t current_time = slap_get_time();
+       int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
 
        Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_modrdn: %s\n",
              op->o_req_dn.bv_val );
 
-       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
+       if (current_time > op->o_time) {
+               Debug(asyncmeta_debug, "==> asyncmeta_back_modrdn[%s]: o_time:[%ld], current time: [%ld]\n",
+                     op->o_log_prefix, op->o_time, current_time );
+       }
+       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
        if (bc == NULL) {
                rs->sr_err = LDAP_OTHER;
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
        candidates = bc->candidates;
        mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
        if ( !mc || rs->sr_err != LDAP_SUCCESS) {
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+               send_ldap_result(op, rs);
                return rs->sr_err;
        }
 
@@ -227,66 +278,69 @@ asyncmeta_back_modrdn( Operation *op, SlapReply *rs )
        bc->retrying = LDAP_BACK_RETRYING;
        bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
        bc->stoptime = op->o_time + bc->timeout;
+       bc->bc_active = 1;
+
+       if (mc->pending_ops >= max_pending_ops) {
+               rs->sr_err = LDAP_BUSY;
+               rs->sr_text = "Maximum pending ops limit exceeded";
+               send_ldap_result(op, rs);
+               return rs->sr_err;
+       }
 
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
        rc = asyncmeta_add_message_queue(mc, bc);
+       mc->mc_conns[candidate].msc_active++;
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
 
        if (rc != LDAP_SUCCESS) {
                rs->sr_err = LDAP_BUSY;
                rs->sr_text = "Maximum pending ops limit exceeded";
-               asyncmeta_clear_bm_context(bc);
-               asyncmeta_sender_error(op, rs, cb);
+               send_ldap_result(op, rs);
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+               mc->mc_conns[candidate].msc_active--;
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
                goto finish;
        }
 
+retry:
+       if (bc->timeout && bc->stoptime < slap_get_time()) {
+               int             timeout_err;
+               timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+                       LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+               rs->sr_err = timeout_err;
+               rs->sr_text = "Operation timed out before it was sent to target";
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+               goto finish;
+
+       }
+
        rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
        switch (rc)
        {
        case META_SEARCH_CANDIDATE:
                /* target is already bound, just send the request */
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn:  "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
 
-               rc = asyncmeta_back_modrdn_start( op, rs, mc, bc, candidate);
+               rc = asyncmeta_back_modrdn_start( op, rs, mc, bc, candidate, 1);
                if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
 
+               } else if (rc == META_SEARCH_NEED_BIND) {
+                       goto retry;
                }
-                       break;
+               break;
        case META_SEARCH_NOT_CANDIDATE:
                Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: NOT_CANDIDATE "
-                      "cnd=\"%ld\"\n", op->o_log_prefix, candidate );
-               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-               asyncmeta_drop_bc(mc, bc);
-               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-               asyncmeta_sender_error(op, rs, cb);
-               asyncmeta_clear_bm_context(bc);
+                      "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+               asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                goto finish;
 
        case META_SEARCH_NEED_BIND:
-       case META_SEARCH_CONNECTING:
-               Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: NEED_BIND "
-                      "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
-               rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
-               if (rc == META_SEARCH_ERR) {
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
-                       goto finish;
-               }
-               break;
        case META_SEARCH_BINDING:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: BINDING "
-                              "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+                              "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
                        /* Todo add the context to the message queue but do not send the request
                           the receiver must send this when we are done binding */
                        /* question - how would do receiver know to which targets??? */
@@ -294,22 +348,21 @@ asyncmeta_back_modrdn( Operation *op, SlapReply *rs )
 
        case META_SEARCH_ERR:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: ERR "
-                              "cnd=\"%ldd\"\n", op->o_log_prefix, candidate );
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       candidates[ candidate ].sr_type = REP_RESULT;
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       asyncmeta_drop_bc(mc, bc);
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       asyncmeta_sender_error(op, rs, cb);
-                       asyncmeta_clear_bm_context(bc);
+                              "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+                       asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
                        goto finish;
                default:
                        assert( 0 );
                        break;
                }
+
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+       mc->mc_conns[candidate].msc_active--;
        asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+       bc->bc_active--;
+       asyncmeta_memctx_toggle(thrctx);
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+       rs->sr_err = SLAPD_ASYNCOP;
 finish:
        return rs->sr_err;
 }
index 3013b6a84308e6a796b5da4081e0341ea45a09bd..0dbe3f0c74f9918b92acc4955531f9303112567e 100644 (file)
@@ -43,7 +43,6 @@ extern BI_op_modify           asyncmeta_back_modify;
 extern BI_op_modrdn            asyncmeta_back_modrdn;
 extern BI_op_add               asyncmeta_back_add;
 extern BI_op_delete            asyncmeta_back_delete;
-extern BI_op_abandon           asyncmeta_back_abandon;
 
 extern BI_connection_destroy   asyncmeta_back_conn_destroy;
 
index 97ba375344b4828d1feed44d955ff7b00e6173d0..9423f36ae0f3808e5946071e011fb47ce3648ac1 100644 (file)
 #include <ac/socket.h>
 #include <ac/string.h>
 #include <ac/time.h>
-
-#include "lutil.h"
 #include "slap.h"
-#include "../back-ldap/back-ldap.h"
-#include "back-asyncmeta.h"
 #include "../../../libraries/liblber/lber-int.h"
-
 #include "../../../libraries/libldap/ldap-int.h"
-#undef ldap_debug
-#define        ldap_debug      slap_debug
+#include "lutil.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
 
 static void
 asyncmeta_handle_onerr_stop(Operation *op,
                            SlapReply *rs,
                            a_metaconn_t *mc,
                            bm_context_t *bc,
-                           int candidate,
-                           slap_callback *cb)
+                           int candidate)
 {
        a_metainfo_t *mi = mc->mc_info;
        int j;
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-       if (bc->bc_active > 0) {
+       if (asyncmeta_bc_in_queue(mc,bc) == NULL || bc->bc_active > 1) {
+               bc->bc_active--;
                ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
                return;
        }
-       bc->bc_active = 1;
        asyncmeta_drop_bc(mc, bc);
-       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-
        for (j=0; j<mi->mi_ntargets; j++) {
                if (j != candidate && bc->candidates[j].sr_msgid >= 0
-                   && mc->mc_conns[j].msc_ld != NULL) {
-                       asyncmeta_back_abandon_candidate( mc, op,
+                   && mc->mc_conns[j].msc_ld != NULL && !META_BACK_CONN_CREATING( &mc->mc_conns[j] )) {
+                       asyncmeta_back_cancel( mc, op,
                                                bc->candidates[ j ].sr_msgid, j );
                }
        }
-       if (cb != NULL) {
-               op->o_callback = cb;
-       }
+       slap_sl_mem_setctx(op->o_threadctx, op->o_tmpmemctx);
+       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
        send_ldap_result(op, rs);
-       asyncmeta_clear_bm_context(bc);
 }
 
+static int
+asyncmeta_int_filter2bv( a_dncookie            *dc,
+                        Filter                 *f,
+                        struct berval  *fstr )
+{
+       int             i;
+       Filter          *p;
+       struct berval   atmp,
+                       vtmp,
+                       ntmp,
+                       *tmp;
+       static struct berval
+                       /* better than nothing... */
+                       ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
+                       ber_bvtf_false = BER_BVC( "(|)" ),
+                       /* better than nothing... */
+                       ber_bvtrue = BER_BVC( "(objectClass=*)" ),
+                       ber_bvtf_true = BER_BVC( "(&)" ),
+                       ber_bverror = BER_BVC( "(?=error)" ),
+                       ber_bvunknown = BER_BVC( "(?=unknown)" ),
+                       ber_bvnone = BER_BVC( "(?=none)" );
+       ber_len_t       len;
+       void *memctx = dc->memctx;
+
+       assert( fstr != NULL );
+       BER_BVZERO( fstr );
+
+       if ( f == NULL ) {
+               ber_dupbv_x( fstr, &ber_bvnone, memctx );
+               return LDAP_OTHER;
+       }
+
+       switch ( ( f->f_choice & SLAPD_FILTER_MASK ) ) {
+       case LDAP_FILTER_EQUALITY:
+               if ( f->f_av_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
+                       asyncmeta_dn_massage( dc, &f->f_av_value, &vtmp );
+               } else {
+                       vtmp = f->f_av_value;
+               }
+
+               filter_escape_value_x( &vtmp, &ntmp, memctx );
+               fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+                       + ( sizeof("(=)") - 1 );
+               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
+                       f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+               ber_memfree_x( ntmp.bv_val, memctx );
+               break;
+
+       case LDAP_FILTER_GE:
+               filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+               fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+                       + ( sizeof("(>=)") - 1 );
+               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
+                       f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+               ber_memfree_x( ntmp.bv_val, memctx );
+               break;
+
+       case LDAP_FILTER_LE:
+               filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+               fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+                       + ( sizeof("(<=)") - 1 );
+               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
+                       f->f_av_desc->ad_cname.bv_val,  ntmp.bv_len ? ntmp.bv_val : "" );
+
+               ber_memfree_x( ntmp.bv_val, memctx );
+               break;
+
+       case LDAP_FILTER_APPROX:
+               filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+               fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+                       + ( sizeof("(~=)") - 1 );
+               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
+                       f->f_av_desc->ad_cname.bv_val,  ntmp.bv_len ? ntmp.bv_val : "" );
+
+               ber_memfree_x( ntmp.bv_val, memctx );
+               break;
+
+       case LDAP_FILTER_SUBSTRINGS:
+               fstr->bv_len = f->f_sub_desc->ad_cname.bv_len + ( STRLENOF( "(=*)" ) );
+               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 128, memctx ); /* FIXME: why 128 ? */
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+                       f->f_sub_desc->ad_cname.bv_val );
+
+               if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
+                       len = fstr->bv_len;
+
+                       filter_escape_value_x( &f->f_sub_initial, &ntmp, memctx );
+
+                       fstr->bv_len += ntmp.bv_len;
+                       fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+                       snprintf( &fstr->bv_val[len - 2], ntmp.bv_len + 3,
+                               /* "(attr=" */ "%s*)",
+                               ntmp.bv_len ? ntmp.bv_val : "" );
+
+                       ber_memfree_x( ntmp.bv_val, memctx );
+               }
+
+               if ( f->f_sub_any != NULL ) {
+                       for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
+                               len = fstr->bv_len;
+                               filter_escape_value_x( &f->f_sub_any[i], &ntmp, memctx );
+
+                               fstr->bv_len += ntmp.bv_len + 1;
+                               fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+                               snprintf( &fstr->bv_val[len - 1], ntmp.bv_len + 3,
+                                       /* "(attr=[init]*[any*]" */ "%s*)",
+                                       ntmp.bv_len ? ntmp.bv_val : "" );
+                               ber_memfree_x( ntmp.bv_val, memctx );
+                       }
+               }
+
+               if ( !BER_BVISNULL( &f->f_sub_final ) ) {
+                       len = fstr->bv_len;
+
+                       filter_escape_value_x( &f->f_sub_final, &ntmp, memctx );
+
+                       fstr->bv_len += ntmp.bv_len;
+                       fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+                       snprintf( &fstr->bv_val[len - 1], ntmp.bv_len + 3,
+                               /* "(attr=[init*][any*]" */ "%s)",
+                               ntmp.bv_len ? ntmp.bv_val : "" );
+
+                       ber_memfree_x( ntmp.bv_val, memctx );
+               }
+
+               break;
+
+       case LDAP_FILTER_PRESENT:
+               fstr->bv_len = f->f_desc->ad_cname.bv_len + ( STRLENOF( "(=*)" ) );
+               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+                       f->f_desc->ad_cname.bv_val );
+               break;
+
+       case LDAP_FILTER_AND:
+       case LDAP_FILTER_OR:
+       case LDAP_FILTER_NOT:
+               fstr->bv_len = STRLENOF( "(%)" );
+               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 128, memctx );    /* FIXME: why 128? */
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
+                       f->f_choice == LDAP_FILTER_AND ? '&' :
+                       f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+
+               for ( p = f->f_list; p != NULL; p = p->f_next ) {
+                       int     rc;
+
+                       len = fstr->bv_len;
+
+                       rc = asyncmeta_int_filter2bv( dc, p, &vtmp );
+                       if ( rc != LDAP_SUCCESS ) {
+                               return rc;
+                       }
+
+                       fstr->bv_len += vtmp.bv_len;
+                       fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+                       snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
+                               /*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
+
+                       ber_memfree_x( vtmp.bv_val, memctx );
+               }
+
+               break;
+
+       case LDAP_FILTER_EXT:
+               if ( f->f_mr_desc ) {
+                       atmp = f->f_mr_desc->ad_cname;
+
+               } else {
+                       BER_BVSTR( &atmp, "" );
+               }
+               filter_escape_value_x( &f->f_mr_value, &ntmp, memctx );
+
+               /* FIXME: cleanup (less ?: operators...) */
+               fstr->bv_len = atmp.bv_len +
+                       ( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
+                       ( !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
+                       ntmp.bv_len + ( STRLENOF( "(:=)" ) );
+               fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+
+               snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
+                       atmp.bv_val,
+                       f->f_mr_dnattrs ? ":dn" : "",
+                       !BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
+                       !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
+                       ntmp.bv_len ? ntmp.bv_val : "" );
+               ber_memfree_x( ntmp.bv_val, memctx );
+               break;
+
+       case SLAPD_FILTER_COMPUTED:
+               switch ( f->f_result ) {
+               /* FIXME: treat UNDEFINED as FALSE */
+               case SLAPD_COMPARE_UNDEFINED:
+                       if ( META_BACK_TGT_NOUNDEFFILTER( dc->target ) ) {
+                               return LDAP_COMPARE_FALSE;
+                       }
+                       /* fallthru */
+
+               case LDAP_COMPARE_FALSE:
+                       if ( META_BACK_TGT_T_F( dc->target ) ) {
+                               tmp = &ber_bvtf_false;
+                               break;
+                       }
+                       tmp = &ber_bvfalse;
+                       break;
+
+               case LDAP_COMPARE_TRUE:
+                       if ( META_BACK_TGT_T_F( dc->target ) ) {
+                               tmp = &ber_bvtf_true;
+                               break;
+                       }
+
+                       tmp = &ber_bvtrue;
+                       break;
+
+               default:
+                       tmp = &ber_bverror;
+                       break;
+               }
+
+               ber_dupbv_x( fstr, tmp, memctx );
+               break;
+
+       default:
+               ber_dupbv_x( fstr, &ber_bvunknown, memctx );
+               break;
+       }
+
+       return 0;
+}
 meta_search_candidate_t
 asyncmeta_back_search_start(
                                Operation *op,
                                SlapReply *rs,
-                           a_metaconn_t *mc,
-                           bm_context_t *bc,
-                           int candidate,
-                           struct berval               *prcookie,
-                           ber_int_t           prsize )
+                               a_metaconn_t *mc,
+                               bm_context_t *bc,
+                               int candidate,
+                               struct berval           *prcookie,
+                               ber_int_t               prsize,
+                               int do_lock)
 {
        SlapReply               *candidates = bc->candidates;
        a_metainfo_t            *mi = ( a_metainfo_t * )mc->mc_info;
@@ -87,16 +325,15 @@ asyncmeta_back_search_start(
        a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
        a_dncookie              dc;
        struct berval           realbase = op->o_req_dn;
+       char            **attrs;
        int                     realscope = op->ors_scope;
        struct berval           mbase = BER_BVNULL;
-       struct berval           mfilter = BER_BVNULL;
-       char                    **mapped_attrs = NULL;
        int                     rc;
+       struct berval   filterbv = BER_BVNULL;
        meta_search_candidate_t retcode;
        int timelimit;
-       int                     nretries = 1;
        LDAPControl             **ctrls = NULL;
-       BerElement *ber;
+       BerElement *ber = NULL;
        ber_int_t       msgid;
 #ifdef SLAPD_META_CLIENT_PR
        LDAPControl             **save_ctrls = NULL;
@@ -116,7 +353,8 @@ asyncmeta_back_search_start(
                return META_SEARCH_NOT_CANDIDATE;
        }
 
-       Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_back_search_start[%d]\n", op->o_log_prefix, candidate );
+       Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_back_search_start: dn=%s filter=%s\n",
+              op->o_log_prefix, op->o_req_dn.bv_val, op->ors_filterstr.bv_val );
        /*
         * modifies the base according to the scope, if required
         */
@@ -199,59 +437,13 @@ asyncmeta_back_search_start(
        /*
         * Rewrite the search base, if required
         */
+       dc.op = op;
        dc.target = mt;
-       dc.ctx = "searchBase";
-       dc.conn = op->o_conn;
-       dc.rs = rs;
-       switch ( asyncmeta_dn_massage( &dc, &realbase, &mbase ) ) {
-       case LDAP_SUCCESS:
-               break;
-
-       case LDAP_UNWILLING_TO_PERFORM:
-               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-               rs->sr_text = "Operation not allowed";
-               retcode = META_SEARCH_ERR;
-               goto doreturn;
+       dc.memctx = op->o_tmpmemctx;
+       dc.to_from = MASSAGE_REQ;
+       asyncmeta_dn_massage( &dc, &realbase, &mbase );
 
-       default:
-
-               /*
-                * this target is no longer candidate
-                */
-               retcode = META_SEARCH_NOT_CANDIDATE;
-               goto doreturn;
-       }
-
-       /*
-        * Maps filter
-        */
-       rc = asyncmeta_filter_map_rewrite( &dc, op->ors_filter,
-                       &mfilter, BACKLDAP_MAP, NULL );
-       switch ( rc ) {
-       case LDAP_SUCCESS:
-               break;
-
-       case LDAP_COMPARE_FALSE:
-       default:
-               /*
-                * this target is no longer candidate
-                */
-               retcode = META_SEARCH_NOT_CANDIDATE;
-               goto done;
-       }
-
-       /*
-        * Maps required attributes
-        */
-       rc = asyncmeta_map_attrs( op, &mt->mt_rwmap.rwm_at,
-                       op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
-       if ( rc != LDAP_SUCCESS ) {
-               /*
-                * this target is no longer candidate
-                */
-               retcode = META_SEARCH_NOT_CANDIDATE;
-               goto done;
-       }
+       attrs = anlist2charray_x( op->ors_attrs, 0, op->o_tmpmemctx );
 
        if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
                timelimit = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
@@ -338,21 +530,10 @@ done_pr:;
        }
 #endif /* SLAPD_META_CLIENT_PR */
 
-retry:;
        asyncmeta_set_msc_time(msc);
        ctrls = op->o_ctrls;
-       if (nretries == 0)
-       {
-               if (rc != LDAP_SUCCESS)
-               {
-                       rs->sr_err = LDAP_BUSY;
-                       retcode = META_SEARCH_ERR;
-                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
-                       goto done;
-               }
-       }
 
-       if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls )
+       if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls )
                != LDAP_SUCCESS )
        {
                candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
@@ -363,48 +544,114 @@ retry:;
        /*
         * Starts the search
         */
+               /* someone reset the connection */
+       if (!( LDAP_BACK_CONN_ISBOUND( msc )
+              || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+               Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+               goto error_unavailable;
+       }
+       rc = asyncmeta_int_filter2bv( &dc, op->ors_filter, &filterbv );
+       if ( rc ) {
+               retcode = META_SEARCH_ERR;
+               goto done;
+       }
+
        ber = ldap_build_search_req( msc->msc_ld,
-                       mbase.bv_val, realscope, mfilter.bv_val,
-                       mapped_attrs, op->ors_attrsonly,
+                       mbase.bv_val, realscope, filterbv.bv_val,
+                       attrs, op->ors_attrsonly,
                        ctrls, NULL, timelimit, op->ors_slimit, op->ors_deref,
                        &msgid );
+       if (!ber) {
+               Debug( asyncmeta_debug, "%s asyncmeta_back_search_start: Operation encoding failed with errno %d\n",
+                      op->o_log_prefix, msc->msc_ld->ld_errno );
+               rs->sr_err = LDAP_OPERATIONS_ERROR;
+               rs->sr_text = "Failed to encode proxied request";
+               retcode = META_SEARCH_ERR;
+               goto done;
+       }
+
        if (ber) {
-               candidates[ candidate ].sr_msgid = msgid;
-               rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_SEARCH,
-                       mbase.bv_val, ber, msgid );
-               if (rc == msgid)
-                       rc = LDAP_SUCCESS;
-               else
-                       rc = LDAP_SERVER_DOWN;
+               struct timeval tv = {0, mt->mt_network_timeout*1000};
+               ber_socket_t s;
+
+               if (!( LDAP_BACK_CONN_ISBOUND( msc )
+                      || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+               if (s < 0) {
+                       Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+               }
+
+               rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+               if (rc < 0) {
+                       Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+                       if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+                               rc = LDAP_SERVER_DOWN;
+                       } else {
+                               goto error_unavailable;
+                       }
+               } else {
+                       candidates[ candidate ].sr_msgid = msgid;
+                       rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_SEARCH,
+                                                       mbase.bv_val, ber, msgid );
+                       if (rc == msgid)
+                               rc = LDAP_SUCCESS;
+                       else
+                               rc = LDAP_SERVER_DOWN;
+                       ber = NULL;
+               }
+
                switch ( rc ) {
                case LDAP_SUCCESS:
                        retcode = META_SEARCH_CANDIDATE;
                        asyncmeta_set_msc_time(msc);
-                       break;
+                       goto done;
 
                case LDAP_SERVER_DOWN:
-                       ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
-                       if (mc->mc_active < 1) {
-                               asyncmeta_clear_one_msc(NULL, mc, candidate);
+                       /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+                       if (do_lock > 0) {
+                               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+                               asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+                               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
                        }
-                       ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-                       if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) {
-                               nretries = 0;
-                               /* if the identity changed, there might be need to re-authz */
-                               (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
-                               goto retry;
-                       }
-                       rs->sr_err = LDAP_UNAVAILABLE;
-                       retcode = META_SEARCH_ERR;
-                       break;
+                       Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+                       goto error_unavailable;
+
                default:
                        candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
                        retcode = META_SEARCH_NOT_CANDIDATE;
+                       goto done;
                }
        }
 
+error_unavailable:
+       if (ber)
+               ber_free(ber, 1);
+       switch (bc->nretries[candidate]) {
+       case -1: /* nretries = forever */
+               retcode = META_SEARCH_NEED_BIND;
+               ldap_pvt_thread_yield();
+               break;
+       case 0: /* no retries left */
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               rs->sr_err = LDAP_UNAVAILABLE;
+               rs->sr_text = "Unable to send search request to target";
+               retcode = META_SEARCH_ERR;
+               break;
+       default: /* more retries left - try to rebind and go again */
+               retcode = META_SEARCH_NEED_BIND;
+               bc->nretries[candidate]--;
+               ldap_pvt_thread_yield();
+               break;
+       }
 done:;
+#if 0
        (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+#endif
 #ifdef SLAPD_META_CLIENT_PR
        if ( save_ctrls != op->o_ctrls ) {
                op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
@@ -412,14 +659,8 @@ done:;
        }
 #endif /* SLAPD_META_CLIENT_PR */
 
-       if ( mapped_attrs ) {
-               ber_memfree_x( mapped_attrs, op->o_tmpmemctx );
-       }
-       if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
-               ber_memfree_x( mfilter.bv_val, NULL );
-       }
        if ( mbase.bv_val != realbase.bv_val ) {
-               free( mbase.bv_val );
+               op->o_tmpfree( mbase.bv_val, op->o_tmpmemctx );
        }
 
 doreturn:;
@@ -431,25 +672,16 @@ int
 asyncmeta_back_search( Operation *op, SlapReply *rs )
 {
        a_metainfo_t    *mi = ( a_metainfo_t * )op->o_bd->be_private;
-       struct timeval  save_tv = { 0, 0 },
-                       tv;
-       time_t          stoptime = (time_t)(-1),
-                       lastres_time = slap_get_time(),
-                       timeout = 0;
-       int             rc = 0, sres = LDAP_SUCCESS;
-       char            *matched = NULL;
-       int             last = 0, ncandidates = 0,
-                       initial_candidates = 0, candidate_match = 0,
-                       needbind = 0;
-       ldap_back_send_t        sendok = LDAP_BACK_SENDERR;
-       long            i,j;
-       int             is_ok = 0;
-       void            *savepriv;
+       time_t          timeout = 0;
+       int             rc = 0;
+       int             ncandidates = 0, initial_candidates = 0;
+       long            i;
        SlapReply       *candidates = NULL;
-       int             do_taint = 0;
+       void *thrctx = op->o_threadctx;
        bm_context_t *bc;
        a_metaconn_t *mc;
-       slap_callback *cb = op->o_callback;
+       int msc_decr = 0;
+       int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
 
        rs_assert_ready( rs );
        rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
@@ -461,7 +693,7 @@ asyncmeta_back_search( Operation *op, SlapReply *rs )
         * to map attrs and maybe rewrite value
         */
 
-       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
+       asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
        if (bc == NULL) {
                rs->sr_err = LDAP_OTHER;
                send_ldap_result(op, rs);
@@ -471,9 +703,7 @@ asyncmeta_back_search( Operation *op, SlapReply *rs )
        candidates = bc->candidates;
        mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_DONTSEND, 0);
        if ( !mc || rs->sr_err != LDAP_SUCCESS) {
-               op->o_callback = cb;
                send_ldap_result(op, rs);
-               asyncmeta_clear_bm_context(bc);
                return rs->sr_err;
        }
 
@@ -511,27 +741,33 @@ asyncmeta_back_search( Operation *op, SlapReply *rs )
                }
        }
 
-       bc->timeout = timeout;
+       if ( op->ors_tlimit != SLAP_NO_LIMIT && (timeout == 0 || op->ors_tlimit < timeout)) {
+               bc->searchtime = 1;
+               bc->timeout = op->ors_tlimit;
+       } else {
+               bc->timeout = timeout;
+       }
+
        bc->stoptime = op->o_time + bc->timeout;
+       bc->bc_active = 1;
 
-       if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
-               stoptime = op->o_time + op->ors_tlimit;
-               if (stoptime < bc->stoptime) {
-                       bc->stoptime = stoptime;
-                       bc->searchtime = 1;
-                       bc->timeout = op->ors_tlimit;
-               }
+       if (mc->pending_ops >= max_pending_ops) {
+               rs->sr_err = LDAP_BUSY;
+               rs->sr_text = "Maximum pending ops limit exceeded";
+               send_ldap_result(op, rs);
+               return rs->sr_err;
        }
 
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
        rc = asyncmeta_add_message_queue(mc, bc);
+       for ( i = 0; i < mi->mi_ntargets; i++ ) {
+               mc->mc_conns[i].msc_active++;
+       }
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
 
        if (rc != LDAP_SUCCESS) {
                rs->sr_err = LDAP_BUSY;
                rs->sr_text = "Maximum pending ops limit exceeded";
-               asyncmeta_clear_bm_context(bc);
-               op->o_callback = cb;
                send_ldap_result(op, rs);
                goto finish;
        }
@@ -542,6 +778,30 @@ asyncmeta_back_search( Operation *op, SlapReply *rs )
                {
                        continue;
                }
+retry:
+               if (bc->timeout && bc->stoptime < slap_get_time() && META_BACK_ONERR_STOP( mi )) {
+                       int             timeout_err;
+                       const char *timeout_text;
+                       if (bc->searchtime) {
+                               timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+                               timeout_text = NULL;
+                       } else {
+                               timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+                                       LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+                               timeout_text = "Operation timed out before it was sent to target";
+                       }
+                       rs->sr_err = timeout_err;
+                       rs->sr_text = timeout_text;
+                       asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+                       goto finish;
+
+               }
+
+               if (op->o_abandon) {
+                       rs->sr_err = SLAPD_ABANDON;
+                       asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+                       goto finish;
+               }
 
                rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, i);
                switch (rc)
@@ -552,17 +812,19 @@ asyncmeta_back_search( Operation *op, SlapReply *rs )
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: IS_CANDIDATE "
                               "cnd=\"%ld\"\n", op->o_log_prefix, i );
 
-                       rc = asyncmeta_back_search_start( op, rs, mc, bc, i,  NULL, 0 );
+                       rc = asyncmeta_back_search_start( op, rs, mc, bc, i,  NULL, 0 , 1);
                        if (rc == META_SEARCH_ERR) {
                                META_CANDIDATE_CLEAR(&candidates[i]);
                                candidates[ i ].sr_msgid = META_MSGID_IGNORE;
                                if ( META_BACK_ONERR_STOP( mi ) ) {
-                                       asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
+                                       asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
                                        goto finish;
                                }
                                else {
                                        continue;
                                }
+                       } else if (rc == META_SEARCH_NEED_BIND) {
+                               goto retry;
                        }
                        break;
                case META_SEARCH_NOT_CANDIDATE:
@@ -572,22 +834,6 @@ asyncmeta_back_search( Operation *op, SlapReply *rs )
                        break;
 
                case META_SEARCH_NEED_BIND:
-               case META_SEARCH_CONNECTING:
-                       Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NEED_BIND "
-                              "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]);
-                       ncandidates++;
-                       rc = asyncmeta_dobind_init(op, rs, bc, mc, i);
-                       if (rc == META_SEARCH_ERR) {
-                               candidates[ i ].sr_msgid = META_MSGID_IGNORE;
-                               if ( META_BACK_ONERR_STOP( mi ) ) {
-                                       asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
-                                       goto finish;
-                               }
-                               else {
-                                       continue;
-                               }
-                       }
-                       break;
                case META_SEARCH_BINDING:
                        Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: BINDING "
                               "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]);
@@ -604,7 +850,7 @@ asyncmeta_back_search( Operation *op, SlapReply *rs )
                        candidates[ i ].sr_type = REP_RESULT;
 
                        if ( META_BACK_ONERR_STOP( mi ) ) {
-                               asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
+                               asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
                                goto finish;
                        }
                        else {
@@ -666,14 +912,29 @@ asyncmeta_back_search( Operation *op, SlapReply *rs )
                ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
                asyncmeta_drop_bc(mc, bc);
                ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
-               op->o_callback = cb;
                send_ldap_result(op, rs);
-               asyncmeta_clear_bm_context(bc);
                goto finish;
        }
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+       for ( i = 0; i < mi->mi_ntargets; i++ ) {
+               mc->mc_conns[i].msc_active--;
+       }
+       msc_decr = 1;
+
        asyncmeta_start_listeners(mc, candidates, bc);
+       bc->bc_active--;
+       asyncmeta_memctx_toggle(thrctx);
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+       rs->sr_err = SLAPD_ASYNCOP;
+
 finish:
+       /* we ended up straight here due to error and need to reset the msc_active*/
+       if (msc_decr == 0) {
+               ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+               for ( i = 0; i < mi->mi_ntargets; i++ ) {
+                       mc->mc_conns[i].msc_active--;
+               }
+               ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+       }
        return rs->sr_err;
 }
diff --git a/servers/slapd/back-asyncmeta/suffixmassage.c b/servers/slapd/back-asyncmeta/suffixmassage.c
deleted file mode 100644 (file)
index 36889c0..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/* suffixmassage.c - massages ldap backend dns */
-/* $OpenLDAP$ */
-/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 2016-2019 The OpenLDAP Foundation.
- * Portions Copyright 2016 Symas Corporation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted only as authorized by the OpenLDAP
- * Public License.
- *
- * A copy of this license is available in the file LICENSE in the
- * top-level directory of the distribution or, alternatively, at
- * <http://www.OpenLDAP.org/license.html>.
- */
-
-/* ACKNOWLEDGEMENTS:
- * This work was developed by Symas Corporation
- * based on back-meta module for inclusion in OpenLDAP Software.
- * This work was sponsored by Ericsson. */
-
-/* This is an altered version */
-
-/*
- * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
- * Copyright 2000, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
- *
- * Module back-ldap, originally developed by Howard Chu
- *
- * has been modified by Pierangelo Masarati. The original copyright
- * notice has been maintained.
- *
- * Permission is granted to anyone to use this software for any purpose
- * on any computer system, and to alter it and redistribute it, subject
- * to the following restrictions:
- *
- * 1. The author is not responsible for the consequences of use of this
- *    software, no matter how awful, even if they arise from flaws in it.
- *
- * 2. The origin of this software must not be misrepresented, either by
- *    explicit claim or by omission.  Since few users ever read sources,
- *    credits should appear in the documentation.
- *
- * 3. Altered versions must be plainly marked as such, and must not be
- *    misrepresented as being the original software.  Since few users
- *    ever read sources, credits should appear in the documentation.
- *
- * 4. This notice may not be removed or altered.
- */
-
-#include "portable.h"
-
-#include <stdio.h>
-
-#include <ac/string.h>
-#include <ac/socket.h>
-
-#include "slap.h"
-#include "../back-ldap/back-ldap.h"
-#include "back-asyncmeta.h"
-
-int
-asyncmeta_dn_massage(
-       a_dncookie      *dc,
-       struct berval   *dn,
-       struct berval   *res )
-{
-       int             rc = 0;
-       static char     *dmy = "";
-
-       switch ( rewrite_session( dc->target->mt_rwmap.rwm_rw, dc->ctx,
-                               ( dn->bv_val ? dn->bv_val : dmy ),
-                               dc->conn, &res->bv_val ) )
-       {
-       case REWRITE_REGEXEC_OK:
-               if ( res->bv_val != NULL ) {
-                       res->bv_len = strlen( res->bv_val );
-               } else {
-                       *res = *dn;
-               }
-               Debug( LDAP_DEBUG_ARGS,
-                       "[rw] %s: \"%s\" -> \"%s\"\n",
-                       dc->ctx,
-                       BER_BVISNULL( dn ) ? "" : dn->bv_val,
-                       BER_BVISNULL( res ) ? "" : res->bv_val );
-               rc = LDAP_SUCCESS;
-               break;
-
-       case REWRITE_REGEXEC_UNWILLING:
-               if ( dc->rs ) {
-                       dc->rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-                       dc->rs->sr_text = "Operation not allowed";
-               }
-               rc = LDAP_UNWILLING_TO_PERFORM;
-               break;
-
-       case REWRITE_REGEXEC_ERR:
-               if ( dc->rs ) {
-                       dc->rs->sr_err = LDAP_OTHER;
-                       dc->rs->sr_text = "Rewrite error";
-               }
-               rc = LDAP_OTHER;
-               break;
-       }
-
-       if ( res->bv_val == dmy ) {
-               BER_BVZERO( res );
-       }
-
-       return rc;
-}
diff --git a/servers/slapd/back-asyncmeta/unbind.c b/servers/slapd/back-asyncmeta/unbind.c
deleted file mode 100644 (file)
index 6ad3b1a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* unbind.c - unbind handler for back-asyncmeta */
-/* $OpenLDAP$ */
-/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 2016-2019 The OpenLDAP Foundation.
- * Portions Copyright 2016 Symas Corporation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted only as authorized by the OpenLDAP
- * Public License.
- *
- * A copy of this license is available in the file LICENSE in the
- * top-level directory of the distribution or, alternatively, at
- * <http://www.OpenLDAP.org/license.html>.
- */
-
-/* ACKNOWLEDGEMENTS:
- * This work was developed by Symas Corporation
- * based on back-meta module for inclusion in OpenLDAP Software.
- * This work was sponsored by Ericsson. */
-
-#include "portable.h"
-
-#include <stdio.h>
-
-#include <ac/errno.h>
-#include <ac/socket.h>
-#include <ac/string.h>
-
-#include "slap.h"
-#include "../back-ldap/back-ldap.h"
-#include "back-asyncmeta.h"
-
-int
-asyncmeta_back_conn_destroy(
-       Backend         *be,
-       Connection      *conn )
-{
-       a_metainfo_t    *mi = ( a_metainfo_t * )be->be_private;
-       int             i;
-
-       Debug( LDAP_DEBUG_TRACE,
-               "=>asyncmeta_back_conn_destroy: fetching conn=%ld DN=\"%s\"\n",
-               conn->c_connid,
-               BER_BVISNULL( &conn->c_ndn ) ? "" : conn->c_ndn.bv_val );
-       /*
-        * Cleanup rewrite session
-        */
-       for ( i = 0; i < mi->mi_ntargets; ++i ) {
-               rewrite_session_delete( mi->mi_targets[ i ]->mt_rwmap.rwm_rw, conn );
-       }
-
-       return 0;
-}
diff --git a/tests/data/slapd-asyncmeta.conf b/tests/data/slapd-asyncmeta.conf
new file mode 100644 (file)
index 0000000..8af4932
--- /dev/null
@@ -0,0 +1,88 @@
+# master slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2019 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include                @SCHEMADIR@/core.schema
+include                @SCHEMADIR@/cosine.schema
+include                @SCHEMADIR@/inetorgperson.schema
+include                @SCHEMADIR@/openldap.schema
+include                @SCHEMADIR@/nis.schema
+include                @SCHEMADIR@/ppolicy.schema
+pidfile                @TESTDIR@/slapd.m.pid
+argsfile       @TESTDIR@/slapd.m.args
+
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#metamod#modulepath ../servers/slapd/back-meta/
+#metamod#moduleload back_meta.la
+#monitormod#modulepath ../servers/slapd/back-monitor/
+#monitormod#moduleload back_monitor.la
+
+# seems to improve behavior under very heavy load
+# (i.e. it alleviates load on target systems)
+threads                8
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database       asyncmeta
+suffix         "o=Example,c=US"
+rootdn         "cn=Manager,o=Example,c=US"
+rootpw         secret
+chase-referrals        no
+#nretries      forever
+nretries       100
+#norefs                true
+network-timeout 500
+#max-timeout-ops 50
+#max-pending-ops 128
+#max-target-conns 16
+
+# local
+uri            "@URI2@ou=Meta,o=Example,c=US"
+subtree-exclude "ou=Excluded,ou=Meta,o=Example,c=US"
+suffixmassage  "ou=Meta,o=Example,c=US" "ou=Meta,dc=example,dc=com"
+###pseudorootdn        "cn=manager,ou=meta,dc=example,dc=com"
+###pseudorootpw        secret
+idassert-bind  bindmethod=simple
+               binddn="cn=manager,ou=meta,dc=example,dc=com"
+               credentials="secret"
+               mode=self
+               flags=non-prescriptive
+idassert-authzFrom     "dn.exact:cn=Manager,o=Local"
+
+# remote
+uri            "@URI1@o=Example,c=US"
+subtree-include "dn.subtree:o=Example,c=US"
+suffixmassage  "o=Example,c=US" "dc=example,dc=com"
+###pseudorootdn        "cn=manager,dc=example,dc=com"
+###pseudorootpw        secret
+idassert-bind  bindmethod=simple
+               binddn="cn=manager,dc=example,dc=com"
+               credentials="secret"
+               mode=self
+               flags=non-prescriptive
+idassert-authzFrom     "dn.exact:cn=Manager,o=Local"
+
+limits         dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=US" time=1 size=8
+
+# This is only for binding as the rootdn
+database       asyncmeta
+suffix         "o=Local"
+rootdn         "cn=Manager,o=Local"
+rootpw         secret
+uri            "@URI6@o=Local"
+
+#monitor#database      monitor
index 0c2667af3e1587ea6d5cb5d7239d523988edf0d2..9d81b1d846ea15818b5b94c55f3bc77a00cf02ad 100644 (file)
@@ -33,6 +33,7 @@ AC_null=@BUILD_NULL@
 # other backends
 AC_ldap=ldap@BUILD_LDAP@
 AC_meta=meta@BUILD_META@
+AC_asyncmeta=asyncmeta@BUILD_ASYNCMETA@
 AC_monitor=@BUILD_MONITOR@
 AC_relay=relay@BUILD_RELAY@
 AC_sql=sql@BUILD_SQL@
@@ -70,8 +71,10 @@ fi
 if test "${AC_meta}" = "metamod" && test "${AC_LIBS_DYNAMIC}" = "static" ; then
        AC_meta="metano"
 fi
-
-export AC_bdb AC_hdb AC_ldap AC_mdb AC_meta AC_monitor AC_null AC_relay AC_sql \
+if test "${AC_asyncmeta}" = "metamod" && test "${AC_LIBS_DYNAMIC}" = "static" ; then
+       AC_meta="asyncmetano"
+fi
+export AC_bdb AC_hdb AC_ldap AC_mdb AC_meta AC_asyncmeta AC_monitor AC_null AC_relay AC_sql \
        AC_accesslog AC_autoca AC_constraint AC_dds AC_dynlist AC_memberof AC_pcache AC_ppolicy \
        AC_refint AC_retcode AC_rwm AC_unique AC_syncprov AC_translucent \
        AC_valsort \
index 6a8b103dbf53bd6ac1075c33b4deccc6315f3169..a52dc285dfee7d8543aec466a597e5acd35e2968 100755 (executable)
@@ -21,6 +21,7 @@ TESTWD=`pwd`
 MONITORDB=${AC_monitor-no}
 BACKLDAP=${AC_ldap-ldapno}
 BACKMETA=${AC_meta-metano}
+BACKASYNCMETA=${AC_asyncmeta-asyncmetano}
 BACKRELAY=${AC_relay-relayno}
 BACKSQL=${AC_sql-sqlno}
        RDBMS=${SLAPD_USE_SQL-rdbmsno}
@@ -134,6 +135,7 @@ TRANSLUCENTREMOTECONF=$DATADIR/slapd-translucent-remote.conf
 METACONF=$DATADIR/slapd-meta.conf
 METACONF1=$DATADIR/slapd-meta-target1.conf
 METACONF2=$DATADIR/slapd-meta-target2.conf
+ASYNCMETACONF=$DATADIR/slapd-asyncmeta.conf
 GLUELDAPCONF=$DATADIR/slapd-glue-ldap.conf
 ACICONF=$DATADIR/slapd-aci.conf
 VALSORTCONF=$DATADIR/slapd-valsort.conf
diff --git a/tests/scripts/test073-asyncmeta b/tests/scripts/test073-asyncmeta
new file mode 100755 (executable)
index 0000000..e24a7d2
--- /dev/null
@@ -0,0 +1,620 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2019 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKASYNCMETA = asyncmetano ; then
+       echo "asyncmeta backend not available, test skipped"
+       exit 0
+fi
+
+if test $BACKLDAP = ldapno ; then
+       echo "ldap backend not available, test skipped"
+       exit 0
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND $MONITORDB < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL $TIMING > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "$MONITOR" -h $LOCALHOST -p $PORT1 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -h $LOCALHOST -p $PORT1 -w $PASSWD < \
+       $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND $MONITORDB < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL $TIMING > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "$MONITOR" -h $LOCALHOST -p $PORT2 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -h $LOCALHOST -p $PORT2 -w $PASSWD < \
+       $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND $MONITORDB < $ASYNCMETACONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL $TIMING > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "$MONITOR" -h $LOCALHOST -p $PORT3 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+BASEDN="o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Search failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+# ITS#4195: spurious matchedDN when the search scopes the main target,
+# and the searchBase is not present, so that target returns noSuchObject
+BASEDN="ou=Meta,o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Search failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+#
+# Do some modifications
+#
+
+BASEDN="o=Example,c=US"
+echo "Modifying database \"$BASEDN\"..."
+$LDAPMODIFY -v -D "cn=Manager,$BASEDN" -h $LOCALHOST -p $PORT3 -w $PASSWD \
+       -M >> $TESTOUT 2>&1 << EOMODS
+# These operations (updates with objectClass mapping) triggered ITS#3499
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+objectClass: uidObject
+cn: Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+uid: added
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+cn: Another Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+member: cn=Another Added Group,ou=Groups,$BASEDN
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: modify
+add: objectClass
+objectClass: uidObject
+-
+add: uid
+uid: added
+-
+
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: modify
+delete: objectClass
+objectClass: uidObject
+-
+delete: uid
+-
+
+dn: ou=Meta,$BASEDN
+changetype: modify
+add: description
+description: added to "ou=Meta,$BASEDN"
+-
+
+dn: ou=Who's going to handle this?,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Who's going to handle this?
+description: added
+description: will be deleted
+
+dn: ou=Same as above,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: ou=Who's going to handle this?,$BASEDN
+changetype: delete
+
+dn: ou=Who's going to handle this?,ou=Meta,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Who's going to handle this?
+description: added
+description: will be deleted
+
+dn: ou=Same as above,ou=Meta,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: cn=Added User,ou=Same as above,ou=Meta,$BASEDN
+changetype: add
+objectClass: inetOrgPerson
+cn: Added User
+sn: User
+userPassword: secret
+
+dn: ou=Who's going to handle this?,ou=Meta,$BASEDN
+changetype: delete
+EOMODS
+
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Modify failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Modify failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Search failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+BASEDN="o=Example,c=US"
+echo " base=\"$BASEDN\"..."
+echo "#        base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 -b "$BASEDN" -M "$FILTER" '*' ref \
+       >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Search failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+BASEDN="o=Example,c=US"
+FILTER="(seeAlso=cn=all staff,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"seeAlso\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "#        attrs=\"seeAlso\"" >> $SEARCHOUT
+echo "#        base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 -b "$BASEDN" "$FILTER" seeAlso \
+       >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Search failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+FILTER="(uid=example)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"uid\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "#        attrs=\"uid\"" >> $SEARCHOUT
+echo "#        base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 -b "$BASEDN" "$FILTER" uid \
+       >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Search failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+FILTER="(member=cn=Another Added Group,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"member\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "#        attrs=\"member\"" >> $SEARCHOUT
+echo "#        base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 -b "$BASEDN" "$FILTER" member \
+       >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Search failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+echo "Waiting 10 seconds for cached connections to timeout..."
+sleep 10
+
+echo "Searching with a timed out connection..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "#        attrs=\"member\"" >> $SEARCHOUT
+echo "#        base=\"$BASEDN\"" >> $SEARCHOUT
+echo "#        with a timed out connection..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 -D "cn=Manager,$BASEDN" -w $PASSWD \
+       -b "$BASEDN" "$FILTER" member \
+       >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "Search failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit 0
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+# NOTE: cannot send to $SEARCHOUT because the returned entries
+# are not predictable...
+echo "Checking server-enforced size limit..."
+echo "# Checking server-enforced size limit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 \
+       -D "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" -w bjorn \
+       -b "$BASEDN" "(objectClass=*)" 1.1 \
+       >> $TESTOUT 2>&1
+RC=$?
+case $RC,$BACKEND in
+       4,* | 0,null)
+       ;;
+       0,*)
+               echo "Search should have failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit -1
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+# NOTE: cannot send to $SEARCHOUT because the returned entries
+# are not predictable...
+echo "Checking client-requested size limit..."
+echo "# Checking client-requested size limit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -h $LOCALHOST -p $PORT3 \
+       -D "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" -w bjorn \
+       -b "$BASEDN" -z 2 "(objectClass=*)" 1.1 \
+       >> $TESTOUT 2>&1
+RC=$?
+case $RC,$BACKEND in
+       4,* | 0,null)
+       ;;
+       0,*)
+               echo "Search should have failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit -1
+       ;;
+       *)
+               echo "Search failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METAOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+       echo "comparison failed - meta search/modification didn't succeed"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit 1
+fi
+
+echo "Binding as newly added user to database \"$BASEDN\"..."
+$LDAPWHOAMI -h $LOCALHOST -p $PORT3 \
+       -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+       -w $PASSWD >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "WhoAmI failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC in
+       0)
+       ;;
+       51)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+       ;;
+       *)
+               echo "WhoAmI failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit $RC
+       ;;
+esac
+
+
+echo "Binding with incorrect password to database \"$BASEDN\"..."
+$LDAPWHOAMI -h $LOCALHOST -p $PORT3 \
+       -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+       -w bogus >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "WhoAmI failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC,$BACKEND in
+       0,null)
+       ;;
+       0,*)
+               echo "WhoAmI should have failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit -1
+       ;;
+       51,*)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+       ;;
+       *)
+       ;;
+esac
+
+echo "Binding with non-existing user to database \"$BASEDN\"..."
+$LDAPWHOAMI -h $LOCALHOST -p $PORT3 \
+       -D "cn=Non-existing User,ou=Same as above,ou=Meta,$BASEDN" \
+       -w bogus >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+#      echo "WhoAmI failed ($RC)!"
+#      test $KILLSERVERS != no && kill -HUP $KILLPIDS
+#      exit $RC
+#fi
+case $RC,$BACKEND in
+       0,null)
+       ;;
+       0,*)
+               echo "WhoAmI should have failed ($RC)!"
+               test $KILLSERVERS != no && kill -HUP $KILLPIDS
+               exit -1
+       ;;
+       51,*)
+               echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+       ;;
+       *)
+       ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test074-asyncmeta-concurrency b/tests/scripts/test074-asyncmeta-concurrency
new file mode 100755 (executable)
index 0000000..a9274af
--- /dev/null
@@ -0,0 +1,242 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2019 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKASYNCMETA = asyncmetano ; then
+       echo "asyncmeta backend not available, test skipped"
+       exit 0
+fi
+
+if test $BACKLDAP = ldapno ; then
+       echo "ldap backend not available, test skipped"
+       exit 0
+fi
+
+if test x$TESTLOOPS = x ; then
+       TESTLOOPS=50
+fi
+
+if test x$TESTCHILDREN = x ; then
+       TESTCHILDREN=20
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+# NOTE: this could be added to all tests...
+if test "$BACKEND" = "bdb" || test "$BACKEND" = "hdb" ; then
+       if test "x$DB_CONFIG" != "x" ; then \
+               if test -f $DB_CONFIG ; then
+                       echo "==> using DB_CONFIG \"$DB_CONFIG\""
+                       cp $DB_CONFIG $DBDIR1
+                       cp $DB_CONFIG $DBDIR2
+               else
+                       echo "==> DB_CONFIG must point to a valid file (ignored)"
+               fi
+       else
+               echo "==> set \"DB_CONFIG\" to the DB_CONFIG file you want to use for the test."
+       fi
+       echo ""
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND $MONITORDB < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL $TIMING > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "$MONITOR" -h $LOCALHOST -p $PORT1 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -h $LOCALHOST -p $PORT1 -w $PASSWD < \
+       $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND $MONITORDB < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL $TIMING > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "$MONITOR" -h $LOCALHOST -p $PORT2 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -h $LOCALHOST -p $PORT2 -w $PASSWD < \
+       $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND $MONITORDB < $ASYNCMETACONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL $TIMING > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "$MONITOR" -h $LOCALHOST -p $PORT3 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+mkdir -p $TESTDIR/$DATADIR
+METABASEDN="o=Example,c=US"
+for f in $DATADIR/do_* ; do
+       sed -e "s;$BASEDN;$METABASEDN;" $f > $TESTDIR/$f
+done
+
+# add a read that matches only the local database, but selects
+# also the remote as candidate; this should be removed to compare
+# execution times with test008...
+for f in $TESTDIR/$DATADIR/do_read.* ; do
+       echo "ou=Meta,$METABASEDN" >> $f
+done
+
+# add a read that matches a referral in the local database only,
+# but selects also the remote as candidate; this should be removed
+# to compare execution times with test008...
+for f in $TESTDIR/$DATADIR/do_read.* ; do
+       echo "cn=Somewhere,ou=Meta,$METABASEDN" >> $f
+done
+
+# add a bind that resolves to a referral
+for f in $TESTDIR/$DATADIR/do_bind.* ; do
+       echo "cn=Foo,ou=Meta,$METABASEDN" >> $f
+       echo "bar" >> $f
+       echo "" >> $f
+       echo "" >> $f
+done
+
+# fix test data to include back-monitor, if available
+# NOTE: copies do_* files from $TESTDIR/$DATADIR to $TESTDIR
+$MONITORDATA "$MONITORDB" "$TESTDIR/$DATADIR" "$TESTDIR"
+
+BINDDN="cn=Manager,o=Local"
+PASSWD="secret"
+echo "Using tester for concurrent server access..."
+$SLAPDTESTER -P "$PROGDIR" -d "$TESTDIR" -h $LOCALHOST -p $PORT3 \
+       -D "$BINDDN" -w $PASSWD -l $TESTLOOPS -j $TESTCHILDREN \
+       -r 20 -i '!REFERRAL' -i '*INVALID_CREDENTIALS' -SS
+RC=$?
+
+if test $RC != 0 ; then
+       echo "slapd-tester failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$METABASEDN" -h $LOCALHOST -p $PORT3 \
+                       'objectClass=*' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METACONCURRENCYOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+       echo "comparison failed - slapd-asyncmeta search/modification didn't succeed"
+       exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0