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