VirtualBox

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

Last change on this file since 82411 was 78065, checked in by vboxsync, 6 years ago

Main/HostDnsService: Pass fResume = FALSE to SetWaitableTimer() so
that DNS change triggered by suspend doesn't cancel that suspend.
ticketref:18549

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: HostDnsServiceWin.cpp 78065 2019-04-09 14:25:04Z vboxsync $ */
2/** @file
3 * Host DNS listener for Windows.
4 */
5
6/*
7 * Copyright (C) 2014-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. 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/*
19 * XXX: need <winsock2.h> to reveal IP_ADAPTER_ADDRESSES in
20 * <iptypes.h> and it must be included before <windows.h>, which is
21 * pulled in by IPRT headers.
22 */
23#include <iprt/win/winsock2.h>
24
25#include "../HostDnsService.h"
26
27#include <VBox/com/string.h>
28#include <VBox/com/ptr.h>
29
30#include <iprt/assert.h>
31#include <iprt/errcore.h>
32#include <VBox/log.h>
33
34#include <iprt/win/windows.h>
35#include <windns.h>
36#include <iptypes.h>
37#include <iprt/win/iphlpapi.h>
38
39#include <algorithm>
40#include <sstream>
41#include <string>
42#include <vector>
43
44static inline int registerNotification(const HKEY& hKey, HANDLE& hEvent);
45static void appendTokenizedStrings(std::vector<std::string> &vecStrings, const std::string &strToAppend, char chDelim = ' ');
46
47struct HostDnsServiceWin::Data
48{
49 HKEY hKeyTcpipParameters;
50 bool fTimerArmed;
51
52#define DATA_SHUTDOWN_EVENT 0
53#define DATA_DNS_UPDATE_EVENT 1
54#define DATA_TIMER 2
55#define DATA_MAX_EVENT 3
56 HANDLE haDataEvent[DATA_MAX_EVENT];
57
58 Data()
59 {
60 hKeyTcpipParameters = NULL;
61 fTimerArmed = false;
62
63 for (size_t i = 0; i < DATA_MAX_EVENT; ++i)
64 haDataEvent[i] = NULL;
65 }
66
67 ~Data()
68 {
69 if (hKeyTcpipParameters != NULL)
70 RegCloseKey(hKeyTcpipParameters);
71
72 for (size_t i = 0; i < DATA_MAX_EVENT; ++i)
73 if (haDataEvent[i] != NULL)
74 CloseHandle(haDataEvent[i]);
75 }
76};
77
78
79HostDnsServiceWin::HostDnsServiceWin()
80 : HostDnsServiceBase(true)
81{
82 m = new Data();
83}
84
85HostDnsServiceWin::~HostDnsServiceWin()
86{
87 if (m != NULL)
88 delete m;
89}
90
91HRESULT HostDnsServiceWin::init(HostDnsMonitorProxy *pProxy)
92{
93 if (m == NULL)
94 return E_FAIL;
95
96 bool fRc = true;
97 LONG lRc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
98 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
99 0,
100 KEY_READ|KEY_NOTIFY,
101 &m->hKeyTcpipParameters);
102 if (lRc != ERROR_SUCCESS)
103 {
104 LogRel(("HostDnsServiceWin: failed to open key Tcpip\\Parameters (error %d)\n", lRc));
105 fRc = false;
106 }
107 else
108 {
109 for (size_t i = 0; i < DATA_MAX_EVENT; ++i)
110 {
111 HANDLE h;
112
113 if (i == DATA_TIMER)
114 h = CreateWaitableTimer(NULL, FALSE, NULL);
115 else
116 h = CreateEvent(NULL, TRUE, FALSE, NULL);
117
118 if (h == NULL)
119 {
120 LogRel(("HostDnsServiceWin: failed to create event (error %d)\n", GetLastError()));
121 fRc = false;
122 break;
123 }
124
125 m->haDataEvent[i] = h;
126 }
127 }
128
129 if (!fRc)
130 return E_FAIL;
131
132 HRESULT hrc = HostDnsServiceBase::init(pProxy);
133 if (FAILED(hrc))
134 return hrc;
135
136 return updateInfo();
137}
138
139void HostDnsServiceWin::uninit(void)
140{
141 HostDnsServiceBase::uninit();
142}
143
144int HostDnsServiceWin::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs)
145{
146 RT_NOREF(uTimeoutMs);
147
148 AssertPtr(m);
149 SetEvent(m->haDataEvent[DATA_SHUTDOWN_EVENT]);
150 /** @todo r=andy Wait for thread? Check rc here. Timeouts? */
151
152 return VINF_SUCCESS;
153}
154
155int HostDnsServiceWin::monitorThreadProc(void)
156{
157 Assert(m != NULL);
158
159 registerNotification(m->hKeyTcpipParameters,
160 m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
161
162 onMonitorThreadInitDone();
163
164 for (;;)
165 {
166 DWORD dwReady;
167
168 dwReady = WaitForMultipleObjects(DATA_MAX_EVENT, m->haDataEvent,
169 FALSE, INFINITE);
170
171 if (dwReady == WAIT_OBJECT_0 + DATA_SHUTDOWN_EVENT)
172 break;
173
174 if (dwReady == WAIT_OBJECT_0 + DATA_DNS_UPDATE_EVENT)
175 {
176 /*
177 * Registry updates for multiple values are not atomic, so
178 * wait a bit to avoid racing and reading partial update.
179 */
180 if (!m->fTimerArmed)
181 {
182 LARGE_INTEGER delay; /* in 100ns units */
183 delay.QuadPart = -2 * 1000 * 1000 * 10LL; /* relative: 2s */
184
185 BOOL ok = SetWaitableTimer(m->haDataEvent[DATA_TIMER], &delay,
186 0, NULL, NULL, FALSE);
187 if (ok)
188 {
189 m->fTimerArmed = true;
190 }
191 else
192 {
193 LogRel(("HostDnsServiceWin: failed to arm timer (error %d)\n", GetLastError()));
194 updateInfo();
195 }
196 }
197
198 ResetEvent(m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
199 registerNotification(m->hKeyTcpipParameters,
200 m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
201 }
202 else if (dwReady == WAIT_OBJECT_0 + DATA_TIMER)
203 {
204 m->fTimerArmed = false;
205 updateInfo();
206 }
207 else if (dwReady == WAIT_FAILED)
208 {
209 LogRel(("HostDnsServiceWin: WaitForMultipleObjects failed: error %d\n", GetLastError()));
210 return VERR_INTERNAL_ERROR;
211 }
212 else
213 {
214 LogRel(("HostDnsServiceWin: WaitForMultipleObjects unexpected return value %d\n", dwReady));
215 return VERR_INTERNAL_ERROR;
216 }
217 }
218
219 return VINF_SUCCESS;
220}
221
222HRESULT HostDnsServiceWin::updateInfo(void)
223{
224 HostDnsInformation info;
225
226 LONG lrc;
227 int rc;
228
229 std::string strDomain;
230 std::string strSearchList; /* NB: comma separated, no spaces */
231
232 /*
233 * We ignore "DhcpDomain" key here since it's not stable. If
234 * there are two active interfaces that use DHCP (in particular
235 * when host uses OpenVPN) then DHCP ACKs will take turns updating
236 * that key. Instead we call GetAdaptersAddresses() below (which
237 * is what ipconfig.exe seems to do).
238 */
239 for (DWORD regIndex = 0; /**/; ++regIndex) {
240 char keyName[256];
241 DWORD cbKeyName = sizeof(keyName);
242 DWORD keyType = 0;
243 char keyData[1024];
244 DWORD cbKeyData = sizeof(keyData);
245
246 lrc = RegEnumValueA(m->hKeyTcpipParameters, regIndex,
247 keyName, &cbKeyName, 0,
248 &keyType, (LPBYTE)keyData, &cbKeyData);
249
250 if (lrc == ERROR_NO_MORE_ITEMS)
251 break;
252
253 if (lrc == ERROR_MORE_DATA) /* buffer too small; handle? */
254 continue;
255
256 if (lrc != ERROR_SUCCESS)
257 {
258 LogRel2(("HostDnsServiceWin: RegEnumValue error %d\n", (int)lrc));
259 return E_FAIL;
260 }
261
262 if (keyType != REG_SZ)
263 continue;
264
265 if (cbKeyData > 0 && keyData[cbKeyData - 1] == '\0')
266 --cbKeyData; /* don't count trailing NUL if present */
267
268 if (RTStrICmp("Domain", keyName) == 0)
269 {
270 strDomain.assign(keyData, cbKeyData);
271 LogRel2(("HostDnsServiceWin: Domain=\"%s\"\n", strDomain.c_str()));
272 }
273 else if (RTStrICmp("DhcpDomain", keyName) == 0)
274 {
275 std::string strDhcpDomain(keyData, cbKeyData);
276 LogRel2(("HostDnsServiceWin: DhcpDomain=\"%s\"\n", strDhcpDomain.c_str()));
277 }
278 else if (RTStrICmp("SearchList", keyName) == 0)
279 {
280 strSearchList.assign(keyData, cbKeyData);
281 LogRel2(("HostDnsServiceWin: SearchList=\"%s\"\n", strSearchList.c_str()));
282 }
283 }
284
285 /* statically configured domain name */
286 if (!strDomain.empty())
287 {
288 info.domain = strDomain;
289 info.searchList.push_back(strDomain);
290 }
291
292 /* statically configured search list */
293 if (!strSearchList.empty())
294 appendTokenizedStrings(info.searchList, strSearchList, ',');
295
296 /*
297 * When name servers are configured statically it seems that the
298 * value of Tcpip\Parameters\NameServer is NOT set, inly interface
299 * specific NameServer value is (which triggers notification for
300 * us to pick up the change). Fortunately, DnsApi seems to do the
301 * right thing there.
302 */
303 DNS_STATUS status;
304 PIP4_ARRAY pIp4Array = NULL;
305
306 // NB: must be set on input it seems, despite docs' claim to the contrary.
307 DWORD cbBuffer = sizeof(&pIp4Array);
308
309 status = DnsQueryConfig(DnsConfigDnsServerList,
310 DNS_CONFIG_FLAG_ALLOC, NULL, NULL,
311 &pIp4Array, &cbBuffer);
312
313 if (status == NO_ERROR && pIp4Array != NULL)
314 {
315 for (DWORD i = 0; i < pIp4Array->AddrCount; ++i)
316 {
317 char szAddrStr[16] = "";
318 RTStrPrintf(szAddrStr, sizeof(szAddrStr), "%RTnaipv4", pIp4Array->AddrArray[i]);
319
320 LogRel2(("HostDnsServiceWin: server %d: %s\n", i+1, szAddrStr));
321 info.servers.push_back(szAddrStr);
322 }
323
324 LocalFree(pIp4Array);
325 }
326
327
328 /**
329 * DnsQueryConfig(DnsConfigSearchList, ...) is not implemented.
330 * Call GetAdaptersAddresses() that orders the returned list
331 * appropriately and collect IP_ADAPTER_ADDRESSES::DnsSuffix.
332 */
333 do {
334 PIP_ADAPTER_ADDRESSES pAddrBuf = NULL;
335 ULONG cbAddrBuf = 8 * 1024;
336 bool fReallocated = false;
337 ULONG err;
338
339 pAddrBuf = (PIP_ADAPTER_ADDRESSES) malloc(cbAddrBuf);
340 if (pAddrBuf == NULL)
341 {
342 LogRel2(("HostDnsServiceWin: failed to allocate %zu bytes"
343 " of GetAdaptersAddresses buffer\n",
344 (size_t)cbAddrBuf));
345 break;
346 }
347
348 while (pAddrBuf != NULL)
349 {
350 ULONG cbAddrBufProvided = cbAddrBuf;
351
352 err = GetAdaptersAddresses(AF_UNSPEC,
353 GAA_FLAG_SKIP_ANYCAST
354 | GAA_FLAG_SKIP_MULTICAST,
355 NULL,
356 pAddrBuf, &cbAddrBuf);
357 if (err == NO_ERROR)
358 {
359 break;
360 }
361 else if (err == ERROR_BUFFER_OVERFLOW)
362 {
363 LogRel2(("HostDnsServiceWin: provided GetAdaptersAddresses with %zu"
364 " but asked again for %zu bytes\n",
365 (size_t)cbAddrBufProvided, (size_t)cbAddrBuf));
366
367 if (RT_UNLIKELY(fReallocated)) /* what? again?! */
368 {
369 LogRel2(("HostDnsServiceWin: ... not going to realloc again\n"));
370 free(pAddrBuf);
371 pAddrBuf = NULL;
372 break;
373 }
374
375 PIP_ADAPTER_ADDRESSES pNewBuf = (PIP_ADAPTER_ADDRESSES) realloc(pAddrBuf, cbAddrBuf);
376 if (pNewBuf == NULL)
377 {
378 LogRel2(("HostDnsServiceWin: failed to reallocate %zu bytes\n", (size_t)cbAddrBuf));
379 free(pAddrBuf);
380 pAddrBuf = NULL;
381 break;
382 }
383
384 /* try again */
385 pAddrBuf = pNewBuf; /* cbAddrBuf already updated */
386 fReallocated = true;
387 }
388 else
389 {
390 LogRel2(("HostDnsServiceWin: GetAdaptersAddresses error %d\n", err));
391 free(pAddrBuf);
392 pAddrBuf = NULL;
393 break;
394 }
395 }
396
397 if (pAddrBuf == NULL)
398 break;
399
400 for (PIP_ADAPTER_ADDRESSES pAdp = pAddrBuf; pAdp != NULL; pAdp = pAdp->Next)
401 {
402 LogRel2(("HostDnsServiceWin: %ls (status %u) ...\n",
403 pAdp->FriendlyName ? pAdp->FriendlyName : L"(null)",
404 pAdp->OperStatus));
405
406 if (pAdp->OperStatus != IfOperStatusUp)
407 continue;
408
409 if (pAdp->DnsSuffix == NULL || *pAdp->DnsSuffix == L'\0')
410 continue;
411
412 char *pszDnsSuffix = NULL;
413 rc = RTUtf16ToUtf8Ex(pAdp->DnsSuffix, RTSTR_MAX,
414 &pszDnsSuffix, 0, /* allocate */
415 NULL);
416 if (RT_FAILURE(rc))
417 {
418 LogRel2(("HostDnsServiceWin: failed to convert DNS suffix \"%ls\": %Rrc\n",
419 pAdp->DnsSuffix, rc));
420 continue;
421 }
422
423 AssertContinue(pszDnsSuffix != NULL);
424 AssertContinue(*pszDnsSuffix != '\0');
425 LogRel2(("HostDnsServiceWin: ... suffix = \"%s\"\n", pszDnsSuffix));
426
427 appendTokenizedStrings(info.searchList, pszDnsSuffix);
428 RTStrFree(pszDnsSuffix);
429 }
430
431 free(pAddrBuf);
432 } while (0);
433
434
435 if (info.domain.empty() && !info.searchList.empty())
436 info.domain = info.searchList[0];
437
438 if (info.searchList.size() == 1)
439 info.searchList.clear();
440
441 HostDnsServiceBase::setInfo(info);
442
443 return S_OK;
444}
445
446static inline int registerNotification(const HKEY& hKey, HANDLE& hEvent)
447{
448 LONG lrc = RegNotifyChangeKeyValue(hKey,
449 TRUE,
450 REG_NOTIFY_CHANGE_LAST_SET,
451 hEvent,
452 TRUE);
453 AssertMsgReturn(lrc == ERROR_SUCCESS,
454 ("Failed to register event on the key. Please debug me!"),
455 VERR_INTERNAL_ERROR);
456
457 return VINF_SUCCESS;
458}
459
460static void appendTokenizedStrings(std::vector<std::string> &vecStrings, const std::string &strToAppend, char chDelim /* = ' ' */)
461{
462 if (strToAppend.empty())
463 return;
464
465 std::istringstream stream(strToAppend);
466 std::string substr;
467
468 while (std::getline(stream, substr, chDelim))
469 {
470 if (substr.empty())
471 continue;
472
473 if (std::find(vecStrings.cbegin(), vecStrings.cend(), substr) != vecStrings.cend())
474 continue;
475
476 vecStrings.push_back(substr);
477 }
478}
479
Note: See TracBrowser for help on using the repository browser.

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