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