search.c revision 1.1.1.3 1 /* $NetBSD: search.c,v 1.1.1.3 2018/02/06 01:53:17 christos Exp $ */
2
3 /* search.c - search operation */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2017 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19 #include <sys/cdefs.h>
20 __RCSID("$NetBSD: search.c,v 1.1.1.3 2018/02/06 01:53:17 christos Exp $");
21
22 #include "portable.h"
23
24 #include <stdio.h>
25 #include <ac/string.h>
26
27 #include "back-mdb.h"
28 #include "idl.h"
29
30 static int base_candidate(
31 BackendDB *be,
32 Entry *e,
33 ID *ids );
34
35 static int search_candidates(
36 Operation *op,
37 SlapReply *rs,
38 Entry *e,
39 IdScopes *isc,
40 MDB_cursor *mci,
41 ID *ids,
42 ID *stack );
43
44 static int parse_paged_cookie( Operation *op, SlapReply *rs );
45
46 static void send_paged_response(
47 Operation *op,
48 SlapReply *rs,
49 ID *lastid,
50 int tentries );
51
52 /* Dereference aliases for a single alias entry. Return the final
53 * dereferenced entry on success, NULL on any failure.
54 */
55 static Entry * deref_base (
56 Operation *op,
57 SlapReply *rs,
58 Entry *e,
59 Entry **matched,
60 MDB_txn *txn,
61 ID *tmp,
62 ID *visited )
63 {
64 struct berval ndn;
65
66 rs->sr_err = LDAP_ALIAS_DEREF_PROBLEM;
67 rs->sr_text = "maximum deref depth exceeded";
68
69 for (;;) {
70 /* Remember the last entry we looked at, so we can
71 * report broken links
72 */
73 *matched = e;
74
75 if (MDB_IDL_N(tmp) >= op->o_bd->be_max_deref_depth) {
76 e = NULL;
77 break;
78 }
79
80 /* If this is part of a subtree or onelevel search,
81 * have we seen this ID before? If so, quit.
82 */
83 if ( visited && mdb_idl_insert( visited, e->e_id ) ) {
84 e = NULL;
85 break;
86 }
87
88 /* If we've seen this ID during this deref iteration,
89 * we've hit a loop.
90 */
91 if ( mdb_idl_insert( tmp, e->e_id ) ) {
92 rs->sr_err = LDAP_ALIAS_PROBLEM;
93 rs->sr_text = "circular alias";
94 e = NULL;
95 break;
96 }
97
98 /* If there was a problem getting the aliasedObjectName,
99 * get_alias_dn will have set the error status.
100 */
101 if ( get_alias_dn(e, &ndn, &rs->sr_err, &rs->sr_text) ) {
102 e = NULL;
103 break;
104 }
105
106 rs->sr_err = mdb_dn2entry( op, txn, NULL, &ndn, &e, NULL, 0 );
107 if (rs->sr_err) {
108 rs->sr_err = LDAP_ALIAS_PROBLEM;
109 rs->sr_text = "aliasedObject not found";
110 break;
111 }
112
113 /* Free the previous entry, continue to work with the
114 * one we just retrieved.
115 */
116 mdb_entry_return( op, *matched );
117
118 /* We found a regular entry. Return this to the caller.
119 */
120 if (!is_entry_alias(e)) {
121 rs->sr_err = LDAP_SUCCESS;
122 rs->sr_text = NULL;
123 break;
124 }
125 }
126 return e;
127 }
128
129 /* Look for and dereference all aliases within the search scope.
130 * Requires "stack" to be able to hold 6 levels of DB_SIZE IDLs.
131 * Of course we're hardcoded to require a minimum of 8 UM_SIZE
132 * IDLs so this is never a problem.
133 */
134 static int search_aliases(
135 Operation *op,
136 SlapReply *rs,
137 ID e_id,
138 IdScopes *isc,
139 MDB_cursor *mci,
140 ID *stack )
141 {
142 ID *aliases, *curscop, *visited, *newsubs, *oldsubs, *tmp;
143 ID cursora, ida, cursoro, ido;
144 Entry *matched, *a;
145 struct berval bv_alias = BER_BVC( "alias" );
146 AttributeAssertion aa_alias = ATTRIBUTEASSERTION_INIT;
147 Filter af;
148
149 aliases = stack; /* IDL of all aliases in the database */
150 curscop = aliases + MDB_IDL_DB_SIZE; /* Aliases in the current scope */
151 visited = curscop + MDB_IDL_DB_SIZE; /* IDs we've seen in this search */
152 newsubs = visited + MDB_IDL_DB_SIZE; /* New subtrees we've added */
153 oldsubs = newsubs + MDB_IDL_DB_SIZE; /* Subtrees added previously */
154 tmp = oldsubs + MDB_IDL_DB_SIZE; /* Scratch space for deref_base() */
155
156 af.f_choice = LDAP_FILTER_EQUALITY;
157 af.f_ava = &aa_alias;
158 af.f_av_desc = slap_schema.si_ad_objectClass;
159 af.f_av_value = bv_alias;
160 af.f_next = NULL;
161
162 /* Find all aliases in database */
163 MDB_IDL_ZERO( aliases );
164 rs->sr_err = mdb_filter_candidates( op, isc->mt, &af, aliases,
165 curscop, visited );
166 if (rs->sr_err != LDAP_SUCCESS || MDB_IDL_IS_ZERO( aliases )) {
167 return rs->sr_err;
168 }
169 oldsubs[0] = 1;
170 oldsubs[1] = e_id;
171
172 MDB_IDL_ZERO( visited );
173 MDB_IDL_ZERO( newsubs );
174
175 cursoro = 0;
176 ido = mdb_idl_first( oldsubs, &cursoro );
177
178 for (;;) {
179 /* Set curscop to only the aliases in the current scope. Start with
180 * all the aliases, then get the intersection with the scope.
181 */
182 rs->sr_err = mdb_idscope( op, isc->mt, e_id, aliases, curscop );
183
184 /* Dereference all of the aliases in the current scope. */
185 cursora = 0;
186 for (ida = mdb_idl_first(curscop, &cursora); ida != NOID;
187 ida = mdb_idl_next(curscop, &cursora))
188 {
189 rs->sr_err = mdb_id2entry(op, mci, ida, &a);
190 if (rs->sr_err != LDAP_SUCCESS) {
191 continue;
192 }
193
194 /* This should only happen if the curscop IDL has maxed out and
195 * turned into a range that spans IDs indiscriminately
196 */
197 if (!is_entry_alias(a)) {
198 mdb_entry_return(op, a);
199 continue;
200 }
201
202 /* Actually dereference the alias */
203 MDB_IDL_ZERO(tmp);
204 a = deref_base( op, rs, a, &matched, isc->mt,
205 tmp, visited );
206 if (a) {
207 /* If the target was not already in our current scopes,
208 * make note of it in the newsubs list.
209 */
210 ID2 mid;
211 mid.mid = a->e_id;
212 mid.mval.mv_data = NULL;
213 if (op->ors_scope == LDAP_SCOPE_SUBTREE) {
214 isc->id = a->e_id;
215 /* if ID is a child of any of our current scopes,
216 * ignore it, it's already included.
217 */
218 if (mdb_idscopechk(op, isc))
219 goto skip;
220 }
221 if (mdb_id2l_insert(isc->scopes, &mid) == 0) {
222 mdb_idl_insert(newsubs, a->e_id);
223 }
224 skip: mdb_entry_return( op, a );
225
226 } else if (matched) {
227 /* Alias could not be dereferenced, or it deref'd to
228 * an ID we've already seen. Ignore it.
229 */
230 mdb_entry_return( op, matched );
231 rs->sr_text = NULL;
232 rs->sr_err = 0;
233 }
234 }
235 /* If this is a OneLevel search, we're done; oldsubs only had one
236 * ID in it. For a Subtree search, oldsubs may be a list of scope IDs.
237 */
238 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) break;
239 nextido:
240 ido = mdb_idl_next( oldsubs, &cursoro );
241
242 /* If we're done processing the old scopes, did we add any new
243 * scopes in this iteration? If so, go back and do those now.
244 */
245 if (ido == NOID) {
246 if (MDB_IDL_IS_ZERO(newsubs)) break;
247 MDB_IDL_CPY(oldsubs, newsubs);
248 MDB_IDL_ZERO(newsubs);
249 cursoro = 0;
250 ido = mdb_idl_first( oldsubs, &cursoro );
251 }
252
253 /* Find the entry corresponding to the next scope. If it can't
254 * be found, ignore it and move on. This should never happen;
255 * we should never see the ID of an entry that doesn't exist.
256 */
257 {
258 MDB_val edata;
259 rs->sr_err = mdb_id2edata(op, mci, ido, &edata);
260 if ( rs->sr_err != MDB_SUCCESS ) {
261 goto nextido;
262 }
263 e_id = ido;
264 }
265 }
266 return rs->sr_err;
267 }
268
269 /* Get the next ID from the DB. Used if the candidate list is
270 * a range and simple iteration hits missing entryIDs
271 */
272 static int
273 mdb_get_nextid(MDB_cursor *mci, ID *cursor)
274 {
275 MDB_val key;
276 ID id;
277 int rc;
278
279 id = *cursor + 1;
280 key.mv_data = &id;
281 key.mv_size = sizeof(ID);
282 rc = mdb_cursor_get( mci, &key, NULL, MDB_SET_RANGE );
283 if ( rc )
284 return rc;
285 memcpy( cursor, key.mv_data, sizeof(ID));
286 return 0;
287 }
288
289 static void scope_chunk_free( void *key, void *data )
290 {
291 ID2 *p1, *p2;
292 for (p1 = data; p1; p1 = p2) {
293 p2 = p1[0].mval.mv_data;
294 ber_memfree_x(p1, NULL);
295 }
296 }
297
298 static ID2 *scope_chunk_get( Operation *op )
299 {
300 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
301 ID2 *ret = NULL;
302
303 ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
304 (void *)&ret, NULL );
305 if ( !ret ) {
306 ret = ch_malloc( MDB_IDL_UM_SIZE * sizeof( ID2 ));
307 } else {
308 void *r2 = ret[0].mval.mv_data;
309 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
310 r2, scope_chunk_free, NULL, NULL );
311 }
312 return ret;
313 }
314
315 static void scope_chunk_ret( Operation *op, ID2 *scopes )
316 {
317 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
318 void *ret = NULL;
319
320 ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
321 &ret, NULL );
322 scopes[0].mval.mv_data = ret;
323 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
324 (void *)scopes, scope_chunk_free, NULL, NULL );
325 }
326
327 static void *search_stack( Operation *op );
328
329 typedef struct ww_ctx {
330 MDB_txn *txn;
331 MDB_cursor *mcd; /* if set, save cursor context */
332 ID key;
333 MDB_val data;
334 int flag;
335 int nentries;
336 } ww_ctx;
337
338 /* ITS#7904 if we get blocked while writing results to client,
339 * release the current reader txn and reacquire it after we
340 * unblock.
341 * Slight problem - if we're doing a scope-based walk (mdb_dn2id_walk)
342 * to return results, we need to remember the state of the mcd cursor.
343 * If the node that cursor was pointing to gets deleted while we're
344 * blocked, we may be unable to restore the cursor position. In that
345 * case return an LDAP_BUSY error - let the client know this search
346 * couldn't succeed, but might succeed on a retry.
347 */
348 static void
349 mdb_rtxn_snap( Operation *op, ww_ctx *ww )
350 {
351 /* save cursor position and release read txn */
352 if ( ww->mcd ) {
353 MDB_val key, data;
354 mdb_cursor_get( ww->mcd, &key, &data, MDB_GET_CURRENT );
355 memcpy( &ww->key, key.mv_data, sizeof(ID) );
356 ww->data.mv_size = data.mv_size;
357 ww->data.mv_data = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
358 memcpy(ww->data.mv_data, data.mv_data, data.mv_size);
359 }
360 mdb_txn_reset( ww->txn );
361 ww->flag = 1;
362 }
363
364 static void
365 mdb_writewait( Operation *op, slap_callback *sc )
366 {
367 ww_ctx *ww = sc->sc_private;
368 if ( !ww->flag ) {
369 mdb_rtxn_snap( op, ww );
370 }
371 }
372
373 static int
374 mdb_waitfixup( Operation *op, ww_ctx *ww, MDB_cursor *mci, MDB_cursor *mcd, IdScopes *isc )
375 {
376 MDB_val key;
377 int rc = 0;
378 ww->flag = 0;
379 mdb_txn_renew( ww->txn );
380 mdb_cursor_renew( ww->txn, mci );
381 mdb_cursor_renew( ww->txn, mcd );
382
383 key.mv_size = sizeof(ID);
384 if ( ww->mcd ) { /* scope-based search using dn2id_walk */
385 MDB_val data;
386
387 if ( isc->numrdns )
388 mdb_dn2id_wrestore( op, isc );
389
390 key.mv_data = &ww->key;
391 data = ww->data;
392 rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH );
393 if ( rc == MDB_NOTFOUND ) {
394 data = ww->data;
395 rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH_RANGE );
396 /* the loop will skip this node using NEXT_DUP but we want it
397 * sent, so go back one space first
398 */
399 if ( rc == MDB_SUCCESS )
400 mdb_cursor_get( mcd, &key, &data, MDB_PREV_DUP );
401 else
402 rc = LDAP_BUSY;
403 } else if ( rc ) {
404 rc = LDAP_OTHER;
405 }
406 op->o_tmpfree( ww->data.mv_data, op->o_tmpmemctx );
407 ww->data.mv_data = NULL;
408 } else if ( isc->scopes[0].mid > 1 ) { /* candidate-based search */
409 int i;
410 for ( i=1; i<isc->scopes[0].mid; i++ ) {
411 if ( !isc->scopes[i].mval.mv_data )
412 continue;
413 key.mv_data = &isc->scopes[i].mid;
414 mdb_cursor_get( mcd, &key, &isc->scopes[i].mval, MDB_SET );
415 }
416 }
417 return rc;
418 }
419
420 int
421 mdb_search( Operation *op, SlapReply *rs )
422 {
423 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
424 ID id, cursor, nsubs, ncand, cscope;
425 ID lastid = NOID;
426 ID candidates[MDB_IDL_UM_SIZE];
427 ID iscopes[MDB_IDL_DB_SIZE];
428 ID2 *scopes;
429 void *stack;
430 Entry *e = NULL, *base = NULL;
431 Entry *matched = NULL;
432 AttributeName *attrs;
433 slap_mask_t mask;
434 time_t stoptime;
435 int manageDSAit;
436 int tentries = 0;
437 IdScopes isc;
438 MDB_cursor *mci, *mcd;
439 ww_ctx wwctx;
440 slap_callback cb = { 0 };
441
442 mdb_op_info opinfo = {{{0}}}, *moi = &opinfo;
443 MDB_txn *ltid = NULL;
444
445 Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_search) "\n", 0, 0, 0);
446 attrs = op->oq_search.rs_attrs;
447
448 manageDSAit = get_manageDSAit( op );
449
450 rs->sr_err = mdb_opinfo_get( op, mdb, 1, &moi );
451 switch(rs->sr_err) {
452 case 0:
453 break;
454 default:
455 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
456 return rs->sr_err;
457 }
458
459 ltid = moi->moi_txn;
460
461 rs->sr_err = mdb_cursor_open( ltid, mdb->mi_id2entry, &mci );
462 if ( rs->sr_err ) {
463 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
464 return rs->sr_err;
465 }
466
467 rs->sr_err = mdb_cursor_open( ltid, mdb->mi_dn2id, &mcd );
468 if ( rs->sr_err ) {
469 mdb_cursor_close( mci );
470 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
471 return rs->sr_err;
472 }
473
474 scopes = scope_chunk_get( op );
475 stack = search_stack( op );
476 isc.mt = ltid;
477 isc.mc = mcd;
478 isc.scopes = scopes;
479 isc.oscope = op->ors_scope;
480 isc.sctmp = stack;
481
482 if ( op->ors_deref & LDAP_DEREF_FINDING ) {
483 MDB_IDL_ZERO(candidates);
484 }
485 dn2entry_retry:
486 /* get entry with reader lock */
487 rs->sr_err = mdb_dn2entry( op, ltid, mcd, &op->o_req_ndn, &e, &nsubs, 1 );
488
489 switch(rs->sr_err) {
490 case MDB_NOTFOUND:
491 matched = e;
492 e = NULL;
493 break;
494 case 0:
495 break;
496 case LDAP_BUSY:
497 send_ldap_error( op, rs, LDAP_BUSY, "ldap server busy" );
498 goto done;
499 default:
500 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
501 goto done;
502 }
503
504 if ( op->ors_deref & LDAP_DEREF_FINDING ) {
505 if ( matched && is_entry_alias( matched )) {
506 struct berval stub;
507
508 stub.bv_val = op->o_req_ndn.bv_val;
509 stub.bv_len = op->o_req_ndn.bv_len - matched->e_nname.bv_len - 1;
510 e = deref_base( op, rs, matched, &matched, ltid,
511 candidates, NULL );
512 if ( e ) {
513 build_new_dn( &op->o_req_ndn, &e->e_nname, &stub,
514 op->o_tmpmemctx );
515 mdb_entry_return(op, e);
516 matched = NULL;
517 goto dn2entry_retry;
518 }
519 } else if ( e && is_entry_alias( e )) {
520 e = deref_base( op, rs, e, &matched, ltid,
521 candidates, NULL );
522 }
523 }
524
525 if ( e == NULL ) {
526 struct berval matched_dn = BER_BVNULL;
527
528 if ( matched != NULL ) {
529 BerVarray erefs = NULL;
530
531 /* return referral only if "disclose"
532 * is granted on the object */
533 if ( ! access_allowed( op, matched,
534 slap_schema.si_ad_entry,
535 NULL, ACL_DISCLOSE, NULL ) )
536 {
537 rs->sr_err = LDAP_NO_SUCH_OBJECT;
538
539 } else {
540 ber_dupbv( &matched_dn, &matched->e_name );
541
542 erefs = is_entry_referral( matched )
543 ? get_entry_referrals( op, matched )
544 : NULL;
545 if ( rs->sr_err == MDB_NOTFOUND )
546 rs->sr_err = LDAP_REFERRAL;
547 rs->sr_matched = matched_dn.bv_val;
548 }
549
550 mdb_entry_return(op, matched);
551 matched = NULL;
552
553 if ( erefs ) {
554 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
555 &op->o_req_dn, op->oq_search.rs_scope );
556 ber_bvarray_free( erefs );
557 }
558
559 } else {
560 rs->sr_ref = referral_rewrite( default_referral,
561 NULL, &op->o_req_dn, op->oq_search.rs_scope );
562 rs->sr_err = rs->sr_ref != NULL ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT;
563 }
564
565 send_ldap_result( op, rs );
566
567 if ( rs->sr_ref ) {
568 ber_bvarray_free( rs->sr_ref );
569 rs->sr_ref = NULL;
570 }
571 if ( !BER_BVISNULL( &matched_dn ) ) {
572 ber_memfree( matched_dn.bv_val );
573 rs->sr_matched = NULL;
574 }
575 goto done;
576 }
577
578 /* NOTE: __NEW__ "search" access is required
579 * on searchBase object */
580 if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
581 NULL, ACL_SEARCH, NULL, &mask ) )
582 {
583 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
584 rs->sr_err = LDAP_NO_SUCH_OBJECT;
585 } else {
586 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
587 }
588
589 mdb_entry_return( op,e);
590 send_ldap_result( op, rs );
591 goto done;
592 }
593
594 if ( !manageDSAit && is_entry_referral( e ) ) {
595 /* entry is a referral */
596 struct berval matched_dn = BER_BVNULL;
597 BerVarray erefs = NULL;
598
599 ber_dupbv( &matched_dn, &e->e_name );
600 erefs = get_entry_referrals( op, e );
601
602 rs->sr_err = LDAP_REFERRAL;
603
604 mdb_entry_return( op, e );
605 e = NULL;
606
607 if ( erefs ) {
608 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
609 &op->o_req_dn, op->oq_search.rs_scope );
610 ber_bvarray_free( erefs );
611
612 if ( !rs->sr_ref ) {
613 rs->sr_text = "bad_referral object";
614 }
615 }
616
617 Debug( LDAP_DEBUG_TRACE,
618 LDAP_XSTRING(mdb_search) ": entry is referral\n",
619 0, 0, 0 );
620
621 rs->sr_matched = matched_dn.bv_val;
622 send_ldap_result( op, rs );
623
624 ber_bvarray_free( rs->sr_ref );
625 rs->sr_ref = NULL;
626 ber_memfree( matched_dn.bv_val );
627 rs->sr_matched = NULL;
628 goto done;
629 }
630
631 if ( get_assert( op ) &&
632 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
633 {
634 rs->sr_err = LDAP_ASSERTION_FAILED;
635 mdb_entry_return( op,e);
636 send_ldap_result( op, rs );
637 goto done;
638 }
639
640 /* compute it anyway; root does not use it */
641 stoptime = op->o_time + op->ors_tlimit;
642
643 base = e;
644
645 e = NULL;
646
647 /* select candidates */
648 if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
649 rs->sr_err = base_candidate( op->o_bd, base, candidates );
650 scopes[0].mid = 0;
651 ncand = 1;
652 } else {
653 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
654 size_t nkids;
655 MDB_val key, data;
656 key.mv_data = &base->e_id;
657 key.mv_size = sizeof( ID );
658 mdb_cursor_get( mcd, &key, &data, MDB_SET );
659 mdb_cursor_count( mcd, &nkids );
660 nsubs = nkids - 1;
661 } else if ( !base->e_id ) {
662 /* we don't maintain nsubs for entryID 0.
663 * just grab entry count from id2entry stat
664 */
665 MDB_stat ms;
666 mdb_stat( ltid, mdb->mi_id2entry, &ms );
667 nsubs = ms.ms_entries;
668 }
669 MDB_IDL_ZERO( candidates );
670 scopes[0].mid = 1;
671 scopes[1].mid = base->e_id;
672 scopes[1].mval.mv_data = NULL;
673 rs->sr_err = search_candidates( op, rs, base,
674 &isc, mci, candidates, stack );
675 ncand = MDB_IDL_N( candidates );
676 if ( !base->e_id || ncand == NOID ) {
677 /* grab entry count from id2entry stat
678 */
679 MDB_stat ms;
680 mdb_stat( ltid, mdb->mi_id2entry, &ms );
681 if ( !base->e_id )
682 nsubs = ms.ms_entries;
683 if ( ncand == NOID )
684 ncand = ms.ms_entries;
685 }
686 }
687
688 /* start cursor at beginning of candidates.
689 */
690 cursor = 0;
691
692 if ( candidates[0] == 0 ) {
693 Debug( LDAP_DEBUG_TRACE,
694 LDAP_XSTRING(mdb_search) ": no candidates\n",
695 0, 0, 0 );
696
697 goto nochange;
698 }
699
700 /* if not root and candidates exceed to-be-checked entries, abort */
701 if ( op->ors_limit /* isroot == FALSE */ &&
702 op->ors_limit->lms_s_unchecked != -1 &&
703 ncand > (unsigned) op->ors_limit->lms_s_unchecked )
704 {
705 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
706 send_ldap_result( op, rs );
707 rs->sr_err = LDAP_SUCCESS;
708 goto done;
709 }
710
711 if ( op->ors_limit == NULL /* isroot == TRUE */ ||
712 !op->ors_limit->lms_s_pr_hide )
713 {
714 tentries = ncand;
715 }
716
717 wwctx.flag = 0;
718 /* If we're running in our own read txn */
719 if ( moi == &opinfo ) {
720 cb.sc_writewait = mdb_writewait;
721 cb.sc_private = &wwctx;
722 wwctx.txn = ltid;
723 wwctx.mcd = NULL;
724 cb.sc_next = op->o_callback;
725 op->o_callback = &cb;
726 }
727
728 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
729 PagedResultsState *ps = op->o_pagedresults_state;
730 /* deferred cookie parsing */
731 rs->sr_err = parse_paged_cookie( op, rs );
732 if ( rs->sr_err != LDAP_SUCCESS ) {
733 send_ldap_result( op, rs );
734 goto done;
735 }
736
737 cursor = (ID) ps->ps_cookie;
738 if ( cursor && ps->ps_size == 0 ) {
739 rs->sr_err = LDAP_SUCCESS;
740 rs->sr_text = "search abandoned by pagedResult size=0";
741 send_ldap_result( op, rs );
742 goto done;
743 }
744 id = mdb_idl_first( candidates, &cursor );
745 if ( id == NOID ) {
746 Debug( LDAP_DEBUG_TRACE,
747 LDAP_XSTRING(mdb_search)
748 ": no paged results candidates\n",
749 0, 0, 0 );
750 send_paged_response( op, rs, &lastid, 0 );
751
752 rs->sr_err = LDAP_OTHER;
753 goto done;
754 }
755 if ( id == (ID)ps->ps_cookie )
756 id = mdb_idl_next( candidates, &cursor );
757 nsubs = ncand; /* always bypass scope'd search */
758 goto loop_begin;
759 }
760 if ( nsubs < ncand ) {
761 int rc;
762 /* Do scope-based search */
763
764 /* if any alias scopes were set, save them */
765 if (scopes[0].mid > 1) {
766 cursor = 1;
767 for (cscope = 1; cscope <= scopes[0].mid; cscope++) {
768 /* Ignore the original base */
769 if (scopes[cscope].mid == base->e_id)
770 continue;
771 iscopes[cursor++] = scopes[cscope].mid;
772 }
773 iscopes[0] = scopes[0].mid - 1;
774 } else {
775 iscopes[0] = 0;
776 }
777
778 wwctx.mcd = mcd;
779 isc.id = base->e_id;
780 isc.numrdns = 0;
781 rc = mdb_dn2id_walk( op, &isc );
782 if ( rc )
783 id = NOID;
784 else
785 id = isc.id;
786 cscope = 0;
787 } else {
788 id = mdb_idl_first( candidates, &cursor );
789 }
790
791 while (id != NOID)
792 {
793 int scopeok;
794 MDB_val edata;
795
796 loop_begin:
797
798 /* check for abandon */
799 if ( op->o_abandon ) {
800 rs->sr_err = SLAPD_ABANDON;
801 send_ldap_result( op, rs );
802 goto done;
803 }
804
805 /* mostly needed by internal searches,
806 * e.g. related to syncrepl, for whom
807 * abandon does not get set... */
808 if ( slapd_shutdown ) {
809 rs->sr_err = LDAP_UNAVAILABLE;
810 send_ldap_disconnect( op, rs );
811 goto done;
812 }
813
814 /* check time limit */
815 if ( op->ors_tlimit != SLAP_NO_LIMIT
816 && slap_get_time() > stoptime )
817 {
818 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
819 rs->sr_ref = rs->sr_v2ref;
820 send_ldap_result( op, rs );
821 rs->sr_err = LDAP_SUCCESS;
822 goto done;
823 }
824
825
826 if ( nsubs < ncand ) {
827 unsigned i;
828 /* Is this entry in the candidate list? */
829 scopeok = 0;
830 if (MDB_IDL_IS_RANGE( candidates )) {
831 if ( id >= MDB_IDL_RANGE_FIRST( candidates ) &&
832 id <= MDB_IDL_RANGE_LAST( candidates ))
833 scopeok = 1;
834 } else {
835 i = mdb_idl_search( candidates, id );
836 if (i <= candidates[0] && candidates[i] == id )
837 scopeok = 1;
838 }
839 if ( scopeok )
840 goto scopeok;
841 goto loop_continue;
842 }
843
844 /* Does this candidate actually satisfy the search scope?
845 */
846 scopeok = 0;
847 isc.numrdns = 0;
848 switch( op->ors_scope ) {
849 case LDAP_SCOPE_BASE:
850 /* This is always true, yes? */
851 if ( id == base->e_id ) scopeok = 1;
852 break;
853
854 #ifdef LDAP_SCOPE_CHILDREN
855 case LDAP_SCOPE_CHILDREN:
856 if ( id == base->e_id ) break;
857 /* Fall-thru */
858 #endif
859 case LDAP_SCOPE_SUBTREE:
860 if ( id == base->e_id ) {
861 scopeok = 1;
862 break;
863 }
864 /* Fall-thru */
865 case LDAP_SCOPE_ONELEVEL:
866 if ( id == base->e_id ) break;
867 isc.id = id;
868 isc.nscope = 0;
869 rs->sr_err = mdb_idscopes( op, &isc );
870 if ( rs->sr_err == MDB_SUCCESS ) {
871 if ( isc.nscope )
872 scopeok = 1;
873 } else {
874 if ( rs->sr_err == MDB_NOTFOUND )
875 goto notfound;
876 }
877 break;
878 }
879
880 /* Not in scope, ignore it */
881 if ( !scopeok )
882 {
883 Debug( LDAP_DEBUG_TRACE,
884 LDAP_XSTRING(mdb_search)
885 ": %ld scope not okay\n",
886 (long) id, 0, 0 );
887 goto loop_continue;
888 }
889
890 scopeok:
891 if ( id == base->e_id ) {
892 e = base;
893 } else {
894
895 /* get the entry */
896 rs->sr_err = mdb_id2edata( op, mci, id, &edata );
897 if ( rs->sr_err == MDB_NOTFOUND ) {
898 notfound:
899 if( nsubs < ncand )
900 goto loop_continue;
901
902 if( !MDB_IDL_IS_RANGE(candidates) ) {
903 /* only complain for non-range IDLs */
904 Debug( LDAP_DEBUG_TRACE,
905 LDAP_XSTRING(mdb_search)
906 ": candidate %ld not found\n",
907 (long) id, 0, 0 );
908 } else {
909 /* get the next ID from the DB */
910 rs->sr_err = mdb_get_nextid( mci, &cursor );
911 if ( rs->sr_err == MDB_NOTFOUND ) {
912 break;
913 }
914 if ( rs->sr_err ) {
915 rs->sr_err = LDAP_OTHER;
916 rs->sr_text = "internal error in get_nextid";
917 send_ldap_result( op, rs );
918 goto done;
919 }
920 cursor--;
921 }
922
923 goto loop_continue;
924 } else if ( rs->sr_err ) {
925 rs->sr_err = LDAP_OTHER;
926 rs->sr_text = "internal error in mdb_id2edata";
927 send_ldap_result( op, rs );
928 goto done;
929 }
930
931 rs->sr_err = mdb_entry_decode( op, ltid, &edata, &e );
932 if ( rs->sr_err ) {
933 rs->sr_err = LDAP_OTHER;
934 rs->sr_text = "internal error in mdb_entry_decode";
935 send_ldap_result( op, rs );
936 goto done;
937 }
938 e->e_id = id;
939 e->e_name.bv_val = NULL;
940 e->e_nname.bv_val = NULL;
941 }
942
943 if ( is_entry_subentry( e ) ) {
944 if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
945 if(!get_subentries_visibility( op )) {
946 /* only subentries are visible */
947 goto loop_continue;
948 }
949
950 } else if ( get_subentries( op ) &&
951 !get_subentries_visibility( op ))
952 {
953 /* only subentries are visible */
954 goto loop_continue;
955 }
956
957 } else if ( get_subentries_visibility( op )) {
958 /* only subentries are visible */
959 goto loop_continue;
960 }
961
962 /* aliases were already dereferenced in candidate list */
963 if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
964 /* but if the search base is an alias, and we didn't
965 * deref it when finding, return it.
966 */
967 if ( is_entry_alias(e) &&
968 ((op->ors_deref & LDAP_DEREF_FINDING) || e != base ))
969 {
970 goto loop_continue;
971 }
972 }
973
974 if ( !manageDSAit && is_entry_glue( e )) {
975 goto loop_continue;
976 }
977
978 if (e != base) {
979 struct berval pdn, pndn;
980 char *d, *n;
981 int i;
982
983 /* child of base, just append RDNs to base->e_name */
984 if ( nsubs < ncand || isc.scopes[isc.nscope].mid == base->e_id ) {
985 pdn = base->e_name;
986 pndn = base->e_nname;
987 } else {
988 mdb_id2name( op, ltid, &isc.mc, scopes[isc.nscope].mid, &pdn, &pndn );
989 }
990 e->e_name.bv_len = pdn.bv_len;
991 e->e_nname.bv_len = pndn.bv_len;
992 for (i=0; i<isc.numrdns; i++) {
993 e->e_name.bv_len += isc.rdns[i].bv_len + 1;
994 e->e_nname.bv_len += isc.nrdns[i].bv_len + 1;
995 }
996 e->e_name.bv_val = op->o_tmpalloc(e->e_name.bv_len + 1, op->o_tmpmemctx);
997 e->e_nname.bv_val = op->o_tmpalloc(e->e_nname.bv_len + 1, op->o_tmpmemctx);
998 d = e->e_name.bv_val;
999 n = e->e_nname.bv_val;
1000 if (nsubs < ncand) {
1001 /* RDNs are in top-down order */
1002 for (i=isc.numrdns-1; i>=0; i--) {
1003 memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
1004 d += isc.rdns[i].bv_len;
1005 *d++ = ',';
1006 memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
1007 n += isc.nrdns[i].bv_len;
1008 *n++ = ',';
1009 }
1010 } else {
1011 /* RDNs are in bottom-up order */
1012 for (i=0; i<isc.numrdns; i++) {
1013 memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
1014 d += isc.rdns[i].bv_len;
1015 *d++ = ',';
1016 memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
1017 n += isc.nrdns[i].bv_len;
1018 *n++ = ',';
1019 }
1020 }
1021
1022 if (pdn.bv_len) {
1023 memcpy(d, pdn.bv_val, pdn.bv_len+1);
1024 memcpy(n, pndn.bv_val, pndn.bv_len+1);
1025 } else {
1026 *--d = '\0';
1027 *--n = '\0';
1028 e->e_name.bv_len--;
1029 e->e_nname.bv_len--;
1030 }
1031 if (pndn.bv_val != base->e_nname.bv_val) {
1032 op->o_tmpfree(pndn.bv_val, op->o_tmpmemctx);
1033 op->o_tmpfree(pdn.bv_val, op->o_tmpmemctx);
1034 }
1035 }
1036
1037 /*
1038 * if it's a referral, add it to the list of referrals. only do
1039 * this for non-base searches, and don't check the filter
1040 * explicitly here since it's only a candidate anyway.
1041 */
1042 if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
1043 && is_entry_referral( e ) )
1044 {
1045 BerVarray erefs = get_entry_referrals( op, e );
1046 rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
1047 op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
1048 ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
1049
1050 rs->sr_entry = e;
1051 rs->sr_flags = 0;
1052
1053 send_search_reference( op, rs );
1054
1055 if (e != base)
1056 mdb_entry_return( op, e );
1057 rs->sr_entry = NULL;
1058 e = NULL;
1059
1060 ber_bvarray_free( rs->sr_ref );
1061 ber_bvarray_free( erefs );
1062 rs->sr_ref = NULL;
1063
1064 goto loop_continue;
1065 }
1066
1067 /* if it matches the filter and scope, send it */
1068 rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
1069
1070 if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
1071 /* check size limit */
1072 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1073 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
1074 if (e != base)
1075 mdb_entry_return( op, e );
1076 e = NULL;
1077 send_paged_response( op, rs, &lastid, tentries );
1078 goto done;
1079 }
1080 lastid = id;
1081 }
1082
1083 if (e) {
1084 /* safe default */
1085 rs->sr_attrs = op->oq_search.rs_attrs;
1086 rs->sr_operational_attrs = NULL;
1087 rs->sr_ctrls = NULL;
1088 rs->sr_entry = e;
1089 RS_ASSERT( e->e_private != NULL );
1090 rs->sr_flags = 0;
1091 rs->sr_err = LDAP_SUCCESS;
1092 rs->sr_err = send_search_entry( op, rs );
1093 rs->sr_attrs = NULL;
1094 rs->sr_entry = NULL;
1095 if (e != base)
1096 mdb_entry_return( op, e );
1097 e = NULL;
1098
1099 switch ( rs->sr_err ) {
1100 case LDAP_SUCCESS: /* entry sent ok */
1101 break;
1102 default: /* entry not sent */
1103 break;
1104 case LDAP_BUSY:
1105 send_ldap_result( op, rs );
1106 goto done;
1107 case LDAP_UNAVAILABLE:
1108 case LDAP_SIZELIMIT_EXCEEDED:
1109 if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ) {
1110 rs->sr_ref = rs->sr_v2ref;
1111 send_ldap_result( op, rs );
1112 rs->sr_err = LDAP_SUCCESS;
1113
1114 } else {
1115 rs->sr_err = LDAP_OTHER;
1116 }
1117 goto done;
1118 }
1119 }
1120
1121 } else {
1122 Debug( LDAP_DEBUG_TRACE,
1123 LDAP_XSTRING(mdb_search)
1124 ": %ld does not match filter\n",
1125 (long) id, 0, 0 );
1126 }
1127
1128 loop_continue:
1129 if ( moi == &opinfo && !wwctx.flag && mdb->mi_rtxn_size ) {
1130 wwctx.nentries++;
1131 if ( wwctx.nentries >= mdb->mi_rtxn_size ) {
1132 wwctx.nentries = 0;
1133 mdb_rtxn_snap( op, &wwctx );
1134 }
1135 }
1136 if ( wwctx.flag ) {
1137 rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd, &isc );
1138 if ( rs->sr_err ) {
1139 send_ldap_result( op, rs );
1140 goto done;
1141 }
1142 }
1143
1144 if( e != NULL ) {
1145 if ( e != base )
1146 mdb_entry_return( op, e );
1147 RS_ASSERT( rs->sr_entry == NULL );
1148 e = NULL;
1149 rs->sr_entry = NULL;
1150 }
1151
1152 if ( nsubs < ncand ) {
1153 int rc = mdb_dn2id_walk( op, &isc );
1154 if (rc) {
1155 id = NOID;
1156 /* We got to the end of a subtree. If there are any
1157 * alias scopes left, search them too.
1158 */
1159 while (iscopes[0] && cscope < iscopes[0]) {
1160 cscope++;
1161 isc.id = iscopes[cscope];
1162 if ( base )
1163 mdb_entry_return( op, base );
1164 rs->sr_err = mdb_id2entry(op, mci, isc.id, &base);
1165 if ( !rs->sr_err ) {
1166 mdb_id2name( op, ltid, &isc.mc, isc.id, &base->e_name, &base->e_nname );
1167 isc.numrdns = 0;
1168 if (isc.oscope == LDAP_SCOPE_ONELEVEL)
1169 isc.oscope = LDAP_SCOPE_BASE;
1170 rc = mdb_dn2id_walk( op, &isc );
1171 if ( !rc ) {
1172 id = isc.id;
1173 break;
1174 }
1175 }
1176 }
1177 } else
1178 id = isc.id;
1179 } else {
1180 id = mdb_idl_next( candidates, &cursor );
1181 }
1182 }
1183
1184 nochange:
1185 rs->sr_ctrls = NULL;
1186 rs->sr_ref = rs->sr_v2ref;
1187 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
1188 rs->sr_rspoid = NULL;
1189 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1190 send_paged_response( op, rs, NULL, 0 );
1191 } else {
1192 send_ldap_result( op, rs );
1193 }
1194
1195 rs->sr_err = LDAP_SUCCESS;
1196
1197 done:
1198 if ( cb.sc_private ) {
1199 /* remove our writewait callback */
1200 slap_callback **scp = &op->o_callback;
1201 while ( *scp ) {
1202 if ( *scp == &cb ) {
1203 *scp = cb.sc_next;
1204 cb.sc_private = NULL;
1205 break;
1206 }
1207 }
1208 }
1209 mdb_cursor_close( mcd );
1210 mdb_cursor_close( mci );
1211 if ( moi == &opinfo ) {
1212 mdb_txn_reset( moi->moi_txn );
1213 LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
1214 } else {
1215 moi->moi_ref--;
1216 }
1217 if( rs->sr_v2ref ) {
1218 ber_bvarray_free( rs->sr_v2ref );
1219 rs->sr_v2ref = NULL;
1220 }
1221 if (base)
1222 mdb_entry_return( op, base );
1223 scope_chunk_ret( op, scopes );
1224
1225 return rs->sr_err;
1226 }
1227
1228
1229 static int base_candidate(
1230 BackendDB *be,
1231 Entry *e,
1232 ID *ids )
1233 {
1234 Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
1235 e->e_nname.bv_val, (long) e->e_id, 0);
1236
1237 ids[0] = 1;
1238 ids[1] = e->e_id;
1239 return 0;
1240 }
1241
1242 /* Look for "objectClass Present" in this filter.
1243 * Also count depth of filter tree while we're at it.
1244 */
1245 static int oc_filter(
1246 Filter *f,
1247 int cur,
1248 int *max )
1249 {
1250 int rc = 0;
1251
1252 assert( f != NULL );
1253
1254 if( cur > *max ) *max = cur;
1255
1256 switch( f->f_choice ) {
1257 case LDAP_FILTER_PRESENT:
1258 if (f->f_desc == slap_schema.si_ad_objectClass) {
1259 rc = 1;
1260 }
1261 break;
1262
1263 case LDAP_FILTER_AND:
1264 case LDAP_FILTER_OR:
1265 cur++;
1266 for ( f=f->f_and; f; f=f->f_next ) {
1267 (void) oc_filter(f, cur, max);
1268 }
1269 break;
1270
1271 default:
1272 break;
1273 }
1274 return rc;
1275 }
1276
1277 static void search_stack_free( void *key, void *data )
1278 {
1279 ber_memfree_x(data, NULL);
1280 }
1281
1282 static void *search_stack( Operation *op )
1283 {
1284 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
1285 void *ret = NULL;
1286
1287 if ( op->o_threadctx ) {
1288 ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
1289 &ret, NULL );
1290 } else {
1291 ret = mdb->mi_search_stack;
1292 }
1293
1294 if ( !ret ) {
1295 ret = ch_malloc( mdb->mi_search_stack_depth * MDB_IDL_UM_SIZE
1296 * sizeof( ID ) );
1297 if ( op->o_threadctx ) {
1298 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
1299 ret, search_stack_free, NULL, NULL );
1300 } else {
1301 mdb->mi_search_stack = ret;
1302 }
1303 }
1304 return ret;
1305 }
1306
1307 static int search_candidates(
1308 Operation *op,
1309 SlapReply *rs,
1310 Entry *e,
1311 IdScopes *isc,
1312 MDB_cursor *mci,
1313 ID *ids,
1314 ID *stack )
1315 {
1316 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
1317 int rc, depth = 1;
1318 Filter *f, rf, xf, nf, sf;
1319 AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
1320 AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
1321
1322 /*
1323 * This routine takes as input a filter (user-filter)
1324 * and rewrites it as follows:
1325 * (&(scope=DN)[(objectClass=subentry)]
1326 * (|[(objectClass=referral)](user-filter))
1327 */
1328
1329 Debug(LDAP_DEBUG_TRACE,
1330 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
1331 e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
1332
1333 f = op->oq_search.rs_filter;
1334
1335 /* If the user's filter uses objectClass=*,
1336 * these clauses are redundant.
1337 */
1338 if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
1339 && !get_subentries_visibility(op)) {
1340 if( !get_manageDSAit(op) && !get_domainScope(op) ) {
1341 /* match referral objects */
1342 struct berval bv_ref = BER_BVC( "referral" );
1343 rf.f_choice = LDAP_FILTER_EQUALITY;
1344 rf.f_ava = &aa_ref;
1345 rf.f_av_desc = slap_schema.si_ad_objectClass;
1346 rf.f_av_value = bv_ref;
1347 rf.f_next = f;
1348 xf.f_or = &rf;
1349 xf.f_choice = LDAP_FILTER_OR;
1350 xf.f_next = NULL;
1351 f = &xf;
1352 depth++;
1353 }
1354 }
1355
1356 if( get_subentries_visibility( op ) ) {
1357 struct berval bv_subentry = BER_BVC( "subentry" );
1358 sf.f_choice = LDAP_FILTER_EQUALITY;
1359 sf.f_ava = &aa_subentry;
1360 sf.f_av_desc = slap_schema.si_ad_objectClass;
1361 sf.f_av_value = bv_subentry;
1362 sf.f_next = f;
1363 nf.f_choice = LDAP_FILTER_AND;
1364 nf.f_and = &sf;
1365 nf.f_next = NULL;
1366 f = &nf;
1367 depth++;
1368 }
1369
1370 /* Allocate IDL stack, plus 1 more for former tmp */
1371 if ( depth+1 > mdb->mi_search_stack_depth ) {
1372 stack = ch_malloc( (depth + 1) * MDB_IDL_UM_SIZE * sizeof( ID ) );
1373 }
1374
1375 if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
1376 rc = search_aliases( op, rs, e->e_id, isc, mci, stack );
1377 } else {
1378 rc = LDAP_SUCCESS;
1379 }
1380
1381 if ( rc == LDAP_SUCCESS ) {
1382 rc = mdb_filter_candidates( op, isc->mt, f, ids,
1383 stack, stack+MDB_IDL_UM_SIZE );
1384 }
1385
1386 if ( depth+1 > mdb->mi_search_stack_depth ) {
1387 ch_free( stack );
1388 }
1389
1390 if( rc ) {
1391 Debug(LDAP_DEBUG_TRACE,
1392 "mdb_search_candidates: failed (rc=%d)\n",
1393 rc, NULL, NULL );
1394
1395 } else {
1396 Debug(LDAP_DEBUG_TRACE,
1397 "mdb_search_candidates: id=%ld first=%ld last=%ld\n",
1398 (long) ids[0],
1399 (long) MDB_IDL_FIRST(ids),
1400 (long) MDB_IDL_LAST(ids) );
1401 }
1402
1403 return rc;
1404 }
1405
1406 static int
1407 parse_paged_cookie( Operation *op, SlapReply *rs )
1408 {
1409 int rc = LDAP_SUCCESS;
1410 PagedResultsState *ps = op->o_pagedresults_state;
1411
1412 /* this function must be invoked only if the pagedResults
1413 * control has been detected, parsed and partially checked
1414 * by the frontend */
1415 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
1416
1417 /* cookie decoding/checks deferred to backend... */
1418 if ( ps->ps_cookieval.bv_len ) {
1419 PagedResultsCookie reqcookie;
1420 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
1421 /* bad cookie */
1422 rs->sr_text = "paged results cookie is invalid";
1423 rc = LDAP_PROTOCOL_ERROR;
1424 goto done;
1425 }
1426
1427 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
1428
1429 if ( reqcookie > ps->ps_cookie ) {
1430 /* bad cookie */
1431 rs->sr_text = "paged results cookie is invalid";
1432 rc = LDAP_PROTOCOL_ERROR;
1433 goto done;
1434
1435 } else if ( reqcookie < ps->ps_cookie ) {
1436 rs->sr_text = "paged results cookie is invalid or old";
1437 rc = LDAP_UNWILLING_TO_PERFORM;
1438 goto done;
1439 }
1440
1441 } else {
1442 /* we're going to use ps_cookie */
1443 op->o_conn->c_pagedresults_state.ps_cookie = 0;
1444 }
1445
1446 done:;
1447
1448 return rc;
1449 }
1450
1451 static void
1452 send_paged_response(
1453 Operation *op,
1454 SlapReply *rs,
1455 ID *lastid,
1456 int tentries )
1457 {
1458 LDAPControl *ctrls[2];
1459 BerElementBuffer berbuf;
1460 BerElement *ber = (BerElement *)&berbuf;
1461 PagedResultsCookie respcookie;
1462 struct berval cookie;
1463
1464 Debug(LDAP_DEBUG_ARGS,
1465 "send_paged_response: lastid=0x%08lx nentries=%d\n",
1466 lastid ? *lastid : 0, rs->sr_nentries, NULL );
1467
1468 ctrls[1] = NULL;
1469
1470 ber_init2( ber, NULL, LBER_USE_DER );
1471
1472 if ( lastid ) {
1473 respcookie = ( PagedResultsCookie )(*lastid);
1474 cookie.bv_len = sizeof( respcookie );
1475 cookie.bv_val = (char *)&respcookie;
1476
1477 } else {
1478 respcookie = ( PagedResultsCookie )0;
1479 BER_BVSTR( &cookie, "" );
1480 }
1481
1482 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
1483 op->o_conn->c_pagedresults_state.ps_count =
1484 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
1485 rs->sr_nentries;
1486
1487 /* return size of 0 -- no estimate */
1488 ber_printf( ber, "{iO}", 0, &cookie );
1489
1490 ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
1491 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
1492 goto done;
1493 }
1494
1495 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
1496 ctrls[0]->ldctl_iscritical = 0;
1497
1498 slap_add_ctrls( op, rs, ctrls );
1499 rs->sr_err = LDAP_SUCCESS;
1500 send_ldap_result( op, rs );
1501
1502 done:
1503 (void) ber_free_buf( ber );
1504 }
1505