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