/*==========================================================================
 *
 *  Copyright (C) 1998-2002 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       WSockSP.cpp
 *  Content:	Protocol-independent APIs for the DN Winsock SP
 *
 *
 *  History:
 *   Date		By		Reason
 *   ====		==		======
 *	10/26/1998	jwo		Created it.
 *	11/1/1998	jwo		Un-subclassed everything (moved it to this generic
 *						file from IP and IPX specific ones
 *	03/22/2000	jtk		Updated with changes to interface names
 *	04/22/2000	mjn		Allow all flags in DNSP_GetAddressInfo()
 *	08/06/2000	RichGr	IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
 *	03/12/2001	mjn		Prevent enum responses from being indicated up after completion
 ***************************************************************************/

#include "dnwsocki.h"



//**********************************************************************
// Constant definitions
//**********************************************************************

//
// maximum bandwidth in bits per second
//
#define	UNKNOWN_BANDWIDTH	0

#define WAIT_FOR_CLOSE_TIMEOUT 30000		// milliseconds

#define	ADDRESS_ENCODE_KEY	0

//**********************************************************************
// Macro definitions
//**********************************************************************

//**********************************************************************
// Structure definitions
//**********************************************************************

//**********************************************************************
// Variable definitions
//**********************************************************************

//**********************************************************************
// Function prototypes
//**********************************************************************

//**********************************************************************
// Function definitions
//**********************************************************************


//**********************************************************************
/*
 *
 *	DNSP_Initialize initializes the instance of the SP.  It must be called
 *		at least once before using any other functions.  Further attempts
 *		to initialize the SP are ignored.
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_Initialize"

STDMETHODIMP DNSP_Initialize( IDP8ServiceProvider *pThis, SPINITIALIZEDATA *pData )
{
	HRESULT			hr;
	CSPData			*pSPData;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pData);

	DNASSERT( pThis != NULL );
	DNASSERT( pData != NULL );

	//
	// initialize
	//
	hr = DPN_OK;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	// Trust protocol to call us only in the uninitialized state
	DNASSERT( pSPData->GetState() == SPSTATE_UNINITIALIZED );

	//
	// prevent anyone else from messing with this interface
	//
	pSPData->Lock();

	hr = pSPData->Startup( pData );
	if (hr != DPN_OK)
	{
		goto Failure;
	}

	pSPData->Unlock();

	IDP8ServiceProvider_AddRef( pThis );

Exit:

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);
	
	return hr;

Failure:
	pSPData->Unlock();
	goto Exit;
}
//**********************************************************************


//**********************************************************************
/*
 *
 *	DNSP_Close is the opposite of Initialize.  Call it when you're done
 *		using the SP
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_Close"

STDMETHODIMP DNSP_Close( IDP8ServiceProvider *pThis )
{
	HRESULT		hr;
	CSPData		*pSPData;
	
	
	DPFX(DPFPREP, 2, "Parameters: (0x%p)", pThis);

	DNASSERT( pThis != NULL );

	//
	// initialize
	//
	hr = DPN_OK;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	pSPData->Shutdown();
	IDP8ServiceProvider_Release( pThis );
			
	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return hr;
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// DNSP_AddRef - increment reference count
//
// Entry:		Pointer to interface
//
// Exit:		New reference count
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_AddRef"

STDMETHODIMP_(ULONG) DNSP_AddRef( IDP8ServiceProvider *pThis )
{	
	CSPData *	pSPData;
	ULONG		ulResult;


	DPFX(DPFPREP, 2, "Parameters: (0x%p)", pThis);

	DNASSERT( pThis != NULL );
	pSPData = CSPData::SPDataFromCOMInterface( pThis );
	
	ulResult = pSPData->AddRef();

	
	DPFX(DPFPREP, 2, "Returning: [0x%u]", ulResult);

	return ulResult;
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// DNSP_Release - decrement reference count
//
// Entry:		Pointer to interface
//
// Exit:		New reference count
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_Release"

STDMETHODIMP_(ULONG) DNSP_Release( IDP8ServiceProvider *pThis )
{
	CSPData *	pSPData;
	ULONG		ulResult;

	
	DPFX(DPFPREP, 2, "Parameters: (0x%p)", pThis);

	DNASSERT( pThis != NULL );
	pSPData = CSPData::SPDataFromCOMInterface( pThis );
	
	ulResult = pSPData->DecRef();

	
	DPFX(DPFPREP, 2, "Returning: [0x%u]", ulResult);

	return ulResult;
}
//**********************************************************************


//**********************************************************************
/*
 *
 *	DNSP_EnumQuery  sends out the
 *		specified data to the specified address.  If the SP is unable to
 *		determine the address based on the input params, it checks to see
 *		if it's allowed to put up a dialog querying the user for address
 *		info.  If it is, it queries the user for address info.
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_EnumQuery"

STDMETHODIMP DNSP_EnumQuery( IDP8ServiceProvider *pThis, SPENUMQUERYDATA *pEnumQueryData)
{
	HRESULT					hr;
	CEndpoint				*pEndpoint;
	CCommandData			*pCommand;
	BOOL					fEndpointOpen;
	CSPData					*pSPData;
#ifndef DPNBUILD_NONATHELP
	DWORD					dwTraversalMode;
	DWORD					dwComponentSize;
	DWORD					dwComponentType;
#endif // ! DPNBUILD_NONATHELP
#ifdef DBG
	DWORD					dwAllowedFlags;
	DWORD					dwTotalBufferSize;
	DWORD					dwTemp;
#endif // DBG


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pEnumQueryData);

	DNASSERT( pThis != NULL );
	DNASSERT( pEnumQueryData != NULL );
	DNASSERT( pEnumQueryData->pAddressHost != NULL );
	DNASSERT( pEnumQueryData->pAddressDeviceInfo != NULL );

#ifdef DBG
	dwAllowedFlags = DPNSPF_NOBROADCASTFALLBACK | DPNSPF_SESSIONDATA;
#ifndef DPNBUILD_NOSPUI
	dwAllowedFlags |= DPNSPF_OKTOQUERY;
#endif // ! DPNBUILD_NOSPUI
#ifndef DPNBUILD_ONLYONEADAPTER
	dwAllowedFlags |= DPNSPF_ADDITIONALMULTIPLEXADAPTERS;
#endif // ! DPNBUILD_ONLYONEADAPTER

	DNASSERT( ( pEnumQueryData->dwFlags & ~( dwAllowedFlags ) ) == 0 );

	if ( pEnumQueryData->dwFlags & DPNSPF_SESSIONDATA )
	{
		DNASSERT( pEnumQueryData->pvSessionData!= NULL );
		DNASSERT( pEnumQueryData->dwSessionDataSize > 0 );
	}
#endif // DBG

	DBG_CASSERT( sizeof( pEnumQueryData->dwRetryInterval ) == sizeof( DWORD ) );


#ifndef DPNBUILD_NOREGISTRY
	//
	// Make sure someone isn't getting silly.
	//
	if ( g_fIgnoreEnums )
	{
		DPFX(DPFPREP, 0, "Trying to initiate an enumeration when registry option to ignore all enums/response is set!");
		DNASSERT( ! "Trying to initiate an enumeration when registry option to ignore all enums/response is set!" );
	}
#endif // ! DPNBUILD_NOREGISTRY
	

	//
	// initialize
	//
	hr = DPNERR_PENDING;
	pEndpoint = NULL;
	pCommand = NULL;
	fEndpointOpen = FALSE;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );
	DNASSERT( pSPData != NULL );

	pEnumQueryData->hCommand = NULL;
	pEnumQueryData->dwCommandDescriptor = NULL_DESCRIPTOR;

	DumpAddress( 8, _T("Enum destination:"), pEnumQueryData->pAddressHost );
	DumpAddress( 8, _T("Enuming on device:"), pEnumQueryData->pAddressDeviceInfo );


	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );
	

	//
	// the user is attempting an operation that relies on the thread pool, lock
	// it down to prevent threads from being lost.  This also performs other
	// first time initialization.
	//
	hr = pSPData->GetThreadPool()->PreventThreadPoolReduction();
	if ( hr != DPN_OK )
	{
		DPFX(DPFPREP, 0, "Failed to prevent thread pool reduction!" );
		goto Failure;
	}


#ifdef DBG
	//
	// Make sure message is not too large.
	//
	dwTotalBufferSize = 0;
	for(dwTemp = 0; dwTemp < pEnumQueryData->dwBufferCount; dwTemp++)
	{
		dwTotalBufferSize += pEnumQueryData->pBuffers[dwTemp].dwBufferSize;
	}

#ifdef DPNBUILD_NOREGISTRY
	DNASSERT(dwTotalBufferSize <= DEFAULT_MAX_ENUM_DATA_SIZE);
#else // ! DPNBUILD_NOREGISTRY
	DNASSERT(dwTotalBufferSize <= g_dwMaxEnumDataSize);
#endif // ! DPNBUILD_NOREGISTRY
#endif // DBG


	//
	// create and new endpoint
	//
	pEndpoint = pSPData->GetNewEndpoint();
	if ( pEndpoint == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		DPFX(DPFPREP, 0, "Cannot create new endpoint in DNSP_EnumQuery!" );
		goto Failure;
	}

	
#ifndef DPNBUILD_NONATHELP
	//
	// We need to detect up front whether NAT traversal is disabled or not so we can optimize
	// the Open call below.
	//
	dwComponentSize = sizeof(dwTraversalMode);
	hr = IDirectPlay8Address_GetComponentByName(pEnumQueryData->pAddressDeviceInfo,
												DPNA_KEY_TRAVERSALMODE,
												&dwTraversalMode,
												&dwComponentSize,
												&dwComponentType);
	if ( hr == DPN_OK )
	{
		//
		// We found the component.  Make sure it's the right size and type.
		//
		if ((dwComponentSize == sizeof(dwTraversalMode)) && (dwComponentType == DPNA_DATATYPE_DWORD))
		{
			switch (dwTraversalMode)
			{
				case DPNA_TRAVERSALMODE_NONE:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is NONE.");
					break;
				}

				case DPNA_TRAVERSALMODE_PORTREQUIRED:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is PORTREQUIRED.");
					break;
				}

				case DPNA_TRAVERSALMODE_PORTRECOMMENDED:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is PORTRECOMMENDED.");
					break;
				}

				default:
				{
					DPFX(DPFPREP, 0, "Ignoring correctly formed traversal mode key with invalid value %u!  Using default mode %u.",
						dwTraversalMode, g_dwDefaultTraversalMode);
					dwTraversalMode = g_dwDefaultTraversalMode;
					break;
				}
			}
		}
		else
		{
			DPFX(DPFPREP, 0, "Traversal mode key exists, but doesn't match expected type (%u != %u) or size (%u != %u)!  Using default mode %u.",
				dwComponentSize, sizeof(dwTraversalMode),
				dwComponentType, DPNA_DATATYPE_DWORD,
				g_dwDefaultTraversalMode);
			dwTraversalMode = g_dwDefaultTraversalMode;
		}
	}
	else
	{
		//
		// The key is not there, it's the wrong size (too big for our buffer
		// and returned BUFFERTOOSMALL), or something else bad happened.
		// It doesn't matter.  Carry on.
		//
		DPFX(DPFPREP, 8, "Could not get traversal mode key, error = 0x%lx, component size = %u, type = %u, using default mode %u.",
			hr, dwComponentSize, dwComponentType, g_dwDefaultTraversalMode);
		dwTraversalMode = g_dwDefaultTraversalMode;
	}
	
	if (g_dwDefaultTraversalMode & FORCE_TRAVERSALMODE_BIT)
	{
		DPFX(DPFPREP, 1, "Forcing traversal mode %u.");
		dwTraversalMode = g_dwDefaultTraversalMode & (~FORCE_TRAVERSALMODE_BIT);
	}
	
	pEndpoint->SetUserTraversalMode(dwTraversalMode);
#endif // ! DPNBUILD_NONATHELP


	//
	// get new command and initialize it
	//
	pCommand = (CCommandData*)g_CommandDataPool.Get();
	if ( pCommand == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		DPFX(DPFPREP, 0, "Cannot get command handle for DNSP_EnumQuery!" );
		goto Failure;
	}
	
	DPFX(DPFPREP, 7, "(0x%p) Enum query command 0x%p created.",
		pSPData, pCommand);

	pEnumQueryData->hCommand = pCommand;
	pEnumQueryData->dwCommandDescriptor = pCommand->GetDescriptor();
	pCommand->SetType( COMMAND_TYPE_ENUM_QUERY );
	pCommand->SetState( COMMAND_STATE_PENDING );
	pCommand->SetEndpoint( pEndpoint );

	//
	// open endpoint with outgoing address
	//
	fEndpointOpen = TRUE;
	hr = pEndpoint->Open( ENDPOINT_TYPE_ENUM,
						pEnumQueryData->pAddressHost,
						((pEnumQueryData->dwFlags & DPNSPF_SESSIONDATA) ? pEnumQueryData->pvSessionData: NULL),
						((pEnumQueryData->dwFlags & DPNSPF_SESSIONDATA) ? pEnumQueryData->dwSessionDataSize : 0),
						NULL );
	switch ( hr )
	{
		//
		// Incomplete address passed in, query user for more information if
		// we're allowed.  If we're on IPX (no dialog available), don't attempt
		// to display the dialog, skip to checking for broadcast fallback.
		// Since we don't have a complete address at this time,
		// don't bind this endpoint to the socket port!
		//
		case DPNERR_INCOMPLETEADDRESS:
		{
#ifndef DPNBUILD_NOSPUI
			if ( ( ( pEnumQueryData->dwFlags & DPNSPF_OKTOQUERY ) != 0 )
#if ((! defined(DPNBUILD_NOIPV6)) || (! defined(DPNBUILD_NOIPX)))
#ifdef DPNBUILD_NOIPV6
				&& (( pSPData->GetType() == AF_INET6 ) || ( pSPData->GetType() == AF_INET ))
#else // ! DPNBUILD_NOIPV6
				&& ( pSPData->GetType() == AF_INET )
#endif // ! DPNBUILD_NOIPV6
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
				)
			{
				//
				// Copy the connect data locally and start the dialog.  When the
				// dialog completes, the connection will attempt to complete.
				// Since the dialog is being popped, this command is in progress,
				// not pending.
				//
				DNASSERT( pSPData != NULL );

				pCommand->SetState( COMMAND_STATE_INPROGRESS );
				
				hr = pEndpoint->CopyEnumQueryData( pEnumQueryData );
				if ( hr != DPN_OK )
				{
					DPFX(DPFPREP, 0, "Failed to copy enum query data before settings dialog!" );
					DisplayDNError( 0, hr );
					goto Failure;
				}


				//
				// Initialize the bind type.  It will get changed to DEFAULT or SPECIFIC
				//
				pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);


				hr = pEndpoint->ShowSettingsDialog( pSPData->GetThreadPool() );
				if ( hr != DPN_OK )
				{
					DPFX(DPFPREP, 0, "Problem showing settings dialog for enum query!" );
					DisplayDNError( 0, hr );

					goto Failure;
				}

				//
				// this endpoint has been handed off, remove our reference to it
				//
				pEndpoint = NULL;
				hr = DPNERR_PENDING;

				goto Exit;
			}
#endif // !DPNBUILD_NOSPUI

			if ( pEnumQueryData->dwFlags & DPNSPF_NOBROADCASTFALLBACK )
			{
				goto Failure;
			}
			
			//
			// we're OK, we can use the broadcast address.
			//
			
#if ((! defined(DPNBUILD_NONATHELP)) && (! defined(DPNBUILD_ONLYONETHREAD)))
			//
			// If NAT traversal is allowed, we may need to load and start
			// NAT Help, which can block.  Submit a blocking job.  This
			// will redetect the incomplete address and use broadcast (see
			// CEndpoint::EnumQueryBlockingJob).
			//
			if ( pEndpoint->GetUserTraversalMode() != DPNA_TRAVERSALMODE_NONE )
			{
				goto SubmitBlockingJob;
			}
#endif // ! DPNBUILD_NONATHELP and ! DPNBUILD_ONLYONETHREAD

			//
			// Mash in the broadcast address, but actually complete the
			// enum on another thread.
			//
			pEndpoint->ReinitializeWithBroadcast();
			goto SubmitDelayedCommand;
			
			break;
		}

#ifndef DPNBUILD_ONLYONETHREAD
		//
		// some blocking operation might occur, submit it to be run
		// on a background thread.
		//
		case DPNERR_TIMEDOUT:
		{
SubmitBlockingJob:
			//
			// Copy enum data and submit job to finish off enum.
			//
			DNASSERT( pSPData != NULL );
			hr = pEndpoint->CopyEnumQueryData( pEnumQueryData );
			if ( hr != DPN_OK )
			{
				DPFX(DPFPREP, 0, "Failed to copy enum query data before blocking job!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}


			//
			// Initialize the bind type.  It will get changed to DEFAULT or SPECIFIC
			//
			pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);


			pEndpoint->AddRef();

			hr = pSPData->GetThreadPool()->SubmitBlockingJob( CEndpoint::EnumQueryBlockingJobWrapper,
															pEndpoint );
			if ( hr != DPN_OK )
			{
				pEndpoint->DecRef();
				DPFX(DPFPREP, 0, "Failed to submit blocking enum query job!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}

			//
			// this endpoint has been handed off, remove our reference
			//
			pEndpoint = NULL;
			hr = DPNERR_PENDING;
			goto Exit;
		}
#endif // ! DPNBUILD_ONLYONETHREAD

		//
		// address conversion was fine, copy connect data and finish connection
		// on background thread.
		//
		case DPN_OK:
		{
SubmitDelayedCommand:
			//
			// Copy enum data and submit job to finish off enum.
			//
			DNASSERT( pSPData != NULL );
			hr = pEndpoint->CopyEnumQueryData( pEnumQueryData );
			if ( hr != DPN_OK )
			{
				DPFX(DPFPREP, 0, "Failed to copy enum query data before delayed command!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}


			//
			// Initialize the bind type.  It will get changed to DEFAULT or SPECIFIC
			//
			pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);


			pEndpoint->AddRef();

#ifdef DPNBUILD_ONLYONEPROCESSOR
			hr = pSPData->GetThreadPool()->SubmitDelayedCommand( CEndpoint::EnumQueryJobCallback,
																pEndpoint );
#else // ! DPNBUILD_ONLYONEPROCESSOR
			hr = pSPData->GetThreadPool()->SubmitDelayedCommand( -1,								// we don't know the CPU yet, so pick any
																CEndpoint::EnumQueryJobCallback,
																pEndpoint );
#endif // ! DPNBUILD_ONLYONEPROCESSOR
			if ( hr != DPN_OK )
			{
				pEndpoint->DecRef();
				DPFX(DPFPREP, 0, "Failed to set delayed enum query!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}

			//
			// this endpoint has been handed off, remove our reference
			//
			pEndpoint = NULL;
			hr = DPNERR_PENDING;
			goto Exit;

			break;
		}

		default:
		{
			//
			// this endpoint is screwed
			//
			DPFX(DPFPREP, 0, "Problem initializing endpoint in DNSP_EnumQuery!" );
			DisplayDNError( 0, hr );
			goto Failure;

			break;
		}
	}

Exit:

	DNASSERT( pEndpoint == NULL );

	if ( hr != DPNERR_PENDING )
	{
		// this command cannot complete synchronously!
		DNASSERT( hr != DPN_OK );

		DPFX(DPFPREP, 0, "Problem with DNSP_EnumQuery()" );
		DisplayDNError( 0, hr );
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return hr;

Failure:
	//
	// if there's an allocated command, clean up and then
	// return the command
	//
	if ( pCommand != NULL )
	{
		pCommand->DecRef();
		pCommand = NULL;

		pEnumQueryData->hCommand = NULL;
		pEnumQueryData->dwCommandDescriptor = NULL_DESCRIPTOR;
	}

	//
	// is there an endpoint to free?
	//
	if ( pEndpoint != NULL )
	{
		if ( fEndpointOpen != FALSE )
		{
			pEndpoint->Close( hr );
			fEndpointOpen = FALSE;
		}
		
		pSPData->CloseEndpointHandle( pEndpoint );
		pEndpoint = NULL;
	}

	goto Exit;
}
//**********************************************************************


//**********************************************************************
/*
 *
 *	DNSP_EnumRespond  sends a response to an enum request by
 *		sending the specified data to the address provided (on
 *		unreliable transport).
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_EnumRespond"

STDMETHODIMP DNSP_EnumRespond( IDP8ServiceProvider *pThis, SPENUMRESPONDDATA *pEnumRespondData )
{
	HRESULT								hr;
	CEndpoint							*pEndpoint;
	CSPData								*pSPData;
	const ENDPOINT_ENUM_QUERY_CONTEXT	*pEnumQueryContext;
	PREPEND_BUFFER						PrependBuffer;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pEnumRespondData);

	DNASSERT( pThis != NULL );
	DNASSERT( pEnumRespondData != NULL );
	DNASSERT( pEnumRespondData->dwFlags == 0 );

	//
	// initialize
	//
	DBG_CASSERT( OFFSETOF( ENDPOINT_ENUM_QUERY_CONTEXT, EnumQueryData ) == 0 );
	pEnumQueryContext = reinterpret_cast<ENDPOINT_ENUM_QUERY_CONTEXT*>( pEnumRespondData->pQuery );
	pEndpoint = NULL;
	pEnumRespondData->hCommand = NULL;
	pEnumRespondData->dwCommandDescriptor = NULL_DESCRIPTOR;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );
	
	//
	// check for valid endpoint
	//
	pEndpoint = pSPData->EndpointFromHandle( pEnumQueryContext->hEndpoint );
	if ( pEndpoint == NULL )
	{
		hr = DPNERR_INVALIDENDPOINT;
		DPFX(DPFPREP, 8, "Invalid endpoint handle in DNSP_EnumRespond" );
		goto Failure;
	}

	//
	// no need to poke at the thread pool here to lock down threads because we
	// can only really be here if there's an enum and that enum locked down the
	// thread pool.
	//

	DNASSERT( pEnumQueryContext->dwEnumKey <= WORD_MAX );

	pEnumRespondData->pBuffers[-1].pBufferData = reinterpret_cast<BYTE*>( &PrependBuffer.EnumResponseDataHeader );
	pEnumRespondData->pBuffers[-1].dwBufferSize = sizeof( PrependBuffer.EnumResponseDataHeader );

	PrependBuffer.EnumResponseDataHeader.bSPLeadByte = SP_HEADER_LEAD_BYTE;
	PrependBuffer.EnumResponseDataHeader.bSPCommandByte = ENUM_RESPONSE_DATA_KIND;
	PrependBuffer.EnumResponseDataHeader.wEnumResponsePayload = static_cast<WORD>( pEnumQueryContext->dwEnumKey );

#ifdef DPNBUILD_XNETSECURITY
	//
	// Secure transport does not allow directed replies without having a
	// security context established.  We need to broadcast the reply.
	//
	if (pEndpoint->IsUsingXNetSecurity())
	{
		SOCKADDR_IN *	psaddrin;
		XNADDR			xnaddr;
		DWORD			dwAddressType;


#pragma BUGBUG(vanceo, "Is it possible to have a security context?  How can we tell?  XNetInAddrToXnAddr failing?")
#pragma TODO(vanceo, "Cache title address?")

		dwAddressType = XNetGetTitleXnAddr(&xnaddr);
		if ((dwAddressType != XNET_GET_XNADDR_PENDING) &&
			(dwAddressType != XNET_GET_XNADDR_NONE))
		{
			DNASSERT(pEnumQueryContext->pReturnAddress->GetFamily() == AF_INET);
			psaddrin = (SOCKADDR_IN*) (pEnumQueryContext->pReturnAddress->GetWritableAddress());
			psaddrin->sin_addr.S_un.S_addr = INADDR_BROADCAST;

			pEnumRespondData->pBuffers[-1].dwBufferSize = sizeof( PrependBuffer.XNetSecEnumResponseDataHeader );
			
			PrependBuffer.EnumResponseDataHeader.bSPCommandByte = XNETSEC_ENUM_RESPONSE_DATA_KIND;

			memcpy(&PrependBuffer.XNetSecEnumResponseDataHeader.xnaddr,
					&xnaddr,
					sizeof(xnaddr));
		}
		else
		{
			DPFX(DPFPREP, 0, "Couldn't get XNAddr (type = %u)!  Ignoring and trying to send unsecure response.",
				dwAddressType);
		}
	}
#endif // DPNBUILD_XNETSECURITY

#ifdef DPNBUILD_ASYNCSPSENDS
	pEndpoint->GetSocketPort()->SendData( (pEnumRespondData->pBuffers - 1),
											(pEnumRespondData->dwBufferCount + 1),
											pEnumQueryContext->pReturnAddress,
											NULL );
#else // ! DPNBUILD_ASYNCSPSENDS
	pEndpoint->GetSocketPort()->SendData( (pEnumRespondData->pBuffers - 1),
											(pEnumRespondData->dwBufferCount + 1),
											pEnumQueryContext->pReturnAddress );
#endif // ! DPNBUILD_ASYNCSPSENDS

	// We can only return DPNERR_PENDING or failure, so we need to separately call the completion if 
	// we want to return DPN_OK.
	DPFX(DPFPREP, 5, "Endpoint 0x%p completing command synchronously (result = DPN_OK, user context = 0x%p) to interface 0x%p.",
		pEndpoint, pEnumRespondData->pvContext, pSPData->DP8SPCallbackInterface());

	hr = IDP8SPCallback_CommandComplete( pSPData->DP8SPCallbackInterface(),	// pointer to callbacks
										NULL,								// command handle
										DPN_OK,								// return
										pEnumRespondData->pvContext			// user cookie
										);

	DPFX(DPFPREP, 5, "Endpoint 0x%p returning from command complete [0x%lx].", pEndpoint, hr);
	hr = DPNERR_PENDING;

	pEndpoint->DecCommandRef();
	pEndpoint = NULL;


Exit:
	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return hr;

Failure:

	if ( pEndpoint != NULL )
	{
		pEndpoint->DecCommandRef();
		pEndpoint = NULL;
	}

	goto Exit;
}
//**********************************************************************


//**********************************************************************
/*
 *
 *	DNSP_Connect "connects" to the specified address.  This doesn't
 *		necessarily mean a real (TCP) connection is made.  It could
 *		just be a virtual UDP connection
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_Connect"

STDMETHODIMP DNSP_Connect( IDP8ServiceProvider *pThis, SPCONNECTDATA *pConnectData )
{
	HRESULT					hr;
	CEndpoint				*pEndpoint;
	CCommandData			*pCommand;
	BOOL					fEndpointOpen;
	CSPData					*pSPData;
#ifndef DPNBUILD_NONATHELP
	DWORD					dwTraversalMode;
	DWORD					dwComponentSize;
	DWORD					dwComponentType;
#endif // ! DPNBUILD_NONATHELP
#ifdef DBG
	DWORD					dwAllowedFlags;
#endif // DBG


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pConnectData);

	DNASSERT( pThis != NULL );
	DNASSERT( pConnectData != NULL );
	DNASSERT( pConnectData->pAddressHost != NULL );
	DNASSERT( pConnectData->pAddressDeviceInfo != NULL );

#ifdef DBG
	dwAllowedFlags = DPNSPF_SESSIONDATA;
#ifndef DPNBUILD_NOSPUI
	dwAllowedFlags |= DPNSPF_OKTOQUERY;
#endif // ! DPNBUILD_NOSPUI
#ifndef DPNBUILD_ONLYONEADAPTER
	dwAllowedFlags |= DPNSPF_ADDITIONALMULTIPLEXADAPTERS;
#endif // ! DPNBUILD_ONLYONEADAPTER
#ifndef DPNBUILD_NOMULTICAST
	dwAllowedFlags |= DPNSPF_CONNECT_MULTICAST_SEND | DPNSPF_CONNECT_MULTICAST_RECEIVE;
#endif // ! DPNBUILD_NOMULTICAST

	DNASSERT( ( pConnectData->dwFlags & ~( dwAllowedFlags ) ) == 0 );
#ifndef DPNBUILD_NOMULTICAST
	DNASSERT( !( ( pConnectData->dwFlags & DPNSPF_CONNECT_MULTICAST_SEND ) && ( pConnectData->dwFlags & DPNSPF_CONNECT_MULTICAST_RECEIVE ) ) );
#endif // ! DPNBUILD_NOMULTICAST

	if ( pConnectData->dwFlags & DPNSPF_SESSIONDATA )
	{
		DNASSERT( pConnectData->pvSessionData != NULL );
		DNASSERT( pConnectData->dwSessionDataSize > 0 );
	}
#endif // DBG


	//
	// initialize
	//
	hr = DPNERR_PENDING;
	pEndpoint = NULL;
	pCommand = NULL;
	fEndpointOpen = FALSE;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	pConnectData->hCommand = NULL;
	pConnectData->dwCommandDescriptor = NULL_DESCRIPTOR;

	
	// Trust protocol to call us only in the initialized state
	DNASSERT(pSPData->GetState() == SPSTATE_INITIALIZED);


	DumpAddress( 8, _T("Connect destination:"), pConnectData->pAddressHost );
	DumpAddress( 8, _T("Connecting on device:"), pConnectData->pAddressDeviceInfo );

	
	//
	// the user is attempting an operation that relies on the thread pool, lock
	// it down to prevent threads from being lost.  This also performs other
	// first time initialization.
	//
	hr = pSPData->GetThreadPool()->PreventThreadPoolReduction();
	if ( hr != DPN_OK )
	{
		DPFX(DPFPREP, 0, "Failed to prevent thread pool reduction!" );
		goto Failure;
	}

	//
	// create and new endpoint
	//
	pEndpoint = pSPData->GetNewEndpoint();
	if ( pEndpoint == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		DPFX(DPFPREP, 0, "Cannot create new endpoint in DNSP_Connect!" );
		goto Failure;
	}
	
	
#ifndef DPNBUILD_NONATHELP
	//
	// We need to detect up front whether NAT traversal is disabled or not so we can optimize
	// the Open call below.
	//
	dwComponentSize = sizeof(dwTraversalMode);
	hr = IDirectPlay8Address_GetComponentByName(pConnectData->pAddressDeviceInfo,
												DPNA_KEY_TRAVERSALMODE,
												&dwTraversalMode,
												&dwComponentSize,
												&dwComponentType);
	if ( hr == DPN_OK )
	{
		//
		// We found the component.  Make sure it's the right size and type.
		//
		if ((dwComponentSize == sizeof(dwTraversalMode)) && (dwComponentType == DPNA_DATATYPE_DWORD))
		{
			switch (dwTraversalMode)
			{
				case DPNA_TRAVERSALMODE_NONE:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is NONE.");
					break;
				}

				case DPNA_TRAVERSALMODE_PORTREQUIRED:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is PORTREQUIRED.");
					break;
				}

				case DPNA_TRAVERSALMODE_PORTRECOMMENDED:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is PORTRECOMMENDED.");
					break;
				}

				default:
				{
					DPFX(DPFPREP, 0, "Ignoring correctly formed traversal mode key with invalid value %u!  Using default mode %u.",
						dwTraversalMode, g_dwDefaultTraversalMode);
					dwTraversalMode = g_dwDefaultTraversalMode;
					break;
				}
			}
		}
		else
		{
			DPFX(DPFPREP, 0, "Traversal mode key exists, but doesn't match expected type (%u != %u) or size (%u != %u)!  Using default mode %u.",
				dwComponentSize, sizeof(dwTraversalMode),
				dwComponentType, DPNA_DATATYPE_DWORD,
				g_dwDefaultTraversalMode);
			dwTraversalMode = g_dwDefaultTraversalMode;
		}
	}
	else
	{
		//
		// The key is not there, it's the wrong size (too big for our buffer
		// and returned BUFFERTOOSMALL), or something else bad happened.
		// It doesn't matter.  Carry on.
		//
		DPFX(DPFPREP, 8, "Could not get traversal mode key, error = 0x%lx, component size = %u, type = %u, using default mode %u.",
			hr, dwComponentSize, dwComponentType, g_dwDefaultTraversalMode);
		dwTraversalMode = g_dwDefaultTraversalMode;
	}
	
	if (g_dwDefaultTraversalMode & FORCE_TRAVERSALMODE_BIT)
	{
		DPFX(DPFPREP, 1, "Forcing traversal mode %u.");
		dwTraversalMode = g_dwDefaultTraversalMode & (~FORCE_TRAVERSALMODE_BIT);
	}
	
	pEndpoint->SetUserTraversalMode(dwTraversalMode);
#endif // ! DPNBUILD_NONATHELP


	//
	// get new command and initialize it
	//
	pCommand = (CCommandData*)g_CommandDataPool.Get();
	if ( pCommand == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		DPFX(DPFPREP, 0, "Cannot get command handle for DNSP_Connect!" );
		goto Failure;
	}
	
	DPFX(DPFPREP, 7, "(0x%p) Connect command 0x%p created.",
		pSPData, pCommand);

	pConnectData->hCommand = pCommand;
	pConnectData->dwCommandDescriptor = pCommand->GetDescriptor();
#ifndef DPNBUILD_NOMULTICAST
	if ( pConnectData->dwFlags & DPNSPF_CONNECT_MULTICAST_SEND )
	{
		pCommand->SetType( COMMAND_TYPE_MULTICAST_SEND );
	}
	else if ( pConnectData->dwFlags & DPNSPF_CONNECT_MULTICAST_RECEIVE )
	{
		pCommand->SetType( COMMAND_TYPE_MULTICAST_RECEIVE );
	}
	else
#endif // ! DPNBUILD_NOMULTICAST
	{
		pCommand->SetType( COMMAND_TYPE_CONNECT );
	}
	pCommand->SetState( COMMAND_STATE_PENDING );
	pCommand->SetEndpoint( pEndpoint );

	//
	// open endpoint with outgoing address
	//
	fEndpointOpen = TRUE;
#ifndef DPNBUILD_NOMULTICAST
	if ( pConnectData->dwFlags & DPNSPF_CONNECT_MULTICAST_SEND )
	{
		hr = pEndpoint->Open( ENDPOINT_TYPE_MULTICAST_SEND,
							  pConnectData->pAddressHost,
							  ((pConnectData->dwFlags & DPNSPF_SESSIONDATA) ? pConnectData->pvSessionData : NULL),
							  ((pConnectData->dwFlags & DPNSPF_SESSIONDATA) ? pConnectData->dwSessionDataSize : 0),
							  NULL );
	}
	else if ( pConnectData->dwFlags & DPNSPF_CONNECT_MULTICAST_RECEIVE )
	{
		hr = pEndpoint->Open( ENDPOINT_TYPE_MULTICAST_RECEIVE,
							  pConnectData->pAddressHost,
							  ((pConnectData->dwFlags & DPNSPF_SESSIONDATA) ? pConnectData->pvSessionData : NULL),
							  ((pConnectData->dwFlags & DPNSPF_SESSIONDATA) ? pConnectData->dwSessionDataSize : 0),
							  NULL );
	}
	else
#endif // ! DPNBUILD_NOMULTICAST
	{
		hr = pEndpoint->Open( ENDPOINT_TYPE_CONNECT,
							  pConnectData->pAddressHost,
							  ((pConnectData->dwFlags & DPNSPF_SESSIONDATA) ? pConnectData->pvSessionData : NULL),
							  ((pConnectData->dwFlags & DPNSPF_SESSIONDATA) ? pConnectData->dwSessionDataSize : 0),
							  NULL );
	}
	switch ( hr )
	{
		//
		// address conversion was fine, copy connect data and finish connection
		// on background thread.
		//
		case DPN_OK:
		{
			//
			// Copy connection data and submit job to finish off connection.
			//
			DNASSERT( pSPData != NULL );

			hr = pEndpoint->CopyConnectData( pConnectData );
			if ( hr != DPN_OK )
			{
				DPFX(DPFPREP, 0, "Failed to copy connect data before delayed command!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}


			//
			// Initialize the bind type.  It will get changed to DEFAULT or SPECIFIC
			//
			pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);


			pEndpoint->AddRef();

#ifdef DPNBUILD_ONLYONEPROCESSOR
			hr = pSPData->GetThreadPool()->SubmitDelayedCommand( CEndpoint::ConnectJobCallback,
																pEndpoint );
#else // ! DPNBUILD_ONLYONEPROCESSOR
			hr = pSPData->GetThreadPool()->SubmitDelayedCommand( -1,								// we don't know the CPU yet, so pick any
																CEndpoint::ConnectJobCallback,
																pEndpoint );
#endif // ! DPNBUILD_ONLYONEPROCESSOR
			if ( hr != DPN_OK )
			{
				pEndpoint->DecRef();
				DPFX(DPFPREP, 0, "Failed to set delayed connect!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}

			//
			// this endpoint has been handed off, remove our reference to it
			//
			pEndpoint = NULL;
			hr = DPNERR_PENDING;
			goto Exit;

			break;
		}

		//
		// Incomplete address passed in, query user for more information if
		// we're allowed.  Since we don't have a complete address at this time,
		// don't bind this endpoint to the socket port!
		//
		case DPNERR_INCOMPLETEADDRESS:
		{
#ifndef DPNBUILD_NOSPUI
			if ( ( pConnectData->dwFlags & DPNSPF_OKTOQUERY ) != 0 )
			{
				//
				// Copy the connect data locally and start the dialog.  When the
				// dialog completes, the connection will attempt to complete.
				// Since a dialog is being displayed, the command is in-progress,
				// not pending.  However, you can't cancel the dialog once it's
				// displayed (the UI would suddenly disappear).
				//
				pCommand->SetState( COMMAND_STATE_INPROGRESS_CANNOT_CANCEL );
				
				hr = pEndpoint->CopyConnectData( pConnectData );
				if ( hr != DPN_OK )
				{
					DPFX(DPFPREP, 0, "Failed to copy connect data before dialog!" );
					DisplayDNError( 0, hr );
					goto Failure;
				}

				//
				// Initialize the bind type.  It will get changed to DEFAULT or SPECIFIC
				//
				pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);


				hr = pEndpoint->ShowSettingsDialog( pSPData->GetThreadPool() );
				if ( hr != DPN_OK )
				{
					DPFX(DPFPREP, 0, "Problem showing settings dialog for connect!" );
					DisplayDNError( 0, hr );

					goto Failure;
				}

				//
				// this endpoint has been handed off, remove our reference to it
				//
				pEndpoint = NULL;
				hr = DPNERR_PENDING;

				goto Exit;
			}
			else
#endif // !DPNBUILD_NOSPUI
			{
				goto Failure;
			}

			break;
		}

#ifndef DPNBUILD_ONLYONETHREAD
		//
		// some blocking operation might occur, submit it to be run
		// on a background thread.
		//
		case DPNERR_TIMEDOUT:
		{
			//
			// Copy connect data and submit job to finish off enum.
			//
			DNASSERT( pSPData != NULL );
			hr = pEndpoint->CopyConnectData( pConnectData );
			if ( hr != DPN_OK )
			{
				DPFX(DPFPREP, 0, "Failed to copy connect data before blocking job!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}


			//
			// Initialize the bind type.  It will get changed to DEFAULT or SPECIFIC
			//
			pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);


			pEndpoint->AddRef();

			hr = pSPData->GetThreadPool()->SubmitBlockingJob( CEndpoint::ConnectBlockingJobWrapper,
															pEndpoint );
			if ( hr != DPN_OK )
			{
				pEndpoint->DecRef();
				DPFX(DPFPREP, 0, "Failed to submit blocking connect job!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}

			//
			// this endpoint has been handed off, remove our reference
			//
			pEndpoint = NULL;
			hr = DPNERR_PENDING;
			goto Exit;
		}
#endif // ! DPNBUILD_ONLYONETHREAD

		default:
		{
			DPFX(DPFPREP, 0, "Problem initializing endpoint in DNSP_Connect!" );
			DisplayDNError( 0, hr );
			goto Failure;

			break;
		}
	}

Exit:
	
	DNASSERT( pEndpoint == NULL );

	if ( hr != DPNERR_PENDING )
	{
		// this command cannot complete synchronously!
		DNASSERT( hr != DPN_OK );

		DPFX(DPFPREP, 0, "Problem with DNSP_Connect()" );
		DisplayDNError( 0, hr );
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return hr;

Failure:
	//
	// if there's an allocated command, clean up and then
	// return the command
	//
	if ( pCommand != NULL )
	{
		pCommand->DecRef();
		pCommand = NULL;

		pConnectData->hCommand = NULL;
		pConnectData->dwCommandDescriptor = NULL_DESCRIPTOR;
	}

	//
	// is there an endpoint to free?
	//
	if ( pEndpoint != NULL )
	{
		if ( fEndpointOpen != FALSE )
		{
			pEndpoint->Close( hr );
			fEndpointOpen = FALSE;
		}

		pSPData->CloseEndpointHandle( pEndpoint );
		pEndpoint = NULL;
	}

	goto Exit;
}
//**********************************************************************


//**********************************************************************
/*
 *
 *	DNSP_Disconnect disconnects an active connection
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_Disconnect"

STDMETHODIMP DNSP_Disconnect( IDP8ServiceProvider *pThis, SPDISCONNECTDATA *pDisconnectData )
{
	HRESULT		hr;
	HRESULT		hTempResult;
	CEndpoint	*pEndpoint;
	CSPData		*pSPData;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pDisconnectData);

	DNASSERT( pThis != NULL );
	DNASSERT( pDisconnectData != NULL );
	DNASSERT( pDisconnectData->dwFlags == 0 );
	DNASSERT( pDisconnectData->hEndpoint != INVALID_HANDLE_VALUE && pDisconnectData->hEndpoint != 0 );
	DNASSERT( pDisconnectData->dwFlags == 0 );

	//
	// initialize
	//
	hr = DPN_OK;
	pEndpoint = NULL;
	pDisconnectData->hCommand = NULL;
	pDisconnectData->dwCommandDescriptor = NULL_DESCRIPTOR;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	//
	// no need to poke at the thread pool here because there was already a connect
	// issued and that connect should have locked down the thread pool.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// look up the endpoint and if it's found, close its handle
	//
	pEndpoint = pSPData->GetEndpointAndCloseHandle( pDisconnectData->hEndpoint );
	if ( pEndpoint == NULL )
	{
		hr = DPNERR_INVALIDENDPOINT;
		goto Failure;
	}
	
	hTempResult = pEndpoint->Disconnect();
	switch ( hTempResult )
	{
		//
		// endpoint disconnected immediately
		//
		case DPNERR_PENDING:
		case DPN_OK:
		{
			break;
		}

		//
		// Other return.  Since the disconnect didn't complete, we need
		// to unlock the endpoint.
		//
		default:
		{
			DPFX(DPFPREP, 0, "Error reported when attempting to disconnect endpoint in DNSP_Disconnect!" );
			DisplayDNError( 0, hTempResult );
			DNASSERT( FALSE );

			break;
		}
	}

Exit:
	//
	// remove outstanding reference from GetEndpointHandleAndClose()
	//
	if ( pEndpoint != NULL )
	{
		pEndpoint->DecRef();
		pEndpoint = NULL;
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return hr;

Failure:
	goto Exit;
}
//**********************************************************************


//**********************************************************************
/*
 *
 *	DNSP_Listen "listens" on the specified address/port.  This doesn't
 *		necessarily mean that a true TCP socket is used.  It could just
 *		be a UDP port that's opened for receiving packets
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_Listen"

STDMETHODIMP DNSP_Listen( IDP8ServiceProvider *pThis, SPLISTENDATA *pListenData)
{
	HRESULT					hr;
	CEndpoint				*pEndpoint;
	CCommandData			*pCommand;
	IDirectPlay8Address		*pDeviceAddress;
	BOOL					fEndpointOpen;
	CSPData					*pSPData;
#ifndef DPNBUILD_NONATHELP
	DWORD					dwTraversalMode;
	DWORD					dwComponentSize;
	DWORD					dwComponentType;
#endif // ! DPNBUILD_NONATHELP
#ifdef DBG
	DWORD					dwAllowedFlags;
#endif // DBG


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pListenData);

	DNASSERT( pThis != NULL );
	DNASSERT( pListenData != NULL );

#ifdef DBG
	dwAllowedFlags = DPNSPF_BINDLISTENTOGATEWAY | DPNSPF_LISTEN_DISALLOWENUMS | DPNSPF_SESSIONDATA;
#ifndef DPNBUILD_NOSPUI
	dwAllowedFlags |= DPNSPF_OKTOQUERY;
#endif // ! DPNBUILD_NOSPUI
#ifndef DPNBUILD_NOMULTICAST
	dwAllowedFlags |= DPNSPF_LISTEN_MULTICAST | DPNSPF_LISTEN_ALLOWUNKNOWNSENDERS;
#endif // ! DPNBUILD_NOMULTICAST

	DNASSERT( ( pListenData->dwFlags & ~( dwAllowedFlags ) ) == 0 );

	if ( pListenData->dwFlags & DPNSPF_SESSIONDATA )
	{
		DNASSERT( pListenData->pvSessionData!= NULL );
		DNASSERT( pListenData->dwSessionDataSize > 0 );
	}
#endif // DBG


	//
	// initialize
	//
	hr = DPNERR_PENDING;
	pEndpoint = NULL;
	pCommand = NULL;
	pDeviceAddress = NULL;
	fEndpointOpen = FALSE;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	pListenData->hCommand = NULL;
	pListenData->dwCommandDescriptor = NULL_DESCRIPTOR;

	DumpAddress( 8, _T("Listening on device:"), pListenData->pAddressDeviceInfo );


	//
	// the user is attempting an operation that relies on the thread pool, lock
	// it down to prevent threads from being lost.  This also performs other
	// first time initialization.
	//
	hr = pSPData->GetThreadPool()->PreventThreadPoolReduction();
	if ( hr != DPN_OK )
	{
		DPFX(DPFPREP, 0, "Failed to prevent thread pool reduction!" );
		goto Failure;
	}


	//
	// AddRef the device address.
	//
	IDirectPlay8Address_AddRef(pListenData->pAddressDeviceInfo);
	pDeviceAddress = pListenData->pAddressDeviceInfo;
	
	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// create and new endpoint
	//
	pEndpoint = pSPData->GetNewEndpoint();
	if ( pEndpoint == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		DPFX(DPFPREP, 0, "Cannot create new endpoint in DNSP_Listen!" );
		goto Failure;
	}
	
	
#ifndef DPNBUILD_NONATHELP
	//
	// We need to detect up front whether NAT traversal is disabled or not so we can optimize
	// the Open call below.
	//
	dwComponentSize = sizeof(dwTraversalMode);
	hr = IDirectPlay8Address_GetComponentByName(pListenData->pAddressDeviceInfo,
												DPNA_KEY_TRAVERSALMODE,
												&dwTraversalMode,
												&dwComponentSize,
												&dwComponentType);
	if ( hr == DPN_OK )
	{
		//
		// We found the component.  Make sure it's the right size and type.
		//
		if ((dwComponentSize == sizeof(dwTraversalMode)) && (dwComponentType == DPNA_DATATYPE_DWORD))
		{
			switch (dwTraversalMode)
			{
				case DPNA_TRAVERSALMODE_NONE:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is NONE.");
					break;
				}

				case DPNA_TRAVERSALMODE_PORTREQUIRED:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is PORTREQUIRED.");
					break;
				}

				case DPNA_TRAVERSALMODE_PORTRECOMMENDED:
				{
					DPFX(DPFPREP, 1, "Found traversal mode key, value is PORTRECOMMENDED.");
					break;
				}

				default:
				{
					DPFX(DPFPREP, 0, "Ignoring correctly formed traversal mode key with invalid value %u!  Using PORTRECOMMENDED.",
						dwTraversalMode);
					dwTraversalMode = g_dwDefaultTraversalMode;
					break;
				}
			}
		}
		else
		{
			DPFX(DPFPREP, 0, "Traversal mode key exists, but doesn't match expected type (%u != %u) or size (%u != %u)!  Using default mode %u.",
				dwComponentSize, sizeof(dwTraversalMode),
				dwComponentType, DPNA_DATATYPE_DWORD,
				g_dwDefaultTraversalMode);
			dwTraversalMode = g_dwDefaultTraversalMode;
		}
	}
	else
	{
		//
		// The key is not there, it's the wrong size (too big for our buffer
		// and returned BUFFERTOOSMALL), or something else bad happened.
		// It doesn't matter.  Carry on.
		//
		DPFX(DPFPREP, 8, "Could not get traversal mode key, error = 0x%lx, component size = %u, type = %u, using default mode %u.",
			hr, dwComponentSize, dwComponentType, g_dwDefaultTraversalMode);
		dwTraversalMode = g_dwDefaultTraversalMode;
	}
	
	if (g_dwDefaultTraversalMode & FORCE_TRAVERSALMODE_BIT)
	{
		DPFX(DPFPREP, 1, "Forcing traversal mode %u.");
		dwTraversalMode = g_dwDefaultTraversalMode & (~FORCE_TRAVERSALMODE_BIT);
	}
	
	pEndpoint->SetUserTraversalMode(dwTraversalMode);
#endif // ! DPNBUILD_NONATHELP


	//
	// get new command and initialize it
	//
	pCommand = (CCommandData*)g_CommandDataPool.Get();
	if ( pCommand == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		DPFX(DPFPREP, 0, "Cannot get command handle for DNSP_Listen!" );
		goto Failure;
	}
	
	DPFX(DPFPREP, 7, "(0x%p) Listen command 0x%p created.",
		pSPData, pCommand);

	pListenData->hCommand = pCommand;
	pListenData->dwCommandDescriptor = pCommand->GetDescriptor();
#ifndef DPNBUILD_NOMULTICAST
	if (pListenData->dwFlags & DPNSPF_LISTEN_MULTICAST)
	{
		pCommand->SetType( COMMAND_TYPE_MULTICAST_LISTEN );
	}
	else
#endif // ! DPNBUILD_NOMULTICAST
	{
		pCommand->SetType( COMMAND_TYPE_LISTEN );
	}
	pCommand->SetState( COMMAND_STATE_PENDING );
	pCommand->SetEndpoint( pEndpoint );
	pCommand->SetUserContext( pListenData->pvContext );

	//
	// open endpoint with outgoing address
	//
	fEndpointOpen = TRUE;
#ifndef DPNBUILD_NOMULTICAST
	if (pListenData->dwFlags & DPNSPF_LISTEN_MULTICAST)
	{
		//
		// The device address should also contain the multicast address to be joined.
		//
		hr = pEndpoint->Open( ENDPOINT_TYPE_MULTICAST_LISTEN,
							pDeviceAddress,
							((pListenData->dwFlags & DPNSPF_SESSIONDATA) ? pListenData->pvSessionData : NULL),
							((pListenData->dwFlags & DPNSPF_SESSIONDATA) ? pListenData->dwSessionDataSize : 0),
							NULL );
	}
	else
#endif // ! DPNBUILD_NOMULTICAST
	{
		hr = pEndpoint->Open( ENDPOINT_TYPE_LISTEN,
							NULL,
							((pListenData->dwFlags & DPNSPF_SESSIONDATA) ? pListenData->pvSessionData : NULL),
							((pListenData->dwFlags & DPNSPF_SESSIONDATA) ? pListenData->dwSessionDataSize : 0),
							NULL );
	}

	switch ( hr )
	{
		//
		// address conversion was fine, copy connect data and finish connection
		// on background thread.
		//
		case DPN_OK:
		{
			//
			// Copy listen data and submit job to finish off listen.
			//
			DNASSERT( pSPData != NULL );

			hr = pEndpoint->CopyListenData( pListenData, pDeviceAddress );
			if ( hr != DPN_OK )
			{
				DPFX(DPFPREP, 0, "Failed to copy listen data before delayed command!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}


			//
			// Initialize the bind type.
			//
			if ((pListenData->dwFlags & DPNSPF_BINDLISTENTOGATEWAY))
			{
				//
				// This must always stay SPECIFIC_SHARED.
				//
				pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_SPECIFIC_SHARED);
			}
			else
			{
				//
				// This will get changed to DEFAULT or SPECIFIC.
				//
				pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);
			}


			pEndpoint->AddRef();

#ifdef DPNBUILD_ONLYONEPROCESSOR
			hr = pSPData->GetThreadPool()->SubmitDelayedCommand( CEndpoint::ListenJobCallback,
																pEndpoint );
#else // ! DPNBUILD_ONLYONEPROCESSOR
			hr = pSPData->GetThreadPool()->SubmitDelayedCommand( -1,								// we don't know the CPU yet, so pick any
																CEndpoint::ListenJobCallback,
																pEndpoint );
#endif // ! DPNBUILD_ONLYONEPROCESSOR
			if ( hr != DPN_OK )
			{
				pEndpoint->DecRef();
				DPFX(DPFPREP, 0, "Failed to set delayed listen!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}

			//
			// this endpoint has been handed off, remove our reference to it
			//
			pEndpoint = NULL;
			hr = DPNERR_PENDING;

			break;
		}

		//
		// Incomplete address passed in, query user for more information if
		// we're allowed.  Since we don't have a complete address at this time,
		// don't bind this endpoint to the socket port!
		//
		case DPNERR_INCOMPLETEADDRESS:
		{
			//
			// This SP will never encounter the case where there's not enough
			// information to start listening.  Either the adapter GUID is there
			// or not, and we won't know until CEndpoint::CompleteListen.
			//
			DNASSERT( FALSE );
			
#ifndef DPNBUILD_NOSPUI
			if ( ( pListenData->dwFlags & DPNSPF_OKTOQUERY ) != 0 )
			{
				//
				// Copy the listen data locally and start the dialog.  When the
				// dialog completes, the connection will attempt to complete.
				// Since this endpoint is being handed off to another thread,
				// make sure it's in the unbound list.  Since a dialog is being
				// displayed, the command state is in progress, not pending.
				//
				DNASSERT( pSPData != NULL );

				hr = pEndpoint->CopyListenData( pListenData, pDeviceAddress );
				if ( hr != DPN_OK )
				{
					DPFX(DPFPREP, 0, "Failed to copy listen data before dialog!" );
					DisplayDNError( 0, hr );
					goto Failure;
				}


				//
				// Initialize the bind type.
				//
				if ((pListenData->dwFlags & DPNSPF_BINDLISTENTOGATEWAY))
				{
					//
					// This must always stay SPECIFIC_SHARED.
					//
					pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_SPECIFIC_SHARED);
				}
				else
				{
					//
					// This will get changed to DEFAULT or SPECIFIC.
					//
					pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);
				}


				pCommand->SetState( COMMAND_STATE_INPROGRESS );
				hr = pEndpoint->ShowSettingsDialog( pSPData->GetThreadPool() );
				if ( hr != DPN_OK )
				{
					DPFX(DPFPREP, 0, "Problem showing settings dialog for listen!" );
					DisplayDNError( 0, hr );

					goto Failure;
				}

				//
				// this endpoint has been handed off, remove our reference to it
				//
				pEndpoint = NULL;
				hr = DPNERR_PENDING;

				goto Exit;
			}
			else
#endif // !DPNBUILD_NOSPUI
			{
				goto Failure;
			}

			break;
		}

#ifndef DPNBUILD_ONLYONETHREAD
		//
		// some blocking operation might occur, submit it to be run
		// on a background thread.
		//
		case DPNERR_TIMEDOUT:
		{
			//
			// Copy listen data and submit job to finish off enum.
			//
			DNASSERT( pSPData != NULL );
			hr = pEndpoint->CopyListenData( pListenData, pDeviceAddress );
			if ( hr != DPN_OK )
			{
				DPFX(DPFPREP, 0, "Failed to copy listen data before blocking job!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}


			//
			// Initialize the bind type.
			//
			if ((pListenData->dwFlags & DPNSPF_BINDLISTENTOGATEWAY))
			{
				//
				// This must always stay SPECIFIC_SHARED.
				//
				pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_SPECIFIC_SHARED);
			}
			else
			{
				//
				// This will get changed to DEFAULT or SPECIFIC.
				//
				pEndpoint->SetCommandParametersGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);
			}


			pEndpoint->AddRef();

			hr = pSPData->GetThreadPool()->SubmitBlockingJob( CEndpoint::ListenBlockingJobWrapper,
															pEndpoint );
			if ( hr != DPN_OK )
			{
				pEndpoint->DecRef();
				DPFX(DPFPREP, 0, "Failed to submit blocking listen job!" );
				DisplayDNError( 0, hr );
				goto Failure;
			}

			//
			// this endpoint has been handed off, remove our reference
			//
			pEndpoint = NULL;
			hr = DPNERR_PENDING;
			goto Exit;
		}
#endif // ! DPNBUILD_ONLYONETHREAD

		default:
		{
			DPFX(DPFPREP, 0, "Problem initializing endpoint in DNSP_Listen!" );
			DisplayDNError( 0, hr );
			goto Failure;

			break;
		}
	}

Exit:
	if ( pDeviceAddress != NULL )
	{
		IDirectPlay8Address_Release( pDeviceAddress );
		pDeviceAddress = NULL;
	}

	DNASSERT( pEndpoint == NULL );

	if ( hr != DPNERR_PENDING )
	{
		// this command cannot complete synchronously!
		DNASSERT( hr != DPN_OK );

		DPFX(DPFPREP, 0, "Problem with DNSP_Listen()" );
		DisplayDNError( 0, hr );
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return hr;

Failure:
	//
	// if there's an allocated command, clean up and then
	// return the command
	//
	if ( pCommand != NULL )
	{
		pCommand->DecRef();
		pCommand = NULL;

		pListenData->hCommand = NULL;
		pListenData->dwCommandDescriptor = NULL_DESCRIPTOR;
	}

	//
	// is there an endpoint to free?
	//
	if ( pEndpoint != NULL )
	{
		if ( fEndpointOpen != FALSE )
		{
			pEndpoint->Close( hr );
			fEndpointOpen = FALSE;
		}

		pSPData->CloseEndpointHandle( pEndpoint );
		pEndpoint = NULL;
	}

	goto Exit;
}
//**********************************************************************





//**********************************************************************
/*
 *
 *	DNSP_SendData sends data to the specified "player"
 *
 *	This call MUST BE HIGHLY OPTIMIZED
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_SendData"

STDMETHODIMP DNSP_SendData( IDP8ServiceProvider *pThis, SPSENDDATA *pSendData )
{
	HRESULT				hr;
	CEndpoint			*pEndpoint;
	CSPData				*pSPData;
#ifdef DPNBUILD_ASYNCSPSENDS
	CCommandData *		pCommand = NULL;
	OVERLAPPED *		pOverlapped;
#endif // DPNBUILD_ASYNCSPSENDS
#ifdef DBG
	DWORD				dwTotalBufferSize;
	DWORD				dwTemp;
#endif // DBG


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pSendData);

	DNASSERT( pThis != NULL );
	DNASSERT( pSendData != NULL );
	DNASSERT( pSendData->pBuffers != NULL );
	DNASSERT( pSendData->dwBufferCount != 0 );
	DNASSERT( pSendData->hEndpoint != INVALID_HANDLE_VALUE && pSendData->hEndpoint != 0 );
	DNASSERT( pSendData->dwFlags == 0 );

	//
	// initialize
	//
	pEndpoint = NULL;
	pSendData->hCommand = NULL;
	pSendData->dwCommandDescriptor = NULL_DESCRIPTOR;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// No need to lock down the thread counts here because the user already has
	// a connect or something running or they wouldn't be calling this function.
	// That outstanding connect would have locked down the thread pool.
	//

	//
	// Attempt to grab the endpoint from the handle.  If this succeeds, the
	// endpoint can send.
	//
	pEndpoint = pSPData->EndpointFromHandle( pSendData->hEndpoint );
	if ( pEndpoint == NULL )
	{
		hr = DPNERR_INVALIDHANDLE;
		DPFX(DPFPREP, 0, "Invalid endpoint handle on send!" );
		goto Failure;
	}

#ifdef DBG
	//
	// Make sure message is not too large.
	//
	dwTotalBufferSize = 0;
	for(dwTemp = 0; dwTemp < pSendData->dwBufferCount; dwTemp++)
	{
		dwTotalBufferSize += pSendData->pBuffers[dwTemp].dwBufferSize;
	}
#pragma TODO(vanceo, "No direct way for application to retrieve, they think max is g_dwMaxEnumDataSize")
#ifdef DPNBUILD_NOREGISTRY
	DNASSERT(dwTotalBufferSize <= DEFAULT_MAX_USER_DATA_SIZE);
#else // ! DPNBUILD_NOREGISTRY
	DNASSERT(dwTotalBufferSize <= g_dwMaxUserDataSize);
#endif // ! DPNBUILD_NOREGISTRY
	
	// Protocol guarantees that the first byte will never be zero
	DNASSERT(pSendData->pBuffers[ 0 ].pBufferData[ 0 ] != SP_HEADER_LEAD_BYTE);
#endif // DBG

	//
	// Assume user data.  There's no need to prepend a buffer because the
	// receiving machine will realize that it's not a 'special' message and
	// will default the contents to 'user data'.
	//
	
#ifdef DPNBUILD_ASYNCSPSENDS

#ifdef DPNBUILD_NOWINSOCK2
This won't compile because we need the Winsock2 API to perform overlapped sends
#endif // DPNBUILD_NOWINSOCK2

#ifndef DPNBUILD_ONLYWINSOCK2
	DNASSERT(pEndpoint->GetSocketPort() != NULL);
	DNASSERT(pEndpoint->GetSocketPort()->GetNetworkAddress() != NULL);
	if ( ( LOWORD( GetWinsockVersion() ) < 2 ) 
#ifndef DPNBUILD_NOIPX
		|| ( pEndpoint->GetSocketPort()->GetNetworkAddress()->GetFamily() != AF_INET ) 
#endif // ! DPNBUILD_NOIPX
		)
	{
		//
		// We can't perform overlapped sends on Winsock < 2 or on 9x IPX.
		//
		pEndpoint->GetSocketPort()->SendData( pSendData->pBuffers,
											pSendData->dwBufferCount,
											pEndpoint->GetRemoteAddressPointer(),
											NULL );

		hr = DPN_OK;

		pEndpoint->DecCommandRef();
	}
	else
#endif // ! DPNBUILD_ONLYWINSOCK2
	{
		//
		// get new command and initialize it
		//
		pCommand = (CCommandData*)g_CommandDataPool.Get();
		if ( pCommand == NULL )
		{
			hr = DPNERR_OUTOFMEMORY;
			DPFX(DPFPREP, 0, "Cannot get command handle!" );
			goto Failure;
		}
		
		DPFX(DPFPREP, 8, "(0x%p) Send command 0x%p created.",
			pSPData, pCommand);

		pSendData->hCommand = pCommand;
		pSendData->dwCommandDescriptor = pCommand->GetDescriptor();
		pCommand->SetType( COMMAND_TYPE_SEND );
		pCommand->SetState( COMMAND_STATE_INPROGRESS_CANNOT_CANCEL );	// can't cancel async sends
		pCommand->SetEndpoint( pEndpoint );
		pCommand->SetUserContext( pSendData->pvContext );


#ifdef DPNBUILD_ONLYONEPROCESSOR
		hr = IDirectPlay8ThreadPoolWork_CreateOverlapped(pSPData->GetThreadPool()->GetDPThreadPoolWork(),
														-1,
														CEndpoint::CompleteAsyncSend,
														pCommand,
														&pOverlapped,
														0);
#else // ! DPNBUILD_ONLYONEPROCESSOR
		hr = IDirectPlay8ThreadPoolWork_CreateOverlapped(pSPData->GetThreadPool()->GetDPThreadPoolWork(),
														pEndpoint->GetSocketPort()->GetCPU(),
														CEndpoint::CompleteAsyncSend,
														pCommand,
														&pOverlapped,
														0);
#endif // ! DPNBUILD_ONLYONEPROCESSOR
		if (hr != DPN_OK)
		{
			DPFX(DPFPREP, 0, "Couldn't create overlapped structure!");
			goto Failure;
		}
		
		pEndpoint->GetSocketPort()->SendData( pSendData->pBuffers,
												pSendData->dwBufferCount,
												pEndpoint->GetRemoteAddressPointer(),
												pOverlapped );

		//
		// Whether the submission to Winsock succeeds or fails, it should still
		// fill out the overlapped structure, so we will just let the async
		// completion handler do everything.
		//
		hr = IDirectPlay8ThreadPoolWork_SubmitIoOperation(pSPData->GetThreadPool()->GetDPThreadPoolWork(),
															pOverlapped,
															0);
		DNASSERT(hr == DPN_OK);

		//
		// Keep endpoint's command ref on send until send completes.
		//
		
		hr = DPNSUCCESS_PENDING;
	}
#else // ! DPNBUILD_ASYNCSPSENDS
	pEndpoint->GetSocketPort()->SendData( pSendData->pBuffers,
										pSendData->dwBufferCount,
										pEndpoint->GetRemoteAddressPointer() );

	hr = DPN_OK;

	pEndpoint->DecCommandRef();
#endif // ! DPNBUILD_ASYNCSPSENDS
	pEndpoint = NULL;

Exit:
	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return hr;

Failure:
#ifdef DPNBUILD_ASYNCSPSENDS
	//
	// if there's an allocated command, clean up and then
	// return the command
	//
	if ( pCommand != NULL )
	{
		pCommand->DecRef();
		pCommand = NULL;

		pSendData->hCommand = NULL;
		pSendData->dwCommandDescriptor = NULL_DESCRIPTOR;
	}
#endif // DPNBUILD_ASYNCSPSENDS
	if ( pEndpoint != NULL )
	{
		pEndpoint->DecCommandRef();
		pEndpoint = NULL;
	}

	goto Exit;
}
//**********************************************************************




//**********************************************************************
/*
 *
 *	DNSP_CancelCommand cancels a command in progress
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_CancelCommand"

STDMETHODIMP DNSP_CancelCommand( IDP8ServiceProvider *pThis, HANDLE hCommand, DWORD dwCommandDescriptor )
{
	HRESULT hr;
	CCommandData	*pCommandData;
	BOOL			fCommandLocked;
	CSPData			*pSPData;
	CEndpoint		*pEndpoint;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p, %ld)", pThis, hCommand, dwCommandDescriptor);

	DNASSERT( pThis != NULL );
	DNASSERT( hCommand != NULL );
	DNASSERT( dwCommandDescriptor != NULL_DESCRIPTOR );

	//
	// initialize
	//
	hr = DPN_OK;
	fCommandLocked = FALSE;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	//
	// No need to lock the thread pool counts because there's already some outstanding
	// enum, connect or listen running that has done so.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	pCommandData = static_cast<CCommandData*>( hCommand );

	pCommandData->Lock();
	fCommandLocked = TRUE;

	//
	// make sure the right command is being cancelled
	//
	if ( dwCommandDescriptor != pCommandData->GetDescriptor() )
	{
		hr = DPNERR_INVALIDCOMMAND;
		DPFX(DPFPREP, 0, "Attempt to cancel command (0x%p) with mismatched command descriptor (%u != %u)!",
			hCommand, dwCommandDescriptor, pCommandData->GetDescriptor() );
		goto Exit;
	}

	switch ( pCommandData->GetState() )
	{
		//
		// unknown command state
		//
		case COMMAND_STATE_UNKNOWN:
		{
			hr = DPNERR_INVALIDCOMMAND;
			DNASSERT( FALSE );
			break;
		}

		//
		// command is waiting to be processed, set command state to be cancelling
		// and wait for someone to pick it up
		//
		case COMMAND_STATE_PENDING:
		{
			DPFX(DPFPREP, 5, "Marking command 0x%p as cancelling.", pCommandData);
			pCommandData->SetState( COMMAND_STATE_CANCELLING );
			break;
		}

		//
		// command in progress, and can't be cancelled
		//
		case COMMAND_STATE_INPROGRESS_CANNOT_CANCEL:
		{
			DPFX(DPFPREP, 1, "Cannot cancel command 0x%p.", pCommandData);
			hr = DPNERR_CANNOTCANCEL;
			break;
		}

		//
		// Command is already being cancelled.  This is not a problem, but shouldn't
		// be happening for any endpoints other than connects.
		//
		case COMMAND_STATE_CANCELLING:
		{
			DPFX(DPFPREP, 1, "Cancelled already cancelling command 0x%p.", pCommandData);
			DNASSERT( pCommandData->GetEndpoint()->GetType() == ENDPOINT_TYPE_CONNECT );
			DNASSERT( hr == DPN_OK );
			break;
		}
		
#ifndef DPNBUILD_ONLYONETHREAD
		//
		// A blocking operation is already failing, let it complete.
		//
		case COMMAND_STATE_FAILING:
		{
			DPFX(DPFPREP, 1, "Cancelled already failing command 0x%p.", pCommandData);
			DNASSERT( hr == DPN_OK );
			break;
		}
#endif // ! DPNBUILD_ONLYONETHREAD

		//
		// command is in progress, find out what type of command it is
		//
		case COMMAND_STATE_INPROGRESS:
		{
			switch ( pCommandData->GetType() )
			{
				case COMMAND_TYPE_CONNECT:
				case COMMAND_TYPE_LISTEN:
#ifndef DPNBUILD_NOMULTICAST
				case COMMAND_TYPE_MULTICAST_LISTEN:
				case COMMAND_TYPE_MULTICAST_SEND:
				case COMMAND_TYPE_MULTICAST_RECEIVE:
#endif // ! DPNBUILD_NOMULTICAST
				{
					//
					// Set this command to the cancel state before we shut down
					// this endpoint.  Make sure a reference is added to the
					// endpoint so it stays around for the cancel.
					//
					pCommandData->SetState( COMMAND_STATE_CANCELLING );
					pEndpoint = pCommandData->GetEndpoint();
					pEndpoint->AddRef();

					DPFX(DPFPREP, 3, "Cancelling connect/listen/multicast command 0x%p (endpoint 0x%p).",
 						pCommandData, pEndpoint);

					pCommandData->Unlock();
					fCommandLocked = FALSE;

					pEndpoint->Lock();
					switch ( pEndpoint->GetState() )
					{
						//
						// endpoint is already disconnecting, no action needs to be taken
						//
						case ENDPOINT_STATE_DISCONNECTING:
						{
							DPFX(DPFPREP, 7, "Endpoint 0x%p already marked as disconnecting.",
								pEndpoint);
							pEndpoint->Unlock();
							pEndpoint->DecRef();
							goto Exit;
							break;
						}

						//
						// Endpoint is connecting.  Flag it as disconnecting and
						// add a reference so it doesn't disappear on us.
						//
						case ENDPOINT_STATE_ATTEMPTING_CONNECT:
						{
							DPFX(DPFPREP, 7, "Endpoint 0x%p attempting to connect, marking as disconnecting.",
								pEndpoint);
#ifdef DPNBUILD_NOMULTICAST
							DNASSERT(pEndpoint->GetType() == ENDPOINT_TYPE_CONNECT);
#else // ! DPNBUILD_NOMULTICAST
							DNASSERT((pEndpoint->GetType() == ENDPOINT_TYPE_CONNECT) || (pEndpoint->GetType() == ENDPOINT_TYPE_MULTICAST_SEND) || (pEndpoint->GetType() == ENDPOINT_TYPE_MULTICAST_RECEIVE));
#endif // ! DPNBUILD_NOMULTICAST
							pEndpoint->SetState( ENDPOINT_STATE_DISCONNECTING );
							break;
						}

						//
						// Endpoint has finished connecting.  Report that the
						// command is uncancellable.  Sorry Charlie, we missed
						// the window.
						//
						case ENDPOINT_STATE_CONNECT_CONNECTED:
						{
#ifdef DPNBUILD_NOMULTICAST
							DNASSERT(pEndpoint->GetType() == ENDPOINT_TYPE_CONNECT);
#else // ! DPNBUILD_NOMULTICAST
							DNASSERT((pEndpoint->GetType() == ENDPOINT_TYPE_CONNECT) || (pEndpoint->GetType() == ENDPOINT_TYPE_MULTICAST_SEND) || (pEndpoint->GetType() == ENDPOINT_TYPE_MULTICAST_RECEIVE));
#endif // ! DPNBUILD_NOMULTICAST
							DPFX(DPFPREP, 1, "Cannot cancel connect command 0x%p (endpoint 0x%p) that's already (or is about to) complete.",
								pCommandData, pEndpoint);
							pEndpoint->Unlock();
							pEndpoint->DecRef();
							hr = DPNERR_CANNOTCANCEL;
							goto Exit;
							break;
						}

						//
						// Endpoint is listening.  Flag it as disconnecting and
						// add a reference so it doesn't disappear on us
						//
						case ENDPOINT_STATE_LISTEN:
						{
							DPFX(DPFPREP, 7, "Endpoint 0x%p listening, marking as disconnecting.",
								pEndpoint);
#ifdef DPNBUILD_NOMULTICAST
							DNASSERT(pEndpoint->GetType() == ENDPOINT_TYPE_LISTEN);
#else // ! DPNBUILD_NOMULTICAST
							DNASSERT((pEndpoint->GetType() == ENDPOINT_TYPE_LISTEN) || (pEndpoint->GetType() == ENDPOINT_TYPE_MULTICAST_LISTEN));
#endif // ! DPNBUILD_NOMULTICAST
							pEndpoint->SetState( ENDPOINT_STATE_DISCONNECTING );
							break;
						}

						//
						// other state
						//
						default:
						{
							DNASSERT( FALSE );
							break;
						}
					}
					pEndpoint->Unlock();

					pEndpoint->Close( DPNERR_USERCANCEL );
					pSPData->CloseEndpointHandle( pEndpoint );
					pEndpoint->DecRef();

					break;
				}

				case COMMAND_TYPE_ENUM_QUERY:
				{
					pEndpoint = pCommandData->GetEndpoint();
					DNASSERT( pEndpoint != NULL );

					DPFX(DPFPREP, 3, "Cancelling enum query command 0x%p (endpoint 0x%p).",
						pCommandData, pEndpoint);
					
					pEndpoint->AddRef();

					pCommandData->SetState( COMMAND_STATE_CANCELLING );
					pCommandData->Unlock();
					fCommandLocked = FALSE;
						
					pEndpoint->StopEnumCommand( DPNERR_USERCANCEL );
					pEndpoint->DecRef();

					break;
				}

				default:
				{
					DNASSERT( FALSE );
					break;
				}
			}

			break;
		}

		//
		// other command state
		//
		default:
		{
			DNASSERT( FALSE );
			break;
		}
	}


Exit:
	if ( fCommandLocked != FALSE  )
	{
		DNASSERT( pCommandData != NULL );
		pCommandData->Unlock();
		fCommandLocked = FALSE;
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return hr;
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// DNSP_GetCaps - get SP capabilities
//
// Entry:		Pointer to DNSP interface
//				Pointer to caps data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_GetCaps"

STDMETHODIMP	DNSP_GetCaps( IDP8ServiceProvider *pThis, SPGETCAPSDATA *pCapsData )
{
	HRESULT		hr;
	CSPData		*pSPData;
#ifndef DPNBUILD_ONLYONETHREAD
	LONG		iIOThreadCount;
#endif // ! DPNBUILD_ONLYONETHREAD
	

	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pCapsData);

	DNASSERT( pThis != NULL );
	DNASSERT( pCapsData != NULL );
	DNASSERT( pCapsData->dwSize == sizeof( *pCapsData ) );
	DNASSERT( pCapsData->hEndpoint == INVALID_HANDLE_VALUE );

	//
	// initialize
	//
	hr = DPN_OK;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	//
	// no need to tell thread pool to lock the thread count for this function.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// set flags
	//

	pCapsData->dwFlags = DPNSPCAPS_SUPPORTSDPNSRV |
							DPNSPCAPS_SUPPORTSBROADCAST |
							DPNSPCAPS_SUPPORTSALLADAPTERS;

#ifndef DPNBUILD_ONLYONETHREAD
	pCapsData->dwFlags |= DPNSPCAPS_SUPPORTSTHREADPOOL;
#endif // ! DPNBUILD_ONLYONETHREAD

#ifndef DPNBUILD_NOMULTICAST
#if ((! defined(DPNBUILD_NOIPV6)) || (! defined(DPNBUILD_NOIPX)))
	if (pSPData->GetType() != AF_IPX)
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	{
		pCapsData->dwFlags |= DPNSPCAPS_SUPPORTSMULTICAST;
	}
#endif // ! DPNBUILD_NOMULTICAST


	//
	// set frame sizes
	//
#ifdef DPNBUILD_NOREGISTRY
	pCapsData->dwUserFrameSize = DEFAULT_MAX_USER_DATA_SIZE;
	pCapsData->dwEnumFrameSize = DEFAULT_MAX_ENUM_DATA_SIZE;
#else // ! DPNBUILD_NOREGISTRY
	pCapsData->dwUserFrameSize = g_dwMaxUserDataSize;
	pCapsData->dwEnumFrameSize = g_dwMaxEnumDataSize;
#endif // ! DPNBUILD_NOREGISTRY

	//
	// Set link speed, no need to check for endpoint because
	// the link speed cannot be determined.
	//
	pCapsData->dwLocalLinkSpeed = UNKNOWN_BANDWIDTH;

#ifdef DPNBUILD_ONLYONETHREAD
	pCapsData->dwIOThreadCount = 0;
#else // ! DPNBUILD_ONLYONETHREAD
	hr = pSPData->GetThreadPool()->GetIOThreadCount( &iIOThreadCount );
	if ( hr != DPN_OK )
	{
		DPFX(DPFPREP, 0, "DNSP_GetCaps: Failed to get thread pool count!" );
		DisplayDNError( 0, hr );
		goto Failure;
	}
	pCapsData->dwIOThreadCount = iIOThreadCount;
#endif // ! DPNBUILD_ONLYONETHREAD

	//
	// set enumeration defaults
	//
	pCapsData->dwDefaultEnumRetryCount = DEFAULT_ENUM_RETRY_COUNT;
	pCapsData->dwDefaultEnumRetryInterval = DEFAULT_ENUM_RETRY_INTERVAL;
	pCapsData->dwDefaultEnumTimeout = DEFAULT_ENUM_TIMEOUT;

	//
	// dwBuffersPerThread is ignored
	//
	pCapsData->dwBuffersPerThread = 1;

	//
	// set receive buffering information
	//
	pCapsData->dwSystemBufferSize = 8192;
	if ( g_fWinsockReceiveBufferSizeOverridden == FALSE )
	{
		SOCKET		TestSocket;
	
		
#if ((defined(DPNBUILD_NOIPV6)) && (defined(DPNBUILD_NOIPX)))
		TestSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_IP );
#else // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
		switch (pSPData->GetType())
		{
#ifndef DPNBUILD_NOIPV6
			case AF_INET6:
			{
				TestSocket = socket( AF_INET6, SOCK_DGRAM, IPPROTO_IP );
				break;
			}
#endif // ! DPNBUILD_NOIPV6

#ifndef DPNBUILD_NOIPX
			case AF_IPX:
			{
				TestSocket = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
				break;
			}
#endif // ! DPNBUILD_NOIPX

			default:
			{
				DNASSERT(pSPData->GetType() == AF_INET);
				TestSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_IP );
				break;
			}
		}
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
		if ( TestSocket != INVALID_SOCKET )
		{
			INT		iBufferSize;
			INT		iBufferSizeSize;
			INT		iWSAReturn;


			iBufferSizeSize = sizeof( iBufferSize );
			iWSAReturn = getsockopt( TestSocket,									// socket
									   SOL_SOCKET,									// socket level option
									   SO_RCVBUF,									// socket option
									   reinterpret_cast<char*>( &iBufferSize ),		// pointer to destination
									   &iBufferSizeSize								// pointer to destination size
									   );
			if ( iWSAReturn != SOCKET_ERROR )
			{
				pCapsData->dwSystemBufferSize = iBufferSize;
			}
			else
			{
				DPFX(DPFPREP, 0, "Failed to get socket receive buffer options!" );
				DisplayWinsockError( 0, iWSAReturn );
			}

			closesocket( TestSocket );
			TestSocket = INVALID_SOCKET;
		}
	}
	else
	{
		pCapsData->dwSystemBufferSize = g_iWinsockReceiveBufferSize;
	}

#ifndef DPNBUILD_ONLYONETHREAD
Exit:
#endif // !DPNBUILD_ONLYONETHREAD
	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;

#ifndef DPNBUILD_ONLYONETHREAD
Failure:
	goto Exit;
#endif // !DPNBUILD_ONLYONETHREAD
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// DNSP_SetCaps - set SP capabilities
//
// Entry:		Pointer to DNSP interface
//				Pointer to caps data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_SetCaps"

STDMETHODIMP	DNSP_SetCaps( IDP8ServiceProvider *pThis, SPSETCAPSDATA *pCapsData )
{
	HRESULT			hr;
	CSPData			*pSPData;
#ifndef DPNBUILD_NOREGISTRY
	CRegistry		RegObject;
#endif // ! DPNBUILD_NOREGISTRY


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pCapsData);

	DNASSERT( pThis != NULL );
	DNASSERT( pCapsData != NULL );
	DNASSERT( pCapsData->dwSize == sizeof( *pCapsData ) );


	//
	// initialize
	//
	hr = DPN_OK;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );
	

	//
	// no need to tell thread pool to lock the thread count for this function.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// validate caps
	//
	if ( pCapsData->dwBuffersPerThread == 0 )
	{
		DPFX(DPFPREP, 0, "Failing SetCaps because dwBuffersPerThread == 0" );
		hr = DPNERR_INVALIDPARAM;
		goto Failure;
	}

#ifndef DPNBUILD_ONLYONETHREAD
	//
	// change thread count, if requested
	//
	if ( pCapsData->dwIOThreadCount != 0 )
	{
		hr = pSPData->GetThreadPool()->SetIOThreadCount( pCapsData->dwIOThreadCount );
		if ( hr != DPN_OK )
		{
			DPFX(DPFPREP, 0, "Failed to set thread pool count!" );
			DisplayDNError( 0, hr );
			goto Failure;
		}
	}
#endif // ! DPNBUILD_ONLYONETHREAD


	//
	// dwBuffersPerThread is ignored.
	//


	//
	// Set the receive buffer size.
	//
	DBG_CASSERT( sizeof( pCapsData->dwSystemBufferSize ) == sizeof( g_iWinsockReceiveBufferSize ) );
	g_fWinsockReceiveBufferSizeOverridden = TRUE;
	g_iWinsockReceiveBufferSize = pCapsData->dwSystemBufferSize;
#ifndef WINCE
	pSPData->SetWinsockBufferSizeOnAllSockets( g_iWinsockReceiveBufferSize );
#endif // ! WINCE


Exit:
	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;

Failure:
	goto Exit;
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// DNSP_ReturnReceiveBuffers - return receive buffers to pool
//
// Entry:		Pointer to DNSP interface
//				Pointer to caps data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_ReturnReceiveBuffers"

STDMETHODIMP	DNSP_ReturnReceiveBuffers( IDP8ServiceProvider *pThis, SPRECEIVEDBUFFER *pReceivedBuffers )
{
	SPRECEIVEDBUFFER	*pBuffers;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pReceivedBuffers);

	//
	// no need to tell thread pool to lock the thread count for this function.
	//

	DNASSERT( pThis != NULL );
	DNASSERT( pReceivedBuffers != NULL );

	pBuffers = pReceivedBuffers;
	while ( pBuffers != NULL )
	{
		SPRECEIVEDBUFFER	*pTemp;
		CReadIOData			*pReadData;


		pTemp = pBuffers;
		pBuffers = pBuffers->pNext;
		pReadData = CReadIOData::ReadDataFromSPReceivedBuffer( pTemp );
		DEBUG_ONLY( pReadData->m_fRetainedByHigherLayer = FALSE );
		pReadData->DecRef();
	}

	//DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);
	DPFX(DPFPREP, 2, "Returning: DPN_OK");

	return DPN_OK;
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// DNSP_GetAddressInfo - get address information for an endpoint
//
// Entry:		Pointer to DNSP Interface
//				Pointer to input data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_GetAddressInfo"

STDMETHODIMP	DNSP_GetAddressInfo( IDP8ServiceProvider *pThis, SPGETADDRESSINFODATA *pGetAddressInfoData )
{
	HRESULT		hr;
	CEndpoint	*pEndpoint;
	CSPData		*pSPData;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pGetAddressInfoData);

	DNASSERT( pThis != NULL );
	DNASSERT( pGetAddressInfoData != NULL );
	DNASSERT( pGetAddressInfoData->hEndpoint != INVALID_HANDLE_VALUE && pGetAddressInfoData->hEndpoint != 0 );
#ifdef DPNBUILD_NOMULTICAST
	DNASSERT( ( pGetAddressInfoData->Flags & ~( SP_GET_ADDRESS_INFO_LOCAL_ADAPTER |
												SP_GET_ADDRESS_INFO_LISTEN_HOST_ADDRESSES |
												SP_GET_ADDRESS_INFO_LOCAL_HOST_PUBLIC_ADDRESS |
												SP_GET_ADDRESS_INFO_REMOTE_HOST ) ) == 0 );
#else // ! DPNBUILD_NOMULTICAST
	DNASSERT( ( pGetAddressInfoData->Flags & ~( SP_GET_ADDRESS_INFO_LOCAL_ADAPTER |
												SP_GET_ADDRESS_INFO_LISTEN_HOST_ADDRESSES |
												SP_GET_ADDRESS_INFO_LOCAL_HOST_PUBLIC_ADDRESS |
												SP_GET_ADDRESS_INFO_REMOTE_HOST |
												SP_GET_ADDRESS_INFO_MULTICAST_GROUP ) ) == 0 );
#endif // ! DPNBUILD_NOMULTICAST

	//
	// initialize
	//
	hr = DPN_OK;
	DBG_CASSERT( sizeof( pEndpoint ) == sizeof( pGetAddressInfoData->hEndpoint ) );
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// no need to tell thread pool to lock the thread count for this function.
	//
	pEndpoint = pSPData->EndpointFromHandle( pGetAddressInfoData->hEndpoint );
	if ( pEndpoint != NULL )
	{
		switch ( pGetAddressInfoData->Flags )
		{
			case SP_GET_ADDRESS_INFO_LOCAL_ADAPTER:
			{
				pGetAddressInfoData->pAddress = pEndpoint->GetLocalAdapterDP8Address( SP_ADDRESS_TYPE_DEVICE );
				if (pGetAddressInfoData->pAddress == NULL)
				{
					DPFX(DPFPREP, 0, "Couldn't get local adapter device address!");
					hr = DPNERR_OUTOFMEMORY;
				}
				break;
			}

			case SP_GET_ADDRESS_INFO_LISTEN_HOST_ADDRESSES:
			{
				pGetAddressInfoData->pAddress = pEndpoint->GetLocalAdapterDP8Address( SP_ADDRESS_TYPE_HOST );
				if (pGetAddressInfoData->pAddress == NULL)
				{
					DPFX(DPFPREP, 0, "Couldn't get local adapter host address!");
					hr = DPNERR_OUTOFMEMORY;
				}
				break;
			}

			case SP_GET_ADDRESS_INFO_LOCAL_HOST_PUBLIC_ADDRESS:
			{
				pGetAddressInfoData->pAddress = pEndpoint->GetLocalAdapterDP8Address( SP_ADDRESS_TYPE_PUBLIC_HOST_ADDRESS );
				break;
			}

			case SP_GET_ADDRESS_INFO_REMOTE_HOST:
			{
				pGetAddressInfoData->pAddress = pEndpoint->GetRemoteHostDP8Address();
				if (pGetAddressInfoData->pAddress == NULL)
				{
					DPFX(DPFPREP, 0, "Couldn't get remote host address!");
					hr = DPNERR_OUTOFMEMORY;
				}
				break;
			}
			
#ifndef DPNBUILD_NOMULTICAST
			case SP_GET_ADDRESS_INFO_MULTICAST_GROUP:
			{
				pGetAddressInfoData->pAddress = pEndpoint->GetRemoteHostDP8Address();

				//
				// If we successfully got an address, add the multicast scope GUID.
				//
				if (pGetAddressInfoData->pAddress != NULL)
				{
					GUID	guidScope;


					pEndpoint->GetScopeGuid(&guidScope);
					hr = IDirectPlay8Address_AddComponent(pGetAddressInfoData->pAddress,
															DPNA_KEY_SCOPE,
															&guidScope,
															sizeof(guidScope),
															DPNA_DATATYPE_GUID);
					if (hr != DPN_OK)
					{
						DPFX(DPFPREP, 0, "Couldn't add scope GUID component to address (err = 0x%lx)!  Ignoring.", hr);
						hr = DPN_OK;
					}
				}
				else
				{
					DPFX(DPFPREP, 0, "Couldn't get multicast group address!");
					hr = DPNERR_OUTOFMEMORY;
				}
				break;
			}
#endif // ! DPNBUILD_NOMULTICAST
			
			default:
			{
				DNASSERT( FALSE );
				break;
			}
		}
	
		pEndpoint->DecCommandRef();
		pEndpoint = NULL;
	}
	else
	{
		hr = DPNERR_INVALIDENDPOINT;
	}

	if ( hr != DPN_OK )
	{
		DPFX(DPFPREP, 0, "Problem getting DNAddress from endpoint!" );
		DisplayDNError( 0, hr );
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;
}
//**********************************************************************



//**********************************************************************
// ------------------------------
// DNSP_Update - update information/status of an endpoint
//
// Entry:		Pointer to DNSP Interface
//				Pointer to input data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_Update"

STDMETHODIMP	DNSP_Update( IDP8ServiceProvider *pThis, SPUPDATEDATA *pUpdateData )
{
	HRESULT		hr;
	CSPData		*pSPData;
	CEndpoint	*pEndpoint;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pUpdateData);

	DNASSERT( pThis != NULL );
	DNASSERT( pUpdateData != NULL );
	
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// no need to tell thread pool to lock the thread count for this function.
	//
	
	switch ( pUpdateData->UpdateType )
	{
		case SP_UPDATE_HOST_MIGRATE:
		{
#ifdef DBG
			DNASSERT( ( pUpdateData->hEndpoint != INVALID_HANDLE_VALUE ) && ( pUpdateData->hEndpoint != NULL ) );
			DBG_CASSERT( sizeof( pEndpoint ) == sizeof( pUpdateData->hEndpoint ) );
			pEndpoint = pSPData->EndpointFromHandle( pUpdateData->hEndpoint );
			if (pEndpoint == NULL)
			{
				DPFX(DPFPREP, 0, "Host migrate endpoint 0x%p is invalid!", pEndpoint);
				DNASSERT( FALSE );
				hr = DPNERR_INVALIDENDPOINT;
				break;
			}

			DNASSERT( pEndpoint->GetType() == ENDPOINT_TYPE_LISTEN );

			DPFX(DPFPREP, 3, "Host migrated to listen endpoint 0x%p.", pEndpoint);

			pEndpoint->DecCommandRef();
			pEndpoint = NULL;
#endif // DBG

			hr = DPN_OK;
			break;
		}
		
		case SP_UPDATE_ALLOW_ENUMS:
		case SP_UPDATE_DISALLOW_ENUMS:
		{
			DNASSERT( ( pUpdateData->hEndpoint != INVALID_HANDLE_VALUE ) && ( pUpdateData->hEndpoint != NULL ) );
			DBG_CASSERT( sizeof( pEndpoint ) == sizeof( pUpdateData->hEndpoint ) );
			pEndpoint = pSPData->EndpointFromHandle( pUpdateData->hEndpoint );
			if (pEndpoint == NULL)
			{
				DPFX(DPFPREP, 0, "Allow/disallow enums endpoint 0x%p is invalid!", pEndpoint);
				DNASSERT( FALSE );
				hr = DPNERR_INVALIDENDPOINT;
				break;
			}
			
			DNASSERT( pEndpoint->GetType() == ENDPOINT_TYPE_LISTEN );

			if ( pUpdateData->UpdateType == SP_UPDATE_ALLOW_ENUMS )
			{
				DPFX(DPFPREP, 3, "Allowing enums on listen endpoint 0x%p.", pEndpoint);
				pEndpoint->SetEnumsAllowedOnListen( TRUE, TRUE );
			}
			else
			{
				DPFX(DPFPREP, 3, "Disallowing enums on listen endpoint 0x%p.", pEndpoint);
				pEndpoint->SetEnumsAllowedOnListen( FALSE, TRUE );
			}
			
			pEndpoint->DecCommandRef();
			pEndpoint = NULL;
			
			hr = DPN_OK;
			break;
		}

		default:
		{
			DPFX(DPFPREP, 0, "Unsupported update type %u!", pUpdateData->UpdateType);
			hr = DPNERR_UNSUPPORTED;
			break;
		}
	}

	
	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;
}
//**********************************************************************

#ifndef DPNBUILD_LIBINTERFACE

//**********************************************************************
// ------------------------------
// DNSP_IsApplicationSupported - determine if this application is supported by this
//		SP.
//
// Entry:		Pointer to DNSP Interface
//				Pointer to input data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_IsApplicationSupported"

STDMETHODIMP	DNSP_IsApplicationSupported( IDP8ServiceProvider *pThis, SPISAPPLICATIONSUPPORTEDDATA *pIsApplicationSupportedData )
{
	CSPData			*pSPData;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pIsApplicationSupportedData);

	DNASSERT( pThis != NULL );
	DNASSERT( pIsApplicationSupportedData != NULL );
	DNASSERT( pIsApplicationSupportedData->pApplicationGuid != NULL );
	DNASSERT( pIsApplicationSupportedData->dwFlags == 0 );

	//
	// initialize, we support all applications with this SP
	//
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	//
	// no need to tell thread pool to lock the thread count for this function.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );


	DPFX(DPFPREP, 2, "Returning: DPN_OK");

	return	DPN_OK;
}
//**********************************************************************

#endif // ! DPNBUILD_LIBINTERFACE



#ifndef DPNBUILD_ONLYONEADAPTER

//**********************************************************************
// ------------------------------
// DNSP_EnumAdapters - get a list of adapters for this SP
//
// Entry:		Pointer DNSP Interface
//				Pointer to input data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_EnumAdapters"

STDMETHODIMP	DNSP_EnumAdapters( IDP8ServiceProvider *pThis, SPENUMADAPTERSDATA *pEnumAdaptersData )
{
	HRESULT			hr;
	CSocketAddress	*pSPAddress;
	CSPData			*pSPData;	


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pEnumAdaptersData);

	DNASSERT( pThis != NULL );
	DNASSERT( pEnumAdaptersData != NULL );
	DNASSERT( ( pEnumAdaptersData->pAdapterData != NULL ) ||
			  ( pEnumAdaptersData->dwAdapterDataSize == 0 ) );
	DNASSERT( pEnumAdaptersData->dwFlags == 0 );


	//
	// initialize
	//
	hr = DPN_OK;
	pEnumAdaptersData->dwAdapterCount = 0;
	pSPAddress = NULL;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	//
	// no need to tell thread pool to lock the thread count for this function.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// get an SP address from the pool to perform conversions to GUIDs
	//
#if ((defined(DPNBUILD_NOIPV6)) && (defined(DPNBUILD_NOIPX)))
	pSPAddress = (CSocketAddress*) g_SocketAddressPool.Get((PVOID) ((DWORD_PTR) AF_INET));
#else // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	pSPAddress = (CSocketAddress*) g_SocketAddressPool.Get((PVOID) ((DWORD_PTR) pSPData->GetType()));
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	if ( pSPAddress == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		DPFX(DPFPREP, 0, "Failed to get address for GUID conversions in DNSP_EnumAdapters!" );
		goto Failure;
	}

	//
	// enumerate adapters
	//
	hr = pSPAddress->EnumAdapters( pEnumAdaptersData );
	if ( hr != DPN_OK )
	{
		if (hr == DPNERR_BUFFERTOOSMALL)
		{
			DPFX(DPFPREP, 1, "Buffer too small for enumerating adapters.");
		}
		else
		{
			DPFX(DPFPREP, 0, "Problem enumerating adapters (err = 0x%lx)!", hr);
			DisplayDNError( 0, hr );
		}

		goto Failure;
	}

Exit:
	if ( pSPAddress != NULL )
	{
		g_SocketAddressPool.Release( pSPAddress );
		pSPAddress = NULL;
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;

Failure:
	goto Exit;
}
//**********************************************************************

#endif // ! DPNBUILD_ONLYONEADAPTER


#ifndef DPNBUILD_SINGLEPROCESS
//**********************************************************************
// ------------------------------
// DNSP_ProxyEnumQuery - proxy an enum query
//
// Entry:		Pointer DNSP Interface
//				Pointer to input data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_ProxyEnumQuery"

STDMETHODIMP	DNSP_ProxyEnumQuery( IDP8ServiceProvider *pThis, SPPROXYENUMQUERYDATA *pProxyEnumQueryData )
{
	HRESULT								hr;
	CSPData								*pSPData;
	CSocketAddress						*pDestinationAddress;
	CSocketAddress						*pReturnAddress;
	CEndpoint							*pEndpoint;
	const ENDPOINT_ENUM_QUERY_CONTEXT	*pEndpointEnumContext;
	BUFFERDESC							BufferDesc[2];
	PREPEND_BUFFER						PrependBuffer;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pProxyEnumQueryData);

	DNASSERT( pThis != NULL );
	DNASSERT( pProxyEnumQueryData != NULL );
	DNASSERT( pProxyEnumQueryData->dwFlags == 0 );

	//
	// initialize
	//
	hr = DPN_OK;
	DBG_CASSERT( OFFSETOF( ENDPOINT_ENUM_QUERY_CONTEXT, EnumQueryData ) == 0 );
	pEndpointEnumContext = reinterpret_cast<ENDPOINT_ENUM_QUERY_CONTEXT*>( pProxyEnumQueryData->pIncomingQueryData );
	DNASSERT(pEndpointEnumContext->pReturnAddress != NULL);
	pSPData = CSPData::SPDataFromCOMInterface( pThis );
	pDestinationAddress = NULL;
	pReturnAddress = NULL;
	pEndpoint = NULL;

	//
	// No need to tell thread pool to lock the thread count for this function
	// because there's already an outstanding enum that did.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// preallocate addresses
	//
#if ((defined(DPNBUILD_NOIPV6)) && (defined(DPNBUILD_NOIPX)))
	pDestinationAddress = (CSocketAddress*) g_SocketAddressPool.Get((PVOID) ((DWORD_PTR) AF_INET));
#else // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	pDestinationAddress = (CSocketAddress*) g_SocketAddressPool.Get((PVOID) ((DWORD_PTR) pEndpointEnumContext->pReturnAddress->GetFamily()));
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	if ( pDestinationAddress == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		goto Failure;
	}

#if ((defined(DPNBUILD_NOIPV6)) && (defined(DPNBUILD_NOIPX)))
	pReturnAddress = (CSocketAddress*) g_SocketAddressPool.Get((PVOID) ((DWORD_PTR) AF_INET));
#else // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	pReturnAddress = (CSocketAddress*) g_SocketAddressPool.Get((PVOID) ((DWORD_PTR) pEndpointEnumContext->pReturnAddress->GetFamily()));
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	if ( pReturnAddress == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		goto Failure;
	}

	//
	// set the endpoint and send it along
	//
	pEndpoint = pSPData->EndpointFromHandle( pEndpointEnumContext->hEndpoint );
	if ( pEndpoint == NULL )
	{
		hr = DPNERR_INVALIDENDPOINT;
		DPFX(DPFPREP, 8, "Invalid endpoint handle in DNSP_ProxyEnumQuery" );
		goto Failure;
	}

	
	//
	// set destination address from the supplied data
	//
	hr = pDestinationAddress->SocketAddressFromDP8Address( pProxyEnumQueryData->pDestinationAdapter,
#ifdef DPNBUILD_XNETSECURITY
															NULL,
#endif // DPNBUILD_XNETSECURITY
#ifndef DPNBUILD_ONLYONETHREAD
															FALSE,
#endif // DPNBUILD_ONLYONETHREAD
															SP_ADDRESS_TYPE_DEVICE );
	if ( hr != DPN_OK )
	{
		DPFX(DPFPREP, 0, "ProxyEnumQuery: Failed to convert target adapter address" );
		goto Failure;
	}

	//
	// set return address from incoming enum query
	//
	memcpy( pReturnAddress->GetWritableAddress(),
			pEndpointEnumContext->pReturnAddress->GetAddress(),
			pEndpointEnumContext->pReturnAddress->GetAddressSize() );
	

	DNASSERT(pProxyEnumQueryData->pIncomingQueryData->pReceivedData->pNext == NULL);
	DNASSERT( pEndpointEnumContext->dwEnumKey <= WORD_MAX );

	BufferDesc[0].pBufferData = reinterpret_cast<BYTE*>(&PrependBuffer.ProxiedEnumDataHeader);
	BufferDesc[0].dwBufferSize = sizeof( PrependBuffer.ProxiedEnumDataHeader );
	memcpy(&BufferDesc[1],
			&pProxyEnumQueryData->pIncomingQueryData->pReceivedData->BufferDesc,
			sizeof(BufferDesc[1]));

	PrependBuffer.ProxiedEnumDataHeader.bSPLeadByte = SP_HEADER_LEAD_BYTE;
	PrependBuffer.ProxiedEnumDataHeader.bSPCommandByte = PROXIED_ENUM_DATA_KIND;
	PrependBuffer.ProxiedEnumDataHeader.wEnumKey = static_cast<WORD>( pEndpointEnumContext->dwEnumKey );
	//
	// We could save 2 bytes on IPX by only passing 14 bytes for the
	// SOCKADDR structure but it's not worth it, especially since it's
	// looping back in the local network stack.  SOCKADDR structures are also
	// 16 bytes so reducing the data passed to 14 bytes would destroy alignment.
	//
	// Note that if we're using the large IPv6 addresses, the IPX wasted space is
	// larger, and IPv4 addresses will now waste some, too.
	//
	DBG_CASSERT( (sizeof( PrependBuffer.ProxiedEnumDataHeader.ReturnAddress ) % 4) == 0 );
	memcpy( &PrependBuffer.ProxiedEnumDataHeader.ReturnAddress,
			pReturnAddress->GetAddress(),
			sizeof( PrependBuffer.ProxiedEnumDataHeader.ReturnAddress ) );

#ifdef DPNBUILD_ASYNCSPSENDS
	pEndpoint->GetSocketPort()->SendData( BufferDesc, 2, pDestinationAddress, NULL );
#else // ! DPNBUILD_ASYNCSPSENDS
	pEndpoint->GetSocketPort()->SendData( BufferDesc, 2, pDestinationAddress );
#endif // ! DPNBUILD_ASYNCSPSENDS

	pEndpoint->DecCommandRef();
	pEndpoint = NULL;

Exit:
	if ( pReturnAddress != NULL )
	{
		g_SocketAddressPool.Release( pReturnAddress );
		pReturnAddress = NULL;
	}
	if (pDestinationAddress != NULL )
	{
		g_SocketAddressPool.Release( pDestinationAddress );
		pDestinationAddress = NULL;
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;

Failure:
	if (pEndpoint != NULL )
	{
		pEndpoint->DecCommandRef();
		pEndpoint = NULL;
	}
	goto Exit;
}
//**********************************************************************

#endif // ! DPNBUILD_SINGLEPROCESS

//**********************************************************************
/*
 *
 *	DNSP_NotSupported is used for methods required to implement the
 *  interface but that are not supported by this SP.
 *
 */
//**********************************************************************
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_NotSupported"

STDMETHODIMP DNSP_NotSupported( IDP8ServiceProvider *pThis, PVOID pvParam )
{
	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pvParam);
	DPFX(DPFPREP, 2, "Returning: [DPNERR_UNSUPPORTED]");
	return DPNERR_UNSUPPORTED;
}
//**********************************************************************



#ifndef DPNBUILD_NOMULTICAST

//**********************************************************************
// ------------------------------
// DNSP_EnumMulticastScopes - get a list of multicast scopes for this SP
//
// Entry:		Pointer DNSP Interface
//				Pointer to input data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_EnumMulticastScopes"

STDMETHODIMP	DNSP_EnumMulticastScopes( IDP8ServiceProvider *pThis, SPENUMMULTICASTSCOPESDATA *pEnumMulticastScopesData )
{
	HRESULT			hr;
	CSPData			*pSPData;
	CSocketAddress	*pSPAddress;
	BOOL			fUseMADCAP;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pEnumMulticastScopesData);

	DNASSERT( pThis != NULL );
	DNASSERT( pEnumMulticastScopesData != NULL );
	DNASSERT( pEnumMulticastScopesData->pguidAdapter != NULL );
	DNASSERT( ( pEnumMulticastScopesData->pScopeData != NULL ) ||
			  ( pEnumMulticastScopesData->dwScopeDataSize == 0 ) );
	DNASSERT( pEnumMulticastScopesData->dwFlags == 0 );


	//
	// initialize
	//
	hr = DPN_OK;
	pEnumMulticastScopesData->dwScopeCount = 0;
	pSPAddress = NULL;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	//
	// no need to tell thread pool to lock the thread count for this function.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	// get an SP address from the pool to perform conversions to GUIDs
	//
#if ((defined(DPNBUILD_NOIPV6)) && (defined(DPNBUILD_NOIPX)))
	pSPAddress = (CSocketAddress*) g_SocketAddressPool.Get((PVOID) ((DWORD_PTR) AF_INET));
#else // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	pSPAddress = (CSocketAddress*) g_SocketAddressPool.Get((PVOID) ((DWORD_PTR) pSPData->GetType()));
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX
	if ( pSPAddress == NULL )
	{
		hr = DPNERR_OUTOFMEMORY;
		DPFX(DPFPREP, 0, "Failed to get address for GUID conversions in DNSP_EnumMulticastScopes!" );
		goto Failure;
	}

	//
	// enumerate adapters
	//
#ifdef WINNT
	fUseMADCAP = pSPData->GetThreadPool()->EnsureMadcapLoaded();
#else // ! WINNT
	fUseMADCAP = FALSE;
#endif // ! WINNT
	hr = pSPAddress->EnumMulticastScopes( pEnumMulticastScopesData, fUseMADCAP );
	if ( hr != DPN_OK )
	{
		if (hr == DPNERR_BUFFERTOOSMALL)
		{
			DPFX(DPFPREP, 1, "Buffer too small for enumerating scopes.");
		}
		else
		{
			DPFX(DPFPREP, 0, "Problem enumerating scopes (err = 0x%lx)!", hr);
			DisplayDNError( 0, hr );
		}

		goto Failure;
	}


Exit:

	if ( pSPAddress != NULL )
	{
		g_SocketAddressPool.Release( pSPAddress );
		pSPAddress = NULL;
	}

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;


Failure:

	goto Exit;
}
//**********************************************************************

//**********************************************************************
// ------------------------------
// DNSP_ShareEndpointInfo - get a list of multicast scopes for this SP
//
// Entry:		Pointer DNSP Interface
//				Pointer to input data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_ShareEndpointInfo"

STDMETHODIMP	DNSP_ShareEndpointInfo( IDP8ServiceProvider *pThis, SPSHAREENDPOINTINFODATA *pShareEndpointInfoData )
{
	HRESULT			hr;
	CSPData			*pSPData;
	CSPData			*pSPDataShare;
	BOOL			fShareInterfaceReferenceAdded;
	CSocketData		*pSocketData;
#if ((! defined(DPNBUILD_NOIPV6)) || (! defined(DPNBUILD_NOIPX)))
	short			sShareSPType;
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pShareEndpointInfoData);

	DNASSERT( pThis != NULL );
	DNASSERT( pShareEndpointInfoData != NULL );
	DNASSERT( pShareEndpointInfoData->pDP8ServiceProvider != NULL );
	DNASSERT( pShareEndpointInfoData->dwFlags == 0 );


	//
	// initialize
	//
	hr = DPN_OK;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );
	pSPDataShare = CSPData::SPDataFromCOMInterface( pShareEndpointInfoData->pDP8ServiceProvider );
	fShareInterfaceReferenceAdded = FALSE;
	pSocketData = NULL;

	//
	// no need to tell thread pool to lock the thread count for this function.
	//


	//
	// First, validate the source (shared) SP's state.  We must assume it's a
	// valid dpnwsock SP (CSPData::SPDataFromCOMInterface should assert if not),
	// but we can make sure it has been initialized.
	//
	pSPDataShare->Lock();
	switch ( pSPDataShare->GetState() )
	{
		//
		// provider is initialized, add a reference and proceed
		//
		case SPSTATE_INITIALIZED:
		{
			IDP8ServiceProvider_AddRef( pShareEndpointInfoData->pDP8ServiceProvider );
			fShareInterfaceReferenceAdded = TRUE;
			DNASSERT( hr == DPN_OK );
			break;
		}

		//
		// provider is uninitialized
		//
		case SPSTATE_UNINITIALIZED:
		{
			hr = DPNERR_UNINITIALIZED;
			DPFX(DPFPREP, 0, "ShareEndpointInfo called with uninitialized shared SP 0x%p!",
				pSPDataShare );

			break;
		}

		//
		// provider is closing
		//
		case SPSTATE_CLOSING:
		{
			hr = DPNERR_ABORTED;
				DPFX(DPFPREP, 0, "ShareEndpointInfo called with shared SP 0x%p that is closing!",
					pSPDataShare );

			break;
		}

		//
		// unknown
		//
		default:
		{
			DPFX(DPFPREP, 0, "ShareEndpointInfo called with shared SP 0x%p in unrecognized state %u!",
				pSPDataShare, pSPDataShare->GetState() );
			DNASSERT( FALSE );
			hr = DPNERR_GENERIC;

			break;
		}
	}
	pSPDataShare->Unlock();
	if ( hr != DPN_OK )
	{
		goto Failure;
	}

#if ((! defined(DPNBUILD_NOIPV6)) || (! defined(DPNBUILD_NOIPX)))
	//
	// We can also double check that it's not the wrong type (IP vs. IPX).
	//
	sShareSPType = pSPDataShare->GetType();
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX


	//
	// Make sure the source SP has a valid socket data object, and get a
	// reference to it.  Don't create it if it didn't exist, though.
	//
	pSPDataShare->Lock();
	pSocketData = pSPDataShare->GetSocketData();
	if (pSocketData == NULL)
	{
		pSPDataShare->Unlock();
		
		DPFX(DPFPREP, 0, "Cannot share endpoint info, shared SP has not created its own endpoint information yet!" );
		hr = DPNERR_NOTREADY;
		goto Failure;
	}

	pSocketData->AddRef();
	pSPDataShare->Unlock();


	IDP8ServiceProvider_Release( pShareEndpointInfoData->pDP8ServiceProvider );
	fShareInterfaceReferenceAdded = FALSE;


	//
	// Validate the local SP's state
	//
	pSPData->Lock();

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

#if ((! defined(DPNBUILD_NOIPV6)) || (! defined(DPNBUILD_NOIPX)))
	if (pSPData->GetType() != sShareSPType)
	{
		pSPData->Unlock();
		DPFX(DPFPREP, 0, "ShareEndpointInfo called on different SP types (0x%p == state %u, 0x%p == state %u)!",
			pSPData, pSPData->GetState(), pSPDataShare, pSPDataShare->GetState() );
		hr = DPNERR_INVALIDINTERFACE;
		goto Failure;
	}
#endif // ! DPNBUILD_NOIPV6 or ! DPNBUILD_NOIPX


	//
	// If we're here, the provider is initialized and of the right type.
	// Make sure we do not already have "endpoint info" of our own.
	//
	if (pSPData->GetSocketData() != NULL)
	{
		pSPData->Unlock();
		DPFX(DPFPREP, 0, "Cannot share endpoint info, SP has already created its own endpoint information!" );
		hr = DPNERR_ALREADYINITIALIZED;
		goto Failure;
	}

	//
	// Transfer the local reference to the SP data object.
	//
	pSPData->SetSocketData(pSocketData);
	pSocketData = NULL;

	pSPData->Unlock();

	DNASSERT( hr == DPN_OK );


Exit:

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;


Failure:

	if ( pSocketData != NULL )
	{
		pSocketData->Release();
		pSocketData = NULL;
	}

	if ( fShareInterfaceReferenceAdded != FALSE )
	{
		IDP8ServiceProvider_Release( pThis );
		fShareInterfaceReferenceAdded = FALSE;
	}

	goto Exit;
}
//**********************************************************************

//**********************************************************************
// ------------------------------
// DNSP_GetEndpointByAddress - retrieves an endpoint, given its addressing information
//
// Entry:		Pointer DNSP Interface
//				Pointer to input data
//
// Exit:		Error code
// ------------------------------
#undef DPF_MODNAME
#define	DPF_MODNAME "DNSP_GetEndpointByAddress"

STDMETHODIMP	DNSP_GetEndpointByAddress( IDP8ServiceProvider* pThis, SPGETENDPOINTBYADDRESSDATA *pGetEndpointByAddressData )
{
	HRESULT			hr;
	CSPData			*pSPData;


	DPFX(DPFPREP, 2, "Parameters: (0x%p, 0x%p)", pThis, pGetEndpointByAddressData);

	DNASSERT( pThis != NULL );
	DNASSERT( pGetEndpointByAddressData != NULL );
	DNASSERT( pGetEndpointByAddressData->pAddressHost != NULL );
	DNASSERT( pGetEndpointByAddressData->pAddressDeviceInfo != NULL );
	DNASSERT( pGetEndpointByAddressData->dwFlags == 0 );


	//
	// initialize
	//
	hr = DPN_OK;
	pSPData = CSPData::SPDataFromCOMInterface( pThis );

	//
	// no need to tell thread pool to lock the thread count for this function.
	//

	// Trust protocol to call us only in the initialized state
	DNASSERT( pSPData->GetState() == SPSTATE_INITIALIZED );

	//
	//  Look up the endpoint handle and context
	//
	hr = pSPData->GetEndpointFromAddress(pGetEndpointByAddressData->pAddressHost,
										pGetEndpointByAddressData->pAddressDeviceInfo,
										&pGetEndpointByAddressData->hEndpoint,
										&pGetEndpointByAddressData->pvEndpointContext);
	if (hr != DPN_OK)
	{
		DPFX(DPFPREP, 0, "Couldn't get endpoint from address (err = 0x%lx)!", hr);
		goto Failure;
	}


Exit:

	DPFX(DPFPREP, 2, "Returning: [0x%lx]", hr);

	return	hr;


Failure:

	goto Exit;
}
//**********************************************************************

#endif // ! DPNBUILD_NOMULTICAST

