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