VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use