]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb2/search.c
Round two of referrals/aliases: WORK IN PROGRESS, may not compile!
[openldap] / servers / slapd / back-bdb2 / search.c
1 /* search.c - bdb2 backend search function */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6
7 #include <ac/string.h>
8 #include <ac/socket.h>
9 #include <ac/time.h>
10
11 #include "slap.h"
12 #include "back-bdb2.h"
13 #include "proto-back-bdb2.h"
14
15 static ID_BLOCK *base_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, int *err);
16 static ID_BLOCK *onelevel_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, int *err);
17 static ID_BLOCK *subtree_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, Entry *e, int *err, int lookupbase);
18
19 #define GRABSIZE        BUFSIZ
20
21 #define MAKE_SPACE( n ) { \
22         if ( rcur + (n) > rbuf + rmaxsize ) { \
23                 int     offset = rcur - rbuf; \
24                 rbuf =  ch_realloc( rbuf, rmaxsize + GRABSIZE ); \
25                 rmaxsize += GRABSIZE; \
26                 rcur = rbuf + offset; \
27         } \
28 }
29
30 static int
31 bdb2i_back_search_internal(
32     BackendDB   *be,
33     Connection  *conn,
34     Operation   *op,
35     char        *base,
36     int         scope,
37     int         deref,
38     int         slimit,
39     int         tlimit,
40     Filter      *filter,
41     char        *filterstr,
42     char        **attrs,
43     int         attrsonly
44 )
45 {
46         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
47         int             err;
48         time_t          stoptime;
49         ID_BLOCK                *candidates;
50         ID              id;
51         Entry           *e;
52         Attribute       *ref;
53         struct berval **refs;
54         char            *matched = NULL;
55         int             nentries = 0;
56         char            *realBase;
57
58         Debug(LDAP_DEBUG_ARGS, "=> bdb2i_back_search\n", 0, 0, 0);
59
60         if ( tlimit == 0 && be_isroot( be, op->o_ndn ) ) {
61                 tlimit = -1;    /* allow root to set no limit */
62         } else {
63                 tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
64                     be->be_timelimit : tlimit;
65                 stoptime = op->o_time + tlimit;
66         }
67         if ( slimit == 0 && be_isroot( be, op->o_ndn ) ) {
68                 slimit = -1;    /* allow root to set no limit */
69         } else {
70                 slimit = (slimit > be->be_sizelimit || slimit < 1) ?
71                     be->be_sizelimit : slimit;
72         }
73
74         /*
75          * check and apply aliasing where the dereferencing applies to
76          * the subordinates of the base
77          */
78
79         switch ( deref ) {
80         case LDAP_DEREF_FINDING:
81         case LDAP_DEREF_ALWAYS:
82                 realBase = bdb2i_derefDN ( be, conn, op, base );
83                 break;
84         default:
85                 realBase = ch_strdup(base);
86         }
87
88         (void) dn_normalize_case( realBase );
89
90         Debug( LDAP_DEBUG_TRACE, "using base \"%s\"\n",
91                 realBase, 0, 0 );
92
93         switch ( scope ) {
94         case LDAP_SCOPE_BASE:
95                 candidates = base_candidates( be, conn, op, realBase, filter,
96                     attrs, attrsonly, &matched, &err );
97                 break;
98
99         case LDAP_SCOPE_ONELEVEL:
100                 candidates = onelevel_candidates( be, conn, op, realBase, filter,
101                     attrs, attrsonly, &matched, &err );
102                 break;
103
104         case LDAP_SCOPE_SUBTREE:
105                 candidates = subtree_candidates( be, conn, op, realBase, filter,
106                     attrs, attrsonly, &matched, NULL, &err, 1 );
107                 break;
108
109         default:
110                 send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
111                         NULL, "Bad scope", NULL );
112                 if( realBase != NULL) {
113                         free( realBase );
114                 }
115                 return( -1 );
116         }
117
118         /* null candidates means we could not find the base object */
119         if ( candidates == NULL ) {
120                 send_ldap_result( conn, op, err, matched, NULL, NULL );
121                 if ( matched != NULL ) {
122                         free( matched );
123                 }
124                 if( realBase != NULL) {
125                         free( realBase );
126                 }
127                 return( -1 );
128         }
129
130         if ( matched != NULL ) {
131                 free( matched );
132         }
133
134         refs = NULL;
135
136         for ( id = bdb2i_idl_firstid( candidates ); id != NOID;
137             id = bdb2i_idl_nextid( candidates, id ) ) {
138                 /* check for abandon */
139                 ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
140                 if ( op->o_abandon ) {
141                         ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
142                         bdb2i_idl_free( candidates );
143                         ber_bvecfree( refs );
144                         if( realBase != NULL) {
145                                 free( realBase );
146                         }
147                         return( 0 );
148                 }
149                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
150
151                 /* check time limit */
152                 if ( tlimit != -1 && slap_get_time() > stoptime ) {
153                         send_search_result( conn, op, LDAP_TIMELIMIT_EXCEEDED,
154                                 NULL, NULL, refs, nentries );
155                         bdb2i_idl_free( candidates );
156                         ber_bvecfree( refs );
157                         if( realBase != NULL) {
158                                 free( realBase );
159                         }
160                         return( 0 );
161                 }
162
163                 /* get the entry with reader lock */
164                 if ( (e = bdb2i_id2entry_r( be, id )) == NULL ) {
165                         Debug( LDAP_DEBUG_ARGS, "candidate %ld not found\n",
166                                id, 0, 0 );
167                         continue;
168                 }
169
170                 /*
171                  * if it's a referral, add it to the list of referrals. only do
172                  * this for subtree searches, and don't check the filter explicitly
173                  * here since it's only a candidate anyway.
174                  */
175                 if ( scope == LDAP_SCOPE_SUBTREE &&
176                         e->e_ndn != NULL &&
177                         strncmp( e->e_ndn, "REF=", 4 ) == 0 &&
178                         (ref = attr_find( e->e_attrs, "ref" )) != NULL )
179                 {
180                         send_search_reference( be, conn, op,
181                                 e, ref->a_vals, &refs );
182
183                 /* otherwise it's an entry - see if it matches the filter */
184                 } else {
185                         /* if it matches the filter and scope, send it */
186                         if ( test_filter( be, conn, op, e, filter ) == 0 ) {
187                                 int             scopeok;
188                                 char    *dn;
189
190                                 /* check scope */
191                                 scopeok = 1;
192                                 if ( scope == LDAP_SCOPE_ONELEVEL ) {
193                                         if ( (dn = dn_parent( be, e->e_dn )) != NULL ) {
194                                                 (void) dn_normalize_case( dn );
195                                                 scopeok = (dn == realBase)
196                                                         ? 1
197                                                         : (strcmp( dn, realBase ) ? 0 : 1 );
198                                                 free( dn );
199                                         } else {
200                                                 scopeok = (realBase == NULL || *realBase == '\0');
201                                         }
202                                 } else if ( scope == LDAP_SCOPE_SUBTREE ) {
203                                         dn = ch_strdup( e->e_ndn );
204                                         scopeok = dn_issuffix( dn, realBase );
205                                         free( dn );
206                                 }
207
208                                 if ( scopeok ) {
209                                         /* check size limit */
210                                         if ( --slimit == -1 ) {
211                                                 bdb2i_cache_return_entry_r( &li->li_cache, e );
212                                                 send_search_result( conn, op, LDAP_SIZELIMIT_EXCEEDED,
213                                                         NULL, NULL, refs, nentries );
214                                                 bdb2i_idl_free( candidates );
215                                                 ber_bvecfree( refs );
216
217                                                 if( realBase != NULL) {
218                                                         free( realBase );
219                                                 }
220                                                 return( 0 );
221                                         }
222
223                                         /*
224                                          * check and apply aliasing where the dereferencing applies to
225                                          * the subordinates of the base
226                                          */
227                                         switch ( deref ) {
228                                         case LDAP_DEREF_SEARCHING:
229                                         case LDAP_DEREF_ALWAYS:
230                                                 {
231                                                         Entry *newe = bdb2i_derefAlias_r( be, conn, op, e );
232                                                         if ( newe == NULL ) { /* problem with the alias */
233                                                                 bdb2i_cache_return_entry_r( &li->li_cache, e );
234                                                                 e = NULL;
235                                                         }
236                                                         else if ( newe != e ) { /* reassign e */
237                                                                 bdb2i_cache_return_entry_r( &li->li_cache, e );
238                                                                 e = newe;
239                                                         }
240                                                 }
241                                                 break;
242                                         }
243
244                                         if (e) {
245                                                 switch ( send_search_entry( be, conn, op, e,
246                                                         attrs, attrsonly, 0 ) ) {
247                                                 case 0:         /* entry sent ok */
248                                                         nentries++;
249                                                         break;
250                                                 case 1:         /* entry not sent */
251                                                         break;
252                                                 case -1:        /* connection closed */
253                                                         bdb2i_cache_return_entry_r( &li->li_cache, e );
254                                                         bdb2i_idl_free( candidates );
255                                                         ber_bvecfree( refs );
256
257                                                         if( realBase != NULL) {
258                                                                 free( realBase );
259                                                         }
260                                                         return( 0 );
261                                                 }
262                                         }
263                                 }
264                         }
265                 }
266
267                 if( e != NULL ) {
268                         /* free reader lock */
269                         bdb2i_cache_return_entry_r( &li->li_cache, e );
270                 }
271
272                 ldap_pvt_thread_yield();
273         }
274         bdb2i_idl_free( candidates );
275
276         send_search_result( conn, op,
277                 refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
278                 NULL, NULL, refs, nentries );
279
280         ber_bvecfree( refs );
281
282         if( realBase != NULL) {
283                 free( realBase );
284         }
285
286         return( 0 );
287 }
288
289
290 int
291 bdb2_back_search(
292     BackendDB   *be,
293     Connection  *conn,
294     Operation   *op,
295     char        *base,
296     int         scope,
297     int         deref,
298     int         slimit,
299     int         tlimit,
300     Filter      *filter,
301     char        *filterstr,
302     char        **attrs,
303     int         attrsonly
304 )
305 {
306         DB_LOCK         lock;
307         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
308         struct timeval  time1;
309         int             ret;
310
311         bdb2i_start_timing( be->bd_info, &time1 );
312
313         if ( bdb2i_enter_backend_r( &lock ) != 0 ) {
314                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
315                         NULL, NULL, NULL );
316                 return( -1 );
317
318         }
319
320         ret = bdb2i_back_search_internal( be, conn, op, base, scope, deref,
321                                         slimit, tlimit, filter, filterstr, attrs, attrsonly );
322
323         (void) bdb2i_leave_backend_r( lock );
324         bdb2i_stop_timing( be->bd_info, time1, "SRCH", conn, op );
325
326         return( ret );
327 }
328
329
330 static ID_BLOCK *
331 base_candidates(
332     BackendDB   *be,
333     Connection  *conn,
334     Operation   *op,
335     char        *base,
336     Filter      *filter,
337     char        **attrs,
338     int         attrsonly,
339     char        **matched,
340     int         *err
341 )
342 {
343         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
344         ID_BLOCK                *idl;
345         Entry           *e;
346
347         Debug(LDAP_DEBUG_TRACE, "base_candidates: base: \"%s\"\n", base, 0, 0);
348
349         *err = LDAP_SUCCESS;
350
351         /* get entry with reader lock */
352         if ( (e = bdb2i_dn2entry_r( be, base, matched )) == NULL ) {
353                 *err = LDAP_NO_SUCH_OBJECT;
354                 return( NULL );
355         }
356
357         /* check for deleted */
358
359         idl = bdb2i_idl_alloc( 1 );
360         bdb2i_idl_insert( &idl, e->e_id, 1 );
361
362
363         /* free reader lock */
364         bdb2i_cache_return_entry_r( &li->li_cache, e );
365
366         return( idl );
367 }
368
369 static ID_BLOCK *
370 onelevel_candidates(
371     BackendDB   *be,
372     Connection  *conn,
373     Operation   *op,
374     char        *base,
375     Filter      *filter,
376     char        **attrs,
377     int         attrsonly,
378     char        **matched,
379     int         *err
380 )
381 {
382         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
383         Entry           *e = NULL;
384         Filter          *f;
385         char            buf[20];
386         ID_BLOCK                *candidates;
387
388         Debug(LDAP_DEBUG_TRACE, "onelevel_candidates: base: \"%s\"\n", base, 0, 0);
389
390         *err = LDAP_SUCCESS;
391
392         /* get the base object with reader lock */
393         if ( base != NULL && *base != '\0' &&
394                 (e = bdb2i_dn2entry_r( be, base, matched )) == NULL )
395         {
396                 *err = LDAP_NO_SUCH_OBJECT;
397                 return( NULL );
398         }
399
400         /*
401          * modify the filter to be something like this:
402          *
403          *      parent=baseobject & originalfilter
404          */
405
406         f = (Filter *) ch_malloc( sizeof(Filter) );
407         f->f_next = NULL;
408         f->f_choice = LDAP_FILTER_AND;
409         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
410         f->f_and->f_choice = LDAP_FILTER_EQUALITY;
411         f->f_and->f_ava.ava_type = ch_strdup( "id2children" );
412         sprintf( buf, "%ld", e != NULL ? e->e_id : 0 );
413         f->f_and->f_ava.ava_value.bv_val = ch_strdup( buf );
414         f->f_and->f_ava.ava_value.bv_len = strlen( buf );
415         f->f_and->f_next = filter;
416
417         /* from here, it's just like subtree_candidates */
418         candidates = subtree_candidates( be, conn, op, base, f, attrs,
419             attrsonly, matched, e, err, 0 );
420
421         /* free up just the filter stuff we allocated above */
422         f->f_and->f_next = NULL;
423         filter_free( f );
424
425         /* free entry and reader lock */
426         if( e != NULL ) {
427                 bdb2i_cache_return_entry_r( &li->li_cache, e );
428         }
429         return( candidates );
430 }
431
432 static ID_BLOCK *
433 subtree_candidates(
434     BackendDB   *be,
435     Connection  *conn,
436     Operation   *op,
437     char        *base,
438     Filter      *filter,
439     char        **attrs,
440     int         attrsonly,
441     char        **matched,
442     Entry       *e,
443     int         *err,
444     int         lookupbase
445 )
446 {
447         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
448         Filter          *f, **filterarg_ptr;
449         ID_BLOCK                *candidates;
450
451         Debug(LDAP_DEBUG_TRACE, "subtree_candidates: base: \"%s\" %s\n",
452                 base ? base : "NULL", lookupbase ? "lookupbase" : "", 0);
453
454         /*
455          * get the base object - unless we already have it (from one-level).
456          * also, unless this is a one-level search or a subtree search
457          * starting at the very top of our subtree, we need to modify the
458          * filter to be something like this:
459          *
460          *      dn=*baseobjectdn & (originalfilter | ref=*)
461          *
462          * the "objectclass=referral" part is used to select referrals to return
463          */
464
465         *err = LDAP_SUCCESS;
466         f = NULL;
467         if ( lookupbase ) {
468                 e = NULL;
469
470                 if ( base != NULL && *base != '\0' &&
471                         (e = bdb2i_dn2entry_r( be, base, matched )) == NULL )
472                 {
473                         *err = LDAP_NO_SUCH_OBJECT;
474                         return( NULL );
475                 }
476
477                 if (e) {
478                         bdb2i_cache_return_entry_r( &li->li_cache, e );
479                 }
480
481                 f = (Filter *) ch_malloc( sizeof(Filter) );
482                 f->f_next = NULL;
483                 f->f_choice = LDAP_FILTER_OR;
484                 f->f_or = (Filter *) ch_malloc( sizeof(Filter) );
485                 f->f_or->f_choice = LDAP_FILTER_EQUALITY;
486                 f->f_or->f_avtype = ch_strdup( "objectclass" );
487                 /* Patch to use normalized uppercase */
488                 f->f_or->f_avvalue.bv_val = ch_strdup( "REFERRAL" );
489                 f->f_or->f_avvalue.bv_len = sizeof( "REFERRAL" )-1;
490                 filterarg_ptr = &f->f_or->f_next;
491                 *filterarg_ptr = filter;
492                 filter = f;
493
494                 if ( ! be_issuffix( be, base ) ) {
495                         f = (Filter *) ch_malloc( sizeof(Filter) );
496                         f->f_next = NULL;
497                         f->f_choice = LDAP_FILTER_AND;
498                         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
499                         f->f_and->f_choice = LDAP_FILTER_SUBSTRINGS;
500                         f->f_and->f_sub_type = ch_strdup( "dn" );
501                         f->f_and->f_sub_initial = NULL;
502                         f->f_and->f_sub_any = NULL;
503                         f->f_and->f_sub_final = ch_strdup( base );
504                         value_normalize( f->f_and->f_sub_final, SYNTAX_DN|SYNTAX_CIS );
505                         f->f_and->f_next = filter;
506                         filter = f;
507                 }
508         }
509
510         candidates = bdb2i_filter_candidates( be, filter );
511
512         /* free up just the parts we allocated above */
513         if ( f != NULL ) {
514                 *filterarg_ptr = NULL;
515                 filter_free( f );
516         }
517
518         return( candidates );
519 }