]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/conn.c
cfabeb17534f1b3dba1618e19cd48b425ae0a1d0
[openldap] / servers / slapd / back-meta / conn.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2005 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/socket.h>
28 #include <ac/string.h>
29
30
31 #define AVL_INTERNAL
32 #include "slap.h"
33 #include "../back-ldap/back-ldap.h"
34 #include "back-meta.h"
35
36 /*
37  * Set PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
38  */
39 #define PRINT_CONNTREE 0
40
41 /*
42  * meta_back_conn_cmp
43  *
44  * compares two struct metaconn based on the value of the conn pointer;
45  * used by avl stuff
46  */
47 int
48 meta_back_conn_cmp(
49         const void *c1,
50         const void *c2
51         )
52 {
53         struct metaconn *lc1 = ( struct metaconn * )c1;
54         struct metaconn *lc2 = ( struct metaconn * )c2;
55         
56         return SLAP_PTRCMP( lc1->mc_conn, lc2->mc_conn );
57 }
58
59 /*
60  * meta_back_conn_dup
61  *
62  * returns -1 in case a duplicate struct metaconn has been inserted;
63  * used by avl stuff
64  */
65 int
66 meta_back_conn_dup(
67         void *c1,
68         void *c2
69         )
70 {
71         struct metaconn *lc1 = ( struct metaconn * )c1;
72         struct metaconn *lc2 = ( struct metaconn * )c2;
73
74         return( ( lc1->mc_conn == lc2->mc_conn ) ? -1 : 0 );
75 }
76
77 /*
78  * Debug stuff (got it from libavl)
79  */
80 #if PRINT_CONNTREE > 0
81 static void
82 ravl_print( Avlnode *root, int depth )
83 {
84         int     i;
85         
86         if ( root == 0 ) {
87                 return;
88         }
89         
90         ravl_print( root->avl_right, depth + 1 );
91         
92         for ( i = 0; i < depth; i++ ) {
93                 printf( "    " );
94         }
95
96         printf( "c(%d) %d\n", ( ( struct metaconn * )root->avl_data )->mc_conn->c_connid, root->avl_bf );
97         
98         ravl_print( root->avl_left, depth + 1 );
99 }
100
101 static void
102 myprint( Avlnode *root )
103 {
104         printf( "********\n" );
105         
106         if ( root == 0 ) {
107                 printf( "\tNULL\n" );
108         } else {
109                 ravl_print( root, 0 );
110         }
111         
112         printf( "********\n" );
113 }
114 #endif /* PRINT_CONNTREE */
115 /*
116  * End of debug stuff
117  */
118
119 /*
120  * metaconn_alloc
121  * 
122  * Allocates a connection structure, making room for all the referenced targets
123  */
124 static struct metaconn *
125 metaconn_alloc( int ntargets )
126 {
127         struct metaconn *lc;
128
129         assert( ntargets > 0 );
130
131         lc = ch_calloc( sizeof( struct metaconn ), 1 );
132         if ( lc == NULL ) {
133                 return NULL;
134         }
135         
136         /*
137          * make it a null-terminated array ...
138          */
139         lc->mc_conns = ch_calloc( sizeof( struct metasingleconn ), ntargets + 1 );
140         if ( lc->mc_conns == NULL ) {
141                 free( lc );
142                 return NULL;
143         }
144
145         /* FIXME: needed by META_LAST() */
146         lc->mc_conns[ ntargets ].msc_candidate = META_LAST_CONN;
147
148         for ( ; ntargets-- > 0; ) {
149                 lc->mc_conns[ ntargets ].msc_ld = NULL;
150                 BER_BVZERO( &lc->mc_conns[ ntargets ].msc_bound_ndn );
151                 BER_BVZERO( &lc->mc_conns[ ntargets ].msc_cred );
152                 lc->mc_conns[ ntargets ].msc_bound = META_UNBOUND;
153         }
154
155         lc->mc_bound_target = META_BOUND_NONE;
156
157         return lc;
158 }
159
160 /*
161  * metaconn_free
162  *
163  * clears a metaconn
164  */
165 static void
166 metaconn_free(
167                 struct metaconn *lc
168 )
169 {
170         if ( !lc ) {
171                 return;
172         }
173         
174         if ( lc->mc_conns ) {
175                 ch_free( lc->mc_conns );
176         }
177
178         free( lc );
179 }
180
181 /*
182  * init_one_conn
183  * 
184  * Initializes one connection
185  */
186 static int
187 init_one_conn(
188                 Operation               *op,
189                 SlapReply               *rs,
190                 struct metatarget       *lt, 
191                 struct metasingleconn   *lsc,
192                 char                    *candidate,
193                 ldap_back_send_t        sendok )
194 {
195         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
196         int             vers;
197         dncookie        dc;
198
199         /*
200          * Already init'ed
201          */
202         if ( lsc->msc_ld != NULL ) {
203                 rs->sr_err = LDAP_SUCCESS;
204                 goto error_return;
205         }
206        
207         /*
208          * Attempts to initialize the connection to the target ds
209          */
210         rs->sr_err = ldap_initialize( &lsc->msc_ld, lt->mt_uri );
211         if ( rs->sr_err != LDAP_SUCCESS ) {
212                 goto error_return;
213         }
214
215         /*
216          * Set LDAP version. This will always succeed: If the client
217          * bound with a particular version, then so can we.
218          */
219         vers = op->o_conn->c_protocol;
220         ldap_set_option( lsc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &vers );
221
222         /* automatically chase referrals ("chase-referrals"/"dont-chase-referrals" statement) */
223         if ( LDAP_BACK_CHASE_REFERRALS( li ) ) {
224                 ldap_set_option( lsc->msc_ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
225         }
226
227 #ifdef HAVE_TLS
228         /* start TLS ("start-tls"/"try-start-tls" statements) */
229         if ( ( LDAP_BACK_USE_TLS( li ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( li ) ) )
230                         && !ldap_is_ldaps_url( lt->mt_uri ) )
231         {
232 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
233                 /*
234                  * use asynchronous StartTLS
235                  * in case, chase referral (not implemented yet)
236                  */
237                 int             msgid;
238
239                 rs->sr_err = ldap_start_tls( lsc->msc_ld, NULL, NULL, &msgid );
240                 if ( rs->sr_err == LDAP_SUCCESS ) {
241                         LDAPMessage     *res = NULL;
242                         int             rc, retries = 1;
243                         struct timeval  tv = { 0, 0 };
244
245 retry:;
246                         rc = ldap_result( lsc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
247                         if ( rc < 0 ) {
248                                 rs->sr_err = LDAP_OTHER;
249
250                         } else if ( rc == 0 ) {
251                                 if ( retries ) {
252                                         retries--;
253                                         tv.tv_sec = 0;
254                                         tv.tv_usec = 100000;
255                                         goto retry;
256                                 }
257                                 rs->sr_err = LDAP_OTHER;
258
259                         } else if ( rc == LDAP_RES_EXTENDED ) {
260                                 struct berval   *data = NULL;
261
262                                 rs->sr_err = ldap_parse_extended_result( lsc->msc_ld, res,
263                                                 NULL, &data, 0 );
264                                 if ( rs->sr_err == LDAP_SUCCESS ) {
265                                         rs->sr_err = ldap_result2error( lsc->msc_ld, res, 1 );
266                                         res = NULL;
267                                         
268                                         /* FIXME: in case a referral 
269                                          * is returned, should we try
270                                          * using it instead of the 
271                                          * configured URI? */
272                                         if ( rs->sr_err == LDAP_SUCCESS ) {
273                                                 ldap_install_tls( lsc->msc_ld );
274
275                                         } else if ( rs->sr_err == LDAP_REFERRAL ) {
276                                                 rs->sr_err = LDAP_OTHER;
277                                                 rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
278                                         }
279
280                                         if ( data ) {
281                                                 if ( data->bv_val ) {
282                                                         ber_memfree( data->bv_val );
283                                                 }
284                                                 ber_memfree( data );
285                                         }
286                                 }
287
288                         } else {
289                                 rs->sr_err = LDAP_OTHER;
290                         }
291
292                         if ( res != NULL ) {
293                                 ldap_msgfree( res );
294                         }
295                 }
296 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
297                 /*
298                  * use synchronous StartTLS
299                  */
300                 rs->sr_err = ldap_start_tls_s( lsc->msc_ld, NULL, NULL );
301 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
302
303                 /* if StartTLS is requested, only attempt it if the URL
304                  * is not "ldaps://"; this may occur not only in case
305                  * of misconfiguration, but also when used in the chain 
306                  * overlay, where the "uri" can be parsed out of a referral */
307                 if ( rs->sr_err == LDAP_SERVER_DOWN
308                                 || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( li ) ) )
309                 {
310                         ldap_unbind_ext_s( lsc->msc_ld, NULL, NULL );
311                         goto error_return;
312                 }
313         }
314 #endif /* HAVE_TLS */
315
316         /*
317          * Set the network timeout if set
318          */
319         if ( li->mi_network_timeout != 0 ) {
320                 struct timeval  network_timeout;
321
322                 network_timeout.tv_usec = 0;
323                 network_timeout.tv_sec = li->mi_network_timeout;
324
325                 ldap_set_option( lsc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
326                                 (void *)&network_timeout );
327         }
328
329         /*
330          * Sets a cookie for the rewrite session
331          */
332         ( void )rewrite_session_init( lt->mt_rwmap.rwm_rw, op->o_conn );
333
334         /*
335          * If the connection DN is not null, an attempt to rewrite it is made
336          */
337         if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
338                 dc.rwmap = &lt->mt_rwmap;
339                 dc.conn = op->o_conn;
340                 dc.rs = rs;
341                 dc.ctx = "bindDN";
342                 
343                 /*
344                  * Rewrite the bind dn if needed
345                  */
346                 if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
347                                         &lsc->msc_bound_ndn ) )
348                 {
349                         goto error_return;
350                 }
351
352                 /* copy the DN idf needed */
353                 if ( lsc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
354                         ber_dupbv( &lsc->msc_bound_ndn, &op->o_conn->c_dn );
355                 }
356
357                 assert( !BER_BVISNULL( &lsc->msc_bound_ndn ) );
358
359         } else {
360                 ber_str2bv( "", 0, 1, &lsc->msc_bound_ndn );
361         }
362
363         lsc->msc_bound = META_UNBOUND;
364
365 error_return:;
366         if ( rs->sr_err != LDAP_SUCCESS ) {
367                 rs->sr_err = slap_map_api2result( rs );
368                 if ( sendok & LDAP_BACK_SENDERR ) {
369                         send_ldap_result( op, rs );
370                         rs->sr_text = NULL;
371                 }
372
373         } else {
374
375                 /*
376                  * The candidate is activated
377                  */
378                 *candidate = META_CANDIDATE;
379         }
380
381         return rs->sr_err;
382 }
383
384 /*
385  * callback for unique candidate selection
386  */
387 static int
388 meta_back_conn_cb( Operation *op, SlapReply *rs )
389 {
390         assert( op->o_tag == LDAP_REQ_SEARCH );
391
392         switch ( rs->sr_type ) {
393         case REP_SEARCH:
394                 ((int *)op->o_callback->sc_private)[0] = (int)op->o_private;
395                 break;
396
397         case REP_SEARCHREF:
398         case REP_RESULT:
399                 break;
400
401         default:
402                 return rs->sr_err;
403         }
404
405         return 0;
406 }
407
408
409 static int
410 meta_back_get_candidate(
411         Operation       *op,
412         SlapReply       *rs,
413         struct berval   *ndn )
414 {
415         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
416         int             candidate;
417
418         /*
419          * tries to get a unique candidate
420          * (takes care of default target)
421          */
422         candidate = meta_back_select_unique_candidate( li, ndn );
423
424         /*
425          * if any is found, inits the connection
426          */
427         if ( candidate == META_TARGET_NONE ) {
428                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
429                 rs->sr_text = "no suitable candidate target found";
430
431         } else if ( candidate == META_TARGET_MULTIPLE ) {
432                 Filter          f = { 0 };
433                 Operation       op2 = *op;
434                 SlapReply       rs2 = { 0 };
435                 slap_callback   cb2 = { 0 };
436                 int             rc;
437
438                 /* try to get a unique match for the request ndn
439                  * among the multiple candidates available */
440                 op2.o_tag = LDAP_REQ_SEARCH;
441                 op2.o_req_dn = *ndn;
442                 op2.o_req_ndn = *ndn;
443                 op2.ors_scope = LDAP_SCOPE_BASE;
444                 op2.ors_deref = LDAP_DEREF_NEVER;
445                 op2.ors_attrs = slap_anlist_no_attrs;
446                 op2.ors_attrsonly = 0;
447                 op2.ors_limit = NULL;
448                 op2.ors_slimit = 1;
449                 op2.ors_tlimit = SLAP_NO_LIMIT;
450
451                 f.f_choice = LDAP_FILTER_PRESENT;
452                 f.f_desc = slap_schema.si_ad_objectClass;
453                 op2.ors_filter = &f;
454                 BER_BVSTR( &op2.ors_filterstr, "(objectClass=*)" );
455
456                 op2.o_callback = &cb2;
457                 cb2.sc_response = meta_back_conn_cb;
458                 cb2.sc_private = (void *)&candidate;
459
460                 rc = op->o_bd->be_search( &op2, &rs2 );
461
462                 switch ( rs2.sr_err ) {
463                 case LDAP_SUCCESS:
464                 default:
465                         rs->sr_err = rs2.sr_err;
466                         break;
467
468                 case LDAP_SIZELIMIT_EXCEEDED:
469                         /* if multiple candidates can serve the operation,
470                          * and a default target is defined, and it is
471                          * a candidate, try using it (FIXME: YMMV) */
472                         if ( li->mi_defaulttarget != META_DEFAULT_TARGET_NONE
473                                 && meta_back_is_candidate( &li->mi_targets[ li->mi_defaulttarget ]->mt_nsuffix, ndn ) )
474                         {
475                                 candidate = li->mi_defaulttarget;
476                                 rs->sr_err = LDAP_SUCCESS;
477                                 rs->sr_text = NULL;
478
479                         } else {
480                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
481                                 rs->sr_text = "cannot select unique candidate target";
482                         }
483                         break;
484                 }
485         }
486
487         return candidate;
488 }
489
490 static void
491 meta_back_candidate_keyfree( void *key, void *data )
492 {
493         ber_memfree_x( data, NULL );
494 }
495
496 char *
497 meta_back_candidates_get( Operation *op )
498 {
499         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
500         void            *data = NULL;
501
502         if ( op->o_threadctx ) {
503                 ldap_pvt_thread_pool_getkey( op->o_threadctx,
504                                 meta_back_candidate_keyfree, &data, NULL );
505         } else {
506                 data = (void *)li->mi_candidates;
507         }
508
509         if ( data == NULL ) {
510                 data = ber_memalloc_x( sizeof( char ) * li->mi_ntargets, NULL );
511                 if ( op->o_threadctx ) {
512                         ldap_pvt_thread_pool_setkey( op->o_threadctx,
513                                         meta_back_candidate_keyfree, data,
514                                         meta_back_candidate_keyfree );
515
516                 } else {
517                         li->mi_candidates = (char *)data;
518                 }
519         }
520
521         return (char *)data;
522 }
523
524 /*
525  * meta_back_getconn
526  * 
527  * Prepares the connection structure
528  * 
529  * RATIONALE:
530  *
531  * - determine what DN is being requested:
532  *
533  *      op      requires candidate      checks
534  *
535  *      add     unique                  parent of o_req_ndn
536  *      bind    unique^*[/all]          o_req_ndn [no check]
537  *      compare unique^+                o_req_ndn
538  *      delete  unique                  o_req_ndn
539  *      modify  unique                  o_req_ndn
540  *      search  any                     o_req_ndn
541  *      modrdn  unique[, unique]        o_req_ndn[, orr_nnewSup]
542  *
543  * - for ops that require the candidate to be unique, in case of multiple
544  *   occurrences an internal search with sizeLimit=1 is performed
545  *   if a unique candidate can actually be determined.  If none is found,
546  *   the operation aborts; if multiple are found, the default target
547  *   is used if defined and candidate; otherwise the operation aborts.
548  *
549  * *^note: actually, the bind operation is handled much like a search;
550  *   i.e. the bind is broadcast to all candidate targets.
551  *
552  * +^note: actually, the compare operation is handled much like a search;
553  *   i.e. the compare is broadcast to all candidate targets, while checking
554  *   that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
555  *   returned.
556  */
557 struct metaconn *
558 meta_back_getconn(
559                 Operation               *op,
560                 SlapReply               *rs,
561                 int                     *candidate,
562                 ldap_back_send_t        sendok )
563 {
564         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
565         struct metaconn *lc, lc_curr;
566         int             cached = META_TARGET_NONE,
567                         i = META_TARGET_NONE,
568                         err = LDAP_SUCCESS,
569                         new_conn = 0;
570
571         meta_op_type    op_type = META_OP_REQUIRE_SINGLE;
572         int             parent = 0,
573                         newparent = 0;
574         struct berval   ndn = op->o_req_ndn;
575
576         char            *candidates = meta_back_candidates_get( op );
577
578         /* Searches for a metaconn in the avl tree */
579         lc_curr.mc_conn = op->o_conn;
580         ldap_pvt_thread_mutex_lock( &li->mi_conn_mutex );
581         lc = (struct metaconn *)avl_find( li->mi_conntree, 
582                 (caddr_t)&lc_curr, meta_back_conn_cmp );
583         ldap_pvt_thread_mutex_unlock( &li->mi_conn_mutex );
584
585         switch ( op->o_tag ) {
586         case LDAP_REQ_ADD:
587                 /* if we go to selection, the entry must not exist,
588                  * and we must be able to resolve the parent */
589                 parent = 1;
590                 dnParent( &ndn, &ndn );
591                 break;
592
593         case LDAP_REQ_MODRDN:
594                 /* if nnewSuperior is not NULL, it must resolve
595                  * to the same candidate as the req_ndn */
596                 if ( op->orr_nnewSup ) {
597                         newparent = 1;
598                 }
599                 break;
600
601         case LDAP_REQ_BIND:
602                 /* if bound as rootdn, the backend must bind to all targets
603                  * with the administrative identity */
604                 if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
605                         op_type = META_OP_REQUIRE_ALL;
606                 }
607                 break;
608
609         case LDAP_REQ_DELETE:
610         case LDAP_REQ_MODIFY:
611                 /* just a unique candidate */
612                 break;
613
614         case LDAP_REQ_COMPARE:
615         case LDAP_REQ_SEARCH:
616                 /* allow multiple candidates for the searchBase */
617                 op_type = META_OP_ALLOW_MULTIPLE;
618                 break;
619
620         default:
621                 /* right now, just break (exop?) */
622                 break;
623         }
624
625         /*
626          * require all connections ...
627          */
628         if ( op_type == META_OP_REQUIRE_ALL ) {
629
630                 /* Looks like we didn't get a bind. Open a new session... */
631                 if ( !lc ) {
632                         lc = metaconn_alloc( li->mi_ntargets );
633                         lc->mc_conn = op->o_conn;
634                         new_conn = 1;
635                 }
636
637                 for ( i = 0; i < li->mi_ntargets; i++ ) {
638
639                         /*
640                          * The target is activated; if needed, it is
641                          * also init'd
642                          */
643                         int lerr = init_one_conn( op, rs, li->mi_targets[ i ],
644                                         &lc->mc_conns[ i ], &candidates[ i ],
645                                         sendok );
646                         if ( lerr != LDAP_SUCCESS ) {
647                                 
648                                 /*
649                                  * FIXME: in case one target cannot
650                                  * be init'd, should the other ones
651                                  * be tried?
652                                  */
653                                 candidates[ i ] = META_NOT_CANDIDATE;
654 #if 0
655                                 ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
656 #endif
657                                 err = lerr;
658                                 continue;
659                         }
660                 }
661                 goto done;
662         }
663         
664         /*
665          * looks in cache, if any
666          */
667         if ( li->mi_cache.ttl != META_DNCACHE_DISABLED ) {
668                 cached = i = meta_dncache_get_target( &li->mi_cache, &op->o_req_ndn );
669         }
670
671         if ( op_type == META_OP_REQUIRE_SINGLE ) {
672
673                 memset( candidates, META_NOT_CANDIDATE, sizeof( char ) * li->mi_ntargets );
674
675                 /*
676                  * tries to get a unique candidate
677                  * (takes care of default target)
678                  */
679                 if ( i == META_TARGET_NONE ) {
680                         i = meta_back_get_candidate( op, rs, &ndn );
681
682                         if ( rs->sr_err != LDAP_SUCCESS ) {
683                                 if ( sendok & LDAP_BACK_SENDERR ) {
684                                         send_ldap_result( op, rs );
685                                         rs->sr_text = NULL;
686                                 }
687                                 return NULL;
688                         }
689                 }
690
691                 if ( newparent && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
692                 {
693                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
694                         rs->sr_text = "cross-target rename not supported";
695                         if ( sendok & LDAP_BACK_SENDERR ) {
696                                 send_ldap_result( op, rs );
697                                 rs->sr_text = NULL;
698                         }
699                         return NULL;
700                 }
701
702                 Debug( LDAP_DEBUG_CACHE,
703         "==>meta_back_getconn: got target %d for ndn=\"%s\" from cache\n",
704                                 i, op->o_req_ndn.bv_val, 0 );
705
706                 /* Retries searching for a metaconn in the avl tree */
707                 lc_curr.mc_conn = op->o_conn;
708                 ldap_pvt_thread_mutex_lock( &li->mi_conn_mutex );
709                 lc = (struct metaconn *)avl_find( li->mi_conntree, 
710                         (caddr_t)&lc_curr, meta_back_conn_cmp );
711                 ldap_pvt_thread_mutex_unlock( &li->mi_conn_mutex );
712
713                 /* Looks like we didn't get a bind. Open a new session... */
714                 if ( !lc ) {
715                         lc = metaconn_alloc( li->mi_ntargets );
716                         lc->mc_conn = op->o_conn;
717                         new_conn = 1;
718                 }
719
720                 /*
721                  * Clear all other candidates
722                  */
723                 ( void )meta_clear_unused_candidates( op, lc, i );
724
725                 /*
726                  * The target is activated; if needed, it is
727                  * also init'd. In case of error, init_one_conn
728                  * sends the appropriate result.
729                  */
730                 err = init_one_conn( op, rs, li->mi_targets[ i ],
731                                 &lc->mc_conns[ i ], &candidates[ i ],
732                                 sendok );
733                 if ( err != LDAP_SUCCESS ) {
734                 
735                         /*
736                          * FIXME: in case one target cannot
737                          * be init'd, should the other ones
738                          * be tried?
739                          */
740                         candidates[ i ] = META_NOT_CANDIDATE;
741                         if ( new_conn ) {
742                                 ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
743                                 metaconn_free( lc );
744                         }
745                         return NULL;
746                 }
747
748                 if ( candidate ) {
749                         *candidate = i;
750                 }
751
752         /*
753          * if no unique candidate ...
754          */
755         } else {
756
757                 /* Looks like we didn't get a bind. Open a new session... */
758                 if ( !lc ) {
759                         lc = metaconn_alloc( li->mi_ntargets );
760                         lc->mc_conn = op->o_conn;
761                         new_conn = 1;
762                 }
763
764                 for ( i = 0; i < li->mi_ntargets; i++ ) {
765                         if ( i == cached 
766                                 || meta_back_is_candidate( &li->mi_targets[ i ]->mt_nsuffix,
767                                                 &op->o_req_ndn ) )
768                         {
769
770                                 /*
771                                  * The target is activated; if needed, it is
772                                  * also init'd
773                                  */
774                                 int lerr = init_one_conn( op, rs,
775                                                 li->mi_targets[ i ],
776                                                 &lc->mc_conns[ i ],
777                                                 &candidates[ i ],
778                                                 sendok );
779                                 if ( lerr != LDAP_SUCCESS ) {
780                                 
781                                         /*
782                                          * FIXME: in case one target cannot
783                                          * be init'd, should the other ones
784                                          * be tried?
785                                          */
786                                         candidates[ i ] = META_NOT_CANDIDATE;
787 #if 0
788                                         ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
789 #endif
790                                         err = lerr;
791                                         continue;
792                                 }
793
794                         } else {
795                                 candidates[ i ] = META_NOT_CANDIDATE;
796                         }
797                 }
798         }
799
800 done:;
801         /* clear out init_one_conn non-fatal errors */
802         rs->sr_err = LDAP_SUCCESS;
803         rs->sr_text = NULL;
804
805         if ( new_conn ) {
806                 
807                 /*
808                  * Inserts the newly created metaconn in the avl tree
809                  */
810                 ldap_pvt_thread_mutex_lock( &li->mi_conn_mutex );
811                 err = avl_insert( &li->mi_conntree, ( caddr_t )lc,
812                                 meta_back_conn_cmp, meta_back_conn_dup );
813
814 #if PRINT_CONNTREE > 0
815                 myprint( li->mi_conntree );
816 #endif /* PRINT_CONNTREE */
817                 
818                 ldap_pvt_thread_mutex_unlock( &li->mi_conn_mutex );
819
820                 Debug( LDAP_DEBUG_TRACE,
821                         "=>meta_back_getconn: conn %ld inserted\n",
822                         lc->mc_conn->c_connid, 0, 0 );
823                 
824                 /*
825                  * Err could be -1 in case a duplicate metaconn is inserted
826                  */
827                 if ( err != 0 ) {
828                         rs->sr_err = LDAP_OTHER;
829                         rs->sr_text = "Internal server error";
830                         metaconn_free( lc );
831                         if ( sendok & LDAP_BACK_SENDERR ) {
832                                 send_ldap_result( op, rs );
833                                 rs->sr_text = NULL;
834                         }
835                         return NULL;
836                 }
837
838         } else {
839                 Debug( LDAP_DEBUG_TRACE,
840                         "=>meta_back_getconn: conn %ld fetched\n",
841                         lc->mc_conn->c_connid, 0, 0 );
842         }
843         
844         return lc;
845 }
846