xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-asyncmeta/search.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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 
42 static void
asyncmeta_handle_onerr_stop(Operation * op,SlapReply * rs,a_metaconn_t * mc,bm_context_t * bc,int candidate)43 asyncmeta_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 
70 static int
asyncmeta_int_filter2bv(a_dncookie * dc,Filter * f,struct berval * fstr)71 asyncmeta_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 }
316 meta_search_candidate_t
asyncmeta_back_search_start(Operation * op,SlapReply * rs,a_metaconn_t * mc,bm_context_t * bc,int candidate,struct berval * prcookie,ber_int_t prsize,int do_lock)317 asyncmeta_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 		}
535 done_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 
636 error_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 	}
656 done:;
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 
671 doreturn:;
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 
676 int
asyncmeta_back_search(Operation * op,SlapReply * rs)677 asyncmeta_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 		}
787 retry:
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 
957 finish:
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