1/*	$NetBSD: search.c,v 1.2 2021/08/14 16:14:59 christos Exp $	*/
2
3/* search.c - search request handler for back-asyncmeta */
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: search.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/socket.h>
33#include <ac/string.h>
34#include <ac/time.h>
35#include "slap.h"
36#include "../../../libraries/liblber/lber-int.h"
37#include "../../../libraries/libldap/ldap-int.h"
38#include "lutil.h"
39#include "../back-ldap/back-ldap.h"
40#include "back-asyncmeta.h"
41
42static void
43asyncmeta_handle_onerr_stop(Operation *op,
44			    SlapReply *rs,
45			    a_metaconn_t *mc,
46			    bm_context_t *bc,
47			    int candidate)
48{
49	a_metainfo_t *mi = mc->mc_info;
50	int j;
51	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
52	if (asyncmeta_bc_in_queue(mc,bc) == NULL || bc->bc_active > 1) {
53		bc->bc_active--;
54		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
55		return;
56	}
57	asyncmeta_drop_bc(mc, bc);
58	for (j=0; j<mi->mi_ntargets; j++) {
59		if (j != candidate && bc->candidates[j].sr_msgid >= 0
60		    && mc->mc_conns[j].msc_ld != NULL && !META_BACK_CONN_CREATING( &mc->mc_conns[j] )) {
61			asyncmeta_back_cancel( mc, op,
62						bc->candidates[ j ].sr_msgid, j );
63		}
64	}
65	slap_sl_mem_setctx(op->o_threadctx, op->o_tmpmemctx);
66	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
67	send_ldap_result(op, rs);
68}
69
70static int
71asyncmeta_int_filter2bv( a_dncookie		*dc,
72			 Filter			*f,
73			 struct berval	*fstr )
74{
75	int		i;
76	Filter		*p;
77	struct berval	atmp,
78			vtmp,
79			ntmp,
80			*tmp;
81	static struct berval
82			/* better than nothing... */
83			ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
84			ber_bvtf_false = BER_BVC( "(|)" ),
85			/* better than nothing... */
86			ber_bvtrue = BER_BVC( "(objectClass=*)" ),
87			ber_bvtf_true = BER_BVC( "(&)" ),
88			ber_bverror = BER_BVC( "(?=error)" ),
89			ber_bvunknown = BER_BVC( "(?=unknown)" ),
90			ber_bvnone = BER_BVC( "(?=none)" );
91	ber_len_t	len;
92	void *memctx = dc->memctx;
93
94	assert( fstr != NULL );
95	BER_BVZERO( fstr );
96
97	if ( f == NULL ) {
98		ber_dupbv_x( fstr, &ber_bvnone, memctx );
99		return LDAP_OTHER;
100	}
101
102	switch ( ( f->f_choice & SLAPD_FILTER_MASK ) ) {
103	case LDAP_FILTER_EQUALITY:
104		if ( f->f_av_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
105			asyncmeta_dn_massage( dc, &f->f_av_value, &vtmp );
106		} else {
107			vtmp = f->f_av_value;
108		}
109
110		filter_escape_value_x( &vtmp, &ntmp, memctx );
111		fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
112			+ ( sizeof("(=)") - 1 );
113		fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
114
115		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
116			f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
117
118		ber_memfree_x( ntmp.bv_val, memctx );
119		break;
120
121	case LDAP_FILTER_GE:
122		filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
123		fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
124			+ ( sizeof("(>=)") - 1 );
125		fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
126
127		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
128			f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
129
130		ber_memfree_x( ntmp.bv_val, memctx );
131		break;
132
133	case LDAP_FILTER_LE:
134		filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
135		fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
136			+ ( sizeof("(<=)") - 1 );
137		fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
138
139		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
140			f->f_av_desc->ad_cname.bv_val,  ntmp.bv_len ? ntmp.bv_val : "" );
141
142		ber_memfree_x( ntmp.bv_val, memctx );
143		break;
144
145	case LDAP_FILTER_APPROX:
146		filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
147		fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
148			+ ( sizeof("(~=)") - 1 );
149		fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
150
151		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
152			f->f_av_desc->ad_cname.bv_val,  ntmp.bv_len ? ntmp.bv_val : "" );
153
154		ber_memfree_x( ntmp.bv_val, memctx );
155		break;
156
157	case LDAP_FILTER_SUBSTRINGS:
158		fstr->bv_len = f->f_sub_desc->ad_cname.bv_len + ( STRLENOF( "(=*)" ) );
159		fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 128, memctx ); /* FIXME: why 128 ? */
160
161		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
162			f->f_sub_desc->ad_cname.bv_val );
163
164		if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
165			len = fstr->bv_len;
166
167			filter_escape_value_x( &f->f_sub_initial, &ntmp, memctx );
168
169			fstr->bv_len += ntmp.bv_len;
170			fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
171
172			snprintf( &fstr->bv_val[len - 2], ntmp.bv_len + 3,
173				/* "(attr=" */ "%s*)",
174				ntmp.bv_len ? ntmp.bv_val : "" );
175
176			ber_memfree_x( ntmp.bv_val, memctx );
177		}
178
179		if ( f->f_sub_any != NULL ) {
180			for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
181				len = fstr->bv_len;
182				filter_escape_value_x( &f->f_sub_any[i], &ntmp, memctx );
183
184				fstr->bv_len += ntmp.bv_len + 1;
185				fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
186
187				snprintf( &fstr->bv_val[len - 1], ntmp.bv_len + 3,
188					/* "(attr=[init]*[any*]" */ "%s*)",
189					ntmp.bv_len ? ntmp.bv_val : "" );
190				ber_memfree_x( ntmp.bv_val, memctx );
191			}
192		}
193
194		if ( !BER_BVISNULL( &f->f_sub_final ) ) {
195			len = fstr->bv_len;
196
197			filter_escape_value_x( &f->f_sub_final, &ntmp, memctx );
198
199			fstr->bv_len += ntmp.bv_len;
200			fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
201
202			snprintf( &fstr->bv_val[len - 1], ntmp.bv_len + 3,
203				/* "(attr=[init*][any*]" */ "%s)",
204				ntmp.bv_len ? ntmp.bv_val : "" );
205
206			ber_memfree_x( ntmp.bv_val, memctx );
207		}
208
209		break;
210
211	case LDAP_FILTER_PRESENT:
212		fstr->bv_len = f->f_desc->ad_cname.bv_len + ( STRLENOF( "(=*)" ) );
213		fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
214
215		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
216			f->f_desc->ad_cname.bv_val );
217		break;
218
219	case LDAP_FILTER_AND:
220	case LDAP_FILTER_OR:
221	case LDAP_FILTER_NOT:
222		fstr->bv_len = STRLENOF( "(%)" );
223		fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 128, memctx );	/* FIXME: why 128? */
224
225		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
226			f->f_choice == LDAP_FILTER_AND ? '&' :
227			f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
228
229		for ( p = f->f_list; p != NULL; p = p->f_next ) {
230			int	rc;
231
232			len = fstr->bv_len;
233
234			rc = asyncmeta_int_filter2bv( dc, p, &vtmp );
235			if ( rc != LDAP_SUCCESS ) {
236				return rc;
237			}
238
239			fstr->bv_len += vtmp.bv_len;
240			fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
241
242			snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
243				/*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
244
245			ber_memfree_x( vtmp.bv_val, memctx );
246		}
247
248		break;
249
250	case LDAP_FILTER_EXT:
251		if ( f->f_mr_desc ) {
252			atmp = f->f_mr_desc->ad_cname;
253
254		} else {
255			BER_BVSTR( &atmp, "" );
256		}
257		filter_escape_value_x( &f->f_mr_value, &ntmp, memctx );
258
259		/* FIXME: cleanup (less ?: operators...) */
260		fstr->bv_len = atmp.bv_len +
261			( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
262			( !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
263			ntmp.bv_len + ( STRLENOF( "(:=)" ) );
264		fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
265
266		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
267			atmp.bv_val,
268			f->f_mr_dnattrs ? ":dn" : "",
269			!BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
270			!BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
271			ntmp.bv_len ? ntmp.bv_val : "" );
272		ber_memfree_x( ntmp.bv_val, memctx );
273		break;
274
275	case SLAPD_FILTER_COMPUTED:
276		switch ( f->f_result ) {
277		/* FIXME: treat UNDEFINED as FALSE */
278		case SLAPD_COMPARE_UNDEFINED:
279			if ( META_BACK_TGT_NOUNDEFFILTER( dc->target ) ) {
280				return LDAP_COMPARE_FALSE;
281			}
282			/* fallthru */
283
284		case LDAP_COMPARE_FALSE:
285			if ( META_BACK_TGT_T_F( dc->target ) ) {
286				tmp = &ber_bvtf_false;
287				break;
288			}
289			tmp = &ber_bvfalse;
290			break;
291
292		case LDAP_COMPARE_TRUE:
293			if ( META_BACK_TGT_T_F( dc->target ) ) {
294				tmp = &ber_bvtf_true;
295				break;
296			}
297
298			tmp = &ber_bvtrue;
299			break;
300
301		default:
302			tmp = &ber_bverror;
303			break;
304		}
305
306		ber_dupbv_x( fstr, tmp, memctx );
307		break;
308
309	default:
310		ber_dupbv_x( fstr, &ber_bvunknown, memctx );
311		break;
312	}
313
314	return 0;
315}
316meta_search_candidate_t
317asyncmeta_back_search_start(
318				Operation *op,
319				SlapReply *rs,
320				a_metaconn_t *mc,
321				bm_context_t *bc,
322				int candidate,
323				struct berval		*prcookie,
324				ber_int_t		prsize,
325				int do_lock)
326{
327	SlapReply		*candidates = bc->candidates;
328	a_metainfo_t		*mi = ( a_metainfo_t * )mc->mc_info;
329	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
330	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
331	a_dncookie		dc;
332	struct berval		realbase = op->o_req_dn;
333	char		**attrs;
334	int			realscope = op->ors_scope;
335	struct berval		mbase = BER_BVNULL;
336	int			rc;
337	struct berval	filterbv = BER_BVNULL;
338	meta_search_candidate_t	retcode;
339	int timelimit;
340	LDAPControl		**ctrls = NULL;
341	BerElement *ber = NULL;
342	ber_int_t	msgid;
343	ber_socket_t s = -1;
344#ifdef SLAPD_META_CLIENT_PR
345	LDAPControl		**save_ctrls = NULL;
346#endif /* SLAPD_META_CLIENT_PR */
347
348	/* this should not happen; just in case... */
349	if ( msc->msc_ld == NULL ) {
350		Debug( LDAP_DEBUG_ANY,
351			"%s: asyncmeta_back_search_start candidate=%d ld=NULL%s.\n",
352			op->o_log_prefix, candidate,
353			META_BACK_ONERR_STOP( mi ) ? "" : " (ignored)" );
354		candidates[ candidate ].sr_err = LDAP_OTHER;
355		if ( META_BACK_ONERR_STOP( mi ) ) {
356			return META_SEARCH_ERR;
357		}
358		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
359		return META_SEARCH_NOT_CANDIDATE;
360	}
361
362	Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_back_search_start: dn=%s filter=%s\n",
363	       op->o_log_prefix, op->o_req_dn.bv_val, op->ors_filterstr.bv_val );
364	/*
365	 * modifies the base according to the scope, if required
366	 */
367	if ( mt->mt_nsuffix.bv_len > op->o_req_ndn.bv_len ) {
368		switch ( op->ors_scope ) {
369		case LDAP_SCOPE_SUBTREE:
370			/*
371			 * make the target suffix the new base
372			 * FIXME: this is very forgiving, because
373			 * "illegal" searchBases may be turned
374			 * into the suffix of the target; however,
375			 * the requested searchBase already passed
376			 * thru the candidate analyzer...
377			 */
378			if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) {
379				realbase = mt->mt_nsuffix;
380				if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
381					realscope = LDAP_SCOPE_SUBORDINATE;
382				}
383
384			} else {
385				/*
386				 * this target is no longer candidate
387				 */
388				retcode = META_SEARCH_NOT_CANDIDATE;
389				goto doreturn;
390			}
391			break;
392
393		case LDAP_SCOPE_SUBORDINATE:
394		case LDAP_SCOPE_ONELEVEL:
395		{
396			struct berval	rdn = mt->mt_nsuffix;
397			rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
398			if ( dnIsOneLevelRDN( &rdn )
399					&& dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) )
400			{
401				/*
402				 * if there is exactly one level,
403				 * make the target suffix the new
404				 * base, and make scope "base"
405				 */
406				realbase = mt->mt_nsuffix;
407				if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
408					if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
409						realscope = LDAP_SCOPE_SUBORDINATE;
410					} else {
411						realscope = LDAP_SCOPE_SUBTREE;
412					}
413				} else {
414					realscope = LDAP_SCOPE_BASE;
415				}
416				break;
417			} /* else continue with the next case */
418		}
419
420		case LDAP_SCOPE_BASE:
421			/*
422			 * this target is no longer candidate
423			 */
424			retcode = META_SEARCH_NOT_CANDIDATE;
425			goto doreturn;
426		}
427	}
428
429	/* check filter expression */
430	if ( mt->mt_filter ) {
431		metafilter_t *mf;
432		for ( mf = mt->mt_filter; mf; mf = mf->mf_next ) {
433			if ( regexec( &mf->mf_regex, op->ors_filterstr.bv_val, 0, NULL, 0 ) == 0 )
434				break;
435		}
436		/* nothing matched, this target is no longer a candidate */
437		if ( !mf ) {
438			retcode = META_SEARCH_NOT_CANDIDATE;
439			goto doreturn;
440		}
441	}
442
443	/*
444	 * Rewrite the search base, if required
445	 */
446	dc.op = op;
447	dc.target = mt;
448	dc.memctx = op->o_tmpmemctx;
449	dc.to_from = MASSAGE_REQ;
450	asyncmeta_dn_massage( &dc, &realbase, &mbase );
451
452	attrs = anlist2charray_x( op->ors_attrs, 0, op->o_tmpmemctx );
453
454	if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
455		timelimit = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
456	} else {
457		timelimit = -1;	/* no limit */
458	}
459
460#ifdef SLAPD_META_CLIENT_PR
461	save_ctrls = op->o_ctrls;
462	{
463		LDAPControl *pr_c = NULL;
464		int i = 0, nc = 0;
465
466		if ( save_ctrls ) {
467			for ( ; save_ctrls[i] != NULL; i++ );
468			nc = i;
469			pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, save_ctrls, NULL );
470		}
471
472		if ( pr_c != NULL ) nc--;
473		if ( mt->mt_ps > 0 || prcookie != NULL ) nc++;
474
475		if ( mt->mt_ps > 0 || prcookie != NULL || pr_c != NULL ) {
476			int src = 0, dst = 0;
477			BerElementBuffer berbuf;
478			BerElement *ber = (BerElement *)&berbuf;
479			struct berval val = BER_BVNULL;
480			ber_len_t len;
481
482			len = sizeof( LDAPControl * )*( nc + 1 ) + sizeof( LDAPControl );
483
484			if ( mt->mt_ps > 0 || prcookie != NULL ) {
485				struct berval nullcookie = BER_BVNULL;
486				ber_tag_t tag;
487
488				if ( prsize == 0 && mt->mt_ps > 0 ) prsize = mt->mt_ps;
489				if ( prcookie == NULL ) prcookie = &nullcookie;
490
491				ber_init2( ber, NULL, LBER_USE_DER );
492				tag = ber_printf( ber, "{iO}", prsize, prcookie );
493				if ( tag == LBER_ERROR ) {
494					/* error */
495					(void) ber_free_buf( ber );
496					goto done_pr;
497				}
498
499				tag = ber_flatten2( ber, &val, 0 );
500				if ( tag == LBER_ERROR ) {
501					/* error */
502					(void) ber_free_buf( ber );
503					goto done_pr;
504				}
505
506				len += val.bv_len + 1;
507			}
508
509			op->o_ctrls = op->o_tmpalloc( len, op->o_tmpmemctx );
510			if ( save_ctrls ) {
511				for ( ; save_ctrls[ src ] != NULL; src++ ) {
512					if ( save_ctrls[ src ] != pr_c ) {
513						op->o_ctrls[ dst ] = save_ctrls[ src ];
514						dst++;
515					}
516				}
517			}
518
519			if ( mt->mt_ps > 0 || prcookie != NULL ) {
520				op->o_ctrls[ dst ] = (LDAPControl *)&op->o_ctrls[ nc + 1 ];
521
522				op->o_ctrls[ dst ]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
523				op->o_ctrls[ dst ]->ldctl_iscritical = 1;
524
525				op->o_ctrls[ dst ]->ldctl_value.bv_val = (char *)&op->o_ctrls[ dst ][ 1 ];
526				AC_MEMCPY( op->o_ctrls[ dst ]->ldctl_value.bv_val, val.bv_val, val.bv_len + 1 );
527				op->o_ctrls[ dst ]->ldctl_value.bv_len = val.bv_len;
528				dst++;
529
530				(void)ber_free_buf( ber );
531			}
532
533			op->o_ctrls[ dst ] = NULL;
534		}
535done_pr:;
536	}
537#endif /* SLAPD_META_CLIENT_PR */
538
539	asyncmeta_set_msc_time(msc);
540	ctrls = op->o_ctrls;
541
542	if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls )
543		!= LDAP_SUCCESS )
544	{
545		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
546		retcode = META_SEARCH_NOT_CANDIDATE;
547		goto done;
548	}
549
550	/*
551	 * Starts the search
552	 */
553		/* someone reset the connection */
554	if (!( LDAP_BACK_CONN_ISBOUND( msc )
555	       || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
556		Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
557		goto error_unavailable;
558	}
559	rc = asyncmeta_int_filter2bv( &dc, op->ors_filter, &filterbv );
560	if ( rc ) {
561		retcode = META_SEARCH_ERR;
562		goto done;
563	}
564
565	ber = ldap_build_search_req( msc->msc_ld,
566			mbase.bv_val, realscope, filterbv.bv_val,
567			attrs, op->ors_attrsonly,
568			ctrls, NULL, timelimit, op->ors_slimit, op->ors_deref,
569			&msgid );
570	if (!ber) {
571		Debug( asyncmeta_debug, "%s asyncmeta_back_search_start: Operation encoding failed with errno %d\n",
572		       op->o_log_prefix, msc->msc_ld->ld_errno );
573		rs->sr_err = LDAP_OPERATIONS_ERROR;
574		rs->sr_text = "Failed to encode proxied request";
575		retcode = META_SEARCH_ERR;
576		goto done;
577	}
578
579	if (ber) {
580		struct timeval tv = {0, mt->mt_network_timeout*1000};
581
582		if (!( LDAP_BACK_CONN_ISBOUND( msc )
583		       || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
584			Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
585			goto error_unavailable;
586		}
587
588		ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
589		if (s < 0) {
590			Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
591			goto error_unavailable;
592		}
593
594		rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
595		if (rc < 0) {
596			Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
597			if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
598				rc = LDAP_SERVER_DOWN;
599			} else {
600				goto error_unavailable;
601			}
602		} else {
603			candidates[ candidate ].sr_msgid = msgid;
604			rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_SEARCH,
605							mbase.bv_val, ber, msgid );
606			if (rc == msgid)
607				rc = LDAP_SUCCESS;
608			else
609				rc = LDAP_SERVER_DOWN;
610			ber = NULL;
611		}
612
613		switch ( rc ) {
614		case LDAP_SUCCESS:
615			retcode = META_SEARCH_CANDIDATE;
616			asyncmeta_set_msc_time(msc);
617			goto done;
618
619		case LDAP_SERVER_DOWN:
620			/* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
621			if (do_lock > 0) {
622				ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
623				asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
624				ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
625			}
626			Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
627			goto error_unavailable;
628
629		default:
630			candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
631			retcode = META_SEARCH_NOT_CANDIDATE;
632			goto done;
633		}
634	}
635
636error_unavailable:
637	if (ber)
638		ber_free(ber, 1);
639	switch (bc->nretries[candidate]) {
640	case -1: /* nretries = forever */
641		retcode = META_SEARCH_NEED_BIND;
642		ldap_pvt_thread_yield();
643		break;
644	case 0: /* no retries left */
645		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
646		rs->sr_err = LDAP_UNAVAILABLE;
647		rs->sr_text = "Unable to send search request to target";
648		retcode = META_SEARCH_ERR;
649		break;
650	default: /* more retries left - try to rebind and go again */
651		retcode = META_SEARCH_NEED_BIND;
652		bc->nretries[candidate]--;
653		ldap_pvt_thread_yield();
654		break;
655	}
656done:;
657#if 0
658	(void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
659#endif
660#ifdef SLAPD_META_CLIENT_PR
661	if ( save_ctrls != op->o_ctrls ) {
662		op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
663		op->o_ctrls = save_ctrls;
664	}
665#endif /* SLAPD_META_CLIENT_PR */
666
667	if ( mbase.bv_val != realbase.bv_val ) {
668		op->o_tmpfree( mbase.bv_val, op->o_tmpmemctx );
669	}
670
671doreturn:;
672	Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_search_start[%p] (fd %d)=%d\n", op->o_log_prefix, msc, s, candidates[candidate].sr_msgid );
673	return retcode;
674}
675
676int
677asyncmeta_back_search( Operation *op, SlapReply *rs )
678{
679	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
680	time_t          timeout = 0;
681	int		rc = 0;
682	int		ncandidates = 0, initial_candidates = 0;
683	long		i;
684	SlapReply	*candidates = NULL;
685	void *thrctx = op->o_threadctx;
686	bm_context_t *bc;
687	a_metaconn_t *mc;
688	int msc_decr = 0;
689	int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
690	int check_bind = 0;
691
692	rs_assert_ready( rs );
693	rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
694
695	/*
696	 * controls are set in ldap_back_dobind()
697	 *
698	 * FIXME: in case of values return filter, we might want
699	 * to map attrs and maybe rewrite value
700	 */
701
702	asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
703	if (bc == NULL) {
704		rs->sr_err = LDAP_OTHER;
705		send_ldap_result(op, rs);
706		return rs->sr_err;
707	}
708
709	candidates = bc->candidates;
710	mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_DONTSEND, 0);
711	if ( !mc || rs->sr_err != LDAP_SUCCESS) {
712		send_ldap_result(op, rs);
713		return rs->sr_err;
714	}
715
716	/*
717	 * Inits searches
718	 */
719
720	for ( i = 0; i < mi->mi_ntargets; i++ ) {
721		/* reset sr_msgid; it is used in most loops
722		 * to check if that target is still to be considered */
723		candidates[i].sr_msgid = META_MSGID_UNDEFINED;
724		/* a target is marked as candidate by asyncmeta_getconn();
725		 * if for any reason (an error, it's over or so) it is
726		 * no longer active, sr_msgid is set to META_MSGID_IGNORE
727		 * but it remains candidate, which means it has been active
728		 * at some point during the operation.  This allows to
729		 * use its response code and more to compute the final
730		 * response */
731		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
732			continue;
733		}
734
735		candidates[ i ].sr_matched = NULL;
736		candidates[ i ].sr_text = NULL;
737		candidates[ i ].sr_ref = NULL;
738		candidates[ i ].sr_ctrls = NULL;
739		candidates[ i ].sr_nentries = 0;
740		candidates[ i ].sr_type = -1;
741
742		/* get largest timeout among candidates */
743		if ( mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ]
744			&& mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] > timeout )
745		{
746			timeout = mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ];
747		}
748	}
749
750	if ( op->ors_tlimit != SLAP_NO_LIMIT && (timeout == 0 || op->ors_tlimit < timeout)) {
751		bc->searchtime = 1;
752		bc->timeout = op->ors_tlimit;
753	} else {
754		bc->timeout = timeout;
755	}
756
757	bc->stoptime = op->o_time + bc->timeout;
758	bc->bc_active = 1;
759
760	if (mc->pending_ops >= max_pending_ops) {
761		rs->sr_err = LDAP_BUSY;
762		rs->sr_text = "Maximum pending ops limit exceeded";
763		send_ldap_result(op, rs);
764		return rs->sr_err;
765	}
766
767	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
768	rc = asyncmeta_add_message_queue(mc, bc);
769	for ( i = 0; i < mi->mi_ntargets; i++ ) {
770		mc->mc_conns[i].msc_active++;
771	}
772	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
773
774	if (rc != LDAP_SUCCESS) {
775		rs->sr_err = LDAP_BUSY;
776		rs->sr_text = "Maximum pending ops limit exceeded";
777		send_ldap_result(op, rs);
778		goto finish;
779	}
780
781	for ( i = 0; i < mi->mi_ntargets; i++ ) {
782		if ( !META_IS_CANDIDATE( &candidates[ i ] )
783			|| candidates[ i ].sr_err != LDAP_SUCCESS )
784		{
785			continue;
786		}
787retry:
788		if (bc->timeout && bc->stoptime < slap_get_time() && META_BACK_ONERR_STOP( mi )) {
789			int		timeout_err;
790			const char *timeout_text;
791			if (bc->searchtime) {
792				timeout_err = LDAP_TIMELIMIT_EXCEEDED;
793				timeout_text = NULL;
794			} else {
795				timeout_err = op->o_protocol >= LDAP_VERSION3 ?
796					LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
797				timeout_text = "Operation timed out before it was sent to target";
798			}
799			rs->sr_err = timeout_err;
800			rs->sr_text = timeout_text;
801			asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
802			goto finish;
803
804		}
805
806		if (op->o_abandon) {
807			rs->sr_err = SLAPD_ABANDON;
808			asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
809			goto finish;
810		}
811
812		rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, i);
813		switch (rc)
814		{
815		case META_SEARCH_CANDIDATE:
816			/* target is already bound, just send the search request */
817			ncandidates++;
818			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: IS_CANDIDATE "
819			       "cnd=\"%ld\"\n", op->o_log_prefix, i );
820
821			rc = asyncmeta_back_search_start( op, rs, mc, bc, i,  NULL, 0 , 1);
822			if (rc == META_SEARCH_ERR) {
823				META_CANDIDATE_CLEAR(&candidates[i]);
824				candidates[ i ].sr_msgid = META_MSGID_IGNORE;
825				if ( META_BACK_ONERR_STOP( mi ) ) {
826					asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
827					goto finish;
828				}
829				else {
830					continue;
831				}
832			} else if (rc == META_SEARCH_NEED_BIND) {
833				goto retry;
834			}
835			break;
836		case META_SEARCH_NOT_CANDIDATE:
837			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NOT_CANDIDATE "
838			       "cnd=\"%ld\"\n", op->o_log_prefix, i );
839			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
840			break;
841
842		case META_SEARCH_NEED_BIND:
843		case META_SEARCH_BINDING:
844			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: BINDING "
845			       "cnd=\"%ld\" mc %p msc %p\n", op->o_log_prefix, i , mc, &mc->mc_conns[i]);
846			check_bind++;
847			ncandidates++;
848			/* Todo add the context to the message queue but do not send the request
849			 the receiver must send this when we are done binding */
850			/* question - how would do receiver know to which targets??? */
851			break;
852
853		case META_SEARCH_ERR:
854			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: SEARCH_ERR "
855			       "cnd=\"%ldd\"\n", op->o_log_prefix, i );
856			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
857			candidates[ i ].sr_type = REP_RESULT;
858
859			if ( META_BACK_ONERR_STOP( mi ) ) {
860				asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
861				goto finish;
862			}
863			else {
864				continue;
865			}
866			break;
867
868		default:
869			assert( 0 );
870			break;
871		}
872	}
873
874	initial_candidates = ncandidates;
875
876	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
877		char	cnd[ SLAP_TEXT_BUFLEN ];
878		int	c;
879
880		for ( c = 0; c < mi->mi_ntargets; c++ ) {
881			if ( META_IS_CANDIDATE( &candidates[ c ] ) ) {
882				cnd[ c ] = '*';
883			} else {
884				cnd[ c ] = ' ';
885			}
886		}
887		cnd[ c ] = '\0';
888
889		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: ncandidates=%d "
890			"cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
891	}
892
893	if ( initial_candidates == 0 ) {
894		/* NOTE: here we are not sending any matchedDN;
895		 * this is intended, because if the back-meta
896		 * is serving this search request, but no valid
897		 * candidate could be looked up, it means that
898		 * there is a hole in the mapping of the targets
899		 * and thus no knowledge of any remote superior
900		 * is available */
901		Debug( LDAP_DEBUG_ANY, "%s asyncmeta_back_search: "
902			"base=\"%s\" scope=%d: "
903			"no candidate could be selected\n",
904			op->o_log_prefix, op->o_req_dn.bv_val,
905			op->ors_scope );
906
907		/* FIXME: we're sending the first error we encounter;
908		 * maybe we should pick the worst... */
909		rc = LDAP_NO_SUCH_OBJECT;
910		for ( i = 0; i < mi->mi_ntargets; i++ ) {
911			if ( META_IS_CANDIDATE( &candidates[ i ] )
912				&& candidates[ i ].sr_err != LDAP_SUCCESS )
913			{
914				rc = candidates[ i ].sr_err;
915				break;
916			}
917		}
918		rs->sr_err = rc;
919		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
920		asyncmeta_drop_bc(mc, bc);
921		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
922		send_ldap_result(op, rs);
923		goto finish;
924	}
925
926	/* If we were processing many targets the result from a pending Bind
927	 * on an earlier target may have arrived while we were sending to a
928	 * later target. See if we can now send our pending request.
929	 */
930	if ( check_bind ) {
931		for ( i = 0; i < mi->mi_ntargets; i++ ) {
932			if ( candidates[ i ].sr_msgid == META_MSGID_GOT_BIND ) {
933				rc = asyncmeta_back_search_start( op, rs, mc, bc, i, NULL, 0, 1 );
934				if ( rc == META_SEARCH_ERR ) {
935					META_CANDIDATE_CLEAR( &candidates[i] );
936					candidates[ i ].sr_msgid = META_MSGID_IGNORE;
937					if ( META_BACK_ONERR_STOP( mi ) ) {
938						asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
939						goto finish;
940					}
941				}
942			}
943		}
944	}
945
946	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
947	for ( i = 0; i < mi->mi_ntargets; i++ ) {
948		mc->mc_conns[i].msc_active--;
949	}
950	msc_decr = 1;
951
952	asyncmeta_start_listeners(mc, candidates, bc);
953	bc->bc_active--;
954	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
955	rs->sr_err = SLAPD_ASYNCOP;
956
957finish:
958	/* we ended up straight here due to error and need to reset the msc_active*/
959	if (msc_decr == 0) {
960		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
961		for ( i = 0; i < mi->mi_ntargets; i++ ) {
962			mc->mc_conns[i].msc_active--;
963		}
964		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
965	}
966	return rs->sr_err;
967}
968