#include "precomp.h" #include "events.hpp" #include "ernccm.hpp" #include "erncconf.hpp" #include "erncvrsn.hpp" #include "nccglbl.hpp" extern PController g_pMCSController; GUID g_csguidSecurity = GUID_SECURITY; CWorkItem::~CWorkItem(void) { } // pure virtual BOOL GetSecurityInfo(ConnectionHandle connection_handle, PBYTE pInfo, PDWORD pcbInfo); // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // Implementation of Methods for CInviteIndWork // // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CInviteIndWork:: CInviteIndWork ( PCONFERENCE _pConference, LPCWSTR _wszCallerID, PT120PRODUCTVERSION _pRequestorVersion, GCCUserData **_ppUserData, UINT _nUserData, CLogicalConnection * _pConEntry ) : CWorkItem(_pConference), m_pConf(_pConference), m_pRequestorVersion(_pRequestorVersion), m_nUserData(_nUserData), m_fSecure(_pConEntry->IsConnectionSecure()) { DebugEntry(CInviteIndWork::CInviteIndWork); // If there is version information, then take a copy of it // as this is going asynchronous. if (m_pRequestorVersion) { m_RequestorVersion = *m_pRequestorVersion; } // Take copy of caller ID. // Note memory allocation failure proceeds with NULL ID. m_pwszCallerID = ::My_strdupW(_wszCallerID); // Create the user data list for the ui if(_nUserData) { DBG_SAVE_FILE_LINE m_pUserDataList = new USERDATAINFO[_nUserData]; if (NULL != m_pUserDataList) { for (UINT i = 0; i < m_nUserData; i++) { if ((*_ppUserData)->octet_string->length < sizeof(GUID)) { // skip this user data i--; m_nUserData--; _ppUserData++; continue; } m_pUserDataList[i].pGUID = (GUID*)(*_ppUserData)->octet_string->value; m_pUserDataList[i].pData = (*_ppUserData)->octet_string->value + sizeof(GUID); m_pUserDataList[i].cbData = (*_ppUserData)->octet_string->length - sizeof(GUID); // Verify security data if (0 == CompareGuid(m_pUserDataList[i].pGUID, &g_csguidSecurity)) { // Check data against transport level PBYTE pbData = NULL; DWORD cbData = 0; BOOL fTrust = FALSE; if (m_pUserDataList[i].cbData != 0 && GetSecurityInfo(_pConEntry->GetConnectionHandle(),NULL,&cbData)) { if (cbData) { // We are directly connected, so verify the information pbData = new BYTE[cbData]; if (NULL != pbData) { GetSecurityInfo(_pConEntry->GetConnectionHandle(),pbData,&cbData); if ( m_pUserDataList[i].cbData != cbData || memcmp(pbData, m_pUserDataList[i].pData, cbData)) { WARNING_OUT(("SECURITY MISMATCH: (%s) vs (%s)", pbData, m_pUserDataList[i].pData)); } else { // Verification OK fTrust = TRUE; } delete [] pbData; } else { ERROR_OUT(("Failed to alloc %d bytes for security data verification", cbData)); } } } if (FALSE == fTrust) { // Leave the security GUID in place, but NULL out the data to signal distrust. WARNING_OUT(("CInviteIndWork: Nulling out security")); m_pUserDataList[i].pData = NULL; m_pUserDataList[i].cbData = 0; } } _ppUserData++; } } else { ERROR_OUT(("CInviteIndWork::CInviteIndWork: Out of memory")); m_nUserData = 0; } } else { m_pUserDataList = NULL; } DebugExitVOID(CInviteIndWork::CInviteIndWork); } CInviteIndWork:: ~CInviteIndWork(void) { DebugEntry(CInviteIndWork::~CInviteIndWork); // // If we substituted transport security data for roster data, // free that buffer now // delete m_pwszCallerID; delete [] m_pUserDataList; DebugExitVOID(CInviteIndWork::~CInviteIndWork); } void CInviteIndWork:: DoWork(void) { DebugEntry(CInviteIndWork::DoWork); // Now we are actually processing the invite, validate that there // are no other conferences of the same name, and, if not, block // a conference of the same name by setting the conference to be active, // and give invite request up to the UI. PCONFERENCE pOtherConf = g_pNCConfMgr->GetConferenceFromName(m_pConf->GetName()); if (NULL == pOtherConf) { m_pConf->SetNotifyToDo(TRUE); g_pCallbackInterface->OnIncomingInviteRequest((CONF_HANDLE) m_pConf, GetCallerID(), m_pRequestorVersion, m_pUserDataList, m_nUserData, m_fSecure); } else { m_pConf->InviteResponse(UI_RC_CONFERENCE_ALREADY_EXISTS); } DebugExitVOID(CInviteIndWork::DoWork); } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // Implementation of Methods for CJoinIndWork // // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CJoinIndWork:: CJoinIndWork ( GCCResponseTag Tag, PCONFERENCE _pConference, LPCWSTR _wszCallerID, CLogicalConnection *_pConEntry, PT120PRODUCTVERSION _pRequestorVersion, UINT _nUserData, GCCUserData **_ppUserData, HRESULT *pRetCode ) : CWorkItem(_pConference), m_nResponseTag(Tag), m_pConf(_pConference), m_pConEntry(_pConEntry), m_pRequestorVersion(_pRequestorVersion), m_nUserData(_nUserData), m_pUserDataList(NULL), m_ppUserData(NULL) { DebugEntry(CJoinIndWork::CJoinIndWork); *pRetCode = NO_ERROR; #ifdef DEBUG SOCKET socket_number; g_pMCSController->FindSocketNumber(m_pConEntry->GetConnectionHandle(),&socket_number); #endif // If there is version information, then take a copy of it // as this is going asynchronous. if (m_pRequestorVersion) { m_RequestorVersion = *m_pRequestorVersion; } // Take copy of caller ID because T120 // implementation is not keeping its copy valid // until the join response. // Note that memory allocation failure proceeds with // NULL caller ID. m_pwszCallerID = ::My_strdupW(_wszCallerID); // Add the user data list for forwarded join requests and the UI. if (m_nUserData && NULL != _ppUserData) { DBG_SAVE_FILE_LINE m_pUserDataList = new USERDATAINFO[m_nUserData]; if (NULL != m_pUserDataList) { ::ZeroMemory(m_pUserDataList, sizeof(USERDATAINFO) * m_nUserData); DBG_SAVE_FILE_LINE m_ppUserData = new GCCUserData * [m_nUserData]; if (NULL != m_ppUserData) { ::ZeroMemory(m_ppUserData, sizeof(GCCUserData *) * m_nUserData); for (UINT i = 0; i < m_nUserData; i++) { // Calculate the total size to allocate for this entry. UINT cbUserDataStructSize = ROUNDTOBOUNDARY(sizeof(GCCUserData)); UINT cbNonStdIDSize = ROUNDTOBOUNDARY((* _ppUserData)->key.h221_non_standard_id.length); UINT cbOctetStringSize = ROUNDTOBOUNDARY((* _ppUserData)->octet_string->length); UINT cbTotalSize = cbUserDataStructSize + cbNonStdIDSize + sizeof(OSTR) + cbOctetStringSize; // Allocate a single memory buffer DBG_SAVE_FILE_LINE LPBYTE pData = new BYTE[cbTotalSize]; if (NULL != pData) { // Set up pointers GCCUserData *pUserData = (GCCUserData *) pData; ::ZeroMemory(pUserData, sizeof(GCCUserData)); pUserData->key.h221_non_standard_id.value = (LPBYTE) (pData + cbUserDataStructSize); pUserData->octet_string = (LPOSTR) (pData + cbUserDataStructSize + cbNonStdIDSize); pUserData->octet_string->value = ((LPBYTE) pUserData->octet_string) + sizeof(OSTR); // Copy user data to prevent it from being lost when callback message is freed. m_ppUserData[i] = pUserData; // Copy key pUserData->key.key_type = (* _ppUserData)->key.key_type; ASSERT(pUserData->key.key_type == GCC_H221_NONSTANDARD_KEY); pUserData->key.h221_non_standard_id.length = (* _ppUserData)->key.h221_non_standard_id.length; ::CopyMemory(pUserData->key.h221_non_standard_id.value, (* _ppUserData)->key.h221_non_standard_id.value, pUserData->key.h221_non_standard_id.length); // Copy data pUserData->octet_string->length = (* _ppUserData)->octet_string->length; ::CopyMemory(pUserData->octet_string->value, (* _ppUserData)->octet_string->value, pUserData->octet_string->length); m_pUserDataList[i].pGUID = (GUID *)pUserData->octet_string->value; m_pUserDataList[i].cbData = pUserData->octet_string->length - sizeof(GUID); m_pUserDataList[i].pData = pUserData->octet_string->value + sizeof(GUID); if (0 == CompareGuid(m_pUserDataList[i].pGUID, &g_csguidSecurity)) { // Check data against transport level PBYTE pbData = NULL; DWORD cbData = 0; BOOL fTrust = FALSE; if (m_pUserDataList[i].cbData != 0 && GetSecurityInfo(m_pConEntry->GetConnectionHandle(),NULL,&cbData)) { if (cbData == NOT_DIRECTLY_CONNECTED) { // This means we are not directly connected, // transitivity. so trust by fTrust = TRUE; } else { pbData = new BYTE[cbData]; if (NULL != pbData) { GetSecurityInfo(m_pConEntry->GetConnectionHandle(),pbData,&cbData); // Does the data match? if (cbData != m_pUserDataList[i].cbData || memcmp(pbData, m_pUserDataList[i].pData, cbData)) { WARNING_OUT(("SECURITY MISMATCH: (%s) vs (%s)", pbData, m_pUserDataList[i].pData)); } else { fTrust = TRUE; } delete [] pbData; } else { ERROR_OUT(("Failed to alloc %d bytes for security data verification", cbData)); } } } if (FALSE == fTrust) { // Leave the security GUID in place, but NULL out the data to signal distrust. m_pUserDataList[i].pData = NULL; m_pUserDataList[i].cbData = 0; pUserData->octet_string->length = sizeof(GUID); } } _ppUserData++; } else { ERROR_OUT(("CJoinIndWork::CJoinIndWork: can't create pData, cbTotalSize=%u", cbTotalSize)); *pRetCode = UI_RC_OUT_OF_MEMORY; } } // for } else { ERROR_OUT(("CJoinIndWork::CJoinIndWork: can't create m_ppUserData, m_nUserData=%u", m_nUserData)); *pRetCode = UI_RC_OUT_OF_MEMORY; } } else { ERROR_OUT(("CJoinIndWork::CJoinIndWork: can't create m_pUserDataList, m_nUserData=%u", m_nUserData)); *pRetCode = UI_RC_OUT_OF_MEMORY; } } // if DebugExitVOID(CJoinIndWork::CJoinIndWork); } CJoinIndWork:: ~CJoinIndWork(void) { DebugEntry(CJoinIndWork::~CJoinIndWork); delete m_pwszCallerID; for (UINT i = 0; i < m_nUserData; i++) { delete (LPBYTE) m_ppUserData[i]; // pData in the constructor } delete m_ppUserData; delete m_pUserDataList; DebugExitVOID(CJoinIndWork::~CJoinIndWork); } void CJoinIndWork:: DoWork(void) { DebugEntry(CJoinIndWork::DoWork); // Notify the core. g_pCallbackInterface->OnIncomingJoinRequest((CONF_HANDLE) m_pConf, m_pwszCallerID, m_pRequestorVersion, m_pUserDataList, m_nUserData); DebugExitVOID(CJoinIndWork::DoWork); } HRESULT CJoinIndWork:: Respond ( GCCResult _Result ) { DebugEntry(CJoinIndWork::Respond); // It is a response from the core. HRESULT hr = ::GCCJoinResponseWrapper(m_nResponseTag, NULL, _Result, m_pConf->GetID(), m_nUserData, m_ppUserData); DebugExitHRESULT(CJoinIndWork::Respond, hr); return hr; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // Implementation of Methods for CSequentialWorkList // // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void CSequentialWorkList:: AddWorkItem ( CWorkItem *pWorkItem ) { DebugEntry(CSequentialWorkList::AddWorkItem); Append(pWorkItem); // If first entry in list, then kick off handler. if (1 == GetCount()) { pWorkItem->DoWork(); } DebugExitVOID(CSequentialWorkList::AddWorkItem); } void CSequentialWorkList:: RemoveWorkItem ( CWorkItem *pWorkItem ) { DebugEntry(CSequentialWorkList::RemoveWorkItem); if (pWorkItem) { // Make a note as to whether we are going to remove the head // work item in the list. BOOL bHeadItemRemoved = (pWorkItem == PeekHead()); // Remove work item from list and destroy it. if (Remove(pWorkItem)) { delete pWorkItem; // If there are more entries in the list, and we removed the // first one, then start the work of the next one in line. // Note that before doing this, the pointer to the workitem // was NULLed out (above) to prevent reentracy problems. if (bHeadItemRemoved && !IsEmpty()) { PeekHead()->DoWork(); } } else { ASSERT(! bHeadItemRemoved); } } DebugExitVOID(CSequentialWorkList::RemoveWorkItem); } void CSequentialWorkList:: PurgeListEntriesByOwner ( DCRNCConference *pOwner ) { CWorkItem *pWorkItem; DebugEntry(CSequentialWorkList::PurgeListEntriesByOwner); if (NULL != pOwner) { // Note that head entry is removed last to stop work being started // on other entries in the list that are owned by pOwner. // Check to ensure there is a head item in the list. if (NULL != (pWorkItem = PeekHead())) { // Remember we are going to remove the head. BOOL fHeadToRemove = pWorkItem->IsOwnedBy(pOwner); // Walk remaining entries in the list removing them. BOOL fMoreToRemove; do { fMoreToRemove = FALSE; Reset(); while (NULL != (pWorkItem = Iterate())) { if (pWorkItem->IsOwnedBy(pOwner)) { Remove(pWorkItem); delete pWorkItem; fMoreToRemove = TRUE; break; } } } while (fMoreToRemove); // Now done removing all entries, including the head if needed... if (fHeadToRemove && ! IsEmpty()) { PeekHead()->DoWork(); } } } DebugExitVOID(CSequentialWorkList::PurgeListEntriesByOwner); } void CSequentialWorkList:: DeleteList(void) { CWorkItem *pWorkItem; while (NULL != (pWorkItem = Get())) { delete pWorkItem; } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // Implementation of Methods for CQueryRemoteWork // // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CQueryRemoteWork:: CQueryRemoteWork ( LPVOID pCallerContext, GCCAsymmetryType eAsymType, LPCSTR pcszNodeAddress, BOOL fSecure, HRESULT *pRetCode ) : CWorkItem(pCallerContext), m_hGCCConnHandle(NULL), m_apConfNames(NULL), m_fRemoteIsMCU(FALSE), m_eAsymType(eAsymType), m_pVersion(NULL), m_fSecure(fSecure), m_apConfDescriptors(NULL) { DebugEntry(CQueryRemoteWork::CQueryRemoteWork); char szAddress[RNC_MAX_NODE_STRING_LEN]; ::BuildAddressFromNodeDetails((LPSTR) pcszNodeAddress, &szAddress[0]); m_pszAddress = ::My_strdupA(&szAddress[0]); m_hr = (NULL != m_pszAddress) ? NO_ERROR : UI_RC_OUT_OF_MEMORY; *pRetCode = m_hr; DebugExitVOID(CQueryRemoteWork::CQueryRemoteWork); } CQueryRemoteWork:: ~CQueryRemoteWork(void) { LPWSTR *ppTempTargetName; LPWSTR *ppTempTargetDescriptor; DebugEntry(CQueryRemoteWork::~CQueryRemoteWork); // Clean up memory allocated. if (m_apConfNames) { ppTempTargetName = m_apConfNames; while (*ppTempTargetName) { delete *(ppTempTargetName++); } delete [] m_apConfNames; } if (m_apConfDescriptors) { ppTempTargetDescriptor = m_apConfDescriptors; while (*ppTempTargetDescriptor) { delete *(ppTempTargetDescriptor++); } delete [] m_apConfDescriptors; } delete m_pszAddress; DebugExitVOID(CQueryRemoteWork::~CQueryRemoteWork); } void CQueryRemoteWork:: DoWork(void) { GCCError GCCrc; GCCNodeType nodeType; GCCAsymmetryIndicator asymmetry_indicator; DebugEntry(CQueryRemoteWork::DoWork); ::LoadAnnouncePresenceParameters(&nodeType, NULL, NULL, NULL); asymmetry_indicator.asymmetry_type = m_eAsymType; asymmetry_indicator.random_number = 0; if (asymmetry_indicator.asymmetry_type == GCC_ASYMMETRY_UNKNOWN) { m_nRandSeed = (int) ::GetTickCount(); m_LocalAsymIndicator.random_number = ((GenerateRand() << 16) + GenerateRand()); asymmetry_indicator.random_number = m_LocalAsymIndicator.random_number; m_LocalAsymIndicator.asymmetry_type = GCC_ASYMMETRY_UNKNOWN; m_fInUnknownQueryRequest = TRUE; } GCCrc = g_pIT120ControlSap->ConfQueryRequest( nodeType, &asymmetry_indicator, NULL, (TransportAddress) m_pszAddress, m_fSecure, g_nVersionRecords, g_ppVersionUserData, &m_hGCCConnHandle); TRACE_OUT(("GCC call: g_pIT120ControlSap->ConfQueryRequest, rc=%d", GCCrc)); if (NO_ERROR != (m_hr = ::GetGCCRCDetails(GCCrc))) { AsyncQueryRemoteResult(); } DebugExitHRESULT(CQueryRemoteWork::DoWork, m_hr); } void CQueryRemoteWork:: HandleQueryConfirmation ( QueryConfirmMessage * pQueryMessage ) { UINT NumberOfConferences; GCCConferenceDescriptor ** ppConferenceDescriptor; PWSTR * ppTempTargetName; PWSTR ConferenceTextName; GCCConferenceName * pGCCConferenceName; PWSTR * ppTempTargetDescriptor; PWSTR pwszConfDescriptor=NULL; HRESULT hrTmp; DebugEntry(CQueryRemoteWork::HandleQueryConfirmation); // If no error, then package up information. m_hr = ::GetGCCResultDetails(pQueryMessage->result); if (NO_ERROR == m_hr) { m_fRemoteIsMCU = (pQueryMessage->node_type == GCC_MCU); NumberOfConferences = pQueryMessage->number_of_descriptors; DBG_SAVE_FILE_LINE m_apConfNames = new PWSTR[NumberOfConferences + 1]; m_apConfDescriptors = new PWSTR[NumberOfConferences + 1]; if (!m_apConfNames || !m_apConfDescriptors) { m_hr = UI_RC_OUT_OF_MEMORY; } else { ppConferenceDescriptor = pQueryMessage->conference_descriptor_list; ppTempTargetName = m_apConfNames; ppTempTargetDescriptor = m_apConfDescriptors; while (NumberOfConferences--) { pwszConfDescriptor = (*(ppConferenceDescriptor))->conference_descriptor; pGCCConferenceName = &(*(ppConferenceDescriptor++))->conference_name; if (pwszConfDescriptor != NULL) { pwszConfDescriptor = ::My_strdupW(pwszConfDescriptor); } ConferenceTextName = pGCCConferenceName->text_string; if (ConferenceTextName != NULL) { ConferenceTextName = ::My_strdupW(ConferenceTextName); if (!ConferenceTextName) { // Out of memory, give back what we have. m_hr = UI_RC_OUT_OF_MEMORY; break; } } else if (pGCCConferenceName->numeric_string != NULL) { ConferenceTextName = ::AnsiToUnicode((PCSTR)pGCCConferenceName->numeric_string); if (!ConferenceTextName) { // Out of memory, give back what we have. m_hr = UI_RC_OUT_OF_MEMORY; break; } } if (ConferenceTextName) { *(ppTempTargetName++) = ConferenceTextName; *(ppTempTargetDescriptor++) = pwszConfDescriptor; } } *ppTempTargetName = NULL; *ppTempTargetDescriptor = NULL; } } // Copy version information out of message. m_pVersion = ::GetVersionData(pQueryMessage->number_of_user_data_members, pQueryMessage->user_data_list); if (m_pVersion) { m_Version = *m_pVersion; m_pVersion = &m_Version; } m_fInUnknownQueryRequest = FALSE; hrTmp = m_hr; // Propagate the result directly without posting a message. SyncQueryRemoteResult(); DebugExitHRESULT(CQueryRemoteWork::HandleQueryConfirmation, hrTmp); } void CQueryRemoteWork:: SyncQueryRemoteResult(void) { DebugEntry(CQueryRemoteWork::SyncQueryRemoteResult); // Let the user know the result of his request. // The user is expected to call Release() after getting the result, // if he wants to drop the line - and should for errors. // Also, if the user is being called back before the inline code // has filled in the handle, then fill it in here - see comments in // DCRNCConferenceManager::QueryRemote for additional background. g_pCallbackInterface->OnQueryRemoteResult( m_pOwner, m_hr, m_fRemoteIsMCU, m_apConfNames, m_pVersion, m_apConfDescriptors); // If we are not inline, and this request made it into // the sequential work item list, // then remove from list (which will cause item to be deleted), // otherwise, just delete item. g_pQueryRemoteList->RemoveWorkItem(this); DebugExitVOID(CQueryRemoteWork::SyncQueryRemoteResult); } void CQueryRemoteWork:: AsyncQueryRemoteResult(void) { g_pNCConfMgr->PostWndMsg(NCMSG_QUERY_REMOTE_FAILURE, (LPARAM) this); } int CQueryRemoteWork:: GenerateRand(void) { // code from CRT return (((m_nRandSeed = m_nRandSeed * 214013L + 2531011L) >> 16) & 0x7fff); } HRESULT CQueryRemoteWorkList:: Cancel ( LPVOID pCallerContext ) { HRESULT hr = S_FALSE; // if not found CQueryRemoteWork *p; Reset(); while (NULL != (p = Iterate())) { if (p->IsOwnedBy(pCallerContext)) { // clean up the underlying plumbing. g_pIT120ControlSap->CancelConfQueryRequest(p->GetConnectionHandle()); // clean up node controller data. RemoveWorkItem(p); hr = S_OK; break; } } return hr; }