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