]> git.sur5r.net Git - openldap/commitdiff
import selected fixes/improvements to proxy backends from HEAD:
authorPierangelo Masarati <ando@openldap.org>
Fri, 5 Jan 2007 10:47:09 +0000 (10:47 +0000)
committerPierangelo Masarati <ando@openldap.org>
Fri, 5 Jan 2007 10:47:09 +0000 (10:47 +0000)
        Fixed slapd-ldap/meta privileged conn caching (ITS#4547)
        Fixed slapd-ldap chase-referrals switch (ITS#4557)
        Fixed slapd-ldap bind behavior when idassert is always used (ITS#4781)
        Fixed slapd-ldap response handling bugs (ITS#4782)
        Fixed slapd-ldap/meta privileged connections handling (ITS#4791)
        Fixed slapd-meta retrying (ITS#4594, 4762)
        Fixed slapo-chain referral DN use (ITS#4776)

34 files changed:
CHANGES
include/lutil.h
servers/slapd/back-ldap/add.c
servers/slapd/back-ldap/back-ldap.h
servers/slapd/back-ldap/bind.c
servers/slapd/back-ldap/chain.c
servers/slapd/back-ldap/compare.c
servers/slapd/back-ldap/config.c
servers/slapd/back-ldap/delete.c
servers/slapd/back-ldap/extended.c
servers/slapd/back-ldap/init.c
servers/slapd/back-ldap/modify.c
servers/slapd/back-ldap/modrdn.c
servers/slapd/back-ldap/proto-ldap.h
servers/slapd/back-ldap/search.c
servers/slapd/back-ldap/unbind.c
servers/slapd/back-meta/add.c
servers/slapd/back-meta/back-meta.h
servers/slapd/back-meta/bind.c
servers/slapd/back-meta/candidates.c
servers/slapd/back-meta/compare.c
servers/slapd/back-meta/config.c
servers/slapd/back-meta/conn.c
servers/slapd/back-meta/delete.c
servers/slapd/back-meta/init.c
servers/slapd/back-meta/map.c
servers/slapd/back-meta/modify.c
servers/slapd/back-meta/modrdn.c
servers/slapd/back-meta/search.c
servers/slapd/back-meta/unbind.c
servers/slapd/back-monitor/database.c
servers/slapd/overlays/rwm.c
servers/slapd/overlays/rwmconf.c
servers/slapd/overlays/rwmmap.c

diff --git a/CHANGES b/CHANGES
index 11c893a3f8021143d4c5f22517f50dfbfda618ad..bd1a4cd90198568c4c83a3ec7e21d5f2b122a662 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,13 @@
 OpenLDAP 2.3 Change Log
 
 OpenLDAP 2.3.33 Engineering
+       Fixed slapd-ldap/meta privileged conn caching (ITS#4547)
+       Fixed slapd-ldap chase-referrals switch (ITS#4557)
+       Fixed slapd-ldap bind behavior when idassert is always used (ITS#4781)
+       Fixed slapd-ldap response handling bugs (ITS#4782)
+       Fixed slapd-ldap/meta privileged connections handling (ITS#4791)
+       Fixed slapd-meta retrying (ITS#4594, 4762)
+       Fixed slapo-chain referral DN use (ITS#4776)
 
 OpenLDAP 2.3.32 Release (2007/01/04)
        Fixed slapd add redundant duplicate value check (ITS#4600)
index 1fc523dc9797e1cb652992f5c7139dbd7e669991..6b2bf76beface0d086256eeb03d310d7aaf82516 100644 (file)
@@ -296,6 +296,32 @@ lutil_parse_time( const char *in, unsigned long *tp );
 LDAP_LUTIL_F (int)
 lutil_unparse_time( char *buf, size_t buflen, unsigned long t );
 
+#ifdef timerdiv
+#define lutil_timerdiv timerdiv
+#else /* ! timerdiv */
+/* works inplace (x == t) */
+#define lutil_timerdiv(t,d,x) \
+       do { \
+               time_t s = (t)->tv_sec; \
+               assert( d > 0 ); \
+               (x)->tv_sec = s / d; \
+               (x)->tv_usec = ( (t)->tv_usec + 1000000 * ( s % d ) ) / d; \
+       } while ( 0 )
+#endif /* ! timerdiv */
+
+#ifdef timermul
+#define lutil_timermul timermul
+#else /* ! timermul */
+/* works inplace (x == t) */
+#define lutil_timermul(t,m,x) \
+       do { \
+               time_t u = (t)->tv_usec * m; \
+               assert( m > 0 ); \
+               (x)->tv_sec = (t)->tv_sec * m + u / 1000000; \
+               (x)->tv_usec = u % 1000000; \
+       } while ( 0 );
+#endif /* ! timermul */
+
 LDAP_END_DECL
 
 #endif /* _LUTIL_H */
index fb3b8546d32c9f18b9a09434bb94e7c854e47a8c..b5e07df05cab64163cd7d5303f8592f340a929c0 100644 (file)
@@ -36,26 +36,25 @@ ldap_back_add(
        Operation       *op,
        SlapReply       *rs )
 {
-       ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
-
-       ldapconn_t      *lc;
-       int             i = 0,
-                       j = 0;
-       Attribute       *a;
-       LDAPMod         **attrs = NULL,
-                       *attrs2 = NULL;
-       ber_int_t       msgid;
-       int             isupdate;
-       int             do_retry = 1;
-       LDAPControl     **ctrls = NULL;
+       ldapinfo_t              *li = (ldapinfo_t *)op->o_bd->be_private;
+
+       ldapconn_t              *lc = NULL;
+       int                     i = 0,
+                               j = 0;
+       Attribute               *a;
+       LDAPMod                 **attrs = NULL,
+                               *attrs2 = NULL;
+       ber_int_t               msgid;
+       int                     isupdate;
+       ldap_back_send_t        retrying = LDAP_BACK_RETRYING;
+       LDAPControl             **ctrls = NULL;
 
        rs->sr_err = LDAP_SUCCESS;
        
        Debug( LDAP_DEBUG_ARGS, "==> ldap_back_add(\"%s\")\n",
                        op->o_req_dn.bv_val, 0, 0 );
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                lc = NULL;
                goto cleanup;
        }
@@ -94,7 +93,8 @@ ldap_back_add(
 
 retry:
        ctrls = op->o_ctrls;
-       rs->sr_err = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
+       rs->sr_err = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
+               li->li_version, &li->li_idassert, op, rs, &ctrls );
        if ( rs->sr_err != LDAP_SUCCESS ) {
                send_ldap_result( op, rs );
                goto cleanup;
@@ -103,9 +103,10 @@ retry:
        rs->sr_err = ldap_add_ext( lc->lc_ld, op->o_req_dn.bv_val, attrs,
                        ctrls, NULL, &msgid );
        rs->sr_err = ldap_back_op_result( lc, op, rs, msgid,
-               li->li_timeout[ LDAP_BACK_OP_ADD ], LDAP_BACK_SENDRESULT );
-       if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
-               do_retry = 0;
+               li->li_timeout[ SLAP_OP_ADD ],
+               ( LDAP_BACK_SENDRESULT | retrying ) );
+       if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+               retrying &= ~LDAP_BACK_RETRYING;
                if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                        /* if the identity changed, there might be need to re-authz */
                        (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
index 320e4f8e5ddc84c581ce9b1bd612b2d453b53697..d4442655eea3593080d23b56f3447127d4049b1d 100644 (file)
 
 LDAP_BEGIN_DECL
 
+struct ldapinfo_t;
+
+enum {
+       /* even numbers are connection types */
+       LDAP_BACK_PCONN_FIRST = 0,
+       LDAP_BACK_PCONN_ROOTDN = LDAP_BACK_PCONN_FIRST,
+       LDAP_BACK_PCONN_ANON = 2,
+       LDAP_BACK_PCONN_BIND = 4,
+
+       /* add the TLS bit */
+       LDAP_BACK_PCONN_TLS = 0x1U,
+
+       LDAP_BACK_PCONN_ROOTDN_TLS = (LDAP_BACK_PCONN_ROOTDN|LDAP_BACK_PCONN_TLS),
+       LDAP_BACK_PCONN_ANON_TLS = (LDAP_BACK_PCONN_ANON|LDAP_BACK_PCONN_TLS),
+       LDAP_BACK_PCONN_BIND_TLS = (LDAP_BACK_PCONN_BIND|LDAP_BACK_PCONN_TLS),
+
+       LDAP_BACK_PCONN_LAST
+};
+
 typedef struct ldapconn_t {
        Connection              *lc_conn;
-#define        LDAP_BACK_PCONN         ((void *)0x0)
-#define        LDAP_BACK_PCONN_TLS     ((void *)0x1)
-#define        LDAP_BACK_PCONN_ID(c)   ((void *)(c) > LDAP_BACK_PCONN_TLS ? (c)->c_connid : -1)
+#define        LDAP_BACK_CONN2PRIV(lc)         ((unsigned long)(lc)->lc_conn)
+#define LDAP_BACK_PCONN_ISPRIV(lc)     ((void *)(lc)->lc_conn >= (void *)LDAP_BACK_PCONN_FIRST \
+                                               && (void *)(lc)->lc_conn < (void *)LDAP_BACK_PCONN_LAST)
+#define LDAP_BACK_PCONN_ISROOTDN(lc)   (LDAP_BACK_PCONN_ISPRIV((lc)) \
+                                               && (LDAP_BACK_CONN2PRIV((lc)) < LDAP_BACK_PCONN_ANON))
+#define LDAP_BACK_PCONN_ISANON(lc)     (LDAP_BACK_PCONN_ISPRIV((lc)) \
+                                               && (LDAP_BACK_CONN2PRIV((lc)) < LDAP_BACK_PCONN_BIND) \
+                                               && (LDAP_BACK_CONN2PRIV((lc)) >= LDAP_BACK_PCONN_ANON))
+#define LDAP_BACK_PCONN_ISBIND(lc)     (LDAP_BACK_PCONN_ISPRIV((lc)) \
+                                               && (LDAP_BACK_CONN2PRIV((lc)) >= LDAP_BACK_PCONN_BIND))
+#define LDAP_BACK_PCONN_ISTLS(lc)      (LDAP_BACK_PCONN_ISPRIV((lc)) \
+                                               && (LDAP_BACK_CONN2PRIV((lc)) & LDAP_BACK_PCONN_TLS))
+#define        LDAP_BACK_PCONN_ID(lc)          (LDAP_BACK_PCONN_ISPRIV((lc)) ? \
+                                               ( -1 - (long)(lc)->lc_conn ) : (lc)->lc_conn->c_connid )
 #ifdef HAVE_TLS
-#define        LDAP_BACK_PCONN_SET(op) ((op)->o_conn->c_is_tls ? LDAP_BACK_PCONN_TLS : LDAP_BACK_PCONN)
+#define        LDAP_BACK_PCONN_ROOTDN_SET(lc, op) \
+       ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? LDAP_BACK_PCONN_ROOTDN_TLS : LDAP_BACK_PCONN_ROOTDN))
+#define        LDAP_BACK_PCONN_ANON_SET(lc, op) \
+       ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? LDAP_BACK_PCONN_ANON_TLS : LDAP_BACK_PCONN_ANON))
+#define        LDAP_BACK_PCONN_BIND_SET(lc, op) \
+       ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? LDAP_BACK_PCONN_BIND_TLS : LDAP_BACK_PCONN_BIND))
 #else /* ! HAVE_TLS */
-#define        LDAP_BACK_PCONN_SET(op) (LDAP_BACK_PCONN)
+#define        LDAP_BACK_PCONN_ROOTDN_SET(lc, op) \
+       ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_ROOTDN)
+#define        LDAP_BACK_PCONN_ANON_SET(lc, op) \
+       ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_ANON)
+#define        LDAP_BACK_PCONN_BIND_SET(lc, op) \
+       ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_BIND)
 #endif /* ! HAVE_TLS */
+#define        LDAP_BACK_PCONN_SET(lc, op) \
+       (BER_BVISEMPTY(&(op)->o_ndn) ? \
+               LDAP_BACK_PCONN_ANON_SET((lc), (op)) : LDAP_BACK_PCONN_ROOTDN_SET((lc), (op)))
 
        LDAP                    *lc_ld;
        struct berval           lc_cred;
        struct berval           lc_bound_ndn;
        struct berval           lc_local_ndn;
        unsigned                lc_lcflags;
-#define LDAP_BACK_CONN_ISSET(lc,f)     ((lc)->lc_lcflags & (f))
-#define        LDAP_BACK_CONN_SET(lc,f)        ((lc)->lc_lcflags |= (f))
-#define        LDAP_BACK_CONN_CLEAR(lc,f)      ((lc)->lc_lcflags &= ~(f))
-#define        LDAP_BACK_CONN_CPY(lc,f,mlc) \
+#define LDAP_BACK_CONN_ISSET_F(fp,f)   (*(fp) & (f))
+#define        LDAP_BACK_CONN_SET_F(fp,f)      (*(fp) |= (f))
+#define        LDAP_BACK_CONN_CLEAR_F(fp,f)    (*(fp) &= ~(f))
+#define        LDAP_BACK_CONN_CPY_F(fp,f,mfp) \
        do { \
-               if ( ((f) & (mlc)->lc_lcflags) == (f) ) { \
-                       (lc)->lc_lcflags |= (f); \
+               if ( ((f) & *(mfp)) == (f) ) { \
+                       *(fp) |= (f); \
                } else { \
-                       (lc)->lc_lcflags &= ~(f); \
+                       *(fp) &= ~(f); \
                } \
        } while ( 0 )
 
-#define        LDAP_BACK_FCONN_ISBOUND (0x01)
-#define        LDAP_BACK_FCONN_ISANON  (0x02)
+#define LDAP_BACK_CONN_ISSET(lc,f)     LDAP_BACK_CONN_ISSET_F(&(lc)->lc_lcflags, (f))
+#define        LDAP_BACK_CONN_SET(lc,f)        LDAP_BACK_CONN_SET_F(&(lc)->lc_lcflags, (f))
+#define        LDAP_BACK_CONN_CLEAR(lc,f)      LDAP_BACK_CONN_CLEAR_F(&(lc)->lc_lcflags, (f))
+#define        LDAP_BACK_CONN_CPY(lc,f,mlc)    LDAP_BACK_CONN_CPY_F(&(lc)->lc_lcflags, (f), &(mlc)->lc_lcflags)
+
+/* 0xFFF00000U are reserved for back-meta */
+
+#define        LDAP_BACK_FCONN_ISBOUND (0x00000001U)
+#define        LDAP_BACK_FCONN_ISANON  (0x00000002U)
 #define        LDAP_BACK_FCONN_ISBMASK (LDAP_BACK_FCONN_ISBOUND|LDAP_BACK_FCONN_ISANON)
-#define        LDAP_BACK_FCONN_ISPRIV  (0x04)
-#define        LDAP_BACK_FCONN_ISTLS   (0x08)
-#define        LDAP_BACK_FCONN_BINDING (0x10)
-#define        LDAP_BACK_FCONN_TAINTED (0x20)
+#define        LDAP_BACK_FCONN_ISPRIV  (0x00000004U)
+#define        LDAP_BACK_FCONN_ISTLS   (0x00000008U)
+#define        LDAP_BACK_FCONN_BINDING (0x00000010U)
+#define        LDAP_BACK_FCONN_TAINTED (0x00000020U)
+#define        LDAP_BACK_FCONN_ISIDASR (0x00000040U)
+#define        LDAP_BACK_FCONN_CACHED  (0x00000080U)
 
 #define        LDAP_BACK_CONN_ISBOUND(lc)              LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISBOUND)
 #define        LDAP_BACK_CONN_ISBOUND_SET(lc)          LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISBOUND)
@@ -84,105 +136,159 @@ typedef struct ldapconn_t {
 #define        LDAP_BACK_CONN_TAINTED(lc)              LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_TAINTED)
 #define        LDAP_BACK_CONN_TAINTED_SET(lc)          LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_TAINTED)
 #define        LDAP_BACK_CONN_TAINTED_CLEAR(lc)        LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_TAINTED)
+#define        LDAP_BACK_CONN_ISIDASSERT(lc)           LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISIDASR)
+#define        LDAP_BACK_CONN_ISIDASSERT_SET(lc)       LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISIDASR)
+#define        LDAP_BACK_CONN_ISIDASSERT_CLEAR(lc)     LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISIDASR)
+#define        LDAP_BACK_CONN_ISIDASSERT_CPY(lc, mlc)  LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISIDASR, (mlc))
+#define        LDAP_BACK_CONN_CACHED(lc)               LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_CACHED)
+#define        LDAP_BACK_CONN_CACHED_SET(lc)           LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_CACHED)
+#define        LDAP_BACK_CONN_CACHED_CLEAR(lc)         LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_CACHED)
 
        unsigned                lc_refcnt;
        unsigned                lc_binding;
        unsigned                lc_flags;
        time_t                  lc_create_time;
        time_t                  lc_time;
+
+       LDAP_TAILQ_ENTRY(ldapconn_t)    lc_q;
 } ldapconn_t;
 
+typedef struct ldap_avl_info_t {
+       ldap_pvt_thread_mutex_t         lai_mutex;
+       Avlnode                         *lai_tree;
+} ldap_avl_info_t;
+
+typedef struct slap_retry_info_t {
+       time_t          *ri_interval;
+       int             *ri_num;
+       int             ri_idx;
+       int             ri_count;
+       time_t          ri_last;
+
+#define SLAP_RETRYNUM_FOREVER  (-1)            /* retry forever */
+#define SLAP_RETRYNUM_TAIL     (-2)            /* end of retrynum array */
+#define SLAP_RETRYNUM_VALID(n) ((n) >= SLAP_RETRYNUM_FOREVER)  /* valid retrynum */
+#define SLAP_RETRYNUM_FINITE(n)        ((n) > SLAP_RETRYNUM_FOREVER)   /* not forever */
+} slap_retry_info_t;
+
 /*
  * identity assertion modes
  */
-enum {
+typedef enum {
        LDAP_BACK_IDASSERT_LEGACY = 1,
        LDAP_BACK_IDASSERT_NOASSERT,
        LDAP_BACK_IDASSERT_ANONYMOUS,
        LDAP_BACK_IDASSERT_SELF,
        LDAP_BACK_IDASSERT_OTHERDN,
        LDAP_BACK_IDASSERT_OTHERID
-};
+} slap_idassert_mode_t;
+
+/* ID assert stuff */
+typedef struct slap_idassert_t {
+       slap_idassert_mode_t    si_mode;
+#define        li_idassert_mode        li_idassert.si_mode
+
+       slap_bindconf   si_bc;
+#define        li_idassert_authcID     li_idassert.si_bc.sb_authcId
+#define        li_idassert_authcDN     li_idassert.si_bc.sb_binddn
+#define        li_idassert_passwd      li_idassert.si_bc.sb_cred
+#define        li_idassert_authzID     li_idassert.si_bc.sb_authzId
+#define        li_idassert_authmethod  li_idassert.si_bc.sb_method
+#define        li_idassert_sasl_mech   li_idassert.si_bc.sb_saslmech
+#define        li_idassert_sasl_realm  li_idassert.si_bc.sb_realm
+#define        li_idassert_secprops    li_idassert.si_bc.sb_secprops
+#define        li_idassert_tls         li_idassert.si_bc.sb_tls
+
+       unsigned        si_flags;
+#define LDAP_BACK_AUTH_NONE                            (0x00U)
+#define        LDAP_BACK_AUTH_NATIVE_AUTHZ                     (0x01U)
+#define        LDAP_BACK_AUTH_OVERRIDE                         (0x02U)
+#define        LDAP_BACK_AUTH_PRESCRIPTIVE                     (0x04U)
+#define        LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ             (0x08U)
+#define        LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND     (0x10U)
+#define        LDAP_BACK_AUTH_AUTHZ_ALL                        (0x20U)
+#define        li_idassert_flags       li_idassert.si_flags
+
+       BerVarray       si_authz;
+#define        li_idassert_authz       li_idassert.si_authz
+} slap_idassert_t;
 
 /*
- * operation enumeration for timeouts
+ * Hook to allow mucking with ldapinfo_t when quarantine is over
  */
-enum {
-       LDAP_BACK_OP_ADD = 0,
-       LDAP_BACK_OP_DELETE,
-       LDAP_BACK_OP_MODIFY,
-       LDAP_BACK_OP_MODRDN,
-       LDAP_BACK_OP_LAST
-};
-
-typedef struct ldap_avl_info_t {
-       ldap_pvt_thread_mutex_t         lai_mutex;
-       Avlnode                         *lai_tree;
-} ldap_avl_info_t;
+typedef int (*ldap_back_quarantine_f)( struct ldapinfo_t *, void * );
 
 typedef struct ldapinfo_t {
        /* li_uri: the string that goes into ldap_initialize()
         * TODO: use li_acl.sb_uri instead */
-       char            *li_uri;
+       char                    *li_uri;
        /* li_bvuri: an array of each single URI that is equivalent;
         * to be checked for the presence of a certain item */
-       BerVarray       li_bvuri;
+       BerVarray               li_bvuri;
+       ldap_pvt_thread_mutex_t li_uri_mutex;
+
+       LDAP_REBIND_PROC        *li_rebind_f;
+       void                    *li_urllist_p;
 
-       slap_bindconf   li_acl;
-#define        li_acl_authcID  li_acl.sb_authcId
-#define        li_acl_authcDN  li_acl.sb_binddn
-#define        li_acl_passwd   li_acl.sb_cred
-#define        li_acl_authzID  li_acl.sb_authzId
+       slap_bindconf           li_acl;
+#define        li_acl_authcID          li_acl.sb_authcId
+#define        li_acl_authcDN          li_acl.sb_binddn
+#define        li_acl_passwd           li_acl.sb_cred
+#define        li_acl_authzID          li_acl.sb_authzId
 #define        li_acl_authmethod       li_acl.sb_method
 #define        li_acl_sasl_mech        li_acl.sb_saslmech
 #define        li_acl_sasl_realm       li_acl.sb_realm
-#define        li_acl_secprops li_acl.sb_secprops
+#define        li_acl_secprops         li_acl.sb_secprops
 
        /* ID assert stuff */
-       int             li_idassert_mode;
-
-       slap_bindconf   li_idassert;
-#define        li_idassert_authcID     li_idassert.sb_authcId
-#define        li_idassert_authcDN     li_idassert.sb_binddn
-#define        li_idassert_passwd      li_idassert.sb_cred
-#define        li_idassert_authzID     li_idassert.sb_authzId
-#define        li_idassert_authmethod  li_idassert.sb_method
-#define        li_idassert_sasl_mech   li_idassert.sb_saslmech
-#define        li_idassert_sasl_realm  li_idassert.sb_realm
-#define        li_idassert_secprops    li_idassert.sb_secprops
-
-       unsigned        li_idassert_flags;
-#define LDAP_BACK_AUTH_NONE            0x00U
-#define        LDAP_BACK_AUTH_NATIVE_AUTHZ     0x01U
-#define        LDAP_BACK_AUTH_OVERRIDE         0x02U
-#define        LDAP_BACK_AUTH_PRESCRIPTIVE     0x04U
-
-       BerVarray       li_idassert_authz;
+       slap_idassert_t         li_idassert;
        /* end of ID assert stuff */
 
-       int             li_nretries;
+       int                     li_nretries;
 #define LDAP_BACK_RETRY_UNDEFINED      (-2)
 #define LDAP_BACK_RETRY_FOREVER                (-1)
 #define LDAP_BACK_RETRY_NEVER          (0)
 #define LDAP_BACK_RETRY_DEFAULT                (3)
 
-       unsigned        li_flags;
-#define LDAP_BACK_F_NONE               0x00U
-#define LDAP_BACK_F_SAVECRED           0x01U
-#define LDAP_BACK_F_USE_TLS            0x02U
-#define LDAP_BACK_F_PROPAGATE_TLS      0x04U
-#define LDAP_BACK_F_TLS_CRITICAL       0x08U
+       unsigned                li_flags;
+
+/* 0xFFF00000U are reserved for back-meta */
+
+#define LDAP_BACK_F_NONE               (0x00000000U)
+#define LDAP_BACK_F_SAVECRED           (0x00000001U)
+#define LDAP_BACK_F_USE_TLS            (0x00000002U)
+#define LDAP_BACK_F_PROPAGATE_TLS      (0x00000004U)
+#define LDAP_BACK_F_TLS_CRITICAL       (0x00000008U)
 #define LDAP_BACK_F_TLS_USE_MASK       (LDAP_BACK_F_USE_TLS|LDAP_BACK_F_TLS_CRITICAL)
 #define LDAP_BACK_F_TLS_PROPAGATE_MASK (LDAP_BACK_F_PROPAGATE_TLS|LDAP_BACK_F_TLS_CRITICAL)
 #define LDAP_BACK_F_TLS_MASK           (LDAP_BACK_F_TLS_USE_MASK|LDAP_BACK_F_TLS_PROPAGATE_MASK)
-#define LDAP_BACK_F_CHASE_REFERRALS    0x10U
-#define LDAP_BACK_F_PROXY_WHOAMI       0x20U
+#define LDAP_BACK_F_CHASE_REFERRALS    (0x00000010U)
+#define LDAP_BACK_F_PROXY_WHOAMI       (0x00000020U)
+
+#define        LDAP_BACK_F_T_F                 (0x00000040U)
+#define        LDAP_BACK_F_T_F_DISCOVER        (0x00000080U)
+#define        LDAP_BACK_F_T_F_MASK            (LDAP_BACK_F_T_F)
+#define        LDAP_BACK_F_T_F_MASK2           (LDAP_BACK_F_T_F_MASK|LDAP_BACK_F_T_F_DISCOVER)
+
+#define LDAP_BACK_F_MONITOR            (0x00000100U)
+#define        LDAP_BACK_F_SINGLECONN          (0x00000200U)
+#define LDAP_BACK_F_USE_TEMPORARIES    (0x00000400U)
+
+#define        LDAP_BACK_F_ISOPEN              (0x00000800U)
+
+#define        LDAP_BACK_F_CANCEL_ABANDON      (0x00000000U)
+#define        LDAP_BACK_F_CANCEL_IGNORE       (0x00001000U)
+#define        LDAP_BACK_F_CANCEL_EXOP         (0x00002000U)
+#define        LDAP_BACK_F_CANCEL_EXOP_DISCOVER        (0x00004000U)
+#define        LDAP_BACK_F_CANCEL_MASK         (LDAP_BACK_F_CANCEL_IGNORE|LDAP_BACK_F_CANCEL_EXOP)
+#define        LDAP_BACK_F_CANCEL_MASK2        (LDAP_BACK_F_CANCEL_MASK|LDAP_BACK_F_CANCEL_EXOP_DISCOVER)
 
-#define        LDAP_BACK_F_SUPPORT_T_F                 0x80U
-#define        LDAP_BACK_F_SUPPORT_T_F_DISCOVER        0x40U
-#define        LDAP_BACK_F_SUPPORT_T_F_MASK            (LDAP_BACK_F_SUPPORT_T_F|LDAP_BACK_F_SUPPORT_T_F_DISCOVER)
+#define        LDAP_BACK_ISSET_F(ff,f)         ( ( (ff) & (f) ) == (f) )
+#define        LDAP_BACK_ISMASK_F(ff,m,f)      ( ( (ff) & (m) ) == (f) )
+
+#define        LDAP_BACK_ISSET(li,f)           LDAP_BACK_ISSET_F( (li)->li_flags, (f) )
+#define        LDAP_BACK_ISMASK(li,m,f)        LDAP_BACK_ISMASK_F( (li)->li_flags, (m), (f) )
 
-#define        LDAP_BACK_ISSET(li,f)           ( ( (li)->li_flags & (f) ) == (f) )
 #define LDAP_BACK_SAVECRED(li)         LDAP_BACK_ISSET( (li), LDAP_BACK_F_SAVECRED )
 #define LDAP_BACK_USE_TLS(li)          LDAP_BACK_ISSET( (li), LDAP_BACK_F_USE_TLS )
 #define LDAP_BACK_PROPAGATE_TLS(li)    LDAP_BACK_ISSET( (li), LDAP_BACK_F_PROPAGATE_TLS )
@@ -190,14 +296,55 @@ typedef struct ldapinfo_t {
 #define LDAP_BACK_CHASE_REFERRALS(li)  LDAP_BACK_ISSET( (li), LDAP_BACK_F_CHASE_REFERRALS )
 #define LDAP_BACK_PROXY_WHOAMI(li)     LDAP_BACK_ISSET( (li), LDAP_BACK_F_PROXY_WHOAMI )
 
-       int             li_version;
+#define LDAP_BACK_USE_TLS_F(ff)                LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_USE_TLS )
+#define LDAP_BACK_PROPAGATE_TLS_F(ff)  LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_PROPAGATE_TLS )
+#define LDAP_BACK_TLS_CRITICAL_F(ff)   LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_TLS_CRITICAL )
+
+#define        LDAP_BACK_T_F(li)               LDAP_BACK_ISMASK( (li), LDAP_BACK_F_T_F_MASK, LDAP_BACK_F_T_F )
+#define        LDAP_BACK_T_F_DISCOVER(li)      LDAP_BACK_ISMASK( (li), LDAP_BACK_F_T_F_MASK2, LDAP_BACK_F_T_F_DISCOVER )
+
+#define LDAP_BACK_MONITOR(li)          LDAP_BACK_ISSET( (li), LDAP_BACK_F_MONITOR )
+#define        LDAP_BACK_SINGLECONN(li)        LDAP_BACK_ISSET( (li), LDAP_BACK_F_SINGLECONN )
+#define        LDAP_BACK_USE_TEMPORARIES(li)   LDAP_BACK_ISSET( (li), LDAP_BACK_F_USE_TEMPORARIES)
+
+#define        LDAP_BACK_ISOPEN(li)            LDAP_BACK_ISSET( (li), LDAP_BACK_F_ISOPEN )
+
+#define        LDAP_BACK_ABANDON(li)           LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_ABANDON )
+#define        LDAP_BACK_IGNORE(li)            LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_IGNORE )
+#define        LDAP_BACK_CANCEL(li)            LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_EXOP )
+#define        LDAP_BACK_CANCEL_DISCOVER(li)   LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK2, LDAP_BACK_F_CANCEL_EXOP_DISCOVER )
+
+       int                     li_version;
 
-       ldap_avl_info_t li_conninfo;
+       /* cached connections; 
+        * special conns are in tailq rather than in tree */
+       ldap_avl_info_t         li_conninfo;
+       struct {
+               int                                             lic_num;
+               LDAP_TAILQ_HEAD(lc_conn_priv_q, ldapconn_t)     lic_priv;
+       }                       li_conn_priv[ LDAP_BACK_PCONN_LAST ];
+       int                     li_conn_priv_max;
+#define        LDAP_BACK_CONN_PRIV_MIN         (1)
+#define        LDAP_BACK_CONN_PRIV_MAX         (256)
+       /* must be between LDAP_BACK_CONN_PRIV_MIN
+        * and LDAP_BACK_CONN_PRIV_MAX ! */
+#define        LDAP_BACK_CONN_PRIV_DEFAULT     (16)
 
-       time_t          li_network_timeout;
-       time_t          li_conn_ttl;
-       time_t          li_idle_timeout;
-       time_t          li_timeout[ LDAP_BACK_OP_LAST ];
+       sig_atomic_t            li_isquarantined;
+#define        LDAP_BACK_FQ_NO         (0)
+#define        LDAP_BACK_FQ_YES        (1)
+#define        LDAP_BACK_FQ_RETRYING   (2)
+
+       slap_retry_info_t       li_quarantine;
+#define        LDAP_BACK_QUARANTINE(li)        ( (li)->li_quarantine.ri_num != NULL )
+       ldap_pvt_thread_mutex_t li_quarantine_mutex;
+       ldap_back_quarantine_f  li_quarantine_f;
+       void                    *li_quarantine_p;
+
+       time_t                  li_network_timeout;
+       time_t                  li_conn_ttl;
+       time_t                  li_idle_timeout;
+       time_t                  li_timeout[ SLAP_OP_LAST ];
 } ldapinfo_t;
 
 typedef enum ldap_back_send_t {
@@ -206,10 +353,19 @@ typedef enum ldap_back_send_t {
        LDAP_BACK_SENDERR               = 0x02,
        LDAP_BACK_SENDRESULT            = (LDAP_BACK_SENDOK|LDAP_BACK_SENDERR),
        LDAP_BACK_BINDING               = 0x04,
+
        LDAP_BACK_BIND_DONTSEND         = (LDAP_BACK_BINDING),
        LDAP_BACK_BIND_SOK              = (LDAP_BACK_BINDING|LDAP_BACK_SENDOK),
        LDAP_BACK_BIND_SERR             = (LDAP_BACK_BINDING|LDAP_BACK_SENDERR),
-       LDAP_BACK_BIND_SRES             = (LDAP_BACK_BINDING|LDAP_BACK_SENDRESULT)
+       LDAP_BACK_BIND_SRES             = (LDAP_BACK_BINDING|LDAP_BACK_SENDRESULT),
+
+       LDAP_BACK_RETRYING              = 0x08,
+       LDAP_BACK_RETRY_DONTSEND        = (LDAP_BACK_RETRYING),
+       LDAP_BACK_RETRY_SOK             = (LDAP_BACK_RETRYING|LDAP_BACK_SENDOK),
+       LDAP_BACK_RETRY_SERR            = (LDAP_BACK_RETRYING|LDAP_BACK_SENDERR),
+       LDAP_BACK_RETRY_SRES            = (LDAP_BACK_RETRYING|LDAP_BACK_SENDRESULT),
+
+       LDAP_BACK_GETCONN               = 0x10
 } ldap_back_send_t;
 
 /* define to use asynchronous StartTLS */
@@ -224,6 +380,10 @@ typedef enum ldap_back_send_t {
                (tv)->tv_usec = LDAP_BACK_RESULT_UTIMEOUT; \
        } while ( 0 )
 
+#ifndef LDAP_BACK_PRINT_CONNTREE
+#define LDAP_BACK_PRINT_CONNTREE 0
+#endif /* !LDAP_BACK_PRINT_CONNTREE */
+
 LDAP_END_DECL
 
 #include "proto-ldap.h"
index ae7ca9e2e6e4ad89a54061a672e9855a40d844d9..83c12237f30366dedb8b0e5ee3799439ce5c8266 100644 (file)
 #define AVL_INTERNAL
 #include "slap.h"
 #include "back-ldap.h"
+#undef ldap_debug      /* silence a warning in ldap-int.h */
+#include "../../../libraries/libldap/ldap-int.h"
 
-#include <lutil_ldap.h>
-
-#ifndef PRINT_CONNTREE
-#define PRINT_CONNTREE 0
-#endif /* !PRINT_CONNTREE */
+#include "lutil_ldap.h"
 
 #define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ      "2.16.840.1.113730.3.4.12"
 
-static LDAP_REBIND_PROC        ldap_back_default_rebind;
+#if LDAP_BACK_PRINT_CONNTREE > 0
+static void
+ldap_back_ravl_print( Avlnode *root, int depth )
+{
+       int             i;
+       ldapconn_t      *lc;
+       
+       if ( root == 0 ) {
+               return;
+       }
+       
+       ldap_back_ravl_print( root->avl_right, depth+1 );
+       
+       for ( i = 0; i < depth; i++ ) {
+               fprintf( stderr, "-" );
+       }
+
+       lc = root->avl_data;
+       fprintf( stderr, "lc=%p local=\"%s\" conn=%p %s refcnt=%d flags=0x%08x\n",
+               (void *)lc,
+               lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "",
+               (void *)lc->lc_conn,
+               avl_bf2str( root->avl_bf ), lc->lc_refcnt, lc->lc_lcflags );
+       
+       ldap_back_ravl_print( root->avl_left, depth+1 );
+}
+
+static char* priv2str[] = {
+       "privileged",
+       "privileged/TLS",
+       "anonymous",
+       "anonymous/TLS",
+       "bind",
+       "bind/TLS",
+       NULL
+};
+
+void
+ldap_back_print_conntree( ldapinfo_t *li, char *msg )
+{
+       int     c;
+
+       fprintf( stderr, "========> %s\n", msg );
+
+       for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
+               int             i = 0;
+               ldapconn_t      *lc;
+
+               fprintf( stderr, "  %s[%d]\n", priv2str[ c ], li->li_conn_priv[ c ].lic_num );
+
+               LDAP_TAILQ_FOREACH( lc, &li->li_conn_priv[ c ].lic_priv, lc_q )
+               {
+                       fprintf( stderr, "    [%d] lc=%p local=\"%s\" conn=%p refcnt=%d flags=0x%08x\n",
+                               i,
+                               (void *)lc,
+                               lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "",
+                               (void *)lc->lc_conn, lc->lc_refcnt, lc->lc_lcflags );
+                       i++;
+               }
+       }
+       
+       if ( li->li_conninfo.lai_tree == 0 ) {
+               fprintf( stderr, "\t(empty)\n" );
+
+       } else {
+               ldap_back_ravl_print( li->li_conninfo.lai_tree, 0 );
+       }
+       
+       fprintf( stderr, "<======== %s\n", msg );
+}
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+static int
+ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock );
+
+static ldapconn_t *
+ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+       struct berval *binddn, struct berval *bindcred );
 
-LDAP_REBIND_PROC       *ldap_back_rebind_f = ldap_back_default_rebind;
+static int
+ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+       struct berval *binddn, struct berval *bindcred );
 
 static int
-ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
+ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs,
+       ldap_back_send_t sendok, struct berval *binddn, struct berval *bindcred );
 
 static int
-ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
+ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs,
+       ldap_back_send_t sendok );
 
 static int
 ldap_back_conndnlc_cmp( const void *c1, const void *c2 );
@@ -57,64 +136,89 @@ ldap_back_conndnlc_cmp( const void *c1, const void *c2 );
 int
 ldap_back_bind( Operation *op, SlapReply *rs )
 {
-       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
-       ldapconn_t      *lc;
+       ldapinfo_t              *li = (ldapinfo_t *) op->o_bd->be_private;
+       ldapconn_t              *lc;
 
-       int rc = 0;
-       ber_int_t msgid;
+       int                     rc = 0;
+       ber_int_t               msgid;
+       ldap_back_send_t        retrying = LDAP_BACK_RETRYING;
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR );
+       lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR, NULL, NULL );
        if ( !lc ) {
                return rs->sr_err;
        }
 
+       /* we can do (almost) whatever we want with this conn,
+        * because either it's temporary, or it's marked as binding */
        if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
                ch_free( lc->lc_bound_ndn.bv_val );
                BER_BVZERO( &lc->lc_bound_ndn );
        }
+       if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+               memset( lc->lc_cred.bv_val, 0, lc->lc_cred.bv_len );
+               ch_free( lc->lc_cred.bv_val );
+               BER_BVZERO( &lc->lc_cred );
+       }
        LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
 
+retry:;
        /* method is always LDAP_AUTH_SIMPLE if we got here */
        rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
                        LDAP_SASL_SIMPLE,
                        &op->orb_cred, op->o_ctrls, NULL, &msgid );
-       rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDERR );
+       /* FIXME: should we always retry, or only when piping the bind
+        * in the "override" connection pool? */
+       rc = ldap_back_op_result( lc, op, rs, msgid,
+               li->li_timeout[ SLAP_OP_BIND ],
+               LDAP_BACK_BIND_SERR | retrying );
+       if ( rc == LDAP_UNAVAILABLE && retrying ) {
+               retrying &= ~LDAP_BACK_RETRYING;
+               if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_BIND_SERR ) ) {
+                       goto retry;
+               }
+       }
 
        if ( rc == LDAP_SUCCESS ) {
                /* If defined, proxyAuthz will be used also when
                 * back-ldap is the authorizing backend; for this
-                * purpose, a successful bind is followed by a
-                * bind with the configured identity assertion */
+                * purpose, after a successful bind the connection
+                * is left for further binds, and further operations 
+                * on this client connection will use a default
+                * connection with identity assertion */
                /* NOTE: use with care */
                if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
-                       ldap_back_proxy_authz_bind( lc, op, rs, LDAP_BACK_SENDERR );
-                       if ( !LDAP_BACK_CONN_ISBOUND( lc ) ) {
-                               rc = 1;
-                               goto done;
-                       }
+                       assert( lc->lc_binding == 1 );
+                       lc->lc_binding = 0;
+                       ldap_back_release_conn( op, rs, lc );
+                       return( rc );
                }
 
+               /* rebind is now done inside ldap_back_proxy_authz_bind()
+                * in case of success */
                LDAP_BACK_CONN_ISBOUND_SET( lc );
                ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
 
+               if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+                       memset( lc->lc_cred.bv_val, 0,
+                                       lc->lc_cred.bv_len );
+               }
+
                if ( LDAP_BACK_SAVECRED( li ) ) {
-                       if ( !BER_BVISNULL( &lc->lc_cred ) ) {
-                               memset( lc->lc_cred.bv_val, 0,
-                                               lc->lc_cred.bv_len );
-                       }
                        ber_bvreplace( &lc->lc_cred, &op->orb_cred );
-                       ldap_set_rebind_proc( lc->lc_ld, ldap_back_rebind_f, lc );
+                       ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
+
+               } else {
+                       lc->lc_cred.bv_len = 0;
                }
        }
-done:;
 
        assert( lc->lc_binding == 1 );
        lc->lc_binding = 0;
 
        /* must re-insert if local DN changed as result of bind */
        if ( !LDAP_BACK_CONN_ISBOUND( lc )
-               || ( LDAP_BACK_CONN_ISBOUND( lc )
-                       && !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) ) )
+               || ( !dn_match( &op->o_req_ndn, &lc->lc_local_ndn )
+                       && !LDAP_BACK_PCONN_ISPRIV( lc ) ) )
        {
                int             lerr = -1;
                ldapconn_t      *tmplc;
@@ -128,20 +232,74 @@ retry_lock:;
                        goto retry_lock;
                }
 
+#if LDAP_BACK_PRINT_CONNTREE > 0
+               ldap_back_print_conntree( li, ">>> ldap_back_bind" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
                assert( lc->lc_refcnt == 1 );
-               tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
-                               ldap_back_conndnlc_cmp );
-               assert( tmplc == NULL || lc == tmplc );
+               if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+                       /* this can happen, for example, if the bind fails
+                        * for some reason... */
+                       if ( lc->lc_q.tqe_prev != NULL ) {
+                               assert( LDAP_BACK_CONN_CACHED( lc ) );
+                               assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 );
+                               LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
+                               li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--;
+                               lc->lc_q.tqe_prev = NULL;
+                               lc->lc_q.tqe_next = NULL;
+
+                       } else {
+                               assert( !LDAP_BACK_CONN_CACHED( lc ) );
+                       }
+
+               } else {
+                       tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
+                                       ldap_back_conndnlc_cmp );
+                       assert( ( LDAP_BACK_CONN_TAINTED( lc ) && tmplc == NULL ) || lc == tmplc );
+               }
+               LDAP_BACK_CONN_CACHED_CLEAR( lc );
+
+               /* delete all cached connections with the current connection */
+               if ( LDAP_BACK_SINGLECONN( li ) ) {
+                       while ( ( tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conn_cmp ) ) != NULL )
+                       {
+                               Debug( LDAP_DEBUG_TRACE,
+                                       "=>ldap_back_bind: destroying conn %ld (refcnt=%u)\n",
+                                       LDAP_BACK_PCONN_ID( lc ), lc->lc_refcnt, 0 );
+
+                               if ( tmplc->lc_refcnt != 0 ) {
+                                       /* taint it */
+                                       LDAP_BACK_CONN_TAINTED_SET( tmplc );
+                                       LDAP_BACK_CONN_CACHED_CLEAR( tmplc );
+
+                               } else {
+                                       /*
+                                        * Needs a test because the handler may be corrupted,
+                                        * and calling ldap_unbind on a corrupted header results
+                                        * in a segmentation fault
+                                        */
+                                       ldap_back_conn_free( tmplc );
+                               }
+                       }
+               }
 
                if ( LDAP_BACK_CONN_ISBOUND( lc ) ) {
                        ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn );
+                       if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
+                               LDAP_BACK_PCONN_ROOTDN_SET( lc, op );
+                       }
                        lerr = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
                                ldap_back_conndn_cmp, ldap_back_conndn_dup );
                }
 
+#if LDAP_BACK_PRINT_CONNTREE > 0
+               ldap_back_print_conntree( li, "<<< ldap_back_bind" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+       
                ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
                switch ( lerr ) {
                case 0:
+                       LDAP_BACK_CONN_CACHED_SET( lc );
                        break;
 
                case -1:
@@ -255,64 +413,53 @@ ldap_back_conndn_dup( void *c1, void *c2 )
        return 0;
 }
 
-#if PRINT_CONNTREE > 0
-static void
-ravl_print( Avlnode *root, int depth )
+static int
+ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock )
 {
-       int             i;
-       ldapconn_t      *lc;
-       
-       if ( root == 0 ) {
-               return;
-       }
-       
-       ravl_print( root->avl_right, depth+1 );
-       
-       for ( i = 0; i < depth; i++ ) {
-               fprintf( stderr, "-" );
+       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
+
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
        }
 
-       lc = root->avl_data;
-       fprintf( stderr, "lc=%p local=\"%s\" conn=%p %s refcnt=%d\n",
-               (void *)lc, lc->lc_local_ndn.bv_val, (void *)lc->lc_conn,
-               avl_bf2str( root->avl_bf ), lc->lc_refcnt );
-       
-       ravl_print( root->avl_left, depth+1 );
-}
+#if LDAP_BACK_PRINT_CONNTREE > 0
+       ldap_back_print_conntree( li, ">>> ldap_back_freeconn" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
 
-static void
-myprint( Avlnode *root )
-{
-       fprintf( stderr, "========>\n" );
-       
-       if ( root == 0 ) {
-               fprintf( stderr, "\tNULL\n" );
+       if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+               if ( lc->lc_q.tqe_prev != NULL ) {
+                       assert( LDAP_BACK_CONN_CACHED( lc ) );
+                       assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 );
+                       li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--;
+                       LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
+                       LDAP_BACK_CONN_CACHED_CLEAR( lc );
 
-       } else {
-               ravl_print( root, 0 );
-       }
-       
-       fprintf( stderr, "<========\n" );
-}
-#endif /* PRINT_CONNTREE */
+               } else {
+                       assert( !LDAP_BACK_CONN_CACHED( lc ) );
+               }
+               lc->lc_q.tqe_prev = NULL;
+               lc->lc_q.tqe_next = NULL;
 
-int
-ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock )
-{
-       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
-       ldapconn_t      *tmplc;
+       } else {
+               ldapconn_t      *tmplc = NULL;
 
-       if ( dolock ) {
-               ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+               if ( LDAP_BACK_CONN_CACHED( lc ) ) {
+                       tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
+                               ldap_back_conndnlc_cmp );
+                       assert( tmplc == lc );
+                       LDAP_BACK_CONN_CACHED_CLEAR( lc );
+               }
+               assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc );
        }
 
-       tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
-                       ldap_back_conndnlc_cmp );
-       assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc );
        if ( lc->lc_refcnt == 0 ) {
                ldap_back_conn_free( (void *)lc );
        }
 
+#if LDAP_BACK_PRINT_CONNTREE > 0
+       ldap_back_print_conntree( li, "<<< ldap_back_freeconn" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
        if ( dolock ) {
                ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
        }
@@ -332,13 +479,9 @@ ldap_back_start_tls(
        const char      **text )
 {
        int             rc = LDAP_SUCCESS;
-       ldapinfo_t      dummy;
-
-       /* this is ridiculous... */
-       dummy.li_flags = flags;
 
        /* start TLS ("tls-[try-]{start,propagate}" statements) */
-       if ( ( LDAP_BACK_USE_TLS( &dummy ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS( &dummy ) ) )
+       if ( ( LDAP_BACK_USE_TLS_F( flags ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS_F( flags ) ) )
                                && !ldap_is_ldaps_url( url ) )
        {
 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
@@ -354,13 +497,15 @@ ldap_back_start_tls(
                }
 
                if ( protocol < LDAP_VERSION3 ) {
-                       protocol = LDAP_VERSION3;
-                       /* Set LDAP version */
-                       ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
-                                       (const void *)&protocol );
+                       /* we should rather bail out... */
+                       rc = LDAP_UNWILLING_TO_PERFORM;
+                       *text = "invalid protocol version";
+               }
+
+               if ( rc == LDAP_SUCCESS ) {
+                       rc = ldap_start_tls( ld, NULL, NULL, &msgid );
                }
 
-               rc = ldap_start_tls( ld, NULL, NULL, &msgid );
                if ( rc == LDAP_SUCCESS ) {
                        LDAPMessage     *res = NULL;
                        struct timeval  tv;
@@ -445,7 +590,7 @@ retry:;
                        break;
 
                default:
-                       if ( LDAP_BACK_TLS_CRITICAL( &dummy ) ) {
+                       if ( LDAP_BACK_TLS_CRITICAL_F( flags ) ) {
                                *text = "could not start TLS";
                                break;
                        }
@@ -468,15 +613,18 @@ static int
 ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
-       int             vers = op->o_protocol;
+       int             version;
        LDAP            *ld = NULL;
 #ifdef HAVE_TLS
        int             is_tls = op->o_conn->c_is_tls;
+       time_t          lc_time = (time_t)(-1);
 #endif /* HAVE_TLS */
 
        assert( lcp != NULL );
 
+       ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
        rs->sr_err = ldap_initialize( &ld, li->li_uri );
+       ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
        if ( rs->sr_err != LDAP_SUCCESS ) {
                goto error_return;
        }
@@ -484,11 +632,17 @@ ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_bac
        /* Set LDAP version. This will always succeed: If the client
         * bound with a particular version, then so can we.
         */
-       if ( vers == 0 ) {
+       if ( li->li_version != 0 ) {
+               version = li->li_version;
+
+       } else if ( op->o_protocol != 0 ) {
+               version = op->o_protocol;
+
+       } else {
                /* assume it's an internal op; set to LDAPv3 */
-               vers = LDAP_VERSION3;
+               version = LDAP_VERSION3;
        }
-       ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
+       ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&version );
 
        /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
        ldap_set_option( ld, LDAP_OPT_REFERRALS,
@@ -503,17 +657,23 @@ ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_bac
        }
 
 #ifdef HAVE_TLS
+       ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
        rs->sr_err = ldap_back_start_tls( ld, op->o_protocol, &is_tls,
                        li->li_uri, li->li_flags, li->li_nretries, &rs->sr_text );
+       ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
        if ( rs->sr_err != LDAP_SUCCESS ) {
                ldap_unbind_ext( ld, NULL, NULL );
                goto error_return;
+
+       } else if ( li->li_idle_timeout ) {
+               /* only touch when activity actually took place... */
+               lc_time = op->o_time;
        }
 #endif /* HAVE_TLS */
 
        if ( *lcp == NULL ) {
                *lcp = (ldapconn_t *)ch_calloc( 1, sizeof( ldapconn_t ) );
-               (*lcp)->lc_flags= li->li_flags;
+               (*lcp)->lc_flags = li->li_flags;
        }
        (*lcp)->lc_ld = ld;
        (*lcp)->lc_refcnt = 1;
@@ -524,6 +684,9 @@ ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_bac
        } else {
                LDAP_BACK_CONN_ISTLS_CLEAR( *lcp );
        }
+       if ( lc_time != (time_t)(-1) ) {
+               (*lcp)->lc_time = lc_time;
+       }
 #endif /* HAVE_TLS */
 
 error_return:;
@@ -546,48 +709,179 @@ error_return:;
        return rs->sr_err;
 }
 
-ldapconn_t *
-ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+static ldapconn_t *
+ldap_back_getconn(
+       Operation               *op,
+       SlapReply               *rs,
+       ldap_back_send_t        sendok,
+       struct berval           *binddn,
+       struct berval           *bindcred )
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
        ldapconn_t      *lc = NULL,
                        lc_curr = { 0 };
-       int             refcnt = 1, binding = 1;
+       int             refcnt = 1,
+                       binding = 1,
+                       lookupconn = !( sendok & LDAP_BACK_BINDING );
+
+       /* if the server is quarantined, and
+        * - the current interval did not expire yet, or
+        * - no more retries should occur,
+        * don't return the connection */
+       if ( li->li_isquarantined ) {
+               slap_retry_info_t       *ri = &li->li_quarantine;
+               int                     dont_retry = 1;
+
+               if ( li->li_quarantine.ri_interval ) {
+                       ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
+                       if ( li->li_isquarantined == LDAP_BACK_FQ_YES ) {
+                               dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
+                                       || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
+                               if ( !dont_retry ) {
+                                       Debug( LDAP_DEBUG_ANY,
+                                               "%s: ldap_back_getconn quarantine "
+                                               "retry block #%d try #%d.\n",
+                                               op->o_log_prefix, ri->ri_idx, ri->ri_count );
+                                       li->li_isquarantined = LDAP_BACK_FQ_RETRYING;
+                               }
+                       }
+                       ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
+               }
+
+               if ( dont_retry ) {
+                       rs->sr_err = LDAP_UNAVAILABLE;
+                       if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+                               send_ldap_result( op, rs );
+                       }
+                       return NULL;
+               }
+       }
 
        /* Internal searches are privileged and shared. So is root. */
        if ( op->o_do_not_cache || be_isroot( op ) ) {
                LDAP_BACK_CONN_ISPRIV_SET( &lc_curr );
                lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
-               lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op );
+               LDAP_BACK_PCONN_ROOTDN_SET( &lc_curr, op );
 
        } else {
+               struct berval   tmpbinddn,
+                               tmpbindcred,
+                               save_o_dn,
+                               save_o_ndn;
+               int             isproxyauthz;
+
+               /* need cleanup */
+               if ( binddn == NULL ) {
+                       binddn = &tmpbinddn;
+               }       
+               if ( bindcred == NULL ) {
+                       bindcred = &tmpbindcred;
+               }
+               if ( op->o_tag == LDAP_REQ_BIND ) {
+                       save_o_dn = op->o_dn;
+                       save_o_ndn = op->o_ndn;
+                       op->o_dn = op->o_req_dn;
+                       op->o_ndn = op->o_req_ndn;
+               }
+               isproxyauthz = ldap_back_is_proxy_authz( op, rs, sendok, binddn, bindcred );
+               if ( isproxyauthz == -1 ) {
+                       return NULL;
+               }
+               if ( op->o_tag == LDAP_REQ_BIND ) {
+                       op->o_dn = save_o_dn;
+                       op->o_ndn = save_o_ndn;
+               }
+
                lc_curr.lc_local_ndn = op->o_ndn;
-               /* Explicit binds must not be shared */
-               if ( op->o_tag == LDAP_REQ_BIND || SLAP_IS_AUTHZ_BACKEND( op ) ) {
+               /* Explicit binds must not be shared;
+                * however, explicit binds are piped in a special connection
+                * when idassert is to occur with "override" set */
+               if ( op->o_tag == LDAP_REQ_BIND && !isproxyauthz ) {
                        lc_curr.lc_conn = op->o_conn;
-       
+
                } else {
-                       lc_curr.lc_conn = LDAP_BACK_PCONN_SET( op );
+                       if ( isproxyauthz && !( sendok & LDAP_BACK_BINDING ) ) {
+                               lc_curr.lc_local_ndn = *binddn;
+                               LDAP_BACK_PCONN_ROOTDN_SET( &lc_curr, op );
+                               LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
+
+                       } else if ( isproxyauthz && ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) {
+                               lc_curr.lc_local_ndn = slap_empty_bv;
+                               LDAP_BACK_PCONN_BIND_SET( &lc_curr, op );
+                               LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
+                               lookupconn = 1;
+
+                       } else if ( SLAP_IS_AUTHZ_BACKEND( op ) ) {
+                               lc_curr.lc_conn = op->o_conn;
+
+                       } else {
+                               LDAP_BACK_PCONN_ANON_SET( &lc_curr, op );
+                       }
                }
        }
 
        /* Explicit Bind requests always get their own conn */
-       if ( !( sendok & LDAP_BACK_BINDING ) ) {
-               /* Searches for a ldapconn in the avl tree */
+       if ( lookupconn ) {
 retry_lock:
                ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+               if ( LDAP_BACK_PCONN_ISPRIV( &lc_curr ) ) {
+                       /* lookup a conn that's not binding */
+                       LDAP_TAILQ_FOREACH( lc,
+                               &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_priv,
+                               lc_q )
+                       {
+                               if ( !LDAP_BACK_CONN_BINDING( lc ) && lc->lc_refcnt == 0 ) {
+                                       break;
+                               }
+                       }
+
+                       if ( lc != NULL ) {
+                               if ( lc != LDAP_TAILQ_LAST( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+                                       ldapconn_t, lc_q ) )
+                               {
+                                       LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+                                               lc, lc_q );
+                                       lc->lc_q.tqe_prev = NULL;
+                                       lc->lc_q.tqe_next = NULL;
+                                       LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+                                               lc, lc_q );
+                               }
+
+                       } else if ( !LDAP_BACK_USE_TEMPORARIES( li )
+                               && li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_num == li->li_conn_priv_max )
+                       {
+                               lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_priv );
+                       }
+                       
+               } else {
+
+                       /* Searches for a ldapconn in the avl tree */
+                       lc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
+                                       (caddr_t)&lc_curr, ldap_back_conndn_cmp );
+               }
 
-               lc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
-                               (caddr_t)&lc_curr, ldap_back_conndn_cmp );
                if ( lc != NULL ) {
                        /* Don't reuse connections while they're still binding */
                        if ( LDAP_BACK_CONN_BINDING( lc ) ) {
-                               ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
-                               ldap_pvt_thread_yield();
-                               goto retry_lock;
+                               if ( !LDAP_BACK_USE_TEMPORARIES( li ) ) {
+                                       ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+                                       ldap_pvt_thread_yield();
+                                       goto retry_lock;
+                               }
+                               lc = NULL;
+                       }
+
+                       if ( lc != NULL ) {
+                               if ( op->o_tag == LDAP_REQ_BIND ) {
+                                       /* right now, this is the only possible case */
+                                       assert( ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) );
+                                       LDAP_BACK_CONN_BINDING_SET( lc );
+                               }
+
+                               refcnt = ++lc->lc_refcnt;
+                               binding = ++lc->lc_binding;
                        }
-                       refcnt = ++lc->lc_refcnt;
-                       binding = ++lc->lc_binding;
                }
                ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
        }
@@ -597,29 +891,52 @@ retry_lock:
                if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
                        return NULL;
                }
+
                if ( sendok & LDAP_BACK_BINDING ) {
                        LDAP_BACK_CONN_BINDING_SET( lc );
                }
+
                lc->lc_conn = lc_curr.lc_conn;
                ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
 
+               /*
+                * the rationale is: connections as the rootdn are privileged,
+                * so acl_authcDN is to be used; however, in some cases
+                * one already configured identity assertion with a highly
+                * privileged idassert_authcDN, so if acl_authcDN is NULL
+                * and idassert_authcDN is not, use the second instead.
+                *
+                * might change in the future, because it's preferable
+                * to make clear what identity is being used, since
+                * the only drawback is that one risks to configure
+                * the same identity twice...
+                */
                if ( LDAP_BACK_CONN_ISPRIV( &lc_curr ) ) {
-                       ber_dupbv( &lc->lc_cred, &li->li_acl_passwd );
-                       ber_dupbv( &lc->lc_bound_ndn, &li->li_acl_authcDN );
+                       if ( BER_BVISNULL( &li->li_acl_authcDN ) && !BER_BVISNULL( &li->li_idassert_authcDN ) ) {
+                               ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
+                               ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
+
+                       } else {
+                               ber_dupbv( &lc->lc_bound_ndn, &li->li_acl_authcDN );
+                               ber_dupbv( &lc->lc_cred, &li->li_acl_passwd );
+                       }
                        LDAP_BACK_CONN_ISPRIV_SET( lc );
 
+               } else if ( LDAP_BACK_CONN_ISIDASSERT( &lc_curr ) ) {
+                       if ( !LDAP_BACK_PCONN_ISBIND( &lc_curr ) ) {
+                               ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
+                               ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
+                       }
+                       LDAP_BACK_CONN_ISIDASSERT_SET( lc );
+
                } else {
                        BER_BVZERO( &lc->lc_cred );
                        BER_BVZERO( &lc->lc_bound_ndn );
-#if 0
-                       /* FIXME: if we set lc_bound_ndn = o_ndn
-                        * we end up with a bind with DN but no password! */
                        if ( !BER_BVISEMPTY( &op->o_ndn )
                                && SLAP_IS_AUTHZ_BACKEND( op ) )
                        {
                                ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
                        }
-#endif
                }
 
 #ifdef HAVE_TLS
@@ -627,15 +944,22 @@ retry_lock:
                 * check if the non-TLS connection was already
                 * in cache; in case, destroy the newly created
                 * connection and use the existing one */
-               if ( lc->lc_conn == LDAP_BACK_PCONN_TLS
+               if ( LDAP_BACK_PCONN_ISTLS( lc ) 
                                && !ldap_tls_inplace( lc->lc_ld ) )
                {
-                       ldapconn_t *tmplc;
+                       ldapconn_t      *tmplc = NULL;
+                       int             idx = LDAP_BACK_CONN2PRIV( &lc_curr ) - 1;
                        
-                       lc_curr.lc_conn = LDAP_BACK_PCONN;
                        ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
-                       tmplc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
-                                       (caddr_t)&lc_curr, ldap_back_conndn_cmp );
+                       LDAP_TAILQ_FOREACH( tmplc,
+                               &li->li_conn_priv[ idx ].lic_priv,
+                               lc_q )
+                       {
+                               if ( !LDAP_BACK_CONN_BINDING( tmplc ) ) {
+                                       break;
+                               }
+                       }
+
                        if ( tmplc != NULL ) {
                                refcnt = ++tmplc->lc_refcnt;
                                binding = ++tmplc->lc_binding;
@@ -650,57 +974,86 @@ retry_lock:
                }
 #endif /* HAVE_TLS */
 
-               LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
-
                /* Inserts the newly created ldapconn in the avl tree */
                ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
 
+               LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+
                assert( lc->lc_refcnt == 1 );
                assert( lc->lc_binding == 1 );
-               rs->sr_err = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
-                       ldap_back_conndn_cmp, ldap_back_conndn_dup );
 
-#if PRINT_CONNTREE > 0
-               myprint( li->li_conninfo.lai_tree );
-#endif /* PRINT_CONNTREE */
+#if LDAP_BACK_PRINT_CONNTREE > 0
+               ldap_back_print_conntree( li, ">>> ldap_back_getconn(insert)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+       
+               if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+                       if ( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num < li->li_conn_priv_max ) {
+                               LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
+                               li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num++;
+                               LDAP_BACK_CONN_CACHED_SET( lc );
+
+                       } else {
+                               LDAP_BACK_CONN_TAINTED_SET( lc );
+                       }
+                       rs->sr_err = 0;
+
+               } else {
+                       rs->sr_err = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
+                               ldap_back_conndn_cmp, ldap_back_conndn_dup );
+                       LDAP_BACK_CONN_CACHED_SET( lc );
+               }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+               ldap_back_print_conntree( li, "<<< ldap_back_getconn(insert)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
        
                ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
 
-               Debug( LDAP_DEBUG_TRACE,
-                       "=>ldap_back_getconn: conn %p inserted refcnt=%u binding=%u\n",
-                       (void *)lc, refcnt, binding );
+               if ( StatslogTest( LDAP_DEBUG_TRACE ) ) {
+                       char    buf[ SLAP_TEXT_BUFLEN ];
+
+                       snprintf( buf, sizeof( buf ),
+                               "lc=%p inserted refcnt=%u binding=%u rc=%d",
+                               (void *)lc, refcnt, binding, rs->sr_err );
+                               
+                       Debug( LDAP_DEBUG_TRACE,
+                               "=>ldap_back_getconn: %s: %s\n",
+                               op->o_log_prefix, buf, 0 );
+               }
        
-               /* Err could be -1 in case a duplicate ldapconn is inserted */
-               switch ( rs->sr_err ) {
-               case 0:
-                       break;
+               if ( !LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+                       /* Err could be -1 in case a duplicate ldapconn is inserted */
+                       switch ( rs->sr_err ) {
+                       case 0:
+                               break;
 
-               case -1:
-                       if ( !( sendok & LDAP_BACK_BINDING ) ) {
-                               /* duplicate: free and try to get the newly created one */
-                               goto retry_lock;
-                       }
-                       /* taint connection, so that it'll be freed when released */
-                       ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
-                       (void *)avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
-                                       ldap_back_conndnlc_cmp );
-                       ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
-                       LDAP_BACK_CONN_TAINTED_SET( lc );
-                       break;
+                       case -1:
+                               LDAP_BACK_CONN_CACHED_CLEAR( lc );
+                               if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( li ) ) {
+                                       /* duplicate: free and try to get the newly created one */
+                                       ldap_back_conn_free( lc );
+                                       lc = NULL;
+                                       goto retry_lock;
+                               }
 
-               default:
-                       ldap_back_conn_free( lc );
-                       rs->sr_err = LDAP_OTHER;
-                       rs->sr_text = "proxy bind collision";
-                       if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
-                               send_ldap_result( op, rs );
-                               rs->sr_text = NULL;
+                               /* taint connection, so that it'll be freed when released */
+                               LDAP_BACK_CONN_TAINTED_SET( lc );
+                               break;
+
+                       default:
+                               LDAP_BACK_CONN_CACHED_CLEAR( lc );
+                               ldap_back_conn_free( lc );
+                               rs->sr_err = LDAP_OTHER;
+                               rs->sr_text = "proxy bind collision";
+                               if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+                                       send_ldap_result( op, rs );
+                                       rs->sr_text = NULL;
+                               }
+                               return NULL;
                        }
-                       return NULL;
                }
 
        } else {
-               char    buf[ SLAP_TEXT_BUFLEN ];
                int     expiring = 0;
 
                if ( ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout )
@@ -711,28 +1064,53 @@ retry_lock:
                        /* let it be used, but taint/delete it so that 
                         * no-one else can look it up any further */
                        ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
-                       (void *)avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+                       ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+                       if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+                               if ( lc->lc_q.tqe_prev != NULL ) {
+                                       assert( LDAP_BACK_CONN_CACHED( lc ) );
+                                       assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 );
+                                       LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+                                               lc, lc_q );
+                                       li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--;
+                                       lc->lc_q.tqe_prev = NULL;
+                                       lc->lc_q.tqe_next = NULL;
+
+                               } else {
+                                       assert( !LDAP_BACK_CONN_CACHED( lc ) );
+                               }
+
+                       } else {
+                               (void)avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
                                        ldap_back_conndnlc_cmp );
-                       ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+                       }
                        LDAP_BACK_CONN_TAINTED_SET( lc );
+                       LDAP_BACK_CONN_CACHED_CLEAR( lc );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+                       ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+                       ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
                }
 
-               {
+               if ( StatslogTest( LDAP_DEBUG_TRACE ) ) {
+                       char    buf[ SLAP_TEXT_BUFLEN ];
+
                        snprintf( buf, sizeof( buf ),
                                "conn %p fetched refcnt=%u binding=%u%s",
                                (void *)lc, refcnt, binding, expiring ? " expiring" : "" );
                        Debug( LDAP_DEBUG_TRACE,
                                "=>ldap_back_getconn: %s.\n", buf, 0, 0 );
                }
-       
        }
 
 #ifdef HAVE_TLS
 done:;
 #endif /* HAVE_TLS */
-       if ( li->li_idle_timeout && lc ) {
-               lc->lc_time = op->o_time;
-       }
 
        return lc;
 }
@@ -741,11 +1119,13 @@ void
 ldap_back_release_conn_lock(
        Operation               *op,
        SlapReply               *rs,
-       ldapconn_t              *lc,
+       ldapconn_t              **lcp,
        int                     dolock )
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
 
+       ldapconn_t      *lc = *lcp;
+
        if ( dolock ) {
                ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
        }
@@ -754,24 +1134,92 @@ ldap_back_release_conn_lock(
        lc->lc_refcnt--;
        if ( LDAP_BACK_CONN_TAINTED( lc ) ) {
                ldap_back_freeconn( op, lc, 0 );
+               *lcp = NULL;
        }
        if ( dolock ) {
                ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
        }
 }
 
+void
+ldap_back_quarantine(
+       Operation       *op,
+       SlapReply       *rs )
+{
+       ldapinfo_t              *li = (ldapinfo_t *)op->o_bd->be_private;
+
+       slap_retry_info_t       *ri = &li->li_quarantine;
+
+       ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
+
+       if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+               time_t          new_last = slap_get_time();
+
+               switch ( li->li_isquarantined ) {
+               case LDAP_BACK_FQ_NO:
+                       if ( ri->ri_last == new_last ) {
+                               goto done;
+                       }
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: ldap_back_quarantine enter.\n",
+                               op->o_log_prefix, 0, 0 );
+
+                       ri->ri_idx = 0;
+                       ri->ri_count = 0;
+                       break;
+
+               case LDAP_BACK_FQ_RETRYING:
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: ldap_back_quarantine block #%d try #%d failed.\n",
+                               op->o_log_prefix, ri->ri_idx, ri->ri_count );
+
+                       ++ri->ri_count;
+                       if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
+                               && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
+                       {
+                               ri->ri_count = 0;
+                               ++ri->ri_idx;
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+
+               li->li_isquarantined = LDAP_BACK_FQ_YES;
+               ri->ri_last = new_last;
+
+       } else if ( li->li_isquarantined != LDAP_BACK_FQ_NO ) {
+               if ( ri->ri_last == slap_get_time() ) {
+                       goto done;
+               }
+
+               Debug( LDAP_DEBUG_ANY,
+                       "%s: ldap_back_quarantine exit (%d) err=%d.\n",
+                       op->o_log_prefix, li->li_isquarantined, rs->sr_err );
+
+               if ( li->li_quarantine_f ) {
+                       (void)li->li_quarantine_f( li, li->li_quarantine_p );
+               }
+
+               ri->ri_count = 0;
+               ri->ri_idx = 0;
+               li->li_isquarantined = LDAP_BACK_FQ_NO;
+       }
+
+done:;
+       ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
+}
+
 /*
- * ldap_back_dobind
- *
- * Note: as the check for the value of lc->lc_bound was already here, I removed
- * it from all the callers, and I made the function return the flag, so
- * it can be used to simplify the check.
+ * ldap_back_dobind_int
  *
  * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
  */
 static int
 ldap_back_dobind_int(
-       ldapconn_t              *lc,
+       ldapconn_t              **lcp,
        Operation               *op,
        SlapReply               *rs,
        ldap_back_send_t        sendok,
@@ -780,11 +1228,33 @@ ldap_back_dobind_int(
 {      
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
 
-       int             rc, binding = 0;
+       ldapconn_t      *lc;
+       struct berval   binddn = slap_empty_bv,
+                       bindcred = slap_empty_bv;
+
+       int             rc = 0,
+                       isbound,
+                       binding = 0;
        ber_int_t       msgid;
 
+       assert( lcp != NULL );
        assert( retries >= 0 );
 
+       if ( sendok & LDAP_BACK_GETCONN ) {
+               assert( *lcp == NULL );
+
+               lc = ldap_back_getconn( op, rs, sendok, &binddn, &bindcred );
+               if ( lc == NULL ) {
+                       return 0;
+               }
+               *lcp = lc;
+
+       } else {
+               lc = *lcp;
+       }
+
+       assert( lc != NULL );
+
 retry_lock:;
        if ( dolock ) {
                ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
@@ -792,8 +1262,8 @@ retry_lock:;
 
        if ( binding == 0 ) {
                /* check if already bound */
-               rc = LDAP_BACK_CONN_ISBOUND( lc );
-               if ( rc ) {
+               rc = isbound = LDAP_BACK_CONN_ISBOUND( lc );
+               if ( isbound ) {
                        lc->lc_binding--;
                        if ( dolock ) {
                                ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
@@ -830,24 +1300,6 @@ retry_lock:;
                ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
        }
 
-#if 0
-       while ( lc->lc_refcnt > 1 ) {
-               ldap_pvt_thread_yield();
-               rc = LDAP_BACK_CONN_ISBOUND( lc );
-               if ( rc ) {
-                       return rc;
-               }
-       }
-
-       if ( dolock ) {
-               ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
-       }
-       LDAP_BACK_CONN_BINDING_SET( lc );
-       if ( dolock ) {
-               ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
-       }
-#endif
-
        /*
         * FIXME: we need to let clients use proxyAuthz
         * otherwise we cannot do symmetric pools of servers;
@@ -870,12 +1322,14 @@ retry_lock:;
         * but the "override" flag is given to idassert.
         * It allows to use SASL bind and yet proxyAuthz users
         */
-       if ( op->o_conn != NULL &&
-                       !op->o_do_not_cache &&
-                       ( BER_BVISNULL( &lc->lc_bound_ndn ) ||
-                         ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
-       {
-               (void)ldap_back_proxy_authz_bind( lc, op, rs, sendok );
+       if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) {
+               if ( BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &bindcred ) ) {
+                       /* if we got here, it shouldn't return result */
+                       rc = ldap_back_is_proxy_authz( op, rs,
+                               LDAP_BACK_DONTSEND, &binddn, &bindcred );
+                       assert( rc == 1 );
+               }
+               rc = ldap_back_proxy_authz_bind( lc, op, rs, sendok, &binddn, &bindcred );
                goto done;
        }
 
@@ -887,12 +1341,12 @@ retry_lock:;
 
                if ( li->li_acl_secprops != NULL ) {
                        rc = ldap_set_option( lc->lc_ld,
-                               LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops);
+                               LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops );
 
                        if ( rc != LDAP_OPT_SUCCESS ) {
                                Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
-                                       "(%s,SECPROPS,\"%s\") failed!\n",
-                                       li->li_uri, li->li_acl_secprops, 0 );
+                                       "(SECPROPS,\"%s\") failed!\n",
+                                       li->li_acl_secprops, 0, 0 );
                                goto done;
                        }
                }
@@ -915,18 +1369,25 @@ retry_lock:;
                rs->sr_err = slap_map_api2result( rs );
                if ( rs->sr_err != LDAP_SUCCESS ) {
                        LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
-                       send_ldap_result( op, rs );
+                       if ( sendok & LDAP_BACK_SENDERR ) {
+                               send_ldap_result( op, rs );
+                       }
 
                } else {
                        LDAP_BACK_CONN_ISBOUND_SET( lc );
                }
+
+               if ( LDAP_BACK_QUARANTINE( li ) ) {
+                       ldap_back_quarantine( op, rs );
+               }
+
                goto done;
        }
 #endif /* HAVE_CYRUS_SASL */
 
 retry:;
        rs->sr_err = ldap_sasl_bind( lc->lc_ld,
-                       lc->lc_bound_ndn.bv_val,
+                       BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val,
                        LDAP_SASL_SIMPLE, &lc->lc_cred,
                        NULL, NULL, &msgid );
 
@@ -970,13 +1431,29 @@ retry:;
                        }
                }
 
+               /* FIXME: one binding-- too many? */
+               lc->lc_binding--;
+               assert( lc->lc_refcnt == 1 );
+               lc->lc_refcnt = 0;
                ldap_back_freeconn( op, lc, dolock );
+               *lcp = NULL;
                rs->sr_err = slap_map_api2result( rs );
 
+               if ( LDAP_BACK_QUARANTINE( li ) ) {
+                       ldap_back_quarantine( op, rs );
+               }
+
+               if ( rs->sr_err != LDAP_SUCCESS &&
+                       ( sendok & LDAP_BACK_SENDERR ) )
+               {
+                       send_ldap_result( op, rs );
+               }
+
                return 0;
        }
 
-       rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
+       rc = ldap_back_op_result( lc, op, rs, msgid,
+               -1, (sendok|LDAP_BACK_BINDING) );
        if ( rc == LDAP_SUCCESS ) {
                LDAP_BACK_CONN_ISBOUND_SET( lc );
        }
@@ -986,18 +1463,27 @@ done:;
        LDAP_BACK_CONN_BINDING_CLEAR( lc );
        rc = LDAP_BACK_CONN_ISBOUND( lc );
        if ( !rc ) {
-               ldap_back_release_conn_lock( op, rs, lc, dolock );
+               ldap_back_release_conn_lock( op, rs, lcp, dolock );
+
+       } else if ( LDAP_BACK_SAVECRED( li ) ) {
+               ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
        }
 
        return rc;
 }
 
+/*
+ * ldap_back_dobind
+ *
+ * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
+ */
 int
-ldap_back_dobind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+ldap_back_dobind( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
 
-       return ldap_back_dobind_int( lc, op, rs, sendok, li->li_nretries, 1 );
+       return ldap_back_dobind_int( lcp, op, rs,
+               ( sendok | LDAP_BACK_GETCONN ), li->li_nretries, 1 );
 }
 
 /*
@@ -1006,7 +1492,7 @@ ldap_back_dobind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t
  * This is a callback used for chasing referrals using the same
  * credentials as the original user on this session.
  */
-static int 
+int 
 ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
        ber_int_t msgid, void *params )
 {
@@ -1031,10 +1517,36 @@ ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
 
        /* FIXME: add checks on the URL/identity? */
 
-       return ldap_sasl_bind_s( ld, lc->lc_bound_ndn.bv_val,
+       return ldap_sasl_bind_s( ld,
+                       BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val,
                        LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
 }
 
+int
+ldap_back_cancel(
+               ldapconn_t              *lc,
+               Operation               *op,
+               SlapReply               *rs,
+               ber_int_t               msgid,
+               ldap_back_send_t        sendok )
+{
+       ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
+
+       /* default behavior */
+       if ( LDAP_BACK_ABANDON( li ) ) {
+               return ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
+       }
+
+       if ( LDAP_BACK_CANCEL( li ) ) {
+               /* FIXME: asynchronous? */
+               return ldap_cancel_s( lc->lc_ld, msgid, NULL, NULL );
+       }
+
+       assert( 0 );
+
+       return LDAP_OTHER;
+}
+
 int
 ldap_back_op_result(
                ldapconn_t              *lc,
@@ -1044,14 +1556,19 @@ ldap_back_op_result(
                time_t                  timeout,
                ldap_back_send_t        sendok )
 {
+       ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
+
        char            *match = NULL;
-       LDAPMessage     *res = NULL;
        char            *text = NULL;
+       char            **refs = NULL;
+       LDAPControl     **ctrls = NULL;
 
 #define        ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
 
        rs->sr_text = NULL;
        rs->sr_matched = NULL;
+       rs->sr_ref = NULL;
+       rs->sr_ctrls = NULL;
 
        /* if the error recorded in the reply corresponds
         * to a successful state, get the error from the
@@ -1059,28 +1576,68 @@ ldap_back_op_result(
        if ( 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 ) {
-                       tv.tv_sec = timeout;
-                       tv.tv_usec = 0;
+               /* 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 );
 
-               } else {
-                       LDAP_BACK_TV_SET( &tv );
+                       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 = li->li_timeout[ opidx ];
+                       }
+               }
+
+               /* better than nothing :) */
+               if ( timeout == 0 ) {
+                       if ( li->li_idle_timeout ) {
+                               timeout = li->li_idle_timeout;
+
+                       } else if ( li->li_conn_ttl ) {
+                               timeout = li->li_conn_ttl;
+                       }
+               }
+
+               if ( timeout ) {
+                       stoptime = op->o_time + timeout;
                }
 
+               LDAP_BACK_TV_SET( &tv );
+
 retry:;
                /* if result parsing fails, note the failure reason */
                rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
                switch ( rc ) {
                case 0:
-                       if ( timeout ) {
-                               (void)ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
-                               rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
-                                       LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR;
-                               rs->sr_text = "Operation timed out";
+                       if ( timeout && slap_get_time() > stoptime ) {
+                               if ( sendok & LDAP_BACK_BINDING ) {
+                                       ldap_unbind_ext( lc->lc_ld, NULL, NULL );
+                                       lc->lc_ld = NULL;
+                                       LDAP_BACK_CONN_TAINTED_SET( lc );
+
+                               } else {
+                                       (void)ldap_back_cancel( lc, op, rs, msgid, sendok );
+                               }
+                               rs->sr_err = timeout_err;
+                               rs->sr_text = timeout_text;
                                break;
                        }
 
+                       /* timeout == 0 */
                        LDAP_BACK_TV_SET( &tv );
                        ldap_pvt_thread_yield();
                        goto retry;
@@ -1096,22 +1653,32 @@ retry:;
                 * structure (this includes 
                 * LDAP_COMPARE_{TRUE|FALSE}) */
                default:
-                       rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
-                                       &match, &text, NULL, NULL, 1 );
-#ifndef LDAP_NULL_IS_NULL
-                       if ( match != NULL && match[ 0 ] == '\0' ) {
-                               ldap_memfree( match );
-                               match = NULL;
-                       }
-                       if ( text != NULL && text[ 0 ] == '\0' ) {
-                               ldap_memfree( text );
-                               text = NULL;
+                       /* only touch when activity actually took place... */
+                       if ( li->li_idle_timeout && lc ) {
+                               lc->lc_time = op->o_time;
                        }
-#endif /* LDAP_NULL_IS_NULL */
+
+                       rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+                                       &match, &text, &refs, &ctrls, 1 );
                        rs->sr_text = text;
                        if ( rc != LDAP_SUCCESS ) {
                                rs->sr_err = rc;
                        }
+                       if ( refs != NULL ) {
+                               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 ] );
+                       }
+                       if ( ctrls != NULL ) {
+                               rs->sr_ctrls = ctrls;
+                       }
                }
        }
 
@@ -1130,12 +1697,24 @@ retry:;
                        rs->sr_matched = match;
                }
        }
-       if ( op->o_conn &&
-                       ( ( sendok & LDAP_BACK_SENDOK ) 
-                         || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
+
+       if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+               if ( !( sendok & LDAP_BACK_RETRYING ) ) {
+                       if ( LDAP_BACK_QUARANTINE( li ) ) {
+                               ldap_back_quarantine( op, rs );
+                       }
+                       if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+                               send_ldap_result( op, rs );
+                       }
+               }
+
+       } else if ( op->o_conn &&
+               ( ( ( sendok & LDAP_BACK_SENDOK ) && ERR_OK( rs->sr_err ) )
+                       || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
        {
                send_ldap_result( op, rs );
        }
+
        if ( match ) {
                if ( rs->sr_matched != match ) {
                        free( (char *)rs->sr_matched );
@@ -1143,10 +1722,25 @@ retry:;
                rs->sr_matched = NULL;
                ldap_memfree( match );
        }
+
        if ( text ) {
                ldap_memfree( text );
        }
        rs->sr_text = NULL;
+
+       if ( rs->sr_ref ) {
+               assert( refs != NULL );
+               ber_memvfree( (void **)refs );
+               op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+               rs->sr_ref = NULL;
+       }
+
+       if ( ctrls ) {
+               assert( rs->sr_ctrls != NULL );
+               ldap_controls_free( ctrls );
+               rs->sr_ctrls = NULL;
+       }
+
        return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
 }
 
@@ -1154,8 +1748,9 @@ retry:;
 int
 ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 {
-       int             rc = 0;
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
+       int             rc = 0,
+                       binding;
 
        assert( lcp != NULL );
        assert( *lcp != NULL );
@@ -1163,11 +1758,15 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_
        ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
 
        if ( (*lcp)->lc_refcnt == 1 ) {
+               binding = LDAP_BACK_CONN_BINDING( *lcp );
+
+               ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
                Debug( LDAP_DEBUG_ANY,
                        "%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
                        op->o_log_prefix, li->li_uri,
                        BER_BVISNULL( &(*lcp)->lc_bound_ndn ) ?
                                "" : (*lcp)->lc_bound_ndn.bv_val );
+               ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
 
                ldap_unbind_ext( (*lcp)->lc_ld, NULL, NULL );
                (*lcp)->lc_ld = NULL;
@@ -1176,12 +1775,25 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_
                /* lc here must be the regular lc, reset and ready for init */
                rc = ldap_back_prepare_conn( lcp, op, rs, sendok );
                if ( rc != LDAP_SUCCESS ) {
-                       rc = 0;
+                       /* freeit, because lc_refcnt == 1 */
+                       (*lcp)->lc_refcnt = 0;
+                       (void)ldap_back_freeconn( op, *lcp, 0 );
                        *lcp = NULL;
+                       rc = 0;
+
+               } else if ( ( sendok & LDAP_BACK_BINDING ) ) {
+                       if ( binding ) {
+                               LDAP_BACK_CONN_BINDING_SET( *lcp );
+                       }
+                       rc = 1;
 
                } else {
-                       rc = ldap_back_dobind_int( *lcp, op, rs, sendok, 0, 0 );
-                       if ( rc == 0 ) {
+                       rc = ldap_back_dobind_int( lcp, op, rs, sendok, 0, 0 );
+                       if ( rc == 0 && *lcp != NULL ) {
+                               /* freeit, because lc_refcnt == 1 */
+                               (*lcp)->lc_refcnt = 0;
+                               LDAP_BACK_CONN_TAINTED_SET( *lcp );
+                               (void)ldap_back_freeconn( op, *lcp, 0 );
                                *lcp = NULL;
                        }
                }
@@ -1191,8 +1803,9 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_
                        "ldap_back_retry: conn %p refcnt=%u unable to retry.\n",
                        (void *)(*lcp), (*lcp)->lc_refcnt, 0 );
 
-               ldap_back_release_conn_lock( op, rs, *lcp, 0 );
-               *lcp = NULL;
+               LDAP_BACK_CONN_TAINTED_SET( *lcp );
+               ldap_back_release_conn_lock( op, rs, lcp, 0 );
+               assert( *lcp == NULL );
 
                if ( sendok ) {
                        rs->sr_err = LDAP_UNAVAILABLE;
@@ -1207,15 +1820,40 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_
 }
 
 static int
-ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+       struct berval *binddn, struct berval *bindcred )
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
-       struct berval   binddn = slap_empty_bv;
-       struct berval   bindcred = slap_empty_bv;
        struct berval   ndn;
        int             dobind = 0;
-       int             msgid;
-       int             rc;
+
+       if ( op->o_conn == NULL || op->o_do_not_cache ) {
+               goto done;
+       }
+
+       /* don't proxyAuthz if protocol is not LDAPv3 */
+       switch ( li->li_version ) {
+       case LDAP_VERSION3:
+               break;
+
+       case 0:
+               if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+                       break;
+               }
+               /* fall thru */
+
+       default:
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               if ( sendok & LDAP_BACK_SENDERR ) {
+                       send_ldap_result( op, rs );
+                       dobind = -1;
+               }
+               goto done;
+       }
+
+       /* safe default */
+       *binddn = slap_empty_bv;
+       *bindcred = slap_empty_bv;
 
        if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
                ndn = op->o_conn->c_ndn;
@@ -1224,36 +1862,13 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b
                ndn = op->o_ndn;
        }
 
-       /*
-        * FIXME: we need to let clients use proxyAuthz
-        * otherwise we cannot do symmetric pools of servers;
-        * we have to live with the fact that a user can
-        * authorize itself as any ID that is allowed
-        * by the authzTo directive of the "proxyauthzdn".
-        */
-       /*
-        * NOTE: current Proxy Authorization specification
-        * and implementation do not allow proxy authorization
-        * control to be provided with Bind requests
-        */
-       /*
-        * if no bind took place yet, but the connection is bound
-        * and the "proxyauthzdn" is set, then bind as 
-        * "proxyauthzdn" and explicitly add the proxyAuthz 
-        * control to every operation with the dn bound 
-        * to the connection as control value.
-        */
-
-       /* bind as proxyauthzdn only if no idassert mode
-        * is requested, or if the client's identity
-        * is authorized */
        switch ( li->li_idassert_mode ) {
        case LDAP_BACK_IDASSERT_LEGACY:
                if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
                        if ( !BER_BVISNULL( &li->li_idassert_authcDN ) && !BER_BVISEMPTY( &li->li_idassert_authcDN ) )
                        {
-                               binddn = li->li_idassert_authcDN;
-                               bindcred = li->li_idassert_passwd;
+                               *binddn = li->li_idassert_authcDN;
+                               *bindcred = li->li_idassert_passwd;
                                dobind = 1;
                        }
                }
@@ -1266,13 +1881,13 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b
                                rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
                                if ( sendok & LDAP_BACK_SENDERR ) {
                                        send_ldap_result( op, rs );
+                                       dobind = -1;
                                }
-                               LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
 
                        } else {
                                rs->sr_err = LDAP_SUCCESS;
-                               binddn = slap_empty_bv;
-                               bindcred = slap_empty_bv;
+                               *binddn = slap_empty_bv;
+                               *bindcred = slap_empty_bv;
                                break;
                        }
 
@@ -1293,13 +1908,13 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b
                                if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
                                        if ( sendok & LDAP_BACK_SENDERR ) {
                                                send_ldap_result( op, rs );
+                                               dobind = -1;
                                        }
-                                       LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
 
                                } else {
                                        rs->sr_err = LDAP_SUCCESS;
-                                       binddn = slap_empty_bv;
-                                       bindcred = slap_empty_bv;
+                                       *binddn = slap_empty_bv;
+                                       *bindcred = slap_empty_bv;
                                        break;
                                }
 
@@ -1307,13 +1922,38 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b
                        }
                }
 
-               binddn = li->li_idassert_authcDN;
-               bindcred = li->li_idassert_passwd;
+               *binddn = li->li_idassert_authcDN;
+               *bindcred = li->li_idassert_passwd;
                dobind = 1;
                break;
        }
 
-       if ( dobind && li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
+done:;
+       return dobind;
+}
+
+static int
+ldap_back_proxy_authz_bind(
+       ldapconn_t              *lc,
+       Operation               *op,
+       SlapReply               *rs,
+       ldap_back_send_t        sendok,
+       struct berval           *binddn,
+       struct berval           *bindcred )
+{
+       ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
+       struct berval   ndn;
+       int             msgid;
+       int             rc;
+
+       if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+               ndn = op->o_conn->c_ndn;
+
+       } else {
+               ndn = op->o_ndn;
+       }
+
+       if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
 #ifdef HAVE_CYRUS_SASL
                void            *defaults = NULL;
                struct berval   authzID = BER_BVNULL;
@@ -1374,7 +2014,7 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b
                                li->li_idassert_passwd.bv_val,
                                authzID.bv_val );
 
-               rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn.bv_val,
+               rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn->bv_val,
                                li->li_idassert_sasl_mech.bv_val, NULL, NULL,
                                LDAP_SASL_QUIET, lutil_sasl_interact,
                                defaults );
@@ -1401,13 +2041,17 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b
 
        switch ( li->li_idassert_authmethod ) {
        case LDAP_AUTH_NONE:
-               LDAP_BACK_CONN_ISBOUND_SET( lc );
-               goto done;
+               /* FIXME: do we really need this? */
+               BER_BVSTR( binddn, "" );
+               BER_BVSTR( bindcred, "" );
+               /* fallthru */
 
        case LDAP_AUTH_SIMPLE:
                rs->sr_err = ldap_sasl_bind( lc->lc_ld,
-                               binddn.bv_val, LDAP_SASL_SIMPLE,
-                               &bindcred, NULL, NULL, &msgid );
+                               binddn->bv_val, LDAP_SASL_SIMPLE,
+                               bindcred, NULL, NULL, &msgid );
+               rc = ldap_back_op_result( lc, op, rs, msgid,
+                       -1, (sendok|LDAP_BACK_BINDING) );
                break;
 
        default:
@@ -1420,9 +2064,25 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_b
                goto done;
        }
 
-       rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
        if ( rc == LDAP_SUCCESS ) {
+               /* set rebind stuff in case of successful proxyAuthz bind,
+                * so that referral chasing is attempted using the right
+                * identity */
                LDAP_BACK_CONN_ISBOUND_SET( lc );
+               ber_bvreplace( &lc->lc_bound_ndn, binddn );
+
+               if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+                       memset( lc->lc_cred.bv_val, 0,
+                                       lc->lc_cred.bv_len );
+               }
+
+               if ( LDAP_BACK_SAVECRED( li ) ) {
+                       ber_bvreplace( &lc->lc_cred, bindcred );
+                       ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
+
+               } else {
+                       lc->lc_cred.bv_len = 0;
+               }
        }
 done:;
        return LDAP_BACK_CONN_ISBOUND( lc );
@@ -1456,27 +2116,43 @@ done:;
  */
 int
 ldap_back_proxy_authz_ctrl(
-               ldapconn_t      *lc,
+               struct berval   *bound_ndn,
+               int             version,
+               slap_idassert_t *si,
                Operation       *op,
                SlapReply       *rs,
                LDAPControl     ***pctrls )
 {
-       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
-       LDAPControl     **ctrls = NULL;
-       int             i = 0,
-                       mode;
-       struct berval   assertedID,
-                       ndn;
+       LDAPControl             **ctrls = NULL;
+       int                     i = 0;
+       slap_idassert_mode_t    mode;
+       struct berval           assertedID,
+                               ndn;
 
        *pctrls = NULL;
 
        rs->sr_err = LDAP_SUCCESS;
 
+       /* don't proxyAuthz if protocol is not LDAPv3 */
+       switch ( version ) {
+       case LDAP_VERSION3:
+               break;
+
+       case 0:
+               if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+                       break;
+               }
+               /* fall thru */
+
+       default:
+               goto done;
+       }
+
        /* 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( &li->li_idassert_authcID ) || BER_BVISEMPTY( &li->li_idassert_authcID ) )
-                       && ( BER_BVISNULL( &li->li_idassert_authcDN ) || BER_BVISEMPTY( &li->li_idassert_authcDN ) ) )
+       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 ) ) )
        {
                goto done;
        }
@@ -1495,7 +2171,7 @@ ldap_back_proxy_authz_ctrl(
                ndn = op->o_ndn;
        }
 
-       if ( li->li_idassert_mode == LDAP_BACK_IDASSERT_LEGACY ) {
+       if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) {
                if ( op->o_proxy_authz ) {
                        /*
                         * FIXME: we do not want to perform proxyAuthz
@@ -1514,7 +2190,7 @@ ldap_back_proxy_authz_ctrl(
                        goto done;
                }
 
-               if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
+               if ( !BER_BVISNULL( bound_ndn ) ) {
                        goto done;
                }
 
@@ -1522,23 +2198,18 @@ ldap_back_proxy_authz_ctrl(
                        goto done;
                }
 
-               if ( BER_BVISNULL( &li->li_idassert_authcDN ) ) {
+               if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) {
                        goto done;
                }
 
-       } else if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
-               if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ )
-                               /* && ( !BER_BVISNULL( &ndn )
-                                       || LDAP_BACK_CONN_ISBOUND( lc ) ) */ )
+       } 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 */
-                       /* NOTE: the test on lc->lc_bound is used to trap
-                        * native authorization of anonymous users,
-                        * since in that case ndn is NULL */
                        goto done;
                }
 
-       } else if ( li->li_idassert_authz && !be_isroot( op ) ) {
+       } else if ( si->si_authz && !be_isroot( op ) ) {
                int             rc;
                struct berval authcDN;
 
@@ -1547,11 +2218,10 @@ ldap_back_proxy_authz_ctrl(
                } else {
                        authcDN = ndn;
                }
-               rc = slap_sasl_matches( op, li->li_idassert_authz,
+               rc = slap_sasl_matches( op, si->si_authz,
                                &authcDN, & authcDN );
                if ( rc != LDAP_SUCCESS ) {
-                       if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE )
-                       {
+                       if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
                                /* ndn is not authorized
                                 * to use idassert */
                                rs->sr_err = rc;
@@ -1586,7 +2256,7 @@ ldap_back_proxy_authz_ctrl(
                mode = LDAP_BACK_IDASSERT_NOASSERT;
 
        } else {
-               mode = li->li_idassert_mode;
+               mode = si->si_mode;
        }
 
        switch ( mode ) {
@@ -1619,7 +2289,7 @@ ldap_back_proxy_authz_ctrl(
        case LDAP_BACK_IDASSERT_OTHERID:
        case LDAP_BACK_IDASSERT_OTHERDN:
                /* assert idassert DN */
-               assertedID = li->li_idassert_authzID;
+               assertedID = si->si_bc.sb_authzId;
                break;
 
        default:
@@ -1631,7 +2301,7 @@ ldap_back_proxy_authz_ctrl(
        }
 
        /* don't idassert the bound DN (ITS#4497) */
-       if ( dn_match( &assertedID, &lc->lc_bound_ndn ) ) {
+       if ( dn_match( &assertedID, bound_ndn ) ) {
                goto done;
        }
 
@@ -1647,7 +2317,7 @@ ldap_back_proxy_authz_ctrl(
        ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
        ctrls[ 0 ]->ldctl_iscritical = 1;
 
-       switch ( li->li_idassert_mode ) {
+       switch ( si->si_mode ) {
        /* already in u:ID or dn:DN form */
        case LDAP_BACK_IDASSERT_OTHERID:
        case LDAP_BACK_IDASSERT_OTHERDN:
@@ -1665,6 +2335,88 @@ ldap_back_proxy_authz_ctrl(
                break;
        }
 
+       /* 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 = ctrls[ 0 ]->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, &ctrls[ 0 ]->ldctl_value, 1 ) == -1 ) {
+                       rs->sr_err = LDAP_OTHER;
+                       goto free_ber;
+               }
+
+free_ber:;
+               op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+               ber_free_buf( ber );
+
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       op->o_tmpfree( ctrls, op->o_tmpmemctx );
+                       ctrls = NULL;
+                       goto done;
+               }
+
+       } else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+               struct berval           authzID = ctrls[ 0 ]->ldctl_value,
+                                       tmp;
+               BerElementBuffer        berbuf;
+               BerElement              *ber = (BerElement *)&berbuf;
+               ber_tag_t               tag;
+
+               if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) {
+                       op->o_tmpfree( ctrls[ 0 ]->ldctl_value.bv_val, op->o_tmpmemctx );
+                       op->o_tmpfree( ctrls, op->o_tmpmemctx );
+                       ctrls = NULL;
+                       rs->sr_err = LDAP_PROTOCOL_ERROR;
+                       goto done;
+               }
+
+               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;
+               }
+
+               if ( ber_flatten2( ber, &ctrls[ 0 ]->ldctl_value, 1 ) == -1 ) {
+                       rs->sr_err = LDAP_OTHER;
+                       goto free_ber2;
+               }
+
+free_ber2:;
+               op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+               ber_free_buf( ber );
+
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       op->o_tmpfree( ctrls, op->o_tmpmemctx );
+                       ctrls = NULL;
+                       goto done;
+               }
+
+               ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
+       }
+
        if ( op->o_ctrls ) {
                for ( i = 0; op->o_ctrls[ i ]; i++ ) {
                        ctrls[ i + 1 ] = op->o_ctrls[ i ];
index 1ee58f1418abe4ddfefdabfbe67ca724d02e8678..cf3ffa33621e2e4e5459f7d15b8f5f3fdf14770e 100644 (file)
@@ -27,9 +27,9 @@
 #include <ac/string.h>
 #include <ac/socket.h>
 
+#include "lutil.h"
 #include "slap.h"
 #include "back-ldap.h"
-
 #include "config.h"
 
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
 static int             sc_chainingBehavior;
 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
 
-#define        LDAP_CH_NONE                    ((void *)(0))
-#define        LDAP_CH_RES                     ((void *)(1))
-#define LDAP_CH_ERR                    ((void *)(2))
-
+typedef enum {
+       LDAP_CH_NONE = 0,
+       LDAP_CH_RES,
+       LDAP_CH_ERR
+} ldap_chain_status_t;
 static BackendInfo     *lback;
 
 typedef struct ldap_chain_t {
@@ -87,13 +88,19 @@ typedef struct ldap_chain_t {
        /* tree of configured[/generated?] "uri" info */
        ldap_avl_info_t         lc_lai;
 
+       /* max depth in nested referrals chaining */
+       int                     lc_max_depth;
+
        unsigned                lc_flags;
 #define LDAP_CHAIN_F_NONE              (0x00U)
 #define        LDAP_CHAIN_F_CHAINING           (0x01U)
-#define        LDAP_CHAIN_F_CACHE_URI          (0x10U)
+#define        LDAP_CHAIN_F_CACHE_URI          (0x02U)
+#define        LDAP_CHAIN_F_RETURN_ERR         (0x04U)
 
-#define        LDAP_CHAIN_CHAINING( lc )       ( ( (lc)->lc_flags & LDAP_CHAIN_F_CHAINING ) == LDAP_CHAIN_F_CHAINING )
-#define        LDAP_CHAIN_CACHE_URI( lc )      ( ( (lc)->lc_flags & LDAP_CHAIN_F_CACHE_URI ) == LDAP_CHAIN_F_CACHE_URI )
+#define LDAP_CHAIN_ISSET(lc, f)                ( ( (lc)->lc_flags & (f) ) == (f) )
+#define        LDAP_CHAIN_CHAINING( lc )       LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
+#define        LDAP_CHAIN_CACHE_URI( lc )      LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
+#define        LDAP_CHAIN_RETURN_ERR( lc )     LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
 
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
        LDAPControl             lc_chaining_ctrl;
@@ -103,10 +110,34 @@ typedef struct ldap_chain_t {
 
 static int ldap_chain_db_init_common( BackendDB        *be );
 static int ldap_chain_db_init_one( BackendDB *be );
-#define        ldap_chain_db_open_one(be)      (lback)->bi_db_open( (be) )
+static int ldap_chain_db_open_one( BackendDB *be );
 #define        ldap_chain_db_close_one(be)     (0)
 #define        ldap_chain_db_destroy_one(be)   (lback)->bi_db_destroy( (be) )
 
+typedef struct ldap_chain_cb_t {
+       ldap_chain_status_t     lb_status;
+       ldap_chain_t            *lb_lc;
+       BI_op_func              *lb_op_f;
+       int                     lb_depth;
+} ldap_chain_cb_t;
+
+static int
+ldap_chain_op(
+       Operation       *op,
+       SlapReply       *rs,
+       BI_op_func      *op_f,
+       BerVarray       ref,
+       int             depth );
+
+static int
+ldap_chain_search(
+       Operation       *op,
+       SlapReply       *rs,
+       BerVarray       ref,
+       int             depth );
+
+static slap_overinst ldapchain;
+
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
 static int
 chaining_control_add(
@@ -225,10 +256,12 @@ ldap_chain_uri_dup( void *c1, void *c2 )
 static int
 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
 {
+       ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+
        assert( op->o_tag == LDAP_REQ_SEARCH );
 
        /* if in error, don't proceed any further */
-       if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
+       if ( lb->lb_status == LDAP_CH_ERR ) {
                return 0;
        }
 
@@ -260,12 +293,15 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
        } else if ( rs->sr_type == REP_SEARCHREF ) {
                /* if we get it here, it means the library was unable
                 * to chase the referral... */
+               if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
+                       rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
+               }
 
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
-               if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+               if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
                        switch ( get_continuationBehavior( op ) ) {
                        case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
-                               op->o_callback->sc_private = LDAP_CH_ERR;
+                               lb->lb_status = LDAP_CH_ERR;
                                return rs->sr_err = LDAP_X_CANNOT_CHAIN;
 
                        default:
@@ -276,8 +312,15 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
                return SLAP_CB_CONTINUE;
 
        } else if ( rs->sr_type == REP_RESULT ) {
+               if ( rs->sr_err == LDAP_REFERRAL
+                       && lb->lb_depth < lb->lb_lc->lc_max_depth
+                       && rs->sr_ref != NULL )
+               {
+                       rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
+               }
+
                /* back-ldap tried to send result */
-               op->o_callback->sc_private = LDAP_CH_RES;
+               lb->lb_status = LDAP_CH_RES;
        }
 
        return 0;
@@ -290,12 +333,15 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
 static int
 ldap_chain_cb_response( Operation *op, SlapReply *rs )
 {
+       ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+
        /* if in error, don't proceed any further */
-       if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
+       if ( lb->lb_status == LDAP_CH_ERR ) {
                return 0;
        }
 
        if ( rs->sr_type == REP_RESULT ) {
+retry:;
                switch ( rs->sr_err ) {
                case LDAP_COMPARE_TRUE:
                case LDAP_COMPARE_FALSE:
@@ -305,15 +351,20 @@ ldap_chain_cb_response( Operation *op, SlapReply *rs )
                        /* fallthru */
 
                case LDAP_SUCCESS:
-                       op->o_callback->sc_private = LDAP_CH_RES;
+                       lb->lb_status = LDAP_CH_RES;
                        break;
 
                case LDAP_REFERRAL:
+                       if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
+                               rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
+                               goto retry;
+                       }
+
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
                        if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
                                switch ( get_continuationBehavior( op ) ) {
                                case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
-                                       op->o_callback->sc_private = LDAP_CH_ERR;
+                                       lb->lb_status = LDAP_CH_ERR;
                                        return rs->sr_err = LDAP_X_CANNOT_CHAIN;
 
                                default:
@@ -341,15 +392,18 @@ ldap_chain_op(
        Operation       *op,
        SlapReply       *rs,
        BI_op_func      *op_f,
-       BerVarray       ref )
+       BerVarray       ref,
+       int             depth )
 {
        slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
+       ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
        ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
        ldapinfo_t      li = { 0 }, *lip = NULL;
        struct berval   bvuri[ 2 ] = { { 0 } };
 
        /* NOTE: returned if ref is empty... */
-       int             rc = LDAP_OTHER;
+       int             rc = LDAP_OTHER,
+                       first_rc;
 
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
        LDAPControl     **ctrls = NULL;
@@ -358,15 +412,20 @@ ldap_chain_op(
 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
 
        li.li_bvuri = bvuri;
+       first_rc = -1;
        for ( ; !BER_BVISNULL( ref ); ref++ ) {
-               LDAPURLDesc     *srv;
-               char            *save_dn;
+               LDAPURLDesc     *srv = NULL;
+               struct berval   save_req_dn = op->o_req_dn,
+                               save_req_ndn = op->o_req_ndn,
+                               dn,
+                               pdn = BER_BVNULL,
+                               ndn = BER_BVNULL;
                int             temporary = 0;
                        
                /* We're setting the URI of the first referral;
                 * what if there are more?
 
-Document: draft-ietf-ldapbis-protocol-27.txt
+Document: RFC 4511
 
 4.1.10. Referral 
    ...
@@ -388,22 +447,35 @@ Document: draft-ietf-ldapbis-protocol-27.txt
                        continue;
                }
 
-               /* remove DN essentially because later on 
-                * ldap_initialize() will parse the URL 
-                * as a comma-separated URL list */
-               save_dn = srv->lud_dn;
-               srv->lud_dn = "";
-               srv->lud_scope = LDAP_SCOPE_DEFAULT;
-               li.li_uri = ldap_url_desc2str( srv );
-               srv->lud_dn = save_dn;
+               /* normalize DN */
+               ber_str2bv( srv->lud_dn, 0, 0, &dn );
+               rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
+               if ( rc == LDAP_SUCCESS ) {
+                       /* remove DN essentially because later on 
+                        * ldap_initialize() will parse the URL 
+                        * as a comma-separated URL list */
+                       srv->lud_dn = "";
+                       srv->lud_scope = LDAP_SCOPE_DEFAULT;
+                       li.li_uri = ldap_url_desc2str( srv );
+                       srv->lud_dn = dn.bv_val;
+               }
                ldap_free_urldesc( srv );
 
-               if ( li.li_uri == NULL ) {
+               if ( rc != LDAP_SUCCESS ) {
                        /* try next */
                        rc = LDAP_OTHER;
                        continue;
                }
 
+               if ( li.li_uri == NULL ) {
+                       /* try next */
+                       rc = LDAP_OTHER;
+                       goto further_cleanup;
+               }
+
+               op->o_req_dn = pdn;
+               op->o_req_ndn = ndn;
+
                ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
 
                /* Searches for a ldapinfo in the avl tree */
@@ -425,6 +497,8 @@ Document: draft-ietf-ldapbis-protocol-27.txt
                        lip->li_bvuri = bvuri;
                        rc = ldap_chain_db_open_one( op->o_bd );
                        if ( rc != 0 ) {
+                               lip->li_uri = NULL;
+                               lip->li_bvuri = NULL;
                                (void)ldap_chain_db_destroy_one( op->o_bd );
                                goto cleanup;
                        }
@@ -446,8 +520,16 @@ Document: draft-ietf-ldapbis-protocol-27.txt
                        }
                }
 
+               lb->lb_op_f = op_f;
+               lb->lb_depth = depth + 1;
+
                rc = op_f( op, rs );
 
+               /* note the first error */
+               if ( first_rc == -1 ) {
+                       first_rc = rc;
+               }
+
 cleanup:;
                ldap_memfree( li.li_uri );
                li.li_uri = NULL;
@@ -458,6 +540,17 @@ cleanup:;
                        (void)ldap_chain_db_close_one( op->o_bd );
                        (void)ldap_chain_db_destroy_one( op->o_bd );
                }
+
+further_cleanup:;
+               if ( !BER_BVISNULL( &pdn ) ) {
+                       op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
+               }
+               op->o_req_dn = save_req_dn;
+
+               if ( !BER_BVISNULL( &ndn ) ) {
+                       op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+               }
+               op->o_req_ndn = save_req_ndn;
                
                if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
                        break;
@@ -468,6 +561,204 @@ cleanup:;
        (void)chaining_control_remove( op, &ctrls );
 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
 
+       if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
+               rc = first_rc;
+       }
+
+       return rc;
+}
+
+static int
+ldap_chain_search(
+       Operation       *op,
+       SlapReply       *rs,
+       BerVarray       ref,
+       int             depth )
+
+{
+       slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
+       ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+       ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
+       ldapinfo_t      li = { 0 }, *lip = NULL;
+       struct berval   bvuri[ 2 ] = { { 0 } };
+
+       struct berval   odn = op->o_req_dn,
+                       ondn = op->o_req_ndn;
+       slap_response   *save_response = op->o_callback->sc_response;
+
+       int             rc = LDAP_OTHER,
+                       first_rc = -1;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+       LDAPControl     **ctrls = NULL;
+       
+       (void)chaining_control_add( lc, op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+       rs->sr_type = REP_SEARCH;
+
+       op->o_callback->sc_response = ldap_chain_cb_search_response;
+
+       /* if we parse the URI then by no means 
+        * we can cache stuff or reuse connections, 
+        * because in back-ldap there's no caching
+        * based on the URI value, which is supposed
+        * to be set once for all (correct?) */
+       li.li_bvuri = bvuri;
+       for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
+               LDAPURLDesc     *srv;
+               struct berval   save_req_dn = op->o_req_dn,
+                               save_req_ndn = op->o_req_ndn,
+                               dn,
+                               pdn = BER_BVNULL,
+                               ndn = BER_BVNULL;
+               int             temporary = 0;
+
+               /* parse reference and use
+                * proto://[host][:port]/ only */
+               rc = ldap_url_parse_ext( ref[0].bv_val, &srv );
+               if ( rc != LDAP_URL_SUCCESS ) {
+                       /* try next */
+                       rs->sr_err = LDAP_OTHER;
+                       continue;
+               }
+
+               /* normalize DN */
+               ber_str2bv( srv->lud_dn, 0, 0, &dn );
+               rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
+               if ( rc == LDAP_SUCCESS ) {
+                       /* remove DN essentially because later on 
+                        * ldap_initialize() will parse the URL 
+                        * as a comma-separated URL list */
+                       srv->lud_dn = "";
+                       srv->lud_scope = LDAP_SCOPE_DEFAULT;
+                       li.li_uri = ldap_url_desc2str( srv );
+                       srv->lud_dn = dn.bv_val;
+               }
+               ldap_free_urldesc( srv );
+
+               if ( rc != LDAP_SUCCESS ) {
+                       /* try next */
+                       rc = LDAP_OTHER;
+                       continue;
+               }
+
+               if ( li.li_uri == NULL ) {
+                       /* try next */
+                       rc = LDAP_OTHER;
+                       goto further_cleanup;
+               }
+
+               op->o_req_dn = pdn;
+               op->o_req_ndn = ndn;
+
+               ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
+
+               /* Searches for a ldapinfo in the avl tree */
+               ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+               lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
+                       (caddr_t)&li, ldap_chain_uri_cmp );
+               ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+               if ( lip != NULL ) {
+                       op->o_bd->be_private = (void *)lip;
+
+               } else {
+                       /* if none is found, create a temporary... */
+                       rc = ldap_chain_db_init_one( op->o_bd );
+                       if ( rc != 0 ) {
+                               goto cleanup;
+                       }
+                       lip = (ldapinfo_t *)op->o_bd->be_private;
+                       lip->li_uri = li.li_uri;
+                       lip->li_bvuri = bvuri;
+                       rc = ldap_chain_db_open_one( op->o_bd );
+                       if ( rc != 0 ) {
+                               lip->li_uri = NULL;
+                               lip->li_bvuri = NULL;
+                               (void)ldap_chain_db_destroy_one( op->o_bd );
+                               goto cleanup;
+                       }
+
+                       if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
+                               ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+                               if ( avl_insert( &lc->lc_lai.lai_tree,
+                                       (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+                               {
+                                       /* someone just inserted another;
+                                        * don't bother, use this and then
+                                        * just free it */
+                                       temporary = 1;
+                               }
+                               ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+                       } else {
+                               temporary = 1;
+                       }
+               }
+
+               lb->lb_op_f = lback->bi_op_search;
+               lb->lb_depth = depth + 1;
+
+               /* FIXME: should we also copy filter and scope?
+                * according to RFC3296, no */
+               rc = lback->bi_op_search( op, rs );
+               if ( first_rc == -1 ) {
+                       first_rc = rc;
+               }
+
+cleanup:;
+               ldap_memfree( li.li_uri );
+               li.li_uri = NULL;
+
+               op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+               op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+
+               if ( temporary ) {
+                       lip->li_uri = NULL;
+                       lip->li_bvuri = NULL;
+                       (void)ldap_chain_db_close_one( op->o_bd );
+                       (void)ldap_chain_db_destroy_one( op->o_bd );
+               }
+               
+further_cleanup:;
+               if ( !BER_BVISNULL( &pdn ) ) {
+                       op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
+               }
+               op->o_req_dn = save_req_dn;
+
+               if ( !BER_BVISNULL( &ndn ) ) {
+                       op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+               }
+               op->o_req_ndn = save_req_ndn;
+               
+               if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
+                       break;
+               }
+
+               rc = rs->sr_err;
+       }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+       (void)chaining_control_remove( op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+       op->o_req_dn = odn;
+       op->o_req_ndn = ondn;
+       op->o_callback->sc_response = save_response;
+       rs->sr_type = REP_SEARCHREF;
+       rs->sr_entry = NULL;
+
+       if ( rc != LDAP_SUCCESS ) {
+               /* couldn't chase any of the referrals */
+               if ( first_rc != -1 ) {
+                       rc = first_rc;
+
+               } else {
+                       rc = SLAP_CB_CONTINUE;
+               }
+       }
+
        return rc;
 }
 
@@ -475,7 +766,9 @@ static int
 ldap_chain_response( Operation *op, SlapReply *rs )
 {
        slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
-       void            *private = op->o_bd->be_private;
+       ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
+       BackendDB       db, *bd = op->o_bd;
+       ldap_chain_cb_t lb = { 0 };
        slap_callback   *sc = op->o_callback,
                        sc2 = { 0 };
        int             rc = 0;
@@ -530,12 +823,17 @@ ldap_chain_response( Operation *op, SlapReply *rs )
         *   e) what ssf
         */
 
+       db = *op->o_bd;
+       op->o_bd = &db;
+
        matched = rs->sr_matched;
        rs->sr_matched = NULL;
        ref = rs->sr_ref;
        rs->sr_ref = NULL;
 
        /* we need this to know if back-ldap returned any result */
+       lb.lb_lc = lc;
+       sc2.sc_private = &lb;
        sc2.sc_response = ldap_chain_cb_response;
        op->o_callback = &sc2;
 
@@ -556,30 +854,30 @@ ldap_chain_response( Operation *op, SlapReply *rs )
                /* FIXME: can we really get a referral for binds? */
                op->o_req_ndn = slap_empty_bv;
                op->o_conn = NULL;
-               rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
+               rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
                op->o_req_ndn = rndn;
                op->o_conn = conn;
                }
                break;
 
        case LDAP_REQ_ADD:
-               rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
+               rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
                break;
 
        case LDAP_REQ_DELETE:
-               rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
+               rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
                break;
 
        case LDAP_REQ_MODRDN:
-               rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
+               rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
                break;
 
        case LDAP_REQ_MODIFY:
-               rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
+               rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
                break;
 
        case LDAP_REQ_COMPARE:
-               rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
+               rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
                if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
                        rc = LDAP_SUCCESS;
                }
@@ -587,150 +885,7 @@ ldap_chain_response( Operation *op, SlapReply *rs )
 
        case LDAP_REQ_SEARCH:
                if ( rs->sr_type == REP_SEARCHREF ) {
-                       ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
-                       ldapinfo_t      li = { 0 }, *lip = NULL;
-                       struct berval   bvuri[ 2 ] = { { 0 } };
-
-                       struct berval   *curr = ref,
-                                       odn = op->o_req_dn,
-                                       ondn = op->o_req_ndn;
-
-#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
-                       LDAPControl     **ctrls = NULL;
-       
-                       (void)chaining_control_add( lc, op, &ctrls );
-#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
-
-                       rs->sr_type = REP_SEARCH;
-
-                       sc2.sc_response = ldap_chain_cb_search_response;
-
-                       /* if we parse the URI then by no means 
-                        * we can cache stuff or reuse connections, 
-                        * because in back-ldap there's no caching
-                        * based on the URI value, which is supposed
-                        * to be set once for all (correct?) */
-                       li.li_bvuri = bvuri;
-                       for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
-                               LDAPURLDesc     *srv;
-                               char            *save_dn;
-                               int             temporary = 0;
-
-                               /* parse reference and use
-                                * proto://[host][:port]/ only */
-                               rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
-                               if ( rc != LDAP_URL_SUCCESS ) {
-                                       /* try next */
-                                       rs->sr_err = LDAP_OTHER;
-                                       continue;
-                               }
-
-                               /* remove DN essentially because later on 
-                                * ldap_initialize() will parse the URL 
-                                * as a comma-separated URL list */
-                               save_dn = srv->lud_dn;
-                               srv->lud_dn = "";
-                               srv->lud_scope = LDAP_SCOPE_DEFAULT;
-                               li.li_uri = ldap_url_desc2str( srv );
-                               if ( li.li_uri != NULL ) {
-                                       ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
-                                                       op->o_tmpmemctx );
-                                       ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
-                                                       op->o_tmpmemctx );
-                               }
-
-                               srv->lud_dn = save_dn;
-                               ldap_free_urldesc( srv );
-
-                               if ( li.li_uri == NULL ) {
-                                       /* try next */
-                                       rs->sr_err = LDAP_OTHER;
-                                       continue;
-                               }
-
-                               ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
-
-                               /* Searches for a ldapinfo in the avl tree */
-                               ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
-                               lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
-                                       (caddr_t)&li, ldap_chain_uri_cmp );
-                               ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
-
-                               if ( lip != NULL ) {
-                                       op->o_bd->be_private = (void *)lip;
-
-                               } else {
-                                       /* if none is found, create a temporary... */
-                                       rc = ldap_chain_db_init_one( op->o_bd );
-                                       if ( rc != 0 ) {
-                                               goto cleanup;
-                                       }
-                                       lip = (ldapinfo_t *)op->o_bd->be_private;
-                                       lip->li_uri = li.li_uri;
-                                       lip->li_bvuri = bvuri;
-                                       rc = ldap_chain_db_open_one( op->o_bd );
-                                       if ( rc != 0 ) {
-                                               (void)ldap_chain_db_destroy_one( op->o_bd );
-                                               goto cleanup;
-                                       }
-
-                                       if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
-                                               ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
-                                               if ( avl_insert( &lc->lc_lai.lai_tree,
-                                                       (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
-                                               {
-                                                       /* someone just inserted another;
-                                                        * don't bother, use this and then
-                                                        * just free it */
-                                                       temporary = 1;
-                                               }
-                                               ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
-               
-                                       } else {
-                                               temporary = 1;
-                                       }
-                               }
-
-                               /* FIXME: should we also copy filter and scope?
-                                * according to RFC3296, no */
-                               rc = lback->bi_op_search( op, rs );
-
-cleanup:;
-                               ldap_memfree( li.li_uri );
-                               li.li_uri = NULL;
-
-                               op->o_tmpfree( op->o_req_dn.bv_val,
-                                               op->o_tmpmemctx );
-                               op->o_tmpfree( op->o_req_ndn.bv_val,
-                                               op->o_tmpmemctx );
-
-                               if ( temporary ) {
-                                       lip->li_uri = NULL;
-                                       lip->li_bvuri = NULL;
-                                       (void)ldap_chain_db_close_one( op->o_bd );
-                                       (void)ldap_chain_db_destroy_one( op->o_bd );
-                               }
-               
-                               if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
-                                       break;
-                               }
-
-                               rc = rs->sr_err;
-                       }
-
-#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
-                       (void)chaining_control_remove( op, &ctrls );
-#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
-
-                       op->o_req_dn = odn;
-                       op->o_req_ndn = ondn;
-                       rs->sr_type = REP_SEARCHREF;
-                       rs->sr_entry = NULL;
-
-                       if ( rc != LDAP_SUCCESS ) {
-                               /* couldn't chase any of the referrals */
-                               rc = SLAP_CB_CONTINUE;
-                       }
+                       rc = ldap_chain_search( op, rs, ref, 0 );
                        
                } else {
                        /* we might get here before any database actually 
@@ -738,7 +893,7 @@ cleanup:;
                         * to check limits, to make sure safe defaults
                         * are in place */
                        if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
-                               rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
+                               rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
 
                        } else {
                                rc = SLAP_CB_CONTINUE;
@@ -747,7 +902,7 @@ cleanup:;
                break;
 
        case LDAP_REQ_EXTENDED:
-               rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
+               rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
                /* FIXME: ldap_back_extended() by design 
                 * doesn't send result; frontend is expected
                 * to send it... */
@@ -756,7 +911,7 @@ cleanup:;
                        send_ldap_extended( op, rs );
                        rc = LDAP_SUCCESS;
                }
-               sc2.sc_private = LDAP_CH_RES;
+               lb.lb_status = LDAP_CH_RES;
                break;
 
        default:
@@ -771,7 +926,7 @@ cleanup:;
        case LDAP_SUCCESS:
        case LDAP_REFERRAL:
                /* slapd-ldap sent response */
-               if ( !op->o_abandon && sc2.sc_private != LDAP_CH_RES ) {
+               if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
                        /* FIXME: should we send response? */
                        Debug( LDAP_DEBUG_ANY,
                                "%s: ldap_chain_response: "
@@ -782,7 +937,7 @@ cleanup:;
 
        default:
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
-               if ( sc2.sc_private == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
+               if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
                        goto cannot_chain;
                }
 
@@ -796,18 +951,24 @@ cannot_chain:;
 
                default:
 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
-                       rc = SLAP_CB_CONTINUE;
-                       rs->sr_err = sr_err;
-                       rs->sr_type = sr_type;
-                       rs->sr_matched = matched;
-                       rs->sr_ref = ref;
+                       if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
+                               rs->sr_err = rc;
+                               rs->sr_type = sr_type;
+
+                       } else {
+                               rc = SLAP_CB_CONTINUE;
+                               rs->sr_err = sr_err;
+                               rs->sr_type = sr_type;
+                               rs->sr_matched = matched;
+                               rs->sr_ref = ref;
+                       }
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
                        break;
                }
 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
        }
 
-       if ( sc2.sc_private == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
+       if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
                op->o_callback = NULL;
                rc = rs->sr_err = slap_map_api2result( rs );
                send_ldap_result( op, rs );
@@ -818,7 +979,7 @@ dont_chain:;
        rs->sr_type = sr_type;
        rs->sr_matched = matched;
        rs->sr_ref = ref;
-       op->o_bd->be_private = private;
+       op->o_bd = bd;
        op->o_callback = sc;
        op->o_ndn = ndn;
 
@@ -858,7 +1019,9 @@ str2chain( const char *s )
 
 enum {
        CH_CHAINING = 1,
-       CH_CACHE_URI = 2,
+       CH_CACHE_URI,
+       CH_MAX_DEPTH,
+       CH_RETURN_ERR,
 
        CH_LAST
 };
@@ -877,9 +1040,21 @@ static ConfigTable chaincfg[] = {
 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
        { "chain-cache-uri", "TRUE/FALSE",
                2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
-               "( OLcfgOvAt:3.2 NAME 'olcCacheURI' "
+               "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
                        "DESC 'Enables caching of URIs not present in configuration' "
                        "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+       { "chain-max-depth", "args",
+               2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
+               "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
+                       "DESC 'max referral depth' "
+                       "SYNTAX OMsInteger "
+                       "EQUALITY integerMatch "
+                       "SINGLE-VALUE )", NULL, NULL },
+       { "chain-return-error", "TRUE/FALSE",
+               2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
+               "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
+                       "DESC 'Errors are returned instead of the original referral' "
+                       "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
 };
 
@@ -892,7 +1067,9 @@ static ConfigOCs chainocs[] = {
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
                        "olcChainingBehavior $ "
 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
-                       "olcCacheURI "
+                       "olcChainCacheURI $ "
+                       "olcChainMaxReferralDepth $ "
+                       "olcChainReturnError "
                        ") )",
                Cft_Overlay, chaincfg, NULL, chain_cfadd },
        { "( OLcfgOvOc:3.2 "
@@ -976,14 +1153,18 @@ chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
        if ( lc->lc_common_li == NULL ) {
                lc->lc_common_li = li;
 
-       } else if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
-               ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
-       {
-               Debug( LDAP_DEBUG_ANY, "slapd-chain: "
-                       "database \"%s\" insert failed.\n",
-                       e->e_name.bv_val, 0, 0 );
-               rc = LDAP_CONSTRAINT_VIOLATION;
-               goto done;
+       } else {
+               li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
+               value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
+               if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
+                       ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+               {
+                       Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+                               "database \"%s\" insert failed.\n",
+                               e->e_name.bv_val, 0, 0 );
+                       rc = LDAP_CONSTRAINT_VIOLATION;
+                       goto done;
+               }
        }
 
 done:;
@@ -1110,6 +1291,14 @@ chain_cf_gen( ConfigArgs *c )
                        c->value_int = LDAP_CHAIN_CACHE_URI( lc );
                        break;
 
+               case CH_MAX_DEPTH:
+                       c->value_int = lc->lc_max_depth;
+                       break;
+
+               case CH_RETURN_ERR:
+                       c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
+                       break;
+
                default:
                        assert( 0 );
                        rc = 1;
@@ -1125,6 +1314,14 @@ chain_cf_gen( ConfigArgs *c )
                        lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
                        break;
 
+               case CH_MAX_DEPTH:
+                       c->value_int = 0;
+                       break;
+
+               case CH_RETURN_ERR:
+                       lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
+                       break;
+
                default:
                        return 1;
                }
@@ -1257,6 +1454,26 @@ chain_cf_gen( ConfigArgs *c )
                }
                break;
 
+       case CH_MAX_DEPTH:
+               if ( c->value_int < 0 ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "<%s> invalid max referral depth %d",
+                               c->argv[0], c->value_int );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+                               c->log, c->msg, 0 );
+                       rc = 1;
+                       break;
+               }
+               lc->lc_max_depth = c->value_int;
+
+       case CH_RETURN_ERR:
+               if ( c->value_int ) {
+                       lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
+               } else {
+                       lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
+               }
+               break;
+
        default:
                assert( 0 );
                return 1;
@@ -1272,11 +1489,17 @@ ldap_chain_db_init(
        ldap_chain_t    *lc = NULL;
 
        if ( lback == NULL ) {
+               static BackendInfo      lback2;
+
                lback = backend_info( "ldap" );
 
                if ( lback == NULL ) {
                        return 1;
                }
+
+               lback2 = *lback;
+               lback2.bi_type = ldapchain.on_bi.bi_type;
+               lback = &lback2;
        }
 
        lc = ch_malloc( sizeof( ldap_chain_t ) );
@@ -1284,6 +1507,7 @@ ldap_chain_db_init(
                return 1;
        }
        memset( lc, 0, sizeof( ldap_chain_t ) );
+       lc->lc_max_depth = 1;
        ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
 
        on->on_bi.bi_private = (void *)lc;
@@ -1494,10 +1718,9 @@ ldap_chain_db_open(
 {
        slap_overinst   *on = (slap_overinst *) be->bd_info;
        ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
+       int             rc = 0;
 
 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
-       int     rc = 0;
-
        rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
        if ( rc != 0 ) {
                return rc;
@@ -1511,7 +1734,10 @@ ldap_chain_db_open(
                be->be_private = be_private;
        }
 
-       return ldap_chain_db_func( be, db_open );
+       /* filter out and restore monitoring */
+       rc = ldap_chain_db_func( be, db_open );
+
+       return rc;
 }
 
 static int
@@ -1550,14 +1776,17 @@ ldap_chain_db_init_common(
        BackendDB       *be )
 {
        BackendInfo     *bi = be->bd_info;
-       int             t;
+       ldapinfo_t      *li;
+       int             rc;
 
        be->bd_info = lback;
        be->be_private = NULL;
-       t = lback->bi_db_init( be );
-       if ( t != 0 ) {
-               return t;
+       rc = lback->bi_db_init( be );
+       if ( rc != 0 ) {
+               return rc;
        }
+       li = (ldapinfo_t *)be->be_private;
+
        be->bd_info = bi;
 
        return 0;
@@ -1581,7 +1810,7 @@ ldap_chain_db_init_one(
        BackendInfo     *bi = be->bd_info;
        ldapinfo_t      *li;
 
-       int             t;
+       slap_op_t       t;
 
        be->bd_info = lback;
        be->be_private = NULL;
@@ -1595,7 +1824,7 @@ ldap_chain_db_init_one(
        li->li_nretries = lc->lc_common_li->li_nretries;
        li->li_flags = lc->lc_common_li->li_flags;
        li->li_version = lc->lc_common_li->li_version;
-       for ( t = 0; t < LDAP_BACK_OP_LAST; t++ ) {
+       for ( t = 0; t < SLAP_OP_LAST; t++ ) {
                li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
        }
        be->bd_info = bi;
@@ -1603,6 +1832,13 @@ ldap_chain_db_init_one(
        return 0;
 }
 
+static int
+ldap_chain_db_open_one(
+       BackendDB       *be )
+{
+       return lback->bi_db_open( be );
+}
+
 typedef struct ldap_chain_conn_apply_t {
        BackendDB       *be;
        Connection      *conn;
index 16a0e4bdc9d5e203ad6cb393e8c34208747f10c8..ee964a1183d731f38a5965455a0c021e061311dd 100644 (file)
@@ -36,21 +36,23 @@ ldap_back_compare(
                Operation       *op,
                SlapReply       *rs )
 {
-       ldapconn_t      *lc;
-       ber_int_t       msgid;
-       int             do_retry = 1;
-       LDAPControl     **ctrls = NULL;
-       int             rc = LDAP_SUCCESS;
+       ldapinfo_t              *li = (ldapinfo_t *)op->o_bd->be_private;
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       ldapconn_t              *lc = NULL;
+       ber_int_t               msgid;
+       ldap_back_send_t        retrying = LDAP_BACK_RETRYING;
+       LDAPControl             **ctrls = NULL;
+       int                     rc = LDAP_SUCCESS;
+
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                lc = NULL;
                goto cleanup;
        }
 
 retry:
        ctrls = op->o_ctrls;
-       rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
+       rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
+               li->li_version, &li->li_idassert, op, rs, &ctrls );
        if ( rc != LDAP_SUCCESS ) {
                send_ldap_result( op, rs );
                goto cleanup;
@@ -60,9 +62,11 @@ retry:
                        op->orc_ava->aa_desc->ad_cname.bv_val,
                        &op->orc_ava->aa_value, 
                        ctrls, NULL, &msgid );
-       rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDRESULT );
-       if ( rc == LDAP_UNAVAILABLE && do_retry ) {
-               do_retry = 0;
+       rc = ldap_back_op_result( lc, op, rs, msgid,
+               li->li_timeout[ SLAP_OP_COMPARE ],
+               ( LDAP_BACK_SENDRESULT | retrying ) );
+       if ( rc == LDAP_UNAVAILABLE && retrying ) {
+               retrying &= ~LDAP_BACK_RETRYING;
                if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                        /* if the identity changed, there might be need to re-authz */
                        (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
index 0a3bf1828b38f4df1258ec74c5d630b344af9ac3..9fd2f1490ee5cec44b213cbf7418448c963473a1 100644 (file)
@@ -64,6 +64,11 @@ enum {
        LDAP_BACK_CFG_CONN_TTL,
        LDAP_BACK_CFG_NETWORK_TIMEOUT,
        LDAP_BACK_CFG_VERSION,
+       LDAP_BACK_CFG_SINGLECONN,
+       LDAP_BACK_CFG_USETEMP,
+       LDAP_BACK_CFG_CONNPOOLMAX,
+       LDAP_BACK_CFG_CANCEL,
+       LDAP_BACK_CFG_QUARANTINE,
        LDAP_BACK_CFG_REWRITE,
 
        LDAP_BACK_CFG_LAST
@@ -250,6 +255,46 @@ static ConfigTable ldapcfg[] = {
                        "SYNTAX OMsInteger "
                        "SINGLE-VALUE )",
                NULL, NULL },
+       { "single-conn", "TRUE/FALSE", 2, 0, 0,
+               ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.19 "
+                       "NAME 'olcDbSingleConn' "
+                       "DESC 'cache a single connection per identity' "
+                       "SYNTAX OMsBoolean "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
+       { "cancel", "ABANDON|ignore|exop", 2, 0, 0,
+               ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.20 "
+                       "NAME 'olcDbCancel' "
+                       "DESC 'abandon/ignore/exop operations when appropriate' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
+       { "quarantine", "retrylist", 2, 0, 0,
+               ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.21 "
+                       "NAME 'olcDbQuarantine' "
+                       "DESC 'Quarantine database if connection fails and retry according to rule' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
+       { "use-temporary-conn", "TRUE/FALSE", 2, 0, 0,
+               ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_USETEMP,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.22 "
+                       "NAME 'olcDbUseTemporaryConn' "
+                       "DESC 'Use temporary connections if the cached one is busy' "
+                       "SYNTAX OMsBoolean "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
+       { "conn-pool-max", "<n>", 2, 0, 0,
+               ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.23 "
+                       "NAME 'olcDbConnectionPoolMax' "
+                       "DESC 'Max size of privileged connections pool' "
+                       "SYNTAX OMsInteger "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
        { "suffixmassage", "[virtual]> <real", 2, 3, 0,
                ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
                ldap_back_cf_gen, NULL, NULL, NULL },
@@ -268,8 +313,8 @@ static ConfigOCs ldapocs[] = {
                "NAME 'olcLDAPConfig' "
                "DESC 'LDAP backend configuration' "
                "SUP olcDatabaseConfig "
-               "MUST olcDbURI "
-               "MAY ( olcDbStartTLS "
+               "MAY ( olcDbURI "
+                       "$ olcDbStartTLS "
                        "$ olcDbACLAuthcDn "
                        "$ olcDbACLPasswd "
                        "$ olcDbACLBind "
@@ -284,6 +329,11 @@ static ConfigOCs ldapocs[] = {
                        "$ olcDbProxyWhoAmI "
                        "$ olcDbTimeout "
                        "$ olcDbIdleTimeout "
+                       "$ olcDbSingleConn "
+                       "$ olcDbCancel "
+                       "$ olcDbQuarantine "
+                       "$ olcDbUseTemporaryConn "
+                       "$ olcDbConnectionPoolMax "
                ") )",
                        Cft_Database, ldapcfg},
        { NULL, 0, NULL }
@@ -307,30 +357,412 @@ static slap_verbmasks tls_mode[] = {
 };
 
 static slap_verbmasks t_f_mode[] = {
-       { BER_BVC( "yes" ),             LDAP_BACK_F_SUPPORT_T_F },
-       { BER_BVC( "discover" ),        LDAP_BACK_F_SUPPORT_T_F_DISCOVER },
+       { BER_BVC( "yes" ),             LDAP_BACK_F_T_F },
+       { BER_BVC( "discover" ),        LDAP_BACK_F_T_F_DISCOVER },
        { BER_BVC( "no" ),              LDAP_BACK_F_NONE },
        { BER_BVNULL,                   0 }
 };
 
+static slap_verbmasks cancel_mode[] = {
+#if 0  /* needs ldap_int_discard(), 2.4 */
+       { BER_BVC( "ignore" ),          LDAP_BACK_F_CANCEL_IGNORE },
+#endif
+       { BER_BVC( "exop" ),            LDAP_BACK_F_CANCEL_EXOP },
+       { BER_BVC( "exop-discover" ),   LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
+       { BER_BVC( "abandon" ),         LDAP_BACK_F_CANCEL_ABANDON },
+       { BER_BVNULL,                   0 }
+};
+
+/* see enum in slap.h */
 static slap_cf_aux_table timeout_table[] = {
-       { BER_BVC("add="), 0 * sizeof( time_t ), 'u', 0, NULL },
-       { BER_BVC("delete="), 1 * sizeof( time_t ), 'u', 0, NULL },
-       { BER_BVC("modify="), 2 * sizeof( time_t ), 'u', 0, NULL },
-       { BER_BVC("modrdn="), 3 * sizeof( time_t ), 'u', 0, NULL },
+       { BER_BVC("bind="),     SLAP_OP_BIND * sizeof( time_t ),        'u', 0, NULL },
+       /* unbind makes no sense */
+       { BER_BVC("add="),      SLAP_OP_ADD * sizeof( time_t ),         'u', 0, NULL },
+       { BER_BVC("delete="),   SLAP_OP_DELETE * sizeof( time_t ),      'u', 0, NULL },
+       { BER_BVC("modrdn="),   SLAP_OP_MODRDN * sizeof( time_t ),      'u', 0, NULL },
+       { BER_BVC("modify="),   SLAP_OP_MODIFY * sizeof( time_t ),      'u', 0, NULL },
+       { BER_BVC("compare="),  SLAP_OP_COMPARE * sizeof( time_t ),     'u', 0, NULL },
+#if 0  /* uses timelimit instead */
+       { BER_BVC("search="),   SLAP_OP_SEARCH * sizeof( time_t ),      'u', 0, NULL },
+#endif
+       /* abandon makes little sense */
+#if 0  /* not implemented yet */
+       { BER_BVC("extended="), SLAP_OP_EXTENDED * sizeof( time_t ),    'u', 0, NULL },
+#endif
        { BER_BVNULL, 0, 0, 0, NULL }
 };
 
+int
+slap_retry_info_parse(
+       char                    *in,
+       slap_retry_info_t       *ri,
+       char                    *buf,
+       ber_len_t               buflen )
+{
+       char                    **retrylist = NULL;
+       int                     rc = 0;
+       int                     i;
+
+       slap_str2clist( &retrylist, in, " ;" );
+       if ( retrylist == NULL ) {
+               return 1;
+       }
+
+       for ( i = 0; retrylist[ i ] != NULL; i++ )
+               /* count */ ;
+
+       ri->ri_interval = ch_calloc( sizeof( time_t ), i + 1 );
+       ri->ri_num = ch_calloc( sizeof( int ), i + 1 );
+
+       for ( i = 0; retrylist[ i ] != NULL; i++ ) {
+               unsigned long   t;
+               char            *sep = strchr( retrylist[ i ], ',' );
+
+               if ( sep == NULL ) {
+                       snprintf( buf, buflen,
+                               "missing comma in retry pattern #%d \"%s\"",
+                               i, retrylist[ i ] );
+                       rc = 1;
+                       goto done;
+               }
+
+               *sep++ = '\0';
+
+               if ( lutil_parse_time( retrylist[ i ], &t ) ) {
+                       snprintf( buf, buflen,
+                               "unable to parse interval #%d \"%s\"",
+                               i, retrylist[ i ] );
+                       rc = 1;
+                       goto done;
+               }
+               ri->ri_interval[ i ] = (time_t)t;
+
+               if ( strcmp( sep, "+" ) == 0 ) {
+                       if ( retrylist[ i + 1 ] != NULL ) {
+                               snprintf( buf, buflen,
+                                       "extra cruft after retry pattern "
+                                       "#%d \"%s,+\" with \"forever\" mark",
+                                       i, retrylist[ i ] );
+                               rc = 1;
+                               goto done;
+                       }
+                       ri->ri_num[ i ] = SLAP_RETRYNUM_FOREVER;
+                       
+               } else if ( lutil_atoi( &ri->ri_num[ i ], sep ) ) {
+                       snprintf( buf, buflen,
+                               "unable to parse retry num #%d \"%s\"",
+                               i, sep );
+                       rc = 1;
+                       goto done;
+               }
+       }
+
+       ri->ri_num[ i ] = SLAP_RETRYNUM_TAIL;
+
+       ri->ri_idx = 0;
+       ri->ri_count = 0;
+       ri->ri_last = (time_t)(-1);
+
+done:;
+       ldap_charray_free( retrylist );
+
+       if ( rc ) {
+               slap_retry_info_destroy( ri );
+       }
+
+       return rc;
+}
+
+int
+slap_retry_info_unparse(
+       slap_retry_info_t       *ri,
+       struct berval           *bvout )
+{
+       int             i;
+       char            buf[ BUFSIZ * 2 ],
+                       *ptr = buf;
+       struct berval   bv = BER_BVNULL;
+
+       assert( ri != NULL );
+       assert( bvout != NULL );
+
+       BER_BVZERO( bvout );
+
+#define WHATSLEFT      ( sizeof( buf ) - ( ptr - buf ) )
+
+       for ( i = 0; ri->ri_num[ i ] != SLAP_RETRYNUM_TAIL; i++ ) {
+               if ( i > 0 ) {
+                       if ( WHATSLEFT <= 1 ) {
+                               return 1;
+                       }
+                       *ptr++ = ';';
+               }
+
+               if ( lutil_unparse_time( ptr, WHATSLEFT, (long)ri->ri_interval[i] ) ) {
+                       return 1;
+               }
+               ptr += strlen( ptr );
+
+               if ( WHATSLEFT <= 1 ) {
+                       return 1;
+               }
+               *ptr++ = ',';
+
+               if ( ri->ri_num[i] == SLAP_RETRYNUM_FOREVER ) {
+                       if ( WHATSLEFT <= 1 ) {
+                               return 1;
+                       }
+                       *ptr++ = '+';
+
+               } else {
+                       ptr += snprintf( ptr, WHATSLEFT, "%d", ri->ri_num[i] );
+                       if ( WHATSLEFT <= 0 ) {
+                               return 1;
+                       }
+               }
+       }
+
+       bv.bv_val = buf;
+       bv.bv_len = ptr - buf;
+
+       ber_dupbv( bvout, &bv );
+
+       return 0;
+}
+
+void
+slap_retry_info_destroy(
+       slap_retry_info_t       *ri )
+{
+       assert( ri != NULL );
+
+       assert( ri->ri_interval != NULL );
+       ch_free( ri->ri_interval );
+       ri->ri_interval = NULL;
+
+       assert( ri->ri_num != NULL );
+       ch_free( ri->ri_num );
+       ri->ri_num = NULL;
+}
+
+static int
+slap_idassert_authzfrom_parse( ConfigArgs *c, slap_idassert_t *si )
+{
+       struct berval   bv;
+
+       if ( strcmp( c->argv[ 1 ], "*" ) == 0
+               || strcmp( c->argv[ 1 ], ".*" ) == 0
+               || strcmp( c->argv[ 1 ], "dn:*" ) == 0
+               || strcasecmp( c->argv[ 1 ], "dn.regex:.*" ) == 0 )
+       {
+               if ( si->si_authz != NULL ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "\"idassert-authzFrom <authz>\": "
+                               "\"%s\" conflicts with existing authz rules",
+                               c->argv[ 1 ] );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                       return 1;
+               }
+               si->si_flags |= LDAP_BACK_AUTH_AUTHZ_ALL;
+               return 0;
+       } else if ( ( si->si_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) ) {
+               snprintf( c->msg, sizeof( c->msg ),
+                       "\"idassert-authzFrom <authz>\": "
+                       "\"<authz>\" conflicts with \"*\"" );
+               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+               return 1;
+       }
+
+#ifdef SLAP_AUTHZ_SYNTAX
+       {
+               struct berval   in;
+               int             rc;
+
+               ber_str2bv( c->argv[ 1 ], 0, 0, &in );
+               rc = authzNormalize( 0, NULL, NULL, &in, &bv, NULL );
+               if ( rc != LDAP_SUCCESS ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "\"idassert-authzFrom <authz>\": "
+                               "invalid syntax" );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                       return 1;
+               }
+       }
+#else /* !SLAP_AUTHZ_SYNTAX */
+       ber_str2bv( c->argv[ 1 ], 0, 1, &bv );
+#endif /* !SLAP_AUTHZ_SYNTAX */
+
+       ber_bvarray_add( &si->si_authz, &bv );
+
+       return 0;
+}
+
+static int
+slap_idassert_parse( ConfigArgs *c, slap_idassert_t *si )
+{
+       int             i;
+
+       for ( i = 1; i < c->argc; i++ ) {
+               if ( strncasecmp( c->argv[ i ], "mode=", STRLENOF( "mode=" ) ) == 0 ) {
+                       char    *argvi = c->argv[ i ] + STRLENOF( "mode=" );
+                       int     j;
+
+                       j = verb_to_mask( argvi, idassert_mode );
+                       if ( BER_BVISNULL( &idassert_mode[ j ].word ) ) {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "\"idassert-bind <args>\": "
+                                       "unknown mode \"%s\"",
+                                       argvi );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                               return 1;
+                       }
+
+                       si->si_mode = idassert_mode[ j ].mask;
+
+               } else if ( strncasecmp( c->argv[ i ], "authz=", STRLENOF( "authz=" ) ) == 0 ) {
+                       char    *argvi = c->argv[ i ] + STRLENOF( "authz=" );
+
+                       if ( strcasecmp( argvi, "native" ) == 0 ) {
+                               if ( si->si_bc.sb_method != LDAP_AUTH_SASL ) {
+                                       snprintf( c->msg, sizeof( c->msg ),
+                                               "\"idassert-bind <args>\": "
+                                               "authz=\"native\" incompatible "
+                                               "with auth method" );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                                       return 1;
+                               }
+                               si->si_flags |= LDAP_BACK_AUTH_NATIVE_AUTHZ;
+
+                       } else if ( strcasecmp( argvi, "proxyAuthz" ) == 0 ) {
+                               si->si_flags &= ~LDAP_BACK_AUTH_NATIVE_AUTHZ;
+
+                       } else {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "\"idassert-bind <args>\": "
+                                       "unknown authz \"%s\"",
+                                       argvi );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                               return 1;
+                       }
+
+               } else if ( strncasecmp( c->argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 ) {
+                       char    *argvi = c->argv[ i ] + STRLENOF( "flags=" );
+                       char    **flags = ldap_str2charray( argvi, "," );
+                       int     j, err = 0;
+
+                       if ( flags == NULL ) {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "\"idassert-bind <args>\": "
+                                       "unable to parse flags \"%s\"",
+                                       argvi );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                               return 1;
+                       }
+
+                       for ( j = 0; flags[ j ] != NULL; j++ ) {
+
+                               if ( strcasecmp( flags[ j ], "override" ) == 0 ) {
+                                       si->si_flags |= LDAP_BACK_AUTH_OVERRIDE;
+
+                               } else if ( strcasecmp( flags[ j ], "prescriptive" ) == 0 ) {
+                                       si->si_flags |= LDAP_BACK_AUTH_PRESCRIPTIVE;
+
+                               } else if ( strcasecmp( flags[ j ], "non-prescriptive" ) == 0 ) {
+                                       si->si_flags &= ( ~LDAP_BACK_AUTH_PRESCRIPTIVE );
+
+                               } else if ( strcasecmp( flags[ j ], "obsolete-proxy-authz" ) == 0 ) {
+                                       if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+                                               Debug( LDAP_DEBUG_ANY,
+                                                               "%s: \"obsolete-proxy-authz\" flag "
+                                                               "in \"idassert-mode <args>\" "
+                                                               "incompatible with previously issued \"obsolete-encoding-workaround\" flag.\n",
+                                                               c->log, 0, 0 );
+                                               err = 1;
+                                               break;
+
+                                       } else {
+                                               si->si_flags |= LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ;
+                                       }
+
+                               } else if ( strcasecmp( flags[ j ], "obsolete-encoding-workaround" ) == 0 ) {
+                                       if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+                                               Debug( LDAP_DEBUG_ANY,
+                                                               "%s: \"obsolete-encoding-workaround\" flag "
+                                                               "in \"idassert-mode <args>\" "
+                                                               "incompatible with previously issued \"obsolete-proxy-authz\" flag.\n",
+                                                               c->log, 0, 0 );
+                                               err = 1;
+                                               break;
+
+                                       } else {
+                                               si->si_flags |= LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND;
+                                       }
+
+                               } else {
+                                       snprintf( c->msg, sizeof( c->msg ),
+                                               "\"idassert-bind <args>\": "
+                                               "unknown flag \"%s\"",
+                                               flags[ j ] );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                                       err = 1;
+                                       break;
+                               }
+                       }
+
+                       ldap_charray_free( flags );
+                       if ( err ) {
+                               return 1;
+                       }
+
+               } else if ( bindconf_parse( c->argv[ i ], &si->si_bc ) ) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/* NOTE: temporary, until back-meta is ported to back-config */
+int
+slap_idassert_authzfrom_parse_cf( const char *fname, int lineno, const char *arg, slap_idassert_t *si )
+{
+       ConfigArgs      c = { 0 };
+       char            *argv[ 3 ];
+
+       snprintf( c.log, sizeof( c.log ), "%s: line %d", fname, lineno );
+       c.argc = 2;
+       c.argv = argv;
+       argv[ 0 ] = "idassert-authzFrom";
+       argv[ 1 ] = (char *)arg;
+       argv[ 2 ] = NULL;
+
+       return slap_idassert_authzfrom_parse( &c, si );
+}
+
+int
+slap_idassert_parse_cf( const char *fname, int lineno, int argc, char *argv[], slap_idassert_t *si )
+{
+       ConfigArgs      c = { 0 };
+
+       snprintf( c.log, sizeof( c.log ), "%s: line %d", fname, lineno );
+       c.argc = argc;
+       c.argv = argv;
+
+       return slap_idassert_parse( &c, si );
+}
+
 static int
 ldap_back_cf_gen( ConfigArgs *c )
 {
        ldapinfo_t      *li = ( ldapinfo_t * )c->be->be_private;
-       int             rc;
+       int             rc = 0;
        int             i;
 
        if ( c->op == SLAP_CONFIG_EMIT ) {
                struct berval   bv = BER_BVNULL;
-               rc = 0;
 
                if ( li == NULL ) {
                        return 1;
@@ -339,10 +771,14 @@ ldap_back_cf_gen( ConfigArgs *c )
                switch( c->type ) {
                case LDAP_BACK_CFG_URI:
                        if ( li->li_uri != NULL ) {
-                               struct berval   bv;
+                               struct berval   bv, bv2;
 
                                ber_str2bv( li->li_uri, 0, 0, &bv );
-                               value_add_one( &c->rvalue_vals, &bv );
+                               bv2.bv_len = bv.bv_len + STRLENOF( "\"\"" );
+                               bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
+                               snprintf( bv2.bv_val, bv2.bv_len + 1,
+                                       "\"%s\"", bv.bv_val );
+                               ber_bvarray_add( &c->rvalue_vals, &bv2 );
 
                        } else {
                                rc = 1;
@@ -396,7 +832,13 @@ ldap_back_cf_gen( ConfigArgs *c )
                        int             i;
 
                        if ( li->li_idassert_authz == NULL ) {
-                               rc = 1;
+                               if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) ) {
+                                       BER_BVSTR( &bv, "*" );
+                                       value_add_one( &c->rvalue_vals, &bv );
+
+                               } else {
+                                       rc = 1;
+                               }
                                break;
                        }
 
@@ -462,7 +904,7 @@ ldap_back_cf_gen( ConfigArgs *c )
                                        (void)lutil_strcopy( ptr, "authz=native" );
                                }
 
-                               len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override" );
+                               len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround" );
                                /* flags */
                                if ( !BER_BVISEMPTY( &bv ) ) {
                                        len += STRLENOF( " " );
@@ -488,11 +930,18 @@ ldap_back_cf_gen( ConfigArgs *c )
                                        ptr = lutil_strcopy( ptr, ",override" );
                                }
 
+                               if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+                                       ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" );
+
+                               } else if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+                                       ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" );
+                               }
+
                                bv.bv_len = ( ptr - bv.bv_val );
                                /* end-of-flags */
                        }
 
-                       bindconf_unparse( &li->li_idassert, &bc );
+                       bindconf_unparse( &li->li_idassert.si_bc, &bc );
 
                        if ( !BER_BVISNULL( &bv ) ) {
                                ber_len_t       len = bv.bv_len + bc.bv_len;
@@ -531,7 +980,7 @@ ldap_back_cf_gen( ConfigArgs *c )
                        break;
 
                case LDAP_BACK_CFG_T_F:
-                       enum_to_verb( t_f_mode, (li->li_flags & LDAP_BACK_F_SUPPORT_T_F_MASK), &bv );
+                       enum_to_verb( t_f_mode, (li->li_flags & LDAP_BACK_F_T_F_MASK2), &bv );
                        if ( BER_BVISNULL( &bv ) ) {
                                /* there's something wrong... */
                                assert( 0 );
@@ -549,13 +998,13 @@ ldap_back_cf_gen( ConfigArgs *c )
                case LDAP_BACK_CFG_TIMEOUT:
                        BER_BVZERO( &bv );
 
-                       for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) {
+                       for ( i = 0; i < SLAP_OP_LAST; i++ ) {
                                if ( li->li_timeout[ i ] != 0 ) {
                                        break;
                                }
                        }
 
-                       if ( i == LDAP_BACK_OP_LAST ) {
+                       if ( i == SLAP_OP_LAST ) {
                                return 1;
                        }
 
@@ -622,6 +1071,47 @@ ldap_back_cf_gen( ConfigArgs *c )
                        c->value_int = li->li_version;
                        break;
 
+               case LDAP_BACK_CFG_SINGLECONN:
+                       c->value_int = LDAP_BACK_SINGLECONN( li );
+                       break;
+
+               case LDAP_BACK_CFG_USETEMP:
+                       c->value_int = LDAP_BACK_USE_TEMPORARIES( li );
+                       break;
+
+               case LDAP_BACK_CFG_CONNPOOLMAX:
+                       c->value_int = li->li_conn_priv_max;
+                       break;
+
+               case LDAP_BACK_CFG_CANCEL: {
+                       slap_mask_t     mask = LDAP_BACK_F_CANCEL_MASK2;
+
+                       if ( LDAP_BACK_CANCEL_DISCOVER( li ) ) {
+                               mask &= ~LDAP_BACK_F_CANCEL_EXOP;
+                       }
+                       enum_to_verb( cancel_mode, (li->li_flags & mask), &bv );
+                       if ( BER_BVISNULL( &bv ) ) {
+                               /* there's something wrong... */
+                               assert( 0 );
+                               rc = 1;
+
+                       } else {
+                               value_add_one( &c->rvalue_vals, &bv );
+                       }
+                       } break;
+
+               case LDAP_BACK_CFG_QUARANTINE:
+                       if ( !LDAP_BACK_QUARANTINE( li ) ) {
+                               rc = 1;
+                               break;
+                       }
+
+                       rc = slap_retry_info_unparse( &li->li_quarantine, &bv );
+                       if ( rc == 0 ) {
+                               ber_bvarray_add( &c->rvalue_vals, &bv );
+                       }
+                       break;
+
                default:
                        /* FIXME: we need to handle all... */
                        assert( 0 );
@@ -630,7 +1120,6 @@ ldap_back_cf_gen( ConfigArgs *c )
                return rc;
 
        } else if ( c->op == LDAP_MOD_DELETE ) {
-               rc = 0;
                switch( c->type ) {
                case LDAP_BACK_CFG_URI:
                        if ( li->li_uri != NULL ) {
@@ -683,18 +1172,19 @@ ldap_back_cf_gen( ConfigArgs *c )
                        break;
 
                case LDAP_BACK_CFG_IDASSERT_BIND:
-                       bindconf_free( &li->li_idassert );
+                       bindconf_free( &li->li_idassert.si_bc );
                        break;
 
                case LDAP_BACK_CFG_REBIND:
                case LDAP_BACK_CFG_CHASE:
                case LDAP_BACK_CFG_T_F:
                case LDAP_BACK_CFG_WHOAMI:
+               case LDAP_BACK_CFG_CANCEL:
                        rc = 1;
                        break;
 
                case LDAP_BACK_CFG_TIMEOUT:
-                       for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) {
+                       for ( i = 0; i < SLAP_OP_LAST; i++ ) {
                                li->li_timeout[ i ] = 0;
                        }
                        break;
@@ -715,6 +1205,28 @@ ldap_back_cf_gen( ConfigArgs *c )
                        li->li_version = 0;
                        break;
 
+               case LDAP_BACK_CFG_SINGLECONN:
+                       li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
+                       break;
+
+               case LDAP_BACK_CFG_USETEMP:
+                       li->li_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+                       break;
+
+               case LDAP_BACK_CFG_CONNPOOLMAX:
+                       li->li_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN;
+                       break;
+
+               case LDAP_BACK_CFG_QUARANTINE:
+                       if ( !LDAP_BACK_QUARANTINE( li ) ) {
+                               break;
+                       }
+
+                       slap_retry_info_destroy( &li->li_quarantine );
+                       ldap_pvt_thread_mutex_destroy( &li->li_quarantine_mutex );
+                       li->li_isquarantined = 0;
+                       break;
+
                default:
                        /* FIXME: we need to handle all... */
                        assert( 0 );
@@ -988,6 +1500,28 @@ done_url:;
                                } else if ( strcasecmp( c->argv[ i ], "non-prescriptive" ) == 0 ) {
                                        li->li_idassert_flags &= ( ~LDAP_BACK_AUTH_PRESCRIPTIVE );
 
+                               } else if ( strcasecmp( c->argv[ i ], "obsolete-proxy-authz" ) == 0 ) {
+                                       if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+                                               Debug( LDAP_DEBUG_ANY,
+                                                               "%s: line %d: \"obsolete-proxy-authz\" flag "
+                                                       "in \"idassert-mode <args>\" "
+                                                       "incompatible with previously issued \"obsolete-encoding-workaround\" flag.\n",
+                                                       c->fname, c->lineno, 0 );
+                                               return 1;
+                                       }
+                                       li->li_idassert_flags |= LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ;
+
+                               } else if ( strcasecmp( c->argv[ i ], "obsolete-encoding-workaround" ) == 0 ) {
+                                       if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+                                               Debug( LDAP_DEBUG_ANY,
+                                                               "%s: line %d: \"obsolete-encoding-workaround\" flag "
+                                                       "in \"idassert-mode <args>\" "
+                                                       "incompatible with previously issued \"obsolete-proxy-authz\" flag.\n",
+                                                       c->fname, c->lineno, 0 );
+                                               return 1;
+                                       }
+                                       li->li_idassert_flags |= LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND;
+
                                } else {
                                        Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: unknown flag #%d "
@@ -1049,26 +1583,9 @@ done_url:;
                ber_str2bv( c->argv[ 1 ], 0, 1, &li->li_idassert_passwd );
                break;
 
-       case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
-               struct berval   bv;
-#ifdef SLAP_AUTHZ_SYNTAX
-               struct berval   in;
-               int             rc;
-
-               ber_str2bv( c->argv[ 1 ], 0, 0, &in );
-               rc = authzNormalize( 0, NULL, NULL, &in, &bv, NULL );
-               if ( rc != LDAP_SUCCESS ) {
-                       snprintf( c->msg, sizeof( c->msg ),
-                               "\"idassert-authzFrom <authz>\": "
-                               "invalid syntax" );
-                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
-                       return 1;
-               }
-#else /* !SLAP_AUTHZ_SYNTAX */
-               ber_str2bv( c->argv[ 1 ], 0, 1, &bv );
-#endif /* !SLAP_AUTHZ_SYNTAX */
-               ber_bvarray_add( &li->li_idassert_authz, &bv );
-               } break;
+       case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+               rc = slap_idassert_authzfrom_parse( c, &li->li_idassert );
+               break;
 
        case LDAP_BACK_CFG_IDASSERT_METHOD:
                /* no longer supported */
@@ -1079,90 +1596,7 @@ done_url:;
                return 1;
 
        case LDAP_BACK_CFG_IDASSERT_BIND:
-               for ( i = 1; i < c->argc; i++ ) {
-                       if ( strncasecmp( c->argv[ i ], "mode=", STRLENOF( "mode=" ) ) == 0 ) {
-                               char    *argvi = c->argv[ i ] + STRLENOF( "mode=" );
-                               int     j;
-
-                               j = verb_to_mask( argvi, idassert_mode );
-                               if ( BER_BVISNULL( &idassert_mode[ j ].word ) ) {
-                                       snprintf( c->msg, sizeof( c->msg ),
-                                               "\"idassert-bind <args>\": "
-                                               "unknown mode \"%s\"",
-                                               argvi );
-                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
-                                       return 1;
-                               }
-
-                               li->li_idassert_mode = idassert_mode[ j ].mask;
-
-                       } else if ( strncasecmp( c->argv[ i ], "authz=", STRLENOF( "authz=" ) ) == 0 ) {
-                               char    *argvi = c->argv[ i ] + STRLENOF( "authz=" );
-
-                               if ( strcasecmp( argvi, "native" ) == 0 ) {
-                                       if ( li->li_idassert_authmethod != LDAP_AUTH_SASL ) {
-                                               snprintf( c->msg, sizeof( c->msg ),
-                                                       "\"idassert-bind <args>\": "
-                                                       "authz=\"native\" incompatible "
-                                                       "with auth method" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
-                                               return 1;
-                                       }
-                                       li->li_idassert_flags |= LDAP_BACK_AUTH_NATIVE_AUTHZ;
-
-                               } else if ( strcasecmp( argvi, "proxyAuthz" ) == 0 ) {
-                                       li->li_idassert_flags &= ~LDAP_BACK_AUTH_NATIVE_AUTHZ;
-
-                               } else {
-                                       snprintf( c->msg, sizeof( c->msg ),
-                                               "\"idassert-bind <args>\": "
-                                               "unknown authz \"%s\"",
-                                               argvi );
-                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
-                                       return 1;
-                               }
-
-                       } else if ( strncasecmp( c->argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 ) {
-                               char    *argvi = c->argv[ i ] + STRLENOF( "flags=" );
-                               char    **flags = ldap_str2charray( argvi, "," );
-                               int     j;
-
-                               if ( flags == NULL ) {
-                                       snprintf( c->msg, sizeof( c->msg ),
-                                               "\"idassert-bind <args>\": "
-                                               "unable to parse flags \"%s\"",
-                                               argvi );
-                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
-                                       return 1;
-                               }
-
-                               for ( j = 0; flags[ j ] != NULL; j++ ) {
-                                       if ( strcasecmp( flags[ j ], "override" ) == 0 ) {
-                                               li->li_idassert_flags |= LDAP_BACK_AUTH_OVERRIDE;
-
-                                       } else if ( strcasecmp( flags[ j ], "prescriptive" ) == 0 ) {
-                                               li->li_idassert_flags |= LDAP_BACK_AUTH_PRESCRIPTIVE;
-
-                                       } else if ( strcasecmp( flags[ j ], "non-prescriptive" ) == 0 ) {
-                                               li->li_idassert_flags &= ( ~LDAP_BACK_AUTH_PRESCRIPTIVE );
-
-                                       } else {
-                                               snprintf( c->msg, sizeof( c->msg ),
-                                                       "\"idassert-bind <args>\": "
-                                                       "unknown flag \"%s\"",
-                                                       flags[ j ] );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
-                                               ldap_charray_free( flags );
-                                               return 1;
-                                       }
-                               }
-
-                               ldap_charray_free( flags );
-
-                       } else if ( bindconf_parse( c->argv[ i ], &li->li_idassert ) ) {
-                               return 1;
-                       }
-               }
+               rc = slap_idassert_parse( c, &li->li_idassert );
                break;
 
        case LDAP_BACK_CFG_REBIND:
@@ -1183,14 +1617,41 @@ done_url:;
                }
                break;
 
-       case LDAP_BACK_CFG_T_F:
+       case LDAP_BACK_CFG_T_F: {
+               slap_mask_t             mask;
+
                i = verb_to_mask( c->argv[1], t_f_mode );
                if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
                        return 1;
                }
-               li->li_flags &= ~LDAP_BACK_F_SUPPORT_T_F_MASK;
-               li->li_flags |= t_f_mode[i].mask;
-               break;
+
+               mask = t_f_mode[i].mask;
+
+               if ( LDAP_BACK_ISOPEN( li )
+                       && mask == LDAP_BACK_F_T_F_DISCOVER
+                       && !LDAP_BACK_T_F( li ) )
+               {
+                       int             rc;
+
+                       if ( li->li_uri == NULL ) {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "need URI to discover \"cancel\" support "
+                                       "in \"cancel exop-discover\"" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                               return 1;
+                       }
+
+                       rc = slap_discover_feature( li->li_uri, li->li_version,
+                                       slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+                                       LDAP_FEATURE_ABSOLUTE_FILTERS );
+                       if ( rc == LDAP_COMPARE_TRUE ) {
+                               mask |= LDAP_BACK_F_T_F;
+                       }
+               }
+
+               li->li_flags &= ~LDAP_BACK_F_T_F_MASK2;
+               li->li_flags |= mask;
+               } break;
 
        case LDAP_BACK_CFG_WHOAMI:
                if ( c->argc == 1 || c->value_int ) {
@@ -1210,10 +1671,14 @@ done_url:;
                                unsigned        u;
 
                                if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
+                                       snprintf( c->msg, sizeof( c->msg),
+                                               "unable to parse timeout \"%s\"",
+                                               c->argv[ i ] );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
                                        return 1;
                                }
 
-                               for ( j = 0; j < LDAP_BACK_OP_LAST; j++ ) {
+                               for ( j = 0; j < SLAP_OP_LAST; j++ ) {
                                        li->li_timeout[ j ] = u;
                                }
 
@@ -1221,6 +1686,10 @@ done_url:;
                        }
 
                        if ( slap_cf_aux_table_parse( c->argv[ i ], li->li_timeout, timeout_table, "slapd-ldap timeout" ) ) {
+                               snprintf( c->msg, sizeof( c->msg),
+                                       "unable to parse timeout \"%s\"",
+                                       c->argv[ i ] );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
                                return 1;
                        }
                }
@@ -1266,16 +1735,108 @@ done_url:;
                } break;
 
        case LDAP_BACK_CFG_VERSION:
-               switch ( c->value_int ) {
-               case 0:
-               case LDAP_VERSION2:
-               case LDAP_VERSION3:
-                       li->li_version = c->value_int;
-                       break;
+               if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "unsupported version \"%s\" "
+                               "in \"protocol-version <version>\"",
+                               c->argv[ 1 ] );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                       return 1;
+               }
 
-               default:
+               li->li_version = c->value_int;
+               break;
+
+       case LDAP_BACK_CFG_SINGLECONN:
+               if ( c->value_int ) {
+                       li->li_flags |= LDAP_BACK_F_SINGLECONN;
+
+               } else {
+                       li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
+               }
+               break;
+
+       case LDAP_BACK_CFG_USETEMP:
+               if ( c->value_int ) {
+                       li->li_flags |= LDAP_BACK_F_USE_TEMPORARIES;
+
+               } else {
+                       li->li_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+               }
+               break;
+
+       case LDAP_BACK_CFG_CONNPOOLMAX:
+               if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
+                       || c->value_int > LDAP_BACK_CONN_PRIV_MAX )
+               {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "invalid max size " "of privileged "
+                               "connections pool \"%s\" "
+                               "in \"conn-pool-max <n> "
+                               "(must be between %d and %d)\"",
+                               c->argv[ 1 ],
+                               LDAP_BACK_CONN_PRIV_MIN,
+                               LDAP_BACK_CONN_PRIV_MAX );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                       return 1;
+               }
+               li->li_conn_priv_max = c->value_int;
+               break;
+
+       case LDAP_BACK_CFG_CANCEL: {
+               slap_mask_t             mask;
+
+               i = verb_to_mask( c->argv[1], cancel_mode );
+               if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
+                       return 1;
+               }
+
+               mask = cancel_mode[i].mask;
+
+               if ( LDAP_BACK_ISOPEN( li )
+                       && mask == LDAP_BACK_F_CANCEL_EXOP_DISCOVER
+                       && !LDAP_BACK_CANCEL( li ) )
+               {
+                       int             rc;
+
+                       if ( li->li_uri == NULL ) {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "need URI to discover \"cancel\" support "
+                                       "in \"cancel exop-discover\"" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                               return 1;
+                       }
+
+                       rc = slap_discover_feature( li->li_uri, li->li_version,
+                                       slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+                                       LDAP_EXOP_CANCEL );
+                       if ( rc == LDAP_COMPARE_TRUE ) {
+                               mask |= LDAP_BACK_F_CANCEL_EXOP;
+                       }
+               }
+
+               li->li_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+               li->li_flags |= mask;
+               } break;
+
+       case LDAP_BACK_CFG_QUARANTINE:
+               if ( LDAP_BACK_QUARANTINE( li ) ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "quarantine already defined" );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
                        return 1;
                }
+               rc = slap_retry_info_parse( c->argv[1], &li->li_quarantine,
+                       c->msg, sizeof( c->msg ) );
+               if ( rc ) {
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+
+               } else {
+                       ldap_pvt_thread_mutex_init( &li->li_quarantine_mutex );
+                       /* give it a chance to retry if the pattern gets reset
+                        * via back-config */
+                       li->li_isquarantined = 0;
+               }
                break;
 
        case LDAP_BACK_CFG_REWRITE:
@@ -1293,7 +1854,7 @@ done_url:;
                break;
        }
 
-       return 0;
+       return rc;
 }
 
 int
@@ -1368,7 +1929,7 @@ ldap_back_exop_whoami(
                && !strcmp( op->o_conn->c_authz_backend->be_type, "ldap" )
                && !dn_match( &op->o_ndn, &op->o_conn->c_ndn ) )
        {
-               ldapconn_t      *lc;
+               ldapconn_t      *lc = NULL;
                LDAPControl c, *ctrls[2] = {NULL, NULL};
                LDAPMessage *res;
                Operation op2 = *op;
@@ -1378,8 +1939,7 @@ ldap_back_exop_whoami(
 
                ctrls[0] = &c;
                op2.o_ndn = op->o_conn->c_ndn;
-               lc = ldap_back_getconn(&op2, rs, LDAP_BACK_SENDERR);
-               if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+               if ( !ldap_back_dobind( &lc, &op2, rs, LDAP_BACK_SENDERR ) ) {
                        return -1;
                }
                c.ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
index 607108af1b2a1802f0321797582543a3aa54d920..abf56c37f225fddfd36b2b5908c3820d3aff4ab7 100644 (file)
@@ -38,21 +38,20 @@ ldap_back_delete(
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
 
-       ldapconn_t      *lc;
-       ber_int_t       msgid;
-       LDAPControl     **ctrls = NULL;
-       int             do_retry = 1;
-       int             rc = LDAP_SUCCESS;
+       ldapconn_t              *lc = NULL;
+       ber_int_t               msgid;
+       LDAPControl             **ctrls = NULL;
+       ldap_back_send_t        retrying = LDAP_BACK_RETRYING;
+       int                     rc = LDAP_SUCCESS;
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                return rs->sr_err;
        }
 
 retry:
        ctrls = op->o_ctrls;
-       rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
+       rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
+               li->li_version, &li->li_idassert, op, rs, &ctrls );
        if ( rc != LDAP_SUCCESS ) {
                send_ldap_result( op, rs );
                rc = rs->sr_err;
@@ -62,9 +61,10 @@ retry:
        rs->sr_err = ldap_delete_ext( lc->lc_ld, op->o_req_dn.bv_val,
                        ctrls, NULL, &msgid );
        rc = ldap_back_op_result( lc, op, rs, msgid,
-               li->li_timeout[ LDAP_BACK_OP_DELETE], LDAP_BACK_SENDRESULT );
-       if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
-               do_retry = 0;
+               li->li_timeout[ SLAP_OP_DELETE ],
+               ( LDAP_BACK_SENDRESULT | retrying ) );
+       if ( rs->sr_err == LDAP_SERVER_DOWN && retrying ) {
+               retrying &= ~LDAP_BACK_RETRYING;
                if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                        /* if the identity changed, there might be need to re-authz */
                        (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
index 746be96d9f12c2fe854967d7415dd2c83750d24b..8ffb5919858e4b3745871a0e2a5717f59b80ad08 100644 (file)
@@ -42,7 +42,9 @@ static struct exop {
 static int
 ldap_back_extended_one( Operation *op, SlapReply *rs, BI_op_extended exop )
 {
-       ldapconn_t      *lc;
+       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
+
+       ldapconn_t      *lc = NULL;
        LDAPControl     **oldctrls = NULL;
        int             rc;
 
@@ -50,13 +52,14 @@ ldap_back_extended_one( Operation *op, SlapReply *rs, BI_op_extended exop )
         * called twice; maybe we could avoid the 
         * ldap_back_dobind() call inside each extended()
         * call ... */
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                return -1;
        }
 
        oldctrls = op->o_ctrls;
-       if ( ldap_back_proxy_authz_ctrl( lc, op, rs, &op->o_ctrls ) ) {
+       if ( ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
+               li->li_version, &li->li_idassert, op, rs, &op->o_ctrls ) )
+       {
                op->o_ctrls = oldctrls;
                send_ldap_extended( op, rs );
                rs->sr_text = NULL;
@@ -106,7 +109,9 @@ ldap_back_exop_passwd(
                Operation       *op,
                SlapReply       *rs )
 {
-       ldapconn_t      *lc;
+       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
+
+       ldapconn_t      *lc = NULL;
        req_pwdexop_s   *qpw = &op->oq_pwdexop;
        LDAPMessage     *res;
        ber_int_t       msgid;
@@ -114,8 +119,7 @@ ldap_back_exop_passwd(
        int             do_retry = 1;
        char *text = NULL;
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                return -1;
        }
 
@@ -136,6 +140,11 @@ retry:
                        rs->sr_err = rc;
 
                } else {
+                       /* only touch when activity actually took place... */
+                       if ( li->li_idle_timeout && lc ) {
+                               lc->lc_time = op->o_time;
+                       }
+
                        /* sigh. parse twice, because parse_passwd
                         * doesn't give us the err / match / msg info.
                         */
@@ -191,10 +200,18 @@ retry:
                                goto retry;
                        }
                }
+
+               if ( LDAP_BACK_QUARANTINE( li ) ) {
+                       ldap_back_quarantine( op, rs );
+               }
+
                if ( text ) rs->sr_text = text;
                send_ldap_extended( op, rs );
                /* otherwise frontend resends result */
                rc = rs->sr_err = SLAPD_ABANDON;
+
+       } else if ( LDAP_BACK_QUARANTINE( li ) ) {
+               ldap_back_quarantine( op, rs );
        }
 
        /* these have to be freed anyway... */
@@ -220,15 +237,16 @@ ldap_back_exop_generic(
        Operation       *op,
        SlapReply       *rs )
 {
-       ldapconn_t      *lc;
+       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
+
+       ldapconn_t      *lc = NULL;
        LDAPMessage     *res;
        ber_int_t       msgid;
        int             rc;
        int             do_retry = 1;
        char *text = NULL;
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                return -1;
        }
 
@@ -246,6 +264,11 @@ retry:
                        rs->sr_err = rc;
 
                } else {
+                       /* only touch when activity actually took place... */
+                       if ( li->li_idle_timeout && lc ) {
+                               lc->lc_time = op->o_time;
+                       }
+
                        /* sigh. parse twice, because parse_passwd
                         * doesn't give us the err / match / msg info.
                         */
@@ -287,10 +310,18 @@ retry:
                                goto retry;
                        }
                }
+
+               if ( LDAP_BACK_QUARANTINE( li ) ) {
+                       ldap_back_quarantine( op, rs );
+               }
+
                if ( text ) rs->sr_text = text;
                send_ldap_extended( op, rs );
                /* otherwise frontend resends result */
                rc = rs->sr_err = SLAPD_ABANDON;
+
+       } else if ( LDAP_BACK_QUARANTINE( li ) ) {
+               ldap_back_quarantine( op, rs );
        }
 
        /* these have to be freed anyway... */
index 02586a230db4e7183e90bd982fe48539892b060b..9198e0c8f1e005187e704a243972a272616e21e9 100644 (file)
@@ -81,12 +81,16 @@ int
 ldap_back_db_init( Backend *be )
 {
        ldapinfo_t      *li;
+       unsigned        i;
 
        li = (ldapinfo_t *)ch_calloc( 1, sizeof( ldapinfo_t ) );
        if ( li == NULL ) {
                return -1;
        }
 
+       li->li_rebind_f = ldap_back_default_rebind;
+       ldap_pvt_thread_mutex_init( &li->li_uri_mutex );
+
        BER_BVZERO( &li->li_acl_authcID );
        BER_BVZERO( &li->li_acl_authcDN );
        BER_BVZERO( &li->li_acl_passwd );
@@ -105,7 +109,7 @@ ldap_back_db_init( Backend *be )
 
        li->li_idassert_authmethod = LDAP_AUTH_NONE;
        BER_BVZERO( &li->li_idassert_sasl_mech );
-       li->li_idassert.sb_tls = SB_TLS_DEFAULT;
+       li->li_idassert_tls = SB_TLS_DEFAULT;
 
        /* by default, use proxyAuthz control on each operation */
        li->li_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
@@ -120,6 +124,12 @@ ldap_back_db_init( Backend *be )
 
        ldap_pvt_thread_mutex_init( &li->li_conninfo.lai_mutex );
 
+       for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+               li->li_conn_priv[ i ].lic_num = 0;
+               LDAP_TAILQ_INIT( &li->li_conn_priv[ i ].lic_priv );
+       }
+       li->li_conn_priv_max = LDAP_BACK_CONN_PRIV_DEFAULT;
+
        be->be_private = li;
        SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_NOLASTMOD;
 
@@ -150,48 +160,30 @@ ldap_back_db_open( BackendDB *be )
                break;
        }
 
-#if 0 && defined(SLAPD_MONITOR)
-       {
-               /* FIXME: disabled because namingContexts doesn't have
-                * a matching rule, and using an MRA filter doesn't work
-                * because the normalized assertion is compared to the 
-                * non-normalized value, which in general differs from
-                * the normalized one.  See ITS#3406 */
-               struct berval   filter,
-                               base = BER_BVC( "cn=Databases," SLAPD_MONITOR );
-               Attribute       a = { 0 };
-
-               filter.bv_len = STRLENOF( "(&(namingContexts:distinguishedNameMatch:=)(monitoredInfo=ldap))" )
-                       + be->be_nsuffix[ 0 ].bv_len;
-               filter.bv_val = ch_malloc( filter.bv_len + 1 );
-               snprintf( filter.bv_val, filter.bv_len + 1,
-                               "(&(namingContexts:distinguishedNameMatch:=%s)(monitoredInfo=ldap))",
-                               be->be_nsuffix[ 0 ].bv_val );
-
-               a.a_desc = slap_schema.si_ad_labeledURI;
-               a.a_vals = li->li_bvuri;
-               a.a_nvals = li->li_bvuri;
-               if ( monitor_back_register_entry_attrs( NULL, &a, NULL, &base, LDAP_SCOPE_SUBTREE, &filter ) ) {
-                       /* error */
-               }
+       if ( LDAP_BACK_T_F_DISCOVER( li ) && !LDAP_BACK_T_F( li ) ) {
+               int             rc;
 
-               ch_free( filter.bv_val );
+               rc = slap_discover_feature( li->li_uri, li->li_version,
+                               slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+                               LDAP_FEATURE_ABSOLUTE_FILTERS );
+               if ( rc == LDAP_COMPARE_TRUE ) {
+                       li->li_flags |= LDAP_BACK_F_T_F;
+               }
        }
-#endif /* SLAPD_MONITOR */
 
-       if ( li->li_flags & LDAP_BACK_F_SUPPORT_T_F_DISCOVER ) {
+       if ( LDAP_BACK_CANCEL_DISCOVER( li ) && !LDAP_BACK_CANCEL( li ) ) {
                int             rc;
 
-               li->li_flags &= ~LDAP_BACK_F_SUPPORT_T_F_DISCOVER;
-
                rc = slap_discover_feature( li->li_uri, li->li_version,
-                               slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
-                               LDAP_FEATURE_ABSOLUTE_FILTERS );
+                               slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+                               LDAP_EXOP_CANCEL );
                if ( rc == LDAP_COMPARE_TRUE ) {
-                       li->li_flags |= LDAP_BACK_F_SUPPORT_T_F;
+                       li->li_flags |= LDAP_BACK_F_CANCEL_EXOP;
                }
        }
 
+       li->li_flags |= LDAP_BACK_F_ISOPEN;
+
        return 0;
 }
 
@@ -213,16 +205,17 @@ ldap_back_conn_free( void *v_lc )
        if ( !BER_BVISNULL( &lc->lc_local_ndn ) ) {
                ch_free( lc->lc_local_ndn.bv_val );
        }
+       lc->lc_q.tqe_prev = NULL;
+       lc->lc_q.tqe_next = NULL;
        ch_free( lc );
 }
 
 int
-ldap_back_db_destroy(
-    Backend    *be
-)
+ldap_back_db_destroy( Backend *be )
 {
        if ( be->be_private ) {
                ldapinfo_t      *li = ( ldapinfo_t * )be->be_private;
+               unsigned        i;
 
                ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
 
@@ -285,9 +278,22 @@ ldap_back_db_destroy(
                        if ( li->li_conninfo.lai_tree ) {
                        avl_free( li->li_conninfo.lai_tree, ldap_back_conn_free );
                }
+               for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+                       while ( !LDAP_TAILQ_EMPTY( &li->li_conn_priv[ i ].lic_priv ) ) {
+                               ldapconn_t      *lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ i ].lic_priv );
+
+                               LDAP_TAILQ_REMOVE( &li->li_conn_priv[ i ].lic_priv, lc, lc_q );
+                               ldap_back_conn_free( lc );
+                       }
+               }
+               if ( LDAP_BACK_QUARANTINE( li ) ) {
+                       slap_retry_info_destroy( &li->li_quarantine );
+                       ldap_pvt_thread_mutex_destroy( &li->li_quarantine_mutex );
+               }
 
                ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
                ldap_pvt_thread_mutex_destroy( &li->li_conninfo.lai_mutex );
+               ldap_pvt_thread_mutex_destroy( &li->li_uri_mutex );
        }
 
        ch_free( be->be_private );
index 69914feda8778d2f9c82c0e31f3a68cfa4909fdd..fc8b53d04a966958391301cb29a7fa61c87fc40f 100644 (file)
@@ -36,20 +36,19 @@ ldap_back_modify(
                Operation       *op,
                SlapReply       *rs )
 {
-       ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
-
-       ldapconn_t      *lc;
-       LDAPMod         **modv = NULL,
-                       *mods = NULL;
-       Modifications   *ml;
-       int             i, j, rc;
-       ber_int_t       msgid;
-       int             isupdate;
-       int             do_retry = 1;
-       LDAPControl     **ctrls = NULL;
-
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       ldapinfo_t              *li = (ldapinfo_t *)op->o_bd->be_private;
+
+       ldapconn_t              *lc = NULL;
+       LDAPMod                 **modv = NULL,
+                               *mods = NULL;
+       Modifications           *ml;
+       int                     i, j, rc;
+       ber_int_t               msgid;
+       int                     isupdate;
+       ldap_back_send_t        retrying = LDAP_BACK_RETRYING;
+       LDAPControl             **ctrls = NULL;
+
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                return rs->sr_err;
        }
 
@@ -100,7 +99,8 @@ ldap_back_modify(
 
 retry:;
        ctrls = op->o_ctrls;
-       rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
+       rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
+               li->li_version, &li->li_idassert, op, rs, &ctrls );
        if ( rc != LDAP_SUCCESS ) {
                send_ldap_result( op, rs );
                rc = -1;
@@ -110,9 +110,10 @@ retry:;
        rs->sr_err = ldap_modify_ext( lc->lc_ld, op->o_req_dn.bv_val, modv,
                        ctrls, NULL, &msgid );
        rc = ldap_back_op_result( lc, op, rs, msgid,
-               li->li_timeout[ LDAP_BACK_OP_MODIFY], LDAP_BACK_SENDRESULT );
-       if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
-               do_retry = 0;
+               li->li_timeout[ SLAP_OP_MODIFY ],
+               ( LDAP_BACK_SENDRESULT | retrying ) );
+       if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+               retrying &= ~LDAP_BACK_RETRYING;
                if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                        /* if the identity changed, there might be need to re-authz */
                        (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
index e72ed98d37e4f41f0d7bf328f3053f25b8e0cd73..802701d622e74496030ed30cce73954887a9c09e 100644 (file)
@@ -36,17 +36,16 @@ ldap_back_modrdn(
                Operation       *op,
                SlapReply       *rs )
 {
-       ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
+       ldapinfo_t              *li = (ldapinfo_t *)op->o_bd->be_private;
 
-       ldapconn_t      *lc;
-       ber_int_t       msgid;
-       LDAPControl     **ctrls = NULL;
-       int             do_retry = 1;
-       int             rc = LDAP_SUCCESS;
-       char            *newSup = NULL;
+       ldapconn_t              *lc = NULL;
+       ber_int_t               msgid;
+       LDAPControl             **ctrls = NULL;
+       ldap_back_send_t        retrying = LDAP_BACK_RETRYING;
+       int                     rc = LDAP_SUCCESS;
+       char                    *newSup = NULL;
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                return rs->sr_err;
        }
 
@@ -75,7 +74,8 @@ ldap_back_modrdn(
 
 retry:
        ctrls = op->o_ctrls;
-       rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
+       rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
+               li->li_version, &li->li_idassert, op, rs, &ctrls );
        if ( rc != LDAP_SUCCESS ) {
                send_ldap_result( op, rs );
                rc = -1;
@@ -86,9 +86,10 @@ retry:
                        op->orr_newrdn.bv_val, newSup,
                        op->orr_deleteoldrdn, ctrls, NULL, &msgid );
        rc = ldap_back_op_result( lc, op, rs, msgid,
-               li->li_timeout[ LDAP_BACK_OP_MODRDN ], LDAP_BACK_SENDRESULT );
-       if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
-               do_retry = 0;
+               li->li_timeout[ SLAP_OP_MODRDN ],
+               ( LDAP_BACK_SENDRESULT | retrying ) );
+       if ( rs->sr_err == LDAP_SERVER_DOWN && retrying ) {
+               retrying &= ~LDAP_BACK_RETRYING;
                if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                        /* if the identity changed, there might be need to re-authz */
                        (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
index ff791f19baeee5045017ad08b6326805c410c191..3d2f8721d1c9deae12b11b0476bdbf836dd095f3 100644 (file)
@@ -47,15 +47,14 @@ extern BI_connection_destroy        ldap_back_conn_destroy;
 
 extern BI_entry_get_rw         ldap_back_entry_get;
 
-int ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock );
-ldapconn_t *ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok );
-void ldap_back_release_conn_lock( Operation *op, SlapReply *rs, ldapconn_t *lc, int dolock );
-#define ldap_back_release_conn(op, rs, lc) ldap_back_release_conn_lock((op), (rs), (lc), 1)
-int ldap_back_dobind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
+void ldap_back_release_conn_lock( Operation *op, SlapReply *rs, ldapconn_t **lcp, int dolock );
+#define ldap_back_release_conn(op, rs, lc) ldap_back_release_conn_lock((op), (rs), &(lc), 1)
+int ldap_back_dobind( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
 int ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
 int ldap_back_map_result( SlapReply *rs );
 int ldap_back_op_result( ldapconn_t *lc, Operation *op, SlapReply *rs,
        ber_int_t msgid, time_t timeout, ldap_back_send_t sendok );
+int ldap_back_cancel( ldapconn_t *lc, Operation *op, SlapReply *rs, ber_int_t msgid, ldap_back_send_t sendok );
 
 int ldap_back_init_cf( BackendInfo *bi );
 
@@ -66,7 +65,9 @@ extern void ldap_back_conn_free( void *c );
 
 extern int
 ldap_back_proxy_authz_ctrl(
-               ldapconn_t      *lc,
+               struct berval   *bound_ndn,
+               int             version,
+               slap_idassert_t *si,
                Operation       *op,
                SlapReply       *rs,
                LDAPControl     ***pctrls );
@@ -76,9 +77,27 @@ ldap_back_proxy_authz_ctrl_free(
                Operation       *op,
                LDAPControl     ***pctrls );
 
+extern void
+ldap_back_quarantine(
+       Operation       *op,
+       SlapReply       *rs );
+
+#ifdef LDAP_BACK_PRINT_CONNTREE
+extern void
+ldap_back_print_conntree( ldapinfo_t *li, char *msg );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+extern void slap_retry_info_destroy( slap_retry_info_t *ri );
+extern int slap_retry_info_parse( char *in, slap_retry_info_t *ri,
+       char *buf, ber_len_t buflen );
+extern int slap_retry_info_unparse( slap_retry_info_t *ri, struct berval *bvout );
+
+extern int slap_idassert_authzfrom_parse_cf( const char *fname, int lineno, const char *arg, slap_idassert_t *si );
+extern int slap_idassert_parse_cf( const char *fname, int lineno, int argc, char *argv[], slap_idassert_t *si );
+
 extern int chain_init( void );
 
-extern LDAP_REBIND_PROC                *ldap_back_rebind_f;
+extern LDAP_REBIND_PROC                ldap_back_default_rebind;
 
 LDAP_END_DECL
 
index a7645bd9265ca29b80eb2ed1b4b83092ca391627..b27d39795ce35b3a2fa6655dd39ac024a8105e1f 100644 (file)
@@ -75,7 +75,7 @@ ldap_back_munge_filter(
 
                if ( strncmp( ptr, bv_true.bv_val, bv_true.bv_len ) == 0 ) {
                        oldbv = &bv_true;
-                       if ( li->li_flags & LDAP_BACK_F_SUPPORT_T_F ) {
+                       if ( LDAP_BACK_T_F( li ) ) {
                                newbv = &bv_t;
 
                        } else {
@@ -85,7 +85,7 @@ ldap_back_munge_filter(
                } else if ( strncmp( ptr, bv_false.bv_val, bv_false.bv_len ) == 0 )
                {
                        oldbv = &bv_false;
-                       if ( li->li_flags & LDAP_BACK_F_SUPPORT_T_F ) {
+                       if ( LDAP_BACK_T_F( li ) ) {
                                newbv = &bv_f;
 
                        } else {
@@ -141,9 +141,11 @@ ldap_back_search(
                Operation       *op,
                SlapReply       *rs )
 {
-       ldapconn_t      *lc;
+       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
+
+       ldapconn_t      *lc = NULL;
        struct timeval  tv;
-       time_t          stoptime = (time_t)-1;
+       time_t          stoptime = (time_t)(-1);
        LDAPMessage     *res,
                        *e;
        int             rc = 0,
@@ -159,8 +161,7 @@ ldap_back_search(
        /* FIXME: shouldn't this be null? */
        const char      *save_matched = rs->sr_matched;
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
-       if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
+       if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
                return rs->sr_err;
        }
 
@@ -202,7 +203,8 @@ ldap_back_search(
        }
 
        ctrls = op->o_ctrls;
-       rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
+       rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
+               li->li_version, &li->li_idassert, op, rs, &ctrls );
        if ( rc != LDAP_SUCCESS ) {
                goto finish;
        }
@@ -266,7 +268,7 @@ retry:
                        if ( rc > 0 ) {
                                ldap_msgfree( res );
                        }
-                       ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
+                       (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
                        rc = SLAPD_ABANDON;
                        goto finish;
                }
@@ -279,13 +281,18 @@ retry:
                        if ( op->ors_tlimit != SLAP_NO_LIMIT
                                        && slap_get_time() > stoptime )
                        {
-                               ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
+                               (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
                                rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
                                goto finish;
                        }
                        continue;
 
                } else {
+                       /* only touch when activity actually took place... */
+                       if ( li->li_idle_timeout && lc ) {
+                               lc->lc_time = op->o_time;
+                       }
+
                        /* don't retry any more */
                        dont_retry = 1;
                }
@@ -322,7 +329,7 @@ retry:
                                if ( rc == LDAP_UNAVAILABLE ) {
                                        rc = rs->sr_err = LDAP_OTHER;
                                } else {
-                                       ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
+                                       (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
                                }
                                goto finish;
                        }
@@ -346,7 +353,8 @@ retry:
                                        /* NO OP */ ;
 
                                /* FIXME: there MUST be at least one */
-                               rs->sr_ref = ch_malloc( ( cnt + 1 ) * sizeof( struct berval ) );
+                               rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ),
+                                       op->o_tmpmemctx );
 
                                for ( cnt = 0; references[ cnt ]; cnt++ ) {
                                        ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] );
@@ -367,7 +375,7 @@ retry:
                        /* cleanup */
                        if ( references ) {
                                ber_memvfree( (void **)references );
-                               ch_free( rs->sr_ref );
+                               op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
                                rs->sr_ref = NULL;
                        }
 
@@ -407,7 +415,8 @@ retry:
                                for ( cnt = 0; references[ cnt ]; cnt++ )
                                        /* NO OP */ ;
                                
-                               rs->sr_ref = ch_malloc( ( cnt + 1 ) * sizeof( struct berval ) );
+                               rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ),
+                                       op->o_tmpmemctx );
 
                                for ( cnt = 0; references[ cnt ]; cnt++ ) {
                                        /* duplicating ...*/
@@ -476,7 +485,15 @@ retry:
        }
 
 finish:;
-       if ( rc != SLAPD_ABANDON ) {
+       if ( LDAP_BACK_QUARANTINE( li ) ) {
+               ldap_back_quarantine( op, rs );
+       }
+
+#if 0
+       /* let send_ldap_result play cleanup handlers (ITS#4645) */
+       if ( rc != SLAPD_ABANDON )
+#endif
+       {
                send_ldap_result( op, rs );
        }
 
@@ -509,7 +526,7 @@ finish:;
        }
 
        if ( rs->sr_ref ) {
-               ber_bvarray_free( rs->sr_ref );
+               ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx );
                rs->sr_ref = NULL;
        }
 
@@ -716,10 +733,11 @@ ldap_back_entry_get(
                ObjectClass             *oc,
                AttributeDescription    *at,
                int                     rw,
-               Entry                   **ent
-)
+               Entry                   **ent )
 {
-       ldapconn_t      *lc;
+       ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
+
+       ldapconn_t      *lc = NULL;
        int             rc = 1,
                        do_not_cache;
        struct berval   bdn;
@@ -736,8 +754,7 @@ ldap_back_entry_get(
        /* Tell getconn this is a privileged op */
        do_not_cache = op->o_do_not_cache;
        op->o_do_not_cache = 1;
-       lc = ldap_back_getconn( op, &rs, LDAP_BACK_DONTSEND );
-       if ( !lc || !ldap_back_dobind( lc, op, &rs, LDAP_BACK_DONTSEND ) ) {
+       if ( !ldap_back_dobind( &lc, op, &rs, LDAP_BACK_DONTSEND ) ) {
                op->o_do_not_cache = do_not_cache;
                return rs.sr_err;
        }
@@ -769,7 +786,8 @@ ldap_back_entry_get(
 
 retry:
        ctrls = op->o_ctrls;
-       rc = ldap_back_proxy_authz_ctrl( lc, op, &rs, &ctrls );
+       rc = ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
+               li->li_version, &li->li_idassert, op, &rs, &ctrls );
        if ( rc != LDAP_SUCCESS ) {
                goto cleanup;
        }
index 71941b269e6d7b6e52362f0e8968725cbf6925d3..13f488731451f229cea01b1eaf2e192f08e4c732 100644 (file)
@@ -48,11 +48,14 @@ ldap_back_conn_destroy(
        lc_curr.lc_conn = conn;
        
        ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+#if LDAP_BACK_PRINT_CONNTREE > 0
+       ldap_back_print_conntree( li, ">>> ldap_back_conn_destroy" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
        while ( ( lc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)&lc_curr, ldap_back_conn_cmp ) ) != NULL )
        {
                Debug( LDAP_DEBUG_TRACE,
                        "=>ldap_back_conn_destroy: destroying conn %ld (refcnt=%u)\n",
-                       LDAP_BACK_PCONN_ID( lc->lc_conn ), lc->lc_refcnt, 0 );
+                       LDAP_BACK_PCONN_ID( lc ), lc->lc_refcnt, 0 );
 
                assert( lc->lc_refcnt == 0 );
 
@@ -63,6 +66,9 @@ ldap_back_conn_destroy(
                 */
                ldap_back_conn_free( lc );
        }
+#if LDAP_BACK_PRINT_CONNTREE > 0
+       ldap_back_print_conntree( li, "<<< ldap_back_conn_destroy" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
        ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
 
        return 0;
index b20ed994c35c63e3691e33601f340428a91695f6..a8096eff84f52d2a39b1f79ca462bf3a5271d18e 100644 (file)
@@ -36,6 +36,7 @@ int
 meta_back_add( Operation *op, SlapReply *rs )
 {
        metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t    *mt;
        metaconn_t      *mc;
        int             i, candidate = -1;
        int             isupdate;
@@ -45,7 +46,7 @@ meta_back_add( Operation *op, SlapReply *rs )
        dncookie        dc;
        int             msgid;
        int             do_retry = 1;
-       int             maperr = 1;
+       LDAPControl     **ctrls = NULL;
 
        Debug(LDAP_DEBUG_ARGS, "==> meta_back_add: %s\n",
                        op->o_req_dn.bv_val, 0, 0 );
@@ -63,7 +64,8 @@ meta_back_add( Operation *op, SlapReply *rs )
        /*
         * Rewrite the add dn, if needed
         */
-       dc.target = &mi->mi_targets[ candidate ];
+       mt = mi->mi_targets[ candidate ];
+       dc.target = mt;
        dc.conn = op->o_conn;
        dc.rs = rs;
        dc.ctx = "addDN";
@@ -96,7 +98,7 @@ meta_back_add( Operation *op, SlapReply *rs )
                        mapped = a->a_desc->ad_cname;
 
                } else {
-                       ldap_back_map( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at,
+                       ldap_back_map( &mt->mt_rwmap.rwm_at,
                                        &a->a_desc->ad_cname, &mapped, BACKLDAP_MAP );
                        if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
                                continue;
@@ -121,11 +123,11 @@ meta_back_add( Operation *op, SlapReply *rs )
                        for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); ) {
                                struct ldapmapping      *mapping;
 
-                               ldap_back_mapping( &mi->mi_targets[ candidate ].mt_rwmap.rwm_oc,
+                               ldap_back_mapping( &mt->mt_rwmap.rwm_oc,
                                                &a->a_vals[ j ], &mapping, BACKLDAP_MAP );
 
                                if ( mapping == NULL ) {
-                                       if ( mi->mi_targets[ candidate ].mt_rwmap.rwm_oc.drop_missing ) {
+                                       if ( mt->mt_rwmap.rwm_oc.drop_missing ) {
                                                continue;
                                        }
                                        attrs[ i ]->mod_bvalues[ j ] = &a->a_vals[ j ];
@@ -166,64 +168,30 @@ meta_back_add( Operation *op, SlapReply *rs )
        attrs[ i ] = NULL;
 
 retry:;
+       ctrls = op->o_ctrls;
+       if ( ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn,
+               mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ) != LDAP_SUCCESS )
+       {
+               send_ldap_result( op, rs );
+               goto cleanup;
+       }
+
        rs->sr_err = ldap_add_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val,
-                             attrs, op->o_ctrls, NULL, &msgid );
+                             attrs, ctrls, NULL, &msgid );
+       rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+               mt->mt_timeout[ SLAP_OP_ADD ], LDAP_BACK_SENDRESULT );
        if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
                do_retry = 0;
                if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+                       /* if the identity changed, there might be need to re-authz */
+                       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
                        goto retry;
                }
-               goto cleanup;
-
-       } else if ( rs->sr_err == LDAP_SUCCESS ) {
-               struct timeval  tv, *tvp = NULL;
-               LDAPMessage     *res = NULL;
-               int             rc;
-
-               if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_ADD ] != 0 ) {
-                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_ADD ];
-                       tv.tv_usec = 0;
-                       tvp = &tv;
-               }
-
-               rs->sr_err = LDAP_OTHER;
-               maperr = 0;
-               rc = ldap_result( mc->mc_conns[ candidate ].msc_ld,
-                       msgid, LDAP_MSG_ALL, tvp, &res );
-               switch ( rc ) {
-               case -1:
-                       break;
-
-               case 0:
-                       ldap_abandon_ext( mc->mc_conns[ candidate ].msc_ld,
-                               msgid, NULL, NULL );
-                       rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
-                               LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR;
-                       break;
-
-               case LDAP_RES_ADD:
-                       rc = ldap_parse_result( mc->mc_conns[ candidate ].msc_ld,
-                               res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 );
-                       if ( rc != LDAP_SUCCESS ) {
-                               rs->sr_err = rc;
-                       }
-                       maperr = 1;
-                       break;
-
-               default:
-                       ldap_msgfree( res );
-                       break;
-               }
-       }
-
-       if ( maperr ) {
-               rs->sr_err = meta_back_op_result( mc, op, rs, candidate );
-
-       } else {
-               send_ldap_result( op, rs );
        }
 
 cleanup:;
+       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
+
        for ( --i; i >= 0; --i ) {
                free( attrs[ i ]->mod_bvalues );
                free( attrs[ i ] );
index 51a56199c52c65aed42ef1ad4353e7d44c51abdf..4bdc0c7635e502b949447053483102c611e571de 100644 (file)
 #include "rewrite.h"
 LDAP_BEGIN_DECL
 
+/*
+ * Set META_BACK_PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
+ */
+#ifndef META_BACK_PRINT_CONNTREE
+#define META_BACK_PRINT_CONNTREE 0
+#endif /* !META_BACK_PRINT_CONNTREE */
+
 struct slap_conn;
 struct slap_op;
 
@@ -153,26 +160,67 @@ ldap_dnattr_result_rewrite(
 
 /* (end of) from back-ldap.h before rwm removal */
 
+/*
+ * A metasingleconn_t can be in the following, mutually exclusive states:
+ *
+ *     - none                  (0x0U)
+ *     - creating              META_BACK_FCONN_CREATING
+ *     - initialized           META_BACK_FCONN_INITED
+ *     - binding               LDAP_BACK_FCONN_BINDING
+ *     - bound/anonymous       LDAP_BACK_FCONN_ISBOUND/LDAP_BACK_FCONN_ISANON
+ *
+ * possible modifiers are:
+ *
+ *     - privileged            LDAP_BACK_FCONN_ISPRIV
+ *     - privileged, TLS       LDAP_BACK_FCONN_ISTLS
+ *     - subjected to idassert LDAP_BACK_FCONN_ISIDASR
+ *     - tainted               LDAP_BACK_FCONN_TAINTED
+ */
+
+#define META_BACK_FCONN_INITED         (0x00100000U)
+#define META_BACK_FCONN_CREATING       (0x00200000U)
+
+#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)
+#define        META_BACK_CONN_INITED_CLEAR(lc)         LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_INITED)
+#define        META_BACK_CONN_INITED_CPY(lc, mlc)      LDAP_BACK_CONN_CPY((lc), META_BACK_FCONN_INITED, (mlc))
+#define        META_BACK_CONN_CREATING(lc)             LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_CREATING)
+#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))
+
 struct metainfo_t;
 
+#define        META_NOT_CANDIDATE              ((ber_tag_t)0x0)
+#define        META_CANDIDATE                  ((ber_tag_t)0x1)
+#define        META_BINDING                    ((ber_tag_t)0x2)
+
 typedef struct metasingleconn_t {
-       int                     msc_candidate;
-#define        META_NOT_CANDIDATE      ((ber_tag_t)0)
-#define        META_CANDIDATE          ((ber_tag_t)1)
+#define META_CND_ISSET(rs,f)           ( ( (rs)->sr_tag & (f) ) == (f) )
+#define META_CND_SET(rs,f)             ( (rs)->sr_tag |= (f) )
+#define META_CND_CLEAR(rs,f)           ( (rs)->sr_tag &= ~(f) )
+
+#define META_CANDIDATE_RESET(rs)       ( (rs)->sr_tag = 0 )
+#define META_IS_CANDIDATE(rs)          META_CND_ISSET( (rs), META_CANDIDATE )
+#define META_CANDIDATE_SET(rs)         META_CND_SET( (rs), META_CANDIDATE )
+#define META_CANDIDATE_CLEAR(rs)       META_CND_CLEAR( (rs), META_CANDIDATE )
+#define META_IS_BINDING(rs)            META_CND_ISSET( (rs), META_BINDING )
+#define META_BINDING_SET(rs)           META_CND_SET( (rs), META_BINDING )
+#define META_BINDING_CLEAR(rs)         META_CND_CLEAR( (rs), META_BINDING )
        
        LDAP                    *msc_ld;
+       time_t                  msc_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
-
-       struct metainfo_t       *msc_info;
 } metasingleconn_t;
 
 typedef struct metaconn_t {
        struct slap_conn        *mc_conn;
+#define        lc_conn                 mc_conn
        unsigned                mc_refcnt;
 
        time_t                  mc_create_time;
@@ -190,6 +238,11 @@ typedef struct metaconn_t {
        int                     mc_authz_target;
 #define META_BOUND_NONE                (-1)
 #define META_BOUND_ALL         (-2)
+
+       struct metainfo_t       *mc_info;
+
+       LDAP_TAILQ_ENTRY(metaconn_t)    mc_q;
+
        /* supersedes the connection stuff */
        metasingleconn_t        mc_conns[ 1 ];
        /* NOTE: mc_conns must be last, because
@@ -199,6 +252,13 @@ typedef struct metaconn_t {
 
 typedef struct metatarget_t {
        char                    *mt_uri;
+       ldap_pvt_thread_mutex_t mt_uri_mutex;
+
+       /* TODO: we might want to enable different strategies
+        * for different targets */
+       LDAP_REBIND_PROC        *mt_rebind_f;
+       void                    *mt_urllist_p;
+
        BerVarray               mt_subtree_exclude;
        int                     mt_scope;
 
@@ -208,23 +268,50 @@ typedef struct metatarget_t {
        struct berval           mt_binddn;
        struct berval           mt_bindpw;
 
-       struct berval           mt_pseudorootdn;
-       struct berval           mt_pseudorootpw;
+       slap_idassert_t         mt_idassert;
+#define        mt_idassert_mode        mt_idassert.si_mode
+#define        mt_idassert_authcID     mt_idassert.si_bc.sb_authcId
+#define        mt_idassert_authcDN     mt_idassert.si_bc.sb_binddn
+#define        mt_idassert_passwd      mt_idassert.si_bc.sb_cred
+#define        mt_idassert_authzID     mt_idassert.si_bc.sb_authzId
+#define        mt_idassert_authmethod  mt_idassert.si_bc.sb_method
+#define        mt_idassert_sasl_mech   mt_idassert.si_bc.sb_saslmech
+#define        mt_idassert_sasl_realm  mt_idassert.si_bc.sb_realm
+#define        mt_idassert_secprops    mt_idassert.si_bc.sb_secprops
+#define        mt_idassert_tls         mt_idassert.si_bc.sb_tls
+#define        mt_idassert_flags       mt_idassert.si_flags
+#define        mt_idassert_authz       mt_idassert.si_authz
 
        int                     mt_nretries;
 #define META_RETRY_UNDEFINED   (-2)
 #define META_RETRY_FOREVER     (-1)
 #define META_RETRY_NEVER       (0)
-#define META_RETRY_DEFAULT     (3)
+#define META_RETRY_DEFAULT     (10)
 
        struct ldaprwmap        mt_rwmap;
 
+       sig_atomic_t            mt_isquarantined;
+       slap_retry_info_t       mt_quarantine;
+       ldap_pvt_thread_mutex_t mt_quarantine_mutex;
+#define        META_BACK_TGT_QUARANTINE(mt)    ( (mt)->mt_quarantine.ri_num != NULL )
+
        unsigned                mt_flags;
+#define        META_BACK_TGT_ISSET(mt,f)               ( ( (mt)->mt_flags & (f) ) == (f) )
+#define        META_BACK_TGT_ISMASK(mt,m,f)            ( ( (mt)->mt_flags & (m) ) == (f) )
+
+#define        META_BACK_TGT_T_F(mt)                   META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_T_F_MASK, LDAP_BACK_F_T_F )
+#define        META_BACK_TGT_T_F_DISCOVER(mt)          META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_T_F_MASK2, LDAP_BACK_F_T_F_DISCOVER )
+
+#define        META_BACK_TGT_ABANDON(mt)               META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_ABANDON )
+#define        META_BACK_TGT_IGNORE(mt)                META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_IGNORE )
+#define        META_BACK_TGT_CANCEL(mt)                META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_EXOP )
+#define        META_BACK_TGT_CANCEL_DISCOVER(mt)       META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK2, LDAP_BACK_F_CANCEL_EXOP_DISCOVER )
+
        int                     mt_version;
        time_t                  mt_network_timeout;
        struct timeval          mt_bind_timeout;
 #define META_BIND_TIMEOUT      LDAP_BACK_RESULT_UTIMEOUT
-       time_t                  mt_timeout[ LDAP_BACK_OP_LAST ];
+       time_t                  mt_timeout[ SLAP_OP_LAST ];
 } metatarget_t;
 
 typedef struct metadncache_t {
@@ -241,36 +328,62 @@ typedef struct metacandidates_t {
        SlapReply               *mc_candidates;
 } metacandidates_t;
 
+/*
+ * Hook to allow mucking with metainfo_t/metatarget_t when quarantine is over
+ */
+typedef int (*meta_back_quarantine_f)( struct metainfo_t *, int target, void * );
+
 typedef struct metainfo_t {
        int                     mi_ntargets;
        int                     mi_defaulttarget;
 #define META_DEFAULT_TARGET_NONE       (-1)
        int                     mi_nretries;
 
-       metatarget_t            *mi_targets;
+       metatarget_t            **mi_targets;
        metacandidates_t        *mi_candidates;
 
+       LDAP_REBIND_PROC        *mi_rebind_f;
+
        metadncache_t           mi_cache;
        
+       /* cached connections; 
+        * special conns are in tailq rather than in tree */
        ldap_avl_info_t         mi_conninfo;
+       struct {
+               int                                             mic_num;
+               LDAP_TAILQ_HEAD(mc_conn_priv_q, metaconn_t)     mic_priv;
+       }                       mi_conn_priv[ LDAP_BACK_PCONN_LAST ];
+       int                     mi_conn_priv_max;
+
+       /* NOTE: quarantine uses the connection mutex */
+       slap_retry_info_t       mi_quarantine;
+
+#define        META_BACK_QUARANTINE(mi)        ( (mi)->mi_quarantine.ri_num != NULL )
+       meta_back_quarantine_f  mi_quarantine_f;
+       void                    *mi_quarantine_p;
 
        unsigned                mi_flags;
 #define        li_flags                mi_flags
 /* uses flags as defined in <back-ldap/back-ldap.h> */
-#define        META_BACK_F_ONERR_STOP          0x00010000U
-#define        META_BACK_F_DEFER_ROOTDN_BIND   0x00020000U
+#define        META_BACK_F_ONERR_STOP          (0x00100000U)
+#define        META_BACK_F_ONERR_REPORT        (0x00200000U)
+#define        META_BACK_F_ONERR_MASK          (META_BACK_F_ONERR_STOP|META_BACK_F_ONERR_REPORT)
+#define        META_BACK_F_DEFER_ROOTDN_BIND   (0x00400000U)
+#define        META_BACK_F_PROXYAUTHZ_ALWAYS   (0x00800000U)
 
 #define        META_BACK_ONERR_STOP(mi)        ( (mi)->mi_flags & META_BACK_F_ONERR_STOP )
-#define        META_BACK_ONERR_CONTINUE(mi)    ( !META_BACK_ONERR_CONTINUE( (mi) ) )
+#define        META_BACK_ONERR_REPORT(mi)      ( (mi)->mi_flags & META_BACK_F_ONERR_REPORT )
+#define        META_BACK_ONERR_CONTINUE(mi)    ( !( (mi)->mi_flags & META_BACK_F_ONERR_MASK ) )
 
 #define META_BACK_DEFER_ROOTDN_BIND(mi)        ( (mi)->mi_flags & META_BACK_F_DEFER_ROOTDN_BIND )
+#define META_BACK_PROXYAUTHZ_ALWAYS(mi)        ( (mi)->mi_flags & META_BACK_F_PROXYAUTHZ_ALWAYS )
 
        int                     mi_version;
        time_t                  mi_network_timeout;
        time_t                  mi_conn_ttl;
        time_t                  mi_idle_timeout;
        struct timeval          mi_bind_timeout;
-       time_t                  mi_timeout[ LDAP_BACK_OP_LAST ];
+       time_t                  mi_timeout[ SLAP_OP_LAST ];
 } metainfo_t;
 
 typedef enum meta_op_type {
@@ -293,9 +406,8 @@ extern void
 meta_back_release_conn_lock(
                Operation               *op,
        metaconn_t              *mc,
-       int                     dofree,
        int                     dolock );
-#define meta_back_release_conn(op, mc) meta_back_release_conn_lock( (op), (mc), 0, 1 )
+#define meta_back_release_conn(op, mc) meta_back_release_conn_lock( (op), (mc), 1 )
 
 extern int
 meta_back_retry(
@@ -309,23 +421,28 @@ extern void
 meta_back_conn_free(
        void                    *v_mc );
 
+#if META_BACK_PRINT_CONNTREE > 0
+extern void
+meta_back_print_conntree(
+       metainfo_t              *mi,
+       char                    *msg );
+#endif
+
 extern int
 meta_back_init_one_conn(
        Operation               *op,
        SlapReply               *rs,
-       metatarget_t            *mt, 
        metaconn_t              *mc,
        int                     candidate,
        int                     ispriv,
-       ldap_back_send_t        sendok );
+       ldap_back_send_t        sendok,
+       int                     dolock );
 
-extern int
-meta_back_single_bind(
+extern void
+meta_back_quarantine(
        Operation               *op,
        SlapReply               *rs,
-       metaconn_t              *mc,
-       int                     candidate,
-       int                     massage );
+       int                     candidate );
 
 extern int
 meta_back_dobind(
@@ -344,12 +461,35 @@ meta_back_single_dobind(
        int                     retries,
        int                     dolock );
 
+extern int
+meta_back_proxy_authz_cred(
+       metaconn_t              *mc,
+       int                     candidate,
+       Operation               *op,
+       SlapReply               *rs,
+       ldap_back_send_t        sendok,
+       struct berval           *binddn,
+       struct berval           *bindcred,
+       int                     *method );
+
+extern int
+meta_back_cancel(
+       metaconn_t              *mc,
+       Operation               *op,
+       SlapReply               *rs,
+       ber_int_t               msgid,
+       int                     candidate,
+       ldap_back_send_t        sendok );
+
 extern int
 meta_back_op_result(
        metaconn_t              *mc,
        Operation               *op,
        SlapReply               *rs,
-       int                     candidate );
+       int                     candidate,
+       ber_int_t               msgid,
+       time_t                  timeout,
+       ldap_back_send_t        sendok );
 
 extern int
 back_meta_LTX_init_module(
@@ -376,9 +516,7 @@ meta_back_conndn_dup(
  */
 extern int
 meta_back_is_candidate(
-       struct berval           *nsuffix,
-       int                     suffixscope,
-       BerVarray               subtree_exclude,
+       metatarget_t            *mt,
        struct berval           *ndn,
        int                     scope );
 
@@ -394,12 +532,9 @@ meta_clear_unused_candidates(
 
 extern int
 meta_clear_one_candidate(
-       metasingleconn_t        *mc );
-
-extern int
-meta_clear_candidates(
        Operation               *op,
-       metaconn_t              *mc );
+       metaconn_t              *mc,
+       int                     candidate );
 
 /*
  * Dn cache stuff (experimental)
@@ -435,7 +570,7 @@ meta_dncache_delete_entry(
 extern void
 meta_dncache_free( void *entry );
 
-extern LDAP_REBIND_PROC                *meta_back_rebind_f;
+extern LDAP_REBIND_PROC                meta_back_default_rebind;
 
 LDAP_END_DECL
 
index 0cc60f027c1a6d040c6371c7077e71896146cb3e..b6d822a6ce08cce3216e9676a59fb4c187d58763 100644 (file)
 #include "slap.h"
 #include "../back-ldap/back-ldap.h"
 #include "back-meta.h"
+#undef ldap_debug      /* silence a warning in ldap-int.h */
+#include "../../../libraries/libldap/ldap-int.h"
 
-static LDAP_REBIND_PROC        meta_back_default_rebind;
+#include "lutil_ldap.h"
 
-/*
- * a module could register a replacement for this function
- */
-LDAP_REBIND_PROC       *meta_back_rebind_f = meta_back_default_rebind;
+static int
+meta_back_proxy_authz_bind(
+       metaconn_t              *mc,
+       int                     candidate,
+       Operation               *op,
+       SlapReply               *rs,
+       ldap_back_send_t        sendok );
+
+static int
+meta_back_single_bind(
+       Operation               *op,
+       SlapReply               *rs,
+       metaconn_t              *mc,
+       int                     candidate );
 
 int
 meta_back_bind( Operation *op, SlapReply *rs )
@@ -85,17 +97,19 @@ meta_back_bind( Operation *op, SlapReply *rs )
         * invalidCredentials */
        mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_BIND_DONTSEND );
        if ( !mc ) {
-               char    buf[ SLAP_TEXT_BUFLEN ];
-
-               snprintf( buf, sizeof( buf ),
-                       "meta_back_bind: no target "
-                       "for dn \"%s\" (%d%s%s).",
-                       op->o_req_dn.bv_val, rs->sr_err,
-                       rs->sr_text ? ". " : "",
-                       rs->sr_text ? rs->sr_text : "" );
-               Debug( LDAP_DEBUG_ANY,
-                       "%s %s\n",
-                       op->o_log_prefix, buf, 0 );
+               if ( StatslogTest( LDAP_DEBUG_ANY ) ) {
+                       char    buf[ SLAP_TEXT_BUFLEN ];
+
+                       snprintf( buf, sizeof( buf ),
+                               "meta_back_bind: no target "
+                               "for dn \"%s\" (%d%s%s).",
+                               op->o_req_dn.bv_val, rs->sr_err,
+                               rs->sr_text ? ". " : "",
+                               rs->sr_text ? rs->sr_text : "" );
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s %s\n",
+                               op->o_log_prefix, buf, 0 );
+               }
 
                /* FIXME: there might be cases where we don't want
                 * to map the error onto invalidCredentials */
@@ -115,14 +129,13 @@ meta_back_bind( Operation *op, SlapReply *rs )
         */
        mc->mc_authz_target = META_BOUND_NONE;
        for ( i = 0; i < mi->mi_ntargets; i++ ) {
+               metatarget_t    *mt = mi->mi_targets[ i ];
                int             lerr;
-               Operation       op2 = *op;
-               int             massage = 1;
 
                /*
                 * Skip non-candidates
                 */
-               if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
+               if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
                        continue;
                }
 
@@ -144,7 +157,8 @@ meta_back_bind( Operation *op, SlapReply *rs )
                }
 
                if ( isroot ) {
-                       if ( BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
+                       if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
+                               || BER_BVISNULL( &mt->mt_idassert_authcDN ) )
                        {
                                metasingleconn_t        *msc = &mc->mc_conns[ i ];
 
@@ -154,9 +168,7 @@ meta_back_bind( Operation *op, SlapReply *rs )
                                        BER_BVZERO( &msc->msc_bound_ndn );
                                }
 
-                               if ( LDAP_BACK_SAVECRED( mi ) &&
-                                       !BER_BVISNULL( &msc->msc_cred ) )
-                               {
+                               if ( !BER_BVISNULL( &msc->msc_cred ) ) {
                                        /* destroy sensitive data */
                                        memset( msc->msc_cred.bv_val, 0,
                                                msc->msc_cred.bv_len );
@@ -167,15 +179,13 @@ meta_back_bind( Operation *op, SlapReply *rs )
                                continue;
                        }
 
-                       op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
-                       op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
-                       op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
-                       op2.orb_method = LDAP_AUTH_SIMPLE;
+                       
+                       (void)meta_back_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND );
+                       lerr = rs->sr_err;
 
-                       massage = 0;
+               } else {
+                       lerr = meta_back_single_bind( op, rs, mc, i );
                }
-               
-               lerr = meta_back_single_bind( &op2, rs, mc, i, massage );
 
                if ( lerr != LDAP_SUCCESS ) {
                        rc = rs->sr_err = lerr;
@@ -183,7 +193,7 @@ meta_back_bind( Operation *op, SlapReply *rs )
                         * do not assume it's not candidate; rather
                         * mark this as an error to be eventually
                         * reported to client */
-                       candidates[ i ].sr_tag = META_NOT_CANDIDATE;
+                       META_CANDIDATE_CLEAR( &candidates[ i ] );
                        break;
                }
        }
@@ -209,18 +219,50 @@ retry_lock:;
                        }
 
                        assert( mc->mc_refcnt == 1 );
+#if META_BACK_PRINT_CONNTREE > 0
+                       meta_back_print_conntree( mi, ">>> meta_back_bind" );
+#endif /* META_BACK_PRINT_CONNTREE */
                        tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
                                meta_back_conndn_cmp );
                        assert( tmpmc == mc );
 
+                       /* delete all cached connections with the current connection */
+                       if ( LDAP_BACK_SINGLECONN( mi ) ) {
+                               while ( ( tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conn_cmp ) ) != NULL )
+                               {
+                                       Debug( LDAP_DEBUG_TRACE,
+                                               "=>meta_back_bind: destroying conn %ld (refcnt=%u)\n",
+                                               LDAP_BACK_PCONN_ID( mc ), mc->mc_refcnt, 0 );
+
+                                       if ( tmpmc->mc_refcnt != 0 ) {
+                                               /* taint it */
+                                               LDAP_BACK_CONN_TAINTED_SET( tmpmc );
+
+                                       } else {
+                                               /*
+                                                * Needs a test because the handler may be corrupted,
+                                                * and calling ldap_unbind on a corrupted header results
+                                                * in a segmentation fault
+                                                */
+                                               meta_back_conn_free( tmpmc );
+                                       }
+                               }
+                       }
+
                        ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn );
+                       if ( isroot ) {
+                               LDAP_BACK_CONN_ISPRIV_SET( mc );
+                               LDAP_BACK_PCONN_SET( mc, op );
+                       }
                        lerr = avl_insert( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
                                meta_back_conndn_cmp, meta_back_conndn_dup );
+#if META_BACK_PRINT_CONNTREE > 0
+                       meta_back_print_conntree( mi, "<<< meta_back_bind" );
+#endif /* META_BACK_PRINT_CONNTREE */
                        ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
                        if ( lerr == -1 ) {
-                               meta_clear_candidates( op, mc );
-
                                /* we can do this because mc_refcnt == 1 */
+                               assert( mc->mc_refcnt == 1 );
                                mc->mc_refcnt = 0;
                                meta_back_conn_free( mc );
                                mc = NULL;
@@ -260,75 +302,65 @@ retry_lock:;
        return LDAP_SUCCESS;
 }
 
-/*
- * meta_back_single_bind
- *
- * attempts to perform a bind with creds
- */
-int
-meta_back_single_bind(
+static int
+meta_back_bind_op_result(
        Operation               *op,
        SlapReply               *rs,
        metaconn_t              *mc,
        int                     candidate,
-       int                     massage )
+       int                     msgid,
+       ldap_back_send_t        sendok )
 {
        metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
-       metatarget_t            *mt = &mi->mi_targets[ candidate ];
-       struct berval           mdn = BER_BVNULL;
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
        metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
-       int                     msgid,
-                               rebinding = 0;
+       LDAPMessage             *res;
+       struct timeval          tv;
+       int                     rc;
+       int                     nretries = mt->mt_nretries;
+       char                    buf[ SLAP_TEXT_BUFLEN ];
 
-       
-       if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
-               ch_free( msc->msc_bound_ndn.bv_val );
-               BER_BVZERO( &msc->msc_bound_ndn );
-       }
+       Debug( LDAP_DEBUG_TRACE,
+               ">>> %s meta_back_bind_op_result[%d]\n",
+               op->o_log_prefix, candidate, 0 );
 
-       if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &msc->msc_cred ) ) {
-               /* destroy sensitive data */
-               memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
-               ch_free( msc->msc_cred.bv_val );
-               BER_BVZERO( &msc->msc_cred );
-       }
+       if ( rs->sr_err == LDAP_SUCCESS ) {
+               time_t          stoptime = (time_t)(-1),
+                               timeout;
+               int             timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+                               LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+               const char      *timeout_text = "Operation timed out";
+               slap_op_t       opidx = slap_req2op( op->o_tag );
+
+               /* since timeout is not specified, compute and use
+                * the one specific to the ongoing operation */
+               if ( opidx == LDAP_REQ_SEARCH ) {
+                       if ( op->ors_tlimit <= 0 ) {
+                               timeout = 0;
+
+                       } else {
+                               timeout = op->ors_tlimit;
+                               timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+                               timeout_text = NULL;
+                       }
 
-       /*
-        * Rewrite the bind dn if needed
-        */
-       if ( massage ) {
-               dncookie                dc;
+               } else {
+                       timeout = mt->mt_timeout[ opidx ];
+               }
 
-               dc.target = mt;
-               dc.conn = op->o_conn;
-               dc.rs = rs;
-               dc.ctx = "bindDN";
+               /* better than nothing :) */
+               if ( timeout == 0 ) {
+                       if ( mi->mi_idle_timeout ) {
+                               timeout = mi->mi_idle_timeout;
 
-               if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
-                       rs->sr_text = "DN rewrite error";
-                       rs->sr_err = LDAP_OTHER;
-                       return rs->sr_err;
+                       } else if ( mi->mi_conn_ttl ) {
+                               timeout = mi->mi_conn_ttl;
+                       }
                }
 
-       } else {
-               mdn = op->o_req_dn;
-       }
-
-       /* FIXME: this fixes the bind problem right now; we need
-        * to use the asynchronous version to get the "matched"
-        * and more in case of failure ... */
-       /* FIXME: should we check if at least some of the op->o_ctrls
-        * can/should be passed? */
-rebind:;
-       rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
-                       LDAP_SASL_SIMPLE, &op->orb_cred,
-                       op->o_ctrls, NULL, &msgid );
-       if ( rs->sr_err == LDAP_SUCCESS ) {
-               LDAPMessage     *res;
-               struct timeval  tv;
-               int             rc;
-               int             nretries = mt->mt_nretries;
-               char            buf[ SLAP_TEXT_BUFLEN ];
+               if ( timeout ) {
+                       stoptime = op->o_time + timeout;
+               }
 
                LDAP_BACK_TV_SET( &tv );
 
@@ -336,16 +368,18 @@ rebind:;
                 * handle response!!!
                 */
 retry:;
-               switch ( ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
+               rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
+               switch ( rc ) {
                case 0:
-                       snprintf( buf, sizeof( buf ),
-                               "ldap_result=0 nretries=%d%s",
-                               nretries, rebinding ? " rebinding" : "" );
+#if 0
                        Debug( LDAP_DEBUG_ANY,
-                               "%s meta_back_single_bind[%d]: %s.\n",
-                               op->o_log_prefix, candidate, buf );
+                               "%s meta_back_bind_op_result[%d]: ldap_result=0 nretries=%d.\n",
+                               op->o_log_prefix, candidate, nretries );
+#endif
 
-                       if ( nretries != META_RETRY_NEVER ) {
+                       if ( nretries != META_RETRY_NEVER 
+                               || ( timeout && slap_get_time() <= stoptime ) )
+                       {
                                ldap_pvt_thread_yield();
                                if ( nretries > 0 ) {
                                        nretries--;
@@ -354,50 +388,43 @@ retry:;
                                goto retry;
                        }
 
-                       rs->sr_err = LDAP_BUSY;
-                       if ( rebinding ) {
-                               ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
-                               break;
-                       }
+                       /* don't let anyone else use this handler,
+                        * because there's a pending bind that will not
+                        * be acknowledged */
+                       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+                       assert( LDAP_BACK_CONN_BINDING( msc ) );
 
-                       /* FIXME: some times the request times out
-                        * while the other party is not willing to
-                        * send a response any more.  Give it a second
-                        * chance with a freshly bound connection */
-                       rebinding = 1;
-                       nretries = mt->mt_nretries;
-                       /* fallthru */
+#ifdef DEBUG_205
+                       Debug( LDAP_DEBUG_ANY, "### %s meta_back_bind_op_result ldap_unbind_ext[%d] ld=%p\n",
+                               op->o_log_prefix, candidate, (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+                       meta_clear_one_candidate( op, mc, candidate );
+                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+                       rs->sr_err = timeout_err;
+                       rs->sr_text = timeout_text;
+                       break;
 
                case -1:
-                       ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
+                       ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE,
                                &rs->sr_err );
 
-                       if ( rebinding ) {
-                               ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
-                       }
-
                        snprintf( buf, sizeof( buf ),
                                "err=%d (%s) nretries=%d",
                                rs->sr_err, ldap_err2string( rs->sr_err ), nretries );
                        Debug( LDAP_DEBUG_ANY,
-                               "### %s meta_back_single_bind[%d]: %s.\n",
+                               "### %s meta_back_bind_op_result[%d]: %s.\n",
                                op->o_log_prefix, candidate, buf );
-
-                       rc = slap_map_api2result( rs );
-                       if ( rs->sr_err == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
-                               rc = meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND );
-                               if ( rc ) {
-                                       if ( nretries > 0 ) {
-                                               nretries--;
-                                       }
-                                       ldap_pvt_thread_yield();
-                                       goto rebind;
-                               }
-                               goto return_results;
-                       }
                        break;
 
                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;
+                       }
+
+                       /* FIXME: matched? referrals? response controls? */
                        rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
                                        NULL, NULL, NULL, NULL, 1 );
                        if ( rc != LDAP_SUCCESS ) {
@@ -407,22 +434,102 @@ retry:;
                }
        }
 
+       rs->sr_err = slap_map_api2result( rs );
+
+       Debug( LDAP_DEBUG_TRACE,
+               "<<< %s meta_back_bind_op_result[%d] err=%d\n",
+               op->o_log_prefix, candidate, rs->sr_err );
+
+       return rs->sr_err;
+}
+
+/*
+ * meta_back_single_bind
+ *
+ * attempts to perform a bind with creds
+ */
+static int
+meta_back_single_bind(
+       Operation               *op,
+       SlapReply               *rs,
+       metaconn_t              *mc,
+       int                     candidate )
+{
+       metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
+       struct berval           mdn = BER_BVNULL;
+       metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
+       int                     msgid;
+       dncookie                dc;
+       
+       if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+               ch_free( msc->msc_bound_ndn.bv_val );
+               BER_BVZERO( &msc->msc_bound_ndn );
+       }
+
+       if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+               /* destroy sensitive data */
+               memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+               ch_free( msc->msc_cred.bv_val );
+               BER_BVZERO( &msc->msc_cred );
+       }
+
+       /*
+        * Rewrite the bind dn if needed
+        */
+       dc.target = mt;
+       dc.conn = op->o_conn;
+       dc.rs = rs;
+       dc.ctx = "bindDN";
+
+       if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
+               rs->sr_text = "DN rewrite error";
+               rs->sr_err = LDAP_OTHER;
+               return rs->sr_err;
+       }
+
+       /* FIXME: this fixes the bind problem right now; we need
+        * to use the asynchronous version to get the "matched"
+        * and more in case of failure ... */
+       /* FIXME: should we check if at least some of the op->o_ctrls
+        * can/should be passed? */
+       rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
+                       LDAP_SASL_SIMPLE, &op->orb_cred,
+                       op->o_ctrls, NULL, &msgid );
+       meta_back_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND );
        if ( rs->sr_err != LDAP_SUCCESS ) {
-               rs->sr_err = slap_map_api2result( rs );
                goto return_results;
        }
 
-       ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn );
+       /* If defined, proxyAuthz will be used also when
+        * back-ldap is the authorizing backend; for this
+        * purpose, a successful bind is followed by a
+        * bind with the configured identity assertion */
+       /* NOTE: use with care */
+       if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+               meta_back_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR );
+               if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
+                       goto return_results;
+               }
+               goto cache_refresh;
+       }
+
+       ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
        LDAP_BACK_CONN_ISBOUND_SET( msc );
        mc->mc_authz_target = candidate;
 
        if ( LDAP_BACK_SAVECRED( mi ) ) {
+               if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+                       memset( msc->msc_cred.bv_val, 0,
+                               msc->msc_cred.bv_len );
+               }
                ber_bvreplace( &msc->msc_cred, &op->orb_cred );
-               ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind_f, msc );
+               ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
        }
 
+cache_refresh:;
        if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
-                       && op->o_req_ndn.bv_len != 0 )
+                       && !BER_BVISEMPTY( &op->o_req_ndn ) )
        {
                ( void )meta_dncache_update_entry( &mi->mi_cache,
                                &op->o_req_ndn, candidate );
@@ -433,6 +540,10 @@ return_results:;
                free( mdn.bv_val );
        }
 
+       if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+               meta_back_quarantine( op, rs, candidate );
+       }
+
        return rs->sr_err;
 }
 
@@ -450,180 +561,59 @@ meta_back_single_dobind(
        int                     dolock )
 {
        metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
-       metatarget_t            *mt = &mi->mi_targets[ candidate ];
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
        metaconn_t              *mc = *mcp;
        metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
        int                     rc;
        static struct berval    cred = BER_BVC( "" );
-       int                     msgid,
-                               rebinding = 0,
-                               save_nretries = nretries;
+       int                     msgid;
 
        assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
 
-       /*
-        * meta_back_single_dobind() calls meta_back_single_bind()
-        * if required.
-        */
-       if ( be_isroot( op ) && !BER_BVISNULL( &mi->mi_targets[ candidate ].mt_pseudorootdn ) )
+       /* NOTE: this obsoletes pseudorootdn */
+       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 ) ) )
        {
-               Operation       op2 = *op;
-
-               op2.o_tag = LDAP_REQ_BIND;
-               op2.o_req_dn = mi->mi_targets[ candidate ].mt_pseudorootdn;
-               op2.o_req_ndn = mi->mi_targets[ candidate ].mt_pseudorootdn;
-               op2.orb_cred = mi->mi_targets[ candidate ].mt_pseudorootpw;
-               op2.orb_method = LDAP_AUTH_SIMPLE;
-
-               rc = meta_back_single_bind( &op2, rs, *mcp, candidate, 0 );
+               (void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok );
+               rc = rs->sr_err;
                goto done;
        }
 
-       /*
-        * Otherwise an anonymous bind is performed
-        * (note: if the target was already bound, the anonymous
-        * bind clears the previous bind).
-        */
-       if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
-               ber_memfree( msc->msc_bound_ndn.bv_val );
-               BER_BVZERO( &msc->msc_bound_ndn );
-       }
-               
-       if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &msc->msc_cred ) ) {
-               /* destroy sensitive data */
-               memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
-               ber_memfree( msc->msc_cred.bv_val );
-               BER_BVZERO( &msc->msc_cred );
-       }
-
        /* FIXME: should we check if at least some of the op->o_ctrls
         * can/should be passed? */
-rebind:;
-       rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
+       rs->sr_err = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
                        NULL, NULL, &msgid );
-       if ( rc == LDAP_SUCCESS ) {
-               LDAPMessage     *res;
-               struct timeval  tv;
-               char            buf[ SLAP_TEXT_BUFLEN ];
-
-               LDAP_BACK_TV_SET( &tv );
-
-               /*
-                * handle response!!!
-                */
-retry:;
-               switch ( ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
-               case 0:
-                       snprintf( buf, sizeof( buf ),
-                               "ldap_result=0 nretries=%d%s",
-                               nretries, rebinding ? " rebinding" : "" );
-                       Debug( LDAP_DEBUG_ANY,
-                               "%s meta_back_single_dobind[%d]: %s.\n",
-                               op->o_log_prefix, candidate, buf );
-
-                       if ( nretries != META_RETRY_NEVER ) {
-                               ldap_pvt_thread_yield();
-                               if ( nretries > 0 ) {
-                                       nretries--;
-                               }
-                               tv = mt->mt_bind_timeout;
-                               goto retry;
-                       }
-
-                       rc = LDAP_BUSY;
-                       if ( rebinding ) {
-                               ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
-                               break;
-                       }
-
-                       /* FIXME: some times the request times out
-                        * while the other party is not willing to
-                        * send a response any more.  Give it a second
-                        * chance with a freshly bound connection */
-                       rebinding = 1;
-                       nretries = save_nretries;
-                       /* fallthru */
-
-               case -1:
-                       ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
-                               &rs->sr_err );
-
-                       if ( rebinding ) {
-                               ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
-                       }
-
-                       snprintf( buf, sizeof( buf ),
-                               "err=%d (%s) nretries=%d",
-                               rs->sr_err, ldap_err2string( rs->sr_err ), nretries );
-                       Debug( LDAP_DEBUG_ANY,
-                               "### %s meta_back_single_dobind[%d]: %s.\n",
-                               op->o_log_prefix, candidate, buf );
-
-                       rc = slap_map_api2result( rs );
-                       if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
-                               if ( dolock ) {
-                                       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
-                               }
-
-                               if ( mc->mc_refcnt == 1 ) {
-                                       meta_clear_one_candidate( msc );
-                                       LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
-
-                                       ( 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 = meta_back_init_one_conn( op, rs,
-                                               mt, mc, candidate,
-                                               LDAP_BACK_CONN_ISPRIV( mc ),
-                                               LDAP_BACK_DONTSEND );
-                                       LDAP_BACK_CONN_BINDING_SET( msc );
-
-                               } else {
-                                       /* can't do anything about it */
-                                       rc = LDAP_UNAVAILABLE;
-                               }
-
-                               if ( dolock ) {
-                                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
-                               }
-
-                               if ( rc == LDAP_SUCCESS ) {
-                                       ldap_pvt_thread_yield();
-                                       if ( nretries > 0 ) {
-                                               nretries--;
-                                       }
-                                       goto rebind;
-                               }
-                       }
-                       break;
-
-               default:
-                       rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
-                                       NULL, NULL, NULL, NULL, 1 );
-                       if ( rc == LDAP_SUCCESS ) {
-                               rc = slap_map_api2result( rs );
-                       }
-                       break;
-               }
-
-       } else {
-               rs->sr_err = rc;
-               rc = slap_map_api2result( rs );
-       }
+       rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok );
 
 done:;
        rs->sr_err = rc;
-       if ( rc != LDAP_SUCCESS && META_BACK_ONERR_STOP( mi ) ) {
+       if ( rc != LDAP_SUCCESS ) {
+               if ( dolock ) {
+                       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+               }
                LDAP_BACK_CONN_BINDING_CLEAR( msc );
-               meta_back_release_conn_lock( op, mc, 1, dolock );
-               *mcp = NULL;
+               if ( META_BACK_ONERR_STOP( mi ) ) {
+                       LDAP_BACK_CONN_TAINTED_SET( mc );
+                       meta_back_release_conn_lock( op, mc, 0 );
+                       *mcp = NULL;
+               }
+               if ( dolock ) {
+                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+               }
 
-               if ( sendok & LDAP_BACK_SENDERR ) {
+               if ( META_BACK_ONERR_STOP( mi ) && ( sendok & LDAP_BACK_SENDERR ) ) {
                        send_ldap_result( op, rs );
                }
        }
 
+       if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+               meta_back_quarantine( op, rs, candidate );
+       }
+
        return rc;
 }
 
@@ -652,7 +642,7 @@ meta_back_dobind(
        Debug( LDAP_DEBUG_TRACE,
                "%s meta_back_dobind: conn=%ld%s\n",
                op->o_log_prefix,
-               LDAP_BACK_PCONN_ID( mc->mc_conn ),
+               LDAP_BACK_PCONN_ID( mc ),
                isroot ? " (isroot)" : "" );
 
        /*
@@ -664,15 +654,14 @@ meta_back_dobind(
        }
 
        for ( i = 0; i < mi->mi_ntargets; i++ ) {
-               metatarget_t            *mt = &mi->mi_targets[ i ];
+               metatarget_t            *mt = mi->mi_targets[ i ];
                metasingleconn_t        *msc = &mc->mc_conns[ i ];
-               int                     rc, do_retry = 1;
-               char                    *rootdn = NULL;
+               int                     rc;
 
                /*
                 * Not a candidate
                 */
-               if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
+               if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
                        continue;
                }
 
@@ -684,22 +673,25 @@ meta_back_dobind(
 
 retry_binding:;
                ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
-               if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
+               if ( LDAP_BACK_CONN_ISBOUND( msc )
+                       || ( LDAP_BACK_CONN_ISANON( msc )
+                               && mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) )
+               {
                        ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
                        ++bound;
                        continue;
 
-               } else if ( LDAP_BACK_CONN_BINDING( msc ) ) {
+               } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) )
+               {
                        ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
                        ldap_pvt_thread_yield();
                        goto retry_binding;
 
-               } else {
-                       LDAP_BACK_CONN_BINDING_SET( msc );
-                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
-               } 
+               }
+
+               LDAP_BACK_CONN_BINDING_SET( msc );
+               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
 
-retry:;
                rc = meta_back_single_dobind( op, rs, &mc, i,
                        LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
                /*
@@ -716,14 +708,19 @@ retry:;
                        }
 
 
-                       if ( rc == LDAP_UNAVAILABLE && do_retry ) {
-                               do_retry = 0;
+                       if ( rc == LDAP_UNAVAILABLE ) {
+                               /* FIXME: meta_back_retry() already re-calls
+                                * meta_back_single_dobind() */
                                if ( meta_back_retry( op, rs, &mc, i, sendok ) ) {
-                                       goto retry;
+                                       goto retry_ok;
+                               }
+
+                               if ( mc != NULL ) {
+                                       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+                                       LDAP_BACK_CONN_BINDING_CLEAR( msc );
+                                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                                       meta_back_release_conn( op, mc );
                                }
-                               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
-                               LDAP_BACK_CONN_BINDING_CLEAR( msc );
-                               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
 
                                return 0;
                        }
@@ -734,7 +731,7 @@ retry:;
 
                        snprintf( buf, sizeof( buf ),
                                "meta_back_dobind[%d]: (%s) err=%d (%s).",
-                               i, rootdn ? rootdn : "anonymous",
+                               i, isroot ? op->o_bd->be_rootdn.bv_val : "anonymous",
                                rc, ldap_err2string( rc ) );
                        Debug( LDAP_DEBUG_ANY,
                                "%s %s\n",
@@ -756,16 +753,17 @@ retry:;
 
                        continue;
                } /* else */
-               
+
+retry_ok:;
                Debug( LDAP_DEBUG_TRACE,
                        "%s meta_back_dobind[%d]: "
                        "(%s)\n",
                        op->o_log_prefix, i,
-                       rootdn ? rootdn : "anonymous" );
+                       isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" );
 
                ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
                LDAP_BACK_CONN_BINDING_CLEAR( msc );
-               if ( rootdn ) {
+               if ( isroot ) {
                        LDAP_BACK_CONN_ISBOUND_SET( msc );
                } else {
                        LDAP_BACK_CONN_ISANON_SET( msc );
@@ -777,7 +775,7 @@ retry:;
 done:;
        Debug( LDAP_DEBUG_TRACE,
                "%s meta_back_dobind: conn=%ld bound=%d\n",
-               op->o_log_prefix, LDAP_BACK_PCONN_ID( mc->mc_conn ), bound );
+               op->o_log_prefix, LDAP_BACK_PCONN_ID( mc ), bound );
 
        if ( bound == 0 ) {
                meta_back_release_conn( op, mc );
@@ -802,7 +800,7 @@ send_err:;
  * This is a callback used for chasing referrals using the same
  * credentials as the original user on this session.
  */
-static int 
+int 
 meta_back_default_rebind(
        LDAP                    *ld,
        LDAP_CONST char         *url,
@@ -817,68 +815,214 @@ meta_back_default_rebind(
                        NULL, NULL, NULL );
 }
 
+int
+meta_back_cancel(
+       metaconn_t              *mc,
+       Operation               *op,
+       SlapReply               *rs,
+       ber_int_t               msgid,
+       int                     candidate,
+       ldap_back_send_t        sendok )
+{
+       metainfo_t              *mi = (metainfo_t *)op->o_bd->be_private;
+
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
+       metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
+
+       int                     rc = LDAP_OTHER;
+
+       Debug( LDAP_DEBUG_TRACE, ">>> %s meta_back_cancel[%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_CANCEL( mt ) ) {
+               rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
+
+       } else {
+               assert( 0 );
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<<< %s meta_back_cancel[%d] err=%d\n",
+               op->o_log_prefix, candidate, rc );
+
+       return rc;
+}
+
+
+
 /*
  * FIXME: error return must be handled in a cleaner way ...
  */
 int
 meta_back_op_result(
-       metaconn_t      *mc,
-       Operation       *op,
-       SlapReply       *rs,
-       int             candidate )
+       metaconn_t              *mc,
+       Operation               *op,
+       SlapReply               *rs,
+       int                     candidate,
+       ber_int_t               msgid,
+       time_t                  timeout,
+       ldap_back_send_t        sendok )
 {
-       metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
+       metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
+
+       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 );
 
-       int                     i,
-                               rerr = LDAP_SUCCESS;
-       char                    *rmsg = NULL,
-                               *rmatch = NULL;
-       const char              *save_rmsg = NULL,
-                               *save_rmatch = NULL;
-       void                    *rmatch_ctx = NULL;
+       rs->sr_text = NULL;
+       rs->sr_matched = NULL;
+       rs->sr_ref = NULL;
+       rs->sr_ctrls = NULL;
 
        if ( candidate != META_TARGET_NONE ) {
+               metatarget_t            *mt = mi->mi_targets[ candidate ];
                metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
 
-               rs->sr_err = LDAP_SUCCESS;
+#define        ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
+
+               if ( 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;
+                                       }
 
-               ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &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_ERROR_STRING, &rmsg );
-                       if ( rmsg != NULL && rmsg[ 0 ] == '\0' ) {
-                               ldap_memfree( rmsg );
-                               rmsg = NULL;
+                               } else {
+                                       timeout = mt->mt_timeout[ opidx ];
+                               }
+                       }
+
+                       /* better than nothing :) */
+                       if ( timeout == 0 ) {
+                               if ( mi->mi_idle_timeout ) {
+                                       timeout = mi->mi_idle_timeout;
+
+                               } else if ( mi->mi_conn_ttl ) {
+                                       timeout = mi->mi_conn_ttl;
+                               }
                        }
 
-                       ldap_get_option( msc->msc_ld,
-                                       LDAP_OPT_MATCHED_DN, &rmatch );
-                       if ( rmatch != NULL && rmatch[ 0 ] == '\0' ) {
-                               ldap_memfree( rmatch );
-                               rmatch = NULL;
+                       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)meta_back_cancel( mc, op, rs, msgid, candidate, sendok );
+                                       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;
+                               rs->sr_text = text;
+                               if ( rc != LDAP_SUCCESS ) {
+                                       rs->sr_err = rc;
+                               }
+                               if ( refs != NULL ) {
+                                       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 ] );
+                               }
+                               if ( ctrls != NULL ) {
+                                       rs->sr_ctrls = ctrls;
+                               }
                        }
 
-                       rerr = rs->sr_err = slap_map_api2result( rs );
+                       assert( res == NULL );
+               }
+
+               /* if the error in the reply structure is not
+                * LDAP_SUCCESS, try to map it from client 
+                * to server error */
+               if ( !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 ) {
 
-                       Debug(LDAP_DEBUG_ANY,
-                                       "==> meta_back_op_result: target"
-                                       " <%d> sending msg \"%s\""
-                                       " (matched \"%s\")\n", 
-                                       candidate, ( rmsg ? rmsg : "" ),
-                                       ( rmatch ? rmatch : "" ) );
+                               /* record the (massaged) matched
+                                * DN into the reply structure */
+                               rs->sr_matched = matched;
+                       }
+               }
+
+               if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+                       meta_back_quarantine( op, rs, candidate );
                }
 
        } else {
+               int     i,
+                       err = rs->sr_err;
+
                for ( i = 0; i < mi->mi_ntargets; i++ ) {
                        metasingleconn_t        *msc = &mc->mc_conns[ i ];
-                       char                    *msg = NULL;
-                       char                    *match = NULL;
+                       char                    *xtext = NULL;
+                       char                    *xmatched = NULL;
 
                        rs->sr_err = LDAP_SUCCESS;
 
@@ -891,89 +1035,415 @@ meta_back_op_result(
                                 * positive result ...
                                 */
                                ldap_get_option( msc->msc_ld,
-                                               LDAP_OPT_ERROR_STRING, &msg );
-                               if ( msg != NULL && msg[ 0 ] == '\0' ) {
-                                       ldap_memfree( msg );
-                                       msg = NULL;
+                                               LDAP_OPT_ERROR_STRING, &xtext );
+                               if ( xtext != NULL && xtext [ 0 ] == '\0' ) {
+                                       ldap_memfree( xtext );
+                                       xtext = NULL;
                                }
 
                                ldap_get_option( msc->msc_ld,
-                                               LDAP_OPT_MATCHED_DN, &match );
-                               if ( match != NULL && match[ 0 ] == '\0' ) {
-                                       ldap_memfree( match );
-                                       match = NULL;
+                                               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,
-                                               "==> meta_back_op_result: target"
-                                               " <%d> sending msg \"%s\""
-                                               " (matched \"%s\")\n", 
-                                               i, ( msg ? msg : "" ),
-                                               ( match ? match : "" ) );
-       
+                               if ( StatslogTest( LDAP_DEBUG_ANY ) ) {
+                                       char    buf[ SLAP_TEXT_BUFLEN ];
+
+                                       snprintf( buf, sizeof( buf ),
+                                               "meta_back_op_result[%d] "
+                                               "err=%d text=\"%s\" matched=\"%s\"", 
+                                               i, rs->sr_err,
+                                               ( xtext ? xtext : "" ),
+                                               ( xmatched ? xmatched : "" ) );
+                                       Debug( LDAP_DEBUG_ANY, "%s %s.\n",
+                                               op->o_log_prefix, buf, 0 );
+                               }
+
                                /*
                                 * FIXME: need to rewrite "match" (need rwinfo)
                                 */
                                switch ( rs->sr_err ) {
                                default:
-                                       rerr = rs->sr_err;
-                                       if ( msg != NULL ) {
-                                               if ( rmsg ) {
-                                                       ldap_memfree( rmsg );
+                                       err = rs->sr_err;
+                                       if ( xtext != NULL ) {
+                                               if ( text ) {
+                                                       ldap_memfree( text );
                                                }
-                                               rmsg = msg;
-                                               msg = NULL;
+                                               text = xtext;
+                                               xtext = NULL;
                                        }
-                                       if ( match != NULL ) {
-                                               if ( rmatch ) {
-                                                       ldap_memfree( rmatch );
+                                       if ( xmatched != NULL ) {
+                                               if ( matched ) {
+                                                       ldap_memfree( matched );
                                                }
-                                               rmatch = match;
-                                               match = NULL;
+                                               matched = xmatched;
+                                               xmatched = NULL;
                                        }
                                        break;
                                }
 
-                               if ( msg ) {
-                                       ldap_memfree( msg );
+                               if ( xtext ) {
+                                       ldap_memfree( xtext );
                                }
        
-                               if ( match ) {
-                                       ldap_memfree( match );
+                               if ( xmatched ) {
+                                       ldap_memfree( xmatched );
                                }
                        }
+
+                       if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
+                               meta_back_quarantine( op, rs, i );
+                       }
+               }
+
+               if ( err != LDAP_SUCCESS ) {
+                       rs->sr_err = err;
                }
        }
-       
-       rs->sr_err = rerr;
-       if ( rmsg != NULL ) {
-               save_rmsg = rs->sr_text;
-               rs->sr_text = rmsg;
-       }
-       if ( rmatch != NULL ) {
+
+       if ( matched != NULL ) {
                struct berval   dn, pdn;
 
-               ber_str2bv( rmatch, 0, 0, &dn );
+               ber_str2bv( matched, 0, 0, &dn );
                if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
-                       ldap_memfree( rmatch );
-                       rmatch_ctx = op->o_tmpmemctx;
-                       rmatch = pdn.bv_val;
+                       ldap_memfree( matched );
+                       matched_ctx = op->o_tmpmemctx;
+                       matched = pdn.bv_val;
                }
-               save_rmatch = rs->sr_matched;
-               rs->sr_matched = rmatch;
+               rs->sr_matched = matched;
+       }
+
+       if ( op->o_conn &&
+               ( ( sendok & LDAP_BACK_SENDOK ) 
+                       || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
+       {
+               send_ldap_result( op, rs );
        }
-       send_ldap_result( op, rs );
-       if ( rmsg != NULL ) {
-               ber_memfree( rmsg );
-               rs->sr_text = save_rmsg;
+       if ( matched ) {
+               op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
        }
-       if ( rmatch != NULL ) {
-               ber_memfree_x( rmatch, rmatch_ctx );
-               rs->sr_matched = save_rmatch;
+       if ( text ) {
+               ldap_memfree( text );
+       }
+       if ( rs->sr_ref ) {
+               assert( refs != NULL );
+               ber_memvfree( (void **)refs );
+               op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+       }
+       if ( ctrls ) {
+               assert( rs->sr_ctrls != NULL );
+               ldap_controls_free( ctrls );
        }
 
-       return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
+       rs->sr_text = save_text;
+       rs->sr_matched = save_matched;
+       rs->sr_ref = save_ref;
+       rs->sr_ctrls = save_ctrls;
+
+       return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
 }
 
+/*
+ * meta_back_proxy_authz_cred()
+ *
+ * prepares credentials & method for meta_back_proxy_authz_bind();
+ * or, if method is SASL, performs the SASL bind directly.
+ */
+int
+meta_back_proxy_authz_cred(
+       metaconn_t              *mc,
+       int                     candidate,
+       Operation               *op,
+       SlapReply               *rs,
+       ldap_back_send_t        sendok,
+       struct berval           *binddn,
+       struct berval           *bindcred,
+       int                     *method )
+{
+       metainfo_t              *mi = (metainfo_t *)op->o_bd->be_private;
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
+       metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
+       struct berval           ndn;
+       int                     dobind = 0;
+
+       /* don't proxyAuthz if protocol is not LDAPv3 */
+       switch ( mt->mt_version ) {
+       case LDAP_VERSION3:
+               break;
+
+       case 0:
+               if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+                       break;
+               }
+               /* fall thru */
+
+       default:
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               if ( sendok & LDAP_BACK_SENDERR ) {
+                       send_ldap_result( op, rs );
+               }
+               LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+               goto done;
+       }
+
+       if ( op->o_tag == LDAP_REQ_BIND ) {
+               ndn = op->o_req_ndn;
+
+       } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+               ndn = op->o_conn->c_ndn;
+
+       } else {
+               ndn = op->o_ndn;
+       }
+
+       /*
+        * FIXME: we need to let clients use proxyAuthz
+        * otherwise we cannot do symmetric pools of servers;
+        * we have to live with the fact that a user can
+        * authorize itself as any ID that is allowed
+        * by the authzTo directive of the "proxyauthzdn".
+        */
+       /*
+        * NOTE: current Proxy Authorization specification
+        * and implementation do not allow proxy authorization
+        * control to be provided with Bind requests
+        */
+       /*
+        * if no bind took place yet, but the connection is bound
+        * and the "proxyauthzdn" is set, then bind as 
+        * "proxyauthzdn" and explicitly add the proxyAuthz 
+        * control to every operation with the dn bound 
+        * to the connection as control value.
+        */
+
+       /* bind as proxyauthzdn only if no idassert mode
+        * is requested, or if the client's identity
+        * is authorized */
+       switch ( mt->mt_idassert_mode ) {
+       case LDAP_BACK_IDASSERT_LEGACY:
+               if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
+                       if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) )
+                       {
+                               *binddn = mt->mt_idassert_authcDN;
+                               *bindcred = mt->mt_idassert_passwd;
+                               dobind = 1;
+                       }
+               }
+               break;
+
+       default:
+               /* NOTE: rootdn can always idassert */
+               if ( BER_BVISNULL( &ndn ) && mt->mt_idassert_authz == NULL ) {
+                       if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+                               rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
+                               if ( sendok & LDAP_BACK_SENDERR ) {
+                                       send_ldap_result( op, rs );
+                               }
+                               LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+
+                       } else {
+                               rs->sr_err = LDAP_SUCCESS;
+                               *binddn = slap_empty_bv;
+                               *bindcred = slap_empty_bv;
+                               break;
+                       }
+
+                       goto done;
+
+               } else if ( mt->mt_idassert_authz && !be_isroot( op ) ) {
+                       struct berval authcDN;
+
+                       if ( BER_BVISNULL( &ndn ) ) {
+                               authcDN = slap_empty_bv;
+
+                       } else {
+                               authcDN = ndn;
+                       }       
+                       rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz,
+                                       &authcDN, &authcDN );
+                       if ( rs->sr_err != LDAP_SUCCESS ) {
+                               if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+                                       if ( sendok & LDAP_BACK_SENDERR ) {
+                                               send_ldap_result( op, rs );
+                                       }
+                                       LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+
+                               } else {
+                                       rs->sr_err = LDAP_SUCCESS;
+                                       *binddn = slap_empty_bv;
+                                       *bindcred = slap_empty_bv;
+                                       break;
+                               }
+
+                               goto done;
+                       }
+               }
+
+               *binddn = mt->mt_idassert_authcDN;
+               *bindcred = mt->mt_idassert_passwd;
+               dobind = 1;
+               break;
+       }
+
+       if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+               void            *defaults = NULL;
+               struct berval   authzID = BER_BVNULL;
+               int             freeauthz = 0;
+
+               /* if SASL supports native authz, prepare for it */
+               if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
+                               ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+               {
+                       switch ( mt->mt_idassert_mode ) {
+                       case LDAP_BACK_IDASSERT_OTHERID:
+                       case LDAP_BACK_IDASSERT_OTHERDN:
+                               authzID = mt->mt_idassert_authzID;
+                               break;
+
+                       case LDAP_BACK_IDASSERT_ANONYMOUS:
+                               BER_BVSTR( &authzID, "dn:" );
+                               break;
+
+                       case LDAP_BACK_IDASSERT_SELF:
+                               if ( BER_BVISNULL( &ndn ) ) {
+                                       /* connection is not authc'd, so don't idassert */
+                                       BER_BVSTR( &authzID, "dn:" );
+                                       break;
+                               }
+                               authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
+                               authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
+                               AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
+                               AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
+                                               ndn.bv_val, ndn.bv_len + 1 );
+                               freeauthz = 1;
+                               break;
+
+                       default:
+                               break;
+                       }
+               }
+
+               if ( mt->mt_idassert_secprops != NULL ) {
+                       rs->sr_err = ldap_set_option( msc->msc_ld,
+                               LDAP_OPT_X_SASL_SECPROPS,
+                               (void *)mt->mt_idassert_secprops );
+
+                       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,
+                               mt->mt_idassert_authcID.bv_val,
+                               mt->mt_idassert_passwd.bv_val,
+                               authzID.bv_val );
+
+               rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val,
+                               mt->mt_idassert_sasl_mech.bv_val, NULL, NULL,
+                               LDAP_SASL_QUIET, lutil_sasl_interact,
+                               defaults );
+
+               rs->sr_err = slap_map_api2result( rs );
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+                       if ( sendok & LDAP_BACK_SENDERR ) {
+                               send_ldap_result( op, rs );
+                       }
+
+               } else {
+                       LDAP_BACK_CONN_ISBOUND_SET( msc );
+               }
+
+               lutil_sasl_freedefs( defaults );
+               if ( freeauthz ) {
+                       slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
+               }
+
+               goto done;
+#endif /* HAVE_CYRUS_SASL */
+       }
+
+       *method = mt->mt_idassert_authmethod;
+       switch ( mt->mt_idassert_authmethod ) {
+       case LDAP_AUTH_NONE:
+               BER_BVSTR( binddn, "" );
+               BER_BVSTR( bindcred, "" );
+               /* fallthru */
+
+       case LDAP_AUTH_SIMPLE:
+               break;
+
+       default:
+               /* unsupported! */
+               LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+               rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+               if ( sendok & LDAP_BACK_SENDERR ) {
+                       send_ldap_result( op, rs );
+               }
+               break;
+       }
+
+done:;
+       return rs->sr_err;
+}
+
+static int
+meta_back_proxy_authz_bind( metaconn_t *mc, int candidate, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+       metainfo_t              *mi = (metainfo_t *)op->o_bd->be_private;
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
+       metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
+       struct berval           binddn = BER_BVC( "" ),
+                               cred = BER_BVC( "" );
+       int                     method = LDAP_AUTH_NONE,
+                               rc;
+
+       rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
+       if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
+               int     msgid;
+
+               switch ( method ) {
+               case LDAP_AUTH_NONE:
+               case LDAP_AUTH_SIMPLE:
+                       rs->sr_err = ldap_sasl_bind( msc->msc_ld,
+                                       binddn.bv_val, LDAP_SASL_SIMPLE,
+                                       &cred, NULL, NULL, &msgid );
+                       rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok );
+                       if ( rc == LDAP_SUCCESS ) {
+                               /* set rebind stuff in case of successful proxyAuthz bind,
+                                * so that referral chasing is attempted using the right
+                                * identity */
+                               LDAP_BACK_CONN_ISBOUND_SET( msc );
+                               ber_bvreplace( &msc->msc_bound_ndn, &binddn );
+
+                               if ( LDAP_BACK_SAVECRED( mi ) ) {
+                                       if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+                                               memset( msc->msc_cred.bv_val, 0,
+                                                       msc->msc_cred.bv_len );
+                                       }
+                                       ber_bvreplace( &msc->msc_cred, &cred );
+                                       ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
+                               }
+                       }
+                       break;
+
+               default:
+                       assert( 0 );
+                       break;
+               }
+       }
+
+       return LDAP_BACK_CONN_ISBOUND( msc );
+}
index a7b2aef2da1a740a340f9807d7d236c3dffd91aa..7d3b6b7c522f4647810dfecc76159b7261f5372a 100644 (file)
  */
 int 
 meta_back_is_candidate(
-       struct berval   *nsuffix,
-       int             suffixscope,
-       BerVarray       subtree_exclude,
+       metatarget_t    *mt,
        struct berval   *ndn,
        int             scope )
 {
-       if ( dnIsSuffix( ndn, nsuffix ) ) {
-               if ( subtree_exclude ) {
+       if ( dnIsSuffix( ndn, &mt->mt_nsuffix ) ) {
+               if ( mt->mt_subtree_exclude ) {
                        int     i;
 
-                       for ( i = 0; !BER_BVISNULL( &subtree_exclude[ i ] ); i++ ) {
-                               if ( dnIsSuffix( ndn, &subtree_exclude[ i ] ) ) {
+                       for ( i = 0; !BER_BVISNULL( &mt->mt_subtree_exclude[ i ] ); i++ ) {
+                               if ( dnIsSuffix( ndn, &mt->mt_subtree_exclude[ i ] ) ) {
                                        return META_NOT_CANDIDATE;
                                }
                        }
                }
 
-               switch ( suffixscope ) {
+               switch ( mt->mt_scope ) {
                case LDAP_SCOPE_SUBTREE:
                default:
                        return META_CANDIDATE;
 
                case LDAP_SCOPE_SUBORDINATE:
-                       if ( ndn->bv_len > nsuffix->bv_len ) {
+                       if ( ndn->bv_len > mt->mt_nsuffix.bv_len ) {
                                return META_CANDIDATE;
                        }
                        break;
 
                /* nearly useless; not allowed by config */
                case LDAP_SCOPE_ONELEVEL:
-                       if ( ndn->bv_len > nsuffix->bv_len ) {
+                       if ( ndn->bv_len > mt->mt_nsuffix.bv_len ) {
                                struct berval   rdn = *ndn;
 
-                               rdn.bv_len -= nsuffix->bv_len
+                               rdn.bv_len -= mt->mt_nsuffix.bv_len
                                        + STRLENOF( "," );
                                if ( dnIsOneLevelRDN( &rdn ) ) {
                                        return META_CANDIDATE;
@@ -102,7 +100,7 @@ meta_back_is_candidate(
 
                /* nearly useless; not allowed by config */
                case LDAP_SCOPE_BASE:
-                       if ( ndn->bv_len == nsuffix->bv_len ) {
+                       if ( ndn->bv_len == mt->mt_nsuffix.bv_len ) {
                                return META_CANDIDATE;
                        }
                        break;
@@ -111,7 +109,7 @@ meta_back_is_candidate(
                return META_NOT_CANDIDATE;
        }
 
-       if ( scope == LDAP_SCOPE_SUBTREE && dnIsSuffix( nsuffix, ndn ) ) {
+       if ( scope == LDAP_SCOPE_SUBTREE && dnIsSuffix( &mt->mt_nsuffix, ndn ) ) {
                /*
                 * suffix longer than dn, but common part matches
                 */
@@ -136,12 +134,10 @@ meta_back_select_unique_candidate(
 {
        int     i, candidate = META_TARGET_NONE;
 
-       for ( i = 0; i < mi->mi_ntargets; ++i ) {
-               if ( meta_back_is_candidate( &mi->mi_targets[ i ].mt_nsuffix,
-                               mi->mi_targets[ i ].mt_scope,
-                               mi->mi_targets[ i ].mt_subtree_exclude,
-                               ndn, LDAP_SCOPE_BASE ) )
-               {
+       for ( i = 0; i < mi->mi_ntargets; i++ ) {
+               metatarget_t    *mt = mi->mi_targets[ i ];
+
+               if ( meta_back_is_candidate( mt, ndn, LDAP_SCOPE_BASE ) ) {
                        if ( candidate == META_TARGET_NONE ) {
                                candidate = i;
 
@@ -172,7 +168,7 @@ meta_clear_unused_candidates(
                if ( i == candidate ) {
                        continue;
                }
-               candidates[ i ].sr_tag = META_NOT_CANDIDATE;
+               META_CANDIDATE_RESET( &candidates[ i ] );
        }
 
        return 0;
@@ -185,9 +181,23 @@ meta_clear_unused_candidates(
  */
 int
 meta_clear_one_candidate(
-       metasingleconn_t        *msc )
+       Operation       *op,
+       metaconn_t      *mc,
+       int             candidate )
 {
-       if ( msc->msc_ld ) {
+       metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
+
+       if ( msc->msc_ld != NULL ) {
+
+#ifdef DEBUG_205
+               char    buf[ BUFSIZ ];
+
+               snprintf( buf, sizeof( buf ), "meta_clear_one_candidate ldap_unbind_ext[%d] mc=%p ld=%p",
+                       candidate, (void *)mc, (void *)msc->msc_ld );
+               Debug( LDAP_DEBUG_ANY, "### %s %s\n",
+                       op ? op->o_log_prefix : "", buf, 0 );
+#endif /* DEBUG_205 */
+
                ldap_unbind_ext( msc->msc_ld, NULL, NULL );
                msc->msc_ld = NULL;
        }
@@ -203,25 +213,8 @@ meta_clear_one_candidate(
                BER_BVZERO( &msc->msc_cred );
        }
 
-       return 0;
-}
-
-/*
- * meta_clear_candidates
- *
- * clears all candidates
- */
-int
-meta_clear_candidates( Operation *op, metaconn_t *mc )
-{
-       metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
-       int             c;
-
-       for ( c = 0; c < mi->mi_ntargets; c++ ) {
-               if ( mc->mc_conns[ c ].msc_ld != NULL ) {
-                       meta_clear_one_candidate( &mc->mc_conns[ c ] );
-               }
-       }
+       msc->msc_mscflags = 0;
 
        return 0;
 }
+
index 4dbd0157ea0a98227c6e5a794138fd5e955600a4..7cef5c040cb210ef444cb8da38d02d9cae64580b 100644 (file)
 int
 meta_back_compare( Operation *op, SlapReply *rs )
 {
-       metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
-       metaconn_t              *mc = NULL;
-       char                    *match = NULL,
-                               *err = NULL;
-       struct berval           mmatch = BER_BVNULL;
-       int                     ncandidates = 0,
-                               last = 0,
-                               i,
-                               count = 0,
-                               rc,
-                                       cres = LDAP_SUCCESS,
-                               rres = LDAP_SUCCESS,
-                               *msgid;
-       dncookie                dc;
-
-       SlapReply               *candidates = meta_back_candidates_get( op );
-
-       mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR );
+       metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t    *mt;
+       metaconn_t      *mc;
+       int             rc = 0;
+       int             candidate = -1;
+       struct berval   mdn = BER_BVNULL;
+       dncookie        dc;
+       struct berval   mapped_attr = op->orc_ava->aa_desc->ad_cname;
+       struct berval   mapped_value = op->orc_ava->aa_value;
+       int             msgid;
+       int             do_retry = 1;
+       LDAPControl     **ctrls = NULL;
+
+       mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
        if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
                return rs->sr_err;
        }
-       
-       msgid = ch_calloc( sizeof( int ), mi->mi_ntargets );
-       if ( msgid == NULL ) {
-               send_ldap_error( op, rs, LDAP_OTHER, NULL );
-               rc = LDAP_OTHER;
-               goto done;
-       }
+
+       assert( mc->mc_conns[ candidate ].msc_ld != NULL );
 
        /*
-        * start an asynchronous compare for each candidate target
+        * Rewrite the modify dn, if needed
         */
+       mt = mi->mi_targets[ candidate ];
+       dc.target = mt;
        dc.conn = op->o_conn;
        dc.rs = rs;
        dc.ctx = "compareDN";
 
-       for ( i = 0; i < mi->mi_ntargets; i++ ) {
-               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;
-
-               if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
-                       msgid[ i ] = -1;
-                       continue;
-               }
-
-               /*
-                * Rewrite the compare dn, if needed
-                */
-               dc.target = &mi->mi_targets[ i ];
-
-               switch ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
-               case LDAP_UNWILLING_TO_PERFORM:
-                       rc = 1;
-                       goto finish;
-
-               default:
-                       break;
-               }
-
-               /*
-                * if attr is objectClass, try to remap the value
-                */
-               if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass ) {
-                       ldap_back_map( &mi->mi_targets[ i ].mt_rwmap.rwm_oc,
-                                       &op->orc_ava->aa_value,
-                                       &mapped_value, BACKLDAP_MAP );
-
-                       if ( BER_BVISNULL( &mapped_value ) || mapped_value.bv_val[0] == '\0' ) {
-                               continue;
-                       }
-               /*
-                * else try to remap the attribute
-                */
-               } else {
-                       ldap_back_map( &mi->mi_targets[ i ].mt_rwmap.rwm_at,
-                               &op->orc_ava->aa_desc->ad_cname,
-                               &mapped_attr, BACKLDAP_MAP );
-                       if ( BER_BVISNULL( &mapped_attr ) || mapped_attr.bv_val[0] == '\0' ) {
-                               continue;
-                       }
+       switch ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
+       case LDAP_UNWILLING_TO_PERFORM:
+               rc = 1;
+               goto cleanup;
 
-                       if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
-                       {
-                               dc.ctx = "compareAttrDN";
-
-                               switch ( ldap_back_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value ) )
-                               {
-                               case LDAP_UNWILLING_TO_PERFORM:
-                                       rc = 1;
-                                       goto finish;
-
-                               default:
-                                       break;
-                               }
-                       }
-               }
-               
-               /*
-                * the compare op is spawned across the targets and the first
-                * that returns determines the result; a constraint on unicity
-                * of the result ought to be enforced
-                */
-                rc = ldap_compare_ext( mc->mc_conns[ i ].msc_ld, mdn.bv_val,
-                               mapped_attr.bv_val, &mapped_value,
-                               op->o_ctrls, NULL, &msgid[ i ] );
-
-               if ( mdn.bv_val != op->o_req_dn.bv_val ) {
-                       free( mdn.bv_val );
-                       BER_BVZERO( &mdn );
-               }
-
-               if ( mapped_attr.bv_val != op->orc_ava->aa_desc->ad_cname.bv_val ) {
-                       free( mapped_attr.bv_val );
-                       BER_BVZERO( &mapped_attr );
-               }
-
-               if ( mapped_value.bv_val != op->orc_ava->aa_value.bv_val ) {
-                       free( mapped_value.bv_val );
-                       BER_BVZERO( &mapped_value );
-               }
-
-               if ( rc != LDAP_SUCCESS ) {
-                       /* FIXME: what should we do with the error? */
-                       continue;
-               }
-
-               ++ncandidates;
+       default:
+               break;
        }
 
        /*
-        * wait for replies
+        * if attr is objectClass, try to remap the value
         */
-       for ( rc = 0, count = 0; ncandidates > 0; ) {
-
-               /*
-                * FIXME: should we check for abandon?
-                */
-               for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                       metasingleconn_t        *msc = &mc->mc_conns[ i ];
-                       int                     lrc;
-                       LDAPMessage             *res = NULL;
-                       struct timeval          tv;
-
-                       LDAP_BACK_TV_SET( &tv );
-
-                       if ( msgid[ i ] == -1 ) {
-                               continue;
-                       }
-
-                       lrc = ldap_result( msc->msc_ld, msgid[ i ],
-                                       LDAP_MSG_ALL, &tv, &res );
-
-                       if ( lrc == 0 ) {
-                               assert( res == NULL );
-                               continue;
-
-                       } else if ( lrc == -1 ) {
-                               /* we do not retry in this case;
-                                * only for unique operations... */
-                               ldap_get_option( msc->msc_ld,
-                                       LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
-                               rres = slap_map_api2result( rs );
-                               rres = rc;
-                               rc = -1;
-                               goto finish;
-
-                       } else if ( lrc == LDAP_RES_COMPARE ) {
-                               if ( count > 0 ) {
-                                       rres = LDAP_OTHER;
-                                       rc = -1;
-                                       goto finish;
-                               }
-
-                               rc = ldap_parse_result( msc->msc_ld, res,
-                                               &rs->sr_err,
-                                               NULL, NULL, NULL, NULL, 1 );
-                               if ( rc != LDAP_SUCCESS ) {
-                                       rres = rc;
-                                       rc = -1;
-                                       goto finish;
-                               }
-                               
-                               switch ( rs->sr_err ) {
-                               case LDAP_COMPARE_TRUE:
-                               case LDAP_COMPARE_FALSE:
-
-                                       /*
-                                        * true or false, got it;
-                                        * sending to cache ...
-                                        */
-                                       if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
-                                               ( void )meta_dncache_update_entry( &mi->mi_cache, &op->o_req_ndn, i );
-                                       }
-
-                                       count++;
-                                       rc = 0;
-                                       break;
-
-                               default:
-                                       rres = slap_map_api2result( rs );
+       if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass ) {
+               ldap_back_map( &mt->mt_rwmap.rwm_oc,
+                               &op->orc_ava->aa_value,
+                               &mapped_value, BACKLDAP_MAP );
 
-                                       if ( err != NULL ) {
-                                               free( err );
-                                       }
-                                       ldap_get_option( msc->msc_ld,
-                                               LDAP_OPT_ERROR_STRING, &err );
-
-                                       if ( match != NULL ) {
-                                               free( match );
-                                       }
-                                       ldap_get_option( msc->msc_ld,
-                                               LDAP_OPT_MATCHED_DN, &match );
-                                       
-                                       last = i;
-                                       break;
-                               }
-                               msgid[ i ] = -1;
-                               --ncandidates;
-
-                       } else {
-                               msgid[ i ] = -1;
-                               --ncandidates;
-                               if ( res ) {
-                                       ldap_msgfree( res );
-                               }
-                               break;
-                       }
+               if ( BER_BVISNULL( &mapped_value ) || BER_BVISEMPTY( &mapped_value ) ) {
+                       goto cleanup;
                }
-       }
-
-finish:;
 
        /*
-        * Rewrite the matched portion of the search base, if required
-        * 
-        * FIXME: only the last one gets caught!
+        * else try to remap the attribute
         */
-       if ( count == 1 ) {
-               if ( match != NULL ) {
-                       free( match );
-                       match = NULL;
+       } else {
+               ldap_back_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 ) ) {
+                       goto cleanup;
                }
-               
-               /*
-                * the result of the compare is assigned to the res code
-                * that will be returned
-                */
-               rres = cres;
-               
-               /*
-                * At least one compare failed with matched portion,
-                * and none was successful
-                */
-       } else if ( match != NULL && match[ 0 ] != '\0' ) {
-               struct berval matched, pmatched;
 
-               ber_str2bv( match, 0, 0, &matched );
+               if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+               {
+                       dc.ctx = "compareAttrDN";
 
-               dc.ctx = "matchedDN";
-               ldap_back_dn_massage( &dc, &matched, &mmatch );
-               if ( dnPretty( NULL, &mmatch, &pmatched, NULL ) == LDAP_SUCCESS ) {
-                       if ( mmatch.bv_val != match ) {
-                               free( mmatch.bv_val );
+                       switch ( ldap_back_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value ) )
+                       {
+                       case LDAP_UNWILLING_TO_PERFORM:
+                               rc = 1;
+                               goto cleanup;
+
+                       default:
+                               break;
                        }
-                       mmatch = pmatched;
                }
        }
 
-       if ( rres != LDAP_SUCCESS ) {
-               rs->sr_err = rres;
+retry:;
+       ctrls = op->o_ctrls;
+       rc = ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn,
+               mt->mt_version, &mt->mt_idassert, op, rs, &ctrls );
+       if ( rc != LDAP_SUCCESS ) {
+               send_ldap_result( op, rs );
+               goto cleanup;
        }
-       rs->sr_matched = mmatch.bv_val;
-       send_ldap_result( op, rs );
-       rs->sr_matched = NULL;
 
-       if ( match != NULL ) {
-               if ( mmatch.bv_val != match ) {
-                       free( mmatch.bv_val );
+       rs->sr_err = ldap_compare_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val,
+                       mapped_attr.bv_val, &mapped_value,
+                       ctrls, NULL, &msgid );
+
+       rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+               mt->mt_timeout[ SLAP_OP_COMPARE ], LDAP_BACK_SENDRESULT );
+       if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
+               do_retry = 0;
+               if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+                       /* if the identity changed, there might be need to re-authz */
+                       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
+                       goto retry;
                }
-               free( match );
        }
 
-       if ( msgid ) {
-               free( msgid );
+cleanup:;
+       (void)ldap_back_proxy_authz_ctrl_free( op, &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 ) {
+               free( mapped_value.bv_val );
        }
 
-done:;
-       meta_back_release_conn( op, mc );
+       if ( mc ) {
+               meta_back_release_conn( op, mc );
+       }
 
-       return rc;
+       return rs->sr_err;
 }
 
index e7e9ec3a68d1352b7988aa9b039a48de0fe8b908..56ca9dcc6f7e8505e3436b7945f674d22dae1e16 100644 (file)
 
 static int
 meta_back_new_target( 
-       metatarget_t    *mt )
+       metatarget_t    **mtp )
 {
-        struct ldapmapping     *mapping;
        char                    *rargv[ 3 ];
+       metatarget_t            *mt;
 
-       memset( mt, 0, sizeof( metatarget_t ) );
+       *mtp = NULL;
+
+       mt = ch_calloc( sizeof( metatarget_t ), 1 );
 
        mt->mt_rwmap.rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
        if ( mt->mt_rwmap.rwm_rw == NULL ) {
-                return -1;
+               ch_free( mt );
+               return -1;
        }
 
-
        /*
         * the filter rewrite as a string must be disabled
         * by default; it can be re-enabled by adding rules;
@@ -64,7 +66,16 @@ meta_back_new_target(
        rargv[ 2 ] = NULL;
        rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
 
-       ldap_back_map_init( &mt->mt_rwmap.rwm_at, &mapping );
+       ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
+
+       mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
+       mt->mt_idassert_authmethod = LDAP_AUTH_NONE;
+       mt->mt_idassert_tls = SB_TLS_DEFAULT;
+
+       /* by default, use proxyAuthz control on each operation */
+       mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
+
+       *mtp = mt;
 
        return 0;
 }
@@ -107,6 +118,8 @@ meta_back_db_config(
                struct berval   dn;
                int             rc;
                int             c;
+
+               metatarget_t    *mt;
                
                switch ( argc ) {
                case 1:
@@ -136,8 +149,8 @@ meta_back_db_config(
                
                ++mi->mi_ntargets;
 
-               mi->mi_targets = ( metatarget_t * )ch_realloc( mi->mi_targets, 
-                       sizeof( metatarget_t ) * mi->mi_ntargets );
+               mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets, 
+                       sizeof( metatarget_t ) * mi->mi_ntargets );
                if ( mi->mi_targets == NULL ) {
                        Debug( LDAP_DEBUG_ANY,
        "%s: line %d: out of memory while storing server name"
@@ -154,19 +167,29 @@ meta_back_db_config(
                        return 1;
                }
 
-               mi->mi_targets[ i ].mt_nretries = mi->mi_nretries;
-               mi->mi_targets[ i ].mt_flags = mi->mi_flags;
-               mi->mi_targets[ i ].mt_version = mi->mi_version;
-               mi->mi_targets[ i ].mt_network_timeout = mi->mi_network_timeout;
-               mi->mi_targets[ i ].mt_bind_timeout = mi->mi_bind_timeout;
-               for ( c = 0; c < LDAP_BACK_OP_LAST; c++ ) {
-                       mi->mi_targets[ i ].mt_timeout[ c ] = mi->mi_timeout[ c ];
+               mt = mi->mi_targets[ i ];
+
+               mt->mt_rebind_f = mi->mi_rebind_f;
+               mt->mt_urllist_p = mt;
+
+               mt->mt_nretries = mi->mi_nretries;
+               mt->mt_quarantine = mi->mi_quarantine;
+               if ( META_BACK_QUARANTINE( mi ) ) {
+                       ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
+               }
+               mt->mt_flags = mi->mi_flags;
+               mt->mt_version = mi->mi_version;
+               mt->mt_network_timeout = mi->mi_network_timeout;
+               mt->mt_bind_timeout = mi->mi_bind_timeout;
+               for ( c = 0; c < SLAP_OP_LAST; c++ ) {
+                       mt->mt_timeout[ c ] = mi->mi_timeout[ c ];
                }
 
                /*
                 * uri MUST be legal!
                 */
-               if ( ldap_url_parselist_ext( &ludp, argv[ 1 ], "\t" ) != LDAP_SUCCESS ) {
+               if ( ldap_url_parselist_ext( &ludp, argv[ 1 ], "\t" ) != LDAP_SUCCESS )
+               {
                        Debug( LDAP_DEBUG_ANY,
        "%s: line %d: unable to parse URI"
        " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
@@ -206,8 +229,8 @@ meta_back_db_config(
                 * copies and stores uri and suffix
                 */
                ber_str2bv( ludp->lud_dn, 0, 0, &dn );
-               rc = dnPrettyNormal( NULL, &dn, &mi->mi_targets[ i ].mt_psuffix,
-                       &mi->mi_targets[ i ].mt_nsuffix, NULL );
+               rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix,
+                       &mt->mt_nsuffix, NULL );
                if( rc != LDAP_SUCCESS ) {
                        Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                "target \"%s\" DN is invalid\n",
@@ -219,12 +242,12 @@ meta_back_db_config(
 
                switch ( ludp->lud_scope ) {
                case LDAP_SCOPE_DEFAULT:
-                       mi->mi_targets[ i ].mt_scope = LDAP_SCOPE_SUBTREE;
+                       mt->mt_scope = LDAP_SCOPE_SUBTREE;
                        break;
 
                case LDAP_SCOPE_SUBTREE:
                case LDAP_SCOPE_SUBORDINATE:
-                       mi->mi_targets[ i ].mt_scope = ludp->lud_scope;
+                       mt->mt_scope = ludp->lud_scope;
                        break;
 
                default:
@@ -246,9 +269,9 @@ meta_back_db_config(
                        }
                }
 
-               mi->mi_targets[ i ].mt_uri = ldap_url_list2urls( ludp );
+               mt->mt_uri = ldap_url_list2urls( ludp );
                ldap_free_urllist( ludp );
-               if ( mi->mi_targets[ i ].mt_uri == NULL) {
+               if ( mt->mt_uri == NULL) {
                        Debug( LDAP_DEBUG_ANY, "%s: line %d: no memory?\n",
                                fname, lineno, 0 );
                        return( 1 );
@@ -258,7 +281,7 @@ meta_back_db_config(
                 * uri MUST be a branch of suffix!
                 */
 #if 0 /* too strict a constraint */
-               if ( select_backend( &mi->mi_targets[ i ].suffix, 0, 0 ) != be ) {
+               if ( select_backend( &mt->mt_nsuffix, 0, 0 ) != be ) {
                        Debug( LDAP_DEBUG_ANY,
        "%s: line %d: <naming context> of URI does not refer to current backend"
        " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
@@ -269,7 +292,7 @@ meta_back_db_config(
                /*
                 * uri MUST be a branch of a suffix!
                 */
-               if ( select_backend( &mi->mi_targets[ i ].mt_nsuffix, 0, 0 ) == NULL ) {
+               if ( select_backend( &mt->mt_nsuffix, 0, 0 ) == NULL ) {
                        Debug( LDAP_DEBUG_ANY,
        "%s: line %d: <naming context> of URI does not resolve to a backend"
        " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
@@ -317,7 +340,7 @@ meta_back_db_config(
                        return( 1 );
                }
 
-               if ( !dnIsSuffix( &ndn, &mi->mi_targets[ i ].mt_nsuffix ) ) {
+               if ( !dnIsSuffix( &ndn, &mi->mi_targets[ i ]->mt_nsuffix ) ) {
                        Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                        "subtree-exclude DN=\"%s\" "
                                        "must be subtree of target\n",
@@ -326,12 +349,12 @@ meta_back_db_config(
                        return( 1 );
                }
 
-               if ( mi->mi_targets[ i ].mt_subtree_exclude != NULL ) {
+               if ( mi->mi_targets[ i ]->mt_subtree_exclude != NULL ) {
                        int             j;
 
-                       for ( j = 0; !BER_BVISNULL( &mi->mi_targets[ i ].mt_subtree_exclude[ j ] ); j++ )
+                       for ( j = 0; !BER_BVISNULL( &mi->mi_targets[ i ]->mt_subtree_exclude[ j ] ); j++ )
                        {
-                               if ( dnIsSuffix( &mi->mi_targets[ i ].mt_subtree_exclude[ j ], &ndn ) ) {
+                               if ( dnIsSuffix( &mi->mi_targets[ i ]->mt_subtree_exclude[ j ], &ndn ) ) {
                                        Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                        "subtree-exclude DN=\"%s\" "
                                                        "is suffix of another subtree-exclude\n",
@@ -341,7 +364,7 @@ meta_back_db_config(
                                        ber_memfree( ndn.bv_val );
                                        return( 1 );
 
-                               } else if ( dnIsSuffix( &ndn, &mi->mi_targets[ i ].mt_subtree_exclude[ j ] ) ) {
+                               } else if ( dnIsSuffix( &ndn, &mi->mi_targets[ i ]->mt_subtree_exclude[ j ] ) ) {
                                        Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                        "another subtree-exclude is suffix of "
                                                        "subtree-exclude DN=\"%s\"\n",
@@ -352,7 +375,7 @@ meta_back_db_config(
                        }
                }
 
-               ber_bvarray_add( &mi->mi_targets[ i ].mt_subtree_exclude, &ndn );
+               ber_bvarray_add( &mi->mi_targets[ i ]->mt_subtree_exclude, &ndn );
 
        /* default target directive */
        } else if ( strcasecmp( argv[ 0 ], "default-target" ) == 0 ) {
@@ -423,7 +446,7 @@ meta_back_db_config(
        } else if ( strcasecmp( argv[ 0 ], "network-timeout" ) == 0 ) {
                unsigned long   t;
                time_t          *tp = mi->mi_ntargets ?
-                               &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_network_timeout
+                               &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_network_timeout
                                : &mi->mi_network_timeout;
 
                if ( argc != 2 ) {
@@ -505,7 +528,7 @@ meta_back_db_config(
        } else if ( strcasecmp( argv[ 0 ], "bind-timeout" ) == 0 ) {
                unsigned long   t;
                struct timeval  *tp = mi->mi_ntargets ?
-                               &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_bind_timeout
+                               &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_bind_timeout
                                : &mi->mi_bind_timeout;
 
                switch ( argc ) {
@@ -564,7 +587,7 @@ meta_back_db_config(
                }
 
                ber_str2bv( argv[ 1 ], 0, 0, &dn );
-               if ( dnNormalize( 0, NULL, NULL, &dn, &mi->mi_targets[ i ].mt_binddn,
+               if ( dnNormalize( 0, NULL, NULL, &dn, &mi->mi_targets[ i ]->mt_binddn,
                        NULL ) != LDAP_SUCCESS )
                {
                        Debug( LDAP_DEBUG_ANY, "%s: line %d: "
@@ -601,7 +624,7 @@ meta_back_db_config(
                        /* FIXME: some day we'll need to throw an error */
                }
 
-               ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_bindpw );
+               ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ]->mt_bindpw );
                
        /* save bind creds for referral rebinds? */
        } else if ( strcasecmp( argv[ 0 ], "rebind-as-user" ) == 0 ) {
@@ -638,7 +661,7 @@ meta_back_db_config(
 
        } else if ( strcasecmp( argv[ 0 ], "chase-referrals" ) == 0 ) {
                unsigned        *flagsp = mi->mi_ntargets ?
-                               &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
+                               &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
                                : &mi->mi_flags;
 
                if ( argc != 2 ) {
@@ -667,7 +690,7 @@ meta_back_db_config(
        
        } else if ( strcasecmp( argv[ 0 ], "tls" ) == 0 ) {
                unsigned        *flagsp = mi->mi_ntargets ?
-                               &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
+                               &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
                                : &mi->mi_flags;
 
                if ( argc != 2 ) {
@@ -704,7 +727,7 @@ meta_back_db_config(
 
        } else if ( strcasecmp( argv[ 0 ], "t-f-support" ) == 0 ) {
                unsigned        *flagsp = mi->mi_ntargets ?
-                               &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
+                               &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
                                : &mi->mi_flags;
 
                if ( argc != 2 ) {
@@ -716,16 +739,16 @@ meta_back_db_config(
 
                switch ( check_true_false( argv[ 1 ] ) ) {
                case 0:
-                       *flagsp &= ~(LDAP_BACK_F_SUPPORT_T_F|LDAP_BACK_F_SUPPORT_T_F_DISCOVER);
+                       *flagsp &= ~LDAP_BACK_F_T_F_MASK2;
                        break;
 
                case 1:
-                       *flagsp |= LDAP_BACK_F_SUPPORT_T_F;
+                       *flagsp |= LDAP_BACK_F_T_F;
                        break;
 
                default:
                        if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) {
-                               *flagsp |= LDAP_BACK_F_SUPPORT_T_F_DISCOVER;
+                               *flagsp |= LDAP_BACK_F_T_F_DISCOVER;
 
                        } else {
                                Debug( LDAP_DEBUG_ANY,
@@ -740,29 +763,34 @@ meta_back_db_config(
        } else if ( strcasecmp( argv[ 0 ], "onerr" ) == 0 ) {
                if ( argc != 2 ) {
                        Debug( LDAP_DEBUG_ANY,
-       "%s: line %d: \"onerr {CONTINUE|stop}\" takes 1 argument\n",
+       "%s: line %d: \"onerr {CONTINUE|report|stop}\" takes 1 argument\n",
                                fname, lineno, 0 );
                        return( 1 );
                }
 
                if ( strcasecmp( argv[ 1 ], "continue" ) == 0 ) {
-                       mi->mi_flags &= ~META_BACK_F_ONERR_STOP;
+                       mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
 
                } else if ( strcasecmp( argv[ 1 ], "stop" ) == 0 ) {
                        mi->mi_flags |= META_BACK_F_ONERR_STOP;
 
+               } else if ( strcasecmp( argv[ 1 ], "report" ) == 0 ) {
+                       mi->mi_flags |= META_BACK_F_ONERR_REPORT;
+
                } else {
                        Debug( LDAP_DEBUG_ANY,
-       "%s: line %d: \"onerr {CONTINUE|stop}\": invalid arg \"%s\".\n",
+       "%s: line %d: \"onerr {CONTINUE|report|stop}\": invalid arg \"%s\".\n",
                                fname, lineno, argv[ 1 ] );
                        return 1;
                }
 
        /* bind-defer? */
-       } else if ( strcasecmp( argv[ 0 ], "pseudoroot-bind-defer" ) == 0 ) {
+       } else if ( strcasecmp( argv[ 0 ], "pseudoroot-bind-defer" ) == 0
+               || strcasecmp( argv[ 0 ], "root-bind-defer" ) == 0 )
+       {
                if ( argc != 2 ) {
                        Debug( LDAP_DEBUG_ANY,
-       "%s: line %d: \"pseudoroot-bind-defer {FALSE|true}\" takes 1 argument\n",
+       "%s: line %d: \"[pseudo]root-bind-defer {FALSE|true}\" takes 1 argument\n",
                                fname, lineno, 0 );
                        return( 1 );
                }
@@ -778,21 +806,148 @@ meta_back_db_config(
 
                default:
                        Debug( LDAP_DEBUG_ANY,
-       "%s: line %d: \"pseudoroot-bind-defer {FALSE|true}\": invalid arg \"%s\".\n",
+       "%s: line %d: \"[pseudo]root-bind-defer {FALSE|true}\": invalid arg \"%s\".\n",
+                               fname, lineno, argv[ 1 ] );
+                       return 1;
+               }
+
+       /* single-conn? */
+       } else if ( strcasecmp( argv[ 0 ], "single-conn" ) == 0 ) {
+               if ( argc != 2 ) {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"single-conn {FALSE|true}\" takes 1 argument\n",
+                               fname, lineno, 0 );
+                       return( 1 );
+               }
+
+               if ( mi->mi_ntargets > 0 ) {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"single-conn\" must appear before target definitions\n",
+                               fname, lineno, 0 );
+                       return( 1 );
+               }
+
+               switch ( check_true_false( argv[ 1 ] ) ) {
+               case 0:
+                       mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
+                       break;
+
+               case 1:
+                       mi->mi_flags |= LDAP_BACK_F_SINGLECONN;
+                       break;
+
+               default:
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"single-conn {FALSE|true}\": invalid arg \"%s\".\n",
+                               fname, lineno, argv[ 1 ] );
+                       return 1;
+               }
+
+       /* use-temporaries? */
+       } else if ( strcasecmp( argv[ 0 ], "use-temporary-conn" ) == 0 ) {
+               if ( argc != 2 ) {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"use-temporary-conn {FALSE|true}\" takes 1 argument\n",
+                               fname, lineno, 0 );
+                       return( 1 );
+               }
+
+               if ( mi->mi_ntargets > 0 ) {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"use-temporary-conn\" must appear before target definitions\n",
+                               fname, lineno, 0 );
+                       return( 1 );
+               }
+
+               switch ( check_true_false( argv[ 1 ] ) ) {
+               case 0:
+                       mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+                       break;
+
+               case 1:
+                       mi->mi_flags |= LDAP_BACK_F_USE_TEMPORARIES;
+                       break;
+
+               default:
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"use-temporary-conn {FALSE|true}\": invalid arg \"%s\".\n",
+                               fname, lineno, argv[ 1 ] );
+                       return 1;
+               }
+
+       /* privileged connections pool max size ? */
+       } else if ( strcasecmp( argv[ 0 ], "conn-pool-max" ) == 0 ) {
+               if ( argc != 2 ) {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"conn-pool-max <n>\" takes 1 argument\n",
+                               fname, lineno, 0 );
+                       return( 1 );
+               }
+
+               if ( mi->mi_ntargets > 0 ) {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"conn-pool-max\" must appear before target definitions\n",
+                               fname, lineno, 0 );
+                       return( 1 );
+               }
+
+               if ( lutil_atoi( &mi->mi_conn_priv_max, argv[1] )
+                       || mi->mi_conn_priv_max < LDAP_BACK_CONN_PRIV_MIN
+                       || mi->mi_conn_priv_max > LDAP_BACK_CONN_PRIV_MAX )
+               {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"conn-pool-max <n>\": invalid arg \"%s\".\n",
                                fname, lineno, argv[ 1 ] );
                        return 1;
                }
 
+       } else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) {
+               unsigned        flag = 0;
+               unsigned        *flagsp = mi->mi_ntargets ?
+                               &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
+                               : &mi->mi_flags;
+
+               if ( argc != 2 ) {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"cancel {abandon|ignore|exop}\" takes 1 argument\n",
+                               fname, lineno, 0 );
+                       return( 1 );
+               }
+
+               if ( strcasecmp( argv[ 1 ], "abandon" ) == 0 ) {
+                       flag = LDAP_BACK_F_CANCEL_ABANDON;
+
+#if 0  /* needs ldap_int_discard(), 2.4 */
+               } else if ( strcasecmp( argv[ 1 ], "ignore" ) == 0 ) {
+                       flag = LDAP_BACK_F_CANCEL_IGNORE;
+#endif
+
+               } else if ( strcasecmp( argv[ 1 ], "exop" ) == 0 ) {
+                       flag = LDAP_BACK_F_CANCEL_EXOP;
+
+               } else if ( strcasecmp( argv[ 1 ], "exop-discover" ) == 0 ) {
+                       flag = LDAP_BACK_F_CANCEL_EXOP_DISCOVER;
+
+               } else {
+                       Debug( LDAP_DEBUG_ANY,
+       "%s: line %d: \"cancel {abandon|ignore|exop[-discover]}\": unknown mode \"%s\" \n",
+                               fname, lineno, argv[ 1 ] );
+                       return( 1 );
+               }
+
+               *flagsp &= ~LDAP_BACK_F_CANCEL_MASK2;
+               *flagsp |= flag;
+
        } else if ( strcasecmp( argv[ 0 ], "timeout" ) == 0 ) {
                char    *sep;
                time_t  *tv = mi->mi_ntargets ?
-                               mi->mi_targets[ mi->mi_ntargets - 1 ].mt_timeout
+                               mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_timeout
                                : mi->mi_timeout;
                int     c;
 
                if ( argc < 2 ) {
                        Debug( LDAP_DEBUG_ANY,
-       "%s: line %d: \"timeout [{add|delete|modify|modrdn}=]<val> [...]\" takes at least 1 argument\n",
+       "%s: line %d: \"timeout [{add|bind|delete|modify|modrdn}=]<val> [...]\" takes at least 1 argument\n",
                                fname, lineno, 0 );
                        return( 1 );
                }
@@ -805,19 +960,33 @@ meta_back_db_config(
                        if ( sep != NULL ) {
                                size_t  len = sep - argv[ c ];
 
-                               if ( strncasecmp( argv[ c ], "add", len ) == 0 ) {
-                                       t = &tv[ LDAP_BACK_OP_ADD ];
+                               if ( strncasecmp( argv[ c ], "bind", len ) == 0 ) {
+                                       t = &tv[ SLAP_OP_BIND ];
+                               /* unbind makes little sense */
+                               } else if ( strncasecmp( argv[ c ], "add", len ) == 0 ) {
+                                       t = &tv[ SLAP_OP_ADD ];
                                } else if ( strncasecmp( argv[ c ], "delete", len ) == 0 ) {
-                                       t = &tv[ LDAP_BACK_OP_DELETE ];
-                               } else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) {
-                                       t = &tv[ LDAP_BACK_OP_MODIFY ];
+                                       t = &tv[ SLAP_OP_DELETE ];
                                } else if ( strncasecmp( argv[ c ], "modrdn", len ) == 0 ) {
-                                       t = &tv[ LDAP_BACK_OP_MODRDN ];
+                                       t = &tv[ SLAP_OP_MODRDN ];
+                               } else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) {
+                                       t = &tv[ SLAP_OP_MODIFY ];
+                               } else if ( strncasecmp( argv[ c ], "compare", len ) == 0 ) {
+                                       t = &tv[ SLAP_OP_COMPARE ];
+#if 0                          /* uses timelimit instead */
+                               } else if ( strncasecmp( argv[ c ], "search", len ) == 0 ) {
+                                       t = &tv[ SLAP_OP_SEARCH ];
+#endif
+                               /* abandon makes little sense */
+#if 0                          /* not implemented yet */
+                               } else if ( strncasecmp( argv[ c ], "extended", len ) == 0 ) {
+                                       t = &tv[ SLAP_OP_EXTENDED ];
+#endif
                                } else {
                                        char    buf[ SLAP_TEXT_BUFLEN ];
                                        snprintf( buf, sizeof( buf ),
-                                               "unknown operation \"%s\" for timeout #%d",
-                                               argv[ c ], c );
+                                               "unknown/unhandled operation \"%s\" for timeout #%d",
+                                               argv[ c ], c - 1 );
                                        Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: %s.\n",
                                                fname, lineno, buf );
@@ -842,7 +1011,7 @@ meta_back_db_config(
                        } else {
                                int     i;
        
-                               for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) {
+                               for ( i = 0; i < SLAP_OP_LAST; i++ ) {
                                        tv[ i ] = (time_t)val;
                                }
                        }
@@ -851,7 +1020,6 @@ meta_back_db_config(
        /* name to use as pseudo-root dn */
        } else if ( strcasecmp( argv[ 0 ], "pseudorootdn" ) == 0 ) {
                int             i = mi->mi_ntargets - 1;
-               struct berval   dn;
 
                if ( i < 0 ) {
                        Debug( LDAP_DEBUG_ANY,
@@ -867,15 +1035,74 @@ meta_back_db_config(
                        return 1;
                }
 
-               dn.bv_val = argv[ 1 ];
-               dn.bv_len = strlen( argv[ 1 ] );
-               if ( dnNormalize( 0, NULL, NULL, &dn,
-                       &mi->mi_targets[ i ].mt_pseudorootdn, NULL ) != LDAP_SUCCESS )
+               /*
+                * exact replacement:
+                *
+
+idassert-bind  bindmethod=simple
+               binddn=<pseudorootdn>
+               credentials=<pseudorootpw>
+               mode=none
+               flags=non-prescriptive
+idassert-authzFrom     "dn:<rootdn>"
+
+                * so that only when authc'd as <rootdn> the proxying occurs
+                * rebinding as the <pseudorootdn> without proxyAuthz.
+                */
+
+               Debug( LDAP_DEBUG_ANY,
+                       "%s: line %d: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
+                       "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
+                       fname, lineno, 0 );
+
                {
-                       Debug( LDAP_DEBUG_ANY, "%s: line %d: "
-                                       "pseudoroot DN '%s' is invalid\n",
-                                       fname, lineno, argv[ 1 ] );
-                       return( 1 );
+                       char    binddn[ SLAP_TEXT_BUFLEN ];
+                       char    *cargv[] = {
+                               "idassert-bind",
+                               "bindmethod=simple",
+                               NULL,
+                               "mode=none",
+                               "flags=non-prescriptive",
+                               NULL
+                       };
+                       int     cargc = 5;
+                       int     rc;
+
+                       if ( BER_BVISNULL( &be->be_rootndn ) ) {
+                               Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootpw\": \"rootdn\" must be defined first.\n",
+                                       fname, lineno, 0 );
+                               return 1;
+                       }
+
+                       if ( snprintf( binddn, sizeof( binddn ), "binddn=%s", argv[ 1 ] ) >= sizeof( binddn ) ) {
+                               Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootdn\" too long.\n",
+                                       fname, lineno, 0 );
+                               return 1;
+                       }
+                       cargv[ 2 ] = binddn;
+
+                       rc = slap_idassert_parse_cf( fname, lineno, cargc, cargv, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
+                       if ( rc == 0 ) {
+                               struct berval   bv;
+
+                               if ( mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz != NULL ) {
+                                       Debug( LDAP_DEBUG_ANY, "%s: line %d: \"idassert-authzFrom\" already defined (discarded).\n",
+                                               fname, lineno, 0 );
+                                       ber_bvarray_free( mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz );
+                                       mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz = NULL;
+                               }
+
+                               assert( !BER_BVISNULL( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authcDN ) );
+
+                               bv.bv_len = STRLENOF( "dn:" ) + be->be_rootndn.bv_len;
+                               bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+                               AC_MEMCPY( bv.bv_val, "dn:", STRLENOF( "dn:" ) );
+                               AC_MEMCPY( &bv.bv_val[ STRLENOF( "dn:" ) ], be->be_rootndn.bv_val, be->be_rootndn.bv_len + 1 );
+
+                               ber_bvarray_add( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz, &bv );
+                       }
+
+                       return rc;
                }
 
        /* password to use as pseudo-root */
@@ -895,7 +1122,114 @@ meta_back_db_config(
                            fname, lineno, 0 );
                        return 1;
                }
-               ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_pseudorootpw );
+
+               Debug( LDAP_DEBUG_ANY,
+                       "%s: line %d: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
+                       "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
+                       fname, lineno, 0 );
+
+               if ( BER_BVISNULL( &mi->mi_targets[ i ]->mt_idassert_authcDN ) ) {
+                       Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootpw\": \"pseudorootdn\" must be defined first.\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
+               if ( !BER_BVISNULL( &mi->mi_targets[ i ]->mt_idassert_passwd ) ) {
+                       memset( mi->mi_targets[ i ]->mt_idassert_passwd.bv_val, 0,
+                               mi->mi_targets[ i ]->mt_idassert_passwd.bv_len );
+                       ber_memfree( mi->mi_targets[ i ]->mt_idassert_passwd.bv_val );
+               }
+               ber_str2bv( argv[ 1 ], 0, 1, &mi->mi_targets[ i ]->mt_idassert_passwd );
+
+       /* idassert-bind */
+       } else if ( strcasecmp( argv[ 0 ], "idassert-bind" ) == 0 ) {
+               if ( mi->mi_ntargets == 0 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: line %d: \"idassert-bind\" "
+                               "must appear inside a target specification.\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
+               return slap_idassert_parse_cf( fname, lineno, argc, argv, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
+
+       /* idassert-authzFrom */
+       } else if ( strcasecmp( argv[ 0 ], "idassert-authzFrom" ) == 0 ) {
+               if ( mi->mi_ntargets == 0 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: line %d: \"idassert-bind\" "
+                               "must appear inside a target specification.\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
+               switch ( argc ) {
+               case 2:
+                       break;
+
+               case 1:
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: line %d: missing <id> in \"idassert-authzFrom <id>\".\n",
+                               fname, lineno, 0 );
+                       return 1;
+
+               default:
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: line %d: extra cruft after <id> in \"idassert-authzFrom <id>\".\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
+               return slap_idassert_authzfrom_parse_cf( fname, lineno, argv[ 1 ], &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
+
+       /* quarantine */
+       } else if ( strcasecmp( argv[ 0 ], "quarantine" ) == 0 ) {
+               char                    buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+               slap_retry_info_t       *ri = mi->mi_ntargets ?
+                               &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine
+                               : &mi->mi_quarantine;
+
+               if ( ( mi->mi_ntargets == 0 && META_BACK_QUARANTINE( mi ) )
+                       || ( mi->mi_ntargets > 0 && META_BACK_TGT_QUARANTINE( mi->mi_targets[ mi->mi_ntargets - 1 ] ) ) )
+               {
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: line %d: quarantine already defined.\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
+               switch ( argc ) {
+               case 2:
+                       break;
+
+               case 1:
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: line %d: missing arg in \"quarantine <pattern list>\".\n",
+                               fname, lineno, 0 );
+                       return 1;
+
+               default:
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s: line %d: extra cruft after \"quarantine <pattern list>\".\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
+               if ( ri != &mi->mi_quarantine ) {
+                       ri->ri_interval = NULL;
+                       ri->ri_num = NULL;
+               }
+
+               if ( mi->mi_ntargets > 0 && !META_BACK_QUARANTINE( mi ) ) {
+                       ldap_pvt_thread_mutex_init( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine_mutex );
+               }
+
+               if ( slap_retry_info_parse( argv[ 1 ], ri, buf, sizeof( buf ) ) ) {
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s line %d: %s.\n",
+                               fname, lineno, buf );
+                       return 1;
+               }
        
        /* dn massaging */
        } else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) {
@@ -978,7 +1312,7 @@ meta_back_db_config(
                 * FIXME: no extra rewrite capabilities should be added
                 * to the database
                 */
-               rc = suffix_massage_config( mi->mi_targets[ i ].mt_rwmap.rwm_rw,
+               rc = suffix_massage_config( mi->mi_targets[ i ]->mt_rwmap.rwm_rw,
                                &pvnc, &nvnc, &prnc, &nrnc );
 
                free( pvnc.bv_val );
@@ -999,7 +1333,7 @@ meta_back_db_config(
                        return 1;
                }
                
-               return rewrite_parse( mi->mi_targets[ i ].mt_rwmap.rwm_rw,
+               return rewrite_parse( mi->mi_targets[ i ]->mt_rwmap.rwm_rw,
                                fname, lineno, argc, argv );
 
        /* objectclass/attribute mapping */
@@ -1013,8 +1347,8 @@ meta_back_db_config(
                        return 1;
                }
 
-               return ldap_back_map_config( &mi->mi_targets[ i ].mt_rwmap.rwm_oc, 
-                               &mi->mi_targets[ i ].mt_rwmap.rwm_at,
+               return ldap_back_map_config( &mi->mi_targets[ i ]->mt_rwmap.rwm_oc, 
+                               &mi->mi_targets[ i ]->mt_rwmap.rwm_at,
                                fname, lineno, argc, argv );
 
        } else if ( strcasecmp( argv[ 0 ], "nretries" ) == 0 ) {
@@ -1047,12 +1381,12 @@ meta_back_db_config(
                        mi->mi_nretries = nretries;
 
                } else {
-                       mi->mi_targets[ i ].mt_nretries = nretries;
+                       mi->mi_targets[ i ]->mt_nretries = nretries;
                }
 
        } else if ( strcasecmp( argv[ 0 ], "protocol-version" ) == 0 ) {
                int     *version = mi->mi_ntargets ?
-                               &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_version
+                               &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_version
                                : &mi->mi_version;
 
                if ( argc != 2 ) {
@@ -1122,7 +1456,7 @@ ldap_back_map_config(
        if ( strcmp( argv[ 2 ], "*" ) == 0 ) {
                if ( argc < 4 || strcmp( argv[ 3 ], "*" ) == 0 ) {
                        map->drop_missing = ( argc < 4 );
-                       return 0;
+                       goto success_return;
                }
                src = dst = argv[ 3 ];
 
@@ -1136,7 +1470,7 @@ ldap_back_map_config(
        }
 
        if ( ( map == at_map )
-                       && ( strcasecmp( src, "objectclass" ) == 0
+               && ( strcasecmp( src, "objectclass" ) == 0
                        || strcasecmp( dst, "objectclass" ) == 0 ) )
        {
                Debug( LDAP_DEBUG_ANY,
@@ -1264,6 +1598,12 @@ ldap_back_map_config(
        avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
                                mapping_cmp, mapping_dup );
 
+success_return:;
+       if ( !is_oc && map->map == NULL ) {
+               /* only init if required */
+               ldap_back_map_init( map, &mapping );
+       }
+
        return 0;
 
 error_return:;
index ffe99d8467043d92c52689a0c4b35f92d43e0c2b..8227ed3d92a7e4cea45eb1fd932139ebce0099d1 100644 (file)
 #include "../back-ldap/back-ldap.h"
 #include "back-meta.h"
 
-/*
- * Set PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
- */
-#define PRINT_CONNTREE 0
-
 /*
  * meta_back_conndn_cmp
  *
@@ -143,46 +138,79 @@ meta_back_conndn_dup(
 /*
  * Debug stuff (got it from libavl)
  */
-#if PRINT_CONNTREE > 0
+#if META_BACK_PRINT_CONNTREE > 0
 static void
-ravl_print( Avlnode *root, int depth )
+meta_back_ravl_print( Avlnode *root, int depth )
 {
        int             i;
-       metaconn_t      *mc = (metaconn_t *)root->avl_data;
+       metaconn_t      *mc;
        
        if ( root == 0 ) {
                return;
        }
        
-       ravl_print( root->avl_right, depth + 1 );
+       meta_back_ravl_print( root->avl_right, depth + 1 );
        
        for ( i = 0; i < depth; i++ ) {
-               printf( "    " );
+               fprintf( stderr, "-" );
        }
 
-       printf( "c(%d%s%s) %d\n",
-               LDAP_BACK_PCONN_ID( mc->mc_conn ),
-               BER_BVISNULL( &mc->mc_local_ndn ) ? "" : ": ",
-               BER_BVISNULL( &mc->mc_local_ndn ) ? "" : mc->mc_local_ndn.bv_val,
-               root->avl_bf );
+       mc = (metaconn_t *)root->avl_data;
+       fprintf( stderr, "mc=%p local=\"%s\" conn=%p %s refcnt=%d%s\n",
+               (void *)mc,
+               mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "",
+               (void *)mc->mc_conn,
+               avl_bf2str( root->avl_bf ), mc->mc_refcnt,
+               LDAP_BACK_CONN_TAINTED( mc ) ? " tainted" : "" );
        
-       ravl_print( root->avl_left, depth + 1 );
+       meta_back_ravl_print( root->avl_left, depth + 1 );
 }
 
-static void
-myprint( Avlnode *root )
+/* NOTE: duplicate from back-ldap/bind.c */
+static char* priv2str[] = {
+       "privileged",
+       "privileged/TLS",
+       "anonymous",
+       "anonymous/TLS",
+       "bind",
+       "bind/TLS",
+       NULL
+};
+
+void
+meta_back_print_conntree( metainfo_t *mi, char *msg )
 {
-       printf( "********\n" );
+       int     c;
+
+       fprintf( stderr, "========> %s\n", msg );
        
-       if ( root == 0 ) {
-               printf( "\tNULL\n" );
+       for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
+               int             i = 0;
+               metaconn_t      *mc;
+
+               fprintf( stderr, "  %s[%d]\n", priv2str[ c ], mi->mi_conn_priv[ c ].mic_num );
+
+               LDAP_TAILQ_FOREACH( mc, &mi->mi_conn_priv[ c ].mic_priv, mc_q )
+               {
+                       fprintf( stderr, "    [%d] mc=%p local=\"%s\" conn=%p refcnt=%d flags=0x%08x\n",
+                               i,
+                               (void *)mc,
+                               mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "",
+                               (void *)mc->mc_conn, mc->mc_refcnt, mc->msc_mscflags );
+                       i++;
+               }
+       }
+       
+       if ( mi->mi_conninfo.lai_tree == NULL ) {
+               fprintf( stderr, "\t(empty)\n" );
+
        } else {
-               ravl_print( root, 0 );
+               meta_back_ravl_print( mi->mi_conninfo.lai_tree, 0 );
        }
        
-       printf( "********\n" );
+       fprintf( stderr, "<======== %s\n", msg );
 }
-#endif /* PRINT_CONNTREE */
+#endif /* META_BACK_PRINT_CONNTREE */
 /*
  * End of debug stuff
  */
@@ -198,51 +226,25 @@ metaconn_alloc(
 {
        metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
        metaconn_t      *mc;
-       int             i, ntargets = mi->mi_ntargets;
+       int             ntargets = mi->mi_ntargets;
 
        assert( ntargets > 0 );
 
        /* malloc all in one */
-       mc = ( metaconn_t * )ch_malloc( sizeof( metaconn_t )
-                       + sizeof( metasingleconn_t ) * ntargets );
+       mc = ( metaconn_t * )ch_calloc( 1, sizeof( metaconn_t )
+               + sizeof( metasingleconn_t ) * ( ntargets - 1 ) );
        if ( mc == NULL ) {
                return NULL;
        }
 
-       for ( i = 0; i < ntargets; i++ ) {
-               mc->mc_conns[ i ].msc_ld = NULL;
-               BER_BVZERO( &mc->mc_conns[ i ].msc_bound_ndn );
-               BER_BVZERO( &mc->mc_conns[ i ].msc_cred );
-               mc->mc_conns[ i ].msc_mscflags = 0;
-               mc->mc_conns[ i ].msc_info = mi;
-       }
+       mc->mc_info = mi;
 
-       BER_BVZERO( &mc->mc_local_ndn );
-       mc->msc_mscflags = 0;
        mc->mc_authz_target = META_BOUND_NONE;
        mc->mc_refcnt = 1;
 
        return mc;
 }
 
-static void
-meta_back_freeconn(
-       Operation       *op,
-       metaconn_t      *mc )
-{
-       metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
-
-       assert( mc != NULL );
-
-       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
-
-       if ( --mc->mc_refcnt == 0 ) {
-               meta_back_conn_free( mc );
-       }
-
-       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
-}
-
 /*
  * meta_back_init_one_conn
  * 
@@ -252,31 +254,125 @@ int
 meta_back_init_one_conn(
        Operation               *op,
        SlapReply               *rs,
-       metatarget_t            *mt, 
        metaconn_t              *mc,
        int                     candidate,
        int                     ispriv,
-       ldap_back_send_t        sendok )
+       ldap_back_send_t        sendok,
+       int                     dolock )
 {
        metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
        metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
        int                     version;
        dncookie                dc;
        int                     isauthz = ( candidate == mc->mc_authz_target );
+       int                     do_return = 0;
+#ifdef HAVE_TLS
+       int                     is_ldaps = 0;
+#endif /* HAVE_TLS */
+
+       /* if the server is quarantined, and
+        * - the current interval did not expire yet, or
+        * - no more retries should occur,
+        * don't return the connection */
+       if ( mt->mt_isquarantined ) {
+               slap_retry_info_t       *ri = &mt->mt_quarantine;
+               int                     dont_retry = 1;
+
+               if ( mt->mt_isquarantined == LDAP_BACK_FQ_YES ) {
+                       dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
+                               || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
+                       if ( !dont_retry ) {
+                               if ( StatslogTest( LDAP_DEBUG_ANY ) ) {
+                                       char    buf[ SLAP_TEXT_BUFLEN ];
+
+                                       snprintf( buf, sizeof( buf ),
+                                               "meta_back_init_one_conn[%d]: quarantine "
+                                               "retry block #%d try #%d",
+                                               candidate, ri->ri_idx, ri->ri_count );
+                                       Debug( LDAP_DEBUG_ANY, "%s %s.\n",
+                                               op->o_log_prefix, buf, 0 );
+                               }
+
+                               mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
+                       }
+               }
+
+               if ( dont_retry ) {
+                       rs->sr_err = LDAP_UNAVAILABLE;
+                       if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+                               send_ldap_result( op, rs );
+                       }
+                       return rs->sr_err;
+               }
+       }
+
+retry_lock:;
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+       }
 
        /*
         * Already init'ed
         */
-       if ( msc->msc_ld != NULL ) {
-               return rs->sr_err = LDAP_SUCCESS;
+       if ( LDAP_BACK_CONN_ISBOUND( msc )
+               || LDAP_BACK_CONN_ISANON( msc ) )
+       {
+               assert( msc->msc_ld != NULL );
+               rs->sr_err = LDAP_SUCCESS;
+               do_return = 1;
+
+       } else if ( META_BACK_CONN_CREATING( msc )
+               || LDAP_BACK_CONN_BINDING( msc ) )
+       {
+               if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
+                       if ( dolock ) {
+                               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                       }
+
+                       ldap_pvt_thread_yield();
+                       goto retry_lock;
+               }
+
+               /* sounds more appropriate */
+               rs->sr_err = LDAP_BUSY;
+               do_return = 1;
+
+       } else if ( META_BACK_CONN_INITED( msc ) ) {
+               assert( msc->msc_ld != NULL );
+               rs->sr_err = LDAP_SUCCESS;
+               do_return = 1;
+
+       } else {
+               /*
+                * creating...
+                */
+               META_BACK_CONN_CREATING_SET( msc );
+       }
+
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
        }
 
-       msc->msc_mscflags = 0;
+       if ( do_return ) {
+               if ( rs->sr_err != LDAP_SUCCESS && op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+                       send_ldap_result( op, rs );
+               }
+
+               return rs->sr_err;
+       }
+
+       assert( msc->msc_ld == NULL );
        
        /*
         * Attempts to initialize the connection to the target ds
         */
+       ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
        rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
+#ifdef HAVE_TLS
+       is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
+#endif /* HAVE_TLS */
+       ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
        if ( rs->sr_err != LDAP_SUCCESS ) {
                goto error_return;
        }
@@ -303,7 +399,7 @@ meta_back_init_one_conn(
 #ifdef HAVE_TLS
        /* start TLS ("tls [try-]{start|propagate}" statement) */
        if ( ( LDAP_BACK_USE_TLS( mi ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( mi ) ) )
-                       && !ldap_is_ldaps_url( mt->mt_uri ) )
+                       && !is_ldaps )
        {
 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
                /*
@@ -322,10 +418,12 @@ meta_back_init_one_conn(
 
 retry:;
                        rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
-                       if ( rc < 0 ) {
+                       switch ( rc ) {
+                       case -1:
                                rs->sr_err = LDAP_OTHER;
+                               break;
 
-                       } else if ( rc == 0 ) {
+                       case 0:
                                if ( nretries != 0 ) {
                                        if ( nretries > 0 ) {
                                                nretries--;
@@ -334,15 +432,26 @@ retry:;
                                        goto retry;
                                }
                                rs->sr_err = LDAP_OTHER;
+                               break;
 
-                       } else if ( rc == LDAP_RES_EXTENDED ) {
+                       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;
+                               }
+                               break;
+                       }
+
+                       if ( rc == LDAP_RES_EXTENDED ) {
                                struct berval   *data = NULL;
 
+                               /* NOTE: right now, data is unused, so don't get it */
                                rs->sr_err = ldap_parse_extended_result( msc->msc_ld, res,
-                                               NULL, &data, 0 );
+                                               NULL, NULL /* &data */ , 0 );
                                if ( rs->sr_err == LDAP_SUCCESS ) {
                                        int             err;
 
+                                       /* FIXME: matched? referrals? response controls? */
                                        rs->sr_err = ldap_parse_result( msc->msc_ld, res,
                                                &err, NULL, NULL, NULL, NULL, 1 );
                                        res = NULL;
@@ -359,6 +468,7 @@ retry:;
                                                ldap_install_tls( msc->msc_ld );
 
                                        } else if ( rs->sr_err == LDAP_REFERRAL ) {
+                                               /* FIXME: LDAP_OPERATIONS_ERROR? */
                                                rs->sr_err = LDAP_OTHER;
                                                rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
                                        }
@@ -393,7 +503,14 @@ retry:;
                if ( rs->sr_err == LDAP_SERVER_DOWN
                                || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( mi ) ) )
                {
-                       ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
+
+#ifdef DEBUG_205
+                       Debug( LDAP_DEBUG_ANY, "### %s meta_back_init_one_conn(TLS) ldap_unbind_ext[%d] ld=%p\n",
+                               op->o_log_prefix, candidate, (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+                       /* need to trash a failed Start TLS */
+                       meta_clear_one_candidate( op, mc, candidate );
                        goto error_return;
                }
        }
@@ -417,21 +534,30 @@ retry:;
         */
 
        if ( ispriv ) {
-               if ( !BER_BVISNULL( &mt->mt_pseudorootdn ) ) {
-                       ber_dupbv( &msc->msc_bound_ndn, &mt->mt_pseudorootdn );
-                       if ( !BER_BVISNULL( &mt->mt_pseudorootpw ) ) {
-                               ber_dupbv( &msc->msc_cred, &mt->mt_pseudorootpw );
+               if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
+                       ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
+                       if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
+                               if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+                                       memset( msc->msc_cred.bv_val, 0,
+                                               msc->msc_cred.bv_len );
+                               }
+                               ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
                        }
 
                } else {
-                       ber_str2bv( "", 0, 1, &msc->msc_bound_ndn );
+                       ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
                }
 
-               LDAP_BACK_CONN_ISPRIV_SET( msc );
-
        } else {
-               BER_BVZERO( &msc->msc_cred );
-               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 );
+               }
+               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_BVISEMPTY( &op->o_ndn )
                        && SLAP_IS_AUTHZ_BACKEND( op )
                        && isauthz )
@@ -447,30 +573,48 @@ retry:;
                        if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
                                                &msc->msc_bound_ndn ) )
                        {
-                               ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
+
+#ifdef DEBUG_205
+                               Debug( LDAP_DEBUG_ANY, "### %s meta_back_init_one_conn(rewrite) ldap_unbind_ext[%d] ld=%p\n",
+                                       op->o_log_prefix, candidate, (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+                               /* need to trash a connection not fully established */
+                               meta_clear_one_candidate( op, mc, candidate );
                                goto error_return;
                        }
                        
-                       /* copy the DN idf needed */
+                       /* 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_str2bv( "", 0, 1, &msc->msc_bound_ndn );
+                       ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
                }
        }
 
        assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
 
 error_return:;
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+       }
+       META_BACK_CONN_CREATING_CLEAR( msc );
        if ( rs->sr_err == LDAP_SUCCESS ) {
                /*
                 * 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 );
+       }
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+       }
 
-       } else {
+       if ( rs->sr_err != LDAP_SUCCESS ) {
                rs->sr_err = slap_map_api2result( rs );
                if ( sendok & LDAP_BACK_SENDERR ) {
                        send_ldap_result( op, rs );
@@ -495,35 +639,48 @@ meta_back_retry(
        ldap_back_send_t        sendok )
 {
        metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
-       metatarget_t            *mt = &mi->mi_targets[ candidate ];
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
        metaconn_t              *mc = *mcp;
        metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
        int                     rc = LDAP_UNAVAILABLE,
-                               binding = LDAP_BACK_CONN_BINDING( msc );
+                               binding;
 
        ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
 
+       assert( !META_BACK_CONN_CREATING( msc ) );
+       binding = LDAP_BACK_CONN_BINDING( msc );
+       LDAP_BACK_CONN_BINDING_CLEAR( msc );
+
        assert( mc->mc_refcnt > 0 );
        if ( mc->mc_refcnt == 1 ) {
-               char    buf[ SLAP_TEXT_BUFLEN ];
+               if ( StatslogTest( LDAP_DEBUG_ANY ) ) {
+                       char    buf[ SLAP_TEXT_BUFLEN ];
+
+                       /* this lock is required; however,
+                        * it's invoked only when logging is on */
+                       ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
+                       snprintf( buf, sizeof( buf ),
+                               "retrying URI=\"%s\" DN=\"%s\"",
+                               mt->mt_uri,
+                               BER_BVISNULL( &msc->msc_bound_ndn ) ?
+                                       "" : msc->msc_bound_ndn.bv_val );
+                       ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
 
-               snprintf( buf, sizeof( buf ),
-                       "retrying URI=\"%s\" DN=\"%s\"",
-                       mt->mt_uri,
-                       BER_BVISNULL( &msc->msc_bound_ndn ) ?
-                               "" : msc->msc_bound_ndn.bv_val );
-               Debug( LDAP_DEBUG_ANY,
-                       "%s meta_back_retry[%d]: %s.\n",
-                       op->o_log_prefix, candidate, buf );
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s meta_back_retry[%d]: %s.\n",
+                               op->o_log_prefix, candidate, buf );
+               }
 
-               meta_clear_one_candidate( msc );
+               meta_clear_one_candidate( op, mc, candidate );
                LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
 
                ( 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 = meta_back_init_one_conn( op, rs, mt, mc, candidate,
-                       LDAP_BACK_CONN_ISPRIV( mc ), sendok );
+               rc = meta_back_init_one_conn( op, rs, mc, candidate,
+                       LDAP_BACK_CONN_ISPRIV( mc ), sendok, 0 );
+
+               /* restore the "binding" flag, in case */
                if ( binding ) {
                        LDAP_BACK_CONN_BINDING_SET( msc );
                }
@@ -531,25 +688,97 @@ meta_back_retry(
                if ( rc == LDAP_SUCCESS ) {
                        rc = meta_back_single_dobind( op, rs, mcp, candidate,
                                sendok, mt->mt_nretries, 0 );
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s meta_back_retry[%d]: "
+                               "meta_back_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 );
+                               }
+                       }
                }
+
+               /* don't send twice */
+               sendok &= ~LDAP_BACK_SENDERR;
        }
 
        if ( rc != LDAP_SUCCESS ) {
+               SlapReply               *candidates = meta_back_candidates_get( op );
+
+               candidates[ candidate ].sr_err = rc;
+
                if ( *mcp != NULL ) {
-                       if ( binding ) {
-                               LDAP_BACK_CONN_BINDING_CLEAR( msc );
+                       if ( mc->mc_refcnt == 1 ) {
+                               if ( binding ) {
+                                       LDAP_BACK_CONN_BINDING_CLEAR( msc );
+                               }
+                               (void)meta_clear_one_candidate( op, mc, candidate );
+                       }
+
+                       LDAP_BACK_CONN_TAINTED_SET( mc );
+                       /* only release if mandatory; otherwise
+                        * let the caller do what's best before
+                        * releasing */
+                       if ( META_BACK_ONERR_STOP( mi ) ) {
+                               meta_back_release_conn_lock( op, mc, 0 );
+                               *mcp = NULL;
+
+                       } else {
+#if META_BACK_PRINT_CONNTREE > 0
+                               meta_back_print_conntree( mi, ">>> meta_back_retry" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+                               /* FIXME: could be done better, reworking meta_back_release_conn_lock() */
+                               if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+                                       if ( mc->mc_q.tqe_prev != NULL ) {
+                                               assert( LDAP_BACK_CONN_CACHED( mc ) );
+                                               assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
+                                               LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+                                                       mc, mc_q );
+                                               mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
+                                               mc->mc_q.tqe_prev = NULL;
+                                               mc->mc_q.tqe_next = NULL;
+
+                                       } else {
+                                               assert( !LDAP_BACK_CONN_CACHED( mc ) );
+                                       }
+
+                               } else {
+                                       /* FIXME: check if in tree, for consistency? */
+                                       (void)avl_delete( &mi->mi_conninfo.lai_tree,
+                                               ( caddr_t )mc, meta_back_conndnmc_cmp );
+                               }
+                               LDAP_BACK_CONN_CACHED_CLEAR( mc );
+
+#if META_BACK_PRINT_CONNTREE > 0
+                               meta_back_print_conntree( mi, "<<< meta_back_retry" );
+#endif /* META_BACK_PRINT_CONNTREE */
                        }
-                       meta_back_release_conn_lock( op, mc, 1, 0 );
-                       *mcp = NULL;
                }
 
-               if ( sendok ) {
-                       rs->sr_err = LDAP_UNAVAILABLE;
+               if ( sendok & LDAP_BACK_SENDERR ) {
+                       rs->sr_err = rc;
                        rs->sr_text = NULL;
                        send_ldap_result( op, rs );
                }
        }
 
+       if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+               meta_back_quarantine( op, rs, candidate );
+       }
+
        ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
 
        return rc == LDAP_SUCCESS ? 1 : 0;
@@ -644,9 +873,7 @@ meta_back_get_candidate(
                         * and a default target is defined, and it is
                         * a candidate, try using it (FIXME: YMMV) */
                        if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
-                               && meta_back_is_candidate( &mi->mi_targets[ mi->mi_defaulttarget ].mt_nsuffix,
-                                               mi->mi_targets[ mi->mi_defaulttarget ].mt_scope,
-                                               mi->mi_targets[ mi->mi_defaulttarget ].mt_subtree_exclude,
+                               && meta_back_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;
@@ -716,9 +943,11 @@ meta_back_candidates_get( Operation *op )
        } else if ( mc->mc_ntargets < mi->mi_ntargets ) {
                /* NOTE: in the future, may want to allow back-config
                 * to add/remove targets from back-meta... */
-               mc->mc_ntargets = mi->mi_ntargets;
                mc->mc_candidates = ch_realloc( mc->mc_candidates,
-                               sizeof( SlapReply ) * mc->mc_ntargets );
+                               sizeof( SlapReply ) * mi->mi_ntargets );
+               memset( &mc->mc_candidates[ mc->mc_ntargets ], 0,
+                       sizeof( SlapReply ) * ( mi->mi_ntargets - mc->mc_ntargets ) );
+               mc->mc_ntargets = mi->mi_ntargets;
        }
 
        return mc->mc_candidates;
@@ -779,18 +1008,18 @@ meta_back_getconn(
                META_DNTYPE_ENTRY,
                META_DNTYPE_PARENT,
                META_DNTYPE_NEWPARENT
-                       } dn_type = META_DNTYPE_ENTRY;
+       }               dn_type = META_DNTYPE_ENTRY;
        struct berval   ndn = op->o_req_ndn,
                        pndn;
 
        SlapReply       *candidates = meta_back_candidates_get( op );
 
        /* Internal searches are privileged and shared. So is root. */
-       /* FIXME: there seem to be concurrency issues */
-       if ( op->o_do_not_cache || be_isroot( op ) ) {
+       /* FIXME: there seems to be concurrency issues */
+       if ( META_BACK_PROXYAUTHZ_ALWAYS( mi ) || op->o_do_not_cache || be_isroot( op ) ) {
                mc_curr.mc_local_ndn = op->o_bd->be_rootndn;
                LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
-               mc_curr.mc_conn = LDAP_BACK_PCONN_SET( op );
+               LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op );
 
        } else {
                mc_curr.mc_local_ndn = op->o_ndn;
@@ -800,37 +1029,110 @@ meta_back_getconn(
                        mc_curr.mc_conn = op->o_conn;
        
                } else {
-                       mc_curr.mc_conn = LDAP_BACK_PCONN_SET( op );
+                       LDAP_BACK_CONN_ISANON_SET( &mc_curr );
+                       LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
                }
        }
 
        /* Explicit Bind requests always get their own conn */
        if ( !( sendok & LDAP_BACK_BINDING ) ) {
                /* Searches for a metaconn in the avl tree */
-retry_lock:
+retry_lock:;
                ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
-               mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, 
-                       (caddr_t)&mc_curr, meta_back_conndn_cmp );
-               if ( mc ) {
-                       if ( ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl )
-                               || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) )
+               if ( LDAP_BACK_PCONN_ISPRIV( &mc_curr ) ) {
+                       /* lookup a conn that's not binding */
+                       LDAP_TAILQ_FOREACH( mc,
+                               &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv,
+                               mc_q )
                        {
-                               /* don't let anyone else use this expired connection */
-                               (void)avl_delete( &mi->mi_conninfo.lai_tree,
-                                       (caddr_t)mc, meta_back_conndnmc_cmp );
+                               if ( !LDAP_BACK_CONN_BINDING( mc ) && mc->mc_refcnt == 0 ) {
+                                       break;
+                               }
+                       }
 
-                               Debug( LDAP_DEBUG_TRACE, "%s meta_back_getconn: mc=%p conn=%ld expired.\n",
-                                       op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc->mc_conn ) );
+                       if ( mc != NULL ) {
+                               if ( mc != LDAP_TAILQ_LAST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+                                       metaconn_t, mc_q ) )
+                               {
+                                       LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+                                               mc, mc_q );
+                                       mc->mc_q.tqe_prev = NULL;
+                                       mc->mc_q.tqe_next = NULL;
+                                       LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+                                               mc, mc_q );
+                               }
+
+                       } else if ( !LDAP_BACK_USE_TEMPORARIES( mi )
+                               && mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_num == mi->mi_conn_priv_max )
+                       {
+                               mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv );
                        }
+                       
 
-                       /* Don't reuse connections while they're still binding */
+               } else {
+                       mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, 
+                               (caddr_t)&mc_curr, meta_back_conndn_cmp );
+               }
+
+               if ( mc ) {
+                       /* catch taint errors */
+                       assert( !LDAP_BACK_CONN_TAINTED( mc ) );
+
+                       /* Don't reuse connections while they're still binding
+                        * NOTE: only makes sense for binds */
                        if ( LDAP_BACK_CONN_BINDING( mc ) ) {
-                               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
-                               ldap_pvt_thread_yield();
-                               goto retry_lock;
-                       }
+                               if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
+                                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+                                       ldap_pvt_thread_yield();
+                                       goto retry_lock;
+                               }
+
+                               /* release conn, and create a temporary */
+                               mc = NULL;
+
+                       } else {
+                               if ( ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl )
+                                       || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) )
+                               {
+#if META_BACK_PRINT_CONNTREE > 0
+                                       meta_back_print_conntree( mi,
+                                               ">>> meta_back_getconn(expired)" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+                                       /* don't let anyone else use this expired connection */
+                                       if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+                                               if ( mc->mc_q.tqe_prev != NULL ) {
+                                                       assert( LDAP_BACK_CONN_CACHED( mc ) );
+                                                       assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
+                                                       LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+                                                               mc, mc_q );
+                                                       mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
+                                                       mc->mc_q.tqe_prev = NULL;
+                                                       mc->mc_q.tqe_next = NULL;
+
+                                               } else {
+                                                       assert( !LDAP_BACK_CONN_CACHED( mc ) );
+                                               }
 
-                       mc->mc_refcnt++;
+                                       } else {
+                                               (void)avl_delete( &mi->mi_conninfo.lai_tree,
+                                                       (caddr_t)mc, meta_back_conndnmc_cmp );
+                                       }
+
+#if META_BACK_PRINT_CONNTREE > 0
+                                       meta_back_print_conntree( mi,
+                                               "<<< meta_back_getconn(expired)" );
+#endif /* META_BACK_PRINT_CONNTREE */
+                                       LDAP_BACK_CONN_TAINTED_SET( mc );
+                                       LDAP_BACK_CONN_CACHED_CLEAR( mc );
+
+                                       Debug( LDAP_DEBUG_TRACE, "%s meta_back_getconn: mc=%p conn=%ld expired (tainted).\n",
+                                               op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc ) );
+                               }
+
+                               mc->mc_refcnt++;
+                       }
                }
                ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
        }
@@ -859,12 +1161,12 @@ retry_lock:
                }
                break;
 
+       case LDAP_REQ_COMPARE:
        case LDAP_REQ_DELETE:
        case LDAP_REQ_MODIFY:
                /* just a unique candidate */
                break;
 
-       case LDAP_REQ_COMPARE:
        case LDAP_REQ_SEARCH:
                /* allow multiple candidates for the searchBase */
                op_type = META_OP_ALLOW_MULTIPLE;
@@ -882,6 +1184,7 @@ retry_lock:
 
                /* Looks like we didn't get a bind. Open a new session... */
                if ( mc == NULL ) {
+                       assert( new_conn == 0 );
                        mc = metaconn_alloc( op );
                        mc->mc_conn = mc_curr.mc_conn;
                        ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
@@ -889,20 +1192,28 @@ retry_lock:
                        if ( sendok & LDAP_BACK_BINDING ) {
                                LDAP_BACK_CONN_BINDING_SET( mc );
                        }
+                       if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+                               LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+                       } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+                               LDAP_BACK_CONN_ISANON_SET( mc );
+                       }
+
+               } else if ( 0 ) {
+                       /* TODO: if any of the connections is binding,
+                        * release mc and create a new one */
                }
 
                for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                       metatarget_t            *mt = &mi->mi_targets[ i ];
-
                        /*
                         * The target is activated; if needed, it is
                         * also init'd
                         */
                        candidates[ i ].sr_err = meta_back_init_one_conn( op,
-                               rs, mt, mc, i,
-                               LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok );
+                               rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
+                               LDAP_BACK_DONTSEND, !new_conn );
                        if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
-                               candidates[ i ].sr_tag = META_CANDIDATE;
+                               META_CANDIDATE_SET( &candidates[ i ] );
                                ncandidates++;
        
                        } else {
@@ -912,7 +1223,7 @@ retry_lock:
                                 * be init'd, should the other ones
                                 * be tried?
                                 */
-                               candidates[ i ].sr_tag = META_NOT_CANDIDATE;
+                               META_CANDIDATE_RESET( &candidates[ i ] );
                                err = candidates[ i ].sr_err;
                                continue;
                        }
@@ -920,7 +1231,8 @@ retry_lock:
 
                if ( ncandidates == 0 ) {
                        if ( new_conn ) {
-                               meta_back_freeconn( op, mc );
+                               mc->mc_refcnt = 0;
+                               meta_back_conn_free( mc );
 
                        } else {
                                meta_back_release_conn( op, mc );
@@ -958,7 +1270,7 @@ retry_lock:
                int                     j;
 
                for ( j = 0; j < mi->mi_ntargets; j++ ) {
-                       candidates[ j ].sr_tag = META_NOT_CANDIDATE;
+                       META_CANDIDATE_RESET( &candidates[ j ] );
                }
 
                /*
@@ -1020,20 +1332,31 @@ retry_lock2:;
                                mc = (metaconn_t *)avl_find( mi->mi_conninfo.lai_tree, 
                                        (caddr_t)&mc_curr, meta_back_conndn_cmp );
                                if ( mc != NULL ) {
+                                       /* catch taint errors */
+                                       assert( !LDAP_BACK_CONN_TAINTED( mc ) );
+
                                        /* Don't reuse connections while they're still binding */
-                                       if ( LDAP_BACK_CONN_BINDING( mc ) ) {
-                                               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
-                                               ldap_pvt_thread_yield();
-                                               goto retry_lock2;
-                                       }
+                                       if ( META_BACK_CONN_CREATING( &mc->mc_conns[ i ] )
+                                               || LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) )
+                                       {
+                                               if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
+                                                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                                                       ldap_pvt_thread_yield();
+                                                       goto retry_lock2;
+                                               }
 
-                                       mc->mc_refcnt++;
+                                               mc = NULL;
+
+                                       } else {
+                                               mc->mc_refcnt++;
+                                       }
                                }
                                ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
                        }
 
                        /* Looks like we didn't get a bind. Open a new session... */
                        if ( mc == NULL ) {
+                               assert( new_conn == 0 );
                                mc = metaconn_alloc( op );
                                mc->mc_conn = mc_curr.mc_conn;
                                ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
@@ -1041,6 +1364,12 @@ retry_lock2:;
                                if ( sendok & LDAP_BACK_BINDING ) {
                                        LDAP_BACK_CONN_BINDING_SET( mc );
                                }
+                               if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+                                       LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+                               } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+                                       LDAP_BACK_CONN_ISANON_SET( mc );
+                               }
                        }
                }
 
@@ -1049,7 +1378,7 @@ retry_lock2:;
                 */
                ( void )meta_clear_unused_candidates( op, i );
 
-               mt = &mi->mi_targets[ i ];
+               mt = mi->mi_targets[ i ];
                msc = &mc->mc_conns[ i ];
 
                /*
@@ -1057,18 +1386,18 @@ retry_lock2:;
                 * also init'd. In case of error, meta_back_init_one_conn
                 * sends the appropriate result.
                 */
-               err = meta_back_init_one_conn( op, rs, mt, mc, i,
-                       LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok );
+               err = meta_back_init_one_conn( op, rs, mc, i,
+                       LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn );
                if ( err != LDAP_SUCCESS ) {
                        /*
                         * FIXME: in case one target cannot
                         * be init'd, should the other ones
                         * be tried?
                         */
-                       candidates[ i ].sr_tag = META_NOT_CANDIDATE;
+                       META_CANDIDATE_RESET( &candidates[ i ] );
                        if ( new_conn ) {
-                               (void)meta_clear_one_candidate( msc );
-                               meta_back_freeconn( op, mc );
+                               mc->mc_refcnt = 0;
+                               meta_back_conn_free( mc );
 
                        } else {
                                meta_back_release_conn( op, mc );
@@ -1077,7 +1406,7 @@ retry_lock2:;
                }
 
                candidates[ i ].sr_err = LDAP_SUCCESS;
-               candidates[ i ].sr_tag = META_CANDIDATE;
+               META_CANDIDATE_SET( &candidates[ i ] );
                ncandidates++;
 
                if ( candidate ) {
@@ -1091,43 +1420,52 @@ retry_lock2:;
 
                /* Looks like we didn't get a bind. Open a new session... */
                if ( mc == NULL ) {
+                       assert( new_conn == 0 );
                        mc = metaconn_alloc( op );
                        mc->mc_conn = mc_curr.mc_conn;
                        ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
                        new_conn = 1;
-                       if ( sendok & LDAP_BACK_BINDING ) {
-                               LDAP_BACK_CONN_BINDING_SET( mc );
+                       if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+                               LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+                       } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+                               LDAP_BACK_CONN_ISANON_SET( mc );
                        }
                }
 
                for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                       metatarget_t            *mt = &mi->mi_targets[ i ];
-                       metasingleconn_t        *msc = &mc->mc_conns[ i ];
+                       metatarget_t            *mt = mi->mi_targets[ i ];
+
+                       META_CANDIDATE_RESET( &candidates[ i ] );
 
                        if ( i == cached 
-                               || meta_back_is_candidate( &mt->mt_nsuffix,
-                                               mt->mt_scope,
-                                               mt->mt_subtree_exclude,
-                                               &op->o_req_ndn,
-                                               LDAP_SCOPE_SUBTREE ) )
+                               || meta_back_is_candidate( mt, &op->o_req_ndn,
+                                       LDAP_SCOPE_SUBTREE ) )
                        {
 
                                /*
                                 * The target is activated; if needed, it is
                                 * also init'd
                                 */
-                               int lerr = meta_back_init_one_conn( op, rs,
-                                               mt, mc, i,
-                                               LDAP_BACK_CONN_ISPRIV( &mc_curr ),
-                                               sendok );
+                               int lerr = meta_back_init_one_conn( op, rs, mc, i,
+                                       LDAP_BACK_CONN_ISPRIV( &mc_curr ),
+                                       LDAP_BACK_DONTSEND, !new_conn );
                                if ( lerr == LDAP_SUCCESS ) {
-                                       candidates[ i ].sr_tag = META_CANDIDATE;
+                                       META_CANDIDATE_SET( &candidates[ i ] );
                                        candidates[ i ].sr_err = LDAP_SUCCESS;
                                        ncandidates++;
 
                                        Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d]\n",
                                                op->o_log_prefix, i, 0 );
 
+                               } else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
+                                       META_CANDIDATE_SET( &candidates[ i ] );
+                                       candidates[ i ].sr_err = LDAP_UNAVAILABLE;
+
+                                       Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] %s\n",
+                                               op->o_log_prefix, i,
+                                               mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
+
                                } else {
                                
                                        /*
@@ -1136,36 +1474,61 @@ retry_lock2:;
                                         * be tried?
                                         */
                                        if ( new_conn ) {
-                                               ( void )meta_clear_one_candidate( msc );
+                                               ( void )meta_clear_one_candidate( op, mc, i );
                                        }
                                        /* leave the target candidate, but record the error for later use */
                                        candidates[ i ].sr_err = lerr;
                                        err = lerr;
 
-                                       Debug( LDAP_DEBUG_ANY, "%s: meta_back_getconn[%d] failed: %d\n",
-                                               op->o_log_prefix, i, lerr );
+                                       if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
+                                               Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] quarantined err=%d\n",
+                                                       op->o_log_prefix, i, lerr );
 
+                                       } else {
+                                               Debug( LDAP_DEBUG_ANY, "%s: meta_back_getconn[%d] failed err=%d\n",
+                                                       op->o_log_prefix, i, lerr );
+                                       }
+
+                                       if ( META_BACK_ONERR_STOP( mi ) ) {
+                                               if ( sendok & LDAP_BACK_SENDERR ) {
+                                                       send_ldap_result( op, rs );
+                                                       rs->sr_text = NULL;
+                                               }
+                                               if ( new_conn ) {
+                                                       mc->mc_refcnt = 0;
+                                                       meta_back_conn_free( mc );
+
+                                               } else {
+                                                       meta_back_release_conn( op, mc );
+                                               }
+
+                                               return NULL;
+                                       }
+
+                                       rs->sr_text = NULL;
                                        continue;
                                }
 
                        } else {
                                if ( new_conn ) {
-                                       ( void )meta_clear_one_candidate( msc );
+                                       ( void )meta_clear_one_candidate( op, mc, i );
                                }
-                               candidates[ i ].sr_tag = META_NOT_CANDIDATE;
                        }
                }
 
                if ( ncandidates == 0 ) {
                        if ( new_conn ) {
-                               meta_back_freeconn( op, mc );
+                               mc->mc_refcnt = 0;
+                               meta_back_conn_free( mc );
 
                        } else {
                                meta_back_release_conn( op, mc );
                        }
 
-                       rs->sr_err = LDAP_NO_SUCH_OBJECT;
-                       rs->sr_text = "Unable to select valid candidates";
+                       if ( rs->sr_err == LDAP_SUCCESS ) {
+                               rs->sr_err = LDAP_NO_SUCH_OBJECT;
+                               rs->sr_text = "Unable to select valid candidates";
+                       }
 
                        if ( sendok & LDAP_BACK_SENDERR ) {
                                if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
@@ -1191,7 +1554,6 @@ done:;
        }
 
        if ( new_conn ) {
-               
                if ( mi->mi_conn_ttl ) {
                        mc->mc_create_time = op->o_time;
                }
@@ -1200,49 +1562,86 @@ done:;
                 * Inserts the newly created metaconn in the avl tree
                 */
                ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
-               err = avl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc,
+#if META_BACK_PRINT_CONNTREE > 0
+               meta_back_print_conntree( mi, ">>> meta_back_getconn" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+               if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+                       if ( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num < mi->mi_conn_priv_max ) {
+                               LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
+                               mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num++;
+                               LDAP_BACK_CONN_CACHED_SET( mc );
+
+                       } else {
+                               LDAP_BACK_CONN_TAINTED_SET( mc );
+                       }
+                       rs->sr_err = 0;
+
+               } else {
+                       err = avl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc,
                                meta_back_conndn_cmp, meta_back_conndn_dup );
+                       LDAP_BACK_CONN_CACHED_SET( mc );
+               }
 
-#if PRINT_CONNTREE > 0
-               myprint( mi->mi_conninfo.lai_tree );
-#endif /* PRINT_CONNTREE */
-               
+#if META_BACK_PRINT_CONNTREE > 0
+               meta_back_print_conntree( mi, ">>> meta_back_getconn" );
+#endif /* META_BACK_PRINT_CONNTREE */
                ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
 
-               /*
-                * Err could be -1 in case a duplicate metaconn is inserted
-                *
-                * FIXME: what if the same client issues more than one
-                * asynchronous operations?
-                */
-               if ( err != 0 ) {
-                       Debug( LDAP_DEBUG_ANY,
-                               "%s meta_back_getconn: candidates=%d conn=%ld insert failed\n",
-                               op->o_log_prefix, ncandidates,
-                               LDAP_BACK_PCONN_ID( mc->mc_conn ) );
-               
-                       rs->sr_err = LDAP_OTHER;
-                       rs->sr_text = "Internal server error";
-                       meta_back_freeconn( op, mc );
-                       if ( sendok & LDAP_BACK_SENDERR ) {
-                               send_ldap_result( op, rs );
-                               rs->sr_text = NULL;
+               if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+                       /*
+                        * Err could be -1 in case a duplicate metaconn is inserted
+                        */
+                       switch ( err ) {
+                       case 0:
+                               break;
+
+                       case -1:
+                               LDAP_BACK_CONN_CACHED_CLEAR( mc );
+                               /* duplicate: free and try to get the newly created one */
+                               if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
+                                       mc->mc_refcnt = 0;      
+                                       meta_back_conn_free( mc );
+       
+                                       new_conn = 0;
+                                       goto retry_lock;
+                               }
+
+                               LDAP_BACK_CONN_TAINTED_SET( mc );
+                               break;
+
+                       default:
+                               LDAP_BACK_CONN_CACHED_CLEAR( mc );
+                               Debug( LDAP_DEBUG_ANY,
+                                       "%s meta_back_getconn: candidates=%d conn=%ld insert failed\n",
+                                       op->o_log_prefix, ncandidates,
+                                       LDAP_BACK_PCONN_ID( mc ) );
+       
+                               mc->mc_refcnt = 0;      
+                               meta_back_conn_free( mc );
+
+                               rs->sr_err = LDAP_OTHER;
+                               rs->sr_text = "proxy bind collision";
+                               if ( sendok & LDAP_BACK_SENDERR ) {
+                                       send_ldap_result( op, rs );
+                                       rs->sr_text = NULL;
+                               }
+                               return NULL;
                        }
-                       return NULL;
                }
 
                Debug( LDAP_DEBUG_TRACE,
                        "%s meta_back_getconn: candidates=%d conn=%ld inserted\n",
                        op->o_log_prefix, ncandidates,
-                       LDAP_BACK_PCONN_ID( mc->mc_conn ) );
+                       LDAP_BACK_PCONN_ID( mc ) );
 
        } else {
                Debug( LDAP_DEBUG_TRACE,
                        "%s meta_back_getconn: candidates=%d conn=%ld fetched\n",
                        op->o_log_prefix, ncandidates,
-                       LDAP_BACK_PCONN_ID( mc->mc_conn ) );
+                       LDAP_BACK_PCONN_ID( mc ) );
        }
-       
+
        return mc;
 }
 
@@ -1250,7 +1649,6 @@ void
 meta_back_release_conn_lock(
                Operation               *op,
        metaconn_t              *mc,
-       int                     dofree,
        int                     dolock )
 {
        metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
@@ -1262,21 +1660,140 @@ meta_back_release_conn_lock(
        }
        assert( mc->mc_refcnt > 0 );
        mc->mc_refcnt--;
-       LDAP_BACK_CONN_BINDING_CLEAR( mc );
-       if ( dofree
-               || ( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl )
-               || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout ) )
-       {
-               Debug( LDAP_DEBUG_TRACE, "%s meta_back_release_conn: mc=%p conn=%ld expired.\n",
-                       op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc->mc_conn ) );
-               (void)avl_delete( &mi->mi_conninfo.lai_tree,
-                       ( caddr_t )mc, meta_back_conndnmc_cmp );
+       /* NOTE: the connection is removed if either it is tainted
+        * or if it is shared and no one else is using it.  This needs
+        * to occur because for intrinsic reasons cached connections
+        * that are not privileged would live forever and pollute
+        * the connection space (and eat up resources).  Maybe this
+        * should be configurable... */
+       if ( LDAP_BACK_CONN_TAINTED( mc ) ) {
+               Debug( LDAP_DEBUG_TRACE, "%s meta_back_release_conn: mc=%p conn=%ld tainted.\n",
+                       op->o_log_prefix, (void *)mc, LDAP_BACK_PCONN_ID( mc ) );
+#if META_BACK_PRINT_CONNTREE > 0
+               meta_back_print_conntree( mi, ">>> meta_back_release_conn" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+               if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+                       if ( mc->mc_q.tqe_prev != NULL ) {
+                               assert( LDAP_BACK_CONN_CACHED( mc ) );
+                               assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
+                               mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
+                               LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
+
+                       } else {
+                               assert( !LDAP_BACK_CONN_CACHED( mc ) );
+                       }
+                       mc->mc_q.tqe_prev = NULL;
+                       mc->mc_q.tqe_next = NULL;
+
+               } else {
+                       metaconn_t      *tmpmc;
+
+                       tmpmc = avl_delete( &mi->mi_conninfo.lai_tree,
+                               ( caddr_t )mc, meta_back_conndnmc_cmp );
+
+                       if ( tmpmc == NULL ) {
+                               Debug( LDAP_DEBUG_TRACE, "%s: meta_back_release_conn: unable to find mc=%p\n",
+                                       op->o_log_prefix, (void *)mc, 0 );
+                       } else {
+                               assert( tmpmc == mc );
+                       }
+               }
+
+               LDAP_BACK_CONN_CACHED_CLEAR( mc );
+
+#if META_BACK_PRINT_CONNTREE > 0
+               meta_back_print_conntree( mi, "<<< meta_back_release_conn" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
                if ( mc->mc_refcnt == 0 ) {
-                       meta_clear_candidates( op, mc );
                        meta_back_conn_free( mc );
+                       mc = NULL;
                }
        }
+
+       if ( mc != NULL && LDAP_BACK_CONN_BINDING( mc ) ) {
+               LDAP_BACK_CONN_BINDING_CLEAR( mc );
+       }
+
        if ( dolock ) {
                ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
        }
 }
+
+void
+meta_back_quarantine(
+       Operation       *op,
+       SlapReply       *rs,
+       int             candidate )
+{
+       metainfo_t              *mi = (metainfo_t *)op->o_bd->be_private;
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
+
+       slap_retry_info_t       *ri = &mt->mt_quarantine;
+
+       ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
+
+       if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+               time_t  new_last = slap_get_time();
+
+               switch ( mt->mt_isquarantined ) {
+               case LDAP_BACK_FQ_NO:
+                       if ( ri->ri_last == new_last ) {
+                               goto done;
+                       }
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s meta_back_quarantine[%d]: enter.\n",
+                               op->o_log_prefix, candidate, 0 );
+
+                       ri->ri_idx = 0;
+                       ri->ri_count = 0;
+                       break;
+
+               case LDAP_BACK_FQ_RETRYING:
+                       if ( StatslogTest( LDAP_DEBUG_ANY ) ) {
+                               char    buf[ SLAP_TEXT_BUFLEN ];
+
+                               snprintf( buf, sizeof( buf ),
+                                       "meta_back_quarantine[%d]: block #%d try #%d failed",
+                                       candidate, ri->ri_idx, ri->ri_count );
+                               Debug( LDAP_DEBUG_ANY, "%s %s.\n",
+                                       op->o_log_prefix, buf, 0 );
+                       }
+
+                       ++ri->ri_count;
+                       if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
+                               && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
+                       {
+                               ri->ri_count = 0;
+                               ++ri->ri_idx;
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+
+               mt->mt_isquarantined = LDAP_BACK_FQ_YES;
+               ri->ri_last = new_last;
+
+       } else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "%s meta_back_quarantine[%d]: exit.\n",
+                       op->o_log_prefix, candidate, 0 );
+
+               if ( mi->mi_quarantine_f ) {
+                       (void)mi->mi_quarantine_f( mi, candidate,
+                               mi->mi_quarantine_p );
+               }
+
+               ri->ri_count = 0;
+               ri->ri_idx = 0;
+               mt->mt_isquarantined = LDAP_BACK_FQ_NO;
+       }
+
+done:;
+       ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
+}
+
index 3e188e09abc920c214a83cbde613f2ea47f2922b..883fac495ca6c008905ac684312afc778913c259 100644 (file)
@@ -35,13 +35,14 @@ int
 meta_back_delete( Operation *op, SlapReply *rs )
 {
        metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t    *mt;
        metaconn_t      *mc = NULL;
        int             candidate = -1;
        struct berval   mdn = BER_BVNULL;
        dncookie        dc;
        int             msgid;
        int             do_retry = 1;
-       int             maperr = 1;
+       LDAPControl     **ctrls = NULL;
 
        mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
        if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
@@ -53,82 +54,47 @@ meta_back_delete( Operation *op, SlapReply *rs )
        /*
         * Rewrite the compare dn, if needed
         */
-       dc.target = &mi->mi_targets[ candidate ];
+       mt = mi->mi_targets[ candidate ];
+       dc.target = mt;
        dc.conn = op->o_conn;
        dc.rs = rs;
        dc.ctx = "deleteDN";
 
        if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
                send_ldap_result( op, rs );
-               goto done;
+               goto cleanup;
        }
 
 retry:;
+       ctrls = op->o_ctrls;
+       if ( ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn,
+               mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ) != LDAP_SUCCESS )
+       {
+               send_ldap_result( op, rs );
+               goto cleanup;
+       }
+
        rs->sr_err = ldap_delete_ext( mc->mc_conns[ candidate ].msc_ld,
-                       mdn.bv_val, op->o_ctrls, NULL, &msgid );
+                       mdn.bv_val, ctrls, NULL, &msgid );
+       rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+               mt->mt_timeout[ SLAP_OP_DELETE ], LDAP_BACK_SENDRESULT );
        if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
                do_retry = 0;
                if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+                       /* if the identity changed, there might be need to re-authz */
+                       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
                        goto retry;
                }
-               goto cleanup;
-
-       } else if ( rs->sr_err == LDAP_SUCCESS ) {
-               struct timeval  tv, *tvp = NULL;
-               LDAPMessage     *res = NULL;
-               int             rc;
-
-               if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_DELETE ] != 0 ) {
-                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_DELETE ];
-                       tv.tv_usec = 0;
-                       tvp = &tv;
-               }
-
-               rs->sr_err = LDAP_OTHER;
-               maperr = 0;
-               rc = ldap_result( mc->mc_conns[ candidate ].msc_ld,
-                       msgid, LDAP_MSG_ALL, tvp, &res );
-               switch ( rc ) {
-               case -1:
-                       rs->sr_err = LDAP_OTHER;
-                       break;
-
-               case 0:
-                       ldap_abandon_ext( mc->mc_conns[ candidate ].msc_ld,
-                               msgid, NULL, NULL );
-                       rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
-                               LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR;
-                       break;
-
-               case LDAP_RES_DELETE:
-                       rc = ldap_parse_result( mc->mc_conns[ candidate ].msc_ld,
-                               res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 );
-                       if ( rc != LDAP_SUCCESS ) {
-                               rs->sr_err = rc;
-                       }
-                       maperr = 1;
-                       break;
-
-               default:
-                       ldap_msgfree( res );
-                       break;
-               }
-       }
-
-       if ( maperr ) {
-               rs->sr_err = meta_back_op_result( mc, op, rs, candidate );
-
-       } else {
-               send_ldap_result( op, rs );
        }
 
 cleanup:;
+       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
+
        if ( mdn.bv_val != op->o_req_dn.bv_val ) {
                free( mdn.bv_val );
                BER_BVZERO( &mdn );
        }
        
-done:;
        if ( mc ) {
                meta_back_release_conn( op, mc );
        }
index 2c625acb6d29584fd5d22b93604ef51150f7a9ae..13d59f8a7343bfb82480af48dde9186186ceaa2a 100644 (file)
@@ -76,6 +76,7 @@ meta_back_db_init(
        Backend         *be )
 {
        metainfo_t      *mi;
+       int             i;
 
        mi = ch_calloc( 1, sizeof( metainfo_t ) );
        if ( mi == NULL ) {
@@ -90,12 +91,20 @@ meta_back_db_init(
        mi->mi_bind_timeout.tv_sec = 0;
        mi->mi_bind_timeout.tv_usec = META_BIND_TIMEOUT;
 
+       mi->mi_rebind_f = meta_back_default_rebind;
+
        ldap_pvt_thread_mutex_init( &mi->mi_conninfo.lai_mutex );
        ldap_pvt_thread_mutex_init( &mi->mi_cache.mutex );
 
        /* safe default */
        mi->mi_nretries = META_RETRY_DEFAULT;
        mi->mi_version = LDAP_VERSION3;
+
+       for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+               mi->mi_conn_priv[ i ].mic_num = 0;
+               LDAP_TAILQ_INIT( &mi->mi_conn_priv[ i ].mic_priv );
+       }
+       mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_DEFAULT;
        
        be->be_private = mi;
 
@@ -108,46 +117,73 @@ meta_back_db_open(
 {
        metainfo_t      *mi = (metainfo_t *)be->be_private;
 
-       int             i, rc;
+       int             i,
+                       not_always = 0,
+                       rc;
 
        for ( i = 0; i < mi->mi_ntargets; i++ ) {
-               if ( mi->mi_targets[ i ].mt_flags & LDAP_BACK_F_SUPPORT_T_F_DISCOVER )
-               {
-                       mi->mi_targets[ i ].mt_flags &= ~LDAP_BACK_F_SUPPORT_T_F_DISCOVER;
-                       rc = slap_discover_feature( mi->mi_targets[ i ].mt_uri,
-                                       mi->mi_targets[ i ].mt_version,
+               metatarget_t    *mt = mi->mi_targets[ i ];
+
+               if ( META_BACK_TGT_T_F_DISCOVER( mt ) ) {
+                       rc = slap_discover_feature( mt->mt_uri, mt->mt_version,
                                        slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
                                        LDAP_FEATURE_ABSOLUTE_FILTERS );
                        if ( rc == LDAP_COMPARE_TRUE ) {
-                               mi->mi_targets[ i ].mt_flags |= LDAP_BACK_F_SUPPORT_T_F;
+                               mt->mt_flags |= LDAP_BACK_F_T_F;
+                       }
+               }
+
+               if ( META_BACK_TGT_CANCEL_DISCOVER( mt ) ) {
+                       rc = slap_discover_feature( mt->mt_uri, mt->mt_version,
+                                       slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+                                       LDAP_EXOP_CANCEL );
+                       if ( rc == LDAP_COMPARE_TRUE ) {
+                               mt->mt_flags |= LDAP_BACK_F_CANCEL_EXOP;
                        }
                }
+
+               if ( not_always == 0 ) {
+                       if ( !( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE )
+                               || !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+                       {
+                               not_always = 1;
+                       }
+               }
+       }
+
+       if ( not_always == 0 ) {
+               mi->mi_flags |= META_BACK_F_PROXYAUTHZ_ALWAYS;
        }
 
        return 0;
 }
 
+/*
+ * meta_back_conn_free()
+ *
+ * actually frees a connection; the reference count must be 0,
+ * and it must not (or no longer) be in the cache.
+ */
 void
 meta_back_conn_free( 
        void            *v_mc )
 {
        metaconn_t              *mc = v_mc;
-       int                     i, ntargets;
+       int                     ntargets;
 
        assert( mc != NULL );
        assert( mc->mc_refcnt == 0 );
 
-       if ( !BER_BVISNULL( &mc->mc_local_ndn ) ) {
-               free( mc->mc_local_ndn.bv_val );
-       }
-
-       assert( mc->mc_conns != NULL );
-
        /* at least one must be present... */
-       ntargets = mc->mc_conns[ 0 ].msc_info->mi_ntargets;
+       ntargets = mc->mc_info->mi_ntargets;
+       assert( ntargets > 0 );
+
+       for ( ; ntargets--; ) {
+               (void)meta_clear_one_candidate( NULL, mc, ntargets );
+       }
 
-       for ( i = 0; i < ntargets; i++ ) {
-               (void)meta_clear_one_candidate( &mc->mc_conns[ i ] );
+       if ( !BER_BVISNULL( &mc->mc_local_ndn ) ) {
+               free( mc->mc_local_ndn.bv_val );
        }
 
        free( mc );
@@ -180,6 +216,7 @@ target_free(
 {
        if ( mt->mt_uri ) {
                free( mt->mt_uri );
+               ldap_pvt_thread_mutex_destroy( &mt->mt_uri_mutex );
        }
        if ( mt->mt_subtree_exclude ) {
                ber_bvarray_free( mt->mt_subtree_exclude );
@@ -196,11 +233,26 @@ target_free(
        if ( !BER_BVISNULL( &mt->mt_bindpw ) ) {
                free( mt->mt_bindpw.bv_val );
        }
-       if ( !BER_BVISNULL( &mt->mt_pseudorootdn ) ) {
-               free( mt->mt_pseudorootdn.bv_val );
+       if ( !BER_BVISNULL( &mt->mt_idassert_authcID ) ) {
+               ch_free( mt->mt_idassert_authcID.bv_val );
+       }
+       if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
+               ch_free( mt->mt_idassert_authcDN.bv_val );
+       }
+       if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
+               ch_free( mt->mt_idassert_passwd.bv_val );
+       }
+       if ( !BER_BVISNULL( &mt->mt_idassert_authzID ) ) {
+               ch_free( mt->mt_idassert_authzID.bv_val );
+       }
+       if ( !BER_BVISNULL( &mt->mt_idassert_sasl_mech ) ) {
+               ch_free( mt->mt_idassert_sasl_mech.bv_val );
+       }
+       if ( !BER_BVISNULL( &mt->mt_idassert_sasl_realm ) ) {
+               ch_free( mt->mt_idassert_sasl_realm.bv_val );
        }
-       if ( !BER_BVISNULL( &mt->mt_pseudorootpw ) ) {
-               free( mt->mt_pseudorootpw.bv_val );
+       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 );
@@ -209,6 +261,8 @@ target_free(
        avl_free( mt->mt_rwmap.rwm_oc.map, mapping_free );
        avl_free( mt->mt_rwmap.rwm_at.remap, mapping_dst_free );
        avl_free( mt->mt_rwmap.rwm_at.map, mapping_free );
+
+       free( mt );
 }
 
 int
@@ -230,6 +284,14 @@ meta_back_db_destroy(
                if ( mi->mi_conninfo.lai_tree ) {
                        avl_free( mi->mi_conninfo.lai_tree, meta_back_conn_free );
                }
+               for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+                       while ( !LDAP_TAILQ_EMPTY( &mi->mi_conn_priv[ i ].mic_priv ) ) {
+                               metaconn_t      *mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ i ].mic_priv );
+
+                               LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ i ].mic_priv, mc, mc_q );
+                               meta_back_conn_free( mc );
+                       }
+               }
 
                /*
                 * Destroy the per-target stuff (assuming there's at
@@ -237,7 +299,18 @@ meta_back_db_destroy(
                 */
                if ( mi->mi_targets != NULL ) {
                        for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                               target_free( &mi->mi_targets[ i ] );
+                               metatarget_t    *mt = mi->mi_targets[ i ];
+
+                               if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+                                       if ( mt->mt_quarantine.ri_num != mi->mi_quarantine.ri_num )
+                                       {
+                                               slap_retry_info_destroy( &mt->mt_quarantine );
+                                       }
+
+                                       ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex );
+                               }
+
+                               target_free( mt );
                        }
 
                        free( mi->mi_targets );
@@ -257,6 +330,10 @@ meta_back_db_destroy(
                if ( mi->mi_candidates != NULL ) {
                        ber_memfree_x( mi->mi_candidates, NULL );
                }
+
+               if ( META_BACK_QUARANTINE( mi ) ) {
+                       slap_retry_info_destroy( &mi->mi_quarantine );
+               }
        }
 
        free( be->be_private );
index e6291cfbb80bf14dd48bb8d3e3345ac4bb15daa1..4d6aa59b61fc49f73c169052028a73363da0870d 100644 (file)
@@ -90,19 +90,19 @@ ldap_back_map_init ( struct ldapmap *lm, struct ldapmapping **m )
        assert( m != NULL );
 
        *m = NULL;
-       
+
        mapping = (struct ldapmapping *)ch_calloc( 2, 
                        sizeof( struct ldapmapping ) );
        if ( mapping == NULL ) {
                return;
        }
 
-       ber_str2bv( "objectclass", sizeof("objectclass")-1, 1, &mapping->src);
-       ber_dupbv( &mapping->dst, &mapping->src );
-       mapping[1].src = mapping->src;
-       mapping[1].dst = mapping->dst;
+       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
+       avl_insert( &lm->map, (caddr_t)&mapping[0]
                        mapping_cmp, mapping_dup );
        avl_insert( &lm->remap, (caddr_t)&mapping[1], 
                        mapping_cmp, mapping_dup );
@@ -120,6 +120,7 @@ ldap_back_mapping ( struct ldapmap *map, struct berval *s, struct ldapmapping **
 
        if ( remap == BACKLDAP_REMAP ) {
                tree = map->remap;
+
        } else {
                tree = map->map;
        }
@@ -139,6 +140,13 @@ ldap_back_map ( struct ldapmap *map, struct berval *s, struct berval *bv,
 {
        struct ldapmapping *mapping;
 
+       /* map->map may be NULL when mapping is configured,
+        * but map->remap can't */
+       if ( map->remap == NULL ) {
+               *bv = *s;
+               return;
+       }
+
        BER_BVZERO( bv );
        ( void )ldap_back_mapping( map, s, &mapping, remap );
        if ( mapping != NULL ) {
@@ -297,6 +305,9 @@ ldap_back_int_filter_map_rewrite(
                        ber_bvnone = BER_BVC( "(?=none)" );
        ber_len_t       len;
 
+       assert( fstr != NULL );
+       BER_BVZERO( fstr );
+
        if ( f == NULL ) {
                ber_dupbv( fstr, &ber_bvnone );
                return LDAP_OTHER;
@@ -513,7 +524,7 @@ ldap_back_int_filter_map_rewrite(
                /* FIXME: treat UNDEFINED as FALSE */
                case SLAPD_COMPARE_UNDEFINED:
 computed:;
-                       if ( dc->target->mt_flags & LDAP_BACK_F_SUPPORT_T_F ) {
+                       if ( META_BACK_TGT_T_F( dc->target ) ) {
                                tmp = &ber_bvtf_false;
                                break;
                        }
@@ -521,7 +532,7 @@ computed:;
                        break;
 
                case LDAP_COMPARE_TRUE:
-                       if ( dc->target->mt_flags & LDAP_BACK_F_SUPPORT_T_F ) {
+                       if ( META_BACK_TGT_T_F( dc->target ) ) {
                                tmp = &ber_bvtf_true;
                                break;
                        }
index a07b4cef40dc91b0d2c0b81f13e2a70d4370531a..40d67b53685e232b5d1fbe60c9e541c5a54c8ba1 100644 (file)
@@ -35,9 +35,9 @@ int
 meta_back_modify( Operation *op, SlapReply *rs )
 {
        metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t    *mt;
        metaconn_t      *mc;
        int             rc = 0;
-       int             maperr = 1;
        LDAPMod         **modv = NULL;
        LDAPMod         *mods = NULL;
        Modifications   *ml;
@@ -48,6 +48,7 @@ meta_back_modify( Operation *op, SlapReply *rs )
        dncookie        dc;
        int             msgid;
        int             do_retry = 1;
+       LDAPControl     **ctrls = NULL;
 
        mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
        if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
@@ -59,13 +60,14 @@ meta_back_modify( Operation *op, SlapReply *rs )
        /*
         * Rewrite the modify dn, if needed
         */
-       dc.target = &mi->mi_targets[ candidate ];
+       mt = mi->mi_targets[ candidate ];
+       dc.target = mt;
        dc.conn = op->o_conn;
        dc.rs = rs;
        dc.ctx = "modifyDN";
 
        if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
-               maperr = 0;
+               send_ldap_result( op, rs );
                goto cleanup;
        }
 
@@ -74,14 +76,14 @@ meta_back_modify( Operation *op, SlapReply *rs )
 
        mods = ch_malloc( sizeof( LDAPMod )*i );
        if ( mods == NULL ) {
-               rs->sr_err = LDAP_NO_MEMORY;
-               maperr = 0;
+               rs->sr_err = LDAP_OTHER;
+               send_ldap_result( op, rs );
                goto cleanup;
        }
        modv = ( LDAPMod ** )ch_malloc( ( i + 1 )*sizeof( LDAPMod * ) );
        if ( modv == NULL ) {
-               rs->sr_err = LDAP_NO_MEMORY;
-               maperr = 0;
+               rs->sr_err = LDAP_OTHER;
+               send_ldap_result( op, rs );
                goto cleanup;
        }
 
@@ -102,7 +104,7 @@ meta_back_modify( Operation *op, SlapReply *rs )
                        mapped = ml->sml_desc->ad_cname;
 
                } else {
-                       ldap_back_map( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at,
+                       ldap_back_map( &mt->mt_rwmap.rwm_at,
                                        &ml->sml_desc->ad_cname, &mapped,
                                        BACKLDAP_MAP );
                        if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
@@ -129,11 +131,11 @@ meta_back_modify( Operation *op, SlapReply *rs )
                                for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); ) {
                                        struct ldapmapping      *mapping;
 
-                                       ldap_back_mapping( &mi->mi_targets[ candidate ].mt_rwmap.rwm_oc,
+                                       ldap_back_mapping( &mt->mt_rwmap.rwm_oc,
                                                        &ml->sml_values[ j ], &mapping, BACKLDAP_MAP );
 
                                        if ( mapping == NULL ) {
-                                               if ( mi->mi_targets[ candidate ].mt_rwmap.rwm_oc.drop_missing ) {
+                                               if ( mt->mt_rwmap.rwm_oc.drop_missing ) {
                                                        continue;
                                                }
                                                mods[ i ].mod_bvalues[ j ] = &ml->sml_values[ j ];
@@ -175,66 +177,30 @@ meta_back_modify( Operation *op, SlapReply *rs )
        modv[ i ] = 0;
 
 retry:;
+       ctrls = op->o_ctrls;
+       rc = ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn,
+               mt->mt_version, &mt->mt_idassert, op, rs, &ctrls );
+       if ( rc != LDAP_SUCCESS ) {
+               send_ldap_result( op, rs );
+               goto cleanup;
+       }
+
        rs->sr_err = ldap_modify_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val,
-                       modv, op->o_ctrls, NULL, &msgid );
+                       modv, ctrls, NULL, &msgid );
+       rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+               mt->mt_timeout[ SLAP_OP_MODIFY ], LDAP_BACK_SENDRESULT );
        if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
                do_retry = 0;
                if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+                       /* if the identity changed, there might be need to re-authz */
+                       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
                        goto retry;
                }
-               goto done;
-
-       } else if ( rs->sr_err == LDAP_SUCCESS ) {
-               struct timeval  tv, *tvp = NULL;
-               LDAPMessage     *res = NULL;
-
-               if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODIFY ] != 0 ) {
-                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODIFY ];
-                       tv.tv_usec = 0;
-                       tvp = &tv;
-               }
-
-               rs->sr_err = LDAP_OTHER;
-               rc = ldap_result( mc->mc_conns[ candidate ].msc_ld,
-                       msgid, LDAP_MSG_ALL, tvp, &res );
-               switch ( rc ) {
-               case -1:
-                       maperr = 0;
-                       break;
-
-               case 0:
-                       ldap_abandon_ext( mc->mc_conns[ candidate ].msc_ld,
-                               msgid, NULL, NULL );
-                       rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
-                               LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR;
-                       maperr = 0;
-                       break;
-
-               case LDAP_RES_MODIFY:
-                       rc = ldap_parse_result( mc->mc_conns[ candidate ].msc_ld,
-                               res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 );
-                       if ( rc != LDAP_SUCCESS ) {
-                               rs->sr_err = rc;
-                       }
-                       maperr = 1;
-                       break;
-
-               default:
-                       maperr = 0;
-                       ldap_msgfree( res );
-                       break;
-               }
        }
 
 cleanup:;
-       if ( maperr ) {
-               rc = meta_back_op_result( mc, op, rs, candidate );
-
-       } else {
-               send_ldap_result( op, rs );
-       }
+       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
 
-done:;
        if ( mdn.bv_val != op->o_req_dn.bv_val ) {
                free( mdn.bv_val );
                BER_BVZERO( &mdn );
index cb84beb77ff095c51454fdc6c0f6c444c95b7c63..604257cc9d0315fe442bbdd7bf603365ba1012bf 100644 (file)
@@ -35,6 +35,7 @@ int
 meta_back_modrdn( Operation *op, SlapReply *rs )
 {
        metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t    *mt;
        metaconn_t      *mc;
        int             candidate = -1;
        struct berval   mdn = BER_BVNULL,
@@ -42,7 +43,7 @@ meta_back_modrdn( Operation *op, SlapReply *rs )
        dncookie        dc;
        int             msgid;
        int             do_retry = 1;
-       int             maperr = 1;
+       LDAPControl     **ctrls = NULL;
 
        mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
        if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
@@ -51,6 +52,8 @@ meta_back_modrdn( Operation *op, SlapReply *rs )
 
        assert( mc->mc_conns[ candidate ].msc_ld != NULL );
 
+       mt = mi->mi_targets[ candidate ];
+       dc.target = mt;
        dc.conn = op->o_conn;
        dc.rs = rs;
 
@@ -76,7 +79,7 @@ meta_back_modrdn( Operation *op, SlapReply *rs )
                 */
 
                /* needs LDAPv3 */
-               switch ( mi->mi_targets[ candidate ].mt_version ) {
+               switch ( mt->mt_version ) {
                case LDAP_VERSION3:
                        break;
 
@@ -90,18 +93,17 @@ meta_back_modrdn( Operation *op, SlapReply *rs )
                        /* op->o_protocol cannot be anything but LDAPv3,
                         * otherwise wouldn't be here */
                        rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
-                       maperr = 0;
+                       send_ldap_result( op, rs );
                        goto cleanup;
                }
                
                /*
                 * Rewrite the new superior, if defined and required
                 */
-               dc.target = &mi->mi_targets[ candidate ];
                dc.ctx = "newSuperiorDN";
                if ( ldap_back_dn_massage( &dc, op->orr_newSup, &mnewSuperior ) ) {
                        rs->sr_err = LDAP_OTHER;
-                       maperr = 0;
+                       send_ldap_result( op, rs );
                        goto cleanup;
                }
        }
@@ -109,76 +111,40 @@ meta_back_modrdn( Operation *op, SlapReply *rs )
        /*
         * Rewrite the modrdn dn, if required
         */
-       dc.target = &mi->mi_targets[ candidate ];
        dc.ctx = "modrDN";
        if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
                rs->sr_err = LDAP_OTHER;
-               maperr = 0;
+               send_ldap_result( op, rs );
                goto cleanup;
        }
 
 retry:;
+       ctrls = op->o_ctrls;
+       if ( ldap_back_proxy_authz_ctrl( &mc->mc_conns[ candidate ].msc_bound_ndn,
+               mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ) != LDAP_SUCCESS )
+       {
+               send_ldap_result( op, rs );
+               goto cleanup;
+       }
+
        rs->sr_err = ldap_rename( mc->mc_conns[ candidate ].msc_ld,
                        mdn.bv_val, op->orr_newrdn.bv_val,
                        mnewSuperior.bv_val, op->orr_deleteoldrdn,
-                       op->o_ctrls, NULL, &msgid );
+                       ctrls, NULL, &msgid );
+       rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+               mt->mt_timeout[ SLAP_OP_MODRDN ], LDAP_BACK_SENDRESULT );
        if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
                do_retry = 0;
                if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+                       /* if the identity changed, there might be need to re-authz */
+                       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
                        goto retry;
                }
-               goto done;
-
-       } else if ( rs->sr_err == LDAP_SUCCESS ) {
-               struct timeval  tv, *tvp = NULL;
-               LDAPMessage     *res = NULL;
-               int             rc;
-
-               if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODRDN ] != 0 ) {
-                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODRDN ];
-                       tv.tv_usec = 0;
-                       tvp = &tv;
-               }
-
-               rs->sr_err = LDAP_OTHER;
-               rc = ldap_result( mc->mc_conns[ candidate ].msc_ld,
-                       msgid, LDAP_MSG_ALL, tvp, &res );
-               maperr = 0;
-               switch ( rc ) {
-               case -1:
-                       break;
-
-               case 0:
-                       ldap_abandon_ext( mc->mc_conns[ candidate ].msc_ld,
-                               msgid, NULL, NULL );
-                       rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
-                               LDAP_ADMINLIMIT_EXCEEDED : LDAP_OPERATIONS_ERROR;
-                       break;
-
-               case LDAP_RES_RENAME:
-                       rc = ldap_parse_result( mc->mc_conns[ candidate ].msc_ld,
-                               res, &rs->sr_err, NULL, NULL, NULL, NULL, 1 );
-                       if ( rc != LDAP_SUCCESS ) {
-                               rs->sr_err = rc;
-                       }
-                       maperr = 1;
-                       break;
-
-               default:
-                       ldap_msgfree( res );
-                       break;
-               }
        }
 
 cleanup:;
-       if ( maperr ) {
-               meta_back_op_result( mc, op, rs, candidate );
-
-       } else {
-               send_ldap_result( op, rs );
-       }
+       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
 
-done:;
        if ( mdn.bv_val != op->o_req_dn.bv_val ) {
                free( mdn.bv_val );
                BER_BVZERO( &mdn );
index c45bfbd70b10a80f5432912b7bb5801174e0f2d0..fe250bee09325c68feee7a1c8ec3ca6ca06e6d71 100644 (file)
@@ -28,6 +28,7 @@
 #include <ac/string.h>
 #include <ac/time.h>
 
+#include "lutil.h"
 #include "slap.h"
 #include "../back-ldap/back-ldap.h"
 #include "back-meta.h"
 #include "ldap_log.h"
 #include "../../../libraries/libldap/ldap-int.h"
 
+/* IGNORE means that target does not (no longer) participate
+ * in the search;
+ * NOTREADY means the search on that target has not been initialized yet
+ */
+#define        META_MSGID_IGNORE       (-1)
+#define        META_MSGID_NEED_BIND    (-2)
+
 static int
 meta_send_entry(
        Operation       *op,
@@ -46,48 +54,356 @@ meta_send_entry(
 typedef enum meta_search_candidate_t {
        META_SEARCH_ERR = -1,
        META_SEARCH_NOT_CANDIDATE,
-       META_SEARCH_CANDIDATE
+       META_SEARCH_CANDIDATE,
+       META_SEARCH_BINDING,
+       META_SEARCH_NEED_BIND
 } meta_search_candidate_t;
 
+/*
+ * meta_search_dobind_init()
+ *
+ * initiates bind for a candidate target of a search.
+ */
 static meta_search_candidate_t
-meta_back_search_start(
+meta_search_dobind_init(
        Operation               *op,
        SlapReply               *rs,
-       dncookie                *dc,
-       metasingleconn_t        *msc,
+       metaconn_t              **mcp,
        int                     candidate,
-       SlapReply               *candidates
-)
+       SlapReply               *candidates )
 {
-       metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
-       struct berval   realbase = op->o_req_dn;
-       int             realscope = op->ors_scope;
-       ber_len_t       suffixlen = 0;
-       struct berval   mbase = BER_BVNULL; 
-       struct berval   mfilter = BER_BVNULL;
-       char            **mapped_attrs = NULL;
-       int             rc;
+       metaconn_t              *mc = *mcp;
+       metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
+       metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
+
+       struct berval           binddn = msc->msc_bound_ndn,
+                               cred = msc->msc_cred;
+       int                     method;
+
+       int                     rc;
+
        meta_search_candidate_t retcode;
-       struct timeval  tv, *tvp = NULL;
 
-       /* should we check return values? */
-       if ( op->ors_deref != -1 ) {
-               ldap_set_option( msc->msc_ld, LDAP_OPT_DEREF,
-                               ( void * )&op->ors_deref );
+       Debug( LDAP_DEBUG_TRACE, "%s >>> meta_search_dobind_init[%d]\n",
+               op->o_log_prefix, candidate, 0 );
+
+       /*
+        * all the targets are already bound as pseudoroot
+        */
+       if ( mc->mc_authz_target == META_BOUND_ALL ) {
+               return META_SEARCH_CANDIDATE;
        }
 
-       if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
-               tv.tv_sec = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
-               tvp = &tv;
+       retcode = META_SEARCH_BINDING;
+       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+       if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
+               /* already bound (or anonymous) */
+
+#ifdef DEBUG_205
+               char    buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+               int     bound = 0;
+
+               if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
+                       bound = 1;
+               }
+
+               snprintf( buf, sizeof( buf ), " mc=%p ld=%p%s DN=\"%s\"",
+                       (void *)mc, (void *)msc->msc_ld,
+                       bound ? " bound" : " anonymous",
+                       bound == 0 ? "" : msc->msc_bound_ndn.bv_val );
+               Debug( LDAP_DEBUG_ANY, "### %s meta_search_dobind_init[%d]%s\n",
+                       op->o_log_prefix, candidate, buf );
+#endif /* DEBUG_205 */
+
+               retcode = META_SEARCH_CANDIDATE;
+
+       } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) {
+               /* another thread is binding the target for this conn; wait */
+
+#ifdef DEBUG_205
+               char    buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+
+               snprintf( buf, sizeof( buf ), " mc=%p ld=%p needbind",
+                       (void *)mc, (void *)msc->msc_ld );
+               Debug( LDAP_DEBUG_ANY, "### %s meta_search_dobind_init[%d]%s\n",
+                       op->o_log_prefix, candidate, buf );
+#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 ];
+
+               snprintf( buf, sizeof( buf ), " mc=%p ld=%p binding",
+                       (void *)mc, (void *)msc->msc_ld );
+               Debug( LDAP_DEBUG_ANY, "### %s meta_search_dobind_init[%d]%s\n",
+                       op->o_log_prefix, candidate, buf );
+#endif /* DEBUG_205 */
+
+               if ( msc->msc_ld == NULL ) {
+                       /* for some reason (e.g. because formerly in "binding"
+                        * state, with eventual connection expiration or invalidation)
+                        * it was not initialized as expected */
+
+                       Debug( LDAP_DEBUG_ANY, "%s meta_search_dobind_init[%d] mc=%p ld=NULL\n",
+                               op->o_log_prefix, candidate, (void *)mc );
+
+                       rc = meta_back_init_one_conn( op, rs, *mcp, candidate,
+                               LDAP_BACK_CONN_ISPRIV( *mcp ), LDAP_BACK_DONTSEND, 0 );
+                       switch ( rc ) {
+                       case LDAP_SUCCESS:
+                               assert( msc->msc_ld != NULL );
+                               break;
+
+                       case LDAP_SERVER_DOWN:
+                       case LDAP_UNAVAILABLE:
+                               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                               goto down;
+       
+                       default:
+                               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                               goto other;
+                       }
+               }
+
+               LDAP_BACK_CONN_BINDING_SET( msc );
+       }
+
+       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+       if ( retcode != META_SEARCH_BINDING ) {
+               return retcode;
+       }
+
+       /* NOTE: this obsoletes pseudorootdn */
+       if ( op->o_conn != NULL &&
+               !op->o_do_not_cache &&
+               ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
+                       BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
+                       ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
+       {
+               rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, LDAP_BACK_DONTSEND, &binddn, &cred, &method );
+               if ( rc != LDAP_SUCCESS ) {
+                       goto down;
+               }
+
+               /* 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 ) ) {
+                       ber_bvreplace( &msc->msc_bound_ndn, &binddn );
+                       if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &cred ) ) {
+                               if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+                                       memset( msc->msc_cred.bv_val, 0,
+                                               msc->msc_cred.bv_len );
+                               }
+                               ber_bvreplace( &msc->msc_cred, &cred );
+                       }
+               }
+
+               if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
+                       /* apparently, idassert was configured with SASL bind,
+                        * so bind occurred inside meta_back_proxy_authz_cred() */
+                       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+                       LDAP_BACK_CONN_BINDING_CLEAR( msc );
+                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                       return META_SEARCH_CANDIDATE;
+               }
+
+               /* paranoid */
+               switch ( method ) {
+               case LDAP_AUTH_NONE:
+               case LDAP_AUTH_SIMPLE:
+                       /* do a simple bind with binddn, cred */
+                       break;
+
+               default:
+                       assert( 0 );
+                       break;
+               }
+       }
+
+       assert( msc->msc_ld != NULL );
+
+       rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred,
+                       NULL, NULL, &candidates[ candidate ].sr_msgid );
+
+#ifdef DEBUG_205
+       {
+               char buf[ SLAP_TEXT_BUFLEN ];
+
+               snprintf( buf, sizeof( buf ), "meta_search_dobind_init[%d] mc=%p ld=%p rc=%d",
+                       candidate, (void *)mc, (void *)mc->mc_conns[ candidate ].msc_ld, rc );
+               Debug( LDAP_DEBUG_ANY, "### %s %s\n",
+                       op->o_log_prefix, buf, 0 );
+       }
+#endif /* DEBUG_205 */
+
+       switch ( rc ) {
+       case LDAP_SUCCESS:
+               assert( candidates[ candidate ].sr_msgid >= 0 );
+               META_BINDING_SET( &candidates[ candidate ] );
+               return META_SEARCH_BINDING;
+
+       case LDAP_SERVER_DOWN:
+down:;
+               /* This is the worst thing that could happen:
+                * the search will wait until the retry is over. */
+               if ( meta_back_retry( op, rs, mcp, candidate, LDAP_BACK_DONTSEND ) ) {
+                       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+                       return META_SEARCH_CANDIDATE;
+               }
+
+               if ( *mcp == NULL ) {
+                       retcode = META_SEARCH_ERR;
+                       rs->sr_err = LDAP_UNAVAILABLE;
+                       break;
+               }
+               /* fall thru */
+
+       default:
+other:;
+               rs->sr_err = rc;
+               rc = slap_map_api2result( rs );
+
+               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+               meta_clear_one_candidate( op, mc, candidate );
+               if ( META_BACK_ONERR_STOP( mi ) ) {
+                       LDAP_BACK_CONN_TAINTED_SET( mc );
+                       meta_back_release_conn_lock( op, mc, 0 );
+                       *mcp = NULL;
+
+                       retcode = META_SEARCH_ERR;
+
+               } else {
+                       if ( META_BACK_ONERR_REPORT( mi ) ) {
+                               candidates[ candidate ].sr_err = rc;
+                       }
+
+                       retcode = META_SEARCH_NOT_CANDIDATE;
+               }
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+               break;
+       }
+
+       return retcode;
+}
+
+static meta_search_candidate_t
+meta_search_dobind_result(
+       Operation               *op,
+       SlapReply               *rs,
+       metaconn_t              **mcp,
+       int                     candidate,
+       SlapReply               *candidates,
+       LDAPMessage             *res )
+{
+       metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
+       metaconn_t              *mc = *mcp;
+       metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
+
+       meta_search_candidate_t retcode = META_SEARCH_NOT_CANDIDATE;
+       int                     rc;
+
+       assert( msc->msc_ld != NULL );
+
+       /* FIXME: matched? referrals? response controls? */
+       rc = ldap_parse_result( msc->msc_ld, res,
+               &candidates[ candidate ].sr_err,
+               NULL, NULL, NULL, NULL, 0 );
+       if ( rc != LDAP_SUCCESS ) {
+               candidates[ candidate ].sr_err = rc;
+
+       } else {
+               rc = slap_map_api2result( &candidates[ candidate ] );
+       }
+
+       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+       LDAP_BACK_CONN_BINDING_CLEAR( msc );
+       if ( rc != LDAP_SUCCESS ) {
+               if ( META_BACK_ONERR_STOP( mi ) ) {
+                       LDAP_BACK_CONN_TAINTED_SET( mc );
+                       meta_clear_one_candidate( op, mc, candidate );
+                       meta_back_release_conn_lock( op, mc, 0 );
+                       *mcp = NULL;
+                       retcode = META_SEARCH_ERR;
+                       rs->sr_err = rc;
+
+               } else if ( META_BACK_ONERR_REPORT( mi ) ) {
+                       candidates[ candidate ].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 );
+
+               } else {
+                       LDAP_BACK_CONN_ISBOUND_SET( msc );
+               }
+               retcode = META_SEARCH_CANDIDATE;
+       }
+
+       candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+       META_BINDING_CLEAR( &candidates[ candidate ] );
+
+       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+       return retcode;
+}
+
+static meta_search_candidate_t
+meta_back_search_start(
+       Operation               *op,
+       SlapReply               *rs,
+       dncookie                *dc,
+       metaconn_t              **mcp,
+       int                     candidate,
+       SlapReply               *candidates )
+{
+       metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
+       metatarget_t            *mt = mi->mi_targets[ candidate ];
+       metasingleconn_t        *msc = &(*mcp)->mc_conns[ candidate ];
+       struct berval           realbase = op->o_req_dn;
+       int                     realscope = op->ors_scope;
+       struct berval           mbase = BER_BVNULL; 
+       struct berval           mfilter = BER_BVNULL;
+       char                    **mapped_attrs = NULL;
+       int                     rc;
+       meta_search_candidate_t retcode;
+       struct timeval          tv, *tvp = NULL;
+       int                     nretries = 1;
+       LDAPControl             **ctrls = NULL;
+
+       /* this should not happen; just in case... */
+       if ( msc->msc_ld == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "%s: meta_back_search_start candidate=%d ld=NULL%s.\n",
+                       op->o_log_prefix, candidate,
+                       META_BACK_ONERR_STOP( mi ) ? "" : " (ignored)" );
+               if ( META_BACK_ONERR_STOP( mi ) ) {
+                       return META_SEARCH_ERR;
+               }
+               if ( META_BACK_ONERR_REPORT( mi ) ) {
+                       candidates[ candidate ].sr_err = LDAP_OTHER;
+               }
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               return META_SEARCH_NOT_CANDIDATE;
        }
 
-       dc->target = &mi->mi_targets[ candidate ];
+       Debug( LDAP_DEBUG_TRACE, "%s >>> meta_back_search_start[%d]\n", op->o_log_prefix, candidate, 0 );
 
        /*
         * modifies the base according to the scope, if required
         */
-       suffixlen = mi->mi_targets[ candidate ].mt_nsuffix.bv_len;
-       if ( suffixlen > op->o_req_ndn.bv_len ) {
+       if ( mt->mt_nsuffix.bv_len > op->o_req_ndn.bv_len ) {
                switch ( op->ors_scope ) {
                case LDAP_SCOPE_SUBTREE:
                        /*
@@ -98,11 +414,9 @@ meta_back_search_start(
                         * the requested searchBase already passed
                         * thru the candidate analyzer...
                         */
-                       if ( dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix,
-                                       &op->o_req_ndn ) )
-                       {
-                               realbase = mi->mi_targets[ candidate ].mt_nsuffix;
-                               if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+                       if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) {
+                               realbase = mt->mt_nsuffix;
+                               if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
                                        realscope = LDAP_SCOPE_SUBORDINATE;
                                }
 
@@ -110,26 +424,27 @@ meta_back_search_start(
                                /*
                                 * this target is no longer candidate
                                 */
-                               return META_SEARCH_NOT_CANDIDATE;
+                               retcode = META_SEARCH_NOT_CANDIDATE;
+                               goto doreturn;
                        }
                        break;
 
                case LDAP_SCOPE_SUBORDINATE:
                case LDAP_SCOPE_ONELEVEL:
                {
-                       struct berval   rdn = mi->mi_targets[ candidate ].mt_nsuffix;
+                       struct berval   rdn = mt->mt_nsuffix;
                        rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
                        if ( dnIsOneLevelRDN( &rdn )
-                                       && dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix, &op->o_req_ndn ) )
+                                       && dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) )
                        {
                                /*
                                 * if there is exactly one level,
                                 * make the target suffix the new
                                 * base, and make scope "base"
                                 */
-                               realbase = mi->mi_targets[ candidate ].mt_nsuffix;
+                               realbase = mt->mt_nsuffix;
                                if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
-                                       if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+                                       if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
                                                realscope = LDAP_SCOPE_SUBORDINATE;
                                        } else {
                                                realscope = LDAP_SCOPE_SUBTREE;
@@ -145,13 +460,24 @@ meta_back_search_start(
                        /*
                         * this target is no longer candidate
                         */
-                       return META_SEARCH_NOT_CANDIDATE;
+                       retcode = META_SEARCH_NOT_CANDIDATE;
+                       goto doreturn;
                }
        }
 
+       /* initiate dobind */
+       retcode = meta_search_dobind_init( op, rs, mcp, candidate, candidates );
+
+       Debug( LDAP_DEBUG_TRACE, "%s <<< meta_search_dobind_init[%d]=%d\n", op->o_log_prefix, candidate, retcode );
+
+       if ( retcode != META_SEARCH_CANDIDATE ) {
+               goto doreturn;
+       }
+
        /*
         * Rewrite the search base, if required
         */
+       dc->target = mt;
        dc->ctx = "searchBase";
        switch ( ldap_back_dn_massage( dc, &realbase, &mbase ) ) {
        case LDAP_SUCCESS:
@@ -161,14 +487,16 @@ meta_back_search_start(
                rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
                rs->sr_text = "Operation not allowed";
                send_ldap_result( op, rs );
-               return META_SEARCH_ERR;
+               retcode = META_SEARCH_ERR;
+               goto doreturn;
 
        default:
 
                /*
                 * this target is no longer candidate
                 */
-               return META_SEARCH_NOT_CANDIDATE;
+               retcode = META_SEARCH_NOT_CANDIDATE;
+               goto doreturn;
        }
 
        /*
@@ -192,7 +520,7 @@ meta_back_search_start(
        /*
         * Maps required attributes
         */
-       rc = ldap_back_map_attrs( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at,
+       rc = ldap_back_map_attrs( &mt->mt_rwmap.rwm_at,
                        op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
        if ( rc != LDAP_SUCCESS ) {
                /*
@@ -202,23 +530,66 @@ meta_back_search_start(
                goto done;
        }
 
+       /* should we check return values? */
+       if ( op->ors_deref != -1 ) {
+               assert( msc->msc_ld != NULL );
+               (void)ldap_set_option( msc->msc_ld, LDAP_OPT_DEREF,
+                               ( void * )&op->ors_deref );
+       }
+
+       if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
+               tv.tv_sec = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
+               tv.tv_usec = 0;
+               tvp = &tv;
+       }
+
+retry:;
+       ctrls = op->o_ctrls;
+       if ( ldap_back_proxy_authz_ctrl( &msc->msc_bound_ndn,
+               mt->mt_version, &mt->mt_idassert, op, rs, &ctrls )
+               != LDAP_SUCCESS )
+       {
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+               retcode = META_SEARCH_NOT_CANDIDATE;
+               goto done;
+       }
+
        /*
         * Starts the search
         */
+       assert( msc->msc_ld != NULL );
        rc = ldap_search_ext( msc->msc_ld,
                        mbase.bv_val, realscope, mfilter.bv_val,
                        mapped_attrs, op->ors_attrsonly,
-                       op->o_ctrls, NULL, tvp, op->ors_slimit,
+                       ctrls, NULL, tvp, op->ors_slimit,
                        &candidates[ candidate ].sr_msgid ); 
-       if ( rc == LDAP_SUCCESS ) {
+       switch ( rc ) {
+       case LDAP_SUCCESS:
                retcode = META_SEARCH_CANDIDATE;
+               break;
+       
+       case LDAP_SERVER_DOWN:
+               if ( nretries && meta_back_retry( op, rs, mcp, candidate, LDAP_BACK_DONTSEND ) ) {
+                       nretries = 0;
+                       /* if the identity changed, there might be need to re-authz */
+                       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
+                       goto retry;
+               }
 
-       } else {
-               candidates[ candidate ].sr_msgid = -1;
+               if ( *mcp == NULL ) {
+                       retcode = META_SEARCH_ERR;
+                       break;
+               }
+               /* fall thru */
+
+       default:
+               candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
                retcode = META_SEARCH_NOT_CANDIDATE;
        }
 
 done:;
+       (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
+
        if ( mapped_attrs ) {
                free( mapped_attrs );
        }
@@ -229,6 +600,9 @@ done:;
                free( mbase.bv_val );
        }
 
+doreturn:;
+       Debug( LDAP_DEBUG_TRACE, "%s <<< meta_back_search_start[%d]=%d\n", op->o_log_prefix, candidate, retcode );
+
        return retcode;
 }
 
@@ -237,13 +611,15 @@ meta_back_search( Operation *op, SlapReply *rs )
 {
        metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
        metaconn_t      *mc;
-       struct timeval  tv = { 0, 0 };
+       struct timeval  save_tv = { 0, 0 },
+                       tv;
        time_t          stoptime = (time_t)-1;
-       LDAPMessage     *res = NULL, *e;
        int             rc = 0, sres = LDAP_SUCCESS;
        char            *matched = NULL;
        int             last = 0, ncandidates = 0,
-                       initial_candidates = 0, candidate_match = 0;
+                       initial_candidates = 0, candidate_match = 0,
+                       needbind = 0;
+       ldap_back_send_t        sendok = LDAP_BACK_SENDERR;
        long            i;
        dncookie        dc;
        int             is_ok = 0;
@@ -256,8 +632,9 @@ meta_back_search( Operation *op, SlapReply *rs )
         * FIXME: in case of values return filter, we might want
         * to map attrs and maybe rewrite value
         */
-       mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR );
-       if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
+getconn:;
+       mc = meta_back_getconn( op, rs, NULL, sendok );
+       if ( !mc ) {
                return rs->sr_err;
        }
 
@@ -268,56 +645,114 @@ meta_back_search( Operation *op, SlapReply *rs )
         * Inits searches
         */
        for ( i = 0; i < mi->mi_ntargets; i++ ) {
-               metasingleconn_t        *msc = &mc->mc_conns[ i ];
+               /* reset sr_msgid; it is used in most loops
+                * to check if that target is still to be considered */
+               candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+
+               /* a target is marked as candidate by meta_back_getconn();
+                * if for any reason (an error, it's over or so) it is
+                * no longer active, sr_msgid is set to META_MSGID_IGNORE
+                * but it remains candidate, which means it has been active
+                * at some point during the operation.  This allows to 
+                * use its response code and more to compute the final
+                * response */
+               if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+                       continue;
+               }
 
-               candidates[ i ].sr_msgid = -1;
                candidates[ i ].sr_matched = NULL;
                candidates[ i ].sr_text = NULL;
                candidates[ i ].sr_ref = NULL;
                candidates[ i ].sr_ctrls = NULL;
+       }
 
-               if ( candidates[ i ].sr_tag != META_CANDIDATE
+       for ( i = 0; i < mi->mi_ntargets; i++ ) {
+               if ( !META_IS_CANDIDATE( &candidates[ i ] )
                        || candidates[ i ].sr_err != LDAP_SUCCESS )
                {
                        continue;
                }
 
-               switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) )
+               switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates ) )
                {
                case META_SEARCH_NOT_CANDIDATE:
+                       candidates[ i ].sr_msgid = META_MSGID_IGNORE;
                        break;
 
+               case META_SEARCH_NEED_BIND:
+                       ++needbind;
+                       /* fallthru */
+
                case META_SEARCH_CANDIDATE:
+               case META_SEARCH_BINDING:
                        candidates[ i ].sr_type = REP_INTERMEDIATE;
                        ++ncandidates;
                        break;
 
                case META_SEARCH_ERR:
+                       savepriv = op->o_private;
+                       op->o_private = (void *)i;
+                       send_ldap_result( op, rs );
+                       op->o_private = savepriv;
                        rc = -1;
                        goto finish;
                }
        }
 
+       if ( ncandidates > 0 && needbind == ncandidates ) {
+               /*
+                * give up the second time...
+                *
+                * NOTE: this should not occur the second time, since a fresh
+                * connection has ben created; however, targets may also
+                * need bind because the bind timed out or so.
+                */
+               if ( sendok & LDAP_BACK_BINDING ) {
+                       Debug( LDAP_DEBUG_ANY,
+                               "%s meta_back_search: unable to initialize conn\n",
+                               op->o_log_prefix, 0, 0 );
+                       rs->sr_err = LDAP_UNAVAILABLE;
+                       rs->sr_text = "unable to initialize connection to remote targets";
+                       send_ldap_result( op, rs );
+                       rc = -1;
+                       goto finish;
+               }
+
+               /* FIXME: better create a separate connection? */
+               sendok |= LDAP_BACK_BINDING;
+
+#ifdef DEBUG_205
+               Debug( LDAP_DEBUG_ANY, "*** %s drop mc=%p create new connection\n",
+                       op->o_log_prefix, (void *)mc, 0 );
+#endif /* DEBUG_205 */
+
+               meta_back_release_conn( op, mc );
+               mc = NULL;
+
+               needbind = 0;
+               ncandidates = 0;
+
+               goto getconn;
+       }
+
        initial_candidates = ncandidates;
 
-#if 0
-       {
-               char    cnd[BUFSIZ];
-               int     i;
+       if ( StatslogTest( LDAP_DEBUG_TRACE ) ) {
+               char    cnd[ SLAP_TEXT_BUFLEN ];
+               int     c;
 
-               for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                       if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
-                               cnd[ i ] = '*';
+               for ( c = 0; c < mi->mi_ntargets; c++ ) {
+                       if ( META_IS_CANDIDATE( &candidates[ c ] ) ) {
+                               cnd[ c ] = '*';
                        } else {
-                               cnd[ i ] = ' ';
+                               cnd[ c ] = ' ';
                        }
                }
-               cnd[ i ] = '\0';
+               cnd[ c ] = '\0';
 
-               Debug( LDAP_DEBUG_ANY, "%s meta_back_search: ncandidates=%d "
+               Debug( LDAP_DEBUG_TRACE, "%s meta_back_search: ncandidates=%d "
                        "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
        }
-#endif
 
        if ( initial_candidates == 0 ) {
                /* NOTE: here we are not sending any matchedDN;
@@ -337,7 +772,7 @@ meta_back_search( Operation *op, SlapReply *rs )
                 * maybe we should pick the worst... */
                rc = LDAP_NO_SUCH_OBJECT;
                for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                       if ( candidates[ i ].sr_tag == META_CANDIDATE
+                       if ( META_IS_CANDIDATE( &candidates[ i ] )
                                && candidates[ i ].sr_err != LDAP_SUCCESS )
                        {
                                rc = candidates[ i ].sr_err;
@@ -366,12 +801,114 @@ meta_back_search( Operation *op, SlapReply *rs )
         * among the candidates
         */
        for ( rc = 0; ncandidates > 0; ) {
-               int     gotit = 0, doabandon = 0;
+               int     gotit = 0,
+                       doabandon = 0,
+                       alreadybound = ncandidates;
+
+               /* check time limit */
+               if ( op->ors_tlimit != SLAP_NO_LIMIT
+                               && slap_get_time() > stoptime )
+               {
+                       doabandon = 1;
+                       rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+                       savepriv = op->o_private;
+                       op->o_private = (void *)i;
+                       send_ldap_result( op, rs );
+                       op->o_private = savepriv;
+                       goto finish;
+               }
 
                for ( i = 0; i < mi->mi_ntargets; i++ ) {
                        metasingleconn_t        *msc = &mc->mc_conns[ i ];
+                       LDAPMessage             *res = NULL, *msg;
+
+                       /* if msgid is invalid, don't ldap_result() */
+                       if ( candidates[ i ].sr_msgid == META_MSGID_IGNORE ) {
+                               continue;
+                       }
+
+                       /* if target still needs bind, retry */
+                       if ( candidates[ i ].sr_msgid == META_MSGID_NEED_BIND ) {
+                               meta_search_candidate_t retcode;
+
+                               /* initiate dobind */
+                               retcode = meta_search_dobind_init( op, rs, &mc, i, candidates );
+
+                               Debug( LDAP_DEBUG_TRACE, "%s <<< meta_search_dobind_init[%ld]=%d\n",
+                                       op->o_log_prefix, i, retcode );
+
+                               switch ( retcode ) {
+                               case META_SEARCH_NEED_BIND:
+                                       alreadybound--;
+                                       /* fallthru */
+
+                               case META_SEARCH_BINDING:
+                                       break;
+
+                               case META_SEARCH_ERR:
+                                       if ( META_BACK_ONERR_STOP( mi ) ) {
+                                               savepriv = op->o_private;
+                                               op->o_private = (void *)i;
+                                               send_ldap_result( op, rs );
+                                               op->o_private = savepriv;
+                                               goto finish;
+                                       }
+                                       if ( META_BACK_ONERR_REPORT( mi ) ) {
+                                               candidates[ i ].sr_err = rs->sr_err;
+                                       }
+                                       /* fallthru */
+
+                               case META_SEARCH_NOT_CANDIDATE:
+                                       /*
+                                        * When no candidates are left,
+                                        * the outer cycle finishes
+                                        */
+                                       candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                       assert( ncandidates > 0 );
+                                       --ncandidates;
+                                       break;
+
+                               case META_SEARCH_CANDIDATE:
+                                       candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                       switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates ) )
+                                       {
+                                       case META_SEARCH_CANDIDATE:
+                                               assert( candidates[ i ].sr_msgid >= 0 );
+                                               break;
+
+                                       case META_SEARCH_ERR:
+                                               if ( META_BACK_ONERR_STOP( mi ) ) {
+                                                       savepriv = op->o_private;
+                                                       op->o_private = (void *)i;
+                                                       send_ldap_result( op, rs );
+                                                       op->o_private = savepriv;
+                                                       goto finish;
+                                               }
+                                               if ( META_BACK_ONERR_REPORT( mi ) ) {
+                                                       candidates[ i ].sr_err = rs->sr_err;
+                                               }
+                                               /* fallthru */
+
+                                       case META_SEARCH_NOT_CANDIDATE:
+                                               /* means that meta_back_search_start()
+                                                * failed but onerr == continue */
+                                               candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                               assert( ncandidates > 0 );
+                                               --ncandidates;
+                                               break;
+
+                                       default:
+                                               /* impossible */
+                                               assert( 0 );
+                                               break;
+                                       }
+                                       break;
 
-                       if ( candidates[ i ].sr_msgid == -1 ) {
+                               default:
+                                       /* impossible */
+                                       assert( 0 );
+                                       break;
+                               }
                                continue;
                        }
 
@@ -379,6 +916,24 @@ meta_back_search( Operation *op, SlapReply *rs )
                        if ( op->o_abandon ) {
                                break;
                        }
+
+#ifdef DEBUG_205
+                       if ( msc->msc_ld == NULL ) {
+                               char    buf[ SLAP_TEXT_BUFLEN ];
+
+                               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+                               snprintf( buf, sizeof( buf ),
+                                       "%s meta_back_search[%ld] mc=%p msgid=%d%s%s%s\n",
+                                       op->o_log_prefix, (long)i, (void *)mc,
+                                       candidates[ i ].sr_msgid,
+                                       META_IS_BINDING( &candidates[ i ] ) ? " binding" : "",
+                                       LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) ? " connbinding" : "",
+                                       META_BACK_CONN_CREATING( &mc->mc_conns[ i ] ) ? " conncreating" : "" );
+                               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                                       
+                               Debug( LDAP_DEBUG_ANY, "!!! %s\n", buf, 0, 0 );
+                       }
+#endif /* DEBUG_205 */
                        
                        /*
                         * FIXME: handle time limit as well?
@@ -387,230 +942,235 @@ meta_back_search( Operation *op, SlapReply *rs )
                         * get a LDAP_TIMELIMIT_EXCEEDED from
                         * one of them ...
                         */
-get_result:;
+                       tv = save_tv;
                        rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid,
-                                       LDAP_MSG_ONE, &tv, &res );
-
-                       if ( rc == 0 ) {
+                                       LDAP_MSG_RECEIVED, &tv, &res );
+                       switch ( rc ) {
+                       case 0:
                                /* FIXME: res should not need to be freed */
                                assert( res == NULL );
-
-                               /* check time limit */
-                               if ( op->ors_tlimit != SLAP_NO_LIMIT
-                                               && slap_get_time() > stoptime )
-                               {
-                                       doabandon = 1;
-                                       rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
-                                       savepriv = op->o_private;
-                                       op->o_private = (void *)i;
-                                       send_ldap_result( op, rs );
-                                       op->o_private = savepriv;
-                                       goto finish;
-                               }
-
                                continue;
 
-                       } else if ( rc == -1 ) {
+                       case -1:
 really_bad:;
                                /* something REALLY bad happened! */
                                if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
                                        candidates[ i ].sr_type = REP_RESULT;
 
                                        if ( meta_back_retry( op, rs, &mc, i, LDAP_BACK_DONTSEND ) ) {
-                                               switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) )
+                                               candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                               switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates ) )
                                                {
                                                case META_SEARCH_CANDIDATE:
-                                                       goto get_result;
+                                                       /* get back into business... */
+                                                       continue;
+
+                                                       /* means that failed but onerr == continue */
+                                               case META_SEARCH_NOT_CANDIDATE:
+                                                       candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                                       --ncandidates;
+
+                                                       if ( META_BACK_ONERR_STOP( mi ) ) {
+                                                               savepriv = op->o_private;
+                                                               op->o_private = (void *)i;
+                                                               send_ldap_result( op, rs );
+                                                               op->o_private = savepriv;
+                                                               goto finish;
+                                                       }
+                                                       if ( META_BACK_ONERR_REPORT( mi ) ) {
+                                                               candidates[ i ].sr_err = rs->sr_err;
+                                                       }
+                                                       break;
+
+                                               case META_SEARCH_BINDING:
+                                               case META_SEARCH_NEED_BIND:
+                                                       assert( 0 );
 
                                                default:
+                                                       /* unrecoverable error */
+                                                       candidates[ i ].sr_msgid = META_MSGID_IGNORE;
                                                        rc = rs->sr_err = LDAP_OTHER;
                                                        goto finish;
                                                }
                                        }
 
-                                       savepriv = op->o_private;
-                                       op->o_private = (void *)i;
-                                       send_ldap_result( op, rs );
-                                       op->o_private = savepriv;
-                                       goto finish;
+                                       if ( META_BACK_ONERR_STOP( mi ) ) {
+                                               savepriv = op->o_private;
+                                               op->o_private = (void *)i;
+                                               send_ldap_result( op, rs );
+                                               op->o_private = savepriv;
+                                               goto finish;
+                                       }
+                                       if ( META_BACK_ONERR_REPORT( mi ) ) {
+                                               candidates[ i ].sr_err = rs->sr_err;
+                                       }
                                }
 
                                /*
                                 * When no candidates are left,
                                 * the outer cycle finishes
                                 */
-                               candidates[ i ].sr_msgid = -1;
+                               candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                               assert( ncandidates > 0 );
                                --ncandidates;
-                               rs->sr_err = candidates[ i ].sr_err = LDAP_OTHER;
-                               rs->sr_text = "remote server unavailable";
+                               rs->sr_err = candidates[ i ].sr_err;
+                               continue;
 
-                       } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
-                               if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
-                                       /* don't retry any more... */
-                                       candidates[ i ].sr_type = REP_RESULT;
+                       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;
                                }
+                               break;
+                       }
+
+                       for ( msg = ldap_first_message( msc->msc_ld, res );
+                               msg != NULL;
+                               msg = ldap_next_message( msc->msc_ld, msg ) )
+                       {
+                               rc = ldap_msgtype( msg );
+                               if ( rc == LDAP_RES_SEARCH_ENTRY ) {
+                                       LDAPMessage     *e;
 
-                               is_ok++;
+                                       if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+                                               /* don't retry any more... */
+                                               candidates[ i ].sr_type = REP_RESULT;
+                                       }
 
-                               e = ldap_first_entry( msc->msc_ld, res );
-                               savepriv = op->o_private;
-                               op->o_private = (void *)i;
-                               rs->sr_err = meta_send_entry( op, rs, mc, i, e );
-                               ldap_msgfree( res );
-                               res = NULL;
+                                       is_ok++;
 
-                               switch ( rs->sr_err ) {
-                               case LDAP_SIZELIMIT_EXCEEDED:
+                                       e = ldap_first_entry( msc->msc_ld, msg );
                                        savepriv = op->o_private;
                                        op->o_private = (void *)i;
-                                       send_ldap_result( op, rs );
-                                       op->o_private = savepriv;
-                                       rs->sr_err = LDAP_SUCCESS;
-                                       goto finish;
+                                       rs->sr_err = meta_send_entry( op, rs, mc, i, e );
 
-                               case LDAP_UNAVAILABLE:
-                                       rs->sr_err = LDAP_OTHER;
-                                       goto finish;
-                               }
-                               op->o_private = savepriv;
-
-                               /* don't wait any longer... */
-                               gotit = 1;
-                               tv.tv_sec = 0;
-                               tv.tv_usec = 0;
-
-#if 0
-                               /*
-                                * If scope is BASE, we need to jump out
-                                * as soon as one entry is found; if
-                                * the target pool is properly crafted,
-                                * this should correspond to the sole
-                                * entry that has the base DN
-                                */
-                               /* FIXME: this defeats the purpose of
-                                * doing a search with scope == base and
-                                * sizelimit = 1 to determine if a
-                                * candidate is actually unique */
-                               if ( op->ors_scope == LDAP_SCOPE_BASE
-                                               && rs->sr_nentries > 0 )
-                               {
-                                       doabandon = 1;
-                                       ncandidates = 0;
-                                       sres = LDAP_SUCCESS;
-                                       break;
-                               }
-#endif
-
-                       } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
-                               char            **references = NULL;
-                               int             cnt;
-
-                               if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
-                                       /* don't retry any more... */
-                                       candidates[ i ].sr_type = REP_RESULT;
-                               }
+                                       switch ( rs->sr_err ) {
+                                       case LDAP_SIZELIMIT_EXCEEDED:
+                                               savepriv = op->o_private;
+                                               op->o_private = (void *)i;
+                                               send_ldap_result( op, rs );
+                                               op->o_private = savepriv;
+                                               rs->sr_err = LDAP_SUCCESS;
+                                               ldap_msgfree( res );
+                                               res = NULL;
+                                               goto finish;
 
-                               is_ok++;
+                                       case LDAP_UNAVAILABLE:
+                                               rs->sr_err = LDAP_OTHER;
+                                               ldap_msgfree( res );
+                                               res = NULL;
+                                               goto finish;
+                                       }
+                                       op->o_private = savepriv;
 
-                               rc = ldap_parse_reference( msc->msc_ld, res,
-                                               &references, &rs->sr_ctrls, 1 );
-                               res = NULL;
+                                       /* don't wait any longer... */
+                                       gotit = 1;
+                                       save_tv.tv_sec = 0;
+                                       save_tv.tv_usec = 0;
 
-                               if ( rc != LDAP_SUCCESS ) {
-                                       continue;
-                               }
+                               } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
+                                       char            **references = NULL;
+                                       int             cnt;
 
-                               if ( references == NULL ) {
-                                       continue;
-                               }
+                                       if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+                                               /* don't retry any more... */
+                                               candidates[ i ].sr_type = REP_RESULT;
+                                       }
+       
+                                       is_ok++;
+       
+                                       rc = ldap_parse_reference( msc->msc_ld, msg,
+                                                       &references, &rs->sr_ctrls, 0 );
+       
+                                       if ( rc != LDAP_SUCCESS ) {
+                                               continue;
+                                       }
+       
+                                       if ( references == NULL ) {
+                                               continue;
+                                       }
 
 #ifdef ENABLE_REWRITE
-                               dc.ctx = "referralDN";
+                                       dc.ctx = "referralDN";
 #else /* ! ENABLE_REWRITE */
-                               dc.tofrom = 0;
-                               dc.normalized = 0;
+                                       dc.tofrom = 0;
+                                       dc.normalized = 0;
 #endif /* ! ENABLE_REWRITE */
 
-                               /* FIXME: merge all and return at the end */
-
-                               for ( cnt = 0; references[ cnt ]; cnt++ )
-                                       ;
-
-                               rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
-
-                               for ( cnt = 0; references[ cnt ]; cnt++ ) {
-                                       ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
-                               }
-                               BER_BVZERO( &rs->sr_ref[ cnt ] );
-
-                               ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
-
-                               if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
-                                       /* ignore return value by now */
-                                       savepriv = op->o_private;
-                                       op->o_private = (void *)i;
-                                       ( void )send_search_reference( op, rs );
-                                       op->o_private = savepriv;
-
-                                       ber_bvarray_free( rs->sr_ref );
-                                       rs->sr_ref = NULL;
-                               }
+                                       /* FIXME: merge all and return at the end */
+       
+                                       for ( cnt = 0; references[ cnt ]; cnt++ )
+                                               ;
+       
+                                       rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
+       
+                                       for ( cnt = 0; references[ cnt ]; cnt++ ) {
+                                               ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
+                                       }
+                                       BER_BVZERO( &rs->sr_ref[ cnt ] );
+       
+                                       ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
 
-                               /* cleanup */
-                               if ( references ) {
-                                       ber_memvfree( (void **)references );
-                               }
+                                       if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
+                                               /* ignore return value by now */
+                                               savepriv = op->o_private;
+                                               op->o_private = (void *)i;
+                                               ( void )send_search_reference( op, rs );
+                                               op->o_private = savepriv;
+       
+                                               ber_bvarray_free( rs->sr_ref );
+                                               rs->sr_ref = NULL;
+                                       }
 
-                               if ( rs->sr_ctrls ) {
-                                       ldap_controls_free( rs->sr_ctrls );
-                                       rs->sr_ctrls = NULL;
-                               }
+                                       /* cleanup */
+                                       if ( references ) {
+                                               ber_memvfree( (void **)references );
+                                       }
 
-                       } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
-                               char            buf[ SLAP_TEXT_BUFLEN ];
-                               char            **references = NULL;
+                                       if ( rs->sr_ctrls ) {
+                                               ldap_controls_free( rs->sr_ctrls );
+                                               rs->sr_ctrls = NULL;
+                                       }
 
-                               if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
-                                       /* don't retry any more... */
-                                       candidates[ i ].sr_type = REP_RESULT;
-                               }
+                               } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
+                                       char            buf[ SLAP_TEXT_BUFLEN ];
+                                       char            **references = NULL;
 
-                               /* NOTE: ignores response controls
-                                * (and intermediate response controls
-                                * as well, except for those with search
-                                * references); this may not be correct,
-                                * but if they're not ignored then
-                                * back-meta would need to merge them
-                                * consistently (think of pagedResults...)
-                                */
-                               rs->sr_err = ldap_parse_result( msc->msc_ld,
-                                                       res,
-                                                       &candidates[ i ].sr_err,
-                                                       (char **)&candidates[ i ].sr_matched,
-                                                       NULL /* (char **)&candidates[ i ].sr_text */ ,
-                                                       &references,
-                                                       NULL /* &candidates[ i ].sr_ctrls (unused) */ ,
-                                                       1 );
-                               res = NULL;
-                               if ( rs->sr_err != LDAP_SUCCESS ) {
-                                       ldap_get_option( msc->msc_ld,
+                                       if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+                                               /* don't retry any more... */
+                                               candidates[ i ].sr_type = REP_RESULT;
+                                       }
+       
+                                       /* NOTE: ignores response controls
+                                        * (and intermediate response controls
+                                        * as well, except for those with search
+                                        * references); this may not be correct,
+                                        * but if they're not ignored then
+                                        * back-meta would need to merge them
+                                        * consistently (think of pagedResults...)
+                                        */
+                                       /* FIXME: response controls? */
+                                       rs->sr_err = ldap_parse_result( msc->msc_ld,
+                                               msg,
+                                               &candidates[ i ].sr_err,
+                                               (char **)&candidates[ i ].sr_matched,
+                                               NULL /* (char **)&candidates[ i ].sr_text */ ,
+                                               &references,
+                                               NULL /* &candidates[ i ].sr_ctrls (unused) */ ,
+                                               0 );
+                                       if ( rs->sr_err != LDAP_SUCCESS ) {
+                                               ldap_get_option( msc->msc_ld,
                                                        LDAP_OPT_ERROR_NUMBER,
                                                        &rs->sr_err );
-                                       sres = slap_map_api2result( rs );
-                                       candidates[ i ].sr_type = REP_RESULT;
-                                       goto really_bad;
-                               }
-
-                               /* massage matchedDN if need be */
-                               if ( candidates[ i ].sr_matched != NULL ) {
-#ifndef LDAP_NULL_IS_NULL
-                                       if ( candidates[ i ].sr_matched[ 0 ] == '\0' ) {
-                                               ldap_memfree( (char *)candidates[ i ].sr_matched );
-                                               candidates[ i ].sr_matched = NULL;
+                                               sres = slap_map_api2result( rs );
+                                               candidates[ i ].sr_type = REP_RESULT;
+                                               ldap_msgfree( res );
+                                               res = NULL;
+                                               goto really_bad;
+                                       }
 
-                                       } else
-#endif /* LDAP_NULL_IS_NULL */
-                                       {
+                                       /* massage matchedDN if need be */
+                                       if ( candidates[ i ].sr_matched != NULL ) {
                                                struct berval   match, mmatch;
 
                                                ber_str2bv( candidates[ i ].sr_matched,
@@ -618,10 +1178,11 @@ really_bad:;
                                                candidates[ i ].sr_matched = NULL;
 
                                                dc.ctx = "matchedDN";
-                                               dc.target = &mi->mi_targets[ i ];
+                                               dc.target = mi->mi_targets[ i ];
                                                if ( !ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
                                                        if ( mmatch.bv_val == match.bv_val ) {
-                                                               candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val );
+                                                               candidates[ i ].sr_matched
+                                                                       = ch_strdup( mmatch.bv_val );
 
                                                        } else {
                                                                candidates[ i ].sr_matched = mmatch.bv_val;
@@ -631,156 +1192,268 @@ really_bad:;
                                                } 
                                                ldap_memfree( match.bv_val );
                                        }
-                               }
-
-#ifndef LDAP_NULL_IS_NULL
-                               /* just get rid of the error message, if any */
-                               if ( candidates[ i ].sr_text && candidates[ i ].sr_text[ 0 ] == '\0' )
-                               {
-                                       ldap_memfree( (char *)candidates[ i ].sr_text );
-                                       candidates[ i ].sr_text = NULL;
-                               }
-#endif /* LDAP_NULL_IS_NULL */
-
-                               /* add references to array */
-                               if ( references ) {
-                                       BerVarray       sr_ref;
-                                       int             cnt;
-
-                                       for ( cnt = 0; references[ cnt ]; cnt++ )
-                                               ;
-
-                                       sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
-
-                                       for ( cnt = 0; references[ cnt ]; cnt++ ) {
-                                               ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] );
-                                       }
-                                       BER_BVZERO( &sr_ref[ cnt ] );
 
-                                       ( void )ldap_back_referral_result_rewrite( &dc, sr_ref );
-                               
-                                       /* cleanup */
-                                       ber_memvfree( (void **)references );
-
-                                       if ( rs->sr_v2ref == NULL ) {
-                                               rs->sr_v2ref = sr_ref;
+                                       /* add references to array */
+                                       if ( references ) {
+                                               BerVarray       sr_ref;
+                                               int             cnt;
+       
+                                               for ( cnt = 0; references[ cnt ]; cnt++ )
+                                                       ;
+       
+                                               sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
+       
+                                               for ( cnt = 0; references[ cnt ]; cnt++ ) {
+                                                       ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] );
+                                               }
+                                               BER_BVZERO( &sr_ref[ cnt ] );
+       
+                                               ( void )ldap_back_referral_result_rewrite( &dc, sr_ref );
+                                       
+                                               /* cleanup */
+                                               ber_memvfree( (void **)references );
+       
+                                               if ( rs->sr_v2ref == NULL ) {
+                                                       rs->sr_v2ref = sr_ref;
 
-                                       } else {
-                                               for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
-                                                       ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] );
+                                               } else {
+                                                       for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
+                                                               ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] );
+                                                       }
+                                                       ber_memfree( sr_ref );
                                                }
-                                               ber_memfree( sr_ref );
                                        }
-                               }
-
-                               rs->sr_err = candidates[ i ].sr_err;
-                               sres = slap_map_api2result( rs );
-
-                               if ( StatslogTest( LDAP_DEBUG_TRACE | LDAP_DEBUG_ANY ) ) {
-                                       snprintf( buf, sizeof( buf ),
-                                               "%s meta_back_search[%ld] "
-                                               "match=\"%s\" err=%ld",
-                                               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, 0, 0 );
-
-                                       } else {
-                                               Debug( LDAP_DEBUG_ANY, "%s (%s).\n",
-                                                       buf, ldap_err2string( candidates[ i ].sr_err ), 0 );
+       
+                                       rs->sr_err = candidates[ i ].sr_err;
+                                       sres = slap_map_api2result( rs );
+       
+                                       if ( StatslogTest( LDAP_DEBUG_TRACE | LDAP_DEBUG_ANY ) ) {
+                                               snprintf( buf, sizeof( buf ),
+                                                       "%s meta_back_search[%ld] "
+                                                       "match=\"%s\" err=%ld",
+                                                       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, 0, 0 );
+       
+                                               } else {
+                                                       Debug( LDAP_DEBUG_ANY, "%s (%s).\n",
+                                                               buf, ldap_err2string( candidates[ i ].sr_err ), 0 );
+                                               }
                                        }
-                               }
-
-                               switch ( sres ) {
-                               case LDAP_NO_SUCH_OBJECT:
-                                       /* is_ok is touched any time a valid
-                                        * (even intermediate) result is
-                                        * returned; as a consequence, if
-                                        * a candidate returns noSuchObject
-                                        * it is ignored and the candidate
-                                        * is simply demoted. */
-                                       if ( is_ok ) {
-                                               sres = LDAP_SUCCESS;
+       
+                                       switch ( sres ) {
+                                       case LDAP_NO_SUCH_OBJECT:
+                                               /* is_ok is touched any time a valid
+                                                * (even intermediate) result is
+                                                * returned; as a consequence, if
+                                                * a candidate returns noSuchObject
+                                                * it is ignored and the candidate
+                                                * is simply demoted. */
+                                               if ( is_ok ) {
+                                                       sres = LDAP_SUCCESS;
+                                               }
+                                               break;
+       
+                                       case LDAP_SUCCESS:
+                                       case LDAP_REFERRAL:
+                                               is_ok++;
+                                               break;
+       
+                                       case LDAP_SIZELIMIT_EXCEEDED:
+                                               /* if a target returned sizelimitExceeded
+                                                * and the entry count is equal to the
+                                                * proxy's limit, the target would have
+                                                * returned more, and the error must be
+                                                * propagated to the client; otherwise,
+                                                * the target enforced a limit lower
+                                                * than what requested by the proxy;
+                                                * ignore it */
+                                               if ( rs->sr_nentries == op->ors_slimit
+                                                       || META_BACK_ONERR_STOP( mi ) )
+                                               {
+                                                       savepriv = op->o_private;
+                                                       op->o_private = (void *)i;
+                                                       send_ldap_result( op, rs );
+                                                       op->o_private = savepriv;
+                                                       ldap_msgfree( res );
+                                                       res = NULL;
+                                                       goto finish;
+                                               }
+                                               if ( META_BACK_ONERR_REPORT( mi ) ) {
+                                                       candidates[ i ].sr_err = rs->sr_err;
+                                               }
+                                               break;
+       
+                                       default:
+                                               if ( META_BACK_ONERR_STOP( mi ) ) {
+                                                       savepriv = op->o_private;
+                                                       op->o_private = (void *)i;
+                                                       send_ldap_result( op, rs );
+                                                       op->o_private = savepriv;
+                                                       ldap_msgfree( res );
+                                                       res = NULL;
+                                                       goto finish;
+                                               }
+                                               if ( META_BACK_ONERR_REPORT( mi ) ) {
+                                                       candidates[ i ].sr_err = rs->sr_err;
+                                               }
+                                               break;
                                        }
-                                       break;
-
-                               case LDAP_SUCCESS:
-                               case LDAP_REFERRAL:
-                                       is_ok++;
-                                       break;
-
-                               case LDAP_SIZELIMIT_EXCEEDED:
-                                       /* if a target returned sizelimitExceeded
-                                        * and the entry count is equal to the
-                                        * proxy's limit, the target would have
-                                        * returned more, and the error must be
-                                        * propagated to the client; otherwise,
-                                        * the target enforced a limit lower
-                                        * than what requested by the proxy;
-                                        * ignore it */
-                                       if ( rs->sr_nentries == op->ors_slimit
-                                               || META_BACK_ONERR_STOP( mi ) )
-                                       {
-                                               savepriv = op->o_private;
-                                               op->o_private = (void *)i;
-                                               send_ldap_result( op, rs );
-                                               op->o_private = savepriv;
-                                               goto finish;
+       
+                                       last = i;
+                                       rc = 0;
+       
+                                       /*
+                                        * When no candidates are left,
+                                        * the outer cycle finishes
+                                        */
+                                       candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                       --ncandidates;
+       
+                               } else if ( rc == LDAP_RES_BIND ) {
+                                       meta_search_candidate_t retcode;
+       
+                                       retcode = meta_search_dobind_result( op, rs, &mc, i, candidates, msg );
+                                       if ( retcode == META_SEARCH_CANDIDATE ) {
+                                               candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                               retcode = meta_back_search_start( op, rs, &dc, &mc, i, candidates );
                                        }
-                                       break;
-
-                               default:
-                                       if ( META_BACK_ONERR_STOP( mi ) ) {
-                                               savepriv = op->o_private;
-                                               op->o_private = (void *)i;
-                                               send_ldap_result( op, rs );
-                                               op->o_private = savepriv;
-                                               goto finish;
+       
+                                       switch ( retcode ) {
+                                       case META_SEARCH_CANDIDATE:
+                                               break;
+       
+                                               /* means that failed but onerr == continue */
+                                       case META_SEARCH_NOT_CANDIDATE:
+                                       case META_SEARCH_ERR:
+                                               candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                               --ncandidates;
+       
+                                               if ( META_BACK_ONERR_STOP( mi ) ) {
+                                                       savepriv = op->o_private;
+                                                       op->o_private = (void *)i;
+                                                       send_ldap_result( op, rs );
+                                                       op->o_private = savepriv;
+                                                       ldap_msgfree( res );
+                                                       res = NULL;
+                                                       goto finish;
+                                               }
+                                               if ( META_BACK_ONERR_REPORT( mi ) ) {
+                                                       candidates[ i ].sr_err = rs->sr_err;
+                                               }
+                                               break;
+       
+                                       default:
+                                               assert( 0 );
+                                               break;
                                        }
-                                       break;
+       
+                               } else {
+                                       assert( 0 );
+                                       ldap_msgfree( res );
+                                       res = NULL;
+                                       goto really_bad;
                                }
-
-                               last = i;
-                               rc = 0;
-
-                               /*
-                                * When no candidates are left,
-                                * the outer cycle finishes
-                                */
-                               candidates[ i ].sr_msgid = -1;
-                               --ncandidates;
-
-                       } else {
-                               assert( 0 );
-                               goto really_bad;
                        }
+
+                       ldap_msgfree( res );
+                       res = NULL;
                }
 
                /* check for abandon */
                if ( op->o_abandon || doabandon ) {
                        for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                               metasingleconn_t        *msc = &mc->mc_conns[ i ];
+                               if ( candidates[ i ].sr_msgid >= 0 ) {
+                                       if ( META_IS_BINDING( &candidates[ i ] ) ) {
+                                               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+                                               if ( LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) ) {
+                                                       /* if still binding, destroy */
 
-                               if ( candidates[ i ].sr_msgid != -1 ) {
-                                       ldap_abandon_ext( msc->msc_ld,
-                                               candidates[ i ].sr_msgid,
-                                               NULL, NULL );
-                                       candidates[ i ].sr_msgid = -1;
+#ifdef DEBUG_205
+                                                       char buf[ SLAP_TEXT_BUFLEN ];
+
+                                                       snprintf( buf, sizeof( buf), "%s meta_back_search(abandon) "
+                                                               "ldap_unbind_ext[%ld] mc=%p ld=%p",
+                                                               op->o_log_prefix, i, (void *)mc,
+                                                               (void *)mc->mc_conns[i].msc_ld );
+
+                                                       Debug( LDAP_DEBUG_ANY, "### %s\n", buf, 0, 0 );
+#endif /* DEBUG_205 */
+
+                                                       meta_clear_one_candidate( op, mc, i );
+                                               }
+                                               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                                               META_BINDING_CLEAR( &candidates[ i ] );
+                                               
+                                       } else {
+                                               (void)meta_back_cancel( mc, op, rs,
+                                                       candidates[ i ].sr_msgid, i,
+                                                       LDAP_BACK_DONTSEND );
+                                       }
+
+                                       candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+                                       assert( ncandidates > 0 );
+                                       --ncandidates;
                                }
                        }
 
                        if ( op->o_abandon ) {
                                rc = SLAPD_ABANDON;
-                               goto finish;
+                               /* let send_ldap_result play cleanup handlers (ITS#4645) */
+                               break;
                        }
                }
 
                /* if no entry was found during this loop,
                 * set a minimal timeout */
-               if ( gotit == 0 ) {
-                       LDAP_BACK_TV_SET( &tv );
-                        ldap_pvt_thread_yield();
+               if ( ncandidates > 0 && gotit == 0 ) {
+                       if ( save_tv.tv_sec == 0 && save_tv.tv_usec == 0 ) {
+                               save_tv.tv_usec = LDAP_BACK_RESULT_UTIMEOUT/initial_candidates;
+
+                               /* arbitrarily limit to something between 1 and 2 minutes */
+                       } else if ( ( stoptime == -1 && save_tv.tv_sec < 60 )
+                               || save_tv.tv_sec < ( stoptime - slap_get_time() ) / ( 2 * ncandidates ) )
+                       {
+                               /* double the timeout */
+                               lutil_timermul( &save_tv, 2, &save_tv );
+                       }
+
+#if 0
+                       if ( StatslogTest( LDAP_DEBUG_TRACE ) ) {
+                               char    buf[ SLAP_TEXT_BUFLEN ];
+
+                               snprintf( buf, sizeof( buf ), "%s %ld.%06ld %d/%d mc=%p",
+                                       op->o_log_prefix, save_tv.tv_sec, save_tv.tv_usec,
+                                       ncandidates, initial_candidates, mc );
+                               Debug( LDAP_DEBUG_TRACE, "### %s\n", buf, 0, 0 );
+                               for ( i = 0; i < mi->mi_ntargets; i++ ) {
+                                       if ( candidates[ i ].sr_msgid == META_MSGID_IGNORE ) {
+                                               continue;
+                                       }
+                       
+                                       snprintf( buf, sizeof( buf ), "[%ld] ld=%p%s%s\n",
+                                               i,
+                                               mc->mc_conns[ i ].msc_ld,
+                                               ( candidates[ i ].sr_msgid == META_MSGID_NEED_BIND ) ?  " needbind" : "",
+                                               META_IS_BINDING( &candidates[ i ] ) ? " binding" : "" );
+                                       Debug( LDAP_DEBUG_TRACE, "###    %s\n", buf, 0, 0 );
+                               }
+                       }
+#endif
+
+                       if ( alreadybound == 0 ) {
+#if 0
+                               Debug( LDAP_DEBUG_TRACE, "### %s select(%ld.%06ld)\n",
+                                       op->o_log_prefix, save_tv.tv_sec, save_tv.tv_usec );
+#endif
+                               tv = save_tv;
+                               (void)select( 0, NULL, NULL, NULL, &tv );
+
+                       } else {
+                               ldap_pvt_thread_yield();
+                       }
                }
        }
 
@@ -788,7 +1461,13 @@ really_bad:;
                /*
                 * FIXME: need a better strategy to handle errors
                 */
-               rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE );
+               if ( mc ) {
+                       rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE,
+                               -1, stoptime != -1 ? (stoptime - slap_get_time()) : 0,
+                               LDAP_BACK_SENDERR );
+               } else {
+                       rc = rs->sr_err;
+               }
                goto finish;
        }
 
@@ -804,7 +1483,7 @@ really_bad:;
 
                /* we use the first one */
                for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                       if ( candidates[ i ].sr_tag == META_CANDIDATE
+                       if ( META_IS_CANDIDATE( &candidates[ i ] )
                                        && candidates[ i ].sr_matched != NULL )
                        {
                                struct berval   bv, pbv;
@@ -816,7 +1495,7 @@ really_bad:;
                                 * ignore the matchedDN */
                                if ( sres == LDAP_SUCCESS
                                        && candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT
-                                       && op->o_req_ndn.bv_len > mi->mi_targets[ i ].mt_nsuffix.bv_len )
+                                       && op->o_req_ndn.bv_len > mi->mi_targets[ i ]->mt_nsuffix.bv_len )
                                {
                                        free( (char *)candidates[ i ].sr_matched );
                                        candidates[ i ].sr_matched = NULL;
@@ -860,12 +1539,12 @@ really_bad:;
 
 #if 0
        {
-               char    buf[BUFSIZ];
-               char    cnd[BUFSIZ];
+               char    buf[ SLAP_TEXT_BUFLEN ];
+               char    cnd[ SLAP_TEXT_BUFLEN ];
                int     i;
 
                for ( i = 0; i < mi->mi_ntargets; i++ ) {
-                       if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
+                       if ( META_IS_CANDIDATE( &candidates[ i ] ) ) {
                                cnd[ i ] = '*';
                        } else {
                                cnd[ i ] = ' ';
@@ -883,14 +1562,35 @@ really_bad:;
        /*
         * In case we returned at least one entry, we return LDAP_SUCCESS
         * otherwise, the latter error code we got
-        *
-        * FIXME: we should handle error codes and return the more 
-        * important/reasonable
         */
 
-       if ( sres == LDAP_SUCCESS && rs->sr_v2ref ) {
-               sres = LDAP_REFERRAL;
+       if ( sres == LDAP_SUCCESS ) {
+               if ( rs->sr_v2ref ) {
+                       sres = LDAP_REFERRAL;
+               }
+
+               if ( META_BACK_ONERR_REPORT( mi ) ) {
+                       /*
+                        * Report errors, if any
+                        *
+                        * FIXME: we should handle error codes and return the more 
+                        * important/reasonable
+                        */
+                       for ( i = 0; i < mi->mi_ntargets; i++ ) {
+                               if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+                                       continue;
+                               }
+
+                               if ( candidates[ i ].sr_err != LDAP_SUCCESS
+                                       && candidates[ i ].sr_err != LDAP_NO_SUCH_OBJECT )
+                               {
+                                       sres = candidates[ i ].sr_err;
+                                       break;
+                               }
+                       }
+               }
        }
+
        rs->sr_err = sres;
        rs->sr_matched = matched;
        rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
@@ -909,10 +1609,29 @@ finish:;
        }
 
        for ( i = 0; i < mi->mi_ntargets; i++ ) {
-               if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
+               if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
                        continue;
                }
 
+               if ( mc && META_IS_BINDING( &candidates[ i ] ) ) {
+                       ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+                       if ( LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) ) {
+                               assert( candidates[ i ].sr_msgid >= 0 );
+                               assert( mc->mc_conns[ i ].msc_ld != NULL );
+
+#ifdef DEBUG_205
+                               Debug( LDAP_DEBUG_ANY, "### %s meta_back_search(cleanup) "
+                                       "ldap_unbind_ext[%ld] ld=%p\n",
+                                       op->o_log_prefix, i, (void *)mc->mc_conns[i].msc_ld );
+#endif /* DEBUG_205 */
+
+                               /* if still binding, destroy */
+                               meta_clear_one_candidate( op, mc, i );
+                       }
+                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+                       META_BINDING_CLEAR( &candidates[ i ] );
+               }
+
                if ( candidates[ i ].sr_matched ) {
                        free( (char *)candidates[ i ].sr_matched );
                        candidates[ i ].sr_matched = NULL;
@@ -932,6 +1651,23 @@ finish:;
                        ldap_controls_free( candidates[ i ].sr_ctrls );
                        candidates[ i ].sr_ctrls = NULL;
                }
+
+               if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
+                       meta_back_quarantine( op, &candidates[ i ], i );
+               }
+
+               /* 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 */
+                       LDAP_BACK_CONN_TAINTED_SET( mc );
+               }
        }
 
        if ( mc ) {
@@ -967,7 +1703,7 @@ meta_send_entry(
        /*
         * Rewrite the dn of the result, if needed
         */
-       dc.target = &mi->mi_targets[ target ];
+       dc.target = mi->mi_targets[ target ];
        dc.conn = op->o_conn;
        dc.rs = rs;
        dc.ctx = "searchResult";
@@ -1011,7 +1747,7 @@ meta_send_entry(
                slap_syntax_validate_func       *validate;
                slap_syntax_transform_func      *pretty;
 
-               ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_at, 
+               ldap_back_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] */ );
@@ -1086,7 +1822,7 @@ meta_send_entry(
                        struct berval   *bv;
 
                        for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
-                               ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_oc,
+                               ldap_back_map( &mi->mi_targets[ target ]->mt_rwmap.rwm_oc,
                                                bv, &mapped, BACKLDAP_REMAP );
                                if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
                                        free( bv->bv_val );
index 671dc840e046cb8ef706b422c8c9c8eb34466932..b63d485b24697aee2a89e071aaddf24c68765c94 100644 (file)
@@ -51,23 +51,29 @@ meta_back_conn_destroy(
        mc_curr.mc_conn = conn;
        
        ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+#if META_BACK_PRINT_CONNTREE > 0
+       meta_back_print_conntree( mi, ">>> meta_back_conn_destroy" );
+#endif /* META_BACK_PRINT_CONNTREE */
        while ( ( mc = avl_delete( &mi->mi_conninfo.lai_tree, ( caddr_t )&mc_curr, meta_back_conn_cmp ) ) != NULL )
        {
                Debug( LDAP_DEBUG_TRACE,
                        "=>meta_back_conn_destroy: destroying conn %ld\n",
-                       LDAP_BACK_PCONN_ID( mc->mc_conn ), 0, 0 );
+                       LDAP_BACK_PCONN_ID( mc ), 0, 0 );
                
                assert( mc->mc_refcnt == 0 );
 
                meta_back_conn_free( mc );
        }
+#if META_BACK_PRINT_CONNTREE > 0
+       meta_back_print_conntree( mi, "<<< meta_back_conn_destroy" );
+#endif /* META_BACK_PRINT_CONNTREE */
        ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
 
        /*
         * Cleanup rewrite session
         */
        for ( i = 0; i < mi->mi_ntargets; ++i ) {
-               rewrite_session_delete( mi->mi_targets[ i ].mt_rwmap.rwm_rw, conn );
+               rewrite_session_delete( mi->mi_targets[ i ]->mt_rwmap.rwm_rw, conn );
        }
 
        return 0;
index 2cf9e42e862059aa97af6eb7e8649e469ba040a0..8418df57acd067f5988fc6b06e1af22023cc50c0 100644 (file)
@@ -383,7 +383,7 @@ monitor_subsys_database_init(
                        int             t;
 
                        for ( t = 0; t < mi->mi_ntargets; t++ ) {
-                               char            **urls = ldap_str2charray( mi->mi_targets[ t ].mt_uri, " " );
+                               char            **urls = ldap_str2charray( mi->mi_targets[ t ]->mt_uri, " " );
                                int             u;
 
                                for ( u = 0; urls[ u ] != NULL; u++ ) {
index 533ec2f7190aa80ba963789d460fec2f05c6f609..bbd83b186537ab66c4b7d2c9480d3d3dbb4b339d 100644 (file)
@@ -121,6 +121,7 @@ rwm_callback_get( Operation *op, SlapReply *rs )
        return roc;
 }
 
+
 static int
 rwm_op_dn_massage( Operation *op, SlapReply *rs, void *cookie,
        rwm_op_state *ros )
@@ -1642,7 +1643,6 @@ rwm_db_init(
        BackendDB       *be )
 {
        slap_overinst           *on = (slap_overinst *) be->bd_info;
-       struct ldapmapping      *mapping = NULL;
        struct ldaprwmap        *rwmap;
 #ifdef ENABLE_REWRITE
        char                    *rargv[ 3 ];
@@ -1671,13 +1671,6 @@ rwm_db_init(
        rewrite_parse( rwmap->rwm_rw, "<suffix massage>", 2, 2, rargv );
 #endif /* ENABLE_REWRITE */
 
-       if ( rwm_map_init( &rwmap->rwm_oc, &mapping ) != LDAP_SUCCESS ||
-                       rwm_map_init( &rwmap->rwm_at, &mapping ) != LDAP_SUCCESS )
-       {
-               rc = 1;
-               goto error_return;
-       }
-
 error_return:;
        on->on_bi.bi_private = (void *)rwmap;
 
index a37ffa8a13f5f0e4949dde0d9852ab5f513bf811..13ef727f1ebf537e433d24d0d3133c7fcbc24f70 100644 (file)
@@ -47,6 +47,7 @@ rwm_map_config(
        struct ldapmapping      *mapping;
        char                    *src, *dst;
        int                     is_oc = 0;
+       int                     rc = 0;
 
        if ( argc < 3 || argc > 4 ) {
                fprintf( stderr,
@@ -73,7 +74,7 @@ rwm_map_config(
        if ( strcmp( argv[2], "*" ) == 0 ) {
                if ( argc < 4 || strcmp( argv[3], "*" ) == 0 ) {
                        map->drop_missing = ( argc < 4 );
-                       return 0;
+                       goto success_return;
                }
                src = dst = argv[3];
 
@@ -230,7 +231,13 @@ rwm_map_config(
        avl_insert( &map->remap, (caddr_t)&mapping[1],
                                rwm_mapping_cmp, rwm_mapping_dup );
 
-       return 0;
+success_return:;
+       if ( !is_oc && map->map == NULL ) {
+               /* only init if required */
+               rc = rwm_map_init( map, &mapping ) != LDAP_SUCCESS;
+       }
+
+       return rc;
 
 error_return:;
        if ( mapping ) {
index 337795a70778cc24101e6ecd5d3e50420b400f5c..8ba1d1d9fae20c8ddcc21532dfaadb5aba1b28a8 100644 (file)
@@ -84,6 +84,7 @@ rwm_map_init( struct ldapmap *lm, struct ldapmapping **m )
        /* FIXME: I don't think this is needed any more... */
        rc = slap_str2ad( "objectClass", &mapping[0].m_src_ad, &text );
        if ( rc != LDAP_SUCCESS ) {
+               ch_free( mapping );
                return rc;
        }
 
@@ -112,6 +113,10 @@ rwm_mapping( struct ldapmap *map, struct berval *s, struct ldapmapping **m, int
        Avlnode *tree;
        struct ldapmapping fmapping;
 
+       if ( map == NULL ) {
+               return 0;
+       }
+
        assert( m != NULL );
 
        if ( remap == RWM_REMAP ) {
@@ -137,6 +142,13 @@ rwm_map( struct ldapmap *map, struct berval *s, struct berval *bv, int remap )
 {
        struct ldapmapping *mapping;
 
+       /* map->map may be NULL when mapping is configured,
+        * but map->remap can't */
+       if ( map->remap == NULL ) {
+               *bv = *s;
+               return;
+       }
+
        BER_BVZERO( bv );
        ( void )rwm_mapping( map, s, &mapping, remap );
        if ( mapping != NULL ) {