4764 lines
129 KiB
C++
4764 lines
129 KiB
C++
/*==========================================================================
|
|
*
|
|
* Copyright (C) 1998-2000 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: SocketPort.cpp
|
|
* Content: Winsock socket port that manages data flow on a given adapter,
|
|
* address and port.
|
|
*
|
|
*
|
|
* History:
|
|
* Date By Reason
|
|
* ==== == ======
|
|
* 01/20/99 jtk Created
|
|
* 05/12/99 jtk Derived from modem endpoint class
|
|
* 03/22/20000 jtk Updated with changes to interface names
|
|
***************************************************************************/
|
|
|
|
#include "dnwsocki.h"
|
|
|
|
|
|
#undef DPF_SUBCOMP
|
|
#define DPF_SUBCOMP DN_SUBCOMP_WSOCK
|
|
|
|
|
|
//**********************************************************************
|
|
// Constant definitions
|
|
//**********************************************************************
|
|
|
|
#define SOCKET_RECEIVE_BUFFER_SIZE ( 128 * 1024 )
|
|
|
|
#define NAT_LEASE_TIME 3600000 // ask for 1 hour
|
|
|
|
|
|
//
|
|
// DPlay port limits (inclusive) scanned to find an available port.
|
|
// Exclude 2300 and 2301 because there are network broadcasts on 2301
|
|
// that we may receive.
|
|
//
|
|
static const WORD g_wBaseDPlayPort = 2302;
|
|
static const WORD g_wMaxDPlayPort = 2400;
|
|
|
|
//**********************************************************************
|
|
// Macro definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Structure definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Variable definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Function prototypes
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Function definitions
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::CSocketPort - constructor
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
//
|
|
// Notes: Do not allocate anything in a constructor
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::CSocketPort"
|
|
|
|
CSocketPort::CSocketPort():
|
|
m_pSPData( NULL ),
|
|
m_pThreadPool( NULL ),
|
|
m_iRefCount( 0 ),
|
|
m_iEndpointRefCount( 0 ),
|
|
m_State( SOCKET_PORT_STATE_UNKNOWN ),
|
|
m_pNetworkSocketAddress( NULL ),
|
|
m_pAdapterEntry( NULL ),
|
|
m_Socket( INVALID_SOCKET ),
|
|
m_hListenEndpoint( INVALID_HANDLE_VALUE ),
|
|
m_iEnumKey( INVALID_ENUM_KEY ),
|
|
m_dwSocketPortID( 0 ),
|
|
m_fUsingProxyWinSockLSP( FALSE ),
|
|
m_pRemoveSocketPortData( NULL ),
|
|
m_pSendFunction( NULL ),
|
|
m_iThreadsInReceive(0)
|
|
{
|
|
m_Sig[0] = 'S';
|
|
m_Sig[1] = 'O';
|
|
m_Sig[2] = 'K';
|
|
m_Sig[3] = 'P';
|
|
|
|
DEBUG_ONLY( m_fInitialized = FALSE );
|
|
m_ActiveListLinkage.Initialize();
|
|
m_blConnectEndpointList.Initialize();
|
|
ZeroMemory( m_ahNATHelpPorts, sizeof(m_ahNATHelpPorts) );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::~CSocketPort - destructor
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::~CSocketPort"
|
|
|
|
CSocketPort::~CSocketPort()
|
|
{
|
|
#ifdef DEBUG
|
|
DWORD dwTemp;
|
|
|
|
|
|
//
|
|
// m_pThis needs to be around for the life of the endpoint
|
|
// it should be part of the constructor, but can't be since we're using
|
|
// a pool manager
|
|
//
|
|
DNASSERT( m_pSPData == NULL );
|
|
DNASSERT( m_fInitialized == FALSE );
|
|
|
|
DNASSERT( m_iRefCount == 0 );
|
|
DNASSERT( m_iEndpointRefCount == 0 );
|
|
DNASSERT( m_State == SOCKET_PORT_STATE_UNKNOWN );
|
|
DNASSERT( GetSocket() == INVALID_SOCKET );
|
|
DNASSERT( m_pNetworkSocketAddress == NULL );
|
|
DNASSERT( m_pAdapterEntry == NULL );
|
|
for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
|
|
{
|
|
DNASSERT( m_ahNATHelpPorts[dwTemp] == NULL );
|
|
}
|
|
DNASSERT( m_ActiveListLinkage.IsEmpty() != FALSE );
|
|
DNASSERT( m_blConnectEndpointList.IsEmpty() != FALSE );
|
|
DNASSERT( m_hListenEndpoint == INVALID_HANDLE_VALUE );
|
|
DNASSERT( m_iEnumKey == INVALID_ENUM_KEY );
|
|
DNASSERT( m_pRemoveSocketPortData == NULL );
|
|
DNASSERT( m_pSendFunction == NULL );
|
|
DNASSERT( m_pThreadPool == NULL );
|
|
DNASSERT( m_pSPData == NULL );
|
|
|
|
DNASSERT( m_iThreadsInReceive == 0);
|
|
#endif // DEBUG
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Initialize - initialize this socket port
|
|
//
|
|
// Entry: Pointer to CSPData
|
|
// Pointer to address to bind to
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Initialize"
|
|
|
|
HRESULT CSocketPort::Initialize( CSPData *const pSPData, CSocketAddress *const pAddress )
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hTempResult;
|
|
|
|
|
|
DNASSERT( pSPData != NULL );
|
|
DNASSERT( pAddress != NULL );
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Parameters (0x%p, 0x%p)", this, pSPData, pAddress);
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
m_pSPData = pSPData;
|
|
m_pSPData->ObjectAddRef();
|
|
m_pThreadPool = m_pSPData->GetThreadPool();
|
|
|
|
// Deinitialize will assert that these are set in the fail cases, so we set them up front
|
|
DEBUG_ONLY( m_fInitialized = TRUE );
|
|
DNASSERT( m_State != SOCKET_PORT_STATE_INITIALIZED );
|
|
m_State = SOCKET_PORT_STATE_INITIALIZED;
|
|
|
|
//
|
|
// attempt to initialize the internal critical sections
|
|
//
|
|
if ( DNInitializeCriticalSection( &m_Lock ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Failed to initialize critical section for socket port!" );
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_Lock, 0 );
|
|
|
|
if ( DNInitializeCriticalSection( &m_EndpointDataLock ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Failed to initialize EndpointDataLock critical section!" );
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_EndpointDataLock, 0 );
|
|
|
|
//
|
|
// attempt to initialize the contained send queue
|
|
//
|
|
hr = m_SendQueue.Initialize();
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem initializing send queue!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// initialize the endpoint list with 64 entries and grow by a factor of 16
|
|
//
|
|
DNASSERT( hr == DPN_OK );
|
|
if ( m_ConnectEndpointHash.Initialize( 6, 4 ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Could not initialize the connect endpoint list!" );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// initialize enum list with 16 entries and grow by a factor of 4
|
|
//
|
|
DNASSERT( hr == DPN_OK );
|
|
if ( m_EnumEndpointHash.Initialize( 4, 2 ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Could not initialize the enum endpoint list!" );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// allocate addresses:
|
|
// local address this socket is binding to
|
|
// address of received messages
|
|
//
|
|
DNASSERT( m_pNetworkSocketAddress == NULL );
|
|
m_pNetworkSocketAddress = pAddress;
|
|
|
|
DNASSERT( m_pSendFunction == NULL );
|
|
|
|
//
|
|
// Winsock 2 functionality always exists on WinNT. If we're on Win9x, we can
|
|
// only use Winsock2 interfaces for TCP.
|
|
//
|
|
#ifdef WINNT
|
|
m_pSendFunction = Winsock2Send;
|
|
#else // WIN95
|
|
if ( ( LOWORD( GetWinsockVersion() ) >= 2 ) && ( m_pSPData->GetType() == TYPE_IP ) )
|
|
{
|
|
m_pSendFunction = Winsock2Send;
|
|
}
|
|
else
|
|
{
|
|
m_pSendFunction = Winsock1Send;
|
|
}
|
|
#endif
|
|
|
|
Exit:
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem in CSocketPort::Initialize()" );
|
|
DisplayDNError( 0, hr );
|
|
}
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Leave [0x%lx]", this, hr);
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
DEBUG_ONLY( m_fInitialized = FALSE );
|
|
|
|
hTempResult = Deinitialize();
|
|
if ( hTempResult != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem deinitializing CSocketPort on failed Initialize!" );
|
|
DisplayDNError( 0, hTempResult );
|
|
}
|
|
|
|
m_pNetworkSocketAddress = NULL;
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Deinitialize - deinitialize this socket port
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Deinitialize"
|
|
|
|
HRESULT CSocketPort::Deinitialize( void )
|
|
{
|
|
HRESULT hr;
|
|
#ifdef DEBUG
|
|
DWORD dwTemp;
|
|
#endif // DEBUG
|
|
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Enter", this);
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
Lock();
|
|
DNASSERT( ( m_State == SOCKET_PORT_STATE_INITIALIZED ) ||
|
|
( m_State == SOCKET_PORT_STATE_UNBOUND ) );
|
|
DEBUG_ONLY( m_fInitialized = FALSE );
|
|
|
|
DNASSERT( m_iEndpointRefCount == 0 );
|
|
DNASSERT( m_iRefCount == 0 );
|
|
|
|
//
|
|
// return base network socket addresses
|
|
//
|
|
if ( m_pNetworkSocketAddress != NULL )
|
|
{
|
|
m_pSPData->ReturnAddress( m_pNetworkSocketAddress );
|
|
m_pNetworkSocketAddress = NULL;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
|
|
{
|
|
DNASSERT( m_ahNATHelpPorts[dwTemp] == NULL );
|
|
}
|
|
#endif // DEBUG
|
|
|
|
m_pSendFunction = NULL;
|
|
m_iEnumKey = INVALID_ENUM_KEY;
|
|
|
|
m_SendQueue.Deinitialize();
|
|
|
|
m_EnumEndpointHash.Deinitialize();
|
|
m_ConnectEndpointHash.Deinitialize();
|
|
|
|
Unlock();
|
|
|
|
DNDeleteCriticalSection( &m_EndpointDataLock );
|
|
DNDeleteCriticalSection( &m_Lock );
|
|
|
|
DNASSERT( m_pSPData != NULL );
|
|
m_pSPData->ObjectDecRef();
|
|
m_pSPData = NULL;
|
|
m_pThreadPool = NULL;
|
|
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Leave [0x%lx]", this, hr);
|
|
|
|
return hr;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::EndpointAddRef - increment endpoint reference count
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
//
|
|
// Notes: This function should not be called without having the SP's SocketPort
|
|
// data locked to make sure we're the only ones playing with socket ports!
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::EndpointAddRef"
|
|
|
|
void CSocketPort::EndpointAddRef( void )
|
|
{
|
|
Lock();
|
|
|
|
//
|
|
// add a global reference and then add an endpoint reference
|
|
//
|
|
DNASSERT( m_iEndpointRefCount != -1 );
|
|
m_iEndpointRefCount++;
|
|
AddRef();
|
|
|
|
DPFX(DPFPREP, 9, "(0x%p) Endpoint refcount is now %i.",
|
|
this, m_iEndpointRefCount );
|
|
|
|
Unlock();
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::EndpointDecRef - decrement endpoint reference count
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Endpoint reference count
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::EndpointDecRef"
|
|
|
|
DWORD CSocketPort::EndpointDecRef( void )
|
|
{
|
|
DWORD dwReturn;
|
|
|
|
|
|
Lock();
|
|
|
|
DNASSERT( m_iEndpointRefCount != 0 );
|
|
|
|
m_iEndpointRefCount--;
|
|
dwReturn = m_iEndpointRefCount;
|
|
if ( m_iEndpointRefCount == 0 )
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
DPFX(DPFPREP, 7, "(0x%p) Endpoint refcount hit 0, beginning to unbind from network.", this );
|
|
|
|
//
|
|
// No more endpoints are referencing this item, unbind this socket port
|
|
// from the network and then remove it from the active socket port list.
|
|
// If we're on Winsock1, tell the other thread that this socket needs to
|
|
// be removed so we can get rid of our outstanding I/O reference.
|
|
//
|
|
#ifdef WIN95
|
|
if ( ( LOWORD( GetWinsockVersion() ) == 1 ) || ( m_pSPData->GetType() == TYPE_IPX ) )
|
|
{
|
|
m_pSPData->GetThreadPool()->RemoveSocketPort( this );
|
|
}
|
|
#endif
|
|
|
|
// Don't allow any more receives through
|
|
m_State = SOCKET_PORT_STATE_UNBOUND;
|
|
|
|
// Wait for any receives that were already in to get out
|
|
while (m_iThreadsInReceive != 0)
|
|
{
|
|
DPFX(DPFPREP, 9, "There are %i threads still receiving for socketport 0x%p...", m_iThreadsInReceive, this);
|
|
Unlock();
|
|
Sleep(10);
|
|
Lock();
|
|
}
|
|
|
|
hr = UnbindFromNetwork();
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem unbinding from network when final endpoint has disconnected!" );
|
|
DisplayDNError( 0, hr );
|
|
}
|
|
|
|
DNASSERT( m_pNetworkSocketAddress != NULL );
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 9, "(0x%p) Endpoint refcount is %i, not unbinding from network.",
|
|
this, m_iEndpointRefCount );
|
|
}
|
|
|
|
//
|
|
// Decrement global reference count. This had better not result in this
|
|
// socketport being returned to the pool! There should always be at
|
|
// least one more regular reference than an endpoint reference because
|
|
// our caller should have a regular reference.
|
|
//
|
|
DNASSERT(m_iRefCount > 1);
|
|
DecRef();
|
|
|
|
Unlock();
|
|
|
|
return dwReturn;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::BindEndpoint - add an endpoint to this SP's list
|
|
//
|
|
// Entry: Pointer to endpoint
|
|
// Gateway bind type
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::BindEndpoint"
|
|
|
|
HRESULT CSocketPort::BindEndpoint( CEndpoint *const pEndpoint, GATEWAY_BIND_TYPE GatewayBindType )
|
|
{
|
|
HRESULT hr;
|
|
#ifdef DEBUG
|
|
const CSocketAddress * pSocketAddress;
|
|
const SOCKADDR * pSockAddr;
|
|
#endif // DEBUG
|
|
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Parameters (0x%p, %i)",
|
|
this, pEndpoint, GatewayBindType);
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
DNASSERT( m_iRefCount != 0 );
|
|
DNASSERT( m_iEndpointRefCount != 0 );
|
|
|
|
pEndpoint->ChangeLoopbackAlias( GetNetworkAddress() );
|
|
|
|
LockEndpointData();
|
|
|
|
|
|
switch ( pEndpoint->GetType() )
|
|
{
|
|
//
|
|
// Treat 'connect' and 'connect on listen' endpoints as the same type.
|
|
// We don't care how many connections are made through this socket port,
|
|
// just make sure we're not connecting to the same person more than once.
|
|
//
|
|
case ENDPOINT_TYPE_CONNECT:
|
|
case ENDPOINT_TYPE_CONNECT_ON_LISTEN:
|
|
{
|
|
HANDLE hExistingEndpoint;
|
|
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// Make sure it's a valid address.
|
|
//
|
|
|
|
pSocketAddress = pEndpoint->GetRemoteAddressPointer();
|
|
DNASSERT(pSocketAddress != NULL);
|
|
pSockAddr = pSocketAddress->GetAddress();
|
|
DNASSERT(pSockAddr != NULL);
|
|
|
|
if (pSocketAddress->GetFamily() == AF_INET)
|
|
{
|
|
DNASSERT( ((SOCKADDR_IN*) pSockAddr)->sin_addr.S_un.S_addr != 0 );
|
|
DNASSERT( ((SOCKADDR_IN*) pSockAddr)->sin_addr.S_un.S_addr != INADDR_BROADCAST );
|
|
DNASSERT( pSocketAddress->GetPort() != 0 );
|
|
}
|
|
#endif // DEBUG
|
|
|
|
if ( m_ConnectEndpointHash.Find( pEndpoint->GetRemoteAddressPointer() , &hExistingEndpoint ) != FALSE )
|
|
{
|
|
hr = DPNERR_ALREADYINITIALIZED;
|
|
DPFX(DPFPREP, 0, "Attempted to connect twice to the same endpoint!" );
|
|
goto Failure;
|
|
}
|
|
|
|
DNASSERT( hr == DPN_OK );
|
|
if ( m_ConnectEndpointHash.Insert( pEndpoint->GetRemoteAddressPointer(), pEndpoint->GetHandle() ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Problem adding endpoint to connect socket port hash!" );
|
|
goto Failure;
|
|
}
|
|
|
|
if (pEndpoint->GetType() == ENDPOINT_TYPE_CONNECT)
|
|
{
|
|
pEndpoint->AddToSocketPortList(&this->m_blConnectEndpointList);
|
|
|
|
//
|
|
// CONNECTs must be on a DPlay selected or fixed port. They can't be
|
|
// shared but the underlying socketport must be mapped on the gateway.
|
|
//
|
|
DNASSERT((GatewayBindType == GATEWAY_BIND_TYPE_DEFAULT) || (GatewayBindType == GATEWAY_BIND_TYPE_SPECIFIC));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// CONNECT_ON_LISTEN endpoints should always be bound as NONE
|
|
// since they should not need port mappings on the gateway.
|
|
//
|
|
DNASSERT(GatewayBindType == GATEWAY_BIND_TYPE_NONE);
|
|
}
|
|
pEndpoint->SetSocketPort( this );
|
|
pEndpoint->AddRef();
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// we only allow one listen endpoint on a socket port
|
|
//
|
|
case ENDPOINT_TYPE_LISTEN:
|
|
{
|
|
if ( m_hListenEndpoint != INVALID_HANDLE_VALUE )
|
|
{
|
|
hr = DPNERR_ALREADYINITIALIZED;
|
|
DPFX(DPFPREP, 0, "Attempted to listen more than once on a given SocketPort!" );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// LISTENs can be on a DPlay selected or fixed port, and the fixed port
|
|
// may be shared.
|
|
//
|
|
DNASSERT((GatewayBindType == GATEWAY_BIND_TYPE_DEFAULT) || (GatewayBindType == GATEWAY_BIND_TYPE_SPECIFIC) || (GatewayBindType == GATEWAY_BIND_TYPE_SPECIFIC_SHARED));
|
|
|
|
m_hListenEndpoint = pEndpoint->GetHandle();
|
|
pEndpoint->SetSocketPort( this );
|
|
pEndpoint->AddRef();
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// we don't allow duplicate enum endpoints
|
|
//
|
|
case ENDPOINT_TYPE_ENUM:
|
|
{
|
|
HANDLE hExistingEndpoint;
|
|
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// Make sure it's a valid address.
|
|
//
|
|
|
|
pSocketAddress = pEndpoint->GetRemoteAddressPointer();
|
|
DNASSERT(pSocketAddress != NULL);
|
|
pSockAddr = pSocketAddress->GetAddress();
|
|
DNASSERT(pSockAddr != NULL);
|
|
|
|
if (pSocketAddress->GetFamily() == AF_INET)
|
|
{
|
|
DNASSERT( ((SOCKADDR_IN*) pSockAddr)->sin_addr.S_un.S_addr != 0 );
|
|
DNASSERT( pSocketAddress->GetPort() != 0 );
|
|
}
|
|
#endif // DEBUG
|
|
|
|
pEndpoint->SetEnumKey( GetEnumKey() );
|
|
if ( m_EnumEndpointHash.Find( pEndpoint->GetEnumKey(), &hExistingEndpoint ) != FALSE )
|
|
{
|
|
hr = DPNERR_ALREADYINITIALIZED;
|
|
DPFX(DPFPREP, 0, "Attempted to enum twice to the same endpoint!" );
|
|
goto Failure;
|
|
}
|
|
|
|
DNASSERT( hr == DPN_OK );
|
|
if ( m_EnumEndpointHash.Insert( pEndpoint->GetEnumKey(), pEndpoint->GetHandle() ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Problem adding endpoint to enum socket port h!" );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// ENUMs must be on a DPlay selected or fixed port. They can't be
|
|
// shared but the underlying socketport must be mapped on the gateway.
|
|
//
|
|
DNASSERT((GatewayBindType == GATEWAY_BIND_TYPE_DEFAULT) || (GatewayBindType == GATEWAY_BIND_TYPE_SPECIFIC));
|
|
|
|
pEndpoint->SetSocketPort( this );
|
|
pEndpoint->AddRef();
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// unknown endpoint type
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
hr = DPNERR_GENERIC;
|
|
goto Failure;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pEndpoint->SetGatewayBindType(GatewayBindType);
|
|
|
|
|
|
UnlockEndpointData();
|
|
|
|
Exit:
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Returning [0x%lx]", this, hr);
|
|
|
|
return hr;
|
|
|
|
|
|
Failure:
|
|
UnlockEndpointData();
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::UnbindEndpoint - remove an endpoint from the SP's list
|
|
//
|
|
// Entry: Pointer to endpoint
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::UnbindEndpoint"
|
|
|
|
void CSocketPort::UnbindEndpoint( CEndpoint *const pEndpoint )
|
|
{
|
|
#ifdef DEBUG
|
|
HANDLE hFindTemp;
|
|
#endif // DEBUG
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Parameters (0x%p)", this, pEndpoint);
|
|
|
|
LockEndpointData();
|
|
|
|
|
|
pEndpoint->SetGatewayBindType(GATEWAY_BIND_TYPE_UNKNOWN);
|
|
|
|
|
|
//
|
|
// adjust any special pointers before removing endpoint
|
|
//
|
|
switch ( pEndpoint->GetType() )
|
|
{
|
|
//
|
|
// Connect and connect-on-listen endpoints are the same.
|
|
// Remove endpoint from connect list.
|
|
//
|
|
case ENDPOINT_TYPE_CONNECT:
|
|
case ENDPOINT_TYPE_CONNECT_ON_LISTEN:
|
|
{
|
|
DNASSERT( m_ConnectEndpointHash.Find( pEndpoint->GetRemoteAddressPointer(), &hFindTemp ) );
|
|
m_ConnectEndpointHash.Remove( pEndpoint->GetRemoteAddressPointer() );
|
|
|
|
if (pEndpoint->GetType() == ENDPOINT_TYPE_CONNECT)
|
|
{
|
|
pEndpoint->RemoveFromSocketPortList();
|
|
}
|
|
|
|
//
|
|
// The multiplex list is protected by the SPData -> LockSocketPortData() lock,
|
|
// which should already be taken by our caller. Unfortunately we can't assert
|
|
// that since we don't have access to that member variable.
|
|
// Removing from a list when not in a list does not cause any problems.
|
|
//
|
|
//AssertCriticalSectionIsTakenByThisThread( &this->m_pSPData->m_SocketPortDataLock, TRUE );
|
|
pEndpoint->RemoveFromMultiplexList();
|
|
|
|
pEndpoint->SetSocketPort( NULL );
|
|
pEndpoint->DecRef();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// make sure this is really the active listen and then remove it
|
|
//
|
|
case ENDPOINT_TYPE_LISTEN:
|
|
{
|
|
DNASSERT( m_hListenEndpoint != INVALID_HANDLE_VALUE );
|
|
m_hListenEndpoint = INVALID_HANDLE_VALUE;
|
|
pEndpoint->SetSocketPort( NULL );
|
|
pEndpoint->DecRef();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// remove endpoint from enum list
|
|
//
|
|
case ENDPOINT_TYPE_ENUM:
|
|
{
|
|
DEBUG_ONLY( DNASSERT( m_EnumEndpointHash.Find( pEndpoint->GetEnumKey(), &hFindTemp ) != FALSE ) );
|
|
m_EnumEndpointHash.Remove( pEndpoint->GetEnumKey() );
|
|
|
|
//
|
|
// The multiplex list is protected by the SPData -> LockSocketPortData() lock,
|
|
// which should already be taken by our caller. Unfortunately we can't assert
|
|
// that since we don't have access to that member variable.
|
|
// Removing from a list when not in a list does not cause any problems.
|
|
//
|
|
//AssertCriticalSectionIsTakenByThisThread( &this->m_pSPData->m_SocketPortDataLock, TRUE );
|
|
pEndpoint->RemoveFromMultiplexList();
|
|
|
|
pEndpoint->SetSocketPort( NULL );
|
|
pEndpoint->DecRef();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
UnlockEndpointData();
|
|
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Leave", this);
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::ReturnSelfToPool - return this object to the pool
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::ReturnSelfToPool"
|
|
|
|
void CSocketPort::ReturnSelfToPool( void )
|
|
{
|
|
m_State = SOCKET_PORT_STATE_UNKNOWN;
|
|
DNASSERT( m_pSPData == NULL );
|
|
ReturnSocketPort( this );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::SendEnumQueryData - send data for an enum query
|
|
//
|
|
// Entry: Pointer to write data
|
|
// Enum key
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::SendEnumQueryData"
|
|
|
|
void CSocketPort::SendEnumQueryData( CWriteIOData *const pWriteData, const UINT_PTR uEnumKey )
|
|
{
|
|
pWriteData->m_pBuffers = &pWriteData->m_pBuffers[ -1 ];
|
|
pWriteData->m_uBufferCount++;
|
|
DBG_CASSERT( sizeof( &pWriteData->m_PrependBuffer.EnumDataHeader ) == sizeof( BYTE* ) );
|
|
pWriteData->m_pBuffers[ 0 ].pBufferData = reinterpret_cast<BYTE*>( &pWriteData->m_PrependBuffer.EnumDataHeader );
|
|
pWriteData->m_pBuffers[ 0 ].dwBufferSize = sizeof( pWriteData->m_PrependBuffer.EnumDataHeader );
|
|
|
|
DNASSERT( pWriteData->m_PrependBuffer.EnumResponseDataHeader.bSPLeadByte == SP_HEADER_LEAD_BYTE);
|
|
pWriteData->m_PrependBuffer.EnumResponseDataHeader.bSPCommandByte = ENUM_DATA_KIND;
|
|
|
|
DNASSERT( uEnumKey <= WORD_MAX );
|
|
pWriteData->m_PrependBuffer.EnumResponseDataHeader.wEnumResponsePayload = static_cast<WORD>( uEnumKey );
|
|
|
|
|
|
SendData( pWriteData );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::SendData - send data
|
|
//
|
|
// Entry: Pointer to write data
|
|
// Pointer to return address (real address the message should be returned to)
|
|
// Enum key
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::SendProxiedEnumData"
|
|
|
|
void CSocketPort::SendProxiedEnumData( CWriteIOData *const pWriteData, const CSocketAddress *const pReturnAddress, const UINT_PTR uOldEnumKey )
|
|
{
|
|
DNASSERT( pWriteData != NULL );
|
|
DNASSERT( pReturnAddress != NULL );
|
|
|
|
pWriteData->m_pBuffers = &pWriteData->m_pBuffers[ -1 ];
|
|
pWriteData->m_uBufferCount++;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
DBG_CASSERT( sizeof( pWriteData->m_PrependBuffer.ProxiedEnumDataHeader.ReturnAddress ) == 16 );
|
|
DBG_CASSERT( sizeof( &pWriteData->m_PrependBuffer.ProxiedEnumDataHeader ) == sizeof( BYTE* ) );
|
|
pWriteData->m_pBuffers[ 0 ].pBufferData = reinterpret_cast<BYTE*>( &pWriteData->m_PrependBuffer.ProxiedEnumDataHeader );
|
|
pWriteData->m_pBuffers[ 0 ].dwBufferSize = sizeof( pWriteData->m_PrependBuffer.ProxiedEnumDataHeader );
|
|
|
|
DNASSERT( pWriteData->m_PrependBuffer.ProxiedEnumDataHeader.bSPLeadByte == SP_HEADER_LEAD_BYTE );
|
|
pWriteData->m_PrependBuffer.ProxiedEnumDataHeader.bSPCommandByte = PROXIED_ENUM_DATA_KIND;
|
|
|
|
DNASSERT( uOldEnumKey <= WORD_MAX );
|
|
pWriteData->m_PrependBuffer.ProxiedEnumDataHeader.wEnumKey = static_cast<WORD>( uOldEnumKey );
|
|
|
|
DBG_CASSERT( sizeof( pWriteData->m_PrependBuffer.ProxiedEnumDataHeader.ReturnAddress ) == sizeof( *pReturnAddress->GetAddress() ) );
|
|
memcpy( &pWriteData->m_PrependBuffer.ProxiedEnumDataHeader.ReturnAddress,
|
|
pReturnAddress->GetAddress(),
|
|
sizeof( pWriteData->m_PrependBuffer.ProxiedEnumDataHeader.ReturnAddress ) );
|
|
|
|
SendData( pWriteData );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::SendData - send data
|
|
//
|
|
// Entry: Pointer to write data
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::SendData"
|
|
|
|
void CSocketPort::SendData( CWriteIOData *const pWriteData )
|
|
{
|
|
DNASSERT( pWriteData != NULL );
|
|
DNASSERT( pWriteData->SocketPort() == NULL );
|
|
|
|
pWriteData->SetSocketPort( this );
|
|
|
|
m_SendQueue.Lock();
|
|
|
|
//
|
|
// If the send queue is not empty, add this item to the end of the queue and
|
|
// then attempt to send as much as possible. Otherwise attempt to send this
|
|
// data immediately.
|
|
//
|
|
if ( m_SendQueue.IsEmpty() == FALSE )
|
|
{
|
|
BOOL fIOServiced;
|
|
|
|
|
|
m_SendQueue.Enqueue( pWriteData );
|
|
m_SendQueue.Unlock();
|
|
fIOServiced = SendFromWriteQueue();
|
|
}
|
|
else
|
|
{
|
|
SEND_COMPLETION_CODE SendCompletionCode;
|
|
|
|
|
|
//
|
|
// there are no items in the queue, attempt to send
|
|
//
|
|
m_SendQueue.Unlock();
|
|
|
|
DPFX(DPFPREP, 8, "WriteData 0x%p", pWriteData);
|
|
|
|
pWriteData->m_pCommand->SetState( COMMAND_STATE_INPROGRESS_CANNOT_CANCEL );
|
|
|
|
SendCompletionCode = (this->*m_pSendFunction)( pWriteData );
|
|
switch ( SendCompletionCode )
|
|
{
|
|
//
|
|
// Send has been spooled to Winsock, nothing to do
|
|
//
|
|
case SEND_IN_PROGRESS:
|
|
{
|
|
DPFX(DPFPREP, 8, "SendInProgress, will complete later" );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// send completed immediately on Winsock1
|
|
//
|
|
case SEND_COMPLETED_IMMEDIATELY_WS1:
|
|
{
|
|
SendComplete( pWriteData, DPN_OK );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Send can't be submitted, spool it so it will go out the next
|
|
// time someone tries to send. Reset the command state to allow the
|
|
// user to cancel the command.
|
|
//
|
|
case SEND_WINSOCK_BUSY:
|
|
{
|
|
DPFX(DPFPREP, 8, "Winsock Busy - Requeueing Send" );
|
|
pWriteData->m_pCommand->SetState( COMMAND_STATE_PENDING );
|
|
m_SendQueue.Lock();
|
|
m_SendQueue.AddToFront( pWriteData );
|
|
m_SendQueue.Unlock();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// something went wrong, tell the user that their send barfed and
|
|
// that the connection is probably going bye-bye
|
|
//
|
|
case SEND_FAILED:
|
|
{
|
|
SendComplete( pWriteData, DPNERR_CONNECTIONLOST );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// invalid return
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Winsock1ReadService - service a read request on a socket
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Boolean indicating whether I/O was serviced
|
|
// TRUE = I/O serviced
|
|
// FALSE = I/O not serviced
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Winsock1ReadService"
|
|
|
|
BOOL CSocketPort::Winsock1ReadService( void )
|
|
{
|
|
BOOL fIOServiced;
|
|
INT iSocketReturn;
|
|
READ_IO_DATA_POOL_CONTEXT PoolContext;
|
|
CReadIOData *pReadData;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
fIOServiced = FALSE;
|
|
|
|
//
|
|
// Attempt to get a new receive buffer from the pool. If we fail, we'll
|
|
// just fail to service this read and the socket will still be labeled
|
|
// as ready to receive so we'll try again later.
|
|
//
|
|
PoolContext.SPType = m_pSPData->GetType();
|
|
pReadData = m_pThreadPool->GetNewReadIOData( &PoolContext );
|
|
if ( pReadData == NULL )
|
|
{
|
|
DPFX(DPFPREP, 0, "Could not get read data to perform a Winsock1 read!" );
|
|
goto Exit;
|
|
}
|
|
|
|
DBG_CASSERT( sizeof( pReadData->ReceivedBuffer()->BufferDesc.pBufferData ) == sizeof( char* ) );
|
|
pReadData->m_iSocketAddressSize = pReadData->m_pSourceSocketAddress->GetAddressSize();
|
|
pReadData->SetSocketPort( NULL );
|
|
iSocketReturn = p_recvfrom( GetSocket(), // socket to read from
|
|
reinterpret_cast<char*>( pReadData->ReceivedBuffer()->BufferDesc.pBufferData ), // pointer to receive buffer
|
|
pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize, // size of receive buffer
|
|
0, // flags (none)
|
|
pReadData->m_pSourceSocketAddress->GetWritableAddress(), // address of sending socket
|
|
&pReadData->m_iSocketAddressSize // size of address of sending socket
|
|
);
|
|
switch ( iSocketReturn )
|
|
{
|
|
//
|
|
// socket has been closed
|
|
//
|
|
case 0:
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// problem
|
|
//
|
|
case SOCKET_ERROR:
|
|
{
|
|
DWORD dwWinsockError;
|
|
|
|
|
|
dwWinsockError = p_WSAGetLastError();
|
|
switch ( dwWinsockError )
|
|
{
|
|
//
|
|
// one of our previous sends failed to get through,
|
|
// and we don't really care anymore
|
|
//
|
|
case WSAECONNRESET:
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This socket was probably closed
|
|
//
|
|
case WSAENOTSOCK:
|
|
{
|
|
DPFX(DPFPREP, 8, "Winsock1 reporting 'Not a socket' on receive!" );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// there is no data to read
|
|
//
|
|
case WSAEWOULDBLOCK:
|
|
{
|
|
DPFX(DPFPREP, 8, "Winsock1 reporting there is no data to receive on a socket!" );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// something bad happened
|
|
//
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem with Winsock1 recvfrom!" );
|
|
DisplayWinsockError( 0, dwWinsockError );
|
|
DNASSERT( FALSE );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// bytes were read
|
|
//
|
|
default:
|
|
{
|
|
fIOServiced = TRUE;
|
|
pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize = iSocketReturn;
|
|
ProcessReceivedData( pReadData );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
DNASSERT( pReadData != NULL );
|
|
pReadData->DecRef();
|
|
|
|
Exit:
|
|
return fIOServiced;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Winsock1WriteService - service a write request on a socket
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Boolean indicating whether I/O was serviced
|
|
// TRUE = I/O serviced
|
|
// FALSE = I/O not serviced
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Winsock1WriteService"
|
|
|
|
BOOL CSocketPort::Winsock1WriteService( void )
|
|
{
|
|
BOOL fIOServiced;
|
|
|
|
|
|
fIOServiced = FALSE;
|
|
m_SendQueue.Lock();
|
|
|
|
//
|
|
// if there's data to send, attempt to send it
|
|
//
|
|
if ( m_SendQueue.IsEmpty() == FALSE )
|
|
{
|
|
fIOServiced = SendFromWriteQueue();
|
|
}
|
|
|
|
m_SendQueue.Unlock();
|
|
|
|
return fIOServiced;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Winsock1ErrorService - service an error on this socket
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Boolean indicating whether I/O was serviced
|
|
// TRUE = I/O serviced
|
|
// FALSE = I/O not serviced
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Winsock1ErrorService"
|
|
|
|
BOOL CSocketPort::Winsock1ErrorService( void )
|
|
{
|
|
//
|
|
// this function doesn't do anything because errors on sockets will usually
|
|
// result in the socket being closed soon
|
|
//
|
|
return FALSE;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Winsock1Send - send data in a Winsock 1.0 fashion
|
|
//
|
|
// Entry: Pointer to write data
|
|
//
|
|
// Exit: Send completion code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Winsock1Send"
|
|
|
|
SEND_COMPLETION_CODE CSocketPort::Winsock1Send( CWriteIOData *const pWriteData )
|
|
{
|
|
SEND_COMPLETION_CODE SendCompletionCode;
|
|
INT iSendToReturn;
|
|
UINT_PTR uOutputBufferIndex;
|
|
INT iOutputByteCount;
|
|
char TempBuffer[ MAX_MESSAGE_SIZE ];
|
|
|
|
|
|
DNASSERT( pWriteData != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
SendCompletionCode = SEND_COMPLETED_IMMEDIATELY_WS1;
|
|
|
|
//
|
|
// flatten output data
|
|
//
|
|
iOutputByteCount = 0;
|
|
uOutputBufferIndex = 0;
|
|
|
|
DNASSERT( pWriteData->m_uBufferCount != 0 );
|
|
do
|
|
{
|
|
DNASSERT( ( iOutputByteCount + pWriteData->m_pBuffers[ uOutputBufferIndex ].dwBufferSize ) <= LENGTHOF( TempBuffer ) );
|
|
memcpy( &TempBuffer[ iOutputByteCount ], pWriteData->m_pBuffers[ uOutputBufferIndex ].pBufferData, pWriteData->m_pBuffers[ uOutputBufferIndex ].dwBufferSize );
|
|
iOutputByteCount += pWriteData->m_pBuffers[ uOutputBufferIndex ].dwBufferSize;
|
|
|
|
uOutputBufferIndex++;
|
|
} while( uOutputBufferIndex < pWriteData->m_uBufferCount );
|
|
|
|
#ifdef DEBUG
|
|
DPFX(DPFPREP, 8, "Winsock1 sending %i bytes to socket:", iOutputByteCount);
|
|
DumpSocketAddress( 8, pWriteData->m_pDestinationSocketAddress->GetAddress(), pWriteData->m_pDestinationSocketAddress->GetFamily() );
|
|
|
|
switch (pWriteData->m_pDestinationSocketAddress->GetFamily() )
|
|
{
|
|
case AF_INET:
|
|
{
|
|
SOCKADDR_IN * psaddrin;
|
|
|
|
psaddrin = (SOCKADDR_IN *) pWriteData->m_pDestinationSocketAddress->GetAddress();
|
|
|
|
DNASSERT( psaddrin->sin_addr.S_un.S_addr != 0 );
|
|
DNASSERT( psaddrin->sin_port != 0 );
|
|
|
|
break;
|
|
}
|
|
|
|
case AF_IPX:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
#endif // DEBUG
|
|
|
|
|
|
//
|
|
// there is no need to note an I/O reference because our Winsock1 I/O is synchronous
|
|
//
|
|
iSendToReturn = p_sendto( GetSocket(), // socket
|
|
TempBuffer, // data to send
|
|
iOutputByteCount, // number of bytes to send
|
|
0, // flags (none)
|
|
pWriteData->m_pDestinationSocketAddress->GetAddress(), // pointer to destination address
|
|
pWriteData->m_pDestinationSocketAddress->GetAddressSize() // size of destination address
|
|
);
|
|
switch ( iSendToReturn )
|
|
{
|
|
//
|
|
// problem with send
|
|
//
|
|
case SOCKET_ERROR:
|
|
{
|
|
DWORD dwWinsockError;
|
|
|
|
|
|
dwWinsockError = p_WSAGetLastError();
|
|
switch ( dwWinsockError )
|
|
{
|
|
//
|
|
// socket would block on call
|
|
//
|
|
case WSAEWOULDBLOCK:
|
|
{
|
|
SendCompletionCode = SEND_WINSOCK_BUSY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other problem
|
|
//
|
|
default:
|
|
{
|
|
SendCompletionCode = SEND_FAILED;
|
|
DNASSERT( pWriteData->Win9xOperationPending() == FALSE );
|
|
|
|
DPFX(DPFPREP, 0, "Problem with Winsock1 sendto!" );
|
|
DisplayWinsockError( 0, dwWinsockError );
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// send went through, make sure all bytes were sent
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( iSendToReturn == iOutputByteCount );
|
|
DNASSERT( SendCompletionCode == SEND_COMPLETED_IMMEDIATELY_WS1 );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return SendCompletionCode;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Winsock2Send - send data in a Winsock 2.0 fashion
|
|
//
|
|
// Entry: Pointer to write data
|
|
//
|
|
// Exit: Send completion code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Winsock2Send"
|
|
|
|
SEND_COMPLETION_CODE CSocketPort::Winsock2Send( CWriteIOData *const pWriteData )
|
|
{
|
|
SEND_COMPLETION_CODE SendCompletionCode;
|
|
INT iWSAReturn;
|
|
#ifdef DEBUG
|
|
UINT_PTR uBuffer;
|
|
UINT_PTR uTotalSize;
|
|
#endif // DEBUG
|
|
|
|
|
|
DNASSERT( pWriteData != NULL );
|
|
DNASSERT( pWriteData->SocketPort() == this );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
SendCompletionCode = SEND_IN_PROGRESS;
|
|
|
|
//
|
|
// note an I/O reference before submitting command
|
|
//
|
|
AddRef();
|
|
|
|
DBG_CASSERT( sizeof( pWriteData->m_pBuffers ) == sizeof( WSABUF* ) );
|
|
DBG_CASSERT( sizeof( *pWriteData->m_pBuffers ) == sizeof( WSABUF ) );
|
|
#ifdef WIN95
|
|
DNASSERT(pWriteData->OverlapEvent() != NULL );
|
|
#endif
|
|
DNASSERT( pWriteData->m_pDestinationSocketAddress != NULL );
|
|
|
|
|
|
#ifdef DEBUG
|
|
uTotalSize = 0;
|
|
|
|
for(uBuffer = 0; uBuffer < pWriteData->m_uBufferCount; uBuffer++)
|
|
{
|
|
DNASSERT(pWriteData->m_pBuffers[uBuffer].pBufferData != NULL);
|
|
DNASSERT(pWriteData->m_pBuffers[uBuffer].dwBufferSize != 0);
|
|
|
|
uTotalSize += pWriteData->m_pBuffers[uBuffer].dwBufferSize;
|
|
}
|
|
|
|
DPFX(DPFPREP, 7, "(0x%p) Winsock2 sending %u bytes (in WriteData 0x%p's %u buffers, command = 0x%p) from + to:",
|
|
this, uTotalSize, pWriteData, uBuffer, pWriteData->m_pCommand );
|
|
DumpSocketAddress( 7, this->GetNetworkAddress()->GetAddress(), this->GetNetworkAddress()->GetFamily() );
|
|
DumpSocketAddress( 7, pWriteData->m_pDestinationSocketAddress->GetAddress(), pWriteData->m_pDestinationSocketAddress->GetFamily() );
|
|
|
|
switch (pWriteData->m_pDestinationSocketAddress->GetFamily() )
|
|
{
|
|
case AF_INET:
|
|
{
|
|
SOCKADDR_IN * psaddrin;
|
|
|
|
psaddrin = (SOCKADDR_IN *) pWriteData->m_pDestinationSocketAddress->GetAddress();
|
|
|
|
DNASSERT( psaddrin->sin_addr.S_un.S_addr != 0 );
|
|
DNASSERT( psaddrin->sin_port != 0 );
|
|
|
|
break;
|
|
}
|
|
|
|
case AF_IPX:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
#endif // DEBUG
|
|
|
|
//
|
|
// lock the 'pending operation' list over the call to Winsock to prevent the
|
|
// operation from being completed while it's being set up.
|
|
//
|
|
#ifdef WIN95
|
|
m_pSPData->GetThreadPool()->LockWriteData();
|
|
#endif
|
|
|
|
//
|
|
// Note that this operation is now in a 'pending' status. It really
|
|
// isn't yet, but Windows should alert us to that if we attempt to query
|
|
// for I/O completion before the operation has been submitted. Only assert
|
|
// the 'pending' flag on Win9x.
|
|
//
|
|
DNASSERT( pWriteData->m_dwOverlappedBytesSent == 0 );
|
|
#ifdef WIN95
|
|
DNASSERT( pWriteData->Win9xOperationPending() == FALSE );
|
|
pWriteData->SetWin9xOperationPending( TRUE );
|
|
#endif
|
|
|
|
DNASSERT( pWriteData->m_uBufferCount <= UINT32_MAX );
|
|
iWSAReturn = p_WSASendTo( GetSocket(), // socket
|
|
reinterpret_cast<WSABUF*>( pWriteData->m_pBuffers ), // buffers
|
|
static_cast<DWORD>( pWriteData->m_uBufferCount ), // count of buffers
|
|
&pWriteData->m_dwBytesSent, // pointer to number of bytes sent
|
|
0, // send flags
|
|
pWriteData->m_pDestinationSocketAddress->GetAddress(), // pointer to destination address
|
|
pWriteData->m_pDestinationSocketAddress->GetAddressSize(), // size of destination address
|
|
pWriteData->Overlap(), // pointer to overlap structure
|
|
NULL // APC callback (unused)
|
|
);
|
|
#ifdef WIN95
|
|
m_pSPData->GetThreadPool()->UnlockWriteData();
|
|
#endif
|
|
|
|
if ( iWSAReturn == SOCKET_ERROR )
|
|
{
|
|
DWORD dwWSAError;
|
|
|
|
|
|
dwWSAError = p_WSAGetLastError();
|
|
switch ( dwWSAError )
|
|
{
|
|
//
|
|
// I/O is pending, note that the command cannot be cancelled,
|
|
// wait for completion
|
|
//
|
|
case WSA_IO_PENDING:
|
|
{
|
|
DNASSERT( SendCompletionCode == SEND_IN_PROGRESS );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// could not submit another overlapped I/O request, indicate that
|
|
// the send was busy so someone above us spools the send for later
|
|
//
|
|
case WSAEWOULDBLOCK:
|
|
{
|
|
DPFX(DPFPREP, 8, "Got WSAEWOULDBLOCK from WSASendTo." );
|
|
SendCompletionCode = SEND_WINSOCK_BUSY;
|
|
DecRef();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// socket was closed on us
|
|
//
|
|
case WSAENOTSOCK:
|
|
default:
|
|
{
|
|
SendCompletionCode = SEND_FAILED;
|
|
|
|
DPFX(DPFPREP, 8, "WSASendTo failed (error = %u).", dwWSAError );
|
|
|
|
//
|
|
// the operation was assumed to be pending and it's definitely
|
|
// not going to be sent now
|
|
//
|
|
#ifdef WIN95
|
|
DNASSERT( pWriteData->Win9xOperationPending() != FALSE );
|
|
pWriteData->SetWin9xOperationPending( FALSE );
|
|
#endif
|
|
|
|
switch ( dwWSAError )
|
|
{
|
|
//
|
|
// WSAENOTSOCK: another thread closed the socket
|
|
// WSAENOBUFS: machine out of memory
|
|
// WSAEADDRNOTAVAIL: can't reach destination (dialup connection probably dropped)
|
|
//
|
|
case WSAENOTSOCK:
|
|
case WSAENOBUFS:
|
|
case WSAEADDRNOTAVAIL:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
//
|
|
// something bad happened, stop and take a look
|
|
//
|
|
DisplayWinsockError( 0, dwWSAError );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
DecRef();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Send completed immediately. There should be nothing to do because
|
|
// the delayed I/O completion notification will still be given and we
|
|
// will do final processing at that time.
|
|
//
|
|
DNASSERT( SendCompletionCode == SEND_IN_PROGRESS );
|
|
}
|
|
|
|
return SendCompletionCode;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Winsock2Receive - receive data in a Winsock 2.0 fashion
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Winsock2Receive"
|
|
|
|
HRESULT CSocketPort::Winsock2Receive( void )
|
|
{
|
|
HRESULT hr;
|
|
INT iWSAReturn;
|
|
READ_IO_DATA_POOL_CONTEXT PoolContext;
|
|
CReadIOData *pReadData;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
PoolContext.SPType = m_pSPData->GetType();
|
|
pReadData = m_pThreadPool->GetNewReadIOData( &PoolContext );
|
|
if ( pReadData == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Out of memory attempting Winsock2 read!" );
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// pReadData has one reference so far, the one for this function.
|
|
//
|
|
|
|
|
|
//
|
|
// note the IO reference before attempting the read
|
|
//
|
|
AddRef();
|
|
|
|
DNASSERT( pReadData->m_pSourceSocketAddress != NULL );
|
|
DNASSERT( pReadData->SocketPort() == NULL );
|
|
|
|
DBG_CASSERT( sizeof( pReadData->ReceivedBuffer()->BufferDesc ) == sizeof( WSABUF ) );
|
|
DBG_CASSERT( OFFSETOF( BUFFERDESC, dwBufferSize ) == OFFSETOF( WSABUF, len ) );
|
|
DBG_CASSERT( OFFSETOF( BUFFERDESC, pBufferData ) == OFFSETOF( WSABUF, buf ) );
|
|
|
|
#ifdef WIN95
|
|
DNASSERT(pReadData->OverlapEvent() != NULL );
|
|
#endif
|
|
|
|
pReadData->m_dwReadFlags = 0;
|
|
pReadData->m_iSocketAddressSize = pReadData->m_pSourceSocketAddress->GetAddressSize();
|
|
pReadData->SetSocketPort( this );
|
|
|
|
DPFX(DPFPREP, 8, "Submitting read 0x%p (socketport 0x%p, socket 0x%p).",
|
|
pReadData, this, GetSocket());
|
|
|
|
|
|
//
|
|
// Add a reference for submitting the read to WinSock. This should be
|
|
// removed when the receive completes.
|
|
//
|
|
pReadData->AddRef();
|
|
|
|
|
|
//
|
|
// lock the 'pending operation' list over the call to Winsock to prevent the
|
|
// operation from being completed while it's being set up.
|
|
//
|
|
#ifdef WIN95
|
|
m_pSPData->GetThreadPool()->LockReadData();
|
|
#endif
|
|
|
|
//
|
|
// Note that this operation is 'pending'. It really isn't, but Windows
|
|
// should let us know if we query for completion status and this operation
|
|
// isn't complete. Only assert state on Win9x because NT doesn't use the
|
|
// 'pending' field.
|
|
//
|
|
DNASSERT( pReadData->m_dwOverlappedBytesReceived == 0 );
|
|
#ifdef WIN95
|
|
DNASSERT( pReadData->Win9xOperationPending() == FALSE );
|
|
pReadData->SetWin9xOperationPending( TRUE );
|
|
#endif
|
|
|
|
Reread:
|
|
|
|
if ( GetSocket() == INVALID_SOCKET )
|
|
{
|
|
DPFX(DPFPREP, 1, "Attempting to submit read 0x%p on socketport (0x%p) that does not have a valid handle.",
|
|
pReadData, this);
|
|
}
|
|
|
|
iWSAReturn = p_WSARecvFrom( GetSocket(), // socket
|
|
reinterpret_cast<WSABUF*>( &pReadData->ReceivedBuffer()->BufferDesc ), // pointer to receive buffers
|
|
1, // number of receive buffers
|
|
&pReadData->m_dwBytesRead, // pointer to bytes received (if command completes immediately)
|
|
&pReadData->m_dwReadFlags, // flags (none)
|
|
pReadData->m_pSourceSocketAddress->GetWritableAddress(), // address of sending socket
|
|
&pReadData->m_iSocketAddressSize, // size of address of sending socket
|
|
pReadData->Overlap(), // pointer to overlapped structure
|
|
NULL // APC callback (unused)
|
|
);
|
|
if ( iWSAReturn == 0 )
|
|
{
|
|
DPFX(DPFPREP, 8, "WSARecvFrom for read data 0x%p completed immediately.",
|
|
pReadData );
|
|
|
|
#ifdef WIN95
|
|
//
|
|
// Function completed immediately, drop the lock and signal the
|
|
// read event to ensure that the I/O threads pick up this completed
|
|
// read.
|
|
// Ignore SetEvent failure, there's nothing we can do (and it should
|
|
// never happen anyway).
|
|
//
|
|
|
|
m_pSPData->GetThreadPool()->UnlockReadData();
|
|
|
|
SetEvent(m_pSPData->GetThreadPool()->GetWinsock2ReceiveCompleteEvent());
|
|
#else // WINNT
|
|
//
|
|
// function completed immediately, do nothing, wait for IOCompletion
|
|
// notification to be processed
|
|
//
|
|
#endif // WINNT
|
|
}
|
|
else
|
|
{
|
|
DWORD dwWSAReceiveError;
|
|
|
|
|
|
//
|
|
// failure, check for pending operation
|
|
//
|
|
dwWSAReceiveError = p_WSAGetLastError();
|
|
switch ( dwWSAReceiveError )
|
|
{
|
|
//
|
|
// the send is pending, nothing to do
|
|
//
|
|
case WSA_IO_PENDING:
|
|
{
|
|
#ifdef WIN95
|
|
m_pSPData->GetThreadPool()->UnlockReadData();
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Since this is a UDP socket, this is an indication
|
|
// that a previous send failed. Ignore it and move
|
|
// on.
|
|
//
|
|
case WSAECONNRESET:
|
|
{
|
|
DPFX(DPFPREP, 8, "WSARecvFrom issued a WSACONNRESET." );
|
|
goto Reread;
|
|
break;
|
|
}
|
|
|
|
case WSAENOTSOCK:
|
|
{
|
|
DPFX(DPFPREP, 8, "Got WSAENOTSOCK on RecvFrom." );
|
|
|
|
#ifdef WIN95
|
|
m_pSPData->GetThreadPool()->UnlockReadData();
|
|
#endif
|
|
|
|
hr = DPNERR_GENERIC;
|
|
|
|
DNASSERT( pReadData != NULL );
|
|
|
|
#ifdef WIN95
|
|
DNASSERT( pReadData->Win9xOperationPending() != FALSE );
|
|
pReadData->SetWin9xOperationPending( FALSE );
|
|
#endif
|
|
|
|
//
|
|
// Remove the WinSock reference.
|
|
//
|
|
pReadData->DecRef();
|
|
|
|
//
|
|
// the following DecRef may result in this object being returned to the
|
|
// pool, make sure we don't access member variables after this point!
|
|
//
|
|
DecRef();
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// there was a problem, no completion notification will
|
|
// be given, decrement our IO reference count
|
|
//
|
|
default:
|
|
{
|
|
#ifdef WIN95
|
|
m_pSPData->GetThreadPool()->UnlockReadData();
|
|
#endif
|
|
hr = DPNERR_GENERIC;
|
|
|
|
//
|
|
// 'Known Errors' that we don't want to ASSERT on.
|
|
//
|
|
// WSAEINTR: the socket has been shut down and is about to be/has been closed
|
|
// WSAESHUTDOWN: the socket has been shut down and is about to be/has been closed
|
|
// WSAENOBUFS: out of memory (stress condition)
|
|
//
|
|
switch ( dwWSAReceiveError )
|
|
{
|
|
case WSAEINTR:
|
|
{
|
|
DPFX(DPFPREP, 1, "Got WSAEINTR while trying to RecvFrom." );
|
|
break;
|
|
}
|
|
|
|
case WSAESHUTDOWN:
|
|
{
|
|
DPFX(DPFPREP, 1, "Got WSAESHUTDOWN while trying to RecvFrom." );
|
|
break;
|
|
}
|
|
|
|
case WSAENOBUFS:
|
|
{
|
|
DPFX(DPFPREP, 1, "Got WSAENOBUFS while trying to RecvFrom." );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "Unknown WinSock error when issuing read!" );
|
|
DisplayWinsockError( 0, dwWSAReceiveError );
|
|
DNASSERT( FALSE );
|
|
}
|
|
}
|
|
|
|
DNASSERT( pReadData != NULL );
|
|
|
|
#ifdef WIN95
|
|
DNASSERT( pReadData->Win9xOperationPending() != FALSE );
|
|
pReadData->SetWin9xOperationPending( FALSE );
|
|
#endif
|
|
|
|
//
|
|
// Remove the WinSock reference.
|
|
//
|
|
pReadData->DecRef();
|
|
|
|
//
|
|
// the following DecRef may result in this object being returned to the
|
|
// pool, make sure we don't access member variables after this point!
|
|
//
|
|
DecRef();
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if ( pReadData != NULL )
|
|
{
|
|
pReadData->DecRef();
|
|
}
|
|
return hr;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::SendComplete - a Wisock send is complete, clean up and
|
|
// notify user
|
|
//
|
|
// Entry: Pointer to write data
|
|
// Error code for this operation
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::SendComplete"
|
|
|
|
void CSocketPort::SendComplete( CWriteIOData *const pWriteData, const HRESULT hResult )
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
DNASSERT( pWriteData != NULL );
|
|
#ifdef WIN95
|
|
DNASSERT( pWriteData->Win9xOperationPending() == FALSE );
|
|
#endif
|
|
|
|
|
|
//
|
|
// only signal user if requested
|
|
//
|
|
switch ( pWriteData->m_SendCompleteAction )
|
|
{
|
|
//
|
|
// send command completion to user (most common case)
|
|
//
|
|
case SEND_COMPLETE_ACTION_COMPLETE_COMMAND:
|
|
{
|
|
DPFX(DPFPREP, 8, "Socket port 0x%p completing send command 0x%p, hr = 0x%lx, context = 0x%p to interface 0x%p.",
|
|
this, pWriteData->m_pCommand, hResult,
|
|
pWriteData->m_pCommand->GetUserContext(),
|
|
m_pSPData->DP8SPCallbackInterface());
|
|
|
|
hr = IDP8SPCallback_CommandComplete( m_pSPData->DP8SPCallbackInterface(), // pointer to DirectNet
|
|
pWriteData->m_pCommand, // command handle
|
|
hResult, // error code
|
|
pWriteData->m_pCommand->GetUserContext() // user cookie
|
|
);
|
|
|
|
DPFX(DPFPREP, 8, "Socketport 0x%p returning from command complete [0x%lx].", this, hr);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// no action
|
|
//
|
|
case SEND_COMPLETE_ACTION_NONE:
|
|
{
|
|
if (pWriteData->m_pCommand != NULL)
|
|
{
|
|
DPFX(DPFPREP, 8, "Socket port 0x%p not completing send command 0x%p, hr = 0x%lx, context = 0x%p.",
|
|
this, pWriteData->m_pCommand, hResult, pWriteData->m_pCommand->GetUserContext() );
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 8, "Socket port 0x%p not completing NULL send command, hr = 0x%lx",
|
|
this, hResult );
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Clean up after proxied enum. The proxied enum will remove the
|
|
// reference on the leave the receive buffer that came in on the enum.
|
|
// The destination address that was supplied needs to be released
|
|
// because it was allocated specifically for this task.
|
|
//
|
|
case SEND_COMPLETE_ACTION_PROXIED_ENUM_CLEANUP:
|
|
{
|
|
DPFX(DPFPREP, 8, "Socket port 0x%p completing proxied enum command 0x%p, hr = 0x%lx, context = 0x%p.",
|
|
this, pWriteData->m_pCommand, hResult, pWriteData->m_pCommand->GetUserContext() );
|
|
|
|
DNASSERT( pWriteData->m_pProxiedEnumReceiveBuffer != NULL );
|
|
DNASSERT( pWriteData->m_pDestinationSocketAddress != NULL );
|
|
|
|
pWriteData->m_pProxiedEnumReceiveBuffer->DecRef();
|
|
pWriteData->m_pProxiedEnumReceiveBuffer = NULL;
|
|
|
|
//
|
|
// We know that this address was allocated specifically for this
|
|
// proxied enum and we need to free it.
|
|
//
|
|
m_pSPData->ReturnAddress( const_cast<CSocketAddress*>( pWriteData->m_pDestinationSocketAddress ) );
|
|
pWriteData->m_pDestinationSocketAddress = NULL;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// unknown case
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_pThreadPool->ReturnWriteIOData( pWriteData );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::SetWinsockBufferSize - set the buffer size used by Winsock for
|
|
// this socket.
|
|
//
|
|
// Entry: Buffer size
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::SetWinsockBufferSize"
|
|
|
|
void CSocketPort::SetWinsockBufferSize( const INT iBufferSize ) const
|
|
{
|
|
INT iReturnValue;
|
|
|
|
|
|
DPFX(DPFPREP, 3, "(0x%p) Setting socket 0x%p receive buffer size to: %d",
|
|
this, GetSocket(), g_iWinsockReceiveBufferSize );
|
|
|
|
iReturnValue = p_setsockopt( GetSocket(),
|
|
SOL_SOCKET,
|
|
SO_RCVBUF,
|
|
reinterpret_cast<char*>( &g_iWinsockReceiveBufferSize ),
|
|
sizeof( g_iWinsockReceiveBufferSize )
|
|
);
|
|
if ( iReturnValue == SOCKET_ERROR )
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to set the socket buffer receive size!" );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::IncreaseOutstandingReceives - increase the number of outstanding
|
|
// receives on this socket port.
|
|
//
|
|
// Entry: Number of receives to add
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::IncreaseOutstandingReceives"
|
|
|
|
void CSocketPort::IncreaseOutstandingReceives( const DWORD dwDelta )
|
|
{
|
|
if ( m_pSendFunction == CSocketPort::Winsock2Send )
|
|
{
|
|
DWORD dwIdx;
|
|
|
|
|
|
dwIdx = dwDelta;
|
|
while ( dwIdx != 0 )
|
|
{
|
|
dwIdx--;
|
|
Winsock2Receive();
|
|
}
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::BindToNetwork - bind this socket port to the network
|
|
//
|
|
// Entry: Handle of I/O completion port (NT only)
|
|
// Type of socket
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::BindToNetwork"
|
|
|
|
HRESULT CSocketPort::BindToNetwork( const HANDLE hIOCompletionPort, const GATEWAY_BIND_TYPE GatewayBindType )
|
|
{
|
|
HRESULT hr;
|
|
INT iReturnValue;
|
|
BOOL fTemp;
|
|
BOOL fBoundToNetwork;
|
|
INT iSendBufferSize;
|
|
CSocketAddress * pBoundSocketAddress;
|
|
WORD wBasePort;
|
|
DWORD dwErrorCode;
|
|
DWORD * pdwAddressChunk;
|
|
DWORD * pdwLastAddressChunk;
|
|
DWORD dwTemp;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
fBoundToNetwork = FALSE;
|
|
pBoundSocketAddress = NULL;
|
|
|
|
wBasePort = g_wBaseDPlayPort;
|
|
|
|
|
|
RebindToNextPort:
|
|
|
|
DNASSERT( m_fInitialized != FALSE );
|
|
DNASSERT( m_State == SOCKET_PORT_STATE_INITIALIZED );
|
|
|
|
//
|
|
// get a socket for this socket port
|
|
//
|
|
DNASSERT( GetSocket() == INVALID_SOCKET );
|
|
|
|
#ifdef WIN95
|
|
if (GetWinsockVersion() >= 2)
|
|
{
|
|
#endif
|
|
m_Socket = p_WSASocket( m_pNetworkSocketAddress->GetFamily(), // address family
|
|
SOCK_DGRAM, // datagram (connectionless) socket
|
|
m_pNetworkSocketAddress->GetProtocol(), // protocol
|
|
NULL, // protocol info structure
|
|
NULL, // group
|
|
WSA_FLAG_OVERLAPPED ); // flags
|
|
#ifdef WIN95
|
|
}
|
|
else
|
|
{
|
|
m_Socket = p_socket( m_pNetworkSocketAddress->GetFamily(), // address family
|
|
SOCK_DGRAM, // datagram (connectionless) socket
|
|
m_pNetworkSocketAddress->GetProtocol() ); // protocol
|
|
}
|
|
#endif
|
|
if ( GetSocket() == INVALID_SOCKET )
|
|
{
|
|
hr = DPNERR_NOCONNECTION;
|
|
DPFX(DPFPREP, 0, "Failed to bind to socket!" );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// set socket to allow broadcasts
|
|
//
|
|
fTemp = TRUE;
|
|
DBG_CASSERT( sizeof( &fTemp ) == sizeof( char * ) );
|
|
iReturnValue = p_setsockopt( GetSocket(), // socket
|
|
SOL_SOCKET, // level (set socket options)
|
|
SO_BROADCAST, // set broadcast option
|
|
reinterpret_cast<char *>( &fTemp ), // allow broadcast
|
|
sizeof( fTemp ) // size of parameter
|
|
);
|
|
if ( iReturnValue == SOCKET_ERROR )
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Unable to set broadcast socket option (err = %u)!",
|
|
dwErrorCode );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
hr = DPNERR_GENERIC;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// set socket receive buffer space if the user overrode it
|
|
// Failing this is a preformance hit so ignore and errors.
|
|
//
|
|
if ( g_fWinsockReceiveBufferSizeOverridden != FALSE )
|
|
{
|
|
SetWinsockBufferSize( g_iWinsockReceiveBufferSize) ;
|
|
}
|
|
|
|
//
|
|
// set socket send buffer space to 0 (we will supply all buffers).
|
|
// Failing this is only a performance hit so ignore any errors.
|
|
//
|
|
iSendBufferSize = 0;
|
|
iReturnValue = p_setsockopt( GetSocket(),
|
|
SOL_SOCKET,
|
|
SO_SNDBUF,
|
|
reinterpret_cast<char*>( &iSendBufferSize ),
|
|
sizeof( iSendBufferSize )
|
|
);
|
|
if ( iReturnValue == SOCKET_ERROR )
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to set the socket buffer send size (err = %u)!", dwErrorCode );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
}
|
|
|
|
|
|
#ifdef WIN95
|
|
//
|
|
// put socket into non-blocking mode, if WinSock 1 or 9x IPX
|
|
//
|
|
if ( ( LOWORD( GetWinsockVersion() ) == 1 ) || ( m_pSPData->GetType() == TYPE_IPX ) )
|
|
{
|
|
DPFX(DPFPREP, 5, "Marking socket as non-blocking." );
|
|
|
|
dwTemp = 1;
|
|
iReturnValue = p_ioctlsocket( GetSocket(), // socket
|
|
FIONBIO, // I/O option to set (blocking mode)
|
|
&dwTemp // I/O option value (non-zero puts socked into non-block mode)
|
|
);
|
|
if ( iReturnValue == SOCKET_ERROR )
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Could not set socket into non-blocking mode (err = %u)!",
|
|
dwErrorCode );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
hr = DPNERR_GENERIC;
|
|
goto Failure;
|
|
}
|
|
}
|
|
#else // ! WIN95
|
|
//
|
|
// Attempt to make buffer circular.
|
|
//
|
|
|
|
iReturnValue = p_WSAIoctl(GetSocket(), // socket
|
|
SIO_ENABLE_CIRCULAR_QUEUEING, // io control code
|
|
NULL, // in buffer
|
|
0, // in buffer size
|
|
NULL, // out buffer
|
|
0, // out buffer size
|
|
&dwTemp, // pointer to bytes returned
|
|
NULL, // overlapped
|
|
NULL // completion routine
|
|
);
|
|
if ( iReturnValue == SOCKET_ERROR )
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 1, "Could not enable circular queuing (err = %u), ignoring.",
|
|
dwErrorCode );
|
|
DisplayWinsockError( 1, dwErrorCode );
|
|
}
|
|
|
|
|
|
//
|
|
// Make broadcasts only go out on the interface on which they were sent
|
|
// (as opposed to all interfaces).
|
|
//
|
|
|
|
fTemp = TRUE;
|
|
iReturnValue = p_WSAIoctl(GetSocket(), // socket
|
|
SIO_LIMIT_BROADCASTS, // io control code
|
|
&fTemp, // in buffer
|
|
sizeof(fTemp), // in buffer size
|
|
NULL, // out buffer
|
|
0, // out buffer size
|
|
&dwTemp, // pointer to bytes returned
|
|
NULL, // overlapped
|
|
NULL // completion routine
|
|
);
|
|
if ( iReturnValue == SOCKET_ERROR )
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 1, "Could not limit broadcasts (err = %u), ignoring.",
|
|
dwErrorCode );
|
|
DisplayWinsockError( 1, dwErrorCode );
|
|
}
|
|
#endif // ! WIN95
|
|
|
|
|
|
//
|
|
// bind socket
|
|
//
|
|
DPFX(DPFPREP, 1, "Binding to socket addess:" );
|
|
DumpSocketAddress( 1, m_pNetworkSocketAddress->GetAddress(), m_pNetworkSocketAddress->GetFamily() );
|
|
|
|
DNASSERT( GetSocket() != INVALID_SOCKET );
|
|
|
|
hr = BindToNextAvailablePort( m_pNetworkSocketAddress, wBasePort );
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to bind to network!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
fBoundToNetwork = TRUE;
|
|
|
|
//
|
|
// Find out what address we really bound to. This information is needed to
|
|
// talk to the Internet gateway and will be needed when someone above queries for
|
|
// what the local network address is.
|
|
//
|
|
pBoundSocketAddress = GetBoundNetworkAddress( SP_ADDRESS_TYPE_DEVICE_USE_ANY_PORT );
|
|
if ( pBoundSocketAddress == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Failed to get bound adapter address!" );
|
|
goto Failure;
|
|
}
|
|
DPFX(DPFPREP, 1, "Socket we really bound to:" );
|
|
DumpSocketAddress( 1, pBoundSocketAddress->GetAddress(), pBoundSocketAddress->GetFamily() );
|
|
|
|
|
|
//
|
|
// Perform the same error handling twice for two different functions.
|
|
// 0 = check for an existing mapping
|
|
// 1 = attempt to create a new mapping
|
|
//
|
|
for(dwTemp = 0; dwTemp < 2; dwTemp++)
|
|
{
|
|
if (dwTemp == 0)
|
|
{
|
|
//
|
|
// Make sure we're not slipping under an existing Internet gateway mapping.
|
|
// We have to do this because the current Windows NAT implementations do
|
|
// not mark the port as "in use", so if you bound to a port on the public
|
|
// adapter that had a mapping, you'd never receive any data. It would all
|
|
// be forwarded according to the mapping.
|
|
//
|
|
hr = this->CheckForOverridingMapping( pBoundSocketAddress );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Attempt to bind to an Internet gateway.
|
|
//
|
|
hr = this->BindToInternetGateway( pBoundSocketAddress, GatewayBindType );
|
|
}
|
|
|
|
switch (hr)
|
|
{
|
|
case DPN_OK:
|
|
{
|
|
//
|
|
// 0 = there's no existing mapping that would override our socket
|
|
// 1 = mapping on Internet gateway (if any) was successful
|
|
//
|
|
break;
|
|
}
|
|
|
|
case DPNERR_ALREADYINITIALIZED:
|
|
{
|
|
//
|
|
// 0 = there's an existing mapping that would override our socket
|
|
// 1 = Internet gateway already had a conflicting mapping
|
|
//
|
|
// If we can, try binding to a different port. Otherwise we have to fail.
|
|
//
|
|
if (GatewayBindType == GATEWAY_BIND_TYPE_DEFAULT)
|
|
{
|
|
DPFX(DPFPREP, 1, "%s address already in use on Internet gateway (port = %u), rebinding.",
|
|
((dwTemp == 0) ? "Private" : "Public"),
|
|
p_ntohs(pBoundSocketAddress->GetPort()));
|
|
|
|
|
|
//
|
|
// Whether we succeed in unbinding or not, don't consider this bound anymore.
|
|
//
|
|
fBoundToNetwork = FALSE;
|
|
|
|
hr = UnbindFromNetwork();
|
|
if (hr != DPN_OK)
|
|
{
|
|
DPFX(DPFPREP, 0, "Couldn't unbind network socket address 0x%p from network before rebind attempt!",
|
|
this );
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
//
|
|
// Move to the next port and try again.
|
|
//
|
|
wBasePort = p_ntohs(pBoundSocketAddress->GetPort()) + 1;
|
|
|
|
//
|
|
// If we weren't in the DPlay range, then we must have gone through all
|
|
// of the DPlay range, plus let WinSock pick at least once. Since we can't
|
|
// trust WinSock to not keep picking the same port, we need to manually
|
|
// increase the port number.
|
|
//
|
|
if ((p_ntohs(pBoundSocketAddress->GetPort()) < g_wBaseDPlayPort) || (p_ntohs(pBoundSocketAddress->GetPort()) > g_wMaxDPlayPort))
|
|
{
|
|
//
|
|
// If we just walked back into the DPlay range, skip over it.
|
|
//
|
|
if ((wBasePort >= g_wBaseDPlayPort) && (wBasePort <= g_wMaxDPlayPort))
|
|
{
|
|
wBasePort = g_wMaxDPlayPort + 1;
|
|
}
|
|
|
|
//
|
|
// If we have wrapped all the way back to 0 (!) then fail, to prevent
|
|
// infinite looping.
|
|
//
|
|
if (wBasePort == 0)
|
|
{
|
|
DPFX(DPFPREP, 0, "Managed to fail binding socket address 0x%p to every port, aborting!",
|
|
this );
|
|
hr = DPNERR_ALREADYINITIALIZED;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Force the "fixed port" code path in BindToNextAvailablePort, even
|
|
// though it isn't really fixed.
|
|
//
|
|
DPFX(DPFPREP, 5, "Forcing port %u.", wBasePort );
|
|
m_pNetworkSocketAddress->SetPort(p_htons(wBasePort));
|
|
}
|
|
|
|
|
|
//
|
|
// Return the previous address and try again.
|
|
//
|
|
m_pSPData->ReturnAddress( pBoundSocketAddress );
|
|
pBoundSocketAddress = NULL;
|
|
|
|
|
|
goto RebindToNextPort;
|
|
}
|
|
|
|
DPFX(DPFPREP, 0, "%s address already in use on Internet gateway (port = %u)!",
|
|
((dwTemp == 0) ? "Private" : "Public"),
|
|
p_ntohs(pBoundSocketAddress->GetPort()));
|
|
goto Failure;
|
|
break;
|
|
}
|
|
|
|
case DPNERR_UNSUPPORTED:
|
|
{
|
|
//
|
|
// 0 & 1 = NATHelp not loaded or isn't supported for SP
|
|
//
|
|
if (dwTemp == 0)
|
|
{
|
|
DPFX(DPFPREP, 2, "Not able to find existing private mapping for socketport 0x%p on local Internet gateway, unsupported/not necessary.",
|
|
this);
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 2, "Didn't bind socketport 0x%p to Internet gateway, unsupported/not necessary.",
|
|
this);
|
|
}
|
|
|
|
//
|
|
// Ignore the error.
|
|
//
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
//
|
|
// 0 & 1 = ?
|
|
//
|
|
if (dwTemp == 0)
|
|
{
|
|
DPFX(DPFPREP, 1, "Unable to look for existing private mapping for socketport 0x%p on local Internet gateway (error = 0x%lx), ignoring.",
|
|
this, hr);
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 1, "Unable to bind socketport 0x%p to Internet gateway (error = 0x%lx), ignoring.",
|
|
this, hr);
|
|
}
|
|
|
|
//
|
|
// Ignore the error, we can survive without the mapping.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go to the next function to be handled in this manner.
|
|
//
|
|
}
|
|
|
|
|
|
//
|
|
// Save the address we actually ended up with.
|
|
//
|
|
m_pSPData->ReturnAddress( m_pNetworkSocketAddress );
|
|
m_pNetworkSocketAddress = pBoundSocketAddress;
|
|
pBoundSocketAddress = NULL;
|
|
|
|
|
|
//
|
|
// Generate a unique socketport ID. Start with the current time and
|
|
// combine in the address.
|
|
//
|
|
m_dwSocketPortID = GETTIMESTAMP();
|
|
pdwAddressChunk = (DWORD*) m_pNetworkSocketAddress->GetAddress();
|
|
pdwLastAddressChunk = (DWORD*) (((BYTE*) pdwAddressChunk) + m_pNetworkSocketAddress->GetAddressSize() - sizeof(DWORD));
|
|
while (pdwAddressChunk <= pdwLastAddressChunk)
|
|
{
|
|
m_dwSocketPortID ^= (*pdwAddressChunk);
|
|
pdwAddressChunk++;
|
|
}
|
|
|
|
|
|
|
|
if (m_pSPData->GetType() == TYPE_IP)
|
|
{
|
|
//
|
|
// Detect whether this socket has WinSock Proxy Client a.k.a. ISA Firewall
|
|
// Client installed (unless the user turned auto-detection off in the
|
|
// registry). We do this by looking at the name of the protocol that got
|
|
// bound to the socket. If it contains "Proxy", consider it proxied.
|
|
//
|
|
// Ignore failure (WinSock 1 probably doesn't have this socket option), and
|
|
// assume the proxy client isn't installed.
|
|
//
|
|
if (! g_fDontAutoDetectProxyLSP)
|
|
{
|
|
|
|
#if 1
|
|
|
|
int aiProtocols[2];
|
|
WSAPROTOCOL_INFO * pwsapi;
|
|
DWORD dwBufferSize;
|
|
int i;
|
|
#ifdef DEBUG
|
|
#ifdef WINNT
|
|
WCHAR wszProtocol[WSAPROTOCOL_LEN+1];
|
|
#else // WIN95
|
|
char szProtocol[WSAPROTOCOL_LEN+1];
|
|
#endif // WIN95
|
|
#endif // DEBUG
|
|
|
|
|
|
#ifdef WIN95
|
|
if (GetWinsockVersion() == 2)
|
|
#endif // WIN95
|
|
{
|
|
aiProtocols[0] = IPPROTO_UDP;
|
|
aiProtocols[1] = 0;
|
|
|
|
dwBufferSize = 0;
|
|
|
|
//
|
|
// Ignore error, assume the buffer is too small.
|
|
//
|
|
#ifdef WINNT
|
|
p_WSAEnumProtocolsW(aiProtocols, NULL, &dwBufferSize);
|
|
#else // WIN95
|
|
p_WSAEnumProtocolsA(aiProtocols, NULL, &dwBufferSize);
|
|
#endif // WIN95
|
|
|
|
|
|
pwsapi = (WSAPROTOCOL_INFO*) DNMalloc(dwBufferSize);
|
|
if (pwsapi == NULL)
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
#ifdef WINNT
|
|
iReturnValue = p_WSAEnumProtocolsW(aiProtocols, pwsapi, &dwBufferSize);
|
|
#else // WIN95
|
|
iReturnValue = p_WSAEnumProtocolsA(aiProtocols, pwsapi, &dwBufferSize);
|
|
#endif // WIN95
|
|
if (iReturnValue == SOCKET_ERROR)
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 1, "Couldn't enumerate UDP protocols for socketport 0x%p ID 0x%x (err = %u)! Assuming not using proxy client.",
|
|
this, m_dwSocketPortID, dwErrorCode);
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Loop through all the UDP protocols installed.
|
|
//
|
|
for(i = 0; i < iReturnValue; i++)
|
|
{
|
|
//
|
|
// See if the name contains "Proxy", case insensitive.
|
|
// Save the original string in debug so we can print it.
|
|
//
|
|
#ifdef WINNT
|
|
#ifdef DEBUG
|
|
wcscpy(wszProtocol, pwsapi[i].szProtocol);
|
|
#endif // DEBUG
|
|
_wcslwr(pwsapi[i].szProtocol);
|
|
if (wcsstr(pwsapi[i].szProtocol, L"proxy") != NULL)
|
|
{
|
|
DPFX(DPFPREP, 5, "Socketport 0x%p (ID 0x%x) appears to be using proxy client (protocol %i = \"%S\").",
|
|
this, m_dwSocketPortID, i, wszProtocol);
|
|
this->m_fUsingProxyWinSockLSP = TRUE;
|
|
|
|
//
|
|
// Stop searching.
|
|
//
|
|
break;
|
|
}
|
|
|
|
|
|
DPFX(DPFPREP, 5, "Socketport 0x%p (ID 0x%x) protocol %i (\"%S\") does not contain \"proxy\".",
|
|
this, m_dwSocketPortID, i, wszProtocol);
|
|
#else // WIN95
|
|
#ifdef DEBUG
|
|
strcpy(szProtocol, pwsapi[i].szProtocol);
|
|
#endif // DEBUG
|
|
_strlwr(pwsapi[i].szProtocol);
|
|
if (strstr(pwsapi[i].szProtocol, "proxy") != NULL)
|
|
{
|
|
DPFX(DPFPREP, 5, "Socketport 0x%p (ID 0x%x) appears to be using proxy client (protocol %i = \"%s\").",
|
|
this, m_dwSocketPortID, i, szProtocol);
|
|
this->m_fUsingProxyWinSockLSP = TRUE;
|
|
|
|
//
|
|
// Stop searching.
|
|
//
|
|
break;
|
|
}
|
|
|
|
|
|
DPFX(DPFPREP, 5, "Socketport 0x%p (ID 0x%x) protocol %i (\"%s\") does not contain \"proxy\".",
|
|
this, m_dwSocketPortID, i, szProtocol);
|
|
#endif // WIN95
|
|
} // end for (each returned protocol)
|
|
}
|
|
|
|
DNFree(pwsapi);
|
|
}
|
|
#ifdef WIN95
|
|
else
|
|
{
|
|
//
|
|
// WinSock 1 doesn't have this entry point.
|
|
//
|
|
DPFX(DPFPREP, 1, "Unable to auto-detect proxy client on WinSock 1, assuming not present.");
|
|
}
|
|
#endif // WIN95
|
|
|
|
|
|
|
|
|
|
#else // 0
|
|
|
|
|
|
|
|
WSAPROTOCOL_INFO wsapi;
|
|
int iBufferSize;
|
|
#ifdef DEBUG
|
|
TCHAR tszProtocol[WSAPROTOCOL_LEN+1];
|
|
#endif // DEBUG
|
|
|
|
|
|
memset(&wsapi, 0, sizeof(wsapi));
|
|
iBufferSize = sizeof(wsapi);
|
|
|
|
iReturnValue = p_getsockopt( GetSocket(),
|
|
SOL_SOCKET,
|
|
SO_PROTOCOL_INFO,
|
|
(char*) &wsapi,
|
|
&iBufferSize );
|
|
if (iReturnValue == SOCKET_ERROR)
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 1, "Couldn't get bound protocol info for socketport 0x%p ID 0x%x (err = %u)! Assuming not using proxy client.",
|
|
this, m_dwSocketPortID, dwErrorCode);
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// See if the name contains "Proxy", case insensitive.
|
|
// Save the original string in debug so we can print it.
|
|
//
|
|
#ifdef DEBUG
|
|
_tcscpy(tszProtocol, wsapi.szProtocol);
|
|
#endif // DEBUG
|
|
_tcslwr(wsapi.szProtocol);
|
|
if (_tcsstr(wsapi.szProtocol, _T("proxy")) != NULL)
|
|
{
|
|
#ifdef UNICODE
|
|
DPFX(DPFPREP, 5, "Socketport 0x%p (ID 0x%x) appears to be using proxy client (bound protocol = \"%S\").",
|
|
#else // ! UNICODE
|
|
DPFX(DPFPREP, 5, "Socketport 0x%p (ID 0x%x) appears to be using proxy client (bound protocol = \"%s\").",
|
|
#endif // ! UNICODE
|
|
this, m_dwSocketPortID, tszProtocol);
|
|
this->m_fUsingProxyWinSockLSP = TRUE;
|
|
}
|
|
else
|
|
{
|
|
#ifdef UNICODE
|
|
DPFX(DPFPREP, 5, "Socketport 0x%p (ID 0x%x) does not appear to be using proxy client (bound protocol = \"%S\").",
|
|
#else // ! UNICODE
|
|
DPFX(DPFPREP, 5, "Socketport 0x%p (ID 0x%x) does not appear to be using proxy client (bound protocol = \"%s\").",
|
|
#endif // ! UNICODE
|
|
this, m_dwSocketPortID, tszProtocol);
|
|
}
|
|
}
|
|
|
|
|
|
#endif // 0
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 5, "Not auto-detecting whether socketport 0x%p (ID 0x%x) is using proxy client.",
|
|
this, m_dwSocketPortID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// IPX, don't worry about proxies.
|
|
//
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// start processing input messages
|
|
// It's possible that messages will arrive before an endpoint is officially
|
|
// bound to this socket port, but that's not a problem, the contents will
|
|
// be lost
|
|
//
|
|
hr = StartReceiving( hIOCompletionPort );
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem starting endpoint receiving!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
|
|
Exit:
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem in CSocketPort::BindToNetwork()" );
|
|
DisplayDNError( 0, hr );
|
|
}
|
|
|
|
if ( pBoundSocketAddress != NULL )
|
|
{
|
|
m_pSPData->ReturnAddress( pBoundSocketAddress );
|
|
pBoundSocketAddress = NULL;
|
|
}
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
DEBUG_ONLY( m_fInitialized = FALSE );
|
|
if ( fBoundToNetwork != FALSE )
|
|
{
|
|
UnbindFromNetwork();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we were bound to network, m_Socket will be reset to
|
|
// INVALID_SOCKET.
|
|
// Otherwise, we will take care of this ourselves (!)
|
|
//
|
|
iReturnValue = p_closesocket( m_Socket );
|
|
if ( iReturnValue == SOCKET_ERROR )
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing socket!" );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
}
|
|
m_Socket = INVALID_SOCKET;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::UnbindFromNetwork - unbind this socket port from the network
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Error code
|
|
//
|
|
// Note: It is assumed that this socket port's information is locked!
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::UnbindFromNetwork"
|
|
|
|
HRESULT CSocketPort::UnbindFromNetwork( void )
|
|
{
|
|
INT iWSAReturn;
|
|
SOCKET TempSocket;
|
|
DWORD dwTemp;
|
|
DWORD dwErrorCode;
|
|
|
|
|
|
DPFX(DPFPREP, 7, "(0x%p) Enter", this );
|
|
|
|
|
|
TempSocket = GetSocket();
|
|
m_Socket = INVALID_SOCKET;
|
|
DNASSERT( TempSocket != INVALID_SOCKET );
|
|
|
|
iWSAReturn = p_shutdown( TempSocket, SD_BOTH );
|
|
if ( iWSAReturn == SOCKET_ERROR )
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Problem shutting down socket!" );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
}
|
|
|
|
DPFX(DPFPREP, 5, "Closing socketport 0x%p socket 0x%p.", this, TempSocket);
|
|
|
|
iWSAReturn = p_closesocket( TempSocket );
|
|
if ( iWSAReturn == SOCKET_ERROR )
|
|
{
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing socket!" );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
}
|
|
|
|
//
|
|
// Unbind with all DirectPlayNATHelp instances.
|
|
//
|
|
for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
|
|
{
|
|
if ( m_ahNATHelpPorts[dwTemp] != NULL )
|
|
{
|
|
DNASSERT( m_pThreadPool != NULL );
|
|
DNASSERT( m_pThreadPool->IsNATHelpLoaded() );
|
|
|
|
//
|
|
// Ignore error.
|
|
//
|
|
IDirectPlayNATHelp_DeregisterPorts( g_papNATHelpObjects[dwTemp], m_ahNATHelpPorts[dwTemp], 0 );
|
|
m_ahNATHelpPorts[dwTemp] = NULL;
|
|
}
|
|
}
|
|
|
|
DPFX(DPFPREP, 7, "(0x%p) Returning [DPN_OK]", this );
|
|
|
|
return DPN_OK;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::BindToNextAvailablePort - bind to next available port
|
|
//
|
|
// Entry: Pointer adapter address to bind to
|
|
// Base port to try assigning.
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::BindToNextAvailablePort"
|
|
|
|
HRESULT CSocketPort::BindToNextAvailablePort( const CSocketAddress *const pNetworkAddress,
|
|
const WORD wBasePort) const
|
|
{
|
|
HRESULT hr;
|
|
INT iSocketReturn;
|
|
CSocketAddress * pDuplicateNetworkAddress;
|
|
|
|
|
|
DNASSERT( pNetworkAddress != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
pDuplicateNetworkAddress = NULL;
|
|
|
|
//
|
|
// If a port was specified, try to bind to that port. If no port was
|
|
// specified, start walking the reserved DPlay port range looking for an
|
|
// available port. If none is found, let Winsock choose the port.
|
|
//
|
|
if ( pNetworkAddress->GetPort() != ANY_PORT )
|
|
{
|
|
iSocketReturn = p_bind( GetSocket(),
|
|
pNetworkAddress->GetAddress(),
|
|
pNetworkAddress->GetAddressSize()
|
|
);
|
|
if ( iSocketReturn == SOCKET_ERROR )
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
|
|
hr = DPNERR_ALREADYINITIALIZED;
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to bind socket to fixed port!" );
|
|
DumpSocketAddress(0, pNetworkAddress->GetAddress(), pNetworkAddress->GetFamily() );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
goto Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WORD wPort;
|
|
BOOL fBound;
|
|
|
|
|
|
fBound = FALSE;
|
|
DNASSERT( pDuplicateNetworkAddress == NULL );
|
|
pDuplicateNetworkAddress = m_pSPData->GetNewAddress();
|
|
if ( pDuplicateNetworkAddress == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Failed to get address for walking DPlay port range!" );
|
|
goto Failure;
|
|
}
|
|
|
|
pDuplicateNetworkAddress->CopyAddressSettings( pNetworkAddress );
|
|
|
|
wPort = wBasePort;
|
|
|
|
//
|
|
// Try picking the next port in the DPlay range.
|
|
//
|
|
while ( ( wPort >= g_wBaseDPlayPort ) && ( wPort <= g_wMaxDPlayPort ) && ( fBound == FALSE ) )
|
|
{
|
|
pDuplicateNetworkAddress->SetPort( p_htons( wPort ) );
|
|
iSocketReturn = p_bind( GetSocket(),
|
|
pDuplicateNetworkAddress->GetAddress(),
|
|
pDuplicateNetworkAddress->GetAddressSize()
|
|
);
|
|
if ( iSocketReturn == SOCKET_ERROR )
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
|
|
dwErrorCode = p_WSAGetLastError();
|
|
switch ( dwErrorCode )
|
|
{
|
|
case WSAEADDRINUSE:
|
|
{
|
|
DPFX(DPFPREP, 8, "Port %u in use, skipping to next port.", wPort );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
hr = DPNERR_NOCONNECTION;
|
|
DPFX(DPFPREP, 0, "Failed to bind socket to port in DPlay range!" );
|
|
DumpSocketAddress(0, pDuplicateNetworkAddress->GetAddress(), pDuplicateNetworkAddress->GetFamily() );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
goto Failure;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNASSERT( hr == DPN_OK );
|
|
fBound = TRUE;
|
|
}
|
|
|
|
wPort++;
|
|
}
|
|
|
|
//
|
|
// For some reason, all of the default DPlay ports were in use, let
|
|
// Winsock choose. We can use the network address passed because it
|
|
// has 'ANY_PORT'.
|
|
//
|
|
if ( fBound == FALSE )
|
|
{
|
|
DNASSERT( pNetworkAddress->GetPort() == ANY_PORT );
|
|
iSocketReturn = p_bind( GetSocket(),
|
|
pNetworkAddress->GetAddress(),
|
|
pNetworkAddress->GetAddressSize()
|
|
);
|
|
if ( iSocketReturn == SOCKET_ERROR )
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
|
|
hr = DPNERR_NOCONNECTION;
|
|
dwErrorCode = p_WSAGetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to bind socket (any port)!" );
|
|
DumpSocketAddress(0, pNetworkAddress->GetAddress(), pNetworkAddress->GetFamily() );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
goto Failure;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
if ( pDuplicateNetworkAddress != NULL )
|
|
{
|
|
m_pSPData->ReturnAddress( pDuplicateNetworkAddress );
|
|
pDuplicateNetworkAddress = NULL;
|
|
}
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::CheckForOverridingMapping - looks for an existing mapping if there's a local NAT
|
|
//
|
|
// Entry: Pointer to SocketAddress to query
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::CheckForOverridingMapping"
|
|
|
|
HRESULT CSocketPort::CheckForOverridingMapping( const CSocketAddress *const pBoundSocketAddress )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwTemp;
|
|
SOCKADDR saddrSource;
|
|
SOCKADDR saddrPublic;
|
|
|
|
|
|
DNASSERT( pBoundSocketAddress != NULL );
|
|
DNASSERT( GetAdapterEntry() != NULL );
|
|
DNASSERT( m_pThreadPool != NULL );
|
|
|
|
|
|
if ((pBoundSocketAddress->GetFamily() != AF_INET) ||
|
|
( ! m_pThreadPool->IsNATHelpLoaded() ))
|
|
{
|
|
//
|
|
// We skipped initializing NAT Help, it failed starting up, or this is just
|
|
// not an IP socket.
|
|
//
|
|
hr = DPNERR_UNSUPPORTED;
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Query using INADDR_ANY. This will ensure that the best device is picked
|
|
// (i.e. the private interface on a NAT, whose public mappings matter when
|
|
// we're looking for overriding mappings on the public adapter).
|
|
// Alternatively, we could query on every device, but this should do the trick.
|
|
//
|
|
ZeroMemory(&saddrSource, sizeof(saddrSource));
|
|
saddrSource.sa_family = AF_INET;
|
|
//saddrinSource.sin_addr.S_un.S_addr = INADDR_ANY;
|
|
//saddrinSource.sin_port = 0;
|
|
|
|
|
|
//
|
|
// Register the ports with all DirectPlayNATHelp instances. We might break
|
|
// out of the loop if we detect a gateway mapping.
|
|
//
|
|
for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
|
|
{
|
|
DNASSERT(m_ahNATHelpPorts[dwTemp] == NULL);
|
|
|
|
if ( g_papNATHelpObjects[dwTemp] != NULL )
|
|
{
|
|
hr = IDirectPlayNATHelp_QueryAddress( g_papNATHelpObjects[dwTemp],
|
|
&saddrSource,
|
|
pBoundSocketAddress->GetAddress(),
|
|
&saddrPublic,
|
|
sizeof (saddrPublic),
|
|
0 );
|
|
switch ( hr )
|
|
{
|
|
case DPNH_OK:
|
|
{
|
|
//
|
|
// Uh oh, this address is in use.
|
|
//
|
|
DPFX(DPFPREP, 0, "Private address already in use according to NAT Help object %u!", dwTemp );
|
|
DumpSocketAddress( 0, pBoundSocketAddress->GetAddress(), pBoundSocketAddress->GetFamily() );
|
|
DumpSocketAddress( 0, &saddrPublic, pBoundSocketAddress->GetFamily() );
|
|
hr = DPNERR_ALREADYINITIALIZED;
|
|
goto Exit;
|
|
break;
|
|
}
|
|
|
|
case DPNHERR_NOMAPPING:
|
|
{
|
|
//
|
|
// It's not in use.
|
|
//
|
|
DPFX(DPFPREP, 8, "Private address not in use according to NAT Help object %u.", dwTemp );
|
|
break;
|
|
}
|
|
|
|
case DPNHERR_SERVERNOTAVAILABLE:
|
|
{
|
|
//
|
|
// There's no server.
|
|
//
|
|
DPFX(DPFPREP, 8, "Private address not in use because NAT Help object %u didn't detect any servers.",
|
|
dwTemp );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
//
|
|
// Something else. Assume it's not in use.
|
|
//
|
|
DPFX(DPFPREP, 1, "NAT Help object %u failed private address lookup (err = 0x%lx), assuming not in use.",
|
|
dwTemp, hr );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No NAT Help object.
|
|
//
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we're here, no Internet gateways reported the port as in use.
|
|
//
|
|
DPFX(DPFPREP, 2, "No NAT Help object reported private address as in use." );
|
|
hr = DPN_OK;
|
|
|
|
|
|
Exit:
|
|
|
|
return hr;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::BindToInternetGateway - binds a socket to a NAT, if available
|
|
//
|
|
// Entry: Pointer to SocketAddress we bound to
|
|
// Gateway bind type
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::BindToInternetGateway"
|
|
|
|
HRESULT CSocketPort::BindToInternetGateway( const CSocketAddress *const pBoundSocketAddress,
|
|
const GATEWAY_BIND_TYPE GatewayBindType )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwTemp;
|
|
DWORD dwRegisterFlags;
|
|
DWORD dwAddressTypeFlags;
|
|
BOOL fUnavailable;
|
|
#ifdef DEBUG
|
|
BOOL fFirewallMapping = FALSE;
|
|
#endif // DEBUG
|
|
|
|
|
|
DNASSERT( pBoundSocketAddress != NULL );
|
|
DNASSERT( GetAdapterEntry() != NULL );
|
|
DNASSERT( m_pThreadPool != NULL );
|
|
|
|
|
|
if ((pBoundSocketAddress->GetFamily() != AF_INET) ||
|
|
( ! m_pThreadPool->IsNATHelpLoaded() ))
|
|
{
|
|
//
|
|
// We skipped initializing NAT Help, it failed starting up, or this is just
|
|
// not an IP socket.
|
|
//
|
|
hr = DPNERR_UNSUPPORTED;
|
|
goto Exit;
|
|
}
|
|
|
|
switch ( GatewayBindType )
|
|
{
|
|
//
|
|
// just ask the server to open a generic port for us (connect, listen, enum)
|
|
//
|
|
case GATEWAY_BIND_TYPE_DEFAULT:
|
|
{
|
|
dwRegisterFlags = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// ask the NAT to open a fixed port for us (address is specified)
|
|
//
|
|
case GATEWAY_BIND_TYPE_SPECIFIC:
|
|
{
|
|
dwRegisterFlags = DPNHREGISTERPORTS_FIXEDPORTS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// ask the NAT to share the listen for us (this should be DPNSVR only)
|
|
//
|
|
case GATEWAY_BIND_TYPE_SPECIFIC_SHARED:
|
|
{
|
|
dwRegisterFlags = DPNHREGISTERPORTS_FIXEDPORTS | DPNHREGISTERPORTS_SHAREDPORTS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// no binding
|
|
//
|
|
case GATEWAY_BIND_TYPE_NONE:
|
|
{
|
|
DPFX(DPFPREP, 8, "Not binding socket address 0x%p to NAT because bind type is NONE.",
|
|
pBoundSocketAddress);
|
|
|
|
DNASSERT( ! "Gateway bind type is NONE?" );
|
|
hr = DPNERR_GENERIC;
|
|
goto Failure;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// unknown condition, someone broke the code!
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
hr = DPNERR_GENERIC;
|
|
goto Failure;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Detect whether any servers said the port was unavailable.
|
|
//
|
|
fUnavailable = FALSE;
|
|
|
|
|
|
//
|
|
// Register the ports with all DirectPlayNATHelp instances. We might break
|
|
// out of the loop if we detect a gateway mapping.
|
|
//
|
|
for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
|
|
{
|
|
DNASSERT(m_ahNATHelpPorts[dwTemp] == NULL);
|
|
|
|
if ( g_papNATHelpObjects[dwTemp] != NULL )
|
|
{
|
|
hr = IDirectPlayNATHelp_RegisterPorts( g_papNATHelpObjects[dwTemp],
|
|
pBoundSocketAddress->GetAddress(),
|
|
sizeof (SOCKADDR),
|
|
1,
|
|
NAT_LEASE_TIME,
|
|
&m_ahNATHelpPorts[dwTemp],
|
|
dwRegisterFlags );
|
|
if ( hr != DPNH_OK )
|
|
{
|
|
DNASSERT(m_ahNATHelpPorts[dwTemp] == NULL);
|
|
DPFX(DPFPREP, 0, "Failed to register port with NAT Help object %u! Ignoring.", dwTemp );
|
|
DumpSocketAddress( 0, pBoundSocketAddress->GetAddress(), pBoundSocketAddress->GetFamily() );
|
|
DisplayDNError( 0, hr );
|
|
hr = DPN_OK;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There might be an Internet gateway device already present. If so,
|
|
// then DPNATHelp already tried to register the port mapping with it, which
|
|
// might have failed because the port is already in use. If we're not
|
|
// binding to a fixed port, then we could just pick a different port and try
|
|
// again. So check if there's a UPnP device but DPNATHelp couldn't map
|
|
// the port and return that error to the caller so he can make the
|
|
// decision to retry or not.
|
|
//
|
|
// IDirectPlayNATHelp::GetCaps had better have been called with the
|
|
// DPNHGETCAPS_UPDATESERVERSTATUS flag at least once prior to this.
|
|
// See CThreadPool::PreventThreadPoolReduction
|
|
//
|
|
hr = IDirectPlayNATHelp_GetRegisteredAddresses( g_papNATHelpObjects[dwTemp], // object
|
|
m_ahNATHelpPorts[dwTemp], // port binding
|
|
NULL, // don't need address
|
|
NULL, // don't need address buffer size
|
|
&dwAddressTypeFlags, // get address type flags
|
|
NULL, // don't need lease time remaining
|
|
0 ); // no flags
|
|
switch (hr)
|
|
{
|
|
case DPNH_OK:
|
|
{
|
|
//
|
|
// If this is a mapping on a gateway, then we're done.
|
|
// We don't need to try to make any more NAT mappings.
|
|
//
|
|
if (dwAddressTypeFlags & DPNHADDRESSTYPE_GATEWAY)
|
|
{
|
|
DPFX(DPFPREP, 4, "Address has already successfully been registered with gateway using object index %u (type flags = 0x%lx), not trying additional mappings.",
|
|
dwTemp, dwAddressTypeFlags);
|
|
goto Exit;
|
|
}
|
|
|
|
DNASSERT(dwAddressTypeFlags & DPNHADDRESSTYPE_LOCALFIREWALL);
|
|
|
|
DPFX(DPFPREP, 4, "Address has already successfully been registered with firewall using object index %u (type flags = 0x%lx), looking for gateways.",
|
|
dwTemp, dwAddressTypeFlags);
|
|
|
|
#ifdef DEBUG
|
|
fFirewallMapping = TRUE;
|
|
#endif // DEBUG
|
|
|
|
break;
|
|
}
|
|
|
|
case DPNHERR_NOMAPPING:
|
|
{
|
|
DPFX(DPFPREP, 4, "Address already registered with Internet gateway index %u, but it does not have a public address (type flags = 0x%lx).",
|
|
dwTemp, dwAddressTypeFlags);
|
|
|
|
|
|
//
|
|
// It doesn't make any sense for a firewall not to have a
|
|
// mapping.
|
|
//
|
|
DNASSERT(dwAddressTypeFlags & DPNHADDRESSTYPE_GATEWAY);
|
|
DNASSERT(! (dwAddressTypeFlags & DPNHADDRESSTYPE_LOCALFIREWALL));
|
|
|
|
|
|
//
|
|
// Since it is a gateway (that might have a public address
|
|
// at some point, we don't need to try to make any more
|
|
// NAT mappings.
|
|
//
|
|
goto Exit;
|
|
|
|
break;
|
|
}
|
|
|
|
case DPNHERR_PORTUNAVAILABLE:
|
|
{
|
|
DPFX(DPFPREP, 1, "Port is unavailable on Internet gateway device index %u (type flags = 0x%lx).",
|
|
dwTemp, dwAddressTypeFlags);
|
|
|
|
fUnavailable = TRUE;
|
|
break;
|
|
}
|
|
|
|
case DPNHERR_SERVERNOTAVAILABLE:
|
|
{
|
|
DPFX(DPFPREP, 6, "No Internet gateway detected by object index %u at this time.", dwTemp);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 1, "An error (0x%lx) occurred while getting registered address mapping (index %u)! Ignoring.",
|
|
hr, dwTemp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No NAT Help object.
|
|
//
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we're here, no Internet gateways were detected, or if one was, the
|
|
// mapping was already in use there. If it's the latter, fail so our caller
|
|
// can unbind locally and possibly try again. Note that we are ignoring
|
|
// firewall mappings, since it's assumed we can make those with pretty
|
|
// much any port, so there's no point in hanging on to those mappings
|
|
// if the NAT port is in use.
|
|
//
|
|
if (fUnavailable)
|
|
{
|
|
DPFX(DPFPREP, 2, "At least one Internet gateway reported port as unavailable, failing.");
|
|
hr = DPNERR_ALREADYINITIALIZED;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
if (fFirewallMapping)
|
|
{
|
|
DPFX(DPFPREP, 2, "No gateway mappings but there is at least one firewall mapping.");
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 2, "No gateway or firewall mappings detected.");
|
|
}
|
|
#endif // DEBUG
|
|
hr = DPN_OK;
|
|
}
|
|
|
|
|
|
Exit:
|
|
return hr;
|
|
|
|
Failure:
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::StartReceiving - start receiving data on this socket port
|
|
//
|
|
// Entry: Handle of I/O completion port to bind to (used on NT only)
|
|
//
|
|
// Exit: Error code
|
|
//
|
|
// Notes: There is no 'Failure' label in this function because failures need
|
|
// to be cleaned up for each OS variant.
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::StartReceiving"
|
|
|
|
HRESULT CSocketPort::StartReceiving( const HANDLE hIOCompletionPort )
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
#ifdef WINNT
|
|
//
|
|
// we're on NT, bind to the completion port, issue a read and we're done
|
|
//
|
|
HRESULT hTempResult;
|
|
HANDLE hCreateReturn;
|
|
LONG lIndex;
|
|
DWORD_PTR dwTotalReceives;
|
|
|
|
|
|
//
|
|
// bind to completion port
|
|
//
|
|
DNASSERT( GetSocket() != INVALID_SOCKET );
|
|
DNASSERT( hIOCompletionPort != NULL );
|
|
DBG_CASSERT( sizeof( m_Socket ) == sizeof( HANDLE ) );
|
|
hCreateReturn = CreateIoCompletionPort( reinterpret_cast<HANDLE>( GetSocket() ), // current file handle (socket)
|
|
hIOCompletionPort, // handle of completion port
|
|
IO_COMPLETION_KEY_IO_COMPLETE, // completion key
|
|
0 // number of concurrent threads (default to number of processors)
|
|
);
|
|
if ( hCreateReturn == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Cannot bind SocketPort to completion port!" );
|
|
DisplayErrorCode( 0, GetLastError() );
|
|
goto Failure;
|
|
}
|
|
DNASSERT( hCreateReturn == hIOCompletionPort );
|
|
|
|
//
|
|
// We're set, read requests equal to the number of I/O completion
|
|
// threads. If the reads fail, nothing will be received.
|
|
//
|
|
hTempResult = m_pSPData->GetThreadPool()->GetIOThreadCount( &lIndex );
|
|
DNASSERT( hTempResult == DPN_OK );
|
|
|
|
dwTotalReceives = lIndex * g_dwWinsockReceiveBufferMultiplier;
|
|
DNASSERT( dwTotalReceives != 0 );
|
|
|
|
while ( dwTotalReceives != 0 )
|
|
{
|
|
dwTotalReceives--;
|
|
|
|
hr = Winsock2Receive();
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem issuing initial IOCompletion read in StartReceiving!" );
|
|
DisplayDNError( 0, hr );
|
|
DNASSERT( FALSE );
|
|
}
|
|
}
|
|
|
|
#else // WIN95
|
|
//
|
|
// Win9x.
|
|
// If this is not an IPX socket and Winsock 2 (or greater) is available,
|
|
// call the Winsock 2 read function. If this is IPX or we're stuck with
|
|
// Winsock 1, inform the thread pool as such.
|
|
//
|
|
DNASSERT( hIOCompletionPort == NULL );
|
|
|
|
if ( ( LOWORD( GetWinsockVersion() ) >= 2 ) &&
|
|
( m_pSPData->GetType() == TYPE_IP ) )
|
|
{
|
|
DWORD_PTR dwTotalReceives;
|
|
|
|
|
|
|
|
//
|
|
// we're using Winsock2, call for two outstanding reads per socket.
|
|
//
|
|
dwTotalReceives = m_pSPData->GetThreadPool()->ThreadCount() * g_dwWinsockReceiveBufferMultiplier;
|
|
|
|
DNASSERT( dwTotalReceives != 0 );
|
|
while ( dwTotalReceives != 0 )
|
|
{
|
|
dwTotalReceives--;
|
|
|
|
hr = Winsock2Receive();
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem issuing Win9x read in StartReceiving!" );
|
|
DisplayDNError( 0, hr );
|
|
DNASSERT( FALSE );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNASSERT( m_pSPData != NULL );
|
|
hr = m_pSPData->GetThreadPool()->AddSocketPort( this );
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to add to active socket list!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Exit:
|
|
return hr;
|
|
|
|
Failure:
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::SendFromWriteQueue - send as many items as possible from the
|
|
// write queue
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Boolean indicating that something was sent
|
|
// TRUE = data was sent
|
|
// FALSE = no data was sent
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::SendFromWriteQueue"
|
|
|
|
BOOL CSocketPort::SendFromWriteQueue( void )
|
|
{
|
|
BOOL fDataSent;
|
|
CWriteIOData *pWriteData;
|
|
SEND_COMPLETION_CODE TempCompletionCode;
|
|
|
|
|
|
DPFX(DPFPREP, 8,"SendFromWriteQueue");
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
fDataSent = FALSE;
|
|
TempCompletionCode = SEND_IN_PROGRESS;
|
|
|
|
m_SendQueue.Lock();
|
|
pWriteData = m_SendQueue.Dequeue();
|
|
DPFX(DPFPREP, 8,"WriteData 0x%p",pWriteData);
|
|
m_SendQueue.Unlock();
|
|
|
|
while ( ( pWriteData != NULL ) && ( TempCompletionCode == SEND_IN_PROGRESS ) )
|
|
{
|
|
pWriteData->m_pCommand->Lock();
|
|
switch ( pWriteData->m_pCommand->GetState() )
|
|
{
|
|
//
|
|
// command is still pending, attempt to send the data
|
|
//
|
|
case COMMAND_STATE_PENDING:
|
|
{
|
|
DPFX(DPFPREP, 8,"COMMAND_STATE_PENDING");
|
|
TempCompletionCode = (this->*m_pSendFunction)( pWriteData );
|
|
DPFX(DPFPREP, 8,"TempCompletionCode %x",TempCompletionCode);
|
|
switch ( TempCompletionCode )
|
|
{
|
|
//
|
|
// We managed to get this send going. It's already been removed
|
|
// from the queue. Get the next item from the queue and restart
|
|
// the loop.
|
|
//
|
|
case SEND_IN_PROGRESS:
|
|
{
|
|
DPFX(DPFPREP, 8,"Send In Progress, going ok, so getting another");
|
|
pWriteData->m_pCommand->SetState( COMMAND_STATE_INPROGRESS_CANNOT_CANCEL );
|
|
pWriteData->m_pCommand->Unlock();
|
|
|
|
m_SendQueue.Lock();
|
|
pWriteData = m_SendQueue.Dequeue();
|
|
DPFX(DPFPREP, 8,"(SIP) WriteData %p",pWriteData);
|
|
m_SendQueue.Unlock();
|
|
|
|
fDataSent = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Winsock is still busy, put this item back at the front of
|
|
// the queue. Clear 'pWriteData' to stop the loop.
|
|
//
|
|
case SEND_WINSOCK_BUSY:
|
|
{
|
|
DPFX(DPFPREP, 8,"Winsock Busy, requeuing for later");
|
|
DNASSERT( pWriteData->m_pCommand->GetState() == COMMAND_STATE_PENDING );
|
|
m_SendQueue.Lock();
|
|
m_SendQueue.AddToFront( pWriteData );
|
|
m_SendQueue.Unlock();
|
|
pWriteData->m_pCommand->Unlock();
|
|
pWriteData = NULL;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Winsock1 send completed immediately, try sending something
|
|
// else
|
|
//
|
|
case SEND_COMPLETED_IMMEDIATELY_WS1:
|
|
{
|
|
DPFX(DPFPREP, 8,"Completed immediately (Winsock1), getting another");
|
|
pWriteData->m_pCommand->SetState( COMMAND_STATE_INPROGRESS_CANNOT_CANCEL );
|
|
pWriteData->m_pCommand->Unlock();
|
|
|
|
SendComplete( pWriteData, DPN_OK );
|
|
|
|
m_SendQueue.Lock();
|
|
pWriteData = m_SendQueue.Dequeue();
|
|
m_SendQueue.Unlock();
|
|
DPFX(DPFPREP, 8,"(SIW) WriteData %p",pWriteData);
|
|
fDataSent = TRUE;
|
|
TempCompletionCode = SEND_IN_PROGRESS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// send failed, try sending the next item
|
|
//
|
|
case SEND_FAILED:
|
|
{
|
|
DPFX(DPFPREP, 8,"Send Failed");
|
|
pWriteData->m_pCommand->SetState( COMMAND_STATE_INPROGRESS_CANNOT_CANCEL );
|
|
pWriteData->m_pCommand->Unlock();
|
|
|
|
SendComplete( pWriteData, DPNERR_GENERIC );
|
|
|
|
m_SendQueue.Lock();
|
|
pWriteData = m_SendQueue.Dequeue();
|
|
m_SendQueue.Unlock();
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// invalid return
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This command is to be cancelled, remove it from the queue
|
|
// and issue completion notification to caller. Then we can
|
|
// look at the next item.
|
|
//
|
|
case COMMAND_STATE_CANCELLING:
|
|
{
|
|
pWriteData->m_pCommand->SetState( COMMAND_STATE_INPROGRESS_CANNOT_CANCEL );
|
|
pWriteData->m_pCommand->Unlock();
|
|
|
|
SendComplete( pWriteData, DPNERR_USERCANCEL );
|
|
|
|
m_SendQueue.Lock();
|
|
pWriteData = m_SendQueue.Dequeue();
|
|
m_SendQueue.Unlock();
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// invalid command state
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fDataSent;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::GetBoundNetworkAddress - get the full network address that
|
|
// this socket port was really bound to
|
|
//
|
|
// Entry: Address type for bound address
|
|
//
|
|
// Exit: Pointer to network address
|
|
//
|
|
// Note: Since this function creates a local address to derive the network
|
|
// address from, it needs to know what kind of address to derive. This
|
|
// address type is supplied as the function parameter.
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::GetBoundNetworkAddress"
|
|
|
|
CSocketAddress *CSocketPort::GetBoundNetworkAddress( const SP_ADDRESS_TYPE AddressType ) const
|
|
{
|
|
HRESULT hr;
|
|
CSocketAddress *pTempSocketAddress;
|
|
SOCKADDR BoundSocketAddress;
|
|
INT_PTR iReturnValue;
|
|
INT iBoundSocketAddressSize;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
pTempSocketAddress = NULL;
|
|
|
|
//
|
|
// create addresses
|
|
//
|
|
pTempSocketAddress = m_pSPData->GetNewAddress();
|
|
if ( pTempSocketAddress == NULL )
|
|
{
|
|
DPFX(DPFPREP, 0, "GetBoundNetworkAddress: Failed to create socket address!" );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// find out what address we really bound to and reset the information for
|
|
// this socket port
|
|
//
|
|
iBoundSocketAddressSize = pTempSocketAddress->GetAddressSize();
|
|
iReturnValue = p_getsockname( GetSocket(), &BoundSocketAddress, &iBoundSocketAddressSize );
|
|
if ( iReturnValue == SOCKET_ERROR )
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
|
|
dwErrorCode = p_WSAGetLastError();
|
|
hr = DPNERR_GENERIC;
|
|
DPFX(DPFPREP, 0, "GetBoundNetworkAddress: Failed to get local socket name after bind!" );
|
|
DisplayWinsockError( 0, dwErrorCode );
|
|
goto Failure;
|
|
}
|
|
pTempSocketAddress->SetAddressFromSOCKADDR( BoundSocketAddress, iBoundSocketAddressSize );
|
|
DNASSERT( iBoundSocketAddressSize == pTempSocketAddress->GetAddressSize() );
|
|
|
|
//
|
|
// Since this address was created locally, we need to tell it what type of
|
|
// address to export according to the input.
|
|
//
|
|
pTempSocketAddress->SetAddressType( AddressType );
|
|
|
|
switch ( AddressType )
|
|
{
|
|
//
|
|
// known types
|
|
//
|
|
case SP_ADDRESS_TYPE_DEVICE_USE_ANY_PORT:
|
|
case SP_ADDRESS_TYPE_DEVICE_PROXIED_ENUM_TARGET:
|
|
case SP_ADDRESS_TYPE_HOST:
|
|
case SP_ADDRESS_TYPE_READ_HOST:
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if we're looking for a public address, we need to make sure that this
|
|
// is not an undefined address. If it is, don't return an address.
|
|
// Otherwise, remap the address type to a 'host' address.
|
|
//
|
|
case SP_ADDRESS_TYPE_PUBLIC_HOST_ADDRESS:
|
|
{
|
|
if ( pTempSocketAddress->IsUndefinedHostAddress() != FALSE )
|
|
{
|
|
m_pSPData->ReturnAddress( pTempSocketAddress );
|
|
pTempSocketAddress = NULL;
|
|
}
|
|
else
|
|
{
|
|
pTempSocketAddress->SetAddressType( SP_ADDRESS_TYPE_HOST );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// unknown address type, fix the code!
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
Exit:
|
|
return pTempSocketAddress;
|
|
|
|
Failure:
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::GetDP8BoundNetworkAddress - get the network address this machine
|
|
// is bound to according to the input parameter. If the requested address
|
|
// for the public address and an Internet gateway are available, use the
|
|
// public address. If a public address is requested but is unavailable,
|
|
// fall back to the bound network address for local host-style device
|
|
// addresses. If a public address is unavailable but we're explicitly
|
|
// looking for a public address, return NULL.
|
|
//
|
|
// Entry: Type of address to get (local adapter vs. host)
|
|
//
|
|
// Exit: Pointer to network address
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::GetDP8BoundNetworkAddress"
|
|
|
|
IDirectPlay8Address *CSocketPort::GetDP8BoundNetworkAddress( const SP_ADDRESS_TYPE AddressType,
|
|
const GATEWAY_BIND_TYPE GatewayBindType) const
|
|
{
|
|
HRESULT hr;
|
|
IDirectPlay8Address * pAddress;
|
|
CSocketAddress * pTempAddress = NULL;
|
|
DWORD dwTemp;
|
|
SOCKADDR saddr;
|
|
DWORD dwAddressSize;
|
|
#ifdef DEBUG
|
|
SOCKADDR_IN * psaddrin;
|
|
DWORD dwAddressTypeFlags;
|
|
#endif // DEBUG
|
|
|
|
|
|
DPFX(DPFPREP, 8, "(0x%p) Parameters: (0x%i)", this, AddressType );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
pAddress = NULL;
|
|
|
|
|
|
DNASSERT( m_pThreadPool != NULL );
|
|
DNASSERT( m_pNetworkSocketAddress != NULL );
|
|
|
|
#ifdef DEBUG
|
|
switch ( m_pNetworkSocketAddress->GetFamily() )
|
|
{
|
|
case AF_INET:
|
|
{
|
|
psaddrin = (SOCKADDR_IN *) m_pNetworkSocketAddress->GetAddress();
|
|
DNASSERT( psaddrin->sin_addr.S_un.S_addr != 0 );
|
|
DNASSERT( psaddrin->sin_addr.S_un.S_addr != INADDR_BROADCAST );
|
|
DNASSERT( psaddrin->sin_port != 0 );
|
|
break;
|
|
}
|
|
|
|
case AF_IPX:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
#endif // DEBUG
|
|
|
|
switch ( AddressType )
|
|
{
|
|
case SP_ADDRESS_TYPE_DEVICE_USE_ANY_PORT:
|
|
{
|
|
|
|
pAddress = m_pNetworkSocketAddress->DP8AddressFromSocketAddress();
|
|
|
|
|
|
//
|
|
// We hand up the exact device address we ended up using for this adapter.
|
|
// In multi-adapter systems, our user is probably going to switch in a different
|
|
// device GUID and pass it back down for another connect attempt (because
|
|
// we told them we support ALL_ADAPTERS). This can pose a problem since
|
|
// we include the specific port in this address. If the port was available on this
|
|
// adapter but not on others. The other attempts will fail. This can also cause
|
|
// problems when indicating the device with enum responses. If the application
|
|
// allowed us to select a local port, enumerated and got a response, shutdown
|
|
// the interface (or just the enum), then connected with the device address, we
|
|
// would try to use that port again, even though it may now be in use by
|
|
// another local application (or more likely, on the NAT).
|
|
//
|
|
// We are not required to use the same port on all adapters if the caller did
|
|
// not choose a specific port in the first place, so there's no reason why we
|
|
// couldn't try a different one.
|
|
//
|
|
// We know whether the port was specified or not, because GatewayBindType
|
|
// will be GATEWAY_BIND_TYPE_DEFAULT if the port can float, _SPECIFIC or
|
|
// _SPECIFIC_SHARED if not.
|
|
//
|
|
// So we can add a special key to the device address indicating that while it
|
|
// does contain a port, don't take that too seriously. That way, if this device
|
|
// address is reused, we can detect the special key and handle port-in-use
|
|
// problems gracefully by trying a different one.
|
|
//
|
|
// This special key is not documented and should not be used by anyone but
|
|
// us. We'll use the socketport ID as the value so that it's seemingly random,
|
|
// just to try to scare anyone off from mimicking it in addresses they generate.
|
|
// But we're not going to actually use the value. If the component is present
|
|
// and the value is the right size, we'll use it. If someone puts this into an
|
|
// address on their own, they get what they deserve (not like this will cause
|
|
// us to blow up or anything)...
|
|
//
|
|
// Look in CSPData::BindEndpoint for where this gets read back in.
|
|
//
|
|
|
|
if ( GatewayBindType == GATEWAY_BIND_TYPE_DEFAULT )
|
|
{
|
|
//
|
|
// Add the component, but ignore failure, we can still survive without it.
|
|
//
|
|
hr = IDirectPlay8Address_AddComponent( pAddress, // interface
|
|
DPNA_PRIVATEKEY_PORT_NOT_SPECIFIC, // tag
|
|
&(this->m_dwSocketPortID), // component data
|
|
sizeof(this->m_dwSocketPortID), // component data size
|
|
DPNA_DATATYPE_DWORD // component data type
|
|
);
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Couldn't add private port-not-specific component (err = 0x%lx)! Ignoring.", hr);
|
|
hr = DPN_OK;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SP_ADDRESS_TYPE_HOST:
|
|
case SP_ADDRESS_TYPE_PUBLIC_HOST_ADDRESS:
|
|
{
|
|
//
|
|
// Try to get the public address, if we have one.
|
|
//
|
|
if ( ( m_pNetworkSocketAddress->GetFamily() == AF_INET ) &&
|
|
( m_pThreadPool->IsNATHelpLoaded() ) )
|
|
{
|
|
pTempAddress = m_pSPData->GetNewAddress();
|
|
if ( pTempAddress != NULL)
|
|
{
|
|
//
|
|
// IDirectPlayNATHelp::GetCaps had better have been called with the
|
|
// DPNHGETCAPS_UPDATESERVERSTATUS flag at least once prior to this.
|
|
// See CThreadPool::PreventThreadPoolReduction
|
|
//
|
|
|
|
for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
|
|
{
|
|
if (g_papNATHelpObjects[dwTemp] != NULL)
|
|
{
|
|
dwAddressSize = sizeof(saddr);
|
|
#ifdef DEBUG
|
|
hr = IDirectPlayNATHelp_GetRegisteredAddresses( g_papNATHelpObjects[dwTemp], // object
|
|
m_ahNATHelpPorts[dwTemp], // port binding
|
|
&saddr, // place to store address
|
|
&dwAddressSize, // address buffer size
|
|
&dwAddressTypeFlags, // get type flags for printing in debug
|
|
NULL, // don't need lease time remaining
|
|
0 ); // no flags
|
|
#else
|
|
hr = IDirectPlayNATHelp_GetRegisteredAddresses( g_papNATHelpObjects[dwTemp], // object
|
|
m_ahNATHelpPorts[dwTemp], // port binding
|
|
&saddr, // place to store address
|
|
&dwAddressSize, // address buffer size
|
|
NULL, // don't bother getting type flags in retail
|
|
NULL, // don't need lease time remaining
|
|
0 ); // no flags
|
|
#endif // DEBUG
|
|
if (hr == DPNH_OK)
|
|
{
|
|
pTempAddress->SetAddressFromSOCKADDR( saddr, sizeof(saddr) );
|
|
|
|
DPFX(DPFPREP, 2, "Internet gateway index %u currently maps address (type flags = 0x%lx):",
|
|
dwTemp, dwAddressTypeFlags);
|
|
DumpSocketAddress( 2, m_pNetworkSocketAddress->GetAddress(), m_pNetworkSocketAddress->GetFamily() );
|
|
DumpSocketAddress( 2, pTempAddress->GetAddress(), pTempAddress->GetFamily() );
|
|
|
|
//
|
|
// Double check that the address we got was valid.
|
|
//
|
|
DNASSERT( ((SOCKADDR_IN*) (&saddr))->sin_addr.S_un.S_addr != 0 );
|
|
|
|
//
|
|
// Get out of the loop since we have a mapping.
|
|
//
|
|
break;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
switch (hr)
|
|
{
|
|
case DPNHERR_NOMAPPING:
|
|
{
|
|
DPFX(DPFPREP, 1, "Internet gateway (index %u, type flags = 0x%lx) does not have a public address.",
|
|
dwTemp, dwAddressTypeFlags);
|
|
break;
|
|
}
|
|
|
|
case DPNHERR_PORTUNAVAILABLE:
|
|
{
|
|
DPFX(DPFPREP, 1, "Port is unavailable on Internet gateway (index %u).", dwTemp );
|
|
break;
|
|
}
|
|
|
|
case DPNHERR_SERVERNOTAVAILABLE:
|
|
{
|
|
DPFX(DPFPREP, 1, "No Internet gateway (index %u).", dwTemp );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 1, "An error (0x%lx) occurred while getting registered address mapping index %u.",
|
|
hr, dwTemp);
|
|
break;
|
|
}
|
|
}
|
|
#endif // DEBUG
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No object in this slot.
|
|
//
|
|
}
|
|
} // end for (each DPNATHelp object)
|
|
|
|
|
|
//
|
|
// If we found a mapping, pTempAddress is not NULL and contains the mapping's
|
|
// address. If we couldn't find any mappings with any of the NAT Help objects,
|
|
// pTempAddress will be non-NULL, but bogus. We should return the local address
|
|
// if it's a HOST address, or NULL if the caller was trying to get the public
|
|
// address.
|
|
//
|
|
if (hr != DPNH_OK)
|
|
{
|
|
if (AddressType == SP_ADDRESS_TYPE_HOST)
|
|
{
|
|
DPFX(DPFPREP, 1, "No NAT Help mappings exist, using regular address:");
|
|
DumpSocketAddress( 1, m_pNetworkSocketAddress->GetAddress(), m_pNetworkSocketAddress->GetFamily() );
|
|
pTempAddress->CopyAddressSettings( m_pNetworkSocketAddress );
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 1, "No NAT Help mappings exist, not returning address.");
|
|
m_pSPData->ReturnAddress( pTempAddress );
|
|
pTempAddress = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We found a mapping.
|
|
//
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Couldn't get temporary address object, we won't return an address.
|
|
//
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// NAT Help not loaded or not necessary.
|
|
//
|
|
|
|
if (AddressType == SP_ADDRESS_TYPE_HOST)
|
|
{
|
|
pTempAddress = m_pSPData->GetNewAddress();
|
|
if ( pTempAddress != NULL )
|
|
{
|
|
pTempAddress->CopyAddressSettings( m_pNetworkSocketAddress );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Couldn't allocate memory, we won't return an address.
|
|
//
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Public host address requested. NAT Help not available, so of course
|
|
// there won't be a public address. Return NULL.
|
|
//
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we determined we had an address to return, convert it to the
|
|
// IDirectPlay8Address object our caller is expecting.
|
|
//
|
|
if ( pTempAddress != NULL )
|
|
{
|
|
//
|
|
// We have an address to return.
|
|
//
|
|
#ifdef DEBUG
|
|
if (pTempAddress->GetFamily() == AF_INET)
|
|
{
|
|
psaddrin = (SOCKADDR_IN *) pTempAddress->GetAddress();
|
|
DNASSERT( psaddrin->sin_addr.S_un.S_addr != 0 );
|
|
DNASSERT( psaddrin->sin_addr.S_un.S_addr != INADDR_BROADCAST );
|
|
DNASSERT( psaddrin->sin_port != 0 );
|
|
}
|
|
else
|
|
{
|
|
DNASSERT(pTempAddress->GetFamily() == AF_IPX);
|
|
}
|
|
#endif // DEBUG
|
|
|
|
|
|
//
|
|
// Convert the socket address to an IDirectPlay8Address
|
|
//
|
|
pTempAddress->SetAddressType( SP_ADDRESS_TYPE_HOST );
|
|
pAddress = pTempAddress->DP8AddressFromSocketAddress();
|
|
|
|
m_pSPData->ReturnAddress( pTempAddress );
|
|
pTempAddress = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not returning an address.
|
|
//
|
|
DNASSERT( pAddress == NULL );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
//
|
|
// shouldn't be here
|
|
//
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
DPFX(DPFPREP, 8, "(0x%p) Returning [0x%p]", this, pAddress );
|
|
|
|
return pAddress;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::Winsock2ReceiveComplete - a Winsock2 socket receive completed
|
|
//
|
|
// Entry: Pointer to read data
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::Winsock2ReceiveComplete"
|
|
|
|
void CSocketPort::Winsock2ReceiveComplete( CReadIOData *const pReadData )
|
|
{
|
|
DPFX(DPFPREP, 8, "Socket port 0x%p completing read data 0x%p with result %i, bytes %u.",
|
|
this, pReadData, pReadData->m_ReceiveWSAReturn, pReadData->m_dwOverlappedBytesReceived);
|
|
|
|
|
|
DNASSERT( pReadData != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
#ifdef WIN95
|
|
DNASSERT( pReadData->Win9xOperationPending() == FALSE );
|
|
#endif
|
|
|
|
//
|
|
// figure out what's happening with this socket port
|
|
//
|
|
Lock();
|
|
switch ( m_State )
|
|
{
|
|
//
|
|
// we're unbound, discard this message and don't ask for any more
|
|
//
|
|
case SOCKET_PORT_STATE_UNBOUND:
|
|
{
|
|
DPFX(DPFPREP, 1, "Socket port 0x%p is unbound ignoring result %i (%u bytes).",
|
|
this, pReadData->m_ReceiveWSAReturn, pReadData->m_dwOverlappedBytesReceived );
|
|
Unlock();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// we're initialized, process input data and submit a new receive if
|
|
// applicable
|
|
//
|
|
case SOCKET_PORT_STATE_INITIALIZED:
|
|
{
|
|
switch ( pReadData->m_ReceiveWSAReturn )
|
|
{
|
|
//
|
|
// the socket was closed on an outstanding read, stop
|
|
// receiving
|
|
//
|
|
case WSAENOTSOCK: // WinNT return for closed socket
|
|
case ERROR_OPERATION_ABORTED: // Win9x return for closed socket
|
|
{
|
|
Unlock();
|
|
DPFX(DPFPREP, 1, "Ignoring socket closing err (%u/0x%lx).",
|
|
pReadData->m_ReceiveWSAReturn, pReadData->m_ReceiveWSAReturn );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other error, perform another receive and process data if
|
|
// applicable
|
|
//
|
|
default:
|
|
{
|
|
//
|
|
// stop if the error isn't 'expected'
|
|
//
|
|
switch ( pReadData->m_ReceiveWSAReturn )
|
|
{
|
|
//
|
|
// ERROR_SUCCESS = no problem (process received data)
|
|
//
|
|
case ERROR_SUCCESS:
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// WSAECONNRESET = previous send failed
|
|
// ERROR_PORT_UNREACHABLE = previous send failed
|
|
// ERROR_MORE_DATA = datagram was sent that was too large
|
|
// ERROR_FILE_NOT_FOUND = socket was closed or previous send failed
|
|
//
|
|
case WSAECONNRESET:
|
|
case ERROR_PORT_UNREACHABLE:
|
|
case ERROR_MORE_DATA:
|
|
case ERROR_FILE_NOT_FOUND:
|
|
{
|
|
DPFX(DPFPREP, 1, "Ignoring known receive err 0x%lx.", pReadData->m_ReceiveWSAReturn );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "Unexpected return from WSARecvFrom() 0x%lx.", pReadData->m_ReceiveWSAReturn );
|
|
DisplayErrorCode( 0, pReadData->m_ReceiveWSAReturn );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// The socket state must not go to UNBOUND while we are in a receive or we will be using
|
|
// an invalid socket handle.
|
|
m_iThreadsInReceive++;
|
|
|
|
Unlock();
|
|
|
|
Winsock2Receive();
|
|
|
|
Lock();
|
|
|
|
m_iThreadsInReceive--;
|
|
|
|
Unlock();
|
|
|
|
if ( pReadData->m_ReceiveWSAReturn == ERROR_SUCCESS )
|
|
{
|
|
pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize = pReadData->m_dwOverlappedBytesReceived;
|
|
ProcessReceivedData( pReadData );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other state
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
Unlock();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the current data to the pool and note that this I/O operation is
|
|
// complete. Clear the overlapped bytes received so they aren't misinterpreted
|
|
// if this item is reused from the pool.
|
|
//
|
|
DNASSERT( pReadData != NULL );
|
|
pReadData->m_dwOverlappedBytesReceived = 0;
|
|
pReadData->DecRef();
|
|
DecRef();
|
|
|
|
return;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::CancelReceive - cancel a pending receive
|
|
//
|
|
// Entry: Poiner to read data
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::CancelReceive"
|
|
|
|
void CSocketPort::CancelReceive( CReadIOData *const pRead )
|
|
{
|
|
DPFX(DPFPREP, 1, "Cancelling receive 0x%p.", pRead);
|
|
|
|
DNASSERT( pRead != NULL );
|
|
|
|
pRead->DecRef();
|
|
DecRef();
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CSocketPort::ProcessReceivedData - process received data
|
|
//
|
|
// Entry: Pointer to CReadIOData
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CSocketPort::ProcessReceivedData"
|
|
|
|
void CSocketPort::ProcessReceivedData( CReadIOData *const pReadData )
|
|
{
|
|
PREPEND_BUFFER * pPrependBuffer;
|
|
HANDLE hEndpoint;
|
|
CEndpoint * pEndpoint;
|
|
BOOL fDataClaimed;
|
|
CBilink * pBilink;
|
|
CEndpoint * pCurrentEndpoint;
|
|
CSocketAddress * pSocketAddress;
|
|
|
|
|
|
DNASSERT( pReadData != NULL );
|
|
|
|
|
|
DPFX(DPFPREP, 7, "(0x%p) Processing %u bytes of data from + to:", this, pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize);
|
|
DumpSocketAddress(7, pReadData->m_pSourceSocketAddress->GetAddress(), pReadData->m_pSourceSocketAddress->GetFamily());
|
|
DumpSocketAddress(7, this->GetNetworkAddress()->GetAddress(), this->GetNetworkAddress()->GetFamily());
|
|
|
|
|
|
DBG_CASSERT( sizeof( pReadData->ReceivedBuffer()->BufferDesc.pBufferData ) == sizeof( PREPEND_BUFFER* ) );
|
|
pPrependBuffer = reinterpret_cast<PREPEND_BUFFER*>( pReadData->ReceivedBuffer()->BufferDesc.pBufferData );
|
|
|
|
//
|
|
// Check data for integrity and decide what to do with it. If there is
|
|
// enough data to determine an SP command type, try that. If there isn't
|
|
// enough data, and it looks spoofed, reject it.
|
|
//
|
|
if ( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize >= sizeof( pPrependBuffer->GenericHeader ) )
|
|
{
|
|
if ( pPrependBuffer->GenericHeader.bSPLeadByte != SP_HEADER_LEAD_BYTE )
|
|
{
|
|
goto ProcessUserData;
|
|
}
|
|
else
|
|
{
|
|
switch ( pPrependBuffer->GenericHeader.bSPCommandByte )
|
|
{
|
|
//
|
|
// Normal data, the user's first byte matched the service
|
|
// provider tag byte. Check for a non-zero payload and skip the
|
|
// header that was added.
|
|
//
|
|
case ESCAPED_USER_DATA_KIND:
|
|
{
|
|
if ( ( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize > sizeof( pPrependBuffer->EscapedUserDataHeader ) ) &&
|
|
( pPrependBuffer->EscapedUserDataHeader.wPad == ESCAPED_USER_DATA_PAD_VALUE ) )
|
|
{
|
|
pReadData->ReceivedBuffer()->BufferDesc.pBufferData = &pReadData->ReceivedBuffer()->BufferDesc.pBufferData[ sizeof( pPrependBuffer->EscapedUserDataHeader ) ];
|
|
DNASSERT( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize > sizeof( pPrependBuffer->EscapedUserDataHeader ) );
|
|
pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize -= sizeof( pPrependBuffer->EscapedUserDataHeader );
|
|
goto ProcessUserData;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Enum data, send it to the active listen (if there's one). Allow users to send
|
|
// enum requests that contain no data.
|
|
//
|
|
case ENUM_DATA_KIND:
|
|
{
|
|
if ( ! g_fIgnoreEnums )
|
|
{
|
|
if ( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize >= sizeof( pPrependBuffer->EnumDataHeader ) )
|
|
{
|
|
LockEndpointData();
|
|
if ( m_hListenEndpoint != INVALID_HANDLE_VALUE )
|
|
{
|
|
//
|
|
// add a reference to this endpoint so it doesn't go away while we're processing
|
|
// this data
|
|
//
|
|
pEndpoint = m_pSPData->EndpointFromHandle( m_hListenEndpoint );
|
|
UnlockEndpointData();
|
|
|
|
if ( pEndpoint != NULL )
|
|
{
|
|
//
|
|
// skip prepended enum header
|
|
//
|
|
pReadData->ReceivedBuffer()->BufferDesc.pBufferData = &pReadData->ReceivedBuffer()->BufferDesc.pBufferData[ sizeof( pPrependBuffer->EnumDataHeader ) ];
|
|
DNASSERT( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize >= sizeof( pPrependBuffer->EnumDataHeader ) );
|
|
pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize -= sizeof( pPrependBuffer->EnumDataHeader );
|
|
|
|
//
|
|
// process data
|
|
//
|
|
pEndpoint->ProcessEnumData( pReadData->ReceivedBuffer(),
|
|
pPrependBuffer->EnumDataHeader.wEnumPayload,
|
|
pReadData->m_pSourceSocketAddress );
|
|
pEndpoint->DecCommandRef();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// there's no listen active, return the receive buffer to the pool
|
|
//
|
|
UnlockEndpointData();
|
|
|
|
DPFX(DPFPREP, 7, "Ignoring enumeration, no associated listen." );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 7, "Ignoring enumeration attempt." );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Enum response data, find the appropriate enum and pass it on. Allow users to send
|
|
// enum responses that contain no data.
|
|
//
|
|
case ENUM_RESPONSE_DATA_KIND:
|
|
{
|
|
if ( ! g_fIgnoreEnums )
|
|
{
|
|
if ( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize >= sizeof( pPrependBuffer->EnumResponseDataHeader ) )
|
|
{
|
|
CEndpointEnumKey Key;
|
|
|
|
|
|
Key.SetKey( pPrependBuffer->EnumResponseDataHeader.wEnumResponsePayload );
|
|
LockEndpointData();
|
|
if ( m_EnumEndpointHash.Find( &Key, &hEndpoint ) != FALSE )
|
|
{
|
|
UnlockEndpointData();
|
|
|
|
DNASSERT( hEndpoint != INVALID_HANDLE_VALUE );
|
|
pEndpoint = m_pSPData->EndpointFromHandle( hEndpoint );
|
|
if ( pEndpoint != NULL )
|
|
{
|
|
pReadData->ReceivedBuffer()->BufferDesc.pBufferData = &pReadData->ReceivedBuffer()->BufferDesc.pBufferData[ sizeof( pPrependBuffer->EnumResponseDataHeader ) ];
|
|
DNASSERT( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize > sizeof( pPrependBuffer->EnumResponseDataHeader ) );
|
|
pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize -= sizeof( pPrependBuffer->EnumResponseDataHeader );
|
|
|
|
pEndpoint->ProcessEnumResponseData( pReadData->ReceivedBuffer(),
|
|
pReadData->m_pSourceSocketAddress,
|
|
( pPrependBuffer->EnumResponseDataHeader.wEnumResponsePayload & ENUM_RTT_MASK ) );
|
|
pEndpoint->DecCommandRef();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// the associated ENUM doesn't exist, return the receive buffer
|
|
//
|
|
UnlockEndpointData();
|
|
|
|
DPFX(DPFPREP, 7, "Ignoring enumeration response, no enum associated with key (%u).",
|
|
pPrependBuffer->EnumResponseDataHeader.wEnumResponsePayload );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 7, "Ignoring enumeration response attempt." );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// proxied query data, this data was forwarded from another port. Munge
|
|
// the return address, modify the buffer pointer and then send it up
|
|
// through the normal enum data processing pipeline.
|
|
//
|
|
case PROXIED_ENUM_DATA_KIND:
|
|
{
|
|
if ( ! g_fIgnoreEnums )
|
|
{
|
|
if ( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize >= sizeof( pPrependBuffer->ProxiedEnumDataHeader ) )
|
|
{
|
|
LockEndpointData();
|
|
if ( m_hListenEndpoint != INVALID_HANDLE_VALUE )
|
|
{
|
|
//
|
|
// add a reference to this endpoint so it doesn't go
|
|
// away while we're processing this data
|
|
//
|
|
pEndpoint = m_pSPData->EndpointFromHandle( m_hListenEndpoint );
|
|
UnlockEndpointData();
|
|
|
|
if ( pEndpoint != NULL )
|
|
{
|
|
pReadData->m_pSourceSocketAddress->SetAddressFromSOCKADDR( pPrependBuffer->ProxiedEnumDataHeader.ReturnAddress,
|
|
pReadData->m_pSourceSocketAddress->GetAddressSize() );
|
|
|
|
pReadData->ReceivedBuffer()->BufferDesc.pBufferData = &pReadData->ReceivedBuffer()->BufferDesc.pBufferData[ sizeof( pPrependBuffer->ProxiedEnumDataHeader ) ];
|
|
|
|
DNASSERT( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize > sizeof( pPrependBuffer->ProxiedEnumDataHeader ) );
|
|
pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize -= sizeof( pPrependBuffer->ProxiedEnumDataHeader );
|
|
|
|
pEndpoint->ProcessEnumData( pReadData->ReceivedBuffer(),
|
|
pPrependBuffer->ProxiedEnumDataHeader.wEnumKey,
|
|
pReadData->m_pSourceSocketAddress );
|
|
pEndpoint->DecCommandRef();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// there's no listen active, return the receive buffer to the pool
|
|
//
|
|
UnlockEndpointData();
|
|
|
|
DPFX(DPFPREP, 7, "Ignoring proxied enumeration, no associated listen." );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 7, "Ignoring proxied enumeration attempt." );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize >= sizeof( pPrependBuffer->GenericHeader
|
|
{
|
|
if ( ( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize == 1 ) &&
|
|
( pPrependBuffer->GenericHeader.bSPLeadByte != SP_HEADER_LEAD_BYTE ) )
|
|
{
|
|
goto ProcessUserData;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return;
|
|
|
|
ProcessUserData:
|
|
//
|
|
// If there's an active connection, send it to the connection. If there's
|
|
// no active connection, send it to an available 'listen' to indicate a
|
|
// potential new connection.
|
|
//
|
|
LockEndpointData();
|
|
DNASSERT( pReadData->ReceivedBuffer()->BufferDesc.dwBufferSize != 0 );
|
|
|
|
if ( m_ConnectEndpointHash.Find( pReadData->m_pSourceSocketAddress, &hEndpoint ) )
|
|
{
|
|
DNASSERT( hEndpoint != INVALID_HANDLE_VALUE );
|
|
pEndpoint = m_pSPData->EndpointFromHandle( hEndpoint );
|
|
if ( pEndpoint != NULL )
|
|
{
|
|
pEndpoint->IncNumReceives();
|
|
|
|
UnlockEndpointData();
|
|
|
|
pEndpoint->ProcessUserData( pReadData );
|
|
pEndpoint->DecCommandRef();
|
|
}
|
|
else
|
|
{
|
|
UnlockEndpointData();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Next see if the data is a proxied response
|
|
//
|
|
if ((this->IsUsingProxyWinSockLSP()) || (g_fTreatAllResponsesAsProxied))
|
|
{
|
|
pEndpoint = NULL;
|
|
pBilink = this->m_blConnectEndpointList.GetNext();
|
|
while (pBilink != &this->m_blConnectEndpointList)
|
|
{
|
|
pCurrentEndpoint = CEndpoint::EndpointFromSocketPortListBilink(pBilink);
|
|
|
|
if (pCurrentEndpoint->GetNumReceives() == 0)
|
|
{
|
|
if (pEndpoint != NULL)
|
|
{
|
|
DPFX(DPFPREP, 1, "Receiving data from unknown source, but two or more connects are pending on socketport 0x%p, no proxied association can be made.",
|
|
this);
|
|
pEndpoint = NULL;
|
|
break;
|
|
}
|
|
|
|
pEndpoint = pCurrentEndpoint;
|
|
|
|
//
|
|
// Continue, so we can verify there aren't two simultaneous
|
|
// connects going on.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This endpoint has already received some data.
|
|
// It can't be proxied.
|
|
//
|
|
}
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
|
|
if (pEndpoint != NULL)
|
|
{
|
|
pSocketAddress = pEndpoint->GetWritableRemoteAddressPointer();
|
|
|
|
DPFX(DPFPREP, 1, "Found connecting endpoint 0x%p off socketport 0x%p, assuming data from unknown source is a proxied response. Changing target (was + now):",
|
|
pEndpoint, this);
|
|
DumpSocketAddress(1, pSocketAddress->GetAddress(), pSocketAddress->GetFamily());
|
|
DumpSocketAddress(1, pReadData->m_pSourceSocketAddress->GetAddress(), pReadData->m_pSourceSocketAddress->GetFamily());
|
|
|
|
|
|
DNASSERT( this->m_ConnectEndpointHash.Find( pSocketAddress, &hEndpoint ) );
|
|
this->m_ConnectEndpointHash.Remove( pSocketAddress );
|
|
|
|
//
|
|
// We could have regkey to leave the target socketaddress the same
|
|
// so outbound always goes to that address and inbound always comes
|
|
// in via the different one, however that means adding a variable to
|
|
// hold the original target address because we currently pull the
|
|
// endpoint out of the hash table by it's remoteaddress pointer, so
|
|
// if that differed from what was in the hash, we would fail to find
|
|
// it. Since we're not trying to enable that scenario for now
|
|
// (we're just doing this for ISA Server proxy), I'm not doing that
|
|
// work yet. See CSPData::BindEndpoint.
|
|
//
|
|
pSocketAddress->CopyAddressSettings( pReadData->m_pSourceSocketAddress );
|
|
|
|
if ( m_ConnectEndpointHash.Insert( pSocketAddress, pEndpoint->GetHandle() ) == FALSE )
|
|
{
|
|
UnlockEndpointData();
|
|
|
|
DPFX(DPFPREP, 0, "Problem adding endpoint to connect socket port hash!" );
|
|
|
|
//
|
|
// Nothing we can really do... We're hosed.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Indicate the data via the new address.
|
|
//
|
|
|
|
pEndpoint->IncNumReceives();
|
|
pEndpoint->AddCommandRef();
|
|
|
|
UnlockEndpointData();
|
|
|
|
pEndpoint->ProcessUserData( pReadData );
|
|
pEndpoint->DecCommandRef();
|
|
}
|
|
|
|
fDataClaimed = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Either there weren't any connects pending, or there
|
|
// were 2 or more so we couldn't pick.
|
|
//
|
|
fDataClaimed = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not considering data as proxied.
|
|
//
|
|
|
|
fDataClaimed = FALSE;
|
|
}
|
|
|
|
|
|
if (! fDataClaimed)
|
|
{
|
|
if ( m_hListenEndpoint != INVALID_HANDLE_VALUE )
|
|
{
|
|
pEndpoint = m_pSPData->EndpointFromHandle( m_hListenEndpoint );
|
|
UnlockEndpointData();
|
|
|
|
if ( pEndpoint != NULL )
|
|
{
|
|
pEndpoint->ProcessUserDataOnListen( pReadData, pReadData->m_pSourceSocketAddress );
|
|
pEndpoint->DecCommandRef();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Nobody claimed this data.
|
|
//
|
|
UnlockEndpointData();
|
|
}
|
|
}
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|