/*++ 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 #include #include #include #include #include "inetreg.h" #include #include #include #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; }