/****************************************************************************/ // aco.cpp // // Core class. // // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #define TRC_GROUP TRC_GROUP_CORE #define TRC_FILE "aco" #define TSC_HR_FILEID TSC_HR_ACO_CPP #include #include "aco.h" // Order important #include "wui.h" #include "cd.h" #include "cc.h" #include "snd.h" #include "ih.h" #include "uh.h" #include "sl.h" #include "op.h" #include "rcv.h" #include "cm.h" #include "sp.h" #include "or.h" #include "autil.h" #ifdef OS_WINCE #include #endif extern "C" VOID WINAPI CO_StaticInit(HINSTANCE hInstance) { CIH::IH_StaticInit(hInstance); } extern "C" VOID WINAPI CO_StaticTerm() { CIH::IH_StaticTerm(); } CCO::CCO(CObjs* objs) { _pClientObjects = objs; _fCOInitComplete = FALSE; } CCO::~CCO() { } // // API functions // /****************************************************************************/ /* Name: CO_Init */ /* */ /* Purpose: Core Initialization */ /* */ /* Params: IN hInstance - the instance handle */ /* IN hwndMain - the handle of the main window */ /* IN hwndContainer - the handle of the container window */ /****************************************************************************/ void DCAPI CCO::CO_Init(HINSTANCE hInstance, HWND hwndMain, HWND hwndContainer) { DC_BEGIN_FN("CO_Init"); TRC_ASSERT(_pClientObjects, (TB,_T("_pClientObjects is NULL"))); _pClientObjects->AddObjReference(CO_OBJECT_FLAG); //Setup local object pointers _pUt = _pClientObjects->_pUtObject; _pUi = _pClientObjects->_pUiObject; _pSl = _pClientObjects->_pSlObject; _pUh = _pClientObjects->_pUHObject; _pRcv = _pClientObjects->_pRcvObject; _pCd = _pClientObjects->_pCdObject; _pSnd = _pClientObjects->_pSndObject; _pCc = _pClientObjects->_pCcObject; _pIh = _pClientObjects->_pIhObject; _pOr = _pClientObjects->_pOrObject; _pSp = _pClientObjects->_pSPObject; _pOp = _pClientObjects->_pOPObject; _pCm = _pClientObjects->_pCMObject; _pClx = _pClientObjects->_pCLXObject; DC_MEMSET(&_CO, 0, sizeof(_CO)); memset(m_disconnectHRs, 0, sizeof(m_disconnectHRs)); m_disconnectHRIndex = 0; /************************************************************************/ /* Set UT instance handle */ /************************************************************************/ TRC_NRM((TB, _T("CO setting Instance handle in UT to %p"), hInstance)); _pUi->UI_SetInstanceHandle(hInstance); _pUt->UT_SetInstanceHandle(hInstance); /************************************************************************/ /* Set UT Main Window handle. */ /************************************************************************/ TRC_NRM((TB, _T("CO setting Main Window handle in UT to %p"), hwndMain)); _pUi->UI_SetUIMainWindow(hwndMain); /************************************************************************/ /* Set UT Container Window handle. */ /************************************************************************/ TRC_NRM((TB, _T("CO setting Container Window handle in UT to %p"), hwndContainer)); _pUi->UI_SetUIContainerWindow(hwndContainer); // // Initialize the component decoupler and register the UI (as we are // running on the UI thread here). CD_Init must be called after // setting UT.hInstance. // _pCd->CD_Init(); _pCd->CD_RegisterComponent(CD_UI_COMPONENT); COSubclassUIWindows(); // Start the sender thread _pUt->UT_StartThread(CSND::SND_StaticMain, &_CO.sendThreadID, _pSnd); _fCOInitComplete = TRUE; DC_END_FN(); } /* CO_Init */ /****************************************************************************/ /* Name: CO_Term */ /* */ /* Purpose: Core Termination */ /****************************************************************************/ void DCAPI CCO::CO_Term() { DC_BEGIN_FN("CO_Term"); if(_fCOInitComplete) { // Deregister from the component decoupler _pCd->CD_UnregisterComponent(CD_UI_COMPONENT); #ifdef OS_WIN32 // We're on Win32 so terminate the Sender Thread. // We're called on the UI thread so PUMP messages while // waiting for the thread to get destroyed // // // Pump messages while waiting since this is the UI thread // _pUt->UT_DestroyThread(_CO.sendThreadID, TRUE); #else // We're on Win16 so just call SND_Term directly. SND_Term(); #endif _pCd->CD_Term(); _pClientObjects->ReleaseObjReference(CO_OBJECT_FLAG); } else { TRC_DBG((TB,_T("Skipping CO_Term because _fCOInitComplete is false"))); } DC_END_FN(); } /* CO_Term */ /****************************************************************************/ /* Name: CO_Connect */ /* */ /* Purpose: Connect to an RNS */ /* */ /* Params: IN pConnectStruct - connection information */ /****************************************************************************/ void DCAPI CCO::CO_Connect(PCONNECTSTRUCT pConnectStruct) { DC_BEGIN_FN("CO_Connect"); /************************************************************************/ /* Check that the core is initialized. */ /************************************************************************/ TRC_ASSERT((_pUi->UI_IsCoreInitialized()), (TB, _T("Core not initialized"))); /************************************************************************/ /* Call CC with a Connect event */ /************************************************************************/ _pCd->CD_DecoupleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_Connect), pConnectStruct, sizeof(CONNECTSTRUCT)); DC_END_FN(); } /* CO_Connect */ /****************************************************************************/ /* Name: CO_Disconnect */ /* */ /* Purpose: Disconnect */ /* */ /* Operation: call the Call Controller FSM */ /****************************************************************************/ void DCAPI CCO::CO_Disconnect(void) { DC_BEGIN_FN("CO_Disconnect"); // Check that the core is initialized. TRC_ASSERT((_pUi->UI_IsCoreInitialized()), (TB, _T("Core not initialized"))); // Call CC with a Disconnect event. _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_Event), (ULONG_PTR) CC_EVT_API_DISCONNECT); DC_END_FN(); } /* CO_Disconnect */ /****************************************************************************/ /* Name: CO_Shutdown */ /* */ /* Purpose: Shutdown the client */ /* */ /* Params: IN shutdownCode - what kind of shutdown is required. */ /* */ /* Operation: call the Call Controller FSM */ /****************************************************************************/ void DCAPI CCO::CO_Shutdown(unsigned shutdownCode) { DC_BEGIN_FN("CO_Shutdown"); //Prevent race by ensuring CO init is complete if(_fCOInitComplete) { // Check that the core is initialized. if (!_pUi->UI_IsCoreInitialized()) { // // Trace and then fake-up a call to UI_OnShutdown to pretend to the // UI that the shutdown completed successfully. // TRC_NRM((TB,_T("Core NOT initialized"))); _pUi->UI_OnShutDown(UI_SHUTDOWN_SUCCESS); DC_QUIT; } switch (shutdownCode) { case CO_DISCONNECT_AND_EXIT: { TRC_DBG((TB, _T("Shutdown type: disconnect and exit"))); // Call CC with a Shutdown event _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_Event), (ULONG_PTR) CC_EVT_API_DISCONNECTANDEXIT); } break; case CO_SHUTDOWN: { TRC_DBG((TB, _T("Shutdown type: shutdown"))); // Call CC with a Shutdown event _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_Event), (ULONG_PTR) CC_EVT_API_SHUTDOWN); } break; default: { TRC_ABORT((TB, _T("Illegal shutdown code"))); } break; } } DC_EXIT_POINT: DC_END_FN(); } /* CO_Shutdown */ /****************************************************************************/ /* Name: CO_OnSaveSessionInfoPDU */ /* */ /* Purpose: Process Save Session PDU */ /* */ /* Params: pInfoPDU - Ptr to PTS_SAVE_SESSION_INFO_PDU */ /****************************************************************************/ // SECURITY - the size of the packet has been checked only to be sure there is // enough data to read the PTS_SAVE_SESSION_INFO_PDU_DATA.InfoType field HRESULT DCAPI CCO::CO_OnSaveSessionInfoPDU( PTS_SAVE_SESSION_INFO_PDU_DATA pInfoPDU, DCUINT dataLength) { HRESULT hr = S_OK; UINT32 sessionId; TSUINT8 UserNameGot[TS_MAX_USERNAME_LENGTH]; TSUINT8 DomainNameGot[TS_MAX_DOMAIN_LENGTH]; TSUINT32 DomainLength, UserNameLength; TSUINT16 VersionGot; DCUINT packetSize; DC_BEGIN_FN("CO_OnSaveSessionInfoPDU"); switch (pInfoPDU->InfoType) { case TS_INFOTYPE_LOGON: { TRC_NRM((TB, _T("Logon PDU"))); packetSize = FIELDOFFSET(TS_SAVE_SESSION_INFO_PDU_DATA, Info) + sizeof(TS_LOGON_INFO); if (packetSize >= dataLength) sessionId = pInfoPDU->Info.LogonInfo.SessionId; else if (packetSize - FIELDSIZE(TS_LOGON_INFO, SessionId) >= dataLength) { // NT4 servers did not send the session ID so default to zero // if there is no data. sessionId = 0; } else { TRC_ABORT((TB,_T("bad TS_SAVE_SESSION_INFO_PDU_DATA; size %u"), dataLength )); hr = E_TSC_CORE_LENGTH; DC_QUIT; } TRC_ALT((TB, _T("Session ID is: %ld"), sessionId)); if (pInfoPDU->Info.LogonInfo.cbDomain > TS_MAX_DOMAIN_LENGTH_OLD || pInfoPDU->Info.LogonInfo.cbUserName > TS_MAX_USERNAME_LENGTH ) { TRC_ABORT(( TB, _T("Invalid TS_INFOTYPE_LOGON; cbDomain %u ") _T("cbUserName %u"), pInfoPDU->Info.LogonInfo.cbDomain, pInfoPDU->Info.LogonInfo.cbUserName)); hr = E_TSC_CORE_LENGTH; DC_QUIT; } _pUi->UI_UpdateSessionInfo((PDCWCHAR)(pInfoPDU->Info.LogonInfo.Domain), (DCUINT) (pInfoPDU->Info.LogonInfo.cbDomain), (PDCWCHAR)(pInfoPDU->Info.LogonInfo.UserName), (DCUINT) (pInfoPDU->Info.LogonInfo.cbUserName), sessionId); } break; case TS_INFOTYPE_LOGON_LONG: { TRC_NRM((TB, _T("Logon PDU"))); VersionGot = pInfoPDU->Info.LogonInfoVersionTwo.Version ; DomainLength = pInfoPDU->Info.LogonInfoVersionTwo.cbDomain ; UserNameLength = pInfoPDU->Info.LogonInfoVersionTwo.cbUserName ; if ((FIELDOFFSET( TS_SAVE_SESSION_INFO_PDU_DATA, Info) + sizeof(TS_LOGON_INFO_VERSION_2) + DomainLength + UserNameLength > dataLength) || (DomainLength > TS_MAX_DOMAIN_LENGTH) || (UserNameLength > TS_MAX_USERNAME_LENGTH)) { TRC_ABORT(( TB, _T("Invalid TS_INFOTYPE_LOGON_LONG; cbDomain ") _T("%u cbUserName %u"), DomainLength, UserNameLength)); hr = E_TSC_CORE_LENGTH; DC_QUIT; } // Get the sessionId sessionId = pInfoPDU->Info.LogonInfoVersionTwo.SessionId; TRC_ALT((TB, _T("Session ID is: %ld"), sessionId)); // Parse out the Domain and UserName from the pInfoPDU memset( DomainNameGot, 0, TS_MAX_DOMAIN_LENGTH); memset( UserNameGot, 0, TS_MAX_USERNAME_LENGTH); memcpy( DomainNameGot, (PBYTE)(pInfoPDU + 1), DomainLength) ; memcpy(UserNameGot, (PBYTE)(pInfoPDU + 1) + DomainLength, UserNameLength) ; _pUi->UI_UpdateSessionInfo((PDCWCHAR)(DomainNameGot), (DCUINT) (DomainLength), (PDCWCHAR)(UserNameGot), (DCUINT) (UserNameLength), sessionId); } break; case TS_INFOTYPE_LOGON_PLAINNOTIFY: { //Notify of login event _pUi->UI_OnLoginComplete(); } break; case TS_INFOTYPE_LOGON_EXTENDED_INFO: { TRC_NRM((TB,_T("Received TS_INFOTYPE_LOGON_EXTENDED_INFO"))); TS_LOGON_INFO_EXTENDED UNALIGNED* pLogonInfoExPkt = (TS_LOGON_INFO_EXTENDED UNALIGNED*)&pInfoPDU->Info.LogonInfoEx; if (FIELDOFFSET(TS_SAVE_SESSION_INFO_PDU_DATA, Info) + pLogonInfoExPkt->Length > dataLength) { TRC_ABORT(( TB, _T("Invalid TS_INFOTYPE_LOGON_EXTENDED_INFO") _T("[expected %u got %u]"), sizeof(TS_SAVE_SESSION_INFO_PDU_DATA) - FIELDSIZE(TS_SAVE_SESSION_INFO_PDU_DATA, Info) + pLogonInfoExPkt->Length, dataLength)); hr = E_TSC_CORE_LENGTH; DC_QUIT; } PBYTE pBuf = (PBYTE)(pLogonInfoExPkt + 1); if (pLogonInfoExPkt && pLogonInfoExPkt->Flags & LOGON_EX_AUTORECONNECTCOOKIE) { // // Autoreconnect cookie is present // ULONG cbAutoReconnectSize = *((ULONG UNALIGNED *)(pBuf)); PBYTE pAutoReconnectCookie = (PBYTE)(pBuf) + sizeof(ULONG); pBuf += cbAutoReconnectSize + sizeof(ULONG); if (cbAutoReconnectSize > TS_MAX_AUTORECONNECT_LEN) { TRC_ABORT(( TB, _T("TS_INFOTYPE_LOGON_EXTENDED_INFO") _T("autoreconnect wrong size; [got %u]"), cbAutoReconnectSize)); hr = E_TSC_CORE_LENGTH; DC_QUIT; } CHECK_READ_N_BYTES( pAutoReconnectCookie, (PBYTE)pInfoPDU + dataLength, cbAutoReconnectSize, hr, (TB,_T("TS_INFOTYPE_LOGON_EXTENDED_INFO") _T("autoreconnect wrong size; [got %u]"), cbAutoReconnectSize)); TRC_ALT((TB,_T("Received autoreconnect cookie - size: %d"), cbAutoReconnectSize)); // // Store the autoreconnect cookie. It will be used // if we get disconnected unexpectedly to allow a // fast reconnect to the server. // _pUi->UI_SetAutoReconnectCookie(pAutoReconnectCookie, cbAutoReconnectSize); } } break; default: { TRC_ERR((TB, _T("Unexpected Save Session Info PDU type: %u"), (DCUINT)pInfoPDU->InfoType)); } break; } DC_EXIT_POINT: DC_END_FN(); return hr; } /* CO_OnSaveSessionInfoPDU */ /****************************************************************************/ /* Name: CO_OnSetKeyboardIndicatorsPDU */ /* */ /* Purpose: Process the TS_SET_KEYBOARD_INDICATORS_PDU */ /* */ /* Params: pKeyPDU - Ptr to TS_SET_KEYBOARD_INDICATORS_PDU */ /****************************************************************************/ HRESULT DCAPI CCO::CO_OnSetKeyboardIndicatorsPDU( PTS_SET_KEYBOARD_INDICATORS_PDU pKeyPDU, DCUINT dataLen) { DC_BEGIN_FN("CO_OnSetKeyboardIndicatorsPDU"); DC_IGNORE_PARAMETER(dataLen); _pIh->IH_UpdateKeyboardIndicators(pKeyPDU->UnitId, pKeyPDU->LedFlags); DC_END_FN(); return S_OK; } /* CO_OnSetKeyboardIndicatorsPDU */ /****************************************************************************/ /* Name: CO_SetConfigurationValue */ /* */ /* Purpose: Sets a given configuration setting to a given value */ /* */ /* Params: configItem - the configuration item to change */ /* configValue - the new value of the configuration item */ /* (see acoapi.h for valid values) */ /****************************************************************************/ void DCAPI CCO::CO_SetConfigurationValue( unsigned configItem, unsigned configValue) { DC_BEGIN_FN("CO_SetConfigurationValue"); TRC_ASSERT((_pUi->UI_IsCoreInitialized()), (TB, _T("Core not initialized"))); switch (configItem) { case CO_CFG_ACCELERATOR_PASSTHROUGH: { _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pIh, CD_NOTIFICATION_FUNC(CIH,IH_SetAcceleratorPassthrough), (ULONG_PTR) configValue); } break; case CO_CFG_ENCRYPTION: { _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pSl, CD_NOTIFICATION_FUNC(CSL,SL_EnableEncryption), (ULONG_PTR) configValue); } break; #ifdef DC_DEBUG case CO_CFG_DEBUG_SETTINGS: { _pCd->CD_DecoupleSimpleNotification(CD_RCV_COMPONENT, _pUh, CD_NOTIFICATION_FUNC(CUH,UH_ChangeDebugSettings), (ULONG_PTR) configValue); } break; #endif /* DC_DEBUG */ default: { TRC_ABORT((TB, _T("Invalid configItem: %u"), configItem)); } break; } DC_END_FN(); } /* CO_SetConfigurationValue */ /****************************************************************************/ /* Name: CO_SetHotkey */ /* */ /* Purpose: call the function with the given data */ /****************************************************************************/ void DCAPI CCO::CO_SetHotkey(PDCHOTKEY pHotkey) { _pCd->CD_DecoupleNotification(CD_SND_COMPONENT, _pIh, CD_NOTIFICATION_FUNC(CIH,IH_SetHotkey), &pHotkey, sizeof(PDCHOTKEY)); } #ifdef DC_DEBUG /****************************************************************************/ /* Name: CO_GetRandomFailureItem */ /* */ /* Purpose: Simple wrapper to _pUt->UT_GetRandomFailureItem */ /* */ /* Returns: % times item currently fails */ /* */ /* Params: ItemID - IN - requested item */ /****************************************************************************/ int DCAPI CCO::CO_GetRandomFailureItem(unsigned itemID) { DC_BEGIN_FN("CO_GetRandomFailureItem"); DC_END_FN(); return _pUt->UT_GetRandomFailureItem(itemID); } /* CO_GetRandomFailureItem */ /****************************************************************************/ /* Name: CO_SetRandomFailureItem */ /* */ /* Purpose: Simple wrapper for _pUi->UI_SetRandomFailureItem */ /* */ /* Params: See _pUi->UI_SetRandomFailureItem */ /****************************************************************************/ void DCAPI CCO::CO_SetRandomFailureItem(unsigned itemID, int percent) { DC_BEGIN_FN("CO_SetRandomFailureItem"); _pUt->UT_SetRandomFailureItem(itemID, percent); DC_END_FN(); } /* CO_SetRandomFailureItem */ #endif /* DC_DEBUG */ /****************************************************************************/ /* Name: COContainerWindowSubclassProc */ /* */ /* Purpose: Subclass procedure for the UI Container Window */ /****************************************************************************/ LRESULT CALLBACK CCO::COContainerWindowSubclassProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; POINT newPos; DC_BEGIN_FN("COContainerWindowSubclassProc"); switch (message) { case WM_SETFOCUS: { // // Note, the code here used to use CallWindowProc. // this has been changed to use a direct call. // If there is ever a need for Unicode/ANSI conversions // then change the direct calls to the window proc // to use CallWindowProc // rc =_pUi->UIContainerWndProc( hwnd, message, wParam, lParam); if (rc) { SetFocus(_pIh->IH_GetInputHandlerWindow()); } } break; case WM_MOVE: { /****************************************************************/ /* Tell IH about the new window position. */ /* Take care with sign extension here. */ /****************************************************************/ newPos.x = (DCINT)((DCINT16)LOWORD(lParam)); newPos.y = (DCINT)((DCINT16)HIWORD(lParam)); TRC_DBG((TB, _T("Move to %d,%d"), newPos.x, newPos.y)); _pCd->CD_DecoupleNotification(CD_SND_COMPONENT, _pIh, CD_NOTIFICATION_FUNC(CIH,IH_SetVisiblePos), &newPos, sizeof(newPos)); rc = _pUi->UIContainerWndProc( hwnd, message, wParam, lParam); } break; default: { rc =_pUi->UIContainerWndProc( hwnd, message, wParam, lParam); } break; } DC_END_FN(); return rc; } /* COContainerWindowSubclassProc */ /****************************************************************************/ /* Name: COMainFrameWindowSubclassProc */ /* */ /* Purpose: Subclass procedure for the UI Main Frame Window */ /****************************************************************************/ LRESULT CALLBACK CCO::COMainWindowSubclassProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; DCSIZE newSize; DC_BEGIN_FN("COMainWindowSubclassProc"); switch (message) { case WM_SIZE: { /****************************************************************/ /* Tell IH about the new window size. */ /* Take care with sign extension here. */ /****************************************************************/ newSize.width = (DCINT)((DCINT16)LOWORD(lParam)); newSize.height = (DCINT)((DCINT16)HIWORD(lParam)); TRC_DBG((TB, _T("Size now %d,%d"), newSize.width, newSize.height)); switch (wParam) { case SIZE_MINIMIZED: case SIZE_MAXIMIZED: case SIZE_RESTORED: { WPARAM newWindowState = wParam; // // This is slightly hack-erific. // because we're now an ActiveX nested child window // we don't get a WM_MINIMIZED. But on minimize // the size becomes 0,0 so fake it. // if(!newSize.width && !newSize.height) { newWindowState = SIZE_MINIMIZED; } /********************************************************/ /* OR interested in these to maybe send */ /* SuppressOutputPDU */ /********************************************************/ _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pOr, CD_NOTIFICATION_FUNC(COR,OR_SetSuppressOutput), (UINT) newWindowState); } break; default: { /********************************************************/ /* OR not interested in these - do nothing */ /********************************************************/ } break; } rc =_pUi->UIMainWndProc( hwnd, message, wParam, lParam); } break; case WM_PALETTECHANGED: { TRC_NRM((TB, _T("WM_PALETTECHANGED"))); /****************************************************************/ /* Note that we are calling this function (which should */ /* logically be called on the receive thread) on the UI thread. */ /* See comment in function description. */ /****************************************************************/ _pOp->OP_PaletteChanged(hwnd, (HWND)wParam); } break; case WM_QUERYNEWPALETTE: { TRC_NRM((TB, _T("WM_QUERYNEWPALETTE"))); /****************************************************************/ /* Note that we are calling this function (which should */ /* logically be called on the receive thread) on the UI thread. */ /* See comment in function description. */ /****************************************************************/ rc = _pOp->OP_QueryNewPalette(hwnd); } break; #ifdef OS_WINNT case WM_ENTERSIZEMOVE: { /****************************************************************/ /* Tell IH we're entering Size/Move mode */ /****************************************************************/ TRC_NRM((TB, _T("Enter Size/Move"))); _CO.inSizeMove = TRUE; _pCd->CD_DecoupleSyncNotification(CD_SND_COMPONENT, _pIh, CD_NOTIFICATION_FUNC(CIH,IH_InputEvent), WM_ENTERSIZEMOVE); } break; case WM_CAPTURECHANGED: { if (_CO.inSizeMove) { /************************************************************/ /* Tell IH we're leaving size/move mode (Windows doesn't */ /* always send WM_EXITSIZEMOVE, but it does seem to always */ /* send WM_CAPTURECHANGED). */ /************************************************************/ TRC_NRM((TB, _T("Capture Changed when in Size/Move"))); _CO.inSizeMove = FALSE; _pCd->CD_DecoupleSyncNotification(CD_SND_COMPONENT, _pIh, CD_NOTIFICATION_FUNC(CIH,IH_InputEvent), WM_EXITSIZEMOVE); } } break; case WM_EXITSIZEMOVE: { // Tell IH we're leaving size/move mode. TRC_NRM((TB, _T("Exit Size/Move"))); _CO.inSizeMove = FALSE; _pCd->CD_DecoupleSyncNotification(CD_SND_COMPONENT, _pIh, CD_NOTIFICATION_FUNC(CIH,IH_InputEvent), WM_EXITSIZEMOVE); } break; case WM_EXITMENULOOP: { // Tell IH we're exiting the system menu handler. TRC_NRM((TB, _T("Exit menu loop"))); _pCd->CD_DecoupleSyncNotification(CD_SND_COMPONENT, _pIh, CD_NOTIFICATION_FUNC(CIH,IH_InputEvent), WM_EXITMENULOOP); } break; #endif #ifdef OS_WINCE // HPC devices do not get WM_SIZE (SIZE_RESTORED) on a maximize, // but we do get the WM_ACTIVATE message. case WM_ACTIVATE: { if (g_CEConfig != CE_CONFIG_WBT && LOWORD(wParam) != WA_INACTIVE) { _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pOr, CD_NOTIFICATION_FUNC(COR,OR_SetSuppressOutput), SIZE_RESTORED); } } // FALL-THRU #endif // OS_WINCE default: { rc =_pUi->UIMainWndProc( hwnd, message, wParam, lParam); } break; } DC_END_FN(); return rc; } /* COContainerWindowSubclassProc */ LRESULT CALLBACK CCO::COStaticContainerWindowSubclassProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { // delegate to appropriate instance CCO* pCO = (CCO*)GetWindowLongPtr(hwnd, GWLP_USERDATA); return pCO->COContainerWindowSubclassProc(hwnd, message, wParam, lParam); } LRESULT CALLBACK CCO::COStaticMainWindowSubclassProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { // delegate to appropriate instance CCO* pCO = (CCO*)GetWindowLongPtr(hwnd, GWLP_USERDATA); return pCO->COMainWindowSubclassProc(hwnd, message, wParam, lParam); } /****************************************************************************/ /* Name: COSubclassUIWindows */ /* */ /* Purpose: Subclasses the UI's Main Frame and Container Windows */ /****************************************************************************/ void DCINTERNAL CCO::COSubclassUIWindows() { DC_BEGIN_FN("COSubclassUIWindows"); // Replace the instance pointer with that for the CO object // when subclassing. Have to do this because CO is not derived from UI SetWindowLongPtr( _pUi->UI_GetUIMainWindow(), GWLP_USERDATA, (LONG_PTR)this); SetWindowLongPtr( _pUi->UI_GetUIContainerWindow(), GWLP_USERDATA, (LONG_PTR)this); _CO.pUIMainWndProc = SubclassWindow( _pUi->UI_GetUIMainWindow(), COStaticMainWindowSubclassProc ); _CO.pUIContainerWndProc = SubclassWindow( _pUi->UI_GetUIContainerWindow(), COStaticContainerWindowSubclassProc ); DC_END_FN(); } /* COSubclassUIWindows */ /****************************************************************************/ /* Name: CO_OnInitialized */ /* */ /* Purpose: Handle Initialized notification from SL */ /* */ /* Operation: Initialize the Receiver Thread */ /****************************************************************************/ void DCCALLBACK CCO::CO_OnInitialized() { DC_BEGIN_FN("CO_OnInitialized"); // Call RCV_Init to initialize the Core. _pRcv->RCV_Init(); DC_END_FN(); } /* CO_OnInitialized */ /****************************************************************************/ /* Name: CO_OnTerminating */ /* */ /* Purpose: Handle Terminating notification from SL */ /****************************************************************************/ void DCCALLBACK CCO::CO_OnTerminating() { DC_BEGIN_FN("CO_OnTerminating"); // Terminate Core components. _pRcv->RCV_Term(); DC_END_FN(); } /* CO_OnTerminating */ /****************************************************************************/ /* Name: CO_OnConnected */ /* */ /* Purpose: Handle Connected notification from SL */ /* */ /* Params: IN channelID */ /* IN pUserData */ /* IN userDataLength */ /****************************************************************************/ void DCCALLBACK CCO::CO_OnConnected( unsigned channelID, PVOID pUserData, unsigned userDataLength, UINT32 serverVersion) { DC_BEGIN_FN("CO_OnConnected"); TRC_DBG((TB, _T("Channel %d"), channelID)); DC_IGNORE_PARAMETER(serverVersion); // Currently there is no Core userdata sent from the Server. DC_IGNORE_PARAMETER(pUserData); DC_IGNORE_PARAMETER(userDataLength); DC_IGNORE_PARAMETER(channelID); // Pass to CC. TRC_NRM((TB, _T("Connect OK"))); _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_Event), (ULONG_PTR) CC_EVT_API_ONCONNECTOK); DC_END_FN(); } /* CO_OnConnected */ /****************************************************************************/ /* Name: CO_OnDisconnected */ /* */ /* Purpose: Handle Disconnected notification from SL */ /* */ /* Params: IN result - disconnection reason code */ /****************************************************************************/ void DCCALLBACK CCO::CO_OnDisconnected(unsigned result) { DC_BEGIN_FN("CO_OnDisconnected"); DC_IGNORE_PARAMETER(result); // Pass to CC. _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_OnDisconnected), (ULONG_PTR) result); DC_END_FN(); } /* CO_OnDisconnected */ #define CO_CHECKPACKETCAST( type, size, hr ) \ if ( (size) < sizeof(type)) { \ TRC_ABORT((TB, _T("Bad ") _T( #type ) _T(" len [expected %u got %u]"), \ sizeof(type), (size) )); \ hr = E_TSC_CORE_LENGTH; \ DC_QUIT; \ } #define CO_CHECKPACKETCAST_SPECIFC( type, size, expected, hr ) \ if ( (size) < (expected)) { \ TRC_ABORT((TB, _T("Bad ") _T( #type ) _T(" len [expected %u got %u]"), \ (expected), (size) )); \ hr = E_TSC_CORE_LENGTH; \ DC_QUIT; \ } // This macro is used to see that when a pointer to a TS_SHAREDATAHEADER // is passed on to a handler, the TS_SHAREDATAHEADER is an uncompressed packet // If the packet was compressed, then the data after the TS_SHAREDATAHEADER is // still compressed, and the method we pass this header onto will puke on the // data, assuming it does not uncompress the data, which no layers above this // do #define COPR_MUSTBEUNCOMP( type, pDataHdr, hr ) \ if (pDataHdr->generalCompressedType & PACKET_COMPRESSED) { \ TRC_ABORT((TB, _T( #type ) \ _T(" was unexpectedly compressed"))); \ hr = E_TSC_CORE_UNEXPECTEDCOMP; \ DC_QUIT; \ } /****************************************************************************/ /* Name: CO_OnPacketReceived */ /* */ /* Purpose: Handle PacketReceived notification from SL */ /* */ /* Params: IN pData - packet received */ /* IN dataLen - length of packet */ /* IN flags - RNS_SEC_ flags */ /* IN channelID - MCS channel on which packet was sent */ /* IN priority - priority of packet */ /****************************************************************************/ HRESULT DCCALLBACK CCO::CO_OnPacketReceived( PBYTE pData, unsigned dataLen, unsigned flags, unsigned channelID, unsigned priority) { HRESULT hr = S_OK; PTS_SHARECONTROLHEADER pCtrlHdr; PTS_SHAREDATAHEADER pDataHdr; PTS_FLOW_PDU pFlowPDU; PDCUINT8 pCurrentPDU; DCUINT currentDataLen; DCUINT dataBufLen; #ifdef DC_DEBUG DCUINT countPDU = 0; #endif DCUINT dataRemaining = dataLen; DC_BEGIN_FN("CO_OnPacketReceived"); DC_IGNORE_PARAMETER(flags); DC_IGNORE_PARAMETER(priority); DC_IGNORE_PARAMETER(channelID); TRC_ASSERT((pData != NULL), (TB, _T("NULL packet"))); TRC_NRM((TB, _T("channelID %#x"), channelID)); TRC_NRM((TB, _T("(fixed) Buffer: %p len %d"), pData, dataLen)); TRC_DATA_DBG("Contents", pData, dataLen); // Find the first PDU in the packet. pCurrentPDU = pData; // And while there are more PDUs in the packet, route each PDU. while (pCurrentPDU != NULL) { // Intermediate variable to save in casts! pCtrlHdr = (PTS_SHARECONTROLHEADER)pCurrentPDU; // SECURITY: NOTE - it is not safe to read the pduSource // field of the TS_SHARECONTROLHEADER since we are not // requiring this data to be there. This is to support // the TS_PDUTYPE_DEACTIVATEALLPDU which appears smaller // on win2k // Be sure there is enough data to read the totalLength if (dataRemaining < FIELDOFFSET(TS_SHARECONTROLHEADER, pduSource)) { TRC_ABORT(( TB, _T("dataRemaining %u partial sizeof(TS_") _T("SHARECONTROLHEADER) %u "), dataRemaining, FIELDOFFSET(TS_SHARECONTROLHEADER, pduSource))); hr = E_TSC_CORE_LENGTH; DC_QUIT; } // All PDUs start with the length and PDU type, except FlowPDUs. if (pCtrlHdr->totalLength != TS_FLOW_MARKER) { currentDataLen = pCtrlHdr->totalLength; // Be sure there is enough data for the entire packet if (dataRemaining < currentDataLen) { TRC_ABORT(( TB, _T("dataRemaining %u currentDataLen %u"), dataRemaining, currentDataLen)); hr = E_TSC_CORE_LENGTH; DC_QUIT; } if (sizeof(PTS_SHAREDATAHEADER) <= dataRemaining) { TRC_NRM((TB, _T("current PDU x%p type %u, type2 %u, data len %u"), pCurrentPDU, (pCtrlHdr->pduType) & TS_MASK_PDUTYPE, ((PTS_SHAREDATAHEADER)pCurrentPDU)->pduType2, currentDataLen)); } switch ((pCtrlHdr->pduType) & TS_MASK_PDUTYPE) { case TS_PDUTYPE_DATAPDU: { PDCUINT8 pDataBuf; TRC_DBG((TB, _T("A Data PDU"))); CO_CHECKPACKETCAST(TS_SHAREDATAHEADER, currentDataLen, hr); pDataHdr = (PTS_SHAREDATAHEADER)pCurrentPDU; pDataBuf = pCurrentPDU + sizeof(TS_SHAREDATAHEADER); dataBufLen = currentDataLen - sizeof(TS_SHAREDATAHEADER); if (pDataHdr->generalCompressedType & PACKET_COMPRESSED) { UCHAR *buf; int bufSize; if (pDataHdr->generalCompressedType & PACKET_FLUSHED) initrecvcontext (&_pUi->_UI.Context1, (RecvContext2_Generic*)_pUi->_UI.pRecvContext2, PACKET_COMPR_TYPE_64K); if (decompress(pDataBuf, pCtrlHdr->totalLength - sizeof(TS_SHAREDATAHEADER), (pDataHdr->generalCompressedType & PACKET_AT_FRONT), &buf, &bufSize, &_pUi->_UI.Context1, (RecvContext2_Generic*)_pUi->_UI.pRecvContext2, (pDataHdr->generalCompressedType & PACKET_COMPR_TYPE_MASK))) { pDataBuf = buf; dataBufLen = bufSize; } else { TRC_ABORT((TB, _T("Decompression FAILURE!!!"))); hr = E_TSC_UI_DECOMPRESSION; DC_QUIT; } } switch (pDataHdr->pduType2) { case TS_PDUTYPE2_UPDATE: CO_CHECKPACKETCAST(TS_UPDATE_HDR_DATA, dataBufLen, hr); TRC_DBG((TB, _T("Update PDU"))); hr = _pUh->UH_OnUpdatePDU( (TS_UPDATE_HDR_DATA UNALIGNED FAR *) pDataBuf, dataBufLen); DC_QUIT_ON_FAIL(hr); break; case TS_PDUTYPE2_POINTER: // SECURITY: Only the TS_POINTER_PDU_DATA.messageType needs to be // read. NT4 servers may not send a TS_POINTER_PDU_DATA.pointerData CO_CHECKPACKETCAST_SPECIFC(TS_POINTER_PDU_DATA, dataBufLen, sizeof(TSUINT16), hr); TRC_DBG((TB, _T("Mouse Pointer PDU"))); hr = _pCm->CM_SlowPathPDU( (TS_POINTER_PDU_DATA UNALIGNED FAR *) pDataBuf, dataBufLen); DC_QUIT_ON_FAIL(hr); break; case TS_PDUTYPE2_FONTMAP: case TS_PDUTYPE2_INPUT: case TS_PDUTYPE2_UPDATECAPABILITY: case TS_PDUTYPE2_DESKTOP_SCROLL: case TS_PDUTYPE2_APPLICATION: case TS_PDUTYPE2_CONTROL: case TS_PDUTYPE2_MEDIATEDCONTROL: case TS_PDUTYPE2_REMOTESHARE: case TS_PDUTYPE2_SYNCHRONIZE: case TS_PDUTYPE2_WINDOWLISTUPDATE: case TS_PDUTYPE2_WINDOWACTIVATION: TRC_DBG((TB, _T("Ignore pdutype2 %#x"), pDataHdr->pduType2)); break; case TS_PDUTYPE2_SHUTDOWN_DENIED: TRC_DBG((TB, _T("ShutdownDeniedPDU"))); _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_Event), (ULONG_PTR)CC_EVT_API_ONSHUTDOWNDENIED); break; case TS_PDUTYPE2_PLAY_SOUND: CO_CHECKPACKETCAST(TS_PLAY_SOUND_PDU_DATA, dataBufLen, hr); TRC_DBG((TB, _T("PlaySoundPDU"))); hr = _pSp->SP_OnPlaySoundPDU((PTS_PLAY_SOUND_PDU_DATA) pDataBuf, dataBufLen); DC_QUIT_ON_FAIL(hr); break; case TS_PDUTYPE2_SAVE_SESSION_INFO: //CO_CHECKPACKETCAST(TS_SAVE_SESSION_INFO_PDU_DATA, dataBufLen, hr); // SECURITY: TS_SAVE_SESSION_INFO_PDU_DATA may be sent from NT4 with // only a TS_SAVE_SESSION_INFO_PDU_DATA.InfoType CO_CHECKPACKETCAST_SPECIFC(TS_SAVE_SESSION_INFO_PDU_DATA, dataBufLen, sizeof(TSUINT32), hr); TRC_DBG((TB, _T("Save Session Info PDU"))); hr = CO_OnSaveSessionInfoPDU( (PTS_SAVE_SESSION_INFO_PDU_DATA)pDataBuf, dataBufLen); DC_QUIT_ON_FAIL(hr); break; case TS_PDUTYPE2_SET_KEYBOARD_INDICATORS: CO_CHECKPACKETCAST(TS_SET_KEYBOARD_INDICATORS_PDU, currentDataLen, hr); COPR_MUSTBEUNCOMP(TS_SET_KEYBOARD_INDICATORS_PDU, pDataHdr, hr); TRC_DBG((TB, _T("TS_PDUTYPE2_SET_KEYBOARD_INDICATORS PDU"))); hr = CO_OnSetKeyboardIndicatorsPDU ((PTS_SET_KEYBOARD_INDICATORS_PDU)pCurrentPDU, currentDataLen); DC_QUIT_ON_FAIL(hr); break; case TS_PDUTYPE2_SET_KEYBOARD_IME_STATUS: { PTS_SET_KEYBOARD_IME_STATUS_PDU pImePDU; CO_CHECKPACKETCAST(TS_SET_KEYBOARD_IME_STATUS_PDU, currentDataLen, hr); COPR_MUSTBEUNCOMP(TS_SET_KEYBOARD_IME_STATUS_PDU, pDataHdr, hr); TRC_DBG((TB, _T("TS_PDUTYPE2_SET_KEYBOARD_IME_STATUS PDU"))); pImePDU = (PTS_SET_KEYBOARD_IME_STATUS_PDU)pCurrentPDU; _pIh->IH_SetKeyboardImeStatus(pImePDU->ImeOpen, pImePDU->ImeConvMode); break; } case TS_PDUTYPE2_SET_ERROR_INFO_PDU: { PTS_SET_ERROR_INFO_PDU pErrInfoPDU; CO_CHECKPACKETCAST(TS_SET_ERROR_INFO_PDU, currentDataLen, hr); COPR_MUSTBEUNCOMP(TS_SET_ERROR_INFO_PDU, pDataHdr, hr); TRC_DBG((TB, _T("TS_SET_ERROR_INFO_PDU PDU"))); pErrInfoPDU = (PTS_SET_ERROR_INFO_PDU)pCurrentPDU; _pUi->UI_SetServerErrorInfo(pErrInfoPDU->errorInfo); break; } case TS_PDUTYPE2_ARC_STATUS_PDU: { PTS_AUTORECONNECT_STATUS_PDU pArcStatusPDU; CO_CHECKPACKETCAST(TS_AUTORECONNECT_STATUS_PDU, currentDataLen, hr); COPR_MUSTBEUNCOMP(TS_AUTORECONNECT_STATUS_PDU, pDataHdr, hr); TRC_DBG((TB, _T("TS_PDUTYPE2_ARC_STATUS_PDU"))); pArcStatusPDU = (PTS_AUTORECONNECT_STATUS_PDU)pCurrentPDU; _pUi->UI_OnReceivedArcStatus(pArcStatusPDU->arcStatus); break; } default: TRC_ABORT((TB, _T("Invalid pduType2 %#x"), pDataHdr->pduType2)); break; } } break; case TS_PDUTYPE_DEMANDACTIVEPDU: TRC_DBG((TB, _T("DemandActivePDU"))); CO_CHECKPACKETCAST(TS_DEMAND_ACTIVE_PDU, currentDataLen, hr); _pCd->CD_DecoupleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_OnDemandActivePDU), pCurrentPDU, currentDataLen); break; case TS_PDUTYPE_DEACTIVATEALLPDU: TRC_DBG((TB, _T("DeactivateAllPDU"))); _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pCc, CD_NOTIFICATION_FUNC(CCC,CC_Event), (ULONG_PTR)CC_EVT_API_ONDEACTIVATEALL); break; case TS_PDUTYPE_DEACTIVATESELFPDU: case TS_PDUTYPE_DEACTIVATEOTHERPDU: case TS_PDUTYPE_CONFIRMACTIVEPDU: case TS_PDUTYPE_REQUESTACTIVEPDU: TRC_ERR((TB, _T("PDU type %x unexpected!"), pCtrlHdr->pduType)); break; default: TRC_ABORT((TB, _T("Unrecognized PDU type: %#x"), pCtrlHdr->pduType)); break; } } else { TRC_NRM((TB, _T("FlowPDU"))); pFlowPDU = (PTS_FLOW_PDU)pData; switch (pFlowPDU->pduType) { case TS_PDUTYPE_FLOWTESTPDU: TRC_NRM((TB, _T("FlowTestPDU ignored"))); break; case TS_PDUTYPE_FLOWRESPONSEPDU: TRC_NRM((TB, _T("FlowResponsePDU ignored"))); break; default: TRC_ABORT((TB, _T("Unknown FlowPDU %#x"), pFlowPDU->pduType)); break; } DC_QUIT; } // Now look for the next PDU in the packet. pCurrentPDU += pCtrlHdr->totalLength; dataRemaining -= pCtrlHdr->totalLength; if ((DCUINT)(pCurrentPDU - pData) >= dataLen) { TRC_NRM((TB, _T("Last PDU in packet"))); pCurrentPDU = NULL; } #ifdef DC_DEBUG countPDU++; #endif } #ifdef DC_DEBUG if (countPDU > 1) { TRC_NRM((TB, _T("*** PDU count %u"), countPDU)); } #endif DC_EXIT_POINT: if (FAILED(hr) && IMMEDIATE_DISSCONNECT_ON_HR(hr)) { TRC_ABORT((TB, _T("Disconnect for security"))); CO_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT, hr); } DC_END_FN(); return hr; } /* CO_OnPacketReceived */ /****************************************************************************/ // CO_OnFastPathOutputReceived // // Handles fast-path output from the server by dispatching subpackets // to the right handler component. /****************************************************************************/ #define DC_QUIT_ON_FAIL_TRC(hr, trc) if (FAILED(hr)) {TRC_ABORT( trc );DC_QUIT;} HRESULT DCAPI CCO::CO_OnFastPathOutputReceived(BYTE FAR *pData, unsigned DataLen) { HRESULT hr = S_OK; unsigned RawPDUSize; unsigned HdrSize; unsigned PDUSize; BYTE FAR *pPDU; DC_BEGIN_FN("CO_OnFastPathOutputReceived"); // Fast-path output is a series of PDUs packed on byte boundaries. while (DataLen) { // First byte is header containing the update type and compression- // used flag. If compression-used is true the following byte is the // compression flags, otherwise it's not present. Final part of // header is 2-byte little-endian size field. if (*pData & TS_OUTPUT_FASTPATH_COMPRESSION_USED) { HdrSize = 4; if (HdrSize > DataLen) { TRC_ABORT((TB, _T("Bad comp fast path PDU"))); hr = E_TSC_CORE_LENGTH; DC_QUIT; } RawPDUSize = *((UINT16 UNALIGNED FAR *)(pData + 2)); // Be sure there is enough size for the header if (HdrSize + RawPDUSize > DataLen) { TRC_ABORT((TB, _T("Bad comp fast path PDU; [need %u have %u]"), HdrSize + RawPDUSize, DataLen)); hr = E_TSC_CORE_LENGTH; DC_QUIT; } if (pData[1] & PACKET_COMPRESSED) { if (pData[1] & PACKET_FLUSHED) initrecvcontext (&_pUi->_UI.Context1, (RecvContext2_Generic*)_pUi->_UI.pRecvContext2, PACKET_COMPR_TYPE_64K); if (!decompress(pData + 4, RawPDUSize, pData[1] & PACKET_AT_FRONT, &pPDU, (PDCINT) &PDUSize, &_pUi->_UI.Context1, (RecvContext2_Generic*)_pUi->_UI.pRecvContext2, pData[1] & PACKET_COMPR_TYPE_MASK)) { TRC_ABORT((TB, _T("Decompression FAILURE!!!"))); hr = E_TSC_UI_DECOMPRESSION; DC_QUIT; } } else { pPDU = pData + 4; PDUSize = RawPDUSize; } } else { // Compression flags not present. HdrSize = 3; if (HdrSize > DataLen) { TRC_ABORT((TB, _T("Bad uncomp fast path PDU; [need %u have %u]"), HdrSize, DataLen)); hr = E_TSC_CORE_LENGTH; DC_QUIT; } PDUSize = RawPDUSize = *((UINT16 UNALIGNED FAR *)(pData + 1)); pPDU = pData + 3; // Be sure there is enough size for the header if (HdrSize + RawPDUSize > DataLen) { TRC_ABORT((TB, _T("Bad uncomp fast path PDU; [need %u have %u]"), HdrSize + RawPDUSize, DataLen)); hr = E_TSC_CORE_LENGTH; DC_QUIT; } } switch (*pData & TS_OUTPUT_FASTPATH_UPDATETYPE_MASK) { case TS_UPDATETYPE_ORDERS: // Number of orders is in little-endian format in // the first two bytes of the PDU area. TRC_NRM((TB, _T("Fast-path Order PDU"))); hr = _pUh->UH_ProcessOrders(*((UINT16 UNALIGNED FAR *)pPDU), pPDU + 2, PDUSize); DC_QUIT_ON_FAIL_TRC( hr, (TB,_T("UH_ProcessOrders"))); break; case TS_UPDATETYPE_BITMAP: // BitmapPDU format unchanged from normal path. CO_CHECKPACKETCAST(TS_UPDATE_BITMAP_PDU_DATA, PDUSize, hr); TRC_NRM((TB, _T("Bitmap PDU"))); hr = _pUh->UH_ProcessBitmapPDU( (TS_UPDATE_BITMAP_PDU_DATA UNALIGNED FAR *)pPDU, PDUSize); DC_QUIT_ON_FAIL_TRC( hr, (TB,_T("UH_ProcessBitmapPDU"))); break; case TS_UPDATETYPE_PALETTE: CO_CHECKPACKETCAST(TS_UPDATE_PALETTE_PDU_DATA, PDUSize, hr); // PalettePDU format unchanged from normal path. TRC_NRM((TB, _T("Palette PDU"))); hr = _pUh->UH_ProcessPalettePDU( (TS_UPDATE_PALETTE_PDU_DATA UNALIGNED FAR *)pPDU, PDUSize); DC_QUIT_ON_FAIL_TRC( hr, (TB,_T("UH_ProcessPalettePDU"))); break; case TS_UPDATETYPE_SYNCHRONIZE: TRC_NRM((TB, _T("Sync PDU"))); break; case TS_UPDATETYPE_MOUSEPTR_SYSTEM_NULL: TRC_NRM((TB,_T("Mouse null system pointer PDU"))); _pCm->CM_NullSystemPointerPDU(); break; case TS_UPDATETYPE_MOUSEPTR_SYSTEM_DEFAULT: TRC_NRM((TB,_T("Mouse default system pointer PDU"))); _pCm->CM_DefaultSystemPointerPDU(); break; case TS_UPDATETYPE_MOUSEPTR_MONO: CO_CHECKPACKETCAST(TS_MONOPOINTERATTRIBUTE, PDUSize, hr); TRC_NRM((TB,_T("Mouse mono pointer PDU"))); hr = _pCm->CM_MonoPointerPDU( (TS_MONOPOINTERATTRIBUTE UNALIGNED FAR *)pPDU,PDUSize); DC_QUIT_ON_FAIL_TRC( hr, (TB,_T("CM_MonoPointerPDU"))); break; case TS_UPDATETYPE_MOUSEPTR_POSITION: CO_CHECKPACKETCAST(TS_POINT16, PDUSize, hr); TRC_NRM((TB,_T("Mouse position PDU"))); _pCm->CM_PositionPDU((TS_POINT16 UNALIGNED FAR *)pPDU); break; case TS_UPDATETYPE_MOUSEPTR_COLOR: CO_CHECKPACKETCAST(TS_COLORPOINTERATTRIBUTE, PDUSize, hr); TRC_NRM((TB,_T("Mouse color pointer PDU"))); hr = _pCm->CM_ColorPointerPDU( (TS_COLORPOINTERATTRIBUTE UNALIGNED FAR *)pPDU, PDUSize); DC_QUIT_ON_FAIL_TRC( hr, (TB,_T("CM_ColorPointerPDU"))); break; case TS_UPDATETYPE_MOUSEPTR_CACHED: CO_CHECKPACKETCAST(TSUINT16, PDUSize, hr); TRC_NRM((TB,_T("Mouse cached pointer PDU"))); _pCm->CM_CachedPointerPDU(*((TSUINT16 UNALIGNED FAR *)pPDU)); break; case TS_UPDATETYPE_MOUSEPTR_POINTER: CO_CHECKPACKETCAST(TS_POINTERATTRIBUTE, PDUSize, hr); TRC_NRM((TB,_T("Mouse pointer PDU"))); hr = _pCm->CM_PointerPDU( (TS_POINTERATTRIBUTE UNALIGNED FAR *)pPDU, PDUSize); DC_QUIT_ON_FAIL_TRC( hr, (TB,_T("CM_PointerPDU"))); break; default: TRC_ERR((TB, _T("Unexpected Update PDU type: %u"), *pData & TS_OUTPUT_FASTPATH_UPDATETYPE_MASK)); break; } pData += HdrSize + RawPDUSize; DataLen -= HdrSize + RawPDUSize; } /************************************************************************/ /* If there are a large number of PDUs arriving, messages flood the */ /* Receive Thread's message queue and it is possible for WM_PAINT */ /* messages to not get processed within a reasonable amount of time */ /* (as they have the lowest priority). We therefore ensure that */ /* any outstanding WM_PAINTs are flushed if they have not been */ /* processed within UH_WORST_CASE_WM_PAINT_PERIOD. */ /* */ /* Note that the normal processing of updates does not involve */ /* WM_PAINT messages - we draw directly to the Output Window. */ /* WM_PAINTs are only generated by resizing or obscuring/revealing */ /* an area of the client window. */ /************************************************************************/ _pOp->OP_MaybeForcePaint(); DC_EXIT_POINT: if (FAILED(hr) && IMMEDIATE_DISSCONNECT_ON_HR(hr)) { TRC_ABORT((TB, _T("Disconnect for security"))); CO_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT, hr); } DC_END_FN(); return hr; } #define CHECK_ULONGLEN_STRING(p, pEnd, str, ulSize, descr) \ {\ CHECK_READ_N_BYTES(p, pEnd, sizeof(ULONG), hr, (TB,_T("can not read ") _T( #descr ) _T("size"))) \ (ulSize) = *((ULONG UNALIGNED *)(p)); \ (str) = (PBYTE)(p) + sizeof(ULONG); \ CHECK_READ_N_BYTES(str , pEnd, ulSize, hr, (TB,_T("can not read ") _T( #descr ))) \ (p) += (ulSize) + sizeof(ULONG); \ } /****************************************************************************/ // CO_OnServerRedirectionPacket // // Called from SL on receipt of a server redir packet for load balancing. /****************************************************************************/ HRESULT DCAPI CCO::CO_OnServerRedirectionPacket( RDP_SERVER_REDIRECTION_PACKET UNALIGNED *pPkt, DCUINT dataLen) { HRESULT hr = S_OK; WCHAR AlignedAddress[TS_MAX_SERVERADDRESS_LENGTH]; unsigned length; PBYTE pEnd = ((BYTE *)pPkt) + pPkt->Length; BOOL fNeedRedirect = TRUE; DC_BEGIN_FN("CO_OnServerRedirectionPacket"); TRC_DBG((TB, _T("RDP_SERVER_REDIRECTION_PACKET"))); // // Notify the CLX test harness of the redirection packet // _pClx->CLX_RedirectNotify(pPkt, dataLen); if (dataLen < pPkt->Length) { TRC_ABORT(( TB, _T("packet length incorrect"))); hr = E_TSC_CORE_LENGTH; DC_QUIT; } if (pPkt->Flags & RDP_SEC_REDIRECTION_PKT) { // Copy the address to an aligned buffer. length = min(pPkt->Length + sizeof(WCHAR) - sizeof(RDP_SERVER_REDIRECTION_PACKET), sizeof(AlignedAddress)); if (length > 0 && length <= sizeof(AlignedAddress)) { memcpy(AlignedAddress, pPkt->ServerAddress, length); // Set the redir info in UI and disconnect. _pUi->UI_SetServerRedirectionInfo(pPkt->SessionID, AlignedAddress, NULL, 0, fNeedRedirect); } } else if (pPkt->Flags & RDP_SEC_REDIRECTION_PKT2) { RDP_SERVER_REDIRECTION_PACKET_V2 UNALIGNED *pPkt2 = (RDP_SERVER_REDIRECTION_PACKET_V2 UNALIGNED*)pPkt; PBYTE LBInfo = NULL; PBYTE ServerName = NULL; PBYTE pBuf = NULL; unsigned LBInfoSize = 0; unsigned ServerNameSize = 0; pBuf = (PBYTE)(pPkt2 + 1); if (pPkt2->RedirFlags & TARGET_NET_ADDRESS) { CHECK_ULONGLEN_STRING(pBuf, pEnd, ServerName, ServerNameSize, TARGET_NET_ADDRESS); } if (pPkt2->RedirFlags & LOAD_BALANCE_INFO) { CHECK_ULONGLEN_STRING(pBuf, pEnd, LBInfo, LBInfoSize, LOAD_BALANCE_INFO); } if (ServerNameSize > 0 && ServerNameSize <= sizeof(AlignedAddress)) { memcpy(AlignedAddress, ServerName, ServerNameSize); // Set the redir info in UI and disconnect. _pUi->UI_SetServerRedirectionInfo(pPkt2->SessionID, AlignedAddress, NULL, 0, fNeedRedirect); } else { // Set the redir info in UI and disconnect. That LBInfo is going to // be there indicates to XT_SendCR that we are in the middle of // a redirect and should use the redirect info instead of scripting // or hash-mode cookie. _pUi->UI_SetServerRedirectionInfo(pPkt2->SessionID, _pUi->_UI.strAddress, LBInfo, LBInfoSize, fNeedRedirect); } } else if (pPkt->Flags & RDP_SEC_REDIRECTION_PKT3) { RDP_SERVER_REDIRECTION_PACKET_V3 UNALIGNED *pPkt3 = (RDP_SERVER_REDIRECTION_PACKET_V3 UNALIGNED*)pPkt; PBYTE LBInfo = NULL; PBYTE ServerName = NULL; PBYTE pBuf = NULL; PBYTE pUserName = NULL; PBYTE pDomain = NULL; PBYTE pClearPassword = NULL; unsigned LBInfoSize = 0; unsigned ServerNameSize = 0; unsigned UserNameSize = 0; unsigned DomainSize = 0; unsigned PasswordSize = 0; pBuf = (PBYTE)(pPkt3 + 1); if (pPkt3->RedirFlags & TARGET_NET_ADDRESS) { CHECK_ULONGLEN_STRING(pBuf, pEnd, ServerName, ServerNameSize, TARGET_NET_ADDRESS); } if (pPkt3->RedirFlags & LOAD_BALANCE_INFO) { CHECK_ULONGLEN_STRING(pBuf, pEnd, LBInfo, LBInfoSize, LOAD_BALANCE_INFO); } if (pPkt3->RedirFlags & LB_USERNAME) { CHECK_ULONGLEN_STRING(pBuf, pEnd, pUserName, UserNameSize, LB_USERNAME); } if (pPkt3->RedirFlags & LB_DOMAIN) { CHECK_ULONGLEN_STRING(pBuf, pEnd, pDomain, DomainSize, LB_DOMAIN); } if (pPkt3->RedirFlags & LB_PASSWORD) { CHECK_ULONGLEN_STRING(pBuf, pEnd, pClearPassword, PasswordSize, LB_PASSWORD); } if (pPkt3->RedirFlags & LB_SMARTCARD_LOGON) { _pUi->UI_SetUseSmartcardLogon(TRUE); } if ((pPkt3->RedirFlags & LB_NOREDIRECT) != 0) { fNeedRedirect = FALSE; } if (UserNameSize > 0) { PBYTE pAlignedUserName = (PBYTE)LocalAlloc(LPTR, UserNameSize+sizeof(TCHAR)); if (pAlignedUserName) { memset(pAlignedUserName, 0, UserNameSize+sizeof(TCHAR)); memcpy(pAlignedUserName, pUserName, UserNameSize); if (pPkt3->RedirFlags & LB_DONTSTOREUSERNAME) { _pUi->UI_SetRedirectionUserName((LPTSTR)pAlignedUserName); } else { _pUi->UI_SetUserName((LPTSTR)pAlignedUserName); } LocalFree(pAlignedUserName); } } if (DomainSize > 0) { PBYTE pAlignedDomain = (PBYTE)LocalAlloc(LPTR, DomainSize+sizeof(TCHAR)); if (pAlignedDomain) { memset(pAlignedDomain, 0, DomainSize+sizeof(TCHAR)); memcpy(pAlignedDomain, pDomain, DomainSize); _pUi->UI_SetDomain((LPTSTR)pAlignedDomain); LocalFree(pAlignedDomain); } } if ((PasswordSize > 0) && ((PasswordSize + sizeof(TCHAR)) <= UI_MAX_PASSWORD_LENGTH)) { PBYTE pAlignedClearPass = (PBYTE)LocalAlloc(LPTR, UI_MAX_PASSWORD_LENGTH); if (pAlignedClearPass) { SecureZeroMemory(pAlignedClearPass, UI_MAX_PASSWORD_LENGTH); memcpy(pAlignedClearPass, pClearPassword, PasswordSize); BYTE Salt[UT_SALT_LENGTH]; // // The password has to be stored in our internal 'obfuscated' // format. // if(TSRNG_GenerateRandomBits(Salt, sizeof(Salt))) { //Encrypt the password if(EncryptDecryptLocalData50(pAlignedClearPass, PasswordSize, Salt, sizeof(Salt))) { _pUi->UI_SetPassword( pAlignedClearPass ); _pUi->UI_SetSalt( Salt); } else { TRC_ERR((TB,_T("Error encrytping password"))); } } else { TRC_ERR((TB,_T("Error generating salt"))); } // // For security reasons clear the password field // SecureZeroMemory(pAlignedClearPass, PasswordSize+sizeof(TCHAR)); SecureZeroMemory(pClearPassword, PasswordSize); LocalFree(pAlignedClearPass); // // Set the autologon flag // if (UserNameSize) { _pUi->_UI.fAutoLogon = TRUE; } } } if (ServerNameSize > 0 && ServerNameSize <= sizeof(AlignedAddress)) { memcpy(AlignedAddress, ServerName, ServerNameSize); // Set the redir info in UI and disconnect. _pUi->UI_SetServerRedirectionInfo(pPkt3->SessionID, AlignedAddress, NULL, 0, fNeedRedirect); } else { // Set the redir info in UI and disconnect. That LBInfo is going to // be there indicates to XT_SendCR that we are in the middle of // a redirect and should use the redirect info instead of scripting // or hash-mode cookie. _pUi->UI_SetServerRedirectionInfo(pPkt3->SessionID, _pUi->_UI.strAddress, LBInfo, LBInfoSize, fNeedRedirect); } } else { TRC_ERR((TB,_T("Unexpected redirection packet"))); } if (fNeedRedirect) { CO_Disconnect(); } DC_EXIT_POINT: if (FAILED(hr) && IMMEDIATE_DISSCONNECT_ON_HR(hr)) { TRC_ABORT((TB, _T("Disconnect for security"))); CO_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT, hr); } DC_END_FN(); return hr; } /****************************************************************************/ /* Name: CO_OnBufferAvailable */ /* */ /* Purpose: Handle BufferAvailable notification from SL. */ /****************************************************************************/ void DCCALLBACK CCO::CO_OnBufferAvailable() { DC_BEGIN_FN("CO_OnBufferAvailable"); // Tell the Sender Thread. _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, _pSnd, CD_NOTIFICATION_FUNC(CSND,SND_BufferAvailable), (ULONG_PTR) 0); DC_END_FN(); } /* CO_OnBufferAvailable */ // // CO_DropLinkImmediate // // Purpose: Immediately drops the link without doing a gracefull connection // shutdown (i.e. no DPUm is sent and we don't transition to the SND // thread at any point before dropping the link). Higher level components // will still get all the usual disconnect notifications so they can // be properly torn down. // // This call was added to trigger an immediate disconnect in cases // where we detect invalid data that could be due to an attack, it // ensures we won't receive any more data after the call returns // // Params: reason - SL disconnect reason code // hrDisconnect - hresult as to why the TSC_CORE wanted to disconnect // // Returns: HRESULT // // Thread context: Call on RCV thread // HRESULT DCINTERNAL CCO::CO_DropLinkImmediate(UINT reason, HRESULT hrDisconnect ) { DC_BEGIN_FN("CO_DropLinkImmediate"); HRESULT hr = E_FAIL; TRC_ASSERT((NULL != _pSl), (TB, _T("SL not connected can not drop immediate"))); if (_pSl) { hr = _pSl->SL_DropLinkImmediate(reason); } COSetDisconnectHR(hrDisconnect); DC_END_FN(); return hr; }