VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp@ 52897

Last change on this file since 52897 was 52897, checked in by vboxsync, 11 years ago

Main: DnsMonitorService: step on unicode rails: store network configuration in unicode-compatible containner (std::wstring) (preliminary tested on Linux, Mac and Windows); DnsMonitorService for Windows: reworked in order to subscribe to correct events and filter-out those ones we don't interested in. (Experimantal, needs more testing and review).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1/* $Id: HostDnsServiceWin.cpp 52897 2014-09-30 14:45:00Z vboxsync $ */
2/** @file
3 * Host DNS listener for Windows.
4 */
5
6/*
7 * Copyright (C) 2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <VBox/com/string.h>
19#include <VBox/com/ptr.h>
20#include <VBox/log.h>
21
22#include <iprt/assert.h>
23#include <iprt/err.h>
24
25#include <Windows.h>
26
27#include <string>
28#include <sstream>
29#include <vector>
30#include "../HostDnsService.h"
31
32/* In order to monitor DNS setting updates we need to receive notification about
33 * Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\* keys changes.
34 * Since it is not possible to use patterns when subscribing key changes, we need to find valid paths for all such
35 * keys manually and subscribe to changes one by one (see enumerateSubTree()). */
36const TCHAR HostDnsServiceWin::m_pwcKeyRoot[] = _T("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces");
37
38
39HostDnsServiceWin::HostDnsServiceWin()
40 : HostDnsMonitor(true),
41 m_aWarehouse(),
42 m_hostInfoCache(),
43 m_fInitialized(false)
44{
45 /* Add monitor destroy event.
46 * This event should have index '0' at the events array in order to separate it from
47 * DNS and tree change events. When this event occurs all resources should be released. */
48 if (subscribeTo(NULL, NULL, 0))
49 {
50 /* Add registry tree change event and corresponding key.
51 * This event should have index '1' at the events array in order to separate it from DNS events.
52 * When this event occurs it means there are changes in the list of available network interfaces.
53 * Network interfaces should be re-enumerated, all DNS events and keys data should re-initialized. */
54 if (subscribeTo(const_cast<TCHAR *>(m_pwcKeyRoot), NULL, REG_NOTIFY_CHANGE_NAME))
55 {
56 /* Enumerate all available network interfaces, create events and corresponding keys and perform subscription. */
57 if (enumerateSubTree())
58 {
59 updateInfo(NULL);
60 m_fInitialized = true;
61 return;
62 }
63 else
64 LogRel(("WARNING: cannot set up monitor properly (3); monitor now disabled.\n"));
65 }
66 else
67 /* Too bad we can't even subscribe to notifications about network interfaces changes. */
68 LogRel(("WARNING: cannot set up monitor properly (2); monitor now disabled.\n"));
69 }
70 else
71 /* Too bad we can't even subscribe to destroy event. */
72 LogRel(("WARNING: cannot set up monitor properly (1); monitor now disabled.\n"));
73
74 releaseResources();
75}
76
77
78HostDnsServiceWin::~HostDnsServiceWin()
79{
80 monitorThreadShutdown();
81 releaseResources();
82 m_fInitialized = false;
83}
84
85
86bool HostDnsServiceWin::releaseWarehouseItem(int idxItem)
87{
88 bool rc = true;
89 /* We do not check if idxItem is in valid range of m_aWarehouse here
90 * (a bit of performance optimization), so make sure you provided a valid value! */
91 struct Item oTmpItem = m_aWarehouse[idxItem];
92
93 /* Non-zero return code means ResetEvent() succeeded. */
94 rc = ResetEvent(oTmpItem.hEvent) != 0;
95 if (!rc) LogRel(("Failed to reset event (idxItem=%d); monitor unstable (rc=%d).\n", idxItem, GetLastError()));
96 CloseHandle(oTmpItem.hEvent);
97 oTmpItem.hEvent = NULL;
98
99 RegCloseKey(oTmpItem.hKey);
100 oTmpItem.hKey = NULL;
101
102 Log2(("Unsubscribed from %ls notifications\n", oTmpItem.wcsInterface));
103
104 m_aWarehouse.erase(m_aWarehouse.begin() + idxItem);
105
106 return rc;
107}
108
109
110bool HostDnsServiceWin::dropSubTreeNotifications()
111{
112 bool rc = true;
113 /* Any sub-tree events we subscribed? */
114 if (m_aWarehouse.size() > VBOX_OFFSET_SUBTREE_EVENTS)
115 /* Going from the end to the beginning. */
116 for (int idxItem = (int)m_aWarehouse.size() - 1; idxItem >= VBOX_OFFSET_SUBTREE_EVENTS; idxItem--)
117 rc &= releaseWarehouseItem(idxItem);
118
119 size_t cElementsLeft = m_aWarehouse.size();
120 if (cElementsLeft != VBOX_OFFSET_SUBTREE_EVENTS)
121 {
122 LogRel(("DNS monitor unstable; %d events left after dropping.\n", (int)cElementsLeft - VBOX_OFFSET_SUBTREE_EVENTS));
123 return false;
124 }
125
126 return rc;
127}
128
129
130void HostDnsServiceWin::releaseResources()
131{
132 /* First, drop notifications subscription for sub-tree keys. */
133 dropSubTreeNotifications();
134
135 /* Then release notification about tree structure changes. */
136 if (m_aWarehouse.size() > VBOX_OFFSET_TREE_EVENT)
137 releaseWarehouseItem(VBOX_OFFSET_TREE_EVENT);
138
139 /* Release shutdown event. */
140 if (m_aWarehouse.size() > VBOX_OFFSET_SHUTDOWN_EVENT)
141 releaseWarehouseItem(VBOX_OFFSET_SHUTDOWN_EVENT);
142
143 AssertReturnVoid(m_aWarehouse.size() == 0);
144}
145
146
147bool HostDnsServiceWin::subscribeTo(TCHAR *wcsPath, TCHAR *wcsInterface, DWORD fFilter)
148{
149 HKEY hTmpKey = NULL;
150 HANDLE hTmpEvent = NULL;
151
152 /* Do not add more than MAXIMUM_WAIT_OBJECTS items to the array due to WaitForMultipleObjects() limitation. */
153 if ((m_aWarehouse.size() + 1 /* the array size if we would add an extra item */ ) > MAXIMUM_WAIT_OBJECTS)
154 {
155 LogRel(("Too many items to monitor.\n"));
156 return false;
157 }
158
159 hTmpEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
160 if (!hTmpEvent)
161 return false;
162
163 /* wcsPath might not be specified if we want to subscribe to the termination event. In this case
164 * it is assumed that this is the first issued subscription request (i.e., m_aWarehouse.size() == 0). */
165 if (wcsPath)
166 {
167 LONG rc;
168 /* Open registry key itself. */
169 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, wcsPath, 0, KEY_READ | KEY_NOTIFY, &hTmpKey);
170 if (rc == ERROR_SUCCESS)
171 {
172 /* Link registry key and notification event. */
173 rc = RegNotifyChangeKeyValue(hTmpKey, TRUE, fFilter, hTmpEvent, TRUE);
174 if (rc != ERROR_SUCCESS)
175 {
176 /* Don't leak! */
177 RegCloseKey(hTmpKey);
178 LogRel(("Unable to register key notification (rc=0x%X).\n", rc));
179 }
180 }
181 else
182 LogRel(("Unable to open key (rc=0x%X)\n", rc));
183
184 /* All good so far? */
185 if (rc != ERROR_SUCCESS)
186 {
187 LogRel(("WARNING: unable to set up %ls registry key notifications.\n", wcsPath));
188 CloseHandle(hTmpEvent);
189 return false;
190 }
191 }
192 else if (m_aWarehouse.size() > 0)
193 {
194 LogRel(("Subscription to termination event already established.\n"));
195 CloseHandle(hTmpEvent);
196 return false;
197 }
198
199 /* Finally, construct array item and queue it. */
200 struct Item oTmpItem = { hTmpKey, hTmpEvent, NULL };
201
202 /* Sub-tree keys should provide interface name (UUID). This is needed in order to
203 * collect all useful network settings to HostDnsInformation storage object to provide it to parent class. */
204 if (wcsInterface)
205 _tcscpy(oTmpItem.wcsInterface, wcsInterface);
206
207 if (wcsPath)
208 Log2(("Subscription to %ls established.\n", wcsPath));
209
210 m_aWarehouse.push_back(oTmpItem);
211
212 return true;
213}
214
215
216bool HostDnsServiceWin::enumerateSubTree()
217{
218 LONG rc = 0;
219 HKEY hTmpKey;
220 DWORD cSubKeys = 0;
221 DWORD cbSubKeyNameMax = 0;
222
223 /* Enumerate all the available interfaces. */
224 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, m_pwcKeyRoot, 0, KEY_READ, &hTmpKey);
225 if (rc == ERROR_SUCCESS)
226 {
227 /* Get info about amount of available network interfaces. */
228 rc = RegQueryInfoKey(hTmpKey, NULL, NULL, NULL, &cSubKeys, &cbSubKeyNameMax, NULL, NULL, NULL, NULL, NULL, NULL);
229 if (rc == ERROR_SUCCESS)
230 {
231 /* Now iterate over interfaces if:
232 * 1) there are interfaces available and
233 * 2) maximum length of an interface name conforms to our buffer allocation size. */
234 if (cSubKeys > 0 && cbSubKeyNameMax <= VBOX_KEY_NAME_LEN_MAX)
235 {
236 TCHAR sSubKeyName[VBOX_KEY_NAME_LEN_MAX];
237 for (DWORD idxSubKey = 0; idxSubKey < cSubKeys; idxSubKey++)
238 {
239 rc = RegEnumKey(hTmpKey, idxSubKey, sSubKeyName, VBOX_KEY_NAME_LEN_MAX);
240 if (rc == ERROR_SUCCESS)
241 {
242 /* Since we already know interface name (actually UUID), construct full registry path here. */
243 TCHAR sSubKeyFullPath[VBOX_KEY_NAME_LEN_MAX];
244 RT_ZERO(sSubKeyFullPath);
245 _tcscpy(sSubKeyFullPath, m_pwcKeyRoot);
246 rc = _tcscat_s(sSubKeyFullPath, VBOX_KEY_NAME_LEN_MAX, _T("\\"));
247 rc |= _tcscat_s(sSubKeyFullPath, VBOX_KEY_NAME_LEN_MAX, sSubKeyName);
248 if (rc == 0)
249 subscribeTo(sSubKeyFullPath, sSubKeyName, REG_NOTIFY_CHANGE_LAST_SET);
250 }
251 else
252 LogRel(("Unable to open interfaces list (1).\n"));
253 }
254 RegCloseKey(hTmpKey);
255 return true;
256 }
257 else
258 LogRel(("Unable to open interfaces list (2).\n"));
259 }
260 else
261 LogRel(("Unable to open interfaces list (3).\n"));
262 RegCloseKey(hTmpKey);
263 }
264 else
265 LogRel(("Unable to open interfaces list (4).\n"));
266 return false;
267}
268
269
270HRESULT HostDnsServiceWin::init()
271{
272 HRESULT hrc = HostDnsMonitor::init();
273 AssertComRCReturn(hrc, hrc);
274
275 return updateInfo(NULL);
276}
277
278
279void HostDnsServiceWin::monitorThreadShutdown()
280{
281 AssertReturnVoid(m_aWarehouse.size() > VBOX_OFFSET_SHUTDOWN_EVENT);
282 SetEvent(m_aWarehouse[VBOX_OFFSET_SHUTDOWN_EVENT].hEvent);
283}
284
285
286void HostDnsServiceWin::extendVectorWithStrings(std::vector<std::wstring>& pVectorToExtend, std::wstring &wcsParameter)
287{
288 std::wstringstream wcsStream(wcsParameter);
289 std::wstring wcsSubString;
290
291 while (std::getline(wcsStream, wcsSubString, _T(' ')))
292 pVectorToExtend.push_back(wcsSubString);
293}
294
295
296#ifdef DEBUG
297static void hostDnsWinDumpList(std::vector<std::wstring>& awcszValues)
298{
299 for (int idxItem = 0; idxItem < awcszValues.size(); idxItem++)
300 {
301 LogRel(("%ls\n", awcszValues[idxItem].data()));
302 }
303}
304#endif /* DEBUG */
305
306
307HRESULT HostDnsServiceWin::updateInfo(uint8_t *fWhatsChanged)
308{
309 HostDnsInformation pHostDnsInfo;
310 RT_ZERO(pHostDnsInfo);
311
312 /* Any interfaces available? */
313 if (m_aWarehouse.size() > VBOX_OFFSET_SUBTREE_EVENTS)
314 {
315 /* Walk across all the available interfaces and collect network configuration data:
316 * domain name, name servers and search list. */
317 for (int idxKey = VBOX_OFFSET_SUBTREE_EVENTS; idxKey < m_aWarehouse.size(); idxKey++)
318 {
319 LONG rc;
320
321 /* Get number of key values. */
322 DWORD cValues = 0;
323 rc = RegQueryInfoKey(m_aWarehouse[idxKey].hKey, NULL, NULL, NULL, NULL, NULL, NULL, &cValues, NULL, NULL, NULL, NULL);
324 if (rc == ERROR_SUCCESS)
325 {
326 for (DWORD idxValue = 0; idxValue < cValues; idxValue++)
327 {
328 TCHAR wcsValueName[VBOX_KEY_NAME_LEN_MAX];
329 DWORD cbValueName = VBOX_KEY_NAME_LEN_MAX;
330 TCHAR wcsData[VBOX_KEY_NAME_LEN_MAX];
331 DWORD cbData = VBOX_KEY_NAME_LEN_MAX;
332
333 /* Walk across all the properties of given interface. */
334 rc = RegEnumValue(m_aWarehouse[idxKey].hKey, idxValue, wcsValueName, &cbValueName, 0, NULL, (LPBYTE)wcsData, &cbData);
335 if (rc == ERROR_SUCCESS)
336 {
337
338 if (( _tcscmp(wcsValueName, _T("Domain")) == 0
339 || _tcscmp(wcsValueName, _T("DhcpDomain")) == 0)
340 && _tcslen(wcsData) > 0)
341 {
342 /* We rely on that fact that Windows host cannot be a member of more than one domain in the same time! */
343 if (pHostDnsInfo.domain.empty())
344 pHostDnsInfo.domain = std::wstring(wcsData);
345 }
346 else if (( _tcscmp(wcsValueName, _T("NameServer")) == 0
347 || _tcscmp(wcsValueName, _T("DhcpNameServer")) == 0)
348 && _tcslen(wcsData) > 0)
349 {
350 extendVectorWithStrings(pHostDnsInfo.servers, std::wstring(wcsData));
351 }
352 else if (_tcscmp(wcsValueName, _T("SearchList")) == 0
353 && _tcslen(wcsData) > 0)
354 {
355 extendVectorWithStrings(pHostDnsInfo.searchList, std::wstring(wcsData));
356 }
357 }
358 }
359 }
360 }
361
362 uint8_t fChanged = VBOX_EVENT_NO_CHANGES;
363 /* Compare cached network settings and newly obtained ones. */
364 if (pHostDnsInfo.servers != m_hostInfoCache.servers)
365 {
366#ifdef DEBUG
367 LogRel(("Servers changed from:\n"));
368 hostDnsWinDumpList(m_hostInfoCache.servers);
369 LogRel(("to:\n"));
370 hostDnsWinDumpList(pHostDnsInfo.servers);
371#endif /* DEBUG */
372 fChanged |= VBOX_EVENT_SERVERS_CHANGED;
373 }
374
375 if (pHostDnsInfo.domain != m_hostInfoCache.domain)
376 {
377#ifdef DEBUG
378 LogRel(("Domain changed: [%ls]->[%ls].\n",
379 m_hostInfoCache.domain.empty() ? _T("NONE") : (const unsigned short *)m_hostInfoCache.domain.data(),
380 pHostDnsInfo.domain.data()));
381#endif /* DEBUG */
382 fChanged |= VBOX_EVENT_DOMAIN_CHANGED;
383 }
384
385 if (pHostDnsInfo.searchList != m_hostInfoCache.searchList)
386 {
387#ifdef DEBUG
388 LogRel(("SearchList changed from:\n"));
389 hostDnsWinDumpList(m_hostInfoCache.searchList);
390 LogRel(("to:\n"));
391 hostDnsWinDumpList(pHostDnsInfo.searchList);
392#endif /* DEBUG */
393 fChanged |= VBOX_EVENT_SEARCHLIST_CHANGED;
394 }
395
396 /* Provide info about changes if requested. */
397 if (fWhatsChanged)
398 *fWhatsChanged = fChanged;
399
400 /* Update host network configuration cache. */
401 m_hostInfoCache.servers.clear();
402 m_hostInfoCache.servers = pHostDnsInfo.servers;
403 m_hostInfoCache.domain.clear();
404 m_hostInfoCache.domain.assign(pHostDnsInfo.domain);
405 m_hostInfoCache.searchList.clear();
406 m_hostInfoCache.searchList = pHostDnsInfo.searchList;
407
408 HostDnsMonitor::setInfo(pHostDnsInfo);
409 }
410
411 return S_OK;
412}
413
414
415void HostDnsServiceWin::getEventHandles(HANDLE *ahEvents)
416{
417 AssertReturnVoid(m_aWarehouse.size() > 0);
418 for (int idxHandle = 0; idxHandle < m_aWarehouse.size(); idxHandle++)
419 ahEvents[idxHandle] = m_aWarehouse[idxHandle].hEvent;
420}
421
422
423int HostDnsServiceWin::monitorWorker()
424{
425 monitorThreadInitializationDone();
426
427 uint8_t fWhatsChabged = VBOX_EVENT_NO_CHANGES;
428
429 if (!m_fInitialized)
430 {
431 LogRel(("Host DNS monitor was not initialized properly.\n"));
432 return VERR_INTERNAL_ERROR;
433 }
434
435 HANDLE ahEvents[MAXIMUM_WAIT_OBJECTS];
436
437 while (true)
438 {
439 /* Each new iteration we need to update event handles list we monitor. */
440 RT_ZERO(ahEvents);
441 getEventHandles(ahEvents);
442
443 DWORD rc = WaitForMultipleObjects((DWORD)m_aWarehouse.size(), ahEvents, FALSE, INFINITE);
444
445 AssertMsgReturn(rc != WAIT_FAILED,
446 ("WaitForMultipleObjects failed (%d) to wait! Please debug",
447 GetLastError()), VERR_INTERNAL_ERROR);
448
449 /* Shutdown requested. */
450 if (rc == (WAIT_OBJECT_0 + VBOX_OFFSET_SHUTDOWN_EVENT)) break;
451 /* Interfaces amount changed. */
452 else if (rc == (WAIT_OBJECT_0 + VBOX_OFFSET_TREE_EVENT))
453 {
454 Log2(("Network interfaces amount changed.\n"));
455
456 /* Drop interface events. */
457 if (dropSubTreeNotifications())
458 {
459 /* Drop event which is corresponds to interfaces tree changes. */
460 if (releaseWarehouseItem(VBOX_OFFSET_TREE_EVENT))
461 {
462 /* Restart interface tree monitoring. */
463 if (subscribeTo(const_cast<TCHAR *>(m_pwcKeyRoot), NULL, REG_NOTIFY_CHANGE_NAME))
464 {
465 /* Restart interface events. */
466 if (enumerateSubTree())
467 {
468 Log2(("Monitor restarted successfully.\n"));
469 fWhatsChabged = VBOX_EVENT_NO_CHANGES;
470 updateInfo(&fWhatsChabged);
471 if (fWhatsChabged & VBOX_EVENT_SERVERS_CHANGED)
472 {
473 LogRel(("Notification sent (1).\n"));
474 notifyAll();
475 }
476 continue;
477 }
478 else
479 LogRel(("Monitor unstable: failed to subscribe network configuration changes.\n"));
480 }
481 else
482 LogRel(("Monitor unstable: failed to subscribe interface changes.\n"));
483 }
484 else
485 LogRel(("Monitor unstable: failed to unsubscribe from interfaces amount changes.\n"));
486 }
487 else
488 LogRel(("Monitor unstable: failed to unsubscribe from previous notifications.\n"));
489
490 /* If something went wrong, we break monitoring. */
491 break;
492
493 }
494 /* DNS update events range. */
495 else if (rc > (WAIT_OBJECT_0 + VBOX_OFFSET_SUBTREE_EVENTS) &&
496 rc < (WAIT_OBJECT_0 + m_aWarehouse.size() - VBOX_OFFSET_SUBTREE_EVENTS))
497 {
498 Log2(("Network setting has changed at interface %ls.\n", m_aWarehouse[rc - WAIT_OBJECT_0].wcsInterface));
499
500 /* Drop previous notifications first. */
501 if (dropSubTreeNotifications())
502 {
503 /* Re-subscribe. */
504 if (enumerateSubTree())
505 {
506 Log2(("Restart monitoring.\n"));
507 fWhatsChabged = VBOX_EVENT_NO_CHANGES;
508 updateInfo(&fWhatsChabged);
509 if (fWhatsChabged & VBOX_EVENT_SERVERS_CHANGED)
510 {
511 LogRel(("Notification sent (2).\n"));
512 notifyAll();
513 }
514 continue;
515 }
516 else
517 LogRel(("WARNING: Monitor unstable: unable to re-subscribe to notifications.\n"));
518 }
519 else
520 LogRel(("WARNING: Monitor unstable: failed to unsubscribe from previous notifications.\n"));
521
522 /* If something went wrong, we stop monitoring. */
523 break;
524 }
525 else
526 AssertMsgFailedReturn(("WaitForMultipleObjects returns out of bound (%d) index %d. Please debug!", m_aWarehouse.size(), rc), VERR_INTERNAL_ERROR);
527 }
528 LogRel(("Monitor thread exited.\n"));
529 return VINF_SUCCESS;
530}
531
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette