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