VirtualBox

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

Last change on this file since 98110 was 98103, checked in by vboxsync, 23 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
Line 
1/* $Id: HostDnsServiceWin.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Host DNS listener for Windows.
4 */
5
6/*
7 * Copyright (C) 2014-2023 Oracle and/or its affiliates.
8 *
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
26 */
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 */
33#include <iprt/win/winsock2.h>
34
35#include "../HostDnsService.h"
36
37#include <VBox/com/string.h>
38#include <VBox/com/ptr.h>
39
40#include <iprt/assert.h>
41#include <iprt/errcore.h>
42#include <VBox/log.h>
43
44#include <iprt/win/windows.h>
45#include <windns.h>
46#include <iptypes.h>
47#include <iprt/win/iphlpapi.h>
48
49#include <algorithm>
50#include <iprt/sanitized/sstream>
51#include <iprt/sanitized/string>
52#include <vector>
53
54
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
69static void appendTokenizedStrings(std::vector<std::string> &vecStrings, const std::string &strToAppend, char chDelim = ' ')
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
90struct HostDnsServiceWin::Data
91{
92 HKEY hKeyTcpipParameters;
93 bool fTimerArmed;
94
95#define DATA_SHUTDOWN_EVENT 0
96#define DATA_DNS_UPDATE_EVENT 1
97#define DATA_TIMER 2
98#define DATA_MAX_EVENT 3
99 HANDLE haDataEvent[DATA_MAX_EVENT];
100
101 Data()
102 {
103 hKeyTcpipParameters = NULL;
104 fTimerArmed = false;
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 }
119};
120
121
122HostDnsServiceWin::HostDnsServiceWin()
123 : HostDnsServiceBase(true)
124{
125 m = new Data();
126}
127
128HostDnsServiceWin::~HostDnsServiceWin()
129{
130 if (m != NULL)
131 delete m;
132}
133
134HRESULT HostDnsServiceWin::init(HostDnsMonitorProxy *pProxy)
135{
136 if (m == NULL)
137 return E_FAIL;
138
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)
146 {
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)
153 {
154 HANDLE h;
155
156 if (i == DATA_TIMER)
157 h = CreateWaitableTimer(NULL, FALSE, NULL);
158 else
159 h = CreateEvent(NULL, TRUE, FALSE, NULL);
160
161 if (h == NULL)
162 {
163 LogRel(("HostDnsServiceWin: failed to create event (error %d)\n", GetLastError()));
164 fRc = false;
165 break;
166 }
167
168 m->haDataEvent[i] = h;
169 }
170 }
171
172 if (!fRc)
173 return E_FAIL;
174
175 HRESULT hrc = HostDnsServiceBase::init(pProxy);
176 if (FAILED(hrc))
177 return hrc;
178
179 return updateInfo();
180}
181
182void HostDnsServiceWin::uninit(void)
183{
184 HostDnsServiceBase::uninit();
185}
186
187int HostDnsServiceWin::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs)
188{
189 RT_NOREF(uTimeoutMs);
190
191 AssertPtr(m);
192 SetEvent(m->haDataEvent[DATA_SHUTDOWN_EVENT]);
193 /** @todo r=andy Wait for thread? Check rc here. Timeouts? */
194
195 return VINF_SUCCESS;
196}
197
198int HostDnsServiceWin::monitorThreadProc(void)
199{
200 Assert(m != NULL);
201
202 registerNotification(m->hKeyTcpipParameters,
203 m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
204
205 onMonitorThreadInitDone();
206
207 for (;;)
208 {
209 DWORD dwReady;
210
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)
218 {
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 */
227
228 BOOL ok = SetWaitableTimer(m->haDataEvent[DATA_TIMER], &delay,
229 0, NULL, NULL, FALSE);
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
241 ResetEvent(m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
242 registerNotification(m->hKeyTcpipParameters,
243 m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
244 }
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 }
255 else
256 {
257 LogRel(("HostDnsServiceWin: WaitForMultipleObjects unexpected return value %d\n", dwReady));
258 return VERR_INTERNAL_ERROR;
259 }
260 }
261
262 return VINF_SUCCESS;
263}
264
265HRESULT HostDnsServiceWin::updateInfo(void)
266{
267 HostDnsInformation info;
268
269 LONG lrc;
270 int rc;
271
272 std::string strDomain;
273 std::string strSearchList; /* NB: comma separated, no spaces */
274
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 */
282 for (DWORD regIndex = 0; /**/; ++regIndex) {
283 char keyName[256];
284 DWORD cbKeyName = sizeof(keyName);
285 DWORD keyType = 0;
286 char keyData[1024];
287 DWORD cbKeyData = sizeof(keyData);
288
289 lrc = RegEnumValueA(m->hKeyTcpipParameters, regIndex,
290 keyName, &cbKeyName, 0,
291 &keyType, (LPBYTE)keyData, &cbKeyData);
292
293 if (lrc == ERROR_NO_MORE_ITEMS)
294 break;
295
296 if (lrc == ERROR_MORE_DATA) /* buffer too small; handle? */
297 continue;
298
299 if (lrc != ERROR_SUCCESS)
300 {
301 LogRel2(("HostDnsServiceWin: RegEnumValue error %d\n", (int)lrc));
302 return E_FAIL;
303 }
304
305 if (keyType != REG_SZ)
306 continue;
307
308 if (cbKeyData > 0 && keyData[cbKeyData - 1] == '\0')
309 --cbKeyData; /* don't count trailing NUL if present */
310
311 if (RTStrICmp("Domain", keyName) == 0)
312 {
313 strDomain.assign(keyData, cbKeyData);
314 LogRel2(("HostDnsServiceWin: Domain=\"%s\"\n", strDomain.c_str()));
315 }
316 else if (RTStrICmp("DhcpDomain", keyName) == 0)
317 {
318 std::string strDhcpDomain(keyData, cbKeyData);
319 LogRel2(("HostDnsServiceWin: DhcpDomain=\"%s\"\n", strDhcpDomain.c_str()));
320 }
321 else if (RTStrICmp("SearchList", keyName) == 0)
322 {
323 strSearchList.assign(keyData, cbKeyData);
324 LogRel2(("HostDnsServiceWin: SearchList=\"%s\"\n", strSearchList.c_str()));
325 }
326 }
327
328 /* statically configured domain name */
329 if (!strDomain.empty())
330 {
331 info.domain = strDomain;
332 info.searchList.push_back(strDomain);
333 }
334
335 /* statically configured search list */
336 if (!strSearchList.empty())
337 appendTokenizedStrings(info.searchList, strSearchList, ',');
338
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;
348
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);
355
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
363 LogRel2(("HostDnsServiceWin: server %d: %s\n", i+1, szAddrStr));
364 info.servers.push_back(szAddrStr);
365 }
366
367 LocalFree(pIp4Array);
368 }
369
370
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;
381
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
470 appendTokenizedStrings(info.searchList, pszDnsSuffix);
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
481 if (info.searchList.size() == 1)
482 info.searchList.clear();
483
484 HostDnsServiceBase::setInfo(info);
485
486 return S_OK;
487}
488
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