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