1 /* $NetBSD: sssvlv.c,v 1.4 2025/09/05 21:16:32 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-2024 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.4 2025/09/05 21:16:32 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 */ 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 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 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 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 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 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 */ 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 1438 int init_module( int argc, char *argv[]) 1439 { 1440 return sssvlv_initialize(); 1441 } 1442 #endif 1443 1444 #endif /* SLAPD_OVER_SSSVLV */ 1445