VirtualBox

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

Last change on this file since 87578 was 87578, checked in by vboxsync, 4 years ago

Main: Doxygen v1.8.20 seems to get confused by appendTokenizedStrings and dumpHostDnsStrVector prototypes and unable to match them to definitions, so just move the definitions up to the top and drop the prototypes.

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