Newer
Older
/* result.c - wait for an ldap result */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
/* Portions Copyright (c) 1990 Regents of the University of Michigan.
* All rights reserved.
/* This notice applies to changes, created by or for Novell, Inc.,
* to preexisting works for which notices appear elsewhere in this file.
*
* Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
*
* THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
* USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
* 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
* HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
* TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
* WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
* LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
* PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
* Modification to OpenLDAP source by Novell, Inc.
* April 2000 sfs Add code to process V3 referrals and search results
*---
* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
* can be found in the file "build/LICENSE-2.0.1" in this distribution
* of OpenLDAP Software.
*/
* resultCode ENUMERATED { ... },
* matchedDN LDAPDN,
Pierangelo Masarati
committed
* diagnosticMessage LDAPString,
* }
* Referral ::= SEQUENCE OF LDAPURL (one or more)
Pierangelo Masarati
committed
* LDAPURL ::= LDAPString (limited to URL chars)
#include <ac/errno.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>
#include <ac/unistd.h>
static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int *idx ));
static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int idx ));
static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
/*
* ldap_result - wait for an ldap result response to a message from the
* ldap server. If msgid is LDAP_RES_ANY (-1), any message will be
* accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
* message is accepted. Otherwise ldap_result will wait for a response
* with msgid. If all is LDAP_MSG_ONE (0) the first message with id
* msgid will be accepted, otherwise, ldap_result will wait for all
* responses with id msgid and then return a pointer to the entire list
* of messages. In general, this is only useful for search responses,
* which can be of three message types (zero or more entries, zero or
* search references, followed by an ldap result). An extension to
* LDAPv3 allows partial extended responses to be returned in response
* to any request. The type of the first message received is returned.
Pierangelo Masarati
committed
* When waiting, any messages that have been abandoned/discarded are
* discarded.
*
* Example:
* ldap_result( s, msgid, all, timeout, result )
*/
int
ldap_result(
LDAP *ld,
int msgid,
int all,
struct timeval *timeout,
assert( ld != NULL );
assert( result != NULL );
Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
#endif
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
#endif
return rc;
chkResponseList(
LDAP *ld,
int msgid,
int all)
LDAPMessage *lm, **lastlm, *nextlm;
* Look through the list of responses we have received on
* this association and see if the response we're interested in
* is there. If it is, return it. If not, call wait4msg() to
* wait until it arrives or timeout occurs.
*/
#ifdef LDAP_R_COMPILE
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
#endif
"ldap_chkResponseList ld %p msgid %d all %d\n",
lastlm = &ld->ld_responses;
for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
Debug( LDAP_DEBUG_ANY,
"response list msg abandoned, "
"msgid %d message type %s\n",
lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
switch ( lm->lm_msgtype ) {
case LDAP_RES_SEARCH_ENTRY:
case LDAP_RES_SEARCH_REFERENCE:
case LDAP_RES_INTERMEDIATE:
break;
default:
/* there's no need to keep the id
* in the abandoned list any longer */
ldap_mark_abandoned( ld, lm->lm_msgid, idx );
break;
}
/* Remove this entry from list */
*lastlm = nextlm;
ldap_msgfree( lm );
continue;
}
if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
LDAPMessage *tmp;
if ( all == LDAP_MSG_ONE ||
all == LDAP_MSG_RECEIVED ||
msgid == LDAP_RES_UNSOLICITED )
{
tmp = lm->lm_chain_tail;
if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
lastlm = &lm->lm_next;
if ( lm != NULL ) {
/* Found an entry, remove it from the list */
if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
*lastlm = lm->lm_chain;
lm->lm_chain->lm_next = lm->lm_next;
lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
lm->lm_chain = NULL;
lm->lm_chain_tail = NULL;
*lastlm = lm->lm_next;
}
lm->lm_next = NULL;
}
#ifdef LDAP_DEBUG
if ( lm == NULL) {
"ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
"ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
(void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
wait4msg(
LDAP *ld,
ber_int_t msgid,
int all,
struct timeval *timeout,
start_time_tv = { 0 },
*tvp = NULL;
Pierangelo Masarati
committed
LDAPConn *lc;
assert( ld != NULL );
assert( result != NULL );
#ifdef LDAP_R_COMPILE
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
#endif
if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
tv = ld->ld_options.ldo_tm_api;
timeout = &tv;
}
Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
(void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
#ifdef HAVE_GETTIMEOFDAY
gettimeofday( &start_time_tv, NULL );
#else /* ! HAVE_GETTIMEOFDAY */
time( &start_time_tv.tv_sec );
start_time_tv.tv_usec = 0;
#endif /* ! HAVE_GETTIMEOFDAY */
rc = LDAP_MSG_X_KEEP_LOOKING;
while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
#ifdef LDAP_DEBUG
if ( ldap_debug & LDAP_DEBUG_TRACE ) {
Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
Pierangelo Masarati
committed
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
#endif
ldap_dump_connection( ld, ld->ld_conns, 1 );
Pierangelo Masarati
committed
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
#endif
ldap_dump_requests_and_responses( ld );
Pierangelo Masarati
committed
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
#endif
if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
rc = (*result)->lm_msgtype;
} else {
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
#endif
Pierangelo Masarati
committed
for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
if ( ber_sockbuf_ctrl( lc->lconn_sb,
}
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
#endif
Debug( LDAP_DEBUG_TRACE,
"ldap_int_select returned -1: errno %d\n",
if ( rc == 0 || ( rc == -1 && (
!LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
{
ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
LDAP_TIMEOUT);
return( rc );
}
if ( rc == -1 ) {
rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
lc_ready = 1;
}
}
if ( lc_ready ) {
LDAPConn *lnext;
rc = LDAP_MSG_X_KEEP_LOOKING;
#ifdef LDAP_R_COMPILE
#endif
if ( ld->ld_requests &&
ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
ldap_is_write_ready( ld,
ld->ld_requests->lr_conn->lconn_sb ) )
{
ldap_int_flush_request( ld, ld->ld_requests );
}
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
#endif
for ( lc = ld->ld_conns;
rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
lc = lnext )
{
if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
ldap_is_read_ready( ld, lc->lconn_sb ) )
/* Don't let it get freed out from under us */
++lc->lconn_refcnt;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
#endif
rc = try_read1msg( ld, msgid, all, lc, result );
lnext = lc->lconn_next;
/* Only take locks if we're really freeing */
if ( lc->lconn_refcnt <= 1 ) {
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
#endif
struct timeval curr_time_tv = { 0 },
delta_time_tv = { 0 };
#ifdef HAVE_GETTIMEOFDAY
gettimeofday( &curr_time_tv, NULL );
#else /* ! HAVE_GETTIMEOFDAY */
time( &curr_time_tv.tv_sec );
curr_time_tv.tv_usec = 0;
#endif /* ! HAVE_GETTIMEOFDAY */
/* delta_time = tmp_time - start_time */
delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
if ( delta_time_tv.tv_usec < 0 ) {
delta_time_tv.tv_sec--;
delta_time_tv.tv_usec += 1000000;
}
/* tv0 < delta_time ? */
if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
{
rc = 0; /* timed out */
/* tv0 -= delta_time */
tv0.tv_sec -= delta_time_tv.tv_sec;
tv0.tv_usec -= delta_time_tv.tv_usec;
if ( tv0.tv_usec < 0 ) {
tv0.tv_sec--;
tv0.tv_usec += 1000000;
}
tv.tv_sec = tv0.tv_sec;
tv.tv_usec = tv0.tv_usec;
Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
(void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
start_time_tv.tv_sec = curr_time_tv.tv_sec;
start_time_tv.tv_usec = curr_time_tv.tv_usec;
static ber_tag_t
try_read1msg(
LDAP *ld,
ber_int_t msgid,
int all,
BerElement *ber;
Pierangelo Masarati
committed
LDAPMessage *newmsg, *l, *prev;
ber_tag_t tag;
ber_len_t len;
Pierangelo Masarati
committed
LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
Pierangelo Masarati
committed
#ifdef LDAP_CONNECTIONLESS
Pierangelo Masarati
committed
LDAPMessage *tmp = NULL, *chain_head = NULL;
int moremsgs = 0, isv2 = 0;
#endif
Pierangelo Masarati
committed
#ifdef LDAP_R_COMPILE
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
#endif
Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
(void *)ld, msgid, all );
retry:
if ( lc->lconn_ber == NULL ) {
return -1;
}
}
ber = lc->lconn_ber;
sock_errset(0);
#ifdef LDAP_CONNECTIONLESS
if ( LDAP_IS_UDP(ld) ) {
struct sockaddr from;
ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
nextresp3:
tag = ber_get_next( lc->lconn_sb, &len, ber );
/*
* We read a complete message.
* The connection should no longer need this ber.
*/
lc->lconn_ber = NULL;
#ifdef LDAP_DEBUG
Debug( LDAP_DEBUG_CONNS,
"ber_get_next failed.\n", 0, 0, 0 );
#endif
#ifdef EWOULDBLOCK
if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
#endif
#ifdef EAGAIN
#endif
ld->ld_errno = LDAP_LOCAL_ERROR;
return -1;
if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
ber_free( ber, 1 );
ld->ld_errno = LDAP_DECODING_ERROR;
return( -1 );
}
Pierangelo Masarati
committed
/* id == 0 iff unsolicited notification message (RFC 4511) */
/* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
if ( id < 0 ) {
goto retry_ber;
}
Pierangelo Masarati
committed
if ( id > 0 ) {
if ( ldap_abandoned( ld, id, &idx ) ) {
/* the message type */
tag = ber_peek_tag( ber, &len );
switch ( tag ) {
case LDAP_RES_SEARCH_ENTRY:
case LDAP_RES_SEARCH_REFERENCE:
case LDAP_RES_INTERMEDIATE:
case LBER_ERROR:
break;
Pierangelo Masarati
committed
default:
/* there's no need to keep the id
* in the abandoned list any longer */
ldap_mark_abandoned( ld, id, idx );
break;
}
Pierangelo Masarati
committed
Debug( LDAP_DEBUG_ANY,
"abandoned/discarded ld %p msgid %d message type %s\n",
(void *)ld, id, ldap_int_msgtype2str( tag ) );
retry_ber:
Pierangelo Masarati
committed
ber_free( ber, 1 );
if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
goto retry;
}
return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
Pierangelo Masarati
committed
lr = ldap_find_request_by_msgid( ld, id );
if ( lr == NULL ) {
const char *msg = "unknown";
Pierangelo Masarati
committed
/* the message type */
tag = ber_peek_tag( ber, &len );
switch ( tag ) {
case LBER_ERROR:
break;
Pierangelo Masarati
committed
default:
msg = ldap_int_msgtype2str( tag );
break;
}
Pierangelo Masarati
committed
Debug( LDAP_DEBUG_ANY,
"no request for response on ld %p msgid %d message type %s (tossing)\n",
(void *)ld, id, msg );
Pierangelo Masarati
committed
goto retry_ber;
}
#ifdef LDAP_CONNECTIONLESS
Pierangelo Masarati
committed
if ( LDAP_IS_UDP(ld) && isv2 ) {
ber_scanf(ber, "x{");
}
Pierangelo Masarati
committed
}
tag = ber_peek_tag( ber, &len );
if ( tag == LBER_ERROR ) {
ber_free( ber, 1 );
Debug( LDAP_DEBUG_TRACE,
"read1msg: ld %p msgid %d message type %s\n",
(void *)ld, id, ldap_int_msgtype2str( tag ) );
Pierangelo Masarati
committed
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
if ( id == 0 ) {
/* unsolicited notification message (RFC 4511) */
if ( tag != LDAP_RES_EXTENDED ) {
/* toss it */
goto retry_ber;
/* strictly speaking, it's an error; from RFC 4511:
4.4. Unsolicited Notification
An unsolicited notification is an LDAPMessage sent from the server to
the client that is not in response to any LDAPMessage received by the
server. It is used to signal an extraordinary condition in the
server or in the LDAP session between the client and the server. The
notification is of an advisory nature, and the server will not expect
any response to be returned from the client.
The unsolicited notification is structured as an LDAPMessage in which
the messageID is zero and protocolOp is set to the extendedResp
choice using the ExtendedResponse type (See Section 4.12). The
responseName field of the ExtendedResponse always contains an LDAPOID
that is unique for this notification.
* however, since unsolicited responses
* are of advisory nature, better
* toss it, right now
*/
#if 0
ld->ld_errno = LDAP_DECODING_ERROR;
ber_free( ber, 1 );
return( -1 );
#endif
}
lr = &dummy_lr;
}
id = lr->lr_origid;
rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
if ( ld->ld_version > LDAP_VERSION2 ) {
/* This is a V3 search reference */
if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
lr->lr_parent != NULL )
char **refs = NULL;
tmpber = *ber;
/* Get the referral list */
if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
rc = LDAP_DECODING_ERROR;
/* Note: refs array is freed by ldap_chase_v3referrals */
refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
if ( refer_cnt > 0 ) {
/* successfully chased reference */
/* If haven't got end search, set chasing referrals */
lr->lr_status = LDAP_REQST_CHASINGREFS;
Debug( LDAP_DEBUG_TRACE,
"read1msg: search ref chased, "
"mark request chasing refs, "
"id = %d\n",
Pierangelo Masarati
committed
} else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
/* All results that just return a status, i.e. don't return data
* go through the following code. This code also chases V2 referrals
* and checks if all referrals have been chased.
*/
tmpber = *ber; /* struct copy */
if ( ber_scanf( &tmpber, "{eAA", &lderr,
&lr->lr_res_matched, &lr_res_error )
!= LBER_ERROR )
{
if ( lr_res_error != NULL ) {
if ( lr->lr_res_error != NULL ) {
(void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
LDAP_FREE( (char *)lr_res_error );
} else {
lr->lr_res_error = lr_res_error;
lr_res_error = NULL;
}
/* Do we need to check for referrals? */
if ( tag != LDAP_RES_BIND &&
( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
lr->lr_parent != NULL ))
{
char **refs = NULL;
ber_len_t len;
/* Check if V3 referral */
if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
if ( ld->ld_version > LDAP_VERSION2 ) {
/* Get the referral list */
if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
rc = LDAP_DECODING_ERROR;
lr->lr_status = LDAP_REQST_COMPLETED;
Debug( LDAP_DEBUG_TRACE,
"read1msg: referral decode error, "
"mark request completed, ld %p msgid %d\n",
(void *)ld, lr->lr_msgid, 0 );
} else {
/* Chase the referral
* refs array is freed by ldap_chase_v3referrals
*/
refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
lr->lr_status = LDAP_REQST_COMPLETED;
Debug( LDAP_DEBUG_TRACE,
"read1msg: referral %s chased, "
"mark request completed, ld %p msgid %d\n",
(void *)ld, lr->lr_msgid);
} else {
switch ( lderr ) {
case LDAP_SUCCESS:
case LDAP_COMPARE_TRUE:
case LDAP_COMPARE_FALSE:
break;
Pierangelo Masarati
committed
if ( lr->lr_res_error == NULL ) {
Pierangelo Masarati
committed
/* pedantic, should never happen */
if ( lr->lr_res_error[ 0 ] == '\0' ) {
LDAP_FREE( lr->lr_res_error );
lr->lr_res_error = NULL;
break;
}
/* V2 referrals are in error string */
refer_cnt = ldap_chase_referrals( ld, lr,
&lr->lr_res_error, -1, &hadref );
lr->lr_status = LDAP_REQST_COMPLETED;
Debug( LDAP_DEBUG_TRACE,
"read1msg: V2 referral chased, "
"mark request completed, id = %d\n",
lr->lr_msgid, 0, 0 );
break;
}
/* save errno, message, and matched string */
if ( !hadref || lr->lr_res_error == NULL ) {
lr->lr_res_errno =
lderr == LDAP_PARTIAL_RESULTS
? LDAP_SUCCESS : lderr;
} else if ( ld->ld_errno != LDAP_SUCCESS ) {
lr->lr_res_errno = ld->ld_errno;
} else {
lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
/* in any case, don't leave any lr_res_error 'round */
if ( lr_res_error ) {
LDAP_FREE( lr_res_error );
"read1msg: ld %p %d new referrals\n",
(void *)ld, refer_cnt, 0 );
ber_free( ber, 1 );
ber = NULL;
lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
if ( lr->lr_res_matched ) {
LDAP_FREE( lr->lr_res_matched );
lr->lr_res_matched = NULL;
}
} else {
if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
/* request without any referrals */
simple_request = ( hadref ? 0 : 1 );
} else {
/* request with referrals or child request */
ber_free( ber, 1 );
ber = NULL;
lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
Debug( LDAP_DEBUG_TRACE,
"read1msg: mark request completed, ld %p msgid %d\n",
(void *)ld, lr->lr_msgid, 0);
while ( lr->lr_parent != NULL ) {
merge_error_info( ld, lr->lr_parent, lr );
lr = lr->lr_parent;
if ( --lr->lr_outrefcnt > 0 ) {
break; /* not completely done yet */
}
}
/* Check if all requests are finished, lr is now parent */
}
}
/* This is the parent request if the request has referrals */
if ( lr->lr_outrefcnt <= 0 &&
lr->lr_parent == NULL &&
Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
(void *)ld, id, 0 );
Debug( LDAP_DEBUG_TRACE,
"res_errno: %d, res_error: <%s>, "
"res_matched: <%s>\n",
lr->lr_res_errno,
lr->lr_res_error ? lr->lr_res_error : "",
lr->lr_res_matched ? lr->lr_res_matched : "" );
ber_free( ber, 1 );
ber = NULL;
Kurt Zeilenga
committed
if ( build_result_ber( ld, &ber, lr )
Pierangelo Masarati
committed
if ( lr != &dummy_lr ) {
ldap_return_request( ld, lr, 1 );
}
/*
* RF 4511 unsolicited (id == 0) responses
* shouldn't necessarily end the connection
*/
if ( lc != NULL && id != 0 ) {
Pierangelo Masarati
committed
if ( lr != &dummy_lr ) {
ldap_return_request( ld, lr, 0 );
}
if ( ber == NULL ) {
Pierangelo Masarati
committed
/* try to handle unsolicited responses as appropriate */
if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
Pierangelo Masarati
committed
int is_nod = 0;
Pierangelo Masarati
committed
/* we have a res oid */
if ( tag == LDAP_TAG_EXOP_RES_OID ) {
static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
struct berval resoid = BER_BVNULL;
Pierangelo Masarati
committed
if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
Pierangelo Masarati
committed
ld->ld_errno = LDAP_DECODING_ERROR;
ber_free( ber, 1 );
return -1;
}
assert( !BER_BVISEMPTY( &resoid ) );
Pierangelo Masarati
committed
is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
Pierangelo Masarati
committed
Pierangelo Masarati
committed
}
#if 0 /* don't need right now */
/* we have res data */
Pierangelo Masarati
committed
if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
ld->ld_errno = LDAP_DECODING_ERROR;
ber_free( ber, 0 );
return ld->ld_errno;
}
/* use it... */
Pierangelo Masarati
committed
}
Pierangelo Masarati
committed
/* handle RFC 4511 "Notice of Disconnection" locally */
if ( is_nod ) {
if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
ld->ld_errno = LDAP_DECODING_ERROR;
ber_free( ber, 1 );
return -1;
}
/* get rid of the connection... */
if ( lc != NULL ) {
Pierangelo Masarati
committed
}
/* need to return -1, because otherwise
* a valid result is expected */
return -1;