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