]> git.sur5r.net Git - openldap/blob - libraries/libldap/result.c
happy belated New Year
[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-2010 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 && timeout->tv_sec != -1 ) {
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 ( tag != LDAP_RES_BIND &&
751                                 ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
752                                         lr->lr_parent != NULL ))
753                         {
754                                 char            **refs = NULL;
755                                 ber_len_t       len;
756
757                                 /* Check if V3 referral */
758                                 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
759                                         if ( ld->ld_version > LDAP_VERSION2 ) {
760                                                 /* Get the referral list */
761                                                 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
762                                                         rc = LDAP_DECODING_ERROR;
763                                                         lr->lr_status = LDAP_REQST_COMPLETED;
764                                                         Debug( LDAP_DEBUG_TRACE,
765                                                                 "read1msg: referral decode error, "
766                                                                 "mark request completed, ld %p msgid %d\n",
767                                                                 (void *)ld, lr->lr_msgid, 0 );
768
769                                                 } else {
770                                                         /* Chase the referral 
771                                                          * refs array is freed by ldap_chase_v3referrals
772                                                          */
773                                                         refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
774                                                                 0, &lr->lr_res_error, &hadref );
775                                                         lr->lr_status = LDAP_REQST_COMPLETED;
776                                                         Debug( LDAP_DEBUG_TRACE,
777                                                                 "read1msg: referral %s chased, "
778                                                                 "mark request completed, ld %p msgid %d\n",
779                                                                 refer_cnt > 0 ? "" : "not",
780                                                                 (void *)ld, lr->lr_msgid);
781                                                         if ( refer_cnt < 0 ) {
782                                                                 refer_cnt = 0;
783                                                         }
784                                                 }
785                                         }
786                                 } else {
787                                         switch ( lderr ) {
788                                         case LDAP_SUCCESS:
789                                         case LDAP_COMPARE_TRUE:
790                                         case LDAP_COMPARE_FALSE:
791                                                 break;
792
793                                         default:
794                                                 if ( lr->lr_res_error == NULL ) {
795                                                         break;
796                                                 }
797
798                                                 /* pedantic, should never happen */
799                                                 if ( lr->lr_res_error[ 0 ] == '\0' ) {
800                                                         LDAP_FREE( lr->lr_res_error );
801                                                         lr->lr_res_error = NULL;
802                                                         break;  
803                                                 }
804
805                                                 /* V2 referrals are in error string */
806                                                 refer_cnt = ldap_chase_referrals( ld, lr,
807                                                         &lr->lr_res_error, -1, &hadref );
808                                                 lr->lr_status = LDAP_REQST_COMPLETED;
809                                                 Debug( LDAP_DEBUG_TRACE,
810                                                         "read1msg:  V2 referral chased, "
811                                                         "mark request completed, id = %d\n",
812                                                         lr->lr_msgid, 0, 0 );
813                                                 break;
814                                         }
815                                 }
816                         }
817
818                         /* save errno, message, and matched string */
819                         if ( !hadref || lr->lr_res_error == NULL ) {
820                                 lr->lr_res_errno =
821                                         lderr == LDAP_PARTIAL_RESULTS
822                                         ? LDAP_SUCCESS : lderr;
823
824                         } else if ( ld->ld_errno != LDAP_SUCCESS ) {
825                                 lr->lr_res_errno = ld->ld_errno;
826
827                         } else {
828                                 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
829                         }
830                 }
831
832                 /* in any case, don't leave any lr_res_error 'round */
833                 if ( lr_res_error ) {
834                         LDAP_FREE( lr_res_error );
835                 }
836
837                 Debug( LDAP_DEBUG_TRACE,
838                         "read1msg: ld %p %d new referrals\n",
839                         (void *)ld, refer_cnt, 0 );
840
841                 if ( refer_cnt != 0 ) { /* chasing referrals */
842                         ber_free( ber, 1 );
843                         ber = NULL;
844                         if ( refer_cnt < 0 ) {
845                                 ldap_return_request( ld, lr, 0 );
846                                 return( -1 );   /* fatal error */
847                         }
848                         lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
849                         if ( lr->lr_res_matched ) {
850                                 LDAP_FREE( lr->lr_res_matched );
851                                 lr->lr_res_matched = NULL;
852                         }
853
854                 } else {
855                         if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
856                                 /* request without any referrals */
857                                 simple_request = ( hadref ? 0 : 1 );
858
859                         } else {
860                                 /* request with referrals or child request */
861                                 ber_free( ber, 1 );
862                                 ber = NULL;
863                         }
864
865                         lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
866                         Debug( LDAP_DEBUG_TRACE,
867                                 "read1msg:  mark request completed, ld %p msgid %d\n",
868                                 (void *)ld, lr->lr_msgid, 0);
869                         while ( lr->lr_parent != NULL ) {
870                                 merge_error_info( ld, lr->lr_parent, lr );
871
872                                 lr = lr->lr_parent;
873                                 if ( --lr->lr_outrefcnt > 0 ) {
874                                         break;  /* not completely done yet */
875                                 }
876                         }
877
878                         /* Check if all requests are finished, lr is now parent */
879                         tmplr = lr;
880                         if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
881                                 for ( tmplr = lr->lr_child;
882                                         tmplr != NULL;
883                                         tmplr = tmplr->lr_refnext )
884                                 {
885                                         if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
886                                 }
887                         }
888
889                         /* This is the parent request if the request has referrals */
890                         if ( lr->lr_outrefcnt <= 0 &&
891                                 lr->lr_parent == NULL &&
892                                 tmplr == NULL )
893                         {
894                                 id = lr->lr_msgid;
895                                 tag = lr->lr_res_msgtype;
896                                 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
897                                         (void *)ld, id, 0 );
898                                 Debug( LDAP_DEBUG_TRACE,
899                                         "res_errno: %d, res_error: <%s>, "
900                                         "res_matched: <%s>\n",
901                                         lr->lr_res_errno,
902                                         lr->lr_res_error ? lr->lr_res_error : "",
903                                         lr->lr_res_matched ? lr->lr_res_matched : "" );
904                                 if ( !simple_request ) {
905                                         ber_free( ber, 1 );
906                                         ber = NULL;
907                                         if ( build_result_ber( ld, &ber, lr )
908                                             == LBER_ERROR )
909                                         {
910                                                 rc = -1; /* fatal error */
911                                         }
912                                 }
913
914                                 if ( lr != &dummy_lr ) {
915                                         ldap_return_request( ld, lr, 1 );
916                                 }
917                                 lr = NULL;
918                         }
919
920                         /*
921                          * RF 4511 unsolicited (id == 0) responses
922                          * shouldn't necessarily end the connection
923                          */
924                         if ( lc != NULL && id != 0 ) {
925                                 --lc->lconn_refcnt;
926                                 lc = NULL;
927                         }
928                 }
929         }
930
931         if ( lr != NULL ) {
932                 if ( lr != &dummy_lr ) {
933                         ldap_return_request( ld, lr, 0 );
934                 }
935                 lr = NULL;
936         }
937
938         if ( ber == NULL ) {
939                 return( rc );
940         }
941
942         /* try to handle unsolicited responses as appropriate */
943         if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
944                 int     is_nod = 0;
945
946                 tag = ber_peek_tag( &tmpber, &len );
947
948                 /* we have a res oid */
949                 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
950                         static struct berval    bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
951                         struct berval           resoid = BER_BVNULL;
952
953                         if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
954                                 ld->ld_errno = LDAP_DECODING_ERROR;
955                                 ber_free( ber, 1 );
956                                 return -1;
957                         }
958
959                         assert( !BER_BVISEMPTY( &resoid ) );
960
961                         is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
962
963                         tag = ber_peek_tag( &tmpber, &len );
964                 }
965
966 #if 0 /* don't need right now */
967                 /* we have res data */
968                 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
969                         struct berval resdata;
970
971                         if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
972                                 ld->ld_errno = LDAP_DECODING_ERROR;
973                                 ber_free( ber, 0 );
974                                 return ld->ld_errno;
975                         }
976
977                         /* use it... */
978                 }
979 #endif
980
981                 /* handle RFC 4511 "Notice of Disconnection" locally */
982
983                 if ( is_nod ) {
984                         if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
985                                 ld->ld_errno = LDAP_DECODING_ERROR;
986                                 ber_free( ber, 1 );
987                                 return -1;
988                         }
989
990                         /* get rid of the connection... */
991                         if ( lc != NULL ) {
992                                 --lc->lconn_refcnt;
993                         }
994
995                         /* need to return -1, because otherwise
996                          * a valid result is expected */
997                         return -1;
998                 }
999         }
1000
1001         /* make a new ldap message */
1002         newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
1003         if ( newmsg == NULL ) {
1004                 ld->ld_errno = LDAP_NO_MEMORY;
1005                 return( -1 );
1006         }
1007         newmsg->lm_msgid = (int)id;
1008         newmsg->lm_msgtype = tag;
1009         newmsg->lm_ber = ber;
1010         newmsg->lm_chain_tail = newmsg;
1011
1012 #ifdef LDAP_CONNECTIONLESS
1013         /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1014          * the responses are all a sequence wrapped in one message. In
1015          * LDAPv3 each response is in its own message. The datagram must
1016          * end with a SearchResult. We can't just parse each response in
1017          * separate calls to try_read1msg because the header info is only
1018          * present at the beginning of the datagram, not at the beginning
1019          * of each response. So parse all the responses at once and queue
1020          * them up, then pull off the first response to return to the
1021          * caller when all parsing is complete.
1022          */
1023         if ( LDAP_IS_UDP(ld) ) {
1024                 /* If not a result, look for more */
1025                 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1026                         int ok = 0;
1027                         moremsgs = 1;
1028                         if (isv2) {
1029                                 /* LDAPv2: dup the current ber, skip past the current
1030                                  * response, and see if there are any more after it.
1031                                  */
1032                                 ber = ber_dup( ber );
1033                                 ber_scanf( ber, "x" );
1034                                 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1035                                         /* There's more - dup the ber buffer so they can all be
1036                                          * individually freed by ldap_msgfree.
1037                                          */
1038                                         struct berval bv;
1039                                         ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1040                                         bv.bv_val = LDAP_MALLOC( len );
1041                                         if ( bv.bv_val ) {
1042                                                 ok = 1;
1043                                                 ber_read( ber, bv.bv_val, len );
1044                                                 bv.bv_len = len;
1045                                                 ber_init2( ber, &bv, ld->ld_lberoptions );
1046                                         }
1047                                 }
1048                         } else {
1049                                 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1050                                  * datagram, if the sockbuf is readable we still have data
1051                                  * to parse.
1052                                  */
1053                                 ber = ldap_alloc_ber_with_options( ld );
1054                                 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1055                         }
1056                         /* set up response chain */
1057                         if ( tmp == NULL ) {
1058                                 newmsg->lm_next = ld->ld_responses;
1059                                 ld->ld_responses = newmsg;
1060                                 chain_head = newmsg;
1061                         } else {
1062                                 tmp->lm_chain = newmsg;
1063                         }
1064                         chain_head->lm_chain_tail = newmsg;
1065                         tmp = newmsg;
1066                         /* "ok" means there's more to parse */
1067                         if ( ok ) {
1068                                 if ( isv2 ) {
1069                                         goto nextresp2;
1070
1071                                 } else {
1072                                         goto nextresp3;
1073                                 }
1074                         } else {
1075                                 /* got to end of datagram without a SearchResult. Free
1076                                  * our dup'd ber, but leave any buffer alone. For v2 case,
1077                                  * the previous response is still using this buffer. For v3,
1078                                  * the new ber has no buffer to free yet.
1079                                  */
1080                                 ber_free( ber, 0 );
1081                                 return -1;
1082                         }
1083                 } else if ( moremsgs ) {
1084                 /* got search result, and we had multiple responses in 1 datagram.
1085                  * stick the result onto the end of the chain, and then pull the
1086                  * first response off the head of the chain.
1087                  */
1088                         tmp->lm_chain = newmsg;
1089                         chain_head->lm_chain_tail = newmsg;
1090                         *result = chkResponseList( ld, msgid, all );
1091                         ld->ld_errno = LDAP_SUCCESS;
1092                         return( (*result)->lm_msgtype );
1093                 }
1094         }
1095 #endif /* LDAP_CONNECTIONLESS */
1096
1097         /* is this the one we're looking for? */
1098         if ( msgid == LDAP_RES_ANY || id == msgid ) {
1099                 if ( all == LDAP_MSG_ONE
1100                         || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1101                                 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1102                                 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1103                                 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1104                 {
1105                         *result = newmsg;
1106                         ld->ld_errno = LDAP_SUCCESS;
1107                         return( tag );
1108
1109                 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1110                         foundit = 1;    /* return the chain later */
1111                 }
1112         }
1113
1114         /* 
1115          * if not, we must add it to the list of responses.  if
1116          * the msgid is already there, it must be part of an existing
1117          * search response.
1118          */
1119
1120         prev = NULL;
1121         for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1122                 if ( l->lm_msgid == newmsg->lm_msgid ) {
1123                         break;
1124                 }
1125                 prev = l;
1126         }
1127
1128         /* not part of an existing search response */
1129         if ( l == NULL ) {
1130                 if ( foundit ) {
1131                         *result = newmsg;
1132                         goto exit;
1133                 }
1134
1135                 newmsg->lm_next = ld->ld_responses;
1136                 ld->ld_responses = newmsg;
1137                 goto exit;
1138         }
1139
1140         Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1141                 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1142
1143         /* part of a search response - add to end of list of entries */
1144         l->lm_chain_tail->lm_chain = newmsg;
1145         l->lm_chain_tail = newmsg;
1146
1147         /* return the whole chain if that's what we were looking for */
1148         if ( foundit ) {
1149                 if ( prev == NULL ) {
1150                         ld->ld_responses = l->lm_next;
1151                 } else {
1152                         prev->lm_next = l->lm_next;
1153                 }
1154                 *result = l;
1155         }
1156
1157 exit:
1158         if ( foundit ) {
1159                 ld->ld_errno = LDAP_SUCCESS;
1160                 return( tag );
1161         }
1162         if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1163                 goto retry;
1164         }
1165         return( LDAP_MSG_X_KEEP_LOOKING );      /* continue looking */
1166 }
1167
1168
1169 static ber_tag_t
1170 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1171 {
1172         ber_len_t       len;
1173         ber_tag_t       tag;
1174         ber_int_t       along;
1175         BerElement *ber;
1176
1177         *bp = NULL;
1178         ber = ldap_alloc_ber_with_options( ld );
1179
1180         if( ber == NULL ) {
1181                 ld->ld_errno = LDAP_NO_MEMORY;
1182                 return LBER_ERROR;
1183         }
1184
1185         if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1186                 lr->lr_res_msgtype, lr->lr_res_errno,
1187                 lr->lr_res_matched ? lr->lr_res_matched : "",
1188                 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1189         {
1190                 ld->ld_errno = LDAP_ENCODING_ERROR;
1191                 ber_free( ber, 1 );
1192                 return( LBER_ERROR );
1193         }
1194
1195         ber_reset( ber, 1 );
1196
1197         if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1198                 ld->ld_errno = LDAP_DECODING_ERROR;
1199                 ber_free( ber, 1 );
1200                 return( LBER_ERROR );
1201         }
1202
1203         if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1204                 ld->ld_errno = LDAP_DECODING_ERROR;
1205                 ber_free( ber, 1 );
1206                 return( LBER_ERROR );
1207         }
1208
1209         tag = ber_peek_tag( ber, &len );
1210
1211         if ( tag == LBER_ERROR ) {
1212                 ld->ld_errno = LDAP_DECODING_ERROR;
1213                 ber_free( ber, 1 );
1214                 return( LBER_ERROR );
1215         }
1216
1217         *bp = ber;
1218         return tag;
1219 }
1220
1221
1222 /*
1223  * Merge error information in "lr" with "parentr" error code and string.
1224  */
1225 static void
1226 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1227 {
1228         if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1229                 parentr->lr_res_errno = lr->lr_res_errno;
1230                 if ( lr->lr_res_error != NULL ) {
1231                         (void)ldap_append_referral( ld, &parentr->lr_res_error,
1232                                 lr->lr_res_error );
1233                 }
1234
1235         } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1236                 parentr->lr_res_errno == LDAP_SUCCESS )
1237         {
1238                 parentr->lr_res_errno = lr->lr_res_errno;
1239                 if ( parentr->lr_res_error != NULL ) {
1240                         LDAP_FREE( parentr->lr_res_error );
1241                 }
1242                 parentr->lr_res_error = lr->lr_res_error;
1243                 lr->lr_res_error = NULL;
1244                 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1245                         if ( parentr->lr_res_matched != NULL ) {
1246                                 LDAP_FREE( parentr->lr_res_matched );
1247                         }
1248                         parentr->lr_res_matched = lr->lr_res_matched;
1249                         lr->lr_res_matched = NULL;
1250                 }
1251         }
1252
1253         Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
1254                 parentr->lr_msgid, 0, 0 );
1255         Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1256                 parentr->lr_res_errno,
1257                 parentr->lr_res_error ?  parentr->lr_res_error : "",
1258                 parentr->lr_res_matched ?  parentr->lr_res_matched : "" );
1259 }
1260
1261
1262
1263 int
1264 ldap_msgtype( LDAPMessage *lm )
1265 {
1266         assert( lm != NULL );
1267         return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1268 }
1269
1270
1271 int
1272 ldap_msgid( LDAPMessage *lm )
1273 {
1274         assert( lm != NULL );
1275
1276         return ( lm != NULL ) ? lm->lm_msgid : -1;
1277 }
1278
1279
1280 const char *
1281 ldap_int_msgtype2str( ber_tag_t tag )
1282 {
1283         switch( tag ) {
1284         case LDAP_RES_ADD: return "add";
1285         case LDAP_RES_BIND: return "bind";
1286         case LDAP_RES_COMPARE: return "compare";
1287         case LDAP_RES_DELETE: return "delete";
1288         case LDAP_RES_EXTENDED: return "extended-result";
1289         case LDAP_RES_INTERMEDIATE: return "intermediate";
1290         case LDAP_RES_MODIFY: return "modify";
1291         case LDAP_RES_RENAME: return "rename";
1292         case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1293         case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1294         case LDAP_RES_SEARCH_RESULT: return "search-result";
1295         }
1296         return "unknown";
1297 }
1298
1299 int
1300 ldap_msgfree( LDAPMessage *lm )
1301 {
1302         LDAPMessage     *next;
1303         int             type = 0;
1304
1305         Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1306
1307         for ( ; lm != NULL; lm = next ) {
1308                 next = lm->lm_chain;
1309                 type = lm->lm_msgtype;
1310                 ber_free( lm->lm_ber, 1 );
1311                 LDAP_FREE( (char *) lm );
1312         }
1313
1314         return type;
1315 }
1316
1317 /*
1318  * ldap_msgdelete - delete a message.  It returns:
1319  *      0       if the entire message was deleted
1320  *      -1      if the message was not found, or only part of it was found
1321  */
1322 int
1323 ldap_msgdelete( LDAP *ld, int msgid )
1324 {
1325         LDAPMessage     *lm, *prev;
1326         int             rc = 0;
1327
1328         assert( ld != NULL );
1329
1330         Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1331                 (void *)ld, msgid, 0 );
1332
1333 #ifdef LDAP_R_COMPILE
1334         ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1335 #endif
1336         prev = NULL;
1337         for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1338                 if ( lm->lm_msgid == msgid ) {
1339                         break;
1340                 }
1341                 prev = lm;
1342         }
1343
1344         if ( lm == NULL ) {
1345                 rc = -1;
1346
1347         } else {
1348                 if ( prev == NULL ) {
1349                         ld->ld_responses = lm->lm_next;
1350                 } else {
1351                         prev->lm_next = lm->lm_next;
1352                 }
1353         }
1354 #ifdef LDAP_R_COMPILE
1355         ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1356 #endif
1357         if ( lm ) {
1358                 switch ( ldap_msgfree( lm ) ) {
1359                 case LDAP_RES_SEARCH_ENTRY:
1360                 case LDAP_RES_SEARCH_REFERENCE:
1361                 case LDAP_RES_INTERMEDIATE:
1362                         rc = -1;
1363                         break;
1364
1365                 default:
1366                         break;
1367                 }
1368         }
1369
1370         return rc;
1371 }
1372
1373
1374 /*
1375  * ldap_abandoned
1376  *
1377  * return the location of the message id in the array of abandoned
1378  * message ids, or -1
1379  *
1380  * expects ld_res_mutex to be locked
1381  */
1382 static int
1383 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1384 {
1385 #ifdef LDAP_R_COMPILE
1386         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1387 #endif
1388
1389         assert( idxp != NULL );
1390         assert( msgid >= 0 );
1391
1392         return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1393 }
1394
1395 /*
1396  * ldap_mark_abandoned
1397  *
1398  * expects ld_res_mutex to be locked
1399  */
1400 static int
1401 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1402 {
1403 #ifdef LDAP_R_COMPILE
1404         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1405 #endif
1406
1407         /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1408         assert( idx >= 0 );
1409         assert( (unsigned) idx < ld->ld_nabandoned );
1410         assert( ld->ld_abandoned[ idx ] == msgid );
1411
1412         return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
1413                 msgid, idx );
1414 }