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;
53 struct metaconn *lc1 = ( struct metaconn * )c1;
54 struct metaconn *lc2 = ( struct metaconn * )c2;
56 return SLAP_PTRCMP( lc1->mc_conn, lc2->mc_conn );
62 * returns -1 in case a duplicate struct metaconn has been inserted;
71 struct metaconn *lc1 = ( struct metaconn * )c1;
72 struct metaconn *lc2 = ( struct metaconn * )c2;
74 return( ( lc1->mc_conn == lc2->mc_conn ) ? -1 : 0 );
78 * Debug stuff (got it from libavl)
80 #if PRINT_CONNTREE > 0
82 ravl_print( Avlnode *root, int depth )
90 ravl_print( root->avl_right, depth + 1 );
92 for ( i = 0; i < depth; i++ ) {
96 printf( "c(%d) %d\n", ( ( struct metaconn * )root->avl_data )->mc_conn->c_connid, root->avl_bf );
98 ravl_print( root->avl_left, depth + 1 );
102 myprint( Avlnode *root )
104 printf( "********\n" );
107 printf( "\tNULL\n" );
109 ravl_print( root, 0 );
112 printf( "********\n" );
114 #endif /* PRINT_CONNTREE */
122 * Allocates a connection structure, making room for all the referenced targets
124 static struct metaconn *
125 metaconn_alloc( int ntargets )
129 assert( ntargets > 0 );
131 lc = ch_calloc( sizeof( struct metaconn ), 1 );
137 * make it a null-terminated array ...
139 lc->mc_conns = ch_calloc( sizeof( struct metasingleconn ), ntargets + 1 );
140 if ( lc->mc_conns == NULL ) {
145 /* FIXME: needed by META_LAST() */
146 lc->mc_conns[ ntargets ].msc_candidate = META_LAST_CONN;
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;
155 lc->mc_bound_target = META_BOUND_NONE;
174 if ( lc->mc_conns ) {
175 ch_free( lc->mc_conns );
184 * Initializes one connection
190 struct metatarget *lt,
191 struct metasingleconn *lsc,
193 ldap_back_send_t sendok )
195 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
202 if ( lsc->msc_ld != NULL ) {
203 rs->sr_err = LDAP_SUCCESS;
208 * Attempts to initialize the connection to the target ds
210 rs->sr_err = ldap_initialize( &lsc->msc_ld, lt->mt_uri );
211 if ( rs->sr_err != LDAP_SUCCESS ) {
216 * Set LDAP version. This will always succeed: If the client
217 * bound with a particular version, then so can we.
219 vers = op->o_conn->c_protocol;
220 ldap_set_option( lsc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &vers );
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 );
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 ) )
232 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
234 * use asynchronous StartTLS
235 * in case, chase referral (not implemented yet)
239 rs->sr_err = ldap_start_tls( lsc->msc_ld, NULL, NULL, &msgid );
240 if ( rs->sr_err == LDAP_SUCCESS ) {
241 LDAPMessage *res = NULL;
243 struct timeval tv = { 0, 0 };
246 rc = ldap_result( lsc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
248 rs->sr_err = LDAP_OTHER;
250 } else if ( rc == 0 ) {
257 rs->sr_err = LDAP_OTHER;
259 } else if ( rc == LDAP_RES_EXTENDED ) {
260 struct berval *data = NULL;
262 rs->sr_err = ldap_parse_extended_result( lsc->msc_ld, res,
264 if ( rs->sr_err == LDAP_SUCCESS ) {
265 rs->sr_err = ldap_result2error( lsc->msc_ld, res, 1 );
268 /* FIXME: in case a referral
269 * is returned, should we try
270 * using it instead of the
272 if ( rs->sr_err == LDAP_SUCCESS ) {
273 ldap_install_tls( lsc->msc_ld );
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";
281 if ( data->bv_val ) {
282 ber_memfree( data->bv_val );
289 rs->sr_err = LDAP_OTHER;
296 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
298 * use synchronous StartTLS
300 rs->sr_err = ldap_start_tls_s( lsc->msc_ld, NULL, NULL );
301 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
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 ) ) )
310 ldap_unbind_ext_s( lsc->msc_ld, NULL, NULL );
314 #endif /* HAVE_TLS */
317 * Set the network timeout if set
319 if ( li->mi_network_timeout != 0 ) {
320 struct timeval network_timeout;
322 network_timeout.tv_usec = 0;
323 network_timeout.tv_sec = li->mi_network_timeout;
325 ldap_set_option( lsc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
326 (void *)&network_timeout );
330 * Sets a cookie for the rewrite session
332 ( void )rewrite_session_init( lt->mt_rwmap.rwm_rw, op->o_conn );
335 * If the connection DN is not null, an attempt to rewrite it is made
337 if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
338 dc.rwmap = <->mt_rwmap;
339 dc.conn = op->o_conn;
344 * Rewrite the bind dn if needed
346 if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
347 &lsc->msc_bound_ndn ) )
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 );
357 assert( !BER_BVISNULL( &lsc->msc_bound_ndn ) );
360 ber_str2bv( "", 0, 1, &lsc->msc_bound_ndn );
363 lsc->msc_bound = META_UNBOUND;
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 );
376 * The candidate is activated
378 *candidate = META_CANDIDATE;
385 * callback for unique candidate selection
388 meta_back_conn_cb( Operation *op, SlapReply *rs )
390 assert( op->o_tag == LDAP_REQ_SEARCH );
392 switch ( rs->sr_type ) {
394 ((int *)op->o_callback->sc_private)[0] = (int)op->o_private;
410 meta_back_get_candidate(
415 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
419 * tries to get a unique candidate
420 * (takes care of default target)
422 candidate = meta_back_select_unique_candidate( li, ndn );
425 * if any is found, inits the connection
427 if ( candidate == META_TARGET_NONE ) {
428 rs->sr_err = LDAP_NO_SUCH_OBJECT;
429 rs->sr_text = "no suitable candidate target found";
431 } else if ( candidate == META_TARGET_MULTIPLE ) {
434 SlapReply rs2 = { 0 };
435 slap_callback cb2 = { 0 };
438 /* try to get a unique match for the request ndn
439 * among the multiple candidates available */
440 op2.o_tag = LDAP_REQ_SEARCH;
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;
449 op2.ors_tlimit = SLAP_NO_LIMIT;
451 f.f_choice = LDAP_FILTER_PRESENT;
452 f.f_desc = slap_schema.si_ad_objectClass;
454 BER_BVSTR( &op2.ors_filterstr, "(objectClass=*)" );
456 op2.o_callback = &cb2;
457 cb2.sc_response = meta_back_conn_cb;
458 cb2.sc_private = (void *)&candidate;
460 rc = op->o_bd->be_search( &op2, &rs2 );
462 switch ( rs2.sr_err ) {
465 rs->sr_err = rs2.sr_err;
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 ) )
475 candidate = li->mi_defaulttarget;
476 rs->sr_err = LDAP_SUCCESS;
480 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
481 rs->sr_text = "cannot select unique candidate target";
491 meta_back_candidate_keyfree( void *key, void *data )
493 ber_memfree_x( data, NULL );
497 meta_back_candidates_get( Operation *op )
499 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
502 if ( op->o_threadctx ) {
503 ldap_pvt_thread_pool_getkey( op->o_threadctx,
504 meta_back_candidate_keyfree, &data, NULL );
506 data = (void *)li->mi_candidates;
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 );
517 li->mi_candidates = (char *)data;
527 * Prepares the connection structure
531 * - determine what DN is being requested:
533 * op requires candidate checks
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]
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.
549 * *^note: actually, the bind operation is handled much like a search;
550 * i.e. the bind is broadcast to all candidate targets.
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
562 ldap_back_send_t sendok )
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,
571 meta_op_type op_type = META_OP_REQUIRE_SINGLE;
574 struct berval ndn = op->o_req_ndn;
576 char *candidates = meta_back_candidates_get( op );
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 );
585 switch ( op->o_tag ) {
587 /* if we go to selection, the entry must not exist,
588 * and we must be able to resolve the parent */
590 dnParent( &ndn, &ndn );
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 ) {
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;
609 case LDAP_REQ_DELETE:
610 case LDAP_REQ_MODIFY:
611 /* just a unique candidate */
614 case LDAP_REQ_COMPARE:
615 case LDAP_REQ_SEARCH:
616 /* allow multiple candidates for the searchBase */
617 op_type = META_OP_ALLOW_MULTIPLE;
621 /* right now, just break (exop?) */
626 * require all connections ...
628 if ( op_type == META_OP_REQUIRE_ALL ) {
630 /* Looks like we didn't get a bind. Open a new session... */
632 lc = metaconn_alloc( li->mi_ntargets );
633 lc->mc_conn = op->o_conn;
637 for ( i = 0; i < li->mi_ntargets; i++ ) {
640 * The target is activated; if needed, it is
643 int lerr = init_one_conn( op, rs, li->mi_targets[ i ],
644 &lc->mc_conns[ i ], &candidates[ i ],
646 if ( lerr != LDAP_SUCCESS ) {
649 * FIXME: in case one target cannot
650 * be init'd, should the other ones
653 candidates[ i ] = META_NOT_CANDIDATE;
655 ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
665 * looks in cache, if any
667 if ( li->mi_cache.ttl != META_DNCACHE_DISABLED ) {
668 cached = i = meta_dncache_get_target( &li->mi_cache, &op->o_req_ndn );
671 if ( op_type == META_OP_REQUIRE_SINGLE ) {
673 memset( candidates, META_NOT_CANDIDATE, sizeof( char ) * li->mi_ntargets );
676 * tries to get a unique candidate
677 * (takes care of default target)
679 if ( i == META_TARGET_NONE ) {
680 i = meta_back_get_candidate( op, rs, &ndn );
682 if ( rs->sr_err != LDAP_SUCCESS ) {
683 if ( sendok & LDAP_BACK_SENDERR ) {
684 send_ldap_result( op, rs );
691 if ( newparent && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
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 );
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 );
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 );
713 /* Looks like we didn't get a bind. Open a new session... */
715 lc = metaconn_alloc( li->mi_ntargets );
716 lc->mc_conn = op->o_conn;
721 * Clear all other candidates
723 ( void )meta_clear_unused_candidates( op, lc, i );
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.
730 err = init_one_conn( op, rs, li->mi_targets[ i ],
731 &lc->mc_conns[ i ], &candidates[ i ],
733 if ( err != LDAP_SUCCESS ) {
736 * FIXME: in case one target cannot
737 * be init'd, should the other ones
740 candidates[ i ] = META_NOT_CANDIDATE;
742 ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
753 * if no unique candidate ...
757 /* Looks like we didn't get a bind. Open a new session... */
759 lc = metaconn_alloc( li->mi_ntargets );
760 lc->mc_conn = op->o_conn;
764 for ( i = 0; i < li->mi_ntargets; i++ ) {
766 || meta_back_is_candidate( &li->mi_targets[ i ]->mt_nsuffix,
771 * The target is activated; if needed, it is
774 int lerr = init_one_conn( op, rs,
779 if ( lerr != LDAP_SUCCESS ) {
782 * FIXME: in case one target cannot
783 * be init'd, should the other ones
786 candidates[ i ] = META_NOT_CANDIDATE;
788 ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
795 candidates[ i ] = META_NOT_CANDIDATE;
801 /* clear out init_one_conn non-fatal errors */
802 rs->sr_err = LDAP_SUCCESS;
808 * Inserts the newly created metaconn in the avl tree
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 );
814 #if PRINT_CONNTREE > 0
815 myprint( li->mi_conntree );
816 #endif /* PRINT_CONNTREE */
818 ldap_pvt_thread_mutex_unlock( &li->mi_conn_mutex );
820 Debug( LDAP_DEBUG_TRACE,
821 "=>meta_back_getconn: conn %ld inserted\n",
822 lc->mc_conn->c_connid, 0, 0 );
825 * Err could be -1 in case a duplicate metaconn is inserted
828 rs->sr_err = LDAP_OTHER;
829 rs->sr_text = "Internal server error";
831 if ( sendok & LDAP_BACK_SENDERR ) {
832 send_ldap_result( op, rs );
839 Debug( LDAP_DEBUG_TRACE,
840 "=>meta_back_getconn: conn %ld fetched\n",
841 lc->mc_conn->c_connid, 0, 0 );