summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/HostDnsService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/HostDnsService.cpp')
-rw-r--r--src/VBox/Main/src-server/HostDnsService.cpp440
1 files changed, 440 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/HostDnsService.cpp b/src/VBox/Main/src-server/HostDnsService.cpp
new file mode 100644
index 00000000..d571dd6d
--- /dev/null
+++ b/src/VBox/Main/src-server/HostDnsService.cpp
@@ -0,0 +1,440 @@
+/* $Id: HostDnsService.cpp $ */
+/** @file
+ * Base class for Host DNS & Co services.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_HOST
+#include <VBox/com/array.h>
+#include <VBox/com/ptr.h>
+#include <VBox/com/string.h>
+
+#include <iprt/cpp/utils.h>
+
+#include "LoggingNew.h"
+#include "VirtualBoxImpl.h"
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/semaphore.h>
+#include <iprt/critsect.h>
+
+#include <algorithm>
+#include <set>
+#include <iprt/sanitized/string>
+#include "HostDnsService.h"
+
+
+
+static void dumpHostDnsStrVector(const std::string &prefix, const std::vector<std::string> &v)
+{
+ int i = 1;
+ for (std::vector<std::string>::const_iterator it = v.begin();
+ it != v.end();
+ ++it, ++i)
+ LogRel((" %s %d: %s\n", prefix.c_str(), i, it->c_str()));
+ if (v.empty())
+ LogRel((" no %s entries\n", prefix.c_str()));
+}
+
+static void dumpHostDnsInformation(const HostDnsInformation &info)
+{
+ dumpHostDnsStrVector("server", info.servers);
+
+ if (!info.domain.empty())
+ LogRel((" domain: %s\n", info.domain.c_str()));
+ else
+ LogRel((" no domain set\n"));
+
+ dumpHostDnsStrVector("search string", info.searchList);
+}
+
+bool HostDnsInformation::equals(const HostDnsInformation &info, uint32_t fLaxComparison) const
+{
+ bool fSameServers;
+ if ((fLaxComparison & IGNORE_SERVER_ORDER) == 0)
+ fSameServers = (servers == info.servers);
+ else
+ {
+ std::set<std::string> l(servers.begin(), servers.end());
+ std::set<std::string> r(info.servers.begin(), info.servers.end());
+
+ fSameServers = (l == r);
+ }
+
+ bool fSameDomain, fSameSearchList;
+ if ((fLaxComparison & IGNORE_SUFFIXES) == 0)
+ {
+ fSameDomain = (domain == info.domain);
+ fSameSearchList = (searchList == info.searchList);
+ }
+ else
+ fSameDomain = fSameSearchList = true;
+
+ return fSameServers && fSameDomain && fSameSearchList;
+}
+
+DECLINLINE(void) detachVectorOfString(const std::vector<std::string>& v, std::vector<com::Utf8Str> &aArray)
+{
+ aArray.resize(v.size());
+ size_t i = 0;
+ for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it, ++i)
+ aArray[i] = Utf8Str(it->c_str()); /** @todo r=bird: *it isn't necessarily UTF-8 clean!!
+ * On darwin we do silly shit like using CFStringGetSystemEncoding()
+ * that may be UTF-8 but doesn't need to be.
+ *
+ * Why on earth are we using std::string here anyway?
+ */
+}
+
+struct HostDnsServiceBase::Data
+{
+ Data(bool aThreaded)
+ : pProxy(NULL)
+ , fThreaded(aThreaded)
+ , hMonitorThreadEvent(NIL_RTSEMEVENT)
+ , hMonitorThread(NIL_RTTHREAD)
+ {}
+
+ /** Weak pointer to parent proxy object. */
+ HostDnsMonitorProxy *pProxy;
+ /** Whether the DNS monitor implementation has a dedicated monitoring thread. Optional. */
+ const bool fThreaded;
+ /** Event for the monitor thread, if any. */
+ RTSEMEVENT hMonitorThreadEvent;
+ /** Handle of the monitor thread, if any. */
+ RTTHREAD hMonitorThread;
+ /** Generic host DNS information. */
+ HostDnsInformation info;
+};
+
+struct HostDnsMonitorProxy::Data
+{
+ Data(HostDnsServiceBase *aMonitor, VirtualBox *aParent)
+ : pVirtualBox(aParent)
+ , pMonitorImpl(aMonitor)
+ , uLastExtraDataPoll(0)
+ , fLaxComparison(0)
+ , info()
+ {}
+
+ VirtualBox *pVirtualBox;
+ HostDnsServiceBase *pMonitorImpl;
+
+ uint64_t uLastExtraDataPoll;
+ uint32_t fLaxComparison;
+ HostDnsInformation info;
+};
+
+
+HostDnsServiceBase::HostDnsServiceBase(bool fThreaded)
+ : m(NULL)
+{
+ m = new HostDnsServiceBase::Data(fThreaded);
+}
+
+HostDnsServiceBase::~HostDnsServiceBase()
+{
+ if (m)
+ {
+ delete m;
+ m = NULL;
+ }
+}
+
+/* static */
+HostDnsServiceBase *HostDnsServiceBase::createHostDnsMonitor(void)
+{
+ HostDnsServiceBase *pMonitor = NULL;
+
+#if defined (RT_OS_DARWIN)
+ pMonitor = new HostDnsServiceDarwin();
+#elif defined(RT_OS_WINDOWS)
+ pMonitor = new HostDnsServiceWin();
+#elif defined(RT_OS_LINUX)
+ pMonitor = new HostDnsServiceLinux();
+#elif defined(RT_OS_SOLARIS)
+ pMonitor = new HostDnsServiceSolaris();
+#elif defined(RT_OS_FREEBSD)
+ pMonitor = new HostDnsServiceFreebsd();
+#elif defined(RT_OS_OS2)
+ pMonitor = new HostDnsServiceOs2();
+#else
+ pMonitor = new HostDnsServiceBase();
+#endif
+
+ return pMonitor;
+}
+
+HRESULT HostDnsServiceBase::init(HostDnsMonitorProxy *pProxy)
+{
+ LogRel(("HostDnsMonitor: initializing\n"));
+
+ AssertPtrReturn(pProxy, E_POINTER);
+ m->pProxy = pProxy;
+
+ if (m->fThreaded)
+ {
+ LogRel2(("HostDnsMonitor: starting thread ...\n"));
+
+ int vrc = RTSemEventCreate(&m->hMonitorThreadEvent);
+ AssertRCReturn(vrc, E_FAIL);
+
+ vrc = RTThreadCreate(&m->hMonitorThread,
+ HostDnsServiceBase::threadMonitorProc,
+ this, 128 * _1K, RTTHREADTYPE_IO,
+ RTTHREADFLAGS_WAITABLE, "dns-monitor");
+ AssertRCReturn(vrc, E_FAIL);
+
+ RTSemEventWait(m->hMonitorThreadEvent, RT_INDEFINITE_WAIT);
+
+ LogRel2(("HostDnsMonitor: thread started\n"));
+ }
+
+ return S_OK;
+}
+
+void HostDnsServiceBase::uninit(void)
+{
+ LogRel(("HostDnsMonitor: shutting down ...\n"));
+
+ if (m->fThreaded)
+ {
+ LogRel2(("HostDnsMonitor: waiting for thread ...\n"));
+
+ const RTMSINTERVAL uTimeoutMs = 30 * 1000; /* 30s */
+
+ monitorThreadShutdown(uTimeoutMs);
+
+ int vrc = RTThreadWait(m->hMonitorThread, uTimeoutMs, NULL);
+ if (RT_FAILURE(vrc))
+ LogRel(("HostDnsMonitor: waiting for thread failed with vrc=%Rrc\n", vrc));
+
+ if (m->hMonitorThreadEvent != NIL_RTSEMEVENT)
+ {
+ RTSemEventDestroy(m->hMonitorThreadEvent);
+ m->hMonitorThreadEvent = NIL_RTSEMEVENT;
+ }
+ }
+
+ LogRel(("HostDnsMonitor: shut down\n"));
+}
+
+void HostDnsServiceBase::setInfo(const HostDnsInformation &info)
+{
+ if (m->pProxy != NULL)
+ m->pProxy->notify(info);
+}
+
+void HostDnsMonitorProxy::pollGlobalExtraData(void)
+{
+ VirtualBox *pVirtualBox = m->pVirtualBox;
+ if (RT_UNLIKELY(pVirtualBox == NULL))
+ return;
+
+ uint64_t uNow = RTTimeNanoTS();
+ if (uNow - m->uLastExtraDataPoll >= RT_NS_30SEC || m->uLastExtraDataPoll == 0)
+ {
+ m->uLastExtraDataPoll = uNow;
+
+ /*
+ * Should we ignore the order of DNS servers?
+ */
+ const com::Bstr bstrHostDNSOrderIgnoreKey("VBoxInternal2/HostDNSOrderIgnore");
+ com::Bstr bstrHostDNSOrderIgnore;
+ pVirtualBox->GetExtraData(bstrHostDNSOrderIgnoreKey.raw(),
+ bstrHostDNSOrderIgnore.asOutParam());
+ uint32_t fDNSOrderIgnore = 0;
+ if (bstrHostDNSOrderIgnore.isNotEmpty())
+ {
+ if (bstrHostDNSOrderIgnore != "0")
+ fDNSOrderIgnore = HostDnsInformation::IGNORE_SERVER_ORDER;
+ }
+
+ if (fDNSOrderIgnore != (m->fLaxComparison & HostDnsInformation::IGNORE_SERVER_ORDER))
+ {
+
+ m->fLaxComparison ^= HostDnsInformation::IGNORE_SERVER_ORDER;
+ LogRel(("HostDnsMonitor: %ls=%ls\n",
+ bstrHostDNSOrderIgnoreKey.raw(),
+ bstrHostDNSOrderIgnore.raw()));
+ }
+
+ /*
+ * Should we ignore changes to the domain name or the search list?
+ */
+ const com::Bstr bstrHostDNSSuffixesIgnoreKey("VBoxInternal2/HostDNSSuffixesIgnore");
+ com::Bstr bstrHostDNSSuffixesIgnore;
+ pVirtualBox->GetExtraData(bstrHostDNSSuffixesIgnoreKey.raw(),
+ bstrHostDNSSuffixesIgnore.asOutParam());
+ uint32_t fDNSSuffixesIgnore = 0;
+ if (bstrHostDNSSuffixesIgnore.isNotEmpty())
+ {
+ if (bstrHostDNSSuffixesIgnore != "0")
+ fDNSSuffixesIgnore = HostDnsInformation::IGNORE_SUFFIXES;
+ }
+
+ if (fDNSSuffixesIgnore != (m->fLaxComparison & HostDnsInformation::IGNORE_SUFFIXES))
+ {
+
+ m->fLaxComparison ^= HostDnsInformation::IGNORE_SUFFIXES;
+ LogRel(("HostDnsMonitor: %ls=%ls\n",
+ bstrHostDNSSuffixesIgnoreKey.raw(),
+ bstrHostDNSSuffixesIgnore.raw()));
+ }
+ }
+}
+
+void HostDnsServiceBase::onMonitorThreadInitDone(void)
+{
+ if (!m->fThreaded) /* If non-threaded, bail out, nothing to do here. */
+ return;
+
+ RTSemEventSignal(m->hMonitorThreadEvent);
+}
+
+DECLCALLBACK(int) HostDnsServiceBase::threadMonitorProc(RTTHREAD, void *pvUser)
+{
+ HostDnsServiceBase *pThis = static_cast<HostDnsServiceBase *>(pvUser);
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ return pThis->monitorThreadProc();
+}
+
+/* HostDnsMonitorProxy */
+HostDnsMonitorProxy::HostDnsMonitorProxy()
+ : m(NULL)
+{
+}
+
+HostDnsMonitorProxy::~HostDnsMonitorProxy()
+{
+ uninit();
+}
+
+HRESULT HostDnsMonitorProxy::init(VirtualBox* aParent)
+{
+ AssertMsgReturn(m == NULL, ("DNS monitor proxy already initialized\n"), E_FAIL);
+
+ HostDnsServiceBase *pMonitorImpl = HostDnsServiceBase::createHostDnsMonitor();
+ AssertPtrReturn(pMonitorImpl, E_OUTOFMEMORY);
+
+ Assert(m == NULL); /* Paranoia. */
+ m = new HostDnsMonitorProxy::Data(pMonitorImpl, aParent);
+ AssertPtrReturn(m, E_OUTOFMEMORY);
+
+ return m->pMonitorImpl->init(this);
+}
+
+void HostDnsMonitorProxy::uninit(void)
+{
+ if (m)
+ {
+ if (m->pMonitorImpl)
+ {
+ m->pMonitorImpl->uninit();
+
+ delete m->pMonitorImpl;
+ m->pMonitorImpl = NULL;
+ }
+
+ delete m;
+ m = NULL;
+ }
+}
+
+void HostDnsMonitorProxy::notify(const HostDnsInformation &info)
+{
+ const bool fNotify = updateInfo(info);
+ if (fNotify)
+ m->pVirtualBox->i_onHostNameResolutionConfigurationChange();
+}
+
+HRESULT HostDnsMonitorProxy::GetNameServers(std::vector<com::Utf8Str> &aNameServers)
+{
+ AssertReturn(m != NULL, E_FAIL);
+ RTCLock grab(m_LockMtx);
+
+ LogRel(("HostDnsMonitorProxy::GetNameServers:\n"));
+ dumpHostDnsStrVector("name server", m->info.servers);
+
+ detachVectorOfString(m->info.servers, aNameServers);
+
+ return S_OK;
+}
+
+HRESULT HostDnsMonitorProxy::GetDomainName(com::Utf8Str *pDomainName)
+{
+ AssertReturn(m != NULL, E_FAIL);
+ RTCLock grab(m_LockMtx);
+
+ LogRel(("HostDnsMonitorProxy::GetDomainName: %s\n",
+ m->info.domain.empty() ? "no domain set" : m->info.domain.c_str()));
+
+ *pDomainName = m->info.domain.c_str();
+
+ return S_OK;
+}
+
+HRESULT HostDnsMonitorProxy::GetSearchStrings(std::vector<com::Utf8Str> &aSearchStrings)
+{
+ AssertReturn(m != NULL, E_FAIL);
+ RTCLock grab(m_LockMtx);
+
+ LogRel(("HostDnsMonitorProxy::GetSearchStrings:\n"));
+ dumpHostDnsStrVector("search string", m->info.searchList);
+
+ detachVectorOfString(m->info.searchList, aSearchStrings);
+
+ return S_OK;
+}
+
+bool HostDnsMonitorProxy::updateInfo(const HostDnsInformation &info)
+{
+ LogRel(("HostDnsMonitor: updating information\n"));
+ RTCLock grab(m_LockMtx);
+
+ if (info.equals(m->info))
+ {
+ LogRel(("HostDnsMonitor: unchanged\n"));
+ return false;
+ }
+
+ pollGlobalExtraData();
+
+ LogRel(("HostDnsMonitor: old information\n"));
+ dumpHostDnsInformation(m->info);
+ LogRel(("HostDnsMonitor: new information\n"));
+ dumpHostDnsInformation(info);
+
+ bool fIgnore = m->fLaxComparison != 0 && info.equals(m->info, m->fLaxComparison);
+ m->info = info;
+
+ if (fIgnore)
+ {
+ LogRel(("HostDnsMonitor: lax comparison %#x, not notifying\n", m->fLaxComparison));
+ return false;
+ }
+
+ return true;
+}
+