xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/sssvlv.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: sssvlv.c,v 1.1.1.3 2014/05/28 09:58:52 tron Exp $	*/
2 
3 /* sssvlv.c - server side sort / virtual list view */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2009-2014 The OpenLDAP Foundation.
8  * Portions copyright 2009 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 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Howard Chu for inclusion in
21  * OpenLDAP Software. Support for multiple sorts per connection added
22  * by Raphael Ouazana.
23  */
24 
25 #include "portable.h"
26 
27 #ifdef SLAPD_OVER_SSSVLV
28 
29 #include <stdio.h>
30 
31 #include <ac/string.h>
32 #include <ac/ctype.h>
33 
34 #include <avl.h>
35 
36 #include "slap.h"
37 #include "lutil.h"
38 #include "config.h"
39 
40 #include "../../../libraries/liblber/lber-int.h"	/* ber_rewind */
41 
42 /* RFC2891: Server Side Sorting
43  * RFC2696: Paged Results
44  */
45 #ifndef LDAP_MATCHRULE_IDENTIFIER
46 #define LDAP_MATCHRULE_IDENTIFIER      0x80L
47 #define LDAP_REVERSEORDER_IDENTIFIER   0x81L
48 #define LDAP_ATTRTYPES_IDENTIFIER      0x80L
49 #endif
50 
51 /* draft-ietf-ldapext-ldapv3-vlv-09.txt: Virtual List Views
52  */
53 #ifndef LDAP_VLVBYINDEX_IDENTIFIER
54 #define LDAP_VLVBYINDEX_IDENTIFIER	   0xa0L
55 #define LDAP_VLVBYVALUE_IDENTIFIER     0x81L
56 #define LDAP_VLVCONTEXT_IDENTIFIER     0x04L
57 
58 #define LDAP_VLV_SSS_MISSING	0x4C
59 #define LDAP_VLV_RANGE_ERROR	0x4D
60 #endif
61 
62 #define SAFESTR(macro_str, macro_def) ((macro_str) ? (macro_str) : (macro_def))
63 
64 #define SSSVLV_DEFAULT_MAX_KEYS	5
65 #define SSSVLV_DEFAULT_MAX_REQUEST_PER_CONN 5
66 
67 #define NO_PS_COOKIE (PagedResultsCookie) -1
68 #define NO_VC_CONTEXT (unsigned long) -1
69 
70 typedef struct vlv_ctrl {
71 	int vc_before;
72 	int vc_after;
73 	int	vc_offset;
74 	int vc_count;
75 	struct berval vc_value;
76 	unsigned long vc_context;
77 } vlv_ctrl;
78 
79 typedef struct sort_key
80 {
81 	AttributeDescription	*sk_ad;
82 	MatchingRule			*sk_ordering;
83 	int						sk_direction;	/* 1=normal, -1=reverse */
84 } sort_key;
85 
86 typedef struct sort_ctrl {
87 	int sc_nkeys;
88 	sort_key sc_keys[1];
89 } sort_ctrl;
90 
91 
92 typedef struct sort_node
93 {
94 	int sn_conn;
95 	int sn_session;
96 	struct berval sn_dn;
97 	struct berval *sn_vals;
98 } sort_node;
99 
100 typedef struct sssvlv_info
101 {
102 	int svi_max;	/* max concurrent sorts */
103 	int svi_num;	/* current # sorts */
104 	int svi_max_keys;	/* max sort keys per request */
105 	int svi_max_percon; /* max concurrent sorts per con */
106 } sssvlv_info;
107 
108 typedef struct sort_op
109 {
110 	Avlnode	*so_tree;
111 	sort_ctrl *so_ctrl;
112 	sssvlv_info *so_info;
113 	int so_paged;
114 	int so_page_size;
115 	int so_nentries;
116 	int so_vlv;
117 	int so_vlv_rc;
118 	int so_vlv_target;
119 	int so_session;
120 	unsigned long so_vcontext;
121 } sort_op;
122 
123 /* There is only one conn table for all overlay instances */
124 /* Each conn can handle one session by context */
125 static sort_op ***sort_conns;
126 static ldap_pvt_thread_mutex_t sort_conns_mutex;
127 static int ov_count;
128 static const char *debug_header = "sssvlv";
129 
130 static int sss_cid;
131 static int vlv_cid;
132 
133 /* RFC 2981 Section 2.2
134  * If a sort key is a multi-valued attribute, and an entry happens to
135  * have multiple values for that attribute and no other controls are
136  * present that affect the sorting order, then the server SHOULD use the
137  * least value (according to the ORDERING rule for that attribute).
138  */
139 static struct berval* select_value(
140 	Attribute		*attr,
141 	sort_key			*key )
142 {
143 	struct berval* ber1, *ber2;
144 	MatchingRule *mr = key->sk_ordering;
145 	unsigned i;
146 	int cmp;
147 
148 	ber1 = &(attr->a_nvals[0]);
149 	ber2 = ber1+1;
150 	for ( i = 1; i < attr->a_numvals; i++,ber2++ ) {
151 		mr->smr_match( &cmp, 0, mr->smr_syntax, mr, ber1, ber2 );
152 		if ( cmp > 0 ) {
153 			ber1 = ber2;
154 		}
155 	}
156 
157 	Debug(LDAP_DEBUG_TRACE, "%s: value selected for compare: %s\n",
158 		debug_header,
159 		SAFESTR(ber1->bv_val, "<Empty>"),
160 		0);
161 
162 	return ber1;
163 }
164 
165 static int node_cmp( const void* val1, const void* val2 )
166 {
167 	sort_node *sn1 = (sort_node *)val1;
168 	sort_node *sn2 = (sort_node *)val2;
169 	sort_ctrl *sc;
170 	MatchingRule *mr;
171 	int i, cmp = 0;
172 	assert( sort_conns[sn1->sn_conn]
173 		&& sort_conns[sn1->sn_conn][sn1->sn_session]
174 		&& sort_conns[sn1->sn_conn][sn1->sn_session]->so_ctrl );
175 	sc = sort_conns[sn1->sn_conn][sn1->sn_session]->so_ctrl;
176 
177 	for ( i=0; cmp == 0 && i<sc->sc_nkeys; i++ ) {
178 		if ( BER_BVISNULL( &sn1->sn_vals[i] )) {
179 			if ( BER_BVISNULL( &sn2->sn_vals[i] ))
180 				cmp = 0;
181 			else
182 				cmp = sc->sc_keys[i].sk_direction;
183 		} else if ( BER_BVISNULL( &sn2->sn_vals[i] )) {
184 			cmp = sc->sc_keys[i].sk_direction * -1;
185 		} else {
186 			mr = sc->sc_keys[i].sk_ordering;
187 			mr->smr_match( &cmp, 0, mr->smr_syntax, mr,
188 				&sn1->sn_vals[i], &sn2->sn_vals[i] );
189 			if ( cmp )
190 				cmp *= sc->sc_keys[i].sk_direction;
191 		}
192 	}
193 	return cmp;
194 }
195 
196 static int node_insert( const void *val1, const void *val2 )
197 {
198 	/* Never return equal so that new entries are always inserted */
199 	return node_cmp( val1, val2 ) < 0 ? -1 : 1;
200 }
201 
202 static int pack_vlv_response_control(
203 	Operation		*op,
204 	SlapReply		*rs,
205 	sort_op			*so,
206 	LDAPControl	**ctrlsp )
207 {
208 	LDAPControl			*ctrl;
209 	BerElementBuffer	berbuf;
210 	BerElement			*ber		= (BerElement *)&berbuf;
211 	struct berval		cookie, bv;
212 	int					rc;
213 
214 	ber_init2( ber, NULL, LBER_USE_DER );
215 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
216 
217 	rc = ber_printf( ber, "{iie", so->so_vlv_target, so->so_nentries,
218 		so->so_vlv_rc );
219 
220 	if ( rc != -1 && so->so_vcontext ) {
221 		cookie.bv_val = (char *)&so->so_vcontext;
222 		cookie.bv_len = sizeof(so->so_vcontext);
223 		rc = ber_printf( ber, "tO", LDAP_VLVCONTEXT_IDENTIFIER, &cookie );
224 	}
225 
226 	if ( rc != -1 ) {
227 		rc = ber_printf( ber, "}" );
228 	}
229 
230 	if ( rc != -1 ) {
231 		rc = ber_flatten2( ber, &bv, 0 );
232 	}
233 
234 	if ( rc != -1 ) {
235 		ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
236 			bv.bv_len, op->o_tmpmemctx );
237 		ctrl->ldctl_oid			= LDAP_CONTROL_VLVRESPONSE;
238 		ctrl->ldctl_iscritical	= 0;
239 		ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
240 		ctrl->ldctl_value.bv_len = bv.bv_len;
241 		AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
242 		ctrlsp[0] = ctrl;
243 	} else {
244 		ctrlsp[0] = NULL;
245 		rs->sr_err = LDAP_OTHER;
246 	}
247 
248 	ber_free_buf( ber );
249 
250 	return rs->sr_err;
251 }
252 
253 static int pack_pagedresult_response_control(
254 	Operation		*op,
255 	SlapReply		*rs,
256 	sort_op			*so,
257 	LDAPControl	**ctrlsp )
258 {
259 	LDAPControl			*ctrl;
260 	BerElementBuffer	berbuf;
261 	BerElement			*ber		= (BerElement *)&berbuf;
262 	PagedResultsCookie	resp_cookie;
263 	struct berval		cookie, bv;
264 	int					rc;
265 
266 	ber_init2( ber, NULL, LBER_USE_DER );
267 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
268 
269 	if ( so->so_nentries > 0 ) {
270 		resp_cookie		= ( PagedResultsCookie )so->so_tree;
271 		cookie.bv_len	= sizeof( PagedResultsCookie );
272 		cookie.bv_val	= (char *)&resp_cookie;
273 	} else {
274 		resp_cookie		= ( PagedResultsCookie )0;
275 		BER_BVZERO( &cookie );
276 	}
277 
278 	op->o_conn->c_pagedresults_state.ps_cookie = resp_cookie;
279 	op->o_conn->c_pagedresults_state.ps_count
280 		= ((PagedResultsState *)op->o_pagedresults_state)->ps_count
281 		  + rs->sr_nentries;
282 
283 	rc = ber_printf( ber, "{iO}", so->so_nentries, &cookie );
284 	if ( rc != -1 ) {
285 		rc = ber_flatten2( ber, &bv, 0 );
286 	}
287 
288 	if ( rc != -1 ) {
289 		ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
290 			bv.bv_len, op->o_tmpmemctx );
291 		ctrl->ldctl_oid			= LDAP_CONTROL_PAGEDRESULTS;
292 		ctrl->ldctl_iscritical	= 0;
293 		ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
294 		ctrl->ldctl_value.bv_len = bv.bv_len;
295 		AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
296 		ctrlsp[0] = ctrl;
297 	} else {
298 		ctrlsp[0] = NULL;
299 		rs->sr_err = LDAP_OTHER;
300 	}
301 
302 	ber_free_buf( ber );
303 
304 	return rs->sr_err;
305 }
306 
307 static int pack_sss_response_control(
308 	Operation		*op,
309 	SlapReply		*rs,
310 	LDAPControl	**ctrlsp )
311 {
312 	LDAPControl			*ctrl;
313 	BerElementBuffer	berbuf;
314 	BerElement			*ber		= (BerElement *)&berbuf;
315 	struct berval		bv;
316 	int					rc;
317 
318 	ber_init2( ber, NULL, LBER_USE_DER );
319 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
320 
321 	/* Pack error code */
322 	rc = ber_printf(ber, "{e}", rs->sr_err);
323 
324 	if ( rc != -1)
325 		rc = ber_flatten2( ber, &bv, 0 );
326 
327 	if ( rc != -1 ) {
328 		ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
329 			bv.bv_len, op->o_tmpmemctx );
330 		ctrl->ldctl_oid			= LDAP_CONTROL_SORTRESPONSE;
331 		ctrl->ldctl_iscritical	= 0;
332 		ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
333 		ctrl->ldctl_value.bv_len = bv.bv_len;
334 		AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
335 		ctrlsp[0] = ctrl;
336 	} else {
337 		ctrlsp[0] = NULL;
338 		rs->sr_err = LDAP_OTHER;
339 	}
340 
341 	ber_free_buf( ber );
342 
343 	return rs->sr_err;
344 }
345 
346 /* Return the session id or -1 if unknown */
347 static int find_session_by_so(
348 	int svi_max_percon,
349 	int conn_id,
350 	sort_op *so )
351 {
352 	int sess_id;
353 	if (so == NULL) {
354 		return -1;
355 	}
356 	for (sess_id = 0; sess_id < svi_max_percon; sess_id++) {
357 		if ( sort_conns[conn_id] && sort_conns[conn_id][sess_id] == so )
358 			return sess_id;
359 	}
360 	return -1;
361 }
362 
363 /* Return the session id or -1 if unknown */
364 static int find_session_by_context(
365 	int svi_max_percon,
366 	int conn_id,
367 	unsigned long vc_context,
368 	PagedResultsCookie ps_cookie )
369 {
370 	int sess_id;
371 	for(sess_id = 0; sess_id < svi_max_percon; sess_id++) {
372 		if( sort_conns[conn_id] && sort_conns[conn_id][sess_id] &&
373 		    ( sort_conns[conn_id][sess_id]->so_vcontext == vc_context ||
374                       (PagedResultsCookie) sort_conns[conn_id][sess_id]->so_tree == ps_cookie ) )
375 			return sess_id;
376 	}
377 	return -1;
378 }
379 
380 static int find_next_session(
381 	int svi_max_percon,
382 	int conn_id )
383 {
384 	int sess_id;
385 	assert(sort_conns[conn_id] != NULL);
386 	for(sess_id = 0; sess_id < svi_max_percon; sess_id++) {
387 		if(!sort_conns[conn_id][sess_id]) {
388 			return sess_id;
389 		}
390 	}
391 	if (sess_id >= svi_max_percon) {
392 		return -1;
393 	} else {
394 		return sess_id;
395 	}
396 }
397 
398 static void free_sort_op( Connection *conn, sort_op *so )
399 {
400 	int sess_id;
401 	if ( so->so_tree ) {
402 		if ( so->so_paged > SLAP_CONTROL_IGNORED ) {
403 			Avlnode *cur_node, *next_node;
404 			cur_node = so->so_tree;
405 			while ( cur_node ) {
406 				next_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
407 				ch_free( cur_node->avl_data );
408 				ber_memfree( cur_node );
409 
410 				cur_node = next_node;
411 			}
412 		} else {
413 			tavl_free( so->so_tree, ch_free );
414 		}
415 		so->so_tree = NULL;
416 	}
417 
418 	ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
419 	sess_id = find_session_by_so( so->so_info->svi_max_percon, conn->c_conn_idx, so );
420 	sort_conns[conn->c_conn_idx][sess_id] = NULL;
421 	so->so_info->svi_num--;
422 	ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
423 
424 	ch_free( so );
425 }
426 
427 static void free_sort_ops( Connection *conn, sort_op **sos, int svi_max_percon )
428 {
429 	int sess_id;
430 	sort_op *so;
431 
432 	for( sess_id = 0; sess_id < svi_max_percon ; sess_id++ ) {
433 		so = sort_conns[conn->c_conn_idx][sess_id];
434 		if ( so ) {
435 			free_sort_op( conn, so );
436 			sort_conns[conn->c_conn_idx][sess_id] = NULL;
437 		}
438 	}
439 }
440 
441 static void send_list(
442 	Operation		*op,
443 	SlapReply		*rs,
444 	sort_op			*so)
445 {
446 	Avlnode	*cur_node, *tmp_node;
447 	vlv_ctrl *vc = op->o_controls[vlv_cid];
448 	int i, j, dir, rc;
449 	BackendDB *be;
450 	Entry *e;
451 	LDAPControl *ctrls[2];
452 
453 	rs->sr_attrs = op->ors_attrs;
454 
455 	/* FIXME: it may be better to just flatten the tree into
456 	 * an array before doing all of this...
457 	 */
458 
459 	/* Are we just counting an offset? */
460 	if ( BER_BVISNULL( &vc->vc_value )) {
461 		if ( vc->vc_offset == vc->vc_count ) {
462 			/* wants the last entry in the list */
463 			cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
464 			so->so_vlv_target = so->so_nentries;
465 		} else if ( vc->vc_offset == 1 ) {
466 			/* wants the first entry in the list */
467 			cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
468 			so->so_vlv_target = 1;
469 		} else {
470 			int target;
471 			/* Just iterate to the right spot */
472 			if ( vc->vc_count && vc->vc_count != so->so_nentries ) {
473 				if ( vc->vc_offset > vc->vc_count )
474 					goto range_err;
475 				target = so->so_nentries * vc->vc_offset / vc->vc_count;
476 			} else {
477 				if ( vc->vc_offset > so->so_nentries ) {
478 range_err:
479 					so->so_vlv_rc = LDAP_VLV_RANGE_ERROR;
480 					pack_vlv_response_control( op, rs, so, ctrls );
481 					ctrls[1] = NULL;
482 					slap_add_ctrls( op, rs, ctrls );
483 					rs->sr_err = LDAP_VLV_ERROR;
484 					return;
485 				}
486 				target = vc->vc_offset;
487 			}
488 			so->so_vlv_target = target;
489 			/* Start at left and go right, or start at right and go left? */
490 			if ( target < so->so_nentries / 2 ) {
491 				cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
492 				dir = TAVL_DIR_RIGHT;
493 			} else {
494 				cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
495 				dir = TAVL_DIR_LEFT;
496 				target = so->so_nentries - target + 1;
497 			}
498 			for ( i=1; i<target; i++ )
499 				cur_node = tavl_next( cur_node, dir );
500 		}
501 	} else {
502 	/* we're looking for a specific value */
503 		sort_ctrl *sc = so->so_ctrl;
504 		MatchingRule *mr = sc->sc_keys[0].sk_ordering;
505 		sort_node *sn;
506 		struct berval bv;
507 
508 		if ( mr->smr_normalize ) {
509 			rc = mr->smr_normalize( SLAP_MR_VALUE_OF_SYNTAX,
510 				mr->smr_syntax, mr, &vc->vc_value, &bv, op->o_tmpmemctx );
511 			if ( rc ) {
512 				so->so_vlv_rc = LDAP_INAPPROPRIATE_MATCHING;
513 				pack_vlv_response_control( op, rs, so, ctrls );
514 				ctrls[1] = NULL;
515 				slap_add_ctrls( op, rs, ctrls );
516 				rs->sr_err = LDAP_VLV_ERROR;
517 				return;
518 			}
519 		} else {
520 			bv = vc->vc_value;
521 		}
522 
523 		sn = op->o_tmpalloc( sizeof(sort_node) +
524 			sc->sc_nkeys * sizeof(struct berval), op->o_tmpmemctx );
525 		sn->sn_vals = (struct berval *)(sn+1);
526 		sn->sn_conn = op->o_conn->c_conn_idx;
527 		sn->sn_session = find_session_by_so( so->so_info->svi_max_percon, op->o_conn->c_conn_idx, so );
528 		sn->sn_vals[0] = bv;
529 		for (i=1; i<sc->sc_nkeys; i++) {
530 			BER_BVZERO( &sn->sn_vals[i] );
531 		}
532 		cur_node = tavl_find3( so->so_tree, sn, node_cmp, &j );
533 		/* didn't find >= match */
534 		if ( j > 0 ) {
535 			if ( cur_node )
536 				cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
537 		}
538 		op->o_tmpfree( sn, op->o_tmpmemctx );
539 
540 		if ( !cur_node ) {
541 			so->so_vlv_target = so->so_nentries + 1;
542 		} else {
543 			sort_node *sn = so->so_tree->avl_data;
544 			/* start from the left or the right side? */
545 			mr->smr_match( &i, 0, mr->smr_syntax, mr, &bv, &sn->sn_vals[0] );
546 			if ( i > 0 ) {
547 				tmp_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
548 				dir = TAVL_DIR_LEFT;
549 			} else {
550 				tmp_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
551 				dir = TAVL_DIR_RIGHT;
552 			}
553 			for (i=0; tmp_node != cur_node;
554 				tmp_node = tavl_next( tmp_node, dir ), i++);
555 			so->so_vlv_target = (dir == TAVL_DIR_RIGHT) ? i+1 : so->so_nentries - i;
556 		}
557 		if ( bv.bv_val != vc->vc_value.bv_val )
558 			op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
559 	}
560 	if ( !cur_node ) {
561 		i = 1;
562 		cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
563 	} else {
564 		i = 0;
565 	}
566 	for ( ; i<vc->vc_before; i++ ) {
567 		tmp_node = tavl_next( cur_node, TAVL_DIR_LEFT );
568 		if ( !tmp_node ) break;
569 		cur_node = tmp_node;
570 	}
571 	j = i + vc->vc_after + 1;
572 	be = op->o_bd;
573 	for ( i=0; i<j; i++ ) {
574 		sort_node *sn = cur_node->avl_data;
575 
576 		if ( slapd_shutdown ) break;
577 
578 		op->o_bd = select_backend( &sn->sn_dn, 0 );
579 		e = NULL;
580 		rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
581 
582 		if ( e && rc == LDAP_SUCCESS ) {
583 			rs->sr_entry = e;
584 			rs->sr_flags = REP_ENTRY_MUSTRELEASE;
585 			rs->sr_err = send_search_entry( op, rs );
586 			if ( rs->sr_err == LDAP_UNAVAILABLE )
587 				break;
588 		}
589 		cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
590 		if ( !cur_node ) break;
591 	}
592 	so->so_vlv_rc = LDAP_SUCCESS;
593 
594 	op->o_bd = be;
595 }
596 
597 static void send_page( Operation *op, SlapReply *rs, sort_op *so )
598 {
599 	Avlnode		*cur_node		= so->so_tree;
600 	Avlnode		*next_node		= NULL;
601 	BackendDB *be = op->o_bd;
602 	Entry *e;
603 	int rc;
604 
605 	rs->sr_attrs = op->ors_attrs;
606 
607 	while ( cur_node && rs->sr_nentries < so->so_page_size ) {
608 		sort_node *sn = cur_node->avl_data;
609 
610 		if ( slapd_shutdown ) break;
611 
612 		next_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
613 
614 		op->o_bd = select_backend( &sn->sn_dn, 0 );
615 		e = NULL;
616 		rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
617 
618 		ch_free( cur_node->avl_data );
619 		ber_memfree( cur_node );
620 
621 		cur_node = next_node;
622 		so->so_nentries--;
623 
624 		if ( e && rc == LDAP_SUCCESS ) {
625 			rs->sr_entry = e;
626 			rs->sr_flags = REP_ENTRY_MUSTRELEASE;
627 			rs->sr_err = send_search_entry( op, rs );
628 			if ( rs->sr_err == LDAP_UNAVAILABLE )
629 				break;
630 		}
631 	}
632 
633 	/* Set the first entry to send for the next page */
634 	so->so_tree = next_node;
635 	if ( next_node )
636 		next_node->avl_left = NULL;
637 
638 	op->o_bd = be;
639 }
640 
641 static void send_entry(
642 	Operation		*op,
643 	SlapReply		*rs,
644 	sort_op			*so)
645 {
646 	Debug(LDAP_DEBUG_TRACE,
647 		"%s: response control: status=%d, text=%s\n",
648 		debug_header, rs->sr_err, SAFESTR(rs->sr_text, "<None>"));
649 
650 	if ( !so->so_tree )
651 		return;
652 
653 	/* RFC 2891: If critical then send the entries iff they were
654 	 * succesfully sorted.  If non-critical send all entries
655 	 * whether they were sorted or not.
656 	 */
657 	if ( (op->o_ctrlflag[sss_cid] != SLAP_CONTROL_CRITICAL) ||
658 		 (rs->sr_err == LDAP_SUCCESS) )
659 	{
660 		if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
661 			send_list( op, rs, so );
662 		} else {
663 			/* Get the first node to send */
664 			Avlnode *start_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
665 			so->so_tree = start_node;
666 
667 			if ( so->so_paged <= SLAP_CONTROL_IGNORED ) {
668 				/* Not paged result search.  Send all entries.
669 				 * Set the page size to the number of entries
670 				 * so that send_page() will send all entries.
671 				 */
672 				so->so_page_size = so->so_nentries;
673 			}
674 
675 			send_page( op, rs, so );
676 		}
677 	}
678 }
679 
680 static void send_result(
681 	Operation		*op,
682 	SlapReply		*rs,
683 	sort_op			*so)
684 {
685 	LDAPControl *ctrls[3];
686 	int rc, i = 0;
687 
688 	rc = pack_sss_response_control( op, rs, ctrls );
689 	if ( rc == LDAP_SUCCESS ) {
690 		i++;
691 		rc = -1;
692 		if ( so->so_paged > SLAP_CONTROL_IGNORED ) {
693 			rc = pack_pagedresult_response_control( op, rs, so, ctrls+1 );
694 		} else if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
695 			rc = pack_vlv_response_control( op, rs, so, ctrls+1 );
696 		}
697 		if ( rc == LDAP_SUCCESS )
698 			i++;
699 	}
700 	ctrls[i] = NULL;
701 
702 	if ( ctrls[0] != NULL )
703 		slap_add_ctrls( op, rs, ctrls );
704 	send_ldap_result( op, rs );
705 
706 	if ( so->so_tree == NULL ) {
707 		/* Search finished, so clean up */
708 		free_sort_op( op->o_conn, so );
709 	}
710 }
711 
712 static int sssvlv_op_response(
713 	Operation	*op,
714 	SlapReply	*rs )
715 {
716 	sort_ctrl *sc = op->o_controls[sss_cid];
717 	sort_op *so = op->o_callback->sc_private;
718 
719 	if ( rs->sr_type == REP_SEARCH ) {
720 		int i;
721 		size_t len;
722 		sort_node *sn, *sn2;
723 		struct berval *bv;
724 		char *ptr;
725 
726 		len = sizeof(sort_node) + sc->sc_nkeys * sizeof(struct berval) +
727 			rs->sr_entry->e_nname.bv_len + 1;
728 		sn = op->o_tmpalloc( len, op->o_tmpmemctx );
729 		sn->sn_vals = (struct berval *)(sn+1);
730 
731 		/* Build tmp list of key values */
732 		for ( i=0; i<sc->sc_nkeys; i++ ) {
733 			Attribute *a = attr_find( rs->sr_entry->e_attrs,
734 				sc->sc_keys[i].sk_ad );
735 			if ( a ) {
736 				if ( a->a_numvals > 1 ) {
737 					bv = select_value( a, &sc->sc_keys[i] );
738 				} else {
739 					bv = a->a_nvals;
740 				}
741 				sn->sn_vals[i] = *bv;
742 				len += bv->bv_len + 1;
743 			} else {
744 				BER_BVZERO( &sn->sn_vals[i] );
745 			}
746 		}
747 
748 		/* Now dup into regular memory */
749 		sn2 = ch_malloc( len );
750 		sn2->sn_vals = (struct berval *)(sn2+1);
751 		AC_MEMCPY( sn2->sn_vals, sn->sn_vals,
752 				sc->sc_nkeys * sizeof(struct berval));
753 
754 		ptr = (char *)(sn2->sn_vals + sc->sc_nkeys);
755 		sn2->sn_dn.bv_val = ptr;
756 		sn2->sn_dn.bv_len = rs->sr_entry->e_nname.bv_len;
757 		AC_MEMCPY( ptr, rs->sr_entry->e_nname.bv_val,
758 			rs->sr_entry->e_nname.bv_len );
759 		ptr += rs->sr_entry->e_nname.bv_len;
760 		*ptr++ = '\0';
761 		for ( i=0; i<sc->sc_nkeys; i++ ) {
762 			if ( !BER_BVISNULL( &sn2->sn_vals[i] )) {
763 				AC_MEMCPY(ptr, sn2->sn_vals[i].bv_val, sn2->sn_vals[i].bv_len);
764 				sn2->sn_vals[i].bv_val = ptr;
765 				ptr += sn2->sn_vals[i].bv_len;
766 				*ptr++ = '\0';
767 			}
768 		}
769 		op->o_tmpfree( sn, op->o_tmpmemctx );
770 		sn = sn2;
771 		sn->sn_conn = op->o_conn->c_conn_idx;
772 		sn->sn_session = find_session_by_so( so->so_info->svi_max_percon, op->o_conn->c_conn_idx, so );
773 
774 		/* Insert into the AVL tree */
775 		tavl_insert(&(so->so_tree), sn, node_insert, avl_dup_error);
776 
777 		so->so_nentries++;
778 
779 		/* Collected the keys so that they can be sorted.  Thus, stop
780 		 * the entry from propagating.
781 		 */
782 		rs->sr_err = LDAP_SUCCESS;
783 	}
784 	else if ( rs->sr_type == REP_RESULT ) {
785 		/* Remove serversort response callback.
786 		 * We don't want the entries that we are about to send to be
787 		 * processed by serversort response again.
788 		 */
789 		if ( op->o_callback->sc_response == sssvlv_op_response ) {
790 			op->o_callback = op->o_callback->sc_next;
791 		}
792 
793 		send_entry( op, rs, so );
794 		send_result( op, rs, so );
795 	}
796 
797 	return rs->sr_err;
798 }
799 
800 static int sssvlv_op_search(
801 	Operation		*op,
802 	SlapReply		*rs)
803 {
804 	slap_overinst			*on			= (slap_overinst *)op->o_bd->bd_info;
805 	sssvlv_info				*si			= on->on_bi.bi_private;
806 	int						rc			= SLAP_CB_CONTINUE;
807 	int	ok;
808 	sort_op *so = NULL, so2;
809 	sort_ctrl *sc;
810 	PagedResultsState *ps;
811 	vlv_ctrl *vc;
812 	int sess_id;
813 
814 	if ( op->o_ctrlflag[sss_cid] <= SLAP_CONTROL_IGNORED ) {
815 		if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
816 			LDAPControl *ctrls[2];
817 			so2.so_vcontext = 0;
818 			so2.so_vlv_target = 0;
819 			so2.so_nentries = 0;
820 			so2.so_vlv_rc = LDAP_VLV_SSS_MISSING;
821 			so2.so_vlv = op->o_ctrlflag[vlv_cid];
822 			rc = pack_vlv_response_control( op, rs, &so2, ctrls );
823 			if ( rc == LDAP_SUCCESS ) {
824 				ctrls[1] = NULL;
825 				slap_add_ctrls( op, rs, ctrls );
826 			}
827 			rs->sr_err = LDAP_VLV_ERROR;
828 			rs->sr_text = "Sort control is required with VLV";
829 			goto leave;
830 		}
831 		/* Not server side sort so just continue */
832 		return SLAP_CB_CONTINUE;
833 	}
834 
835 	Debug(LDAP_DEBUG_TRACE,
836 		"==> sssvlv_search: <%s> %s, control flag: %d\n",
837 		op->o_req_dn.bv_val, op->ors_filterstr.bv_val,
838 		op->o_ctrlflag[sss_cid]);
839 
840 	sc = op->o_controls[sss_cid];
841 	if ( sc->sc_nkeys > si->svi_max_keys ) {
842 		rs->sr_text = "Too many sort keys";
843 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
844 		goto leave;
845 	}
846 
847 	ps = ( op->o_pagedresults > SLAP_CONTROL_IGNORED ) ?
848 		(PagedResultsState*)(op->o_pagedresults_state) : NULL;
849 	vc = op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ?
850 		op->o_controls[vlv_cid] : NULL;
851 
852 	if ( ps && vc ) {
853 		rs->sr_text = "VLV incompatible with PagedResults";
854 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
855 		goto leave;
856 	}
857 
858 	ok = 1;
859 	ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
860 	/* Is there already a sort running on this conn? */
861 	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 );
862 	if ( sess_id >= 0 ) {
863 		so = sort_conns[op->o_conn->c_conn_idx][sess_id];
864 		/* Is it a continuation of a VLV search? */
865 		if ( !vc || so->so_vlv <= SLAP_CONTROL_IGNORED ||
866 			vc->vc_context != so->so_vcontext ) {
867 			/* Is it a continuation of a paged search? */
868 			if ( !ps || so->so_paged <= SLAP_CONTROL_IGNORED ||
869 				op->o_conn->c_pagedresults_state.ps_cookie != ps->ps_cookie ) {
870 				ok = 0;
871 			} else if ( !ps->ps_size ) {
872 			/* Abandoning current request */
873 				ok = 0;
874 				so->so_nentries = 0;
875 				rs->sr_err = LDAP_SUCCESS;
876 			}
877 		}
878 		if (( vc && so->so_paged > SLAP_CONTROL_IGNORED ) ||
879 			( ps && so->so_vlv > SLAP_CONTROL_IGNORED )) {
880 			/* changed from paged to vlv or vice versa, abandon */
881 			ok = 0;
882 			so->so_nentries = 0;
883 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
884 		}
885 	/* Are there too many running overall? */
886 	} else if ( si->svi_num >= si->svi_max ) {
887 		ok = 0;
888 	} else if ( ( sess_id = find_next_session(si->svi_max_percon, op->o_conn->c_conn_idx ) ) < 0 ) {
889 		ok = 0;
890 	} else {
891 		/* OK, this connection now has a sort running */
892 		si->svi_num++;
893 		sort_conns[op->o_conn->c_conn_idx][sess_id] = &so2;
894 		sort_conns[op->o_conn->c_conn_idx][sess_id]->so_session = sess_id;
895 	}
896 	ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
897 	if ( ok ) {
898 		/* If we're a global overlay, this check got bypassed */
899 		if ( !op->ors_limit && limits_check( op, rs ))
900 			return rs->sr_err;
901 		/* are we continuing a VLV search? */
902 		if ( so && vc && vc->vc_context ) {
903 			so->so_ctrl = sc;
904 			send_list( op, rs, so );
905 			send_result( op, rs, so );
906 			rc = LDAP_SUCCESS;
907 		/* are we continuing a paged search? */
908 		} else if ( so && ps && ps->ps_cookie ) {
909 			so->so_ctrl = sc;
910 			send_page( op, rs, so );
911 			send_result( op, rs, so );
912 			rc = LDAP_SUCCESS;
913 		} else {
914 			slap_callback *cb = op->o_tmpalloc( sizeof(slap_callback),
915 				op->o_tmpmemctx );
916 			/* Install serversort response callback to handle a new search */
917 			if ( ps || vc ) {
918 				so = ch_calloc( 1, sizeof(sort_op));
919 			} else {
920 				so = op->o_tmpcalloc( 1, sizeof(sort_op), op->o_tmpmemctx );
921 			}
922 			sort_conns[op->o_conn->c_conn_idx][sess_id] = so;
923 
924 			cb->sc_cleanup		= NULL;
925 			cb->sc_response		= sssvlv_op_response;
926 			cb->sc_next			= op->o_callback;
927 			cb->sc_private		= so;
928 
929 			so->so_tree = NULL;
930 			so->so_ctrl = sc;
931 			so->so_info = si;
932 			if ( ps ) {
933 				so->so_paged = op->o_pagedresults;
934 				so->so_page_size = ps->ps_size;
935 				op->o_pagedresults = SLAP_CONTROL_IGNORED;
936 			} else {
937 				so->so_paged = 0;
938 				so->so_page_size = 0;
939 				if ( vc ) {
940 					so->so_vlv = op->o_ctrlflag[vlv_cid];
941 					so->so_vlv_target = 0;
942 					so->so_vlv_rc = 0;
943 				} else {
944 					so->so_vlv = SLAP_CONTROL_NONE;
945 				}
946 			}
947 			so->so_session = sess_id;
948 			so->so_vlv = op->o_ctrlflag[vlv_cid];
949 			so->so_vcontext = (unsigned long)so;
950 			so->so_nentries = 0;
951 
952 			op->o_callback		= cb;
953 		}
954 	} else {
955 		if ( so && !so->so_nentries ) {
956 			free_sort_op( op->o_conn, so );
957 		} else {
958 			rs->sr_text = "Other sort requests already in progress";
959 			rs->sr_err = LDAP_BUSY;
960 		}
961 leave:
962 		rc = rs->sr_err;
963 		send_ldap_result( op, rs );
964 	}
965 
966 	return rc;
967 }
968 
969 static int get_ordering_rule(
970 	AttributeDescription	*ad,
971 	struct berval			*matchrule,
972 	SlapReply				*rs,
973 	MatchingRule			**ordering )
974 {
975 	MatchingRule* mr;
976 
977 	if ( matchrule && matchrule->bv_val ) {
978 		mr = mr_find( matchrule->bv_val );
979 		if ( mr == NULL ) {
980 			rs->sr_err = LDAP_INAPPROPRIATE_MATCHING;
981 			rs->sr_text = "serverSort control: No ordering rule";
982 			Debug(LDAP_DEBUG_TRACE, "%s: no ordering rule function for %s\n",
983 				debug_header, matchrule->bv_val, 0);
984 		}
985 	}
986 	else {
987 		mr = ad->ad_type->sat_ordering;
988 		if ( mr == NULL ) {
989 			rs->sr_err = LDAP_INAPPROPRIATE_MATCHING;
990 			rs->sr_text = "serverSort control: No ordering rule";
991 			Debug(LDAP_DEBUG_TRACE,
992 				"%s: no ordering rule specified and no default ordering rule for attribute %s\n",
993 				debug_header, ad->ad_cname.bv_val, 0);
994 		}
995 	}
996 
997 	*ordering = mr;
998 	return rs->sr_err;
999 }
1000 
1001 static int count_key(BerElement *ber)
1002 {
1003 	char *end;
1004 	ber_len_t len;
1005 	ber_tag_t tag;
1006 	int count = 0;
1007 
1008 	/* Server Side Sort Control is a SEQUENCE of SEQUENCE */
1009 	for ( tag = ber_first_element( ber, &len, &end );
1010 		  tag == LBER_SEQUENCE;
1011 		  tag = ber_next_element( ber, &len, end ))
1012 	{
1013 		tag = ber_skip_tag( ber, &len );
1014 		ber_skip_data( ber, len );
1015 		++count;
1016 	}
1017 	ber_rewind( ber );
1018 
1019 	return count;
1020 }
1021 
1022 static int build_key(
1023 	BerElement		*ber,
1024 	SlapReply		*rs,
1025 	sort_key			*key )
1026 {
1027 	struct berval attr;
1028 	struct berval matchrule = BER_BVNULL;
1029 	ber_int_t reverse = 0;
1030 	ber_tag_t tag;
1031 	ber_len_t len;
1032 	MatchingRule *ordering = NULL;
1033 	AttributeDescription *ad = NULL;
1034 	const char *text;
1035 
1036 	if (( tag = ber_scanf( ber, "{" )) == LBER_ERROR ) {
1037 		rs->sr_text = "serverSort control: decoding error";
1038 		rs->sr_err = LDAP_PROTOCOL_ERROR;
1039 		return rs->sr_err;
1040 	}
1041 
1042 	if (( tag = ber_scanf( ber, "m", &attr )) == LBER_ERROR ) {
1043 		rs->sr_text = "serverSort control: attribute decoding error";
1044 		rs->sr_err = LDAP_PROTOCOL_ERROR;
1045 		return rs->sr_err;
1046 	}
1047 
1048 	tag = ber_peek_tag( ber, &len );
1049 	if ( tag == LDAP_MATCHRULE_IDENTIFIER ) {
1050 		if (( tag = ber_scanf( ber, "m", &matchrule )) == LBER_ERROR ) {
1051 			rs->sr_text = "serverSort control: matchrule decoding error";
1052 			rs->sr_err = LDAP_PROTOCOL_ERROR;
1053 			return rs->sr_err;
1054 		}
1055 		tag = ber_peek_tag( ber, &len );
1056 	}
1057 
1058 	if ( tag == LDAP_REVERSEORDER_IDENTIFIER ) {
1059 		if (( tag = ber_scanf( ber, "b", &reverse )) == LBER_ERROR ) {
1060 			rs->sr_text = "serverSort control: reverse decoding error";
1061 			rs->sr_err = LDAP_PROTOCOL_ERROR;
1062 			return rs->sr_err;
1063 		}
1064 	}
1065 
1066 	if (( tag = ber_scanf( ber, "}" )) == LBER_ERROR ) {
1067 		rs->sr_text = "serverSort control: decoding error";
1068 		rs->sr_err = LDAP_PROTOCOL_ERROR;
1069 		return rs->sr_err;
1070 	}
1071 
1072 	if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) {
1073 		rs->sr_text =
1074 			"serverSort control: Unrecognized attribute type in sort key";
1075 		Debug(LDAP_DEBUG_TRACE,
1076 			"%s: Unrecognized attribute type in sort key: %s\n",
1077 			debug_header, SAFESTR(attr.bv_val, "<None>"), 0);
1078 		rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
1079 		return rs->sr_err;
1080 	}
1081 
1082 	/* get_ordering_rule will set sr_err and sr_text */
1083 	get_ordering_rule( ad, &matchrule, rs, &ordering );
1084 	if ( rs->sr_err != LDAP_SUCCESS ) {
1085 		return rs->sr_err;
1086 	}
1087 
1088 	key->sk_ad = ad;
1089 	key->sk_ordering = ordering;
1090 	key->sk_direction = reverse ? -1 : 1;
1091 
1092 	return rs->sr_err;
1093 }
1094 
1095 /* Conforms to RFC4510 re: Criticality, original RFC2891 spec is broken
1096  * Also see ITS#7253 for discussion
1097  */
1098 static int sss_parseCtrl(
1099 	Operation		*op,
1100 	SlapReply		*rs,
1101 	LDAPControl		*ctrl )
1102 {
1103 	BerElementBuffer	berbuf;
1104 	BerElement			*ber;
1105 	ber_tag_t		tag;
1106 	ber_len_t		len;
1107 	int					i;
1108 	sort_ctrl	*sc;
1109 
1110 	rs->sr_err = LDAP_PROTOCOL_ERROR;
1111 
1112 	if ( op->o_ctrlflag[sss_cid] > SLAP_CONTROL_IGNORED ) {
1113 		rs->sr_text = "sorted results control specified multiple times";
1114 	} else if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
1115 		rs->sr_text = "sorted results control value is absent";
1116 	} else if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1117 		rs->sr_text = "sorted results control value is empty";
1118 	} else {
1119 		rs->sr_err = LDAP_SUCCESS;
1120 	}
1121 	if ( rs->sr_err != LDAP_SUCCESS )
1122 		return rs->sr_err;
1123 
1124 	op->o_ctrlflag[sss_cid] = ctrl->ldctl_iscritical ?
1125 		SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
1126 
1127 	ber = (BerElement *)&berbuf;
1128 	ber_init2( ber, &ctrl->ldctl_value, 0 );
1129 	i = count_key( ber );
1130 
1131 	sc = op->o_tmpalloc( sizeof(sort_ctrl) +
1132 		(i-1) * sizeof(sort_key), op->o_tmpmemctx );
1133 	sc->sc_nkeys = i;
1134 	op->o_controls[sss_cid] = sc;
1135 
1136 	/* peel off initial sequence */
1137 	ber_scanf( ber, "{" );
1138 
1139 	i = 0;
1140 	do {
1141 		if ( build_key( ber, rs, &sc->sc_keys[i] ) != LDAP_SUCCESS )
1142 			break;
1143 		i++;
1144 		tag = ber_peek_tag( ber, &len );
1145 	} while ( tag != LBER_DEFAULT );
1146 
1147 	return rs->sr_err;
1148 }
1149 
1150 static int vlv_parseCtrl(
1151 	Operation		*op,
1152 	SlapReply		*rs,
1153 	LDAPControl		*ctrl )
1154 {
1155 	BerElementBuffer	berbuf;
1156 	BerElement			*ber;
1157 	ber_tag_t		tag;
1158 	ber_len_t		len;
1159 	vlv_ctrl	*vc, vc2;
1160 
1161 	rs->sr_err = LDAP_PROTOCOL_ERROR;
1162 	rs->sr_text = NULL;
1163 
1164 	if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
1165 		rs->sr_text = "vlv control specified multiple times";
1166 	} else if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
1167 		rs->sr_text = "vlv control value is absent";
1168 	} else if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1169 		rs->sr_text = "vlv control value is empty";
1170 	}
1171 	if ( rs->sr_text != NULL )
1172 		return rs->sr_err;
1173 
1174 	op->o_ctrlflag[vlv_cid] = ctrl->ldctl_iscritical ?
1175 		SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
1176 
1177 	ber = (BerElement *)&berbuf;
1178 	ber_init2( ber, &ctrl->ldctl_value, 0 );
1179 
1180 	rs->sr_err = LDAP_PROTOCOL_ERROR;
1181 
1182 	tag = ber_scanf( ber, "{ii", &vc2.vc_before, &vc2.vc_after );
1183 	if ( tag == LBER_ERROR ) {
1184 		return rs->sr_err;
1185 	}
1186 
1187 	tag = ber_peek_tag( ber, &len );
1188 	if ( tag == LDAP_VLVBYINDEX_IDENTIFIER ) {
1189 		tag = ber_scanf( ber, "{ii}", &vc2.vc_offset, &vc2.vc_count );
1190 		if ( tag == LBER_ERROR )
1191 			return rs->sr_err;
1192 		BER_BVZERO( &vc2.vc_value );
1193 	} else if ( tag == LDAP_VLVBYVALUE_IDENTIFIER ) {
1194 		tag = ber_scanf( ber, "m", &vc2.vc_value );
1195 		if ( tag == LBER_ERROR || BER_BVISNULL( &vc2.vc_value ))
1196 			return rs->sr_err;
1197 	} else {
1198 		return rs->sr_err;
1199 	}
1200 	tag = ber_peek_tag( ber, &len );
1201 	if ( tag == LDAP_VLVCONTEXT_IDENTIFIER ) {
1202 		struct berval bv;
1203 		tag = ber_scanf( ber, "m", &bv );
1204 		if ( tag == LBER_ERROR || bv.bv_len != sizeof(vc2.vc_context))
1205 			return rs->sr_err;
1206 		AC_MEMCPY( &vc2.vc_context, bv.bv_val, bv.bv_len );
1207 	} else {
1208 		vc2.vc_context = 0;
1209 	}
1210 
1211 	vc = op->o_tmpalloc( sizeof(vlv_ctrl), op->o_tmpmemctx );
1212 	*vc = vc2;
1213 	op->o_controls[vlv_cid] = vc;
1214 	rs->sr_err = LDAP_SUCCESS;
1215 
1216 	return rs->sr_err;
1217 }
1218 
1219 static int sssvlv_connection_destroy( BackendDB *be, Connection *conn )
1220 {
1221 	slap_overinst	*on		= (slap_overinst *)be->bd_info;
1222 	sssvlv_info *si = on->on_bi.bi_private;
1223 
1224 	if ( sort_conns[conn->c_conn_idx] ) {
1225 		free_sort_ops( conn, sort_conns[conn->c_conn_idx], si->svi_max_percon );
1226 	}
1227 
1228 	return LDAP_SUCCESS;
1229 }
1230 
1231 static int sssvlv_db_open(
1232 	BackendDB		*be,
1233 	ConfigReply		*cr )
1234 {
1235 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1236 	sssvlv_info *si = on->on_bi.bi_private;
1237 	int rc;
1238 	int conn_index;
1239 
1240 	/* If not set, default to 1/2 of available threads */
1241 	if ( !si->svi_max )
1242 		si->svi_max = connection_pool_max / 2;
1243 
1244 	if ( dtblsize && !sort_conns ) {
1245 		ldap_pvt_thread_mutex_init( &sort_conns_mutex );
1246 		/* accommodate for c_conn_idx == -1 */
1247 		sort_conns = ch_calloc( dtblsize + 1, sizeof(sort_op **) );
1248 		for ( conn_index = 0 ; conn_index < dtblsize + 1 ; conn_index++ ) {
1249 			sort_conns[conn_index] = ch_calloc( si->svi_max_percon, sizeof(sort_op *) );
1250 		}
1251 		sort_conns++;
1252 	}
1253 
1254 	rc = overlay_register_control( be, LDAP_CONTROL_SORTREQUEST );
1255 	if ( rc == LDAP_SUCCESS )
1256 		rc = overlay_register_control( be, LDAP_CONTROL_VLVREQUEST );
1257 	return rc;
1258 }
1259 
1260 static ConfigTable sssvlv_cfg[] = {
1261 	{ "sssvlv-max", "num",
1262 		2, 2, 0, ARG_INT|ARG_OFFSET,
1263 			(void *)offsetof(sssvlv_info, svi_max),
1264 		"( OLcfgOvAt:21.1 NAME 'olcSssVlvMax' "
1265 			"DESC 'Maximum number of concurrent Sort requests' "
1266 			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
1267 	{ "sssvlv-maxkeys", "num",
1268 		2, 2, 0, ARG_INT|ARG_OFFSET,
1269 			(void *)offsetof(sssvlv_info, svi_max_keys),
1270 		"( OLcfgOvAt:21.2 NAME 'olcSssVlvMaxKeys' "
1271 			"DESC 'Maximum number of Keys in a Sort request' "
1272 			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
1273 	{ "sssvlv-maxperconn", "num",
1274 		2, 2, 0, ARG_INT|ARG_OFFSET,
1275 			(void *)offsetof(sssvlv_info, svi_max_percon),
1276 		"( OLcfgOvAt:21.3 NAME 'olcSssVlvMaxPerConn' "
1277 			"DESC 'Maximum number of concurrent paged search requests per connection' "
1278 			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
1279 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1280 };
1281 
1282 static ConfigOCs sssvlv_ocs[] = {
1283 	{ "( OLcfgOvOc:21.1 "
1284 		"NAME 'olcSssVlvConfig' "
1285 		"DESC 'SSS VLV configuration' "
1286 		"SUP olcOverlayConfig "
1287 		"MAY ( olcSssVlvMax $ olcSssVlvMaxKeys ) )",
1288 		Cft_Overlay, sssvlv_cfg, NULL, NULL },
1289 	{ NULL, 0, NULL }
1290 };
1291 
1292 static int sssvlv_db_init(
1293 	BackendDB		*be,
1294 	ConfigReply		*cr)
1295 {
1296 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1297 	sssvlv_info *si;
1298 
1299 	if ( ov_count == 0 ) {
1300 		int rc;
1301 
1302 		rc = register_supported_control2( LDAP_CONTROL_SORTREQUEST,
1303 			SLAP_CTRL_SEARCH,
1304 			NULL,
1305 			sss_parseCtrl,
1306 			1 /* replace */,
1307 			&sss_cid );
1308 		if ( rc != LDAP_SUCCESS ) {
1309 			Debug( LDAP_DEBUG_ANY, "Failed to register Sort Request control '%s' (%d)\n",
1310 				LDAP_CONTROL_SORTREQUEST, rc, 0 );
1311 			return rc;
1312 		}
1313 
1314 		rc = register_supported_control2( LDAP_CONTROL_VLVREQUEST,
1315 			SLAP_CTRL_SEARCH,
1316 			NULL,
1317 			vlv_parseCtrl,
1318 			1 /* replace */,
1319 			&vlv_cid );
1320 		if ( rc != LDAP_SUCCESS ) {
1321 			Debug( LDAP_DEBUG_ANY, "Failed to register VLV Request control '%s' (%d)\n",
1322 				LDAP_CONTROL_VLVREQUEST, rc, 0 );
1323 #ifdef SLAP_CONFIG_DELETE
1324 			overlay_unregister_control( be, LDAP_CONTROL_SORTREQUEST );
1325 			unregister_supported_control( LDAP_CONTROL_SORTREQUEST );
1326 #endif /* SLAP_CONFIG_DELETE */
1327 			return rc;
1328 		}
1329 	}
1330 
1331 	si = (sssvlv_info *)ch_malloc(sizeof(sssvlv_info));
1332 	on->on_bi.bi_private = si;
1333 
1334 	si->svi_max = 0;
1335 	si->svi_num = 0;
1336 	si->svi_max_keys = SSSVLV_DEFAULT_MAX_KEYS;
1337 	si->svi_max_percon = SSSVLV_DEFAULT_MAX_REQUEST_PER_CONN;
1338 
1339 	ov_count++;
1340 
1341 	return LDAP_SUCCESS;
1342 }
1343 
1344 static int sssvlv_db_destroy(
1345 	BackendDB		*be,
1346 	ConfigReply		*cr )
1347 {
1348 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1349 	sssvlv_info *si = (sssvlv_info *)on->on_bi.bi_private;
1350 	int conn_index;
1351 
1352 	ov_count--;
1353 	if ( !ov_count && sort_conns) {
1354 		sort_conns--;
1355 		for ( conn_index = 0 ; conn_index < dtblsize + 1 ; conn_index++ ) {
1356 			ch_free(sort_conns[conn_index]);
1357 		}
1358 		ch_free(sort_conns);
1359 		ldap_pvt_thread_mutex_destroy( &sort_conns_mutex );
1360 	}
1361 
1362 #ifdef SLAP_CONFIG_DELETE
1363 	overlay_unregister_control( be, LDAP_CONTROL_SORTREQUEST );
1364 	overlay_unregister_control( be, LDAP_CONTROL_VLVREQUEST );
1365 	if ( ov_count == 0 ) {
1366 		unregister_supported_control( LDAP_CONTROL_SORTREQUEST );
1367 		unregister_supported_control( LDAP_CONTROL_VLVREQUEST );
1368 	}
1369 #endif /* SLAP_CONFIG_DELETE */
1370 
1371 	if ( si ) {
1372 		ch_free( si );
1373 		on->on_bi.bi_private = NULL;
1374 	}
1375 	return LDAP_SUCCESS;
1376 }
1377 
1378 static slap_overinst sssvlv;
1379 
1380 int sssvlv_initialize()
1381 {
1382 	int rc;
1383 
1384 	sssvlv.on_bi.bi_type				= "sssvlv";
1385 	sssvlv.on_bi.bi_db_init				= sssvlv_db_init;
1386 	sssvlv.on_bi.bi_db_destroy			= sssvlv_db_destroy;
1387 	sssvlv.on_bi.bi_db_open				= sssvlv_db_open;
1388 	sssvlv.on_bi.bi_connection_destroy	= sssvlv_connection_destroy;
1389 	sssvlv.on_bi.bi_op_search			= sssvlv_op_search;
1390 
1391 	sssvlv.on_bi.bi_cf_ocs = sssvlv_ocs;
1392 
1393 	rc = config_register_schema( sssvlv_cfg, sssvlv_ocs );
1394 	if ( rc )
1395 		return rc;
1396 
1397 	rc = overlay_register( &sssvlv );
1398 	if ( rc != LDAP_SUCCESS ) {
1399 		Debug( LDAP_DEBUG_ANY, "Failed to register server side sort overlay\n", 0, 0, 0 );
1400 	}
1401 
1402 	return rc;
1403 }
1404 
1405 #if SLAPD_OVER_SSSVLV == SLAPD_MOD_DYNAMIC
1406 int init_module( int argc, char *argv[])
1407 {
1408 	return sssvlv_initialize();
1409 }
1410 #endif
1411 
1412 #endif /* SLAPD_OVER_SSSVLV */
1413