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