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