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