From 5ea77a75dd2d2158401331879f3c8f47940a732c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:35:32 +0200 Subject: Adding upstream version 2.5.13+dfsg. Signed-off-by: Daniel Baumann --- servers/slapd/back-asyncmeta/meta_result.c | 1825 ++++++++++++++++++++++++++++ 1 file changed, 1825 insertions(+) create mode 100644 servers/slapd/back-asyncmeta/meta_result.c (limited to 'servers/slapd/back-asyncmeta/meta_result.c') diff --git a/servers/slapd/back-asyncmeta/meta_result.c b/servers/slapd/back-asyncmeta/meta_result.c new file mode 100644 index 0000000..0ce279a --- /dev/null +++ b/servers/slapd/back-asyncmeta/meta_result.c @@ -0,0 +1,1825 @@ +/* meta_result.c - target responses processing */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 2016-2022 The OpenLDAP Foundation. + * Portions Copyright 2016 Symas Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ + +/* ACKNOWLEDGEMENTS: + * This work was developed by Symas Corporation + * based on back-meta module for inclusion in OpenLDAP Software. + * This work was sponsored by Ericsson. */ + +#include "portable.h" + +#include + +#include +#include + +#include "slap.h" +#include "../back-ldap/back-ldap.h" +#include "back-asyncmeta.h" +#include "ldap_rq.h" +#include "../../../libraries/liblber/lber-int.h" + +static void +asyncmeta_send_ldap_result(bm_context_t *bc, Operation *op, SlapReply *rs) +{ + if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !bc->op->o_abandon ) { + send_ldap_result(&bc->copy_op, rs); + bc->op->o_callback = bc->copy_op.o_callback; + bc->op->o_extra = bc->copy_op.o_extra; + bc->op->o_ctrls = bc->copy_op.o_ctrls; + } +} + +static int +asyncmeta_is_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate) +{ + a_metainfo_t *mi = mc->mc_info; + int i; + SlapReply *candidates = bc->candidates; + for ( i = 0; i < mi->mi_ntargets; i++ ) { + if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { + continue; + } + if (candidates[ i ].sr_msgid != META_MSGID_IGNORE || + candidates[ i ].sr_type != REP_RESULT) { + return 1; + } + } + return 0; +} + +meta_search_candidate_t +asyncmeta_dobind_result( + a_metaconn_t *mc, + int candidate, + SlapReply *bind_result, + LDAPMessage *res ) +{ + a_metainfo_t *mi = mc->mc_info; + a_metatarget_t *mt = mi->mi_targets[ candidate ]; + a_metasingleconn_t *msc = &mc->mc_conns[ candidate ]; + + meta_search_candidate_t retcode = META_SEARCH_NOT_CANDIDATE; + int rc; + + assert( msc->msc_ldr != NULL ); + + if ( mi->mi_idle_timeout != 0 ) { + asyncmeta_set_msc_time(msc); + } + + if ( LogTest( asyncmeta_debug ) ) { + char time_buf[ SLAP_TEXT_BUFLEN ]; + asyncmeta_get_timestamp(time_buf); + Debug( asyncmeta_debug, "[%x] [%s] asyncmeta_dobind_result msc: %p, " + "msc->msc_binding_time: %x, msc->msc_flags:%x\n ", + (unsigned int)slap_get_time(), time_buf, msc, + (unsigned int)msc->msc_binding_time, msc->msc_mscflags ); + } + /* FIXME: matched? referrals? response controls? */ + rc = ldap_parse_result( msc->msc_ldr, res, + &(bind_result->sr_err), + (char **)&(bind_result->sr_matched), + (char **)&(bind_result->sr_text), + NULL, NULL, 0 ); + + if ( LogTest( asyncmeta_debug ) ) { + char time_buf[ SLAP_TEXT_BUFLEN ]; + asyncmeta_get_timestamp(time_buf); + Debug( asyncmeta_debug, + "[%s] asyncmeta_dobind_result error=%d msc: %p\n", + time_buf,bind_result->sr_err, msc ); + } + + if ( rc != LDAP_SUCCESS ) { + bind_result->sr_err = rc; + } + rc = slap_map_api2result( bind_result ); + + LDAP_BACK_CONN_BINDING_CLEAR( msc ); + if ( rc != LDAP_SUCCESS ) { + bind_result->sr_err = rc; + } else { + /* FIXME: check if bound as idassert authcDN! */ + if ( BER_BVISNULL( &msc->msc_bound_ndn ) + || BER_BVISEMPTY( &msc->msc_bound_ndn ) ) + { + LDAP_BACK_CONN_ISANON_SET( msc ); + if ( LogTest( asyncmeta_debug ) ) { + char time_buf[ SLAP_TEXT_BUFLEN ]; + asyncmeta_get_timestamp(time_buf); + Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result anonymous msc: %p\n", + time_buf, msc ); + } + + } else { + if ( META_BACK_TGT_SAVECRED( mt ) && + !BER_BVISNULL( &msc->msc_cred ) && + !BER_BVISEMPTY( &msc->msc_cred ) ) + { + ldap_set_rebind_proc( msc->msc_ldr, mt->mt_rebind_f, msc ); + } + if ( LogTest( asyncmeta_debug ) ) { + char time_buf[ SLAP_TEXT_BUFLEN ]; + asyncmeta_get_timestamp(time_buf); + Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result success msc: %p\n", + time_buf, msc ); + } + LDAP_BACK_CONN_ISBOUND_SET( msc ); + } + retcode = META_SEARCH_CANDIDATE; + } + return retcode; +} + +static int +asyncmeta_send_entry( + Operation *op, + SlapReply *rs, + a_metaconn_t *mc, + int target, + LDAPMessage *e ) +{ + a_metainfo_t *mi = mc->mc_info; + struct berval a, mapped = BER_BVNULL; + int check_sorted_attrs = 0; + Entry ent = {0}; + BerElement ber = *ldap_get_message_ber( e ); + Attribute *attr, **attrp; + struct berval bdn, + dn = BER_BVNULL; + const char *text; + a_dncookie dc; + ber_len_t len; + int rc; + void *mem_mark; + + mem_mark = slap_sl_mark( op->o_tmpmemctx ); + ber_set_option( &ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx ); + + if ( ber_scanf( &ber, "l{", &len ) == LBER_ERROR ) { + return LDAP_DECODING_ERROR; + } + + if ( ber_set_option( &ber, LBER_OPT_REMAINING_BYTES, &len ) != LBER_OPT_SUCCESS ) { + return LDAP_OTHER; + } + + if ( ber_scanf( &ber, "m{", &bdn ) == LBER_ERROR ) { + return LDAP_DECODING_ERROR; + } + + /* + * Rewrite the dn of the result, if needed + */ + dc.op = op; + dc.target = mi->mi_targets[ target ]; + dc.memctx = op->o_tmpmemctx; + dc.to_from = MASSAGE_REP; + asyncmeta_dn_massage( &dc, &bdn, &dn ); + + /* + * Note: this may fail if the target host(s) schema differs + * from the one known to the meta, and a DN with unknown + * attributes is returned. + * + * FIXME: should we log anything, or delegate to dnNormalize? + */ + rc = dnPrettyNormal( NULL, &dn, &ent.e_name, &ent.e_nname, + op->o_tmpmemctx ); + if ( dn.bv_val != bdn.bv_val ) { + op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); + } + BER_BVZERO( &dn ); + + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, + "%s asyncmeta_send_entry(\"%s\"): " + "invalid DN syntax\n", + op->o_log_prefix, ent.e_name.bv_val ); + rc = LDAP_INVALID_DN_SYNTAX; + goto done; + } + + /* + * cache dn + */ + if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) { + ( void )asyncmeta_dncache_update_entry( &mi->mi_cache, + &ent.e_nname, target ); + } + + attrp = &ent.e_attrs; + + while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) { + int last = 0; + slap_syntax_validate_func *validate; + slap_syntax_transform_func *pretty; + + if ( ber_pvt_ber_remaining( &ber ) < 0 ) { + Debug( LDAP_DEBUG_ANY, + "%s asyncmeta_send_entry(\"%s\"): " + "unable to parse attr \"%s\".\n", + op->o_log_prefix, ent.e_name.bv_val, a.bv_val ); + + rc = LDAP_OTHER; + goto done; + } + + if ( ber_pvt_ber_remaining( &ber ) == 0 ) { + break; + } + + attr = op->o_tmpcalloc( 1, sizeof(Attribute), op->o_tmpmemctx ); + if ( slap_bv2ad( &a, &attr->a_desc, &text ) + != LDAP_SUCCESS) { + if ( slap_bv2undef_ad( &a, &attr->a_desc, &text, + SLAP_AD_PROXIED ) != LDAP_SUCCESS ) + { + Debug(LDAP_DEBUG_ANY, + "%s meta_send_entry(\"%s\"): " "slap_bv2undef_ad(%s): %s\n", + op->o_log_prefix, ent.e_name.bv_val, + mapped.bv_val, text ); + ( void )ber_scanf( &ber, "x" /* [W] */ ); + op->o_tmpfree( attr, op->o_tmpmemctx ); + continue; + } + } + + if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) + check_sorted_attrs = 1; + + /* no subschemaSubentry */ + if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry + || attr->a_desc == slap_schema.si_ad_entryDN ) + { + + /* + * We eat target's subschemaSubentry because + * a search for this value is likely not + * to resolve to the appropriate backend; + * later, the local subschemaSubentry is + * added. + * + * We also eat entryDN because the frontend + * will reattach it without checking if already + * present... + */ + ( void )ber_scanf( &ber, "x" /* [W] */ ); + op->o_tmpfree( attr, op->o_tmpmemctx ); + continue; + } + + if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR + || attr->a_vals == NULL ) + { + attr->a_vals = (struct berval *)&slap_dummy_bv; + + } else { + for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last ) + ; + } + attr->a_numvals = last; + + validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate; + pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty; + + if ( !validate && !pretty ) { + ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx ); + op->o_tmpfree( attr, op->o_tmpmemctx ); + goto next_attr; + } + + /* + * It is necessary to try to rewrite attributes with + * dn syntax because they might be used in ACLs as + * members of groups; since ACLs are applied to the + * rewritten stuff, no dn-based subecj clause could + * be used at the ldap backend side (see + * http://www.OpenLDAP.org/faq/data/cache/452.html) + * The problem can be overcome by moving the dn-based + * ACLs to the target directory server, and letting + * everything pass thru the ldap backend. + */ + { + int i; + + if ( attr->a_desc->ad_type->sat_syntax == + slap_schema.si_syn_distinguishedName ) + { + asyncmeta_dnattr_result_rewrite( &dc, attr->a_vals ); + + } else if ( attr->a_desc == slap_schema.si_ad_ref ) { + asyncmeta_referral_result_rewrite( &dc, attr->a_vals ); + + } + + for ( i = 0; i < last; i++ ) { + struct berval pval; + int rc; + + if ( pretty ) { + rc = ordered_value_pretty( attr->a_desc, + &attr->a_vals[i], &pval, op->o_tmpmemctx ); + + } else { + rc = ordered_value_validate( attr->a_desc, + &attr->a_vals[i], 0 ); + } + + if ( rc ) { + ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx ); + if ( --last == i ) { + BER_BVZERO( &attr->a_vals[ i ] ); + break; + } + attr->a_vals[i] = attr->a_vals[last]; + BER_BVZERO( &attr->a_vals[last] ); + i--; + continue; + } + + if ( pretty ) { + ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx ); + attr->a_vals[i] = pval; + } + } + + if ( last == 0 && attr->a_vals != &slap_dummy_bv ) { + ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx ); + op->o_tmpfree( attr, op->o_tmpmemctx ); + goto next_attr; + } + } + + if ( last && attr->a_desc->ad_type->sat_equality && + attr->a_desc->ad_type->sat_equality->smr_normalize ) + { + int i; + + attr->a_nvals = op->o_tmpalloc( ( last + 1 ) * sizeof( struct berval ), op->o_tmpmemctx ); + for ( i = 0; ia_desc, + attr->a_desc->ad_type->sat_equality, + &attr->a_vals[i], &attr->a_nvals[i], + op->o_tmpmemctx )) { + ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx ); + if ( --last == i ) { + BER_BVZERO( &attr->a_vals[ i ] ); + break; + } + attr->a_vals[i] = attr->a_vals[last]; + BER_BVZERO( &attr->a_vals[last] ); + i--; + } + } + BER_BVZERO( &attr->a_nvals[i] ); + if ( last == 0 ) { + ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx ); + ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx ); + op->o_tmpfree( attr, op->o_tmpmemctx ); + goto next_attr; + } + + } else { + attr->a_nvals = attr->a_vals; + } + + attr->a_numvals = last; + *attrp = attr; + attrp = &attr->a_next; +next_attr:; + } + + /* Check for sorted attributes */ + if ( check_sorted_attrs ) { + for ( attr = ent.e_attrs; attr; attr = attr->a_next ) { + if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) { + while ( attr->a_numvals > 1 ) { + int i; + int rc = slap_sort_vals( (Modifications *)attr, &text, &i, op->o_tmpmemctx ); + if ( rc != LDAP_TYPE_OR_VALUE_EXISTS ) + break; + + /* Strip duplicate values */ + if ( attr->a_nvals != attr->a_vals ) + ber_memfree_x( attr->a_nvals[i].bv_val, op->o_tmpmemctx ); + ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx ); + attr->a_numvals--; + if ( (unsigned)i < attr->a_numvals ) { + attr->a_vals[i] = attr->a_vals[attr->a_numvals]; + if ( attr->a_nvals != attr->a_vals ) + attr->a_nvals[i] = attr->a_nvals[attr->a_numvals]; + } + BER_BVZERO(&attr->a_vals[attr->a_numvals]); + if ( attr->a_nvals != attr->a_vals ) + BER_BVZERO(&attr->a_nvals[attr->a_numvals]); + } + attr->a_flags |= SLAP_ATTR_SORTED_VALS; + } + } + } + Debug( LDAP_DEBUG_TRACE, + "%s asyncmeta_send_entry(\"%s\"): " + ".\n", + op->o_log_prefix, ent.e_name.bv_val ); + ldap_get_entry_controls( mc->mc_conns[target].msc_ldr, + e, &rs->sr_ctrls ); + rs->sr_entry = &ent; + rs->sr_attrs = op->ors_attrs; + rs->sr_operational_attrs = NULL; + rs->sr_flags = mi->mi_targets[ target ]->mt_rep_flags; + rs->sr_err = LDAP_SUCCESS; + rc = send_search_entry( op, rs ); + switch ( rc ) { + case LDAP_UNAVAILABLE: + rc = LDAP_OTHER; + break; + } + +done:; + if ( rs->sr_ctrls != NULL ) { + ldap_controls_free( rs->sr_ctrls ); + rs->sr_ctrls = NULL; + } +#if 0 + while ( ent.e_attrs ) { + attr = ent.e_attrs; + ent.e_attrs = attr->a_next; + if ( attr->a_nvals != attr->a_vals ) + ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx ); + ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx ); + op->o_tmpfree( attr, op->o_tmpmemctx ); + } + if (ent.e_name.bv_val != NULL) { + op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx ); + } + + if (ent.e_nname.bv_val != NULL) { + op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx ); + } + if (rs->sr_entry && rs->sr_entry != &ent) { + entry_free( rs->sr_entry ); + } +#endif + slap_sl_release( mem_mark, op->o_tmpmemctx ); + rs->sr_entry = NULL; + rs->sr_attrs = NULL; + return rc; +} + +static void +asyncmeta_search_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate, int sres) +{ + a_metainfo_t *mi = mc->mc_info; + Operation *op = bc->op; + SlapReply *rs = &bc->rs; + int i; + SlapReply *candidates = bc->candidates; + char *matched = NULL; + + if ( bc->candidate_match > 0 ) { + struct berval pmatched = BER_BVNULL; + + /* we use the first one */ + for ( i = 0; i < mi->mi_ntargets; i++ ) { + if ( META_IS_CANDIDATE( &candidates[ i ] ) + && candidates[ i ].sr_matched != NULL ) + { + struct berval bv, pbv; + int rc; + + /* if we got success, and this target + * returned noSuchObject, and its suffix + * is a superior of the searchBase, + * ignore the matchedDN */ + if ( sres == LDAP_SUCCESS + && candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT + && op->o_req_ndn.bv_len > mi->mi_targets[ i ]->mt_nsuffix.bv_len ) + { + free( (char *)candidates[ i ].sr_matched ); + candidates[ i ].sr_matched = NULL; + continue; + } + + ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv ); + rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx ); + + if ( rc == LDAP_SUCCESS ) { + + /* NOTE: if they all are superiors + * of the baseDN, the shorter is also + * superior of the longer... */ + if ( pbv.bv_len > pmatched.bv_len ) { + if ( !BER_BVISNULL( &pmatched ) ) { + op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx ); + } + pmatched = pbv; + + } else { + op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx ); + } + } + + if ( candidates[ i ].sr_matched != NULL ) { + free( (char *)candidates[ i ].sr_matched ); + candidates[ i ].sr_matched = NULL; + } + } + } + + if ( !BER_BVISNULL( &pmatched ) ) { + matched = pmatched.bv_val; + } + + } else if ( sres == LDAP_NO_SUCH_OBJECT ) { + matched = mi->mi_suffix.bv_val; + } + + /* + * In case we returned at least one entry, we return LDAP_SUCCESS + * otherwise, the latter error code we got + */ + + if ( sres == LDAP_SUCCESS ) { + if ( rs->sr_v2ref ) { + sres = LDAP_REFERRAL; + } + + if ( META_BACK_ONERR_REPORT( mi ) ) { + /* + * Report errors, if any + * + * FIXME: we should handle error codes and return the more + * important/reasonable + */ + for ( i = 0; i < mi->mi_ntargets; i++ ) { + if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) { + continue; + } + + if ( candidates[ i ].sr_err != LDAP_SUCCESS + && candidates[ i ].sr_err != LDAP_NO_SUCH_OBJECT ) + { + sres = candidates[ i ].sr_err; + break; + } + } + } + } + Debug( LDAP_DEBUG_TRACE, + "%s asyncmeta_search_last_result(\"%d\"): " + ".\n", + op->o_log_prefix, candidate ); + rs->sr_err = sres; + rs->sr_matched = ( sres == LDAP_SUCCESS ? NULL : matched ); + rs->sr_text = ( sres == LDAP_SUCCESS ? NULL : candidates[candidate].sr_text ); + rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL ); + asyncmeta_send_ldap_result(bc, op, rs); + rs->sr_text = NULL; + rs->sr_matched = NULL; + rs->sr_ref = NULL; +} + +static meta_search_candidate_t +asyncmeta_send_pending_op(bm_context_t *bc, int candidate) +{ + meta_search_candidate_t retcode; + switch (bc->op->o_tag) { + case LDAP_REQ_SEARCH: + retcode = asyncmeta_back_search_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, NULL, 0 , 0); + break; + case LDAP_REQ_ADD: + retcode = asyncmeta_back_add_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0); + break; + case LDAP_REQ_MODIFY: + retcode = asyncmeta_back_modify_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0); + break; + case LDAP_REQ_MODRDN: + retcode = asyncmeta_back_modrdn_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0); + break; + case LDAP_REQ_COMPARE: + retcode = asyncmeta_back_compare_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0); + break; + case LDAP_REQ_DELETE: + retcode = asyncmeta_back_delete_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0); + break; + default: + retcode = META_SEARCH_NOT_CANDIDATE; + } + return retcode; +} + + +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->candidates[candidate].sr_msgid = META_MSGID_GOT_BIND; + if (bc->candidates[candidate].sr_msgid != META_MSGID_GOT_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); + operation_counter_init( bc->op, ctx ); + 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; +} + +meta_search_candidate_t +asyncmeta_return_bind_errors(a_metaconn_t *mc, int candidate, SlapReply *bind_result, void *ctx, int dolock) +{ + a_metainfo_t *mi = mc->mc_info; + bm_context_t *bc, *onext; + + if ( dolock ) + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + + for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) { + 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->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; + bc->candidates[ candidate ].sr_type = REP_RESULT; + bc->candidates[ candidate ].sr_err = bind_result->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); + bc->op->o_threadctx = ctx; + bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx ); + slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx); + operation_counter_init( bc->op, ctx ); + bc->rs.sr_err = bind_result->sr_err; + bc->rs.sr_text = bind_result->sr_text; + mc->pending_ops--; + asyncmeta_send_ldap_result(bc, bc->op, &bc->rs); + asyncmeta_clear_bm_context(bc); + } + } + + if ( dolock ) + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + + return META_SEARCH_CANDIDATE; +} + +static meta_search_candidate_t +asyncmeta_handle_bind_result(LDAPMessage *msg, a_metaconn_t *mc, int candidate, void *ctx) +{ + meta_search_candidate_t retcode; + SlapReply bind_result = {0}; + /* could modify the msc, safer to lock it */ + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + retcode = asyncmeta_dobind_result( mc, candidate, &bind_result, msg ); + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + if ( retcode == META_SEARCH_CANDIDATE ) { + /* send the remaining pending ops */ + asyncmeta_send_all_pending_ops(mc, candidate, ctx, 1); + } else { + asyncmeta_return_bind_errors(mc, candidate, &bind_result, ctx, 1); + } + return retcode; +} + +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 = op->o_tmpalloc( 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 = op->o_tmpalloc( 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\n", + 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)\n", + 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; +} + +/* handles the received result for add, modify, modrdn, compare and delete ops */ + +int asyncmeta_handle_common_result(LDAPMessage *msg, a_metaconn_t *mc, bm_context_t *bc, int candidate) +{ + a_metainfo_t *mi; + a_metatarget_t *mt; + a_metasingleconn_t *msc; + const char *save_text = NULL, + *save_matched = NULL; + BerVarray save_ref = NULL; + LDAPControl **save_ctrls = NULL; + void *matched_ctx = NULL; + + char *matched = NULL; + char *text = NULL; + char **refs = NULL; + LDAPControl **ctrls = NULL; + Operation *op; + SlapReply *rs; + int rc; + + mi = mc->mc_info; + mt = mi->mi_targets[ candidate ]; + msc = &mc->mc_conns[ candidate ]; + + op = bc->op; + rs = &bc->rs; + save_text = rs->sr_text, + save_matched = rs->sr_matched; + save_ref = rs->sr_ref; + save_ctrls = rs->sr_ctrls; + rs->sr_text = NULL; + rs->sr_matched = NULL; + rs->sr_ref = NULL; + rs->sr_ctrls = NULL; + + /* only touch when activity actually took place... */ + if ( mi->mi_idle_timeout != 0 ) { + asyncmeta_set_msc_time(msc); + } + + rc = ldap_parse_result( msc->msc_ldr, msg, &rs->sr_err, + &matched, &text, &refs, &ctrls, 0 ); + + if ( rc == LDAP_SUCCESS ) { + rs->sr_text = text; + } else { + rs->sr_err = rc; + } + rs->sr_err = slap_map_api2result( rs ); + + /* RFC 4511: referrals can only appear + * if result code is LDAP_REFERRAL */ + if ( refs != NULL + && refs[ 0 ] != NULL + && refs[ 0 ][ 0 ] != '\0' ) + { + if ( rs->sr_err != LDAP_REFERRAL ) { + Debug( LDAP_DEBUG_ANY, + "%s asyncmeta_handle_common_result[%d]: " + "got referrals with err=%d\n", + op->o_log_prefix, + candidate, rs->sr_err ); + + } else { + int i; + + for ( i = 0; refs[ i ] != NULL; i++ ) + /* count */ ; + rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ), + op->o_tmpmemctx ); + for ( i = 0; refs[ i ] != NULL; i++ ) { + ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] ); + } + BER_BVZERO( &rs->sr_ref[ i ] ); + } + + } else if ( rs->sr_err == LDAP_REFERRAL ) { + Debug( LDAP_DEBUG_ANY, + "%s asyncmeta_handle_common_result[%d]: " + "got err=%d with null " + "or empty referrals\n", + op->o_log_prefix, + candidate, rs->sr_err ); + + rs->sr_err = LDAP_NO_SUCH_OBJECT; + } + + if ( ctrls != NULL ) { + rs->sr_ctrls = ctrls; + } + + /* if the error in the reply structure is not + * LDAP_SUCCESS, try to map it from client + * to server error */ + if ( !LDAP_ERR_OK( rs->sr_err ) ) { + rs->sr_err = slap_map_api2result( rs ); + + /* internal ops ( op->o_conn == NULL ) + * must not reply to client */ + if ( op->o_conn && !op->o_do_not_cache && matched ) { + + /* record the (massaged) matched + * DN into the reply structure */ + rs->sr_matched = matched; + } + } + + if ( META_BACK_TGT_QUARANTINE( mt ) ) { + asyncmeta_quarantine( op, mi, rs, candidate ); + } + + if ( matched != NULL ) { + struct berval dn, pdn; + + ber_str2bv( matched, 0, 0, &dn ); + if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) { + ldap_memfree( matched ); + matched_ctx = op->o_tmpmemctx; + matched = pdn.bv_val; + } + rs->sr_matched = matched; + } + + if ( rs->sr_err == LDAP_UNAVAILABLE || rs->sr_err == LDAP_SERVER_DOWN ) { + if ( rs->sr_text == NULL ) { + rs->sr_text = "Target is unavailable"; + } + } + + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + asyncmeta_drop_bc( mc, bc); + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + + if ( op->o_conn ) { + asyncmeta_send_ldap_result(bc, op, rs); + } + + if ( matched ) { + op->o_tmpfree( (char *)rs->sr_matched, matched_ctx ); + } + if ( text ) { + ldap_memfree( text ); + } + if ( rs->sr_ref ) { + op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx ); + rs->sr_ref = NULL; + } + if ( refs ) { + ber_memvfree( (void **)refs ); + } + if ( ctrls ) { + assert( rs->sr_ctrls != NULL ); + ldap_controls_free( ctrls ); + } + + rs->sr_text = save_text; + rs->sr_matched = save_matched; + rs->sr_ref = save_ref; + rs->sr_ctrls = save_ctrls; + rc = (LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err); + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + asyncmeta_clear_bm_context(bc); + 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, err=%d\n", + mc->mc_conns[candidate].msc_ldr, error ); + + 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) { + bc->bc_invalid = 1; + 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); + operation_counter_init( bc->op, ctx ); + + 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; jmi_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; +} + +void * +asyncmeta_op_handle_result(void *ctx, void *arg) +{ + a_metaconn_t *mc = arg; + int i, j, rc, ntargets; + struct timeval tv = {0}; + LDAPMessage *msg; + a_metasingleconn_t *msc; + bm_context_t *bc; + void *oldctx; + + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + rc = ++mc->mc_active; + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + if (rc > 1) + return NULL; + + ntargets = mc->mc_info->mi_ntargets; + i = ntargets; + oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0); /* get existing memctx */ + +again: + for (j=0; j= ntargets) i = 0; + msc = &mc->mc_conns[i]; + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + if (!mc->mc_conns[i].msc_ldr || + META_BACK_CONN_CREATING( &mc->mc_conns[i] ) || + META_BACK_CONN_INVALID(&mc->mc_conns[i])) { + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + continue; + } + + msc->msc_active++; + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + + rc = ldap_result( mc->mc_conns[i].msc_ldr, LDAP_RES_ANY, LDAP_MSG_RECEIVED, &tv, &msg ); + if (rc < 1) { + if (rc < 0) { + ldap_get_option( mc->mc_conns[i].msc_ldr, LDAP_OPT_ERROR_NUMBER, &rc); + META_BACK_CONN_INVALID_SET(&mc->mc_conns[i]); + asyncmeta_op_read_error(mc, i, rc, ctx); + } + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + msc->msc_active--; + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + continue; + } + rc = ldap_msgtype( msg ); + if (rc == LDAP_RES_BIND) { + if ( LogTest( asyncmeta_debug ) ) { + char time_buf[ SLAP_TEXT_BUFLEN ]; + asyncmeta_get_timestamp(time_buf); + Debug( asyncmeta_debug, "[%s] asyncmeta_op_handle_result received bind msgid=%d msc: %p\n", + time_buf, ldap_msgid(msg), msc ); + } + asyncmeta_handle_bind_result(msg, mc, i, ctx); + mc->mc_info->mi_targets[i]->mt_timeout_ops = 0; + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + msc->msc_result_time = slap_get_time(); + msc->msc_active--; + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + if (msg) + ldap_msgfree(msg); + + continue; + } +retry_bc: + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + bc = asyncmeta_find_message(ldap_msgid(msg), mc, i); +/* The sender might not be yet done with the context. On error it might also remove it + * so it's best to try and find it again after a wait */ + if (bc && bc->bc_active > 0) { + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + ldap_pvt_thread_yield(); + goto retry_bc; + } + if (bc) { + bc->bc_active++; + } + + msc->msc_result_time = slap_get_time(); + msc->msc_active--; + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + if (!bc) { + Debug( asyncmeta_debug, + "asyncmeta_op_handle_result: Unable to find bc for msguid %d, msc: %p\n", ldap_msgid(msg), msc ); + ldap_msgfree(msg); + continue; + } + + /* set our memctx */ + bc->op->o_threadctx = ctx; + bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx ); + slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx); + operation_counter_init( bc->op, ctx ); + if (bc->op->o_abandon) { + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + asyncmeta_drop_bc( mc, bc); + if ( bc->op->o_tag == LDAP_REQ_SEARCH ) { + int j; + for (j=0; jcandidates[j].sr_msgid >= 0) { + a_metasingleconn_t *tmp_msc = &mc->mc_conns[j]; + tmp_msc->msc_active++; + asyncmeta_back_cancel( mc, bc->op, + bc->candidates[ j ].sr_msgid, j ); + tmp_msc->msc_active--; + } + } + } + asyncmeta_clear_bm_context(bc); + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + if (msg) + ldap_msgfree(msg); + continue; + } + + switch (rc) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_SEARCH_RESULT: + case LDAP_RES_INTERMEDIATE: + asyncmeta_handle_search_msg(msg, mc, bc, i); + mc->mc_info->mi_targets[i]->mt_timeout_ops = 0; + msg = NULL; + break; + case LDAP_RES_ADD: + case LDAP_RES_DELETE: + case LDAP_RES_MODDN: + case LDAP_RES_COMPARE: + case LDAP_RES_MODIFY: + rc = asyncmeta_handle_common_result(msg, mc, bc, i); + mc->mc_info->mi_targets[i]->mt_timeout_ops = 0; + break; + default: + { + Debug( asyncmeta_debug, + "asyncmeta_op_handle_result: " + "unrecognized response message tag=%d\n", + rc ); + + } + } + if (msg) + ldap_msgfree(msg); + } + + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + rc = --mc->mc_active; + if (rc) { + i++; + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + goto again; + } + slap_sl_mem_setctx(ctx, oldctx); + if (mc->mc_conns) { + for (i=0; imc_conns[i].msc_ldr && mc->mc_conns[i].conn) { + connection_client_enable(mc->mc_conns[i].conn); + } + } + } + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + return NULL; +} + +void asyncmeta_set_msc_time(a_metasingleconn_t *msc) +{ + msc->msc_time = slap_get_time(); +} + +void* asyncmeta_timeout_loop(void *ctx, void *arg) +{ + struct re_s* rtask = arg; + a_metainfo_t *mi = rtask->arg; + bm_context_t *bc, *onext; + time_t current_time = slap_get_time(); + int i, j; + LDAP_STAILQ_HEAD(BCList, bm_context_t) timeout_list; + LDAP_STAILQ_INIT( &timeout_list ); + + Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time ); + void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0); + for (i=0; imi_num_conns; i++) { + a_metaconn_t * mc= &mi->mi_conns[i]; + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) { + onext = LDAP_STAILQ_NEXT(bc, bc_next); + if (bc->bc_active > 0) { + continue; + } + + if (bc->op->o_abandon ) { + Operation *op = bc->op; + + /* set our memctx */ + op->o_threadctx = ctx; + op->o_tid = ldap_pvt_thread_pool_tid( ctx ); + slap_sl_mem_setctx(ctx, op->o_tmpmemctx); + operation_counter_init( op, ctx ); + + LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next); + mc->pending_ops--; + for (j=0; jmi_ntargets; j++) { + if (bc->candidates[j].sr_msgid >= 0) { + a_metasingleconn_t *msc = &mc->mc_conns[j]; + if ( op->o_tag == LDAP_REQ_SEARCH ) { + msc->msc_active++; + asyncmeta_back_cancel( mc, op, + bc->candidates[ j ].sr_msgid, j ); + msc->msc_active--; + } + } + } + asyncmeta_clear_bm_context(bc); + continue; + } + if (bc->bc_invalid) { + LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next); + mc->pending_ops--; + LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next); + continue; + } + + if (bc->timeout && bc->stoptime < current_time) { + Operation *op = bc->op; + LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next); + mc->pending_ops--; + LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next); + for (j=0; jmi_ntargets; j++) { + if (bc->candidates[j].sr_msgid >= 0) { + a_metasingleconn_t *msc = &mc->mc_conns[j]; + asyncmeta_set_msc_time(msc); + if ( op->o_tag == LDAP_REQ_SEARCH ) { + msc->msc_active++; + asyncmeta_back_cancel( mc, op, + bc->candidates[ j ].sr_msgid, j ); + msc->msc_active--; + } + } + } + } + } + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + + for (bc = LDAP_STAILQ_FIRST(&timeout_list); bc; bc = onext) { + Operation *op = bc->op; + SlapReply *rs = &bc->rs; + int timeout_err; + const char *timeout_text; + + onext = LDAP_STAILQ_NEXT(bc, bc_next); + LDAP_STAILQ_REMOVE(&timeout_list, bc, bm_context_t, bc_next); + /* set our memctx */ + bc->op->o_threadctx = ctx; + bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx ); + slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx); + operation_counter_init( bc->op, ctx ); + + if (bc->searchtime) { + timeout_err = LDAP_TIMELIMIT_EXCEEDED; + } else { + timeout_err = op->o_protocol >= LDAP_VERSION3 ? + LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER; + } + + if ( bc->bc_invalid ) { + timeout_text = "Operation is invalid - target connection has been reset"; + } else { + a_metasingleconn_t *log_msc = &mc->mc_conns[0]; + Debug( asyncmeta_debug, + "asyncmeta_timeout_loop:Timeout op %s loop[%p], " + "current_time:%ld, op->o_time:%ld msc: %p, " + "msc->msc_binding_time: %x, msc->msc_flags:%x \n", + bc->op->o_log_prefix, rtask, current_time, bc->op->o_time, + log_msc, (unsigned int)log_msc->msc_binding_time, log_msc->msc_mscflags ); + + if (bc->searchtime) { + timeout_text = NULL; + } else { + timeout_text = "Operation timed out"; + } + + for (j=0; jmi_ntargets; j++) { + if (bc->candidates[j].sr_msgid >= 0) { + a_metatarget_t *mt = mi->mi_targets[j]; + if (!META_BACK_TGT_QUARANTINE( mt ) || + bc->candidates[j].sr_type == REP_RESULT) { + continue; + } + + if (mt->mt_isquarantined > LDAP_BACK_FQ_NO) { + timeout_err = LDAP_UNAVAILABLE; + } else { + mt->mt_timeout_ops++; + if ((mi->mi_max_timeout_ops > 0) && + (mt->mt_timeout_ops > mi->mi_max_timeout_ops)) { + timeout_err = LDAP_UNAVAILABLE; + rs->sr_err = timeout_err; + if (mt->mt_isquarantined == LDAP_BACK_FQ_NO) + asyncmeta_quarantine(op, mi, rs, j); + } + } + } + } + } + rs->sr_err = timeout_err; + rs->sr_text = timeout_text; + if (!bc->op->o_abandon ) { + asyncmeta_send_ldap_result( bc, bc->op, &bc->rs ); + } + asyncmeta_clear_bm_context(bc); + } + + ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); + if (mi->mi_idle_timeout) { + for (j=0; jmi_ntargets; j++) { + a_metasingleconn_t *msc = &mc->mc_conns[j]; + if ( msc->msc_active > 0 ) { + continue; + } + if (mc->pending_ops > 0) { + continue; + } + current_time = slap_get_time(); + if (msc->msc_ld && msc->msc_time > 0 && msc->msc_time + mi->mi_idle_timeout < current_time) { + asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__); + } + } + } + ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); + } + + slap_sl_mem_setctx(ctx, oldctx); + current_time = slap_get_time(); + Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] stop at [%ld] \n", rtask, current_time ); + ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); + if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) { + ldap_pvt_runqueue_stoptask( &slapd_rq, rtask ); + } + ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); + return NULL; +} + -- cgit v1.2.3