1768 lines
48 KiB
C
1768 lines
48 KiB
C
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
blkconn.c
|
||
|
||
Abstract:
|
||
|
||
This module contains allocate, free, close, reference, and dereference
|
||
routines for AFD connections.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 10-Mar-1992
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "afdp.h"
|
||
|
||
VOID
|
||
AfdFreeConnection (
|
||
IN PVOID Context
|
||
);
|
||
|
||
VOID
|
||
AfdFreeConnectionResources (
|
||
PAFD_CONNECTION connection
|
||
);
|
||
|
||
VOID
|
||
AfdFreeNPConnectionResources (
|
||
PAFD_CONNECTION connection
|
||
);
|
||
|
||
VOID
|
||
AfdRefreshConnection (
|
||
PAFD_CONNECTION connection
|
||
);
|
||
|
||
PAFD_CONNECTION
|
||
AfdReuseConnection (
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGEAFD, AfdAbortConnection )
|
||
#pragma alloc_text( PAGE, AfdAddFreeConnection )
|
||
#pragma alloc_text( PAGE, AfdAllocateConnection )
|
||
#pragma alloc_text( PAGE, AfdCreateConnection )
|
||
#pragma alloc_text( PAGE, AfdFreeConnection )
|
||
#pragma alloc_text( PAGE, AfdFreeConnectionResources )
|
||
#pragma alloc_text( PAGE, AfdReuseConnection )
|
||
#pragma alloc_text( PAGEAFD, AfdRefreshConnection )
|
||
#pragma alloc_text( PAGEAFD, AfdFreeNPConnectionResources )
|
||
#pragma alloc_text( PAGEAFD, AfdGetConnectionReferenceFromEndpoint )
|
||
#if REFERENCE_DEBUG
|
||
#pragma alloc_text( PAGEAFD, AfdReferenceConnection )
|
||
#pragma alloc_text( PAGEAFD, AfdDereferenceConnection )
|
||
#else
|
||
#pragma alloc_text( PAGEAFD, AfdCloseConnection )
|
||
#endif
|
||
#pragma alloc_text( PAGEAFD, AfdGetFreeConnection )
|
||
#pragma alloc_text( PAGEAFD, AfdGetReturnedConnection )
|
||
#pragma alloc_text( PAGEAFD, AfdFindReturnedConnection )
|
||
#pragma alloc_text( PAGEAFD, AfdGetUnacceptedConnection )
|
||
#pragma alloc_text( PAGEAFD, AfdAddConnectedReference )
|
||
#pragma alloc_text( PAGEAFD, AfdDeleteConnectedReference )
|
||
#endif
|
||
|
||
#if GLOBAL_REFERENCE_DEBUG
|
||
AFD_GLOBAL_REFERENCE_DEBUG AfdGlobalReference[MAX_GLOBAL_REFERENCE];
|
||
LONG AfdGlobalReferenceSlot = -1;
|
||
#endif
|
||
|
||
#if AFD_PERF_DBG
|
||
#define CONNECTION_REUSE_DISABLED (AfdDisableConnectionReuse)
|
||
#else
|
||
#define CONNECTION_REUSE_DISABLED (FALSE)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
AfdAbortConnection (
|
||
IN PAFD_CONNECTION Connection
|
||
)
|
||
{
|
||
|
||
NTSTATUS status;
|
||
|
||
ASSERT( Connection != NULL );
|
||
ASSERT( Connection->ConnectedReferenceAdded );
|
||
|
||
//
|
||
// Abort the connection. We need to set the CleanupBegun flag
|
||
// before initiating the abort so that the connected reference
|
||
// will get properly removed in AfdRestartAbort.
|
||
//
|
||
// Note that if AfdBeginAbort fails then AfdRestartAbort will not
|
||
// get invoked, so we must remove the connected reference ourselves.
|
||
//
|
||
|
||
Connection->CleanupBegun = TRUE;
|
||
status = AfdBeginAbort( Connection );
|
||
|
||
if( !NT_SUCCESS(status) ) {
|
||
AfdDeleteConnectedReference( Connection, FALSE );
|
||
}
|
||
|
||
//
|
||
// Remove the active reference.
|
||
//
|
||
|
||
DEREFERENCE_CONNECTION( Connection );
|
||
|
||
} // AfdAbortConnection
|
||
|
||
|
||
NTSTATUS
|
||
AfdAddFreeConnection (
|
||
IN PAFD_ENDPOINT Endpoint
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds a connection object to an endpoints pool of connections available
|
||
to satisfy a connect indication.
|
||
|
||
Arguments:
|
||
|
||
Endpoint - a pointer to the endpoint to which to add a connection.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates the status of the request.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAFD_CONNECTION connection;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE( );
|
||
|
||
ASSERT( Endpoint->Type == AfdBlockTypeVcListening ||
|
||
Endpoint->Type == AfdBlockTypeVcBoth );
|
||
|
||
//
|
||
// Create a new connection block and associated connection object.
|
||
//
|
||
|
||
status = AfdCreateConnection(
|
||
&Endpoint->TransportInfo->TransportDeviceName,
|
||
Endpoint->AddressHandle,
|
||
IS_TDI_BUFFERRING(Endpoint),
|
||
Endpoint->InLine,
|
||
Endpoint->OwningProcess,
|
||
&connection
|
||
);
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
ASSERT( IS_TDI_BUFFERRING(Endpoint) == connection->TdiBufferring );
|
||
|
||
if (IS_DELAYED_ACCEPTANCE_ENDPOINT (Endpoint)) {
|
||
status = AfdDelayedAcceptListen (Endpoint, connection);
|
||
if (!NT_SUCCESS (status)) {
|
||
DEREFERENCE_CONNECTION (connection);
|
||
}
|
||
}
|
||
else {
|
||
//
|
||
// Set up the handle in the listening connection structure and place
|
||
// the connection on the endpoint's list of listening connections.
|
||
//
|
||
|
||
ASSERT (connection->Endpoint==NULL);
|
||
InterlockedPushEntrySList(
|
||
&Endpoint->Common.VcListening.FreeConnectionListHead,
|
||
&connection->SListEntry);
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
} // AfdAddFreeConnection
|
||
|
||
|
||
PAFD_CONNECTION
|
||
AfdAllocateConnection (
|
||
VOID
|
||
)
|
||
{
|
||
PAFD_CONNECTION connection;
|
||
|
||
PAGED_CODE( );
|
||
|
||
if ((AfdConnectionsFreeing<AFD_CONNECTIONS_FREEING_MAX)
|
||
|| ((connection = AfdReuseConnection ())==NULL)) {
|
||
|
||
//
|
||
// Allocate a buffer to hold the connection structure.
|
||
//
|
||
|
||
connection = AFD_ALLOCATE_POOL(
|
||
NonPagedPool,
|
||
sizeof(AFD_CONNECTION),
|
||
AFD_CONNECTION_POOL_TAG
|
||
);
|
||
|
||
if ( connection == NULL ) {
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
RtlZeroMemory( connection, sizeof(AFD_CONNECTION) );
|
||
|
||
//
|
||
// Initialize the reference count to 1 to account for the caller's
|
||
// reference. Connection blocks are temporary--as soon as the last
|
||
// reference goes away, so does the connection. There is no active
|
||
// reference on a connection block.
|
||
//
|
||
|
||
connection->ReferenceCount = 1;
|
||
|
||
//
|
||
// Initialize the connection structure.
|
||
//
|
||
|
||
connection->Type = AfdBlockTypeConnection;
|
||
connection->State = AfdConnectionStateFree;
|
||
//connection->Handle = NULL;
|
||
//connection->FileObject = NULL;
|
||
//connection->RemoteAddress = NULL;
|
||
//connection->Endpoint = NULL;
|
||
//connection->ReceiveBytesIndicated = 0;
|
||
//connection->ReceiveBytesTaken = 0;
|
||
//connection->ReceiveBytesOutstanding = 0;
|
||
//connection->ReceiveExpeditedBytesIndicated = 0;
|
||
//connection->ReceiveExpeditedBytesTaken = 0;
|
||
//connection->ReceiveExpeditedBytesOutstanding = 0;
|
||
//connection->ConnectDataBuffers = NULL;
|
||
//connection->DisconnectIndicated = FALSE;
|
||
//connection->AbortIndicated = FALSE;
|
||
//connection->ConnectedReferenceAdded = FALSE;
|
||
//connection->SpecialCondition = FALSE;
|
||
//connection->CleanupBegun = FALSE;
|
||
//connection->OwningProcess = NULL;
|
||
//connection->ClosePendedTransmit = FALSE;
|
||
|
||
#if REFERENCE_DEBUG
|
||
connection->CurrentReferenceSlot = -1;
|
||
RtlZeroMemory(
|
||
&connection->ReferenceDebug,
|
||
sizeof(AFD_REFERENCE_DEBUG) * MAX_REFERENCE
|
||
);
|
||
#endif
|
||
|
||
//
|
||
// Return a pointer to the new connection to the caller.
|
||
//
|
||
|
||
IF_DEBUG(CONNECTION) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdAllocateConnection: connection at %p\n", connection ));
|
||
}
|
||
|
||
return connection;
|
||
|
||
} // AfdAllocateConnection
|
||
|
||
|
||
NTSTATUS
|
||
AfdCreateConnection (
|
||
IN PUNICODE_STRING TransportDeviceName,
|
||
IN HANDLE AddressHandle,
|
||
IN BOOLEAN TdiBufferring,
|
||
IN LOGICAL InLine,
|
||
IN PEPROCESS ProcessToCharge,
|
||
OUT PAFD_CONNECTION *Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Allocates a connection block and creates a connection object to
|
||
go with the block. This routine also associates the connection
|
||
with the specified address handle (if any).
|
||
|
||
Arguments:
|
||
|
||
TransportDeviceName - Name to use when creating the connection object.
|
||
|
||
AddressHandle - a handle to an address object for the specified
|
||
transport. If specified (non NULL), the connection object that
|
||
is created is associated with the address object.
|
||
|
||
TdiBufferring - whether the TDI provider supports data bufferring.
|
||
Only passed so that it can be stored in the connection
|
||
structure.
|
||
|
||
InLine - if TRUE, the endpoint should be created in OOB inline
|
||
mode.
|
||
|
||
ProcessToCharge - the process which should be charged the quota
|
||
for this connection.
|
||
|
||
Connection - receives a pointer to the new connection.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates the status of the request.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
CHAR eaBuffer[sizeof(FILE_FULL_EA_INFORMATION) - 1 +
|
||
TDI_CONNECTION_CONTEXT_LENGTH + 1 +
|
||
sizeof(CONNECTION_CONTEXT)];
|
||
PFILE_FULL_EA_INFORMATION ea;
|
||
CONNECTION_CONTEXT UNALIGNED *ctx;
|
||
PAFD_CONNECTION connection;
|
||
|
||
PAGED_CODE( );
|
||
|
||
|
||
//
|
||
// Attempt to charge this process quota for the data bufferring we
|
||
// will do on its behalf.
|
||
//
|
||
|
||
status = PsChargeProcessPoolQuota(
|
||
ProcessToCharge,
|
||
NonPagedPool,
|
||
sizeof (AFD_CONNECTION)
|
||
);
|
||
if (!NT_SUCCESS (status)) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
||
"AfdCreateConnection: PsChargeProcessPoolQuota failed.\n" ));
|
||
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Allocate a connection block.
|
||
//
|
||
|
||
connection = AfdAllocateConnection( );
|
||
|
||
if ( connection == NULL ) {
|
||
PsReturnPoolQuota(
|
||
ProcessToCharge,
|
||
NonPagedPool,
|
||
sizeof (AFD_CONNECTION)
|
||
);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
AfdRecordQuotaHistory(
|
||
ProcessToCharge,
|
||
(LONG)sizeof (AFD_CONNECTION),
|
||
"CreateConn ",
|
||
connection
|
||
);
|
||
|
||
AfdRecordPoolQuotaCharged(sizeof (AFD_CONNECTION));
|
||
|
||
//
|
||
// Remember the process that got charged the pool quota for this
|
||
// connection object. Also reference the process to which we're
|
||
// going to charge the quota so that it is still around when we
|
||
// return the quota.
|
||
//
|
||
|
||
ASSERT( connection->OwningProcess == NULL );
|
||
connection->OwningProcess = ProcessToCharge;
|
||
|
||
ObReferenceObject( ProcessToCharge );
|
||
|
||
//
|
||
// If the provider does not buffer, initialize appropriate lists in
|
||
// the connection object.
|
||
//
|
||
|
||
connection->TdiBufferring = TdiBufferring;
|
||
|
||
if ( !TdiBufferring ) {
|
||
|
||
InitializeListHead( &connection->VcReceiveIrpListHead );
|
||
InitializeListHead( &connection->VcSendIrpListHead );
|
||
InitializeListHead( &connection->VcReceiveBufferListHead );
|
||
|
||
connection->VcBufferredReceiveBytes = 0;
|
||
connection->VcBufferredExpeditedBytes = 0;
|
||
connection->VcBufferredReceiveCount = 0;
|
||
connection->VcBufferredExpeditedCount = 0;
|
||
|
||
connection->VcReceiveBytesInTransport = 0;
|
||
|
||
#if DBG
|
||
connection->VcReceiveIrpsInTransport = 0;
|
||
#endif
|
||
|
||
connection->VcBufferredSendBytes = 0;
|
||
connection->VcBufferredSendCount = 0;
|
||
|
||
} else {
|
||
|
||
connection->VcNonBlockingSendPossible = TRUE;
|
||
connection->VcZeroByteReceiveIndicated = FALSE;
|
||
}
|
||
|
||
//
|
||
// Set up the send and receive window with default maximums.
|
||
//
|
||
|
||
connection->MaxBufferredReceiveBytes = AfdReceiveWindowSize;
|
||
|
||
connection->MaxBufferredSendBytes = AfdSendWindowSize;
|
||
|
||
//
|
||
// We need to open a connection object to the TDI provider for this
|
||
// endpoint. First create the EA for the connection context and the
|
||
// object attributes structure which will be used for all the
|
||
// connections we open here.
|
||
//
|
||
|
||
ea = (PFILE_FULL_EA_INFORMATION)eaBuffer;
|
||
ea->NextEntryOffset = 0;
|
||
ea->Flags = 0;
|
||
ea->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
|
||
ea->EaValueLength = sizeof(CONNECTION_CONTEXT);
|
||
|
||
RtlMoveMemory( ea->EaName, TdiConnectionContext, ea->EaNameLength + 1 );
|
||
|
||
//
|
||
// Use the pointer to the connection block as the connection context.
|
||
//
|
||
|
||
ctx = (CONNECTION_CONTEXT UNALIGNED *)&ea->EaName[ea->EaNameLength + 1];
|
||
*ctx = (CONNECTION_CONTEXT)connection;
|
||
|
||
// We ask to create a kernel handle which is
|
||
// the handle in the context of the system process
|
||
// so that application cannot close it on us while
|
||
// we are creating and referencing it.
|
||
InitializeObjectAttributes(
|
||
&objectAttributes,
|
||
TransportDeviceName,
|
||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // attributes
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Do the actual open of the connection object.
|
||
//
|
||
|
||
status = IoCreateFile(
|
||
&connection->Handle,
|
||
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
||
&objectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0, // FileAttributes
|
||
0, // ShareAccess
|
||
FILE_CREATE, // CreateDisposition
|
||
0, // CreateOptions
|
||
eaBuffer,
|
||
FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
|
||
ea->EaNameLength + 1 + ea->EaValueLength,
|
||
CreateFileTypeNone, // CreateFileType
|
||
NULL, // ExtraCreateParameters
|
||
IO_NO_PARAMETER_CHECKING // Options
|
||
);
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
if ( !NT_SUCCESS(status) ) {
|
||
DEREFERENCE_CONNECTION( connection );
|
||
return status;
|
||
}
|
||
|
||
#if DBG
|
||
{
|
||
NTSTATUS status1;
|
||
OBJECT_HANDLE_FLAG_INFORMATION handleInfo;
|
||
handleInfo.Inherit = FALSE;
|
||
handleInfo.ProtectFromClose = TRUE;
|
||
status1 = ZwSetInformationObject (
|
||
connection->Handle,
|
||
ObjectHandleFlagInformation,
|
||
&handleInfo,
|
||
sizeof (handleInfo)
|
||
);
|
||
ASSERT (NT_SUCCESS (status1));
|
||
}
|
||
#endif
|
||
AfdRecordConnOpened();
|
||
|
||
//
|
||
// Reference the connection's file object.
|
||
//
|
||
|
||
status = ObReferenceObjectByHandle(
|
||
connection->Handle,
|
||
0,
|
||
(POBJECT_TYPE) NULL,
|
||
KernelMode,
|
||
(PVOID *)&connection->FileObject,
|
||
NULL
|
||
);
|
||
|
||
ASSERT( NT_SUCCESS(status) );
|
||
|
||
|
||
IF_DEBUG(OPEN_CLOSE) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdCreateConnection: file object for connection %p at %p\n",
|
||
connection, connection->FileObject ));
|
||
}
|
||
|
||
AfdRecordConnRef();
|
||
|
||
//
|
||
// Remember the device object to which we need to give requests for
|
||
// this connection object. We can't just use the
|
||
// fileObject->DeviceObject pointer because there may be a device
|
||
// attached to the transport protocol.
|
||
//
|
||
|
||
connection->DeviceObject =
|
||
IoGetRelatedDeviceObject( connection->FileObject );
|
||
|
||
//
|
||
// Associate the connection with the address object on the endpoint if
|
||
// an address handle was specified.
|
||
//
|
||
|
||
if ( AddressHandle != NULL ) {
|
||
|
||
TDI_REQUEST_KERNEL_ASSOCIATE associateRequest;
|
||
|
||
associateRequest.AddressHandle = AddressHandle;
|
||
|
||
status = AfdIssueDeviceControl(
|
||
connection->FileObject,
|
||
&associateRequest,
|
||
sizeof (associateRequest),
|
||
NULL,
|
||
0,
|
||
TDI_ASSOCIATE_ADDRESS
|
||
);
|
||
if ( !NT_SUCCESS(status) ) {
|
||
DEREFERENCE_CONNECTION( connection );
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If requested, set the connection to be inline.
|
||
//
|
||
|
||
if ( InLine ) {
|
||
status = AfdSetInLineMode( connection, TRUE );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
DEREFERENCE_CONNECTION( connection );
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set up the connection pointer and return.
|
||
//
|
||
|
||
*Connection = connection;
|
||
|
||
UPDATE_CONN2( connection, "Connection object handle: %lx", HandleToUlong (connection->Handle));
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // AfdCreateConnection
|
||
|
||
|
||
VOID
|
||
AfdFreeConnection (
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PAFD_CONNECTION connection;
|
||
PAFD_ENDPOINT listenEndpoint;
|
||
|
||
PAGED_CODE( );
|
||
|
||
InterlockedDecrement (&AfdConnectionsFreeing);
|
||
ASSERT( Context != NULL );
|
||
|
||
connection = CONTAINING_RECORD(
|
||
Context,
|
||
AFD_CONNECTION,
|
||
WorkItem
|
||
);
|
||
|
||
if (connection->Endpoint != NULL &&
|
||
!CONNECTION_REUSE_DISABLED &&
|
||
!connection->Endpoint->EndpointCleanedUp &&
|
||
connection->Endpoint->Type == AfdBlockTypeVcConnecting &&
|
||
(listenEndpoint=connection->Endpoint->Common.VcConnecting.ListenEndpoint) != NULL &&
|
||
-listenEndpoint->Common.VcListening.FailedConnectionAdds <
|
||
listenEndpoint->Common.VcListening.MaxExtraConnections &&
|
||
(IS_DELAYED_ACCEPTANCE_ENDPOINT (listenEndpoint) ||
|
||
ExQueryDepthSList (
|
||
&listenEndpoint->Common.VcListening.FreeConnectionListHead)
|
||
< AFD_MAXIMUM_FREE_CONNECTIONS ) ) {
|
||
|
||
AfdRefreshConnection (connection);
|
||
}
|
||
else {
|
||
AfdFreeConnectionResources (connection);
|
||
|
||
//
|
||
// Free the space that holds the connection itself.
|
||
//
|
||
|
||
IF_DEBUG(CONNECTION) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFreeConnection: Freeing connection at %p\n",
|
||
connection ));
|
||
}
|
||
|
||
connection->Type = AfdBlockTypeInvalidConnection;
|
||
|
||
AFD_FREE_POOL(
|
||
connection,
|
||
AFD_CONNECTION_POOL_TAG
|
||
);
|
||
}
|
||
|
||
} // AfdFreeConnection
|
||
|
||
|
||
PAFD_CONNECTION
|
||
AfdReuseConnection (
|
||
) {
|
||
PAFD_CONNECTION connection;
|
||
PAFD_ENDPOINT listenEndpoint;
|
||
PVOID Context;
|
||
|
||
PAGED_CODE( );
|
||
|
||
while ((Context = AfdGetWorkerByRoutine (AfdFreeConnection))!=NULL) {
|
||
connection = CONTAINING_RECORD(
|
||
Context,
|
||
AFD_CONNECTION,
|
||
WorkItem
|
||
);
|
||
if (connection->Endpoint != NULL &&
|
||
!CONNECTION_REUSE_DISABLED &&
|
||
!connection->Endpoint->EndpointCleanedUp &&
|
||
connection->Endpoint->Type == AfdBlockTypeVcConnecting &&
|
||
(listenEndpoint=connection->Endpoint->Common.VcConnecting.ListenEndpoint) != NULL &&
|
||
-listenEndpoint->Common.VcListening.FailedConnectionAdds <
|
||
listenEndpoint->Common.VcListening.MaxExtraConnections &&
|
||
(IS_DELAYED_ACCEPTANCE_ENDPOINT (listenEndpoint) ||
|
||
ExQueryDepthSList (
|
||
&listenEndpoint->Common.VcListening.FreeConnectionListHead)
|
||
< AFD_MAXIMUM_FREE_CONNECTIONS ) ) {
|
||
AfdRefreshConnection (connection);
|
||
}
|
||
else {
|
||
AfdFreeConnectionResources (connection);
|
||
return connection;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
VOID
|
||
AfdFreeNPConnectionResources (
|
||
PAFD_CONNECTION connection
|
||
)
|
||
{
|
||
|
||
if ( !connection->TdiBufferring && connection->VcDisconnectIrp != NULL ) {
|
||
IoFreeIrp( connection->VcDisconnectIrp );
|
||
connection->VcDisconnectIrp = NULL;
|
||
}
|
||
|
||
if ( connection->ConnectDataBuffers != NULL ) {
|
||
AfdFreeConnectDataBuffers( connection->ConnectDataBuffers );
|
||
connection->ConnectDataBuffers = NULL;
|
||
}
|
||
|
||
//
|
||
// If this is a bufferring connection, remove all the AFD buffers
|
||
// from the connection's lists and free them.
|
||
//
|
||
|
||
if ( !connection->TdiBufferring ) {
|
||
|
||
PAFD_BUFFER_HEADER afdBuffer;
|
||
|
||
PLIST_ENTRY listEntry;
|
||
|
||
ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) );
|
||
ASSERT( IsListEmpty( &connection->VcSendIrpListHead ) );
|
||
|
||
while ( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) {
|
||
|
||
listEntry = RemoveHeadList( &connection->VcReceiveBufferListHead );
|
||
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER_HEADER, BufferListEntry );
|
||
ASSERT (afdBuffer->RefCount == 1);
|
||
|
||
afdBuffer->ExpeditedData = FALSE;
|
||
|
||
AfdReturnBuffer( afdBuffer, connection->OwningProcess );
|
||
}
|
||
}
|
||
|
||
if ( connection->Endpoint != NULL ) {
|
||
|
||
//
|
||
// If there is a transmit file IRP on the endpoint, complete it.
|
||
//
|
||
|
||
if ( connection->ClosePendedTransmit ) {
|
||
AfdCompleteClosePendedTPackets( connection->Endpoint );
|
||
}
|
||
|
||
DEREFERENCE_ENDPOINT( connection->Endpoint );
|
||
connection->Endpoint = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
AfdRefreshConnection (
|
||
PAFD_CONNECTION connection
|
||
)
|
||
{
|
||
PAFD_ENDPOINT listeningEndpoint;
|
||
|
||
ASSERT( connection->ReferenceCount == 0 );
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
ASSERT( connection->OnLRList == FALSE );
|
||
|
||
UPDATE_CONN( connection);
|
||
|
||
//
|
||
// Reference the listening endpoint so that it does not
|
||
// go away while we are cleaning up this connection object
|
||
// for reuse. Note that we actually have an implicit reference
|
||
// to the listening endpoint through the connection's endpoint
|
||
//
|
||
|
||
listeningEndpoint = connection->Endpoint->Common.VcConnecting.ListenEndpoint;
|
||
|
||
#if REFERENCE_DEBUG
|
||
{
|
||
BOOLEAN res;
|
||
CHECK_REFERENCE_ENDPOINT (listeningEndpoint, res);
|
||
ASSERT (res);
|
||
}
|
||
#else
|
||
REFERENCE_ENDPOINT( listeningEndpoint );
|
||
#endif
|
||
|
||
ASSERT( listeningEndpoint->Type == AfdBlockTypeVcListening ||
|
||
listeningEndpoint->Type == AfdBlockTypeVcBoth );
|
||
|
||
AfdFreeNPConnectionResources (connection);
|
||
|
||
|
||
//
|
||
// Reinitialize various fields in the connection object.
|
||
//
|
||
|
||
connection->ReferenceCount = 1;
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
connection->State = AfdConnectionStateFree;
|
||
|
||
connection->ConnectionStateFlags = 0;
|
||
|
||
connection->TdiBufferring = IS_TDI_BUFFERRING (listeningEndpoint);
|
||
if ( !connection->TdiBufferring ) {
|
||
|
||
ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) );
|
||
ASSERT( IsListEmpty( &connection->VcSendIrpListHead ) );
|
||
ASSERT( IsListEmpty( &connection->VcReceiveBufferListHead ) );
|
||
|
||
connection->VcBufferredReceiveBytes = 0;
|
||
connection->VcBufferredExpeditedBytes = 0;
|
||
connection->VcBufferredReceiveCount = 0;
|
||
connection->VcBufferredExpeditedCount = 0;
|
||
|
||
connection->VcReceiveBytesInTransport = 0;
|
||
#if DBG
|
||
connection->VcReceiveIrpsInTransport = 0;
|
||
#endif
|
||
|
||
connection->VcBufferredSendBytes = 0;
|
||
connection->VcBufferredSendCount = 0;
|
||
|
||
} else {
|
||
|
||
connection->VcNonBlockingSendPossible = TRUE;
|
||
connection->VcZeroByteReceiveIndicated = FALSE;
|
||
}
|
||
|
||
if (IS_DELAYED_ACCEPTANCE_ENDPOINT (listeningEndpoint)) {
|
||
NTSTATUS status;
|
||
status = AfdDelayedAcceptListen (listeningEndpoint, connection);
|
||
if (NT_SUCCESS (status)) {
|
||
//
|
||
// Reduce the count of failed connection adds on the listening
|
||
// endpoint to account for this connection object which we're
|
||
// adding back onto the queue.
|
||
//
|
||
|
||
InterlockedDecrement(
|
||
&listeningEndpoint->Common.VcListening.FailedConnectionAdds
|
||
);
|
||
|
||
AfdRecordConnectionsReused ();
|
||
}
|
||
else {
|
||
DEREFERENCE_CONNECTION (connection);
|
||
}
|
||
}
|
||
else {
|
||
//
|
||
// Place the connection on the listening endpoint's list of
|
||
// available connections.
|
||
//
|
||
|
||
ASSERT (connection->Endpoint == NULL);
|
||
InterlockedPushEntrySList(
|
||
&listeningEndpoint->Common.VcListening.FreeConnectionListHead,
|
||
&connection->SListEntry);
|
||
//
|
||
// Reduce the count of failed connection adds on the listening
|
||
// endpoint to account for this connection object which we're
|
||
// adding back onto the queue.
|
||
//
|
||
|
||
InterlockedDecrement(
|
||
&listeningEndpoint->Common.VcListening.FailedConnectionAdds
|
||
);
|
||
|
||
AfdRecordConnectionsReused ();
|
||
}
|
||
|
||
|
||
//
|
||
// Get rid of the reference we added to the listening endpoint
|
||
// above.
|
||
//
|
||
|
||
DEREFERENCE_ENDPOINT( listeningEndpoint );
|
||
}
|
||
|
||
|
||
VOID
|
||
AfdFreeConnectionResources (
|
||
PAFD_CONNECTION connection
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE( );
|
||
|
||
ASSERT( connection->ReferenceCount == 0 );
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
ASSERT( connection->OnLRList == FALSE );
|
||
|
||
UPDATE_CONN( connection );
|
||
|
||
|
||
//
|
||
// Free and dereference the various objects on the connection.
|
||
// Close and dereference the TDI connection object on the endpoint,
|
||
// if any.
|
||
//
|
||
|
||
if ( connection->Handle != NULL ) {
|
||
|
||
|
||
//
|
||
// Disassociate this connection object from the address object.
|
||
//
|
||
|
||
status = AfdIssueDeviceControl(
|
||
connection->FileObject,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0,
|
||
TDI_DISASSOCIATE_ADDRESS
|
||
);
|
||
// ASSERT( NT_SUCCESS(status) );
|
||
|
||
|
||
//
|
||
// Close the handle.
|
||
//
|
||
|
||
#if DBG
|
||
{
|
||
NTSTATUS status1;
|
||
OBJECT_HANDLE_FLAG_INFORMATION handleInfo;
|
||
handleInfo.Inherit = FALSE;
|
||
handleInfo.ProtectFromClose = FALSE;
|
||
status1 = ZwSetInformationObject (
|
||
connection->Handle,
|
||
ObjectHandleFlagInformation,
|
||
&handleInfo,
|
||
sizeof (handleInfo)
|
||
);
|
||
ASSERT (NT_SUCCESS (status1));
|
||
}
|
||
#endif
|
||
status = ZwClose( connection->Handle );
|
||
|
||
#if DBG
|
||
if (!NT_SUCCESS(status) ) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_ERROR_LEVEL,
|
||
"AfdFreeConnectionResources: ZwClose() failed (%lx)\n",
|
||
status));
|
||
ASSERT (FALSE);
|
||
}
|
||
#endif
|
||
AfdRecordConnClosed();
|
||
}
|
||
|
||
if ( connection->FileObject != NULL ) {
|
||
|
||
ObDereferenceObject( connection->FileObject );
|
||
connection->FileObject = NULL;
|
||
AfdRecordConnDeref();
|
||
|
||
}
|
||
//
|
||
// Free remaining buffers and return quota charges associated with them.
|
||
//
|
||
|
||
AfdFreeNPConnectionResources (connection);
|
||
|
||
//
|
||
// Return the quota we charged to this process when we allocated
|
||
// the connection object and buffered data on it.
|
||
//
|
||
|
||
PsReturnPoolQuota(
|
||
connection->OwningProcess,
|
||
NonPagedPool,
|
||
sizeof (AFD_CONNECTION)
|
||
);
|
||
AfdRecordQuotaHistory(
|
||
connection->OwningProcess,
|
||
-(LONG)sizeof (AFD_CONNECTION),
|
||
"ConnDealloc ",
|
||
connection
|
||
);
|
||
AfdRecordPoolQuotaReturned(
|
||
sizeof (AFD_CONNECTION)
|
||
);
|
||
|
||
//
|
||
// Dereference the process that got the quota charge.
|
||
//
|
||
|
||
ASSERT( connection->OwningProcess != NULL );
|
||
ObDereferenceObject( connection->OwningProcess );
|
||
connection->OwningProcess = NULL;
|
||
|
||
if ( connection->RemoteAddress != NULL ) {
|
||
AFD_RETURN_REMOTE_ADDRESS (
|
||
connection->RemoteAddress,
|
||
connection->RemoteAddressLength,
|
||
);
|
||
connection->RemoteAddress = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
#if REFERENCE_DEBUG
|
||
VOID
|
||
AfdReferenceConnection (
|
||
IN PAFD_CONNECTION Connection,
|
||
IN LONG LocationId,
|
||
IN ULONG Param
|
||
)
|
||
{
|
||
|
||
LONG result;
|
||
|
||
ASSERT( Connection->Type == AfdBlockTypeConnection );
|
||
ASSERT( Connection->ReferenceCount > 0 );
|
||
ASSERT( Connection->ReferenceCount != 0xD1000000 );
|
||
|
||
IF_DEBUG(CONNECTION) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdReferenceConnection: connection %p, new refcnt %ld\n",
|
||
Connection, Connection->ReferenceCount+1 ));
|
||
}
|
||
|
||
//
|
||
// Do the actual increment of the reference count.
|
||
//
|
||
|
||
result = InterlockedIncrement( (PLONG)&Connection->ReferenceCount );
|
||
|
||
#if REFERENCE_DEBUG
|
||
AFD_UPDATE_REFERENCE_DEBUG(Connection, result, LocationId, Param)
|
||
#endif
|
||
|
||
} // AfdReferenceConnection
|
||
#endif
|
||
|
||
|
||
PAFD_CONNECTION
|
||
AfdGetConnectionReferenceFromEndpoint (
|
||
PAFD_ENDPOINT Endpoint
|
||
)
|
||
// Why do we need this routine?
|
||
// If VC endpoint is in connected state it maintains the referenced
|
||
// pointer to the connection object until it is closed (e.g. all references
|
||
// to the underlying file object are removed). So checking for connected
|
||
// state should be enough in any dispatch routine (or any routine called
|
||
// from the dispatch routine) because Irp that used to get to AFD maintains
|
||
// a reference to the corresponding file object.
|
||
// However, there exist a notable exception from this case: TransmitFile
|
||
// can remove the reference to the connection object in the process of endpoint
|
||
// reuse. So, to be 100% safe, it is better to use this routine in all cases.
|
||
{
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
PAFD_CONNECTION connection;
|
||
|
||
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
||
connection = AFD_CONNECTION_FROM_ENDPOINT (Endpoint);
|
||
if (connection!=NULL) {
|
||
REFERENCE_CONNECTION (connection);
|
||
}
|
||
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
||
|
||
return connection;
|
||
}
|
||
|
||
|
||
#if REFERENCE_DEBUG
|
||
VOID
|
||
AfdDereferenceConnection (
|
||
IN PAFD_CONNECTION Connection,
|
||
IN LONG LocationId,
|
||
IN ULONG Param
|
||
)
|
||
{
|
||
LONG result;
|
||
PAFD_ENDPOINT listenEndpoint;
|
||
|
||
ASSERT( Connection->Type == AfdBlockTypeConnection );
|
||
ASSERT( Connection->ReferenceCount > 0 );
|
||
ASSERT( Connection->ReferenceCount != 0xD1000000 );
|
||
|
||
IF_DEBUG(CONNECTION) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdDereferenceConnection: connection %p, new refcnt %ld\n",
|
||
Connection, Connection->ReferenceCount-1 ));
|
||
}
|
||
|
||
//
|
||
// Note that if we're tracking refcnts, we *must* call
|
||
// AfdUpdateConnectionTrack before doing the dereference. This is
|
||
// because the connection object might go away in another thread as
|
||
// soon as we do the dereference. However, because of this,
|
||
// the refcnt we store with this may sometimes be incorrect.
|
||
//
|
||
|
||
AFD_UPDATE_REFERENCE_DEBUG(Connection, Connection->ReferenceCount-1, LocationId, Param);
|
||
|
||
//
|
||
// We must hold AfdSpinLock while doing the dereference and check
|
||
// for free. This is because some code makes the assumption that
|
||
// the connection structure will not go away while AfdSpinLock is
|
||
// held, and that code references the endpoint before releasing
|
||
// AfdSpinLock. If we did the InterlockedDecrement() without the
|
||
// lock held, our count may go to zero, that code may reference the
|
||
// connection, and then a double free might occur.
|
||
//
|
||
// There is no such code anymore. The endpoint spinlock is now
|
||
// held when getting a connection from endpoint structure.
|
||
// Other code uses InterlockedCompareExchange to never increment
|
||
// connection reference if it is at 0.
|
||
//
|
||
//
|
||
|
||
result = InterlockedDecrement( (PLONG)&Connection->ReferenceCount );
|
||
|
||
//
|
||
// If the reference count is now 0, free the connection in an
|
||
// executive worker thread.
|
||
//
|
||
|
||
if ( result == 0 ) {
|
||
#else
|
||
VOID
|
||
AfdCloseConnection (
|
||
IN PAFD_CONNECTION Connection
|
||
)
|
||
{
|
||
PAFD_ENDPOINT listenEndpoint;
|
||
#endif
|
||
|
||
if (Connection->Endpoint != NULL &&
|
||
!CONNECTION_REUSE_DISABLED &&
|
||
!Connection->Endpoint->EndpointCleanedUp &&
|
||
Connection->Endpoint->Type == AfdBlockTypeVcConnecting &&
|
||
(listenEndpoint=Connection->Endpoint->Common.VcConnecting.ListenEndpoint) != NULL &&
|
||
-listenEndpoint->Common.VcListening.FailedConnectionAdds <
|
||
listenEndpoint->Common.VcListening.MaxExtraConnections &&
|
||
(IS_DELAYED_ACCEPTANCE_ENDPOINT (listenEndpoint) ||
|
||
ExQueryDepthSList (
|
||
&listenEndpoint->Common.VcListening.FreeConnectionListHead)
|
||
< AFD_MAXIMUM_FREE_CONNECTIONS ) ) {
|
||
|
||
AfdRefreshConnection (Connection);
|
||
}
|
||
else {
|
||
InterlockedIncrement (&AfdConnectionsFreeing);
|
||
//
|
||
// We're going to do this by queueing a request to an executive
|
||
// worker thread. We do this for several reasons: to ensure
|
||
// that we're at IRQL 0 so we can free pageable memory, and to
|
||
// ensure that we're in a legitimate context for a close
|
||
// operation
|
||
//
|
||
|
||
AfdQueueWorkItem(
|
||
AfdFreeConnection,
|
||
&Connection->WorkItem
|
||
);
|
||
}
|
||
#if REFERENCE_DEBUG
|
||
}
|
||
} // AfdDereferenceConnection
|
||
#else
|
||
} // AfdCloseConnection
|
||
#endif
|
||
|
||
|
||
|
||
#if REFERENCE_DEBUG
|
||
BOOLEAN
|
||
AfdCheckAndReferenceConnection (
|
||
PAFD_CONNECTION Connection,
|
||
IN LONG LocationId,
|
||
IN ULONG Param
|
||
)
|
||
#else
|
||
BOOLEAN
|
||
AfdCheckAndReferenceConnection (
|
||
PAFD_CONNECTION Connection
|
||
)
|
||
#endif
|
||
{
|
||
LONG result;
|
||
|
||
do {
|
||
result = Connection->ReferenceCount;
|
||
if (result<=0) {
|
||
result = 0;
|
||
break;
|
||
}
|
||
}
|
||
while (InterlockedCompareExchange ((PLONG)&Connection->ReferenceCount,
|
||
(result+1),
|
||
result)!=result);
|
||
|
||
if (result>0) {
|
||
#if REFERENCE_DEBUG
|
||
AFD_UPDATE_REFERENCE_DEBUG(Connection, result+1, LocationId, Param);
|
||
#endif
|
||
ASSERT( result < 0xFFFF );
|
||
return TRUE;
|
||
}
|
||
else {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_ERROR_LEVEL,
|
||
"AfdCheckAndReferenceConnection: Connection %p is gone (refcount: %ld!\n",
|
||
Connection, result));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
PAFD_CONNECTION
|
||
AfdGetFreeConnection (
|
||
IN PAFD_ENDPOINT Endpoint,
|
||
OUT PIRP *Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Takes a connection off of the endpoint's queue of listening
|
||
connections.
|
||
|
||
Arguments:
|
||
|
||
Endpoint - a pointer to the endpoint from which to get a connection.
|
||
Irp - place to return a super accept IRP if we have any
|
||
|
||
Return Value:
|
||
|
||
AFD_CONNECTION - a pointer to an AFD connection block.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAFD_CONNECTION connection;
|
||
PSINGLE_LIST_ENTRY listEntry;
|
||
PIRP irp;
|
||
|
||
ASSERT( Endpoint->Type == AfdBlockTypeVcListening ||
|
||
Endpoint->Type == AfdBlockTypeVcBoth );
|
||
|
||
|
||
//
|
||
// First try pre-accepted connections
|
||
//
|
||
|
||
while ((listEntry = InterlockedPopEntrySList (
|
||
&Endpoint->Common.VcListening.PreacceptedConnectionsListHead
|
||
))!=NULL) {
|
||
|
||
|
||
//
|
||
// Find the connection pointer from the list entry and return a
|
||
// pointer to the connection object.
|
||
//
|
||
|
||
connection = CONTAINING_RECORD(
|
||
listEntry,
|
||
AFD_CONNECTION,
|
||
SListEntry
|
||
);
|
||
|
||
//
|
||
// Check if super accept Irp has not been cancelled
|
||
//
|
||
irp = InterlockedExchangePointer ((PVOID *)&connection->AcceptIrp, NULL);
|
||
if ((irp!=NULL) && (IoSetCancelRoutine (irp, NULL)!=NULL)) {
|
||
//
|
||
// Return the IRP to the caller along with the connection.
|
||
//
|
||
*Irp = irp;
|
||
goto ReturnConnection;
|
||
}
|
||
|
||
//
|
||
// Irp has been or is about to be cancelled
|
||
//
|
||
if (irp!=NULL) {
|
||
KIRQL cancelIrql;
|
||
|
||
//
|
||
// Cleanup and cancel the super accept IRP.
|
||
//
|
||
AfdCleanupSuperAccept (irp, STATUS_CANCELLED);
|
||
|
||
//
|
||
// The cancel routine won't find the IRP in the connection,
|
||
// so we need to cancel it ourselves. Just make sure that
|
||
// the cancel routine is done before doing so.
|
||
//
|
||
IoAcquireCancelSpinLock (&cancelIrql);
|
||
IoReleaseCancelSpinLock (cancelIrql);
|
||
|
||
IoCompleteRequest (irp, AfdPriorityBoost);
|
||
}
|
||
|
||
//
|
||
// This connection has already been diassociated from endpoint.
|
||
// If backlog is below the level we need, put it on the free
|
||
// list, otherwise, get rid of it.
|
||
//
|
||
|
||
ASSERT (connection->Endpoint==NULL);
|
||
if (Endpoint->Common.VcListening.FailedConnectionAdds>=0 &&
|
||
ExQueryDepthSList (&Endpoint->Common.VcListening.FreeConnectionListHead)<AFD_MAXIMUM_FREE_CONNECTIONS) {
|
||
InterlockedPushEntrySList (
|
||
&Endpoint->Common.VcListening.FreeConnectionListHead,
|
||
&connection->SListEntry);
|
||
}
|
||
else {
|
||
InterlockedIncrement (&Endpoint->Common.VcListening.FailedConnectionAdds);
|
||
DEREFERENCE_CONNECTION (connection);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Remove the first entry from the list. If the list is empty,
|
||
// return NULL.
|
||
//
|
||
|
||
listEntry = InterlockedPopEntrySList (
|
||
&Endpoint->Common.VcListening.FreeConnectionListHead);
|
||
if (listEntry==NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Find the connection pointer from the list entry and return a
|
||
// pointer to the connection object.
|
||
//
|
||
|
||
connection = CONTAINING_RECORD(
|
||
listEntry,
|
||
AFD_CONNECTION,
|
||
SListEntry
|
||
);
|
||
|
||
*Irp = NULL;
|
||
|
||
ReturnConnection:
|
||
//
|
||
// Assign unique non-zero sequence number (unique in the context
|
||
// of the given listening endpoint).
|
||
//
|
||
|
||
connection->Sequence = InterlockedIncrement (&Endpoint->Common.VcListening.Sequence);
|
||
if (connection->Sequence==0) {
|
||
connection->Sequence = InterlockedIncrement (&Endpoint->Common.VcListening.Sequence);
|
||
ASSERT (connection->Sequence!=0);
|
||
}
|
||
|
||
return connection;
|
||
|
||
} // AfdGetFreeConnection
|
||
|
||
|
||
PAFD_CONNECTION
|
||
AfdGetReturnedConnection (
|
||
IN PAFD_ENDPOINT Endpoint,
|
||
IN LONG Sequence
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Takes a connection off of the endpoint's queue of returned
|
||
connections.
|
||
|
||
*** NOTE: This routine must be called with endpoint spinlock held!!
|
||
|
||
Arguments:
|
||
|
||
Endpoint - a pointer to the endpoint from which to get a connection.
|
||
|
||
Sequence - the sequence the connection must match. If 0, the first returned
|
||
connection is used.
|
||
|
||
Return Value:
|
||
|
||
AFD_CONNECTION - a pointer to an AFD connection block.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAFD_CONNECTION connection;
|
||
PLIST_ENTRY listEntry;
|
||
|
||
ASSERT( Endpoint->Type == AfdBlockTypeVcListening ||
|
||
Endpoint->Type == AfdBlockTypeVcBoth );
|
||
|
||
|
||
//
|
||
// Walk the endpoint's list of returned connections until we reach
|
||
// the end or until we find one with a matching sequence.
|
||
//
|
||
|
||
for ( listEntry = Endpoint->Common.VcListening.ReturnedConnectionListHead.Flink;
|
||
listEntry != &Endpoint->Common.VcListening.ReturnedConnectionListHead;
|
||
listEntry = listEntry->Flink ) {
|
||
|
||
|
||
connection = CONTAINING_RECORD(
|
||
listEntry,
|
||
AFD_CONNECTION,
|
||
ListEntry
|
||
);
|
||
|
||
if ( Sequence == connection->Sequence || Sequence == 0 ) {
|
||
|
||
//
|
||
// Found the connection we were looking for. Remove
|
||
// the connection from the list, release the spin lock,
|
||
// and return the connection.
|
||
//
|
||
|
||
RemoveEntryList( listEntry );
|
||
|
||
return connection;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
|
||
} // AfdGetReturnedConnection
|
||
|
||
|
||
PAFD_CONNECTION
|
||
AfdFindReturnedConnection(
|
||
IN PAFD_ENDPOINT Endpoint,
|
||
IN LONG Sequence
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Scans the endpoints queue of returned connections looking for one
|
||
with the specified sequence number.
|
||
|
||
Arguments:
|
||
|
||
Endpoint - A pointer to the endpoint from which to get a connection.
|
||
|
||
Sequence - The sequence the connection must match.
|
||
|
||
Return Value:
|
||
|
||
AFD_CONNECTION - A pointer to an AFD connection block if successful,
|
||
NULL if not.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PAFD_CONNECTION connection;
|
||
PLIST_ENTRY listEntry;
|
||
|
||
ASSERT( Endpoint != NULL );
|
||
ASSERT( IS_AFD_ENDPOINT_TYPE( Endpoint ) );
|
||
|
||
//
|
||
// Walk the endpoint's list of returned connections until we reach
|
||
// the end or until we find one with a matching sequence.
|
||
//
|
||
|
||
for( listEntry = Endpoint->Common.VcListening.ReturnedConnectionListHead.Flink;
|
||
listEntry != &Endpoint->Common.VcListening.ReturnedConnectionListHead;
|
||
listEntry = listEntry->Flink ) {
|
||
|
||
connection = CONTAINING_RECORD(
|
||
listEntry,
|
||
AFD_CONNECTION,
|
||
ListEntry
|
||
);
|
||
|
||
if( Sequence == connection->Sequence ) {
|
||
|
||
return connection;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return NULL;
|
||
|
||
} // AfdFindReturnedConnection
|
||
|
||
|
||
PAFD_CONNECTION
|
||
AfdGetUnacceptedConnection (
|
||
IN PAFD_ENDPOINT Endpoint
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Takes a connection of the endpoint's queue of unaccpted connections.
|
||
|
||
*** NOTE: This routine must be called with endpoint spinlock held!!
|
||
|
||
Arguments:
|
||
|
||
Endpoint - a pointer to the endpoint from which to get a connection.
|
||
|
||
Return Value:
|
||
|
||
AFD_CONNECTION - a pointer to an AFD connection block.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAFD_CONNECTION connection;
|
||
PLIST_ENTRY listEntry;
|
||
|
||
ASSERT( Endpoint->Type == AfdBlockTypeVcListening ||
|
||
Endpoint->Type == AfdBlockTypeVcBoth );
|
||
ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
|
||
|
||
if ( IsListEmpty( &Endpoint->Common.VcListening.UnacceptedConnectionListHead ) ) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Dequeue a listening connection and remember its handle.
|
||
//
|
||
|
||
listEntry = RemoveHeadList( &Endpoint->Common.VcListening.UnacceptedConnectionListHead );
|
||
connection = CONTAINING_RECORD( listEntry, AFD_CONNECTION, ListEntry );
|
||
|
||
return connection;
|
||
|
||
} // AfdGetUnacceptedConnection
|
||
|
||
|
||
|
||
VOID
|
||
AfdAddConnectedReference (
|
||
IN PAFD_CONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds the connected reference to an AFD connection block. The
|
||
connected reference is special because it prevents the connection
|
||
object from being freed until we receive a disconnect event, or know
|
||
through some other means that the virtual circuit is disconnected.
|
||
|
||
Arguments:
|
||
|
||
Connection - a pointer to an AFD connection block.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
|
||
AfdAcquireSpinLock( &Connection->Endpoint->SpinLock, &lockHandle );
|
||
|
||
IF_DEBUG(CONNECTION) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdAddConnectedReference: connection %p, new refcnt %ld\n",
|
||
Connection, Connection->ReferenceCount+1 ));
|
||
}
|
||
|
||
ASSERT( !Connection->ConnectedReferenceAdded );
|
||
ASSERT( Connection->Type == AfdBlockTypeConnection );
|
||
|
||
//
|
||
// Increment the reference count and remember that the connected
|
||
// reference has been placed on the connection object.
|
||
//
|
||
|
||
Connection->ConnectedReferenceAdded = TRUE;
|
||
AfdRecordConnectedReferencesAdded();
|
||
|
||
AfdReleaseSpinLock( &Connection->Endpoint->SpinLock, &lockHandle );
|
||
|
||
REFERENCE_CONNECTION( Connection );
|
||
|
||
} // AfdAddConnectedReference
|
||
|
||
|
||
VOID
|
||
AfdDeleteConnectedReference (
|
||
IN PAFD_CONNECTION Connection,
|
||
IN BOOLEAN EndpointLockHeld
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes the connected reference to an AFD connection block. If the
|
||
connected reference has already been removed, this routine does
|
||
nothing. The connected reference should be removed as soon as we
|
||
know that it is OK to close the connection object handle, but not
|
||
before. Removing this reference too soon could abort a connection
|
||
which shouldn't get aborted.
|
||
|
||
Arguments:
|
||
|
||
Connection - a pointer to an AFD connection block.
|
||
|
||
EndpointLockHeld - TRUE if the caller already has the endpoint
|
||
spin lock. The lock remains held on exit.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
PAFD_ENDPOINT endpoint;
|
||
#if REFERENCE_DEBUG
|
||
PVOID caller, callersCaller;
|
||
|
||
RtlGetCallersAddress( &caller, &callersCaller );
|
||
#endif
|
||
|
||
endpoint = Connection->Endpoint;
|
||
|
||
if ( !EndpointLockHeld ) {
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
|
||
//
|
||
// Only do a dereference if the connected reference is still active
|
||
// on the connectiuon object.
|
||
//
|
||
|
||
if ( Connection->ConnectedReferenceAdded ) {
|
||
|
||
//
|
||
// Three things must be true before we can remove the connected
|
||
// reference:
|
||
//
|
||
// 1) There must be no sends outstanding on the connection if
|
||
// the TDI provider does not support bufferring. This is
|
||
// because AfdRestartBufferSend() looks at the connection
|
||
// object.
|
||
//
|
||
// 2) Cleanup must have started on the endpoint. Until we get a
|
||
// cleanup IRP on the endpoint, we could still get new sends.
|
||
//
|
||
// 3) We have been indicated with a disconnect on the
|
||
// connection. We want to keep the connection object around
|
||
// until we get a disconnect indication in order to avoid
|
||
// premature closes on the connection object resulting in an
|
||
// unintended abort. If the transport does not support
|
||
// orderly release, then this condition is not necessary.
|
||
//
|
||
|
||
if ( (Connection->TdiBufferring ||
|
||
Connection->VcBufferredSendCount == 0)
|
||
|
||
&&
|
||
|
||
Connection->CleanupBegun
|
||
|
||
&&
|
||
|
||
(Connection->AbortIndicated || Connection->DisconnectIndicated ||
|
||
!IS_TDI_ORDERLY_RELEASE(endpoint) ||
|
||
IS_CROOT_ENDPOINT(endpoint)) ) {
|
||
|
||
IF_DEBUG(CONNECTION) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdDeleteConnectedReference: connection %p, new refcnt %ld\n",
|
||
Connection, Connection->ReferenceCount-1 ));
|
||
}
|
||
|
||
//
|
||
// Be careful about the order of things here. We must FIRST
|
||
// reset the flag, then release the spin lock and call
|
||
// AfdDereferenceConnection(). Note that it is illegal to
|
||
// call AfdDereferenceConnection() with a spin lock held.
|
||
//
|
||
|
||
Connection->ConnectedReferenceAdded = FALSE;
|
||
AfdRecordConnectedReferencesDeleted();
|
||
|
||
if ( !EndpointLockHeld ) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
|
||
DEREFERENCE_CONNECTION( Connection );
|
||
|
||
} else {
|
||
|
||
IF_DEBUG(CONNECTION) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdDeleteConnectedReference: connection %p, %ld sends pending\n",
|
||
Connection, Connection->VcBufferredSendCount ));
|
||
}
|
||
|
||
UPDATE_CONN2( Connection, "Not removing cref, state flags: %lx",
|
||
Connection->ConnectionStateFlags);
|
||
//
|
||
// Remember that the connected reference deletion is still
|
||
// pending, i.e. there is a special condition on the
|
||
// endpoint. This will cause AfdRestartBufferSend() to do
|
||
// the actual dereference when the last send completes.
|
||
//
|
||
|
||
Connection->SpecialCondition = TRUE;
|
||
|
||
if ( !EndpointLockHeld ) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
IF_DEBUG(CONNECTION) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdDeleteConnectedReference: already removed on connection %p, refcnt %ld\n",
|
||
Connection, Connection->ReferenceCount ));
|
||
}
|
||
|
||
if ( !EndpointLockHeld ) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
}
|
||
|
||
return;
|
||
|
||
} // AfdDeleteConnectedReference
|
||
|
||
|
||
#if REFERENCE_DEBUG
|
||
|
||
|
||
VOID
|
||
AfdUpdateConnectionTrack (
|
||
IN PAFD_CONNECTION Connection,
|
||
IN LONG LocationId,
|
||
IN ULONG Param
|
||
)
|
||
{
|
||
AFD_UPDATE_REFERENCE_DEBUG (Connection, Connection->ReferenceCount, LocationId, Param);
|
||
|
||
#if GLOBAL_REFERENCE_DEBUG
|
||
{
|
||
PAFD_GLOBAL_REFERENCE_DEBUG globalSlot;
|
||
|
||
newSlot = InterlockedIncrement( &AfdGlobalReferenceSlot );
|
||
globalSlot = &AfdGlobalReference[newSlot % MAX_GLOBAL_REFERENCE];
|
||
|
||
globalSlot->Info1 = Info1;
|
||
globalSlot->Info2 = Info2;
|
||
globalSlot->Action = Action;
|
||
globalSlot->NewCount = NewReferenceCount;
|
||
globalSlot->Connection = Connection;
|
||
KeQueryTickCount( &globalSlot->TickCounter );
|
||
}
|
||
#endif
|
||
|
||
} // AfdUpdateConnectionTrack
|
||
|
||
#endif
|
||
|