]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/connection.c
also log cookie parsing
[openldap] / servers / slapd / connection.c
index f067714cd8c88df9bbec81537856ada270d3f853..9ec67f503a6d049ee48eec22e6e75089100234c6 100644 (file)
@@ -1,7 +1,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2006 The OpenLDAP Foundation.
+ * Copyright 1998-2007 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -196,23 +196,22 @@ int connections_shutdown(void)
        ber_socket_t i;
 
        for ( i = 0; i < dtblsize; i++ ) {
-               if( connections[i].c_struct_state != SLAP_C_USED ) {
-                       continue;
-               }
-               /* give persistent clients a chance to cleanup */
-               if( connections[i].c_conn_state == SLAP_C_CLIENT ) {
-                       ldap_pvt_thread_pool_submit( &connection_pool,
-                       connections[i].c_clientfunc, connections[i].c_clientarg );
-                       continue;
+               if( connections[i].c_struct_state != SLAP_C_UNINITIALIZED ) {
+                       ldap_pvt_thread_mutex_lock( &connections[i].c_mutex );
+                       if( connections[i].c_struct_state == SLAP_C_USED ) {
+
+                               /* give persistent clients a chance to cleanup */
+                               if( connections[i].c_conn_state == SLAP_C_CLIENT ) {
+                                       ldap_pvt_thread_pool_submit( &connection_pool,
+                                       connections[i].c_clientfunc, connections[i].c_clientarg );
+                               } else {
+                                       /* c_mutex is locked */
+                                       connection_closing( &connections[i], "slapd shutdown" );
+                                       connection_close( &connections[i] );
+                               }
+                       }
+                       ldap_pvt_thread_mutex_unlock( &connections[i].c_mutex );
                }
-
-               ldap_pvt_thread_mutex_lock( &connections[i].c_mutex );
-
-               /* c_mutex is locked */
-               connection_closing( &connections[i], "slapd shutdown" );
-               connection_close( &connections[i] );
-
-               ldap_pvt_thread_mutex_unlock( &connections[i].c_mutex );
        }
 
        return 0;
@@ -306,11 +305,20 @@ static Connection* connection_get( ber_socket_t s )
        if( c != NULL ) {
                ber_socket_t    sd;
 
-               assert( c->c_struct_state != SLAP_C_UNINITIALIZED );
-
                ldap_pvt_thread_mutex_lock( &c->c_mutex );
 
+               assert( c->c_struct_state != SLAP_C_UNINITIALIZED );
+
                ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_GET_FD, &sd );
+#ifdef HAVE_WINSOCK
+               /* Avoid race condition after releasing
+                * connections_mutex
+                */
+               if ( sd != s ) {
+                       ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+                       return NULL;
+               }
+#endif
                if( c->c_struct_state != SLAP_C_USED ) {
                        /* connection must have been closed due to resched */
 
@@ -351,14 +359,15 @@ static void connection_return( Connection *c )
        ldap_pvt_thread_mutex_unlock( &c->c_mutex );
 }
 
-long connection_init(
+Connection * connection_init(
        ber_socket_t s,
        Listener *listener,
        const char* dnsname,
        const char* peername,
        int flags,
        slap_ssf_t ssf,
-       struct berval *authid )
+       struct berval *authid
+       LDAP_PF_LOCAL_SENDMSG_ARG(struct berval *peerbv))
 {
        unsigned long id;
        Connection *c;
@@ -371,13 +380,13 @@ long connection_init(
        assert( peername != NULL );
 
 #ifndef HAVE_TLS
-       assert( flags != CONN_IS_TLS );
+       assert( !( flags & CONN_IS_TLS ));
 #endif
 
        if( s == AC_SOCKET_INVALID ) {
                Debug( LDAP_DEBUG_ANY,
                        "connection_init: init of socket %ld invalid.\n", (long)s, 0, 0 );
-               return -1;
+               return NULL;
        }
 
        assert( s >= 0 );
@@ -434,7 +443,7 @@ long connection_init(
                        Debug( LDAP_DEBUG_ANY,
                                "connection_init(%d): connection table full "
                                "(%d/%d)\n", s, i, dtblsize);
-                       return -1;
+                       return NULL;
                }
        }
 #endif
@@ -517,14 +526,15 @@ long connection_init(
 
        c->c_listener = listener;
 
-       if ( flags == CONN_IS_CLIENT ) {
+       if ( flags & CONN_IS_CLIENT ) {
+               c->c_connid = 0;
                c->c_conn_state = SLAP_C_CLIENT;
                c->c_struct_state = SLAP_C_USED;
                c->c_close_reason = "?";                        /* should never be needed */
                ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_FD, &s );
                ldap_pvt_thread_mutex_unlock( &c->c_mutex );
 
-               return 0;
+               return c;
        }
 
        ber_str2bv( dnsname, 0, 1, &c->c_peer_domain );
@@ -551,7 +561,7 @@ long connection_init(
 
 #ifdef LDAP_CONNECTIONLESS
        c->c_is_udp = 0;
-       if( flags == CONN_IS_UDP ) {
+       if( flags & CONN_IS_UDP ) {
                c->c_is_udp = 1;
 #ifdef LDAP_DEBUG
                ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
@@ -562,7 +572,21 @@ long connection_init(
                ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_readahead,
                        LBER_SBIOD_LEVEL_PROVIDER, NULL );
        } else
+#endif /* LDAP_CONNECTIONLESS */
+#ifdef LDAP_PF_LOCAL
+       if ( flags & CONN_IS_IPC ) {
+#ifdef LDAP_DEBUG
+               ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+                       LBER_SBIOD_LEVEL_PROVIDER, (void*)"ipc_" );
+#endif
+               ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_fd,
+                       LBER_SBIOD_LEVEL_PROVIDER, (void *)&s );
+#ifdef LDAP_PF_LOCAL_SENDMSG
+               if ( !BER_BVISEMPTY( peerbv ))
+                       ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_UNGET_BUF, peerbv );
 #endif
+       } else
+#endif /* LDAP_PF_LOCAL */
        {
 #ifdef LDAP_DEBUG
                ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
@@ -597,7 +621,7 @@ long connection_init(
        c->c_tls_ssf = 0;
 
 #ifdef HAVE_TLS
-       if ( flags == CONN_IS_TLS ) {
+       if ( flags & CONN_IS_TLS ) {
                c->c_is_tls = 1;
                c->c_needs_tls_accept = 1;
        } else {
@@ -614,7 +638,7 @@ long connection_init(
 
        backend_connection_init(c);
 
-       return id;
+       return c;
 }
 
 void connection2anonymous( Connection *c )
@@ -731,22 +755,20 @@ connection_destroy( Connection *c )
        }
 
        ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
-       slapd_sd_lock();
-
-       ber_sockbuf_free( sb );
 
        /* c must be fully reset by this point; when we call slapd_remove
         * it may get immediately reused by a new connection.
         */
        if ( sd != AC_SOCKET_INVALID ) {
-               slapd_remove( sd, 1, 0, 1 );
+               slapd_remove( sd, sb, 1, 0, 0 );
 
-               Statslog( LDAP_DEBUG_STATS, (close_reason
-                                                                        ? "conn=%lu fd=%ld closed (%s)\n"
-                                                                        : "conn=%lu fd=%ld closed\n"),
-                       connid, (long) sd, close_reason, 0, 0 );
-       } else {
-               slapd_sd_unlock();
+               if ( close_reason == NULL ) {
+                       Statslog( LDAP_DEBUG_STATS, "conn=%lu fd=%ld closed\n",
+                               connid, (long) sd, 0, 0, 0 );
+               } else {
+                       Statslog( LDAP_DEBUG_STATS, "conn=%lu fd=%ld closed (%s)\n",
+                               connid, (long) sd, close_reason, 0, 0 );
+               }
        }
 }
 
@@ -834,9 +856,19 @@ void connection_closing( Connection *c, const char *why )
                connection_abandon( c );
 
                /* wake write blocked operations */
-               slapd_clr_write( sd, 1 );
                if ( c->c_writewaiter ) {
                        ldap_pvt_thread_cond_signal( &c->c_write_cv );
+                       /* ITS#4667 this may allow another thread to drop into
+                        * connection_resched / connection_close before we
+                        * finish, but that's OK.
+                        */
+                       slapd_clr_write( sd, 1 );
+                       ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+                       ldap_pvt_thread_mutex_lock( &c->c_write_mutex );
+                       ldap_pvt_thread_mutex_lock( &c->c_mutex );
+                       ldap_pvt_thread_mutex_unlock( &c->c_write_mutex );
+               } else {
+                       slapd_clr_write( sd, 1 );
                }
 
        } else if( why == NULL && c->c_close_reason == conn_lost_str ) {
@@ -845,19 +877,31 @@ void connection_closing( Connection *c, const char *why )
        }
 }
 
-static void connection_close( Connection *c )
+static void
+connection_close( Connection *c )
 {
-       ber_socket_t    sd;
+       ber_socket_t    sd = AC_SOCKET_INVALID;
 
        assert( connections != NULL );
        assert( c != NULL );
+
+       /* ITS#4667 we may have gotten here twice */
+       if ( c->c_conn_state == SLAP_C_INVALID )
+               return;
+
        assert( c->c_struct_state == SLAP_C_USED );
        assert( c->c_conn_state == SLAP_C_CLOSING );
 
-       /* note: c_mutex should be locked by caller */
+       /* NOTE: c_mutex should be locked by caller */
+
+       /* NOTE: don't get the file descriptor if not needed */
+       if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+               ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_GET_FD, &sd );
+       }
 
-       ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_GET_FD, &sd );
-       if( !LDAP_STAILQ_EMPTY(&c->c_ops) ) {
+       if ( !LDAP_STAILQ_EMPTY(&c->c_ops) ||
+               !LDAP_STAILQ_EMPTY(&c->c_pending_ops) )
+       {
                Debug( LDAP_DEBUG_TRACE,
                        "connection_close: deferring conn=%lu sd=%d\n",
                        c->c_connid, sd, 0 );
@@ -866,6 +910,7 @@ static void connection_close( Connection *c )
 
        Debug( LDAP_DEBUG_TRACE, "connection_close: conn=%lu sd=%d\n",
                c->c_connid, sd, 0 );
+
        connection_destroy( c );
 }
 
@@ -911,9 +956,14 @@ Connection* connection_next( Connection *c, ber_socket_t *index )
 
        ldap_pvt_thread_mutex_lock( &connections_mutex );
        for(; *index < dtblsize; (*index)++) {
+               int c_struct;
                if( connections[*index].c_struct_state == SLAP_C_UNINITIALIZED ) {
                        assert( connections[*index].c_conn_state == SLAP_C_INVALID );
+#ifdef HAVE_WINSOCK
                        break;
+#else
+                       continue;
+#endif
                }
 
                if( connections[*index].c_struct_state == SLAP_C_USED ) {
@@ -933,7 +983,10 @@ Connection* connection_next( Connection *c, ber_socket_t *index )
                        break;
                }
 
-               assert( connections[*index].c_struct_state == SLAP_C_UNUSED );
+               c_struct = connections[*index].c_struct_state;
+               if ( c_struct == SLAP_C_PENDING )
+                       continue;
+               assert( c_struct == SLAP_C_UNUSED );
                assert( connections[*index].c_conn_state == SLAP_C_INVALID );
        }
 
@@ -985,12 +1038,12 @@ void connection_done( Connection *c )
 static BI_op_func *opfun[] = {
        do_bind,
        do_unbind,
+       do_search,
+       do_compare,
+       do_modify,
+       do_modrdn,
        do_add,
        do_delete,
-       do_modrdn,
-       do_modify,
-       do_compare,
-       do_search,
        do_abandon,
        do_extended,
        NULL
@@ -1003,7 +1056,7 @@ connection_operation( void *ctx, void *arg_v )
        Operation *op = arg_v;
        SlapReply rs = {REP_RESULT};
        ber_tag_t tag = op->o_tag;
-       int opidx = -1;
+       slap_op_t opidx = SLAP_OP_LAST;
        Connection *conn = op->o_conn;
        void *memctx = NULL;
        void *memctx_null = NULL;
@@ -1074,7 +1127,7 @@ connection_operation( void *ctx, void *arg_v )
 #endif
        memsiz = SLAP_SLAB_SIZE;
 
-       memctx = slap_sl_mem_create( memsiz, SLAP_SLAB_STACK, ctx );
+       memctx = slap_sl_mem_create( memsiz, SLAP_SLAB_STACK, ctx, 1 );
        op->o_tmpmemctx = memctx;
        op->o_tmpmfuncs = &slap_sl_mfuncs;
        if ( tag != LDAP_REQ_ADD && tag != LDAP_REQ_MODIFY ) {
@@ -1086,53 +1139,8 @@ connection_operation( void *ctx, void *arg_v )
        }
        }
 
-       switch ( tag ) {
-       case LDAP_REQ_BIND:
-               opidx = SLAP_OP_BIND;
-               break;
-
-       case LDAP_REQ_UNBIND:
-               opidx = SLAP_OP_UNBIND;
-               break;
-
-       case LDAP_REQ_ADD:
-               opidx = SLAP_OP_ADD;
-               break;
-
-       case LDAP_REQ_DELETE:
-               opidx = SLAP_OP_DELETE;
-               break;
-
-       case LDAP_REQ_MODRDN:
-               opidx = SLAP_OP_MODRDN;
-               break;
-
-       case LDAP_REQ_MODIFY:
-               opidx = SLAP_OP_MODIFY;
-               break;
-
-       case LDAP_REQ_COMPARE:
-               opidx = SLAP_OP_COMPARE;
-               break;
-
-       case LDAP_REQ_SEARCH:
-               opidx = SLAP_OP_SEARCH;
-               break;
-
-       case LDAP_REQ_ABANDON:
-               opidx = SLAP_OP_ABANDON;
-               break;
-
-       case LDAP_REQ_EXTENDED:
-               opidx = SLAP_OP_EXTENDED;
-               break;
-
-       default:
-               /* not reachable */
-               assert( 0 );
-       }
-
-       assert( opidx > -1 );
+       opidx = slap_req2op( tag );
+       assert( opidx != SLAP_OP_LAST );
        INCR_OP_INITIATED( opidx );
        rc = (*(opfun[opidx]))( op, &rs );
 
@@ -1140,7 +1148,7 @@ operations_error:
        if ( rc == SLAPD_DISCONNECT ) {
                tag = LBER_ERROR;
 
-       } else if ( opidx > -1 ) {
+       } else if ( opidx != SLAP_OP_LAST ) {
                /* increment completed operations count 
                 * only if operation was initiated
                 * and rc != SLAPD_DISCONNECT */
@@ -1154,6 +1162,7 @@ operations_error:
                        op->o_cancel = LDAP_TOO_LATE;
                }
        }
+
        while ( op->o_cancel != SLAP_CANCEL_NONE &&
                op->o_cancel != SLAP_CANCEL_DONE )
        {
@@ -1164,7 +1173,7 @@ operations_error:
 
        ber_set_option( op->o_ber, LBER_OPT_BER_MEMCTX, &memctx_null );
 
-       LDAP_STAILQ_REMOVE( &conn->c_ops, op, slap_op, o_next);
+       LDAP_STAILQ_REMOVE( &conn->c_ops, op, Operation, o_next);
        LDAP_STAILQ_NEXT(op, o_next) = NULL;
        slap_op_free( op );
        conn->c_n_ops_executing--;
@@ -1191,20 +1200,18 @@ int connection_client_setup(
        ldap_pvt_thread_start_t *func,
        void *arg )
 {
-       int rc;
        Connection *c;
 
-       rc = connection_init( s, (Listener *)&dummy_list, "", "",
-               CONN_IS_CLIENT, 0, NULL );
-       if ( rc < 0 ) return -1;
+       c = connection_init( s, (Listener *)&dummy_list, "", "",
+               CONN_IS_CLIENT, 0, NULL
+               LDAP_PF_LOCAL_SENDMSG_ARG(NULL));
+       if ( !c ) return -1;
 
-       c = connection_get( s );
        c->c_clientfunc = func;
        c->c_clientarg = arg;
 
        slapd_add_internal( s, 0 );
        slapd_set_read( s, 1 );
-       connection_return( c );
        return 0;
 }
 
@@ -1218,6 +1225,7 @@ void connection_client_stop(
        ber_socket_t s )
 {
        Connection *c;
+       Sockbuf *sb;
 
        /* get (locked) connection */
        c = connection_get( s );
@@ -1228,14 +1236,13 @@ void connection_client_stop(
        c->c_conn_state = SLAP_C_INVALID;
        c->c_struct_state = SLAP_C_UNUSED;
        c->c_close_reason = "?";                        /* should never be needed */
-       slapd_sd_lock();
-       ber_sockbuf_free( c->c_sb );
-       slapd_remove( s, 0, 1, 1 );
+       sb = c->c_sb;
        c->c_sb = ber_sockbuf_alloc( );
        {
                ber_len_t max = sockbuf_max_incoming;
                ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
        }
+       slapd_remove( s, sb, 0, 1, 0 );
 
        connection_return( c );
 }
@@ -1445,7 +1452,7 @@ int connection_read(ber_socket_t s)
        }
 #ifdef DATA_READY_LOOP
        while( !rc && ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_DATA_READY, NULL ));
-#elif CONNECTION_INPUT_LOOP
+#elif defined CONNECTION_INPUT_LOOP
        while(!rc);
 #else
        while(0);
@@ -1510,7 +1517,7 @@ connection_input( Connection *conn )
                return -1;
        }
 
-       errno = 0;
+       sock_errset(0);
 
 #ifdef LDAP_CONNECTIONLESS
        if ( conn->c_is_udp ) {
@@ -1530,16 +1537,16 @@ connection_input( Connection *conn )
 
        tag = ber_get_next( conn->c_sb, &len, conn->c_currentber );
        if ( tag != LDAP_TAG_MESSAGE ) {
-               int err = errno;
+               int err = sock_errno();
                ber_socket_t    sd;
 
                ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_GET_FD, &sd );
 
-               Debug( LDAP_DEBUG_TRACE,
-                       "ber_get_next on fd %d failed errno=%d (%s)\n",
-                       sd, err, sock_errstr(err) );
                if ( err != EWOULDBLOCK && err != EAGAIN ) {
                        /* log, close and send error */
+                       Debug( LDAP_DEBUG_TRACE,
+                               "ber_get_next on fd %d failed errno=%d (%s)\n",
+                       sd, err, sock_errstr(err) );
                        ber_free( conn->c_currentber, 1 );
                        conn->c_currentber = NULL;
 
@@ -1767,13 +1774,17 @@ static int connection_bind_cleanup_cb( Operation *op, SlapReply *rs )
 {
        op->o_conn->c_sasl_bindop = NULL;
 
+       ch_free( op->o_callback );
+       op->o_callback = NULL;
+
        return SLAP_CB_CONTINUE;
 }
 
 static int connection_bind_cb( Operation *op, SlapReply *rs )
 {
        ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
-       op->o_conn->c_conn_state = SLAP_C_ACTIVE;
+       if ( op->o_conn->c_conn_state == SLAP_C_BINDING )
+               op->o_conn->c_conn_state = SLAP_C_ACTIVE;
        op->o_conn->c_sasl_bind_in_progress =
                ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS );
 
@@ -1804,13 +1815,14 @@ static int connection_bind_cb( Operation *op, SlapReply *rs )
 
                        /* log authorization identity */
                        Statslog( LDAP_DEBUG_STATS,
-                               "%s BIND dn=\"%s\" mech=%s ssf=%d\n",
+                               "%s BIND dn=\"%s\" mech=%s sasl_ssf=%d ssf=%d\n",
                                op->o_log_prefix,
                                BER_BVISNULL( &op->o_conn->c_dn ) ? "<empty>" : op->o_conn->c_dn.bv_val,
-                               op->o_conn->c_authmech.bv_val, op->orb_ssf, 0 );
+                               op->o_conn->c_authmech.bv_val,
+                               op->orb_ssf, op->o_conn->c_ssf );
 
                        Debug( LDAP_DEBUG_TRACE,
-                               "do_bind: SASL/%s bind: dn=\"%s\" ssf=%d\n",
+                               "do_bind: SASL/%s bind: dn=\"%s\" sasl_ssf=%d\n",
                                op->o_conn->c_authmech.bv_val,
                                BER_BVISNULL( &op->o_conn->c_dn ) ? "<empty>" : op->o_conn->c_dn.bv_val,
                                op->orb_ssf );
@@ -1949,12 +1961,54 @@ int connection_write(ber_socket_t s)
        return 0;
 }
 
+#ifdef LDAP_SLAPI
+typedef struct conn_fake_extblock {
+       void *eb_conn;
+       void *eb_op;
+} conn_fake_extblock;
+
+static void
+connection_fake_destroy(
+       void *key,
+       void *data )
+{
+       Connection conn = {0};
+       Operation op = {0};
+       Opheader ohdr = {0};
+
+       conn_fake_extblock *eb = data;
+       
+       op.o_hdr = &ohdr;
+       op.o_hdr->oh_extensions = eb->eb_op;
+       conn.c_extensions = eb->eb_conn;
+       op.o_conn = &conn;
+       conn.c_connid = -1;
+       op.o_connid = -1;
+
+       ber_memfree_x( eb, NULL );
+       slapi_int_free_object_extensions( SLAPI_X_EXT_OPERATION, &op );
+       slapi_int_free_object_extensions( SLAPI_X_EXT_CONNECTION, &conn );
+}
+#endif
+
 void
 connection_fake_init(
        Connection *conn,
-       Operation *op,
+       OperationBuffer *opbuf,
        void *ctx )
 {
+       connection_fake_init2( conn, opbuf, ctx, 1 );
+}
+
+void
+connection_fake_init2(
+       Connection *conn,
+       OperationBuffer *opbuf,
+       void *ctx,
+       int newmem )
+{
+       Operation *op = (Operation *) opbuf;
+
        conn->c_connid = -1;
        conn->c_send_ldap_result = slap_send_ldap_result;
        conn->c_send_search_entry = slap_send_search_entry;
@@ -1963,11 +2017,13 @@ connection_fake_init(
        conn->c_peer_domain = slap_empty_bv;
        conn->c_peer_name = slap_empty_bv;
 
-       memset(op, 0, OPERATION_BUFFER_SIZE);
-       op->o_hdr = (Opheader *)(op+1);
-       op->o_controls = (void **)(op->o_hdr+1);
+       memset( opbuf, 0, sizeof( *opbuf ));
+       op->o_hdr = &opbuf->ob_hdr;
+       op->o_controls = opbuf->ob_controls;
+
        /* set memory context */
-       op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx);
+       op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx,
+               newmem );
        op->o_tmpmfuncs = &slap_sl_mfuncs;
        op->o_threadctx = ctx;
        op->o_tid = ldap_pvt_thread_pool_tid( ctx );
@@ -1977,8 +2033,26 @@ connection_fake_init(
        connection_init_log_prefix( op );
 
 #ifdef LDAP_SLAPI
-       slapi_int_create_object_extensions( SLAPI_X_EXT_CONNECTION, conn );
-       slapi_int_create_object_extensions( SLAPI_X_EXT_OPERATION, op );
+       if ( slapi_plugins_used ) {
+               conn_fake_extblock *eb;
+               void *ebx = NULL;
+
+               /* Use thread keys to make sure these eventually get cleaned up */
+               if ( ldap_pvt_thread_pool_getkey( ctx, connection_fake_init, &ebx,
+                       NULL )) {
+                       eb = ch_malloc( sizeof( *eb ));
+                       slapi_int_create_object_extensions( SLAPI_X_EXT_CONNECTION, conn );
+                       slapi_int_create_object_extensions( SLAPI_X_EXT_OPERATION, op );
+                       eb->eb_conn = conn->c_extensions;
+                       eb->eb_op = op->o_hdr->oh_extensions;
+                       ldap_pvt_thread_pool_setkey( ctx, connection_fake_init, eb,
+                               connection_fake_destroy );
+               } else {
+                       eb = ebx;
+                       conn->c_extensions = eb->eb_conn;
+                       op->o_hdr->oh_extensions = eb->eb_op;
+               }
+       }
 #endif /* LDAP_SLAPI */
 
        slap_op_time( &op->o_time, &op->o_tincr );