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.
1554 lines
44 KiB
1554 lines
44 KiB
/*++
|
|
|
|
Copyright (c) 1999-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
RDPRemoteDesktopServerHost
|
|
|
|
Abstract:
|
|
|
|
This module contains the CRemoteDesktopServerHost implementation
|
|
of RDS session objects.
|
|
|
|
It manages a collection of open ISAFRemoteDesktopSession objects.
|
|
New RDS session objects instances are created using the CreateRemoteDesktopSession
|
|
method. Existing RDS session objects are opened using the OpenRemoteDesktopSession
|
|
method. RDS session objects are closed using the CloseRemoteDesktopSession method.
|
|
|
|
When an RDS object is opened or created, the CRemoteDesktopServerHost
|
|
object adds a reference of its own to the object so that the object will
|
|
stay around, even if the opening application exits. This reference
|
|
is retracted when the application calls the CloseRemoteDesktopSession method.
|
|
|
|
In addition to the reference count the CRemoteDesktopServerHost adds to
|
|
the RDS session object, a reference count is also added to itself so that
|
|
the associated exe continues to run while RDS session objects are active.
|
|
|
|
Author:
|
|
|
|
Tad Brockway 02/00
|
|
|
|
Revision History:
|
|
|
|
Aug 3, 01 HueiWang
|
|
|
|
Add ticket expiration logic, reason is sessmgr will expire the ticket and
|
|
rdshost never got inform of this action so it will keep ticket object open
|
|
until reboot or user/caller ever try to open the ticket again, this cause leak
|
|
in rdshost and sessmgr since ticket object (CRemoteDesktopSession) has a reference
|
|
count on sessmgr's IRemoteDesktopHelpSession object.
|
|
|
|
We have different ways to implement expiration logic, waitable timer or event via
|
|
threadpool or simple windows WM_TIMER message, for waitable timer, threads owns timer
|
|
must persist (MSDN), WM_TIMER is simpliest but WM_TIMER procedure does not take user
|
|
define data as parameter which will require us to store server host object in _Module,
|
|
this works fine with STA and SINGLETON but if we change to MTA, we would get into
|
|
trouble.
|
|
|
|
--*/
|
|
|
|
//#include <RemoteDesktop.h>
|
|
|
|
#include "stdafx.h"
|
|
|
|
#ifdef TRC_FILE
|
|
#undef TRC_FILE
|
|
#endif
|
|
|
|
#define TRC_FILE "_svrhst"
|
|
#include "RemoteDesktopUtils.h"
|
|
#include "parseaddr.h"
|
|
#include "RDSHost.h"
|
|
#include "RemoteDesktopServerHost.h"
|
|
#include "TSRDPRemoteDesktopSession.h"
|
|
#include "rderror.h"
|
|
|
|
|
|
CRemoteDesktopServerHost* g_pRemoteDesktopServerHostObj = NULL;
|
|
|
|
void
|
|
CRemoteDesktopServerHost::RemoteDesktopDisabled()
|
|
/*++
|
|
Routine Description:
|
|
|
|
Function to disconnect all connections because RA is disabled.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
SessionMap::iterator iter;
|
|
SessionMap::iterator iter_delete;
|
|
//
|
|
// Cleanup m_SessionMap entries.
|
|
//
|
|
iter = m_SessionMap.begin();
|
|
while( iter != m_SessionMap.end() ) {
|
|
|
|
if( NULL != (*iter).second ) {
|
|
//
|
|
// We are shutting down, fire disconnect to all client
|
|
//
|
|
if( NULL != (*iter).second->obj ) {
|
|
(*iter).second->obj->Disconnect();
|
|
(*iter).second->obj->Release();
|
|
}
|
|
|
|
delete (*iter).second;
|
|
(*iter).second = NULL;
|
|
}
|
|
|
|
iter_delete = iter;
|
|
iter++;
|
|
m_SessionMap.erase(iter_delete);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
//
|
|
// CRemoteDesktopServerHost Methods
|
|
//
|
|
|
|
HRESULT
|
|
CRemoteDesktopServerHost::FinalConstruct()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::FinalConstruct");
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD status;
|
|
|
|
ASSERT( m_hTicketExpiration == NULL );
|
|
ASSERT( g_pRemoteDesktopServerHostObj == NULL );
|
|
|
|
//
|
|
// We are singleton object so cache this object for RA policy change.
|
|
//
|
|
g_pRemoteDesktopServerHostObj = this;
|
|
|
|
//
|
|
// Create manual event to expire ticket.
|
|
//
|
|
m_hTicketExpiration = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if( NULL == m_hTicketExpiration ) {
|
|
status = GetLastError();
|
|
hr = HRESULT_FROM_WIN32( status );
|
|
TRC_ERR((TB, L"CreateEvent: %08X", hr));
|
|
ASSERT( FALSE );
|
|
}
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
CRemoteDesktopServerHost::~CRemoteDesktopServerHost()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::~CRemoteDesktopServerHost");
|
|
BOOL success;
|
|
|
|
//
|
|
// Clean up the local system SID.
|
|
//
|
|
if (m_LocalSystemSID != NULL) {
|
|
FreeSid(m_LocalSystemSID);
|
|
}
|
|
|
|
if( NULL != m_hTicketExpirationWaitObject ) {
|
|
success = UnregisterWait( m_hTicketExpirationWaitObject );
|
|
ASSERT( TRUE == success );
|
|
if( FALSE == success ) {
|
|
TRC_ERR((TB, L"UnregisterWait: %08X", GetLastError()));
|
|
|
|
//
|
|
// MSDN on RegisterWaitForSingleObject(),
|
|
//
|
|
// If this handle (m_hTicketExpiration) is closed while
|
|
// the wait is still pending, the function's behavior
|
|
// is undefined.
|
|
//
|
|
// So we ignore closing m_hTicketExpiration and exit.
|
|
//
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close our expiration handle
|
|
//
|
|
if( NULL != m_hTicketExpiration ) {
|
|
CloseHandle( m_hTicketExpiration );
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CRemoteDesktopServerHost::CreateRemoteDesktopSession(
|
|
REMOTE_DESKTOP_SHARING_CLASS sharingClass,
|
|
BOOL fEnableCallback,
|
|
LONG timeOut,
|
|
BSTR userHelpBlob,
|
|
ISAFRemoteDesktopSession **session
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a new RDS session
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::CreateRemoteDesktopSession");
|
|
HRESULT hr;
|
|
|
|
|
|
CComBSTR bstr; bstr = "";
|
|
hr = CreateRemoteDesktopSessionEx(
|
|
sharingClass,
|
|
fEnableCallback,
|
|
timeOut,
|
|
userHelpBlob,
|
|
-1,
|
|
bstr,
|
|
session
|
|
);
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CRemoteDesktopServerHost::CreateRemoteDesktopSessionEx(
|
|
REMOTE_DESKTOP_SHARING_CLASS sharingClass,
|
|
BOOL bEnableCallback,
|
|
LONG timeout,
|
|
BSTR userHelpCreateBlob,
|
|
LONG tsSessionID,
|
|
BSTR userSID,
|
|
ISAFRemoteDesktopSession **session
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a new RDS session
|
|
Note that the caller MUST call OpenRemoteDesktopSession() subsequent to
|
|
a successful completion of this call.
|
|
The connection does NOT happen until OpenRemoteDesktopSession() is called.
|
|
This call just initializes certain data, it does not open a connection
|
|
|
|
Arguments:
|
|
|
|
sharingClass - Desktop Sharing Class
|
|
fEnableCallback - TRUE if the Resolver is Enabled
|
|
timeOut - Lifespan of Remote Desktop Session
|
|
userHelpBlob - Optional User Blob to be Passed
|
|
to Resolver.
|
|
tsSessionID - Terminal Services Session ID or -1 if
|
|
undefined.
|
|
userSID - User SID or "" if undefined.
|
|
session - Returned Remote Desktop Session Interface.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::CreateRemoteDesktopSessionEx");
|
|
|
|
HRESULT hr = S_OK;
|
|
HRESULT hr_tmp;
|
|
CComObject<CRemoteDesktopSession> *obj = NULL;
|
|
PSESSIONMAPENTRY mapEntry;
|
|
PSID psid;
|
|
DWORD ticketExpireTime;
|
|
|
|
|
|
//
|
|
// Get the local system SID.
|
|
//
|
|
psid = GetLocalSystemSID();
|
|
if (psid == NULL) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Need to impersonate the caller in order to determine if it is
|
|
// running in SYSTEM context.
|
|
//
|
|
hr = CoImpersonateClient();
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"CoImpersonateClient: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// For Whistler, instances of a Remote Desktop Session are only
|
|
// "openable" from SYSTEM context, for security reasons.
|
|
//
|
|
#ifndef DISABLESECURITYCHECKS
|
|
if (!IsCallerSystem(psid)) {
|
|
TRC_ERR((TB, L"Caller is not SYSTEM."));
|
|
ASSERT(FALSE);
|
|
CoRevertToSelf();
|
|
hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
#endif
|
|
hr = CoRevertToSelf();
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"CoRevertToSelf: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( sharingClass != DESKTOPSHARING_DEFAULT &&
|
|
sharingClass != NO_DESKTOP_SHARING &&
|
|
sharingClass != VIEWDESKTOP_PERMISSION_REQUIRE &&
|
|
sharingClass != VIEWDESKTOP_PERMISSION_NOT_REQUIRE &&
|
|
sharingClass != CONTROLDESKTOP_PERMISSION_REQUIRE &&
|
|
sharingClass != CONTROLDESKTOP_PERMISSION_NOT_REQUIRE ) {
|
|
|
|
// invalid parameter.
|
|
hr = E_INVALIDARG;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( timeout <= 0 ) {
|
|
|
|
hr = E_INVALIDARG;
|
|
goto CLEANUPANDEXIT;
|
|
|
|
}
|
|
|
|
if( NULL == session ) {
|
|
|
|
hr = E_POINTER;
|
|
goto CLEANUPANDEXIT;
|
|
|
|
}
|
|
|
|
//
|
|
// Instantiate the desktop server. Currently, we only support
|
|
// TSRDP.
|
|
//
|
|
obj = new CComObject<CTSRDPRemoteDesktopSession>();
|
|
if (obj != NULL) {
|
|
|
|
//
|
|
// ATL would normally take care of this for us.
|
|
//
|
|
obj->InternalFinalConstructAddRef();
|
|
hr = obj->FinalConstruct();
|
|
obj->InternalFinalConstructRelease();
|
|
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"Can't instantiate CTSRDPRemoteDesktopServer"));
|
|
hr = E_OUTOFMEMORY;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Initialize the object.
|
|
//
|
|
hr = obj->Initialize(
|
|
NULL, this, sharingClass, bEnableCallback,
|
|
timeout, userHelpCreateBlob, tsSessionID, userSID
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = obj->get_ExpireTime( &ticketExpireTime );
|
|
if( FAILED(hr) ) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( ticketExpireTime < (DWORD)time(NULL) ) {
|
|
// ticket already expired, no need to continue,
|
|
// overactive assert here is just to check we
|
|
// should never come to this.
|
|
hr = E_INVALIDARG;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Add it to the session map.
|
|
//
|
|
mapEntry = new SESSIONMAPENTRY();
|
|
if (mapEntry == NULL) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
mapEntry->obj = obj;
|
|
mapEntry->ticketExpireTime = ticketExpireTime;
|
|
|
|
try {
|
|
m_SessionMap.insert(
|
|
SessionMap::value_type(obj->GetHelpSessionID(), mapEntry)
|
|
);
|
|
}
|
|
catch(CRemoteDesktopException x) {
|
|
hr = HRESULT_FROM_WIN32(x.m_ErrorCode);
|
|
delete mapEntry;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Get the ISAFRemoteDesktopSession interface pointer.
|
|
//
|
|
hr = obj->QueryInterface(
|
|
IID_ISAFRemoteDesktopSession,
|
|
(void**)session
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
//
|
|
// TODO : remove from m_SessionMap, this should never
|
|
// failed but just in case, then we would have orphan object
|
|
// in the m_SessionMap which might cause AV when we loop for
|
|
// next ticket to expire
|
|
//
|
|
TRC_ERR((TB, L"m_RemoteDesktopSession->QueryInterface: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
|
|
//
|
|
// Add a reference to the object and to ourself so we can both
|
|
// stick around, even if the app goes away. The app needs to explicitly
|
|
// call CloseRemoteDesktopSession for the object to go away.
|
|
//
|
|
obj->AddRef();
|
|
|
|
long count;
|
|
count = this->AddRef();
|
|
TRC_NRM((TB, TEXT("CreateRemoteDesktopSessionEx AddRef SrvHost count: %08X %08X"), count, m_SessionMap.size()));
|
|
|
|
//
|
|
// Added ticket in expiration monitor list, if anything goes wrong,
|
|
// we still can function, just no expiration running until next
|
|
// CreateXXX, OpenXXX or CloseXXX call.
|
|
//
|
|
hr_tmp = AddTicketToExpirationList( ticketExpireTime, obj );
|
|
if( FAILED(hr_tmp) ) {
|
|
TRC_ERR((TB, L"AddTicketToExpirationList failed : %08X", hr));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
//
|
|
// Delete the object on error.
|
|
//
|
|
if (!SUCCEEDED(hr)) {
|
|
if (obj != NULL) delete obj;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open an existing RDS session
|
|
This call should ALWAYS be made in order to connect to the client
|
|
Once this is called and connection is complete, the caller
|
|
MUST call DisConnect() to make another connection to the client
|
|
Otherwise, the connection does not happen
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
STDMETHODIMP
|
|
CRemoteDesktopServerHost::OpenRemoteDesktopSession(
|
|
BSTR parms,
|
|
BSTR userSID,
|
|
ISAFRemoteDesktopSession **session
|
|
)
|
|
{
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::OpenRemoteDesktopSession");
|
|
|
|
CComObject<CRemoteDesktopSession> *obj = NULL;
|
|
CComBSTR hostname;
|
|
CComBSTR tmp("");
|
|
HRESULT hr = S_OK;
|
|
HRESULT hr_tmp;
|
|
SessionMap::iterator iter;
|
|
CComBSTR parmsHelpSessionId;
|
|
DWORD protocolType;
|
|
PSESSIONMAPENTRY mapEntry;
|
|
PSID psid;
|
|
DWORD ticketExpireTime;
|
|
VARIANT_BOOL bUserIsOwner;
|
|
|
|
//
|
|
// Get the local system SID.
|
|
//
|
|
psid = GetLocalSystemSID();
|
|
if (psid == NULL) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Need to impersonate the caller in order to determine if it is
|
|
// running in SYSTEM context.
|
|
//
|
|
hr = CoImpersonateClient();
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"CoImpersonateClient: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// For Whistler, instances of a Remote Desktop Session are only
|
|
// "openable" from SYSTEM context, for security reasons.
|
|
//
|
|
#ifndef DISABLESECURITYCHECKS
|
|
if (!IsCallerSystem(psid)) {
|
|
TRC_ERR((TB, L"Caller is not SYSTEM."));
|
|
ASSERT(FALSE);
|
|
CoRevertToSelf();
|
|
hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
#endif
|
|
hr = CoRevertToSelf();
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"CoRevertToSelf: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Parse out the help session ID.
|
|
// TODO: Need to modify this so some of the parms are
|
|
// optional.
|
|
//
|
|
DWORD dwVersion;
|
|
DWORD result = ParseConnectParmsString(
|
|
parms, &dwVersion, &protocolType, hostname, tmp, tmp,
|
|
parmsHelpSessionId, tmp, tmp, tmp
|
|
);
|
|
if (result != ERROR_SUCCESS) {
|
|
hr = HRESULT_FROM_WIN32(result);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// If we already have the session open then just return a
|
|
// reference.
|
|
//
|
|
iter = m_SessionMap.find(parmsHelpSessionId);
|
|
|
|
// refer to DeleteRemoteDesktopSession() why we keep this entry
|
|
// in m_SessionMap and check for (*iter).second
|
|
if (iter != m_SessionMap.end()) {
|
|
mapEntry = (*iter).second;
|
|
|
|
if( mapEntry == NULL || mapEntry->ticketExpireTime <= time(NULL) ) {
|
|
// ticket already expired or about to expire, return
|
|
// error and let expiration to take care of ticket.
|
|
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( FALSE == mapEntry->obj->CheckAccessRight(userSID) ) {
|
|
TRC_ERR((TB, L"CheckAccessRight return FALSE"));
|
|
ASSERT( FALSE );
|
|
hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = mapEntry->obj->QueryInterface(
|
|
IID_ISAFRemoteDesktopSession,
|
|
(void**)session
|
|
);
|
|
//
|
|
//Start listening if we succeeded
|
|
//
|
|
if (SUCCEEDED(hr)) {
|
|
hr = mapEntry->obj->StartListening();
|
|
//
|
|
//release the interface pointer if we didn't succeed
|
|
//
|
|
if (!SUCCEEDED(hr)) {
|
|
(*session)->Release();
|
|
*session = NULL;
|
|
}
|
|
}
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Instantiate the desktop server. Currently, we only support
|
|
// TSRDP.
|
|
//
|
|
obj = new CComObject<CTSRDPRemoteDesktopSession>();
|
|
if (obj != NULL) {
|
|
|
|
//
|
|
// ATL would normally take care of this for us.
|
|
//
|
|
obj->InternalFinalConstructAddRef();
|
|
hr = obj->FinalConstruct();
|
|
obj->InternalFinalConstructRelease();
|
|
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"Can't instantiate CTSRDPRemoteDesktopServer"));
|
|
hr = E_OUTOFMEMORY;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Initialize the object.
|
|
//
|
|
// The desktop sharing parameter (NO_DESKTOP_SHARING) is ignored for
|
|
// an existing session.
|
|
// bEnableCallback and timeout parameter is ignored for existing session
|
|
//
|
|
hr = obj->Initialize(parms, this, NO_DESKTOP_SHARING, TRUE, 0, CComBSTR(L""), -1, userSID);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = obj->StartListening();
|
|
|
|
if (!SUCCEEDED(hr)) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = obj->UseHostName( hostname );
|
|
if( FAILED(hr) ) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
hr = obj->get_ExpireTime( &ticketExpireTime );
|
|
if( FAILED(hr) ) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( ticketExpireTime < (DWORD)time(NULL) ) {
|
|
// ticket already expired, no need to continue,
|
|
// overactive assert here is just to check we
|
|
// should never come to this.
|
|
ASSERT(FALSE);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Add it to the session map.
|
|
//
|
|
mapEntry = new SESSIONMAPENTRY();
|
|
if (mapEntry == NULL) {
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
mapEntry->obj = obj;
|
|
mapEntry->ticketExpireTime = ticketExpireTime;
|
|
|
|
ASSERT( obj->GetHelpSessionID() == parmsHelpSessionId );
|
|
|
|
try {
|
|
m_SessionMap.insert(
|
|
SessionMap::value_type(obj->GetHelpSessionID(), mapEntry)
|
|
);
|
|
}
|
|
catch(CRemoteDesktopException x) {
|
|
hr = HRESULT_FROM_WIN32(x.m_ErrorCode);
|
|
delete mapEntry;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Get the ISAFRemoteDesktopSession interface pointer.
|
|
//
|
|
hr = obj->QueryInterface(
|
|
IID_ISAFRemoteDesktopSession,
|
|
(void**)session
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
//
|
|
// TODO : remove from m_SessionMap, this should never
|
|
// failed but just in case, then we would have orphan object
|
|
// in the m_SessionMap which might cause AV when we loop for
|
|
// next ticket to expire
|
|
//
|
|
TRC_ERR((TB, L"m_RemoteDesktopSession->QueryInterface: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Add a reference to the object and to ourself so we can both
|
|
// stick around, even if the app goes away. The app needs to explicitly
|
|
// call CloseRemoteDesktopSession for the object to go away.
|
|
//
|
|
obj->AddRef();
|
|
|
|
long count;
|
|
count = this->AddRef();
|
|
TRC_NRM((TB, TEXT("OpenRemoteDesktopSession AddRef SrvHost count: %08X %08X"), count, m_SessionMap.size()));
|
|
|
|
|
|
//
|
|
// Added ticket in expiration monitor list, if anything goes wrong,
|
|
// we still can function, just no expiration running until next
|
|
// CreateXXX, OpenXXX or CloseXXX call.
|
|
//
|
|
hr_tmp = AddTicketToExpirationList( ticketExpireTime, obj );
|
|
if( FAILED(hr_tmp) ) {
|
|
TRC_ERR((TB, L"AddTicketToExpirationList failed : %08X", hr));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
//
|
|
// Delete the object on error.
|
|
//
|
|
if (!SUCCEEDED(hr)) {
|
|
if (obj != NULL) delete obj;
|
|
}
|
|
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CRemoteDesktopServerHost::CloseRemoteDesktopSession(
|
|
ISAFRemoteDesktopSession *session
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close an existing RDS session
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::CloseRemoteDesktopSession");
|
|
|
|
hr = DeleteRemoteDesktopSession(session);
|
|
|
|
//
|
|
// Can't call ExpirateTicketAndSetupNextExpiration() because
|
|
// ExpirateTicketAndSetupNextExpiration() might have outgoing
|
|
// COM call and COM will pump message causing COM re-entry.
|
|
// also for performance reason, we don't want to post
|
|
// more than on WM_TICKETEXPIRED message until we have it
|
|
// processed
|
|
//
|
|
if( !GetExpireMsgPosted() ) {
|
|
//
|
|
// We need extra ref. counter here since we still reference
|
|
// CRemoteDesktopServerHost object in expiration,
|
|
//
|
|
long count;
|
|
|
|
count = this->AddRef();
|
|
TRC_NRM((TB, TEXT("CloseRemoteDesktopSession AddRef SrvHost count: %08X"), count));
|
|
|
|
SetExpireMsgPosted(TRUE);
|
|
PostThreadMessage(
|
|
_Module.dwThreadID,
|
|
WM_TICKETEXPIRED,
|
|
(WPARAM) 0,
|
|
(LPARAM) this
|
|
);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CRemoteDesktopServerHost::DeleteRemoteDesktopSession(
|
|
ISAFRemoteDesktopSession *session
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete an existing RDS session
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
S_OK on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::DeleteRemoteDesktopSession");
|
|
|
|
HRESULT hr;
|
|
HRESULT hr_tmp;
|
|
CComBSTR parmsHelpSessionId;
|
|
SessionMap::iterator iter;
|
|
long count;
|
|
|
|
//
|
|
// Get the connection parameters.
|
|
//
|
|
hr = session->get_HelpSessionId(&parmsHelpSessionId);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, TEXT("get_HelpSessionId: %08X"), hr));
|
|
|
|
//
|
|
// This is really bad, we will leave object hanging
|
|
// around in cache, not much can be done since our map
|
|
// entry is indexed on HelpSession ID
|
|
//
|
|
ASSERT(FALSE);
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
|
|
//
|
|
// Delete the entry from the session map.
|
|
//
|
|
iter = m_SessionMap.find(parmsHelpSessionId);
|
|
if (iter != m_SessionMap.end()) {
|
|
if( NULL != (*iter).second ) {
|
|
delete (*iter).second;
|
|
(*iter).second = NULL;
|
|
}
|
|
else {
|
|
// Ticket has been delete by expiration loop.
|
|
hr = S_OK;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Two CloseRemoteDesktopSession() re-enter calls while we are in expire loop
|
|
// causes AV. In expire loop, we go thru all entries in m_SessionMap, if
|
|
// entry is expired, we invoke DeleteRemoteDesktopSession() to delete entry
|
|
// in m_SessionMap but DeleteRemoteDesktopSession() makes outgoing COM call
|
|
// which permit incoming COM call so if during outgoing COM call, two consecutive
|
|
// CloseRemoteDesktopSession() re-enter calls, we will erase the iterator and
|
|
// causes AV. Need to keep this entry for expire loop to erase.
|
|
//
|
|
// m_SessionMap.erase(iter);
|
|
}
|
|
else {
|
|
//
|
|
// It's possible that we expire ticket
|
|
// from m_SessionMap but client still holding object.
|
|
//
|
|
|
|
//
|
|
// Cached entry has been deleted via expire loop which already
|
|
// release the associated AddRef() we put on session object and
|
|
// host object, also ticket has been deleted from session,
|
|
// so simply return S_OK
|
|
//
|
|
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Remove our reference to the session object. This way it can
|
|
// go away when the application releases it.
|
|
//
|
|
session->Release();
|
|
|
|
//
|
|
// Remove the reference to ourself that we added when we opened
|
|
// the session object.
|
|
//
|
|
count = this->Release();
|
|
|
|
TRC_NRM((TB, TEXT("DeleteRemoteDesktopSession Release SrvHost count: %08X"), count));
|
|
ASSERT( count >= 0 );
|
|
|
|
//
|
|
// Get the session manager interface, if we don't already have one.
|
|
//
|
|
//
|
|
// Open an instance of the Remote Desktop Help Session Manager service.
|
|
//
|
|
if (m_HelpSessionManager == NULL) {
|
|
hr = m_HelpSessionManager.CoCreateInstance(CLSID_RemoteDesktopHelpSessionMgr, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_DISABLE_AAA);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, TEXT("Can't create help session manager: %08X"), hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Set the security level to impersonate. This is required by
|
|
// the session manager.
|
|
//
|
|
hr = CoSetProxyBlanket(
|
|
(IUnknown *)m_HelpSessionManager,
|
|
RPC_C_AUTHN_DEFAULT,
|
|
RPC_C_AUTHZ_DEFAULT,
|
|
NULL,
|
|
RPC_C_AUTHN_LEVEL_DEFAULT,
|
|
RPC_C_IMP_LEVEL_IDENTIFY,
|
|
NULL,
|
|
EOAC_NONE
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
TRC_ERR((TB, TEXT("CoSetProxyBlanket: %08X"), hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the help session with the session manager.
|
|
//
|
|
hr = m_HelpSessionManager->DeleteHelpSession(parmsHelpSessionId);
|
|
if (!SUCCEEDED(hr)) {
|
|
if( HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr ) {
|
|
// HelpSession might have been expired by sessmgr, reset
|
|
// error code here.
|
|
hr = S_OK;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, L"DeleteHelpSession: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CRemoteDesktopServerHost::ConnectToExpert(
|
|
/*[in]*/ BSTR connectParmToExpert,
|
|
/*[in]*/ LONG timeout,
|
|
/*[out, retval]*/ LONG* pSafErrCode
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Given connection parameters to expert machine, routine invoke TermSrv winsta API to
|
|
initiate connection from TS server to TS client ActiveX control on the expert side.
|
|
|
|
Parameters:
|
|
|
|
connectParmToExpert : connection parameter to connect to expert machine.
|
|
timeout : Connection timeout, this timeout is per ip address listed in connection parameter
|
|
not total connection timeout for the routine.
|
|
pSafErrCode : Pointer to LONG to receive detail error code.
|
|
|
|
Returns:
|
|
|
|
S_OK or E_FAIL
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ServerAddress expertAddress;
|
|
ServerAddressList expertAddressList;
|
|
LONG SafErrCode = SAFERROR_NOERROR;
|
|
TDI_ADDRESS_IP expertTDIAddress;
|
|
ULONG netaddr;
|
|
WSADATA wsaData;
|
|
PSID psid;
|
|
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::ConnectToExpert");
|
|
|
|
//
|
|
// Get the local system SID.
|
|
//
|
|
psid = GetLocalSystemSID();
|
|
if (psid == NULL) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Need to impersonate the caller in order to determine if it is
|
|
// running in SYSTEM context.
|
|
//
|
|
hr = CoImpersonateClient();
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"CoImpersonateClient: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// For Whistler, instances of a Remote Desktop Session are only
|
|
// "openable" from SYSTEM context, for security reasons.
|
|
//
|
|
#ifndef DISABLESECURITYCHECKS
|
|
if (!IsCallerSystem(psid)) {
|
|
TRC_ERR((TB, L"Caller is not SYSTEM."));
|
|
ASSERT(FALSE);
|
|
CoRevertToSelf();
|
|
hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
#endif
|
|
|
|
hr = CoRevertToSelf();
|
|
if (hr != S_OK) {
|
|
TRC_ERR((TB, L"CoRevertToSelf: %08X", hr));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Parse address list in connection parameter.
|
|
//
|
|
hr = ParseAddressList( connectParmToExpert, expertAddressList );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, TEXT("ParseAddressList: %08X"), hr));
|
|
hr = E_INVALIDARG;
|
|
SafErrCode = SAFERROR_INVALIDPARAMETERSTRING;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
if( 0 == expertAddressList.size() ) {
|
|
TRC_ERR((TB, L"Invalid connection address list"));
|
|
SafErrCode = SAFERROR_INVALIDPARAMETERSTRING;
|
|
hr = E_INVALIDARG;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Loop thru all address in parm and try connection one
|
|
// at a time, bail out if system is shutting down or
|
|
// some critical error
|
|
//
|
|
while( expertAddressList.size() > 0 ) {
|
|
|
|
expertAddress = expertAddressList.front();
|
|
expertAddressList.pop_front();
|
|
|
|
//
|
|
// Invalid connect parameters, we must have port number at least.
|
|
//
|
|
if( 0 == expertAddress.portNumber ||
|
|
0 == lstrlen(expertAddress.ServerName) ) {
|
|
TRC_ERR((TB, L"Invalid address/port %s %d", expertAddress.ServerName, expertAddress.portNumber));
|
|
SafErrCode = SAFERROR_INVALIDPARAMETERSTRING;
|
|
continue;
|
|
}
|
|
|
|
hr = TranslateStringAddress( expertAddress.ServerName, &netaddr );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, L"TranslateStringAddress() on %s failed with 0x%08x", expertAddress.ServerName, hr));
|
|
SafErrCode = SAFERROR_INVALIDPARAMETERSTRING;
|
|
continue;
|
|
}
|
|
|
|
ZeroMemory(&expertTDIAddress, TDI_ADDRESS_LENGTH_IP);
|
|
expertTDIAddress.in_addr = netaddr;
|
|
expertTDIAddress.sin_port = htons(expertAddress.portNumber);
|
|
|
|
if( FALSE == WinStationConnectCallback(
|
|
SERVERNAME_CURRENT,
|
|
timeout,
|
|
TDI_ADDRESS_TYPE_IP,
|
|
(PBYTE)&expertTDIAddress,
|
|
TDI_ADDRESS_LENGTH_IP
|
|
) ) {
|
|
//
|
|
// TransferConnectionToIdleWinstation() in TermSrv might just return -1
|
|
// few of them we need to bail out.
|
|
|
|
DWORD dwStatus;
|
|
|
|
dwStatus = GetLastError();
|
|
if( ERROR_SHUTDOWN_IN_PROGRESS == dwStatus ) {
|
|
// system or termsrv is shuting down.
|
|
hr = HRESULT_FROM_WIN32( ERROR_SHUTDOWN_IN_PROGRESS );
|
|
SafErrCode = SAFERROR_SYSTEMSHUTDOWN;
|
|
break;
|
|
}
|
|
else if( ERROR_ACCESS_DENIED == dwStatus ) {
|
|
// security check failed
|
|
hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
|
|
SafErrCode = SAFERROR_BYSERVER;
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
else if( ERROR_INVALID_PARAMETER == dwStatus ) {
|
|
// internal error in rdshost.
|
|
hr = HRESULT_FROM_WIN32( ERROR_INTERNAL_ERROR );
|
|
SafErrCode = SAFERROR_INTERNALERROR;
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
SafErrCode = SAFERROR_WINSOCK_FAILED;
|
|
}
|
|
else {
|
|
//
|
|
// successful connection
|
|
//
|
|
|
|
SafErrCode = SAFERROR_NOERROR;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Try next connection.
|
|
//
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
*pSafErrCode = SafErrCode;
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CRemoteDesktopServerHost::TranslateStringAddress(
|
|
IN LPTSTR pszAddress,
|
|
OUT ULONG* pNetAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Translate IP Address or machine name to network address.
|
|
|
|
Parameters:
|
|
|
|
pszAddress : Pointer to IP address or machine name.
|
|
pNetAddr : Point to ULONG to receive address in IPV4.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
unsigned long addr;
|
|
LPSTR pszAnsiAddress = NULL;
|
|
DWORD dwAddressBufSize;
|
|
DWORD dwStatus;
|
|
|
|
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::TranslateStringAddress");
|
|
|
|
|
|
dwAddressBufSize = lstrlen(pszAddress) + 1;
|
|
pszAnsiAddress = (LPSTR)LocalAlloc(LPTR, dwAddressBufSize); // converting from WCHAR to CHAR.
|
|
if( NULL == pszAnsiAddress ) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Convert wide char to ANSI string
|
|
//
|
|
dwStatus = WideCharToMultiByte(
|
|
GetACP(),
|
|
0,
|
|
pszAddress,
|
|
-1,
|
|
pszAnsiAddress,
|
|
dwAddressBufSize,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if( 0 == dwStatus ) {
|
|
dwStatus = GetLastError();
|
|
hr = HRESULT_FROM_WIN32(dwStatus);
|
|
|
|
TRC_ERR((TB, L"WideCharToMultiByte() failed with %d", dwStatus));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
addr = inet_addr( pszAnsiAddress );
|
|
if( INADDR_NONE == addr ) {
|
|
struct hostent* pHostEnt = NULL;
|
|
pHostEnt = gethostbyname( pszAnsiAddress );
|
|
if( NULL != pHostEnt ) {
|
|
addr = ((struct sockaddr_in *)(pHostEnt->h_addr))->sin_addr.S_un.S_addr;
|
|
}
|
|
}
|
|
|
|
if( INADDR_NONE == addr ) {
|
|
dwStatus = GetLastError();
|
|
|
|
hr = HRESULT_FROM_WIN32(dwStatus);
|
|
TRC_ERR((TB, L"Can't translate address %w", pszAddress));
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
*pNetAddr = addr;
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if( NULL != pszAnsiAddress ) {
|
|
LocalFree(pszAnsiAddress);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
VOID
|
|
CRemoteDesktopServerHost::TicketExpirationProc(
|
|
IN LPVOID lpArg,
|
|
IN BOOLEAN TimerOrWaitFired
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Ticket expiration procedure, this routine is invoked by threadpool, refer
|
|
to RegisterWaitForSingleObject() and WAITORTIMERCALLBACK for detail.
|
|
|
|
Parameters:
|
|
|
|
lpArg : Pointer to user defined data, expecting CRemoteDesktopServerHost*.
|
|
TimerOrWaitFired : Refer to WAITORTIMERCALLBACK.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::TicketExpirationProc");
|
|
|
|
if( NULL == lpArg ) {
|
|
ASSERT( NULL != lpArg );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
CRemoteDesktopServerHost *pServerHostObj = (CRemoteDesktopServerHost *)lpArg;
|
|
|
|
if( TimerOrWaitFired ) {
|
|
if( !pServerHostObj->GetExpireMsgPosted() ) {
|
|
// Wait has timed out, Post main thread an message to expire ticket
|
|
pServerHostObj->SetExpireMsgPosted(TRUE);
|
|
PostThreadMessage(
|
|
_Module.dwThreadID,
|
|
WM_TICKETEXPIRED,
|
|
(WPARAM) 0,
|
|
(LPARAM) pServerHostObj
|
|
);
|
|
}
|
|
else {
|
|
long count;
|
|
|
|
count = pServerHostObj->Release();
|
|
TRC_NRM((TB, TEXT("TicketExpirationProc Release SrvHost count: %08X"), count));
|
|
ASSERT( count >= 0 );
|
|
}
|
|
}
|
|
else {
|
|
// Do nothing, our manual event never signal.
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
return;
|
|
}
|
|
|
|
HRESULT
|
|
CRemoteDesktopServerHost::AddTicketToExpirationList(
|
|
DWORD ticketExpireTime,
|
|
CComObject<CRemoteDesktopSession> *pTicketObj
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to sets up timer for newly created/opened ticket.
|
|
|
|
Parameters:
|
|
|
|
ticketExpireTime : Ticket expiration time, expecting time_t value.
|
|
pTicketObj : Pointer to ticket object to be expired.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL success;
|
|
DWORD status;
|
|
DWORD currentTime;
|
|
DWORD waitTime;
|
|
long count;
|
|
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::AddTicketToExpirationList");
|
|
|
|
//
|
|
// Invalid parameters
|
|
//
|
|
if( NULL == pTicketObj ) {
|
|
hr = E_INVALIDARG;
|
|
ASSERT( FALSE );
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
|
|
//
|
|
// Notice in the case of ticket already expire, we immediately signal
|
|
//
|
|
|
|
// Created at FinalConstruct() and deleted at destructor time,
|
|
// so can't be NULL
|
|
ASSERT( NULL != m_hTicketExpiration );
|
|
|
|
//
|
|
// Check to see if there is already a ticket waiting to be expired,
|
|
// if so, Check ticket expire time and reset timer if necessary.
|
|
//
|
|
if( m_ToBeExpireTicketExpirateTime > ticketExpireTime ) {
|
|
//
|
|
// Cancel the thread pool wait if there is one already in progress.
|
|
//
|
|
if( m_hTicketExpirationWaitObject ) {
|
|
success = UnregisterWait( m_hTicketExpirationWaitObject );
|
|
if( FALSE == success ) {
|
|
TRC_ERR((TB, TEXT("UnRegisterWait() failed: %08X"), GetLastError()));
|
|
// ASSERT( TRUE == success );
|
|
// Leak handle but not critical error, could return ERROR_IO_PENDING.
|
|
}
|
|
|
|
m_hTicketExpirationWaitObject = NULL;
|
|
}
|
|
|
|
if( m_ToBeExpireTicketExpirateTime == INFINITE_TICKET_EXPIRATION ) {
|
|
//
|
|
// put an extra ref. counter on srv. host object so we don't
|
|
// accidently delete it, this ref. count will be decrement on the
|
|
// corresponding ExpirateTicketAndSetupNextExpiration() call.
|
|
//
|
|
count = this->AddRef();
|
|
TRC_NRM((TB, TEXT("AddTicketToExpirationList AddRef SrvHost count: %08X"), count));
|
|
}
|
|
|
|
//
|
|
// Setup new ticket to be expired.
|
|
//
|
|
InterlockedExchange(
|
|
(LONG *)&m_ToBeExpireTicketExpirateTime,
|
|
ticketExpireTime
|
|
);
|
|
|
|
currentTime = (DWORD)time(NULL);
|
|
if( ticketExpireTime < currentTime ) {
|
|
// if ticket already expire, immediately signal TicketExpirationProc
|
|
// to expire ticket.
|
|
waitTime = 0;
|
|
}
|
|
else {
|
|
waitTime = (ticketExpireTime - currentTime) * 1000;
|
|
}
|
|
|
|
TRC_NRM((TB, TEXT("Expiration Wait Time : %d"), waitTime));
|
|
|
|
// Setup threadpool wait, there might not be any more object to be expired so
|
|
// it is executed only once.
|
|
success = RegisterWaitForSingleObject(
|
|
&m_hTicketExpirationWaitObject,
|
|
m_hTicketExpiration,
|
|
(WAITORTIMERCALLBACK) TicketExpirationProc,
|
|
(PVOID)this,
|
|
waitTime,
|
|
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE
|
|
);
|
|
|
|
if( FALSE == success ) {
|
|
status = GetLastError();
|
|
hr = HRESULT_FROM_WIN32(status);
|
|
TRC_ERR((TB, TEXT("RegisterWaitForSingleObject() failed: %08X"), hr));
|
|
ASSERT(FALSE);
|
|
|
|
count = this->Release();
|
|
TRC_NRM((TB, TEXT("AddTicketToExpirationList Release SrvHost count: %08X"), count));
|
|
ASSERT( count >= 0 );
|
|
|
|
// TODO : what can we do here, no signal (expiration) until next close or
|
|
// create.
|
|
}
|
|
}
|
|
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CRemoteDesktopServerHost::ExpirateTicketAndSetupNextExpiration()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to process all expired ticket and sets up timer for next
|
|
ticket expiration. Routine loop thru m_SessionMap cache so entry
|
|
must be removed first if setting up timer for next run.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
S_OK or error code.
|
|
|
|
--*/
|
|
{
|
|
DC_BEGIN_FN("CRemoteDesktopServerHost::ExpirateTicketAndSetupNextExpiration");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
SessionMap::iterator iter;
|
|
SessionMap::iterator iter_delete;
|
|
|
|
DWORD nextExpireTicketTime = INFINITE_TICKET_EXPIRATION;
|
|
CComObject<CRemoteDesktopSession> *pNextTicketObj = NULL;
|
|
CComObject<CRemoteDesktopSession> *pDeleteTicketObj = NULL;
|
|
|
|
//
|
|
// processing ticket expiration, set next ticket expiration
|
|
// time to infinite, this also prevent missing ticket,
|
|
// for example, adding/opening new ticket while we are in the
|
|
// middle of expiring ticket but new ticket is added at the
|
|
// beginning of m_SessionMap.
|
|
//
|
|
InterlockedExchange(
|
|
(LONG *)&m_ToBeExpireTicketExpirateTime,
|
|
INFINITE_TICKET_EXPIRATION
|
|
);
|
|
|
|
|
|
// we are deleting next ticket to be expired, loop m_SessionMap
|
|
// to find next candidate.
|
|
TRC_NRM((TB, TEXT("ExpirateTicketAndSetupNextExpiration Begin Loop: %08X"), m_SessionMap.size()));
|
|
|
|
iter = m_SessionMap.begin();
|
|
while( iter != m_SessionMap.end() ) {
|
|
|
|
if( NULL == (*iter).second ) {
|
|
// this entry has been deleted via DeleteRemoteDesktopSession.
|
|
iter_delete = iter;
|
|
iter++;
|
|
m_SessionMap.erase(iter_delete);
|
|
}
|
|
else if( (*iter).second->ticketExpireTime < (DWORD) time(NULL) ) {
|
|
//
|
|
// close ticket that is already expire.
|
|
//
|
|
pDeleteTicketObj = (*iter).second->obj;
|
|
|
|
ASSERT( pDeleteTicketObj != NULL );
|
|
|
|
// DeleteRemoteDesktopSession() will delete iterator from m_SessionMap
|
|
// which makes iter invalid so we advance pointer first before
|
|
// calling DeleteRemoteDesktopSession().
|
|
iter_delete = iter;
|
|
iter++;
|
|
DeleteRemoteDesktopSession(pDeleteTicketObj);
|
|
m_SessionMap.erase(iter_delete);
|
|
}
|
|
else {
|
|
|
|
if( (*iter).second->ticketExpireTime < nextExpireTicketTime ) {
|
|
pNextTicketObj = (*iter).second->obj;
|
|
ASSERT( pNextTicketObj != NULL );
|
|
nextExpireTicketTime = (*iter).second->ticketExpireTime;
|
|
}
|
|
|
|
iter++;
|
|
}
|
|
}
|
|
|
|
TRC_NRM((TB, TEXT("ExpirateTicketAndSetupNextExpiration End Loop: %08X"), m_SessionMap.size()));
|
|
|
|
// ready to process next expiration.
|
|
SetExpireMsgPosted(FALSE);
|
|
|
|
if( pNextTicketObj != NULL ) {
|
|
hr = AddTicketToExpirationList( nextExpireTicketTime, pNextTicketObj );
|
|
if( FAILED(hr) ) {
|
|
TRC_ERR((TB, TEXT("AddTicketToExpirationList() failed: %08X"), hr));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release the extra ref. counter to prevent 'this' from been
|
|
// deleted at AddTicketToExpirationList() or CloseRemoteDesktopSession().
|
|
//
|
|
long count;
|
|
|
|
count = this->Release();
|
|
TRC_NRM((TB, TEXT("ExpirateTicketAndSetupNextExpiration Release SrvHost count: %08X %08X"), count, m_SessionMap.size()));
|
|
|
|
ASSERT( count >= 0 );
|
|
|
|
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|