Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

752 lines
19 KiB

/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name:
Channel.cpp
Abstract:
This module implements a thin wrapper on the Read and Write routines so
we can issue Read/Write Irps to termdd.
Environment:
Kernel mode
--*/
#include "precomp.hxx"
#define TRC_FILE "channel"
#include "trc.h"
#include <winsta.h>
#include <ntddvdeo.h>
#include <icadd.h>
#include "TSQPublic.h"
//
// RDPDr.cpp : The TS Worker Queue pointer
//
extern PVOID RDPDR_TsQueue;
typedef struct tagCHANNELIOCONTEXT {
SmartPtr<VirtualChannel> Channel;
PIO_COMPLETION_ROUTINE CompletionRoutine;
PVOID Context;
PVOID Buffer;
ULONG Length;
ULONG IoOperation;
BOOL LowPrioSend;
} CHANNELIOCONTEXT, *PCHANNELIOCONTEXT;
typedef struct tagCHANNELCLOSECONTEXT {
SmartPtr<VirtualChannel> Channel;
} CHANNELCLOSECONTEXT, *PCHANNELCLOSECONTEXT;
VirtualChannel::VirtualChannel()
{
BEGIN_FN("VirtualChannel::VirtualChannel");
SetClassName("VirtualChannel");
_Channel = NULL;
_ChannelFileObject = NULL;
_ChannelDeviceObject = NULL;
_DeletionEvent = NULL;
_LowPrioChannelWriteFlags = 0;
_LowPrioChannelWriteFlags |= CHANNEL_WRITE_LOWPRIO;
}
VirtualChannel::~VirtualChannel()
{
BEGIN_FN("VirtualChannel::~VirtualChannel");
if (_DeletionEvent != NULL) {
KeSetEvent(_DeletionEvent, IO_NO_INCREMENT, FALSE);
}
}
BOOL VirtualChannel::Create(HANDLE hIca, ULONG SessionID, ULONG ChannelId,
PKEVENT DeletionEvent)
/*++
Routine Description:
Opens the virtual channel and make it a kernel handle
Arguments:
Channel - A pointer to a location to store the Channel pointer
hIca - Required context for opening a channel
SessionId - The session for the channel
ChannelId - The Id of the RdpDr channel
Return Value:
a valid NTSTATUS code.
Notes:
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_HANDLE_INFORMATION HandleInformation;
BEGIN_FN("VirtualChannel::Create");
ASSERT(_DeletionEvent == NULL);
_DeletionEvent = DeletionEvent;
//
// Get the channel open
//
Status = CreateTermDD(&_Channel, hIca, SessionID, ChannelId);
if (NT_SUCCESS(Status)) {
//
// Get the file object from the handle
//
Status = ObReferenceObjectByHandle(_Channel,
STANDARD_RIGHTS_REQUIRED, NULL, KernelMode, (PVOID *)(&_ChannelFileObject),
&HandleInformation);
}
if (NT_SUCCESS(Status)) {
TRC_DBG((TB, "ObReferenced channel"));
_ChannelDeviceObject = IoGetRelatedDeviceObject((PFILE_OBJECT)_ChannelFileObject);
}
else {
TRC_ERR((TB, "Failed to open channel"));
if (_Channel != NULL) {
ZwClose(_Channel);
_Channel = NULL;
}
}
if (NT_SUCCESS(Status)) {
return TRUE;
} else {
if (_DeletionEvent) {
KeSetEvent(_DeletionEvent, IO_NO_INCREMENT, FALSE);
}
return FALSE;
}
}
NTSTATUS VirtualChannel::Read(
IN PIO_COMPLETION_ROUTINE ReadRoutine OPTIONAL,
IN PVOID Context,
OUT PVOID Buffer,
IN ULONG Length,
IN BOOL bWorkerItem
)
/*++
Routine Description:
Reads data from the virtual channel for the specified Client
Arguments:
ReadRoutine - Completetion routine
Context - Data to pass to the completion routine
Buffer - Data to transfer
Length - Size of data to transfer
Return Value:
a valid NTSTATUS code.
Notes:
--*/
{
NTSTATUS Status;
BEGIN_FN("VirtualChannel::Read");
#if DBG
SmartPtr<DrSession> Session = (DrSession *)Context;
//ASSERT(InterlockedIncrement(&(Session->_ApcCount)) == 1);
InterlockedIncrement(&(Session->_ApcCount));
InterlockedIncrement(&(Session->_ApcChannelRef));
#endif
Status = SubmitIo(ReadRoutine, Context, Buffer, Length, IRP_MJ_READ, bWorkerItem, FALSE);
#if DBG
if (!NT_SUCCESS(Status)) {
//ASSERT(InterlockedDecrement(&(Session->_ApcCount)) == 0);
//ASSERT(InterlockedDecrement(&(Session->_ApcChannelRef)) == 0);
InterlockedDecrement(&(Session->_ApcCount));
InterlockedDecrement(&(Session->_ApcChannelRef));
}
#endif
return Status;
}
NTSTATUS VirtualChannel::Write(
IN PIO_COMPLETION_ROUTINE WriteRoutine OPTIONAL,
IN PVOID Context,
OUT PVOID Buffer,
IN ULONG Length,
IN BOOL bWorkerItem,
IN BOOL LowPrioSend
)
/*++
Routine Description:
Writes data to the virtual channel for the specified Client
Arguments:
WriteRoutine - Completetion routine
Context - Data to pass to the completion routine
Buffer - Data to transfer
Length - Size of data to transfer
LowPrioSend - Indicate that the channel write should be at
lower priority than other client destined data.
Return Value:
a valid NTSTATUS code.
Notes:
--*/
{
BEGIN_FN("VirtualChannel::Write");
return SubmitIo(WriteRoutine, Context, Buffer, Length, IRP_MJ_WRITE,
bWorkerItem, LowPrioSend);
}
NTSTATUS VirtualChannel::SubmitIo(
IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
IN PVOID Context,
OUT PVOID Buffer,
IN ULONG Length,
ULONG IoOperation,
BOOL bWorkerItem,
BOOL LowPrioSend
)
{
PCHANNELIOCONTEXT pChannelIoContext;
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
BEGIN_FN("VirtualChannel::SubmitIo");
TRC_ASSERT((IoOperation == IRP_MJ_READ) ||
(IoOperation == IRP_MJ_WRITE), (TB, "Bad ChannelIo operation"));
if (bWorkerItem) {
//
// Move this operation to a system thread
//
TRC_NRM((TB, "DrChannelIo: queueing the I/O to a system thread"));
pChannelIoContext = new (NonPagedPool) CHANNELIOCONTEXT;
if (pChannelIoContext != NULL) {
pChannelIoContext->Channel = this;
pChannelIoContext->CompletionRoutine = CompletionRoutine;
pChannelIoContext->Context = Context;
pChannelIoContext->Buffer = Buffer;
pChannelIoContext->Length = Length;
pChannelIoContext->IoOperation = IoOperation;
pChannelIoContext->LowPrioSend = LowPrioSend;
//
// Use our own TS worker queue
//
Status = TSAddWorkItemToQueue(RDPDR_TsQueue,
pChannelIoContext,
IoWorker);
if (Status == STATUS_SUCCESS) {
Status = STATUS_PENDING;
goto EXIT;
}
else {
//
// Ts Queue failed
//
TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", Status));
delete pChannelIoContext;
}
}
if (IoOperation == IRP_MJ_WRITE) {
PIO_STATUS_BLOCK pIoStatusBlock = (PIO_STATUS_BLOCK)Context;
pIoStatusBlock->Status = Status;
pIoStatusBlock->Information = 0;
CompletionRoutine(NULL, NULL, Context);
}
else {
// No read should go through here for now
ASSERT(FALSE);
}
}
else {
Status = Io(CompletionRoutine,
Context,
Buffer,
Length,
IoOperation,
LowPrioSend);
}
EXIT:
return Status;
}
VOID VirtualChannel::IoWorker(PDEVICE_OBJECT DeviceObject, PVOID Context)
/*++
Routine Description:
Reads data from the virtual channel for the specified Client, and
signals the thread wanted it done
Arguments:
ClientEntry - The client with which to communicate
ApcRoutine - Completetion routine
ApcContext - Data to pass to the completion routine
IoStatusBlock - Place to store result code
Buffer - Data to transfer
Length - Size of data to transfer
ByteOffset - Offset into Buffer
IoOperation - Read or Write
Event - Event to signal when done
Status - Result code
Return Value:
None
Notes:
--*/
{
NTSTATUS Status;
PCHANNELIOCONTEXT ChannelIoContext = (PCHANNELIOCONTEXT)Context;
BEGIN_FN_STATIC("VirtualChannel::IoWorker");
ASSERT(ChannelIoContext != NULL);
UNREFERENCED_PARAMETER(DeviceObject);
#if DBG
SmartPtr<DrSession> Session;
if (ChannelIoContext->IoOperation == IRP_MJ_READ) {
Session = (DrSession *)(ChannelIoContext->Context);
ASSERT(Session->GetBuffer() == ChannelIoContext->Buffer);
}
#endif
Status = ChannelIoContext->Channel->Io(
ChannelIoContext->CompletionRoutine,
ChannelIoContext->Context,
ChannelIoContext->Buffer,
ChannelIoContext->Length,
ChannelIoContext->IoOperation,
ChannelIoContext->LowPrioSend);
//
// Now delete the context
//
delete ChannelIoContext;
}
NTSTATUS VirtualChannel::Io(
IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
IN PVOID Context,
OUT PVOID Buffer,
IN ULONG Length,
ULONG IoOperation,
BOOL LowPrioSend
)
/*++
Routine Description:
Reads/Writes data from/to the virtual channel for the specified Client
Arguments:
CompletionRoutine - Completetion routine
Context - Data to pass to the completion routine
Buffer - Data to transfer
Length - Size of data to transfer
IoOperation - Read or Write
Return Value:
a valid NTSTATUS code.
Notes:
--*/
{
NTSTATUS Status;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
LARGE_INTEGER StartOffset;
IO_STATUS_BLOCK IoStatusBlock;
BEGIN_FN("VirtualChannel::SubmitIo");
SharedLock sl(_HandleLock);
if (_Channel != NULL) {
//
// Build a read/write irp
//
StartOffset.QuadPart = 0;
Irp = IoBuildAsynchronousFsdRequest(IoOperation, _ChannelDeviceObject, Buffer, Length,
&StartOffset, &IoStatusBlock);
if (Irp) {
//
// Setup the fileobject parameter
//
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->FileObject = _ChannelFileObject;
Irp->Tail.Overlay.Thread = NULL;
//
// Set for low prio write, if specified.
//
if (!LowPrioSend) {
Irp->Tail.Overlay.DriverContext[0] = NULL;
}
else {
Irp->Tail.Overlay.DriverContext[0] = &_LowPrioChannelWriteFlags;
}
//
// Setup the completion routine
//
IoSetCompletionRoutine(Irp, CompletionRoutine, Context, TRUE, TRUE, TRUE);
//
// Send the Irp to Termdd
//
Status = IoCallDriver(_ChannelDeviceObject, Irp);
goto EXIT;
}
else {
Status = STATUS_NO_MEMORY;
}
}
else {
Status = STATUS_DEVICE_NOT_CONNECTED;
}
if (IoOperation == IRP_MJ_WRITE) {
PIO_STATUS_BLOCK pIoStatusBlock = (PIO_STATUS_BLOCK)Context;
pIoStatusBlock->Status = Status;
pIoStatusBlock->Information = 0;
CompletionRoutine(NULL, NULL, Context);
// read completion is not called this way.
}
EXIT:
return Status;
}
NTSTATUS VirtualChannel::CreateTermDD(HANDLE *Channel, HANDLE hIca,
ULONG SessionID, ULONG ChannelId)
/*++
Routine Description:
Opens a virtual channel based on the supplied context
Arguments:
Channel - A pointer to a location to store the Channel pointer
hIca - Required context for opening a channel
SessionId - The session for the channel
ChannelId - The Id of the RdpDr channel
Return Value:
NTSTATUS code
Notes:
--*/
{
NTSTATUS Status;
HANDLE hChannel = NULL;
WCHAR ChannelNameBuffer[MAX_PATH];
UNICODE_STRING ChannelName;
UNICODE_STRING Number;
OBJECT_ATTRIBUTES ChannelAttributes;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FULL_EA_INFORMATION pEa = NULL;
ICA_OPEN_PACKET UNALIGNED * pIcaOpenPacket;
ULONG cbEa = sizeof( FILE_FULL_EA_INFORMATION )
+ ICA_OPEN_PACKET_NAME_LENGTH
+ sizeof( ICA_OPEN_PACKET );
BEGIN_FN("VirtualChannel::CreateTermDD");
//
// Kernel-mode applications open a virtual channel using ZwCreateFile on
// \Device\ICA\sss\Virtualvvv , where
//
// · sss is the logon session ID
// · vvv is the virtual channel number.
//
ChannelName.Buffer = ChannelNameBuffer;
ChannelName.Length = 0;
ChannelName.MaximumLength = sizeof(ChannelNameBuffer);
Status = RtlAppendUnicodeToString(&ChannelName, L"\\Device\\Termdd\\");
TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
//
// Create and append on the sessionID string
//
// Point another UNICODE_STRING to the next part of the buffer
Number.Buffer = (PWCHAR)(((PBYTE)ChannelName.Buffer) + ChannelName.Length);
Number.Length = 0;
Number.MaximumLength = ChannelName.MaximumLength - ChannelName.Length;
// Use that string to put the characters in the right place
Status = RtlIntegerToUnicodeString(SessionID, 10, &Number);
TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
// Add the length of that string to the real string
ChannelName.Length += Number.Length;
//
// Append the next part of the channel path
//
Status = RtlAppendUnicodeToString(&ChannelName, L"\\Virtual");
TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
//
// Create and append the channelID string
//
// Point another UNICODE_STRING to the next part of the buffer
Number.Buffer = (PWCHAR)(((PBYTE)ChannelName.Buffer) + ChannelName.Length);
Number.Length = 0;
Number.MaximumLength = ChannelName.MaximumLength - ChannelName.Length;
// Use that string to put the characters in the right place
Status = RtlIntegerToUnicodeString(ChannelId, 10, &Number);
TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
// Add the length of that string to the real string
ChannelName.Length += Number.Length;
//
// Actually open the channel
//
InitializeObjectAttributes(&ChannelAttributes,
&ChannelName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
//
// Pass in a cool EaBuffer thing so this will really work
// I basically lifted this code from private\tsext\icaapi\stack.c
// It's supposed to be a temporary measure
//
/*
* Allocate some memory for the EA buffer
*/
pEa = (PFILE_FULL_EA_INFORMATION)new BYTE[cbEa];
if (pEa != NULL) {
/*
* Initialize the EA buffer
*/
pEa->NextEntryOffset = 0;
pEa->Flags = 0;
pEa->EaNameLength = ICA_OPEN_PACKET_NAME_LENGTH;
RtlCopyMemory(pEa->EaName, ICAOPENPACKET, ICA_OPEN_PACKET_NAME_LENGTH + 1 );
pEa->EaValueLength = sizeof( ICA_OPEN_PACKET );
pIcaOpenPacket = (ICA_OPEN_PACKET UNALIGNED *)(pEa->EaName +
pEa->EaNameLength + 1);
/*
* Now put the open packet parameters into the EA buffer
*/
pIcaOpenPacket->IcaHandle = hIca;
pIcaOpenPacket->OpenType = IcaOpen_Channel;
pIcaOpenPacket->TypeInfo.ChannelClass = Channel_Virtual;
RtlCopyMemory(pIcaOpenPacket->TypeInfo.VirtualName, DR_CHANNEL_NAME,
sizeof(DR_CHANNEL_NAME));
//
// We keep this next line without "pEa, cbEa"
//
Status = ZwCreateFile(&hChannel, GENERIC_READ | GENERIC_WRITE,
&ChannelAttributes, &IoStatusBlock, 0, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OPEN_IF, FILE_SEQUENTIAL_ONLY, pEa, cbEa);
delete pEa;
} else {
TRC_ERR((TB, "Unable to allocate EaBuffer for ZwCreateFile(channel)"));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(Status)) {
*Channel = hChannel;
}
return Status;
}
void VirtualChannel::CloseWorker(PDEVICE_OBJECT DeviceObject, PVOID Context)
/*++
Routine Description:
Closes the virtual channel in a work item
Arguments:
None
Return Value:
NTSTATUS code from ZwClose.
Notes:
--*/
{
PCHANNELCLOSECONTEXT ChannelCloseContext = (PCHANNELCLOSECONTEXT)Context;
BEGIN_FN_STATIC("VirtualChannel::CloseWorker");
ASSERT(ChannelCloseContext != NULL);
UNREFERENCED_PARAMETER(DeviceObject);
ChannelCloseContext->Channel->Close();
//
// Now delete the context
//
delete ChannelCloseContext;
}
NTSTATUS VirtualChannel::Close()
/*++
Routine Description:
Closes the virtual channel
Arguments:
None
Return Value:
NTSTATUS code from ZwClose.
Notes:
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BEGIN_FN("VirtualChannel::Close");
ExclusiveLock el(_HandleLock);
ASSERT(_Channel != NULL);
ASSERT(_ChannelFileObject != NULL);
TRC_NRM((TB, "DrChannelClose: Close the channel"));
_ChannelDeviceObject = NULL;
ObDereferenceObject(_ChannelFileObject);
_ChannelFileObject = NULL;
ZwClose(_Channel);
_Channel = NULL;
return Status;
}
NTSTATUS VirtualChannel::SubmitClose()
/*++
Routine Description:
Post a close virtual channel request to a worker item
Arguments:
None
Return Value:
NTSTATUS code from ZwClose.
Notes:
--*/
{
PCHANNELCLOSECONTEXT pChannelCloseContext;
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
BEGIN_FN("VirtualChannel::SubmitClose");
//
// Move this operation to a system thread
//
TRC_NRM((TB, "DrChannelClose: queueing the I/O to a system thread"));
pChannelCloseContext = new (NonPagedPool) CHANNELCLOSECONTEXT;
if (pChannelCloseContext != NULL) {
pChannelCloseContext->Channel = this;
//
// Use our own TS worker queue
//
Status = TSAddWorkItemToQueue(RDPDR_TsQueue,
pChannelCloseContext,
CloseWorker);
if( Status == STATUS_SUCCESS) {
Status = STATUS_PENDING;
}
else {
//
// Ts Queue failed
//
TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", Status));
delete pChannelCloseContext;
}
}
return Status;
}