WindowsXP-SP1/termsrv/remdsk/server/rdhost/tsrdpserverdatachannelmgr.cpp
2020-09-30 16:53:49 +02:00

1759 lines
42 KiB
C++

/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
CTSRDPServerChannelMgr.cpp
Abstract:
This module contains the TSRDP server-side subclass of
CRemoteDesktopChannelMgr. Classes in this hierarchy are used to multiplex
a single data channel into multiple client channels.
CRemoteDesktopChannelMgr handles most of the details of multiplexing
the data. Subclasses are responsible for implementing the details of
interfacing with the transport for the underlying single data channel.
The CTSRDPServerChannelMgr creates a named pipe that
can be connected to by the TSRDP Assistant SessionVC Add-In. The TSRDP
Assistant Session VC Add-In acts as a proxy for virtual channel data
from the client-side Remote Desktop Host ActiveX Control. A background
thread in this class handles the movement of data between an instance
of this class and the proxy.
Author:
Tad Brockway 02/00
Revision History:
--*/
#include "stdafx.h"
#ifdef TRC_FILE
#undef TRC_FILE
#endif
#define TRC_FILE "_tsrdpscm"
#include "TSRDPServerDataChannelMgr.h"
#include <TSRDPRemoteDesktop.h>
#include "TSRDPRemoteDesktopSession.h"
#include <RemoteDesktopUtils.h>
#define INCOMINGBUFFER_RESIZEDELTA 1024
///////////////////////////////////////////////////////
//
// CTSRDPServerDataChannel Members
//
CTSRDPServerDataChannel::CTSRDPServerDataChannel()
/*++
Routine Description:
Constructor
Arguments:
Return Value:
None.
--*/
{
DC_BEGIN_FN("CTSRDPServerDataChannel::CTSRDPServerDataChannel");
TRC_NRM((TB, L"***Ref count is: %ld", m_dwRef));
DC_END_FN();
}
CTSRDPServerDataChannel::~CTSRDPServerDataChannel()
/*++
Routine Description:
Destructor
Arguments:
7
Return Value:
None.
--*/
{
DC_BEGIN_FN("CTSRDPServerDataChannel::~CTSRDPServerDataChannel");
//
// Notify the channel manager that we have gone away.
//
m_ChannelMgr->RemoveChannel(m_ChannelName);
DC_END_FN();
}
STDMETHODIMP
CTSRDPServerDataChannel::ReceiveChannelData(
BSTR *data
)
/*++
Routine Description:
Receive the next complete data packet on this channel.
Arguments:
data - The next data packet. Should be released by the
caller.
Return Value:
S_OK on success. Otherwise, an error result is returned.
--*/
{
HRESULT result;
DC_BEGIN_FN("CTSRDPServerDataChannel::ReceiveChannelData");
result = m_ChannelMgr->ReadChannelData(m_ChannelName, data);
DC_END_FN();
return result;
}
STDMETHODIMP
CTSRDPServerDataChannel::SendChannelData(
BSTR data
)
/*++
Routine Description:
Send data on this channel.
Arguments:
data - Data to send.
Return Value:
S_OK on success. Otherwise, an error result is returned.
--*/
{
HRESULT hr;
DC_BEGIN_FN("CTSRDPServerDataChannel::SendChannelData");
hr = m_ChannelMgr->SendChannelData(m_ChannelName, data);
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPServerDataChannel::put_OnChannelDataReady(
IDispatch * newVal
)
/*++
Routine Description:
SAFRemoteDesktopDataChannel Scriptable Event Object Registration
Properties
Arguments:
Return Value:
S_OK on success. Otherwise, an error status is returned.
--*/
{
DC_BEGIN_FN("CTSRDPServerDataChannel::put_OnChannelDataReady");
m_OnChannelDataReady = newVal;
DC_END_FN();
return S_OK;
}
STDMETHODIMP
CTSRDPServerDataChannel::get_ChannelName(
BSTR *pVal
)
/*++
Routine Description:
Return the channel name.
Arguments:
pVal - Returned channel name.
Return Value:
S_OK on success. Otherwise, an error status is returned.
--*/
{
DC_BEGIN_FN("CTSRDPServerDataChannel::get_ChannelName");
CComBSTR str;
str = m_ChannelName;
*pVal = str.Detach();
DC_END_FN();
return S_OK;
}
/*++
Routine Description:
Called when data is ready on our channel.
Arguments:
pVal - Returned channel name.
Return Value:
S_OK on success. Otherwise, an error status is returned.
--*/
VOID
CTSRDPServerDataChannel::DataReady()
{
DC_BEGIN_FN("CTSRDPServerDataChannel::DataReady");
//
// Fire our data ready event.
//
Fire_ChannelDataReady(m_ChannelName, m_OnChannelDataReady);
DC_END_FN();
}
///////////////////////////////////////////////////////
//
// CTSRDPServerChannelMgr Methods
//
CTSRDPServerChannelMgr::CTSRDPServerChannelMgr()
/*++
Routine Description:
Constructor
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::CTSRDPServerChannelMgr");
m_IOThreadBridge = NULL;
m_IOThreadBridgeThreadID = 0;
m_IOThreadBridgeStream = NULL;
m_VCAddInPipe = INVALID_HANDLE_VALUE;
m_Connected = FALSE;
m_ReadIOCompleteEvent = NULL;
m_WriteIOCompleteEvent = NULL;
m_PipeCreateEvent = NULL;
m_IncomingBufferSize = 0;
m_IncomingBuffer = NULL;
TRC_NRM((TB, L"***Ref count is: %ld", m_dwRef));
#if DBG
m_LockCount = 0;
#endif
m_IOThreadHndl = NULL;
//
// Initialize the critical section.
//
InitializeCriticalSection(&m_cs);
//
// Not valid, until initialized.
//
SetValid(FALSE);
DC_END_FN();
}
CTSRDPServerChannelMgr::~CTSRDPServerChannelMgr()
/*++
Routine Description:
Destructor
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::~CTSRDPServerChannelMgr");
//
// Make sure we are no longer listening for data.
//
StopListening();
//
// Close the read IO processing event.
//
if (m_ReadIOCompleteEvent != NULL) {
CloseHandle(m_ReadIOCompleteEvent);
m_ReadIOCompleteEvent = NULL;
}
//
// Close the write IO processing event.
//
if (m_WriteIOCompleteEvent != NULL) {
CloseHandle(m_WriteIOCompleteEvent);
m_WriteIOCompleteEvent = NULL;
}
//
// Release the incoming buffer.
//
if (m_IncomingBuffer != NULL) {
SysFreeString(m_IncomingBuffer);
m_IncomingBuffer = NULL;
}
if (m_PipeCreateEvent != NULL) {
CloseHandle(m_PipeCreateEvent);
m_PipeCreateEvent = NULL;
}
if( NULL != m_IOThreadHndl ) {
CloseHandle( m_IOThreadHndl );
m_IOThreadHndl = NULL;
}
//
// This should have been cleaned up in the background thread.
//
ASSERT(m_IOThreadBridge == NULL);
ASSERT(m_IOThreadBridgeStream == NULL);
DeleteCriticalSection(&m_cs);
DC_END_FN();
}
HRESULT
CTSRDPServerChannelMgr::Initialize(
CTSRDPRemoteDesktopSession *sessionObject,
BSTR helpSessionID
)
/*++
Routine Description:
Initialize an instance of this class.
Arguments:
sessionObject - Back pointer to the containing
session object.
Return Value:
S_OK is returned on success. Otherwise, an error code
is returned.
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::Initialize");
HRESULT result = ERROR_SUCCESS;
TRC_NRM((TB, L"***Ref count is: %ld", m_dwRef));
//
// Record help session id.
//
m_HelpSessionID = helpSessionID;
//
// Record the containing session object.
//
m_RDPSessionObject = sessionObject;
//
// Set the initial buffer size and buffer to be at least the
// size of a channel buffer header.
//
ASSERT(m_IncomingBuffer == NULL);
m_IncomingBuffer = SysAllocStringByteLen(
NULL,
INCOMINGBUFFER_RESIZEDELTA
);
if (m_IncomingBuffer == NULL) {
result = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto CLEANUPANDEXIT;
}
m_IncomingBufferSize = INCOMINGBUFFER_RESIZEDELTA;
//
// Create the read IO processing event.
//
ASSERT(m_ReadIOCompleteEvent == NULL);
m_ReadIOCompleteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_ReadIOCompleteEvent == NULL) {
result = HRESULT_FROM_WIN32(GetLastError());
TRC_ERR((TB, L"CreateEvent: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Create the write IO processing event.
//
ASSERT(m_WriteIOCompleteEvent == NULL);
m_WriteIOCompleteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_WriteIOCompleteEvent == NULL) {
result = HRESULT_FROM_WIN32(GetLastError());
TRC_ERR((TB, L"CreateEvent: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Create the named pipe create event
//
ASSERT(m_PipeCreateEvent == NULL);
m_PipeCreateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_PipeCreateEvent == NULL) {
result = HRESULT_FROM_WIN32(GetLastError());
TRC_ERR((TB, L"CreateEvent: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Initialize the parent class.
//
result = CRemoteDesktopChannelMgr::Initialize();
if (result != S_OK) {
goto CLEANUPANDEXIT;
}
//
// We are valid, if we made it here.
//
SetValid(TRUE);
TRC_NRM((TB, L"***Ref count is: %ld", m_dwRef));
CLEANUPANDEXIT:
DC_END_FN();
return result;
}
VOID
CTSRDPServerChannelMgr::ClosePipe()
/*++
Routine Description:
Close the named pipe.
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::ClosePipe");
ASSERT(m_VCAddInPipe != INVALID_HANDLE_VALUE);
FlushFileBuffers(m_VCAddInPipe);
//
// reset the pipe creation event so that the foreground
// thread can wait for the next help session
//
ResetEvent(m_PipeCreateEvent);
DisconnectNamedPipe(m_VCAddInPipe);
CloseHandle(m_VCAddInPipe);
m_VCAddInPipe = INVALID_HANDLE_VALUE;
m_Connected = FALSE;
DC_END_FN();
}
HRESULT
CTSRDPServerChannelMgr::StartListening(
BSTR assistAccount
)
/*++
Routine Description:
Start listening for data channel data.
Arguments:
assistAccount - Name of machine assistant account.
Return Value:
S_OK is returned on success. Otherwise, an error code
is returned.
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::StartListening");
HRESULT hr = S_OK;
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
//
// If the background thread is still active, then fail. This
// means that it is still trying to shut down.
//
if (m_IOThreadHndl != NULL) {
if (WaitForSingleObject(m_IOThreadHndl, 0) == WAIT_OBJECT_0) {
CloseHandle( m_IOThreadHndl );
m_IOThreadHndl = NULL;
}
else {
TRC_ERR((TB, L"Background thread not shut down, yet: %08X.",
GetLastError()));
hr = HRESULT_FROM_WIN32(ERROR_ACTIVE_CONNECTIONS);
goto CLEANUPANDEXIT;
}
}
//
// Make the thread bridge interface available to the background thread.
//
hr = CoMarshalInterThreadInterfaceInStream(
IID_IRDSThreadBridge,
(ISAFRemoteDesktopChannelMgr*)this,
&m_IOThreadBridgeStream
);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, TEXT("CoMarshalInterThreadInterfaceInStream: %08X"), hr));
goto CLEANUPANDEXIT;
}
//
// Reset the connected flag.
//
m_Connected = FALSE;
//
// Record the machine assistant account name.
//
ASSERT(assistAccount != NULL);
m_AssistAccount = assistAccount;
//
// Reset the read IO processing event.
//
ASSERT(m_ReadIOCompleteEvent != NULL);
ResetEvent(m_ReadIOCompleteEvent);
//
// Reset the write IO processing event.
//
ASSERT(m_WriteIOCompleteEvent != NULL);
ResetEvent(m_WriteIOCompleteEvent);
//
//reset the named pipe creation event
ASSERT(m_PipeCreateEvent != NULL);
ResetEvent(m_PipeCreateEvent);
//
// Create the background thread that receives data from the
// named pipe.
//
ASSERT(m_IOThreadHndl == NULL);
m_IOThreadHndl = CreateThread(
NULL, 0,
(LPTHREAD_START_ROUTINE)_IOThread,
this,
0,&m_IOThreadID
);
if (m_IOThreadHndl == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
TRC_ERR((TB, TEXT("CreateThread: %08X"), hr));
goto CLEANUPANDEXIT;
}
//
//wait for the named pipe creation event to be signaled
//and check for the result. If failed, bail
//
WaitForSingleObject(m_PipeCreateEvent, INFINITE);
//
//set the error to pipe_busy because we assume that someone else
//has already created it and that is the reason the CreateNamedPipe call failed.
//
if (m_VCAddInPipe == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(ERROR_PIPE_BUSY);
TRC_ERR((TB, L"CreateNamedPipe returned fail"));
goto CLEANUPANDEXIT;
}
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
HRESULT
CTSRDPServerChannelMgr::StopListening()
/*++
Routine Description:
Stop listening for data channel data.
Arguments:
Return Value:
S_OK is returned on success. Otherwise, an error code
is returned.
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::StopListening");
DWORD waitResult;
//
// Close the named pipe.
//
ThreadLock();
if (m_VCAddInPipe != INVALID_HANDLE_VALUE) {
ClosePipe();
}
ThreadUnlock();
TRC_NRM((TB, L"***Ref count is: %ld", m_dwRef));
DC_END_FN();
return S_OK;
}
HRESULT
CTSRDPServerChannelMgr::SendData(
PREMOTEDESKTOP_CHANNELBUFHEADER msg
)
/*++
Routine Description:
Send Function Invoked by Parent Class
Arguments:
msg - Message data. Note that the underlying representation
for this data structure is a BSTR so that it is compatible
with COM methods.
Return Value:
S_OK is returned on success. Otherwise, an error code
is returned.
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::SendData");
HRESULT result = S_OK;
DWORD bytesWritten;
OVERLAPPED ol;
DWORD msgLen;
if (!IsValid()) {
ASSERT(FALSE);
result = E_FAIL;
goto CLEANUPANDEXIT;
}
if (m_Connected) {
//
// Write the header.
//
memset(&ol, 0, sizeof(ol));
ol.hEvent = m_WriteIOCompleteEvent;
ResetEvent(ol.hEvent);
BOOL ret = WriteFile(
m_VCAddInPipe,
msg,
sizeof(REMOTEDESKTOP_CHANNELBUFHEADER),
NULL,
&ol
);
if (ret || (!ret && (GetLastError() == ERROR_IO_PENDING))) {
ret = GetOverlappedResult(
m_VCAddInPipe,
&ol,
&bytesWritten,
TRUE
);
}
if (!ret) {
result = HRESULT_FROM_WIN32(GetLastError());
TRC_ALT((TB, TEXT("Header write failed: %08X"), result));
goto CLEANUPANDEXIT;
}
ASSERT(bytesWritten == sizeof(REMOTEDESKTOP_CHANNELBUFHEADER));
//
// Write the rest of the message.
//
msgLen = msg->dataLen + msg->channelNameLen;
memset(&ol, 0, sizeof(ol));
ol.hEvent = m_WriteIOCompleteEvent;
ResetEvent(ol.hEvent);
ret = WriteFile(
m_VCAddInPipe,
(PBYTE)(msg+1),
msgLen,
NULL,
&ol
);
if (ret || (!ret && (GetLastError() == ERROR_IO_PENDING))) {
ret = GetOverlappedResult(
m_VCAddInPipe,
&ol,
&bytesWritten,
TRUE
);
}
if (!ret) {
result = HRESULT_FROM_WIN32(GetLastError());
TRC_ALT((TB, TEXT("Message write failed: %08X"), result));
goto CLEANUPANDEXIT;
}
ASSERT(bytesWritten == msgLen);
}
else {
result = HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE);
}
CLEANUPANDEXIT:
//
// If there was an error, we should close the pipe so it
// can be reopened in the background thread.
//
if (result != S_OK) {
ThreadLock();
if (m_VCAddInPipe != INVALID_HANDLE_VALUE) {
ClosePipe();
}
ThreadUnlock();
}
DC_END_FN();
return result;
}
DWORD
CTSRDPServerChannelMgr::IOThread()
/*++
Routine Description:
Background Thread Managing Named Pipe Connection to the
TSRDP Assistant SessionVC Add-In.
Arguments:
Return Value:
Returns 0
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::IOThread");
DWORD result;
DWORD lastError;
WCHAR pipePath[MAX_PATH+1];
OVERLAPPED ol;
DWORD waitResult;
WCHAR pipeName[MAX_PATH];
PSECURITY_ATTRIBUTES pipeAttribs = NULL;
//
// Notify the parent class that the IO thread is being initialized.
//
result = IOThreadInit();
if (result != ERROR_SUCCESS) {
goto CLEANUPANDEXIT;
}
//
// Get the security descriptor for the named pipe.
//
pipeAttribs = GetPipeSecurityAttribs(m_AssistAccount);
if (pipeAttribs == NULL) {
result = GetLastError();
goto CLEANUPANDEXIT;
}
lastError = ERROR_SUCCESS;
ASSERT(!m_Connected);
//
// Handle connections by the TSRDP Assistant SessionVC Add-In
// until we are supposed to shut down.
//
ASSERT(m_VCAddInPipe == INVALID_HANDLE_VALUE);
ASSERT(!m_Connected);
wsprintf(pipeName, L"%s-%s", TSRDPREMOTEDESKTOP_PIPENAME, m_HelpSessionID);
wsprintf(pipePath, L"\\\\.\\pipe\\%s", pipeName);
m_VCAddInPipe = CreateNamedPipe(
pipePath,
PIPE_ACCESS_DUPLEX |
FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
1,
TSRDPREMOTEDESKTOP_PIPEBUFSIZE,
TSRDPREMOTEDESKTOP_PIPEBUFSIZE,
TSRDPREMOTEDESKTOP_PIPETIMEOUT,
pipeAttribs
);
//
//signal the foreground thread about the pipe creation result
//
SetEvent(m_PipeCreateEvent);
if (m_VCAddInPipe == INVALID_HANDLE_VALUE) {
lastError = GetLastError();
TRC_ERR((TB, TEXT("CreatePipe: %08X. Shutting down background thread."),
lastError));
goto CLEANUPANDEXIT;
}
//
// Wait for the TSRDP Assistant SesionVC Add-In to connect.
// If it succeeds, the function returns a nonzero value. If the
// function returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
//
memset(&ol, 0, sizeof(ol));
ol.hEvent = m_ReadIOCompleteEvent;
ResetEvent(ol.hEvent);
if (!ConnectNamedPipe(m_VCAddInPipe, &ol) && (GetLastError() == ERROR_IO_PENDING)) {
TRC_NRM((TB, L"Waiting for connect."));
//
// Wait for the connect event to fire.
//
waitResult = WaitForSingleObject(m_ReadIOCompleteEvent, INFINITE);
if (waitResult != WAIT_OBJECT_0)
{
m_Connected = FALSE;
}
//
// Otherwise, if the io complete event fired.
//
else
{
//
// If the io complete event fired.
//
TRC_NRM((TB, L"Connect event signaled."));
DWORD ignored;
m_Connected = GetOverlappedResult(m_VCAddInPipe, &ol, &ignored, TRUE);
if (!m_Connected) {
lastError = GetLastError();
TRC_ERR((TB, L"GetOverlappedResult: %08X", lastError));
}
else {
TRC_NRM((TB, L"Connection established."));
}
}
} //!ConnectNamedPipe
else if (GetLastError() == ERROR_PIPE_CONNECTED) {
TRC_NRM((TB, L"Connected without pending."));
m_Connected = TRUE;
}
else {
lastError = GetLastError();
TRC_ERR((TB, L"ConnectNamedPipe: %08X", lastError));
}
//
// If we got a valid connection, process reads until the pipe is
// disconnected, after notifying the parent class that we have
// a valid connection.
//
if (m_Connected) {
//
// Notify the foreground thread that the client connected.
//
m_IOThreadBridge->ClientConnectedNotify();
ProcessPipeMessagesUntilDisconnect();
//
// Notify the foreground thread that the the client has disconnected.
//
m_IOThreadBridge->ClientDisconnectedNotify();
}
CLEANUPANDEXIT:
//
// Close the pipe if it is still open.
//
ThreadLock();
if (m_VCAddInPipe != INVALID_HANDLE_VALUE) {
ClosePipe();
}
ThreadUnlock();
//
// Clean up the named pipe security attribs.
//
if (pipeAttribs != NULL) {
FreePipeSecurityAttribs(pipeAttribs);
}
//
// Notify the parent class that the IO thread is shutting down.
// The parent class will signal this event when the class is completely
// shut down.
//
result = IOThreadShutdown(NULL);
DC_END_FN();
return result;
}
DWORD CTSRDPServerChannelMgr::_IOThread(
CTSRDPServerChannelMgr *instance
)
{
return instance->IOThread();
}
VOID
CTSRDPServerChannelMgr::ProcessPipeMessagesUntilDisconnect()
/*++
Routine Description:
Process messages on the named pipe until it disconnects or
until the shutdown flag is set.
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::ProcessPipeMessagesUntilDisconnect");
DWORD bytesRead;
DWORD result;
PREMOTEDESKTOP_CHANNELBUFHEADER hdr;
DWORD msgLen;
//
// Loop until the connection is terminated or we are to shut down.
//
while (m_Connected) {
//
// Read the next buffer header.
//
ASSERT(m_IncomingBufferSize >= sizeof(REMOTEDESKTOP_CHANNELBUFHEADER));
result = ReadNextPipeMessage(
sizeof(REMOTEDESKTOP_CHANNELBUFHEADER),
&bytesRead,
(PBYTE)m_IncomingBuffer
);
if ((result != ERROR_SUCCESS) && (result != ERROR_MORE_DATA)) {
break;
}
ASSERT(bytesRead == sizeof(REMOTEDESKTOP_CHANNELBUFHEADER));
hdr = (PREMOTEDESKTOP_CHANNELBUFHEADER)m_IncomingBuffer;
#ifdef USE_MAGICNO
ASSERT(hdr->magicNo == CHANNELBUF_MAGICNO);
#endif
//
// Size the incoming buffer.
//
msgLen = hdr->dataLen + hdr->channelNameLen;
if (m_IncomingBufferSize < msgLen) {
DWORD sz = msgLen + sizeof(REMOTEDESKTOP_CHANNELBUFHEADER);
m_IncomingBuffer = (BSTR)ReallocBSTR(
m_IncomingBuffer, sz
);
if (m_IncomingBuffer != NULL) {
hdr = (PREMOTEDESKTOP_CHANNELBUFHEADER)m_IncomingBuffer;
m_IncomingBufferSize = sz;
}
else {
TRC_ERR((TB, L"Can't resize %ld bytes for incoming buffer.",
m_IncomingBufferSize + INCOMINGBUFFER_RESIZEDELTA));
m_IncomingBufferSize = 0;
break;
}
}
//
// Read the buffer data.
//
result = ReadNextPipeMessage(
msgLen,
&bytesRead,
((PBYTE)m_IncomingBuffer) + sizeof(REMOTEDESKTOP_CHANNELBUFHEADER)
);
if (result != ERROR_SUCCESS) {
break;
}
ASSERT(bytesRead == msgLen);
//
// Process the complete buffer in the foreground thread.
//
m_IOThreadBridge->DataReadyNotify(m_IncomingBuffer);
}
//
// We are here because something went wrong and we should disconnect.
//
ThreadLock();
if (m_VCAddInPipe != INVALID_HANDLE_VALUE) {
ClosePipe();
}
ThreadUnlock();
DC_END_FN();
}
DWORD
CTSRDPServerChannelMgr::ReadNextPipeMessage(
IN DWORD bytesToRead,
OUT DWORD *bytesRead,
IN PBYTE buf
)
/*++
Abstract:
Read the next message from the pipe.
Parameter:
bytesToRead - Number of bytes to read.
bytesRead - Number of bytes read.
buf - Buffer for data read.
Returns:
ERROR_SUCCESS on success. Otherwise, a windows error code is
returned.
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::ReadNextPipeMessage");
OVERLAPPED ol;
BOOL result;
DWORD lastError;
DWORD waitResult;
memset(&ol, 0, sizeof(ol));
ol.hEvent = m_ReadIOCompleteEvent;
ResetEvent(ol.hEvent);
lastError = ERROR_SUCCESS;
result = ReadFile(m_VCAddInPipe, buf, bytesToRead, bytesRead, &ol);
if (!result) {
//
// If IO is pending.
//
lastError = GetLastError();
if (lastError == ERROR_IO_PENDING) {
//
// Wait for the read to finish and for the shutdown event to fire.
//
waitResult = WaitForSingleObject(m_ReadIOCompleteEvent, INFINITE);
if (waitResult == WAIT_OBJECT_0)
{
if (GetOverlappedResult(m_VCAddInPipe, &ol, bytesRead, TRUE)) {
lastError = ERROR_SUCCESS;
}
else {
lastError = GetLastError();
TRC_ALT((TB, L"GetOverlappedResult: %08X", lastError));
}
}
else {
lastError = GetLastError();
TRC_NRM((TB, L"WaitForSingleObject failed : %08x", lastError));
}
}
}
else {
lastError = ERROR_SUCCESS;
}
DC_END_FN();
return lastError;
}
PSECURITY_ATTRIBUTES
CTSRDPServerChannelMgr::GetPipeSecurityAttribs(
IN LPTSTR assistantUserName
)
/*++
Abstract:
Returns the security attribs for the named pipe.
Parameter:
assistantUserName - Machine assistant user account.
Returns:
NULL on error. Otherwise, the security attribs are returned
and should be freed via call to FREEMEM. On error, GetLastError()
can be used to get extended error information.
--*/
{
PACL pAcl=NULL;
DWORD sidSz;
DWORD domainSz;
PSID pCurrentUserSid = NULL;
PSID pHelpAssistantSid = NULL;
DWORD result = ERROR_SUCCESS;
PSECURITY_ATTRIBUTES attribs = NULL;
SID_NAME_USE sidNameUse;
DWORD aclSz;
HANDLE userToken = NULL;
WCHAR *domainName = NULL;
DC_BEGIN_FN("CTSRDPServerChannelMgr::GetPipeSecurityAttribs");
//
// Allocate the security attributes.
//
attribs = (PSECURITY_ATTRIBUTES)ALLOCMEM(sizeof(SECURITY_ATTRIBUTES));
if (attribs == NULL) {
TRC_ERR((TB, L"Can't allocate security attribs."));
result = ERROR_NOT_ENOUGH_MEMORY;
goto CLEANUPANDEXIT;
}
memset(attribs, 0, sizeof(SECURITY_ATTRIBUTES));
//
// Allocate the security descriptor.
//
attribs->lpSecurityDescriptor = ALLOCMEM(sizeof(SECURITY_DESCRIPTOR));
if (attribs->lpSecurityDescriptor == NULL) {
TRC_ERR((TB, L"Can't allocate SD"));
result = ERROR_NOT_ENOUGH_MEMORY;
goto CLEANUPANDEXIT;
}
//
// Initialize the security descriptor.
//
if (!InitializeSecurityDescriptor(
attribs->lpSecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION
)) {
result = GetLastError();
TRC_ERR((TB, L"InitializeSecurityDescriptor: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Get the token for the current process.
//
if (!OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY,
&userToken
)) {
result = GetLastError();
TRC_ERR((TB, L"OpenProcessToken: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Get the SID for the current user.
//
pCurrentUserSid = GetUserSid(userToken);
if (pCurrentUserSid == NULL) {
result = GetLastError();
goto CLEANUPANDEXIT;
}
//
// Get the size for the assistant account user SID.
//
domainSz = 0; sidSz = 0;
if (!LookupAccountName(NULL, assistantUserName, NULL,
&sidSz, NULL, &domainSz, &sidNameUse
) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
result = GetLastError();
TRC_ERR((TB, L"LookupAccountName: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Allocate the SID.
//
pHelpAssistantSid = (PSID)ALLOCMEM(sidSz);
if (pHelpAssistantSid == NULL) {
TRC_ERR((TB, L"Can't allocate help asistant SID."));
result = ERROR_NOT_ENOUGH_MEMORY;
goto CLEANUPANDEXIT;
}
//
// Allocate the domain name.
//
domainName = (WCHAR *)ALLOCMEM(domainSz * sizeof(WCHAR));
if (domainName == NULL) {
TRC_ERR((TB, L"Can't allocate domain"));
result = ERROR_NOT_ENOUGH_MEMORY;
goto CLEANUPANDEXIT;
}
//
// Get the assistant account SID.
//
if (!LookupAccountName(NULL, assistantUserName, pHelpAssistantSid,
&sidSz, domainName, &domainSz, &sidNameUse
)) {
result = GetLastError();
TRC_ERR((TB, L"LookupAccountName: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Allocate space for the ACL.
//
aclSz = GetLengthSid(pCurrentUserSid) +
GetLengthSid(pHelpAssistantSid) +
sizeof(ACL) +
(2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));
pAcl = (PACL)ALLOCMEM(aclSz);
if(pAcl == NULL) {
TRC_ERR((TB, L"Can't allocate ACL"));
result = ERROR_NOT_ENOUGH_MEMORY;
goto CLEANUPANDEXIT;
}
//
// Initialize the ACL.
//
if (!InitializeAcl(pAcl, aclSz, ACL_REVISION)) {
result = GetLastError();
TRC_ERR((TB, L"InitializeACL: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Add the current user ace.
//
if (!AddAccessAllowedAce(pAcl,
ACL_REVISION,
GENERIC_READ | GENERIC_WRITE | GENERIC_ALL,
pCurrentUserSid
)) {
result = GetLastError();
TRC_ERR((TB, L"AddAccessAllowedAce: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Add the help assistant ace.
//
if (!AddAccessAllowedAce(pAcl,
ACL_REVISION,
GENERIC_READ | GENERIC_WRITE | GENERIC_ALL,
pHelpAssistantSid
)) {
result = GetLastError();
TRC_ERR((TB, L"AddAccessAllowedAce: %08X", result));
goto CLEANUPANDEXIT;
}
//
// Set the secrity descriptor discretionary ACL.
//
if (!SetSecurityDescriptorDacl(attribs->lpSecurityDescriptor,
TRUE, pAcl, FALSE)) {
result = GetLastError();
TRC_ERR((TB, L"SetSecurityDescriptorDacl: %08X", result));
goto CLEANUPANDEXIT;
}
CLEANUPANDEXIT:
if (pCurrentUserSid != NULL) {
FREEMEM(pCurrentUserSid);
}
if (pHelpAssistantSid != NULL) {
FREEMEM(pHelpAssistantSid);
}
if (domainName != NULL) {
FREEMEM(domainName);
}
if( userToken != NULL ) {
CloseHandle( userToken );
}
//
// Clean up on error.
//
if (result != ERROR_SUCCESS) {
if (attribs != NULL) {
FreePipeSecurityAttribs(attribs);
attribs = NULL;
}
}
SetLastError(result);
DC_END_FN();
return attribs;
}
VOID
CTSRDPServerChannelMgr::FreePipeSecurityAttribs(
PSECURITY_ATTRIBUTES attribs
)
/*++
Abstract:
Release security attribs allocated via a call to GetPipeSecurityAttribs
Parameter:
attribs - Attribs returned by GetPipeSecurityAttribs.
Returns:
--*/
{
BOOL daclPresent;
PACL pDacl = NULL;
BOOL daclDefaulted;
DC_BEGIN_FN("CTSRDPServerChannelMgr::FreePipeSecurityAttribs");
ASSERT(attribs != NULL);
if (attribs->lpSecurityDescriptor) {
if (GetSecurityDescriptorDacl(
attribs->lpSecurityDescriptor,
&daclPresent,
&pDacl,
&daclDefaulted
)) {
ASSERT(!daclDefaulted);
if (pDacl != NULL) {
FREEMEM(pDacl);
}
}
FREEMEM(attribs->lpSecurityDescriptor);
}
FREEMEM(attribs);
DC_END_FN();
}
PSID
CTSRDPServerChannelMgr::GetUserSid(
IN HANDLE userToken
)
/*++
Routine Description:
Get the SID for a particular user.
Arguments:
Access Token for the User
Return Value:
The PSID if successful. Otherwise, NULL is returned and
GetLastError can be used to retrieve the windows error code.
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::GetUserSid");
TOKEN_USER * ptu = NULL;
BOOL bResult;
PSID psid = NULL;
DWORD defaultSize = sizeof(TOKEN_USER);
DWORD size;
DWORD result = ERROR_SUCCESS;
ptu = (TOKEN_USER *)ALLOCMEM(defaultSize);
if (ptu == NULL) {
goto CLEANUPANDEXIT;
}
//
// Get information about the user token.
//
bResult = GetTokenInformation(
userToken,
TokenUser,
ptu,
defaultSize,
&size
);
if (bResult == FALSE) {
result = GetLastError();
if (result == ERROR_INSUFFICIENT_BUFFER) {
//
// sAllocate required memory
//
FREEMEM(ptu);
ptu = (TOKEN_USER *)ALLOCMEM(size);
if (ptu == NULL) {
TRC_ERR((TB, L"Can't allocate user token."));
result = ERROR_NOT_ENOUGH_MEMORY;
goto CLEANUPANDEXIT;
}
else {
defaultSize = size;
bResult = GetTokenInformation(
userToken,
TokenUser,
ptu,
defaultSize,
&size
);
if (bResult == FALSE) {
result = GetLastError();
TRC_ERR((TB, L"GetTokenInformation: %08X", result));
goto CLEANUPANDEXIT;
}
}
}
else {
TRC_ERR((TB, L"GetTokenInformation: %08X", result));
goto CLEANUPANDEXIT;
}
}
//
// Get the length of the SID.
//
size = GetLengthSid(ptu->User.Sid);
//
// Allocate memory. This will be freed by the caller.
//
psid = (PSID)ALLOCMEM(size);
if (psid != NULL) {
CopySid(size, psid, ptu->User.Sid);
}
else {
TRC_ERR((TB, L"Can't allocate SID"));
result = ERROR_NOT_ENOUGH_MEMORY;
goto CLEANUPANDEXIT;
}
CLEANUPANDEXIT:
if (ptu != NULL) {
FREEMEM(ptu);
}
SetLastError(result);
DC_END_FN();
return psid;
}
DWORD
CTSRDPServerChannelMgr::IOThreadInit()
/*++
Routine Description:
Called on Init of Background Thread
Arguments:
Return Value:
Returns ERROR_SUCCESS on success. Otherwise, an error code
is returned.
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::IOThreadInit");
HRESULT hr;
DWORD result = ERROR_SUCCESS;
if (!IsValid()) {
ASSERT(FALSE);
return E_FAIL;
}
//
// We only allow one IO thread.
//
ASSERT(m_IOThreadBridgeThreadID == 0);
m_IOThreadBridgeThreadID = GetCurrentThreadId();
//
// Need to define the apartment for this thread as STA.
//
hr = CoInitialize(NULL);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, TEXT("CoInitializeEx: %08X"), hr));
result = E_FAIL;
goto CLEANUPANDEXIT;
}
//
// Grab the thread bridge
//
hr = CoGetInterfaceAndReleaseStream(
m_IOThreadBridgeStream,
IID_IRDSThreadBridge,
(PVOID*)&m_IOThreadBridge
);
if (SUCCEEDED(hr)) {
m_IOThreadBridgeStream = NULL;
}
else {
TRC_ERR((TB, TEXT("CoGetInterfaceAndReleaseStream: %08X"), hr));
result = E_FAIL;
goto CLEANUPANDEXIT;
}
CLEANUPANDEXIT:
if (!SUCCEEDED(hr)) {
m_IOThreadBridgeThreadID = 0;
}
DC_END_FN();
return result;
}
DWORD
CTSRDPServerChannelMgr::IOThreadShutdown(
HANDLE shutDownEvent
)
/*++
Routine Description:
Called on Shutdown of Background Thread
Arguments:
shutDownEvent - We need to signal this when we are completely
done shutting down.
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::IOThreadShutdown");
IRDSThreadBridge *tmp;
//
// Make sure the init CB was called and succeded.
//
ASSERT(m_IOThreadBridgeThreadID != 0);
m_IOThreadBridgeThreadID = 0;
//
// Get a reference to the thread interface bridge so the foreground
// thread doesn't try to whack it when we signal the event, indicating
// that the background thread has completely shut down.
//
tmp = m_IOThreadBridge;
m_IOThreadBridge = NULL;
//
// Signal that the thread is shut down, completely.
//
if (shutDownEvent != NULL) {
SetEvent(shutDownEvent);
}
//
// Decrement the ref count on the IO thread bridge. This may cause the
// COM object that contains us to go away, so we need to do this,
// carefully, as the last thing before shutting down COM for this thread.
//
if (tmp != NULL) {
tmp->Release();
}
CoUninitialize();
DC_END_FN();
return ERROR_SUCCESS;
}
STDMETHODIMP
CTSRDPServerChannelMgr::ClientConnectedNotify()
/*++
Routine Description:
This function is implemented for the IRDSThreadBridge interface and
is called by the background thread when a client connects. This
function, in turn, notifies the containing Remote Desktop Session class.
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::ClientConnectedNotify");
m_RDPSessionObject->ClientConnected();
DC_END_FN();
return S_OK;
}
STDMETHODIMP
CTSRDPServerChannelMgr::ClientDisconnectedNotify()
/*++
Routine Description:
This function is implemented for the IRDSThreadBridge interface and
is called by the background thread when a client disconnects. This
function, in turn, notifies the containing Remote Desktop Session class.
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::ClientDisconnectedNotify");
m_RDPSessionObject->ClientDisconnected();
DC_END_FN();
return S_OK;
}
STDMETHODIMP
CTSRDPServerChannelMgr::DataReadyNotify(
BSTR data
)
/*++
Routine Description:
This function is implemented for the IRDSThreadBridge interface and
is called by the background thread when new data is received. This
function, in turn, notifies the parent class.
Arguments:
data - New data.
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPServerChannelMgr::ClientDisconnectedNotify");
CRemoteDesktopChannelMgr::DataReady(data);
DC_END_FN();
return S_OK;
}