|
|
/*++
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; } }
|