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
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;
|
|
}
|