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.
3304 lines
83 KiB
3304 lines
83 KiB
/*++
|
|
|
|
Copyright (c) 1999-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
TSRDPRemoteDesktopClient
|
|
|
|
Abstract:
|
|
|
|
This is the TS/RDP implementation of the Remote Desktop Client class.
|
|
|
|
The Remote Desktop Client class hierarchy provides a pluggable C++
|
|
interface for remote desktop access, by abstracting the implementation
|
|
specific details of remote desktop access for the client-side
|
|
|
|
The TSRDPRemoteDesktopClass implements remote-desktopping
|
|
with the help of an instance of the MSTSC ActiveX client control.
|
|
|
|
Author:
|
|
|
|
Tad Brockway 02/00
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#ifdef TRC_FILE
|
|
#undef TRC_FILE
|
|
#endif
|
|
|
|
#define TRC_FILE "_tsrdpc"
|
|
|
|
#include "RDCHost.h"
|
|
#include "TSRDPRemoteDesktopClient.h"
|
|
#include <RemoteDesktopChannels.h>
|
|
#include <mstsax_i.c>
|
|
#include <TSRDPRemoteDesktop.h>
|
|
#include <Security.h>
|
|
#include "pchannel.h"
|
|
#include <tsremdsk.h>
|
|
#include <sessmgr.h>
|
|
#include <sessmgr_i.c>
|
|
#include <regapi.h>
|
|
#include "parseaddr.h"
|
|
#include "icshelpapi.h"
|
|
#include <tsperf.h>
|
|
#include "base64.h"
|
|
#include "RAEventMsg.h"
|
|
|
|
#define ISRCSTATUSCODE(code) ((code) > SAFERROR_SHADOWEND_BASE)
|
|
|
|
//
|
|
// Variable to manage WinSock and ICS library startup/shutdown
|
|
//
|
|
LONG CTSRDPRemoteDesktopClient::gm_ListeningLibraryRefCount = 0; // Number of time that WinSock is intialized
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::InitListeningLibrary()
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Function to initialize WinSock and ICS library for StartListen(), function add
|
|
reference count to library if WinSock/ICS library already initialized.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code.
|
|
|
|
--*/
|
|
{
|
|
WSADATA wsaData;
|
|
WORD versionRequested;
|
|
INT intRC;
|
|
DWORD dwStatus;
|
|
HRESULT hr = S_OK;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::InitListeningLibrary");
|
|
|
|
|
|
// Our COM object is apartment-threaded model, need a critical section if
|
|
// we switch to multi-threaded
|
|
if( gm_ListeningLibraryRefCount == 0 )
|
|
{
|
|
//
|
|
// Initialize WinSock.
|
|
//
|
|
versionRequested = MAKEWORD(1, 1);
|
|
intRC = WSAStartup(versionRequested, &wsaData);
|
|
if( intRC != 0 )
|
|
{
|
|
intRC = WSAGetLastError();
|
|
|
|
TRC_ERR((TB, _T("WSAStartup failed %d"), intRC));
|
|
TRC_ASSERT( (intRC == 0), (TB, _T("WSAStartup failed...\n")) );
|
|
|
|
hr = HRESULT_FROM_WIN32( intRC );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now confirm that this WinSock supports version 1.1. Note that if */
|
|
/* the DLL supports versions greater than 1.1 in addition to 1.1 then */
|
|
/* it will still return 1.1 in the version information as that is the */
|
|
/* version requested. */
|
|
/************************************************************************/
|
|
if ((LOBYTE(wsaData.wVersion) != 1) ||
|
|
(HIBYTE(wsaData.wVersion) != 1))
|
|
{
|
|
/********************************************************************/
|
|
/* Oops - this WinSock doesn't support version 1.1. */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, _T("WinSock doesn't support version 1.1")));
|
|
|
|
WSACleanup();
|
|
|
|
hr = HRESULT_FROM_WIN32( WSAVERNOTSUPPORTED );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Initialize ICS library.
|
|
//
|
|
dwStatus = StartICSLib();
|
|
if( ERROR_SUCCESS != dwStatus )
|
|
{
|
|
// Shutdown WinSock so that we have a matching WSAStatup() and StartICSLib().
|
|
WSACleanup();
|
|
|
|
hr = HRESULT_FROM_WIN32( dwStatus );
|
|
|
|
TRC_ERR((TB, _T("StartICSLib() failed with %d"), dwStatus));
|
|
TRC_ASSERT( (ERROR_SUCCESS == dwStatus), (TB, _T("StartICSLib() failed...\n")) );
|
|
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
}
|
|
|
|
InterlockedIncrement( &gm_ListeningLibraryRefCount );
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::TerminateListeningLibrary()
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Function to shutdown ICS libaray and WinSock, decrement reference count
|
|
if more than one object is referencing WinSock/ICS library.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code
|
|
|
|
Note:
|
|
|
|
Not multi-thread safe, need CRITICAL_SECTION if we switch to multi-threaded
|
|
model.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::TerminateListeningLibrary");
|
|
|
|
|
|
ASSERT( gm_ListeningLibraryRefCount > 0 );
|
|
if( gm_ListeningLibraryRefCount <= 0 )
|
|
{
|
|
TRC_ERR((TB, _T("TerminateListeningLibrary() called before InitListeningLibrary()")));
|
|
|
|
hr = HRESULT_FROM_WIN32(WSANOTINITIALISED);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
|
|
if( 0 == InterlockedDecrement( &gm_ListeningLibraryRefCount ) )
|
|
{
|
|
// Stop ICS libray.
|
|
StopICSLib();
|
|
|
|
// Shutdown WinSock
|
|
WSACleanup();
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
//
|
|
// CMSTSCClientEventSink Methods
|
|
//
|
|
|
|
CMSTSCClientEventSink::~CMSTSCClientEventSink()
|
|
{
|
|
DC_BEGIN_FN("CMSTSCClientEventSink::~CMSTSCClientEventSink");
|
|
|
|
if (m_Obj) {
|
|
ASSERT(m_Obj->IsValid());
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
//
|
|
// Event Sinks
|
|
//
|
|
HRESULT __stdcall
|
|
CMSTSCClientEventSink::OnRDPConnected()
|
|
{
|
|
m_Obj->OnRDPConnected();
|
|
return S_OK;
|
|
}
|
|
HRESULT __stdcall
|
|
CMSTSCClientEventSink::OnLoginComplete()
|
|
{
|
|
m_Obj->OnLoginComplete();
|
|
return S_OK;
|
|
}
|
|
HRESULT __stdcall
|
|
CMSTSCClientEventSink::OnDisconnected(
|
|
long disconReason
|
|
)
|
|
{
|
|
m_Obj->OnDisconnected(disconReason);
|
|
return S_OK;
|
|
}
|
|
void __stdcall CMSTSCClientEventSink::OnReceiveData(
|
|
BSTR chanName,
|
|
BSTR data
|
|
)
|
|
{
|
|
m_Obj->OnMSTSCReceiveData(data);
|
|
}
|
|
void __stdcall CMSTSCClientEventSink::OnReceivedTSPublicKey(
|
|
BSTR publicKey,
|
|
VARIANT_BOOL* pfbContinueLogon
|
|
)
|
|
{
|
|
m_Obj->OnReceivedTSPublicKey(publicKey, pfbContinueLogon);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
//
|
|
// CCtlChannelEventSink Methods
|
|
//
|
|
|
|
CCtlChannelEventSink::~CCtlChannelEventSink()
|
|
{
|
|
DC_BEGIN_FN("CCtlChannelEventSink::~CCtlChannelEventSink");
|
|
|
|
if (m_Obj) {
|
|
ASSERT(m_Obj->IsValid());
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
//
|
|
// Event Sinks
|
|
//
|
|
void __stdcall
|
|
CCtlChannelEventSink::DataReady(BSTR channelName)
|
|
{
|
|
m_Obj->HandleControlChannelMsg();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////
|
|
//
|
|
// CTSRDPRemoteDesktopClient Methods
|
|
//
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::FinalConstruct()
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::FinalConstruct");
|
|
|
|
HRESULT hr = S_OK;
|
|
if (!AtlAxWinInit()) {
|
|
TRC_ERR((TB, L"AtlAxWinInit failed."));
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
CTSRDPRemoteDesktopClient::~CTSRDPRemoteDesktopClient()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The Destructor
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::~CTSRDPRemoteDesktopClient");
|
|
|
|
if (m_ChannelMgr) {
|
|
m_CtlChannelEventSink.DispEventUnadvise(m_CtlChannel);
|
|
}
|
|
|
|
if (m_TSClient != NULL) {
|
|
m_TSClient->Release();
|
|
m_TSClient = NULL;
|
|
}
|
|
|
|
if( m_TimerId > 0 ) {
|
|
KillTimer( m_TimerId );
|
|
}
|
|
|
|
ListenConnectCleanup();
|
|
|
|
if( m_InitListeningLibrary )
|
|
{
|
|
// Dereference listening library.
|
|
TerminateListeningLibrary();
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::Initialize(
|
|
LPCREATESTRUCT pCreateStruct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Final Initialization
|
|
|
|
Arguments:
|
|
|
|
pCreateStruct - WM_CREATE, create struct.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::Initialize");
|
|
|
|
RECT rcClient = { 0, 0, pCreateStruct->cx, pCreateStruct->cy };
|
|
HRESULT hr;
|
|
IUnknown *pUnk = NULL;
|
|
DWORD result;
|
|
IMsRdpClientAdvancedSettings2 *advancedSettings;
|
|
CComBSTR bstr;
|
|
HKEY hKey = NULL;
|
|
HRESULT hrIgnore;
|
|
|
|
ASSERT(!m_Initialized);
|
|
|
|
//
|
|
// Create the client Window.
|
|
//
|
|
m_TSClientWnd = m_TSClientAxView.Create(
|
|
m_hWnd, rcClient, MSTSCAX_TEXTGUID,
|
|
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0
|
|
);
|
|
|
|
if (m_TSClientWnd == NULL) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
TRC_ERR((TB, L"Window Create: %08X", GetLastError()));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Get IUnknown
|
|
//
|
|
hr = AtlAxGetControl(m_TSClientWnd, &pUnk);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"AtlAxGetControl: %08X", hr));
|
|
pUnk = NULL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Initialize the event sink.
|
|
//
|
|
m_TSClientEventSink.m_Obj = this;
|
|
|
|
//
|
|
// Add the event sink.
|
|
//
|
|
hr = m_TSClientEventSink.DispEventAdvise(pUnk);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"DispEventAdvise: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Get the control.
|
|
//
|
|
hr = pUnk->QueryInterface(__uuidof(IMsRdpClient2), (void**)&m_TSClient);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"QueryInterface: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Specify that the MSTSC input handler window should accept background
|
|
// events.
|
|
//
|
|
hr = m_TSClient->get_AdvancedSettings3(&advancedSettings);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"IMsTscAdvancedSettings: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
hr = advancedSettings->put_allowBackgroundInput(1);
|
|
|
|
|
|
//
|
|
// Disable autoreconnect it doesn't apply to Salem
|
|
//
|
|
hr = advancedSettings->put_EnableAutoReconnect(VARIANT_FALSE);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"put_EnableAutoReconnect: %08X", hr));
|
|
result = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
|
|
//
|
|
// Disable advanced desktop features for the help session.
|
|
// An error here is not critical, so we ignore it.
|
|
//
|
|
LONG flags = TS_PERF_DISABLE_WALLPAPER | TS_PERF_DISABLE_THEMING;
|
|
hrIgnore = advancedSettings->put_PerformanceFlags(flags);
|
|
if (!SUCCEEDED(hrIgnore)) {
|
|
TRC_ERR((TB, L"put_PerformanceFlags: %08X", hrIgnore));
|
|
}
|
|
|
|
//
|
|
// Disable CTRL_ALT_BREAK, ignore error
|
|
//
|
|
hrIgnore = advancedSettings->put_HotKeyFullScreen(0);
|
|
if (!SUCCEEDED(hrIgnore)) {
|
|
TRC_ERR((TB, L"put_HotKeyFullScreen: %08X", hrIgnore));
|
|
}
|
|
|
|
//
|
|
// Don't allow mstscax to grab input focus on connect. Ignore error
|
|
// on failure.
|
|
//
|
|
hrIgnore = advancedSettings->put_GrabFocusOnConnect(FALSE);
|
|
if (!SUCCEEDED(hrIgnore)) {
|
|
TRC_ERR((TB, L"put_HotKeyFullScreen: %08X", hrIgnore));
|
|
}
|
|
|
|
advancedSettings->Release();
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"put_allowBackgroundInput: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Create the "remote desktop" virtual channel with the TS Client.
|
|
//
|
|
bstr = TSRDPREMOTEDESKTOP_VC_CHANNEL;
|
|
hr = m_TSClient->CreateVirtualChannels(bstr);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"CreateVirtualChannels: %08X", hr));
|
|
result = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Set the Shadow Persistent option
|
|
//
|
|
hr = m_TSClient->SetVirtualChannelOptions(bstr, CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"SetVirtualChannelOptions: %08X", hr));
|
|
result = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//initialize timer-related stuff
|
|
m_PrevTimer = GetTickCount();
|
|
//
|
|
//get the time interval for pings from the registry
|
|
//
|
|
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
REG_CONTROL_SALEM,
|
|
0,
|
|
KEY_READ,
|
|
&hKey
|
|
) == ERROR_SUCCESS ) {
|
|
|
|
DWORD dwSize = sizeof(DWORD);
|
|
DWORD dwType;
|
|
if((RegQueryValueEx(hKey,
|
|
RDC_CONNCHECK_ENTRY,
|
|
NULL,
|
|
&dwType,
|
|
(PBYTE) &m_RdcConnCheckTimeInterval,
|
|
&dwSize
|
|
) == ERROR_SUCCESS) && dwType == REG_DWORD ) {
|
|
|
|
m_RdcConnCheckTimeInterval *= 1000; //we need this in millisecs
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//fall back to default, if reg lookup failed
|
|
//
|
|
m_RdcConnCheckTimeInterval = RDC_CHECKCONN_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if(NULL != hKey )
|
|
RegCloseKey(hKey);
|
|
//
|
|
// m_TSClient keeps our reference to the client object until
|
|
// the destructor is called.
|
|
//
|
|
if (pUnk != NULL) {
|
|
pUnk->Release();
|
|
}
|
|
|
|
SetValid(SUCCEEDED(hr));
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::SendData(
|
|
BSTR data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IDataChannelIO Data Channel Send Method
|
|
|
|
Arguments:
|
|
|
|
data - Data to send.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SendData");
|
|
|
|
CComBSTR channelName;
|
|
HRESULT hr;
|
|
|
|
ASSERT(IsValid());
|
|
|
|
channelName = TSRDPREMOTEDESKTOP_VC_CHANNEL;
|
|
hr = m_TSClient->SendOnVirtualChannel(
|
|
channelName,
|
|
(BSTR)data
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"SendOnVirtualChannel: %08X", hr));
|
|
}
|
|
|
|
//
|
|
//update timer
|
|
//
|
|
m_PrevTimer = GetTickCount();
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::put_EnableSmartSizing(
|
|
BOOL val
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enable/Disable Smart Sizing
|
|
|
|
Arguments:
|
|
|
|
val - TRUE for enable. FALSE, otherwise.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_EnableSmartSizing");
|
|
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_AdvancedSettings2(&pAdvSettings);
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"get_AdvancedSettings2: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
hr = pAdvSettings->put_SmartSizing(val ? VARIANT_TRUE : VARIANT_FALSE);
|
|
pAdvSettings->Release();
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::get_EnableSmartSizing(
|
|
BOOL *pVal
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enable/Disable Smart Sizing
|
|
|
|
Arguments:
|
|
|
|
val - TRUE for enable. FALSE, otherwise.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
VARIANT_BOOL vb;
|
|
IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_EnableSmartSizing");
|
|
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_AdvancedSettings2(&pAdvSettings);
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"get_AdvancedSettings2: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = pAdvSettings->get_SmartSizing(&vb);
|
|
*pVal = (vb != 0);
|
|
pAdvSettings->Release();
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::put_ChannelMgr(
|
|
ISAFRemoteDesktopChannelMgr *newVal
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assign the data channel manager interface.
|
|
|
|
Arguments:
|
|
|
|
newVal - Data Channel Manager
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_ChannelMgr");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// We should get called one time.
|
|
//
|
|
ASSERT(m_ChannelMgr == NULL);
|
|
m_ChannelMgr = newVal;
|
|
|
|
//
|
|
// Register the Remote Desktop control channel
|
|
//
|
|
hr = m_ChannelMgr->OpenDataChannel(
|
|
REMOTEDESKTOP_RC_CONTROL_CHANNEL, &m_CtlChannel
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Register an event sink with the channel manager.
|
|
//
|
|
m_CtlChannelEventSink.m_Obj = this;
|
|
|
|
//
|
|
// Add the event sink.
|
|
//
|
|
hr = m_CtlChannelEventSink.DispEventAdvise(m_CtlChannel);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"DispEventAdvise: %08X", hr));
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::ConnectServerWithOpenedSocket()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connects the client component to the server-side Remote Desktop Host COM
|
|
Object with already opened socket.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectServerWithSocket");
|
|
|
|
IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
|
|
|
|
TRC_NRM((TB, L"ConnectServerWithOpenedSocket"));
|
|
|
|
ASSERT( INVALID_SOCKET != m_TSConnectSocket );
|
|
|
|
//
|
|
// Direct the MSTSCAX control to connect.
|
|
//
|
|
hr = m_TSClient->put_Server( m_ConnectedServer );
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"put_Server: %ld", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
|
|
if( SUCCEEDED(hr) && ptsAdvSettings ) {
|
|
VARIANT var;
|
|
|
|
VariantClear(&var);
|
|
var.vt = VT_BYREF;
|
|
var.byref = (PVOID)m_TSConnectSocket;
|
|
|
|
hr = ptsAdvSettings->put_ConnectWithEndpoint( &var );
|
|
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, _T("put_ConnectWithEndpoint failed - GLE:%x"), hr));
|
|
}
|
|
|
|
VariantClear(&var);
|
|
ptsAdvSettings->Release();
|
|
}
|
|
|
|
if( FAILED(hr) ) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// mstscax owns this socket and will close it
|
|
//
|
|
m_TSConnectSocket = INVALID_SOCKET;
|
|
|
|
hr = m_TSClient->Connect();
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"Connect: 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::ConnectServerPort(
|
|
BSTR bstrServer,
|
|
LONG portNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connects the client component to the server-side Remote Desktop Host COM
|
|
Object with specific port number
|
|
|
|
Arguments:
|
|
|
|
bstrServer : Name or IP address of server.
|
|
portNumber : optional port number.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectServerPort");
|
|
|
|
HRESULT hr;
|
|
IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
|
|
|
|
TRC_NRM((TB, L"ConnectServerPort %s %d", bstrServer, portNumber));
|
|
|
|
//
|
|
// Direct the MSTSCAX control to connect.
|
|
//
|
|
hr = m_TSClient->put_Server( bstrServer );
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"put_Server: %ld", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
|
|
if( SUCCEEDED(hr) && ptsAdvSettings ) {
|
|
//
|
|
// Previous ConnectServerPort() might have set this port number
|
|
// other than 3389
|
|
//
|
|
hr = ptsAdvSettings->put_RDPPort(
|
|
(0 != portNumber) ? portNumber : TERMSRV_TCPPORT
|
|
);
|
|
|
|
if (FAILED(hr) ) {
|
|
TRC_ERR((TB, L"put_RDPPort failed: 0x%08x", hr));
|
|
}
|
|
|
|
ptsAdvSettings->Release();
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"get_AdvancedSettings2 failed: 0x%08x", hr));
|
|
}
|
|
|
|
//
|
|
// Failed the connection if we can't set the port number
|
|
//
|
|
if( FAILED(hr) )
|
|
{
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
m_ConnectedServer = bstrServer;
|
|
m_ConnectedPort = (0 != portNumber) ? portNumber : TERMSRV_TCPPORT;
|
|
|
|
hr = m_TSClient->Connect();
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"Connect: 0x%08x", hr));
|
|
}
|
|
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::SetupConnectionInfo(
|
|
BOOL bListenConnectInfo,
|
|
BSTR bstrExpertBlob
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connects the client component to the server-side Remote Desktop Host COM
|
|
Object.
|
|
|
|
Arguments:
|
|
|
|
bstrExpertBlob : Optional parameter to be transmitted to SAF resolver.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SetupConnectionInfo");
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD result;
|
|
DWORD protocolType;
|
|
IMsTscNonScriptable* ptsns = NULL;
|
|
IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
|
|
IMsRdpClientSecuredSettings* ptsSecuredSettings = NULL;
|
|
CComBSTR bstrAssistantAccount;
|
|
CComBSTR bstrAccountDomainName;
|
|
CComBSTR machineAddressList;
|
|
|
|
//
|
|
// Parse the connection parameters.
|
|
//
|
|
result = ParseConnectParmsString(
|
|
m_ConnectParms,
|
|
&m_ConnectParmVersion,
|
|
&protocolType,
|
|
machineAddressList,
|
|
bstrAssistantAccount,
|
|
m_AssistantAccountPwd,
|
|
m_HelpSessionID,
|
|
m_HelpSessionName,
|
|
m_HelpSessionPwd,
|
|
m_TSSecurityBlob
|
|
);
|
|
if (result != ERROR_SUCCESS) {
|
|
hr = HRESULT_FROM_WIN32(result);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// If the protocol type doesn't match, then fail.
|
|
//
|
|
if (protocolType != REMOTEDESKTOP_TSRDP_PROTOCOL) {
|
|
TRC_ERR((TB, L"Invalid connection protocol %ld", protocolType));
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if (bListenConnectInfo) {
|
|
m_ServerAddressList.clear();
|
|
}
|
|
else {
|
|
//
|
|
// Parse address list in connect parm.
|
|
//
|
|
result = ParseAddressList( machineAddressList, m_ServerAddressList );
|
|
if( ERROR_SUCCESS != result ) {
|
|
TRC_ERR((TB, L"Invalid address list 0x%08x", result));
|
|
hr = HRESULT_FROM_WIN32(result);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( 0 == m_ServerAddressList.size() ) {
|
|
TRC_ERR((TB, L"Invalid connection address list"));
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
}
|
|
|
|
hr = m_TSClient->put_UserName(SALEMHELPASSISTANTACCOUNT_NAME);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"put_UserName: %ld", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
|
|
if( SUCCEEDED(hr) && ptsAdvSettings ) {
|
|
hr = ptsAdvSettings->put_DisableRdpdr( TRUE );
|
|
|
|
if (FAILED(hr) ) {
|
|
TRC_ERR((TB, L"put_DisableRdpdr failed: 0x%08x", hr));
|
|
}
|
|
|
|
// Security: Always have activex control to notify us of receiving
|
|
// TS public key, pre-XP ticket is no longer supported.
|
|
|
|
// tell activeX control to notify us TS public key
|
|
hr = ptsAdvSettings->put_NotifyTSPublicKey(VARIANT_TRUE);
|
|
if (FAILED(hr) ) {
|
|
TRC_ERR((TB, L"put_NotifyTSPublicKey failed: 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Setting connection timeout, ICS might take sometime to routine
|
|
// opened port to actual TS server, neither is critical error.
|
|
//
|
|
hr = ptsAdvSettings->put_singleConnectionTimeout( 60 * 2 ); // try two mins timeout
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"put_singleConnectionTimeout : 0x%x", hr));
|
|
}
|
|
|
|
hr = ptsAdvSettings->put_overallConnectionTimeout( 60 * 2 );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"put_overallConnectionTimeout : 0x%x", hr));
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"QueryInterface IID_IMsRdpClientAdvancedSettings: %ld", hr));
|
|
}
|
|
|
|
// Password encryption is based on encyption cycle key + help session ID
|
|
hr = m_TSClient->get_SecuredSettings2( &ptsSecuredSettings );
|
|
|
|
if( FAILED(hr) || !ptsSecuredSettings ) {
|
|
TRC_ERR((TB, L"get_IMsTscSecuredSettings : 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// TermSrv invoke sessmgr to check if help session is valid
|
|
// before kicking off rdsaddin.exe, we need to send over
|
|
// help session ID and password, only place available and big
|
|
// enough is on WorkDir and StartProgram property, TermSrv will
|
|
// ignore these and fill appropriate value for it
|
|
//
|
|
hr = ptsSecuredSettings->put_WorkDir( m_HelpSessionID );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"put_WorkDir: 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = ptsSecuredSettings->put_StartProgram( m_HelpSessionPwd );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"put_StartProgram: 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
ptsSecuredSettings->Release();
|
|
|
|
|
|
// we only use this to disable redirection, not a critical
|
|
// error, just ugly
|
|
|
|
hr = m_TSClient->QueryInterface(IID_IMsTscNonScriptable,
|
|
(void**)&ptsns);
|
|
if(!SUCCEEDED(hr) || !ptsns){
|
|
TRC_ERR((TB, L"QueryInterface IID_IMsTscNonScriptable: %ld", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
// Whistler XP client, password is just a junk
|
|
hr = ptsns->put_ClearTextPassword( m_AssistantAccountPwd );
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, L"put_ClearTextPassword: 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
m_ExpertBlob = bstrExpertBlob;
|
|
|
|
//
|
|
// Instruct mstscax to connect with certain screen resolution,
|
|
// mstscax will default to 200x20 (???), min. is VGA size.
|
|
//
|
|
{
|
|
RECT rect;
|
|
LONG cx;
|
|
LONG cy;
|
|
|
|
GetClientRect(&rect);
|
|
cx = rect.right - rect.left;
|
|
cy = rect.bottom - rect.top;
|
|
|
|
if( cx < 640 || cy < 480 )
|
|
{
|
|
cx = 640;
|
|
cy = 480;
|
|
}
|
|
|
|
m_TSClient->put_DesktopWidth(cx);
|
|
m_TSClient->put_DesktopHeight(cy);
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if( ptsAdvSettings ) {
|
|
ptsAdvSettings->Release();
|
|
}
|
|
|
|
if(ptsns) {
|
|
ptsns->Release();
|
|
ptsns = NULL;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::AcceptListenConnection(
|
|
BSTR bstrExpertBlob
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Establish reverse connection with TS server, TS server must be connected
|
|
wia reverse connection.
|
|
|
|
Parameters:
|
|
|
|
bstrExpertBlob : Same as ConnectToServer().
|
|
|
|
Returns:
|
|
|
|
S_OK or error code.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPTSTR pszUserName = NULL;
|
|
LPTSTR eventString[2];
|
|
TCHAR buffer[125]; // this is enough for port number
|
|
int numChars;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::AcceptListenConnection");
|
|
|
|
//
|
|
// If we are already connected or not valid, then just
|
|
// return.
|
|
//
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if (m_ConnectedToServer || m_ConnectionInProgress) {
|
|
TRC_ERR((TB, L"Connection active"));
|
|
hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_ACTIVE);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( !ListenConnectInProgress() ) {
|
|
TRC_ERR((TB, L"Connection in-active"));
|
|
hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( INVALID_SOCKET == m_TSConnectSocket ) {
|
|
TRC_ERR((TB, L"Socket is not connected"));
|
|
hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = SetupConnectionInfo(TRUE, bstrExpertBlob);
|
|
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"SetupConnectionInfo() failed with 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Log SESSMGR_I_ACCEPTLISTENREVERSECONNECT Event
|
|
//
|
|
hr = GetCurrentUser( &pszUserName );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"GetCurrentUser() failed with 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
numChars = _sntprintf( buffer, sizeof(buffer)/sizeof(buffer[0]), _TEXT("%d"), m_ConnectedPort );
|
|
if( numChars <= 0 ) {
|
|
// 125 chars is way too big for a port number.
|
|
TRC_ERR((TB, L"_sntprintf() return failure"));
|
|
hr = HRESULT_FROM_WIN32( ERROR_INTERNAL_ERROR );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
eventString[0] = pszUserName;
|
|
eventString[1] = buffer;
|
|
|
|
LogRemoteAssistanceEventString(
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
SESSMGR_I_ACCEPTLISTENREVERSECONNECT,
|
|
2,
|
|
eventString
|
|
);
|
|
|
|
hr = ConnectServerWithOpenedSocket();
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if( pszUserName != NULL ) {
|
|
LocalFree(pszUserName);
|
|
}
|
|
|
|
m_ConnectionInProgress = SUCCEEDED(hr);
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::ConnectToServer(BSTR bstrExpertBlob)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connects the client component to the server-side Remote Desktop Host COM
|
|
Object.
|
|
|
|
Arguments:
|
|
|
|
bstrExpertBlob : Optional parameter to be transmitted to SAF resolver.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
Params--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectToServer");
|
|
|
|
HRESULT hr = S_OK;
|
|
ServerAddress address;
|
|
LPTSTR pszUserName = NULL;
|
|
LPTSTR eventString[2];
|
|
|
|
//
|
|
// If we are already connected or not valid, then just
|
|
// return.
|
|
//
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if (m_ConnectedToServer || m_ConnectionInProgress) {
|
|
TRC_ERR((TB, L"Connection active"));
|
|
hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_ACTIVE);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = SetupConnectionInfo(FALSE, bstrExpertBlob);
|
|
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"SetupConnectionInfo() failed with 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
address = m_ServerAddressList.front();
|
|
m_ServerAddressList.pop_front();
|
|
|
|
hr = ConnectServerPort(address.ServerName, address.portNumber);
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB, L"ConnectServerPort: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Log SESSMGR_I_ACCEPTLISTENREVERSECONNECT Event
|
|
//
|
|
hr = GetCurrentUser( &pszUserName );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"GetCurrentUser() failed with 0x%08x", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
eventString[0] = pszUserName;
|
|
eventString[1] = (LPTSTR)m_ConnectParms;
|
|
|
|
LogRemoteAssistanceEventString(
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
SESSMGR_I_EXPERTUSETICKET,
|
|
2,
|
|
eventString
|
|
);
|
|
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if( pszUserName != NULL ) {
|
|
LocalFree(pszUserName);
|
|
}
|
|
|
|
//
|
|
// If we succeeded, remember that we are in a state of connecting.
|
|
//
|
|
m_ConnectionInProgress = SUCCEEDED(hr);
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::DisconnectFromServer()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Disconnects the client from the server to which we are currently
|
|
connected.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
return DisconnectFromServerInternal(
|
|
SAFERROR_LOCALNOTERROR
|
|
);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::DisconnectFromServerInternal(
|
|
LONG errorCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Disconnects the client from the server to which we are currently
|
|
connected.
|
|
|
|
Arguments:
|
|
|
|
reason - Reason for disconnect.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::DisconnectFromServerInternal");
|
|
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Make sure our window is hidden.
|
|
//
|
|
//ShowWindow(SW_HIDE);
|
|
|
|
ListenConnectCleanup();
|
|
|
|
if (m_ConnectedToServer || m_ConnectionInProgress) {
|
|
hr = m_TSClient->Disconnect();
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
m_ConnectionInProgress = FALSE;
|
|
m_ConnectedToServer = FALSE;
|
|
|
|
if (m_RemoteControlRequestInProgress) {
|
|
m_RemoteControlRequestInProgress = FALSE;
|
|
Fire_RemoteControlRequestComplete(SAFERROR_SHADOWEND_UNKNOWN);
|
|
}
|
|
|
|
//
|
|
// Fire the server disconnect event.
|
|
//
|
|
Fire_Disconnected(errorCode);
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, L"Not connected."));
|
|
hr = S_OK;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::ConnectRemoteDesktop()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Once "remote desktop mode" has been enabled for the server-side Remote
|
|
Desktop Host COM Object and we are connected to the server, the
|
|
ConnectRemoteDesktop method can be invoked to take control of the remote
|
|
user's desktop.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectRemoteDesktop");
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD result;
|
|
BSTR rcRequest = NULL;
|
|
|
|
//
|
|
// Fail if we are not valid or not connected to the server.
|
|
//
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
if (!m_ConnectedToServer) {
|
|
hr = HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_CONNECTED);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Succeed if a remote control request is already in progress.
|
|
//
|
|
if (m_RemoteControlRequestInProgress) {
|
|
hr = S_OK;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Generate the remote control connect request message.
|
|
//
|
|
hr = GenerateRCRequest(&rcRequest);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Send it.
|
|
//
|
|
hr = m_CtlChannel->SendChannelData(rcRequest);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// A request is in progress, if we successfully sent the request.
|
|
//
|
|
m_RemoteControlRequestInProgress = TRUE;
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if (rcRequest != NULL) {
|
|
SysFreeString(rcRequest);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::DisconnectRemoteDesktop()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Once "remote desktop mode" has been enabled for the server-side Remote
|
|
Desktop Host COM Object and we are connected to the server, the
|
|
ConnectRemoteDesktop method can be invoked to take control of the remote
|
|
user's desktop.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::DisconnectRemoteDesktop");
|
|
|
|
HRESULT hr = S_OK;
|
|
CComBSTR rcRequest;
|
|
|
|
//
|
|
// Fail if we are not valid or not connected.
|
|
//
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
if (!m_ConnectedToServer) {
|
|
hr = HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_CONNECTED);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Generate the terminate remote control key sequence and sent it to the
|
|
// server.
|
|
//
|
|
if (m_RemoteControlRequestInProgress) {
|
|
hr = SendTerminateRCKeysToServer();
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// ISAFRemoteDesktopTestExtension
|
|
//
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::put_TestExtDllName(/*[in]*/ BSTR newVal)
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
IMsTscAdvancedSettings *pMstscAdvSettings = NULL;
|
|
IMsTscDebug *pMstscDebug = NULL;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_TestExtDllName" );
|
|
|
|
if ( NULL == m_TSClient )
|
|
{
|
|
TRC_ERR((TB, L"m_TSClient is NULL" ));
|
|
hr = E_NOINTERFACE;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_AdvancedSettings( &pMstscAdvSettings );
|
|
if (FAILED( hr ))
|
|
{
|
|
TRC_ERR((TB, L"m_TSClient->get_AdvancedSettings failed %08X", hr ));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_Debugger( &pMstscDebug );
|
|
if ( FAILED( hr ))
|
|
{
|
|
TRC_ERR((TB, L"m_TSClient->get_Debugger failed %08X", hr ));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = pMstscAdvSettings->put_allowBackgroundInput( 1 );
|
|
if (FAILED( hr ))
|
|
{
|
|
TRC_ERR((TB, L"put_allowBackgroundInput failed %08X", hr ));
|
|
}
|
|
pMstscDebug->put_CLXDll( newVal );
|
|
|
|
CLEANUPANDEXIT:
|
|
if ( NULL != pMstscAdvSettings )
|
|
pMstscAdvSettings->Release();
|
|
|
|
if ( NULL != pMstscDebug )
|
|
pMstscDebug->Release();
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::put_TestExtParams(/*[in]*/ BSTR newVal)
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
IMsTscDebug *pMstscDebug = NULL;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_TestExtParams" );
|
|
|
|
if ( NULL == m_TSClient )
|
|
{
|
|
TRC_ERR((TB, L"m_TSClient is NULL" ));
|
|
hr = E_NOINTERFACE;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_Debugger( &pMstscDebug );
|
|
if (FAILED( hr ))
|
|
{
|
|
TRC_ERR((TB, L"m_TSClient->get_Debugger failed %08X", hr ));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = pMstscDebug->put_CLXCmdLine( newVal );
|
|
|
|
CLEANUPANDEXIT:
|
|
if ( NULL != pMstscDebug )
|
|
pMstscDebug->Release();
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
VOID
|
|
CTSRDPRemoteDesktopClient::OnMSTSCReceiveData(
|
|
BSTR data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle Remote Control Control Channel messages.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnMSTSCReceiveData");
|
|
|
|
//
|
|
//we got some data, so we must be connected, update timer
|
|
//
|
|
m_PrevTimer = GetTickCount();
|
|
|
|
//
|
|
// Fire the data ready event.
|
|
//
|
|
Fire_DataReady(data);
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
VOID
|
|
CTSRDPRemoteDesktopClient::HandleControlChannelMsg()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle Remote Control Control Channel messages.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::HandleControlChannelMsg");
|
|
|
|
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
|
|
BSTR msg = NULL;
|
|
LONG *pResult;
|
|
BSTR authenticateReq = NULL;
|
|
BSTR versionInfoPacket = NULL;
|
|
HRESULT hr;
|
|
|
|
DWORD result;
|
|
|
|
ASSERT(IsValid());
|
|
|
|
//
|
|
// Read the next message.
|
|
//
|
|
result = m_CtlChannel->ReceiveChannelData(&msg);
|
|
if (result != ERROR_SUCCESS) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Dispatch, based on the message type.
|
|
//
|
|
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)msg;
|
|
|
|
//
|
|
// If the server-side of the VC link is alive.
|
|
//
|
|
//
|
|
if ((msgHeader->msgType == REMOTEDESKTOP_CTL_SERVER_ANNOUNCE) &&
|
|
m_ConnectionInProgress) {
|
|
|
|
//
|
|
// Send version information to the server.
|
|
//
|
|
hr = GenerateVersionInfoPacket(
|
|
&versionInfoPacket
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
hr = m_CtlChannel->SendChannelData(versionInfoPacket);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Request client authentication.
|
|
//
|
|
hr = GenerateClientAuthenticateRequest(
|
|
&authenticateReq
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
hr = m_CtlChannel->SendChannelData(authenticateReq);
|
|
}
|
|
//
|
|
// If the message is from the server, indicating that it is
|
|
// disconnecting. This can happen if the RDSRemoteDesktopServer
|
|
// is directed to exit listening mode.
|
|
//
|
|
else if (msgHeader->msgType == REMOTEDESKTOP_CTL_DISCONNECT) {
|
|
TRC_NRM((TB, L"Server indicated a disconnect."));
|
|
DisconnectFromServerInternal(SAFERROR_BYSERVER);
|
|
}
|
|
//
|
|
// If the message is a message result.
|
|
//
|
|
else if (msgHeader->msgType == REMOTEDESKTOP_CTL_RESULT) {
|
|
|
|
pResult = (LONG *)(msgHeader+1);
|
|
|
|
//
|
|
// If a remote control request is in progress, then we should check
|
|
// for a remote control complete status.
|
|
//
|
|
if (m_RemoteControlRequestInProgress && ISRCSTATUSCODE(*pResult)) {
|
|
|
|
TRC_ERR((TB, L"Received RC terminate status code."));
|
|
|
|
m_RemoteControlRequestInProgress = FALSE;
|
|
Fire_RemoteControlRequestComplete(*pResult);
|
|
}
|
|
//
|
|
// Otherwise, if a connection is in progress, then the client
|
|
// authentication request must have succeeded.
|
|
//
|
|
else if (m_ConnectionInProgress) {
|
|
|
|
//
|
|
// Should not be getting a remote control status here.
|
|
//
|
|
ASSERT(!ISRCSTATUSCODE(*pResult));
|
|
|
|
//
|
|
// Fire connect request succeeded message.
|
|
//
|
|
if (*pResult == SAFERROR_NOERROR ) {
|
|
m_ConnectedToServer = TRUE;
|
|
m_ConnectionInProgress = FALSE;
|
|
|
|
//
|
|
//set the timer to check if the user is still connected
|
|
//ignore errors, worst case - the ui is up even after the user disconnects
|
|
//
|
|
if( m_RdcConnCheckTimeInterval )
|
|
m_TimerId = SetTimer(WM_CONNECTCHECK_TIMER, m_RdcConnCheckTimeInterval);
|
|
|
|
//
|
|
// Not in progress once connected
|
|
//
|
|
m_ListenConnectInProgress = FALSE;
|
|
m_TSConnectSocket = INVALID_SOCKET;
|
|
|
|
Fire_Connected();
|
|
}
|
|
//
|
|
// Otherwise, fire a disconnected event.
|
|
//
|
|
else {
|
|
DisconnectFromServerInternal(*pResult);
|
|
m_ConnectionInProgress = FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// We will ignore other packets to support forward compatibility.
|
|
//
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
//
|
|
// Release the message.
|
|
//
|
|
if (msg != NULL) {
|
|
SysFreeString(msg);
|
|
}
|
|
|
|
if (versionInfoPacket != NULL) {
|
|
SysFreeString(versionInfoPacket);
|
|
}
|
|
|
|
if (authenticateReq != NULL) {
|
|
SysFreeString(authenticateReq);
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::GenerateRCRequest(
|
|
BSTR *rcRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a remote control request message for the
|
|
server.
|
|
|
|
TODO: We might need to be able to push this up
|
|
to the parent class, if it makes sense for
|
|
NetMeeting.
|
|
|
|
Arguments:
|
|
|
|
rcRequest - Returned request message.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateRCRequest");
|
|
|
|
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
|
|
PBYTE ptr;
|
|
HRESULT hr;
|
|
DWORD len;
|
|
|
|
len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
|
|
|
|
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
|
|
if (msgHeader != NULL) {
|
|
msgHeader->msgType = REMOTEDESKTOP_CTL_REMOTE_CONTROL_DESKTOP;
|
|
ptr = (PBYTE)(msgHeader + 1);
|
|
memcpy(ptr, (BSTR)m_ConnectParms,
|
|
((m_ConnectParms.Length()+1) * sizeof(WCHAR)));
|
|
*rcRequest = (BSTR)msgHeader;
|
|
|
|
hr = S_OK;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::GenerateClientAuthenticateRequest(
|
|
BSTR *authenticateReq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a 'client authenticate' request.
|
|
|
|
TODO: We might need to be able to push this up
|
|
to the parent class, if it makes sense for
|
|
NetMeeting.
|
|
|
|
Arguments:
|
|
|
|
rcRequest - Returned request message.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateClientAuthenticateRequest");
|
|
|
|
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
|
|
PBYTE ptr;
|
|
HRESULT hr;
|
|
DWORD len;
|
|
|
|
len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
|
|
|
|
#if FEATURE_USERBLOBS
|
|
|
|
if( m_ExpertBlob.Length() > 0 ) {
|
|
len += ((m_ExpertBlob.Length() + 1) * sizeof(WCHAR));
|
|
}
|
|
|
|
#endif
|
|
|
|
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
|
|
if (msgHeader != NULL) {
|
|
msgHeader->msgType = REMOTEDESKTOP_CTL_AUTHENTICATE;
|
|
ptr = (PBYTE)(msgHeader + 1);
|
|
memcpy(ptr, (BSTR)m_ConnectParms,
|
|
((m_ConnectParms.Length()+1) * sizeof(WCHAR)));
|
|
|
|
#if FEATURE_USERBLOBS
|
|
|
|
if( m_ExpertBlob.Length() > 0 ) {
|
|
ptr += ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
|
|
memcpy(ptr, (BSTR)m_ExpertBlob,
|
|
((m_ExpertBlob.Length()+1) * sizeof(WCHAR)));
|
|
}
|
|
|
|
#endif
|
|
|
|
*authenticateReq = (BSTR)msgHeader;
|
|
|
|
hr = S_OK;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::GenerateVersionInfoPacket(
|
|
BSTR *versionInfoPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a version information packet.
|
|
|
|
Arguments:
|
|
|
|
versionInfoPacket - Version Information Returned Packet
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateVersionInfoPacket");
|
|
|
|
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
|
|
PDWORD ptr;
|
|
HRESULT hr;
|
|
DWORD len;
|
|
|
|
len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + (sizeof(DWORD) * 2);
|
|
|
|
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
|
|
if (msgHeader != NULL) {
|
|
msgHeader->msgType = REMOTEDESKTOP_CTL_VERSIONINFO;
|
|
ptr = (PDWORD)(msgHeader + 1);
|
|
*ptr = REMOTEDESKTOP_VERSION_MAJOR; ptr++;
|
|
*ptr = REMOTEDESKTOP_VERSION_MINOR;
|
|
*versionInfoPacket = (BSTR)msgHeader;
|
|
hr = S_OK;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
VOID
|
|
CTSRDPRemoteDesktopClient::OnReceivedTSPublicKey(BSTR bstrPublicKey, VARIANT_BOOL* pfContinue)
|
|
{
|
|
DWORD dwStatus;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnReceivedPublicKey");
|
|
CComBSTR bstrTSPublicKey;
|
|
|
|
if( m_ConnectParmVersion >= SALEM_CONNECTPARM_SECURITYBLOB_VERSION ) {
|
|
|
|
//
|
|
// hash TS public key send from client activeX control, reverse
|
|
// hashing from what we got in connect parm might not give us
|
|
// back the original value.
|
|
//
|
|
dwStatus = HashSecurityData(
|
|
(PBYTE) bstrPublicKey,
|
|
::SysStringByteLen(bstrPublicKey),
|
|
bstrTSPublicKey
|
|
);
|
|
|
|
if( ERROR_SUCCESS != dwStatus )
|
|
{
|
|
TRC_ERR((TB, L"Hashed Public Key Send from TS %s", bstrPublicKey));
|
|
TRC_ERR((TB, L"Hashed public Key in parm %s", m_TSSecurityBlob));
|
|
TRC_ERR((TB, L"HashSecurityData() failed with %d", dwStatus));
|
|
*pfContinue = FALSE;
|
|
}
|
|
else if( !(bstrTSPublicKey == m_TSSecurityBlob) )
|
|
{
|
|
TRC_ERR((TB, L"Hashed Public Key Send from TS %s", bstrPublicKey));
|
|
TRC_ERR((TB, L"Hashed public Key in parm %s", m_TSSecurityBlob));
|
|
|
|
*pfContinue = VARIANT_FALSE;
|
|
}
|
|
else
|
|
{
|
|
*pfContinue = VARIANT_TRUE;
|
|
}
|
|
}
|
|
else {
|
|
// SECURITY : Disconnect if TS public key not present on Salem ticket,
|
|
*pfContinue = VARIANT_FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
VOID
|
|
CTSRDPRemoteDesktopClient::OnRDPConnected()
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnRDPConnected");
|
|
|
|
Fire_BeginConnect();
|
|
DC_END_FN();
|
|
}
|
|
|
|
VOID
|
|
CTSRDPRemoteDesktopClient::OnLoginComplete()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnLoginComplete");
|
|
|
|
//
|
|
// Clear server address list
|
|
//
|
|
m_ServerAddressList.clear();
|
|
|
|
//
|
|
// We got some event from the mstsc, so we must be connected, update timer
|
|
//
|
|
m_PrevTimer = GetTickCount();
|
|
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
LONG
|
|
CTSRDPRemoteDesktopClient::TranslateMSTSCDisconnectCode(
|
|
DisconnectReasonCode disconReason,
|
|
ExtendedDisconnectReasonCode extendedReasonCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Translate an MSTSC disconnect code into a Salem disconnect
|
|
code.
|
|
|
|
Arguments:
|
|
|
|
disconReason - Disconnect Reason
|
|
extendedReasonCode - MSTSCAX Extended Reason Code
|
|
|
|
Return Value:
|
|
|
|
Salem disconnect code.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::TranslateMSTSCDisconnectCode");
|
|
|
|
LONG ret;
|
|
BOOL handled;
|
|
|
|
//
|
|
// First check the extended error information.
|
|
// TODO: Need to keep track of additional values added by NadimA
|
|
// and company, here, before we ship.
|
|
//
|
|
if (extendedReasonCode != exDiscReasonNoInfo) {
|
|
//
|
|
// Record the extended error code, if given. Note that this may be
|
|
// overridden below if we have better information.
|
|
//
|
|
m_LastExtendedErrorInfo = extendedReasonCode;
|
|
|
|
//
|
|
// Check for a protocol error.
|
|
//
|
|
if ((extendedReasonCode >= exDiscReasonProtocolRangeStart) &&
|
|
(extendedReasonCode <= exDiscReasonProtocolRangeEnd)) {
|
|
ret = SAFERROR_RCPROTOCOLERROR;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the extended error information didn't help us.
|
|
//
|
|
switch(disconReason)
|
|
{
|
|
case disconnectReasonNoInfo : ret = SAFERROR_NOINFO;
|
|
break;
|
|
|
|
case disconnectReasonLocalNotError : ret = SAFERROR_LOCALNOTERROR;
|
|
break;
|
|
|
|
case disconnectReasonRemoteByUser : ret = SAFERROR_REMOTEBYUSER;
|
|
break;
|
|
|
|
case disconnectReasonByServer : ret = SAFERROR_BYSERVER;
|
|
break;
|
|
|
|
case disconnectReasonDNSLookupFailed2 : m_LastExtendedErrorInfo = disconReason;
|
|
case disconnectReasonDNSLookupFailed : ret = SAFERROR_DNSLOOKUPFAILED;
|
|
break;
|
|
|
|
case disconnectReasonOutOfMemory3 :
|
|
case disconnectReasonOutOfMemory2 : m_LastExtendedErrorInfo = disconReason;
|
|
case disconnectReasonOutOfMemory : ret = SAFERROR_OUTOFMEMORY;
|
|
break;
|
|
|
|
case disconnectReasonConnectionTimedOut : ret = SAFERROR_CONNECTIONTIMEDOUT;
|
|
break;
|
|
|
|
case disconnectReasonSocketConnectFailed : ret = SAFERROR_SOCKETCONNECTFAILED;
|
|
break;
|
|
|
|
case disconnectReasonHostNotFound : ret = SAFERROR_HOSTNOTFOUND;
|
|
break;
|
|
|
|
case disconnectReasonWinsockSendFailed : ret = SAFERROR_WINSOCKSENDFAILED;
|
|
break;
|
|
|
|
case disconnectReasonInvalidIP : m_LastExtendedErrorInfo = disconReason;
|
|
case disconnectReasonInvalidIPAddr : ret = SAFERROR_INVALIDIPADDR;
|
|
break;
|
|
|
|
case disconnectReasonSocketRecvFailed : ret = SAFERROR_SOCKETRECVFAILED;
|
|
break;
|
|
|
|
case disconnectReasonInvalidEncryption : ret = SAFERROR_INVALIDENCRYPTION;
|
|
break;
|
|
|
|
case disconnectReasonGetHostByNameFailed : ret = SAFERROR_GETHOSTBYNAMEFAILED;
|
|
break;
|
|
|
|
case disconnectReasonLicensingFailed : m_LastExtendedErrorInfo = disconReason;
|
|
case disconnectReasonLicensingTimeout : ret = SAFERROR_LICENSINGFAILED;
|
|
break;
|
|
|
|
case disconnectReasonDecryptionError : ret = SAFERROR_DECRYPTIONERROR;
|
|
break;
|
|
|
|
case disconnectReasonServerCertificateUnpackErr : ret = SAFERROR_MISMATCHPARMS;
|
|
break;
|
|
|
|
//
|
|
// Following are list of error code that is not defined in active X control IDL file
|
|
//
|
|
|
|
// NL_ERR_TDFDCLOSE
|
|
case 0x904 : ret = SAFERROR_SOCKETCONNECTFAILED;
|
|
m_LastExtendedErrorInfo = disconReason;
|
|
break;
|
|
|
|
// UI_ERR_NORMAL_DISCONNECT
|
|
case 0xb08 : ret = SAFERROR_LOCALNOTERROR;
|
|
m_LastExtendedErrorInfo = disconReason;
|
|
break;
|
|
|
|
// UI_ERR_LOOPBACK_CONSOLE_CONNECT
|
|
case 0x708 : ret = SAFERROR_SELFHELPNOTSUPPORTED;
|
|
m_LastExtendedErrorInfo = disconReason;
|
|
break;
|
|
|
|
// NL_ERR_TDTIMEOUT
|
|
case 0x704 : ret = SAFERROR_CONNECTIONTIMEDOUT;
|
|
m_LastExtendedErrorInfo = disconReason;
|
|
break;
|
|
|
|
// UI_ERR_UNEXPECTED_DISCONNECT
|
|
case 0xa08 : ret = SAFERROR_BYSERVER;
|
|
m_LastExtendedErrorInfo = disconReason;
|
|
break;
|
|
// SL_ERR_ENCRYPTFAILED
|
|
case 0xB06 : m_LastExtendedErrorInfo = disconReason;
|
|
ret = SAFERROR_ENCRYPTIONERROR;
|
|
break;
|
|
|
|
|
|
case 0x406 : // SL_ERR_NOSECURITYUSERDATA
|
|
case 0x606 : // SL_ERR_INVALIDSRVRAND
|
|
case 0x806 : // SL_ERR_GENSRVRANDFAILED
|
|
case 0x906 : // SL_ERR_ENCCLNTRANDFAILED
|
|
case 0xA06 : // SL_ERR_MKSESSKEYFAILED
|
|
|
|
case 0xA04 : // NL_ERR_TDANSICONVERT
|
|
case 0x1104 : // NL_ERR_XTBADPKTVERSION
|
|
case 0x1204 : // NL_ERR_XTBADHEADER
|
|
case 0x1304 : // NL_ERR_XTUNEXPECTEDDATA
|
|
|
|
case 0x2104 : // NL_ERR_MCSUNEXPECTEDPDU
|
|
case 0x2204 : // NL_ERR_MCSNOTCRPDU
|
|
case 0x2304 : // NL_ERR_MCSBADCRLENGTH
|
|
case 0x2404 : // NL_ERR_MCSBADCRFIELDS
|
|
case 0x2604 : // NL_ERR_MCSBADMCSREASON
|
|
case 0x2704 : // NL_ERR_MCSNOUSERIDINAUC
|
|
case 0x2804 : // NL_ERR_MCSNOCHANNELIDINCJC
|
|
|
|
case 0x3104 : // NL_ERR_NCBADMCSRESULT
|
|
case 0x3304 : // NL_ERR_NCNOUSERDATA
|
|
case 0x3404 : // NL_ERR_NCINVALIDH221KEY
|
|
case 0x3504 : // NL_ERR_NCNONETDATA
|
|
case 0x3604 : // NL_ERR_NCATTACHUSERFAILED
|
|
case 0x3704 : // NL_ERR_NCCHANNELJOINFAILED
|
|
case 0x3804 : // NL_ERR_NCJOINBADCHANNEL
|
|
case 0x3904 : // NL_ERR_NCNOCOREDATA
|
|
case 0x3a04 : // NL_ERR_NCVERSIONMISMATCH
|
|
|
|
case 0x408 : // UI_ERR_ANSICONVERT
|
|
case 0x608 : // UI_ERR_NOTIMER
|
|
m_LastExtendedErrorInfo = disconReason;
|
|
ret = SAFERROR_RCPROTOCOLERROR;
|
|
break;
|
|
|
|
|
|
//
|
|
// New active X control disconnect code, assert to track this
|
|
//
|
|
default: ret = SAFERROR_RCUNKNOWNERROR;
|
|
m_LastExtendedErrorInfo = disconReason;
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
DC_END_FN();
|
|
return ret;
|
|
}
|
|
|
|
VOID
|
|
CTSRDPRemoteDesktopClient::OnDisconnected(
|
|
long disconReason
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnDisconnected");
|
|
HRESULT hr = E_HANDLE; // initialize an error code.
|
|
|
|
long clientReturnCode;
|
|
ExtendedDisconnectReasonCode extendedClientCode;
|
|
|
|
TRC_ERR((TB, L"Disconnected because %ld", disconReason));
|
|
|
|
m_TSClient->get_ExtendedDisconnectReason(&extendedClientCode);
|
|
clientReturnCode = TranslateMSTSCDisconnectCode(
|
|
(DisconnectReasonCode)disconReason,
|
|
extendedClientCode
|
|
);
|
|
|
|
// Go thru all remaining server:port, mstscax might return some
|
|
// error code that we don't understand.
|
|
if( m_ServerAddressList.size() > 0 ) {
|
|
|
|
ServerAddress address;
|
|
|
|
address = m_ServerAddressList.front();
|
|
m_ServerAddressList.pop_front();
|
|
|
|
hr = ConnectServerPort( address.ServerName, address.portNumber );
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB, L"ConnectServerPort: %08X", hr));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the error code from connecting to 'last' server to client
|
|
//
|
|
if( FAILED(hr) ) {
|
|
|
|
m_ServerAddressList.clear();
|
|
|
|
//
|
|
// Always fire remote control request before disconnect event
|
|
//
|
|
|
|
//
|
|
// Fire the remote control request failure event, if appropriate.
|
|
//
|
|
if (m_RemoteControlRequestInProgress) {
|
|
ASSERT(clientReturnCode != SAFERROR_NOERROR);
|
|
Fire_RemoteControlRequestComplete(SAFERROR_SHADOWEND_UNKNOWN);
|
|
m_RemoteControlRequestInProgress = FALSE;
|
|
}
|
|
|
|
//
|
|
// Fire the server disconnect event, if we are really connected or
|
|
// we have a connection in progress.
|
|
//
|
|
if (m_ConnectedToServer || m_ConnectionInProgress) {
|
|
Fire_Disconnected(clientReturnCode);
|
|
}
|
|
|
|
m_ConnectedToServer = FALSE;
|
|
m_ConnectionInProgress = FALSE;
|
|
|
|
ListenConnectCleanup();
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::SendTerminateRCKeysToServer()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send the terminate shadowing key sequence to the server.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error status is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SendTerminateRCKeysToServer");
|
|
|
|
HRESULT hr = S_OK;
|
|
IMsRdpClientNonScriptable* pTscNonScript = NULL;
|
|
|
|
VARIANT_BOOL keyUp[] = {
|
|
VARIANT_FALSE, VARIANT_FALSE, VARIANT_TRUE, VARIANT_TRUE
|
|
};
|
|
LONG keyData[] = {
|
|
MapVirtualKey(VK_CONTROL, 0), // these are SCANCODES.
|
|
MapVirtualKey(VK_MULTIPLY, 0),
|
|
MapVirtualKey(VK_MULTIPLY, 0),
|
|
MapVirtualKey(VK_CONTROL, 0),
|
|
};
|
|
|
|
//
|
|
// Send the terminate keys to the server.
|
|
//
|
|
hr = m_TSClient->QueryInterface(
|
|
IID_IMsRdpClientNonScriptable,
|
|
(void**)&pTscNonScript
|
|
);
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"QI: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
pTscNonScript->NotifyRedirectDeviceChange(0, 0);
|
|
pTscNonScript->SendKeys(4, keyUp, keyData);
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"SendKeys, QI: %08X", hr));
|
|
}
|
|
|
|
pTscNonScript->Release();
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HWND CTSRDPRemoteDesktopClient::SearchForWindow(
|
|
HWND hwndParent,
|
|
LPTSTR srchCaption,
|
|
LPTSTR srchClass
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Search for a child window of the specified parent window.
|
|
|
|
Arguments:
|
|
|
|
srchCaption - Window caption for which to search. NULL is
|
|
considered a wildcard.
|
|
srchClass - Window class for which to search. NULL is
|
|
considred a wildcard.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error status is returned.
|
|
|
|
--*/
|
|
{
|
|
WINSEARCH srch;
|
|
|
|
srch.foundWindow = NULL;
|
|
srch.srchCaption = srchCaption;
|
|
srch.srchClass = srchClass;
|
|
|
|
BOOL result = EnumChildWindows(
|
|
hwndParent,
|
|
(WNDENUMPROC)_WindowSrchProc,
|
|
(LPARAM)&srch
|
|
);
|
|
|
|
return srch.foundWindow;
|
|
}
|
|
BOOL CALLBACK
|
|
CTSRDPRemoteDesktopClient::_WindowSrchProc(HWND hwnd, PWINSEARCH srch)
|
|
{
|
|
TCHAR classname[128];
|
|
TCHAR caption[128];
|
|
|
|
if (srch->srchClass && !GetClassName(hwnd, classname, sizeof(classname) / sizeof(TCHAR)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
if (srch->srchCaption && !::GetWindowText(hwnd, caption, sizeof(caption)/sizeof(TCHAR)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if ((!srch->srchClass || !_tcscmp(classname, srch->srchClass)
|
|
&&
|
|
(!srch->srchCaption || !_tcscmp(caption, srch->srchCaption)))
|
|
)
|
|
{
|
|
srch->foundWindow = hwnd;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::GenerateNullData( BSTR* pbstrData )
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateNullData");
|
|
HRESULT hr;
|
|
DWORD len;
|
|
|
|
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader = NULL;
|
|
len = sizeof( REMOTEDESKTOP_CTL_BUFHEADER );
|
|
|
|
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
|
|
if (msgHeader != NULL) {
|
|
msgHeader->msgType = REMOTEDESKTOP_CTL_ISCONNECTED;
|
|
//nothing else other than the message
|
|
*pbstrData = (BSTR)msgHeader;
|
|
hr = S_OK;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
|
|
LRESULT CTSRDPRemoteDesktopClient::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnCheckConnectTimer");
|
|
BSTR bstrMsg = NULL;
|
|
|
|
if( WM_LISTENTIMEOUT_TIMER == wParam ) {
|
|
|
|
bHandled = TRUE;
|
|
if( TRUE == ListenConnectInProgress() ) {
|
|
//
|
|
// This function will fire the listen connect callback for us.
|
|
//
|
|
StopListenInternal(SAFERROR_CONNECTIONTIMEDOUT);
|
|
}
|
|
}
|
|
else if ( WM_CONNECTCHECK_TIMER == wParam ) {
|
|
DWORD dwCurTimer = GetTickCount();
|
|
|
|
bHandled = TRUE;
|
|
|
|
if(m_ConnectedToServer) {
|
|
//
|
|
//see if the timer wrapped around to zero (does so if the system was up 49.7 days or something)
|
|
//if so reset it
|
|
//
|
|
if( dwCurTimer > m_PrevTimer && ( dwCurTimer - m_PrevTimer >= m_RdcConnCheckTimeInterval )) {
|
|
//
|
|
//time to send a null data
|
|
//
|
|
if(SUCCEEDED(GenerateNullData(&bstrMsg))) {
|
|
if(!SUCCEEDED(m_CtlChannel->SendChannelData(bstrMsg))) {
|
|
//
|
|
//could not send data, assume disconnected
|
|
//
|
|
DisconnectFromServer();
|
|
//
|
|
//don't need the timer anymore, kill it
|
|
//
|
|
KillTimer( m_TimerId );
|
|
m_TimerId = 0;
|
|
}
|
|
}
|
|
}
|
|
} //m_ConnectedToServer
|
|
|
|
//
|
|
//update the timer
|
|
//
|
|
m_PrevTimer = dwCurTimer;
|
|
|
|
if( NULL != bstrMsg ) {
|
|
SysFreeString(bstrMsg);
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::CreateListenEndpoint(
|
|
IN LONG port,
|
|
OUT BSTR* pConnectParm
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Routine to create a listening socket and return connection parameter to this 'listen' socket.
|
|
|
|
Parameters:
|
|
|
|
port : Port that socket should listen on.
|
|
pConnectParm : Return connection parameter to this listening socket.
|
|
|
|
returns:
|
|
|
|
S_OK or error code.
|
|
|
|
Notes:
|
|
|
|
Function is async, return code, if error, is for listening thread set up, caller is notified of
|
|
successful or error in network connection via ListenConnect event.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPTSTR pszUserName = NULL;
|
|
LPTSTR eventString[2];
|
|
|
|
SOCKET hListenSocket = INVALID_SOCKET;
|
|
IMsRdpClientAdvancedSettings* pAdvSettings;
|
|
LONG rdpPort = 0;
|
|
int intRC;
|
|
int lastError;
|
|
SOCKADDR_IN sockAddr;
|
|
int sockAddrSize;
|
|
int optvalue;
|
|
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::CreateListenEndpoint");
|
|
|
|
if( NULL == pConnectParm )
|
|
{
|
|
hr = E_POINTER;
|
|
return hr;
|
|
}
|
|
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Return error if we are in progress of connect or connected.
|
|
//
|
|
if( TRUE == ListenConnectInProgress() || // Listen already started.
|
|
TRUE == m_ConnectionInProgress || // Connection already in progress
|
|
TRUE == m_ConnectedToServer ) { // Already connected to server
|
|
hr = HRESULT_FROM_WIN32( ERROR_SHARING_VIOLATION );
|
|
TRC_ERR((TB, L"StartListen() already in listen"));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Initialize Winsock and ICS library if not yet initialized.
|
|
// InitListeningLibrary() will only add ref. count
|
|
// if library already initialized by other instance.
|
|
//
|
|
if( FALSE == m_InitListeningLibrary ) {
|
|
|
|
hr = InitListeningLibrary();
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"InitListeningLibrary() failed : %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
m_InitListeningLibrary = TRUE;
|
|
}
|
|
|
|
//
|
|
// mstscax will close the socket once connection is ended.
|
|
//
|
|
m_TSConnectSocket = INVALID_SOCKET;
|
|
|
|
//
|
|
// Create a listening socket
|
|
//
|
|
m_ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
if( INVALID_SOCKET == m_ListenSocket ) {
|
|
intRC = WSAGetLastError();
|
|
TRC_ERR((TB, _T("socket failed %d"), intRC));
|
|
hr = HRESULT_FROM_WIN32(intRC);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Disable NAGLE algorithm and enable don't linger option.
|
|
//
|
|
optvalue = 1;
|
|
setsockopt( m_ListenSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&optvalue, sizeof(optvalue) );
|
|
|
|
optvalue = 1;
|
|
setsockopt( m_ListenSocket, SOL_SOCKET, SO_DONTLINGER, (char *)&optvalue, sizeof(optvalue) );
|
|
|
|
|
|
//
|
|
// Request async notifications to send to our window
|
|
//
|
|
intRC = WSAAsyncSelect(
|
|
m_ListenSocket,
|
|
m_hWnd,
|
|
WM_TSCONNECT,
|
|
FD_ACCEPT
|
|
);
|
|
|
|
if(SOCKET_ERROR == intRC) {
|
|
intRC = WSAGetLastError();
|
|
|
|
TRC_ERR((TB, _T("WSAAsyncSelect failed %d"), intRC));
|
|
hr = HRESULT_FROM_WIN32(intRC);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
sockAddr.sin_family = AF_INET;
|
|
sockAddr.sin_port = htons(port);
|
|
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);;
|
|
|
|
intRC = bind( m_ListenSocket, (struct sockaddr *) &sockAddr, sizeof(sockAddr) );
|
|
if( SOCKET_ERROR == intRC ) {
|
|
lastError = WSAGetLastError();
|
|
TRC_ERR((TB, _T("bind failed - %d"), lastError));
|
|
hr = HRESULT_FROM_WIN32( lastError );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( 0 == port ) {
|
|
//
|
|
// Retrieve which port we are listening
|
|
//
|
|
sockAddrSize = sizeof( sockAddr );
|
|
intRC = getsockname(
|
|
m_ListenSocket,
|
|
(struct sockaddr *)&sockAddr,
|
|
&sockAddrSize
|
|
);
|
|
if( SOCKET_ERROR == intRC )
|
|
{
|
|
lastError = WSAGetLastError();
|
|
TRC_ERR((TB, _T("getsockname failed - GLE:%d"),lastError));
|
|
hr = HRESULT_FROM_WIN32( lastError );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
m_ConnectedPort = ntohs(sockAddr.sin_port);
|
|
}
|
|
else {
|
|
m_ConnectedPort = port;
|
|
}
|
|
|
|
TRC_ERR((TB, _T("Listenin on port %d"),m_ConnectedPort));
|
|
|
|
//
|
|
// Tell ICS library to punch a hole thru ICS, no-op
|
|
// if not ICS configuration.
|
|
//
|
|
m_ICSPort = OpenPort( m_ConnectedPort );
|
|
|
|
//
|
|
// Retrieve connection parameters for this client (expert).
|
|
//
|
|
hr = RetrieveUserConnectParm( pConnectParm );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, _T("RetrieveUserConnectParm failed - 0x%08x"),hr));
|
|
}
|
|
|
|
//
|
|
// Log SESSMGR_I_CREATEXPERTTICKET Event, if we can't current user,
|
|
// stoplisten and return error
|
|
//
|
|
hr = GetCurrentUser( &pszUserName );
|
|
if( SUCCEEDED(hr) ) {
|
|
|
|
eventString[0] = pszUserName;
|
|
eventString[1] = *pConnectParm;
|
|
|
|
LogRemoteAssistanceEventString(
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
SESSMGR_I_CREATEXPERTTICKET,
|
|
2,
|
|
eventString
|
|
);
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if( pszUserName != NULL ) {
|
|
LocalFree(pszUserName);
|
|
}
|
|
|
|
if( FAILED(hr) ) {
|
|
StopListen();
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::StopListenInternal(
|
|
LONG returnCode
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Stop listening waiting for TS server (helpee, user) to connect.
|
|
|
|
This is an internal version for dealing with calls from external
|
|
and internal contexts.
|
|
|
|
Parameters:
|
|
|
|
returnCode - If non-zero then we return it back to the client
|
|
in the ListenConnect event callback. Otherwise, we will
|
|
return SAFERROR_STOPLISTENBYUSER.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::StopListenInternal");
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
// End listening, either we are actually issued a listen() to socket
|
|
// or we just created the listen socket but not yet start listening
|
|
if( TRUE == ListenConnectInProgress() || INVALID_SOCKET != m_ListenSocket ) {
|
|
ListenConnectCleanup();
|
|
Fire_ListenConnect((returnCode != 0) ? returnCode : SAFERROR_STOPLISTENBYUSER);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, _T("StopListen called while not in listen mode")));
|
|
hr = HRESULT_FROM_WIN32( WSANOTINITIALISED );
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
|
|
LRESULT
|
|
CTSRDPRemoteDesktopClient::OnTSConnect(
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL& bHandled
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Window Message Handler FD_ACCEPT from async. winsock.
|
|
|
|
Parameters:
|
|
|
|
Refer to async. winsock FD_ACCEPT.
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
WORD eventWSA;
|
|
WORD errorWSA;
|
|
HRESULT hr = S_OK;
|
|
SOCKADDR_IN inSockAddr;
|
|
int inSockAddrSize;
|
|
SOCKET s;
|
|
DWORD dwStatus;
|
|
|
|
DWORD SafErrorCode = SAFERROR_NOERROR;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnTSConnect");
|
|
|
|
eventWSA = WSAGETSELECTEVENT(lParam);
|
|
errorWSA = WSAGETSELECTERROR(lParam);
|
|
|
|
//
|
|
// MSDN : Message might already in our queue before we stop listen.
|
|
//
|
|
if( INVALID_SOCKET == m_ListenSocket || FALSE == ListenConnectInProgress() ) {
|
|
bHandled = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// we are not expecting event other than FD_CONNECT
|
|
//
|
|
if( eventWSA != FD_ACCEPT ) {
|
|
TRC_ERR((TB, _T("Expecting event %d got got %d"), FD_CONNECT, eventWSA));
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Make sure we don't do anything other than our own socket
|
|
//
|
|
if( (SOCKET)wParam != m_ListenSocket ) {
|
|
TRC_ERR((TB, _T("Expecting listening socket %d got %d"), m_ListenSocket, wParam));
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// We handle the message
|
|
//
|
|
bHandled = TRUE;
|
|
|
|
//
|
|
// Error occurred, fire a error event.
|
|
//
|
|
if( 0 != errorWSA ) {
|
|
TRC_ERR((TB, _T("WSA socket listen failed : %d"), errorWSA));
|
|
hr = HRESULT_FROM_WIN32( errorWSA );
|
|
SafErrorCode = SAFERROR_SOCKETCONNECTFAILED;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
inSockAddrSize = sizeof(inSockAddr);
|
|
m_TSConnectSocket = accept( m_ListenSocket,
|
|
(struct sockaddr DCPTR)&inSockAddr,
|
|
&inSockAddrSize
|
|
);
|
|
|
|
if( INVALID_SOCKET == m_TSConnectSocket ) {
|
|
dwStatus = WSAGetLastError();
|
|
hr = HRESULT_FROM_WIN32(dwStatus);
|
|
TRC_ERR((TB, _T("accept failed : %d"), dwStatus));
|
|
SafErrorCode = SAFERROR_SOCKETCONNECTFAILED;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Cached connecting TS server IP address.
|
|
// m_ConnectPort is set at the time we bind socket
|
|
//
|
|
m_ConnectedServer = inet_ntoa(inSockAddr.sin_addr);
|
|
|
|
|
|
//
|
|
// Stop async. event notification now, accepted socket
|
|
// has same properties as original listening socket.
|
|
//
|
|
dwStatus = WSAAsyncSelect(
|
|
m_TSConnectSocket,
|
|
m_hWnd,
|
|
0,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Not critical,
|
|
// listening socket.
|
|
//
|
|
if((DWORD)SOCKET_ERROR == dwStatus) {
|
|
TRC_ERR((TB, _T("WSAAsyncSelect resetting notification failed : %d"), dwStatus));
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
//
|
|
// Close listening socket and kill timer.
|
|
//
|
|
if( (UINT_PTR)0 != m_ListenTimeoutTimerID )
|
|
{
|
|
KillTimer( m_ListenTimeoutTimerID );
|
|
m_ListenTimeoutTimerID = (UINT_PTR)0;
|
|
}
|
|
|
|
if( INVALID_SOCKET != m_ListenSocket )
|
|
{
|
|
closesocket( m_ListenSocket );
|
|
m_ListenSocket = INVALID_SOCKET;
|
|
}
|
|
|
|
//
|
|
// Successfully established connection, terminate listening socket
|
|
//
|
|
Fire_ListenConnect( SafErrorCode );
|
|
|
|
DC_END_FN();
|
|
return 0;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::StartListen(
|
|
/*[in]*/ LONG timeout
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Put client into listen mode with optionally timeout.
|
|
|
|
Parameters:
|
|
|
|
timeout : Listen wait timeout, 0 for infinite.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnTSConnect");
|
|
HRESULT hr = S_OK;
|
|
int intRC;
|
|
int lastError;
|
|
|
|
if( FALSE == IsValid() ) {
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( INVALID_SOCKET == m_ListenSocket ) {
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Start listening on the port
|
|
//
|
|
intRC = listen( m_ListenSocket, SOMAXCONN );
|
|
if( SOCKET_ERROR == intRC )
|
|
{
|
|
lastError = WSAGetLastError();
|
|
TRC_ERR((TB, _T("listen failed - GLE:%d"), lastError));
|
|
hr = HRESULT_FROM_WIN32( lastError );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// we are in listening now.
|
|
//
|
|
m_ListenConnectInProgress = TRUE;
|
|
|
|
//
|
|
// Start listening timer
|
|
//
|
|
if( 0 != timeout )
|
|
{
|
|
m_ListenTimeoutTimerID = SetTimer( (UINT_PTR)WM_LISTENTIMEOUT_TIMER, (UINT)(timeout * 1000) );
|
|
if( (UINT_PTR)0 == m_ListenTimeoutTimerID )
|
|
{
|
|
DWORD dwStatus;
|
|
|
|
// Failed to create a timer
|
|
dwStatus = GetLastError();
|
|
|
|
TRC_ERR((TB, _T("SetTimer failed - %d"),dwStatus));
|
|
hr = HRESULT_FROM_WIN32( dwStatus );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_ListenTimeoutTimerID = (UINT_PTR)0;
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if( FAILED(hr) ) {
|
|
StopListen();
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::RetrieveUserConnectParm(
|
|
BSTR* pConnectParm
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve Salem connection parameter to this expert.
|
|
|
|
Parameters:
|
|
|
|
pConnectParm : Pointer to BSTR to receive connect parm.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code.
|
|
|
|
--*/
|
|
{
|
|
LPTSTR pszAddress = NULL;
|
|
int BufSize = 0;
|
|
CComBSTR bstrConnParm;
|
|
DWORD dwRetry;
|
|
HRESULT hRes;
|
|
DWORD dwBufferRequire;
|
|
DWORD dwNumChars;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::RetrieveUserConnectParm");
|
|
|
|
if( NULL == pConnectParm )
|
|
{
|
|
hRes = E_POINTER;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Address might have change which might require bigger buffer, retry
|
|
//
|
|
//
|
|
for(dwRetry=0; dwRetry < MAX_FETCHIPADDRESSRETRY; dwRetry++) {
|
|
|
|
if( NULL != pszAddress ) {
|
|
LocalFree( pszAddress );
|
|
}
|
|
|
|
//
|
|
// Fetch all address on local machine.
|
|
//
|
|
dwBufferRequire = FetchAllAddresses( NULL, 0 );
|
|
if( 0 == dwBufferRequire ) {
|
|
hRes = E_UNEXPECTED;
|
|
ASSERT(FALSE);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
pszAddress = (LPTSTR) LocalAlloc( LPTR, sizeof(TCHAR)*(dwBufferRequire+1) );
|
|
if( NULL == pszAddress ) {
|
|
hRes = E_OUTOFMEMORY;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
dwNumChars = FetchAllAddresses( pszAddress, dwBufferRequire );
|
|
ASSERT( dwNumChars <= dwBufferRequire );
|
|
if( dwNumChars <= dwBufferRequire ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( NULL == pszAddress ) {
|
|
hRes = E_UNEXPECTED;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
bstrConnParm = pszAddress;
|
|
*pConnectParm = bstrConnParm.Copy();
|
|
|
|
if( NULL == *pConnectParm ) {
|
|
hRes = E_OUTOFMEMORY;
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if( NULL != pszAddress ) {
|
|
LocalFree(pszAddress);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hRes;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::put_ColorDepth(
|
|
LONG val
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set Color depth
|
|
|
|
Arguments:
|
|
|
|
val - Value in bits perpel to set
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_ColorDepth");
|
|
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->put_ColorDepth(val);
|
|
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"put_ColorDepth: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTSRDPRemoteDesktopClient::get_ColorDepth(
|
|
LONG *pVal
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get Color depth
|
|
|
|
Arguments:
|
|
|
|
pVal - address to place the colordepth value in
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::get_ColorDepth");
|
|
|
|
if (!IsValid()) {
|
|
ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = m_TSClient->get_ColorDepth(pVal);
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"get_ColorDepth: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CTSRDPRemoteDesktopClient::GetCurrentUser(
|
|
LPTSTR* ppszUserName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return currently logon user name in the form of
|
|
Windows NT® 4.0 account name (for example, Engineering\JSmith).
|
|
|
|
Parameters:
|
|
|
|
ppszUserName : Pointer to pointer to receive currently logon user name, use LocalFree()
|
|
to free memory.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
LPTSTR pszUserNameBuf = NULL;
|
|
DWORD userNameBufSize = 0;
|
|
BOOLEAN succeeded;
|
|
|
|
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GetCurrentUser");
|
|
|
|
succeeded = GetUserNameEx( NameSamCompatible, pszUserNameBuf, &userNameBufSize );
|
|
if( FALSE == succeeded ) {
|
|
status = GetLastError();
|
|
if( ERROR_MORE_DATA != status ) {
|
|
TRC_ERR((TB, _T("GetUserNameEx failed - 0x%08x"), status));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
}
|
|
|
|
pszUserNameBuf = (LPTSTR)LocalAlloc( LPTR, sizeof(TCHAR)*(userNameBufSize+1) );
|
|
if( NULL == pszUserNameBuf ) {
|
|
// out of memory
|
|
status = GetLastError();
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
succeeded = GetUserNameEx( NameSamCompatible, pszUserNameBuf, &userNameBufSize );
|
|
if( FALSE == succeeded ) {
|
|
status = GetLastError();
|
|
TRC_ERR((TB, _T("GetUserNameEx failed - 0x%08x"),status));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
status = ERROR_SUCCESS;
|
|
|
|
*ppszUserName = pszUserNameBuf;
|
|
pszUserNameBuf = NULL;
|
|
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if( NULL != pszUserNameBuf ) {
|
|
LocalFree(pszUserNameBuf);
|
|
}
|
|
|
|
DC_END_FN();
|
|
|
|
return HRESULT_FROM_WIN32(status);
|
|
}
|
|
|