2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2005 The OpenLDAP Foundation.
5 * Portions Copyright 2001-2003 Pierangelo Masarati.
6 * Portions Copyright 1999-2003 Howard Chu.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
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>.
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software and subsequently enhanced by Pierangelo
27 #include <ac/socket.h>
28 #include <ac/string.h>
33 #include "../back-ldap/back-ldap.h"
34 #include "back-meta.h"
37 * Set PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
39 #define PRINT_CONNTREE 0
44 * compares two struct metaconn based on the value of the conn pointer;
52 struct metaconn *mc1 = ( struct metaconn * )c1;
53 struct metaconn *mc2 = ( struct metaconn * )c2;
55 return SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
61 * returns -1 in case a duplicate struct metaconn has been inserted;
69 struct metaconn *mc1 = ( struct metaconn * )c1;
70 struct metaconn *mc2 = ( struct metaconn * )c2;
72 return( ( mc1->mc_conn == mc2->mc_conn ) ? -1 : 0 );
76 * Debug stuff (got it from libavl)
78 #if PRINT_CONNTREE > 0
80 ravl_print( Avlnode *root, int depth )
88 ravl_print( root->avl_right, depth + 1 );
90 for ( i = 0; i < depth; i++ ) {
94 printf( "c(%d) %d\n", ( ( struct metaconn * )root->avl_data )->mc_conn->c_connid, root->avl_bf );
96 ravl_print( root->avl_left, depth + 1 );
100 myprint( Avlnode *root )
102 printf( "********\n" );
105 printf( "\tNULL\n" );
107 ravl_print( root, 0 );
110 printf( "********\n" );
112 #endif /* PRINT_CONNTREE */
120 * Allocates a connection structure, making room for all the referenced targets
122 static struct metaconn *
123 metaconn_alloc( int ntargets )
127 assert( ntargets > 0 );
129 /* malloc once only; leave an extra one for one-past-end */
130 mc = ch_malloc( sizeof( struct metaconn )
131 + sizeof( struct metasingleconn ) * ( ntargets + 1 ) );
136 mc->mc_conns = (struct metasingleconn *)&mc[ 1 ];
138 /* FIXME: needed by META_LAST() */
139 mc->mc_conns[ ntargets ].msc_candidate = META_LAST_CONN;
141 for ( ; ntargets-- > 0; ) {
142 mc->mc_conns[ ntargets ].msc_ld = NULL;
143 BER_BVZERO( &mc->mc_conns[ ntargets ].msc_bound_ndn );
144 BER_BVZERO( &mc->mc_conns[ ntargets ].msc_cred );
145 mc->mc_conns[ ntargets ].msc_bound = META_UNBOUND;
148 mc->mc_auth_target = META_BOUND_NONE;
149 ldap_pvt_thread_mutex_init( &mc->mc_mutex );
155 * meta_back_conn_free
160 meta_back_conn_free( struct metaconn *mc )
166 ldap_pvt_thread_mutex_destroy( &mc->mc_mutex );
172 * meta_back_init_one_conn
174 * Initializes one connection
177 meta_back_init_one_conn(
180 struct metatarget *lt,
181 struct metasingleconn *msc,
182 ldap_back_send_t sendok )
184 struct metainfo *mi = ( struct metainfo * )op->o_bd->be_private;
191 if ( msc->msc_ld != NULL ) {
192 rs->sr_err = LDAP_SUCCESS;
197 * Attempts to initialize the connection to the target ds
199 rs->sr_err = ldap_initialize( &msc->msc_ld, lt->mt_uri );
200 if ( rs->sr_err != LDAP_SUCCESS ) {
205 * Set LDAP version. This will always succeed: If the client
206 * bound with a particular version, then so can we.
208 vers = op->o_conn->c_protocol;
209 ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &vers );
211 /* automatically chase referrals ("chase-referrals"/"dont-chase-referrals" statement) */
212 if ( LDAP_BACK_CHASE_REFERRALS( mi ) ) {
213 ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
217 /* start TLS ("start-tls"/"try-start-tls" statements) */
218 if ( ( LDAP_BACK_USE_TLS( mi ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( mi ) ) )
219 && !ldap_is_ldaps_url( lt->mt_uri ) )
221 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
223 * use asynchronous StartTLS
224 * in case, chase referral (not implemented yet)
228 rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
229 if ( rs->sr_err == LDAP_SUCCESS ) {
230 LDAPMessage *res = NULL;
232 struct timeval tv = { 0, 0 };
235 rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
237 rs->sr_err = LDAP_OTHER;
239 } else if ( rc == 0 ) {
246 rs->sr_err = LDAP_OTHER;
248 } else if ( rc == LDAP_RES_EXTENDED ) {
249 struct berval *data = NULL;
251 rs->sr_err = ldap_parse_extended_result( msc->msc_ld, res,
253 if ( rs->sr_err == LDAP_SUCCESS ) {
254 rs->sr_err = ldap_result2error( msc->msc_ld, res, 1 );
257 /* FIXME: in case a referral
258 * is returned, should we try
259 * using it instead of the
261 if ( rs->sr_err == LDAP_SUCCESS ) {
262 ldap_install_tls( msc->msc_ld );
264 } else if ( rs->sr_err == LDAP_REFERRAL ) {
265 rs->sr_err = LDAP_OTHER;
266 rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
270 if ( data->bv_val ) {
271 ber_memfree( data->bv_val );
278 rs->sr_err = LDAP_OTHER;
285 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
287 * use synchronous StartTLS
289 rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
290 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
292 /* if StartTLS is requested, only attempt it if the URL
293 * is not "ldaps://"; this may occur not only in case
294 * of misconfiguration, but also when used in the chain
295 * overlay, where the "uri" can be parsed out of a referral */
296 if ( rs->sr_err == LDAP_SERVER_DOWN
297 || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( mi ) ) )
299 ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
303 #endif /* HAVE_TLS */
306 * Set the network timeout if set
308 if ( mi->mi_network_timeout != 0 ) {
309 struct timeval network_timeout;
311 network_timeout.tv_usec = 0;
312 network_timeout.tv_sec = mi->mi_network_timeout;
314 ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
315 (void *)&network_timeout );
319 * Sets a cookie for the rewrite session
321 ( void )rewrite_session_init( lt->mt_rwmap.rwm_rw, op->o_conn );
324 * If the connection DN is not null, an attempt to rewrite it is made
326 if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
327 dc.rwmap = <->mt_rwmap;
328 dc.conn = op->o_conn;
333 * Rewrite the bind dn if needed
335 if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
336 &msc->msc_bound_ndn ) )
341 /* copy the DN idf needed */
342 if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
343 ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
346 assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
349 ber_str2bv( "", 0, 1, &msc->msc_bound_ndn );
352 msc->msc_bound = META_UNBOUND;
355 if ( rs->sr_err != LDAP_SUCCESS ) {
356 rs->sr_err = slap_map_api2result( rs );
357 if ( sendok & LDAP_BACK_SENDERR ) {
358 send_ldap_result( op, rs );
369 * Retries one connection
377 ldap_back_send_t sendok )
379 struct metainfo *mi = (struct metainfo *)op->o_bd->be_private;
380 struct metatarget *lt = mi->mi_targets[ candidate ];
382 struct metasingleconn *msc = &mc->mc_conns[ candidate ];
384 ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
386 ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
390 /* mc here must be the regular mc, reset and ready for init */
391 rc = meta_back_init_one_conn( op, rs, lt, msc, sendok );
393 if ( rc == LDAP_SUCCESS ) {
394 rc = meta_back_single_dobind( op, msc, sendok, 0 );
397 ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
399 return rc == LDAP_SUCCESS ? 1 : 0;
403 * callback for unique candidate selection
406 meta_back_conn_cb( Operation *op, SlapReply *rs )
408 assert( op->o_tag == LDAP_REQ_SEARCH );
410 switch ( rs->sr_type ) {
412 ((int *)op->o_callback->sc_private)[0] = (int)op->o_private;
428 meta_back_get_candidate(
433 struct metainfo *mi = ( struct metainfo * )op->o_bd->be_private;
437 * tries to get a unique candidate
438 * (takes care of default target)
440 candidate = meta_back_select_unique_candidate( mi, ndn );
443 * if any is found, inits the connection
445 if ( candidate == META_TARGET_NONE ) {
446 rs->sr_err = LDAP_NO_SUCH_OBJECT;
447 rs->sr_text = "no suitable candidate target found";
449 } else if ( candidate == META_TARGET_MULTIPLE ) {
452 SlapReply rs2 = { 0 };
453 slap_callback cb2 = { 0 };
456 /* try to get a unique match for the request ndn
457 * among the multiple candidates available */
458 op2.o_tag = LDAP_REQ_SEARCH;
460 op2.o_req_ndn = *ndn;
461 op2.ors_scope = LDAP_SCOPE_BASE;
462 op2.ors_deref = LDAP_DEREF_NEVER;
463 op2.ors_attrs = slap_anlist_no_attrs;
464 op2.ors_attrsonly = 0;
465 op2.ors_limit = NULL;
467 op2.ors_tlimit = SLAP_NO_LIMIT;
469 f.f_choice = LDAP_FILTER_PRESENT;
470 f.f_desc = slap_schema.si_ad_objectClass;
472 BER_BVSTR( &op2.ors_filterstr, "(objectClass=*)" );
474 op2.o_callback = &cb2;
475 cb2.sc_response = meta_back_conn_cb;
476 cb2.sc_private = (void *)&candidate;
478 rc = op->o_bd->be_search( &op2, &rs2 );
480 switch ( rs2.sr_err ) {
483 rs->sr_err = rs2.sr_err;
486 case LDAP_SIZELIMIT_EXCEEDED:
487 /* if multiple candidates can serve the operation,
488 * and a default target is defined, and it is
489 * a candidate, try using it (FIXME: YMMV) */
490 if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
491 && meta_back_is_candidate( &mi->mi_targets[ mi->mi_defaulttarget ]->mt_nsuffix,
492 ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
494 candidate = mi->mi_defaulttarget;
495 rs->sr_err = LDAP_SUCCESS;
499 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
500 rs->sr_text = "cannot select unique candidate target";
510 meta_back_candidate_keyfree( void *key, void *data )
512 ber_memfree_x( data, NULL );
516 meta_back_candidates_get( Operation *op )
518 struct metainfo *mi = ( struct metainfo * )op->o_bd->be_private;
521 if ( op->o_threadctx ) {
522 ldap_pvt_thread_pool_getkey( op->o_threadctx,
523 meta_back_candidate_keyfree, &data, NULL );
525 data = (void *)mi->mi_candidates;
528 if ( data == NULL ) {
529 data = ber_memalloc_x( sizeof( char ) * mi->mi_ntargets, NULL );
530 if ( op->o_threadctx ) {
531 ldap_pvt_thread_pool_setkey( op->o_threadctx,
532 meta_back_candidate_keyfree, data,
533 meta_back_candidate_keyfree );
536 mi->mi_candidates = (char *)data;
546 * Prepares the connection structure
550 * - determine what DN is being requested:
552 * op requires candidate checks
554 * add unique parent of o_req_ndn
555 * bind unique^*[/all] o_req_ndn [no check]
556 * compare unique^+ o_req_ndn
557 * delete unique o_req_ndn
558 * modify unique o_req_ndn
559 * search any o_req_ndn
560 * modrdn unique[, unique] o_req_ndn[, orr_nnewSup]
562 * - for ops that require the candidate to be unique, in case of multiple
563 * occurrences an internal search with sizeLimit=1 is performed
564 * if a unique candidate can actually be determined. If none is found,
565 * the operation aborts; if multiple are found, the default target
566 * is used if defined and candidate; otherwise the operation aborts.
568 * *^note: actually, the bind operation is handled much like a search;
569 * i.e. the bind is broadcast to all candidate targets.
571 * +^note: actually, the compare operation is handled much like a search;
572 * i.e. the compare is broadcast to all candidate targets, while checking
573 * that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
581 ldap_back_send_t sendok )
583 struct metainfo *mi = ( struct metainfo * )op->o_bd->be_private;
584 struct metaconn *mc, mc_curr;
585 int cached = META_TARGET_NONE,
586 i = META_TARGET_NONE,
590 meta_op_type op_type = META_OP_REQUIRE_SINGLE;
593 struct berval ndn = op->o_req_ndn,
596 char *candidates = meta_back_candidates_get( op );
598 /* Searches for a metaconn in the avl tree */
599 mc_curr.mc_conn = op->o_conn;
600 ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
601 mc = (struct metaconn *)avl_find( mi->mi_conntree,
602 (caddr_t)&mc_curr, meta_back_conn_cmp );
603 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
605 switch ( op->o_tag ) {
607 /* if we go to selection, the entry must not exist,
608 * and we must be able to resolve the parent */
610 dnParent( &ndn, &pndn );
613 case LDAP_REQ_MODRDN:
614 /* if nnewSuperior is not NULL, it must resolve
615 * to the same candidate as the req_ndn */
616 if ( op->orr_nnewSup ) {
622 /* if bound as rootdn, the backend must bind to all targets
623 * with the administrative identity */
624 if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
625 op_type = META_OP_REQUIRE_ALL;
629 case LDAP_REQ_DELETE:
630 case LDAP_REQ_MODIFY:
631 /* just a unique candidate */
634 case LDAP_REQ_COMPARE:
635 case LDAP_REQ_SEARCH:
636 /* allow multiple candidates for the searchBase */
637 op_type = META_OP_ALLOW_MULTIPLE;
641 /* right now, just break (exop?) */
646 * require all connections ...
648 if ( op_type == META_OP_REQUIRE_ALL ) {
650 /* Looks like we didn't get a bind. Open a new session... */
652 mc = metaconn_alloc( mi->mi_ntargets );
653 mc->mc_conn = op->o_conn;
657 for ( i = 0; i < mi->mi_ntargets; i++ ) {
660 * The target is activated; if needed, it is
663 int lerr = meta_back_init_one_conn( op, rs, mi->mi_targets[ i ],
664 &mc->mc_conns[ i ], sendok );
665 if ( lerr == LDAP_SUCCESS ) {
666 candidates[ i ] = META_CANDIDATE;
671 * FIXME: in case one target cannot
672 * be init'd, should the other ones
675 candidates[ i ] = META_NOT_CANDIDATE;
684 * looks in cache, if any
686 if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
687 cached = i = meta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
690 if ( op_type == META_OP_REQUIRE_SINGLE ) {
692 memset( candidates, META_NOT_CANDIDATE, sizeof( char ) * mi->mi_ntargets );
695 * tries to get a unique candidate
696 * (takes care of default target)
698 if ( i == META_TARGET_NONE ) {
699 i = meta_back_get_candidate( op, rs, &ndn );
701 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && parent ) {
702 i = meta_back_get_candidate( op, rs, &pndn );
705 if ( rs->sr_err != LDAP_SUCCESS ) {
706 if ( sendok & LDAP_BACK_SENDERR ) {
707 send_ldap_result( op, rs );
714 if ( newparent && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
716 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
717 rs->sr_text = "cross-target rename not supported";
718 if ( sendok & LDAP_BACK_SENDERR ) {
719 send_ldap_result( op, rs );
725 Debug( LDAP_DEBUG_CACHE,
726 "==>meta_back_getconn: got target %d for ndn=\"%s\" from cache\n",
727 i, op->o_req_ndn.bv_val, 0 );
729 /* Retries searching for a metaconn in the avl tree */
730 mc_curr.mc_conn = op->o_conn;
731 ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
732 mc = (struct metaconn *)avl_find( mi->mi_conntree,
733 (caddr_t)&mc_curr, meta_back_conn_cmp );
734 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
736 /* Looks like we didn't get a bind. Open a new session... */
738 mc = metaconn_alloc( mi->mi_ntargets );
739 mc->mc_conn = op->o_conn;
744 * Clear all other candidates
746 ( void )meta_clear_unused_candidates( op, mc, i );
749 * The target is activated; if needed, it is
750 * also init'd. In case of error, meta_back_init_one_conn
751 * sends the appropriate result.
753 err = meta_back_init_one_conn( op, rs, mi->mi_targets[ i ],
754 &mc->mc_conns[ i ], sendok );
755 if ( err == LDAP_SUCCESS ) {
756 candidates[ i ] = META_CANDIDATE;
761 * FIXME: in case one target cannot
762 * be init'd, should the other ones
765 candidates[ i ] = META_NOT_CANDIDATE;
767 ( void )meta_clear_one_candidate( &mc->mc_conns[ i ] );
768 meta_back_conn_free( mc );
778 * if no unique candidate ...
782 /* Looks like we didn't get a bind. Open a new session... */
784 mc = metaconn_alloc( mi->mi_ntargets );
785 mc->mc_conn = op->o_conn;
789 for ( i = 0; i < mi->mi_ntargets; i++ ) {
791 || meta_back_is_candidate( &mi->mi_targets[ i ]->mt_nsuffix,
792 &op->o_req_ndn, LDAP_SCOPE_SUBTREE ) )
796 * The target is activated; if needed, it is
799 int lerr = meta_back_init_one_conn( op, rs,
801 &mc->mc_conns[ i ], sendok );
802 if ( lerr == LDAP_SUCCESS ) {
803 candidates[ i ] = META_CANDIDATE;
808 * FIXME: in case one target cannot
809 * be init'd, should the other ones
812 candidates[ i ] = META_NOT_CANDIDATE;
815 Debug( LDAP_DEBUG_ANY, "%s: meta_back_init_one_conn(%d) failed: %d\n",
816 op->o_log_prefix, i, lerr );
822 candidates[ i ] = META_NOT_CANDIDATE;
828 /* clear out meta_back_init_one_conn non-fatal errors */
829 rs->sr_err = LDAP_SUCCESS;
835 * Inserts the newly created metaconn in the avl tree
837 ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
838 err = avl_insert( &mi->mi_conntree, ( caddr_t )mc,
839 meta_back_conn_cmp, meta_back_conn_dup );
841 #if PRINT_CONNTREE > 0
842 myprint( mi->mi_conntree );
843 #endif /* PRINT_CONNTREE */
845 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
847 Debug( LDAP_DEBUG_TRACE,
848 "=>meta_back_getconn: conn %ld inserted\n",
849 mc->mc_conn->c_connid, 0, 0 );
852 * Err could be -1 in case a duplicate metaconn is inserted
855 rs->sr_err = LDAP_OTHER;
856 rs->sr_text = "Internal server error";
857 meta_back_conn_free( mc );
858 if ( sendok & LDAP_BACK_SENDERR ) {
859 send_ldap_result( op, rs );
866 Debug( LDAP_DEBUG_TRACE,
867 "=>meta_back_getconn: conn %ld fetched\n",
868 mc->mc_conn->c_connid, 0, 0 );