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