VirtualBox

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

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