1 /*        $NetBSD: conn.c,v 1.3 2021/08/14 16:15:00 christos Exp $    */
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1999-2021 The OpenLDAP Foundation.
7  * Portions Copyright 2001-2003 Pierangelo Masarati.
8  * Portions Copyright 1999-2003 Howard Chu.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by the Howard Chu for inclusion
21  * in OpenLDAP Software and subsequently enhanced by Pierangelo
22  * Masarati.
23  */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: conn.c,v 1.3 2021/08/14 16:15:00 christos Exp $");
27 
28 #include "portable.h"
29 
30 #include <stdio.h>
31 
32 #include <ac/errno.h>
33 #include <ac/socket.h>
34 #include <ac/string.h>
35 
36 
37 #define AVL_INTERNAL
38 #include "slap.h"
39 #include "../back-ldap/back-ldap.h"
40 #include "back-meta.h"
41 
42 /*
43  * meta_back_conndn_cmp
44  *
45  * compares two struct metaconn based on the value of the conn pointer
46  * and of the local DN; used by avl stuff
47  */
48 int
meta_back_conndn_cmp(const void * c1,const void * c2)49 meta_back_conndn_cmp(
50           const void *c1,
51           const void *c2 )
52 {
53           metaconn_t          *mc1 = ( metaconn_t * )c1;
54         metaconn_t  *mc2 = ( metaconn_t * )c2;
55           int                 rc;
56 
57           /* If local DNs don't match, it is definitely not a match */
58           /* For shared sessions, conn is NULL. Only explicitly
59            * bound sessions will have non-NULL conn.
60            */
61           rc = SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
62           if ( rc == 0 ) {
63                     rc = ber_bvcmp( &mc1->mc_local_ndn, &mc2->mc_local_ndn );
64           }
65 
66           return rc;
67 }
68 
69 /*
70  * meta_back_conndnmc_cmp
71  *
72  * compares two struct metaconn based on the value of the conn pointer,
73  * the local DN and the struct pointer; used by avl stuff
74  */
75 static int
meta_back_conndnmc_cmp(const void * c1,const void * c2)76 meta_back_conndnmc_cmp(
77           const void *c1,
78           const void *c2 )
79 {
80           metaconn_t          *mc1 = ( metaconn_t * )c1;
81         metaconn_t  *mc2 = ( metaconn_t * )c2;
82           int                 rc;
83 
84           /* If local DNs don't match, it is definitely not a match */
85           /* For shared sessions, conn is NULL. Only explicitly
86            * bound sessions will have non-NULL conn.
87            */
88           rc = SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
89           if ( rc == 0 ) {
90                     rc = ber_bvcmp( &mc1->mc_local_ndn, &mc2->mc_local_ndn );
91                     if ( rc == 0 ) {
92                               rc = SLAP_PTRCMP( mc1, mc2 );
93                     }
94           }
95 
96           return rc;
97 }
98 
99 /*
100  * meta_back_conn_cmp
101  *
102  * compares two struct metaconn based on the value of the conn pointer;
103  * used by avl stuff
104  */
105 int
meta_back_conn_cmp(const void * c1,const void * c2)106 meta_back_conn_cmp(
107           const void *c1,
108           const void *c2 )
109 {
110           metaconn_t          *mc1 = ( metaconn_t * )c1;
111         metaconn_t  *mc2 = ( metaconn_t * )c2;
112 
113           /* For shared sessions, conn is NULL. Only explicitly
114            * bound sessions will have non-NULL conn.
115            */
116           return SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
117 }
118 
119 /*
120  * meta_back_conndn_dup
121  *
122  * returns -1 in case a duplicate struct metaconn has been inserted;
123  * used by avl stuff
124  */
125 int
meta_back_conndn_dup(void * c1,void * c2)126 meta_back_conndn_dup(
127           void *c1,
128           void *c2 )
129 {
130           metaconn_t          *mc1 = ( metaconn_t * )c1;
131           metaconn_t          *mc2 = ( metaconn_t * )c2;
132 
133           /* Cannot have more than one shared session with same DN */
134           if ( mc1->mc_conn == mc2->mc_conn &&
135                     dn_match( &mc1->mc_local_ndn, &mc2->mc_local_ndn ) )
136           {
137                     return -1;
138           }
139 
140           return 0;
141 }
142 
143 /*
144  * Debug stuff (got it from libavl)
145  */
146 #if META_BACK_PRINT_CONNTREE > 0
147 static void
meta_back_print(metaconn_t * mc,char * avlstr)148 meta_back_print( metaconn_t *mc, char *avlstr )
149 {
150           int       i;
151 
152           fputs( "targets=[", stderr );
153           for ( i = 0; i < mc->mc_info->mi_ntargets; i++ ) {
154                     fputc( mc->mc_conns[ i ].msc_ld ? '*' : 'o', stderr);
155           }
156           fputc( ']', stderr );
157 
158           fprintf( stderr, " mc=%p local=\"%s\" conn=%p refcnt=%d%s %s\n",
159                     (void *)mc,
160                     mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "",
161                     (void *)mc->mc_conn,
162                     mc->mc_refcnt,
163                     LDAP_BACK_CONN_TAINTED( mc ) ? " tainted" : "",
164                     avlstr );
165 }
166 
167 static void
meta_back_ravl_print(TAvlnode * root,int depth)168 meta_back_ravl_print( TAvlnode *root, int depth )
169 {
170           int       i;
171 
172           if ( root == 0 ) {
173                     return;
174           }
175 
176           meta_back_ravl_print( root->avl_right, depth + 1 );
177 
178           for ( i = 0; i < depth; i++ ) {
179                     fprintf( stderr, "-" );
180           }
181           fputc( ' ', stderr );
182 
183           meta_back_print( (metaconn_t *)root->avl_data,
184                     avl_bf2str( root->avl_bf ) );
185 
186           meta_back_ravl_print( root->avl_left, depth + 1 );
187 }
188 
189 /* NOTE: duplicate from back-ldap/bind.c */
190 static char* priv2str[] = {
191           "privileged",
192           "privileged/TLS",
193           "anonymous",
194           "anonymous/TLS",
195           "bind",
196           "bind/TLS",
197           NULL
198 };
199 
200 void
meta_back_print_conntree(metainfo_t * mi,char * msg)201 meta_back_print_conntree( metainfo_t *mi, char *msg )
202 {
203           int       c;
204 
205           fprintf( stderr, "========> %s\n", msg );
206 
207           for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
208                     int                 i = 0;
209                     metaconn_t          *mc;
210 
211                     fprintf( stderr, "  %s[%d]\n", priv2str[ c ], mi->mi_conn_priv[ c ].mic_num );
212 
213                     LDAP_TAILQ_FOREACH( mc, &mi->mi_conn_priv[ c ].mic_priv, mc_q )
214                     {
215                               fprintf( stderr, "    [%d] ", i );
216                               meta_back_print( mc, "" );
217                               i++;
218                     }
219           }
220 
221           if ( mi->mi_conninfo.lai_tree == NULL ) {
222                     fprintf( stderr, "\t(empty)\n" );
223 
224           } else {
225                     meta_back_ravl_print( mi->mi_conninfo.lai_tree, 0 );
226           }
227 
228           fprintf( stderr, "<======== %s\n", msg );
229 }
230 #endif /* META_BACK_PRINT_CONNTREE */
231 /*
232  * End of debug stuff
233  */
234 
235 /*
236  * metaconn_alloc
237  *
238  * Allocates a connection structure, making room for all the referenced targets
239  */
240 static metaconn_t *
metaconn_alloc(Operation * op)241 metaconn_alloc(
242           Operation                     *op )
243 {
244           metainfo_t          *mi = ( metainfo_t * )op->o_bd->be_private;
245           metaconn_t          *mc;
246           int                 ntargets = mi->mi_ntargets;
247 
248           assert( ntargets > 0 );
249 
250           /* malloc all in one */
251           mc = ( metaconn_t * )ch_calloc( 1, sizeof( metaconn_t )
252                     + sizeof( metasingleconn_t ) * ( ntargets - 1 ) );
253           if ( mc == NULL ) {
254                     return NULL;
255           }
256 
257           mc->mc_info = mi;
258 
259           mc->mc_authz_target = META_BOUND_NONE;
260           mc->mc_refcnt = 1;
261 
262           return mc;
263 }
264 
265 /*
266  * meta_back_init_one_conn
267  *
268  * Initializes one connection
269  */
270 int
meta_back_init_one_conn(Operation * op,SlapReply * rs,metaconn_t * mc,int candidate,int ispriv,ldap_back_send_t sendok,int dolock)271 meta_back_init_one_conn(
272           Operation           *op,
273           SlapReply           *rs,
274           metaconn_t                    *mc,
275           int                           candidate,
276           int                           ispriv,
277           ldap_back_send_t    sendok,
278           int                           dolock )
279 {
280           metainfo_t                    *mi = ( metainfo_t * )op->o_bd->be_private;
281           metatarget_t                  *mt = mi->mi_targets[ candidate ];
282           metasingleconn_t    *msc = &mc->mc_conns[ candidate ];
283           int                           version;
284           dncookie            dc;
285           int                           isauthz = ( candidate == mc->mc_authz_target );
286           int                           do_return = 0;
287 #ifdef HAVE_TLS
288           int                           is_ldaps = 0;
289           int                           do_start_tls = 0;
290 #endif /* HAVE_TLS */
291 
292           /* if the server is quarantined, and
293            * - the current interval did not expire yet, or
294            * - no more retries should occur,
295            * don't return the connection */
296           if ( mt->mt_isquarantined ) {
297                     slap_retry_info_t   *ri = &mt->mt_quarantine;
298                     int                           dont_retry = 0;
299 
300                     if ( mt->mt_quarantine.ri_interval ) {
301                               ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
302                               dont_retry = ( mt->mt_isquarantined > LDAP_BACK_FQ_NO );
303                               if ( dont_retry ) {
304                                         dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
305                                                   || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
306                                         if ( !dont_retry ) {
307                                                   Debug(LDAP_DEBUG_ANY,
308                                                         "%s meta_back_init_one_conn[%d]: quarantine " "retry block #%d try #%d.\n",
309                                                         op->o_log_prefix,
310                                                         candidate, ri->ri_idx,
311                                                         ri->ri_count );
312 
313                                                   mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
314                                         }
315 
316                               }
317                               ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
318                     }
319 
320                     if ( dont_retry ) {
321                               rs->sr_err = LDAP_UNAVAILABLE;
322                               if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
323                                         rs->sr_text = "Target is quarantined";
324                                         send_ldap_result( op, rs );
325                               }
326                               return rs->sr_err;
327                     }
328           }
329 
330 retry_lock:;
331           if ( dolock ) {
332                     ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
333           }
334 
335           /*
336            * Already init'ed
337            */
338           if ( LDAP_BACK_CONN_ISBOUND( msc )
339                     || LDAP_BACK_CONN_ISANON( msc ) )
340           {
341                     assert( msc->msc_ld != NULL );
342                     rs->sr_err = LDAP_SUCCESS;
343                     do_return = 1;
344 
345           } else if ( META_BACK_CONN_CREATING( msc )
346                     || LDAP_BACK_CONN_BINDING( msc ) )
347           {
348                     if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
349                               if ( dolock ) {
350                                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
351                               }
352 
353                               ldap_pvt_thread_yield();
354                               goto retry_lock;
355                     }
356 
357                     /* sounds more appropriate */
358                     rs->sr_err = LDAP_BUSY;
359                     rs->sr_text = "No connections to target are available";
360                     do_return = 1;
361 
362           } else if ( META_BACK_CONN_INITED( msc ) ) {
363                     assert( msc->msc_ld != NULL );
364                     rs->sr_err = LDAP_SUCCESS;
365                     do_return = 1;
366 
367           } else {
368                     /*
369                      * creating...
370                      */
371                     META_BACK_CONN_CREATING_SET( msc );
372           }
373 
374           if ( dolock ) {
375                     ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
376           }
377 
378           if ( do_return ) {
379                     if ( rs->sr_err != LDAP_SUCCESS
380                               && op->o_conn
381                               && ( sendok & LDAP_BACK_SENDERR ) )
382                     {
383                               send_ldap_result( op, rs );
384                     }
385 
386                     return rs->sr_err;
387           }
388 
389           assert( msc->msc_ld == NULL );
390 
391           /*
392            * Attempts to initialize the connection to the target ds
393            */
394           ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
395           rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
396 #ifdef HAVE_TLS
397           is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
398 #endif /* HAVE_TLS */
399           ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
400           if ( rs->sr_err != LDAP_SUCCESS ) {
401                     goto error_return;
402           }
403 
404           /*
405            * Set LDAP version. This will always succeed: If the client
406            * bound with a particular version, then so can we.
407            */
408           if ( mt->mt_version != 0 ) {
409                     version = mt->mt_version;
410 
411           } else if ( op->o_conn->c_protocol != 0 ) {
412                     version = op->o_conn->c_protocol;
413 
414           } else {
415                     version = LDAP_VERSION3;
416           }
417           ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &version );
418           ldap_set_urllist_proc( msc->msc_ld, mt->mt_urllist_f, mt->mt_urllist_p );
419 
420           /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
421           ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS,
422                     META_BACK_TGT_CHASE_REFERRALS( mt ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
423 
424           slap_client_keepalive(msc->msc_ld, &mt->mt_tls.sb_keepalive);
425 
426           if ( mt->mt_tls.sb_tcp_user_timeout > 0 ) {
427                     ldap_set_option( msc->msc_ld, LDAP_OPT_TCP_USER_TIMEOUT,
428                                         &mt->mt_tls.sb_tcp_user_timeout );
429           }
430 
431 
432 
433 #ifdef HAVE_TLS
434           {
435                     slap_bindconf *sb = NULL;
436 
437                     if ( ispriv ) {
438                               sb = &mt->mt_idassert.si_bc;
439                     } else {
440                               sb = &mt->mt_tls;
441                     }
442 
443                     bindconf_tls_set( sb, msc->msc_ld );
444 
445                     if ( !is_ldaps ) {
446                               if ( META_BACK_TGT_USE_TLS( mt )
447                                         || ( op->o_conn->c_is_tls && META_BACK_TGT_PROPAGATE_TLS( mt ) ) )
448                               {
449                                         do_start_tls = 1;
450                               }
451                     }
452           }
453 
454           /* start TLS ("tls [try-]{start|propagate}" statement) */
455           if ( do_start_tls ) {
456 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
457                     /*
458                      * use asynchronous StartTLS; in case, chase referral
459                      * FIXME: OpenLDAP does not return referral on StartTLS yet
460                      */
461                     int                 msgid;
462 
463                     rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
464                     if ( rs->sr_err == LDAP_SUCCESS ) {
465                               LDAPMessage         *res = NULL;
466                               int                 rc, nretries = mt->mt_nretries;
467                               struct timeval      tv;
468 
469                               LDAP_BACK_TV_SET( &tv );
470 
471 retry:;
472                               rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
473                               switch ( rc ) {
474                               case -1:
475                                         rs->sr_err = LDAP_UNAVAILABLE;
476                                         rs->sr_text = "Remote server down";
477                                         break;
478 
479                               case 0:
480                                         if ( nretries != 0 ) {
481                                                   if ( nretries > 0 ) {
482                                                             nretries--;
483                                                   }
484                                                   LDAP_BACK_TV_SET( &tv );
485                                                   goto retry;
486                                         }
487                                         rs->sr_err = LDAP_OTHER;
488                                         rs->sr_text = "Timeout, no more retries";
489                                         break;
490 
491                               default:
492                                         /* only touch when activity actually took place... */
493                                         if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
494                                                   msc->msc_time = op->o_time;
495                                         }
496                                         break;
497                               }
498 
499                               if ( rc == LDAP_RES_EXTENDED ) {
500                                         struct berval       *data = NULL;
501 
502                                         /* NOTE: right now, data is unused, so don't get it */
503                                         rs->sr_err = ldap_parse_extended_result( msc->msc_ld,
504                                                   res, NULL, NULL /* &data */ , 0 );
505                                         if ( rs->sr_err == LDAP_SUCCESS ) {
506                                                   int                 err;
507 
508                                                   /* FIXME: matched? referrals? response controls? */
509                                                   rs->sr_err = ldap_parse_result( msc->msc_ld,
510                                                             res, &err, NULL, NULL, NULL, NULL, 1 );
511                                                   res = NULL;
512 
513                                                   if ( rs->sr_err == LDAP_SUCCESS ) {
514                                                             rs->sr_err = err;
515                                                   }
516                                                   rs->sr_err = slap_map_api2result( rs );
517 
518                                                   /* FIXME: in case a referral
519                                                    * is returned, should we try
520                                                    * using it instead of the
521                                                    * configured URI? */
522                                                   if ( rs->sr_err == LDAP_SUCCESS ) {
523                                                             rs->sr_err = ldap_install_tls( msc->msc_ld );
524 
525                                                   } else if ( rs->sr_err == LDAP_REFERRAL ) {
526                                                             /* FIXME: LDAP_OPERATIONS_ERROR? */
527                                                             rs->sr_err = LDAP_OTHER;
528                                                             rs->sr_text = "Unwilling to chase referral "
529                                                                       "returned by Start TLS exop";
530                                                   }
531 
532                                                   if ( data ) {
533                                                             ber_bvfree( data );
534                                                   }
535                                         }
536 
537                               } else {
538                                         rs->sr_err = LDAP_OTHER;
539                                         rs->sr_text = "Unknown response to StartTLS request :"
540                                                   " an ExtendedResponse is expected";
541                               }
542 
543                               if ( res != NULL ) {
544                                         ldap_msgfree( res );
545                               }
546                     }
547 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
548                     /*
549                      * use synchronous StartTLS
550                      */
551                     rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
552 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
553 
554                     /* if StartTLS is requested, only attempt it if the URL
555                      * is not "ldaps://"; this may occur not only in case
556                      * of misconfiguration, but also when used in the chain
557                      * overlay, where the "uri" can be parsed out of a referral */
558                     if ( rs->sr_err == LDAP_SERVER_DOWN
559                               || ( rs->sr_err != LDAP_SUCCESS
560                                         && META_BACK_TGT_TLS_CRITICAL( mt ) ) )
561                     {
562 
563 #ifdef DEBUG_205
564                               Debug( LDAP_DEBUG_ANY,
565                                         "### %s meta_back_init_one_conn(TLS) "
566                                         "ldap_unbind_ext[%d] ld=%p\n",
567                                         op->o_log_prefix, candidate,
568                                         (void *)msc->msc_ld );
569 #endif /* DEBUG_205 */
570 
571                               /* need to trash a failed Start TLS */
572                               meta_clear_one_candidate( op, mc, candidate );
573                               goto error_return;
574                     }
575           }
576 #endif /* HAVE_TLS */
577 
578           /*
579            * Set the network timeout if set
580            */
581           if ( mt->mt_network_timeout != 0 ) {
582                     struct timeval      network_timeout;
583 
584                     network_timeout.tv_usec = 0;
585                     network_timeout.tv_sec = mt->mt_network_timeout;
586 
587                     ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
588                                         (void *)&network_timeout );
589           }
590 
591           /*
592            * If the connection DN is not null, an attempt to rewrite it is made
593            */
594 
595           if ( ispriv ) {
596                     if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
597                               ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
598                               if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
599                                         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
600                                                   memset( msc->msc_cred.bv_val, 0,
601                                                             msc->msc_cred.bv_len );
602                                         }
603                                         ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
604                               }
605                               LDAP_BACK_CONN_ISIDASSERT_SET( msc );
606 
607                     } else {
608                               ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
609                     }
610 
611           } else {
612                     if ( !BER_BVISNULL( &msc->msc_cred ) ) {
613                               memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
614                               ber_memfree_x( msc->msc_cred.bv_val, NULL );
615                               BER_BVZERO( &msc->msc_cred );
616                     }
617                     if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
618                               ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
619                               BER_BVZERO( &msc->msc_bound_ndn );
620                     }
621                     if ( !BER_BVISEMPTY( &op->o_ndn )
622                               && SLAP_IS_AUTHZ_BACKEND( op )
623                               && isauthz )
624                     {
625                               dc.target = mt;
626                               dc.conn = op->o_conn;
627                               dc.rs = rs;
628                               dc.ctx = "bindDN";
629 
630                               /*
631                                * Rewrite the bind dn if needed
632                                */
633                               if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
634                                                             &msc->msc_bound_ndn ) )
635                               {
636 
637 #ifdef DEBUG_205
638                                         Debug( LDAP_DEBUG_ANY,
639                                                   "### %s meta_back_init_one_conn(rewrite) "
640                                                   "ldap_unbind_ext[%d] ld=%p\n",
641                                                   op->o_log_prefix, candidate,
642                                                   (void *)msc->msc_ld );
643 #endif /* DEBUG_205 */
644 
645                                         /* need to trash a connection not fully established */
646                                         meta_clear_one_candidate( op, mc, candidate );
647                                         goto error_return;
648                               }
649 
650                               /* copy the DN if needed */
651                               if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
652                                         ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
653                               }
654 
655                               assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
656 
657                     } else {
658                               ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
659                     }
660           }
661 
662           assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
663 
664 error_return:;
665           if ( dolock ) {
666                     ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
667           }
668           META_BACK_CONN_CREATING_CLEAR( msc );
669           if ( rs->sr_err == LDAP_SUCCESS ) {
670                     /*
671                      * Sets a cookie for the rewrite session
672                      */
673                     ( void )rewrite_session_init( mt->mt_rwmap.rwm_rw, op->o_conn );
674                     META_BACK_CONN_INITED_SET( msc );
675           }
676           if ( dolock ) {
677                     ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
678           }
679 
680           if ( rs->sr_err != LDAP_SUCCESS ) {
681                     /* Get the error message and print it in TRACE mode */
682                     if ( LogTest( LDAP_DEBUG_TRACE ) ) {
683                               Log( LDAP_DEBUG_TRACE, ldap_syslog_level, "%s: meta_back_init_one_conn[%d] failed err=%d text=%s\n",
684                                         op->o_log_prefix, candidate, rs->sr_err, rs->sr_text );
685                     }
686 
687                     rs->sr_err = slap_map_api2result( rs );
688                     if ( sendok & LDAP_BACK_SENDERR ) {
689                               send_ldap_result( op, rs );
690                     }
691           }
692 
693           return rs->sr_err;
694 }
695 
696 /*
697  * meta_back_retry
698  *
699  * Retries one connection
700  */
701 int
meta_back_retry(Operation * op,SlapReply * rs,metaconn_t ** mcp,int candidate,ldap_back_send_t sendok)702 meta_back_retry(
703           Operation           *op,
704           SlapReply           *rs,
705           metaconn_t                    **mcp,
706           int                           candidate,
707           ldap_back_send_t    sendok )
708 {
709           metainfo_t                    *mi = ( metainfo_t * )op->o_bd->be_private;
710           metatarget_t                  *mt = mi->mi_targets[ candidate ];
711           metaconn_t                    *mc = *mcp;
712           metasingleconn_t    *msc = &mc->mc_conns[ candidate ];
713           int                           rc = LDAP_UNAVAILABLE,
714                                         binding,
715                                         quarantine = 1;
716 
717           ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
718 
719           assert( !META_BACK_CONN_CREATING( msc ) );
720           binding = LDAP_BACK_CONN_BINDING( msc );
721           LDAP_BACK_CONN_BINDING_CLEAR( msc );
722 
723           assert( mc->mc_refcnt > 0 );
724           if ( mc->mc_refcnt == 1 ) {
725                     struct berval save_cred;
726 
727                     if ( LogTest( LDAP_DEBUG_ANY ) ) {
728                               /* this lock is required; however,
729                                * it's invoked only when logging is on */
730                               ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
731                               Debug(LDAP_DEBUG_ANY,
732                                     "%s meta_back_retry[%d]: retrying URI=\"%s\" DN=\"%s\".\n",
733                                     op->o_log_prefix, candidate, mt->mt_uri,
734                                     BER_BVISNULL(&msc->msc_bound_ndn) ? "" : msc->msc_bound_ndn.bv_val );
735                               ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
736                     }
737 
738                     /* save credentials, if any, for later use;
739                      * meta_clear_one_candidate() would free them */
740                     save_cred = msc->msc_cred;
741                     BER_BVZERO( &msc->msc_cred );
742 
743                     meta_clear_one_candidate( op, mc, candidate );
744                     LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
745 
746                     ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
747 
748                     /* mc here must be the regular mc, reset and ready for init */
749                     rc = meta_back_init_one_conn( op, rs, mc, candidate,
750                               LDAP_BACK_CONN_ISPRIV( mc ), sendok, 0 );
751 
752                     /* restore credentials, if any and if needed;
753                      * meta_back_init_one_conn() restores msc_bound_ndn, if any;
754                      * if no msc_bound_ndn is restored, destroy credentials */
755                     if ( !BER_BVISNULL( &msc->msc_bound_ndn )
756                               && BER_BVISNULL( &msc->msc_cred ) )
757                     {
758                               msc->msc_cred = save_cred;
759 
760                     } else if ( !BER_BVISNULL( &save_cred ) ) {
761                               memset( save_cred.bv_val, 0, save_cred.bv_len );
762                               ber_memfree_x( save_cred.bv_val, NULL );
763                     }
764 
765                     /* restore the "binding" flag, in case */
766                     if ( binding ) {
767                               LDAP_BACK_CONN_BINDING_SET( msc );
768                     }
769 
770                     if ( rc == LDAP_SUCCESS ) {
771                               quarantine = 0;
772                               LDAP_BACK_CONN_BINDING_SET( msc ); binding = 1;
773                               rc = meta_back_single_dobind( op, rs, mcp, candidate,
774                                         sendok, mt->mt_nretries, 0 );
775 
776                               Debug( LDAP_DEBUG_ANY,
777                                         "%s meta_back_retry[%d]: "
778                                         "meta_back_single_dobind=%d\n",
779                                         op->o_log_prefix, candidate, rc );
780                               if ( rc == LDAP_SUCCESS ) {
781                                         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) &&
782                                                   !BER_BVISEMPTY( &msc->msc_bound_ndn ) )
783                                         {
784                                                   LDAP_BACK_CONN_ISBOUND_SET( msc );
785 
786                                         } else {
787                                                   LDAP_BACK_CONN_ISANON_SET( msc );
788                                         }
789 
790                                         /* when bound, dispose of the "binding" flag */
791                                         if ( binding ) {
792                                                   LDAP_BACK_CONN_BINDING_CLEAR( msc );
793                                         }
794                               }
795                     }
796 
797 #if 0     /* ITS#7591, following stmt drops needed result msgs */
798                     /* don't send twice */
799                     sendok &= ~LDAP_BACK_SENDERR;
800 #endif
801           }
802 
803           if ( rc != LDAP_SUCCESS ) {
804                     SlapReply           *candidates = meta_back_candidates_get( op );
805 
806                     candidates[ candidate ].sr_err = rc;
807 
808                     if ( *mcp != NULL ) {
809                               if ( mc->mc_refcnt == 1 ) {
810                                         if ( binding ) {
811                                                   LDAP_BACK_CONN_BINDING_CLEAR( msc );
812                                         }
813                                         (void)meta_clear_one_candidate( op, mc, candidate );
814                               }
815 
816                               LDAP_BACK_CONN_TAINTED_SET( mc );
817                               /* only release if mandatory; otherwise
818                                * let the caller do what's best before
819                                * releasing */
820                               if ( META_BACK_ONERR_STOP( mi ) ) {
821                                         meta_back_release_conn_lock( mi, mc, 0 );
822                                         *mcp = NULL;
823 
824                               } else {
825 #if META_BACK_PRINT_CONNTREE > 0
826                                         meta_back_print_conntree( mi, ">>> meta_back_retry" );
827 #endif /* META_BACK_PRINT_CONNTREE */
828 
829                                         /* FIXME: could be done better, reworking meta_back_release_conn_lock() */
830                                         if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
831                                                   if ( mc->mc_q.tqe_prev != NULL ) {
832                                                             assert( LDAP_BACK_CONN_CACHED( mc ) );
833                                                             assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
834                                                             LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
835                                                                       mc, mc_q );
836                                                             mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
837                                                             LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
838 
839                                                   } else {
840                                                             assert( !LDAP_BACK_CONN_CACHED( mc ) );
841                                                   }
842 
843                                         } else {
844                                                   /* FIXME: check if in tree, for consistency? */
845                                                   (void)ldap_tavl_delete( &mi->mi_conninfo.lai_tree,
846                                                             ( caddr_t )mc, meta_back_conndnmc_cmp );
847                                         }
848                                         LDAP_BACK_CONN_CACHED_CLEAR( mc );
849 
850 #if META_BACK_PRINT_CONNTREE > 0
851                                         meta_back_print_conntree( mi, "<<< meta_back_retry" );
852 #endif /* META_BACK_PRINT_CONNTREE */
853                               }
854                     }
855 
856                     if ( sendok & LDAP_BACK_SENDERR ) {
857                               rs->sr_err = rc;
858                               rs->sr_text = "Unable to retry";
859                               send_ldap_result( op, rs );
860                     }
861           }
862 
863           if ( quarantine && META_BACK_TGT_QUARANTINE( mt ) ) {
864                     meta_back_quarantine( op, rs, candidate );
865           }
866 
867           ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
868 
869           return rc == LDAP_SUCCESS ? 1 : 0;
870 }
871 
872 /*
873  * callback for unique candidate selection
874  */
875 static int
meta_back_conn_cb(Operation * op,SlapReply * rs)876 meta_back_conn_cb( Operation *op, SlapReply *rs )
877 {
878           assert( op->o_tag == LDAP_REQ_SEARCH );
879 
880           switch ( rs->sr_type ) {
881           case REP_SEARCH:
882                     ((long *)op->o_callback->sc_private)[0] = (long)op->o_private;
883                     break;
884 
885           case REP_SEARCHREF:
886           case REP_RESULT:
887                     break;
888 
889           default:
890                     return rs->sr_err;
891           }
892 
893           return 0;
894 }
895 
896 
897 static int
meta_back_get_candidate(Operation * op,SlapReply * rs,struct berval * ndn)898 meta_back_get_candidate(
899           Operation *op,
900           SlapReply *rs,
901           struct berval       *ndn )
902 {
903           metainfo_t          *mi = ( metainfo_t * )op->o_bd->be_private;
904           long                candidate;
905 
906           /*
907            * tries to get a unique candidate
908            * (takes care of default target)
909            */
910           candidate = meta_back_select_unique_candidate( mi, ndn );
911 
912           /*
913            * if any is found, inits the connection
914            */
915           if ( candidate == META_TARGET_NONE ) {
916                     rs->sr_err = LDAP_NO_SUCH_OBJECT;
917                     rs->sr_text = "No suitable candidate target found";
918 
919           } else if ( candidate == META_TARGET_MULTIPLE ) {
920                     Operation op2 = *op;
921                     SlapReply rs2 = { REP_RESULT };
922                     slap_callback       cb2 = { 0 };
923                     int                 rc;
924 
925                     /* try to get a unique match for the request ndn
926                      * among the multiple candidates available */
927                     op2.o_tag = LDAP_REQ_SEARCH;
928                     op2.o_req_dn = *ndn;
929                     op2.o_req_ndn = *ndn;
930                     op2.ors_scope = LDAP_SCOPE_BASE;
931                     op2.ors_deref = LDAP_DEREF_NEVER;
932                     op2.ors_attrs = slap_anlist_no_attrs;
933                     op2.ors_attrsonly = 0;
934                     op2.ors_limit = NULL;
935                     op2.ors_slimit = 1;
936                     op2.ors_tlimit = SLAP_NO_LIMIT;
937 
938                     op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
939                     op2.ors_filterstr = *slap_filterstr_objectClass_pres;
940 
941                     op2.o_callback = &cb2;
942                     cb2.sc_response = meta_back_conn_cb;
943                     cb2.sc_private = (void *)&candidate;
944 
945                     rc = op->o_bd->be_search( &op2, &rs2 );
946 
947                     switch ( rs2.sr_err ) {
948                     case LDAP_SUCCESS:
949                     default:
950                               rs->sr_err = rs2.sr_err;
951                               break;
952 
953                     case LDAP_SIZELIMIT_EXCEEDED:
954                               /* if multiple candidates can serve the operation,
955                                * and a default target is defined, and it is
956                                * a candidate, try using it (FIXME: YMMV) */
957                               if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
958                                         && meta_back_is_candidate( mi->mi_targets[ mi->mi_defaulttarget ],
959                                                             ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
960                               {
961                                         candidate = mi->mi_defaulttarget;
962                                         rs->sr_err = LDAP_SUCCESS;
963                                         rs->sr_text = NULL;
964 
965                               } else {
966                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
967                                         rs->sr_text = "Unable to select unique candidate target";
968                               }
969                               break;
970                     }
971 
972           } else {
973                     rs->sr_err = LDAP_SUCCESS;
974           }
975 
976           return candidate;
977 }
978 
979 static void         *meta_back_candidates_dummy;
980 
981 static void
meta_back_candidates_keyfree(void * key,void * data)982 meta_back_candidates_keyfree(
983           void                *key,
984           void                *data )
985 {
986           metacandidates_t    *mc = (metacandidates_t *)data;
987 
988           ber_memfree_x( mc->mc_candidates, NULL );
989           ber_memfree_x( data, NULL );
990 }
991 
992 SlapReply *
meta_back_candidates_get(Operation * op)993 meta_back_candidates_get( Operation *op )
994 {
995           metainfo_t                    *mi = ( metainfo_t * )op->o_bd->be_private;
996           metacandidates_t    *mc;
997 
998           if ( op->o_threadctx ) {
999                     void                *data = NULL;
1000 
1001                     ldap_pvt_thread_pool_getkey( op->o_threadctx,
1002                                         &meta_back_candidates_dummy, &data, NULL );
1003                     mc = (metacandidates_t *)data;
1004 
1005           } else {
1006                     mc = mi->mi_candidates;
1007           }
1008 
1009           if ( mc == NULL ) {
1010                     mc = ch_calloc( sizeof( metacandidates_t ), 1 );
1011                     mc->mc_ntargets = mi->mi_ntargets;
1012                     mc->mc_candidates = ch_calloc( sizeof( SlapReply ), mc->mc_ntargets );
1013                     if ( op->o_threadctx ) {
1014                               void                *data = NULL;
1015 
1016                               data = (void *)mc;
1017                               ldap_pvt_thread_pool_setkey( op->o_threadctx,
1018                                                   &meta_back_candidates_dummy, data,
1019                                                   meta_back_candidates_keyfree,
1020                                                   NULL, NULL );
1021 
1022                     } else {
1023                               mi->mi_candidates = mc;
1024                     }
1025 
1026           } else if ( mc->mc_ntargets < mi->mi_ntargets ) {
1027                     /* NOTE: in the future, may want to allow back-config
1028                      * to add/remove targets from back-meta... */
1029                     mc->mc_candidates = ch_realloc( mc->mc_candidates,
1030                                         sizeof( SlapReply ) * mi->mi_ntargets );
1031                     memset( &mc->mc_candidates[ mc->mc_ntargets ], 0,
1032                               sizeof( SlapReply ) * ( mi->mi_ntargets - mc->mc_ntargets ) );
1033                     mc->mc_ntargets = mi->mi_ntargets;
1034           }
1035 
1036           return mc->mc_candidates;
1037 }
1038 
1039 /*
1040  * meta_back_getconn
1041  *
1042  * Prepares the connection structure
1043  *
1044  * RATIONALE:
1045  *
1046  * - determine what DN is being requested:
1047  *
1048  *        op        requires candidate  checks
1049  *
1050  *        add       unique                        parent of o_req_ndn
1051  *        bind      unique^*[/all]                o_req_ndn [no check]
1052  *        compare   unique^+            o_req_ndn
1053  *        delete    unique                        o_req_ndn
1054  *        modify    unique                        o_req_ndn
1055  *        search    any                           o_req_ndn
1056  *        modrdn    unique[, unique]    o_req_ndn[, orr_nnewSup]
1057  *
1058  * - for ops that require the candidate to be unique, in case of multiple
1059  *   occurrences an internal search with sizeLimit=1 is performed
1060  *   if a unique candidate can actually be determined.  If none is found,
1061  *   the operation aborts; if multiple are found, the default target
1062  *   is used if defined and candidate; otherwise the operation aborts.
1063  *
1064  * *^note: actually, the bind operation is handled much like a search;
1065  *   i.e. the bind is broadcast to all candidate targets.
1066  *
1067  * +^note: actually, the compare operation is handled much like a search;
1068  *   i.e. the compare is broadcast to all candidate targets, while checking
1069  *   that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
1070  *   returned.
1071  */
1072 metaconn_t *
meta_back_getconn(Operation * op,SlapReply * rs,int * candidate,ldap_back_send_t sendok)1073 meta_back_getconn(
1074           Operation                     *op,
1075           SlapReply           *rs,
1076           int                           *candidate,
1077           ldap_back_send_t    sendok )
1078 {
1079           metainfo_t          *mi = ( metainfo_t * )op->o_bd->be_private;
1080           metaconn_t          *mc = NULL,
1081                               mc_curr = {{ 0 }};
1082           int                 cached = META_TARGET_NONE,
1083                               i = META_TARGET_NONE,
1084                               err = LDAP_SUCCESS,
1085                               new_conn = 0,
1086                               ncandidates = 0;
1087 
1088 
1089           meta_op_type        op_type = META_OP_REQUIRE_SINGLE;
1090           enum                {
1091                     META_DNTYPE_ENTRY,
1092                     META_DNTYPE_PARENT,
1093                     META_DNTYPE_NEWPARENT
1094           }                   dn_type = META_DNTYPE_ENTRY;
1095           struct berval       ndn = op->o_req_ndn,
1096                               pndn;
1097 
1098           SlapReply *candidates = meta_back_candidates_get( op );
1099 
1100           /* Internal searches are privileged and shared. So is root. */
1101           if ( ( !BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ALWAYS( mi ) )
1102                     || ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ANON( mi ) )
1103                     || op->o_do_not_cache || be_isroot( op ) )
1104           {
1105                     LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
1106                     mc_curr.mc_local_ndn = op->o_bd->be_rootndn;
1107                     LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op );
1108 
1109           } else if ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_NOANON( mi ) )
1110           {
1111                     LDAP_BACK_CONN_ISANON_SET( &mc_curr );
1112                     BER_BVSTR( &mc_curr.mc_local_ndn, "" );
1113                     LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
1114 
1115           } else {
1116                     mc_curr.mc_local_ndn = op->o_ndn;
1117 
1118                     /* Explicit binds must not be shared */
1119                     if ( !BER_BVISEMPTY( &op->o_ndn )
1120                               || op->o_tag == LDAP_REQ_BIND
1121                               || SLAP_IS_AUTHZ_BACKEND( op ) )
1122                     {
1123                               mc_curr.mc_conn = op->o_conn;
1124 
1125                     } else {
1126                               LDAP_BACK_CONN_ISANON_SET( &mc_curr );
1127                               LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
1128                     }
1129           }
1130 
1131           /* Explicit Bind requests always get their own conn */
1132           if ( sendok & LDAP_BACK_BINDING ) {
1133                     mc_curr.mc_conn = op->o_conn;
1134 
1135           } else {
1136                     /* Searches for a metaconn in the avl tree */
1137 retry_lock:;
1138                     ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1139                     if ( LDAP_BACK_PCONN_ISPRIV( &mc_curr ) ) {
1140                               /* lookup a conn that's not binding */
1141                               LDAP_TAILQ_FOREACH( mc,
1142                                         &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv,
1143                                         mc_q )
1144                               {
1145                                         if ( !LDAP_BACK_CONN_BINDING( mc ) && mc->mc_refcnt == 0 ) {
1146                                                   break;
1147                                         }
1148                               }
1149 
1150                               if ( mc != NULL ) {
1151                                         /* move to tail of queue */
1152                                         if ( mc != LDAP_TAILQ_LAST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
1153                                                   mc_conn_priv_q ) )
1154                                         {
1155                                                   LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
1156                                                             mc, mc_q );
1157                                                   LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
1158                                                   LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
1159                                                             mc, mc_q );
1160                                         }
1161 
1162                               } else if ( !LDAP_BACK_USE_TEMPORARIES( mi )
1163                                         && mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_num == mi->mi_conn_priv_max )
1164                               {
1165                                         mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv );
1166                               }
1167 
1168 
1169                     } else {
1170                               mc = (metaconn_t *)ldap_tavl_find( mi->mi_conninfo.lai_tree,
1171                                         (caddr_t)&mc_curr, meta_back_conndn_cmp );
1172                     }
1173 
1174                     if ( mc ) {
1175                               /* catch taint errors */
1176                               assert( !LDAP_BACK_CONN_TAINTED( mc ) );
1177 
1178                               /* Don't reuse connections while they're still binding
1179                                * NOTE: only makes sense for binds */
1180                               if ( LDAP_BACK_CONN_BINDING( mc ) ) {
1181                                         if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
1182                                                   ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1183 
1184                                                   ldap_pvt_thread_yield();
1185                                                   goto retry_lock;
1186                                         }
1187 
1188                                         /* release conn, and create a temporary */
1189                                         mc = NULL;
1190 
1191                               } else {
1192                                         if ( mc->mc_refcnt == 0 && (( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl )
1193                                                   || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout )) )
1194                                         {
1195 #if META_BACK_PRINT_CONNTREE > 0
1196                                                   meta_back_print_conntree( mi,
1197                                                             ">>> meta_back_getconn(expired)" );
1198 #endif /* META_BACK_PRINT_CONNTREE */
1199 
1200                                                   /* don't let anyone else use this expired connection */
1201                                                   if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
1202                                                             if ( mc->mc_q.tqe_prev != NULL ) {
1203                                                                       assert( LDAP_BACK_CONN_CACHED( mc ) );
1204                                                                       assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
1205                                                                       LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
1206                                                                                 mc, mc_q );
1207                                                                       mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
1208                                                                       LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
1209 
1210                                                             } else {
1211                                                                       assert( !LDAP_BACK_CONN_CACHED( mc ) );
1212                                                             }
1213 
1214                                                   } else {
1215                                                             (void)ldap_tavl_delete( &mi->mi_conninfo.lai_tree,
1216                                                                       (caddr_t)mc, meta_back_conndnmc_cmp );
1217                                                   }
1218 
1219 #if META_BACK_PRINT_CONNTREE > 0
1220                                                   meta_back_print_conntree( mi,
1221                                                             "<<< meta_back_getconn(expired)" );
1222 #endif /* META_BACK_PRINT_CONNTREE */
1223                                                   LDAP_BACK_CONN_TAINTED_SET( mc );
1224                                                   LDAP_BACK_CONN_CACHED_CLEAR( mc );
1225 
1226                                                   if ( LogTest( LDAP_DEBUG_TRACE ) ) {
1227                                                             char buf[STRLENOF("4294967295U") + 1] = { 0 };
1228                                                             mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
1229 
1230                                                             Debug( LDAP_DEBUG_TRACE,
1231                                                                       "%s meta_back_getconn: mc=%p conn=%s expired (tainted).\n",
1232                                                                       op->o_log_prefix, (void *)mc, buf );
1233                                                   }
1234                                         }
1235 
1236                                         mc->mc_refcnt++;
1237                               }
1238                     }
1239                     ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1240           }
1241 
1242           switch ( op->o_tag ) {
1243           case LDAP_REQ_ADD:
1244                     /* if we go to selection, the entry must not exist,
1245                      * and we must be able to resolve the parent */
1246                     dn_type = META_DNTYPE_PARENT;
1247                     dnParent( &ndn, &pndn );
1248                     break;
1249 
1250           case LDAP_REQ_MODRDN:
1251                     /* if nnewSuperior is not NULL, it must resolve
1252                      * to the same candidate as the req_ndn */
1253                     if ( op->orr_nnewSup ) {
1254                               dn_type = META_DNTYPE_NEWPARENT;
1255                     }
1256                     break;
1257 
1258           case LDAP_REQ_BIND:
1259                     /* if bound as rootdn, the backend must bind to all targets
1260                      * with the administrative identity
1261                      * (unless pseoudoroot-bind-defer is TRUE) */
1262                     if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
1263                               op_type = META_OP_REQUIRE_ALL;
1264                     }
1265                     break;
1266 
1267           case LDAP_REQ_COMPARE:
1268           case LDAP_REQ_DELETE:
1269           case LDAP_REQ_MODIFY:
1270                     /* just a unique candidate */
1271                     break;
1272 
1273           case LDAP_REQ_SEARCH:
1274                     /* allow multiple candidates for the searchBase */
1275                     op_type = META_OP_ALLOW_MULTIPLE;
1276                     break;
1277 
1278           default:
1279                     /* right now, just break (exop?) */
1280                     break;
1281           }
1282 
1283           /*
1284            * require all connections ...
1285            */
1286           if ( op_type == META_OP_REQUIRE_ALL ) {
1287 
1288                     /* Looks like we didn't get a bind. Open a new session... */
1289                     if ( mc == NULL ) {
1290                               assert( new_conn == 0 );
1291                               mc = metaconn_alloc( op );
1292                               mc->mc_conn = mc_curr.mc_conn;
1293                               ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
1294                               new_conn = 1;
1295                               if ( sendok & LDAP_BACK_BINDING ) {
1296                                         LDAP_BACK_CONN_BINDING_SET( mc );
1297                               }
1298                               if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
1299                                         LDAP_BACK_CONN_ISPRIV_SET( mc );
1300 
1301                               } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
1302                                         LDAP_BACK_CONN_ISANON_SET( mc );
1303                               }
1304 
1305                     } else if ( 0 ) {
1306                               /* TODO: if any of the connections is binding,
1307                                * release mc and create a new one */
1308                     }
1309 
1310                     for ( i = 0; i < mi->mi_ntargets; i++ ) {
1311                               /*
1312                                * The target is activated; if needed, it is
1313                                * also init'd
1314                                */
1315                               candidates[ i ].sr_err = meta_back_init_one_conn( op,
1316                                         rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
1317                                         LDAP_BACK_DONTSEND, !new_conn );
1318                               if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
1319                                         if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
1320                                                   LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
1321                                         }
1322                                         META_CANDIDATE_SET( &candidates[ i ] );
1323                                         ncandidates++;
1324 
1325                               } else {
1326 
1327                                         /*
1328                                          * FIXME: in case one target cannot
1329                                          * be init'd, should the other ones
1330                                          * be tried?
1331                                          */
1332                                         META_CANDIDATE_RESET( &candidates[ i ] );
1333                                         err = candidates[ i ].sr_err;
1334                                         continue;
1335                               }
1336                     }
1337 
1338                     if ( ncandidates == 0 ) {
1339                               if ( new_conn ) {
1340                                         mc->mc_refcnt = 0;
1341                                         meta_back_conn_free( mc );
1342 
1343                               } else {
1344                                         meta_back_release_conn( mi, mc );
1345                               }
1346 
1347                               rs->sr_err = LDAP_NO_SUCH_OBJECT;
1348                               rs->sr_text = "Unable to select valid candidates";
1349 
1350                               if ( sendok & LDAP_BACK_SENDERR ) {
1351                                         if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1352                                                   rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1353                                         }
1354                                         send_ldap_result( op, rs );
1355                                         rs->sr_matched = NULL;
1356                               }
1357 
1358                               return NULL;
1359                     }
1360 
1361                     goto done;
1362           }
1363 
1364           /*
1365            * looks in cache, if any
1366            */
1367           if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
1368                     cached = i = meta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
1369           }
1370 
1371           if ( op_type == META_OP_REQUIRE_SINGLE ) {
1372                     metatarget_t                  *mt = NULL;
1373                     metasingleconn_t    *msc = NULL;
1374 
1375                     int                           j;
1376 
1377                     for ( j = 0; j < mi->mi_ntargets; j++ ) {
1378                               META_CANDIDATE_RESET( &candidates[ j ] );
1379                     }
1380 
1381                     /*
1382                      * tries to get a unique candidate
1383                      * (takes care of default target)
1384                      */
1385                     if ( i == META_TARGET_NONE ) {
1386                               i = meta_back_get_candidate( op, rs, &ndn );
1387 
1388                               if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && dn_type == META_DNTYPE_PARENT ) {
1389                                         i = meta_back_get_candidate( op, rs, &pndn );
1390                               }
1391 
1392                               if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
1393                                         if ( mc != NULL ) {
1394                                                   meta_back_release_conn( mi, mc );
1395                                         }
1396 
1397                                         if ( sendok & LDAP_BACK_SENDERR ) {
1398                                                   if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1399                                                             rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1400                                                   }
1401                                                   send_ldap_result( op, rs );
1402                                                   rs->sr_matched = NULL;
1403                                         }
1404 
1405                                         return NULL;
1406                               }
1407                     }
1408 
1409                     if ( dn_type == META_DNTYPE_NEWPARENT && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
1410                     {
1411                               if ( mc != NULL ) {
1412                                         meta_back_release_conn( mi, mc );
1413                               }
1414 
1415                               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1416                               rs->sr_text = "Cross-target rename not supported";
1417                               if ( sendok & LDAP_BACK_SENDERR ) {
1418                                         send_ldap_result( op, rs );
1419                               }
1420 
1421                               return NULL;
1422                     }
1423 
1424                     Debug( LDAP_DEBUG_TRACE,
1425           "==>meta_back_getconn: got target=%d for ndn=\"%s\" from cache\n",
1426                                         i, op->o_req_ndn.bv_val );
1427 
1428                     if ( mc == NULL ) {
1429                               /* Retries searching for a metaconn in the avl tree
1430                                * the reason is that the connection might have been
1431                                * created by meta_back_get_candidate() */
1432                               if ( !( sendok & LDAP_BACK_BINDING ) ) {
1433 retry_lock2:;
1434                                         ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1435                                         mc = (metaconn_t *)ldap_tavl_find( mi->mi_conninfo.lai_tree,
1436                                                   (caddr_t)&mc_curr, meta_back_conndn_cmp );
1437                                         if ( mc != NULL ) {
1438                                                   /* catch taint errors */
1439                                                   assert( !LDAP_BACK_CONN_TAINTED( mc ) );
1440 
1441                                                   /* Don't reuse connections while they're still binding */
1442                                                   if ( META_BACK_CONN_CREATING( &mc->mc_conns[ i ] )
1443                                                             || LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) )
1444                                                   {
1445                                                             if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
1446                                                                       ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1447                                                                       ldap_pvt_thread_yield();
1448                                                                       goto retry_lock2;
1449                                                             }
1450 
1451                                                             mc = NULL;
1452 
1453                                                   } else {
1454                                                             mc->mc_refcnt++;
1455                                                   }
1456                                         }
1457                                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1458                               }
1459 
1460                               /* Looks like we didn't get a bind. Open a new session... */
1461                               if ( mc == NULL ) {
1462                                         assert( new_conn == 0 );
1463                                         mc = metaconn_alloc( op );
1464                                         mc->mc_conn = mc_curr.mc_conn;
1465                                         ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
1466                                         new_conn = 1;
1467                                         if ( sendok & LDAP_BACK_BINDING ) {
1468                                                   LDAP_BACK_CONN_BINDING_SET( mc );
1469                                         }
1470                                         if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
1471                                                   LDAP_BACK_CONN_ISPRIV_SET( mc );
1472 
1473                                         } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
1474                                                   LDAP_BACK_CONN_ISANON_SET( mc );
1475                                         }
1476                               }
1477                     }
1478 
1479                     /*
1480                      * Clear all other candidates
1481                      */
1482                     ( void )meta_clear_unused_candidates( op, i );
1483 
1484                     mt = mi->mi_targets[ i ];
1485                     msc = &mc->mc_conns[ i ];
1486 
1487                     /*
1488                      * The target is activated; if needed, it is
1489                      * also init'd. In case of error, meta_back_init_one_conn
1490                      * sends the appropriate result.
1491                      */
1492                     err = meta_back_init_one_conn( op, rs, mc, i,
1493                               LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn );
1494                     if ( err != LDAP_SUCCESS ) {
1495                               /*
1496                                * FIXME: in case one target cannot
1497                                * be init'd, should the other ones
1498                                * be tried?
1499                                */
1500                               META_CANDIDATE_RESET( &candidates[ i ] );
1501                               if ( new_conn ) {
1502                                         mc->mc_refcnt = 0;
1503                                         meta_back_conn_free( mc );
1504 
1505                               } else {
1506                                         meta_back_release_conn( mi, mc );
1507                               }
1508                               return NULL;
1509                     }
1510 
1511                     if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
1512                               LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
1513                     }
1514 
1515                     candidates[ i ].sr_err = LDAP_SUCCESS;
1516                     META_CANDIDATE_SET( &candidates[ i ] );
1517                     ncandidates++;
1518 
1519                     if ( candidate ) {
1520                               *candidate = i;
1521                     }
1522 
1523           /*
1524            * if no unique candidate ...
1525            */
1526           } else {
1527 
1528                     /* Looks like we didn't get a bind. Open a new session... */
1529                     if ( mc == NULL ) {
1530                               assert( new_conn == 0 );
1531                               mc = metaconn_alloc( op );
1532                               mc->mc_conn = mc_curr.mc_conn;
1533                               ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
1534                               new_conn = 1;
1535                               if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
1536                                         LDAP_BACK_CONN_ISPRIV_SET( mc );
1537 
1538                               } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
1539                                         LDAP_BACK_CONN_ISANON_SET( mc );
1540                               }
1541                     }
1542 
1543                     for ( i = 0; i < mi->mi_ntargets; i++ ) {
1544                               metatarget_t                  *mt = mi->mi_targets[ i ];
1545 
1546                               META_CANDIDATE_RESET( &candidates[ i ] );
1547 
1548                               if ( i == cached
1549                                         || meta_back_is_candidate( mt, &op->o_req_ndn,
1550                                                   op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_SUBTREE ) )
1551                               {
1552 
1553                                         /*
1554                                          * The target is activated; if needed, it is
1555                                          * also init'd
1556                                          */
1557                                         int lerr = meta_back_init_one_conn( op, rs, mc, i,
1558                                                   LDAP_BACK_CONN_ISPRIV( &mc_curr ),
1559                                                   LDAP_BACK_DONTSEND, !new_conn );
1560                                         candidates[ i ].sr_err = lerr;
1561                                         if ( lerr == LDAP_SUCCESS ) {
1562                                                   META_CANDIDATE_SET( &candidates[ i ] );
1563                                                   ncandidates++;
1564 
1565                                                   Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d]\n",
1566                                                             op->o_log_prefix, i );
1567 
1568                                         } else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
1569                                                   META_CANDIDATE_SET( &candidates[ i ] );
1570 
1571                                                   Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] %s\n",
1572                                                             op->o_log_prefix, i,
1573                                                             mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
1574 
1575                                         } else {
1576 
1577                                                   /*
1578                                                    * FIXME: in case one target cannot
1579                                                    * be init'd, should the other ones
1580                                                    * be tried?
1581                                                    */
1582                                                   if ( new_conn ) {
1583                                                             ( void )meta_clear_one_candidate( op, mc, i );
1584                                                   }
1585                                                   /* leave the target candidate, but record the error for later use */
1586                                                   err = lerr;
1587 
1588                                                   if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
1589                                                             Log( LDAP_DEBUG_TRACE, ldap_syslog_level, "%s: meta_back_getconn[%d] quarantined err=%d text=%s\n",
1590                                                                       op->o_log_prefix, i, lerr, rs->sr_text );
1591 
1592                                                   } else {
1593                                                             Log( LDAP_DEBUG_ANY, ldap_syslog, "%s: meta_back_getconn[%d] failed err=%d text=%s\n",
1594                                                                       op->o_log_prefix, i, lerr, rs->sr_text );
1595                                                   }
1596 
1597                                                   if ( META_BACK_ONERR_STOP( mi ) ) {
1598                                                             if ( sendok & LDAP_BACK_SENDERR ) {
1599                                                                       send_ldap_result( op, rs );
1600                                                             }
1601                                                             if ( new_conn ) {
1602                                                                       mc->mc_refcnt = 0;
1603                                                                       meta_back_conn_free( mc );
1604 
1605                                                             } else {
1606                                                                       meta_back_release_conn( mi, mc );
1607                                                             }
1608 
1609                                                             return NULL;
1610                                                   }
1611 
1612                                                   continue;
1613                                         }
1614 
1615                               } else {
1616                                         if ( new_conn ) {
1617                                                   ( void )meta_clear_one_candidate( op, mc, i );
1618                                         }
1619                               }
1620                     }
1621 
1622                     if ( ncandidates == 0 ) {
1623                               if ( new_conn ) {
1624                                         mc->mc_refcnt = 0;
1625                                         meta_back_conn_free( mc );
1626 
1627                               } else {
1628                                         meta_back_release_conn( mi, mc );
1629                               }
1630 
1631                               if ( rs->sr_err == LDAP_SUCCESS ) {
1632                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
1633                                         rs->sr_text = "Unable to select valid candidates";
1634                               }
1635 
1636                               if ( sendok & LDAP_BACK_SENDERR ) {
1637                                         if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
1638                                                   rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
1639                                         }
1640                                         send_ldap_result( op, rs );
1641                                         rs->sr_matched = NULL;
1642                               }
1643 
1644                               return NULL;
1645                     }
1646           }
1647 
1648 done:;
1649           /* clear out meta_back_init_one_conn non-fatal errors */
1650           rs->sr_err = LDAP_SUCCESS;
1651           rs->sr_text = NULL;
1652 
1653           /* touch the timestamp */
1654           if ( mi->mi_idle_timeout != 0 ) {
1655                     mc->mc_time = op->o_time;
1656           }
1657 
1658           if ( new_conn ) {
1659                     if ( mi->mi_conn_ttl ) {
1660                               mc->mc_create_time = op->o_time;
1661                     }
1662 
1663                     /*
1664                      * Inserts the newly created metaconn in the avl tree
1665                      */
1666                     ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1667 #if META_BACK_PRINT_CONNTREE > 0
1668                     meta_back_print_conntree( mi, ">>> meta_back_getconn" );
1669 #endif /* META_BACK_PRINT_CONNTREE */
1670 
1671                     err = 0;
1672                     if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
1673                               if ( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num < mi->mi_conn_priv_max ) {
1674                                         LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
1675                                         mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num++;
1676                                         LDAP_BACK_CONN_CACHED_SET( mc );
1677 
1678                               } else {
1679                                         LDAP_BACK_CONN_TAINTED_SET( mc );
1680                               }
1681                               rs->sr_err = 0;
1682 
1683                     } else if ( !( sendok & LDAP_BACK_BINDING ) ) {
1684                               err = ldap_tavl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc,
1685                                         meta_back_conndn_cmp, meta_back_conndn_dup );
1686                               LDAP_BACK_CONN_CACHED_SET( mc );
1687                     }
1688 
1689 #if META_BACK_PRINT_CONNTREE > 0
1690                     meta_back_print_conntree( mi, "<<< meta_back_getconn" );
1691 #endif /* META_BACK_PRINT_CONNTREE */
1692                     ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1693 
1694                     if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) {
1695                               /*
1696                                * Err could be -1 in case a duplicate metaconn is inserted
1697                                */
1698                               switch ( err ) {
1699                               case 0:
1700                                         break;
1701 
1702                               case -1:
1703                                         LDAP_BACK_CONN_CACHED_CLEAR( mc );
1704                                         /* duplicate: free and try to get the newly created one */
1705                                         if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
1706                                                   mc->mc_refcnt = 0;
1707                                                   meta_back_conn_free( mc );
1708 
1709                                                   new_conn = 0;
1710                                                   goto retry_lock;
1711                                         }
1712 
1713                                         LDAP_BACK_CONN_TAINTED_SET( mc );
1714                                         break;
1715 
1716                               default:
1717                                         LDAP_BACK_CONN_CACHED_CLEAR( mc );
1718                                         if ( LogTest( LDAP_DEBUG_ANY ) ) {
1719                                                   char buf[STRLENOF("4294967295U") + 1] = { 0 };
1720                                                   mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
1721 
1722                                                   Debug( LDAP_DEBUG_ANY,
1723                                                             "%s meta_back_getconn: candidates=%d conn=%s insert failed\n",
1724                                                             op->o_log_prefix, ncandidates, buf );
1725                                         }
1726 
1727                                         mc->mc_refcnt = 0;
1728                                         meta_back_conn_free( mc );
1729 
1730                                         rs->sr_err = LDAP_OTHER;
1731                                         rs->sr_text = "Proxy bind collision";
1732                                         if ( sendok & LDAP_BACK_SENDERR ) {
1733                                                   send_ldap_result( op, rs );
1734                                         }
1735                                         return NULL;
1736                               }
1737                     }
1738 
1739                     if ( LogTest( LDAP_DEBUG_TRACE ) ) {
1740                               char buf[STRLENOF("4294967295U") + 1] = { 0 };
1741                               mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
1742 
1743                               Debug( LDAP_DEBUG_TRACE,
1744                                         "%s meta_back_getconn: candidates=%d conn=%s inserted\n",
1745                                         op->o_log_prefix, ncandidates, buf );
1746                     }
1747 
1748           } else {
1749                     if ( LogTest( LDAP_DEBUG_TRACE ) ) {
1750                               char buf[STRLENOF("4294967295U") + 1] = { 0 };
1751                               mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
1752 
1753                               Debug( LDAP_DEBUG_TRACE,
1754                                         "%s meta_back_getconn: candidates=%d conn=%s fetched\n",
1755                                         op->o_log_prefix, ncandidates, buf );
1756                     }
1757           }
1758 
1759           return mc;
1760 }
1761 
1762 void
meta_back_release_conn_lock(metainfo_t * mi,metaconn_t * mc,int dolock)1763 meta_back_release_conn_lock(
1764           metainfo_t                    *mi,
1765           metaconn_t                    *mc,
1766           int                           dolock )
1767 {
1768           assert( mc != NULL );
1769 
1770           if ( dolock ) {
1771                     ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
1772           }
1773           assert( mc->mc_refcnt > 0 );
1774           mc->mc_refcnt--;
1775           /* NOTE: the connection is removed if either it is tainted
1776            * or if it is shared and no one else is using it.  This needs
1777            * to occur because for intrinsic reasons cached connections
1778            * that are not privileged would live forever and pollute
1779            * the connection space (and eat up resources).  Maybe this
1780            * should be configurable... */
1781           if ( LDAP_BACK_CONN_TAINTED( mc ) || !LDAP_BACK_CONN_CACHED( mc ) ) {
1782 #if META_BACK_PRINT_CONNTREE > 0
1783                     meta_back_print_conntree( mi, ">>> meta_back_release_conn" );
1784 #endif /* META_BACK_PRINT_CONNTREE */
1785 
1786                     if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
1787                               if ( mc->mc_q.tqe_prev != NULL ) {
1788                                         assert( LDAP_BACK_CONN_CACHED( mc ) );
1789                                         assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
1790                                         LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
1791                                         mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
1792                                         LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
1793 
1794                               } else {
1795                                         assert( !LDAP_BACK_CONN_CACHED( mc ) );
1796                               }
1797 
1798                     } else if ( LDAP_BACK_CONN_CACHED( mc ) ) {
1799                               metaconn_t          *tmpmc;
1800 
1801                               tmpmc = ldap_tavl_delete( &mi->mi_conninfo.lai_tree,
1802                                         ( caddr_t )mc, meta_back_conndnmc_cmp );
1803 
1804                               /* Overparanoid, but useful... */
1805                               assert( tmpmc == NULL || tmpmc == mc );
1806                     }
1807 
1808                     LDAP_BACK_CONN_CACHED_CLEAR( mc );
1809 
1810 #if META_BACK_PRINT_CONNTREE > 0
1811                     meta_back_print_conntree( mi, "<<< meta_back_release_conn" );
1812 #endif /* META_BACK_PRINT_CONNTREE */
1813 
1814                     if ( mc->mc_refcnt == 0 ) {
1815                               meta_back_conn_free( mc );
1816                               mc = NULL;
1817                     }
1818           }
1819 
1820           if ( mc != NULL && LDAP_BACK_CONN_BINDING( mc ) ) {
1821                     LDAP_BACK_CONN_BINDING_CLEAR( mc );
1822           }
1823 
1824           if ( dolock ) {
1825                     ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
1826           }
1827 }
1828 
1829 void
meta_back_quarantine(Operation * op,SlapReply * rs,int candidate)1830 meta_back_quarantine(
1831           Operation *op,
1832           SlapReply *rs,
1833           int                 candidate )
1834 {
1835           metainfo_t                    *mi = (metainfo_t *)op->o_bd->be_private;
1836           metatarget_t                  *mt = mi->mi_targets[ candidate ];
1837 
1838           slap_retry_info_t   *ri = &mt->mt_quarantine;
1839 
1840           ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
1841 
1842           if ( rs->sr_err == LDAP_UNAVAILABLE ) {
1843                     time_t    new_last = slap_get_time();
1844 
1845                     switch ( mt->mt_isquarantined ) {
1846                     case LDAP_BACK_FQ_NO:
1847                               if ( ri->ri_last == new_last ) {
1848                                         goto done;
1849                               }
1850 
1851                               Debug( LDAP_DEBUG_ANY,
1852                                         "%s meta_back_quarantine[%d]: enter.\n",
1853                                         op->o_log_prefix, candidate );
1854 
1855                               ri->ri_idx = 0;
1856                               ri->ri_count = 0;
1857                               break;
1858 
1859                     case LDAP_BACK_FQ_RETRYING:
1860                               Debug(LDAP_DEBUG_ANY,
1861                                     "%s meta_back_quarantine[%d]: block #%d try #%d failed.\n",
1862                                     op->o_log_prefix, candidate, ri->ri_idx,
1863                                     ri->ri_count );
1864 
1865                               ++ri->ri_count;
1866                               if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
1867                                         && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
1868                               {
1869                                         ri->ri_count = 0;
1870                                         ++ri->ri_idx;
1871                               }
1872                               break;
1873 
1874                     default:
1875                               goto done;
1876                     }
1877 
1878                     mt->mt_isquarantined = LDAP_BACK_FQ_YES;
1879                     ri->ri_last = new_last;
1880 
1881           } else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
1882                     Debug( LDAP_DEBUG_ANY,
1883                               "%s meta_back_quarantine[%d]: exit.\n",
1884                               op->o_log_prefix, candidate );
1885 
1886                     if ( mi->mi_quarantine_f ) {
1887                               (void)mi->mi_quarantine_f( mi, candidate,
1888                                         mi->mi_quarantine_p );
1889                     }
1890 
1891                     ri->ri_count = 0;
1892                     ri->ri_idx = 0;
1893                     mt->mt_isquarantined = LDAP_BACK_FQ_NO;
1894           }
1895 
1896 done:;
1897           ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
1898 }
1899