Home | History | Annotate | Line # | Download | only in lloadd
client.c revision 1.3
      1 /*	$NetBSD: client.c,v 1.3 2025/09/05 21:16:24 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2024 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 __RCSID("$NetBSD: client.c,v 1.3 2025/09/05 21:16:24 christos Exp $");
     20 
     21 #include "portable.h"
     22 
     23 #include <ac/socket.h>
     24 #include <ac/errno.h>
     25 #include <ac/string.h>
     26 #include <ac/time.h>
     27 #include <ac/unistd.h>
     28 
     29 #include "lutil.h"
     30 #include "lload.h"
     31 
     32 long lload_client_max_pending = 0;
     33 
     34 lload_c_head clients = LDAP_CIRCLEQ_HEAD_INITIALIZER( clients );
     35 
     36 ldap_pvt_thread_mutex_t clients_mutex;
     37 
     38 static void client_unlink( LloadConnection *upstream );
     39 
     40 int
     41 request_abandon( LloadConnection *c, LloadOperation *op )
     42 {
     43     LloadOperation *request, needle = { .o_client_connid = c->c_connid };
     44     int rc = LDAP_SUCCESS;
     45 
     46     op->o_res = LLOAD_OP_COMPLETED;
     47 
     48     if ( ber_decode_int( &op->o_request, &needle.o_client_msgid ) ) {
     49         Debug( LDAP_DEBUG_STATS, "request_abandon: "
     50                 "connid=%lu msgid=%d invalid integer sent in abandon request\n",
     51                 c->c_connid, op->o_client_msgid );
     52 
     53         OPERATION_UNLINK(op);
     54         CONNECTION_LOCK_DESTROY(c);
     55         return -1;
     56     }
     57 
     58     CONNECTION_LOCK(c);
     59     request = ldap_tavl_find( c->c_ops, &needle, operation_client_cmp );
     60     if ( !request ) {
     61         Debug( LDAP_DEBUG_STATS, "request_abandon: "
     62                 "connid=%lu msgid=%d requests abandon of an operation "
     63                 "msgid=%d not being processed anymore\n",
     64                 c->c_connid, op->o_client_msgid, needle.o_client_msgid );
     65         CONNECTION_UNLOCK(c);
     66         goto done;
     67     } else if ( request->o_tag == LDAP_REQ_BIND ) {
     68         /* RFC 4511 states we must not allow Abandon on Binds */
     69         Debug( LDAP_DEBUG_STATS, "request_abandon: "
     70                 "connid=%lu msgid=%d requests abandon of a bind operation "
     71                 "msgid=%d\n",
     72                 c->c_connid, op->o_client_msgid, needle.o_client_msgid );
     73         CONNECTION_UNLOCK(c);
     74         goto done;
     75     }
     76     Debug( LDAP_DEBUG_STATS, "request_abandon: "
     77             "connid=%lu msgid=%d abandoning %s msgid=%d\n",
     78             c->c_connid, op->o_client_msgid,
     79             lload_msgtype2str( request->o_tag ), needle.o_client_msgid );
     80 
     81     if ( c->c_state == LLOAD_C_BINDING ) {
     82         assert(0);
     83     }
     84 
     85     CONNECTION_UNLOCK(c);
     86     operation_abandon( request );
     87 
     88 done:
     89     OPERATION_UNLINK(op);
     90     return rc;
     91 }
     92 
     93 int
     94 request_process( LloadConnection *client, LloadOperation *op )
     95 {
     96     BerElement *output;
     97     LloadConnection *upstream = NULL;
     98     LloadBackend *b = NULL;
     99     ber_int_t msgid;
    100     int res = LDAP_UNAVAILABLE, rc = LDAP_SUCCESS;
    101     char *message = "no connections available";
    102     enum op_restriction client_restricted;
    103 
    104     if ( lload_control_actions && !BER_BVISNULL( &op->o_ctrls ) ) {
    105         BerElementBuffer copy_berbuf;
    106         BerElement *copy = (BerElement *)&copy_berbuf;
    107         struct berval control;
    108 
    109         ber_init2( copy, &op->o_ctrls, 0 );
    110 
    111         while ( ber_skip_element( copy, &control ) == LBER_SEQUENCE ) {
    112             struct restriction_entry *entry, needle = {};
    113             BerElementBuffer control_berbuf;
    114             BerElement *control_ber = (BerElement *)&control_berbuf;
    115 
    116             ber_init2( control_ber, &control, 0 );
    117 
    118             if ( ber_skip_element( control_ber, &needle.oid ) == LBER_ERROR ) {
    119                 res = LDAP_PROTOCOL_ERROR;
    120                 message = "invalid control";
    121 
    122                 operation_send_reject( op, res, message, 1 );
    123                 goto fail;
    124             }
    125 
    126             entry = ldap_tavl_find(
    127                     lload_control_actions, &needle, lload_restriction_cmp );
    128             if ( entry && op->o_restricted < entry->action ) {
    129                 op->o_restricted = entry->action;
    130             }
    131         }
    132     }
    133     if ( op->o_restricted < LLOAD_OP_RESTRICTED_WRITE &&
    134             lload_write_coherence &&
    135             op->o_tag != LDAP_REQ_SEARCH &&
    136             op->o_tag != LDAP_REQ_COMPARE ) {
    137         op->o_restricted = LLOAD_OP_RESTRICTED_WRITE;
    138     }
    139 
    140     if ( op->o_restricted == LLOAD_OP_RESTRICTED_REJECT ) {
    141         res = LDAP_UNWILLING_TO_PERFORM;
    142         message = "extended operation or control disallowed";
    143 
    144         operation_send_reject( op, res, message, 1 );
    145         goto fail;
    146     }
    147 
    148     CONNECTION_LOCK(client);
    149     client_restricted = client->c_restricted;
    150     if ( client_restricted ) {
    151         if ( client_restricted == LLOAD_OP_RESTRICTED_WRITE &&
    152                 client->c_restricted_inflight == 0 &&
    153                 client->c_restricted_at >= 0 &&
    154                 client->c_restricted_at + lload_write_coherence <
    155                     op->o_start.tv_sec ) {
    156             Debug( LDAP_DEBUG_TRACE, "request_process: "
    157                     "connid=%lu write coherence to backend '%s' expired\n",
    158                     client->c_connid, client->c_backend->b_name.bv_val );
    159             client->c_backend = NULL;
    160             client_restricted = client->c_restricted = LLOAD_OP_NOT_RESTRICTED;
    161         }
    162         switch ( client_restricted ) {
    163             case LLOAD_OP_NOT_RESTRICTED:
    164                 break;
    165             case LLOAD_OP_RESTRICTED_WRITE:
    166             case LLOAD_OP_RESTRICTED_BACKEND:
    167                 b = client->c_backend;
    168                 assert( b );
    169                 break;
    170             case LLOAD_OP_RESTRICTED_UPSTREAM:
    171             case LLOAD_OP_RESTRICTED_ISOLATE:
    172                 upstream = client->c_linked_upstream;
    173                 assert( upstream );
    174                 break;
    175             default:
    176                 assert(0);
    177                 break;
    178         }
    179     }
    180     if ( op->o_restricted < client_restricted ) {
    181         op->o_restricted = client_restricted;
    182     }
    183     CONNECTION_UNLOCK(client);
    184 
    185     if ( upstream ) {
    186         b = upstream->c_backend;
    187         checked_lock( &b->b_mutex );
    188         if ( !try_upstream( b, NULL, op, upstream, &res, &message ) ) {
    189             upstream = NULL;
    190         }
    191         checked_unlock( &b->b_mutex );
    192     } else if ( b ) {
    193         backend_select( b, op, &upstream, &res, &message );
    194     } else {
    195         upstream_select( op, &upstream, &res, &message );
    196     }
    197 
    198     if ( !upstream ) {
    199         Debug( LDAP_DEBUG_STATS, "request_process: "
    200                 "connid=%lu, msgid=%d no available connection found\n",
    201                 op->o_client_connid, op->o_client_msgid );
    202 
    203         operation_send_reject( op, res, message, 1 );
    204         goto fail;
    205     }
    206     CONNECTION_ASSERT_LOCKED(upstream);
    207     assert_locked( &upstream->c_io_mutex );
    208     op->o_upstream = upstream;
    209     op->o_upstream_connid = upstream->c_connid;
    210     op->o_res = LLOAD_OP_FAILED;
    211 
    212     /* Was it unlinked in the meantime? No need to send a response since the
    213      * client is dead */
    214     if ( !IS_ALIVE( op, o_refcnt ) ) {
    215         LloadBackend *b = upstream->c_backend;
    216 
    217         upstream->c_n_ops_executing--;
    218         checked_unlock( &upstream->c_io_mutex );
    219         CONNECTION_UNLOCK(upstream);
    220 
    221         checked_lock( &b->b_mutex );
    222         b->b_n_ops_executing--;
    223         checked_unlock( &b->b_mutex );
    224 
    225         assert( !IS_ALIVE( client, c_live ) );
    226         checked_lock( &op->o_link_mutex );
    227         if ( op->o_upstream ) {
    228             op->o_upstream = NULL;
    229         }
    230         checked_unlock( &op->o_link_mutex );
    231         return -1;
    232     }
    233 
    234     output = upstream->c_pendingber;
    235     if ( output == NULL && (output = ber_alloc()) == NULL ) {
    236         LloadBackend *b = upstream->c_backend;
    237 
    238         upstream->c_n_ops_executing--;
    239         CONNECTION_UNLOCK(upstream);
    240         checked_unlock( &upstream->c_io_mutex );
    241 
    242         checked_lock( &b->b_mutex );
    243         b->b_n_ops_executing--;
    244         operation_update_backend_counters( op, b );
    245         checked_unlock( &b->b_mutex );
    246 
    247         Debug( LDAP_DEBUG_ANY, "request_process: "
    248                 "ber_alloc failed\n" );
    249 
    250         rc = -1;
    251         goto fail;
    252     }
    253     upstream->c_pendingber = output;
    254 
    255     if ( client_restricted < LLOAD_OP_RESTRICTED_UPSTREAM &&
    256             op->o_restricted >= LLOAD_OP_RESTRICTED_UPSTREAM ) {
    257         rc = ldap_tavl_insert(
    258                 &upstream->c_linked, client, lload_upstream_entry_cmp,
    259                 ldap_avl_dup_error );
    260         assert( rc == LDAP_SUCCESS );
    261     }
    262 
    263     op->o_upstream_msgid = msgid = upstream->c_next_msgid++;
    264     rc = ldap_tavl_insert(
    265             &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
    266 
    267     CONNECTION_UNLOCK(upstream);
    268 
    269     Debug( LDAP_DEBUG_TRACE, "request_process: "
    270             "client connid=%lu added %s msgid=%d to upstream connid=%lu as "
    271             "msgid=%d\n",
    272             op->o_client_connid, lload_msgtype2str( op->o_tag ),
    273             op->o_client_msgid, op->o_upstream_connid, op->o_upstream_msgid );
    274     assert( rc == LDAP_SUCCESS );
    275 
    276     lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_forwarded++;
    277 
    278     if ( op->o_restricted > client_restricted ||
    279             client_restricted == LLOAD_OP_RESTRICTED_WRITE ) {
    280         CONNECTION_LOCK(client);
    281         if ( op->o_restricted > client_restricted ) {
    282             client->c_restricted = op->o_restricted;
    283         }
    284         if ( op->o_restricted == LLOAD_OP_RESTRICTED_WRITE ) {
    285             client->c_restricted_inflight++;
    286         }
    287         if ( op->o_restricted >= LLOAD_OP_RESTRICTED_UPSTREAM ) {
    288             if ( client_restricted < LLOAD_OP_RESTRICTED_UPSTREAM ) {
    289                 client->c_linked_upstream = upstream;
    290             }
    291             assert( client->c_linked_upstream == upstream );
    292             client->c_backend = NULL;
    293         } else if ( op->o_restricted >= LLOAD_OP_RESTRICTED_WRITE ) {
    294             if ( client_restricted < LLOAD_OP_RESTRICTED_WRITE ) {
    295                 client->c_backend = upstream->c_backend;
    296             }
    297             assert( client->c_backend == upstream->c_backend );
    298         }
    299         CONNECTION_UNLOCK(client);
    300     }
    301 
    302     if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) &&
    303             client->c_type != LLOAD_C_PRIVILEGED ) {
    304         CONNECTION_LOCK(client);
    305         Debug( LDAP_DEBUG_TRACE, "request_process: "
    306                 "proxying identity %s to upstream\n",
    307                 client->c_auth.bv_val );
    308         ber_printf( output, "t{titOt{{sbO}" /* "}}" */, LDAP_TAG_MESSAGE,
    309                 LDAP_TAG_MSGID, msgid,
    310                 op->o_tag, &op->o_request,
    311                 LDAP_TAG_CONTROLS,
    312                 LDAP_CONTROL_PROXY_AUTHZ, 1, &client->c_auth );
    313         CONNECTION_UNLOCK(client);
    314 
    315         if ( !BER_BVISNULL( &op->o_ctrls ) ) {
    316             ber_write( output, op->o_ctrls.bv_val, op->o_ctrls.bv_len, 0 );
    317         }
    318 
    319         ber_printf( output, /* "{{" */ "}}" );
    320     } else {
    321         ber_printf( output, "t{titOtO}", LDAP_TAG_MESSAGE,
    322                 LDAP_TAG_MSGID, msgid,
    323                 op->o_tag, &op->o_request,
    324                 LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
    325     }
    326     checked_unlock( &upstream->c_io_mutex );
    327 
    328     connection_write_cb( -1, 0, upstream );
    329     return rc;
    330 
    331 fail:
    332     if ( upstream ) {
    333         CONNECTION_LOCK_DESTROY(upstream);
    334 
    335         /* We have not committed any restrictions in the end */
    336         op->o_restricted = LLOAD_OP_NOT_RESTRICTED;
    337         operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
    338     }
    339 
    340     OPERATION_UNLINK(op);
    341     if ( rc ) {
    342         CONNECTION_LOCK_DESTROY(client);
    343     }
    344     return rc;
    345 }
    346 
    347 int
    348 handle_one_request( LloadConnection *c )
    349 {
    350     BerElement *ber;
    351     LloadOperation *op = NULL;
    352     RequestHandler handler = NULL;
    353     int over_limit = 0;
    354     enum sc_state state;
    355     enum sc_io_state io_state;
    356 
    357     ber = c->c_currentber;
    358     c->c_currentber = NULL;
    359 
    360     CONNECTION_LOCK(c);
    361     op = operation_init( c, ber );
    362     if ( !op ) {
    363         Debug( LDAP_DEBUG_ANY, "handle_one_request: "
    364                 "connid=%lu, operation_init failed\n",
    365                 c->c_connid );
    366         CONNECTION_DESTROY(c);
    367         ber_free( ber, 1 );
    368         return -1;
    369     }
    370     if ( lload_client_max_pending &&
    371             c->c_n_ops_executing >= lload_client_max_pending ) {
    372         over_limit = 1;
    373     }
    374 
    375     /*
    376      * Remember the current state so we don't have to lock again,
    377      * we're only screening whether we can keep going, e.g. noone can change
    378      * state to LLOAD_C_BINDING from under us (would imply a new operation was
    379      * received but that's us), but the opposite is possible - a Bind response
    380      * could be received and processed in the meantime.
    381      */
    382     state = c->c_state;
    383     CONNECTION_UNLOCK(c);
    384 
    385     switch ( op->o_tag ) {
    386         case LDAP_REQ_UNBIND:
    387             /* There is never a response for this operation */
    388             op->o_res = LLOAD_OP_COMPLETED;
    389             OPERATION_UNLINK(op);
    390 
    391             Debug( LDAP_DEBUG_STATS, "handle_one_request: "
    392                     "received unbind, closing client connid=%lu\n",
    393                     c->c_connid );
    394             CONNECTION_LOCK_DESTROY(c);
    395             return -1;
    396         case LDAP_REQ_BIND:
    397             handler = request_bind;
    398             break;
    399         case LDAP_REQ_ABANDON:
    400             /* We can't send a response to abandon requests even if a bind is
    401              * currently in progress */
    402             return request_abandon( c, op );
    403         case LDAP_REQ_EXTENDED:
    404         default:
    405             if ( state == LLOAD_C_BINDING ) {
    406                 operation_send_reject(
    407                         op, LDAP_PROTOCOL_ERROR, "bind in progress", 0 );
    408                 return LDAP_SUCCESS;
    409             }
    410             if ( over_limit ) {
    411                 operation_send_reject( op, LDAP_BUSY,
    412                         "pending operation limit reached on this connection",
    413                         0 );
    414                 return LDAP_SUCCESS;
    415             }
    416 
    417             checked_lock( &c->c_io_mutex );
    418             io_state = c->c_io_state;
    419             checked_unlock( &c->c_io_mutex );
    420             if ( io_state & LLOAD_C_READ_PAUSE ) {
    421                 operation_send_reject( op, LDAP_BUSY,
    422                         "writing side backlogged, please keep reading", 0 );
    423                 return LDAP_SUCCESS;
    424             }
    425 
    426             if ( op->o_tag == LDAP_REQ_EXTENDED ) {
    427                 handler = request_extended;
    428             } else {
    429                 handler = request_process;
    430             }
    431             break;
    432     }
    433 
    434     if ( state == LLOAD_C_CLOSING ) {
    435         operation_send_reject(
    436                 op, LDAP_UNAVAILABLE, "connection is shutting down", 0 );
    437         return LDAP_SUCCESS;
    438     }
    439 
    440     return handler( c, op );
    441 }
    442 
    443 #ifdef HAVE_TLS
    444 /*
    445  * The connection has a token assigned to it when the callback is set up.
    446  */
    447 void
    448 client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
    449 {
    450     LloadConnection *c = arg;
    451     epoch_t epoch;
    452     int rc = 0;
    453 
    454     if ( what & EV_TIMEOUT ) {
    455         Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
    456                 "connid=%lu, timeout reached, destroying\n",
    457                 c->c_connid );
    458         goto fail;
    459     }
    460 
    461     /*
    462      * In case of StartTLS, make sure we flush the response first.
    463      * Also before we try to read anything from the connection, it isn't
    464      * permitted to Abandon a StartTLS exop per RFC4511 anyway.
    465      */
    466     checked_lock( &c->c_io_mutex );
    467     if ( c->c_pendingber ) {
    468         checked_unlock( &c->c_io_mutex );
    469         connection_write_cb( s, what, arg );
    470 
    471         if ( !IS_ALIVE( c, c_live ) ) {
    472             goto fail;
    473         }
    474 
    475         /* Do we still have data pending? If so, connection_write_cb would
    476          * already have arranged the write callback to trigger again */
    477         checked_lock( &c->c_io_mutex );
    478         if ( c->c_pendingber ) {
    479             checked_unlock( &c->c_io_mutex );
    480             return;
    481         }
    482     }
    483 
    484     rc = ldap_pvt_tls_accept( c->c_sb, LLOAD_TLS_CTX );
    485     checked_unlock( &c->c_io_mutex );
    486     if ( rc < 0 ) {
    487         goto fail;
    488     }
    489 
    490     if ( rc == 0 ) {
    491         struct event_base *base = event_get_base( c->c_read_event );
    492 
    493         /*
    494          * We're finished, replace the callbacks
    495          *
    496          * This is deadlock-safe, since both share the same base - the one
    497          * that's just running us.
    498          */
    499         CONNECTION_LOCK(c);
    500         event_del( c->c_read_event );
    501         event_del( c->c_write_event );
    502 
    503         c->c_read_timeout = NULL;
    504         event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
    505                 connection_read_cb, c );
    506         if ( IS_ALIVE( c, c_live ) ) {
    507             event_add( c->c_read_event, c->c_read_timeout );
    508         }
    509 
    510         event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
    511                 connection_write_cb, c );
    512         Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
    513                 "connid=%lu finished\n",
    514                 c->c_connid );
    515 
    516         c->c_is_tls = LLOAD_TLS_ESTABLISHED;
    517         CONNECTION_UNLOCK(c);
    518         return;
    519     } else if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
    520         if ( IS_ALIVE( c, c_live ) ) {
    521             CONNECTION_LOCK(c);
    522             event_add( c->c_write_event, lload_write_timeout );
    523             CONNECTION_UNLOCK(c);
    524         }
    525         Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
    526                 "connid=%lu need write rc=%d\n",
    527                 c->c_connid, rc );
    528     }
    529     return;
    530 
    531 fail:
    532     Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
    533             "connid=%lu failed rc=%d\n",
    534             c->c_connid, rc );
    535 
    536     assert( c->c_ops == NULL );
    537     epoch = epoch_join();
    538     CONNECTION_LOCK_DESTROY(c);
    539     epoch_leave( epoch );
    540 }
    541 #endif /* HAVE_TLS */
    542 
    543 LloadConnection *
    544 client_init(
    545         ber_socket_t s,
    546         const char *peername,
    547         struct event_base *base,
    548         int flags )
    549 {
    550     LloadConnection *c;
    551     struct event *event;
    552     event_callback_fn read_cb = connection_read_cb,
    553                       write_cb = connection_write_cb;
    554 
    555     if ( (c = lload_connection_init( s, peername, flags) ) == NULL ) {
    556         return NULL;
    557     }
    558 
    559     {
    560         ber_len_t max = sockbuf_max_incoming_client;
    561         ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
    562     }
    563 
    564     c->c_state = LLOAD_C_READY;
    565 
    566     if ( flags & CONN_IS_TLS ) {
    567 #ifdef HAVE_TLS
    568         int rc;
    569 
    570         c->c_is_tls = LLOAD_LDAPS;
    571 
    572         rc = ldap_pvt_tls_accept( c->c_sb, LLOAD_TLS_CTX );
    573         if ( rc < 0 ) {
    574             Debug( LDAP_DEBUG_CONNS, "client_init: "
    575                     "connid=%lu failed initial TLS accept rc=%d\n",
    576                     c->c_connid, rc );
    577             CONNECTION_LOCK(c);
    578             goto fail;
    579         }
    580 
    581         if ( rc ) {
    582             c->c_read_timeout = lload_timeout_net;
    583             read_cb = write_cb = client_tls_handshake_cb;
    584         }
    585 #else /* ! HAVE_TLS */
    586         assert(0);
    587 #endif /* ! HAVE_TLS */
    588     }
    589 
    590     event = event_new( base, s, EV_READ|EV_PERSIST, read_cb, c );
    591     if ( !event ) {
    592         Debug( LDAP_DEBUG_ANY, "client_init: "
    593                 "Read event could not be allocated\n" );
    594         CONNECTION_LOCK(c);
    595         goto fail;
    596     }
    597     c->c_read_event = event;
    598 
    599     event = event_new( base, s, EV_WRITE, write_cb, c );
    600     if ( !event ) {
    601         Debug( LDAP_DEBUG_ANY, "client_init: "
    602                 "Write event could not be allocated\n" );
    603         CONNECTION_LOCK(c);
    604         goto fail;
    605     }
    606     c->c_write_event = event;
    607 
    608     CONNECTION_LOCK(c);
    609 #ifdef BALANCER_MODULE
    610     if ( lload_monitor_client_subsys ) {
    611         acquire_ref( &c->c_refcnt );
    612         CONNECTION_UNLOCK(c);
    613         if ( lload_monitor_conn_entry_create(
    614                     c, lload_monitor_client_subsys ) ) {
    615             CONNECTION_LOCK(c);
    616             RELEASE_REF( c, c_refcnt, c->c_destroy );
    617             goto fail;
    618         }
    619         CONNECTION_LOCK(c);
    620         RELEASE_REF( c, c_refcnt, c->c_destroy );
    621     }
    622 #endif /* BALANCER_MODULE */
    623 
    624     c->c_destroy = client_destroy;
    625     c->c_unlink = client_unlink;
    626     c->c_pdu_cb = handle_one_request;
    627 
    628     /* We only register the write event when we have data pending */
    629     event_add( c->c_read_event, c->c_read_timeout );
    630 
    631     checked_lock( &clients_mutex );
    632     LDAP_CIRCLEQ_INSERT_TAIL( &clients, c, c_next );
    633     checked_unlock( &clients_mutex );
    634     CONNECTION_UNLOCK(c);
    635 
    636     return c;
    637 fail:
    638     if ( !IS_ALIVE( c, c_live ) ) {
    639         /*
    640          * Released while we were unlocked, it's scheduled for destruction
    641          * already
    642          */
    643         return NULL;
    644     }
    645 
    646     if ( c->c_write_event ) {
    647         event_free( c->c_write_event );
    648         c->c_write_event = NULL;
    649     }
    650     if ( c->c_read_event ) {
    651         event_free( c->c_read_event );
    652         c->c_read_event = NULL;
    653     }
    654 
    655     c->c_state = LLOAD_C_INVALID;
    656     c->c_live--;
    657     c->c_refcnt--;
    658     connection_destroy( c );
    659     return NULL;
    660 }
    661 
    662 void
    663 client_reset( LloadConnection *c )
    664 {
    665     TAvlnode *root;
    666     long freed = 0, executing;
    667     LloadConnection *linked_upstream = NULL;
    668     enum op_restriction restricted = c->c_restricted;
    669 
    670     CONNECTION_ASSERT_LOCKED(c);
    671     root = c->c_ops;
    672     c->c_ops = NULL;
    673     executing = c->c_n_ops_executing;
    674     c->c_n_ops_executing = 0;
    675 
    676     if ( !BER_BVISNULL( &c->c_auth ) ) {
    677         ch_free( c->c_auth.bv_val );
    678         BER_BVZERO( &c->c_auth );
    679     }
    680     if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
    681         ch_free( c->c_sasl_bind_mech.bv_val );
    682         BER_BVZERO( &c->c_sasl_bind_mech );
    683     }
    684 
    685     if ( restricted && restricted < LLOAD_OP_RESTRICTED_ISOLATE ) {
    686         if ( c->c_backend ) {
    687             assert( c->c_restricted <= LLOAD_OP_RESTRICTED_BACKEND );
    688             assert( c->c_restricted_inflight == 0 );
    689             c->c_backend = NULL;
    690             c->c_restricted_at = 0;
    691         } else {
    692             assert( c->c_restricted == LLOAD_OP_RESTRICTED_UPSTREAM );
    693             assert( c->c_linked_upstream != NULL );
    694             linked_upstream = c->c_linked_upstream;
    695             c->c_linked_upstream = NULL;
    696         }
    697     }
    698     CONNECTION_UNLOCK(c);
    699 
    700     if ( root ) {
    701         freed = ldap_tavl_free( root, (AVL_FREE)operation_abandon );
    702         Debug( LDAP_DEBUG_TRACE, "client_reset: "
    703                 "dropped %ld operations\n",
    704                 freed );
    705     }
    706     assert( freed == executing );
    707 
    708     if ( linked_upstream && restricted == LLOAD_OP_RESTRICTED_UPSTREAM ) {
    709         LloadConnection *removed = ldap_tavl_delete(
    710                 &linked_upstream->c_linked, c, lload_upstream_entry_cmp );
    711         assert( removed == c );
    712     }
    713 
    714     CONNECTION_LOCK(c);
    715     CONNECTION_ASSERT_LOCKED(c);
    716 }
    717 
    718 void
    719 client_unlink( LloadConnection *c )
    720 {
    721     enum sc_state state;
    722     struct event *read_event, *write_event;
    723 
    724     Debug( LDAP_DEBUG_CONNS, "client_unlink: "
    725             "removing client connid=%lu\n",
    726             c->c_connid );
    727 
    728     CONNECTION_ASSERT_LOCKED(c);
    729     assert( c->c_state != LLOAD_C_INVALID );
    730     assert( c->c_state != LLOAD_C_DYING );
    731 
    732     state = c->c_state;
    733     c->c_state = LLOAD_C_DYING;
    734 
    735     if ( c->c_restricted == LLOAD_OP_RESTRICTED_ISOLATE ) {
    736         /* Allow upstream connection to be severed in client_reset() */
    737         c->c_restricted = LLOAD_OP_RESTRICTED_UPSTREAM;
    738     }
    739 
    740     read_event = c->c_read_event;
    741     write_event = c->c_write_event;
    742     CONNECTION_UNLOCK(c);
    743 
    744     if ( read_event ) {
    745         event_del( read_event );
    746     }
    747 
    748     if ( write_event ) {
    749         event_del( write_event );
    750     }
    751 
    752     if ( state != LLOAD_C_DYING ) {
    753         checked_lock( &clients_mutex );
    754         LDAP_CIRCLEQ_REMOVE( &clients, c, c_next );
    755         checked_unlock( &clients_mutex );
    756     }
    757 
    758     CONNECTION_LOCK(c);
    759     client_reset( c );
    760     CONNECTION_ASSERT_LOCKED(c);
    761 }
    762 
    763 void
    764 client_destroy( LloadConnection *c )
    765 {
    766     Debug( LDAP_DEBUG_CONNS, "client_destroy: "
    767             "destroying client connid=%lu\n",
    768             c->c_connid );
    769 
    770     CONNECTION_LOCK(c);
    771     assert( c->c_state == LLOAD_C_DYING );
    772 
    773 #ifdef BALANCER_MODULE
    774     /*
    775      * Can't do this in client_unlink as that could be run from cn=monitor
    776      * modify callback.
    777      */
    778     if ( !BER_BVISNULL( &c->c_monitor_dn ) ) {
    779         lload_monitor_conn_unlink( c );
    780     }
    781 #endif /* BALANCER_MODULE */
    782 
    783     c->c_state = LLOAD_C_INVALID;
    784 
    785     assert( c->c_ops == NULL );
    786 
    787     if ( c->c_read_event ) {
    788         event_free( c->c_read_event );
    789         c->c_read_event = NULL;
    790     }
    791 
    792     if ( c->c_write_event ) {
    793         event_free( c->c_write_event );
    794         c->c_write_event = NULL;
    795     }
    796 
    797     assert( c->c_refcnt == 0 );
    798     connection_destroy( c );
    799 }
    800 
    801 void
    802 clients_destroy( int gentle )
    803 {
    804     epoch_t epoch = epoch_join();
    805     checked_lock( &clients_mutex );
    806     connections_walk(
    807             &clients_mutex, &clients, lload_connection_close, &gentle );
    808     checked_unlock( &clients_mutex );
    809     epoch_leave( epoch );
    810 }
    811