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.
2372 lines
75 KiB
2372 lines
75 KiB
/*++
|
|
|
|
Copyright (c) 1989-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
send.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code for passing on send IRPs to
|
|
TDI providers.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 13-Mar-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "afdp.h"
|
|
|
|
VOID
|
|
AfdCancelSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartSendConnDatagram (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartSendTdiConnDatagram (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartSendDatagram (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
typedef struct _AFD_SEND_CONN_DATAGRAM_CONTEXT {
|
|
PAFD_ENDPOINT Endpoint;
|
|
TDI_CONNECTION_INFORMATION ConnectionInformation;
|
|
} AFD_SEND_CONN_DATAGRAM_CONTEXT, *PAFD_SEND_CONN_DATAGRAM_CONTEXT;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGEAFD, AfdSend )
|
|
#pragma alloc_text( PAGEAFD, AfdSendDatagram )
|
|
#pragma alloc_text( PAGEAFD, AfdCancelSend )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartSend )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartBufferSend )
|
|
#pragma alloc_text( PAGEAFD, AfdProcessBufferSend )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartSendConnDatagram )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartSendTdiConnDatagram )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartSendDatagram )
|
|
#pragma alloc_text( PAGEAFD, AfdSendPossibleEventHandler )
|
|
#endif
|
|
|
|
//
|
|
// Macros to make the send restart code more maintainable.
|
|
//
|
|
|
|
#define AfdRestartSendInfo DeviceIoControl
|
|
#define AfdMdlChain Type3InputBuffer
|
|
#define AfdSendFlags InputBufferLength
|
|
#define AfdOriginalLength OutputBufferLength
|
|
#define AfdCurrentLength IoControlCode
|
|
|
|
#define AFD_SEND_MDL_HAS_NOT_BEEN_MAPPED 0x80000000
|
|
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
AfdSend (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PAFD_ENDPOINT endpoint;
|
|
ULONG sendLength;
|
|
ULONG sendOffset;
|
|
ULONG currentOffset;
|
|
PMDL mdl;
|
|
PAFD_CONNECTION connection;
|
|
PAFD_BUFFER afdBuffer;
|
|
PEPROCESS process;
|
|
ULONG sendFlags;
|
|
ULONG afdFlags;
|
|
ULONG bufferCount;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
//
|
|
// Make sure that the endpoint is in the correct state.
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
if ( endpoint->State != AfdEndpointStateConnected) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// If send has been shut down on this endpoint, fail. We need to be
|
|
// careful about what error code we return here: if the connection
|
|
// has been aborted, be sure to return the apprpriate error code.
|
|
//
|
|
|
|
if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 ) {
|
|
|
|
if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ) {
|
|
status = STATUS_LOCAL_DISCONNECT;
|
|
} else {
|
|
status = STATUS_PIPE_DISCONNECTED;
|
|
}
|
|
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Set up the IRP on the assumption that it will complete successfully.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// If this is an IOCTL_AFD_SEND, then grab the parameters from the
|
|
// supplied AFD_SEND_INFO structure, build an MDL chain describing
|
|
// the WSABUF array, and attach the MDL chain to the IRP.
|
|
//
|
|
// If this is an IRP_MJ_WRITE IRP, just grab the length from the IRP
|
|
// and set the flags to zero.
|
|
//
|
|
|
|
if ( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) {
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (Irp)) {
|
|
PAFD_SEND_INFO32 sendInfo32;
|
|
LPWSABUF32 bufferArray32;
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
|
|
sizeof(*sendInfo32) ) {
|
|
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
sendInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
ProbeForReadSmallStructure(
|
|
sendInfo32,
|
|
sizeof(*sendInfo32),
|
|
PROBE_ALIGNMENT32(AFD_SEND_INFO32)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
sendFlags = sendInfo32->TdiFlags;
|
|
afdFlags = sendInfo32->AfdFlags;
|
|
bufferArray32 = UlongToPtr(sendInfo32->BufferArray);
|
|
bufferCount = sendInfo32->BufferCount;
|
|
|
|
|
|
//
|
|
// Create the MDL chain describing the WSABUF array.
|
|
// This will also validate the buffer array and individual
|
|
// buffers
|
|
//
|
|
|
|
status = AfdAllocateMdlChain32(
|
|
Irp, // Requestor mode passed along
|
|
bufferArray32,
|
|
bufferCount,
|
|
IoReadAccess,
|
|
&sendLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto complete;
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
|
|
ASSERT (NT_ERROR (status));
|
|
//
|
|
// Exception accessing input structure.
|
|
//
|
|
goto complete;
|
|
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Invalid input buffer length.
|
|
//
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
|
|
}
|
|
}
|
|
else
|
|
#endif _WIN64
|
|
{
|
|
PAFD_SEND_INFO sendInfo;
|
|
LPWSABUF bufferArray;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode==IOCTL_AFD_SEND );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
|
|
sizeof(*sendInfo) ) {
|
|
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
sendInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
ProbeForReadSmallStructure(
|
|
sendInfo,
|
|
sizeof(*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
|
|
//
|
|
|
|
sendFlags = sendInfo->TdiFlags;
|
|
afdFlags = sendInfo->AfdFlags;
|
|
bufferArray = sendInfo->BufferArray;
|
|
bufferCount = sendInfo->BufferCount;
|
|
|
|
|
|
//
|
|
// Create the MDL chain describing the WSABUF array.
|
|
// This will also validate the buffer array and individual
|
|
// buffers
|
|
//
|
|
|
|
status = AfdAllocateMdlChain(
|
|
Irp, // Requestor mode passed along
|
|
bufferArray,
|
|
bufferCount,
|
|
IoReadAccess,
|
|
&sendLength
|
|
);
|
|
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
goto complete;
|
|
}
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
ASSERT (NT_ERROR (status));
|
|
|
|
//
|
|
// Exception accessing input structure.
|
|
//
|
|
goto complete;
|
|
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Invalid input buffer length.
|
|
//
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
|
|
}
|
|
}
|
|
|
|
if (IS_SAN_ENDPOINT(endpoint)) {
|
|
IrpSp->MajorFunction = IRP_MJ_WRITE;
|
|
IrpSp->Parameters.Write.Length = sendLength;
|
|
return AfdSanRedirectRequest (Irp, IrpSp);
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT( IrpSp->MajorFunction == IRP_MJ_WRITE );
|
|
|
|
sendFlags = 0;
|
|
afdFlags = AFD_OVERLAPPED;
|
|
sendLength = IrpSp->Parameters.Write.Length;
|
|
|
|
}
|
|
|
|
//
|
|
// AfdSend() will either complete fully or will fail.
|
|
//
|
|
|
|
Irp->IoStatus.Information = sendLength;
|
|
|
|
//
|
|
// Setup for possible restart if the transport completes
|
|
// the send partially.
|
|
//
|
|
|
|
IrpSp->Parameters.AfdRestartSendInfo.AfdMdlChain = Irp->MdlAddress;
|
|
IrpSp->Parameters.AfdRestartSendInfo.AfdSendFlags = sendFlags;
|
|
IrpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength = sendLength;
|
|
IrpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength = sendLength;
|
|
|
|
//
|
|
// Buffer sends if the TDI provider does not buffer.
|
|
//
|
|
|
|
if ( IS_TDI_BUFFERRING(endpoint) &&
|
|
endpoint->NonBlocking) {
|
|
//
|
|
// If this is a nonblocking endpoint, set the TDI nonblocking
|
|
// send flag so that the request will fail if the send cannot be
|
|
// performed immediately.
|
|
//
|
|
|
|
sendFlags |= TDI_SEND_NON_BLOCKING;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is a datagram endpoint, format up a send datagram request
|
|
// and pass it on to the TDI provider.
|
|
//
|
|
|
|
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
|
//
|
|
// It is illegal to send expedited data on a datagram socket.
|
|
//
|
|
|
|
if ( (sendFlags & TDI_SEND_EXPEDITED) != 0 ) {
|
|
status = STATUS_NOT_SUPPORTED;
|
|
goto complete;
|
|
}
|
|
|
|
if (!IS_TDI_DGRAM_CONNECTION(endpoint)) {
|
|
PAFD_SEND_CONN_DATAGRAM_CONTEXT context;
|
|
ULONG remoteAddressLength;
|
|
|
|
|
|
//
|
|
// Allocate space to hold the connection information structure
|
|
// we'll use on input.
|
|
//
|
|
|
|
retry:
|
|
remoteAddressLength = endpoint->Common.Datagram.RemoteAddressLength;
|
|
|
|
try {
|
|
context = AFD_ALLOCATE_POOL_WITH_QUOTA(
|
|
NonPagedPool,
|
|
sizeof(*context) + remoteAddressLength,
|
|
AFD_TDI_POOL_TAG
|
|
);
|
|
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode ();
|
|
context = NULL;
|
|
goto complete;
|
|
}
|
|
|
|
context->Endpoint = endpoint;
|
|
context->ConnectionInformation.UserDataLength = 0;
|
|
context->ConnectionInformation.UserData = NULL;
|
|
context->ConnectionInformation.OptionsLength = 0;
|
|
context->ConnectionInformation.Options = NULL;
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
if (remoteAddressLength <
|
|
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 );
|
|
|
|
AFD_FREE_POOL(
|
|
context,
|
|
AFD_TDI_POOL_TAG
|
|
);
|
|
|
|
goto retry;
|
|
}
|
|
|
|
//
|
|
// Copy the address to the context buffer.
|
|
// endpoint->Common.Datagram.RemoteAddress can be freed
|
|
// by someone else if endpoint lock is not held
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
context+1,
|
|
endpoint->Common.Datagram.RemoteAddress,
|
|
endpoint->Common.Datagram.RemoteAddressLength
|
|
);
|
|
|
|
context->ConnectionInformation.RemoteAddressLength =
|
|
endpoint->Common.Datagram.RemoteAddressLength;
|
|
context->ConnectionInformation.RemoteAddress =
|
|
(PTRANSPORT_ADDRESS)(context+1);
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
REFERENCE_ENDPOINT2 (endpoint,"AfdSend, length: 0x%lX", sendLength);
|
|
|
|
//
|
|
// Build a send datagram request.
|
|
//
|
|
|
|
TdiBuildSendDatagram(
|
|
Irp,
|
|
endpoint->AddressDeviceObject,
|
|
endpoint->AddressFileObject,
|
|
AfdRestartSendConnDatagram,
|
|
context,
|
|
Irp->MdlAddress,
|
|
sendLength,
|
|
&context->ConnectionInformation
|
|
);
|
|
}
|
|
else {
|
|
REFERENCE_ENDPOINT2 (endpoint,"AfdSend(conn), length: 0x%lX", sendLength);
|
|
TdiBuildSend(
|
|
Irp,
|
|
endpoint->AddressDeviceObject,
|
|
endpoint->AddressFileObject,
|
|
AfdRestartSendTdiConnDatagram,
|
|
endpoint,
|
|
Irp->MdlAddress,
|
|
0,
|
|
sendLength
|
|
);
|
|
//
|
|
// 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) {
|
|
if (AfdEnqueueTpSendIrp (endpoint, Irp, FALSE)) {
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the transport to actually perform the send operation.
|
|
//
|
|
|
|
return AfdIoCallDriver(
|
|
endpoint,
|
|
endpoint->AddressDeviceObject,
|
|
Irp
|
|
);
|
|
}
|
|
|
|
afdBuffer = NULL;
|
|
process = endpoint->OwningProcess;
|
|
sendOffset = 0;
|
|
|
|
retry_buffer:
|
|
if (!IS_TDI_BUFFERRING(endpoint) &&
|
|
(!endpoint->DisableFastIoSend ||
|
|
(endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED )) ) ) {
|
|
ULONG copyThreshold = AfdBlockingSendCopyThreshold;
|
|
//
|
|
// If application does a blocking send for more than 128k, we
|
|
// copy only the last 64k and send the first part from
|
|
// the application buffer to avoid huge overhead of allocating
|
|
// non-paged pool and copying.
|
|
//
|
|
if (sendLength>=2*copyThreshold &&
|
|
!IS_MESSAGE_ENDPOINT (endpoint) &&
|
|
(!endpoint->NonBlocking || (afdFlags & AFD_OVERLAPPED ) ) ) {
|
|
sendOffset += sendLength-copyThreshold;
|
|
sendLength = copyThreshold;
|
|
}
|
|
//
|
|
// Get AFD buffer structure that contains an IRP and a
|
|
// buffer to hold the data.
|
|
//
|
|
|
|
retry_allocate:
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
afdBuffer = AfdGetBufferRaiseOnFailure (
|
|
endpoint,
|
|
sendLength,
|
|
0,
|
|
process );
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (status)) {
|
|
ASSERT (NT_ERROR (status));
|
|
//
|
|
// If we failed to get the buffer, and application request
|
|
// is larger than one page, and it is blocking or overlapped,
|
|
// and this is not a message-oriented socket,
|
|
// allocate space for last page only (if we can) and send
|
|
// the first portion from the app buffer.
|
|
//
|
|
|
|
if ( (sendLength>AfdBufferLengthForOnePage) &&
|
|
!IS_MESSAGE_ENDPOINT (endpoint) &&
|
|
(!endpoint->NonBlocking || (afdFlags & AFD_OVERLAPPED ) ) ) {
|
|
|
|
sendOffset += sendLength-AfdBufferLengthForOnePage;
|
|
sendLength = AfdBufferLengthForOnePage;
|
|
goto retry_allocate;
|
|
} // not qualified for partial allocation
|
|
else {
|
|
goto cleanup_buffer;
|
|
}
|
|
} // exception allocating big buffer.
|
|
|
|
currentOffset = sendOffset;
|
|
mdl = Irp->MdlAddress;
|
|
if (sendOffset!=0) {
|
|
//
|
|
// Adjust MDL length to be in sync with IRP
|
|
// send length parameter to not to confuse
|
|
// the transport
|
|
//
|
|
|
|
while (currentOffset>MmGetMdlByteCount (mdl)) {
|
|
currentOffset -= MmGetMdlByteCount (mdl);
|
|
mdl = mdl->Next;
|
|
}
|
|
}
|
|
|
|
if (sendLength != 0) {
|
|
status = AfdCopyMdlChainToBufferAvoidMapping(
|
|
mdl,
|
|
currentOffset,
|
|
sendLength,
|
|
afdBuffer->Buffer,
|
|
afdBuffer->BufferLength
|
|
);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
goto cleanup_buffer;
|
|
}
|
|
}
|
|
else {
|
|
ASSERT (IrpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength == 0);
|
|
}
|
|
|
|
}
|
|
else {
|
|
AFD_W4_INIT currentOffset = 0;
|
|
AFD_W4_INIT mdl = NULL;
|
|
}
|
|
|
|
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);
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto cleanup_buffer;
|
|
}
|
|
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
//
|
|
// Check whether the remote end has aborted the connection, in which
|
|
// case we should complete the receive.
|
|
//
|
|
|
|
if ( connection->Aborted ) {
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
status = STATUS_CONNECTION_RESET;
|
|
goto cleanup_buffer;
|
|
}
|
|
|
|
//
|
|
// Buffer sends if the TDI provider does not buffer
|
|
// and application did not specifically requested us not
|
|
// to do so
|
|
//
|
|
|
|
if ( !IS_TDI_BUFFERRING(endpoint)) {
|
|
if ( afdBuffer!=NULL ) {
|
|
BOOLEAN completeSend = FALSE;
|
|
PFILE_OBJECT fileObject = NULL;
|
|
|
|
if (connection->OwningProcess!=process) {
|
|
//
|
|
// Weird case when connection and endpoint belong to
|
|
// different processes.
|
|
//
|
|
|
|
process = connection->OwningProcess;
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
AfdReturnBuffer (&afdBuffer->Header, process);
|
|
afdBuffer = NULL;
|
|
goto retry_buffer;
|
|
}
|
|
|
|
ASSERT( !connection->TdiBufferring );
|
|
|
|
//
|
|
// First make sure that we don't have too many bytes of send
|
|
// data already outstanding and that someone else isn't already
|
|
// in the process of completing pended send IRPs. We can't
|
|
// issue the send here if someone else is completing pended
|
|
// sends because we have to preserve ordering of the sends.
|
|
//
|
|
// Note that we'll give the send data to the TDI provider even
|
|
// if we have exceeded our send buffer limits, but that we don't
|
|
// complete the user's IRP until some send buffer space has
|
|
// freed up. This effects flow control by blocking the user's
|
|
// thread while ensuring that the TDI provider always has lots
|
|
// of data available to be sent.
|
|
//
|
|
|
|
|
|
if ( connection->VcBufferredSendBytes >= connection->MaxBufferredSendBytes &&
|
|
endpoint->NonBlocking &&
|
|
!( afdFlags & AFD_OVERLAPPED ) &&
|
|
connection->VcBufferredSendBytes>0) {
|
|
//
|
|
// There is already as much send data bufferred on the
|
|
// connection as is allowed. If this is a nonblocking
|
|
// endpoint and this is not an overlapped operation and at least
|
|
// on byte is buferred, fail the request.
|
|
// Note, that we have already allocated the buffer and copied data
|
|
// and now we are dropping it. We should only be here in some
|
|
// really weird case when fast IO has been bypassed.
|
|
//
|
|
|
|
|
|
//
|
|
// Enable the send event.
|
|
//
|
|
|
|
endpoint->EventsActive &= ~AFD_POLL_SEND;
|
|
endpoint->EnableSendEvent = TRUE;
|
|
|
|
IF_DEBUG(EVENT_SELECT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdSend: Endp %p, Active %lx\n",
|
|
endpoint,
|
|
endpoint->EventsActive
|
|
));
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
status = STATUS_DEVICE_NOT_READY;
|
|
goto cleanup_buffer;
|
|
}
|
|
|
|
if (sendOffset==0) {
|
|
if ( connection->VcBufferredSendBytes >= connection->MaxBufferredSendBytes ) {
|
|
|
|
//
|
|
// Special hack to prevent completion of this IRP
|
|
// while we have not finished sending all the data
|
|
// that came with it. If we do not do this, the
|
|
// app can receive completion port notificaiton in
|
|
// another thread and come back with another send
|
|
// which can get in the middle of this one.
|
|
//
|
|
Irp->Tail.Overlay.DriverContext[0] = NULL;
|
|
|
|
//
|
|
// Set up the cancellation routine in the IRP. If the IRP
|
|
// has already been cancelled, just complete the IRP
|
|
//
|
|
|
|
IoSetCancelRoutine( Irp, AfdCancelSend );
|
|
|
|
if ( Irp->Cancel ) {
|
|
|
|
Irp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
|
|
if ( IoSetCancelRoutine( Irp, NULL ) == NULL ) {
|
|
IoMarkIrpPending (Irp);
|
|
Irp->Tail.Overlay.DriverContext[0] = (PVOID)-1;
|
|
//
|
|
// The cancel routine is running and will complete the IRP
|
|
//
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
AfdReturnBuffer (&afdBuffer->Header, process);
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
status = STATUS_CANCELLED;
|
|
goto cleanup_buffer;
|
|
}
|
|
|
|
//
|
|
// We're going to have to pend the request here in AFD.
|
|
// Place the IRP on the connection's list of pended send
|
|
// IRPs and mark the IRP as pended.
|
|
//
|
|
|
|
InsertTailList(
|
|
&connection->VcSendIrpListHead,
|
|
&Irp->Tail.Overlay.ListEntry
|
|
);
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
}
|
|
else {
|
|
//
|
|
// We are going to complete the IRP inline
|
|
//
|
|
completeSend = TRUE;
|
|
}
|
|
|
|
}
|
|
else {
|
|
connection->VcBufferredSendBytes += sendOffset;
|
|
connection->VcBufferredSendCount += 1;
|
|
|
|
//
|
|
// Special hack to prevent completion of this IRP
|
|
// while we have not finished sending all the data
|
|
// that came with it. If we do not do this, the
|
|
// app can receive completion port notificaiton in
|
|
// another thread and come back with another send
|
|
// which can get in the middle of this one.
|
|
//
|
|
fileObject = IrpSp->FileObject;
|
|
IrpSp->FileObject = NULL;
|
|
|
|
|
|
REFERENCE_CONNECTION2( connection, "AfdSend (split,non-buffered part), offset: 0x%lX", sendOffset );
|
|
}
|
|
|
|
//
|
|
// Update count of send bytes pending on the connection.
|
|
//
|
|
|
|
connection->VcBufferredSendBytes += sendLength;
|
|
connection->VcBufferredSendCount += 1;
|
|
|
|
//
|
|
// Reference the conneciton so it does not go away
|
|
// until we finish with send
|
|
//
|
|
REFERENCE_CONNECTION2( connection, "AfdSend (buffered), length: 0x%lX", sendLength );
|
|
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
//
|
|
// Remember the connection in the AFD buffer structure. We need
|
|
// this in order to access the connection in the restart routine.
|
|
//
|
|
|
|
afdBuffer->Context = connection;
|
|
|
|
|
|
//
|
|
// 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;
|
|
|
|
|
|
if (sendOffset==0) {
|
|
// 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,
|
|
sendFlags,
|
|
sendLength
|
|
);
|
|
|
|
//
|
|
// 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)) {
|
|
//
|
|
// Call the transport to actually perform the send.
|
|
//
|
|
|
|
status = IoCallDriver(connection->DeviceObject, afdBuffer->Irp );
|
|
}
|
|
else {
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// If we did not pend the Irp, complete it
|
|
//
|
|
if (completeSend) {
|
|
if (NT_SUCCESS (status)) {
|
|
ASSERT (Irp->IoStatus.Status == STATUS_SUCCESS);
|
|
ASSERT (Irp->IoStatus.Information == sendLength);
|
|
ASSERT ((status==STATUS_SUCCESS) || (status==STATUS_PENDING));
|
|
status = STATUS_SUCCESS; // We did not mark irp as
|
|
// pending, so returning
|
|
// STATUS_PENDING (most likely
|
|
// to be status returned by the
|
|
// transport) will really confuse
|
|
// io subsystem.
|
|
}
|
|
else {
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
UPDATE_CONN2 (connection, "AfdSend, bytes sent/status reported 0x%lX",
|
|
(NT_SUCCESS(Irp->IoStatus.Status)
|
|
? (ULONG)Irp->IoStatus.Information
|
|
: (ULONG)Irp->IoStatus.Status));
|
|
IoCompleteRequest (Irp, AfdPriorityBoost);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// We no longer need MDL in the IRP, free it.
|
|
// Do this before we release the ownership
|
|
// in InterlockedOperation below.
|
|
//
|
|
AfdDestroyMdlChain (Irp);
|
|
|
|
//
|
|
// Complete the IRP if it was completed by the transport
|
|
// and kept around to let us finish posting all the data
|
|
// originally submitted by the app before completing it
|
|
//
|
|
ASSERT (Irp->Tail.Overlay.DriverContext[0]==NULL
|
|
|| Irp->Tail.Overlay.DriverContext[0]==(PVOID)-1);
|
|
if (InterlockedExchangePointer (
|
|
&Irp->Tail.Overlay.DriverContext[0],
|
|
(PVOID)Irp)!=NULL) {
|
|
UPDATE_CONN2 (connection, "AfdSend, bytes sent reported 0x%lX",
|
|
(ULONG)Irp->IoStatus.Information);
|
|
IoCompleteRequest (Irp, AfdPriorityBoost);
|
|
}
|
|
|
|
status = STATUS_PENDING;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Save the original values to restore in
|
|
// completion routine.
|
|
//
|
|
|
|
IrpSp->Parameters.AfdRestartSendInfo.AfdMdlChain = mdl->Next;
|
|
IrpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength =
|
|
MmGetMdlByteCount (mdl);
|
|
|
|
//
|
|
// Note if we need to unmap MDL before completing
|
|
// the IRP if it is mapped by the transport.
|
|
//
|
|
if ((mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)==0) {
|
|
IrpSp->Parameters.AfdRestartSendInfo.AfdSendFlags |=
|
|
AFD_SEND_MDL_HAS_NOT_BEEN_MAPPED;
|
|
}
|
|
|
|
//
|
|
// Reset the last MDL to not confuse the transport
|
|
// with different length values in MDL and send parameters
|
|
//
|
|
mdl->ByteCount = currentOffset;
|
|
mdl->Next = NULL;
|
|
|
|
//
|
|
// Build and pass first portion of the data with original (app)
|
|
// IRP
|
|
//
|
|
TdiBuildSend(
|
|
Irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartSend,
|
|
connection,
|
|
Irp->MdlAddress,
|
|
sendFlags,
|
|
sendOffset
|
|
);
|
|
|
|
//
|
|
// 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, Irp, FALSE)) {
|
|
status = AfdIoCallDriver (endpoint,
|
|
connection->DeviceObject,
|
|
Irp);
|
|
}
|
|
else {
|
|
|
|
status = STATUS_PENDING;
|
|
}
|
|
//
|
|
// Build and pass buffered last page
|
|
//
|
|
|
|
TdiBuildSend(
|
|
afdBuffer->Irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartBufferSend,
|
|
afdBuffer,
|
|
afdBuffer->Mdl,
|
|
sendFlags,
|
|
sendLength
|
|
);
|
|
|
|
|
|
//
|
|
// Similar check for the second part of the IRP.
|
|
// There might be a slight problem here since we
|
|
// can end-up interleaving sends with another thread
|
|
//
|
|
if (endpoint->Irp==NULL ||
|
|
!AfdEnqueueTpSendIrp (endpoint, afdBuffer->Irp, TRUE)) {
|
|
IoCallDriver(connection->DeviceObject, afdBuffer->Irp );
|
|
}
|
|
|
|
|
|
//
|
|
// Complete the IRP if it was completed by the transport
|
|
// and kept around to let us finish posting all the data
|
|
// originally submitted by the app before completing it
|
|
//
|
|
ASSERT (fileObject!=NULL);
|
|
ASSERT (IrpSp->FileObject==NULL || IrpSp->FileObject==(PFILE_OBJECT)-1);
|
|
if (InterlockedExchangePointer (
|
|
(PVOID *)&IrpSp->FileObject,
|
|
fileObject)!=NULL) {
|
|
UPDATE_CONN2 (connection, "AfdSend(split), bytes sent reported 0x%lX",
|
|
(ULONG)Irp->IoStatus.Information);
|
|
IoCompleteRequest (Irp, AfdPriorityBoost);
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
else {
|
|
//
|
|
// Count sends pended in the provider too, so
|
|
// we do not buffer in excess and complete
|
|
// buffered application sends before the transport
|
|
// completes sends forwarded to it.
|
|
//
|
|
connection->VcBufferredSendBytes += sendLength;
|
|
connection->VcBufferredSendCount += 1;
|
|
}
|
|
}
|
|
else {
|
|
ASSERT (afdBuffer==NULL);
|
|
}
|
|
|
|
//
|
|
// Add a reference to the connection object since the send
|
|
// request will complete asynchronously.
|
|
//
|
|
|
|
REFERENCE_CONNECTION2( connection, "AfdSend (non-buffered), length: 0x%lX", sendLength );
|
|
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
TdiBuildSend(
|
|
Irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartSend,
|
|
connection,
|
|
Irp->MdlAddress,
|
|
sendFlags,
|
|
sendLength
|
|
);
|
|
|
|
|
|
//
|
|
// 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, Irp, FALSE)) {
|
|
|
|
//
|
|
// Call the transport to actually perform the send.
|
|
//
|
|
status = AfdIoCallDriver( endpoint, connection->DeviceObject, Irp );
|
|
}
|
|
else {
|
|
status = STATUS_PENDING;
|
|
}
|
|
return status;
|
|
|
|
cleanup_buffer:
|
|
if (afdBuffer!=NULL) {
|
|
AfdReturnBuffer (&afdBuffer->Header, process);
|
|
}
|
|
|
|
complete:
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, AfdPriorityBoost );
|
|
|
|
return status;
|
|
|
|
} // AfdSend
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_CONNECTION connection;
|
|
NTSTATUS status;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
connection = Context;
|
|
ASSERT( connection != NULL );
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
endpoint = connection->Endpoint;
|
|
ASSERT( endpoint != NULL );
|
|
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
|
endpoint->Type == AfdBlockTypeVcBoth );
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
|
|
IF_DEBUG(SEND) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartSend: send completed for IRP %p, endpoint %p, "
|
|
"status = %X\n",
|
|
Irp, Context, Irp->IoStatus.Status ));
|
|
}
|
|
|
|
AfdCompleteOutstandingIrp( endpoint, Irp );
|
|
|
|
if (IS_TDI_BUFFERRING (endpoint)) {
|
|
|
|
ASSERT (irpSp->FileObject!=NULL);
|
|
|
|
//
|
|
// If the request failed indicating that the send would have blocked,
|
|
// and the client issues a nonblocking send, remember that nonblocking
|
|
// sends won't work until we get a send possible indication. This
|
|
// is required for write polls to work correctly.
|
|
//
|
|
// If the status code is STATUS_REQUEST_NOT_ACCEPTED, then the
|
|
// transport does not want us to update our internal variable that
|
|
// remembers that nonblocking sends are possible. The transport
|
|
// will tell us when sends are or are not possible.
|
|
//
|
|
// !!! should we also say that nonblocking sends are not possible if
|
|
// a send is completed with fewer bytes than were requested?
|
|
|
|
if ( Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY ) {
|
|
|
|
//
|
|
// Reenable the send event.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
endpoint->EventsActive &= ~AFD_POLL_SEND;
|
|
endpoint->EnableSendEvent = TRUE;
|
|
|
|
IF_DEBUG(EVENT_SELECT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartSend: Endp %p, Active %lx\n",
|
|
endpoint,
|
|
endpoint->EventsActive
|
|
));
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
connection->VcNonBlockingSendPossible = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is a send IRP on a nonblocking endpoint and fewer bytes
|
|
// were actually sent than were requested to be sent, reissue
|
|
// another send for the remaining buffer space.
|
|
//
|
|
|
|
if ( !endpoint->NonBlocking && NT_SUCCESS(Irp->IoStatus.Status) &&
|
|
Irp->IoStatus.Information <
|
|
irpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength ) {
|
|
|
|
ASSERT( Irp->MdlAddress != NULL );
|
|
|
|
//
|
|
// Advance the MDL chain by the number of bytes actually sent.
|
|
//
|
|
|
|
Irp->MdlAddress = AfdAdvanceMdlChain(
|
|
Irp->MdlAddress,
|
|
(ULONG)Irp->IoStatus.Information
|
|
);
|
|
|
|
|
|
//
|
|
// Update our restart info.
|
|
//
|
|
|
|
irpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength -=
|
|
(ULONG)Irp->IoStatus.Information;
|
|
|
|
//
|
|
// Reissue the send.
|
|
//
|
|
|
|
TdiBuildSend(
|
|
Irp,
|
|
connection->FileObject->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartSend,
|
|
connection,
|
|
Irp->MdlAddress,
|
|
irpSp->Parameters.AfdRestartSendInfo.AfdSendFlags,
|
|
irpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength
|
|
);
|
|
|
|
UPDATE_CONN2 (connection, "Restarting incomplete send, bytes: 0x%lX",
|
|
(ULONG)Irp->IoStatus.Information);
|
|
|
|
status = AfdIoCallDriver(
|
|
endpoint,
|
|
connection->FileObject->DeviceObject,
|
|
Irp
|
|
);
|
|
|
|
IF_DEBUG(SEND) {
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartSend: IoCallDriver returned %lx\n",
|
|
status
|
|
));
|
|
}
|
|
}
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Restore the IRP to its former glory before completing it
|
|
// unless it is a non-blocking endpoint in which case
|
|
// we shouldn't have modified it in the first place and
|
|
// we also want to return the actual number of bytes sent
|
|
// by the transport.
|
|
//
|
|
|
|
if ( !endpoint->NonBlocking ) {
|
|
Irp->IoStatus.Information = irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength;
|
|
}
|
|
//
|
|
// Remove the reference added just before calling the transport.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION2( connection, "AfdRestartSend-tdib, sent/error: 0x%lX",
|
|
(NT_SUCCESS (Irp->IoStatus.Status)
|
|
? (ULONG)Irp->IoStatus.Information
|
|
: (ULONG)Irp->IoStatus.Status));
|
|
}
|
|
else {
|
|
|
|
AfdProcessBufferSend (connection, Irp);
|
|
//
|
|
// If we buffered last page of the send, adjust last MDL
|
|
// and fix returned byte count if necessary
|
|
//
|
|
|
|
if (Irp->MdlAddress!=irpSp->Parameters.AfdRestartSendInfo.AfdMdlChain) {
|
|
PMDL mdl = Irp->MdlAddress;
|
|
|
|
ASSERT (mdl!=NULL);
|
|
|
|
while (mdl->Next!=NULL) {
|
|
mdl = mdl->Next;
|
|
}
|
|
|
|
//
|
|
// Unmap the pages that could have been mapped by
|
|
// the transport before adjusting the MDL size back
|
|
// so that MM does not try to unmap more than was
|
|
// mapped by the transport.
|
|
//
|
|
|
|
if ((mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) &&
|
|
(irpSp->Parameters.AfdRestartSendInfo.AfdSendFlags &
|
|
AFD_SEND_MDL_HAS_NOT_BEEN_MAPPED)) {
|
|
MmUnmapLockedPages (mdl->MappedSystemVa, mdl);
|
|
}
|
|
|
|
mdl->ByteCount
|
|
= irpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength;
|
|
mdl->Next = irpSp->Parameters.AfdRestartSendInfo.AfdMdlChain;
|
|
|
|
//
|
|
// Remove the reference added just before calling the transport.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION2( connection, "AfdRestartSend-split, sent/error: 0x%lX",
|
|
(NT_SUCCESS (Irp->IoStatus.Status)
|
|
? (ULONG)Irp->IoStatus.Information
|
|
: (ULONG)Irp->IoStatus.Status));
|
|
|
|
if (NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
//
|
|
// Make sure that the TDI provider sent everything we requested that
|
|
// he send.
|
|
//
|
|
ASSERT (Irp->IoStatus.Information+(ULONG)AfdBufferLengthForOnePage==
|
|
irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength ||
|
|
Irp->IoStatus.Information+(ULONG)AfdBlockingSendCopyThreshold==
|
|
irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength);
|
|
Irp->IoStatus.Information =
|
|
irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength;
|
|
}
|
|
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Remove the reference added just before calling the transport.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION2( connection, "AfdRestartSend, sent/error: 0x%lX",
|
|
(NT_SUCCESS (Irp->IoStatus.Status)
|
|
? (ULONG)Irp->IoStatus.Information
|
|
: (ULONG)Irp->IoStatus.Status));
|
|
|
|
|
|
//
|
|
// Make sure that the TDI provider sent everything we requested that
|
|
// he send.
|
|
//
|
|
|
|
ASSERT (!NT_SUCCESS (Irp->IoStatus.Status) ||
|
|
(Irp->IoStatus.Information ==
|
|
irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If pending has be returned for this irp then mark the current
|
|
// stack as pending.
|
|
//
|
|
|
|
if ( Irp->PendingReturned ) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
//
|
|
// The send dispatch routine temporarily yanks the file
|
|
// object pointer if it wants to make sure that the IRP
|
|
// is not completed until it is fully done with it.
|
|
//
|
|
if (InterlockedExchangePointer (
|
|
(PVOID *)&irpSp->FileObject,
|
|
(PVOID)-1)==NULL) {
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
return STATUS_SUCCESS;
|
|
|
|
} // AfdRestartSend
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartBufferSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PAFD_BUFFER afdBuffer;
|
|
PAFD_CONNECTION connection;
|
|
#if REFERENCE_DEBUG
|
|
IO_STATUS_BLOCK ioStatus = Irp->IoStatus;
|
|
#endif
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
afdBuffer = Context;
|
|
ASSERT (IS_VALID_AFD_BUFFER (afdBuffer));
|
|
|
|
connection = afdBuffer->Context;
|
|
ASSERT( connection != NULL );
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
ASSERT( connection->ReferenceCount > 0 );
|
|
|
|
//
|
|
// Make sure that the TDI provider sent everything we requested that
|
|
// he send.
|
|
//
|
|
|
|
ASSERT( !NT_SUCCESS (Irp->IoStatus.Status)
|
|
|| (Irp->IoStatus.Information == afdBuffer->Mdl->ByteCount) );
|
|
|
|
//
|
|
// Process the Irp (note that Irp is part of the buffer)
|
|
//
|
|
AfdProcessBufferSend (connection, Irp);
|
|
|
|
//
|
|
// Now we can free the buffer
|
|
//
|
|
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
AfdReturnBuffer( &afdBuffer->Header, connection->OwningProcess );
|
|
|
|
|
|
//
|
|
// Remove the reference added just before calling the transport.
|
|
//
|
|
|
|
|
|
DEREFERENCE_CONNECTION2( connection, "AfdRestartBufferSend, sent/error: 0x%lX",
|
|
(NT_SUCCESS (ioStatus.Status)
|
|
? (ULONG)ioStatus.Information
|
|
: (ULONG)ioStatus.Status));
|
|
|
|
//
|
|
// Tell the IO system to stop processing IO completion for this IRP.
|
|
// becuase it belongs to our buffer structure and we do not want
|
|
// to have it freed
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
} // AfdRestartBufferSend
|
|
|
|
VOID
|
|
AfdProcessBufferSend (
|
|
PAFD_CONNECTION Connection,
|
|
PIRP Irp
|
|
)
|
|
{
|
|
|
|
PAFD_ENDPOINT endpoint;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PLIST_ENTRY listEntry;
|
|
PIRP irp;
|
|
BOOLEAN sendPossible;
|
|
PIRP disconnectIrp;
|
|
LIST_ENTRY irpsToComplete;
|
|
|
|
endpoint = Connection->Endpoint;
|
|
ASSERT( endpoint != NULL );
|
|
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
|
endpoint->Type == AfdBlockTypeVcBoth);
|
|
ASSERT( !IS_TDI_BUFFERRING(endpoint) );
|
|
|
|
|
|
IF_DEBUG(SEND) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdProcessBufferSend: send completed for IRP %p, connection %p, "
|
|
"status = %X\n",
|
|
Irp, Connection, Irp->IoStatus.Status ));
|
|
}
|
|
|
|
//
|
|
// Update the count of send bytes outstanding on the connection.
|
|
// Note that we must do this BEFORE we check to see whether there
|
|
// are any pended sends--otherwise, there is a timing window where
|
|
// a new send could come in, get pended, and we would not kick
|
|
// the sends here.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
ASSERT( Connection->VcBufferredSendBytes >= Irp->IoStatus.Information );
|
|
ASSERT( (Connection->VcBufferredSendCount & 0x8000) == 0 );
|
|
ASSERT( Connection->VcBufferredSendCount != 0 );
|
|
|
|
Connection->VcBufferredSendBytes -= (ULONG)Irp->IoStatus.Information;
|
|
Connection->VcBufferredSendCount -= 1;
|
|
|
|
//
|
|
// If the send failed, abort the connection.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(Irp->IoStatus.Status) ) {
|
|
|
|
disconnectIrp = Connection->VcDisconnectIrp;
|
|
if ( disconnectIrp != NULL ) {
|
|
Connection->VcDisconnectIrp = NULL;
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
|
|
AfdBeginAbort( Connection );
|
|
|
|
//
|
|
// If there was a disconnect IRP, rather than just freeing it
|
|
// give it to the transport. This will cause the correct cleanup
|
|
// stuff (dereferenvce objects, free IRP and disconnect context)
|
|
// to occur. Note that we do this AFTER starting to abort the
|
|
// Connection so that we do not confuse the other side.
|
|
//
|
|
|
|
if ( disconnectIrp != NULL ) {
|
|
IoCallDriver( Connection->DeviceObject, disconnectIrp );
|
|
}
|
|
|
|
AfdDeleteConnectedReference( Connection, FALSE );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Before we release the lock on the endpoint, remember
|
|
// if the number of bytes outstanding in the TDI provider exceeds
|
|
// the limit. We must grab this while holding the endpoint lock.
|
|
//
|
|
|
|
sendPossible = (BOOLEAN)(Connection->VcBufferredSendBytes<Connection->MaxBufferredSendBytes);
|
|
|
|
//
|
|
// If there are no pended sends on the connection, we're done. Tell
|
|
// the IO system to stop processing IO completion for this IRP.
|
|
//
|
|
|
|
if ( IsListEmpty( &Connection->VcSendIrpListHead ) ) {
|
|
|
|
//
|
|
// If there is no "special condition" on the endpoint, return
|
|
// immediately. We use the special condition indication so that
|
|
// we need only a single test in the typical case.
|
|
//
|
|
|
|
if ( !Connection->SpecialCondition ) {
|
|
|
|
ASSERT( Connection->TdiBufferring || Connection->VcDisconnectIrp == NULL );
|
|
ASSERT( Connection->ConnectedReferenceAdded );
|
|
|
|
//
|
|
// There are no sends outstanding on the Connection, so indicate
|
|
// that the endpoint is writable.
|
|
//
|
|
|
|
if (sendPossible) {
|
|
AfdIndicateEventSelectEvent(
|
|
endpoint,
|
|
AFD_POLL_SEND,
|
|
STATUS_SUCCESS
|
|
);
|
|
}
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
if (sendPossible) {
|
|
AfdIndicatePollEvent(
|
|
endpoint,
|
|
AFD_POLL_SEND,
|
|
STATUS_SUCCESS
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
disconnectIrp = Connection->VcDisconnectIrp;
|
|
if ( disconnectIrp != NULL && Connection->VcBufferredSendCount == 0 ) {
|
|
Connection->VcDisconnectIrp = NULL;
|
|
} else {
|
|
disconnectIrp = NULL;
|
|
if ( sendPossible ) {
|
|
AfdIndicateEventSelectEvent(
|
|
endpoint,
|
|
AFD_POLL_SEND,
|
|
STATUS_SUCCESS
|
|
);
|
|
}
|
|
}
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
//
|
|
// If there is a disconnect IRP, give it to the TDI provider.
|
|
//
|
|
|
|
if ( disconnectIrp != NULL ) {
|
|
IoCallDriver( Connection->DeviceObject, disconnectIrp );
|
|
}
|
|
else if ( sendPossible ) {
|
|
|
|
AfdIndicatePollEvent(
|
|
endpoint,
|
|
AFD_POLL_SEND,
|
|
STATUS_SUCCESS
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// If the connected reference delete is pending, attempt to
|
|
// remove it.
|
|
//
|
|
|
|
AfdDeleteConnectedReference( Connection, FALSE );
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Now loop completing as many pended sends as possible. Note that
|
|
// in order to avoid a nasty race condition (between this thread and
|
|
// a thread performing sends on this connection) we must build a local
|
|
// list of IRPs to complete while holding the endpoint
|
|
// spinlock. After that list is built then we can release the lock
|
|
// and scan the list to actually complete the IRPs.
|
|
//
|
|
// We complete sends when we fall below the send bufferring limits, OR
|
|
// when there is only a single send pended. We want to be agressive
|
|
// in completing the send if there is only one because we want to
|
|
// give applications every oppurtunity to get data down to us--we
|
|
// definitely do not want to incur excessive blocking in the
|
|
// application.
|
|
//
|
|
|
|
InitializeListHead( &irpsToComplete );
|
|
|
|
while ( (Connection->VcBufferredSendBytes <=
|
|
Connection->MaxBufferredSendBytes ||
|
|
Connection->VcSendIrpListHead.Flink ==
|
|
Connection->VcSendIrpListHead.Blink)
|
|
|
|
|
|
&&
|
|
|
|
!IsListEmpty( &Connection->VcSendIrpListHead ) ) {
|
|
|
|
//
|
|
// Take the first pended user send IRP off the connection's
|
|
// list of pended send IRPs.
|
|
//
|
|
|
|
listEntry = RemoveHeadList( &Connection->VcSendIrpListHead );
|
|
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
|
|
|
|
//
|
|
// Reset the cancel routine in the user IRP since we're about
|
|
// to complete it.
|
|
//
|
|
|
|
if ( IoSetCancelRoutine( irp, NULL ) == NULL ) {
|
|
//
|
|
// This IRP is about to be canceled. Look for another in the
|
|
// list. Set the Flink to NULL so the cancel routine knows
|
|
// it is not on the list.
|
|
//
|
|
|
|
irp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Append the IRP to the local list.
|
|
//
|
|
|
|
InsertTailList(
|
|
&irpsToComplete,
|
|
&irp->Tail.Overlay.ListEntry
|
|
);
|
|
|
|
}
|
|
|
|
if ( sendPossible ) {
|
|
|
|
AfdIndicateEventSelectEvent(
|
|
endpoint,
|
|
AFD_POLL_SEND,
|
|
STATUS_SUCCESS
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Now we can release the locks and scan the local list of IRPs
|
|
// we need to complete, and actually complete them.
|
|
//
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
while( !IsListEmpty( &irpsToComplete ) ) {
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
//
|
|
// Remove the first item from the IRP list.
|
|
//
|
|
|
|
listEntry = RemoveHeadList( &irpsToComplete );
|
|
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
|
|
|
|
//
|
|
// Complete the user's IRP with a successful status code. The IRP
|
|
// should already be set up with the correct status and bytes
|
|
// written count.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( irp );
|
|
|
|
#if DBG
|
|
if ( irp->IoStatus.Status == STATUS_SUCCESS ) {
|
|
ASSERT( irp->IoStatus.Information == irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength );
|
|
}
|
|
#endif
|
|
//
|
|
// The send dispatch routine puts NULL into this
|
|
// field if it wants to make sure that the IRP
|
|
// is not completed until it is fully done with it
|
|
//
|
|
if (InterlockedExchangePointer (
|
|
&irp->Tail.Overlay.DriverContext[0],
|
|
(PVOID)-1)!=NULL) {
|
|
UPDATE_CONN2 (Connection, "AfdProcessBufferSend, bytes sent reported 0x%lX",
|
|
(ULONG)irp->IoStatus.Information);
|
|
IoCompleteRequest( irp, AfdPriorityBoost );
|
|
}
|
|
}
|
|
|
|
if ( sendPossible ) {
|
|
|
|
AfdIndicatePollEvent(
|
|
endpoint,
|
|
AFD_POLL_SEND,
|
|
STATUS_SUCCESS
|
|
);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} // AfdProcessBufferSend
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartSendConnDatagram (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PAFD_SEND_CONN_DATAGRAM_CONTEXT context = Context;
|
|
PAFD_ENDPOINT endpoint = context->Endpoint;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
IF_DEBUG(SEND) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartSendConnDatagram: send conn completed for "
|
|
"IRP %p, endpoint %p, status = %X\n",
|
|
Irp, endpoint, Irp->IoStatus.Status ));
|
|
}
|
|
|
|
ASSERT (Irp->IoStatus.Status!=STATUS_SUCCESS ||
|
|
Irp->IoStatus.Information
|
|
==IoGetCurrentIrpStackLocation (Irp)->Parameters.AfdRestartSendInfo.AfdOriginalLength);
|
|
|
|
//
|
|
// Free the context structure we allocated earlier.
|
|
//
|
|
|
|
AfdCompleteOutstandingIrp( endpoint, Irp );
|
|
AFD_FREE_POOL(
|
|
context,
|
|
AFD_TDI_POOL_TAG
|
|
);
|
|
|
|
//
|
|
// If pending has be returned for this irp then mark the current
|
|
// stack as pending.
|
|
//
|
|
|
|
if ( Irp->PendingReturned ) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
DEREFERENCE_ENDPOINT2 (endpoint, "AfdRestartSendConnDatagram, status: 0x%lX", Irp->IoStatus.Status);
|
|
return STATUS_SUCCESS;
|
|
|
|
} // AfdRestartSendConnDatagram
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartSendTdiConnDatagram (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PAFD_ENDPOINT endpoint = Context;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
ASSERT (Irp->IoStatus.Status!=STATUS_SUCCESS ||
|
|
Irp->IoStatus.Information
|
|
==IoGetCurrentIrpStackLocation (Irp)->Parameters.AfdRestartSendInfo.AfdOriginalLength);
|
|
IF_DEBUG(SEND) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartSendTdiConnDatagram: send conn completed for "
|
|
"IRP %p, endpoint %p, status = %X\n",
|
|
Irp, endpoint, Irp->IoStatus.Status ));
|
|
}
|
|
|
|
AfdCompleteOutstandingIrp( endpoint, Irp );
|
|
|
|
//
|
|
// If pending has be returned for this irp then mark the current
|
|
// stack as pending.
|
|
//
|
|
|
|
if ( Irp->PendingReturned ) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
DEREFERENCE_ENDPOINT2 (endpoint, "AfdRestartSendTdiConnDatagram, status: 0x%lX", Irp->IoStatus.Status);
|
|
return STATUS_SUCCESS;
|
|
|
|
} // AfdRestartSendTdiConnDatagram
|
|
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
AfdSendDatagram (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PAFD_ENDPOINT endpoint;
|
|
PTRANSPORT_ADDRESS destinationAddress;
|
|
ULONG destinationAddressLength;
|
|
PAFD_BUFFER_TAG afdBuffer = NULL;
|
|
ULONG sendLength;
|
|
ULONG bufferCount;
|
|
|
|
//
|
|
// Make sure that the endpoint is in the correct state.
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_DGRAM_ENDPOINT(endpoint) );
|
|
|
|
|
|
if ( !IS_DGRAM_ENDPOINT (endpoint) ||
|
|
((endpoint->State != AfdEndpointStateBound )
|
|
&& (endpoint->State != AfdEndpointStateConnected)) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (Irp)) {
|
|
PAFD_SEND_DATAGRAM_INFO32 sendInfo32;
|
|
LPWSABUF32 bufferArray32;
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
|
|
sizeof(*sendInfo32) ) {
|
|
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
sendInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
ProbeForReadSmallStructure(
|
|
sendInfo32,
|
|
sizeof(*sendInfo32),
|
|
PROBE_ALIGNMENT32 (AFD_SEND_DATAGRAM_INFO32)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
bufferArray32 = UlongToPtr(sendInfo32->BufferArray);
|
|
bufferCount = sendInfo32->BufferCount;
|
|
destinationAddress =
|
|
UlongToPtr(sendInfo32->TdiConnInfo.RemoteAddress);
|
|
destinationAddressLength =
|
|
sendInfo32->TdiConnInfo.RemoteAddressLength;
|
|
|
|
|
|
//
|
|
// Create the MDL chain describing the WSABUF array.
|
|
// This will also validate the buffer array and individual
|
|
// buffers
|
|
//
|
|
|
|
status = AfdAllocateMdlChain32(
|
|
Irp, // Requestor mode passed along
|
|
bufferArray32,
|
|
bufferCount,
|
|
IoReadAccess,
|
|
&sendLength
|
|
);
|
|
if( !NT_SUCCESS(status) ) {
|
|
goto complete;
|
|
}
|
|
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
|
|
ASSERT (NT_ERROR (status));
|
|
//
|
|
// Exception accessing input structure.
|
|
//
|
|
|
|
goto complete;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Invalid input buffer length.
|
|
//
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
|
|
}
|
|
}
|
|
else
|
|
#endif _WIN64
|
|
{
|
|
PAFD_SEND_DATAGRAM_INFO sendInfo;
|
|
LPWSABUF bufferArray;
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
|
|
sizeof(*sendInfo) ) {
|
|
|
|
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
|
|
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
sendInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
ProbeForReadSmallStructure(
|
|
sendInfo,
|
|
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
|
|
//
|
|
|
|
bufferArray = sendInfo->BufferArray;
|
|
bufferCount = sendInfo->BufferCount;
|
|
destinationAddress =
|
|
sendInfo->TdiConnInfo.RemoteAddress;
|
|
destinationAddressLength =
|
|
sendInfo->TdiConnInfo.RemoteAddressLength;
|
|
|
|
|
|
|
|
//
|
|
// Create the MDL chain describing the WSABUF array.
|
|
// This will also validate the buffer array and individual
|
|
// buffers
|
|
//
|
|
|
|
status = AfdAllocateMdlChain(
|
|
Irp, // Requestor mode passed along
|
|
bufferArray,
|
|
bufferCount,
|
|
IoReadAccess,
|
|
&sendLength
|
|
);
|
|
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
goto complete;
|
|
}
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
|
|
ASSERT (NT_ERROR (status));
|
|
//
|
|
// Exception accessing input structure.
|
|
//
|
|
goto complete;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Invalid input buffer length.
|
|
//
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If send has been shut down on this endpoint, fail.
|
|
//
|
|
|
|
if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) ) {
|
|
status = STATUS_PIPE_DISCONNECTED;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Copy the destination address to the AFD buffer.
|
|
//
|
|
|
|
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
|
|
try {
|
|
|
|
//
|
|
// Get an AFD buffer to use for the request. We need this to
|
|
// hold the destination address for the datagram.
|
|
//
|
|
|
|
afdBuffer = AfdGetBufferTagRaiseOnFailure(
|
|
destinationAddressLength,
|
|
endpoint->OwningProcess );
|
|
//
|
|
// Probe the address buffer if it comes from the user mode
|
|
// application
|
|
//
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
ProbeForRead (
|
|
destinationAddress,
|
|
destinationAddressLength,
|
|
sizeof (UCHAR));
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
afdBuffer->TdiInfo.RemoteAddress,
|
|
destinationAddress,
|
|
destinationAddressLength
|
|
);
|
|
|
|
//
|
|
// 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)destinationAddressLength<
|
|
FIELD_OFFSET (TRANSPORT_ADDRESS,
|
|
Address[0].Address[((PTRANSPORT_ADDRESS)afdBuffer->TdiInfo.RemoteAddress)->Address[0].AddressLength])) {
|
|
ExRaiseStatus (STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
afdBuffer->TdiInfo.RemoteAddressLength = destinationAddressLength;
|
|
ASSERT (afdBuffer->TdiInfo.RemoteAddress !=NULL);
|
|
afdBuffer->TdiInfo.Options = NULL;
|
|
afdBuffer->TdiInfo.OptionsLength = 0;
|
|
afdBuffer->TdiInfo.UserData = NULL;
|
|
afdBuffer->TdiInfo.UserDataLength = 0;
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
|
|
ASSERT (NT_ERROR (status));
|
|
if (afdBuffer!=NULL) {
|
|
AfdReturnBuffer ( &afdBuffer->Header, endpoint->OwningProcess );
|
|
}
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Build the request to send the datagram.
|
|
//
|
|
|
|
REFERENCE_ENDPOINT2 (endpoint,"AfdSendDatagram, length: 0x%lX", sendLength);
|
|
afdBuffer->Context = endpoint;
|
|
#if DBG
|
|
//
|
|
// Store send length to check transport upon completion
|
|
//
|
|
IrpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength = sendLength;
|
|
#endif
|
|
|
|
TdiBuildSendDatagram(
|
|
Irp,
|
|
endpoint->AddressDeviceObject,
|
|
endpoint->AddressFileObject,
|
|
AfdRestartSendDatagram,
|
|
afdBuffer,
|
|
Irp->MdlAddress,
|
|
sendLength,
|
|
&afdBuffer->TdiInfo
|
|
);
|
|
|
|
IF_DEBUG(SEND) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdSendDatagram: SendDGInfo at %p, len = %ld\n",
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength ));
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdSendDatagram: remote address at %p, len = %ld\n",
|
|
destinationAddress,
|
|
destinationAddressLength ));
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdSendDatagram: output buffer length = %ld\n",
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength ));
|
|
}
|
|
|
|
//
|
|
// Call the transport to actually perform the send datagram.
|
|
//
|
|
|
|
return AfdIoCallDriver( endpoint, endpoint->AddressDeviceObject, Irp );
|
|
|
|
complete:
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, AfdPriorityBoost );
|
|
|
|
return status;
|
|
|
|
} // AfdSendDatagram
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartSendDatagram (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PAFD_BUFFER_TAG afdBuffer;
|
|
PAFD_ENDPOINT endpoint;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
afdBuffer = Context;
|
|
|
|
endpoint = afdBuffer->Context;
|
|
|
|
ASSERT( IS_DGRAM_ENDPOINT(endpoint) );
|
|
|
|
ASSERT (Irp->IoStatus.Status!=STATUS_SUCCESS ||
|
|
Irp->IoStatus.Information
|
|
==IoGetCurrentIrpStackLocation (Irp)->Parameters.AfdRestartSendInfo.AfdOriginalLength);
|
|
AfdCompleteOutstandingIrp( endpoint, Irp );
|
|
|
|
IF_DEBUG(SEND) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartSendDatagram: send datagram completed for "
|
|
"IRP %p, endpoint %p, status = %X\n",
|
|
Irp, Context, Irp->IoStatus.Status ));
|
|
}
|
|
|
|
//
|
|
// If pending has be returned for this irp then mark the current
|
|
// stack as pending.
|
|
//
|
|
|
|
if ( Irp->PendingReturned ) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess );
|
|
|
|
DEREFERENCE_ENDPOINT2 (endpoint, "AfdRestartSendDatagram, status: 0x%lX", Irp->IoStatus.Status);
|
|
return STATUS_SUCCESS;
|
|
|
|
} // AfdRestartSendDatagram
|
|
|
|
|
|
NTSTATUS
|
|
AfdSendPossibleEventHandler (
|
|
IN PVOID TdiEventContext,
|
|
IN PVOID ConnectionContext,
|
|
IN ULONG BytesAvailable
|
|
)
|
|
{
|
|
PAFD_CONNECTION connection;
|
|
PAFD_ENDPOINT endpoint;
|
|
BOOLEAN result;
|
|
|
|
UNREFERENCED_PARAMETER( TdiEventContext );
|
|
UNREFERENCED_PARAMETER( BytesAvailable );
|
|
|
|
connection = (PAFD_CONNECTION)ConnectionContext;
|
|
ASSERT( connection != NULL );
|
|
|
|
CHECK_REFERENCE_CONNECTION (connection, result);
|
|
if (!result) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
endpoint = connection->Endpoint;
|
|
ASSERT( endpoint != NULL );
|
|
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
ASSERT( IS_TDI_BUFFERRING(endpoint) );
|
|
ASSERT( connection->TdiBufferring );
|
|
|
|
IF_DEBUG(SEND) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdSendPossibleEventHandler: send possible on endpoint %p "
|
|
" conn %p bytes=%ld\n", endpoint, connection, BytesAvailable ));
|
|
}
|
|
|
|
//
|
|
// Remember that it is now possible to do a send on this connection.
|
|
//
|
|
|
|
if ( BytesAvailable != 0 ) {
|
|
|
|
connection->VcNonBlockingSendPossible = TRUE;
|
|
|
|
//
|
|
// Complete any outstanding poll IRPs waiting for a send poll.
|
|
//
|
|
|
|
// Make sure connection was accepted/connected to prevent
|
|
// indication on listening endpoint
|
|
//
|
|
|
|
if (connection->State==AfdConnectionStateConnected) {
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
AfdIndicateEventSelectEvent(
|
|
endpoint,
|
|
AFD_POLL_SEND,
|
|
STATUS_SUCCESS
|
|
);
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
ASSERT (endpoint->Type & AfdBlockTypeVcConnecting);
|
|
AfdIndicatePollEvent(
|
|
endpoint,
|
|
AFD_POLL_SEND,
|
|
STATUS_SUCCESS
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
connection->VcNonBlockingSendPossible = FALSE;
|
|
}
|
|
|
|
DEREFERENCE_CONNECTION (connection);
|
|
return STATUS_SUCCESS;
|
|
|
|
} // AfdSendPossibleEventHandler
|
|
|
|
|
|
VOID
|
|
AfdCancelSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancels a send IRP that is pended in AFD.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - not used.
|
|
|
|
Irp - the IRP to cancel.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PAFD_ENDPOINT endpoint;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
//
|
|
// Get the endpoint pointer from our IRP stack location and the
|
|
// connection pointer from the endpoint.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
endpoint = irpSp->FileObject->FsContext;
|
|
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
|
endpoint->Type == AfdBlockTypeVcBoth );
|
|
|
|
//
|
|
// Remove the IRP from the endpoint's IRP list if it has not been
|
|
// removed already
|
|
//
|
|
|
|
ASSERT (KeGetCurrentIrql ()==DISPATCH_LEVEL);
|
|
AfdAcquireSpinLockAtDpcLevel ( &endpoint->SpinLock, &lockHandle);
|
|
|
|
if ( Irp->Tail.Overlay.ListEntry.Flink != NULL ) {
|
|
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
|
}
|
|
|
|
//
|
|
// Release the cancel spin lock and complete the IRP with a
|
|
// cancellation status code.
|
|
//
|
|
|
|
AfdReleaseSpinLockFromDpcLevel ( &endpoint->SpinLock, &lockHandle);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
|
|
//
|
|
// The send dispatch routine puts NULL into this
|
|
// field if it wants to make sure that the IRP
|
|
// is not completed until it is fully done with it
|
|
//
|
|
if (InterlockedExchangePointer (
|
|
&Irp->Tail.Overlay.DriverContext[0],
|
|
(PVOID)-1)!=NULL) {
|
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|
IoCompleteRequest( Irp, AfdPriorityBoost );
|
|
}
|
|
else {
|
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|
}
|
|
return;
|
|
} // AfdCancelSend
|
|
|
|
|
|
BOOLEAN
|
|
AfdCleanupSendIrp (
|
|
PIRP Irp
|
|
)
|
|
{
|
|
//
|
|
// The send dispatch routine puts NULL into this
|
|
// field if it wants to make sure that the IRP
|
|
// is not completed until it is fully done with it
|
|
//
|
|
if (InterlockedExchangePointer (
|
|
&Irp->Tail.Overlay.DriverContext[0],
|
|
(PVOID)-1)!=NULL) {
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|