]> git.sur5r.net Git - openldap/blob - libraries/libldap/result.c
ITS#5980
[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         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 ) {
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 ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
751                                         lr->lr_parent != NULL )
752                         {
753                                 char            **refs = NULL;
754                                 ber_len_t       len;
755
756                                 /* Check if V3 referral */
757                                 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
758                                         if ( ld->ld_version > LDAP_VERSION2 ) {
759                                                 /* Get the referral list */
760                                                 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
761                                                         rc = LDAP_DECODING_ERROR;
762                                                         lr->lr_status = LDAP_REQST_COMPLETED;
763                                                         Debug( LDAP_DEBUG_TRACE,
764                                                                 "read1msg: referral decode error, "
765                                                                 "mark request completed, ld %p msgid %d\n",
766                                                                 (void *)ld, lr->lr_msgid, 0 );
767
768                                                 } else {
769                                                         /* Chase the referral 
770                                                          * refs array is freed by ldap_chase_v3referrals
771                                                          */
772                                                         refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
773                                                                 0, &lr->lr_res_error, &hadref );
774                                                         lr->lr_status = LDAP_REQST_COMPLETED;
775                                                         Debug( LDAP_DEBUG_TRACE,
776                                                                 "read1msg: referral %s chased, "
777                                                                 "mark request completed, ld %p msgid %d\n",
778                                                                 refer_cnt > 0 ? "" : "not",
779                                                                 (void *)ld, lr->lr_msgid);
780                                                         if ( refer_cnt < 0 ) {
781                                                                 refer_cnt = 0;
782                                                         }
783                                                 }
784                                         }
785                                 } else {
786                                         switch ( lderr ) {
787                                         case LDAP_SUCCESS:
788                                         case LDAP_COMPARE_TRUE:
789                                         case LDAP_COMPARE_FALSE:
790                                                 break;
791
792                                         default:
793                                                 if ( lr->lr_res_error == NULL ) {
794                                                         break;
795                                                 }
796
797                                                 /* pedantic, should never happen */
798                                                 if ( lr->lr_res_error[ 0 ] == '\0' ) {
799                                                         LDAP_FREE( lr->lr_res_error );
800                                                         lr->lr_res_error = NULL;
801                                                         break;  
802                                                 }
803
804                                                 /* V2 referrals are in error string */
805                                                 refer_cnt = ldap_chase_referrals( ld, lr,
806                                                         &lr->lr_res_error, -1, &hadref );
807                                                 lr->lr_status = LDAP_REQST_COMPLETED;
808                                                 Debug( LDAP_DEBUG_TRACE,
809                                                         "read1msg:  V2 referral chased, "
810                                                         "mark request completed, id = %d\n",
811                                                         lr->lr_msgid, 0, 0 );
812                                                 break;
813                                         }
814                                 }
815                         }
816
817                         /* save errno, message, and matched string */
818                         if ( !hadref || lr->lr_res_error == NULL ) {
819                                 lr->lr_res_errno =
820                                         lderr == LDAP_PARTIAL_RESULTS
821                                         ? LDAP_SUCCESS : lderr;
822
823                         } else if ( ld->ld_errno != LDAP_SUCCESS ) {
824                                 lr->lr_res_errno = ld->ld_errno;
825
826                         } else {
827                                 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
828                         }
829                 }
830
831                 /* in any case, don't leave any lr_res_error 'round */
832                 if ( lr_res_error ) {
833                         LDAP_FREE( lr_res_error );
834                 }
835
836                 Debug( LDAP_DEBUG_TRACE,
837                         "read1msg: ld %p %d new referrals\n",
838                         (void *)ld, refer_cnt, 0 );
839
840                 if ( refer_cnt != 0 ) { /* chasing referrals */
841                         ber_free( ber, 1 );
842                         ber = NULL;
843                         if ( refer_cnt < 0 ) {
844                                 ldap_return_request( ld, lr, 0 );
845                                 return( -1 );   /* fatal error */
846                         }
847                         lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
848                         if ( lr->lr_res_matched ) {
849                                 LDAP_FREE( lr->lr_res_matched );
850                                 lr->lr_res_matched = NULL;
851                         }
852
853                 } else {
854                         if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
855                                 /* request without any referrals */
856                                 simple_request = ( hadref ? 0 : 1 );
857
858                         } else {
859                                 /* request with referrals or child request */
860                                 ber_free( ber, 1 );
861                                 ber = NULL;
862                         }
863
864                         lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
865                         Debug( LDAP_DEBUG_TRACE,
866                                 "read1msg:  mark request completed, ld %p msgid %d\n",
867                                 (void *)ld, lr->lr_msgid, 0);
868                         while ( lr->lr_parent != NULL ) {
869                                 merge_error_info( ld, lr->lr_parent, lr );
870
871                                 lr = lr->lr_parent;
872                                 if ( --lr->lr_outrefcnt > 0 ) {
873                                         break;  /* not completely done yet */
874                                 }
875                         }
876
877                         /* Check if all requests are finished, lr is now parent */
878                         tmplr = lr;
879                         if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
880                                 for ( tmplr = lr->lr_child;
881                                         tmplr != NULL;
882                                         tmplr = tmplr->lr_refnext )
883                                 {
884                                         if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
885                                 }
886                         }
887
888                         /* This is the parent request if the request has referrals */
889                         if ( lr->lr_outrefcnt <= 0 &&
890                                 lr->lr_parent == NULL &&
891                                 tmplr == NULL )
892                         {
893                                 id = lr->lr_msgid;
894                                 tag = lr->lr_res_msgtype;
895                                 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
896                                         (void *)ld, id, 0 );
897                                 Debug( LDAP_DEBUG_TRACE,
898                                         "res_errno: %d, res_error: <%s>, "
899                                         "res_matched: <%s>\n",
900                                         lr->lr_res_errno,
901                                         lr->lr_res_error ? lr->lr_res_error : "",
902                                         lr->lr_res_matched ? lr->lr_res_matched : "" );
903                                 if ( !simple_request ) {
904                                         ber_free( ber, 1 );
905                                         ber = NULL;
906                                         if ( build_result_ber( ld, &ber, lr )
907                                             == LBER_ERROR )
908                                         {
909                                                 rc = -1; /* fatal error */
910                                         }
911                                 }
912
913                                 if ( lr != &dummy_lr ) {
914                                         ldap_return_request( ld, lr, 1 );
915                                 }
916                                 lr = NULL;
917                         }
918
919                         /*
920                          * RF 4511 unsolicited (id == 0) responses
921                          * shouldn't necessarily end the connection
922                          */
923                         if ( lc != NULL && id != 0 ) {
924                                 --lc->lconn_refcnt;
925                                 lc = NULL;
926                         }
927                 }
928         }
929
930         if ( lr != NULL ) {
931                 if ( lr != &dummy_lr ) {
932                         ldap_return_request( ld, lr, 0 );
933                 }
934                 lr = NULL;
935         }
936
937         if ( ber == NULL ) {
938                 return( rc );
939         }
940
941         /* try to handle unsolicited responses as appropriate */
942         if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
943                 int     is_nod = 0;
944
945                 tag = ber_peek_tag( &tmpber, &len );
946
947                 /* we have a res oid */
948                 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
949                         static struct berval    bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
950                         struct berval           resoid = BER_BVNULL;
951
952                         if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
953                                 ld->ld_errno = LDAP_DECODING_ERROR;
954                                 ber_free( ber, 1 );
955                                 return -1;
956                         }
957
958                         assert( !BER_BVISEMPTY( &resoid ) );
959
960                         is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
961
962                         tag = ber_peek_tag( &tmpber, &len );
963                 }
964
965 #if 0 /* don't need right now */
966                 /* we have res data */
967                 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
968                         struct berval resdata;
969
970                         if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
971                                 ld->ld_errno = LDAP_DECODING_ERROR;
972                                 ber_free( ber, 0 );
973                                 return ld->ld_errno;
974                         }
975
976                         /* use it... */
977                 }
978 #endif
979
980                 /* handle RFC 4511 "Notice of Disconnection" locally */
981
982                 if ( is_nod ) {
983                         if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
984                                 ld->ld_errno = LDAP_DECODING_ERROR;
985                                 ber_free( ber, 1 );
986                                 return -1;
987                         }
988
989                         /* get rid of the connection... */
990                         if ( lc != NULL ) {
991                                 --lc->lconn_refcnt;
992                         }
993
994                         /* need to return -1, because otherwise
995                          * a valid result is expected */
996                         return -1;
997                 }
998         }
999
1000         /* make a new ldap message */
1001         newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
1002         if ( newmsg == NULL ) {
1003                 ld->ld_errno = LDAP_NO_MEMORY;
1004                 return( -1 );
1005         }
1006         newmsg->lm_msgid = (int)id;
1007         newmsg->lm_msgtype = tag;
1008         newmsg->lm_ber = ber;
1009         newmsg->lm_chain_tail = newmsg;
1010
1011 #ifdef LDAP_CONNECTIONLESS
1012         /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1013          * the responses are all a sequence wrapped in one message. In
1014          * LDAPv3 each response is in its own message. The datagram must
1015          * end with a SearchResult. We can't just parse each response in
1016          * separate calls to try_read1msg because the header info is only
1017          * present at the beginning of the datagram, not at the beginning
1018          * of each response. So parse all the responses at once and queue
1019          * them up, then pull off the first response to return to the
1020          * caller when all parsing is complete.
1021          */
1022         if ( LDAP_IS_UDP(ld) ) {
1023                 /* If not a result, look for more */
1024                 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1025                         int ok = 0;
1026                         moremsgs = 1;
1027                         if (isv2) {
1028                                 /* LDAPv2: dup the current ber, skip past the current
1029                                  * response, and see if there are any more after it.
1030                                  */
1031                                 ber = ber_dup( ber );
1032                                 ber_scanf( ber, "x" );
1033                                 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1034                                         /* There's more - dup the ber buffer so they can all be
1035                                          * individually freed by ldap_msgfree.
1036                                          */
1037                                         struct berval bv;
1038                                         ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1039                                         bv.bv_val = LDAP_MALLOC( len );
1040                                         if ( bv.bv_val ) {
1041                                                 ok = 1;
1042                                                 ber_read( ber, bv.bv_val, len );
1043                                                 bv.bv_len = len;
1044                                                 ber_init2( ber, &bv, ld->ld_lberoptions );
1045                                         }
1046                                 }
1047                         } else {
1048                                 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1049                                  * datagram, if the sockbuf is readable we still have data
1050                                  * to parse.
1051                                  */
1052                                 ber = ldap_alloc_ber_with_options( ld );
1053                                 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1054                         }
1055                         /* set up response chain */
1056                         if ( tmp == NULL ) {
1057                                 newmsg->lm_next = ld->ld_responses;
1058                                 ld->ld_responses = newmsg;
1059                                 chain_head = newmsg;
1060                         } else {
1061                                 tmp->lm_chain = newmsg;
1062                         }
1063                         chain_head->lm_chain_tail = newmsg;
1064                         tmp = newmsg;
1065                         /* "ok" means there's more to parse */
1066                         if ( ok ) {
1067                                 if ( isv2 ) {
1068                                         goto nextresp2;
1069
1070                                 } else {
1071                                         goto nextresp3;
1072                                 }
1073                         } else {
1074                                 /* got to end of datagram without a SearchResult. Free
1075                                  * our dup'd ber, but leave any buffer alone. For v2 case,
1076                                  * the previous response is still using this buffer. For v3,
1077                                  * the new ber has no buffer to free yet.
1078                                  */
1079                                 ber_free( ber, 0 );
1080                                 return -1;
1081                         }
1082                 } else if ( moremsgs ) {
1083                 /* got search result, and we had multiple responses in 1 datagram.
1084                  * stick the result onto the end of the chain, and then pull the
1085                  * first response off the head of the chain.
1086                  */
1087                         tmp->lm_chain = newmsg;
1088                         chain_head->lm_chain_tail = newmsg;
1089                         *result = chkResponseList( ld, msgid, all );
1090                         ld->ld_errno = LDAP_SUCCESS;
1091                         return( (*result)->lm_msgtype );
1092                 }
1093         }
1094 #endif /* LDAP_CONNECTIONLESS */
1095
1096         /* is this the one we're looking for? */
1097         if ( msgid == LDAP_RES_ANY || id == msgid ) {
1098                 if ( all == LDAP_MSG_ONE
1099                         || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1100                                 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1101                                 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1102                                 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1103                 {
1104                         *result = newmsg;
1105                         ld->ld_errno = LDAP_SUCCESS;
1106                         return( tag );
1107
1108                 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1109                         foundit = 1;    /* return the chain later */
1110                 }
1111         }
1112
1113         /* 
1114          * if not, we must add it to the list of responses.  if
1115          * the msgid is already there, it must be part of an existing
1116          * search response.
1117          */
1118
1119         prev = NULL;
1120         for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1121                 if ( l->lm_msgid == newmsg->lm_msgid ) {
1122                         break;
1123                 }
1124                 prev = l;
1125         }
1126
1127         /* not part of an existing search response */
1128         if ( l == NULL ) {
1129                 if ( foundit ) {
1130                         *result = newmsg;
1131                         goto exit;
1132                 }
1133
1134                 newmsg->lm_next = ld->ld_responses;
1135                 ld->ld_responses = newmsg;
1136                 goto exit;
1137         }
1138
1139         Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1140                 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1141
1142         /* part of a search response - add to end of list of entries */
1143         l->lm_chain_tail->lm_chain = newmsg;
1144         l->lm_chain_tail = newmsg;
1145
1146         /* return the whole chain if that's what we were looking for */
1147         if ( foundit ) {
1148                 if ( prev == NULL ) {
1149                         ld->ld_responses = l->lm_next;
1150                 } else {
1151                         prev->lm_next = l->lm_next;
1152                 }
1153                 *result = l;
1154         }
1155
1156 exit:
1157         if ( foundit ) {
1158                 ld->ld_errno = LDAP_SUCCESS;
1159                 return( tag );
1160         }
1161         if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1162                 goto retry;
1163         }
1164         return( LDAP_MSG_X_KEEP_LOOKING );      /* continue looking */
1165 }
1166
1167
1168 static ber_tag_t
1169 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1170 {
1171         ber_len_t       len;
1172         ber_tag_t       tag;
1173         ber_int_t       along;
1174         BerElement *ber;
1175
1176         *bp = NULL;
1177         ber = ldap_alloc_ber_with_options( ld );
1178
1179         if( ber == NULL ) {
1180                 ld->ld_errno = LDAP_NO_MEMORY;
1181                 return LBER_ERROR;
1182         }
1183
1184         if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1185                 lr->lr_res_msgtype, lr->lr_res_errno,
1186                 lr->lr_res_matched ? lr->lr_res_matched : "",
1187                 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1188         {
1189                 ld->ld_errno = LDAP_ENCODING_ERROR;
1190                 ber_free( ber, 1 );
1191                 return( LBER_ERROR );
1192         }
1193
1194         ber_reset( ber, 1 );
1195
1196         if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1197                 ld->ld_errno = LDAP_DECODING_ERROR;
1198                 ber_free( ber, 1 );
1199                 return( LBER_ERROR );
1200         }
1201
1202         if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1203                 ld->ld_errno = LDAP_DECODING_ERROR;
1204                 ber_free( ber, 1 );
1205                 return( LBER_ERROR );
1206         }
1207
1208         tag = ber_peek_tag( ber, &len );
1209
1210         if ( tag == LBER_ERROR ) {
1211                 ld->ld_errno = LDAP_DECODING_ERROR;
1212                 ber_free( ber, 1 );
1213                 return( LBER_ERROR );
1214         }
1215
1216         *bp = ber;
1217         return tag;
1218 }
1219
1220
1221 /*
1222  * Merge error information in "lr" with "parentr" error code and string.
1223  */
1224 static void
1225 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1226 {
1227         if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1228                 parentr->lr_res_errno = lr->lr_res_errno;
1229                 if ( lr->lr_res_error != NULL ) {
1230                         (void)ldap_append_referral( ld, &parentr->lr_res_error,
1231                                 lr->lr_res_error );
1232                 }
1233
1234         } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1235                 parentr->lr_res_errno == LDAP_SUCCESS )
1236         {
1237                 parentr->lr_res_errno = lr->lr_res_errno;
1238                 if ( parentr->lr_res_error != NULL ) {
1239                         LDAP_FREE( parentr->lr_res_error );
1240                 }
1241                 parentr->lr_res_error = lr->lr_res_error;
1242                 lr->lr_res_error = NULL;
1243                 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1244                         if ( parentr->lr_res_matched != NULL ) {
1245                                 LDAP_FREE( parentr->lr_res_matched );
1246                         }
1247                         parentr->lr_res_matched = lr->lr_res_matched;
1248                         lr->lr_res_matched = NULL;
1249                 }
1250         }
1251
1252         Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
1253                 parentr->lr_msgid, 0, 0 );
1254         Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1255                 parentr->lr_res_errno,
1256                 parentr->lr_res_error ?  parentr->lr_res_error : "",
1257                 parentr->lr_res_matched ?  parentr->lr_res_matched : "" );
1258 }
1259
1260
1261
1262 int
1263 ldap_msgtype( LDAPMessage *lm )
1264 {
1265         assert( lm != NULL );
1266         return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1267 }
1268
1269
1270 int
1271 ldap_msgid( LDAPMessage *lm )
1272 {
1273         assert( lm != NULL );
1274
1275         return ( lm != NULL ) ? lm->lm_msgid : -1;
1276 }
1277
1278
1279 const char *
1280 ldap_int_msgtype2str( ber_tag_t tag )
1281 {
1282         switch( tag ) {
1283         case LDAP_RES_ADD: return "add";
1284         case LDAP_RES_BIND: return "bind";
1285         case LDAP_RES_COMPARE: return "compare";
1286         case LDAP_RES_DELETE: return "delete";
1287         case LDAP_RES_EXTENDED: return "extended-result";
1288         case LDAP_RES_INTERMEDIATE: return "intermediate";
1289         case LDAP_RES_MODIFY: return "modify";
1290         case LDAP_RES_RENAME: return "rename";
1291         case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1292         case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1293         case LDAP_RES_SEARCH_RESULT: return "search-result";
1294         }
1295         return "unknown";
1296 }
1297
1298 int
1299 ldap_msgfree( LDAPMessage *lm )
1300 {
1301         LDAPMessage     *next;
1302         int             type = 0;
1303
1304         Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1305
1306         for ( ; lm != NULL; lm = next ) {
1307                 next = lm->lm_chain;
1308                 type = lm->lm_msgtype;
1309                 ber_free( lm->lm_ber, 1 );
1310                 LDAP_FREE( (char *) lm );
1311         }
1312
1313         return type;
1314 }
1315
1316 /*
1317  * ldap_msgdelete - delete a message.  It returns:
1318  *      0       if the entire message was deleted
1319  *      -1      if the message was not found, or only part of it was found
1320  */
1321 int
1322 ldap_msgdelete( LDAP *ld, int msgid )
1323 {
1324         LDAPMessage     *lm, *prev;
1325         int             rc = 0;
1326
1327         assert( ld != NULL );
1328
1329         Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1330                 (void *)ld, msgid, 0 );
1331
1332 #ifdef LDAP_R_COMPILE
1333         ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1334 #endif
1335         prev = NULL;
1336         for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1337                 if ( lm->lm_msgid == msgid ) {
1338                         break;
1339                 }
1340                 prev = lm;
1341         }
1342
1343         if ( lm == NULL ) {
1344                 rc = -1;
1345
1346         } else {
1347                 if ( prev == NULL ) {
1348                         ld->ld_responses = lm->lm_next;
1349                 } else {
1350                         prev->lm_next = lm->lm_next;
1351                 }
1352         }
1353 #ifdef LDAP_R_COMPILE
1354         ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1355 #endif
1356         if ( lm ) {
1357                 switch ( ldap_msgfree( lm ) ) {
1358                 case LDAP_RES_SEARCH_ENTRY:
1359                 case LDAP_RES_SEARCH_REFERENCE:
1360                 case LDAP_RES_INTERMEDIATE:
1361                         rc = -1;
1362                         break;
1363
1364                 default:
1365                         break;
1366                 }
1367         }
1368
1369         return rc;
1370 }
1371
1372
1373 /*
1374  * ldap_abandoned
1375  *
1376  * return the location of the message id in the array of abandoned
1377  * message ids, or -1
1378  *
1379  * expects ld_res_mutex to be locked
1380  */
1381 static int
1382 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1383 {
1384 #ifdef LDAP_R_COMPILE
1385         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1386 #endif
1387
1388         assert( idxp != NULL );
1389         assert( msgid >= 0 );
1390
1391         return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1392 }
1393
1394 /*
1395  * ldap_mark_abandoned
1396  *
1397  * expects ld_res_mutex to be locked
1398  */
1399 static int
1400 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1401 {
1402 #ifdef LDAP_R_COMPILE
1403         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1404 #endif
1405
1406         /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1407         assert( idx >= 0 );
1408         assert( (unsigned) idx < ld->ld_nabandoned );
1409         assert( ld->ld_abandoned[ idx ] == msgid );
1410
1411         return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
1412                 msgid, idx );
1413 }