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