OpenSolaris_b135/lib/libldap4/common/result.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright 1998-2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 *  Copyright (c) 1990 Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  result.c - wait for an ldap result
 */

#ifndef lint 
static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
#endif

#include <stdio.h>
#include <string.h>
#ifdef MACOS
#include <stdlib.h>
#include <time.h>
#include "macos.h"
#else /* MACOS */
#if defined( DOS ) || defined( _WIN32 )
#include <time.h>
#include "msdos.h"
#ifdef PCNFS
#include <tklib.h>
#include <tk_errno.h>
#include <bios.h>
#endif /* PCNFS */
#ifdef NCSA
#include "externs.h"
#endif /* NCSA */
#else /* DOS */
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#ifdef _AIX
#include <sys/select.h>
#endif /* _AIX */
#include "portable.h"
#endif /* DOS */
#endif /* MACOS */
#ifdef VMS
#include "ucx_select.h"
#endif
#include "lber.h"
#include "ldap.h"
#include "ldap-private.h"
#include "ldap-int.h"

#ifdef USE_SYSCONF
#include <unistd.h>
#endif /* USE_SYSCONF */

#ifdef NEEDPROTOS
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, struct timeval *timeout,
	LDAPMessage **result );
static int read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc,
	LDAPMessage **result );
static int build_result_ber( LDAP *ld, BerElement *ber, LDAPRequest *lr );
static void merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr );
#ifdef CLDAP
static int ldap_select1( LDAP *ld, struct timeval *timeout );
#endif
static int Ref_AddToRequest(LDAPRequest *lr, char **refs);
static void Ref_FreeAll(LDAPRequest *lr);
#else /* NEEDPROTOS */
static int ldap_abandoned();
static int ldap_mark_abandoned();
static int wait4msg();
static int read1msg();
static int build_result_ber();
static void merge_error_info();
#ifdef CLDAP
static int ldap_select1();
#endif
#endif /* NEEDPROTOS */

#if !defined( MACOS ) && !defined( DOS )
extern int	errno;
#endif

/*
 * 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 LDAP_MSG_ONE the first message with id msgid will be accepted. 
 * If all is LDAP_MSG_RECEIVED, the received messages with the 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 3 message types (zero or
 * more entries, zero or more references, one or more results).  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_result( LDAP *ld, int msgid, int all, struct timeval *timeout,
	LDAPMessage **result )
{
	LDAPMessage	*lm, *lastlm, *nextlm;
	int rv;

	/*
	 * 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.
	 */

#ifdef _REENTRANT
	LOCK_RESPONSE(ld);
	LOCK_LDAP(ld);
#endif
	Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 223, "ldap_result\n"), 0, 0, 0 );

	*result = NULLMSG;
	lastlm = NULLMSG;

	/* look in the received responses */
	for ( lm = ld->ld_responses; lm != NULLMSG; lm = nextlm ) {
		nextlm = lm->lm_next;

		/* if the msg has been abandonned, free it */
		if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
			ldap_mark_abandoned( ld, lm->lm_msgid );

			if ( lastlm == NULLMSG ) {
				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 return ONE or RECEIVED message(s) or not a search result, return lm */
			if ( all == LDAP_MSG_ONE || all == LDAP_MSG_RECEIVED
				 || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
					&& lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY
					&& lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE) )
				break;

			/* Search in the set of messages if one is a search result */
			for ( tmp = lm; tmp != NULLMSG; tmp = tmp->lm_chain ) {
				if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
					break;
			}
			/* No, well wait for the result message */
			if ( tmp == NULLMSG ) {
#ifdef _REENTRANT
				UNLOCK_LDAP(ld);
#endif
				rv = wait4msg( ld, msgid, all, timeout, result );
#ifdef _REENTRANT
				UNLOCK_RESPONSE(ld);
#endif
				return( rv );
			}
			/* Here we have the Search result pointed by tmp */
			break;
		}
		/* Check next response */
		lastlm = lm;
	}

	/* No response matching found : Wait for one  */
	if ( lm == NULLMSG ) {
#ifdef _REENTRANT
		UNLOCK_LDAP(ld);
#endif
		rv = wait4msg( ld, msgid, all, timeout, result );
#ifdef _REENTRANT
		UNLOCK_RESPONSE(ld);
#endif
		return( rv );
	}

	/* lm points to the message (chain) to return */

	/* Remove message to return from ld_responses list */
	if ( lastlm == NULLMSG ) {
		if (all == LDAP_MSG_ONE && lm->lm_chain != NULLMSG){
			ld->ld_responses = lm->lm_chain;
		} else {
			ld->ld_responses = lm->lm_next;
		}
	} else {
		if (all == LDAP_MSG_ONE && lm->lm_chain != NULLMSG) {
			lastlm->lm_next = lm->lm_chain;
		} else {
			lastlm->lm_next = lm->lm_next;
		}
	}

	if ( all == LDAP_MSG_ONE )
		lm->lm_chain = NULLMSG;
	/* Otherwise return the whole chain */
	/* No reponses attached */
	lm->lm_next = NULLMSG;

	*result = lm;
	ld->ld_errno = LDAP_SUCCESS;
	rv = lm->lm_msgtype;
#ifdef _REENTRANT
	UNLOCK_LDAP(ld);
	UNLOCK_RESPONSE(ld);
#endif
	return( rv );
}

static int
wait4msg( LDAP *ld, int msgid, int all, struct timeval *timeout,
	LDAPMessage **result )
{
	int		rc;
	struct timeval	tv, *tvp;
	time_t		start_time, tmp_time;
	LDAPConn	*lc, *nextlc;

#ifdef LDAP_DEBUG
	if ( timeout == NULL ) {
		Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 224, "wait4msg (infinite timeout)\n"),
		    0, 0, 0 );
	} else {
		Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 225, "wait4msg (timeout %1$ld sec, %2$ld usec)\n"),
		    timeout->tv_sec, timeout->tv_usec, 0 );
	}
#endif /* LDAP_DEBUG */

	if ( timeout == NULL ) {
		tvp = NULL;
	} else {
		tv = *timeout;
		tvp = &tv;
		start_time = time( NULL );
	}
		    
	rc = -2;
	while ( rc == -2 ) {
#ifdef LDAP_DEBUG
		if ( ldap_debug & LDAP_DEBUG_TRACE ) {
			dump_connection( ld, ld->ld_conns, 1 );
			dump_requests_and_responses( ld );
		}
#endif /* LDAP_DEBUG */
		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 ) {
				/* A Message is available, decode and process it */
				rc = read1msg( ld, msgid, all, lc->lconn_sb,
				    lc, result );
				break;
			}
		}
		/* There was no message available : Wait for one */
		if ( lc == NULL ) {
			rc = do_ldap_select( ld, tvp );


#if defined( LDAP_DEBUG ) && !defined( MACOS ) && !defined( DOS )
			if ( rc == -1 ) {
			    Debug( LDAP_DEBUG_TRACE,
				    catgets(slapdcat, 1, 226, "do_ldap_select returned -1: errno %d\n"),
				    errno, 0, 0 );
			}
#endif

#if !defined( MACOS ) && !defined( DOS )
			if ( rc == 0 || ( rc == -1 && (ld->ld_restart || errno != EINTR ))) {
#else
			if ( rc == -1 || rc == 0 ) {
#endif
				ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
				    LDAP_TIMEOUT);
				if ( rc == -1 ) {
#ifdef _REENTRANT
					LOCK_LDAP(ld);
#endif
					nsldapi_connection_lost_nolock( ld, NULL);
#ifdef _REENTRANT
					UNLOCK_LDAP(ld);
#endif
				}
				return( rc );
			}

			if ( rc == -1 ) {
				rc = -2;	/* select interrupted: Continue the loop */
			} else {
				rc = -2;
				for ( lc = ld->ld_conns; rc == -2 && lc != NULL;
				    lc = nextlc ) {
					nextlc = lc->lconn_next;
					if ( lc->lconn_status == LDAP_CONNST_CONNECTED) {
						/* Check on each connection. */
						long is_ready = is_read_ready( ld, lc->lconn_sb );
						
						if (is_ready > 0) {
							/* A Message is available, decode and process it */
							rc = read1msg( ld, msgid, all,
										   lc->lconn_sb, lc, result );
						} else if ( is_ready < 0){
							/* Error in the select : what to do in here ? */
							/* So far : */
							rc = -1;
						}
					}
				}
			}
		}

		if ( rc == -2 && tvp != NULL ) {
			tmp_time = time( NULL );
			if (( tv.tv_sec -=  ( tmp_time - start_time )) <= 0 ) {
				/* At this point if all == LDAP_MSG_RECEIVED, we must 
				   return all available messages for the msgid */
				if (all == LDAP_MSG_RECEIVED) {
					/* Search in responses if some have the correct id */
					/* if yes return the chain */
					/* Otherwise return timeout */ 
					break;
				}

				rc = 0;	/* timed out */
				ld->ld_errno = LDAP_TIMEOUT;
				break;
			}

			Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 227, "wait4msg:  %ld secs to go\n"),
				tv.tv_sec, 0, 0 );
			start_time = tmp_time;
		}
	}

	return( rc );
}


static int
read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb,
    LDAPConn *lc,
    LDAPMessage **result )
{
	BerElement	ber;
	LDAPMessage	*new, *L_res, *l, *prev, *tmp;
	int		id;
	unsigned int	tag, atag, len;
	int		foundit = 0;
	LDAPRequest	*lr, *lrparent;
	LDAPRef *theReferences;
	BerElement	tmpber;
	int		rc, refer_cnt, hadref, simple_request, samereq = 0, total_count;
	int retcode;
	int theErrCode = LDAP_SUCCESS;
	unsigned int	lderr;
	char *msgtypestr;
	char ** theRefs = NULL;
	char * theOid = NULL;
	char *lddn, *lderrmsg;
	
	Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 228, "read1msg\n"), 0, 0, 0 );

read_from_sb:
	lderr = LDAP_SUCCESS; /* Be optimistic */
	
	ber_zero_init( &ber, 0 );
	set_ber_options( ld, &ber );

	/* get the next message */
	if ( (tag = ber_get_next( sb, &len, &ber ))
	    != LDAP_TAG_MESSAGE ) {
		ld->ld_errno = (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
		    LDAP_LOCAL_ERROR);
		if ( tag == LBER_DEFAULT ) {
#ifdef _REENTRANT
			LOCK_LDAP(ld);
#endif
			nsldapi_connection_lost_nolock( ld, sb );
#ifdef _REENTRANT
			UNLOCK_LDAP(ld);
#endif
		}
		return( -1 );
	}

	/* message id */
	if ( ber_get_int( &ber, &id ) == LBER_ERROR ) {
		ld->ld_errno = LDAP_DECODING_ERROR;
		return( -1 );
	}

	/* if it's been abandoned, toss it */
	if ( ldap_abandoned( ld, (int)id ) ) {
		free( ber.ber_buf );	/* gack! */
		return( -2 );	/* continue looking */
	}

	/* the message type */
	if ( (tag = ber_peek_tag( &ber, &len )) == LBER_ERROR ) {
		ld->ld_errno = LDAP_DECODING_ERROR;
		return( -1 );
	}

	/* KE
	 * Treat unsolicited notification if we got one!
	 * 	id==0
	 * 	tag==LDAP_RES_EXTENDED
	 * 
	 * 	resultCode==	protocolError
	 *							strongAuthRequired
	 * 						unavailable
	 *		tag==LDAP_TAG_EXT_RESPNAME
	 *		response name (oid)==1.3.6.1.1.4.1.1466.20036
	 * 	no response field
	 * 
	 * Example:
	 * --------
	 * Ber format: 	{iaata}
	 * which means: 	returnCode dn errorMessage LDAP_TAG_EXT_RESPNAME "1.3.6.1.1.4.1.1466.20036"
	 */
	if ( (id==0) && (tag==LDAP_RES_EXTENDED) )
	{
		tmpber = ber;
		if (ber_scanf( &ber, "{iaa", &lderr, &lddn, &lderrmsg) != LBER_ERROR)
		{
			if (ber_peek_tag ( &ber, &atag) == LDAP_TAG_EXT_RESPNAME)
			{
			  if ( ber_get_stringa( &ber, &theOid) == LBER_ERROR )
				{
					ld->ld_errno = LDAP_DECODING_ERROR;
					return(-1);
				}
			}
			else
			{
				ld->ld_errno = LDAP_DECODING_ERROR;
				return(-1);
			}

			if (ber_peek_tag ( &ber, &atag) == LDAP_TAG_EXT_RESPONSE)
			{
				/* this field must be absent */
				ld->ld_errno = LDAP_DECODING_ERROR;
				return(-1);
			}
			if ( ber_scanf(&ber, "}")== LBER_ERROR)
			{
				ld->ld_errno = LDAP_DECODING_ERROR;
				return(-1);
			}      

			/* make a new ldap message to return the result */
			if ( (new = (LDAPMessage *) calloc( 1, sizeof(LDAPMessage) )) == NULL ) 
			{
				ld->ld_errno = LDAP_NO_MEMORY;
				return(-1);
			}
			new->lm_msgid = 0;
			new->lm_msgtype = tag;
			new->lm_ber = ber_dup( &tmpber );

			if ( 	(strncmp(theOid, "1.3.6.1.1.4.1.1466.20036", 24)==0) && 
					(lderr==LDAP_PROTOCOL_ERROR) || 
					(lderr==LDAP_STRONG_AUTH_REQUIRED) ||
					(lderr==LDAP_UNAVAILABLE) )
			{
				/* make a new ldap message to return the result */
				if ( (L_res = (LDAPMessage *) calloc( 1, sizeof(LDAPMessage) )) == NULL ) 
				{
					ld->ld_errno = LDAP_NO_MEMORY;
					return(-1);
				}
				L_res->lm_msgid = 0;
				L_res->lm_msgtype = tag;
				L_res->lm_ber = ber_dup( &tmpber );
				*result = L_res;

				/* It is a notice of disconnection
				 * Return immediatly with an error code to stop
				 * reading any new message and to prevent the use
				 */
				ld->ld_errno = LDAP_SERVER_DOWN;
				ldap_insert_notif(ld, new);		/* in head */
				return(-1);
			}
			else
			{
				/* This is another notification
				 *	Keep on the processing of received messages 
				 */
				ldap_add_notif(ld, new);			/* in tail */
				goto read_from_sb;
			}
		}
		else
		{
			Debug(LDAP_DEBUG_ANY, catgets(slapdcat, 1, 1673, "Error while decoding Extended Response message"), NULL, NULL, NULL);
			ld->ld_errno = LDAP_DECODING_ERROR;
			return(-1);
		}
	}
	else if (( lr = find_request_by_msgid( ld, id )) == NULL ) 
	{
		Debug( LDAP_DEBUG_ANY, catgets(slapdcat, 1, 229, "no request for response with msgid %ld (tossing)\n"), id, 0, 0 );
		free( ber.ber_buf );	/* gack! */
		return( -2 );	/* continue looking */
	}

	if (tag == LDAP_RES_SEARCH_ENTRY)
		msgtypestr = catgets(slapdcat, 1, 1281, "search entry");
	else if (tag == LDAP_RES_SEARCH_REFERENCE)
		msgtypestr = catgets(slapdcat, 1, 1282, "search reference");
	else 
		msgtypestr = catgets(slapdcat, 1, 1283, "result");
	
	Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 230, "got %1$s msgid %2$ld, original id %3$d\n"),
		   msgtypestr, id, lr->lr_origid );

	id = lr->lr_origid;

	/* REFERRALS HANDLING*/
	refer_cnt = 0;
	simple_request = 0;
	hadref = 0;
	rc = -2;	/* default is to keep looking (no response found) */
	lr->lr_res_msgtype = tag;

	if ( tag != LDAP_RES_SEARCH_ENTRY ) { /* If it's not an entry, ie it's a result or a reference */
		if ( ld->ld_version >= LDAP_VERSION2 &&
			    ( lr->lr_parent != NULL ||
				  ld->ld_follow_referral /* || ClientControl to follow referral present */ )) {
			tmpber = ber;
			if (tag == LDAP_RES_SEARCH_REFERENCE){
				/* LDAP V3 reference. Decode it */
				Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "LDAP search reference received. Will follow it later\n"),
					  0, 0,0);
				if (ber_scanf(&tmpber, "{v}", &theRefs) == LBER_ERROR){
					Debug ( LDAP_DEBUG_ANY, catgets(slapdcat, 1, 1284, "Error while decoding Search Reference Result message\n"),
							NULL, NULL, NULL);
					rc = -1;
					theRefs = NULL;
				} else {
					/* Store the referrals in request. We will follow them when the result arrives */
					Ref_AddToRequest(lr, theRefs);
					theRefs = NULL;
					free( ber.ber_buf );	/* gack! */
					ber.ber_buf = NULL;
					return (rc);
				}
			} else {
				if (ber_scanf( &tmpber, "{iaa", &lderr, &lr->lr_res_matched, &lr->lr_res_error) != LBER_ERROR){
					if (lderr == LDAP_PARTIAL_RESULTS){
						Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "LDAPv2 partial error received\n"), 0, 0,0);
						/* Ldapv2 referrals */
						theRefs = ldap_errormsg2referrals(lr->lr_res_error);
						ber_scanf(&tmpber, "}");
					} else if (lderr == LDAP_REFERRAL ){
						/* We have some referrals, decode them */
						Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "LDAPv3 referral error received\n"), 0, 0,0);
						if (ber_peek_tag ( &tmpber, &atag) == LDAP_TAG_REFERRAL){
							if (ber_scanf(&tmpber, "{v}}", &theRefs) == LBER_ERROR){
								Debug( LDAP_DEBUG_ANY, catgets(slapdcat, 1, 1285, "Error while decoding referrals in msg\n"),
									   NULL, NULL, NULL );
								rc = -1; /* ??? */
								theRefs = NULL;
							}
						} /* else error there should be at least one ref */						
					} else if (((lderr == LDAP_NO_SUCH_OBJECT) || 
							   (lderr == LDAP_BUSY) ||
							   (lderr == LDAP_UNAVAILABLE) ||
							   (lderr == LDAP_SERVER_DOWN) ||
							   (lderr == LDAP_CONNECT_ERROR)) && 
							   (lr->lr_parent != NULL) && /* its  subrequest */
							   (lr->lr_ref_tofollow != NULL)) { /* And it has some other referral to try */
						samereq = 1;
						theRefs = lr->lr_ref_tofollow;
						lr->lr_ref_tofollow = NULL;
						lrparent = lr->lr_parent;
						/* delete lr */
						free_request(ld, lr);
						/* lr now points on parent request */
						lr = lrparent;
						/* Follow referrals */
					} else {
						/* Here we have a simple result */
						hadref = lr->lr_outrefcnt;
					}
				} else {
					Debug( LDAP_DEBUG_ANY, catgets(slapdcat, 1, 1286, "Error while decoding result for request %$1d\n"),
						   lr->lr_origid, NULL, NULL);
					rc = -1; /* ??? */
				}
			}

			total_count = 0;
			if (tag != LDAP_RES_SEARCH_REFERENCE && lr->lr_references) {
				/* Some search references pending... Let's try to chase them */
				hadref = 1;
				theReferences = lr->lr_references;

				Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "Now following the search references received\n"),
					  0, 0,0);
				
				while (theReferences != NULL){
					if ((retcode = chase_referrals(ld, lr, theReferences->lref_refs , &refer_cnt, 0)) != LDAP_SUCCESS) {
						/* think about what to do */
						Debug( LDAP_DEBUG_ANY, catgets(slapdcat, 1, -1, "Error while chasing referral (%1$d)\n"),
							   retcode, NULL, NULL);
						theErrCode = LDAP_REFERRAL;
					}
					if (refer_cnt >= 0)
						total_count += refer_cnt;
					theReferences = theReferences->lref_next;
				}
				Ref_FreeAll(lr);
				if (theErrCode != LDAP_SUCCESS) {
					if (ld->ld_error != NULL && *ld->ld_error) {
						if (lr->lr_res_error)
							free(lr->lr_res_error);
						lr->lr_res_error = strdup(ld->ld_error);
					}
				}
				lr->lr_res_errno = theErrCode;
			}
			/* if theRefs != NULL we have some referrals to chase, do it */
			if (theRefs){
				hadref = 1;
				if ((retcode = chase_referrals(ld, lr, theRefs, &refer_cnt, samereq)) != LDAP_SUCCESS){
					/* think about what to do */
					Debug( LDAP_DEBUG_ANY, catgets(slapdcat, 1, -1, "Error while chasing referral (%1$d)\n"),
						   retcode, NULL, NULL);
				}
				
				if (refer_cnt >= 0)
					total_count += refer_cnt;

				ldap_value_free(theRefs);
				if (samereq){ /* Just tried another referral for same request */
					free(ber.ber_buf);
					ber.ber_buf = NULL;
					rc = -2;
					/* continue */
				}
				if (retcode != LDAP_SUCCESS) {
					if (ld->ld_version == LDAP_VERSION2){
						if (lr->lr_res_error)
							free(lr->lr_res_error);
						lr->lr_res_error = ldap_referral2error_msg(lr->lr_ref_unfollowed);
					} else if (ld->ld_error != NULL && *ld->ld_error) {
						if (lr->lr_res_error)
							free(lr->lr_res_error);
						lr->lr_res_error = strdup(ld->ld_error);
					}
				}
				lr->lr_res_errno = ld->ld_errno;
				
			}  else if (theErrCode == LDAP_SUCCESS) {
				/* no referral have been chased */
				lr->lr_res_errno = (lderr == LDAP_PARTIAL_RESULTS || lderr == LDAP_REFERRAL) ? LDAP_SUCCESS : lderr;
			}
			
			Debug( LDAP_DEBUG_TRACE,
				   catgets(slapdcat, 1, 231, "new result:  res_errno: %1$d, res_error: <%2$s>, res_matched: <%3$s>\n"),
				   lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
				   lr->lr_res_matched ? lr->lr_res_matched : "" );
		}
	
		
		Debug( LDAP_DEBUG_TRACE,
			   catgets(slapdcat, 1, 232, "read1msg:  %1$d new referrals\n"), total_count, 0, 0 );
		
		if ( refer_cnt != 0 ) {	/* chasing referrals */
			free( ber.ber_buf );	/* gack! */
			ber.ber_buf = NULL;
			if ( refer_cnt < 0 ) {
				return( -1 );	/* fatal error */
			}
			lr->lr_status = LDAP_REQST_CHASINGREFS;
		} else if (tag == LDAP_RES_SEARCH_REFERENCE && !ld->ld_follow_referral) {
			/* We had a ref and we don't follow referral : Do nothing there ?! */
			Debug( LDAP_DEBUG_TRACE,
				   catgets(slapdcat, 1, -1, "read1msg: returning search reference\n"), 0, 0, 0 );

		} else {
			/* No referral chasing */
			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 */
				free( ber.ber_buf );	/* gack! */
				ber.ber_buf = NULL;
			}


 			while ( lr->lr_parent != NULL ) {
 				merge_error_info( ld, lr->lr_parent, lr );
 				lr = lr->lr_parent;
 				if ( --lr->lr_outrefcnt > 0 ) {
 					break;	/* not completedly done yet */
 				}
 			}
			
			if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) { /* The main request has no more outstanding refs */
				id = lr->lr_msgid;
				tag = lr->lr_res_msgtype;
				Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 233, "request %1$ld done\n"),
				    id, 0, 0 );
				Debug( LDAP_DEBUG_TRACE,
					   catgets(slapdcat, 1, 234, "res_errno: %1$d, res_error: <%2$s>, res_matched: <%3$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 ) { /* We have to rebuild the result */
					if ( ber.ber_buf != NULL ) {
						free( ber.ber_buf ); /* gack! */
						ber.ber_buf = NULL;
					}
					if ( build_result_ber( ld, &ber, lr )
					    == LBER_ERROR ) {
						ld->ld_errno = LDAP_NO_MEMORY;
						rc = -1; /* fatal error */
					}
				}

				free_request( ld, lr );
			}

			if ( lc != NULL ) {
				free_connection( ld, lc, 0, 1 );
			}
		}
	}

	if ( ber.ber_buf == NULL ) { /* If the buffer has been freed, return */
		return( rc );
	}
	/* End of REFERRALS */

	/* make a new ldap message */
	if ( (new = (LDAPMessage *) calloc( 1, sizeof(LDAPMessage) ))
	    == NULL ) {
		ld->ld_errno = LDAP_NO_MEMORY;
		return( -1 );
	}
	new->lm_msgid = (int)id;
	new->lm_msgtype = tag;
	new->lm_ber = ber_dup( &ber );

#ifndef NO_CACHE
	if ( ld->ld_cache != NULL ) {
		add_result_to_cache( ld, new );
	}
#endif /* NO_CACHE */

	/* is this the one we're looking for? */
	if ( msgid == LDAP_RES_ANY || id == msgid ) {
		if ( all == LDAP_MSG_ONE	/* all apply only to search, so if not a search,return the val */
		    || (new->lm_msgtype != LDAP_RES_SEARCH_RESULT
				&& new->lm_msgtype != LDAP_RES_SEARCH_ENTRY
				&& new->lm_msgtype != LDAP_RES_SEARCH_REFERENCE) ) {
			*result = new;
			ld->ld_errno = LDAP_SUCCESS;
			return( tag );
		} else if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
			foundit = 1;	/* return the chain later */
		}
	}

	/* 
	 * 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 = NULLMSG;
	for ( l = ld->ld_responses; l != NULLMSG; l = l->lm_next ) {
		if ( l->lm_msgid == new->lm_msgid )
			break;
		prev = l;
	}

	/* not part of an existing search response */
	if ( l == NULLMSG ) {
		if ( foundit ) { /* it a search result anyway, so return it */
			*result = new;
			ld->ld_errno = LDAP_SUCCESS;
			return( tag );
		}

		new->lm_next = ld->ld_responses;
		ld->ld_responses = new;
		return( -2 );	/* continue looking */
	}

	Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 235, "adding response id %1$d type %2$d:\n"),
	    new->lm_msgid, new->lm_msgtype, 0 );

	/* part of a search response - add to end of list of entries or references */
	for ( tmp = l; tmp->lm_chain != NULLMSG &&
	    (tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
		 tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE);
	    tmp = tmp->lm_chain )
		;	/* NULL */
	tmp->lm_chain = new;

	/* return the whole chain if that's what we were looking for */
	if ( foundit ) {
		if ( prev == NULLMSG )
			ld->ld_responses = l->lm_next;
		else
			prev->lm_next = l->lm_next;
		*result = l;
		ld->ld_errno = LDAP_SUCCESS;
		return( l->lm_msgtype ); /* Patch 16 : was return(tag) */
	}

	return( -2 );	/* continue looking */
}


static int
build_result_ber( LDAP *ld, BerElement *ber, LDAPRequest *lr )
{
	unsigned int	len;
	int		along;

	Debug (LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 1287, "=> building_ber_error msgid %ld\n"), lr->lr_msgid, 0,0);
	ber_zero_init( ber, 0 );
	set_ber_options( ld, ber );
	if (ld->ld_version == LDAP_VERSION3){
		if ( ber_printf( ber, "{it{ess", 
						 lr->lr_msgid, 
						 lr->lr_res_msgtype,
						 lr->lr_res_errno,
						 lr->lr_res_matched ? lr->lr_res_matched : "",
						 lr->lr_res_error ? lr->lr_res_error : "" ) == LBER_ERROR){
			return (LBER_ERROR);
		}
		if (lr->lr_res_errno == LDAP_REFERRAL && 
			ber_printf(ber, "t{v}", LDAP_TAG_REFERRAL, lr->lr_ref_unfollowed) == LBER_ERROR){
			return (LBER_ERROR);
		}
		if (ber_printf(ber, "}}") == LBER_ERROR){
			return (LBER_ERROR);
		}
	} else {
		if ( ber_printf( ber, "{it{ess}}", 
						 lr->lr_msgid,
						 lr->lr_res_msgtype, 
						 lr->lr_res_errno,
						 lr->lr_res_matched ? lr->lr_res_matched : "",
						 lr->lr_res_error ? lr->lr_res_error : "" ) == LBER_ERROR ) {
			return( LBER_ERROR );
		}
	}
	
	ber_reset( ber, 1 );
	if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
		return( LBER_ERROR );
	}

	if ( ber_get_int( ber, &along ) == LBER_ERROR ) {
		return( LBER_ERROR );
	}

	return( ber_peek_tag( ber, &len ));
}


static void
merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
{
	int i, j;
/*
 * 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)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 ) {
			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 ) {
				free( parentr->lr_res_matched );
			}
			parentr->lr_res_matched = lr->lr_res_matched;
			lr->lr_res_matched = NULL;
		}
		if (lr->lr_ref_unfollowed != NULL){
			for (i=0;lr->lr_ref_unfollowed[i] != NULL; i++);
			j = 0;
			if (parentr->lr_ref_unfollowed != NULL){
				for (j=0;parentr->lr_ref_unfollowed[j]!= NULL ;j++);
				j++;
			}
			parentr->lr_ref_unfollowed = (char **)realloc (parentr->lr_ref_unfollowed, (j+i+1) * sizeof(char *));
			if (parentr->lr_ref_unfollowed != NULL){
				for (i = 0; lr->lr_ref_unfollowed[i] != NULL; i++){
					parentr->lr_ref_unfollowed[j+i] = lr->lr_ref_unfollowed[i];
					lr->lr_ref_unfollowed[i] = NULL;
				}
				parentr->lr_ref_unfollowed[i+j+1] = NULL;
			} else {
				if (parentr->lr_res_errno == LDAP_SUCCESS)
					parentr->lr_res_errno =  LDAP_NO_MEMORY;
			}
		}
	}

	Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 236, "merged parent (id %1$d) error info:  "),
	    parentr->lr_msgid, 0, 0 );
	Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 237, "result errno %1$d, error <%2$s>, matched <%3$s>\n"),
		   parentr->lr_res_errno, 
		   parentr->lr_res_error ? parentr->lr_res_error : "", 
		   parentr->lr_res_matched ? parentr->lr_res_matched : "" );
}

#ifdef CLDAP
#if !defined( MACOS ) && !defined( DOS ) && !defined( _WIN32 )
static int
ldap_select1( LDAP *ld, struct timeval *timeout )
{
	fd_set		readfds;
	static int	tblsize;

	if ( tblsize == 0 ) {
#ifdef USE_SYSCONF
		tblsize = (int) sysconf( _SC_OPEN_MAX );
#else /* USE_SYSCONF */
		tblsize = getdtablesize();
#endif /* USE_SYSCONF */
	}

	FD_ZERO( &readfds );
	FD_SET( ld->ld_sb.sb_sd, &readfds );

	return( select( tblsize, &readfds, 0, 0, timeout ) );
}
#endif /* !MACOS */


#ifdef MACOS
static int
ldap_select1( LDAP *ld, struct timeval *timeout )
{
	return( tcpselect( ld->ld_sb.sb_sd, timeout ));
}
#endif /* MACOS */


#if ( defined( DOS ) && defined( WINSOCK )) || defined( _WIN32 )
static int
ldap_select1( LDAP *ld, struct timeval *timeout )
{
    fd_set          readfds;
    int             rc;

    FD_ZERO( &readfds );
    FD_SET( ld->ld_sb.sb_sd, &readfds );

    rc = select( 1, &readfds, 0, 0, timeout );
    return( rc == SOCKET_ERROR ? -1 : rc );
}
#endif /* WINSOCK || _WIN32 */


#ifdef DOS
#ifdef PCNFS
static int
ldap_select1( LDAP *ld, struct timeval *timeout )
{
	fd_set	readfds;
	int	res;

	FD_ZERO( &readfds );
	FD_SET( ld->ld_sb.sb_sd, &readfds );

	res = select( FD_SETSIZE, &readfds, NULL, NULL, timeout );
	if ( res == -1 && errno == EINTR) {
		/* We've been CTRL-C'ed at this point.  It'd be nice to
		   carry on but PC-NFS currently won't let us! */
		printf("\n*** CTRL-C ***\n");
		exit(-1);
	}
	return( res );
}
#endif /* PCNFS */

#ifdef NCSA
static int
ldap_select1( LDAP *ld, struct timeval *timeout )
{
	int rc;
	clock_t	endtime;

	if ( timeout != NULL ) {
		endtime = timeout->tv_sec * CLK_TCK +
			timeout->tv_usec * CLK_TCK / 1000000 + clock();
	}

	do {
		Stask();
		rc = netqlen( ld->ld_sb.sb_sd );
	} while ( rc <= 0 && ( timeout == NULL || clock() < endtime ));

	return( rc > 0 ? 1 : 0 );
}
#endif /* NCSA */
#endif /* DOS */
#endif /* CLDAP */


int
ldap_msgfree( LDAPMessage *lm )
{
	LDAPMessage	*next;
	int		type = 0;

	Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 238, "ldap_msgfree\n"), 0, 0, 0 );

	for ( ; lm != NULLMSG; lm = next ) {
		next = lm->lm_chain;
		type = lm->lm_msgtype;
		if (lm->lm_ber) 
			ber_free( lm->lm_ber, 1 );
		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;

#ifdef _REENTRANT
	LOCK_LDAP(ld);
	LOCK_RESPONSE(ld);
#endif
	Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 239, "ldap_msgdelete\n"), 0, 0, 0 );

	prev = NULLMSG;
	for ( lm = ld->ld_responses; lm != NULLMSG; lm = lm->lm_next ) {
		if ( lm->lm_msgid == msgid )
			break;
		prev = lm;
	}

	if ( lm == NULLMSG ) {
#ifdef _REENTRANT
		UNLOCK_LDAP(ld);
		UNLOCK_RESPONSE(ld);
#endif
		return( -1 );
	}

	if ( prev == NULLMSG )
		ld->ld_responses = lm->lm_next;
	else
		prev->lm_next = lm->lm_next;

	if ( ldap_msgfree( lm ) == LDAP_RES_SEARCH_ENTRY ) {
#ifdef _REENTRANT
		UNLOCK_LDAP(ld);
		UNLOCK_RESPONSE(ld);
#endif
		return( -1 );
	}

#ifdef _REENTRANT
	UNLOCK_LDAP(ld);
	UNLOCK_RESPONSE(ld);
#endif
	return( 0 );
}


/*
 * return 1 if message msgid is waiting to be abandoned, 0 otherwise
 */
static int
ldap_abandoned( LDAP *ld, int msgid )
{
	int	i;

	if ( ld == NULL ) return(1);
	if ( ld->ld_abandoned == NULL )
		return( 0 );

	for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
		if ( ld->ld_abandoned[i] == msgid )
			return( 1 );

	return( 0 );
}


static int
ldap_mark_abandoned( LDAP *ld, int msgid )
{
	int	i;

	if ( ld->ld_abandoned == NULL )
		return( -1 );

	for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
		if ( ld->ld_abandoned[i] == msgid )
			break;

	if ( ld->ld_abandoned[i] == -1 )
		return( -1 );

	for ( ; ld->ld_abandoned[i] != -1; i++ ) {
		ld->ld_abandoned[i] = ld->ld_abandoned[i + 1];
	}

	return( 0 );
}


#ifdef CLDAP
int
cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement *ber )
{
	int		rc;
	unsigned int	tag, len;

#ifdef _REENTRANT
	LOCK_LDAP(ld);
#endif
	if ( ld->ld_sb.sb_ber.ber_ptr >= ld->ld_sb.sb_ber.ber_end ) {
		rc = ldap_select1( ld, timeout );
		if ( rc == -1 || rc == 0 ) {
			ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
			    LDAP_TIMEOUT);
#ifdef _REENTRANT
			UNLOCK_LDAP(ld);
#endif
			return( rc );
		}
	}

	/* get the next message */
	if ( (tag = ber_get_next( &ld->ld_sb, &len, ber ))
	    != LDAP_TAG_MESSAGE ) {
		ld->ld_errno = (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
		    LDAP_LOCAL_ERROR);
#ifdef _REENTRANT
		UNLOCK_LDAP(ld);
#endif
		return( -1 );
	}

#ifdef _REENTRANT
	UNLOCK_LDAP(ld);
#endif
	return( tag );
}
#endif /* CLDAP */

/* ldapv3 API extensions */

int ldap_msgtype(LDAPMessage *res)
{
	if (res == NULL)
		return (LDAP_RES_ANY);
	return (res->lm_msgtype);
}


int ldap_msgid(LDAPMessage *res)
{
	if (res == NULL)
		return (LDAP_RES_ANY);
	return (res->lm_msgid);
}

int ldap_parse_result(LDAP *ld, LDAPMessage *res, int *errcodep, char **matcheddnp,
					  char **errmsgp, char ***referralsp, LDAPControl ***serverctrlsp,
					  int freeit)
{
	LDAPMessage *lm;
	BerElement ber;
	unsigned int alen;
	int along;
	unsigned int tag;
	int i;
	size_t rc;
	char * acharp = NULL, * a2ndcharp = NULL;
	char ** arefs = NULL;
	
	Debug( LDAP_DEBUG_TRACE, "ldap_parse_result\n", 0, 0, 0 );

	if (res == NULLMSG)
		return (LDAP_PARAM_ERROR);

	if (matcheddnp && *matcheddnp){
		free(*matcheddnp);
		*matcheddnp = NULL;
	}
	if (errmsgp && *errmsgp){
		free(*errmsgp);
		*errmsgp = NULL;
	}
	if (referralsp && *referralsp){
		free_strarray(*referralsp);
		*referralsp = NULL;
	}
	
	if (serverctrlsp && *serverctrlsp){
		ldap_controls_free(*serverctrlsp);
		*serverctrlsp = NULL;
	}
	
	for (lm = res; lm->lm_chain != NULL; lm = lm->lm_chain)

		if ( lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY 
			 && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE)
			break;

	ber = *(lm->lm_ber);
	
#ifdef _REENTRANT
	LOCK_LDAP(ld);
#endif
	if (ld->ld_version == LDAP_VERSION3) {
		rc = ber_scanf( &ber, "{iaa", &along, &acharp, &a2ndcharp);
		if (rc == LBER_ERROR){
			if (freeit)
				ldap_msgfree( res );
#ifdef _REENTRANT
			UNLOCK_LDAP(ld);
#endif
			return (LDAP_DECODING_ERROR);
		}
		if (matcheddnp) {
			*matcheddnp = acharp;
		} else {
			ldap_memfree(acharp);
		}
		if (errmsgp) {
			*errmsgp = a2ndcharp;
		} else {
			ldap_memfree(a2ndcharp);
		}
				
		if (errcodep) {
			*errcodep = along;
		}

		if (along == LDAP_REFERRAL){
			if (ber_peek_tag ( &ber, &tag) == LDAP_TAG_REFERRAL) {
				rc = ber_scanf(&ber, "{v}", &arefs);
				if (rc == LBER_ERROR){
					/* try to free other stuff */
					if (freeit)
						ldap_msgfree( res );
#ifdef _REENTRANT
					UNLOCK_LDAP(ld);
#endif
					return (LDAP_DECODING_ERROR);
				}
				if (referralsp) {
					*referralsp = arefs;
				} else {
					for (i = 0; arefs[i] != NULL; i++)
						ldap_memfree(arefs[i]);
					ldap_memfree((char *)arefs);
				}
			} else {
				/* referral errcode without URL is forbiden */
				if (freeit)
					ldap_msgfree( res );
#ifdef _REENTRANT
				UNLOCK_LDAP(ld);
#endif
				return (LDAP_DECODING_ERROR);
			}
		}
		rc = ber_scanf(&ber, "}");
		if (rc == LBER_ERROR){
			if (freeit)
				ldap_msgfree( res );
#ifdef _REENTRANT
			UNLOCK_LDAP(ld);
#endif
			return (LDAP_DECODING_ERROR);
		}
		/* It's the end of the result but the PDU may have controls */
		if (serverctrlsp && (ber_peek_tag(&ber, &alen) == LDAP_TAG_CONTROL_LIST)) {
			Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 392, "Controls found in result\n"), 0, 0, 0 );
			*serverctrlsp =  ldap_controls_decode(&ber,
							    (int *)&rc);
			if (*serverctrlsp == NULL) {
				if (freeit)
					ldap_msgfree( res );
#ifdef _REENTRANT
				UNLOCK_LDAP(ld);
#endif
				return (LDAP_DECODING_ERROR);
			}
		} else {
			Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 393, "NO controls found in result\n"), 0, 0, 0 );
		}
	}
	else if (ld->ld_version == LDAP_VERSION2) {
		rc = ber_scanf( &ber, "{iaa}", &along, &acharp,
		     &a2ndcharp );
		if (rc == LBER_ERROR){
			if (freeit)
				ldap_msgfree( res );
#ifdef _REENTRANT
			UNLOCK_LDAP(ld);
#endif
			return (LDAP_DECODING_ERROR);
		}
		if (matcheddnp) {
			*matcheddnp = acharp;
		} else {
			ldap_memfree(acharp);
		}
		if (errmsgp) {
			*errmsgp = a2ndcharp;
		} else {
			ldap_memfree(a2ndcharp);
		}
		if (errcodep) {
			*errcodep = along;
		}
	}
	else {
		rc = ber_scanf( &ber, "{ia}", &along, &a2ndcharp );
		if (rc == LBER_ERROR){
			if (freeit)
				ldap_msgfree( res );
#ifdef _REENTRANT
			UNLOCK_LDAP(ld);
#endif
			return (LDAP_DECODING_ERROR);
		}
		
		if (errmsgp) {
			*errmsgp = a2ndcharp;
		} else {
			ldap_memfree(a2ndcharp);
		}
		if (errcodep) {
			*errcodep = along;
		}
	}
	
	if ( freeit )
		ldap_msgfree(res);
	
#ifdef _REENTRANT
	UNLOCK_LDAP(ld);
#endif
	return (LDAP_SUCCESS);
}

int ldap_parse_sasl_bind_result(LDAP *ld, LDAPMessage *res, struct berval **servercredp, int freeit)
{
	LDAPMessage *lm;
	BerElement ber;
	int along;
	unsigned int tag;
	int i;
	size_t rc;
	char * acharp = NULL, *a2ndcharp = NULL;
	char ** arefs = NULL;
	struct berval * creds = NULL;
	
	Debug( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n", 0, 0, 0 );

	if (res == NULLMSG)
		return (LDAP_PARAM_ERROR);
	
#ifdef _REENTRANT
	LOCK_LDAP(ld);
#endif
	if ((res->lm_msgtype != LDAP_RES_BIND) || (ld->ld_version != LDAP_VERSION3)){
		ld->ld_errno = LDAP_PARAM_ERROR;
#ifdef _REENTRANT
		UNLOCK_LDAP(ld);
#endif
		return (LDAP_PARAM_ERROR);
	}
#ifdef _REENTRANT
	UNLOCK_LDAP(ld);
#endif
	
	ber = *(res->lm_ber);
	rc = ber_scanf( &ber, "{iaa", &along, &acharp, &a2ndcharp);
	if (rc == LBER_ERROR){
		if (freeit)
			ldap_msgfree( res );
#ifdef _REENTRANT
		LOCK_LDAP(ld);
#endif
		ld->ld_errno = LDAP_DECODING_ERROR;
#ifdef _REENTRANT
		UNLOCK_LDAP(ld);
#endif
		return (LDAP_DECODING_ERROR);
	}
	ldap_memfree(acharp);
	ldap_memfree(a2ndcharp);
	if (along == LDAP_SUCCESS || along == LDAP_SASL_BIND_INPROGRESS){
		/* Decode the serverSaslCreds if any */
		if (ber_peek_tag ( &ber, &tag) == LDAP_TAG_SASLCREDS) {
			rc = ber_get_stringal( &ber, &creds);
			if (rc == LBER_ERROR ){
				if (freeit)
					ldap_msgfree(res);
#ifdef _REENTRANT
				LOCK_LDAP(ld);
#endif
				ld->ld_errno = LDAP_DECODING_ERROR;
#ifdef _REENTRANT
				UNLOCK_LDAP(ld);
#endif
				return (LDAP_DECODING_ERROR);
			}
			if (servercredp) {
				*servercredp = creds;
			} else {
				ber_bvfree( creds );
			}
		}
	} else if (along == LDAP_REFERRAL) {
		if (ber_peek_tag ( &ber, &tag) == LDAP_TAG_REFERRAL){
			rc = ber_scanf(&ber, "{v}", &arefs);
			if (rc == LBER_ERROR){
				/* try to free other stuff */
				if (freeit)
					ldap_msgfree( res );
#ifdef _REENTRANT
				LOCK_LDAP(ld);
#endif
				ld->ld_errno = LDAP_DECODING_ERROR;
#ifdef _REENTRANT
				UNLOCK_LDAP(ld);
#endif
				return (LDAP_DECODING_ERROR);
			}
			for (i = 0; arefs[i] != NULL; i++)
				ldap_memfree(arefs[i]);
			ldap_memfree((char *)arefs);
		} else {
			/* There should be at least one ref */
			if (freeit)
				ldap_msgfree( res );
#ifdef _REENTRANT
			LOCK_LDAP(ld);
#endif
			ld->ld_errno = LDAP_DECODING_ERROR;
#ifdef _REENTRANT
			UNLOCK_LDAP(ld);
#endif
			return (LDAP_DECODING_ERROR);
		}
	}
		
	rc = ber_scanf(&ber, "}");
	if (rc == LBER_ERROR){
		if (freeit)
			ldap_msgfree( res );
#ifdef _REENTRANT
		LOCK_LDAP(ld);
#endif
		ld->ld_errno = LDAP_DECODING_ERROR;
#ifdef _REENTRANT
		UNLOCK_LDAP(ld);
#endif
		return (LDAP_DECODING_ERROR);
	}
	
	if ( freeit )
		ldap_msgfree(res);
#ifdef _REENTRANT
	LOCK_LDAP(ld);
#endif
	ld->ld_errno = along;
#ifdef _REENTRANT
	UNLOCK_LDAP(ld);
#endif
	return (along);
}

int ldap_parse_extended_result(LDAP *ld, LDAPMessage *res, char **resultoidp, 
							   struct berval **resultdata, int freeit)
{
	LDAPMessage *lm;
	BerElement ber;
	int along;
	unsigned int tag;
	int i;
	size_t rc;
	char * acharp = NULL, *a2ndcharp = NULL, *anoid = NULL;
	char **arefs = NULL;
	struct berval * aresp = NULL;
	
	Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );

	if ( res == NULLMSG )
		return (LDAP_PARAM_ERROR);
		
#ifdef _REENTRANT
	LOCK_LDAP(ld);
#endif
	if ((res->lm_msgtype != LDAP_RES_EXTENDED) || (ld->ld_version != LDAP_VERSION3))
	{
		if ( res->lm_msgid != 0 )
#ifdef _REENTRANT
			UNLOCK_LDAP(ld);
#endif
			return (LDAP_PARAM_ERROR);
	}
#ifdef _REENTRANT
	UNLOCK_LDAP(ld);
#endif
	
	ber = *(res->lm_ber);
	rc = ber_scanf( &ber, "{iaa", &along, &acharp, &a2ndcharp);
	if (rc == LBER_ERROR){
		if (freeit)
			ldap_msgfree( res );
		return (LDAP_DECODING_ERROR);
	}
	ldap_memfree(acharp);
	ldap_memfree(a2ndcharp);

	if (along == LDAP_REFERRAL) {
		if (ber_peek_tag ( &ber, &tag) == LDAP_TAG_REFERRAL){
			rc = ber_scanf(&ber, "{v}", &arefs);
			if (rc == LBER_ERROR){
				/* try to free other stuff */
				if (freeit)
					ldap_msgfree( res );
				return (LDAP_DECODING_ERROR);
			}
			for (i = 0; arefs[i] != NULL; i++)
				ldap_memfree(arefs[i]);
			ldap_memfree((char *)arefs);
		} else {
			/* There should be at least one ref */
			if (freeit)
				ldap_msgfree( res );
			return (LDAP_DECODING_ERROR);
		}
	}

	if (ber_peek_tag ( &ber, &tag) == LDAP_TAG_EXT_RESPNAME) {
		rc = ber_get_stringa( &ber, &anoid);
		if (rc == LBER_ERROR ){
			if (freeit)
				ldap_msgfree(res);
			return (LDAP_DECODING_ERROR);
		}
		if (resultoidp) {
			*resultoidp = anoid;
		} else {
			ldap_memfree( anoid );
		}
	}
	if (ber_peek_tag ( &ber, &tag) == LDAP_TAG_EXT_RESPONSE) {
		rc = ber_get_stringal( &ber, &aresp);
		if (rc == LBER_ERROR ){
			if (freeit)
				ldap_msgfree(res);
			return (LDAP_DECODING_ERROR);
		}
		if (resultdata) {
			*resultdata = aresp;
		} else {
			ber_bvfree( aresp );
		}
	}
		
	rc = ber_scanf(&ber, "}");
	if (rc == LBER_ERROR){
		if (freeit)
			ldap_msgfree( res );
		return (LDAP_DECODING_ERROR);
	}
	
	if ( freeit )
		ldap_msgfree(res);
	
	return (along);
}


static int Ref_AddToRequest(LDAPRequest *lr, char **refs) {
	int count;
	LDAPRef *lref;
	LDAPRef *newRef;
	
	if ((newRef = (LDAPRef *)calloc(1, sizeof (LDAPRef))) == NULL){
		return LDAP_NO_MEMORY;
	}
	newRef->lref_refs = refs;
	newRef->lref_next = NULL;
	lref = lr->lr_references;
	if (lref == NULL){
		lr->lr_references = newRef;
	} else {
		while (lref->lref_next != NULL)
			lref = lref->lref_next;
		lref->lref_next = newRef;
	}
	return LDAP_SUCCESS;
}

static void Ref_FreeAll(LDAPRequest *lr)
{
	LDAPRef *lref, *next;
	lref = lr->lr_references;
	while (lref != NULL){
		next = lref->lref_next;
		ldap_value_free(lref->lref_refs);
		free (lref);
		lref = next;
	}
	lr->lr_references = NULL;
}