You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1391 lines
31 KiB
1391 lines
31 KiB
/*++
|
|
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 ) {
|
|
delete pParam;
|
|
pParam = NULL;
|
|
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;
|
|
}
|