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