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