//+------------------------------------------------------------------- // // File: excladdr.cxx // // Contents: Implements classes for managing the current address // exclusion list // // Classes: CAddrExclusionMgr // // History: 07-Oct-00 jsimmons Created //-------------------------------------------------------------------- #include "act.hxx" // The single instance of this object CAddrExclusionMgr gAddrExclusionMgr; CAddrExclusionMgr::CAddrExclusionMgr() : _dwNumStrings(0), _ppszStrings(NULL), _bInitRegistry(FALSE) { } // // GetExclusionList // // Returns the current exclusion list. // HRESULT CAddrExclusionMgr::GetExclusionList( DWORD* pdwNumStrings, LPWSTR** pppszStrings) { gpClientLock->LockExclusive(); // Handle easy case if (_dwNumStrings == 0) { *pdwNumStrings = 0; *pppszStrings = NULL; gpClientLock->UnlockExclusive(); return S_OK; } DWORD i; DWORD dwCopiedStrings; LPWSTR* ppszCopiedStrings; ppszCopiedStrings = (LPWSTR*)MIDL_user_allocate(sizeof(WCHAR*) * _dwNumStrings); if (!ppszCopiedStrings) { gpClientLock->UnlockExclusive(); return E_OUTOFMEMORY; } dwCopiedStrings = 0; for (i = 0; i < _dwNumStrings; i++) { if (_ppszStrings[i]) { ppszCopiedStrings[i] = (LPWSTR)MIDL_user_allocate( sizeof(WCHAR) * (lstrlen(_ppszStrings[i]) + 1)); if (!ppszCopiedStrings[i]) { // failure in the middle. cleanup previous allocations and return for (i = 0; i < _dwNumStrings; i++) { if (ppszCopiedStrings[i]) MIDL_user_free(ppszCopiedStrings[i]); } MIDL_user_free(ppszCopiedStrings); gpClientLock->UnlockExclusive(); return E_OUTOFMEMORY; } lstrcpy(ppszCopiedStrings[i], _ppszStrings[i]); dwCopiedStrings++; } } *pdwNumStrings = dwCopiedStrings; *pppszStrings = ppszCopiedStrings; gpClientLock->UnlockExclusive(); return S_OK; } // // SetExclusionList // // Sets the current exclusion list, and pushes the // new bindings to all current running processes // registered with COM. // HRESULT CAddrExclusionMgr::SetExclusionList( DWORD dwNumStrings, LPWSTR* ppszStrings) { HRESULT hr = S_OK; RPC_STATUS status; DWORD i, j; LPWSTR* ppszStringsNew = NULL; gpClientLock->LockExclusive(); if (dwNumStrings > 0) { ppszStringsNew = (LPWSTR*)PrivMemAlloc(sizeof(WCHAR*) * dwNumStrings); if (!ppszStringsNew) { gpClientLock->UnlockExclusive(); return E_OUTOFMEMORY; } for (i = 0; i < dwNumStrings; i++) { // Skip any null entries. Assert in debug builds since // this is likely a sign of a broken app ASSERT(ppszStrings[i]); if (!ppszStrings[i]) continue; ppszStringsNew[i] = (LPWSTR)PrivMemAlloc( sizeof(WCHAR) * (lstrlen(ppszStrings[i]) + 1)); if (!ppszStringsNew[i]) { // free up any earlier allocations that succeeded for (j = 0; j < i; j++) PrivMemFree(ppszStringsNew[j]); PrivMemFree(ppszStringsNew); gpClientLock->UnlockExclusive(); // and return error return E_OUTOFMEMORY; } lstrcpy(ppszStringsNew[i], ppszStrings[i]); } } // Get rid of the old list FreeCurrentBuffers(); // Save off the new one _dwNumStrings = dwNumStrings; _ppszStrings = ppszStringsNew; // Recompute the bindings status = ComputeNewResolverBindings(); if (status != RPC_S_OK) { hr = E_OUTOFMEMORY; } gpClientLock->UnlockExclusive(); if (status == RPC_S_OK) { // Update currently running processes PushCurrentBindings(); } return hr; } // // EnableDisableDynamicTracking // // Turns on\off the dynamic address binding feature, and updates // the registry key accordingly. // HRESULT CAddrExclusionMgr::EnableDisableDynamicTracking(BOOL fEnable) { HRESULT hr = S_OK; LONG error; HKEY hOle; WCHAR* szY = L"Y"; WCHAR* szN = L"N"; gpClientLock->LockExclusive(); // Open the registry key and change the value error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\OLE", NULL, KEY_WRITE, &hOle); if (error == ERROR_SUCCESS) { error = RegSetValueEx(hOle, L"EnableSystemDynamicIPTracking", 0, REG_SZ, (BYTE*)(fEnable ? szY : szN), 4); // 4 = one wchar + null if (error == ERROR_SUCCESS) { // Reset the global. Will be seen immediately gbDynamicIPChangesEnabled = fEnable ? TRUE : FALSE; } else { hr = HRESULT_FROM_WIN32(GetLastError()); } CloseHandle(hOle); } else { hr = HRESULT_FROM_WIN32(GetLastError()); } gpClientLock->UnlockExclusive(); return hr; } // // IsExcludedAddress // // Checks to see if the specified address is in our // current exclusion list. // BOOL CAddrExclusionMgr::IsExcludedAddress(LPWSTR pszAddress) { DWORD i; ASSERT(gpClientLock->HeldExclusive()); ASSERT(_ppszStrings); for (i = 0; i < _dwNumStrings; i++) { if (_ppszStrings[i]) { if (lstrcmpi(_ppszStrings[i], pszAddress) == 0) { // found it in the list return TRUE; } } } return FALSE; } // // BuildExclusionDSA // // Constructs a new dsa and puts it in *ppdsaOut; the new // dsa is the same as pdsaSrc, minus any addresses that are // currently in the exclusion list. // HRESULT CAddrExclusionMgr::BuildExclusionDSA( DUALSTRINGARRAY* pdsaSrc, DUALSTRINGARRAY** ppdsaOut ) { ASSERT(pdsaSrc && ppdsaOut); ASSERT(dsaValid(pdsaSrc)); ASSERT(gpClientLock->HeldExclusive()); SCMVDATEHEAP(); DWORD i; BOOL bDone; USHORT* pStart; USHORT* pCurrent; USHORT* pCurrentNew; DWORD dwBindingsToExclude = 0; USHORT usNewDSALen = 0; DUALSTRINGARRAY* pdsaNew; BOOL* afIsExcludedAddress; *ppdsaOut = NULL; // If our list is empty, nothing to do if (_dwNumStrings == 0) return dsaAllocateAndCopy(ppdsaOut, pdsaSrc); // Allocate an array of bools on the stack. This // will be used to remember if an address is excluded // or not on the first pass, so we don't have to be // figure that out again on the second pass. // // Note that I will almost always allocate too much stack // memory -- better that than not enough. To be exact // I'd have to make an extra pass thru the dsa to see how // many string bindings there are - which sorta defeats // the purpose. afIsExcludedAddress = (BOOL*)_alloca(sizeof(BOOL) * pdsaSrc->wSecurityOffset); // First make one pass thru the incoming dsa to // see how many addresses we should retain, and // calculate how much space they will need. bDone = FALSE; pStart = pCurrent = &(pdsaSrc->aStringArray[0]); i = 0; do { // Find end of the current string binding. Be careful // to handle cases where there are no string bindings. while (*pCurrent != 0) pCurrent++; if ((i > 0) && (pCurrent == pStart) || ((i == 0) && (*(pCurrent+1) == 0))) { // either found a zero after the previous // binding, or we found two zeroes in a row // at the beginning of pdsaSrc->aStringArray. bDone = TRUE; } else { i++; // count total # of strings found STRINGBINDING* psb = (STRINGBINDING*)pStart; // We only exclude addresses if they are using tcp if (psb->wTowerId == ID_TCP && IsExcludedAddress(&(psb->aNetworkAddr))) { dwBindingsToExclude++; afIsExcludedAddress[i] = TRUE; } else { // Add string len plus 2 (space for towerid + null terminator) usNewDSALen = usNewDSALen + (USHORT) (lstrlen(&(psb->aNetworkAddr)) + 2); afIsExcludedAddress[i] = FALSE; } // advance to next string binding pCurrent++; pStart = pCurrent; } } while (!bDone); // If there are no addresses that need excluding, just copy // and return the incoming bindings. if (dwBindingsToExclude == 0) return dsaAllocateAndCopy(ppdsaOut, pdsaSrc); // If we didn't find any string bindings to use at all, then we // need to manually add a word for the initial NULL. if (usNewDSALen == 0) usNewDSALen = 1; // Add space for wNumEntries + wSecurityOffset usNewDSALen += (sizeof(USHORT) * 2); // Add size of all security bindings usNewDSALen += ((pdsaSrc->wNumEntries - pdsaSrc->wSecurityOffset)); // Add one for the string bindings terminating NULL. usNewDSALen++; // Allocate the memory pdsaNew = (DUALSTRINGARRAY*)MIDL_user_allocate(usNewDSALen * sizeof(USHORT)); if (!pdsaNew) return E_OUTOFMEMORY; ZeroMemory(pdsaNew, usNewDSALen * sizeof(USHORT)); // Set length pdsaNew->wNumEntries = usNewDSALen - 2; // don't count wNumEntries\wSecOffset // Initialize ptr into the new dsa pCurrentNew = &(pdsaNew->aStringArray[0]); // If we are excluding all of the string bindings in pdsaSrc, then // there's no need to make a second pass if (dwBindingsToExclude == i) { // No string bindings left at all. Not a very useful // situation. Need to add the initial NULL: *pCurrentNew = NULL; pCurrentNew++; } else { // Make a second pass, copying the non-excluded addresses // over to the new dsa as we go. bDone = FALSE; pStart = pCurrent = &(pdsaSrc->aStringArray[0]); i = 0; do { // Find end of the current string binding while (*pCurrent != 0) pCurrent++; if ((i > 0) && (pCurrent == pStart) || ((i == 0) && (*(pCurrent+1) == 0))) { // either found a zero after the previous // binding, or we found two zeroes in a row // at the beginning of pdsaSrc->aStringArray. bDone = TRUE; } else { i++; // count total # of strings found if (!afIsExcludedAddress[i]) // remembered from first pass above { STRINGBINDING* psb = (STRINGBINDING*)pStart; STRINGBINDING* psbNew = (STRINGBINDING*)pCurrentNew; // Copy tower id psbNew->wTowerId = psb->wTowerId; // Copy address lstrcpy(&(psbNew->aNetworkAddr), &(psb->aNetworkAddr)); // Move cursor for new dsa (2=towerid + null terminator) pCurrentNew += (2 + lstrlen(&(psbNew->aNetworkAddr))); } pCurrent++; pStart = pCurrent; } } while (!bDone); } // Add final null terminator for string bindings *pCurrentNew = NULL; // points to after last string binding, NULL it out pCurrentNew++; // now points to first security binding // Set security offset pdsaNew->wSecurityOffset = (unsigned short)(pCurrentNew - &(pdsaNew->aStringArray[0])); // Copy security bindings en masse memcpy(pCurrentNew, &(pdsaSrc->aStringArray[pdsaSrc->wSecurityOffset]), (pdsaSrc->wNumEntries - pdsaSrc->wSecurityOffset) * sizeof(USHORT)); ASSERT(dsaValid(pdsaNew)); // Success *ppdsaOut = pdsaNew; SCMVDATEHEAP(); return S_OK; } // // InitializeFromRegistry // // Reads initial address list from registry. Can only // be called once, after that subsequent calls will be // ignored. // void CAddrExclusionMgr::InitializeFromRegistry() { ASSERT(gpClientLock->HeldExclusive()); if (_bInitRegistry) return; _bInitRegistry = TRUE; // this is it, success or fail // UNDONE -- if we wanted to, we could support persisting // the exclusion list in the registry, and read it out // here shortly after boot. return; } // Private function, no lock needed void CAddrExclusionMgr::FreeCurrentBuffers() { ASSERT((_dwNumStrings == 0 && _ppszStrings == 0) || (_dwNumStrings != 0 && _ppszStrings != 0)); DWORD i; for (i = 0; i < _dwNumStrings; i++) { PrivMemFree(_ppszStrings[i]); } if (_ppszStrings) PrivMemFree(_ppszStrings); _dwNumStrings = 0; _ppszStrings = NULL; return; }