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