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.
 
 
 
 
 
 

2050 lines
54 KiB

/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name :
session.cpp
Abstract:
Session object is created to handle redirection for this session
Revision History:
--*/
#include "precomp.hxx"
#define TRC_FILE "session"
#include "trc.h"
typedef enum {
scidServerAnnounce,
scidClientConfirm,
scidDeviceReply,
scidIoRequest
} DrSessionCallbackId;
DrSession::DrSession() :
_ChannelDeletionEvent(NotificationEvent, FALSE)
{
BEGIN_FN("DrSession::DrSession");
TRC_NRM((TB, "Session Class=%p", this));
SetClassName("DrSession");
_crefs = 0;
SetSessionState(csExpired);
_ConnectCount = 0;
_ChannelBuffer = NULL;
_ChannelBufferSize = 0;
_PartialPacketData = 0;
_Initialized = FALSE;
_ClientDisplayName[0] = L'\0';
//
// Initialize the server capability set
// This is the capability set that we'll send to client
//
memcpy(&_SrvCapabilitySet, &SERVER_CAPABILITY_SET_DEFAULT,
sizeof(SERVER_CAPABILITY_SET_DEFAULT));
//
// Initialize the client capability set
// Once we receive the client side capability, we'll combine with our local
// capability and stores it.
//
memcpy(&_CliCapabilitySet, &CLIENT_CAPABILITY_SET_DEFAULT,
sizeof(CLIENT_CAPABILITY_SET_DEFAULT));
#if DBG
_BufCount = 1;
_ApcCount = 0;
_ApcChannelRef = 0;
#endif
}
BOOL DrSession::Initialize()
{
BOOL Registered = FALSE;
BOOL ExchangeInitialized = FALSE;
BOOL DeviceManagerInitialized = FALSE;
BEGIN_FN("DrSession::Initialize");
if (!_Initialized) {
Registered = !NT_ERROR(RegisterPacketReceiver(this));
if (Registered) {
TRC_DBG((TB, "Initilazing ExchangeManager"));
ExchangeInitialized = _ExchangeManager.Initialize(this);
}
if (ExchangeInitialized) {
TRC_DBG((TB, "Initilazing DeviceManager"));
DeviceManagerInitialized = _DeviceManager.Initialize(this);
}
if (DeviceManagerInitialized) {
TRC_DBG((TB, "Allocating Channel buffer"));
_ChannelBuffer = new UCHAR[CHANNEL_CHUNK_LENGTH];
}
if (_ChannelBuffer != NULL) {
TRC_DBG((TB, "Allocated default channel buffer=%p", _ChannelBuffer));
_ChannelBufferSize = CHANNEL_CHUNK_LENGTH;
_Initialized = TRUE;
} else {
//
// Error Path, tear down initialization steps
//
if (DeviceManagerInitialized) {
TRC_ERR((TB, "Failed to allocate default channel buffer"));
_DeviceManager.Uninitialize();
}
if (ExchangeInitialized) {
TRC_ALT((TB, "Tearing down ExchangeManager"));
_ExchangeManager.Uninitialize();
}
if (Registered) {
TRC_ALT((TB, "Unregistering for packets"));
RemovePacketReceiver(this);
}
}
}
return _Initialized;
}
DrSession::~DrSession()
{
BEGIN_FN("DrSession::~DrSession");
ASSERT(_crefs == 0);
TRC_NRM((TB, "Session is deleted=%p", this));
if (_ChannelBuffer) {
delete _ChannelBuffer;
_ChannelBuffer = NULL;
}
}
#if DBG
VOID DrSession::DumpUserConfigSettings()
{
BEGIN_FN("DrSession::DumpUserConfigSettings");
TRC_NRM((TB, "Automatically map client drives: %s",
_AutoClientDrives ? "True" : "False"));
TRC_NRM((TB, "Automatically map client printers: %s",
_AutoClientLpts ? "True" : "False"));
TRC_NRM((TB, "Force client printer as default: %s",
_ForceClientLptDef ? "True" : "False"));
TRC_NRM((TB, "Disable client printer mapping: %s",
_DisableCpm ? "True" : "False"));
TRC_NRM((TB, "Disable client drive mapping: %s",
_DisableCdm ? "True" : "False"));
TRC_NRM((TB, "Disable client COM port mapping: %s",
_DisableCcm ? "True" : "False"));
TRC_NRM((TB, "Disable client printer mapping: %s",
_DisableLPT ? "True" : "False"));
TRC_NRM((TB, "Disable clipboard redirection: %s",
_DisableClip ? "True" : "False"));
TRC_NRM((TB, "DisableExe: %s",
_DisableExe ? "True" : "False"));
TRC_NRM((TB, "Disable client audio mapping: %s",
_DisableCam ? "True" : "False"));
}
#endif // DBG
void DrSession::Release(void)
{
BEGIN_FN("DrSession::Release");
ULONG crefs;
ASSERT(_crefs > 0);
ASSERT(Sessions != NULL);
Sessions->LockExclusive();
if ((crefs = InterlockedDecrement(&_crefs)) == 0)
{
TRC_DBG((TB, "Deleting object type %s",
_ClassName));
if (_Initialized) {
Sessions->Remove(this);
}
Sessions->Unlock();
delete this;
} else {
TRC_DBG((TB, "Releasing object type %s to %d",
_ClassName, crefs));
ASSERT(_Initialized);
Sessions->Unlock();
}
}
BOOL DrSession::Connect(PCHANNEL_CONNECT_IN ConnectIn,
PCHANNEL_CONNECT_OUT ConnectOut)
{
ULONG i;
SmartPtr<VirtualChannel> Channel = NULL;
BOOL ExchangeManagerStarted = FALSE;
NTSTATUS Status;
PCHANNEL_CONNECT_DEF Channels;
BEGIN_FN("DrSession::Connect");
_ConnectNotificationLock.AcquireResourceExclusive();
if (InterlockedCompareExchange(&_ConnectCount, 1, 0) != 0) {
TRC_ALT((TB, "RDPDR connect reentry called"));
ASSERT(FALSE);
_ConnectNotificationLock.ReleaseResource();
return FALSE;
}
//ASSERT(_ApcChannelRef == 0);
ASSERT(GetState() == csExpired);
//
// Need granular locking on notifying RDPDYN so we don't deadlock.
//
LockRDPDYNConnectStateChange();
//
// Tell RDPDYN about the new session.
//
RDPDYN_SessionConnected(ConnectIn->hdr.sessionID);
UnlockRDPDYNConnectStateChange();
//
// Clear the channel event
//
_ChannelDeletionEvent.ResetEvent();
//
// Save all the user settings, we may need them later.
// This is conceptually "wasteful" because we don't care
// about some of them. But some may be supported in the
// the future, and the size is padded out to a ULONG anyway
//
_SessionId = ConnectIn->hdr.sessionID;
_AutoClientDrives = ConnectIn->fAutoClientDrives;
_AutoClientLpts = ConnectIn->fAutoClientLpts;
_ForceClientLptDef = ConnectIn->fForceClientLptDef;
_DisableCpm = ConnectIn->fDisableCpm;
_DisableCdm = ConnectIn->fDisableCdm;
_DisableCcm = ConnectIn->fDisableCcm;
_DisableLPT = ConnectIn->fDisableLPT;
_DisableClip = ConnectIn->fDisableClip;
_DisableExe = ConnectIn->fDisableExe;
_DisableCam = ConnectIn->fDisableCam;
_ClientId = 0xFFFFFFFF;
#if DBG
DumpUserConfigSettings();
#endif // DBG
//
// Is our channel name in the list of channels on which the client is
// prepared to communicate?
//
if (!FindChannelFromConnectIn(&i, ConnectIn)) {
TRC_ALT((TB, "Undoctored client"));
Status = STATUS_UNSUCCESSFUL;
}
else {
Status = STATUS_SUCCESS;
}
if (NT_SUCCESS(Status)) {
ExchangeManagerStarted = _ExchangeManager.Start();
}
if (ExchangeManagerStarted) {
Status = STATUS_SUCCESS;
} else {
Status = STATUS_UNSUCCESSFUL;
}
if (NT_SUCCESS(Status)) {
Channel = new(NonPagedPool) VirtualChannel;
if (Channel != NULL) {
Channels = (PCHANNEL_CONNECT_DEF)(ConnectIn + 1);
if (Channel->Create(ConnectIn->hdr.IcaHandle,
ConnectIn->hdr.sessionID, Channels[i].ID,
_ChannelDeletionEvent.GetEvent())) {
TRC_NRM((TB, "Channel opened for session %d",
ConnectIn->hdr.sessionID));
Status = STATUS_SUCCESS;
} else {
TRC_ALT((TB, "Channel not opened for session %d",
ConnectIn->hdr.sessionID));
Status = STATUS_UNSUCCESSFUL;
}
}
}
TRC_NRM((TB, "Channel open result: %lx", Status));
if (NT_SUCCESS(Status)) {
_Channel = Channel;
//
// Send a server announce
//
Status = ServerAnnounceWrite();
if (Status == STATUS_PENDING) {
Status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(Status)) {
TRC_ERR((TB, "DrServerAnnounceWrite returned error: "
"%lx", Status));
}
}
if (NT_SUCCESS(Status)) {
SetSessionState(csPendingClientConfirm);
//
// Start Reading.
//
ReadPacket();
}
if (NT_SUCCESS(Status)) {
//
// Release the Notification resource
//
_ConnectNotificationLock.ReleaseResource();
} else {
TRC_ALT((TB, "Cleaning up DrOnSession create work due "
"to error condition: %lx.", Status));
//
// Since we just Closed the channel, NULL out the handle
// to it before deleting the client entry
//
if (Channel != NULL) {
Channel = NULL;
DeleteChannel(TRUE);
}
if (ExchangeManagerStarted) {
_ExchangeManager.Stop();
}
//
// Need granular locking on notifying RDPDYN so we don't deadlock.
//
LockRDPDYNConnectStateChange();
SetSessionState(csExpired);
// Notify RDPDYN about the session terminating.
// We won't do this later because we're not making this a
// doctored session after all, so we need this to
// properly frame this from an RDPDYN perspective
RDPDYN_SessionDisconnected(ConnectIn->hdr.sessionID);
UnlockRDPDYNConnectStateChange();
LONG Count = InterlockedCompareExchange(&_ConnectCount, 0, 1);
ASSERT(Count == 1);
//
// Release the resource and dereference the client entry.
//
_ConnectNotificationLock.ReleaseResource();
}
return NT_SUCCESS(Status);
}
VOID DrSession::Disconnect(PCHANNEL_DISCONNECT_IN DisconnectIn,
PCHANNEL_DISCONNECT_OUT DisconnectOut)
{
BEGIN_FN("DrSession::Disconnect");
//
// Ensure synchronization of notification
//
_ConnectNotificationLock.AcquireResourceExclusive();
//
// Delete our reference to the channel handle and close it
//
DeleteChannel(TRUE);
//
// Enumerate the atlas entries and cancel the I/O
//
_ExchangeManager.Stop();
//
// Enumerate the devices and mark them disconnected
//
_DeviceManager.Disconnect();
//
// Need granular locking on notifying RDPDYN so we don't deadlock.
//
LockRDPDYNConnectStateChange();
//
// Notify RDPDYN about the session terminating.
// This function is called when an existing session is disconnected.
ASSERT(_SessionId == DisconnectIn->hdr.sessionID);
RDPDYN_SessionDisconnected(DisconnectIn->hdr.sessionID);
//
// Avoid creating additional references which would only lead to
// disappointing results. DrOnSessionDisconnect sets this to
// csDisconnected, so we have to be after that
//
SetSessionState(csExpired);
UnlockRDPDYNConnectStateChange();
TRC_NRM((TB, "Session: %d is disconnected", _SessionId));
LONG Count = InterlockedCompareExchange(&_ConnectCount, 0, 1);
ASSERT(Count == 1);
//
// Release the resource before we dereference (and potentially
// delete) the ClientEntry
//
_ConnectNotificationLock.ReleaseResource();
}
BOOL DrSession::FindChannelFromConnectIn(PULONG ChannelId,
PCHANNEL_CONNECT_IN ConnectIn)
/*++
Routine Description:
Finds the appropriate channel id given a ConnectIn structure
Arguments:
ChannelId - Where to put the channel if it is found
Return Value:
Whether the channel was found
--*/
{
ANSI_STRING DrChannelName;
ANSI_STRING ChannelSearch;
PCHANNEL_CONNECT_DEF Channels = (PCHANNEL_CONNECT_DEF)(ConnectIn + 1);
BEGIN_FN("DrSession::FindChannelFromConnectIn");
TRC_NRM((TB, "%ld Channels", ConnectIn->channelCount));
RtlInitString(&DrChannelName, DR_CHANNEL_NAME);
for (*ChannelId = 0; *ChannelId < ConnectIn->channelCount; *ChannelId++) {
Channels[*ChannelId].name[CHANNEL_NAME_LEN] = 0;
RtlInitString(&ChannelSearch, Channels[*ChannelId].name);
TRC_DBG((TB, "Found channel %wZ", &ChannelSearch));
if (RtlEqualString(&DrChannelName, &ChannelSearch, TRUE))
break;
}
return (*ChannelId != ConnectIn->channelCount);
}
VOID DrSession::DeleteChannel(BOOL bWait)
/*++
Routine Description:
Safely removes the Channel from the session and ditches the reference
Arguments:
ClientEntry - The relevant client entry
Return Value:
None
Notes:
--*/
{
SmartPtr<VirtualChannel> Channel;
BEGIN_FN("DrSession::DeleteChannel");
DrAcquireSpinLock();
Channel = _Channel;
_Channel = NULL;
DrReleaseSpinLock();
if (Channel != NULL) {
//
// Do the ZwClose on it so all I/O will be cancelled
//
Channel->SubmitClose();
//
// Remove our reference to it so it can go to zero
//
Channel = NULL;
}
if (bWait) {
//
// Wait for all of our Irps to finish.
//
#if DBG
LARGE_INTEGER Timeout;
NTSTATUS Status;
KeQuerySystemTime(&Timeout);
Timeout.QuadPart += 6000000000; // 10 min in hundreds of nano-seconds
while ((Status = _ChannelDeletionEvent.Wait(UserRequest, KernelMode,
TRUE, &Timeout)) != STATUS_SUCCESS) {
//TRC_ASSERT(Status != STATUS_TIMEOUT,
// (TB, "Timed out waiting for channel 0x%p", Channel));
if (Status == STATUS_TIMEOUT) {
TRC_DBG((TB, "Timed out waiting for channel 0x%p", Channel));
//
// If we just hit go in the debugger, we want to give it
// just another 2 min
//
KeQuerySystemTime(&Timeout);
Timeout.QuadPart += 1200000000; // 2 min in hundreds of nano-seconds
}
// Do nothing, just hit an alerted state
}
#else // !DBG
while (_ChannelDeletionEvent.Wait(UserRequest, KernelMode,
TRUE) != STATUS_SUCCESS) {
// Do nothing, just hit an alerted state
}
#endif // DBG
//ASSERT(_ApcChannelRef == 0);
_ChannelDeletionEvent.ResetEvent();
} // if (bWait)
}
BOOL
DrSession::GetChannel(
SmartPtr<VirtualChannel> &Channel
)
/*++
Routine Description:
Safely gets the Channel from the session and adds a reference
Arguments:
ClientEntry - The relevant client entry
Return Value:
The freshly referenced channel or NULL
Notes:
--*/
{
BEGIN_FN("DrSession::GetChannel");
_ChannelLock.AcquireResourceShared();
Channel = _Channel;
_ChannelLock.ReleaseResource();
return Channel != NULL;
}
VOID
DrSession::SetChannel(
SmartPtr<VirtualChannel> &Channel
)
/*++
Routine Description:
Safely sets the Channel for the session
Arguments:
ClientEntry - The relevant client entry
Return Value:
None
Notes:
--*/
{
BEGIN_FN("DrSession::SetChannel");
_ChannelLock.AcquireResourceExclusive();
_Channel = Channel;
_ChannelLock.ReleaseResource();
}
NTSTATUS DrSession::SendToClient(PVOID Buffer, ULONG Length,
ISessionPacketSender *PacketSender, BOOL bWorkerItem,
BOOL LowPrioSend, PVOID AdditionalContext)
{
BEGIN_FN("DrSession::SendToClient A");
return PrivateSendToClient(
Buffer, Length, PacketSender, NULL, bWorkerItem,
LowPrioSend, AdditionalContext
);
}
NTSTATUS DrSession::SendToClient(PVOID Buffer, ULONG Length,
DrWriteCallback WriteCallback, BOOL bWorkerItem,
BOOL LowPrioSend, PVOID AdditionalContext)
{
BEGIN_FN("DrSession::SendToClient B");
return PrivateSendToClient(
Buffer, Length, NULL, WriteCallback, bWorkerItem,
LowPrioSend, AdditionalContext
);
}
NTSTATUS DrSession::PrivateSendToClient(PVOID Buffer, ULONG Length,
ISessionPacketSender *PacketSender, DrWriteCallback WriteCallback,
BOOL bWorkerItem, BOOL LowPrioSend, PVOID AdditionalContext)
/*++
Routine Description:
Sends data to the client. Handles details of allocating contextual memory
and verifying the virtual channel is available, etc.
Arguments:
Buffer - The data to be sent
Length - the length of Buffer in bytes
CallbackId - An identifier for the completion work
AdditionalContext - Context specific to CallbackId, NULL by default
Return Value:
NTSTATUS code indicating communication status
--*/
{
NTSTATUS Status;
RDPDR_SERVER_ANNOUNCE_PACKET ServerAnnouncePacket;
SmartPtr<VirtualChannel> Channel;
DrWriteContext *WriteContext = NULL;
BEGIN_FN("DrSession::SendToClient C");
ASSERT(Buffer != NULL);
ASSERT(Length > 0);
if (GetChannel(Channel)) {
TRC_DBG((TB, "Got session channel"));
WriteContext = new DrWriteContext;
if (WriteContext != NULL) {
TRC_DBG((TB, "WriteContext allocated, sending"));
WriteContext->Session = this;
if (bWorkerItem) {
WriteContext->BufferToFree = Buffer;
}
else {
WriteContext->BufferToFree = NULL;
}
WriteContext->PacketSender = PacketSender;
WriteContext->WriteCallback = WriteCallback;
WriteContext->AdditionalContext = AdditionalContext;
Status = Channel->Write(SendCompletionRoutine,
WriteContext, Buffer, Length, bWorkerItem, LowPrioSend);
} else {
TRC_ERR((TB, "DrServerAnnounceWrite unable to allocate "
"WriteContext"));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
TRC_NRM((TB, "Channel not available"));
Status = STATUS_DEVICE_NOT_CONNECTED;
}
return Status;
}
NTSTATUS DrSession::SendCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
IoCompletion routine once a Server Announce packet has been sent to
the client
Arguments:
Context - Contains a pointer to the ClientEntry information
IoStatusBlock - Status information about the operation. The Information
indicates the actual number of bytes written
Reserved - Reserved
Return Value:
None
--*/
{
DrWriteContext *WriteContext = (DrWriteContext *)Context;
BEGIN_FN_STATIC("DrSession::SendCompletionRoutine");
ASSERT(Context != NULL);
if (Irp) {
TRC_NRM((TB, "status: 0x%x", Irp->IoStatus.Status));
WriteContext->Session->SendCompletion(WriteContext, &(Irp->IoStatus));
IoFreeIrp(Irp);
}
else {
TRC_NRM((TB, "status: 0x%x", WriteContext->IoStatusBlock.Status));
WriteContext->Session->SendCompletion(WriteContext, &(WriteContext->IoStatusBlock));
}
if (WriteContext->BufferToFree) {
delete (WriteContext->BufferToFree);
}
delete WriteContext;
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID DrSession::SendCompletion(DrWriteContext *WriteContext,
PIO_STATUS_BLOCK IoStatusBlock)
{
NTSTATUS Status = STATUS_SUCCESS;
BEGIN_FN("DrSession::SendCompletion");
//
// One of these should be null
//
ASSERT(WriteContext->PacketSender == NULL ||
WriteContext->WriteCallback == NULL);
if (WriteContext->PacketSender != NULL) {
Status = WriteContext->PacketSender->SendCompleted(
WriteContext->AdditionalContext, IoStatusBlock);
} else if (WriteContext->WriteCallback != NULL) {
Status = WriteContext->WriteCallback(
WriteContext->AdditionalContext, IoStatusBlock);
}
if (!NT_ERROR(Status)) {
TRC_NRM((TB, "SendCompletion succeded"));
} else {
TRC_NRM((TB, "SendCompletion failed"));
ChannelIoFailed();
}
}
NTSTATUS DrSession::ServerAnnounceWrite()
{
NTSTATUS Status;
RDPDR_SERVER_ANNOUNCE_PACKET ServerAnnouncePacket;
BEGIN_FN("DrSession::ServerAnnounceWrite");
//
// Construct the packet
//
ServerAnnouncePacket.Header.Component = RDPDR_CTYP_CORE;
ServerAnnouncePacket.Header.PacketId = DR_CORE_SERVER_ANNOUNCE;
ServerAnnouncePacket.VersionInfo.Major = RDPDR_MAJOR_VERSION;
ServerAnnouncePacket.VersionInfo.Minor = RDPDR_MINOR_VERSION;
ServerAnnouncePacket.ServerAnnounce.ClientId = _ClientId;
//
// This is synchronous write
//
Status = SendToClient(&ServerAnnouncePacket, sizeof(RDPDR_SERVER_ANNOUNCE_PACKET), this, FALSE);
return Status;
}
VOID DrSession::ReadPacket()
{
NTSTATUS Status;
SmartPtr<VirtualChannel> Channel;
BEGIN_FN("DrSession::ReadPacket");
ASSERT(_ChannelBuffer != NULL);
ASSERT(_ChannelBufferSize > 0);
if (GetChannel(Channel)) {
TRC_DBG((TB, "Got session channel"));
//
// It'd be ineficient to allocate a SmartPtr just to do an
// AddRef when instead we'd neet to remember to call delete on
// the allocated memory. Thus the explicit AddRef.
//
AddRef();
_PartialPacketData = 0;
DEBUG_DEREF_BUF();
Status = Channel->Read(ReadCompletionRoutine, this,
_ChannelBuffer, _ChannelBufferSize, FALSE);
if (!NT_SUCCESS(Status)) {
//
// Frame the AddRef above for error case
//
Release();
ChannelIoFailed();
}
} else {
TRC_NRM((TB, "Channel not available"));
Status = STATUS_DEVICE_NOT_CONNECTED;
}
}
BOOL DrSession::ReadMore(ULONG cbSaveData, ULONG cbWantData)
/*++
Routine Description:
Initiates a read operation on the channel to retrieve more
data and place it in the channel buffer after the
current data. Sets an appropriate completion handler.
Arguments:
cbSaveData - Data to be saved from the previous read
cbWantData - Expected total size (including saved data) needed
Return Value:
BOOL - True if reading worked, False otherwise
--*/
{
ULONG cbNewBufferSize = _ChannelBufferSize;
NTSTATUS Status = STATUS_SUCCESS;
SmartPtr<VirtualChannel> Channel;
BEGIN_FN("DrSession::ReadMore");
if ((cbWantData != 0) && (cbNewBufferSize < cbWantData)) {
cbNewBufferSize = ((cbWantData / CHANNEL_CHUNK_LENGTH) + 1)
* CHANNEL_CHUNK_LENGTH;
}
if (cbNewBufferSize - cbSaveData < CHANNEL_CHUNK_LENGTH) {
cbNewBufferSize += CHANNEL_CHUNK_LENGTH;
}
if (cbNewBufferSize > _ChannelBufferSize ) {
//
// Need to expand the buffer size
//
TRC_NRM((TB, "Buffer full, expanding"));
Status = ReallocateChannelBuffer(cbNewBufferSize, cbSaveData);
if (!NT_SUCCESS(Status)) {
// We didn't get a bigger buffer, so we really can't
// read any more.
TRC_ERR((TB, "Failed to expand channel buffer, %lx", Status));
ChannelIoFailed();
}
}
if (NT_SUCCESS(Status)) {
//
// Go ahead and read the additional data
//
if (GetChannel(Channel)) {
TRC_DBG((TB, "Got session channel"));
//
// It'd be inefiecent to allocate a SmartPtr just to do an
// AddRef when instead we'd neet to remember to call delete on
// the allocated memory. Thus the explicit AddRef.
//
AddRef();
_PartialPacketData = cbSaveData;
// Deref channel buffer
DEBUG_DEREF_BUF();
Status = Channel->Read(ReadCompletionRoutine, this,
_ChannelBuffer + cbSaveData, _ChannelBufferSize - cbSaveData, FALSE);
if (!NT_SUCCESS(Status)) {
//
// Frame the AddRef above for error case
//
TRC_ERR((TB, "Failed (0x%x) to Read channel in ReadMore", Status));
Release();
ChannelIoFailed();
}
} else {
TRC_NRM((TB, "Channel not available"));
Status = STATUS_DEVICE_NOT_CONNECTED;
}
}
return NT_SUCCESS(Status);
}
NTSTATUS DrSession::ReadCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
/*++
Routine Description:
IoCompletion routine once a Server Announce packet has been sent to
the client
Arguments:
Context - Contains a pointer to the ClientEntry information
IoStatusBlock - Status information about the operation. The Information
indicates the actual number of bytes written
Reserved - Reserved
Return Value:
None
--*/
{
DrSession *Session = (DrSession *)Context;
BEGIN_FN_STATIC("DrSession::ReadCompletionRoutine");
ASSERT(Context != NULL);
#if DBG
InterlockedDecrement(&(Session->_ApcChannelRef));
#endif
if (NT_SUCCESS(Irp->IoStatus.Status) && Irp->AssociatedIrp.SystemBuffer != NULL) {
ASSERT(Irp->Flags & (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION));
RtlCopyMemory(Irp->UserBuffer, Irp->AssociatedIrp.SystemBuffer, Irp->IoStatus.Information);
}
//
// Call the real completion routine
//
Session->ReadCompletion(&(Irp->IoStatus));
//
// Free the AddRef in ReadPacket()
//
Session->Release();
// Free the system buffer
if (NT_SUCCESS(Irp->IoStatus.Status) && Irp->AssociatedIrp.SystemBuffer != NULL) {
ExFreePool(Irp->AssociatedIrp.SystemBuffer);
Irp->AssociatedIrp.SystemBuffer = NULL;
}
//
// Free the irp
//
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID DrSession::ReadCompletion(PIO_STATUS_BLOCK IoStatusBlock)
/*++
Routine Description:
Completion routine once a packet header has been read from
the client. Dispatches the request to the appropriate handler
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS Status;
BOOL fDoDefaultRead = TRUE;
ISessionPacketReceiver *PacketReceiver;
ListEntry *ListEnum;
BOOL bFound = FALSE;
#if DBG
ULONG cFound = 0;
#endif // DBG
BEGIN_FN("DrSession::ReadCompletion");
DEBUG_REF_BUF();
ASSERT(InterlockedDecrement(&_ApcCount) == 0);
PRDPDR_HEADER RdpdrHeader = (PRDPDR_HEADER)_ChannelBuffer;
_ReadStatus = *IoStatusBlock;
TRC_NRM((TB, "IoStatus %lx, Bytes %lx, Component %c%c, PacketId %c%c",
_ReadStatus.Status, _ReadStatus.Information,
HIBYTE(RdpdrHeader->Component), LOBYTE(RdpdrHeader->Component),
HIBYTE(RdpdrHeader->PacketId), LOBYTE(RdpdrHeader->PacketId)));
if (NT_SUCCESS(_ReadStatus.Status)) {
TRC_NRM((TB, "Successful channel read"));
Status = STATUS_SUCCESS;
//
// Update the information field to reflect any data saved from a
// previous read
//
_ReadStatus.Information += _PartialPacketData;
TRC_ASSERT(_ChannelBufferSize >= _ReadStatus.Information,
(TB, "ReadCompleted with too much data"));
TRC_DBG((TB, "In ReadCompletion, _ChannelBuffer=%p", _ChannelBuffer));
_PacketReceivers.LockShared();
#if DBG
//
// We should only have one handler, in debug, assert this,
//
ListEnum = _PacketReceivers.First();
while (ListEnum != NULL) {
PacketReceiver = (ISessionPacketReceiver *)ListEnum->Node();
TRC_DBG((TB, "PacketReceiver=%p", PacketReceiver));
if (PacketReceiver->RecognizePacket(RdpdrHeader)) {
cFound++;
// "assert this"
ASSERT(cFound == 1);
}
ListEnum = _PacketReceivers.Next(ListEnum);
}
#endif // DBG
if (_ReadStatus.Information < sizeof(RDPDR_HEADER)) {
TRC_ERR((TB, "Bad RDPDR packet size"));
Status = STATUS_DEVICE_PROTOCOL_ERROR;
_PacketReceivers.Unlock();
goto CleanUp;
}
ListEnum = _PacketReceivers.First();
while (ListEnum != NULL) {
PacketReceiver = (ISessionPacketReceiver *)ListEnum->Node();
TRC_DBG((TB, "PacketReceiver=%p", PacketReceiver));
if (PacketReceiver->RecognizePacket(RdpdrHeader)) {
//
// Set the _DoDefaultRead here, if we get called back to do
// a read from the packet handler we'll clear in back out
//
bFound = TRUE;
Status = PacketReceiver->HandlePacket(RdpdrHeader, (ULONG)(_ReadStatus.Information),
&fDoDefaultRead);
// Once we found the one handler we're done
break;
}
ListEnum = _PacketReceivers.Next(ListEnum);
}
_PacketReceivers.Unlock();
if (!bFound) {
TRC_ERR((TB, "Unrecognized RDPDR Header %d", RdpdrHeader->Component));
Status = STATUS_DEVICE_PROTOCOL_ERROR;
//ASSERT(bFound);
}
} else {
Status = _ReadStatus.Status;
TRC_ALT((TB, "Channel read failed 0x%X", Status));
}
CleanUp:
if (NT_SUCCESS(Status)) {
if (fDoDefaultRead) {
//
// Start the next read before releasing our reference to the ClientEntry
//
TRC_DBG((TB, "Starting default read"));
ReadPacket();
} else {
TRC_DBG((TB, "Skipping default read"));
}
} else {
TRC_ERR((TB, "Error detected in ReadCompletion %lx",
Status));
ChannelIoFailed();
}
}
BOOL DrSession::RecognizePacket(PRDPDR_HEADER RdpdrHeader)
/*++
Routine Description:
Determines if the packet will be handled by this object
Arguments:
RdpdrHeader - Header of the packet.
Return Value:
TRUE if this object should handle this packet
FALSE if this object should not handle this packet
--*/
{
BEGIN_FN("DrSession::RecognizePacket");
//
// If you add a packet here, update the ASSERTS in HandlePacket
//
switch (RdpdrHeader->Component) {
case RDPDR_CTYP_CORE:
switch (RdpdrHeader->PacketId) {
case DR_CORE_CLIENTID_CONFIRM:
TRC_NRM((TB, "Recognized CLIENTID_CONFIRM packet"));
return TRUE;
case DR_CORE_CLIENT_NAME:
TRC_NRM((TB, "Recognized CLIENT_NAME packet"));
return TRUE;
case DR_CORE_CLIENT_CAPABILITY:
TRC_NRM((TB, "Recognized CLIENT_CAPABILITY packet"));
return TRUE;
case DR_CORE_CLIENT_DISPLAY_NAME:
TRC_NRM((TB, "Recognized CLIENT_DISPLAY_NAME packet"));
return TRUE;
}
}
return FALSE;
}
NTSTATUS DrSession::HandlePacket(PRDPDR_HEADER RdpdrHeader, ULONG Length,
BOOL *DoDefaultRead)
/*++
Routine Description:
Handles this packet
Arguments:
RdpdrHeader - Header of the packet.
Length - Total length of the packet
Return Value:
NTSTATUS - An error code indicates the client is Bad and should be
disconnected, otherwise SUCCESS.
--*/
{
NTSTATUS Status = STATUS_DEVICE_PROTOCOL_ERROR;
BEGIN_FN("DrSession::HandlePacket");
//
// RdpdrHeader read, dispatch based on the header
//
ASSERT(RdpdrHeader->Component == RDPDR_CTYP_CORE);
switch (RdpdrHeader->Component) {
case RDPDR_CTYP_CORE:
ASSERT(RdpdrHeader->PacketId == DR_CORE_CLIENTID_CONFIRM ||
RdpdrHeader->PacketId == DR_CORE_CLIENT_NAME ||
RdpdrHeader->PacketId == DR_CORE_CLIENT_CAPABILITY ||
RdpdrHeader->PacketId == DR_CORE_CLIENT_DISPLAY_NAME);
switch (RdpdrHeader->PacketId) {
case DR_CORE_CLIENTID_CONFIRM:
Status = OnClientIdConfirm(RdpdrHeader, Length, DoDefaultRead);
break;
case DR_CORE_CLIENT_NAME:
Status = OnClientName(RdpdrHeader, Length, DoDefaultRead);
break;
case DR_CORE_CLIENT_CAPABILITY:
Status = OnClientCapability(RdpdrHeader, Length, DoDefaultRead);
break;
case DR_CORE_CLIENT_DISPLAY_NAME:
Status = OnClientDisplayName(RdpdrHeader, Length, DoDefaultRead);
break;
}
}
return Status;
}
#if DBG
BOOL DrSession::PacketReceiverExists(ISessionPacketReceiver *PacketReceiver)
{
PVOID NodeEnum;
PVOID NodeFound = NULL;
ListEntry *ListEnum;
BEGIN_FN("DrSession::PacketReceiverExists");
_PacketReceivers.LockShared();
ListEnum = _PacketReceivers.First();
while (ListEnum != NULL) {
NodeEnum = ListEnum->Node();
if (NodeEnum == (PVOID) PacketReceiver) {
NodeFound = NodeEnum;
NodeEnum = NULL;
ListEnum = NULL;
break;
}
ListEnum = _PacketReceivers.Next(ListEnum);
}
_PacketReceivers.Unlock();
return NodeFound != NULL;
}
#endif // DBG
NTSTATUS DrSession::RegisterPacketReceiver(ISessionPacketReceiver *PacketReceiver)
/*++
Routine Description:
Adds an object to the queue of Packet handlers.
Arguments:
PacketReceiver - An interface to the object that wants to handle
some packets
Return Value:
Boolean indication of whether to do a default read (TRUE) or not (FALSE),
where FALSE might be specified if another read has been requested
explicitly to get a full packet
--*/
{
BEGIN_FN("DrSession::RegisterPacketReceiver");
ASSERT(!PacketReceiverExists(PacketReceiver));
ASSERT(PacketReceiver != NULL);
if (_PacketReceivers.CreateEntry(PacketReceiver)) {
return STATUS_SUCCESS;
} else {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
VOID DrSession::RemovePacketReceiver(ISessionPacketReceiver *PacketReceiver)
{
PVOID NodeEnum;
ListEntry *ListEnum;
BEGIN_FN("DrSession::RemovePacketReceiver");
_PacketReceivers.LockExclusive();
ListEnum = _PacketReceivers.First();
while (ListEnum != NULL) {
NodeEnum = ListEnum->Node();
if (NodeEnum == (PVOID) PacketReceiver) {
break;
}
ListEnum = _PacketReceivers.Next(ListEnum);
}
ASSERT(ListEnum != NULL);
_PacketReceivers.RemoveEntry(ListEnum);
_PacketReceivers.Unlock();
}
NTSTATUS DrSession::OnClientIdConfirm(PRDPDR_HEADER RdpdrHeader, ULONG cbPacket,
BOOL *DoDefaultRead)
/*++
Routine Description:
Called in response to recognizing a ClientIdConfirm packet has been
received.
Arguments:
RdpdrHeader - The packet
cbPacket - Bytes in the packet
DoDefaultRead - Set this to false if you do an explicit read
Return Value:
--*/
{
NTSTATUS Status;
PRDPDR_CLIENT_CONFIRM_PACKET ClientConfirmPacket =
(PRDPDR_CLIENT_CONFIRM_PACKET)RdpdrHeader;
BEGIN_FN("DrSession::OnClientIdConfirm");
TRC_ASSERT(ClientConfirmPacket->Header.Component == RDPDR_CTYP_CORE,
(TB, "Expected Core packet type!"));
TRC_ASSERT(ClientConfirmPacket->Header.PacketId == DR_CORE_CLIENTID_CONFIRM,
(TB, "Expected ClientConfirmPacket!"));
*DoDefaultRead = TRUE;
//
// Check the version. The original protocol didn't have a version field,
// so we first check to make sure the packet is big enough to indicate
// one is present.
//
if (cbPacket < sizeof(RDPDR_CLIENT_CONFIRM_PACKET)) {
//
// Client version too old to have version info. Just close
// the channel and be done with him.
//
TRC_ERR((TB, "ClientConfirmPacket size incorrect, may be old "
"client. Size: %ld",
cbPacket));
return STATUS_DEVICE_PROTOCOL_ERROR;
}
//
// We have a version field
//
TRC_NRM((TB, "Client version Major: %d Minor: %d",
ClientConfirmPacket->VersionInfo.Major,
ClientConfirmPacket->VersionInfo.Minor));
// Keep the version info
_ClientVersion.Major = ClientConfirmPacket->VersionInfo.Major;
_ClientVersion.Minor = ClientConfirmPacket->VersionInfo.Minor;
// Send server capability to client
if (COMPARE_VERSION(_ClientVersion.Minor, _ClientVersion.Major,
5, 1) >= 0) {
SendClientCapability();
}
// Look for ClientID to have changed
if (ClientConfirmPacket->ClientConfirm.ClientId != _ClientId) {
TRC_NRM((TB, "Client %lx replied with alternate "
"ClientId %lx", _ClientId,
ClientConfirmPacket->ClientConfirm.ClientId));
SetSessionState(csConnected);
// TODO:
// Kill off the old devices
//
_ClientId = ClientConfirmPacket->ClientConfirm.ClientId;
//
// Accept the clientid
//
SendClientConfirm();
}
return STATUS_SUCCESS;
}
NTSTATUS DrSession::OnClientCapability(PRDPDR_HEADER RdpdrHeader, ULONG cbPacket,
BOOL *DoDefaultRead)
/*++
Routine Description:
Called in response to recognizing a client capability packet has been
received.
Arguments:
RdpdrHeader - The packet
cbPacket - Bytes in the packet
DoDefaultRead - Set this to false if you do an explicit read
Return Value:
--*/
{
NTSTATUS Status;
PRDPDR_CAPABILITY_SET_HEADER pCapSetHeader = (PRDPDR_CAPABILITY_SET_HEADER)RdpdrHeader;
PRDPDR_CAPABILITY_HEADER pCapHdr = (PRDPDR_CAPABILITY_HEADER)(pCapSetHeader + 1);
PBYTE pPacketEnd;
ULONG PacketLen;
BOOL CapSupported;
BEGIN_FN("DrSession::OnClientCapability");
TRC_ASSERT(pCapSetHeader->Header.Component == RDPDR_CTYP_CORE,
(TB, "Expected Core packet type!"));
TRC_ASSERT(pCapSetHeader->Header.PacketId == DR_CORE_CLIENT_CAPABILITY,
(TB, "Expected ClientCapabilityPacket!"));
*DoDefaultRead = TRUE;
//
// Check to make sure the server send us at least the header size
//
if (cbPacket < sizeof(RDPDR_CAPABILITY_SET_HEADER)) {
TRC_ERR((TB, "ClientCapabilityPacket size incorrect. Size: %ld",
cbPacket));
return STATUS_DEVICE_PROTOCOL_ERROR;
}
pPacketEnd = (PBYTE)RdpdrHeader + cbPacket;
//
// Grab the supported capability info from client's capability PDU
//
// TODO: Should check for large capability set?
//
for (unsigned i = 0; i < pCapSetHeader->numberCapabilities; i++) {
if (((PBYTE)(pCapHdr) + sizeof(RDPDR_CAPABILITY_HEADER) <= pPacketEnd) &&
(pCapHdr->capabilityLength <= (pPacketEnd - (PBYTE)pCapHdr))) {
PacketLen = (ULONG)(pPacketEnd - (PBYTE)pCapHdr);
Status = InitClientCapability(pCapHdr, &PacketLen, &CapSupported);
if (!NT_SUCCESS(Status)) {
TRC_ASSERT(FALSE,(TB, "Bad client capability packet"));
return Status;
}
pCapHdr = (PRDPDR_CAPABILITY_HEADER)(((PBYTE)pCapHdr) + pCapHdr->capabilityLength);
}
else {
TRC_ERR((TB, "ClientCapabilityPacket incorrect packet."));
return STATUS_DEVICE_PROTOCOL_ERROR;
}
}
return STATUS_SUCCESS;
}
VOID DrSession::SendClientConfirm()
/*++
Routine Description:
Sends a ClientIdConfirm packet to the client
Arguments:
None.
Return Value:
None.
--*/
{
PRDPDR_CLIENT_CONFIRM_PACKET pClientConfirmPacket;
BEGIN_FN("DrSession::SendClientConfirm");
//
// Construct the packet
//
pClientConfirmPacket = new RDPDR_CLIENT_CONFIRM_PACKET;
if (pClientConfirmPacket != NULL) {
pClientConfirmPacket->Header.Component = RDPDR_CTYP_CORE;
pClientConfirmPacket->Header.PacketId = DR_CORE_CLIENTID_CONFIRM;
pClientConfirmPacket->VersionInfo.Major = RDPDR_MAJOR_VERSION;
pClientConfirmPacket->VersionInfo.Minor = RDPDR_MINOR_VERSION;
pClientConfirmPacket->ClientConfirm.ClientId = _ClientId;
//
// Send it - asynchronous write, cleanup not here
//
SendToClient(pClientConfirmPacket, sizeof(RDPDR_CLIENT_CONFIRM_PACKET), this, TRUE);
}
}
VOID DrSession::SendClientCapability()
/*++
Routine Description:
Sends server capability packet to the client
Arguments:
None.
Return Value:
None.
--*/
{
PRDPDR_SERVER_COMBINED_CAPABILITYSET pSrvCapabilitySet;
BEGIN_FN("DrSession::SendClientCapability");
//
// Send it
//
pSrvCapabilitySet = new RDPDR_SERVER_COMBINED_CAPABILITYSET;
if (pSrvCapabilitySet != NULL) {
memcpy(pSrvCapabilitySet, &_SrvCapabilitySet, sizeof(RDPDR_SERVER_COMBINED_CAPABILITYSET));
//
// Send it - asynchronous write, cleanup not here
//
SendToClient(pSrvCapabilitySet, sizeof(RDPDR_SERVER_COMBINED_CAPABILITYSET), this, TRUE);
}
}
NTSTATUS DrSession::InitClientCapability(PRDPDR_CAPABILITY_HEADER pCapHdr, ULONG *pPacketLen, BOOL *pCapSupported)
/*++
Routine Description:
Initialize the client capability
Arguments:
pCapHdr - client capability
pPacketLen - In: Length of the total packet
Out: Length used in this function
CapSupported - TRUE - if we found the same capability supported on the server side
FALSE - if this is not a supported capability
Return Value:
TRUE - if we found the same capability supported on the server side
FALSE - if this is not a supported capability
--*/
{
*pCapSupported = FALSE;
switch(pCapHdr->capabilityType) {
case RDPDR_GENERAL_CAPABILITY_TYPE:
{
PRDPDR_GENERAL_CAPABILITY pGeneralCap = (PRDPDR_GENERAL_CAPABILITY)pCapHdr;
if (*pPacketLen < sizeof(RDPDR_GENERAL_CAPABILITY)) {
return STATUS_DEVICE_PROTOCOL_ERROR;
}
*pPacketLen = sizeof(RDPDR_GENERAL_CAPABILITY);
_CliCapabilitySet.GeneralCap.version = pGeneralCap->version;
_CliCapabilitySet.GeneralCap.osType = pGeneralCap->osType;
_CliCapabilitySet.GeneralCap.osVersion = pGeneralCap->osVersion;
_CliCapabilitySet.GeneralCap.ioCode1 = pGeneralCap->ioCode1;
_CliCapabilitySet.GeneralCap.extendedPDU = pGeneralCap->extendedPDU;
_CliCapabilitySet.GeneralCap.protocolMajorVersion = pGeneralCap->protocolMajorVersion;
_CliCapabilitySet.GeneralCap.protocolMinorVersion = pGeneralCap->protocolMinorVersion;
*pCapSupported = TRUE;
}
break;
case RDPDR_PRINT_CAPABILITY_TYPE:
{
PRDPDR_PRINT_CAPABILITY pPrintCap = (PRDPDR_PRINT_CAPABILITY)pCapHdr;
if (*pPacketLen < sizeof(RDPDR_PRINT_CAPABILITY)) {
return STATUS_DEVICE_PROTOCOL_ERROR;
}
*pPacketLen = sizeof(RDPDR_PRINT_CAPABILITY);
_CliCapabilitySet.PrintCap.version = pPrintCap->version;
*pCapSupported = TRUE;
}
break;
case RDPDR_PORT_CAPABILITY_TYPE:
{
PRDPDR_PORT_CAPABILITY pPortCap = (PRDPDR_PORT_CAPABILITY)pCapHdr;
if (*pPacketLen < sizeof(RDPDR_PORT_CAPABILITY)) {
return STATUS_DEVICE_PROTOCOL_ERROR;
}
*pPacketLen = sizeof(RDPDR_PORT_CAPABILITY);
_CliCapabilitySet.PortCap.version = pPortCap->version;
*pCapSupported = TRUE;
}
break;
case RDPDR_FS_CAPABILITY_TYPE:
{
PRDPDR_FS_CAPABILITY pFsCap = (PRDPDR_FS_CAPABILITY)pCapHdr;
if (*pPacketLen < sizeof(RDPDR_FS_CAPABILITY)) {
return STATUS_DEVICE_PROTOCOL_ERROR;
}
*pPacketLen = sizeof(RDPDR_FS_CAPABILITY);
_CliCapabilitySet.FileSysCap.version = pFsCap->version;
*pCapSupported = TRUE;
}
break;
case RDPDR_SMARTCARD_CAPABILITY_TYPE:
{
PRDPDR_SMARTCARD_CAPABILITY pSmartCardCap = (PRDPDR_SMARTCARD_CAPABILITY)pCapHdr;
if (*pPacketLen < sizeof(RDPDR_SMARTCARD_CAPABILITY)) {
return STATUS_DEVICE_PROTOCOL_ERROR;
}
*pPacketLen = sizeof(RDPDR_SMARTCARD_CAPABILITY);
_CliCapabilitySet.SmartCardCap.version = pSmartCardCap->version;
*pCapSupported = TRUE;
}
break;
default:
{
if (*pPacketLen < pCapHdr->capabilityLength) {
return STATUS_DEVICE_PROTOCOL_ERROR;
}
*pPacketLen = pCapHdr->capabilityLength;
}
break;
}
return STATUS_SUCCESS;
}
VOID DrSession::SendDeviceReply(ULONG DeviceId, NTSTATUS Result)
/*++
Routine Description:
Sends a DeviceReply packet to the client
Arguments:
DeviceId - Id that the client proposed
Result - Indication of whether the device was accepted
Return Value:
NTSTATUS - Success/failure indication of the operation
--*/
{
PRDPDR_DEVICE_REPLY_PACKET pDeviceReplyPacket;
BEGIN_FN("DrSession::SendDeviceReply");
//
// Construct the packet
//
pDeviceReplyPacket = new RDPDR_DEVICE_REPLY_PACKET;
if (pDeviceReplyPacket != NULL) {
pDeviceReplyPacket->Header.Component = RDPDR_CTYP_CORE;
pDeviceReplyPacket->Header.PacketId = DR_CORE_DEVICE_REPLY;
pDeviceReplyPacket->DeviceReply.DeviceId = DeviceId;
pDeviceReplyPacket->DeviceReply.ResultCode = Result;
//
// Send it - asynchronous write, cleanup not here
//
SendToClient(pDeviceReplyPacket, sizeof(RDPDR_DEVICE_REPLY_PACKET), this, TRUE);
}
}
VOID DrSession::ChannelIoFailed()
/*++
Routine Description:
Handles Virtual channel IO failure. Marks the client as disabled and cancels
all the outstanding Io operations
Arguments:
ClientEntry - The client which has been disconnected
Return Value:
None
--*/
{
BEGIN_FN("DrSession::ChannelIoFailed");
//
// Mark as disconnected
//
SetSessionState(csDisconnected);
//
// Close down the channel, but don't need to wait for all IO to
// finish
//
DeleteChannel(FALSE);
//
// Fail outstanding IO
// Should be done via Delete devices?
//
_ExchangeManager.Stop();
}
NTSTATUS DrSession::OnClientName(PRDPDR_HEADER RdpdrHeader, ULONG cbPacket,
BOOL *DoDefaultRead)
/*++
Routine Description:
Called in response to recognizing a ClientName packet has been
received.
Arguments:
RdpdrHeader - The packet
cbPacket - Bytes in the packet
Return Value:
Boolean indication of whether to do a default read (TRUE) or not (FALSE),
where FALSE might be specified if another read has been requested
explicitly to get a full packet
--*/
{
NTSTATUS Status;
PRDPDR_CLIENT_NAME_PACKET ClientNamePacket =
(PRDPDR_CLIENT_NAME_PACKET)RdpdrHeader;
ULONG cb;
BEGIN_FN("DrSession::OnClientName");
*DoDefaultRead = TRUE;
if (cbPacket < sizeof(RDPDR_CLIENT_NAME_PACKET)) {
//
// Sent an undersized packet
//
return STATUS_DEVICE_PROTOCOL_ERROR;
}
// Copy and possibly convert the computer name
if (ClientNamePacket->Name.Unicode) {
TRC_NRM((TB, "Copying Unicode client name"));
// Restrict size to max size
cb = ClientNamePacket->Name.ComputerNameLen;
if ((cbPacket - sizeof(RDPDR_CLIENT_DISPLAY_NAME_PACKET)) < cb) {
//
// Sent an undersized packet
//
return STATUS_DEVICE_PROTOCOL_ERROR;
}
if (cb > (RDPDR_MAX_COMPUTER_NAME_LENGTH * sizeof(WCHAR))) {
cb = RDPDR_MAX_COMPUTER_NAME_LENGTH * sizeof(WCHAR);
}
// Copy the text
RtlCopyMemory(_ClientName, (ClientNamePacket + 1), cb);
// Ensure buffer termination
_ClientName[RDPDR_MAX_COMPUTER_NAME_LENGTH - 1] = 0;
TRC_NRM((TB, "Copied client computer name: %S",
_ClientName));
} else {
cb = ClientNamePacket->Name.ComputerNameLen;
if (cbPacket - sizeof(RDPDR_CLIENT_NAME_PACKET) < cb) {
//
// Sent an undersized packet
//
return STATUS_DEVICE_PROTOCOL_ERROR;
}
if (cb > (RDPDR_MAX_COMPUTER_NAME_LENGTH)) {
cb = RDPDR_MAX_COMPUTER_NAME_LENGTH;
}
// CopyConvert the buffer
cb = ConvertToAndFromWideChar(ClientNamePacket->Name.CodePage,
_ClientName, sizeof(_ClientName),
(LPSTR)(ClientNamePacket + 1),
cb, TRUE);
if (cb != -1) {
// Successful conversion
_ClientName[RDPDR_MAX_COMPUTER_NAME_LENGTH - 1] = 0;
TRC_NRM((TB, "Converted client computer name: %S",
_ClientName));
} else {
// Doh
TRC_ERR((TB, "Failed to convert ComputerName to "
"Unicode."));
_ClientName[0] = 0;
}
}
return TRUE;
}
NTSTATUS DrSession::OnClientDisplayName(PRDPDR_HEADER RdpdrHeader, ULONG cbPacket,
BOOL *DoDefaultRead)
/*++
Routine Description:
Called in response to recognizing a ClientDisplayName packet has been
received.
Arguments:
RdpdrHeader - The packet
cbPacket - Bytes in the packet
Return Value:
Boolean indication of whether to do a default read (TRUE) or not (FALSE),
where FALSE might be specified if another read has been requested
explicitly to get a full packet
--*/
{
NTSTATUS Status;
PRDPDR_CLIENT_DISPLAY_NAME_PACKET ClientDisplayNamePacket =
(PRDPDR_CLIENT_DISPLAY_NAME_PACKET)RdpdrHeader;
ULONG cb;
BEGIN_FN("DrSession::OnClientDisplayName");
*DoDefaultRead = TRUE;
if (cbPacket < sizeof(RDPDR_CLIENT_DISPLAY_NAME_PACKET)) {
//
// Sent an undersized packet
//
return STATUS_DEVICE_PROTOCOL_ERROR;
}
// Copy the computer display name
TRC_NRM((TB, "Copying Unicode client display name"));
// Restrict size to max size
cb = ClientDisplayNamePacket->Name.ComputerDisplayNameLen;
if ((cbPacket - sizeof(RDPDR_CLIENT_DISPLAY_NAME_PACKET)) < cb) {
//
// Sent an undersized packet
//
return STATUS_DEVICE_PROTOCOL_ERROR;
}
if (cb > (RDPDR_MAX_CLIENT_DISPLAY_NAME * sizeof(WCHAR))) {
cb = RDPDR_MAX_CLIENT_DISPLAY_NAME * sizeof(WCHAR);
}
// Copy the text
RtlCopyMemory(_ClientDisplayName, (ClientDisplayNamePacket + 1), cb);
// Ensure buffer termination
_ClientDisplayName[RDPDR_MAX_CLIENT_DISPLAY_NAME - 1] = L'\0';
TRC_NRM((TB, "Copied client computer display name: %S",
_ClientName));
return TRUE;
}
NTSTATUS DrSession::ReallocateChannelBuffer(ULONG ulNewBufferSize,
ULONG ulSaveBytes)
/*++
Routine Description:
Attempts to make the channel buffer at least the given size, preserving
as many bytes as desired
Arguments:
ulNewBufferSize - The desired size
ulSaveBytes - The number of bytes in the existing buffer that should be
preserved
Return Value:
STATUS_SUCCESS - The channel buffer is now at least the desired size
STATUS_INSUFFICIENT_RESOURCES - The new buffer could not be allocated,
but the old buffer was preserved.
--*/
{
PUCHAR pNewBuffer;
NTSTATUS Status;
BEGIN_FN("DrSession::ReallocateChannelBuffer");
TRC_NRM((TB, "Old size: %ld, "
"desired size: %ld save bytes: %ld",
_ChannelBufferSize,
ulNewBufferSize,
ulSaveBytes));
if (ulNewBufferSize <= _ChannelBufferSize) {
return STATUS_SUCCESS;
}
pNewBuffer = new UCHAR[ulNewBufferSize];
if (pNewBuffer != NULL) {
TRC_NRM((TB, "saving the old bytes."));
// Save the current data
RtlCopyMemory(pNewBuffer, _ChannelBuffer, ulSaveBytes);
ASSERT(_ApcCount == 0);
delete _ChannelBuffer;
_ChannelBuffer = pNewBuffer;
TRC_DBG((TB, "New ChannelBuffer=%p", _ChannelBuffer));
_ChannelBufferSize = ulNewBufferSize;
Status = STATUS_SUCCESS;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return Status;
}
NTSTATUS DrSession::SendCompleted(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock)
{
BEGIN_FN("DrSession::SendCompleted");
//
// return the status, if it is an error the connection will be dropped
// automatically
//
return IoStatusBlock->Status;
}