]> git.sur5r.net Git - openldap/blob - libraries/libldap/result.c
unsolicited responses shouldn't necessarily end the connection
[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                         /*
882                          * RF 4511 unsolicited (id == 0) responses
883                          * shouldn't necessarily end the connection
884                          */
885                         if ( lc != NULL && id != 0 ) {
886 #ifdef LDAP_R_COMPILE
887                                 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
888 #endif
889                                 ldap_free_connection( ld, lc, 0, 1 );
890 #ifdef LDAP_R_COMPILE
891                                 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
892 #endif
893                                 lc = *lcp = NULL;
894                         }
895                 }
896         }
897
898         if ( lr != NULL ) {
899                 if ( lr != &dummy_lr ) {
900                         ldap_return_request( ld, lr, 0 );
901                 }
902                 lr = NULL;
903         }
904
905         if ( ber == NULL ) {
906                 return( rc );
907         }
908
909         /* try to handle unsolicited responses as appropriate */
910         if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
911                 int     is_nod = 0;
912
913                 tag = ber_peek_tag( &tmpber, &len );
914
915                 /* we have a res oid */
916                 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
917                         static struct berval    bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
918                         struct berval           resoid = BER_BVNULL;
919
920                         if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
921                                 ld->ld_errno = LDAP_DECODING_ERROR;
922                                 ber_free( ber, 1 );
923                                 return -1;
924                         }
925
926                         assert( !BER_BVISEMPTY( &resoid ) );
927
928                         is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
929
930                         tag = ber_peek_tag( &tmpber, &len );
931                 }
932
933 #if 0 /* don't need right now */
934                 /* we have res data */
935                 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
936                         struct berval resdata;
937
938                         if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
939                                 ld->ld_errno = LDAP_DECODING_ERROR;
940                                 ber_free( ber, 0 );
941                                 return ld->ld_errno;
942                         }
943
944                         /* use it... */
945                 }
946 #endif
947
948                 /* handle RFC 4511 "Notice of Disconnection" locally */
949
950                 if ( is_nod ) {
951                         if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
952                                 ld->ld_errno = LDAP_DECODING_ERROR;
953                                 ber_free( ber, 1 );
954                                 return -1;
955                         }
956
957                         /* get rid of the connection... */
958                         if ( lc != NULL ) {
959 #ifdef LDAP_R_COMPILE
960                                 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
961 #endif
962                                 ldap_free_connection( ld, lc, 0, 1 );
963 #ifdef LDAP_R_COMPILE
964                                 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
965 #endif
966                                 lc = *lcp = NULL;
967                         }
968
969                         /* need to return -1, because otherwise
970                          * a valid result is expected */
971                         return -1;
972                 }
973         }
974
975         /* make a new ldap message */
976         newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
977         if ( newmsg == NULL ) {
978                 ld->ld_errno = LDAP_NO_MEMORY;
979                 return( -1 );
980         }
981         newmsg->lm_msgid = (int)id;
982         newmsg->lm_msgtype = tag;
983         newmsg->lm_ber = ber;
984         newmsg->lm_chain_tail = newmsg;
985
986 #ifdef LDAP_CONNECTIONLESS
987         /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
988          * the responses are all a sequence wrapped in one message. In
989          * LDAPv3 each response is in its own message. The datagram must
990          * end with a SearchResult. We can't just parse each response in
991          * separate calls to try_read1msg because the header info is only
992          * present at the beginning of the datagram, not at the beginning
993          * of each response. So parse all the responses at once and queue
994          * them up, then pull off the first response to return to the
995          * caller when all parsing is complete.
996          */
997         if ( LDAP_IS_UDP(ld) ) {
998                 /* If not a result, look for more */
999                 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1000                         int ok = 0;
1001                         moremsgs = 1;
1002                         if (isv2) {
1003                                 /* LDAPv2: dup the current ber, skip past the current
1004                                  * response, and see if there are any more after it.
1005                                  */
1006                                 ber = ber_dup( ber );
1007                                 ber_scanf( ber, "x" );
1008                                 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1009                                         /* There's more - dup the ber buffer so they can all be
1010                                          * individually freed by ldap_msgfree.
1011                                          */
1012                                         struct berval bv;
1013                                         ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1014                                         bv.bv_val = LDAP_MALLOC( len );
1015                                         if ( bv.bv_val ) {
1016                                                 ok = 1;
1017                                                 ber_read( ber, bv.bv_val, len );
1018                                                 bv.bv_len = len;
1019                                                 ber_init2( ber, &bv, ld->ld_lberoptions );
1020                                         }
1021                                 }
1022                         } else {
1023                                 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1024                                  * datagram, if the sockbuf is readable we still have data
1025                                  * to parse.
1026                                  */
1027                                 ber = ldap_alloc_ber_with_options( ld );
1028                                 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1029                         }
1030                         /* set up response chain */
1031                         if ( tmp == NULL ) {
1032                                 newmsg->lm_next = ld->ld_responses;
1033                                 ld->ld_responses = newmsg;
1034                                 chain_head = newmsg;
1035                         } else {
1036                                 tmp->lm_chain = newmsg;
1037                         }
1038                         chain_head->lm_chain_tail = newmsg;
1039                         tmp = newmsg;
1040                         /* "ok" means there's more to parse */
1041                         if ( ok ) {
1042                                 if ( isv2 ) {
1043                                         goto nextresp2;
1044
1045                                 } else {
1046                                         goto nextresp3;
1047                                 }
1048                         } else {
1049                                 /* got to end of datagram without a SearchResult. Free
1050                                  * our dup'd ber, but leave any buffer alone. For v2 case,
1051                                  * the previous response is still using this buffer. For v3,
1052                                  * the new ber has no buffer to free yet.
1053                                  */
1054                                 ber_free( ber, 0 );
1055                                 return -1;
1056                         }
1057                 } else if ( moremsgs ) {
1058                 /* got search result, and we had multiple responses in 1 datagram.
1059                  * stick the result onto the end of the chain, and then pull the
1060                  * first response off the head of the chain.
1061                  */
1062                         tmp->lm_chain = newmsg;
1063                         chain_head->lm_chain_tail = newmsg;
1064                         *result = chkResponseList( ld, msgid, all );
1065                         ld->ld_errno = LDAP_SUCCESS;
1066                         return( (*result)->lm_msgtype );
1067                 }
1068         }
1069 #endif /* LDAP_CONNECTIONLESS */
1070
1071         /* is this the one we're looking for? */
1072         if ( msgid == LDAP_RES_ANY || id == msgid ) {
1073                 if ( all == LDAP_MSG_ONE
1074                         || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1075                                 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1076                                 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1077                 {
1078                         *result = newmsg;
1079                         ld->ld_errno = LDAP_SUCCESS;
1080                         return( tag );
1081
1082                 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1083                         foundit = 1;    /* return the chain later */
1084                 }
1085         }
1086
1087         /* 
1088          * if not, we must add it to the list of responses.  if
1089          * the msgid is already there, it must be part of an existing
1090          * search response.
1091          */
1092
1093         prev = NULL;
1094         for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1095                 if ( l->lm_msgid == newmsg->lm_msgid ) {
1096                         break;
1097                 }
1098                 prev = l;
1099         }
1100
1101         /* not part of an existing search response */
1102         if ( l == NULL ) {
1103                 if ( foundit ) {
1104                         *result = newmsg;
1105                         goto exit;
1106                 }
1107
1108                 newmsg->lm_next = ld->ld_responses;
1109                 ld->ld_responses = newmsg;
1110                 goto exit;
1111         }
1112
1113         Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %ld type %ld:\n",
1114                 (void *)ld, (long) newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1115
1116         /* part of a search response - add to end of list of entries */
1117         l->lm_chain_tail->lm_chain = newmsg;
1118         l->lm_chain_tail = newmsg;
1119
1120         /* return the whole chain if that's what we were looking for */
1121         if ( foundit ) {
1122                 if ( prev == NULL ) {
1123                         ld->ld_responses = l->lm_next;
1124                 } else {
1125                         prev->lm_next = l->lm_next;
1126                 }
1127                 *result = l;
1128         }
1129
1130 exit:
1131         if ( foundit ) {
1132                 ld->ld_errno = LDAP_SUCCESS;
1133                 return( tag );
1134         }
1135         if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1136                 goto retry;
1137         }
1138         return( LDAP_MSG_X_KEEP_LOOKING );      /* continue looking */
1139 }
1140
1141
1142 static ber_tag_t
1143 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1144 {
1145         ber_len_t       len;
1146         ber_tag_t       tag;
1147         ber_int_t       along;
1148         BerElement *ber;
1149
1150         *bp = NULL;
1151         ber = ldap_alloc_ber_with_options( ld );
1152
1153         if( ber == NULL ) {
1154                 ld->ld_errno = LDAP_NO_MEMORY;
1155                 return LBER_ERROR;
1156         }
1157
1158         if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1159                 lr->lr_res_msgtype, lr->lr_res_errno,
1160                 lr->lr_res_matched ? lr->lr_res_matched : "",
1161                 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1162         {
1163                 ld->ld_errno = LDAP_ENCODING_ERROR;
1164                 ber_free( ber, 1 );
1165                 return( LBER_ERROR );
1166         }
1167
1168         ber_reset( ber, 1 );
1169
1170         if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1171                 ld->ld_errno = LDAP_DECODING_ERROR;
1172                 ber_free( ber, 1 );
1173                 return( LBER_ERROR );
1174         }
1175
1176         if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1177                 ld->ld_errno = LDAP_DECODING_ERROR;
1178                 ber_free( ber, 1 );
1179                 return( LBER_ERROR );
1180         }
1181
1182         tag = ber_peek_tag( ber, &len );
1183
1184         if ( tag == LBER_ERROR ) {
1185                 ld->ld_errno = LDAP_DECODING_ERROR;
1186                 ber_free( ber, 1 );
1187                 return( LBER_ERROR );
1188         }
1189
1190         *bp = ber;
1191         return tag;
1192 }
1193
1194
1195 /*
1196  * Merge error information in "lr" with "parentr" error code and string.
1197  */
1198 static void
1199 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1200 {
1201         if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1202                 parentr->lr_res_errno = lr->lr_res_errno;
1203                 if ( lr->lr_res_error != NULL ) {
1204                         (void)ldap_append_referral( ld, &parentr->lr_res_error,
1205                                 lr->lr_res_error );
1206                 }
1207
1208         } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1209                 parentr->lr_res_errno == LDAP_SUCCESS )
1210         {
1211                 parentr->lr_res_errno = lr->lr_res_errno;
1212                 if ( parentr->lr_res_error != NULL ) {
1213                         LDAP_FREE( parentr->lr_res_error );
1214                 }
1215                 parentr->lr_res_error = lr->lr_res_error;
1216                 lr->lr_res_error = NULL;
1217                 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1218                         if ( parentr->lr_res_matched != NULL ) {
1219                                 LDAP_FREE( parentr->lr_res_matched );
1220                         }
1221                         parentr->lr_res_matched = lr->lr_res_matched;
1222                         lr->lr_res_matched = NULL;
1223                 }
1224         }
1225
1226         Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
1227                 parentr->lr_msgid, 0, 0 );
1228         Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1229                 parentr->lr_res_errno,
1230                 parentr->lr_res_error ?  parentr->lr_res_error : "",
1231                 parentr->lr_res_matched ?  parentr->lr_res_matched : "" );
1232 }
1233
1234
1235
1236 int
1237 ldap_msgtype( LDAPMessage *lm )
1238 {
1239         assert( lm != NULL );
1240         return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1241 }
1242
1243
1244 int
1245 ldap_msgid( LDAPMessage *lm )
1246 {
1247         assert( lm != NULL );
1248
1249         return ( lm != NULL ) ? lm->lm_msgid : -1;
1250 }
1251
1252
1253 const char *
1254 ldap_int_msgtype2str( ber_tag_t tag )
1255 {
1256         switch( tag ) {
1257         case LDAP_RES_ADD: return "add";
1258         case LDAP_RES_BIND: return "bind";
1259         case LDAP_RES_COMPARE: return "compare";
1260         case LDAP_RES_DELETE: return "delete";
1261         case LDAP_RES_EXTENDED: return "extended-result";
1262         case LDAP_RES_INTERMEDIATE: return "intermediate";
1263         case LDAP_RES_MODIFY: return "modify";
1264         case LDAP_RES_RENAME: return "rename";
1265         case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1266         case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1267         case LDAP_RES_SEARCH_RESULT: return "search-result";
1268         }
1269         return "unknown";
1270 }
1271
1272 int
1273 ldap_msgfree( LDAPMessage *lm )
1274 {
1275         LDAPMessage     *next;
1276         int             type = 0;
1277
1278         Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1279
1280         for ( ; lm != NULL; lm = next ) {
1281                 next = lm->lm_chain;
1282                 type = lm->lm_msgtype;
1283                 ber_free( lm->lm_ber, 1 );
1284                 LDAP_FREE( (char *) lm );
1285         }
1286
1287         return type;
1288 }
1289
1290 /*
1291  * ldap_msgdelete - delete a message.  It returns:
1292  *      0       if the entire message was deleted
1293  *      -1      if the message was not found, or only part of it was found
1294  */
1295 int
1296 ldap_msgdelete( LDAP *ld, int msgid )
1297 {
1298         LDAPMessage     *lm, *prev;
1299         int             rc = 0;
1300
1301         assert( ld != NULL );
1302
1303         Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1304                 (void *)ld, msgid, 0 );
1305
1306 #ifdef LDAP_R_COMPILE
1307         ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1308 #endif
1309         prev = NULL;
1310         for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1311                 if ( lm->lm_msgid == msgid ) {
1312                         break;
1313                 }
1314                 prev = lm;
1315         }
1316
1317         if ( lm == NULL ) {
1318                 rc = -1;
1319
1320         } else {
1321                 if ( prev == NULL ) {
1322                         ld->ld_responses = lm->lm_next;
1323                 } else {
1324                         prev->lm_next = lm->lm_next;
1325                 }
1326         }
1327 #ifdef LDAP_R_COMPILE
1328         ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1329 #endif
1330         if ( lm ) {
1331                 switch ( ldap_msgfree( lm ) ) {
1332                 case LDAP_RES_SEARCH_ENTRY:
1333                 case LDAP_RES_SEARCH_REFERENCE:
1334                 case LDAP_RES_INTERMEDIATE:
1335                         rc = -1;
1336                         break;
1337
1338                 default:
1339                         break;
1340                 }
1341         }
1342
1343         return rc;
1344 }
1345
1346
1347 /*
1348  * ldap_abandoned
1349  *
1350  * return the location of the message id in the array of abandoned
1351  * message ids, or -1
1352  *
1353  * expects ld_res_mutex to be locked
1354  */
1355 static int
1356 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1357 {
1358 #ifdef LDAP_R_COMPILE
1359         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1360 #endif
1361
1362         assert( idxp != NULL );
1363         assert( msgid >= 0 );
1364         assert( ld->ld_nabandoned >= 0 );
1365
1366         return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1367 }
1368
1369 /*
1370  * ldap_mark_abandoned
1371  *
1372  * expects ld_res_mutex to be locked
1373  */
1374 static int
1375 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1376 {
1377 #ifdef LDAP_R_COMPILE
1378         LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1379 #endif
1380
1381         /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1382         assert( idx >= 0 );
1383         assert( idx < ld->ld_nabandoned );
1384         assert( ld->ld_abandoned[ idx ] == msgid );
1385
1386         return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
1387                 msgid, idx );
1388 }