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