NT4/private/ntos/afd/blkendp.c
2020-09-30 17:12:29 +02:00

1011 lines
24 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
blkendp.c
Abstract:
This module contains allocate, free, close, reference, and dereference
routines for AFD endpoints.
Author:
David Treadwell (davidtr) 10-Mar-1992
Revision History:
--*/
#include "afdp.h"
VOID
AfdFreeEndpoint (
IN PVOID Context
);
PAFD_TRANSPORT_INFO
AfdGetTransportInfo (
IN PUNICODE_STRING TransportDeviceName
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfdAllocateEndpoint )
#pragma alloc_text( PAGE, AfdCloseEndpoint )
#pragma alloc_text( PAGE, AfdFreeEndpoint )
#pragma alloc_text( PAGE, AfdGetTransportInfo )
#pragma alloc_text( PAGE, AfdRefreshEndpoint )
#pragma alloc_text( PAGEAFD, AfdDereferenceEndpoint )
#if REFERENCE_DEBUG
#pragma alloc_text( PAGEAFD, AfdReferenceEndpoint )
#endif
#pragma alloc_text( PAGEAFD, AfdFreeQueuedConnections )
#endif
NTSTATUS
AfdAllocateEndpoint (
OUT PAFD_ENDPOINT * NewEndpoint,
IN PUNICODE_STRING TransportDeviceName,
IN LONG GroupID
)
/*++
Routine Description:
Allocates and initializes a new AFD endpoint structure.
Arguments:
NewEndpoint - Receives a pointer to the new endpoint structure if
successful.
TransportDeviceName - the name of the TDI transport provider
corresponding to the endpoint structure.
GroupID - Identifies the group ID for the new endpoint.
Return Value:
NTSTATUS - The completion status.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_TRANSPORT_INFO transportInfo;
NTSTATUS status;
AFD_GROUP_TYPE groupType;
PAGED_CODE( );
DEBUG *NewEndpoint = NULL;
if ( TransportDeviceName != NULL ) {
//
// First, make sure that the transport device name is stored globally
// for AFD. Since there will typically only be a small number of
// transport device names, we store the name strings once globally
// for access by all endpoints.
//
transportInfo = AfdGetTransportInfo( TransportDeviceName );
if ( transportInfo == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// Validate the incoming group ID, allocate a new one if necessary.
//
if( !AfdGetGroup( &GroupID, &groupType ) ) {
return STATUS_INVALID_PARAMETER;
}
//
// Allocate a buffer to hold the endpoint structure.
//
endpoint = AFD_ALLOCATE_POOL(
NonPagedPool,
sizeof(AFD_ENDPOINT),
AFD_ENDPOINT_POOL_TAG
);
if ( endpoint == NULL ) {
if( GroupID != 0 ) {
AfdDereferenceGroup( GroupID );
}
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory( endpoint, sizeof(AFD_ENDPOINT) );
//
// Initialize the reference count to 2--one for the caller's
// reference, one for the active reference.
//
endpoint->ReferenceCount = 2;
//
// Initialize the endpoint structure.
//
if ( TransportDeviceName == NULL ) {
endpoint->Type = AfdBlockTypeHelper;
endpoint->State = AfdEndpointStateInvalid;
endpoint->EndpointType = AfdEndpointTypeUnknown;
} else {
endpoint->Type = AfdBlockTypeEndpoint;
endpoint->State = AfdEndpointStateOpen;
endpoint->TransportInfo = transportInfo;
}
endpoint->GroupID = GroupID;
endpoint->GroupType = groupType;
KeInitializeSpinLock( &endpoint->SpinLock );
#if REFERENCE_DEBUG
{
PAFD_REFERENCE_DEBUG referenceDebug;
referenceDebug = AFD_ALLOCATE_POOL(
NonPagedPool,
sizeof(AFD_REFERENCE_DEBUG) * MAX_REFERENCE,
AFD_DEBUG_POOL_TAG
);
if ( referenceDebug != NULL ) {
RtlZeroMemory( referenceDebug, sizeof(AFD_REFERENCE_DEBUG) * MAX_REFERENCE );
}
endpoint->CurrentReferenceSlot = -1;
endpoint->ReferenceDebug = referenceDebug;
}
#endif
#if DBG
InitializeListHead( &endpoint->OutstandingIrpListHead );
#endif
//
// Remember the process which opened the endpoint. We'll use this to
// charge quota to the process as necessary. Reference the process
// so that it does not go away until we have returned all charged
// quota to the process.
//
endpoint->OwningProcess = IoGetCurrentProcess( );
ObReferenceObject(endpoint->OwningProcess);
//
// Insert the endpoint on the global list.
//
AfdInsertNewEndpointInList( endpoint );
//
// Return a pointer to the new endpoint to the caller.
//
IF_DEBUG(ENDPOINT) {
KdPrint(( "AfdAllocateEndpoint: new endpoint at %lx\n", endpoint ));
}
*NewEndpoint = endpoint;
return STATUS_SUCCESS;
} // AfdAllocateEndpoint
VOID
AfdCloseEndpoint (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Initiates the closing of an AFD endpoint structure.
Arguments:
Endpoint - a pointer to the AFD endpoint structure.
Return Value:
None.
--*/
{
PAFD_CONNECTION connection;
PAGED_CODE( );
IF_DEBUG(ENDPOINT) {
KdPrint(( "AfdCloseEndpoint: closing endpoint at %lx\n", Endpoint ));
}
if ( Endpoint->State == AfdEndpointStateClosing ) {
return;
}
//
// Set the state of the endpoint to closing and dereference to
// get rid of the active reference.
//
Endpoint->State = AfdEndpointStateClosing;
//
// If there is a connection on this endpoint, dereference it here
// rather than in AfdDereferenceEndpoint, because the connection
// has a referenced pointer to the endpoint which must be removed
// before the endpoint can dereference the connection.
//
connection = AFD_CONNECTION_FROM_ENDPOINT( Endpoint );
if ( connection != NULL ) {
DEREFERENCE_CONNECTION( Endpoint->Common.VcConnecting.Connection );
}
//
// Dereference the endpoint to get rid of the active reference.
// This will result in the endpoint storage being freed as soon
// as all other references go away.
//
DEREFERENCE_ENDPOINT( Endpoint );
} // AfdCloseEndpoint
VOID
AfdFreeQueuedConnections (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Frees queued connection objects on a listening AFD endpoint.
Arguments:
Endpoint - a pointer to the AFD endpoint structure.
Return Value:
None.
--*/
{
KIRQL oldIrql;
PAFD_CONNECTION connection;
NTSTATUS status;
ASSERT( Endpoint->Type == AfdBlockTypeVcListening );
//
// Free the unaccepted connections.
//
// We must hold AfdSpinLock to call AfdGetUnacceptedConnection,
// but we may not hold it when calling AfdDereferenceConnection.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
while ( (connection = AfdGetUnacceptedConnection( Endpoint )) != NULL ) {
ASSERT( connection->Endpoint == Endpoint );
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
AfdAbortConnection( connection );
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
}
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
//
// Free the returned connections.
//
while ( (connection = AfdGetReturnedConnection( Endpoint, 0 )) != NULL ) {
ASSERT( connection->Endpoint == Endpoint );
AfdAbortConnection( connection );
}
//
// And finally, purge the free connection queue.
//
while ( (connection = AfdGetFreeConnection( Endpoint )) != NULL ) {
ASSERT( connection->Endpoint == NULL );
DEREFERENCE_CONNECTION( connection );
}
return;
} // AfdFreeQueuedConnections
VOID
AfdFreeEndpoint (
IN PVOID Context
)
/*++
Routine Description:
Does the actual work to deallocate an AFD endpoint structure and
associated structures. Note that all other references to the
endpoint structure must be gone before this routine is called, since
it frees the endpoint and assumes that nobody else will be looking
at the endpoint.
Arguments:
Context - Actually points to the endpoint's embedded AFD_WORK_ITEM
structure. From this we can determine the endpoint to free.
Return Value:
None.
--*/
{
NTSTATUS status;
PAFD_ENDPOINT endpoint;
PLIST_ENTRY listEntry;
PAFD_CONNECTION connection;
PAGED_CODE( );
ASSERT( Context != NULL );
endpoint = CONTAINING_RECORD(
Context,
AFD_ENDPOINT,
WorkItem
);
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
ASSERT( endpoint->ReferenceCount == 0 );
ASSERT( endpoint->State == AfdEndpointStateClosing );
ASSERT( endpoint->ObReferenceBias == 0 );
ASSERT( KeGetCurrentIrql( ) == 0 );
//
// If this is a listening endpoint, then purge the endpoint of all
// queued connections.
//
if ( endpoint->Type == AfdBlockTypeVcListening ) {
AfdFreeQueuedConnections( endpoint );
}
//
// Dereference any group ID associated with this endpoint.
//
if( endpoint->GroupID != 0 ) {
AfdDereferenceGroup( endpoint->GroupID );
}
//
// If we set up an owning process for the endpoint, dereference the
// process.
//
if ( endpoint->OwningProcess != NULL ) {
ObDereferenceObject( endpoint->OwningProcess );
endpoint->OwningProcess = NULL;
}
//
// If this is a bufferring datagram endpoint, remove all the
// bufferred datagrams from the endpoint's list and free them.
//
if ( endpoint->Type == AfdBlockTypeDatagram &&
endpoint->ReceiveDatagramBufferListHead.Flink != NULL ) {
while ( !IsListEmpty( &endpoint->ReceiveDatagramBufferListHead ) ) {
PAFD_BUFFER afdBuffer;
listEntry = RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead );
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
AfdReturnBuffer( afdBuffer );
}
}
//
// Close and dereference the TDI address object on the endpoint, if
// any.
//
if ( endpoint->AddressFileObject != NULL ) {
ObDereferenceObject( endpoint->AddressFileObject );
endpoint->AddressFileObject = NULL;
AfdRecordAddrDeref();
}
if ( endpoint->AddressHandle != NULL ) {
KeAttachProcess( AfdSystemProcess );
status = ZwClose( endpoint->AddressHandle );
ASSERT( NT_SUCCESS(status) );
KeDetachProcess( );
endpoint->AddressHandle = NULL;
AfdRecordAddrClosed();
}
//
// Remove the endpoint from the global list. Do this before any
// deallocations to prevent someone else from seeing an endpoint in
// an invalid state.
//
AfdRemoveEndpointFromList( endpoint );
//
// Dereference the listening endpoint on the endpoint, if
// any.
//
if ( endpoint->Type == AfdBlockTypeVcConnecting &&
endpoint->Common.VcConnecting.ListenEndpoint != NULL ) {
ASSERT( endpoint->Common.VcConnecting.ListenEndpoint->Type == AfdBlockTypeVcListening );
DEREFERENCE_ENDPOINT( endpoint->Common.VcConnecting.ListenEndpoint );
endpoint->Common.VcConnecting.ListenEndpoint = NULL;
}
//
// Free local and remote address buffers.
//
if ( endpoint->LocalAddress != NULL ) {
AFD_FREE_POOL(
endpoint->LocalAddress,
AFD_LOCAL_ADDRESS_POOL_TAG
);
endpoint->LocalAddress = NULL;
}
if ( endpoint->Type == AfdBlockTypeDatagram &&
endpoint->Common.Datagram.RemoteAddress != NULL ) {
AFD_FREE_POOL(
endpoint->Common.Datagram.RemoteAddress,
AFD_REMOTE_ADDRESS_POOL_TAG
);
endpoint->Common.Datagram.RemoteAddress = NULL;
}
//
// Free context and connect data buffers.
//
if ( endpoint->Context != NULL ) {
AFD_FREE_POOL(
endpoint->Context,
AFD_CONTEXT_POOL_TAG
);
endpoint->Context = NULL;
}
if ( endpoint->ConnectDataBuffers != NULL ) {
AfdFreeConnectDataBuffers( endpoint->ConnectDataBuffers );
}
//
// If there's an active EventSelect() on this endpoint, dereference
// the associated event object.
//
if( endpoint->EventObject != NULL ) {
ObDereferenceObject( endpoint->EventObject );
endpoint->EventObject = NULL;
}
//
// Free any reusable TransmitFile info attached to the endpoint.
//
if( endpoint->TransmitInfo != NULL ) {
AFD_FREE_POOL(
endpoint->TransmitInfo,
AFD_TRANSMIT_INFO_POOL_TAG
);
}
//
// Free the space that holds the endpoint itself.
//
IF_DEBUG(ENDPOINT) {
KdPrint(( "AfdFreeEndpoint: freeing endpoint at %lx\n", endpoint ));
}
endpoint->Type = 0xAFDE;
#if REFERENCE_DEBUG
if ( endpoint->ReferenceDebug != NULL ) {
AFD_FREE_POOL(
endpoint->ReferenceDebug,
AFD_DEBUG_POOL_TAG
);
}
#endif
//
// Free the pool used for the endpoint itself.
//
AFD_FREE_POOL(
endpoint,
AFD_ENDPOINT_POOL_TAG
);
} // AfdFreeEndpoint
#if REFERENCE_DEBUG
VOID
AfdDereferenceEndpoint (
IN PAFD_ENDPOINT Endpoint,
IN PVOID Info1,
IN PVOID Info2
)
#else
VOID
AfdDereferenceEndpoint (
IN PAFD_ENDPOINT Endpoint
)
#endif
/*++
Routine Description:
Dereferences an AFD endpoint and calls the routine to free it if
appropriate.
Arguments:
Endpoint - a pointer to the AFD endpoint structure.
Return Value:
None.
--*/
{
LONG result;
KIRQL oldIrql;
#if REFERENCE_DEBUG
PAFD_REFERENCE_DEBUG slot;
LONG newSlot;
#endif
#if REFERENCE_DEBUG
IF_DEBUG(ENDPOINT) {
KdPrint(( "AfdDereferenceEndpoint: endpoint at %lx, new refcnt %ld\n",
Endpoint, Endpoint->ReferenceCount-1 ));
}
ASSERT( IS_AFD_ENDPOINT_TYPE( Endpoint ) );
ASSERT( Endpoint->ReferenceCount > 0 );
ASSERT( Endpoint->ReferenceCount != 0xDAADF00D );
if ( Endpoint->ReferenceDebug != NULL ) {
newSlot = InterlockedIncrement( &Endpoint->CurrentReferenceSlot );
slot = &Endpoint->ReferenceDebug[newSlot % MAX_REFERENCE];
slot->Action = 0xFFFFFFFF;
slot->NewCount = Endpoint->ReferenceCount - 1;
slot->Info1 = Info1;
slot->Info2 = Info2;
}
#endif
//
// We must hold AfdSpinLock while doing the dereference and check
// for free. This is because some code makes the assumption that
// the endpoint 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
// endpoint, and then a double free might occur.
//
// It is still valuable to use the interlocked routines for
// increment and decrement of structures because it allows us to
// avoid having to hold the spin lock for a reference.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
//
// Decrement the reference count; if it is 0, free the endpoint.
//
result = InterlockedDecrement( &Endpoint->ReferenceCount );
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
if ( result == 0 ) {
ASSERT( Endpoint->State == AfdEndpointStateClosing );
//
// 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(
AfdFreeEndpoint,
&Endpoint->WorkItem
);
}
} // AfdDereferenceEndpoint
#if REFERENCE_DEBUG
VOID
AfdReferenceEndpoint (
IN PAFD_ENDPOINT Endpoint,
IN PVOID Info1,
IN PVOID Info2
)
/*++
Routine Description:
References an AFD endpoint.
Arguments:
Endpoint - a pointer to the AFD endpoint structure.
Return Value:
None.
--*/
{
PAFD_REFERENCE_DEBUG slot;
LONG newSlot;
LONG result;
ASSERT( Endpoint->ReferenceCount > 0 );
if( Endpoint->ReferenceDebug != NULL ) {
newSlot = InterlockedIncrement( &Endpoint->CurrentReferenceSlot );
slot = &Endpoint->ReferenceDebug[newSlot % MAX_REFERENCE];
slot->Action = 1;
slot->NewCount = Endpoint->ReferenceCount + 1;
slot->Info1 = Info1;
slot->Info2 = Info2;
}
IF_DEBUG(ENDPOINT) {
KdPrint(( "AfdReferenceEndpoint: endpoint at %lx, new refcnt %ld\n",
Endpoint, Endpoint->ReferenceCount+1 ));
}
ASSERT( Endpoint->ReferenceCount < 0xFFFF );
result = InterlockedIncrement( &Endpoint->ReferenceCount );
} // AfdReferenceEndpoint
#endif
VOID
AfdRefreshEndpoint (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Prepares an AFD endpoint structure to be reused. All other
references to the endpoint must be freed before this routine is
called, since this routine assumes that nobody will access the old
information in the endpoint structure.
Arguments:
Endpoint - a pointer to the AFD endpoint structure.
Return Value:
None.
--*/
{
NTSTATUS status;
PAGED_CODE( );
//
// This routine must be called at low IRQL. At initial
// implementation, it is only called through AfdFreeConnection in an
// executive worker thread.
//
ASSERT( Endpoint->Type == AfdBlockTypeVcConnecting );
ASSERT( Endpoint->Common.VcConnecting.Connection == NULL );
ASSERT( KeGetCurrentIrql( ) < DISPATCH_LEVEL );
//
// Dereference the listening endpoint and its address object.
//
if ( Endpoint->Common.VcConnecting.ListenEndpoint != NULL ) {
DEREFERENCE_ENDPOINT( Endpoint->Common.VcConnecting.ListenEndpoint );
Endpoint->Common.VcConnecting.ListenEndpoint = NULL;
}
//
// Close and dereference the TDI address object on the endpoint, if
// any.
//
if ( Endpoint->AddressFileObject != NULL ) {
ObDereferenceObject( Endpoint->AddressFileObject );
Endpoint->AddressFileObject = NULL;
AfdRecordAddrDeref();
}
if ( Endpoint->AddressHandle != NULL ) {
KeAttachProcess( AfdSystemProcess );
status = ZwClose( Endpoint->AddressHandle );
ASSERT( NT_SUCCESS(status) );
KeDetachProcess( );
Endpoint->AddressHandle = NULL;
AfdRecordAddrClosed();
}
//
// Reinitialize the endpoint structure.
//
Endpoint->Type = AfdBlockTypeEndpoint;
Endpoint->State = AfdEndpointStateOpen;
Endpoint->DisconnectMode = 0;
Endpoint->PollCalled = FALSE;
return;
} // AfdRefreshEndpoint
PAFD_TRANSPORT_INFO
AfdGetTransportInfo (
IN PUNICODE_STRING TransportDeviceName
)
/*++
Routine Description:
Returns a transport information structure corresponding to the
specified TDI transport provider. Each unique transport string gets
a single provider structure, so that multiple endpoints for the same
transport share the same transport information structure.
Arguments:
TransportDeviceName - the name of the TDI transport provider.
Return Value:
None.
--*/
{
PLIST_ENTRY listEntry;
PAFD_TRANSPORT_INFO transportInfo;
ULONG structureLength;
NTSTATUS status;
HANDLE controlChannel;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iosb;
TDI_REQUEST_KERNEL_QUERY_INFORMATION kernelQueryInfo;
PAGED_CODE( );
//
// First walk the list of transport device names looking for an
// identical name.
//
ExAcquireResourceExclusive( AfdResource, TRUE );
for ( listEntry = AfdTransportInfoListHead.Flink;
listEntry != &AfdTransportInfoListHead;
listEntry = listEntry->Flink ) {
transportInfo = CONTAINING_RECORD(
listEntry,
AFD_TRANSPORT_INFO,
TransportInfoListEntry
);
if ( RtlCompareUnicodeString(
&transportInfo->TransportDeviceName,
TransportDeviceName,
TRUE ) == 0 ) {
//
// We found an exact match. Return a pointer to the
// UNICODE_STRING field of this structure.
//
ExReleaseResource( AfdResource );
return transportInfo;
}
}
//
// There were no matches, so this is a new transport device name
// which we've never seen before. Allocate a structure to hold the
// new name and place the name on the global list.
//
structureLength = sizeof(AFD_TRANSPORT_INFO) +
TransportDeviceName->Length + sizeof(WCHAR);
transportInfo = AFD_ALLOCATE_POOL(
NonPagedPool,
structureLength,
AFD_TRANSPORT_INFO_POOL_TAG
);
if ( transportInfo == NULL ) {
ExReleaseResource( AfdResource );
return NULL;
}
//
// Set up the IRP stack location information to query the TDI
// provider information.
//
kernelQueryInfo.QueryType = TDI_QUERY_PROVIDER_INFORMATION;
kernelQueryInfo.RequestConnectionInformation = NULL;
//
// Open a control channel to the TDI provider.
//
InitializeObjectAttributes(
&objectAttributes,
TransportDeviceName,
OBJ_CASE_INSENSITIVE, // attributes
NULL,
NULL
);
status = ZwCreateFile(
&controlChannel,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&objectAttributes,
&iosb, // returned status information.
0, // block size (unused).
0, // file attributes.
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_CREATE, // create disposition.
0, // create options.
NULL,
0
);
if ( !NT_SUCCESS(status) ) {
ExReleaseResource( AfdResource );
AFD_FREE_POOL(
transportInfo,
AFD_TRANSPORT_INFO_POOL_TAG
);
return NULL;
}
//
// Get the TDI provider information for the transport.
//
status = AfdIssueDeviceControl(
controlChannel,
NULL,
&kernelQueryInfo,
sizeof(kernelQueryInfo),
&transportInfo->ProviderInfo,
sizeof(transportInfo->ProviderInfo),
TDI_QUERY_INFORMATION
);
if ( !NT_SUCCESS(status) ) {
ExReleaseResource( AfdResource );
AFD_FREE_POOL(
transportInfo,
AFD_TRANSPORT_INFO_POOL_TAG
);
ZwClose( controlChannel );
return NULL;
}
//
// Fill in the transport device name.
//
transportInfo->TransportDeviceName.MaximumLength =
TransportDeviceName->Length + sizeof(WCHAR);
transportInfo->TransportDeviceName.Buffer =
(PWSTR)(transportInfo + 1);
RtlCopyUnicodeString(
&transportInfo->TransportDeviceName,
TransportDeviceName
);
//
// Place the transport info structure on the global list.
//
InsertTailList(
&AfdTransportInfoListHead,
&transportInfo->TransportInfoListEntry
);
//
// Return the transport info structure to the caller.
//
ExReleaseResource( AfdResource );
ZwClose( controlChannel );
return transportInfo;
} // AfdGetTransportInfo