1 /* result.c - wait for an ldap result */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2009 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
16 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
17 * All rights reserved.
19 /* This notice applies to changes, created by or for Novell, Inc.,
20 * to preexisting works for which notices appear elsewhere in this file.
22 * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
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.
33 * Modification to OpenLDAP source by Novell, Inc.
34 * April 2000 sfs Add code to process V3 referrals and search results
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.
43 * LDAPResult ::= SEQUENCE {
44 * resultCode ENUMERATED { ... },
46 * diagnosticMessage LDAPString,
47 * referral [3] Referral OPTIONAL
49 * Referral ::= SEQUENCE OF LDAPURL (one or more)
50 * LDAPURL ::= LDAPString (limited to URL chars)
57 #include <ac/stdlib.h>
60 #include <ac/socket.h>
61 #include <ac/string.h>
63 #include <ac/unistd.h>
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));
79 #define LDAP_MSG_X_KEEP_LOOKING (-2)
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
99 * ldap_result( s, msgid, all, timeout, result )
106 struct timeval *timeout,
107 LDAPMessage **result )
109 LDAPMessage *lm = NULL;
112 assert( ld != NULL );
113 assert( result != NULL );
115 Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
117 #ifdef LDAP_R_COMPILE
118 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
122 /* this is already done inside wait4msg(), right?... */
123 lm = chkResponseList( ld, msgid, all );
127 rc = wait4msg( ld, msgid, all, timeout, result );
131 ld->ld_errno = LDAP_SUCCESS;
135 #ifdef LDAP_R_COMPILE
136 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
148 LDAPMessage *lm, **lastlm, *nextlm;
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.
158 #ifdef LDAP_R_COMPILE
159 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
162 Debug( LDAP_DEBUG_TRACE,
163 "ldap_chkResponseList ld %p msgid %d all %d\n",
164 (void *)ld, msgid, all );
166 lastlm = &ld->ld_responses;
167 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
170 nextlm = lm->lm_next;
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 );
179 switch ( lm->lm_msgtype ) {
180 case LDAP_RES_SEARCH_ENTRY:
181 case LDAP_RES_SEARCH_REFERENCE:
182 case LDAP_RES_INTERMEDIATE:
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 );
192 /* Remove this entry from list */
200 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
203 if ( all == LDAP_MSG_ONE ||
204 all == LDAP_MSG_RECEIVED ||
205 msgid == LDAP_RES_UNSOLICITED )
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 )
224 lastlm = &lm->lm_next;
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;
234 lm->lm_chain_tail = NULL;
236 *lastlm = lm->lm_next;
243 Debug( LDAP_DEBUG_TRACE,
244 "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
246 Debug( LDAP_DEBUG_TRACE,
247 "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
248 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
260 struct timeval *timeout,
261 LDAPMessage **result )
264 struct timeval tv = { 0 },
266 start_time_tv = { 0 },
270 assert( ld != NULL );
271 assert( result != NULL );
273 #ifdef LDAP_R_COMPILE
274 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
277 if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
278 tv = ld->ld_options.ldo_tm_api;
283 if ( timeout == NULL ) {
284 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
285 (void *)ld, msgid, 0 );
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 );
290 #endif /* LDAP_DEBUG */
292 if ( timeout != NULL ) {
296 #ifdef HAVE_GETTIMEOFDAY
297 gettimeofday( &start_time_tv, NULL );
298 #else /* ! HAVE_GETTIMEOFDAY */
299 time( &start_time_tv.tv_sec );
300 start_time_tv.tv_usec = 0;
301 #endif /* ! HAVE_GETTIMEOFDAY */
304 rc = LDAP_MSG_X_KEEP_LOOKING;
305 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
307 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
308 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
309 (void *)ld, msgid, all );
310 #ifdef LDAP_R_COMPILE
311 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
313 ldap_dump_connection( ld, ld->ld_conns, 1 );
314 #ifdef LDAP_R_COMPILE
315 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
316 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
318 ldap_dump_requests_and_responses( ld );
319 #ifdef LDAP_R_COMPILE
320 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
323 #endif /* LDAP_DEBUG */
325 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
326 rc = (*result)->lm_msgtype;
331 #ifdef LDAP_R_COMPILE
332 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
334 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
335 if ( ber_sockbuf_ctrl( lc->lconn_sb,
336 LBER_SB_OPT_DATA_READY, NULL ) )
338 #ifdef LDAP_R_COMPILE
339 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
341 rc = try_read1msg( ld, msgid, all, &lc, result );
342 #ifdef LDAP_R_COMPILE
343 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
349 #ifdef LDAP_R_COMPILE
350 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
355 rc = ldap_int_select( ld, tvp );
359 Debug( LDAP_DEBUG_TRACE,
360 "ldap_int_select returned -1: errno %d\n",
365 if ( rc == 0 || ( rc == -1 && (
366 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
367 || err != EINTR ) ) )
369 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
375 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
378 rc = LDAP_MSG_X_KEEP_LOOKING;
379 #ifdef LDAP_R_COMPILE
380 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
382 if ( ld->ld_requests &&
383 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
384 ldap_is_write_ready( ld,
385 ld->ld_requests->lr_conn->lconn_sb ) )
387 ldap_int_flush_request( ld, ld->ld_requests );
389 #ifdef LDAP_R_COMPILE
390 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
391 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
393 for ( lc = ld->ld_conns;
394 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; )
396 if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
397 ldap_is_read_ready( ld, lc->lconn_sb ) )
399 #ifdef LDAP_R_COMPILE
400 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
402 rc = try_read1msg( ld, msgid, all, &lc, result );
403 #ifdef LDAP_R_COMPILE
404 ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
407 /* if lc gets free()'d,
408 * there's no guarantee
409 * lc->lconn_next is still
410 * sane; better restart
414 /* don't get to next conn! */
422 #ifdef LDAP_R_COMPILE
423 ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
429 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
430 struct timeval curr_time_tv = { 0 },
431 delta_time_tv = { 0 };
433 #ifdef HAVE_GETTIMEOFDAY
434 gettimeofday( &curr_time_tv, NULL );
435 #else /* ! HAVE_GETTIMEOFDAY */
436 time( &curr_time_tv.tv_sec );
437 curr_time_tv.tv_usec = 0;
438 #endif /* ! HAVE_GETTIMEOFDAY */
440 /* delta_time = tmp_time - start_time */
441 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
442 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
443 if ( delta_time_tv.tv_usec < 0 ) {
444 delta_time_tv.tv_sec--;
445 delta_time_tv.tv_usec += 1000000;
448 /* tv0 < delta_time ? */
449 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
450 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
452 rc = 0; /* timed out */
453 ld->ld_errno = LDAP_TIMEOUT;
457 /* tv0 -= delta_time */
458 tv0.tv_sec -= delta_time_tv.tv_sec;
459 tv0.tv_usec -= delta_time_tv.tv_usec;
460 if ( tv0.tv_usec < 0 ) {
462 tv0.tv_usec += 1000000;
465 tv.tv_sec = tv0.tv_sec;
466 tv.tv_usec = tv0.tv_usec;
468 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
469 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
471 start_time_tv.tv_sec = curr_time_tv.tv_sec;
472 start_time_tv.tv_usec = curr_time_tv.tv_usec;
486 LDAPMessage **result )
489 LDAPMessage *newmsg, *l, *prev;
495 LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
498 int rc, refer_cnt, hadref, simple_request, err;
501 #ifdef LDAP_CONNECTIONLESS
502 LDAPMessage *tmp = NULL, *chain_head = NULL;
503 int moremsgs = 0, isv2 = 0;
506 assert( ld != NULL );
507 assert( lcp != NULL );
508 assert( *lcp != NULL );
510 #ifdef LDAP_R_COMPILE
511 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
514 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
515 (void *)ld, msgid, all );
520 if ( lc->lconn_ber == NULL ) {
521 lc->lconn_ber = ldap_alloc_ber_with_options( ld );
523 if ( lc->lconn_ber == NULL ) {
529 assert( LBER_VALID (ber) );
531 /* get the next message */
533 #ifdef LDAP_CONNECTIONLESS
534 if ( LDAP_IS_UDP(ld) ) {
535 struct sockaddr from;
536 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
537 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
541 tag = ber_get_next( lc->lconn_sb, &len, ber );
543 case LDAP_TAG_MESSAGE:
545 * We read a complete message.
546 * The connection should no longer need this ber.
548 lc->lconn_ber = NULL;
554 Debug( LDAP_DEBUG_CONNS,
555 "ber_get_next failed.\n", 0, 0, 0 );
558 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
561 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
563 ld->ld_errno = LDAP_SERVER_DOWN;
564 #ifdef LDAP_R_COMPILE
565 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
567 ldap_free_connection( ld, lc, 1, 0 );
568 #ifdef LDAP_R_COMPILE
569 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
575 ld->ld_errno = LDAP_LOCAL_ERROR;
580 if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
582 ld->ld_errno = LDAP_DECODING_ERROR;
586 /* id == 0 iff unsolicited notification message (RFC 4511) */
588 /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
593 /* if it's been abandoned, toss it */
595 if ( ldap_abandoned( ld, id, &idx ) ) {
596 /* the message type */
597 tag = ber_peek_tag( ber, &len );
599 case LDAP_RES_SEARCH_ENTRY:
600 case LDAP_RES_SEARCH_REFERENCE:
601 case LDAP_RES_INTERMEDIATE:
606 /* there's no need to keep the id
607 * in the abandoned list any longer */
608 ldap_mark_abandoned( ld, id, idx );
612 Debug( LDAP_DEBUG_ANY,
613 "abandoned/discarded ld %p msgid %d message type %s\n",
614 (void *)ld, id, ldap_int_msgtype2str( tag ) );
618 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
621 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
624 lr = ldap_find_request_by_msgid( ld, id );
626 const char *msg = "unknown";
628 /* the message type */
629 tag = ber_peek_tag( ber, &len );
635 msg = ldap_int_msgtype2str( tag );
639 Debug( LDAP_DEBUG_ANY,
640 "no request for response on ld %p msgid %d message type %s (tossing)\n",
641 (void *)ld, id, msg );
646 #ifdef LDAP_CONNECTIONLESS
647 if ( LDAP_IS_UDP(ld) && isv2 ) {
648 ber_scanf(ber, "x{");
655 /* the message type */
656 tag = ber_peek_tag( ber, &len );
657 if ( tag == LBER_ERROR ) {
658 ld->ld_errno = LDAP_DECODING_ERROR;
663 Debug( LDAP_DEBUG_TRACE,
664 "read1msg: ld %p msgid %d message type %s\n",
665 (void *)ld, id, ldap_int_msgtype2str( tag ) );
668 /* unsolicited notification message (RFC 4511) */
669 if ( tag != LDAP_RES_EXTENDED ) {
673 /* strictly speaking, it's an error; from RFC 4511:
675 4.4. Unsolicited Notification
677 An unsolicited notification is an LDAPMessage sent from the server to
678 the client that is not in response to any LDAPMessage received by the
679 server. It is used to signal an extraordinary condition in the
680 server or in the LDAP session between the client and the server. The
681 notification is of an advisory nature, and the server will not expect
682 any response to be returned from the client.
684 The unsolicited notification is structured as an LDAPMessage in which
685 the messageID is zero and protocolOp is set to the extendedResp
686 choice using the ExtendedResponse type (See Section 4.12). The
687 responseName field of the ExtendedResponse always contains an LDAPOID
688 that is unique for this notification.
690 * however, since unsolicited responses
691 * are of advisory nature, better
696 ld->ld_errno = LDAP_DECODING_ERROR;
707 hadref = simple_request = 0;
708 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
709 lr->lr_res_msgtype = tag;
712 * Check for V3 search reference
714 if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
715 if ( ld->ld_version > LDAP_VERSION2 ) {
716 /* This is a V3 search reference */
717 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
718 lr->lr_parent != NULL )
723 /* Get the referral list */
724 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
725 rc = LDAP_DECODING_ERROR;
728 /* Note: refs array is freed by ldap_chase_v3referrals */
729 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
730 1, &lr->lr_res_error, &hadref );
731 if ( refer_cnt > 0 ) {
732 /* successfully chased reference */
733 /* If haven't got end search, set chasing referrals */
734 if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
735 lr->lr_status = LDAP_REQST_CHASINGREFS;
736 Debug( LDAP_DEBUG_TRACE,
737 "read1msg: search ref chased, "
738 "mark request chasing refs, "
740 lr->lr_msgid, 0, 0 );
747 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
748 /* All results that just return a status, i.e. don't return data
749 * go through the following code. This code also chases V2 referrals
750 * and checks if all referrals have been chased.
752 char *lr_res_error = NULL;
754 tmpber = *ber; /* struct copy */
755 if ( ber_scanf( &tmpber, "{eAA", &lderr,
756 &lr->lr_res_matched, &lr_res_error )
759 if ( lr_res_error != NULL ) {
760 if ( lr->lr_res_error != NULL ) {
761 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
762 LDAP_FREE( (char *)lr_res_error );
765 lr->lr_res_error = lr_res_error;
770 /* Do we need to check for referrals? */
771 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
772 lr->lr_parent != NULL )
777 /* Check if V3 referral */
778 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
779 if ( ld->ld_version > LDAP_VERSION2 ) {
780 /* Get the referral list */
781 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
782 rc = LDAP_DECODING_ERROR;
783 lr->lr_status = LDAP_REQST_COMPLETED;
784 Debug( LDAP_DEBUG_TRACE,
785 "read1msg: referral decode error, "
786 "mark request completed, ld %p msgid %d\n",
787 (void *)ld, lr->lr_msgid, 0 );
790 /* Chase the referral
791 * refs array is freed by ldap_chase_v3referrals
793 refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
794 0, &lr->lr_res_error, &hadref );
795 lr->lr_status = LDAP_REQST_COMPLETED;
796 Debug( LDAP_DEBUG_TRACE,
797 "read1msg: referral %s chased, "
798 "mark request completed, ld %p msgid %d\n",
799 refer_cnt > 0 ? "" : "not",
800 (void *)ld, lr->lr_msgid);
801 if ( refer_cnt < 0 ) {
809 case LDAP_COMPARE_TRUE:
810 case LDAP_COMPARE_FALSE:
814 if ( lr->lr_res_error == NULL ) {
818 /* pedantic, should never happen */
819 if ( lr->lr_res_error[ 0 ] == '\0' ) {
820 LDAP_FREE( lr->lr_res_error );
821 lr->lr_res_error = NULL;
825 /* V2 referrals are in error string */
826 refer_cnt = ldap_chase_referrals( ld, lr,
827 &lr->lr_res_error, -1, &hadref );
828 lr->lr_status = LDAP_REQST_COMPLETED;
829 Debug( LDAP_DEBUG_TRACE,
830 "read1msg: V2 referral chased, "
831 "mark request completed, id = %d\n",
832 lr->lr_msgid, 0, 0 );
838 /* save errno, message, and matched string */
839 if ( !hadref || lr->lr_res_error == NULL ) {
841 lderr == LDAP_PARTIAL_RESULTS
842 ? LDAP_SUCCESS : lderr;
844 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
845 lr->lr_res_errno = ld->ld_errno;
848 lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
852 /* in any case, don't leave any lr_res_error 'round */
853 if ( lr_res_error ) {
854 LDAP_FREE( lr_res_error );
857 Debug( LDAP_DEBUG_TRACE,
858 "read1msg: ld %p %d new referrals\n",
859 (void *)ld, refer_cnt, 0 );
861 if ( refer_cnt != 0 ) { /* chasing referrals */
864 if ( refer_cnt < 0 ) {
865 ldap_return_request( ld, lr, 0 );
866 return( -1 ); /* fatal error */
868 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
871 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
872 /* request without any referrals */
873 simple_request = ( hadref ? 0 : 1 );
876 /* request with referrals or child request */
881 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
882 Debug( LDAP_DEBUG_TRACE,
883 "read1msg: mark request completed, ld %p msgid %d\n",
884 (void *)ld, lr->lr_msgid, 0);
885 while ( lr->lr_parent != NULL ) {
886 merge_error_info( ld, lr->lr_parent, lr );
889 if ( --lr->lr_outrefcnt > 0 ) {
890 break; /* not completely done yet */
894 /* Check if all requests are finished, lr is now parent */
896 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
897 for ( tmplr = lr->lr_child;
899 tmplr = tmplr->lr_refnext )
901 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
905 /* This is the parent request if the request has referrals */
906 if ( lr->lr_outrefcnt <= 0 &&
907 lr->lr_parent == NULL &&
911 tag = lr->lr_res_msgtype;
912 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
914 Debug( LDAP_DEBUG_TRACE,
915 "res_errno: %d, res_error: <%s>, "
916 "res_matched: <%s>\n",
918 lr->lr_res_error ? lr->lr_res_error : "",
919 lr->lr_res_matched ? lr->lr_res_matched : "" );
920 if ( !simple_request ) {
923 if ( build_result_ber( ld, &ber, lr )
926 rc = -1; /* fatal error */
930 if ( lr != &dummy_lr ) {
931 ldap_return_request( ld, lr, 1 );
937 * RF 4511 unsolicited (id == 0) responses
938 * shouldn't necessarily end the connection
940 if ( lc != NULL && id != 0 ) {
941 #ifdef LDAP_R_COMPILE
942 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
944 ldap_free_connection( ld, lc, 0, 1 );
945 #ifdef LDAP_R_COMPILE
946 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
954 if ( lr != &dummy_lr ) {
955 ldap_return_request( ld, lr, 0 );
964 /* try to handle unsolicited responses as appropriate */
965 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
968 tag = ber_peek_tag( &tmpber, &len );
970 /* we have a res oid */
971 if ( tag == LDAP_TAG_EXOP_RES_OID ) {
972 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
973 struct berval resoid = BER_BVNULL;
975 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
976 ld->ld_errno = LDAP_DECODING_ERROR;
981 assert( !BER_BVISEMPTY( &resoid ) );
983 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
985 tag = ber_peek_tag( &tmpber, &len );
988 #if 0 /* don't need right now */
989 /* we have res data */
990 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
991 struct berval resdata;
993 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
994 ld->ld_errno = LDAP_DECODING_ERROR;
1003 /* handle RFC 4511 "Notice of Disconnection" locally */
1006 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
1007 ld->ld_errno = LDAP_DECODING_ERROR;
1012 /* get rid of the connection... */
1014 #ifdef LDAP_R_COMPILE
1015 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
1017 ldap_free_connection( ld, lc, 0, 1 );
1018 #ifdef LDAP_R_COMPILE
1019 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
1024 /* need to return -1, because otherwise
1025 * a valid result is expected */
1030 /* make a new ldap message */
1031 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
1032 if ( newmsg == NULL ) {
1033 ld->ld_errno = LDAP_NO_MEMORY;
1036 newmsg->lm_msgid = (int)id;
1037 newmsg->lm_msgtype = tag;
1038 newmsg->lm_ber = ber;
1039 newmsg->lm_chain_tail = newmsg;
1041 #ifdef LDAP_CONNECTIONLESS
1042 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1043 * the responses are all a sequence wrapped in one message. In
1044 * LDAPv3 each response is in its own message. The datagram must
1045 * end with a SearchResult. We can't just parse each response in
1046 * separate calls to try_read1msg because the header info is only
1047 * present at the beginning of the datagram, not at the beginning
1048 * of each response. So parse all the responses at once and queue
1049 * them up, then pull off the first response to return to the
1050 * caller when all parsing is complete.
1052 if ( LDAP_IS_UDP(ld) ) {
1053 /* If not a result, look for more */
1054 if ( tag != LDAP_RES_SEARCH_RESULT ) {
1058 /* LDAPv2: dup the current ber, skip past the current
1059 * response, and see if there are any more after it.
1061 ber = ber_dup( ber );
1062 ber_scanf( ber, "x" );
1063 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1064 /* There's more - dup the ber buffer so they can all be
1065 * individually freed by ldap_msgfree.
1068 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1069 bv.bv_val = LDAP_MALLOC( len );
1072 ber_read( ber, bv.bv_val, len );
1074 ber_init2( ber, &bv, ld->ld_lberoptions );
1078 /* LDAPv3: Just allocate a new ber. Since this is a buffered
1079 * datagram, if the sockbuf is readable we still have data
1082 ber = ldap_alloc_ber_with_options( ld );
1083 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1085 /* set up response chain */
1086 if ( tmp == NULL ) {
1087 newmsg->lm_next = ld->ld_responses;
1088 ld->ld_responses = newmsg;
1089 chain_head = newmsg;
1091 tmp->lm_chain = newmsg;
1093 chain_head->lm_chain_tail = newmsg;
1095 /* "ok" means there's more to parse */
1104 /* got to end of datagram without a SearchResult. Free
1105 * our dup'd ber, but leave any buffer alone. For v2 case,
1106 * the previous response is still using this buffer. For v3,
1107 * the new ber has no buffer to free yet.
1112 } else if ( moremsgs ) {
1113 /* got search result, and we had multiple responses in 1 datagram.
1114 * stick the result onto the end of the chain, and then pull the
1115 * first response off the head of the chain.
1117 tmp->lm_chain = newmsg;
1118 chain_head->lm_chain_tail = newmsg;
1119 *result = chkResponseList( ld, msgid, all );
1120 ld->ld_errno = LDAP_SUCCESS;
1121 return( (*result)->lm_msgtype );
1124 #endif /* LDAP_CONNECTIONLESS */
1126 /* is this the one we're looking for? */
1127 if ( msgid == LDAP_RES_ANY || id == msgid ) {
1128 if ( all == LDAP_MSG_ONE
1129 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1130 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1131 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1132 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1135 ld->ld_errno = LDAP_SUCCESS;
1138 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1139 foundit = 1; /* return the chain later */
1144 * if not, we must add it to the list of responses. if
1145 * the msgid is already there, it must be part of an existing
1150 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1151 if ( l->lm_msgid == newmsg->lm_msgid ) {
1157 /* not part of an existing search response */
1164 newmsg->lm_next = ld->ld_responses;
1165 ld->ld_responses = newmsg;
1169 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1170 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1172 /* part of a search response - add to end of list of entries */
1173 l->lm_chain_tail->lm_chain = newmsg;
1174 l->lm_chain_tail = newmsg;
1176 /* return the whole chain if that's what we were looking for */
1178 if ( prev == NULL ) {
1179 ld->ld_responses = l->lm_next;
1181 prev->lm_next = l->lm_next;
1188 ld->ld_errno = LDAP_SUCCESS;
1191 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1194 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1199 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1207 ber = ldap_alloc_ber_with_options( ld );
1210 ld->ld_errno = LDAP_NO_MEMORY;
1214 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1215 lr->lr_res_msgtype, lr->lr_res_errno,
1216 lr->lr_res_matched ? lr->lr_res_matched : "",
1217 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1219 ld->ld_errno = LDAP_ENCODING_ERROR;
1221 return( LBER_ERROR );
1224 ber_reset( ber, 1 );
1226 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1227 ld->ld_errno = LDAP_DECODING_ERROR;
1229 return( LBER_ERROR );
1232 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1233 ld->ld_errno = LDAP_DECODING_ERROR;
1235 return( LBER_ERROR );
1238 tag = ber_peek_tag( ber, &len );
1240 if ( tag == LBER_ERROR ) {
1241 ld->ld_errno = LDAP_DECODING_ERROR;
1243 return( LBER_ERROR );
1252 * Merge error information in "lr" with "parentr" error code and string.
1255 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1257 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1258 parentr->lr_res_errno = lr->lr_res_errno;
1259 if ( lr->lr_res_error != NULL ) {
1260 (void)ldap_append_referral( ld, &parentr->lr_res_error,
1264 } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1265 parentr->lr_res_errno == LDAP_SUCCESS )
1267 parentr->lr_res_errno = lr->lr_res_errno;
1268 if ( parentr->lr_res_error != NULL ) {
1269 LDAP_FREE( parentr->lr_res_error );
1271 parentr->lr_res_error = lr->lr_res_error;
1272 lr->lr_res_error = NULL;
1273 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1274 if ( parentr->lr_res_matched != NULL ) {
1275 LDAP_FREE( parentr->lr_res_matched );
1277 parentr->lr_res_matched = lr->lr_res_matched;
1278 lr->lr_res_matched = NULL;
1282 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1283 parentr->lr_msgid, 0, 0 );
1284 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1285 parentr->lr_res_errno,
1286 parentr->lr_res_error ? parentr->lr_res_error : "",
1287 parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1293 ldap_msgtype( LDAPMessage *lm )
1295 assert( lm != NULL );
1296 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1301 ldap_msgid( LDAPMessage *lm )
1303 assert( lm != NULL );
1305 return ( lm != NULL ) ? lm->lm_msgid : -1;
1310 ldap_int_msgtype2str( ber_tag_t tag )
1313 case LDAP_RES_ADD: return "add";
1314 case LDAP_RES_BIND: return "bind";
1315 case LDAP_RES_COMPARE: return "compare";
1316 case LDAP_RES_DELETE: return "delete";
1317 case LDAP_RES_EXTENDED: return "extended-result";
1318 case LDAP_RES_INTERMEDIATE: return "intermediate";
1319 case LDAP_RES_MODIFY: return "modify";
1320 case LDAP_RES_RENAME: return "rename";
1321 case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1322 case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1323 case LDAP_RES_SEARCH_RESULT: return "search-result";
1329 ldap_msgfree( LDAPMessage *lm )
1334 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1336 for ( ; lm != NULL; lm = next ) {
1337 next = lm->lm_chain;
1338 type = lm->lm_msgtype;
1339 ber_free( lm->lm_ber, 1 );
1340 LDAP_FREE( (char *) lm );
1347 * ldap_msgdelete - delete a message. It returns:
1348 * 0 if the entire message was deleted
1349 * -1 if the message was not found, or only part of it was found
1352 ldap_msgdelete( LDAP *ld, int msgid )
1354 LDAPMessage *lm, *prev;
1357 assert( ld != NULL );
1359 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1360 (void *)ld, msgid, 0 );
1362 #ifdef LDAP_R_COMPILE
1363 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1366 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1367 if ( lm->lm_msgid == msgid ) {
1377 if ( prev == NULL ) {
1378 ld->ld_responses = lm->lm_next;
1380 prev->lm_next = lm->lm_next;
1383 #ifdef LDAP_R_COMPILE
1384 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1387 switch ( ldap_msgfree( lm ) ) {
1388 case LDAP_RES_SEARCH_ENTRY:
1389 case LDAP_RES_SEARCH_REFERENCE:
1390 case LDAP_RES_INTERMEDIATE:
1406 * return the location of the message id in the array of abandoned
1407 * message ids, or -1
1409 * expects ld_res_mutex to be locked
1412 ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1414 #ifdef LDAP_R_COMPILE
1415 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1418 assert( idxp != NULL );
1419 assert( msgid >= 0 );
1420 assert( ld->ld_nabandoned >= 0 );
1422 return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1426 * ldap_mark_abandoned
1428 * expects ld_res_mutex to be locked
1431 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1433 #ifdef LDAP_R_COMPILE
1434 LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1437 /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1439 assert( (unsigned) idx < ld->ld_nabandoned );
1440 assert( ld->ld_abandoned[ idx ] == msgid );
1442 return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,