HostDnsServiceWin.cpp revision 75db06bb2439280f956fee47d8a499d4169d4872
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * Host DNS listener for Windows.
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * Copyright (C) 2014 Oracle Corporation
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * available from http://www.virtualbox.org. This file is free software;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * you can redistribute it and/or modify it under the terms of the GNU
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * General Public License (GPL) as published by the Free Software
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync/* In order to monitor DNS setting updates we need to receive notification about
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\* keys changes.
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * Since it is not possible to use patterns when subscribing key changes, we need to find valid paths for all such
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * keys manually and subscribe to changes one by one (see enumerateSubTree()). */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsyncconst TCHAR HostDnsServiceWin::m_pwcKeyRoot[] = _T("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces");
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Add monitor destroy event.
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * This event should have index '0' at the events array in order to separate it from
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * DNS and tree change events. When this event occurs all resources should be released. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Add registry tree change event and corresponding key.
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * This event should have index '1' at the events array in order to separate it from DNS events.
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * When this event occurs it means there are changes in the list of available network interfaces.
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * Network interfaces should be re-enumerated, all DNS events and keys data should re-initialized. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (subscribeTo(const_cast<TCHAR *>(m_pwcKeyRoot), NULL, REG_NOTIFY_CHANGE_NAME))
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Enumerate all available network interfaces, create events and corresponding keys and perform subscription. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("WARNING: cannot set up monitor properly (3); monitor now disabled.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Too bad we can't even subscribe to notifications about network interfaces changes. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("WARNING: cannot set up monitor properly (2); monitor now disabled.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Too bad we can't even subscribe to destroy event. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("WARNING: cannot set up monitor properly (1); monitor now disabled.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsyncbool HostDnsServiceWin::releaseWarehouseItem(int idxItem)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync bool rc = true;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* We do not check if idxItem is in valid range of m_aWarehouse here
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * (a bit of performance optimization), so make sure you provided a valid value! */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Non-zero return code means ResetEvent() succeeded. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (!rc) LogRel(("Failed to reset event (idxItem=%d); monitor unstable (rc=%d).\n", idxItem, GetLastError()));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync Log2(("Unsubscribed from %ls notifications\n", oTmpItem.wcsInterface));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync m_aWarehouse.erase(m_aWarehouse.begin() + idxItem);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync bool rc = true;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Any sub-tree events we subscribed? */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (m_aWarehouse.size() > VBOX_OFFSET_SUBTREE_EVENTS)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Going from the end to the beginning. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync for (int idxItem = (int)m_aWarehouse.size() - 1; idxItem >= VBOX_OFFSET_SUBTREE_EVENTS; idxItem--)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("DNS monitor unstable; %d events left after dropping.\n", (int)cElementsLeft - VBOX_OFFSET_SUBTREE_EVENTS));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync return false;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* First, drop notifications subscription for sub-tree keys. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Then release notification about tree structure changes. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Release shutdown event. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (m_aWarehouse.size() > VBOX_OFFSET_SHUTDOWN_EVENT)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsyncbool HostDnsServiceWin::subscribeTo(TCHAR *wcsPath, TCHAR *wcsInterface, DWORD fFilter)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Do not add more than MAXIMUM_WAIT_OBJECTS items to the array due to WaitForMultipleObjects() limitation. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if ((m_aWarehouse.size() + 1 /* the array size if we would add an extra item */ ) > MAXIMUM_WAIT_OBJECTS)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync return false;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync return false;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* wcsPath might not be specified if we want to subscribe to the termination event. In this case
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * it is assumed that this is the first issued subscription request (i.e., m_aWarehouse.size() == 0). */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Open registry key itself. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, wcsPath, 0, KEY_READ | KEY_NOTIFY, &hTmpKey);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Link registry key and notification event. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc = RegNotifyChangeKeyValue(hTmpKey, TRUE, fFilter, hTmpEvent, TRUE);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Don't leak! */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("Unable to register key notification (rc=0x%X).\n", rc));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* All good so far? */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("WARNING: unable to set up %ls registry key notifications.\n", wcsPath));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync return false;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("Subscription to termination event already established.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync return false;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Finally, construct array item and queue it. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync struct Item oTmpItem = { hTmpKey, hTmpEvent, NULL };
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Sub-tree keys should provide interface name (UUID). This is needed in order to
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * collect all useful network settings to HostDnsInformation storage object to provide it to parent class. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync Log2(("Subscription to %ls established.\n", wcsPath));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync return true;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Enumerate all the available interfaces. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, m_pwcKeyRoot, 0, KEY_READ, &hTmpKey);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Get info about amount of available network interfaces. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc = RegQueryInfoKey(hTmpKey, NULL, NULL, NULL, &cSubKeys, &cbSubKeyNameMax, NULL, NULL, NULL, NULL, NULL, NULL);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Now iterate over interfaces if:
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * 1) there are interfaces available and
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * 2) maximum length of an interface name conforms to our buffer allocation size. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (cSubKeys > 0 && cbSubKeyNameMax <= VBOX_KEY_NAME_LEN_MAX)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync for (DWORD idxSubKey = 0; idxSubKey < cSubKeys; idxSubKey++)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc = RegEnumKey(hTmpKey, idxSubKey, sSubKeyName, VBOX_KEY_NAME_LEN_MAX);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Since we already know interface name (actually UUID), construct full registry path here. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc = _tcscat_s(sSubKeyFullPath, VBOX_KEY_NAME_LEN_MAX, _T("\\"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc |= _tcscat_s(sSubKeyFullPath, VBOX_KEY_NAME_LEN_MAX, sSubKeyName);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync subscribeTo(sSubKeyFullPath, sSubKeyName, REG_NOTIFY_CHANGE_LAST_SET);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync return true;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync return false;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync AssertReturnVoid(m_aWarehouse.size() > VBOX_OFFSET_SHUTDOWN_EVENT);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync SetEvent(m_aWarehouse[VBOX_OFFSET_SHUTDOWN_EVENT].hEvent);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsyncvoid HostDnsServiceWin::extendVectorWithStrings(std::vector<std::wstring>& pVectorToExtend, std::wstring &wcsParameter)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync while (std::getline(wcsStream, wcsSubString, _T(' ')))
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsyncstatic void hostDnsWinDumpList(std::vector<std::wstring>& awcszValues)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync for (int idxItem = 0; idxItem < awcszValues.size(); idxItem++)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync#endif /* DEBUG */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsyncHRESULT HostDnsServiceWin::updateInfo(uint8_t *fWhatsChanged)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Any interfaces available? */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (m_aWarehouse.size() > VBOX_OFFSET_SUBTREE_EVENTS)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Walk across all the available interfaces and collect network configuration data:
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync * domain name, name servers and search list. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync for (int idxKey = VBOX_OFFSET_SUBTREE_EVENTS; idxKey < m_aWarehouse.size(); idxKey++)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Get number of key values. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc = RegQueryInfoKey(m_aWarehouse[idxKey].hKey, NULL, NULL, NULL, NULL, NULL, NULL, &cValues, NULL, NULL, NULL, NULL);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync for (DWORD idxValue = 0; idxValue < cValues; idxValue++)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Walk across all the properties of given interface. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync rc = RegEnumValue(m_aWarehouse[idxKey].hKey, idxValue, wcsValueName, &cbValueName, 0, NULL, (LPBYTE)wcsData, &cbData);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* We rely on that fact that Windows host cannot be a member of more than one domain in the same time! */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync else if (( _tcscmp(wcsValueName, _T("NameServer")) == 0
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync || _tcscmp(wcsValueName, _T("DhcpNameServer")) == 0)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync extendVectorWithStrings(pHostDnsInfo.servers, std::wstring(wcsData));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync else if (_tcscmp(wcsValueName, _T("SearchList")) == 0
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync extendVectorWithStrings(pHostDnsInfo.searchList, std::wstring(wcsData));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Compare cached network settings and newly obtained ones. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (pHostDnsInfo.servers != m_hostInfoCache.servers)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync#endif /* DEBUG */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync m_hostInfoCache.domain.empty() ? _T("NONE") : (const unsigned short *)m_hostInfoCache.domain.data(),
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync#endif /* DEBUG */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (pHostDnsInfo.searchList != m_hostInfoCache.searchList)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync#endif /* DEBUG */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Provide info about changes if requested. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Update host network configuration cache. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync m_hostInfoCache.domain.assign(pHostDnsInfo.domain);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync m_hostInfoCache.searchList = pHostDnsInfo.searchList;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsyncvoid HostDnsServiceWin::getEventHandles(HANDLE *ahEvents)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync for (int idxHandle = 0; idxHandle < m_aWarehouse.size(); idxHandle++)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync ahEvents[idxHandle] = m_aWarehouse[idxHandle].hEvent;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("Host DNS monitor was not initialized properly.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync while (true)
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Each new iteration we need to update event handles list we monitor. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync DWORD rc = WaitForMultipleObjects((DWORD)m_aWarehouse.size(), ahEvents, FALSE, INFINITE);
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync ("WaitForMultipleObjects failed (%d) to wait! Please debug",
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Shutdown requested. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (rc == (WAIT_OBJECT_0 + VBOX_OFFSET_SHUTDOWN_EVENT)) break;
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Interfaces amount changed. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync else if (rc == (WAIT_OBJECT_0 + VBOX_OFFSET_TREE_EVENT))
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Drop interface events. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Drop event which is corresponds to interfaces tree changes. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Restart interface tree monitoring. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync if (subscribeTo(const_cast<TCHAR *>(m_pwcKeyRoot), NULL, REG_NOTIFY_CHANGE_NAME))
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Restart interface events. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("Monitor unstable: failed to subscribe network configuration changes.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("Monitor unstable: failed to subscribe interface changes.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("Monitor unstable: failed to unsubscribe from interfaces amount changes.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("Monitor unstable: failed to unsubscribe from previous notifications.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* If something went wrong, we break monitoring. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* DNS update events range. */
75db06bb2439280f956fee47d8a499d4169d4872vboxsync else if (rc >= (WAIT_OBJECT_0 + VBOX_OFFSET_SUBTREE_EVENTS) &&
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync Log2(("Network setting has changed at interface %ls.\n", m_aWarehouse[rc - WAIT_OBJECT_0].wcsInterface));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Drop previous notifications first. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* Re-subscribe. */
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("WARNING: Monitor unstable: unable to re-subscribe to notifications.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync LogRel(("WARNING: Monitor unstable: failed to unsubscribe from previous notifications.\n"));
63fdb92b88f31446f6ea53241c0d127677c71c7evboxsync /* If something went wrong, we stop monitoring. */
75db06bb2439280f956fee47d8a499d4169d4872vboxsync AssertMsgFailedReturn(("WaitForMultipleObjects returns out of bound (%d) index %d. Please debug!\n", m_aWarehouse.size(), rc), VERR_INTERNAL_ERROR);