/*++ 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 #include "TSRDPRemoteDesktopSession.h" #include #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; }