Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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;
}
}