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 <event2/event.h>
25 #include <event2/dns.h>
26 
27 #include "lutil.h"
28 #include "lload.h"
29 
30 static void
upstream_connect_cb(evutil_socket_t s,short what,void * arg)31 upstream_connect_cb( evutil_socket_t s, short what, void *arg )
32 {
33     LloadPendingConnection *conn = arg;
34     LloadBackend *b = conn->backend;
35     int error = 0, rc = -1;
36     epoch_t epoch;
37 
38     checked_lock( &b->b_mutex );
39     Debug( LDAP_DEBUG_CONNS, "upstream_connect_cb: "
40             "fd=%d connection callback for backend uri='%s'\n",
41             s, b->b_uri.bv_val );
42 
43     if ( s != conn->fd ) {
44         /* backend_reset has been here first */
45         goto preempted;
46     }
47 
48     epoch = epoch_join();
49 
50     if ( what == EV_WRITE ) {
51         socklen_t optlen = sizeof(error);
52 
53         if ( getsockopt( conn->fd, SOL_SOCKET, SO_ERROR, (void *)&error,
54                      &optlen ) < 0 ) {
55             goto done;
56         }
57         if ( error == EINTR || error == EINPROGRESS || error == EWOULDBLOCK ) {
58             checked_unlock( &b->b_mutex );
59             epoch_leave( epoch );
60             return;
61         } else if ( error ) {
62             goto done;
63         } else if ( upstream_init( s, conn->backend ) == NULL ) {
64             goto done;
65         }
66         rc = LDAP_SUCCESS;
67     }
68 
69 done:
70     epoch_leave( epoch );
71 
72     LDAP_LIST_REMOVE( conn, next );
73     if ( rc ) {
74         evutil_closesocket( conn->fd );
75         b->b_opening--;
76         b->b_failed++;
77         if ( what & EV_TIMEOUT ) {
78             Debug( LDAP_DEBUG_ANY, "upstream_connect_cb: "
79                     "fd=%d connection timed out\n",
80                     s );
81         } else {
82             char ebuf[128];
83             Debug( LDAP_DEBUG_ANY, "upstream_connect_cb: "
84                     "fd=%d connection set up failed%s%s\n",
85                     s, error ? ": " : "",
86                     error ? sock_errstr( error, ebuf, sizeof(ebuf) ) : "" );
87         }
88         backend_retry( b );
89     }
90 preempted:
91     checked_unlock( &b->b_mutex );
92 
93     event_free( conn->event );
94     ch_free( conn );
95 }
96 
97 static void
upstream_name_cb(int result,struct evutil_addrinfo * res,void * arg)98 upstream_name_cb( int result, struct evutil_addrinfo *res, void *arg )
99 {
100     LloadBackend *b = arg;
101     ber_socket_t s = AC_SOCKET_INVALID;
102     epoch_t epoch;
103     int rc;
104 
105     if ( result == EVUTIL_EAI_CANCEL ) {
106         Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
107                 "cancelled\n" );
108         return;
109     }
110 
111     checked_lock( &b->b_mutex );
112     /* We were already running when backend_reset tried to cancel us, but were
113      * already stuck waiting for the mutex, nothing to do and b_opening has
114      * been decremented as well */
115     if ( b->b_dns_req == NULL ) {
116         checked_unlock( &b->b_mutex );
117         return;
118     }
119     b->b_dns_req = NULL;
120 
121     epoch = epoch_join();
122     if ( result || !res ) {
123         Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
124                 "name resolution failed for backend '%s': %s\n",
125                 b->b_uri.bv_val, evutil_gai_strerror( result ) );
126         goto fail;
127     }
128 
129     /* TODO: if we get failures, try the other addrinfos */
130     if ( (s = socket( res->ai_family, SOCK_STREAM, 0 )) ==
131             AC_SOCKET_INVALID ) {
132         goto fail;
133     }
134 
135     if ( ber_pvt_socket_set_nonblock( s, 1 ) ) {
136         goto fail;
137     }
138 
139 #if defined(SO_KEEPALIVE) || defined(TCP_NODELAY)
140     if ( b->b_proto == LDAP_PROTO_TCP ) {
141         int dummy = 1;
142 #ifdef SO_KEEPALIVE
143         if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE, (char *)&dummy,
144                      sizeof(dummy) ) == AC_SOCKET_ERROR ) {
145             Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
146                     "setsockopt(%d, SO_KEEPALIVE) failed (ignored).\n",
147                     s );
148         }
149         if ( bindconf.sb_keepalive.sk_idle > 0 ) {
150 #ifdef TCP_KEEPIDLE
151             if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPIDLE,
152                          (void *)&bindconf.sb_keepalive.sk_idle,
153                          sizeof(bindconf.sb_keepalive.sk_idle) ) ==
154                     AC_SOCKET_ERROR ) {
155                 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
156                         "setsockopt(%d, TCP_KEEPIDLE) failed (ignored).\n",
157                         s );
158             }
159 #else
160             Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
161                     "sockopt TCP_KEEPIDLE not supported on this system.\n" );
162 #endif /* TCP_KEEPIDLE */
163         }
164         if ( bindconf.sb_keepalive.sk_probes > 0 ) {
165 #ifdef TCP_KEEPCNT
166             if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPCNT,
167                          (void *)&bindconf.sb_keepalive.sk_probes,
168                          sizeof(bindconf.sb_keepalive.sk_probes) ) ==
169                     AC_SOCKET_ERROR ) {
170                 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
171                         "setsockopt(%d, TCP_KEEPCNT) failed (ignored).\n",
172                         s );
173             }
174 #else
175             Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
176                     "sockopt TCP_KEEPCNT not supported on this system.\n" );
177 #endif /* TCP_KEEPCNT */
178         }
179         if ( bindconf.sb_keepalive.sk_interval > 0 ) {
180 #ifdef TCP_KEEPINTVL
181             if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPINTVL,
182                          (void *)&bindconf.sb_keepalive.sk_interval,
183                          sizeof(bindconf.sb_keepalive.sk_interval) ) ==
184                     AC_SOCKET_ERROR ) {
185                 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
186                         "setsockopt(%d, TCP_KEEPINTVL) failed (ignored).\n",
187                         s );
188             }
189 #else
190             Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
191                     "sockopt TCP_KEEPINTVL not supported on this system.\n" );
192 #endif /* TCP_KEEPINTVL */
193         }
194 #endif /* SO_KEEPALIVE */
195         if ( bindconf.sb_tcp_user_timeout > 0 ) {
196 #ifdef TCP_USER_TIMEOUT
197             if ( setsockopt( s, IPPROTO_TCP, TCP_USER_TIMEOUT,
198                          (void *)&bindconf.sb_tcp_user_timeout,
199                          sizeof(bindconf.sb_tcp_user_timeout) ) ==
200                     AC_SOCKET_ERROR ) {
201                 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
202                         "setsockopt(%d, TCP_USER_TIMEOUT) failed (ignored).\n",
203                         s );
204             }
205 #else
206             Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
207                     "sockopt TCP_USER_TIMEOUT not supported on this "
208                     "system.\n" );
209 #endif /* TCP_USER_TIMEOUT */
210         }
211 #ifdef TCP_NODELAY
212         if ( setsockopt( s, IPPROTO_TCP, TCP_NODELAY, (char *)&dummy,
213                      sizeof(dummy) ) == AC_SOCKET_ERROR ) {
214             Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
215                     "setsockopt(%d, TCP_NODELAY) failed (ignored).\n",
216                     s );
217         }
218 #endif /* TCP_NODELAY */
219     }
220 #endif /* SO_KEEPALIVE || TCP_NODELAY */
221 
222     if ( res->ai_family == PF_INET ) {
223         struct sockaddr_in *ai = (struct sockaddr_in *)res->ai_addr;
224         ai->sin_port = htons( b->b_port );
225         rc = connect( s, (struct sockaddr *)ai, res->ai_addrlen );
226     } else {
227         struct sockaddr_in6 *ai = (struct sockaddr_in6 *)res->ai_addr;
228         ai->sin6_port = htons( b->b_port );
229         rc = connect( s, (struct sockaddr *)ai, res->ai_addrlen );
230     }
231     /* Asynchronous connect */
232     if ( rc ) {
233         LloadPendingConnection *conn;
234 
235         if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) {
236             Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
237                     "failed to connect to server '%s'\n",
238                     b->b_uri.bv_val );
239             evutil_closesocket( s );
240             goto fail;
241         }
242 
243         conn = ch_calloc( 1, sizeof(LloadPendingConnection) );
244         LDAP_LIST_ENTRY_INIT( conn, next );
245         conn->backend = b;
246         conn->fd = s;
247 
248         conn->event = event_new( lload_get_base( s ), s, EV_WRITE|EV_PERSIST,
249                 upstream_connect_cb, conn );
250         if ( !conn->event ) {
251             Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
252                     "failed to acquire an event to finish upstream "
253                     "connection setup.\n" );
254             ch_free( conn );
255             evutil_closesocket( s );
256             goto fail;
257         }
258 
259         event_add( conn->event, lload_timeout_net );
260         LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next );
261         Debug( LDAP_DEBUG_CONNS, "upstream_name_cb: "
262                 "connection to backend uri=%s in progress\n",
263                 b->b_uri.bv_val );
264     } else if ( upstream_init( s, b ) == NULL ) {
265         goto fail;
266     }
267 
268     checked_unlock( &b->b_mutex );
269     evutil_freeaddrinfo( res );
270     epoch_leave( epoch );
271     return;
272 
273 fail:
274     if ( s != AC_SOCKET_INVALID ) {
275         evutil_closesocket( s );
276     }
277     b->b_opening--;
278     b->b_failed++;
279     backend_retry( b );
280     checked_unlock( &b->b_mutex );
281     if ( res ) {
282         evutil_freeaddrinfo( res );
283     }
284     epoch_leave( epoch );
285 }
286 
287 LloadConnection *
backend_select(LloadOperation * op,int * res)288 backend_select( LloadOperation *op, int *res )
289 {
290     LloadBackend *b, *first, *next;
291 
292     checked_lock( &backend_mutex );
293     first = b = current_backend;
294     checked_unlock( &backend_mutex );
295 
296     *res = LDAP_UNAVAILABLE;
297 
298     if ( !first ) {
299         return NULL;
300     }
301 
302     /* TODO: Two runs, one with trylock, then one actually locked if we don't
303      * find anything? */
304     do {
305         lload_c_head *head;
306         LloadConnection *c;
307 
308         checked_lock( &b->b_mutex );
309         next = LDAP_CIRCLEQ_LOOP_NEXT( &backend, b, b_next );
310 
311         if ( b->b_max_pending && b->b_n_ops_executing >= b->b_max_pending ) {
312             Debug( LDAP_DEBUG_CONNS, "backend_select: "
313                     "backend %s too busy\n",
314                     b->b_uri.bv_val );
315             checked_unlock( &b->b_mutex );
316             b = next;
317             *res = LDAP_BUSY;
318             continue;
319         }
320 
321         if ( op->o_tag == LDAP_REQ_BIND
322 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
323                 && !(lload_features & LLOAD_FEATURE_VC)
324 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
325         ) {
326             head = &b->b_bindconns;
327         } else {
328             head = &b->b_conns;
329         }
330         if ( !LDAP_CIRCLEQ_EMPTY( head ) ) {
331             *res = LDAP_BUSY;
332         }
333 
334         LDAP_CIRCLEQ_FOREACH ( c, head, c_next ) {
335             checked_lock( &c->c_io_mutex );
336             CONNECTION_LOCK(c);
337             if ( c->c_state == LLOAD_C_READY && !c->c_pendingber &&
338                     ( b->b_max_conn_pending == 0 ||
339                             c->c_n_ops_executing < b->b_max_conn_pending ) ) {
340                 Debug( LDAP_DEBUG_CONNS, "backend_select: "
341                         "selected connection connid=%lu for client "
342                         "connid=%lu msgid=%d\n",
343                         c->c_connid, op->o_client_connid, op->o_client_msgid );
344 
345                 /* c_state is DYING if we're about to be unlinked */
346                 assert( IS_ALIVE( c, c_live ) );
347 
348                 /*
349                  * Round-robin step:
350                  * Rotate the queue to put this connection at the end, same for
351                  * the backend.
352                  */
353                 LDAP_CIRCLEQ_MAKE_TAIL( head, c, c_next );
354 
355                 checked_lock( &backend_mutex );
356                 current_backend = next;
357                 checked_unlock( &backend_mutex );
358 
359                 b->b_n_ops_executing++;
360                 if ( op->o_tag == LDAP_REQ_BIND ) {
361                     b->b_counters[LLOAD_STATS_OPS_BIND].lc_ops_received++;
362                 } else {
363                     b->b_counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++;
364                 }
365                 c->c_n_ops_executing++;
366                 c->c_counters.lc_ops_received++;
367 
368                 checked_unlock( &b->b_mutex );
369                 *res = LDAP_SUCCESS;
370                 CONNECTION_ASSERT_LOCKED(c);
371                 assert_locked( &c->c_io_mutex );
372                 return c;
373             }
374             CONNECTION_UNLOCK(c);
375             checked_unlock( &c->c_io_mutex );
376         }
377         checked_unlock( &b->b_mutex );
378 
379         b = next;
380     } while ( b != first );
381 
382     return NULL;
383 }
384 
385 /*
386  * Will schedule a connection attempt if there is a need for it. Need exclusive
387  * access to backend, its b_mutex is not touched here, though.
388  */
389 void
backend_retry(LloadBackend * b)390 backend_retry( LloadBackend *b )
391 {
392     int requested;
393 
394     if ( slapd_shutdown ) {
395         Debug( LDAP_DEBUG_CONNS, "backend_retry: "
396                 "shutting down\n" );
397         return;
398     }
399     assert_locked( &b->b_mutex );
400 
401     requested = b->b_numconns;
402 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
403     if ( !(lload_features & LLOAD_FEATURE_VC) )
404 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
405     {
406         requested += b->b_numbindconns;
407     }
408 
409     if ( b->b_active + b->b_bindavail + b->b_opening >= requested ) {
410         Debug( LDAP_DEBUG_CONNS, "backend_retry: "
411                 "no more connections needed for this backend\n" );
412         assert_locked( &b->b_mutex );
413         return;
414     }
415 
416     if ( b->b_opening > 0 ) {
417         Debug( LDAP_DEBUG_CONNS, "backend_retry: "
418                 "retry in progress already\n" );
419         assert( b->b_opening == 1 );
420         assert_locked( &b->b_mutex );
421         return;
422     }
423 
424     /* We incremented b_opening when we activated the event, so it can't be
425      * pending */
426     assert( !event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) );
427     b->b_opening++;
428 
429     if ( b->b_failed > 0 ) {
430         Debug( LDAP_DEBUG_CONNS, "backend_retry: "
431                 "scheduling a retry in %d ms\n",
432                 b->b_retry_timeout );
433         event_add( b->b_retry_event, &b->b_retry_tv );
434         assert_locked( &b->b_mutex );
435         return;
436     }
437 
438     Debug( LDAP_DEBUG_CONNS, "backend_retry: "
439             "scheduling re-connection straight away\n" );
440 
441     if ( ldap_pvt_thread_pool_submit2(
442                  &connection_pool, backend_connect_task, b, &b->b_cookie ) ) {
443         Debug( LDAP_DEBUG_ANY, "backend_retry: "
444                 "failed to submit retry task, scheduling a retry instead\n" );
445         /* The current implementation of ldap_pvt_thread_pool_submit2 can fail
446          * and still set (an invalid) cookie */
447         b->b_cookie = NULL;
448         b->b_failed++;
449         event_add( b->b_retry_event, &b->b_retry_tv );
450     }
451     assert_locked( &b->b_mutex );
452 }
453 
454 void
backend_connect(evutil_socket_t s,short what,void * arg)455 backend_connect( evutil_socket_t s, short what, void *arg )
456 {
457     struct evutil_addrinfo hints = {};
458     LloadBackend *b = arg;
459     struct evdns_getaddrinfo_request *request, *placeholder;
460     char *hostname;
461     epoch_t epoch;
462 
463     checked_lock( &b->b_mutex );
464     assert( b->b_dns_req == NULL );
465 
466     if ( b->b_cookie ) {
467         b->b_cookie = NULL;
468     }
469 
470     if ( slapd_shutdown ) {
471         Debug( LDAP_DEBUG_CONNS, "backend_connect: "
472                 "doing nothing, shutdown in progress\n" );
473         b->b_opening--;
474         checked_unlock( &b->b_mutex );
475         return;
476     }
477 
478     epoch = epoch_join();
479 
480     Debug( LDAP_DEBUG_CONNS, "backend_connect: "
481             "%sattempting connection to %s\n",
482             (what & EV_TIMEOUT) ? "retry timeout finished, " : "",
483             b->b_host );
484 
485 #ifdef LDAP_PF_LOCAL
486     if ( b->b_proto == LDAP_PROTO_IPC ) {
487         struct sockaddr_un addr;
488         ber_socket_t s = socket( PF_LOCAL, SOCK_STREAM, 0 );
489         int rc;
490 
491         if ( s == AC_SOCKET_INVALID ) {
492             goto fail;
493         }
494 
495         rc = ber_pvt_socket_set_nonblock( s, 1 );
496         if ( rc ) {
497             evutil_closesocket( s );
498             goto fail;
499         }
500 
501         if ( strlen( b->b_host ) > ( sizeof(addr.sun_path) - 1 ) ) {
502             evutil_closesocket( s );
503             goto fail;
504         }
505         memset( &addr, '\0', sizeof(addr) );
506         addr.sun_family = AF_LOCAL;
507         strcpy( addr.sun_path, b->b_host );
508 
509         rc = connect(
510                 s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un) );
511         /* Asynchronous connect */
512         if ( rc ) {
513             LloadPendingConnection *conn;
514 
515             if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) {
516                 evutil_closesocket( s );
517                 goto fail;
518             }
519 
520             conn = ch_calloc( 1, sizeof(LloadPendingConnection) );
521             LDAP_LIST_ENTRY_INIT( conn, next );
522             conn->backend = b;
523             conn->fd = s;
524 
525             conn->event = event_new( lload_get_base( s ), s,
526                     EV_WRITE|EV_PERSIST, upstream_connect_cb, conn );
527             if ( !conn->event ) {
528                 Debug( LDAP_DEBUG_ANY, "backend_connect: "
529                         "failed to acquire an event to finish upstream "
530                         "connection setup.\n" );
531                 ch_free( conn );
532                 evutil_closesocket( s );
533                 goto fail;
534             }
535 
536             event_add( conn->event, lload_timeout_net );
537             LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next );
538             Debug( LDAP_DEBUG_CONNS, "backend_connect: "
539                     "connection to backend uri=%s in progress\n",
540                     b->b_uri.bv_val );
541         } else if ( upstream_init( s, b ) == NULL ) {
542             goto fail;
543         }
544 
545         checked_unlock( &b->b_mutex );
546         epoch_leave( epoch );
547         return;
548     }
549 #endif /* LDAP_PF_LOCAL */
550 
551     hints.ai_family = AF_UNSPEC;
552     hints.ai_flags = EVUTIL_AI_CANONNAME;
553     hints.ai_socktype = SOCK_STREAM;
554     hints.ai_protocol = IPPROTO_TCP;
555 
556     hostname = b->b_host;
557 
558     /*
559      * Picking any value on the stack. This is unique to our thread without
560      * having to call ldap_pvt_thread_self.
561      * We might have to revert to using ldap_pvt_thread_self eventually since
562      * this betrays where exactly our stack lies - potentially weakening some
563      * protections like ASLR.
564      */
565     placeholder = (struct evdns_getaddrinfo_request *)&request;
566     b->b_dns_req = placeholder;
567     checked_unlock( &b->b_mutex );
568 
569     request = evdns_getaddrinfo(
570             dnsbase, hostname, NULL, &hints, upstream_name_cb, b );
571 
572     checked_lock( &b->b_mutex );
573     assert( request || b->b_dns_req != placeholder );
574 
575     /* Record the request, unless upstream_name_cb or another thread
576      * cleared it. Another thread is usually backend_reset or backend_connect
577      * if upstream_name_cb finished and scheduled another one */
578     if ( b->b_dns_req == placeholder ) {
579         b->b_dns_req = request;
580     }
581     checked_unlock( &b->b_mutex );
582     epoch_leave( epoch );
583     return;
584 
585 fail:
586     b->b_opening--;
587     b->b_failed++;
588     backend_retry( b );
589     checked_unlock( &b->b_mutex );
590     epoch_leave( epoch );
591 }
592 
593 void *
backend_connect_task(void * ctx,void * arg)594 backend_connect_task( void *ctx, void *arg )
595 {
596     backend_connect( -1, 0, arg );
597     return NULL;
598 }
599 
600 /*
601  * Needs exclusive access to the backend and no other thread is allowed to call
602  * backend_retry while we're handling this.
603  *
604  * If gentle == 0, a full pause must be in effect, else we risk deadlocking on
605  * event_free().
606  */
607 void
backend_reset(LloadBackend * b,int gentle)608 backend_reset( LloadBackend *b, int gentle )
609 {
610     assert_locked( &b->b_mutex );
611     if ( b->b_cookie ) {
612         if ( ldap_pvt_thread_pool_retract( b->b_cookie ) ) {
613             b->b_cookie = NULL;
614             b->b_opening--;
615         } else {
616             /*
617              * The task might not be cancelable because it just started
618              * executing.
619              *
620              * Shutdown should be the only time when the thread pool is
621              * in that state. Keep the cookie in to keep an eye on whether
622              * it's finished yet.
623              */
624             assert( slapd_shutdown );
625         }
626     }
627     /* Not safe to hold our mutex and call event_del/free if the event's
628      * callback is running, relinquish the mutex while we do so. */
629     if ( b->b_retry_event &&
630             event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) ) {
631         assert( b->b_failed );
632         checked_unlock( &b->b_mutex );
633         event_del( b->b_retry_event );
634         checked_lock( &b->b_mutex );
635         b->b_opening--;
636     }
637     if ( b->b_dns_req ) {
638         evdns_getaddrinfo_cancel( b->b_dns_req );
639         b->b_dns_req = NULL;
640         b->b_opening--;
641     }
642     while ( !LDAP_LIST_EMPTY( &b->b_connecting ) ) {
643         LloadPendingConnection *pending = LDAP_LIST_FIRST( &b->b_connecting );
644 
645         Debug( LDAP_DEBUG_CONNS, "backend_reset: "
646                 "destroying socket pending connect() fd=%d\n",
647                 pending->fd );
648 
649         event_active( pending->event, EV_WRITE, 0 );
650         evutil_closesocket( pending->fd );
651         pending->fd = -1;
652         LDAP_LIST_REMOVE( pending, next );
653 
654         if ( !gentle ) {
655             /* None of the event bases are running, we're safe to free the
656              * event right now and potentially free the backend itself */
657             event_free( pending->event );
658             ch_free( pending );
659         }
660         /* else, just let the event dispose of the resources on its own later */
661         b->b_opening--;
662     }
663     connections_walk(
664             &b->b_mutex, &b->b_preparing, lload_connection_close, &gentle );
665     assert( LDAP_CIRCLEQ_EMPTY( &b->b_preparing ) );
666     assert( b->b_opening == ( b->b_cookie ? 1 : 0 ) );
667     b->b_failed = 0;
668 
669     connections_walk_last( &b->b_mutex, &b->b_bindconns, b->b_last_bindconn,
670             lload_connection_close, &gentle );
671     assert( gentle || b->b_bindavail == 0 );
672 
673     connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
674             lload_connection_close, &gentle );
675     assert( gentle || b->b_active == 0 );
676     assert_locked( &b->b_mutex );
677 }
678 
679 void
lload_backend_destroy(LloadBackend * b)680 lload_backend_destroy( LloadBackend *b )
681 {
682     LloadBackend *next = LDAP_CIRCLEQ_LOOP_NEXT( &backend, b, b_next );
683 
684     Debug( LDAP_DEBUG_CONNS, "lload_backend_destroy: "
685             "destroying backend uri='%s', numconns=%d, numbindconns=%d\n",
686             b->b_uri.bv_val, b->b_numconns, b->b_numbindconns );
687 
688     checked_lock( &b->b_mutex );
689     b->b_numconns = b->b_numbindconns = 0;
690     backend_reset( b, 0 );
691 
692     LDAP_CIRCLEQ_REMOVE( &backend, b, b_next );
693     if ( b == next ) {
694         current_backend = NULL;
695     } else {
696         current_backend = next;
697     }
698 
699 #ifdef BALANCER_MODULE
700     if ( b->b_monitor ) {
701         BackendDB *be;
702         struct berval monitordn = BER_BVC("cn=monitor");
703         int rc;
704 
705         be = select_backend( &monitordn, 0 );
706 
707         /* FIXME: implement proper subsys shutdown in back-monitor or make
708          * backend just an entry, not a subsys */
709         rc = b->b_monitor->mss_destroy( be, b->b_monitor );
710         assert( rc == LDAP_SUCCESS );
711     }
712 #endif /* BALANCER_MODULE */
713     checked_unlock( &b->b_mutex );
714     ldap_pvt_thread_mutex_destroy( &b->b_mutex );
715 
716     if ( b->b_retry_event ) {
717         event_del( b->b_retry_event );
718         event_free( b->b_retry_event );
719         b->b_retry_event = NULL;
720     }
721 
722     ch_free( b->b_host );
723     ch_free( b->b_uri.bv_val );
724     ch_free( b->b_name.bv_val );
725     ch_free( b );
726 }
727 
728 void
lload_backends_destroy(void)729 lload_backends_destroy( void )
730 {
731     while ( !LDAP_CIRCLEQ_EMPTY( &backend ) ) {
732         LloadBackend *b = LDAP_CIRCLEQ_FIRST( &backend );
733 
734         lload_backend_destroy( b );
735     }
736 }
737