]> git.sur5r.net Git - openldap/blob - libraries/libldap/result.c
ITS#5580
[openldap] / libraries / libldap / result.c
1 /* result.c - wait for an ldap result */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2008 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
17  * All rights reserved.
18  */
19 /* This notice applies to changes, created by or for Novell, Inc.,
20  * to preexisting works for which notices appear elsewhere in this file.
21  *
22  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
23  *
24  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
25  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
26  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
27  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
28  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
29  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
30  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
31  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
32  *---
33  * Modification to OpenLDAP source by Novell, Inc.
34  * April 2000 sfs Add code to process V3 referrals and search results
35  *---
36  * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 
37  * can be found in the file "build/LICENSE-2.0.1" in this distribution
38  * of OpenLDAP Software.
39  */
40
41 /*
42  * LDAPv3 (RFC 4511)
43  *      LDAPResult ::= SEQUENCE {
44  *              resultCode                      ENUMERATED { ... },
45  *              matchedDN                       LDAPDN,
46  *              diagnosticMessage               LDAPString,
47  *              referral                        [3] Referral OPTIONAL
48  *      }
49  *      Referral ::= SEQUENCE OF LDAPURL        (one or more)
50  *      LDAPURL ::= LDAPString                  (limited to URL chars)
51  */
52
53 #include "portable.h"
54
55 #include <stdio.h>
56
57 #include <ac/stdlib.h>
58
59 #include <ac/errno.h>
60 #include <ac/socket.h>
61 #include <ac/string.h>
62 #include <ac/time.h>
63 #include <ac/unistd.h>
64
65 #include "ldap-int.h"
66 #include "ldap_log.h"
67 #include "lutil.h"
68
69 static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int *idx ));
70 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int idx ));
71 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
72         LDAPMessage **result ));
73 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
74         int all, LDAPConn **lc, LDAPMessage **result ));
75 static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
76 static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
77 static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
78
79 #define LDAP_MSG_X_KEEP_LOOKING         (-2)
80
81
82 /*
83  * ldap_result - wait for an ldap result response to a message from the
84  * ldap server.  If msgid is LDAP_RES_ANY (-1), any message will be
85  * accepted.  If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
86  * message is accepted.  Otherwise ldap_result will wait for a response
87  * with msgid.  If all is LDAP_MSG_ONE (0) the first message with id
88  * msgid will be accepted, otherwise, ldap_result will wait for all
89  * responses with id msgid and then return a pointer to the entire list
90  * of messages.  In general, this is only useful for search responses,
91  * which can be of three message types (zero or more entries, zero or
92  * search references, followed by an ldap result).  An extension to
93  * LDAPv3 allows partial extended responses to be returned in response
94  * to any request.  The type of the first message received is returned.
95  * When waiting, any messages that have been abandoned/discarded are 
96  * discarded.
97  *
98  * Example:
99  *      ldap_result( s, msgid, all, timeout, result )
100  */
101 int
102 ldap_result(
103         LDAP *ld,
104         int msgid,
105         int all,
106         struct timeval *timeout,
107         LDAPMessage **result )
108 {
109         LDAPMessage     *lm = NULL;
110         int             rc;
111
112         assert( ld != NULL );
113         assert( result != NULL );
114
115         Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
116
117 #ifdef LDAP_R_COMPILE
118         ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
119 #endif
120
121 #if 0
122         /* this is already done inside wait4msg(), right?... */
123         lm = chkResponseList( ld, msgid, all );
124 #endif
125
126         if ( lm == NULL ) {
127                 rc = wait4msg( ld, msgid, all, timeout, result );
128
129         } else {
130                 *result = lm;
131                 ld->ld_errno = LDAP_SUCCESS;
132                 rc = lm->lm_msgtype;
133         }
134
135 #ifdef LDAP_R_COMPILE
136         ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
137 #endif
138
139         return rc;
140 }
141
142 static LDAPMessage *
143 chkResponseList(
144         LDAP *ld,
145         int msgid,
146         int all)
147 {
148         LDAPMessage     *lm, **lastlm, *nextlm;
149         int             cnt = 0;
150
151         /*
152          * Look through the list of responses we have received on
153          * this association and see if the response we're interested in
154          * is there.  If it is, return it.  If not, call wait4msg() to
155          * wait until it arrives or timeout occurs.
156          */
157
158 #ifdef LDAP_R_COMPILE
159         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
160 #endif
161
162         Debug( LDAP_DEBUG_TRACE,
163                 "ldap_chkResponseList ld %p msgid %d all %d\n",
164                 (void *)ld, msgid, all );
165
166         lastlm = &ld->ld_responses;
167         for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
168                 int     idx;
169
170                 nextlm = lm->lm_next;
171                 ++cnt;
172
173                 if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
174                         Debug( LDAP_DEBUG_ANY,
175                                 "response list msg abandoned, "
176                                 "msgid %d message type %s\n",
177                                 lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
178
179                         switch ( lm->lm_msgtype ) {
180                         case LDAP_RES_SEARCH_ENTRY:
181                         case LDAP_RES_SEARCH_REFERENCE:
182                         case LDAP_RES_INTERMEDIATE:
183                                 break;
184
185                         default:
186                                 /* there's no need to keep the id
187                                  * in the abandoned list any longer */
188                                 ldap_mark_abandoned( ld, lm->lm_msgid, idx );
189                                 break;
190                         }
191
192                         /* Remove this entry from list */
193                         *lastlm = nextlm;
194
195                         ldap_msgfree( lm );
196
197                         continue;
198                 }
199
200                 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
201                         LDAPMessage     *tmp;
202
203                         if ( all == LDAP_MSG_ONE ||
204                                 all == LDAP_MSG_RECEIVED ||
205                                 msgid == LDAP_RES_UNSOLICITED )
206                         {
207                                 break;
208                         }
209
210                         tmp = lm->lm_chain_tail;
211                         if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
212                                 tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
213                                 tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
214                         {
215                                 tmp = NULL;
216                         }
217
218                         if ( tmp == NULL ) {
219                                 lm = NULL;
220                         }
221
222                         break;
223                 }
224                 lastlm = &lm->lm_next;
225         }
226
227         if ( lm != NULL ) {
228                 /* Found an entry, remove it from the list */
229                 if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
230                         *lastlm = lm->lm_chain;
231                         lm->lm_chain->lm_next = lm->lm_next;
232                         lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
233                         lm->lm_chain = NULL;
234                         lm->lm_chain_tail = NULL;
235                 } else {
236                         *lastlm = lm->lm_next;
237                 }
238                 lm->lm_next = NULL;
239         }
240
241 #ifdef LDAP_DEBUG
242         if ( lm == NULL) {
243                 Debug( LDAP_DEBUG_TRACE,
244                         "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
245         } else {
246                 Debug( LDAP_DEBUG_TRACE,
247                         "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
248                         (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
249         }
250 #endif
251
252         return lm;
253 }
254
255 static int
256 wait4msg(
257         LDAP *ld,
258         ber_int_t msgid,
259         int all,
260         struct timeval *timeout,
261         LDAPMessage **result )
262 {
263         int             rc;
264         struct timeval  tv = { 0 },
265                         tv0 = { 0 },
266                         start_time_tv = { 0 },
267                         *tvp = NULL;
268         LDAPConn        *lc;
269
270         assert( ld != NULL );
271         assert( result != NULL );
272
273 #ifdef LDAP_R_COMPILE
274         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
275 #endif
276
277         if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
278                 tv = ld->ld_options.ldo_tm_api;
279                 timeout = &tv;
280         }
281
282 #ifdef LDAP_DEBUG
283         if ( timeout == NULL ) {
284                 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
285                         (void *)ld, msgid, 0 );
286         } else {
287                 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
288                         (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
289         }
290 #endif /* LDAP_DEBUG */
291
292         if ( timeout != NULL ) {
293                 tv0 = *timeout;
294                 tv = *timeout;
295                 tvp = &tv;
296 #ifdef HAVE_GETTIMEOFDAY
297                 gettimeofday( &start_time_tv, NULL );
298 #else /* ! HAVE_GETTIMEOFDAY */
299                 time( &start_time_tv.tv_sec );
300                 start_time_tv.tv_usec = 0;
301 #endif /* ! HAVE_GETTIMEOFDAY */
302         }
303                     
304         rc = LDAP_MSG_X_KEEP_LOOKING;
305         while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
306 #ifdef LDAP_DEBUG
307                 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
308                         Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
309                                 (void *)ld, msgid, all );
310 #ifdef LDAP_R_COMPILE
311                         ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
312 #endif
313                         ldap_dump_connection( ld, ld->ld_conns, 1 );
314 #ifdef LDAP_R_COMPILE
315                         ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
316                         ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
317 #endif
318                         ldap_dump_requests_and_responses( ld );
319 #ifdef LDAP_R_COMPILE
320                         ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
321 #endif
322                 }
323 #endif /* LDAP_DEBUG */
324
325                 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
326                         rc = (*result)->lm_msgtype;
327
328                 } else {
329                         int lc_ready = 0;
330
331 #ifdef LDAP_R_COMPILE
332                         ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
333 #endif
334                         for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
335                                 if ( ber_sockbuf_ctrl( lc->lconn_sb,
336                                         LBER_SB_OPT_DATA_READY, NULL ) )
337                                 {
338 #ifdef LDAP_R_COMPILE
339                                         ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
340 #endif
341                                         rc = try_read1msg( ld, msgid, all, &lc, result );
342 #ifdef LDAP_R_COMPILE
343                                         ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
344 #endif
345                                         lc_ready = 1;
346                                         break;
347                                 }
348                         }
349 #ifdef LDAP_R_COMPILE
350                         ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
351 #endif
352
353                         if ( !lc_ready ) {
354                                 int err;
355                                 rc = ldap_int_select( ld, tvp );
356                                 if ( rc == -1 ) {
357                                         err = sock_errno();
358 #ifdef LDAP_DEBUG
359                                         Debug( LDAP_DEBUG_TRACE,
360                                                 "ldap_int_select returned -1: errno %d\n",
361                                                 err, 0, 0 );
362 #endif
363                                 }
364
365                                 if ( rc == 0 || ( rc == -1 && (
366                                         !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
367                                                 || err != EINTR ) ) )
368                                 {
369                                         ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
370                                                 LDAP_TIMEOUT);
371                                         return( rc );
372                                 }
373
374                                 if ( rc == -1 ) {
375                                         rc = LDAP_MSG_X_KEEP_LOOKING;   /* select interrupted: loop */
376
377                                 } else {
378                                         rc = LDAP_MSG_X_KEEP_LOOKING;
379 #ifdef LDAP_R_COMPILE
380                                         ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
381 #endif
382                                         if ( ld->ld_requests &&
383                                                 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
384                                                 ldap_is_write_ready( ld,
385                                                         ld->ld_requests->lr_conn->lconn_sb ) )
386                                         {
387                                                 ldap_int_flush_request( ld, ld->ld_requests );
388                                         }
389 #ifdef LDAP_R_COMPILE
390                                         ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
391                                         ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
392 #endif
393                                         for ( lc = ld->ld_conns;
394                                                 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; )
395                                         {
396                                                 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
397                                                         ldap_is_read_ready( ld, lc->lconn_sb ) )
398                                                 {
399 #ifdef LDAP_R_COMPILE
400                                                         ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
401 #endif
402                                                         rc = try_read1msg( ld, msgid, all, &lc, result );
403 #ifdef LDAP_R_COMPILE
404                                                         ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
405 #endif
406                                                         if ( lc == NULL ) {
407                                                                 /* if lc gets free()'d,
408                                                                  * there's no guarantee
409                                                                  * lc->lconn_next is still
410                                                                  * sane; better restart
411                                                                  * (ITS#4405) */
412                                                                 lc = ld->ld_conns;
413
414                                                                 /* don't get to next conn! */
415                                                                 break;
416                                                         }
417                                                 }
418
419                                                 /* next conn */
420                                                 lc = lc->lconn_next;
421                                         }
422 #ifdef LDAP_R_COMPILE
423                                         ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
424 #endif
425                                 }
426                         }
427                 }
428
429                 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
430                         struct timeval  curr_time_tv = { 0 },
431                                         delta_time_tv = { 0 };
432
433 #ifdef HAVE_GETTIMEOFDAY
434                         gettimeofday( &curr_time_tv, NULL );
435 #else /* ! HAVE_GETTIMEOFDAY */
436                         time( &curr_time_tv.tv_sec );
437                         curr_time_tv.tv_usec = 0;
438 #endif /* ! HAVE_GETTIMEOFDAY */
439
440                         /* delta_time = tmp_time - start_time */
441                         delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
442                         delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
443                         if ( delta_time_tv.tv_usec < 0 ) {
444                                 delta_time_tv.tv_sec--;
445                                 delta_time_tv.tv_usec += 1000000;
446                         }
447
448                         /* tv0 < delta_time ? */
449                         if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
450                              ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
451                         {
452                                 rc = 0; /* timed out */
453                                 ld->ld_errno = LDAP_TIMEOUT;
454                                 break;
455                         }
456
457                         /* tv0 -= delta_time */
458                         tv0.tv_sec -= delta_time_tv.tv_sec;
459                         tv0.tv_usec -= delta_time_tv.tv_usec;
460                         if ( tv0.tv_usec < 0 ) {
461                                 tv0.tv_sec--;
462                                 tv0.tv_usec += 1000000;
463                         }
464
465                         tv.tv_sec = tv0.tv_sec;
466                         tv.tv_usec = tv0.tv_usec;
467
468                         Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
469                                 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
470
471                         start_time_tv.tv_sec = curr_time_tv.tv_sec;
472                         start_time_tv.tv_usec = curr_time_tv.tv_usec;
473                 }
474         }
475
476         return( rc );
477 }
478
479
480 static ber_tag_t
481 try_read1msg(
482         LDAP *ld,
483         ber_int_t msgid,
484         int all,
485         LDAPConn **lcp,
486         LDAPMessage **result )
487 {
488         BerElement      *ber;
489         LDAPMessage     *newmsg, *l, *prev;
490         ber_int_t       id;
491         int             idx;
492         ber_tag_t       tag;
493         ber_len_t       len;
494         int             foundit = 0;
495         LDAPRequest     *lr, *tmplr, dummy_lr = { 0 };
496         LDAPConn        *lc;
497         BerElement      tmpber;
498         int             rc, refer_cnt, hadref, simple_request, err;
499         ber_int_t       lderr;
500
501 #ifdef LDAP_CONNECTIONLESS
502         LDAPMessage     *tmp = NULL, *chain_head = NULL;
503         int             moremsgs = 0, isv2 = 0;
504 #endif
505
506         assert( ld != NULL );
507         assert( lcp != NULL );
508         assert( *lcp != NULL );
509         
510 #ifdef LDAP_R_COMPILE
511         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
512 #endif
513
514         Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
515                 (void *)ld, msgid, all );
516
517         lc = *lcp;
518
519 retry:
520         if ( lc->lconn_ber == NULL ) {
521                 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
522
523                 if ( lc->lconn_ber == NULL ) {
524                         return -1;
525                 }
526         }
527
528         ber = lc->lconn_ber;
529         assert( LBER_VALID (ber) );
530
531         /* get the next message */
532         sock_errset(0);
533 #ifdef LDAP_CONNECTIONLESS
534         if ( LDAP_IS_UDP(ld) ) {
535                 struct sockaddr from;
536                 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
537                 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
538         }
539 nextresp3:
540 #endif
541         tag = ber_get_next( lc->lconn_sb, &len, ber );
542         switch ( tag ) {
543         case LDAP_TAG_MESSAGE:
544                 /*
545                  * We read a complete message.
546                  * The connection should no longer need this ber.
547                  */
548                 lc->lconn_ber = NULL;
549                 break;
550
551         case LBER_DEFAULT:
552                 err = sock_errno();
553 #ifdef LDAP_DEBUG                  
554                 Debug( LDAP_DEBUG_CONNS,
555                         "ber_get_next failed.\n", 0, 0, 0 );
556 #endif             
557 #ifdef EWOULDBLOCK                      
558                 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
559 #endif
560 #ifdef EAGAIN
561                 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
562 #endif
563                 ld->ld_errno = LDAP_SERVER_DOWN;
564 #ifdef LDAP_R_COMPILE
565                 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
566 #endif
567                 ldap_free_connection( ld, lc, 1, 0 );
568 #ifdef LDAP_R_COMPILE
569                 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
570 #endif
571                 lc = *lcp = NULL;
572                 return -1;
573
574         default:
575                 ld->ld_errno = LDAP_LOCAL_ERROR;
576                 return -1;
577         }
578
579         /* message id */
580         if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
581                 ber_free( ber, 1 );
582                 ld->ld_errno = LDAP_DECODING_ERROR;
583                 return( -1 );
584         }
585
586         /* id == 0 iff unsolicited notification message (RFC 4511) */
587
588         /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
589         if ( id < 0 ) {
590                 goto retry_ber;
591         }
592         
593         /* if it's been abandoned, toss it */
594         if ( id > 0 ) {
595                 if ( ldap_abandoned( ld, id, &idx ) ) {
596                         /* the message type */
597                         tag = ber_peek_tag( ber, &len );
598                         switch ( tag ) {
599                         case LDAP_RES_SEARCH_ENTRY:
600                         case LDAP_RES_SEARCH_REFERENCE:
601                         case LDAP_RES_INTERMEDIATE:
602                         case LBER_ERROR:
603                                 break;
604
605                         default:
606                                 /* there's no need to keep the id
607                                  * in the abandoned list any longer */
608                                 ldap_mark_abandoned( ld, id, idx );
609                                 break;
610                         }
611
612                         Debug( LDAP_DEBUG_ANY,
613                                 "abandoned/discarded ld %p msgid %d message type %s\n",
614                                 (void *)ld, id, ldap_int_msgtype2str( tag ) );
615
616 retry_ber:
617                         ber_free( ber, 1 );
618                         if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
619                                 goto retry;
620                         }
621                         return( LDAP_MSG_X_KEEP_LOOKING );      /* continue looking */
622                 }
623
624                 lr = ldap_find_request_by_msgid( ld, id );
625                 if ( lr == NULL ) {
626                         const char      *msg = "unknown";
627
628                         /* the message type */
629                         tag = ber_peek_tag( ber, &len );
630                         switch ( tag ) {
631                         case LBER_ERROR:
632                                 break;
633
634                         default:
635                                 msg = ldap_int_msgtype2str( tag );
636                                 break;
637                         }
638
639                         Debug( LDAP_DEBUG_ANY,
640                                 "no request for response on ld %p msgid %d message type %s (tossing)\n",
641                                 (void *)ld, id, msg );
642
643                         goto retry_ber;
644                 }
645
646 #ifdef LDAP_CONNECTIONLESS
647                 if ( LDAP_IS_UDP(ld) && isv2 ) {
648                         ber_scanf(ber, "x{");
649                 }
650 nextresp2:
651 #endif
652         }
653
654         /* the message type */
655         tag = ber_peek_tag( ber, &len );
656         if ( tag == LBER_ERROR ) {
657                 ld->ld_errno = LDAP_DECODING_ERROR;
658                 ber_free( ber, 1 );
659                 return( -1 );
660         }
661
662         Debug( LDAP_DEBUG_TRACE,
663                 "read1msg: ld %p msgid %d message type %s\n",
664                 (void *)ld, id, ldap_int_msgtype2str( tag ) );
665
666         if ( id == 0 ) {
667                 /* unsolicited notification message (RFC 4511) */
668                 if ( tag != LDAP_RES_EXTENDED ) {
669                         /* toss it */
670                         goto retry_ber;
671
672                         /* strictly speaking, it's an error; from RFC 4511:
673
674 4.4.  Unsolicited Notification
675
676    An unsolicited notification is an LDAPMessage sent from the server to
677    the client that is not in response to any LDAPMessage received by the
678    server.  It is used to signal an extraordinary condition in the
679    server or in the LDAP session between the client and the server.  The
680    notification is of an advisory nature, and the server will not expect
681    any response to be returned from the client.
682
683    The unsolicited notification is structured as an LDAPMessage in which
684    the messageID is zero and protocolOp is set to the extendedResp
685    choice using the ExtendedResponse type (See Section 4.12).  The
686    responseName field of the ExtendedResponse always contains an LDAPOID
687    that is unique for this notification.
688
689                          * however, since unsolicited responses
690                          * are of advisory nature, better
691                          * toss it, right now
692                          */
693
694 #if 0
695                         ld->ld_errno = LDAP_DECODING_ERROR;
696                         ber_free( ber, 1 );
697                         return( -1 );
698 #endif
699                 }
700
701                 lr = &dummy_lr;
702         }
703
704         id = lr->lr_origid;
705         refer_cnt = 0;
706         hadref = simple_request = 0;
707         rc = LDAP_MSG_X_KEEP_LOOKING;   /* default is to keep looking (no response found) */
708         lr->lr_res_msgtype = tag;
709
710         /*
711          * Check for V3 search reference
712          */
713         if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
714                 if ( ld->ld_version > LDAP_VERSION2 ) {
715                         /* This is a V3 search reference */
716                         if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
717                                         lr->lr_parent != NULL )
718                         {
719                                 char **refs = NULL;
720                                 tmpber = *ber;
721
722                                 /* Get the referral list */
723                                 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
724                                         rc = LDAP_DECODING_ERROR;
725
726                                 } else {
727                                         /* Note: refs array is freed by ldap_chase_v3referrals */
728                                         refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
729                                                 1, &lr->lr_res_error, &hadref );
730                                         if ( refer_cnt > 0 ) {
731                                                 /* successfully chased reference */
732                                                 /* If haven't got end search, set chasing referrals */
733                                                 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
734                                                         lr->lr_status = LDAP_REQST_CHASINGREFS;
735                                                         Debug( LDAP_DEBUG_TRACE,
736                                                                 "read1msg:  search ref chased, "
737                                                                 "mark request chasing refs, "
738                                                                 "id = %d\n",
739                                                                 lr->lr_msgid, 0, 0 );
740                                                 }
741                                         }
742                                 }
743                         }
744                 }
745
746         } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
747                 /* All results that just return a status, i.e. don't return data
748                  * go through the following code.  This code also chases V2 referrals
749                  * and checks if all referrals have been chased.
750                  */
751                 char            *lr_res_error = NULL;
752
753                 tmpber = *ber;  /* struct copy */
754                 if ( ber_scanf( &tmpber, "{eAA", &lderr,
755                                 &lr->lr_res_matched, &lr_res_error )
756                                 != LBER_ERROR )
757                 {
758                         if ( lr_res_error != NULL ) {
759                                 if ( lr->lr_res_error != NULL ) {
760                                         (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
761                                         LDAP_FREE( (char *)lr_res_error );
762
763                                 } else {
764                                         lr->lr_res_error = lr_res_error;
765                                 }
766                                 lr_res_error = NULL;
767                         }
768
769                         /* Do we need to check for referrals? */
770                         if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
771                                         lr->lr_parent != NULL )
772                         {
773                                 char            **refs = NULL;
774                                 ber_len_t       len;
775
776                                 /* Check if V3 referral */
777                                 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
778                                         if ( ld->ld_version > LDAP_VERSION2 ) {
779                                                 /* Get the referral list */
780                                                 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
781                                                         rc = LDAP_DECODING_ERROR;
782                                                         lr->lr_status = LDAP_REQST_COMPLETED;
783                                                         Debug( LDAP_DEBUG_TRACE,
784                                                                 "read1msg: referral decode error, "
785                                                                 "mark request completed, ld %p msgid %d\n",
786                                                                 (void *)ld, lr->lr_msgid, 0 );
787
788                                                 } else {
789                                                         /* Chase the referral 
790                                                          * refs array is freed by ldap_chase_v3referrals
791                                                          */
792                                                         refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
793                                                                 0, &lr->lr_res_error, &hadref );
794                                                         lr->lr_status = LDAP_REQST_COMPLETED;
795                                                         Debug( LDAP_DEBUG_TRACE,
796                                                                 "read1msg: referral %s chased, "
797                                                                 "mark request completed, ld %p msgid %d\n",
798                                                                 refer_cnt > 0 ? "" : "not",
799                                                                 (void *)ld, lr->lr_msgid);
800                                                         if ( refer_cnt < 0 ) {
801                                                                 refer_cnt = 0;
802                                                         }
803                                                 }
804                                         }
805                                 } else {
806                                         switch ( lderr ) {
807                                         case LDAP_SUCCESS:
808                                         case LDAP_COMPARE_TRUE:
809                                         case LDAP_COMPARE_FALSE:
810                                                 break;
811
812                                         default:
813                                                 if ( lr->lr_res_error == NULL ) {
814                                                         break;
815                                                 }
816
817                                                 /* pedantic, should never happen */
818                                                 if ( lr->lr_res_error[ 0 ] == '\0' ) {
819                                                         LDAP_FREE( lr->lr_res_error );
820                                                         lr->lr_res_error = NULL;
821                                                         break;  
822                                                 }
823
824                                                 /* V2 referrals are in error string */
825                                                 refer_cnt = ldap_chase_referrals( ld, lr,
826                                                         &lr->lr_res_error, -1, &hadref );
827                                                 lr->lr_status = LDAP_REQST_COMPLETED;
828                                                 Debug( LDAP_DEBUG_TRACE,
829                                                         "read1msg:  V2 referral chased, "
830                                                         "mark request completed, id = %d\n",
831                                                         lr->lr_msgid, 0, 0 );
832                                                 break;
833                                         }
834                                 }
835                         }
836
837                         /* save errno, message, and matched string */
838                         if ( !hadref || lr->lr_res_error == NULL ) {
839                                 lr->lr_res_errno =
840                                         lderr == LDAP_PARTIAL_RESULTS
841                                         ? LDAP_SUCCESS : lderr;
842
843                         } else if ( ld->ld_errno != LDAP_SUCCESS ) {
844                                 lr->lr_res_errno = ld->ld_errno;
845
846                         } else {
847                                 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
848                         }
849                 }
850
851                 /* in any case, don't leave any lr_res_error 'round */
852                 if ( lr_res_error ) {
853                         LDAP_FREE( lr_res_error );
854                 }
855
856                 Debug( LDAP_DEBUG_TRACE,
857                         "read1msg: ld %p %d new referrals\n",
858                         (void *)ld, refer_cnt, 0 );
859
860                 if ( refer_cnt != 0 ) { /* chasing referrals */
861                         ber_free( ber, 1 );
862                         ber = NULL;
863                         if ( refer_cnt < 0 ) {
864                                 ldap_return_request( ld, lr, 0 );
865                                 return( -1 );   /* fatal error */
866                         }
867                         lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
868
869                 } else {
870                         if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
871                                 /* request without any referrals */
872                                 simple_request = ( hadref ? 0 : 1 );
873
874                         } else {
875                                 /* request with referrals or child request */
876                                 ber_free( ber, 1 );
877                                 ber = NULL;
878                         }
879
880                         lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
881                         Debug( LDAP_DEBUG_TRACE,
882                                 "read1msg:  mark request completed, ld %p msgid %d\n",
883                                 (void *)ld, lr->lr_msgid, 0);
884                         while ( lr->lr_parent != NULL ) {
885                                 merge_error_info( ld, lr->lr_parent, lr );
886
887                                 lr = lr->lr_parent;
888                                 if ( --lr->lr_outrefcnt > 0 ) {
889                                         break;  /* not completely done yet */
890                                 }
891                         }
892
893                         /* Check if all requests are finished, lr is now parent */
894                         tmplr = lr;
895                         if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
896                                 for ( tmplr = lr->lr_child;
897                                         tmplr != NULL;
898                                         tmplr = tmplr->lr_refnext )
899                                 {
900                                         if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
901                                 }
902                         }
903
904                         /* This is the parent request if the request has referrals */
905                         if ( lr->lr_outrefcnt <= 0 &&
906                                 lr->lr_parent == NULL &&
907                                 tmplr == NULL )
908                         {
909                                 id = lr->lr_msgid;
910                                 tag = lr->lr_res_msgtype;
911                                 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
912                                         (void *)ld, id, 0 );
913                                 Debug( LDAP_DEBUG_TRACE,
914                                         "res_errno: %d, res_error: <%s>, "
915                                         "res_matched: <%s>\n",
916                                         lr->lr_res_errno,
917                                         lr->lr_res_error ? lr->lr_res_error : "",
918                                         lr->lr_res_matched ? lr->lr_res_matched : "" );
919                                 if ( !simple_request ) {
920                                         ber_free( ber, 1 );
921                                         ber = NULL;
922                                         if ( build_result_ber( ld, &ber, lr )
923                                             == LBER_ERROR )
924                                         {
925                                                 rc = -1; /* fatal error */
926                                         }
927                                 }
928
929                                 if ( lr != &dummy_lr ) {
930                                         ldap_return_request( ld, lr, 1 );
931                                 }
932                                 lr = NULL;
933                         }
934
935                         /*
936                          * RF 4511 unsolicited (id == 0) responses
937                          * shouldn't necessarily end the connection
938                          */
939                         if ( lc != NULL && id != 0 ) {
940 #ifdef LDAP_R_COMPILE
941                                 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
942 #endif
943                                 ldap_free_connection( ld, lc, 0, 1 );
944 #ifdef LDAP_R_COMPILE
945                                 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
946 #endif
947                                 lc = *lcp = NULL;
948                         }
949                 }
950         }
951
952         if ( lr != NULL ) {
953                 if ( lr != &dummy_lr ) {
954                         ldap_return_request( ld, lr, 0 );
955                 }
956                 lr = NULL;
957         }
958
959         if ( ber == NULL ) {
960                 return( rc );
961         }
962
963         /* try to handle unsolicited responses as appropriate */
964         if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
965                 int     is_nod = 0;
966
967                 tag = ber_peek_tag( &tmpber, &len );
968
969                 /* we have a res oid */
970                 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
971                         static struct berval    bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
972                         struct berval           resoid = BER_BVNULL;
973
974                         if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
975                                 ld->ld_errno = LDAP_DECODING_ERROR;
976                                 ber_free( ber, 1 );
977                                 return -1;
978                         }
979
980                         assert( !BER_BVISEMPTY( &resoid ) );
981
982                         is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
983
984                         tag = ber_peek_tag( &tmpber, &len );
985                 }
986
987 #if 0 /* don't need right now */
988                 /* we have res data */
989                 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
990                         struct berval resdata;
991
992                         if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
993                                 ld->ld_errno = LDAP_DECODING_ERROR;
994                                 ber_free( ber, 0 );
995                                 return ld->ld_errno;
996                         }
997
998                         /* use it... */
999                 }
1000 #endif
1001
1002                 /* handle RFC 4511 "Notice of Disconnection" locally */
1003
1004                 if ( is_nod ) {
1005                         if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
1006                                 ld->ld_errno = LDAP_DECODING_ERROR;
1007                                 ber_free( ber, 1 );
1008                                 return -1;
1009                         }
1010
1011                         /* get rid of the connection... */
1012                         if ( lc != NULL ) {
1013 #ifdef LDAP_R_COMPILE
1014                                 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
1015 #endif
1016                                 ldap_free_connection( ld, lc, 0, 1 );
1017 #ifdef LDAP_R_COMPILE
1018                                 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
1019 #endif
1020                                 lc = *lcp = NULL;
1021                         }
1022
1023                         /* need to return -1, because otherwise
1024                          * a valid result is expected */
1025                         return -1;
1026                 }
1027         }
1028
1029         /* make a new ldap message */
1030         newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
1031         if ( newmsg == NULL ) {
1032                 ld->ld_errno = LDAP_NO_MEMORY;
1033                 return( -1 );
1034         }
1035         newmsg->lm_msgid = (int)id;
1036         newmsg->lm_msgtype = tag;
1037         newmsg->lm_ber = ber;
1038         newmsg->lm_chain_tail = newmsg;
1039
1040 #ifdef LDAP_CONNECTIONLESS
1041         /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1042          * the responses are all a sequence wrapped in one message. In
1043          * LDAPv3 each response is in its own message. The datagram must
1044          * end with a SearchResult. We can't just parse each response in
1045          * separate calls to try_read1msg because the header info is only
1046          * present at the beginning of the datagram, not at the beginning
1047          * of each response. So parse all the responses at once and queue
1048          * them up, then pull off the first response to return to the
1049          * caller when all parsing is complete.
1050          */
1051         if ( LDAP_IS_UDP(ld) ) {
1052                 /* If not a result, look for more */
1053                 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1054                         int ok = 0;
1055                         moremsgs = 1;
1056                         if (isv2) {
1057                                 /* LDAPv2: dup the current ber, skip past the current
1058                                  * response, and see if there are any more after it.
1059                                  */
1060                                 ber = ber_dup( ber );
1061                                 ber_scanf( ber, "x" );
1062                                 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1063                                         /* There's more - dup the ber buffer so they can all be
1064                                          * individually freed by ldap_msgfree.
1065                                          */
1066                                         struct berval bv;
1067                                         ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1068                                         bv.bv_val = LDAP_MALLOC( len );
1069                                         if ( bv.bv_val ) {
1070                                                 ok = 1;
1071                                                 ber_read( ber, bv.bv_val, len );
1072                                                 bv.bv_len = len;
1073                                                 ber_init2( ber, &bv, ld->ld_lberoptions );
1074                                         }
1075                                 }
1076                         } else {
1077                                 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1078                                  * datagram, if the sockbuf is readable we still have data
1079                                  * to parse.
1080                                  */
1081                                 ber = ldap_alloc_ber_with_options( ld );
1082                                 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1083                         }
1084                         /* set up response chain */
1085                         if ( tmp == NULL ) {
1086                                 newmsg->lm_next = ld->ld_responses;
1087                                 ld->ld_responses = newmsg;
1088                                 chain_head = newmsg;
1089                         } else {
1090                                 tmp->lm_chain = newmsg;
1091                         }
1092                         chain_head->lm_chain_tail = newmsg;
1093                         tmp = newmsg;
1094                         /* "ok" means there's more to parse */
1095                         if ( ok ) {
1096                                 if ( isv2 ) {
1097                                         goto nextresp2;
1098
1099                                 } else {
1100                                         goto nextresp3;
1101                                 }
1102                         } else {
1103                                 /* got to end of datagram without a SearchResult. Free
1104                                  * our dup'd ber, but leave any buffer alone. For v2 case,
1105                                  * the previous response is still using this buffer. For v3,
1106                                  * the new ber has no buffer to free yet.
1107                                  */
1108                                 ber_free( ber, 0 );
1109                                 return -1;
1110                         }
1111                 } else if ( moremsgs ) {
1112                 /* got search result, and we had multiple responses in 1 datagram.
1113                  * stick the result onto the end of the chain, and then pull the
1114                  * first response off the head of the chain.
1115                  */
1116                         tmp->lm_chain = newmsg;
1117                         chain_head->lm_chain_tail = newmsg;
1118                         *result = chkResponseList( ld, msgid, all );
1119                         ld->ld_errno = LDAP_SUCCESS;
1120                         return( (*result)->lm_msgtype );
1121                 }
1122         }
1123 #endif /* LDAP_CONNECTIONLESS */
1124
1125         /* is this the one we're looking for? */
1126         if ( msgid == LDAP_RES_ANY || id == msgid ) {
1127                 if ( all == LDAP_MSG_ONE
1128                         || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1129                                 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1130                                 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1131                 {
1132                         *result = newmsg;
1133                         ld->ld_errno = LDAP_SUCCESS;
1134                         return( tag );
1135
1136                 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1137                         foundit = 1;    /* return the chain later */
1138                 }
1139         }
1140
1141         /* 
1142          * if not, we must add it to the list of responses.  if
1143          * the msgid is already there, it must be part of an existing
1144          * search response.
1145          */
1146
1147         prev = NULL;
1148         for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1149                 if ( l->lm_msgid == newmsg->lm_msgid ) {
1150                         break;
1151                 }
1152                 prev = l;
1153         }
1154
1155         /* not part of an existing search response */
1156         if ( l == NULL ) {
1157                 if ( foundit ) {
1158                         *result = newmsg;
1159                         goto exit;
1160                 }
1161
1162                 newmsg->lm_next = ld->ld_responses;
1163                 ld->ld_responses = newmsg;
1164                 goto exit;
1165         }
1166
1167         Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1168                 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1169
1170         /* part of a search response - add to end of list of entries */
1171         l->lm_chain_tail->lm_chain = newmsg;
1172         l->lm_chain_tail = newmsg;
1173
1174         /* return the whole chain if that's what we were looking for */
1175         if ( foundit ) {
1176                 if ( prev == NULL ) {
1177                         ld->ld_responses = l->lm_next;
1178                 } else {
1179                         prev->lm_next = l->lm_next;
1180                 }
1181                 *result = l;
1182         }
1183
1184 exit:
1185         if ( foundit ) {
1186                 ld->ld_errno = LDAP_SUCCESS;
1187                 return( tag );
1188         }
1189         if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1190                 goto retry;
1191         }
1192         return( LDAP_MSG_X_KEEP_LOOKING );      /* continue looking */
1193 }
1194
1195
1196 static ber_tag_t
1197 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1198 {
1199         ber_len_t       len;
1200         ber_tag_t       tag;
1201         ber_int_t       along;
1202         BerElement *ber;
1203
1204         *bp = NULL;
1205         ber = ldap_alloc_ber_with_options( ld );
1206
1207         if( ber == NULL ) {
1208                 ld->ld_errno = LDAP_NO_MEMORY;
1209                 return LBER_ERROR;
1210         }
1211
1212         if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1213                 lr->lr_res_msgtype, lr->lr_res_errno,
1214                 lr->lr_res_matched ? lr->lr_res_matched : "",
1215                 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1216         {
1217                 ld->ld_errno = LDAP_ENCODING_ERROR;
1218                 ber_free( ber, 1 );
1219                 return( LBER_ERROR );
1220         }
1221
1222         ber_reset( ber, 1 );
1223
1224         if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1225                 ld->ld_errno = LDAP_DECODING_ERROR;
1226                 ber_free( ber, 1 );
1227                 return( LBER_ERROR );
1228         }
1229
1230         if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1231                 ld->ld_errno = LDAP_DECODING_ERROR;
1232                 ber_free( ber, 1 );
1233                 return( LBER_ERROR );
1234         }
1235
1236         tag = ber_peek_tag( ber, &len );
1237
1238         if ( tag == LBER_ERROR ) {
1239                 ld->ld_errno = LDAP_DECODING_ERROR;
1240                 ber_free( ber, 1 );
1241                 return( LBER_ERROR );
1242         }
1243
1244         *bp = ber;
1245         return tag;
1246 }
1247
1248
1249 /*
1250  * Merge error information in "lr" with "parentr" error code and string.
1251  */
1252 static void
1253 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1254 {
1255         if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1256                 parentr->lr_res_errno = lr->lr_res_errno;
1257                 if ( lr->lr_res_error != NULL ) {
1258                         (void)ldap_append_referral( ld, &parentr->lr_res_error,
1259                                 lr->lr_res_error );
1260                 }
1261
1262         } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1263                 parentr->lr_res_errno == LDAP_SUCCESS )
1264         {
1265                 parentr->lr_res_errno = lr->lr_res_errno;
1266                 if ( parentr->lr_res_error != NULL ) {
1267                         LDAP_FREE( parentr->lr_res_error );
1268                 }
1269                 parentr->lr_res_error = lr->lr_res_error;
1270                 lr->lr_res_error = NULL;
1271                 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1272                         if ( parentr->lr_res_matched != NULL ) {
1273                                 LDAP_FREE( parentr->lr_res_matched );
1274                         }
1275                         parentr->lr_res_matched = lr->lr_res_matched;
1276                         lr->lr_res_matched = NULL;
1277                 }
1278         }
1279
1280         Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
1281                 parentr->lr_msgid, 0, 0 );
1282         Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1283                 parentr->lr_res_errno,
1284                 parentr->lr_res_error ?  parentr->lr_res_error : "",
1285                 parentr->lr_res_matched ?  parentr->lr_res_matched : "" );
1286 }
1287
1288
1289
1290 int
1291 ldap_msgtype( LDAPMessage *lm )
1292 {
1293         assert( lm != NULL );
1294         return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1295 }
1296
1297
1298 int
1299 ldap_msgid( LDAPMessage *lm )
1300 {
1301         assert( lm != NULL );
1302
1303         return ( lm != NULL ) ? lm->lm_msgid : -1;
1304 }
1305
1306
1307 const char *
1308 ldap_int_msgtype2str( ber_tag_t tag )
1309 {
1310         switch( tag ) {
1311         case LDAP_RES_ADD: return "add";
1312         case LDAP_RES_BIND: return "bind";
1313         case LDAP_RES_COMPARE: return "compare";
1314         case LDAP_RES_DELETE: return "delete";
1315         case LDAP_RES_EXTENDED: return "extended-result";
1316         case LDAP_RES_INTERMEDIATE: return "intermediate";
1317         case LDAP_RES_MODIFY: return "modify";
1318         case LDAP_RES_RENAME: return "rename";
1319         case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1320         case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1321         case LDAP_RES_SEARCH_RESULT: return "search-result";
1322         }
1323         return "unknown";
1324 }
1325
1326 int
1327 ldap_msgfree( LDAPMessage *lm )
1328 {
1329         LDAPMessage     *next;
1330         int             type = 0;
1331
1332         Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1333
1334         for ( ; lm != NULL; lm = next ) {
1335                 next = lm->lm_chain;
1336                 type = lm->lm_msgtype;
1337                 ber_free( lm->lm_ber, 1 );
1338                 LDAP_FREE( (char *) lm );
1339         }
1340
1341         return type;
1342 }
1343
1344 /*
1345  * ldap_msgdelete - delete a message.  It returns:
1346  *      0       if the entire message was deleted
1347  *      -1      if the message was not found, or only part of it was found
1348  */
1349 int
1350 ldap_msgdelete( LDAP *ld, int msgid )
1351 {
1352         LDAPMessage     *lm, *prev;
1353         int             rc = 0;
1354
1355         assert( ld != NULL );
1356
1357         Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1358                 (void *)ld, msgid, 0 );
1359
1360 #ifdef LDAP_R_COMPILE
1361         ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1362 #endif
1363         prev = NULL;
1364         for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1365                 if ( lm->lm_msgid == msgid ) {
1366                         break;
1367                 }
1368                 prev = lm;
1369         }
1370
1371         if ( lm == NULL ) {
1372                 rc = -1;
1373
1374         } else {
1375                 if ( prev == NULL ) {
1376                         ld->ld_responses = lm->lm_next;
1377                 } else {
1378                         prev->lm_next = lm->lm_next;
1379                 }
1380         }
1381 #ifdef LDAP_R_COMPILE
1382         ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1383 #endif
1384         if ( lm ) {
1385                 switch ( ldap_msgfree( lm ) ) {
1386                 case LDAP_RES_SEARCH_ENTRY:
1387                 case LDAP_RES_SEARCH_REFERENCE:
1388                 case LDAP_RES_INTERMEDIATE:
1389                         rc = -1;
1390                         break;
1391
1392                 default:
1393                         break;
1394                 }
1395         }
1396
1397         return rc;
1398 }
1399
1400
1401 /*
1402  * ldap_abandoned
1403  *
1404  * return the location of the message id in the array of abandoned
1405  * message ids, or -1
1406  *
1407  * expects ld_res_mutex to be locked
1408  */
1409 static int
1410 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1411 {
1412 #ifdef LDAP_R_COMPILE
1413         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1414 #endif
1415
1416         assert( idxp != NULL );
1417         assert( msgid >= 0 );
1418         assert( ld->ld_nabandoned >= 0 );
1419
1420         return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1421 }
1422
1423 /*
1424  * ldap_mark_abandoned
1425  *
1426  * expects ld_res_mutex to be locked
1427  */
1428 static int
1429 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1430 {
1431 #ifdef LDAP_R_COMPILE
1432         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1433 #endif
1434
1435         /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1436         assert( idx >= 0 );
1437         assert( idx < ld->ld_nabandoned );
1438         assert( ld->ld_abandoned[ idx ] == msgid );
1439
1440         return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
1441                 msgid, idx );
1442 }