/*++ Copyright (c) 1989-1999 Microsoft Corporation Module Name: misc.c Abstract: This module contains the miscellaneous AFD routines. Author: David Treadwell (davidtr) 13-Nov-1992 Revision History: Vadim Eydelman (vadime) 1998-1999 Misc changes --*/ #include "afdp.h" #define TL_INSTANCE 0 #include #include #include #include VOID AfdDoWork ( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ); NTSTATUS AfdRestartDeviceControl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); VOID AfdUnlockDriver ( IN PVOID Context ); BOOLEAN AfdCompareAddresses( IN PTRANSPORT_ADDRESS Address1, IN ULONG Address1Length, IN PTRANSPORT_ADDRESS Address2, IN ULONG Address2Length ); NTSTATUS AfdCompleteTransportIoctl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); NTSTATUS AfdCompleteNBTransportIoctl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); BOOLEAN AfdCleanupTransportIoctl ( PAFD_ENDPOINT Endpoint, PAFD_REQUEST_CONTEXT RequestCtx ); BOOLEAN AfdCleanupNBTransportIoctl ( PAFD_ENDPOINT Endpoint, PAFD_REQUEST_CONTEXT RequestCtx ); #ifdef _WIN64 NTSTATUS AfdQueryHandles32 ( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS AfdSetQos32( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS AfdGetQos32( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS AfdNoOperation32( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); #endif VOID AfdLRListTimeout ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); VOID AfdProcessLRList ( PVOID Param ); VOID AfdLRStartTimer ( VOID ); #ifdef _AFD_VARIABLE_STACK_ VOID AfdCancelStackIncreaseIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS AfdRestartStackIncreaseIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); PIRP AfdGetStackIncreaseIrp ( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ); #endif //_AFD_VARIABLE_STACK_ #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, AfdCalcBufferArrayByteLength ) #pragma alloc_text( PAGE, AfdCopyBufferArrayToBuffer ) #pragma alloc_text( PAGE, AfdCopyBufferToBufferArray ) #pragma alloc_text( PAGE, AfdCopyMdlChainToBufferArray ) #pragma alloc_text( PAGEAFD, AfdMapMdlChain ) #pragma alloc_text( PAGEAFD, AfdCopyMdlChainToMdlChain ) #pragma alloc_text( PAGEAFD, AfdAdvanceMdlChain ) #pragma alloc_text( PAGEAFD, AfdAllocateMdlChain ) #pragma alloc_text( PAGE, AfdQueryHandles ) #pragma alloc_text( PAGE, AfdGetInformation ) #pragma alloc_text( PAGEAFD, AfdSetInformation ) #pragma alloc_text( PAGE, AfdSetSecurity ) #pragma alloc_text( PAGE, AfdGetSecurity ) #pragma alloc_text( PAGE, AfdSetInLineMode ) #pragma alloc_text( PAGE, AfdGetContext ) #pragma alloc_text( PAGE, AfdGetRemoteAddress ) #pragma alloc_text( PAGE, AfdSetContext ) #pragma alloc_text( PAGE, AfdIssueDeviceControl ) #pragma alloc_text( PAGE, AfdSetEventHandler ) #pragma alloc_text( PAGE, AfdInsertNewEndpointInList ) #pragma alloc_text( PAGE, AfdRemoveEndpointFromList ) #pragma alloc_text( PAGE, AfdQueryProviderInfo ) #pragma alloc_text( PAGE, AfdLockEndpointContext ) #pragma alloc_text( PAGE, AfdUnlockEndpointContext ) #pragma alloc_text( PAGEAFD, AfdCompleteIrpList ) #pragma alloc_text( PAGEAFD, AfdErrorEventHandler ) #pragma alloc_text( PAGEAFD, AfdErrorExEventHandler ) //#pragma alloc_text( PAGEAFD, AfdRestartDeviceControl ) // can't ever be paged! #pragma alloc_text( PAGEAFD, AfdGetConnectData ) #pragma alloc_text( PAGEAFD, AfdSetConnectData ) #pragma alloc_text( PAGEAFD, AfdFreeConnectDataBuffers ) #pragma alloc_text( PAGEAFD, AfdSaveReceivedConnectData ) // The routines below can be called when no endpoints are in the list //#pragma alloc_text( PAGEAFD, AfdDoWork ) //#pragma alloc_text( PAGEAFD, AfdQueueWorkItem ) #pragma alloc_text( PAGEAFD, AfdGetWorkerByRoutine ) #pragma alloc_text( PAGE, AfdProcessLRList) #pragma alloc_text( PAGEAFD, AfdLRListTimeout) #pragma alloc_text( PAGEAFD, AfdLRStartTimer) #pragma alloc_text( PAGEAFD, AfdLRListAddItem) // Re-enable paging of the routines below when // KeFlushQueuedDpcs is exported from kernel. //#pragma alloc_text( PAGEAFD, AfdTrimLookaside) //#pragma alloc_text( PAGEAFD, AfdCheckLookasideLists) #if DBG #pragma alloc_text( PAGEAFD, AfdRecordOutstandingIrpDebug ) #endif #pragma alloc_text( PAGE, AfdExceptionFilter ) #pragma alloc_text( PAGEAFD, AfdSetQos ) #pragma alloc_text( PAGE, AfdGetQos ) #pragma alloc_text( PAGE, AfdNoOperation ) #pragma alloc_text (PAGE, AfdValidateStatus) #pragma alloc_text( PAGEAFD, AfdValidateGroup ) #pragma alloc_text( PAGEAFD, AfdCompareAddresses ) #pragma alloc_text( PAGEAFD, AfdGetUnacceptedConnectData ) #pragma alloc_text( PAGE, AfdDoTransportIoctl ) #pragma alloc_text( PAGEAFD, AfdCancelIrp ) #ifdef _WIN64 #pragma alloc_text( PAGEAFD, AfdAllocateMdlChain32 ) #pragma alloc_text( PAGEAFD, AfdSetQos32 ) #pragma alloc_text( PAGE, AfdGetQos32 ) #pragma alloc_text( PAGE, AfdNoOperation32 ) #endif #ifdef _AFD_VARIABLE_STACK_ #pragma alloc_text( PAGE, AfdFixTransportEntryPointsForBigStackSize ) #pragma alloc_text( PAGEAFD, AfdCallDriverStackIncrease) #pragma alloc_text( PAGEAFD, AfdGetStackIncreaseIrpAndRecordIt) #pragma alloc_text( PAGEAFD, AfdGetStackIncreaseIrp) #pragma alloc_text( PAGEAFD, AfdCancelStackIncreaseIrp) #pragma alloc_text( PAGEAFD, AfdRestartStackIncreaseIrp) #endif // _AFD_VARIABLE_STACK_ #endif VOID AfdCompleteIrpList ( IN PLIST_ENTRY IrpListHead, IN PAFD_ENDPOINT Endpoint, IN NTSTATUS Status, IN PAFD_IRP_CLEANUP_ROUTINE CleanupRoutine OPTIONAL ) /*++ Routine Description: Completes a list of IRPs with the specified status. Arguments: IrpListHead - the head of the list of IRPs to complete. Endpoint - an endpoint which lock which protects the list of IRPs. Status - the status to use for completing the IRPs. CleanupRoutine - a pointer to an optional IRP cleanup routine called before the IRP is completed. Return Value: None. --*/ { PLIST_ENTRY listEntry; PIRP irp; AFD_LOCK_QUEUE_HANDLE lockHandle; AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle ); while ( !IsListEmpty( IrpListHead ) ) { // // Remove the first IRP from the list, get a pointer to // the IRP and reset the cancel routine in the IRP. The // IRP is no longer cancellable. // listEntry = RemoveHeadList( IrpListHead ); irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); 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; } // // If we have a cleanup routine, call it. // if( CleanupRoutine != NULL ) { if (!(CleanupRoutine)( irp )) { // // Cleanup routine indicated that IRP should not // be completed. // continue; } } // // We must release the locks in order to actually // complete the IRP. It is OK to release these locks // because we don't maintain any absolute pointer into // the list; the loop termination condition is just // whether the list is completely empty. // AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle ); // // Complete the IRP. // irp->IoStatus.Status = Status; irp->IoStatus.Information = 0; IoCompleteRequest( irp, AfdPriorityBoost ); // // Reacquire the locks and continue completing IRPs. // AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle ); } AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle ); return; } // AfdCompleteIrpList NTSTATUS AfdErrorEventHandler ( IN PVOID TdiEventContext, IN NTSTATUS Status ) { PAFD_ENDPOINT endpoint = TdiEventContext; BOOLEAN result; CHECK_REFERENCE_ENDPOINT (endpoint, result); if (!result) return STATUS_SUCCESS; switch (Status) { case STATUS_PORT_UNREACHABLE: AfdErrorExEventHandler (TdiEventContext, Status, NULL); break; default: KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL, "AfdErrorEventHandler called for endpoint %p\n", endpoint )); } DEREFERENCE_ENDPOINT (endpoint); return STATUS_SUCCESS; } NTSTATUS AfdErrorExEventHandler ( IN PVOID TdiEventContext, IN NTSTATUS Status, IN PVOID Context ) { PAFD_ENDPOINT endpoint = TdiEventContext; BOOLEAN result; CHECK_REFERENCE_ENDPOINT (endpoint, result); if (!result) return STATUS_SUCCESS; switch (Status) { case STATUS_PORT_UNREACHABLE: // // UDP uses error ex handler to report ICMP rejects // if (IS_DGRAM_ENDPOINT (endpoint) && !endpoint->Common.Datagram.DisablePUError) { AFD_LOCK_QUEUE_HANDLE lockHandle; PLIST_ENTRY listEntry; PIRP irp = NULL; PTRANSPORT_ADDRESS sourceAddress = Context; int sourceAddressLength; PAFD_BUFFER_TAG afdBuffer; if (sourceAddress!=NULL) { sourceAddressLength = FIELD_OFFSET(TRANSPORT_ADDRESS, Address[0].Address[sourceAddress->Address[0].AddressLength]); } else sourceAddressLength = 0; AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); // // First try to fail any of the receive IRPs // while (!IsListEmpty (&endpoint->ReceiveDatagramIrpListHead)) { listEntry = RemoveHeadList( &endpoint->ReceiveDatagramIrpListHead ); // // Get a pointer to the IRP and reset the cancel routine in // the IRP. The IRP is no longer cancellable. // irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); if ( IoSetCancelRoutine( irp, NULL ) != NULL ) { AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); irp->IoStatus.Status = Status; irp->IoStatus.Information = 0; AfdSetupReceiveDatagramIrp (irp, NULL, 0, NULL, 0, sourceAddress, sourceAddressLength, 0 ); IoCompleteRequest( irp, AfdPriorityBoost ); goto Exit; } else { // // 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; irp = NULL; } } ASSERT (irp==NULL); // // See if there are any PEEK IRPs // while (!IsListEmpty (&endpoint->PeekDatagramIrpListHead)) { listEntry = RemoveHeadList( &endpoint->PeekDatagramIrpListHead ); // // Get a pointer to the IRP and reset the cancel routine in // the IRP. The IRP is no longer cancellable. // irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); if ( IoSetCancelRoutine( irp, NULL ) != NULL ) { break; } else { // // 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; irp = NULL; } } // // If we can buffer this indication, do it // if (endpoint->DgBufferredReceiveBytes < endpoint->Common.Datagram.MaxBufferredReceiveBytes && (endpoint->DgBufferredReceiveBytes>0 || (endpoint->DgBufferredReceiveCount*sizeof (AFD_BUFFER_TAG)) < endpoint->Common.Datagram.MaxBufferredReceiveBytes) ) { afdBuffer = AfdGetBufferTag( sourceAddressLength, endpoint->OwningProcess ); if ( afdBuffer != NULL) { // // Save the status do distinguish this from // normal datagram IRP // afdBuffer->Status = Status; afdBuffer->DataLength = 0; afdBuffer->DatagramFlags = 0; afdBuffer->DataOffset = 0; RtlCopyMemory( afdBuffer->TdiInfo.RemoteAddress, sourceAddress, sourceAddressLength ); afdBuffer->TdiInfo.RemoteAddressLength = sourceAddressLength; // // Place the buffer on this endpoint's list of bufferred datagrams // and update the counts of datagrams and datagram bytes on the // endpoint. // InsertTailList( &endpoint->ReceiveDatagramBufferListHead, &afdBuffer->BufferListEntry ); endpoint->DgBufferredReceiveCount++; // // All done. Release the lock and tell the provider that we // took all the data. // AfdIndicateEventSelectEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS ); AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); // // Indicate that it is possible to receive on the endpoint now. // AfdIndicatePollEvent( endpoint, AFD_POLL_RECEIVE, STATUS_SUCCESS ); } else { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); } } else { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); } // // If there was a peek IRP on the endpoint, complete it now. // if ( irp != NULL ) { irp->IoStatus.Status = Status; irp->IoStatus.Information = 0; AfdSetupReceiveDatagramIrp (irp, NULL, 0, NULL, 0, sourceAddress, sourceAddressLength, 0 ); IoCompleteRequest( irp, AfdPriorityBoost ); } } break; } Exit: DEREFERENCE_ENDPOINT (endpoint); return STATUS_SUCCESS; } // AfdErrorEventHandler VOID AfdInsertNewEndpointInList ( IN PAFD_ENDPOINT Endpoint ) /*++ Routine Description: Inserts a new endpoint in the global list of AFD endpoints. If this is the first endpoint, then this routine does various allocations to prepare AFD for usage. Arguments: Endpoint - the endpoint being added. Return Value: None. --*/ { PAGED_CODE( ); // // Acquire a lock which prevents other threads from performing this // operation. // // // Make sure the thread in which we execute cannot get // suspeneded in APC while we own the global resource. // KeEnterCriticalRegion (); ExAcquireResourceExclusiveLite( AfdResource, TRUE ); InterlockedIncrement( &AfdEndpointsOpened ); // // If the list of endpoints is empty, do some allocations. // if ( IsListEmpty( &AfdEndpointListHead ) ) { // // Tell MM to revert to normal paging semantics. // if (!AfdLoaded) { MmResetDriverPaging( (PVOID)DriverEntry ); AfdLoaded = (PKEVENT)1; } // // Lock down the AFD section that cannot be pagable if any // sockets are open. // ASSERT( AfdDiscardableCodeHandle == NULL ); AfdDiscardableCodeHandle = MmLockPagableCodeSection( (PVOID)AfdGetBufferFast ); ASSERT( AfdDiscardableCodeHandle != NULL ); // // Add extra reference to afd device object so that the // driver cannot be unloaded while at least one endpoint // is in the list. // ObReferenceObject (AfdDeviceObject); // // Setup 30 sec timer to flush lookaside lists // if too many items are there for too long. // KeInitializeTimer (&AfdLookasideLists->Timer); KeInitializeDpc (&AfdLookasideLists->Dpc, AfdCheckLookasideLists, AfdLookasideLists); { LARGE_INTEGER dueTime; dueTime.QuadPart = -(30*1000*1000*10); KeSetTimerEx (&AfdLookasideLists->Timer, dueTime, 30*1000, &AfdLookasideLists->Dpc); } } ASSERT (AfdLoaded==(PKEVENT)1); // // Add the endpoint to the list(s). // InsertHeadList( &AfdEndpointListHead, &Endpoint->GlobalEndpointListEntry ); if( Endpoint->GroupType == GroupTypeConstrained ) { InsertHeadList( &AfdConstrainedEndpointListHead, &Endpoint->ConstrainedEndpointListEntry ); } // // Release the lock and return. // ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); return; } // AfdInsertNewEndpointInList VOID AfdRemoveEndpointFromList ( IN PAFD_ENDPOINT Endpoint ) /*++ Routine Description: Removes a new endpoint from the global list of AFD endpoints. If this is the last endpoint in the list, then this routine does various deallocations to save resource utilization. Arguments: Endpoint - the endpoint being removed. Return Value: None. --*/ { PAGED_CODE( ); // // Acquire a lock which prevents other threads from performing this // operation. // // // Make sure the thread in which we execute cannot get // suspeneded in APC while we own the global resource. // KeEnterCriticalRegion (); ExAcquireResourceExclusiveLite( AfdResource, TRUE ); InterlockedIncrement( &AfdEndpointsClosed ); // // Remove the endpoint from the list(s). // RemoveEntryList( &Endpoint->GlobalEndpointListEntry ); if( Endpoint->GroupType == GroupTypeConstrained ) { RemoveEntryList( &Endpoint->ConstrainedEndpointListEntry ); } // // If the list of endpoints is now empty, do some deallocations. // if ( IsListEmpty( &AfdEndpointListHead ) ) { // // Stop the timer that scans lookaside lists. // KeCancelTimer (&AfdLookasideLists->Timer); // // Make sure DPC is completed since we may need to reinitialize // it after we exit this routine and new endpoint is created again. // KeRemoveQueueDpc (&AfdLookasideLists->Dpc); // // Make sure that DPC routine has actually completed before // unlocking code section where this routine resides. // // Not exported from kernel - so don't put the routine // into the discardable code section until it is. // // KeFlushQueuedDpcs (); // // We don't need PnP stuff anymore. // AfdDeregisterPnPHandlers (NULL); // // Unlock the AFD section that can be pagable when no sockets // are open. // ASSERT( IsListEmpty( &AfdConstrainedEndpointListHead ) ); ASSERT( AfdDiscardableCodeHandle != NULL ); MmUnlockPagableImageSection( AfdDiscardableCodeHandle ); AfdDiscardableCodeHandle = NULL; // // Queue off an executive worker thread to unlock AFD. We do // this using special hacks in the AFD worker thread code so // that we don't need to acuire a spin lock after the unlock. // AfdQueueWorkItem( AfdUnlockDriver, &AfdUnloadWorker ); } // // Release the lock and return. // ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); return; } // AfdRemoveEndpointFromList VOID AfdUnlockDriver ( IN PVOID Context ) { UNREFERENCED_PARAMETER (Context); // // Acquire a lock which prevents other threads from performing this // operation. // // // Make sure the thread in which we execute cannot get // suspeneded in APC while we own the global resource. // KeEnterCriticalRegion (); ExAcquireResourceExclusiveLite( AfdResource, TRUE ); // // Test whether the endpoint list remains empty. If it is still // empty, we can proceed with unlocking the driver. If a new // endpoint has been placed on the list, then do not make AFD // pagable. // if ( IsListEmpty( &AfdEndpointListHead ) ) { // // Tell MM that it can page all of AFD as it desires. // if (AfdLoaded!=NULL && AfdLoaded!=(PKEVENT)1) { KeSetEvent (AfdLoaded, AfdPriorityBoost, FALSE); } else { MmPageEntireDriver( (PVOID)DriverEntry ); } AfdLoaded = NULL; } ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); } // AfdUnlockDriver NTSTATUS AfdQueryHandles ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) /*++ Routine Description: Returns information about the TDI handles corresponding to an AFD endpoint. NULL is returned for either the connection handle or the address handle (or both) if the endpoint does not have that particular object. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; AFD_HANDLE_INFO handleInfo; ULONG getHandleInfo; NTSTATUS status; UNREFERENCED_PARAMETER (IoctlCode); PAGED_CODE( ); // // Set up local pointers. // *Information = 0; endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); // // Make sure that the input and output buffers are large enough. // #ifdef _WIN64 if (IoIs32bitProcess (NULL)) { if ( InputBufferLength < sizeof(getHandleInfo) || OutputBufferLength < sizeof(AFD_HANDLE_INFO32) ) { return STATUS_BUFFER_TOO_SMALL; } } else #endif { if ( InputBufferLength < sizeof(getHandleInfo) || OutputBufferLength < sizeof(handleInfo) ) { return STATUS_BUFFER_TOO_SMALL; } } AFD_W4_INIT status = STATUS_SUCCESS; try { // // Validate the input structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForReadSmallStructure (InputBuffer, sizeof (getHandleInfo), PROBE_ALIGNMENT(ULONG)); } // // 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 // getHandleInfo = *((PULONG)InputBuffer); } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); return status; } // // If no handle information or invalid handle information was // requested, fail. // if ( (getHandleInfo & ~(AFD_QUERY_ADDRESS_HANDLE | AFD_QUERY_CONNECTION_HANDLE)) != 0 || getHandleInfo == 0 ) { return STATUS_INVALID_PARAMETER; } // // Initialize the output buffer. // handleInfo.TdiAddressHandle = NULL; handleInfo.TdiConnectionHandle = NULL; // // If the caller requested a TDI address handle and we have an // address handle for this endpoint, dupe the address handle to the // user process. // if ( (getHandleInfo & AFD_QUERY_ADDRESS_HANDLE) != 0 && (endpoint->State == AfdEndpointStateBound || endpoint->State == AfdEndpointStateConnected) && endpoint->AddressFileObject != NULL ) { // If transport does not support new TDI_SERVICE_FORCE_ACCESS_CHECK_FLAG // we get the maximum possible access for the handle so that helper // DLL can do what it wants with it. Of course this compromises the // security, but we can't enforce it without the transport cooperation. status = ObOpenObjectByPointer( endpoint->AddressFileObject, OBJ_CASE_INSENSITIVE, NULL, MAXIMUM_ALLOWED, *IoFileObjectType, (KPROCESSOR_MODE)((endpoint->TdiServiceFlags&TDI_SERVICE_FORCE_ACCESS_CHECK) ? RequestorMode : KernelMode), &handleInfo.TdiAddressHandle ); if ( !NT_SUCCESS(status) ) { return status; } } // // If the caller requested a TDI connection handle and we have a // connection handle for this endpoint, dupe the connection handle // to the user process. Note that we can have a connection and // TDI handle when endpoint is in process of being connected. // We should not return the connection handle until enpoint is // fully connected or it may go away while we are trying to // reference it if connection fails (bug 93096) // if ( (getHandleInfo & AFD_QUERY_CONNECTION_HANDLE) != 0 && (endpoint->Type & AfdBlockTypeVcConnecting) == AfdBlockTypeVcConnecting && endpoint->State == AfdEndpointStateConnected && ((connection=AfdGetConnectionReferenceFromEndpoint (endpoint))!=NULL)) { ASSERT( connection->Type == AfdBlockTypeConnection ); ASSERT( connection->FileObject != NULL ); // If transport does not support new TDI_SERVICE_FORCE_ACCESS_CHECK_FLAG // we get the maximum possible access for the handle so that helper // DLL can do what it wants with it. Of course this compromises the // security, but we can't enforce it without the transport cooperation. status = ObOpenObjectByPointer( connection->FileObject, OBJ_CASE_INSENSITIVE, NULL, MAXIMUM_ALLOWED, *IoFileObjectType, (KPROCESSOR_MODE)((endpoint->TdiServiceFlags & TDI_SERVICE_FORCE_ACCESS_CHECK) ? RequestorMode : KernelMode), &handleInfo.TdiConnectionHandle ); DEREFERENCE_CONNECTION (connection); if ( !NT_SUCCESS(status) ) { if ( handleInfo.TdiAddressHandle != NULL ) { // // Call ObCloseHandle directly (instead of ZwClose) to be able // to set PreviousMode. ZwClose goes thru TRAP which always // results in PreviousMode==KernelMode which will cause // bugcheck if app managed to close this handle between our // creating it and now // ObCloseHandle( handleInfo.TdiAddressHandle, RequestorMode ); } return status; } } AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { #ifdef _WIN64 if (IoIs32bitProcess (NULL)) { if (RequestorMode!=KernelMode) { ProbeForWrite (OutputBuffer, sizeof (AFD_HANDLE_INFO32), PROBE_ALIGNMENT32 (AFD_HANDLE_INFO32)); } ((PAFD_HANDLE_INFO32)OutputBuffer)->TdiAddressHandle = (VOID * POINTER_32)HandleToUlong(handleInfo.TdiAddressHandle); ((PAFD_HANDLE_INFO32)OutputBuffer)->TdiConnectionHandle = (VOID * POINTER_32)HandleToUlong(handleInfo.TdiConnectionHandle); *Information = sizeof (AFD_HANDLE_INFO32); } else #endif { if (RequestorMode!=KernelMode) { ProbeAndWriteStructure (((PAFD_HANDLE_INFO)OutputBuffer), handleInfo, AFD_HANDLE_INFO); } else { *((PAFD_HANDLE_INFO)OutputBuffer) = handleInfo; } *Information = sizeof (handleInfo); } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); if ( handleInfo.TdiAddressHandle != NULL ) { // // Call ObCloseHandle directly (instead of ZwClose) to be able // to set PreviousMode. ZwClose goes thru TRAP which always // results in PreviousMode==KernelMode which will cause // bugcheck if app managed to close this handle between our // creating it and now // ObCloseHandle( handleInfo.TdiAddressHandle, RequestorMode ); } if ( handleInfo.TdiConnectionHandle != NULL ) { // // Call ObCloseHandle directly (instead of ZwClose) to be able // to set PreviousMode. ZwClose goes thru TRAP which always // results in PreviousMode==KernelMode which will cause // bugcheck if app managed to close this handle between our // creating it and now // ObCloseHandle( handleInfo.TdiConnectionHandle, RequestorMode ); } return status; } return STATUS_SUCCESS; } // AfdQueryHandles NTSTATUS AfdGetInformation ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) /*++ Routine Description: Gets information in the endpoint. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; AFD_INFORMATION afdInfo; NTSTATUS status; LONGLONG currentTime; LONGLONG connectTime; UNREFERENCED_PARAMETER (IoctlCode); PAGED_CODE( ); // // Initialize number of bytes returned to zero. // *Information = 0; // // Initialize local variables. // endpoint = FileObject->FsContext; ASSERT(IS_AFD_ENDPOINT_TYPE(endpoint)); if (endpoint->Type==AfdBlockTypeHelper || endpoint->Type==AfdBlockTypeSanHelper) return STATUS_INVALID_PARAMETER; RtlZeroMemory(&afdInfo, sizeof(afdInfo)); status = STATUS_SUCCESS; // // Make sure that the input and output buffers are large enough. // #ifdef _WIN64 { C_ASSERT(sizeof(AFD_INFORMATION) == sizeof(AFD_INFORMATION32)); } #endif if ((InputBufferLength < sizeof(afdInfo)) || (OutputBufferLength < sizeof(afdInfo))) { return STATUS_BUFFER_TOO_SMALL; } AFD_W4_INIT ASSERT(status == STATUS_SUCCESS); try { #ifdef _WIN64 if (IoIs32bitProcess (NULL)) { // // Validate the input structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForReadSmallStructure (InputBuffer, sizeof (afdInfo), PROBE_ALIGNMENT32(AFD_INFORMATION32)); } // // 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 // afdInfo.InformationType = ((PAFD_INFORMATION32)InputBuffer)->InformationType; } else #endif _WIN64 { // // Validate the input structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForReadSmallStructure (InputBuffer, sizeof (afdInfo), PROBE_ALIGNMENT(AFD_INFORMATION)); } // // 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 // afdInfo.InformationType = ((PAFD_INFORMATION)InputBuffer)->InformationType; } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); return status; } // // Set up appropriate information in the endpoint. // switch ( afdInfo.InformationType ) { case AFD_MAX_PATH_SEND_SIZE: if (InputBufferLength>sizeof (afdInfo) && (endpoint->State==AfdEndpointStateBound || endpoint->State==AfdEndpointStateConnected)) { TDI_REQUEST_KERNEL_QUERY_INFORMATION kernelQueryInfo; TDI_CONNECTION_INFORMATION connectionInfo; PMDL mdl; InputBuffer = (PUCHAR)InputBuffer+sizeof (afdInfo); InputBufferLength -= sizeof (afdInfo); mdl = IoAllocateMdl( InputBuffer, // VirtualAddress InputBufferLength, // Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if (mdl!=NULL) { AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { MmProbeAndLockPages( mdl, // MemoryDescriptorList RequestorMode, // AccessMode IoWriteAccess // Operation ); status = STATUS_SUCCESS; } except (AFD_EXCEPTION_FILTER (status)) { ASSERT(NT_ERROR (status)); } if (NT_SUCCESS (status)) { connectionInfo.RemoteAddress = MmGetSystemAddressForMdlSafe (mdl, LowPagePriority); if (connectionInfo.RemoteAddress!=NULL) { connectionInfo.RemoteAddressLength = InputBufferLength; // // Set up a query to the TDI provider to obtain the largest // datagram that can be sent to a particular address. // kernelQueryInfo.QueryType = TDI_QUERY_MAX_DATAGRAM_INFO; kernelQueryInfo.RequestConnectionInformation = &connectionInfo; connectionInfo.UserDataLength = 0; connectionInfo.UserData = NULL; connectionInfo.OptionsLength = 0; connectionInfo.Options = NULL; // // Ask the TDI provider for the information. // status = AfdIssueDeviceControl( endpoint->AddressFileObject, &kernelQueryInfo, sizeof(kernelQueryInfo), &afdInfo.Information.Ulong, sizeof(afdInfo.Information.Ulong), TDI_QUERY_INFORMATION ); } else status = STATUS_INSUFFICIENT_RESOURCES; MmUnlockPages (mdl); } IoFreeMdl (mdl); } else status = STATUS_INSUFFICIENT_RESOURCES; // // If the request succeeds, use this information. Otherwise, // fall through and use the transport's global information. // This is done because not all transports support this // particular TDI request, and for those which do not the // global information is a reasonable approximation. // if ( NT_SUCCESS(status) ) { break; } } case AFD_MAX_SEND_SIZE: { // // With PnP some provider info fields can change over time. // so we query them each time we are asked. // TDI_PROVIDER_INFO providerInfo; status = AfdQueryProviderInfo ( &endpoint->TransportInfo->TransportDeviceName, #ifdef _AFD_VARIABLE_STACK_ NULL, #endif //_AFD_VARIABLE_STACK_ &providerInfo); if (NT_SUCCESS (status)) { // // Return the MaxSendSize or MaxDatagramSendSize from the // TDI_PROVIDER_INFO based on whether or not this is a datagram // endpoint. // if ( IS_DGRAM_ENDPOINT(endpoint) ) { afdInfo.Information.Ulong = providerInfo.MaxDatagramSize; } else { afdInfo.Information.Ulong = providerInfo.MaxSendSize; } } } break; case AFD_SENDS_PENDING: // // If this is an endpoint on a bufferring transport, no sends // are pending in AFD. If it is on a nonbufferring transport, // return the count of sends pended in AFD. // if ( IS_TDI_BUFFERRING(endpoint) || (endpoint->Type & AfdBlockTypeVcConnecting) != AfdBlockTypeVcConnecting || endpoint->State != AfdEndpointStateConnected || ((connection=AfdGetConnectionReferenceFromEndpoint (endpoint))==NULL)) { afdInfo.Information.Ulong = 0; } else { afdInfo.Information.Ulong = connection->VcBufferredSendCount; DEREFERENCE_CONNECTION (connection); } break; case AFD_RECEIVE_WINDOW_SIZE: // // Return the default receive window. // afdInfo.Information.Ulong = AfdReceiveWindowSize; break; case AFD_SEND_WINDOW_SIZE: // // Return the default send window. // afdInfo.Information.Ulong = AfdSendWindowSize; break; case AFD_CONNECT_TIME: // // If the endpoint is not yet connected, return -1. Otherwise, // calculate the number of seconds that the connection has been // active. // if ( endpoint->State != AfdEndpointStateConnected || IS_DGRAM_ENDPOINT (endpoint) || (connection=AfdGetConnectionReferenceFromEndpoint( endpoint ))==NULL) { afdInfo.Information.Ulong = 0xFFFFFFFF; } else { ASSERT( connection->Type == AfdBlockTypeConnection ); // // Calculate how long the connection has been active by // subtracting the time at which the connection started from // the current time. Note that we convert the units of the // time value from 100s of nanoseconds to seconds. // currentTime = KeQueryInterruptTime (); connectTime = (currentTime - connection->ConnectTime); connectTime /= 10*1000*1000; // // We can safely convert this to a ULONG because it takes // 127 years to overflow a ULONG counting seconds. The // bizarre conversion to a LARGE_INTEGER is required to // prevent the compiler from optimizing out the full 64-bit // division above. Without this, the compiler would do only // a 32-bit division and lose some information. // //afdInfo->Information.Ulong = (ULONG)connectTime; afdInfo.Information.Ulong = ((PLARGE_INTEGER)&connectTime)->LowPart; DEREFERENCE_CONNECTION (connection); } break; case AFD_GROUP_ID_AND_TYPE : { PAFD_GROUP_INFO groupInfo; groupInfo = (PAFD_GROUP_INFO)&afdInfo.Information.LargeInteger; // // Return the endpoint's group ID and group type. // groupInfo->GroupID = endpoint->GroupID; groupInfo->GroupType = endpoint->GroupType; } break; default: return STATUS_INVALID_PARAMETER; } try { #ifdef _WIN64 if (IoIs32bitProcess (NULL)) { // // Validate the input structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForWrite (OutputBuffer, sizeof (afdInfo), PROBE_ALIGNMENT32(AFD_INFORMATION32)); } // // Copy parameters back to application's memory // RtlMoveMemory(InputBuffer, &afdInfo, sizeof (afdInfo)); } else #endif _WIN64 { // // Validate the output structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeAndWriteStructure (((PAFD_INFORMATION)OutputBuffer), afdInfo, AFD_INFORMATION); } else { // // Copy parameters back to application's memory // *((PAFD_INFORMATION)OutputBuffer) = afdInfo; } } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); return status; } *Information = sizeof(afdInfo); return STATUS_SUCCESS; } // AfdGetInformation NTSTATUS AfdSetInformation ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) /*++ Routine Description: Sets information in the endpoint. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; AFD_INFORMATION afdInfo; NTSTATUS status; AFD_LOCK_QUEUE_HANDLE lockHandle; UNREFERENCED_PARAMETER (IoctlCode); UNREFERENCED_PARAMETER (OutputBuffer); UNREFERENCED_PARAMETER (OutputBufferLength); // // Nothing to return. // *Information = 0; // // Initialize locals for cleanup. // connection = NULL; status = STATUS_SUCCESS; // // Set up local pointers. // endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); if (endpoint->Type==AfdBlockTypeHelper || endpoint->Type==AfdBlockTypeSanHelper) return STATUS_INVALID_PARAMETER; // // Make sure that the input buffer is large enough. // #ifdef _WIN64 { C_ASSERT (sizeof (AFD_INFORMATION)==sizeof (AFD_INFORMATION32)); } #endif if ( InputBufferLength < sizeof(afdInfo) ) { return STATUS_BUFFER_TOO_SMALL; } AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { #ifdef _WIN64 if (IoIs32bitProcess (NULL)) { // // Validate the input structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForReadSmallStructure (InputBuffer, sizeof (afdInfo), PROBE_ALIGNMENT32(AFD_INFORMATION32)); } // // 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 // RtlMoveMemory (&afdInfo, InputBuffer, sizeof (afdInfo)); } else #endif _WIN64 { // // Validate the input structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForReadSmallStructure (InputBuffer, sizeof (afdInfo), PROBE_ALIGNMENT(AFD_INFORMATION)); } // // 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 // afdInfo = *((PAFD_INFORMATION)InputBuffer); } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); return status; } // // Set up appropriate information in the endpoint. // switch ( afdInfo.InformationType ) { case AFD_NONBLOCKING_MODE: // // Set the blocking mode of the endpoint. If TRUE, send and receive // calls on the endpoint will fail if they cannot be completed // immediately. // AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); endpoint->NonBlocking = (afdInfo.Information.Boolean!=FALSE); AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); break; case AFD_CIRCULAR_QUEUEING: // // Enables circular queuing on the endpoint. // if( !IS_DGRAM_ENDPOINT( endpoint ) ) { status = STATUS_INVALID_PARAMETER; goto Cleanup; } AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); endpoint->Common.Datagram.CircularQueueing = (afdInfo.Information.Boolean!=FALSE); AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); break; case AFD_REPORT_PORT_UNREACHABLE: // // Enables reporting PORT_UNREACHABLE to the app. // if( !IS_DGRAM_ENDPOINT( endpoint ) ) { status = STATUS_INVALID_PARAMETER; goto Cleanup; } AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); endpoint->Common.Datagram.DisablePUError = (afdInfo.Information.Boolean==FALSE); AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); break; case AFD_INLINE_MODE: // // Set the inline mode of the endpoint. If TRUE, a receive for // normal data will be completed with either normal data or // expedited data. If the endpoint is connected, we need to // tell the TDI provider that the endpoint is inline so that it // delivers data to us in order. If the endpoint is not yet // connected, then we will set the inline mode when we create // the TDI connection object. // if ( (endpoint->Type & AfdBlockTypeVcConnecting) == AfdBlockTypeVcConnecting ) { connection = AfdGetConnectionReferenceFromEndpoint( endpoint ); if (connection!=NULL) { status = AfdSetInLineMode( connection, afdInfo.Information.Boolean ); if ( !NT_SUCCESS(status) ) { goto Cleanup; } } } AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); endpoint->InLine = (afdInfo.Information.Boolean!=FALSE); AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); break; case AFD_RECEIVE_WINDOW_SIZE: case AFD_SEND_WINDOW_SIZE: { PCLONG maxBytes; AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); // // First determine where the appropriate limits are stored in the // connection or endpoint. We do this so that we can use common // code to charge quota and set the new counters. // if ( (endpoint->Type & AfdBlockTypeVcConnecting) == AfdBlockTypeVcConnecting && endpoint->State == AfdEndpointStateConnected && endpoint->Common.VcConnecting.Connection!=NULL ) { if ( afdInfo.InformationType == AFD_SEND_WINDOW_SIZE ) { maxBytes = &endpoint->Common.VcConnecting.Connection->MaxBufferredSendBytes; } else { maxBytes = &endpoint->Common.VcConnecting.Connection->MaxBufferredReceiveBytes; } } else if ( IS_DGRAM_ENDPOINT(endpoint) ) { if ( afdInfo.InformationType == AFD_SEND_WINDOW_SIZE ) { maxBytes = &endpoint->Common.Datagram.MaxBufferredSendBytes; } else { maxBytes = &endpoint->Common.Datagram.MaxBufferredReceiveBytes; } } else if (IS_SAN_ENDPOINT (endpoint) ) { AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); status = STATUS_SUCCESS; goto Cleanup; } else { AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Make sure that we always allow at least one message to be // bufferred on an endpoint. // if ( afdInfo.Information.Ulong == 0 ) { // // Don't allow the max receive bytes to go to zero, but // max send bytes IS allowed to go to zero because it has // special meaning: specifically, do not buffer sends. // if ( afdInfo.InformationType == AFD_RECEIVE_WINDOW_SIZE ) { afdInfo.Information.Ulong = 1; } else { ASSERT (afdInfo.InformationType == AFD_SEND_WINDOW_SIZE); endpoint->DisableFastIoSend = TRUE; } } else { if( afdInfo.InformationType == AFD_SEND_WINDOW_SIZE ) { endpoint->DisableFastIoSend = FALSE; } } // // Set up the new information in the AFD internal structure. // *maxBytes = (CLONG)afdInfo.Information.Ulong; AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); break; } default: status = STATUS_INVALID_PARAMETER; } Cleanup: if (connection!=NULL) { DEREFERENCE_CONNECTION (connection); } return status; } // AfdSetInformation NTSTATUS AfdSetSecurity ( IN PAFD_ENDPOINT Endpoint, IN SECURITY_INFORMATION SecurityInformation, IN PSECURITY_DESCRIPTOR SecurityDescriptor ) { NTSTATUS status; PSECURITY_DESCRIPTOR newSd, oldSd, cachedSd; PAGED_CODE (); ASSERT(Endpoint->TransportInfo || Endpoint->Type==AfdBlockTypeHelper || Endpoint->Type==AfdBlockTypeSanHelper); if (Endpoint->TransportInfo && Endpoint->TransportInfo->InfoValid) { // // Transport has already been loaded // ensure we have up to date flags // Endpoint->TdiServiceFlags = Endpoint->TransportInfo->ProviderInfo.ServiceFlags; if (!IS_TDI_ADDRESS_SECURITY (Endpoint)) { // // SD is not supported by the transport -> bail. // IO manager will still succeed the request and // will assume a World descriptor (as it does for FAT). // status = STATUS_INVALID_DEVICE_REQUEST; goto complete; } } else { // // We do not know yet if we can support this feature, fail it. // status = STATUS_NOT_IMPLEMENTED; goto complete; } // // Protect SD setting with state change lock. // if (!AFD_START_STATE_CHANGE (Endpoint, AfdEndpointStateOpen)) { status = STATUS_INVALID_DEVICE_REQUEST; goto complete; } if (Endpoint->State!=AfdEndpointStateOpen) { // // We may want to call transport if endpoint state // is bound or connected. // status = STATUS_NOT_IMPLEMENTED; goto complete_state_change; } // // Call the security routine to do the actual set // newSd = oldSd = Endpoint->SecurityDescriptor; if (newSd==NULL) { ACCESS_STATE accessState; AUX_ACCESS_DATA auxData; status = SeCreateAccessState (&accessState, &auxData, GENERIC_ALL, IoGetFileObjectGenericMapping()); if (NT_SUCCESS (status)) { SeLockSubjectContext (&accessState.SubjectSecurityContext); status = SeAssignSecurity ( NULL, // Parent SD - not used SecurityDescriptor, &newSd, FALSE, &accessState.SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool); SeUnlockSubjectContext (&accessState.SubjectSecurityContext); SeDeleteAccessState (&accessState); } } else { status = SeSetSecurityDescriptorInfo( NULL, &SecurityInformation, SecurityDescriptor, &newSd, PagedPool, IoGetFileObjectGenericMapping() ); } if (NT_SUCCESS(status)) { status = ObLogSecurityDescriptor (newSd, &cachedSd, 1); ExFreePool (newSd); if (NT_SUCCESS(status)) { Endpoint->SecurityDescriptor = cachedSd; if (oldSd!=NULL) { ObDereferenceSecurityDescriptor( oldSd, 1 ); } } } complete_state_change: AFD_END_STATE_CHANGE (Endpoint); complete: return status; } NTSTATUS AfdGetSecurity ( IN PAFD_ENDPOINT Endpoint, IN SECURITY_INFORMATION SecurityInformation, IN ULONG BufferLength, OUT PVOID Buffer, OUT PSIZE_T DataLength ) { NTSTATUS status; PSECURITY_DESCRIPTOR sd; PAGED_CODE (); ASSERT(Endpoint->TransportInfo || Endpoint->Type==AfdBlockTypeHelper || Endpoint->Type==AfdBlockTypeSanHelper); if (Endpoint->TransportInfo && Endpoint->TransportInfo->InfoValid) { // // Transport has already been loaded // ensure we have up to date flags // Endpoint->TdiServiceFlags = Endpoint->TransportInfo->ProviderInfo.ServiceFlags; if (!IS_TDI_ADDRESS_SECURITY (Endpoint)) { // // SD is not supported by the transport -> bail. // IO manager will still succeed the request and // will return a World descriptor (as it does for FAT). // status = STATUS_INVALID_DEVICE_REQUEST; goto complete; } } else { // // We do not know yet if we can support this feature -> bail. // IO manager will still succeed the request and // will return a World descriptor (as it does for FAT). // status = STATUS_INVALID_DEVICE_REQUEST; goto complete; } if (!AFD_PREVENT_STATE_CHANGE (Endpoint)) { // // IO manager will still succeed the request and // will return a World descriptor (as it does for FAT). // status = STATUS_INVALID_DEVICE_REQUEST; goto complete; } if (Endpoint->State!=AfdEndpointStateOpen) { // // IO manager will still succeed the request and // will return a World descriptor (as it does for FAT). // status = STATUS_INVALID_DEVICE_REQUEST; goto complete_state_change; } sd = Endpoint->SecurityDescriptor; if (sd==NULL) { ACCESS_STATE accessState; AUX_ACCESS_DATA auxData; status = SeCreateAccessState (&accessState, &auxData, GENERIC_ALL, IoGetFileObjectGenericMapping()); if (!NT_SUCCESS (status)) { goto complete_state_change; } SeLockSubjectContext (&accessState.SubjectSecurityContext); status = SeAssignSecurity ( NULL, // Parent SD - not used NULL, &sd, FALSE, &accessState.SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool); SeUnlockSubjectContext (&accessState.SubjectSecurityContext); SeDeleteAccessState (&accessState); } // // Call the security routine to do the actual query // status = SeQuerySecurityDescriptorInfo( &SecurityInformation, Buffer, &BufferLength, &sd ); if (status == STATUS_BUFFER_TOO_SMALL ) { *DataLength = BufferLength; status = STATUS_BUFFER_OVERFLOW; } if (sd!=Endpoint->SecurityDescriptor) { ExFreePool (sd); } complete_state_change: AFD_REALLOW_STATE_CHANGE (Endpoint); complete: return status; } NTSTATUS AfdSetInLineMode ( IN PAFD_CONNECTION Connection, IN BOOLEAN InLine ) /*++ Routine Description: Sets a connection to be in inline mode. In inline mode, urgent data is delivered in the order in which it is received. We must tell the TDI provider about this so that it indicates data in the proper order. Arguments: Connection - the AFD connection to set as inline. InLine - TRUE to enable inline mode, FALSE to disable inline mode. Return Value: NTSTATUS -- Indicates whether the request was successfully performed. --*/ { // // Since TCP does not implement this correctly, do everything in AFD!!! // Background: // When this options is enabled, TCP indicates all the data as normal // data, so we end up mixing it together which is against the spec. // Also, since TCP stops reporting expedited data, SIOATMARK fails // to report presence of OOB data altogether. // When handling OOB data completely inside AFD we can only run into // one problem: if AFD runs out of its receive buffer for the socket // and refuses to accept more data from TCP so that TCP buffers it // within itself, any OOB data arriving at this point can be indicated // out of order (not inline). // // Well, this appears to be even worse. Some apps (SQL) send more than // one byte of OOB data, TCP can only send one, so it sends everything // but the last byte as normal and the last one as OOB. It then turns // around and indicates the OOB (last byte) first which breaks the // ordering required by OOBINLINE. // In the end, we are broken one way or the other, so keep the things // the way they were for number of years and wait for TCP to fix. NTSTATUS status; PTCP_REQUEST_SET_INFORMATION_EX setInfoEx; TCPSocketOption *option; UCHAR buffer[sizeof(*setInfoEx) + sizeof(*option)]; IO_STATUS_BLOCK ioStatusBlock; KEVENT event; PIRP irp; PIO_STACK_LOCATION irpSp; PAGED_CODE( ); // // Initialize the TDI information buffers. // setInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)buffer; setInfoEx->ID.toi_entity.tei_entity = CO_TL_ENTITY; setInfoEx->ID.toi_entity.tei_instance = TL_INSTANCE; setInfoEx->ID.toi_class = INFO_CLASS_PROTOCOL; setInfoEx->ID.toi_type = INFO_TYPE_CONNECTION; setInfoEx->ID.toi_id = TCP_SOCKET_OOBINLINE; setInfoEx->BufferSize = sizeof(*option); option = (TCPSocketOption *)&setInfoEx->Buffer; option->tso_value = InLine; // // Initialize the kernel event that will signal I/O completion. // KeInitializeEvent( &event, SynchronizationEvent, FALSE ); // // Build TDI set information IRP. // irp = IoBuildDeviceIoControlRequest ( IOCTL_TCP_SET_INFORMATION_EX, Connection->DeviceObject, setInfoEx, sizeof(*setInfoEx) + setInfoEx->BufferSize, NULL, 0, FALSE, // InternalDeviceIoControl &event, &ioStatusBlock); if (irp==NULL) { return STATUS_INSUFFICIENT_RESOURCES; } irpSp = IoGetNextIrpStackLocation (irp); irpSp->FileObject = Connection->FileObject; // // Call the driver. // status = IoCallDriver (Connection->DeviceObject, irp); // // Must be at below APC level or this IRP will never get fully completed. // ASSERT (KeGetCurrentIrql ()ID.toi_entity.tei_entity = CO_TL_ENTITY; setInfoEx->ID.toi_entity.tei_instance = TL_INSTANCE; setInfoEx->ID.toi_class = INFO_CLASS_PROTOCOL; setInfoEx->ID.toi_type = INFO_TYPE_ADDRESS_OBJECT; setInfoEx->ID.toi_id = AO_OPTION_UNBIND; setInfoEx->BufferSize = sizeof (BOOLEAN); *((BOOLEAN *)&setInfoEx->Buffer) = TRUE; // // Initialize the kernel event that will signal I/O completion. // KeInitializeEvent( &event, SynchronizationEvent, FALSE ); // // Build TDI set information IRP. // irp = IoBuildDeviceIoControlRequest ( IOCTL_TCP_SET_INFORMATION_EX, Endpoint->AddressDeviceObject, setInfoEx, sizeof(*setInfoEx) + setInfoEx->BufferSize, NULL, 0, FALSE, // InternalDeviceIoControl &event, &ioStatusBlock); if (irp==NULL) { return STATUS_INSUFFICIENT_RESOURCES; } irpSp = IoGetNextIrpStackLocation (irp); irpSp->FileObject = Endpoint->AddressFileObject; // // Call the driver. // status = IoCallDriver (Endpoint->AddressDeviceObject, irp); // // Must be at below APC level or this IRP will never get fully completed. // ASSERT (KeGetCurrentIrql ()Context; // // See if someone else is manipulating the context. // if ((context==AFD_CONTEXT_BUSY) || (context==AFD_CONTEXT_WAITING)) { // // If this has not changed while we were checking, // tell the current owner that we are waiting (if not // already told) and wait for a few miliseconds. // if (InterlockedCompareExchangePointer ( (PVOID *)&Endpoint->Context, AFD_CONTEXT_WAITING, context)==context) { NTSTATUS status; LARGE_INTEGER afd10Milliseconds = {(ULONG)(-10 * 1000 * 10), -1}; KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdLockEndpointContext: Waiting for endp %p\n", Endpoint)); KeLeaveCriticalRegion (); status = KeWaitForSingleObject( (PVOID)&AfdContextWaitEvent, Executive, KernelMode, FALSE, &afd10Milliseconds); KeEnterCriticalRegion (); } else { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdLockEndpointContext: ICEP contention on %p\n", Endpoint)); } // // Try again. // } else { // // Context is not owned, try to get the ownership // if (InterlockedCompareExchangePointer ( (PVOID *)&Endpoint->Context, AFD_CONTEXT_BUSY, context)==context) { // // We now own the context, return it. // break; } // // Try again. // KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdLockEndpointContext: ICEP contention on %p\n", Endpoint)); } } return context; } VOID AfdUnlockEndpointContext ( PAFD_ENDPOINT Endpoint, PVOID Context ) { PAGED_CODE (); ASSERT ((Context!=AFD_CONTEXT_BUSY) && (Context!=AFD_CONTEXT_WAITING)); // // Set the new context pointer and see what the old value was. // Context = InterlockedExchangePointer ((PVOID)&Endpoint->Context, Context); if (Context==AFD_CONTEXT_WAITING) { LONG prevState; KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdUnlockEndpointContext: Unwaiting endp %p\n", Endpoint)); // // Someone was waiting, tell them to go get it now. // prevState = KePulseEvent (&AfdContextWaitEvent, AfdPriorityBoost, FALSE ); ASSERT (prevState==0); } else { // // Better be busy or someone has changed it on us. // ASSERT (Context==AFD_CONTEXT_BUSY); } KeLeaveCriticalRegion (); } NTSTATUS AfdGetContext ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) { PAFD_ENDPOINT endpoint; PVOID context; NTSTATUS status; UNREFERENCED_PARAMETER (IoctlCode); UNREFERENCED_PARAMETER (InputBuffer); UNREFERENCED_PARAMETER (InputBufferLength); PAGED_CODE( ); // // Set up local pointers. // endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); *Information = 0; context = AfdLockEndpointContext (endpoint); // // Make sure that the output buffer is large enough to hold all the // context information for this socket. // // // If there is no context, return nothing. // if ( context == NULL ) { status = STATUS_INVALID_PARAMETER; } // // Return the context information we have stored for this endpoint. // else { // // If application buffer is too small, just // copy whatever fits in and return the error code. // if ( OutputBufferLength < endpoint->ContextLength ) { status = STATUS_BUFFER_OVERFLOW; } else { OutputBufferLength = endpoint->ContextLength; if (IS_SAN_ENDPOINT (endpoint)) { // // Indicate to the caller that it may also need to // acqiure the control of the endpoint and // fetch san specific information. // status = STATUS_MORE_ENTRIES; } else { status = STATUS_SUCCESS; } } try { // // Validate the output structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForWrite (OutputBuffer, OutputBufferLength, sizeof (UCHAR)); } // // Copy parameters back to application's memory // RtlCopyMemory( OutputBuffer, context, OutputBufferLength ); *Information = endpoint->ContextLength; } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); } } AfdUnlockEndpointContext (endpoint, context); return status; } // AfdGetContext NTSTATUS AfdGetRemoteAddress ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) { PAFD_ENDPOINT endpoint; PVOID context; NTSTATUS status; UNREFERENCED_PARAMETER (IoctlCode); UNREFERENCED_PARAMETER (InputBuffer); UNREFERENCED_PARAMETER (InputBufferLength); PAGED_CODE( ); // // Set up local pointers. // endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); *Information = 0; context = AfdLockEndpointContext (endpoint); // // If there is no context or endpoint is of wrong type state or // context information has been changed below the original size, // return error. // if ( context == NULL || endpoint->Type!=AfdBlockTypeVcConnecting || endpoint->State!= AfdEndpointStateConnected || ((CLONG)(endpoint->Common.VcConnecting.RemoteSocketAddressOffset+ endpoint->Common.VcConnecting.RemoteSocketAddressLength)) > endpoint->ContextLength ) { status = STATUS_INVALID_CONNECTION; } else { if (OutputBufferLengthCommon.VcConnecting.RemoteSocketAddressLength) { status = STATUS_BUFFER_OVERFLOW; } else { OutputBufferLength = endpoint->Common.VcConnecting.RemoteSocketAddressLength; status = STATUS_SUCCESS; } try { // // Validate the output structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForWrite (OutputBuffer, OutputBufferLength, sizeof (UCHAR)); } // // Copy parameters to application's memory // RtlCopyMemory( OutputBuffer, (PUCHAR)context+endpoint->Common.VcConnecting.RemoteSocketAddressOffset, endpoint->Common.VcConnecting.RemoteSocketAddressLength ); *Information = endpoint->ContextLength; } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); } } AfdUnlockEndpointContext (endpoint, context); return status; } // AfdGetRemoteAddress NTSTATUS AfdSetContext ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) { PAFD_ENDPOINT endpoint; PVOID context; NTSTATUS status; UNREFERENCED_PARAMETER (IoctlCode); PAGED_CODE( ); // // Set up local pointers. // endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); status = STATUS_SUCCESS; *Information = 0; context = AfdLockEndpointContext (endpoint); try { // // Validate the input structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForRead (InputBuffer, InputBufferLength, sizeof (UCHAR)); if (OutputBuffer!=NULL) { // // Validate that output buffer is completely inside // of the input buffer and offsets are inside of supported ranges. // if ((PUCHAR)OutputBuffer<(PUCHAR)InputBuffer || (PUCHAR)OutputBuffer-(PUCHAR)InputBuffer>MAXUSHORT || OutputBufferLength>MAXUSHORT || OutputBufferLength>InputBufferLength || (ULONG)((PUCHAR)OutputBuffer-(PUCHAR)InputBuffer)> InputBufferLength-OutputBufferLength) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } } } // // If the context buffer is too small, allocate a new context // buffer from paged pool. // if ( endpoint->ContextLength < InputBufferLength ) { PVOID newContext; // // Allocate a new context buffer. // Note since the socket context usually gets // populated on socket creation during boot and not used // right away (untill socket state is chaged), we // make it a "cold" allocation. The flag has no effect // after system is booted. newContext = AFD_ALLOCATE_POOL_WITH_QUOTA( PagedPool|POOL_COLD_ALLOCATION, InputBufferLength, AFD_CONTEXT_POOL_TAG ); // AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag ASSERT ( newContext != NULL ); // // Free the old context buffer, if there was one. // if ( context != NULL ) { AFD_FREE_POOL( context, AFD_CONTEXT_POOL_TAG ); } context = newContext; } // // Store the passed-in context buffer. // endpoint->ContextLength = InputBufferLength; RtlCopyMemory( context, InputBuffer, InputBufferLength ); // // Save pointer to remote socket address which we fill // at the time of AcceptEx processing. // if (OutputBuffer!=NULL) { if (AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateOpen)) { if (endpoint->Type==AfdBlockTypeEndpoint && endpoint->State==AfdEndpointStateOpen) { endpoint->Common.VcConnecting.RemoteSocketAddressOffset = (USHORT) ((PUCHAR)OutputBuffer-(PUCHAR)InputBuffer); endpoint->Common.VcConnecting.RemoteSocketAddressLength = (USHORT) OutputBufferLength; } AFD_END_STATE_CHANGE (endpoint); } } } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); } AfdUnlockEndpointContext (endpoint, context); return status; } // AfdSetContext NTSTATUS AfdSetEventHandler ( IN PFILE_OBJECT FileObject, IN ULONG EventType, IN PVOID EventHandler, IN PVOID EventContext ) /*++ Routine Description: Sets up a TDI indication handler on a connection or address object (depending on the file handle). This is done synchronously, which shouldn't usually be an issue since TDI providers can usually complete indication handler setups immediately. Arguments: FileObject - a pointer to the file object for an open connection or address object. EventType - the event for which the indication handler should be called. EventHandler - the routine to call when tghe specified event occurs. EventContext - context which is passed to the indication routine. Return Value: NTSTATUS -- Indicates the status of the request. --*/ { TDI_REQUEST_KERNEL_SET_EVENT parameters; PAGED_CODE( ); parameters.EventType = EventType; parameters.EventHandler = EventHandler; parameters.EventContext = EventContext; return AfdIssueDeviceControl( FileObject, ¶meters, sizeof(parameters), NULL, 0, TDI_SET_EVENT_HANDLER ); } // AfdSetEventHandler NTSTATUS AfdIssueDeviceControl ( IN PFILE_OBJECT FileObject, IN PVOID IrpParameters, IN ULONG IrpParametersLength, IN PVOID MdlBuffer, IN ULONG MdlBufferLength, IN UCHAR MinorFunction ) /*++ Routine Description: Issues a device control returst to a TDI provider and waits for the request to complete. Arguments: FileObject - a pointer to the file object corresponding to a TDI handle IrpParameters - information to write to the parameters section of the stack location of the IRP. IrpParametersLength - length of the parameter information. Cannot be greater than 16. MdlBuffer - if non-NULL, a buffer of nonpaged pool to be mapped into an MDL and placed in the MdlAddress field of the IRP. MdlBufferLength - the size of the buffer pointed to by MdlBuffer. MinorFunction - the minor function code for the request. Return Value: NTSTATUS -- Indicates the status of the request. --*/ { NTSTATUS status; PIRP irp; PIO_STACK_LOCATION irpSp; KEVENT event; IO_STATUS_BLOCK ioStatusBlock; PDEVICE_OBJECT deviceObject; PMDL mdl; PAGED_CODE( ); // // Initialize the kernel event that will signal I/O completion. // KeInitializeEvent( &event, SynchronizationEvent, FALSE ); // // Attempt to allocate and initialize the I/O Request Packet (IRP) // for this operation. // deviceObject = IoGetRelatedDeviceObject ( FileObject ); DEBUG ioStatusBlock.Status = STATUS_UNSUCCESSFUL; DEBUG ioStatusBlock.Information = (ULONG)-1; // // If an MDL buffer was specified, get an MDL, and map the buffer // if ( MdlBuffer != NULL ) { mdl = IoAllocateMdl( MdlBuffer, MdlBufferLength, FALSE, FALSE, NULL ); if ( mdl == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool( mdl ); } else { mdl = NULL; } irp = TdiBuildInternalDeviceControlIrp ( MinorFunction, deviceObject, FileObject, &event, &ioStatusBlock ); if ( irp == NULL ) { if (mdl!=NULL) { IoFreeMdl (mdl); } return STATUS_INSUFFICIENT_RESOURCES; } // // Install MDL (if any) in the IRP. // irp->MdlAddress = mdl; // // Put the file object pointer in the stack location. // irpSp = IoGetNextIrpStackLocation( irp ); ASSERT (irpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL); irpSp->MinorFunction = MinorFunction; irpSp->FileObject = FileObject; // // Fill in the service-dependent parameters for the request. // ASSERT( IrpParametersLength <= sizeof(irpSp->Parameters) ); RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength ); // // Set up a completion routine which we'll use to free the MDL // allocated previously. // IoSetCompletionRoutine( irp, AfdRestartDeviceControl, NULL, TRUE, TRUE, TRUE ); status = IoCallDriver( deviceObject, irp ); // // Must be at below APC level or this IRP will never get fully completed. // ASSERT (KeGetCurrentIrql ()MdlAddress != NULL ) { IoFreeMdl( Irp->MdlAddress ); Irp->MdlAddress = NULL; } return STATUS_SUCCESS; } // AfdRestartDeviceControl NTSTATUS AfdGetConnectData ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) { PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; PAFD_CONNECT_DATA_INFO connectDataInfo; AFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo; AFD_LOCK_QUEUE_HANDLE lockHandle; PMDL mdl; NTSTATUS status; UCHAR localBuffer[AFD_FAST_CONNECT_DATA_SIZE]; // // Set up local pointers. // endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); mdl = NULL; status = STATUS_SUCCESS; *Information = 0; try { if (InputBufferLength>0) { if (InputBufferLength0) { if (OutputBufferLength>sizeof (localBuffer)) { mdl = IoAllocateMdl( OutputBuffer, // VirtualAddress OutputBufferLength, // Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if (mdl==NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } MmProbeAndLockPages( mdl, // MemoryDescriptorList RequestorMode, // AccessMode IoWriteAccess // Operation ); OutputBuffer = MmGetSystemAddressForMdlSafe(mdl, LowPagePriority); if (OutputBuffer==NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } } else { if (RequestorMode!=KernelMode) { ProbeForWrite (OutputBuffer, OutputBufferLength, sizeof (UCHAR)); } } } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); goto exit; } // // If there is a connection on this endpoint, use the data buffers // on the connection. Otherwise, use the data buffers from the // endpoint. // AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); if (InputBufferLength>0) { if ((endpoint->Type & AfdBlockTypeVcListening)==AfdBlockTypeVcListening) { connection = AfdFindReturnedConnection( endpoint, connectInfo.Sequence ); } else connection = NULL; if( connection == NULL ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_INVALID_PARAMETER; goto exit; } connectDataBuffers = connection->ConnectDataBuffers; } else if ( (connection= AFD_CONNECTION_FROM_ENDPOINT (endpoint)) != NULL ) { connectDataBuffers = connection->ConnectDataBuffers; } else if (IS_VC_ENDPOINT (endpoint)) { connectDataBuffers = endpoint->Common.VirtualCircuit.ConnectDataBuffers; } else { connectDataBuffers = NULL; } // // If there are no connect data buffers on the endpoint, complete // the IRP with no bytes. // if ( connectDataBuffers == NULL ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_SUCCESS; goto exit; } // // Determine what sort of data we're handling and where it should // come from. // switch ( IoctlCode ) { case IOCTL_AFD_GET_CONNECT_DATA: connectDataInfo = &connectDataBuffers->ReceiveConnectData; break; case IOCTL_AFD_GET_CONNECT_OPTIONS: connectDataInfo = &connectDataBuffers->ReceiveConnectOptions; break; case IOCTL_AFD_GET_DISCONNECT_DATA: connectDataInfo = &connectDataBuffers->ReceiveDisconnectData; break; case IOCTL_AFD_GET_DISCONNECT_OPTIONS: connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions; break; default: ASSERT(!"Unknown GET_CONNECT_DATA IOCTL!"); AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_INVALID_PARAMETER; goto exit; } if ((InputBufferLength>0) && connectInfo.LengthOnly) { connectInfo.ConnectDataLength = connectDataInfo->BufferLength; AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); try { RtlCopyMemory (OutputBuffer, &connectInfo, sizeof (connectInfo)); *Information = sizeof (connectInfo); } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); } goto exit; } // // If there is none of the requested data type, again complete // the IRP with no bytes. // if ( connectDataInfo->Buffer == NULL || connectDataInfo->BufferLength == 0 ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); *Information = 0; goto exit; } // // If the output buffer is too small, fail. // if ( OutputBufferLength < connectDataInfo->BufferLength ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_BUFFER_TOO_SMALL; goto exit; } // // Copy over the buffer and return the number of bytes copied. // RtlCopyMemory( mdl ? OutputBuffer : localBuffer, connectDataInfo->Buffer, connectDataInfo->BufferLength ); *Information = connectDataInfo->BufferLength; AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); if (mdl==NULL) { AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { RtlCopyMemory (OutputBuffer, localBuffer, *Information); } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); *Information = 0; } } exit: if (mdl!=NULL) { if (mdl->MdlFlags & MDL_PAGES_LOCKED) { MmUnlockPages (mdl); } IoFreeMdl (mdl); } return status; } // AfdGetConnectData NTSTATUS AfdSetConnectData ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) { PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; PAFD_CONNECT_DATA_BUFFERS * connectDataBuffersTarget; PAFD_CONNECT_DATA_INFO connectDataInfo; AFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo; AFD_LOCK_QUEUE_HANDLE lockHandle; BOOLEAN size = FALSE; PMDL mdl; NTSTATUS status; UCHAR localBuffer[AFD_FAST_CONNECT_DATA_SIZE]; // // Set up local pointers. // endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); mdl = NULL; status = STATUS_SUCCESS; *Information = 0; if (!IS_VC_ENDPOINT (endpoint)) { status = STATUS_INVALID_PARAMETER; goto exit; } AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { if (InputBufferLength>0) { if (InputBufferLength0) { if (OutputBufferLength>sizeof (localBuffer)) { mdl = IoAllocateMdl( OutputBuffer, // VirtualAddress OutputBufferLength, // Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if (mdl==NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } MmProbeAndLockPages( mdl, // MemoryDescriptorList RequestorMode, // AccessMode IoReadAccess // Operation ); OutputBuffer = MmGetSystemAddressForMdlSafe(mdl, LowPagePriority); if (OutputBuffer==NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } } else { if (RequestorMode!=KernelMode) { ProbeForRead (OutputBuffer, OutputBufferLength, sizeof (UCHAR)); RtlCopyMemory (localBuffer, OutputBuffer, OutputBufferLength); OutputBuffer = localBuffer; } } } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); goto exit; } AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); // // If there is a connect outstanding on this endpoint or if it // has already been shut down, fail this request. This prevents // the connect code from accessing buffers which may be freed soon. // if( endpoint->StateChangeInProgress || ((endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 )) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_INVALID_PARAMETER; goto exit; } if (InputBufferLength>0) { if ((endpoint->Type & AfdBlockTypeVcListening)==AfdBlockTypeVcListening) { connection = AfdFindReturnedConnection( endpoint, connectInfo.Sequence ); } else connection = NULL; if( connection == NULL ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_INVALID_PARAMETER; goto exit; } connectDataBuffersTarget = &connection->ConnectDataBuffers; } else if ( (connection= AFD_CONNECTION_FROM_ENDPOINT (endpoint)) != NULL ) { connectDataBuffersTarget = &connection->ConnectDataBuffers; } else { connectDataBuffersTarget = &endpoint->Common.VirtualCircuit.ConnectDataBuffers; } connectDataBuffers = *connectDataBuffersTarget; if( connectDataBuffers == NULL ) { try { connectDataBuffers = AFD_ALLOCATE_POOL_WITH_QUOTA( NonPagedPool, sizeof(*connectDataBuffers), AFD_CONNECT_DATA_POOL_TAG ); // AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag ASSERT ( connectDataBuffers != NULL ); *connectDataBuffersTarget = connectDataBuffers; } except( EXCEPTION_EXECUTE_HANDLER ) { status = GetExceptionCode (); AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); goto exit; } RtlZeroMemory( connectDataBuffers, sizeof(*connectDataBuffers) ); } // // Determine what sort of data we're handling and where it should // go. // switch( IoctlCode ) { case IOCTL_AFD_SET_CONNECT_DATA: connectDataInfo = &connectDataBuffers->SendConnectData; break; case IOCTL_AFD_SET_CONNECT_OPTIONS: connectDataInfo = &connectDataBuffers->SendConnectOptions; break; case IOCTL_AFD_SET_DISCONNECT_DATA: connectDataInfo = &connectDataBuffers->SendDisconnectData; break; case IOCTL_AFD_SET_DISCONNECT_OPTIONS: connectDataInfo = &connectDataBuffers->SendDisconnectOptions; break; case IOCTL_AFD_SIZE_CONNECT_DATA: connectDataInfo = &connectDataBuffers->ReceiveConnectData; size = TRUE; break; case IOCTL_AFD_SIZE_CONNECT_OPTIONS: connectDataInfo = &connectDataBuffers->ReceiveConnectOptions; size = TRUE; break; case IOCTL_AFD_SIZE_DISCONNECT_DATA: connectDataInfo = &connectDataBuffers->ReceiveDisconnectData; size = TRUE; break; case IOCTL_AFD_SIZE_DISCONNECT_OPTIONS: connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions; size = TRUE; break; default: ASSERT (!"Unsupported set connect data code"); AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_INVALID_PARAMETER; goto exit; } // // Determine the buffer size based on whether we're setting a buffer // into which data will be received, in which case the size is // in the four bytes of input buffer, or setting a buffer which we're // going to send, in which case the size is the length of the input // buffer. // if( size ) { if( OutputBufferLength < sizeof(ULONG) ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_INVALID_PARAMETER; goto exit; } OutputBufferLength = *(ULONG UNALIGNED *)OutputBuffer; } // // If there's not currently a buffer of the requested type, or there is // such a buffer and it's smaller than the requested size, free it // and allocate a new one. // if( connectDataInfo->Buffer == NULL || connectDataInfo->BufferLength < OutputBufferLength ) { if( connectDataInfo->Buffer != NULL ) { AFD_FREE_POOL( connectDataInfo->Buffer, AFD_CONNECT_DATA_POOL_TAG ); } connectDataInfo->Buffer = NULL; connectDataInfo->BufferLength = 0; if (OutputBufferLength>0) { try { connectDataInfo->Buffer = AFD_ALLOCATE_POOL_WITH_QUOTA( NonPagedPool, OutputBufferLength, AFD_CONNECT_DATA_POOL_TAG ); // AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag ASSERT ( connectDataInfo->Buffer != NULL ); } except( EXCEPTION_EXECUTE_HANDLER ) { status = GetExceptionCode (); AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); goto exit; } RtlZeroMemory( connectDataInfo->Buffer, OutputBufferLength ); } } // // If this wasn't simply a "size" request, copy the data into the buffer. // if( !size ) { RtlCopyMemory( connectDataInfo->Buffer, OutputBuffer, OutputBufferLength ); } connectDataInfo->BufferLength = OutputBufferLength; AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); exit: if (mdl!=NULL) { if (mdl->MdlFlags & MDL_PAGES_LOCKED) { MmUnlockPages (mdl); } IoFreeMdl (mdl); } return status; } // AfdSetConnectData NTSTATUS AfdSaveReceivedConnectData ( IN OUT PAFD_CONNECT_DATA_BUFFERS * DataBuffers, IN ULONG IoControlCode, IN PVOID Buffer, IN ULONG BufferLength ) /*++ Routine Description: This helper routine stores the specified *received* connect/disconnect data/options on the specified endpoint/connection. N.B. This routine MUST be called with endpoint SpinLock held! N.B. Unlike AfdSetConnectData(), this routine cannot allocate the AFD_CONNECT_DATA_BUFFERS structure with quota, as it may be called from AfdDisconnectEventHandler() in an unknown thread context. Arguments: DataBuffers -Points to a pointer to the connect data buffers structure. If the value pointed to by DataBuffers is NULL, then a new structure is allocated, otherwise the existing structure is used. IoControlCode - Specifies the type of data to save. Buffer - Points to the buffer containing the data. BufferLength - The length of Buffer. Return Value: NTSTATUS - The completion status. --*/ { PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; PAFD_CONNECT_DATA_INFO connectDataInfo; ASSERT( KeGetCurrentIrql() >= DISPATCH_LEVEL ); // // If there's no connect data buffer structure, allocate one now. // connectDataBuffers = *DataBuffers; if( connectDataBuffers == NULL ) { connectDataBuffers = AFD_ALLOCATE_POOL( NonPagedPool, sizeof(*connectDataBuffers), AFD_CONNECT_DATA_POOL_TAG ); if( connectDataBuffers == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory( connectDataBuffers, sizeof(*connectDataBuffers) ); *DataBuffers = connectDataBuffers; } // // Determine what sort of data we're handling and where it should // go. // switch( IoControlCode ) { case IOCTL_AFD_SET_CONNECT_DATA: connectDataInfo = &connectDataBuffers->ReceiveConnectData; break; case IOCTL_AFD_SET_CONNECT_OPTIONS: connectDataInfo = &connectDataBuffers->ReceiveConnectOptions; break; case IOCTL_AFD_SET_DISCONNECT_DATA: connectDataInfo = &connectDataBuffers->ReceiveDisconnectData; break; case IOCTL_AFD_SET_DISCONNECT_OPTIONS: connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions; break; default: ASSERT (!"Unsupported save received connect data code"); return STATUS_INVALID_PARAMETER; } // // If the buffer in the connect structure matches the one // passed in, must be the same buffer we passed in the request. // Just adjust the length. // if (connectDataInfo->Buffer==Buffer) { ASSERT (connectDataInfo->BufferLength>=BufferLength); connectDataInfo->BufferLength = BufferLength; return STATUS_SUCCESS; } // // If there was previously a buffer of the requested type, free it. // if( connectDataInfo->Buffer != NULL ) { AFD_FREE_POOL( connectDataInfo->Buffer, AFD_CONNECT_DATA_POOL_TAG ); connectDataInfo->Buffer = NULL; } // // Allocate a new buffer for the data and copy in the data we're to // send. // connectDataInfo->Buffer = AFD_ALLOCATE_POOL( NonPagedPool, BufferLength, AFD_CONNECT_DATA_POOL_TAG ); if( connectDataInfo->Buffer == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory( connectDataInfo->Buffer, Buffer, BufferLength ); connectDataInfo->BufferLength = BufferLength; return STATUS_SUCCESS; } // AfdSaveReceivedConnectData VOID AfdFreeConnectDataBuffers ( IN PAFD_CONNECT_DATA_BUFFERS ConnectDataBuffers ) { if ( ConnectDataBuffers->SendConnectData.Buffer != NULL ) { AFD_FREE_POOL( ConnectDataBuffers->SendConnectData.Buffer, AFD_CONNECT_DATA_POOL_TAG ); } if ( ConnectDataBuffers->ReceiveConnectData.Buffer != NULL ) { AFD_FREE_POOL( ConnectDataBuffers->ReceiveConnectData.Buffer, AFD_CONNECT_DATA_POOL_TAG ); } if ( ConnectDataBuffers->SendConnectOptions.Buffer != NULL ) { AFD_FREE_POOL( ConnectDataBuffers->SendConnectOptions.Buffer, AFD_CONNECT_DATA_POOL_TAG ); } if ( ConnectDataBuffers->ReceiveConnectOptions.Buffer != NULL ) { AFD_FREE_POOL( ConnectDataBuffers->ReceiveConnectOptions.Buffer, AFD_CONNECT_DATA_POOL_TAG ); } if ( ConnectDataBuffers->SendDisconnectData.Buffer != NULL ) { AFD_FREE_POOL( ConnectDataBuffers->SendDisconnectData.Buffer, AFD_CONNECT_DATA_POOL_TAG ); } if ( ConnectDataBuffers->ReceiveDisconnectData.Buffer != NULL ) { AFD_FREE_POOL( ConnectDataBuffers->ReceiveDisconnectData.Buffer, AFD_CONNECT_DATA_POOL_TAG ); } if ( ConnectDataBuffers->SendDisconnectOptions.Buffer != NULL ) { AFD_FREE_POOL( ConnectDataBuffers->SendDisconnectOptions.Buffer, AFD_CONNECT_DATA_POOL_TAG ); } if ( ConnectDataBuffers->ReceiveDisconnectOptions.Buffer != NULL ) { AFD_FREE_POOL( ConnectDataBuffers->ReceiveDisconnectOptions.Buffer, AFD_CONNECT_DATA_POOL_TAG ); } AFD_FREE_POOL( ConnectDataBuffers, AFD_CONNECT_DATA_POOL_TAG ); return; } // AfdFreeConnectDataBuffers VOID AfdQueueWorkItem ( IN PWORKER_THREAD_ROUTINE AfdWorkerRoutine, IN PAFD_WORK_ITEM AfdWorkItem ) { KIRQL oldIrql; ASSERT( AfdWorkerRoutine != NULL ); ASSERT( AfdWorkItem != NULL ); AfdWorkItem->AfdWorkerRoutine = AfdWorkerRoutine; // // Insert the work item at the tail of AFD's list of work itrems. // oldIrql = KeAcquireQueuedSpinLock( LockQueueAfdWorkQueueLock ); InsertTailList( &AfdWorkQueueListHead, &AfdWorkItem->WorkItemListEntry ); AfdRecordAfdWorkItemsQueued(); // // If there is no executive worker thread working on AFD work, fire // off an executive worker thread to start servicing the list. // if ( !AfdWorkThreadRunning ) { // // Remember that the work thread is running and release the // lock. Note that we must release the lock before queuing the // work because the worker thread may unlock AFD and we can't // hold a lock when AFD is unlocked. // AfdRecordExWorkItemsQueued(); AfdWorkThreadRunning = TRUE; KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql ); IoQueueWorkItem (AfdWorkQueueItem, AfdDoWork, DelayedWorkQueue, NULL); } else { KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql ); } return; } // AfdQueueWorkItem VOID AfdDoWork ( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PAFD_WORK_ITEM afdWorkItem; PLIST_ENTRY listEntry; KIRQL oldIrql; PWORKER_THREAD_ROUTINE workerRoutine; UNREFERENCED_PARAMETER (DeviceObject); UNREFERENCED_PARAMETER (Context); ASSERT( AfdWorkThreadRunning ); // // Empty the queue of AFD work items. // oldIrql = KeAcquireQueuedSpinLock( LockQueueAfdWorkQueueLock ); AfdRecordWorkerEnter(); AfdRecordAfdWorkerThread( PsGetCurrentThread() ); while ( !IsListEmpty( &AfdWorkQueueListHead ) ) { // // Take the first item from the queue and find the address // of the AFD work item structure. // listEntry = RemoveHeadList( &AfdWorkQueueListHead ); afdWorkItem = CONTAINING_RECORD( listEntry, AFD_WORK_ITEM, WorkItemListEntry ); AfdRecordAfdWorkItemsProcessed(); // // Capture the worker thread routine from the item. // workerRoutine = afdWorkItem->AfdWorkerRoutine; // // If this work item is going to unlock AFD, then remember that // the worker thread is no longer running. This closes the // window where AFD gets unloaded at the same time as new work // comes in and gets put on the work queue. Note that we // must reset this boolean BEFORE releasing the spin lock. // if( workerRoutine == AfdUnlockDriver ) { AfdWorkThreadRunning = FALSE; AfdRecordAfdWorkerThread( NULL ); AfdRecordWorkerLeave(); } // // Release the lock and then call the AFD worker routine. // KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql ); workerRoutine( afdWorkItem ); // // If the purpose of this work item was to unload AFD, then // we know that there is no more work to do and we CANNOT // acquire a spin lock. Quit servicing the list and return. if( workerRoutine == AfdUnlockDriver ) { return; } // // Reacquire the spin lock and continue servicing the list. // oldIrql = KeAcquireQueuedSpinLock( LockQueueAfdWorkQueueLock ); } // // Remember that we're no longer servicing the list and release the // spin lock. // AfdRecordAfdWorkerThread( NULL ); AfdRecordWorkerLeave(); AfdWorkThreadRunning = FALSE; KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql ); } // AfdDoWork PAFD_WORK_ITEM AfdGetWorkerByRoutine ( PWORKER_THREAD_ROUTINE Routine ) { KIRQL oldIrql; PLIST_ENTRY listEntry; oldIrql = KeAcquireQueuedSpinLock( LockQueueAfdWorkQueueLock ); listEntry = AfdWorkQueueListHead.Flink; while (listEntry!=&AfdWorkQueueListHead) { PAFD_WORK_ITEM afdWorkItem = CONTAINING_RECORD( listEntry, AFD_WORK_ITEM, WorkItemListEntry ); if (afdWorkItem->AfdWorkerRoutine==Routine) { RemoveEntryList (&afdWorkItem->WorkItemListEntry); KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql ); return afdWorkItem; } else listEntry = listEntry->Flink; } KeReleaseQueuedSpinLock( LockQueueAfdWorkQueueLock, oldIrql ); return NULL; } // AfdGetWorkerByRoutine #if DBG typedef struct _AFD_OUTSTANDING_IRP { LIST_ENTRY OutstandingIrpListEntry; PIRP OutstandingIrp; PCHAR FileName; ULONG LineNumber; } AFD_OUTSTANDING_IRP, *PAFD_OUTSTANDING_IRP; BOOLEAN AfdRecordOutstandingIrpDebug ( IN PAFD_ENDPOINT Endpoint, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PCHAR FileName, IN ULONG LineNumber ) { PAFD_OUTSTANDING_IRP outstandingIrp; AFD_LOCK_QUEUE_HANDLE lockHandle; UNREFERENCED_PARAMETER (DeviceObject); // // Get an outstanding IRP structure to hold the IRP. // outstandingIrp = AFD_ALLOCATE_POOL_PRIORITY ( NonPagedPool, sizeof(AFD_OUTSTANDING_IRP), AFD_DEBUG_POOL_TAG, NormalPoolPriority ); if ( outstandingIrp == NULL ) { // // Because our completion routine will try to // find this IRP anyway and check for completion // we use the stack space to put it in the list. // The completion routine will just remove this // element from the list without attempting to free it. // AFD_OUTSTANDING_IRP OutstandingIrp; OutstandingIrp.OutstandingIrp = Irp; OutstandingIrp.FileName = NULL; // To let completion // routine know that this // is not an allocated element OutstandingIrp.LineNumber = 0; AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle ); InsertTailList( &Endpoint->OutstandingIrpListHead, &OutstandingIrp.OutstandingIrpListEntry ); Endpoint->OutstandingIrpCount++; AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle ); KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdRecordOutstandingIrp: Could not track Irp %p on endpoint %p, failing it.\n", Irp, Endpoint)); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; IoSetNextIrpStackLocation( Irp ); IoCompleteRequest( Irp, AfdPriorityBoost ); return FALSE; } // // Initialize the structure and place it on the endpoint's list of // outstanding IRPs. // outstandingIrp->OutstandingIrp = Irp; outstandingIrp->FileName = FileName; outstandingIrp->LineNumber = LineNumber; AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle ); InsertHeadList( &Endpoint->OutstandingIrpListHead, &outstandingIrp->OutstandingIrpListEntry ); Endpoint->OutstandingIrpCount++; AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle ); return TRUE; } // AfdRecordOutstandingIrpDebug VOID AfdCompleteOutstandingIrpDebug ( IN PAFD_ENDPOINT Endpoint, IN PIRP Irp ) { PAFD_OUTSTANDING_IRP outstandingIrp; AFD_LOCK_QUEUE_HANDLE lockHandle; PLIST_ENTRY listEntry; // // First find the IRP on the endpoint's list of outstanding IRPs. // AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle ); for ( listEntry = Endpoint->OutstandingIrpListHead.Flink; listEntry != &Endpoint->OutstandingIrpListHead; listEntry = listEntry->Flink ) { outstandingIrp = CONTAINING_RECORD( listEntry, AFD_OUTSTANDING_IRP, OutstandingIrpListEntry ); if ( outstandingIrp->OutstandingIrp == Irp ) { RemoveEntryList( listEntry ); ASSERT( Endpoint->OutstandingIrpCount != 0 ); Endpoint->OutstandingIrpCount--; AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle ); if (outstandingIrp->FileName!=NULL) { AFD_FREE_POOL( outstandingIrp, AFD_DEBUG_POOL_TAG ); } return; } } // // The corresponding outstanding IRP structure was not found. This // should never happen unless an allocate for an outstanding IRP // structure failed above. // KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdCompleteOutstandingIrp: Irp %p not found on endpoint %p\n", Irp, Endpoint )); ASSERT( Endpoint->OutstandingIrpCount != 0 ); Endpoint->OutstandingIrpCount--; AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle ); return; } // AfdCompleteOutstandingIrpDebug #endif #if REFERENCE_DEBUG AFD_QSPIN_LOCK AfdLocationTableLock; PAFD_REFERENCE_LOCATION AfdLocationTable; SIZE_T AfdLocationTableSize; LONG AfdLocationId; LONG AfdFindReferenceLocation ( IN PCHAR Format, OUT PLONG LocationId ) { AFD_LOCK_QUEUE_HANDLE lockHandle; PVOID ignore; AfdAcquireSpinLock (&AfdLocationTableLock, &lockHandle); if (*LocationId==0) { if (AfdLocationId >= (LONG)(AfdLocationTableSize/sizeof(AfdLocationTable[0]))) { PAFD_REFERENCE_LOCATION newTable; newTable = ExAllocatePoolWithTag (NonPagedPool, AfdLocationTableSize+PAGE_SIZE, AFD_DEBUG_POOL_TAG); if (newTable!=NULL) { if (AfdLocationTable!=NULL) { RtlCopyMemory (newTable, AfdLocationTable, AfdLocationTableSize); ExFreePoolWithTag (AfdLocationTable, AFD_DEBUG_POOL_TAG); } AfdLocationTable = newTable; AfdLocationTableSize += PAGE_SIZE; } else { goto Unlock; } } AfdLocationTable[AfdLocationId].Format = Format; RtlGetCallersAddress (&AfdLocationTable[AfdLocationId].Address, &ignore); *LocationId = ++AfdLocationId; } Unlock: AfdReleaseSpinLock (&AfdLocationTableLock, &lockHandle); return *LocationId; } #endif #if DBG || REFERENCE_DEBUG VOID AfdInitializeDebugData ( VOID ) { AfdInitializeSpinLock (&AfdLocationTableLock); } // AfdInitializeDebugData VOID AfdFreeDebugData ( VOID ) { if (AfdLocationTable!=NULL) { ExFreePoolWithTag (AfdLocationTable, AFD_DEBUG_POOL_TAG); AfdLocationTable = NULL; } } // AfdFreeDebugData #endif #if DBG LONG AfdTotalAllocations = 0; LONG AfdTotalFrees = 0; LARGE_INTEGER AfdTotalBytesAllocated; LARGE_INTEGER AfdTotalBytesFreed; PVOID AfdAllocatePool ( IN POOL_TYPE PoolType, IN SIZE_T NumberOfBytes, IN ULONG Tag, IN PCHAR FileName, IN ULONG LineNumber, IN BOOLEAN WithQuota, IN EX_POOL_PRIORITY Priority ) { PVOID memBlock; PAFD_POOL_HEADER header; SIZE_T allocBytes; // // Check for overflow first. // if (NumberOfBytes+sizeof (*header)<=NumberOfBytes) { if (WithQuota) { ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES); } return NULL; } if (NumberOfBytes+sizeof (*header)>=PAGE_SIZE) { allocBytes = NumberOfBytes; if (allocBytesFileName = FileName; header->LineNumber = LineNumber; header->Size = NumberOfBytes; header->InUse = PoolType; } else { NumberOfBytes = PAGE_SIZE; ASSERT (PAGE_ALIGN(memBlock)==memBlock); } ExInterlockedAddLargeStatistic( &AfdTotalBytesAllocated, (CLONG)NumberOfBytes ); InterlockedIncrement( &AfdTotalAllocations ); return memBlock; } // AfdAllocatePool #define AFD_POOL_DEBUG 0 #if AFD_POOL_DEBUG #define MAX_LRU_POOL_BLOCKS 256 PVOID AfdLRUPoolBlocks[MAX_LRU_POOL_BLOCKS]; LONG AfdLRUPoolIndex = -1; #endif // AFD_POOL_DEBUG VOID AfdFreePool ( IN PVOID Pointer, IN ULONG Tag ) { ULONG PoolType; ULONG numberOfBytes; ASSERT (((ULONG_PTR)Pointer & (MEMORY_ALLOCATION_ALIGNMENT-1))==0); if (PAGE_ALIGN (Pointer)==Pointer) { numberOfBytes = PAGE_SIZE; } else { PAFD_POOL_HEADER header; Pointer = ((PAFD_POOL_HEADER)Pointer) - 1; header = Pointer; ASSERT (header->Size>0); PoolType = InterlockedExchange (&header->InUse, -1); ASSERT( PoolType == NonPagedPool || PoolType == NonPagedPoolMustSucceed || PoolType == PagedPool || PoolType == (NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE) || PoolType == (PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE) || PoolType == (PagedPool | POOL_COLD_ALLOCATION) || PoolType == (PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE | POOL_COLD_ALLOCATION)); numberOfBytes = (CLONG)header->Size; } ExInterlockedAddLargeStatistic( &AfdTotalBytesFreed, numberOfBytes ); InterlockedIncrement( &AfdTotalFrees ); #if AFD_POOL_DEBUG { LONG idx = InterlockedIncrement (&AfdLRUPoolIndex)%MAX_LRU_POOL_BLOCKS; RtlFillMemoryUlong ( Pointer, (numberOfBytes+3)&(~3), Tag); if (PoolType!=PagedPool) { ULONG size; Pointer = InterlockedExchangePointer ( &AfdLRUPoolBlocks[idx], Pointer); if (Pointer==NULL) return; if (PAGE_ALIGN(Pointer)==Pointer) numberOfBytes = PAGE_SIZE; else { PAFD_HEADER header; header = (PAFD_POOL_HEADER)Pointer - 1; Tag = *((PULONG)Pointer); numberOfBytes = (CLONG)(header->Size+3)&(~3); } size = RtlCompareMemoryUlong (Pointer, numberOfBytes, Tag); if (size!=numberOfBytes) { DbgPrint ("Block %p is modified at %p after it was freed.\n", Pointer, (PUCHAR)Pointer+size); DbgBreakPoint (); } } } #endif AFD_POOL_DEBUG MyFreePoolWithTag( Pointer, Tag ); } // AfdFreePool #ifdef AFDDBG_QUOTA typedef struct _AFD_QUOTA_HASH { PSZ Type; LONG TotalAmount; } AFD_QUOTA_HASH, *PAFD_QUOTA_HASH; #define AFD_QUOTA_HASH_SIZE 31 AFD_QUOTA_HASH AfdQuotaHash[AFD_QUOTA_HASH_SIZE]; PEPROCESS AfdQuotaProcess; typedef struct { union { ULONG Bytes; struct { UCHAR Reserved[3]; UCHAR Sign; } ; } ; UCHAR Location[12]; PVOID Block; PVOID Process; PVOID Reserved2[2]; } QUOTA_HISTORY, *PQUOTA_HISTORY; #define QUOTA_HISTORY_LENGTH 512 QUOTA_HISTORY AfdQuotaHistory[QUOTA_HISTORY_LENGTH]; LONG AfdQuotaHistoryIndex = 0; VOID AfdRecordQuotaHistory( IN PEPROCESS Process, IN LONG Bytes, IN PSZ Type, IN PVOID Block ) { LONG index; PQUOTA_HISTORY history; index = InterlockedIncrement( &AfdQuotaHistoryIndex ); index &= QUOTA_HISTORY_LENGTH - 1; history = &AfdQuotaHistory[index]; history->Bytes = Bytes; history->Sign = Bytes < 0 ? '-' : '+'; RtlCopyMemory( history->Location, Type, 12 ); history->Block = Block; history->Process = Process; index = (ULONG_PTR)Type % AFD_QUOTA_HASH_SIZE; if (AfdQuotaHash[index].Type!=Type) { if (InterlockedCompareExchangePointer ( (PVOID *)&AfdQuotaHash[index].Type, Type, NULL)!=NULL) { AfdQuotaHash[index].Type = (PVOID)-1; } } InterlockedExchangeAdd (&AfdQuotaHash[index].TotalAmount, Bytes); } // AfdRecordQuotaHistory #endif #endif PMDL AfdAdvanceMdlChain( IN PMDL Mdl, IN ULONG Offset ) /*++ Routine Description: Accepts a pointer to an existing MDL chain and offsets that chain by a specified number of bytes. This may involve the creation of a partial MDL for the first entry in the new chain. Arguments: Mdl - Pointer to the MDL chain to advance. Offset - The number of bytes to offset the chain. Return Value: NTSTATUS -- Indicates the status of the request. --*/ { // // Sanity check. // ASSERT( Mdl != NULL ); ASSERT( Offset > 0 ); // // Scan past any fully completed MDLs. // while ( Offset > MmGetMdlByteCount( Mdl ) ) { PMDL prev = Mdl; Offset -= MmGetMdlByteCount( Mdl ); ASSERT( Mdl->Next != NULL ); Mdl = Mdl->Next; prev->Next = NULL; MmUnlockPages (prev); IoFreeMdl (prev); } // // Tautology of the day: Offset will either be zero (meaning that // we've advanced to a clean boundary between MDLs) or non-zero // (meaning we need to now build a partial MDL). // if ( Offset > 0 ) { NTSTATUS status; // // Use new MM routine. // This saves us use of MustSucceed pool since the routine // below is guaranteed to succeed (as it should because // we already have the whole range locked and possibly mapped // and there should be no problem extracting part of it within // the same MDL). // status = MmAdvanceMdl (Mdl, Offset); ASSERT (status==STATUS_SUCCESS); } return Mdl; } // AfdAdvanceMdlChain NTSTATUS AfdAllocateMdlChain( IN PIRP Irp, IN LPWSABUF BufferArray, IN ULONG BufferCount, IN LOCK_OPERATION Operation, OUT PULONG TotalByteCount ) /*++ Routine Description: Allocates a MDL chain describing the WSABUF array and attaches the chain to the specified IRP. Arguments: Irp - The IRP that will receive the MDL chain. BufferArray - Points to an array of WSABUF structures describing the user's buffers. BufferCount - Contains the number of WSABUF structures in the array. Operation - Specifies the type of operation being performed (either IoReadAccess or IoWriteAccess). TotalByteCount - Will receive the total number of BYTEs described by the WSABUF array. Return Value: NTSTATUS -- Indicates the status of the request. --*/ { NTSTATUS status; PMDL currentMdl; PMDL * chainTarget; KPROCESSOR_MODE previousMode; ULONG totalLength; PVOID bufferPointer; ULONG bufferLength; // // Sanity check. // ASSERT( Irp != NULL ); ASSERT( Irp->MdlAddress == NULL ); ASSERT( ( Operation == IoReadAccess ) || ( Operation == IoWriteAccess ) ); ASSERT( TotalByteCount != NULL ); // // Get the previous processor mode. // previousMode = Irp->RequestorMode; // // Get into a known state. // status = STATUS_SUCCESS; currentMdl = NULL; chainTarget = &Irp->MdlAddress; totalLength = 0; // // Walk the array of WSABUF structures, creating the MDLs and // probing & locking the pages. // try { if( previousMode != KernelMode ) { if ((BufferArray==NULL) || (BufferCount==0) || (BufferCount>(MAXULONG/sizeof (WSABUF)))) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Probe the WSABUF array. // ProbeForRead( BufferArray, // Address BufferCount * sizeof(WSABUF), // Length PROBE_ALIGNMENT(WSABUF) // Alignment ); } else { ASSERT( BufferArray != NULL ); ASSERT( BufferCount > 0 ); } // // Scan the array. // for ( ; BufferCount>0; BufferCount--, BufferArray++) { bufferPointer = BufferArray->buf; bufferLength = BufferArray->len; if (bufferLength > 0) { // // Check for integer overflow. // C_ASSERT(sizeof(totalLength) == sizeof(MAXULONG)); if ((MAXULONG - totalLength) < bufferLength) { status = STATUS_INVALID_PARAMETER; break; } // // Create a new MDL. // currentMdl = IoAllocateMdl( bufferPointer, // VirtualAddress bufferLength, // Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if (currentMdl != NULL) { // // Lock the pages. This will raise an exception // if the operation fails. // MmProbeAndLockPages( currentMdl, // MemoryDescriptorList previousMode, // AccessMode Operation // Operation ); // // Chain the MDL onto the IRP. In theory, we could // do this by passing the IRP into IoAllocateMdl(), // but IoAllocateMdl() does a linear scan on the MDL // chain to find the last one in the chain. // // We can do much better. // *chainTarget = currentMdl; chainTarget = ¤tMdl->Next; } else { // // Cannot allocate new MDL, return appropriate error. // status = STATUS_INSUFFICIENT_RESOURCES; break; } // // Update the total byte counter. // totalLength += bufferLength; } } // // Ensure the MDL chain is NULL terminated. // ASSERT(*chainTarget == NULL); } except(AFD_EXCEPTION_FILTER(status)) { ASSERT(NT_ERROR(status)); // // currentMdl will only be non-NULL at this point if an MDL // has been created, but MmProbeAndLockPages() raised an // exception. If this is true, then free the MDL. // Also account for the case when currentMdl has been linked // onto the chain and exception occured when accesing next user // buffer. // if ((currentMdl != NULL) && (chainTarget != ¤tMdl->Next)) IoFreeMdl(currentMdl); } // // Return the total buffer count. // *TotalByteCount = totalLength; return status; } // AfdAllocateMdlChain #ifdef _WIN64 NTSTATUS AfdAllocateMdlChain32( IN PIRP Irp, IN LPWSABUF32 BufferArray, IN ULONG BufferCount, IN LOCK_OPERATION Operation, OUT PULONG TotalByteCount ) /*++ Routine Description: Allocates a MDL chain describing the WSABUF array and attaches the chain to the specified IRP. Arguments: Irp - The IRP that will receive the MDL chain. BufferArray - Points to an array of WSABUF structures describing the user's buffers. BufferCount - Contains the number of WSABUF structures in the array. Operation - Specifies the type of operation being performed (either IoReadAccess or IoWriteAccess). TotalByteCount - Will receive the total number of BYTEs described by the WSABUF array. Return Value: NTSTATUS -- Indicates the status of the request. --*/ { NTSTATUS status; PMDL currentMdl; PMDL * chainTarget; KPROCESSOR_MODE previousMode; ULONG totalLength; PVOID bufferPointer; ULONG bufferLength; // // Sanity check. // ASSERT( Irp != NULL ); ASSERT( Irp->MdlAddress == NULL ); ASSERT( ( Operation == IoReadAccess ) || ( Operation == IoWriteAccess ) ); ASSERT( TotalByteCount != NULL ); // // Get the previous processor mode. // previousMode = Irp->RequestorMode; // // Get into a known state. // status = STATUS_SUCCESS; currentMdl = NULL; chainTarget = &Irp->MdlAddress; totalLength = 0; // // Walk the array of WSABUF structures, creating the MDLs and // probing & locking the pages. // try { if( previousMode != KernelMode ) { if ((BufferArray==NULL) || (BufferCount==0) || (BufferCount>(MAXULONG/sizeof (WSABUF32)))) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Probe the WSABUF array. // ProbeForRead( BufferArray, // Address BufferCount * sizeof(WSABUF32), // Length PROBE_ALIGNMENT32(WSABUF32) // Alignment ); } else { ASSERT( BufferArray != NULL ); ASSERT( BufferCount > 0 ); } // // Scan the array. // for ( ; BufferCount>0; BufferCount--, BufferArray++) { bufferPointer = UlongToPtr(BufferArray->buf); bufferLength = BufferArray->len; if (bufferLength > 0) { // // Check for integer overflow. // C_ASSERT(sizeof(totalLength) == sizeof(MAXULONG)); if ((MAXULONG - totalLength) < bufferLength) { status = STATUS_INVALID_PARAMETER; break; } // // Create a new MDL. // currentMdl = IoAllocateMdl( bufferPointer, // VirtualAddress bufferLength, // Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if (currentMdl != NULL) { // // Lock the pages. This will raise an exception // if the operation fails. // MmProbeAndLockPages( currentMdl, // MemoryDescriptorList previousMode, // AccessMode Operation // Operation ); // // Chain the MDL onto the IRP. In theory, we could // do this by passing the IRP into IoAllocateMdl(), // but IoAllocateMdl() does a linear scan on the MDL // chain to find the last one in the chain. // // We can do much better. // *chainTarget = currentMdl; chainTarget = ¤tMdl->Next; } else { // // Cannot allocate new MDL, return appropriate error. // status = STATUS_INSUFFICIENT_RESOURCES; break; } // // Update the total byte counter. // totalLength += bufferLength; } } // // Ensure the MDL chain is NULL terminated. // ASSERT(*chainTarget == NULL); } except(AFD_EXCEPTION_FILTER(status)) { ASSERT(NT_ERROR(status)); // // currentMdl will only be non-NULL at this point if an MDL // has been created, but MmProbeAndLockPages() raised an // exception. If this is true, then free the MDL. // Also account for the case when currentMdl has been linked // onto the chain and exception occured when accesing next user // buffer. // if ((currentMdl != NULL) && (chainTarget != ¤tMdl->Next)) IoFreeMdl(currentMdl); } // // Return the total buffer count. // *TotalByteCount = totalLength; return status; } // AfdAllocateMdlChain32 #endif //_WIN64 VOID AfdDestroyMdlChain ( IN PIRP Irp ) /*++ Routine Description: Unlocks & frees the MDLs in the MDL chain attached to the given IRP. Arguments: Irp - The IRP that owns the MDL chain to destroy. Return Value: None. --*/ { PMDL mdl; PMDL nextMdl; mdl = Irp->MdlAddress; Irp->MdlAddress = NULL; while( mdl != NULL ) { nextMdl = mdl->Next; MmUnlockPages( mdl ); IoFreeMdl( mdl ); mdl = nextMdl; } } // AfdDestroyMdlChain ULONG AfdCalcBufferArrayByteLength( IN LPWSABUF BufferArray, IN ULONG BufferCount ) /*++ Routine Description: Calculates the total size (in bytes) of the buffers described by the specified WSABUF array. Arguments: BufferArray - Points to an array of WSABUF structures. BufferCount - The number of entries in BufferArray. Return Value: ULONG - The total size (in bytes) of the buffers described by the WSABUF array. Will raise an exception & return -1 if the total size is obviously too large. --*/ { LARGE_INTEGER totalLength; PAGED_CODE( ); // // Sanity check. // ASSERT( BufferArray != NULL ); ASSERT( BufferCount > 0 ); ASSERT( BufferCount <= (MAXULONG/sizeof (WSABUF))); // // Scan the array & sum the lengths. // totalLength.QuadPart = 0; while( BufferCount-- ) { totalLength.QuadPart += (LONGLONG)BufferArray->len; BufferArray++; } if( totalLength.HighPart != 0 || ( totalLength.LowPart & 0x80000000 ) != 0 ) { ExRaiseAccessViolation(); } return totalLength.LowPart; } // AfdCalcBufferArrayByteLength ULONG AfdCopyBufferArrayToBuffer( IN PVOID Destination, IN ULONG DestinationLength, IN LPWSABUF BufferArray, IN ULONG BufferCount ) /*++ Routine Description: Copies data from a WSABUF array to a linear buffer. Arguments: Destination - Points to the linear destination of the data. DestinationLength - The length of Destination. BufferArray - Points to an array of WSABUF structures describing the source for the copy. BufferCount - The number of entries in BufferArray. Return Value: ULONG - The number of bytes copied. --*/ { PVOID destinationStart; ULONG bytesToCopy; PAGED_CODE( ); // // Sanity check. // ASSERT( Destination != NULL ); ASSERT( BufferArray != NULL ); ASSERT( BufferCount > 0 ); // // Remember this so we can calc number of bytes copied. // destinationStart = Destination; // // Scan the array & copy to the linear buffer. // while( BufferCount-- && DestinationLength > 0 ) { WSABUF Buffer = *BufferArray++; bytesToCopy = min( DestinationLength, Buffer.len ); if( ExGetPreviousMode() != KernelMode ) { ProbeForRead( Buffer.buf, // Address bytesToCopy, // Length sizeof(UCHAR) // Alignment ); } RtlCopyMemory( Destination, Buffer.buf, bytesToCopy ); Destination = (PCHAR)Destination + bytesToCopy; DestinationLength -= bytesToCopy; } // // Return number of bytes copied. // return (ULONG)((PUCHAR)Destination - (PUCHAR)destinationStart); } // AfdCopyBufferArrayToBuffer ULONG AfdCopyBufferToBufferArray( IN LPWSABUF BufferArray, IN ULONG Offset, IN ULONG BufferCount, IN PVOID Source, IN ULONG SourceLength ) /*++ Routine Description: Copies data from a linear buffer to a WSABUF array. Arguments: BufferArray - Points to an array of WSABUF structures describing the destination for the copy. Offset - An offset within the buffer array at which the data should be copied. BufferCount - The number of entries in BufferArray. Source - Points to the linear source of the data. SourceLength - The length of Source. Return Value: ULONG - The number of bytes copied. --*/ { PVOID sourceStart; ULONG bytesToCopy; WSABUF buffer; PAGED_CODE( ); // // Sanity check. // ASSERT( BufferArray != NULL ); ASSERT( BufferCount > 0 ); ASSERT( Source != NULL ); ASSERT( SourceLength > 0 ); // // Remember this so we can return the number of bytes copied. // sourceStart = Source; // // Handle the offset if one was specified. // if( Offset > 0 ) { // // Skip whole entries if necessary. // while( BufferCount-- > 0 ) { buffer = *BufferArray++; if (Offset < buffer.len) { // // If we have buffers left, fix up the buffer pointer // and length to keep the loop below fast. // buffer.buf += Offset; buffer.len -= Offset; // // We have already copied buffer array element, so jump // to the body of the loop to avoid doing this again (this // is not a mere optimization, but protection from application // that plays tricks on us by changing content of the buffer // array while we are looking at it). // goto DoCopy; } Offset -= buffer.len; } return 0; } // // Scan the array & copy from the linear buffer. // while( BufferCount-->0 && SourceLength > 0 ) { buffer = *BufferArray++; DoCopy: bytesToCopy = min( SourceLength, buffer.len ); if( ExGetPreviousMode() != KernelMode ) { ProbeForWrite( buffer.buf, // Address bytesToCopy, // Length sizeof(UCHAR) // Alignment ); } RtlCopyMemory( buffer.buf, Source, bytesToCopy ); Source = (PCHAR)Source + bytesToCopy; SourceLength -= bytesToCopy; } // // Return number of bytes copied. // return (ULONG)((PUCHAR)Source - (PUCHAR)sourceStart); } // AfdCopyBufferToBufferArray ULONG AfdCopyMdlChainToBufferArray( IN LPWSABUF BufferArray, IN ULONG BufferOffset, IN ULONG BufferCount, IN PMDL SourceMdl, IN ULONG SourceOffset, IN ULONG SourceLength ) /*++ Routine Description: Copies data from a MDL chain to a WSABUF array. Arguments: BufferArray - Points to an array of WSABUF structures describing the destination for the copy. BufferOffset - An offset within the buffer array at which the data should be copied. BufferCount - The number of entries in BufferArray. Source - Points to the MDL chain with data SourceOffset - An offset within the MDL chain from which the data should be copied. SourceLength - The length of Source. Return Value: ULONG - The number of bytes copied. --*/ { ULONG bytesCopied; ULONG bytesToCopy, len; WSABUF buffer; PAGED_CODE( ); // // Assume we can copy everything. // bytesCopied = SourceLength; // // Sanity check. // ASSERT( BufferArray != NULL ); ASSERT( BufferCount > 0 ); ASSERT( SourceMdl != NULL ); ASSERT( SourceLength>0 ); // // Skip offset into the MDL chain // while (SourceOffset>=MmGetMdlByteCount (SourceMdl)) { SourceOffset -= MmGetMdlByteCount (SourceMdl); SourceMdl = SourceMdl->Next; } // // Handle buffer array offset if specified // if (BufferOffset>0) { // // Skip whole entries. // while( BufferCount-- > 0) { buffer = *BufferArray++; if (BufferOffset < buffer.len) { // // We have buffers left, fix up the buffer pointer // and length to keep the loop below fast. // ASSERT (BufferOffset < buffer.len); buffer.buf += BufferOffset; buffer.len -= BufferOffset; // // We have already copied buffer array element, so jump // to the body of the loop to avoid doing this again (this // is not a mere optimization, but protection from application // that plays tricks on us by changing content of the buffer // array while we are looking at it). // goto DoCopy; } BufferOffset -= buffer.len; } return 0; } // // Scan the array & copy from the mdl chain. // while (SourceLength>0 && BufferCount-->0) { buffer = *BufferArray++; DoCopy: bytesToCopy = min( SourceLength, buffer.len ); if( ExGetPreviousMode() != KernelMode ) { ProbeForWrite( buffer.buf, // Address bytesToCopy, // Length sizeof(UCHAR) // Alignment ); } // // Update source length for data we are going to copy // SourceLength -= bytesToCopy; // // Copy full source MDLs // while (bytesToCopy>0 && (bytesToCopy>=(len=MmGetMdlByteCount (SourceMdl)-SourceOffset))) { ASSERT (SourceMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL)); RtlCopyMemory (buffer.buf, (PUCHAR)MmGetSystemAddressForMdl(SourceMdl)+SourceOffset, len); bytesToCopy -= len; buffer.buf += len; SourceMdl = SourceMdl->Next; SourceOffset = 0; } // // Copy partial source MDL if space remains. // if (bytesToCopy>0) { ASSERT (bytesToCopyMdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL)); RtlCopyMemory (buffer.buf, (PUCHAR)MmGetSystemAddressForMdl (SourceMdl)+SourceOffset, bytesToCopy ); SourceOffset += bytesToCopy; } } // // Return number of bytes copied except for those we couldn't. // return bytesCopied-SourceLength; } // AfdCopyMdlChainToBufferArray NTSTATUS AfdCopyMdlChainToBufferAvoidMapping( IN PMDL SrcMdl, IN ULONG SrcOffset, IN ULONG SrcLength, IN PUCHAR Dst, IN ULONG DstSize ) /*++ Routine Description: Copies data from a MDL chain to a buffer and avoids mapping MDLs to system space if possible. Arguments: Dst - Points to destination for the copy. DstSize - Size of the buffer Source - Points to the MDL chain with data SourceOffset - An offset within the MDL chain from which the data should be copied. SourceLength - The length of Source. Return Value: NTSTATUS - success if everything was copied OK STATUS_INSUFFICIENT_RESOURCES - mapping failed --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG bytesToCopy; PUCHAR DstEnd = Dst+DstSize; PAGED_CODE( ); // // Sanity check. // ASSERT( Dst != NULL ); ASSERT( DstSize > 0 ); ASSERT( SrcMdl != NULL ); ASSERT( SrcLength>0 ); // // Skip offset into the MDL chain // while (SrcOffset>=MmGetMdlByteCount (SrcMdl)) { SrcOffset -= MmGetMdlByteCount (SrcMdl); SrcMdl = SrcMdl->Next; } while (DstSrcLength) { bytesToCopy = SrcLength; } if (SrcMdl->Process==IoGetCurrentProcess ()) { // // If we are in the context of the same process that // MDL was created for, copy using VAs. // AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { RtlCopyMemory ( Dst, (PUCHAR)MmGetMdlVirtualAddress (SrcMdl)+SrcOffset, bytesToCopy ); } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); return status; } } else { // // Otherwise, map MDL into the system space. // PCHAR src = MmGetSystemAddressForMdlSafe (SrcMdl, LowPagePriority); if (!src) return STATUS_INSUFFICIENT_RESOURCES; RtlCopyMemory ( Dst, src+SrcOffset, bytesToCopy ); } // // Update source length for data we are going to copy // SrcLength -= bytesToCopy; if (SrcLength==0) return STATUS_SUCCESS; SrcMdl = SrcMdl->Next; SrcOffset = 0; Dst += bytesToCopy; } return STATUS_BUFFER_OVERFLOW; } // AfdCopyMdlChainToBufferAvoidMapping NTSTATUS AfdMapMdlChain ( PMDL MdlChain ) /*++ Routine Description: Makes sure that eveyr MDL in the chains is mapped into the system address space. Arguments: MdlChain - Destination MDL. Return Value: STATUS_SUCCESS - MDL chain is fully mapped STATUS_INSUFFICIENT_RESOURCES - at least one MDL could not be mapped --*/ { while (MdlChain!=NULL) { if (MmGetSystemAddressForMdlSafe(MdlChain, LowPagePriority)==NULL) { return STATUS_INSUFFICIENT_RESOURCES; } MdlChain = MdlChain->Next; } return STATUS_SUCCESS; } // AfdMapMdlChain NTSTATUS AfdCopyMdlChainToMdlChain ( PMDL DstMdl, ULONG DstOffset, PMDL SrcMdl, ULONG SrcOffset, ULONG SrcLength, PULONG BytesCopied ) /*++ Routine Description: Copies data from an MDL chain to an MDL chain. Arguments: DstMdl - Destination MDL. DstOffset - Offset withih the destination MDL. SrcMdl - Source MDL. SrcOffset - Offset within the source. SrcLength - lenght of the data in the source chain BytesCopied - points to variable that received total number of bytes actually copied Return Value: STATUS_SUCCESS - all of the source data was copied STATUS_BUFFER_OVERFLOW - destination MDL was not long enough to hold all of the source data. --*/ { ULONG bytesToCopy = 0, len; PUCHAR dst; ASSERT( SrcMdl != NULL ); ASSERT( DstMdl != NULL ); // // Assume we can copy everything. // *BytesCopied = SrcLength; // // Skip full MDLs in source. // while ((SrcMdl!=NULL) && (SrcOffset>=MmGetMdlByteCount (SrcMdl))) { SrcOffset -= MmGetMdlByteCount (SrcMdl); SrcMdl = SrcMdl->Next; } // // Skip full MDLs in destination // while ((DstMdl!=NULL) && (DstOffset>=MmGetMdlByteCount (DstMdl))) { DstOffset -= MmGetMdlByteCount (DstMdl); DstMdl = DstMdl->Next; } // // Handle remaining destination offset separately to simplify the main loop. // if (DstOffset>0) { dst = MmGetSystemAddressForMdlSafe (DstMdl, LowPagePriority); if (dst==NULL) return STATUS_INSUFFICIENT_RESOURCES; dst += DstOffset; bytesToCopy = MmGetMdlByteCount(DstMdl)-DstOffset; goto DoCopy; } // // For each MDL in destination copy source MDLs // while source data and free space in destination remain // while ((SrcLength>0) && (DstMdl!=NULL)) { dst = MmGetSystemAddressForMdlSafe (DstMdl, LowPagePriority); if (dst==NULL) return STATUS_INSUFFICIENT_RESOURCES; bytesToCopy = MmGetMdlByteCount(DstMdl); DoCopy: bytesToCopy = min (SrcLength, bytesToCopy); // // Adjust source length // SrcLength -= bytesToCopy; // // Copy full source MDLs // while (bytesToCopy>0 && (bytesToCopy>=(len=MmGetMdlByteCount (SrcMdl)-SrcOffset))) { ASSERT (SrcMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL)); RtlCopyMemory (dst, (PUCHAR)MmGetSystemAddressForMdl(SrcMdl)+SrcOffset, len); bytesToCopy -= len; dst += len; SrcMdl = SrcMdl->Next; SrcOffset = 0; } // // Copy partial source MDL if space remains // if (bytesToCopy>0) { ASSERT (bytesToCopyMdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL)); RtlCopyMemory (dst, (PUCHAR)MmGetSystemAddressForMdl (SrcMdl)+SrcOffset, bytesToCopy ); SrcOffset += bytesToCopy; } // // Advance to next MDL in destination // DstMdl = DstMdl->Next; } // // If we copied everything, return success // if (SrcLength==0) { return STATUS_SUCCESS; } else { // // Otherwise, adjust for number of bytes not copied // and return destination overflow // *BytesCopied -= SrcLength; return STATUS_BUFFER_OVERFLOW; } } #if DBG VOID AfdAssert( IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber, IN PCHAR Message OPTIONAL ) { if( AfdUsePrivateAssert ) { DbgPrint( "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n", Message ? Message : "", FailedAssertion, FileName, LineNumber ); DbgBreakPoint(); } else { RtlAssert( FailedAssertion, FileName, LineNumber, Message ); } } // AfdAssert #endif // DBG NTSTATUS FASTCALL AfdSetQos( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine sets the QOS for the given endpoint. Note that, since we don't really (yet) support QOS, we just ignore the incoming data and issue a AFD_POLL_QOS or AFD_POLL_GROUP_QOS event as appropriate. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAFD_QOS_INFO qosInfo; NTSTATUS status = STATUS_SUCCESS; #ifdef _WIN64 if (IoIs32bitProcess (Irp)) { status = AfdSetQos32 (Irp, IrpSp); goto Complete; } #endif // // Set up local pointers. // endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); qosInfo = Irp->AssociatedIrp.SystemBuffer; // // Make sure that the input buffer is large enough. // if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*qosInfo) ) { status = STATUS_BUFFER_TOO_SMALL; goto Complete; } // // If the incoming data doesn't match the default QOS, // indicate the appropriate event. // if( !RtlEqualMemory( &qosInfo->Qos, &AfdDefaultQos, sizeof(QOS) ) ) { AFD_LOCK_QUEUE_HANDLE lockHandle; AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); AfdIndicateEventSelectEvent( endpoint, qosInfo->GroupQos ? AFD_POLL_GROUP_QOS : AFD_POLL_QOS, STATUS_SUCCESS ); AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); AfdIndicatePollEvent( endpoint, qosInfo->GroupQos ? AFD_POLL_GROUP_QOS : AFD_POLL_QOS, STATUS_SUCCESS ); } Complete: // // Complete the IRP. // Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest( Irp, AfdPriorityBoost ); return status; } // AfdSetQos NTSTATUS FASTCALL AfdGetQos( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine gets the QOS for the given endpoint. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAFD_QOS_INFO qosInfo; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); Irp->IoStatus.Information = 0; #ifdef _WIN64 if (IoIs32bitProcess (Irp)) { status = AfdGetQos32 (Irp, IrpSp); goto Complete; } #endif // // Set up local pointers. // endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); qosInfo = Irp->AssociatedIrp.SystemBuffer; // // Make sure that the output buffer is large enough. // if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*qosInfo) ) { status = STATUS_BUFFER_TOO_SMALL; goto Complete; } // // Just return the default data. // RtlCopyMemory( &qosInfo->Qos, &AfdDefaultQos, sizeof(QOS) ); Irp->IoStatus.Information = sizeof(*qosInfo); Irp->IoStatus.Information = sizeof(*qosInfo); Complete: // // Complete the IRP. // Irp->IoStatus.Status = status; IoCompleteRequest( Irp, AfdPriorityBoost ); return status; } // AfdGetQos #ifdef _WIN64 NTSTATUS AfdSetQos32( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine sets the QOS for the given endpoint. Note that, since we don't really (yet) support QOS, we just ignore the incoming data and issue a AFD_POLL_QOS or AFD_POLL_GROUP_QOS event as appropriate. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAFD_QOS_INFO32 qosInfo; // // Set up local pointers. // endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); qosInfo = Irp->AssociatedIrp.SystemBuffer; // // Make sure that the input buffer is large enough. // if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*qosInfo) ) { return STATUS_BUFFER_TOO_SMALL; } // // If the incoming data doesn't match the default QOS, // indicate the appropriate event. // if( !RtlEqualMemory( &qosInfo->Qos, &AfdDefaultQos32, sizeof(QOS32) ) ) { AFD_LOCK_QUEUE_HANDLE lockHandle; AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle); AfdIndicateEventSelectEvent( endpoint, qosInfo->GroupQos ? AFD_POLL_GROUP_QOS : AFD_POLL_QOS, STATUS_SUCCESS ); AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle); AfdIndicatePollEvent( endpoint, qosInfo->GroupQos ? AFD_POLL_GROUP_QOS : AFD_POLL_QOS, STATUS_SUCCESS ); } // // Complete the IRP. // Irp->IoStatus.Information = 0; return STATUS_SUCCESS; } // AfdSetQos NTSTATUS AfdGetQos32( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine gets the QOS for the given endpoint. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAFD_QOS_INFO32 qosInfo; PAGED_CODE(); // // Set up local pointers. // endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); qosInfo = Irp->AssociatedIrp.SystemBuffer; // // Make sure that the output buffer is large enough. // if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*qosInfo) ) { return STATUS_BUFFER_TOO_SMALL; } // // Just return the default data. // RtlCopyMemory( &qosInfo->Qos, &AfdDefaultQos32, sizeof(QOS32) ); // // Complete the IRP. // Irp->IoStatus.Information = sizeof(*qosInfo); return STATUS_SUCCESS; } // AfdGetQos32 #endif // _WIN64 NTSTATUS AfdValidateStatus ( NTSTATUS Status ) { PAGED_CODE (); // // Validate the status code. // It must match the status code conversion algorithm in msafd. // switch (Status) { case STATUS_SUCCESS: // return NO_ERROR; case STATUS_INVALID_HANDLE: case STATUS_OBJECT_TYPE_MISMATCH: // return WSAENOTSOCK; case STATUS_INSUFFICIENT_RESOURCES: case STATUS_PAGEFILE_QUOTA: case STATUS_COMMITMENT_LIMIT: case STATUS_WORKING_SET_QUOTA: case STATUS_NO_MEMORY: case STATUS_CONFLICTING_ADDRESSES: case STATUS_QUOTA_EXCEEDED: case STATUS_TOO_MANY_PAGING_FILES: case STATUS_REMOTE_RESOURCES: case STATUS_TOO_MANY_ADDRESSES: // return WSAENOBUFS; case STATUS_SHARING_VIOLATION: case STATUS_ADDRESS_ALREADY_EXISTS: // return WSAEADDRINUSE; case STATUS_LINK_TIMEOUT: case STATUS_IO_TIMEOUT: case STATUS_TIMEOUT: // return WSAETIMEDOUT; case STATUS_GRACEFUL_DISCONNECT: // return WSAEDISCON; case STATUS_REMOTE_DISCONNECT: case STATUS_CONNECTION_RESET: case STATUS_LINK_FAILED: case STATUS_CONNECTION_DISCONNECTED: case STATUS_PORT_UNREACHABLE: // return WSAECONNRESET; case STATUS_LOCAL_DISCONNECT: case STATUS_TRANSACTION_ABORTED: case STATUS_CONNECTION_ABORTED: // return WSAECONNABORTED; case STATUS_BAD_NETWORK_PATH: case STATUS_NETWORK_UNREACHABLE: case STATUS_PROTOCOL_UNREACHABLE: // return WSAENETUNREACH; case STATUS_HOST_UNREACHABLE: // return WSAEHOSTUNREACH; case STATUS_HOST_DOWN: // return WSAEHOSTDOWN; case STATUS_CANCELLED: case STATUS_REQUEST_ABORTED: // return WSAEINTR; case STATUS_BUFFER_OVERFLOW: case STATUS_INVALID_BUFFER_SIZE: // return WSAEMSGSIZE; case STATUS_BUFFER_TOO_SMALL: case STATUS_ACCESS_VIOLATION: // return WSAEFAULT; // case STATUS_DEVICE_NOT_READY: // case STATUS_REQUEST_NOT_ACCEPTED: // return WSAEWOULDBLOCK; case STATUS_INVALID_NETWORK_RESPONSE: case STATUS_NETWORK_BUSY: case STATUS_NO_SUCH_DEVICE: case STATUS_NO_SUCH_FILE: case STATUS_OBJECT_PATH_NOT_FOUND: case STATUS_OBJECT_NAME_NOT_FOUND: case STATUS_UNEXPECTED_NETWORK_ERROR: // return WSAENETDOWN; case STATUS_INVALID_CONNECTION: // return WSAENOTCONN; case STATUS_REMOTE_NOT_LISTENING: case STATUS_CONNECTION_REFUSED: // return WSAECONNREFUSED; case STATUS_PIPE_DISCONNECTED: // return WSAESHUTDOWN; case STATUS_INVALID_ADDRESS: case STATUS_INVALID_ADDRESS_COMPONENT: // return WSAEADDRNOTAVAIL; case STATUS_NOT_SUPPORTED: case STATUS_NOT_IMPLEMENTED: // return WSAEOPNOTSUPP; case STATUS_ACCESS_DENIED: // return WSAEACCES; case STATUS_CONNECTION_ACTIVE: // return WSAEISCONN; break; case STATUS_UNSUCCESSFUL: case STATUS_INVALID_PARAMETER: case STATUS_ADDRESS_CLOSED: case STATUS_CONNECTION_INVALID: case STATUS_ADDRESS_ALREADY_ASSOCIATED: case STATUS_ADDRESS_NOT_ASSOCIATED: case STATUS_INVALID_DEVICE_STATE: case STATUS_INVALID_DEVICE_REQUEST: // return WSAEINVAL; break; default: KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdValidateStatus: Unsupported status code %lx, converting to %lx(INVALID_PARAMETER)\n", Status, STATUS_INVALID_PARAMETER)); Status = STATUS_INVALID_PARAMETER; break; } return Status; } NTSTATUS FASTCALL AfdNoOperation( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine does nothing but complete the IRP. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; NTSTATUS status; PAGED_CODE(); #ifdef _WIN64 if (IoIs32bitProcess (Irp)) { status = AfdNoOperation32 (Irp, IrpSp); goto Complete; } #endif // // Set up local pointers. // endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); // // Assume success // status = STATUS_SUCCESS; if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof (IO_STATUS_BLOCK)) { try { if (Irp->RequestorMode!=KernelMode) { ProbeForReadSmallStructure (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof (IO_STATUS_BLOCK), PROBE_ALIGNMENT(IO_STATUS_BLOCK)) } // // Copy the status block // Irp->IoStatus = *((PIO_STATUS_BLOCK)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer); Irp->IoStatus.Status = AfdValidateStatus (Irp->IoStatus.Status); } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); // // Fail the call, no completion notification // should be delivered via async IO. // Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; } } else { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; } #ifdef _WIN64 Complete: #endif if (status==STATUS_SUCCESS && Irp->IoStatus.Status!=STATUS_SUCCESS) { // // Make sure we deliver error via async IO // operation instead of just failing this call itself. // IoMarkIrpPending (Irp); status = STATUS_PENDING; } else { ASSERT (status==Irp->IoStatus.Status); } IoCompleteRequest( Irp, AfdPriorityBoost ); return status; } // AfdNoOperation #ifdef _WIN64 NTSTATUS AfdNoOperation32( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine does nothing but complete the IRP. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; NTSTATUS status; PAGED_CODE(); // // Set up local pointers. // endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); // // Assume success // status = STATUS_SUCCESS; if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof (IO_STATUS_BLOCK32)) { try { if (Irp->RequestorMode!=KernelMode) { ProbeForReadSmallStructure (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof (IO_STATUS_BLOCK32), PROBE_ALIGNMENT32(IO_STATUS_BLOCK32)) } Irp->IoStatus.Status = ((PIO_STATUS_BLOCK32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->Status; Irp->IoStatus.Information = ((PIO_STATUS_BLOCK32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->Information; // // Validate the status code. // It must match the status code conversion algorithm in msafd. // Irp->IoStatus.Status = AfdValidateStatus (Irp->IoStatus.Status); } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); // // Fail the call, no completion notification // should be delivered via async IO. // Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; } } else { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; } return status; } // AfdNoOperation32 #endif //_WIN64 NTSTATUS FASTCALL AfdValidateGroup( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine examines a group ID. If the ID is for a "constrained" group, then all endpoints are scanned to validate the given address is consistent with the constrained group. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAFD_ENDPOINT compareEndpoint; PAFD_CONNECTION connection; PLIST_ENTRY listEntry; PAFD_VALIDATE_GROUP_INFO validateInfo; AFD_GROUP_TYPE groupType; PTRANSPORT_ADDRESS requestAddress; ULONG requestAddressLength; AFD_LOCK_QUEUE_HANDLE lockHandle; BOOLEAN result; LONG groupId; NTSTATUS status = STATUS_SUCCESS; // // Set up local pointers. // endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); validateInfo = Irp->AssociatedIrp.SystemBuffer; // // Make sure that the input buffer is large enough. // if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*validateInfo) ) { status = STATUS_BUFFER_TOO_SMALL; goto Complete; } if( validateInfo->RemoteAddress.TAAddressCount != 1 ) { status = STATUS_INVALID_PARAMETER; goto Complete; } if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < ( sizeof(*validateInfo) - sizeof(TRANSPORT_ADDRESS) + validateInfo->RemoteAddress.Address[0].AddressLength ) ) { status = STATUS_BUFFER_TOO_SMALL; goto Complete; } // // Start by referencing the group so it doesn't go away unexpectedly. // This will also validate the group ID, and give us the group type. // groupId = validateInfo->GroupID; if( !AfdReferenceGroup( groupId, &groupType ) ) { status = STATUS_INVALID_PARAMETER; goto Complete; } // // If it's not a constrained group ID, we can just complete the IRP // successfully right now. // if( groupType != GroupTypeConstrained ) { AfdDereferenceGroup( validateInfo->GroupID ); Irp->IoStatus.Information = 0; status = STATUS_SUCCESS; goto Complete; } // // Calculate the size of the incoming TDI address. // requestAddress = &validateInfo->RemoteAddress; requestAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength - sizeof(AFD_VALIDATE_GROUP_INFO) + sizeof(TRANSPORT_ADDRESS); // // OK, it's a constrained group. Scan the list of constrained endpoints, // find those that are either datagram endpoints or have associated // connections, and validate the remote addresses. // result = TRUE; // // Make sure the thread in which we execute cannot get // suspeneded in APC while we own the global resource. // KeEnterCriticalRegion (); ExAcquireResourceSharedLite( AfdResource, TRUE ); for( listEntry = AfdConstrainedEndpointListHead.Flink ; listEntry != &AfdConstrainedEndpointListHead ; listEntry = listEntry->Flink ) { compareEndpoint = CONTAINING_RECORD( listEntry, AFD_ENDPOINT, ConstrainedEndpointListEntry ); ASSERT( IS_AFD_ENDPOINT_TYPE( compareEndpoint ) ); ASSERT( compareEndpoint->GroupType == GroupTypeConstrained ); // // Skip this endpoint if the group IDs don't match. // if( groupId != compareEndpoint->GroupID ) { continue; } // // If this is a datagram endpoint, check it's remote address. // if( IS_DGRAM_ENDPOINT( compareEndpoint ) ) { AfdAcquireSpinLock( &compareEndpoint->SpinLock, &lockHandle ); if( compareEndpoint->Common.Datagram.RemoteAddress != NULL ) { result = AfdCompareAddresses( compareEndpoint->Common.Datagram.RemoteAddress, compareEndpoint->Common.Datagram.RemoteAddressLength, requestAddress, requestAddressLength ); } AfdReleaseSpinLock( &compareEndpoint->SpinLock, &lockHandle ); if( !result ) { break; } } else { // // Not a datagram. If it's a connected endpoint, still has // a connection object, and that object has a remote address, // then compare the addresses. // AfdAcquireSpinLock( &compareEndpoint->SpinLock, &lockHandle ); connection = AFD_CONNECTION_FROM_ENDPOINT( compareEndpoint ); if( compareEndpoint->State == AfdEndpointStateConnected && connection != NULL ) { REFERENCE_CONNECTION( connection ); if( connection->RemoteAddress != NULL ) { result = AfdCompareAddresses( connection->RemoteAddress, connection->RemoteAddressLength, requestAddress, requestAddressLength ); } AfdReleaseSpinLock( &compareEndpoint->SpinLock, &lockHandle ); DEREFERENCE_CONNECTION( connection ); if( !result ) { break; } } else { AfdReleaseSpinLock( &compareEndpoint->SpinLock, &lockHandle ); } } } ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); AfdDereferenceGroup( validateInfo->GroupID ); if( !result ) { status = STATUS_INVALID_PARAMETER; } Complete: Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest( Irp, AfdPriorityBoost ); return status; } // AfdValidateGroup BOOLEAN AfdCompareAddresses( IN PTRANSPORT_ADDRESS Address1, IN ULONG Address1Length, IN PTRANSPORT_ADDRESS Address2, IN ULONG Address2Length ) /*++ Routine Description: This routine compares two addresses in a special way to support constrained socket groups. This routine will return TRUE if the two addresses represent the same "interface". By "interface", I mean something like an IP address or an IPX address. Note that for some address types (such as IP) certain portions of the address should be ignored (such as the port). I really hate hard-coded knowledge of "select" address types, but there's no easy way around it. Ideally, this should be the protocol driver's responsibility. We could really use a standard "compare these addresses" IOCTL in TDI. Arguments: Address1 - The first address. Address1Length - The length of Address1. Address2 - The second address. Address2Length - The length of Address2. Return Value: BOOLEAN - TRUE if the addresses reference the same interface, FALSE otherwise. --*/ { USHORT addressType; if (Address1Length!=Address2Length) return FALSE; if (Address1Length<(ULONG)FIELD_OFFSET (TRANSPORT_ADDRESS,Address[0].Address)) { return FALSE; } addressType = Address1->Address[0].AddressType; if( addressType != Address2->Address[0].AddressType ) { // // If they're not the same address type, they can't be the // same address... // return FALSE; } // // Special case a few addresses. // switch( addressType ) { case TDI_ADDRESS_TYPE_IP : { TDI_ADDRESS_IP UNALIGNED * ip1; TDI_ADDRESS_IP UNALIGNED * ip2; ip1 = (PVOID)&Address1->Address[0].Address[0]; ip2 = (PVOID)&Address2->Address[0].Address[0]; // // IP addresses. Compare the address portion (ignoring // the port). // if( (Address1Length>=(ULONG)FIELD_OFFSET (TA_IP_ADDRESS, Address[0].Address[0].sin_zero)) && (ip1->in_addr == ip2->in_addr) ) { return TRUE; } } return FALSE; case TDI_ADDRESS_TYPE_IP6 : { TDI_ADDRESS_IP6 UNALIGNED * ip1; TDI_ADDRESS_IP6 UNALIGNED * ip2; ip1 = (PVOID)&Address1->Address[0].Address; ip2 = (PVOID)&Address2->Address[0].Address; // // IPv6 addresses. Compare the address portion (ignoring // the port and flow info). // if( (Address1Length>=sizeof (TA_IP6_ADDRESS)) && RtlEqualMemory(ip1->sin6_addr, ip2->sin6_addr, sizeof (ip1->sin6_addr)) ) { return TRUE; } } return FALSE; case TDI_ADDRESS_TYPE_IPX : { TDI_ADDRESS_IPX UNALIGNED * ipx1; TDI_ADDRESS_IPX UNALIGNED * ipx2; ipx1 = (PVOID)&Address1->Address[0].Address[0]; ipx2 = (PVOID)&Address2->Address[0].Address[0]; // // IPX addresses. Compare the network and node addresses. // if( (Address1Length>=sizeof (TA_IPX_ADDRESS)) && ipx1->NetworkAddress == ipx2->NetworkAddress && RtlEqualMemory( ipx1->NodeAddress, ipx2->NodeAddress, sizeof(ipx1->NodeAddress) ) ) { return TRUE; } } return FALSE; case TDI_ADDRESS_TYPE_APPLETALK : { TDI_ADDRESS_APPLETALK UNALIGNED * atalk1; TDI_ADDRESS_APPLETALK UNALIGNED * atalk2; atalk1 = (PVOID)&Address1->Address[0].Address[0]; atalk2 = (PVOID)&Address2->Address[0].Address[0]; // // APPLETALK address. Compare the network and node // addresses. // if( (Address1Length>=sizeof (TA_APPLETALK_ADDRESS)) && (atalk1->Network == atalk2->Network) && (atalk1->Node == atalk2->Node) ) { return TRUE; } } return FALSE; case TDI_ADDRESS_TYPE_VNS : { TDI_ADDRESS_VNS UNALIGNED * vns1; TDI_ADDRESS_VNS UNALIGNED * vns2; vns1 = (PVOID)&Address1->Address[0].Address[0]; vns2 = (PVOID)&Address2->Address[0].Address[0]; // // VNS addresses. Compare the network and subnet addresses. // if( (Address1Length>=sizeof (TA_VNS_ADDRESS)) && RtlEqualMemory( vns1->net_address, vns2->net_address, sizeof(vns1->net_address) ) && RtlEqualMemory( vns1->subnet_addr, vns2->subnet_addr, sizeof(vns1->subnet_addr) ) ) { return TRUE; } } return FALSE; default : // // Unknown address type. Do a simple memory compare. // return (BOOLEAN)RtlEqualMemory( Address1, Address2, Address2Length ); } } // AfdCompareAddresses NTSTATUS AfdGetUnacceptedConnectData ( IN PFILE_OBJECT FileObject, IN ULONG IoctlCode, IN KPROCESSOR_MODE RequestorMode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG_PTR Information ) { PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; AFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo; AFD_LOCK_QUEUE_HANDLE lockHandle; ULONG dataLength; PMDL mdl; NTSTATUS status; UCHAR localBuffer[AFD_FAST_CONNECT_DATA_SIZE]; UNREFERENCED_PARAMETER (IoctlCode); // // Set up local pointers. // endpoint = FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); status = STATUS_SUCCESS; mdl = NULL; *Information = 0; // // Validate the request. // if( !endpoint->Listening || InputBufferLength < sizeof(connectInfo) ) { return STATUS_INVALID_PARAMETER; } try { // // Validate the input structure if it comes from the user mode // application // if (RequestorMode != KernelMode ) { ProbeForReadSmallStructure (InputBuffer, sizeof (connectInfo), PROBE_ALIGNMENT(AFD_UNACCEPTED_CONNECT_DATA_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 // connectInfo = *((PAFD_UNACCEPTED_CONNECT_DATA_INFO)InputBuffer); if (connectInfo.LengthOnly && OutputBufferLength0) { if (OutputBufferLength>sizeof (localBuffer)) { mdl = IoAllocateMdl( OutputBuffer, // VirtualAddress OutputBufferLength, // Length FALSE, // SecondaryBuffer TRUE, // ChargeQuota NULL // Irp ); if (mdl==NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } MmProbeAndLockPages( mdl, // MemoryDescriptorList RequestorMode, // AccessMode IoWriteAccess // Operation ); OutputBuffer = MmGetSystemAddressForMdlSafe(mdl, LowPagePriority); if (OutputBuffer==NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } } else { if (RequestorMode!=KernelMode) { ProbeForWrite (OutputBuffer, OutputBufferLength, sizeof (UCHAR)); } } } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); goto exit; } AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); // // Find the specified connection. // connection = AfdFindReturnedConnection( endpoint, connectInfo.Sequence ); if( connection == NULL ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_INVALID_PARAMETER; goto exit; } // // Determine the length of any received connect data. // dataLength = 0; connectDataBuffers = connection->ConnectDataBuffers; if( connectDataBuffers != NULL && connectDataBuffers->ReceiveConnectData.Buffer != NULL ) { dataLength = connectDataBuffers->ReceiveConnectData.BufferLength; } // // If the caller is just interested in the data length, return it. // if( connectInfo.LengthOnly ) { connectInfo.ConnectDataLength = dataLength; AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { RtlCopyMemory (OutputBuffer, &connectInfo, sizeof (connectInfo)); *Information = sizeof (connectInfo); } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); } goto exit; } // // If there is no connect data, complete the IRP with no bytes. // if( dataLength == 0 ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); goto exit; } // // If the output buffer is too small, fail. // if( OutputBufferLength < dataLength ) { AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); status = STATUS_BUFFER_TOO_SMALL; goto exit; } RtlCopyMemory( mdl ? OutputBuffer : localBuffer, connectDataBuffers->ReceiveConnectData.Buffer, dataLength ); *Information = dataLength; AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); if (mdl==NULL) { AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { RtlCopyMemory (OutputBuffer, localBuffer, *Information); } except (AFD_EXCEPTION_FILTER (status)) { ASSERT (NT_ERROR (status)); *Information = 0; } } exit: if (mdl!=NULL) { if (mdl->MdlFlags & MDL_PAGES_LOCKED) { MmUnlockPages (mdl); } IoFreeMdl (mdl); } return status; } // AfdGetUnacceptedConnectData #ifdef _WIN64 ULONG AfdComputeCMSGLength32 ( PVOID ControlBuffer, ULONG ControlLength ) { ULONG length = 0; ASSERT (ControlLength>=sizeof (TDI_CMSGHDR)); while (ControlLength>=sizeof (TDI_CMSGHDR)) { PTDI_CMSGHDR hdr; hdr = ControlBuffer; // // Data comes from the trusted kernel mode driver source. // ASSERT (ControlLength >= TDI_CMSGHDR_ALIGN((hdr)->cmsg_len)); ControlLength -= (ULONG)TDI_CMSGHDR_ALIGN((hdr)->cmsg_len); ControlBuffer = (PUCHAR)ControlBuffer + TDI_CMSGHDR_ALIGN((hdr)->cmsg_len); length += (ULONG)TDI_CMSGHDR_ALIGN32( (hdr)->cmsg_len - TDI_CMSGDATA_ALIGN (sizeof (TDI_CMSGHDR)) + TDI_CMSGDATA_ALIGN32(sizeof(TDI_CMSGHDR32)) ); } ASSERT (ControlLength==0); return length; } VOID AfdCopyCMSGBuffer32 ( PVOID Dst, PVOID ControlBuffer, ULONG CopyLength ) { while (CopyLength>=sizeof (TDI_CMSGHDR32)) { PTDI_CMSGHDR hdr; PTDI_CMSGHDR32 hdr32; hdr = ControlBuffer; hdr32 = Dst; hdr32->cmsg_len = (ULONG)( (hdr)->cmsg_len - TDI_CMSGDATA_ALIGN (sizeof (TDI_CMSGHDR)) + TDI_CMSGDATA_ALIGN32(sizeof(TDI_CMSGHDR32)) ); hdr32->cmsg_level = hdr->cmsg_level; hdr32->cmsg_type = hdr->cmsg_type; if (CopyLength<(ULONG)TDI_CMSGHDR_ALIGN32(hdr32->cmsg_len)) break; CopyLength -= (ULONG)TDI_CMSGHDR_ALIGN32(hdr32->cmsg_len); RtlMoveMemory ((PUCHAR)hdr32+TDI_CMSGDATA_ALIGN32(sizeof(TDI_CMSGHDR32)), (PUCHAR)hdr+TDI_CMSGDATA_ALIGN(sizeof(TDI_CMSGHDR)), hdr32->cmsg_len-TDI_CMSGDATA_ALIGN32(sizeof(TDI_CMSGHDR32))); ControlBuffer = (PUCHAR)ControlBuffer + TDI_CMSGHDR_ALIGN((hdr)->cmsg_len); Dst = (PUCHAR)Dst + TDI_CMSGHDR_ALIGN32((hdr32)->cmsg_len); } } #endif //_WIN64 // // This is currently not used by helper dlls. // Commented out because of security concerns // #if NOT_YET // // Context structure allocated for non-blocking IOCTLs // typedef struct _AFD_NBIOCTL_CONTEXT { AFD_REQUEST_CONTEXT Context; // Context to keep track of request ULONG PollEvent; // Poll event to signal upon completion // IRP Irp; // Irp to queue transport // PCHAR SystemBuffer; // Input buffer if method!=3 } AFD_NBIOCTL_CONTEXT, *PAFD_NBIOCTL_CONTEXT; NTSTATUS FASTCALL AfdDoTransportIoctl ( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Passes the request from the helper DLL to the TDI transport driver. In oreder to let the IO system properly complete asynchronous IOCTL issued by the helper DLL, it should come on the socket handle (afd endpoint object), and then afd redirects it to the transport driver on the handle that herlper DLL specifies (normally address, connection, or control channel handle) Arguments: Irp IrpSp Return Value: NTSTATUS --*/ { PAFD_ENDPOINT endpoint; AFD_TRANSPORT_IOCTL_INFO ioctlInfo; PFILE_OBJECT fileObject; PDEVICE_OBJECT deviceObject; ULONG method; PIRP newIrp; PIO_STACK_LOCATION nextSp; PAFD_REQUEST_CONTEXT requestCtx; NTSTATUS status; PAGED_CODE (); endpoint = IrpSp->FileObject->FsContext; ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint)); method = IrpSp->Parameters.DeviceIoControl.IoControlCode & 3; if (method==METHOD_NEITHER) { // // We have to manually verify input buffer // AFD_W4_INIT status = STATUS_SUCCESS; try { #ifdef _WIN64 if (IoIs32bitProcess (Irp)) { PAFD_TRANSPORT_IOCTL_INFO32 ioctlInfo32; if (IrpSp->Parameters.DeviceIoControl.InputBufferLengthParameters.DeviceIoControl.Type3InputBuffer; if( Irp->RequestorMode != KernelMode ) { ProbeForReadSmallStructure( ioctlInfo32, sizeof(*ioctlInfo32), PROBE_ALIGNEMENT(AFD_TRANSPORT_IOCTL_INFO32) ); } ioctlInfo.Handle = ioctlInfo32->Handle; ioctlInfo.InputBuffer = ioctlInfo32->InputBuffer; ioctlInfo.InputBufferLength = ioctlInfo32->InputBufferLength; ioctlInfo.IoControlCode = ioctlInfo32->IoControlCode; ioctlInfo.AfdFlags = ioctlInfo32->AfdFlags; ioctlInfo.PollEvent = ioctlInfo32->PollEvent; } else #endif // _WIN64 { if (IrpSp->Parameters.DeviceIoControl.InputBufferLengthRequestorMode != KernelMode ) { ProbeForReadSmallStructure( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ioctlInfo), PROBE_ALIGNMENT(AFD_TRANSPORT_IOCTL_INFO) ); } ioctlInfo = *((PAFD_TRANSPORT_IOCTL_INFO) IrpSp->Parameters.DeviceIoControl.Type3InputBuffer); } } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); // // Exception accessing input structure. // goto Complete; } } else { #ifdef _WIN64 if (IoIs32bitProcess (Irp)) { PAFD_TRANSPORT_IOCTL_INFO32 ioctlInfo32; if (IrpSp->Parameters.DeviceIoControl.InputBufferLengthAssociatedIrp.SystemBuffer; ioctlInfo.Handle = ioctlInfo32->Handle; ioctlInfo.InputBuffer = ioctlInfo32->InputBuffer; ioctlInfo.InputBufferLength = ioctlInfo32->InputBufferLength; ioctlInfo.IoControlCode = ioctlInfo32->IoControlCode; ioctlInfo.AfdFlags = ioctlInfo32->AfdFlags; ioctlInfo.PollEvent = ioctlInfo32->PollEvent; } else #endif // _WIN64 { if (IrpSp->Parameters.DeviceIoControl.InputBufferLengthAssociatedIrp.SystemBuffer); } } // // We rely as much as we can on the IO system to process // IOCTL parameters for us. For this we have to make // sure that method of AFD and helper DLL IOCTLs // are the same, otherwise, someone can play tricks with // buffer verification on us. // // If endpoint is non-blocking and request is not overlapped // helper DLL MUST specify an event to check before queueing // the request/signal upon its completion // if ((method!=(ioctlInfo.IoControlCode & 3)) || (endpoint->NonBlocking && !(ioctlInfo.AfdFlags & AFD_OVERLAPPED) && !ioctlInfo.PollEvent) ) { status = STATUS_INVALID_PARAMETER; goto Complete; } // // Make sure application has access to handle // and get object reference // status = ObReferenceObjectByHandle( ioctlInfo.Handle, (ioctlInfo.IoControlCode >> 14) & 3, // DesiredAccess *IoFileObjectType, // Must be a file object Irp->RequestorMode, (PVOID *)&fileObject, NULL ); if (NT_SUCCESS(status)) { // // Get the device object of the driver to which we send the IRP // deviceObject = IoGetRelatedDeviceObject (fileObject); // // If this is a non-blocking endpoint and IO is not overlapped // and the specified event is not signalled, // we'll have complete the helper DLL IRP with // STATUS_DEVICE_NOT_READY (translates to WSAEWOUDLBLOCK) // and queue another IRP to the transport so that // the specified event can be completed when IRP is completed // if (endpoint->NonBlocking && !(ioctlInfo.AfdFlags & AFD_OVERLAPPED) && !(ioctlInfo.PollEvent & endpoint->EventsActive)) { PAFD_NBIOCTL_CONTEXT nbIoctlCtx; USHORT irpSize; ULONG allocSize; irpSize = IoSizeOfIrp (deviceObject->StackSize); // // Compute the block size and check for overflow // allocSize = sizeof (*nbIoctlCtx) + irpSize + ioctlInfo.InputBufferLength; if (allocSize < ioctlInfo.InputBufferLength || allocSize < irpSize) { status = STATUS_INVALID_PARAMETER; ObDereferenceObject (fileObject); goto Complete; } // // Allocate an IRP and associated strucutures // try { nbIoctlCtx = AFD_ALLOCATE_POOL_WITH_QUOTA ( NonPagedPool, allocSize, AFD_TRANSPORT_IRP_POOL_TAG ); // AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag ASSERT (nbIoctlCtx!=NULL); } except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode (); ObDereferenceObject (fileObject); goto Complete; } // // Initialize context structures // requestCtx = &nbIoctlCtx->Context; requestCtx->CleanupRoutine = AfdCleanupNBTransportIoctl; nbIoctlCtx->PollEvent = ioctlInfo.PollEvent; // // Initialize IRP itself // newIrp = (PIRP)(nbIoctlCtx+1); IoInitializeIrp( newIrp, irpSize, deviceObject->StackSize); newIrp->RequestorMode = KernelMode; newIrp->Tail.Overlay.AuxiliaryBuffer = NULL; newIrp->Tail.Overlay.OriginalFileObject = IrpSp->FileObject; nextSp = IoGetNextIrpStackLocation (newIrp); if ((ioctlInfo.InputBuffer!=NULL) && (ioctlInfo.InputBufferLength>0)) { // // If helper DLL specified input buffer // we'll have to make a copy of it in case // driver really pends the IRP while we complete the // helper DLL IRP an system frees the input buffer // PVOID newBuffer; newBuffer = (PUCHAR)newIrp+IoSizeOfIrp(deviceObject->StackSize); AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { if (Irp->RequestorMode != KernelMode) { ProbeForRead( ioctlInfo.InputBuffer, ioctlInfo.InputBufferLength, sizeof(UCHAR) ); } RtlCopyMemory (newBuffer, ioctlInfo.InputBuffer, ioctlInfo.InputBufferLength); } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); // // Exception accessing input structure. // AFD_FREE_POOL (nbIoctlCtx, AFD_TRANSPORT_IRP_POOL_TAG); ObDereferenceObject (fileObject); goto Complete; } // // Store new buffer parameters in appropriate places // in the IRP depending on the method // if (method==METHOD_NEITHER) { nextSp->Parameters.DeviceIoControl.Type3InputBuffer = newBuffer; newIrp->AssociatedIrp.SystemBuffer = NULL; } else { nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL; newIrp->AssociatedIrp.SystemBuffer = newBuffer; } nextSp->Parameters.DeviceIoControl.InputBufferLength = ioctlInfo.InputBufferLength; } else { // // No input buffer, clear correspoinding entries // nextSp->Parameters.DeviceIoControl.InputBufferLength = 0; nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL; newIrp->AssociatedIrp.SystemBuffer = NULL; } // // NOTE: We do not allow output buffer parameters on // non-blocking calls because the output buffer is deallocated // when we complete helper DLL IRP // // Done during IRP initialization (IoInitializeIrp) // newIrp->MdlAddress = NULL; // newIrp->UserBuffer = NULL; // nextSp->Parameters.DeviceIoControl.OutputBufferLength = 0; IoSetCompletionRoutine( newIrp, AfdCompleteNBTransportIoctl, nbIoctlCtx, TRUE, TRUE, TRUE ); } else { // // Blocking call, reuse the application's IRP // newIrp = Irp; nextSp = IoGetNextIrpStackLocation (Irp); nextSp->Parameters.DeviceIoControl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; if ((ioctlInfo.InputBuffer!=NULL) && (ioctlInfo.InputBufferLength>0)) { // // If application wants to pass input buffer to transport, // we'll have to copy it to the system buffer allocated with // Irp // if (method!=METHOD_NEITHER) { ULONG sysBufferLength; if (method==METHOD_BUFFERED) { sysBufferLength = max ( IrpSp->Parameters.DeviceIoControl.InputBufferLength, IrpSp->Parameters.DeviceIoControl.OutputBufferLength); } else { sysBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; } // // Methods 0-2 use system buffer to pass input // parameters and we need to reuse original system buffer // Make sure it has enough space for this purpose // AFD_W4_INIT ASSERT (status == STATUS_SUCCESS); try { if (Irp->RequestorMode != KernelMode) { ProbeForRead( ioctlInfo.InputBuffer, ioctlInfo.InputBufferLength, sizeof(UCHAR) ); } if (ioctlInfo.InputBufferLength>sysBufferLength){ PVOID newSystemBuffer; newSystemBuffer = ExAllocatePoolWithQuotaTag ( NonPagedPool|POOL_RAISE_IF_ALLOCATION_FAILURE, ioctlInfo.InputBufferLength, AFD_SYSTEM_BUFFER_POOL_TAG ); if (newSystemBuffer==NULL) { ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES); } ExFreePool (Irp->AssociatedIrp.SystemBuffer); Irp->AssociatedIrp.SystemBuffer = newSystemBuffer; } // // Copy application data to the system buffer // RtlCopyMemory (Irp->AssociatedIrp.SystemBuffer, ioctlInfo.InputBuffer, ioctlInfo.InputBufferLength); } except( AFD_EXCEPTION_FILTER (status) ) { ASSERT (NT_ERROR (status)); ObDereferenceObject (fileObject); goto Complete; } nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL; } else { // // METHOD_NEITHER, just pass whatever application // passed to use, the driver should handle it // appropriately. // // This is of course a potentialy security breach // if transport driver is buggy // nextSp->Parameters.DeviceIoControl.Type3InputBuffer = ioctlInfo.InputBuffer; } nextSp->Parameters.DeviceIoControl.InputBufferLength = ioctlInfo.InputBufferLength; } else { // // No input buffer, clear correspoiding parameters // Note that we can't clean system buffer as // it has to be deallocated on completion // nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL; nextSp->Parameters.DeviceIoControl.InputBufferLength = 0; } // // We reuse our stack location parameters area for context // requestCtx = (PAFD_REQUEST_CONTEXT)&IrpSp->Parameters.DeviceIoControl; requestCtx->CleanupRoutine = AfdCleanupTransportIoctl; IoSetCompletionRoutine( newIrp, AfdCompleteTransportIoctl, requestCtx, TRUE, TRUE, TRUE ); } // // Set the rest of IRP fields. // nextSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; nextSp->FileObject = fileObject; nextSp->Parameters.DeviceIoControl.IoControlCode = ioctlInfo.IoControlCode; // // Insert context into the endpoint list so we can cancel // the IRP when endpoint is being closed and reference endpoint // so it does not go away until this IRP is completed // requestCtx->Context = newIrp; REFERENCE_ENDPOINT (endpoint); AfdEnqueueRequest(endpoint,requestCtx); // // Finally call the transport driver // status = IoCallDriver (deviceObject, newIrp); // // We no longer need our private reference to the file object // IO system will take care of keeping this reference while our IRP // is there // ObDereferenceObject (fileObject); // // If we used helper DLL IRP, just return whatever transport // driver returned to us // if (newIrp==Irp) return status; // // If driver pended or immediately completed non-blocking call, // make sure helper DLL gets WSAEWOULDBLOCK. It will have to // call again whenever the driver completes the IRP and corresponding // event is set (if driver completed the IRP, event is set already). // if (NT_SUCCESS (status)) status = STATUS_DEVICE_NOT_READY; } // // Complete the application request in case of processing failure or // non-blocking call // Complete: Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest( Irp, AfdPriorityBoost ); return status; } NTSTATUS AfdCompleteTransportIoctl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Called to complete transport driver IOCTL for blocking endpoints Arguments: Return Value: STATUS_SUCCESS - IO system should finish IRP processing STATUS_MORE_PROCESSING_REQUIRED - we are not yet done (we are actually in the middle of cancelling) --*/ { PAFD_ENDPOINT endpoint = Irp->Tail.Overlay.OriginalFileObject->FsContext; PAFD_REQUEST_CONTEXT requestCtx = Context; AFD_LOCK_QUEUE_HANDLE lockHandle; NTSTATUS status = STATUS_SUCCESS; // // We used Parameters structure in our stack location for context // ASSERT (&(IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl) ==Context); ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint)); AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); // // We use list entry fields to synchronize with cleanup/cancel // routine assuming that as long as the entry is in the list // both Flink and Blink fields cannot be NULL. (using these // fields for synchronization allows us to cut down on // cancel spinlock usage) // if (AfdIsRequestInQueue(requestCtx)) { // // Context is still in the list, just remove it so // noone can see it anymore // RemoveEntryList (&requestCtx->EndpointListLink); } else if (AfdIsRequestCompleted(requestCtx)) { // // During endpoint cleanup, this context was removed from the // list and cancel routine is about to be called, don't let // IO system free this IRP until cancel routine is called // Also, indicate to the cancel routine that we are done // with this IRP and it can complete it. // AfdMarkRequestCompleted (requestCtx); status = STATUS_MORE_PROCESSING_REQUIRED; } AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); // // Release reference added when we posted this IRP // DEREFERENCE_ENDPOINT (endpoint); return status; } NTSTATUS AfdCompleteNBTransportIoctl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Called to complete transport driver IOCTL for non-blocking endpoints Arguments: Return Value: STATUS_MORE_PROCESSING_REQUIRED - we handle releasing resources for this IRP ourselves --*/ { PAFD_ENDPOINT endpoint = Irp->Tail.Overlay.OriginalFileObject->FsContext; PAFD_NBIOCTL_CONTEXT nbIoctlCtx = Context; PAFD_REQUEST_CONTEXT requestCtx = &nbIoctlCtx->Context; AFD_LOCK_QUEUE_HANDLE lockHandle; // // The irp should be a part of our notify structure // ASSERT (Irp==(PIRP)(nbIoctlCtx+1)); ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint)); // // First indicate the event reported by the driver // ASSERT (nbIoctlCtx->PollEvent!=0); AfdIndicatePollEvent (endpoint, 1<PollEvent, Irp->IoStatus.Status); AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle ); AfdIndicateEventSelectEvent (endpoint, 1<PollEvent, Irp->IoStatus.Status); // // We use list entry fields to synchronize with cleanup/cancel // routine assuming that as long as the entry is in the list // both Flink and Blink fields cannot be NULL. (using these // fields for synchronization allows us to cut down on // cancel spinlock usage) // if (AfdIsRequestInQueue(requestCtx)) { // // Context is still in the list, just remove it so // noone can see it anymore and free the structure // RemoveEntryList (&requestCtx->EndpointListLink); AFD_FREE_POOL (nbIoctlCtx, AFD_TRANSPORT_IRP_POOL_TAG); } else if (AfdIsRequestCompleted (requestCtx)) { // // During endpoint cleanup, this context was removed from the // list and cancel routine is about to be called, don't // free this IRP until cancel routine is called // Also, indicate to the cancel routine that we are done // with this IRP and it can free it. // AfdMarkRequestCompleted (requestCtx); } else { // // Cancel routine has completed processing this request, free it // AFD_FREE_POOL (nbIoctlCtx, AFD_TRANSPORT_IRP_POOL_TAG); } AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle ); // // Release reference added when we posted this IRP // DEREFERENCE_ENDPOINT (endpoint); return STATUS_MORE_PROCESSING_REQUIRED; } BOOLEAN AfdCleanupTransportIoctl ( PAFD_ENDPOINT Endpoint, PAFD_REQUEST_CONTEXT RequestCtx ) /*++ Routine Description: Cancels outstanding transport IOCTL during endpoint cleanup Used for blocking requests. Arguments: Endpoint - endpoint on which IOCTL was issued RequestCtx - context associated with the request Return Value: TRUE - request has been completed FALSE - request is still in driver's queue --*/ { PIRP Irp = RequestCtx->Context; AFD_LOCK_QUEUE_HANDLE lockHandle; // // First attempt to cancel the IRP, if it is already completed // this is just a no-op. In no case IRP and request structure // could have been freed until we mark it as completed as // the caller of this routine should have marked the request // as being cancelled // ASSERT (RequestCtx->EndpointListLink.Flink==NULL); IoCancelIrp (Irp); AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle); if (AfdIsRequestCompleted (RequestCtx)) { // // Driver has initiated the completion of the request // as we were cancelling it. // "Complete the completion" // AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle); IoCompleteRequest (Irp, IO_NO_INCREMENT); return TRUE; } else { // // Driver has not completed the request before returning // from cancel routine, mark the request to indicate // that we are done with it and completion routine // can free it // AfdMarkRequestCompleted (RequestCtx); AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle); return FALSE; } } BOOLEAN AfdCleanupNBTransportIoctl ( PAFD_ENDPOINT Endpoint, PAFD_REQUEST_CONTEXT RequestCtx ) /*++ Routine Description: Cancels outstanding transport IOCTL during endpoint cleanup Used for non-blocking requests Arguments: Endpoint - endpoint on which IOCTL was issued RequestCtx - context associated with the request Return Value: TRUE - request has been completed FALSE - request is still in driver's queue --*/ { PIRP Irp = RequestCtx->Context; AFD_LOCK_QUEUE_HANDLE lockHandle; // // The IRP should be a part of the context block, verify. // ASSERT (Irp==(PIRP)(CONTAINING_RECORD (RequestCtx, AFD_NBIOCTL_CONTEXT, Context)+1)); // // First attempt to cancel the IRP, if it is already completed // this is just a no-op. In no case IRP and request structure // could have been freed until we mark it as completed as // the caller of this routine should have marked the request // as being cancelled // ASSERT (RequestCtx->EndpointListLink.Flink==NULL); IoCancelIrp (Irp); AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle); if (AfdIsRequestCompleted (RequestCtx)) { // // Driver has initiated the completion of the request // as we were cancelling it. // Free the context structure. // AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle); AFD_FREE_POOL ( CONTAINING_RECORD (RequestCtx, AFD_NBIOCTL_CONTEXT, Context), AFD_TRANSPORT_IRP_POOL_TAG); return TRUE; } else { // // Driver has not completed the request before returning // from cancel routine, mark the request to indicate // that we are done with it and completion routine // can free it // AfdMarkRequestCompleted (RequestCtx); AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle); return FALSE; } } #endif // NOT_YET NTSTATUS AfdQueryProviderInfo ( IN PUNICODE_STRING TransportDeviceName, #ifdef _AFD_VARIABLE_STACK_ OUT CCHAR *StackSize OPTIONAL, #endif //_AFD_VARIABLE_STACK OUT PTDI_PROVIDER_INFO ProviderInfo ) /*++ Routine Description: Returns a provider information structure corresponding to the specified TDI transport provider. Arguments: TransportDeviceName - the name of the TDI transport provider. ProviderInfo - buffer to place provider info into Return Value: STATUS_SUCCESS - returned transport info is valid. STATUS_OBJECT_NAME_NOT_FOUND - transport's device is not available yet --*/ { NTSTATUS status; HANDLE controlChannel; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK iosb; TDI_REQUEST_KERNEL_QUERY_INFORMATION kernelQueryInfo; PAGED_CODE (); // // Set up the IRP stack location information to query the TDI // provider information. // kernelQueryInfo.QueryType = TDI_QUERY_PROVIDER_INFORMATION; kernelQueryInfo.RequestConnectionInformation = NULL; // // Open a control channel to the TDI provider. // We ask to create a kernel handle which is // the handle in the context of the system process // so that application cannot close it on us while // we are creating and referencing it. // InitializeObjectAttributes( &objectAttributes, TransportDeviceName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // attributes NULL, NULL ); status = IoCreateFile( &controlChannel, MAXIMUM_ALLOWED, &objectAttributes, &iosb, // returned status information. 0, // block size (unused). 0, // file attributes. FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, // create disposition. 0, // create options. NULL, // eaInfo 0, // eaLength CreateFileTypeNone, // CreateFileType NULL, // ExtraCreateParameters IO_FORCE_ACCESS_CHECK // Options | IO_NO_PARAMETER_CHECKING ); if ( NT_SUCCESS(status) ) { PFILE_OBJECT controlObject; status = ObReferenceObjectByHandle ( controlChannel, // Handle MAXIMUM_ALLOWED, // DesiredAccess *IoFileObjectType, // ObjectType KernelMode, // AccessMode (PVOID *)&controlObject, // Object, NULL // HandleInformation ); if (NT_SUCCESS (status)) { #ifdef _AFD_VARIABLE_STACK_ if (ARGUMENT_PRESENT (StackSize)) { *StackSize = IoGetRelatedDeviceObject (controlObject)->StackSize; } #endif // _AFD_VARIABLE_STACK_ // // Get the TDI provider information for the transport. // status = AfdIssueDeviceControl( controlObject, &kernelQueryInfo, sizeof(kernelQueryInfo), ProviderInfo, sizeof(*ProviderInfo), TDI_QUERY_INFORMATION ); ObDereferenceObject (controlObject); } ZwClose( controlChannel ); } if (!NT_SUCCESS (status)) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL, "AfdQueryProviderInfo:" "Transport %*ls failed provider info query with status %lx.\n", TransportDeviceName->Length/2, TransportDeviceName->Buffer, status)); } return status; } BOOLEAN AfdCancelIrp ( IN PIRP Irp ) /*++ Routine Description: This routine is invoked to cancel an individual I/O Request Packet. It is similiar to IoCancelIrp() except that it *must* be called with the cancel spin lock held. This routine exists because of the synchronization requirements of the cancellation/completion of transmit IRPs. Arguments: Irp - Supplies a pointer to the IRP to be cancelled. The CancelIrql field of the IRP must have been correctly initialized with the IRQL from the cancel spin lock acquisition. Return Value: The function value is TRUE if the IRP was in a cancellable state (it had a cancel routine), else FALSE is returned. Notes: It is assumed that the caller has taken the necessary action to ensure that the packet cannot be fully completed before invoking this routine. --*/ { PDRIVER_CANCEL cancelRoutine; // // Make sure that the cancel spin lock is held. // ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL ); // // Set the cancel flag in the IRP. // Irp->Cancel = TRUE; // // Obtain the address of the cancel routine, and if one was specified, // invoke it. // cancelRoutine = IoSetCancelRoutine( Irp, NULL ); if (cancelRoutine) { if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1)) { KeBugCheckEx( CANCEL_STATE_IN_COMPLETED_IRP, (ULONG_PTR) Irp, 0, 0, 0 ); } cancelRoutine( Irp->Tail.Overlay.CurrentStackLocation->DeviceObject, Irp ); // // The cancel spinlock should have been released by the cancel routine. // return(TRUE); } else { // // There was no cancel routine, so release the cancel spinlock and // return indicating the Irp was not currently cancelable. // IoReleaseCancelSpinLock( Irp->CancelIrql ); return(FALSE); } } // AfdCancelIrp VOID AfdTrimLookaside ( PNPAGED_LOOKASIDE_LIST Lookaside ) { PVOID entry; #if DBG LONG count = 0; #endif while (ExQueryDepthSList (&(Lookaside->L.ListHead))>Lookaside->L.Depth*2) { entry = InterlockedPopEntrySList( &Lookaside->L.ListHead); if (entry) { #if DBG count++; #endif (Lookaside->L.Free)(entry); } else { break; } } #if DBG if (count>0) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AFD: Flushed %d items from lookaside list @ %p\n", count, Lookaside)); } #endif } VOID AfdCheckLookasideLists ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { LONG i; UNREFERENCED_PARAMETER (SystemArgument1); UNREFERENCED_PARAMETER (SystemArgument2); #if DBG ASSERT (Dpc==&AfdLookasideLists->Dpc); ASSERT (DeferredContext==AfdLookasideLists); #else UNREFERENCED_PARAMETER (Dpc); UNREFERENCED_PARAMETER (DeferredContext); #endif for (i=0; iList[i].L.ListHead)) > AfdLookasideLists->List[i].L.Depth*2) { if (AfdLookasideLists->TrimFlags & (1<List[i]); AfdLookasideLists->TrimFlags &= (~(1<TrimFlags |= (1<TrimFlags & (1<TrimFlags &= (~(1<Routine = Routine; InterlockedPushEntrySList ( &AfdLRList, &Item->SListLink); count = InterlockedIncrement (&AfdLRListCount); ASSERT (count>0); if (count==1) { AfdLRStartTimer (); } } VOID AfdLRListTimeout ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ DPC routine for low resource list timer Simply schedules worker thread - do not want to do low resource processing at DPC --*/ { UNREFERENCED_PARAMETER (Dpc); UNREFERENCED_PARAMETER (DeferredContext); UNREFERENCED_PARAMETER (SystemArgument1); UNREFERENCED_PARAMETER (SystemArgument2); AfdQueueWorkItem (AfdProcessLRList, &AfdLRListWorker); } VOID AfdProcessLRList ( PVOID Param ) /*++ Routine Description: Processeses items on low resource list and reschedules processing if unprocessed items remain (still failing to buffer data due to low resource condition) Arguments: None Return Value: None Notes: --*/ { PSLIST_ENTRY localList, entry; LONG count = 0; UNREFERENCED_PARAMETER (Param); PAGED_CODE (); KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AFD: Processing low resource list: %ld entries\n", AfdLRListCount)); // // Flush the list // localList = InterlockedFlushSList (&AfdLRList); // // Reverse it to preserve order of processing (FIFO). // entry = NULL; while (localList!=NULL) { PSLIST_ENTRY next; next = localList->Next; localList->Next = entry; entry = localList; localList = next; } localList = entry; while (localList!=NULL) { PAFD_LR_LIST_ITEM item; entry = localList; localList = localList->Next; item = CONTAINING_RECORD (entry, AFD_LR_LIST_ITEM, SListLink); // // Try to restart receive processing on connection where buffer allocation failed // if (item->Routine (item)) { // // Success, decrement number of items outstanding, // and note current number of items. If we did not empty // the list, we'll have to restart the timer. // count = InterlockedDecrement (&AfdLRListCount); ASSERT (count>=0); } else { // // Failure, put it back on the list. Note, that we have at list one // item there and thus have to restart the timer again. // InterlockedPushEntrySList (&AfdLRList, &item->SListLink); count = 1; } } if (count!=0) { // // We did not empty the list, so restart the timer. // AfdLRStartTimer (); } } VOID AfdLRStartTimer ( VOID ) /*++ Routine Description: Start low resource timer to retry receive operation on connections that could not buffer data due to low reaource condition. Arguments: None Return Value: None Notes: --*/ { LARGE_INTEGER timeout; BOOLEAN res; timeout.QuadPart = -50000000i64; // 5 seconds #if DBG { TIME_FIELDS timeFields; LARGE_INTEGER currentTime; LARGE_INTEGER localTime; KeQuerySystemTime (¤tTime); currentTime.QuadPart -= timeout.QuadPart; ExSystemTimeToLocalTime (¤tTime, &localTime); RtlTimeToTimeFields (&localTime, &timeFields); KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AFD: Scheduling low resource timer for %2.2d:%2.2d:%2.2d\n", timeFields.Hour, timeFields.Minute, timeFields.Second)); } #endif KeInitializeDpc( &AfdLRListDpc, AfdLRListTimeout, &AfdLRList ); KeInitializeTimer( &AfdLRListTimer ); res = KeSetTimer( &AfdLRListTimer, timeout, &AfdLRListDpc ); ASSERT (res==FALSE); } #ifdef _AFD_VARIABLE_STACK_ VOID AfdFixTransportEntryPointsForBigStackSize ( IN OUT PAFD_TRANSPORT_INFO TransportInfo, IN CCHAR StackSize ) { KeEnterCriticalRegion (); ExAcquireResourceExclusiveLite( AfdResource, TRUE ); TransportInfo->StackSize = StackSize; if (TransportInfo->StackSize>AfdTdiStackSize) { if (TransportInfo->StackSize>AfdMaxStackSize) { AfdMaxStackSize = TransportInfo->StackSize; } TransportInfo->GetBuffer = AfdGetBufferWithMaxStackSize; TransportInfo->GetTpInfo = AfdGetTpInfoWithMaxStackSize; TransportInfo->CallDriver = AfdCallDriverStackIncrease; } ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); } VOID AfdCancelStackIncreaseIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PIRP newIrp; UNREFERENCED_PARAMETER (DeviceObject); newIrp = (PIRP)Irp->IoStatus.Information; newIrp->Cancel = TRUE; newIrp->CancelIrql = Irp->CancelIrql; AfdCancelIrp (newIrp); } NTSTATUS AfdRestartStackIncreaseIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PIRP oldIrp = Context; UNREFERENCED_PARAMETER (DeviceObject); if (IoSetCancelRoutine (oldIrp, NULL)==NULL) { KIRQL oldIrql; IoAcquireCancelSpinLock (&oldIrql); IoReleaseCancelSpinLock (oldIrql); } oldIrp->IoStatus = Irp->IoStatus; IoCompleteRequest (oldIrp, AfdPriorityBoost); IoFreeIrp (Irp); return STATUS_MORE_PROCESSING_REQUIRED; } PIRP AfdGetStackIncreaseIrp ( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) { PIRP newIrp; newIrp = IoAllocateIrp (DeviceObject->StackSize, FALSE); if (newIrp!=NULL) { *IoGetNextIrpStackLocation (newIrp) = *IoGetCurrentIrpStackLocation (Irp); IoSetCompletionRoutine (newIrp, AfdRestartStackIncreaseIrp, Irp, TRUE, TRUE, TRUE); newIrp->MdlAddress = Irp->MdlAddress; newIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread; Irp->IoStatus.Information = (ULONG_PTR)newIrp; IoSetCancelRoutine (Irp, AfdCancelStackIncreaseIrp); newIrp->Cancel = Irp->Cancel; } return newIrp; } NTSTATUS FASTCALL AfdCallDriverStackIncrease ( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) { if (Irp->CurrentLocation <= DeviceObject->StackSize) { PIRP newIrp; IoSetNextIrpStackLocation (Irp); newIrp = AfdGetStackIncreaseIrp (DeviceObject, Irp); if (newIrp!=NULL) { IoMarkIrpPending (Irp); IoCallDriver (DeviceObject, newIrp); return STATUS_PENDING; } else { Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, AfdPriorityBoost); return STATUS_INSUFFICIENT_RESOURCES; } } else { return IoCallDriver (DeviceObject, Irp); } } PIRP AfdGetStackIncreaseIrpAndRecordIt ( IN PAFD_ENDPOINT Endpoint, IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp #if DBG , IN PCHAR File, IN ULONG Line #endif ) { #if DBG if (AfdRecordOutstandingIrpDebug (Endpoint, DeviceObject, Irp, File, Line)) #else if (AfdRecordOutstandingIrp(Endpoint,DeviceObject,Irp)) #endif { PIRP newIrp; IoSetNextIrpStackLocation (Irp); newIrp = AfdGetStackIncreaseIrp (DeviceObject, Irp); if (newIrp!=NULL) { return newIrp; } AfdCompleteOutstandingIrp (Endpoint, Irp); } return NULL; } #endif // _AFD_VARIABLE_STACK_ #ifdef _AFD_VERIFY_DATA_ VOID AfdVerifyBuffer ( PAFD_CONNECTION Connection, PVOID Buffer, ULONG Length ) { if (Connection->VerifySequenceNumber!=0) { PUCHAR start, end; ULONGLONG seq; for (start=Buffer, end = (PUCHAR)Buffer+Length, seq = Connection->VerifySequenceNumber-1; start>(byte*8))) { DbgPrint ("AfdVerifyBuffer: Data sequence number mismatch on connection %p:\n" " data buffer-%p, offset-%lx, expected-%2.2lx, got-%2.2lx.\n", Connection, Buffer, start-(PUCHAR)Buffer, (UCHAR)(num>>(byte*8)), *start); DbgBreakPoint (); // // Disable verification to continue. // Connection->VerifySequenceNumber = 0; return; } } Connection->VerifySequenceNumber = seq+1; } } VOID AfdVerifyMdl ( PAFD_CONNECTION Connection, PMDL Mdl, ULONG Offset, ULONG Length ) { if (Connection->VerifySequenceNumber!=0) { while (Mdl!=NULL) { if (Offset>=MmGetMdlByteCount (Mdl)) { Offset-=MmGetMdlByteCount (Mdl); } else if (Length<=MmGetMdlByteCount (Mdl)-Offset) { AfdVerifyBuffer (Connection, (PUCHAR)MmGetSystemAddressForMdl (Mdl)+Offset, Length); break; } else { AfdVerifyBuffer (Connection, (PUCHAR)MmGetSystemAddressForMdl (Mdl)+Offset, MmGetMdlByteCount (Mdl)-Offset ); Length-=(MmGetMdlByteCount (Mdl)-Offset); Offset = 0; } Mdl = Mdl->Next; } } } ULONG AfdVerifyType = 0; ULONG AfdVerifyPort = 0; PEPROCESS AfdVerifyProcess = NULL; VOID AfdVerifyAddress ( PAFD_CONNECTION Connection, PTRANSPORT_ADDRESS Address ) { Connection->VerifySequenceNumber = 0; if ((AfdVerifyPort==0) || ((AfdVerifyProcess!=NULL) && (AfdVerifyProcess!=Connection->OwningProcess)) || ((AfdVerifyType!=0) && (AfdVerifyType!=(USHORT)Address->Address[0].AddressType)) ) { return; } switch (Address->Address[0].AddressType) { case TDI_ADDRESS_TYPE_IP : { TDI_ADDRESS_IP UNALIGNED * ip; ip = (PVOID)&Address->Address[0].Address[0]; if (ip->sin_port!=(USHORT)AfdVerifyPort) { return; } } break; case TDI_ADDRESS_TYPE_IPX : { TDI_ADDRESS_IPX UNALIGNED * ipx; ipx = (PVOID)&Address->Address[0].Address[0]; if (ipx->Socket!=(USHORT)AfdVerifyPort) { return; } } break; case TDI_ADDRESS_TYPE_APPLETALK : { TDI_ADDRESS_APPLETALK UNALIGNED * atalk; atalk = (PVOID)&Address->Address[0].Address[0]; if (atalk->Socket!=(UCHAR)AfdVerifyPort) { return; } } break; default: if (AfdVerifyType==0) return; DbgPrint ("AfdVerifyAddress: connection-%8.8lx, addres-%8.8lx\n", Connection, Address); DbgBreakPoint (); } Connection->VerifySequenceNumber = 1; } #endif // _AFD_VERIFY_DATA_ LONG AfdExceptionFilter( #if DBG IN PCHAR SourceFile, IN LONG LineNumber, #endif IN PEXCEPTION_POINTERS ExceptionPointers, OUT PNTSTATUS ExceptionCode OPTIONAL ) { PAGED_CODE (); // // Return exception code and translate alignment warnings into // alignment errors if requested. // if (ExceptionCode) { *ExceptionCode = ExceptionPointers->ExceptionRecord->ExceptionCode; if (*ExceptionCode == STATUS_DATATYPE_MISALIGNMENT) { *ExceptionCode = STATUS_DATATYPE_MISALIGNMENT_ERROR; } } #if DBG // // Protect ourselves in case the process is totally messed up. // try { PCHAR fileName; // // Strip off the path from the source file. // fileName = strrchr( SourceFile, '\\' ); if( fileName == NULL ) { fileName = SourceFile; } else { fileName++; } // // Whine about the exception. // KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL, "AfdExceptionFilter: exception %08lx @ %08lx, caught in %s:%d\n", ExceptionPointers->ExceptionRecord->ExceptionCode, ExceptionPointers->ExceptionRecord->ExceptionAddress, fileName, LineNumber )); } except( EXCEPTION_EXECUTE_HANDLER ) { // // Not much we can do here... // NOTHING; } #endif //DBG return EXCEPTION_EXECUTE_HANDLER; } // AfdExceptionFilter #if DBG LONG AfdApcExceptionFilter( PEXCEPTION_POINTERS ExceptionPointers, PCHAR SourceFile, LONG LineNumber ) { PCHAR fileName; PAGED_CODE (); // // Protect ourselves in case the process is totally messed up. // try { // // Strip off the path from the source file. // fileName = strrchr( SourceFile, '\\' ); if( fileName == NULL ) { fileName = SourceFile; } else { fileName++; } // // Whine about the exception. // KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_ERROR_LEVEL, "AfdApcExceptionFilter: exception %08lx, exr:%p cxr:%p, caught in %s:%d\n", ExceptionPointers->ExceptionRecord->ExceptionCode, ExceptionPointers->ExceptionRecord, ExceptionPointers->ContextRecord, fileName, LineNumber )); DbgBreakPoint (); } except( EXCEPTION_EXECUTE_HANDLER ) { // // Not much we can do here... // NOTHING; } return EXCEPTION_CONTINUE_SEARCH; } // AfdApcExceptionFilter #endif