예제 #1
0
static void send_result(
	Operation		*op,
	SlapReply		*rs,
	sort_op			*so)
{
	LDAPControl *ctrls[3];
	int rc, i = 0;

	rc = pack_sss_response_control( op, rs, ctrls );
	if ( rc == LDAP_SUCCESS ) {
		i++;
		rc = -1;
		if ( so->so_paged > SLAP_CONTROL_IGNORED ) {
			rc = pack_pagedresult_response_control( op, rs, so, ctrls+1 );
		} else if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
			rc = pack_vlv_response_control( op, rs, so, ctrls+1 );
		}
		if ( rc == LDAP_SUCCESS )
			i++;
	}
	ctrls[i] = NULL;

	if ( ctrls[0] != NULL )
		slap_add_ctrls( op, rs, ctrls );
	send_ldap_result( op, rs );

	if ( so->so_tree == NULL ) {
		/* Search finished, so clean up */
		free_sort_op( op->o_conn, so );
	} else {
		so->so_running = 0;
	}
}
예제 #2
0
static void
send_paged_response( 
	Operation	*op,
	SlapReply	*rs,
	ID		*lastid,
	int		tentries )
{
	LDAPControl	*ctrls[2];
	BerElementBuffer berbuf;
	BerElement	*ber = (BerElement *)&berbuf;
	PagedResultsCookie respcookie;
	struct berval cookie;

	Debug(LDAP_DEBUG_ARGS,
		"send_paged_response: lastid=0x%08lx nentries=%d\n", 
		lastid ? *lastid : 0, rs->sr_nentries, NULL );

	ctrls[1] = NULL;

	ber_init2( ber, NULL, LBER_USE_DER );

	if ( lastid ) {
		respcookie = ( PagedResultsCookie )(*lastid);
		cookie.bv_len = sizeof( respcookie );
		cookie.bv_val = (char *)&respcookie;

	} else {
		respcookie = ( PagedResultsCookie )0;
		BER_BVSTR( &cookie, "" );
	}

	op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
	op->o_conn->c_pagedresults_state.ps_count =
		((PagedResultsState *)op->o_pagedresults_state)->ps_count +
		rs->sr_nentries;

	/* return size of 0 -- no estimate */
	ber_printf( ber, "{iO}", 0, &cookie ); 

	ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
	if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
		goto done;
	}

	ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
	ctrls[0]->ldctl_iscritical = 0;

	slap_add_ctrls( op, rs, ctrls );
	rs->sr_err = LDAP_SUCCESS;
	send_ldap_result( op, rs );

done:
	(void) ber_free_buf( ber );
}
예제 #3
0
파일: dupent.c 프로젝트: openldap/openldap
static int
dupent_response_entry_1level(
	Operation *op,
	SlapReply *rs,
	Entry *e,
	valnum_t *valnum,
	int nattrs,
	int level )
{
	int i, rc = LDAP_SUCCESS;

	for ( i = 0; i < valnum[level].ap->a_numvals; i++ ) {
		LDAPControl	*ctrl = NULL, *ctrlsp[2];

		valnum[level].a.a_vals[0] = valnum[level].ap->a_vals[i];
		if ( valnum[level].ap->a_nvals != valnum[level].ap->a_vals ) {
			valnum[level].a.a_nvals[0] = valnum[level].ap->a_nvals[i];
		}

		if ( level < nattrs - 1 ) {
			rc = dupent_response_entry_1level( op, rs,
				e, valnum, nattrs, level + 1 );
			if ( rc != LDAP_SUCCESS ) {
				break;
			}

			continue;
		}

		/* NOTE: add the control all times, under the assumption
		 * send_search_entry() honors the REP_CTRLS_MUSTBEFREED
		 * set by slap_add_ctrls(); this is not true (ITS#6629)
		 */
		ctrl = op->o_tmpcalloc( 1, sizeof( LDAPControl ), op->o_tmpmemctx );
		ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_ENTRY;
		ctrl->ldctl_iscritical = 0;

		ctrlsp[0] = ctrl;
		ctrlsp[1] = NULL;
		slap_add_ctrls( op, rs, ctrlsp );

		/* do the real send */
		rs->sr_entry = e;
		rc = send_search_entry( op, rs );
		if ( rc != LDAP_SUCCESS ) {
			break;
		}
	}

	return rc;
}
예제 #4
0
파일: dupent.c 프로젝트: openldap/openldap
static int
dupent_response_done( Operation *op, SlapReply *rs )
{
	BerElementBuffer	berbuf;
	BerElement			*ber = (BerElement *) &berbuf;
	struct berval		ctrlval;
	LDAPControl			*ctrl, *ctrlsp[2];

	ber_init2( ber, NULL, LBER_USE_DER );

	/*

      DuplicateEntryResponseDone ::= SEQUENCE { 
         resultCode,     -- From [RFC2251] 
         errorMessage    [0] LDAPString OPTIONAL, 
         attribute       [1] AttributeDescription OPTIONAL } 

	 */

	ber_printf( ber, "{i}", rs->sr_err );
	if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
		ber_free_buf( ber );
		if ( op->o_dupent == SLAP_CONTROL_CRITICAL ) {
			return LDAP_CONSTRAINT_VIOLATION;
		}
		return SLAP_CB_CONTINUE;
	}

	ctrl = op->o_tmpcalloc( 1,
		sizeof( LDAPControl ) + ctrlval.bv_len + 1,
		op->o_tmpmemctx );
	ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
	ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_RESPONSE;
	ctrl->ldctl_iscritical = 0;
	ctrl->ldctl_value.bv_len = ctrlval.bv_len;
	AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
	ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';

	ber_free_buf( ber );

	ctrlsp[0] = ctrl;
	ctrlsp[1] = NULL;
	slap_add_ctrls( op, rs, ctrlsp );

	return SLAP_CB_CONTINUE;
}
예제 #5
0
static int
deref_response( Operation *op, SlapReply *rs )
{
	int rc = SLAP_CB_CONTINUE;

	if ( rs->sr_type == REP_SEARCH ) {
		BerElementBuffer berbuf;
		BerElement *ber = (BerElement *) &berbuf;
		deref_cb_t *dc = (deref_cb_t *)op->o_callback->sc_private;
		DerefSpec *ds;
		DerefRes *dr, *drhead = NULL, **drp = &drhead;
		struct berval bv = BER_BVNULL;
		int nDerefRes = 0, nDerefVals = 0, nAttrs = 0, nVals = 0;
		struct berval ctrlval;
		LDAPControl *ctrl, *ctrlsp[2];
		AccessControlState acl_state = ACL_STATE_INIT;
		static char dummy = '\0';
		Entry *ebase;
		int i;

		rc = overlay_entry_get_ov( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &ebase, dc->dc_on );
		if ( rc != LDAP_SUCCESS || ebase == NULL ) {
			return SLAP_CB_CONTINUE;
		}

		for ( ds = dc->dc_ds; ds; ds = ds->ds_next ) {
			Attribute *a = attr_find( ebase->e_attrs, ds->ds_derefAttr );

			if ( a != NULL ) {
				DerefVal *dv;
				BerVarray *bva;

				if ( !access_allowed( op, rs->sr_entry, a->a_desc,
						NULL, ACL_READ, &acl_state ) )
				{
					continue;
				}

				dr = op->o_tmpcalloc( 1,
					sizeof( DerefRes ) + ( sizeof( DerefVal ) + sizeof( BerVarray * ) * ds->ds_nattrs ) * ( a->a_numvals + 1 ),
					op->o_tmpmemctx );
				dr->dr_spec = *ds;
				dv = dr->dr_vals = (DerefVal *)&dr[ 1 ];
				bva = (BerVarray *)&dv[ a->a_numvals + 1 ];

				bv.bv_len += ds->ds_derefAttr->ad_cname.bv_len;
				nAttrs++;
				nDerefRes++;

				for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
					Entry *e = NULL;

					dv[ i ].dv_attrVals = bva;
					bva += ds->ds_nattrs;


					if ( !access_allowed( op, rs->sr_entry, a->a_desc,
							&a->a_nvals[ i ], ACL_READ, &acl_state ) )
					{
						dv[ i ].dv_derefSpecVal.bv_val = &dummy;
						continue;
					}

					ber_dupbv_x( &dv[ i ].dv_derefSpecVal, &a->a_vals[ i ], op->o_tmpmemctx );
					bv.bv_len += dv[ i ].dv_derefSpecVal.bv_len;
					nVals++;
					nDerefVals++;

					rc = overlay_entry_get_ov( op, &a->a_nvals[ i ], NULL, NULL, 0, &e, dc->dc_on );
					if ( rc == LDAP_SUCCESS && e != NULL ) {
						int j;

						if ( access_allowed( op, e, slap_schema.si_ad_entry,
							NULL, ACL_READ, NULL ) )
						{
							for ( j = 0; j < ds->ds_nattrs; j++ ) {
								Attribute *aa;

								if ( !access_allowed( op, e, ds->ds_attributes[ j ], NULL,
									ACL_READ, &acl_state ) )
								{
									continue;
								}

								aa = attr_find( e->e_attrs, ds->ds_attributes[ j ] );
								if ( aa != NULL ) {
									unsigned k, h, last = aa->a_numvals;

									ber_bvarray_dup_x( &dv[ i ].dv_attrVals[ j ],
										aa->a_vals, op->o_tmpmemctx );

									bv.bv_len += ds->ds_attributes[ j ]->ad_cname.bv_len;

									for ( k = 0, h = 0; k < aa->a_numvals; k++ ) {
										if ( !access_allowed( op, e,
											aa->a_desc,
											&aa->a_nvals[ k ],
											ACL_READ, &acl_state ) )
										{
											op->o_tmpfree( dv[ i ].dv_attrVals[ j ][ h ].bv_val,
												op->o_tmpmemctx );
											dv[ i ].dv_attrVals[ j ][ h ] = dv[ i ].dv_attrVals[ j ][ --last ];
											BER_BVZERO( &dv[ i ].dv_attrVals[ j ][ last ] );
											continue;
										}
										bv.bv_len += dv[ i ].dv_attrVals[ j ][ h ].bv_len;
										nVals++;
										h++;
									}
									nAttrs++;
								}
							}
						}

						overlay_entry_release_ov( op, e, 0, dc->dc_on );
					}
				}

				*drp = dr;
				drp = &dr->dr_next;
			}
		}
		overlay_entry_release_ov( op, ebase, 0, dc->dc_on );

		if ( drhead == NULL ) {
			return SLAP_CB_CONTINUE;
		}

		/* cook the control value */
		bv.bv_len += nVals * sizeof(struct berval)
			+ nAttrs * sizeof(struct berval)
			+ nDerefVals * sizeof(DerefVal)
			+ nDerefRes * sizeof(DerefRes);
		bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx );

		ber_init2( ber, &bv, LBER_USE_DER );
		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );

		rc = ber_printf( ber, "{" /*}*/ );
		for ( dr = drhead; dr != NULL; dr = dr->dr_next ) {
			for ( i = 0; !BER_BVISNULL( &dr->dr_vals[ i ].dv_derefSpecVal ); i++ ) {
				int j, first = 1;

				if ( dr->dr_vals[ i ].dv_derefSpecVal.bv_val == &dummy ) {
					continue;
				}

				rc = ber_printf( ber, "{OO" /*}*/,
					&dr->dr_spec.ds_derefAttr->ad_cname,
					&dr->dr_vals[ i ].dv_derefSpecVal );
				op->o_tmpfree( dr->dr_vals[ i ].dv_derefSpecVal.bv_val, op->o_tmpmemctx );
				for ( j = 0; j < dr->dr_spec.ds_nattrs; j++ ) {
					if ( dr->dr_vals[ i ].dv_attrVals[ j ] != NULL ) {
						if ( first ) {
							rc = ber_printf( ber, "t{" /*}*/,
								(LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) );
							first = 0;
						}
						rc = ber_printf( ber, "{O[W]}",
							&dr->dr_spec.ds_attributes[ j ]->ad_cname,
							dr->dr_vals[ i ].dv_attrVals[ j ] );
						op->o_tmpfree( dr->dr_vals[ i ].dv_attrVals[ j ],
							op->o_tmpmemctx );
					}
				}
				if ( !first ) {
					rc = ber_printf( ber, /*{{*/ "}N}" );
				} else {
					rc = ber_printf( ber, /*{*/ "}" );
				}
			}
		}
		rc = ber_printf( ber, /*{*/ "}" );
		if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
			if ( op->o_deref == SLAP_CONTROL_CRITICAL ) {
				rc = LDAP_CONSTRAINT_VIOLATION;

			} else {
				rc = SLAP_CB_CONTINUE;
			}
			goto cleanup;
		}

		ctrl = op->o_tmpcalloc( 1,
			sizeof( LDAPControl ) + ctrlval.bv_len + 1,
			op->o_tmpmemctx );
		ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
		ctrl->ldctl_oid = LDAP_CONTROL_X_DEREF;
		ctrl->ldctl_iscritical = 0;
		ctrl->ldctl_value.bv_len = ctrlval.bv_len;
		memcpy( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
		ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';

		ber_free_buf( ber );

		ctrlsp[0] = ctrl;
		ctrlsp[1] = NULL;
		slap_add_ctrls( op, rs, ctrlsp );

		rc = SLAP_CB_CONTINUE;

cleanup:;
		/* release all */
		for ( ; drhead != NULL; ) {
			DerefRes *drnext = drhead->dr_next;
			op->o_tmpfree( drhead, op->o_tmpmemctx );
			drhead = drnext;
		}

	} else if ( rs->sr_type == REP_RESULT ) {
		rc = deref_cleanup( op, rs );
	}

	return rc;
}
예제 #6
0
static int sssvlv_op_search(
	Operation		*op,
	SlapReply		*rs)
{
	slap_overinst			*on			= (slap_overinst *)op->o_bd->bd_info;
	sssvlv_info				*si			= on->on_bi.bi_private;
	int						rc			= SLAP_CB_CONTINUE;
	int	ok, need_unlock = 0;
	sort_ctrl *sc;
	PagedResultsState *ps;
	vlv_ctrl *vc;
	int sess_id;

	if ( op->o_ctrlflag[sss_cid] <= SLAP_CONTROL_IGNORED ) {
		if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
			sort_op so; memset(&so, 0, sizeof(so));
			so.so_vlv_rc = LDAP_VLV_SSS_MISSING;
			so.so_vlv = op->o_ctrlflag[vlv_cid];
			LDAPControl *ctrls[2];
			rc = pack_vlv_response_control( op, rs, &so, ctrls );
			if ( rc == LDAP_SUCCESS ) {
				ctrls[1] = NULL;
				slap_add_ctrls( op, rs, ctrls );
			}
			rs->sr_err = LDAP_VLV_ERROR;
			rs->sr_text = "Sort control is required with VLV";
			goto leave;
		}
		/* Not server side sort so just continue */
		return SLAP_CB_CONTINUE;
	}

	Debug(LDAP_DEBUG_TRACE,
		"==> sssvlv_search: <%s> %s, control flag: %d\n",
		op->o_req_dn.bv_val, op->ors_filterstr.bv_val,
		op->o_ctrlflag[sss_cid]);

	sc = op->o_controls[sss_cid];
	if ( sc->sc_nkeys > si->svi_max_keys ) {
		rs->sr_text = "Too many sort keys";
		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
		goto leave;
	}

	ps = ( op->o_pagedresults > SLAP_CONTROL_IGNORED ) ?
		(PagedResultsState*)(op->o_pagedresults_state) : NULL;
	vc = op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ?
		op->o_controls[vlv_cid] : NULL;

	if ( ps && vc ) {
		rs->sr_text = "VLV incompatible with PagedResults";
		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
		goto leave;
	}

	ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
	ok = need_unlock = 1;
	sort_op *so = NULL;

	/* Is there already a sort running on this conn? */
	sess_id = find_session_by_context( si->svi_max_percon, op->o_conn->c_conn_idx, vc ? vc->vc_context : NO_VC_CONTEXT, ps ? ps->ps_cookie : NO_PS_COOKIE );
	if ( sess_id >= 0 ) {
		so = sort_conns[op->o_conn->c_conn_idx][sess_id];
		if (so->so_running) {
			/* another thread is handling, response busy to client */
			ok = 0;
			so = NULL;
		} else {
			/* Is it a continuation of a VLV search? */
			if ( !vc || so->so_vlv <= SLAP_CONTROL_IGNORED ||
					vc->vc_context != so->so_vcontext ) {
				/* Is it a continuation of a paged search? */
				if ( !ps || so->so_paged <= SLAP_CONTROL_IGNORED ||
					op->o_conn->c_pagedresults_state.ps_cookie != ps->ps_cookie ) {
					ok = 0;
				} else if ( !ps->ps_size ) {
					/* Abandoning current request */
					ok = 0;
					so->so_nentries = 0;
					rs->sr_err = LDAP_SUCCESS;
				}
			}
			if (( vc && so->so_paged > SLAP_CONTROL_IGNORED ) ||
					( ps && so->so_vlv > SLAP_CONTROL_IGNORED )) {
				/* changed from paged to vlv or vice versa, abandon */
				ok = 0;
				so->so_nentries = 0;
				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
			}
			if ( ok ) {
				/* occupy before mutex unlock */
				so->so_running = 1;
			}
		}
	/* Are there too many running overall? */
	} else if ( si->svi_num >= si->svi_max ) {
		ok = 0;
	} else if ( ( sess_id = find_next_session(si->svi_max_percon, op->o_conn->c_conn_idx ) ) < 0 ) {
		ok = 0;
	} else {
		/* OK, this connection going a sort running as the sess_id */
	}

	if (! ok || so != NULL) {
		assert(need_unlock != 0);
		ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
		need_unlock = 0;
	}

	if ( ok ) {
		/* If we're a global overlay, this check got bypassed */
		if ( !op->ors_limit && limits_check( op, rs )) {
			if (need_unlock) {
				ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
				need_unlock = 0;
			}
			if (so)
				free_sort_op( op->o_conn, so );
			return rs->sr_err;
		}
		/* are we continuing a VLV search? */
		if ( so && vc && vc->vc_context ) {
			assert(need_unlock == 0);
			so->so_ctrl = sc;
			send_list( op, rs, so );
			send_result( op, rs, so );
			rc = LDAP_SUCCESS;
		/* are we continuing a paged search? */
		} else if ( so && ps && ps->ps_cookie ) {
			assert(need_unlock == 0);
			so->so_ctrl = sc;
			send_page( op, rs, so );
			send_result( op, rs, so );
			rc = LDAP_SUCCESS;
		} else {
			/* Install serversort response callback to handle a new search */
			assert(need_unlock != 0);
			assert(so == NULL);

			so = ch_calloc( 1, sizeof(sort_op));
			slap_callback *cb = op->o_tmpcalloc( 1, sizeof(slap_callback),
				op->o_tmpmemctx );
			LDAP_ENSURE(so != NULL && cb != NULL); /* FIXME: LDAP_OTHER */

			cb->sc_response		= sssvlv_op_response;
			cb->sc_next			= op->o_callback;
			cb->sc_private		= so;

			assert(so->so_tree == NULL);
			so->so_ctrl = sc;
			so->so_info = si;
			if ( ps ) {
				so->so_paged = op->o_pagedresults;
				so->so_page_size = ps->ps_size;
				op->o_pagedresults = SLAP_CONTROL_IGNORED;
				assert(so->so_page_size != 0);
			} else {
				if ( vc ) {
					so->so_vlv = op->o_ctrlflag[vlv_cid];
					assert(so->so_vlv_target == 0);
					assert(so->so_vlv_rc == 0);
					assert(so->so_vlv != SLAP_CONTROL_NONE);
				} else {
					assert(so->so_vlv == SLAP_CONTROL_NONE);
				}
			}
			so->so_session = sess_id;
			so->so_vlv = op->o_ctrlflag[vlv_cid];
			so->so_vcontext = (size_t)so;
			assert(so->so_nentries == 0);
			op->o_callback = cb;

			assert(sess_id >= 0);
			so->so_running = 1;
			sort_conns[op->o_conn->c_conn_idx][sess_id] = so;
			si->svi_num++;
			ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
			need_unlock = 0;
		}
		assert(need_unlock == 0);
	} else {
		assert(need_unlock == 0);
		if ( so && !so->so_nentries ) {
			free_sort_op( op->o_conn, so );
		} else {
			rs->sr_text = "Other sort requests already in progress";
			rs->sr_err = LDAP_BUSY;
		}
leave:
		assert(need_unlock == 0);
		rc = rs->sr_err;
		send_ldap_result( op, rs );
	}

	assert(need_unlock == 0);
	return rc;
}
예제 #7
0
static void send_list(
	Operation		*op,
	SlapReply		*rs,
	sort_op			*so)
{
	TAvlnode *cur_node, *tmp_node;
	vlv_ctrl *vc = op->o_controls[vlv_cid];
	int i, j, dir, rc;
	BackendDB *be;
	Entry *e;
	LDAPControl *ctrls[2];

	rs->sr_attrs = op->ors_attrs;

	/* FIXME: it may be better to just flatten the tree into
	 * an array before doing all of this...
	 */

	/* Are we just counting an offset? */
	if ( BER_BVISNULL( &vc->vc_value )) {
		if ( vc->vc_offset == vc->vc_count ) {
			/* wants the last entry in the list */
			cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
			so->so_vlv_target = so->so_nentries;
		} else if ( vc->vc_offset == 1 ) {
			/* wants the first entry in the list */
			cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
			so->so_vlv_target = 1;
		} else {
			int target;
			/* Just iterate to the right spot */
			if ( vc->vc_count && vc->vc_count != so->so_nentries ) {
				if ( vc->vc_offset > vc->vc_count )
					goto range_err;
				target = so->so_nentries * vc->vc_offset / vc->vc_count;
			} else {
				if ( vc->vc_offset > so->so_nentries ) {
range_err:
					so->so_vlv_rc = LDAP_VLV_RANGE_ERROR;
					pack_vlv_response_control( op, rs, so, ctrls );
					ctrls[1] = NULL;
					slap_add_ctrls( op, rs, ctrls );
					rs->sr_err = LDAP_VLV_ERROR;
					return;
				}
				target = vc->vc_offset;
			}
			so->so_vlv_target = target;
			/* Start at left and go right, or start at right and go left? */
			if ( target < so->so_nentries / 2 ) {
				cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
				dir = TAVL_DIR_RIGHT;
			} else {
				cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
				dir = TAVL_DIR_LEFT;
				target = so->so_nentries - target + 1;
			}
			for ( i=1; i<target; i++ )
				cur_node = tavl_next( cur_node, dir );
		}
	} else {
	/* we're looking for a specific value */
		sort_ctrl *sc = so->so_ctrl;
		MatchingRule *mr = sc->sc_keys[0].sk_ordering;
		sort_node *sn;
		struct berval bv;

		if ( mr->smr_normalize ) {
			rc = mr->smr_normalize( SLAP_MR_VALUE_OF_SYNTAX,
				mr->smr_syntax, mr, &vc->vc_value, &bv, op->o_tmpmemctx );
			if ( rc ) {
				so->so_vlv_rc = LDAP_INAPPROPRIATE_MATCHING;
				pack_vlv_response_control( op, rs, so, ctrls );
				ctrls[1] = NULL;
				slap_add_ctrls( op, rs, ctrls );
				rs->sr_err = LDAP_VLV_ERROR;
				return;
			}
		} else {
			bv = vc->vc_value;
		}

		sn = op->o_tmpalloc( sizeof(sort_node) +
			sc->sc_nkeys * sizeof(struct berval), op->o_tmpmemctx );
		sn->sn_vals = (struct berval *)(sn+1);
		sn->sn_conn = op->o_conn->c_conn_idx;
		sn->sn_session = find_session_by_so( so->so_info->svi_max_percon, op->o_conn->c_conn_idx, so );
		sn->sn_vals[0] = bv;
		for (i=1; i<sc->sc_nkeys; i++) {
			BER_BVZERO( &sn->sn_vals[i] );
		}
		cur_node = tavl_find3( so->so_tree, sn, node_cmp, &j );
		/* didn't find >= match */
		if ( j > 0 ) {
			if ( cur_node )
				cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
		}
		op->o_tmpfree( sn, op->o_tmpmemctx );

		if ( !cur_node ) {
			so->so_vlv_target = so->so_nentries + 1;
		} else {
			sort_node *sn = so->so_tree->avl_data;
			/* start from the left or the right side? */
			mr->smr_match( &i, 0, mr->smr_syntax, mr, &bv, &sn->sn_vals[0] );
			if ( i > 0 ) {
				tmp_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
				dir = TAVL_DIR_LEFT;
			} else {
				tmp_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
				dir = TAVL_DIR_RIGHT;
			}
			for (i=0; tmp_node != cur_node;
				tmp_node = tavl_next( tmp_node, dir ), i++);
			so->so_vlv_target = (dir == TAVL_DIR_RIGHT) ? i+1 : so->so_nentries - i;
		}
		if ( bv.bv_val != vc->vc_value.bv_val )
			op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
	}
	if ( !cur_node ) {
		i = 1;
		cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
	} else {
		i = 0;
	}
	for ( ; i<vc->vc_before; i++ ) {
		tmp_node = tavl_next( cur_node, TAVL_DIR_LEFT );
		if ( !tmp_node ) break;
		cur_node = tmp_node;
	}
	j = i + vc->vc_after + 1;
	be = op->o_bd;
	for ( i=0; i<j; i++ ) {
		sort_node *sn = cur_node->avl_data;

		if ( slapd_shutdown ) break;

		op->o_bd = select_backend( &sn->sn_dn, 0 );
		e = NULL;
		rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );

		if ( e && rc == LDAP_SUCCESS ) {
			rs->sr_entry = e;
			rs->sr_flags = REP_ENTRY_MUSTRELEASE;
			rs->sr_err = send_search_entry( op, rs );
			if ( rs->sr_err == LDAP_UNAVAILABLE )
				break;
		}
		cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
		if ( !cur_node ) break;
	}
	so->so_vlv_rc = LDAP_SUCCESS;

	op->o_bd = be;
}