VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/HostDnsService.cpp@ 108010

Last change on this file since 108010 was 108010, checked in by vboxsync, 2 weeks ago

Main/HostDnsService.cpp: Fixed lock order problem with HostDnsMonitorProxy::updateInfo / HostDnsMonitorProxy::pollGlobalExtraData / VirtualBox::getExtraData after r166866. This replaces the IPRT scope-based locking with the main-style locking for flexibility.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1/* $Id: HostDnsService.cpp 108010 2025-01-31 23:49:41Z vboxsync $ */
2/** @file
3 * Base class for Host DNS & Co services.
4 */
5
6/*
7 * Copyright (C) 2013-2024 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#define LOG_GROUP LOG_GROUP_MAIN_HOST
29#include <VBox/com/array.h>
30#include <VBox/com/ptr.h>
31#include <VBox/com/string.h>
32
33#include <iprt/cpp/utils.h>
34
35#include "LoggingNew.h"
36#include "VirtualBoxImpl.h"
37#include <iprt/time.h>
38#include <iprt/thread.h>
39#include <iprt/semaphore.h>
40#include <iprt/critsect.h>
41
42#include <algorithm>
43#include <set>
44#include <iprt/sanitized/string>
45#include "HostDnsService.h"
46
47
48
49static void dumpHostDnsStrVector(const std::string &prefix, const std::vector<std::string> &v)
50{
51 int i = 1;
52 for (std::vector<std::string>::const_iterator it = v.begin();
53 it != v.end();
54 ++it, ++i)
55 LogRel((" %s %d: %s\n", prefix.c_str(), i, it->c_str()));
56 if (v.empty())
57 LogRel((" no %s entries\n", prefix.c_str()));
58}
59
60static void dumpHostDnsInformation(const HostDnsInformation &info)
61{
62 dumpHostDnsStrVector("server", info.servers);
63
64 if (!info.domain.empty())
65 LogRel((" domain: %s\n", info.domain.c_str()));
66 else
67 LogRel((" no domain set\n"));
68
69 dumpHostDnsStrVector("search string", info.searchList);
70}
71
72bool HostDnsInformation::equals(const HostDnsInformation &info, uint32_t fLaxComparison) const
73{
74 bool fSameServers;
75 if ((fLaxComparison & IGNORE_SERVER_ORDER) == 0)
76 fSameServers = (servers == info.servers);
77 else
78 {
79 std::set<std::string> l(servers.begin(), servers.end());
80 std::set<std::string> r(info.servers.begin(), info.servers.end());
81
82 fSameServers = (l == r);
83 }
84
85 bool fSameDomain, fSameSearchList;
86 if ((fLaxComparison & IGNORE_SUFFIXES) == 0)
87 {
88 fSameDomain = (domain == info.domain);
89 fSameSearchList = (searchList == info.searchList);
90 }
91 else
92 fSameDomain = fSameSearchList = true;
93
94 return fSameServers && fSameDomain && fSameSearchList;
95}
96
97DECLINLINE(void) detachVectorOfStrings(const std::vector<std::string>& v, std::vector<com::Utf8Str> &aArray)
98{
99 aArray.resize(v.size());
100 size_t i = 0;
101 for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it, ++i)
102 aArray[i] = Utf8Str(it->c_str()); /** @todo r=bird: *it isn't necessarily UTF-8 clean!!
103 * On darwin we do silly shit like using CFStringGetSystemEncoding()
104 * that may be UTF-8 but doesn't need to be.
105 *
106 * Why on earth are we using std::string here anyway?
107 */
108}
109
110struct HostDnsServiceBase::Data
111{
112 Data(bool aThreaded)
113 : pProxy(NULL)
114 , fThreaded(aThreaded)
115 , hMonitorThreadEvent(NIL_RTSEMEVENT)
116 , hMonitorThread(NIL_RTTHREAD)
117 {}
118
119 /** Weak pointer to parent proxy object. */
120 HostDnsMonitorProxy *pProxy;
121 /** Whether the DNS monitor implementation has a dedicated monitoring thread. Optional. */
122 const bool fThreaded;
123 /** Event for the monitor thread, if any. */
124 RTSEMEVENT hMonitorThreadEvent;
125 /** Handle of the monitor thread, if any. */
126 RTTHREAD hMonitorThread;
127 /** Generic host DNS information. */
128 HostDnsInformation info;
129};
130
131struct HostDnsMonitorProxy::Data
132{
133 Data(HostDnsServiceBase *aMonitor, VirtualBox *aParent)
134 : pVirtualBox(aParent)
135 , pMonitorImpl(aMonitor)
136 , uLastExtraDataPoll(0)
137 , fLaxComparison(0)
138 , info()
139 {}
140
141 VirtualBox *pVirtualBox;
142 HostDnsServiceBase *pMonitorImpl;
143
144 uint64_t uLastExtraDataPoll;
145 uint32_t fLaxComparison;
146 HostDnsInformation info;
147};
148
149
150HostDnsServiceBase::HostDnsServiceBase(bool fThreaded)
151 : m(NULL)
152{
153 m = new HostDnsServiceBase::Data(fThreaded);
154}
155
156HostDnsServiceBase::~HostDnsServiceBase()
157{
158 if (m)
159 {
160 delete m;
161 m = NULL;
162 }
163}
164
165/* static */
166HostDnsServiceBase *HostDnsServiceBase::createHostDnsMonitor(void)
167{
168 HostDnsServiceBase *pMonitor = NULL;
169
170#if defined (RT_OS_DARWIN)
171 pMonitor = new HostDnsServiceDarwin();
172#elif defined(RT_OS_WINDOWS)
173 pMonitor = new HostDnsServiceWin();
174#elif defined(RT_OS_LINUX)
175 pMonitor = new HostDnsServiceLinux();
176#elif defined(RT_OS_SOLARIS)
177 pMonitor = new HostDnsServiceSolaris();
178#elif defined(RT_OS_FREEBSD)
179 pMonitor = new HostDnsServiceFreebsd();
180#elif defined(RT_OS_OS2)
181 pMonitor = new HostDnsServiceOs2();
182#else
183 pMonitor = new HostDnsServiceBase();
184#endif
185
186 return pMonitor;
187}
188
189HRESULT HostDnsServiceBase::init(HostDnsMonitorProxy *pProxy)
190{
191 LogRel(("HostDnsMonitor: initializing\n"));
192
193 AssertPtrReturn(pProxy, E_POINTER);
194 m->pProxy = pProxy;
195
196 if (m->fThreaded)
197 {
198 LogRel2(("HostDnsMonitor: starting thread ...\n"));
199
200 int vrc = RTSemEventCreate(&m->hMonitorThreadEvent);
201 AssertRCReturn(vrc, E_FAIL);
202
203 vrc = RTThreadCreate(&m->hMonitorThread,
204 HostDnsServiceBase::threadMonitorProc,
205 this, 128 * _1K, RTTHREADTYPE_IO,
206 RTTHREADFLAGS_WAITABLE, "dns-monitor");
207 AssertRCReturn(vrc, E_FAIL);
208
209 RTSemEventWait(m->hMonitorThreadEvent, RT_INDEFINITE_WAIT);
210
211 LogRel2(("HostDnsMonitor: thread started\n"));
212 }
213
214 return S_OK;
215}
216
217void HostDnsServiceBase::uninit(void)
218{
219 LogRel(("HostDnsMonitor: shutting down ...\n"));
220
221 if (m->fThreaded)
222 {
223 LogRel2(("HostDnsMonitor: waiting for thread ...\n"));
224
225 const RTMSINTERVAL uTimeoutMs = 30 * 1000; /* 30s */
226
227 monitorThreadShutdown(uTimeoutMs);
228
229 int vrc = RTThreadWait(m->hMonitorThread, uTimeoutMs, NULL);
230 if (RT_FAILURE(vrc))
231 LogRel(("HostDnsMonitor: waiting for thread failed with vrc=%Rrc\n", vrc));
232
233 if (m->hMonitorThreadEvent != NIL_RTSEMEVENT)
234 {
235 RTSemEventDestroy(m->hMonitorThreadEvent);
236 m->hMonitorThreadEvent = NIL_RTSEMEVENT;
237 }
238 }
239
240 LogRel(("HostDnsMonitor: shut down\n"));
241}
242
243void HostDnsServiceBase::setInfo(const HostDnsInformation &info)
244{
245 if (m->pProxy != NULL)
246 m->pProxy->notify(info);
247}
248
249/**
250 * Updates HostDnsMonitorProxy::Data::fLaxComparison every 30 seconds, returning
251 * the new value.
252 *
253 * @note This will leave the lock while calling IVirtualBox::GetExtraData.
254 */
255uint32_t HostDnsMonitorProxy::pollGlobalExtraData(AutoWriteLock &aLock)
256{
257 uint32_t fLaxComparison = m->fLaxComparison;
258 VirtualBox *pVirtualBox = m->pVirtualBox;
259 if (pVirtualBox)
260 {
261 uint64_t uNow = RTTimeNanoTS();
262 if (uNow - m->uLastExtraDataPoll >= RT_NS_30SEC || m->uLastExtraDataPoll == 0)
263 {
264 m->uLastExtraDataPoll = uNow;
265
266 /* We cannot do GetExtraData holding this lock, so temporarily release it. */
267 aLock.release();
268
269 /*
270 * Should we ignore the order of DNS servers?
271 */
272 const com::Bstr bstrHostDNSOrderIgnoreKey("VBoxInternal2/HostDNSOrderIgnore");
273 com::Bstr bstrHostDNSOrderIgnore;
274 pVirtualBox->GetExtraData(bstrHostDNSOrderIgnoreKey.raw(), bstrHostDNSOrderIgnore.asOutParam());
275 uint32_t fDNSOrderIgnore = 0;
276 if (bstrHostDNSOrderIgnore.isNotEmpty() && bstrHostDNSOrderIgnore != "0")
277 fDNSOrderIgnore = HostDnsInformation::IGNORE_SERVER_ORDER;
278
279 if (fDNSOrderIgnore != (fLaxComparison & HostDnsInformation::IGNORE_SERVER_ORDER))
280 {
281 fLaxComparison ^= HostDnsInformation::IGNORE_SERVER_ORDER;
282 LogRel(("HostDnsMonitor: %ls=%ls\n", bstrHostDNSOrderIgnoreKey.raw(), bstrHostDNSOrderIgnore.raw()));
283 }
284
285 /*
286 * Should we ignore changes to the domain name or the search list?
287 */
288 const com::Bstr bstrHostDNSSuffixesIgnoreKey("VBoxInternal2/HostDNSSuffixesIgnore");
289 com::Bstr bstrHostDNSSuffixesIgnore;
290 pVirtualBox->GetExtraData(bstrHostDNSSuffixesIgnoreKey.raw(), bstrHostDNSSuffixesIgnore.asOutParam());
291 uint32_t fDNSSuffixesIgnore = 0;
292 if (bstrHostDNSSuffixesIgnore.isNotEmpty() && bstrHostDNSSuffixesIgnore != "0")
293 fDNSSuffixesIgnore = HostDnsInformation::IGNORE_SUFFIXES;
294
295 if (fDNSSuffixesIgnore != (fLaxComparison & HostDnsInformation::IGNORE_SUFFIXES))
296 {
297 fLaxComparison ^= HostDnsInformation::IGNORE_SUFFIXES;
298 LogRel(("HostDnsMonitor: %ls=%ls\n", bstrHostDNSSuffixesIgnoreKey.raw(), bstrHostDNSSuffixesIgnore.raw()));
299 }
300
301 aLock.acquire();
302 m->fLaxComparison = fLaxComparison;
303 }
304 }
305 return fLaxComparison;
306}
307
308void HostDnsServiceBase::onMonitorThreadInitDone(void)
309{
310 if (!m->fThreaded) /* If non-threaded, bail out, nothing to do here. */
311 return;
312
313 RTSemEventSignal(m->hMonitorThreadEvent);
314}
315
316DECLCALLBACK(int) HostDnsServiceBase::threadMonitorProc(RTTHREAD, void *pvUser)
317{
318 HostDnsServiceBase *pThis = static_cast<HostDnsServiceBase *>(pvUser);
319 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
320 return pThis->monitorThreadProc();
321}
322
323/* HostDnsMonitorProxy */
324HostDnsMonitorProxy::HostDnsMonitorProxy()
325 : m(NULL)
326 , m_ObjectLock(LOCKCLASS_OTHEROBJECT, "HostDnsMonitorProxy")
327{
328}
329
330HostDnsMonitorProxy::~HostDnsMonitorProxy()
331{
332 uninit();
333}
334
335HRESULT HostDnsMonitorProxy::init(VirtualBox *aParent)
336{
337 AssertMsgReturn(m == NULL, ("DNS monitor proxy already initialized\n"), E_FAIL);
338
339 HostDnsServiceBase *pMonitorImpl = HostDnsServiceBase::createHostDnsMonitor();
340 AssertPtrReturn(pMonitorImpl, E_OUTOFMEMORY);
341
342 Assert(m == NULL); /* Paranoia. */
343 m = new HostDnsMonitorProxy::Data(pMonitorImpl, aParent);
344 AssertPtrReturn(m, E_OUTOFMEMORY);
345
346 return m->pMonitorImpl->init(this);
347}
348
349void HostDnsMonitorProxy::uninit(void)
350{
351 if (m)
352 {
353 if (m->pMonitorImpl)
354 {
355 m->pMonitorImpl->uninit();
356
357 delete m->pMonitorImpl;
358 m->pMonitorImpl = NULL;
359 }
360
361 delete m;
362 m = NULL;
363 }
364}
365
366util::LockHandle *HostDnsMonitorProxy::lockHandle() const
367{
368 return &m_ObjectLock;
369}
370
371void HostDnsMonitorProxy::notify(const HostDnsInformation &info)
372{
373 const bool fNotify = updateInfo(info);
374 if (fNotify)
375 m->pVirtualBox->i_onHostNameResolutionConfigurationChange();
376}
377
378HRESULT HostDnsMonitorProxy::GetNameServers(std::vector<com::Utf8Str> &aNameServers)
379{
380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
381 AssertReturn(m != NULL, E_FAIL);
382
383 LogRel(("HostDnsMonitorProxy::GetNameServers:\n"));
384 dumpHostDnsStrVector("name server", m->info.servers);
385
386 detachVectorOfStrings(m->info.servers, aNameServers);
387
388 return S_OK;
389}
390
391HRESULT HostDnsMonitorProxy::GetDomainName(com::Utf8Str *pDomainName)
392{
393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
394 AssertReturn(m != NULL, E_FAIL);
395
396 LogRel(("HostDnsMonitorProxy::GetDomainName: %s\n", m->info.domain.empty() ? "no domain set" : m->info.domain.c_str()));
397 *pDomainName = m->info.domain.c_str();
398
399 return S_OK;
400}
401
402HRESULT HostDnsMonitorProxy::GetSearchStrings(std::vector<com::Utf8Str> &aSearchStrings)
403{
404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
405 AssertReturn(m != NULL, E_FAIL);
406
407 LogRel(("HostDnsMonitorProxy::GetSearchStrings:\n"));
408 dumpHostDnsStrVector("search string", m->info.searchList);
409
410 detachVectorOfStrings(m->info.searchList, aSearchStrings);
411
412 return S_OK;
413}
414
415bool HostDnsMonitorProxy::updateInfo(const HostDnsInformation &info)
416{
417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
418 LogRel(("HostDnsMonitor: updating information\n"));
419
420 if (info.equals(m->info))
421 {
422 LogRel(("HostDnsMonitor: unchanged\n"));
423 return false;
424 }
425
426 uint32_t const fLaxComparison = pollGlobalExtraData(alock);
427
428 LogRel(("HostDnsMonitor: old information\n"));
429 dumpHostDnsInformation(m->info);
430 LogRel(("HostDnsMonitor: new information\n"));
431 dumpHostDnsInformation(info);
432
433 bool fIgnore = fLaxComparison != 0 && info.equals(m->info, fLaxComparison);
434 m->info = info;
435
436 if (fIgnore)
437 {
438 LogRel(("HostDnsMonitor: lax comparison %#x, not notifying\n", m->fLaxComparison));
439 return false;
440 }
441
442 return true;
443}
444
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