]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/search.c
make sure NULL pointers are not dereferenced
[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-2006 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 /* IGNORE means that target does not (no longer) participate
39  * in the search;
40  * NOTREADY means the search on that target has not been initialized yet
41  */
42 #define META_MSGID_IGNORE       (-1)
43
44 static int
45 meta_send_entry(
46         Operation       *op,
47         SlapReply       *rs,
48         metaconn_t      *mc,
49         int             i,
50         LDAPMessage     *e );
51
52 typedef enum meta_search_candidate_t {
53         META_SEARCH_ERR = -1,
54         META_SEARCH_NOT_CANDIDATE,
55         META_SEARCH_CANDIDATE
56 } meta_search_candidate_t;
57
58 static meta_search_candidate_t
59 meta_back_search_start(
60         Operation               *op,
61         SlapReply               *rs,
62         dncookie                *dc,
63         metaconn_t              **mcp,
64         int                     candidate,
65         SlapReply               *candidates )
66 {
67         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
68         metatarget_t            *mt = &mi->mi_targets[ candidate ];
69         metaconn_t              *mc = *mcp;
70         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
71         struct berval           realbase = op->o_req_dn;
72         int                     realscope = op->ors_scope;
73         ber_len_t               suffixlen = 0;
74         struct berval           mbase = BER_BVNULL; 
75         struct berval           mfilter = BER_BVNULL;
76         char                    **mapped_attrs = NULL;
77         int                     rc;
78         meta_search_candidate_t retcode;
79         struct timeval          tv, *tvp = NULL;
80         int                     nretries = 1;
81
82         /* should we check return values? */
83         if ( op->ors_deref != -1 ) {
84                 ldap_set_option( msc->msc_ld, LDAP_OPT_DEREF,
85                                 ( void * )&op->ors_deref );
86         }
87
88         if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
89                 tv.tv_sec = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
90                 tv.tv_usec = 0;
91                 tvp = &tv;
92         }
93
94         dc->target = mt;
95
96         /*
97          * modifies the base according to the scope, if required
98          */
99         suffixlen = mt->mt_nsuffix.bv_len;
100         if ( suffixlen > op->o_req_ndn.bv_len ) {
101                 switch ( op->ors_scope ) {
102                 case LDAP_SCOPE_SUBTREE:
103                         /*
104                          * make the target suffix the new base
105                          * FIXME: this is very forgiving, because
106                          * "illegal" searchBases may be turned
107                          * into the suffix of the target; however,
108                          * the requested searchBase already passed
109                          * thru the candidate analyzer...
110                          */
111                         if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) {
112                                 realbase = mt->mt_nsuffix;
113                                 if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
114                                         realscope = LDAP_SCOPE_SUBORDINATE;
115                                 }
116
117                         } else {
118                                 /*
119                                  * this target is no longer candidate
120                                  */
121                                 return META_SEARCH_NOT_CANDIDATE;
122                         }
123                         break;
124
125                 case LDAP_SCOPE_SUBORDINATE:
126                 case LDAP_SCOPE_ONELEVEL:
127                 {
128                         struct berval   rdn = mt->mt_nsuffix;
129                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
130                         if ( dnIsOneLevelRDN( &rdn )
131                                         && dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) )
132                         {
133                                 /*
134                                  * if there is exactly one level,
135                                  * make the target suffix the new
136                                  * base, and make scope "base"
137                                  */
138                                 realbase = mt->mt_nsuffix;
139                                 if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
140                                         if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
141                                                 realscope = LDAP_SCOPE_SUBORDINATE;
142                                         } else {
143                                                 realscope = LDAP_SCOPE_SUBTREE;
144                                         }
145                                 } else {
146                                         realscope = LDAP_SCOPE_BASE;
147                                 }
148                                 break;
149                         } /* else continue with the next case */
150                 }
151
152                 case LDAP_SCOPE_BASE:
153                         /*
154                          * this target is no longer candidate
155                          */
156                         return META_SEARCH_NOT_CANDIDATE;
157                 }
158         }
159
160         /*
161          * Rewrite the search base, if required
162          */
163         dc->ctx = "searchBase";
164         switch ( ldap_back_dn_massage( dc, &realbase, &mbase ) ) {
165         default:
166                 break;
167
168         case REWRITE_REGEXEC_UNWILLING:
169                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
170                 rs->sr_text = "Operation not allowed";
171                 send_ldap_result( op, rs );
172                 return META_SEARCH_ERR;
173
174         case REWRITE_REGEXEC_ERR:
175
176                 /*
177                  * this target is no longer candidate
178                  */
179                 return META_SEARCH_NOT_CANDIDATE;
180         }
181
182         /*
183          * Maps filter
184          */
185         rc = ldap_back_filter_map_rewrite( dc, op->ors_filter,
186                         &mfilter, BACKLDAP_MAP );
187         switch ( rc ) {
188         case LDAP_SUCCESS:
189                 break;
190
191         case LDAP_COMPARE_FALSE:
192         default:
193                 /*
194                  * this target is no longer candidate
195                  */
196                 retcode = META_SEARCH_NOT_CANDIDATE;
197                 goto done;
198         }
199
200         /*
201          * Maps required attributes
202          */
203         rc = ldap_back_map_attrs( &mt->mt_rwmap.rwm_at,
204                         op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
205         if ( rc != LDAP_SUCCESS ) {
206                 /*
207                  * this target is no longer candidate
208                  */
209                 retcode = META_SEARCH_NOT_CANDIDATE;
210                 goto done;
211         }
212
213         /*
214          * Starts the search
215          */
216 retry:;
217         rc = ldap_search_ext( msc->msc_ld,
218                         mbase.bv_val, realscope, mfilter.bv_val,
219                         mapped_attrs, op->ors_attrsonly,
220                         op->o_ctrls, NULL, tvp, op->ors_slimit,
221                         &candidates[ candidate ].sr_msgid ); 
222         switch ( rc ) {
223         case LDAP_SUCCESS:
224                 retcode = META_SEARCH_CANDIDATE;
225                 break;
226         
227         case LDAP_SERVER_DOWN:
228                 if ( nretries && meta_back_retry( op, rs, mcp, candidate, LDAP_BACK_DONTSEND ) ) {
229                         nretries = 0;
230                         goto retry;
231                 }
232
233                 if ( *mcp == NULL ) {
234                         retcode = META_SEARCH_ERR;
235                         break;
236                 }
237                 /* fall thru */
238
239         default:
240                 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
241                 retcode = META_SEARCH_NOT_CANDIDATE;
242         }
243
244 done:;
245         if ( mapped_attrs ) {
246                 free( mapped_attrs );
247         }
248         if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
249                 free( mfilter.bv_val );
250         }
251         if ( mbase.bv_val != realbase.bv_val ) {
252                 free( mbase.bv_val );
253         }
254
255         return retcode;
256 }
257
258 int
259 meta_back_search( Operation *op, SlapReply *rs )
260 {
261         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
262         metaconn_t      *mc;
263         struct timeval  tv = { 0, 0 };
264         time_t          stoptime = (time_t)-1;
265         LDAPMessage     *res = NULL, *e;
266         int             rc = 0, sres = LDAP_SUCCESS;
267         char            *matched = NULL;
268         int             last = 0, ncandidates = 0,
269                         initial_candidates = 0, candidate_match = 0;
270         long            i;
271         dncookie        dc;
272         int             is_ok = 0;
273         void            *savepriv;
274         SlapReply       *candidates = meta_back_candidates_get( op );
275
276         /*
277          * controls are set in ldap_back_dobind()
278          * 
279          * FIXME: in case of values return filter, we might want
280          * to map attrs and maybe rewrite value
281          */
282         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR );
283         if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
284                 return rs->sr_err;
285         }
286
287         dc.conn = op->o_conn;
288         dc.rs = rs;
289
290         /*
291          * Inits searches
292          */
293         for ( i = 0; i < mi->mi_ntargets; i++ ) {
294                 candidates[ i ].sr_msgid = META_MSGID_IGNORE;
295                 candidates[ i ].sr_matched = NULL;
296                 candidates[ i ].sr_text = NULL;
297                 candidates[ i ].sr_ref = NULL;
298                 candidates[ i ].sr_ctrls = NULL;
299
300                 if ( candidates[ i ].sr_tag != META_CANDIDATE
301                         || candidates[ i ].sr_err != LDAP_SUCCESS )
302                 {
303                         continue;
304                 }
305
306                 switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates ) )
307                 {
308                 case META_SEARCH_NOT_CANDIDATE:
309                         break;
310
311                 case META_SEARCH_CANDIDATE:
312                         candidates[ i ].sr_type = REP_INTERMEDIATE;
313                         ++ncandidates;
314                         break;
315
316                 case META_SEARCH_ERR:
317                         rc = -1;
318                         goto finish;
319                 }
320         }
321
322         initial_candidates = ncandidates;
323
324 #if 0
325         {
326                 char    cnd[BUFSIZ];
327                 int     i;
328
329                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
330                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
331                                 cnd[ i ] = '*';
332                         } else {
333                                 cnd[ i ] = ' ';
334                         }
335                 }
336                 cnd[ i ] = '\0';
337
338                 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: ncandidates=%d "
339                         "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
340         }
341 #endif
342
343         if ( initial_candidates == 0 ) {
344                 /* NOTE: here we are not sending any matchedDN;
345                  * this is intended, because if the back-meta
346                  * is serving this search request, but no valid
347                  * candidate could be looked up, it means that
348                  * there is a hole in the mapping of the targets
349                  * and thus no knowledge of any remote superior
350                  * is available */
351                 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: "
352                         "base=\"%s\" scope=%d: "
353                         "no candidate could be selected\n",
354                         op->o_log_prefix, op->o_req_dn.bv_val,
355                         op->ors_scope );
356
357                 /* FIXME: we're sending the first error we encounter;
358                  * maybe we should pick the worst... */
359                 rc = LDAP_NO_SUCH_OBJECT;
360                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
361                         if ( candidates[ i ].sr_tag == META_CANDIDATE
362                                 && candidates[ i ].sr_err != LDAP_SUCCESS )
363                         {
364                                 rc = candidates[ i ].sr_err;
365                                 break;
366                         }
367                 }
368
369                 send_ldap_error( op, rs, rc, NULL );
370
371                 goto finish;
372         }
373
374         /* We pull apart the ber result, stuff it into a slapd entry, and
375          * let send_search_entry stuff it back into ber format. Slow & ugly,
376          * but this is necessary for version matching, and for ACL processing.
377          */
378
379         if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
380                 stoptime = op->o_time + op->ors_tlimit;
381         }
382
383         /*
384          * In case there are no candidates, no cycle takes place...
385          *
386          * FIXME: we might use a queue, to better balance the load 
387          * among the candidates
388          */
389         for ( rc = 0; ncandidates > 0; ) {
390                 int     gotit = 0, doabandon = 0;
391
392                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
393                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
394
395                         if ( candidates[ i ].sr_msgid == META_MSGID_IGNORE ) {
396                                 continue;
397                         }
398
399                         /* check for abandon */
400                         if ( op->o_abandon ) {
401                                 break;
402                         }
403                         
404                         /*
405                          * FIXME: handle time limit as well?
406                          * Note that target servers are likely 
407                          * to handle it, so at some time we'll
408                          * get a LDAP_TIMELIMIT_EXCEEDED from
409                          * one of them ...
410                          */
411 get_result:;
412                         rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid,
413                                         LDAP_MSG_ONE, &tv, &res );
414
415                         if ( rc == 0 ) {
416                                 /* FIXME: res should not need to be freed */
417                                 assert( res == NULL );
418
419                                 /* check time limit */
420                                 if ( op->ors_tlimit != SLAP_NO_LIMIT
421                                                 && slap_get_time() > stoptime )
422                                 {
423                                         doabandon = 1;
424                                         rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
425                                         savepriv = op->o_private;
426                                         op->o_private = (void *)i;
427                                         send_ldap_result( op, rs );
428                                         op->o_private = savepriv;
429                                         goto finish;
430                                 }
431
432                                 continue;
433
434                         } else if ( rc == -1 ) {
435 really_bad:;
436                                 /* something REALLY bad happened! */
437                                 if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
438                                         candidates[ i ].sr_type = REP_RESULT;
439
440                                         if ( meta_back_retry( op, rs, &mc, i, LDAP_BACK_DONTSEND ) ) {
441                                                 switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates ) )
442                                                 {
443                                                 case META_SEARCH_CANDIDATE:
444                                                         goto get_result;
445
446                                                 case META_SEARCH_NOT_CANDIDATE:
447                                                         assert( 0 );
448
449                                                 default:
450                                                         rc = rs->sr_err = LDAP_OTHER;
451                                                         goto finish;
452                                                 }
453                                         }
454
455                                         if ( META_BACK_ONERR_STOP( mi ) ) {
456                                                 savepriv = op->o_private;
457                                                 op->o_private = (void *)i;
458                                                 send_ldap_result( op, rs );
459                                                 op->o_private = savepriv;
460                                                 goto finish;
461                                         }
462                                 }
463
464                                 /*
465                                  * When no candidates are left,
466                                  * the outer cycle finishes
467                                  */
468                                 candidates[ i ].sr_msgid = META_MSGID_IGNORE;
469                                 --ncandidates;
470                                 rs->sr_err = candidates[ i ].sr_err = LDAP_OTHER;
471                                 rs->sr_text = "remote server unavailable";
472
473                         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
474                                 if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
475                                         /* don't retry any more... */
476                                         candidates[ i ].sr_type = REP_RESULT;
477                                 }
478
479                                 is_ok++;
480
481                                 e = ldap_first_entry( msc->msc_ld, res );
482                                 savepriv = op->o_private;
483                                 op->o_private = (void *)i;
484                                 rs->sr_err = meta_send_entry( op, rs, mc, i, e );
485                                 ldap_msgfree( res );
486                                 res = NULL;
487
488                                 switch ( rs->sr_err ) {
489                                 case LDAP_SIZELIMIT_EXCEEDED:
490                                         savepriv = op->o_private;
491                                         op->o_private = (void *)i;
492                                         send_ldap_result( op, rs );
493                                         op->o_private = savepriv;
494                                         rs->sr_err = LDAP_SUCCESS;
495                                         goto finish;
496
497                                 case LDAP_UNAVAILABLE:
498                                         rs->sr_err = LDAP_OTHER;
499                                         goto finish;
500                                 }
501                                 op->o_private = savepriv;
502
503                                 /* don't wait any longer... */
504                                 gotit = 1;
505                                 tv.tv_sec = 0;
506                                 tv.tv_usec = 0;
507
508 #if 0
509                                 /*
510                                  * If scope is BASE, we need to jump out
511                                  * as soon as one entry is found; if
512                                  * the target pool is properly crafted,
513                                  * this should correspond to the sole
514                                  * entry that has the base DN
515                                  */
516                                 /* FIXME: this defeats the purpose of
517                                  * doing a search with scope == base and
518                                  * sizelimit = 1 to determine if a
519                                  * candidate is actually unique */
520                                 if ( op->ors_scope == LDAP_SCOPE_BASE
521                                                 && rs->sr_nentries > 0 )
522                                 {
523                                         doabandon = 1;
524                                         ncandidates = 0;
525                                         sres = LDAP_SUCCESS;
526                                         break;
527                                 }
528 #endif
529
530                         } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
531                                 char            **references = NULL;
532                                 int             cnt;
533
534                                 if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
535                                         /* don't retry any more... */
536                                         candidates[ i ].sr_type = REP_RESULT;
537                                 }
538
539                                 is_ok++;
540
541                                 rc = ldap_parse_reference( msc->msc_ld, res,
542                                                 &references, &rs->sr_ctrls, 1 );
543                                 res = NULL;
544
545                                 if ( rc != LDAP_SUCCESS ) {
546                                         continue;
547                                 }
548
549                                 if ( references == NULL ) {
550                                         continue;
551                                 }
552
553 #ifdef ENABLE_REWRITE
554                                 dc.ctx = "referralDN";
555 #else /* ! ENABLE_REWRITE */
556                                 dc.tofrom = 0;
557                                 dc.normalized = 0;
558 #endif /* ! ENABLE_REWRITE */
559
560                                 /* FIXME: merge all and return at the end */
561
562                                 for ( cnt = 0; references[ cnt ]; cnt++ )
563                                         ;
564
565                                 rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
566
567                                 for ( cnt = 0; references[ cnt ]; cnt++ ) {
568                                         ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
569                                 }
570                                 BER_BVZERO( &rs->sr_ref[ cnt ] );
571
572                                 ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
573
574                                 if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
575                                         /* ignore return value by now */
576                                         savepriv = op->o_private;
577                                         op->o_private = (void *)i;
578                                         ( void )send_search_reference( op, rs );
579                                         op->o_private = savepriv;
580
581                                         ber_bvarray_free( rs->sr_ref );
582                                         rs->sr_ref = NULL;
583                                 }
584
585                                 /* cleanup */
586                                 if ( references ) {
587                                         ber_memvfree( (void **)references );
588                                 }
589
590                                 if ( rs->sr_ctrls ) {
591                                         ldap_controls_free( rs->sr_ctrls );
592                                         rs->sr_ctrls = NULL;
593                                 }
594
595                         } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
596                                 char            buf[ SLAP_TEXT_BUFLEN ];
597                                 char            **references = NULL;
598
599                                 if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
600                                         /* don't retry any more... */
601                                         candidates[ i ].sr_type = REP_RESULT;
602                                 }
603
604                                 /* NOTE: ignores response controls
605                                  * (and intermediate response controls
606                                  * as well, except for those with search
607                                  * references); this may not be correct,
608                                  * but if they're not ignored then
609                                  * back-meta would need to merge them
610                                  * consistently (think of pagedResults...)
611                                  */
612                                 rs->sr_err = ldap_parse_result( msc->msc_ld,
613                                                         res,
614                                                         &candidates[ i ].sr_err,
615                                                         (char **)&candidates[ i ].sr_matched,
616                                                         NULL /* (char **)&candidates[ i ].sr_text */ ,
617                                                         &references,
618                                                         NULL /* &candidates[ i ].sr_ctrls (unused) */ ,
619                                                         1 );
620                                 res = NULL;
621                                 if ( rs->sr_err != LDAP_SUCCESS ) {
622                                         ldap_get_option( msc->msc_ld,
623                                                         LDAP_OPT_ERROR_NUMBER,
624                                                         &rs->sr_err );
625                                         sres = slap_map_api2result( rs );
626                                         candidates[ i ].sr_type = REP_RESULT;
627                                         goto really_bad;
628                                 }
629
630                                 /* massage matchedDN if need be */
631                                 if ( candidates[ i ].sr_matched != NULL ) {
632                                         struct berval   match, mmatch;
633
634                                         ber_str2bv( candidates[ i ].sr_matched,
635                                                 0, 0, &match );
636                                         candidates[ i ].sr_matched = NULL;
637
638                                         dc.ctx = "matchedDN";
639                                         dc.target = &mi->mi_targets[ i ];
640                                         if ( !ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
641                                                 if ( mmatch.bv_val == match.bv_val ) {
642                                                         candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val );
643
644                                                 } else {
645                                                         candidates[ i ].sr_matched = mmatch.bv_val;
646                                                 }
647
648                                                 candidate_match++;
649                                         } 
650                                         ldap_memfree( match.bv_val );
651                                 }
652
653                                 /* add references to array */
654                                 if ( references ) {
655                                         BerVarray       sr_ref;
656                                         int             cnt;
657
658                                         for ( cnt = 0; references[ cnt ]; cnt++ )
659                                                 ;
660
661                                         sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
662
663                                         for ( cnt = 0; references[ cnt ]; cnt++ ) {
664                                                 ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] );
665                                         }
666                                         BER_BVZERO( &sr_ref[ cnt ] );
667
668                                         ( void )ldap_back_referral_result_rewrite( &dc, sr_ref );
669                                 
670                                         /* cleanup */
671                                         ber_memvfree( (void **)references );
672
673                                         if ( rs->sr_v2ref == NULL ) {
674                                                 rs->sr_v2ref = sr_ref;
675
676                                         } else {
677                                                 for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
678                                                         ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] );
679                                                 }
680                                                 ber_memfree( sr_ref );
681                                         }
682                                 }
683
684                                 rs->sr_err = candidates[ i ].sr_err;
685                                 sres = slap_map_api2result( rs );
686
687                                 if ( LogTest( LDAP_DEBUG_TRACE | LDAP_DEBUG_ANY ) ) {
688                                         snprintf( buf, sizeof( buf ),
689                                                 "%s meta_back_search[%ld] "
690                                                 "match=\"%s\" err=%ld",
691                                                 op->o_log_prefix, i,
692                                                 candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
693                                                 (long) candidates[ i ].sr_err );
694                                         if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
695                                                 Debug( LDAP_DEBUG_TRACE, "%s.\n", buf, 0, 0 );
696
697                                         } else {
698                                                 Debug( LDAP_DEBUG_ANY, "%s (%s).\n",
699                                                         buf, ldap_err2string( candidates[ i ].sr_err ), 0 );
700                                         }
701                                 }
702
703                                 switch ( sres ) {
704                                 case LDAP_NO_SUCH_OBJECT:
705                                         /* is_ok is touched any time a valid
706                                          * (even intermediate) result is
707                                          * returned; as a consequence, if
708                                          * a candidate returns noSuchObject
709                                          * it is ignored and the candidate
710                                          * is simply demoted. */
711                                         if ( is_ok ) {
712                                                 sres = LDAP_SUCCESS;
713                                         }
714                                         break;
715
716                                 case LDAP_SUCCESS:
717                                 case LDAP_REFERRAL:
718                                         is_ok++;
719                                         break;
720
721                                 case LDAP_SIZELIMIT_EXCEEDED:
722                                         /* if a target returned sizelimitExceeded
723                                          * and the entry count is equal to the
724                                          * proxy's limit, the target would have
725                                          * returned more, and the error must be
726                                          * propagated to the client; otherwise,
727                                          * the target enforced a limit lower
728                                          * than what requested by the proxy;
729                                          * ignore it */
730                                         if ( rs->sr_nentries == op->ors_slimit
731                                                 || META_BACK_ONERR_STOP( mi ) )
732                                         {
733                                                 savepriv = op->o_private;
734                                                 op->o_private = (void *)i;
735                                                 send_ldap_result( op, rs );
736                                                 op->o_private = savepriv;
737                                                 goto finish;
738                                         }
739                                         break;
740
741                                 default:
742                                         if ( META_BACK_ONERR_STOP( mi ) ) {
743                                                 savepriv = op->o_private;
744                                                 op->o_private = (void *)i;
745                                                 send_ldap_result( op, rs );
746                                                 op->o_private = savepriv;
747                                                 goto finish;
748                                         }
749                                         break;
750                                 }
751
752                                 last = i;
753                                 rc = 0;
754
755                                 /*
756                                  * When no candidates are left,
757                                  * the outer cycle finishes
758                                  */
759                                 candidates[ i ].sr_msgid = META_MSGID_IGNORE;
760                                 --ncandidates;
761
762                         } else {
763                                 assert( 0 );
764                                 goto really_bad;
765                         }
766                 }
767
768                 /* check for abandon */
769                 if ( op->o_abandon || doabandon ) {
770                         for ( i = 0; i < mi->mi_ntargets; i++ ) {
771                                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
772
773                                 if ( candidates[ i ].sr_msgid != META_MSGID_IGNORE )
774                                 {
775                                         ldap_abandon_ext( msc->msc_ld,
776                                                 candidates[ i ].sr_msgid,
777                                                 NULL, NULL );
778                                         candidates[ i ].sr_msgid = META_MSGID_IGNORE;
779                                 }
780                         }
781
782                         if ( op->o_abandon ) {
783                                 rc = SLAPD_ABANDON;
784                                 goto finish;
785                         }
786                 }
787
788                 /* if no entry was found during this loop,
789                  * set a minimal timeout */
790                 if ( gotit == 0 ) {
791                         /* make the entire wait last
792                          * LDAP_BACK_RESULT_UTIMEOUT at worst */
793                         tv.tv_sec = 0;
794                         tv.tv_usec = LDAP_BACK_RESULT_UTIMEOUT/initial_candidates;
795                         ldap_pvt_thread_yield();
796                 }
797         }
798
799         if ( rc == -1 ) {
800                 /*
801                  * FIXME: need a better strategy to handle errors
802                  */
803                 if ( mc ) {
804                         rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE );
805                 } else {
806                         rc = rs->sr_err;
807                 }
808                 goto finish;
809         }
810
811         /*
812          * Rewrite the matched portion of the search base, if required
813          * 
814          * FIXME: only the last one gets caught!
815          */
816         savepriv = op->o_private;
817         op->o_private = (void *)(long)mi->mi_ntargets;
818         if ( candidate_match > 0 ) {
819                 struct berval   pmatched = BER_BVNULL;
820
821                 /* we use the first one */
822                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
823                         if ( candidates[ i ].sr_tag == META_CANDIDATE
824                                         && candidates[ i ].sr_matched != NULL )
825                         {
826                                 struct berval   bv, pbv;
827                                 int             rc;
828
829                                 /* if we got success, and this target
830                                  * returned noSuchObject, and its suffix
831                                  * is a superior of the searchBase,
832                                  * ignore the matchedDN */
833                                 if ( sres == LDAP_SUCCESS
834                                         && candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT
835                                         && op->o_req_ndn.bv_len > mi->mi_targets[ i ].mt_nsuffix.bv_len )
836                                 {
837                                         free( (char *)candidates[ i ].sr_matched );
838                                         candidates[ i ].sr_matched = NULL;
839                                         continue;
840                                 }
841
842                                 ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
843                                 rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );
844
845                                 if ( rc == LDAP_SUCCESS ) {
846
847                                         /* NOTE: if they all are superiors
848                                          * of the baseDN, the shorter is also 
849                                          * superior of the longer... */
850                                         if ( pbv.bv_len > pmatched.bv_len ) {
851                                                 if ( !BER_BVISNULL( &pmatched ) ) {
852                                                         op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
853                                                 }
854                                                 pmatched = pbv;
855                                                 op->o_private = (void *)i;
856
857                                         } else {
858                                                 op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
859                                         }
860                                 }
861
862                                 if ( candidates[ i ].sr_matched != NULL ) {
863                                         free( (char *)candidates[ i ].sr_matched );
864                                         candidates[ i ].sr_matched = NULL;
865                                 }
866                         }
867                 }
868
869                 if ( !BER_BVISNULL( &pmatched ) ) {
870                         matched = pmatched.bv_val;
871                 }
872
873         } else if ( sres == LDAP_NO_SUCH_OBJECT ) {
874                 matched = op->o_bd->be_suffix[ 0 ].bv_val;
875         }
876
877 #if 0
878         {
879                 char    buf[BUFSIZ];
880                 char    cnd[BUFSIZ];
881                 int     i;
882
883                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
884                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
885                                 cnd[ i ] = '*';
886                         } else {
887                                 cnd[ i ] = ' ';
888                         }
889                 }
890                 cnd[ i ] = '\0';
891
892                 snprintf( buf, sizeof( buf ), "%s meta_back_search: is_scope=%d is_ok=%d cnd=\"%s\"\n",
893                         op->o_log_prefix, initial_candidates, is_ok, cnd );
894
895                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
896         }
897 #endif
898
899         /*
900          * In case we returned at least one entry, we return LDAP_SUCCESS
901          * otherwise, the latter error code we got
902          *
903          * FIXME: we should handle error codes and return the more 
904          * important/reasonable
905          */
906
907         if ( sres == LDAP_SUCCESS && rs->sr_v2ref ) {
908                 sres = LDAP_REFERRAL;
909         }
910         rs->sr_err = sres;
911         rs->sr_matched = matched;
912         rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
913         send_ldap_result( op, rs );
914         op->o_private = savepriv;
915         rs->sr_matched = NULL;
916         rs->sr_ref = NULL;
917
918 finish:;
919         if ( matched && matched != op->o_bd->be_suffix[ 0 ].bv_val ) {
920                 op->o_tmpfree( matched, op->o_tmpmemctx );
921         }
922
923         if ( rs->sr_v2ref ) {
924                 ber_bvarray_free( rs->sr_v2ref );
925         }
926
927         for ( i = 0; i < mi->mi_ntargets; i++ ) {
928                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
929                         continue;
930                 }
931
932                 if ( candidates[ i ].sr_matched ) {
933                         free( (char *)candidates[ i ].sr_matched );
934                         candidates[ i ].sr_matched = NULL;
935                 }
936
937                 if ( candidates[ i ].sr_text ) {
938                         ldap_memfree( (char *)candidates[ i ].sr_text );
939                         candidates[ i ].sr_text = NULL;
940                 }
941
942                 if ( candidates[ i ].sr_ref ) {
943                         ber_bvarray_free( candidates[ i ].sr_ref );
944                         candidates[ i ].sr_ref = NULL;
945                 }
946
947                 if ( candidates[ i ].sr_ctrls ) {
948                         ldap_controls_free( candidates[ i ].sr_ctrls );
949                         candidates[ i ].sr_ctrls = NULL;
950                 }
951         }
952
953         if ( mc ) {
954                 meta_back_release_conn( op, mc );
955         }
956
957         return rs->sr_err;
958 }
959
960 static int
961 meta_send_entry(
962         Operation       *op,
963         SlapReply       *rs,
964         metaconn_t      *mc,
965         int             target,
966         LDAPMessage     *e )
967 {
968         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
969         struct berval           a, mapped;
970         Entry                   ent = { 0 };
971         BerElement              ber = *e->lm_ber;
972         Attribute               *attr, **attrp;
973         struct berval           bdn,
974                                 dn = BER_BVNULL;
975         const char              *text;
976         dncookie                dc;
977         int                     rc;
978
979         if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
980                 return LDAP_DECODING_ERROR;
981         }
982
983         /*
984          * Rewrite the dn of the result, if needed
985          */
986         dc.target = &mi->mi_targets[ target ];
987         dc.conn = op->o_conn;
988         dc.rs = rs;
989         dc.ctx = "searchResult";
990
991         rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &dn );
992         if ( rs->sr_err != LDAP_SUCCESS) {
993                 return rs->sr_err;
994         }
995
996         /*
997          * Note: this may fail if the target host(s) schema differs
998          * from the one known to the meta, and a DN with unknown
999          * attributes is returned.
1000          * 
1001          * FIXME: should we log anything, or delegate to dnNormalize?
1002          */
1003         rc = dnPrettyNormal( NULL, &dn, &ent.e_name, &ent.e_nname,
1004                 op->o_tmpmemctx );
1005         if ( dn.bv_val != bdn.bv_val ) {
1006                 free( dn.bv_val );
1007         }
1008         BER_BVZERO( &dn );
1009
1010         if ( rc != LDAP_SUCCESS ) {
1011                 return LDAP_INVALID_DN_SYNTAX;
1012         }
1013
1014         /*
1015          * cache dn
1016          */
1017         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
1018                 ( void )meta_dncache_update_entry( &mi->mi_cache,
1019                                 &ent.e_nname, target );
1020         }
1021
1022         attrp = &ent.e_attrs;
1023
1024         dc.ctx = "searchAttrDN";
1025         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
1026                 int                             last = 0;
1027                 slap_syntax_validate_func       *validate;
1028                 slap_syntax_transform_func      *pretty;
1029
1030                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_at, 
1031                                 &a, &mapped, BACKLDAP_REMAP );
1032                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
1033                         ( void )ber_scanf( &ber, "x" /* [W] */ );
1034                         continue;
1035                 }
1036                 attr = ( Attribute * )ch_calloc( 1, sizeof( Attribute ) );
1037                 if ( attr == NULL ) {
1038                         continue;
1039                 }
1040                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
1041                                 != LDAP_SUCCESS) {
1042                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text,
1043                                 SLAP_AD_PROXIED ) != LDAP_SUCCESS )
1044                         {
1045                                 char    buf[ SLAP_TEXT_BUFLEN ];
1046
1047                                 snprintf( buf, sizeof( buf ),
1048                                         "%s meta_send_entry(\"%s\"): "
1049                                         "slap_bv2undef_ad(%s): %s\n",
1050                                         op->o_log_prefix, ent.e_name.bv_val,
1051                                         mapped.bv_val, text );
1052
1053                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
1054                                 ch_free( attr );
1055                                 continue;
1056                         }
1057                 }
1058
1059                 /* no subschemaSubentry */
1060                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
1061                         || attr->a_desc == slap_schema.si_ad_entryDN )
1062                 {
1063
1064                         /* 
1065                          * We eat target's subschemaSubentry because
1066                          * a search for this value is likely not
1067                          * to resolve to the appropriate backend;
1068                          * later, the local subschemaSubentry is
1069                          * added.
1070                          *
1071                          * We also eat entryDN because the frontend
1072                          * will reattach it without checking if already
1073                          * present...
1074                          */
1075                         ( void )ber_scanf( &ber, "x" /* [W] */ );
1076
1077                         ch_free(attr);
1078                         continue;
1079                 }
1080
1081                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR 
1082                                 || attr->a_vals == NULL )
1083                 {
1084                         attr->a_vals = (struct berval *)&slap_dummy_bv;
1085
1086                 } else {
1087                         for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last )
1088                                 ;
1089                 }
1090
1091                 validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
1092                 pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
1093
1094                 if ( !validate && !pretty ) {
1095                         attr_free( attr );
1096                         goto next_attr;
1097                 }
1098
1099                 if ( attr->a_desc == slap_schema.si_ad_objectClass
1100                                 || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
1101                 {
1102                         struct berval   *bv;
1103
1104                         for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
1105                                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_oc,
1106                                                 bv, &mapped, BACKLDAP_REMAP );
1107                                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
1108                                         free( bv->bv_val );
1109                                         BER_BVZERO( bv );
1110                                         if ( --last < 0 ) {
1111                                                 break;
1112                                         }
1113                                         *bv = attr->a_vals[ last ];
1114                                         BER_BVZERO( &attr->a_vals[ last ] );
1115                                         bv--;
1116
1117                                 } else if ( mapped.bv_val != bv->bv_val ) {
1118                                         free( bv->bv_val );
1119                                         ber_dupbv( bv, &mapped );
1120                                 }
1121                         }
1122                 /*
1123                  * It is necessary to try to rewrite attributes with
1124                  * dn syntax because they might be used in ACLs as
1125                  * members of groups; since ACLs are applied to the
1126                  * rewritten stuff, no dn-based subecj clause could
1127                  * be used at the ldap backend side (see
1128                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
1129                  * The problem can be overcome by moving the dn-based
1130                  * ACLs to the target directory server, and letting
1131                  * everything pass thru the ldap backend.
1132                  */
1133                 } else {
1134                         int     i;
1135
1136                         if ( attr->a_desc->ad_type->sat_syntax ==
1137                                 slap_schema.si_syn_distinguishedName )
1138                         {
1139                                 ldap_dnattr_result_rewrite( &dc, attr->a_vals );
1140
1141                         } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
1142                                 ldap_back_referral_result_rewrite( &dc, attr->a_vals );
1143
1144                         }
1145
1146                         for ( i = 0; i < last; i++ ) {
1147                                 struct berval   pval;
1148                                 int             rc;
1149
1150                                 if ( pretty ) {
1151                                         rc = pretty( attr->a_desc->ad_type->sat_syntax,
1152                                                 &attr->a_vals[i], &pval, NULL );
1153
1154                                 } else {
1155                                         rc = validate( attr->a_desc->ad_type->sat_syntax,
1156                                                 &attr->a_vals[i] );
1157                                 }
1158
1159                                 if ( rc ) {
1160                                         LBER_FREE( attr->a_vals[i].bv_val );
1161                                         if ( --last == i ) {
1162                                                 BER_BVZERO( &attr->a_vals[ i ] );
1163                                                 break;
1164                                         }
1165                                         attr->a_vals[i] = attr->a_vals[last];
1166                                         BER_BVZERO( &attr->a_vals[last] );
1167                                         i--;
1168                                         continue;
1169                                 }
1170
1171                                 if ( pretty ) {
1172                                         LBER_FREE( attr->a_vals[i].bv_val );
1173                                         attr->a_vals[i] = pval;
1174                                 }
1175                         }
1176
1177                         if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
1178                                 attr_free( attr );
1179                                 goto next_attr;
1180                         }
1181                 }
1182
1183                 if ( last && attr->a_desc->ad_type->sat_equality &&
1184                         attr->a_desc->ad_type->sat_equality->smr_normalize )
1185                 {
1186                         int i;
1187
1188                         attr->a_nvals = ch_malloc( ( last + 1 ) * sizeof( struct berval ) );
1189                         for ( i = 0; i<last; i++ ) {
1190                                 attr->a_desc->ad_type->sat_equality->smr_normalize(
1191                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
1192                                         attr->a_desc->ad_type->sat_syntax,
1193                                         attr->a_desc->ad_type->sat_equality,
1194                                         &attr->a_vals[i], &attr->a_nvals[i],
1195                                         NULL );
1196                         }
1197                         BER_BVZERO( &attr->a_nvals[i] );
1198
1199                 } else {
1200                         attr->a_nvals = attr->a_vals;
1201                 }
1202
1203                 *attrp = attr;
1204                 attrp = &attr->a_next;
1205 next_attr:;
1206         }
1207         rs->sr_entry = &ent;
1208         rs->sr_attrs = op->ors_attrs;
1209         rs->sr_flags = 0;
1210         rs->sr_err = LDAP_SUCCESS;
1211         rc = send_search_entry( op, rs );
1212         switch ( rc ) {
1213         case LDAP_UNAVAILABLE:
1214                 rc = LDAP_OTHER;
1215                 break;
1216         }
1217         rs->sr_entry = NULL;
1218         rs->sr_attrs = NULL;
1219         
1220         if ( !BER_BVISNULL( &ent.e_name ) ) {
1221                 free( ent.e_name.bv_val );
1222                 BER_BVZERO( &ent.e_name );
1223         }
1224         if ( !BER_BVISNULL( &ent.e_nname ) ) {
1225                 free( ent.e_nname.bv_val );
1226                 BER_BVZERO( &ent.e_nname );
1227         }
1228         entry_clean( &ent );
1229
1230         return rc;
1231 }
1232