1*cf1d77f7Schristos /*	$NetBSD: meta_result.c,v 1.2 2021/08/14 16:14:59 christos Exp $	*/
292cfeba6Schristos 
392cfeba6Schristos /* meta_result.c - target responses processing */
492cfeba6Schristos /* $OpenLDAP$ */
592cfeba6Schristos /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
692cfeba6Schristos  *
792cfeba6Schristos  * Copyright 2016-2021 The OpenLDAP Foundation.
892cfeba6Schristos  * Portions Copyright 2016 Symas Corporation.
992cfeba6Schristos  * All rights reserved.
1092cfeba6Schristos  *
1192cfeba6Schristos  * Redistribution and use in source and binary forms, with or without
1292cfeba6Schristos  * modification, are permitted only as authorized by the OpenLDAP
1392cfeba6Schristos  * Public License.
1492cfeba6Schristos  *
1592cfeba6Schristos  * A copy of this license is available in the file LICENSE in the
1692cfeba6Schristos  * top-level directory of the distribution or, alternatively, at
1792cfeba6Schristos  * <http://www.OpenLDAP.org/license.html>.
1892cfeba6Schristos  */
1992cfeba6Schristos 
2092cfeba6Schristos /* ACKNOWLEDGEMENTS:
2192cfeba6Schristos  * This work was developed by Symas Corporation
2292cfeba6Schristos  * based on back-meta module for inclusion in OpenLDAP Software.
2392cfeba6Schristos  * This work was sponsored by Ericsson. */
2492cfeba6Schristos 
2592cfeba6Schristos #include <sys/cdefs.h>
26*cf1d77f7Schristos __RCSID("$NetBSD: meta_result.c,v 1.2 2021/08/14 16:14:59 christos Exp $");
2792cfeba6Schristos 
2892cfeba6Schristos #include "portable.h"
2992cfeba6Schristos 
3092cfeba6Schristos #include <stdio.h>
3192cfeba6Schristos 
3292cfeba6Schristos #include <ac/string.h>
3392cfeba6Schristos #include <ac/socket.h>
3492cfeba6Schristos 
3592cfeba6Schristos #include "slap.h"
3692cfeba6Schristos #include "../back-ldap/back-ldap.h"
3792cfeba6Schristos #include "back-asyncmeta.h"
3892cfeba6Schristos #include "ldap_rq.h"
3992cfeba6Schristos #include "../../../libraries/liblber/lber-int.h"
4092cfeba6Schristos 
4192cfeba6Schristos static void
asyncmeta_send_ldap_result(bm_context_t * bc,Operation * op,SlapReply * rs)4292cfeba6Schristos asyncmeta_send_ldap_result(bm_context_t *bc, Operation *op, SlapReply *rs)
4392cfeba6Schristos {
4492cfeba6Schristos 	if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !bc->op->o_abandon ) {
4592cfeba6Schristos 		send_ldap_result(&bc->copy_op, rs);
4692cfeba6Schristos 		bc->op->o_callback = bc->copy_op.o_callback;
4792cfeba6Schristos 		bc->op->o_extra = bc->copy_op.o_extra;
4892cfeba6Schristos 		bc->op->o_ctrls = bc->copy_op.o_ctrls;
4992cfeba6Schristos 	}
5092cfeba6Schristos }
5192cfeba6Schristos 
5292cfeba6Schristos static int
asyncmeta_is_last_result(a_metaconn_t * mc,bm_context_t * bc,int candidate)5392cfeba6Schristos asyncmeta_is_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate)
5492cfeba6Schristos {
5592cfeba6Schristos 	a_metainfo_t	*mi = mc->mc_info;
5692cfeba6Schristos 	int i;
5792cfeba6Schristos 	SlapReply *candidates = bc->candidates;
5892cfeba6Schristos 	for ( i = 0; i < mi->mi_ntargets; i++ ) {
5992cfeba6Schristos 		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
6092cfeba6Schristos 			continue;
6192cfeba6Schristos 		}
6292cfeba6Schristos 		if (candidates[ i ].sr_msgid != META_MSGID_IGNORE ||
6392cfeba6Schristos 		    candidates[ i ].sr_type != REP_RESULT) {
6492cfeba6Schristos 			return 1;
6592cfeba6Schristos 		}
6692cfeba6Schristos 	}
6792cfeba6Schristos 	return 0;
6892cfeba6Schristos }
6992cfeba6Schristos 
7092cfeba6Schristos meta_search_candidate_t
asyncmeta_dobind_result(a_metaconn_t * mc,int candidate,SlapReply * bind_result,LDAPMessage * res)7192cfeba6Schristos asyncmeta_dobind_result(
7292cfeba6Schristos 	a_metaconn_t		*mc,
7392cfeba6Schristos 	int			candidate,
7492cfeba6Schristos 	SlapReply		*bind_result,
7592cfeba6Schristos 	LDAPMessage		*res )
7692cfeba6Schristos {
7792cfeba6Schristos 	a_metainfo_t		*mi = mc->mc_info;
7892cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
7992cfeba6Schristos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
8092cfeba6Schristos 
8192cfeba6Schristos 	meta_search_candidate_t	retcode = META_SEARCH_NOT_CANDIDATE;
8292cfeba6Schristos 	int			rc;
8392cfeba6Schristos 
8492cfeba6Schristos 	assert( msc->msc_ldr != NULL );
8592cfeba6Schristos 
8692cfeba6Schristos 	if ( mi->mi_idle_timeout != 0 ) {
8792cfeba6Schristos 		asyncmeta_set_msc_time(msc);
8892cfeba6Schristos 	}
8992cfeba6Schristos 
9092cfeba6Schristos 	if ( LogTest( asyncmeta_debug ) ) {
9192cfeba6Schristos 		char	time_buf[ SLAP_TEXT_BUFLEN ];
9292cfeba6Schristos 		asyncmeta_get_timestamp(time_buf);
9392cfeba6Schristos 		Debug( asyncmeta_debug, "[%x] [%s] asyncmeta_dobind_result msc: %p, "
9492cfeba6Schristos 		       "msc->msc_binding_time: %x, msc->msc_flags:%x\n ",
9592cfeba6Schristos 		       (unsigned int)slap_get_time(), time_buf, msc,
9692cfeba6Schristos 		       (unsigned int)msc->msc_binding_time, msc->msc_mscflags );
9792cfeba6Schristos 	}
9892cfeba6Schristos 	/* FIXME: matched? referrals? response controls? */
9992cfeba6Schristos 	rc = ldap_parse_result( msc->msc_ldr, res,
10092cfeba6Schristos 				&(bind_result->sr_err),
10192cfeba6Schristos 				(char **)&(bind_result->sr_matched),
10292cfeba6Schristos 				(char **)&(bind_result->sr_text),
10392cfeba6Schristos 				NULL, NULL, 0 );
10492cfeba6Schristos 
10592cfeba6Schristos 	if ( LogTest( asyncmeta_debug ) ) {
10692cfeba6Schristos 		char	time_buf[ SLAP_TEXT_BUFLEN ];
10792cfeba6Schristos 		asyncmeta_get_timestamp(time_buf);
10892cfeba6Schristos 		Debug( asyncmeta_debug,
10992cfeba6Schristos 		       "[%s] asyncmeta_dobind_result error=%d msc: %p\n",
11092cfeba6Schristos 		       time_buf,bind_result->sr_err, msc );
11192cfeba6Schristos 	}
11292cfeba6Schristos 
11392cfeba6Schristos 	if ( rc != LDAP_SUCCESS ) {
11492cfeba6Schristos 		bind_result->sr_err = rc;
11592cfeba6Schristos 	}
11692cfeba6Schristos 	rc = slap_map_api2result( bind_result );
11792cfeba6Schristos 
11892cfeba6Schristos 	LDAP_BACK_CONN_BINDING_CLEAR( msc );
11992cfeba6Schristos 	if ( rc != LDAP_SUCCESS ) {
12092cfeba6Schristos 		bind_result->sr_err = rc;
12192cfeba6Schristos 	} else {
12292cfeba6Schristos 		/* FIXME: check if bound as idassert authcDN! */
12392cfeba6Schristos 		if ( BER_BVISNULL( &msc->msc_bound_ndn )
12492cfeba6Schristos 			|| BER_BVISEMPTY( &msc->msc_bound_ndn ) )
12592cfeba6Schristos 		{
12692cfeba6Schristos 			LDAP_BACK_CONN_ISANON_SET( msc );
12792cfeba6Schristos 			if ( LogTest( asyncmeta_debug ) ) {
12892cfeba6Schristos 				char	time_buf[ SLAP_TEXT_BUFLEN ];
12992cfeba6Schristos 				asyncmeta_get_timestamp(time_buf);
13092cfeba6Schristos 				Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result anonymous msc: %p\n",
13192cfeba6Schristos 				      time_buf, msc );
13292cfeba6Schristos 			}
13392cfeba6Schristos 
13492cfeba6Schristos 		} else {
13592cfeba6Schristos 			if ( META_BACK_TGT_SAVECRED( mt ) &&
13692cfeba6Schristos 				!BER_BVISNULL( &msc->msc_cred ) &&
13792cfeba6Schristos 				!BER_BVISEMPTY( &msc->msc_cred ) )
13892cfeba6Schristos 			{
13992cfeba6Schristos 				ldap_set_rebind_proc( msc->msc_ldr, mt->mt_rebind_f, msc );
14092cfeba6Schristos 			}
14192cfeba6Schristos 			if ( LogTest( asyncmeta_debug ) ) {
14292cfeba6Schristos 				char	time_buf[ SLAP_TEXT_BUFLEN ];
14392cfeba6Schristos 				asyncmeta_get_timestamp(time_buf);
14492cfeba6Schristos 				Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result success msc: %p\n",
14592cfeba6Schristos 				      time_buf, msc );
14692cfeba6Schristos 			}
14792cfeba6Schristos 			LDAP_BACK_CONN_ISBOUND_SET( msc );
14892cfeba6Schristos 		}
14992cfeba6Schristos 		retcode = META_SEARCH_CANDIDATE;
15092cfeba6Schristos 	}
15192cfeba6Schristos 	return retcode;
15292cfeba6Schristos }
15392cfeba6Schristos 
15492cfeba6Schristos static int
asyncmeta_send_entry(Operation * op,SlapReply * rs,a_metaconn_t * mc,int target,LDAPMessage * e)15592cfeba6Schristos asyncmeta_send_entry(
15692cfeba6Schristos 	Operation 	*op,
15792cfeba6Schristos 	SlapReply	*rs,
15892cfeba6Schristos 	a_metaconn_t	*mc,
15992cfeba6Schristos 	int 		target,
16092cfeba6Schristos 	LDAPMessage 	*e )
16192cfeba6Schristos {
16292cfeba6Schristos 	a_metainfo_t 		*mi = mc->mc_info;
16392cfeba6Schristos 	struct berval		a, mapped = BER_BVNULL;
16492cfeba6Schristos 	int			check_sorted_attrs = 0;
16592cfeba6Schristos 	Entry 			ent = {0};
16692cfeba6Schristos 	BerElement 		ber = *ldap_get_message_ber( e );
16792cfeba6Schristos 	Attribute 		*attr, **attrp;
16892cfeba6Schristos 	struct berval 		bdn,
16992cfeba6Schristos 				dn = BER_BVNULL;
17092cfeba6Schristos 	const char 		*text;
17192cfeba6Schristos 	a_dncookie		dc;
17292cfeba6Schristos 	ber_len_t		len;
17392cfeba6Schristos 	int			rc;
17492cfeba6Schristos 	void	*mem_mark;
17592cfeba6Schristos 
17692cfeba6Schristos 	mem_mark = slap_sl_mark( op->o_tmpmemctx );
17792cfeba6Schristos 	ber_set_option( &ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
17892cfeba6Schristos 
17992cfeba6Schristos 	if ( ber_scanf( &ber, "l{", &len ) == LBER_ERROR ) {
18092cfeba6Schristos 		return LDAP_DECODING_ERROR;
18192cfeba6Schristos 	}
18292cfeba6Schristos 
18392cfeba6Schristos 	if ( ber_set_option( &ber, LBER_OPT_REMAINING_BYTES, &len ) != LBER_OPT_SUCCESS ) {
18492cfeba6Schristos 		return LDAP_OTHER;
18592cfeba6Schristos 	}
18692cfeba6Schristos 
18792cfeba6Schristos 	if ( ber_scanf( &ber, "m{", &bdn ) == LBER_ERROR ) {
18892cfeba6Schristos 		return LDAP_DECODING_ERROR;
18992cfeba6Schristos 	}
19092cfeba6Schristos 
19192cfeba6Schristos 	/*
19292cfeba6Schristos 	 * Rewrite the dn of the result, if needed
19392cfeba6Schristos 	 */
19492cfeba6Schristos 	dc.op = op;
19592cfeba6Schristos 	dc.target = mi->mi_targets[ target ];
19692cfeba6Schristos 	dc.memctx = op->o_tmpmemctx;
19792cfeba6Schristos 	dc.to_from = MASSAGE_REP;
19892cfeba6Schristos 	asyncmeta_dn_massage( &dc, &bdn, &dn );
19992cfeba6Schristos 
20092cfeba6Schristos 	/*
20192cfeba6Schristos 	 * Note: this may fail if the target host(s) schema differs
20292cfeba6Schristos 	 * from the one known to the meta, and a DN with unknown
20392cfeba6Schristos 	 * attributes is returned.
20492cfeba6Schristos 	 *
20592cfeba6Schristos 	 * FIXME: should we log anything, or delegate to dnNormalize?
20692cfeba6Schristos 	 */
20792cfeba6Schristos 	rc = dnPrettyNormal( NULL, &dn, &ent.e_name, &ent.e_nname,
20892cfeba6Schristos 		op->o_tmpmemctx );
20992cfeba6Schristos 	if ( dn.bv_val != bdn.bv_val ) {
21092cfeba6Schristos 			op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
21192cfeba6Schristos 	}
21292cfeba6Schristos 	BER_BVZERO( &dn );
21392cfeba6Schristos 
21492cfeba6Schristos 	if ( rc != LDAP_SUCCESS ) {
21592cfeba6Schristos 		Debug( LDAP_DEBUG_ANY,
21692cfeba6Schristos 			"%s asyncmeta_send_entry(\"%s\"): "
21792cfeba6Schristos 			"invalid DN syntax\n",
21892cfeba6Schristos 			op->o_log_prefix, ent.e_name.bv_val );
21992cfeba6Schristos 		rc = LDAP_INVALID_DN_SYNTAX;
22092cfeba6Schristos 		goto done;
22192cfeba6Schristos 	}
22292cfeba6Schristos 
22392cfeba6Schristos 	/*
22492cfeba6Schristos 	 * cache dn
22592cfeba6Schristos 	 */
22692cfeba6Schristos 	if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
22792cfeba6Schristos 		( void )asyncmeta_dncache_update_entry( &mi->mi_cache,
22892cfeba6Schristos 				&ent.e_nname, target );
22992cfeba6Schristos 	}
23092cfeba6Schristos 
23192cfeba6Schristos 	attrp = &ent.e_attrs;
23292cfeba6Schristos 
23392cfeba6Schristos 	while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
23492cfeba6Schristos 		int				last = 0;
23592cfeba6Schristos 		slap_syntax_validate_func	*validate;
23692cfeba6Schristos 		slap_syntax_transform_func	*pretty;
23792cfeba6Schristos 
23892cfeba6Schristos 		if ( ber_pvt_ber_remaining( &ber ) < 0 ) {
23992cfeba6Schristos 			Debug( LDAP_DEBUG_ANY,
24092cfeba6Schristos 				"%s asyncmeta_send_entry(\"%s\"): "
24192cfeba6Schristos 				"unable to parse attr \"%s\".\n",
24292cfeba6Schristos 				op->o_log_prefix, ent.e_name.bv_val, a.bv_val );
24392cfeba6Schristos 
24492cfeba6Schristos 			rc = LDAP_OTHER;
24592cfeba6Schristos 			goto done;
24692cfeba6Schristos 		}
24792cfeba6Schristos 
24892cfeba6Schristos 		if ( ber_pvt_ber_remaining( &ber ) == 0 ) {
24992cfeba6Schristos 			break;
25092cfeba6Schristos 		}
25192cfeba6Schristos 
25292cfeba6Schristos 		attr = op->o_tmpcalloc( 1, sizeof(Attribute), op->o_tmpmemctx );
25392cfeba6Schristos 		if ( slap_bv2ad( &a, &attr->a_desc, &text )
25492cfeba6Schristos 				!= LDAP_SUCCESS) {
25592cfeba6Schristos 			if ( slap_bv2undef_ad( &a, &attr->a_desc, &text,
25692cfeba6Schristos 				SLAP_AD_PROXIED ) != LDAP_SUCCESS )
25792cfeba6Schristos 			{
25892cfeba6Schristos 				Debug(LDAP_DEBUG_ANY,
25992cfeba6Schristos 				      "%s meta_send_entry(\"%s\"): " "slap_bv2undef_ad(%s): %s\n",
26092cfeba6Schristos 				      op->o_log_prefix, ent.e_name.bv_val,
26192cfeba6Schristos 				      mapped.bv_val, text );
26292cfeba6Schristos 				( void )ber_scanf( &ber, "x" /* [W] */ );
26392cfeba6Schristos 				op->o_tmpfree( attr, op->o_tmpmemctx );
26492cfeba6Schristos 				continue;
26592cfeba6Schristos 			}
26692cfeba6Schristos 		}
26792cfeba6Schristos 
26892cfeba6Schristos 		if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL )
26992cfeba6Schristos 			check_sorted_attrs = 1;
27092cfeba6Schristos 
27192cfeba6Schristos 		/* no subschemaSubentry */
27292cfeba6Schristos 		if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
27392cfeba6Schristos 			|| attr->a_desc == slap_schema.si_ad_entryDN )
27492cfeba6Schristos 		{
27592cfeba6Schristos 
27692cfeba6Schristos 			/*
27792cfeba6Schristos 			 * We eat target's subschemaSubentry because
27892cfeba6Schristos 			 * a search for this value is likely not
27992cfeba6Schristos 			 * to resolve to the appropriate backend;
28092cfeba6Schristos 			 * later, the local subschemaSubentry is
28192cfeba6Schristos 			 * added.
28292cfeba6Schristos 			 *
28392cfeba6Schristos 			 * We also eat entryDN because the frontend
28492cfeba6Schristos 			 * will reattach it without checking if already
28592cfeba6Schristos 			 * present...
28692cfeba6Schristos 			 */
28792cfeba6Schristos 			( void )ber_scanf( &ber, "x" /* [W] */ );
28892cfeba6Schristos 			op->o_tmpfree( attr, op->o_tmpmemctx );
28992cfeba6Schristos 			continue;
29092cfeba6Schristos 		}
29192cfeba6Schristos 
29292cfeba6Schristos 		if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
29392cfeba6Schristos 				|| attr->a_vals == NULL )
29492cfeba6Schristos 		{
29592cfeba6Schristos 			attr->a_vals = (struct berval *)&slap_dummy_bv;
29692cfeba6Schristos 
29792cfeba6Schristos 		} else {
29892cfeba6Schristos 			for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last )
29992cfeba6Schristos 				;
30092cfeba6Schristos 		}
30192cfeba6Schristos 		attr->a_numvals = last;
30292cfeba6Schristos 
30392cfeba6Schristos 		validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
30492cfeba6Schristos 		pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
30592cfeba6Schristos 
30692cfeba6Schristos 		if ( !validate && !pretty ) {
30792cfeba6Schristos 			ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
30892cfeba6Schristos 			op->o_tmpfree( attr, op->o_tmpmemctx );
30992cfeba6Schristos 			goto next_attr;
31092cfeba6Schristos 		}
31192cfeba6Schristos 
31292cfeba6Schristos 		/*
31392cfeba6Schristos 		 * It is necessary to try to rewrite attributes with
31492cfeba6Schristos 		 * dn syntax because they might be used in ACLs as
31592cfeba6Schristos 		 * members of groups; since ACLs are applied to the
31692cfeba6Schristos 		 * rewritten stuff, no dn-based subecj clause could
31792cfeba6Schristos 		 * be used at the ldap backend side (see
31892cfeba6Schristos 		 * http://www.OpenLDAP.org/faq/data/cache/452.html)
31992cfeba6Schristos 		 * The problem can be overcome by moving the dn-based
32092cfeba6Schristos 		 * ACLs to the target directory server, and letting
32192cfeba6Schristos 		 * everything pass thru the ldap backend.
32292cfeba6Schristos 		 */
32392cfeba6Schristos 		{
32492cfeba6Schristos 			int	i;
32592cfeba6Schristos 
32692cfeba6Schristos 			if ( attr->a_desc->ad_type->sat_syntax ==
32792cfeba6Schristos 				slap_schema.si_syn_distinguishedName )
32892cfeba6Schristos 			{
32992cfeba6Schristos 				asyncmeta_dnattr_result_rewrite( &dc, attr->a_vals );
33092cfeba6Schristos 
33192cfeba6Schristos 			} else if ( attr->a_desc == slap_schema.si_ad_ref ) {
33292cfeba6Schristos 				asyncmeta_referral_result_rewrite( &dc, attr->a_vals );
33392cfeba6Schristos 
33492cfeba6Schristos 			}
33592cfeba6Schristos 
33692cfeba6Schristos 			for ( i = 0; i < last; i++ ) {
33792cfeba6Schristos 				struct berval	pval;
33892cfeba6Schristos 				int		rc;
33992cfeba6Schristos 
34092cfeba6Schristos 				if ( pretty ) {
34192cfeba6Schristos 					rc = ordered_value_pretty( attr->a_desc,
34292cfeba6Schristos 						&attr->a_vals[i], &pval, op->o_tmpmemctx );
34392cfeba6Schristos 
34492cfeba6Schristos 				} else {
34592cfeba6Schristos 					rc = ordered_value_validate( attr->a_desc,
34692cfeba6Schristos 						&attr->a_vals[i], 0 );
34792cfeba6Schristos 				}
34892cfeba6Schristos 
34992cfeba6Schristos 				if ( rc ) {
35092cfeba6Schristos 					ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
35192cfeba6Schristos 					if ( --last == i ) {
35292cfeba6Schristos 						BER_BVZERO( &attr->a_vals[ i ] );
35392cfeba6Schristos 						break;
35492cfeba6Schristos 					}
35592cfeba6Schristos 					attr->a_vals[i] = attr->a_vals[last];
35692cfeba6Schristos 					BER_BVZERO( &attr->a_vals[last] );
35792cfeba6Schristos 					i--;
35892cfeba6Schristos 					continue;
35992cfeba6Schristos 				}
36092cfeba6Schristos 
36192cfeba6Schristos 				if ( pretty ) {
36292cfeba6Schristos 					ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
36392cfeba6Schristos 					attr->a_vals[i] = pval;
36492cfeba6Schristos 				}
36592cfeba6Schristos 			}
36692cfeba6Schristos 
36792cfeba6Schristos 			if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
36892cfeba6Schristos 				ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
36992cfeba6Schristos 				op->o_tmpfree( attr, op->o_tmpmemctx );
37092cfeba6Schristos 				goto next_attr;
37192cfeba6Schristos 			}
37292cfeba6Schristos 		}
37392cfeba6Schristos 
37492cfeba6Schristos 		if ( last && attr->a_desc->ad_type->sat_equality &&
37592cfeba6Schristos 			attr->a_desc->ad_type->sat_equality->smr_normalize )
37692cfeba6Schristos 		{
37792cfeba6Schristos 			int i;
37892cfeba6Schristos 
37992cfeba6Schristos 			attr->a_nvals = op->o_tmpalloc( ( last + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
38092cfeba6Schristos 			for ( i = 0; i<last; i++ ) {
38192cfeba6Schristos 				/* if normalizer fails, drop this value */
38292cfeba6Schristos 				if ( ordered_value_normalize(
38392cfeba6Schristos 					SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
38492cfeba6Schristos 					attr->a_desc,
38592cfeba6Schristos 					attr->a_desc->ad_type->sat_equality,
38692cfeba6Schristos 					&attr->a_vals[i], &attr->a_nvals[i],
38792cfeba6Schristos 					op->o_tmpmemctx )) {
38892cfeba6Schristos 					ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
38992cfeba6Schristos 					if ( --last == i ) {
39092cfeba6Schristos 						BER_BVZERO( &attr->a_vals[ i ] );
39192cfeba6Schristos 						break;
39292cfeba6Schristos 					}
39392cfeba6Schristos 					attr->a_vals[i] = attr->a_vals[last];
39492cfeba6Schristos 					BER_BVZERO( &attr->a_vals[last] );
39592cfeba6Schristos 					i--;
39692cfeba6Schristos 				}
39792cfeba6Schristos 			}
39892cfeba6Schristos 			BER_BVZERO( &attr->a_nvals[i] );
39992cfeba6Schristos 			if ( last == 0 ) {
40092cfeba6Schristos 				ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
40192cfeba6Schristos 				ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx );
40292cfeba6Schristos 				op->o_tmpfree( attr, op->o_tmpmemctx );
40392cfeba6Schristos 				goto next_attr;
40492cfeba6Schristos 			}
40592cfeba6Schristos 
40692cfeba6Schristos 		} else {
40792cfeba6Schristos 			attr->a_nvals = attr->a_vals;
40892cfeba6Schristos 		}
40992cfeba6Schristos 
41092cfeba6Schristos 		attr->a_numvals = last;
41192cfeba6Schristos 		*attrp = attr;
41292cfeba6Schristos 		attrp = &attr->a_next;
41392cfeba6Schristos next_attr:;
41492cfeba6Schristos 	}
41592cfeba6Schristos 
41692cfeba6Schristos 	/* Check for sorted attributes */
41792cfeba6Schristos 	if ( check_sorted_attrs ) {
41892cfeba6Schristos 		for ( attr = ent.e_attrs; attr; attr = attr->a_next ) {
41992cfeba6Schristos 			if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
42092cfeba6Schristos 				while ( attr->a_numvals > 1 ) {
42192cfeba6Schristos 					int i;
42292cfeba6Schristos 					int rc = slap_sort_vals( (Modifications *)attr, &text, &i, op->o_tmpmemctx );
42392cfeba6Schristos 					if ( rc != LDAP_TYPE_OR_VALUE_EXISTS )
42492cfeba6Schristos 						break;
42592cfeba6Schristos 
42692cfeba6Schristos 					/* Strip duplicate values */
42792cfeba6Schristos 					if ( attr->a_nvals != attr->a_vals )
42892cfeba6Schristos 						ber_memfree_x( attr->a_nvals[i].bv_val, op->o_tmpmemctx );
42992cfeba6Schristos 					ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
43092cfeba6Schristos 					attr->a_numvals--;
43192cfeba6Schristos 					if ( (unsigned)i < attr->a_numvals ) {
43292cfeba6Schristos 						attr->a_vals[i] = attr->a_vals[attr->a_numvals];
43392cfeba6Schristos 						if ( attr->a_nvals != attr->a_vals )
43492cfeba6Schristos 							attr->a_nvals[i] = attr->a_nvals[attr->a_numvals];
43592cfeba6Schristos 					}
43692cfeba6Schristos 					BER_BVZERO(&attr->a_vals[attr->a_numvals]);
43792cfeba6Schristos 					if ( attr->a_nvals != attr->a_vals )
43892cfeba6Schristos 						BER_BVZERO(&attr->a_nvals[attr->a_numvals]);
43992cfeba6Schristos 				}
44092cfeba6Schristos 				attr->a_flags |= SLAP_ATTR_SORTED_VALS;
44192cfeba6Schristos 			}
44292cfeba6Schristos 		}
44392cfeba6Schristos 	}
44492cfeba6Schristos 	Debug( LDAP_DEBUG_TRACE,
44592cfeba6Schristos 	       "%s asyncmeta_send_entry(\"%s\"): "
44692cfeba6Schristos 	       ".\n",
44792cfeba6Schristos 	       op->o_log_prefix, ent.e_name.bv_val );
44892cfeba6Schristos 	ldap_get_entry_controls( mc->mc_conns[target].msc_ldr,
44992cfeba6Schristos 		e, &rs->sr_ctrls );
45092cfeba6Schristos 	rs->sr_entry = &ent;
45192cfeba6Schristos 	rs->sr_attrs = op->ors_attrs;
45292cfeba6Schristos 	rs->sr_operational_attrs = NULL;
45392cfeba6Schristos 	rs->sr_flags = mi->mi_targets[ target ]->mt_rep_flags;
45492cfeba6Schristos 	rs->sr_err = LDAP_SUCCESS;
45592cfeba6Schristos 	rc = send_search_entry( op, rs );
45692cfeba6Schristos 	switch ( rc ) {
45792cfeba6Schristos 	case LDAP_UNAVAILABLE:
45892cfeba6Schristos 		rc = LDAP_OTHER;
45992cfeba6Schristos 		break;
46092cfeba6Schristos 	}
46192cfeba6Schristos 
46292cfeba6Schristos done:;
46392cfeba6Schristos 	if ( rs->sr_ctrls != NULL ) {
46492cfeba6Schristos 		ldap_controls_free( rs->sr_ctrls );
46592cfeba6Schristos 		rs->sr_ctrls = NULL;
46692cfeba6Schristos 	}
46792cfeba6Schristos #if 0
46892cfeba6Schristos 	while ( ent.e_attrs ) {
46992cfeba6Schristos 		attr = ent.e_attrs;
47092cfeba6Schristos 		ent.e_attrs = attr->a_next;
47192cfeba6Schristos 		if ( attr->a_nvals != attr->a_vals )
47292cfeba6Schristos 			ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx );
47392cfeba6Schristos 		ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
47492cfeba6Schristos 		op->o_tmpfree( attr, op->o_tmpmemctx );
47592cfeba6Schristos 	}
47692cfeba6Schristos 	if (ent.e_name.bv_val != NULL) {
47792cfeba6Schristos 		op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx );
47892cfeba6Schristos 	}
47992cfeba6Schristos 
48092cfeba6Schristos 	if (ent.e_nname.bv_val != NULL) {
48192cfeba6Schristos 		op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx );
48292cfeba6Schristos 	}
48392cfeba6Schristos 	if (rs->sr_entry && rs->sr_entry != &ent) {
48492cfeba6Schristos 		entry_free( rs->sr_entry );
48592cfeba6Schristos 	}
48692cfeba6Schristos #endif
48792cfeba6Schristos 	slap_sl_release( mem_mark, op->o_tmpmemctx );
48892cfeba6Schristos 	rs->sr_entry = NULL;
48992cfeba6Schristos 	rs->sr_attrs = NULL;
49092cfeba6Schristos 	return rc;
49192cfeba6Schristos }
49292cfeba6Schristos 
49392cfeba6Schristos static void
asyncmeta_search_last_result(a_metaconn_t * mc,bm_context_t * bc,int candidate,int sres)49492cfeba6Schristos asyncmeta_search_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate, int sres)
49592cfeba6Schristos {
49692cfeba6Schristos 	a_metainfo_t	*mi = mc->mc_info;
49792cfeba6Schristos 	Operation *op = bc->op;
49892cfeba6Schristos 	SlapReply *rs = &bc->rs;
49992cfeba6Schristos 	int	   i;
50092cfeba6Schristos 	SlapReply *candidates = bc->candidates;
50192cfeba6Schristos 	char *matched = NULL;
50292cfeba6Schristos 
50392cfeba6Schristos 	if ( bc->candidate_match > 0 ) {
50492cfeba6Schristos 		struct berval	pmatched = BER_BVNULL;
50592cfeba6Schristos 
50692cfeba6Schristos 		/* we use the first one */
50792cfeba6Schristos 		for ( i = 0; i < mi->mi_ntargets; i++ ) {
50892cfeba6Schristos 			if ( META_IS_CANDIDATE( &candidates[ i ] )
50992cfeba6Schristos 					&& candidates[ i ].sr_matched != NULL )
51092cfeba6Schristos 			{
51192cfeba6Schristos 				struct berval	bv, pbv;
51292cfeba6Schristos 				int		rc;
51392cfeba6Schristos 
51492cfeba6Schristos 				/* if we got success, and this target
51592cfeba6Schristos 				 * returned noSuchObject, and its suffix
51692cfeba6Schristos 				 * is a superior of the searchBase,
51792cfeba6Schristos 				 * ignore the matchedDN */
51892cfeba6Schristos 				if ( sres == LDAP_SUCCESS
51992cfeba6Schristos 					&& candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT
52092cfeba6Schristos 					&& op->o_req_ndn.bv_len > mi->mi_targets[ i ]->mt_nsuffix.bv_len )
52192cfeba6Schristos 				{
52292cfeba6Schristos 					free( (char *)candidates[ i ].sr_matched );
52392cfeba6Schristos 					candidates[ i ].sr_matched = NULL;
52492cfeba6Schristos 					continue;
52592cfeba6Schristos 				}
52692cfeba6Schristos 
52792cfeba6Schristos 				ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
52892cfeba6Schristos 				rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );
52992cfeba6Schristos 
53092cfeba6Schristos 				if ( rc == LDAP_SUCCESS ) {
53192cfeba6Schristos 
53292cfeba6Schristos 					/* NOTE: if they all are superiors
53392cfeba6Schristos 					 * of the baseDN, the shorter is also
53492cfeba6Schristos 					 * superior of the longer... */
53592cfeba6Schristos 					if ( pbv.bv_len > pmatched.bv_len ) {
53692cfeba6Schristos 						if ( !BER_BVISNULL( &pmatched ) ) {
53792cfeba6Schristos 							op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
53892cfeba6Schristos 						}
53992cfeba6Schristos 						pmatched = pbv;
54092cfeba6Schristos 
54192cfeba6Schristos 					} else {
54292cfeba6Schristos 						op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
54392cfeba6Schristos 					}
54492cfeba6Schristos 				}
54592cfeba6Schristos 
54692cfeba6Schristos 				if ( candidates[ i ].sr_matched != NULL ) {
54792cfeba6Schristos 					free( (char *)candidates[ i ].sr_matched );
54892cfeba6Schristos 					candidates[ i ].sr_matched = NULL;
54992cfeba6Schristos 				}
55092cfeba6Schristos 			}
55192cfeba6Schristos 		}
55292cfeba6Schristos 
55392cfeba6Schristos 		if ( !BER_BVISNULL( &pmatched ) ) {
55492cfeba6Schristos 			matched = pmatched.bv_val;
55592cfeba6Schristos 		}
55692cfeba6Schristos 
55792cfeba6Schristos 	} else if ( sres == LDAP_NO_SUCH_OBJECT ) {
55892cfeba6Schristos 		matched = mi->mi_suffix.bv_val;
55992cfeba6Schristos 	}
56092cfeba6Schristos 
56192cfeba6Schristos 	/*
56292cfeba6Schristos 	 * In case we returned at least one entry, we return LDAP_SUCCESS
56392cfeba6Schristos 	 * otherwise, the latter error code we got
56492cfeba6Schristos 	 */
56592cfeba6Schristos 
56692cfeba6Schristos 	if ( sres == LDAP_SUCCESS ) {
56792cfeba6Schristos 		if ( rs->sr_v2ref ) {
56892cfeba6Schristos 			sres = LDAP_REFERRAL;
56992cfeba6Schristos 		}
57092cfeba6Schristos 
57192cfeba6Schristos 		if ( META_BACK_ONERR_REPORT( mi ) ) {
57292cfeba6Schristos 			/*
57392cfeba6Schristos 			 * Report errors, if any
57492cfeba6Schristos 			 *
57592cfeba6Schristos 			 * FIXME: we should handle error codes and return the more
57692cfeba6Schristos 			 * important/reasonable
57792cfeba6Schristos 			 */
57892cfeba6Schristos 			for ( i = 0; i < mi->mi_ntargets; i++ ) {
57992cfeba6Schristos 				if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
58092cfeba6Schristos 					continue;
58192cfeba6Schristos 				}
58292cfeba6Schristos 
58392cfeba6Schristos 				if ( candidates[ i ].sr_err != LDAP_SUCCESS
58492cfeba6Schristos 					&& candidates[ i ].sr_err != LDAP_NO_SUCH_OBJECT )
58592cfeba6Schristos 				{
58692cfeba6Schristos 					sres = candidates[ i ].sr_err;
58792cfeba6Schristos 					break;
58892cfeba6Schristos 				}
58992cfeba6Schristos 			}
59092cfeba6Schristos 		}
59192cfeba6Schristos 	}
59292cfeba6Schristos 	Debug( LDAP_DEBUG_TRACE,
59392cfeba6Schristos 	       "%s asyncmeta_search_last_result(\"%d\"): "
59492cfeba6Schristos 	       ".\n",
59592cfeba6Schristos 	       op->o_log_prefix, candidate );
59692cfeba6Schristos 	rs->sr_err = sres;
59792cfeba6Schristos 	rs->sr_matched = ( sres == LDAP_SUCCESS ? NULL : matched );
59892cfeba6Schristos 	rs->sr_text =  ( sres == LDAP_SUCCESS ? NULL : candidates[candidate].sr_text );
59992cfeba6Schristos 	rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
60092cfeba6Schristos 	asyncmeta_send_ldap_result(bc, op, rs);
60192cfeba6Schristos 	rs->sr_text = NULL;
60292cfeba6Schristos 	rs->sr_matched = NULL;
60392cfeba6Schristos 	rs->sr_ref = NULL;
60492cfeba6Schristos }
60592cfeba6Schristos 
60692cfeba6Schristos static meta_search_candidate_t
asyncmeta_send_pending_op(bm_context_t * bc,int candidate)60792cfeba6Schristos asyncmeta_send_pending_op(bm_context_t *bc, int candidate)
60892cfeba6Schristos {
60992cfeba6Schristos 	meta_search_candidate_t	retcode;
61092cfeba6Schristos 	switch (bc->op->o_tag) {
61192cfeba6Schristos 		case LDAP_REQ_SEARCH:
61292cfeba6Schristos 			retcode = asyncmeta_back_search_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate,  NULL, 0 , 0);
61392cfeba6Schristos 			break;
61492cfeba6Schristos 		case LDAP_REQ_ADD:
61592cfeba6Schristos 			retcode = asyncmeta_back_add_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
61692cfeba6Schristos 			break;
61792cfeba6Schristos 		case LDAP_REQ_MODIFY:
61892cfeba6Schristos 			retcode = asyncmeta_back_modify_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
61992cfeba6Schristos 			break;
62092cfeba6Schristos 		case LDAP_REQ_MODRDN:
62192cfeba6Schristos 			retcode = asyncmeta_back_modrdn_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
62292cfeba6Schristos 			break;
62392cfeba6Schristos 		case LDAP_REQ_COMPARE:
62492cfeba6Schristos 			retcode = asyncmeta_back_compare_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
62592cfeba6Schristos 			break;
62692cfeba6Schristos 		case LDAP_REQ_DELETE:
62792cfeba6Schristos 			retcode = asyncmeta_back_delete_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
62892cfeba6Schristos 			break;
62992cfeba6Schristos 		default:
63092cfeba6Schristos 			retcode = META_SEARCH_NOT_CANDIDATE;
63192cfeba6Schristos 		}
63292cfeba6Schristos 	return retcode;
63392cfeba6Schristos }
63492cfeba6Schristos 
63592cfeba6Schristos 
63692cfeba6Schristos meta_search_candidate_t
asyncmeta_send_all_pending_ops(a_metaconn_t * mc,int candidate,void * ctx,int dolock)63792cfeba6Schristos asyncmeta_send_all_pending_ops(a_metaconn_t *mc, int candidate, void *ctx, int dolock)
63892cfeba6Schristos {
63992cfeba6Schristos 	a_metainfo_t	*mi = mc->mc_info;
64092cfeba6Schristos 	bm_context_t *bc, *onext;
64192cfeba6Schristos 	a_metasingleconn_t *msc = &mc->mc_conns[candidate];
64292cfeba6Schristos 
64392cfeba6Schristos 	if ( dolock )
64492cfeba6Schristos 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
64592cfeba6Schristos 
64692cfeba6Schristos 	msc->msc_active++;
64792cfeba6Schristos 	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
64892cfeba6Schristos 		meta_search_candidate_t ret;
64992cfeba6Schristos 		onext = LDAP_STAILQ_NEXT(bc, bc_next);
65092cfeba6Schristos 		if (bc->candidates[candidate].sr_msgid == META_MSGID_NEED_BIND)
65192cfeba6Schristos 			bc->candidates[candidate].sr_msgid = META_MSGID_GOT_BIND;
65292cfeba6Schristos 		if (bc->candidates[candidate].sr_msgid != META_MSGID_GOT_BIND || bc->bc_active > 0 || bc->op->o_abandon > 0) {
65392cfeba6Schristos 			continue;
65492cfeba6Schristos 		}
65592cfeba6Schristos 		bc->op->o_threadctx = ctx;
65692cfeba6Schristos 		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
65792cfeba6Schristos 		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
65892cfeba6Schristos 		bc->bc_active++;
65992cfeba6Schristos 		ret = asyncmeta_send_pending_op(bc, candidate);
66092cfeba6Schristos 		if (ret != META_SEARCH_CANDIDATE) {
66192cfeba6Schristos 			bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
66292cfeba6Schristos 			bc->candidates[ candidate ].sr_type = REP_RESULT;
66392cfeba6Schristos 			bc->candidates[ candidate ].sr_err = bc->rs.sr_err;
66492cfeba6Schristos 			if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
66592cfeba6Schristos 			    (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
66692cfeba6Schristos 				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
66792cfeba6Schristos 				mc->pending_ops--;
66892cfeba6Schristos 				asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
66992cfeba6Schristos 				asyncmeta_clear_bm_context(bc);
67092cfeba6Schristos 			}
67192cfeba6Schristos 		} else {
67292cfeba6Schristos 			bc->bc_active--;
67392cfeba6Schristos 		}
67492cfeba6Schristos 	}
67592cfeba6Schristos 	msc->msc_active--;
67692cfeba6Schristos 
67792cfeba6Schristos 	if ( dolock )
67892cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
67992cfeba6Schristos 
68092cfeba6Schristos 	return META_SEARCH_CANDIDATE;
68192cfeba6Schristos }
68292cfeba6Schristos 
68392cfeba6Schristos meta_search_candidate_t
asyncmeta_return_bind_errors(a_metaconn_t * mc,int candidate,SlapReply * bind_result,void * ctx,int dolock)68492cfeba6Schristos asyncmeta_return_bind_errors(a_metaconn_t *mc, int candidate, SlapReply *bind_result, void *ctx, int dolock)
68592cfeba6Schristos {
68692cfeba6Schristos 	a_metainfo_t	*mi = mc->mc_info;
68792cfeba6Schristos 	bm_context_t *bc, *onext;
68892cfeba6Schristos 
68992cfeba6Schristos 	if ( dolock )
69092cfeba6Schristos 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
69192cfeba6Schristos 
69292cfeba6Schristos 	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
69392cfeba6Schristos 		onext = LDAP_STAILQ_NEXT(bc, bc_next);
69492cfeba6Schristos 		if (bc->candidates[candidate].sr_msgid != META_MSGID_NEED_BIND
69592cfeba6Schristos 		    || bc->bc_active > 0 || bc->op->o_abandon > 0) {
69692cfeba6Schristos 			continue;
69792cfeba6Schristos 		}
69892cfeba6Schristos 		bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
69992cfeba6Schristos 		bc->candidates[ candidate ].sr_type = REP_RESULT;
70092cfeba6Schristos 		bc->candidates[ candidate ].sr_err = bind_result->sr_err;
70192cfeba6Schristos 		if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
70292cfeba6Schristos 		    (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
70392cfeba6Schristos 			LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
70492cfeba6Schristos 			bc->op->o_threadctx = ctx;
70592cfeba6Schristos 			bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
70692cfeba6Schristos 			slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
70792cfeba6Schristos 			bc->rs.sr_err = bind_result->sr_err;
70892cfeba6Schristos 			bc->rs.sr_text = bind_result->sr_text;
70992cfeba6Schristos 			mc->pending_ops--;
71092cfeba6Schristos 			asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
71192cfeba6Schristos 			asyncmeta_clear_bm_context(bc);
71292cfeba6Schristos 		}
71392cfeba6Schristos 	}
71492cfeba6Schristos 
71592cfeba6Schristos 	if ( dolock )
71692cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
71792cfeba6Schristos 
71892cfeba6Schristos 	return META_SEARCH_CANDIDATE;
71992cfeba6Schristos }
72092cfeba6Schristos 
72192cfeba6Schristos static meta_search_candidate_t
asyncmeta_handle_bind_result(LDAPMessage * msg,a_metaconn_t * mc,int candidate,void * ctx)72292cfeba6Schristos asyncmeta_handle_bind_result(LDAPMessage *msg, a_metaconn_t *mc, int candidate, void *ctx)
72392cfeba6Schristos {
72492cfeba6Schristos 	meta_search_candidate_t	retcode;
72592cfeba6Schristos 	SlapReply bind_result = {0};
72692cfeba6Schristos 	/* could modify the msc, safer to lock it */
72792cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
72892cfeba6Schristos 	retcode = asyncmeta_dobind_result( mc, candidate, &bind_result, msg );
72992cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
73092cfeba6Schristos 	if ( retcode == META_SEARCH_CANDIDATE ) {
73192cfeba6Schristos 		/* send the remaining pending ops */
73292cfeba6Schristos 		asyncmeta_send_all_pending_ops(mc, candidate, ctx, 1);
73392cfeba6Schristos 	} else {
73492cfeba6Schristos 		asyncmeta_return_bind_errors(mc, candidate, &bind_result, ctx, 1);
73592cfeba6Schristos 	}
73692cfeba6Schristos 	return retcode;
73792cfeba6Schristos }
73892cfeba6Schristos 
73992cfeba6Schristos int
asyncmeta_handle_search_msg(LDAPMessage * res,a_metaconn_t * mc,bm_context_t * bc,int candidate)74092cfeba6Schristos asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc, int candidate)
74192cfeba6Schristos {
74292cfeba6Schristos 	a_metainfo_t	*mi;
74392cfeba6Schristos 	a_metatarget_t	*mt;
74492cfeba6Schristos 	a_metasingleconn_t *msc;
74592cfeba6Schristos 	Operation *op = bc->op;
74692cfeba6Schristos 	SlapReply *rs;
74792cfeba6Schristos 	int	   i, rc = LDAP_SUCCESS, sres;
74892cfeba6Schristos 	SlapReply *candidates;
74992cfeba6Schristos 	char		**references = NULL;
75092cfeba6Schristos 	LDAPControl	**ctrls = NULL;
75192cfeba6Schristos 	a_dncookie dc;
75292cfeba6Schristos 	LDAPMessage *msg;
75392cfeba6Schristos 	ber_int_t id;
75492cfeba6Schristos 
75592cfeba6Schristos 	rs = &bc->rs;
75692cfeba6Schristos 	mi = mc->mc_info;
75792cfeba6Schristos 	mt = mi->mi_targets[ candidate ];
75892cfeba6Schristos 	msc = &mc->mc_conns[ candidate ];
75992cfeba6Schristos 	dc.op = op;
76092cfeba6Schristos 	dc.target = mt;
76192cfeba6Schristos 	dc.to_from = MASSAGE_REP;
76292cfeba6Schristos 	id = ldap_msgid(res);
76392cfeba6Schristos 
76492cfeba6Schristos 
76592cfeba6Schristos 	candidates = bc->candidates;
76692cfeba6Schristos 	i = candidate;
76792cfeba6Schristos 
76892cfeba6Schristos 	while (res && !META_BACK_CONN_INVALID(msc)) {
76992cfeba6Schristos 	for (msg = ldap_first_message(msc->msc_ldr, res); msg; msg = ldap_next_message(msc->msc_ldr, msg)) {
77092cfeba6Schristos 		switch(ldap_msgtype(msg)) {
77192cfeba6Schristos 		case LDAP_RES_SEARCH_ENTRY:
77292cfeba6Schristos 			Debug( LDAP_DEBUG_TRACE,
77392cfeba6Schristos 				"%s asyncmeta_handle_search_msg: msc %p entry\n",
77492cfeba6Schristos 				op->o_log_prefix, msc );
77592cfeba6Schristos 			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
77692cfeba6Schristos 				/* don't retry any more... */
77792cfeba6Schristos 				candidates[ i ].sr_type = REP_RESULT;
77892cfeba6Schristos 			}
77992cfeba6Schristos 			/* count entries returned by target */
78092cfeba6Schristos 			candidates[ i ].sr_nentries++;
78192cfeba6Schristos 			if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !op->o_abandon) {
78292cfeba6Schristos 				rs->sr_err = asyncmeta_send_entry( &bc->copy_op, rs, mc, i, msg );
78392cfeba6Schristos 			} else {
78492cfeba6Schristos 				goto err_cleanup;
78592cfeba6Schristos 			}
78692cfeba6Schristos 			switch ( rs->sr_err ) {
78792cfeba6Schristos 			case LDAP_SIZELIMIT_EXCEEDED:
78892cfeba6Schristos 				asyncmeta_send_ldap_result(bc, op, rs);
78992cfeba6Schristos 				rs->sr_err = LDAP_SUCCESS;
79092cfeba6Schristos 				goto err_cleanup;
79192cfeba6Schristos 			case LDAP_UNAVAILABLE:
79292cfeba6Schristos 				rs->sr_err = LDAP_OTHER;
79392cfeba6Schristos 				break;
79492cfeba6Schristos 			default:
79592cfeba6Schristos 				break;
79692cfeba6Schristos 			}
79792cfeba6Schristos 			bc->is_ok++;
79892cfeba6Schristos 			break;
79992cfeba6Schristos 
80092cfeba6Schristos 		case LDAP_RES_SEARCH_REFERENCE:
80192cfeba6Schristos 			if ( META_BACK_TGT_NOREFS( mt ) ) {
80292cfeba6Schristos 				rs->sr_err = LDAP_OTHER;
80392cfeba6Schristos 				asyncmeta_send_ldap_result(bc, op, rs);
80492cfeba6Schristos 				goto err_cleanup;
80592cfeba6Schristos 			}
80692cfeba6Schristos 			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
80792cfeba6Schristos 				/* don't retry any more... */
80892cfeba6Schristos 				candidates[ i ].sr_type = REP_RESULT;
80992cfeba6Schristos 			}
81092cfeba6Schristos 			bc->is_ok++;
81192cfeba6Schristos 			rc = ldap_parse_reference( msc->msc_ldr, msg,
81292cfeba6Schristos 				   &references, &rs->sr_ctrls, 0 );
81392cfeba6Schristos 
81492cfeba6Schristos 			if ( rc != LDAP_SUCCESS || references == NULL ) {
81592cfeba6Schristos 				rs->sr_err = LDAP_OTHER;
81692cfeba6Schristos 				asyncmeta_send_ldap_result(bc, op, rs);
81792cfeba6Schristos 				goto err_cleanup;
81892cfeba6Schristos 			}
81992cfeba6Schristos 
82092cfeba6Schristos 			/* FIXME: merge all and return at the end */
82192cfeba6Schristos 
82292cfeba6Schristos 			{
82392cfeba6Schristos 				int cnt;
82492cfeba6Schristos 				for ( cnt = 0; references[ cnt ]; cnt++ )
82592cfeba6Schristos 					;
82692cfeba6Schristos 
82792cfeba6Schristos 				rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( cnt + 1 ),
82892cfeba6Schristos 								 op->o_tmpmemctx );
82992cfeba6Schristos 
83092cfeba6Schristos 				for ( cnt = 0; references[ cnt ]; cnt++ ) {
83192cfeba6Schristos 					ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ],
83292cfeba6Schristos 							  op->o_tmpmemctx );
83392cfeba6Schristos 				}
83492cfeba6Schristos 				BER_BVZERO( &rs->sr_ref[ cnt ] );
83592cfeba6Schristos 			}
83692cfeba6Schristos 
83792cfeba6Schristos 			{
83892cfeba6Schristos 				dc.memctx = op->o_tmpmemctx;
83992cfeba6Schristos 				( void )asyncmeta_referral_result_rewrite( &dc, rs->sr_ref );
84092cfeba6Schristos 			}
84192cfeba6Schristos 
84292cfeba6Schristos 			if ( rs->sr_ref != NULL ) {
84392cfeba6Schristos 				if (!BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
84492cfeba6Schristos 					/* ignore return value by now */
84592cfeba6Schristos 					( void )send_search_reference( op, rs );
84692cfeba6Schristos 				}
84792cfeba6Schristos 
84892cfeba6Schristos 				ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx );
84992cfeba6Schristos 				rs->sr_ref = NULL;
85092cfeba6Schristos 			}
85192cfeba6Schristos 
85292cfeba6Schristos 			/* cleanup */
85392cfeba6Schristos 			if ( references ) {
85492cfeba6Schristos 				ber_memvfree( (void **)references );
85592cfeba6Schristos 			}
85692cfeba6Schristos 
85792cfeba6Schristos 			if ( rs->sr_ctrls ) {
85892cfeba6Schristos 				ldap_controls_free( rs->sr_ctrls );
85992cfeba6Schristos 				rs->sr_ctrls = NULL;
86092cfeba6Schristos 			}
86192cfeba6Schristos 			break;
86292cfeba6Schristos 
86392cfeba6Schristos 		case LDAP_RES_INTERMEDIATE:
86492cfeba6Schristos 			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
86592cfeba6Schristos 				/* don't retry any more... */
86692cfeba6Schristos 				candidates[ i ].sr_type = REP_RESULT;
86792cfeba6Schristos 			}
86892cfeba6Schristos 			bc->is_ok++;
86992cfeba6Schristos 
87092cfeba6Schristos 			/* FIXME: response controls
87192cfeba6Schristos 			 * are passed without checks */
87292cfeba6Schristos 			rs->sr_err = ldap_parse_intermediate( msc->msc_ldr,
87392cfeba6Schristos 								  msg,
87492cfeba6Schristos 								  (char **)&rs->sr_rspoid,
87592cfeba6Schristos 								  &rs->sr_rspdata,
87692cfeba6Schristos 								  &rs->sr_ctrls,
87792cfeba6Schristos 								  0 );
87892cfeba6Schristos 			if ( rs->sr_err != LDAP_SUCCESS ) {
87992cfeba6Schristos 				candidates[ i ].sr_type = REP_RESULT;
88092cfeba6Schristos 				rs->sr_err = LDAP_OTHER;
88192cfeba6Schristos 				asyncmeta_send_ldap_result(bc, op, rs);
88292cfeba6Schristos 				goto err_cleanup;
88392cfeba6Schristos 			}
88492cfeba6Schristos 
88592cfeba6Schristos 			slap_send_ldap_intermediate( op, rs );
88692cfeba6Schristos 
88792cfeba6Schristos 			if ( rs->sr_rspoid != NULL ) {
88892cfeba6Schristos 				ber_memfree( (char *)rs->sr_rspoid );
88992cfeba6Schristos 				rs->sr_rspoid = NULL;
89092cfeba6Schristos 			}
89192cfeba6Schristos 
89292cfeba6Schristos 			if ( rs->sr_rspdata != NULL ) {
89392cfeba6Schristos 				ber_bvfree( rs->sr_rspdata );
89492cfeba6Schristos 				rs->sr_rspdata = NULL;
89592cfeba6Schristos 			}
89692cfeba6Schristos 
89792cfeba6Schristos 			if ( rs->sr_ctrls != NULL ) {
89892cfeba6Schristos 				ldap_controls_free( rs->sr_ctrls );
89992cfeba6Schristos 				rs->sr_ctrls = NULL;
90092cfeba6Schristos 			}
90192cfeba6Schristos 			break;
90292cfeba6Schristos 
90392cfeba6Schristos 		case LDAP_RES_SEARCH_RESULT:
90492cfeba6Schristos 			if ( mi->mi_idle_timeout != 0 ) {
90592cfeba6Schristos 				asyncmeta_set_msc_time(msc);
90692cfeba6Schristos 			}
90792cfeba6Schristos 			Debug( LDAP_DEBUG_TRACE,
90892cfeba6Schristos 			       "%s asyncmeta_handle_search_msg: msc %p result\n",
90992cfeba6Schristos 			       op->o_log_prefix, msc );
91092cfeba6Schristos 			candidates[ i ].sr_type = REP_RESULT;
91192cfeba6Schristos 			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
91292cfeba6Schristos 			/* NOTE: ignores response controls
91392cfeba6Schristos 			 * (and intermediate response controls
91492cfeba6Schristos 			 * as well, except for those with search
91592cfeba6Schristos 			 * references); this may not be correct,
91692cfeba6Schristos 			 * but if they're not ignored then
91792cfeba6Schristos 			 * back-meta would need to merge them
91892cfeba6Schristos 			 * consistently (think of pagedResults...)
91992cfeba6Schristos 			 */
92092cfeba6Schristos 			/* FIXME: response controls? */
92192cfeba6Schristos 			rs->sr_err = ldap_parse_result( msc->msc_ldr,
92292cfeba6Schristos 							msg,
92392cfeba6Schristos 							&candidates[ i ].sr_err,
92492cfeba6Schristos 								(char **)&candidates[ i ].sr_matched,
92592cfeba6Schristos 							(char **)&candidates[ i ].sr_text,
92692cfeba6Schristos 							&references,
92792cfeba6Schristos 							&ctrls /* &candidates[ i ].sr_ctrls (unused) */ ,
92892cfeba6Schristos 							0 );
92992cfeba6Schristos 			if ( rs->sr_err != LDAP_SUCCESS ) {
93092cfeba6Schristos 				candidates[ i ].sr_err = rs->sr_err;
93192cfeba6Schristos 				sres = slap_map_api2result( &candidates[ i ] );
93292cfeba6Schristos 				candidates[ i ].sr_type = REP_RESULT;
93392cfeba6Schristos 				goto finish;
93492cfeba6Schristos 			}
93592cfeba6Schristos 
93692cfeba6Schristos 			rs->sr_err = candidates[ i ].sr_err;
93792cfeba6Schristos 
93892cfeba6Schristos 			/* massage matchedDN if need be */
93992cfeba6Schristos 			if ( candidates[ i ].sr_matched != NULL ) {
94092cfeba6Schristos 				struct berval	match, mmatch;
94192cfeba6Schristos 
94292cfeba6Schristos 				ber_str2bv( candidates[ i ].sr_matched,
94392cfeba6Schristos 						0, 0, &match );
94492cfeba6Schristos 				candidates[ i ].sr_matched = NULL;
94592cfeba6Schristos 
94692cfeba6Schristos 				dc.memctx = NULL;
94792cfeba6Schristos 				asyncmeta_dn_massage( &dc, &match, &mmatch );
94892cfeba6Schristos 				if ( mmatch.bv_val == match.bv_val ) {
94992cfeba6Schristos 					candidates[ i ].sr_matched
95092cfeba6Schristos 						= ch_strdup( mmatch.bv_val );
95192cfeba6Schristos 
95292cfeba6Schristos 				} else {
95392cfeba6Schristos 					candidates[ i ].sr_matched = mmatch.bv_val;
95492cfeba6Schristos 				}
95592cfeba6Schristos 
95692cfeba6Schristos 				bc->candidate_match++;
95792cfeba6Schristos 				ldap_memfree( match.bv_val );
95892cfeba6Schristos 			}
95992cfeba6Schristos 
96092cfeba6Schristos 			/* add references to array */
96192cfeba6Schristos 			/* RFC 4511: referrals can only appear
96292cfeba6Schristos 			 * if result code is LDAP_REFERRAL */
96392cfeba6Schristos 			if ( references != NULL
96492cfeba6Schristos 				 && references[ 0 ] != NULL
96592cfeba6Schristos 				 && references[ 0 ][ 0 ] != '\0' )
96692cfeba6Schristos 			{
96792cfeba6Schristos 				if ( rs->sr_err != LDAP_REFERRAL ) {
96892cfeba6Schristos 					Debug( LDAP_DEBUG_ANY,
96992cfeba6Schristos 						   "%s asncmeta_search_result[%d]: "
97092cfeba6Schristos 						   "got referrals with err=%d\n",
97192cfeba6Schristos 						   op->o_log_prefix,
97292cfeba6Schristos 						   i, rs->sr_err );
97392cfeba6Schristos 
97492cfeba6Schristos 				} else {
97592cfeba6Schristos 					BerVarray	sr_ref;
97692cfeba6Schristos 					int		cnt;
97792cfeba6Schristos 
97892cfeba6Schristos 					for ( cnt = 0; references[ cnt ]; cnt++ )
97992cfeba6Schristos 						;
98092cfeba6Schristos 
98192cfeba6Schristos 					sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( cnt + 1 ),
98292cfeba6Schristos 								 op->o_tmpmemctx );
98392cfeba6Schristos 
98492cfeba6Schristos 					for ( cnt = 0; references[ cnt ]; cnt++ ) {
98592cfeba6Schristos 						ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ],
98692cfeba6Schristos 								  op->o_tmpmemctx );
98792cfeba6Schristos 					}
98892cfeba6Schristos 					BER_BVZERO( &sr_ref[ cnt ] );
98992cfeba6Schristos 
99092cfeba6Schristos 					dc.memctx = op->o_tmpmemctx;
99192cfeba6Schristos 					( void )asyncmeta_referral_result_rewrite( &dc, sr_ref );
99292cfeba6Schristos 
99392cfeba6Schristos 					if ( rs->sr_v2ref == NULL ) {
99492cfeba6Schristos 						rs->sr_v2ref = sr_ref;
99592cfeba6Schristos 
99692cfeba6Schristos 					} else {
99792cfeba6Schristos 						for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
99892cfeba6Schristos 							ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ],
99992cfeba6Schristos 									   op->o_tmpmemctx );
100092cfeba6Schristos 						}
100192cfeba6Schristos 						ber_memfree_x( sr_ref, op->o_tmpmemctx );
100292cfeba6Schristos 					}
100392cfeba6Schristos 				}
100492cfeba6Schristos 
100592cfeba6Schristos 			} else if ( rs->sr_err == LDAP_REFERRAL ) {
100692cfeba6Schristos 				Debug( LDAP_DEBUG_TRACE,
100792cfeba6Schristos 					   "%s asyncmeta_search_result[%d]: "
100892cfeba6Schristos 					   "got err=%d with null "
100992cfeba6Schristos 					   "or empty referrals\n",
101092cfeba6Schristos 					   op->o_log_prefix,
101192cfeba6Schristos 					   i, rs->sr_err );
101292cfeba6Schristos 
101392cfeba6Schristos 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
101492cfeba6Schristos 			}
101592cfeba6Schristos 
101692cfeba6Schristos 			/* cleanup */
101792cfeba6Schristos 			ber_memvfree( (void **)references );
101892cfeba6Schristos 
101992cfeba6Schristos 			sres = slap_map_api2result( rs );
102092cfeba6Schristos 
102192cfeba6Schristos 			if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
102292cfeba6Schristos 				Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d] "
102392cfeba6Schristos 				       "match=\"%s\" err=%ld\n",
102492cfeba6Schristos 				       op->o_log_prefix, i,
102592cfeba6Schristos 				       candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
102692cfeba6Schristos 				       (long) candidates[ i ].sr_err );
102792cfeba6Schristos 			} else {
102892cfeba6Schristos 					Debug( LDAP_DEBUG_ANY,  "%s asyncmeta_search_result[%d] "
102992cfeba6Schristos 				       "match=\"%s\" err=%ld (%s)\n",
103092cfeba6Schristos 				       op->o_log_prefix, i,
103192cfeba6Schristos 				       candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
103292cfeba6Schristos 					       (long) candidates[ i ].sr_err, ldap_err2string( candidates[ i ].sr_err ) );
103392cfeba6Schristos 			}
103492cfeba6Schristos 
103592cfeba6Schristos 			switch ( sres ) {
103692cfeba6Schristos 			case LDAP_NO_SUCH_OBJECT:
103792cfeba6Schristos 				/* is_ok is touched any time a valid
103892cfeba6Schristos 				 * (even intermediate) result is
103992cfeba6Schristos 				 * returned; as a consequence, if
104092cfeba6Schristos 				 * a candidate returns noSuchObject
104192cfeba6Schristos 				 * it is ignored and the candidate
104292cfeba6Schristos 				 * is simply demoted. */
104392cfeba6Schristos 				if ( bc->is_ok ) {
104492cfeba6Schristos 					sres = LDAP_SUCCESS;
104592cfeba6Schristos 				}
104692cfeba6Schristos 				break;
104792cfeba6Schristos 
104892cfeba6Schristos 			case LDAP_SUCCESS:
104992cfeba6Schristos 				if ( ctrls != NULL && ctrls[0] != NULL ) {
105092cfeba6Schristos #ifdef SLAPD_META_CLIENT_PR
105192cfeba6Schristos 					LDAPControl *pr_c;
105292cfeba6Schristos 
105392cfeba6Schristos 					pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
105492cfeba6Schristos 					if ( pr_c != NULL ) {
105592cfeba6Schristos 						BerElementBuffer berbuf;
105692cfeba6Schristos 						BerElement *ber = (BerElement *)&berbuf;
105792cfeba6Schristos 						ber_tag_t tag;
105892cfeba6Schristos 						ber_int_t prsize;
105992cfeba6Schristos 						struct berval prcookie;
106092cfeba6Schristos 
106192cfeba6Schristos 						/* unsolicited, do not accept */
106292cfeba6Schristos 						if ( mt->mt_ps == 0 ) {
106392cfeba6Schristos 							rs->sr_err = LDAP_OTHER;
106492cfeba6Schristos 							goto err_pr;
106592cfeba6Schristos 						}
106692cfeba6Schristos 
106792cfeba6Schristos 						ber_init2( ber, &pr_c->ldctl_value, LBER_USE_DER );
106892cfeba6Schristos 
106992cfeba6Schristos 						tag = ber_scanf( ber, "{im}", &prsize, &prcookie );
107092cfeba6Schristos 						if ( tag == LBER_ERROR ) {
107192cfeba6Schristos 							rs->sr_err = LDAP_OTHER;
107292cfeba6Schristos 							goto err_pr;
107392cfeba6Schristos 						}
107492cfeba6Schristos 
107592cfeba6Schristos 						/* more pages? new search request */
107692cfeba6Schristos 						if ( !BER_BVISNULL( &prcookie ) && !BER_BVISEMPTY( &prcookie ) ) {
107792cfeba6Schristos 							if ( mt->mt_ps > 0 ) {
107892cfeba6Schristos 								/* ignore size if specified */
107992cfeba6Schristos 								prsize = 0;
108092cfeba6Schristos 
108192cfeba6Schristos 							} else if ( prsize == 0 ) {
108292cfeba6Schristos 								/* guess the page size from the entries returned so far */
108392cfeba6Schristos 								prsize = candidates[ i ].sr_nentries;
108492cfeba6Schristos 							}
108592cfeba6Schristos 
108692cfeba6Schristos 							candidates[ i ].sr_nentries = 0;
108792cfeba6Schristos 							candidates[ i ].sr_msgid = META_MSGID_IGNORE;
108892cfeba6Schristos 							candidates[ i ].sr_type = REP_INTERMEDIATE;
108992cfeba6Schristos 
109092cfeba6Schristos 							assert( candidates[ i ].sr_matched == NULL );
109192cfeba6Schristos 							assert( candidates[ i ].sr_text == NULL );
109292cfeba6Schristos 							assert( candidates[ i ].sr_ref == NULL );
109392cfeba6Schristos 
109492cfeba6Schristos 							switch ( asyncmeta_back_search_start( &bc->copy_op, rs, mc, bc, i, &prcookie, prsize, 1 ) )
109592cfeba6Schristos 							{
109692cfeba6Schristos 							case META_SEARCH_CANDIDATE:
109792cfeba6Schristos 								assert( candidates[ i ].sr_msgid >= 0 );
109892cfeba6Schristos 								ldap_controls_free( ctrls );
109992cfeba6Schristos 								//	goto free_message;
110092cfeba6Schristos 
110192cfeba6Schristos 							case META_SEARCH_ERR:
110292cfeba6Schristos 							case META_SEARCH_NEED_BIND:
110392cfeba6Schristos err_pr:;
110492cfeba6Schristos 								candidates[ i ].sr_err = rs->sr_err;
110592cfeba6Schristos 								candidates[ i ].sr_type = REP_RESULT;
110692cfeba6Schristos 								if ( META_BACK_ONERR_STOP( mi ) ) {
110792cfeba6Schristos 									asyncmeta_send_ldap_result(bc, op, rs);
110892cfeba6Schristos 									ldap_controls_free( ctrls );
110992cfeba6Schristos 									goto err_cleanup;
111092cfeba6Schristos 								}
111192cfeba6Schristos 								/* fallthru */
111292cfeba6Schristos 
111392cfeba6Schristos 							case META_SEARCH_NOT_CANDIDATE:
111492cfeba6Schristos 								/* means that asyncmeta_back_search_start()
111592cfeba6Schristos 								 * failed but onerr == continue */
111692cfeba6Schristos 								candidates[ i ].sr_msgid = META_MSGID_IGNORE;
111792cfeba6Schristos 								candidates[ i ].sr_type = REP_RESULT;
111892cfeba6Schristos 								break;
111992cfeba6Schristos 
112092cfeba6Schristos 							default:
112192cfeba6Schristos 								/* impossible */
112292cfeba6Schristos 								assert( 0 );
112392cfeba6Schristos 								break;
112492cfeba6Schristos 							}
112592cfeba6Schristos 							break;
112692cfeba6Schristos 						}
112792cfeba6Schristos 					}
112892cfeba6Schristos #endif /* SLAPD_META_CLIENT_PR */
112992cfeba6Schristos 
113092cfeba6Schristos 					ldap_controls_free( ctrls );
113192cfeba6Schristos 				}
113292cfeba6Schristos 				/* fallthru */
113392cfeba6Schristos 
113492cfeba6Schristos 			case LDAP_REFERRAL:
113592cfeba6Schristos 				bc->is_ok++;
113692cfeba6Schristos 				break;
113792cfeba6Schristos 
113892cfeba6Schristos 			case LDAP_SIZELIMIT_EXCEEDED:
113992cfeba6Schristos 				/* if a target returned sizelimitExceeded
114092cfeba6Schristos 				 * and the entry count is equal to the
114192cfeba6Schristos 				 * proxy's limit, the target would have
114292cfeba6Schristos 				 * returned more, and the error must be
114392cfeba6Schristos 				 * propagated to the client; otherwise,
114492cfeba6Schristos 				 * the target enforced a limit lower
114592cfeba6Schristos 				 * than what requested by the proxy;
114692cfeba6Schristos 				 * ignore it */
114792cfeba6Schristos 				candidates[ i ].sr_err = rs->sr_err;
114892cfeba6Schristos 				if ( rs->sr_nentries == op->ors_slimit
114992cfeba6Schristos 					 || META_BACK_ONERR_STOP( mi ) )
115092cfeba6Schristos 				{
115192cfeba6Schristos 					const char *save_text;
115292cfeba6Schristos got_err:
115392cfeba6Schristos 					save_text = rs->sr_text;
115492cfeba6Schristos 					rs->sr_text = candidates[ i ].sr_text;
115592cfeba6Schristos 					asyncmeta_send_ldap_result(bc, op, rs);
115692cfeba6Schristos 					if (candidates[ i ].sr_text != NULL) {
115792cfeba6Schristos 						ch_free( (char *)candidates[ i ].sr_text );
115892cfeba6Schristos 						candidates[ i ].sr_text = NULL;
115992cfeba6Schristos 					}
116092cfeba6Schristos 					rs->sr_text = save_text;
116192cfeba6Schristos 					ldap_controls_free( ctrls );
116292cfeba6Schristos 					goto err_cleanup;
116392cfeba6Schristos 				}
116492cfeba6Schristos 				break;
116592cfeba6Schristos 
116692cfeba6Schristos 			default:
116792cfeba6Schristos 				candidates[ i ].sr_err = rs->sr_err;
116892cfeba6Schristos 				if ( META_BACK_ONERR_STOP( mi ) ) {
116992cfeba6Schristos 					goto got_err;
117092cfeba6Schristos 				}
117192cfeba6Schristos 				break;
117292cfeba6Schristos 			}
117392cfeba6Schristos 			/* if this is the last result we will ever receive, send it back  */
117492cfeba6Schristos 			rc = rs->sr_err;
117592cfeba6Schristos 			if (asyncmeta_is_last_result(mc, bc, i) == 0) {
117692cfeba6Schristos 				Debug( LDAP_DEBUG_TRACE,
117792cfeba6Schristos 					"%s asyncmeta_handle_search_msg: msc %p last result\n",
117892cfeba6Schristos 					op->o_log_prefix, msc );
117992cfeba6Schristos 				asyncmeta_search_last_result(mc, bc, i, sres);
118092cfeba6Schristos err_cleanup:
118192cfeba6Schristos 				rc = rs->sr_err;
118292cfeba6Schristos 				ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
118392cfeba6Schristos 				asyncmeta_drop_bc( mc, bc);
118492cfeba6Schristos 				asyncmeta_clear_bm_context(bc);
118592cfeba6Schristos 				ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
118692cfeba6Schristos 				ldap_msgfree(res);
118792cfeba6Schristos 				return rc;
118892cfeba6Schristos 			}
118992cfeba6Schristos finish:
119092cfeba6Schristos 			break;
119192cfeba6Schristos 
119292cfeba6Schristos 		default:
119392cfeba6Schristos 			continue;
119492cfeba6Schristos 		}
119592cfeba6Schristos 	}
119692cfeba6Schristos 		ldap_msgfree(res);
119792cfeba6Schristos 		res = NULL;
119892cfeba6Schristos 		if (candidates[ i ].sr_type != REP_RESULT) {
119992cfeba6Schristos 			struct timeval	tv = {0};
120092cfeba6Schristos 			rc = ldap_result( msc->msc_ldr, id, LDAP_MSG_RECEIVED, &tv, &res );
120192cfeba6Schristos 			if (res != NULL) {
120292cfeba6Schristos 				msc->msc_result_time = slap_get_time();
120392cfeba6Schristos 			}
120492cfeba6Schristos 		}
120592cfeba6Schristos 	}
120692cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
120792cfeba6Schristos 	bc->bc_active--;
120892cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
120992cfeba6Schristos 
121092cfeba6Schristos 	return rc;
121192cfeba6Schristos }
121292cfeba6Schristos 
121392cfeba6Schristos /* handles the received result for add, modify, modrdn, compare and delete ops */
121492cfeba6Schristos 
asyncmeta_handle_common_result(LDAPMessage * msg,a_metaconn_t * mc,bm_context_t * bc,int candidate)121592cfeba6Schristos int asyncmeta_handle_common_result(LDAPMessage *msg, a_metaconn_t *mc, bm_context_t *bc, int candidate)
121692cfeba6Schristos {
121792cfeba6Schristos 	a_metainfo_t	*mi;
121892cfeba6Schristos 	a_metatarget_t	*mt;
121992cfeba6Schristos 	a_metasingleconn_t *msc;
122092cfeba6Schristos 	const char	*save_text = NULL,
122192cfeba6Schristos 		*save_matched = NULL;
122292cfeba6Schristos 	BerVarray	save_ref = NULL;
122392cfeba6Schristos 	LDAPControl	**save_ctrls = NULL;
122492cfeba6Schristos 	void		*matched_ctx = NULL;
122592cfeba6Schristos 
122692cfeba6Schristos 	char		*matched = NULL;
122792cfeba6Schristos 	char		*text = NULL;
122892cfeba6Schristos 	char		**refs = NULL;
122992cfeba6Schristos 	LDAPControl	**ctrls = NULL;
123092cfeba6Schristos 	Operation *op;
123192cfeba6Schristos 	SlapReply *rs;
123292cfeba6Schristos 	int		rc;
123392cfeba6Schristos 
123492cfeba6Schristos 	mi = mc->mc_info;
123592cfeba6Schristos 	mt = mi->mi_targets[ candidate ];
123692cfeba6Schristos 	msc = &mc->mc_conns[ candidate ];
123792cfeba6Schristos 
123892cfeba6Schristos 	op = bc->op;
123992cfeba6Schristos 	rs = &bc->rs;
124092cfeba6Schristos 	save_text = rs->sr_text,
124192cfeba6Schristos 	save_matched = rs->sr_matched;
124292cfeba6Schristos 	save_ref = rs->sr_ref;
124392cfeba6Schristos 	save_ctrls = rs->sr_ctrls;
124492cfeba6Schristos 	rs->sr_text = NULL;
124592cfeba6Schristos 	rs->sr_matched = NULL;
124692cfeba6Schristos 	rs->sr_ref = NULL;
124792cfeba6Schristos 	rs->sr_ctrls = NULL;
124892cfeba6Schristos 
124992cfeba6Schristos 	/* only touch when activity actually took place... */
125092cfeba6Schristos 	if ( mi->mi_idle_timeout != 0 ) {
125192cfeba6Schristos 		asyncmeta_set_msc_time(msc);
125292cfeba6Schristos 	}
125392cfeba6Schristos 
125492cfeba6Schristos 	rc = ldap_parse_result( msc->msc_ldr, msg, &rs->sr_err,
125592cfeba6Schristos 				&matched, &text, &refs, &ctrls, 0 );
125692cfeba6Schristos 
125792cfeba6Schristos 	if ( rc == LDAP_SUCCESS ) {
125892cfeba6Schristos 		rs->sr_text = text;
125992cfeba6Schristos 	} else {
126092cfeba6Schristos 		rs->sr_err = rc;
126192cfeba6Schristos 	}
126292cfeba6Schristos 	rs->sr_err = slap_map_api2result( rs );
126392cfeba6Schristos 
126492cfeba6Schristos 	/* RFC 4511: referrals can only appear
126592cfeba6Schristos 	 * if result code is LDAP_REFERRAL */
126692cfeba6Schristos 	if ( refs != NULL
126792cfeba6Schristos 	     && refs[ 0 ] != NULL
126892cfeba6Schristos 	     && refs[ 0 ][ 0 ] != '\0' )
126992cfeba6Schristos 	{
127092cfeba6Schristos 		if ( rs->sr_err != LDAP_REFERRAL ) {
127192cfeba6Schristos 			Debug( LDAP_DEBUG_ANY,
127292cfeba6Schristos 			       "%s asyncmeta_handle_common_result[%d]: "
127392cfeba6Schristos 			       "got referrals with err=%d\n",
127492cfeba6Schristos 			       op->o_log_prefix,
127592cfeba6Schristos 			       candidate, rs->sr_err );
127692cfeba6Schristos 
127792cfeba6Schristos 		} else {
127892cfeba6Schristos 			int	i;
127992cfeba6Schristos 
128092cfeba6Schristos 			for ( i = 0; refs[ i ] != NULL; i++ )
128192cfeba6Schristos 				/* count */ ;
128292cfeba6Schristos 			rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
128392cfeba6Schristos 						     op->o_tmpmemctx );
128492cfeba6Schristos 			for ( i = 0; refs[ i ] != NULL; i++ ) {
128592cfeba6Schristos 				ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
128692cfeba6Schristos 			}
128792cfeba6Schristos 			BER_BVZERO( &rs->sr_ref[ i ] );
128892cfeba6Schristos 		}
128992cfeba6Schristos 
129092cfeba6Schristos 	} else if ( rs->sr_err == LDAP_REFERRAL ) {
129192cfeba6Schristos 		Debug( LDAP_DEBUG_ANY,
129292cfeba6Schristos 		       "%s asyncmeta_handle_common_result[%d]: "
129392cfeba6Schristos 		       "got err=%d with null "
129492cfeba6Schristos 		       "or empty referrals\n",
129592cfeba6Schristos 		       op->o_log_prefix,
129692cfeba6Schristos 		       candidate, rs->sr_err );
129792cfeba6Schristos 
129892cfeba6Schristos 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
129992cfeba6Schristos 	}
130092cfeba6Schristos 
130192cfeba6Schristos 	if ( ctrls != NULL ) {
130292cfeba6Schristos 		rs->sr_ctrls = ctrls;
130392cfeba6Schristos 	}
130492cfeba6Schristos 
130592cfeba6Schristos 	/* if the error in the reply structure is not
130692cfeba6Schristos 	 * LDAP_SUCCESS, try to map it from client
130792cfeba6Schristos 	 * to server error */
130892cfeba6Schristos 	if ( !LDAP_ERR_OK( rs->sr_err ) ) {
130992cfeba6Schristos 		rs->sr_err = slap_map_api2result( rs );
131092cfeba6Schristos 
131192cfeba6Schristos 		/* internal ops ( op->o_conn == NULL )
131292cfeba6Schristos 		 * must not reply to client */
131392cfeba6Schristos 		if ( op->o_conn && !op->o_do_not_cache && matched ) {
131492cfeba6Schristos 
131592cfeba6Schristos 			/* record the (massaged) matched
131692cfeba6Schristos 			 * DN into the reply structure */
131792cfeba6Schristos 			rs->sr_matched = matched;
131892cfeba6Schristos 		}
131992cfeba6Schristos 	}
132092cfeba6Schristos 
132192cfeba6Schristos 	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
132292cfeba6Schristos 		asyncmeta_quarantine( op, mi, rs, candidate );
132392cfeba6Schristos 	}
132492cfeba6Schristos 
132592cfeba6Schristos 	if ( matched != NULL ) {
132692cfeba6Schristos 		struct berval	dn, pdn;
132792cfeba6Schristos 
132892cfeba6Schristos 		ber_str2bv( matched, 0, 0, &dn );
132992cfeba6Schristos 		if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
133092cfeba6Schristos 			ldap_memfree( matched );
133192cfeba6Schristos 			matched_ctx = op->o_tmpmemctx;
133292cfeba6Schristos 			matched = pdn.bv_val;
133392cfeba6Schristos 		}
133492cfeba6Schristos 		rs->sr_matched = matched;
133592cfeba6Schristos 	}
133692cfeba6Schristos 
133792cfeba6Schristos 	if ( rs->sr_err == LDAP_UNAVAILABLE || rs->sr_err == LDAP_SERVER_DOWN ) {
133892cfeba6Schristos 		if ( rs->sr_text == NULL ) {
133992cfeba6Schristos 				rs->sr_text = "Target is unavailable";
134092cfeba6Schristos 			}
134192cfeba6Schristos 	}
134292cfeba6Schristos 
134392cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
134492cfeba6Schristos 	asyncmeta_drop_bc( mc, bc);
134592cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
134692cfeba6Schristos 
134792cfeba6Schristos 	if ( op->o_conn ) {
134892cfeba6Schristos 		asyncmeta_send_ldap_result(bc, op, rs);
134992cfeba6Schristos 	}
135092cfeba6Schristos 
135192cfeba6Schristos 	if ( matched ) {
135292cfeba6Schristos 		op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
135392cfeba6Schristos 	}
135492cfeba6Schristos 	if ( text ) {
135592cfeba6Schristos 		ldap_memfree( text );
135692cfeba6Schristos 	}
135792cfeba6Schristos 	if ( rs->sr_ref ) {
135892cfeba6Schristos 		op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
135992cfeba6Schristos 		rs->sr_ref = NULL;
136092cfeba6Schristos 	}
136192cfeba6Schristos 	if ( refs ) {
136292cfeba6Schristos 		ber_memvfree( (void **)refs );
136392cfeba6Schristos 	}
136492cfeba6Schristos 	if ( ctrls ) {
136592cfeba6Schristos 		assert( rs->sr_ctrls != NULL );
136692cfeba6Schristos 		ldap_controls_free( ctrls );
136792cfeba6Schristos 	}
136892cfeba6Schristos 
136992cfeba6Schristos 	rs->sr_text = save_text;
137092cfeba6Schristos 	rs->sr_matched = save_matched;
137192cfeba6Schristos 	rs->sr_ref = save_ref;
137292cfeba6Schristos 	rs->sr_ctrls = save_ctrls;
137392cfeba6Schristos 	rc = (LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err);
137492cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
137592cfeba6Schristos 	asyncmeta_clear_bm_context(bc);
137692cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
137792cfeba6Schristos 	return rc;
137892cfeba6Schristos }
137992cfeba6Schristos 
138092cfeba6Schristos /* This takes care to clean out the outbound queue in case we have a read error
138192cfeba6Schristos  * sending back responses to the client */
138292cfeba6Schristos int
asyncmeta_op_read_error(a_metaconn_t * mc,int candidate,int error,void * ctx)138392cfeba6Schristos asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error, void* ctx)
138492cfeba6Schristos {
138592cfeba6Schristos 	bm_context_t *bc, *onext;
138692cfeba6Schristos 	int cleanup;
138792cfeba6Schristos 	Operation *op;
138892cfeba6Schristos 	SlapReply *rs;
138992cfeba6Schristos 	SlapReply *candidates;
139092cfeba6Schristos 
139192cfeba6Schristos 	/* no outstanding ops, nothing to do but log */
139292cfeba6Schristos 	Debug( LDAP_DEBUG_TRACE,
139392cfeba6Schristos 	       "asyncmeta_op_read_error: ldr=%p, err=%d\n",
139492cfeba6Schristos 	       mc->mc_conns[candidate].msc_ldr, error );
139592cfeba6Schristos 
139692cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
139792cfeba6Schristos 	/*someone may be trying to write */
139892cfeba6Schristos 	if (mc->mc_conns[candidate].msc_active <= 1) {
139992cfeba6Schristos 		asyncmeta_clear_one_msc(NULL, mc, candidate, 0, __FUNCTION__);
140092cfeba6Schristos 	} else {
140192cfeba6Schristos 		META_BACK_CONN_INVALID_SET(&mc->mc_conns[candidate]);
140292cfeba6Schristos 	}
140392cfeba6Schristos 
140492cfeba6Schristos 	if (mc->pending_ops <= 0) {
140592cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
140692cfeba6Schristos 		return LDAP_SUCCESS;
140792cfeba6Schristos 	}
140892cfeba6Schristos 
140992cfeba6Schristos 	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
141092cfeba6Schristos 		onext = LDAP_STAILQ_NEXT(bc, bc_next);
141192cfeba6Schristos 		cleanup = 0;
141292cfeba6Schristos 		candidates = bc->candidates;
141392cfeba6Schristos 		/* was this op affected? */
141492cfeba6Schristos 		if ( !META_IS_CANDIDATE( &candidates[ candidate ] ) )
141592cfeba6Schristos 			continue;
141692cfeba6Schristos 
141792cfeba6Schristos 		if (bc->op->o_abandon) {
141892cfeba6Schristos 			bc->bc_invalid = 1;
141992cfeba6Schristos 			continue;
142092cfeba6Schristos 		}
142192cfeba6Schristos 
142292cfeba6Schristos 		if (bc->bc_active > 0) {
142392cfeba6Schristos 			bc->bc_invalid = 1;
142492cfeba6Schristos 			continue;
142592cfeba6Schristos 		}
142692cfeba6Schristos 
142792cfeba6Schristos 		bc->op->o_threadctx = ctx;
142892cfeba6Schristos 		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
142992cfeba6Schristos 		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
143092cfeba6Schristos 
143192cfeba6Schristos 		op = bc->op;
143292cfeba6Schristos 		rs = &bc->rs;
143392cfeba6Schristos 		switch (op->o_tag) {
143492cfeba6Schristos 		case LDAP_REQ_ADD:
143592cfeba6Schristos 		case LDAP_REQ_MODIFY:
143692cfeba6Schristos 		case LDAP_REQ_MODRDN:
143792cfeba6Schristos 		case LDAP_REQ_COMPARE:
143892cfeba6Schristos 		case LDAP_REQ_DELETE:
143992cfeba6Schristos 			rs->sr_err = LDAP_UNAVAILABLE;
144092cfeba6Schristos 			rs->sr_text = "Read error on connection to target";
144192cfeba6Schristos 			asyncmeta_send_ldap_result( bc, op, rs );
144292cfeba6Schristos 			cleanup = 1;
144392cfeba6Schristos 			break;
144492cfeba6Schristos 		case LDAP_REQ_SEARCH:
144592cfeba6Schristos 		{
144692cfeba6Schristos 			a_metainfo_t *mi = mc->mc_info;
144792cfeba6Schristos 			rs->sr_err = LDAP_UNAVAILABLE;
144892cfeba6Schristos 			rs->sr_text = "Read error on connection to target";
144992cfeba6Schristos 			candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
145092cfeba6Schristos 			candidates[ candidate ].sr_type = REP_RESULT;
145192cfeba6Schristos 			if ( (META_BACK_ONERR_STOP( mi ) ||
145292cfeba6Schristos 			      asyncmeta_is_last_result(mc, bc, candidate)) && op->o_conn) {
145392cfeba6Schristos 				asyncmeta_send_ldap_result( bc, op, rs );
145492cfeba6Schristos 				cleanup = 1;
145592cfeba6Schristos 			}
145692cfeba6Schristos 		}
145792cfeba6Schristos 			break;
145892cfeba6Schristos 		default:
145992cfeba6Schristos 			break;
146092cfeba6Schristos 		}
146192cfeba6Schristos 
146292cfeba6Schristos 		if (cleanup) {
146392cfeba6Schristos 			int j;
146492cfeba6Schristos 			a_metainfo_t *mi = mc->mc_info;
146592cfeba6Schristos 			for (j=0; j<mi->mi_ntargets; j++) {
146692cfeba6Schristos 				if (j != candidate && bc->candidates[j].sr_msgid >= 0
146792cfeba6Schristos 				    && mc->mc_conns[j].msc_ld != NULL) {
146892cfeba6Schristos 					asyncmeta_back_cancel( mc, op,
146992cfeba6Schristos 							       bc->candidates[ j ].sr_msgid, j );
147092cfeba6Schristos 				}
147192cfeba6Schristos 			}
147292cfeba6Schristos 			LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
147392cfeba6Schristos 			mc->pending_ops--;
147492cfeba6Schristos 			asyncmeta_clear_bm_context(bc);
147592cfeba6Schristos 		}
147692cfeba6Schristos 	}
147792cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
147892cfeba6Schristos 	return LDAP_SUCCESS;
147992cfeba6Schristos }
148092cfeba6Schristos 
148192cfeba6Schristos void *
asyncmeta_op_handle_result(void * ctx,void * arg)148292cfeba6Schristos asyncmeta_op_handle_result(void *ctx, void *arg)
148392cfeba6Schristos {
148492cfeba6Schristos 	a_metaconn_t *mc = arg;
148592cfeba6Schristos 	int		i, j, rc, ntargets;
148692cfeba6Schristos 	struct timeval	tv = {0};
148792cfeba6Schristos 	LDAPMessage     *msg;
148892cfeba6Schristos 	a_metasingleconn_t *msc;
148992cfeba6Schristos 	bm_context_t *bc;
149092cfeba6Schristos 	void *oldctx;
149192cfeba6Schristos 
149292cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
149392cfeba6Schristos 	rc = ++mc->mc_active;
149492cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
149592cfeba6Schristos 	if (rc > 1)
149692cfeba6Schristos 		return NULL;
149792cfeba6Schristos 
149892cfeba6Schristos 	ntargets = mc->mc_info->mi_ntargets;
149992cfeba6Schristos 	i = ntargets;
150092cfeba6Schristos 	oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);	/* get existing memctx */
150192cfeba6Schristos 
150292cfeba6Schristos again:
150392cfeba6Schristos 	for (j=0; j<ntargets; j++) {
150492cfeba6Schristos 		i++;
150592cfeba6Schristos 		if (i >= ntargets) i = 0;
150692cfeba6Schristos 		msc = &mc->mc_conns[i];
150792cfeba6Schristos 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
150892cfeba6Schristos 		if (!mc->mc_conns[i].msc_ldr ||
150992cfeba6Schristos 		    META_BACK_CONN_CREATING( &mc->mc_conns[i] ) ||
151092cfeba6Schristos 		    META_BACK_CONN_INVALID(&mc->mc_conns[i])) {
151192cfeba6Schristos 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
151292cfeba6Schristos 			continue;
151392cfeba6Schristos 		}
151492cfeba6Schristos 
151592cfeba6Schristos 		msc->msc_active++;
151692cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
151792cfeba6Schristos 
151892cfeba6Schristos 		rc = ldap_result( mc->mc_conns[i].msc_ldr, LDAP_RES_ANY, LDAP_MSG_RECEIVED, &tv, &msg );
151992cfeba6Schristos 		if (rc < 1) {
152092cfeba6Schristos 			if (rc < 0) {
152192cfeba6Schristos 				ldap_get_option( mc->mc_conns[i].msc_ldr, LDAP_OPT_ERROR_NUMBER, &rc);
152292cfeba6Schristos 				META_BACK_CONN_INVALID_SET(&mc->mc_conns[i]);
152392cfeba6Schristos 				asyncmeta_op_read_error(mc, i, rc, ctx);
152492cfeba6Schristos 			}
152592cfeba6Schristos 			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
152692cfeba6Schristos 			msc->msc_active--;
152792cfeba6Schristos 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
152892cfeba6Schristos 			continue;
152992cfeba6Schristos 		}
153092cfeba6Schristos 		rc = ldap_msgtype( msg );
153192cfeba6Schristos 		if (rc == LDAP_RES_BIND) {
153292cfeba6Schristos 			if ( LogTest( asyncmeta_debug ) ) {
153392cfeba6Schristos 				char	time_buf[ SLAP_TEXT_BUFLEN ];
153492cfeba6Schristos 				asyncmeta_get_timestamp(time_buf);
153592cfeba6Schristos 				Debug( asyncmeta_debug, "[%s] asyncmeta_op_handle_result received bind msgid=%d msc: %p\n",
153692cfeba6Schristos 				      time_buf, ldap_msgid(msg), msc );
153792cfeba6Schristos 			}
153892cfeba6Schristos 			asyncmeta_handle_bind_result(msg, mc, i, ctx);
153992cfeba6Schristos 			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
154092cfeba6Schristos 			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
154192cfeba6Schristos 			msc->msc_result_time = slap_get_time();
154292cfeba6Schristos 			msc->msc_active--;
154392cfeba6Schristos 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
154492cfeba6Schristos 			if (msg)
154592cfeba6Schristos 				ldap_msgfree(msg);
154692cfeba6Schristos 
154792cfeba6Schristos 			continue;
154892cfeba6Schristos 		}
154992cfeba6Schristos retry_bc:
155092cfeba6Schristos 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
155192cfeba6Schristos 		bc = asyncmeta_find_message(ldap_msgid(msg), mc, i);
155292cfeba6Schristos /* The sender might not be yet done with the context. On error it might also remove it
155392cfeba6Schristos  * so it's best to try and find it again after a wait */
155492cfeba6Schristos 		if (bc && bc->bc_active > 0) {
155592cfeba6Schristos 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
155692cfeba6Schristos 			ldap_pvt_thread_yield();
155792cfeba6Schristos 			goto retry_bc;
155892cfeba6Schristos 		}
155992cfeba6Schristos 		if (bc) {
156092cfeba6Schristos 			bc->bc_active++;
156192cfeba6Schristos 		}
156292cfeba6Schristos 
156392cfeba6Schristos 		msc->msc_result_time = slap_get_time();
156492cfeba6Schristos 		msc->msc_active--;
156592cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
156692cfeba6Schristos 		if (!bc) {
156792cfeba6Schristos 			Debug( asyncmeta_debug,
156892cfeba6Schristos 				"asyncmeta_op_handle_result: Unable to find bc for msguid %d, msc: %p\n", ldap_msgid(msg), msc );
156992cfeba6Schristos 			ldap_msgfree(msg);
157092cfeba6Schristos 			continue;
157192cfeba6Schristos 		}
157292cfeba6Schristos 
157392cfeba6Schristos 		/* set our memctx */
157492cfeba6Schristos 		bc->op->o_threadctx = ctx;
157592cfeba6Schristos 		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
157692cfeba6Schristos 		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
157792cfeba6Schristos 		if (bc->op->o_abandon) {
157892cfeba6Schristos 			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
157992cfeba6Schristos 			asyncmeta_drop_bc( mc, bc);
158092cfeba6Schristos 			if ( bc->op->o_tag == LDAP_REQ_SEARCH ) {
158192cfeba6Schristos 				int j;
158292cfeba6Schristos 				for (j=0; j<ntargets; j++) {
158392cfeba6Schristos 					if (bc->candidates[j].sr_msgid >= 0) {
158492cfeba6Schristos 						a_metasingleconn_t *tmp_msc = &mc->mc_conns[j];
158592cfeba6Schristos 						tmp_msc->msc_active++;
158692cfeba6Schristos 						asyncmeta_back_cancel( mc, bc->op,
158792cfeba6Schristos 								       bc->candidates[ j ].sr_msgid, j );
158892cfeba6Schristos 						tmp_msc->msc_active--;
158992cfeba6Schristos 					}
159092cfeba6Schristos 				}
159192cfeba6Schristos 			}
159292cfeba6Schristos 			asyncmeta_clear_bm_context(bc);
159392cfeba6Schristos 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
159492cfeba6Schristos 			if (msg)
159592cfeba6Schristos 				ldap_msgfree(msg);
159692cfeba6Schristos 			continue;
159792cfeba6Schristos 		}
159892cfeba6Schristos 
159992cfeba6Schristos 		switch (rc) {
160092cfeba6Schristos 		case LDAP_RES_SEARCH_ENTRY:
160192cfeba6Schristos 		case LDAP_RES_SEARCH_REFERENCE:
160292cfeba6Schristos 		case LDAP_RES_SEARCH_RESULT:
160392cfeba6Schristos 		case LDAP_RES_INTERMEDIATE:
160492cfeba6Schristos 			asyncmeta_handle_search_msg(msg, mc, bc, i);
160592cfeba6Schristos 			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
160692cfeba6Schristos 			msg = NULL;
160792cfeba6Schristos 			break;
160892cfeba6Schristos 		case LDAP_RES_ADD:
160992cfeba6Schristos 		case LDAP_RES_DELETE:
161092cfeba6Schristos 		case LDAP_RES_MODDN:
161192cfeba6Schristos 		case LDAP_RES_COMPARE:
161292cfeba6Schristos 		case LDAP_RES_MODIFY:
161392cfeba6Schristos 			rc = asyncmeta_handle_common_result(msg, mc, bc, i);
161492cfeba6Schristos 			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
161592cfeba6Schristos 			break;
161692cfeba6Schristos 		default:
161792cfeba6Schristos 			{
161892cfeba6Schristos 			Debug( asyncmeta_debug,
161992cfeba6Schristos 				   "asyncmeta_op_handle_result: "
162092cfeba6Schristos 				   "unrecognized response message tag=%d\n",
162192cfeba6Schristos 				   rc );
162292cfeba6Schristos 
162392cfeba6Schristos 			}
162492cfeba6Schristos 		}
162592cfeba6Schristos 		if (msg)
162692cfeba6Schristos 			ldap_msgfree(msg);
162792cfeba6Schristos 	}
162892cfeba6Schristos 
162992cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
163092cfeba6Schristos 	rc = --mc->mc_active;
163192cfeba6Schristos 	if (rc) {
163292cfeba6Schristos 		i++;
163392cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
163492cfeba6Schristos 		goto again;
163592cfeba6Schristos 	}
163692cfeba6Schristos 	slap_sl_mem_setctx(ctx, oldctx);
163792cfeba6Schristos 	if (mc->mc_conns) {
163892cfeba6Schristos 		for (i=0; i<ntargets; i++) {
163992cfeba6Schristos 			if (!slapd_shutdown && !META_BACK_CONN_INVALID(msc)
164092cfeba6Schristos 			    && mc->mc_conns[i].msc_ldr && mc->mc_conns[i].conn) {
164192cfeba6Schristos 				connection_client_enable(mc->mc_conns[i].conn);
164292cfeba6Schristos 			}
164392cfeba6Schristos 		}
164492cfeba6Schristos 	}
164592cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
164692cfeba6Schristos 	return NULL;
164792cfeba6Schristos }
164892cfeba6Schristos 
asyncmeta_set_msc_time(a_metasingleconn_t * msc)164992cfeba6Schristos void asyncmeta_set_msc_time(a_metasingleconn_t *msc)
165092cfeba6Schristos {
165192cfeba6Schristos 	msc->msc_time = slap_get_time();
165292cfeba6Schristos }
165392cfeba6Schristos 
asyncmeta_timeout_loop(void * ctx,void * arg)165492cfeba6Schristos void* asyncmeta_timeout_loop(void *ctx, void *arg)
165592cfeba6Schristos {
165692cfeba6Schristos 	struct re_s* rtask = arg;
165792cfeba6Schristos 	a_metainfo_t *mi = rtask->arg;
165892cfeba6Schristos 	bm_context_t *bc, *onext;
165992cfeba6Schristos 	time_t current_time = slap_get_time();
166092cfeba6Schristos 	int i, j;
166192cfeba6Schristos 	LDAP_STAILQ_HEAD(BCList, bm_context_t) timeout_list;
166292cfeba6Schristos 	LDAP_STAILQ_INIT( &timeout_list );
166392cfeba6Schristos 
166492cfeba6Schristos 	Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time );
166592cfeba6Schristos 	void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);
166692cfeba6Schristos 	for (i=0; i<mi->mi_num_conns; i++) {
166792cfeba6Schristos 		a_metaconn_t * mc= &mi->mi_conns[i];
166892cfeba6Schristos 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
166992cfeba6Schristos 		for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
167092cfeba6Schristos 			onext = LDAP_STAILQ_NEXT(bc, bc_next);
167192cfeba6Schristos 			if (bc->bc_active > 0) {
167292cfeba6Schristos 				continue;
167392cfeba6Schristos 			}
167492cfeba6Schristos 
167592cfeba6Schristos 			if (bc->op->o_abandon ) {
167692cfeba6Schristos 					/* set our memctx */
167792cfeba6Schristos 				bc->op->o_threadctx = ctx;
167892cfeba6Schristos 				bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
167992cfeba6Schristos 				slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
168092cfeba6Schristos 				Operation *op = bc->op;
168192cfeba6Schristos 
168292cfeba6Schristos 				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
168392cfeba6Schristos 				mc->pending_ops--;
168492cfeba6Schristos 				for (j=0; j<mi->mi_ntargets; j++) {
168592cfeba6Schristos 					if (bc->candidates[j].sr_msgid >= 0) {
168692cfeba6Schristos 						a_metasingleconn_t *msc = &mc->mc_conns[j];
168792cfeba6Schristos 						if ( op->o_tag == LDAP_REQ_SEARCH ) {
168892cfeba6Schristos 							msc->msc_active++;
168992cfeba6Schristos 							asyncmeta_back_cancel( mc, op,
169092cfeba6Schristos 									       bc->candidates[ j ].sr_msgid, j );
169192cfeba6Schristos 							msc->msc_active--;
169292cfeba6Schristos 						}
169392cfeba6Schristos 					}
169492cfeba6Schristos 				}
169592cfeba6Schristos 				asyncmeta_clear_bm_context(bc);
169692cfeba6Schristos 				continue;
169792cfeba6Schristos 			}
169892cfeba6Schristos 			if (bc->bc_invalid) {
169992cfeba6Schristos 				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
170092cfeba6Schristos 				mc->pending_ops--;
170192cfeba6Schristos 				LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
170292cfeba6Schristos 				continue;
170392cfeba6Schristos 			}
170492cfeba6Schristos 
170592cfeba6Schristos 			if (bc->timeout && bc->stoptime < current_time) {
170692cfeba6Schristos 				Operation *op = bc->op;
170792cfeba6Schristos 				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
170892cfeba6Schristos 				mc->pending_ops--;
170992cfeba6Schristos 				LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
171092cfeba6Schristos 				for (j=0; j<mi->mi_ntargets; j++) {
171192cfeba6Schristos 					if (bc->candidates[j].sr_msgid >= 0) {
171292cfeba6Schristos 						a_metasingleconn_t *msc = &mc->mc_conns[j];
171392cfeba6Schristos 						asyncmeta_set_msc_time(msc);
171492cfeba6Schristos 						if ( op->o_tag == LDAP_REQ_SEARCH ) {
171592cfeba6Schristos 							msc->msc_active++;
171692cfeba6Schristos 							asyncmeta_back_cancel( mc, op,
171792cfeba6Schristos 									       bc->candidates[ j ].sr_msgid, j );
171892cfeba6Schristos 							msc->msc_active--;
171992cfeba6Schristos 						}
172092cfeba6Schristos 					}
172192cfeba6Schristos 				}
172292cfeba6Schristos 			}
172392cfeba6Schristos 		}
172492cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
172592cfeba6Schristos 
172692cfeba6Schristos 		for (bc = LDAP_STAILQ_FIRST(&timeout_list); bc; bc = onext) {
172792cfeba6Schristos 			Operation *op = bc->op;
172892cfeba6Schristos 			SlapReply *rs = &bc->rs;
172992cfeba6Schristos 			int		timeout_err;
173092cfeba6Schristos 			const char *timeout_text;
173192cfeba6Schristos 
173292cfeba6Schristos 			onext = LDAP_STAILQ_NEXT(bc, bc_next);
173392cfeba6Schristos 			LDAP_STAILQ_REMOVE(&timeout_list, bc, bm_context_t, bc_next);
173492cfeba6Schristos 			/* set our memctx */
173592cfeba6Schristos 			bc->op->o_threadctx = ctx;
173692cfeba6Schristos 			bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
173792cfeba6Schristos 			slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
173892cfeba6Schristos 
173992cfeba6Schristos 			if (bc->searchtime) {
174092cfeba6Schristos 				timeout_err = LDAP_TIMELIMIT_EXCEEDED;
174192cfeba6Schristos 			} else {
174292cfeba6Schristos 				timeout_err = op->o_protocol >= LDAP_VERSION3 ?
174392cfeba6Schristos 					LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
174492cfeba6Schristos 			}
174592cfeba6Schristos 
174692cfeba6Schristos 			if ( bc->bc_invalid ) {
174792cfeba6Schristos 				timeout_text = "Operation is invalid - target connection has been reset";
174892cfeba6Schristos 			} else {
174992cfeba6Schristos 				a_metasingleconn_t *log_msc =  &mc->mc_conns[0];
175092cfeba6Schristos 				Debug( asyncmeta_debug,
175192cfeba6Schristos 				       "asyncmeta_timeout_loop:Timeout op %s loop[%p], "
175292cfeba6Schristos 				       "current_time:%ld, op->o_time:%ld msc: %p, "
175392cfeba6Schristos 				       "msc->msc_binding_time: %x, msc->msc_flags:%x \n",
175492cfeba6Schristos 				       bc->op->o_log_prefix, rtask, current_time, bc->op->o_time,
175592cfeba6Schristos 				       log_msc, (unsigned int)log_msc->msc_binding_time, log_msc->msc_mscflags );
175692cfeba6Schristos 
175792cfeba6Schristos 				if (bc->searchtime) {
175892cfeba6Schristos 					timeout_text = NULL;
175992cfeba6Schristos 				} else {
176092cfeba6Schristos 					timeout_text = "Operation timed out";
176192cfeba6Schristos 				}
176292cfeba6Schristos 
176392cfeba6Schristos 				for (j=0; j<mi->mi_ntargets; j++) {
176492cfeba6Schristos 					if (bc->candidates[j].sr_msgid >= 0) {
176592cfeba6Schristos 						a_metatarget_t     *mt = mi->mi_targets[j];
176692cfeba6Schristos 						if (!META_BACK_TGT_QUARANTINE( mt ) ||
176792cfeba6Schristos 						    bc->candidates[j].sr_type == REP_RESULT) {
176892cfeba6Schristos 							continue;
176992cfeba6Schristos 						}
177092cfeba6Schristos 
177192cfeba6Schristos 						if (mt->mt_isquarantined > LDAP_BACK_FQ_NO) {
177292cfeba6Schristos 							timeout_err = LDAP_UNAVAILABLE;
177392cfeba6Schristos 						} else {
177492cfeba6Schristos 							mt->mt_timeout_ops++;
177592cfeba6Schristos 							if ((mi->mi_max_timeout_ops > 0) &&
177692cfeba6Schristos 							    (mt->mt_timeout_ops > mi->mi_max_timeout_ops)) {
177792cfeba6Schristos 								timeout_err = LDAP_UNAVAILABLE;
177892cfeba6Schristos 								rs->sr_err = timeout_err;
177992cfeba6Schristos 								if (mt->mt_isquarantined == LDAP_BACK_FQ_NO)
178092cfeba6Schristos 									asyncmeta_quarantine(op, mi, rs, j);
178192cfeba6Schristos 							}
178292cfeba6Schristos 						}
178392cfeba6Schristos 					}
178492cfeba6Schristos 				}
178592cfeba6Schristos 			}
178692cfeba6Schristos 			rs->sr_err = timeout_err;
178792cfeba6Schristos 			rs->sr_text = timeout_text;
178892cfeba6Schristos 			if (!bc->op->o_abandon ) {
178992cfeba6Schristos 				asyncmeta_send_ldap_result( bc, bc->op, &bc->rs );
179092cfeba6Schristos 			}
179192cfeba6Schristos 			asyncmeta_clear_bm_context(bc);
179292cfeba6Schristos 		}
179392cfeba6Schristos 
179492cfeba6Schristos 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
179592cfeba6Schristos 		if (mi->mi_idle_timeout) {
179692cfeba6Schristos 			for (j=0; j<mi->mi_ntargets; j++) {
179792cfeba6Schristos 				a_metasingleconn_t *msc = &mc->mc_conns[j];
179892cfeba6Schristos 				if ( msc->msc_active > 0 ) {
179992cfeba6Schristos 					continue;
180092cfeba6Schristos 				}
180192cfeba6Schristos 				if (mc->pending_ops > 0) {
180292cfeba6Schristos 					continue;
180392cfeba6Schristos 				}
180492cfeba6Schristos 				current_time = slap_get_time();
180592cfeba6Schristos 				if (msc->msc_ld && msc->msc_time > 0 && msc->msc_time + mi->mi_idle_timeout < current_time) {
180692cfeba6Schristos 					asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
180792cfeba6Schristos 				}
180892cfeba6Schristos 			}
180992cfeba6Schristos 		}
181092cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
181192cfeba6Schristos 	}
181292cfeba6Schristos 
181392cfeba6Schristos 	slap_sl_mem_setctx(ctx, oldctx);
181492cfeba6Schristos 	current_time = slap_get_time();
181592cfeba6Schristos 	Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] stop at [%ld] \n", rtask, current_time );
181692cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
181792cfeba6Schristos 	if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
181892cfeba6Schristos 		ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
181992cfeba6Schristos 	}
182092cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
182192cfeba6Schristos 	return NULL;
182292cfeba6Schristos }
182392cfeba6Schristos 
1824