|
|
/****************************************************************************/ // stack.c
//
// Routines for managing Terminal Server driver stacks.
//
// Copyright (C) 1997-2000 Microsoft Corporation
/****************************************************************************/
#include <precomp.h>
#pragma hdrstop
#include <ntimage.h>
#include <minmax.h>
#include <regapi.h>
/*
* Prototypes for procedures */
NTSTATUS IcaDeviceControlStack ( IN PICA_STACK pStack, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp );
NTSTATUS IcaCleanupStack ( IN PICA_STACK pStack, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp );
NTSTATUS IcaCloseStack ( IN PICA_STACK pStack, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp );
/*
* Local procedure prototypes */ NTSTATUS _LogError( IN PDEVICE_OBJECT pDeviceObject, IN NTSTATUS Status, IN LPWSTR * pArgStrings, IN ULONG ArgStringCount, IN PVOID pRawData, IN ULONG RawDataLength );
NTSTATUS _IcaDriverThread( IN PVOID pData );
PICA_STACK _IcaAllocateStack( VOID );
VOID _IcaFreeStack( PICA_STACK pStack );
NTSTATUS _IcaPushStack( IN PICA_STACK pStack, IN PICA_STACK_PUSH pStackPush );
NTSTATUS _IcaPopStack( IN PICA_STACK pStack );
NTSTATUS _IcaCallStack( IN PICA_STACK pStack, IN ULONG ProcIndex, IN OUT PVOID pParms );
NTSTATUS _IcaCallStackNoLock( IN PICA_STACK pStack, IN ULONG ProcIndex, IN OUT PVOID pParms );
NTSTATUS _IcaLoadSd( IN PDLLNAME SdName, OUT PSDLINK *ppSdLink );
NTSTATUS _IcaUnloadSd( IN PSDLINK pSdLink );
NTSTATUS _IcaCallSd( IN PSDLINK pSdLink, IN ULONG ProcIndex, IN PVOID pParms );
VOID _IcaReferenceSdLoad( IN PSDLOAD pSdLoad );
VOID _IcaDereferenceSdLoad( IN PSDLOAD pSdLoad );
NTSTATUS _IcaLoadSdWorker( IN PDLLNAME SdName, OUT PSDLOAD *ppSdLoad );
NTSTATUS _IcaUnloadSdWorker( IN PSDLOAD pSdLoad );
NTSTATUS IcaExceptionFilter( IN PWSTR OutputString, IN PEXCEPTION_POINTERS pexi );
NTSTATUS _RegisterBrokenEvent( IN PICA_STACK pStack, IN PICA_STACK_BROKEN pStackBroken );
NTSTATUS _EnablePassthru( PICA_STACK pStack );
NTSTATUS _DisablePassthru( PICA_STACK pStack );
NTSTATUS _ReconnectStack( PICA_STACK pStack, HANDLE hIca );
NTSTATUS IcaBindVirtualChannels( IN PICA_STACK pStack );
VOID IcaRebindVirtualChannels( IN PICA_CONNECTION pConnect );
VOID IcaUnbindVirtualChannels( IN PICA_CONNECTION pConnect );
VOID IcaFreeAllVcBind( IN PICA_CONNECTION pConnect );
/*
* Buffer Allocation counters. */
ULONG gAllocSucceed; ULONG gAllocFailed; ULONG gAllocFreed;
extern HANDLE g_TermServProcessID; ULONG g_KeepAliveInterval=0; /*
* Dispatch table for stack objects */ PICA_DISPATCH IcaStackDispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1] = { NULL, // IRP_MJ_CREATE
NULL, // IRP_MJ_CREATE_NAMED_PIPE
IcaCloseStack, // IRP_MJ_CLOSE
NULL, // IRP_MJ_READ
NULL, // IRP_MJ_WRITE
NULL, // IRP_MJ_QUERY_INFORMATION
NULL, // IRP_MJ_SET_INFORMATION
NULL, // IRP_MJ_QUERY_EA
NULL, // IRP_MJ_SET_EA
NULL, // IRP_MJ_FLUSH_BUFFERS
NULL, // IRP_MJ_QUERY_VOLUME_INFORMATION
NULL, // IRP_MJ_SET_VOLUME_INFORMATION
NULL, // IRP_MJ_DIRECTORY_CONTROL
NULL, // IRP_MJ_FILE_SYSTEM_CONTROL
IcaDeviceControlStack, // IRP_MJ_DEVICE_CONTROL
NULL, // IRP_MJ_INTERNAL_DEVICE_CONTROL
NULL, // IRP_MJ_SHUTDOWN
NULL, // IRP_MJ_LOCK_CONTROL
IcaCleanupStack, // IRP_MJ_CLEANUP
NULL, // IRP_MJ_CREATE_MAILSLOT
NULL, // IRP_MJ_QUERY_SECURITY
NULL, // IRP_MJ_SET_SECURITY
NULL, // IRP_MJ_SET_POWER
NULL, // IRP_MJ_QUERY_POWER
};
NTSTATUS IcaCreateStack ( IN PICA_CONNECTION pConnect, IN PICA_OPEN_PACKET openPacket, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp )
/*++
Routine Description:
This routine is called to create a new ICA_STACK objecte.
Arguments:
pConnect -- pointer to ICA_CONNECTION object
Irp - Pointer to I/O request packet
IrpSp - pointer to the stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{ PLIST_ENTRY Head, Next; PICA_STACK pStack; KIRQL OldIrql; LONG PrimaryCount, ShadowCount, PassthruCount, ConsoleCount; NTSTATUS Status; PICA_STACK pPrimaryStack = NULL;
/*
* Allocate a new ICA stack object */ pStack = _IcaAllocateStack(); if ( pStack == NULL ) return( STATUS_INSUFFICIENT_RESOURCES );
/*
* Finish initializing the stack object * (non-primary stacks are initialized with the fIoDisabled * flag set to TRUE, i.e. they must be manually enabled) */ pStack->StackClass = openPacket->TypeInfo.StackClass; pStack->fIoDisabled = ((pStack->StackClass != Stack_Primary) && (pStack->StackClass != Stack_Console));
/*
* Lock connection object while creating new stack */ IcaLockConnection( pConnect );
/*
* Reference the connection object this stack belongs to. */ IcaReferenceConnection( pConnect ); pStack->pConnect = (PUCHAR)pConnect;
/*
* Search the existing stacks to check for invalid combinations. * 1) there can be only 1 primary stack per connection, * 2) there can be multiple shadow stacks per connection, * but ONLY if there is no passthru stack, * 3) there can be only 1 passthru stack per connection, * but only if there is an existing primary stack AND no shadow stacks. * 4) there can be only 1 console stack */ Head = &pConnect->StackHead; PrimaryCount = ShadowCount = PassthruCount = ConsoleCount = 0; for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) { PICA_STACK pCurrentStack;
pCurrentStack = CONTAINING_RECORD( Next, ICA_STACK, StackEntry );
switch ( pCurrentStack->StackClass ) { case Stack_Primary : PrimaryCount++;
if( pStack->StackClass == Stack_Passthru) { // store the primary stack pointer for
// passthru setup
pPrimaryStack = pCurrentStack; } ASSERT(PrimaryCount == 1);
break;
case Stack_Shadow : ShadowCount++; break;
case Stack_Passthru : PassthruCount++; ASSERT(PassthruCount == 1); break;
case Stack_Console : ConsoleCount++; ASSERT(ConsoleCount == 1); break; } }
Status = STATUS_SUCCESS; switch ( pStack->StackClass ) { case Stack_Primary : if ( PrimaryCount != 0 ) Status = STATUS_INVALID_PARAMETER; break;
case Stack_Shadow : if ( PassthruCount != 0 ) Status = STATUS_INVALID_PARAMETER; break;
case Stack_Passthru : if ( PassthruCount != 0 || PrimaryCount != 1 || ShadowCount != 0 ) Status = STATUS_INVALID_PARAMETER; else { /*
* Put the stack pointers in place. * This will make sure that we don't end up in a * a race condition and drop some pdus before the * shadow sequence completes. */ // PrimaryCount is 1 so pPrimaryStack is valid
ASSERT(pPrimaryStack); pPrimaryStack->pPassthru = pStack; pStack->pPassthru = pPrimaryStack; } break;
case Stack_Console : if ( ConsoleCount != 0 ) Status = STATUS_INVALID_PARAMETER; break; }
if ( Status != STATUS_SUCCESS ) { IcaUnlockConnection( pConnect ); pStack->RefCount = 0; _IcaFreeStack( pStack ); TRACE(( pConnect, TC_ICADD, TT_ERROR, "TermDD: IcaCreateStack failed, 0x%x\n", Status )); return( Status ); }
/*
* Link this stack into the connection object stack list. */ if (( pStack->StackClass == Stack_Primary ) || ( pStack->StackClass == Stack_Console )) { InsertHeadList( &pConnect->StackHead, &pStack->StackEntry ); } else { InsertTailList( &pConnect->StackHead, &pStack->StackEntry ); }
/*
* Unlock connection object now */ IcaUnlockConnection( pConnect );
/*
* Initialize the LastKeepAliveTime field to current system time */ KeQuerySystemTime(&pStack->LastKeepAliveTime);
/*
* Lock the stack list for updating */ IcaAcquireSpinLock(&IcaStackListSpinLock, &OldIrql);
/*
* Insert the stack to the stack list, increment total number of stacks */ InsertTailList(IcaNextStack, &pStack->StackNode); IcaTotalNumOfStacks++;
/*
* Unlock the stack list now */ IcaReleaseSpinLock(&IcaStackListSpinLock, OldIrql);
/*
* Save a pointer to the stack in the file object * so that we can find it in future calls. */ IrpSp->FileObject->FsContext = pStack;
IcaDereferenceStack( pStack );
TRACE(( pConnect, TC_ICADD, TT_API1, "TermDD: IcaCreateStack, success\n" )); return( STATUS_SUCCESS ); }
NTSTATUS IcaDeviceControlStack( IN PICA_STACK pStack, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { PICA_CONNECTION pConnect; PICA_TRACE_BUFFER pTraceBuffer; PICA_STACK_RECONNECT pStackReconnect; SD_IOCTL SdIoctl; NTSTATUS Status; ULONG code; LARGE_INTEGER WaitTimeout; PLARGE_INTEGER pWaitTimeout = NULL; BYTE *Buffer = NULL;
/*
* Extract the IOCTL control code and process the request. */ code = IrpSp->Parameters.DeviceIoControl.IoControlCode;
#if DBG
if ( code != IOCTL_ICA_STACK_TRACE ) { IcaLockStack( pStack ); TRACESTACK(( pStack, TC_ICADD, TT_API2, "TermDD: IcaDeviceControlStack, fc %d (enter)\n", (code & 0x3fff) >> 2 )); IcaUnlockStack( pStack ); } #endif
try { switch ( code ) {
case IOCTL_ICA_STACK_PUSH : { ICA_STACK_PUSH StackPush;
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ICA_STACK_PUSH) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_STACK_PUSH), sizeof(BYTE) ); }
// This IOCTL should only be invoked if we are called from system process
// If not, we deny the request
if (!((BOOLEAN)IrpSp->FileObject->FsContext2)) { return (STATUS_ACCESS_DENIED); }
memcpy(&StackPush, IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_STACK_PUSH));
Status = _IcaPushStack( pStack, &StackPush ); break; } case IOCTL_ICA_STACK_POP : IcaLockConnectionForStack( pStack ); Status = _IcaPopStack( pStack ); IcaUnlockConnectionForStack( pStack ); break;
case IOCTL_ICA_STACK_QUERY_STATUS : if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(pStack->ProtocolStatus) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( Irp->UserBuffer, sizeof(pStack->ProtocolStatus), sizeof(BYTE) ); }
RtlCopyMemory( Irp->UserBuffer, &pStack->ProtocolStatus, sizeof(pStack->ProtocolStatus) ); Irp->IoStatus.Information = sizeof(pStack->ProtocolStatus); Status = STATUS_SUCCESS; break;
case IOCTL_ICA_STACK_QUERY_CLIENT : if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, sizeof(BYTE) ); }
SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = NULL; SdIoctl.InputBufferLength = 0; SdIoctl.OutputBuffer = Irp->UserBuffer; SdIoctl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; Status = _IcaCallStack( pStack, SD$IOCTL, &SdIoctl ); Irp->IoStatus.Information = SdIoctl.BytesReturned; break;
case IOCTL_ICA_STACK_QUERY_CLIENT_EXTENDED : if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, sizeof(BYTE) ); }
SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = NULL; SdIoctl.InputBufferLength = 0; SdIoctl.OutputBuffer = Irp->UserBuffer; SdIoctl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; Status = _IcaCallStack( pStack, SD$IOCTL, &SdIoctl ); Irp->IoStatus.Information = SdIoctl.BytesReturned; break;
case IOCTL_ICA_STACK_QUERY_AUTORECONNECT : if ( Irp->RequestorMode != KernelMode ) {
ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) );
ProbeForWrite( Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, sizeof(BYTE) ); } SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; SdIoctl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; SdIoctl.OutputBuffer = Irp->UserBuffer; SdIoctl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
Status = _IcaCallStack( pStack, SD$IOCTL, &SdIoctl ); Irp->IoStatus.Information = SdIoctl.BytesReturned; break;
case IOCTL_ICA_STACK_QUERY_MODULE_DATA : if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) ); ProbeForWrite( Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, sizeof(BYTE) ); }
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength) { Buffer = ICA_ALLOCATE_POOL( NonPagedPool, IrpSp->Parameters.DeviceIoControl.InputBufferLength); if (Buffer) { memcpy(Buffer, IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength); } else { Status = STATUS_NO_MEMORY; break; } } SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = Buffer; SdIoctl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; SdIoctl.OutputBuffer = Irp->UserBuffer; SdIoctl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; Status = _IcaCallStack( pStack, SD$IOCTL, &SdIoctl ); Irp->IoStatus.Information = SdIoctl.BytesReturned;
/* this is so IoStatus.Information gets returned to the caller */ if (Status == STATUS_BUFFER_TOO_SMALL) Status = STATUS_BUFFER_OVERFLOW; break;
case IOCTL_ICA_STACK_QUERY_LAST_INPUT_TIME : if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ICA_STACK_LAST_INPUT_TIME) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( Irp->UserBuffer, sizeof(ICA_STACK_LAST_INPUT_TIME), sizeof(BYTE) ); }
((PICA_STACK_LAST_INPUT_TIME)Irp->UserBuffer)->LastInputTime = pStack->LastInputTime; Irp->IoStatus.Information = sizeof(ICA_STACK_LAST_INPUT_TIME); Status = STATUS_SUCCESS; break;
case IOCTL_ICA_STACK_TRACE : { unsigned DataLen = 0;
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < (ULONG)FIELD_OFFSET(ICA_TRACE_BUFFER,Data[0]) ) return( STATUS_BUFFER_TOO_SMALL ); if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength > sizeof(ICA_TRACE_BUFFER) ) return( STATUS_INVALID_BUFFER_SIZE ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) ); }
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength) { Buffer = ICA_ALLOCATE_POOL( NonPagedPool, IrpSp->Parameters.DeviceIoControl.InputBufferLength); if (Buffer) { memcpy(Buffer, IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength); } else { Status = STATUS_NO_MEMORY; break; } }
pTraceBuffer = (PICA_TRACE_BUFFER)Buffer;
// Make sure the trace buffer is NULL terminated
DataLen = IrpSp->Parameters.DeviceIoControl.InputBufferLength - FIELD_OFFSET(ICA_TRACE_BUFFER, Data); if (pTraceBuffer->Data[DataLen - 1] == 0) { pConnect = IcaLockConnectionForStack( pStack ); IcaTraceFormat( &pConnect->TraceInfo, pTraceBuffer->TraceClass, pTraceBuffer->TraceEnable, pTraceBuffer->Data ); IcaUnlockConnectionForStack( pStack ); Status = STATUS_SUCCESS; } else { Status = STATUS_BUFFER_OVERFLOW; }
break; }
case IOCTL_ICA_STACK_REGISTER_BROKEN : { ICA_STACK_BROKEN BrokenEvent;
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ICA_STACK_BROKEN) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_STACK_BROKEN), sizeof(BYTE) ); }
memcpy(&BrokenEvent, IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_STACK_BROKEN));
Status = _RegisterBrokenEvent( pStack, &BrokenEvent ); break; }
case IOCTL_ICA_STACK_ENABLE_IO : pStack->fIoDisabled = FALSE; /* If enabling the passthru stack, then enable passthru mode */ if ( pStack->StackClass == Stack_Passthru ) { Status = _EnablePassthru( pStack ); } else { Status = STATUS_SUCCESS; } break;
case IOCTL_ICA_STACK_DISABLE_IO : pStack->fIoDisabled = TRUE; /* If disabling the passthru stack, then disable passthru mode */ if ( pStack->StackClass == Stack_Passthru ) {
Status = _DisablePassthru( pStack );
IcaLockStack( pStack ); // Now wait for any input still in progress to end.
if ( pStack->fDoingInput ) { NTSTATUS WaitStatus;
pStack->fDisablingIo = TRUE; KeClearEvent( &pStack->IoEndEvent ); IcaUnlockStack( pStack );
//
// Convert the timeout to a relative system time value and wait.
//
WaitTimeout = RtlEnlargedIntegerMultiply( 60000, -10000 ); pWaitTimeout = &WaitTimeout;
WaitStatus = KeWaitForSingleObject( &pStack->IoEndEvent, UserRequest, UserMode, FALSE, pWaitTimeout );
#if DBG
if ( WaitStatus != STATUS_SUCCESS ) { DbgPrint("TermDD: IOCTL_ICA_STACK_DISABLE_IO: WaitStatus=%x\n", WaitStatus); ASSERT(WaitStatus == STATUS_SUCCESS); } #endif
IcaLockStack( pStack );
pStack->fDisablingIo = FALSE; } IcaUnlockStack( pStack );
} else { Status = STATUS_SUCCESS; } break;
case IOCTL_ICA_STACK_DISCONNECT : { HANDLE hIca;
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ICA_STACK_RECONNECT) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_STACK_RECONNECT), sizeof(BYTE) ); } pStackReconnect = (PICA_STACK_RECONNECT)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; hIca = pStackReconnect->hIca;
/*
* Notify stack drivers of disconnect */ SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = NULL; SdIoctl.InputBufferLength = 0; SdIoctl.OutputBuffer = NULL; SdIoctl.OutputBufferLength = 0; (void)_IcaCallStack( pStack, SD$IOCTL, &SdIoctl );
/*
* Disconnect stack */ Status = _ReconnectStack( pStack, hIca ); break; }
case IOCTL_ICA_STACK_RECONNECT : { ICA_STACK_RECONNECT StackReconnect; if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ICA_STACK_RECONNECT) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_STACK_RECONNECT), sizeof(BYTE) ); }
/*
* Reconnect stack */ memcpy(&StackReconnect, IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_STACK_RECONNECT));
Status = _ReconnectStack( pStack, StackReconnect.hIca );
/*
* Notify stack drivers of reconnect */ SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = &StackReconnect; SdIoctl.InputBufferLength = sizeof(ICA_STACK_RECONNECT); SdIoctl.OutputBuffer = NULL; SdIoctl.OutputBufferLength = 0; (void)_IcaCallStack( pStack, SD$IOCTL, &SdIoctl );
break; }
case IOCTL_ICA_STACK_WAIT_FOR_ICA: if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) ); ProbeForWrite( Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, sizeof(BYTE) ); }
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength) { Buffer = ICA_ALLOCATE_POOL( NonPagedPool, IrpSp->Parameters.DeviceIoControl.InputBufferLength); if (Buffer) { memcpy(Buffer, IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength); } else { Status = STATUS_NO_MEMORY; break; } }
SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = Buffer; SdIoctl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; SdIoctl.OutputBuffer = Irp->UserBuffer; SdIoctl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; Status = _IcaCallStack( pStack, SD$IOCTL, &SdIoctl ); Irp->IoStatus.Information = SdIoctl.BytesReturned;
if ( NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: IcaDeviceControlStack: Binding vchannels\n")); Status = IcaBindVirtualChannels( pStack ); }
break;
case IOCTL_ICA_STACK_CONSOLE_CONNECT: if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) ); ProbeForWrite( Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, sizeof(BYTE) ); }
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength) { Buffer = ICA_ALLOCATE_POOL( NonPagedPool, IrpSp->Parameters.DeviceIoControl.InputBufferLength); if (Buffer) { memcpy(Buffer, IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength); } else { Status = STATUS_NO_MEMORY; break; } }
SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = Buffer; SdIoctl.OutputBuffer = Irp->UserBuffer; SdIoctl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; SdIoctl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; Status = _IcaCallStack( pStack, SD$IOCTL, &SdIoctl ); Irp->IoStatus.Information = SdIoctl.BytesReturned;
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: IcaDeviceControlStack: console connect\n")); if ( NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: IcaDeviceControlStack: Binding vchannels\n")); Status = IcaBindVirtualChannels( pStack ); }
break;
case IOCTL_ICA_STACK_CANCEL_IO : pStack->fClosing = TRUE; /* fall through */
default: if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) ); ProbeForWrite( Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, sizeof(BYTE) ); }
SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; SdIoctl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; SdIoctl.OutputBuffer = Irp->UserBuffer; SdIoctl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; Status = _IcaCallStack( pStack, SD$IOCTL, &SdIoctl ); Irp->IoStatus.Information = SdIoctl.BytesReturned;
/* initialize virtual channel name bindings */ if ( NT_SUCCESS(Status) && (code == IOCTL_ICA_STACK_CONNECTION_QUERY) ) { Status = IcaBindVirtualChannels( pStack ); if ( Status == STATUS_SUCCESS ) {
ICA_STACK_QUERY_BUFFER icaSQB; NTSTATUS QueryStatus;
SdIoctl.IoControlCode = IOCTL_ICA_STACK_QUERY_BUFFER; SdIoctl.InputBuffer = NULL; SdIoctl.InputBufferLength = 0; SdIoctl.OutputBuffer = &icaSQB; SdIoctl.OutputBufferLength = sizeof(icaSQB); QueryStatus = _IcaCallStack( pStack, SD$IOCTL, &SdIoctl ); if ( NT_SUCCESS(QueryStatus) ) { pStack->OutBufCount = icaSQB.WdBufferCount; pStack->OutBufLength = icaSQB.TdBufferSize; } } }
/* this is so IoStatus.Information gets returned to the caller */ if ( Status == STATUS_BUFFER_TOO_SMALL ) Status = STATUS_BUFFER_OVERFLOW; break; } } except( IcaExceptionFilter( L"IcaDeviceControlStack TRAPPED!!", GetExceptionInformation() ) ) { Status = GetExceptionCode(); }
if (Buffer) { ICA_FREE_POOL(Buffer); Buffer = NULL; }
#if DBG
if ( code != IOCTL_ICA_STACK_TRACE ) { IcaLockStack( pStack ); TRACESTACK(( pStack, TC_ICADD, TT_API1, "TermDD: IcaDeviceControlStack, fc %d, 0x%x\n", (code & 0x3fff) >> 2, Status )); IcaUnlockStack( pStack ); } #endif
return( Status ); }
NTSTATUS IcaCleanupStack( IN PICA_STACK pStack, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { return( STATUS_SUCCESS ); }
NTSTATUS IcaCloseStack( IN PICA_STACK pStack, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { SD_IOCTL SdIoctl; PICA_CONNECTION pConnect; KIRQL OldIrql;
#if DBG
IcaLockStack( pStack ); TRACESTACK(( pStack, TC_ICADD, TT_API1, "TermDD: IcaCloseStack (enter)\n" )); IcaUnlockStack( pStack ); #endif
/*
* If passthru mode is enabled, disable it now. */ if ( pStack->pPassthru ) { _DisablePassthru( pStack ); }
/*
* Send cancel i/o to stack drivers */ SdIoctl.IoControlCode = IOCTL_ICA_STACK_CANCEL_IO; (void) _IcaCallStack( pStack, SD$IOCTL, &SdIoctl );
/*
* Make sure all Stack Drivers are unloaded. */ pConnect = IcaLockConnectionForStack( pStack ); while ( _IcaPopStack( pStack ) == STATUS_SUCCESS ) ; IcaUnlockConnection( pConnect );
/*
* Dereference the broken event if we have one */ if ( pStack->pBrokenEventObject ) { KeSetEvent( pStack->pBrokenEventObject, 0, FALSE ); ObDereferenceObject( pStack->pBrokenEventObject ); pStack->pBrokenEventObject = NULL; }
/*
* If closing the primary stack, unbind the virtual channels. * Unlink this stack from the stack list for this connection. */ pConnect = IcaLockConnectionForStack( pStack ); if ( pStack->StackClass == Stack_Primary || pStack->StackClass == Stack_Console ) { IcaUnbindVirtualChannels( pConnect ); IcaFreeAllVcBind( pConnect ); } RemoveEntryList( &pStack->StackEntry ); IcaUnlockConnection( pConnect );
/*
* Lock the stack list for update */ IcaAcquireSpinLock(&IcaStackListSpinLock, &OldIrql);
/*
* Remove the stack from the stack list. Before doing so, check if IcaNextStack * is pointing to this stack, if so, move IcaNextStack to the next stack * in the list. Also, decrement total number of stacks */ if (&pStack->StackNode == IcaNextStack) { IcaNextStack = pStack->StackNode.Flink; } RemoveEntryList(&pStack->StackNode);
if (IcaTotalNumOfStacks != 0) { IcaTotalNumOfStacks--; }
/*
* Unlock the stack list now */ IcaReleaseSpinLock(&IcaStackListSpinLock, OldIrql);
/*
* Remove the file object reference for this stack. * This will cause the stack to be deleted when all other * references are gone. */ IcaDereferenceStack( pStack );
return( STATUS_SUCCESS ); }
VOID IcaReferenceStack( IN PICA_STACK pStack ) {
ASSERT( pStack->RefCount >= 0 );
/*
* Increment the reference count */ if ( InterlockedIncrement( &pStack->RefCount) <= 0 ) { ASSERT( FALSE ); } }
VOID IcaDereferenceStack( IN PICA_STACK pStack ) {
ASSERT( pStack->RefCount > 0 );
/*
* Decrement the reference count; if it is 0, free the stack. */ if ( InterlockedDecrement( &pStack->RefCount) == 0 ) { _IcaFreeStack( pStack ); } }
PICA_CONNECTION IcaGetConnectionForStack( IN PICA_STACK pStack ) {
/*
* As long as the stack object is locked, it's safe for us * to pick up the pConnect pointer and return it. * WARNING: Once the caller unlocks the stack object, the pointer * returned below must not be referenced anymore and may * no longer be valid. */ ASSERT( ExIsResourceAcquiredExclusiveLite( &pStack->Resource ) );
return( (PICA_CONNECTION)pStack->pConnect ); }
PICA_CONNECTION IcaLockConnectionForStack( IN PICA_STACK pStack ) { PICA_CONNECTION pConnect;
/*
* Acquire the Reconnect resource lock so that the pConnect * pointer cannot change before we get the connection locked. */ KeEnterCriticalRegion(); ExAcquireResourceSharedLite( IcaReconnectResource, TRUE ); pConnect = (PICA_CONNECTION)pStack->pConnect; IcaLockConnection( pConnect ); ExReleaseResourceLite( IcaReconnectResource ); KeLeaveCriticalRegion();
return( pConnect ); }
VOID IcaUnlockConnectionForStack( IN PICA_STACK pStack ) { PICA_CONNECTION pConnect;
/*
* As long as the connection object is locked, it's safe for us * to pick up the pConnect pointer from the stack and use it. */ pConnect = (PICA_CONNECTION)pStack->pConnect;
ASSERT( ExIsResourceAcquiredExclusiveLite( &pConnect->Resource ) );
IcaUnlockConnection( pConnect ); }
/*******************************************************************************
* * IcaCallDriver * * Call the topmost stack driver * * This is the main interface routine that all channels use * to call the stack driver(s). * * ENTRY: * pChannel (input) * pointer to channel object this call is from * ProcIndex (input) * index of driver proc to call * pParms (input) * pointer to driver parms * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/
NTSTATUS IcaCallDriver( IN PICA_CHANNEL pChannel, IN ULONG ProcIndex, IN PVOID pParms ) { PLIST_ENTRY Head, Next; PICA_STACK pStack; NTSTATUS Status = STATUS_SUCCESS;
TRACECHANNEL(( pChannel, TC_ICADD, TT_API4, "TermDD: IcaCallDriver, ProcIndex=%u (enter)\n", ProcIndex ));
// Open/Close should never be called from a channel!
ASSERT( ProcIndex != SD$OPEN && ProcIndex != SD$CLOSE );
/*
* Lock the connection object. * This will serialize all channel calls for this connection. */ IcaLockConnection( pChannel->pConnect );
/*
* Send this call down to the stack(s). * If Passthru mode is enabled, then we bit bucket all channel I/O. * However if this channel is flagged as shadow persistent, let * the data go through. */ if ( !pChannel->pConnect->fPassthruEnabled || (pChannel->Flags & CHANNEL_SHADOW_PERSISTENT) ) {
Head = &pChannel->pConnect->StackHead; for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) { pStack = CONTAINING_RECORD( Next, ICA_STACK, StackEntry );
/*
* If I/O is disabled for this stack, or if this is a * shadow stack and this call is from a channel that does * not process shadow I/O, or if it's a PassThru stack * and the channel is shadow persistent, then skip this stack. */ if ( !(pStack->fIoDisabled || pStack->StackClass == Stack_Shadow && !(pChannel->Flags & CHANNEL_SHADOW_IO) || (pChannel->pConnect->fPassthruEnabled && pStack->StackClass == Stack_Passthru)) ) { Status = _IcaCallStack( pStack, ProcIndex, pParms ); } } }
/*
* Unlock the connection object now. */ IcaUnlockConnection( pChannel->pConnect );
return( Status ); }
NTSTATUS IcaCallNextDriver( IN PSDCONTEXT pContext, IN ULONG ProcIndex, IN PVOID pParms ) { PSDLINK pSdLink; PICA_STACK pStack; NTSTATUS Status;
ASSERT( ProcIndex != SD$OPEN && ProcIndex != SD$CLOSE );
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); pStack = pSdLink->pStack; ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable ); ASSERT( ExIsResourceAcquiredExclusiveLite( &pSdLink->pStack->Resource ) );
TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API4, "TermDD: IcaCallNextDriver, ProcIndex=%u (enter)\n", ProcIndex ));
/*
* Call the next driver if there is one */ if ( (pSdLink = IcaGetNextSdLink( pSdLink )) == NULL ) return( STATUS_INVALID_PARAMETER );
ASSERT( pSdLink->pStack == pStack );
Status = _IcaCallSd( pSdLink, ProcIndex, pParms );
return( Status ); }
NTSTATUS IcaRawInput ( IN PSDCONTEXT pContext, IN PINBUF pInBuf OPTIONAL, IN PUCHAR pBuffer OPTIONAL, IN ULONG ByteCount )
/*++
Routine Description:
This is the input (stack callup) routine for ICA raw input.
Arguments:
pContext - Pointer to SDCONTEXT for this Stack Driver
pInBuf - Pointer to INBUF containing data
pBuffer - Pointer to input data
NOTE: Either pInBuf OR pBuffer must be specified, but not both.
ByteCount - length of data in pBuffer
Return Value:
NTSTATUS -- Indicates whether the request was handled successfully.
--*/
{ PSDLINK pSdLink; PICA_STACK pStack; PICA_CONNECTION pConnect; NTSTATUS Status;
/*
* Use SD passed context to get the stack object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); pStack = pSdLink->pStack; // save stack pointer for use below
ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
TRACESTACK(( pStack, TC_ICADD, TT_API2, "TermDD: IcaRawInput, bc=%u (enter)\n", ByteCount ));
/*
* Only the stack object should be locked during input. */ ASSERT( ExIsResourceAcquiredExclusiveLite( &pStack->Resource ) ); ASSERT( (pConnect = IcaGetConnectionForStack( pStack )) && !ExIsResourceAcquiredExclusiveLite( &pConnect->Resource ) );
/*
* Walk up the SDLINK list looking for a driver which has specified * a RawInput callup routine. If we find one, then call the * driver RawInput routine to let it handle the call. */ while ( (pSdLink = IcaGetPreviousSdLink( pSdLink )) != NULL ) { ASSERT( pSdLink->pStack == pStack ); if ( pSdLink->SdContext.pCallup->pSdRawInput ) { IcaReferenceSdLink( pSdLink ); Status = (pSdLink->SdContext.pCallup->pSdRawInput)( pSdLink->SdContext.pContext, pInBuf, pBuffer, ByteCount ); IcaDereferenceSdLink( pSdLink ); return( Status ); } }
return( IcaRawInputInternal( pStack, pInBuf, pBuffer, ByteCount ) ); }
NTSTATUS IcaRawInputInternal( IN PICA_STACK pStack, IN PINBUF pInBuf OPTIONAL, IN PCHAR pBuffer OPTIONAL, IN ULONG ByteCount ) { SD_RAWWRITE SdRawWrite; NTSTATUS Status;
/*
* See if passthru mode is enabled. * If so, then we simply turn around and write the input data * directly to the passthru stack. */ if ( pStack->pPassthru ) { PICA_STACK pPassthru;
if ( pInBuf ) { SdRawWrite.pOutBuf = NULL; SdRawWrite.pBuffer = pInBuf->pBuffer; SdRawWrite.ByteCount = pInBuf->ByteCount; } else { SdRawWrite.pOutBuf = NULL; SdRawWrite.pBuffer = pBuffer; SdRawWrite.ByteCount = ByteCount; }
// Grab a copy of pPassthru onto our local stack before we release
// the local stack lock. This has been a problem (NT bug #328433)
// where we release the local stack lock and pStack->pPassthru
// becomes NULL in _DisablePassthru() before we take out the
// passthrough stack lock inside of _IcaCallStack().
pPassthru = pStack->pPassthru;
// If data are being forwarded to the passthru, make
// sure it's fully initialized. If not, drop the data.
if((pPassthru->StackClass == Stack_Passthru) && pPassthru->fIoDisabled) { return( STATUS_SUCCESS ); }
// Take a reference on the passthrough stack to make sure it does
// not go away before we get to it in the call below.
IcaReferenceStack(pPassthru);
// Unlock our current stack while in call to passthrough stack.
pStack->fDoingInput = TRUE; IcaUnlockStack(pStack); Status = _IcaCallStack(pPassthru, SD$RAWWRITE, &SdRawWrite); IcaLockStack(pStack);
if ( pStack->fDisablingIo ) { KeSetEvent( &pStack->IoEndEvent, 0, FALSE ); }
pStack->fDoingInput = FALSE;
// Mirror the refrence above.
IcaDereferenceStack(pPassthru);
/*
* Passthru is not enabled. * We have no choice but to drop the input data. */ } else { Status = STATUS_SUCCESS; }
return( Status ); }
NTSTATUS IcaSleep( IN PSDCONTEXT pContext, IN ULONG Duration ) { PSDLINK pSdLink; BOOLEAN LockStack = FALSE; LARGE_INTEGER SleepTime; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaSleep %d msec (enter)\n", Duration ));
/*
* Release stack lock if held */ if ( ExIsResourceAcquiredExclusiveLite( &pSdLink->pStack->Resource ) ) { LockStack = TRUE; IcaUnlockStack( pSdLink->pStack ); }
/*
* Convert the sleep duration to a relative system time value and sleep. */ SleepTime = RtlEnlargedIntegerMultiply( Duration, -10000 ); Status = KeDelayExecutionThread( KernelMode, TRUE, &SleepTime );
/*
* Reacquire stack lock if held on entry */ if ( LockStack ) { IcaLockStack( pSdLink->pStack ); }
/*
* If stack is being closed and we are returning success, * then change return value to indicate stack is being closed. */ if ( pSdLink->pStack->fClosing && Status == STATUS_SUCCESS ) Status = STATUS_CTX_CLOSE_PENDING;
#if DBG
if ( Status != STATUS_SUCCESS ) { TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: Sleep, ERROR 0x%x\n", Status )); } #endif
return( Status ); }
NTSTATUS IcaWaitForSingleObject( IN PSDCONTEXT pContext, IN PVOID pObject, IN LONG Timeout ) { PSDLINK pSdLink; BOOLEAN LockStack = FALSE; LARGE_INTEGER WaitTimeout; PLARGE_INTEGER pWaitTimeout = NULL; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API2, "TermDD: IcaWaitForSingleObject, %d (enter)\n", Timeout ));
/*
* Release stack lock if held */ if ( ExIsResourceAcquiredExclusiveLite( &pSdLink->pStack->Resource ) ) { LockStack = TRUE; IcaUnlockStack( pSdLink->pStack ); }
/*
* Convert the timeout to a relative system time value and wait. */ if ( Timeout != -1 ) { ASSERT( Timeout >= 0 ); WaitTimeout = RtlEnlargedIntegerMultiply( Timeout, -10000 ); pWaitTimeout = &WaitTimeout; }
Status = KeWaitForSingleObject( pObject, UserRequest, UserMode, FALSE, pWaitTimeout );
/*
* Reacquire stack lock if held on entry */ if ( LockStack ) { IcaLockStack( pSdLink->pStack ); }
/*
* If stack is being closed and we are returning success, * then change return value to indicate stack is being closed. */ if ( pSdLink->pStack->fClosing && Status == STATUS_SUCCESS ) Status = STATUS_CTX_CLOSE_PENDING;
#if DBG
if ( Status != STATUS_SUCCESS ) { if ( Status == STATUS_TIMEOUT ) { TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaWaitForSingleObject, TIMEOUT\n" )); } else { TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaWaitForSingleObject, ERROR 0x%x\n", Status )); } } #endif
return( Status ); }
/*
* Same as IcaSleep() except it is assumed that the connection lock is * held. This is used by the VD flow control routines. */ NTSTATUS IcaFlowControlSleep( IN PSDCONTEXT pContext, IN ULONG Duration ) { PSDLINK pSdLink; BOOLEAN LockStack = FALSE; LARGE_INTEGER SleepTime; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaSleep %d msec (enter)\n", Duration ));
/*
* Release stack lock if held */ if ( ExIsResourceAcquiredExclusiveLite( &pSdLink->pStack->Resource ) ) { LockStack = TRUE; IcaUnlockStack( pSdLink->pStack ); }
/*
* Unlock the connection lock */ IcaUnlockConnectionForStack( pSdLink->pStack );
/*
* Convert the sleep duration to a relative system time value and sleep. */ SleepTime = RtlEnlargedIntegerMultiply( Duration, -10000 ); Status = KeDelayExecutionThread( KernelMode, TRUE, &SleepTime );
/*
* Relock the connection lock */ IcaLockConnectionForStack( pSdLink->pStack );
/*
* Reacquire stack lock if held on entry */ if ( LockStack ) { IcaLockStack( pSdLink->pStack ); }
/*
* If stack is being closed and we are returning success, * then change return value to indicate stack is being closed. */ if ( pSdLink->pStack->fClosing && Status == STATUS_SUCCESS ) Status = STATUS_CTX_CLOSE_PENDING;
#if DBG
if ( Status != STATUS_SUCCESS ) { TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: Sleep, ERROR 0x%x\n", Status )); } #endif
return( Status ); }
/*
* Same as IcaWaitForSingleObject() except it is assumed that the connection lock is * held. This is used by the VD flow control routines. */ NTSTATUS IcaFlowControlWait( IN PSDCONTEXT pContext, IN PVOID pObject, IN LONG Timeout ) { PSDLINK pSdLink; BOOLEAN LockStack = FALSE; LARGE_INTEGER WaitTimeout; PLARGE_INTEGER pWaitTimeout = NULL; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API2, "TermDD: IcaWaitForSingleObject, %d (enter)\n", Timeout ));
/*
* Release stack lock if held */ if ( ExIsResourceAcquiredExclusiveLite( &pSdLink->pStack->Resource ) ) { LockStack = TRUE; IcaUnlockStack( pSdLink->pStack ); }
/*
* Unlock the connection lock */ IcaUnlockConnectionForStack( pSdLink->pStack );
/*
* Convert the timeout to a relative system time value and wait. */ if ( Timeout != -1 ) { ASSERT( Timeout >= 0 ); WaitTimeout = RtlEnlargedIntegerMultiply( Timeout, -10000 ); pWaitTimeout = &WaitTimeout; }
Status = KeWaitForSingleObject( pObject, UserRequest, KernelMode, TRUE, pWaitTimeout );
/*
* Relock the connection lock */ IcaLockConnectionForStack( pSdLink->pStack );
/*
* Reacquire stack lock if held on entry */ if ( LockStack ) { IcaLockStack( pSdLink->pStack ); }
/*
* If stack is being closed and we are returning success, * then change return value to indicate stack is being closed. */ if ( pSdLink->pStack->fClosing && Status == STATUS_SUCCESS ) Status = STATUS_CTX_CLOSE_PENDING;
#if DBG
if ( Status != STATUS_SUCCESS ) { if ( Status == STATUS_TIMEOUT ) { TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaWaitForSingleObject, TIMEOUT\n" )); } else { TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaWaitForSingleObject, ERROR 0x%x\n", Status )); } } #endif
return( Status ); }
NTSTATUS IcaWaitForMultipleObjects( IN PSDCONTEXT pContext, IN ULONG Count, IN PVOID Object[], IN WAIT_TYPE WaitType, IN LONG Timeout ) { PSDLINK pSdLink; BOOLEAN LockStack = FALSE; LARGE_INTEGER WaitTimeout; PLARGE_INTEGER pWaitTimeout = NULL; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaWaitForMultipleObjects, %d (enter)\n", Timeout ));
/*
* Release stack lock if held */ if ( ExIsResourceAcquiredExclusiveLite( &pSdLink->pStack->Resource ) ) { LockStack = TRUE; IcaUnlockStack( pSdLink->pStack ); }
/*
* Convert the timeout to a relative system time value and wait. */ if ( Timeout != -1 ) { ASSERT( Timeout >= 0 ); WaitTimeout = RtlEnlargedIntegerMultiply( Timeout, -10000 ); pWaitTimeout = &WaitTimeout; }
Status = KeWaitForMultipleObjects( Count, Object, WaitType, UserRequest, KernelMode, TRUE, pWaitTimeout, NULL );
/*
* Reacquire stack lock if held on entry */ if ( LockStack ) { IcaLockStack( pSdLink->pStack ); }
/*
* If stack is being closed and we are returning success, * then change return value to indicate stack is being closed. */ if ( pSdLink->pStack->fClosing && Status == STATUS_SUCCESS ) Status = STATUS_CTX_CLOSE_PENDING;
#if DBG
if ( Status != STATUS_SUCCESS ) { if ( Status == STATUS_TIMEOUT ) { TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaWaitForMultipleObjects, TIMEOUT\n" )); } else { TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaWaitForMultipleObjects, ERROR 0x%x\n", Status )); } } #endif
return( Status ); }
NTSTATUS IcaLogError( IN PSDCONTEXT pContext, IN NTSTATUS Status, IN LPWSTR * pArgStrings, IN ULONG ArgStringCount, IN PVOID pRawData, IN ULONG RawDataLength ) { return( _LogError( IcaDeviceObject, Status, pArgStrings, ArgStringCount, pRawData, RawDataLength ) ); }
NTSTATUS _LogError( IN PDEVICE_OBJECT pDeviceObject, IN NTSTATUS Status, IN LPWSTR * pArgStrings, IN ULONG ArgStringCount, IN PVOID pRawData, IN ULONG RawDataLength ) { LPWSTR *TmpPtr; PUCHAR ptrToString; ULONG Tmp, StringSize, TotalStringSize; PIO_ERROR_LOG_PACKET errorLogEntry;
// Get the bytes needed for strings storage
Tmp = ArgStringCount; TmpPtr = pArgStrings; TotalStringSize = 0;
while( Tmp ) {
TotalStringSize += ((wcslen(*TmpPtr)+1)*sizeof(WCHAR)); Tmp--; TmpPtr++; }
errorLogEntry = IoAllocateErrorLogEntry( pDeviceObject, (UCHAR)(sizeof(IO_ERROR_LOG_PACKET) + RawDataLength + TotalStringSize) );
if ( errorLogEntry != NULL ) {
errorLogEntry->ErrorCode = Status; errorLogEntry->SequenceNumber = 0; errorLogEntry->MajorFunctionCode = 0; errorLogEntry->RetryCount = 0; errorLogEntry->UniqueErrorValue = 0; errorLogEntry->FinalStatus = Status; errorLogEntry->DumpDataSize = (USHORT)RawDataLength;
// Copy raw data
if (RawDataLength) {
RtlCopyMemory( &errorLogEntry->DumpData[0], pRawData, RawDataLength );
ptrToString = ((PUCHAR)&errorLogEntry->DumpData[0])+RawDataLength;
} else { ptrToString = (PUCHAR)&errorLogEntry->DumpData[0]; }
// round up to next word boundary
// it's ok to add 1 byte because we allocated more bytes than we needed:
// the number of extra bytes is the size of DumpData which is ULONG.
ptrToString = (PUCHAR)((ULONG_PTR)(ptrToString + sizeof(WCHAR) - 1) & ~(ULONG_PTR)(sizeof(WCHAR) - 1));
// Copy strings following raw data
errorLogEntry->NumberOfStrings = (USHORT)ArgStringCount;
if( ArgStringCount ) { errorLogEntry->StringOffset = (USHORT)(ptrToString - (PUCHAR)errorLogEntry); } else { errorLogEntry->StringOffset = 0; }
while( ArgStringCount ) {
StringSize = (wcslen(*pArgStrings)+1)*sizeof(WCHAR);
RtlCopyMemory( ptrToString, *pArgStrings, StringSize );
ptrToString += StringSize; ArgStringCount--; pArgStrings++;
}
IoWriteErrorLogEntry(errorLogEntry); return STATUS_SUCCESS; } else { return STATUS_NO_MEMORY; } }
#define KEEP_ALIVE_MIN_INTERVAL 50000000 // 5 sec in terms of 100 nanosecs
VOID IcaCheckStackAlive( ) { NTSTATUS status; KIRQL OldIrql; PICA_STACK pStack; SD_IOCTL SdIoctl; LARGE_INTEGER SleepTime; LARGE_INTEGER CurrentTime; LONGLONG KeepAliveInterval;
while (TRUE) { KeepAliveInterval = g_KeepAliveInterval * 600000000 ; // in 100 nanosecs
pStack = NULL;
// Lock the stack list for reading
IcaAcquireSpinLock(&IcaStackListSpinLock, &OldIrql);
//KdPrint(("Total number of stacks: %d\n", IcaTotalNumOfStacks));
// determine new sleep time for the keepalive thread
// it is the keepalive interval for a stack divided by total
// number of stacks
// the low threshold for sleep time is 5 sec. Since relative
// sleeptime is a negative value, we use min instead of max
if (IcaTotalNumOfStacks > 1) { SleepTime.QuadPart = min(0 - KEEP_ALIVE_MIN_INTERVAL, 0 - (KeepAliveInterval / IcaTotalNumOfStacks)); } else { SleepTime.QuadPart = min(0 - KEEP_ALIVE_MIN_INTERVAL, 0 - KeepAliveInterval); }
// If the stack list is not empty, get the stack for keepalive
// checking and move the IcaNextStack pointer to the next stack
if (IcaNextStack != &IcaStackListHead) { pStack = CONTAINING_RECORD(IcaNextStack, ICA_STACK, StackNode);
// Reference the stack so that the stack won't be deleted while we
// are accessing it
IcaReferenceStack(pStack);
IcaNextStack = IcaNextStack->Flink; } else { if (IcaNextStack->Flink != &IcaStackListHead) { pStack = CONTAINING_RECORD(IcaNextStack->Flink, ICA_STACK, StackNode);
// Reference the stack so that the stack won't be deleted while we
// are accessing it
IcaReferenceStack(pStack);
IcaNextStack = IcaNextStack->Flink->Flink; } }
// Unlock the stack list now
IcaReleaseSpinLock(&IcaStackListSpinLock, OldIrql);
// If the stack pointer is invalid or LastInputTime on the stack is 0,
// the stack is not in the active state, so we don't need to send
// keepalive pkt on that stack.
if (pStack != NULL && pStack->LastInputTime.QuadPart != 0) { // Get the current system time
KeQuerySystemTime(&CurrentTime);
// Check if it is time to send a keepalive packet depends on
// the keepalive timestamp and lastinput timestamp
if (CurrentTime.QuadPart - pStack->LastKeepAliveTime.QuadPart >= KeepAliveInterval && CurrentTime.QuadPart - pStack->LastInputTime.QuadPart >= KeepAliveInterval) {
// Initialize the IOCTL struct
SdIoctl.IoControlCode = IOCTL_ICA_STACK_SEND_KEEPALIVE_PDU; SdIoctl.InputBuffer = NULL; SdIoctl.InputBufferLength = 0; SdIoctl.OutputBuffer = NULL; SdIoctl.OutputBufferLength = 0;
//KdPrint(("In IcaCheckStackAlive: To call WD, pStack=%p\n", pStack));
// Send an IOCTL to the stack requesting to send a keepalive packet
_IcaCallStack(pStack, SD$IOCTL, &SdIoctl);
// Update the LastKeepAlive timestamp for the stack
KeQuerySystemTime(&pStack->LastKeepAliveTime); } #if DBG
else { if (CurrentTime.QuadPart - pStack->LastKeepAliveTime.QuadPart < KeepAliveInterval) { //KdPrint(("Not time to do keep alive yet, pstack=%p\n", pStack));
} if (CurrentTime.QuadPart - pStack->LastInputTime.QuadPart < KeepAliveInterval) { //KdPrint(("- Last Input Time is less than KeepAliveInterval, pstack=%p\n", pStack));
} } #endif
} #if DBG
else{ if (pStack != NULL) { //KdPrint(("No need to send KeepAlive PDU on pstack=%p\n", pStack));
} } #endif
// Decrement the reference to the stack so that it can be deleted
if (pStack != NULL) { IcaDereferenceStack(pStack); }
// Start sleep timer again
// We would return if the unload module signal the IcaKeepAliveEvent
// to stop this keepalive thread
status = KeWaitForSingleObject(pIcaKeepAliveEvent, Executive, KernelMode, TRUE, &SleepTime);
if (status == STATUS_SUCCESS) { return; } } }
VOID IcaKeepAliveThread( IN PVOID pData) { IcaCheckStackAlive(); }
#ifdef notdef
VOID IcaAcquireIoLock( IN PSDCONTEXT pContext ) { PSDLINK pSdLink;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext );
IcaLockConnectionForStack( pSdLink->pStack ); }
VOID IcaReleaseIoLock( IN PSDCONTEXT pContext ) { PSDLINK pSdLink;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext );
IcaUnlockConnectionForStack( pSdLink->pStack ); }
VOID IcaAcquireDriverLock( IN PSDCONTEXT pContext ) { PSDLINK pSdLink;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext );
IcaLockStack( pSdLink->pStack ); }
VOID IcaReleaseDriverLock( IN PSDCONTEXT pContext ) { PSDLINK pSdLink;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext );
IcaUnlockStack( pSdLink->pStack ); }
VOID IcaIncrementDriverReference( IN PSDCONTEXT pContext ) { PSDLINK pSdLink;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext );
IcaReferenceSdLink( pSdLink ); }
VOID IcaDecrementDriverReference( IN PSDCONTEXT pContext ) { PSDLINK pSdLink;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext );
IcaDereferenceSdLink( pSdLink ); } #endif
typedef NTSTATUS (*PTHREAD_ROUTINE) ( PVOID );
typedef struct _ICACREATETHREADINFO { PTHREAD_ROUTINE pProc; PVOID pParm; PSDLINK pSdLink; ULONG LockFlags; } ICACREATETHREADINFO, *PICACREATETHREADINFO;
NTSTATUS IcaCreateThread( IN PSDCONTEXT pContext, IN PVOID pProc, IN PVOID pParm, IN ULONG LockFlags, OUT PHANDLE pThreadHandle ) { PSDLINK pSdLink; PICACREATETHREADINFO pThreadInfo; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK object pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
TRACESTACK(( pSdLink->pStack, TC_ICADD, TT_API1, "TermDD: IcaCreateThread (enter)\n" ));
pThreadInfo = ICA_ALLOCATE_POOL( NonPagedPool, sizeof(*pThreadInfo) ); if ( pThreadInfo == NULL ) return( STATUS_NO_MEMORY );
pThreadInfo->pProc = pProc; pThreadInfo->pParm = pParm; pThreadInfo->pSdLink = pSdLink; pThreadInfo->LockFlags = LockFlags;
/*
* Reference the SDLINK object on behalf of the new thread. */ IcaReferenceSdLink( pSdLink );
Status = PsCreateSystemThread( pThreadHandle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), NULL, _IcaDriverThread, (PVOID) pThreadInfo ); if ( !NT_SUCCESS( Status ) ) { IcaDereferenceSdLink( pSdLink ); ICA_FREE_POOL( pThreadInfo ); return( Status ); }
return( STATUS_SUCCESS ); }
NTSTATUS _IcaDriverThread( IN PVOID pData ) { PICACREATETHREADINFO pThreadInfo = (PICACREATETHREADINFO)pData; PTHREAD_ROUTINE pProc; PVOID pParm; PSDLINK pSdLink; PICA_STACK pStack; ULONG LockFlags; NTSTATUS Status;
pProc = pThreadInfo->pProc; pParm = pThreadInfo->pParm; pSdLink = pThreadInfo->pSdLink; LockFlags = pThreadInfo->LockFlags; ICA_FREE_POOL( pThreadInfo ); pStack = pSdLink->pStack;
/*
* Obtain any required locks before calling the worker routine. */ ASSERT( !(LockFlags & ICALOCK_IO) ); if ( LockFlags & ICALOCK_DRIVER ) IcaLockStack( pStack );
/*
* Call the thread routine */ #if DBG
try { #endif
/*
* If stack is being closed, then indicate this to caller. */ if ( !pStack->fClosing ) Status = (pProc)( pParm ); else Status = STATUS_CTX_CLOSE_PENDING; #if DBG
} except( IcaExceptionFilter( L"_IcaDriverThread TRAPPED!!", GetExceptionInformation() ) ) { Status = GetExceptionCode(); } #endif
/*
* Release any locks acquired above. */ if ( LockFlags & ICALOCK_DRIVER ) IcaUnlockStack( pStack );
/*
* Dereference the SDLINK object now. * This undoes the reference that was made on our behalf in * the IcaCreateThread routine when this thread was created. */ IcaDereferenceSdLink( pSdLink );
return( Status ); }
PICA_STACK _IcaAllocateStack( VOID ) { PICA_STACK pStack; NTSTATUS Status;
/*
* Allocate and initialize stack structure */ pStack = ICA_ALLOCATE_POOL( NonPagedPool, sizeof(*pStack) ); if ( pStack == NULL ) return NULL; RtlZeroMemory( pStack, sizeof(*pStack) );
/*
* Initialize the reference count to 2, * one for the caller's reference, one for the file object reference. */ pStack->RefCount = 2;
/*
* Initialize the rest of the stack object */ pStack->Header.Type = IcaType_Stack; pStack->Header.pDispatchTable = IcaStackDispatchTable; ExInitializeResourceLite( &pStack->Resource ); InitializeListHead( &pStack->SdLinkHead ); KeInitializeEvent( &pStack->OutBufEvent, NotificationEvent, FALSE ); KeInitializeEvent( &pStack->IoEndEvent, NotificationEvent, FALSE );
return( pStack ); }
VOID _IcaFreeStack( PICA_STACK pStack ) { PICA_CONNECTION pConnect;
ASSERT( pStack->RefCount == 0 ); ASSERT( IsListEmpty( &pStack->SdLinkHead ) ); ASSERT( !ExIsResourceAcquiredExclusiveLite( &pStack->Resource ) );
/*
* Remove the reference to the Connection object for this stack. */ pConnect = (PICA_CONNECTION)pStack->pConnect; IcaDereferenceConnection( pConnect );
ExDeleteResourceLite( &pStack->Resource );
ICA_FREE_POOL( pStack ); }
NTSTATUS _IcaPushStack( IN PICA_STACK pStack, IN PICA_STACK_PUSH pStackPush ) { PSD_OPEN pSdOpen = NULL; PSDLINK pSdLink; NTSTATUS Status;
if ( g_TermServProcessID == NULL) { g_TermServProcessID = IoGetCurrentProcess(); }
/*
* Serialize all stack push/pop/call operations */ IcaLockStack( pStack );
TRACESTACK(( pStack, TC_ICADD, TT_API1, "TermDD: _IcaPushStack, type %u, name %S (enter)\n", pStackPush->StackModuleType, pStackPush->StackModuleName ));
/*
* If stack is being closed, then indicate this to caller */ if ( pStack->fClosing ) { Status = STATUS_CTX_CLOSE_PENDING; goto done; }
pSdOpen = ICA_ALLOCATE_POOL( NonPagedPool, sizeof(SD_OPEN) ); if ( pSdOpen == NULL ) { Status = STATUS_NO_MEMORY; goto done; }
/*
* Load an instance of the requested stack driver */ Status = _IcaLoadSd( pStackPush->StackModuleName, &pSdLink ); if ( !NT_SUCCESS( Status ) ) goto done;
/*
* If this is the first stack driver loaded, then initialize * some of the stack data from the ICA_STACK_PUSH parameters. * NOTE: Since we're testing for an empty list we must make * this check before the InsertHeadList below. */ if ( IsListEmpty( &pStack->SdLinkHead ) ) { pStack->OutBufLength = pStackPush->PdConfig.Create.OutBufLength; pStack->OutBufCount = pStackPush->PdConfig.Create.OutBufCount; //
//Set the low water mark using the PD config
//
if ( !(pStackPush->PdConfig.Create.PdFlag & PD_NOLOW_WATERMARK) ) { //
//set to default
//
pStack->OutBufLowWaterMark = (pStackPush->PdConfig.Create.OutBufCount/ 3) + 1; } else { pStack->OutBufLowWaterMark = MAX_LOW_WATERMARK; } }
/*
* Increment the stack ref count for this SD, * and push the new SD on the stack. */ IcaReferenceStack( pStack ); InsertHeadList( &pStack->SdLinkHead, &pSdLink->Links ); pSdLink->pStack = pStack;
/*
* Initialize the SD open parameters */ pSdOpen->StackClass = pStack->StackClass; pSdOpen->pStatus = &pStack->ProtocolStatus; pSdOpen->pClient = &pStack->ClientModules; pSdOpen->WdConfig = pStackPush->WdConfig; pSdOpen->PdConfig = pStackPush->PdConfig; pSdOpen->OutBufHeader = pStack->SdOutBufHeader; pSdOpen->OutBufTrailer = pStack->SdOutBufTrailer; pSdOpen->DeviceObject = pSdLink->pSdLoad->DeviceObject;
RtlCopyMemory( pSdOpen->OEMId, pStackPush->OEMId, sizeof(pSdOpen->OEMId) ); RtlCopyMemory( pSdOpen->WinStationRegName, pStackPush->WinStationRegName, sizeof(pSdOpen->WinStationRegName) );
/*
* Call the SD open procedure */ Status = _IcaCallSd( pSdLink, SD$OPEN, pSdOpen ); if ( !NT_SUCCESS( Status ) ) { RemoveEntryList( &pSdLink->Links ); pSdLink->Links.Flink = pSdLink->Links.Blink = NULL; IcaDereferenceSdLink( pSdLink ); goto done; }
/*
* Increment number of reserved output buffer bytes */ pStack->SdOutBufHeader += pSdOpen->SdOutBufHeader; pStack->SdOutBufTrailer += pSdOpen->SdOutBufTrailer;
done: if ( pSdOpen ) { ICA_FREE_POOL( pSdOpen ); }
IcaUnlockStack( pStack );
return( Status ); }
NTSTATUS _IcaPopStack( IN PICA_STACK pStack ) { PICA_CONNECTION pConnect; SD_CLOSE SdClose; PSDLINK pSdLink; NTSTATUS Status;
/*
* Serialize all stack push/pop/call operations */ IcaLockStack( pStack );
ASSERT( (pConnect = IcaGetConnectionForStack( pStack )) && ExIsResourceAcquiredExclusiveLite( &pConnect->Resource ) );
/*
* If no SDs remain, then return error */ if ( IsListEmpty( &pStack->SdLinkHead ) ) { Status = STATUS_NO_MORE_ENTRIES; goto done; }
/*
* Call the SD close procedure for the topmost SD */ pSdLink = CONTAINING_RECORD( pStack->SdLinkHead.Flink, SDLINK, Links ); ASSERT( pSdLink->pStack == pStack ); Status = _IcaCallSd( pSdLink, SD$CLOSE, &SdClose );
/*
* Decrement number of reserved output buffer bytes */ pStack->SdOutBufHeader -= SdClose.SdOutBufHeader; pStack->SdOutBufTrailer -= SdClose.SdOutBufTrailer;
/*
* Remove the SdLink from the top of the list, * and dereference the SDLINK object. */ RemoveEntryList( &pSdLink->Links ); pSdLink->Links.Flink = pSdLink->Links.Blink = NULL; IcaDereferenceSdLink( pSdLink );
done: IcaUnlockStack( pStack );
return( Status ); }
NTSTATUS _IcaCallStack( IN PICA_STACK pStack, IN ULONG ProcIndex, IN OUT PVOID pParms ) { PLIST_ENTRY Head; PSDLINK pSdLink; NTSTATUS Status;
/*
* Serialize all stack push/pop/call operations */ IcaLockStack( pStack );
/*
* Call the topmost Stack Driver, if there is one */ if ( IsListEmpty( &pStack->SdLinkHead ) ) { IcaUnlockStack( pStack ); return( STATUS_INVALID_PARAMETER ); }
Head = pStack->SdLinkHead.Flink; pSdLink = CONTAINING_RECORD( Head, SDLINK, Links ); ASSERT( pSdLink->pStack == pStack ); Status = _IcaCallSd( pSdLink, ProcIndex, pParms );
IcaUnlockStack( pStack );
return( Status ); }
NTSTATUS _IcaCallStackNoLock( IN PICA_STACK pStack, IN ULONG ProcIndex, IN OUT PVOID pParms ) { PLIST_ENTRY Head; PSDLINK pSdLink; NTSTATUS Status;
/*
* Call the topmost Stack Driver, if there is one */ if ( IsListEmpty( &pStack->SdLinkHead ) ) { return( STATUS_INVALID_PARAMETER ); }
Head = pStack->SdLinkHead.Flink; pSdLink = CONTAINING_RECORD( Head, SDLINK, Links ); ASSERT( pSdLink->pStack == pStack ); Status = _IcaCallSd( pSdLink, ProcIndex, pParms );
return( Status ); }
NTSTATUS _IcaLoadSd( IN PDLLNAME SdName, OUT PSDLINK *ppSdLink ) { PSDLINK pSdLink; PSDLOAD pSdLoad; PLIST_ENTRY Head, Next; NTSTATUS Status;
/*
* Allocate a SDLINK struct */ pSdLink = ICA_ALLOCATE_POOL( NonPagedPool, sizeof(*pSdLink) ); if ( pSdLink == NULL ) return( STATUS_INSUFFICIENT_RESOURCES ); RtlZeroMemory( pSdLink, sizeof(*pSdLink) );
/*
* Initialize reference count */ pSdLink->RefCount = 1; #if DBG
ExInitializeResourceLite( &pSdLink->Resource ); #endif
/*
* Lock the ICA Resource exclusively to search the SdLoad list. * Note when holding a resource we need to prevent APC calls, so * use KeEnterCriticalRegion(). */ KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite( IcaSdLoadResource, TRUE );
/*
* Look for the requested SD. If found, increment the ref count for it. */ Head = &IcaSdLoadListHead; for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) { pSdLoad = CONTAINING_RECORD( Next, SDLOAD, Links ); if ( !wcscmp( pSdLoad->SdName, SdName ) ) { _IcaReferenceSdLoad( pSdLoad ); break; } }
/*
* If the requested SD was not found, then load it now. */ if ( Next == Head ) { Status = _IcaLoadSdWorker( SdName, &pSdLoad ); if ( !NT_SUCCESS( Status ) ) { ExReleaseResourceLite( IcaSdLoadResource ); KeLeaveCriticalRegion(); #if DBG
ExDeleteResourceLite( &pSdLink->Resource); #endif
ICA_FREE_POOL( pSdLink ); return( Status ); } }
ExReleaseResourceLite( IcaSdLoadResource ); KeLeaveCriticalRegion();
pSdLink->pSdLoad = pSdLoad;
/*
* Call the driver load procedure. * The driver will fill in the fields in the SDCONTEXT structure. */ Status = (pSdLoad->DriverLoad)( &pSdLink->SdContext, TRUE ); if ( !NT_SUCCESS( Status ) ) { KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite( IcaSdLoadResource, TRUE ); _IcaDereferenceSdLoad( pSdLink->pSdLoad ); ExReleaseResourceLite( IcaSdLoadResource ); KeLeaveCriticalRegion(); #if DBG
ExDeleteResourceLite( &pSdLink->Resource ); #endif
ICA_FREE_POOL( pSdLink ); return( Status ); }
*ppSdLink = pSdLink;
return( Status ); }
NTSTATUS _IcaUnloadSd( IN PSDLINK pSdLink ) { KIRQL oldIrql; NTSTATUS Status;
ASSERT( pSdLink->RefCount == 0 ); ASSERT( pSdLink->Links.Flink == NULL );
/*
* Inform driver of unload */ Status = (pSdLink->pSdLoad->DriverLoad)( &pSdLink->SdContext, FALSE );
/*
* Decrement ref count on SdLoad object. * This will cause it to be unloaded if the ref count goes to 0. * Note that while holding a resource we need to disable APC calls, * hence the CriticalRegion calls. */ KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite( IcaSdLoadResource, TRUE ); _IcaDereferenceSdLoad( pSdLink->pSdLoad ); ExReleaseResourceLite( IcaSdLoadResource ); KeLeaveCriticalRegion();
/*
* Remove reference this SDLINK object had on the stack. */ IcaDereferenceStack( pSdLink->pStack );
#if DBG
ExDeleteResourceLite( &pSdLink->Resource ); #endif
ICA_FREE_POOL( pSdLink );
return( Status ); }
NTSTATUS _IcaCallSd( IN PSDLINK pSdLink, IN ULONG ProcIndex, IN PVOID pParms ) { PSDPROCEDURE pSdProcedure; NTSTATUS Status;
/*
* If there is no procedure call table, return success. * This should only happen during load/unload and should not be a problem. */ if ( pSdLink->SdContext.pProcedures == NULL ) return( STATUS_SUCCESS );
/*
* Get a pointer to the SD proc based on specified ProcIndex. * If NULL, then this ProcIndex is not supported by this driver. */ pSdProcedure = ((PSDPROCEDURE *)pSdLink->SdContext.pProcedures)[ ProcIndex ]; if ( pSdProcedure == NULL ) return( STATUS_NOT_SUPPORTED );
IcaReferenceSdLink( pSdLink );
Status = (pSdProcedure)( pSdLink->SdContext.pContext, pParms );
IcaDereferenceSdLink( pSdLink );
return( Status ); }
VOID IcaReferenceSdLink( IN PSDLINK pSdLink ) {
ASSERT( pSdLink->RefCount >= 0 ); ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
/*
* Increment the reference count */ if ( InterlockedIncrement( &pSdLink->RefCount) <= 0 ) { ASSERT( FALSE ); } }
VOID IcaDereferenceSdLink( IN PSDLINK pSdLink ) {
ASSERT( pSdLink->RefCount > 0 ); ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
/*
* Decrement the reference count; if it is 0, unload the SD. */ if ( InterlockedDecrement( &pSdLink->RefCount) == 0 ) { _IcaUnloadSd( pSdLink ); } }
PSDLINK IcaGetNextSdLink( IN PSDLINK pSdLink ) { PLIST_ENTRY Next; PSDLINK pNextSdLink;
ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable ); ASSERT( pSdLink->RefCount > 0 || pSdLink->Links.Flink == NULL ); ASSERT( pSdLink->SdContext.pProcedures ); ASSERT( pSdLink->SdContext.pContext );
if ( pSdLink->Links.Flink == NULL ) return( NULL );
Next = pSdLink->Links.Flink; if ( Next == &pSdLink->pStack->SdLinkHead ) return( NULL );
pNextSdLink = CONTAINING_RECORD( Next, SDLINK, Links ); ASSERT( pNextSdLink->pStack == pSdLink->pStack ); ASSERT( pNextSdLink->RefCount > 0 ); ASSERT( pNextSdLink->SdContext.pProcedures ); ASSERT( pNextSdLink->SdContext.pContext );
return( pNextSdLink ); }
PSDLINK IcaGetPreviousSdLink( IN PSDLINK pSdLink ) { PLIST_ENTRY Prev; PSDLINK pPrevSdLink;
ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable ); ASSERT( pSdLink->RefCount > 0 || pSdLink->Links.Flink == NULL ); ASSERT( pSdLink->SdContext.pProcedures ); ASSERT( pSdLink->SdContext.pContext );
if ( pSdLink->Links.Blink == NULL ) return( NULL );
Prev = pSdLink->Links.Blink; if ( Prev == &pSdLink->pStack->SdLinkHead ) return( NULL );
pPrevSdLink = CONTAINING_RECORD( Prev, SDLINK, Links ); ASSERT( pPrevSdLink->pStack == pSdLink->pStack ); ASSERT( pPrevSdLink->RefCount > 0 ); ASSERT( pPrevSdLink->SdContext.pProcedures ); ASSERT( pPrevSdLink->SdContext.pContext );
return( pPrevSdLink ); }
VOID _IcaReferenceSdLoad( IN PSDLOAD pSdLoad ) {
ASSERT( ExIsResourceAcquiredExclusiveLite( IcaSdLoadResource ) ); ASSERT( pSdLoad->RefCount >= 0 );
/*
* Increment the reference count */ ++pSdLoad->RefCount; ASSERT( pSdLoad->RefCount > 0 ); }
VOID _IcaDereferenceSdLoad( IN PSDLOAD pSdLoad ) {
ASSERT( ExIsResourceAcquiredExclusiveLite( IcaSdLoadResource ) ); ASSERT( pSdLoad->RefCount > 0 );
/*
* Decrement the reference count; if it is 0, unload the SD by queuing * a passive level DPC. We must do this to prevent continuing to hold * ObpInitKillMutant in the loader -- the driver unload can cause RPC * calls which deadlock on that object. */ if ( pSdLoad->RefCount == 1 ) { PWORK_QUEUE_ITEM pItem;
pItem = ICA_ALLOCATE_POOL(NonPagedPool, sizeof(WORK_QUEUE_ITEM)); if (pItem != NULL) { ExInitializeWorkItem(pItem, _IcaUnloadSdWorker, pSdLoad); pSdLoad->pUnloadWorkItem = pItem; ExQueueWorkItem(pItem, DelayedWorkQueue); } /* If we cannot allocate workitem do not unload here. It is
* better to temporarly leak one driver than deadlocking the * system. */ }else{ pSdLoad->RefCount--; } }
NTSTATUS IcaExceptionFilter(PWSTR OutputString, PEXCEPTION_POINTERS pexi) { DbgPrint( "TermDD: %S\n", OutputString ); DbgPrint( "TermDD: ExceptionRecord=%p ContextRecord=%p\n", pexi->ExceptionRecord, pexi->ContextRecord ); #ifdef i386
DbgPrint( "TermDD: Exception code=%08x, flags=%08x, addr=%p, IP=%p\n", pexi->ExceptionRecord->ExceptionCode, pexi->ExceptionRecord->ExceptionFlags, pexi->ExceptionRecord->ExceptionAddress, pexi->ContextRecord->Eip );
DbgPrint( "TermDD: esp=%p ebp=%p\n", pexi->ContextRecord->Esp, pexi->ContextRecord->Ebp ); #else
DbgPrint( "TermDD: Exception code=%08x, flags=%08x, addr=%p\n", pexi->ExceptionRecord->ExceptionCode, pexi->ExceptionRecord->ExceptionFlags, pexi->ExceptionRecord->ExceptionAddress ); #endif
{ SYSTEM_KERNEL_DEBUGGER_INFORMATION KernelDebuggerInfo; NTSTATUS Status;
Status = ZwQuerySystemInformation(SystemKernelDebuggerInformation, &KernelDebuggerInfo, sizeof(KernelDebuggerInfo), NULL); if (NT_SUCCESS(Status) && KernelDebuggerInfo.KernelDebuggerEnabled) DbgBreakPoint(); }
return EXCEPTION_EXECUTE_HANDLER; }
//
// Helper routine to break if there is a debugger attached
//
//
VOID IcaBreakOnDebugger( ) { SYSTEM_KERNEL_DEBUGGER_INFORMATION KernelDebuggerInfo; NTSTATUS Status;
Status = ZwQuerySystemInformation(SystemKernelDebuggerInformation, &KernelDebuggerInfo, sizeof(KernelDebuggerInfo), NULL); if (NT_SUCCESS(Status) && KernelDebuggerInfo.KernelDebuggerEnabled) DbgBreakPoint(); }
/*******************************************************************************
* * _RegisterBrokenEvent * * Register an event to be signaled when the stack is broken * * ENTRY: * pStack (input) * pointer to stack structure * pStackBroken (input) * pointer to buffer containing event info * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/
NTSTATUS _RegisterBrokenEvent( IN PICA_STACK pStack, IN PICA_STACK_BROKEN pStackBroken ) { NTSTATUS Status;
/*
* There should not already be any event registered */ if ( pStack->pBrokenEventObject ) { ASSERT( FALSE ); return( STATUS_OBJECT_NAME_COLLISION ); }
/*
* Reference the event and save a pointer to the object */ Status = ObReferenceObjectByHandle( pStackBroken->BrokenEvent, 0L, *ExEventObjectType, KernelMode, (PVOID *)&pStack->pBrokenEventObject, NULL );
return( Status ); }
/*******************************************************************************
* * _EnablePassthru * * Enable passthru mode for this connection * * ENTRY: * pStack (input) * pointer to passthru stack structure * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/
NTSTATUS _EnablePassthru( PICA_STACK pStack ) { PICA_CONNECTION pConnect; PLIST_ENTRY Prev; PICA_STACK pPrimaryStack; NTSTATUS Status = STATUS_UNSUCCESSFUL;
ASSERT( pStack->pPassthru != NULL ); ASSERT( !IsListEmpty( &pStack->StackEntry ) );
/*
* Lock connection object and get a pointer to it. */ pConnect = IcaLockConnectionForStack( pStack );
/*
* Get pointer to previous stack for this connection. * If there is one (i.e. prev does not point to the stack head), * then it must be the primary stack which we will connect to. */ Prev = pStack->StackEntry.Blink; if ( Prev != &pConnect->StackHead ) { pPrimaryStack = CONTAINING_RECORD( Prev, ICA_STACK, StackEntry ); ASSERT( pPrimaryStack->StackClass == Stack_Primary );
/*
* Connect the primary and passthru stacks */ pConnect->fPassthruEnabled = TRUE; Status = STATUS_SUCCESS; }
IcaUnlockConnection( pConnect );
return( STATUS_SUCCESS ); }
/*******************************************************************************
* * _DisablePassthru * * Disable passthru mode for this connection * * ENTRY: * pStack (input) * pointer to passthru stack structure * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/
NTSTATUS _DisablePassthru(PICA_STACK pStack) { PICA_CONNECTION pConnect;
pConnect = IcaLockConnectionForStack(pStack);
if (pStack->pPassthru) { // Lock each stack while clearing the pPassthru pointer.
// This synchronizes references through the pPassthru pointer
// within the function IcaRawInputInternal().
// NOTE: We assume that we have ZERO locks on entry to this function.
// We then take only one lock at a time so we cannot deadlock.
IcaLockStack(pStack->pPassthru); pStack->pPassthru->pPassthru = NULL; IcaUnlockStack(pStack->pPassthru);
IcaLockStack(pStack); pStack->pPassthru = NULL; IcaUnlockStack(pStack);
pConnect->fPassthruEnabled = FALSE; }
IcaUnlockConnection(pConnect);
return STATUS_SUCCESS; }
/*******************************************************************************
* * _ReconnectStack * * Reconnect the stack to a new connection object. * * ENTRY: * pStack (input) * pointer to stack structure * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/
NTSTATUS _ReconnectStack(PICA_STACK pStack, HANDLE hIca) { PFILE_OBJECT pNewConnectFileObject; PICA_CONNECTION pNewConnect; PICA_CONNECTION pOldConnect; PLIST_ENTRY pSaveVcBind; NTSTATUS Status;
/*
* Only allow a reconnect on a Primary stack. */ if ( pStack->StackClass != Stack_Primary ) return( STATUS_NOT_SUPPORTED );
/*
* If passthru mode is enabled, disable it now. */ if ( pStack->pPassthru ) { _DisablePassthru( pStack ); }
/*
* Open the file object for the new connection we will attach to. */ Status = ObReferenceObjectByHandle( hIca, 0L, // DesiredAccess
*IoFileObjectType, KernelMode, (PVOID *)&pNewConnectFileObject, NULL ); if (!NT_SUCCESS(Status)) return(Status);
/*
* Ensure what we have is a connection object */
if (pNewConnectFileObject->DeviceObject != IcaDeviceObject) { ASSERT(FALSE); ObDereferenceObject( pNewConnectFileObject ); return( STATUS_INVALID_PARAMETER ); }
/*
* Get a pointer to the new connection object and reference it. */ pNewConnect = pNewConnectFileObject->FsContext; ASSERT( pNewConnect->Header.Type == IcaType_Connection ); if ( pNewConnect->Header.Type != IcaType_Connection ) { ObDereferenceObject( pNewConnectFileObject ); return( STATUS_INVALID_CONNECTION ); } IcaReferenceConnection(pNewConnect);
/*
* Obtain the necessary locks to perform the stack reconnect. * * First, we acquire the global resource lock. * * Next lock the connection this stack is currently attached to * as well as the new connection the stack will be moved to. * NOTE: Because of the use of the global resource lock, * there is no possiblility of deadlock even though we * are attempting to lock two connection objects at the * same time. * NOTE: While holding a resource we need to disable APC calls * with the CriticalRegion calls. * * Finally, lock the stack object itself. */ KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(IcaReconnectResource, TRUE);
pOldConnect = IcaLockConnectionForStack(pStack); if (pOldConnect == pNewConnect) { Status = STATUS_UNSUCCESSFUL; goto badoldconnect; }
IcaLockConnection(pNewConnect); if (!IsListEmpty(&pNewConnect->VcBindHead)) { Status = STATUS_UNSUCCESSFUL; goto badnewconnect; } if (!IsListEmpty(&pNewConnect->StackHead)) { PICA_STACK pHeadStack; pHeadStack = CONTAINING_RECORD(pStack->StackEntry.Flink, ICA_STACK, StackEntry); if (pHeadStack->StackClass == Stack_Primary) { Status = STATUS_UNSUCCESSFUL; goto badnewconnect; } }
IcaLockStack(pStack);
/*
* Unbind the virtual channels, * and unlink the VcBind list and save a pointer to it * (but only if the list is non-empty). */ IcaUnbindVirtualChannels( pOldConnect ); if ( !IsListEmpty( &pOldConnect->VcBindHead ) ) { pSaveVcBind = pOldConnect->VcBindHead.Flink; RemoveEntryList( &pOldConnect->VcBindHead ); InitializeListHead( &pOldConnect->VcBindHead ); } else { pSaveVcBind = NULL; }
/*
* Unlink this stack from the stack list for this connection, * and remove the reference to the Connection object. */ RemoveEntryList( &pStack->StackEntry ); IcaDereferenceConnection( pOldConnect );
/*
* We're done with the old connection object so unlock it now. */ IcaUnlockConnection( pOldConnect );
/*
* Restore the VcBind list and Rebind the virtual channels. */ if ( pSaveVcBind ) { InsertTailList( pSaveVcBind, &pNewConnect->VcBindHead ); IcaRebindVirtualChannels( pNewConnect ); }
/*
* Insert this stack in the stack list for this connection, * and save the new Connection object pointer for this stack. */ InsertHeadList( &pNewConnect->StackHead, &pStack->StackEntry ); pStack->pConnect = (PUCHAR)pNewConnect;
/*
* Release stack/connection objects and global resource */ IcaUnlockStack( pStack ); IcaUnlockConnection( pNewConnect ); ExReleaseResourceLite( IcaReconnectResource ); KeLeaveCriticalRegion();
/*
* The stack requires a connection object reference, * so leave the one made above, but dereference the file object. */ //IcaDereferenceConnection( pNewConnect );
ObDereferenceObject( pNewConnectFileObject );
return( STATUS_SUCCESS );
badnewconnect: IcaUnlockConnection( pNewConnect );
badoldconnect: IcaUnlockConnection( pOldConnect ); ExReleaseResourceLite( IcaReconnectResource ); KeLeaveCriticalRegion(); IcaDereferenceConnection( pNewConnect ); ObDereferenceObject( pNewConnectFileObject );
return( Status ); }
PVOID IcaStackAllocatePoolWithTag( IN POOL_TYPE PoolType, IN SIZE_T NumberOfBytes, IN ULONG Tag ) { PVOID pBuffer;
pBuffer = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); if (pBuffer != NULL) { gAllocSucceed++; } else { gAllocFailed++; } return pBuffer; }
PVOID IcaStackAllocatePool( IN POOL_TYPE PoolType, IN SIZE_T NumberOfBytes) {
PVOID pBuffer;
pBuffer = ExAllocatePoolWithTag(PoolType, NumberOfBytes, ICA_POOL_TAG); if (pBuffer != NULL) { gAllocSucceed++; } else { gAllocFailed++; } return pBuffer; }
void IcaStackFreePool(IN PVOID Pointer) {
ExFreePool(Pointer); gAllocFreed++; }
NTSTATUS _IcaKeepAlive( IN BOOLEAN enableKeepAlive, IN ULONG interval ) {
NTSTATUS status = STATUS_SUCCESS; HANDLE ThreadHandle;
if ( enableKeepAlive ) { // a request has come to start the keep alive thread
if (pKeepAliveThreadObject == NULL ) // if we have no thread object, thread is not running
{ // keep alive thread uses this interval.
g_KeepAliveInterval = interval;
// Create a new thread to handle keep alive
status = PsCreateSystemThread( &ThreadHandle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), NULL, IcaKeepAliveThread, NULL ); if (status == STATUS_SUCCESS) { // Reference the thread handle by object
status = ObReferenceObjectByHandle(ThreadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID *)&pKeepAliveThreadObject, NULL); if (status == STATUS_SUCCESS) { // KdPrint(("In TermDD: KeepAlive thread created successfully\n"));
} else { KdPrint(("TermDD: Unable to reference object by thread handle: %d\n", status)); } ZwClose(ThreadHandle); } else { KdPrint(("In TermDD: Unable to create KeepAlive thread.\n")); } } else { // otherwise, keep alive thread is running, but we might have to change the interval to some new value
// set the new value so that next time around the while loop, it will be picked up.
g_KeepAliveInterval = interval; // KdPrint(("In TermDD: KeepAliveInterval was changes to %d \n",g_KeepAliveInterval ));
} } else { // we don't need the keep alive thread
if (pKeepAliveThreadObject != NULL ) { // Set IcaKeepAliveEvent to wake up KeepAlive thread
if (pIcaKeepAliveEvent != NULL ) { KeSetEvent(pIcaKeepAliveEvent, 0, FALSE); }
// Wait for the thread to exit
KeWaitForSingleObject(pKeepAliveThreadObject, Executive, KernelMode, TRUE, NULL);
// Deference the thread object
ObDereferenceObject(pKeepAliveThreadObject); pKeepAliveThreadObject = NULL;
// KdPrint(("In TermDD: KeepAlive thread was terminated successfully \n"));
status = STATUS_SUCCESS; } }
return status;
}
|