Leaked source code of windows server 2003
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.
 
 
 
 
 
 

9137 lines
251 KiB

/*++
Copyright (c) 1989-1999 Microsoft Corporation
Module Name:
misc.c
Abstract:
This module contains the miscellaneous AFD routines.
Author:
David Treadwell (davidtr) 13-Nov-1992
Revision History:
Vadim Eydelman (vadime) 1998-1999 Misc changes
--*/
#include "afdp.h"
#define TL_INSTANCE 0
#include <ipexport.h>
#include <tdiinfo.h>
#include <tcpinfo.h>
#include <ntddtcp.h>
VOID
AfdDoWork (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
);
NTSTATUS
AfdRestartDeviceControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
AfdUnlockDriver (
IN PVOID Context
);
BOOLEAN
AfdCompareAddresses(
IN PTRANSPORT_ADDRESS Address1,
IN ULONG Address1Length,
IN PTRANSPORT_ADDRESS Address2,
IN ULONG Address2Length
);
NTSTATUS
AfdCompleteTransportIoctl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
AfdCompleteNBTransportIoctl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
BOOLEAN
AfdCleanupTransportIoctl (
PAFD_ENDPOINT Endpoint,
PAFD_REQUEST_CONTEXT RequestCtx
);
BOOLEAN
AfdCleanupNBTransportIoctl (
PAFD_ENDPOINT Endpoint,
PAFD_REQUEST_CONTEXT RequestCtx
);
#ifdef _WIN64
NTSTATUS
AfdQueryHandles32 (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
AfdSetQos32(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
AfdGetQos32(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
AfdNoOperation32(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
#endif
VOID
AfdLRListTimeout (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
VOID
AfdProcessLRList (
PVOID Param
);
VOID
AfdLRStartTimer (
VOID
);
#ifdef _AFD_VARIABLE_STACK_
VOID
AfdCancelStackIncreaseIrp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
AfdRestartStackIncreaseIrp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
PIRP
AfdGetStackIncreaseIrp (
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
);
#endif //_AFD_VARIABLE_STACK_
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfdCalcBufferArrayByteLength )
#pragma alloc_text( PAGE, AfdCopyBufferArrayToBuffer )
#pragma alloc_text( PAGE, AfdCopyBufferToBufferArray )
#pragma alloc_text( PAGE, AfdCopyMdlChainToBufferArray )
#pragma alloc_text( PAGEAFD, AfdMapMdlChain )
#pragma alloc_text( PAGEAFD, AfdCopyMdlChainToMdlChain )
#pragma alloc_text( PAGEAFD, AfdAdvanceMdlChain )
#pragma alloc_text( PAGEAFD, AfdAllocateMdlChain )
#pragma alloc_text( PAGE, AfdQueryHandles )
#pragma alloc_text( PAGE, AfdGetInformation )
#pragma alloc_text( PAGEAFD, AfdSetInformation )
#pragma alloc_text( PAGE, AfdSetSecurity )
#pragma alloc_text( PAGE, AfdGetSecurity )
#pragma alloc_text( PAGE, AfdSetInLineMode )
#pragma alloc_text( PAGE, AfdGetContext )
#pragma alloc_text( PAGE, AfdGetRemoteAddress )
#pragma alloc_text( PAGE, AfdSetContext )
#pragma alloc_text( PAGE, AfdIssueDeviceControl )
#pragma alloc_text( PAGE, AfdSetEventHandler )
#pragma alloc_text( PAGE, AfdInsertNewEndpointInList )
#pragma alloc_text( PAGE, AfdRemoveEndpointFromList )
#pragma alloc_text( PAGE, AfdQueryProviderInfo )
#pragma alloc_text( PAGE, AfdLockEndpointContext )
#pragma alloc_text( PAGE, AfdUnlockEndpointContext )
#pragma alloc_text( PAGEAFD, AfdCompleteIrpList )
#pragma alloc_text( PAGEAFD, AfdErrorEventHandler )
#pragma alloc_text( PAGEAFD, AfdErrorExEventHandler )
//#pragma alloc_text( PAGEAFD, AfdRestartDeviceControl ) // can't ever be paged!
#pragma alloc_text( PAGEAFD, AfdGetConnectData )
#pragma alloc_text( PAGEAFD, AfdSetConnectData )
#pragma alloc_text( PAGEAFD, AfdFreeConnectDataBuffers )
#pragma alloc_text( PAGEAFD, AfdSaveReceivedConnectData )
// The routines below can be called when no endpoints are in the list
//#pragma alloc_text( PAGEAFD, AfdDoWork )
//#pragma alloc_text( PAGEAFD, AfdQueueWorkItem )
#pragma alloc_text( PAGEAFD, AfdGetWorkerByRoutine )
#pragma alloc_text( PAGE, AfdProcessLRList)
#pragma alloc_text( PAGEAFD, AfdLRListTimeout)
#pragma alloc_text( PAGEAFD, AfdLRStartTimer)
#pragma alloc_text( PAGEAFD, AfdLRListAddItem)
// Re-enable paging of the routines below when
// KeFlushQueuedDpcs is exported from kernel.
//#pragma alloc_text( PAGEAFD, AfdTrimLookaside)
//#pragma alloc_text( PAGEAFD, AfdCheckLookasideLists)
#if DBG
#pragma alloc_text( PAGEAFD, AfdRecordOutstandingIrpDebug )
#endif
#pragma alloc_text( PAGE, AfdExceptionFilter )
#pragma alloc_text( PAGEAFD, AfdSetQos )
#pragma alloc_text( PAGE, AfdGetQos )
#pragma alloc_text( PAGE, AfdNoOperation )
#pragma alloc_text (PAGE, AfdValidateStatus)
#pragma alloc_text( PAGEAFD, AfdValidateGroup )
#pragma alloc_text( PAGEAFD, AfdCompareAddresses )
#pragma alloc_text( PAGEAFD, AfdGetUnacceptedConnectData )
#pragma alloc_text( PAGE, AfdDoTransportIoctl )
#pragma alloc_text( PAGEAFD, AfdCancelIrp )
#ifdef _WIN64
#pragma alloc_text( PAGEAFD, AfdAllocateMdlChain32 )
#pragma alloc_text( PAGEAFD, AfdSetQos32 )
#pragma alloc_text( PAGE, AfdGetQos32 )
#pragma alloc_text( PAGE, AfdNoOperation32 )
#endif
#ifdef _AFD_VARIABLE_STACK_
#pragma alloc_text( PAGE, AfdFixTransportEntryPointsForBigStackSize )
#pragma alloc_text( PAGEAFD, AfdCallDriverStackIncrease)
#pragma alloc_text( PAGEAFD, AfdGetStackIncreaseIrpAndRecordIt)
#pragma alloc_text( PAGEAFD, AfdGetStackIncreaseIrp)
#pragma alloc_text( PAGEAFD, AfdCancelStackIncreaseIrp)
#pragma alloc_text( PAGEAFD, AfdRestartStackIncreaseIrp)
#endif // _AFD_VARIABLE_STACK_
#endif
VOID
AfdCompleteIrpList (
IN PLIST_ENTRY IrpListHead,
IN PAFD_ENDPOINT Endpoint,
IN NTSTATUS Status,
IN PAFD_IRP_CLEANUP_ROUTINE CleanupRoutine OPTIONAL
)
/*++
Routine Description:
Completes a list of IRPs with the specified status.
Arguments:
IrpListHead - the head of the list of IRPs to complete.
Endpoint - an endpoint which lock which protects the list of IRPs.
Status - the status to use for completing the IRPs.
CleanupRoutine - a pointer to an optional IRP cleanup routine called
before the IRP is completed.
Return Value:
None.
--*/
{
PLIST_ENTRY listEntry;
PIRP irp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
while ( !IsListEmpty( IrpListHead ) ) {
//
// Remove the first IRP from the list, get a pointer to
// the IRP and reset the cancel routine in the IRP. The
// IRP is no longer cancellable.
//
listEntry = RemoveHeadList( IrpListHead );
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
if ( IoSetCancelRoutine( irp, NULL ) == NULL ) {
//
// This IRP is about to be canceled. Look for another in the
// list. Set the Flink to NULL so the cancel routine knows
// it is not on the list.
//
irp->Tail.Overlay.ListEntry.Flink = NULL;
continue;
}
//
// If we have a cleanup routine, call it.
//
if( CleanupRoutine != NULL ) {
if (!(CleanupRoutine)( irp )) {
//
// Cleanup routine indicated that IRP should not
// be completed.
//
continue;
}
}
//
// We must release the locks in order to actually
// complete the IRP. It is OK to release these locks
// because we don't maintain any absolute pointer into
// the list; the loop termination condition is just
// whether the list is completely empty.
//
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
//
// Complete the IRP.
//
irp->IoStatus.Status = Status;
irp->IoStatus.Information = 0;
IoCompleteRequest( irp, AfdPriorityBoost );
//
// Reacquire the locks and continue completing IRPs.
//
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
}
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
return;
} // AfdCompleteIrpList
NTSTATUS
AfdErrorEventHandler (
IN PVOID TdiEventContext,
IN NTSTATUS Status
)
{
PAFD_ENDPOINT endpoint = TdiEventContext;
BOOLEAN result;
CHECK_REFERENCE_ENDPOINT (endpoint, result);
if (!result)
return STATUS_SUCCESS;
switch (Status) {
case STATUS_PORT_UNREACHABLE:
AfdErrorExEventHandler (TdiEventContext, Status, NULL);
break;
default:
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdErrorEventHandler called for endpoint %p\n",
endpoint ));
}
DEREFERENCE_ENDPOINT (endpoint);
return STATUS_SUCCESS;
}
NTSTATUS
AfdErrorExEventHandler (
IN PVOID TdiEventContext,
IN NTSTATUS Status,
IN PVOID Context
)
{
PAFD_ENDPOINT endpoint = TdiEventContext;
BOOLEAN result;
CHECK_REFERENCE_ENDPOINT (endpoint, result);
if (!result)
return STATUS_SUCCESS;
switch (Status) {
case STATUS_PORT_UNREACHABLE:
//
// UDP uses error ex handler to report ICMP rejects
//
if (IS_DGRAM_ENDPOINT (endpoint) &&
!endpoint->Common.Datagram.DisablePUError) {
AFD_LOCK_QUEUE_HANDLE lockHandle;
PLIST_ENTRY listEntry;
PIRP irp = NULL;
PTRANSPORT_ADDRESS sourceAddress = Context;
int sourceAddressLength;
PAFD_BUFFER_TAG afdBuffer;
if (sourceAddress!=NULL) {
sourceAddressLength =
FIELD_OFFSET(TRANSPORT_ADDRESS,
Address[0].Address[sourceAddress->Address[0].AddressLength]);
}
else
sourceAddressLength = 0;
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
//
// First try to fail any of the receive IRPs
//
while (!IsListEmpty (&endpoint->ReceiveDatagramIrpListHead)) {
listEntry = RemoveHeadList( &endpoint->ReceiveDatagramIrpListHead );
//
// Get a pointer to the IRP and reset the cancel routine in
// the IRP. The IRP is no longer cancellable.
//
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
if ( IoSetCancelRoutine( irp, NULL ) != NULL ) {
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
irp->IoStatus.Status = Status;
irp->IoStatus.Information = 0;
AfdSetupReceiveDatagramIrp (irp, NULL, 0, NULL, 0,
sourceAddress,
sourceAddressLength,
0
);
IoCompleteRequest( irp, AfdPriorityBoost );
goto Exit;
}
else {
//
// This IRP is about to be canceled. Look for another in the
// list. Set the Flink to NULL so the cancel routine knows
// it is not on the list.
//
irp->Tail.Overlay.ListEntry.Flink = NULL;
irp = NULL;
}
}
ASSERT (irp==NULL);
//
// See if there are any PEEK IRPs
//
while (!IsListEmpty (&endpoint->PeekDatagramIrpListHead)) {
listEntry = RemoveHeadList( &endpoint->PeekDatagramIrpListHead );
//
// Get a pointer to the IRP and reset the cancel routine in
// the IRP. The IRP is no longer cancellable.
//
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
if ( IoSetCancelRoutine( irp, NULL ) != NULL ) {
break;
}
else {
//
// This IRP is about to be canceled. Look for another in the
// list. Set the Flink to NULL so the cancel routine knows
// it is not on the list.
//
irp->Tail.Overlay.ListEntry.Flink = NULL;
irp = NULL;
}
}
//
// If we can buffer this indication, do it
//
if (endpoint->DgBufferredReceiveBytes <
endpoint->Common.Datagram.MaxBufferredReceiveBytes &&
(endpoint->DgBufferredReceiveBytes>0 ||
(endpoint->DgBufferredReceiveCount*sizeof (AFD_BUFFER_TAG)) <
endpoint->Common.Datagram.MaxBufferredReceiveBytes) ) {
afdBuffer = AfdGetBufferTag( sourceAddressLength, endpoint->OwningProcess );
if ( afdBuffer != NULL) {
//
// Save the status do distinguish this from
// normal datagram IRP
//
afdBuffer->Status = Status;
afdBuffer->DataLength = 0;
afdBuffer->DatagramFlags = 0;
afdBuffer->DataOffset = 0;
RtlCopyMemory(
afdBuffer->TdiInfo.RemoteAddress,
sourceAddress,
sourceAddressLength
);
afdBuffer->TdiInfo.RemoteAddressLength = sourceAddressLength;
//
// Place the buffer on this endpoint's list of bufferred datagrams
// and update the counts of datagrams and datagram bytes on the
// endpoint.
//
InsertTailList(
&endpoint->ReceiveDatagramBufferListHead,
&afdBuffer->BufferListEntry
);
endpoint->DgBufferredReceiveCount++;
//
// All done. Release the lock and tell the provider that we
// took all the data.
//
AfdIndicateEventSelectEvent(
endpoint,
AFD_POLL_RECEIVE,
STATUS_SUCCESS
);
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Indicate that it is possible to receive on the endpoint now.
//
AfdIndicatePollEvent(
endpoint,
AFD_POLL_RECEIVE,
STATUS_SUCCESS
);
}
else {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
}
else {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
//
// If there was a peek IRP on the endpoint, complete it now.
//
if ( irp != NULL ) {
irp->IoStatus.Status = Status;
irp->IoStatus.Information = 0;
AfdSetupReceiveDatagramIrp (irp, NULL, 0, NULL, 0,
sourceAddress,
sourceAddressLength,
0
);
IoCompleteRequest( irp, AfdPriorityBoost );
}
}
break;
}
Exit:
DEREFERENCE_ENDPOINT (endpoint);
return STATUS_SUCCESS;
} // AfdErrorEventHandler
VOID
AfdInsertNewEndpointInList (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Inserts a new endpoint in the global list of AFD endpoints. If this
is the first endpoint, then this routine does various allocations to
prepare AFD for usage.
Arguments:
Endpoint - the endpoint being added.
Return Value:
None.
--*/
{
PAGED_CODE( );
//
// Acquire a lock which prevents other threads from performing this
// operation.
//
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
InterlockedIncrement(
&AfdEndpointsOpened
);
//
// If the list of endpoints is empty, do some allocations.
//
if ( IsListEmpty( &AfdEndpointListHead ) ) {
//
// Tell MM to revert to normal paging semantics.
//
if (!AfdLoaded) {
MmResetDriverPaging( (PVOID)DriverEntry );
AfdLoaded = (PKEVENT)1;
}
//
// Lock down the AFD section that cannot be pagable if any
// sockets are open.
//
ASSERT( AfdDiscardableCodeHandle == NULL );
AfdDiscardableCodeHandle = MmLockPagableCodeSection( (PVOID)AfdGetBufferFast );
ASSERT( AfdDiscardableCodeHandle != NULL );
//
// Add extra reference to afd device object so that the
// driver cannot be unloaded while at least one endpoint
// is in the list.
//
ObReferenceObject (AfdDeviceObject);
//
// Setup 30 sec timer to flush lookaside lists
// if too many items are there for too long.
//
KeInitializeTimer (&AfdLookasideLists->Timer);
KeInitializeDpc (&AfdLookasideLists->Dpc, AfdCheckLookasideLists, AfdLookasideLists);
{
LARGE_INTEGER dueTime;
dueTime.QuadPart = -(30*1000*1000*10);
KeSetTimerEx (&AfdLookasideLists->Timer,
dueTime,
30*1000,
&AfdLookasideLists->Dpc);
}
}
ASSERT (AfdLoaded==(PKEVENT)1);
//
// Add the endpoint to the list(s).
//
InsertHeadList(
&AfdEndpointListHead,
&Endpoint->GlobalEndpointListEntry
);
if( Endpoint->GroupType == GroupTypeConstrained ) {
InsertHeadList(
&AfdConstrainedEndpointListHead,
&Endpoint->ConstrainedEndpointListEntry
);
}
//
// Release the lock and return.
//
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
return;
} // AfdInsertNewEndpointInList
VOID
AfdRemoveEndpointFromList (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Removes a new endpoint from the global list of AFD endpoints. If
this is the last endpoint in the list, then this routine does
various deallocations to save resource utilization.
Arguments:
Endpoint - the endpoint being removed.
Return Value:
None.
--*/
{
PAGED_CODE( );
//
// Acquire a lock which prevents other threads from performing this
// operation.
//
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
InterlockedIncrement(
&AfdEndpointsClosed
);
//
// Remove the endpoint from the list(s).
//
RemoveEntryList(
&Endpoint->GlobalEndpointListEntry
);
if( Endpoint->GroupType == GroupTypeConstrained ) {
RemoveEntryList(
&Endpoint->ConstrainedEndpointListEntry
);
}
//
// If the list of endpoints is now empty, do some deallocations.
//
if ( IsListEmpty( &AfdEndpointListHead ) ) {
//
// Stop the timer that scans lookaside lists.
//
KeCancelTimer (&AfdLookasideLists->Timer);
//
// Make sure DPC is completed since we may need to reinitialize
// it after we exit this routine and new endpoint is created again.
//
KeRemoveQueueDpc (&AfdLookasideLists->Dpc);
//
// Make sure that DPC routine has actually completed before
// unlocking code section where this routine resides.
//
// Not exported from kernel - so don't put the routine
// into the discardable code section until it is.
//
// KeFlushQueuedDpcs ();
//
// We don't need PnP stuff anymore.
//
AfdDeregisterPnPHandlers (NULL);
//
// Unlock the AFD section that can be pagable when no sockets
// are open.
//
ASSERT( IsListEmpty( &AfdConstrainedEndpointListHead ) );
ASSERT( AfdDiscardableCodeHandle != NULL );
MmUnlockPagableImageSection( AfdDiscardableCodeHandle );
AfdDiscardableCodeHandle = NULL;
//
// Queue off an executive worker thread to unlock AFD. We do
// this using special hacks in the AFD worker thread code so
// that we don't need to acuire a spin lock after the unlock.
//
AfdQueueWorkItem( AfdUnlockDriver, &AfdUnloadWorker );
}
//
// Release the lock and return.
//
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
return;
} // AfdRemoveEndpointFromList
VOID
AfdUnlockDriver (
IN PVOID Context
)
{
UNREFERENCED_PARAMETER (Context);
//
// Acquire a lock which prevents other threads from performing this
// operation.
//
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
//
// Test whether the endpoint list remains empty. If it is still
// empty, we can proceed with unlocking the driver. If a new
// endpoint has been placed on the list, then do not make AFD
// pagable.
//
if ( IsListEmpty( &AfdEndpointListHead ) ) {
//
// Tell MM that it can page all of AFD as it desires.
//
if (AfdLoaded!=NULL && AfdLoaded!=(PKEVENT)1) {
KeSetEvent (AfdLoaded, AfdPriorityBoost, FALSE);
}
else {
MmPageEntireDriver( (PVOID)DriverEntry );
}
AfdLoaded = NULL;
}
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
} // AfdUnlockDriver
NTSTATUS
AfdQueryHandles (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Returns information about the TDI handles corresponding to an AFD
endpoint. NULL is returned for either the connection handle or the
address handle (or both) if the endpoint does not have that particular
object.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
AFD_HANDLE_INFO handleInfo;
ULONG getHandleInfo;
NTSTATUS status;
UNREFERENCED_PARAMETER (IoctlCode);
PAGED_CODE( );
//
// Set up local pointers.
//
*Information = 0;
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Make sure that the input and output buffers are large enough.
//
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
if ( InputBufferLength < sizeof(getHandleInfo) ||
OutputBufferLength < sizeof(AFD_HANDLE_INFO32) ) {
return STATUS_BUFFER_TOO_SMALL;
}
}
else
#endif
{
if ( InputBufferLength < sizeof(getHandleInfo) ||
OutputBufferLength < sizeof(handleInfo) ) {
return STATUS_BUFFER_TOO_SMALL;
}
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (getHandleInfo),
PROBE_ALIGNMENT(ULONG));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
getHandleInfo = *((PULONG)InputBuffer);
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
return status;
}
//
// If no handle information or invalid handle information was
// requested, fail.
//
if ( (getHandleInfo &
~(AFD_QUERY_ADDRESS_HANDLE | AFD_QUERY_CONNECTION_HANDLE)) != 0 ||
getHandleInfo == 0 ) {
return STATUS_INVALID_PARAMETER;
}
//
// Initialize the output buffer.
//
handleInfo.TdiAddressHandle = NULL;
handleInfo.TdiConnectionHandle = NULL;
//
// If the caller requested a TDI address handle and we have an
// address handle for this endpoint, dupe the address handle to the
// user process.
//
if ( (getHandleInfo & AFD_QUERY_ADDRESS_HANDLE) != 0 &&
(endpoint->State == AfdEndpointStateBound ||
endpoint->State == AfdEndpointStateConnected) &&
endpoint->AddressFileObject != NULL ) {
// If transport does not support new TDI_SERVICE_FORCE_ACCESS_CHECK_FLAG
// we get the maximum possible access for the handle so that helper
// DLL can do what it wants with it. Of course this compromises the
// security, but we can't enforce it without the transport cooperation.
status = ObOpenObjectByPointer(
endpoint->AddressFileObject,
OBJ_CASE_INSENSITIVE,
NULL,
MAXIMUM_ALLOWED,
*IoFileObjectType,
(KPROCESSOR_MODE)((endpoint->TdiServiceFlags&TDI_SERVICE_FORCE_ACCESS_CHECK)
? RequestorMode
: KernelMode),
&handleInfo.TdiAddressHandle
);
if ( !NT_SUCCESS(status) ) {
return status;
}
}
//
// If the caller requested a TDI connection handle and we have a
// connection handle for this endpoint, dupe the connection handle
// to the user process. Note that we can have a connection and
// TDI handle when endpoint is in process of being connected.
// We should not return the connection handle until enpoint is
// fully connected or it may go away while we are trying to
// reference it if connection fails (bug 93096)
//
if ( (getHandleInfo & AFD_QUERY_CONNECTION_HANDLE) != 0 &&
(endpoint->Type & AfdBlockTypeVcConnecting) == AfdBlockTypeVcConnecting &&
endpoint->State == AfdEndpointStateConnected &&
((connection=AfdGetConnectionReferenceFromEndpoint (endpoint))!=NULL)) {
ASSERT( connection->Type == AfdBlockTypeConnection );
ASSERT( connection->FileObject != NULL );
// If transport does not support new TDI_SERVICE_FORCE_ACCESS_CHECK_FLAG
// we get the maximum possible access for the handle so that helper
// DLL can do what it wants with it. Of course this compromises the
// security, but we can't enforce it without the transport cooperation.
status = ObOpenObjectByPointer(
connection->FileObject,
OBJ_CASE_INSENSITIVE,
NULL,
MAXIMUM_ALLOWED,
*IoFileObjectType,
(KPROCESSOR_MODE)((endpoint->TdiServiceFlags & TDI_SERVICE_FORCE_ACCESS_CHECK)
? RequestorMode
: KernelMode),
&handleInfo.TdiConnectionHandle
);
DEREFERENCE_CONNECTION (connection);
if ( !NT_SUCCESS(status) ) {
if ( handleInfo.TdiAddressHandle != NULL ) {
//
// Call ObCloseHandle directly (instead of ZwClose) to be able
// to set PreviousMode. ZwClose goes thru TRAP which always
// results in PreviousMode==KernelMode which will cause
// bugcheck if app managed to close this handle between our
// creating it and now
//
ObCloseHandle( handleInfo.TdiAddressHandle, RequestorMode );
}
return status;
}
}
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
if (RequestorMode!=KernelMode) {
ProbeForWrite (OutputBuffer,
sizeof (AFD_HANDLE_INFO32),
PROBE_ALIGNMENT32 (AFD_HANDLE_INFO32));
}
((PAFD_HANDLE_INFO32)OutputBuffer)->TdiAddressHandle =
(VOID * POINTER_32)HandleToUlong(handleInfo.TdiAddressHandle);
((PAFD_HANDLE_INFO32)OutputBuffer)->TdiConnectionHandle =
(VOID * POINTER_32)HandleToUlong(handleInfo.TdiConnectionHandle);
*Information = sizeof (AFD_HANDLE_INFO32);
}
else
#endif
{
if (RequestorMode!=KernelMode) {
ProbeAndWriteStructure (((PAFD_HANDLE_INFO)OutputBuffer),
handleInfo,
AFD_HANDLE_INFO);
}
else {
*((PAFD_HANDLE_INFO)OutputBuffer) = handleInfo;
}
*Information = sizeof (handleInfo);
}
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
if ( handleInfo.TdiAddressHandle != NULL ) {
//
// Call ObCloseHandle directly (instead of ZwClose) to be able
// to set PreviousMode. ZwClose goes thru TRAP which always
// results in PreviousMode==KernelMode which will cause
// bugcheck if app managed to close this handle between our
// creating it and now
//
ObCloseHandle( handleInfo.TdiAddressHandle, RequestorMode );
}
if ( handleInfo.TdiConnectionHandle != NULL ) {
//
// Call ObCloseHandle directly (instead of ZwClose) to be able
// to set PreviousMode. ZwClose goes thru TRAP which always
// results in PreviousMode==KernelMode which will cause
// bugcheck if app managed to close this handle between our
// creating it and now
//
ObCloseHandle( handleInfo.TdiConnectionHandle, RequestorMode );
}
return status;
}
return STATUS_SUCCESS;
} // AfdQueryHandles
NTSTATUS
AfdGetInformation (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Gets information in the endpoint.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
AFD_INFORMATION afdInfo;
NTSTATUS status;
LONGLONG currentTime;
LONGLONG connectTime;
UNREFERENCED_PARAMETER (IoctlCode);
PAGED_CODE( );
//
// Initialize number of bytes returned to zero.
//
*Information = 0;
//
// Initialize local variables.
//
endpoint = FileObject->FsContext;
ASSERT(IS_AFD_ENDPOINT_TYPE(endpoint));
if (endpoint->Type==AfdBlockTypeHelper ||
endpoint->Type==AfdBlockTypeSanHelper)
return STATUS_INVALID_PARAMETER;
RtlZeroMemory(&afdInfo, sizeof(afdInfo));
status = STATUS_SUCCESS;
//
// Make sure that the input and output buffers are large enough.
//
#ifdef _WIN64
{
C_ASSERT(sizeof(AFD_INFORMATION) == sizeof(AFD_INFORMATION32));
}
#endif
if ((InputBufferLength < sizeof(afdInfo)) ||
(OutputBufferLength < sizeof(afdInfo))) {
return STATUS_BUFFER_TOO_SMALL;
}
AFD_W4_INIT ASSERT(status == STATUS_SUCCESS);
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (afdInfo),
PROBE_ALIGNMENT32(AFD_INFORMATION32));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
afdInfo.InformationType = ((PAFD_INFORMATION32)InputBuffer)->InformationType;
}
else
#endif _WIN64
{
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (afdInfo),
PROBE_ALIGNMENT(AFD_INFORMATION));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
afdInfo.InformationType = ((PAFD_INFORMATION)InputBuffer)->InformationType;
}
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
return status;
}
//
// Set up appropriate information in the endpoint.
//
switch ( afdInfo.InformationType ) {
case AFD_MAX_PATH_SEND_SIZE:
if (InputBufferLength>sizeof (afdInfo) &&
(endpoint->State==AfdEndpointStateBound || endpoint->State==AfdEndpointStateConnected)) {
TDI_REQUEST_KERNEL_QUERY_INFORMATION kernelQueryInfo;
TDI_CONNECTION_INFORMATION connectionInfo;
PMDL mdl;
InputBuffer = (PUCHAR)InputBuffer+sizeof (afdInfo);
InputBufferLength -= sizeof (afdInfo);
mdl = IoAllocateMdl(
InputBuffer, // VirtualAddress
InputBufferLength, // Length
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
NULL // Irp
);
if (mdl!=NULL) {
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
MmProbeAndLockPages(
mdl, // MemoryDescriptorList
RequestorMode, // AccessMode
IoWriteAccess // Operation
);
status = STATUS_SUCCESS;
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT(NT_ERROR (status));
}
if (NT_SUCCESS (status)) {
connectionInfo.RemoteAddress = MmGetSystemAddressForMdlSafe (mdl, LowPagePriority);
if (connectionInfo.RemoteAddress!=NULL) {
connectionInfo.RemoteAddressLength = InputBufferLength;
//
// Set up a query to the TDI provider to obtain the largest
// datagram that can be sent to a particular address.
//
kernelQueryInfo.QueryType = TDI_QUERY_MAX_DATAGRAM_INFO;
kernelQueryInfo.RequestConnectionInformation = &connectionInfo;
connectionInfo.UserDataLength = 0;
connectionInfo.UserData = NULL;
connectionInfo.OptionsLength = 0;
connectionInfo.Options = NULL;
//
// Ask the TDI provider for the information.
//
status = AfdIssueDeviceControl(
endpoint->AddressFileObject,
&kernelQueryInfo,
sizeof(kernelQueryInfo),
&afdInfo.Information.Ulong,
sizeof(afdInfo.Information.Ulong),
TDI_QUERY_INFORMATION
);
}
else
status = STATUS_INSUFFICIENT_RESOURCES;
MmUnlockPages (mdl);
}
IoFreeMdl (mdl);
}
else
status = STATUS_INSUFFICIENT_RESOURCES;
//
// If the request succeeds, use this information. Otherwise,
// fall through and use the transport's global information.
// This is done because not all transports support this
// particular TDI request, and for those which do not the
// global information is a reasonable approximation.
//
if ( NT_SUCCESS(status) ) {
break;
}
}
case AFD_MAX_SEND_SIZE:
{
//
// With PnP some provider info fields can change over time.
// so we query them each time we are asked.
//
TDI_PROVIDER_INFO providerInfo;
status = AfdQueryProviderInfo (
&endpoint->TransportInfo->TransportDeviceName,
#ifdef _AFD_VARIABLE_STACK_
NULL,
#endif //_AFD_VARIABLE_STACK_
&providerInfo);
if (NT_SUCCESS (status)) {
//
// Return the MaxSendSize or MaxDatagramSendSize from the
// TDI_PROVIDER_INFO based on whether or not this is a datagram
// endpoint.
//
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
afdInfo.Information.Ulong = providerInfo.MaxDatagramSize;
} else {
afdInfo.Information.Ulong = providerInfo.MaxSendSize;
}
}
}
break;
case AFD_SENDS_PENDING:
//
// If this is an endpoint on a bufferring transport, no sends
// are pending in AFD. If it is on a nonbufferring transport,
// return the count of sends pended in AFD.
//
if ( IS_TDI_BUFFERRING(endpoint) ||
(endpoint->Type & AfdBlockTypeVcConnecting) != AfdBlockTypeVcConnecting ||
endpoint->State != AfdEndpointStateConnected ||
((connection=AfdGetConnectionReferenceFromEndpoint (endpoint))==NULL)) {
afdInfo.Information.Ulong = 0;
} else {
afdInfo.Information.Ulong = connection->VcBufferredSendCount;
DEREFERENCE_CONNECTION (connection);
}
break;
case AFD_RECEIVE_WINDOW_SIZE:
//
// Return the default receive window.
//
afdInfo.Information.Ulong = AfdReceiveWindowSize;
break;
case AFD_SEND_WINDOW_SIZE:
//
// Return the default send window.
//
afdInfo.Information.Ulong = AfdSendWindowSize;
break;
case AFD_CONNECT_TIME:
//
// If the endpoint is not yet connected, return -1. Otherwise,
// calculate the number of seconds that the connection has been
// active.
//
if ( endpoint->State != AfdEndpointStateConnected ||
IS_DGRAM_ENDPOINT (endpoint) ||
(connection=AfdGetConnectionReferenceFromEndpoint( endpoint ))==NULL) {
afdInfo.Information.Ulong = 0xFFFFFFFF;
} else {
ASSERT( connection->Type == AfdBlockTypeConnection );
//
// Calculate how long the connection has been active by
// subtracting the time at which the connection started from
// the current time. Note that we convert the units of the
// time value from 100s of nanoseconds to seconds.
//
currentTime = KeQueryInterruptTime ();
connectTime = (currentTime - connection->ConnectTime);
connectTime /= 10*1000*1000;
//
// We can safely convert this to a ULONG because it takes
// 127 years to overflow a ULONG counting seconds. The
// bizarre conversion to a LARGE_INTEGER is required to
// prevent the compiler from optimizing out the full 64-bit
// division above. Without this, the compiler would do only
// a 32-bit division and lose some information.
//
//afdInfo->Information.Ulong = (ULONG)connectTime;
afdInfo.Information.Ulong = ((PLARGE_INTEGER)&connectTime)->LowPart;
DEREFERENCE_CONNECTION (connection);
}
break;
case AFD_GROUP_ID_AND_TYPE : {
PAFD_GROUP_INFO groupInfo;
groupInfo = (PAFD_GROUP_INFO)&afdInfo.Information.LargeInteger;
//
// Return the endpoint's group ID and group type.
//
groupInfo->GroupID = endpoint->GroupID;
groupInfo->GroupType = endpoint->GroupType;
}
break;
default:
return STATUS_INVALID_PARAMETER;
}
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForWrite (OutputBuffer,
sizeof (afdInfo),
PROBE_ALIGNMENT32(AFD_INFORMATION32));
}
//
// Copy parameters back to application's memory
//
RtlMoveMemory(InputBuffer,
&afdInfo,
sizeof (afdInfo));
}
else
#endif _WIN64
{
//
// Validate the output structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeAndWriteStructure (((PAFD_INFORMATION)OutputBuffer),
afdInfo,
AFD_INFORMATION);
}
else {
//
// Copy parameters back to application's memory
//
*((PAFD_INFORMATION)OutputBuffer) = afdInfo;
}
}
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
return status;
}
*Information = sizeof(afdInfo);
return STATUS_SUCCESS;
} // AfdGetInformation
NTSTATUS
AfdSetInformation (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Sets information in the endpoint.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
AFD_INFORMATION afdInfo;
NTSTATUS status;
AFD_LOCK_QUEUE_HANDLE lockHandle;
UNREFERENCED_PARAMETER (IoctlCode);
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
//
// Nothing to return.
//
*Information = 0;
//
// Initialize locals for cleanup.
//
connection = NULL;
status = STATUS_SUCCESS;
//
// Set up local pointers.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
if (endpoint->Type==AfdBlockTypeHelper ||
endpoint->Type==AfdBlockTypeSanHelper)
return STATUS_INVALID_PARAMETER;
//
// Make sure that the input buffer is large enough.
//
#ifdef _WIN64
{
C_ASSERT (sizeof (AFD_INFORMATION)==sizeof (AFD_INFORMATION32));
}
#endif
if ( InputBufferLength < sizeof(afdInfo) ) {
return STATUS_BUFFER_TOO_SMALL;
}
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (afdInfo),
PROBE_ALIGNMENT32(AFD_INFORMATION32));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
RtlMoveMemory (&afdInfo, InputBuffer, sizeof (afdInfo));
}
else
#endif _WIN64
{
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (afdInfo),
PROBE_ALIGNMENT(AFD_INFORMATION));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
afdInfo = *((PAFD_INFORMATION)InputBuffer);
}
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
return status;
}
//
// Set up appropriate information in the endpoint.
//
switch ( afdInfo.InformationType ) {
case AFD_NONBLOCKING_MODE:
//
// Set the blocking mode of the endpoint. If TRUE, send and receive
// calls on the endpoint will fail if they cannot be completed
// immediately.
//
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
endpoint->NonBlocking = (afdInfo.Information.Boolean!=FALSE);
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
break;
case AFD_CIRCULAR_QUEUEING:
//
// Enables circular queuing on the endpoint.
//
if( !IS_DGRAM_ENDPOINT( endpoint ) ) {
status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
endpoint->Common.Datagram.CircularQueueing = (afdInfo.Information.Boolean!=FALSE);
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
break;
case AFD_REPORT_PORT_UNREACHABLE:
//
// Enables reporting PORT_UNREACHABLE to the app.
//
if( !IS_DGRAM_ENDPOINT( endpoint ) ) {
status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
endpoint->Common.Datagram.DisablePUError = (afdInfo.Information.Boolean==FALSE);
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
break;
case AFD_INLINE_MODE:
//
// Set the inline mode of the endpoint. If TRUE, a receive for
// normal data will be completed with either normal data or
// expedited data. If the endpoint is connected, we need to
// tell the TDI provider that the endpoint is inline so that it
// delivers data to us in order. If the endpoint is not yet
// connected, then we will set the inline mode when we create
// the TDI connection object.
//
if ( (endpoint->Type & AfdBlockTypeVcConnecting) == AfdBlockTypeVcConnecting ) {
connection = AfdGetConnectionReferenceFromEndpoint( endpoint );
if (connection!=NULL) {
status = AfdSetInLineMode(
connection,
afdInfo.Information.Boolean
);
if ( !NT_SUCCESS(status) ) {
goto Cleanup;
}
}
}
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
endpoint->InLine = (afdInfo.Information.Boolean!=FALSE);
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
break;
case AFD_RECEIVE_WINDOW_SIZE:
case AFD_SEND_WINDOW_SIZE: {
PCLONG maxBytes;
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
//
// First determine where the appropriate limits are stored in the
// connection or endpoint. We do this so that we can use common
// code to charge quota and set the new counters.
//
if ( (endpoint->Type & AfdBlockTypeVcConnecting) == AfdBlockTypeVcConnecting &&
endpoint->State == AfdEndpointStateConnected &&
endpoint->Common.VcConnecting.Connection!=NULL ) {
if ( afdInfo.InformationType == AFD_SEND_WINDOW_SIZE ) {
maxBytes = &endpoint->Common.VcConnecting.Connection->MaxBufferredSendBytes;
} else {
maxBytes = &endpoint->Common.VcConnecting.Connection->MaxBufferredReceiveBytes;
}
} else if ( IS_DGRAM_ENDPOINT(endpoint) ) {
if ( afdInfo.InformationType == AFD_SEND_WINDOW_SIZE ) {
maxBytes = &endpoint->Common.Datagram.MaxBufferredSendBytes;
} else {
maxBytes = &endpoint->Common.Datagram.MaxBufferredReceiveBytes;
}
} else if (IS_SAN_ENDPOINT (endpoint) ) {
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
status = STATUS_SUCCESS;
goto Cleanup;
}
else {
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// Make sure that we always allow at least one message to be
// bufferred on an endpoint.
//
if ( afdInfo.Information.Ulong == 0 ) {
//
// Don't allow the max receive bytes to go to zero, but
// max send bytes IS allowed to go to zero because it has
// special meaning: specifically, do not buffer sends.
//
if ( afdInfo.InformationType == AFD_RECEIVE_WINDOW_SIZE ) {
afdInfo.Information.Ulong = 1;
}
else {
ASSERT (afdInfo.InformationType == AFD_SEND_WINDOW_SIZE);
endpoint->DisableFastIoSend = TRUE;
}
}
else {
if( afdInfo.InformationType == AFD_SEND_WINDOW_SIZE ) {
endpoint->DisableFastIoSend = FALSE;
}
}
//
// Set up the new information in the AFD internal structure.
//
*maxBytes = (CLONG)afdInfo.Information.Ulong;
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
break;
}
default:
status = STATUS_INVALID_PARAMETER;
}
Cleanup:
if (connection!=NULL) {
DEREFERENCE_CONNECTION (connection);
}
return status;
} // AfdSetInformation
NTSTATUS
AfdSetSecurity (
IN PAFD_ENDPOINT Endpoint,
IN SECURITY_INFORMATION SecurityInformation,
IN PSECURITY_DESCRIPTOR SecurityDescriptor
)
{
NTSTATUS status;
PSECURITY_DESCRIPTOR newSd, oldSd, cachedSd;
PAGED_CODE ();
ASSERT(Endpoint->TransportInfo ||
Endpoint->Type==AfdBlockTypeHelper ||
Endpoint->Type==AfdBlockTypeSanHelper);
if (Endpoint->TransportInfo &&
Endpoint->TransportInfo->InfoValid) {
//
// Transport has already been loaded
// ensure we have up to date flags
//
Endpoint->TdiServiceFlags = Endpoint->TransportInfo->ProviderInfo.ServiceFlags;
if (!IS_TDI_ADDRESS_SECURITY (Endpoint)) {
//
// SD is not supported by the transport -> bail.
// IO manager will still succeed the request and
// will assume a World descriptor (as it does for FAT).
//
status = STATUS_INVALID_DEVICE_REQUEST;
goto complete;
}
}
else {
//
// We do not know yet if we can support this feature, fail it.
//
status = STATUS_NOT_IMPLEMENTED;
goto complete;
}
//
// Protect SD setting with state change lock.
//
if (!AFD_START_STATE_CHANGE (Endpoint, AfdEndpointStateOpen)) {
status = STATUS_INVALID_DEVICE_REQUEST;
goto complete;
}
if (Endpoint->State!=AfdEndpointStateOpen) {
//
// We may want to call transport if endpoint state
// is bound or connected.
//
status = STATUS_NOT_IMPLEMENTED;
goto complete_state_change;
}
//
// Call the security routine to do the actual set
//
newSd = oldSd = Endpoint->SecurityDescriptor;
if (newSd==NULL) {
ACCESS_STATE accessState;
AUX_ACCESS_DATA auxData;
status = SeCreateAccessState (&accessState,
&auxData,
GENERIC_ALL,
IoGetFileObjectGenericMapping());
if (NT_SUCCESS (status)) {
SeLockSubjectContext (&accessState.SubjectSecurityContext);
status = SeAssignSecurity (
NULL, // Parent SD - not used
SecurityDescriptor,
&newSd,
FALSE,
&accessState.SubjectSecurityContext,
IoGetFileObjectGenericMapping(),
PagedPool);
SeUnlockSubjectContext (&accessState.SubjectSecurityContext);
SeDeleteAccessState (&accessState);
}
}
else {
status = SeSetSecurityDescriptorInfo( NULL,
&SecurityInformation,
SecurityDescriptor,
&newSd,
PagedPool,
IoGetFileObjectGenericMapping() );
}
if (NT_SUCCESS(status)) {
status = ObLogSecurityDescriptor (newSd,
&cachedSd,
1);
ExFreePool (newSd);
if (NT_SUCCESS(status)) {
Endpoint->SecurityDescriptor = cachedSd;
if (oldSd!=NULL) {
ObDereferenceSecurityDescriptor( oldSd, 1 );
}
}
}
complete_state_change:
AFD_END_STATE_CHANGE (Endpoint);
complete:
return status;
}
NTSTATUS
AfdGetSecurity (
IN PAFD_ENDPOINT Endpoint,
IN SECURITY_INFORMATION SecurityInformation,
IN ULONG BufferLength,
OUT PVOID Buffer,
OUT PSIZE_T DataLength
)
{
NTSTATUS status;
PSECURITY_DESCRIPTOR sd;
PAGED_CODE ();
ASSERT(Endpoint->TransportInfo ||
Endpoint->Type==AfdBlockTypeHelper ||
Endpoint->Type==AfdBlockTypeSanHelper);
if (Endpoint->TransportInfo &&
Endpoint->TransportInfo->InfoValid) {
//
// Transport has already been loaded
// ensure we have up to date flags
//
Endpoint->TdiServiceFlags = Endpoint->TransportInfo->ProviderInfo.ServiceFlags;
if (!IS_TDI_ADDRESS_SECURITY (Endpoint)) {
//
// SD is not supported by the transport -> bail.
// IO manager will still succeed the request and
// will return a World descriptor (as it does for FAT).
//
status = STATUS_INVALID_DEVICE_REQUEST;
goto complete;
}
}
else {
//
// We do not know yet if we can support this feature -> bail.
// IO manager will still succeed the request and
// will return a World descriptor (as it does for FAT).
//
status = STATUS_INVALID_DEVICE_REQUEST;
goto complete;
}
if (!AFD_PREVENT_STATE_CHANGE (Endpoint)) {
//
// IO manager will still succeed the request and
// will return a World descriptor (as it does for FAT).
//
status = STATUS_INVALID_DEVICE_REQUEST;
goto complete;
}
if (Endpoint->State!=AfdEndpointStateOpen) {
//
// IO manager will still succeed the request and
// will return a World descriptor (as it does for FAT).
//
status = STATUS_INVALID_DEVICE_REQUEST;
goto complete_state_change;
}
sd = Endpoint->SecurityDescriptor;
if (sd==NULL) {
ACCESS_STATE accessState;
AUX_ACCESS_DATA auxData;
status = SeCreateAccessState (&accessState,
&auxData,
GENERIC_ALL,
IoGetFileObjectGenericMapping());
if (!NT_SUCCESS (status)) {
goto complete_state_change;
}
SeLockSubjectContext (&accessState.SubjectSecurityContext);
status = SeAssignSecurity (
NULL, // Parent SD - not used
NULL,
&sd,
FALSE,
&accessState.SubjectSecurityContext,
IoGetFileObjectGenericMapping(),
PagedPool);
SeUnlockSubjectContext (&accessState.SubjectSecurityContext);
SeDeleteAccessState (&accessState);
}
//
// Call the security routine to do the actual query
//
status = SeQuerySecurityDescriptorInfo( &SecurityInformation,
Buffer,
&BufferLength,
&sd );
if (status == STATUS_BUFFER_TOO_SMALL ) {
*DataLength = BufferLength;
status = STATUS_BUFFER_OVERFLOW;
}
if (sd!=Endpoint->SecurityDescriptor) {
ExFreePool (sd);
}
complete_state_change:
AFD_REALLOW_STATE_CHANGE (Endpoint);
complete:
return status;
}
NTSTATUS
AfdSetInLineMode (
IN PAFD_CONNECTION Connection,
IN BOOLEAN InLine
)
/*++
Routine Description:
Sets a connection to be in inline mode. In inline mode, urgent data
is delivered in the order in which it is received. We must tell the
TDI provider about this so that it indicates data in the proper
order.
Arguments:
Connection - the AFD connection to set as inline.
InLine - TRUE to enable inline mode, FALSE to disable inline mode.
Return Value:
NTSTATUS -- Indicates whether the request was successfully
performed.
--*/
{
//
// Since TCP does not implement this correctly, do everything in AFD!!!
// Background:
// When this options is enabled, TCP indicates all the data as normal
// data, so we end up mixing it together which is against the spec.
// Also, since TCP stops reporting expedited data, SIOATMARK fails
// to report presence of OOB data altogether.
// When handling OOB data completely inside AFD we can only run into
// one problem: if AFD runs out of its receive buffer for the socket
// and refuses to accept more data from TCP so that TCP buffers it
// within itself, any OOB data arriving at this point can be indicated
// out of order (not inline).
//
// Well, this appears to be even worse. Some apps (SQL) send more than
// one byte of OOB data, TCP can only send one, so it sends everything
// but the last byte as normal and the last one as OOB. It then turns
// around and indicates the OOB (last byte) first which breaks the
// ordering required by OOBINLINE.
// In the end, we are broken one way or the other, so keep the things
// the way they were for number of years and wait for TCP to fix.
NTSTATUS status;
PTCP_REQUEST_SET_INFORMATION_EX setInfoEx;
TCPSocketOption *option;
UCHAR buffer[sizeof(*setInfoEx) + sizeof(*option)];
IO_STATUS_BLOCK ioStatusBlock;
KEVENT event;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PAGED_CODE( );
//
// Initialize the TDI information buffers.
//
setInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)buffer;
setInfoEx->ID.toi_entity.tei_entity = CO_TL_ENTITY;
setInfoEx->ID.toi_entity.tei_instance = TL_INSTANCE;
setInfoEx->ID.toi_class = INFO_CLASS_PROTOCOL;
setInfoEx->ID.toi_type = INFO_TYPE_CONNECTION;
setInfoEx->ID.toi_id = TCP_SOCKET_OOBINLINE;
setInfoEx->BufferSize = sizeof(*option);
option = (TCPSocketOption *)&setInfoEx->Buffer;
option->tso_value = InLine;
//
// Initialize the kernel event that will signal I/O completion.
//
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
//
// Build TDI set information IRP.
//
irp = IoBuildDeviceIoControlRequest (
IOCTL_TCP_SET_INFORMATION_EX,
Connection->DeviceObject,
setInfoEx,
sizeof(*setInfoEx) + setInfoEx->BufferSize,
NULL,
0,
FALSE, // InternalDeviceIoControl
&event,
&ioStatusBlock);
if (irp==NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irpSp = IoGetNextIrpStackLocation (irp);
irpSp->FileObject = Connection->FileObject;
//
// Call the driver.
//
status = IoCallDriver (Connection->DeviceObject, irp);
//
// Must be at below APC level or this IRP will never get fully completed.
//
ASSERT (KeGetCurrentIrql ()<APC_LEVEL);
//
// If necessary, wait for the I/O to complete.
//
if ( status == STATUS_PENDING ) {
status = KeWaitForSingleObject( (PVOID)&event, Executive, KernelMode, FALSE, NULL );
ASSERT (status==STATUS_SUCCESS);
}
else {
//
// The IRP must have been completed then and event set.
//
if (NT_ERROR (status) || KeReadStateEvent (&event))
;
else {
DbgPrint ("************************************************\n");
DbgPrint ("*AFD: IoCallDriver returned STATUS_SUCCESS,"
" but event in the IRP (%p) is NOT signalled!!!\n",
irp);
DbgPrint ("************************************************\n");
DbgBreakPoint ();
}
}
//
// If the request was successfully completed, get the final I/O status.
//
if ( NT_SUCCESS(status) ) {
status = ioStatusBlock.Status;
}
//
// Since this option is only supported for TCP/IP, always return success.
//
return STATUS_SUCCESS;
} // AfdSetInLineMode
NTSTATUS
AfdUnbind (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Releases the address while connections are still outstanding on it.
Arguments:
Listening endpoint to unbind.
Return Value:
NTSTATUS -- Indicates whether the request was successfully
performed.
--*/
{
#ifdef AO_OPTION_UNBIND
NTSTATUS status;
PTCP_REQUEST_SET_INFORMATION_EX setInfoEx;
UCHAR buffer[sizeof(*setInfoEx)+sizeof (BOOLEAN)];
IO_STATUS_BLOCK ioStatusBlock;
KEVENT event;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PAGED_CODE( );
//
// Initialize the TDI information buffers.
//
setInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)buffer;
setInfoEx->ID.toi_entity.tei_entity = CO_TL_ENTITY;
setInfoEx->ID.toi_entity.tei_instance = TL_INSTANCE;
setInfoEx->ID.toi_class = INFO_CLASS_PROTOCOL;
setInfoEx->ID.toi_type = INFO_TYPE_ADDRESS_OBJECT;
setInfoEx->ID.toi_id = AO_OPTION_UNBIND;
setInfoEx->BufferSize = sizeof (BOOLEAN);
*((BOOLEAN *)&setInfoEx->Buffer) = TRUE;
//
// Initialize the kernel event that will signal I/O completion.
//
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
//
// Build TDI set information IRP.
//
irp = IoBuildDeviceIoControlRequest (
IOCTL_TCP_SET_INFORMATION_EX,
Endpoint->AddressDeviceObject,
setInfoEx,
sizeof(*setInfoEx) + setInfoEx->BufferSize,
NULL,
0,
FALSE, // InternalDeviceIoControl
&event,
&ioStatusBlock);
if (irp==NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irpSp = IoGetNextIrpStackLocation (irp);
irpSp->FileObject = Endpoint->AddressFileObject;
//
// Call the driver.
//
status = IoCallDriver (Endpoint->AddressDeviceObject, irp);
//
// Must be at below APC level or this IRP will never get fully completed.
//
ASSERT (KeGetCurrentIrql ()<APC_LEVEL);
//
// If necessary, wait for the I/O to complete.
//
if ( status == STATUS_PENDING ) {
status = KeWaitForSingleObject( (PVOID)&event, Executive, KernelMode, FALSE, NULL );
ASSERT (status==STATUS_SUCCESS);
}
else {
//
// The IRP must have been completed then and event set.
//
if (NT_ERROR (status) || KeReadStateEvent (&event))
;
else {
DbgPrint ("************************************************\n");
DbgPrint ("*AFD: IoCallDriver returned STATUS_SUCCESS,"
" but event in the IRP (%p) is NOT signalled!!!\n",
irp);
DbgPrint ("************************************************\n");
DbgBreakPoint ();
}
}
//
// If the request was successfully completed, get the final I/O status.
//
if ( NT_SUCCESS(status) ) {
status = ioStatusBlock.Status;
}
#else
UNREFERENCED_PARAMETER (Endpoint);
#endif // AO_OPTION_UNBIND
//
// Since this option is only supported for TCP/IP, always return success.
//
return STATUS_SUCCESS;
} // AfdUnbind
//
// The locking mechanism idea below is stolen from ntos\ex\handle.c
//
PVOID
AfdLockEndpointContext (
PAFD_ENDPOINT Endpoint
)
{
PVOID context;
PAGED_CODE ();
//
// We now use this lock in APC, protect from being
// interrupted by the APC by disallowing them when we
// are holding the lock.
//
KeEnterCriticalRegion ();
while (1) {
context = Endpoint->Context;
//
// See if someone else is manipulating the context.
//
if ((context==AFD_CONTEXT_BUSY) ||
(context==AFD_CONTEXT_WAITING)) {
//
// If this has not changed while we were checking,
// tell the current owner that we are waiting (if not
// already told) and wait for a few miliseconds.
//
if (InterlockedCompareExchangePointer (
(PVOID *)&Endpoint->Context,
AFD_CONTEXT_WAITING,
context)==context) {
NTSTATUS status;
LARGE_INTEGER afd10Milliseconds = {(ULONG)(-10 * 1000 * 10), -1};
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdLockEndpointContext: Waiting for endp %p\n",
Endpoint));
KeLeaveCriticalRegion ();
status = KeWaitForSingleObject( (PVOID)&AfdContextWaitEvent,
Executive,
KernelMode,
FALSE,
&afd10Milliseconds);
KeEnterCriticalRegion ();
}
else {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdLockEndpointContext: ICEP contention on %p\n",
Endpoint));
}
//
// Try again.
//
}
else {
//
// Context is not owned, try to get the ownership
//
if (InterlockedCompareExchangePointer (
(PVOID *)&Endpoint->Context,
AFD_CONTEXT_BUSY,
context)==context) {
//
// We now own the context, return it.
//
break;
}
//
// Try again.
//
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdLockEndpointContext: ICEP contention on %p\n",
Endpoint));
}
}
return context;
}
VOID
AfdUnlockEndpointContext (
PAFD_ENDPOINT Endpoint,
PVOID Context
)
{
PAGED_CODE ();
ASSERT ((Context!=AFD_CONTEXT_BUSY) && (Context!=AFD_CONTEXT_WAITING));
//
// Set the new context pointer and see what the old value was.
//
Context = InterlockedExchangePointer ((PVOID)&Endpoint->Context, Context);
if (Context==AFD_CONTEXT_WAITING) {
LONG prevState;
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdUnlockEndpointContext: Unwaiting endp %p\n", Endpoint));
//
// Someone was waiting, tell them to go get it now.
//
prevState = KePulseEvent (&AfdContextWaitEvent,
AfdPriorityBoost,
FALSE
);
ASSERT (prevState==0);
}
else {
//
// Better be busy or someone has changed it on us.
//
ASSERT (Context==AFD_CONTEXT_BUSY);
}
KeLeaveCriticalRegion ();
}
NTSTATUS
AfdGetContext (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
{
PAFD_ENDPOINT endpoint;
PVOID context;
NTSTATUS status;
UNREFERENCED_PARAMETER (IoctlCode);
UNREFERENCED_PARAMETER (InputBuffer);
UNREFERENCED_PARAMETER (InputBufferLength);
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
*Information = 0;
context = AfdLockEndpointContext (endpoint);
//
// Make sure that the output buffer is large enough to hold all the
// context information for this socket.
//
//
// If there is no context, return nothing.
//
if ( context == NULL ) {
status = STATUS_INVALID_PARAMETER;
}
//
// Return the context information we have stored for this endpoint.
//
else {
//
// If application buffer is too small, just
// copy whatever fits in and return the error code.
//
if ( OutputBufferLength < endpoint->ContextLength ) {
status = STATUS_BUFFER_OVERFLOW;
}
else {
OutputBufferLength = endpoint->ContextLength;
if (IS_SAN_ENDPOINT (endpoint)) {
//
// Indicate to the caller that it may also need to
// acqiure the control of the endpoint and
// fetch san specific information.
//
status = STATUS_MORE_ENTRIES;
}
else {
status = STATUS_SUCCESS;
}
}
try {
//
// Validate the output structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForWrite (OutputBuffer,
OutputBufferLength,
sizeof (UCHAR));
}
//
// Copy parameters back to application's memory
//
RtlCopyMemory(
OutputBuffer,
context,
OutputBufferLength
);
*Information = endpoint->ContextLength;
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
}
}
AfdUnlockEndpointContext (endpoint, context);
return status;
} // AfdGetContext
NTSTATUS
AfdGetRemoteAddress (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
{
PAFD_ENDPOINT endpoint;
PVOID context;
NTSTATUS status;
UNREFERENCED_PARAMETER (IoctlCode);
UNREFERENCED_PARAMETER (InputBuffer);
UNREFERENCED_PARAMETER (InputBufferLength);
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
*Information = 0;
context = AfdLockEndpointContext (endpoint);
//
// If there is no context or endpoint is of wrong type state or
// context information has been changed below the original size,
// return error.
//
if ( context == NULL ||
endpoint->Type!=AfdBlockTypeVcConnecting ||
endpoint->State!= AfdEndpointStateConnected ||
((CLONG)(endpoint->Common.VcConnecting.RemoteSocketAddressOffset+
endpoint->Common.VcConnecting.RemoteSocketAddressLength)) >
endpoint->ContextLength
) {
status = STATUS_INVALID_CONNECTION;
}
else {
if (OutputBufferLength<endpoint->Common.VcConnecting.RemoteSocketAddressLength) {
status = STATUS_BUFFER_OVERFLOW;
}
else {
OutputBufferLength = endpoint->Common.VcConnecting.RemoteSocketAddressLength;
status = STATUS_SUCCESS;
}
try {
//
// Validate the output structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForWrite (OutputBuffer,
OutputBufferLength,
sizeof (UCHAR));
}
//
// Copy parameters to application's memory
//
RtlCopyMemory(
OutputBuffer,
(PUCHAR)context+endpoint->Common.VcConnecting.RemoteSocketAddressOffset,
endpoint->Common.VcConnecting.RemoteSocketAddressLength
);
*Information = endpoint->ContextLength;
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
}
}
AfdUnlockEndpointContext (endpoint, context);
return status;
} // AfdGetRemoteAddress
NTSTATUS
AfdSetContext (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
{
PAFD_ENDPOINT endpoint;
PVOID context;
NTSTATUS status;
UNREFERENCED_PARAMETER (IoctlCode);
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
status = STATUS_SUCCESS;
*Information = 0;
context = AfdLockEndpointContext (endpoint);
try {
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForRead (InputBuffer,
InputBufferLength,
sizeof (UCHAR));
if (OutputBuffer!=NULL) {
//
// Validate that output buffer is completely inside
// of the input buffer and offsets are inside of supported ranges.
//
if ((PUCHAR)OutputBuffer<(PUCHAR)InputBuffer ||
(PUCHAR)OutputBuffer-(PUCHAR)InputBuffer>MAXUSHORT ||
OutputBufferLength>MAXUSHORT ||
OutputBufferLength>InputBufferLength ||
(ULONG)((PUCHAR)OutputBuffer-(PUCHAR)InputBuffer)>
InputBufferLength-OutputBufferLength) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
}
}
//
// If the context buffer is too small, allocate a new context
// buffer from paged pool.
//
if ( endpoint->ContextLength < InputBufferLength ) {
PVOID newContext;
//
// Allocate a new context buffer.
// Note since the socket context usually gets
// populated on socket creation during boot and not used
// right away (untill socket state is chaged), we
// make it a "cold" allocation. The flag has no effect
// after system is booted.
newContext = AFD_ALLOCATE_POOL_WITH_QUOTA(
PagedPool|POOL_COLD_ALLOCATION,
InputBufferLength,
AFD_CONTEXT_POOL_TAG
);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT ( newContext != NULL );
//
// Free the old context buffer, if there was one.
//
if ( context != NULL ) {
AFD_FREE_POOL(
context,
AFD_CONTEXT_POOL_TAG
);
}
context = newContext;
}
//
// Store the passed-in context buffer.
//
endpoint->ContextLength = InputBufferLength;
RtlCopyMemory(
context,
InputBuffer,
InputBufferLength
);
//
// Save pointer to remote socket address which we fill
// at the time of AcceptEx processing.
//
if (OutputBuffer!=NULL) {
if (AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateOpen)) {
if (endpoint->Type==AfdBlockTypeEndpoint &&
endpoint->State==AfdEndpointStateOpen) {
endpoint->Common.VcConnecting.RemoteSocketAddressOffset =
(USHORT) ((PUCHAR)OutputBuffer-(PUCHAR)InputBuffer);
endpoint->Common.VcConnecting.RemoteSocketAddressLength =
(USHORT) OutputBufferLength;
}
AFD_END_STATE_CHANGE (endpoint);
}
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
AfdUnlockEndpointContext (endpoint, context);
return status;
} // AfdSetContext
NTSTATUS
AfdSetEventHandler (
IN PFILE_OBJECT FileObject,
IN ULONG EventType,
IN PVOID EventHandler,
IN PVOID EventContext
)
/*++
Routine Description:
Sets up a TDI indication handler on a connection or address object
(depending on the file handle). This is done synchronously, which
shouldn't usually be an issue since TDI providers can usually complete
indication handler setups immediately.
Arguments:
FileObject - a pointer to the file object for an open connection or
address object.
EventType - the event for which the indication handler should be
called.
EventHandler - the routine to call when tghe specified event occurs.
EventContext - context which is passed to the indication routine.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
TDI_REQUEST_KERNEL_SET_EVENT parameters;
PAGED_CODE( );
parameters.EventType = EventType;
parameters.EventHandler = EventHandler;
parameters.EventContext = EventContext;
return AfdIssueDeviceControl(
FileObject,
&parameters,
sizeof(parameters),
NULL,
0,
TDI_SET_EVENT_HANDLER
);
} // AfdSetEventHandler
NTSTATUS
AfdIssueDeviceControl (
IN PFILE_OBJECT FileObject,
IN PVOID IrpParameters,
IN ULONG IrpParametersLength,
IN PVOID MdlBuffer,
IN ULONG MdlBufferLength,
IN UCHAR MinorFunction
)
/*++
Routine Description:
Issues a device control returst to a TDI provider and waits for the
request to complete.
Arguments:
FileObject - a pointer to the file object corresponding to a TDI
handle
IrpParameters - information to write to the parameters section of the
stack location of the IRP.
IrpParametersLength - length of the parameter information. Cannot be
greater than 16.
MdlBuffer - if non-NULL, a buffer of nonpaged pool to be mapped
into an MDL and placed in the MdlAddress field of the IRP.
MdlBufferLength - the size of the buffer pointed to by MdlBuffer.
MinorFunction - the minor function code for the request.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
NTSTATUS status;
PIRP irp;
PIO_STACK_LOCATION irpSp;
KEVENT event;
IO_STATUS_BLOCK ioStatusBlock;
PDEVICE_OBJECT deviceObject;
PMDL mdl;
PAGED_CODE( );
//
// Initialize the kernel event that will signal I/O completion.
//
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
//
// Attempt to allocate and initialize the I/O Request Packet (IRP)
// for this operation.
//
deviceObject = IoGetRelatedDeviceObject ( FileObject );
DEBUG ioStatusBlock.Status = STATUS_UNSUCCESSFUL;
DEBUG ioStatusBlock.Information = (ULONG)-1;
//
// If an MDL buffer was specified, get an MDL, and map the buffer
//
if ( MdlBuffer != NULL ) {
mdl = IoAllocateMdl(
MdlBuffer,
MdlBufferLength,
FALSE,
FALSE,
NULL
);
if ( mdl == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool( mdl );
} else {
mdl = NULL;
}
irp = TdiBuildInternalDeviceControlIrp (
MinorFunction,
deviceObject,
FileObject,
&event,
&ioStatusBlock
);
if ( irp == NULL ) {
if (mdl!=NULL) {
IoFreeMdl (mdl);
}
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Install MDL (if any) in the IRP.
//
irp->MdlAddress = mdl;
//
// Put the file object pointer in the stack location.
//
irpSp = IoGetNextIrpStackLocation( irp );
ASSERT (irpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL);
irpSp->MinorFunction = MinorFunction;
irpSp->FileObject = FileObject;
//
// Fill in the service-dependent parameters for the request.
//
ASSERT( IrpParametersLength <= sizeof(irpSp->Parameters) );
RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength );
//
// Set up a completion routine which we'll use to free the MDL
// allocated previously.
//
IoSetCompletionRoutine( irp, AfdRestartDeviceControl, NULL, TRUE, TRUE, TRUE );
status = IoCallDriver( deviceObject, irp );
//
// Must be at below APC level or this IRP will never get fully completed.
//
ASSERT (KeGetCurrentIrql ()<APC_LEVEL);
//
// If necessary, wait for the I/O to complete.
//
if ( status == STATUS_PENDING ) {
status = KeWaitForSingleObject( (PVOID)&event, Executive, KernelMode, FALSE, NULL );
ASSERT (status==STATUS_SUCCESS);
}
else {
//
// The IRP must have been completed then and event set.
//
if (NT_ERROR (status) || KeReadStateEvent (&event))
;
else {
DbgPrint ("************************************************\n");
DbgPrint ("*AFD: IoCallDriver returned STATUS_SUCCESS,"
" but event in the IRP (%p) is NOT signalled!!!\n",
irp);
DbgPrint ("************************************************\n");
DbgBreakPoint ();
}
}
//
// If the request was successfully completed, get the final I/O status.
//
if ( NT_SUCCESS(status) ) {
status = ioStatusBlock.Status;
}
return status;
} // AfdIssueDeviceControl
NTSTATUS
AfdRestartDeviceControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
UNREFERENCED_PARAMETER (DeviceObject);
UNREFERENCED_PARAMETER (Context);
//
// N.B. This routine can never be demand paged because it can be
// called before any endpoints have been placed on the global
// list--see AfdAllocateEndpoint() and it's call to
// AfdGetTransportInfo().
//
//
// If there was an MDL in the IRP, free it and reset the pointer to
// NULL. The IO system can't handle a nonpaged pool MDL being freed
// in an IRP, which is why we do it here.
//
if ( Irp->MdlAddress != NULL ) {
IoFreeMdl( Irp->MdlAddress );
Irp->MdlAddress = NULL;
}
return STATUS_SUCCESS;
} // AfdRestartDeviceControl
NTSTATUS
AfdGetConnectData (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAFD_CONNECT_DATA_BUFFERS connectDataBuffers;
PAFD_CONNECT_DATA_INFO connectDataInfo;
AFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PMDL mdl;
NTSTATUS status;
UCHAR localBuffer[AFD_FAST_CONNECT_DATA_SIZE];
//
// Set up local pointers.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
mdl = NULL;
status = STATUS_SUCCESS;
*Information = 0;
try {
if (InputBufferLength>0) {
if (InputBufferLength<sizeof(connectInfo)) {
status = STATUS_INVALID_PARAMETER;
goto exit;
}
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (connectInfo),
PROBE_ALIGNMENT(AFD_UNACCEPTED_CONNECT_DATA_INFO));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
connectInfo = *((PAFD_UNACCEPTED_CONNECT_DATA_INFO)InputBuffer);
if (connectInfo.LengthOnly &&
OutputBufferLength<sizeof (connectInfo)) {
status = STATUS_INVALID_PARAMETER;
goto exit;
}
}
else {
AFD_W4_INIT connectInfo.Sequence = 0;
AFD_W4_INIT connectInfo.LengthOnly = 0;
}
if (OutputBufferLength>0) {
if (OutputBufferLength>sizeof (localBuffer)) {
mdl = IoAllocateMdl(
OutputBuffer, // VirtualAddress
OutputBufferLength, // Length
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
NULL // Irp
);
if (mdl==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
MmProbeAndLockPages(
mdl, // MemoryDescriptorList
RequestorMode, // AccessMode
IoWriteAccess // Operation
);
OutputBuffer = MmGetSystemAddressForMdlSafe(mdl, LowPagePriority);
if (OutputBuffer==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
}
else {
if (RequestorMode!=KernelMode) {
ProbeForWrite (OutputBuffer,
OutputBufferLength,
sizeof (UCHAR));
}
}
}
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
goto exit;
}
//
// If there is a connection on this endpoint, use the data buffers
// on the connection. Otherwise, use the data buffers from the
// endpoint.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
if (InputBufferLength>0) {
if ((endpoint->Type & AfdBlockTypeVcListening)==AfdBlockTypeVcListening) {
connection = AfdFindReturnedConnection(
endpoint,
connectInfo.Sequence
);
}
else
connection = NULL;
if( connection == NULL ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_INVALID_PARAMETER;
goto exit;
}
connectDataBuffers = connection->ConnectDataBuffers;
}
else if ( (connection= AFD_CONNECTION_FROM_ENDPOINT (endpoint)) != NULL ) {
connectDataBuffers = connection->ConnectDataBuffers;
} else if (IS_VC_ENDPOINT (endpoint)) {
connectDataBuffers = endpoint->Common.VirtualCircuit.ConnectDataBuffers;
}
else {
connectDataBuffers = NULL;
}
//
// If there are no connect data buffers on the endpoint, complete
// the IRP with no bytes.
//
if ( connectDataBuffers == NULL ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_SUCCESS;
goto exit;
}
//
// Determine what sort of data we're handling and where it should
// come from.
//
switch ( IoctlCode ) {
case IOCTL_AFD_GET_CONNECT_DATA:
connectDataInfo = &connectDataBuffers->ReceiveConnectData;
break;
case IOCTL_AFD_GET_CONNECT_OPTIONS:
connectDataInfo = &connectDataBuffers->ReceiveConnectOptions;
break;
case IOCTL_AFD_GET_DISCONNECT_DATA:
connectDataInfo = &connectDataBuffers->ReceiveDisconnectData;
break;
case IOCTL_AFD_GET_DISCONNECT_OPTIONS:
connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions;
break;
default:
ASSERT(!"Unknown GET_CONNECT_DATA IOCTL!");
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_INVALID_PARAMETER;
goto exit;
}
if ((InputBufferLength>0) && connectInfo.LengthOnly) {
connectInfo.ConnectDataLength = connectDataInfo->BufferLength;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
try {
RtlCopyMemory (OutputBuffer,
&connectInfo,
sizeof (connectInfo));
*Information = sizeof (connectInfo);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
goto exit;
}
//
// If there is none of the requested data type, again complete
// the IRP with no bytes.
//
if ( connectDataInfo->Buffer == NULL ||
connectDataInfo->BufferLength == 0 ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
*Information = 0;
goto exit;
}
//
// If the output buffer is too small, fail.
//
if ( OutputBufferLength < connectDataInfo->BufferLength ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_BUFFER_TOO_SMALL;
goto exit;
}
//
// Copy over the buffer and return the number of bytes copied.
//
RtlCopyMemory(
mdl ? OutputBuffer : localBuffer,
connectDataInfo->Buffer,
connectDataInfo->BufferLength
);
*Information = connectDataInfo->BufferLength;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
if (mdl==NULL) {
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
RtlCopyMemory (OutputBuffer,
localBuffer,
*Information);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
*Information = 0;
}
}
exit:
if (mdl!=NULL) {
if (mdl->MdlFlags & MDL_PAGES_LOCKED) {
MmUnlockPages (mdl);
}
IoFreeMdl (mdl);
}
return status;
} // AfdGetConnectData
NTSTATUS
AfdSetConnectData (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAFD_CONNECT_DATA_BUFFERS connectDataBuffers;
PAFD_CONNECT_DATA_BUFFERS * connectDataBuffersTarget;
PAFD_CONNECT_DATA_INFO connectDataInfo;
AFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo;
AFD_LOCK_QUEUE_HANDLE lockHandle;
BOOLEAN size = FALSE;
PMDL mdl;
NTSTATUS status;
UCHAR localBuffer[AFD_FAST_CONNECT_DATA_SIZE];
//
// Set up local pointers.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
mdl = NULL;
status = STATUS_SUCCESS;
*Information = 0;
if (!IS_VC_ENDPOINT (endpoint)) {
status = STATUS_INVALID_PARAMETER;
goto exit;
}
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
if (InputBufferLength>0) {
if (InputBufferLength<sizeof(connectInfo)) {
status = STATUS_INVALID_PARAMETER;
goto exit;
}
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (connectInfo),
PROBE_ALIGNMENT(AFD_UNACCEPTED_CONNECT_DATA_INFO));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
connectInfo = *((PAFD_UNACCEPTED_CONNECT_DATA_INFO)InputBuffer);
}
else {
AFD_W4_INIT connectInfo.Sequence = 0;
AFD_W4_INIT connectInfo.LengthOnly = 0;
}
if (OutputBufferLength>0) {
if (OutputBufferLength>sizeof (localBuffer)) {
mdl = IoAllocateMdl(
OutputBuffer, // VirtualAddress
OutputBufferLength, // Length
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
NULL // Irp
);
if (mdl==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
MmProbeAndLockPages(
mdl, // MemoryDescriptorList
RequestorMode, // AccessMode
IoReadAccess // Operation
);
OutputBuffer = MmGetSystemAddressForMdlSafe(mdl, LowPagePriority);
if (OutputBuffer==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
}
else {
if (RequestorMode!=KernelMode) {
ProbeForRead (OutputBuffer,
OutputBufferLength,
sizeof (UCHAR));
RtlCopyMemory (localBuffer,
OutputBuffer,
OutputBufferLength);
OutputBuffer = localBuffer;
}
}
}
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
goto exit;
}
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// If there is a connect outstanding on this endpoint or if it
// has already been shut down, fail this request. This prevents
// the connect code from accessing buffers which may be freed soon.
//
if( endpoint->StateChangeInProgress ||
((endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 )) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_INVALID_PARAMETER;
goto exit;
}
if (InputBufferLength>0) {
if ((endpoint->Type & AfdBlockTypeVcListening)==AfdBlockTypeVcListening) {
connection = AfdFindReturnedConnection(
endpoint,
connectInfo.Sequence
);
}
else
connection = NULL;
if( connection == NULL ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_INVALID_PARAMETER;
goto exit;
}
connectDataBuffersTarget = &connection->ConnectDataBuffers;
}
else if ( (connection= AFD_CONNECTION_FROM_ENDPOINT (endpoint)) != NULL ) {
connectDataBuffersTarget = &connection->ConnectDataBuffers;
} else {
connectDataBuffersTarget = &endpoint->Common.VirtualCircuit.ConnectDataBuffers;
}
connectDataBuffers = *connectDataBuffersTarget;
if( connectDataBuffers == NULL ) {
try {
connectDataBuffers = AFD_ALLOCATE_POOL_WITH_QUOTA(
NonPagedPool,
sizeof(*connectDataBuffers),
AFD_CONNECT_DATA_POOL_TAG
);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT ( connectDataBuffers != NULL );
*connectDataBuffersTarget = connectDataBuffers;
} except( EXCEPTION_EXECUTE_HANDLER ) {
status = GetExceptionCode ();
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
goto exit;
}
RtlZeroMemory(
connectDataBuffers,
sizeof(*connectDataBuffers)
);
}
//
// Determine what sort of data we're handling and where it should
// go.
//
switch( IoctlCode ) {
case IOCTL_AFD_SET_CONNECT_DATA:
connectDataInfo = &connectDataBuffers->SendConnectData;
break;
case IOCTL_AFD_SET_CONNECT_OPTIONS:
connectDataInfo = &connectDataBuffers->SendConnectOptions;
break;
case IOCTL_AFD_SET_DISCONNECT_DATA:
connectDataInfo = &connectDataBuffers->SendDisconnectData;
break;
case IOCTL_AFD_SET_DISCONNECT_OPTIONS:
connectDataInfo = &connectDataBuffers->SendDisconnectOptions;
break;
case IOCTL_AFD_SIZE_CONNECT_DATA:
connectDataInfo = &connectDataBuffers->ReceiveConnectData;
size = TRUE;
break;
case IOCTL_AFD_SIZE_CONNECT_OPTIONS:
connectDataInfo = &connectDataBuffers->ReceiveConnectOptions;
size = TRUE;
break;
case IOCTL_AFD_SIZE_DISCONNECT_DATA:
connectDataInfo = &connectDataBuffers->ReceiveDisconnectData;
size = TRUE;
break;
case IOCTL_AFD_SIZE_DISCONNECT_OPTIONS:
connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions;
size = TRUE;
break;
default:
ASSERT (!"Unsupported set connect data code");
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_INVALID_PARAMETER;
goto exit;
}
//
// Determine the buffer size based on whether we're setting a buffer
// into which data will be received, in which case the size is
// in the four bytes of input buffer, or setting a buffer which we're
// going to send, in which case the size is the length of the input
// buffer.
//
if( size ) {
if( OutputBufferLength < sizeof(ULONG) ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_INVALID_PARAMETER;
goto exit;
}
OutputBufferLength = *(ULONG UNALIGNED *)OutputBuffer;
}
//
// If there's not currently a buffer of the requested type, or there is
// such a buffer and it's smaller than the requested size, free it
// and allocate a new one.
//
if( connectDataInfo->Buffer == NULL ||
connectDataInfo->BufferLength < OutputBufferLength ) {
if( connectDataInfo->Buffer != NULL ) {
AFD_FREE_POOL(
connectDataInfo->Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
connectDataInfo->Buffer = NULL;
connectDataInfo->BufferLength = 0;
if (OutputBufferLength>0) {
try {
connectDataInfo->Buffer = AFD_ALLOCATE_POOL_WITH_QUOTA(
NonPagedPool,
OutputBufferLength,
AFD_CONNECT_DATA_POOL_TAG
);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT ( connectDataInfo->Buffer != NULL );
} except( EXCEPTION_EXECUTE_HANDLER ) {
status = GetExceptionCode ();
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
goto exit;
}
RtlZeroMemory(
connectDataInfo->Buffer,
OutputBufferLength
);
}
}
//
// If this wasn't simply a "size" request, copy the data into the buffer.
//
if( !size ) {
RtlCopyMemory(
connectDataInfo->Buffer,
OutputBuffer,
OutputBufferLength
);
}
connectDataInfo->BufferLength = OutputBufferLength;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
exit:
if (mdl!=NULL) {
if (mdl->MdlFlags & MDL_PAGES_LOCKED) {
MmUnlockPages (mdl);
}
IoFreeMdl (mdl);
}
return status;
} // AfdSetConnectData
NTSTATUS
AfdSaveReceivedConnectData (
IN OUT PAFD_CONNECT_DATA_BUFFERS * DataBuffers,
IN ULONG IoControlCode,
IN PVOID Buffer,
IN ULONG BufferLength
)
/*++
Routine Description:
This helper routine stores the specified *received* connect/disconnect
data/options on the specified endpoint/connection.
N.B. This routine MUST be called with endpoint SpinLock held!
N.B. Unlike AfdSetConnectData(), this routine cannot allocate the
AFD_CONNECT_DATA_BUFFERS structure with quota, as it may be
called from AfdDisconnectEventHandler() in an unknown thread
context.
Arguments:
DataBuffers -Points to a pointer to the connect data buffers structure.
If the value pointed to by DataBuffers is NULL, then a new structure
is allocated, otherwise the existing structure is used.
IoControlCode - Specifies the type of data to save.
Buffer - Points to the buffer containing the data.
BufferLength - The length of Buffer.
Return Value:
NTSTATUS - The completion status.
--*/
{
PAFD_CONNECT_DATA_BUFFERS connectDataBuffers;
PAFD_CONNECT_DATA_INFO connectDataInfo;
ASSERT( KeGetCurrentIrql() >= DISPATCH_LEVEL );
//
// If there's no connect data buffer structure, allocate one now.
//
connectDataBuffers = *DataBuffers;
if( connectDataBuffers == NULL ) {
connectDataBuffers = AFD_ALLOCATE_POOL(
NonPagedPool,
sizeof(*connectDataBuffers),
AFD_CONNECT_DATA_POOL_TAG
);
if( connectDataBuffers == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(
connectDataBuffers,
sizeof(*connectDataBuffers)
);
*DataBuffers = connectDataBuffers;
}
//
// Determine what sort of data we're handling and where it should
// go.
//
switch( IoControlCode ) {
case IOCTL_AFD_SET_CONNECT_DATA:
connectDataInfo = &connectDataBuffers->ReceiveConnectData;
break;
case IOCTL_AFD_SET_CONNECT_OPTIONS:
connectDataInfo = &connectDataBuffers->ReceiveConnectOptions;
break;
case IOCTL_AFD_SET_DISCONNECT_DATA:
connectDataInfo = &connectDataBuffers->ReceiveDisconnectData;
break;
case IOCTL_AFD_SET_DISCONNECT_OPTIONS:
connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions;
break;
default:
ASSERT (!"Unsupported save received connect data code");
return STATUS_INVALID_PARAMETER;
}
//
// If the buffer in the connect structure matches the one
// passed in, must be the same buffer we passed in the request.
// Just adjust the length.
//
if (connectDataInfo->Buffer==Buffer) {
ASSERT (connectDataInfo->BufferLength>=BufferLength);
connectDataInfo->BufferLength = BufferLength;
return STATUS_SUCCESS;
}
//
// If there was previously a buffer of the requested type, free it.
//
if( connectDataInfo->Buffer != NULL ) {
AFD_FREE_POOL(
connectDataInfo->Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
connectDataInfo->Buffer = NULL;
}
//
// Allocate a new buffer for the data and copy in the data we're to
// send.
//
connectDataInfo->Buffer = AFD_ALLOCATE_POOL(
NonPagedPool,
BufferLength,
AFD_CONNECT_DATA_POOL_TAG
);
if( connectDataInfo->Buffer == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(
connectDataInfo->Buffer,
Buffer,
BufferLength
);
connectDataInfo->BufferLength = BufferLength;
return STATUS_SUCCESS;
} // AfdSaveReceivedConnectData
VOID
AfdFreeConnectDataBuffers (
IN PAFD_CONNECT_DATA_BUFFERS ConnectDataBuffers
)
{
if ( ConnectDataBuffers->SendConnectData.Buffer != NULL ) {
AFD_FREE_POOL(
ConnectDataBuffers->SendConnectData.Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
if ( ConnectDataBuffers->ReceiveConnectData.Buffer != NULL ) {
AFD_FREE_POOL(
ConnectDataBuffers->ReceiveConnectData.Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
if ( ConnectDataBuffers->SendConnectOptions.Buffer != NULL ) {
AFD_FREE_POOL(
ConnectDataBuffers->SendConnectOptions.Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
if ( ConnectDataBuffers->ReceiveConnectOptions.Buffer != NULL ) {
AFD_FREE_POOL(
ConnectDataBuffers->ReceiveConnectOptions.Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
if ( ConnectDataBuffers->SendDisconnectData.Buffer != NULL ) {
AFD_FREE_POOL(
ConnectDataBuffers->SendDisconnectData.Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
if ( ConnectDataBuffers->ReceiveDisconnectData.Buffer != NULL ) {
AFD_FREE_POOL(
ConnectDataBuffers->ReceiveDisconnectData.Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
if ( ConnectDataBuffers->SendDisconnectOptions.Buffer != NULL ) {
AFD_FREE_POOL(
ConnectDataBuffers->SendDisconnectOptions.Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
if ( ConnectDataBuffers->ReceiveDisconnectOptions.Buffer != NULL ) {
AFD_FREE_POOL(
ConnectDataBuffers->ReceiveDisconnectOptions.Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
AFD_FREE_POOL(
ConnectDataBuffers,
AFD_CONNECT_DATA_POOL_TAG
);
return;
} // AfdFreeConnectDataBuffers
VOID
AfdQueueWorkItem (
IN PWORKER_THREAD_ROUTINE AfdWorkerRoutine,
IN PAFD_WORK_ITEM AfdWorkItem
)
{
KIRQL oldIrql;
ASSERT( AfdWorkerRoutine != NULL );
ASSERT( AfdWorkItem != NULL );
AfdWorkItem->AfdWorkerRoutine = AfdWorkerRoutine;
//
// Insert the work item at the tail of AFD's list of work itrems.
//
oldIrql = KeAcquireQueuedSpinLock( LockQueueAfdWorkQueueLock );
InsertTailList( &AfdWorkQueueListHead, &AfdWorkItem->WorkItemListEntry );
AfdRecordAfdWorkItemsQueued();
//
// If there is no executive worker thread working on AFD work, fire
// off an executive worker thread to start servicing the list.
//
if ( !AfdWorkThreadRunning ) {
//
// Remember that the work thread is running and release the
// lock. Note that we must release the lock before queuing the
// work because the worker thread may unlock AFD and we can't
// hold a lock when AFD is unlocked.
//
AfdRecordExWorkItemsQueued();
AfdWorkThreadRunning = TRUE;
KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql );
IoQueueWorkItem (AfdWorkQueueItem,
AfdDoWork,
DelayedWorkQueue,
NULL);
} else {
KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql );
}
return;
} // AfdQueueWorkItem
VOID
AfdDoWork (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
{
PAFD_WORK_ITEM afdWorkItem;
PLIST_ENTRY listEntry;
KIRQL oldIrql;
PWORKER_THREAD_ROUTINE workerRoutine;
UNREFERENCED_PARAMETER (DeviceObject);
UNREFERENCED_PARAMETER (Context);
ASSERT( AfdWorkThreadRunning );
//
// Empty the queue of AFD work items.
//
oldIrql = KeAcquireQueuedSpinLock( LockQueueAfdWorkQueueLock );
AfdRecordWorkerEnter();
AfdRecordAfdWorkerThread( PsGetCurrentThread() );
while ( !IsListEmpty( &AfdWorkQueueListHead ) ) {
//
// Take the first item from the queue and find the address
// of the AFD work item structure.
//
listEntry = RemoveHeadList( &AfdWorkQueueListHead );
afdWorkItem = CONTAINING_RECORD(
listEntry,
AFD_WORK_ITEM,
WorkItemListEntry
);
AfdRecordAfdWorkItemsProcessed();
//
// Capture the worker thread routine from the item.
//
workerRoutine = afdWorkItem->AfdWorkerRoutine;
//
// If this work item is going to unlock AFD, then remember that
// the worker thread is no longer running. This closes the
// window where AFD gets unloaded at the same time as new work
// comes in and gets put on the work queue. Note that we
// must reset this boolean BEFORE releasing the spin lock.
//
if( workerRoutine == AfdUnlockDriver ) {
AfdWorkThreadRunning = FALSE;
AfdRecordAfdWorkerThread( NULL );
AfdRecordWorkerLeave();
}
//
// Release the lock and then call the AFD worker routine.
//
KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql );
workerRoutine( afdWorkItem );
//
// If the purpose of this work item was to unload AFD, then
// we know that there is no more work to do and we CANNOT
// acquire a spin lock. Quit servicing the list and return.
if( workerRoutine == AfdUnlockDriver ) {
return;
}
//
// Reacquire the spin lock and continue servicing the list.
//
oldIrql = KeAcquireQueuedSpinLock( LockQueueAfdWorkQueueLock );
}
//
// Remember that we're no longer servicing the list and release the
// spin lock.
//
AfdRecordAfdWorkerThread( NULL );
AfdRecordWorkerLeave();
AfdWorkThreadRunning = FALSE;
KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql );
} // AfdDoWork
PAFD_WORK_ITEM
AfdGetWorkerByRoutine (
PWORKER_THREAD_ROUTINE Routine
) {
KIRQL oldIrql;
PLIST_ENTRY listEntry;
oldIrql = KeAcquireQueuedSpinLock( LockQueueAfdWorkQueueLock );
listEntry = AfdWorkQueueListHead.Flink;
while (listEntry!=&AfdWorkQueueListHead) {
PAFD_WORK_ITEM afdWorkItem = CONTAINING_RECORD(
listEntry,
AFD_WORK_ITEM,
WorkItemListEntry
);
if (afdWorkItem->AfdWorkerRoutine==Routine) {
RemoveEntryList (&afdWorkItem->WorkItemListEntry);
KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql );
return afdWorkItem;
}
else
listEntry = listEntry->Flink;
}
KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql );
return NULL;
} // AfdGetWorkerByRoutine
#if DBG
typedef struct _AFD_OUTSTANDING_IRP {
LIST_ENTRY OutstandingIrpListEntry;
PIRP OutstandingIrp;
PCHAR FileName;
ULONG LineNumber;
} AFD_OUTSTANDING_IRP, *PAFD_OUTSTANDING_IRP;
BOOLEAN
AfdRecordOutstandingIrpDebug (
IN PAFD_ENDPOINT Endpoint,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PCHAR FileName,
IN ULONG LineNumber
)
{
PAFD_OUTSTANDING_IRP outstandingIrp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
UNREFERENCED_PARAMETER (DeviceObject);
//
// Get an outstanding IRP structure to hold the IRP.
//
outstandingIrp = AFD_ALLOCATE_POOL_PRIORITY (
NonPagedPool,
sizeof(AFD_OUTSTANDING_IRP),
AFD_DEBUG_POOL_TAG,
NormalPoolPriority
);
if ( outstandingIrp == NULL ) {
//
// Because our completion routine will try to
// find this IRP anyway and check for completion
// we use the stack space to put it in the list.
// The completion routine will just remove this
// element from the list without attempting to free it.
//
AFD_OUTSTANDING_IRP OutstandingIrp;
OutstandingIrp.OutstandingIrp = Irp;
OutstandingIrp.FileName = NULL; // To let completion
// routine know that this
// is not an allocated element
OutstandingIrp.LineNumber = 0;
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
InsertTailList(
&Endpoint->OutstandingIrpListHead,
&OutstandingIrp.OutstandingIrpListEntry
);
Endpoint->OutstandingIrpCount++;
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdRecordOutstandingIrp: Could not track Irp %p on endpoint %p, failing it.\n",
Irp, Endpoint));
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoSetNextIrpStackLocation( Irp );
IoCompleteRequest( Irp, AfdPriorityBoost );
return FALSE;
}
//
// Initialize the structure and place it on the endpoint's list of
// outstanding IRPs.
//
outstandingIrp->OutstandingIrp = Irp;
outstandingIrp->FileName = FileName;
outstandingIrp->LineNumber = LineNumber;
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
InsertHeadList(
&Endpoint->OutstandingIrpListHead,
&outstandingIrp->OutstandingIrpListEntry
);
Endpoint->OutstandingIrpCount++;
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
return TRUE;
} // AfdRecordOutstandingIrpDebug
VOID
AfdCompleteOutstandingIrpDebug (
IN PAFD_ENDPOINT Endpoint,
IN PIRP Irp
)
{
PAFD_OUTSTANDING_IRP outstandingIrp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PLIST_ENTRY listEntry;
//
// First find the IRP on the endpoint's list of outstanding IRPs.
//
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
for ( listEntry = Endpoint->OutstandingIrpListHead.Flink;
listEntry != &Endpoint->OutstandingIrpListHead;
listEntry = listEntry->Flink ) {
outstandingIrp = CONTAINING_RECORD(
listEntry,
AFD_OUTSTANDING_IRP,
OutstandingIrpListEntry
);
if ( outstandingIrp->OutstandingIrp == Irp ) {
RemoveEntryList( listEntry );
ASSERT( Endpoint->OutstandingIrpCount != 0 );
Endpoint->OutstandingIrpCount--;
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
if (outstandingIrp->FileName!=NULL) {
AFD_FREE_POOL(
outstandingIrp,
AFD_DEBUG_POOL_TAG
);
}
return;
}
}
//
// The corresponding outstanding IRP structure was not found. This
// should never happen unless an allocate for an outstanding IRP
// structure failed above.
//
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdCompleteOutstandingIrp: Irp %p not found on endpoint %p\n",
Irp, Endpoint ));
ASSERT( Endpoint->OutstandingIrpCount != 0 );
Endpoint->OutstandingIrpCount--;
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
return;
} // AfdCompleteOutstandingIrpDebug
#endif
#if REFERENCE_DEBUG
AFD_QSPIN_LOCK AfdLocationTableLock;
PAFD_REFERENCE_LOCATION AfdLocationTable;
SIZE_T AfdLocationTableSize;
LONG AfdLocationId;
LONG
AfdFindReferenceLocation (
IN PCHAR Format,
OUT PLONG LocationId
)
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
PVOID ignore;
AfdAcquireSpinLock (&AfdLocationTableLock, &lockHandle);
if (*LocationId==0) {
if (AfdLocationId >= (LONG)(AfdLocationTableSize/sizeof(AfdLocationTable[0]))) {
PAFD_REFERENCE_LOCATION newTable;
newTable = ExAllocatePoolWithTag (NonPagedPool,
AfdLocationTableSize+PAGE_SIZE,
AFD_DEBUG_POOL_TAG);
if (newTable!=NULL) {
if (AfdLocationTable!=NULL) {
RtlCopyMemory (newTable, AfdLocationTable, AfdLocationTableSize);
ExFreePoolWithTag (AfdLocationTable, AFD_DEBUG_POOL_TAG);
}
AfdLocationTable = newTable;
AfdLocationTableSize += PAGE_SIZE;
}
else {
goto Unlock;
}
}
AfdLocationTable[AfdLocationId].Format = Format;
RtlGetCallersAddress (&AfdLocationTable[AfdLocationId].Address, &ignore);
*LocationId = ++AfdLocationId;
}
Unlock:
AfdReleaseSpinLock (&AfdLocationTableLock, &lockHandle);
return *LocationId;
}
#endif
#if DBG || REFERENCE_DEBUG
VOID
AfdInitializeDebugData (
VOID
)
{
AfdInitializeSpinLock (&AfdLocationTableLock);
} // AfdInitializeDebugData
VOID
AfdFreeDebugData (
VOID
)
{
if (AfdLocationTable!=NULL) {
ExFreePoolWithTag (AfdLocationTable, AFD_DEBUG_POOL_TAG);
AfdLocationTable = NULL;
}
} // AfdFreeDebugData
#endif
#if DBG
LONG AfdTotalAllocations = 0;
LONG AfdTotalFrees = 0;
LARGE_INTEGER AfdTotalBytesAllocated;
LARGE_INTEGER AfdTotalBytesFreed;
PVOID
AfdAllocatePool (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag,
IN PCHAR FileName,
IN ULONG LineNumber,
IN BOOLEAN WithQuota,
IN EX_POOL_PRIORITY Priority
)
{
PVOID memBlock;
PAFD_POOL_HEADER header;
SIZE_T allocBytes;
//
// Check for overflow first.
//
if (NumberOfBytes+sizeof (*header)<=NumberOfBytes) {
if (WithQuota) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
return NULL;
}
if (NumberOfBytes+sizeof (*header)>=PAGE_SIZE) {
allocBytes = NumberOfBytes;
if (allocBytes<PAGE_SIZE)
allocBytes = PAGE_SIZE;
}
else {
allocBytes = NumberOfBytes+sizeof (*header);
}
if ( WithQuota ) {
ASSERT (PoolType == (NonPagedPool|POOL_RAISE_IF_ALLOCATION_FAILURE) ||
PoolType == (PagedPool|POOL_RAISE_IF_ALLOCATION_FAILURE) ||
PoolType == (PagedPool|POOL_RAISE_IF_ALLOCATION_FAILURE|POOL_COLD_ALLOCATION));
memBlock = ExAllocatePoolWithQuotaTag(
PoolType,
allocBytes,
Tag
);
ASSERT (memBlock!=NULL);
} else {
ASSERT( PoolType == NonPagedPool ||
PoolType == NonPagedPoolMustSucceed ||
PoolType == PagedPool ||
PoolType == (PagedPool|POOL_COLD_ALLOCATION));
memBlock = ExAllocatePoolWithTagPriority(
PoolType,
allocBytes,
Tag,
Priority
);
if ( memBlock == NULL ) {
return NULL;
}
}
if (allocBytes<PAGE_SIZE) {
header = memBlock;
memBlock = header+1;
header->FileName = FileName;
header->LineNumber = LineNumber;
header->Size = NumberOfBytes;
header->InUse = PoolType;
}
else {
NumberOfBytes = PAGE_SIZE;
ASSERT (PAGE_ALIGN(memBlock)==memBlock);
}
ExInterlockedAddLargeStatistic(
&AfdTotalBytesAllocated,
(CLONG)NumberOfBytes
);
InterlockedIncrement(
&AfdTotalAllocations
);
return memBlock;
} // AfdAllocatePool
#define AFD_POOL_DEBUG 0
#if AFD_POOL_DEBUG
#define MAX_LRU_POOL_BLOCKS 256
PVOID AfdLRUPoolBlocks[MAX_LRU_POOL_BLOCKS];
LONG AfdLRUPoolIndex = -1;
#endif // AFD_POOL_DEBUG
VOID
AfdFreePool (
IN PVOID Pointer,
IN ULONG Tag
)
{
ULONG PoolType;
ULONG numberOfBytes;
ASSERT (((ULONG_PTR)Pointer & (MEMORY_ALLOCATION_ALIGNMENT-1))==0);
if (PAGE_ALIGN (Pointer)==Pointer) {
numberOfBytes = PAGE_SIZE;
}
else {
PAFD_POOL_HEADER header;
Pointer = ((PAFD_POOL_HEADER)Pointer) - 1;
header = Pointer;
ASSERT (header->Size>0);
PoolType = InterlockedExchange (&header->InUse, -1);
ASSERT( PoolType == NonPagedPool ||
PoolType == NonPagedPoolMustSucceed ||
PoolType == PagedPool ||
PoolType == (NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE) ||
PoolType == (PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE) ||
PoolType == (PagedPool | POOL_COLD_ALLOCATION) ||
PoolType == (PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE | POOL_COLD_ALLOCATION));
numberOfBytes = (CLONG)header->Size;
}
ExInterlockedAddLargeStatistic(
&AfdTotalBytesFreed,
numberOfBytes
);
InterlockedIncrement(
&AfdTotalFrees
);
#if AFD_POOL_DEBUG
{
LONG idx = InterlockedIncrement (&AfdLRUPoolIndex)%MAX_LRU_POOL_BLOCKS;
RtlFillMemoryUlong (
Pointer,
(numberOfBytes+3)&(~3),
Tag);
if (PoolType!=PagedPool) {
ULONG size;
Pointer = InterlockedExchangePointer (
&AfdLRUPoolBlocks[idx],
Pointer);
if (Pointer==NULL)
return;
if (PAGE_ALIGN(Pointer)==Pointer)
numberOfBytes = PAGE_SIZE;
else {
PAFD_HEADER header;
header = (PAFD_POOL_HEADER)Pointer - 1;
Tag = *((PULONG)Pointer);
numberOfBytes = (CLONG)(header->Size+3)&(~3);
}
size = RtlCompareMemoryUlong (Pointer, numberOfBytes, Tag);
if (size!=numberOfBytes) {
DbgPrint ("Block %p is modified at %p after it was freed.\n",
Pointer, (PUCHAR)Pointer+size);
DbgBreakPoint ();
}
}
}
#endif AFD_POOL_DEBUG
MyFreePoolWithTag(
Pointer,
Tag
);
} // AfdFreePool
#ifdef AFDDBG_QUOTA
typedef struct _AFD_QUOTA_HASH {
PSZ Type;
LONG TotalAmount;
} AFD_QUOTA_HASH, *PAFD_QUOTA_HASH;
#define AFD_QUOTA_HASH_SIZE 31
AFD_QUOTA_HASH AfdQuotaHash[AFD_QUOTA_HASH_SIZE];
PEPROCESS AfdQuotaProcess;
typedef struct {
union {
ULONG Bytes;
struct {
UCHAR Reserved[3];
UCHAR Sign;
} ;
} ;
UCHAR Location[12];
PVOID Block;
PVOID Process;
PVOID Reserved2[2];
} QUOTA_HISTORY, *PQUOTA_HISTORY;
#define QUOTA_HISTORY_LENGTH 512
QUOTA_HISTORY AfdQuotaHistory[QUOTA_HISTORY_LENGTH];
LONG AfdQuotaHistoryIndex = 0;
VOID
AfdRecordQuotaHistory(
IN PEPROCESS Process,
IN LONG Bytes,
IN PSZ Type,
IN PVOID Block
)
{
LONG index;
PQUOTA_HISTORY history;
index = InterlockedIncrement( &AfdQuotaHistoryIndex );
index &= QUOTA_HISTORY_LENGTH - 1;
history = &AfdQuotaHistory[index];
history->Bytes = Bytes;
history->Sign = Bytes < 0 ? '-' : '+';
RtlCopyMemory( history->Location, Type, 12 );
history->Block = Block;
history->Process = Process;
index = (ULONG_PTR)Type % AFD_QUOTA_HASH_SIZE;
if (AfdQuotaHash[index].Type!=Type) {
if (InterlockedCompareExchangePointer (
(PVOID *)&AfdQuotaHash[index].Type,
Type,
NULL)!=NULL) {
AfdQuotaHash[index].Type = (PVOID)-1;
}
}
InterlockedExchangeAdd (&AfdQuotaHash[index].TotalAmount, Bytes);
} // AfdRecordQuotaHistory
#endif
#endif
PMDL
AfdAdvanceMdlChain(
IN PMDL Mdl,
IN ULONG Offset
)
/*++
Routine Description:
Accepts a pointer to an existing MDL chain and offsets that chain
by a specified number of bytes. This may involve the creation
of a partial MDL for the first entry in the new chain.
Arguments:
Mdl - Pointer to the MDL chain to advance.
Offset - The number of bytes to offset the chain.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
//
// Sanity check.
//
ASSERT( Mdl != NULL );
ASSERT( Offset > 0 );
//
// Scan past any fully completed MDLs.
//
while ( Offset > MmGetMdlByteCount( Mdl ) ) {
PMDL prev = Mdl;
Offset -= MmGetMdlByteCount( Mdl );
ASSERT( Mdl->Next != NULL );
Mdl = Mdl->Next;
prev->Next = NULL;
MmUnlockPages (prev);
IoFreeMdl (prev);
}
//
// Tautology of the day: Offset will either be zero (meaning that
// we've advanced to a clean boundary between MDLs) or non-zero
// (meaning we need to now build a partial MDL).
//
if ( Offset > 0 ) {
NTSTATUS status;
//
// Use new MM routine.
// This saves us use of MustSucceed pool since the routine
// below is guaranteed to succeed (as it should because
// we already have the whole range locked and possibly mapped
// and there should be no problem extracting part of it within
// the same MDL).
//
status = MmAdvanceMdl (Mdl, Offset);
ASSERT (status==STATUS_SUCCESS);
}
return Mdl;
} // AfdAdvanceMdlChain
NTSTATUS
AfdAllocateMdlChain(
IN PIRP Irp,
IN LPWSABUF BufferArray,
IN ULONG BufferCount,
IN LOCK_OPERATION Operation,
OUT PULONG TotalByteCount
)
/*++
Routine Description:
Allocates a MDL chain describing the WSABUF array and attaches
the chain to the specified IRP.
Arguments:
Irp - The IRP that will receive the MDL chain.
BufferArray - Points to an array of WSABUF structures describing
the user's buffers.
BufferCount - Contains the number of WSABUF structures in the
array.
Operation - Specifies the type of operation being performed (either
IoReadAccess or IoWriteAccess).
TotalByteCount - Will receive the total number of BYTEs described
by the WSABUF array.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
NTSTATUS status;
PMDL currentMdl;
PMDL * chainTarget;
KPROCESSOR_MODE previousMode;
ULONG totalLength;
PVOID bufferPointer;
ULONG bufferLength;
//
// Sanity check.
//
ASSERT( Irp != NULL );
ASSERT( Irp->MdlAddress == NULL );
ASSERT( ( Operation == IoReadAccess ) || ( Operation == IoWriteAccess ) );
ASSERT( TotalByteCount != NULL );
//
// Get the previous processor mode.
//
previousMode = Irp->RequestorMode;
//
// Get into a known state.
//
status = STATUS_SUCCESS;
currentMdl = NULL;
chainTarget = &Irp->MdlAddress;
totalLength = 0;
//
// Walk the array of WSABUF structures, creating the MDLs and
// probing & locking the pages.
//
try {
if( previousMode != KernelMode ) {
if ((BufferArray==NULL) ||
(BufferCount==0) ||
(BufferCount>(MAXULONG/sizeof (WSABUF)))) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
//
// Probe the WSABUF array.
//
ProbeForRead(
BufferArray, // Address
BufferCount * sizeof(WSABUF), // Length
PROBE_ALIGNMENT(WSABUF) // Alignment
);
}
else {
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
}
//
// Scan the array.
//
for ( ; BufferCount>0; BufferCount--, BufferArray++) {
bufferPointer = BufferArray->buf;
bufferLength = BufferArray->len;
if (bufferLength > 0) {
//
// Check for integer overflow.
//
C_ASSERT(sizeof(totalLength) == sizeof(MAXULONG));
if ((MAXULONG - totalLength) < bufferLength) {
status = STATUS_INVALID_PARAMETER;
break;
}
//
// Create a new MDL.
//
currentMdl = IoAllocateMdl(
bufferPointer, // VirtualAddress
bufferLength, // Length
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
NULL // Irp
);
if (currentMdl != NULL) {
//
// Lock the pages. This will raise an exception
// if the operation fails.
//
MmProbeAndLockPages(
currentMdl, // MemoryDescriptorList
previousMode, // AccessMode
Operation // Operation
);
//
// Chain the MDL onto the IRP. In theory, we could
// do this by passing the IRP into IoAllocateMdl(),
// but IoAllocateMdl() does a linear scan on the MDL
// chain to find the last one in the chain.
//
// We can do much better.
//
*chainTarget = currentMdl;
chainTarget = &currentMdl->Next;
} else {
//
// Cannot allocate new MDL, return appropriate error.
//
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Update the total byte counter.
//
totalLength += bufferLength;
}
}
//
// Ensure the MDL chain is NULL terminated.
//
ASSERT(*chainTarget == NULL);
} except(AFD_EXCEPTION_FILTER(status)) {
ASSERT(NT_ERROR(status));
//
// currentMdl will only be non-NULL at this point if an MDL
// has been created, but MmProbeAndLockPages() raised an
// exception. If this is true, then free the MDL.
// Also account for the case when currentMdl has been linked
// onto the chain and exception occured when accesing next user
// buffer.
//
if ((currentMdl != NULL) && (chainTarget != &currentMdl->Next))
IoFreeMdl(currentMdl);
}
//
// Return the total buffer count.
//
*TotalByteCount = totalLength;
return status;
} // AfdAllocateMdlChain
#ifdef _WIN64
NTSTATUS
AfdAllocateMdlChain32(
IN PIRP Irp,
IN LPWSABUF32 BufferArray,
IN ULONG BufferCount,
IN LOCK_OPERATION Operation,
OUT PULONG TotalByteCount
)
/*++
Routine Description:
Allocates a MDL chain describing the WSABUF array and attaches
the chain to the specified IRP.
Arguments:
Irp - The IRP that will receive the MDL chain.
BufferArray - Points to an array of WSABUF structures describing
the user's buffers.
BufferCount - Contains the number of WSABUF structures in the
array.
Operation - Specifies the type of operation being performed (either
IoReadAccess or IoWriteAccess).
TotalByteCount - Will receive the total number of BYTEs described
by the WSABUF array.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
NTSTATUS status;
PMDL currentMdl;
PMDL * chainTarget;
KPROCESSOR_MODE previousMode;
ULONG totalLength;
PVOID bufferPointer;
ULONG bufferLength;
//
// Sanity check.
//
ASSERT( Irp != NULL );
ASSERT( Irp->MdlAddress == NULL );
ASSERT( ( Operation == IoReadAccess ) || ( Operation == IoWriteAccess ) );
ASSERT( TotalByteCount != NULL );
//
// Get the previous processor mode.
//
previousMode = Irp->RequestorMode;
//
// Get into a known state.
//
status = STATUS_SUCCESS;
currentMdl = NULL;
chainTarget = &Irp->MdlAddress;
totalLength = 0;
//
// Walk the array of WSABUF structures, creating the MDLs and
// probing & locking the pages.
//
try {
if( previousMode != KernelMode ) {
if ((BufferArray==NULL) ||
(BufferCount==0) ||
(BufferCount>(MAXULONG/sizeof (WSABUF32)))) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
//
// Probe the WSABUF array.
//
ProbeForRead(
BufferArray, // Address
BufferCount * sizeof(WSABUF32), // Length
PROBE_ALIGNMENT32(WSABUF32) // Alignment
);
}
else {
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
}
//
// Scan the array.
//
for ( ; BufferCount>0; BufferCount--, BufferArray++) {
bufferPointer = UlongToPtr(BufferArray->buf);
bufferLength = BufferArray->len;
if (bufferLength > 0) {
//
// Check for integer overflow.
//
C_ASSERT(sizeof(totalLength) == sizeof(MAXULONG));
if ((MAXULONG - totalLength) < bufferLength) {
status = STATUS_INVALID_PARAMETER;
break;
}
//
// Create a new MDL.
//
currentMdl = IoAllocateMdl(
bufferPointer, // VirtualAddress
bufferLength, // Length
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
NULL // Irp
);
if (currentMdl != NULL) {
//
// Lock the pages. This will raise an exception
// if the operation fails.
//
MmProbeAndLockPages(
currentMdl, // MemoryDescriptorList
previousMode, // AccessMode
Operation // Operation
);
//
// Chain the MDL onto the IRP. In theory, we could
// do this by passing the IRP into IoAllocateMdl(),
// but IoAllocateMdl() does a linear scan on the MDL
// chain to find the last one in the chain.
//
// We can do much better.
//
*chainTarget = currentMdl;
chainTarget = &currentMdl->Next;
} else {
//
// Cannot allocate new MDL, return appropriate error.
//
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Update the total byte counter.
//
totalLength += bufferLength;
}
}
//
// Ensure the MDL chain is NULL terminated.
//
ASSERT(*chainTarget == NULL);
} except(AFD_EXCEPTION_FILTER(status)) {
ASSERT(NT_ERROR(status));
//
// currentMdl will only be non-NULL at this point if an MDL
// has been created, but MmProbeAndLockPages() raised an
// exception. If this is true, then free the MDL.
// Also account for the case when currentMdl has been linked
// onto the chain and exception occured when accesing next user
// buffer.
//
if ((currentMdl != NULL) && (chainTarget != &currentMdl->Next))
IoFreeMdl(currentMdl);
}
//
// Return the total buffer count.
//
*TotalByteCount = totalLength;
return status;
} // AfdAllocateMdlChain32
#endif //_WIN64
VOID
AfdDestroyMdlChain (
IN PIRP Irp
)
/*++
Routine Description:
Unlocks & frees the MDLs in the MDL chain attached to the given IRP.
Arguments:
Irp - The IRP that owns the MDL chain to destroy.
Return Value:
None.
--*/
{
PMDL mdl;
PMDL nextMdl;
mdl = Irp->MdlAddress;
Irp->MdlAddress = NULL;
while( mdl != NULL ) {
nextMdl = mdl->Next;
MmUnlockPages( mdl );
IoFreeMdl( mdl );
mdl = nextMdl;
}
} // AfdDestroyMdlChain
ULONG
AfdCalcBufferArrayByteLength(
IN LPWSABUF BufferArray,
IN ULONG BufferCount
)
/*++
Routine Description:
Calculates the total size (in bytes) of the buffers described by the
specified WSABUF array.
Arguments:
BufferArray - Points to an array of WSABUF structures.
BufferCount - The number of entries in BufferArray.
Return Value:
ULONG - The total size (in bytes) of the buffers described by the
WSABUF array. Will raise an exception & return -1 if the total
size is obviously too large.
--*/
{
LARGE_INTEGER totalLength;
PAGED_CODE( );
//
// Sanity check.
//
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
ASSERT( BufferCount <= (MAXULONG/sizeof (WSABUF)));
//
// Scan the array & sum the lengths.
//
totalLength.QuadPart = 0;
while( BufferCount-- ) {
totalLength.QuadPart += (LONGLONG)BufferArray->len;
BufferArray++;
}
if( totalLength.HighPart != 0 ||
( totalLength.LowPart & 0x80000000 ) != 0 ) {
ExRaiseAccessViolation();
}
return totalLength.LowPart;
} // AfdCalcBufferArrayByteLength
ULONG
AfdCopyBufferArrayToBuffer(
IN PVOID Destination,
IN ULONG DestinationLength,
IN LPWSABUF BufferArray,
IN ULONG BufferCount
)
/*++
Routine Description:
Copies data from a WSABUF array to a linear buffer.
Arguments:
Destination - Points to the linear destination of the data.
DestinationLength - The length of Destination.
BufferArray - Points to an array of WSABUF structures describing the
source for the copy.
BufferCount - The number of entries in BufferArray.
Return Value:
ULONG - The number of bytes copied.
--*/
{
PVOID destinationStart;
ULONG bytesToCopy;
PAGED_CODE( );
//
// Sanity check.
//
ASSERT( Destination != NULL );
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
//
// Remember this so we can calc number of bytes copied.
//
destinationStart = Destination;
//
// Scan the array & copy to the linear buffer.
//
while( BufferCount-- && DestinationLength > 0 ) {
WSABUF Buffer = *BufferArray++;
bytesToCopy = min( DestinationLength, Buffer.len );
if( ExGetPreviousMode() != KernelMode ) {
ProbeForRead(
Buffer.buf, // Address
bytesToCopy, // Length
sizeof(UCHAR) // Alignment
);
}
RtlCopyMemory(
Destination,
Buffer.buf,
bytesToCopy
);
Destination = (PCHAR)Destination + bytesToCopy;
DestinationLength -= bytesToCopy;
}
//
// Return number of bytes copied.
//
return (ULONG)((PUCHAR)Destination - (PUCHAR)destinationStart);
} // AfdCopyBufferArrayToBuffer
ULONG
AfdCopyBufferToBufferArray(
IN LPWSABUF BufferArray,
IN ULONG Offset,
IN ULONG BufferCount,
IN PVOID Source,
IN ULONG SourceLength
)
/*++
Routine Description:
Copies data from a linear buffer to a WSABUF array.
Arguments:
BufferArray - Points to an array of WSABUF structures describing the
destination for the copy.
Offset - An offset within the buffer array at which the data should
be copied.
BufferCount - The number of entries in BufferArray.
Source - Points to the linear source of the data.
SourceLength - The length of Source.
Return Value:
ULONG - The number of bytes copied.
--*/
{
PVOID sourceStart;
ULONG bytesToCopy;
WSABUF buffer;
PAGED_CODE( );
//
// Sanity check.
//
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
ASSERT( Source != NULL );
ASSERT( SourceLength > 0 );
//
// Remember this so we can return the number of bytes copied.
//
sourceStart = Source;
//
// Handle the offset if one was specified.
//
if( Offset > 0 ) {
//
// Skip whole entries if necessary.
//
while( BufferCount-- > 0 ) {
buffer = *BufferArray++;
if (Offset < buffer.len) {
//
// If we have buffers left, fix up the buffer pointer
// and length to keep the loop below fast.
//
buffer.buf += Offset;
buffer.len -= Offset;
//
// We have already copied buffer array element, so jump
// to the body of the loop to avoid doing this again (this
// is not a mere optimization, but protection from application
// that plays tricks on us by changing content of the buffer
// array while we are looking at it).
//
goto DoCopy;
}
Offset -= buffer.len;
}
return 0;
}
//
// Scan the array & copy from the linear buffer.
//
while( BufferCount-->0 && SourceLength > 0 ) {
buffer = *BufferArray++;
DoCopy:
bytesToCopy = min( SourceLength, buffer.len );
if( ExGetPreviousMode() != KernelMode ) {
ProbeForWrite(
buffer.buf, // Address
bytesToCopy, // Length
sizeof(UCHAR) // Alignment
);
}
RtlCopyMemory(
buffer.buf,
Source,
bytesToCopy
);
Source = (PCHAR)Source + bytesToCopy;
SourceLength -= bytesToCopy;
}
//
// Return number of bytes copied.
//
return (ULONG)((PUCHAR)Source - (PUCHAR)sourceStart);
} // AfdCopyBufferToBufferArray
ULONG
AfdCopyMdlChainToBufferArray(
IN LPWSABUF BufferArray,
IN ULONG BufferOffset,
IN ULONG BufferCount,
IN PMDL SourceMdl,
IN ULONG SourceOffset,
IN ULONG SourceLength
)
/*++
Routine Description:
Copies data from a MDL chain to a WSABUF array.
Arguments:
BufferArray - Points to an array of WSABUF structures describing the
destination for the copy.
BufferOffset - An offset within the buffer array at which the data should
be copied.
BufferCount - The number of entries in BufferArray.
Source - Points to the MDL chain with data
SourceOffset - An offset within the MDL chain from which the data should
be copied.
SourceLength - The length of Source.
Return Value:
ULONG - The number of bytes copied.
--*/
{
ULONG bytesCopied;
ULONG bytesToCopy, len;
WSABUF buffer;
PAGED_CODE( );
//
// Assume we can copy everything.
//
bytesCopied = SourceLength;
//
// Sanity check.
//
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
ASSERT( SourceMdl != NULL );
ASSERT( SourceLength>0 );
//
// Skip offset into the MDL chain
//
while (SourceOffset>=MmGetMdlByteCount (SourceMdl)) {
SourceOffset -= MmGetMdlByteCount (SourceMdl);
SourceMdl = SourceMdl->Next;
}
//
// Handle buffer array offset if specified
//
if (BufferOffset>0) {
//
// Skip whole entries.
//
while( BufferCount-- > 0) {
buffer = *BufferArray++;
if (BufferOffset < buffer.len) {
//
// We have buffers left, fix up the buffer pointer
// and length to keep the loop below fast.
//
ASSERT (BufferOffset < buffer.len);
buffer.buf += BufferOffset;
buffer.len -= BufferOffset;
//
// We have already copied buffer array element, so jump
// to the body of the loop to avoid doing this again (this
// is not a mere optimization, but protection from application
// that plays tricks on us by changing content of the buffer
// array while we are looking at it).
//
goto DoCopy;
}
BufferOffset -= buffer.len;
}
return 0;
}
//
// Scan the array & copy from the mdl chain.
//
while (SourceLength>0 && BufferCount-->0) {
buffer = *BufferArray++;
DoCopy:
bytesToCopy = min( SourceLength, buffer.len );
if( ExGetPreviousMode() != KernelMode ) {
ProbeForWrite(
buffer.buf, // Address
bytesToCopy, // Length
sizeof(UCHAR) // Alignment
);
}
//
// Update source length for data we are going to copy
//
SourceLength -= bytesToCopy;
//
// Copy full source MDLs
//
while (bytesToCopy>0 &&
(bytesToCopy>=(len=MmGetMdlByteCount (SourceMdl)-SourceOffset))) {
ASSERT (SourceMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL));
RtlCopyMemory (buffer.buf,
(PUCHAR)MmGetSystemAddressForMdl(SourceMdl)+SourceOffset,
len);
bytesToCopy -= len;
buffer.buf += len;
SourceMdl = SourceMdl->Next;
SourceOffset = 0;
}
//
// Copy partial source MDL if space remains.
//
if (bytesToCopy>0) {
ASSERT (bytesToCopy<MmGetMdlByteCount (SourceMdl)-SourceOffset);
ASSERT (SourceMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL));
RtlCopyMemory (buffer.buf,
(PUCHAR)MmGetSystemAddressForMdl (SourceMdl)+SourceOffset,
bytesToCopy
);
SourceOffset += bytesToCopy;
}
}
//
// Return number of bytes copied except for those we couldn't.
//
return bytesCopied-SourceLength;
} // AfdCopyMdlChainToBufferArray
NTSTATUS
AfdCopyMdlChainToBufferAvoidMapping(
IN PMDL SrcMdl,
IN ULONG SrcOffset,
IN ULONG SrcLength,
IN PUCHAR Dst,
IN ULONG DstSize
)
/*++
Routine Description:
Copies data from a MDL chain to a buffer and avoids mapping
MDLs to system space if possible.
Arguments:
Dst - Points to destination for the copy.
DstSize - Size of the buffer
Source - Points to the MDL chain with data
SourceOffset - An offset within the MDL chain from which the data should
be copied.
SourceLength - The length of Source.
Return Value:
NTSTATUS - success if everything was copied OK
STATUS_INSUFFICIENT_RESOURCES - mapping failed
--*/
{
NTSTATUS status = STATUS_SUCCESS;
ULONG bytesToCopy;
PUCHAR DstEnd = Dst+DstSize;
PAGED_CODE( );
//
// Sanity check.
//
ASSERT( Dst != NULL );
ASSERT( DstSize > 0 );
ASSERT( SrcMdl != NULL );
ASSERT( SrcLength>0 );
//
// Skip offset into the MDL chain
//
while (SrcOffset>=MmGetMdlByteCount (SrcMdl)) {
SrcOffset -= MmGetMdlByteCount (SrcMdl);
SrcMdl = SrcMdl->Next;
}
while (Dst<DstEnd) {
//
// Determine how much we can copy and m
// ake sure not to exceed limits.
//
bytesToCopy = MmGetMdlByteCount(SrcMdl)-SrcOffset;
ASSERT (bytesToCopy<=(ULONG)(DstEnd-Dst));
if (bytesToCopy>SrcLength) {
bytesToCopy = SrcLength;
}
if (SrcMdl->Process==IoGetCurrentProcess ()) {
//
// If we are in the context of the same process that
// MDL was created for, copy using VAs.
//
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
RtlCopyMemory (
Dst,
(PUCHAR)MmGetMdlVirtualAddress (SrcMdl)+SrcOffset,
bytesToCopy
);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
return status;
}
}
else {
//
// Otherwise, map MDL into the system space.
//
PCHAR src = MmGetSystemAddressForMdlSafe (SrcMdl, LowPagePriority);
if (!src)
return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory (
Dst,
src+SrcOffset,
bytesToCopy
);
}
//
// Update source length for data we are going to copy
//
SrcLength -= bytesToCopy;
if (SrcLength==0)
return STATUS_SUCCESS;
SrcMdl = SrcMdl->Next;
SrcOffset = 0;
Dst += bytesToCopy;
}
return STATUS_BUFFER_OVERFLOW;
} // AfdCopyMdlChainToBufferAvoidMapping
NTSTATUS
AfdMapMdlChain (
PMDL MdlChain
)
/*++
Routine Description:
Makes sure that eveyr MDL in the chains is mapped into
the system address space.
Arguments:
MdlChain - Destination MDL.
Return Value:
STATUS_SUCCESS - MDL chain is fully mapped
STATUS_INSUFFICIENT_RESOURCES - at least one MDL could not be mapped
--*/
{
while (MdlChain!=NULL) {
if (MmGetSystemAddressForMdlSafe(MdlChain, LowPagePriority)==NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
MdlChain = MdlChain->Next;
}
return STATUS_SUCCESS;
} // AfdMapMdlChain
NTSTATUS
AfdCopyMdlChainToMdlChain (
PMDL DstMdl,
ULONG DstOffset,
PMDL SrcMdl,
ULONG SrcOffset,
ULONG SrcLength,
PULONG BytesCopied
)
/*++
Routine Description:
Copies data from an MDL chain to an MDL chain.
Arguments:
DstMdl - Destination MDL.
DstOffset - Offset withih the destination MDL.
SrcMdl - Source MDL.
SrcOffset - Offset within the source.
SrcLength - lenght of the data in the source chain
BytesCopied - points to variable that received total number
of bytes actually copied
Return Value:
STATUS_SUCCESS - all of the source data was copied
STATUS_BUFFER_OVERFLOW - destination MDL was not long enough
to hold all of the source data.
--*/
{
ULONG bytesToCopy = 0, len;
PUCHAR dst;
ASSERT( SrcMdl != NULL );
ASSERT( DstMdl != NULL );
//
// Assume we can copy everything.
//
*BytesCopied = SrcLength;
//
// Skip full MDLs in source.
//
while ((SrcMdl!=NULL) && (SrcOffset>=MmGetMdlByteCount (SrcMdl))) {
SrcOffset -= MmGetMdlByteCount (SrcMdl);
SrcMdl = SrcMdl->Next;
}
//
// Skip full MDLs in destination
//
while ((DstMdl!=NULL) && (DstOffset>=MmGetMdlByteCount (DstMdl))) {
DstOffset -= MmGetMdlByteCount (DstMdl);
DstMdl = DstMdl->Next;
}
//
// Handle remaining destination offset separately to simplify the main loop.
//
if (DstOffset>0) {
dst = MmGetSystemAddressForMdlSafe (DstMdl, LowPagePriority);
if (dst==NULL)
return STATUS_INSUFFICIENT_RESOURCES;
dst += DstOffset;
bytesToCopy = MmGetMdlByteCount(DstMdl)-DstOffset;
goto DoCopy;
}
//
// For each MDL in destination copy source MDLs
// while source data and free space in destination remain
//
while ((SrcLength>0) && (DstMdl!=NULL)) {
dst = MmGetSystemAddressForMdlSafe (DstMdl, LowPagePriority);
if (dst==NULL)
return STATUS_INSUFFICIENT_RESOURCES;
bytesToCopy = MmGetMdlByteCount(DstMdl);
DoCopy:
bytesToCopy = min (SrcLength, bytesToCopy);
//
// Adjust source length
//
SrcLength -= bytesToCopy;
//
// Copy full source MDLs
//
while (bytesToCopy>0 &&
(bytesToCopy>=(len=MmGetMdlByteCount (SrcMdl)-SrcOffset))) {
ASSERT (SrcMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL));
RtlCopyMemory (dst,
(PUCHAR)MmGetSystemAddressForMdl(SrcMdl)+SrcOffset,
len);
bytesToCopy -= len;
dst += len;
SrcMdl = SrcMdl->Next;
SrcOffset = 0;
}
//
// Copy partial source MDL if space remains
//
if (bytesToCopy>0) {
ASSERT (bytesToCopy<MmGetMdlByteCount (SrcMdl)-SrcOffset);
ASSERT (SrcMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL));
RtlCopyMemory (dst,
(PUCHAR)MmGetSystemAddressForMdl (SrcMdl)+SrcOffset,
bytesToCopy
);
SrcOffset += bytesToCopy;
}
//
// Advance to next MDL in destination
//
DstMdl = DstMdl->Next;
}
//
// If we copied everything, return success
//
if (SrcLength==0) {
return STATUS_SUCCESS;
}
else {
//
// Otherwise, adjust for number of bytes not copied
// and return destination overflow
//
*BytesCopied -= SrcLength;
return STATUS_BUFFER_OVERFLOW;
}
}
#if DBG
VOID
AfdAssert(
IN PVOID FailedAssertion,
IN PVOID FileName,
IN ULONG LineNumber,
IN PCHAR Message OPTIONAL
)
{
if( AfdUsePrivateAssert ) {
DbgPrint(
"\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n",
Message
? Message
: "",
FailedAssertion,
FileName,
LineNumber
);
DbgBreakPoint();
} else {
RtlAssert(
FailedAssertion,
FileName,
LineNumber,
Message
);
}
} // AfdAssert
#endif // DBG
NTSTATUS
FASTCALL
AfdSetQos(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine sets the QOS for the given endpoint. Note that, since
we don't really (yet) support QOS, we just ignore the incoming
data and issue a AFD_POLL_QOS or AFD_POLL_GROUP_QOS event as
appropriate.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_QOS_INFO qosInfo;
NTSTATUS status = STATUS_SUCCESS;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
status = AfdSetQos32 (Irp, IrpSp);
goto Complete;
}
#endif
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
qosInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that the input buffer is large enough.
//
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(*qosInfo) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto Complete;
}
//
// If the incoming data doesn't match the default QOS,
// indicate the appropriate event.
//
if( !RtlEqualMemory(
&qosInfo->Qos,
&AfdDefaultQos,
sizeof(QOS)
) ) {
AFD_LOCK_QUEUE_HANDLE lockHandle;
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
AfdIndicateEventSelectEvent(
endpoint,
qosInfo->GroupQos
? AFD_POLL_GROUP_QOS
: AFD_POLL_QOS,
STATUS_SUCCESS
);
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
AfdIndicatePollEvent(
endpoint,
qosInfo->GroupQos
? AFD_POLL_GROUP_QOS
: AFD_POLL_QOS,
STATUS_SUCCESS
);
}
Complete:
//
// Complete the IRP.
//
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdSetQos
NTSTATUS
FASTCALL
AfdGetQos(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine gets the QOS for the given endpoint.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_QOS_INFO qosInfo;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
Irp->IoStatus.Information = 0;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
status = AfdGetQos32 (Irp, IrpSp);
goto Complete;
}
#endif
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
qosInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that the output buffer is large enough.
//
if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(*qosInfo) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto Complete;
}
//
// Just return the default data.
//
RtlCopyMemory(
&qosInfo->Qos,
&AfdDefaultQos,
sizeof(QOS)
);
Irp->IoStatus.Information = sizeof(*qosInfo);
Irp->IoStatus.Information = sizeof(*qosInfo);
Complete:
//
// Complete the IRP.
//
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdGetQos
#ifdef _WIN64
NTSTATUS
AfdSetQos32(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine sets the QOS for the given endpoint. Note that, since
we don't really (yet) support QOS, we just ignore the incoming
data and issue a AFD_POLL_QOS or AFD_POLL_GROUP_QOS event as
appropriate.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_QOS_INFO32 qosInfo;
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
qosInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that the input buffer is large enough.
//
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(*qosInfo) ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// If the incoming data doesn't match the default QOS,
// indicate the appropriate event.
//
if( !RtlEqualMemory(
&qosInfo->Qos,
&AfdDefaultQos32,
sizeof(QOS32)
) ) {
AFD_LOCK_QUEUE_HANDLE lockHandle;
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
AfdIndicateEventSelectEvent(
endpoint,
qosInfo->GroupQos
? AFD_POLL_GROUP_QOS
: AFD_POLL_QOS,
STATUS_SUCCESS
);
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
AfdIndicatePollEvent(
endpoint,
qosInfo->GroupQos
? AFD_POLL_GROUP_QOS
: AFD_POLL_QOS,
STATUS_SUCCESS
);
}
//
// Complete the IRP.
//
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
} // AfdSetQos
NTSTATUS
AfdGetQos32(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine gets the QOS for the given endpoint.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_QOS_INFO32 qosInfo;
PAGED_CODE();
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
qosInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that the output buffer is large enough.
//
if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(*qosInfo) ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Just return the default data.
//
RtlCopyMemory(
&qosInfo->Qos,
&AfdDefaultQos32,
sizeof(QOS32)
);
//
// Complete the IRP.
//
Irp->IoStatus.Information = sizeof(*qosInfo);
return STATUS_SUCCESS;
} // AfdGetQos32
#endif // _WIN64
NTSTATUS
AfdValidateStatus (
NTSTATUS Status
)
{
PAGED_CODE ();
//
// Validate the status code.
// It must match the status code conversion algorithm in msafd.
//
switch (Status) {
case STATUS_SUCCESS:
// return NO_ERROR;
case STATUS_INVALID_HANDLE:
case STATUS_OBJECT_TYPE_MISMATCH:
// return WSAENOTSOCK;
case STATUS_INSUFFICIENT_RESOURCES:
case STATUS_PAGEFILE_QUOTA:
case STATUS_COMMITMENT_LIMIT:
case STATUS_WORKING_SET_QUOTA:
case STATUS_NO_MEMORY:
case STATUS_CONFLICTING_ADDRESSES:
case STATUS_QUOTA_EXCEEDED:
case STATUS_TOO_MANY_PAGING_FILES:
case STATUS_REMOTE_RESOURCES:
case STATUS_TOO_MANY_ADDRESSES:
// return WSAENOBUFS;
case STATUS_SHARING_VIOLATION:
case STATUS_ADDRESS_ALREADY_EXISTS:
// return WSAEADDRINUSE;
case STATUS_LINK_TIMEOUT:
case STATUS_IO_TIMEOUT:
case STATUS_TIMEOUT:
// return WSAETIMEDOUT;
case STATUS_GRACEFUL_DISCONNECT:
// return WSAEDISCON;
case STATUS_REMOTE_DISCONNECT:
case STATUS_CONNECTION_RESET:
case STATUS_LINK_FAILED:
case STATUS_CONNECTION_DISCONNECTED:
case STATUS_PORT_UNREACHABLE:
// return WSAECONNRESET;
case STATUS_LOCAL_DISCONNECT:
case STATUS_TRANSACTION_ABORTED:
case STATUS_CONNECTION_ABORTED:
// return WSAECONNABORTED;
case STATUS_BAD_NETWORK_PATH:
case STATUS_NETWORK_UNREACHABLE:
case STATUS_PROTOCOL_UNREACHABLE:
// return WSAENETUNREACH;
case STATUS_HOST_UNREACHABLE:
// return WSAEHOSTUNREACH;
case STATUS_HOST_DOWN:
// return WSAEHOSTDOWN;
case STATUS_CANCELLED:
case STATUS_REQUEST_ABORTED:
// return WSAEINTR;
case STATUS_BUFFER_OVERFLOW:
case STATUS_INVALID_BUFFER_SIZE:
// return WSAEMSGSIZE;
case STATUS_BUFFER_TOO_SMALL:
case STATUS_ACCESS_VIOLATION:
// return WSAEFAULT;
// case STATUS_DEVICE_NOT_READY:
// case STATUS_REQUEST_NOT_ACCEPTED:
// return WSAEWOULDBLOCK;
case STATUS_INVALID_NETWORK_RESPONSE:
case STATUS_NETWORK_BUSY:
case STATUS_NO_SUCH_DEVICE:
case STATUS_NO_SUCH_FILE:
case STATUS_OBJECT_PATH_NOT_FOUND:
case STATUS_OBJECT_NAME_NOT_FOUND:
case STATUS_UNEXPECTED_NETWORK_ERROR:
// return WSAENETDOWN;
case STATUS_INVALID_CONNECTION:
// return WSAENOTCONN;
case STATUS_REMOTE_NOT_LISTENING:
case STATUS_CONNECTION_REFUSED:
// return WSAECONNREFUSED;
case STATUS_PIPE_DISCONNECTED:
// return WSAESHUTDOWN;
case STATUS_INVALID_ADDRESS:
case STATUS_INVALID_ADDRESS_COMPONENT:
// return WSAEADDRNOTAVAIL;
case STATUS_NOT_SUPPORTED:
case STATUS_NOT_IMPLEMENTED:
// return WSAEOPNOTSUPP;
case STATUS_ACCESS_DENIED:
// return WSAEACCES;
case STATUS_CONNECTION_ACTIVE:
// return WSAEISCONN;
break;
case STATUS_UNSUCCESSFUL:
case STATUS_INVALID_PARAMETER:
case STATUS_ADDRESS_CLOSED:
case STATUS_CONNECTION_INVALID:
case STATUS_ADDRESS_ALREADY_ASSOCIATED:
case STATUS_ADDRESS_NOT_ASSOCIATED:
case STATUS_INVALID_DEVICE_STATE:
case STATUS_INVALID_DEVICE_REQUEST:
// return WSAEINVAL;
break;
default:
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdValidateStatus: Unsupported status code %lx, converting to %lx(INVALID_PARAMETER)\n",
Status,
STATUS_INVALID_PARAMETER));
Status = STATUS_INVALID_PARAMETER;
break;
}
return Status;
}
NTSTATUS
FASTCALL
AfdNoOperation(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine does nothing but complete the IRP.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
NTSTATUS status;
PAGED_CODE();
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
status = AfdNoOperation32 (Irp, IrpSp);
goto Complete;
}
#endif
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Assume success
//
status = STATUS_SUCCESS;
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength
>= sizeof (IO_STATUS_BLOCK)) {
try {
if (Irp->RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
sizeof (IO_STATUS_BLOCK),
PROBE_ALIGNMENT(IO_STATUS_BLOCK))
}
//
// Copy the status block
//
Irp->IoStatus
= *((PIO_STATUS_BLOCK)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer);
Irp->IoStatus.Status = AfdValidateStatus (Irp->IoStatus.Status);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
//
// Fail the call, no completion notification
// should be delivered via async IO.
//
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
}
}
else {
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
}
#ifdef _WIN64
Complete:
#endif
if (status==STATUS_SUCCESS && Irp->IoStatus.Status!=STATUS_SUCCESS) {
//
// Make sure we deliver error via async IO
// operation instead of just failing this call itself.
//
IoMarkIrpPending (Irp);
status = STATUS_PENDING;
}
else {
ASSERT (status==Irp->IoStatus.Status);
}
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdNoOperation
#ifdef _WIN64
NTSTATUS
AfdNoOperation32(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine does nothing but complete the IRP.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
NTSTATUS status;
PAGED_CODE();
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Assume success
//
status = STATUS_SUCCESS;
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength
>= sizeof (IO_STATUS_BLOCK32)) {
try {
if (Irp->RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
sizeof (IO_STATUS_BLOCK32),
PROBE_ALIGNMENT32(IO_STATUS_BLOCK32))
}
Irp->IoStatus.Status
= ((PIO_STATUS_BLOCK32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->Status;
Irp->IoStatus.Information
= ((PIO_STATUS_BLOCK32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->Information;
//
// Validate the status code.
// It must match the status code conversion algorithm in msafd.
//
Irp->IoStatus.Status = AfdValidateStatus (Irp->IoStatus.Status);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
//
// Fail the call, no completion notification
// should be delivered via async IO.
//
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
}
}
else {
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
}
return status;
} // AfdNoOperation32
#endif //_WIN64
NTSTATUS
FASTCALL
AfdValidateGroup(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine examines a group ID. If the ID is for a "constrained"
group, then all endpoints are scanned to validate the given address
is consistent with the constrained group.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT endpoint;
PAFD_ENDPOINT compareEndpoint;
PAFD_CONNECTION connection;
PLIST_ENTRY listEntry;
PAFD_VALIDATE_GROUP_INFO validateInfo;
AFD_GROUP_TYPE groupType;
PTRANSPORT_ADDRESS requestAddress;
ULONG requestAddressLength;
AFD_LOCK_QUEUE_HANDLE lockHandle;
BOOLEAN result;
LONG groupId;
NTSTATUS status = STATUS_SUCCESS;
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
validateInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that the input buffer is large enough.
//
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(*validateInfo) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto Complete;
}
if( validateInfo->RemoteAddress.TAAddressCount != 1 ) {
status = STATUS_INVALID_PARAMETER;
goto Complete;
}
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
( sizeof(*validateInfo) -
sizeof(TRANSPORT_ADDRESS) +
validateInfo->RemoteAddress.Address[0].AddressLength ) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto Complete;
}
//
// Start by referencing the group so it doesn't go away unexpectedly.
// This will also validate the group ID, and give us the group type.
//
groupId = validateInfo->GroupID;
if( !AfdReferenceGroup( groupId, &groupType ) ) {
status = STATUS_INVALID_PARAMETER;
goto Complete;
}
//
// If it's not a constrained group ID, we can just complete the IRP
// successfully right now.
//
if( groupType != GroupTypeConstrained ) {
AfdDereferenceGroup( validateInfo->GroupID );
Irp->IoStatus.Information = 0;
status = STATUS_SUCCESS;
goto Complete;
}
//
// Calculate the size of the incoming TDI address.
//
requestAddress = &validateInfo->RemoteAddress;
requestAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength -
sizeof(AFD_VALIDATE_GROUP_INFO) +
sizeof(TRANSPORT_ADDRESS);
//
// OK, it's a constrained group. Scan the list of constrained endpoints,
// find those that are either datagram endpoints or have associated
// connections, and validate the remote addresses.
//
result = TRUE;
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion ();
ExAcquireResourceSharedLite( AfdResource, TRUE );
for( listEntry = AfdConstrainedEndpointListHead.Flink ;
listEntry != &AfdConstrainedEndpointListHead ;
listEntry = listEntry->Flink ) {
compareEndpoint = CONTAINING_RECORD(
listEntry,
AFD_ENDPOINT,
ConstrainedEndpointListEntry
);
ASSERT( IS_AFD_ENDPOINT_TYPE( compareEndpoint ) );
ASSERT( compareEndpoint->GroupType == GroupTypeConstrained );
//
// Skip this endpoint if the group IDs don't match.
//
if( groupId != compareEndpoint->GroupID ) {
continue;
}
//
// If this is a datagram endpoint, check it's remote address.
//
if( IS_DGRAM_ENDPOINT( compareEndpoint ) ) {
AfdAcquireSpinLock( &compareEndpoint->SpinLock, &lockHandle );
if( compareEndpoint->Common.Datagram.RemoteAddress != NULL ) {
result = AfdCompareAddresses(
compareEndpoint->Common.Datagram.RemoteAddress,
compareEndpoint->Common.Datagram.RemoteAddressLength,
requestAddress,
requestAddressLength
);
}
AfdReleaseSpinLock( &compareEndpoint->SpinLock, &lockHandle );
if( !result ) {
break;
}
} else {
//
// Not a datagram. If it's a connected endpoint, still has
// a connection object, and that object has a remote address,
// then compare the addresses.
//
AfdAcquireSpinLock( &compareEndpoint->SpinLock, &lockHandle );
connection = AFD_CONNECTION_FROM_ENDPOINT( compareEndpoint );
if( compareEndpoint->State == AfdEndpointStateConnected &&
connection != NULL ) {
REFERENCE_CONNECTION( connection );
if( connection->RemoteAddress != NULL ) {
result = AfdCompareAddresses(
connection->RemoteAddress,
connection->RemoteAddressLength,
requestAddress,
requestAddressLength
);
}
AfdReleaseSpinLock( &compareEndpoint->SpinLock, &lockHandle );
DEREFERENCE_CONNECTION( connection );
if( !result ) {
break;
}
} else {
AfdReleaseSpinLock( &compareEndpoint->SpinLock, &lockHandle );
}
}
}
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
AfdDereferenceGroup( validateInfo->GroupID );
if( !result ) {
status = STATUS_INVALID_PARAMETER;
}
Complete:
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdValidateGroup
BOOLEAN
AfdCompareAddresses(
IN PTRANSPORT_ADDRESS Address1,
IN ULONG Address1Length,
IN PTRANSPORT_ADDRESS Address2,
IN ULONG Address2Length
)
/*++
Routine Description:
This routine compares two addresses in a special way to support
constrained socket groups. This routine will return TRUE if the
two addresses represent the same "interface". By "interface", I
mean something like an IP address or an IPX address. Note that for
some address types (such as IP) certain portions of the address
should be ignored (such as the port).
I really hate hard-coded knowledge of "select" address types, but
there's no easy way around it. Ideally, this should be the protocol
driver's responsibility. We could really use a standard "compare
these addresses" IOCTL in TDI.
Arguments:
Address1 - The first address.
Address1Length - The length of Address1.
Address2 - The second address.
Address2Length - The length of Address2.
Return Value:
BOOLEAN - TRUE if the addresses reference the same interface, FALSE
otherwise.
--*/
{
USHORT addressType;
if (Address1Length!=Address2Length)
return FALSE;
if (Address1Length<(ULONG)FIELD_OFFSET (TRANSPORT_ADDRESS,Address[0].Address)) {
return FALSE;
}
addressType = Address1->Address[0].AddressType;
if( addressType != Address2->Address[0].AddressType ) {
//
// If they're not the same address type, they can't be the
// same address...
//
return FALSE;
}
//
// Special case a few addresses.
//
switch( addressType ) {
case TDI_ADDRESS_TYPE_IP : {
TDI_ADDRESS_IP UNALIGNED * ip1;
TDI_ADDRESS_IP UNALIGNED * ip2;
ip1 = (PVOID)&Address1->Address[0].Address[0];
ip2 = (PVOID)&Address2->Address[0].Address[0];
//
// IP addresses. Compare the address portion (ignoring
// the port).
//
if( (Address1Length>=(ULONG)FIELD_OFFSET (TA_IP_ADDRESS, Address[0].Address[0].sin_zero)) &&
(ip1->in_addr == ip2->in_addr) ) {
return TRUE;
}
}
return FALSE;
case TDI_ADDRESS_TYPE_IP6 : {
TDI_ADDRESS_IP6 UNALIGNED * ip1;
TDI_ADDRESS_IP6 UNALIGNED * ip2;
ip1 = (PVOID)&Address1->Address[0].Address;
ip2 = (PVOID)&Address2->Address[0].Address;
//
// IPv6 addresses. Compare the address portion (ignoring
// the port and flow info).
//
if( (Address1Length>=sizeof (TA_IP6_ADDRESS)) &&
RtlEqualMemory(ip1->sin6_addr,
ip2->sin6_addr,
sizeof (ip1->sin6_addr)) ) {
return TRUE;
}
}
return FALSE;
case TDI_ADDRESS_TYPE_IPX : {
TDI_ADDRESS_IPX UNALIGNED * ipx1;
TDI_ADDRESS_IPX UNALIGNED * ipx2;
ipx1 = (PVOID)&Address1->Address[0].Address[0];
ipx2 = (PVOID)&Address2->Address[0].Address[0];
//
// IPX addresses. Compare the network and node addresses.
//
if( (Address1Length>=sizeof (TA_IPX_ADDRESS)) &&
ipx1->NetworkAddress == ipx2->NetworkAddress &&
RtlEqualMemory(
ipx1->NodeAddress,
ipx2->NodeAddress,
sizeof(ipx1->NodeAddress)
) ) {
return TRUE;
}
}
return FALSE;
case TDI_ADDRESS_TYPE_APPLETALK : {
TDI_ADDRESS_APPLETALK UNALIGNED * atalk1;
TDI_ADDRESS_APPLETALK UNALIGNED * atalk2;
atalk1 = (PVOID)&Address1->Address[0].Address[0];
atalk2 = (PVOID)&Address2->Address[0].Address[0];
//
// APPLETALK address. Compare the network and node
// addresses.
//
if( (Address1Length>=sizeof (TA_APPLETALK_ADDRESS)) &&
(atalk1->Network == atalk2->Network) &&
(atalk1->Node == atalk2->Node) ) {
return TRUE;
}
}
return FALSE;
case TDI_ADDRESS_TYPE_VNS : {
TDI_ADDRESS_VNS UNALIGNED * vns1;
TDI_ADDRESS_VNS UNALIGNED * vns2;
vns1 = (PVOID)&Address1->Address[0].Address[0];
vns2 = (PVOID)&Address2->Address[0].Address[0];
//
// VNS addresses. Compare the network and subnet addresses.
//
if( (Address1Length>=sizeof (TA_VNS_ADDRESS)) &&
RtlEqualMemory(
vns1->net_address,
vns2->net_address,
sizeof(vns1->net_address)
) &&
RtlEqualMemory(
vns1->subnet_addr,
vns2->subnet_addr,
sizeof(vns1->subnet_addr)
) ) {
return TRUE;
}
}
return FALSE;
default :
//
// Unknown address type. Do a simple memory compare.
//
return (BOOLEAN)RtlEqualMemory(
Address1,
Address2,
Address2Length
);
}
} // AfdCompareAddresses
NTSTATUS
AfdGetUnacceptedConnectData (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAFD_CONNECT_DATA_BUFFERS connectDataBuffers;
AFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo;
AFD_LOCK_QUEUE_HANDLE lockHandle;
ULONG dataLength;
PMDL mdl;
NTSTATUS status;
UCHAR localBuffer[AFD_FAST_CONNECT_DATA_SIZE];
UNREFERENCED_PARAMETER (IoctlCode);
//
// Set up local pointers.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
status = STATUS_SUCCESS;
mdl = NULL;
*Information = 0;
//
// Validate the request.
//
if( !endpoint->Listening ||
InputBufferLength < sizeof(connectInfo) ) {
return STATUS_INVALID_PARAMETER;
}
try {
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (connectInfo),
PROBE_ALIGNMENT(AFD_UNACCEPTED_CONNECT_DATA_INFO));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
connectInfo = *((PAFD_UNACCEPTED_CONNECT_DATA_INFO)InputBuffer);
if (connectInfo.LengthOnly &&
OutputBufferLength<sizeof (connectInfo)) {
status = STATUS_INVALID_PARAMETER;
goto exit;
}
if (OutputBufferLength>0) {
if (OutputBufferLength>sizeof (localBuffer)) {
mdl = IoAllocateMdl(
OutputBuffer, // VirtualAddress
OutputBufferLength, // Length
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
NULL // Irp
);
if (mdl==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
MmProbeAndLockPages(
mdl, // MemoryDescriptorList
RequestorMode, // AccessMode
IoWriteAccess // Operation
);
OutputBuffer = MmGetSystemAddressForMdlSafe(mdl, LowPagePriority);
if (OutputBuffer==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
}
else {
if (RequestorMode!=KernelMode) {
ProbeForWrite (OutputBuffer,
OutputBufferLength,
sizeof (UCHAR));
}
}
}
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
goto exit;
}
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Find the specified connection.
//
connection = AfdFindReturnedConnection(
endpoint,
connectInfo.Sequence
);
if( connection == NULL ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_INVALID_PARAMETER;
goto exit;
}
//
// Determine the length of any received connect data.
//
dataLength = 0;
connectDataBuffers = connection->ConnectDataBuffers;
if( connectDataBuffers != NULL &&
connectDataBuffers->ReceiveConnectData.Buffer != NULL ) {
dataLength = connectDataBuffers->ReceiveConnectData.BufferLength;
}
//
// If the caller is just interested in the data length, return it.
//
if( connectInfo.LengthOnly ) {
connectInfo.ConnectDataLength = dataLength;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
RtlCopyMemory (OutputBuffer,
&connectInfo,
sizeof (connectInfo));
*Information = sizeof (connectInfo);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
goto exit;
}
//
// If there is no connect data, complete the IRP with no bytes.
//
if( dataLength == 0 ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
goto exit;
}
//
// If the output buffer is too small, fail.
//
if( OutputBufferLength < dataLength ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_BUFFER_TOO_SMALL;
goto exit;
}
RtlCopyMemory(
mdl ? OutputBuffer : localBuffer,
connectDataBuffers->ReceiveConnectData.Buffer,
dataLength
);
*Information = dataLength;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
if (mdl==NULL) {
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
RtlCopyMemory (OutputBuffer,
localBuffer,
*Information);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
*Information = 0;
}
}
exit:
if (mdl!=NULL) {
if (mdl->MdlFlags & MDL_PAGES_LOCKED) {
MmUnlockPages (mdl);
}
IoFreeMdl (mdl);
}
return status;
} // AfdGetUnacceptedConnectData
#ifdef _WIN64
ULONG
AfdComputeCMSGLength32 (
PVOID ControlBuffer,
ULONG ControlLength
)
{
ULONG length = 0;
ASSERT (ControlLength>=sizeof (TDI_CMSGHDR));
while (ControlLength>=sizeof (TDI_CMSGHDR)) {
PTDI_CMSGHDR hdr;
hdr = ControlBuffer;
//
// Data comes from the trusted kernel mode driver source.
//
ASSERT (ControlLength >= TDI_CMSGHDR_ALIGN((hdr)->cmsg_len));
ControlLength -= (ULONG)TDI_CMSGHDR_ALIGN((hdr)->cmsg_len);
ControlBuffer = (PUCHAR)ControlBuffer +
TDI_CMSGHDR_ALIGN((hdr)->cmsg_len);
length += (ULONG)TDI_CMSGHDR_ALIGN32(
(hdr)->cmsg_len -
TDI_CMSGDATA_ALIGN (sizeof (TDI_CMSGHDR)) +
TDI_CMSGDATA_ALIGN32(sizeof(TDI_CMSGHDR32)) );
}
ASSERT (ControlLength==0);
return length;
}
VOID
AfdCopyCMSGBuffer32 (
PVOID Dst,
PVOID ControlBuffer,
ULONG CopyLength
)
{
while (CopyLength>=sizeof (TDI_CMSGHDR32)) {
PTDI_CMSGHDR hdr;
PTDI_CMSGHDR32 hdr32;
hdr = ControlBuffer;
hdr32 = Dst;
hdr32->cmsg_len = (ULONG)( (hdr)->cmsg_len -
TDI_CMSGDATA_ALIGN (sizeof (TDI_CMSGHDR)) +
TDI_CMSGDATA_ALIGN32(sizeof(TDI_CMSGHDR32)) );
hdr32->cmsg_level = hdr->cmsg_level;
hdr32->cmsg_type = hdr->cmsg_type;
if (CopyLength<(ULONG)TDI_CMSGHDR_ALIGN32(hdr32->cmsg_len))
break;
CopyLength -= (ULONG)TDI_CMSGHDR_ALIGN32(hdr32->cmsg_len);
RtlMoveMemory ((PUCHAR)hdr32+TDI_CMSGDATA_ALIGN32(sizeof(TDI_CMSGHDR32)),
(PUCHAR)hdr+TDI_CMSGDATA_ALIGN(sizeof(TDI_CMSGHDR)),
hdr32->cmsg_len-TDI_CMSGDATA_ALIGN32(sizeof(TDI_CMSGHDR32)));
ControlBuffer = (PUCHAR)ControlBuffer +
TDI_CMSGHDR_ALIGN((hdr)->cmsg_len);
Dst = (PUCHAR)Dst + TDI_CMSGHDR_ALIGN32((hdr32)->cmsg_len);
}
}
#endif //_WIN64
//
// This is currently not used by helper dlls.
// Commented out because of security concerns
//
#if NOT_YET
//
// Context structure allocated for non-blocking IOCTLs
//
typedef struct _AFD_NBIOCTL_CONTEXT {
AFD_REQUEST_CONTEXT Context; // Context to keep track of request
ULONG PollEvent; // Poll event to signal upon completion
// IRP Irp; // Irp to queue transport
// PCHAR SystemBuffer; // Input buffer if method!=3
} AFD_NBIOCTL_CONTEXT, *PAFD_NBIOCTL_CONTEXT;
NTSTATUS
FASTCALL
AfdDoTransportIoctl (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Passes the request from the helper DLL to the TDI transport
driver. In oreder to let the IO system properly complete asynchronous
IOCTL issued by the helper DLL, it should come on the socket handle
(afd endpoint object), and then afd redirects it to the transport
driver on the handle that herlper DLL specifies (normally address,
connection, or control channel handle)
Arguments:
Irp
IrpSp
Return Value:
NTSTATUS
--*/
{
PAFD_ENDPOINT endpoint;
AFD_TRANSPORT_IOCTL_INFO ioctlInfo;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
ULONG method;
PIRP newIrp;
PIO_STACK_LOCATION nextSp;
PAFD_REQUEST_CONTEXT requestCtx;
NTSTATUS status;
PAGED_CODE ();
endpoint = IrpSp->FileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
method = IrpSp->Parameters.DeviceIoControl.IoControlCode & 3;
if (method==METHOD_NEITHER) {
//
// We have to manually verify input buffer
//
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
PAFD_TRANSPORT_IOCTL_INFO32 ioctlInfo32;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (*ioctlInfo32)) {
status = STATUS_INVALID_PARAMETER;
goto Complete;
}
ioctlInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
if( Irp->RequestorMode != KernelMode ) {
ProbeForReadSmallStructure(
ioctlInfo32,
sizeof(*ioctlInfo32),
PROBE_ALIGNEMENT(AFD_TRANSPORT_IOCTL_INFO32)
);
}
ioctlInfo.Handle = ioctlInfo32->Handle;
ioctlInfo.InputBuffer = ioctlInfo32->InputBuffer;
ioctlInfo.InputBufferLength = ioctlInfo32->InputBufferLength;
ioctlInfo.IoControlCode = ioctlInfo32->IoControlCode;
ioctlInfo.AfdFlags = ioctlInfo32->AfdFlags;
ioctlInfo.PollEvent = ioctlInfo32->PollEvent;
}
else
#endif // _WIN64
{
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (ioctlInfo)) {
status = STATUS_INVALID_PARAMETER;
goto Complete;
}
if( Irp->RequestorMode != KernelMode ) {
ProbeForReadSmallStructure(
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
sizeof(ioctlInfo),
PROBE_ALIGNMENT(AFD_TRANSPORT_IOCTL_INFO)
);
}
ioctlInfo = *((PAFD_TRANSPORT_IOCTL_INFO)
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer);
}
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
//
// Exception accessing input structure.
//
goto Complete;
}
}
else {
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
PAFD_TRANSPORT_IOCTL_INFO32 ioctlInfo32;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (*ioctlInfo32)) {
status = STATUS_INVALID_PARAMETER;
goto Complete;
}
ioctlInfo32 = Irp->AssociatedIrp.SystemBuffer;
ioctlInfo.Handle = ioctlInfo32->Handle;
ioctlInfo.InputBuffer = ioctlInfo32->InputBuffer;
ioctlInfo.InputBufferLength = ioctlInfo32->InputBufferLength;
ioctlInfo.IoControlCode = ioctlInfo32->IoControlCode;
ioctlInfo.AfdFlags = ioctlInfo32->AfdFlags;
ioctlInfo.PollEvent = ioctlInfo32->PollEvent;
}
else
#endif // _WIN64
{
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (ioctlInfo)) {
status = STATUS_INVALID_PARAMETER;
goto Complete;
}
//
// Just copy the buffer verified by the IO system
//
ioctlInfo = *((PAFD_TRANSPORT_IOCTL_INFO)
Irp->AssociatedIrp.SystemBuffer);
}
}
//
// We rely as much as we can on the IO system to process
// IOCTL parameters for us. For this we have to make
// sure that method of AFD and helper DLL IOCTLs
// are the same, otherwise, someone can play tricks with
// buffer verification on us.
//
// If endpoint is non-blocking and request is not overlapped
// helper DLL MUST specify an event to check before queueing
// the request/signal upon its completion
//
if ((method!=(ioctlInfo.IoControlCode & 3))
|| (endpoint->NonBlocking
&& !(ioctlInfo.AfdFlags & AFD_OVERLAPPED)
&& !ioctlInfo.PollEvent)
) {
status = STATUS_INVALID_PARAMETER;
goto Complete;
}
//
// Make sure application has access to handle
// and get object reference
//
status = ObReferenceObjectByHandle(
ioctlInfo.Handle,
(ioctlInfo.IoControlCode >> 14) & 3, // DesiredAccess
*IoFileObjectType, // Must be a file object
Irp->RequestorMode,
(PVOID *)&fileObject,
NULL
);
if (NT_SUCCESS(status)) {
//
// Get the device object of the driver to which we send the IRP
//
deviceObject = IoGetRelatedDeviceObject (fileObject);
//
// If this is a non-blocking endpoint and IO is not overlapped
// and the specified event is not signalled,
// we'll have complete the helper DLL IRP with
// STATUS_DEVICE_NOT_READY (translates to WSAEWOUDLBLOCK)
// and queue another IRP to the transport so that
// the specified event can be completed when IRP is completed
//
if (endpoint->NonBlocking
&& !(ioctlInfo.AfdFlags & AFD_OVERLAPPED)
&& !(ioctlInfo.PollEvent & endpoint->EventsActive)) {
PAFD_NBIOCTL_CONTEXT nbIoctlCtx;
USHORT irpSize;
ULONG allocSize;
irpSize = IoSizeOfIrp (deviceObject->StackSize);
//
// Compute the block size and check for overflow
//
allocSize = sizeof (*nbIoctlCtx) + irpSize + ioctlInfo.InputBufferLength;
if (allocSize < ioctlInfo.InputBufferLength ||
allocSize < irpSize) {
status = STATUS_INVALID_PARAMETER;
ObDereferenceObject (fileObject);
goto Complete;
}
//
// Allocate an IRP and associated strucutures
//
try {
nbIoctlCtx = AFD_ALLOCATE_POOL_WITH_QUOTA (
NonPagedPool,
allocSize,
AFD_TRANSPORT_IRP_POOL_TAG
);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT (nbIoctlCtx!=NULL);
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode ();
ObDereferenceObject (fileObject);
goto Complete;
}
//
// Initialize context structures
//
requestCtx = &nbIoctlCtx->Context;
requestCtx->CleanupRoutine = AfdCleanupNBTransportIoctl;
nbIoctlCtx->PollEvent = ioctlInfo.PollEvent;
//
// Initialize IRP itself
//
newIrp = (PIRP)(nbIoctlCtx+1);
IoInitializeIrp( newIrp, irpSize, deviceObject->StackSize);
newIrp->RequestorMode = KernelMode;
newIrp->Tail.Overlay.AuxiliaryBuffer = NULL;
newIrp->Tail.Overlay.OriginalFileObject = IrpSp->FileObject;
nextSp = IoGetNextIrpStackLocation (newIrp);
if ((ioctlInfo.InputBuffer!=NULL)
&& (ioctlInfo.InputBufferLength>0)) {
//
// If helper DLL specified input buffer
// we'll have to make a copy of it in case
// driver really pends the IRP while we complete the
// helper DLL IRP an system frees the input buffer
//
PVOID newBuffer;
newBuffer = (PUCHAR)newIrp+IoSizeOfIrp(deviceObject->StackSize);
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
if (Irp->RequestorMode != KernelMode) {
ProbeForRead(
ioctlInfo.InputBuffer,
ioctlInfo.InputBufferLength,
sizeof(UCHAR)
);
}
RtlCopyMemory (newBuffer,
ioctlInfo.InputBuffer,
ioctlInfo.InputBufferLength);
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
//
// Exception accessing input structure.
//
AFD_FREE_POOL (nbIoctlCtx, AFD_TRANSPORT_IRP_POOL_TAG);
ObDereferenceObject (fileObject);
goto Complete;
}
//
// Store new buffer parameters in appropriate places
// in the IRP depending on the method
//
if (method==METHOD_NEITHER) {
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = newBuffer;
newIrp->AssociatedIrp.SystemBuffer = NULL;
}
else {
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
newIrp->AssociatedIrp.SystemBuffer = newBuffer;
}
nextSp->Parameters.DeviceIoControl.InputBufferLength =
ioctlInfo.InputBufferLength;
}
else {
//
// No input buffer, clear correspoinding entries
//
nextSp->Parameters.DeviceIoControl.InputBufferLength = 0;
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
newIrp->AssociatedIrp.SystemBuffer = NULL;
}
//
// NOTE: We do not allow output buffer parameters on
// non-blocking calls because the output buffer is deallocated
// when we complete helper DLL IRP
//
// Done during IRP initialization (IoInitializeIrp)
// newIrp->MdlAddress = NULL;
// newIrp->UserBuffer = NULL;
// nextSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
IoSetCompletionRoutine( newIrp, AfdCompleteNBTransportIoctl,
nbIoctlCtx,
TRUE, TRUE, TRUE );
}
else {
//
// Blocking call, reuse the application's IRP
//
newIrp = Irp;
nextSp = IoGetNextIrpStackLocation (Irp);
nextSp->Parameters.DeviceIoControl.OutputBufferLength =
IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
if ((ioctlInfo.InputBuffer!=NULL)
&& (ioctlInfo.InputBufferLength>0)) {
//
// If application wants to pass input buffer to transport,
// we'll have to copy it to the system buffer allocated with
// Irp
//
if (method!=METHOD_NEITHER) {
ULONG sysBufferLength;
if (method==METHOD_BUFFERED) {
sysBufferLength = max (
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
}
else {
sysBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
}
//
// Methods 0-2 use system buffer to pass input
// parameters and we need to reuse original system buffer
// Make sure it has enough space for this purpose
//
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
if (Irp->RequestorMode != KernelMode) {
ProbeForRead(
ioctlInfo.InputBuffer,
ioctlInfo.InputBufferLength,
sizeof(UCHAR)
);
}
if (ioctlInfo.InputBufferLength>sysBufferLength){
PVOID newSystemBuffer;
newSystemBuffer = ExAllocatePoolWithQuotaTag (
NonPagedPool|POOL_RAISE_IF_ALLOCATION_FAILURE,
ioctlInfo.InputBufferLength,
AFD_SYSTEM_BUFFER_POOL_TAG
);
if (newSystemBuffer==NULL) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
ExFreePool (Irp->AssociatedIrp.SystemBuffer);
Irp->AssociatedIrp.SystemBuffer = newSystemBuffer;
}
//
// Copy application data to the system buffer
//
RtlCopyMemory (Irp->AssociatedIrp.SystemBuffer,
ioctlInfo.InputBuffer,
ioctlInfo.InputBufferLength);
}
except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
ObDereferenceObject (fileObject);
goto Complete;
}
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
}
else {
//
// METHOD_NEITHER, just pass whatever application
// passed to use, the driver should handle it
// appropriately.
//
// This is of course a potentialy security breach
// if transport driver is buggy
//
nextSp->Parameters.DeviceIoControl.Type3InputBuffer
= ioctlInfo.InputBuffer;
}
nextSp->Parameters.DeviceIoControl.InputBufferLength
= ioctlInfo.InputBufferLength;
}
else {
//
// No input buffer, clear correspoiding parameters
// Note that we can't clean system buffer as
// it has to be deallocated on completion
//
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
nextSp->Parameters.DeviceIoControl.InputBufferLength = 0;
}
//
// We reuse our stack location parameters area for context
//
requestCtx = (PAFD_REQUEST_CONTEXT)&IrpSp->Parameters.DeviceIoControl;
requestCtx->CleanupRoutine = AfdCleanupTransportIoctl;
IoSetCompletionRoutine( newIrp, AfdCompleteTransportIoctl,
requestCtx, TRUE, TRUE, TRUE );
}
//
// Set the rest of IRP fields.
//
nextSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
nextSp->FileObject = fileObject;
nextSp->Parameters.DeviceIoControl.IoControlCode = ioctlInfo.IoControlCode;
//
// Insert context into the endpoint list so we can cancel
// the IRP when endpoint is being closed and reference endpoint
// so it does not go away until this IRP is completed
//
requestCtx->Context = newIrp;
REFERENCE_ENDPOINT (endpoint);
AfdEnqueueRequest(endpoint,requestCtx);
//
// Finally call the transport driver
//
status = IoCallDriver (deviceObject, newIrp);
//
// We no longer need our private reference to the file object
// IO system will take care of keeping this reference while our IRP
// is there
//
ObDereferenceObject (fileObject);
//
// If we used helper DLL IRP, just return whatever transport
// driver returned to us
//
if (newIrp==Irp)
return status;
//
// If driver pended or immediately completed non-blocking call,
// make sure helper DLL gets WSAEWOULDBLOCK. It will have to
// call again whenever the driver completes the IRP and corresponding
// event is set (if driver completed the IRP, event is set already).
//
if (NT_SUCCESS (status))
status = STATUS_DEVICE_NOT_READY;
}
//
// Complete the application request in case of processing failure or
// non-blocking call
//
Complete:
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
}
NTSTATUS
AfdCompleteTransportIoctl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
Called to complete transport driver IOCTL for blocking endpoints
Arguments:
Return Value:
STATUS_SUCCESS - IO system should finish IRP processing
STATUS_MORE_PROCESSING_REQUIRED - we are not yet done (we are actually
in the middle of cancelling)
--*/
{
PAFD_ENDPOINT endpoint = Irp->Tail.Overlay.OriginalFileObject->FsContext;
PAFD_REQUEST_CONTEXT requestCtx = Context;
AFD_LOCK_QUEUE_HANDLE lockHandle;
NTSTATUS status = STATUS_SUCCESS;
//
// We used Parameters structure in our stack location for context
//
ASSERT (&(IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl)
==Context);
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// We use list entry fields to synchronize with cleanup/cancel
// routine assuming that as long as the entry is in the list
// both Flink and Blink fields cannot be NULL. (using these
// fields for synchronization allows us to cut down on
// cancel spinlock usage)
//
if (AfdIsRequestInQueue(requestCtx)) {
//
// Context is still in the list, just remove it so
// noone can see it anymore
//
RemoveEntryList (&requestCtx->EndpointListLink);
}
else if (AfdIsRequestCompleted(requestCtx)) {
//
// During endpoint cleanup, this context was removed from the
// list and cancel routine is about to be called, don't let
// IO system free this IRP until cancel routine is called
// Also, indicate to the cancel routine that we are done
// with this IRP and it can complete it.
//
AfdMarkRequestCompleted (requestCtx);
status = STATUS_MORE_PROCESSING_REQUIRED;
}
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Release reference added when we posted this IRP
//
DEREFERENCE_ENDPOINT (endpoint);
return status;
}
NTSTATUS
AfdCompleteNBTransportIoctl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
Called to complete transport driver IOCTL for non-blocking endpoints
Arguments:
Return Value:
STATUS_MORE_PROCESSING_REQUIRED - we handle releasing resources
for this IRP ourselves
--*/
{
PAFD_ENDPOINT endpoint = Irp->Tail.Overlay.OriginalFileObject->FsContext;
PAFD_NBIOCTL_CONTEXT nbIoctlCtx = Context;
PAFD_REQUEST_CONTEXT requestCtx = &nbIoctlCtx->Context;
AFD_LOCK_QUEUE_HANDLE lockHandle;
//
// The irp should be a part of our notify structure
//
ASSERT (Irp==(PIRP)(nbIoctlCtx+1));
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
//
// First indicate the event reported by the driver
//
ASSERT (nbIoctlCtx->PollEvent!=0);
AfdIndicatePollEvent (endpoint, 1<<nbIoctlCtx->PollEvent, Irp->IoStatus.Status);
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
AfdIndicateEventSelectEvent (endpoint, 1<<nbIoctlCtx->PollEvent, Irp->IoStatus.Status);
//
// We use list entry fields to synchronize with cleanup/cancel
// routine assuming that as long as the entry is in the list
// both Flink and Blink fields cannot be NULL. (using these
// fields for synchronization allows us to cut down on
// cancel spinlock usage)
//
if (AfdIsRequestInQueue(requestCtx)) {
//
// Context is still in the list, just remove it so
// noone can see it anymore and free the structure
//
RemoveEntryList (&requestCtx->EndpointListLink);
AFD_FREE_POOL (nbIoctlCtx, AFD_TRANSPORT_IRP_POOL_TAG);
}
else if (AfdIsRequestCompleted (requestCtx)) {
//
// During endpoint cleanup, this context was removed from the
// list and cancel routine is about to be called, don't
// free this IRP until cancel routine is called
// Also, indicate to the cancel routine that we are done
// with this IRP and it can free it.
//
AfdMarkRequestCompleted (requestCtx);
}
else {
//
// Cancel routine has completed processing this request, free it
//
AFD_FREE_POOL (nbIoctlCtx, AFD_TRANSPORT_IRP_POOL_TAG);
}
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Release reference added when we posted this IRP
//
DEREFERENCE_ENDPOINT (endpoint);
return STATUS_MORE_PROCESSING_REQUIRED;
}
BOOLEAN
AfdCleanupTransportIoctl (
PAFD_ENDPOINT Endpoint,
PAFD_REQUEST_CONTEXT RequestCtx
)
/*++
Routine Description:
Cancels outstanding transport IOCTL during endpoint cleanup
Used for blocking requests.
Arguments:
Endpoint - endpoint on which IOCTL was issued
RequestCtx - context associated with the request
Return Value:
TRUE - request has been completed
FALSE - request is still in driver's queue
--*/
{
PIRP Irp = RequestCtx->Context;
AFD_LOCK_QUEUE_HANDLE lockHandle;
//
// First attempt to cancel the IRP, if it is already completed
// this is just a no-op. In no case IRP and request structure
// could have been freed until we mark it as completed as
// the caller of this routine should have marked the request
// as being cancelled
//
ASSERT (RequestCtx->EndpointListLink.Flink==NULL);
IoCancelIrp (Irp);
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
if (AfdIsRequestCompleted (RequestCtx)) {
//
// Driver has initiated the completion of the request
// as we were cancelling it.
// "Complete the completion"
//
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return TRUE;
}
else {
//
// Driver has not completed the request before returning
// from cancel routine, mark the request to indicate
// that we are done with it and completion routine
// can free it
//
AfdMarkRequestCompleted (RequestCtx);
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
return FALSE;
}
}
BOOLEAN
AfdCleanupNBTransportIoctl (
PAFD_ENDPOINT Endpoint,
PAFD_REQUEST_CONTEXT RequestCtx
)
/*++
Routine Description:
Cancels outstanding transport IOCTL during endpoint cleanup
Used for non-blocking requests
Arguments:
Endpoint - endpoint on which IOCTL was issued
RequestCtx - context associated with the request
Return Value:
TRUE - request has been completed
FALSE - request is still in driver's queue
--*/
{
PIRP Irp = RequestCtx->Context;
AFD_LOCK_QUEUE_HANDLE lockHandle;
//
// The IRP should be a part of the context block, verify.
//
ASSERT (Irp==(PIRP)(CONTAINING_RECORD (RequestCtx, AFD_NBIOCTL_CONTEXT, Context)+1));
//
// First attempt to cancel the IRP, if it is already completed
// this is just a no-op. In no case IRP and request structure
// could have been freed until we mark it as completed as
// the caller of this routine should have marked the request
// as being cancelled
//
ASSERT (RequestCtx->EndpointListLink.Flink==NULL);
IoCancelIrp (Irp);
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
if (AfdIsRequestCompleted (RequestCtx)) {
//
// Driver has initiated the completion of the request
// as we were cancelling it.
// Free the context structure.
//
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
AFD_FREE_POOL (
CONTAINING_RECORD (RequestCtx, AFD_NBIOCTL_CONTEXT, Context),
AFD_TRANSPORT_IRP_POOL_TAG);
return TRUE;
}
else {
//
// Driver has not completed the request before returning
// from cancel routine, mark the request to indicate
// that we are done with it and completion routine
// can free it
//
AfdMarkRequestCompleted (RequestCtx);
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
return FALSE;
}
}
#endif // NOT_YET
NTSTATUS
AfdQueryProviderInfo (
IN PUNICODE_STRING TransportDeviceName,
#ifdef _AFD_VARIABLE_STACK_
OUT CCHAR *StackSize OPTIONAL,
#endif //_AFD_VARIABLE_STACK
OUT PTDI_PROVIDER_INFO ProviderInfo
)
/*++
Routine Description:
Returns a provider information structure corresponding to the
specified TDI transport provider.
Arguments:
TransportDeviceName - the name of the TDI transport provider.
ProviderInfo - buffer to place provider info into
Return Value:
STATUS_SUCCESS - returned transport info is valid.
STATUS_OBJECT_NAME_NOT_FOUND - transport's device is not available yet
--*/
{
NTSTATUS status;
HANDLE controlChannel;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iosb;
TDI_REQUEST_KERNEL_QUERY_INFORMATION kernelQueryInfo;
PAGED_CODE ();
//
// 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.
// 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
);
status = IoCreateFile(
&controlChannel,
MAXIMUM_ALLOWED,
&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, // eaInfo
0, // eaLength
CreateFileTypeNone, // CreateFileType
NULL, // ExtraCreateParameters
IO_FORCE_ACCESS_CHECK // Options
| IO_NO_PARAMETER_CHECKING
);
if ( NT_SUCCESS(status) ) {
PFILE_OBJECT controlObject;
status = ObReferenceObjectByHandle (
controlChannel, // Handle
MAXIMUM_ALLOWED, // DesiredAccess
*IoFileObjectType, // ObjectType
KernelMode, // AccessMode
(PVOID *)&controlObject, // Object,
NULL // HandleInformation
);
if (NT_SUCCESS (status)) {
#ifdef _AFD_VARIABLE_STACK_
if (ARGUMENT_PRESENT (StackSize)) {
*StackSize = IoGetRelatedDeviceObject (controlObject)->StackSize;
}
#endif // _AFD_VARIABLE_STACK_
//
// Get the TDI provider information for the transport.
//
status = AfdIssueDeviceControl(
controlObject,
&kernelQueryInfo,
sizeof(kernelQueryInfo),
ProviderInfo,
sizeof(*ProviderInfo),
TDI_QUERY_INFORMATION
);
ObDereferenceObject (controlObject);
}
ZwClose( controlChannel );
}
if (!NT_SUCCESS (status)) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdQueryProviderInfo:"
"Transport %*ls failed provider info query with status %lx.\n",
TransportDeviceName->Length/2, TransportDeviceName->Buffer, status));
}
return status;
}
BOOLEAN
AfdCancelIrp (
IN PIRP Irp
)
/*++
Routine Description:
This routine is invoked to cancel an individual I/O Request Packet.
It is similiar to IoCancelIrp() except that it *must* be called with
the cancel spin lock held. This routine exists because of the
synchronization requirements of the cancellation/completion of
transmit IRPs.
Arguments:
Irp - Supplies a pointer to the IRP to be cancelled. The CancelIrql
field of the IRP must have been correctly initialized with the
IRQL from the cancel spin lock acquisition.
Return Value:
The function value is TRUE if the IRP was in a cancellable state (it
had a cancel routine), else FALSE is returned.
Notes:
It is assumed that the caller has taken the necessary action to ensure
that the packet cannot be fully completed before invoking this routine.
--*/
{
PDRIVER_CANCEL cancelRoutine;
//
// Make sure that the cancel spin lock is held.
//
ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
//
// Set the cancel flag in the IRP.
//
Irp->Cancel = TRUE;
//
// Obtain the address of the cancel routine, and if one was specified,
// invoke it.
//
cancelRoutine = IoSetCancelRoutine( Irp, NULL );
if (cancelRoutine) {
if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1)) {
KeBugCheckEx( CANCEL_STATE_IN_COMPLETED_IRP, (ULONG_PTR) Irp, 0, 0, 0 );
}
cancelRoutine( Irp->Tail.Overlay.CurrentStackLocation->DeviceObject,
Irp );
//
// The cancel spinlock should have been released by the cancel routine.
//
return(TRUE);
} else {
//
// There was no cancel routine, so release the cancel spinlock and
// return indicating the Irp was not currently cancelable.
//
IoReleaseCancelSpinLock( Irp->CancelIrql );
return(FALSE);
}
} // AfdCancelIrp
VOID
AfdTrimLookaside (
PNPAGED_LOOKASIDE_LIST Lookaside
)
{
PVOID entry;
#if DBG
LONG count = 0;
#endif
while (ExQueryDepthSList (&(Lookaside->L.ListHead))>Lookaside->L.Depth*2) {
entry = InterlockedPopEntrySList(
&Lookaside->L.ListHead);
if (entry) {
#if DBG
count++;
#endif
(Lookaside->L.Free)(entry);
}
else {
break;
}
}
#if DBG
if (count>0) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Flushed %d items from lookaside list @ %p\n",
count, Lookaside));
}
#endif
}
VOID
AfdCheckLookasideLists (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
LONG i;
UNREFERENCED_PARAMETER (SystemArgument1);
UNREFERENCED_PARAMETER (SystemArgument2);
#if DBG
ASSERT (Dpc==&AfdLookasideLists->Dpc);
ASSERT (DeferredContext==AfdLookasideLists);
#else
UNREFERENCED_PARAMETER (Dpc);
UNREFERENCED_PARAMETER (DeferredContext);
#endif
for (i=0; i<AFD_NUM_LOOKASIDE_LISTS; i++) {
if (ExQueryDepthSList (&(AfdLookasideLists->List[i].L.ListHead)) >
AfdLookasideLists->List[i].L.Depth*2) {
if (AfdLookasideLists->TrimFlags & (1<<i)) {
AfdTrimLookaside (&AfdLookasideLists->List[i]);
AfdLookasideLists->TrimFlags &= (~(1<<i));
}
else {
AfdLookasideLists->TrimFlags |= (1<<i);
}
}
else if (AfdLookasideLists->TrimFlags & (1<<i)) {
AfdLookasideLists->TrimFlags &= (~(1<<i));
}
}
}
VOID
AfdLRListAddItem (
PAFD_LR_LIST_ITEM Item,
PAFD_LR_LIST_ROUTINE Routine
)
/*++
Adds item to low resource list and starts low resource timer if not already
started.
Arguments:
Item - item to add
Routine - routine to execute when timeout expires.
Return Value:
None
Notes:
--*/
{
LONG count;
Item->Routine = Routine;
InterlockedPushEntrySList (
&AfdLRList,
&Item->SListLink);
count = InterlockedIncrement (&AfdLRListCount);
ASSERT (count>0);
if (count==1) {
AfdLRStartTimer ();
}
}
VOID
AfdLRListTimeout (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
DPC routine for low resource list timer
Simply schedules worker thread - do not want to do low resource processing at DPC
--*/
{
UNREFERENCED_PARAMETER (Dpc);
UNREFERENCED_PARAMETER (DeferredContext);
UNREFERENCED_PARAMETER (SystemArgument1);
UNREFERENCED_PARAMETER (SystemArgument2);
AfdQueueWorkItem (AfdProcessLRList, &AfdLRListWorker);
}
VOID
AfdProcessLRList (
PVOID Param
)
/*++
Routine Description:
Processeses items on low resource list and reschedules processing
if unprocessed items remain (still failing to buffer data due to
low resource condition)
Arguments:
None
Return Value:
None
Notes:
--*/
{
PSLIST_ENTRY localList, entry;
LONG count = 0;
UNREFERENCED_PARAMETER (Param);
PAGED_CODE ();
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AFD: Processing low resource list: %ld entries\n",
AfdLRListCount));
//
// Flush the list
//
localList = InterlockedFlushSList (&AfdLRList);
//
// Reverse it to preserve order of processing (FIFO).
//
entry = NULL;
while (localList!=NULL) {
PSLIST_ENTRY next;
next = localList->Next;
localList->Next = entry;
entry = localList;
localList = next;
}
localList = entry;
while (localList!=NULL) {
PAFD_LR_LIST_ITEM item;
entry = localList;
localList = localList->Next;
item = CONTAINING_RECORD (entry, AFD_LR_LIST_ITEM, SListLink);
//
// Try to restart receive processing on connection where buffer allocation failed
//
if (item->Routine (item)) {
//
// Success, decrement number of items outstanding,
// and note current number of items. If we did not empty
// the list, we'll have to restart the timer.
//
count = InterlockedDecrement (&AfdLRListCount);
ASSERT (count>=0);
}
else {
//
// Failure, put it back on the list. Note, that we have at list one
// item there and thus have to restart the timer again.
//
InterlockedPushEntrySList (&AfdLRList, &item->SListLink);
count = 1;
}
}
if (count!=0) {
//
// We did not empty the list, so restart the timer.
//
AfdLRStartTimer ();
}
}
VOID
AfdLRStartTimer (
VOID
)
/*++
Routine Description:
Start low resource timer to retry receive operation on connections
that could not buffer data due to low reaource condition.
Arguments:
None
Return Value:
None
Notes:
--*/
{
LARGE_INTEGER timeout;
BOOLEAN res;
timeout.QuadPart = -50000000i64; // 5 seconds
#if DBG
{
TIME_FIELDS timeFields;
LARGE_INTEGER currentTime;
LARGE_INTEGER localTime;
KeQuerySystemTime (&currentTime);
currentTime.QuadPart -= timeout.QuadPart;
ExSystemTimeToLocalTime (&currentTime, &localTime);
RtlTimeToTimeFields (&localTime, &timeFields);
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AFD: Scheduling low resource timer for %2.2d:%2.2d:%2.2d\n",
timeFields.Hour,
timeFields.Minute,
timeFields.Second));
}
#endif
KeInitializeDpc(
&AfdLRListDpc,
AfdLRListTimeout,
&AfdLRList
);
KeInitializeTimer( &AfdLRListTimer );
res = KeSetTimer(
&AfdLRListTimer,
timeout,
&AfdLRListDpc
);
ASSERT (res==FALSE);
}
#ifdef _AFD_VARIABLE_STACK_
VOID
AfdFixTransportEntryPointsForBigStackSize (
IN OUT PAFD_TRANSPORT_INFO TransportInfo,
IN CCHAR StackSize
)
{
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
TransportInfo->StackSize = StackSize;
if (TransportInfo->StackSize>AfdTdiStackSize) {
if (TransportInfo->StackSize>AfdMaxStackSize) {
AfdMaxStackSize = TransportInfo->StackSize;
}
TransportInfo->GetBuffer = AfdGetBufferWithMaxStackSize;
TransportInfo->GetTpInfo = AfdGetTpInfoWithMaxStackSize;
TransportInfo->CallDriver = AfdCallDriverStackIncrease;
}
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
}
VOID
AfdCancelStackIncreaseIrp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIRP newIrp;
UNREFERENCED_PARAMETER (DeviceObject);
newIrp = (PIRP)Irp->IoStatus.Information;
newIrp->Cancel = TRUE;
newIrp->CancelIrql = Irp->CancelIrql;
AfdCancelIrp (newIrp);
}
NTSTATUS
AfdRestartStackIncreaseIrp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PIRP oldIrp = Context;
UNREFERENCED_PARAMETER (DeviceObject);
if (IoSetCancelRoutine (oldIrp, NULL)==NULL) {
KIRQL oldIrql;
IoAcquireCancelSpinLock (&oldIrql);
IoReleaseCancelSpinLock (oldIrql);
}
oldIrp->IoStatus = Irp->IoStatus;
IoCompleteRequest (oldIrp, AfdPriorityBoost);
IoFreeIrp (Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
PIRP
AfdGetStackIncreaseIrp (
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
{
PIRP newIrp;
newIrp = IoAllocateIrp (DeviceObject->StackSize, FALSE);
if (newIrp!=NULL) {
*IoGetNextIrpStackLocation (newIrp) = *IoGetCurrentIrpStackLocation (Irp);
IoSetCompletionRoutine (newIrp, AfdRestartStackIncreaseIrp, Irp, TRUE, TRUE, TRUE);
newIrp->MdlAddress = Irp->MdlAddress;
newIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
Irp->IoStatus.Information = (ULONG_PTR)newIrp;
IoSetCancelRoutine (Irp, AfdCancelStackIncreaseIrp);
newIrp->Cancel = Irp->Cancel;
}
return newIrp;
}
NTSTATUS
FASTCALL
AfdCallDriverStackIncrease (
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
{
if (Irp->CurrentLocation <= DeviceObject->StackSize) {
PIRP newIrp;
IoSetNextIrpStackLocation (Irp);
newIrp = AfdGetStackIncreaseIrp (DeviceObject, Irp);
if (newIrp!=NULL) {
IoMarkIrpPending (Irp);
IoCallDriver (DeviceObject, newIrp);
return STATUS_PENDING;
}
else {
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
IoCompleteRequest (Irp, AfdPriorityBoost);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
else {
return IoCallDriver (DeviceObject, Irp);
}
}
PIRP
AfdGetStackIncreaseIrpAndRecordIt (
IN PAFD_ENDPOINT Endpoint,
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
#if DBG
,
IN PCHAR File,
IN ULONG Line
#endif
)
{
#if DBG
if (AfdRecordOutstandingIrpDebug (Endpoint, DeviceObject, Irp, File, Line))
#else
if (AfdRecordOutstandingIrp(Endpoint,DeviceObject,Irp))
#endif
{
PIRP newIrp;
IoSetNextIrpStackLocation (Irp);
newIrp = AfdGetStackIncreaseIrp (DeviceObject, Irp);
if (newIrp!=NULL) {
return newIrp;
}
AfdCompleteOutstandingIrp (Endpoint, Irp);
}
return NULL;
}
#endif // _AFD_VARIABLE_STACK_
#ifdef _AFD_VERIFY_DATA_
VOID
AfdVerifyBuffer (
PAFD_CONNECTION Connection,
PVOID Buffer,
ULONG Length
)
{
if (Connection->VerifySequenceNumber!=0) {
PUCHAR start, end;
ULONGLONG seq;
for (start=Buffer,
end = (PUCHAR)Buffer+Length,
seq = Connection->VerifySequenceNumber-1;
start<end;
seq++, start++) {
ULONG num = (ULONG)(seq/4);
ULONG byte = (ULONG)(seq%4);
if (*start!=(UCHAR)(num>>(byte*8))) {
DbgPrint ("AfdVerifyBuffer: Data sequence number mismatch on connection %p:\n"
" data buffer-%p, offset-%lx, expected-%2.2lx, got-%2.2lx.\n",
Connection,
Buffer,
start-(PUCHAR)Buffer,
(UCHAR)(num>>(byte*8)),
*start);
DbgBreakPoint ();
//
// Disable verification to continue.
//
Connection->VerifySequenceNumber = 0;
return;
}
}
Connection->VerifySequenceNumber = seq+1;
}
}
VOID
AfdVerifyMdl (
PAFD_CONNECTION Connection,
PMDL Mdl,
ULONG Offset,
ULONG Length
) {
if (Connection->VerifySequenceNumber!=0) {
while (Mdl!=NULL) {
if (Offset>=MmGetMdlByteCount (Mdl)) {
Offset-=MmGetMdlByteCount (Mdl);
}
else if (Length<=MmGetMdlByteCount (Mdl)-Offset) {
AfdVerifyBuffer (Connection,
(PUCHAR)MmGetSystemAddressForMdl (Mdl)+Offset,
Length);
break;
}
else {
AfdVerifyBuffer (Connection,
(PUCHAR)MmGetSystemAddressForMdl (Mdl)+Offset,
MmGetMdlByteCount (Mdl)-Offset
);
Length-=(MmGetMdlByteCount (Mdl)-Offset);
Offset = 0;
}
Mdl = Mdl->Next;
}
}
}
ULONG AfdVerifyType = 0;
ULONG AfdVerifyPort = 0;
PEPROCESS AfdVerifyProcess = NULL;
VOID
AfdVerifyAddress (
PAFD_CONNECTION Connection,
PTRANSPORT_ADDRESS Address
)
{
Connection->VerifySequenceNumber = 0;
if ((AfdVerifyPort==0) ||
((AfdVerifyProcess!=NULL) &&
(AfdVerifyProcess!=Connection->OwningProcess)) ||
((AfdVerifyType!=0) &&
(AfdVerifyType!=(USHORT)Address->Address[0].AddressType))
) {
return;
}
switch (Address->Address[0].AddressType) {
case TDI_ADDRESS_TYPE_IP : {
TDI_ADDRESS_IP UNALIGNED * ip;
ip = (PVOID)&Address->Address[0].Address[0];
if (ip->sin_port!=(USHORT)AfdVerifyPort) {
return;
}
}
break;
case TDI_ADDRESS_TYPE_IPX : {
TDI_ADDRESS_IPX UNALIGNED * ipx;
ipx = (PVOID)&Address->Address[0].Address[0];
if (ipx->Socket!=(USHORT)AfdVerifyPort) {
return;
}
}
break;
case TDI_ADDRESS_TYPE_APPLETALK : {
TDI_ADDRESS_APPLETALK UNALIGNED * atalk;
atalk = (PVOID)&Address->Address[0].Address[0];
if (atalk->Socket!=(UCHAR)AfdVerifyPort) {
return;
}
}
break;
default:
if (AfdVerifyType==0)
return;
DbgPrint ("AfdVerifyAddress: connection-%8.8lx, addres-%8.8lx\n",
Connection, Address);
DbgBreakPoint ();
}
Connection->VerifySequenceNumber = 1;
}
#endif // _AFD_VERIFY_DATA_
LONG
AfdExceptionFilter(
#if DBG
IN PCHAR SourceFile,
IN LONG LineNumber,
#endif
IN PEXCEPTION_POINTERS ExceptionPointers,
OUT PNTSTATUS ExceptionCode OPTIONAL
)
{
PAGED_CODE ();
//
// Return exception code and translate alignment warnings into
// alignment errors if requested.
//
if (ExceptionCode) {
*ExceptionCode = ExceptionPointers->ExceptionRecord->ExceptionCode;
if (*ExceptionCode == STATUS_DATATYPE_MISALIGNMENT) {
*ExceptionCode = STATUS_DATATYPE_MISALIGNMENT_ERROR;
}
}
#if DBG
//
// Protect ourselves in case the process is totally messed up.
//
try {
PCHAR fileName;
//
// Strip off the path from the source file.
//
fileName = strrchr( SourceFile, '\\' );
if( fileName == NULL ) {
fileName = SourceFile;
} else {
fileName++;
}
//
// Whine about the exception.
//
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdExceptionFilter: exception %08lx @ %08lx, caught in %s:%d\n",
ExceptionPointers->ExceptionRecord->ExceptionCode,
ExceptionPointers->ExceptionRecord->ExceptionAddress,
fileName,
LineNumber
));
}
except( EXCEPTION_EXECUTE_HANDLER ) {
//
// Not much we can do here...
//
NOTHING;
}
#endif //DBG
return EXCEPTION_EXECUTE_HANDLER;
} // AfdExceptionFilter
#if DBG
LONG
AfdApcExceptionFilter(
PEXCEPTION_POINTERS ExceptionPointers,
PCHAR SourceFile,
LONG LineNumber
)
{
PCHAR fileName;
PAGED_CODE ();
//
// Protect ourselves in case the process is totally messed up.
//
try {
//
// Strip off the path from the source file.
//
fileName = strrchr( SourceFile, '\\' );
if( fileName == NULL ) {
fileName = SourceFile;
} else {
fileName++;
}
//
// Whine about the exception.
//
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_ERROR_LEVEL,
"AfdApcExceptionFilter: exception %08lx, exr:%p cxr:%p, caught in %s:%d\n",
ExceptionPointers->ExceptionRecord->ExceptionCode,
ExceptionPointers->ExceptionRecord,
ExceptionPointers->ContextRecord,
fileName,
LineNumber
));
DbgBreakPoint ();
}
except( EXCEPTION_EXECUTE_HANDLER ) {
//
// Not much we can do here...
//
NOTHING;
}
return EXCEPTION_CONTINUE_SEARCH;
} // AfdApcExceptionFilter
#endif