summaryrefslogtreecommitdiffstats
path: root/servers/slapd/back-asyncmeta/search.c
diff options
context:
space:
mode:
Diffstat (limited to 'servers/slapd/back-asyncmeta/search.c')
-rw-r--r--servers/slapd/back-asyncmeta/search.c963
1 files changed, 963 insertions, 0 deletions
diff --git a/servers/slapd/back-asyncmeta/search.c b/servers/slapd/back-asyncmeta/search.c
new file mode 100644
index 0000000..0b0db82
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/search.c
@@ -0,0 +1,963 @@
+/* search.c - search request handler for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include "slap.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "../../../libraries/libldap/ldap-int.h"
+#include "lutil.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+static void
+asyncmeta_handle_onerr_stop(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ int j;
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ if (asyncmeta_bc_in_queue(mc,bc) == NULL || bc->bc_active > 1) {
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ return;
+ }
+ asyncmeta_drop_bc(mc, bc);
+ for (j=0; j<mi->mi_ntargets; j++) {
+ if (j != candidate && bc->candidates[j].sr_msgid >= 0
+ && mc->mc_conns[j].msc_ld != NULL && !META_BACK_CONN_CREATING( &mc->mc_conns[j] )) {
+ asyncmeta_back_cancel( mc, op,
+ bc->candidates[ j ].sr_msgid, j );
+ }
+ }
+ slap_sl_mem_setctx(op->o_threadctx, op->o_tmpmemctx);
+ operation_counter_init( op, op->o_threadctx );
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ send_ldap_result(op, rs);
+}
+
+static int
+asyncmeta_int_filter2bv( a_dncookie *dc,
+ Filter *f,
+ struct berval *fstr )
+{
+ int i;
+ Filter *p;
+ struct berval atmp,
+ vtmp,
+ ntmp,
+ *tmp;
+ static struct berval
+ /* better than nothing... */
+ ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
+ ber_bvtf_false = BER_BVC( "(|)" ),
+ /* better than nothing... */
+ ber_bvtrue = BER_BVC( "(objectClass=*)" ),
+ ber_bvtf_true = BER_BVC( "(&)" ),
+ ber_bverror = BER_BVC( "(?=error)" ),
+ ber_bvunknown = BER_BVC( "(?=unknown)" ),
+ ber_bvnone = BER_BVC( "(?=none)" );
+ ber_len_t len;
+ void *memctx = dc->memctx;
+
+ assert( fstr != NULL );
+ BER_BVZERO( fstr );
+
+ if ( f == NULL ) {
+ ber_dupbv_x( fstr, &ber_bvnone, memctx );
+ return LDAP_OTHER;
+ }
+
+ switch ( ( f->f_choice & SLAPD_FILTER_MASK ) ) {
+ case LDAP_FILTER_EQUALITY:
+ if ( f->f_av_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
+ asyncmeta_dn_massage( dc, &f->f_av_value, &vtmp );
+ } else {
+ vtmp = f->f_av_value;
+ }
+
+ filter_escape_value_x( &vtmp, &ntmp, memctx );
+ fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+ + ( sizeof("(=)") - 1 );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
+ f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_GE:
+ filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+ fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+ + ( sizeof("(>=)") - 1 );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
+ f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_LE:
+ filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+ fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+ + ( sizeof("(<=)") - 1 );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
+ f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+ fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+ + ( sizeof("(~=)") - 1 );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
+ f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ fstr->bv_len = f->f_sub_desc->ad_cname.bv_len + ( STRLENOF( "(=*)" ) );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 128, memctx ); /* FIXME: why 128 ? */
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ f->f_sub_desc->ad_cname.bv_val );
+
+ if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_initial, &ntmp, memctx );
+
+ fstr->bv_len += ntmp.bv_len;
+ fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+ snprintf( &fstr->bv_val[len - 2], ntmp.bv_len + 3,
+ /* "(attr=" */ "%s*)",
+ ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ }
+
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
+ len = fstr->bv_len;
+ filter_escape_value_x( &f->f_sub_any[i], &ntmp, memctx );
+
+ fstr->bv_len += ntmp.bv_len + 1;
+ fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+ snprintf( &fstr->bv_val[len - 1], ntmp.bv_len + 3,
+ /* "(attr=[init]*[any*]" */ "%s*)",
+ ntmp.bv_len ? ntmp.bv_val : "" );
+ ber_memfree_x( ntmp.bv_val, memctx );
+ }
+ }
+
+ if ( !BER_BVISNULL( &f->f_sub_final ) ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_final, &ntmp, memctx );
+
+ fstr->bv_len += ntmp.bv_len;
+ fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+ snprintf( &fstr->bv_val[len - 1], ntmp.bv_len + 3,
+ /* "(attr=[init*][any*]" */ "%s)",
+ ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ fstr->bv_len = f->f_desc->ad_cname.bv_len + ( STRLENOF( "(=*)" ) );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ f->f_desc->ad_cname.bv_val );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ fstr->bv_len = STRLENOF( "(%)" );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 128, memctx ); /* FIXME: why 128? */
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
+ f->f_choice == LDAP_FILTER_AND ? '&' :
+ f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ int rc;
+
+ len = fstr->bv_len;
+
+ rc = asyncmeta_int_filter2bv( dc, p, &vtmp );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+ snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
+ /*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_EXT:
+ if ( f->f_mr_desc ) {
+ atmp = f->f_mr_desc->ad_cname;
+
+ } else {
+ BER_BVSTR( &atmp, "" );
+ }
+ filter_escape_value_x( &f->f_mr_value, &ntmp, memctx );
+
+ /* FIXME: cleanup (less ?: operators...) */
+ fstr->bv_len = atmp.bv_len +
+ ( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
+ ( !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
+ ntmp.bv_len + ( STRLENOF( "(:=)" ) );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
+ atmp.bv_val,
+ f->f_mr_dnattrs ? ":dn" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
+ ntmp.bv_len ? ntmp.bv_val : "" );
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case SLAPD_FILTER_COMPUTED:
+ switch ( f->f_result ) {
+ /* FIXME: treat UNDEFINED as FALSE */
+ case SLAPD_COMPARE_UNDEFINED:
+ if ( META_BACK_TGT_NOUNDEFFILTER( dc->target ) ) {
+ return LDAP_COMPARE_FALSE;
+ }
+ /* fallthru */
+
+ case LDAP_COMPARE_FALSE:
+ if ( META_BACK_TGT_T_F( dc->target ) ) {
+ tmp = &ber_bvtf_false;
+ break;
+ }
+ tmp = &ber_bvfalse;
+ break;
+
+ case LDAP_COMPARE_TRUE:
+ if ( META_BACK_TGT_T_F( dc->target ) ) {
+ tmp = &ber_bvtf_true;
+ break;
+ }
+
+ tmp = &ber_bvtrue;
+ break;
+
+ default:
+ tmp = &ber_bverror;
+ break;
+ }
+
+ ber_dupbv_x( fstr, tmp, memctx );
+ break;
+
+ default:
+ ber_dupbv_x( fstr, &ber_bvunknown, memctx );
+ break;
+ }
+
+ return 0;
+}
+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)
+{
+ SlapReply *candidates = bc->candidates;
+ a_metainfo_t *mi = ( a_metainfo_t * )mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ a_dncookie dc;
+ struct berval realbase = op->o_req_dn;
+ char **attrs;
+ int realscope = op->ors_scope;
+ struct berval mbase = BER_BVNULL;
+ int rc;
+ struct berval filterbv = BER_BVNULL;
+ meta_search_candidate_t retcode;
+ int timelimit;
+ LDAPControl **ctrls = NULL;
+ BerElement *ber = NULL;
+ ber_int_t msgid;
+ ber_socket_t s = -1;
+#ifdef SLAPD_META_CLIENT_PR
+ LDAPControl **save_ctrls = NULL;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ /* this should not happen; just in case... */
+ if ( msc->msc_ld == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: asyncmeta_back_search_start candidate=%d ld=NULL%s.\n",
+ op->o_log_prefix, candidate,
+ META_BACK_ONERR_STOP( mi ) ? "" : " (ignored)" );
+ candidates[ candidate ].sr_err = LDAP_OTHER;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ return META_SEARCH_ERR;
+ }
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ return META_SEARCH_NOT_CANDIDATE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_back_search_start: dn=%s filter=%s\n",
+ op->o_log_prefix, op->o_req_dn.bv_val, op->ors_filterstr.bv_val );
+ /*
+ * modifies the base according to the scope, if required
+ */
+ if ( mt->mt_nsuffix.bv_len > op->o_req_ndn.bv_len ) {
+ switch ( op->ors_scope ) {
+ case LDAP_SCOPE_SUBTREE:
+ /*
+ * make the target suffix the new base
+ * FIXME: this is very forgiving, because
+ * "illegal" searchBases may be turned
+ * into the suffix of the target; however,
+ * the requested searchBase already passed
+ * thru the candidate analyzer...
+ */
+ if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) {
+ realbase = mt->mt_nsuffix;
+ if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+ realscope = LDAP_SCOPE_SUBORDINATE;
+ }
+
+ } else {
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ break;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ case LDAP_SCOPE_ONELEVEL:
+ {
+ struct berval rdn = mt->mt_nsuffix;
+ rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
+ if ( dnIsOneLevelRDN( &rdn )
+ && dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) )
+ {
+ /*
+ * if there is exactly one level,
+ * make the target suffix the new
+ * base, and make scope "base"
+ */
+ realbase = mt->mt_nsuffix;
+ if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
+ if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+ realscope = LDAP_SCOPE_SUBORDINATE;
+ } else {
+ realscope = LDAP_SCOPE_SUBTREE;
+ }
+ } else {
+ realscope = LDAP_SCOPE_BASE;
+ }
+ break;
+ } /* else continue with the next case */
+ }
+
+ case LDAP_SCOPE_BASE:
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ }
+
+ /* check filter expression */
+ if ( mt->mt_filter ) {
+ metafilter_t *mf;
+ for ( mf = mt->mt_filter; mf; mf = mf->mf_next ) {
+ if ( regexec( &mf->mf_regex, op->ors_filterstr.bv_val, 0, NULL, 0 ) == 0 )
+ break;
+ }
+ /* nothing matched, this target is no longer a candidate */
+ if ( !mf ) {
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ }
+
+ /*
+ * Rewrite the search base, if required
+ */
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REQ;
+ asyncmeta_dn_massage( &dc, &realbase, &mbase );
+
+ attrs = anlist2charray_x( op->ors_attrs, 0, op->o_tmpmemctx );
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
+ timelimit = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
+ } else {
+ timelimit = -1; /* no limit */
+ }
+
+#ifdef SLAPD_META_CLIENT_PR
+ save_ctrls = op->o_ctrls;
+ {
+ LDAPControl *pr_c = NULL;
+ int i = 0, nc = 0;
+
+ if ( save_ctrls ) {
+ for ( ; save_ctrls[i] != NULL; i++ );
+ nc = i;
+ pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, save_ctrls, NULL );
+ }
+
+ if ( pr_c != NULL ) nc--;
+ if ( mt->mt_ps > 0 || prcookie != NULL ) nc++;
+
+ if ( mt->mt_ps > 0 || prcookie != NULL || pr_c != NULL ) {
+ int src = 0, dst = 0;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval val = BER_BVNULL;
+ ber_len_t len;
+
+ len = sizeof( LDAPControl * )*( nc + 1 ) + sizeof( LDAPControl );
+
+ if ( mt->mt_ps > 0 || prcookie != NULL ) {
+ struct berval nullcookie = BER_BVNULL;
+ ber_tag_t tag;
+
+ if ( prsize == 0 && mt->mt_ps > 0 ) prsize = mt->mt_ps;
+ if ( prcookie == NULL ) prcookie = &nullcookie;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ tag = ber_printf( ber, "{iO}", prsize, prcookie );
+ if ( tag == LBER_ERROR ) {
+ /* error */
+ (void) ber_free_buf( ber );
+ goto done_pr;
+ }
+
+ tag = ber_flatten2( ber, &val, 0 );
+ if ( tag == LBER_ERROR ) {
+ /* error */
+ (void) ber_free_buf( ber );
+ goto done_pr;
+ }
+
+ len += val.bv_len + 1;
+ }
+
+ op->o_ctrls = op->o_tmpalloc( len, op->o_tmpmemctx );
+ if ( save_ctrls ) {
+ for ( ; save_ctrls[ src ] != NULL; src++ ) {
+ if ( save_ctrls[ src ] != pr_c ) {
+ op->o_ctrls[ dst ] = save_ctrls[ src ];
+ dst++;
+ }
+ }
+ }
+
+ if ( mt->mt_ps > 0 || prcookie != NULL ) {
+ op->o_ctrls[ dst ] = (LDAPControl *)&op->o_ctrls[ nc + 1 ];
+
+ op->o_ctrls[ dst ]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ op->o_ctrls[ dst ]->ldctl_iscritical = 1;
+
+ op->o_ctrls[ dst ]->ldctl_value.bv_val = (char *)&op->o_ctrls[ dst ][ 1 ];
+ AC_MEMCPY( op->o_ctrls[ dst ]->ldctl_value.bv_val, val.bv_val, val.bv_len + 1 );
+ op->o_ctrls[ dst ]->ldctl_value.bv_len = val.bv_len;
+ dst++;
+
+ (void)ber_free_buf( ber );
+ }
+
+ op->o_ctrls[ dst ] = NULL;
+ }
+done_pr:;
+ }
+#endif /* SLAPD_META_CLIENT_PR */
+
+ asyncmeta_set_msc_time(msc);
+ ctrls = op->o_ctrls;
+
+ if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls )
+ != LDAP_SUCCESS )
+ {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto done;
+ }
+
+ /*
+ * Starts the search
+ */
+ /* someone reset the connection */
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ rc = asyncmeta_int_filter2bv( &dc, op->ors_filter, &filterbv );
+ if ( rc ) {
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ ber = ldap_build_search_req( msc->msc_ld,
+ mbase.bv_val, realscope, filterbv.bv_val,
+ attrs, op->ors_attrsonly,
+ ctrls, NULL, timelimit, op->ors_slimit, op->ors_deref,
+ &msgid );
+ if (!ber) {
+ Debug( asyncmeta_debug, "%s asyncmeta_back_search_start: Operation encoding failed with errno %d\n",
+ op->o_log_prefix, msc->msc_ld->ld_errno );
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "Failed to encode proxied request";
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ if (ber) {
+ struct timeval tv = {0, mt->mt_network_timeout*1000};
+
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+ if (rc < 0) {
+ Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+ if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+ rc = LDAP_SERVER_DOWN;
+ } else {
+ goto error_unavailable;
+ }
+ } else {
+ candidates[ candidate ].sr_msgid = msgid;
+ rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_SEARCH,
+ mbase.bv_val, ber, msgid );
+ if (rc == msgid)
+ rc = LDAP_SUCCESS;
+ else
+ rc = LDAP_SERVER_DOWN;
+ ber = NULL;
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ retcode = META_SEARCH_CANDIDATE;
+ asyncmeta_set_msc_time(msc);
+ goto done;
+
+ case LDAP_SERVER_DOWN:
+ /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+ if (do_lock > 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+
+ default:
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto done;
+ }
+ }
+
+error_unavailable:
+ if (ber)
+ ber_free(ber, 1);
+ switch (bc->nretries[candidate]) {
+ case -1: /* nretries = forever */
+ retcode = META_SEARCH_NEED_BIND;
+ ldap_pvt_thread_yield();
+ break;
+ case 0: /* no retries left */
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to send search request to target";
+ retcode = META_SEARCH_ERR;
+ break;
+ default: /* more retries left - try to rebind and go again */
+ retcode = META_SEARCH_NEED_BIND;
+ bc->nretries[candidate]--;
+ ldap_pvt_thread_yield();
+ break;
+ }
+done:;
+#if 0
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+#endif
+#ifdef SLAPD_META_CLIENT_PR
+ if ( save_ctrls != op->o_ctrls ) {
+ op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
+ op->o_ctrls = save_ctrls;
+ }
+#endif /* SLAPD_META_CLIENT_PR */
+
+ if ( mbase.bv_val != realbase.bv_val ) {
+ op->o_tmpfree( mbase.bv_val, op->o_tmpmemctx );
+ }
+
+doreturn:;
+ Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_search_start[%p] (fd %d)=%d\n", op->o_log_prefix, msc, s, candidates[candidate].sr_msgid );
+ return retcode;
+}
+
+int
+asyncmeta_back_search( Operation *op, SlapReply *rs )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ time_t timeout = 0;
+ int rc = 0;
+ int ncandidates = 0, initial_candidates = 0;
+ long i;
+ SlapReply *candidates = NULL;
+ void *thrctx = op->o_threadctx;
+ bm_context_t *bc;
+ a_metaconn_t *mc;
+ int msc_decr = 0;
+ int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
+ int check_bind = 0;
+
+ rs_assert_ready( rs );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
+
+ /*
+ * controls are set in ldap_back_dobind()
+ *
+ * FIXME: in case of values return filter, we might want
+ * to map attrs and maybe rewrite value
+ */
+
+ asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
+ if (bc == NULL) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ candidates = bc->candidates;
+ mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_DONTSEND, 0);
+ if ( !mc || rs->sr_err != LDAP_SUCCESS) {
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ /*
+ * Inits searches
+ */
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ /* reset sr_msgid; it is used in most loops
+ * to check if that target is still to be considered */
+ candidates[i].sr_msgid = META_MSGID_UNDEFINED;
+ /* a target is marked as candidate by asyncmeta_getconn();
+ * if for any reason (an error, it's over or so) it is
+ * no longer active, sr_msgid is set to META_MSGID_IGNORE
+ * but it remains candidate, which means it has been active
+ * at some point during the operation. This allows to
+ * use its response code and more to compute the final
+ * response */
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ candidates[ i ].sr_matched = NULL;
+ candidates[ i ].sr_text = NULL;
+ candidates[ i ].sr_ref = NULL;
+ candidates[ i ].sr_ctrls = NULL;
+ candidates[ i ].sr_nentries = 0;
+ candidates[ i ].sr_type = -1;
+
+ /* get largest timeout among candidates */
+ if ( mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ]
+ && mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] > timeout )
+ {
+ timeout = mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ];
+ }
+ }
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT && (timeout == 0 || op->ors_tlimit < timeout)) {
+ bc->searchtime = 1;
+ bc->timeout = op->ors_tlimit;
+ } else {
+ bc->timeout = timeout;
+ }
+
+ bc->stoptime = op->o_time + bc->timeout;
+ bc->bc_active = 1;
+
+ if (mc->pending_ops >= max_pending_ops) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ rc = asyncmeta_add_message_queue(mc, bc);
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ mc->mc_conns[i].msc_active++;
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+
+ if (rc != LDAP_SUCCESS) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ goto finish;
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( !META_IS_CANDIDATE( &candidates[ i ] )
+ || candidates[ i ].sr_err != LDAP_SUCCESS )
+ {
+ continue;
+ }
+retry:
+ if (bc->timeout && bc->stoptime < slap_get_time() && META_BACK_ONERR_STOP( mi )) {
+ int timeout_err;
+ const char *timeout_text;
+ if (bc->searchtime) {
+ timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+ timeout_text = NULL;
+ } else {
+ timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ timeout_text = "Operation timed out before it was sent to target";
+ }
+ rs->sr_err = timeout_err;
+ rs->sr_text = timeout_text;
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+
+ }
+
+ if (op->o_abandon) {
+ rs->sr_err = SLAPD_ABANDON;
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+ }
+
+ rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, i);
+ switch (rc)
+ {
+ case META_SEARCH_CANDIDATE:
+ /* target is already bound, just send the search request */
+ ncandidates++;
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: IS_CANDIDATE "
+ "cnd=\"%ld\"\n", op->o_log_prefix, i );
+
+ rc = asyncmeta_back_search_start( op, rs, mc, bc, i, NULL, 0 , 1);
+ if (rc == META_SEARCH_ERR) {
+ META_CANDIDATE_CLEAR(&candidates[i]);
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+ }
+ else {
+ continue;
+ }
+ } else if (rc == META_SEARCH_NEED_BIND) {
+ goto retry;
+ }
+ break;
+ case META_SEARCH_NOT_CANDIDATE:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NOT_CANDIDATE "
+ "cnd=\"%ld\"\n", op->o_log_prefix, i );
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ break;
+
+ case META_SEARCH_NEED_BIND:
+ case META_SEARCH_BINDING:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: BINDING "
+ "cnd=\"%ld\" mc %p msc %p\n", op->o_log_prefix, i , mc, &mc->mc_conns[i]);
+ check_bind++;
+ ncandidates++;
+ /* Todo add the context to the message queue but do not send the request
+ the receiver must send this when we are done binding */
+ /* question - how would do receiver know to which targets??? */
+ break;
+
+ case META_SEARCH_ERR:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: SEARCH_ERR "
+ "cnd=\"%ldd\"\n", op->o_log_prefix, i );
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ candidates[ i ].sr_type = REP_RESULT;
+
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+ }
+ else {
+ continue;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ }
+
+ initial_candidates = ncandidates;
+
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char cnd[ SLAP_TEXT_BUFLEN ];
+ int c;
+
+ for ( c = 0; c < mi->mi_ntargets; c++ ) {
+ if ( META_IS_CANDIDATE( &candidates[ c ] ) ) {
+ cnd[ c ] = '*';
+ } else {
+ cnd[ c ] = ' ';
+ }
+ }
+ cnd[ c ] = '\0';
+
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: ncandidates=%d "
+ "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
+ }
+
+ if ( initial_candidates == 0 ) {
+ /* NOTE: here we are not sending any matchedDN;
+ * this is intended, because if the back-meta
+ * is serving this search request, but no valid
+ * candidate could be looked up, it means that
+ * there is a hole in the mapping of the targets
+ * and thus no knowledge of any remote superior
+ * is available */
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_back_search: "
+ "base=\"%s\" scope=%d: "
+ "no candidate could be selected\n",
+ op->o_log_prefix, op->o_req_dn.bv_val,
+ op->ors_scope );
+
+ /* FIXME: we're sending the first error we encounter;
+ * maybe we should pick the worst... */
+ rc = LDAP_NO_SUCH_OBJECT;
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( META_IS_CANDIDATE( &candidates[ i ] )
+ && candidates[ i ].sr_err != LDAP_SUCCESS )
+ {
+ rc = candidates[ i ].sr_err;
+ break;
+ }
+ }
+ rs->sr_err = rc;
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_drop_bc(mc, bc);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ send_ldap_result(op, rs);
+ goto finish;
+ }
+
+ /* If we were processing many targets the result from a pending Bind
+ * on an earlier target may have arrived while we were sending to a
+ * later target. See if we can now send our pending request.
+ */
+ if ( check_bind ) {
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( candidates[ i ].sr_msgid == META_MSGID_GOT_BIND ) {
+ rc = asyncmeta_back_search_start( op, rs, mc, bc, i, NULL, 0, 1 );
+ if ( rc == META_SEARCH_ERR ) {
+ META_CANDIDATE_CLEAR( &candidates[i] );
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+ }
+ }
+ }
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ mc->mc_conns[i].msc_active--;
+ }
+ msc_decr = 1;
+
+ asyncmeta_start_listeners(mc, candidates, bc);
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ rs->sr_err = SLAPD_ASYNCOP;
+
+finish:
+ /* we ended up straight here due to error and need to reset the msc_active*/
+ if (msc_decr == 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ mc->mc_conns[i].msc_active--;
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ return rs->sr_err;
+}