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