]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/search.c
semicolon after colon in goto labels; no C++ style comments; unused vars removed...
[openldap] / servers / slapd / back-meta / search.c
1 /*
2  * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  *
5  * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
6  *
7  * This work has been developed to fulfill the requirements
8  * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
9  * to the OpenLDAP Foundation in the hope that it may be useful
10  * to the Open Source community, but WITHOUT ANY WARRANTY.
11  *
12  * Permission is granted to anyone to use this software for any purpose
13  * on any computer system, and to alter it and redistribute it, subject
14  * to the following restrictions:
15  *
16  * 1. The author and SysNet s.n.c. are not responsible for the consequences
17  *    of use of this software, no matter how awful, even if they arise from 
18  *    flaws in it.
19  *
20  * 2. The origin of this software must not be misrepresented, either by
21  *    explicit claim or by omission.  Since few users ever read sources,
22  *    credits should appear in the documentation.
23  *
24  * 3. Altered versions must be plainly marked as such, and must not be
25  *    misrepresented as being the original software.  Since few users
26  *    ever read sources, credits should appear in the documentation.
27  *    SysNet s.n.c. cannot be responsible for the consequences of the
28  *    alterations.
29  *
30  * 4. This notice may not be removed or altered.
31  *
32  *
33  * This software is based on the backend back-ldap, implemented
34  * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
35  * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
36  * contributors. The contribution of the original software to the present
37  * implementation is acknowledged in this copyright statement.
38  *
39  * A special acknowledgement goes to Howard for the overall architecture
40  * (and for borrowing large pieces of code), and to Mark, who implemented
41  * from scratch the attribute/objectclass mapping.
42  *
43  * The original copyright statement follows.
44  *
45  * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
46  *
47  * Permission is granted to anyone to use this software for any purpose
48  * on any computer system, and to alter it and redistribute it, subject
49  * to the following restrictions:
50  *
51  * 1. The author is not responsible for the consequences of use of this
52  *    software, no matter how awful, even if they arise from flaws in it.
53  *
54  * 2. The origin of this software must not be misrepresented, either by
55  *    explicit claim or by omission.  Since few users ever read sources,
56  *    credits should appear in the documentation.
57  *
58  * 3. Altered versions must be plainly marked as such, and must not be
59  *    misrepresented as being the original software.  Since few users
60  *    ever read sources, credits should appear in the
61  *    documentation.
62  *
63  * 4. This notice may not be removed or altered.
64  *                
65  */
66
67 #include "portable.h"
68
69 #include <stdio.h>
70
71 #include <ac/socket.h>
72 #include <ac/string.h>
73 #include <ac/time.h>
74
75 #include "slap.h"
76 #include "../back-ldap/back-ldap.h"
77 #include "back-meta.h"
78 #include "ldap_pvt.h"
79
80 static void
81 meta_send_entry(
82                 Backend         *be,
83                 Operation       *op,
84                 struct metaconn *lc,
85                 int             i,
86                 LDAPMessage     *e,
87                 char            **attrs,
88                 int             attrsonly
89 );
90
91 static int
92 is_one_level_rdn(
93                 const char *rdn,
94                 int len
95 );
96
97 int
98 meta_back_search(
99                 Backend         *be,
100                 Connection      *conn,
101                 Operation       *op,
102                 const char      *base,
103                 const char      *nbase,
104                 int             scope,
105                 int             deref,
106                 int             size,
107                 int             time,
108                 Filter          *filter,
109                 const char      *filterstr,
110                 char            **attrs,
111                 int             attrsonly
112 )
113 {
114         struct metainfo *li = ( struct metainfo * )be->be_private;
115         struct metaconn *lc;
116         struct metasingleconn **lsc;
117         struct timeval  tv;
118         LDAPMessage     *res, *e;
119         int     count, rc = 0, *msgid, sres = LDAP_NO_SUCH_OBJECT;
120         char *match = NULL, *err = NULL;
121         char *mbase = NULL, *mfilter = NULL, *mmatch = NULL, 
122                 *mapped_filter = NULL, **mapped_attrs = NULL;
123                 
124         int i, last = 0, candidates = 0, nbaselen, op_type;
125
126         if ( scope == LDAP_SCOPE_BASE ) {
127                 op_type = META_OP_REQUIRE_SINGLE;
128         } else {
129                 op_type = META_OP_ALLOW_MULTIPLE;
130         }
131         
132         lc = meta_back_getconn( li, conn, op, op_type, nbase, NULL );
133         if ( !lc || !meta_back_dobind( lc, op ) ) {
134                 return -1;
135         }
136
137         /*
138          * Array of message id of each target
139          */
140         msgid = ch_calloc( sizeof( int ), li->ntargets );
141         if ( msgid == NULL ) {
142                 return -1;
143         }
144         
145         nbaselen = strlen( nbase );
146
147         /*
148          * Inits searches
149          */
150         for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) {
151                 char *realbase = ( char * )base;
152                 int realscope = scope;
153                 int suffixlen;
154                 
155                 if ( lsc[ 0 ]->candidate != META_CANDIDATE ) {
156                         continue;
157                 }
158
159                 if ( deref != -1 ) {
160                         ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_DEREF,
161                                         ( void * )&deref);
162                 }
163                 if ( time != -1 ) {
164                         ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_TIMELIMIT,
165                                         ( void * )&time);
166                 }
167                 if ( size != -1 ) {
168                         ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_SIZELIMIT,
169                                         ( void * )&size);
170                 }
171
172                 /*
173                  * modifies the base according to the scope, if required
174                  */
175                 suffixlen = strlen( li->targets[ i ]->suffix );
176                 if ( suffixlen > nbaselen ) {
177                         switch ( scope ) {
178                         case LDAP_SCOPE_SUBTREE:
179                                 /*
180                                  * make the target suffix the new base
181                                  */
182                                 realbase = li->targets[ i ]->suffix;
183                                 break;
184
185                         case LDAP_SCOPE_ONELEVEL:
186                                 if ( is_one_level_rdn( li->targets[ i ]->suffix,
187                                                 suffixlen-nbaselen-1) ) {
188                                         /*
189                                          * if there is exactly one level,
190                                          * make the target suffix the new
191                                          * base, and make scope "base"
192                                          */
193                                         realbase = li->targets[ i ]->suffix;
194                                         realscope = LDAP_SCOPE_BASE;
195                                         break;
196                                 } /* else continue with the next case */
197
198                         case LDAP_SCOPE_BASE:
199                                 /*
200                                  * this target is no longer candidate
201                                  */
202                                 lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
203                                 continue;
204                                 /*
205                                 rc = meta_back_op_result(lc, op);
206                                 goto finish;
207                                  */
208                         }
209
210                 }
211
212                 /*
213                  * Rewrite the search base, if required
214                  */
215                 switch ( rewrite_session( li->targets[ i ]->rwinfo,
216                                         "searchBase",
217                                         realbase, conn, &mbase ) ) {
218                 case REWRITE_REGEXEC_OK:
219                 if ( mbase == NULL ) {
220                         mbase = realbase;
221                 }
222 #ifdef NEW_LOGGING
223                 LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
224                                 "[rw] searchBase: \"%s\" -> \"%s\"\n",
225                                 base, mbase ));
226 #else /* !NEW_LOGGING */
227                 Debug( LDAP_DEBUG_ARGS, "rw> searchBase: \"%s\" -> \"%s\"\n%s",
228                                 base, mbase, "" );
229 #endif /* !NEW_LOGGING */
230                 break;
231                 
232                 case REWRITE_REGEXEC_UNWILLING:
233                         send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
234                                         NULL, "Unwilling to perform",
235                                         NULL, NULL );
236                         rc = -1;
237                         goto finish;
238
239                 case REWRITE_REGEXEC_ERR:
240                         send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
241                                         NULL, "Operations error",
242                                         NULL, NULL );
243                         rc = -1;
244                         goto finish;
245                 }
246         
247                 /*
248                  * Rewrite the search filter, if required
249                  */
250                 switch ( rewrite_session( li->targets[ i ]->rwinfo,
251                                         "searchFilter",
252                                         filterstr, conn, &mfilter ) ) {
253                 case REWRITE_REGEXEC_OK:
254                         if ( mfilter == NULL || mfilter[ 0 ] == '\0') {
255                                 if ( mfilter != NULL ) {
256                                         free( mfilter );
257                                 }
258                                 mfilter = ( char * )filterstr;
259                         }
260 #ifdef NEW_LOGGING
261                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
262                                         "[rw] searchFilter: \"%s\" -> \"%s\"\n",
263                                         filterstr, mfilter ));
264 #else /* !NEW_LOGGING */
265                         Debug( LDAP_DEBUG_ARGS,
266                                 "rw> searchFilter: \"%s\" -> \"%s\"\n%s",
267                                 filterstr, mfilter, "" );
268 #endif /* !NEW_LOGGING */
269                         break;
270                 
271                 case REWRITE_REGEXEC_UNWILLING:
272                         send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
273                                         NULL, "Unwilling to perform",
274                                         NULL, NULL );
275                         /* continue to the next case */
276
277                 case REWRITE_REGEXEC_ERR:
278                         rc = -1;
279                         goto finish;
280                 }
281
282                 /*
283                  * Maps attributes in filter
284                  */
285                 mapped_filter = ldap_back_map_filter( &li->targets[ i ]->at_map,
286                                 &li->targets[ i ]->oc_map,
287                                 ( char * )mfilter, 0 );
288                 if ( mapped_filter == NULL ) {
289                         mapped_filter = ( char * )mfilter;
290                 }
291         
292                 /*
293                  * Maps required attributes
294                  */
295                 mapped_attrs = ldap_back_map_attrs( &li->targets[ i ]->at_map,
296                                 attrs, 0 );
297                 if ( mapped_attrs == NULL ) {
298                         mapped_attrs = attrs;
299                 }
300
301                 /*
302                  * Starts the search
303                  */
304                 msgid[ i ] = ldap_search( lsc[ 0 ]->ld, mbase, realscope,
305                                 mapped_filter, mapped_attrs, attrsonly); 
306                 if ( msgid[ i ] == -1 ) {
307                         lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
308                         continue;
309                 }
310
311                 if ( mapped_attrs != attrs ) {
312                         charray_free( mapped_attrs );
313                         mapped_attrs = NULL;
314                 }
315                 if ( mapped_filter != mfilter ) {
316                         free( mapped_filter );
317                         mapped_filter = NULL;
318                 }
319                 if ( mfilter != filterstr ) {
320                         free( mfilter );
321                         mfilter = NULL;
322                 }
323                 if ( mbase != realbase ) {
324                         free( mbase );
325                         mbase = NULL;
326                 }
327
328                 ++candidates;
329         }
330
331         /* We pull apart the ber result, stuff it into a slapd entry, and
332          * let send_search_entry stuff it back into ber format. Slow & ugly,
333          * but this is necessary for version matching, and for ACL processing.
334          */
335
336
337         /*
338          * In case there are no candidates, no cycle takes place...
339          */
340         for ( count = 0, rc = 0; candidates > 0; ) {
341                 int ab, gotit = 0;
342
343                 /* check for abandon */
344                 ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
345                 ab = op->o_abandon;
346                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
347
348                 for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; lsc++, i++ ) {
349                         if ( lsc[ 0 ]->candidate != META_CANDIDATE ) {
350                                 continue;
351                         }
352                         
353                         if ( ab ) {
354                                 ldap_abandon( lsc[ 0 ]->ld, msgid[ i ] );
355                                 rc = 0;
356                                 break;
357                         }
358
359                         rc = ldap_result( lsc[ 0 ]->ld, msgid[ i ],
360                                         0, &tv, &res );
361
362                         if ( rc == 0 ) {
363                                 continue;
364                         } else if ( rc == -1 ) {
365                                 /* something REALLY bad happened! */
366                                 ( void )meta_clear_unused_candidates( li,
367                                                 lc, -1, 0 );
368                                 send_search_result( conn, op,
369                                                 LDAP_OPERATIONS_ERROR,
370                                                 "", "", NULL, NULL, count );
371                                 
372                                 /* anything else needs be done? */
373                                 goto finish;
374                         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
375                                 e = ldap_first_entry( lsc[ 0 ]->ld,res );
376                                 meta_send_entry(be, op, lc, i, e, attrs,
377                                                 attrsonly);
378                                 count++;
379                                 ldap_msgfree( res );
380                                 gotit = 1;
381                         } else {
382                                 sres = ldap_result2error( lsc[ 0 ]->ld,
383                                                 res, 1 );
384                                 sres = ldap_back_map_result( sres );
385                                 if ( err != NULL ) {
386                                         free( err );
387                                 }
388                                 ldap_get_option( lsc[ 0 ]->ld,
389                                                 LDAP_OPT_ERROR_STRING, &err );
390                                 if ( match != NULL ) {
391                                         free( match );
392                                 }
393                                 ldap_get_option( lsc[ 0 ]->ld,
394                                                 LDAP_OPT_MATCHED_DN, &match );
395
396 #ifdef NEW_LOGGING
397                                 LDAP_LOG(( "backend", LDAP_LEVEL_ERR,
398                                                 "meta_back_search [%d]"
399                                                 " match=\"%s\" err=\"%s\"\n",
400                                                 i, match, err ));
401 #else /* !NEW_LOGGING */
402                                 Debug( LDAP_DEBUG_ANY,
403         "=>meta_back_search [%d] match=\"%s\" err=\"%s\"\n",
404                                         i, match, err );        
405 #endif /* !NEW_LOGGING */
406                                 
407                                 last = i;
408                                 rc = 0;
409
410                                 /*
411                                  * When no candidates are left,
412                                  * the outer cycle finishes
413                                  */
414                                 lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
415                                 --candidates;
416                         }
417                 }
418
419                 if ( ab ) {
420                         goto finish;
421                 }
422
423                 if ( gotit == 0 ) {
424                         tv.tv_sec = 0;
425                         tv.tv_usec = 100000;
426                         ldap_pvt_thread_yield();
427                 } else {
428                         tv.tv_sec = 0;
429                         tv.tv_usec = 0;
430                 }
431         }
432
433         if ( rc == -1 ) {
434                 /*
435                  * FIXME: need a strategy to handle errors
436                  */
437                 rc = meta_back_op_result( lc, op );
438                 goto finish;
439         }
440
441         /*
442          * Rewrite the matched portion of the search base, if required
443          * 
444          * FIXME: only the last one gets caught!
445          */
446         if ( match != NULL ) {
447                 switch ( rewrite_session( li->targets[ last ]->rwinfo,
448                                         "matchedDn", match, conn, &mmatch ) ) {
449                 case REWRITE_REGEXEC_OK:
450                         if ( mmatch == NULL ) {
451                                 mmatch = ( char * )match;
452                         }
453 #ifdef NEW_LOGGING
454                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
455                                         "[rw] matchedDn: \"%s\" -> \"%s\"\n",
456                                         match, mmatch ));
457 #else /* !NEW_LOGGING */
458                         Debug( LDAP_DEBUG_ARGS, "rw> matchedDn:"
459                                        " \"%s\" -> \"%s\"\n%s",
460                                        match, mmatch, "" );
461 #endif /* !NEW_LOGGING */
462                         break;
463                         
464                 case REWRITE_REGEXEC_UNWILLING:
465                         send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
466                                         NULL, "Unwilling to perform",
467                                         NULL, NULL );
468                         /* continue to the next case */
469                         
470                 case REWRITE_REGEXEC_ERR:
471                         rc = -1;
472                         goto finish;
473                 }
474         }
475
476         send_search_result( conn, op, sres,
477                 mmatch, err, NULL, NULL, count );
478
479 finish:;
480         if ( match ) {
481                 if ( mmatch != match ) {
482                         free( mmatch );
483                 }
484                 free(match);
485         }
486         
487         if ( err ) {
488                 free( err );
489         }
490         
491         if ( msgid ) {
492                 free( msgid );
493         }
494         
495         return rc;
496 }
497
498 static void
499 meta_send_entry(
500                 Backend *be,
501                 Operation *op,
502                 struct metaconn *lc,
503                 int target,
504                 LDAPMessage *e,
505                 char **attrs,
506                 int attrsonly
507 )
508 {
509         struct metainfo *li = ( struct metainfo * )be->be_private;
510         char *a, *mapped;
511         Entry ent;
512         BerElement *ber = NULL;
513         Attribute *attr, **attrp;
514         struct berval *dummy = NULL;
515         struct berval *bv;
516         const char *text;
517         char *dn;
518
519         struct metasingleconn *lsc = lc->conns[ target ];
520
521         dn = ldap_get_dn( lsc->ld, e );
522         if ( dn == NULL ) {
523                 return;
524         }
525
526         /*
527          * Rewrite the dn of the result, if needed
528          */
529         switch ( rewrite_session( li->targets[ target ]->rwinfo,
530                                 "searchResult", dn, lc->conn, &ent.e_dn ) ) {
531         case REWRITE_REGEXEC_OK:
532                 if ( ent.e_dn == NULL ) {
533                         ent.e_dn = dn;
534                 } else {
535 #ifdef NEW_LOGGING
536                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
537                                         "[rw] searchResult[%d]:"
538                                         " \"%s\" -> \"%s\"\n",
539                                         target, dn, ent.e_dn ));
540 #else /* !NEW_LOGGING */
541                         Debug( LDAP_DEBUG_ARGS, "rw> searchResult[%d]: \"%s\""
542                                         " -> \"%s\"\n", target, dn, ent.e_dn );
543 #endif /* !NEW_LOGGING */
544                         free( dn );
545                         dn = NULL;
546                 }
547                 break;
548                 
549         case REWRITE_REGEXEC_ERR:
550         case REWRITE_REGEXEC_UNWILLING:
551                 free( dn );
552                 return;
553         }
554
555         ent.e_ndn = ch_strdup( ent.e_dn );
556         ( void )dn_normalize( ent.e_ndn );
557
558         /*
559          * cache dn
560          */
561         if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
562                 ( void )meta_dncache_update_entry( &li->cache,
563                                                    ch_strdup( ent.e_ndn ),
564                                                    target );
565         }
566
567         ent.e_id = 0;
568         ent.e_attrs = 0;
569         ent.e_private = 0;
570         attrp = &ent.e_attrs;
571
572         for ( a = ldap_first_attribute( lsc->ld, e, &ber );
573                         a != NULL;
574                         a = ldap_next_attribute( lsc->ld, e, ber ) )
575         {
576                 mapped = ldap_back_map( &li->targets[ target ]->at_map, a, 1 );
577                 if ( mapped == NULL ) {
578                         continue;
579                 }
580                 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
581                 if ( attr == NULL ) {
582                         continue;
583                 }
584                 attr->a_next = 0;
585                 attr->a_desc = NULL;
586                 if ( slap_str2ad( mapped, &attr->a_desc, &text )
587                                 != LDAP_SUCCESS) {
588                         ch_free( attr );
589                         continue;
590                 }
591                 attr->a_vals = ldap_get_values_len( lsc->ld, e, a );
592                 if ( !attr->a_vals ) {
593                         attr->a_vals = &dummy;
594                 } else if ( strcasecmp( mapped, "objectClass" ) == 0 ) {
595                         int i, last;
596                         for ( last = 0; attr->a_vals[ last ]; ++last ) ;
597                         for ( i = 0; ( bv = attr->a_vals[ i ] ); i++ ) {
598                                 mapped = ldap_back_map(
599                                                 &li->targets[ target]->oc_map,
600                                                 bv->bv_val, 1 );
601                                 if ( mapped == NULL ) {
602                                         ber_bvfree( attr->a_vals[ i ] );
603                                         attr->a_vals[ i ] = NULL;
604                                         if ( --last < 0 ) {
605                                                 break;
606                                         }
607                                         attr->a_vals[ i ] = 
608                                                 attr->a_vals[ last ];
609                                         attr->a_vals[ last ] = NULL;
610                                         --i;
611                                 } else if ( mapped != bv->bv_val ) {
612                                         ch_free( bv->bv_val );
613                                         bv->bv_val = ch_strdup( mapped );
614                                         bv->bv_len = strlen( mapped );
615                                 }
616                         }
617                 /*
618                  * It is necessary to try to rewrite attributes with
619                  * dn syntax because they might be used in ACLs as
620                  * members of groups; since ACLs are applied to the
621                  * rewritten stuff, no dn-based subecj clause could
622                  * be used at the ldap backend side (see
623                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
624                  * The problem can be overcome by moving the dn-based
625                  * ACLs to the target directory server, and letting
626                  * everything pass thru the ldap backend.
627                  */
628                 } else if ( strcmp( attr->a_desc->ad_type->sat_syntax->ssyn_oid,
629                                         SLAPD_DN_SYNTAX ) == 0 ) {
630                         int i;
631                         for ( i = 0; ( bv = attr->a_vals[ i ] ); i++ ) {
632                                 char *newval;
633
634                                 switch ( rewrite_session( li->targets[ target ]->rwinfo,
635                                                         "searchResult",
636                                                         bv->bv_val,
637                                                         lc->conn, &newval )) {
638                                 case REWRITE_REGEXEC_OK:
639                                         /* left as is */
640                                         if ( newval == NULL ) {
641                                                 break;
642                                         }
643 #ifdef NEW_LOGGING
644                                         LDAP_LOG(( "backend",
645                                                         LDAP_LEVEL_DETAIL1,
646                                                         "[rw] searchResult on"
647                                                         " attr=%s:"
648                                                         " \"%s\" -> \"%s\"\n",
649                                         attr->a_desc->ad_type->sat_cname,
650                                                         bv->bv_val, newval ));
651 #else /* !NEW_LOGGING */
652                                         Debug( LDAP_DEBUG_ARGS,
653                                                 "rw> searchResult on attr=%s:"
654                                                 " \"%s\" -> \"%s\"\n",
655                                         attr->a_desc->ad_type->sat_cname,
656                                                 bv->bv_val, newval );
657 #endif /* !NEW_LOGGING */
658                                         
659                                         free( bv->bv_val );
660                                         bv->bv_val = newval;
661                                         bv->bv_len = strlen( newval );
662
663                                         break;
664
665                                 case REWRITE_REGEXEC_UNWILLING:
666                                         
667                                 case REWRITE_REGEXEC_ERR:
668                                         /*
669                                          * FIXME: better give up,
670                                          * skip the attribute
671                                          * or leave it untouched?
672                                          */
673                                         break;
674                                 }
675                         }
676                 }
677                 *attrp = attr;
678                 attrp = &attr->a_next;
679         }
680         send_search_entry( be, lc->conn, op, &ent, attrs, attrsonly, NULL );
681         while ( ent.e_attrs ) {
682                 attr = ent.e_attrs;
683                 ent.e_attrs = attr->a_next;
684                 ad_free( attr->a_desc, 1 );
685                 if ( attr->a_vals != &dummy ) {
686                         ber_bvecfree(attr->a_vals);
687                 }
688                 free( attr );
689         }
690         if ( ber ) {
691                 ber_free( ber, 0 );
692         }
693         
694         if ( ent.e_dn ) {
695                 free( ent.e_dn );
696         }
697         if ( ent.e_ndn ) {
698                 free( ent.e_ndn );
699         }
700 }
701
702 static int
703 is_one_level_rdn(
704                 const char *rdn,
705                 int len
706 )
707 {
708         for ( ; len--; ) {
709                 if ( LDAP_DNSEPARATOR( rdn[ len ] ) ) {
710                         return 0;
711                 }
712         }
713
714         return 1;
715 }
716