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