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