VirtualBox

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

Last change on this file since 93412 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • 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 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * Host DNS listener for Windows.
4 */
5
6/*
7 * Copyright (C) 2014-2022 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.

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