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.
3378 lines
116 KiB
3378 lines
116 KiB
/*++
|
|
|
|
Copyright (c) 1989-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fastio.c
|
|
|
|
Abstract:
|
|
|
|
This module contains routines for handling fast ("turbo") IO
|
|
in AFD.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 12-Oct-1992
|
|
|
|
Revision History:
|
|
VadimE 14-Jan-1998 Restructurred the code.
|
|
|
|
--*/
|
|
|
|
#include "afdp.h"
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastConnectionReceive (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_RECV_INFO recvInfo,
|
|
IN ULONG recvLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdFastDatagramReceive (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_RECV_MESSAGE_INFO recvInfo,
|
|
IN ULONG recvLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdFastConnectionSend (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_SEND_INFO sendInfo,
|
|
IN ULONG sendLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdFastDatagramSend (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_SEND_DATAGRAM_INFO sendInfo,
|
|
IN ULONG sendLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartFastDatagramSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, AfdFastIoDeviceControl )
|
|
#pragma alloc_text( PAGE, AfdFastIoRead )
|
|
#pragma alloc_text( PAGE, AfdFastIoWrite )
|
|
#pragma alloc_text( PAGEAFD, AfdFastDatagramSend )
|
|
#pragma alloc_text( PAGEAFD, AfdFastDatagramReceive )
|
|
#pragma alloc_text( PAGEAFD, AfdFastConnectionSend )
|
|
#pragma alloc_text( PAGEAFD, AfdFastConnectionReceive )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartFastDatagramSend )
|
|
#pragma alloc_text( PAGEAFD, AfdShouldSendBlock )
|
|
#endif
|
|
|
|
|
|
#if AFD_PERF_DBG
|
|
BOOLEAN
|
|
AfdFastIoReadReal (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdFastIoRead (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
{
|
|
BOOLEAN success;
|
|
|
|
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
|
|
|
success = AfdFastIoReadReal (
|
|
FileObject,
|
|
FileOffset,
|
|
Length,
|
|
Wait,
|
|
LockKey,
|
|
Buffer,
|
|
IoStatus,
|
|
DeviceObject
|
|
);
|
|
|
|
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
|
if ( success ) {
|
|
InterlockedIncrement (&AfdFastReadsSucceeded);
|
|
ASSERT (IoStatus->Status == STATUS_SUCCESS ||
|
|
IoStatus->Status == STATUS_DEVICE_NOT_READY );
|
|
} else {
|
|
InterlockedIncrement (&AfdFastReadsFailed);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
BOOLEAN
|
|
AfdFastIoReadReal (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
|
|
#else // AFD_PERF_DBG
|
|
|
|
BOOLEAN
|
|
AfdFastIoRead (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
#endif // AFD_PERF_DBG
|
|
{
|
|
|
|
PAFD_ENDPOINT endpoint;
|
|
WSABUF buf;
|
|
|
|
UNREFERENCED_PARAMETER (FileOffset);
|
|
UNREFERENCED_PARAMETER (Wait);
|
|
UNREFERENCED_PARAMETER (LockKey);
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// All we want to do is pass the request through to the TDI provider
|
|
// if possible. If not, we want to bail out of this code path back
|
|
// onto the main code path (with IRPs) with as little performance
|
|
// overhead as possible.
|
|
//
|
|
// Thus this routine only does general preliminary checks and input
|
|
// parameter validation. If it is determined that fast io path is
|
|
// likely to succeed, an operation specific routine is called
|
|
// to handle all the details.
|
|
//
|
|
|
|
endpoint = FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
|
|
//
|
|
// If fast IO recv is disabled
|
|
// or the endpoint is shut down in any way
|
|
// or the endpoint isn't connected yet
|
|
// or the TDI provider for this endpoint supports bufferring,
|
|
// we do not want to do fast IO on it
|
|
//
|
|
if (endpoint->DisableFastIoRecv ||
|
|
endpoint->DisconnectMode != 0 ||
|
|
endpoint->State != AfdEndpointStateConnected ||
|
|
IS_TDI_BUFFERRING(endpoint)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Fake buffer array.
|
|
//
|
|
|
|
buf.buf = Buffer;
|
|
buf.len = Length;
|
|
|
|
//
|
|
// Call routine based on endpoint type
|
|
//
|
|
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
|
//
|
|
// Fake input parameter strucuture
|
|
//
|
|
AFD_RECV_MESSAGE_INFO msgInfo;
|
|
|
|
msgInfo.dgi.BufferArray = &buf;
|
|
msgInfo.dgi.BufferCount = 1;
|
|
msgInfo.dgi.AfdFlags = AFD_OVERLAPPED;
|
|
msgInfo.dgi.TdiFlags = TDI_RECEIVE_NORMAL;
|
|
msgInfo.dgi.Address = NULL;
|
|
msgInfo.dgi.AddressLength = 0;
|
|
msgInfo.ControlBuffer = NULL;
|
|
msgInfo.ControlLength = NULL;
|
|
msgInfo.MsgFlags = NULL;
|
|
|
|
return AfdFastDatagramReceive(
|
|
endpoint,
|
|
&msgInfo,
|
|
Length,
|
|
IoStatus
|
|
);
|
|
}
|
|
else if (IS_VC_ENDPOINT(endpoint)) {
|
|
//
|
|
// Fake input parameter strucuture
|
|
//
|
|
AFD_RECV_INFO recvInfo;
|
|
|
|
recvInfo.BufferArray = &buf;
|
|
recvInfo.BufferCount = 1;
|
|
recvInfo.AfdFlags = AFD_OVERLAPPED;
|
|
recvInfo.TdiFlags = TDI_RECEIVE_NORMAL;
|
|
|
|
return AfdFastConnectionReceive (
|
|
endpoint,
|
|
&recvInfo,
|
|
Length,
|
|
IoStatus);
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
} // AfdFastIoRead
|
|
|
|
#if AFD_PERF_DBG
|
|
BOOLEAN
|
|
AfdFastIoWriteReal (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdFastIoWrite (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
{
|
|
BOOLEAN success;
|
|
|
|
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
|
|
|
success = AfdFastIoWriteReal (
|
|
FileObject,
|
|
FileOffset,
|
|
Length,
|
|
Wait,
|
|
LockKey,
|
|
Buffer,
|
|
IoStatus,
|
|
DeviceObject
|
|
);
|
|
|
|
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
|
if ( success ) {
|
|
InterlockedIncrement (&AfdFastWritesSucceeded);
|
|
ASSERT (IoStatus->Status == STATUS_SUCCESS ||
|
|
IoStatus->Status == STATUS_DEVICE_NOT_READY);
|
|
} else {
|
|
InterlockedIncrement (&AfdFastWritesFailed);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
BOOLEAN
|
|
AfdFastIoWriteReal (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
IN PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
|
|
#else // AFD_PERF_DBG
|
|
|
|
BOOLEAN
|
|
AfdFastIoWrite (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
IN PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
#endif // AFD_PERF_DBG
|
|
{
|
|
|
|
|
|
PAFD_ENDPOINT endpoint;
|
|
WSABUF buf;
|
|
|
|
UNREFERENCED_PARAMETER (FileOffset);
|
|
UNREFERENCED_PARAMETER (Wait);
|
|
UNREFERENCED_PARAMETER (LockKey);
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// All we want to do is pass the request through to the TDI provider
|
|
// if possible. If not, we want to bail out of this code path back
|
|
// onto the main code path (with IRPs) with as little performance
|
|
// overhead as possible.
|
|
//
|
|
// Thus this routine only does general preliminary checks and input
|
|
// parameter validation. If it is determined that fast io path is
|
|
// likely to succeed, an operation specific routine is called
|
|
// to handle all the details.
|
|
//
|
|
|
|
endpoint = FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
|
|
//
|
|
// If fast IO send is disabled
|
|
// or the endpoint is shut down in any way
|
|
// or the endpoint isn't connected yet
|
|
// or the TDI provider for this endpoint supports bufferring,
|
|
// we do not want to do fast IO on it
|
|
//
|
|
if (endpoint->DisableFastIoSend ||
|
|
endpoint->DisconnectMode != 0 ||
|
|
endpoint->State != AfdEndpointStateConnected ||
|
|
IS_TDI_BUFFERRING(endpoint) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Fake buffer array.
|
|
//
|
|
buf.buf = Buffer;
|
|
buf.len = Length;
|
|
|
|
//
|
|
// Call routine based on endpoint type
|
|
//
|
|
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
|
//
|
|
// Fake input parameter strucuture
|
|
//
|
|
AFD_SEND_DATAGRAM_INFO sendInfo;
|
|
|
|
sendInfo.BufferArray = &buf;
|
|
sendInfo.BufferCount = 1;
|
|
sendInfo.AfdFlags = AFD_OVERLAPPED;
|
|
sendInfo.TdiConnInfo.RemoteAddress = NULL;
|
|
sendInfo.TdiConnInfo.RemoteAddressLength = 0;
|
|
|
|
return AfdFastDatagramSend(
|
|
endpoint,
|
|
&sendInfo,
|
|
Length,
|
|
IoStatus
|
|
);
|
|
}
|
|
else if (IS_VC_ENDPOINT (endpoint)) {
|
|
//
|
|
// Fake input parameter strucuture
|
|
//
|
|
AFD_SEND_INFO sendInfo;
|
|
|
|
sendInfo.BufferArray = &buf;
|
|
sendInfo.BufferCount = 1;
|
|
sendInfo.AfdFlags = AFD_OVERLAPPED;
|
|
sendInfo.TdiFlags = 0;
|
|
|
|
return AfdFastConnectionSend (
|
|
endpoint,
|
|
&sendInfo,
|
|
Length,
|
|
IoStatus);
|
|
}
|
|
else
|
|
return FALSE;
|
|
} // AfdFastIoWrite
|
|
|
|
#if AFD_PERF_DBG
|
|
|
|
BOOLEAN
|
|
AfdFastIoDeviceControlReal (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN BOOLEAN Wait,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
OUT PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength,
|
|
IN ULONG IoControlCode,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
);
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastIoDeviceControl (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN BOOLEAN Wait,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
OUT PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength,
|
|
IN ULONG IoControlCode,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
{
|
|
BOOLEAN success;
|
|
|
|
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
|
|
|
success = AfdFastIoDeviceControlReal (
|
|
FileObject,
|
|
Wait,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
IoControlCode,
|
|
IoStatus,
|
|
DeviceObject
|
|
);
|
|
|
|
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
|
|
|
switch ( IoControlCode ) {
|
|
|
|
case IOCTL_AFD_SEND:
|
|
|
|
if ( success ) {
|
|
InterlockedIncrement (&AfdFastSendsSucceeded);
|
|
} else {
|
|
InterlockedIncrement (&AfdFastSendsFailed);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_AFD_RECEIVE:
|
|
|
|
if ( success ) {
|
|
InterlockedIncrement (&AfdFastReceivesSucceeded);
|
|
} else {
|
|
InterlockedIncrement (&AfdFastReceivesFailed);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_AFD_SEND_DATAGRAM:
|
|
|
|
if ( success ) {
|
|
InterlockedIncrement (&AfdFastSendDatagramsSucceeded);
|
|
} else {
|
|
InterlockedIncrement (&AfdFastSendDatagramsFailed);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_AFD_RECEIVE_MESSAGE:
|
|
case IOCTL_AFD_RECEIVE_DATAGRAM:
|
|
|
|
if ( success ) {
|
|
InterlockedIncrement (&AfdFastReceiveDatagramsSucceeded);
|
|
} else {
|
|
InterlockedIncrement (&AfdFastReceiveDatagramsFailed);
|
|
}
|
|
break;
|
|
case IOCTL_AFD_TRANSMIT_FILE:
|
|
|
|
if ( success ) {
|
|
InterlockedIncrement (&AfdFastTfSucceeded);
|
|
} else {
|
|
InterlockedIncrement (&AfdFastTfFailed);
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
return success;
|
|
|
|
} // AfdFastIoDeviceControl
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastIoDeviceControlReal (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN BOOLEAN Wait,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
OUT PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength,
|
|
IN ULONG IoControlCode,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
#else
|
|
BOOLEAN
|
|
AfdFastIoDeviceControl (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN BOOLEAN Wait,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
OUT PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength,
|
|
IN ULONG IoControlCode,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN struct _DEVICE_OBJECT *DeviceObject
|
|
)
|
|
#endif
|
|
{
|
|
PAFD_ENDPOINT endpoint;
|
|
KPROCESSOR_MODE previousMode;
|
|
BOOLEAN res;
|
|
PAFD_IMMEDIATE_CALL proc;
|
|
ULONG request;
|
|
|
|
#ifdef _WIN64
|
|
WSABUF localArray[8];
|
|
LPWSABUF pArray = localArray;
|
|
#endif
|
|
|
|
UNREFERENCED_PARAMETER (Wait);
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// All we want to do is pass the request through to the TDI provider
|
|
// if possible. If not, we want to bail out of this code path back
|
|
// onto the main code path (with IRPs) with as little performance
|
|
// overhead as possible.
|
|
//
|
|
// Thus this routine only does general preliminary checks and input
|
|
// parameter validation. If it is determined that fast io path is
|
|
// likely to succeed, an operation specific routine is called
|
|
// to handle all the details.
|
|
//
|
|
|
|
//
|
|
// First get the endpoint pointer and previous mode for input parameter
|
|
// validation.
|
|
//
|
|
|
|
endpoint = FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
previousMode = ExGetPreviousMode ();
|
|
|
|
//
|
|
// Switch based on control code
|
|
//
|
|
switch (IoControlCode) {
|
|
case IOCTL_AFD_RECEIVE:
|
|
{
|
|
union {
|
|
AFD_RECV_INFO recvInfo;
|
|
AFD_RECV_MESSAGE_INFO msgInfo;
|
|
} u;
|
|
ULONG recvLength;
|
|
|
|
//
|
|
// Check the validity of the union above.
|
|
//
|
|
C_ASSERT (FIELD_OFFSET (AFD_RECV_MESSAGE_INFO, dgi.BufferArray)
|
|
== FIELD_OFFSET (AFD_RECV_INFO, BufferArray));
|
|
C_ASSERT (FIELD_OFFSET (AFD_RECV_MESSAGE_INFO, dgi.BufferCount)
|
|
== FIELD_OFFSET (AFD_RECV_INFO, BufferCount));
|
|
C_ASSERT (FIELD_OFFSET (AFD_RECV_MESSAGE_INFO, dgi.AfdFlags)
|
|
== FIELD_OFFSET (AFD_RECV_INFO, AfdFlags));
|
|
C_ASSERT (FIELD_OFFSET (AFD_RECV_MESSAGE_INFO, dgi.TdiFlags)
|
|
== FIELD_OFFSET (AFD_RECV_INFO, TdiFlags));
|
|
|
|
//
|
|
//
|
|
// If fast IO send is disabled
|
|
// or the endpoint is shut down in any way
|
|
// or the endpoint isn't connected yet
|
|
// or the TDI provider for this endpoint supports bufferring,
|
|
// we do not want to do fast IO on it
|
|
//
|
|
if (endpoint->DisableFastIoRecv ||
|
|
endpoint->DisconnectMode != 0 ||
|
|
endpoint->State != AfdEndpointStateConnected ||
|
|
IS_TDI_BUFFERRING(endpoint) ) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
try {
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (NULL)) {
|
|
PAFD_RECV_INFO32 recvInfo32;
|
|
LPWSABUF32 tempArray;
|
|
ULONG i;
|
|
|
|
|
|
//
|
|
// If the input structure isn't large enough, return error.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(*recvInfo32) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (*recvInfo32),
|
|
PROBE_ALIGNMENT32(AFD_RECV_INFO32));
|
|
}
|
|
|
|
recvInfo32 = InputBuffer;
|
|
|
|
|
|
//
|
|
// 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
|
|
//
|
|
tempArray = UlongToPtr(recvInfo32->BufferArray);
|
|
u.recvInfo.BufferCount = recvInfo32->BufferCount;
|
|
u.recvInfo.AfdFlags = recvInfo32->AfdFlags;
|
|
u.recvInfo.TdiFlags = recvInfo32->TdiFlags;
|
|
|
|
//
|
|
// If fast IO is not possible or this is not a normal receive.
|
|
// bail.
|
|
//
|
|
if( (u.recvInfo.AfdFlags & AFD_NO_FAST_IO) ||
|
|
u.recvInfo.TdiFlags != TDI_RECEIVE_NORMAL ) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate all the pointers that app gave to us and
|
|
// calculate the length of the send buffer.
|
|
// Buffers in the array will be validated in the
|
|
// process of copying
|
|
//
|
|
|
|
if ((tempArray == NULL) ||
|
|
(u.recvInfo.BufferCount == 0) ||
|
|
// Check for integer overflow (disabled by compiler)
|
|
(u.recvInfo.BufferCount>(MAXULONG/sizeof (WSABUF32))) ) {
|
|
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForRead(
|
|
tempArray, // Address
|
|
// Note check for overflow above (should actually be
|
|
// done here by the compiler generating code
|
|
// that causes exception on integer overflow)
|
|
u.recvInfo.BufferCount * sizeof (WSABUF32), // Length
|
|
PROBE_ALIGNMENT32(WSABUF32) // Alignment
|
|
);
|
|
}
|
|
|
|
if (u.recvInfo.BufferCount>sizeof(localArray)/sizeof(localArray[0])) {
|
|
try {
|
|
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
|
|
NonPagedPool,
|
|
sizeof (WSABUF)*u.recvInfo.BufferCount,
|
|
AFD_TEMPORARY_POOL_TAG);
|
|
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
|
|
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
|
|
ASSERT (pArray!=NULL);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
pArray = localArray;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<u.recvInfo.BufferCount; i++) {
|
|
pArray[i].buf = UlongToPtr(tempArray[i].buf);
|
|
pArray[i].len = tempArray[i].len;
|
|
}
|
|
|
|
u.recvInfo.BufferArray = pArray;
|
|
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
//
|
|
// If the input structure isn't large enough, return error.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(u.recvInfo) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (u.recvInfo),
|
|
PROBE_ALIGNMENT(AFD_RECV_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
|
|
//
|
|
|
|
u.recvInfo = *((PAFD_RECV_INFO)InputBuffer);
|
|
//
|
|
// If fast IO is not possible or this is not a normal receive.
|
|
// bail.
|
|
//
|
|
if( (u.recvInfo.AfdFlags & AFD_NO_FAST_IO) ||
|
|
u.recvInfo.TdiFlags != TDI_RECEIVE_NORMAL ) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate all the pointers that app gave to us and
|
|
// calculate the length of the send buffer.
|
|
// Buffers in the array will be validated in the
|
|
// process of copying
|
|
//
|
|
|
|
if ((u.recvInfo.BufferArray == NULL) ||
|
|
(u.recvInfo.BufferCount == 0) ||
|
|
// Check for integer overflow (disabled by compiler)
|
|
(u.recvInfo.BufferCount>(MAXULONG/sizeof (WSABUF))) ) {
|
|
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForRead(
|
|
u.recvInfo.BufferArray, // Address
|
|
// Note check for overflow above (should actually be
|
|
// done here by the compiler generating code
|
|
// that causes exception on integer overflow)
|
|
u.recvInfo.BufferCount * sizeof (WSABUF), // Length
|
|
PROBE_ALIGNMENT(WSABUF) // Alignment
|
|
);
|
|
}
|
|
}
|
|
|
|
recvLength = AfdCalcBufferArrayByteLength(
|
|
u.recvInfo.BufferArray,
|
|
u.recvInfo.BufferCount
|
|
);
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Call routine based on endpoint type
|
|
//
|
|
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
|
u.msgInfo.dgi.Address = NULL;
|
|
u.msgInfo.dgi.AddressLength = 0;
|
|
u.msgInfo.ControlBuffer = NULL;
|
|
u.msgInfo.ControlLength = NULL;
|
|
u.msgInfo.MsgFlags = NULL;
|
|
|
|
|
|
res = AfdFastDatagramReceive(
|
|
endpoint,
|
|
&u.msgInfo,
|
|
recvLength,
|
|
IoStatus
|
|
);
|
|
}
|
|
else if (IS_VC_ENDPOINT (endpoint)) {
|
|
res = AfdFastConnectionReceive (
|
|
endpoint,
|
|
&u.recvInfo,
|
|
recvLength,
|
|
IoStatus);
|
|
}
|
|
else
|
|
res = FALSE;
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_AFD_RECEIVE_DATAGRAM:
|
|
case IOCTL_AFD_RECEIVE_MESSAGE:
|
|
{
|
|
AFD_RECV_MESSAGE_INFO msgInfo;
|
|
ULONG recvLength;
|
|
|
|
if (endpoint->DisableFastIoRecv ||
|
|
!IS_DGRAM_ENDPOINT(endpoint) ||
|
|
((endpoint->State != AfdEndpointStateBound ) &&
|
|
(endpoint->State != AfdEndpointStateConnected)) ) {
|
|
return FALSE;
|
|
}
|
|
try {
|
|
if (IoControlCode==IOCTL_AFD_RECEIVE_MESSAGE) {
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (NULL)) {
|
|
PAFD_RECV_MESSAGE_INFO32 msgInfo32;
|
|
//
|
|
// If the input structure isn't large enough, return error.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(*msgInfo32) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (*msgInfo32),
|
|
PROBE_ALIGNMENT32 (AFD_RECV_MESSAGE_INFO32));
|
|
}
|
|
|
|
msgInfo32 = InputBuffer;
|
|
|
|
|
|
//
|
|
// 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
|
|
//
|
|
msgInfo.ControlBuffer = UlongToPtr(msgInfo32->ControlBuffer);
|
|
msgInfo.ControlLength = UlongToPtr(msgInfo32->ControlLength);
|
|
msgInfo.MsgFlags = UlongToPtr(msgInfo32->MsgFlags);
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
|
|
if( InputBufferLength < sizeof(msgInfo) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Capture the input structure.
|
|
//
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (msgInfo),
|
|
PROBE_ALIGNMENT (AFD_RECV_MESSAGE_INFO));
|
|
}
|
|
msgInfo = *(PAFD_RECV_MESSAGE_INFO)InputBuffer;
|
|
}
|
|
if (previousMode != KernelMode ) {
|
|
|
|
ProbeForWriteUlong (msgInfo.MsgFlags);
|
|
ProbeForWriteUlong (msgInfo.ControlLength);
|
|
//
|
|
// Checking of recvInfo->Address is postponed till
|
|
// we know the length of the address.
|
|
//
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
msgInfo.ControlBuffer = NULL;
|
|
msgInfo.ControlLength = NULL;
|
|
msgInfo.MsgFlags = NULL;
|
|
}
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (NULL)) {
|
|
PAFD_RECV_DATAGRAM_INFO32 recvInfo32;
|
|
LPWSABUF32 tempArray;
|
|
ULONG i;
|
|
|
|
|
|
//
|
|
// If the input structure isn't large enough, return error.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(*recvInfo32) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (*recvInfo32),
|
|
PROBE_ALIGNMENT32 (AFD_RECV_DATAGRAM_INFO32));
|
|
}
|
|
|
|
recvInfo32 = InputBuffer;
|
|
|
|
|
|
//
|
|
// 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
|
|
//
|
|
tempArray = UlongToPtr(recvInfo32->BufferArray);
|
|
msgInfo.dgi.BufferCount = recvInfo32->BufferCount;
|
|
msgInfo.dgi.AfdFlags = recvInfo32->AfdFlags;
|
|
msgInfo.dgi.TdiFlags = recvInfo32->TdiFlags;
|
|
msgInfo.dgi.Address = UlongToPtr(recvInfo32->Address);
|
|
msgInfo.dgi.AddressLength = UlongToPtr(recvInfo32->AddressLength);
|
|
|
|
//
|
|
// If fast IO is not possible or this is not a normal receive.
|
|
// bail.
|
|
//
|
|
if( (msgInfo.dgi.AfdFlags & AFD_NO_FAST_IO) != 0 ||
|
|
msgInfo.dgi.TdiFlags != TDI_RECEIVE_NORMAL ||
|
|
( (msgInfo.dgi.Address == NULL) ^
|
|
(msgInfo.dgi.AddressLength == NULL) ) ) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate all the pointers that app gave to us and
|
|
// calculate the length of the send buffer.
|
|
// Buffers in the array will be validated in the
|
|
// process of copying
|
|
//
|
|
|
|
if ((tempArray == NULL) ||
|
|
(msgInfo.dgi.BufferCount == 0) ||
|
|
// Check for integer overflow (disabled by compiler)
|
|
(msgInfo.dgi.BufferCount>(MAXULONG/sizeof (WSABUF32))) ) {
|
|
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForRead(
|
|
tempArray, // Address
|
|
// Note check for overflow above (should actually be
|
|
// done here by the compiler generating code
|
|
// that causes exception on integer overflow)
|
|
msgInfo.dgi.BufferCount * sizeof (WSABUF32), // Length
|
|
PROBE_ALIGNMENT (WSABUF32) // Alignment
|
|
);
|
|
}
|
|
|
|
if (msgInfo.dgi.BufferCount>sizeof(localArray)/sizeof(localArray[0])) {
|
|
try {
|
|
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
|
|
NonPagedPool,
|
|
sizeof (WSABUF)*msgInfo.dgi.BufferCount,
|
|
AFD_TEMPORARY_POOL_TAG);
|
|
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
|
|
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
|
|
ASSERT (pArray!=NULL);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
pArray = localArray;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<msgInfo.dgi.BufferCount; i++) {
|
|
pArray[i].buf = UlongToPtr(tempArray[i].buf);
|
|
pArray[i].len = tempArray[i].len;
|
|
}
|
|
|
|
msgInfo.dgi.BufferArray = pArray;
|
|
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
|
|
//
|
|
// If the input structure isn't large enough, return error.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(AFD_RECV_DATAGRAM_INFO) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Capture the input structure.
|
|
//
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (AFD_RECV_DATAGRAM_INFO),
|
|
PROBE_ALIGNMENT (AFD_RECV_DATAGRAM_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
|
|
//
|
|
|
|
msgInfo.dgi = *(PAFD_RECV_DATAGRAM_INFO)InputBuffer;
|
|
|
|
//
|
|
// If fast IO is disabled or this is not a simple
|
|
// recv, fail
|
|
//
|
|
|
|
if( (msgInfo.dgi.AfdFlags & AFD_NO_FAST_IO) != 0 ||
|
|
msgInfo.dgi.TdiFlags != TDI_RECEIVE_NORMAL ||
|
|
( (msgInfo.dgi.Address == NULL) ^
|
|
(msgInfo.dgi.AddressLength == NULL) ) ) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate all the pointers that app gave to us.
|
|
// and calculate total recv length.
|
|
// Buffers in the array will be validated in the
|
|
// process of copying
|
|
//
|
|
|
|
if ((msgInfo.dgi.BufferArray == NULL) ||
|
|
(msgInfo.dgi.BufferCount == 0) ||
|
|
// Check for integer overflow (disabled by compiler)
|
|
(msgInfo.dgi.BufferCount>(MAXULONG/sizeof (WSABUF))) ) {
|
|
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForRead(
|
|
msgInfo.dgi.BufferArray, // Address
|
|
// Note check for overflow above (should actually be
|
|
// done here by the compiler generating code
|
|
// that causes exception on integer overflow)
|
|
msgInfo.dgi.BufferCount * sizeof (WSABUF), // Length
|
|
PROBE_ALIGNMENT(WSABUF) // Alignment
|
|
);
|
|
}
|
|
}
|
|
|
|
recvLength = AfdCalcBufferArrayByteLength(
|
|
msgInfo.dgi.BufferArray,
|
|
msgInfo.dgi.BufferCount
|
|
);
|
|
|
|
if (previousMode != KernelMode ) {
|
|
if (msgInfo.dgi.AddressLength!=NULL) {
|
|
ProbeForWriteUlong (msgInfo.dgi.AddressLength);
|
|
}
|
|
//
|
|
// Checking of recvInfo->Address is postponed till
|
|
// we know the length of the address.
|
|
//
|
|
|
|
}
|
|
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
res = FALSE;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Attempt to perform fast IO on the endpoint.
|
|
//
|
|
|
|
res = AfdFastDatagramReceive(
|
|
endpoint,
|
|
&msgInfo,
|
|
recvLength,
|
|
IoStatus
|
|
);
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_AFD_SEND:
|
|
{
|
|
union {
|
|
AFD_SEND_INFO sendInfo;
|
|
AFD_SEND_DATAGRAM_INFO sendInfoDg;
|
|
} u;
|
|
ULONG sendLength;
|
|
|
|
//
|
|
// Check the validity of the union above.
|
|
//
|
|
C_ASSERT (FIELD_OFFSET (AFD_SEND_DATAGRAM_INFO, BufferArray)
|
|
== FIELD_OFFSET (AFD_SEND_INFO, BufferArray));
|
|
C_ASSERT (FIELD_OFFSET (AFD_SEND_DATAGRAM_INFO, BufferCount)
|
|
== FIELD_OFFSET (AFD_SEND_INFO, BufferCount));
|
|
C_ASSERT (FIELD_OFFSET (AFD_SEND_DATAGRAM_INFO, AfdFlags)
|
|
== FIELD_OFFSET (AFD_SEND_INFO, AfdFlags));
|
|
|
|
//
|
|
// If fast IO send is disabled
|
|
// or the endpoint is shut down in any way
|
|
// or the endpoint isn't connected yet
|
|
// or the TDI provider for this endpoint supports bufferring,
|
|
// we do not want to do fast IO on it
|
|
//
|
|
if (endpoint->DisableFastIoSend ||
|
|
endpoint->DisconnectMode != 0 ||
|
|
endpoint->State != AfdEndpointStateConnected ||
|
|
IS_TDI_BUFFERRING(endpoint) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (NULL)) {
|
|
PAFD_SEND_INFO32 sendInfo32;
|
|
LPWSABUF32 tempArray;
|
|
ULONG i;
|
|
|
|
|
|
//
|
|
// If the input structure isn't large enough, return error.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(*sendInfo32) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (*sendInfo32),
|
|
PROBE_ALIGNMENT32 (AFD_SEND_INFO32));
|
|
}
|
|
|
|
sendInfo32 = InputBuffer;
|
|
|
|
|
|
//
|
|
// 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
|
|
//
|
|
tempArray = UlongToPtr(sendInfo32->BufferArray);
|
|
u.sendInfo.BufferCount = sendInfo32->BufferCount;
|
|
u.sendInfo.AfdFlags = sendInfo32->AfdFlags;
|
|
u.sendInfo.TdiFlags = sendInfo32->TdiFlags;
|
|
|
|
//
|
|
// If fast IO is not possible or this is not a normal receive.
|
|
// bail.
|
|
//
|
|
if( (u.sendInfo.AfdFlags & AFD_NO_FAST_IO) ||
|
|
u.sendInfo.TdiFlags != 0 ) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate all the pointers that app gave to us and
|
|
// calculate the length of the send buffer.
|
|
// Buffers in the array will be validated in the
|
|
// process of copying
|
|
//
|
|
|
|
if ((tempArray == NULL) ||
|
|
(u.sendInfo.BufferCount == 0) ||
|
|
// Check for integer overflow (disabled by compiler)
|
|
(u.sendInfo.BufferCount>(MAXULONG/sizeof (WSABUF32))) ) {
|
|
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForRead(
|
|
tempArray, // Address
|
|
// Note check for overflow above (should actually be
|
|
// done here by the compiler generating code
|
|
// that causes exception on integer overflow)
|
|
u.sendInfo.BufferCount * sizeof (WSABUF32), // Length
|
|
PROBE_ALIGNMENT32(WSABUF32) // Alignment
|
|
);
|
|
}
|
|
|
|
if (u.sendInfo.BufferCount>sizeof(localArray)/sizeof(localArray[0])) {
|
|
try {
|
|
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
|
|
NonPagedPool,
|
|
sizeof (WSABUF)*u.sendInfo.BufferCount,
|
|
AFD_TEMPORARY_POOL_TAG);
|
|
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
|
|
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
|
|
ASSERT (pArray!=NULL);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
pArray = localArray;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<u.sendInfo.BufferCount; i++) {
|
|
pArray[i].buf = UlongToPtr(tempArray[i].buf);
|
|
pArray[i].len = tempArray[i].len;
|
|
}
|
|
|
|
u.sendInfo.BufferArray = pArray;
|
|
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
//
|
|
// If the input structure isn't large enough, return error.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(u.sendInfo) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (u.sendInfo),
|
|
PROBE_ALIGNMENT(AFD_SEND_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
|
|
//
|
|
u.sendInfo = *((PAFD_SEND_INFO)InputBuffer);
|
|
|
|
if( (u.sendInfo.AfdFlags & AFD_NO_FAST_IO) != 0 ||
|
|
u.sendInfo.TdiFlags != 0 ) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
if ((u.sendInfo.BufferArray == NULL) ||
|
|
(u.sendInfo.BufferCount == 0) ||
|
|
// Check for integer overflow (disabled by compiler)
|
|
(u.sendInfo.BufferCount>(MAXULONG/sizeof (WSABUF))) ) {
|
|
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForRead(
|
|
u.sendInfo.BufferArray, // Address
|
|
// Note check for overflow above (should actually be
|
|
// done here by the compiler generating code
|
|
// that causes exception on integer overflow)
|
|
u.sendInfo.BufferCount * sizeof (WSABUF), // Length
|
|
PROBE_ALIGNMENT(WSABUF) // Alignment
|
|
);
|
|
}
|
|
|
|
}
|
|
sendLength = AfdCalcBufferArrayByteLength(
|
|
u.sendInfo.BufferArray,
|
|
u.sendInfo.BufferCount
|
|
);
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (IS_DGRAM_ENDPOINT (endpoint)) {
|
|
u.sendInfoDg.TdiConnInfo.RemoteAddress = NULL;
|
|
u.sendInfoDg.TdiConnInfo.RemoteAddressLength = 0;
|
|
res = AfdFastDatagramSend (
|
|
endpoint,
|
|
&u.sendInfoDg,
|
|
sendLength,
|
|
IoStatus);
|
|
}
|
|
else if (IS_VC_ENDPOINT (endpoint)) {
|
|
res = AfdFastConnectionSend (
|
|
endpoint,
|
|
&u.sendInfo,
|
|
sendLength,
|
|
IoStatus);
|
|
}
|
|
else
|
|
res = FALSE;
|
|
}
|
|
|
|
break;
|
|
case IOCTL_AFD_SEND_DATAGRAM:
|
|
{
|
|
AFD_SEND_DATAGRAM_INFO sendInfo;
|
|
ULONG sendLength;
|
|
|
|
|
|
if (endpoint->DisableFastIoSend ||
|
|
!IS_DGRAM_ENDPOINT(endpoint) ||
|
|
((endpoint->State != AfdEndpointStateBound ) &&
|
|
(endpoint->State != AfdEndpointStateConnected)) ) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
try {
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (NULL)) {
|
|
PAFD_SEND_DATAGRAM_INFO32 sendInfo32;
|
|
LPWSABUF32 tempArray;
|
|
ULONG i;
|
|
|
|
|
|
//
|
|
// If the input structure isn't large enough, return error.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(*sendInfo32) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (*sendInfo32),
|
|
PROBE_ALIGNMENT32(AFD_SEND_DATAGRAM_INFO32));
|
|
}
|
|
|
|
sendInfo32 = InputBuffer;
|
|
|
|
|
|
//
|
|
// 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
|
|
//
|
|
tempArray = UlongToPtr(sendInfo32->BufferArray);
|
|
sendInfo.BufferCount = sendInfo32->BufferCount;
|
|
sendInfo.AfdFlags = sendInfo32->AfdFlags;
|
|
sendInfo.TdiConnInfo.RemoteAddress = UlongToPtr(sendInfo32->TdiConnInfo.RemoteAddress);
|
|
sendInfo.TdiConnInfo.RemoteAddressLength = sendInfo32->TdiConnInfo.RemoteAddressLength;
|
|
|
|
//
|
|
// If fast IO is not possible bail.
|
|
//
|
|
if(sendInfo.AfdFlags & AFD_NO_FAST_IO) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate all the pointers that app gave to us and
|
|
// calculate the length of the send buffer.
|
|
// Buffers in the array will be validated in the
|
|
// process of copying
|
|
//
|
|
|
|
if ((tempArray == NULL) ||
|
|
(sendInfo.BufferCount == 0) ||
|
|
// Check for integer overflow (disabled by compiler)
|
|
(sendInfo.BufferCount>(MAXULONG/sizeof (WSABUF32))) ) {
|
|
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForRead(
|
|
tempArray, // Address
|
|
// Note check for overflow above (should actually be
|
|
// done here by the compiler generating code
|
|
// that causes exception on integer overflow)
|
|
sendInfo.BufferCount * sizeof (WSABUF32), // Length
|
|
PROBE_ALIGNMENT32(WSABUF32) // Alignment
|
|
);
|
|
}
|
|
|
|
if (sendInfo.BufferCount>sizeof(localArray)/sizeof(localArray[0])) {
|
|
try {
|
|
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
|
|
NonPagedPool,
|
|
sizeof (WSABUF)*sendInfo.BufferCount,
|
|
AFD_TEMPORARY_POOL_TAG);
|
|
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
|
|
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
|
|
ASSERT (pArray!=NULL);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
pArray = localArray;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<sendInfo.BufferCount; i++) {
|
|
pArray[i].buf = UlongToPtr(tempArray[i].buf);
|
|
pArray[i].len = tempArray[i].len;
|
|
}
|
|
|
|
sendInfo.BufferArray = pArray;
|
|
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
//
|
|
// If the input structure isn't large enough, bail on fast IO.
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(sendInfo) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (sendInfo),
|
|
PROBE_ALIGNMENT(AFD_SEND_DATAGRAM_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
|
|
//
|
|
|
|
sendInfo = *((PAFD_SEND_DATAGRAM_INFO)InputBuffer);
|
|
//
|
|
// If fast IO is disabled, bail
|
|
//
|
|
|
|
if( (sendInfo.AfdFlags & AFD_NO_FAST_IO) != 0) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate all the pointers that app gave to us
|
|
// and calculate total send length
|
|
// Buffers in the array will be validated in the
|
|
// process of copying
|
|
//
|
|
|
|
if ((sendInfo.BufferArray == NULL) ||
|
|
(sendInfo.BufferCount == 0) ||
|
|
// Check for integer overflow (disabled by compiler)
|
|
(sendInfo.BufferCount>(MAXULONG/sizeof (WSABUF))) ) {
|
|
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForRead(
|
|
sendInfo.BufferArray, // Address
|
|
// Note check for overflow above (should actually be
|
|
// done here by the compiler generating code
|
|
// that causes exception on integer overflow)
|
|
sendInfo.BufferCount * sizeof (WSABUF), // Length
|
|
PROBE_ALIGNMENT(WSABUF) // Alignment
|
|
);
|
|
}
|
|
}
|
|
|
|
sendLength = AfdCalcBufferArrayByteLength(
|
|
sendInfo.BufferArray,
|
|
sendInfo.BufferCount
|
|
);
|
|
|
|
if (previousMode != KernelMode ) {
|
|
ProbeForRead (
|
|
sendInfo.TdiConnInfo.RemoteAddress, // Address
|
|
sendInfo.TdiConnInfo.RemoteAddressLength, // Length,
|
|
sizeof (UCHAR) // Aligment
|
|
);
|
|
}
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
//
|
|
// Attempt to perform fast IO on the endpoint.
|
|
//
|
|
|
|
res = AfdFastDatagramSend(
|
|
endpoint,
|
|
&sendInfo,
|
|
sendLength,
|
|
IoStatus
|
|
);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_AFD_TRANSMIT_FILE:
|
|
{
|
|
|
|
AFD_TRANSMIT_FILE_INFO userTransmitInfo;
|
|
try {
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (NULL)) {
|
|
PAFD_TRANSMIT_FILE_INFO32 userTransmitInfo32;
|
|
if ( InputBufferLength < sizeof(AFD_TRANSMIT_FILE_INFO32) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (*userTransmitInfo32),
|
|
PROBE_ALIGNMENT32(AFD_TRANSMIT_FILE_INFO32));
|
|
}
|
|
|
|
userTransmitInfo32 = InputBuffer;
|
|
userTransmitInfo.Offset = userTransmitInfo32->Offset;
|
|
userTransmitInfo.WriteLength = userTransmitInfo32->WriteLength;
|
|
userTransmitInfo.SendPacketLength = userTransmitInfo32->SendPacketLength;
|
|
userTransmitInfo.FileHandle = userTransmitInfo32->FileHandle;
|
|
userTransmitInfo.Head = UlongToPtr(userTransmitInfo32->Head);
|
|
userTransmitInfo.HeadLength = userTransmitInfo32->HeadLength;
|
|
userTransmitInfo.Tail = UlongToPtr(userTransmitInfo32->Tail);
|
|
userTransmitInfo.TailLength = userTransmitInfo32->TailLength;
|
|
userTransmitInfo.Flags = userTransmitInfo32->Flags;
|
|
|
|
|
|
if (previousMode != KernelMode) {
|
|
if (userTransmitInfo.HeadLength>0)
|
|
ProbeForRead (userTransmitInfo.Head,
|
|
userTransmitInfo.HeadLength,
|
|
sizeof (UCHAR));
|
|
if (userTransmitInfo.TailLength>0)
|
|
ProbeForRead (userTransmitInfo.Tail,
|
|
userTransmitInfo.TailLength,
|
|
sizeof (UCHAR));
|
|
}
|
|
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
if ( InputBufferLength < sizeof(AFD_TRANSMIT_FILE_INFO) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (previousMode != KernelMode) {
|
|
ProbeForReadSmallStructure (InputBuffer,
|
|
sizeof (userTransmitInfo),
|
|
PROBE_ALIGNMENT(AFD_TRANSMIT_FILE_INFO));
|
|
userTransmitInfo = *((PAFD_TRANSMIT_FILE_INFO)InputBuffer);
|
|
if (userTransmitInfo.HeadLength>0)
|
|
ProbeForRead (userTransmitInfo.Head,
|
|
userTransmitInfo.HeadLength,
|
|
sizeof (UCHAR));
|
|
if (userTransmitInfo.TailLength>0)
|
|
ProbeForRead (userTransmitInfo.Tail,
|
|
userTransmitInfo.TailLength,
|
|
sizeof (UCHAR));
|
|
}
|
|
else {
|
|
userTransmitInfo = *((PAFD_TRANSMIT_FILE_INFO)InputBuffer);
|
|
}
|
|
}
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
res = AfdFastTransmitFile (endpoint,
|
|
&userTransmitInfo,
|
|
IoStatus);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
default:
|
|
request = _AFD_REQUEST(IoControlCode);
|
|
if( request < AFD_NUM_IOCTLS &&
|
|
AfdIoctlTable[request] == IoControlCode &&
|
|
AfdImmediateCallDispatch[request]!=NULL) {
|
|
|
|
proc = AfdImmediateCallDispatch[request];
|
|
IoStatus->Status = (*proc) (
|
|
FileObject,
|
|
IoControlCode,
|
|
previousMode,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
&IoStatus->Information
|
|
);
|
|
|
|
ASSERT (IoStatus->Status!=STATUS_PENDING);
|
|
res = TRUE;
|
|
}
|
|
else {
|
|
res = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
|
|
if (pArray!=localArray) {
|
|
AFD_FREE_POOL (pArray, AFD_TEMPORARY_POOL_TAG);
|
|
}
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastConnectionSend (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_SEND_INFO sendInfo,
|
|
IN ULONG sendLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
{
|
|
PAFD_BUFFER afdBuffer;
|
|
PAFD_CONNECTION connection;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
NTSTATUS status;
|
|
|
|
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
|
endpoint->Type == AfdBlockTypeVcBoth );
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
connection = AFD_CONNECTION_FROM_ENDPOINT (endpoint);
|
|
|
|
if (connection==NULL) {
|
|
//
|
|
// connection might have been cleaned up by transmit file.
|
|
//
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
//
|
|
// If the connection has been aborted, then we don't want to try
|
|
// fast IO on it.
|
|
//
|
|
|
|
if ( connection->CleanupBegun || connection->Aborted ) {
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Determine whether we can do fast IO with this send. In order
|
|
// to perform fast IO, there must be no other sends pended on this
|
|
// connection and there must be enough space left for bufferring
|
|
// the requested amount of data.
|
|
//
|
|
|
|
if ( AfdShouldSendBlock( endpoint, connection, sendLength ) ) {
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
//
|
|
// If this is a nonblocking endpoint, fail the request here and
|
|
// save going through the regular path.
|
|
//
|
|
|
|
if ( endpoint->NonBlocking && !( sendInfo->AfdFlags & AFD_OVERLAPPED ) ) {
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port), but we know
|
|
// that it is not overlapped
|
|
IoStatus->Status = STATUS_DEVICE_NOT_READY;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Add a reference to the connection object since the send
|
|
// request will complete asynchronously.
|
|
//
|
|
|
|
REFERENCE_CONNECTION( connection );
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
IF_DEBUG(FAST_IO) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastConnectionSend: attempting fast IO on endp %p, conn %p\n",
|
|
endpoint, connection));
|
|
}
|
|
|
|
//
|
|
// Next get an AFD buffer structure that contains an IRP and a
|
|
// buffer to hold the data.
|
|
//
|
|
|
|
afdBuffer = AfdGetBuffer( endpoint, sendLength, 0, connection->OwningProcess );
|
|
|
|
if ( afdBuffer == NULL) {
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
connection->VcBufferredSendBytes -= sendLength;
|
|
connection->VcBufferredSendCount -= 1;
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
DEREFERENCE_CONNECTION (connection);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// We have to rebuild the MDL in the AFD buffer structure to
|
|
// represent exactly the number of bytes we're going to be
|
|
// sending.
|
|
//
|
|
|
|
afdBuffer->Mdl->ByteCount = sendLength;
|
|
|
|
//
|
|
// Remember the connection in the AFD buffer structure. We need
|
|
// this in order to access the connection in the restart routine.
|
|
//
|
|
|
|
afdBuffer->Context = connection;
|
|
|
|
//
|
|
// Copy the user's data into the AFD buffer.
|
|
//
|
|
|
|
if( sendLength > 0 ) {
|
|
|
|
try {
|
|
|
|
AfdCopyBufferArrayToBuffer(
|
|
afdBuffer->Buffer,
|
|
sendLength,
|
|
sendInfo->BufferArray,
|
|
sendInfo->BufferCount
|
|
);
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
AfdReturnBuffer( &afdBuffer->Header, connection->OwningProcess );
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
connection->VcBufferredSendBytes -= sendLength;
|
|
connection->VcBufferredSendCount -= 1;
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
DEREFERENCE_CONNECTION (connection);
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use the IRP in the AFD buffer structure to give to the TDI
|
|
// provider. Build the TDI send request.
|
|
//
|
|
|
|
TdiBuildSend(
|
|
afdBuffer->Irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartBufferSend,
|
|
afdBuffer,
|
|
afdBuffer->Mdl,
|
|
0,
|
|
sendLength
|
|
);
|
|
|
|
if (endpoint->Irp==NULL ||
|
|
!AfdEnqueueTpSendIrp (endpoint, afdBuffer->Irp, TRUE)) {
|
|
//
|
|
// Call the transport to actually perform the send.
|
|
//
|
|
|
|
status = IoCallDriver (
|
|
connection->DeviceObject,
|
|
afdBuffer->Irp
|
|
);
|
|
}
|
|
else {
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// Complete the user's IRP as appropriate. Note that we change the
|
|
// status code from what was returned by the TDI provider into
|
|
// STATUS_SUCCESS. This is because we don't want to complete
|
|
// the IRP with STATUS_PENDING etc.
|
|
//
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
IoStatus->Information = sendLength;
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The call failed for some reason. Fail fast IO.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastConnectionReceive (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_RECV_INFO recvInfo,
|
|
IN ULONG recvLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
ULONG totalOffset, partialLength;
|
|
PAFD_BUFFER_HEADER afdBuffer, partialAfdBuffer=NULL;
|
|
PAFD_CONNECTION connection;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
LIST_ENTRY bufferListHead;
|
|
BOOLEAN retryReceive = FALSE; // Retry receive if additional data
|
|
// was indicated by the transport and buffered
|
|
// while we were copying current batch.
|
|
|
|
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
|
endpoint->Type == AfdBlockTypeVcBoth );
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
IoStatus->Information = 0;
|
|
|
|
Retry:
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
connection = AFD_CONNECTION_FROM_ENDPOINT (endpoint);
|
|
if (connection==NULL) {
|
|
//
|
|
// connection might have been cleaned up by transmit file.
|
|
//
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
//
|
|
// If we have already copied something before retrying,
|
|
// return success, next receive will report the error.
|
|
//
|
|
return retryReceive;
|
|
}
|
|
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
IF_DEBUG(FAST_IO) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastConnectionReceive: attempting fast IO on endp %p, conn %p\n",
|
|
endpoint, connection));
|
|
}
|
|
|
|
|
|
//
|
|
// Determine whether we'll be able to perform fast IO. In order
|
|
// to do fast IO, there must be some bufferred data on the
|
|
// connection, there must not be any pended receives on the
|
|
// connection, and there must not be any bufferred expedited
|
|
// data on the connection. This last requirement is for
|
|
// the sake of simplicity only.
|
|
//
|
|
|
|
if ( !IsListEmpty( &connection->VcReceiveIrpListHead ) ||
|
|
connection->VcBufferredExpeditedCount != 0 ||
|
|
connection->DisconnectIndicated ||
|
|
connection->Aborted) {
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
//
|
|
// If we have already copied something before retrying,
|
|
// return success, next receive will report the error.
|
|
//
|
|
return retryReceive;
|
|
}
|
|
|
|
if (connection->VcBufferredReceiveCount == 0) {
|
|
ASSERT( IsListEmpty( &connection->VcReceiveBufferListHead ) );
|
|
|
|
//
|
|
// If this is a nonblocking endpoint, fail the request here and
|
|
// save going through the regular path.
|
|
if (!retryReceive &&
|
|
endpoint->NonBlocking &&
|
|
!(recvInfo->AfdFlags & AFD_OVERLAPPED)) {
|
|
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
|
|
|
IF_DEBUG(EVENT_SELECT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastConnectionReceive: Endp %p, Active %lx\n",
|
|
endpoint,
|
|
endpoint->EventsActive
|
|
));
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
IoStatus->Status = STATUS_DEVICE_NOT_READY;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
//
|
|
// If we have already copied something before retrying,
|
|
// return success, next receive will report the error.
|
|
//
|
|
return retryReceive;
|
|
}
|
|
|
|
ASSERT( !IsListEmpty( &connection->VcReceiveBufferListHead ) );
|
|
|
|
//
|
|
// Get a pointer to the first bufferred AFD buffer structure on
|
|
// the connection.
|
|
//
|
|
|
|
afdBuffer = CONTAINING_RECORD(
|
|
connection->VcReceiveBufferListHead.Flink,
|
|
AFD_BUFFER_HEADER,
|
|
BufferListEntry
|
|
);
|
|
|
|
ASSERT( !afdBuffer->ExpeditedData );
|
|
|
|
//
|
|
// For message endpoints if the buffer contains a partial message
|
|
// or doesn't fit into the buffer, bail out.
|
|
// We don't want the added complexity of handling
|
|
// partial messages in the fast path.
|
|
//
|
|
|
|
if ( IS_MESSAGE_ENDPOINT(endpoint) &&
|
|
(afdBuffer->PartialMessage || afdBuffer->DataLength>recvLength)) {
|
|
//
|
|
// We shouldn't be retry-ing for message oriented endpoint
|
|
// since we only allow fast path if complete message is available.
|
|
//
|
|
ASSERT (retryReceive == FALSE);
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Remeber current offset before we update
|
|
// information field (it is not 0 if we are
|
|
// re-trying).
|
|
//
|
|
totalOffset = (ULONG)IoStatus->Information;
|
|
|
|
|
|
InitializeListHead( &bufferListHead );
|
|
|
|
//
|
|
// Reference the connection object so it doen't go away
|
|
// until we return the buffer.
|
|
//
|
|
REFERENCE_CONNECTION (connection);
|
|
|
|
//
|
|
// Loop getting AFD buffers that will fill in the user's
|
|
// buffer with as much data as will fit, or else with a
|
|
// single buffer if this is not a stream endpoint. We don't
|
|
// actually do the copy within this loop because this loop
|
|
// must occur while holding a lock, and we cannot hold a
|
|
// lock while copying the data into the user's buffer
|
|
// because the user's buffer is not locked and we cannot
|
|
// take a page fault at raised IRQL.
|
|
//
|
|
|
|
AFD_W4_INIT partialLength = 0;
|
|
|
|
while (IoStatus->Information<recvLength) {
|
|
ASSERT( connection->VcBufferredReceiveBytes >= afdBuffer->DataLength );
|
|
ASSERT( connection->VcBufferredReceiveCount > 0 );
|
|
|
|
if (recvLength-IoStatus->Information>=afdBuffer->DataLength) {
|
|
//
|
|
// If we can copy the whole buffer, remove it from the connection's list of
|
|
// buffers and place it on our local list of buffers.
|
|
//
|
|
|
|
RemoveEntryList( &afdBuffer->BufferListEntry );
|
|
InsertTailList( &bufferListHead, &afdBuffer->BufferListEntry );
|
|
|
|
//
|
|
// Update the count of bytes on the connection.
|
|
//
|
|
|
|
connection->VcBufferredReceiveBytes -= afdBuffer->DataLength;
|
|
connection->VcBufferredReceiveCount -= 1;
|
|
IoStatus->Information += afdBuffer->DataLength;
|
|
|
|
|
|
//
|
|
// If this is a stream endpoint and more buffers are available,
|
|
// try to fit the next one it as well..
|
|
//
|
|
|
|
if (!IS_MESSAGE_ENDPOINT(endpoint) &&
|
|
!IsListEmpty( &connection->VcReceiveBufferListHead ) ) {
|
|
|
|
afdBuffer = CONTAINING_RECORD(
|
|
connection->VcReceiveBufferListHead.Flink,
|
|
AFD_BUFFER_HEADER,
|
|
BufferListEntry
|
|
);
|
|
|
|
ASSERT( !afdBuffer->ExpeditedData );
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Copy just a part of the buffer that fits and
|
|
// increment its reference count so it doesn't get
|
|
// destroyed until we done copying.
|
|
//
|
|
ASSERT (!IS_MESSAGE_ENDPOINT (endpoint));
|
|
|
|
partialLength = recvLength-(ULONG)IoStatus->Information;
|
|
partialAfdBuffer = afdBuffer;
|
|
partialAfdBuffer->DataLength -= partialLength;
|
|
partialAfdBuffer->DataOffset += partialLength;
|
|
InterlockedIncrement (&partialAfdBuffer->RefCount);
|
|
connection->VcBufferredReceiveBytes -= partialLength;
|
|
IoStatus->Information = recvLength;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
|
|
|
if( !IsListEmpty( &connection->VcReceiveBufferListHead )) {
|
|
|
|
AfdIndicateEventSelectEvent(
|
|
endpoint,
|
|
AFD_POLL_RECEIVE,
|
|
STATUS_SUCCESS
|
|
);
|
|
|
|
retryReceive = FALSE;
|
|
}
|
|
else {
|
|
//
|
|
// We got all the data buffered. It is possible
|
|
// that while we are copying data, more gets indicated
|
|
// by the transport since we copy at passive level
|
|
// and indication occur at DPC (or even on another processor).
|
|
// We'll check again after copying, so we return as much data
|
|
// as possible to the application (to improve performance).
|
|
// For message oriented transports we can only
|
|
// deliver one message at a time and we shouldn't be on the fast path
|
|
// if we do not have a complete message.
|
|
// If application has EventSelect outstanding we can't copy more data
|
|
// as well since it would receive a signal during indication to come
|
|
// back because we just re-enabled receive event and we would
|
|
// have already consumed the data. We are not concerned with the case
|
|
// when application calls EventSelect while we are in this routine
|
|
// because signaling is not guaranteed to be multithread safe (e.g.
|
|
// if EventSelect comes right before we take the spinlock in the
|
|
// beginning of this routine, application will get false signal as well).
|
|
//
|
|
// Select and AsyncSelect are not a concern as well because they cannot
|
|
// be outstanding (should have been completed when the initial piece of data
|
|
// arrived). If new data comes in the middle of the receive processing above
|
|
// there should be no select or AsyncSelect to signal (unless it was issued
|
|
// from another thread which we don't handle anyway). After this receive
|
|
// call completes, the application can get select re-issued or msafd will
|
|
// re-enable AsyncSelect and they will work correctly.
|
|
//
|
|
retryReceive = (BOOLEAN) (IoStatus->Information<recvLength &&
|
|
!IS_MESSAGE_ENDPOINT (endpoint) &&
|
|
(endpoint->EventsEnabled & AFD_POLL_RECEIVE)==0);
|
|
|
|
//
|
|
// Disable fast IO path to avoid performance penalty
|
|
// of unneccessarily going through it.
|
|
//
|
|
if (!endpoint->NonBlocking)
|
|
endpoint->DisableFastIoRecv = TRUE;
|
|
}
|
|
|
|
//
|
|
// If there is indicated but unreceived data in the TDI provider,
|
|
// and we have available buffer space, fire off an IRP to receive
|
|
// the data.
|
|
//
|
|
|
|
if ( connection->VcReceiveBytesInTransport > 0
|
|
|
|
&&
|
|
|
|
connection->VcBufferredReceiveBytes <
|
|
connection->MaxBufferredReceiveBytes
|
|
|
|
) {
|
|
|
|
ULONG bytesToReceive;
|
|
PAFD_BUFFER newAfdBuffer;
|
|
|
|
ASSERT (connection->RcvInitiated==FALSE);
|
|
|
|
//
|
|
// Remember the count of data that we're going to receive,
|
|
// then reset the fields in the connection where we keep
|
|
// track of how much data is available in the transport.
|
|
// We reset it here before releasing the lock so that
|
|
// another thread doesn't try to receive the data at the
|
|
// same time as us.
|
|
//
|
|
|
|
if ( connection->VcReceiveBytesInTransport > AfdLargeBufferSize ) {
|
|
bytesToReceive = connection->VcReceiveBytesInTransport;
|
|
} else {
|
|
bytesToReceive = AfdLargeBufferSize;
|
|
}
|
|
|
|
//
|
|
// Get an AFD buffer structure to hold the data.
|
|
//
|
|
|
|
newAfdBuffer = AfdGetBuffer( endpoint, bytesToReceive, 0,
|
|
connection->OwningProcess );
|
|
if ( newAfdBuffer == NULL ) {
|
|
//
|
|
// If we were unable to get a buffer, just remember
|
|
// that we still have data in transport
|
|
//
|
|
|
|
if (connection->VcBufferredReceiveBytes == 0 &&
|
|
!connection->OnLRList) {
|
|
//
|
|
// Since we do not have any data buffered, application
|
|
// is not notified and will never call with recv.
|
|
// We will have to put this on low resource list
|
|
// and attempt to allocate memory and pull the data
|
|
// later.
|
|
//
|
|
connection->OnLRList = TRUE;
|
|
REFERENCE_CONNECTION (connection);
|
|
AfdLRListAddItem (&connection->LRListItem, AfdLRRepostReceive);
|
|
}
|
|
else {
|
|
UPDATE_CONN (connection);
|
|
}
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
} else {
|
|
|
|
connection->VcReceiveBytesInTransport = 0;
|
|
connection->RcvInitiated = TRUE;
|
|
ASSERT (InterlockedDecrement (&connection->VcReceiveIrpsInTransport)==-1);
|
|
|
|
//
|
|
// We need to remember the connection in the AFD buffer
|
|
// because we'll need to access it in the completion
|
|
// routine.
|
|
//
|
|
|
|
newAfdBuffer->Context = connection;
|
|
|
|
//
|
|
// Acquire connection reference to be released in completion routine
|
|
//
|
|
|
|
REFERENCE_CONNECTION (connection);
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
//
|
|
// Finish building the receive IRP to give to the TDI provider.
|
|
//
|
|
|
|
TdiBuildReceive(
|
|
newAfdBuffer->Irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartBufferReceive,
|
|
newAfdBuffer,
|
|
newAfdBuffer->Mdl,
|
|
TDI_RECEIVE_NORMAL,
|
|
(CLONG)bytesToReceive
|
|
);
|
|
|
|
//
|
|
// Hand off the IRP to the TDI provider.
|
|
//
|
|
|
|
(VOID)IoCallDriver(
|
|
connection->DeviceObject,
|
|
newAfdBuffer->Irp
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
}
|
|
|
|
//
|
|
// We have in a local list all the data we'll use for this
|
|
// IO. Start copying data to the user buffer.
|
|
//
|
|
|
|
while ( !IsListEmpty( &bufferListHead ) ) {
|
|
|
|
//
|
|
// Take the first buffer from the list.
|
|
//
|
|
|
|
listEntry = RemoveHeadList( &bufferListHead );
|
|
afdBuffer = CONTAINING_RECORD(
|
|
listEntry,
|
|
AFD_BUFFER_HEADER,
|
|
BufferListEntry
|
|
);
|
|
DEBUG afdBuffer->BufferListEntry.Flink = NULL;
|
|
|
|
if( afdBuffer->DataLength > 0 ) {
|
|
|
|
ASSERTMSG (
|
|
"NIC Driver freed the packet before it was returned!!!",
|
|
!afdBuffer->NdisPacket ||
|
|
(MmIsAddressValid (afdBuffer->Context) &&
|
|
MmIsAddressValid (MmGetSystemAddressForMdl (afdBuffer->Mdl))) );
|
|
try {
|
|
|
|
//
|
|
// Copy the data in the buffer to the user buffer.
|
|
//
|
|
|
|
AfdCopyMdlChainToBufferArray(
|
|
recvInfo->BufferArray,
|
|
totalOffset,
|
|
recvInfo->BufferCount,
|
|
afdBuffer->Mdl,
|
|
afdBuffer->DataOffset,
|
|
afdBuffer->DataLength
|
|
);
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
//
|
|
// If an exception is hit, there is the possibility of
|
|
// data corruption. However, it is nearly impossible to
|
|
// avoid this in all cases, so just throw out the
|
|
// remainder of the data that we would have copied to
|
|
// the user buffer.
|
|
//
|
|
|
|
if (afdBuffer->RefCount==1 || // Can't change once off the list
|
|
InterlockedDecrement (&afdBuffer->RefCount)==0) {
|
|
AfdReturnBuffer( afdBuffer, connection->OwningProcess );
|
|
}
|
|
|
|
while ( !IsListEmpty( &bufferListHead ) ) {
|
|
listEntry = RemoveHeadList( &bufferListHead );
|
|
afdBuffer = CONTAINING_RECORD(
|
|
listEntry,
|
|
AFD_BUFFER_HEADER,
|
|
BufferListEntry
|
|
);
|
|
DEBUG afdBuffer->BufferListEntry.Flink = NULL;
|
|
if (afdBuffer->RefCount==1 || // Can't change once off the list
|
|
InterlockedDecrement (&afdBuffer->RefCount)==0) {
|
|
AfdReturnBuffer( afdBuffer, connection->OwningProcess );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We'll have to abort since there is a possibility of data corruption.
|
|
// Shame on application for giving us bogus buffers.
|
|
//
|
|
(VOID)AfdBeginAbort (connection);
|
|
|
|
DEREFERENCE_CONNECTION (connection);
|
|
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
return FALSE;
|
|
}
|
|
|
|
totalOffset += afdBuffer->DataLength;
|
|
}
|
|
|
|
//
|
|
// We're done with the AFD buffer.
|
|
//
|
|
|
|
if (afdBuffer->RefCount==1 || // Can't change once off the list
|
|
InterlockedDecrement (&afdBuffer->RefCount)==0) {
|
|
AfdReturnBuffer( afdBuffer, connection->OwningProcess );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy any partial buffers
|
|
//
|
|
if (partialAfdBuffer) {
|
|
ASSERT (partialLength>0);
|
|
ASSERTMSG (
|
|
"NIC Driver freed the packet before it was returned!!!",
|
|
!partialAfdBuffer->NdisPacket ||
|
|
(MmIsAddressValid (partialAfdBuffer->Context) &&
|
|
MmIsAddressValid (MmGetSystemAddressForMdl (partialAfdBuffer->Mdl))) );
|
|
try {
|
|
|
|
//
|
|
// Copy the data in the buffer to the user buffer.
|
|
//
|
|
|
|
AfdCopyMdlChainToBufferArray(
|
|
recvInfo->BufferArray,
|
|
totalOffset,
|
|
recvInfo->BufferCount,
|
|
partialAfdBuffer->Mdl,
|
|
partialAfdBuffer->DataOffset-partialLength,
|
|
partialLength
|
|
);
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
if (InterlockedDecrement (&partialAfdBuffer->RefCount)==0) {
|
|
ASSERT (partialAfdBuffer->BufferListEntry.Flink == NULL);
|
|
AfdReturnBuffer( partialAfdBuffer, connection->OwningProcess );
|
|
}
|
|
//
|
|
// We'll have to abort since there is a possibility of data corruption.
|
|
// Shame on application for giving us bogus buffers.
|
|
//
|
|
(VOID)AfdBeginAbort (connection);
|
|
|
|
DEREFERENCE_CONNECTION (connection);
|
|
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
return FALSE;
|
|
}
|
|
|
|
if (InterlockedDecrement (&partialAfdBuffer->RefCount)==0) {
|
|
ASSERT (partialAfdBuffer->BufferListEntry.Flink == NULL);
|
|
AfdReturnBuffer( partialAfdBuffer, connection->OwningProcess );
|
|
}
|
|
|
|
totalOffset += partialLength;
|
|
}
|
|
|
|
ASSERT (IoStatus->Information==totalOffset);
|
|
|
|
|
|
//
|
|
// If more data is available, we need to retry and attempt to completely
|
|
// fill application's buffer.
|
|
//
|
|
|
|
if (retryReceive && (endpoint->EventsActive & AFD_POLL_RECEIVE)) {
|
|
ASSERT (IoStatus->Information<recvLength && !IS_MESSAGE_ENDPOINT (endpoint));
|
|
DEREFERENCE_CONNECTION2 (connection, "Fast retry receive 0x%lX bytes", (ULONG)IoStatus->Information);
|
|
goto Retry;
|
|
}
|
|
else {
|
|
//
|
|
// Release the reference needed to return the buffer(s).
|
|
//
|
|
DEREFERENCE_CONNECTION2 (connection, "Fast receive 0x%lX bytes", (ULONG)IoStatus->Information);
|
|
}
|
|
|
|
ASSERT( IoStatus->Information <= recvLength );
|
|
ASSERT (IoStatus->Status == STATUS_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastDatagramSend (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_SEND_DATAGRAM_INFO sendInfo,
|
|
IN ULONG sendLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
{
|
|
|
|
PAFD_BUFFER afdBuffer = NULL;
|
|
NTSTATUS status;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
//
|
|
// If this is a send for more than the threshold number of
|
|
// bytes, don't use the fast path. We don't allow larger sends
|
|
// in the fast path because of the extra data copy it entails,
|
|
// which is more expensive for large buffers. For smaller
|
|
// buffers, however, the cost of the copy is small compared to
|
|
// the IO system overhead of the slow path.
|
|
//
|
|
// We also copy and return for non-blocking endpoints regardless
|
|
// of the size. That's what we are supposed to do according
|
|
// to the spec.
|
|
//
|
|
|
|
if ( !endpoint->NonBlocking && sendLength > AfdFastSendDatagramThreshold ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If we already buffered to many sends, go the long way.
|
|
//
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
if ( endpoint->DgBufferredSendBytes >=
|
|
endpoint->Common.Datagram.MaxBufferredSendBytes &&
|
|
endpoint->DgBufferredSendBytes>0) {
|
|
|
|
if ( endpoint->NonBlocking && !( sendInfo->AfdFlags & AFD_OVERLAPPED ) ) {
|
|
endpoint->EventsActive &= ~AFD_POLL_SEND;
|
|
endpoint->EnableSendEvent = TRUE;
|
|
|
|
IF_DEBUG(EVENT_SELECT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastIoDeviceControl: Endp %p, Active %lX\n",
|
|
endpoint,
|
|
endpoint->EventsActive
|
|
));
|
|
}
|
|
}
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
//
|
|
// If this is a nonblocking endpoint, fail the request here and
|
|
// save going through the regular path.(check for non-blocking is
|
|
// below, otherwise status code is ignored).
|
|
//
|
|
|
|
status = STATUS_DEVICE_NOT_READY;
|
|
goto errorset;
|
|
}
|
|
|
|
endpoint->DgBufferredSendBytes += sendLength;
|
|
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
IF_DEBUG(FAST_IO) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastDatagramSend: attempting fast IO on endp %p\n",
|
|
endpoint));
|
|
}
|
|
|
|
|
|
//
|
|
// Get an AFD buffer to use for the request. We'll copy the
|
|
// user's data to the AFD buffer then submit the IRP in the AFD
|
|
// buffer to the TDI provider.
|
|
|
|
if ((sendInfo->TdiConnInfo.RemoteAddressLength==0) &&
|
|
!IS_TDI_DGRAM_CONNECTION(endpoint)) {
|
|
retry:
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
//
|
|
// Get an AFD buffer to use for the request. We'll copy the
|
|
// user to the AFD buffer then submit the IRP in the AFD
|
|
// buffer to the TDI provider.
|
|
//
|
|
|
|
afdBuffer = AfdGetBufferRaiseOnFailure(
|
|
endpoint,
|
|
sendLength,
|
|
endpoint->Common.Datagram.RemoteAddressLength,
|
|
endpoint->OwningProcess
|
|
);
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (status)) {
|
|
ASSERT (NT_ERROR (status));
|
|
goto exit;
|
|
}
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
//
|
|
// If the endpoint is not connected, fail.
|
|
//
|
|
|
|
if ( endpoint->State != AfdEndpointStateConnected ) {
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
AfdReturnBuffer (&afdBuffer->Header, endpoint->OwningProcess);
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto exit;
|
|
}
|
|
|
|
if (afdBuffer->AllocatedAddressLength <
|
|
endpoint->Common.Datagram.RemoteAddressLength ) {
|
|
//
|
|
// Apparently connection address length has changed
|
|
// on us while we were allocating the buffer.
|
|
// This is extremely unlikely (even if endpoint got
|
|
// connected to a different address, the length is unlikely
|
|
// to change), but we must handle this, just try again.
|
|
//
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
AfdReturnBuffer (&afdBuffer->Header, endpoint->OwningProcess);
|
|
goto retry;
|
|
}
|
|
//
|
|
// Copy the address to the AFD buffer.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
afdBuffer->TdiInfo.RemoteAddress,
|
|
endpoint->Common.Datagram.RemoteAddress,
|
|
endpoint->Common.Datagram.RemoteAddressLength
|
|
);
|
|
|
|
afdBuffer->TdiInfo.RemoteAddressLength = endpoint->Common.Datagram.RemoteAddressLength;
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
}
|
|
else {
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
afdBuffer = AfdGetBufferRaiseOnFailure(
|
|
endpoint,
|
|
sendLength,
|
|
sendInfo->TdiConnInfo.RemoteAddressLength,
|
|
endpoint->OwningProcess);
|
|
//
|
|
// Copy address if necessary.
|
|
//
|
|
if (sendInfo->TdiConnInfo.RemoteAddressLength!=0) {
|
|
RtlCopyMemory(
|
|
afdBuffer->TdiInfo.RemoteAddress,
|
|
sendInfo->TdiConnInfo.RemoteAddress,
|
|
sendInfo->TdiConnInfo.RemoteAddressLength
|
|
);
|
|
|
|
//
|
|
// Validate internal consistency of the transport address structure.
|
|
// Note that we HAVE to do this after copying since the malicious
|
|
// application can change the content of the buffer on us any time
|
|
// and our check will be bypassed.
|
|
//
|
|
if ((((PTRANSPORT_ADDRESS)afdBuffer->TdiInfo.RemoteAddress)->TAAddressCount!=1) ||
|
|
(LONG)sendInfo->TdiConnInfo.RemoteAddressLength<
|
|
FIELD_OFFSET (TRANSPORT_ADDRESS,
|
|
Address[0].Address[((PTRANSPORT_ADDRESS)afdBuffer->TdiInfo.RemoteAddress)->Address[0].AddressLength])) {
|
|
ExRaiseStatus (STATUS_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
ASSERT (NT_ERROR (status));
|
|
if (afdBuffer!=NULL) {
|
|
AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess );
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
afdBuffer->TdiInfo.RemoteAddressLength = sendInfo->TdiConnInfo.RemoteAddressLength;
|
|
}
|
|
|
|
//
|
|
// Copy the output buffer to the AFD buffer.
|
|
//
|
|
|
|
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
|
|
try {
|
|
|
|
AfdCopyBufferArrayToBuffer(
|
|
afdBuffer->Buffer,
|
|
sendLength,
|
|
sendInfo->BufferArray,
|
|
sendInfo->BufferCount
|
|
);
|
|
|
|
//
|
|
// Store the length of the data and the address we're going to
|
|
// send.
|
|
//
|
|
afdBuffer->DataLength = sendLength;
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
|
|
ASSERT (NT_ERROR (status));
|
|
AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess );
|
|
goto exit;
|
|
}
|
|
|
|
|
|
if (IS_TDI_DGRAM_CONNECTION(endpoint)
|
|
&& (afdBuffer->TdiInfo.RemoteAddressLength==0)) {
|
|
TdiBuildSend(
|
|
afdBuffer->Irp,
|
|
endpoint->AddressDeviceObject,
|
|
endpoint->AddressFileObject,
|
|
AfdRestartFastDatagramSend,
|
|
afdBuffer,
|
|
afdBuffer->Irp->MdlAddress,
|
|
0,
|
|
sendLength
|
|
);
|
|
}
|
|
else {
|
|
//
|
|
// Set up the input TDI information to point to the destination
|
|
// address.
|
|
//
|
|
|
|
afdBuffer->TdiInfo.Options = NULL;
|
|
afdBuffer->TdiInfo.OptionsLength = 0;
|
|
afdBuffer->TdiInfo.UserData = NULL;
|
|
afdBuffer->TdiInfo.UserDataLength = 0;
|
|
|
|
|
|
//
|
|
// Initialize the IRP in the AFD buffer to do a fast datagram send.
|
|
//
|
|
|
|
TdiBuildSendDatagram(
|
|
afdBuffer->Irp,
|
|
endpoint->AddressDeviceObject,
|
|
endpoint->AddressFileObject,
|
|
AfdRestartFastDatagramSend,
|
|
afdBuffer,
|
|
afdBuffer->Irp->MdlAddress,
|
|
sendLength,
|
|
&afdBuffer->TdiInfo
|
|
);
|
|
}
|
|
|
|
//
|
|
// Change the MDL in the AFD buffer to specify only the number
|
|
// of bytes we're actually sending. This is a requirement of TDI--
|
|
// the MDL chain cannot describe a longer buffer than the send
|
|
// request.
|
|
//
|
|
|
|
afdBuffer->Mdl->ByteCount = sendLength;
|
|
|
|
//
|
|
// Reference the endpoint so that it does not go away until the send
|
|
// completes. This is necessary to ensure that a send which takes a
|
|
// very long time and lasts longer than the process will not cause a
|
|
// crash when the send datragram finally completes.
|
|
//
|
|
|
|
REFERENCE_ENDPOINT2( endpoint, "AfdFastDatagramSend, length: 0x%lX", sendLength );
|
|
|
|
//
|
|
// Set the context to NULL initially so that if the IRP is completed
|
|
// by the stack before IoCallDriver returns, the completion routine
|
|
// does not free the buffer (and IRP in it) and we can figure out
|
|
// what the final status of the operation was and report it to the
|
|
// application
|
|
//
|
|
|
|
afdBuffer->Context = NULL;
|
|
|
|
// Check if there are outstanding TPackets IRP and
|
|
// delay sending to ensure in-order delivery.
|
|
// We do not need to hold the lock while checking
|
|
// because we do not need to maintain order if
|
|
// application does not wait for send call to return
|
|
// before sumbitting TPackets IRP.
|
|
// Of course, we will hold the lock while enqueuing IRP
|
|
//
|
|
if (endpoint->Irp==NULL ||
|
|
!AfdEnqueueTpSendIrp (endpoint, afdBuffer->Irp, TRUE)) {
|
|
//
|
|
// Give the IRP to the TDI provider. If the request fails
|
|
// immediately, then fail fast IO. If the request fails later on,
|
|
// there's nothing we can do about it.
|
|
//
|
|
status = IoCallDriver(
|
|
endpoint->AddressDeviceObject,
|
|
afdBuffer->Irp
|
|
);
|
|
}
|
|
else {
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// Check if completion routine has already been called and we
|
|
// can figure out what the final status is
|
|
//
|
|
if (InterlockedCompareExchangePointer (
|
|
&afdBuffer->Context,
|
|
endpoint,
|
|
NULL)!=NULL) {
|
|
BOOLEAN indicateSendEvent;
|
|
//
|
|
// Completion routine has been called, pick the final status
|
|
// and dereference the endpoint and free the buffer
|
|
//
|
|
status = afdBuffer->Irp->IoStatus.Status;
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
endpoint->DgBufferredSendBytes -= sendLength;
|
|
if (endpoint->DgBufferredSendBytes <
|
|
endpoint->Common.Datagram.MaxBufferredSendBytes ||
|
|
endpoint->DgBufferredSendBytes==0) {
|
|
indicateSendEvent = TRUE;
|
|
AfdIndicateEventSelectEvent (endpoint, AFD_POLL_SEND, STATUS_SUCCESS);
|
|
}
|
|
else {
|
|
indicateSendEvent = FALSE;
|
|
}
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
if (indicateSendEvent) {
|
|
AfdIndicatePollEvent (endpoint, AFD_POLL_SEND, STATUS_SUCCESS);
|
|
}
|
|
|
|
AfdReturnBuffer (&afdBuffer->Header, endpoint->OwningProcess);
|
|
|
|
DEREFERENCE_ENDPOINT2 (endpoint, "AfdFastDatagramSend-inline completion, status: 0x%lX", status );
|
|
}
|
|
//else Completion routine has not been called, we set the pointer
|
|
// to the endpoint in the buffer context, so it can derefernce it
|
|
// and knows to free the buffer
|
|
//
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
IoStatus->Information = sendLength;
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
return TRUE;
|
|
} else {
|
|
goto errorset;
|
|
}
|
|
|
|
exit:
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
endpoint->DgBufferredSendBytes -= sendLength;
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
errorset:
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port),
|
|
if ( endpoint->NonBlocking && !( sendInfo->AfdFlags & AFD_OVERLAPPED ) ) {
|
|
// We know that it is not overlapped
|
|
IoStatus->Status = status;
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
} // AfdFastDatagramSend
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartFastDatagramSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PAFD_BUFFER afdBuffer;
|
|
PAFD_ENDPOINT endpoint;
|
|
ULONG sendLength;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
afdBuffer = Context;
|
|
ASSERT (IS_VALID_AFD_BUFFER (afdBuffer));
|
|
#if DBG
|
|
ASSERT( afdBuffer->Irp == Irp );
|
|
#else
|
|
UNREFERENCED_PARAMETER (Irp);
|
|
#endif
|
|
|
|
//
|
|
// Reset the AFD buffer structure.
|
|
//
|
|
|
|
sendLength = afdBuffer->Mdl->ByteCount;
|
|
ASSERT (afdBuffer->DataLength==sendLength);
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
|
|
|
|
//
|
|
// If call succeeded, transport should have sent the number of bytes requested
|
|
//
|
|
ASSERT (Irp->IoStatus.Status!=STATUS_SUCCESS ||
|
|
Irp->IoStatus.Information==sendLength);
|
|
//
|
|
// Find the endpoint used for this request if
|
|
// the IoCallDriver call has completed already
|
|
//
|
|
|
|
endpoint = InterlockedCompareExchangePointer (&afdBuffer->Context,
|
|
(PVOID)-1,
|
|
NULL);
|
|
if (endpoint!=NULL) {
|
|
BOOLEAN indicateSendEvent;
|
|
#if REFERENCE_DEBUG
|
|
NTSTATUS status;
|
|
#endif
|
|
//
|
|
// IoCallDriver has completed, free the buffer and
|
|
// dereference endpoint here
|
|
//
|
|
ASSERT( IS_DGRAM_ENDPOINT(endpoint) );
|
|
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
endpoint->DgBufferredSendBytes -= sendLength;
|
|
if (endpoint->DgBufferredSendBytes <
|
|
endpoint->Common.Datagram.MaxBufferredSendBytes ||
|
|
endpoint->DgBufferredSendBytes==0) {
|
|
AfdIndicateEventSelectEvent (endpoint, AFD_POLL_SEND, STATUS_SUCCESS);
|
|
indicateSendEvent = TRUE;
|
|
}
|
|
else
|
|
indicateSendEvent = FALSE;
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
if (indicateSendEvent) {
|
|
AfdIndicatePollEvent (endpoint, AFD_POLL_SEND, STATUS_SUCCESS);
|
|
}
|
|
//
|
|
// Get rid of the reference we put on the endpoint when we started
|
|
// this I/O.
|
|
//
|
|
|
|
#if REFERENCE_DEBUG
|
|
status = Irp->IoStatus.Status;
|
|
#endif
|
|
AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess );
|
|
|
|
DEREFERENCE_ENDPOINT2 (endpoint, "AfdRestartFastDatagramSend, status: 0x%lX", status );
|
|
|
|
}
|
|
// else IoCallDriver is not done yet, it will free the buffer
|
|
// and endpoint when done (it will look at final status and
|
|
// report it to the application).
|
|
|
|
//
|
|
// Tell the IO system to stop processing this IRP.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // AfdRestartFastSendDatagram
|
|
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastDatagramReceive (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_RECV_MESSAGE_INFO msgInfo,
|
|
IN ULONG recvLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PLIST_ENTRY listEntry;
|
|
PAFD_BUFFER_HEADER afdBuffer;
|
|
PTRANSPORT_ADDRESS tdiAddress;
|
|
ULONG length;
|
|
|
|
|
|
|
|
IF_DEBUG(FAST_IO) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastDatagramReceive: attempting fast IO on endp %p\n",
|
|
endpoint));
|
|
}
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
|
|
//
|
|
// If there are no datagrams available to be received, don't
|
|
// bother with the fast path.
|
|
//
|
|
if ( !ARE_DATAGRAMS_ON_ENDPOINT( endpoint ) ) {
|
|
|
|
//
|
|
// If this is a nonblocking endpoint, fail the request here and
|
|
// save going through the regular path.
|
|
//
|
|
|
|
if ( endpoint->NonBlocking && !( msgInfo->dgi.AfdFlags & AFD_OVERLAPPED ) ) {
|
|
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
|
|
|
IF_DEBUG(EVENT_SELECT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastDatagramReceive: Endp %p, Active %lX\n",
|
|
endpoint,
|
|
endpoint->EventsActive
|
|
));
|
|
}
|
|
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port), but we know here
|
|
// that call is not overlapped
|
|
IoStatus->Status = STATUS_DEVICE_NOT_READY;
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
return TRUE;
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// There is at least one datagram bufferred on the endpoint. Use it
|
|
// for this receive.
|
|
//
|
|
|
|
listEntry = RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead );
|
|
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER_HEADER, BufferListEntry );
|
|
|
|
//
|
|
// If the datagram is too large or it is an error indication
|
|
// fail fast IO.
|
|
//
|
|
|
|
if ( (afdBuffer->DataLength > recvLength) ||
|
|
!NT_SUCCESS (afdBuffer->Status)) {
|
|
InsertHeadList(
|
|
&endpoint->ReceiveDatagramBufferListHead,
|
|
&afdBuffer->BufferListEntry
|
|
);
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update counts of bufferred datagrams and bytes on the endpoint.
|
|
//
|
|
|
|
endpoint->DgBufferredReceiveCount--;
|
|
endpoint->DgBufferredReceiveBytes -= afdBuffer->DataLength;
|
|
|
|
//
|
|
// Release the lock and copy the datagram into the user buffer. We
|
|
// can't continue to hold the lock, because it is not legal to take
|
|
// an exception at raised IRQL. Releasing the lock may result in a
|
|
// misordered datagram if there is an exception in copying to the
|
|
// user's buffer, but that is the application's fault for giving us a bogus
|
|
// pointer. Besides, datagram order is not guaranteed.
|
|
//
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
try {
|
|
|
|
if (afdBuffer->DataLength>0) {
|
|
AfdCopyMdlChainToBufferArray(
|
|
msgInfo->dgi.BufferArray,
|
|
0,
|
|
msgInfo->dgi.BufferCount,
|
|
afdBuffer->Mdl,
|
|
0,
|
|
afdBuffer->DataLength
|
|
);
|
|
}
|
|
|
|
//
|
|
// If we need to return the source address, copy it to the
|
|
// user's output buffer.
|
|
//
|
|
|
|
if ( msgInfo->dgi.Address != NULL ) {
|
|
|
|
tdiAddress = afdBuffer->TdiInfo.RemoteAddress;
|
|
|
|
length = tdiAddress->Address[0].AddressLength +
|
|
sizeof(u_short); // sa_family
|
|
|
|
if( *msgInfo->dgi.AddressLength < length ) {
|
|
|
|
ExRaiseAccessViolation();
|
|
|
|
}
|
|
|
|
if (ExGetPreviousMode ()!=KernelMode) {
|
|
ProbeForWrite (msgInfo->dgi.Address,
|
|
length,
|
|
sizeof (UCHAR));
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
msgInfo->dgi.Address,
|
|
&tdiAddress->Address[0].AddressType,
|
|
length
|
|
);
|
|
|
|
*msgInfo->dgi.AddressLength = length;
|
|
}
|
|
|
|
if (msgInfo->ControlLength!=NULL) {
|
|
if (afdBuffer->DatagramFlags & TDI_RECEIVE_CONTROL_INFO &&
|
|
afdBuffer->DataOffset>0) {
|
|
PAFD_BUFFER buf = CONTAINING_RECORD (afdBuffer, AFD_BUFFER, Header);
|
|
ASSERT (msgInfo->MsgFlags!=NULL);
|
|
ASSERT (buf->BufferLength != AfdBufferTagSize);
|
|
length = buf->DataOffset;
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (NULL)) {
|
|
length = AfdComputeCMSGLength32 (
|
|
(PUCHAR)buf->Buffer+afdBuffer->DataLength,
|
|
length);
|
|
|
|
if (length>*msgInfo->ControlLength) {
|
|
ExRaiseAccessViolation ();
|
|
}
|
|
if (ExGetPreviousMode ()!=KernelMode) {
|
|
ProbeForWrite (msgInfo->ControlBuffer,
|
|
length,
|
|
sizeof (UCHAR));
|
|
}
|
|
AfdCopyCMSGBuffer32 (
|
|
msgInfo->ControlBuffer,
|
|
(PUCHAR)buf->Buffer+afdBuffer->DataLength,
|
|
length);
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
if (length>*msgInfo->ControlLength) {
|
|
ExRaiseAccessViolation ();
|
|
}
|
|
|
|
if (ExGetPreviousMode ()!=KernelMode) {
|
|
ProbeForWrite (msgInfo->ControlBuffer,
|
|
length,
|
|
sizeof (UCHAR));
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
msgInfo->ControlBuffer,
|
|
(PUCHAR)buf->Buffer+afdBuffer->DataLength,
|
|
length
|
|
);
|
|
}
|
|
|
|
}
|
|
else {
|
|
length = 0;
|
|
}
|
|
|
|
*msgInfo->ControlLength = length;
|
|
}
|
|
|
|
if (msgInfo->MsgFlags!=NULL) {
|
|
ULONG flags = 0;
|
|
if (afdBuffer->DatagramFlags & TDI_RECEIVE_BROADCAST)
|
|
flags |= MSG_BCAST;
|
|
if (afdBuffer->DatagramFlags & TDI_RECEIVE_MULTICAST)
|
|
flags |= MSG_MCAST;
|
|
*msgInfo->MsgFlags = flags;
|
|
}
|
|
|
|
IoStatus->Information = afdBuffer->DataLength;
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
//
|
|
// Put the buffer back on the endpoint's list.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
InsertHeadList(
|
|
&endpoint->ReceiveDatagramBufferListHead,
|
|
&afdBuffer->BufferListEntry
|
|
);
|
|
|
|
endpoint->DgBufferredReceiveCount++;
|
|
endpoint->DgBufferredReceiveBytes += afdBuffer->DataLength;
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
// Fast io can't handle error returns
|
|
// if call is overlapped (completion port)
|
|
// IoStatus->Status = GetExceptionCode ();
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Clear the receive data active bit. If there's more data
|
|
// available, set the corresponding event.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
|
|
|
if( ARE_DATAGRAMS_ON_ENDPOINT( endpoint ) ) {
|
|
|
|
AfdIndicateEventSelectEvent(
|
|
endpoint,
|
|
AFD_POLL_RECEIVE,
|
|
STATUS_SUCCESS
|
|
);
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Disable fast IO path to avoid performance penalty
|
|
// of going through it.
|
|
//
|
|
if (!endpoint->NonBlocking)
|
|
endpoint->DisableFastIoRecv = TRUE;
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
//
|
|
// The fast IO worked! Clean up and return to the user.
|
|
//
|
|
|
|
AfdReturnBuffer( afdBuffer, endpoint->OwningProcess );
|
|
|
|
ASSERT (IoStatus->Status == STATUS_SUCCESS);
|
|
return TRUE;
|
|
|
|
} // AfdFastDatagramReceive
|
|
|
|
|
|
BOOLEAN
|
|
AfdShouldSendBlock (
|
|
IN PAFD_ENDPOINT Endpoint,
|
|
IN PAFD_CONNECTION Connection,
|
|
IN ULONG SendLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether a nonblocking send can be performed on the
|
|
connection, and if the send is possible, updates the connection's
|
|
send tracking information.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - the AFD endpoint for the send.
|
|
|
|
Connection - the AFD connection for the send.
|
|
|
|
SendLength - the number of bytes that the caller wants to send.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the there is not too much data on the endpoint to perform
|
|
the send; FALSE otherwise.
|
|
|
|
Note:
|
|
This routine assumes that endpoint spinlock is held when calling it.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Determine whether we can do fast IO with this send. In order
|
|
// to perform fast IO, there must be no other sends pended on this
|
|
// connection and there must be enough space left for bufferring
|
|
// the requested amount of data.
|
|
//
|
|
|
|
|
|
if ( !IsListEmpty( &Connection->VcSendIrpListHead )
|
|
|
|
||
|
|
|
|
Connection->VcBufferredSendBytes >= Connection->MaxBufferredSendBytes
|
|
) {
|
|
|
|
//
|
|
// If this is a nonblocking endpoint, fail the request here and
|
|
// save going through the regular path.
|
|
//
|
|
|
|
if ( Endpoint->NonBlocking ) {
|
|
Endpoint->EventsActive &= ~AFD_POLL_SEND;
|
|
Endpoint->EnableSendEvent = TRUE;
|
|
|
|
IF_DEBUG(EVENT_SELECT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastIoDeviceControl: Endp %p, Active %lX\n",
|
|
Endpoint,
|
|
Endpoint->EventsActive
|
|
));
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Update count of send bytes pending on the connection.
|
|
//
|
|
|
|
Connection->VcBufferredSendBytes += SendLength;
|
|
Connection->VcBufferredSendCount += 1;
|
|
|
|
//
|
|
// Indicate to the caller that it is OK to proceed with the send.
|
|
//
|
|
|
|
return FALSE;
|
|
|
|
} // AfdShouldSendBlock
|
|
|