1 /* search.c - search request handler for back-asyncmeta */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2016-2018 The OpenLDAP Foundation.
6 * Portions Copyright 2016 Symas Corporation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
19 * This work was developed by Symas Corporation
20 * based on back-meta module for inclusion in OpenLDAP Software.
21 * This work was sponsored by Ericsson. */
27 #include <ac/socket.h>
28 #include <ac/string.h>
33 #include "../back-ldap/back-ldap.h"
34 #include "back-asyncmeta.h"
35 #include "../../../libraries/liblber/lber-int.h"
37 #include "../../../libraries/libldap/ldap-int.h"
39 #define ldap_debug slap_debug
42 asyncmeta_handle_onerr_stop(Operation *op,
49 a_metainfo_t *mi = mc->mc_info;
51 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
52 if (bc->bc_active > 0) {
53 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
57 asyncmeta_drop_bc(mc, bc);
58 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
60 for (j=0; j<mi->mi_ntargets; j++) {
61 if (j != candidate && bc->candidates[j].sr_msgid >= 0
62 && mc->mc_conns[j].msc_ld != NULL) {
63 asyncmeta_back_abandon_candidate( mc, op,
64 bc->candidates[ j ].sr_msgid, j );
70 send_ldap_result(op, rs);
71 asyncmeta_clear_bm_context(bc);
74 meta_search_candidate_t
75 asyncmeta_back_search_start(
81 struct berval *prcookie,
84 SlapReply *candidates = bc->candidates;
85 a_metainfo_t *mi = ( a_metainfo_t * )mc->mc_info;
86 a_metatarget_t *mt = mi->mi_targets[ candidate ];
87 a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
89 struct berval realbase = op->o_req_dn;
90 int realscope = op->ors_scope;
91 struct berval mbase = BER_BVNULL;
92 struct berval mfilter = BER_BVNULL;
93 char **mapped_attrs = NULL;
95 meta_search_candidate_t retcode;
98 LDAPControl **ctrls = NULL;
101 #ifdef SLAPD_META_CLIENT_PR
102 LDAPControl **save_ctrls = NULL;
103 #endif /* SLAPD_META_CLIENT_PR */
105 /* this should not happen; just in case... */
106 if ( msc->msc_ld == NULL ) {
107 Debug( LDAP_DEBUG_ANY,
108 "%s: asyncmeta_back_search_start candidate=%d ld=NULL%s.\n",
109 op->o_log_prefix, candidate,
110 META_BACK_ONERR_STOP( mi ) ? "" : " (ignored)" );
111 candidates[ candidate ].sr_err = LDAP_OTHER;
112 if ( META_BACK_ONERR_STOP( mi ) ) {
113 return META_SEARCH_ERR;
115 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
116 return META_SEARCH_NOT_CANDIDATE;
119 Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_back_search_start[%d]\n", op->o_log_prefix, candidate, 0 );
121 * modifies the base according to the scope, if required
123 if ( mt->mt_nsuffix.bv_len > op->o_req_ndn.bv_len ) {
124 switch ( op->ors_scope ) {
125 case LDAP_SCOPE_SUBTREE:
127 * make the target suffix the new base
128 * FIXME: this is very forgiving, because
129 * "illegal" searchBases may be turned
130 * into the suffix of the target; however,
131 * the requested searchBase already passed
132 * thru the candidate analyzer...
134 if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) {
135 realbase = mt->mt_nsuffix;
136 if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
137 realscope = LDAP_SCOPE_SUBORDINATE;
142 * this target is no longer candidate
144 retcode = META_SEARCH_NOT_CANDIDATE;
149 case LDAP_SCOPE_SUBORDINATE:
150 case LDAP_SCOPE_ONELEVEL:
152 struct berval rdn = mt->mt_nsuffix;
153 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
154 if ( dnIsOneLevelRDN( &rdn )
155 && dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) )
158 * if there is exactly one level,
159 * make the target suffix the new
160 * base, and make scope "base"
162 realbase = mt->mt_nsuffix;
163 if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
164 if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
165 realscope = LDAP_SCOPE_SUBORDINATE;
167 realscope = LDAP_SCOPE_SUBTREE;
170 realscope = LDAP_SCOPE_BASE;
173 } /* else continue with the next case */
176 case LDAP_SCOPE_BASE:
178 * this target is no longer candidate
180 retcode = META_SEARCH_NOT_CANDIDATE;
185 /* check filter expression */
186 if ( mt->mt_filter ) {
188 for ( mf = mt->mt_filter; mf; mf = mf->mf_next ) {
189 if ( regexec( &mf->mf_regex, op->ors_filterstr.bv_val, 0, NULL, 0 ) == 0 )
192 /* nothing matched, this target is no longer a candidate */
194 retcode = META_SEARCH_NOT_CANDIDATE;
200 * Rewrite the search base, if required
203 dc.ctx = "searchBase";
204 dc.conn = op->o_conn;
206 switch ( asyncmeta_dn_massage( &dc, &realbase, &mbase ) ) {
210 case LDAP_UNWILLING_TO_PERFORM:
211 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
212 rs->sr_text = "Operation not allowed";
213 retcode = META_SEARCH_ERR;
219 * this target is no longer candidate
221 retcode = META_SEARCH_NOT_CANDIDATE;
228 rc = asyncmeta_filter_map_rewrite( &dc, op->ors_filter,
229 &mfilter, BACKLDAP_MAP, NULL );
234 case LDAP_COMPARE_FALSE:
237 * this target is no longer candidate
239 retcode = META_SEARCH_NOT_CANDIDATE;
244 * Maps required attributes
246 rc = asyncmeta_map_attrs( op, &mt->mt_rwmap.rwm_at,
247 op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
248 if ( rc != LDAP_SUCCESS ) {
250 * this target is no longer candidate
252 retcode = META_SEARCH_NOT_CANDIDATE;
256 if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
257 timelimit = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
259 timelimit = -1; /* no limit */
262 #ifdef SLAPD_META_CLIENT_PR
263 save_ctrls = op->o_ctrls;
265 LDAPControl *pr_c = NULL;
269 for ( ; save_ctrls[i] != NULL; i++ );
271 pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, save_ctrls, NULL );
274 if ( pr_c != NULL ) nc--;
275 if ( mt->mt_ps > 0 || prcookie != NULL ) nc++;
277 if ( mt->mt_ps > 0 || prcookie != NULL || pr_c != NULL ) {
278 int src = 0, dst = 0;
279 BerElementBuffer berbuf;
280 BerElement *ber = (BerElement *)&berbuf;
281 struct berval val = BER_BVNULL;
284 len = sizeof( LDAPControl * )*( nc + 1 ) + sizeof( LDAPControl );
286 if ( mt->mt_ps > 0 || prcookie != NULL ) {
287 struct berval nullcookie = BER_BVNULL;
290 if ( prsize == 0 && mt->mt_ps > 0 ) prsize = mt->mt_ps;
291 if ( prcookie == NULL ) prcookie = &nullcookie;
293 ber_init2( ber, NULL, LBER_USE_DER );
294 tag = ber_printf( ber, "{iO}", prsize, prcookie );
295 if ( tag == LBER_ERROR ) {
297 (void) ber_free_buf( ber );
301 tag = ber_flatten2( ber, &val, 0 );
302 if ( tag == LBER_ERROR ) {
304 (void) ber_free_buf( ber );
308 len += val.bv_len + 1;
311 op->o_ctrls = op->o_tmpalloc( len, op->o_tmpmemctx );
313 for ( ; save_ctrls[ src ] != NULL; src++ ) {
314 if ( save_ctrls[ src ] != pr_c ) {
315 op->o_ctrls[ dst ] = save_ctrls[ src ];
321 if ( mt->mt_ps > 0 || prcookie != NULL ) {
322 op->o_ctrls[ dst ] = (LDAPControl *)&op->o_ctrls[ nc + 1 ];
324 op->o_ctrls[ dst ]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
325 op->o_ctrls[ dst ]->ldctl_iscritical = 1;
327 op->o_ctrls[ dst ]->ldctl_value.bv_val = (char *)&op->o_ctrls[ dst ][ 1 ];
328 AC_MEMCPY( op->o_ctrls[ dst ]->ldctl_value.bv_val, val.bv_val, val.bv_len + 1 );
329 op->o_ctrls[ dst ]->ldctl_value.bv_len = val.bv_len;
332 (void)ber_free_buf( ber );
335 op->o_ctrls[ dst ] = NULL;
339 #endif /* SLAPD_META_CLIENT_PR */
342 asyncmeta_set_msc_time(msc);
346 if (rc != LDAP_SUCCESS)
348 rs->sr_err = LDAP_BUSY;
349 retcode = META_SEARCH_ERR;
350 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
355 if ( asyncmeta_controls_add( op, rs, mc, candidate, &ctrls )
358 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
359 retcode = META_SEARCH_NOT_CANDIDATE;
366 ber = ldap_build_search_req( msc->msc_ld,
367 mbase.bv_val, realscope, mfilter.bv_val,
368 mapped_attrs, op->ors_attrsonly,
369 ctrls, NULL, timelimit, op->ors_slimit, op->ors_deref,
372 candidates[ candidate ].sr_msgid = msgid;
373 rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_SEARCH,
374 mbase.bv_val, ber, msgid );
378 rc = LDAP_SERVER_DOWN;
381 retcode = META_SEARCH_CANDIDATE;
382 asyncmeta_set_msc_time(msc);
385 case LDAP_SERVER_DOWN:
386 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
387 if (mc->mc_active < 1) {
388 asyncmeta_clear_one_msc(NULL, mc, candidate);
390 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
391 if ( nretries && asyncmeta_retry( op, rs, &mc, candidate, LDAP_BACK_DONTSEND ) ) {
393 /* if the identity changed, there might be need to re-authz */
394 (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
397 rs->sr_err = LDAP_UNAVAILABLE;
398 retcode = META_SEARCH_ERR;
401 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
402 retcode = META_SEARCH_NOT_CANDIDATE;
407 (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
408 #ifdef SLAPD_META_CLIENT_PR
409 if ( save_ctrls != op->o_ctrls ) {
410 op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
411 op->o_ctrls = save_ctrls;
413 #endif /* SLAPD_META_CLIENT_PR */
415 if ( mapped_attrs ) {
416 ber_memfree_x( mapped_attrs, op->o_tmpmemctx );
418 if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
419 ber_memfree_x( mfilter.bv_val, NULL );
421 if ( mbase.bv_val != realbase.bv_val ) {
422 free( mbase.bv_val );
426 Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_search_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
431 asyncmeta_back_search( Operation *op, SlapReply *rs )
433 a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
434 struct timeval save_tv = { 0, 0 },
436 time_t stoptime = (time_t)(-1),
437 lastres_time = slap_get_time(),
439 int rc = 0, sres = LDAP_SUCCESS;
440 char *matched = NULL;
441 int last = 0, ncandidates = 0,
442 initial_candidates = 0, candidate_match = 0,
444 ldap_back_send_t sendok = LDAP_BACK_SENDERR;
448 SlapReply *candidates = NULL;
452 slap_callback *cb = op->o_callback;
454 rs_assert_ready( rs );
455 rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
458 * controls are set in ldap_back_dobind()
460 * FIXME: in case of values return filter, we might want
461 * to map attrs and maybe rewrite value
464 asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
466 rs->sr_err = LDAP_OTHER;
467 send_ldap_result(op, rs);
471 candidates = bc->candidates;
472 mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_DONTSEND, 0);
473 if ( !mc || rs->sr_err != LDAP_SUCCESS) {
475 send_ldap_result(op, rs);
476 asyncmeta_clear_bm_context(bc);
484 for ( i = 0; i < mi->mi_ntargets; i++ ) {
485 /* reset sr_msgid; it is used in most loops
486 * to check if that target is still to be considered */
487 candidates[i].sr_msgid = META_MSGID_UNDEFINED;
488 /* a target is marked as candidate by asyncmeta_getconn();
489 * if for any reason (an error, it's over or so) it is
490 * no longer active, sr_msgid is set to META_MSGID_IGNORE
491 * but it remains candidate, which means it has been active
492 * at some point during the operation. This allows to
493 * use its response code and more to compute the final
495 if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
499 candidates[ i ].sr_matched = NULL;
500 candidates[ i ].sr_text = NULL;
501 candidates[ i ].sr_ref = NULL;
502 candidates[ i ].sr_ctrls = NULL;
503 candidates[ i ].sr_nentries = 0;
504 candidates[ i ].sr_type = -1;
506 /* get largest timeout among candidates */
507 if ( mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ]
508 && mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] > timeout )
510 timeout = mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ];
514 bc->timeout = timeout;
515 bc->stoptime = op->o_time + bc->timeout;
517 if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
518 stoptime = op->o_time + op->ors_tlimit;
519 if (stoptime < bc->stoptime) {
520 bc->stoptime = stoptime;
522 bc->timeout = op->ors_tlimit;
526 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
527 rc = asyncmeta_add_message_queue(mc, bc);
528 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
530 if (rc != LDAP_SUCCESS) {
531 rs->sr_err = LDAP_BUSY;
532 rs->sr_text = "Maximum pending ops limit exceeded";
533 asyncmeta_clear_bm_context(bc);
535 send_ldap_result(op, rs);
539 for ( i = 0; i < mi->mi_ntargets; i++ ) {
540 if ( !META_IS_CANDIDATE( &candidates[ i ] )
541 || candidates[ i ].sr_err != LDAP_SUCCESS )
546 rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, i);
549 case META_SEARCH_CANDIDATE:
550 /* target is already bound, just send the search request */
552 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: IS_CANDIDATE "
553 "cnd=\"%ld\"\n", op->o_log_prefix, i , 0);
555 rc = asyncmeta_back_search_start( op, rs, mc, bc, i, NULL, 0 );
556 if (rc == META_SEARCH_ERR) {
557 META_CANDIDATE_CLEAR(&candidates[i]);
558 candidates[ i ].sr_msgid = META_MSGID_IGNORE;
559 if ( META_BACK_ONERR_STOP( mi ) ) {
560 asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
568 case META_SEARCH_NOT_CANDIDATE:
569 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NOT_CANDIDATE "
570 "cnd=\"%ld\"\n", op->o_log_prefix, i , 0);
571 candidates[ i ].sr_msgid = META_MSGID_IGNORE;
574 case META_SEARCH_NEED_BIND:
575 case META_SEARCH_CONNECTING:
576 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NEED_BIND "
577 "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]);
579 rc = asyncmeta_dobind_init(op, rs, bc, mc, i);
580 if (rc == META_SEARCH_ERR) {
581 candidates[ i ].sr_msgid = META_MSGID_IGNORE;
582 if ( META_BACK_ONERR_STOP( mi ) ) {
583 asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
591 case META_SEARCH_BINDING:
592 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: BINDING "
593 "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]);
595 /* Todo add the context to the message queue but do not send the request
596 the receiver must send this when we are done binding */
597 /* question - how would do receiver know to which targets??? */
600 case META_SEARCH_ERR:
601 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: SEARCH_ERR "
602 "cnd=\"%ldd\"\n", op->o_log_prefix, i , 0);
603 candidates[ i ].sr_msgid = META_MSGID_IGNORE;
604 candidates[ i ].sr_type = REP_RESULT;
606 if ( META_BACK_ONERR_STOP( mi ) ) {
607 asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
621 initial_candidates = ncandidates;
623 if ( LogTest( LDAP_DEBUG_TRACE ) ) {
624 char cnd[ SLAP_TEXT_BUFLEN ];
627 for ( c = 0; c < mi->mi_ntargets; c++ ) {
628 if ( META_IS_CANDIDATE( &candidates[ c ] ) ) {
636 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: ncandidates=%d "
637 "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
640 if ( initial_candidates == 0 ) {
641 /* NOTE: here we are not sending any matchedDN;
642 * this is intended, because if the back-meta
643 * is serving this search request, but no valid
644 * candidate could be looked up, it means that
645 * there is a hole in the mapping of the targets
646 * and thus no knowledge of any remote superior
648 Debug( LDAP_DEBUG_ANY, "%s asyncmeta_back_search: "
649 "base=\"%s\" scope=%d: "
650 "no candidate could be selected\n",
651 op->o_log_prefix, op->o_req_dn.bv_val,
654 /* FIXME: we're sending the first error we encounter;
655 * maybe we should pick the worst... */
656 rc = LDAP_NO_SUCH_OBJECT;
657 for ( i = 0; i < mi->mi_ntargets; i++ ) {
658 if ( META_IS_CANDIDATE( &candidates[ i ] )
659 && candidates[ i ].sr_err != LDAP_SUCCESS )
661 rc = candidates[ i ].sr_err;
666 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
667 asyncmeta_drop_bc(mc, bc);
668 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
670 send_ldap_result(op, rs);
671 asyncmeta_clear_bm_context(bc);
674 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
675 asyncmeta_start_listeners(mc, candidates, bc);
676 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);