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 /* malloc once only; leave an extra one for one-past-end */
132 lc = ch_malloc( sizeof( struct metaconn )
133 + sizeof( struct metasingleconn ) * ( ntargets + 1 ) );
138 lc->mc_conns = (struct metasingleconn *)&lc[ 1 ];
140 /* FIXME: needed by META_LAST() */
141 lc->mc_conns[ ntargets ].msc_candidate = META_LAST_CONN;
143 for ( ; ntargets-- > 0; ) {
144 lc->mc_conns[ ntargets ].msc_ld = NULL;
145 BER_BVZERO( &lc->mc_conns[ ntargets ].msc_bound_ndn );
146 BER_BVZERO( &lc->mc_conns[ ntargets ].msc_cred );
147 lc->mc_conns[ ntargets ].msc_bound = META_UNBOUND;
150 lc->mc_bound_target = META_BOUND_NONE;
175 * Initializes one connection
181 struct metatarget *lt,
182 struct metasingleconn *lsc,
184 ldap_back_send_t sendok )
186 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
193 if ( lsc->msc_ld != NULL ) {
194 rs->sr_err = LDAP_SUCCESS;
199 * Attempts to initialize the connection to the target ds
201 rs->sr_err = ldap_initialize( &lsc->msc_ld, lt->mt_uri );
202 if ( rs->sr_err != LDAP_SUCCESS ) {
207 * Set LDAP version. This will always succeed: If the client
208 * bound with a particular version, then so can we.
210 vers = op->o_conn->c_protocol;
211 ldap_set_option( lsc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &vers );
213 /* automatically chase referrals ("chase-referrals"/"dont-chase-referrals" statement) */
214 if ( LDAP_BACK_CHASE_REFERRALS( li ) ) {
215 ldap_set_option( lsc->msc_ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
219 /* start TLS ("start-tls"/"try-start-tls" statements) */
220 if ( ( LDAP_BACK_USE_TLS( li ) || ( op->o_conn->c_is_tls && LDAP_BACK_PROPAGATE_TLS( li ) ) )
221 && !ldap_is_ldaps_url( lt->mt_uri ) )
223 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
225 * use asynchronous StartTLS
226 * in case, chase referral (not implemented yet)
230 rs->sr_err = ldap_start_tls( lsc->msc_ld, NULL, NULL, &msgid );
231 if ( rs->sr_err == LDAP_SUCCESS ) {
232 LDAPMessage *res = NULL;
234 struct timeval tv = { 0, 0 };
237 rc = ldap_result( lsc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
239 rs->sr_err = LDAP_OTHER;
241 } else if ( rc == 0 ) {
248 rs->sr_err = LDAP_OTHER;
250 } else if ( rc == LDAP_RES_EXTENDED ) {
251 struct berval *data = NULL;
253 rs->sr_err = ldap_parse_extended_result( lsc->msc_ld, res,
255 if ( rs->sr_err == LDAP_SUCCESS ) {
256 rs->sr_err = ldap_result2error( lsc->msc_ld, res, 1 );
259 /* FIXME: in case a referral
260 * is returned, should we try
261 * using it instead of the
263 if ( rs->sr_err == LDAP_SUCCESS ) {
264 ldap_install_tls( lsc->msc_ld );
266 } else if ( rs->sr_err == LDAP_REFERRAL ) {
267 rs->sr_err = LDAP_OTHER;
268 rs->sr_text = "unwilling to chase referral returned by Start TLS exop";
272 if ( data->bv_val ) {
273 ber_memfree( data->bv_val );
280 rs->sr_err = LDAP_OTHER;
287 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
289 * use synchronous StartTLS
291 rs->sr_err = ldap_start_tls_s( lsc->msc_ld, NULL, NULL );
292 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
294 /* if StartTLS is requested, only attempt it if the URL
295 * is not "ldaps://"; this may occur not only in case
296 * of misconfiguration, but also when used in the chain
297 * overlay, where the "uri" can be parsed out of a referral */
298 if ( rs->sr_err == LDAP_SERVER_DOWN
299 || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( li ) ) )
301 ldap_unbind_ext_s( lsc->msc_ld, NULL, NULL );
305 #endif /* HAVE_TLS */
308 * Set the network timeout if set
310 if ( li->mi_network_timeout != 0 ) {
311 struct timeval network_timeout;
313 network_timeout.tv_usec = 0;
314 network_timeout.tv_sec = li->mi_network_timeout;
316 ldap_set_option( lsc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
317 (void *)&network_timeout );
321 * Sets a cookie for the rewrite session
323 ( void )rewrite_session_init( lt->mt_rwmap.rwm_rw, op->o_conn );
326 * If the connection DN is not null, an attempt to rewrite it is made
328 if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
329 dc.rwmap = <->mt_rwmap;
330 dc.conn = op->o_conn;
335 * Rewrite the bind dn if needed
337 if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
338 &lsc->msc_bound_ndn ) )
343 /* copy the DN idf needed */
344 if ( lsc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
345 ber_dupbv( &lsc->msc_bound_ndn, &op->o_conn->c_dn );
348 assert( !BER_BVISNULL( &lsc->msc_bound_ndn ) );
351 ber_str2bv( "", 0, 1, &lsc->msc_bound_ndn );
354 lsc->msc_bound = META_UNBOUND;
357 if ( rs->sr_err != LDAP_SUCCESS ) {
358 rs->sr_err = slap_map_api2result( rs );
359 if ( sendok & LDAP_BACK_SENDERR ) {
360 send_ldap_result( op, rs );
367 * The candidate is activated
369 *candidate = META_CANDIDATE;
376 * callback for unique candidate selection
379 meta_back_conn_cb( Operation *op, SlapReply *rs )
381 assert( op->o_tag == LDAP_REQ_SEARCH );
383 switch ( rs->sr_type ) {
385 ((int *)op->o_callback->sc_private)[0] = (int)op->o_private;
401 meta_back_get_candidate(
406 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
410 * tries to get a unique candidate
411 * (takes care of default target)
413 candidate = meta_back_select_unique_candidate( li, ndn );
416 * if any is found, inits the connection
418 if ( candidate == META_TARGET_NONE ) {
419 rs->sr_err = LDAP_NO_SUCH_OBJECT;
420 rs->sr_text = "no suitable candidate target found";
422 } else if ( candidate == META_TARGET_MULTIPLE ) {
425 SlapReply rs2 = { 0 };
426 slap_callback cb2 = { 0 };
429 /* try to get a unique match for the request ndn
430 * among the multiple candidates available */
431 op2.o_tag = LDAP_REQ_SEARCH;
433 op2.o_req_ndn = *ndn;
434 op2.ors_scope = LDAP_SCOPE_BASE;
435 op2.ors_deref = LDAP_DEREF_NEVER;
436 op2.ors_attrs = slap_anlist_no_attrs;
437 op2.ors_attrsonly = 0;
438 op2.ors_limit = NULL;
440 op2.ors_tlimit = SLAP_NO_LIMIT;
442 f.f_choice = LDAP_FILTER_PRESENT;
443 f.f_desc = slap_schema.si_ad_objectClass;
445 BER_BVSTR( &op2.ors_filterstr, "(objectClass=*)" );
447 op2.o_callback = &cb2;
448 cb2.sc_response = meta_back_conn_cb;
449 cb2.sc_private = (void *)&candidate;
451 rc = op->o_bd->be_search( &op2, &rs2 );
453 switch ( rs2.sr_err ) {
456 rs->sr_err = rs2.sr_err;
459 case LDAP_SIZELIMIT_EXCEEDED:
460 /* if multiple candidates can serve the operation,
461 * and a default target is defined, and it is
462 * a candidate, try using it (FIXME: YMMV) */
463 if ( li->mi_defaulttarget != META_DEFAULT_TARGET_NONE
464 && meta_back_is_candidate( &li->mi_targets[ li->mi_defaulttarget ]->mt_nsuffix, ndn ) )
466 candidate = li->mi_defaulttarget;
467 rs->sr_err = LDAP_SUCCESS;
471 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
472 rs->sr_text = "cannot select unique candidate target";
482 meta_back_candidate_keyfree( void *key, void *data )
484 ber_memfree_x( data, NULL );
488 meta_back_candidates_get( Operation *op )
490 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
493 if ( op->o_threadctx ) {
494 ldap_pvt_thread_pool_getkey( op->o_threadctx,
495 meta_back_candidate_keyfree, &data, NULL );
497 data = (void *)li->mi_candidates;
500 if ( data == NULL ) {
501 data = ber_memalloc_x( sizeof( char ) * li->mi_ntargets, NULL );
502 if ( op->o_threadctx ) {
503 ldap_pvt_thread_pool_setkey( op->o_threadctx,
504 meta_back_candidate_keyfree, data,
505 meta_back_candidate_keyfree );
508 li->mi_candidates = (char *)data;
518 * Prepares the connection structure
522 * - determine what DN is being requested:
524 * op requires candidate checks
526 * add unique parent of o_req_ndn
527 * bind unique^*[/all] o_req_ndn [no check]
528 * compare unique^+ o_req_ndn
529 * delete unique o_req_ndn
530 * modify unique o_req_ndn
531 * search any o_req_ndn
532 * modrdn unique[, unique] o_req_ndn[, orr_nnewSup]
534 * - for ops that require the candidate to be unique, in case of multiple
535 * occurrences an internal search with sizeLimit=1 is performed
536 * if a unique candidate can actually be determined. If none is found,
537 * the operation aborts; if multiple are found, the default target
538 * is used if defined and candidate; otherwise the operation aborts.
540 * *^note: actually, the bind operation is handled much like a search;
541 * i.e. the bind is broadcast to all candidate targets.
543 * +^note: actually, the compare operation is handled much like a search;
544 * i.e. the compare is broadcast to all candidate targets, while checking
545 * that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
553 ldap_back_send_t sendok )
555 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
556 struct metaconn *lc, lc_curr;
557 int cached = META_TARGET_NONE,
558 i = META_TARGET_NONE,
562 meta_op_type op_type = META_OP_REQUIRE_SINGLE;
565 struct berval ndn = op->o_req_ndn;
567 char *candidates = meta_back_candidates_get( op );
569 /* Searches for a metaconn in the avl tree */
570 lc_curr.mc_conn = op->o_conn;
571 ldap_pvt_thread_mutex_lock( &li->mi_conn_mutex );
572 lc = (struct metaconn *)avl_find( li->mi_conntree,
573 (caddr_t)&lc_curr, meta_back_conn_cmp );
574 ldap_pvt_thread_mutex_unlock( &li->mi_conn_mutex );
576 switch ( op->o_tag ) {
578 /* if we go to selection, the entry must not exist,
579 * and we must be able to resolve the parent */
581 dnParent( &ndn, &ndn );
584 case LDAP_REQ_MODRDN:
585 /* if nnewSuperior is not NULL, it must resolve
586 * to the same candidate as the req_ndn */
587 if ( op->orr_nnewSup ) {
593 /* if bound as rootdn, the backend must bind to all targets
594 * with the administrative identity */
595 if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
596 op_type = META_OP_REQUIRE_ALL;
600 case LDAP_REQ_DELETE:
601 case LDAP_REQ_MODIFY:
602 /* just a unique candidate */
605 case LDAP_REQ_COMPARE:
606 case LDAP_REQ_SEARCH:
607 /* allow multiple candidates for the searchBase */
608 op_type = META_OP_ALLOW_MULTIPLE;
612 /* right now, just break (exop?) */
617 * require all connections ...
619 if ( op_type == META_OP_REQUIRE_ALL ) {
621 /* Looks like we didn't get a bind. Open a new session... */
623 lc = metaconn_alloc( li->mi_ntargets );
624 lc->mc_conn = op->o_conn;
628 for ( i = 0; i < li->mi_ntargets; i++ ) {
631 * The target is activated; if needed, it is
634 int lerr = init_one_conn( op, rs, li->mi_targets[ i ],
635 &lc->mc_conns[ i ], &candidates[ i ],
637 if ( lerr != LDAP_SUCCESS ) {
640 * FIXME: in case one target cannot
641 * be init'd, should the other ones
644 candidates[ i ] = META_NOT_CANDIDATE;
646 ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
656 * looks in cache, if any
658 if ( li->mi_cache.ttl != META_DNCACHE_DISABLED ) {
659 cached = i = meta_dncache_get_target( &li->mi_cache, &op->o_req_ndn );
662 if ( op_type == META_OP_REQUIRE_SINGLE ) {
664 memset( candidates, META_NOT_CANDIDATE, sizeof( char ) * li->mi_ntargets );
667 * tries to get a unique candidate
668 * (takes care of default target)
670 if ( i == META_TARGET_NONE ) {
671 i = meta_back_get_candidate( op, rs, &ndn );
673 if ( rs->sr_err != LDAP_SUCCESS ) {
674 if ( sendok & LDAP_BACK_SENDERR ) {
675 send_ldap_result( op, rs );
682 if ( newparent && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
684 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
685 rs->sr_text = "cross-target rename not supported";
686 if ( sendok & LDAP_BACK_SENDERR ) {
687 send_ldap_result( op, rs );
693 Debug( LDAP_DEBUG_CACHE,
694 "==>meta_back_getconn: got target %d for ndn=\"%s\" from cache\n",
695 i, op->o_req_ndn.bv_val, 0 );
697 /* Retries searching for a metaconn in the avl tree */
698 lc_curr.mc_conn = op->o_conn;
699 ldap_pvt_thread_mutex_lock( &li->mi_conn_mutex );
700 lc = (struct metaconn *)avl_find( li->mi_conntree,
701 (caddr_t)&lc_curr, meta_back_conn_cmp );
702 ldap_pvt_thread_mutex_unlock( &li->mi_conn_mutex );
704 /* Looks like we didn't get a bind. Open a new session... */
706 lc = metaconn_alloc( li->mi_ntargets );
707 lc->mc_conn = op->o_conn;
712 * Clear all other candidates
714 ( void )meta_clear_unused_candidates( op, lc, i );
717 * The target is activated; if needed, it is
718 * also init'd. In case of error, init_one_conn
719 * sends the appropriate result.
721 err = init_one_conn( op, rs, li->mi_targets[ i ],
722 &lc->mc_conns[ i ], &candidates[ i ],
724 if ( err != LDAP_SUCCESS ) {
727 * FIXME: in case one target cannot
728 * be init'd, should the other ones
731 candidates[ i ] = META_NOT_CANDIDATE;
733 ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
744 * if no unique candidate ...
748 /* Looks like we didn't get a bind. Open a new session... */
750 lc = metaconn_alloc( li->mi_ntargets );
751 lc->mc_conn = op->o_conn;
755 for ( i = 0; i < li->mi_ntargets; i++ ) {
757 || meta_back_is_candidate( &li->mi_targets[ i ]->mt_nsuffix,
762 * The target is activated; if needed, it is
765 int lerr = init_one_conn( op, rs,
770 if ( lerr != LDAP_SUCCESS ) {
773 * FIXME: in case one target cannot
774 * be init'd, should the other ones
777 candidates[ i ] = META_NOT_CANDIDATE;
779 ( void )meta_clear_one_candidate( &lc->mc_conns[ i ] );
786 candidates[ i ] = META_NOT_CANDIDATE;
792 /* clear out init_one_conn non-fatal errors */
793 rs->sr_err = LDAP_SUCCESS;
799 * Inserts the newly created metaconn in the avl tree
801 ldap_pvt_thread_mutex_lock( &li->mi_conn_mutex );
802 err = avl_insert( &li->mi_conntree, ( caddr_t )lc,
803 meta_back_conn_cmp, meta_back_conn_dup );
805 #if PRINT_CONNTREE > 0
806 myprint( li->mi_conntree );
807 #endif /* PRINT_CONNTREE */
809 ldap_pvt_thread_mutex_unlock( &li->mi_conn_mutex );
811 Debug( LDAP_DEBUG_TRACE,
812 "=>meta_back_getconn: conn %ld inserted\n",
813 lc->mc_conn->c_connid, 0, 0 );
816 * Err could be -1 in case a duplicate metaconn is inserted
819 rs->sr_err = LDAP_OTHER;
820 rs->sr_text = "Internal server error";
822 if ( sendok & LDAP_BACK_SENDERR ) {
823 send_ldap_result( op, rs );
830 Debug( LDAP_DEBUG_TRACE,
831 "=>meta_back_getconn: conn %ld fetched\n",
832 lc->mc_conn->c_connid, 0, 0 );