meta_search_candidate_t asyncmeta_send_all_pending_ops(a_metaconn_t *mc, int candidate, void *ctx, int dolock) { a_metainfo_t *mi = mc->mc_info; bm_context_t *bc, *onext; a_metasingleconn_t *msc = &mc->mc_conns[candidate]; if ( dolock ) ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); msc->msc_active++; for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) { meta_search_candidate_t ret; onext = LDAP_STAILQ_NEXT(bc, bc_next); if (bc->candidates[candidate].sr_msgid != META_MSGID_NEED_BIND || bc->bc_active > 0 || bc->op->o_abandon > 0) { continue; } bc->op->o_threadctx = ctx; bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx ); slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx); bc->bc_active++; ret = asyncmeta_send_pending_op(bc, candidate); if (ret != META_SEARCH_CANDIDATE) { bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; bc->candidates[ candidate ].sr_type = REP_RESULT; bc->candidates[ candidate ].sr_err = bc->rs.sr_err; if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) || (asyncmeta_is_last_result(mc, bc, candidate) == 0)) { LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next); mc->pending_ops--; asyncmeta_send_ldap_result(bc, bc->op, &bc->rs); asyncmeta_clear_bm_context(bc); } } else { bc->bc_active--; } } msc->msc_active--; if ( dolock ) ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); return META_SEARCH_CANDIDATE; }
int asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc, int candidate) { a_metainfo_t *mi; a_metatarget_t *mt; a_metasingleconn_t *msc; Operation *op = bc->op; SlapReply *rs; int i, rc = LDAP_SUCCESS, sres; SlapReply *candidates; char **references = NULL; LDAPControl **ctrls = NULL; a_dncookie dc; LDAPMessage *msg; ber_int_t id; rs = &bc->rs; mi = mc->mc_info; mt = mi->mi_targets[ candidate ]; msc = &mc->mc_conns[ candidate ]; dc.op = op; dc.target = mt; dc.to_from = MASSAGE_REP; id = ldap_msgid(res); candidates = bc->candidates; i = candidate; while (res && !META_BACK_CONN_INVALID(msc)) { for (msg = ldap_first_message(msc->msc_ldr, res); msg; msg = ldap_next_message(msc->msc_ldr, msg)) { switch(ldap_msgtype(msg)) { case LDAP_RES_SEARCH_ENTRY: Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_handle_search_msg: msc %p entry\n", op->o_log_prefix, msc ); if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { /* don't retry any more... */ candidates[ i ].sr_type = REP_RESULT; } /* count entries returned by target */ candidates[ i ].sr_nentries++; if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !op->o_abandon) { rs->sr_err = asyncmeta_send_entry( &bc->copy_op, rs, mc, i, msg ); } else { goto err_cleanup; } switch ( rs->sr_err ) { case LDAP_SIZELIMIT_EXCEEDED: asyncmeta_send_ldap_result(bc, op, rs); rs->sr_err = LDAP_SUCCESS; goto err_cleanup; case LDAP_UNAVAILABLE: rs->sr_err = LDAP_OTHER; break; default: break; } bc->is_ok++; break; case LDAP_RES_SEARCH_REFERENCE: if ( META_BACK_TGT_NOREFS( mt ) ) { rs->sr_err = LDAP_OTHER; asyncmeta_send_ldap_result(bc, op, rs); goto err_cleanup; } if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { /* don't retry any more... */ candidates[ i ].sr_type = REP_RESULT; } bc->is_ok++; rc = ldap_parse_reference( msc->msc_ldr, msg, &references, &rs->sr_ctrls, 0 ); if ( rc != LDAP_SUCCESS || references == NULL ) { rs->sr_err = LDAP_OTHER; asyncmeta_send_ldap_result(bc, op, rs); goto err_cleanup; } /* FIXME: merge all and return at the end */ { int cnt; for ( cnt = 0; references[ cnt ]; cnt++ ) ; rs->sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ), op->o_tmpmemctx ); for ( cnt = 0; references[ cnt ]; cnt++ ) { ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ], op->o_tmpmemctx ); } BER_BVZERO( &rs->sr_ref[ cnt ] ); } { dc.memctx = op->o_tmpmemctx; ( void )asyncmeta_referral_result_rewrite( &dc, rs->sr_ref ); } if ( rs->sr_ref != NULL ) { if (!BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) { /* ignore return value by now */ ( void )send_search_reference( op, rs ); } ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx ); rs->sr_ref = NULL; } /* cleanup */ if ( references ) { ber_memvfree( (void **)references ); } if ( rs->sr_ctrls ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } break; case LDAP_RES_INTERMEDIATE: if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { /* don't retry any more... */ candidates[ i ].sr_type = REP_RESULT; } bc->is_ok++; /* FIXME: response controls * are passed without checks */ rs->sr_err = ldap_parse_intermediate( msc->msc_ldr, msg, (char **)&rs->sr_rspoid, &rs->sr_rspdata, &rs->sr_ctrls, 0 ); if ( rs->sr_err != LDAP_SUCCESS ) { candidates[ i ].sr_type = REP_RESULT; rs->sr_err = LDAP_OTHER; asyncmeta_send_ldap_result(bc, op, rs); goto err_cleanup; } slap_send_ldap_intermediate( op, rs ); if ( rs->sr_rspoid != NULL ) { ber_memfree( (char *)rs->sr_rspoid ); rs->sr_rspoid = NULL; } if ( rs->sr_rspdata != NULL ) { ber_bvfree( rs->sr_rspdata ); rs->sr_rspdata = NULL; } if ( rs->sr_ctrls != NULL ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } break; case LDAP_RES_SEARCH_RESULT: if ( mi->mi_idle_timeout != 0 ) { asyncmeta_set_msc_time(msc); } Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_handle_search_msg: msc %p result\n", op->o_log_prefix, msc ); candidates[ i ].sr_type = REP_RESULT; candidates[ i ].sr_msgid = META_MSGID_IGNORE; /* NOTE: ignores response controls * (and intermediate response controls * as well, except for those with search * references); this may not be correct, * but if they're not ignored then * back-meta would need to merge them * consistently (think of pagedResults...) */ /* FIXME: response controls? */ rs->sr_err = ldap_parse_result( msc->msc_ldr, msg, &candidates[ i ].sr_err, (char **)&candidates[ i ].sr_matched, (char **)&candidates[ i ].sr_text, &references, &ctrls /* &candidates[ i ].sr_ctrls (unused) */ , 0 ); if ( rs->sr_err != LDAP_SUCCESS ) { candidates[ i ].sr_err = rs->sr_err; sres = slap_map_api2result( &candidates[ i ] ); candidates[ i ].sr_type = REP_RESULT; goto finish; } rs->sr_err = candidates[ i ].sr_err; /* massage matchedDN if need be */ if ( candidates[ i ].sr_matched != NULL ) { struct berval match, mmatch; ber_str2bv( candidates[ i ].sr_matched, 0, 0, &match ); candidates[ i ].sr_matched = NULL; dc.memctx = NULL; asyncmeta_dn_massage( &dc, &match, &mmatch ); if ( mmatch.bv_val == match.bv_val ) { candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val ); } else { candidates[ i ].sr_matched = mmatch.bv_val; } bc->candidate_match++; ldap_memfree( match.bv_val ); } /* add references to array */ /* RFC 4511: referrals can only appear * if result code is LDAP_REFERRAL */ if ( references != NULL && references[ 0 ] != NULL && references[ 0 ][ 0 ] != '\0' ) { if ( rs->sr_err != LDAP_REFERRAL ) { Debug( LDAP_DEBUG_ANY, "%s asncmeta_search_result[%d]: " "got referrals with err=%d\n", op->o_log_prefix, i, rs->sr_err ); } else { BerVarray sr_ref; int cnt; for ( cnt = 0; references[ cnt ]; cnt++ ) ; sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ), op->o_tmpmemctx ); for ( cnt = 0; references[ cnt ]; cnt++ ) { ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ], op->o_tmpmemctx ); } BER_BVZERO( &sr_ref[ cnt ] ); dc.memctx = op->o_tmpmemctx; ( void )asyncmeta_referral_result_rewrite( &dc, sr_ref ); if ( rs->sr_v2ref == NULL ) { rs->sr_v2ref = sr_ref; } else { for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) { ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ], op->o_tmpmemctx ); } ber_memfree_x( sr_ref, op->o_tmpmemctx ); } } } else if ( rs->sr_err == LDAP_REFERRAL ) { Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d]: " "got err=%d with null " "or empty referrals\n", op->o_log_prefix, i, rs->sr_err ); rs->sr_err = LDAP_NO_SUCH_OBJECT; } /* cleanup */ ber_memvfree( (void **)references ); sres = slap_map_api2result( rs ); if ( candidates[ i ].sr_err == LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d] " "match=\"%s\" err=%ld", op->o_log_prefix, i, candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "", (long) candidates[ i ].sr_err ); } else { Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_result[%d] " "match=\"%s\" err=%ld (%s)", op->o_log_prefix, i, candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "", (long) candidates[ i ].sr_err, ldap_err2string( candidates[ i ].sr_err ) ); } switch ( sres ) { case LDAP_NO_SUCH_OBJECT: /* is_ok is touched any time a valid * (even intermediate) result is * returned; as a consequence, if * a candidate returns noSuchObject * it is ignored and the candidate * is simply demoted. */ if ( bc->is_ok ) { sres = LDAP_SUCCESS; } break; case LDAP_SUCCESS: if ( ctrls != NULL && ctrls[0] != NULL ) { #ifdef SLAPD_META_CLIENT_PR LDAPControl *pr_c; pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL ); if ( pr_c != NULL ) { BerElementBuffer berbuf; BerElement *ber = (BerElement *)&berbuf; ber_tag_t tag; ber_int_t prsize; struct berval prcookie; /* unsolicited, do not accept */ if ( mt->mt_ps == 0 ) { rs->sr_err = LDAP_OTHER; goto err_pr; } ber_init2( ber, &pr_c->ldctl_value, LBER_USE_DER ); tag = ber_scanf( ber, "{im}", &prsize, &prcookie ); if ( tag == LBER_ERROR ) { rs->sr_err = LDAP_OTHER; goto err_pr; } /* more pages? new search request */ if ( !BER_BVISNULL( &prcookie ) && !BER_BVISEMPTY( &prcookie ) ) { if ( mt->mt_ps > 0 ) { /* ignore size if specified */ prsize = 0; } else if ( prsize == 0 ) { /* guess the page size from the entries returned so far */ prsize = candidates[ i ].sr_nentries; } candidates[ i ].sr_nentries = 0; candidates[ i ].sr_msgid = META_MSGID_IGNORE; candidates[ i ].sr_type = REP_INTERMEDIATE; assert( candidates[ i ].sr_matched == NULL ); assert( candidates[ i ].sr_text == NULL ); assert( candidates[ i ].sr_ref == NULL ); switch ( asyncmeta_back_search_start( &bc->copy_op, rs, mc, bc, i, &prcookie, prsize, 1 ) ) { case META_SEARCH_CANDIDATE: assert( candidates[ i ].sr_msgid >= 0 ); ldap_controls_free( ctrls ); // goto free_message; case META_SEARCH_ERR: case META_SEARCH_NEED_BIND: err_pr:; candidates[ i ].sr_err = rs->sr_err; candidates[ i ].sr_type = REP_RESULT; if ( META_BACK_ONERR_STOP( mi ) ) { asyncmeta_send_ldap_result(bc, op, rs); ldap_controls_free( ctrls ); goto err_cleanup; } /* fallthru */ case META_SEARCH_NOT_CANDIDATE: /* means that asyncmeta_back_search_start() * failed but onerr == continue */ candidates[ i ].sr_msgid = META_MSGID_IGNORE; candidates[ i ].sr_type = REP_RESULT; break; default: /* impossible */ assert( 0 ); break; } break; } } #endif /* SLAPD_META_CLIENT_PR */ ldap_controls_free( ctrls ); } /* fallthru */ case LDAP_REFERRAL: bc->is_ok++; break; case LDAP_SIZELIMIT_EXCEEDED: /* if a target returned sizelimitExceeded * and the entry count is equal to the * proxy's limit, the target would have * returned more, and the error must be * propagated to the client; otherwise, * the target enforced a limit lower * than what requested by the proxy; * ignore it */ candidates[ i ].sr_err = rs->sr_err; if ( rs->sr_nentries == op->ors_slimit || META_BACK_ONERR_STOP( mi ) ) { const char *save_text; got_err: save_text = rs->sr_text; rs->sr_text = candidates[ i ].sr_text; asyncmeta_send_ldap_result(bc, op, rs); if (candidates[ i ].sr_text != NULL) { ch_free( (char *)candidates[ i ].sr_text ); candidates[ i ].sr_text = NULL; } rs->sr_text = save_text; ldap_controls_free( ctrls ); goto err_cleanup; } break; default: candidates[ i ].sr_err = rs->sr_err; if ( META_BACK_ONERR_STOP( mi ) ) { goto got_err; } break; } /* if this is the last result we will ever receive, send it back */ rc = rs->sr_err; if (asyncmeta_is_last_result(mc, bc, i) == 0) { Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_handle_search_msg: msc %p last result\n", op->o_log_prefix, msc ); asyncmeta_search_last_result(mc, bc, i, sres); err_cleanup: rc = rs->sr_err; ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); asyncmeta_drop_bc( mc, bc); asyncmeta_clear_bm_context(bc); ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); ldap_msgfree(res); return rc; } finish: break; default: continue; } } ldap_msgfree(res); res = NULL; if (candidates[ i ].sr_type != REP_RESULT) { struct timeval tv = {0}; rc = ldap_result( msc->msc_ldr, id, LDAP_MSG_RECEIVED, &tv, &res ); if (res != NULL) { msc->msc_result_time = slap_get_time(); } } } ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); bc->bc_active--; ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); return rc; }
/* This takes care to clean out the outbound queue in case we have a read error * sending back responses to the client */ int asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error, void* ctx) { bm_context_t *bc, *onext; int cleanup; Operation *op; SlapReply *rs; SlapReply *candidates; /* no outstanding ops, nothing to do but log */ Debug( LDAP_DEBUG_TRACE, "asyncmeta_op_read_error: ldr=%p\n", mc->mc_conns[candidate].msc_ldr ); ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); /*someone may be trying to write */ if (mc->mc_conns[candidate].msc_active <= 1) { asyncmeta_clear_one_msc(NULL, mc, candidate, 0, __FUNCTION__); } else { META_BACK_CONN_INVALID_SET(&mc->mc_conns[candidate]); } if (mc->pending_ops <= 0) { ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); return LDAP_SUCCESS; } for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) { onext = LDAP_STAILQ_NEXT(bc, bc_next); cleanup = 0; candidates = bc->candidates; /* was this op affected? */ if ( !META_IS_CANDIDATE( &candidates[ candidate ] ) ) continue; if (bc->op->o_abandon) { bc->bc_invalid = 1; continue; } if (bc->bc_active > 0) { continue; } bc->op->o_threadctx = ctx; bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx ); slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx); op = bc->op; rs = &bc->rs; switch (op->o_tag) { case LDAP_REQ_ADD: case LDAP_REQ_MODIFY: case LDAP_REQ_MODRDN: case LDAP_REQ_COMPARE: case LDAP_REQ_DELETE: rs->sr_err = LDAP_UNAVAILABLE; rs->sr_text = "Read error on connection to target"; asyncmeta_send_ldap_result( bc, op, rs ); cleanup = 1; break; case LDAP_REQ_SEARCH: { a_metainfo_t *mi = mc->mc_info; rs->sr_err = LDAP_UNAVAILABLE; rs->sr_text = "Read error on connection to target"; candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; candidates[ candidate ].sr_type = REP_RESULT; if ( (META_BACK_ONERR_STOP( mi ) || asyncmeta_is_last_result(mc, bc, candidate)) && op->o_conn) { asyncmeta_send_ldap_result( bc, op, rs ); cleanup = 1; } } break; default: break; } if (cleanup) { int j; a_metainfo_t *mi = mc->mc_info; for (j=0; j<mi->mi_ntargets; j++) { if (j != candidate && bc->candidates[j].sr_msgid >= 0 && mc->mc_conns[j].msc_ld != NULL) { asyncmeta_back_cancel( mc, op, bc->candidates[ j ].sr_msgid, j ); } } LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next); mc->pending_ops--; asyncmeta_clear_bm_context(bc); } } ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); return LDAP_SUCCESS; }
/* * meta_back_dobind */ int meta_back_dobind( Operation *op, SlapReply *rs, metaconn_t *mc, ldap_back_send_t sendok ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; int bound = 0, i, isroot = 0; SlapReply *candidates; if ( be_isroot( op ) ) { isroot = 1; } if ( LogTest( LDAP_DEBUG_TRACE ) ) { char buf[STRLENOF("4294967295U") + 1] = { 0 }; mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) ); Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind: conn=%s%s\n", op->o_log_prefix, buf, isroot ? " (isroot)" : "" ); } /* * all the targets are bound as pseudoroot */ if ( mc->mc_authz_target == META_BOUND_ALL ) { bound = 1; goto done; } candidates = meta_back_candidates_get( op ); for ( i = 0; i < mi->mi_ntargets; i++ ) { metatarget_t *mt = mi->mi_targets[ i ]; metasingleconn_t *msc = &mc->mc_conns[ i ]; int rc; /* * Not a candidate */ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { continue; } assert( msc->msc_ld != NULL ); /* * If the target is already bound it is skipped */ retry_binding:; ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); if ( LDAP_BACK_CONN_ISBOUND( msc ) || ( LDAP_BACK_CONN_ISANON( msc ) && mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) ) { ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); ++bound; continue; } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) { ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); ldap_pvt_thread_yield(); goto retry_binding; } LDAP_BACK_CONN_BINDING_SET( msc ); ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); rc = meta_back_single_dobind( op, rs, &mc, i, LDAP_BACK_DONTSEND, mt->mt_nretries, 1 ); /* * NOTE: meta_back_single_dobind() already retries; * in case of failure, it resets mc... */ if ( rc != LDAP_SUCCESS ) { char buf[ SLAP_TEXT_BUFLEN ]; if ( mc == NULL ) { /* meta_back_single_dobind() already sent * response and released connection */ goto send_err; } if ( rc == LDAP_UNAVAILABLE ) { /* FIXME: meta_back_retry() already re-calls * meta_back_single_dobind() */ if ( meta_back_retry( op, rs, &mc, i, sendok ) ) { goto retry_ok; } if ( mc != NULL ) { ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); LDAP_BACK_CONN_BINDING_CLEAR( msc ); meta_back_release_conn_lock( mi, mc, 0 ); ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); } return 0; } ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); LDAP_BACK_CONN_BINDING_CLEAR( msc ); ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); snprintf( buf, sizeof( buf ), "meta_back_dobind[%d]: (%s) err=%d (%s).", i, isroot ? op->o_bd->be_rootdn.bv_val : "anonymous", rc, ldap_err2string( rc ) ); Debug( LDAP_DEBUG_ANY, "%s %s\n", op->o_log_prefix, buf ); /* * null cred bind should always succeed * as anonymous, so a failure means * the target is no longer candidate possibly * due to technical reasons (remote host down?) * so better clear the handle */ /* leave the target candidate, but record the error for later use */ candidates[ i ].sr_err = rc; if ( META_BACK_ONERR_STOP( mi ) ) { bound = 0; goto done; } continue; } /* else */ retry_ok:; Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: " "(%s)\n", op->o_log_prefix, i, isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" ); ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); LDAP_BACK_CONN_BINDING_CLEAR( msc ); if ( isroot ) { LDAP_BACK_CONN_ISBOUND_SET( msc ); } else { LDAP_BACK_CONN_ISANON_SET( msc ); } ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); ++bound; } done:; if ( LogTest( LDAP_DEBUG_TRACE ) ) { char buf[STRLENOF("4294967295U") + 1] = { 0 }; mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) ); Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind: conn=%s bound=%d\n", op->o_log_prefix, buf, bound ); } if ( bound == 0 ) { meta_back_release_conn( mi, mc ); send_err:; if ( sendok & LDAP_BACK_SENDERR ) { if ( rs->sr_err == LDAP_SUCCESS ) { rs->sr_err = LDAP_BUSY; } send_ldap_result( op, rs ); } return 0; } return ( bound > 0 ); }
/* * meta_back_single_dobind */ int meta_back_single_dobind( Operation *op, SlapReply *rs, metaconn_t **mcp, int candidate, ldap_back_send_t sendok, int nretries, int dolock ) { metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private; metatarget_t *mt = mi->mi_targets[ candidate ]; metaconn_t *mc = *mcp; metasingleconn_t *msc = &mc->mc_conns[ candidate ]; int msgid; assert( !LDAP_BACK_CONN_ISBOUND( msc ) ); /* NOTE: this obsoletes pseudorootdn */ if ( op->o_conn != NULL && !op->o_do_not_cache && ( BER_BVISNULL( &msc->msc_bound_ndn ) || BER_BVISEMPTY( &msc->msc_bound_ndn ) || ( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) || ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) ) { (void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock ); } else { char *binddn = ""; struct berval cred = BER_BVC( "" ); /* use credentials if available */ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) && !BER_BVISNULL( &msc->msc_cred ) ) { binddn = msc->msc_bound_ndn.bv_val; cred = msc->msc_cred; } /* FIXME: should we check if at least some of the op->o_ctrls * can/should be passed? */ if(!dolock) { ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); } for (;;) { rs->sr_err = ldap_sasl_bind( msc->msc_ld, binddn, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid ); if ( rs->sr_err != LDAP_X_CONNECTING ) { break; } ldap_pvt_thread_yield(); } if(!dolock) { ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); } rs->sr_err = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock ); /* if bind succeeded, but anonymous, clear msc_bound_ndn */ if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) { if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) { ber_memfree( msc->msc_bound_ndn.bv_val ); BER_BVZERO( &msc->msc_bound_ndn ); } if ( !BER_BVISNULL( &msc->msc_cred ) ) { memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len ); ber_memfree( msc->msc_cred.bv_val ); BER_BVZERO( &msc->msc_cred ); } } } if ( rs->sr_err != LDAP_SUCCESS ) { if ( dolock ) { ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex ); } LDAP_BACK_CONN_BINDING_CLEAR( msc ); if ( META_BACK_ONERR_STOP( mi ) ) { LDAP_BACK_CONN_TAINTED_SET( mc ); meta_back_release_conn_lock( mi, mc, 0 ); *mcp = NULL; } if ( dolock ) { ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex ); } } if ( META_BACK_TGT_QUARANTINE( mt ) ) { meta_back_quarantine( op, rs, candidate ); } return rs->sr_err; }
/* * asyncmeta_dobind_init() * * initiates bind for a candidate target */ meta_search_candidate_t asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate) { SlapReply *candidates = bc->candidates; a_metainfo_t *mi = ( a_metainfo_t * )mc->mc_info; a_metatarget_t *mt = mi->mi_targets[ candidate ]; a_metasingleconn_t *msc = &mc->mc_conns[ candidate ]; ber_socket_t s; struct berval binddn = msc->msc_bound_ndn, cred = msc->msc_cred; int method; int rc; ber_int_t msgid; meta_search_candidate_t retcode; Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_search_dobind_init[%d]\n", op->o_log_prefix, candidate, 0 ); if ( mc->mc_authz_target == META_BOUND_ALL ) { return META_SEARCH_CANDIDATE; } retcode = META_SEARCH_BINDING; if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) { /* already bound (or anonymous) */ #ifdef DEBUG_205 char buf[ SLAP_TEXT_BUFLEN ] = { '\0' }; int bound = 0; if ( LDAP_BACK_CONN_ISBOUND( msc ) ) { bound = 1; } snprintf( buf, sizeof( buf ), " mc=%p ld=%p%s DN=\"%s\"", (void *)mc, (void *)msc->msc_ld, bound ? " bound" : " anonymous", bound == 0 ? "" : msc->msc_bound_ndn.bv_val ); Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n", op->o_log_prefix, candidate, buf ); #endif /* DEBUG_205 */ retcode = META_SEARCH_CANDIDATE; } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) { /* another thread is binding the target for this conn; wait */ #ifdef DEBUG_205 char buf[ SLAP_TEXT_BUFLEN ] = { '\0' }; snprintf( buf, sizeof( buf ), " mc=%p ld=%p needbind", (void *)mc, (void *)msc->msc_ld ); Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n", op->o_log_prefix, candidate, buf ); #endif /* DEBUG_205 */ candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND; retcode = META_SEARCH_NEED_BIND; } else { /* we'll need to bind the target for this conn */ #ifdef DEBUG_205 char buf[ SLAP_TEXT_BUFLEN ]; snprintf( buf, sizeof( buf ), " mc=%p ld=%p binding", (void *)mc, (void *)msc->msc_ld ); Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n", op->o_log_prefix, candidate, buf ); #endif /* DEBUG_205 */ if ( msc->msc_ld == NULL ) { /* for some reason (e.g. because formerly in "binding" * state, with eventual connection expiration or invalidation) * it was not initialized as expected */ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p ld=NULL\n", op->o_log_prefix, candidate, (void *)mc ); rc = asyncmeta_init_one_conn( op, rs, mc, candidate, LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 ); switch ( rc ) { case LDAP_SUCCESS: assert( msc->msc_ld != NULL ); break; case LDAP_SERVER_DOWN: case LDAP_UNAVAILABLE: goto down; default: goto other; } } LDAP_BACK_CONN_BINDING_SET( msc ); } if ( retcode != META_SEARCH_BINDING ) { return retcode; } if ( op->o_conn != NULL && !op->o_do_not_cache && ( BER_BVISNULL( &msc->msc_bound_ndn ) || BER_BVISEMPTY( &msc->msc_bound_ndn ) || ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) ) { rc = asyncmeta_back_proxy_authz_cred( mc, candidate, op, rs, LDAP_BACK_DONTSEND, &binddn, &cred, &method ); switch ( rc ) { case LDAP_SUCCESS: break; case LDAP_UNAVAILABLE: goto down; default: goto other; } /* NOTE: we copy things here, even if bind didn't succeed yet, * because the connection is not shared until bind is over */ if ( !BER_BVISNULL( &binddn ) ) { ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex); ber_bvreplace( &msc->msc_bound_ndn, &binddn ); if ( META_BACK_TGT_SAVECRED( mt ) && !BER_BVISNULL( &cred ) ) { if ( !BER_BVISNULL( &msc->msc_cred ) ) { memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len ); } ber_bvreplace( &msc->msc_cred, &cred ); } ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex); } if ( LDAP_BACK_CONN_ISBOUND( msc ) ) { /* apparently, idassert was configured with SASL bind, * so bind occurred inside meta_back_proxy_authz_cred() */ LDAP_BACK_CONN_BINDING_CLEAR( msc ); return META_SEARCH_CANDIDATE; } /* paranoid */ switch ( method ) { case LDAP_AUTH_NONE: case LDAP_AUTH_SIMPLE: /* do a simple bind with binddn, cred */ break; default: assert( 0 ); break; } } assert( msc->msc_ld != NULL ); if ( !BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &cred ) ) { /* bind anonymously? */ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p: " "non-empty dn with empty cred; binding anonymously\n", op->o_log_prefix, candidate, (void *)mc ); cred = slap_empty_bv; } else if ( BER_BVISEMPTY( &binddn ) && !BER_BVISEMPTY( &cred ) ) { /* error */ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p: " "empty dn with non-empty cred: error\n", op->o_log_prefix, candidate, (void *)mc ); rc = LDAP_OTHER; goto other; } retry_bind: rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid ); ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rc ); if (rc == LDAP_SERVER_DOWN ) { goto down; } candidates[ candidate ].sr_msgid = msgid; asyncmeta_set_msc_time(msc); #ifdef DEBUG_205 { char buf[ SLAP_TEXT_BUFLEN ]; snprintf( buf, sizeof( buf ), "asyncmeta_search_dobind_init[%d] mc=%p ld=%p rc=%d", candidate, (void *)mc, (void *)mc->mc_conns[ candidate ].msc_ld, rc ); Debug( LDAP_DEBUG_ANY, "### %s %s\n", op->o_log_prefix, buf, 0 ); } #endif /* DEBUG_205 */ switch ( rc ) { case LDAP_SUCCESS: assert( msgid >= 0 ); META_BINDING_SET( &candidates[ candidate ] ); rs->sr_err = LDAP_SUCCESS; return META_SEARCH_BINDING; case LDAP_X_CONNECTING: /* must retry, same conn */ candidates[ candidate ].sr_msgid = META_MSGID_CONNECTING; LDAP_BACK_CONN_BINDING_CLEAR( msc ); goto retry_bind; case LDAP_SERVER_DOWN: down:; retcode = META_SEARCH_ERR; rs->sr_err = LDAP_UNAVAILABLE; candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; break; /* fall thru */ default: other:; rs->sr_err = rc; rc = slap_map_api2result( rs ); candidates[ candidate ].sr_err = rc; if ( META_BACK_ONERR_STOP( mi ) ) { retcode = META_SEARCH_ERR; } else { retcode = META_SEARCH_NOT_CANDIDATE; } candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; break; } return retcode; }
meta_search_candidate_t asyncmeta_back_search_start( Operation *op, SlapReply *rs, a_metaconn_t *mc, bm_context_t *bc, int candidate, struct berval *prcookie, ber_int_t prsize ) { SlapReply *candidates = bc->candidates; a_metainfo_t *mi = ( a_metainfo_t * )mc->mc_info; a_metatarget_t *mt = mi->mi_targets[ candidate ]; a_metasingleconn_t *msc = &mc->mc_conns[ candidate ]; a_dncookie dc; struct berval realbase = op->o_req_dn; int realscope = op->ors_scope; struct berval mbase = BER_BVNULL; struct berval mfilter = BER_BVNULL; char **mapped_attrs = NULL; int rc; meta_search_candidate_t retcode; int timelimit; int nretries = 1; LDAPControl **ctrls = NULL; BerElement *ber; ber_int_t msgid; #ifdef SLAPD_META_CLIENT_PR LDAPControl **save_ctrls = NULL; #endif /* SLAPD_META_CLIENT_PR */ /* this should not happen; just in case... */ if ( msc->msc_ld == NULL ) { Debug( LDAP_DEBUG_ANY, "%s: asyncmeta_back_search_start candidate=%d ld=NULL%s.\n", op->o_log_prefix, candidate, META_BACK_ONERR_STOP( mi ) ? "" : " (ignored)" ); candidates[ candidate ].sr_err = LDAP_OTHER; if ( META_BACK_ONERR_STOP( mi ) ) { return META_SEARCH_ERR; } candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; return META_SEARCH_NOT_CANDIDATE; } Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_back_search_start[%d]\n", op->o_log_prefix, candidate, 0 ); /* * modifies the base according to the scope, if required */ if ( mt->mt_nsuffix.bv_len > op->o_req_ndn.bv_len ) { switch ( op->ors_scope ) { case LDAP_SCOPE_SUBTREE: /* * make the target suffix the new base * FIXME: this is very forgiving, because * "illegal" searchBases may be turned * into the suffix of the target; however, * the requested searchBase already passed * thru the candidate analyzer... */ if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) { realbase = mt->mt_nsuffix; if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) { realscope = LDAP_SCOPE_SUBORDINATE; } } else { /* * this target is no longer candidate */ retcode = META_SEARCH_NOT_CANDIDATE; goto doreturn; } break; case LDAP_SCOPE_SUBORDINATE: case LDAP_SCOPE_ONELEVEL: { struct berval rdn = mt->mt_nsuffix; rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," ); if ( dnIsOneLevelRDN( &rdn ) && dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) { /* * if there is exactly one level, * make the target suffix the new * base, and make scope "base" */ realbase = mt->mt_nsuffix; if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) { if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) { realscope = LDAP_SCOPE_SUBORDINATE; } else { realscope = LDAP_SCOPE_SUBTREE; } } else { realscope = LDAP_SCOPE_BASE; } break; } /* else continue with the next case */ } case LDAP_SCOPE_BASE: /* * this target is no longer candidate */ retcode = META_SEARCH_NOT_CANDIDATE; goto doreturn; } } /* check filter expression */ if ( mt->mt_filter ) { metafilter_t *mf; for ( mf = mt->mt_filter; mf; mf = mf->mf_next ) { if ( regexec( &mf->mf_regex, op->ors_filterstr.bv_val, 0, NULL, 0 ) == 0 ) break; } /* nothing matched, this target is no longer a candidate */ if ( !mf ) { retcode = META_SEARCH_NOT_CANDIDATE; goto doreturn; } } /* * Rewrite the search base, if required */ dc.target = mt; dc.ctx = "searchBase"; dc.conn = op->o_conn; dc.rs = rs; switch ( asyncmeta_dn_massage( &dc, &realbase, &mbase ) ) { case LDAP_SUCCESS: break; case LDAP_UNWILLING_TO_PERFORM: rs->sr_err = LDAP_UNWILLING_TO_PERFORM; rs->sr_text = "Operation not allowed"; retcode = META_SEARCH_ERR; goto doreturn; default: /* * this target is no longer candidate */ retcode = META_SEARCH_NOT_CANDIDATE; goto doreturn; } /* * Maps filter */ rc = asyncmeta_filter_map_rewrite( &dc, op->ors_filter, &mfilter, BACKLDAP_MAP, NULL ); switch ( rc ) { case LDAP_SUCCESS: break; case LDAP_COMPARE_FALSE: default: /* * this target is no longer candidate */ retcode = META_SEARCH_NOT_CANDIDATE; goto done; } /* * Maps required attributes */ rc = asyncmeta_map_attrs( op, &mt->mt_rwmap.rwm_at, op->ors_attrs, BACKLDAP_MAP, &mapped_attrs ); if ( rc != LDAP_SUCCESS ) { /* * this target is no longer candidate */ retcode = META_SEARCH_NOT_CANDIDATE; goto done; } if ( op->ors_tlimit != SLAP_NO_LIMIT ) { timelimit = op->ors_tlimit > 0 ? op->ors_tlimit : 1; } else { timelimit = -1; /* no limit */ } #ifdef SLAPD_META_CLIENT_PR save_ctrls = op->o_ctrls; { LDAPControl *pr_c = NULL; int i = 0, nc = 0; if ( save_ctrls ) { for ( ; save_ctrls[i] != NULL; i++ ); nc = i; pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, save_ctrls, NULL ); } if ( pr_c != NULL ) nc--; if ( mt->mt_ps > 0 || prcookie != NULL ) nc++; if ( mt->mt_ps > 0 || prcookie != NULL || pr_c != NULL ) { int src = 0, dst = 0; BerElementBuffer berbuf; BerElement *ber = (BerElement *)&berbuf; struct berval val = BER_BVNULL; ber_len_t len; len = sizeof( LDAPControl * )*( nc + 1 ) + sizeof( LDAPControl ); if ( mt->mt_ps > 0 || prcookie != NULL ) { struct berval nullcookie = BER_BVNULL; ber_tag_t tag; if ( prsize == 0 && mt->mt_ps > 0 ) prsize = mt->mt_ps; if ( prcookie == NULL ) prcookie = &nullcookie; ber_init2( ber, NULL, LBER_USE_DER ); tag = ber_printf( ber, "{iO}", prsize, prcookie ); if ( tag == LBER_ERROR ) { /* error */ (void) ber_free_buf( ber ); goto done_pr; } tag = ber_flatten2( ber, &val, 0 ); if ( tag == LBER_ERROR ) { /* error */ (void) ber_free_buf( ber ); goto done_pr; } len += val.bv_len + 1; } op->o_ctrls = op->o_tmpalloc( len, op->o_tmpmemctx ); if ( save_ctrls ) { for ( ; save_ctrls[ src ] != NULL; src++ ) { if ( save_ctrls[ src ] != pr_c ) { op->o_ctrls[ dst ] = save_ctrls[ src ]; dst++; } } } if ( mt->mt_ps > 0 || prcookie != NULL ) { op->o_ctrls[ dst ] = (LDAPControl *)&op->o_ctrls[ nc + 1 ]; op->o_ctrls[ dst ]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; op->o_ctrls[ dst ]->ldctl_iscritical = 1; op->o_ctrls[ dst ]->ldctl_value.bv_val = (char *)&op->o_ctrls[ dst ][ 1 ]; AC_MEMCPY( op->o_ctrls[ dst ]->ldctl_value.bv_val, val.bv_val, val.bv_len + 1 ); op->o_ctrls[ dst ]->ldctl_value.bv_len = val.bv_len; dst++; (void)ber_free_buf( ber ); } op->o_ctrls[ dst ] = NULL; } done_pr:; } #endif /* SLAPD_META_CLIENT_PR */ retry:; asyncmeta_set_msc_time(msc); ctrls = op->o_ctrls; if (nretries == 0) { if (rc != LDAP_SUCCESS) { rs->sr_err = LDAP_BUSY; retcode = META_SEARCH_ERR; candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; goto done; } } if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS ) { candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; retcode = META_SEARCH_NOT_CANDIDATE; goto done; } /* * Starts the search */ ber = ldap_build_search_req( msc->msc_ld, mbase.bv_val, realscope, mfilter.bv_val, mapped_attrs, op->ors_attrsonly, ctrls, NULL, timelimit, op->ors_slimit, op->ors_deref, &msgid ); if (ber) { candidates[ candidate ].sr_msgid = msgid; rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_SEARCH, mbase.bv_val, ber, msgid ); if (rc == msgid) rc = LDAP_SUCCESS; else rc = LDAP_SERVER_DOWN; switch ( rc ) { case LDAP_SUCCESS: retcode = META_SEARCH_CANDIDATE; asyncmeta_set_msc_time(msc); break; case LDAP_SERVER_DOWN: ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); if (mc->mc_active < 1) { asyncmeta_clear_one_msc(NULL, mc, candidate); } ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) { nretries = 0; /* if the identity changed, there might be need to re-authz */ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls ); goto retry; } rs->sr_err = LDAP_UNAVAILABLE; retcode = META_SEARCH_ERR; break; default: candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; retcode = META_SEARCH_NOT_CANDIDATE; } } done:; (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls ); #ifdef SLAPD_META_CLIENT_PR if ( save_ctrls != op->o_ctrls ) { op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx ); op->o_ctrls = save_ctrls; } #endif /* SLAPD_META_CLIENT_PR */ if ( mapped_attrs ) { ber_memfree_x( mapped_attrs, op->o_tmpmemctx ); } if ( mfilter.bv_val != op->ors_filterstr.bv_val ) { ber_memfree_x( mfilter.bv_val, NULL ); } if ( mbase.bv_val != realbase.bv_val ) { free( mbase.bv_val ); } doreturn:; Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_search_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid ); return retcode; }
int asyncmeta_back_search( Operation *op, SlapReply *rs ) { a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private; struct timeval save_tv = { 0, 0 }, tv; time_t stoptime = (time_t)(-1), lastres_time = slap_get_time(), timeout = 0; int rc = 0, sres = LDAP_SUCCESS; char *matched = NULL; int last = 0, ncandidates = 0, initial_candidates = 0, candidate_match = 0, needbind = 0; ldap_back_send_t sendok = LDAP_BACK_SENDERR; long i,j; int is_ok = 0; void *savepriv; SlapReply *candidates = NULL; int do_taint = 0; bm_context_t *bc; a_metaconn_t *mc; slap_callback *cb = op->o_callback; rs_assert_ready( rs ); rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */ /* * controls are set in ldap_back_dobind() * * FIXME: in case of values return filter, we might want * to map attrs and maybe rewrite value */ asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets ); if (bc == NULL) { rs->sr_err = LDAP_OTHER; send_ldap_result(op, rs); return rs->sr_err; } candidates = bc->candidates; mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_DONTSEND, 0); if ( !mc || rs->sr_err != LDAP_SUCCESS) { op->o_callback = cb; send_ldap_result(op, rs); asyncmeta_clear_bm_context(bc); return rs->sr_err; } /* * Inits searches */ for ( i = 0; i < mi->mi_ntargets; i++ ) { /* reset sr_msgid; it is used in most loops * to check if that target is still to be considered */ candidates[i].sr_msgid = META_MSGID_UNDEFINED; /* a target is marked as candidate by asyncmeta_getconn(); * if for any reason (an error, it's over or so) it is * no longer active, sr_msgid is set to META_MSGID_IGNORE * but it remains candidate, which means it has been active * at some point during the operation. This allows to * use its response code and more to compute the final * response */ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { continue; } candidates[ i ].sr_matched = NULL; candidates[ i ].sr_text = NULL; candidates[ i ].sr_ref = NULL; candidates[ i ].sr_ctrls = NULL; candidates[ i ].sr_nentries = 0; candidates[ i ].sr_type = -1; /* get largest timeout among candidates */ if ( mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] && mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] > timeout ) { timeout = mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ]; } } bc->timeout = timeout; bc->stoptime = op->o_time + bc->timeout; if ( op->ors_tlimit != SLAP_NO_LIMIT ) { stoptime = op->o_time + op->ors_tlimit; if (stoptime < bc->stoptime) { bc->stoptime = stoptime; bc->searchtime = 1; bc->timeout = op->ors_tlimit; } } ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); rc = asyncmeta_add_message_queue(mc, bc); ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); if (rc != LDAP_SUCCESS) { rs->sr_err = LDAP_BUSY; rs->sr_text = "Maximum pending ops limit exceeded"; asyncmeta_clear_bm_context(bc); op->o_callback = cb; send_ldap_result(op, rs); goto finish; } for ( i = 0; i < mi->mi_ntargets; i++ ) { if ( !META_IS_CANDIDATE( &candidates[ i ] ) || candidates[ i ].sr_err != LDAP_SUCCESS ) { continue; } rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, i); switch (rc) { case META_SEARCH_CANDIDATE: /* target is already bound, just send the search request */ ncandidates++; Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: IS_CANDIDATE " "cnd=\"%ld\"\n", op->o_log_prefix, i , 0); rc = asyncmeta_back_search_start( op, rs, mc, bc, i, NULL, 0 ); if (rc == META_SEARCH_ERR) { META_CANDIDATE_CLEAR(&candidates[i]); candidates[ i ].sr_msgid = META_MSGID_IGNORE; if ( META_BACK_ONERR_STOP( mi ) ) { asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb); goto finish; } else { continue; } } break; case META_SEARCH_NOT_CANDIDATE: Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NOT_CANDIDATE " "cnd=\"%ld\"\n", op->o_log_prefix, i , 0); candidates[ i ].sr_msgid = META_MSGID_IGNORE; break; case META_SEARCH_NEED_BIND: case META_SEARCH_CONNECTING: Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NEED_BIND " "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]); ncandidates++; rc = asyncmeta_dobind_init(op, rs, bc, mc, i); if (rc == META_SEARCH_ERR) { candidates[ i ].sr_msgid = META_MSGID_IGNORE; if ( META_BACK_ONERR_STOP( mi ) ) { asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb); goto finish; } else { continue; } } break; case META_SEARCH_BINDING: Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: BINDING " "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]); ncandidates++; /* Todo add the context to the message queue but do not send the request the receiver must send this when we are done binding */ /* question - how would do receiver know to which targets??? */ break; case META_SEARCH_ERR: Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: SEARCH_ERR " "cnd=\"%ldd\"\n", op->o_log_prefix, i , 0); candidates[ i ].sr_msgid = META_MSGID_IGNORE; candidates[ i ].sr_type = REP_RESULT; if ( META_BACK_ONERR_STOP( mi ) ) { asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb); goto finish; } else { continue; } break; default: assert( 0 ); break; } } initial_candidates = ncandidates; if ( LogTest( LDAP_DEBUG_TRACE ) ) { char cnd[ SLAP_TEXT_BUFLEN ]; int c; for ( c = 0; c < mi->mi_ntargets; c++ ) { if ( META_IS_CANDIDATE( &candidates[ c ] ) ) { cnd[ c ] = '*'; } else { cnd[ c ] = ' '; } } cnd[ c ] = '\0'; Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: ncandidates=%d " "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd ); } if ( initial_candidates == 0 ) { /* NOTE: here we are not sending any matchedDN; * this is intended, because if the back-meta * is serving this search request, but no valid * candidate could be looked up, it means that * there is a hole in the mapping of the targets * and thus no knowledge of any remote superior * is available */ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_back_search: " "base=\"%s\" scope=%d: " "no candidate could be selected\n", op->o_log_prefix, op->o_req_dn.bv_val, op->ors_scope ); /* FIXME: we're sending the first error we encounter; * maybe we should pick the worst... */ rc = LDAP_NO_SUCH_OBJECT; for ( i = 0; i < mi->mi_ntargets; i++ ) { if ( META_IS_CANDIDATE( &candidates[ i ] ) && candidates[ i ].sr_err != LDAP_SUCCESS ) { rc = candidates[ i ].sr_err; break; } } rs->sr_err = rc; ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); asyncmeta_drop_bc(mc, bc); ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); op->o_callback = cb; send_ldap_result(op, rs); asyncmeta_clear_bm_context(bc); goto finish; } ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); asyncmeta_start_listeners(mc, candidates, bc); ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); finish: return rs->sr_err; }