xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-asyncmeta/meta_result.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: meta_result.c,v 1.2 2021/08/14 16:14:59 christos Exp $	*/
2 
3 /* meta_result.c - target responses processing */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2016-2021 The OpenLDAP Foundation.
8  * Portions Copyright 2016 Symas Corporation.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 
20 /* ACKNOWLEDGEMENTS:
21  * This work was developed by Symas Corporation
22  * based on back-meta module for inclusion in OpenLDAP Software.
23  * This work was sponsored by Ericsson. */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: meta_result.c,v 1.2 2021/08/14 16:14:59 christos Exp $");
27 
28 #include "portable.h"
29 
30 #include <stdio.h>
31 
32 #include <ac/string.h>
33 #include <ac/socket.h>
34 
35 #include "slap.h"
36 #include "../back-ldap/back-ldap.h"
37 #include "back-asyncmeta.h"
38 #include "ldap_rq.h"
39 #include "../../../libraries/liblber/lber-int.h"
40 
41 static void
asyncmeta_send_ldap_result(bm_context_t * bc,Operation * op,SlapReply * rs)42 asyncmeta_send_ldap_result(bm_context_t *bc, Operation *op, SlapReply *rs)
43 {
44 	if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !bc->op->o_abandon ) {
45 		send_ldap_result(&bc->copy_op, rs);
46 		bc->op->o_callback = bc->copy_op.o_callback;
47 		bc->op->o_extra = bc->copy_op.o_extra;
48 		bc->op->o_ctrls = bc->copy_op.o_ctrls;
49 	}
50 }
51 
52 static int
asyncmeta_is_last_result(a_metaconn_t * mc,bm_context_t * bc,int candidate)53 asyncmeta_is_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate)
54 {
55 	a_metainfo_t	*mi = mc->mc_info;
56 	int i;
57 	SlapReply *candidates = bc->candidates;
58 	for ( i = 0; i < mi->mi_ntargets; i++ ) {
59 		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
60 			continue;
61 		}
62 		if (candidates[ i ].sr_msgid != META_MSGID_IGNORE ||
63 		    candidates[ i ].sr_type != REP_RESULT) {
64 			return 1;
65 		}
66 	}
67 	return 0;
68 }
69 
70 meta_search_candidate_t
asyncmeta_dobind_result(a_metaconn_t * mc,int candidate,SlapReply * bind_result,LDAPMessage * res)71 asyncmeta_dobind_result(
72 	a_metaconn_t		*mc,
73 	int			candidate,
74 	SlapReply		*bind_result,
75 	LDAPMessage		*res )
76 {
77 	a_metainfo_t		*mi = mc->mc_info;
78 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
79 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
80 
81 	meta_search_candidate_t	retcode = META_SEARCH_NOT_CANDIDATE;
82 	int			rc;
83 
84 	assert( msc->msc_ldr != NULL );
85 
86 	if ( mi->mi_idle_timeout != 0 ) {
87 		asyncmeta_set_msc_time(msc);
88 	}
89 
90 	if ( LogTest( asyncmeta_debug ) ) {
91 		char	time_buf[ SLAP_TEXT_BUFLEN ];
92 		asyncmeta_get_timestamp(time_buf);
93 		Debug( asyncmeta_debug, "[%x] [%s] asyncmeta_dobind_result msc: %p, "
94 		       "msc->msc_binding_time: %x, msc->msc_flags:%x\n ",
95 		       (unsigned int)slap_get_time(), time_buf, msc,
96 		       (unsigned int)msc->msc_binding_time, msc->msc_mscflags );
97 	}
98 	/* FIXME: matched? referrals? response controls? */
99 	rc = ldap_parse_result( msc->msc_ldr, res,
100 				&(bind_result->sr_err),
101 				(char **)&(bind_result->sr_matched),
102 				(char **)&(bind_result->sr_text),
103 				NULL, NULL, 0 );
104 
105 	if ( LogTest( asyncmeta_debug ) ) {
106 		char	time_buf[ SLAP_TEXT_BUFLEN ];
107 		asyncmeta_get_timestamp(time_buf);
108 		Debug( asyncmeta_debug,
109 		       "[%s] asyncmeta_dobind_result error=%d msc: %p\n",
110 		       time_buf,bind_result->sr_err, msc );
111 	}
112 
113 	if ( rc != LDAP_SUCCESS ) {
114 		bind_result->sr_err = rc;
115 	}
116 	rc = slap_map_api2result( bind_result );
117 
118 	LDAP_BACK_CONN_BINDING_CLEAR( msc );
119 	if ( rc != LDAP_SUCCESS ) {
120 		bind_result->sr_err = rc;
121 	} else {
122 		/* FIXME: check if bound as idassert authcDN! */
123 		if ( BER_BVISNULL( &msc->msc_bound_ndn )
124 			|| BER_BVISEMPTY( &msc->msc_bound_ndn ) )
125 		{
126 			LDAP_BACK_CONN_ISANON_SET( msc );
127 			if ( LogTest( asyncmeta_debug ) ) {
128 				char	time_buf[ SLAP_TEXT_BUFLEN ];
129 				asyncmeta_get_timestamp(time_buf);
130 				Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result anonymous msc: %p\n",
131 				      time_buf, msc );
132 			}
133 
134 		} else {
135 			if ( META_BACK_TGT_SAVECRED( mt ) &&
136 				!BER_BVISNULL( &msc->msc_cred ) &&
137 				!BER_BVISEMPTY( &msc->msc_cred ) )
138 			{
139 				ldap_set_rebind_proc( msc->msc_ldr, mt->mt_rebind_f, msc );
140 			}
141 			if ( LogTest( asyncmeta_debug ) ) {
142 				char	time_buf[ SLAP_TEXT_BUFLEN ];
143 				asyncmeta_get_timestamp(time_buf);
144 				Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result success msc: %p\n",
145 				      time_buf, msc );
146 			}
147 			LDAP_BACK_CONN_ISBOUND_SET( msc );
148 		}
149 		retcode = META_SEARCH_CANDIDATE;
150 	}
151 	return retcode;
152 }
153 
154 static int
asyncmeta_send_entry(Operation * op,SlapReply * rs,a_metaconn_t * mc,int target,LDAPMessage * e)155 asyncmeta_send_entry(
156 	Operation 	*op,
157 	SlapReply	*rs,
158 	a_metaconn_t	*mc,
159 	int 		target,
160 	LDAPMessage 	*e )
161 {
162 	a_metainfo_t 		*mi = mc->mc_info;
163 	struct berval		a, mapped = BER_BVNULL;
164 	int			check_sorted_attrs = 0;
165 	Entry 			ent = {0};
166 	BerElement 		ber = *ldap_get_message_ber( e );
167 	Attribute 		*attr, **attrp;
168 	struct berval 		bdn,
169 				dn = BER_BVNULL;
170 	const char 		*text;
171 	a_dncookie		dc;
172 	ber_len_t		len;
173 	int			rc;
174 	void	*mem_mark;
175 
176 	mem_mark = slap_sl_mark( op->o_tmpmemctx );
177 	ber_set_option( &ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
178 
179 	if ( ber_scanf( &ber, "l{", &len ) == LBER_ERROR ) {
180 		return LDAP_DECODING_ERROR;
181 	}
182 
183 	if ( ber_set_option( &ber, LBER_OPT_REMAINING_BYTES, &len ) != LBER_OPT_SUCCESS ) {
184 		return LDAP_OTHER;
185 	}
186 
187 	if ( ber_scanf( &ber, "m{", &bdn ) == LBER_ERROR ) {
188 		return LDAP_DECODING_ERROR;
189 	}
190 
191 	/*
192 	 * Rewrite the dn of the result, if needed
193 	 */
194 	dc.op = op;
195 	dc.target = mi->mi_targets[ target ];
196 	dc.memctx = op->o_tmpmemctx;
197 	dc.to_from = MASSAGE_REP;
198 	asyncmeta_dn_massage( &dc, &bdn, &dn );
199 
200 	/*
201 	 * Note: this may fail if the target host(s) schema differs
202 	 * from the one known to the meta, and a DN with unknown
203 	 * attributes is returned.
204 	 *
205 	 * FIXME: should we log anything, or delegate to dnNormalize?
206 	 */
207 	rc = dnPrettyNormal( NULL, &dn, &ent.e_name, &ent.e_nname,
208 		op->o_tmpmemctx );
209 	if ( dn.bv_val != bdn.bv_val ) {
210 			op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
211 	}
212 	BER_BVZERO( &dn );
213 
214 	if ( rc != LDAP_SUCCESS ) {
215 		Debug( LDAP_DEBUG_ANY,
216 			"%s asyncmeta_send_entry(\"%s\"): "
217 			"invalid DN syntax\n",
218 			op->o_log_prefix, ent.e_name.bv_val );
219 		rc = LDAP_INVALID_DN_SYNTAX;
220 		goto done;
221 	}
222 
223 	/*
224 	 * cache dn
225 	 */
226 	if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
227 		( void )asyncmeta_dncache_update_entry( &mi->mi_cache,
228 				&ent.e_nname, target );
229 	}
230 
231 	attrp = &ent.e_attrs;
232 
233 	while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
234 		int				last = 0;
235 		slap_syntax_validate_func	*validate;
236 		slap_syntax_transform_func	*pretty;
237 
238 		if ( ber_pvt_ber_remaining( &ber ) < 0 ) {
239 			Debug( LDAP_DEBUG_ANY,
240 				"%s asyncmeta_send_entry(\"%s\"): "
241 				"unable to parse attr \"%s\".\n",
242 				op->o_log_prefix, ent.e_name.bv_val, a.bv_val );
243 
244 			rc = LDAP_OTHER;
245 			goto done;
246 		}
247 
248 		if ( ber_pvt_ber_remaining( &ber ) == 0 ) {
249 			break;
250 		}
251 
252 		attr = op->o_tmpcalloc( 1, sizeof(Attribute), op->o_tmpmemctx );
253 		if ( slap_bv2ad( &a, &attr->a_desc, &text )
254 				!= LDAP_SUCCESS) {
255 			if ( slap_bv2undef_ad( &a, &attr->a_desc, &text,
256 				SLAP_AD_PROXIED ) != LDAP_SUCCESS )
257 			{
258 				Debug(LDAP_DEBUG_ANY,
259 				      "%s meta_send_entry(\"%s\"): " "slap_bv2undef_ad(%s): %s\n",
260 				      op->o_log_prefix, ent.e_name.bv_val,
261 				      mapped.bv_val, text );
262 				( void )ber_scanf( &ber, "x" /* [W] */ );
263 				op->o_tmpfree( attr, op->o_tmpmemctx );
264 				continue;
265 			}
266 		}
267 
268 		if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL )
269 			check_sorted_attrs = 1;
270 
271 		/* no subschemaSubentry */
272 		if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
273 			|| attr->a_desc == slap_schema.si_ad_entryDN )
274 		{
275 
276 			/*
277 			 * We eat target's subschemaSubentry because
278 			 * a search for this value is likely not
279 			 * to resolve to the appropriate backend;
280 			 * later, the local subschemaSubentry is
281 			 * added.
282 			 *
283 			 * We also eat entryDN because the frontend
284 			 * will reattach it without checking if already
285 			 * present...
286 			 */
287 			( void )ber_scanf( &ber, "x" /* [W] */ );
288 			op->o_tmpfree( attr, op->o_tmpmemctx );
289 			continue;
290 		}
291 
292 		if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
293 				|| attr->a_vals == NULL )
294 		{
295 			attr->a_vals = (struct berval *)&slap_dummy_bv;
296 
297 		} else {
298 			for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last )
299 				;
300 		}
301 		attr->a_numvals = last;
302 
303 		validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
304 		pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
305 
306 		if ( !validate && !pretty ) {
307 			ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
308 			op->o_tmpfree( attr, op->o_tmpmemctx );
309 			goto next_attr;
310 		}
311 
312 		/*
313 		 * It is necessary to try to rewrite attributes with
314 		 * dn syntax because they might be used in ACLs as
315 		 * members of groups; since ACLs are applied to the
316 		 * rewritten stuff, no dn-based subecj clause could
317 		 * be used at the ldap backend side (see
318 		 * http://www.OpenLDAP.org/faq/data/cache/452.html)
319 		 * The problem can be overcome by moving the dn-based
320 		 * ACLs to the target directory server, and letting
321 		 * everything pass thru the ldap backend.
322 		 */
323 		{
324 			int	i;
325 
326 			if ( attr->a_desc->ad_type->sat_syntax ==
327 				slap_schema.si_syn_distinguishedName )
328 			{
329 				asyncmeta_dnattr_result_rewrite( &dc, attr->a_vals );
330 
331 			} else if ( attr->a_desc == slap_schema.si_ad_ref ) {
332 				asyncmeta_referral_result_rewrite( &dc, attr->a_vals );
333 
334 			}
335 
336 			for ( i = 0; i < last; i++ ) {
337 				struct berval	pval;
338 				int		rc;
339 
340 				if ( pretty ) {
341 					rc = ordered_value_pretty( attr->a_desc,
342 						&attr->a_vals[i], &pval, op->o_tmpmemctx );
343 
344 				} else {
345 					rc = ordered_value_validate( attr->a_desc,
346 						&attr->a_vals[i], 0 );
347 				}
348 
349 				if ( rc ) {
350 					ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
351 					if ( --last == i ) {
352 						BER_BVZERO( &attr->a_vals[ i ] );
353 						break;
354 					}
355 					attr->a_vals[i] = attr->a_vals[last];
356 					BER_BVZERO( &attr->a_vals[last] );
357 					i--;
358 					continue;
359 				}
360 
361 				if ( pretty ) {
362 					ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
363 					attr->a_vals[i] = pval;
364 				}
365 			}
366 
367 			if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
368 				ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
369 				op->o_tmpfree( attr, op->o_tmpmemctx );
370 				goto next_attr;
371 			}
372 		}
373 
374 		if ( last && attr->a_desc->ad_type->sat_equality &&
375 			attr->a_desc->ad_type->sat_equality->smr_normalize )
376 		{
377 			int i;
378 
379 			attr->a_nvals = op->o_tmpalloc( ( last + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
380 			for ( i = 0; i<last; i++ ) {
381 				/* if normalizer fails, drop this value */
382 				if ( ordered_value_normalize(
383 					SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
384 					attr->a_desc,
385 					attr->a_desc->ad_type->sat_equality,
386 					&attr->a_vals[i], &attr->a_nvals[i],
387 					op->o_tmpmemctx )) {
388 					ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
389 					if ( --last == i ) {
390 						BER_BVZERO( &attr->a_vals[ i ] );
391 						break;
392 					}
393 					attr->a_vals[i] = attr->a_vals[last];
394 					BER_BVZERO( &attr->a_vals[last] );
395 					i--;
396 				}
397 			}
398 			BER_BVZERO( &attr->a_nvals[i] );
399 			if ( last == 0 ) {
400 				ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
401 				ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx );
402 				op->o_tmpfree( attr, op->o_tmpmemctx );
403 				goto next_attr;
404 			}
405 
406 		} else {
407 			attr->a_nvals = attr->a_vals;
408 		}
409 
410 		attr->a_numvals = last;
411 		*attrp = attr;
412 		attrp = &attr->a_next;
413 next_attr:;
414 	}
415 
416 	/* Check for sorted attributes */
417 	if ( check_sorted_attrs ) {
418 		for ( attr = ent.e_attrs; attr; attr = attr->a_next ) {
419 			if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
420 				while ( attr->a_numvals > 1 ) {
421 					int i;
422 					int rc = slap_sort_vals( (Modifications *)attr, &text, &i, op->o_tmpmemctx );
423 					if ( rc != LDAP_TYPE_OR_VALUE_EXISTS )
424 						break;
425 
426 					/* Strip duplicate values */
427 					if ( attr->a_nvals != attr->a_vals )
428 						ber_memfree_x( attr->a_nvals[i].bv_val, op->o_tmpmemctx );
429 					ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
430 					attr->a_numvals--;
431 					if ( (unsigned)i < attr->a_numvals ) {
432 						attr->a_vals[i] = attr->a_vals[attr->a_numvals];
433 						if ( attr->a_nvals != attr->a_vals )
434 							attr->a_nvals[i] = attr->a_nvals[attr->a_numvals];
435 					}
436 					BER_BVZERO(&attr->a_vals[attr->a_numvals]);
437 					if ( attr->a_nvals != attr->a_vals )
438 						BER_BVZERO(&attr->a_nvals[attr->a_numvals]);
439 				}
440 				attr->a_flags |= SLAP_ATTR_SORTED_VALS;
441 			}
442 		}
443 	}
444 	Debug( LDAP_DEBUG_TRACE,
445 	       "%s asyncmeta_send_entry(\"%s\"): "
446 	       ".\n",
447 	       op->o_log_prefix, ent.e_name.bv_val );
448 	ldap_get_entry_controls( mc->mc_conns[target].msc_ldr,
449 		e, &rs->sr_ctrls );
450 	rs->sr_entry = &ent;
451 	rs->sr_attrs = op->ors_attrs;
452 	rs->sr_operational_attrs = NULL;
453 	rs->sr_flags = mi->mi_targets[ target ]->mt_rep_flags;
454 	rs->sr_err = LDAP_SUCCESS;
455 	rc = send_search_entry( op, rs );
456 	switch ( rc ) {
457 	case LDAP_UNAVAILABLE:
458 		rc = LDAP_OTHER;
459 		break;
460 	}
461 
462 done:;
463 	if ( rs->sr_ctrls != NULL ) {
464 		ldap_controls_free( rs->sr_ctrls );
465 		rs->sr_ctrls = NULL;
466 	}
467 #if 0
468 	while ( ent.e_attrs ) {
469 		attr = ent.e_attrs;
470 		ent.e_attrs = attr->a_next;
471 		if ( attr->a_nvals != attr->a_vals )
472 			ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx );
473 		ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
474 		op->o_tmpfree( attr, op->o_tmpmemctx );
475 	}
476 	if (ent.e_name.bv_val != NULL) {
477 		op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx );
478 	}
479 
480 	if (ent.e_nname.bv_val != NULL) {
481 		op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx );
482 	}
483 	if (rs->sr_entry && rs->sr_entry != &ent) {
484 		entry_free( rs->sr_entry );
485 	}
486 #endif
487 	slap_sl_release( mem_mark, op->o_tmpmemctx );
488 	rs->sr_entry = NULL;
489 	rs->sr_attrs = NULL;
490 	return rc;
491 }
492 
493 static void
asyncmeta_search_last_result(a_metaconn_t * mc,bm_context_t * bc,int candidate,int sres)494 asyncmeta_search_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate, int sres)
495 {
496 	a_metainfo_t	*mi = mc->mc_info;
497 	Operation *op = bc->op;
498 	SlapReply *rs = &bc->rs;
499 	int	   i;
500 	SlapReply *candidates = bc->candidates;
501 	char *matched = NULL;
502 
503 	if ( bc->candidate_match > 0 ) {
504 		struct berval	pmatched = BER_BVNULL;
505 
506 		/* we use the first one */
507 		for ( i = 0; i < mi->mi_ntargets; i++ ) {
508 			if ( META_IS_CANDIDATE( &candidates[ i ] )
509 					&& candidates[ i ].sr_matched != NULL )
510 			{
511 				struct berval	bv, pbv;
512 				int		rc;
513 
514 				/* if we got success, and this target
515 				 * returned noSuchObject, and its suffix
516 				 * is a superior of the searchBase,
517 				 * ignore the matchedDN */
518 				if ( sres == LDAP_SUCCESS
519 					&& candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT
520 					&& op->o_req_ndn.bv_len > mi->mi_targets[ i ]->mt_nsuffix.bv_len )
521 				{
522 					free( (char *)candidates[ i ].sr_matched );
523 					candidates[ i ].sr_matched = NULL;
524 					continue;
525 				}
526 
527 				ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
528 				rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );
529 
530 				if ( rc == LDAP_SUCCESS ) {
531 
532 					/* NOTE: if they all are superiors
533 					 * of the baseDN, the shorter is also
534 					 * superior of the longer... */
535 					if ( pbv.bv_len > pmatched.bv_len ) {
536 						if ( !BER_BVISNULL( &pmatched ) ) {
537 							op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
538 						}
539 						pmatched = pbv;
540 
541 					} else {
542 						op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
543 					}
544 				}
545 
546 				if ( candidates[ i ].sr_matched != NULL ) {
547 					free( (char *)candidates[ i ].sr_matched );
548 					candidates[ i ].sr_matched = NULL;
549 				}
550 			}
551 		}
552 
553 		if ( !BER_BVISNULL( &pmatched ) ) {
554 			matched = pmatched.bv_val;
555 		}
556 
557 	} else if ( sres == LDAP_NO_SUCH_OBJECT ) {
558 		matched = mi->mi_suffix.bv_val;
559 	}
560 
561 	/*
562 	 * In case we returned at least one entry, we return LDAP_SUCCESS
563 	 * otherwise, the latter error code we got
564 	 */
565 
566 	if ( sres == LDAP_SUCCESS ) {
567 		if ( rs->sr_v2ref ) {
568 			sres = LDAP_REFERRAL;
569 		}
570 
571 		if ( META_BACK_ONERR_REPORT( mi ) ) {
572 			/*
573 			 * Report errors, if any
574 			 *
575 			 * FIXME: we should handle error codes and return the more
576 			 * important/reasonable
577 			 */
578 			for ( i = 0; i < mi->mi_ntargets; i++ ) {
579 				if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
580 					continue;
581 				}
582 
583 				if ( candidates[ i ].sr_err != LDAP_SUCCESS
584 					&& candidates[ i ].sr_err != LDAP_NO_SUCH_OBJECT )
585 				{
586 					sres = candidates[ i ].sr_err;
587 					break;
588 				}
589 			}
590 		}
591 	}
592 	Debug( LDAP_DEBUG_TRACE,
593 	       "%s asyncmeta_search_last_result(\"%d\"): "
594 	       ".\n",
595 	       op->o_log_prefix, candidate );
596 	rs->sr_err = sres;
597 	rs->sr_matched = ( sres == LDAP_SUCCESS ? NULL : matched );
598 	rs->sr_text =  ( sres == LDAP_SUCCESS ? NULL : candidates[candidate].sr_text );
599 	rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
600 	asyncmeta_send_ldap_result(bc, op, rs);
601 	rs->sr_text = NULL;
602 	rs->sr_matched = NULL;
603 	rs->sr_ref = NULL;
604 }
605 
606 static meta_search_candidate_t
asyncmeta_send_pending_op(bm_context_t * bc,int candidate)607 asyncmeta_send_pending_op(bm_context_t *bc, int candidate)
608 {
609 	meta_search_candidate_t	retcode;
610 	switch (bc->op->o_tag) {
611 		case LDAP_REQ_SEARCH:
612 			retcode = asyncmeta_back_search_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate,  NULL, 0 , 0);
613 			break;
614 		case LDAP_REQ_ADD:
615 			retcode = asyncmeta_back_add_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
616 			break;
617 		case LDAP_REQ_MODIFY:
618 			retcode = asyncmeta_back_modify_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
619 			break;
620 		case LDAP_REQ_MODRDN:
621 			retcode = asyncmeta_back_modrdn_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
622 			break;
623 		case LDAP_REQ_COMPARE:
624 			retcode = asyncmeta_back_compare_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
625 			break;
626 		case LDAP_REQ_DELETE:
627 			retcode = asyncmeta_back_delete_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
628 			break;
629 		default:
630 			retcode = META_SEARCH_NOT_CANDIDATE;
631 		}
632 	return retcode;
633 }
634 
635 
636 meta_search_candidate_t
asyncmeta_send_all_pending_ops(a_metaconn_t * mc,int candidate,void * ctx,int dolock)637 asyncmeta_send_all_pending_ops(a_metaconn_t *mc, int candidate, void *ctx, int dolock)
638 {
639 	a_metainfo_t	*mi = mc->mc_info;
640 	bm_context_t *bc, *onext;
641 	a_metasingleconn_t *msc = &mc->mc_conns[candidate];
642 
643 	if ( dolock )
644 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
645 
646 	msc->msc_active++;
647 	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
648 		meta_search_candidate_t ret;
649 		onext = LDAP_STAILQ_NEXT(bc, bc_next);
650 		if (bc->candidates[candidate].sr_msgid == META_MSGID_NEED_BIND)
651 			bc->candidates[candidate].sr_msgid = META_MSGID_GOT_BIND;
652 		if (bc->candidates[candidate].sr_msgid != META_MSGID_GOT_BIND || bc->bc_active > 0 || bc->op->o_abandon > 0) {
653 			continue;
654 		}
655 		bc->op->o_threadctx = ctx;
656 		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
657 		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
658 		bc->bc_active++;
659 		ret = asyncmeta_send_pending_op(bc, candidate);
660 		if (ret != META_SEARCH_CANDIDATE) {
661 			bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
662 			bc->candidates[ candidate ].sr_type = REP_RESULT;
663 			bc->candidates[ candidate ].sr_err = bc->rs.sr_err;
664 			if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
665 			    (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
666 				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
667 				mc->pending_ops--;
668 				asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
669 				asyncmeta_clear_bm_context(bc);
670 			}
671 		} else {
672 			bc->bc_active--;
673 		}
674 	}
675 	msc->msc_active--;
676 
677 	if ( dolock )
678 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
679 
680 	return META_SEARCH_CANDIDATE;
681 }
682 
683 meta_search_candidate_t
asyncmeta_return_bind_errors(a_metaconn_t * mc,int candidate,SlapReply * bind_result,void * ctx,int dolock)684 asyncmeta_return_bind_errors(a_metaconn_t *mc, int candidate, SlapReply *bind_result, void *ctx, int dolock)
685 {
686 	a_metainfo_t	*mi = mc->mc_info;
687 	bm_context_t *bc, *onext;
688 
689 	if ( dolock )
690 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
691 
692 	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
693 		onext = LDAP_STAILQ_NEXT(bc, bc_next);
694 		if (bc->candidates[candidate].sr_msgid != META_MSGID_NEED_BIND
695 		    || bc->bc_active > 0 || bc->op->o_abandon > 0) {
696 			continue;
697 		}
698 		bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
699 		bc->candidates[ candidate ].sr_type = REP_RESULT;
700 		bc->candidates[ candidate ].sr_err = bind_result->sr_err;
701 		if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
702 		    (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
703 			LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
704 			bc->op->o_threadctx = ctx;
705 			bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
706 			slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
707 			bc->rs.sr_err = bind_result->sr_err;
708 			bc->rs.sr_text = bind_result->sr_text;
709 			mc->pending_ops--;
710 			asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
711 			asyncmeta_clear_bm_context(bc);
712 		}
713 	}
714 
715 	if ( dolock )
716 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
717 
718 	return META_SEARCH_CANDIDATE;
719 }
720 
721 static meta_search_candidate_t
asyncmeta_handle_bind_result(LDAPMessage * msg,a_metaconn_t * mc,int candidate,void * ctx)722 asyncmeta_handle_bind_result(LDAPMessage *msg, a_metaconn_t *mc, int candidate, void *ctx)
723 {
724 	meta_search_candidate_t	retcode;
725 	SlapReply bind_result = {0};
726 	/* could modify the msc, safer to lock it */
727 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
728 	retcode = asyncmeta_dobind_result( mc, candidate, &bind_result, msg );
729 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
730 	if ( retcode == META_SEARCH_CANDIDATE ) {
731 		/* send the remaining pending ops */
732 		asyncmeta_send_all_pending_ops(mc, candidate, ctx, 1);
733 	} else {
734 		asyncmeta_return_bind_errors(mc, candidate, &bind_result, ctx, 1);
735 	}
736 	return retcode;
737 }
738 
739 int
asyncmeta_handle_search_msg(LDAPMessage * res,a_metaconn_t * mc,bm_context_t * bc,int candidate)740 asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc, int candidate)
741 {
742 	a_metainfo_t	*mi;
743 	a_metatarget_t	*mt;
744 	a_metasingleconn_t *msc;
745 	Operation *op = bc->op;
746 	SlapReply *rs;
747 	int	   i, rc = LDAP_SUCCESS, sres;
748 	SlapReply *candidates;
749 	char		**references = NULL;
750 	LDAPControl	**ctrls = NULL;
751 	a_dncookie dc;
752 	LDAPMessage *msg;
753 	ber_int_t id;
754 
755 	rs = &bc->rs;
756 	mi = mc->mc_info;
757 	mt = mi->mi_targets[ candidate ];
758 	msc = &mc->mc_conns[ candidate ];
759 	dc.op = op;
760 	dc.target = mt;
761 	dc.to_from = MASSAGE_REP;
762 	id = ldap_msgid(res);
763 
764 
765 	candidates = bc->candidates;
766 	i = candidate;
767 
768 	while (res && !META_BACK_CONN_INVALID(msc)) {
769 	for (msg = ldap_first_message(msc->msc_ldr, res); msg; msg = ldap_next_message(msc->msc_ldr, msg)) {
770 		switch(ldap_msgtype(msg)) {
771 		case LDAP_RES_SEARCH_ENTRY:
772 			Debug( LDAP_DEBUG_TRACE,
773 				"%s asyncmeta_handle_search_msg: msc %p entry\n",
774 				op->o_log_prefix, msc );
775 			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
776 				/* don't retry any more... */
777 				candidates[ i ].sr_type = REP_RESULT;
778 			}
779 			/* count entries returned by target */
780 			candidates[ i ].sr_nentries++;
781 			if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !op->o_abandon) {
782 				rs->sr_err = asyncmeta_send_entry( &bc->copy_op, rs, mc, i, msg );
783 			} else {
784 				goto err_cleanup;
785 			}
786 			switch ( rs->sr_err ) {
787 			case LDAP_SIZELIMIT_EXCEEDED:
788 				asyncmeta_send_ldap_result(bc, op, rs);
789 				rs->sr_err = LDAP_SUCCESS;
790 				goto err_cleanup;
791 			case LDAP_UNAVAILABLE:
792 				rs->sr_err = LDAP_OTHER;
793 				break;
794 			default:
795 				break;
796 			}
797 			bc->is_ok++;
798 			break;
799 
800 		case LDAP_RES_SEARCH_REFERENCE:
801 			if ( META_BACK_TGT_NOREFS( mt ) ) {
802 				rs->sr_err = LDAP_OTHER;
803 				asyncmeta_send_ldap_result(bc, op, rs);
804 				goto err_cleanup;
805 			}
806 			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
807 				/* don't retry any more... */
808 				candidates[ i ].sr_type = REP_RESULT;
809 			}
810 			bc->is_ok++;
811 			rc = ldap_parse_reference( msc->msc_ldr, msg,
812 				   &references, &rs->sr_ctrls, 0 );
813 
814 			if ( rc != LDAP_SUCCESS || references == NULL ) {
815 				rs->sr_err = LDAP_OTHER;
816 				asyncmeta_send_ldap_result(bc, op, rs);
817 				goto err_cleanup;
818 			}
819 
820 			/* FIXME: merge all and return at the end */
821 
822 			{
823 				int cnt;
824 				for ( cnt = 0; references[ cnt ]; cnt++ )
825 					;
826 
827 				rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( cnt + 1 ),
828 								 op->o_tmpmemctx );
829 
830 				for ( cnt = 0; references[ cnt ]; cnt++ ) {
831 					ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ],
832 							  op->o_tmpmemctx );
833 				}
834 				BER_BVZERO( &rs->sr_ref[ cnt ] );
835 			}
836 
837 			{
838 				dc.memctx = op->o_tmpmemctx;
839 				( void )asyncmeta_referral_result_rewrite( &dc, rs->sr_ref );
840 			}
841 
842 			if ( rs->sr_ref != NULL ) {
843 				if (!BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
844 					/* ignore return value by now */
845 					( void )send_search_reference( op, rs );
846 				}
847 
848 				ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx );
849 				rs->sr_ref = NULL;
850 			}
851 
852 			/* cleanup */
853 			if ( references ) {
854 				ber_memvfree( (void **)references );
855 			}
856 
857 			if ( rs->sr_ctrls ) {
858 				ldap_controls_free( rs->sr_ctrls );
859 				rs->sr_ctrls = NULL;
860 			}
861 			break;
862 
863 		case LDAP_RES_INTERMEDIATE:
864 			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
865 				/* don't retry any more... */
866 				candidates[ i ].sr_type = REP_RESULT;
867 			}
868 			bc->is_ok++;
869 
870 			/* FIXME: response controls
871 			 * are passed without checks */
872 			rs->sr_err = ldap_parse_intermediate( msc->msc_ldr,
873 								  msg,
874 								  (char **)&rs->sr_rspoid,
875 								  &rs->sr_rspdata,
876 								  &rs->sr_ctrls,
877 								  0 );
878 			if ( rs->sr_err != LDAP_SUCCESS ) {
879 				candidates[ i ].sr_type = REP_RESULT;
880 				rs->sr_err = LDAP_OTHER;
881 				asyncmeta_send_ldap_result(bc, op, rs);
882 				goto err_cleanup;
883 			}
884 
885 			slap_send_ldap_intermediate( op, rs );
886 
887 			if ( rs->sr_rspoid != NULL ) {
888 				ber_memfree( (char *)rs->sr_rspoid );
889 				rs->sr_rspoid = NULL;
890 			}
891 
892 			if ( rs->sr_rspdata != NULL ) {
893 				ber_bvfree( rs->sr_rspdata );
894 				rs->sr_rspdata = NULL;
895 			}
896 
897 			if ( rs->sr_ctrls != NULL ) {
898 				ldap_controls_free( rs->sr_ctrls );
899 				rs->sr_ctrls = NULL;
900 			}
901 			break;
902 
903 		case LDAP_RES_SEARCH_RESULT:
904 			if ( mi->mi_idle_timeout != 0 ) {
905 				asyncmeta_set_msc_time(msc);
906 			}
907 			Debug( LDAP_DEBUG_TRACE,
908 			       "%s asyncmeta_handle_search_msg: msc %p result\n",
909 			       op->o_log_prefix, msc );
910 			candidates[ i ].sr_type = REP_RESULT;
911 			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
912 			/* NOTE: ignores response controls
913 			 * (and intermediate response controls
914 			 * as well, except for those with search
915 			 * references); this may not be correct,
916 			 * but if they're not ignored then
917 			 * back-meta would need to merge them
918 			 * consistently (think of pagedResults...)
919 			 */
920 			/* FIXME: response controls? */
921 			rs->sr_err = ldap_parse_result( msc->msc_ldr,
922 							msg,
923 							&candidates[ i ].sr_err,
924 								(char **)&candidates[ i ].sr_matched,
925 							(char **)&candidates[ i ].sr_text,
926 							&references,
927 							&ctrls /* &candidates[ i ].sr_ctrls (unused) */ ,
928 							0 );
929 			if ( rs->sr_err != LDAP_SUCCESS ) {
930 				candidates[ i ].sr_err = rs->sr_err;
931 				sres = slap_map_api2result( &candidates[ i ] );
932 				candidates[ i ].sr_type = REP_RESULT;
933 				goto finish;
934 			}
935 
936 			rs->sr_err = candidates[ i ].sr_err;
937 
938 			/* massage matchedDN if need be */
939 			if ( candidates[ i ].sr_matched != NULL ) {
940 				struct berval	match, mmatch;
941 
942 				ber_str2bv( candidates[ i ].sr_matched,
943 						0, 0, &match );
944 				candidates[ i ].sr_matched = NULL;
945 
946 				dc.memctx = NULL;
947 				asyncmeta_dn_massage( &dc, &match, &mmatch );
948 				if ( mmatch.bv_val == match.bv_val ) {
949 					candidates[ i ].sr_matched
950 						= ch_strdup( mmatch.bv_val );
951 
952 				} else {
953 					candidates[ i ].sr_matched = mmatch.bv_val;
954 				}
955 
956 				bc->candidate_match++;
957 				ldap_memfree( match.bv_val );
958 			}
959 
960 			/* add references to array */
961 			/* RFC 4511: referrals can only appear
962 			 * if result code is LDAP_REFERRAL */
963 			if ( references != NULL
964 				 && references[ 0 ] != NULL
965 				 && references[ 0 ][ 0 ] != '\0' )
966 			{
967 				if ( rs->sr_err != LDAP_REFERRAL ) {
968 					Debug( LDAP_DEBUG_ANY,
969 						   "%s asncmeta_search_result[%d]: "
970 						   "got referrals with err=%d\n",
971 						   op->o_log_prefix,
972 						   i, rs->sr_err );
973 
974 				} else {
975 					BerVarray	sr_ref;
976 					int		cnt;
977 
978 					for ( cnt = 0; references[ cnt ]; cnt++ )
979 						;
980 
981 					sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( cnt + 1 ),
982 								 op->o_tmpmemctx );
983 
984 					for ( cnt = 0; references[ cnt ]; cnt++ ) {
985 						ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ],
986 								  op->o_tmpmemctx );
987 					}
988 					BER_BVZERO( &sr_ref[ cnt ] );
989 
990 					dc.memctx = op->o_tmpmemctx;
991 					( void )asyncmeta_referral_result_rewrite( &dc, sr_ref );
992 
993 					if ( rs->sr_v2ref == NULL ) {
994 						rs->sr_v2ref = sr_ref;
995 
996 					} else {
997 						for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
998 							ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ],
999 									   op->o_tmpmemctx );
1000 						}
1001 						ber_memfree_x( sr_ref, op->o_tmpmemctx );
1002 					}
1003 				}
1004 
1005 			} else if ( rs->sr_err == LDAP_REFERRAL ) {
1006 				Debug( LDAP_DEBUG_TRACE,
1007 					   "%s asyncmeta_search_result[%d]: "
1008 					   "got err=%d with null "
1009 					   "or empty referrals\n",
1010 					   op->o_log_prefix,
1011 					   i, rs->sr_err );
1012 
1013 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
1014 			}
1015 
1016 			/* cleanup */
1017 			ber_memvfree( (void **)references );
1018 
1019 			sres = slap_map_api2result( rs );
1020 
1021 			if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
1022 				Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d] "
1023 				       "match=\"%s\" err=%ld\n",
1024 				       op->o_log_prefix, i,
1025 				       candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
1026 				       (long) candidates[ i ].sr_err );
1027 			} else {
1028 					Debug( LDAP_DEBUG_ANY,  "%s asyncmeta_search_result[%d] "
1029 				       "match=\"%s\" err=%ld (%s)\n",
1030 				       op->o_log_prefix, i,
1031 				       candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
1032 					       (long) candidates[ i ].sr_err, ldap_err2string( candidates[ i ].sr_err ) );
1033 			}
1034 
1035 			switch ( sres ) {
1036 			case LDAP_NO_SUCH_OBJECT:
1037 				/* is_ok is touched any time a valid
1038 				 * (even intermediate) result is
1039 				 * returned; as a consequence, if
1040 				 * a candidate returns noSuchObject
1041 				 * it is ignored and the candidate
1042 				 * is simply demoted. */
1043 				if ( bc->is_ok ) {
1044 					sres = LDAP_SUCCESS;
1045 				}
1046 				break;
1047 
1048 			case LDAP_SUCCESS:
1049 				if ( ctrls != NULL && ctrls[0] != NULL ) {
1050 #ifdef SLAPD_META_CLIENT_PR
1051 					LDAPControl *pr_c;
1052 
1053 					pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
1054 					if ( pr_c != NULL ) {
1055 						BerElementBuffer berbuf;
1056 						BerElement *ber = (BerElement *)&berbuf;
1057 						ber_tag_t tag;
1058 						ber_int_t prsize;
1059 						struct berval prcookie;
1060 
1061 						/* unsolicited, do not accept */
1062 						if ( mt->mt_ps == 0 ) {
1063 							rs->sr_err = LDAP_OTHER;
1064 							goto err_pr;
1065 						}
1066 
1067 						ber_init2( ber, &pr_c->ldctl_value, LBER_USE_DER );
1068 
1069 						tag = ber_scanf( ber, "{im}", &prsize, &prcookie );
1070 						if ( tag == LBER_ERROR ) {
1071 							rs->sr_err = LDAP_OTHER;
1072 							goto err_pr;
1073 						}
1074 
1075 						/* more pages? new search request */
1076 						if ( !BER_BVISNULL( &prcookie ) && !BER_BVISEMPTY( &prcookie ) ) {
1077 							if ( mt->mt_ps > 0 ) {
1078 								/* ignore size if specified */
1079 								prsize = 0;
1080 
1081 							} else if ( prsize == 0 ) {
1082 								/* guess the page size from the entries returned so far */
1083 								prsize = candidates[ i ].sr_nentries;
1084 							}
1085 
1086 							candidates[ i ].sr_nentries = 0;
1087 							candidates[ i ].sr_msgid = META_MSGID_IGNORE;
1088 							candidates[ i ].sr_type = REP_INTERMEDIATE;
1089 
1090 							assert( candidates[ i ].sr_matched == NULL );
1091 							assert( candidates[ i ].sr_text == NULL );
1092 							assert( candidates[ i ].sr_ref == NULL );
1093 
1094 							switch ( asyncmeta_back_search_start( &bc->copy_op, rs, mc, bc, i, &prcookie, prsize, 1 ) )
1095 							{
1096 							case META_SEARCH_CANDIDATE:
1097 								assert( candidates[ i ].sr_msgid >= 0 );
1098 								ldap_controls_free( ctrls );
1099 								//	goto free_message;
1100 
1101 							case META_SEARCH_ERR:
1102 							case META_SEARCH_NEED_BIND:
1103 err_pr:;
1104 								candidates[ i ].sr_err = rs->sr_err;
1105 								candidates[ i ].sr_type = REP_RESULT;
1106 								if ( META_BACK_ONERR_STOP( mi ) ) {
1107 									asyncmeta_send_ldap_result(bc, op, rs);
1108 									ldap_controls_free( ctrls );
1109 									goto err_cleanup;
1110 								}
1111 								/* fallthru */
1112 
1113 							case META_SEARCH_NOT_CANDIDATE:
1114 								/* means that asyncmeta_back_search_start()
1115 								 * failed but onerr == continue */
1116 								candidates[ i ].sr_msgid = META_MSGID_IGNORE;
1117 								candidates[ i ].sr_type = REP_RESULT;
1118 								break;
1119 
1120 							default:
1121 								/* impossible */
1122 								assert( 0 );
1123 								break;
1124 							}
1125 							break;
1126 						}
1127 					}
1128 #endif /* SLAPD_META_CLIENT_PR */
1129 
1130 					ldap_controls_free( ctrls );
1131 				}
1132 				/* fallthru */
1133 
1134 			case LDAP_REFERRAL:
1135 				bc->is_ok++;
1136 				break;
1137 
1138 			case LDAP_SIZELIMIT_EXCEEDED:
1139 				/* if a target returned sizelimitExceeded
1140 				 * and the entry count is equal to the
1141 				 * proxy's limit, the target would have
1142 				 * returned more, and the error must be
1143 				 * propagated to the client; otherwise,
1144 				 * the target enforced a limit lower
1145 				 * than what requested by the proxy;
1146 				 * ignore it */
1147 				candidates[ i ].sr_err = rs->sr_err;
1148 				if ( rs->sr_nentries == op->ors_slimit
1149 					 || META_BACK_ONERR_STOP( mi ) )
1150 				{
1151 					const char *save_text;
1152 got_err:
1153 					save_text = rs->sr_text;
1154 					rs->sr_text = candidates[ i ].sr_text;
1155 					asyncmeta_send_ldap_result(bc, op, rs);
1156 					if (candidates[ i ].sr_text != NULL) {
1157 						ch_free( (char *)candidates[ i ].sr_text );
1158 						candidates[ i ].sr_text = NULL;
1159 					}
1160 					rs->sr_text = save_text;
1161 					ldap_controls_free( ctrls );
1162 					goto err_cleanup;
1163 				}
1164 				break;
1165 
1166 			default:
1167 				candidates[ i ].sr_err = rs->sr_err;
1168 				if ( META_BACK_ONERR_STOP( mi ) ) {
1169 					goto got_err;
1170 				}
1171 				break;
1172 			}
1173 			/* if this is the last result we will ever receive, send it back  */
1174 			rc = rs->sr_err;
1175 			if (asyncmeta_is_last_result(mc, bc, i) == 0) {
1176 				Debug( LDAP_DEBUG_TRACE,
1177 					"%s asyncmeta_handle_search_msg: msc %p last result\n",
1178 					op->o_log_prefix, msc );
1179 				asyncmeta_search_last_result(mc, bc, i, sres);
1180 err_cleanup:
1181 				rc = rs->sr_err;
1182 				ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1183 				asyncmeta_drop_bc( mc, bc);
1184 				asyncmeta_clear_bm_context(bc);
1185 				ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1186 				ldap_msgfree(res);
1187 				return rc;
1188 			}
1189 finish:
1190 			break;
1191 
1192 		default:
1193 			continue;
1194 		}
1195 	}
1196 		ldap_msgfree(res);
1197 		res = NULL;
1198 		if (candidates[ i ].sr_type != REP_RESULT) {
1199 			struct timeval	tv = {0};
1200 			rc = ldap_result( msc->msc_ldr, id, LDAP_MSG_RECEIVED, &tv, &res );
1201 			if (res != NULL) {
1202 				msc->msc_result_time = slap_get_time();
1203 			}
1204 		}
1205 	}
1206 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1207 	bc->bc_active--;
1208 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1209 
1210 	return rc;
1211 }
1212 
1213 /* handles the received result for add, modify, modrdn, compare and delete ops */
1214 
asyncmeta_handle_common_result(LDAPMessage * msg,a_metaconn_t * mc,bm_context_t * bc,int candidate)1215 int asyncmeta_handle_common_result(LDAPMessage *msg, a_metaconn_t *mc, bm_context_t *bc, int candidate)
1216 {
1217 	a_metainfo_t	*mi;
1218 	a_metatarget_t	*mt;
1219 	a_metasingleconn_t *msc;
1220 	const char	*save_text = NULL,
1221 		*save_matched = NULL;
1222 	BerVarray	save_ref = NULL;
1223 	LDAPControl	**save_ctrls = NULL;
1224 	void		*matched_ctx = NULL;
1225 
1226 	char		*matched = NULL;
1227 	char		*text = NULL;
1228 	char		**refs = NULL;
1229 	LDAPControl	**ctrls = NULL;
1230 	Operation *op;
1231 	SlapReply *rs;
1232 	int		rc;
1233 
1234 	mi = mc->mc_info;
1235 	mt = mi->mi_targets[ candidate ];
1236 	msc = &mc->mc_conns[ candidate ];
1237 
1238 	op = bc->op;
1239 	rs = &bc->rs;
1240 	save_text = rs->sr_text,
1241 	save_matched = rs->sr_matched;
1242 	save_ref = rs->sr_ref;
1243 	save_ctrls = rs->sr_ctrls;
1244 	rs->sr_text = NULL;
1245 	rs->sr_matched = NULL;
1246 	rs->sr_ref = NULL;
1247 	rs->sr_ctrls = NULL;
1248 
1249 	/* only touch when activity actually took place... */
1250 	if ( mi->mi_idle_timeout != 0 ) {
1251 		asyncmeta_set_msc_time(msc);
1252 	}
1253 
1254 	rc = ldap_parse_result( msc->msc_ldr, msg, &rs->sr_err,
1255 				&matched, &text, &refs, &ctrls, 0 );
1256 
1257 	if ( rc == LDAP_SUCCESS ) {
1258 		rs->sr_text = text;
1259 	} else {
1260 		rs->sr_err = rc;
1261 	}
1262 	rs->sr_err = slap_map_api2result( rs );
1263 
1264 	/* RFC 4511: referrals can only appear
1265 	 * if result code is LDAP_REFERRAL */
1266 	if ( refs != NULL
1267 	     && refs[ 0 ] != NULL
1268 	     && refs[ 0 ][ 0 ] != '\0' )
1269 	{
1270 		if ( rs->sr_err != LDAP_REFERRAL ) {
1271 			Debug( LDAP_DEBUG_ANY,
1272 			       "%s asyncmeta_handle_common_result[%d]: "
1273 			       "got referrals with err=%d\n",
1274 			       op->o_log_prefix,
1275 			       candidate, rs->sr_err );
1276 
1277 		} else {
1278 			int	i;
1279 
1280 			for ( i = 0; refs[ i ] != NULL; i++ )
1281 				/* count */ ;
1282 			rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
1283 						     op->o_tmpmemctx );
1284 			for ( i = 0; refs[ i ] != NULL; i++ ) {
1285 				ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
1286 			}
1287 			BER_BVZERO( &rs->sr_ref[ i ] );
1288 		}
1289 
1290 	} else if ( rs->sr_err == LDAP_REFERRAL ) {
1291 		Debug( LDAP_DEBUG_ANY,
1292 		       "%s asyncmeta_handle_common_result[%d]: "
1293 		       "got err=%d with null "
1294 		       "or empty referrals\n",
1295 		       op->o_log_prefix,
1296 		       candidate, rs->sr_err );
1297 
1298 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
1299 	}
1300 
1301 	if ( ctrls != NULL ) {
1302 		rs->sr_ctrls = ctrls;
1303 	}
1304 
1305 	/* if the error in the reply structure is not
1306 	 * LDAP_SUCCESS, try to map it from client
1307 	 * to server error */
1308 	if ( !LDAP_ERR_OK( rs->sr_err ) ) {
1309 		rs->sr_err = slap_map_api2result( rs );
1310 
1311 		/* internal ops ( op->o_conn == NULL )
1312 		 * must not reply to client */
1313 		if ( op->o_conn && !op->o_do_not_cache && matched ) {
1314 
1315 			/* record the (massaged) matched
1316 			 * DN into the reply structure */
1317 			rs->sr_matched = matched;
1318 		}
1319 	}
1320 
1321 	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
1322 		asyncmeta_quarantine( op, mi, rs, candidate );
1323 	}
1324 
1325 	if ( matched != NULL ) {
1326 		struct berval	dn, pdn;
1327 
1328 		ber_str2bv( matched, 0, 0, &dn );
1329 		if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
1330 			ldap_memfree( matched );
1331 			matched_ctx = op->o_tmpmemctx;
1332 			matched = pdn.bv_val;
1333 		}
1334 		rs->sr_matched = matched;
1335 	}
1336 
1337 	if ( rs->sr_err == LDAP_UNAVAILABLE || rs->sr_err == LDAP_SERVER_DOWN ) {
1338 		if ( rs->sr_text == NULL ) {
1339 				rs->sr_text = "Target is unavailable";
1340 			}
1341 	}
1342 
1343 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1344 	asyncmeta_drop_bc( mc, bc);
1345 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1346 
1347 	if ( op->o_conn ) {
1348 		asyncmeta_send_ldap_result(bc, op, rs);
1349 	}
1350 
1351 	if ( matched ) {
1352 		op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
1353 	}
1354 	if ( text ) {
1355 		ldap_memfree( text );
1356 	}
1357 	if ( rs->sr_ref ) {
1358 		op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
1359 		rs->sr_ref = NULL;
1360 	}
1361 	if ( refs ) {
1362 		ber_memvfree( (void **)refs );
1363 	}
1364 	if ( ctrls ) {
1365 		assert( rs->sr_ctrls != NULL );
1366 		ldap_controls_free( ctrls );
1367 	}
1368 
1369 	rs->sr_text = save_text;
1370 	rs->sr_matched = save_matched;
1371 	rs->sr_ref = save_ref;
1372 	rs->sr_ctrls = save_ctrls;
1373 	rc = (LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err);
1374 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1375 	asyncmeta_clear_bm_context(bc);
1376 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1377 	return rc;
1378 }
1379 
1380 /* This takes care to clean out the outbound queue in case we have a read error
1381  * sending back responses to the client */
1382 int
asyncmeta_op_read_error(a_metaconn_t * mc,int candidate,int error,void * ctx)1383 asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error, void* ctx)
1384 {
1385 	bm_context_t *bc, *onext;
1386 	int cleanup;
1387 	Operation *op;
1388 	SlapReply *rs;
1389 	SlapReply *candidates;
1390 
1391 	/* no outstanding ops, nothing to do but log */
1392 	Debug( LDAP_DEBUG_TRACE,
1393 	       "asyncmeta_op_read_error: ldr=%p, err=%d\n",
1394 	       mc->mc_conns[candidate].msc_ldr, error );
1395 
1396 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1397 	/*someone may be trying to write */
1398 	if (mc->mc_conns[candidate].msc_active <= 1) {
1399 		asyncmeta_clear_one_msc(NULL, mc, candidate, 0, __FUNCTION__);
1400 	} else {
1401 		META_BACK_CONN_INVALID_SET(&mc->mc_conns[candidate]);
1402 	}
1403 
1404 	if (mc->pending_ops <= 0) {
1405 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1406 		return LDAP_SUCCESS;
1407 	}
1408 
1409 	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
1410 		onext = LDAP_STAILQ_NEXT(bc, bc_next);
1411 		cleanup = 0;
1412 		candidates = bc->candidates;
1413 		/* was this op affected? */
1414 		if ( !META_IS_CANDIDATE( &candidates[ candidate ] ) )
1415 			continue;
1416 
1417 		if (bc->op->o_abandon) {
1418 			bc->bc_invalid = 1;
1419 			continue;
1420 		}
1421 
1422 		if (bc->bc_active > 0) {
1423 			bc->bc_invalid = 1;
1424 			continue;
1425 		}
1426 
1427 		bc->op->o_threadctx = ctx;
1428 		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
1429 		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
1430 
1431 		op = bc->op;
1432 		rs = &bc->rs;
1433 		switch (op->o_tag) {
1434 		case LDAP_REQ_ADD:
1435 		case LDAP_REQ_MODIFY:
1436 		case LDAP_REQ_MODRDN:
1437 		case LDAP_REQ_COMPARE:
1438 		case LDAP_REQ_DELETE:
1439 			rs->sr_err = LDAP_UNAVAILABLE;
1440 			rs->sr_text = "Read error on connection to target";
1441 			asyncmeta_send_ldap_result( bc, op, rs );
1442 			cleanup = 1;
1443 			break;
1444 		case LDAP_REQ_SEARCH:
1445 		{
1446 			a_metainfo_t *mi = mc->mc_info;
1447 			rs->sr_err = LDAP_UNAVAILABLE;
1448 			rs->sr_text = "Read error on connection to target";
1449 			candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
1450 			candidates[ candidate ].sr_type = REP_RESULT;
1451 			if ( (META_BACK_ONERR_STOP( mi ) ||
1452 			      asyncmeta_is_last_result(mc, bc, candidate)) && op->o_conn) {
1453 				asyncmeta_send_ldap_result( bc, op, rs );
1454 				cleanup = 1;
1455 			}
1456 		}
1457 			break;
1458 		default:
1459 			break;
1460 		}
1461 
1462 		if (cleanup) {
1463 			int j;
1464 			a_metainfo_t *mi = mc->mc_info;
1465 			for (j=0; j<mi->mi_ntargets; j++) {
1466 				if (j != candidate && bc->candidates[j].sr_msgid >= 0
1467 				    && mc->mc_conns[j].msc_ld != NULL) {
1468 					asyncmeta_back_cancel( mc, op,
1469 							       bc->candidates[ j ].sr_msgid, j );
1470 				}
1471 			}
1472 			LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
1473 			mc->pending_ops--;
1474 			asyncmeta_clear_bm_context(bc);
1475 		}
1476 	}
1477 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1478 	return LDAP_SUCCESS;
1479 }
1480 
1481 void *
asyncmeta_op_handle_result(void * ctx,void * arg)1482 asyncmeta_op_handle_result(void *ctx, void *arg)
1483 {
1484 	a_metaconn_t *mc = arg;
1485 	int		i, j, rc, ntargets;
1486 	struct timeval	tv = {0};
1487 	LDAPMessage     *msg;
1488 	a_metasingleconn_t *msc;
1489 	bm_context_t *bc;
1490 	void *oldctx;
1491 
1492 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1493 	rc = ++mc->mc_active;
1494 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1495 	if (rc > 1)
1496 		return NULL;
1497 
1498 	ntargets = mc->mc_info->mi_ntargets;
1499 	i = ntargets;
1500 	oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);	/* get existing memctx */
1501 
1502 again:
1503 	for (j=0; j<ntargets; j++) {
1504 		i++;
1505 		if (i >= ntargets) i = 0;
1506 		msc = &mc->mc_conns[i];
1507 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1508 		if (!mc->mc_conns[i].msc_ldr ||
1509 		    META_BACK_CONN_CREATING( &mc->mc_conns[i] ) ||
1510 		    META_BACK_CONN_INVALID(&mc->mc_conns[i])) {
1511 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1512 			continue;
1513 		}
1514 
1515 		msc->msc_active++;
1516 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1517 
1518 		rc = ldap_result( mc->mc_conns[i].msc_ldr, LDAP_RES_ANY, LDAP_MSG_RECEIVED, &tv, &msg );
1519 		if (rc < 1) {
1520 			if (rc < 0) {
1521 				ldap_get_option( mc->mc_conns[i].msc_ldr, LDAP_OPT_ERROR_NUMBER, &rc);
1522 				META_BACK_CONN_INVALID_SET(&mc->mc_conns[i]);
1523 				asyncmeta_op_read_error(mc, i, rc, ctx);
1524 			}
1525 			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1526 			msc->msc_active--;
1527 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1528 			continue;
1529 		}
1530 		rc = ldap_msgtype( msg );
1531 		if (rc == LDAP_RES_BIND) {
1532 			if ( LogTest( asyncmeta_debug ) ) {
1533 				char	time_buf[ SLAP_TEXT_BUFLEN ];
1534 				asyncmeta_get_timestamp(time_buf);
1535 				Debug( asyncmeta_debug, "[%s] asyncmeta_op_handle_result received bind msgid=%d msc: %p\n",
1536 				      time_buf, ldap_msgid(msg), msc );
1537 			}
1538 			asyncmeta_handle_bind_result(msg, mc, i, ctx);
1539 			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
1540 			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1541 			msc->msc_result_time = slap_get_time();
1542 			msc->msc_active--;
1543 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1544 			if (msg)
1545 				ldap_msgfree(msg);
1546 
1547 			continue;
1548 		}
1549 retry_bc:
1550 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1551 		bc = asyncmeta_find_message(ldap_msgid(msg), mc, i);
1552 /* The sender might not be yet done with the context. On error it might also remove it
1553  * so it's best to try and find it again after a wait */
1554 		if (bc && bc->bc_active > 0) {
1555 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1556 			ldap_pvt_thread_yield();
1557 			goto retry_bc;
1558 		}
1559 		if (bc) {
1560 			bc->bc_active++;
1561 		}
1562 
1563 		msc->msc_result_time = slap_get_time();
1564 		msc->msc_active--;
1565 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1566 		if (!bc) {
1567 			Debug( asyncmeta_debug,
1568 				"asyncmeta_op_handle_result: Unable to find bc for msguid %d, msc: %p\n", ldap_msgid(msg), msc );
1569 			ldap_msgfree(msg);
1570 			continue;
1571 		}
1572 
1573 		/* set our memctx */
1574 		bc->op->o_threadctx = ctx;
1575 		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
1576 		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
1577 		if (bc->op->o_abandon) {
1578 			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1579 			asyncmeta_drop_bc( mc, bc);
1580 			if ( bc->op->o_tag == LDAP_REQ_SEARCH ) {
1581 				int j;
1582 				for (j=0; j<ntargets; j++) {
1583 					if (bc->candidates[j].sr_msgid >= 0) {
1584 						a_metasingleconn_t *tmp_msc = &mc->mc_conns[j];
1585 						tmp_msc->msc_active++;
1586 						asyncmeta_back_cancel( mc, bc->op,
1587 								       bc->candidates[ j ].sr_msgid, j );
1588 						tmp_msc->msc_active--;
1589 					}
1590 				}
1591 			}
1592 			asyncmeta_clear_bm_context(bc);
1593 			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1594 			if (msg)
1595 				ldap_msgfree(msg);
1596 			continue;
1597 		}
1598 
1599 		switch (rc) {
1600 		case LDAP_RES_SEARCH_ENTRY:
1601 		case LDAP_RES_SEARCH_REFERENCE:
1602 		case LDAP_RES_SEARCH_RESULT:
1603 		case LDAP_RES_INTERMEDIATE:
1604 			asyncmeta_handle_search_msg(msg, mc, bc, i);
1605 			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
1606 			msg = NULL;
1607 			break;
1608 		case LDAP_RES_ADD:
1609 		case LDAP_RES_DELETE:
1610 		case LDAP_RES_MODDN:
1611 		case LDAP_RES_COMPARE:
1612 		case LDAP_RES_MODIFY:
1613 			rc = asyncmeta_handle_common_result(msg, mc, bc, i);
1614 			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
1615 			break;
1616 		default:
1617 			{
1618 			Debug( asyncmeta_debug,
1619 				   "asyncmeta_op_handle_result: "
1620 				   "unrecognized response message tag=%d\n",
1621 				   rc );
1622 
1623 			}
1624 		}
1625 		if (msg)
1626 			ldap_msgfree(msg);
1627 	}
1628 
1629 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1630 	rc = --mc->mc_active;
1631 	if (rc) {
1632 		i++;
1633 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1634 		goto again;
1635 	}
1636 	slap_sl_mem_setctx(ctx, oldctx);
1637 	if (mc->mc_conns) {
1638 		for (i=0; i<ntargets; i++) {
1639 			if (!slapd_shutdown && !META_BACK_CONN_INVALID(msc)
1640 			    && mc->mc_conns[i].msc_ldr && mc->mc_conns[i].conn) {
1641 				connection_client_enable(mc->mc_conns[i].conn);
1642 			}
1643 		}
1644 	}
1645 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1646 	return NULL;
1647 }
1648 
asyncmeta_set_msc_time(a_metasingleconn_t * msc)1649 void asyncmeta_set_msc_time(a_metasingleconn_t *msc)
1650 {
1651 	msc->msc_time = slap_get_time();
1652 }
1653 
asyncmeta_timeout_loop(void * ctx,void * arg)1654 void* asyncmeta_timeout_loop(void *ctx, void *arg)
1655 {
1656 	struct re_s* rtask = arg;
1657 	a_metainfo_t *mi = rtask->arg;
1658 	bm_context_t *bc, *onext;
1659 	time_t current_time = slap_get_time();
1660 	int i, j;
1661 	LDAP_STAILQ_HEAD(BCList, bm_context_t) timeout_list;
1662 	LDAP_STAILQ_INIT( &timeout_list );
1663 
1664 	Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time );
1665 	void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);
1666 	for (i=0; i<mi->mi_num_conns; i++) {
1667 		a_metaconn_t * mc= &mi->mi_conns[i];
1668 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1669 		for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
1670 			onext = LDAP_STAILQ_NEXT(bc, bc_next);
1671 			if (bc->bc_active > 0) {
1672 				continue;
1673 			}
1674 
1675 			if (bc->op->o_abandon ) {
1676 					/* set our memctx */
1677 				bc->op->o_threadctx = ctx;
1678 				bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
1679 				slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
1680 				Operation *op = bc->op;
1681 
1682 				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
1683 				mc->pending_ops--;
1684 				for (j=0; j<mi->mi_ntargets; j++) {
1685 					if (bc->candidates[j].sr_msgid >= 0) {
1686 						a_metasingleconn_t *msc = &mc->mc_conns[j];
1687 						if ( op->o_tag == LDAP_REQ_SEARCH ) {
1688 							msc->msc_active++;
1689 							asyncmeta_back_cancel( mc, op,
1690 									       bc->candidates[ j ].sr_msgid, j );
1691 							msc->msc_active--;
1692 						}
1693 					}
1694 				}
1695 				asyncmeta_clear_bm_context(bc);
1696 				continue;
1697 			}
1698 			if (bc->bc_invalid) {
1699 				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
1700 				mc->pending_ops--;
1701 				LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
1702 				continue;
1703 			}
1704 
1705 			if (bc->timeout && bc->stoptime < current_time) {
1706 				Operation *op = bc->op;
1707 				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
1708 				mc->pending_ops--;
1709 				LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
1710 				for (j=0; j<mi->mi_ntargets; j++) {
1711 					if (bc->candidates[j].sr_msgid >= 0) {
1712 						a_metasingleconn_t *msc = &mc->mc_conns[j];
1713 						asyncmeta_set_msc_time(msc);
1714 						if ( op->o_tag == LDAP_REQ_SEARCH ) {
1715 							msc->msc_active++;
1716 							asyncmeta_back_cancel( mc, op,
1717 									       bc->candidates[ j ].sr_msgid, j );
1718 							msc->msc_active--;
1719 						}
1720 					}
1721 				}
1722 			}
1723 		}
1724 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1725 
1726 		for (bc = LDAP_STAILQ_FIRST(&timeout_list); bc; bc = onext) {
1727 			Operation *op = bc->op;
1728 			SlapReply *rs = &bc->rs;
1729 			int		timeout_err;
1730 			const char *timeout_text;
1731 
1732 			onext = LDAP_STAILQ_NEXT(bc, bc_next);
1733 			LDAP_STAILQ_REMOVE(&timeout_list, bc, bm_context_t, bc_next);
1734 			/* set our memctx */
1735 			bc->op->o_threadctx = ctx;
1736 			bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
1737 			slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
1738 
1739 			if (bc->searchtime) {
1740 				timeout_err = LDAP_TIMELIMIT_EXCEEDED;
1741 			} else {
1742 				timeout_err = op->o_protocol >= LDAP_VERSION3 ?
1743 					LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
1744 			}
1745 
1746 			if ( bc->bc_invalid ) {
1747 				timeout_text = "Operation is invalid - target connection has been reset";
1748 			} else {
1749 				a_metasingleconn_t *log_msc =  &mc->mc_conns[0];
1750 				Debug( asyncmeta_debug,
1751 				       "asyncmeta_timeout_loop:Timeout op %s loop[%p], "
1752 				       "current_time:%ld, op->o_time:%ld msc: %p, "
1753 				       "msc->msc_binding_time: %x, msc->msc_flags:%x \n",
1754 				       bc->op->o_log_prefix, rtask, current_time, bc->op->o_time,
1755 				       log_msc, (unsigned int)log_msc->msc_binding_time, log_msc->msc_mscflags );
1756 
1757 				if (bc->searchtime) {
1758 					timeout_text = NULL;
1759 				} else {
1760 					timeout_text = "Operation timed out";
1761 				}
1762 
1763 				for (j=0; j<mi->mi_ntargets; j++) {
1764 					if (bc->candidates[j].sr_msgid >= 0) {
1765 						a_metatarget_t     *mt = mi->mi_targets[j];
1766 						if (!META_BACK_TGT_QUARANTINE( mt ) ||
1767 						    bc->candidates[j].sr_type == REP_RESULT) {
1768 							continue;
1769 						}
1770 
1771 						if (mt->mt_isquarantined > LDAP_BACK_FQ_NO) {
1772 							timeout_err = LDAP_UNAVAILABLE;
1773 						} else {
1774 							mt->mt_timeout_ops++;
1775 							if ((mi->mi_max_timeout_ops > 0) &&
1776 							    (mt->mt_timeout_ops > mi->mi_max_timeout_ops)) {
1777 								timeout_err = LDAP_UNAVAILABLE;
1778 								rs->sr_err = timeout_err;
1779 								if (mt->mt_isquarantined == LDAP_BACK_FQ_NO)
1780 									asyncmeta_quarantine(op, mi, rs, j);
1781 							}
1782 						}
1783 					}
1784 				}
1785 			}
1786 			rs->sr_err = timeout_err;
1787 			rs->sr_text = timeout_text;
1788 			if (!bc->op->o_abandon ) {
1789 				asyncmeta_send_ldap_result( bc, bc->op, &bc->rs );
1790 			}
1791 			asyncmeta_clear_bm_context(bc);
1792 		}
1793 
1794 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1795 		if (mi->mi_idle_timeout) {
1796 			for (j=0; j<mi->mi_ntargets; j++) {
1797 				a_metasingleconn_t *msc = &mc->mc_conns[j];
1798 				if ( msc->msc_active > 0 ) {
1799 					continue;
1800 				}
1801 				if (mc->pending_ops > 0) {
1802 					continue;
1803 				}
1804 				current_time = slap_get_time();
1805 				if (msc->msc_ld && msc->msc_time > 0 && msc->msc_time + mi->mi_idle_timeout < current_time) {
1806 					asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
1807 				}
1808 			}
1809 		}
1810 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1811 	}
1812 
1813 	slap_sl_mem_setctx(ctx, oldctx);
1814 	current_time = slap_get_time();
1815 	Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] stop at [%ld] \n", rtask, current_time );
1816 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1817 	if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
1818 		ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
1819 	}
1820 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1821 	return NULL;
1822 }
1823 
1824