Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

6897 lines
172 KiB

/*++
Copyright (c) 1998-2001 Microsoft Corporation
Module Name:
ultdi.cxx
Abstract:
This module implements the TDI/MUX/SSL component.
Author:
Keith Moore (keithmo) 15-Jun-1998
Revision History:
--*/
#include "precomp.h"
#include "repltrace.h"
//
// Private globals.
//
//
// Global lists of all active and all waiting-to-be-deleted endpoints.
//
LIST_ENTRY g_TdiEndpointListHead;
LIST_ENTRY g_TdiDeletedEndpointListHead; // for debugging
ULONG g_TdiEndpointCount; // #elements in active endpoint list
//
// Global lists of all connections, active or idle
//
LIST_ENTRY g_TdiConnectionListHead;
ULONG g_TdiConnectionCount; // #elements in connection list
//
// Spinlock protecting the above lists.
//
UL_SPIN_LOCK g_TdiSpinLock;
//
// Global initialization flag.
//
BOOLEAN g_TdiInitialized;
//
// Used to wait for endpoints and connections to close on shutdown
//
BOOLEAN g_TdiWaitingForEndpointDrain;
KEVENT g_TdiEndpointDrainEvent;
KEVENT g_TdiConnectionDrainEvent;
//
// TCP Send routine if Fast Send is possible.
//
PUL_TCPSEND_DISPATCH g_TcpFastSend = NULL;
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, UlInitializeTdi )
#pragma alloc_text( INIT, UlpQueryTcpFastSend )
#pragma alloc_text( PAGE, UlTerminateTdi )
#pragma alloc_text( PAGE, UlCloseListeningEndpoint )
#pragma alloc_text( PAGE, UlpEndpointCleanupWorker )
#pragma alloc_text( PAGE, UlpConnectionCleanupWorker )
#pragma alloc_text( PAGE, UlpAssociateConnection )
#pragma alloc_text( PAGE, UlpDisassociateConnection )
#pragma alloc_text( PAGE, UlpReplenishEndpoint )
#pragma alloc_text( PAGE, UlpReplenishEndpointWorker )
#pragma alloc_text( PAGE, UlpInitializeConnection )
#pragma alloc_text( PAGE, UlpUrlToAddress )
#pragma alloc_text( PAGE, UlpSetNagling )
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlWaitForEndpointDrain
NOT PAGEABLE -- UlCreateListeningEndpoint
NOT PAGEABLE -- UlCloseConnection
NOT PAGEABLE -- UlReceiveData
NOT PAGEABLE -- UlSendData
NOT PAGEABLE -- UlAddSiteToEndpointList
NOT PAGEABLE -- UlRemoveSiteFromEndpointList
NOT PAGEABLE -- UlpDestroyEndpoint
NOT PAGEABLE -- UlpDestroyConnection
NOT PAGEABLE -- UlpDequeueIdleConnection
NOT PAGEABLE -- UlpEnqueueIdleConnection
NOT PAGEABLE -- UlpEnqueueActiveConnection
NOT PAGEABLE -- UlpConnectHandler
NOT PAGEABLE -- UlpDisconnectHandler
NOT PAGEABLE -- UlpCloseRawConnection
NOT PAGEABLE -- UlpSendRawData
NOT PAGEABLE -- UlpReceiveRawData
NOT PAGEABLE -- UlpReceiveHandler
NOT PAGEABLE -- UlpDummyReceiveHandler
NOT PAGEABLE -- UlpReceiveExpeditedHandler
NOT PAGEABLE -- UlpRestartAccept
NOT PAGEABLE -- UlpRestartSendData
NOT PAGEABLE -- UlpReferenceEndpoint
NOT PAGEABLE -- UlpDereferenceEndpoint
NOT PAGEABLE -- UlReferenceConnection
NOT PAGEABLE -- UlDereferenceConnection
NOT PAGEABLE -- UlpCleanupConnectionId
NOT PAGEABLE -- UlpDecrementIdleConnections
NOT PAGEABLE -- UlpIncrementIdleConnections
NOT PAGEABLE -- UlpClearReplenishScheduledFlag
NOT PAGEABLE -- UlpCreateConnection
NOT PAGEABLE -- UlpSetConnectionFlag
NOT PAGEABLE -- UlpBeginDisconnect
NOT PAGEABLE -- UlpRestartDisconnect
NOT PAGEABLE -- UlpBeginAbort
NOT PAGEABLE -- UlpRestartAbort
NOT PAGEABLE -- UlpRemoveFinalReference
NOT PAGEABLE -- UlpRestartReceive
NOT PAGEABLE -- UlpRestartClientReceive
NOT PAGEABLE -- UlpDisconnectAllActiveConnections
NOT PAGEABLE -- UlpUnbindConnectionFromEndpoint
NOT PAGEABLE -- UlpSynchronousIoComplete
NOT PAGEABLE -- UlpFindEndpointForAddress
NOT PAGEABLE -- UlpRestartQueryAddress
#endif
//
// Public functions.
//
/***************************************************************************++
Routine Description:
Performs global initialization of this module.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlInitializeTdi(
VOID
)
{
NTSTATUS status;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( !g_TdiInitialized );
//
// Initialize global data.
//
InitializeListHead( &g_TdiEndpointListHead );
InitializeListHead( &g_TdiDeletedEndpointListHead );
InitializeListHead( &g_TdiConnectionListHead );
UlInitializeSpinLock( &g_TdiSpinLock, "g_TdiSpinLock" );
g_TdiEndpointCount = 0;
g_TdiConnectionCount = 0;
KeInitializeEvent(
&g_TdiEndpointDrainEvent,
NotificationEvent,
FALSE
);
KeInitializeEvent(
&g_TdiConnectionDrainEvent,
NotificationEvent,
FALSE
);
status = UlpQueryTcpFastSend();
if (NT_SUCCESS(status))
{
g_TdiInitialized = TRUE;
}
return status;
} // UlInitializeTdi
/***************************************************************************++
Routine Description:
Performs global termination of this module.
--***************************************************************************/
VOID
UlTerminateTdi(
VOID
)
{
//
// Sanity check.
//
PAGED_CODE();
if (g_TdiInitialized)
{
ASSERT( IsListEmpty( &g_TdiEndpointListHead )) ;
ASSERT( IsListEmpty( &g_TdiDeletedEndpointListHead )) ;
ASSERT( IsListEmpty( &g_TdiConnectionListHead )) ;
ASSERT( g_TdiEndpointCount == 0 );
ASSERT( g_TdiConnectionCount == 0 );
g_TdiInitialized = FALSE;
}
} // UlTerminateTdi
/***************************************************************************++
Routine Description:
This function blocks until the endpoint list is empty. It also prevents
new endpoints from being created.
Arguments:
None.
--***************************************************************************/
VOID
UlWaitForEndpointDrain(
VOID
)
{
KIRQL oldIrql;
BOOLEAN Wait = FALSE;
if (g_TdiInitialized)
{
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
if (!g_TdiWaitingForEndpointDrain)
{
g_TdiWaitingForEndpointDrain = TRUE;
}
if (g_TdiEndpointCount > 0 || g_TdiConnectionCount > 0)
{
Wait = TRUE;
}
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
if (Wait)
{
PVOID Events[2] = {
&g_TdiEndpointDrainEvent,
&g_TdiConnectionDrainEvent
};
KeWaitForMultipleObjects(
2,
Events,
WaitAll,
UserRequest,
KernelMode,
FALSE,
NULL,
NULL
);
}
}
} // UlWaitForEndpointDrain
/***************************************************************************++
Routine Description:
Creates a new listening endpoint bound to the specified address.
Arguments:
pLocalAddress - Supplies the local address to bind the endpoint to.
LocalAddressLength - Supplies the length of pLocalAddress.
InitialBacklog - Supplies the initial number of idle connections
to add to the endpoint.
pConnectionRequestHandler - Supplies a pointer to an indication
handler to invoke when incoming connections arrive.
pConnectionCompleteHandler - Supplies a pointer to an indication
handler to invoke when either a) the incoming connection is
fully accepted, or b) the incoming connection could not be
accepted due to a fatal error.
pConnectionDisconnectHandler - Supplies a pointer to an indication
handler to invoke when connections are disconnected by the
remote (client) side.
pConnectionDestroyedHandler - Supplies a pointer to an indication
handle to invoke after a connection has been fully destroyed.
This is typically the TDI client's opportunity to cleanup
any allocated resources.
pDataReceiveHandler - Supplies a pointer to an indication handler to
invoke when incoming data arrives.
pListeningContext - Supplies an uninterpreted context value to
associate with the new listening endpoint.
ppListeningEndpoint - Receives a pointer to the new listening
endpoint if successful.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlCreateListeningEndpoint(
IN PTRANSPORT_ADDRESS pLocalAddress,
IN ULONG LocalAddressLength,
IN BOOLEAN Secure,
IN ULONG InitialBacklog,
IN PUL_CONNECTION_REQUEST pConnectionRequestHandler,
IN PUL_CONNECTION_COMPLETE pConnectionCompleteHandler,
IN PUL_CONNECTION_DISCONNECT pConnectionDisconnectHandler,
IN PUL_CONNECTION_DISCONNECT_COMPLETE pConnectionDisconnectCompleteHandler,
IN PUL_CONNECTION_DESTROYED pConnectionDestroyedHandler,
IN PUL_DATA_RECEIVE pDataReceiveHandler,
IN PVOID pListeningContext,
OUT PUL_ENDPOINT *ppListeningEndpoint
)
{
NTSTATUS status;
PUL_ENDPOINT pEndpoint;
UNICODE_STRING deviceName;
LONG i;
//
// Sanity check.
//
ASSERT( LocalAddressLength == sizeof(TA_IP_ADDRESS) );
ASSERT( InitialBacklog < 0x7FFFFFFF );
//
// Setup locals so we know how to cleanup on a fatal exit.
//
pEndpoint = NULL;
//
// Allocate enough pool for the endpoint structure and the
// local address.
//
pEndpoint = UL_ALLOCATE_STRUCT_WITH_SPACE(
NonPagedPool,
UL_ENDPOINT,
LocalAddressLength,
UL_ENDPOINT_POOL_TAG
);
if (pEndpoint == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fatal;
}
InterlockedIncrement((PLONG) &g_TdiEndpointCount);
//
// Initialize the easy parts.
//
pEndpoint->Signature = UL_ENDPOINT_SIGNATURE;
pEndpoint->ReferenceCount = 0;
pEndpoint->UsageCount = 1;
#if ENABLE_OWNER_REF_TRACE
pEndpoint->pEndpointRefOwner = NULL;
CREATE_OWNER_REF_TRACE_LOG( pEndpoint->pOwnerRefTraceLog, 8000, 0 );
#endif // ENABLE_OWNER_REF_TRACE
REFERENCE_ENDPOINT_SELF(pEndpoint, REF_ACTION_INIT);
ExInitializeSListHead( &pEndpoint->IdleConnectionSListHead );
for (i = 0; i < DEFAULT_MAX_CONNECTION_ACTIVE_LISTS; i++)
{
InitializeListHead( &pEndpoint->ActiveConnectionListHead[i] );
UlInitializeSpinLock(
&pEndpoint->ActiveConnectionSpinLock[i],
"ActiveConnectionSpinLock"
);
}
pEndpoint->ActiveConnectionIndex = 0;
UlInitializeSpinLock( &pEndpoint->IdleConnectionSpinLock, "IdleConnectionSpinLock" );
UlInitializeSpinLock( &pEndpoint->EndpointSpinLock, "EndpointSpinLock" );
pEndpoint->AddressObject.Handle = NULL;
pEndpoint->AddressObject.pFileObject = NULL;
pEndpoint->AddressObject.pDeviceObject = NULL;
pEndpoint->pConnectionRequestHandler = pConnectionRequestHandler;
pEndpoint->pConnectionCompleteHandler = pConnectionCompleteHandler;
pEndpoint->pConnectionDisconnectHandler = pConnectionDisconnectHandler;
pEndpoint->pConnectionDisconnectCompleteHandler = pConnectionDisconnectCompleteHandler;
pEndpoint->pConnectionDestroyedHandler = pConnectionDestroyedHandler;
pEndpoint->pDataReceiveHandler = pDataReceiveHandler;
pEndpoint->pListeningContext = pListeningContext;
pEndpoint->pLocalAddress = (PTRANSPORT_ADDRESS)(pEndpoint + 1);
pEndpoint->LocalAddressLength = LocalAddressLength;
RtlCopyMemory(
pEndpoint->pLocalAddress,
pLocalAddress,
LocalAddressLength
);
pEndpoint->Secure = Secure;
pEndpoint->Deleted = FALSE;
pEndpoint->GlobalEndpointListEntry.Flink = NULL;
pEndpoint->EndpointSynch.ReplenishScheduled = TRUE;
pEndpoint->EndpointSynch.IdleConnections = 0;
RtlZeroMemory(
&pEndpoint->CleanupIrpContext,
sizeof(UL_IRP_CONTEXT)
);
pEndpoint->CleanupIrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
//
// Open the TDI address object for this endpoint.
//
status = UxOpenTdiAddressObject(
pLocalAddress,
LocalAddressLength,
&pEndpoint->AddressObject
);
if (!NT_SUCCESS(status))
{
goto fatal;
}
//
// Set the TDI event handlers.
//
status = UxSetEventHandler(
&pEndpoint->AddressObject,
TDI_EVENT_CONNECT,
&UlpConnectHandler,
pEndpoint
);
if (!NT_SUCCESS(status))
{
goto fatal;
}
status = UxSetEventHandler(
&pEndpoint->AddressObject,
TDI_EVENT_DISCONNECT,
&UlpDisconnectHandler,
pEndpoint
);
if (!NT_SUCCESS(status))
{
goto fatal;
}
status = UxSetEventHandler(
&pEndpoint->AddressObject,
TDI_EVENT_RECEIVE,
&UlpReceiveHandler,
pEndpoint
);
if (!NT_SUCCESS(status))
{
goto fatal;
}
status = UxSetEventHandler(
&pEndpoint->AddressObject,
TDI_EVENT_RECEIVE_EXPEDITED,
&UlpReceiveExpeditedHandler,
pEndpoint
);
//
// Put the endpoint onto the global list.
//
ExInterlockedInsertTailList(
&g_TdiEndpointListHead,
&pEndpoint->GlobalEndpointListEntry,
KSPIN_LOCK_FROM_UL_SPIN_LOCK(&g_TdiSpinLock)
);
//
// Replenish the idle connection pool.
//
UlpReplenishEndpoint( pEndpoint );
//
// Success!
//
UlTrace(TDI, (
"UlCreateListeningEndpoint: endpoint %p, addrobj %p\n",
pEndpoint,
pEndpoint->AddressObject.Handle
));
*ppListeningEndpoint = pEndpoint;
return STATUS_SUCCESS;
fatal:
ASSERT( !NT_SUCCESS(status) );
if (pEndpoint != NULL)
{
//
// Release the one-and-only reference on the endpoint, which
// will cause it to destroy itself. Done this way to keep
// the ownerref tracelogging happy, as it will complain if
// the refcount doesn't drop to zero.
//
ASSERT(1 == pEndpoint->ReferenceCount);
DEREFERENCE_ENDPOINT_SELF(pEndpoint, REF_ACTION_FINAL_DEREF);
}
return status;
} // UlCreateListeningEndpoint
/***************************************************************************++
Routine Description:
Closes an existing listening endpoint.
Arguments:
pListeningEndpoint - Supplies a pointer to a listening endpoint
previously created with UlCreateListeningEndpoint().
pCompletionRoutine - Supplies a pointer to a completion routine to
invoke after the listening endpoint is fully closed.
pCompletionContext - Supplies an uninterpreted context value for the
completion routine.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlCloseListeningEndpoint(
IN PUL_ENDPOINT pListeningEndpoint,
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
IN PVOID pCompletionContext
)
{
PUL_IRP_CONTEXT pIrpContext;
NTSTATUS status;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( IS_VALID_ENDPOINT( pListeningEndpoint ) );
ASSERT( pCompletionRoutine != NULL );
UlTrace(TDI, (
"UlCloseListeningEndpoint: endpoint %p, completion %p, ctx %p\n",
pListeningEndpoint,
pCompletionRoutine,
pCompletionContext
));
pIrpContext = &pListeningEndpoint->CleanupIrpContext;
pIrpContext->pCompletionRoutine = pCompletionRoutine;
pIrpContext->pCompletionContext = pCompletionContext;
pIrpContext->pOwnIrp = NULL;
//
// Let UlpDisconnectAllActiveConnections do the dirty work.
//
status = UlpDisconnectAllActiveConnections( pListeningEndpoint );
return status;
} // UlCloseListeningEndpoint
/***************************************************************************++
Routine Description:
Closes a previously accepted connection.
Arguments:
pConnection - Supplies a pointer to a connection as previously
indicated to the PUL_CONNECTION_REQUEST handler.
AbortiveDisconnect - Supplies TRUE if the connection is to be abortively
disconnected, FALSE if it should be gracefully disconnected.
pCompletionRoutine - Supplies a pointer to a completion routine to
invoke after the connection is fully closed.
pCompletionContext - Supplies an uninterpreted context value for the
completion routine.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlCloseConnection(
IN PVOID pObject,
IN BOOLEAN AbortiveDisconnect,
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
IN PVOID pCompletionContext
)
{
NTSTATUS status;
PUL_CONNECTION pConnection = (PUL_CONNECTION) pObject;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
UlTrace(TDI, (
"UlCloseConnection: connection %p, abort %lu\n",
pConnection,
(ULONG)AbortiveDisconnect
));
WRITE_REF_TRACE_LOG2(
g_pTdiTraceLog,
pConnection->pTraceLog,
(AbortiveDisconnect ?
REF_ACTION_CLOSE_UL_CONNECTION_ABORTIVE :
REF_ACTION_CLOSE_UL_CONNECTION_GRACEFUL),
pConnection->ReferenceCount,
pConnection,
__FILE__,
__LINE__
);
//
// We only send graceful disconnects through the filter
// process. There's also no point in going through the
// filter if the connection is already being closed or
// aborted.
//
if (pConnection->FilterInfo.pFilterChannel &&
!pConnection->ConnectionFlags.CleanupBegun &&
!pConnection->ConnectionFlags.DisconnectIndicated &&
!pConnection->ConnectionFlags.AbortIndicated &&
!AbortiveDisconnect)
{
//
// Send graceful disconnect through the filter process.
//
status = UlFilterCloseHandler(
&pConnection->FilterInfo,
pCompletionRoutine,
pCompletionContext
);
}
else
{
//
// Really close the connection.
//
status = UlpCloseRawConnection(
pConnection,
AbortiveDisconnect,
pCompletionRoutine,
pCompletionContext
);
}
return status;
} // UlCloseConnection
/***************************************************************************++
Routine Description:
Sends a block of data on the specified connection. If the connection
is filtered, the data will be sent to the filter first.
Arguments:
pConnection - Supplies a pointer to a connection as previously
indicated to the PUL_CONNECTION_REQUEST handler.
pMdlChain - Supplies a pointer to a MDL chain describing the
data buffers to send.
Length - Supplies the length of the data referenced by the MDL
chain.
pCompletionRoutine - Supplies a pointer to a completion routine to
invoke after the data is sent.
pCompletionContext - Supplies an uninterpreted context value for the
completion routine.
InitiateDisconnect - Supplies TRUE if a graceful disconnect should
be initiated immediately after initiating the send (i.e. before
the send actually completes).
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlSendData(
IN PUL_CONNECTION pConnection,
IN PMDL pMdlChain,
IN ULONG Length,
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
IN PVOID pCompletionContext,
IN PIRP pOwnIrp,
IN PUL_IRP_CONTEXT pOwnIrpContext,
IN BOOLEAN InitiateDisconnect
)
{
NTSTATUS status;
PUL_IRP_CONTEXT pIrpContext;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pMdlChain != NULL );
ASSERT( Length > 0 );
ASSERT( pCompletionRoutine != NULL );
UlTrace(TDI, (
"UlSendData: connection %p, mdl %p, length %lu\n",
pConnection,
pMdlChain,
Length
));
//
// Connection should be around until we make a call
// to close connection. See below.
//
REFERENCE_CONNECTION( pConnection );
//
// Allocate & initialize a context structure if necessary.
//
if (pOwnIrpContext == NULL)
{
pIrpContext = UlPplAllocateIrpContext();
}
else
{
ASSERT( pOwnIrp != NULL );
pIrpContext = pOwnIrpContext;
}
if (pIrpContext == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fatal;
}
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
pIrpContext->pConnectionContext = (PVOID)pConnection;
pIrpContext->pCompletionRoutine = pCompletionRoutine;
pIrpContext->pCompletionContext = pCompletionContext;
pIrpContext->pOwnIrp = pOwnIrp;
//
// Try to send the data. This send operation may complete inline
// fast, if the connection has already been aborted by the client
// In that case connection may gone away. To prevent this we
// keep additional refcount until we make a call to close connection
// below.
//
if (pConnection->FilterInfo.pFilterChannel)
{
//
// First go through the filter.
//
status = UlFilterSendHandler(
&pConnection->FilterInfo,
pMdlChain,
Length,
pIrpContext
);
UlTrace(TDI, (
"UlSendData: sent filtered data, status = 0x%x\n",
status
));
ASSERT(status == STATUS_PENDING);
if (pIrpContext != NULL && pIrpContext != pOwnIrpContext)
{
UlPplFreeIrpContext( pIrpContext );
}
}
else
{
//
// Just send it directly to the network.
//
status = UlpSendRawData(
pConnection,
pMdlChain,
Length,
pIrpContext
);
UlTrace(TDI, (
"UlSendData: sent raw data, status = 0x%x\n",
status
));
}
if (!NT_SUCCESS(status))
{
goto fatal;
}
//
// Now that the send is "in flight", initiate a disconnect if
// so requested.
//
// CODEWORK: Investigate the new-for-NT5 TDI_SEND_AND_DISCONNECT flag.
//
if (InitiateDisconnect)
{
WRITE_REF_TRACE_LOG2(
g_pTdiTraceLog,
pConnection->pTraceLog,
REF_ACTION_CLOSE_UL_CONNECTION_GRACEFUL,
pConnection->ReferenceCount,
pConnection,
__FILE__,
__LINE__
);
(VOID)UlCloseConnection(
pConnection,
FALSE, // AbortiveDisconnect
NULL, // pCompletionRoutine
NULL // pCompletionContext
);
UlTrace(TDI, (
"UlSendData: closed conn\n"
));
}
DEREFERENCE_CONNECTION( pConnection );
return STATUS_PENDING;
fatal:
ASSERT( !NT_SUCCESS(status) );
if (pIrpContext != NULL && pIrpContext != pOwnIrpContext)
{
UlPplFreeIrpContext( pIrpContext );
}
(VOID)UlpCloseRawConnection(
pConnection,
TRUE, // AbortiveDisconnect
NULL, // pCompletionRoutine
NULL // pCompletionContext
);
UlTrace(TDI, (
"UlSendData: error occurred; closed raw conn\n"
));
status = UlInvokeCompletionRoutine(
status,
0,
pCompletionRoutine,
pCompletionContext
);
UlTrace(TDI, (
"UlSendData: finished completion routine: status = 0x%x\n",
status
));
DEREFERENCE_CONNECTION( pConnection );
return status;
} // UlSendData
/***************************************************************************++
Routine Description:
Receives data from the specified connection. This function is
typically used after a receive indication handler has failed to
consume all of the indicated data.
If the connection is filtered the data will be read from the
filter channel.
Arguments:
pConnection - Supplies a pointer to a connection as previously
indicated to the PUL_CONNECTION_REQUEST handler.
pBuffer - Supplies a pointer to the target buffer for the received
data.
BufferLength - Supplies the length of pBuffer.
pCompletionRoutine - Supplies a pointer to a completion routine to
invoke after the listening endpoint is fully closed.
pCompletionContext - Supplies an uninterpreted context value for the
completion routine.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlReceiveData(
IN PVOID pConnectionContext,
IN PVOID pBuffer,
IN ULONG BufferLength,
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
IN PVOID pCompletionContext
)
{
NTSTATUS status;
PUL_CONNECTION pConnection = (PUL_CONNECTION)pConnectionContext;
//
// Sanity check.
//
ASSERT(IS_VALID_CONNECTION(pConnection));
if (pConnection->FilterInfo.pFilterChannel)
{
//
// This is a filtered connection, get the data from the
// filter.
//
status = UlFilterReadHandler(
&pConnection->FilterInfo,
(PBYTE)pBuffer,
BufferLength,
pCompletionRoutine,
pCompletionContext
);
}
else
{
//
// This is not a filtered connection. Get the data from
// TDI.
//
status = UlpReceiveRawData(
pConnectionContext,
pBuffer,
BufferLength,
pCompletionRoutine,
pCompletionContext
);
}
return status;
} // UlReceiveData
/***************************************************************************++
Routine Description:
Either create a new endpoint for the specified address or, if one
already exists, reference it.
Arguments:
pSiteUrl - Supplies the URL specifying the site to add.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlAddSiteToEndpointList(
IN PWSTR pSiteUrl
)
{
NTSTATUS status;
TA_IP_ADDRESS address;
BOOLEAN secure;
PUL_ENDPOINT pEndpoint;
KIRQL oldIrql;
//
// N.B. pSiteUrl is paged and cannot be manipulated with the
// spinlock held. Even though this routine cannot be pageable
// (due to the spinlock aquisition), it must be called at
// low IRQL.
//
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
UlTrace(SITE, (
"UlAddSiteToEndpointList: URL = %ws\n",
pSiteUrl
));
//
// Convert the string into an address.
//
status = UlpUrlToAddress(pSiteUrl, &address, &secure);
if (!NT_SUCCESS(status))
{
goto cleanup;
}
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
//
// make sure we're not shutting down
//
if (g_TdiWaitingForEndpointDrain) {
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
status = STATUS_DEVICE_BUSY;
goto cleanup;
}
//
// Find an existing endpoint for this address.
//
pEndpoint = UlpFindEndpointForAddress(
(PTRANSPORT_ADDRESS)&address,
sizeof(address)
);
//
// Did we find one?
//
if (pEndpoint == NULL)
{
//
// Didn't find it. Try to create one. Since we must release
// the TDI spinlock before we can create a new listening endpoint,
// there is the opportunity for a race condition with other
// threads creating endpoints.
//
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
UlTrace(SITE, (
"UlAddSiteToEndpointList: no site for %ws, creating\n",
pSiteUrl
));
status = UlCreateListeningEndpoint(
(PTRANSPORT_ADDRESS)&address, // pLocalAddress
sizeof(address), // LocalAddressLength
secure, // Secure (SSL) endpoint?
g_UlMinIdleConnections, // InitialBacklog
&UlConnectionRequest, // callback functions
&UlConnectionComplete,
&UlConnectionDisconnect,
&UlConnectionDisconnectComplete,
&UlConnectionDestroyed,
&UlHttpReceive,
NULL, // pListeningContext
&pEndpoint
);
if (!NT_SUCCESS(status))
{
//
// Maybe another thread has already created it?
//
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
pEndpoint = UlpFindEndpointForAddress(
(PTRANSPORT_ADDRESS)&address,
sizeof(address)
);
if (pEndpoint != NULL)
{
//
// Adjust the usage count.
//
pEndpoint->UsageCount++;
ASSERT( pEndpoint->UsageCount > 0 );
status = STATUS_SUCCESS;
}
//
// The endpoint doesn't exist. This is a "real" failure.
//
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
}
}
else
{
//
// Adjust the usage count.
//
pEndpoint->UsageCount++;
ASSERT( pEndpoint->UsageCount > 0 );
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
}
UlTrace(SITE, (
"UlAddSiteToEndpointList: using endpoint %p for URL %ws\n",
pEndpoint,
pSiteUrl
));
cleanup:
RETURN(status);
} // UlAddSiteToEndpointList
/***************************************************************************++
Routine Description:
Dereference the endpoint corresponding to the specified address.
Arguments:
pSiteUrl - Supplies the URL specifying the site to remove.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlRemoveSiteFromEndpointList(
IN PWSTR pSiteUrl
)
{
NTSTATUS status;
TA_IP_ADDRESS address;
BOOLEAN secure;
PUL_ENDPOINT pEndpoint;
KIRQL oldIrql;
BOOLEAN spinlockHeld = FALSE;
UL_STATUS_BLOCK ulStatus;
//
// N.B. pSiteUrl is paged and cannot be manipulated with the
// spinlock held. Even though this routine cannot be pageable
// (due to the spinlock aquisition), it must be called at
// low IRQL.
//
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
UlTrace(SITE, (
"UlRemoveSiteFromEndpointList: URL = %ws\n",
pSiteUrl
));
//
// Convert the string into an address.
//
status = UlpUrlToAddress(pSiteUrl, &address, &secure);
if (!NT_SUCCESS(status))
{
goto cleanup;
}
//
// Find an existing endpoint for this address.
//
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
spinlockHeld = TRUE;
pEndpoint = UlpFindEndpointForAddress(
(PTRANSPORT_ADDRESS)&address,
sizeof(address)
);
//
// Did we find one?
//
if (pEndpoint == NULL)
{
//
// Ideally, this should never happen.
//
status = STATUS_NOT_FOUND;
goto cleanup;
}
//
// Adjust the usage count. If it drops to zero, blow away the
// endpoint.
//
ASSERT( pEndpoint->UsageCount > 0 );
pEndpoint->UsageCount--;
if (pEndpoint->UsageCount == 0)
{
//
// We can't call UlCloseListeningEndpoint() with the TDI spinlock
// held. If the endpoint is still on the global list, then go
// ahead and remove it now, release the TDI spinlock, and then
// close the endpoint.
//
if (! pEndpoint->Deleted)
{
ASSERT(NULL != pEndpoint->GlobalEndpointListEntry.Flink);
RemoveEntryList( &pEndpoint->GlobalEndpointListEntry );
InsertTailList(
&g_TdiDeletedEndpointListHead,
&pEndpoint->GlobalEndpointListEntry
);
pEndpoint->Deleted = TRUE;
}
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
spinlockHeld = FALSE;
UlTrace(SITE, (
"UlRemoveSiteFromEndpointList: closing endpoint %p for URL %ws\n",
pEndpoint,
pSiteUrl
));
//
// Initialize a status block. We'll pass a pointer to this as
// the completion context to UlCloseListeningEndpoint(). The
// completion routine will update the status block and signal
// the event.
//
UlInitializeStatusBlock( &ulStatus );
status = UlCloseListeningEndpoint(
pEndpoint,
&UlpSynchronousIoComplete,
&ulStatus
);
if (status == STATUS_PENDING)
{
//
// Wait for it to finish.
//
UlWaitForStatusBlockEvent( &ulStatus );
//
// Retrieve the updated status.
//
status = ulStatus.IoStatus.Status;
}
}
cleanup:
if (spinlockHeld)
{
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
}
#if DBG
if (status == STATUS_NOT_FOUND)
{
UlTrace(SITE, (
"UlRemoveSiteFromEndpointList: cannot find endpoint for URL %ws\n",
pSiteUrl
));
}
#endif
RETURN(status);
} // UlRemoveSiteFromEndpointList
//
// Private functions.
//
/***************************************************************************++
Routine Description:
Destroys all resources allocated to an endpoint, including the
endpoint structure itself.
Arguments:
pEndpoint - Supplies the endpoint to destroy.
--***************************************************************************/
VOID
UlpDestroyEndpoint(
IN PUL_ENDPOINT pEndpoint
)
{
PUL_IRP_CONTEXT pIrpContext;
PUL_CONNECTION pConnection;
ULONG EndpointCount;
KIRQL oldIrql;
//
// Sanity check.
//
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
ASSERT(0 == pEndpoint->ReferenceCount);
UlTrace(TDI, (
"UlpDestroyEndpoint: endpoint %p\n",
pEndpoint
));
//
// Purge the idle queue.
//
for (;;)
{
pConnection = UlpDequeueIdleConnection( pEndpoint, FALSE );
if (pConnection == NULL )
{
break;
}
ASSERT( IS_VALID_CONNECTION( pConnection ) );
if (pConnection->FilterInfo.pFilterChannel)
{
HTTP_RAW_CONNECTION_ID ConnectionId;
ConnectionId = pConnection->FilterInfo.ConnectionId;
HTTP_SET_NULL_ID( &pConnection->FilterInfo.ConnectionId );
if (! HTTP_IS_NULL_ID( &ConnectionId ))
{
UlFreeOpaqueId(ConnectionId, UlOpaqueIdTypeRawConnection);
ASSERT( pConnection->ReferenceCount >= 2 );
DEREFERENCE_CONNECTION(pConnection);
}
}
UlpDestroyConnection( pConnection );
}
//
// Invoke the completion routine in the IRP context if specified.
//
pIrpContext = &pEndpoint->CleanupIrpContext;
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
if (pIrpContext->pCompletionRoutine != NULL)
{
(pIrpContext->pCompletionRoutine)(
pIrpContext->pCompletionContext,
STATUS_SUCCESS,
0
);
}
//
// Close the TDI object.
//
UxCloseTdiObject( &pEndpoint->AddressObject );
//
// Remove the endpoint from g_TdiDeletedEndpointListHead
//
ASSERT( pEndpoint->Deleted );
ASSERT( NULL != pEndpoint->GlobalEndpointListEntry.Flink );
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
RemoveEntryList( &pEndpoint->GlobalEndpointListEntry );
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
//
// Zap the owner ref trace log
//
DESTROY_OWNER_REF_TRACE_LOG(pEndpoint->pOwnerRefTraceLog);
//
// Free the endpoint structure.
//
pEndpoint->Signature = UL_ENDPOINT_SIGNATURE_X;
UL_FREE_POOL( pEndpoint, UL_ENDPOINT_POOL_TAG );
//
// Decrement the global endpoint count.
//
EndpointCount = InterlockedDecrement((PLONG) &g_TdiEndpointCount);
if (g_TdiWaitingForEndpointDrain && (EndpointCount == 0))
{
KeSetEvent(&g_TdiEndpointDrainEvent, 0, FALSE);
}
} // UlpDestroyEndpoint
/***************************************************************************++
Routine Description:
Destroys all resources allocated to an connection, including the
connection structure itself.
Arguments:
pConnection - Supplies the connection to destroy.
--***************************************************************************/
VOID
UlpDestroyConnection(
IN PUL_CONNECTION pConnection
)
{
KIRQL OldIrql;
LONG ConnectionCount;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pConnection->ActiveListEntry.Flink == NULL );
ASSERT( pConnection->IdleSListEntry.Next == NULL );
UlTrace(TDI, (
"UlpDestroyConnection: connection %p\n",
pConnection
));
//
// Release the filter channel if we still have a ref.
// This only happens when we destroy idle connections.
//
if (pConnection->FilterInfo.pFilterChannel)
{
ASSERT(pConnection->FilterInfo.ConnState == UlFilterConnStateInactive);
DEREFERENCE_FILTER_CHANNEL(pConnection->FilterInfo.pFilterChannel);
pConnection->FilterInfo.pFilterChannel = NULL;
}
// If OpaqueId is non-zero, then refCount should not be zero
ASSERT(HTTP_IS_NULL_ID(&pConnection->FilterInfo.ConnectionId));
#if INVESTIGATE_LATER
//
// Free the filter connection ID
//
UlFreeOpaqueId(
pConnection->FilterInfo.ConnectionId,
UlOpaqueIdTypeRawConnection);
#endif
DESTROY_REF_TRACE_LOG( pConnection->pTraceLog );
DESTROY_REF_TRACE_LOG( pConnection->HttpConnection.pTraceLog );
//
// Close the TDI object.
//
UxCloseTdiObject( &pConnection->ConnectionObject );
//
// Free the accept IRP.
//
if (pConnection->pIrp != NULL)
{
UlFreeIrp( pConnection->pIrp );
}
//
// Remove from global list of connections
//
UlAcquireSpinLock( &g_TdiSpinLock, &OldIrql );
RemoveEntryList( &pConnection->GlobalConnectionListEntry );
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
ConnectionCount = InterlockedDecrement((PLONG) &g_TdiConnectionCount);
//
// Free the connection structure.
//
pConnection->Signature = UL_CONNECTION_SIGNATURE_X;
UL_FREE_POOL( pConnection, UL_CONNECTION_POOL_TAG );
// allow us to shut down
if (g_TdiWaitingForEndpointDrain && (ConnectionCount == 0))
{
KeSetEvent(&g_TdiConnectionDrainEvent, 0, FALSE);
}
} // UlpDestroyConnection
/***************************************************************************++
Routine Description:
Dequeues an idle connection from the specified endpoint.
Arguments:
pEndpoint - Supplies the endpoint to dequeue from.
ScheduleReplenish - Supplies TRUE if a replenishment of the
idle connection list should be scheduled.
Return Value:
PUL_CONNECTION - Pointer to an idle connection is successful,
NULL otherwise.
--***************************************************************************/
PUL_CONNECTION
UlpDequeueIdleConnection(
IN PUL_ENDPOINT pEndpoint,
IN BOOLEAN ScheduleReplenish
)
{
PSINGLE_LIST_ENTRY pSListEntry;
PUL_CONNECTION pConnection;
BOOLEAN ReplenishNeeded;
//
// Sanity check.
//
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// Pop an entry off the list.
//
pSListEntry = ExInterlockedPopEntrySList(
&pEndpoint->IdleConnectionSListHead,
KSPIN_LOCK_FROM_UL_SPIN_LOCK(&pEndpoint->IdleConnectionSpinLock)
);
if (pSListEntry != NULL)
{
pConnection = CONTAINING_RECORD(
pSListEntry,
UL_CONNECTION,
IdleSListEntry
);
pConnection->IdleSListEntry.Next = NULL;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT(pConnection->ConnectionFlags.Value == 0);
if ( pConnection->FilterInfo.pFilterChannel )
{
//
// If the idle connection has filter attached on it, it will have
// an additional refcount because of the opaque id assigned to the
// ul_connection, filter API uses this id to communicate with the
// filter app through various IOCTLs.
//
ASSERT( 2 == pConnection->ReferenceCount );
}
else
{
//
// As long as the connection doesn get destroyed it will sit
// in the idle list with one refcount on it.
//
ASSERT( 1 == pConnection->ReferenceCount );
}
SET_OWNER_REF_TRACE_LOG_MONOTONIC_ID(
pConnection->MonotonicId,
pConnection->pOwningEndpoint->pOwnerRefTraceLog);
//
// See if we need to generate more connections.
//
ReplenishNeeded = UlpDecrementIdleConnections(pEndpoint);
}
else
{
//
// The idle list is empty. However, we should not schedule
// a replenish at this time. The driver's init code ensures
// that the min idle connections for an endpoint is always
// at least two, so the thread that decremented the counter
// to one will have scheduled a replenish by now anyway,
// and the replenish will continue adding connections until
// there are at least the min number of connections.
//
ReplenishNeeded = FALSE;
pConnection = NULL;
}
//
// Schedule a replenish if necessary.
//
if (ReplenishNeeded && ScheduleReplenish)
{
TRACE_REPLENISH(
pEndpoint,
DummySynch,
pEndpoint->EndpointSynch,
REPLENISH_ACTION_QUEUE_REPLENISH
);
//
// Add a reference to the endpoint to ensure that it doesn't
// disappear from under us. UlpReplenishEndpointWorker will
// remove the reference once it's finished.
//
REFERENCE_ENDPOINT_SELF(pEndpoint, REF_ACTION_REPLENISH);
UL_QUEUE_WORK_ITEM(
&pEndpoint->WorkItem,
&UlpReplenishEndpointWorker
);
}
return pConnection;
} // UlpDequeueIdleConnection
/***************************************************************************++
Routine Description:
Enqueues an idle connection onto the specified endpoint.
Arguments:
pConnection - Supplies the connection to enqueue.
Replinishing - TRUE if the connection is being added as part
of a replenish.
Return values:
Returns TRUE if the number of connections on the queue is still less
than the minimum required.
--***************************************************************************/
BOOLEAN
UlpEnqueueIdleConnection(
IN PUL_CONNECTION pConnection,
IN BOOLEAN Replenishing
)
{
PUL_ENDPOINT pEndpoint;
BOOLEAN ContinueReplenish;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
pEndpoint = pConnection->pOwningEndpoint;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
ASSERT(pConnection->ConnectionFlags.Value == 0);
ASSERT(pConnection->ActiveListEntry.Flink == NULL);
// The idle list holds a reference; the filter channel (if it exists)
// holds another reference.
ASSERT(pConnection->ReferenceCount ==
1 + (pConnection->FilterInfo.pFilterChannel != NULL));
//
// Increment the count of connections and see if we need
// to continue the replenish. We need to increment before
// we actually put the item on the list because otherwise
// someone else might pull the entry off and decrement the
// count below zero before we increment. There is no harm
// in the count being temporarily higher than the actual
// size of the list.
//
ContinueReplenish = UlpIncrementIdleConnections(
pEndpoint,
Replenishing
);
//
// Push it onto the list.
//
ExInterlockedPushEntrySList(
&pEndpoint->IdleConnectionSListHead,
&pConnection->IdleSListEntry,
KSPIN_LOCK_FROM_UL_SPIN_LOCK(&pEndpoint->IdleConnectionSpinLock)
);
return ContinueReplenish;
} // UlpEnqueueIdleConnection
/***************************************************************************++
Routine Description:
Enqueues an active connection onto the specified endpoint.
Arguments:
pConnection - Supplies the connection to enqueue.
--***************************************************************************/
VOID
UlpEnqueueActiveConnection(
IN PUL_CONNECTION pConnection
)
{
PUL_ENDPOINT pEndpoint;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT(pConnection->IdleSListEntry.Next == NULL);
pEndpoint = pConnection->pOwningEndpoint;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// Append it to the list.
//
REFERENCE_CONNECTION(pConnection);
ExInterlockedInsertHeadList(
&pEndpoint->ActiveConnectionListHead[pConnection->ActiveListIndex],
&pConnection->ActiveListEntry,
KSPIN_LOCK_FROM_UL_SPIN_LOCK(&pEndpoint->ActiveConnectionSpinLock[pConnection->ActiveListIndex])
);
} // UlpEnqueueActiveConnection
/***************************************************************************++
Routine Description:
Handler for incoming connections.
Arguments:
pTdiEventContext - Supplies the context associated with the address
object. This should be a PUL_ENDPOINT.
RemoteAddressLength - Supplies the length of the remote (client-
side) address.
pRemoteAddress - Supplies a pointer to the remote address as
stored in a TRANSPORT_ADDRESS structure.
UserDataLength - Optionally supplies the length of any connect
data associated with the connection request.
pUserData - Optionally supplies a pointer to any connect data
associated with the connection request.
OptionsLength - Optionally supplies the length of any connect
options associated with the connection request.
pOptions - Optionally supplies a pointer to any connect options
associated with the connection request.
pConnectionContext - Receives the context to associate with this
connection. We'll always use a PUL_CONNECTION as the context.
pAcceptIrp - Receives an IRP that will be completed by the transport
when the incoming connection is fully accepted.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpConnectHandler(
IN PVOID pTdiEventContext,
IN LONG RemoteAddressLength,
IN PVOID pRemoteAddress,
IN LONG UserDataLength,
IN PVOID pUserData,
IN LONG OptionsLength,
IN PVOID pOptions,
OUT CONNECTION_CONTEXT *pConnectionContext,
OUT PIRP *pAcceptIrp
)
{
NTSTATUS status;
BOOLEAN result;
PUL_ENDPOINT pEndpoint;
PUL_CONNECTION pConnection;
PUX_TDI_OBJECT pTdiObject;
PIRP pIrp;
BOOLEAN handlerCalled;
TRANSPORT_ADDRESS UNALIGNED *TAList;
PTA_ADDRESS TA;
UL_ENTER_DRIVER("UlpConnectHandler", NULL);
//
// Sanity check.
//
pEndpoint = (PUL_ENDPOINT)pTdiEventContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
UlTrace(TDI,("UlpConnectHandler: endpoint %p\n", pTdiEventContext));
//
// Setup locals so we know how to cleanup on fatal exit.
//
pConnection = NULL;
handlerCalled = FALSE;
//
// make sure that we are not in the process of destroying this
// endpoint. UlRemoveSiteFromEndpointList will do that and
// start the cleanup process when UsageCount hits 0.
//
if (pEndpoint->UsageCount == 0)
{
status = STATUS_CONNECTION_REFUSED;
goto fatal;
}
//
// Try to pull an idle connection from the endpoint.
//
for (;;)
{
pConnection = UlpDequeueIdleConnection( pEndpoint, TRUE );
if (pConnection == NULL )
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fatal;
}
ASSERT( IS_VALID_CONNECTION( pConnection ) );
//
// Establish a referenced pointer from the connection back
// to the endpoint.
//
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
REFERENCE_ENDPOINT_CONNECTION(
pEndpoint,
REF_ACTION_CONNECT,
pConnection
);
//
// Make sure the filter settings are up to date.
//
if (UlValidateFilterChannel(
pConnection->FilterInfo.pFilterChannel,
pConnection->FilterInfo.SecureConnection
))
{
//
// We found a good connection.
// Break out of the loop and go on.
//
break;
}
//
// This connection doesn't have up to date filter
// settings. Destroy it and get a new connection.
//
// Grab a reference. Worker will deref it.
REFERENCE_CONNECTION( pConnection );
UL_CALL_PASSIVE(
&pConnection->WorkItem,
&UlpCleanupEarlyConnection
);
}
//
// We should have a good connection now.
//
ASSERT(IS_VALID_CONNECTION(pConnection));
pTdiObject = &pConnection->ConnectionObject;
//
// Store the remote address in the connection.
//
TAList = (TRANSPORT_ADDRESS UNALIGNED *) pRemoteAddress;
TA = (PTA_ADDRESS) TAList->Address;
if (TDI_ADDRESS_TYPE_IP == TA->AddressType) {
if (TA->AddressLength >= TDI_ADDRESS_LENGTH_IP) {
TDI_ADDRESS_IP UNALIGNED * ValidAddr = (TDI_ADDRESS_IP UNALIGNED *) TA->Address;
pConnection->RemoteAddress = SWAP_LONG(ValidAddr->in_addr);
pConnection->RemotePort = SWAP_SHORT(ValidAddr->sin_port);
}
} else {
// Add support for IP6 here
}
//
// Invoke the client's handler to see if they can accept
// this connection. If they refuse it, bail.
//
result = (pEndpoint->pConnectionRequestHandler)(
pEndpoint->pListeningContext,
pConnection,
(PTRANSPORT_ADDRESS)(pRemoteAddress),
RemoteAddressLength,
&pConnection->pConnectionContext
);
if (!result)
{
status = STATUS_CONNECTION_REFUSED;
goto fatal;
}
//
// Remember that we've called the handler. If we hit a fatal
// condition (say, out of memory) after this point, we'll
// fake a "failed connection complete" indication to the client
// so they can cleanup their state.
//
handlerCalled = TRUE;
pConnection->pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pConnection->pIrp->Tail.Overlay.OriginalFileObject = pTdiObject->pFileObject;
TdiBuildAccept(
pConnection->pIrp, // Irp
pTdiObject->pDeviceObject, // DeviceObject
pTdiObject->pFileObject, // FileObject
&UlpRestartAccept, // CompletionRoutine
pConnection, // Context
&(pConnection->TdiConnectionInformation), // RequestConnectionInfo
NULL // ReturnConnectionInfo
);
//
// We must trace the IRP before we set the next stack location
// so the trace code can pull goodies from the IRP correctly.
//
TRACE_IRP( IRP_ACTION_CALL_DRIVER, pConnection->pIrp );
//
// Make the next stack location current. Normally, UlCallDriver would
// do this for us, but since we're bypassing UlCallDriver, we must do
// it ourselves.
//
IoSetNextIrpStackLocation( pConnection->pIrp );
//
// Return the IRP to the transport.
//
*pAcceptIrp = pConnection->pIrp;
//
// Establish the connection context.
//
*pConnectionContext = (CONNECTION_CONTEXT)pConnection;
pConnection->ConnectionFlags.AcceptPending = TRUE;
//
// Reference the connection so it doesn't go away before
// the accept IRP completes.
//
REFERENCE_CONNECTION( pConnection );
UL_LEAVE_DRIVER("UlpConnectHandler");
//
// Tell TDI that we gave it an IRP to complete.
//
return STATUS_MORE_PROCESSING_REQUIRED;
//
// Cleanup for fatal error conditions.
//
fatal:
UlTrace(TDI, (
"UlpConnectHandler: endpoint %p, failure %08lx\n",
pTdiEventContext,
status
));
if (handlerCalled)
{
//
// Fake a "failed connection complete" indication.
//
(pEndpoint->pConnectionCompleteHandler)(
pEndpoint->pListeningContext,
pConnection->pConnectionContext,
status
);
}
//
// If we managed to pull a connection off the idle list, then
// put it back and remove the endpoint reference we added.
//
if (pConnection != NULL)
{
// Fake refcount up. Worker will deref it.
REFERENCE_CONNECTION( pConnection );
UL_CALL_PASSIVE(
&pConnection->WorkItem,
&UlpCleanupEarlyConnection
);
}
UL_LEAVE_DRIVER("UlpConnectHandler");
return status;
} // UlpConnectHandler
/***************************************************************************++
Routine Description:
Handler for disconnect requests.
Arguments:
pTdiEventContext - Supplies the context associated with the address
object. This should be a PUL_ENDPOINT.
ConnectionContext - Supplies the context associated with the
connection object. This should be a PUL_CONNECTION.
DisconnectDataLength - Optionally supplies the length of any
disconnect data associated with the disconnect request.
pDisconnectData - Optionally supplies a pointer to any disconnect
data associated with the disconnect request.
DisconnectInformationLength - Optionally supplies the length of any
disconnect information associated with the disconnect request.
pDisconnectInformation - Optionally supplies a pointer to any
disconnect information associated with the disconnect request.
DisconnectFlags - Supplies the disconnect flags. This will be zero
or more TDI_DISCONNECT_* flags.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpDisconnectHandler(
IN PVOID pTdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN LONG DisconnectDataLength,
IN PVOID pDisconnectData,
IN LONG DisconnectInformationLength,
IN PVOID pDisconnectInformation,
IN ULONG DisconnectFlags
)
{
PUL_ENDPOINT pEndpoint;
PUL_CONNECTION pConnection;
PVOID pListeningContext;
PVOID pConnectionContext;
NTSTATUS status;
UL_CONNECTION_FLAGS newFlags;
UL_ENTER_DRIVER("UlpDisconnectHandler", NULL);
//
// Sanity check.
//
pEndpoint = (PUL_ENDPOINT)pTdiEventContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
pConnection = (PUL_CONNECTION)ConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
UlTrace(TDI, (
"UlpDisconnectHandler: endpoint %p, connection %p, flags %08lx\n",
pTdiEventContext,
(PVOID)ConnectionContext,
DisconnectFlags
));
//
// If it's a filtered connection, make sure we stop passing
// on AppWrite data.
//
if (pConnection->FilterInfo.pFilterChannel)
{
UlDestroyFilterConnection(&pConnection->FilterInfo);
}
//
// Update the connection state based on the type of disconnect.
//
if (DisconnectFlags & TDI_DISCONNECT_ABORT)
{
status = STATUS_CONNECTION_ABORTED;
}
else
{
status = STATUS_SUCCESS;
}
//
// Capture the endpoint and connection context values here so we can
// invoke the client's handler *after* dereferencing the connection.
//
pListeningContext = pEndpoint->pListeningContext;
pConnectionContext = pConnection->pConnectionContext;
//
// Tell the client, but only if the accept IRP has already completed.
//
newFlags.Value =
*((volatile LONG *)&pConnection->ConnectionFlags.Value);
if (newFlags.AcceptComplete)
{
//
// Silently close the connection. No need to go httprcv
// disconnect handler. It's just going to callback us anyway.
//
UlpCloseRawConnection( pConnection,
FALSE,
NULL,
NULL
);
}
//
// Done with the disconnect mark the flag, before attempting
// to remove the final reference.
//
if (DisconnectFlags & TDI_DISCONNECT_ABORT)
{
newFlags = UlpSetConnectionFlag(
pConnection,
MakeAbortIndicatedFlag()
);
}
else
{
newFlags = UlpSetConnectionFlag(
pConnection,
MakeDisconnectIndicatedFlag()
);
}
//
// If cleanup has begun on the connection, remove the final reference.
//
UlpRemoveFinalReference( pConnection, newFlags );
UL_LEAVE_DRIVER("UlpDisconnectHandler");
return STATUS_SUCCESS;
} // UlpDisconnectHandler
/***************************************************************************++
Routine Description:
Closes a previously accepted connection.
Arguments:
pConnection - Supplies a pointer to a connection as previously
indicated to the PUL_CONNECTION_REQUEST handler.
AbortiveDisconnect - Supplies TRUE if the connection is to be abortively
disconnected, FALSE if it should be gracefully disconnected.
pCompletionRoutine - Supplies a pointer to a completion routine to
invoke after the connection is fully closed.
pCompletionContext - Supplies an uninterpreted context value for the
completion routine.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpCloseRawConnection(
IN PVOID pConn,
IN BOOLEAN AbortiveDisconnect,
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
IN PVOID pCompletionContext
)
{
NTSTATUS status;
PUL_CONNECTION pConnection = (PUL_CONNECTION)pConn;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
UlTrace(TDI, (
"UlpCloseRawConnection: connection %p, abort %lu\n",
pConnection,
(ULONG)AbortiveDisconnect
));
//
// This is the final close handler for all types of connections
// filter, non filter. We should not go through this path twice
//
if (FALSE != InterlockedExchange(&pConnection->Terminated, TRUE))
{
//
// we've already done it. don't do it twice.
//
status = UlInvokeCompletionRoutine(
STATUS_SUCCESS,
0,
pCompletionRoutine,
pCompletionContext
);
return status;
}
WRITE_REF_TRACE_LOG2(
g_pTdiTraceLog,
pConnection->pTraceLog,
REF_ACTION_CLOSE_UL_CONNECTION_RAW_CLOSE,
pConnection->ReferenceCount,
pConnection,
__FILE__,
__LINE__
);
//
// Get rid of our opaque id if we're a filtered connection.
// Also make sure we stop delivering AppWrite data to the parser.
//
if (pConnection->FilterInfo.pFilterChannel)
{
UL_CALL_PASSIVE(
&pConnection->WorkItem,
&UlpCleanupConnectionId
);
UlDestroyFilterConnection(&pConnection->FilterInfo);
}
//
// Begin a disconnect and let the completion routine do the
// dirty work.
//
if (AbortiveDisconnect)
{
status = UlpBeginAbort(
pConnection,
pCompletionRoutine,
pCompletionContext
);
}
else
{
status = UlpBeginDisconnect(
pConnection,
pCompletionRoutine,
pCompletionContext
);
}
return status;
} // UlpCloseRawConnection
/***************************************************************************++
Routine Description:
Sends a block of data on the specified connection.
Arguments:
pConnection - Supplies a pointer to a connection as previously
indicated to the PUL_CONNECTION_REQUEST handler.
pMdlChain - Supplies a pointer to a MDL chain describing the
data buffers to send.
Length - Supplies the length of the data referenced by the MDL
chain.
pIrpContext - used to indicate completion to the caller.
InitiateDisconnect - Supplies TRUE if a graceful disconnect should
be initiated immediately after initiating the send (i.e. before
the send actually completes).
--***************************************************************************/
NTSTATUS
UlpSendRawData(
IN PVOID pObject,
IN PMDL pMdlChain,
IN ULONG Length,
PUL_IRP_CONTEXT pIrpContext
)
{
NTSTATUS status;
PIRP pIrp;
PUX_TDI_OBJECT pTdiObject;
PUL_CONNECTION pConnection = (PUL_CONNECTION) pObject;
BOOLEAN OwnIrpContext;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
pTdiObject = &pConnection->ConnectionObject;
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
ASSERT( pMdlChain != NULL );
ASSERT( Length > 0 );
ASSERT( pIrpContext != NULL );
//
// Allocate an IRP.
//
if (pIrpContext->pOwnIrp)
{
OwnIrpContext = TRUE;
pIrp = pIrpContext->pOwnIrp;
}
else
{
OwnIrpContext = FALSE;
pIrp = UlAllocateIrp(
pTdiObject->pDeviceObject->StackSize, // StackSize
FALSE // ChargeQuota
);
if (pIrp == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fatal;
}
}
//
// Build the send IRP, call the transport.
//
pIrp->RequestorMode = KernelMode;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.OriginalFileObject = pTdiObject->pFileObject;
TdiBuildSend(
pIrp, // Irp
pTdiObject->pDeviceObject, // DeviceObject
pTdiObject->pFileObject, // FileObject
&UlpRestartSendData, // CompletionRoutine
pIrpContext, // Context
pMdlChain, // MdlAddress
0, // Flags
Length // SendLength
);
UlTrace(TDI, (
"UlpSendRawData: allocated irp %p for connection %p\n",
pIrp,
pConnection
));
WRITE_REF_TRACE_LOG(
g_pMdlTraceLog,
REF_ACTION_SEND_MDL,
PtrToLong(pMdlChain->Next), // bugbug64
pMdlChain,
__FILE__,
__LINE__
);
#ifdef SPECIAL_MDL_FLAG
{
PMDL scan = pMdlChain;
while (scan != NULL)
{
ASSERT( (scan->MdlFlags & SPECIAL_MDL_FLAG) == 0 );
scan->MdlFlags |= SPECIAL_MDL_FLAG;
scan = scan->Next;
}
}
#endif
IF_DEBUG2(TDI, VERBOSE)
{
PMDL pMdl;
ULONG i, NumMdls = 0;
for (pMdl = pMdlChain; pMdl != NULL; pMdl = pMdl->Next)
{
++NumMdls;
}
UlTrace(TDI, (
"UlpSendRawData: irp %p, %d MDLs, %d bytes, [[[[.\n",
pIrp, NumMdls, Length
));
for (pMdl = pMdlChain, i = 1; pMdl != NULL; pMdl = pMdl->Next, ++i)
{
PVOID pBuffer;
UlTrace(TDI, (
"UlpSendRawData: irp %p, MDL[%d of %d], %d bytes.\n",
pIrp, i, NumMdls, pMdl->ByteCount
));
pBuffer = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority);
if (pBuffer != NULL)
UlDbgPrettyPrintBuffer((UCHAR*) pBuffer, pMdl->ByteCount);
}
UlTrace(TDI, (
"UlpSendRawData: irp %p ]]]].\n",
pIrp
));
}
//
// Add a reference to the connection, then call the driver to initiate
// the send.
//
REFERENCE_CONNECTION( pConnection );
ASSERT( g_TcpFastSend != NULL );
IoSetNextIrpStackLocation(pIrp);
(*g_TcpFastSend)(
pIrp,
IoGetCurrentIrpStackLocation(pIrp)
);
UlTrace(TDI, (
"UlpSendRawData: called driver for irp %p; "
"returning STATUS_PENDING\n",
pIrp
));
return STATUS_PENDING;
fatal:
ASSERT( !NT_SUCCESS(status) );
if (pIrp != NULL && OwnIrpContext == FALSE)
{
UlFreeIrp( pIrp );
}
(VOID)UlpCloseRawConnection(
pConnection,
TRUE,
NULL,
NULL
);
return status;
} // UlpSendRawData
/***************************************************************************++
Routine Description:
Receives data from the specified connection. This function is
typically used after a receive indication handler has failed to
consume all of the indicated data.
Arguments:
pConnection - Supplies a pointer to a connection as previously
indicated to the PUL_CONNECTION_REQUEST handler.
pBuffer - Supplies a pointer to the target buffer for the received
data.
BufferLength - Supplies the length of pBuffer.
pCompletionRoutine - Supplies a pointer to a completion routine to
invoke after the listening endpoint is fully closed.
pCompletionContext - Supplies an uninterpreted context value for the
completion routine.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpReceiveRawData(
IN PVOID pConnectionContext,
IN PVOID pBuffer,
IN ULONG BufferLength,
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
IN PVOID pCompletionContext
)
{
NTSTATUS status;
PUX_TDI_OBJECT pTdiObject;
PUL_IRP_CONTEXT pIrpContext;
PIRP pIrp;
PMDL pMdl;
KIRQL oldIrql;
PUL_CONNECTION pConnection = (PUL_CONNECTION) pConnectionContext;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
pTdiObject = &pConnection->ConnectionObject;
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
ASSERT( pCompletionRoutine != NULL );
UlTrace(TDI, (
"UlpReceiveRawData: connection %p, buffer %p, length %lu\n",
pConnection,
pBuffer,
BufferLength
));
//
// Setup locals so we know how to cleanup on failure.
//
pIrpContext = NULL;
pIrp = NULL;
pMdl = NULL;
//
// Create & initialize a receive IRP.
//
pIrp = UlAllocateIrp(
pTdiObject->pDeviceObject->StackSize, // StackSize
FALSE // ChargeQuota
);
if (pIrp != NULL)
{
//
// Snag an IRP context.
//
pIrpContext = UlPplAllocateIrpContext();
if (pIrpContext != NULL)
{
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
pIrpContext->pConnectionContext = (PVOID)pConnection;
pIrpContext->pCompletionRoutine = pCompletionRoutine;
pIrpContext->pCompletionContext = pCompletionContext;
pIrpContext->pOwnIrp = NULL;
//
// Create an MDL describing the client's buffer.
//
pMdl = UlAllocateMdl(
pBuffer, // VirtualAddress
BufferLength, // Length
FALSE, // SecondaryBuffer
FALSE, // ChargeQuota
NULL // Irp
);
if (pMdl != NULL)
{
//
// Adjust the MDL for our non-paged buffer.
//
MmBuildMdlForNonPagedPool( pMdl );
//
// Reference the connection, finish building the IRP.
//
REFERENCE_CONNECTION( pConnection );
TdiBuildReceive(
pIrp, // Irp
pTdiObject->pDeviceObject, // DeviceObject
pTdiObject->pFileObject, // FileObject
&UlpRestartClientReceive, // CompletionRoutine
pIrpContext, // CompletionContext
pMdl, // Mdl
TDI_RECEIVE_NORMAL, // Flags
BufferLength // Length
);
UlTrace(TDI, (
"UlpReceiveRawData: allocated irp %p for connection %p\n",
pIrp,
pConnection
));
//
// Let the transport do the rest.
//
UlCallDriver( pTdiObject->pDeviceObject, pIrp );
return STATUS_PENDING;
}
}
}
//
// We only make it this point if we hit an allocation failure.
//
if (pMdl != NULL)
{
UlFreeMdl( pMdl );
}
if (pIrpContext != NULL)
{
UlPplFreeIrpContext( pIrpContext );
}
if (pIrp != NULL)
{
UlFreeIrp( pIrp );
}
status = UlInvokeCompletionRoutine(
STATUS_INSUFFICIENT_RESOURCES,
0,
pCompletionRoutine,
pCompletionContext
);
return status;
} // UlpReceiveRawData
/***************************************************************************++
Routine Description:
A Dummy handler that is called by the filter code. This just calls
back into UlHttpReceive.
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpDummyReceiveHandler(
IN PVOID pTdiEventContext,
IN PVOID ConnectionContext,
IN PVOID pTsdu,
IN ULONG BytesIndicated,
IN ULONG BytesUnreceived,
OUT ULONG *pBytesTaken
)
{
PUL_ENDPOINT pEndpoint;
PUL_CONNECTION pConnection;
//
// Sanity check.
//
ASSERT(pTdiEventContext == NULL);
ASSERT(BytesUnreceived == 0);
pConnection = (PUL_CONNECTION)ConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
pEndpoint = pConnection->pOwningEndpoint;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
return (pEndpoint->pDataReceiveHandler)(
pEndpoint->pListeningContext,
pConnection->pConnectionContext,
pTsdu,
BytesIndicated,
BytesUnreceived,
pBytesTaken
);
} // UlpDummyReceiveHandler
/***************************************************************************++
Routine Description:
Handler for normal receive data.
Arguments:
pTdiEventContext - Supplies the context associated with the address
object. This should be a PUL_ENDPOINT.
ConnectionContext - Supplies the context associated with the
connection object. This should be a PUL_CONNECTION.
ReceiveFlags - Supplies the receive flags. This will be zero or more
TDI_RECEIVE_* flags.
BytesIndicated - Supplies the number of bytes indicated in pTsdu.
BytesAvailable - Supplies the number of bytes available in this
TSDU.
pBytesTaken - Receives the number of bytes consumed by this handler.
pTsdu - Supplies a pointer to the indicated data.
pIrp - Receives an IRP if the handler needs more data than indicated.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpReceiveHandler(
IN PVOID pTdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN ULONG ReceiveFlags,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *pBytesTaken,
IN PVOID pTsdu,
OUT PIRP *pIrp
)
{
NTSTATUS status;
PUL_ENDPOINT pEndpoint;
PUL_CONNECTION pConnection;
PUX_TDI_OBJECT pTdiObject;
UL_CONNECTION_FLAGS ConnectionFlags;
UL_ENTER_DRIVER("UlpReceiveHandler", NULL);
//
// Sanity check.
//
pEndpoint = (PUL_ENDPOINT)pTdiEventContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
pConnection = (PUL_CONNECTION)ConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
pTdiObject = &pConnection->ConnectionObject;
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
UlTrace(TDI, (
"UlpReceiveHandler: endpoint %p, connection %p, length %lu,%lu\n",
pTdiEventContext,
(PVOID)ConnectionContext,
BytesIndicated,
BytesAvailable
));
//
// Clear the bytes taken output var
//
*pBytesTaken = 0;
//
// Wait for the local address to be set just in case the receive happens
// before accept. This is possible (but rare) on MP machines even when
// using TDI_ACCEPT. We set the ReceivePending flag and reject
// the data if this ever happens. When accept is completed, we will build
// a receive IRP to flush the data if ReceivePending is set.
//
ConnectionFlags.Value = *((volatile LONG *)&pConnection->ConnectionFlags.Value);
if (0 == ConnectionFlags.LocalAddressValid)
{
pConnection->ConnectionFlags.ReceivePending = 1;
status = STATUS_DATA_NOT_ACCEPTED;
goto end;
}
//
// Give the client a crack at the data.
//
if (pConnection->FilterInfo.pFilterChannel)
{
//
// Needs to go through a filter.
//
status = UlFilterReceiveHandler(
&pConnection->FilterInfo,
pTsdu,
BytesIndicated,
BytesAvailable - BytesIndicated,
pBytesTaken
);
}
else
{
//
// Go directly to client.
//
status = (pEndpoint->pDataReceiveHandler)(
pEndpoint->pListeningContext,
pConnection->pConnectionContext,
pTsdu,
BytesIndicated,
BytesAvailable - BytesIndicated,
pBytesTaken
);
}
ASSERT( *pBytesTaken <= BytesIndicated );
if (status == STATUS_SUCCESS)
{
goto end;
}
else if (status == STATUS_MORE_PROCESSING_REQUIRED)
{
ASSERT(!"How could this ever happen?");
//
// The client consumed part of the indicated data.
//
// A subsequent receive indication will be made to the client when
// additional data is available. This subsequent indication will
// include the unconsumed data from the current indication plus
// any additional data received.
//
// We need to allocate a receive buffer so we can pass an IRP back
// to the transport.
//
status = UlpBuildTdiReceiveBuffer(pTdiObject, pConnection, pIrp);
if (status == STATUS_MORE_PROCESSING_REQUIRED)
{
//
// Make the next stack location current. Normally, UlCallDriver
// would do this for us, but since we're bypassing UlCallDriver,
// we must do it ourselves.
//
IoSetNextIrpStackLocation( *pIrp );
goto end;
}
}
//
// If we made it this far, then we've hit a fatal condition. Either the
// client returned a status code other than STATUS_SUCCESS or
// STATUS_MORE_PROCESSING_REQUIRED, or we failed to allocation the
// receive IRP to pass back to the transport. In either case, we need
// to abort the connection.
//
UlpCloseRawConnection(
pConnection,
TRUE, // AbortiveDisconnect
NULL, // pCompletionRoutine
NULL // pCompletionContext
);
end:
UlTrace(TDI, (
"UlpReceiveHandler: endpoint %p, connection %p, length %lu,%lu, taken %lu, status %x\n",
pTdiEventContext,
(PVOID)ConnectionContext,
BytesIndicated,
BytesAvailable,
*pBytesTaken,
status
));
UL_LEAVE_DRIVER("UlpReceiveHandler");
return status;
} // UlpReceiveHandler
/***************************************************************************++
Routine Description:
Handler for expedited receive data.
Arguments:
pTdiEventContext - Supplies the context associated with the address
object. This should be a PUL_ENDPOINT.
ConnectionContext - Supplies the context associated with the
connection object. This should be a PUL_CONNECTION.
ReceiveFlags - Supplies the receive flags. This will be zero or more
TDI_RECEIVE_* flags.
BytesIndiated - Supplies the number of bytes indicated in pTsdu.
BytesAvailable - Supplies the number of bytes available in this
TSDU.
pBytesTaken - Receives the number of bytes consumed by this handler.
pTsdu - Supplies a pointer to the indicated data.
pIrp - Receives an IRP if the handler needs more data than indicated.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpReceiveExpeditedHandler(
IN PVOID pTdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN ULONG ReceiveFlags,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *pBytesTaken,
IN PVOID pTsdu,
OUT PIRP *pIrp
)
{
PUL_ENDPOINT pEndpoint;
PUL_CONNECTION pConnection;
PUX_TDI_OBJECT pTdiObject;
UL_ENTER_DRIVER("UlpReceiveExpeditedHandler", NULL);
//
// Sanity check.
//
pEndpoint = (PUL_ENDPOINT)pTdiEventContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
pConnection = (PUL_CONNECTION)ConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
pTdiObject = &pConnection->ConnectionObject;
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
UlTrace(TDI, (
"UlpReceiveExpeditedHandler: endpoint %p, connection %p, length %lu,%lu\n",
pTdiEventContext,
(PVOID)ConnectionContext,
BytesIndicated,
BytesAvailable
));
//
// We don't support expedited data, so just consume it all.
//
*pBytesTaken = BytesAvailable;
UL_LEAVE_DRIVER("UlpReceiveExpeditedHandler");
return STATUS_SUCCESS;
} // UlpReceiveExpeditedHandler
/***************************************************************************++
Routine Description:
Completion handler for accept IRPs.
Arguments:
pDeviceObject - Supplies the device object for the IRP being
completed.
pIrp - Supplies the IRP being completed.
pContext - Supplies the context associated with this request.
This is actually a PUL_CONNECTION.
Return Value:
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
this IRP.
--***************************************************************************/
NTSTATUS
UlpRestartAccept(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID pContext
)
{
PUL_CONNECTION pConnection;
PUL_ENDPOINT pEndpoint;
BOOLEAN needDisconnect;
NTSTATUS queryStatus;
NTSTATUS irpStatus;
UL_CONNECTION_FLAGS newFlags;
PTA_IP_ADDRESS pIpAddress;
//
// Sanity check.
//
pConnection = (PUL_CONNECTION)pContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pConnection->ConnectionFlags.AcceptPending );
pEndpoint = pConnection->pOwningEndpoint;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
UlTrace(TDI, (
"UlpRestartAccept: irp %p, endpoint %p, connection %p, status %08lx\n",
pIrp,
pEndpoint,
pConnection,
pIrp->IoStatus.Status
));
//
// Assume for now that we don't need to issue a disconnect.
//
needDisconnect = FALSE;
//
// Capture the status from the IRP then free it.
//
irpStatus = pIrp->IoStatus.Status;
//
// If the connection was fully accepted (successfully), then
// move it to the endpoint's active list.
//
if (NT_SUCCESS(irpStatus))
{
UlpEnqueueActiveConnection( pConnection );
//
// Get the Local Address Info
//
pIpAddress = &(pConnection->IpAddress);
if (TDI_ADDRESS_TYPE_IP == pIpAddress->Address[0].AddressType)
{
if (pIpAddress->Address[0].AddressLength >= TDI_ADDRESS_LENGTH_IP)
{
pConnection->LocalAddress = SWAP_LONG(pIpAddress->Address[0].Address[0].in_addr);
pConnection->LocalPort = SWAP_SHORT(pIpAddress->Address[0].Address[0].sin_port);
pConnection->ConnectionFlags.LocalAddressValid = TRUE;
}
}
else
{
// Sabama: Add support for IP6 here
}
//
// Set the AcceptComplete flag. If an abort or disconnect has
// already been indicated, then remember this fact so we can
// fake a call to the client's connection disconnect handler
// after we invoke the connection complete handler.
//
newFlags.Value =
*((volatile LONG *)&pConnection->ConnectionFlags.Value);
if (newFlags.AbortIndicated || newFlags.DisconnectIndicated)
{
needDisconnect = TRUE;
}
if (needDisconnect == FALSE && newFlags.ReceivePending)
{
PIRP pReceiveIrp;
NTSTATUS status;
//
// We may have pending receives that we rejected early on
// inside the receive handler. Build an IRP to flush the
// data now.
//
status = UlpBuildTdiReceiveBuffer(
&pConnection->ConnectionObject,
pConnection,
&pReceiveIrp
);
if (status != STATUS_MORE_PROCESSING_REQUIRED)
{
needDisconnect = TRUE;
}
else
{
UlCallDriver(
pConnection->ConnectionObject.pDeviceObject,
pReceiveIrp
);
}
}
}
//
// Tell the client that the connection is complete. If necessary, also
// tell them that the connection has been disconnected.
//
(pEndpoint->pConnectionCompleteHandler)(
pEndpoint->pListeningContext,
pConnection->pConnectionContext,
irpStatus
);
if (needDisconnect)
{
//
// Silently close the connection. No need to go httprcv
// disconnect handler. It's just going to callback us anyway.
//
UlpCloseRawConnection( pConnection,
FALSE,
NULL,
NULL
);
}
//
// If the accept failed, then mark the connection so we know there is
// no longer an accept pending, enqueue the connection back onto the
// endpoint's idle list, and then remove the endpoint reference added
// in the connect handler,
//
if (!NT_SUCCESS(irpStatus))
{
pConnection->ConnectionFlags.AcceptPending = FALSE;
//
// Need to get rid of our opaque id if we're a filtered connection.
//
UL_CALL_PASSIVE(
&pConnection->WorkItem,
&UlpCleanupEarlyConnection
);
}
else
{
//
// Mark we are done with the accept. And try to disconnect
// if necessary.
//
newFlags = UlpSetConnectionFlag(
pConnection,
MakeAcceptCompleteFlag()
);
if (needDisconnect)
{
//
// We now may be able to remove the final reference since
// we have now set the AcceptComplete flag.
//
UlpRemoveFinalReference(pConnection, newFlags);
}
//
// Drop the reference added in UlpConnectHandler.
//
DEREFERENCE_CONNECTION( pConnection );
}
return STATUS_MORE_PROCESSING_REQUIRED;
} // UlpRestartAccept
/***************************************************************************++
Routine Description:
Completion handler for send IRPs.
Arguments:
pDeviceObject - Supplies the device object for the IRP being
completed.
pIrp - Supplies the IRP being completed.
pContext - Supplies the context associated with this request.
This is actually a PUL_IRP_CONTEXT.
Return Value:
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
this IRP.
--***************************************************************************/
NTSTATUS
UlpRestartSendData(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID pContext
)
{
PIO_STACK_LOCATION pIrpSp;
PUL_CONNECTION pConnection;
PUL_IRP_CONTEXT pIrpContext;
BOOLEAN OwnIrpContext;
//
// Sanity check.
//
pIrpContext = (PUL_IRP_CONTEXT)pContext;
OwnIrpContext = (pIrpContext->pOwnIrp != NULL);
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
ASSERT( pIrpContext->pCompletionRoutine != NULL );
pConnection = (PUL_CONNECTION)pIrpContext->pConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( IS_VALID_ENDPOINT( pConnection->pOwningEndpoint ) );
UlTrace(TDI, (
"UlpRestartSendData: irp %p, connection %p, status %08lx, info %lx\n",
pIrp,
pConnection,
pIrp->IoStatus.Status,
(ULONG)pIrp->IoStatus.Information
));
WRITE_REF_TRACE_LOG(
g_pMdlTraceLog,
REF_ACTION_SEND_MDL_COMPLETE,
PtrToLong(pIrp->MdlAddress->Next), // bugbug64
pIrp->MdlAddress,
__FILE__,
__LINE__
);
#ifdef SPECIAL_MDL_FLAG
{
PMDL scan = pIrp->MdlAddress;
while (scan != NULL)
{
ASSERT( (scan->MdlFlags & SPECIAL_MDL_FLAG) != 0 );
scan->MdlFlags &= ~SPECIAL_MDL_FLAG;
scan = scan->Next;
}
}
#endif
//
// Tell the client that the send is complete.
//
(pIrpContext->pCompletionRoutine)(
pIrpContext->pCompletionContext,
pIrp->IoStatus.Status,
pIrp->IoStatus.Information
);
//
// Remove the reference we added in UlSendData().
//
DEREFERENCE_CONNECTION( pConnection );
//
// Free the context & the IRP since we're done with them, then
// tell IO to stop processing the IRP.
//
if (OwnIrpContext == FALSE)
{
UlFreeIrp( pIrp );
UlPplFreeIrpContext( pIrpContext );
}
return STATUS_MORE_PROCESSING_REQUIRED;
} // UlpRestartSendData
/***************************************************************************++
Routine Description:
Increments the reference count on the specified endpoint.
Arguments:
pEndpoint - Supplies the endpoint to reference.
pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
containing the calling function.
LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
the calling function.
--***************************************************************************/
VOID
UlpReferenceEndpoint(
IN PUL_ENDPOINT pEndpoint
OWNER_REFERENCE_DEBUG_FORMAL_PARAMS
)
{
LONG refCount;
//
// Sanity check.
//
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// Reference it.
//
refCount = InterlockedIncrement( &pEndpoint->ReferenceCount );
ASSERT( refCount > 0 );
WRITE_REF_TRACE_LOG(
g_pTdiTraceLog,
REF_ACTION_REFERENCE_ENDPOINT,
refCount,
pEndpoint,
pFileName,
LineNumber
);
WRITE_OWNER_REF_TRACE_LOG(
pEndpoint->pOwnerRefTraceLog,
pOwner,
ppRefOwner,
OwnerSignature,
Action,
refCount, // absolute refcount
MonotonicId,
+1, // increment relative refcount
pFileName,
LineNumber
);
UlTrace(TDI, (
"UlpReferenceEndpoint: endpoint %p, refcount %ld\n",
pEndpoint,
refCount
));
} // UlpReferenceEndpoint
/***************************************************************************++
Routine Description:
Decrements the reference count on the specified endpoint.
Arguments:
pEndpoint - Supplies the endpoint to dereference.
pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
containing the calling function.
LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
the calling function.
--***************************************************************************/
VOID
UlpDereferenceEndpoint(
IN PUL_ENDPOINT pEndpoint
OWNER_REFERENCE_DEBUG_FORMAL_PARAMS
)
{
LONG refCount;
KIRQL oldIrql;
//
// Sanity check.
//
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// Dereference it.
//
refCount = InterlockedDecrement( &pEndpoint->ReferenceCount );
ASSERT( refCount >= 0 );
WRITE_REF_TRACE_LOG(
g_pTdiTraceLog,
REF_ACTION_DEREFERENCE_ENDPOINT,
refCount,
pEndpoint,
pFileName,
LineNumber
);
WRITE_OWNER_REF_TRACE_LOG(
pEndpoint->pOwnerRefTraceLog,
pOwner,
ppRefOwner,
OwnerSignature,
Action,
refCount, // absolute refcount
MonotonicId,
-1, // decrement relative refcount
pFileName,
LineNumber
);
UlTrace(TDI, (
"UlpDereferenceEndpoint: endpoint %p, refcount %ld\n",
pEndpoint,
refCount
));
if (refCount == 0)
{
//
// The final reference to the endpoint has been removed, so
// it's time to destroy the endpoint. We'll remove the
// endpoint from the global list and move it to the deleted
// list (if necessary), release the TDI spinlock,
// then destroy the connection.
//
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
if (! pEndpoint->Deleted)
{
// If this routine was called by the `fatal' section of
// UlCreateListeningEndpoint, then the endpoint was never
// added to g_TdiEndpointListHead.
if (NULL != pEndpoint->GlobalEndpointListEntry.Flink)
RemoveEntryList( &pEndpoint->GlobalEndpointListEntry );
InsertTailList(
&g_TdiDeletedEndpointListHead,
&pEndpoint->GlobalEndpointListEntry
);
pEndpoint->Deleted = TRUE;
}
else
{
ASSERT(NULL != pEndpoint->GlobalEndpointListEntry.Flink);
}
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
//
// The endpoint is going away. Do final cleanup & resource
// release at passive IRQL.
//
UL_CALL_PASSIVE(
&pEndpoint->WorkItem,
&UlpEndpointCleanupWorker
);
}
} // UlpDereferenceEndpoint
/***************************************************************************++
Routine Description:
Increments the reference count on the specified connection.
Arguments:
pConnection - Supplies the connection to reference.
pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
containing the calling function.
LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
the calling function.
--***************************************************************************/
VOID
UlReferenceConnection(
IN PVOID pObject
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
PUL_ENDPOINT pEndpoint;
LONG refCount;
PUL_CONNECTION pConnection = (PUL_CONNECTION) pObject;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
pEndpoint = pConnection->pOwningEndpoint;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// Reference it.
//
refCount = InterlockedIncrement( &pConnection->ReferenceCount );
ASSERT( refCount > 1 );
WRITE_REF_TRACE_LOG2(
g_pTdiTraceLog,
pConnection->pTraceLog,
REF_ACTION_REFERENCE_CONNECTION,
refCount,
pConnection,
pFileName,
LineNumber
);
UlTrace(TDI, (
"UlReferenceConnection: connection %p, refcount %ld\n",
pConnection,
refCount
));
} // UlReferenceConnection
/***************************************************************************++
Routine Description:
Decrements the reference count on the specified connection.
Arguments:
pConnection - Supplies the connection to dereference.
pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
containing the calling function.
LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
the calling function.
--***************************************************************************/
VOID
UlDereferenceConnection(
IN PVOID pObject
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
PUL_ENDPOINT pEndpoint;
LONG refCount;
PUL_CONNECTION pConnection = (PUL_CONNECTION) pObject;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
pEndpoint = pConnection->pOwningEndpoint;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// Dereference it.
//
refCount = InterlockedDecrement( &pConnection->ReferenceCount );
ASSERT( refCount >= 0 );
WRITE_REF_TRACE_LOG2(
g_pTdiTraceLog,
pConnection->pTraceLog,
REF_ACTION_DEREFERENCE_CONNECTION,
refCount,
pConnection,
pFileName,
LineNumber
);
UlTrace(TDI, (
"UlDereferenceConnection: connection %p, refcount %ld\n",
pConnection,
refCount
));
if (refCount == 0)
{
//
// The final reference to the connection has been removed, so
// it's time to destroy the connection. We'll release the
// endpoint spinlock, dereference the endpoint, then destroy
// the connection.
//
ASSERT(pConnection->ActiveListEntry.Flink == NULL);
//
// Do final cleanup & resource release at passive IRQL. Also
// cleanupworker requires to be running under system process.
//
UL_QUEUE_WORK_ITEM(
&pConnection->WorkItem,
&UlpConnectionCleanupWorker
);
}
} // UlDereferenceConnection
/***************************************************************************++
Routine Description:
Deferred cleanup routine for dead endpoints.
Arguments:
pWorkItem - Supplies a pointer to the work item queued. This should
point to the WORK_ITEM structure embedded in a UL_ENDPOINT.
--***************************************************************************/
VOID
UlpEndpointCleanupWorker(
IN PUL_WORK_ITEM pWorkItem
)
{
PUL_ENDPOINT pEndpoint;
//
// Sanity check.
//
PAGED_CODE();
pEndpoint = CONTAINING_RECORD(
pWorkItem,
UL_ENDPOINT,
WorkItem
);
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// Nuke it.
//
UlpDestroyEndpoint( pEndpoint );
} // UlpEndpointCleanupWorker
/***************************************************************************++
Routine Description:
Removes the opaque id from a connection. This has to happen at passive
level because the opaque id table can't handle high IRQL.
Arguments:
pWorkItem - embedded in the connection object.
--***************************************************************************/
VOID
UlpCleanupConnectionId(
IN PUL_WORK_ITEM pWorkItem
)
{
KIRQL oldIrql;
PUL_CONNECTION pConnection;
HTTP_RAW_CONNECTION_ID ConnectionId;
//
// Sanity check.
//
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
//
// Grab the connection.
//
pConnection = CONTAINING_RECORD(
pWorkItem,
UL_CONNECTION,
WorkItem
);
ASSERT( IS_VALID_CONNECTION(pConnection) );
//
// Pull the id off the connection.
//
UlAcquireSpinLock( &pConnection->FilterInfo.FilterConnLock, &oldIrql );
ConnectionId = pConnection->FilterInfo.ConnectionId;
HTTP_SET_NULL_ID( &pConnection->FilterInfo.ConnectionId );
UlReleaseSpinLock( &pConnection->FilterInfo.FilterConnLock, oldIrql );
//
// Actually get rid of it at low IRQL.
//
if (!HTTP_IS_NULL_ID( &ConnectionId ))
{
UlTrace(TDI, (
"UlpCleanupConnectionId: conn=%p id=%I64x\n",
pConnection, ConnectionId
));
UlFreeOpaqueId(ConnectionId, UlOpaqueIdTypeRawConnection);
DEREFERENCE_CONNECTION(pConnection);
}
} // UlpCleanupConnectionId
/***************************************************************************++
Routine Description:
This function gets called if RestartAccept fails and we cannot establish
a connection on a secure endpoint, or if something goes wrong in
UlpConnectHandler.
The connections over secure endpoints keep an extra refcount to
the UL_CONNECTION because of their opaqueid. They normally
get removed after the CloseRawConnection happens but in the above case
close won't happen and we have to explicitly cleanup the id. This has
to happen at passive level because the opaque id table can't handle high
IRQL.
Arguments:
pWorkItem - embedded in the connection object.
--***************************************************************************/
VOID
UlpCleanupEarlyConnection(
IN PUL_WORK_ITEM pWorkItem
)
{
PUL_CONNECTION pConnection;
UL_CONNECTION_FLAGS Flags;
//
// Sanity check.
//
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
//
// Grab the connection.
//
pConnection = CONTAINING_RECORD(
pWorkItem,
UL_CONNECTION,
WorkItem
);
ASSERT( IS_VALID_CONNECTION(pConnection) );
//
// If we are failing early we should never have been to
// FinalReferenceRemoved in the first place.
//
Flags.Value = *((volatile LONG *)&pConnection->ConnectionFlags.Value);
ASSERT( !Flags.FinalReferenceRemoved );
if (pConnection->FilterInfo.pFilterChannel)
{
//
// Cleanup opaque id. And release the final refcount
//
UlpCleanupConnectionId( pWorkItem );
}
//
// Drop the reference added in UlpConnectHandler.
//
DEREFERENCE_CONNECTION( pConnection );
//
// Remove the final reference.
//
DEREFERENCE_CONNECTION( pConnection );
} // UlpCleanupEarlyConnection
/***************************************************************************++
Routine Description:
Deferred cleanup routine for dead connections. We have to be queued as a
work item and should be running on the passive IRQL. See below comment.
Arguments:
pWorkItem - Supplies a pointer to the work item queued. This should
point to the WORK_ITEM structure embedded in a UL_CONNECTION.
--***************************************************************************/
VOID
UlpConnectionCleanupWorker(
IN PUL_WORK_ITEM pWorkItem
)
{
PUL_CONNECTION pConnection;
PUL_ENDPOINT pEndpoint;
NTSTATUS status;
//
// Sanity check.
//
PAGED_CODE();
//
// Initialize locals.
//
status = STATUS_SUCCESS;
pConnection = CONTAINING_RECORD(
pWorkItem,
UL_CONNECTION,
WorkItem
);
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( IS_VALID_ENDPOINT( pConnection->pOwningEndpoint ) );
ASSERT( pConnection->ReferenceCount == 0 );
ASSERT( pConnection->HttpConnection.RefCount == 0 );
//
// Grab the endpoint.
//
pEndpoint = pConnection->pOwningEndpoint;
//
// Get rid of any buffers we allocated for
// certificate information.
//
if (pConnection->FilterInfo.SslInfo.pServerCertData)
{
UL_FREE_POOL(
pConnection->FilterInfo.SslInfo.pServerCertData,
UL_SSL_CERT_DATA_POOL_TAG
);
pConnection->FilterInfo.SslInfo.pServerCertData = NULL;
}
if (pConnection->FilterInfo.SslInfo.pCertEncoded)
{
UL_FREE_POOL(
pConnection->FilterInfo.SslInfo.pCertEncoded,
UL_SSL_CERT_DATA_POOL_TAG
);
pConnection->FilterInfo.SslInfo.pCertEncoded = NULL;
}
if (pConnection->FilterInfo.SslInfo.Token)
{
HANDLE Token;
Token = (HANDLE) pConnection->FilterInfo.SslInfo.Token;
//
// If we are not running under the system process. And if the
// thread we are running under has some APCs queued currently
// KeAttachProcess won't allow us to attach to another process
// and will bugcheck 5. We have to be queued as a work item and
// should be running on the passive IRQL.
//
ASSERT( PsGetCurrentProcess() == (PEPROCESS) g_pUlSystemProcess );
ZwClose(Token);
}
//
// Release the filter channel.
//
if (pConnection->FilterInfo.pFilterChannel)
{
DEREFERENCE_FILTER_CHANNEL(pConnection->FilterInfo.pFilterChannel);
pConnection->FilterInfo.pFilterChannel = NULL;
}
//
// Check if g_UlEnableConnectionReuse is enabled or we are about to
// exceed g_UlMaxIdleConnections.
//
if (ExQueryDepthSList(&pEndpoint->IdleConnectionSListHead) >=
g_UlMaxIdleConnections)
{
status = STATUS_ALLOTTED_SPACE_EXCEEDED;
}
if (g_UlEnableConnectionReuse == FALSE)
{
status = STATUS_NOT_SUPPORTED;
}
//
// If the connection is still ok and we're reusing
// connection objects, throw it back on the idle list.
//
if (NT_SUCCESS(status))
{
//
// Initialize the connection for reuse.
//
status = UlpInitializeConnection(pConnection);
if (NT_SUCCESS(status))
{
//
// Stick the connection back on the idle list.
//
UlpEnqueueIdleConnection(pConnection, FALSE);
}
}
//
// Active connections hold a reference to the ENDPOINT. Release
// that reference. See the comment on UlpCreateConnection.
//
DEREFERENCE_ENDPOINT_CONNECTION(
pEndpoint,
REF_ACTION_CONN_CLEANUP,
pConnection);
//
// If anything went amiss, blow away the connection.
//
if (!NT_SUCCESS(status))
{
UlpDestroyConnection( pConnection );
}
} // UlpConnectionCleanupWorker
/***************************************************************************++
Routine Description:
Associates the TDI connection object contained in the specified
connection to the TDI address object contained in the specified
endpoint.
Arguments:
pConnection - Supplies the connection to associate with the endpoint.
pEndpoint - Supplies the endpoint to associated with the connection.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpAssociateConnection(
IN PUL_CONNECTION pConnection,
IN PUL_ENDPOINT pEndpoint
)
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
HANDLE handle;
TDI_REQUEST_USER_ASSOCIATE associateInfo;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
ASSERT( pConnection->pOwningEndpoint == NULL );
//
// Associate the connection with the address object.
//
associateInfo.AddressHandle = pEndpoint->AddressObject.Handle;
ASSERT( associateInfo.AddressHandle != NULL );
handle = pConnection->ConnectionObject.Handle;
ASSERT( handle != NULL );
UlAttachToSystemProcess();
status = ZwDeviceIoControlFile(
handle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
IOCTL_TDI_ASSOCIATE_ADDRESS, // IoControlCode
&associateInfo, // InputBuffer
sizeof(associateInfo), // InputBufferLength
NULL, // OutputBuffer
0 // OutputBufferLength
);
if (status == STATUS_PENDING)
{
status = ZwWaitForSingleObject(
handle, // Handle
TRUE, // Alertable
NULL // Timeout
);
ASSERT( NT_SUCCESS(status) );
status = ioStatusBlock.Status;
}
UlDetachFromSystemProcess();
if (NT_SUCCESS(status))
{
pConnection->pOwningEndpoint = pEndpoint;
pConnection->pConnectionDestroyedHandler = pEndpoint->pConnectionDestroyedHandler;
pConnection->pListeningContext = pEndpoint->pListeningContext;
}
else
{
UlTrace(TDI, (
"UlpAssociateConnection conn=%p, endp=%p, status = %08lx\n",
pConnection,
pEndpoint,
status
));
}
return status;
} // UlpAssociateConnection
/***************************************************************************++
Routine Description:
Disassociates the TDI connection object contained in the specified
connection from its TDI address object.
Arguments:
pConnection - Supplies the connection to disassociate.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpDisassociateConnection(
IN PUL_CONNECTION pConnection
)
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
HANDLE handle;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( IS_VALID_ENDPOINT( pConnection->pOwningEndpoint ) );
//
// Disassociate the connection from the address object.
//
handle = pConnection->ConnectionObject.Handle;
UlAttachToSystemProcess();
status = ZwDeviceIoControlFile(
handle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
IOCTL_TDI_DISASSOCIATE_ADDRESS, // IoControlCode
NULL, // InputBuffer
0, // InputBufferLength
NULL, // OutputBuffer
0 // OutputBufferLength
);
if (status == STATUS_PENDING)
{
status = ZwWaitForSingleObject(
handle, // Handle
TRUE, // Alertable
NULL // Timeout
);
ASSERT( NT_SUCCESS(status) );
status = ioStatusBlock.Status;
}
UlDetachFromSystemProcess();
//
// Proceed with the disassociate even if the IOCTL failed.
//
pConnection->pOwningEndpoint = NULL;
return status;
} // UlpDisassociateConnection
/***************************************************************************++
Routine Description:
Replenishes the idle connection pool in the specified endpoint.
Arguments:
pEndpoint - Supplies the endpoint to replenish.
--***************************************************************************/
VOID
UlpReplenishEndpoint(
IN PUL_ENDPOINT pEndpoint
)
{
NTSTATUS status;
PUL_CONNECTION pConnection;
BOOLEAN ContinueReplenish;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
UlTrace(TDI, (
"UlpReplenishEndpoint: endpoint %p\n",
pEndpoint
));
TRACE_REPLENISH(
pEndpoint,
DummySynch,
pEndpoint->EndpointSynch,
REPLENISH_ACTION_START_REPLENISH
);
//
// Loop, creating connections until we're fully replenished.
//
do
{
//
// Create a new connection.
//
status = UlpCreateConnection(
pEndpoint, // pEndpoint
pEndpoint->LocalAddressLength, // AddressLength
&pConnection // ppConnection
);
if (!NT_SUCCESS(status))
{
break;
}
status = UlpInitializeConnection(
pConnection
);
if (!NT_SUCCESS(status))
{
UlpDestroyConnection(pConnection);
break;
}
//
// Enqueue the connection onto the endpoint.
//
ContinueReplenish = UlpEnqueueIdleConnection( pConnection, TRUE );
} while (ContinueReplenish);
TRACE_REPLENISH(
pEndpoint,
DummySynch,
pEndpoint->EndpointSynch,
REPLENISH_ACTION_END_REPLENISH
);
//
// We are done with creating new connections for now. Clear
// the "replenish scheduled" flag so future replenish attempts
// will queue the work.
//
UlpClearReplenishScheduledFlag( pEndpoint );
} // UlpReplenishEndpoint
/***************************************************************************++
Routine Description:
Deferred endpoint replenish routine.
Arguments:
pWorkItem - Supplies a pointer to the work item queued. This should
point to the WORK_ITEM structure embedded in a UL_ENDPOINT.
--***************************************************************************/
VOID
UlpReplenishEndpointWorker(
IN PUL_WORK_ITEM pWorkItem
)
{
PUL_ENDPOINT pEndpoint;
//
// Sanity check.
//
PAGED_CODE();
pEndpoint = CONTAINING_RECORD(
pWorkItem,
UL_ENDPOINT,
WorkItem
);
UlTrace(TDI, (
"UlpReplenishEndpointWorker: endpoint %p\n",
pEndpoint
));
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// Let UlpReplenishEndpoint() do the dirty work.
//
UlpReplenishEndpoint( pEndpoint );
//
// Remove the reference that UlpDequeueIdleConnection added for
// this call.
//
DEREFERENCE_ENDPOINT_SELF(pEndpoint, REF_ACTION_REPLENISH);
} // UlpReplenishEndpointWorker
/***************************************************************************++
Routine Description:
Decrements the number of idle connections availabe on the specified
endpoint and determines if a replenish should be scheduled.
Arguments:
pEndpoint - Supplies the endpoint to increment.
Return Value:
BOOLEAN - TRUE if a replenish should be rescheduled, FALSE otherwise.
--***************************************************************************/
BOOLEAN
UlpDecrementIdleConnections(
IN PUL_ENDPOINT pEndpoint
)
{
ENDPOINT_SYNCH oldState;
ENDPOINT_SYNCH newState;
//
// Sanity check.
//
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
do
{
//
// Capture the current count and initialize the proposed
// new value.
//
newState.Value = oldState.Value =
*((volatile LONG *)&pEndpoint->EndpointSynch.Value);
newState.IdleConnections--;
ASSERT( newState.IdleConnections >= 0 );
if (newState.IdleConnections < g_UlMinIdleConnections)
{
newState.ReplenishScheduled = TRUE;
}
if (UlInterlockedCompareExchange(
&pEndpoint->EndpointSynch.Value,
newState.Value,
oldState.Value
) == oldState.Value)
{
break;
}
} while (TRUE);
UlTrace(TDI, (
"ul!UlpDecrementIdleConnections(pEndpoint = %p)\n"
" idle = %d, replenish = %s, returning %s\n",
pEndpoint,
newState.IdleConnections,
newState.ReplenishScheduled ? "TRUE" : "FALSE",
(newState.ReplenishScheduled && !oldState.ReplenishScheduled) ? "TRUE" : "FALSE"
));
TRACE_REPLENISH(
pEndpoint,
oldState,
newState,
REPLENISH_ACTION_DECREMENT
);
//
// If the "ReplenishScheduled" flag transitioned from FALSE to TRUE,
// then we need to schedule a replenish.
//
return (newState.ReplenishScheduled && !oldState.ReplenishScheduled);
} // UlpDecrementIdleConnections
/***************************************************************************++
Routine Description:
Increments the number of idle connections available on the specified
endpoint.
Arguments:
pEndpoint - Supplies the endpoint to decrement.
Replenishing - TRUE if the connection was added as part of
a replenish operation.
Return Value:
BOOLEAN - TRUE if the count has not reached the minimum number
of idle connections for the endpoint.
--***************************************************************************/
BOOLEAN
UlpIncrementIdleConnections(
IN PUL_ENDPOINT pEndpoint,
IN BOOLEAN Replenishing
)
{
ENDPOINT_SYNCH oldState;
ENDPOINT_SYNCH newState;
BOOLEAN ContinueReplenish;
//
// Sanity check.
//
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
ContinueReplenish = TRUE;
do
{
//
// Capture the current count and initialize the proposed
// new value.
//
newState.Value = oldState.Value =
*((volatile LONG *)&pEndpoint->EndpointSynch.Value);
newState.IdleConnections++;
ASSERT( newState.IdleConnections >= 0 );
if (newState.IdleConnections >= g_UlMinIdleConnections)
{
ContinueReplenish = FALSE;
}
if (UlInterlockedCompareExchange(
&pEndpoint->EndpointSynch.Value,
newState.Value,
oldState.Value
) == oldState.Value)
{
break;
}
} while (TRUE);
UlTrace(TDI, (
"ul!UlpIncrementIdleConnections(pEndpoint = %p)\n"
" idle = %d, replenish = %s\n",
pEndpoint,
newState.IdleConnections,
newState.ReplenishScheduled ? "TRUE" : "FALSE"
));
TRACE_REPLENISH(
pEndpoint,
oldState,
newState,
REPLENISH_ACTION_INCREMENT
);
//
// If we still don't have enough connections we must
// continue the replenish.
//
return ContinueReplenish;
} // UlpIncrementIdleConnections
/***************************************************************************++
Routine Description:
Clears the "replenish scheduled" flag on the endpoint. This is only
called after a replenish failure. The flag is cleared so that future
attempts to replenish will schedule properly.
Arguments:
pEndpoint - Supplies the endpoint to manipulate.
Return Value:
None.
--***************************************************************************/
VOID
UlpClearReplenishScheduledFlag(
IN PUL_ENDPOINT pEndpoint
)
{
ENDPOINT_SYNCH oldState;
ENDPOINT_SYNCH newState;
//
// Sanity check.
//
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
do
{
//
// Capture the current count and initialize the proposed
// new value.
//
newState.Value = oldState.Value =
*((volatile LONG *)&pEndpoint->EndpointSynch.Value);
newState.ReplenishScheduled = FALSE;
if (UlInterlockedCompareExchange(
&pEndpoint->EndpointSynch.Value,
newState.Value,
oldState.Value
) == oldState.Value)
{
break;
}
} while (TRUE);
} // UlpClearReplenishScheduledFlag
/***************************************************************************++
Routine Description:
Creates a new UL_CONNECTION object and opens the corresponding
TDI connection object.
Note: The connection returned from this function will contain
an unreferenced pointer to the owning endpoint. Only active
connections have references to the endpoint because the refcount
is used to decide when to clean up the list of idle connections.
Arguments:
AddressLength - Supplies the length of the TDI addresses used
by the transport associated with this connection.
ppConnection - Receives the pointer to a new UL_CONNECTION if
successful.
Return Value:
NTSTATUS - Completion status. *ppConnection is undefined if the
return value is not STATUS_SUCCESS.
--***************************************************************************/
NTSTATUS
UlpCreateConnection(
IN PUL_ENDPOINT pEndpoint,
IN ULONG AddressLength,
OUT PUL_CONNECTION *ppConnection
)
{
NTSTATUS status;
PUL_CONNECTION pConnection;
PSINGLE_LIST_ENTRY pSListEntry;
BOOLEAN FilterOnlySsl;
PUX_TDI_OBJECT pTdiObject;
ASSERT(NULL != ppConnection);
//
// Allocate the pool for the connection structure.
//
pConnection = UL_ALLOCATE_STRUCT(
NonPagedPool,
UL_CONNECTION,
UL_CONNECTION_POOL_TAG
);
if (pConnection == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fatal;
}
//
// One time field initialization.
//
pConnection->Signature = UL_CONNECTION_SIGNATURE;
ExInterlockedInsertTailList(
&g_TdiConnectionListHead,
&pConnection->GlobalConnectionListEntry,
KSPIN_LOCK_FROM_UL_SPIN_LOCK(&g_TdiSpinLock)
);
InterlockedIncrement((PLONG) &g_TdiConnectionCount);
pConnection->pConnectionContext = NULL;
pConnection->pOwningEndpoint = NULL;
#if ENABLE_OWNER_REF_TRACE
pConnection->pConnRefOwner = NULL;
pConnection->MonotonicId = 0;
#endif // ENABLE_OWNER_REF_TRACE
pConnection->FilterInfo.pFilterChannel = NULL;
HTTP_SET_NULL_ID( &pConnection->FilterInfo.ConnectionId );
pConnection->pIrp = NULL;
pConnection->ActiveListEntry.Flink = NULL;
pConnection->IdleSListEntry.Next = NULL;
//
// Initialize a private trace log.
//
CREATE_REF_TRACE_LOG( pConnection->pTraceLog,
96 - REF_TRACE_OVERHEAD, 0 );
CREATE_REF_TRACE_LOG( pConnection->HttpConnection.pTraceLog,
32 - REF_TRACE_OVERHEAD, 0 );
//
// Open the TDI connection object for this connection.
//
status = UxOpenTdiConnectionObject(
(CONNECTION_CONTEXT)pConnection,
&pConnection->ConnectionObject
);
if (!NT_SUCCESS(status))
{
goto fatal;
}
ASSERT( IS_VALID_TDI_OBJECT( &pConnection->ConnectionObject ) );
//
// Associate the connection with the endpoint.
//
status = UlpAssociateConnection( pConnection, pEndpoint );
if (!NT_SUCCESS(status))
{
goto fatal;
}
pTdiObject = &pConnection->ConnectionObject;
pConnection->pIrp = UlAllocateIrp(
pTdiObject->pDeviceObject->StackSize, // StackSize
FALSE // ChargeQuota
);
if (pConnection->pIrp == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fatal;
}
pConnection->pIrp->RequestorMode = KernelMode;
//
// Success!
//
UlTrace(TDI, (
"UlpCreateConnecton: created %p\n",
pConnection
));
*ppConnection = pConnection;
return STATUS_SUCCESS;
fatal:
UlTrace(TDI, (
"UlpCreateConnecton: failure %08lx\n",
status
));
ASSERT( !NT_SUCCESS(status) );
if (pConnection != NULL)
{
UlpDestroyConnection( pConnection );
}
*ppConnection = NULL;
return status;
} // UlpCreateConnection
/***************************************************************************++
Routine Description:
Initializes a UL_CONNECTION for use.
Note: inactive connections do not have a reference to the endpoint,
so the caller to this function *must* have a reference.
Arguments:
pConnection - Pointer to the UL_CONNECTION to initialize.
SecureConnection - TRUE if this connection is for a secure endpoint.
--***************************************************************************/
NTSTATUS
UlpInitializeConnection(
IN PUL_CONNECTION pConnection
)
{
NTSTATUS status;
BOOLEAN SecureConnection;
PUL_FILTER_CHANNEL pChannel;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pConnection);
ASSERT(IS_VALID_ENDPOINT(pConnection->pOwningEndpoint));
//
// Initialize locals.
//
status = STATUS_SUCCESS;
SecureConnection = pConnection->pOwningEndpoint->Secure;
//
// Initialize the easy parts.
//
pConnection->ReferenceCount = 1;
pConnection->ConnectionFlags.Value = 0;
pConnection->ActiveListEntry.Flink = NULL;
pConnection->Terminated = FALSE;
//
// Setup the Tdi Connection Information space to be filled with Local Address
// Information at the completion of the Accept Irp.
//
pConnection->TdiConnectionInformation.UserDataLength = 0;
pConnection->TdiConnectionInformation.UserData = NULL;
pConnection->TdiConnectionInformation.OptionsLength = 0;
pConnection->TdiConnectionInformation.Options = NULL;
pConnection->TdiConnectionInformation.RemoteAddressLength = sizeof(TA_IP_ADDRESS);
pConnection->TdiConnectionInformation.RemoteAddress = &(pConnection->IpAddress);
//
// Init the index to the ActiveConnectionLists.
//
if (g_UlNumberOfProcessors == 1)
{
pConnection->ActiveListIndex = 0;
}
else
{
pConnection->ActiveListIndex =
pConnection->pOwningEndpoint->ActiveConnectionIndex %
DEFAULT_MAX_CONNECTION_ACTIVE_LISTS;
pConnection->pOwningEndpoint->ActiveConnectionIndex += 1;
}
//
// Init the IrpContext.
//
pConnection->IrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
//
// Init the HTTP_CONNECTION.
//
pConnection->HttpConnection.RefCount = 0;
pChannel = UlQueryFilterChannel(SecureConnection);
status = UxInitializeFilterConnection(
&pConnection->FilterInfo,
pChannel,
SecureConnection,
&UlReferenceConnection,
&UlDereferenceConnection,
&UlpCloseRawConnection,
&UlpSendRawData,
&UlpReceiveRawData,
&UlpDummyReceiveHandler,
&UlpComputeHttpRawConnectionLength,
&UlpGenerateHttpRawConnectionInfo,
NULL,
pConnection->pOwningEndpoint,
pConnection
);
return status;
} // UlpInitializeConnection
/***************************************************************************++
Routine Description:
This function sets a new flag in the connection's flag set. The setting
of the flag is synchronized such that only one flag is set at a time.
Arguments:
pConnection - Supplies the connection whose flags are to be set.
NewFlag - Supplies a 32-bit value to be or-ed into the current flag set.
Return Value:
UL_CONNECTION_FLAGS - The new set of connection flags after the update.
--***************************************************************************/
UL_CONNECTION_FLAGS
UlpSetConnectionFlag(
IN OUT PUL_CONNECTION pConnection,
IN LONG NewFlag
)
{
UL_CONNECTION_FLAGS oldFlags;
UL_CONNECTION_FLAGS newFlags;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
do
{
//
// Capture the current value and initialize the new value.
//
newFlags.Value = oldFlags.Value =
*((volatile LONG *)&pConnection->ConnectionFlags.Value);
newFlags.Value |= NewFlag;
if (UlInterlockedCompareExchange(
&pConnection->ConnectionFlags.Value,
newFlags.Value,
oldFlags.Value
) == oldFlags.Value)
{
break;
}
} while (TRUE);
return newFlags;
} // UlpSetConnectionFlag
/***************************************************************************++
Routine Description:
Initiates a graceful disconnect on the specified connection.
Arguments:
pConnection - Supplies the connection to disconnect.
pCompletionRoutine - Supplies a pointer to a completion routine to
invoke after the connection is disconnected.
pCompletionContext - Supplies an uninterpreted context value for the
completion routine.
CleaningUp - TRUE if we're cleaning up the connection.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpBeginDisconnect(
IN PUL_CONNECTION pConnection,
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
IN PVOID pCompletionContext
)
{
PIRP pIrp;
UL_CONNECTION_FLAGS newFlags;
LONG flagsToSet;
PUL_IRP_CONTEXT pIrpContext;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
UlTrace(TDI, (
"UlpBeginDisconnect: connection %p\n",
pConnection
));
//
// Allocate and initialize an IRP context for this request.
//
pIrpContext = &pConnection->IrpContext;
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
pIrpContext->pConnectionContext = (PVOID)pConnection;
pIrpContext->pCompletionRoutine = pCompletionRoutine;
pIrpContext->pCompletionContext = pCompletionContext;
//
// Allocate and initialize an IRP for the disconnect. Note that we do
// this *before* manipulating the connection state so that we don't have
// to back out the state changes after an IRP allocation failure.
//
pIrp = pConnection->pIrp;
UxInitializeDisconnectIrp(
pIrp,
&pConnection->ConnectionObject,
TDI_DISCONNECT_RELEASE,
&UlpRestartDisconnect,
pIrpContext
);
//
// Add a reference to the connection
//
REFERENCE_CONNECTION( pConnection );
//
// Set the flag indicating that a disconnect is pending &
// we're cleaning up.
//
flagsToSet = MakeDisconnectPendingFlag();
flagsToSet |= MakeCleanupBegunFlag();
newFlags = UlpSetConnectionFlag( pConnection, flagsToSet );
//
// Then call the driver to initiate
// the disconnect.
//
UlCallDriver( pConnection->ConnectionObject.pDeviceObject, pIrp );
return STATUS_PENDING;
} // UlpBeginDisconnect
/***************************************************************************++
Routine Description:
Completion handler for graceful disconnect IRPs.
Arguments:
pDeviceObject - Supplies the device object for the IRP being
completed.
pIrp - Supplies the IRP being completed.
pContext - Supplies the context associated with this request.
This is actually a PUL_IRP_CONTEXT.
Return Value:
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
this IRP.
--***************************************************************************/
NTSTATUS
UlpRestartDisconnect(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID pContext
)
{
PUL_IRP_CONTEXT pIrpContext;
PUL_CONNECTION pConnection;
UL_CONNECTION_FLAGS newFlags;
PUL_ENDPOINT pEndpoint;
//
// Sanity check.
//
pIrpContext = (PUL_IRP_CONTEXT)pContext;
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
pConnection = (PUL_CONNECTION)pIrpContext->pConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pConnection->ConnectionFlags.DisconnectPending );
UlTrace(TDI, (
"UlpRestartDisconnect: connection %p\n",
pConnection
));
pEndpoint = pConnection->pOwningEndpoint;
newFlags.Value =
*((volatile LONG *)&pConnection->ConnectionFlags.Value);
if (!newFlags.DisconnectIndicated &&
!newFlags.AbortIndicated &&
pConnection->FilterInfo.pFilterChannel == NULL
)
{
// Only try to drain if its non-filter connection. Also it's not
// necessary to drain if the connection has already been aborted.
// CODEWORK: Filter code should also be updated to introduce the
// same drain functionality.
(pEndpoint->pConnectionDisconnectCompleteHandler)(
pEndpoint->pListeningContext,
pConnection->pConnectionContext
);
}
//
// Set the flag indicating that a disconnect has completed. If we're
// in the midst of cleaning up this endpoint and we've already received
// a disconnect (graceful or abortive) from the client, then remove the
// final reference to the connection.
//
newFlags = UlpSetConnectionFlag(
pConnection,
MakeDisconnectCompleteFlag()
);
UlpRemoveFinalReference( pConnection, newFlags );
//
// Invoke the user's completion routine, then free the IRP context.
//
if (pIrpContext->pCompletionRoutine)
{
(pIrpContext->pCompletionRoutine)(
pIrpContext->pCompletionContext,
pIrp->IoStatus.Status,
pIrp->IoStatus.Information
);
}
DEREFERENCE_CONNECTION( pConnection );
return STATUS_MORE_PROCESSING_REQUIRED;
} // UlpRestartDisconnect
/***************************************************************************++
Routine Description:
Initiates an abortive disconnect on the specified connection.
Arguments:
pConnection - Supplies the connection to disconnect.
pCompletionRoutine - Supplies a pointer to a completion routine to
invoke after the connection is disconnected.
pCompletionContext - Supplies an uninterpreted context value for the
completion routine.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpBeginAbort(
IN PUL_CONNECTION pConnection,
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
IN PVOID pCompletionContext
)
{
PIRP pIrp;
UL_CONNECTION_FLAGS newFlags;
LONG flagsToSet;
PUL_IRP_CONTEXT pIrpContext;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
UlTrace(TDI, (
"UlpBeginAbort: connection %p\n",
pConnection
));
//
// Allocate and initialize an IRP context for this request.
//
pIrpContext = &pConnection->IrpContext;
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
pIrpContext->pConnectionContext = (PVOID)pConnection;
pIrpContext->pCompletionRoutine = pCompletionRoutine;
pIrpContext->pCompletionContext = pCompletionContext;
//
// Allocate and initialize an IRP for the disconnect. Note that we do
// this *before* manipulating the connection state so that we don't have
// to back out the state changes after an IRP allocation failure.
//
pIrp = pConnection->pIrp;
UxInitializeDisconnectIrp(
pIrp,
&pConnection->ConnectionObject,
TDI_DISCONNECT_ABORT,
&UlpRestartAbort,
pIrpContext
);
//
// Add a reference to the connection,
//
REFERENCE_CONNECTION( pConnection );
//
// Set the flag indicating that a disconnect is pending &
// we're cleaning up.
//
flagsToSet = MakeAbortPendingFlag();
flagsToSet |= MakeCleanupBegunFlag();
newFlags = UlpSetConnectionFlag( pConnection, flagsToSet );
//
// Then call the driver to initiate the disconnect.
//
UlCallDriver( pConnection->ConnectionObject.pDeviceObject, pIrp );
return STATUS_PENDING;
} // UlpBeginAbort
/***************************************************************************++
Routine Description:
Completion handler for abortive disconnect IRPs.
Arguments:
pDeviceObject - Supplies the device object for the IRP being
completed.
pIrp - Supplies the IRP being completed.
pContext - Supplies the context associated with this request.
This is actually a PUL_IRP_CONTEXT.
Return Value:
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
this IRP.
--***************************************************************************/
NTSTATUS
UlpRestartAbort(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID pContext
)
{
PUL_IRP_CONTEXT pIrpContext;
PUL_CONNECTION pConnection;
UL_CONNECTION_FLAGS newFlags;
//
// Sanity check.
//
pIrpContext = (PUL_IRP_CONTEXT)pContext;
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
pConnection = (PUL_CONNECTION)pIrpContext->pConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pConnection->ConnectionFlags.AbortPending );
UlTrace(TDI, (
"UlpRestartAbort: connection %p\n",
pConnection
));
//
// Set the flag indicating that an abort has completed. If we're in the
// midst of cleaning up this endpoint and we've already received a
// disconnect (graceful or abortive) from the client, then remove the
// final reference to the connection.
//
newFlags = UlpSetConnectionFlag(
pConnection,
MakeAbortCompleteFlag()
);
UlpRemoveFinalReference( pConnection, newFlags );
//
// Invoke the user's completion routine, then free the IRP context.
//
if (pIrpContext->pCompletionRoutine)
{
(pIrpContext->pCompletionRoutine)(
pIrpContext->pCompletionContext,
pIrp->IoStatus.Status,
pIrp->IoStatus.Information
);
}
DEREFERENCE_CONNECTION( pConnection );
return STATUS_MORE_PROCESSING_REQUIRED;
} // UlpRestartAbort
/***************************************************************************++
Routine Description:
Removes the final reference from a connection if the conditions are
right. See comments within this function for details on the conditions
required.
Arguments:
pConnection - Supplies the connection to dereference.
Flags - Supplies the connection flags from the most recent update.
Note:
It is very important that the caller of this routine has established
its own reference to the connection. If necessary, this reference
can be immediately removed after calling this routine, but not before.
--***************************************************************************/
VOID
UlpRemoveFinalReference(
IN PUL_CONNECTION pConnection,
IN UL_CONNECTION_FLAGS Flags
)
{
UL_CONNECTION_FLAGS oldFlags;
UL_CONNECTION_FLAGS newFlags;
//
// Sanity check.
//
ASSERT( IS_VALID_CONNECTION( pConnection ) );
//
// We can only remove the final reference if:
//
// We've begun connection cleanup.
//
// We've completed an accept.
//
// We've received a disconnect or abort indication or we've
// issued & completed an abort.
//
// We don't have a disconnect or abort pending.
//
// We haven't already removed it.
//
if (Flags.CleanupBegun &&
Flags.AcceptComplete &&
(Flags.DisconnectIndicated || Flags.AbortIndicated || Flags.AbortComplete) &&
(!Flags.DisconnectPending || Flags.DisconnectComplete) &&
(!Flags.AbortPending || Flags.AbortComplete) &&
!Flags.FinalReferenceRemoved)
{
//
// It looks like we may be able to remove the final reference.
// Attempt to set the "FinalReferenceRemoved" flag and determine
// if this thread is the one that actually needs to remove it.
//
do
{
//
// Capture the current flags and initialize the proposed
// new value.
//
newFlags.Value = oldFlags.Value =
*((volatile LONG *)&pConnection->ConnectionFlags.Value);
newFlags.Value |= MakeFinalReferenceRemovedFlag();
if (UlInterlockedCompareExchange(
&pConnection->ConnectionFlags.Value,
newFlags.Value,
oldFlags.Value
) == oldFlags.Value)
{
break;
}
} while (TRUE);
//
// See if WE actually set the flag.
//
if (!oldFlags.FinalReferenceRemoved)
{
UlTrace(TDI, (
"UlpRemoveFinalReference: connection %p\n",
pConnection
));
//
// Tell the client that the connection is now fully destroyed.
//
(pConnection->pConnectionDestroyedHandler)(
pConnection->pListeningContext,
pConnection->pConnectionContext
);
//
// Unbind from the endpoint if we're still attached.
// This allows it to release any refs it has on the connection.
//
UlpUnbindConnectionFromEndpoint(pConnection);
//
// Release the filter channel.
// This allows it to release any refs it has on the connection.
//
if (pConnection->FilterInfo.pFilterChannel)
{
UlUnbindConnectionFromFilter(&pConnection->FilterInfo);
}
//
// Remove the final reference.
//
DEREFERENCE_CONNECTION( pConnection );
WRITE_OWNER_REF_TRACE_LOG(
pConnection->pOwningEndpoint->pOwnerRefTraceLog,
pConnection,
&pConnection->pConnRefOwner,
UL_CONNECTION_SIGNATURE,
REF_ACTION_FINAL_DEREF,
-1, // newrefct: ignored
pConnection->MonotonicId,
0, // don't adjust local ref count
__FILE__,
__LINE__
);
}
}
else
{
UlTrace(TDI, (
"UlpRemoveFinalReference: cannot remove %p, flags = %08lx:\n",
pConnection,
Flags.Value
));
}
} // UlpRemoveFinalReference
/***************************************************************************++
Routine Description:
Completion handler for receive IRPs passed back to the transport from
our receive indication handler.
Arguments:
pDeviceObject - Supplies the device object for the IRP being
completed.
pIrp - Supplies the IRP being completed.
pContext - Supplies the context associated with this request.
This is actually a PUL_RECEIVE_BUFFER.
Return Value:
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
this IRP.
--***************************************************************************/
NTSTATUS
UlpRestartReceive(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID pContext
)
{
NTSTATUS status;
PUL_RECEIVE_BUFFER pBuffer;
PUL_CONNECTION pConnection;
PUL_ENDPOINT pEndpoint;
PUX_TDI_OBJECT pTdiObject;
ULONG bytesAvailable;
ULONG bytesTaken;
ULONG bytesRemaining;
//
// Sanity check.
//
pBuffer = (PUL_RECEIVE_BUFFER) pContext;
ASSERT( IS_VALID_RECEIVE_BUFFER( pBuffer ) );
pConnection = (PUL_CONNECTION) pBuffer->pConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
pTdiObject = &pConnection->ConnectionObject;
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
pEndpoint = pConnection->pOwningEndpoint;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
//
// The connection could be destroyed before we get a chance to
// receive the completion for the receive IRP. In that case the
// irp status won't be success but STATUS_CONNECTION_RESET or similar.
// We should not attempt to pass this case to the client.
//
status = pBuffer->pIrp->IoStatus.Status;
if ( status != STATUS_SUCCESS )
{
//
// The HttpConnection has already been destroyed
// or receive completion failed for some reason.
// No need to go to client.
//
goto end;
}
//
// Fake a receive indication to the client.
//
pBuffer->UnreadDataLength += (ULONG)pBuffer->pIrp->IoStatus.Information;
bytesTaken = 0;
UlTrace(TDI, (
"UlpRestartReceive: endpoint %p, connection %p, length %lu\n",
pEndpoint,
pConnection,
pBuffer->UnreadDataLength
));
//
// Pass the data on.
//
if (pConnection->FilterInfo.pFilterChannel)
{
//
// Needs to go through a filter.
//
status = UlFilterReceiveHandler(
&pConnection->FilterInfo,
pBuffer->pDataArea,
pBuffer->UnreadDataLength,
0,
&bytesTaken
);
}
else
{
//
// Go directly to client.
//
status = (pEndpoint->pDataReceiveHandler)(
pEndpoint->pListeningContext,
pConnection->pConnectionContext,
pBuffer->pDataArea,
pBuffer->UnreadDataLength,
0,
&bytesTaken
);
}
ASSERT( bytesTaken <= pBuffer->UnreadDataLength );
//
// Note that this basically duplicates the logic that's currently in
// UlpReceiveHandler.
//
if (status == STATUS_MORE_PROCESSING_REQUIRED)
{
//
// The client consumed part of the indicated data.
//
// We'll need to copy the untaken data forward within the receive
// buffer, build an MDL describing the remaining part of the buffer,
// then repost the receive IRP.
//
bytesRemaining = pBuffer->UnreadDataLength - bytesTaken;
//
// Do we have enough buffer space for more?
//
if (bytesRemaining < g_UlReceiveBufferSize)
{
//
// Move the unread portion of the buffer to the beginning.
//
RtlMoveMemory(
pBuffer->pDataArea,
(PUCHAR)pBuffer->pDataArea + bytesTaken,
bytesRemaining
);
pBuffer->UnreadDataLength = bytesRemaining;
//
// Build a partial mdl representing the remainder of the
// buffer.
//
IoBuildPartialMdl(
pBuffer->pMdl, // SourceMdl
pBuffer->pPartialMdl, // TargetMdl
(PUCHAR)pBuffer->pDataArea + bytesRemaining,// VirtualAddress
g_UlReceiveBufferSize - bytesRemaining // Length
);
//
// Finish initializing the IRP.
//
TdiBuildReceive(
pBuffer->pIrp, // Irp
pTdiObject->pDeviceObject, // DeviceObject
pTdiObject->pFileObject, // FileObject
&UlpRestartReceive, // CompletionRoutine
pBuffer, // CompletionContext
pBuffer->pPartialMdl, // MdlAddress
TDI_RECEIVE_NORMAL, // Flags
g_UlReceiveBufferSize - bytesRemaining // Length
);
UlTrace(TDI, (
"UlpRestartReceive: connection %p, reusing irp %p to grab more data\n",
pConnection,
pBuffer->pIrp
));
//
// Call the driver.
//
UlCallDriver( pTdiObject->pDeviceObject, pIrp );
//
// Tell IO to stop processing this request.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
status = STATUS_BUFFER_OVERFLOW;
}
end:
if (status != STATUS_SUCCESS)
{
//
// The client failed the indication. Abort the connection.
//
//
// BUGBUG need to add code to return a response
//
UlpCloseRawConnection(
pConnection,
TRUE, // AbortiveDisconnect
NULL, // pCompletionRoutine
NULL // pCompletionContext
);
}
//
// Remove the connection we added in the receive indication handler,
// free the receive buffer, then tell IO to stop processing the IRP.
//
DEREFERENCE_CONNECTION( pConnection );
UlPplFreeReceiveBuffer( pBuffer );
UlTrace(TDI, (
"UlpRestartReceive: endpoint %p, connection %p, length %lu, taken %lu, status %x\n",
pEndpoint,
pConnection,
pBuffer->UnreadDataLength,
bytesTaken,
status
));
return STATUS_MORE_PROCESSING_REQUIRED;
} // UlpRestartReceive
/***************************************************************************++
Routine Description:
Completion handler for receive IRPs initiated from UlReceiveData().
Arguments:
pDeviceObject - Supplies the device object for the IRP being
completed.
pIrp - Supplies the IRP being completed.
pContext - Supplies the context associated with this request.
This is actually a PUL_IRP_CONTEXT.
Return Value:
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
this IRP.
--***************************************************************************/
NTSTATUS
UlpRestartClientReceive(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID pContext
)
{
PUL_IRP_CONTEXT pIrpContext;
PUL_CONNECTION pConnection;
//
// Sanity check.
//
pIrpContext= (PUL_IRP_CONTEXT)pContext;
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
pConnection = (PUL_CONNECTION)pIrpContext->pConnectionContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
UlTrace(TDI, (
"UlpRestartClientReceive: irp %p, connection %p, status %08lx\n",
pIrp,
pConnection,
pIrp->IoStatus.Status
));
//
// Invoke the client's completion handler.
//
(pIrpContext->pCompletionRoutine)(
pIrpContext->pCompletionContext,
pIrp->IoStatus.Status,
pIrp->IoStatus.Information
);
//
// Free the IRP context we allocated.
//
UlPplFreeIrpContext(pIrpContext);
//
// IO can't handle completing an IRP with a non-paged MDL attached
// to it, so we'll free the MDL here.
//
ASSERT( pIrp->MdlAddress != NULL );
UlFreeMdl( pIrp->MdlAddress );
pIrp->MdlAddress = NULL;
//
// Remove the connection we added in UlReceiveData()
//
DEREFERENCE_CONNECTION( pConnection );
//
// Free the IRP since we're done with it, then tell IO to
// stop processing the IRP.
//
UlFreeIrp(pIrp);
return STATUS_MORE_PROCESSING_REQUIRED;
} // UlpRestartClientReceive
/***************************************************************************++
Routine Description:
Removes all active connections from the specified endpoint and initiates
abortive disconnects.
Arguments:
pEndpoint - Supplies the endpoint to purge.
Return Value:
NTSTATUS - completion status
--***************************************************************************/
NTSTATUS
UlpDisconnectAllActiveConnections(
IN PUL_ENDPOINT pEndpoint
)
{
KIRQL oldIrql;
PLIST_ENTRY pListEntry;
PUL_CONNECTION pConnection;
PUL_IRP_CONTEXT pIrpContext = &pEndpoint->CleanupIrpContext;
NTSTATUS Status;
UL_STATUS_BLOCK ulStatus;
LONG i;
//
// Sanity check.
//
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
UlTrace(TDI, (
"UlpDisconnectAllActiveConnections: endpoint %p\n",
pEndpoint
));
//
// This routine is not pageable because it must acquire a spinlock.
// However, it must be called at passive IRQL because it must
// block on an event object.
//
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
//
// Initialize a status block. We'll pass a pointer to this as
// the completion context to UlpCloseRawConnection(). The
// completion routine will update the status block and signal
// the event.
//
UlInitializeStatusBlock( &ulStatus );
//
// Loop through all of the active connections.
//
for (i = 0; i < DEFAULT_MAX_CONNECTION_ACTIVE_LISTS; i++)
{
for (;;)
{
//
// Remove an active connection.
//
UlAcquireSpinLock(
&pEndpoint->ActiveConnectionSpinLock[i],
&oldIrql
);
pListEntry = RemoveHeadList( &pEndpoint->ActiveConnectionListHead[i] );
if (pListEntry == &pEndpoint->ActiveConnectionListHead[i])
{
UlReleaseSpinLock(
&pEndpoint->ActiveConnectionSpinLock[i],
oldIrql
);
break;
}
//
// Validate the connection.
//
pConnection = CONTAINING_RECORD(
pListEntry,
UL_CONNECTION,
ActiveListEntry
);
ASSERT( IS_VALID_CONNECTION( pConnection ) );
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
pConnection->ActiveListEntry.Flink = NULL;
WRITE_OWNER_REF_TRACE_LOG(
pEndpoint->pOwnerRefTraceLog,
pConnection,
&pConnection->pConnRefOwner,
UL_CONNECTION_SIGNATURE,
REF_ACTION_DISCONN_ALL,
-1, // newrefct: ignored
pConnection->MonotonicId,
0, // don't adjust local ref count
__FILE__,
__LINE__
);
UlReleaseSpinLock(
&pEndpoint->ActiveConnectionSpinLock[i],
oldIrql
);
//
// Abort it.
//
UlResetStatusBlockEvent( &ulStatus );
Status = UlpCloseRawConnection(
pConnection,
TRUE,
&UlpSynchronousIoComplete,
&ulStatus
);
ASSERT( Status == STATUS_PENDING );
//
// Wait for it to complete.
//
UlWaitForStatusBlockEvent( &ulStatus );
//
// Remove the active list's reference.
//
DEREFERENCE_CONNECTION(pConnection);
}
}
//
// No active connections, nuke the endpoint.
//
// We must set the IRP context in the endpoint so that the
// completion will be invoked when the endpoint's reference
// count drops to zero. Since the completion routine may be
// invoked at a later time, we always return STATUS_PENDING.
//
pIrpContext->pConnectionContext = (PVOID)pEndpoint;
DEREFERENCE_ENDPOINT_SELF(pEndpoint, REF_ACTION_DISCONN_ACTIVE);
return STATUS_PENDING;
} // UlpDisconnectAllActiveConnections
/***************************************************************************++
Routine Description:
Unbinds an active connection from the endpoint. If the connection
is on the active list this routine removes it and drops the
list's reference to the connection.
Arguments:
pConnection - the connection to unbind
--***************************************************************************/
VOID
UlpUnbindConnectionFromEndpoint(
IN PUL_CONNECTION pConnection
)
{
KIRQL oldIrql;
PUL_ENDPOINT pEndpoint;
BOOLEAN Dereference;
//
// Sanity check.
//
ASSERT(IS_VALID_CONNECTION(pConnection));
pEndpoint = pConnection->pOwningEndpoint;
ASSERT(IS_VALID_ENDPOINT(pEndpoint));
//
// Init locals.
//
Dereference = FALSE;
//
// Unbind.
//
UlAcquireSpinLock(
&pEndpoint->ActiveConnectionSpinLock[pConnection->ActiveListIndex],
&oldIrql
);
if (pConnection->ActiveListEntry.Flink != NULL)
{
RemoveEntryList(&pConnection->ActiveListEntry);
pConnection->ActiveListEntry.Flink = NULL;
WRITE_OWNER_REF_TRACE_LOG(
pConnection->pOwningEndpoint->pOwnerRefTraceLog,
pConnection,
&pConnection->pConnRefOwner,
UL_CONNECTION_SIGNATURE,
REF_ACTION_UNBIND_CONN,
-1, // newrefct: ignored
pConnection->MonotonicId,
0, // don't adjust local ref count
__FILE__,
__LINE__
);
Dereference = TRUE;
}
UlReleaseSpinLock(
&pEndpoint->ActiveConnectionSpinLock[pConnection->ActiveListIndex],
oldIrql
);
//
// If the list had a reference, remove it.
//
if (Dereference)
{
DEREFERENCE_CONNECTION(pConnection);
}
} // UlpUnbindConnectionFromEndpoint
/***************************************************************************++
Routine Description:
Convert the input url to a TA_IP_ADDRESS.
Arguments:
pUrl - Supplies the URL to convert.
pAddress - Receives the address.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpUrlToAddress(
IN PWSTR pSiteUrl,
OUT PTA_IP_ADDRESS pAddress,
OUT PBOOLEAN pSecure
)
{
NTSTATUS status;
PWSTR pToken;
PWSTR pHost;
PWSTR pPort;
ULONG port;
UNICODE_STRING portString;
BOOLEAN secure;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pAddress);
ASSERT(pSecure);
//
// Find the first '/'.
//
pToken = wcschr(pSiteUrl, '/');
if (pToken == NULL)
{
return STATUS_INVALID_PARAMETER;
}
//
// Is this valid http url?
//
if (DIFF(pToken - pSiteUrl) == (sizeof(L"https:")-1)/sizeof(WCHAR))
{
if (_wcsnicmp(pSiteUrl, L"https:", (sizeof(L"https:")-1)/sizeof(WCHAR)) != 0)
{
return STATUS_INVALID_PARAMETER;
}
secure = TRUE;
}
else if (DIFF(pToken - pSiteUrl) == (sizeof(L"http:")-1)/sizeof(WCHAR))
{
if (_wcsnicmp(pSiteUrl, L"http:", (sizeof(L"http:")-1)/sizeof(WCHAR)) != 0)
{
return STATUS_INVALID_PARAMETER;
}
secure = FALSE;
}
else
{
return STATUS_INVALID_PARAMETER;
}
//
// Parse out the host name.
//
//
// Skip the second '/'.
//
if (pToken[1] != '/')
{
return STATUS_INVALID_PARAMETER;
}
pToken += 2;
pHost = pToken;
//
// Find the ':'.
//
pToken = wcschr(pToken, ':');
if (pToken == NULL)
{
return STATUS_INVALID_PARAMETER;
}
pToken += 1;
pPort = pToken;
//
// Find the end of the string (either UNICODE_NULL or '/' terminated).
//
while (pToken[0] != UNICODE_NULL && pToken[0] != L'/')
{
pToken += 1;
}
//
// Compute the port #.
//
portString.Buffer = pPort;
portString.Length = DIFF(pToken - pPort) * sizeof(WCHAR);
status = RtlUnicodeStringToInteger(&portString, 10, &port);
if (NT_SUCCESS(status) == FALSE)
{
return status;
}
if (port > 0xffff)
{
return STATUS_INVALID_PARAMETER;
}
//
// Is this an IP address or hostname?
//
//
// CODEWORK: crack the ip address out
//
UlInitializeIpTransportAddress(pAddress, 0, (USHORT)port);
*pSecure = secure;
return STATUS_SUCCESS;
} // UlpUrlToAddress
/***************************************************************************++
Routine Description:
Scan the endpoint list looking for one corresponding to the supplied
address.
Note: This routine assumes the TDI spinlock is held.
Arguments:
pAddress - Supplies the address to search for.
AddressLength - Supplies the length of the address structure.
Return Value:
PUL_ENDPOINT - The corresponding endpoint if successful, NULL otherwise.
--***************************************************************************/
PUL_ENDPOINT
UlpFindEndpointForAddress(
IN PTRANSPORT_ADDRESS pAddress,
IN ULONG AddressLength
)
{
PUL_ENDPOINT pEndpoint;
PLIST_ENTRY pListEntry;
//
// Sanity check.
//
ASSERT( UlDbgSpinLockOwned( &g_TdiSpinLock ) );
ASSERT( AddressLength == sizeof(TA_IP_ADDRESS) );
//
// Scan the endpoint list.
//
// CODEWORK: linear searches are BAD, if the list grows long.
// May need to augment this with a hash table or something.
//
for (pListEntry = g_TdiEndpointListHead.Flink ;
pListEntry != &g_TdiEndpointListHead ;
pListEntry = pListEntry->Flink)
{
pEndpoint = CONTAINING_RECORD(
pListEntry,
UL_ENDPOINT,
GlobalEndpointListEntry
);
if (pEndpoint->LocalAddressLength == AddressLength &&
RtlEqualMemory(pEndpoint->pLocalAddress, pAddress, AddressLength))
{
//
// Found the address; return it.
//
return pEndpoint;
}
}
//
// If we made it this far, then we did not find the address.
//
return NULL;
} // UlpFindEndpointForAddress
/***************************************************************************++
Routine Description:
Completion handler for synthetic synchronous IRPs.
Arguments:
pCompletionContext - Supplies an uninterpreted context value
as passed to the asynchronous API. In this case, this is
a pointer to a UL_STATUS_BLOCK structure.
Status - Supplies the final completion status of the
asynchronous API.
Information - Optionally supplies additional information about
the completed operation, such as the number of bytes
transferred. This field is unused for UlCloseListeningEndpoint().
--***************************************************************************/
VOID
UlpSynchronousIoComplete(
IN PVOID pCompletionContext,
IN NTSTATUS Status,
IN ULONG_PTR Information
)
{
PUL_STATUS_BLOCK pStatus;
//
// Snag the status block pointer.
//
pStatus = (PUL_STATUS_BLOCK)pCompletionContext;
//
// Update the completion status and signal the event.
//
UlSignalStatusBlock( pStatus, Status, Information );
} // UlpSynchronousIoComplete
/***************************************************************************++
Routine Description:
Enable/disable Nagle's Algorithm on the specified TDI connection object.
Arguments:
pTdiObject - Supplies the TDI connection object to manipulate.
Flag - Supplies TRUE to enable Nagling, FALSE to disable it.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpSetNagling(
IN PUX_TDI_OBJECT pTdiObject,
IN BOOLEAN Flag
)
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
PTCP_REQUEST_SET_INFORMATION_EX pSetInfoEx;
ULONG value;
UCHAR buffer[sizeof(*pSetInfoEx) - sizeof(pSetInfoEx->Buffer) + sizeof(value)];
//
// Sanity check.
//
PAGED_CODE();
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
//
// Note: NODELAY semantics are inverted from the usual enable/disable
// semantics.
//
value = (ULONG)!Flag;
//
// Setup the buffer.
//
pSetInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)buffer;
pSetInfoEx->ID.toi_entity.tei_entity = CO_TL_ENTITY;
pSetInfoEx->ID.toi_entity.tei_instance = TL_INSTANCE;
pSetInfoEx->ID.toi_class = INFO_CLASS_PROTOCOL;
pSetInfoEx->ID.toi_type = INFO_TYPE_CONNECTION;
pSetInfoEx->ID.toi_id = TCP_SOCKET_NODELAY;
pSetInfoEx->BufferSize = sizeof(value);
RtlCopyMemory( pSetInfoEx->Buffer, &value, sizeof(value) );
UlAttachToSystemProcess();
status = ZwDeviceIoControlFile(
pTdiObject->Handle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
IOCTL_TCP_SET_INFORMATION_EX, // IoControlCode
pSetInfoEx, // InputBuffer
sizeof(buffer), // InputBufferLength
NULL, // OutputBuffer
0 // OutputBufferLength
);
if (status == STATUS_PENDING)
{
status = ZwWaitForSingleObject(
pTdiObject->Handle, // Handle
TRUE, // Alertable
NULL // Timeout
);
ASSERT( NT_SUCCESS(status) );
status = ioStatusBlock.Status;
}
UlDetachFromSystemProcess();
return status;
} // UlpSetNagling
/***************************************************************************++
Routine Description:
Query the TCP fast send routine if fast send is possible.
Arguments:
Return Value:
--***************************************************************************/
NTSTATUS
UlpQueryTcpFastSend()
{
UNICODE_STRING TCPDeviceName;
PFILE_OBJECT pTCPFileObject;
PDEVICE_OBJECT pTCPDeviceObject;
PIRP Irp;
IO_STATUS_BLOCK StatusBlock;
KEVENT Event;
NTSTATUS status;
RtlInitUnicodeString(&TCPDeviceName, DD_TCP_DEVICE_NAME);
status = IoGetDeviceObjectPointer(
&TCPDeviceName,
FILE_ALL_ACCESS,
&pTCPFileObject,
&pTCPDeviceObject
);
if (!NT_SUCCESS(status))
{
return status;
}
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(
IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER,
pTCPDeviceObject,
&g_TcpFastSend,
sizeof(g_TcpFastSend),
NULL,
0,
FALSE,
&Event,
&StatusBlock);
if (Irp)
{
status = UlCallDriver(pTCPDeviceObject, Irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
NULL
);
status = StatusBlock.Status;
}
}
else
{
status = STATUS_NO_MEMORY;
}
ObDereferenceObject(pTCPFileObject);
return status;
} // UlpQueryTcpFastSend
/***************************************************************************++
Routine Description:
Build a receive buffer and IRP to TDI to get any pending data.
Arguments:
pTdiObject - Supplies the TDI connection object to manipulate.
pConnection - Supplies the UL_CONNECTION object.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpBuildTdiReceiveBuffer(
IN PUX_TDI_OBJECT pTdiObject,
IN PUL_CONNECTION pConnection,
OUT PIRP *pIrp
)
{
PUL_RECEIVE_BUFFER pBuffer;
pBuffer = UlPplAllocateReceiveBuffer();
if (pBuffer != NULL)
{
//
// Finish initializing the buffer and the IRP.
//
REFERENCE_CONNECTION( pConnection );
pBuffer->pConnectionContext = pConnection;
pBuffer->UnreadDataLength = 0;
TdiBuildReceive(
pBuffer->pIrp, // Irp
pTdiObject->pDeviceObject, // DeviceObject
pTdiObject->pFileObject, // FileObject
&UlpRestartReceive, // CompletionRoutine
pBuffer, // CompletionContext
pBuffer->pMdl, // MdlAddress
TDI_RECEIVE_NORMAL, // Flags
g_UlReceiveBufferSize // Length
);
UlTrace(TDI, (
"UlpBuildTdiReceiveBuffer: connection %p, "
"allocated irp %p to grab more data\n",
(PVOID)pConnection,
pBuffer->pIrp
));
//
// We must trace the IRP before we set the next stack
// location so the trace code can pull goodies from the
// IRP correctly.
//
TRACE_IRP( IRP_ACTION_CALL_DRIVER, pBuffer->pIrp );
//
// Pass the IRP back to the transport.
//
*pIrp = pBuffer->pIrp;
return STATUS_MORE_PROCESSING_REQUIRED;
}
return STATUS_INSUFFICIENT_RESOURCES;
} // UlpBuildTdiReceiveBuffer
/***************************************************************************++
Routine Description:
Returns the length required for HTTP_RAW_CONNECTION
Arguments:
pConnectionContext - Pointer to the UL_CONNECTION
--***************************************************************************/
ULONG
UlpComputeHttpRawConnectionLength(
IN PVOID pConnectionContext
)
{
return (sizeof(HTTP_RAW_CONNECTION_INFO) +
sizeof(HTTP_NETWORK_ADDRESS_IPV4) * 2
);
}
/***************************************************************************++
Routine Description:
Builds the HTTP_RAW_CONNECTION structure
Arguments:
pContext - Pointer to the UL_CONNECTION
pKernelBuffer - Pointer to kernel buffer
pUserBuffer - Pointer to user buffer
OutputBufferLength - Length of output buffer
pBuffer - Buffer for holding any data
InitialLength - Size of input data.
--***************************************************************************/
ULONG
UlpGenerateHttpRawConnectionInfo(
IN PVOID pContext,
IN PUCHAR pKernelBuffer,
IN PVOID pUserBuffer,
IN ULONG OutputBufferLength,
IN PUCHAR pBuffer,
IN ULONG InitialLength
)
{
PHTTP_RAW_CONNECTION_INFO pConnInfo;
PHTTP_NETWORK_ADDRESS_IPV4 pLocalAddress;
PHTTP_NETWORK_ADDRESS_IPV4 pRemoteAddress;
PHTTP_TRANSPORT_ADDRESS pAddress;
ULONG BytesCopied = 0;
PUCHAR pInitialData;
PUL_CONNECTION pConnection = (PUL_CONNECTION) pContext;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
pConnInfo = (PHTTP_RAW_CONNECTION_INFO) pKernelBuffer;
pLocalAddress = (PHTTP_NETWORK_ADDRESS_IPV4)( pConnInfo + 1 );
pRemoteAddress = pLocalAddress + 1;
pInitialData = (PUCHAR) (pRemoteAddress + 1);
//
// Now fill in the raw connection data structure.
// BUGBUG: handle other address types.
//
pConnInfo->ConnectionId = pConnection->FilterInfo.ConnectionId;
pAddress = &pConnInfo->Address;
pAddress->RemoteAddressLength = sizeof(HTTP_NETWORK_ADDRESS_IPV4);
pAddress->RemoteAddressType = HTTP_NETWORK_ADDRESS_TYPE_IPV4;
pAddress->pRemoteAddress = FIXUP_PTR(
PVOID,
pUserBuffer,
pKernelBuffer,
pRemoteAddress,
OutputBufferLength
);
pAddress->LocalAddressLength = sizeof(HTTP_NETWORK_ADDRESS_IPV4);
pAddress->LocalAddressType = HTTP_NETWORK_ADDRESS_TYPE_IPV4;
pAddress->pLocalAddress = FIXUP_PTR(
PVOID,
pUserBuffer,
pKernelBuffer,
pLocalAddress,
OutputBufferLength
);
pRemoteAddress->IpAddress = pConnection->RemoteAddress;
pRemoteAddress->Port = pConnection->RemotePort;
pLocalAddress->IpAddress = pConnection->LocalAddress;
pLocalAddress->Port = pConnection->LocalPort;
//
// Copy any initial data.
//
if (InitialLength)
{
ASSERT(pBuffer);
pConnInfo->InitialDataSize = InitialLength;
pConnInfo->pInitialData = FIXUP_PTR(
PVOID, // Type
pUserBuffer, // pUserPtr
pKernelBuffer, // pKernelPtr
pInitialData, // pOffsetPtr
OutputBufferLength // BufferLength
);
RtlCopyMemory(
pInitialData,
pBuffer,
InitialLength
);
BytesCopied += InitialLength;
}
return BytesCopied;
}