mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
1459 lines
38 KiB
C
1459 lines
38 KiB
C
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is Mozilla Communicator client code, released
|
||
|
* March 31, 1998.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1998-1999
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
/*
|
||
|
* Copyright (c) 1990 Regents of the University of Michigan.
|
||
|
* All rights reserved.
|
||
|
*/
|
||
|
/*
|
||
|
* result.c - wait for an ldap result
|
||
|
*/
|
||
|
|
||
|
#if 0
|
||
|
#ifndef lint
|
||
|
static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#include "ldap-int.h"
|
||
|
|
||
|
static int check_response_queue( LDAP *ld, int msgid, int all,
|
||
|
int do_abandon_check, LDAPMessage **result );
|
||
|
static int ldap_abandoned( LDAP *ld, int msgid );
|
||
|
static int ldap_mark_abandoned( LDAP *ld, int msgid );
|
||
|
static int wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted,
|
||
|
struct timeval *timeout, LDAPMessage **result );
|
||
|
static int read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc,
|
||
|
LDAPMessage **result );
|
||
|
static void check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber,
|
||
|
int ldapversion, int *totalcountp, int *chasingcountp );
|
||
|
static int build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr );
|
||
|
static void merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr );
|
||
|
#if defined( CLDAP )
|
||
|
static int cldap_select1( LDAP *ld, struct timeval *timeout );
|
||
|
#endif
|
||
|
static void link_pend( LDAP *ld, LDAPPend *lp );
|
||
|
#if 0 /* these functions are no longer used */
|
||
|
static void unlink_pend( LDAP *ld, LDAPPend *lp );
|
||
|
static int unlink_msg( LDAP *ld, int msgid, int all );
|
||
|
#endif /* 0 */
|
||
|
|
||
|
/*
|
||
|
* ldap_result - wait for an ldap result response to a message from the
|
||
|
* ldap server. If msgid is -1, any message will be accepted, otherwise
|
||
|
* ldap_result will wait for a response with msgid. If all is 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. This is only useful for search responses,
|
||
|
* which can be of two message types (zero or more entries, followed by an
|
||
|
* ldap result). The type of the first message received is returned.
|
||
|
* When waiting, any messages that have been abandoned are discarded.
|
||
|
*
|
||
|
* Example:
|
||
|
* ldap_result( s, msgid, all, timeout, result )
|
||
|
*/
|
||
|
int
|
||
|
LDAP_CALL
|
||
|
ldap_result(
|
||
|
LDAP *ld,
|
||
|
int msgid,
|
||
|
int all,
|
||
|
struct timeval *timeout,
|
||
|
LDAPMessage **result
|
||
|
)
|
||
|
{
|
||
|
int rc;
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_result\n", 0, 0, 0 );
|
||
|
|
||
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
|
||
|
return( -1 ); /* punt */
|
||
|
}
|
||
|
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_RESULT_LOCK );
|
||
|
|
||
|
rc = nsldapi_result_nolock(ld, msgid, all, 1, timeout, result);
|
||
|
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESULT_LOCK );
|
||
|
|
||
|
return( rc );
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
nsldapi_result_nolock( LDAP *ld, int msgid, int all, int unlock_permitted,
|
||
|
struct timeval *timeout, LDAPMessage **result )
|
||
|
{
|
||
|
int rc;
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"nsldapi_result_nolock (msgid=%d, all=%d)\n", msgid, all, 0 );
|
||
|
|
||
|
/*
|
||
|
* First, 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.
|
||
|
*/
|
||
|
|
||
|
if ( result == NULL ) {
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
if ( check_response_queue( ld, msgid, all, 1, result ) != 0 ) {
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
|
||
|
rc = (*result)->lm_msgtype;
|
||
|
} else {
|
||
|
rc = wait4msg( ld, msgid, all, unlock_permitted, timeout,
|
||
|
result );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* XXXmcs should use cache function pointers to hook in memcache
|
||
|
*/
|
||
|
if ( ld->ld_memcache != NULL && NSLDAPI_SEARCH_RELATED_RESULT( rc ) &&
|
||
|
!((*result)->lm_fromcache )) {
|
||
|
ldap_memcache_append( ld, (*result)->lm_msgid,
|
||
|
(all || NSLDAPI_IS_SEARCH_RESULT( rc )), *result );
|
||
|
}
|
||
|
|
||
|
return( rc );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Look through the list of queued responses for a message that matches the
|
||
|
* criteria in the msgid and all parameters. msgid == LDAP_RES_ANY matches
|
||
|
* all ids.
|
||
|
*
|
||
|
* If an appropriate message is found, a non-zero value is returned and the
|
||
|
* message is dequeued and assigned to *result.
|
||
|
*
|
||
|
* If not, *result is set to NULL and this function returns 0.
|
||
|
*/
|
||
|
static int
|
||
|
check_response_queue( LDAP *ld, int msgid, int all, int do_abandon_check,
|
||
|
LDAPMessage **result )
|
||
|
{
|
||
|
LDAPMessage *lm, *lastlm, *nextlm;
|
||
|
LDAPRequest *lr;
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"=> check_response_queue (msgid=%d, all=%d)\n", msgid, all, 0 );
|
||
|
|
||
|
*result = NULL;
|
||
|
lastlm = NULL;
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
|
||
|
for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
|
||
|
nextlm = lm->lm_next;
|
||
|
|
||
|
if ( do_abandon_check && ldap_abandoned( ld, lm->lm_msgid ) ) {
|
||
|
ldap_mark_abandoned( ld, lm->lm_msgid );
|
||
|
|
||
|
if ( lastlm == NULL ) {
|
||
|
ld->ld_responses = lm->lm_next;
|
||
|
} else {
|
||
|
lastlm->lm_next = nextlm;
|
||
|
}
|
||
|
|
||
|
ldap_msgfree( lm );
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
|
||
|
LDAPMessage *tmp;
|
||
|
|
||
|
if ( all == 0
|
||
|
|| (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
|
||
|
&& lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
|
||
|
&& lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) )
|
||
|
break;
|
||
|
|
||
|
for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
|
||
|
if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( tmp == NULL ) {
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"<= check_response_queue NOT FOUND\n",
|
||
|
0, 0, 0 );
|
||
|
return( 0 ); /* no message to return */
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
lastlm = lm;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* if we did not find a message OR if the one we found is a result for
|
||
|
* a request that is still pending, return failure.
|
||
|
*/
|
||
|
if ( lm == NULL
|
||
|
|| (( lr = nsldapi_find_request_by_msgid( ld, lm->lm_msgid ))
|
||
|
!= NULL && lr->lr_outrefcnt > 0 )) {
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"<= check_response_queue NOT FOUND\n",
|
||
|
0, 0, 0 );
|
||
|
return( 0 ); /* no message to return */
|
||
|
}
|
||
|
|
||
|
if ( all == 0 ) {
|
||
|
if ( lm->lm_chain == NULL ) {
|
||
|
if ( lastlm == NULL ) {
|
||
|
ld->ld_responses = lm->lm_next;
|
||
|
} else {
|
||
|
lastlm->lm_next = lm->lm_next;
|
||
|
}
|
||
|
} else {
|
||
|
if ( lastlm == NULL ) {
|
||
|
ld->ld_responses = lm->lm_chain;
|
||
|
ld->ld_responses->lm_next = lm->lm_next;
|
||
|
} else {
|
||
|
lastlm->lm_next = lm->lm_chain;
|
||
|
lastlm->lm_next->lm_next = lm->lm_next;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if ( lastlm == NULL ) {
|
||
|
ld->ld_responses = lm->lm_next;
|
||
|
} else {
|
||
|
lastlm->lm_next = lm->lm_next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( all == 0 ) {
|
||
|
lm->lm_chain = NULL;
|
||
|
}
|
||
|
lm->lm_next = NULL;
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
|
||
|
*result = lm;
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"<= check_response_queue returning msgid %d type %d\n",
|
||
|
lm->lm_msgid, lm->lm_msgtype, 0 );
|
||
|
return( 1 ); /* a message was found and returned in *result */
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted,
|
||
|
struct timeval *timeout, LDAPMessage **result )
|
||
|
{
|
||
|
int rc;
|
||
|
struct timeval tv, *tvp;
|
||
|
long start_time = 0, tmp_time;
|
||
|
LDAPConn *lc, *nextlc;
|
||
|
LDAPRequest *lr;
|
||
|
|
||
|
#ifdef LDAP_DEBUG
|
||
|
if ( timeout == NULL ) {
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (infinite timeout)\n",
|
||
|
0, 0, 0 );
|
||
|
} else {
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (timeout %ld sec, %ld usec)\n",
|
||
|
timeout->tv_sec, timeout->tv_usec, 0 );
|
||
|
}
|
||
|
#endif /* LDAP_DEBUG */
|
||
|
|
||
|
/* check the cache */
|
||
|
if ( ld->ld_cache_on && ld->ld_cache_result != NULL ) {
|
||
|
/* if ( unlock_permitted ) LDAP_MUTEX_UNLOCK( ld ); */
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
|
||
|
rc = (ld->ld_cache_result)( ld, msgid, all, timeout, result );
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
|
||
|
/* if ( unlock_permitted ) LDAP_MUTEX_LOCK( ld ); */
|
||
|
if ( rc != 0 ) {
|
||
|
return( rc );
|
||
|
}
|
||
|
if ( ld->ld_cache_strategy == LDAP_CACHE_LOCALDB ) {
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, NULL );
|
||
|
return( 0 ); /* timeout */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* if we are looking for a specific msgid, check to see if it is
|
||
|
* associated with a dead connection and return an error if so.
|
||
|
*/
|
||
|
if ( msgid != LDAP_RES_ANY && msgid != LDAP_RES_UNSOLICITED ) {
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
|
||
|
if (( lr = nsldapi_find_request_by_msgid( ld, msgid ))
|
||
|
== NULL ) {
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL,
|
||
|
nsldapi_strdup( "unknown message id" ));
|
||
|
return( -1 ); /* could not find request for msgid */
|
||
|
}
|
||
|
if ( lr->lr_conn != NULL &&
|
||
|
lr->lr_conn->lconn_status == LDAP_CONNST_DEAD ) {
|
||
|
nsldapi_free_request( ld, lr, 1 );
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
|
||
|
return( -1 ); /* connection dead */
|
||
|
}
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
|
||
|
}
|
||
|
|
||
|
if ( timeout == NULL ) {
|
||
|
tvp = NULL;
|
||
|
} else {
|
||
|
tv = *timeout;
|
||
|
tvp = &tv;
|
||
|
start_time = (long)time( NULL );
|
||
|
}
|
||
|
|
||
|
rc = -2;
|
||
|
while ( rc == -2 ) {
|
||
|
#ifdef LDAP_DEBUG
|
||
|
if ( ldap_debug & LDAP_DEBUG_TRACE ) {
|
||
|
nsldapi_dump_connection( ld, ld->ld_conns, 1 );
|
||
|
nsldapi_dump_requests_and_responses( ld );
|
||
|
}
|
||
|
#endif /* LDAP_DEBUG */
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
|
||
|
for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
|
||
|
if ( lc->lconn_sb->sb_ber.ber_ptr <
|
||
|
lc->lconn_sb->sb_ber.ber_end ) {
|
||
|
rc = read1msg( ld, msgid, all, lc->lconn_sb,
|
||
|
lc, result );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
|
||
|
|
||
|
if ( lc == NULL ) {
|
||
|
rc = nsldapi_iostatus_poll( ld, tvp );
|
||
|
|
||
|
#if defined( LDAP_DEBUG ) && !defined( macintosh ) && !defined( DOS )
|
||
|
if ( rc == -1 ) {
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"nsldapi_iostatus_poll returned -1: errno %d\n",
|
||
|
LDAP_GET_ERRNO( ld ), 0, 0 );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if !defined( macintosh ) && !defined( DOS )
|
||
|
if ( rc == 0 || ( rc == -1 && (( ld->ld_options &
|
||
|
LDAP_BITOPT_RESTART ) == 0 ||
|
||
|
LDAP_GET_ERRNO( ld ) != EINTR ))) {
|
||
|
#else
|
||
|
if ( rc == -1 || rc == 0 ) {
|
||
|
#endif
|
||
|
LDAP_SET_LDERRNO( ld, (rc == -1 ?
|
||
|
LDAP_SERVER_DOWN : LDAP_TIMEOUT), NULL,
|
||
|
NULL );
|
||
|
if ( rc == -1 ) {
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
|
||
|
nsldapi_connection_lost_nolock( ld,
|
||
|
NULL );
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
|
||
|
}
|
||
|
return( rc );
|
||
|
}
|
||
|
|
||
|
if ( rc == -1 ) {
|
||
|
rc = -2; /* select interrupted: loop */
|
||
|
} else {
|
||
|
rc = -2;
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
|
||
|
for ( lc = ld->ld_conns; rc == -2 && lc != NULL;
|
||
|
lc = nextlc ) {
|
||
|
nextlc = lc->lconn_next;
|
||
|
if ( lc->lconn_status ==
|
||
|
LDAP_CONNST_CONNECTED &&
|
||
|
nsldapi_iostatus_is_read_ready( ld,
|
||
|
lc->lconn_sb )) {
|
||
|
rc = read1msg( ld, msgid, all,
|
||
|
lc->lconn_sb, lc, result );
|
||
|
}
|
||
|
else if (ld->ld_options & LDAP_BITOPT_ASYNC) {
|
||
|
if ( lr
|
||
|
&& lc->lconn_status == LDAP_CONNST_CONNECTING
|
||
|
&& nsldapi_iostatus_is_write_ready( ld,
|
||
|
lc->lconn_sb ) ) {
|
||
|
rc = nsldapi_ber_flush( ld, lc->lconn_sb, lr->lr_ber, 0, 1 );
|
||
|
if ( rc == 0 ) {
|
||
|
rc = LDAP_RES_BIND;
|
||
|
lc->lconn_status = LDAP_CONNST_CONNECTED;
|
||
|
|
||
|
lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
|
||
|
lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
|
||
|
nsldapi_iostatus_interest_read( ld, lc->lconn_sb );
|
||
|
}
|
||
|
else if ( rc == -1 ) {
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
|
||
|
nsldapi_free_request( ld, lr, 0 );
|
||
|
nsldapi_free_connection( ld, lc, NULL, NULL,
|
||
|
0, 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* It is possible that recursion occurred while chasing
|
||
|
* referrals and as a result the message we are looking
|
||
|
* for may have been placed on the response queue. Look
|
||
|
* for it there before continuing so we don't end up
|
||
|
* waiting on the network for a message that we already
|
||
|
* received!
|
||
|
*/
|
||
|
if ( rc == -2 &&
|
||
|
check_response_queue( ld, msgid, all, 0, result ) != 0 ) {
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
|
||
|
rc = (*result)->lm_msgtype;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* honor the timeout if specified
|
||
|
*/
|
||
|
if ( rc == -2 && tvp != NULL ) {
|
||
|
tmp_time = (long)time( NULL );
|
||
|
if (( tv.tv_sec -= ( tmp_time - start_time )) <= 0 ) {
|
||
|
rc = 0; /* timed out */
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL,
|
||
|
NULL );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg: %ld secs to go\n",
|
||
|
tv.tv_sec, 0, 0 );
|
||
|
start_time = tmp_time;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return( rc );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* read1msg() should be called with LDAP_CONN_LOCK and LDAP_REQ_LOCK locked.
|
||
|
*/
|
||
|
static int
|
||
|
read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc,
|
||
|
LDAPMessage **result )
|
||
|
{
|
||
|
BerElement *ber;
|
||
|
LDAPMessage *new, *l, *prev, *chainprev, *tmp;
|
||
|
long id;
|
||
|
unsigned long tag, len;
|
||
|
int terrno, lderr, foundit = 0;
|
||
|
LDAPRequest *lr;
|
||
|
int rc, has_parent, message_can_be_returned;
|
||
|
int manufactured_result = 0;
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "read1msg\n", 0, 0, 0 );
|
||
|
|
||
|
message_can_be_returned = 1; /* the usual case... */
|
||
|
|
||
|
/*
|
||
|
* if we are not already in the midst of reading a message, allocate
|
||
|
* a ber that is associated with this connection
|
||
|
*/
|
||
|
if ( lc->lconn_ber == NULLBER && nsldapi_alloc_ber_with_options( ld,
|
||
|
&lc->lconn_ber ) != LDAP_SUCCESS ) {
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ber_get_next() doesn't set errno on EOF, so we pre-set it to
|
||
|
* zero to avoid getting tricked by leftover "EAGAIN" errors
|
||
|
*/
|
||
|
LDAP_SET_ERRNO( ld, 0 );
|
||
|
|
||
|
/* get the next message */
|
||
|
if ( (tag = ber_get_next( sb, &len, lc->lconn_ber ))
|
||
|
!= LDAP_TAG_MESSAGE ) {
|
||
|
terrno = LDAP_GET_ERRNO( ld );
|
||
|
if ( terrno == EWOULDBLOCK || terrno == EAGAIN ) {
|
||
|
return( -2 ); /* try again */
|
||
|
}
|
||
|
LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
|
||
|
LDAP_LOCAL_ERROR), NULL, NULL );
|
||
|
if ( tag == LBER_DEFAULT ) {
|
||
|
nsldapi_connection_lost_nolock( ld, sb );
|
||
|
}
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Since we have received a complete message now, we pull this ber
|
||
|
* out of the connection structure and never read into it again.
|
||
|
*/
|
||
|
ber = lc->lconn_ber;
|
||
|
lc->lconn_ber = NULLBER;
|
||
|
|
||
|
/* message id */
|
||
|
if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
/* if it's been abandoned, toss it */
|
||
|
if ( ldap_abandoned( ld, (int)id ) ) {
|
||
|
ber_free( ber, 1 );
|
||
|
return( -2 ); /* continue looking */
|
||
|
}
|
||
|
|
||
|
if ( id == LDAP_RES_UNSOLICITED ) {
|
||
|
lr = NULL;
|
||
|
} else if (( lr = nsldapi_find_request_by_msgid( ld, id )) == NULL ) {
|
||
|
LDAPDebug( LDAP_DEBUG_ANY,
|
||
|
"no request for response with msgid %ld (tossing)\n",
|
||
|
id, 0, 0 );
|
||
|
ber_free( ber, 1 );
|
||
|
return( -2 ); /* continue looking */
|
||
|
}
|
||
|
|
||
|
/* the message type */
|
||
|
if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) {
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
|
||
|
return( -1 );
|
||
|
}
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "got %s msgid %ld, original id %d\n",
|
||
|
( tag == LDAP_RES_SEARCH_ENTRY ) ? "ENTRY" :
|
||
|
( tag == LDAP_RES_SEARCH_REFERENCE ) ? "REFERENCE" : "RESULT", id,
|
||
|
( lr == NULL ) ? id : lr->lr_origid );
|
||
|
|
||
|
if ( lr != NULL ) {
|
||
|
id = lr->lr_origid;
|
||
|
lr->lr_res_msgtype = tag;
|
||
|
}
|
||
|
rc = -2; /* default is to keep looking (no response found) */
|
||
|
|
||
|
if ( id != LDAP_RES_UNSOLICITED && ( tag == LDAP_RES_SEARCH_REFERENCE ||
|
||
|
tag != LDAP_RES_SEARCH_ENTRY )) {
|
||
|
int refchasing, reftotal, simple_request = 0;
|
||
|
|
||
|
check_for_refs( ld, lr, ber, lc->lconn_version, &reftotal,
|
||
|
&refchasing );
|
||
|
|
||
|
if ( refchasing > 0 || lr->lr_outrefcnt > 0 ) {
|
||
|
/*
|
||
|
* we're chasing one or more new refs...
|
||
|
*/
|
||
|
ber_free( ber, 1 );
|
||
|
ber = NULLBER;
|
||
|
lr->lr_status = LDAP_REQST_CHASINGREFS;
|
||
|
message_can_be_returned = 0;
|
||
|
|
||
|
} else if ( tag != LDAP_RES_SEARCH_REFERENCE ) {
|
||
|
/*
|
||
|
* this request is complete...
|
||
|
*/
|
||
|
has_parent = ( lr->lr_parent != NULL );
|
||
|
|
||
|
if ( lr->lr_outrefcnt <= 0 && !has_parent ) {
|
||
|
/* request without any refs */
|
||
|
simple_request = ( reftotal == 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If this is not a child request and it is a bind
|
||
|
* request, reset the connection's bind DN and
|
||
|
* status based on the result of the operation.
|
||
|
*/
|
||
|
if ( !has_parent &&
|
||
|
LDAP_RES_BIND == lr->lr_res_msgtype &&
|
||
|
lr->lr_conn != NULL ) {
|
||
|
if ( lr->lr_conn->lconn_binddn != NULL ) {
|
||
|
NSLDAPI_FREE(
|
||
|
lr->lr_conn->lconn_binddn );
|
||
|
}
|
||
|
if ( LDAP_SUCCESS == nsldapi_parse_result( ld,
|
||
|
lr->lr_res_msgtype, ber, &lderr, NULL,
|
||
|
NULL, NULL, NULL )
|
||
|
&& LDAP_SUCCESS == lderr ) {
|
||
|
lr->lr_conn->lconn_bound = 1;
|
||
|
lr->lr_conn->lconn_binddn =
|
||
|
lr->lr_binddn;
|
||
|
lr->lr_binddn = NULL;
|
||
|
} else {
|
||
|
lr->lr_conn->lconn_bound = 0;
|
||
|
lr->lr_conn->lconn_binddn = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* if this response is to a child request, we toss
|
||
|
* the message contents and just merge error info.
|
||
|
* into the parent.
|
||
|
*/
|
||
|
if ( has_parent ) {
|
||
|
ber_free( ber, 1 );
|
||
|
ber = NULLBER;
|
||
|
}
|
||
|
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 */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* we recognize a request as complete when:
|
||
|
* 1) it has no outstanding referrals
|
||
|
* 2) it is not a child request
|
||
|
* 3) we have received a result for the request (i.e.,
|
||
|
* something other than an entry or a reference).
|
||
|
*/
|
||
|
if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL &&
|
||
|
lr->lr_res_msgtype != LDAP_RES_SEARCH_ENTRY &&
|
||
|
lr->lr_res_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
|
||
|
id = lr->lr_msgid;
|
||
|
tag = lr->lr_res_msgtype;
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"request %ld done\n", id, 0, 0 );
|
||
|
LDAPDebug( 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 : "" );
|
||
|
if ( !simple_request ) {
|
||
|
if ( ber != NULLBER ) {
|
||
|
ber_free( ber, 1 );
|
||
|
ber = NULLBER;
|
||
|
}
|
||
|
if ( build_result_ber( ld, &ber, lr )
|
||
|
!= LDAP_SUCCESS ) {
|
||
|
rc = -1; /* fatal error */
|
||
|
} else {
|
||
|
manufactured_result = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nsldapi_free_request( ld, lr, 1 );
|
||
|
} else {
|
||
|
message_can_be_returned = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ber == NULLBER ) {
|
||
|
return( rc );
|
||
|
}
|
||
|
|
||
|
/* make a new ldap message */
|
||
|
if ( (new = (LDAPMessage*)NSLDAPI_CALLOC( 1, sizeof(struct ldapmsg) ))
|
||
|
== NULL ) {
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
|
||
|
return( -1 );
|
||
|
}
|
||
|
new->lm_msgid = (int)id;
|
||
|
new->lm_msgtype = tag;
|
||
|
new->lm_ber = ber;
|
||
|
|
||
|
/*
|
||
|
* if this is a search entry or if this request is complete (i.e.,
|
||
|
* there are no outstanding referrals) then add to cache and check
|
||
|
* to see if we should return this to the caller right away or not.
|
||
|
*/
|
||
|
if ( message_can_be_returned ) {
|
||
|
if ( ld->ld_cache_on ) {
|
||
|
nsldapi_add_result_to_cache( ld, new );
|
||
|
}
|
||
|
|
||
|
if ( msgid == LDAP_RES_ANY || id == msgid ) {
|
||
|
if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
|
||
|
/*
|
||
|
* return the first response we have for this
|
||
|
* search request later (possibly an entire
|
||
|
* chain of messages).
|
||
|
*/
|
||
|
foundit = 1;
|
||
|
} else if ( all == 0
|
||
|
|| (new->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
|
||
|
&& new->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) {
|
||
|
*result = new;
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL,
|
||
|
NULL );
|
||
|
return( tag );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* if not, we must add it to the list of responses. if
|
||
|
* the msgid is already there, it must be part of an existing
|
||
|
* search response.
|
||
|
*/
|
||
|
|
||
|
prev = NULL;
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
|
||
|
for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
|
||
|
if ( l->lm_msgid == new->lm_msgid )
|
||
|
break;
|
||
|
prev = l;
|
||
|
}
|
||
|
|
||
|
/* not part of an existing search response */
|
||
|
if ( l == NULL ) {
|
||
|
if ( foundit ) {
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
*result = new;
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
|
||
|
return( tag );
|
||
|
}
|
||
|
|
||
|
new->lm_next = ld->ld_responses;
|
||
|
ld->ld_responses = new;
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"adding new response id %d type %d (looking for id %d)\n",
|
||
|
new->lm_msgid, new->lm_msgtype, msgid );
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
if( message_can_be_returned )
|
||
|
POST( ld, new->lm_msgid, new );
|
||
|
return( -2 ); /* continue looking */
|
||
|
}
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"adding response id %d type %d (looking for id %d)\n",
|
||
|
new->lm_msgid, new->lm_msgtype, msgid );
|
||
|
|
||
|
/*
|
||
|
* part of a search response - add to end of list of entries
|
||
|
*
|
||
|
* the first step is to find the end of the list of entries and
|
||
|
* references. after the following loop is executed, tmp points to
|
||
|
* the last entry or reference in the chain. If there are none,
|
||
|
* tmp points to the search result.
|
||
|
*/
|
||
|
chainprev = NULL;
|
||
|
for ( tmp = l; tmp->lm_chain != NULL &&
|
||
|
( tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY
|
||
|
|| tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE );
|
||
|
tmp = tmp->lm_chain ) {
|
||
|
chainprev = tmp;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If this is a manufactured result message and a result is already
|
||
|
* queued we throw away the one that is queued and replace it with
|
||
|
* our new result. This is necessary so we don't end up returning
|
||
|
* more than one result.
|
||
|
*/
|
||
|
if ( manufactured_result &&
|
||
|
tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
|
||
|
/*
|
||
|
* the result is the only thing in the chain... replace it.
|
||
|
*/
|
||
|
new->lm_chain = tmp->lm_chain;
|
||
|
new->lm_next = tmp->lm_next;
|
||
|
if ( chainprev == NULL ) {
|
||
|
if ( prev == NULL ) {
|
||
|
ld->ld_responses = new;
|
||
|
} else {
|
||
|
prev->lm_next = new;
|
||
|
}
|
||
|
} else {
|
||
|
chainprev->lm_chain = new;
|
||
|
}
|
||
|
if ( l == tmp ) {
|
||
|
l = new;
|
||
|
}
|
||
|
ldap_msgfree( tmp );
|
||
|
|
||
|
} else if ( manufactured_result && tmp->lm_chain != NULL
|
||
|
&& tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
|
||
|
/*
|
||
|
* entries or references are also present, so the result
|
||
|
* is the next entry after tmp. replace it.
|
||
|
*/
|
||
|
new->lm_chain = tmp->lm_chain->lm_chain;
|
||
|
new->lm_next = tmp->lm_chain->lm_next;
|
||
|
ldap_msgfree( tmp->lm_chain );
|
||
|
tmp->lm_chain = new;
|
||
|
|
||
|
} else if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
|
||
|
/*
|
||
|
* the result is the only thing in the chain... add before it.
|
||
|
*/
|
||
|
new->lm_chain = tmp;
|
||
|
if ( chainprev == NULL ) {
|
||
|
if ( prev == NULL ) {
|
||
|
ld->ld_responses = new;
|
||
|
} else {
|
||
|
prev->lm_next = new;
|
||
|
}
|
||
|
} else {
|
||
|
chainprev->lm_chain = new;
|
||
|
}
|
||
|
if ( l == tmp ) {
|
||
|
l = new;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
/*
|
||
|
* entries and/or references are present... add to the end
|
||
|
* of the entry/reference part of the chain.
|
||
|
*/
|
||
|
new->lm_chain = tmp->lm_chain;
|
||
|
tmp->lm_chain = new;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return the first response or the whole chain if that's what
|
||
|
* we were looking for....
|
||
|
*/
|
||
|
if ( foundit ) {
|
||
|
if ( all == 0 && l->lm_chain != NULL ) {
|
||
|
/*
|
||
|
* only return the first response in the chain
|
||
|
*/
|
||
|
if ( prev == NULL ) {
|
||
|
ld->ld_responses = l->lm_chain;
|
||
|
} else {
|
||
|
prev->lm_next = l->lm_chain;
|
||
|
}
|
||
|
l->lm_chain = NULL;
|
||
|
tag = l->lm_msgtype;
|
||
|
} else {
|
||
|
/*
|
||
|
* return all of the responses (may be a chain)
|
||
|
*/
|
||
|
if ( prev == NULL ) {
|
||
|
ld->ld_responses = l->lm_next;
|
||
|
} else {
|
||
|
prev->lm_next = l->lm_next;
|
||
|
}
|
||
|
}
|
||
|
*result = l;
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
|
||
|
return( tag );
|
||
|
}
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
return( -2 ); /* continue looking */
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* check for LDAPv2+ (UMich extension) or LDAPv3 referrals or references
|
||
|
* errors are merged in "lr".
|
||
|
*/
|
||
|
static void
|
||
|
check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber,
|
||
|
int ldapversion, int *totalcountp, int *chasingcountp )
|
||
|
{
|
||
|
int err, origerr;
|
||
|
char *errstr, *matcheddn, **v3refs;
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "check_for_refs\n", 0, 0, 0 );
|
||
|
|
||
|
*chasingcountp = *totalcountp = 0;
|
||
|
|
||
|
if ( ldapversion < LDAP_VERSION2 || ( lr->lr_parent == NULL
|
||
|
&& ( ld->ld_options & LDAP_BITOPT_REFERRALS ) == 0 )) {
|
||
|
/* referrals are not supported or are disabled */
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
|
||
|
err = nsldapi_parse_reference( ld, ber, &v3refs, NULL );
|
||
|
origerr = LDAP_REFERRAL; /* a small lie... */
|
||
|
matcheddn = errstr = NULL;
|
||
|
} else {
|
||
|
err = nsldapi_parse_result( ld, lr->lr_res_msgtype, ber,
|
||
|
&origerr, &matcheddn, &errstr, &v3refs, NULL );
|
||
|
}
|
||
|
|
||
|
if ( err != LDAP_SUCCESS ) {
|
||
|
/* parse failed */
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( origerr == LDAP_REFERRAL ) { /* ldapv3 */
|
||
|
if ( v3refs != NULL ) {
|
||
|
err = nsldapi_chase_v3_refs( ld, lr, v3refs,
|
||
|
( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ),
|
||
|
totalcountp, chasingcountp );
|
||
|
ldap_value_free( v3refs );
|
||
|
}
|
||
|
} else if ( ldapversion == LDAP_VERSION2
|
||
|
&& origerr != LDAP_SUCCESS ) {
|
||
|
/* referrals may be present in the error string */
|
||
|
err = nsldapi_chase_v2_referrals( ld, lr, &errstr,
|
||
|
totalcountp, chasingcountp );
|
||
|
}
|
||
|
|
||
|
/* set LDAP errno, message, and matched string appropriately */
|
||
|
if ( lr->lr_res_error != NULL ) {
|
||
|
NSLDAPI_FREE( lr->lr_res_error );
|
||
|
}
|
||
|
lr->lr_res_error = errstr;
|
||
|
|
||
|
if ( lr->lr_res_matched != NULL ) {
|
||
|
NSLDAPI_FREE( lr->lr_res_matched );
|
||
|
}
|
||
|
lr->lr_res_matched = matcheddn;
|
||
|
|
||
|
if ( err == LDAP_SUCCESS && ( *chasingcountp == *totalcountp )) {
|
||
|
if ( *totalcountp > 0 && ( origerr == LDAP_PARTIAL_RESULTS
|
||
|
|| origerr == LDAP_REFERRAL )) {
|
||
|
/* substitute success for referral error codes */
|
||
|
lr->lr_res_errno = LDAP_SUCCESS;
|
||
|
} else {
|
||
|
/* preserve existing non-referral error code */
|
||
|
lr->lr_res_errno = origerr;
|
||
|
}
|
||
|
} else if ( err != LDAP_SUCCESS ) {
|
||
|
/* error occurred while trying to chase referrals */
|
||
|
lr->lr_res_errno = err;
|
||
|
} else {
|
||
|
/* some referrals were not recognized */
|
||
|
lr->lr_res_errno = ( ldapversion == LDAP_VERSION2 )
|
||
|
? LDAP_PARTIAL_RESULTS : LDAP_REFERRAL;
|
||
|
}
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"check_for_refs: new result: msgid %d, res_errno %d, ",
|
||
|
lr->lr_msgid, lr->lr_res_errno, 0 );
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, " res_error <%s>, res_matched <%s>\n",
|
||
|
lr->lr_res_error ? lr->lr_res_error : "",
|
||
|
lr->lr_res_matched ? lr->lr_res_matched : "", 0 );
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"check_for_refs: %d new refs(s); chasing %d of them\n",
|
||
|
*totalcountp, *chasingcountp, 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
/* returns an LDAP error code and also sets it in LDAP * */
|
||
|
static int
|
||
|
build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr )
|
||
|
{
|
||
|
unsigned long len;
|
||
|
long along;
|
||
|
BerElement *ber;
|
||
|
int err;
|
||
|
|
||
|
if (( err = nsldapi_alloc_ber_with_options( ld, &ber ))
|
||
|
!= LDAP_SUCCESS ) {
|
||
|
return( err );
|
||
|
}
|
||
|
*berp = ber;
|
||
|
if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
|
||
|
(long)lr->lr_res_msgtype, lr->lr_res_errno,
|
||
|
lr->lr_res_matched ? lr->lr_res_matched : "",
|
||
|
lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) {
|
||
|
return( LDAP_ENCODING_ERROR );
|
||
|
}
|
||
|
|
||
|
ber_reset( ber, 1 );
|
||
|
if ( ber_skip_tag( ber, &len ) == LBER_ERROR ||
|
||
|
ber_get_int( ber, &along ) == LBER_ERROR ||
|
||
|
ber_peek_tag( ber, &len ) == LBER_ERROR ) {
|
||
|
return( LDAP_DECODING_ERROR );
|
||
|
}
|
||
|
|
||
|
return( LDAP_SUCCESS );
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
|
||
|
{
|
||
|
/*
|
||
|
* Merge error information in "lr" with "parentr" error code and string.
|
||
|
*/
|
||
|
if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
|
||
|
parentr->lr_res_errno = lr->lr_res_errno;
|
||
|
if ( lr->lr_res_error != NULL ) {
|
||
|
(void)nsldapi_append_referral( ld, &parentr->lr_res_error,
|
||
|
lr->lr_res_error );
|
||
|
}
|
||
|
} else if ( lr->lr_res_errno != LDAP_SUCCESS &&
|
||
|
parentr->lr_res_errno == LDAP_SUCCESS ) {
|
||
|
parentr->lr_res_errno = lr->lr_res_errno;
|
||
|
if ( parentr->lr_res_error != NULL ) {
|
||
|
NSLDAPI_FREE( parentr->lr_res_error );
|
||
|
}
|
||
|
parentr->lr_res_error = lr->lr_res_error;
|
||
|
lr->lr_res_error = NULL;
|
||
|
if ( NAME_ERROR( lr->lr_res_errno )) {
|
||
|
if ( parentr->lr_res_matched != NULL ) {
|
||
|
NSLDAPI_FREE( parentr->lr_res_matched );
|
||
|
}
|
||
|
parentr->lr_res_matched = lr->lr_res_matched;
|
||
|
lr->lr_res_matched = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
|
||
|
parentr->lr_msgid, 0, 0 );
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "result lderrno %d, error <%s>, matched <%s>\n",
|
||
|
parentr->lr_res_errno, parentr->lr_res_error ?
|
||
|
parentr->lr_res_error : "", parentr->lr_res_matched ?
|
||
|
parentr->lr_res_matched : "" );
|
||
|
}
|
||
|
|
||
|
#if defined( CLDAP )
|
||
|
#if !defined( macintosh ) && !defined( DOS ) && !defined( _WINDOWS ) && !defined(XP_OS2)
|
||
|
/* XXXmcs: was revised to support extended I/O callbacks but never compiled! */
|
||
|
static int
|
||
|
cldap_select1( LDAP *ld, struct timeval *timeout )
|
||
|
{
|
||
|
int rc;
|
||
|
static int tblsize = 0;
|
||
|
NSLDAPIIOStatus *iosp = ld->ld_iostatus;
|
||
|
|
||
|
if ( tblsize == 0 ) {
|
||
|
#ifdef USE_SYSCONF
|
||
|
tblsize = sysconf( _SC_OPEN_MAX );
|
||
|
#else /* USE_SYSCONF */
|
||
|
tblsize = getdtablesize();
|
||
|
#endif /* USE_SYSCONF */
|
||
|
}
|
||
|
|
||
|
if ( tblsize >= FD_SETSIZE ) {
|
||
|
/*
|
||
|
* clamp value so we don't overrun the fd_set structure
|
||
|
*/
|
||
|
tblsize = FD_SETSIZE - 1;
|
||
|
}
|
||
|
|
||
|
if ( NSLDAPI_IOSTATUS_TYPE_OSNATIVE == iosp->ios_type ) {
|
||
|
fd_set readfds;
|
||
|
|
||
|
FD_ZERO( &readfds );
|
||
|
FD_SET( ld->ld_sbp->sb_sd, &readfds );
|
||
|
|
||
|
/* XXXmcs: UNIX platforms should use poll() */
|
||
|
rc = select( tblsize, &readfds, 0, 0, timeout ) );
|
||
|
|
||
|
} else if ( NSLDAPI_IOSTATUS_TYPE_CALLBACK == iosp->ios_type ) {
|
||
|
LDAP_X_PollFD pollfds[ 1 ];
|
||
|
|
||
|
pollfds[0].lpoll_fd = ld->ld_sbp->sb_sd;
|
||
|
pollfds[0].lpoll_arg = ld->ld_sbp->sb_arg;
|
||
|
pollfds[0].lpoll_events = LDAP_X_POLLIN;
|
||
|
pollfds[0].lpoll_revents = 0;
|
||
|
rc = ld->ld_extpoll_fn( pollfds, 1, nsldapi_tv2ms( timeout ),
|
||
|
ld->ld_ext_session_arg );
|
||
|
} else {
|
||
|
LDAPDebug( LDAP_DEBUG_ANY,
|
||
|
"nsldapi_iostatus_poll: unknown I/O type %d\n",
|
||
|
rc = 0; /* simulate a timeout (what else to do?) */
|
||
|
}
|
||
|
|
||
|
return( rc );
|
||
|
}
|
||
|
#endif /* !macintosh */
|
||
|
|
||
|
|
||
|
#ifdef macintosh
|
||
|
static int
|
||
|
cldap_select1( LDAP *ld, struct timeval *timeout )
|
||
|
{
|
||
|
/* XXXmcs: needs to be revised to support I/O callbacks */
|
||
|
return( tcpselect( ld->ld_sbp->sb_sd, timeout ));
|
||
|
}
|
||
|
#endif /* macintosh */
|
||
|
|
||
|
|
||
|
#if (defined( DOS ) && defined( WINSOCK )) || defined( _WINDOWS ) || defined(XP_OS2)
|
||
|
/* XXXmcs: needs to be revised to support extended I/O callbacks */
|
||
|
static int
|
||
|
cldap_select1( LDAP *ld, struct timeval *timeout )
|
||
|
{
|
||
|
fd_set readfds;
|
||
|
int rc;
|
||
|
|
||
|
FD_ZERO( &readfds );
|
||
|
FD_SET( ld->ld_sbp->sb_sd, &readfds );
|
||
|
|
||
|
if ( NSLDAPI_IO_TYPE_STANDARD == ld->ldiou_type &&
|
||
|
NULL != ld->ld_select_fn ) {
|
||
|
rc = ld->ld_select_fn( 1, &readfds, 0, 0, timeout );
|
||
|
} else if ( NSLDAPI_IO_TYPE_EXTENDED == ld->ldiou_type &&
|
||
|
NULL != ld->ld_extselect_fn ) {
|
||
|
rc = ld->ld_extselect_fn( ld->ld_ext_session_arg, 1, &readfds, 0,
|
||
|
0, timeout ) );
|
||
|
} else {
|
||
|
/* XXXmcs: UNIX platforms should use poll() */
|
||
|
rc = select( 1, &readfds, 0, 0, timeout ) );
|
||
|
}
|
||
|
|
||
|
return( rc == SOCKET_ERROR ? -1 : rc );
|
||
|
}
|
||
|
#endif /* WINSOCK || _WINDOWS */
|
||
|
#endif /* CLDAP */
|
||
|
|
||
|
int
|
||
|
LDAP_CALL
|
||
|
ldap_msgfree( LDAPMessage *lm )
|
||
|
{
|
||
|
LDAPMessage *next;
|
||
|
int type = 0;
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
|
||
|
|
||
|
for ( ; lm != NULL; lm = next ) {
|
||
|
next = lm->lm_chain;
|
||
|
type = lm->lm_msgtype;
|
||
|
ber_free( lm->lm_ber, 1 );
|
||
|
NSLDAPI_FREE( (char *) lm );
|
||
|
}
|
||
|
|
||
|
return( type );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ldap_msgdelete - delete a message. It returns:
|
||
|
* 0 if the entire message was deleted
|
||
|
* -1 if the message was not found, or only part of it was found
|
||
|
*/
|
||
|
int
|
||
|
ldap_msgdelete( LDAP *ld, int msgid )
|
||
|
{
|
||
|
LDAPMessage *lm, *prev;
|
||
|
int msgtype;
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgdelete\n", 0, 0, 0 );
|
||
|
|
||
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
|
||
|
return( -1 ); /* punt */
|
||
|
}
|
||
|
|
||
|
prev = NULL;
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
|
||
|
for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
|
||
|
if ( lm->lm_msgid == msgid )
|
||
|
break;
|
||
|
prev = lm;
|
||
|
}
|
||
|
|
||
|
if ( lm == NULL )
|
||
|
{
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
if ( prev == NULL )
|
||
|
ld->ld_responses = lm->lm_next;
|
||
|
else
|
||
|
prev->lm_next = lm->lm_next;
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
|
||
|
msgtype = ldap_msgfree( lm );
|
||
|
if ( msgtype == LDAP_RES_SEARCH_ENTRY
|
||
|
|| msgtype == LDAP_RES_SEARCH_REFERENCE ) {
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* return 1 if message msgid is waiting to be abandoned, 0 otherwise
|
||
|
*/
|
||
|
static int
|
||
|
ldap_abandoned( LDAP *ld, int msgid )
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK );
|
||
|
if ( ld->ld_abandoned == NULL )
|
||
|
{
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
|
||
|
if ( ld->ld_abandoned[i] == msgid )
|
||
|
{
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
|
||
|
return( 1 );
|
||
|
}
|
||
|
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
ldap_mark_abandoned( LDAP *ld, int msgid )
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK );
|
||
|
if ( ld->ld_abandoned == NULL )
|
||
|
{
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
|
||
|
if ( ld->ld_abandoned[i] == msgid )
|
||
|
break;
|
||
|
|
||
|
if ( ld->ld_abandoned[i] == -1 )
|
||
|
{
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
for ( ; ld->ld_abandoned[i] != -1; i++ ) {
|
||
|
ld->ld_abandoned[i] = ld->ld_abandoned[i + 1];
|
||
|
}
|
||
|
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef CLDAP
|
||
|
int
|
||
|
cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement **ber )
|
||
|
{
|
||
|
int rc;
|
||
|
unsigned long tag, len;
|
||
|
|
||
|
if ( ld->ld_sbp->sb_ber.ber_ptr >= ld->ld_sbp->sb_ber.ber_end ) {
|
||
|
rc = cldap_select1( ld, timeout );
|
||
|
if ( rc == -1 || rc == 0 ) {
|
||
|
LDAP_SET_LDERRNO( ld, (rc == -1 ? LDAP_SERVER_DOWN :
|
||
|
LDAP_TIMEOUT), NULL, NULL );
|
||
|
return( rc );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* get the next message */
|
||
|
if ( (tag = ber_get_next( ld->ld_sbp, &len, ber ))
|
||
|
!= LDAP_TAG_MESSAGE ) {
|
||
|
LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
|
||
|
LDAP_LOCAL_ERROR), NULL, NULL );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
return( tag );
|
||
|
}
|
||
|
#endif /* CLDAP */
|
||
|
|
||
|
int
|
||
|
nsldapi_post_result( LDAP *ld, int msgid, LDAPMessage *result )
|
||
|
{
|
||
|
LDAPPend *lp;
|
||
|
|
||
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
||
|
"nsldapi_post_result(ld=0x%x, msgid=%d, result=0x%x)\n",
|
||
|
ld, msgid, result );
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_PEND_LOCK );
|
||
|
if( msgid == LDAP_RES_ANY ) {
|
||
|
/*
|
||
|
* Look for any pending request for which someone is waiting.
|
||
|
*/
|
||
|
for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next )
|
||
|
{
|
||
|
if ( lp->lp_sema != NULL ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
* If we did't find a pending request, lp is NULL at this
|
||
|
* point, and we will leave this function without doing
|
||
|
* anything more -- which is exactly what we want to do.
|
||
|
*/
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*
|
||
|
* Look for a pending request specific to this message id
|
||
|
*/
|
||
|
for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next )
|
||
|
{
|
||
|
if( lp->lp_msgid == msgid )
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if( lp == NULL )
|
||
|
{
|
||
|
/*
|
||
|
* No pending requests for this response... append to
|
||
|
* our pending result list.
|
||
|
*/
|
||
|
LDAPPend *newlp;
|
||
|
newlp = (LDAPPend *)NSLDAPI_CALLOC( 1,
|
||
|
sizeof( LDAPPend ));
|
||
|
if( newlp == NULL )
|
||
|
{
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK );
|
||
|
LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL,
|
||
|
NULL );
|
||
|
return (-1);
|
||
|
}
|
||
|
newlp->lp_msgid = msgid;
|
||
|
newlp->lp_result = result;
|
||
|
link_pend( ld, newlp );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if( lp != NULL )
|
||
|
{
|
||
|
/*
|
||
|
* Wake up a thread that is waiting for this result.
|
||
|
*/
|
||
|
lp->lp_msgid = msgid;
|
||
|
lp->lp_result = result;
|
||
|
LDAP_SEMA_POST( ld, lp );
|
||
|
}
|
||
|
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK );
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
link_pend( LDAP *ld, LDAPPend *lp )
|
||
|
{
|
||
|
if (( lp->lp_next = ld->ld_pend ) != NULL )
|
||
|
{
|
||
|
lp->lp_next->lp_prev = lp;
|
||
|
}
|
||
|
ld->ld_pend = lp;
|
||
|
lp->lp_prev = NULL;
|
||
|
}
|
||
|
|
||
|
#if 0 /* these functions are no longer used */
|
||
|
static void
|
||
|
unlink_pend( LDAP *ld, LDAPPend *lp )
|
||
|
{
|
||
|
if ( lp->lp_prev == NULL ) {
|
||
|
ld->ld_pend = lp->lp_next;
|
||
|
} else {
|
||
|
lp->lp_prev->lp_next = lp->lp_next;
|
||
|
}
|
||
|
|
||
|
if ( lp->lp_next != NULL ) {
|
||
|
lp->lp_next->lp_prev = lp->lp_prev;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
unlink_msg( LDAP *ld, int msgid, int all )
|
||
|
{
|
||
|
int rc;
|
||
|
LDAPMessage *lm, *lastlm, *nextlm;
|
||
|
|
||
|
lastlm = NULL;
|
||
|
LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
|
||
|
for ( lm = ld->ld_responses; lm != NULL; lm = nextlm )
|
||
|
{
|
||
|
nextlm = lm->lm_next;
|
||
|
|
||
|
if ( lm->lm_msgid == msgid )
|
||
|
{
|
||
|
LDAPMessage *tmp;
|
||
|
|
||
|
if ( all == 0
|
||
|
|| (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
|
||
|
&& lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
|
||
|
&& lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) )
|
||
|
break;
|
||
|
|
||
|
for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
|
||
|
if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
|
||
|
break;
|
||
|
}
|
||
|
if( tmp != NULL )
|
||
|
break;
|
||
|
}
|
||
|
lastlm = lm;
|
||
|
}
|
||
|
|
||
|
if( lm != NULL )
|
||
|
{
|
||
|
|
||
|
if ( all == 0 )
|
||
|
{
|
||
|
if ( lm->lm_chain == NULL )
|
||
|
{
|
||
|
if ( lastlm == NULL )
|
||
|
ld->ld_responses = lm->lm_next;
|
||
|
else
|
||
|
lastlm->lm_next = lm->lm_next;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( lastlm == NULL )
|
||
|
{
|
||
|
ld->ld_responses = lm->lm_chain;
|
||
|
ld->ld_responses->lm_next = lm->lm_next;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lastlm->lm_next = lm->lm_chain;
|
||
|
lastlm->lm_next->lm_next = lm->lm_next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( lastlm == NULL )
|
||
|
ld->ld_responses = lm->lm_next;
|
||
|
else
|
||
|
lastlm->lm_next = lm->lm_next;
|
||
|
}
|
||
|
|
||
|
if ( all == 0 )
|
||
|
lm->lm_chain = NULL;
|
||
|
lm->lm_next = NULL;
|
||
|
rc = lm->lm_msgtype;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rc = -2;
|
||
|
}
|
||
|
LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
|
||
|
return ( rc );
|
||
|
}
|
||
|
#endif /* 0 */
|