]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/search.c
a1002c92c51e30a4d770fb8c68a600e726a88936
[openldap] / servers / slapd / back-meta / search.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2004 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/socket.h>
28 #include <ac/string.h>
29 #include <ac/time.h>
30
31 #include "slap.h"
32 #include "../back-ldap/back-ldap.h"
33 #include "back-meta.h"
34 #include "ldap_pvt.h"
35 #undef ldap_debug       /* silence a warning in ldap-int.h */
36 #include "ldap_log.h"
37 #include "../../../libraries/libldap/ldap-int.h"
38
39 static int
40 meta_send_entry(
41                 Operation       *op,
42                 SlapReply       *rs,
43                 struct metaconn *lc,
44                 int             i,
45                 LDAPMessage     *e
46 );
47
48 static int
49 is_one_level_rdn(
50                 const char      *rdn,
51                 int             from
52 );
53
54 int
55 meta_back_search( Operation *op, SlapReply *rs )
56 {
57         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
58         struct metaconn *lc;
59         struct metasingleconn *lsc;
60         struct timeval  tv = { 0, 0 };
61         LDAPMessage     *res = NULL, *e;
62         int     rc = 0, *msgid, sres = LDAP_SUCCESS;
63         char *err = NULL;
64         struct berval match = BER_BVNULL, mmatch = BER_BVNULL;
65         BerVarray v2refs = NULL;
66                 
67         int i, last = 0, candidates = 0, initial_candidates = 0,
68                         candidate_match = 0;
69         dncookie dc;
70
71         int     is_scope = 0,
72                 is_filter = 0;
73
74         /*
75          * controls are set in ldap_back_dobind()
76          * 
77          * FIXME: in case of values return filter, we might want
78          * to map attrs and maybe rewrite value
79          */
80         lc = meta_back_getconn( op, rs, META_OP_ALLOW_MULTIPLE, 
81                         &op->o_req_ndn, NULL );
82         if ( !lc ) {
83                 send_ldap_result( op, rs );
84                 return -1;
85         }
86
87         if ( !meta_back_dobind( lc, op ) ) {
88                 rs->sr_err = LDAP_OTHER;
89                 send_ldap_result( op, rs );
90                 return -1;
91         }
92
93         /*
94          * Array of message id of each target
95          */
96         msgid = ch_calloc( sizeof( int ), li->ntargets );
97         if ( msgid == NULL ) {
98                 rs->sr_err = LDAP_OTHER;
99                 send_ldap_result( op, rs );
100                 return -1;
101         }
102         
103         dc.conn = op->o_conn;
104         dc.rs = rs;
105
106         /*
107          * Inits searches
108          */
109         for ( i = 0, lsc = lc->conns; !META_LAST(lsc); ++i, ++lsc ) {
110                 struct berval   realbase = op->o_req_dn;
111                 int             realscope = op->ors_scope;
112                 ber_len_t       suffixlen = 0;
113                 struct berval   mbase = BER_BVNULL; 
114                 struct berval   mfilter = BER_BVNULL;
115                 char            **mapped_attrs = NULL;
116
117                 if ( lsc->candidate != META_CANDIDATE ) {
118                         msgid[ i ] = -1;
119                         continue;
120                 }
121
122                 /* should we check return values? */
123                 if ( op->ors_deref != -1 ) {
124                         ldap_set_option( lsc->ld, LDAP_OPT_DEREF,
125                                         ( void * )&op->ors_deref);
126                 }
127                 if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
128                         ldap_set_option( lsc->ld, LDAP_OPT_TIMELIMIT,
129                                         ( void * )&op->ors_tlimit);
130                 }
131                 if ( op->ors_slimit != SLAP_NO_LIMIT ) {
132                         ldap_set_option( lsc->ld, LDAP_OPT_SIZELIMIT,
133                                         ( void * )&op->ors_slimit);
134                 }
135
136                 dc.rwmap = &li->targets[ i ]->rwmap;
137
138                 /*
139                  * modifies the base according to the scope, if required
140                  */
141                 suffixlen = li->targets[ i ]->suffix.bv_len;
142                 if ( suffixlen > op->o_req_ndn.bv_len ) {
143                         switch ( op->ors_scope ) {
144                         case LDAP_SCOPE_SUBTREE:
145                                 /*
146                                  * make the target suffix the new base
147                                  * FIXME: this is very forgiving, because
148                                  * illegal bases may be turned into 
149                                  * the suffix of the target.
150                                  */
151                                 if ( dnIsSuffix( &li->targets[ i ]->suffix,
152                                                 &op->o_req_ndn ) ) {
153                                         realbase = li->targets[ i ]->suffix;
154                                         is_scope++;
155
156                                 } else {
157                                         /*
158                                          * this target is no longer candidate
159                                          */
160                                         msgid[ i ] = -1;
161                                         goto new_candidate;
162                                 }
163                                 break;
164
165                         case LDAP_SCOPE_ONELEVEL:
166                                 if ( is_one_level_rdn( li->targets[ i ]->suffix.bv_val,
167                                                 suffixlen - op->o_req_ndn.bv_len - 1 ) 
168                         && dnIsSuffix( &li->targets[ i ]->suffix, &op->o_req_ndn ) ) {
169                                         /*
170                                          * if there is exactly one level,
171                                          * make the target suffix the new
172                                          * base, and make scope "base"
173                                          */
174                                         realbase = li->targets[ i ]->suffix;
175                                         realscope = LDAP_SCOPE_BASE;
176                                         is_scope++;
177                                         break;
178                                 } /* else continue with the next case */
179
180                         case LDAP_SCOPE_BASE:
181                                 /*
182                                  * this target is no longer candidate
183                                  */
184                                 msgid[ i ] = -1;
185                                 goto new_candidate;
186                         }
187
188                 }
189
190                 /*
191                  * Rewrite the search base, if required
192                  */
193                 dc.ctx = "searchBase";
194                 switch ( ldap_back_dn_massage( &dc, &realbase, &mbase ) ) {
195                 default:
196                         break;
197
198                 case REWRITE_REGEXEC_UNWILLING:
199                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
200                         rs->sr_text = "Operation not allowed";
201                         send_ldap_result( op, rs );
202                         rc = -1;
203                         goto finish;
204
205                 case REWRITE_REGEXEC_ERR:
206 #if 0
207                         rs->sr_err = LDAP_OTHER;
208                         rs->sr_text = "Rewrite error";
209                         send_ldap_result( op, rs );
210                         rc = -1;
211                         goto finish;
212 #endif 
213
214                         /*
215                          * this target is no longer candidate
216                          */
217                         msgid[ i ] = -1;
218                         goto new_candidate;
219                 }
220
221                 /*
222                  * Maps filter
223                  */
224                 rc = ldap_back_filter_map_rewrite( &dc,
225                                 op->ors_filter,
226                                 &mfilter, BACKLDAP_MAP );
227                 switch ( rc ) {
228                 case LDAP_SUCCESS:
229                         is_filter++;
230                         break;
231
232                 case LDAP_COMPARE_FALSE:
233                         rc = 0;
234
235                 default:
236                         /*
237                          * this target is no longer candidate
238                          */
239                         msgid[ i ] = -1;
240                         goto new_candidate;
241                 }
242
243                 /*
244                  * Maps required attributes
245                  */
246                 rc = ldap_back_map_attrs( &li->targets[ i ]->rwmap.rwm_at,
247                                 op->ors_attrs, BACKLDAP_MAP,
248                                 &mapped_attrs );
249                 if ( rc != LDAP_SUCCESS ) {
250                         /*
251                          * this target is no longer candidate
252                          */
253                         msgid[ i ] = -1;
254                         goto new_candidate;
255                 }
256
257                 /*
258                  * Starts the search
259                  */
260                 msgid[ i ] = ldap_search( lsc->ld, mbase.bv_val, realscope,
261                                 mfilter.bv_val, mapped_attrs,
262                                 op->ors_attrsonly ); 
263                 if ( mapped_attrs ) {
264                         free( mapped_attrs );
265                         mapped_attrs = NULL;
266                 }
267                 if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
268                         free( mfilter.bv_val );
269                         mfilter.bv_val = NULL;
270                 }
271                 if ( mbase.bv_val != realbase.bv_val ) {
272                         free( mbase.bv_val );
273                         mbase.bv_val = NULL;
274                 }
275
276                 if ( msgid[ i ] == -1 ) {
277                         continue;
278                 }
279
280                 ++candidates;
281
282 new_candidate:;
283         }
284
285         initial_candidates = candidates;
286
287         /* We pull apart the ber result, stuff it into a slapd entry, and
288          * let send_search_entry stuff it back into ber format. Slow & ugly,
289          * but this is necessary for version matching, and for ACL processing.
290          */
291
292
293         /*
294          * In case there are no candidates, no cycle takes place...
295          *
296          * FIXME: we might use a queue, to balance the load 
297          * among the candidates
298          */
299         for ( rc = 0; candidates > 0; ) {
300                 int ab, gotit = 0;
301
302                 /* check for abandon */
303                 ab = op->o_abandon;
304
305                 for ( i = 0, lsc = lc->conns; !META_LAST(lsc); lsc++, i++ ) {
306                         if ( msgid[ i ] == -1 ) {
307                                 continue;
308                         }
309                         
310                         if ( ab ) {
311                                 ldap_abandon( lsc->ld, msgid[ i ] );
312                                 rc = 0;
313                                 break;
314                         }
315
316                         if ( op->ors_slimit > 0
317                                         && rs->sr_nentries == op->ors_slimit ) {
318                                 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
319                                 rs->sr_v2ref = v2refs;
320                                 send_ldap_result( op, rs );
321                                 goto finish;
322                         }
323
324                         /*
325                          * FIXME: handle time limit as well?
326                          * Note that target servers are likely 
327                          * to handle it, so at some time we'll
328                          * get a LDAP_TIMELIMIT_EXCEEDED from
329                          * one of them ...
330                          */
331                         rc = ldap_result( lsc->ld, msgid[ i ],
332                                         0, &tv, &res );
333
334                         if ( rc == 0 ) {
335                                 /* timeout exceeded */
336
337                                 /* FIXME: res should not need to be freed */
338                                 assert( res == NULL );
339
340                                 continue;
341
342                         } else if ( rc == -1 ) {
343                                 /* something REALLY bad happened! */
344                                 ( void )meta_clear_unused_candidates( li,
345                                                 lc, -1, 0 );
346                                 rs->sr_err = LDAP_OTHER;
347                                 rs->sr_v2ref = v2refs;
348                                 send_ldap_result( op, rs );
349                                 
350                                 /* anything else needs be done? */
351
352                                 /* FIXME: res should not need to be freed */
353                                 assert( res == NULL );
354
355                                 goto finish;
356
357                         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
358                                 e = ldap_first_entry( lsc->ld, res );
359                                 meta_send_entry( op, rs, lc, i, e );
360
361                                 ldap_msgfree( res );
362                                 res = NULL;
363
364                                 /*
365                                  * If scope is BASE, we need to jump out
366                                  * as soon as one entry is found; if
367                                  * the target pool is properly crafted,
368                                  * this should correspond to the sole
369                                  * entry that has the base DN
370                                  */
371                                 if ( op->ors_scope == LDAP_SCOPE_BASE
372                                                 && rs->sr_nentries > 0 ) {
373                                         candidates = 0;
374                                         sres = LDAP_SUCCESS;
375                                         break;
376                                 }
377
378                                 gotit = 1;
379
380                         } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
381                                 char            **references = NULL;
382                                 int             cnt;
383
384                                 /*
385                                  * FIXME: should we collect references
386                                  * and send them alltogether at the end?
387                                  */
388
389                                 rc = ldap_parse_reference( lsc->ld, res,
390                                                 &references, &rs->sr_ctrls, 1 );
391
392                                 if ( rc != LDAP_SUCCESS ) {
393                                         continue;
394                                 }
395
396                                 if ( references == NULL ) {
397                                         continue;
398                                 }
399
400                                 for ( cnt = 0; references[ cnt ]; cnt++ )
401                                         /* NO OP */ ;
402                                 
403                                 rs->sr_ref = ch_calloc( cnt + 1, sizeof( struct berval ) );
404
405                                 for ( cnt = 0; references[ cnt ]; cnt++ ) {
406                                         rs->sr_ref[ cnt ].bv_val = references[ cnt ];
407                                         rs->sr_ref[ cnt ].bv_len = strlen( references[ cnt ] );
408                                 }
409
410                                 /* ignore return value by now */
411                                 ( void )send_search_reference( op, rs );
412
413                                 /* cleanup */
414                                 if ( references ) {
415                                         ldap_value_free( references );
416                                         ch_free( rs->sr_ref );
417                                         rs->sr_ref = NULL;
418                                 }
419
420                                 if ( rs->sr_ctrls ) {
421                                         ldap_controls_free( rs->sr_ctrls );
422                                         rs->sr_ctrls = NULL;
423                                 }
424
425                                 ldap_msgfree( res );
426                                 res = NULL;
427
428                         } else {
429                                 rs->sr_err = ldap_result2error( lsc->ld,
430                                                 res, 1 );
431                                 res = NULL;
432
433                                 sres = slap_map_api2result( rs );
434                                 if ( err != NULL ) {
435                                         free( err );
436                                 }
437                                 ldap_get_option( lsc->ld,
438                                                 LDAP_OPT_ERROR_STRING, &err );
439                                 if ( match.bv_val != NULL ) {
440                                         free( match.bv_val );
441                                 }
442                                 ldap_get_option( lsc->ld,
443                                                 LDAP_OPT_MATCHED_DN, &match.bv_val );
444
445 #ifdef NEW_LOGGING
446                                 LDAP_LOG( BACK_META, ERR,
447                                         "meta_back_search [%d] "
448                                         "match=\"%s\" err=\"%s\"\n",
449                                         i, match.bv_val, err );
450 #else /* !NEW_LOGGING */
451                                 Debug( LDAP_DEBUG_ANY,
452                                         "=>meta_back_search [%d] "
453                                         "match=\"%s\" err=\"%s\"\n",
454                                         i, match.bv_val, err ); 
455 #endif /* !NEW_LOGGING */
456                                 candidate_match++;
457                                 last = i;
458                                 rc = 0;
459
460                                 /*
461                                  * When no candidates are left,
462                                  * the outer cycle finishes
463                                  */
464                                 msgid[ i ] = -1;
465                                 --candidates;
466                         }
467                 }
468
469                 if ( ab ) {
470                         goto finish;
471                 }
472
473                 if ( gotit == 0 ) {
474                         tv.tv_sec = 0;
475                         tv.tv_usec = 100000;
476                         ldap_pvt_thread_yield();
477                 } else {
478                         tv.tv_sec = 0;
479                         tv.tv_usec = 0;
480                 }
481         }
482
483         if ( rc == -1 ) {
484                 /*
485                  * FIXME: need a strategy to handle errors
486                  */
487                 rc = meta_back_op_result( lc, op, rs );
488                 goto finish;
489         }
490
491         /*
492          * Rewrite the matched portion of the search base, if required
493          * 
494          * FIXME: only the last one gets caught!
495          */
496         if ( candidate_match == initial_candidates
497                         && match.bv_val != NULL && *match.bv_val ) {
498                 dc.ctx = "matchedDN";
499                 dc.rwmap = &li->targets[ last ]->rwmap;
500
501                 if ( ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
502                         mmatch.bv_val = NULL;
503                 }
504         }
505
506         /*
507          * In case we returned at least one entry, we return LDAP_SUCCESS
508          * otherwise, the latter error code we got
509          *
510          * FIXME: we should handle error codes and return the more 
511          * important/reasonable
512          */
513         if ( is_scope == 0 ) {
514                 sres = LDAP_NO_SUCH_OBJECT;
515         }
516
517         if ( sres == LDAP_SUCCESS && v2refs ) {
518                 sres = LDAP_REFERRAL;
519         }
520         rs->sr_err = sres;
521         rs->sr_matched = mmatch.bv_val;
522         rs->sr_v2ref = v2refs;
523         send_ldap_result( op, rs );
524         rs->sr_matched = NULL;
525         rs->sr_v2ref = NULL;
526
527
528 finish:;
529         if ( match.bv_val ) {
530                 if ( mmatch.bv_val != match.bv_val ) {
531                         free( mmatch.bv_val );
532                 }
533                 free( match.bv_val );
534         }
535         
536         if ( err ) {
537                 free( err );
538         }
539         
540         if ( msgid ) {
541                 ch_free( msgid );
542         }
543
544         return rc;
545 }
546
547 static int
548 meta_send_entry(
549                 Operation       *op,
550                 SlapReply       *rs,
551                 struct metaconn *lc,
552                 int             target,
553                 LDAPMessage     *e
554 )
555 {
556         struct metainfo         *li = ( struct metainfo * )op->o_bd->be_private;
557         struct berval           a, mapped;
558         Entry                   ent = {0};
559         BerElement              ber = *e->lm_ber;
560         Attribute               *attr, **attrp;
561         struct berval           dummy = BER_BVNULL;
562         struct berval           *bv, bdn;
563         const char              *text;
564         dncookie                dc;
565
566         if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
567                 return LDAP_DECODING_ERROR;
568         }
569
570         /*
571          * Rewrite the dn of the result, if needed
572          */
573         dc.rwmap = &li->targets[ target ]->rwmap;
574         dc.conn = op->o_conn;
575         dc.rs = rs;
576         dc.ctx = "searchResult";
577
578         rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &ent.e_name );
579         if ( rs->sr_err != LDAP_SUCCESS) {
580                 return rs->sr_err;
581         }
582
583         /*
584          * Note: this may fail if the target host(s) schema differs
585          * from the one known to the meta, and a DN with unknown
586          * attributes is returned.
587          * 
588          * FIXME: should we log anything, or delegate to dnNormalize?
589          */
590         if ( dnNormalize( 0, NULL, NULL, &ent.e_name, &ent.e_nname,
591                 &op->o_tmpmemctx ) != LDAP_SUCCESS )
592         {
593                 return LDAP_INVALID_DN_SYNTAX;
594         }
595
596         /*
597          * cache dn
598          */
599         if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
600                 ( void )meta_dncache_update_entry( &li->cache,
601                                 &ent.e_nname, target );
602         }
603
604         attrp = &ent.e_attrs;
605
606         dc.ctx = "searchAttrDN";
607         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
608                 int             last = 0;
609
610                 ldap_back_map( &li->targets[ target ]->rwmap.rwm_at, 
611                                 &a, &mapped, BACKLDAP_REMAP );
612                 if ( mapped.bv_val == NULL || mapped.bv_val[0] == '\0' ) {
613                         continue;
614                 }
615                 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
616                 if ( attr == NULL ) {
617                         continue;
618                 }
619                 attr->a_flags = 0;
620                 attr->a_next = 0;
621                 attr->a_desc = NULL;
622                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
623                                 != LDAP_SUCCESS) {
624                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text ) 
625                                         != LDAP_SUCCESS) {
626 #ifdef NEW_LOGGING
627                                 LDAP_LOG( BACK_META, DETAIL1,
628                                         "slap_bv2undef_ad(%s): %s\n", mapped.bv_val, text, 0 );
629 #else /* !NEW_LOGGING */
630                                 Debug( LDAP_DEBUG_ANY,
631                                                 "slap_bv2undef_ad(%s): "
632                                                 "%s\n%s", mapped.bv_val, text, "" );
633 #endif /* !NEW_LOGGING */
634                                 ch_free( attr );
635                                 continue;
636                         }
637                 }
638
639                 /* no subschemaSubentry */
640                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry ) {
641
642                         /* 
643                          * We eat target's subschemaSubentry because
644                          * a search for this value is likely not
645                          * to resolve to the appropriate backend;
646                          * later, the local subschemaSubentry is
647                          * added.
648                          */
649                         ( void )ber_scanf( &ber, "x" /* [W] */ );
650
651                         ch_free(attr);
652                         continue;
653                 }
654
655                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR 
656                                 || attr->a_vals == NULL ) {
657                         attr->a_vals = &dummy;
658
659                 } else if ( attr->a_desc == slap_schema.si_ad_objectClass
660                                 || attr->a_desc == slap_schema.si_ad_structuralObjectClass ) {
661
662                         for ( last = 0; attr->a_vals[ last ].bv_val; ++last );
663
664                         for ( bv = attr->a_vals; bv->bv_val; bv++ ) {
665                                 ldap_back_map( &li->targets[ target ]->rwmap.rwm_oc,
666                                                 bv, &mapped, BACKLDAP_REMAP );
667                                 if ( mapped.bv_val == NULL || mapped.bv_val[0] == '\0') {
668                                         free( bv->bv_val );
669                                         bv->bv_val = NULL;
670                                         if ( --last < 0 ) {
671                                                 break;
672                                         }
673                                         *bv = attr->a_vals[ last ];
674                                         attr->a_vals[ last ].bv_val = NULL;
675                                         bv--;
676
677                                 } else if ( mapped.bv_val != bv->bv_val ) {
678                                         free( bv->bv_val );
679                                         ber_dupbv( bv, &mapped );
680                                 }
681                         }
682                 /*
683                  * It is necessary to try to rewrite attributes with
684                  * dn syntax because they might be used in ACLs as
685                  * members of groups; since ACLs are applied to the
686                  * rewritten stuff, no dn-based subecj clause could
687                  * be used at the ldap backend side (see
688                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
689                  * The problem can be overcome by moving the dn-based
690                  * ACLs to the target directory server, and letting
691                  * everything pass thru the ldap backend.
692                  */
693                 } else if ( attr->a_desc->ad_type->sat_syntax ==
694                                 slap_schema.si_syn_distinguishedName ) {
695                         ldap_dnattr_result_rewrite( &dc, attr->a_vals );
696                 }
697
698                 if ( last && attr->a_desc->ad_type->sat_equality &&
699                         attr->a_desc->ad_type->sat_equality->smr_normalize ) {
700                         int i;
701
702                         attr->a_nvals = ch_malloc((last + 1)*sizeof(struct berval));
703                         for ( i = 0; i<last; i++ ) {
704                                 attr->a_desc->ad_type->sat_equality->smr_normalize(
705                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
706                                         attr->a_desc->ad_type->sat_syntax,
707                                         attr->a_desc->ad_type->sat_equality,
708                                         &attr->a_vals[i], &attr->a_nvals[i],
709                                         op->o_tmpmemctx );
710                         }
711                         attr->a_nvals[i].bv_val = NULL;
712                         attr->a_nvals[i].bv_len = 0;
713                 } else {
714                         attr->a_nvals = attr->a_vals;
715                 }
716
717                 *attrp = attr;
718                 attrp = &attr->a_next;
719         }
720         rs->sr_entry = &ent;
721         rs->sr_attrs = op->ors_attrs;
722         rs->sr_flags = 0;
723         send_search_entry( op, rs );
724         rs->sr_entry = NULL;
725         rs->sr_attrs = NULL;
726         while ( ent.e_attrs ) {
727                 attr = ent.e_attrs;
728                 ent.e_attrs = attr->a_next;
729                 if ( attr->a_vals != &dummy ) {
730                         ber_bvarray_free( attr->a_vals );
731                 }
732                 free( attr );
733         }
734         
735         if ( ent.e_dn && ent.e_dn != bdn.bv_val ) {
736                 free( ent.e_dn );
737         }
738         if ( ent.e_ndn ) {
739                 free( ent.e_ndn );
740         }
741
742         return LDAP_SUCCESS;
743 }
744
745 static int
746 is_one_level_rdn(
747                 const char      *rdn,
748                 int             from
749 )
750 {
751         for ( ; from--; ) {
752                 if ( DN_SEPARATOR( rdn[ from ] ) ) {
753                         return 0;
754                 }
755         }
756
757         return 1;
758 }
759