HRESULT HostDnsServiceWin::updateInfo() { HostDnsInformation info; LONG lrc; int rc; std::string strDomain; std::string strSearchList; /* NB: comma separated, no spaces */ /* * We ignore "DhcpDomain" key here since it's not stable. If * there are two active interfaces that use DHCP (in particular * when host uses OpenVPN) then DHCP ACKs will take turns updating * that key. Instead we call GetAdaptersAddresses() below (which * is what ipconfig.exe seems to do). */ for (DWORD regIndex = 0; /**/; ++regIndex) { char keyName[256]; DWORD cbKeyName = sizeof(keyName); DWORD keyType = 0; char keyData[1024]; DWORD cbKeyData = sizeof(keyData); lrc = RegEnumValueA(m->hKeyTcpipParameters, regIndex, keyName, &cbKeyName, 0, &keyType, (LPBYTE)keyData, &cbKeyData); if (lrc == ERROR_NO_MORE_ITEMS) break; if (lrc == ERROR_MORE_DATA) /* buffer too small; handle? */ continue; if (lrc != ERROR_SUCCESS) { LogRel2(("HostDnsServiceWin: RegEnumValue error %d\n", (int)lrc)); return E_FAIL; } if (keyType != REG_SZ) continue; if (cbKeyData > 0 && keyData[cbKeyData - 1] == '\0') --cbKeyData; /* don't count trailing NUL if present */ if (RTStrICmp("Domain", keyName) == 0) { strDomain.assign(keyData, cbKeyData); LogRel2(("HostDnsServiceWin: Domain=\"%s\"\n", strDomain.c_str())); } else if (RTStrICmp("DhcpDomain", keyName) == 0) { std::string strDhcpDomain(keyData, cbKeyData); LogRel2(("HostDnsServiceWin: DhcpDomain=\"%s\"\n", strDhcpDomain.c_str())); } else if (RTStrICmp("SearchList", keyName) == 0) { strSearchList.assign(keyData, cbKeyData); LogRel2(("HostDnsServiceWin: SearchList=\"%s\"\n", strSearchList.c_str())); } } /* statically configured domain name */ if (!strDomain.empty()) { info.domain = strDomain; info.searchList.push_back(strDomain); } /* statically configured search list */ if (!strSearchList.empty()) { vappend(info.searchList, strSearchList, ','); } /* * When name servers are configured statically it seems that the * value of Tcpip\Parameters\NameServer is NOT set, inly interface * specific NameServer value is (which triggers notification for * us to pick up the change). Fortunately, DnsApi seems to do the * right thing there. */ DNS_STATUS status; PIP4_ARRAY pIp4Array = NULL; // NB: must be set on input it seems, despite docs' claim to the contrary. DWORD cbBuffer = sizeof(&pIp4Array); status = DnsQueryConfig(DnsConfigDnsServerList, DNS_CONFIG_FLAG_ALLOC, NULL, NULL, &pIp4Array, &cbBuffer); if (status == NO_ERROR && pIp4Array != NULL) { for (DWORD i = 0; i < pIp4Array->AddrCount; ++i) { char szAddrStr[16] = ""; RTStrPrintf(szAddrStr, sizeof(szAddrStr), "%RTnaipv4", pIp4Array->AddrArray[i]); LogRel2(("HostDnsServiceWin: server %d: %s\n", i+1, szAddrStr)); info.servers.push_back(szAddrStr); } LocalFree(pIp4Array); } /** * DnsQueryConfig(DnsConfigSearchList, ...) is not implemented. * Call GetAdaptersAddresses() that orders the returned list * appropriately and collect IP_ADAPTER_ADDRESSES::DnsSuffix. */ do { PIP_ADAPTER_ADDRESSES pAddrBuf = NULL; ULONG cbAddrBuf = 8 * 1024; bool fReallocated = false; ULONG err; pAddrBuf = (PIP_ADAPTER_ADDRESSES) malloc(cbAddrBuf); if (pAddrBuf == NULL) { LogRel2(("HostDnsServiceWin: failed to allocate %zu bytes" " of GetAdaptersAddresses buffer\n", (size_t)cbAddrBuf)); break; } while (pAddrBuf != NULL) { ULONG cbAddrBufProvided = cbAddrBuf; err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST, NULL, pAddrBuf, &cbAddrBuf); if (err == NO_ERROR) { break; } else if (err == ERROR_BUFFER_OVERFLOW) { LogRel2(("HostDnsServiceWin: provided GetAdaptersAddresses with %zu" " but asked again for %zu bytes\n", (size_t)cbAddrBufProvided, (size_t)cbAddrBuf)); if (RT_UNLIKELY(fReallocated)) /* what? again?! */ { LogRel2(("HostDnsServiceWin: ... not going to realloc again\n")); free(pAddrBuf); pAddrBuf = NULL; break; } PIP_ADAPTER_ADDRESSES pNewBuf = (PIP_ADAPTER_ADDRESSES) realloc(pAddrBuf, cbAddrBuf); if (pNewBuf == NULL) { LogRel2(("HostDnsServiceWin: failed to reallocate %zu bytes\n", (size_t)cbAddrBuf)); free(pAddrBuf); pAddrBuf = NULL; break; } /* try again */ pAddrBuf = pNewBuf; /* cbAddrBuf already updated */ fReallocated = true; } else { LogRel2(("HostDnsServiceWin: GetAdaptersAddresses error %d\n", err)); free(pAddrBuf); pAddrBuf = NULL; break; } } if (pAddrBuf == NULL) break; for (PIP_ADAPTER_ADDRESSES pAdp = pAddrBuf; pAdp != NULL; pAdp = pAdp->Next) { LogRel2(("HostDnsServiceWin: %ls (status %u) ...\n", pAdp->FriendlyName ? pAdp->FriendlyName : L"(null)", pAdp->OperStatus)); if (pAdp->OperStatus != IfOperStatusUp) continue; if (pAdp->DnsSuffix == NULL || *pAdp->DnsSuffix == L'\0') continue; char *pszDnsSuffix = NULL; rc = RTUtf16ToUtf8Ex(pAdp->DnsSuffix, RTSTR_MAX, &pszDnsSuffix, 0, /* allocate */ NULL); if (RT_FAILURE(rc)) { LogRel2(("HostDnsServiceWin: failed to convert DNS suffix \"%ls\": %Rrc\n", pAdp->DnsSuffix, rc)); continue; } AssertContinue(pszDnsSuffix != NULL); AssertContinue(*pszDnsSuffix != '\0'); LogRel2(("HostDnsServiceWin: ... suffix = \"%s\"\n", pszDnsSuffix)); vappend(info.searchList, pszDnsSuffix); RTStrFree(pszDnsSuffix); } free(pAddrBuf); } while (0); if (info.domain.empty() && !info.searchList.empty()) info.domain = info.searchList[0]; if (info.searchList.size() == 1) info.searchList.clear(); HostDnsMonitor::setInfo(info); return S_OK; }
/** * Watcher thread. */ /*static*/ DECLCALLBACK(int) VirtualBoxSDS::i_watcherThreadProc(RTTHREAD hSelf, void *pvUser) { VBoxSDSWatcher *pThis = (VBoxSDSWatcher *)pvUser; VirtualBoxSDS *pVBoxSDS = pThis->pVBoxSDS; RT_NOREF(hSelf); /* * This thread may release references to IVBoxSVCRegistration objects. */ CoInitializeEx(NULL, COINIT_MULTITHREADED); /* * The loop. */ RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect); while (!pThis->fShutdown) { /* * Deal with the todo list. */ uint32_t cHandles = pThis->cHandles; uint32_t cTodos = pThis->cTodos; for (uint32_t i = 0; i < cTodos; i++) { VBoxSDSPerUserData *pUserData = pThis->aTodos[i].Data.pUserData; AssertContinue(pUserData); if (pThis->aTodos[i].hProcess != NULL) { /* Add: */ AssertLogRelMsgBreakStmt(cHandles < RT_ELEMENTS(pThis->aHandles), ("cHandles=%u cTodos=%u i=%u iWatcher=%u\n", cHandles, cTodos, i, pThis->iWatcher), pThis->fShutdown = true); pThis->aHandles[cHandles] = pThis->aTodos[i].hProcess; pThis->aData[cHandles] = pThis->aTodos[i].Data; cHandles++; } else { /* Remove: */ uint32_t cRemoved = 0; uint32_t j = cHandles; while (j-- > 1) if (pThis->aData[j].pUserData == pUserData) { cHandles = pThis->removeHandle(j, cHandles); pUserData->i_release(); cRemoved++; } if (cRemoved != 1) LogRel(("i_watcherThreadProc/#%u: Warning! cRemoved=%u pUserData=%p\n", pThis->iWatcher, cRemoved, pUserData)); } /* Zap the entry in case we assert and leave further up. */ pThis->aTodos[i].Data.setNull(); pThis->aTodos[i].hProcess = NULL; } Assert(cHandles > 0 && cHandles <= RT_ELEMENTS(pThis->aHandles)); pThis->cHandles = cHandles; pThis->cHandlesEffective = cHandles; pThis->cTodos = 0; if (pThis->fShutdown) break; /* * Wait. */ RTCritSectLeave(&pVBoxSDS->m_WatcherCritSect); LogRel(("i_watcherThreadProc/#%u: Waiting on %u handles...\n", pThis->iWatcher, cHandles)); DWORD const dwWait = WaitForMultipleObjects(cHandles, pThis->aHandles, FALSE /*fWaitAll*/, INFINITE); LogRel(("i_watcherThreadProc/#%u: ... wait returned: %#x (%d)\n", pThis->iWatcher, dwWait, dwWait)); uint32_t const iHandle = dwWait - WAIT_OBJECT_0; if (iHandle < cHandles && iHandle > 0) { /* * A VBoxSVC process has terminated. * * Note! We need to take the user data lock before the watcher one here. */ VBoxSDSPerUserData * const pUserData = pThis->aData[iHandle].pUserData; uint32_t const iRevision = pThis->aData[iHandle].iRevision; RTPROCESS const pid = pThis->aData[iHandle].pid; pUserData->i_lock(); RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect); DWORD dwExit = 0; GetExitCodeProcess(pThis->aHandles[iHandle], &dwExit); LogRel(("i_watcherThreadProc/#%u: %p/%s: PID %u/%#x termination detected: %d (%#x) [iRev=%u, cur %u]\n", pThis->iWatcher, pUserData, pUserData->m_strUsername.c_str(), pid, pid, dwExit, dwExit, iRevision, pUserData->m_iTheChosenOneRevision)); /* Remove it from the handle array. */ CloseHandle(pThis->aHandles[iHandle]); pThis->cHandles = cHandles = pThis->removeHandle(iHandle, cHandles); pThis->cHandlesEffective -= 1; /* If the process we were watching is still the current chosen one, unchoose it and decrement the client count. Otherwise we were subject to a deregistration/termination race (unlikely). */ if (pUserData->m_iTheChosenOneRevision == iRevision) { pUserData->i_unchooseTheOne(true /*fIrregular*/); pUserData->i_unlock(); pVBoxSDS->i_decrementClientCount(); } else pUserData->i_unlock(); pUserData->i_release(); } else { RTCritSectEnter(&pThis->pVBoxSDS->m_WatcherCritSect); AssertLogRelMsgBreak(iHandle == 0 || dwWait == WAIT_TIMEOUT, ("dwWait=%u (%#x) cHandles=%u\n", dwWait, dwWait, cHandles)); } } RTCritSectLeave(&pThis->pVBoxSDS->m_WatcherCritSect); /* * In case we quit w/o being told, signal i_watchIt that we're out of action. */ pThis->fShutdown = true; /* * Release all our data on the way out. */ uint32_t i = pThis->cHandles; while (i-- > 1) { if (pThis->aData[i].pUserData) { pThis->aData[i].pUserData->i_release(); pThis->aData[i].pUserData = NULL; } if (pThis->aHandles[i]) { CloseHandle(pThis->aHandles[i]); pThis->aHandles[i] = NULL; } } if (pThis->aHandles[0]) { CloseHandle(pThis->aHandles[0]); pThis->aHandles[0] = NULL; } i = pThis->cTodos; pThis->cTodos = 0; while (i-- > 0) { if (pThis->aTodos[i].Data.pUserData) { pThis->aTodos[i].Data.pUserData->i_release(); pThis->aTodos[i].Data.pUserData = NULL; } if (pThis->aTodos[i].hProcess) { CloseHandle(pThis->aTodos[i].hProcess); pThis->aTodos[i].hProcess = NULL; } } if (ASMAtomicDecU32(&pThis->cRefs) == 0) RTMemFree(pThis); return VINF_SUCCESS; }