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