windows-nt/Source/XPSP1/NT/net/sockets/winsock2/wsp/afdsys/send.c

2233 lines
67 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 = 0;
ULONG currentOffset;
PMDL mdl;
PAFD_CONNECTION connection;
PAFD_BUFFER afdBuffer = NULL;
ULONG sendFlags;
ULONG afdFlags;
ULONG bufferCount;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PEPROCESS process;
//
// 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) ) {
try {
//
// Validate the input structure if it comes from the user mode
// application
//
sendInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
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 = 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
);
} except( AFD_EXCEPTION_FILTER(&status) ) {
//
// Exception accessing input structure.
//
}
} else {
//
// Invalid input buffer length.
//
status = STATUS_INVALID_PARAMETER;
}
}
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) ) {
try {
//
// Validate the input structure if it comes from the user mode
// application
//
sendInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
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
);
} except( AFD_EXCEPTION_FILTER(&status) ) {
//
// Exception accessing input structure.
//
}
} else {
//
// Invalid input buffer length.
//
status = STATUS_INVALID_PARAMETER;
}
}
if( !NT_SUCCESS(status) ) {
goto complete;
}
if (IS_SAN_ENDPOINT(endpoint)) {
IrpSp->MajorFunction = IRP_MJ_WRITE;
IrpSp->Parameters.Read.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;
//
// Allocate space to hold the connection information structure
// we'll use on input.
//
try {
context = AFD_ALLOCATE_POOL_WITH_QUOTA(
NonPagedPool,
sizeof(*context),
AFD_TDI_POOL_TAG
);
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode ();
context = NULL;
goto complete;
}
REFERENCE_ENDPOINT2 (endpoint,"AfdSend, length", sendLength);
context->Endpoint = endpoint;
context->ConnectionInformation.UserDataLength = 0;
context->ConnectionInformation.UserData = NULL;
context->ConnectionInformation.OptionsLength = 0;
context->ConnectionInformation.Options = NULL;
context->ConnectionInformation.RemoteAddressLength =
endpoint->Common.Datagram.RemoteAddressLength;
context->ConnectionInformation.RemoteAddress =
endpoint->Common.Datagram.RemoteAddress;
//
// 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", sendLength);
TdiBuildSend(
Irp,
endpoint->AddressDeviceObject,
endpoint->AddressFileObject,
AfdRestartSendTdiConnDatagram,
endpoint,
Irp->MdlAddress,
0,
sendLength
);
}
//
// Call the transport to actually perform the send operation.
//
return AfdIoCallDriver(
endpoint,
endpoint->AddressDeviceObject,
Irp
);
}
process = endpoint->OwningProcess;
retry_buffer:
if (!IS_TDI_BUFFERRING(endpoint) &&
(!endpoint->DisableFastIoSend ||
(endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED )) ) ) {
//
// Get AFD buffer structure that contains an IRP and a
// buffer to hold the data.
//
try {
afdBuffer = AfdGetBufferRaiseOnFailure (
sendLength,
0,
process );
if ( Irp->MdlAddress != NULL ) {
status = AfdCopyMdlChainToBufferAvoidMapping(
Irp->MdlAddress,
0,
sendLength,
afdBuffer->Buffer,
afdBuffer->BufferLength
);
if (!NT_SUCCESS (status)) {
goto complete;
}
}
else {
ASSERT (sendLength==0);
ASSERT (IrpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength == 0);
}
}
except (AFD_EXCEPTION_FILTER (&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 ) ) ) {
try {
afdBuffer = AfdGetBufferRaiseOnFailure (
AfdBufferLengthForOnePage,
0,
process);
}
except (AFD_EXCEPTION_FILTER (&status)) {
goto complete;
}
sendOffset = sendLength-(ULONG)AfdBufferLengthForOnePage;
sendLength = (ULONG)AfdBufferLengthForOnePage;
currentOffset = sendOffset;
mdl = Irp->MdlAddress;
//
// 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;
}
status = AfdCopyMdlChainToBufferAvoidMapping(
mdl,
currentOffset,
sendLength,
afdBuffer->Buffer,
afdBuffer->BufferLength
);
if (!NT_SUCCESS (status)) {
goto complete;
}
} // not qualified for partial allocation
else {
goto complete;
}
} // exception allocating big buffer.
}
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 complete;
}
ASSERT( connection->Type == AfdBlockTypeConnection );
//
// Check whether the remote end has aborted the connection, in which
// case we should complete the receive.
//
if ( connection->AbortIndicated ) {
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
status = STATUS_CONNECTION_RESET;
goto complete;
}
//
// 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);
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 complete;
}
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 complete;
}
//
// 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:%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:%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;
//
// Copy the user's data into the AFD buffer. If the MDL in the
// IRP is NULL, then don't bother doing the copy--this is a
// send of length 0.
//
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
);
//
// Call the transport to actually perform the send.
//
status = IoCallDriver(connection->DeviceObject, afdBuffer->Irp );
//
// 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 don't need the MDL anymore, destroy it
// while we still own the IRP.
//
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
);
status = AfdIoCallDriver (endpoint,
connection->DeviceObject,
Irp);
//
// Build an pass buffered last page
//
TdiBuildSend(
afdBuffer->Irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartBufferSend,
afdBuffer,
afdBuffer->Mdl,
sendFlags,
sendLength
);
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:%lx", sendLength );
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
TdiBuildSend(
Irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartSend,
connection,
Irp->MdlAddress,
sendFlags,
sendLength
);
//
// Call the transport to actually perform the send.
//
return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp );
complete:
if (afdBuffer!=NULL) {
AfdReturnBuffer (&afdBuffer->Header, process);
}
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;
PMDL mdlChain;
PMDL nextMdl;
NTSTATUS status;
PIRP disconnectIrp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
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: %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: %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 =
irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength;
}
}
else {
//
// Remove the reference added just before calling the transport.
//
DEREFERENCE_CONNECTION2( connection, "AfdRestartSend, sent/error: %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
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:%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 = (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;
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", Irp->IoStatus.Status);
return STATUS_SUCCESS;
} // AfdRestartSendConnDatagram
NTSTATUS
AfdRestartSendTdiConnDatagram (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PAFD_ENDPOINT endpoint = Context;
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", 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) ) {
try {
//
// Validate the input structure if it comes from the user mode
// application
//
sendInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
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 = sendInfo32->BufferArray;
bufferCount = sendInfo32->BufferCount;
destinationAddress =
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
);
} except( AFD_EXCEPTION_FILTER(&status) ) {
//
// Exception accessing input structure.
//
}
} else {
//
// Invalid input buffer length.
//
status = STATUS_INVALID_PARAMETER;
}
}
else
#endif _WIN64
{
PAFD_SEND_DATAGRAM_INFO sendInfo;
LPWSABUF bufferArray;
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
sizeof(*sendInfo) ) {
try {
//
// Validate the input structure if it comes from the user mode
// application
//
sendInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
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
);
} except( AFD_EXCEPTION_FILTER(&status) ) {
//
// Exception accessing input structure.
//
}
} else {
//
// Invalid input buffer length.
//
status = STATUS_INVALID_PARAMETER;
}
}
if( !NT_SUCCESS(status) ) {
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.
//
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) ) {
if (afdBuffer!=NULL) {
AfdReturnBuffer ( &afdBuffer->Header, endpoint->OwningProcess );
}
goto complete;
}
//
// Build the request to send the datagram.
//
REFERENCE_ENDPOINT2 (endpoint,"AfdSendDatagram, length", 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;
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", 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;
//
// 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;
}
}