1390 lines
29 KiB
C++
1390 lines
29 KiB
C++
/*++
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
iisendp.cxx
|
||
|
||
Abstract:
|
||
|
||
This module defines the IIS_ENDPOINT class
|
||
|
||
Author:
|
||
|
||
Johnson Apacible (JohnsonA) June-04-1996
|
||
|
||
--*/
|
||
|
||
#include "tcpdllp.hxx"
|
||
#include <rpc.h>
|
||
#include <tsunami.hxx>
|
||
#include <iistypes.hxx>
|
||
#include <iisendp.hxx>
|
||
#include <iisassoc.hxx>
|
||
#include "inetreg.h"
|
||
#include <tcpcons.h>
|
||
#include <apiutil.h>
|
||
#include <issched.hxx>
|
||
|
||
#include "..\atq\atqtypes.hxx"
|
||
|
||
#if IE_REF_TRACKING
|
||
|
||
//
|
||
// Ref count trace log size
|
||
//
|
||
#define C_IIS_ENDP_REFTRACES 4000
|
||
#define C_LOCAL_ENDP_REFTRACES 40
|
||
|
||
//
|
||
// Ref trace log for IIS_ENDPOINT objects
|
||
// NOTE we make this global so other classes can get at it
|
||
//
|
||
PTRACE_LOG g_pDbgIERefTraceLog = NULL;
|
||
|
||
#endif
|
||
|
||
/*******************************************************************
|
||
|
||
Macro support for IIS_ENDPOINT::Reference/Dereference
|
||
|
||
HISTORY:
|
||
MCourage 31-Oct-1997 Added ref trace logging
|
||
|
||
********************************************************************/
|
||
|
||
#if IE_REF_TRACKING
|
||
#define IE_LOG_REF_COUNT( cRefs ) \
|
||
\
|
||
IE_SHARED_LOG_REF_COUNT( \
|
||
cRefs \
|
||
, (PVOID) this \
|
||
, m_state \
|
||
, m_atqEndpoint \
|
||
, 0xffffffff \
|
||
); \
|
||
IE_LOCAL_LOG_REF_COUNT( \
|
||
cRefs \
|
||
, (PVOID) this \
|
||
, m_state \
|
||
, m_atqEndpoint \
|
||
, 0xffffffff \
|
||
);
|
||
#else
|
||
#define IE_LOG_REF_COUNT( cRefs )
|
||
#endif
|
||
|
||
|
||
|
||
PVOID
|
||
I_IISAddListenEndpoint(
|
||
IN PATQ_ENDPOINT_CONFIGURATION Configuration,
|
||
IN PVOID EndpointContext
|
||
);
|
||
|
||
|
||
BOOL
|
||
IIS_SERVICE::AssociateInstance(
|
||
IN PIIS_SERVER_INSTANCE pInstance
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Associates an instance with an endpoint. It also activates the endpoint.
|
||
|
||
Arguments:
|
||
|
||
pInstance - instance to associate.
|
||
|
||
Return Value:
|
||
|
||
TRUE - if successful, FALSE otherwise
|
||
|
||
--*/
|
||
{
|
||
|
||
DWORD err = NO_ERROR;
|
||
BOOL shouldStart = FALSE;
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"AssociateInstance %p called\n",
|
||
pInstance));
|
||
}
|
||
|
||
//
|
||
// Lock the service.
|
||
//
|
||
|
||
AcquireServiceLock( TRUE );
|
||
|
||
//
|
||
// if service is closing, abort
|
||
//
|
||
|
||
if ( !IsActive() ) {
|
||
err = ERROR_NOT_READY;
|
||
goto exit;
|
||
}
|
||
|
||
if ( pInstance->QueryServerState( ) != MD_SERVER_STATE_STOPPED ) {
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,"Server not in stopped (%d) state\n",
|
||
pInstance->QueryServerState()));
|
||
}
|
||
err = ERROR_INVALID_FUNCTION;
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Start the server instance.
|
||
//
|
||
|
||
shouldStart = TRUE;
|
||
err = pInstance->DoStartInstance();
|
||
|
||
if( err != NO_ERROR ) {
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT, "BindInstance() failed, %lu\n", err));
|
||
}
|
||
}
|
||
|
||
exit:
|
||
|
||
if( shouldStart ) {
|
||
|
||
//
|
||
// We're up and running. Note that the StartInstance() method will
|
||
// set the instance state appropriately if successful, so we only
|
||
// need to set it if the start failed.
|
||
//
|
||
|
||
if( err != NO_ERROR ) {
|
||
pInstance->SetServerState( MD_SERVER_STATE_STOPPED, err );
|
||
}
|
||
|
||
ReleaseServiceLock( TRUE );
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,"AssociateInstace: Error %d\n",
|
||
err));
|
||
}
|
||
|
||
pInstance->SetServerState( MD_SERVER_STATE_STOPPED, err );
|
||
ReleaseServiceLock( TRUE );
|
||
SetLastError(err);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
} // IIS_SERVICE::AssociateInstance
|
||
|
||
|
||
|
||
BOOL
|
||
IIS_SERVICE::DisassociateInstance(
|
||
IN PIIS_SERVER_INSTANCE pInstance
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes an instance from an endpoint.
|
||
|
||
Arguments:
|
||
|
||
pInstance - instance to associate.
|
||
|
||
Return Value:
|
||
|
||
TRUE - if successful, FALSE otherwise
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// if it's running, stop it
|
||
//
|
||
|
||
AcquireServiceLock( TRUE );
|
||
|
||
if ( pInstance->QueryServerState( ) == MD_SERVER_STATE_STOPPED ) {
|
||
ReleaseServiceLock( TRUE );
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"Cannot disassociate stopped server %p\n",pInstance));
|
||
}
|
||
return(TRUE);
|
||
}
|
||
|
||
pInstance->SetServerState( MD_SERVER_STATE_STOPPED, NO_ERROR );
|
||
ReleaseServiceLock( TRUE );
|
||
|
||
//
|
||
// Blow away any users still clinging to this instance,
|
||
// then unbind the instance.
|
||
//
|
||
|
||
pInstance->Reference();
|
||
DisconnectUsersByInstance( pInstance );
|
||
pInstance->UnbindInstance();
|
||
pInstance->Dereference();
|
||
|
||
return TRUE;
|
||
|
||
} // IIS_SERVICE::DisassociateInstance
|
||
|
||
|
||
|
||
BOOL
|
||
IIS_SERVICE::ShutdownService(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Shuts down all endpoints.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE if successful, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// Walk the list and close the instances
|
||
//
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT, "ShutdownService called\n"));
|
||
}
|
||
|
||
//
|
||
// Update the service state.
|
||
//
|
||
|
||
AcquireServiceLock( );
|
||
m_state = BlockStateClosed;
|
||
ReleaseServiceLock( );
|
||
|
||
//
|
||
// Blow away all instances.
|
||
//
|
||
|
||
DestroyAllServerInstances();
|
||
|
||
return TRUE;
|
||
|
||
} // IIS_SERVICE::ShutdownService
|
||
|
||
|
||
|
||
|
||
IIS_ENDPOINT::IIS_ENDPOINT(
|
||
IN PIIS_SERVICE pService,
|
||
IN USHORT Port,
|
||
IN DWORD IpAddress,
|
||
IN BOOL fIsSecure
|
||
)
|
||
:
|
||
m_signature ( IIS_ENDPOINT_SIGNATURE),
|
||
m_state ( BlockStateIdle),
|
||
m_atqEndpoint ( NULL),
|
||
m_isSecure ( fIsSecure),
|
||
m_fAtqEpStopped ( FALSE),
|
||
m_service ( NULL),
|
||
m_reference ( 1),
|
||
m_NumQualifiedInstances ( 0),
|
||
m_WildcardInstance ( NULL),
|
||
m_nAcceptExOutstanding ( 0),
|
||
m_AcceptExTimeout ( 0),
|
||
m_nInstances ( 0)
|
||
{
|
||
|
||
//
|
||
// initialize the lock
|
||
//
|
||
|
||
INITIALIZE_CRITICAL_SECTION(&m_endpointLock);
|
||
|
||
//
|
||
// initialize the association info
|
||
//
|
||
|
||
ZeroMemory(
|
||
m_QualifiedInstances,
|
||
sizeof(m_QualifiedInstances)
|
||
);
|
||
|
||
//
|
||
// reference the service
|
||
//
|
||
|
||
if ( !pService->CheckAndReference( ) ) {
|
||
m_state = BlockStateInvalid;
|
||
return;
|
||
}
|
||
|
||
m_service = pService;
|
||
|
||
//
|
||
// use the service name as the advertised name
|
||
//
|
||
|
||
m_Port = Port;
|
||
m_IpAddress = IpAddress;
|
||
|
||
#if IE_REF_TRACKING
|
||
_pDbgIERefTraceLog = CreateRefTraceLog( C_LOCAL_ENDP_REFTRACES, 0 );
|
||
#endif
|
||
|
||
} // IIS_ENDPOINT::IIS_ENDPOINT
|
||
|
||
|
||
IIS_ENDPOINT::~IIS_ENDPOINT(
|
||
VOID
|
||
)
|
||
{
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,"IIS Endpoint %p freed\n",this));
|
||
}
|
||
|
||
DBG_ASSERT( m_signature == IIS_ENDPOINT_SIGNATURE );
|
||
|
||
//
|
||
// Delete the instance association objects.
|
||
//
|
||
|
||
for( INT qualifier = (INT)FullyQualified ;
|
||
qualifier < (INT)NumInstanceQualifiers ;
|
||
qualifier++ ) {
|
||
|
||
delete m_QualifiedInstances[qualifier];
|
||
|
||
}
|
||
|
||
//
|
||
// Dereference the owning service.
|
||
//
|
||
|
||
if ( m_service != NULL ) {
|
||
m_service->Dereference();
|
||
m_service = NULL;
|
||
}
|
||
|
||
m_signature = IIS_ENDPOINT_SIGNATURE_FREE;
|
||
DeleteCriticalSection(&m_endpointLock);
|
||
|
||
#if IE_REF_TRACKING
|
||
DestroyRefTraceLog( _pDbgIERefTraceLog );
|
||
#endif
|
||
|
||
} // IIS_ENDPOINT::~IIS_ENDPOINT
|
||
|
||
|
||
|
||
BOOL
|
||
IIS_ENDPOINT::AddInstance(
|
||
IN PIIS_SERVER_INSTANCE pInstance,
|
||
IN DWORD IpAddress,
|
||
IN const CHAR * HostName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds an instance to an existing endpoint.
|
||
|
||
Arguments:
|
||
|
||
pInstance - instance to add.
|
||
|
||
IpAddress - The IP address for this instance; may be INADDR_ANY;.
|
||
|
||
HostName - The host name for this instance; may be empty ("").
|
||
|
||
Return Value:
|
||
|
||
TRUE - if successful, FALSE otherwise
|
||
|
||
--*/
|
||
{
|
||
INSTANCE_QUALIFIER qualifier;
|
||
DWORD status;
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"IIS_ENDPOINT::AddInstance %p called\n", pInstance));
|
||
}
|
||
|
||
//
|
||
// Determine the proper qualifier based on the presence of the
|
||
// IP address and host name.
|
||
//
|
||
|
||
qualifier = CalcQualifier( IpAddress, HostName );
|
||
|
||
LockEndpoint();
|
||
|
||
//
|
||
// Put instance into proper association.
|
||
//
|
||
|
||
if( qualifier == WildcardInstance ) {
|
||
|
||
if( m_WildcardInstance == NULL ) {
|
||
m_WildcardInstance = pInstance;
|
||
} else {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"AddInstance: endpoint %p already has a wildcard instance\n",
|
||
this
|
||
));
|
||
|
||
status = ERROR_INVALID_PARAMETER;
|
||
goto unlock_and_fail;
|
||
}
|
||
|
||
} else {
|
||
|
||
PIIS_ASSOCIATION association;
|
||
|
||
//
|
||
// Create a new instance association object if necessary.
|
||
//
|
||
|
||
association = m_QualifiedInstances[qualifier];
|
||
|
||
if( association == NULL ) {
|
||
association = new IIS_ASSOCIATION(
|
||
( qualifier == FullyQualified ) ||
|
||
( qualifier == QualifiedByIpAddress ),
|
||
( qualifier == FullyQualified ) ||
|
||
( qualifier == QualifiedByHostName )
|
||
);
|
||
|
||
if( association == NULL ) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"AddInstance: cannot create new association\n"
|
||
));
|
||
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto unlock_and_fail;
|
||
}
|
||
|
||
m_QualifiedInstances[qualifier] = association;
|
||
}
|
||
|
||
//
|
||
// Add the instance to the association.
|
||
//
|
||
|
||
status = association->AddDescriptor(
|
||
IpAddress,
|
||
HostName,
|
||
(LPVOID)pInstance
|
||
);
|
||
|
||
if( status != NO_ERROR ) {
|
||
goto unlock_and_fail;
|
||
}
|
||
|
||
//
|
||
// Update the number of qualified instances on this endpoint.
|
||
// We use this to "short circuit" the instance lookup in the
|
||
// common case of a single wildcard instance per endpoint.
|
||
//
|
||
|
||
m_NumQualifiedInstances++;
|
||
|
||
}
|
||
|
||
//
|
||
// Setup the necessary references, update the server state.
|
||
//
|
||
|
||
Reference();
|
||
pInstance->Reference();
|
||
InterlockedIncrement( (LPLONG)&m_nInstances );
|
||
|
||
//
|
||
// Aggregate the AcceptEx outstanding parameter.
|
||
//
|
||
|
||
m_nAcceptExOutstanding += pInstance->QueryAcceptExOutstanding();
|
||
m_nMaximumAcceptExOutstanding = pInstance->QueryMaxEndpointConnections();
|
||
|
||
//
|
||
// Activate the endpoint if necessary.
|
||
//
|
||
|
||
if ( !ActivateEndpoint() ) {
|
||
status = GetLastError();
|
||
RemoveInstance( pInstance, IpAddress, HostName );
|
||
goto unlock_and_fail;
|
||
}
|
||
|
||
UnlockEndpoint();
|
||
return TRUE;
|
||
|
||
unlock_and_fail:
|
||
|
||
DBG_ASSERT( status != NO_ERROR );
|
||
UnlockEndpoint();
|
||
SetLastError( status );
|
||
return FALSE;
|
||
|
||
} // IIS_ENDPOINT::AddInstance
|
||
|
||
|
||
BOOL
|
||
IIS_ENDPOINT::RemoveInstance(
|
||
IN PIIS_SERVER_INSTANCE pInstance,
|
||
IN DWORD IpAddress,
|
||
IN const CHAR * HostName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes an instance to an existing endpoint.
|
||
|
||
Arguments:
|
||
|
||
pInstance - instance to remove
|
||
|
||
IpAddress - The IP address for this instance; may be INADDR_ANY;.
|
||
|
||
HostName - The host name for this instance; may be empty ("").
|
||
|
||
Return Value:
|
||
|
||
TRUE - if successful, FALSE otherwise
|
||
|
||
--*/
|
||
{
|
||
INSTANCE_QUALIFIER qualifier;
|
||
DWORD status;
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"RemoveInstance called endpoint %p instance %p\n",
|
||
this, pInstance ));
|
||
}
|
||
|
||
DBG_ASSERT( m_signature == IIS_ENDPOINT_SIGNATURE );
|
||
|
||
//
|
||
// Determine the proper qualifier based on the presence of the
|
||
// IP address and host name.
|
||
//
|
||
|
||
qualifier = CalcQualifier( IpAddress, HostName );
|
||
|
||
LockEndpoint();
|
||
|
||
m_nAcceptExOutstanding -= pInstance->QueryAcceptExOutstanding();
|
||
|
||
if( qualifier == WildcardInstance ) {
|
||
|
||
DBG_ASSERT( m_WildcardInstance == pInstance );
|
||
m_WildcardInstance->Dereference();
|
||
m_WildcardInstance = NULL;
|
||
|
||
} else {
|
||
|
||
LPVOID Context;
|
||
|
||
DBG_ASSERT( m_QualifiedInstances[qualifier] != NULL );
|
||
status = m_QualifiedInstances[qualifier]->RemoveDescriptor(
|
||
IpAddress,
|
||
HostName,
|
||
&Context
|
||
);
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
DBG_ASSERT( Context == (LPVOID)pInstance );
|
||
pInstance->Dereference();
|
||
m_NumQualifiedInstances--;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
UnlockEndpoint();
|
||
|
||
//
|
||
// If this was the last instance, then remove ourselves from
|
||
// the service's endpoint list and initiate shutdown.
|
||
//
|
||
|
||
if ( InterlockedDecrement( (LPLONG ) &m_nInstances) == 0 ) {
|
||
RemoveEntryList( &m_EndpointListEntry );
|
||
ShutdownEndpoint( );
|
||
}
|
||
|
||
//
|
||
// Remove the reference added in AddInstance().
|
||
//
|
||
|
||
Dereference();
|
||
return TRUE;
|
||
|
||
} // IIS_ENDPOINT::RemoveInstance
|
||
|
||
|
||
|
||
|
||
BOOL
|
||
IIS_ENDPOINT::ActivateEndpoint(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Starts an idle endpoint.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE - if successful, FALSE otherwise
|
||
|
||
--*/
|
||
{
|
||
PVOID atqEndpoint = NULL;
|
||
ATQ_ENDPOINT_CONFIGURATION config;
|
||
|
||
//
|
||
// Make sure this is idle
|
||
//
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,"Calling activate on %p\n",this));
|
||
}
|
||
|
||
LockEndpoint( );
|
||
if ( m_state == BlockStateActive ) {
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"Activate called on %p is not in idle state(%d)\n",
|
||
this, (DWORD)m_state ));
|
||
}
|
||
|
||
DBG_ASSERT(m_atqEndpoint != NULL);
|
||
|
||
AtqEndpointSetInfo(
|
||
m_atqEndpoint,
|
||
EndpointInfoAcceptExOutstanding,
|
||
min(
|
||
m_nAcceptExOutstanding,
|
||
m_nMaximumAcceptExOutstanding
|
||
)
|
||
);
|
||
|
||
UnlockEndpoint();
|
||
return(TRUE);
|
||
}
|
||
|
||
config.cbAcceptExRecvBuffer = m_service->m_cbRecvBuffer;
|
||
config.pfnConnect = m_service->m_pfnConnect;
|
||
config.pfnConnectEx = m_service->m_pfnConnectEx;
|
||
config.pfnIoCompletion = m_service->m_pfnIoCompletion;
|
||
|
||
config.ListenPort = m_Port;
|
||
config.IpAddress = m_IpAddress;
|
||
config.nAcceptExOutstanding = min( m_nAcceptExOutstanding,
|
||
m_nMaximumAcceptExOutstanding );
|
||
config.AcceptExTimeout = m_AcceptExTimeout;
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,"%d %d %d\n",
|
||
config.ListenPort,
|
||
config.nAcceptExOutstanding, config.AcceptExTimeout ));
|
||
}
|
||
|
||
atqEndpoint = I_IISAddListenEndpoint(
|
||
&config,
|
||
(PVOID)this
|
||
);
|
||
|
||
if ( atqEndpoint == NULL ) {
|
||
UnlockEndpoint();
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"Activate failed, error %d\n",GetLastError()));
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Update the state
|
||
//
|
||
|
||
m_state = BlockStateActive;
|
||
m_atqEndpoint = atqEndpoint;
|
||
Reference( );
|
||
UnlockEndpoint();
|
||
|
||
return(TRUE);
|
||
|
||
} // IIS_ENDPOINT::ActivateEndpoint
|
||
|
||
|
||
|
||
BOOL
|
||
IIS_ENDPOINT::StopEndpoint( VOID)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Stops the ATQ endpoint structure stored inside the IIS_ENDPOINT
|
||
This will prevent us from accepting new connection. This function
|
||
should be called only when are preparing ourselves to shut this
|
||
endpoint down entirely.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
History:
|
||
MuraliK 7/8/97
|
||
--*/
|
||
{
|
||
BOOL fReturn = TRUE;
|
||
|
||
//
|
||
// lock, check the state and
|
||
// then, stop any Atq Endpoint, if any.
|
||
//
|
||
|
||
LockEndpoint( );
|
||
|
||
//
|
||
// NYI: We mayhave to use an intermediate state for STopped endpoint.
|
||
// Unfortunately the state machine usage in IIS_SERVICE/INSTANCE/ENDPOINT
|
||
// is not setup for doing so. For now I will just assert that this
|
||
// object is in an Active State
|
||
// - muralik 7/8/97
|
||
//
|
||
DBG_ASSERT( m_state == BlockStateActive);
|
||
|
||
if ( m_atqEndpoint != NULL ) {
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"IIS_ENDPOINT(%08p) Stopping ATQ Endpoint %p\n",
|
||
this, m_atqEndpoint));
|
||
}
|
||
|
||
if ( !m_fAtqEpStopped ) {
|
||
|
||
fReturn = AtqStopEndpoint( m_atqEndpoint);
|
||
if ( fReturn ) {
|
||
m_fAtqEpStopped = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
UnlockEndpoint();
|
||
|
||
return (fReturn);
|
||
} // IIS_ENDPOINT::StopEndpoint()
|
||
|
||
|
||
|
||
VOID
|
||
IIS_ENDPOINT::ShutdownEndpoint(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Shuts down an endpoint.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// lock, check the state and mark it closed.
|
||
// then, shutdown any Atq Endpoint, if any.
|
||
//
|
||
|
||
LockEndpoint( );
|
||
|
||
DBG_ASSERT(m_nInstances == 0);
|
||
|
||
if ( m_state == BlockStateActive ) {
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,"Shutting down endpoint %p\n",this));
|
||
}
|
||
m_state = BlockStateClosed;
|
||
UnlockEndpoint();
|
||
|
||
IF_DEBUG( INSTANCE ) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"IIS_ENDPOINT(%08p)::ShutdownEndpoint() for "
|
||
" AtqEndpoint %08p\n",
|
||
this, m_atqEndpoint ));
|
||
}
|
||
|
||
if ( m_atqEndpoint != NULL ) {
|
||
|
||
// I replaced AtqStopAndCloseEndpoint() here
|
||
|
||
if ( !m_fAtqEpStopped ) {
|
||
|
||
DBG_REQUIRE( AtqStopEndpoint( m_atqEndpoint));
|
||
m_fAtqEpStopped = TRUE;
|
||
}
|
||
|
||
/*
|
||
* Moved this to IIS_ENDPOINT::Dereference for shutdown thread hack
|
||
*
|
||
AtqCloseEndpoint( m_atqEndpoint);
|
||
m_atqEndpoint = NULL;
|
||
*/
|
||
}
|
||
|
||
Dereference( );
|
||
} else {
|
||
UnlockEndpoint();
|
||
}
|
||
|
||
return;
|
||
} // IIS_ENDPOINT::ShutdownEndpoint
|
||
|
||
|
||
|
||
PIIS_SERVER_INSTANCE
|
||
IIS_ENDPOINT::FindAndReferenceInstance(
|
||
IN LPCSTR pszDomainName,
|
||
IN const DWORD IPAddress,
|
||
OUT LPBOOL pbMaxConnExceeded
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Finds the appropriate instance given domain name and
|
||
socket information. The instance is referenced if found.
|
||
|
||
Arguments:
|
||
|
||
pszDomainName - Domain name of request.
|
||
IPAddress - Local IP Address of the connection.
|
||
pbMaxConnExceeded - Receives TRUE if the maximum number of connections
|
||
for this endpoint has been exceeded. It is still the caller's
|
||
responsibility to properly dispose of the instance.
|
||
|
||
Return Value:
|
||
|
||
if successful, returns the pointer to the instance.
|
||
NULL, otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
PIIS_SERVER_INSTANCE instance;
|
||
DWORD status;
|
||
INT qualifier;
|
||
IIS_ASSOCIATION::HASH_CONTEXT context;
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
|
||
LPCSTR tmp = pszDomainName;
|
||
if ( tmp == NULL ) {
|
||
tmp = "";
|
||
}
|
||
DBGPRINTF((DBG_CONTEXT,"Finding %s %x\n", tmp, IPAddress));
|
||
}
|
||
|
||
LockEndpoint( );
|
||
DBG_CODE( instance = NULL );
|
||
|
||
if( m_NumQualifiedInstances == 0 ) {
|
||
|
||
//
|
||
// Fast path: only the wildcard instance.
|
||
//
|
||
|
||
instance = m_WildcardInstance;
|
||
status = NO_ERROR;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Less-fast path: we'll need to go hunt for it.
|
||
//
|
||
|
||
if( pszDomainName == NULL ) {
|
||
pszDomainName = "";
|
||
}
|
||
|
||
IIS_ASSOCIATION::InitializeHashContext( &context );
|
||
|
||
for( qualifier = FullyQualified ;
|
||
qualifier < NumInstanceQualifiers ;
|
||
qualifier++ ) {
|
||
|
||
if( m_QualifiedInstances[qualifier] != NULL ) {
|
||
|
||
status = m_QualifiedInstances[qualifier]->LookupDescriptor(
|
||
IPAddress,
|
||
pszDomainName,
|
||
(LPVOID *)&instance,
|
||
&context
|
||
);
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
goto FoundInstance;
|
||
|
||
}
|
||
|
||
DBG_ASSERT( instance == NULL );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If we made it this far, then no qualified instances will
|
||
// take the request, so use the wildcard (if available).
|
||
//
|
||
|
||
instance = m_WildcardInstance;
|
||
|
||
//
|
||
// Reset the status so that we may continue.
|
||
//
|
||
|
||
status = NO_ERROR;
|
||
|
||
}
|
||
|
||
if( instance == NULL ) {
|
||
|
||
status = ERROR_BAD_NET_NAME;
|
||
|
||
}
|
||
|
||
FoundInstance:
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
DBG_ASSERT( instance != NULL );
|
||
|
||
if( instance->QueryServerState() != MD_SERVER_STATE_STARTED ) {
|
||
status = ERROR_FILE_NOT_FOUND;
|
||
}
|
||
|
||
}
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
//
|
||
// Reference this
|
||
//
|
||
|
||
instance->Reference( );
|
||
UnlockEndpoint( );
|
||
|
||
//
|
||
// Make sure that we have not exceeded the max
|
||
//
|
||
|
||
instance->IncrementCurrentConnections();
|
||
|
||
*pbMaxConnExceeded = ( instance->QueryCurrentConnections() >
|
||
instance->QueryMaxConnections() );
|
||
|
||
if( *pbMaxConnExceeded ) {
|
||
|
||
IF_DEBUG(ERROR) {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"Too many connected users (%d) max %d, refusing connection\n",
|
||
instance->QueryCurrentConnections(),
|
||
instance->QueryMaxConnections() ));
|
||
}
|
||
}
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"Found and referenced instance %p\n",instance));
|
||
}
|
||
|
||
return instance;
|
||
|
||
}
|
||
|
||
UnlockEndpoint();
|
||
SetLastError( status );
|
||
return NULL;
|
||
|
||
} // IIS_ENDPOINT::FindInstance
|
||
|
||
|
||
|
||
|
||
PVOID
|
||
I_IISAddListenEndpoint(
|
||
IN PATQ_ENDPOINT_CONFIGURATION Configuration,
|
||
IN PVOID EndpointContext
|
||
)
|
||
/*++
|
||
|
||
Description:
|
||
|
||
Adds a TCPIP ATQ endpoint.
|
||
|
||
Arguments:
|
||
|
||
Configuration - contains the endpoint configuration
|
||
EndpointContext - context to return during completion
|
||
|
||
Returns:
|
||
|
||
TRUE if successful,
|
||
FALSE, otherwise
|
||
|
||
--*/
|
||
{
|
||
PVOID atqEndpoint;
|
||
BOOL fReturn = FALSE;
|
||
|
||
IF_DEBUG( INSTANCE ) {
|
||
DBGPRINTF(( DBG_CONTEXT, "I_IISAddListenEndpoint called\n"));
|
||
}
|
||
|
||
//
|
||
// Create the endpoint
|
||
//
|
||
|
||
atqEndpoint = AtqCreateEndpoint(
|
||
Configuration,
|
||
EndpointContext
|
||
);
|
||
|
||
if ( atqEndpoint == NULL ) {
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Activate the endpoint
|
||
//
|
||
|
||
if ( !AtqStartEndpoint(atqEndpoint) ) {
|
||
goto error_exit;
|
||
}
|
||
|
||
return (atqEndpoint);
|
||
|
||
error_exit:
|
||
|
||
DWORD dwError = GetLastError();
|
||
|
||
DBG_ASSERT( NO_ERROR != dwError );
|
||
|
||
if ( atqEndpoint != NULL ) {
|
||
|
||
AtqCloseEndpoint( atqEndpoint);
|
||
atqEndpoint = NULL;
|
||
}
|
||
|
||
SetLastError(dwError);
|
||
|
||
return(NULL);
|
||
|
||
} // I_IISAddListenEndpoint()
|
||
|
||
|
||
|
||
VOID
|
||
IIS_ENDPOINT::Reference( VOID )
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Increments the reference count for the endpoint
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
InterlockedIncrement( &m_reference );
|
||
|
||
IE_LOG_REF_COUNT( m_reference );
|
||
}
|
||
|
||
|
||
|
||
typedef struct _ENDPOINT_HACK_PARAM {
|
||
PIIS_ENDPOINT piisEndpoint;
|
||
PVOID patqEndpoint;
|
||
} ENDPOINT_HACK_PARAM, *PENDPOINT_HACK_PARAM;
|
||
|
||
|
||
VOID
|
||
WINAPI
|
||
EndpointHackFunc( PVOID pv );
|
||
|
||
|
||
VOID
|
||
IIS_ENDPOINT::Dereference( )
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Decrements the reference count for the endpoint and cleans up if the refcount
|
||
reaches zero.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
|
||
ASSERT( m_signature == IIS_ENDPOINT_SIGNATURE );
|
||
ASSERT( m_reference > 0 );
|
||
|
||
//
|
||
// Write the trace log BEFORE the decrement operation :(
|
||
// If we write it after the decrement, we will run into potential
|
||
// race conditions in this object getting freed up accidentally
|
||
// by another thread
|
||
//
|
||
// NOTE we write (_cRef - 1) == ref count AFTER decrement happens
|
||
//
|
||
LONG cRefsAfter = (m_reference - 1);
|
||
IE_LOG_REF_COUNT( cRefsAfter );
|
||
|
||
|
||
if ( InterlockedDecrement( &m_reference ) == 0 ) {
|
||
DWORD dwCookie;
|
||
PENDPOINT_HACK_PARAM pParam;
|
||
|
||
|
||
DBGPRINTF((DBG_CONTEXT,"deleting endpoint %p\n",this));
|
||
|
||
if ( m_atqEndpoint != NULL ) {
|
||
ASSERT( ((PATQ_ENDPOINT)m_atqEndpoint)->Signature == ATQ_ENDPOINT_SIGNATURE );
|
||
|
||
//
|
||
// Because the ATQ endpoint has an uncounted reference to this object, we can't
|
||
// go away until the ATQ endpoint is closed. Since it may be bad to block our
|
||
// own thread while waiting for the ATQ endpoint, we create a new thread to do
|
||
// it.
|
||
//
|
||
pParam = new ENDPOINT_HACK_PARAM;
|
||
if (pParam == NULL) {
|
||
goto threadfail;
|
||
}
|
||
|
||
pParam->piisEndpoint = this;
|
||
pParam->patqEndpoint = m_atqEndpoint;
|
||
|
||
dwCookie = ScheduleWorkItem( EndpointHackFunc, pParam, 0 );
|
||
|
||
if ( dwCookie == 0 ) {
|
||
goto threadfail;
|
||
}
|
||
} else {
|
||
//
|
||
// If we couldn't activate the endpoint we will not have an ATQ_ENDPOINT
|
||
// to close. In this case we can clean up immediately
|
||
//
|
||
delete this;
|
||
}
|
||
|
||
} else {
|
||
//DBGPRINTF((DBG_CONTEXT,"endpoint deref count %d\n",m_reference));
|
||
}
|
||
|
||
return;
|
||
|
||
threadfail:
|
||
|
||
if ( AtqCloseEndpoint( m_atqEndpoint ) ) {
|
||
|
||
ASSERT( m_signature == IIS_ENDPOINT_SIGNATURE );
|
||
|
||
delete this;
|
||
} else {
|
||
//
|
||
// There could still be some connections to us, so we can't free the memory.
|
||
// However we have to dereference the IIS_SERVICE, which is waiting for us
|
||
// during shutdown. Normally this deref occurs during the IIS_ENDPOINT destructor.
|
||
//
|
||
m_service->Dereference();
|
||
m_service = NULL;
|
||
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"AtqCloseEndpoint returned FALSE! "
|
||
"Leaking endpoints: iisEndpoint = %p, atqEndpoint = %p\n",
|
||
this, m_atqEndpoint));
|
||
}
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
WINAPI
|
||
EndpointHackFunc(
|
||
PVOID pv
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is a work item scheduled by IIS_ENDPOINT::Dereference. When the IIS_ENDPOINT
|
||
refcount hits zero, there is still a reference to the structure from the ATQ_ENDPOINT.
|
||
We call AtqCloseEndpoint, which returns when the reference is gone, and then clean up
|
||
when it is safe to do so.
|
||
|
||
Arguments:
|
||
|
||
pv - a parameter block containing pointers to the IIS_ENDPOINT and it's ATQ_ENDPOINT.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PENDPOINT_HACK_PARAM pParam = (PENDPOINT_HACK_PARAM) pv;
|
||
|
||
ASSERT( pParam->piisEndpoint->CheckSignature(IIS_ENDPOINT_SIGNATURE) );
|
||
ASSERT( ((PATQ_ENDPOINT)pParam->patqEndpoint)->Signature == ATQ_ENDPOINT_SIGNATURE );
|
||
|
||
IF_DEBUG(ENDPOINT) {
|
||
DBGPRINTF((DBG_CONTEXT, "EndpointHackFunc: iis=%p atq=%p\n",
|
||
pParam->piisEndpoint, pParam->patqEndpoint));
|
||
}
|
||
|
||
//
|
||
// if AtqCloseEndpoint fails we can't clean up, because someone could
|
||
// still have a pointer to us!
|
||
//
|
||
if ( AtqCloseEndpoint( pParam->patqEndpoint ) ) {
|
||
|
||
ASSERT( pParam->piisEndpoint->CheckSignature(IIS_ENDPOINT_SIGNATURE) );
|
||
|
||
delete pParam->piisEndpoint;
|
||
} else {
|
||
//
|
||
// There could still be some connections to us, so we can't free the memory.
|
||
// However we have to dereference the IIS_SERVICE, which is waiting for us
|
||
// during shutdown. Normally this deref occurs during the IIS_ENDPOINT destructor.
|
||
//
|
||
pParam->piisEndpoint->QueryService()->Dereference();
|
||
pParam->piisEndpoint->SetService( NULL );
|
||
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"AtqCloseEndpoint returned FALSE! "
|
||
"Leaking endpoints: iisEndpoint = %p, atqEndpoint = %p\n",
|
||
pParam->piisEndpoint, pParam->patqEndpoint));
|
||
}
|
||
|
||
delete pParam;
|
||
}
|
||
|
||
BOOL
|
||
InitializeEndpointUtilities(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called during infocomm initialization. This sets up a global debug trace
|
||
log object
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE if successful
|
||
|
||
--*/
|
||
{
|
||
#if IE_REF_TRACKING
|
||
if ( g_pDbgIERefTraceLog == NULL )
|
||
{
|
||
g_pDbgIERefTraceLog = CreateRefTraceLog( C_IIS_ENDP_REFTRACES, 0 );
|
||
return g_pDbgIERefTraceLog != NULL;
|
||
}
|
||
#endif
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
TerminateEndpointUtilities(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called during infocomm cleanup. This cleans up the global debug trace
|
||
log object
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE if successful
|
||
|
||
--*/
|
||
{
|
||
#if IE_REF_TRACKING
|
||
if ( g_pDbgIERefTraceLog )
|
||
{
|
||
DestroyRefTraceLog( g_pDbgIERefTraceLog );
|
||
g_pDbgIERefTraceLog = NULL;
|
||
}
|
||
#endif
|
||
return TRUE;
|
||
}
|