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