]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/conn.c
737dc2e057bb26e9bacb62309872d186ff89b49e
[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/errno.h>
28 #include <ac/socket.h>
29 #include <ac/string.h>
30
31
32 #define AVL_INTERNAL
33 #include "slap.h"
34 #include "../back-ldap/back-ldap.h"
35 #include "back-meta.h"
36
37 /*
38  * Set PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
39  */
40 #define PRINT_CONNTREE 0
41
42 /*
43  * meta_back_conn_cmp
44  *
45  * compares two struct metaconn based on the value of the conn pointer;
46  * used by avl stuff
47  */
48 int
49 meta_back_conn_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         
56         return SLAP_PTRCMP( mc1->mc_conn, mc2->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         metaconn_t      *mc1 = ( metaconn_t * )c1;
71         metaconn_t      *mc2 = ( metaconn_t * )c2;
72
73         return( ( mc1->mc_conn == mc2->mc_conn ) ? -1 : 0 );
74 }
75
76 /*
77  * Debug stuff (got it from libavl)
78  */
79 #if PRINT_CONNTREE > 0
80 static void
81 ravl_print( Avlnode *root, int depth )
82 {
83         int     i;
84         
85         if ( root == 0 ) {
86                 return;
87         }
88         
89         ravl_print( root->avl_right, depth + 1 );
90         
91         for ( i = 0; i < depth; i++ ) {
92                 printf( "    " );
93         }
94
95         printf( "c(%d) %d\n", ( ( metaconn_t * )root->avl_data )->mc_conn->c_connid, root->avl_bf );
96         
97         ravl_print( root->avl_left, depth + 1 );
98 }
99
100 static void
101 myprint( Avlnode *root )
102 {
103         printf( "********\n" );
104         
105         if ( root == 0 ) {
106                 printf( "\tNULL\n" );
107         } else {
108                 ravl_print( root, 0 );
109         }
110         
111         printf( "********\n" );
112 }
113 #endif /* PRINT_CONNTREE */
114 /*
115  * End of debug stuff
116  */
117
118 /*
119  * metaconn_alloc
120  * 
121  * Allocates a connection structure, making room for all the referenced targets
122  */
123 static metaconn_t *
124 metaconn_alloc(
125         Operation               *op )
126 {
127         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
128         metaconn_t      *mc;
129         int             i, ntargets = mi->mi_ntargets;
130
131         assert( ntargets > 0 );
132
133         /* malloc all in one */
134         mc = ( metaconn_t * )ch_malloc( sizeof( metaconn_t )
135                         + sizeof( metasingleconn_t ) * ntargets );
136         if ( mc == NULL ) {
137                 return NULL;
138         }
139
140         for ( i = 0; i < ntargets; i++ ) {
141                 mc->mc_conns[ i ].msc_ld = NULL;
142                 BER_BVZERO( &mc->mc_conns[ i ].msc_bound_ndn );
143                 BER_BVZERO( &mc->mc_conns[ i ].msc_cred );
144                 mc->mc_conns[ i ].msc_bound = META_UNBOUND;
145                 mc->mc_conns[ i ].msc_info = mi;
146         }
147
148         mc->mc_auth_target = META_BOUND_NONE;
149         ldap_pvt_thread_mutex_init( &mc->mc_mutex );
150         mc->mc_refcnt = 1;
151
152         return mc;
153 }
154
155 /*
156  * meta_back_conn_free
157  *
158  * clears a metaconn
159  */
160 void
161 meta_back_conn_free(
162         metaconn_t      *mc )
163 {
164         if ( mc == NULL ) {
165                 return;
166         }
167
168         ldap_pvt_thread_mutex_destroy( &mc->mc_mutex );
169         
170         free( mc );
171 }
172
173 /*
174  * meta_back_init_one_conn
175  * 
176  * Initializes one connection
177  */
178 int
179 meta_back_init_one_conn(
180         Operation               *op,
181         SlapReply               *rs,
182         metatarget_t            *mt, 
183         metasingleconn_t        *msc,
184         ldap_back_send_t        sendok )
185 {
186         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
187         int             vers;
188         dncookie        dc;
189
190         /*
191          * Already init'ed
192          */
193         if ( msc->msc_ld != NULL ) {
194                 rs->sr_err = LDAP_SUCCESS;
195                 goto error_return;
196         }
197        
198         /*
199          * Attempts to initialize the connection to the target ds
200          */
201         rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
202         if ( rs->sr_err != LDAP_SUCCESS ) {
203                 goto error_return;
204         }
205
206         /*
207          * Set LDAP version. This will always succeed: If the client
208          * bound with a particular version, then so can we.
209          */
210         vers = op->o_conn->c_protocol;
211         ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &vers );
212
213         /* automatically chase referrals ("chase-referrals"/"dont-chase-referrals" statement) */
214         if ( LDAP_BACK_CHASE_REFERRALS( mi ) ) {
215                 ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
216         }
217
218 #ifdef HAVE_TLS
219         /* start TLS ("start-tls"/"try-start-tls" statements) */
220         if ( ( LDAP_BACK_USE_TLS( mi ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( mi ) ) )
221                         && !ldap_is_ldaps_url( mt->mt_uri ) )
222         {
223 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
224                 /*
225                  * use asynchronous StartTLS
226                  * in case, chase referral (not implemented yet)
227                  */
228                 int             msgid;
229
230                 rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
231                 if ( rs->sr_err == LDAP_SUCCESS ) {
232                         LDAPMessage     *res = NULL;
233                         int             rc, nretries = mt->mt_nretries;
234                         struct timeval  tv = { 0, 0 };
235
236 retry:;
237                         rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
238                         if ( rc < 0 ) {
239                                 rs->sr_err = LDAP_OTHER;
240
241                         } else if ( rc == 0 ) {
242                                 if ( nretries != 0 ) {
243                                         if ( nretries > 0 ) {
244                                                 nretries--;
245                                         }
246                                         tv.tv_sec = 0;
247                                         tv.tv_usec = 100000;
248                                         goto retry;
249                                 }
250                                 rs->sr_err = LDAP_OTHER;
251
252                         } else if ( rc == LDAP_RES_EXTENDED ) {
253                                 struct berval   *data = NULL;
254
255                                 rs->sr_err = ldap_parse_extended_result( msc->msc_ld, res,
256                                                 NULL, &data, 0 );
257                                 if ( rs->sr_err == LDAP_SUCCESS ) {
258                                         rs->sr_err = ldap_result2error( msc->msc_ld, res, 1 );
259                                         res = NULL;
260                                         
261                                         /* FIXME: in case a referral 
262                                          * is returned, should we try
263                                          * using it instead of the 
264                                          * configured URI? */
265                                         if ( rs->sr_err == LDAP_SUCCESS ) {
266                                                 ldap_install_tls( msc->msc_ld );
267
268                                         } else if ( rs->sr_err == LDAP_REFERRAL ) {
269                                                 rs->sr_err = LDAP_OTHER;
270                                                 rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
271                                         }
272
273                                         if ( data ) {
274                                                 if ( data->bv_val ) {
275                                                         ber_memfree( data->bv_val );
276                                                 }
277                                                 ber_memfree( data );
278                                         }
279                                 }
280
281                         } else {
282                                 rs->sr_err = LDAP_OTHER;
283                         }
284
285                         if ( res != NULL ) {
286                                 ldap_msgfree( res );
287                         }
288                 }
289 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
290                 /*
291                  * use synchronous StartTLS
292                  */
293                 rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
294 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
295
296                 /* if StartTLS is requested, only attempt it if the URL
297                  * is not "ldaps://"; this may occur not only in case
298                  * of misconfiguration, but also when used in the chain 
299                  * overlay, where the "uri" can be parsed out of a referral */
300                 if ( rs->sr_err == LDAP_SERVER_DOWN
301                                 || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( mi ) ) )
302                 {
303                         ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
304                         goto error_return;
305                 }
306         }
307 #endif /* HAVE_TLS */
308
309         /*
310          * Set the network timeout if set
311          */
312         if ( mi->mi_network_timeout != 0 ) {
313                 struct timeval  network_timeout;
314
315                 network_timeout.tv_usec = 0;
316                 network_timeout.tv_sec = mi->mi_network_timeout;
317
318                 ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
319                                 (void *)&network_timeout );
320         }
321
322         /*
323          * Sets a cookie for the rewrite session
324          */
325         ( void )rewrite_session_init( mt->mt_rwmap.rwm_rw, op->o_conn );
326
327         /*
328          * If the connection DN is not null, an attempt to rewrite it is made
329          */
330         if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
331                 dc.target = mt;
332                 dc.conn = op->o_conn;
333                 dc.rs = rs;
334                 dc.ctx = "bindDN";
335                 
336                 /*
337                  * Rewrite the bind dn if needed
338                  */
339                 if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
340                                         &msc->msc_bound_ndn ) )
341                 {
342                         goto error_return;
343                 }
344
345                 /* copy the DN idf needed */
346                 if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
347                         ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
348                 }
349
350                 assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
351
352         } else {
353                 ber_str2bv( "", 0, 1, &msc->msc_bound_ndn );
354         }
355
356         msc->msc_bound = META_UNBOUND;
357
358 error_return:;
359         if ( rs->sr_err != LDAP_SUCCESS ) {
360                 rs->sr_err = slap_map_api2result( rs );
361                 if ( sendok & LDAP_BACK_SENDERR ) {
362                         send_ldap_result( op, rs );
363                         rs->sr_text = NULL;
364                 }
365         }
366
367         return rs->sr_err;
368 }
369
370 /*
371  * meta_back_retry
372  * 
373  * Retries one connection
374  */
375 int
376 meta_back_retry(
377         Operation               *op,
378         SlapReply               *rs,
379         metaconn_t              *mc,
380         int                     candidate,
381         ldap_back_send_t        sendok )
382 {
383         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
384         metatarget_t            *mt = &mi->mi_targets[ candidate ];
385         int                     rc;
386         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
387
388         ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
389
390         ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
391         msc->msc_ld = NULL;
392         msc->msc_bound = 0;
393
394         /* mc here must be the regular mc, reset and ready for init */
395         rc = meta_back_init_one_conn( op, rs, mt, msc, sendok );
396
397         if ( rc == LDAP_SUCCESS ) {
398                 rc = meta_back_single_dobind( op, rs, mc, candidate,
399                                 sendok, mt->mt_nretries );
400         }
401
402         ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
403
404         return rc == LDAP_SUCCESS ? 1 : 0;
405 }
406
407 /*
408  * callback for unique candidate selection
409  */
410 static int
411 meta_back_conn_cb( Operation *op, SlapReply *rs )
412 {
413         assert( op->o_tag == LDAP_REQ_SEARCH );
414
415         switch ( rs->sr_type ) {
416         case REP_SEARCH:
417                 ((int *)op->o_callback->sc_private)[0] = (int)op->o_private;
418                 break;
419
420         case REP_SEARCHREF:
421         case REP_RESULT:
422                 break;
423
424         default:
425                 return rs->sr_err;
426         }
427
428         return 0;
429 }
430
431
432 static int
433 meta_back_get_candidate(
434         Operation       *op,
435         SlapReply       *rs,
436         struct berval   *ndn )
437 {
438         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
439         int             candidate;
440
441         /*
442          * tries to get a unique candidate
443          * (takes care of default target)
444          */
445         candidate = meta_back_select_unique_candidate( mi, ndn );
446
447         /*
448          * if any is found, inits the connection
449          */
450         if ( candidate == META_TARGET_NONE ) {
451                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
452                 rs->sr_text = "no suitable candidate target found";
453
454         } else if ( candidate == META_TARGET_MULTIPLE ) {
455                 Filter          f = { 0 };
456                 Operation       op2 = *op;
457                 SlapReply       rs2 = { 0 };
458                 slap_callback   cb2 = { 0 };
459                 int             rc;
460
461                 /* try to get a unique match for the request ndn
462                  * among the multiple candidates available */
463                 op2.o_tag = LDAP_REQ_SEARCH;
464                 op2.o_req_dn = *ndn;
465                 op2.o_req_ndn = *ndn;
466                 op2.ors_scope = LDAP_SCOPE_BASE;
467                 op2.ors_deref = LDAP_DEREF_NEVER;
468                 op2.ors_attrs = slap_anlist_no_attrs;
469                 op2.ors_attrsonly = 0;
470                 op2.ors_limit = NULL;
471                 op2.ors_slimit = 1;
472                 op2.ors_tlimit = SLAP_NO_LIMIT;
473
474                 f.f_choice = LDAP_FILTER_PRESENT;
475                 f.f_desc = slap_schema.si_ad_objectClass;
476                 op2.ors_filter = &f;
477                 BER_BVSTR( &op2.ors_filterstr, "(objectClass=*)" );
478
479                 op2.o_callback = &cb2;
480                 cb2.sc_response = meta_back_conn_cb;
481                 cb2.sc_private = (void *)&candidate;
482
483                 rc = op->o_bd->be_search( &op2, &rs2 );
484
485                 switch ( rs2.sr_err ) {
486                 case LDAP_SUCCESS:
487                 default:
488                         rs->sr_err = rs2.sr_err;
489                         break;
490
491                 case LDAP_SIZELIMIT_EXCEEDED:
492                         /* if multiple candidates can serve the operation,
493                          * and a default target is defined, and it is
494                          * a candidate, try using it (FIXME: YMMV) */
495                         if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
496                                 && meta_back_is_candidate( &mi->mi_targets[ mi->mi_defaulttarget ].mt_nsuffix,
497                                                 ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
498                         {
499                                 candidate = mi->mi_defaulttarget;
500                                 rs->sr_err = LDAP_SUCCESS;
501                                 rs->sr_text = NULL;
502
503                         } else {
504                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
505                                 rs->sr_text = "cannot select unique candidate target";
506                         }
507                         break;
508                 }
509         }
510
511         return candidate;
512 }
513
514 static void
515 meta_back_candidates_keyfree(
516         void            *key,
517         void            *data )
518 {
519         metacandidates_t        *mc = (metacandidates_t *)data;
520
521         ber_memfree_x( mc->mc_candidates, NULL );
522         ber_memfree_x( data, NULL );
523 }
524
525 SlapReply *
526 meta_back_candidates_get( Operation *op )
527 {
528         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
529         metacandidates_t        *mc;
530         SlapReply               *rs;
531
532         if ( op->o_threadctx ) {
533                 void            *data = NULL;
534
535                 ldap_pvt_thread_pool_getkey( op->o_threadctx,
536                                 meta_back_candidates_keyfree, &data, NULL );
537                 mc = (metacandidates_t *)data;
538
539         } else {
540                 mc = mi->mi_candidates;
541         }
542
543         if ( mc == NULL ) {
544                 mc = ch_calloc( sizeof( metacandidates_t ), 1 );
545                 mc->mc_ntargets = mi->mi_ntargets;
546                 mc->mc_candidates = ch_calloc( sizeof( SlapReply ), mc->mc_ntargets );
547                 if ( op->o_threadctx ) {
548                         void            *data = NULL;
549
550                         data = (void *)mc;
551                         ldap_pvt_thread_pool_setkey( op->o_threadctx,
552                                         meta_back_candidates_keyfree, data,
553                                         meta_back_candidates_keyfree );
554
555                 } else {
556                         mi->mi_candidates = mc;
557                 }
558
559         } else if ( mc->mc_ntargets < mi->mi_ntargets ) {
560                 /* NOTE: in the future, may want to allow back-config
561                  * to add/remove targets from back-meta... */
562                 mc->mc_ntargets = mi->mi_ntargets;
563                 mc->mc_candidates = ch_realloc( mc->mc_candidates,
564                                 sizeof( SlapReply ) * mc->mc_ntargets );
565         }
566
567         return mc->mc_candidates;
568 }
569
570 /*
571  * meta_back_getconn
572  * 
573  * Prepares the connection structure
574  * 
575  * RATIONALE:
576  *
577  * - determine what DN is being requested:
578  *
579  *      op      requires candidate      checks
580  *
581  *      add     unique                  parent of o_req_ndn
582  *      bind    unique^*[/all]          o_req_ndn [no check]
583  *      compare unique^+                o_req_ndn
584  *      delete  unique                  o_req_ndn
585  *      modify  unique                  o_req_ndn
586  *      search  any                     o_req_ndn
587  *      modrdn  unique[, unique]        o_req_ndn[, orr_nnewSup]
588  *
589  * - for ops that require the candidate to be unique, in case of multiple
590  *   occurrences an internal search with sizeLimit=1 is performed
591  *   if a unique candidate can actually be determined.  If none is found,
592  *   the operation aborts; if multiple are found, the default target
593  *   is used if defined and candidate; otherwise the operation aborts.
594  *
595  * *^note: actually, the bind operation is handled much like a search;
596  *   i.e. the bind is broadcast to all candidate targets.
597  *
598  * +^note: actually, the compare operation is handled much like a search;
599  *   i.e. the compare is broadcast to all candidate targets, while checking
600  *   that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
601  *   returned.
602  */
603 metaconn_t *
604 meta_back_getconn(
605         Operation               *op,
606         SlapReply               *rs,
607         int                     *candidate,
608         ldap_back_send_t        sendok )
609 {
610         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
611         metaconn_t      *mc, mc_curr;
612         int             cached = META_TARGET_NONE,
613                         i = META_TARGET_NONE,
614                         err = LDAP_SUCCESS,
615                         new_conn = 0,
616                         ncandidates = 0;
617
618
619         meta_op_type    op_type = META_OP_REQUIRE_SINGLE;
620         int             parent = 0,
621                         newparent = 0;
622         struct berval   ndn = op->o_req_ndn,
623                         pndn;
624
625         SlapReply       *candidates = meta_back_candidates_get( op );
626
627         /* Searches for a metaconn in the avl tree */
628         mc_curr.mc_conn = op->o_conn;
629 retry_lock:;
630         switch ( ldap_pvt_thread_mutex_trylock( &mi->mi_conn_mutex ) ) {
631         case LDAP_PVT_THREAD_EBUSY:
632         default:
633                 ldap_pvt_thread_yield();
634                 goto retry_lock;
635
636         case 0:
637                 break;
638         }
639         mc = (metaconn_t *)avl_find( mi->mi_conntree, 
640                 (caddr_t)&mc_curr, meta_back_conn_cmp );
641         ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
642
643         switch ( op->o_tag ) {
644         case LDAP_REQ_ADD:
645                 /* if we go to selection, the entry must not exist,
646                  * and we must be able to resolve the parent */
647                 parent = 1;
648                 dnParent( &ndn, &pndn );
649                 break;
650
651         case LDAP_REQ_MODRDN:
652                 /* if nnewSuperior is not NULL, it must resolve
653                  * to the same candidate as the req_ndn */
654                 if ( op->orr_nnewSup ) {
655                         newparent = 1;
656                 }
657                 break;
658
659         case LDAP_REQ_BIND:
660                 /* if bound as rootdn, the backend must bind to all targets
661                  * with the administrative identity */
662                 if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
663                         op_type = META_OP_REQUIRE_ALL;
664                 }
665                 break;
666
667         case LDAP_REQ_DELETE:
668         case LDAP_REQ_MODIFY:
669                 /* just a unique candidate */
670                 break;
671
672         case LDAP_REQ_COMPARE:
673         case LDAP_REQ_SEARCH:
674                 /* allow multiple candidates for the searchBase */
675                 op_type = META_OP_ALLOW_MULTIPLE;
676                 break;
677
678         default:
679                 /* right now, just break (exop?) */
680                 break;
681         }
682
683         /*
684          * require all connections ...
685          */
686         if ( op_type == META_OP_REQUIRE_ALL ) {
687
688                 /* Looks like we didn't get a bind. Open a new session... */
689                 if ( !mc ) {
690                         mc = metaconn_alloc( op );
691                         mc->mc_conn = op->o_conn;
692                         new_conn = 1;
693                 }
694
695                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
696
697                         /*
698                          * The target is activated; if needed, it is
699                          * also init'd
700                          */
701                         int lerr = meta_back_init_one_conn( op, rs, &mi->mi_targets[ i ],
702                                         &mc->mc_conns[ i ], sendok );
703                         if ( lerr == LDAP_SUCCESS ) {
704                                 candidates[ i ].sr_tag = META_CANDIDATE;
705                                 ncandidates++;
706                                 
707                         } else {
708                                 
709                                 /*
710                                  * FIXME: in case one target cannot
711                                  * be init'd, should the other ones
712                                  * be tried?
713                                  */
714                                 candidates[ i ].sr_tag = META_NOT_CANDIDATE;
715                                 err = lerr;
716                                 continue;
717                         }
718                 }
719
720                 if ( ncandidates == 0 ) {
721                         if ( new_conn ) {
722                                 meta_back_conn_free( mc );
723                         }
724
725                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
726                         rs->sr_text = "Unable to select valid candidates";
727
728                         if ( sendok & LDAP_BACK_SENDERR ) {
729                                 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
730                                         rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
731                                 }
732                                 send_ldap_result( op, rs );
733                                 rs->sr_text = NULL;
734                                 rs->sr_matched = NULL;
735                         }
736
737                         return NULL;
738                 }
739
740                 goto done;
741         }
742         
743         /*
744          * looks in cache, if any
745          */
746         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
747                 cached = i = meta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
748         }
749
750         if ( op_type == META_OP_REQUIRE_SINGLE ) {
751                 int     j;
752
753                 for ( j = 0; j < mi->mi_ntargets; j++ ) {
754                         candidates[ j ].sr_tag = META_NOT_CANDIDATE;
755                 }
756
757                 /*
758                  * tries to get a unique candidate
759                  * (takes care of default target)
760                  */
761                 if ( i == META_TARGET_NONE ) {
762                         i = meta_back_get_candidate( op, rs, &ndn );
763
764                         if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && parent ) {
765                                 i = meta_back_get_candidate( op, rs, &pndn );
766                         }
767         
768                         if ( rs->sr_err != LDAP_SUCCESS ) {
769                                 if ( sendok & LDAP_BACK_SENDERR ) {
770                                         if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
771                                                 rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
772                                         }
773                                         send_ldap_result( op, rs );
774                                         rs->sr_text = NULL;
775                                         rs->sr_matched = NULL;
776                                 }
777                                 return NULL;
778                         }
779                 }
780
781                 if ( newparent && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
782                 {
783                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
784                         rs->sr_text = "cross-target rename not supported";
785                         if ( sendok & LDAP_BACK_SENDERR ) {
786                                 send_ldap_result( op, rs );
787                                 rs->sr_text = NULL;
788                         }
789                         return NULL;
790                 }
791
792                 Debug( LDAP_DEBUG_TRACE,
793         "==>meta_back_getconn: got target %d for ndn=\"%s\" from cache\n",
794                                 i, op->o_req_ndn.bv_val, 0 );
795
796                 /* Retries searching for a metaconn in the avl tree */
797                 mc_curr.mc_conn = op->o_conn;
798 retry_lock2:;
799                 switch ( ldap_pvt_thread_mutex_trylock( &mi->mi_conn_mutex ) ) {
800                 case LDAP_PVT_THREAD_EBUSY:
801                 default:
802                         ldap_pvt_thread_yield();
803                         goto retry_lock2;
804
805                 case 0:
806                         break;
807                 }
808                 mc = (metaconn_t *)avl_find( mi->mi_conntree, 
809                         (caddr_t)&mc_curr, meta_back_conn_cmp );
810                 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
811
812                 /* Looks like we didn't get a bind. Open a new session... */
813                 if ( !mc ) {
814                         mc = metaconn_alloc( op );
815                         mc->mc_conn = op->o_conn;
816                         new_conn = 1;
817                 }
818
819                 /*
820                  * Clear all other candidates
821                  */
822                 ( void )meta_clear_unused_candidates( op, i );
823
824                 /*
825                  * The target is activated; if needed, it is
826                  * also init'd. In case of error, meta_back_init_one_conn
827                  * sends the appropriate result.
828                  */
829                 err = meta_back_init_one_conn( op, rs, &mi->mi_targets[ i ],
830                                 &mc->mc_conns[ i ], sendok );
831                 if ( err != LDAP_SUCCESS ) {
832                         /*
833                          * FIXME: in case one target cannot
834                          * be init'd, should the other ones
835                          * be tried?
836                          */
837                         candidates[ i ].sr_tag = META_NOT_CANDIDATE;
838                         if ( new_conn ) {
839                                 ( void )meta_clear_one_candidate( &mc->mc_conns[ i ] );
840                                 meta_back_conn_free( mc );
841                         }
842                         return NULL;
843                 }
844
845                 candidates[ i ].sr_tag = META_CANDIDATE;
846                 ncandidates++;
847
848                 if ( candidate ) {
849                         *candidate = i;
850                 }
851
852         /*
853          * if no unique candidate ...
854          */
855         } else {
856
857                 /* Looks like we didn't get a bind. Open a new session... */
858                 if ( !mc ) {
859                         mc = metaconn_alloc( op );
860                         mc->mc_conn = op->o_conn;
861                         new_conn = 1;
862                 }
863
864                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
865                         if ( i == cached 
866                                 || meta_back_is_candidate( &mi->mi_targets[ i ].mt_nsuffix,
867                                                 &op->o_req_ndn, LDAP_SCOPE_SUBTREE ) )
868                         {
869
870                                 /*
871                                  * The target is activated; if needed, it is
872                                  * also init'd
873                                  */
874                                 int lerr = meta_back_init_one_conn( op, rs,
875                                                 &mi->mi_targets[ i ],
876                                                 &mc->mc_conns[ i ], sendok );
877                                 if ( lerr == LDAP_SUCCESS ) {
878                                         candidates[ i ].sr_tag = META_CANDIDATE;
879                                         ncandidates++;
880
881                                         Debug( LDAP_DEBUG_TRACE, "%s: meta_back_init_one_conn(%d)\n",
882                                                 op->o_log_prefix, i, 0 );
883
884                                 } else {
885                                 
886                                         /*
887                                          * FIXME: in case one target cannot
888                                          * be init'd, should the other ones
889                                          * be tried?
890                                          */
891                                         if ( new_conn ) {
892                                                 ( void )meta_clear_one_candidate( &mc->mc_conns[ i ] );
893                                         }
894                                         candidates[ i ].sr_tag = META_NOT_CANDIDATE;
895                                         err = lerr;
896
897                                         Debug( LDAP_DEBUG_ANY, "%s: meta_back_init_one_conn(%d) failed: %d\n",
898                                                 op->o_log_prefix, i, lerr );
899
900                                         continue;
901                                 }
902
903                         } else {
904                                 if ( new_conn ) {
905                                         ( void )meta_clear_one_candidate( &mc->mc_conns[ i ] );
906                                 }
907                                 candidates[ i ].sr_tag = META_NOT_CANDIDATE;
908                         }
909                 }
910
911                 if ( ncandidates == 0 ) {
912                         if ( new_conn ) {
913                                 meta_back_conn_free( mc );
914                         }
915
916                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
917                         rs->sr_text = "Unable to select valid candidates";
918
919                         if ( sendok & LDAP_BACK_SENDERR ) {
920                                 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
921                                         rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
922                                 }
923                                 send_ldap_result( op, rs );
924                                 rs->sr_text = NULL;
925                                 rs->sr_matched = NULL;
926                         }
927
928                         return NULL;
929                 }
930         }
931
932 done:;
933         /* clear out meta_back_init_one_conn non-fatal errors */
934         rs->sr_err = LDAP_SUCCESS;
935         rs->sr_text = NULL;
936
937         if ( new_conn ) {
938                 
939                 /*
940                  * Inserts the newly created metaconn in the avl tree
941                  */
942 retry_lock3:;
943                 switch ( ldap_pvt_thread_mutex_trylock( &mi->mi_conn_mutex ) ) {
944                 case LDAP_PVT_THREAD_EBUSY:
945                 default:
946                         ldap_pvt_thread_yield();
947                         goto retry_lock3;
948
949                 case 0:
950                         break;
951                 }
952                 err = avl_insert( &mi->mi_conntree, ( caddr_t )mc,
953                                 meta_back_conn_cmp, meta_back_conn_dup );
954
955 #if PRINT_CONNTREE > 0
956                 myprint( mi->mi_conntree );
957 #endif /* PRINT_CONNTREE */
958                 
959                 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
960
961                 /*
962                  * Err could be -1 in case a duplicate metaconn is inserted
963                  */
964                 if ( err != 0 ) {
965                         Debug( LDAP_DEBUG_ANY,
966                                 "%s meta_back_getconn: candidates=%d conn=%ld insert failed\n",
967                                 op->o_log_prefix, ncandidates, mc->mc_conn->c_connid );
968                 
969                         rs->sr_err = LDAP_OTHER;
970                         rs->sr_text = "Internal server error";
971                         meta_back_conn_free( mc );
972                         if ( sendok & LDAP_BACK_SENDERR ) {
973                                 send_ldap_result( op, rs );
974                                 rs->sr_text = NULL;
975                         }
976                         return NULL;
977                 }
978
979                 Debug( LDAP_DEBUG_TRACE,
980                         "%s meta_back_getconn: candidates=%d conn=%ld inserted\n",
981                         op->o_log_prefix, ncandidates, mc->mc_conn->c_connid );
982
983         } else {
984                 Debug( LDAP_DEBUG_TRACE,
985                         "%s meta_back_getconn: candidates=%d conn=%ld fetched\n",
986                         op->o_log_prefix, ncandidates, mc->mc_conn->c_connid );
987         }
988         
989         return mc;
990 }
991