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 *)©_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