/*++ Copyright (c) 1998 Microsoft Corporation Module Name: sendrecv.c Abstract: This module contains the SPUDSendAndRecv service. Author: John Ballard (jballard) 21-Oct-1996 Revision History: Keith Moore (keithmo) 04-Feb-1998 Cleanup, added much needed comments. --*/ #include "spudp.h" #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, SPUDSendAndRecv ) #endif // // Public functions. // NTSTATUS SPUDSendAndRecv( HANDLE hSocket, struct _AFD_SEND_INFO *sendInfo, struct _AFD_RECV_INFO *recvInfo, PSPUD_REQ_CONTEXT reqContext ) /*++ Routine Description: Batch send & receive request. Arguments: hSocket - The target socket for the request. sendInfo - Information describing the send. recvInfo - Information describing the receive. reqContext - The user-mode context for the request. Return Value: NTSTATUS - Completion status. --*/ { NTSTATUS status; PFILE_OBJECT fileObject; IO_STATUS_BLOCK localIoStatus; PIRP irp; PIO_STACK_LOCATION irpSp; PSPUD_AFD_REQ_CONTEXT SpudReqContext; PVOID completionPort; // // Sanity check. // PAGED_CODE(); status = SPUD_ENTER_SERVICE( "SPUDSendAndRecv", TRUE ); if( !NT_SUCCESS(status) ) { return status; } BumpCount( CtrSendAndRecv ); // // SPUD doesn't support kernel-mode callers. In fact, we don't // even build the "system stubs" necessary to invoke SPUD from // kernel-mode. // ASSERT( ExGetPreviousMode() == UserMode ); try { // // Make sure we can write to reqContext // ProbeForWrite( reqContext, sizeof(SPUD_REQ_CONTEXT), sizeof(ULONG) ); // // Make initial status invalid // reqContext->IoStatus1.Status = 0xffffffff; reqContext->IoStatus1.Information = 0; reqContext->IoStatus2.Status = 0xffffffff; reqContext->IoStatus2.Information = 0; reqContext->ReqType = SendAndRecv; reqContext->KernelReqInfo = SPUD_INVALID_REQ_HANDLE; // // Make sure the buffer looks good // ProbeForRead( recvInfo, sizeof(*recvInfo), sizeof(ULONG) ); if( recvInfo->BufferCount < 1 ) { ExRaiseStatus( STATUS_INVALID_PARAMETER ); } ProbeForRead( recvInfo->BufferArray, sizeof(*recvInfo->BufferArray), sizeof(ULONG) ); ProbeForRead( recvInfo->BufferArray->buf, recvInfo->BufferArray->len, sizeof(UCHAR) ); } except( EXCEPTION_EXECUTE_HANDLER ) { status = GetExceptionCode(); SPUD_LEAVE_SERVICE( "SPUDSendAndRecv", status, FALSE ); return status; } // // Reference the socket handle // status = ObReferenceObjectByHandle( hSocket, 0L, *IoFileObjectType, UserMode, (PVOID *)&fileObject, NULL ); if( !NT_SUCCESS(status) ) { SPUD_LEAVE_SERVICE( "SPUDSendAndRecv", status, FALSE ); return status; } TRACE_OB_REFERENCE( fileObject ); // // If we haven't already cached the Device Object and FastIoControl // pointers, then do so now. // if( !SpudAfdDeviceObject ) { status = SpudGetAfdDeviceObject( fileObject ); if( !NT_SUCCESS(status) ) { TRACE_OB_DEREFERENCE( fileObject ); ObDereferenceObject( fileObject ); status = STATUS_INVALID_DEVICE_REQUEST; SPUD_LEAVE_SERVICE( "SPUDSendAndRecv", status, FALSE ); return status; } } // // Reference the completion port. // completionPort = SpudReferenceCompletionPort(); if( completionPort == NULL ) { TRACE_OB_DEREFERENCE( fileObject ); ObDereferenceObject( fileObject ); status = STATUS_INVALID_DEVICE_REQUEST; SPUD_LEAVE_SERVICE( "SPUDSendAndRecv", status, FALSE ); return status; } // // Let's check to see if fast io will work // if( SpudAfdFastIoDeviceControl( fileObject, TRUE, (PVOID)sendInfo, sizeof(AFD_SEND_INFO), NULL, 0, IOCTL_AFD_SEND, &localIoStatus, SpudAfdDeviceObject )) { BumpCount( CtrSendRecvFastSend ); // // Lets remember the completion status for this operation // try { reqContext->IoStatus1 = localIoStatus; } except( EXCEPTION_EXECUTE_HANDLER) { localIoStatus.Status = GetExceptionCode(); localIoStatus.Information = 0; } if( localIoStatus.Status == STATUS_SUCCESS ) { localIoStatus.Status = SpudAfdRecvFastReq( fileObject, recvInfo, reqContext ); } // // If everything completed without pending then we can queue // a completion packet to the port now. // // Note that we must not queue a completion packet if the // request is failing in-line. // if( localIoStatus.Status != STATUS_PENDING ) { if( NT_SUCCESS(localIoStatus.Status) ) { localIoStatus.Status = IoSetIoCompletion( SpudCompletionPort, // IoCompletion reqContext, // KeyContext NULL, // ApcContext STATUS_SUCCESS, // IoStatus 0xFFFFFFFF, // IoStatusInformation TRUE // Quota ); } TRACE_OB_DEREFERENCE( fileObject ); ObDereferenceObject( fileObject ); } // // At this point, we know the fast-path send has completed // in-line and the receive either completed in-line or has pended. // Since it is the receive code's responsibility to add any necessary // references to the completion port (and since we know the send // has not pended) we can remove the reference we added above. // SPUD_LEAVE_SERVICE( "SPUDSendAndRecv", localIoStatus.Status, TRUE ); return localIoStatus.Status; } BumpCount( CtrSendRecvSlowSend ); // // It looks like we will have to it the hard way. // We will now build an IRP for AFD. // KeClearEvent( &fileObject->Event ); // // Allocate and initialize the IRP. // irp = IoAllocateIrp( SpudAfdDeviceObject->StackSize, TRUE ); if( !irp ) { TRACE_OB_DEREFERENCE( fileObject ); ObDereferenceObject( fileObject ); status = STATUS_INSUFFICIENT_RESOURCES; SPUD_LEAVE_SERVICE( "SPUDSendAndRecv", status, TRUE ); return status; } status = SpudAllocateRequestContext( &SpudReqContext, reqContext, recvInfo, irp, NULL ); if( !NT_SUCCESS(status) ) { TRACE_OB_DEREFERENCE( fileObject ); ObDereferenceObject( fileObject ); IoFreeIrp( irp ); SPUD_LEAVE_SERVICE( "SPUDSendAndRecv", status, TRUE ); return status; } BumpCount( CtrSendRecvSlowRecv ); irp->RequestorMode = UserMode; irp->Tail.Overlay.OriginalFileObject = fileObject; irp->Tail.Overlay.Thread = PsGetCurrentThread(); IoQueueThreadIrp( irp ); irpSp = IoGetNextIrpStackLocation( irp ); irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; irpSp->FileObject = fileObject; irpSp->DeviceObject = SpudAfdDeviceObject; // irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0; irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(AFD_SEND_INFO); irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_AFD_SEND; irpSp->Parameters.DeviceIoControl.Type3InputBuffer = sendInfo; IoSetCompletionRoutine( irp, SpudAfdContinueRecv, SpudReqContext, TRUE, TRUE, TRUE ); IoCallDriver( SpudAfdDeviceObject, irp ); status = STATUS_PENDING; SPUD_LEAVE_SERVICE( "SPUDSendAndRecv", status, FALSE ); return status; } // SPUDSendAndRecv