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