]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/search.c
3ce70492a91c90858b31eaa3c038b370279d8fdc
[openldap] / servers / slapd / back-ldbm / search.c
1 /* search.c - ldbm backend search function */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/string.h>
13 #include <ac/socket.h>
14
15 #include "slap.h"
16 #include "back-ldbm.h"
17 #include "proto-back-ldbm.h"
18
19 static ID_BLOCK *base_candidate(
20         Backend *be, Entry *e );
21
22 static ID_BLOCK *search_candidates(
23         Operation *op, Entry *e, Filter *filter,
24         int scope, int deref, int manageDSAit );
25
26
27 int
28 ldbm_back_search(
29     Operation   *op,
30     SlapReply   *rs )
31 {
32         struct ldbminfo *li = (struct ldbminfo *) op->o_bd->be_private;
33         int             rc, err;
34         const char *text = NULL;
35         time_t          stoptime;
36         ID_BLOCK                *candidates;
37         ID              id, cursor;
38         Entry           *e;
39         Entry   *matched = NULL;
40         struct berval   realbase = { 0, NULL };
41         int             manageDSAit = get_manageDSAit( op );
42         int             cscope = LDAP_SCOPE_DEFAULT;
43
44         struct slap_limits_set *limit = NULL;
45         int isroot = 0;
46                 
47 #ifdef NEW_LOGGING
48         LDAP_LOG( BACK_LDBM, ENTRY, "ldbm_back_search: enter\n", 0, 0, 0 );
49 #else
50         Debug(LDAP_DEBUG_TRACE, "=> ldbm_back_search\n", 0, 0, 0);
51 #endif
52
53         /* grab giant lock for reading */
54         ldap_pvt_thread_rdwr_rlock(&li->li_giant_rwlock);
55
56         if ( op->o_req_ndn.bv_len == 0 ) {
57                 /* DIT root special case */
58                 e = (Entry *) &slap_entry_root;
59
60                 /* need normalized dn below */
61                 ber_dupbv( &realbase, &e->e_nname );
62
63                 candidates = search_candidates( op, e, op->oq_search.rs_filter,
64                                 op->oq_search.rs_scope, op->oq_search.rs_deref,
65                                 manageDSAit || get_domainScope(op) );
66
67                 goto searchit;
68                 
69         } else if ( op->oq_search.rs_deref & LDAP_DEREF_FINDING ) {
70                 /* deref dn and get entry with reader lock */
71                 e = deref_dn_r( op->o_bd, &op->o_req_ndn, &rs->sr_err, &matched, &rs->sr_text );
72
73                 if( rs->sr_err == LDAP_NO_SUCH_OBJECT ) rs->sr_err = LDAP_REFERRAL;
74
75         } else {
76                 /* get entry with reader lock */
77                 e = dn2entry_r( op->o_bd, &op->o_req_ndn, &matched );
78                 rs->sr_err = e != NULL ? LDAP_SUCCESS : LDAP_REFERRAL;
79                 rs->sr_text = NULL;
80         }
81
82         if ( e == NULL ) {
83                 struct berval matched_dn = { 0, NULL };
84
85                 if ( matched != NULL ) {
86                         BerVarray erefs;
87                         ber_dupbv( &matched_dn, &matched->e_name );
88
89                         erefs = is_entry_referral( matched )
90                                 ? get_entry_referrals( op, matched )
91                                 : NULL;
92
93                         cache_return_entry_r( &li->li_cache, matched );
94
95                         if( erefs ) {
96                                 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
97                                         &op->o_req_dn, op->oq_search.rs_scope );
98
99                                 ber_bvarray_free( erefs );
100                         }
101
102                 } else {
103                         rs->sr_ref = referral_rewrite( default_referral,
104                                 NULL, &op->o_req_dn, op->oq_search.rs_scope );
105                 }
106
107                 ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
108
109                 rs->sr_matched = matched_dn.bv_val;
110                 send_ldap_result( op, rs );
111
112                 ber_bvarray_free( rs->sr_ref );
113                 ber_memfree( matched_dn.bv_val );
114                 return LDAP_REFERRAL;
115         }
116
117         if (!manageDSAit && is_entry_referral( e ) ) {
118                 /* entry is a referral, don't allow add */
119                 struct berval matched_dn;
120                 BerVarray erefs;
121
122                 ber_dupbv( &matched_dn, &e->e_name );
123                 erefs = get_entry_referrals( op, e );
124                 rs->sr_ref = NULL;
125
126                 cache_return_entry_r( &li->li_cache, e );
127                 ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
128
129 #ifdef NEW_LOGGING
130                 LDAP_LOG( BACK_LDBM, INFO,
131                         "ldbm_search: entry (%s) is a referral.\n",
132                         e->e_dn, 0, 0 );
133 #else
134                 Debug( LDAP_DEBUG_TRACE,
135                         "ldbm_search: entry is referral\n",
136                         0, 0, 0 );
137 #endif
138
139                 if( erefs ) {
140                         rs->sr_ref = referral_rewrite( erefs, &matched_dn,
141                                 &op->o_req_dn, op->oq_search.rs_scope );
142
143                         ber_bvarray_free( erefs );
144                 }
145
146                 rs->sr_matched = matched_dn.bv_val;
147                 if( rs->sr_ref ) {
148                         rs->sr_err = LDAP_REFERRAL;
149                         send_ldap_result( op, rs );
150                         ber_bvarray_free( rs->sr_ref );
151
152                 } else {
153                         send_ldap_error( op, rs, LDAP_OTHER,
154                         "bad referral object" );
155                 }
156
157                 ber_memfree( matched_dn.bv_val );
158                 return LDAP_OTHER;
159         }
160
161         if ( is_entry_alias( e ) ) {
162                 /* don't deref */
163                 op->oq_search.rs_deref = LDAP_DEREF_NEVER;
164         }
165
166         if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
167                 cscope = LDAP_SCOPE_BASE;
168                 candidates = base_candidate( op->o_bd, e );
169
170         } else {
171                 cscope = ( op->oq_search.rs_scope != LDAP_SCOPE_SUBTREE )
172                         ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE;
173                 candidates = search_candidates( op, e, op->oq_search.rs_filter,
174                     op->oq_search.rs_scope, op->oq_search.rs_deref, manageDSAit );
175         }
176
177         /* need normalized dn below */
178         ber_dupbv( &realbase, &e->e_nname );
179
180         cache_return_entry_r( &li->li_cache, e );
181
182 searchit:
183         if ( candidates == NULL ) {
184                 /* no candidates */
185 #ifdef NEW_LOGGING
186                 LDAP_LOG( BACK_LDBM, INFO,
187                         "ldbm_search: no candidates\n" , 0, 0, 0);
188 #else
189                 Debug( LDAP_DEBUG_TRACE, "ldbm_search: no candidates\n",
190                         0, 0, 0 );
191 #endif
192
193                 rs->sr_err = LDAP_SUCCESS;
194                 send_ldap_result( op, rs );
195
196                 rc = LDAP_OTHER;
197                 goto done;
198         }
199
200         /* if not root, get appropriate limits */
201         if ( be_isroot( op->o_bd, &op->o_ndn ) )
202         {
203                 /*
204                  * FIXME: I'd consider this dangerous if someone
205                  * uses isroot for anything but handling limits
206                  */
207                 isroot = 1;
208         } else {
209                 ( void ) get_limits( op->o_bd, &op->o_ndn, &limit );
210         }
211
212         /* if candidates exceed to-be-checked entries, abort */
213         if ( !isroot && limit->lms_s_unchecked != -1 ) {
214                 if ( ID_BLOCK_NIDS( candidates ) > (unsigned) limit->lms_s_unchecked ) {
215                         send_ldap_error( op, rs, LDAP_ADMINLIMIT_EXCEEDED,
216                                         NULL );
217                         rc = 0;
218                         goto done;
219                 }
220         }
221         
222         /* if root an no specific limit is required, allow unlimited search */
223         if ( isroot ) {
224                 if ( op->oq_search.rs_tlimit == 0 ) {
225                         op->oq_search.rs_tlimit = -1;
226                 }
227
228                 if ( op->oq_search.rs_slimit == 0 ) {
229                         op->oq_search.rs_slimit = -1;
230                 }
231
232         } else {
233                 /* if no limit is required, use soft limit */
234                 if ( op->oq_search.rs_tlimit <= 0 ) {
235                         op->oq_search.rs_tlimit = limit->lms_t_soft;
236                 
237                 /* if requested limit higher than hard limit, abort */
238                 } else if ( op->oq_search.rs_tlimit > limit->lms_t_hard ) {
239                         /* no hard limit means use soft instead */
240                         if ( limit->lms_t_hard == 0
241                                         && limit->lms_t_soft > -1
242                                         && op->oq_search.rs_tlimit > limit->lms_t_soft ) {
243                                 op->oq_search.rs_tlimit = limit->lms_t_soft;
244                         
245                         /* positive hard limit means abort */
246                         } else if ( limit->lms_t_hard > 0 ) {
247                                 send_ldap_error( op, rs,
248                                                 LDAP_ADMINLIMIT_EXCEEDED,
249                                                 NULL );
250                                 rc = 0; 
251                                 goto done;
252                         }
253
254                         /* negative hard limit means no limit */
255                 }
256
257                 /* if no limit is required, use soft limit */
258                 if ( op->oq_search.rs_slimit <= 0 ) {
259                         op->oq_search.rs_slimit = limit->lms_s_soft;
260
261                 /* if requested limit higher than hard limit, abort */
262                 } else if ( op->oq_search.rs_slimit > limit->lms_s_hard ) {
263                         /* no hard limit means use soft instead */
264                         if ( limit->lms_s_hard == 0
265                                         && limit->lms_s_soft > -1
266                                         && op->oq_search.rs_slimit > limit->lms_s_soft ) {
267                                 op->oq_search.rs_slimit = limit->lms_s_soft;
268
269                         /* positive hard limit means abort */
270                         } else if ( limit->lms_s_hard > 0 ) {
271                                 send_ldap_error( op, rs,
272                                                 LDAP_ADMINLIMIT_EXCEEDED,
273                                                 NULL );
274                                 rc = 0;
275                                 goto done;
276                         }
277
278                         /* negative hard limit means no limit */
279                 }
280         }
281
282         /* compute it anyway; root does not use it */
283         stoptime = op->o_time + op->oq_search.rs_tlimit;
284         rs->sr_attrs = op->oq_search.rs_attrs;
285
286         for ( id = idl_firstid( candidates, &cursor ); id != NOID;
287             id = idl_nextid( candidates, &cursor ) )
288         {
289                 int scopeok = 0;
290                 int result = 0;
291
292                 /* check for abandon */
293                 if ( op->o_abandon ) {
294                         rc = 0;
295                         goto done;
296                 }
297
298                 /* check time limit */
299                 if ( op->oq_search.rs_tlimit != -1 && slap_get_time() > stoptime ) {
300                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
301                         send_ldap_result( op, rs );
302                         rc = 0;
303                         goto done;
304                 }
305
306                 /* get the entry with reader lock */
307                 e = id2entry_r( op->o_bd, id );
308
309                 if ( e == NULL ) {
310 #ifdef NEW_LOGGING
311                         LDAP_LOG( BACK_LDBM, INFO,
312                                 "ldbm_search: candidate %ld not found.\n", id, 0, 0 );
313 #else
314                         Debug( LDAP_DEBUG_TRACE,
315                                 "ldbm_search: candidate %ld not found\n",
316                                 id, 0, 0 );
317 #endif
318
319                         goto loop_continue;
320                 }
321
322                 rs->sr_entry = e;
323
324 #ifdef LDBM_SUBENTRIES
325         if ( is_entry_subentry( e ) ) {
326                 if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
327                         if(!get_subentries_visibility( op )) {
328                                 /* only subentries are visible */
329                                 goto loop_continue;
330                         }
331                 } else if ( get_subentries( op ) &&
332                         !get_subentries_visibility( op ))
333                 {
334                         /* only subentries are visible */
335                         goto loop_continue;
336                 }
337         } else if ( get_subentries_visibility( op )) {
338                 /* only subentries are visible */
339                 goto loop_continue;
340         }
341 #endif
342
343                 if ( op->oq_search.rs_deref & LDAP_DEREF_SEARCHING && is_entry_alias( e ) ) {
344                         Entry *matched;
345                         int err;
346                         const char *text;
347                         
348                         e = deref_entry_r( op->o_bd, e, &err, &matched, &text );
349
350                         if( e == NULL ) {
351                                 e = matched;
352                                 goto loop_continue;
353                         }
354
355                         if( e->e_id == id ) {
356                                 /* circular loop */
357                                 goto loop_continue;
358                         }
359
360                         /* need to skip alias which deref into scope */
361                         if( op->oq_search.rs_scope & LDAP_SCOPE_ONELEVEL ) {
362                                 struct berval pdn;
363                                 dnParent( &e->e_nname, &pdn );
364                                 if ( ber_bvcmp( &pdn, &realbase ) ) {
365                                         goto loop_continue;
366                                 }
367
368                         } else if ( dnIsSuffix( &e->e_nname, &realbase ) ) {
369                                 /* alias is within scope */
370 #ifdef NEW_LOGGING
371                                 LDAP_LOG( BACK_LDBM, DETAIL1,
372                                         "ldbm_search: alias \"%s\" in subtree\n", e->e_dn, 0, 0 );
373 #else
374                                 Debug( LDAP_DEBUG_TRACE,
375                                         "ldbm_search: alias \"%s\" in subtree\n",
376                                         e->e_dn, 0, 0 );
377 #endif
378
379                                 goto loop_continue;
380                         }
381
382                         rs->sr_entry = e;
383
384                         scopeok = 1;
385                 }
386
387                 /*
388                  * if it's a referral, add it to the list of referrals. only do
389                  * this for non-base searches, and don't check the filter
390                  * explicitly here since it's only a candidate anyway.
391                  */
392                 if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE &&
393                         is_entry_referral( e ) )
394                 {
395                         struct berval   dn;
396
397                         /* check scope */
398                         if ( !scopeok && op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL ) {
399                                 if ( !be_issuffix( op->o_bd, &e->e_nname ) ) {
400                                         dnParent( &e->e_nname, &dn );
401                                         scopeok = dn_match( &dn, &realbase );
402                                 } else {
403                                         scopeok = (realbase.bv_len == 0);
404                                 }
405
406                         } else if ( !scopeok && op->oq_search.rs_scope == LDAP_SCOPE_SUBTREE ) {
407                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
408
409                         } else {
410                                 scopeok = 1;
411                         }
412
413                         if( scopeok ) {
414                                 BerVarray erefs = get_entry_referrals( op, e );
415                                 rs->sr_ref = referral_rewrite( erefs,
416                                         &e->e_name, NULL,
417                                         op->oq_search.rs_scope == LDAP_SCOPE_SUBTREE
418                                                 ? LDAP_SCOPE_SUBTREE
419                                                 : LDAP_SCOPE_BASE );
420
421                                 send_search_reference( op, rs );
422
423                                 ber_bvarray_free( rs->sr_ref );
424                                 rs->sr_ref = NULL;
425
426                         } else {
427 #ifdef NEW_LOGGING
428                                 LDAP_LOG( BACK_LDBM, DETAIL2,
429                                         "ldbm_search: candidate referral %ld scope not okay\n",
430                                         id, 0, 0 );
431 #else
432                                 Debug( LDAP_DEBUG_TRACE,
433                                         "ldbm_search: candidate referral %ld scope not okay\n",
434                                         id, 0, 0 );
435 #endif
436                         }
437
438                         goto loop_continue;
439                 }
440
441                 if ( !manageDSAit && is_entry_glue( e )) {
442                         goto loop_continue;
443                 }
444
445                 /* if it matches the filter and scope, send it */
446                 result = test_filter( op, e, op->oq_search.rs_filter );
447
448                 if ( result == LDAP_COMPARE_TRUE ) {
449                         struct berval   dn;
450
451                         /* check scope */
452                         if ( !scopeok && op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL ) {
453                                 if ( !be_issuffix( op->o_bd, &e->e_nname ) ) {
454                                         dnParent( &e->e_nname, &dn );
455                                         scopeok = dn_match( &dn, &realbase );
456                                 } else {
457                                         scopeok = (realbase.bv_len == 0);
458                                 }
459
460                         } else if ( !scopeok && op->oq_search.rs_scope == LDAP_SCOPE_SUBTREE ) {
461                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
462
463                         } else {
464                                 scopeok = 1;
465                         }
466
467                         if ( scopeok ) {
468                                 /* check size limit */
469                                 if ( --op->oq_search.rs_slimit == -1 ) {
470                                         cache_return_entry_r( &li->li_cache, e );
471                                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
472                                         send_ldap_result( op, rs );
473                                         rc = 0;
474                                         goto done;
475                                 }
476
477                                 if (e) {
478
479                                         result = send_search_entry( op, rs );
480
481                                         switch (result) {
482                                         case 0:         /* entry sent ok */
483                                                 break;
484                                         case 1:         /* entry not sent */
485                                                 break;
486                                         case -1:        /* connection closed */
487                                                 cache_return_entry_r( &li->li_cache, e );
488                                                 rc = 0;
489                                                 goto done;
490                                         }
491                                 }
492                         } else {
493 #ifdef NEW_LOGGING
494                                 LDAP_LOG( BACK_LDBM, DETAIL2,
495                                         "ldbm_search: candidate entry %ld scope not okay\n", 
496                                         id, 0, 0 );
497 #else
498                                 Debug( LDAP_DEBUG_TRACE,
499                                         "ldbm_search: candidate entry %ld scope not okay\n",
500                                         id, 0, 0 );
501 #endif
502                         }
503
504                 } else {
505 #ifdef NEW_LOGGING
506                         LDAP_LOG( BACK_LDBM, DETAIL2,
507                                 "ldbm_search: candidate entry %ld does not match filter\n", 
508                                 id, 0, 0 );
509 #else
510                         Debug( LDAP_DEBUG_TRACE,
511                                 "ldbm_search: candidate entry %ld does not match filter\n",
512                                 id, 0, 0 );
513 #endif
514                 }
515
516 loop_continue:
517                 if( e != NULL ) {
518                         /* free reader lock */
519                         cache_return_entry_r( &li->li_cache, e );
520                 }
521
522                 ldap_pvt_thread_yield();
523         }
524
525         rs->sr_err = rs->sr_v2ref ? LDAP_REFERRAL : LDAP_SUCCESS;
526         rs->sr_ref = rs->sr_v2ref;
527         send_ldap_result( op, rs );
528
529         rc = 0;
530
531 done:
532         ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
533
534         if( candidates != NULL )
535                 idl_free( candidates );
536
537         if( rs->sr_v2ref ) ber_bvarray_free( rs->sr_v2ref );
538         if( realbase.bv_val ) free( realbase.bv_val );
539
540         return rc;
541 }
542
543 static ID_BLOCK *
544 base_candidate(
545     Backend     *be,
546         Entry   *e )
547 {
548         ID_BLOCK                *idl;
549
550 #ifdef NEW_LOGGING
551         LDAP_LOG( BACK_LDBM, ENTRY, "base_candidate: base (%s)\n", e->e_dn, 0, 0 );
552 #else
553         Debug(LDAP_DEBUG_TRACE, "base_candidates: base: \"%s\"\n",
554                 e->e_dn, 0, 0);
555 #endif
556
557
558         idl = idl_alloc( 1 );
559         idl_insert( &idl, e->e_id, 1 );
560
561         return( idl );
562 }
563
564 static ID_BLOCK *
565 search_candidates(
566     Operation   *op,
567     Entry       *e,
568     Filter      *filter,
569     int         scope,
570         int             deref,
571         int             manageDSAit )
572 {
573         ID_BLOCK                *candidates;
574         Filter          f, fand, rf, af, xf;
575     AttributeAssertion aa_ref, aa_alias;
576         struct berval bv_ref = { sizeof("referral")-1, "referral" };
577         struct berval bv_alias = { sizeof("alias")-1, "alias" };
578 #ifdef LDBM_SUBENTRIES
579         Filter  sf;
580         AttributeAssertion aa_subentry;
581 #endif
582
583 #ifdef NEW_LOGGING
584         LDAP_LOG( BACK_LDBM, DETAIL1,
585                    "search_candidates: base (%s) scope %d deref %d\n",
586                    e->e_ndn, scope, deref );
587 #else
588         Debug(LDAP_DEBUG_TRACE,
589                 "search_candidates: base=\"%s\" s=%d d=%d\n",
590                 e->e_ndn, scope, deref );
591 #endif
592
593
594         xf.f_or = filter;
595         xf.f_choice = LDAP_FILTER_OR;
596         xf.f_next = NULL;
597
598         if( !manageDSAit ) {
599                 /* match referrals */
600                 rf.f_choice = LDAP_FILTER_EQUALITY;
601                 rf.f_ava = &aa_ref;
602                 rf.f_av_desc = slap_schema.si_ad_objectClass;
603                 rf.f_av_value = bv_ref;
604                 rf.f_next = xf.f_or;
605                 xf.f_or = &rf;
606         }
607
608         if( deref & LDAP_DEREF_SEARCHING ) {
609                 /* match aliases */
610                 af.f_choice = LDAP_FILTER_EQUALITY;
611                 af.f_ava = &aa_alias;
612                 af.f_av_desc = slap_schema.si_ad_objectClass;
613                 af.f_av_value = bv_alias;
614                 af.f_next = xf.f_or;
615                 xf.f_or = &af;
616         }
617
618         f.f_next = NULL;
619         f.f_choice = LDAP_FILTER_AND;
620         f.f_and = &fand;
621         fand.f_choice = scope == LDAP_SCOPE_SUBTREE
622                 ? SLAPD_FILTER_DN_SUBTREE
623                 : SLAPD_FILTER_DN_ONE;
624         fand.f_dn = &e->e_nname;
625         fand.f_next = xf.f_or == filter ? filter : &xf ;
626
627 #ifdef LDBM_SUBENTRIES
628         if ( get_subentries_visibility( op )) {
629                 struct berval bv_subentry = { sizeof("SUBENTRY")-1, "SUBENTRY" };
630                 sf.f_choice = LDAP_FILTER_EQUALITY;
631                 sf.f_ava = &aa_subentry;
632                 sf.f_av_desc = slap_schema.si_ad_objectClass;
633                 sf.f_av_value = bv_subentry;
634                 sf.f_next = fand.f_next;
635                 fand.f_next = &sf;
636         }
637 #endif
638
639         candidates = filter_candidates( op, &f );
640
641         return( candidates );
642 }