diff --git a/servers/lloadd/bind.c b/servers/lloadd/bind.c index 8f24b5a698..1827324432 100644 --- a/servers/lloadd/bind.c +++ b/servers/lloadd/bind.c @@ -81,8 +81,48 @@ request_bind( LloadConnection *client, LloadOperation *op ) struct berval binddn, auth; ber_int_t version; ber_tag_t tag; + unsigned long pin = client->c_pin_id; int res, rc = LDAP_SUCCESS; + if ( pin ) { + LloadOperation *pinned_op, needle = { + .o_client_connid = client->c_connid, + .o_client_msgid = 0, + .o_pin_id = client->c_pin_id, + }; + + Debug( LDAP_DEBUG_CONNS, "request_bind: " + "client connid=%lu is pinned pin=%lu\n", + client->c_connid, pin ); + + pinned_op = + tavl_delete( &client->c_ops, &needle, operation_client_cmp ); + if ( pinned_op ) { + assert( op->o_tag == pinned_op->o_tag ); + + pinned_op->o_client_msgid = op->o_client_msgid; + + /* Preserve the new BerElement and its pointers, reclaim the old + * one in operation_destroy_from_client if it's still there */ + needle.o_ber = pinned_op->o_ber; + pinned_op->o_ber = op->o_ber; + op->o_ber = needle.o_ber; + + pinned_op->o_request = op->o_request; + pinned_op->o_ctrls = op->o_ctrls; + + /* + * pinned_op is accessible from the upstream, protect it since we + * lose the client lock in operation_destroy_from_client temporarily + */ + pinned_op->o_client_refcnt++; + operation_destroy_from_client( op ); + pinned_op->o_client_refcnt--; + + op = pinned_op; + } + } + /* protect the Bind operation */ op->o_client_refcnt++; tavl_delete( &client->c_ops, op, operation_client_cmp ); @@ -138,10 +178,17 @@ request_bind( LloadConnection *client, LloadOperation *op ) BER_BVZERO( &client->c_sasl_bind_mech ); } } else if ( tag == LDAP_AUTH_SASL ) { - operation_send_reject( op, LDAP_AUTH_METHOD_NOT_SUPPORTED, - "no SASL support available yet", 1 ); - ber_free( copy, 0 ); - return LDAP_SUCCESS; + struct berval mech; + + ber_init2( copy, &auth, 0 ); + + if ( ber_get_stringbv( copy, &mech, LBER_BV_NOTERM ) == LBER_ERROR ) { + goto fail; + } + if ( ber_bvcmp( &mech, &client->c_sasl_bind_mech ) ) { + ber_memfree( client->c_sasl_bind_mech.bv_val ); + ber_dupbv( &client->c_sasl_bind_mech, &mech ); + } } else { goto fail; } @@ -150,12 +197,42 @@ request_bind( LloadConnection *client, LloadOperation *op ) assert( rc == LDAP_SUCCESS ); CONNECTION_UNLOCK_INCREF(client); - upstream = backend_select( op, &res ); + if ( pin ) { + ldap_pvt_thread_mutex_lock( &op->o_link_mutex ); + upstream = op->o_upstream; + if ( upstream ) { + CONNECTION_LOCK(upstream); + if ( !upstream->c_live ) { + CONNECTION_UNLOCK(upstream); + upstream = NULL; + } + } + ldap_pvt_thread_mutex_unlock( &op->o_link_mutex ); + } + + /* If we were pinned but lost the link, don't look for a new upstream, we + * have to reject the op and clear pin */ + if ( upstream ) { + CONNECTION_UNLOCK_INCREF(upstream); + ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex ); + } else if ( !pin ) { + upstream = backend_select( op, &res ); + } else { + Debug( LDAP_DEBUG_STATS, "request_bind: " + "connid=%lu, msgid=%d pinned upstream lost\n", + op->o_client_connid, op->o_client_msgid ); + operation_send_reject( op, LDAP_UNAVAILABLE, + "connection to the remote server has been severed", 1 ); + pin = 0; + goto done; + } + if ( !upstream ) { Debug( LDAP_DEBUG_STATS, "request_bind: " "connid=%lu, msgid=%d no available connection found\n", op->o_client_connid, op->o_client_msgid ); operation_send_reject( op, res, "no connections available", 1 ); + assert( client->c_pin_id == 0 ); goto done; } @@ -173,6 +250,18 @@ request_bind( LloadConnection *client, LloadOperation *op ) upstream->c_pendingber = ber; CONNECTION_LOCK(upstream); + if ( pin ) { + tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ); + } else if ( tag == LDAP_AUTH_SASL && !op->o_pin_id ) { + ldap_pvt_thread_mutex_lock( &lload_pin_mutex ); + pin = op->o_pin_id = lload_next_pin++; + Debug( LDAP_DEBUG_CONNS, "request_bind: " + "client connid=%lu allocated pin=%lu linking it to upstream " + "connid=%lu\n", + op->o_client_connid, pin, upstream->c_connid ); + ldap_pvt_thread_mutex_unlock( &lload_pin_mutex ); + } + op->o_upstream = upstream; op->o_upstream_connid = upstream->c_connid; op->o_upstream_msgid = upstream->c_next_msgid++; @@ -204,11 +293,13 @@ done: ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex ); } + client->c_pin_id = pin; if ( !--op->o_client_refcnt || !upstream ) { operation_destroy_from_client( op ); if ( client->c_state == LLOAD_C_BINDING ) { client->c_state = LLOAD_C_READY; client->c_type = LLOAD_C_OPEN; + client->c_pin_id = 0; if ( !BER_BVISNULL( &client->c_auth ) ) { ch_free( client->c_auth.bv_val ); BER_BVZERO( &client->c_auth ); @@ -234,6 +325,7 @@ fail: CONNECTION_LOCK_DECREF(client); op->o_client_refcnt--; operation_destroy_from_client( op ); + client->c_pin_id = 0; CONNECTION_DESTROY(client); } @@ -250,6 +342,7 @@ handle_bind_response( LloadConnection *upstream = op->o_upstream; BerValue response; BerElement *copy; + LloadOperation *removed; ber_int_t result; ber_tag_t tag; int rc = LDAP_SUCCESS; @@ -280,18 +373,36 @@ handle_bind_response( CONNECTION_LOCK(upstream); if ( result != LDAP_SASL_BIND_IN_PROGRESS ) { upstream->c_state = LLOAD_C_READY; + op->o_pin_id = 0; + } else { + if ( tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ) ) { + op->o_upstream_msgid = 0; + op->o_upstream_refcnt++; + rc = tavl_insert( &upstream->c_ops, op, operation_upstream_cmp, + avl_dup_error ); + assert( rc == LDAP_SUCCESS ); + } } CONNECTION_UNLOCK(upstream); CONNECTION_LOCK(client); + removed = tavl_delete( &client->c_ops, op, operation_client_cmp ); + assert( !removed || op == removed ); + if ( client->c_state == LLOAD_C_BINDING ) { switch ( result ) { case LDAP_SASL_BIND_IN_PROGRESS: + op->o_client_msgid = 0; + rc = tavl_insert( &client->c_ops, op, operation_client_cmp, + avl_dup_error ); + assert( rc == LDAP_SUCCESS ); break; case LDAP_SUCCESS: default: { + op->o_client = NULL; client->c_state = LLOAD_C_READY; client->c_type = LLOAD_C_OPEN; + client->c_pin_id = 0; if ( result != LDAP_SUCCESS ) { ber_memfree( client->c_auth.bv_val ); BER_BVZERO( &client->c_auth ); @@ -314,7 +425,7 @@ handle_bind_response( done: if ( rc ) { - operation_send_reject( op, LDAP_OTHER, "internal error", 0 ); + operation_send_reject( op, LDAP_OTHER, "internal error", 1 ); ber_free( ber, 1 ); return LDAP_SUCCESS; @@ -403,6 +514,7 @@ handle_vc_bind_response( default: { client->c_state = LLOAD_C_READY; client->c_type = LLOAD_C_OPEN; + client->c_pin_id = 0; if ( result != LDAP_SUCCESS ) { ber_memfree( client->c_auth.bv_val ); BER_BVZERO( &client->c_auth ); diff --git a/servers/lloadd/init.c b/servers/lloadd/init.c index dfaf15373b..6cfe725590 100644 --- a/servers/lloadd/init.c +++ b/servers/lloadd/init.c @@ -100,6 +100,7 @@ lload_init( int mode, const char *name ) ldap_pvt_thread_mutex_init( &backend_mutex ); ldap_pvt_thread_mutex_init( &clients_mutex ); + ldap_pvt_thread_mutex_init( &lload_pin_mutex ); lload_exop_init(); diff --git a/servers/lloadd/lload.h b/servers/lloadd/lload.h index 57954f4763..3ae63c1b3f 100644 --- a/servers/lloadd/lload.h +++ b/servers/lloadd/lload.h @@ -257,6 +257,8 @@ struct LloadConnection { struct berval c_sasl_bind_mech; /* mech in progress */ struct berval c_auth; /* authcDN (possibly in progress) */ + unsigned long c_pin_id; + #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS struct berval c_vc_cookie; #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */ @@ -321,6 +323,7 @@ struct LloadOperation { enum op_state o_freeing; ber_tag_t o_tag; time_t o_start; + unsigned long o_pin_id; BerElement *o_ber; BerValue o_request, o_ctrls; diff --git a/servers/lloadd/module_init.c b/servers/lloadd/module_init.c index 88806293ab..3f042f6b38 100644 --- a/servers/lloadd/module_init.c +++ b/servers/lloadd/module_init.c @@ -63,6 +63,7 @@ lload_conn_pool_init() ldap_pvt_thread_mutex_init( &backend_mutex ); ldap_pvt_thread_mutex_init( &clients_mutex ); + ldap_pvt_thread_mutex_init( &lload_pin_mutex ); lload_exop_init(); Debug( LDAP_DEBUG_TRACE, "lload_conn_pool_init: " diff --git a/servers/lloadd/operation.c b/servers/lloadd/operation.c index f83d2ade67..c232c408bc 100644 --- a/servers/lloadd/operation.c +++ b/servers/lloadd/operation.c @@ -18,6 +18,9 @@ #include "lutil.h" #include "lload.h" +ldap_pvt_thread_mutex_t lload_pin_mutex; +unsigned long lload_next_pin = 1; + ber_tag_t slap_req2res( ber_tag_t tag ) { @@ -87,9 +90,14 @@ operation_client_cmp( const void *left, const void *right ) const LloadOperation *l = left, *r = right; assert( l->o_client_connid == r->o_client_connid ); - return ( l->o_client_msgid < r->o_client_msgid ) ? - -1 : - ( l->o_client_msgid > r->o_client_msgid ); + if ( l->o_client_msgid || r->o_client_msgid ) { + return ( l->o_client_msgid < r->o_client_msgid ) ? + -1 : + ( l->o_client_msgid > r->o_client_msgid ); + } else { + return ( l->o_pin_id < r->o_pin_id ) ? -1 : + ( l->o_pin_id > r->o_pin_id ); + } } int @@ -98,9 +106,14 @@ operation_upstream_cmp( const void *left, const void *right ) const LloadOperation *l = left, *r = right; assert( l->o_upstream_connid == r->o_upstream_connid ); - return ( l->o_upstream_msgid < r->o_upstream_msgid ) ? - -1 : - ( l->o_upstream_msgid > r->o_upstream_msgid ); + if ( l->o_upstream_msgid || r->o_upstream_msgid ) { + return ( l->o_upstream_msgid < r->o_upstream_msgid ) ? + -1 : + ( l->o_upstream_msgid > r->o_upstream_msgid ); + } else { + return ( l->o_pin_id < r->o_pin_id ) ? -1 : + ( l->o_pin_id > r->o_pin_id ); + } } /* @@ -632,6 +645,9 @@ done: CONNECTION_LOCK(c); if ( c->c_state == LLOAD_C_BINDING ) { c->c_state = LLOAD_C_READY; + if ( op->o_pin_id ) { + c->c_pin_id = 0; + } } op->o_client_refcnt--; operation_destroy_from_client( op ); diff --git a/servers/lloadd/proto-lload.h b/servers/lloadd/proto-lload.h index b0249391a7..ebad0ada33 100644 --- a/servers/lloadd/proto-lload.h +++ b/servers/lloadd/proto-lload.h @@ -135,6 +135,8 @@ LDAP_SLAPD_F (void) lload_libevent_destroy( void ); /* * operation.c */ +LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) lload_pin_mutex; +LDAP_SLAPD_V (unsigned long) lload_next_pin; LDAP_SLAPD_F (const char *) lload_msgtype2str( ber_tag_t tag ); LDAP_SLAPD_F (int) operation_upstream_cmp( const void *l, const void *r ); LDAP_SLAPD_F (int) operation_client_cmp( const void *l, const void *r ); diff --git a/servers/lloadd/upstream.c b/servers/lloadd/upstream.c index bd05de3997..4cb46b71b4 100644 --- a/servers/lloadd/upstream.c +++ b/servers/lloadd/upstream.c @@ -78,9 +78,11 @@ forward_final_response( "client connid=%lu\n", op->o_upstream_connid, op->o_upstream_msgid, op->o_client_connid ); rc = forward_response( client, op, ber ); - CONNECTION_LOCK_DECREF(op->o_upstream); - operation_destroy_from_upstream( op ); - CONNECTION_UNLOCK_INCREF(op->o_upstream); + CONNECTION_LOCK(op->o_upstream); + if ( !op->o_pin_id || !op->o_upstream_refcnt-- ) { + operation_destroy_from_upstream( op ); + } + CONNECTION_UNLOCK(op->o_upstream); return rc; }