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