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