Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

4804 lines
108 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
misc.c
Abstract:
This module contains the miscellaneous AFD routines.
Author:
David Treadwell (davidtr) 13-Nov-1992
Revision History:
--*/
#include "afdp.h"
#define TL_INSTANCE 0
#include <ipexport.h>
#include <tdiinfo.h>
#include <tcpinfo.h>
#include <ntddtcp.h>
VOID
AfdDoWork (
IN PVOID Context
);
NTSTATUS
AfdRestartDeviceControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
AfdUnlockDriver (
IN PVOID Context
);
#ifdef NT351
typedef struct _AFD_APC {
KAPC Apc;
} AFD_APC, *PAFD_APC;
VOID
AfdSpecialApc (
struct _KAPC *Apc,
PKNORMAL_ROUTINE *NormalRoutine,
PVOID *NormalContext,
PVOID *SystemArgument1,
PVOID *SystemArgument2
);
VOID
AfdSpecialApcRundown (
struct _KAPC *Apc
);
#endif // NT351
BOOLEAN
AfdCompareAddresses(
IN PTRANSPORT_ADDRESS Address1,
IN ULONG Address1Length,
IN PTRANSPORT_ADDRESS Address2,
IN ULONG Address2Length
);
PAFD_CONNECTION
AfdFindReturnedConnection(
IN PAFD_ENDPOINT Endpoint,
IN ULONG Sequence
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfdCalcBufferArrayByteLengthRead )
#pragma alloc_text( PAGE, AfdCalcBufferArrayByteLengthWrite )
#pragma alloc_text( PAGE, AfdCopyBufferArrayToBuffer )
#pragma alloc_text( PAGE, AfdCopyBufferToBufferArray )
#pragma alloc_text( PAGEAFD, AfdAdvanceMdlChain )
#pragma alloc_text( PAGEAFD, AfdAllocateMdlChain )
#pragma alloc_text( PAGE, AfdQueryHandles )
#pragma alloc_text( PAGE, AfdGetInformation )
#pragma alloc_text( PAGE, AfdSetInformation )
#pragma alloc_text( PAGE, AfdSetInLineMode )
#pragma alloc_text( PAGE, AfdGetContext )
#pragma alloc_text( PAGE, AfdGetContextLength )
#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( PAGEAFD, AfdCompleteIrpList )
#pragma alloc_text( PAGEAFD, AfdErrorEventHandler )
//#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 )
//#pragma alloc_text( PAGEAFD, AfdDoWork )
#pragma alloc_text( PAGEAFD, AfdAllocateWorkItem )
#pragma alloc_text( PAGEAFD, AfdQueueWorkItem )
#pragma alloc_text( PAGEAFD, AfdFreeWorkItem )
#if DBG
#pragma alloc_text( PAGEAFD, AfdIoCallDriverDebug )
#pragma alloc_text( PAGEAFD, AfdAllocateWorkItemPool )
#pragma alloc_text( PAGEAFD, AfdFreeWorkItemPool )
#else
#pragma alloc_text( PAGEAFD, AfdIoCallDriverFree )
#endif
#ifdef NT351
#pragma alloc_text( PAGE, AfdReferenceEventObjectByHandle )
#pragma alloc_text( PAGE, AfdQueueUserApc )
#pragma alloc_text( PAGE, AfdSpecialApc )
#pragma alloc_text( PAGE, AfdSpecialApcRundown )
#endif
#pragma alloc_text( PAGE, AfdSetQos )
#pragma alloc_text( PAGE, AfdGetQos )
#pragma alloc_text( PAGE, AfdNoOperation )
#pragma alloc_text( PAGE, AfdValidateGroup )
#pragma alloc_text( PAGE, AfdCompareAddresses )
#pragma alloc_text( PAGE, AfdGetUnacceptedConnectData )
#pragma alloc_text( PAGE, AfdFindReturnedConnection )
#endif
VOID
AfdCompleteIrpList (
IN PLIST_ENTRY IrpListHead,
IN PKSPIN_LOCK SpinLock,
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.
SpinLock - a 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;
KIRQL oldIrql;
KIRQL cancelIrql;
IoAcquireCancelSpinLock( &cancelIrql );
AfdAcquireSpinLock( SpinLock, &oldIrql );
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 );
IoSetCancelRoutine( irp, NULL );
//
// If we have a cleanup routine, call it.
//
if( CleanupRoutine != NULL ) {
(CleanupRoutine)( irp );
}
//
// 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( SpinLock, oldIrql );
IoReleaseCancelSpinLock( cancelIrql );
//
// Complete the IRP.
//
irp->IoStatus.Status = Status;
irp->IoStatus.Information = 0;
IoCompleteRequest( irp, AfdPriorityBoost );
//
// Reacquire the locks and continue completing IRPs.
//
IoAcquireCancelSpinLock( &cancelIrql );
AfdAcquireSpinLock( SpinLock, &oldIrql );
}
AfdReleaseSpinLock( SpinLock, oldIrql );
IoReleaseCancelSpinLock( cancelIrql );
return;
} // AfdCompleteIrpList
NTSTATUS
AfdErrorEventHandler (
IN PVOID TdiEventContext,
IN NTSTATUS Status
)
{
IF_DEBUG(CONNECT) {
KdPrint(( "AfdErrorEventHandler called for endpoint %lx\n",
TdiEventContext ));
}
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.
//
ExAcquireResourceExclusive( 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.
//
MmResetDriverPaging( DriverEntry );
//
// Lock down the AFD section that cannot be pagable if any
// sockets are open.
//
ASSERT( AfdDiscardableCodeHandle == NULL );
AfdDiscardableCodeHandle = MmLockPagableCodeSection( AfdGetBuffer );
ASSERT( AfdDiscardableCodeHandle != NULL );
AfdLoaded = TRUE;
}
//
// Add the endpoint to the list(s).
//
ExInterlockedInsertHeadList(
&AfdEndpointListHead,
&Endpoint->GlobalEndpointListEntry,
&AfdSpinLock
);
if( Endpoint->GroupType == GroupTypeConstrained ) {
ExInterlockedInsertHeadList(
&AfdConstrainedEndpointListHead,
&Endpoint->ConstrainedEndpointListEntry,
&AfdSpinLock
);
}
//
// Release the lock and return.
//
ExReleaseResource( AfdResource );
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.
//
ExAcquireResourceExclusive( AfdResource, TRUE );
InterlockedIncrement(
&AfdEndpointsClosed
);
//
// Remove the endpoint from the list(s).
//
AfdInterlockedRemoveEntryList(
&Endpoint->GlobalEndpointListEntry,
&AfdSpinLock
);
if( Endpoint->GroupType == GroupTypeConstrained ) {
AfdInterlockedRemoveEntryList(
&Endpoint->ConstrainedEndpointListEntry,
&AfdSpinLock
);
}
//
// If the list of endpoints is now empty, do some deallocations.
//
if ( IsListEmpty( &AfdEndpointListHead ) ) {
PAFD_WORK_ITEM afdWorkItem;
//
// 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.
//
afdWorkItem = AfdAllocateWorkItem();
ASSERT( afdWorkItem != NULL );
AfdQueueWorkItem( AfdUnlockDriver, afdWorkItem );
}
//
// Release the lock and return.
//
ExReleaseResource( AfdResource );
return;
} // AfdRemoveEndpointFromList
VOID
AfdUnlockDriver (
IN PVOID Context
)
{
//
// Free the work item allocated in AdfRemoveEndpointFromList().
//
AfdFreeWorkItem( (PAFD_WORK_ITEM)Context );
//
// Acquire a lock which prevents other threads from performing this
// operation.
//
ExAcquireResourceExclusive( 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.
//
AfdLoaded = FALSE;
MmPageEntireDriver( DriverEntry );
}
ExReleaseResource( AfdResource );
} // AfdUnlockDriver
VOID
AfdInterlockedRemoveEntryList (
IN PLIST_ENTRY ListEntry,
IN PKSPIN_LOCK SpinLock
)
{
KIRQL oldIrql;
//
// Our own routine since EX doesn't have a version of this....
//
AfdAcquireSpinLock( SpinLock, &oldIrql );
RemoveEntryList( ListEntry );
AfdReleaseSpinLock( SpinLock, oldIrql );
} // AfdInterlockedRemoveEntryList
NTSTATUS
AfdQueryHandles (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
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_HANDLE_INFO handleInfo;
ULONG getHandleInfo;
NTSTATUS status;
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
handleInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that the input and output buffers are large enough.
//
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(getHandleInfo) ||
IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(*handleInfo) ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Determine which handles we need to get.
//
getHandleInfo = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
//
// 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 != AfdEndpointStateOpen &&
endpoint->AddressHandle != NULL ) {
ASSERT( endpoint->AddressFileObject != NULL );
status = ObOpenObjectByPointer(
endpoint->AddressFileObject,
OBJ_CASE_INSENSITIVE,
NULL,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
*IoFileObjectType,
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.
//
if ( (getHandleInfo & AFD_QUERY_CONNECTION_HANDLE) != 0 &&
endpoint->Type == AfdBlockTypeVcConnecting &&
endpoint->Common.VcConnecting.Connection != NULL &&
endpoint->Common.VcConnecting.Connection->Handle != NULL ) {
ASSERT( endpoint->Common.VcConnecting.Connection->Type == AfdBlockTypeConnection );
ASSERT( endpoint->Common.VcConnecting.Connection->FileObject != NULL );
status = ObOpenObjectByPointer(
endpoint->Common.VcConnecting.Connection->FileObject,
OBJ_CASE_INSENSITIVE,
NULL,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
*IoFileObjectType,
KernelMode,
&handleInfo->TdiConnectionHandle
);
if ( !NT_SUCCESS(status) ) {
if ( handleInfo->TdiAddressHandle != NULL ) {
ZwClose( handleInfo->TdiAddressHandle );
}
return status;
}
}
Irp->IoStatus.Information = sizeof(*handleInfo);
return STATUS_SUCCESS;
} // AfdQueryHandles
NTSTATUS
AfdGetInformation (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
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;
PAFD_INFORMATION afdInfo;
PVOID additionalInfo;
ULONG additionalInfoLength;
TDI_REQUEST_KERNEL_QUERY_INFORMATION kernelQueryInfo;
TDI_CONNECTION_INFORMATION connectionInfo;
NTSTATUS status;
LONGLONG currentTime;
LONGLONG connectTime;
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
afdInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that the input and output buffers are large enough.
//
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(*afdInfo) ||
IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(*afdInfo) ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Figure out the additional information, if any.
//
additionalInfo = afdInfo + 1;
additionalInfoLength =
IrpSp->Parameters.DeviceIoControl.InputBufferLength - sizeof(*afdInfo);
//
// Set up appropriate information in the endpoint.
//
switch ( afdInfo->InformationType ) {
case AFD_MAX_PATH_SEND_SIZE:
//
// 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;
connectionInfo.RemoteAddressLength = additionalInfoLength;
connectionInfo.RemoteAddress = additionalInfo;
//
// Ask the TDI provider for the information.
//
status = AfdIssueDeviceControl(
NULL,
endpoint->AddressFileObject,
&kernelQueryInfo,
sizeof(kernelQueryInfo),
&afdInfo->Information.Ulong,
sizeof(afdInfo->Information.Ulong),
TDI_QUERY_INFORMATION
);
//
// 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:
//
// 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 =
endpoint->TransportInfo->ProviderInfo.MaxDatagramSize;
} else {
afdInfo->Information.Ulong =
endpoint->TransportInfo->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 ( endpoint->TdiBufferring || endpoint->Type != AfdBlockTypeVcConnecting ) {
afdInfo->Information.Ulong = 0;
} else {
afdInfo->Information.Ulong =
endpoint->Common.VcConnecting.Connection->VcBufferredSendCount;
}
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 ||
endpoint->EndpointType == AfdEndpointTypeDatagram ) {
afdInfo->Information.Ulong = 0xFFFFFFFF;
} else {
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
ASSERT( connection != NULL );
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.
//
KeQuerySystemTime( (PLARGE_INTEGER)&currentTime );
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;
}
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;
}
Irp->IoStatus.Information = sizeof(*afdInfo);
Irp->IoStatus.Status = STATUS_SUCCESS;
return STATUS_SUCCESS;
} // AfdGetInformation
NTSTATUS
AfdSetInformation (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
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;
PAFD_INFORMATION afdInfo;
NTSTATUS status;
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
afdInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that the input buffer is large enough.
//
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*afdInfo) ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// 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.
//
endpoint->NonBlocking = afdInfo->Information.Boolean;
break;
case AFD_CIRCULAR_QUEUEING:
//
// Enables circular queuing on the endpoint.
//
if( !IS_DGRAM_ENDPOINT( endpoint ) ) {
return STATUS_INVALID_PARAMETER;
}
endpoint->Common.Datagram.CircularQueueing = afdInfo->Information.Boolean;
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 ) {
status = AfdSetInLineMode(
AFD_CONNECTION_FROM_ENDPOINT( endpoint ),
afdInfo->Information.Boolean
);
if ( !NT_SUCCESS(status) ) {
return status;
}
}
endpoint->InLine = afdInfo->Information.Boolean;
break;
case AFD_RECEIVE_WINDOW_SIZE:
case AFD_SEND_WINDOW_SIZE: {
LONG newBytes;
PCLONG maxBytes;
CLONG requestedCount;
PCSHORT maxCount;
#ifdef AFDDBG_QUOTA
PVOID chargeBlock;
PSZ chargeType;
#endif
//
// 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 ) {
connection = endpoint->Common.VcConnecting.Connection;
if ( afdInfo->InformationType == AFD_SEND_WINDOW_SIZE ) {
maxBytes = &connection->MaxBufferredSendBytes;
maxCount = &connection->MaxBufferredSendCount;
} else {
maxBytes = &connection->MaxBufferredReceiveBytes;
maxCount = &connection->MaxBufferredReceiveCount;
}
#ifdef AFDDBG_QUOTA
chargeBlock = connection;
chargeType = "SetInfo vcnb";
#endif
} else if ( endpoint->Type == AfdBlockTypeDatagram ) {
if ( afdInfo->InformationType == AFD_SEND_WINDOW_SIZE ) {
maxBytes = &endpoint->Common.Datagram.MaxBufferredSendBytes;
maxCount = &endpoint->Common.Datagram.MaxBufferredSendCount;
} else {
maxBytes = &endpoint->Common.Datagram.MaxBufferredReceiveBytes;
maxCount = &endpoint->Common.Datagram.MaxBufferredReceiveCount;
}
#ifdef AFDDBG_QUOTA
chargeBlock = endpoint;
chargeType = "SetInfo dgrm";
#endif
} else {
return STATUS_INVALID_PARAMETER;
}
//
// Make sure that we always allow at least one message to be
// bufferred on an endpoint.
//
requestedCount = afdInfo->Information.Ulong / AfdBufferMultiplier;
if ( requestedCount == 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 = AfdBufferMultiplier;
requestedCount = 1;
}
}
//
// If the count will overflow the field we use to set the max
// count, just use the max count as the limit.
//
if ( requestedCount > 0x7FFF ) {
requestedCount = 0x7FFF;
}
//
// Charge or return quota to the process making this request.
//
newBytes = afdInfo->Information.Ulong - (ULONG)(*maxBytes);
if ( newBytes > 0 ) {
try {
PsChargePoolQuota(
endpoint->OwningProcess,
NonPagedPool,
newBytes
);
} except ( EXCEPTION_EXECUTE_HANDLER ) {
#if DBG
DbgPrint( "AfdSetInformation: PsChargePoolQuota failed.\n" );
#endif
return STATUS_QUOTA_EXCEEDED;
}
AfdRecordQuotaHistory(
endpoint->OwningProcess,
newBytes,
chargeType,
chargeBlock
);
AfdRecordPoolQuotaCharged( newBytes );
} else {
PsReturnPoolQuota(
endpoint->OwningProcess,
NonPagedPool,
-1 * newBytes
);
AfdRecordQuotaHistory(
endpoint->OwningProcess,
newBytes,
chargeType,
chargeBlock
);
AfdRecordPoolQuotaCharged( -1 * newBytes );
}
//
// Set up the new information in the AFD internal structure.
//
*maxBytes = (CLONG)afdInfo->Information.Ulong;
*maxCount = (CSHORT)requestedCount;
break;
}
default:
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
} // AfdSetInformation
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.
--*/
{
NTSTATUS status;
PTCP_REQUEST_SET_INFORMATION_EX setInfoEx;
PIO_STATUS_BLOCK ioStatusBlock;
HANDLE event;
PAGED_CODE( );
//
// Allocate space to hold the TDI set information buffers and the IO
// status block.
//
ioStatusBlock = AFD_ALLOCATE_POOL(
NonPagedPool,
sizeof(*ioStatusBlock) + sizeof(*setInfoEx) +
sizeof(TCPSocketOption),
AFD_INLINE_POOL_TAG
);
if ( ioStatusBlock == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the TDI information buffers.
//
setInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)(ioStatusBlock + 1);
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;
*(PULONG)setInfoEx->Buffer = (ULONG)InLine;
setInfoEx->BufferSize = sizeof(ULONG);
KeAttachProcess( AfdSystemProcess );
status = ZwCreateEvent(
&event,
EVENT_ALL_ACCESS,
NULL,
SynchronizationEvent,
FALSE
);
if ( !NT_SUCCESS(status) ) {
KeDetachProcess( );
AFD_FREE_POOL(
ioStatusBlock,
AFD_INLINE_POOL_TAG
);
return status;
}
//
// Make the actual TDI set information call.
//
status = ZwDeviceIoControlFile(
Connection->Handle,
event,
NULL,
NULL,
ioStatusBlock,
IOCTL_TCP_SET_INFORMATION_EX,
setInfoEx,
sizeof(*setInfoEx) + setInfoEx->BufferSize,
NULL,
0
);
if ( status == STATUS_PENDING ) {
status = ZwWaitForSingleObject( event, FALSE, NULL );
ASSERT( NT_SUCCESS(status) );
status = ioStatusBlock->Status;
}
ZwClose( event );
KeDetachProcess( );
AFD_FREE_POOL(
ioStatusBlock,
AFD_INLINE_POOL_TAG
);
//
// Since this option is only supported for TCP/IP, always return success.
//
return STATUS_SUCCESS;
} // AfdSetInLineMode
NTSTATUS
AfdGetContext (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
{
PAFD_ENDPOINT endpoint;
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Make sure that the output buffer is large enough to hold all the
// context information for this socket.
//
if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
endpoint->ContextLength ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// If there is no context, return nothing.
//
if ( endpoint->Context == NULL ) {
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
}
//
// Return the context information we have stored for this endpoint.
//
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
endpoint->Context,
endpoint->ContextLength
);
Irp->IoStatus.Information = endpoint->ContextLength;
return STATUS_SUCCESS;
} // AfdGetContext
NTSTATUS
AfdGetContextLength (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
{
PAFD_ENDPOINT endpoint;
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Make sure that the output buffer is large enough to hold the
// context buffer length.
//
if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(endpoint->ContextLength) ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the length of the context information we have stored for
// this endpoint.
//
*(PULONG)Irp->AssociatedIrp.SystemBuffer = endpoint->ContextLength;
Irp->IoStatus.Information = sizeof(endpoint->ContextLength);
return STATUS_SUCCESS;
} // AfdGetContextLength
NTSTATUS
AfdSetContext (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
{
PAFD_ENDPOINT endpoint;
ULONG newContextLength;
PAGED_CODE( );
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
newContextLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
//
// If there is no context buffer on the endpoint, or if the context
// buffer is too small, allocate a new context buffer from paged pool.
//
if ( endpoint->Context == NULL ||
endpoint->ContextLength < newContextLength ) {
PVOID newContext;
//
// Allocate a new context buffer.
//
try {
newContext = AFD_ALLOCATE_POOL_WITH_QUOTA(
PagedPool,
newContextLength,
AFD_CONTEXT_POOL_TAG
);
} except( EXCEPTION_EXECUTE_HANDLER ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
if ( newContext == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Free the old context buffer, if there was one.
//
if ( endpoint->Context != NULL ) {
AFD_FREE_POOL(
endpoint->Context,
AFD_CONTEXT_POOL_TAG
);
}
endpoint->Context = newContext;
}
//
// Store the passed-in context buffer.
//
endpoint->ContextLength = newContextLength;
RtlCopyMemory(
endpoint->Context,
Irp->AssociatedIrp.SystemBuffer,
newContextLength
);
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
} // 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(
NULL,
FileObject,
&parameters,
sizeof(parameters),
NULL,
0,
TDI_SET_EVENT_HANDLER
);
} // AfdSetEventHandler
NTSTATUS
AfdIssueDeviceControl (
IN HANDLE FileHandle OPTIONAL,
IN PFILE_OBJECT FileObject OPTIONAL,
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.
Note that while FileHandle and FileObject are both marked as optional,
in reality exactly one of these must be specified.
Arguments:
FileHandle - a TDI handle.
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;
PFILE_OBJECT fileObject;
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 );
if( FileHandle != NULL ) {
ASSERT( FileObject == NULL );
//
// Get the file object corresponding to the directory's handle.
// Referencing the file object every time is necessary because the
// IO completion routine dereferences it.
//
status = ObReferenceObjectByHandle(
FileHandle,
0L, // DesiredAccess
NULL, // ObjectType
KernelMode,
(PVOID *)&fileObject,
NULL
);
if ( !NT_SUCCESS(status) ) {
return status;
}
} else {
ASSERT( FileObject != NULL );
//
// Reference the passed in file object. This is necessary because
// the IO completion routine dereferences it.
//
ObReferenceObject( FileObject );
fileObject = FileObject;
}
//
// Set the file object event to a non-signaled state.
//
(VOID) KeResetEvent( &fileObject->Event );
//
// Attempt to allocate and initialize the I/O Request Packet (IRP)
// for this operation.
//
deviceObject = IoGetRelatedDeviceObject ( fileObject );
irp = IoAllocateIrp( (deviceObject)->StackSize, TRUE );
if ( irp == NULL ) {
ObDereferenceObject( fileObject );
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Fill in the service independent parameters in the IRP.
//
irp->Flags = (LONG)IRP_SYNCHRONOUS_API;
irp->RequestorMode = KernelMode;
irp->PendingReturned = FALSE;
irp->UserIosb = &ioStatusBlock;
irp->UserEvent = &event;
irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
irp->AssociatedIrp.SystemBuffer = NULL;
irp->UserBuffer = NULL;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = fileObject;
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
DEBUG ioStatusBlock.Status = STATUS_UNSUCCESSFUL;
DEBUG ioStatusBlock.Information = (ULONG)-1;
//
// If an MDL buffer was specified, get an MDL, map the buffer,
// and place the MDL pointer in the IRP.
//
if ( MdlBuffer != NULL ) {
mdl = IoAllocateMdl(
MdlBuffer,
MdlBufferLength,
FALSE,
FALSE,
irp
);
if ( mdl == NULL ) {
IoFreeIrp( irp );
ObDereferenceObject( fileObject );
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool( mdl );
} else {
irp->MdlAddress = NULL;
}
//
// Put the file object pointer in the stack location.
//
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->FileObject = fileObject;
irpSp->DeviceObject = deviceObject;
//
// Fill in the service-dependent parameters for the request.
//
ASSERT( IrpParametersLength <= sizeof(irpSp->Parameters) );
RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength );
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
irpSp->MinorFunction = MinorFunction;
//
// Set up a completion routine which we'll use to free the MDL
// allocated previously.
//
IoSetCompletionRoutine( irp, AfdRestartDeviceControl, NULL, TRUE, TRUE, TRUE );
//
// Queue the IRP to the thread and pass it to the driver.
//
IoEnqueueIrp( irp );
status = IoCallDriver( deviceObject, irp );
//
// If necessary, wait for the I/O to complete.
//
if ( status == STATUS_PENDING ) {
KeWaitForSingleObject( (PVOID)&event, UserRequest, KernelMode, FALSE, NULL );
}
//
// If the request was successfully queued, 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
)
{
//
// 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 PIRP Irp,
IN PIO_STACK_LOCATION IrpSp,
IN ULONG Code
)
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAFD_CONNECT_DATA_BUFFERS connectDataBuffers;
PAFD_CONNECT_DATA_INFO connectDataInfo;
KIRQL oldIrql;
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection );
//
// If there is a connection on this endpoint, use the data buffers
// on the connection. Otherwise, use the data buffers from the
// endpoint.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
if ( connection != NULL ) {
connectDataBuffers = connection->ConnectDataBuffers;
} else {
connectDataBuffers = endpoint->ConnectDataBuffers;
}
//
// If there are no connect data buffers on the endpoint, complete
// the IRP with no bytes.
//
if ( connectDataBuffers == NULL ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
}
//
// Determine what sort of data we're handling and where it should
// come from.
//
switch ( Code ) {
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(FALSE);
}
//
// If there is none of the requested data type, again complete
// the IRP with no bytes.
//
if ( connectDataInfo->Buffer == NULL ||
connectDataInfo->BufferLength == 0 ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
}
//
// If the output buffer is too small, fail.
//
if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
connectDataInfo->BufferLength ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Copy over the buffer and return the number of bytes copied.
//
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
connectDataInfo->Buffer,
connectDataInfo->BufferLength
);
Irp->IoStatus.Information = connectDataInfo->BufferLength;
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_SUCCESS;
} // AfdGetConnectData
NTSTATUS
AfdSetConnectData (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp,
IN ULONG Code
)
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAFD_CONNECT_DATA_BUFFERS connectDataBuffers;
PAFD_CONNECT_DATA_BUFFERS * connectDataBuffersTarget;
PAFD_CONNECT_DATA_INFO connectDataInfo;
KIRQL oldIrql;
ULONG bufferLength;
BOOLEAN size = FALSE;
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection );
//
// If there is a connection on this endpoint, use the data buffers
// on the connection. Otherwise, use the data buffers from the
// endpoint. Also, if there is no connect data buffer structure
// yet, allocate one.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
if( connection != NULL ) {
connectDataBuffersTarget = &connection->ConnectDataBuffers;
} else {
connectDataBuffersTarget = &endpoint->ConnectDataBuffers;
}
connectDataBuffers = *connectDataBuffersTarget;
if( connectDataBuffers == NULL ) {
try {
connectDataBuffers = AFD_ALLOCATE_POOL_WITH_QUOTA(
NonPagedPool,
sizeof(*connectDataBuffers),
AFD_CONNECT_DATA_POOL_TAG
);
} except( EXCEPTION_EXECUTE_HANDLER ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_INSUFFICIENT_RESOURCES;
}
if( connectDataBuffers == NULL ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(
connectDataBuffers,
sizeof(*connectDataBuffers)
);
*connectDataBuffersTarget = connectDataBuffers;
}
//
// 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->ConnectOutstanding ||
(endpoint->DisconnectMode & ~AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_INVALID_PARAMETER;
}
//
// Determine what sort of data we're handling and where it should
// go.
//
switch( Code ) {
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(FALSE);
}
//
// 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( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG) ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_INVALID_PARAMETER;
}
bufferLength = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
} else {
bufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
}
//
// 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 < bufferLength ) {
if( connectDataInfo->Buffer != NULL ) {
AFD_FREE_POOL(
connectDataInfo->Buffer,
AFD_CONNECT_DATA_POOL_TAG
);
}
connectDataInfo->Buffer = NULL;
connectDataInfo->BufferLength = 0;
try {
connectDataInfo->Buffer = AFD_ALLOCATE_POOL_WITH_QUOTA(
NonPagedPool,
bufferLength,
AFD_CONNECT_DATA_POOL_TAG
);
} except( EXCEPTION_EXECUTE_HANDLER ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_INSUFFICIENT_RESOURCES;
}
if ( connectDataInfo->Buffer == NULL ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// If this wasn't simply a "size" request, copy the data into the buffer.
//
if( !size ) {
RtlCopyMemory(
connectDataInfo->Buffer,
Irp->AssociatedIrp.SystemBuffer,
bufferLength
);
}
connectDataInfo->BufferLength = bufferLength;
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
} // 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 AfdSpinLock 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(FALSE);
}
//
// 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
PAFD_WORK_ITEM
AfdAllocateWorkItem(
VOID
)
{
PAFD_WORK_ITEM afdWorkItem;
afdWorkItem = ExAllocateFromNPagedLookasideList(
&AfdLookasideLists->WorkQueueList
);
ASSERT( afdWorkItem != NULL );
return afdWorkItem;
} // AfdAllocateWorkItem
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.
//
AfdAcquireSpinLock( &AfdWorkQueueSpinLock, &oldIrql );
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;
AfdReleaseSpinLock( &AfdWorkQueueSpinLock, oldIrql );
ExInitializeWorkItem( &AfdWorkQueueItem, AfdDoWork, NULL );
ExQueueWorkItem( &AfdWorkQueueItem, DelayedWorkQueue );
} else {
AfdReleaseSpinLock( &AfdWorkQueueSpinLock, oldIrql );
}
return;
} // AfdQueueWorkItem
VOID
AfdFreeWorkItem(
IN PAFD_WORK_ITEM AfdWorkItem
)
{
ExFreeToNPagedLookasideList(
&AfdLookasideLists->WorkQueueList,
AfdWorkItem
);
} // AfdFreeWorkItem
#if DBG
PVOID
NTAPI
AfdAllocateWorkItemPool(
IN POOL_TYPE PoolType,
IN ULONG NumberOfBytes,
IN ULONG Tag
)
{
ASSERT( Tag == AFD_WORK_ITEM_POOL_TAG );
return AFD_ALLOCATE_POOL(
PoolType,
NumberOfBytes,
Tag
);
}
VOID
NTAPI
AfdFreeWorkItemPool(
IN PVOID Block
)
{
AFD_FREE_POOL(
Block,
AFD_WORK_ITEM_POOL_TAG
);
}
#endif
VOID
AfdDoWork (
IN PVOID Context
)
{
PAFD_WORK_ITEM afdWorkItem;
PLIST_ENTRY listEntry;
KIRQL oldIrql;
PWORKER_THREAD_ROUTINE workerRoutine;
ASSERT( AfdWorkThreadRunning );
//
// Empty the queue of AFD work items.
//
AfdAcquireSpinLock( &AfdWorkQueueSpinLock, &oldIrql );
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.
//
AfdReleaseSpinLock( &AfdWorkQueueSpinLock, 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.
//
AfdAcquireSpinLock( &AfdWorkQueueSpinLock, &oldIrql );
}
//
// Remember that we're no longer servicing the list and release the
// spin lock.
//
AfdRecordAfdWorkerThread( NULL );
AfdRecordWorkerLeave();
AfdWorkThreadRunning = FALSE;
AfdReleaseSpinLock( &AfdWorkQueueSpinLock, oldIrql );
} // AfdDoWork
#if DBG
typedef struct _AFD_OUTSTANDING_IRP {
LIST_ENTRY OutstandingIrpListEntry;
PIRP OutstandingIrp;
PCHAR FileName;
ULONG LineNumber;
} AFD_OUTSTANDING_IRP, *PAFD_OUTSTANDING_IRP;
NTSTATUS
AfdIoCallDriverDebug (
IN PAFD_ENDPOINT Endpoint,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PCHAR FileName,
IN ULONG LineNumber
)
{
PAFD_OUTSTANDING_IRP outstandingIrp;
KIRQL oldIrql;
//
// Get an outstanding IRP structure to hold the IRP.
//
outstandingIrp = AFD_ALLOCATE_POOL(
NonPagedPool,
sizeof(AFD_OUTSTANDING_IRP),
AFD_DEBUG_POOL_TAG
);
if ( outstandingIrp == NULL ) {
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoSetNextIrpStackLocation( Irp );
IoCompleteRequest( Irp, AfdPriorityBoost );
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the structure and place it on the endpoint's list of
// outstanding IRPs.
//
outstandingIrp->OutstandingIrp = Irp;
outstandingIrp->FileName = FileName;
outstandingIrp->LineNumber = LineNumber;
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
InsertTailList(
&Endpoint->OutstandingIrpListHead,
&outstandingIrp->OutstandingIrpListEntry
);
Endpoint->OutstandingIrpCount++;
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
//
// Pass the IRP to the TDI provider.
//
return IoCallDriver( DeviceObject, Irp );
} // AfdIoCallDriverDebug
VOID
AfdCompleteOutstandingIrpDebug (
IN PAFD_ENDPOINT Endpoint,
IN PIRP Irp
)
{
PAFD_OUTSTANDING_IRP outstandingIrp;
KIRQL oldIrql;
PLIST_ENTRY listEntry;
//
// First find the IRP on the endpoint's list of outstanding IRPs.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
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( &AfdSpinLock, oldIrql );
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.
//
KdPrint(( "AfdCompleteOutstandingIrp: Irp %lx not found on endpoint %lx\n",
Irp, Endpoint ));
ASSERT( Endpoint->OutstandingIrpCount != 0 );
Endpoint->OutstandingIrpCount--;
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return;
} // AfdCompleteOutstandingIrpDebug
#else
NTSTATUS
AfdIoCallDriverFree (
IN PAFD_ENDPOINT Endpoint,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
//
// Increment the count of IRPs outstanding on the endpoint. This
// allows the cleanup code to abort the VC if there is outstanding
// IO when a cleanup occurs.
//
InterlockedIncrement(
&Endpoint->OutstandingIrpCount
);
//
// Pass the IRP to the TDI provider.
//
return IoCallDriver( DeviceObject, Irp );
} // AfdIoCallDriverFree
VOID
AfdCompleteOutstandingIrpFree (
IN PAFD_ENDPOINT Endpoint,
IN PIRP Irp
)
{
//
// Decrement the count of IRPs on the endpoint.
//
InterlockedDecrement(
&Endpoint->OutstandingIrpCount
);
return;
} // AfdCompleteOutstandingIrpFree
#endif
#if DBG || REFERENCE_DEBUG
VOID
AfdInitializeDebugData (
VOID
)
{
//
// Empty for now.
//
} // AfdInitializeDebugData
#endif
#if DBG
#undef ExAllocatePool
#undef ExFreePool
ULONG AfdTotalAllocations = 0;
ULONG AfdTotalFrees = 0;
LARGE_INTEGER AfdTotalBytesAllocated;
LARGE_INTEGER AfdTotalBytesFreed;
//
// N.B. This structure MUST be quadword aligned!
//
typedef struct _AFD_POOL_HEADER {
PCHAR FileName;
ULONG LineNumber;
ULONG Size;
ULONG InUse;
#if !FREE_POOL_WITH_TAG_SUPPORTED
LONG Tag;
LONG Dummy; // for proper alignment
#endif
} AFD_POOL_HEADER, *PAFD_POOL_HEADER;
PVOID
AfdAllocatePool (
IN POOL_TYPE PoolType,
IN ULONG NumberOfBytes,
IN ULONG Tag,
IN PCHAR FileName,
IN ULONG LineNumber,
IN BOOLEAN WithQuota
)
{
PAFD_POOL_HEADER header;
KIRQL oldIrql;
ASSERT( PoolType == NonPagedPool ||
PoolType == NonPagedPoolMustSucceed ||
PoolType == PagedPool );
if ( WithQuota ) {
try {
header = ExAllocatePoolWithQuotaTag(
PoolType,
NumberOfBytes + sizeof(*header),
Tag
);
} except( EXCEPTION_EXECUTE_HANDLER ) {
return NULL;
}
} else {
header = ExAllocatePoolWithTag(
PoolType,
NumberOfBytes + sizeof(*header),
Tag
);
}
if ( header == NULL ) {
return NULL;
}
header->FileName = FileName;
header->LineNumber = LineNumber;
header->Size = NumberOfBytes;
header->InUse = 1;
#if !FREE_POOL_WITH_TAG_SUPPORTED
header->Tag = (LONG)Tag;
#endif
InterlockedIncrement(
&AfdTotalAllocations
);
ExInterlockedAddLargeStatistic(
&AfdTotalBytesAllocated,
header->Size
);
return (PVOID)(header + 1);
} // AfdAllocatePool
VOID
AfdFreePool (
IN PVOID Pointer,
IN ULONG Tag
)
{
KIRQL oldIrql;
PAFD_POOL_HEADER header = (PAFD_POOL_HEADER)Pointer - 1;
InterlockedIncrement(
&AfdTotalFrees
);
ExInterlockedAddLargeStatistic(
&AfdTotalBytesFreed,
header->Size
);
header->InUse = 0;
#if !FREE_POOL_WITH_TAG_SUPPORTED
ASSERT( InterlockedExchange( &header->Tag, 0 ) == (LONG)Tag );
#endif
MyFreePoolWithTag(
(PVOID)header,
Tag
);
} // AfdFreePool
#ifdef AFDDBG_QUOTA
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
)
{
KIRQL oldIrql;
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;
} // 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.
--*/
{
PMDL partialMdl;
PVOID virtualAddress;
//
// Sanity check.
//
ASSERT( Mdl != NULL );
ASSERT( Offset > 0 );
//
// Scan past any fully completed MDLs.
//
while ( Offset > MmGetMdlByteCount( Mdl ) ) {
Offset -= MmGetMdlByteCount( Mdl );
ASSERT( Mdl->Next != NULL );
Mdl = Mdl->Next;
}
//
// 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 ) {
//
// Compute the virtual address for the new MDL.
//
virtualAddress = (PVOID)((PUCHAR)MmGetMdlVirtualAddress( Mdl ) + Offset);
//
// Allocate the partial MDL.
//
partialMdl = IoAllocateMdl(
virtualAddress,
MmGetMdlByteCount( Mdl ) - Offset,
FALSE, // SecondaryBuffer
FALSE, // ChargeQuota
NULL // Irp
);
if ( partialMdl != NULL ) {
//
// Map part of the existing MDL into the parital MDL.
//
IoBuildPartialMdl(
Mdl,
partialMdl,
virtualAddress,
MmGetMdlByteCount( Mdl ) - Offset
);
}
//
// Return the parital MDL.
//
Mdl = partialMdl;
}
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( BufferArray != NULL );
ASSERT( BufferCount > 0 );
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 ) {
//
// Probe the WSABUF array.
//
ProbeForRead(
BufferArray, // Address
BufferCount * sizeof(WSABUF), // Length
sizeof(ULONG) // Alignment
);
}
//
// Scan the array.
//
do {
bufferPointer = BufferArray->buf;
bufferLength = BufferArray->len;
if( bufferPointer != NULL &&
bufferLength > 0 ) {
//
// Update the total byte counter.
//
totalLength += bufferLength;
//
// 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;
//
// Advance to the next WSABUF structure.
//
BufferArray++;
} else {
//
// Cannot allocate new MDL, return appropriate error.
//
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
}
} while( --BufferCount );
//
// Ensure the MDL chain is NULL terminated.
//
ASSERT( *chainTarget == NULL );
} except( EXCEPTION_EXECUTE_HANDLER ) {
//
// Bad news. Snag the exception code.
//
status = GetExceptionCode();
//
// 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.
//
if( currentMdl != NULL ) {
IoFreeMdl( currentMdl );
}
}
//
// Return the total buffer count.
//
*TotalByteCount = totalLength;
return status;
} // AfdAllocateMdlChain
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
AfdCalcBufferArrayByteLengthRead(
IN LPWSABUF BufferArray,
IN ULONG BufferCount
)
/*++
Routine Description:
Calculates the total size (in bytes) of the buffers described by the
specified WSABUF array and probes those buffers for read access.
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;
KPROCESSOR_MODE previousMode;
PAGED_CODE( );
//
// Sanity check.
//
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
previousMode = ExGetPreviousMode();
if( previousMode != KernelMode ) {
//
// Probe the WSABUF array.
//
ProbeForRead(
BufferArray, // Address
BufferCount * sizeof(WSABUF), // Length
sizeof(ULONG) // Alignment
);
}
//
// Scan the array & sum the lengths.
//
totalLength.QuadPart = 0;
while( BufferCount-- ) {
if( previousMode != KernelMode ) {
ProbeForRead(
BufferArray->buf, // Address
BufferArray->len, // Length
sizeof(UCHAR) // Alignment
);
}
totalLength.QuadPart += (LONGLONG)BufferArray->len;
BufferArray++;
}
if( totalLength.HighPart == 0 &&
( totalLength.LowPart & 0x80000000 ) == 0 ) {
return totalLength.LowPart;
}
ExRaiseAccessViolation();
return (ULONG)-1L;
} // AfdCalcBufferArrayByteLengthRead
ULONG
AfdCalcBufferArrayByteLengthWrite(
IN LPWSABUF BufferArray,
IN ULONG BufferCount
)
/*++
Routine Description:
Calculates the total size (in bytes) of the buffers described by the
specified WSABUF array and probes those buffers for write access.
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;
KPROCESSOR_MODE previousMode;
PAGED_CODE( );
//
// Sanity check.
//
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
previousMode = ExGetPreviousMode();
if( previousMode != KernelMode ) {
//
// Probe the WSABUF array.
//
ProbeForRead(
BufferArray, // Address
BufferCount * sizeof(WSABUF), // Length
sizeof(ULONG) // Alignment
);
}
//
// Scan the array & sum the lengths.
//
totalLength.QuadPart = 0;
while( BufferCount-- ) {
if( previousMode != KernelMode ) {
ProbeForWrite(
BufferArray->buf, // Address
BufferArray->len, // Length
sizeof(UCHAR) // Alignment
);
}
totalLength.QuadPart += (LONGLONG)BufferArray->len;
BufferArray++;
}
if( totalLength.HighPart == 0 &&
( totalLength.LowPart & 0x80000000 ) == 0 ) {
return totalLength.LowPart;
}
ExRaiseAccessViolation();
return (ULONG)-1L;
} // AfdCalcBufferArrayByteLengthWrite
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 ) {
bytesToCopy = min( DestinationLength, BufferArray->len );
RtlCopyMemory(
Destination,
BufferArray->buf,
bytesToCopy
);
Destination = (PCHAR)Destination + bytesToCopy;
DestinationLength -= bytesToCopy;
BufferArray++;
}
//
// Return number of bytes copied.
//
return (PCHAR)Destination - (PCHAR)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;
PAGED_CODE( );
//
// Sanity check.
//
ASSERT( BufferArray != NULL );
ASSERT( BufferCount > 0 );
ASSERT( Source != NULL );
//
// 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 && Offset >= BufferArray->len ) {
Offset -= BufferArray->len;
BufferArray++;
BufferCount--;
}
//
// If there's a fragmented portion remaining, and we still
// have buffers left, then copy the fragment here to keep
// the loop below fast.
//
if( Offset > 0 && BufferCount > 0 ) {
ASSERT( Offset < BufferArray->len );
bytesToCopy = min( SourceLength, BufferArray->len - Offset );
RtlCopyMemory(
BufferArray->buf + Offset,
Source,
bytesToCopy
);
Source = (PCHAR)Source + bytesToCopy;
SourceLength -= bytesToCopy;
BufferArray++;
BufferCount--;
}
}
//
// Scan the array & copy from the linear buffer.
//
while( BufferCount-- && SourceLength > 0 ) {
bytesToCopy = min( SourceLength, BufferArray->len );
RtlCopyMemory(
BufferArray->buf,
Source,
bytesToCopy
);
Source = (PCHAR)Source + bytesToCopy;
SourceLength -= bytesToCopy;
BufferArray++;
}
//
// Return number of bytes copied.
//
return (PCHAR)Source - (PCHAR)sourceStart;
} // AfdCopyBufferToBufferArray
#ifdef NT351
NTSTATUS
AfdReferenceEventObjectByHandle(
IN HANDLE Handle,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object
)
/*++
Routine Description:
References an event object by handle, returning a pointer to the
object.
Arguments:
Handle - The event handle to reference.
AccessMode - The requestor mode (user or kernel).
Object - Receives a pointer to the event object.
Return Value:
NTSTATUS - Completion status.
--*/
{
NTSTATUS status;
PKEVENT eventObject;
PAGED_CODE( );
//
// Reference the event object, but pass a NULL ObjectType descriptor
// to ObReferenceObjectByHandle(). We must do this because the NT 3.51
// kernel does not export the ExEventObjectType descriptor.
//
status = ObReferenceObjectByHandle(
Handle, // Handle
0, // DesiredAccess
NULL, // ObjectType
AccessMode, // AccessMode
Object, // Object,
NULL // HandleInformation
);
if( NT_SUCCESS(status) ) {
eventObject = (PKEVENT)*Object;
//
// Since we had to pass in a NULL object type descriptor, OB
// couldn't validate the object type for us. In an attempt to
// ensure we don't just blindly use the resulting object, we'll
// make a few sanity checks here.
//
if( ( eventObject->Header.Type == NotificationEvent ||
eventObject->Header.Type == SynchronizationEvent ) &&
eventObject->Header.Size == sizeof(*eventObject ) &&
eventObject->Header.Inserted == 0 &&
(ULONG)eventObject->Header.SignalState <= 1 ) {
return STATUS_SUCCESS;
}
//
// Object type mismatch.
//
ObDereferenceObject( eventObject );
return STATUS_OBJECT_TYPE_MISMATCH;
}
//
// ObReferenceObjectByHandle() failure.
//
return status;
} // AfdReferenceEventObjectByHandle
NTSTATUS
AfdQueueUserApc (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Queues a user-mode APC.
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 APC was successfully queued.
--*/
{
PAFD_QUEUE_APC_INFO apcInfo;
PAFD_APC afdApc;
PETHREAD threadObject;
NTSTATUS status;
PAGED_CODE( );
//
// Set up local pointers.
//
apcInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Validate the parameters.
//
if( apcInfo == NULL ||
IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*apcInfo) ||
apcInfo->Thread == NULL ||
apcInfo->ApcRoutine == NULL ) {
return STATUS_INVALID_PARAMETER;
}
//
// Reference the target thread object.
//
status = ObReferenceObjectByHandle(
apcInfo->Thread, // Handle
0, // DesiredAccess
*(POBJECT_TYPE *)PsThreadType, // ObjectType
Irp->RequestorMode, // AccessMode
(PVOID *)&threadObject, // Object
NULL // HandleInformation
);
if ( !NT_SUCCESS(status) ) {
return status;
}
//
// Create the APC object.
//
afdApc = AFD_ALLOCATE_POOL(
NonPagedPool,
sizeof(*afdApc),
AFD_APC_POOL_TAG
);
if ( afdApc == NULL ) {
ObDereferenceObject(threadObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the APC object.
//
KeInitializeApc(
&afdApc->Apc, // Apc
&threadObject->Tcb, // Thread
CurrentApcEnvironment, // Environment
AfdSpecialApc, // KernelRoutine
AfdSpecialApcRundown, // RundownRoutine
(PKNORMAL_ROUTINE)apcInfo->ApcRoutine, // NormalRoutine
Irp->RequestorMode, // ProcessorMode
apcInfo->ApcContext // NormalContext
);
//
// Insert the APC into the thread's queue.
//
KeInsertQueueApc(
&afdApc->Apc, // Apc
apcInfo->SystemArgument1, // SystemArgument1
apcInfo->SystemArgument2, // SystemArgument2
0 // Increment
);
//
// Dereference the target thread. Note that the AFD_APC structure
// will be freed in either AfdSpecialApc (if the APC was successfully
// delivered to the target thread) or AfdSpecialApcRundown (if the
// target thread was destroyed before the APC could be delivered).
//
ObDereferenceObject(threadObject);
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
} // AfdQueueUserApc
VOID
AfdSpecialApc (
struct _KAPC *Apc,
PKNORMAL_ROUTINE *NormalRoutine,
PVOID *NormalContext,
PVOID *SystemArgument1,
PVOID *SystemArgument2
)
/*++
Routine Description:
This is the kernel apc routine.
The only real work needed here is to free the AFD_APC structure
allocated in AfdQueueUserApc().
Arguments:
Apc - pointer to apc object
NormalRoutine - Will be called when we return.
NormalContext - will be 1st argument to normal routine.
SystemArgument1 - Uninterpreted.
SystemArgument2 - Uninterpreted.
Return Value:
NONE.
--*/
{
PAFD_APC afdApc;
PAGED_CODE();
ASSERT( Apc != NULL );
//
// Grab a pointer to the APC's containing AFD_APC structure,
// then free it.
//
afdApc = CONTAINING_RECORD( Apc, AFD_APC, Apc );
AFD_FREE_POOL(
afdApc,
AFD_APC_POOL_TAG
);
} // AfdSpecialApc
VOID
AfdSpecialApcRundown (
struct _KAPC *Apc
)
/*++
Routine Description:
This routine is called to clear away apcs in the apc queue
of a thread that has been terminated.
The only real work needed here is to free the AFD_APC structure
allocated in AfdQueueUserApc().
Arguments:
Apc - pointer to apc object
Return Value:
NONE.
--*/
{
PAFD_APC afdApc;
PAGED_CODE();
ASSERT( Apc != NULL );
//
// Grab a pointer to the APC's containing AFD_APC structure,
// then free it.
//
afdApc = CONTAINING_RECORD( Apc, AFD_APC, Apc );
AFD_FREE_POOL(
afdApc,
AFD_APC_POOL_TAG
);
} // AfdSpecialApcRundown
#endif // NT351
#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
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;
PAGED_CODE();
//
// 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,
&AfdDefaultQos,
sizeof(QOS)
) ) {
AfdIndicatePollEvent(
endpoint,
qosInfo->GroupQos
? AFD_POLL_GROUP_QOS_BIT
: AFD_POLL_QOS_BIT,
STATUS_SUCCESS
);
}
//
// Complete the IRP.
//
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
} // AfdSetQos
NTSTATUS
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;
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,
&AfdDefaultQos,
sizeof(QOS)
);
//
// Complete the IRP.
//
Irp->IoStatus.Information = sizeof(*qosInfo);
return STATUS_SUCCESS;
} // AfdGetQos
NTSTATUS
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;
PAGED_CODE();
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Complete the IRP.
//
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
} // AfdNoOperation
NTSTATUS
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;
KIRQL oldIrql;
BOOLEAN result;
LONG groupId;
PAGED_CODE();
//
// 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) ) {
return STATUS_BUFFER_TOO_SMALL;
}
if( validateInfo->RemoteAddress.TAAddressCount != 1 ) {
return STATUS_INVALID_PARAMETER;
}
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
( sizeof(*validateInfo) -
sizeof(TRANSPORT_ADDRESS) +
validateInfo->RemoteAddress.Address[0].AddressLength ) ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// 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 ) ) {
return STATUS_INVALID_PARAMETER;
}
//
// 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;
return STATUS_SUCCESS;
}
//
// 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;
ExAcquireResourceShared( 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, &oldIrql );
if( compareEndpoint->Common.Datagram.RemoteAddress != NULL &&
compareEndpoint->Common.Datagram.RemoteAddressLength ==
requestAddressLength ) {
result = AfdCompareAddresses(
compareEndpoint->Common.Datagram.RemoteAddress,
compareEndpoint->Common.Datagram.RemoteAddressLength,
requestAddress,
requestAddressLength
);
}
AfdReleaseSpinLock( &compareEndpoint->SpinLock, oldIrql );
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, &oldIrql );
connection = AFD_CONNECTION_FROM_ENDPOINT( compareEndpoint );
if( compareEndpoint->State == AfdEndpointStateConnected &&
connection != NULL ) {
REFERENCE_CONNECTION( connection );
AfdReleaseSpinLock( &compareEndpoint->SpinLock, oldIrql );
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
if( connection->RemoteAddress != NULL &&
connection->RemoteAddressLength == requestAddressLength ) {
result = AfdCompareAddresses(
connection->RemoteAddress,
connection->RemoteAddressLength,
requestAddress,
requestAddressLength
);
}
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
DEREFERENCE_CONNECTION( connection );
if( !result ) {
break;
}
} else {
AfdReleaseSpinLock( &compareEndpoint->SpinLock, oldIrql );
}
}
}
ExReleaseResource( AfdResource );
AfdDereferenceGroup( validateInfo->GroupID );
if( !result ) {
return STATUS_INVALID_PARAMETER;
}
//
// Success!
//
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
} // 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;
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( ip1->in_addr == ip2->in_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( 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( 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( 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
PAFD_CONNECTION
AfdFindReturnedConnection(
IN PAFD_ENDPOINT Endpoint,
IN ULONG Sequence
)
/*++
Routine Description:
Scans the endpoints queue of returned connections looking for one
with the specified sequence number.
Arguments:
Endpoint - A pointer to the endpoint from which to get a connection.
Sequence - The sequence the connection must match. This is actually
a pointer to the connection.
Return Value:
AFD_CONNECTION - A pointer to an AFD connection block if successful,
NULL if not.
--*/
{
PAFD_CONNECTION connection;
PLIST_ENTRY listEntry;
ASSERT( Endpoint != NULL );
ASSERT( IS_AFD_ENDPOINT_TYPE( Endpoint ) );
//
// Walk the endpoint's list of returned connections until we reach
// the end or until we find one with a matching sequence.
//
for( listEntry = Endpoint->Common.VcListening.ReturnedConnectionListHead.Flink;
listEntry != &Endpoint->Common.VcListening.ReturnedConnectionListHead;
listEntry = listEntry->Flink ) {
connection = CONTAINING_RECORD(
listEntry,
AFD_CONNECTION,
ListEntry
);
if( Sequence == (ULONG)connection ) {
return connection;
}
}
return NULL;
} // AfdFindReturnedConnection
NTSTATUS
AfdGetUnacceptedConnectData (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
{
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAFD_CONNECT_DATA_BUFFERS connectDataBuffers;
PAFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo;
KIRQL oldIrql;
ULONG dataLength;
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
connectInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Validate the request.
//
if( endpoint->Type != AfdBlockTypeVcListening ||
IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(*connectInfo) ) {
return STATUS_INVALID_PARAMETER;
}
if( connectInfo->LengthOnly &&
IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(*connectInfo) ) {
return STATUS_INVALID_PARAMETER;
}
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
//
// Find the specified connection.
//
connection = AfdFindReturnedConnection(
endpoint,
connectInfo->Sequence
);
if( connection == NULL ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_INVALID_PARAMETER;
}
//
// 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( &AfdSpinLock, oldIrql );
Irp->IoStatus.Information = sizeof(*connectInfo);
return STATUS_SUCCESS;
}
//
// If there is no connect data, complete the IRP with no bytes.
//
if( dataLength == 0 ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
}
//
// If the output buffer is too small, fail.
//
if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < dataLength ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Copy over the buffer and return the number of bytes copied.
//
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
connectDataBuffers->ReceiveConnectData.Buffer,
dataLength
);
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
Irp->IoStatus.Information = dataLength;
return STATUS_SUCCESS;
} // AfdGetUnacceptedConnectData