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