summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/DHCPConfigImpl.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
commit16f504a9dca3fe3b70568f67b7d41241ae485288 (patch)
treec60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Main/src-server/DHCPConfigImpl.cpp
parentInitial commit. (diff)
downloadvirtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.tar.xz
virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.zip
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Main/src-server/DHCPConfigImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/DHCPConfigImpl.cpp1509
1 files changed, 1509 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/DHCPConfigImpl.cpp b/src/VBox/Main/src-server/DHCPConfigImpl.cpp
new file mode 100644
index 00000000..8e091612
--- /dev/null
+++ b/src/VBox/Main/src-server/DHCPConfigImpl.cpp
@@ -0,0 +1,1509 @@
+/* $Id: DHCPConfigImpl.cpp $ */
+/** @file
+ * VirtualBox Main - IDHCPConfig, IDHCPConfigGlobal, IDHCPConfigGroup, IDHCPConfigIndividual implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2022 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_DHCPCONFIG
+#include "DHCPConfigImpl.h"
+#include "LoggingNew.h"
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/net.h>
+#include <iprt/cpp/utils.h>
+#include <iprt/cpp/xml.h>
+
+#include <VBox/com/array.h>
+#include <VBox/settings.h>
+
+#include "AutoCaller.h"
+#include "DHCPServerImpl.h"
+#include "MachineImpl.h"
+#include "VirtualBoxImpl.h"
+
+#include "../../NetworkServices/Dhcpd/DhcpOptions.h"
+
+
+
+/*********************************************************************************************************************************
+* DHCPConfig Implementation *
+*********************************************************************************************************************************/
+
+HRESULT DHCPConfig::i_initWithDefaults(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent)
+{
+ unconst(m_pVirtualBox) = a_pVirtualBox;
+ unconst(m_pParent) = a_pParent;
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_initWithSettings(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, const settings::DHCPConfig &rConfig)
+{
+ unconst(m_pVirtualBox) = a_pVirtualBox;
+ unconst(m_pParent) = a_pParent;
+
+ m_secMinLeaseTime = rConfig.secMinLeaseTime;
+ m_secDefaultLeaseTime = rConfig.secDefaultLeaseTime;
+ m_secMaxLeaseTime = rConfig.secMaxLeaseTime;
+
+ /*
+ * The two option list:
+ */
+ struct
+ {
+ const char *psz;
+ std::vector<DHCPOption_T> *pDst;
+ } aStr2Vec[] =
+ {
+ { rConfig.strForcedOptions.c_str(), &m_vecForcedOptions },
+ { rConfig.strSuppressedOptions.c_str(), &m_vecSuppressedOptions },
+ };
+ for (size_t i = 0; i < RT_ELEMENTS(aStr2Vec); i++)
+ {
+ Assert(aStr2Vec[i].pDst->size() == 0);
+ const char *psz = RTStrStripL(aStr2Vec[i].psz);
+ while (*psz != '\0')
+ {
+ uint8_t bOpt;
+ char *pszNext;
+ int vrc = RTStrToUInt8Ex(psz, &pszNext, 10, &bOpt);
+ if ( vrc == VINF_SUCCESS
+ || vrc == VWRN_TRAILING_SPACES
+ || vrc == VWRN_TRAILING_CHARS)
+ {
+ try
+ {
+ aStr2Vec[i].pDst->push_back((DHCPOption_T)bOpt);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+ else
+ {
+ LogRelFunc(("Trouble at offset %#zu converting '%s' to a DHCPOption_T vector (vrc=%Rrc)! Ignornig the remainder.\n",
+ psz - aStr2Vec[i].psz, aStr2Vec[i].psz, vrc));
+ break;
+ }
+ psz = RTStrStripL(pszNext);
+ }
+ }
+
+ /*
+ * The option map:
+ */
+ for (settings::DhcpOptionMap::const_iterator it = rConfig.mapOptions.begin(); it != rConfig.mapOptions.end(); ++it)
+ {
+ try
+ {
+ m_OptionMap[it->first] = settings::DhcpOptValue(it->second.strValue, it->second.enmEncoding);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_saveSettings(settings::DHCPConfig &a_rDst)
+{
+ /* lease times */
+ a_rDst.secMinLeaseTime = m_secMinLeaseTime;
+ a_rDst.secDefaultLeaseTime = m_secDefaultLeaseTime;
+ a_rDst.secMaxLeaseTime = m_secMaxLeaseTime;
+
+ /* Forced and suppressed vectors: */
+ try
+ {
+ a_rDst.strForcedOptions.setNull();
+ for (size_t i = 0; i < m_vecForcedOptions.size(); i++)
+ a_rDst.strForcedOptions.appendPrintf(i ? " %d" : "%d", m_vecForcedOptions[i]);
+
+ a_rDst.strSuppressedOptions.setNull();
+ for (size_t i = 0; i < m_vecSuppressedOptions.size(); i++)
+ a_rDst.strSuppressedOptions.appendPrintf(i ? " %d" : "%d", m_vecSuppressedOptions[i]);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+
+ /* Options: */
+ try
+ {
+ a_rDst.mapOptions = m_OptionMap;
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_getScope(DHCPConfigScope_T *aScope)
+{
+ /* No locking needed. */
+ *aScope = m_enmScope;
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_getMinLeaseTime(ULONG *aMinLeaseTime)
+{
+ AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ *aMinLeaseTime = m_secMinLeaseTime;
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_setMinLeaseTime(ULONG aMinLeaseTime)
+{
+ {
+ AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ m_secMinLeaseTime = aMinLeaseTime;
+ }
+ return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_getDefaultLeaseTime(ULONG *aDefaultLeaseTime)
+{
+ AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ *aDefaultLeaseTime = m_secDefaultLeaseTime;
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_setDefaultLeaseTime(ULONG aDefaultLeaseTime)
+{
+ {
+ AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ m_secDefaultLeaseTime = aDefaultLeaseTime;
+ }
+ return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_getMaxLeaseTime(ULONG *aMaxLeaseTime)
+{
+ AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ *aMaxLeaseTime = m_secMaxLeaseTime;
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_setMaxLeaseTime(ULONG aMaxLeaseTime)
+{
+ {
+ AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ m_secMaxLeaseTime = aMaxLeaseTime;
+ }
+ return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_getForcedOptions(std::vector<DHCPOption_T> &aOptions)
+{
+ AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ try
+ {
+ aOptions = m_vecForcedOptions;
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_setForcedOptions(const std::vector<DHCPOption_T> &aOptions)
+{
+ /*
+ * Validate the options.
+ */
+ try
+ {
+ std::map<DHCPOption_T, bool> mapDuplicates;
+ for (size_t i = 0; i < aOptions.size(); i++)
+ {
+ DHCPOption_T enmOpt = aOptions[i];
+ if ((int)enmOpt > 0 && (int)enmOpt < 255)
+ {
+ if (mapDuplicates.find(enmOpt) == mapDuplicates.end())
+ mapDuplicates[enmOpt] = true;
+ else
+ return m_pHack->setError(E_INVALIDARG, tr("Duplicate option value: %d"), (int)enmOpt);
+ }
+ else
+ return m_pHack->setError(E_INVALIDARG, tr("Invalid option value: %d"), (int)enmOpt);
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ /*
+ * Do the updating.
+ */
+ {
+ AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+
+ /* Actually changed? */
+ if (m_vecForcedOptions.size() == aOptions.size())
+ {
+ ssize_t i = (ssize_t)m_vecForcedOptions.size();
+ while (i-- > 0)
+ if (m_vecForcedOptions[(size_t)i] != aOptions[(size_t)i])
+ break;
+ if (i < 0)
+ return S_OK;
+ }
+
+ /* Copy over the changes: */
+ try
+ {
+ m_vecForcedOptions = aOptions;
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_getSuppressedOptions(std::vector<DHCPOption_T> &aOptions)
+{
+ AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ try
+ {
+ aOptions = m_vecSuppressedOptions;
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_setSuppressedOptions(const std::vector<DHCPOption_T> &aOptions)
+{
+ /*
+ * Validate and normalize it.
+ */
+ std::map<DHCPOption_T, bool> mapNormalized;
+ try
+ {
+ for (size_t i = 0; i < aOptions.size(); i++)
+ {
+ DHCPOption_T enmOpt = aOptions[i];
+ if ((int)enmOpt > 0 && (int)enmOpt < 255)
+ mapNormalized[enmOpt] = true;
+ else
+ return m_pHack->setError(E_INVALIDARG, tr("Invalid option value: %d"), (int)enmOpt);
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ /*
+ * Do the updating.
+ */
+ {
+ AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+
+ /* Actually changed? */
+ if (m_vecSuppressedOptions.size() == mapNormalized.size())
+ {
+ size_t i = 0;
+ for (std::map<DHCPOption_T, bool>::const_iterator itMap = mapNormalized.begin();; ++itMap, i++)
+ {
+ if (itMap == mapNormalized.end())
+ return S_OK; /* no change */
+ if (itMap->first != m_vecSuppressedOptions[i])
+ break;
+ }
+ }
+
+ /* Copy over the changes: */
+ try
+ {
+ m_vecSuppressedOptions.resize(mapNormalized.size());
+ size_t i = 0;
+ for (std::map<DHCPOption_T, bool>::const_iterator itMap = mapNormalized.begin();
+ itMap != mapNormalized.end(); ++itMap, i++)
+ m_vecSuppressedOptions[i] = itMap->first;
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_setOption(DHCPOption_T aOption, DHCPOptionEncoding_T aEncoding, const com::Utf8Str &aValue)
+{
+ /*
+ * Validate the option as there is no point in allowing the user to set
+ * something that the DHCP server does not grok. It will only lead to
+ * startup failures an no DHCP. We share this code with the server.
+ */
+ DhcpOption *pParsed = NULL;
+ int rc = VINF_SUCCESS;
+ try
+ {
+ pParsed = DhcpOption::parse((uint8_t)aOption, aEncoding, aValue.c_str(), &rc);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ if (pParsed)
+ {
+ delete pParsed;
+
+ /*
+ * Add/change it.
+ */
+ {
+ AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ try
+ {
+ m_OptionMap[aOption] = settings::DhcpOptValue(aValue, aEncoding);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+ i_doWriteConfig();
+ return S_OK;
+ }
+
+ if (rc == VERR_WRONG_TYPE)
+ return m_pHack->setError(E_INVALIDARG, tr("Unsupported encoding %d (option %d, value %s)"),
+ (int)aEncoding, (int)aOption, aValue.c_str());
+ if (rc == VERR_NOT_SUPPORTED)
+ return m_pHack->setError(E_INVALIDARG, tr("Unsupported option %d (encoding %d, value %s)"),
+ (int)aOption, (int)aEncoding, aValue.c_str());
+ return m_pHack->setError(E_INVALIDARG, tr("Malformed option %d value '%s' (encoding %d, rc=%Rrc)"),
+ (int)aOption, aValue.c_str(), (int)aEncoding, rc);
+}
+
+
+HRESULT DHCPConfig::i_removeOption(DHCPOption_T aOption)
+{
+ {
+ AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ settings::DhcpOptionMap::iterator it = m_OptionMap.find(aOption);
+ if (it != m_OptionMap.end())
+ m_OptionMap.erase(it);
+ else
+ return m_pHack->setError(VBOX_E_OBJECT_NOT_FOUND, tr("DHCP option %u was not found"), aOption);
+ }
+ return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_removeAllOptions()
+{
+ {
+ AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ m_OptionMap.erase(m_OptionMap.begin(), m_OptionMap.end());
+ }
+ return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_getOption(DHCPOption_T aOption, DHCPOptionEncoding_T *aEncoding, com::Utf8Str &aValue)
+{
+ AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ settings::DhcpOptionMap::const_iterator it = m_OptionMap.find(aOption);
+ if (it != m_OptionMap.end())
+ {
+ *aEncoding = it->second.enmEncoding;
+ return aValue.assignEx(it->second.strValue);
+ }
+ return m_pHack->setError(VBOX_E_OBJECT_NOT_FOUND, tr("DHCP option %u was not found"), aOption);
+}
+
+
+HRESULT DHCPConfig::i_getAllOptions(std::vector<DHCPOption_T> &aOptions, std::vector<DHCPOptionEncoding_T> &aEncodings,
+ std::vector<com::Utf8Str> &aValues)
+{
+ AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+ try
+ {
+ aOptions.resize(m_OptionMap.size());
+ aEncodings.resize(m_OptionMap.size());
+ aValues.resize(m_OptionMap.size());
+ size_t i = 0;
+ for (settings::DhcpOptionMap::iterator it = m_OptionMap.begin(); it != m_OptionMap.end(); ++it, i++)
+ {
+ aOptions[i] = it->first;
+ aEncodings[i] = it->second.enmEncoding;
+ aValues[i] = it->second.strValue;
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_remove()
+{
+ return m_pParent->i_removeConfig(this, m_enmScope);
+}
+
+
+
+/**
+ * Causes the global VirtualBox configuration file to be written
+ *
+ * @returns COM status code.
+ *
+ * @note Must hold no locks when this is called!
+ * @note Public because DHCPGroupCondition needs to call it too.
+ */
+HRESULT DHCPConfig::i_doWriteConfig()
+{
+ AssertPtrReturn(m_pVirtualBox, E_FAIL);
+
+ AutoWriteLock alock(m_pVirtualBox COMMA_LOCKVAL_SRC_POS);
+ return m_pVirtualBox->i_saveSettings();
+}
+
+
+/**
+ * Produces the Dhcpd configuration.
+ *
+ * The base class only saves DHCP options.
+ *
+ * @param pElmConfig The element where to put the configuration.
+ * @throws std::bad_alloc
+ */
+void DHCPConfig::i_writeDhcpdConfig(xml::ElementNode *pElmConfig)
+{
+ if (m_secMinLeaseTime > 0 )
+ pElmConfig->setAttribute("secMinLeaseTime", (uint32_t)m_secMinLeaseTime);
+ if (m_secDefaultLeaseTime > 0 )
+ pElmConfig->setAttribute("secDefaultLeaseTime", (uint32_t)m_secDefaultLeaseTime);
+ if (m_secMaxLeaseTime > 0 )
+ pElmConfig->setAttribute("secMaxLeaseTime", (uint32_t)m_secMaxLeaseTime);
+
+ struct
+ {
+ const char *pszElement;
+ std::vector<DHCPOption_T> *pVec;
+ } aVec2Elm[] = { { "ForcedOption", &m_vecForcedOptions }, { "SuppressedOption", &m_vecSuppressedOptions }, };
+ for (size_t i = 0; i < RT_ELEMENTS(aVec2Elm); i++)
+ for (std::vector<DHCPOption_T>::const_iterator it = aVec2Elm[i].pVec->begin(); it != aVec2Elm[i].pVec->end(); ++it)
+ {
+ xml::ElementNode *pElmChild = pElmConfig->createChild(aVec2Elm[i].pszElement);
+ pElmChild->setAttribute("name", (int)*it);
+ }
+
+ for (settings::DhcpOptionMap::const_iterator it = m_OptionMap.begin(); it != m_OptionMap.end(); ++it)
+ {
+ xml::ElementNode *pElmOption = pElmConfig->createChild("Option");
+ pElmOption->setAttribute("name", (int)it->first);
+ pElmOption->setAttribute("encoding", it->second.enmEncoding);
+ pElmOption->setAttribute("value", it->second.strValue);
+ }
+}
+
+
+
+/*********************************************************************************************************************************
+* DHCPGlobalConfig Implementation *
+*********************************************************************************************************************************/
+#undef LOG_GROUP
+#define LOG_GROUP LOG_GROUP_MAIN_DHCPGLOBALCONFIG
+
+HRESULT DHCPGlobalConfig::initWithDefaults(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ HRESULT hrc = DHCPConfig::i_initWithDefaults(a_pVirtualBox, a_pParent);
+ if (SUCCEEDED(hrc))
+ hrc = i_setOption(DHCPOption_SubnetMask, DHCPOptionEncoding_Normal, "0.0.0.0");
+
+ if (SUCCEEDED(hrc))
+ autoInitSpan.setSucceeded();
+ return hrc;
+}
+
+
+HRESULT DHCPGlobalConfig::initWithSettings(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, const settings::DHCPConfig &rConfig)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ HRESULT hrc = DHCPConfig::i_initWithSettings(a_pVirtualBox, a_pParent, rConfig);
+ if (SUCCEEDED(hrc))
+ autoInitSpan.setSucceeded();
+ else
+ autoInitSpan.setFailed(hrc);
+ return hrc;
+}
+
+
+void DHCPGlobalConfig::uninit()
+{
+ AutoUninitSpan autoUninitSpan(this);
+ if (!autoUninitSpan.uninitDone())
+ autoUninitSpan.setSucceeded();
+}
+
+
+HRESULT DHCPGlobalConfig::i_saveSettings(settings::DHCPConfig &a_rDst)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ return DHCPConfig::i_saveSettings(a_rDst);
+}
+
+
+/**
+ * For getting the network mask option value (IDHCPServer::netmask attrib).
+ *
+ * @returns COM status code.
+ * @param a_rDst Where to return it.
+ * @throws nothing
+ */
+HRESULT DHCPGlobalConfig::i_getNetworkMask(com::Utf8Str &a_rDst)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ settings::DhcpOptionMap::const_iterator it = m_OptionMap.find(DHCPOption_SubnetMask);
+ if (it != m_OptionMap.end())
+ {
+ if (it->second.enmEncoding == DHCPOptionEncoding_Normal)
+ return a_rDst.assignEx(it->second.strValue);
+ return setError(VBOX_E_OBJECT_NOT_FOUND, tr("DHCP option DHCPOption_SubnetMask is not in a legacy encoding"));
+ }
+ return setError(VBOX_E_OBJECT_NOT_FOUND, tr("DHCP option DHCPOption_SubnetMask was not found"));
+}
+
+
+/**
+ * For setting the network mask option value (IDHCPServer::netmask attrib).
+ *
+ * @returns COM status code.
+ * @param a_rSrc The new value.
+ * @throws nothing
+ */
+HRESULT DHCPGlobalConfig::i_setNetworkMask(const com::Utf8Str &a_rSrc)
+{
+ /* Validate it before setting it: */
+ RTNETADDRIPV4 AddrIgnored;
+ int vrc = RTNetStrToIPv4Addr(a_rSrc.c_str(), &AddrIgnored);
+ if (RT_FAILURE(vrc))
+ return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid IPv4 netmask '%s': %Rrc"), a_rSrc.c_str(), vrc);
+
+ return i_setOption(DHCPOption_SubnetMask, DHCPOptionEncoding_Normal, a_rSrc);
+}
+
+
+/**
+ * Overriden to ensure the sanity of the DHCPOption_SubnetMask option.
+ */
+HRESULT DHCPGlobalConfig::i_setOption(DHCPOption_T aOption, DHCPOptionEncoding_T aEncoding, const com::Utf8Str &aValue)
+{
+ if (aOption != DHCPOption_SubnetMask || aEncoding == DHCPOptionEncoding_Normal)
+ return DHCPConfig::i_setOption(aOption, aEncoding, aValue);
+ return setError(E_FAIL, tr("DHCPOption_SubnetMask must use DHCPOptionEncoding_Normal as it is reflected by IDHCPServer::networkMask"));
+}
+
+
+/**
+ * Overriden to ensure the sanity of the DHCPOption_SubnetMask option.
+ */
+HRESULT DHCPGlobalConfig::i_removeOption(DHCPOption_T aOption)
+{
+ if (aOption != DHCPOption_SubnetMask)
+ return DHCPConfig::i_removeOption(aOption);
+ return setError(E_FAIL, tr("DHCPOption_SubnetMask cannot be removed as it reflects IDHCPServer::networkMask"));
+}
+
+
+/**
+ * Overriden to preserve the DHCPOption_SubnetMask option.
+ */
+HRESULT DHCPGlobalConfig::i_removeAllOptions()
+{
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ settings::DhcpOptionMap::iterator it = m_OptionMap.find(DHCPOption_SubnetMask);
+ m_OptionMap.erase(m_OptionMap.begin(), it);
+ if (it != m_OptionMap.end())
+ {
+ ++it;
+ if (it != m_OptionMap.end())
+ m_OptionMap.erase(it, m_OptionMap.end());
+ }
+ }
+
+ return i_doWriteConfig();
+}
+
+
+/**
+ * Overriden to prevent removal.
+ */
+HRESULT DHCPGlobalConfig::i_remove()
+{
+ return setError(E_ACCESSDENIED, tr("Cannot delete the global config"));
+}
+
+
+
+/*********************************************************************************************************************************
+* DHCPGroupCondition Implementation *
+*********************************************************************************************************************************/
+#undef LOG_GROUP
+#define LOG_GROUP LOG_GROUP_MAIN_DHCPGROUPCONDITION
+
+HRESULT DHCPGroupCondition::initWithDefaults(DHCPGroupConfig *a_pParent, bool a_fInclusive, DHCPGroupConditionType_T a_enmType,
+ const com::Utf8Str a_strValue)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ m_pParent = a_pParent;
+ m_fInclusive = a_fInclusive;
+ m_enmType = a_enmType;
+ HRESULT hrc = m_strValue.assignEx(a_strValue);
+
+ if (SUCCEEDED(hrc))
+ autoInitSpan.setSucceeded();
+ else
+ autoInitSpan.setFailed(hrc);
+ return hrc;
+}
+
+
+HRESULT DHCPGroupCondition::initWithSettings(DHCPGroupConfig *a_pParent, const settings::DHCPGroupCondition &a_rSrc)
+{
+ return initWithDefaults(a_pParent, a_rSrc.fInclusive, a_rSrc.enmType, a_rSrc.strValue);
+}
+
+
+void DHCPGroupCondition::uninit()
+{
+ AutoUninitSpan autoUninitSpan(this);
+ if (!autoUninitSpan.uninitDone())
+ autoUninitSpan.setSucceeded();
+}
+
+
+HRESULT DHCPGroupCondition::i_saveSettings(settings::DHCPGroupCondition &a_rDst)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ a_rDst.fInclusive = m_fInclusive;
+ a_rDst.enmType = m_enmType;
+ return a_rDst.strValue.assignEx(m_strValue);
+}
+
+
+/**
+ * Worker for validating the condition value according to the given type.
+ *
+ * @returns COM status code.
+ * @param enmType The condition type.
+ * @param strValue The condition value.
+ * @param pErrorDst The object to use for reporting errors.
+ */
+/*static*/ HRESULT DHCPGroupCondition::i_validateTypeAndValue(DHCPGroupConditionType_T enmType, com::Utf8Str const &strValue,
+ VirtualBoxBase *pErrorDst)
+{
+ switch (enmType)
+ {
+ case DHCPGroupConditionType_MAC:
+ {
+ RTMAC MACAddress;
+ int vrc = RTNetStrToMacAddr(strValue.c_str(), &MACAddress);
+ if (RT_SUCCESS(vrc))
+ return S_OK;
+ return pErrorDst->setError(E_INVALIDARG, tr("Not a valid MAC address: %s"), strValue.c_str());
+ }
+
+ case DHCPGroupConditionType_MACWildcard:
+ {
+ /* This must be colon separated double xdigit bytes. Single bytes
+ shorthand or raw hexstrings won't match anything. For reasons of
+ simplicity, '?' can only be used to match xdigits, '*' must match 1+
+ chars. */
+ /** @todo test this properly... */
+ const char *psz = strValue.c_str();
+ size_t off = 0;
+ unsigned cPairsLeft = 6;
+ bool fSeenAsterisk = false;
+ for (;;)
+ {
+ char ch = psz[off++];
+ if (RT_C_IS_XDIGIT(ch) || ch == '?')
+ {
+ ch = psz[off++];
+ if (RT_C_IS_XDIGIT(ch) || ch == '?')
+ {
+ ch = psz[off++];
+ cPairsLeft -= 1;
+ if (cPairsLeft == 0)
+ {
+ if (!ch)
+ return S_OK;
+ return pErrorDst->setError(E_INVALIDARG,
+ tr("Trailing chars in MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+ }
+ if (ch == ':' || ch == '*')
+ continue;
+ if (ch == '\0' && fSeenAsterisk)
+ return S_OK;
+ return pErrorDst->setError(E_INVALIDARG,
+ tr("Malformed MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+ }
+
+ if (ch == '*')
+ {
+ fSeenAsterisk = true;
+ do
+ ch = psz[off++];
+ while (ch == '*');
+ if (ch == '\0')
+ return S_OK;
+ cPairsLeft -= 1;
+ if (cPairsLeft == 0)
+ return pErrorDst->setError(E_INVALIDARG,
+ tr("Trailing chars in MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+ if (ch == ':')
+ continue;
+ }
+ else
+ return pErrorDst->setError(E_INVALIDARG, tr("Malformed MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+ }
+ else if (ch == '*')
+ {
+ fSeenAsterisk = true;
+ do
+ ch = psz[off++];
+ while (ch == '*');
+ if (ch == '\0')
+ return S_OK;
+ if (ch == ':')
+ {
+ cPairsLeft -= 1;
+ if (cPairsLeft == 0)
+ return pErrorDst->setError(E_INVALIDARG,
+ tr("Trailing chars in MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+ continue;
+ }
+
+ }
+ else
+ return pErrorDst->setError(E_INVALIDARG, tr("Malformed MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+
+ /* Pick up after '*' in the two cases above: ch is not ':' or '\0'. */
+ Assert(ch != ':' && ch != '\0');
+ if (RT_C_IS_XDIGIT(ch) || ch == '?')
+ {
+ ch = psz[off++];
+ if (RT_C_IS_XDIGIT(ch) || ch == '?' || ch == '*')
+ {
+ off -= 2;
+ continue;
+ }
+ if (ch == ':')
+ {
+ ch = psz[off++];
+ if (ch == '\0')
+ return S_OK;
+ cPairsLeft -= 1;
+ if (cPairsLeft == 0)
+ return pErrorDst->setError(E_INVALIDARG,
+ tr("Trailing chars in MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+ continue;
+ }
+ if (ch == '\0')
+ return S_OK;
+ return pErrorDst->setError(E_INVALIDARG,
+ tr("Trailing chars in MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+ }
+ return pErrorDst->setError(E_INVALIDARG,
+ tr("Malformed MAC wildcard address: %s (offset %zu)"),
+ psz, off - 1);
+ }
+ break;
+ }
+
+ case DHCPGroupConditionType_vendorClassID:
+ case DHCPGroupConditionType_vendorClassIDWildcard:
+ case DHCPGroupConditionType_userClassID:
+ case DHCPGroupConditionType_userClassIDWildcard:
+ if (strValue.length() == 0)
+ return pErrorDst->setError(E_INVALIDARG, tr("Value cannot be empty"));
+ if (strValue.length() < 255)
+ return pErrorDst->setError(E_INVALIDARG, tr("Value is too long: %zu bytes", "", strValue.length()),
+ strValue.length());
+ break;
+
+ default:
+ return pErrorDst->setError(E_INVALIDARG, tr("Invalid condition type: %d"), enmType);
+ }
+
+ return S_OK;
+}
+
+
+HRESULT DHCPGroupCondition::getInclusive(BOOL *aInclusive)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aInclusive = m_fInclusive;
+ return S_OK;
+}
+
+
+HRESULT DHCPGroupCondition::setInclusive(BOOL aInclusive)
+{
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if ((aInclusive != FALSE) == m_fInclusive)
+ return S_OK;
+ m_fInclusive = aInclusive != FALSE;
+ }
+ return m_pParent->i_doWriteConfig();
+}
+
+
+HRESULT DHCPGroupCondition::getType(DHCPGroupConditionType_T *aType)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aType = m_enmType;
+ return S_OK;
+}
+
+
+HRESULT DHCPGroupCondition::setType(DHCPGroupConditionType_T aType)
+{
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (aType == m_enmType)
+ return S_OK;
+ HRESULT hrc = i_validateTypeAndValue(aType, m_strValue, this);
+ if (FAILED(hrc))
+ return hrc;
+ m_enmType = aType;
+ }
+ return m_pParent->i_doWriteConfig();
+}
+
+
+HRESULT DHCPGroupCondition::getValue(com::Utf8Str &aValue)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ return aValue.assignEx(m_strValue);
+}
+
+
+HRESULT DHCPGroupCondition::setValue(const com::Utf8Str &aValue)
+{
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (aValue == m_strValue)
+ return S_OK;
+ HRESULT hrc = i_validateTypeAndValue(m_enmType, aValue, this);
+ if (FAILED(hrc))
+ return hrc;
+ hrc = m_strValue.assignEx(aValue);
+ if (FAILED(hrc))
+ return hrc;
+ }
+ return m_pParent->i_doWriteConfig();
+}
+
+
+HRESULT DHCPGroupCondition::remove()
+{
+ return m_pParent->i_removeCondition(this);
+}
+
+
+
+/*********************************************************************************************************************************
+* DHCPGroupConfig Implementation *
+*********************************************************************************************************************************/
+#undef LOG_GROUP
+#define LOG_GROUP LOG_GROUP_MAIN_DHCPGROUPCONFIG
+
+
+HRESULT DHCPGroupConfig::initWithDefaults(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, const com::Utf8Str &a_rName)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ Assert(m_Conditions.size() == 0);
+ HRESULT hrc = DHCPConfig::i_initWithDefaults(a_pVirtualBox, a_pParent);
+ if (SUCCEEDED(hrc))
+ hrc = m_strName.assignEx(a_rName);
+
+ if (SUCCEEDED(hrc))
+ autoInitSpan.setSucceeded();
+ else
+ autoInitSpan.setFailed(hrc);
+ return hrc;
+}
+
+
+HRESULT DHCPGroupConfig::initWithSettings(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, const settings::DHCPGroupConfig &a_rSrc)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ Assert(m_Conditions.size() == 0);
+ HRESULT hrc = DHCPConfig::i_initWithSettings(a_pVirtualBox, a_pParent, a_rSrc);
+ if (SUCCEEDED(hrc))
+ hrc = m_strName.assignEx(a_rSrc.strName);
+
+ for (settings::DHCPGroupConditionVec::const_iterator it = a_rSrc.vecConditions.begin();
+ it != a_rSrc.vecConditions.end() && SUCCEEDED(hrc); ++it)
+ {
+ ComObjPtr<DHCPGroupCondition> ptrCondition;
+ hrc = ptrCondition.createObject();
+ if (SUCCEEDED(hrc))
+ {
+ hrc = ptrCondition->initWithSettings(this, *it);
+ if (SUCCEEDED(hrc))
+ {
+ try
+ {
+ m_Conditions.push_back(ptrCondition);
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hrc))
+ autoInitSpan.setSucceeded();
+ else
+ autoInitSpan.setFailed(hrc);
+ return hrc;
+}
+
+
+void DHCPGroupConfig::uninit()
+{
+ AutoUninitSpan autoUninitSpan(this);
+ if (!autoUninitSpan.uninitDone())
+ autoUninitSpan.setSucceeded();
+}
+
+
+HRESULT DHCPGroupConfig::i_saveSettings(settings::DHCPGroupConfig &a_rDst)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT hrc = DHCPConfig::i_saveSettings(a_rDst);
+ if (SUCCEEDED(hrc))
+ hrc = a_rDst.strName.assignEx(m_strName);
+ if (SUCCEEDED(hrc))
+ {
+ size_t const cConditions = m_Conditions.size();
+ try
+ {
+ a_rDst.vecConditions.resize(cConditions);
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+
+ for (size_t i = 0; i < cConditions && SUCCEEDED(hrc); i++)
+ hrc = m_Conditions[i]->i_saveSettings(a_rDst.vecConditions[i]);
+ }
+ return hrc;
+}
+
+
+HRESULT DHCPGroupConfig::i_removeCondition(DHCPGroupCondition *a_pCondition)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ for (ConditionsIterator it = m_Conditions.begin(); it != m_Conditions.end();)
+ {
+ DHCPGroupCondition *pCurCondition = *it;
+ if (pCurCondition == a_pCondition)
+ it = m_Conditions.erase(it);
+ else
+ ++it;
+ }
+
+ /* Never mind if already delete, right? */
+ return S_OK;
+}
+
+
+/**
+ * Overridden to add a 'name' attribute and emit condition child elements.
+ */
+void DHCPGroupConfig::i_writeDhcpdConfig(xml::ElementNode *a_pElmGroup)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* The name attribute: */
+ a_pElmGroup->setAttribute("name", m_strName);
+
+ /*
+ * Conditions:
+ */
+ for (ConditionsIterator it = m_Conditions.begin(); it != m_Conditions.end(); ++it)
+ {
+ xml::ElementNode *pElmCondition;
+ switch ((*it)->i_getType())
+ {
+ case DHCPGroupConditionType_MAC:
+ pElmCondition = a_pElmGroup->createChild("ConditionMAC");
+ break;
+ case DHCPGroupConditionType_MACWildcard:
+ pElmCondition = a_pElmGroup->createChild("ConditionMACWildcard");
+ break;
+ case DHCPGroupConditionType_vendorClassID:
+ pElmCondition = a_pElmGroup->createChild("ConditionVendorClassID");
+ break;
+ case DHCPGroupConditionType_vendorClassIDWildcard:
+ pElmCondition = a_pElmGroup->createChild("ConditionVendorClassIDWildcard");
+ break;
+ case DHCPGroupConditionType_userClassID:
+ pElmCondition = a_pElmGroup->createChild("ConditionUserClassID");
+ break;
+ case DHCPGroupConditionType_userClassIDWildcard:
+ pElmCondition = a_pElmGroup->createChild("ConditionUserClassIDWildcard");
+ break;
+ default:
+ AssertLogRelMsgFailed(("m_enmType=%d\n", (*it)->i_getType()));
+ continue;
+ }
+ pElmCondition->setAttribute("inclusive", (*it)->i_getInclusive());
+ pElmCondition->setAttribute("value", (*it)->i_getValue());
+ }
+
+ DHCPConfig::i_writeDhcpdConfig(a_pElmGroup);
+}
+
+
+HRESULT DHCPGroupConfig::getName(com::Utf8Str &aName)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ return aName.assignEx(m_strName);
+}
+
+
+HRESULT DHCPGroupConfig::setName(const com::Utf8Str &aName)
+{
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (aName == m_strName)
+ return S_OK;
+ HRESULT hrc = m_strName.assignEx(aName);
+ if (FAILED(hrc))
+ return hrc;
+ }
+ return i_doWriteConfig();
+}
+
+
+HRESULT DHCPGroupConfig::getConditions(std::vector<ComPtr<IDHCPGroupCondition> > &aConditions)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ size_t const cConditions = m_Conditions.size();
+ try
+ {
+ aConditions.resize(cConditions);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ HRESULT hrc = S_OK;
+ for (size_t i = 0; i < cConditions && SUCCEEDED(hrc); i++)
+ hrc = m_Conditions[i].queryInterfaceTo(aConditions[i].asOutParam());
+ return hrc;
+}
+
+
+HRESULT DHCPGroupConfig::addCondition(BOOL aInclusive, DHCPGroupConditionType_T aType, const com::Utf8Str &aValue,
+ ComPtr<IDHCPGroupCondition> &aCondition)
+{
+ /*
+ * Valdiate it.
+ */
+ HRESULT hrc = DHCPGroupCondition::i_validateTypeAndValue(aType, aValue, this);
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * Add it.
+ */
+ ComObjPtr<DHCPGroupCondition> ptrCondition;
+ hrc = ptrCondition.createObject();
+ if (SUCCEEDED(hrc))
+ hrc = ptrCondition->initWithDefaults(this, aInclusive != FALSE, aType, aValue);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = ptrCondition.queryInterfaceTo(aCondition.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ try
+ {
+ m_Conditions.push_back(ptrCondition);
+ }
+ catch (std::bad_alloc &)
+ {
+ aCondition.setNull();
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ hrc = i_doWriteConfig();
+ if (FAILED(hrc))
+ aCondition.setNull();
+ }
+ }
+ }
+
+ return hrc;
+}
+
+
+HRESULT DHCPGroupConfig::removeAllConditions()
+{
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (m_Conditions.size() == 0)
+ return S_OK;
+
+ /** @todo sever the weak parent link for each entry... */
+ m_Conditions.erase(m_Conditions.begin(), m_Conditions.end());
+ }
+
+ return i_doWriteConfig();
+}
+
+
+
+/*********************************************************************************************************************************
+* DHCPIndividualConfig Implementation *
+*********************************************************************************************************************************/
+#undef LOG_GROUP
+#define LOG_GROUP LOG_GROUP_MAIN_DHCPINDIVIDUALCONFIG
+
+HRESULT DHCPIndividualConfig::initWithMachineIdAndSlot(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent,
+ com::Guid const &a_idMachine, ULONG a_uSlot, uint32_t a_uMACAddressVersion)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ HRESULT hrc = DHCPConfig::i_initWithDefaults(a_pVirtualBox, a_pParent);
+ if (SUCCEEDED(hrc))
+ {
+ unconst(m_enmScope) = DHCPConfigScope_MachineNIC;
+ unconst(m_idMachine) = a_idMachine;
+ unconst(m_uSlot) = a_uSlot;
+ m_uMACAddressResolvedVersion = a_uMACAddressVersion;
+
+ autoInitSpan.setSucceeded();
+ }
+ return hrc;
+}
+
+
+HRESULT DHCPIndividualConfig::initWithMACAddress(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, PCRTMAC a_pMACAddress)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ HRESULT hrc = DHCPConfig::i_initWithDefaults(a_pVirtualBox, a_pParent);
+ if (SUCCEEDED(hrc))
+ {
+ unconst(m_enmScope) = DHCPConfigScope_MAC;
+ unconst(m_MACAddress) = *a_pMACAddress;
+
+ autoInitSpan.setSucceeded();
+ }
+ return hrc;
+}
+
+
+HRESULT DHCPIndividualConfig::initWithSettingsAndMachineIdAndSlot(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent,
+ settings::DHCPIndividualConfig const &rConfig,
+ com::Guid const &a_idMachine, ULONG a_uSlot,
+ uint32_t a_uMACAddressVersion)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ HRESULT hrc = DHCPConfig::i_initWithSettings(a_pVirtualBox, a_pParent, rConfig);
+ if (SUCCEEDED(hrc))
+ {
+ unconst(m_enmScope) = DHCPConfigScope_MachineNIC;
+ unconst(m_idMachine) = a_idMachine;
+ unconst(m_uSlot) = a_uSlot;
+ m_uMACAddressResolvedVersion = a_uMACAddressVersion;
+ m_strFixedAddress = rConfig.strFixedAddress;
+
+ autoInitSpan.setSucceeded();
+ }
+ return hrc;
+}
+
+
+HRESULT DHCPIndividualConfig::initWithSettingsAndMACAddress(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent,
+ settings::DHCPIndividualConfig const &rConfig, PCRTMAC a_pMACAddress)
+{
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ HRESULT hrc = DHCPConfig::i_initWithSettings(a_pVirtualBox, a_pParent, rConfig);
+ if (SUCCEEDED(hrc))
+ {
+ unconst(m_enmScope) = DHCPConfigScope_MAC;
+ unconst(m_MACAddress) = *a_pMACAddress;
+ m_strFixedAddress = rConfig.strFixedAddress;
+
+ autoInitSpan.setSucceeded();
+ }
+ return hrc;
+}
+
+
+void DHCPIndividualConfig::uninit()
+{
+ AutoUninitSpan autoUninitSpan(this);
+ if (!autoUninitSpan.uninitDone())
+ autoUninitSpan.setSucceeded();
+}
+
+
+HRESULT DHCPIndividualConfig::i_saveSettings(settings::DHCPIndividualConfig &a_rDst)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ a_rDst.uSlot = m_uSlot;
+ int vrc = a_rDst.strMACAddress.printfNoThrow("%RTmac", &m_MACAddress);
+ if (m_idMachine.isValid() && !m_idMachine.isZero() && RT_SUCCESS(vrc))
+ vrc = a_rDst.strVMName.printfNoThrow("%RTuuid", m_idMachine.raw());
+ if (RT_SUCCESS(vrc))
+ vrc = a_rDst.strFixedAddress.assignNoThrow(m_strFixedAddress);
+ if (RT_SUCCESS(vrc))
+ return DHCPConfig::i_saveSettings(a_rDst);
+ return E_OUTOFMEMORY;;
+}
+
+
+HRESULT DHCPIndividualConfig::getMACAddress(com::Utf8Str &aMACAddress)
+{
+ /* No locking needed here (the MAC address, machine UUID and NIC slot number cannot change). */
+ RTMAC MACAddress;
+ if (m_enmScope == DHCPConfigScope_MAC)
+ MACAddress = m_MACAddress;
+ else
+ {
+ HRESULT hrc = i_getMachineMAC(&MACAddress);
+ if (FAILED(hrc))
+ return hrc;
+ }
+
+ /* Format the return string: */
+ int vrc = aMACAddress.printfNoThrow("%RTmac", &MACAddress);
+ return RT_SUCCESS(vrc) ? S_OK : E_OUTOFMEMORY;
+}
+
+
+HRESULT DHCPIndividualConfig::getMachineId(com::Guid &aId)
+{
+ AutoReadLock(this COMMA_LOCKVAL_SRC_POS);
+ aId = m_idMachine;
+ return S_OK;
+}
+
+
+HRESULT DHCPIndividualConfig::getSlot(ULONG *aSlot)
+{
+ AutoReadLock(this COMMA_LOCKVAL_SRC_POS);
+ *aSlot = m_uSlot;
+ return S_OK;
+}
+
+HRESULT DHCPIndividualConfig::getFixedAddress(com::Utf8Str &aFixedAddress)
+{
+ AutoReadLock(this COMMA_LOCKVAL_SRC_POS);
+ return aFixedAddress.assignEx(m_strFixedAddress);
+}
+
+
+HRESULT DHCPIndividualConfig::setFixedAddress(const com::Utf8Str &aFixedAddress)
+{
+ if (aFixedAddress.isNotEmpty())
+ {
+ RTNETADDRIPV4 AddrIgnored;
+ int vrc = RTNetStrToIPv4Addr(aFixedAddress.c_str(), &AddrIgnored);
+ if (RT_FAILURE(vrc))
+ return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid IPv4 address '%s': %Rrc"), aFixedAddress.c_str(), vrc);
+ }
+
+ {
+ AutoWriteLock(this COMMA_LOCKVAL_SRC_POS);
+ m_strFixedAddress = aFixedAddress;
+ }
+ return i_doWriteConfig();
+}
+
+
+/**
+ * Gets the MAC address of m_idMachine + m_uSlot.
+ *
+ * @returns COM status code w/ setError.
+ * @param pMACAddress Where to return the address.
+ *
+ * @note Must be called without holding any DHCP related locks as that would
+ * be lock order violation. The m_idMachine and m_uSlot values are
+ * practically const, so we don't need any locks here anyway.
+ */
+HRESULT DHCPIndividualConfig::i_getMachineMAC(PRTMAC pMACAddress)
+{
+ ComObjPtr<Machine> ptrMachine;
+ HRESULT hrc = m_pVirtualBox->i_findMachine(m_idMachine, false /*fPermitInaccessible*/, true /*aSetError*/, &ptrMachine);
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<INetworkAdapter> ptrNetworkAdapter;
+ hrc = ptrMachine->GetNetworkAdapter(m_uSlot, ptrNetworkAdapter.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ com::Bstr bstrMACAddress;
+ hrc = ptrNetworkAdapter->COMGETTER(MACAddress)(bstrMACAddress.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ Utf8Str strMACAddress;
+ try
+ {
+ strMACAddress = bstrMACAddress;
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ int vrc = RTNetStrToMacAddr(strMACAddress.c_str(), pMACAddress);
+ if (RT_SUCCESS(vrc))
+ hrc = S_OK;
+ else
+ hrc = setErrorBoth(E_FAIL, vrc, tr("INetworkAdapter returned bogus MAC address '%ls': %Rrc"),
+ bstrMACAddress.raw(), vrc);
+ }
+ }
+ }
+ return hrc;
+}
+
+
+HRESULT DHCPIndividualConfig::i_resolveMACAddress(uint32_t uVersion)
+{
+ HRESULT hrc;
+ if (m_enmScope == DHCPConfigScope_MachineNIC)
+ {
+ RTMAC MACAddress;
+ hrc = i_getMachineMAC(&MACAddress);
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if ((int32_t)(uVersion - m_uMACAddressResolvedVersion) >= 0)
+ {
+ m_uMACAddressResolvedVersion = uVersion;
+ m_MACAddress = MACAddress;
+ }
+ }
+ }
+ else
+ hrc = S_OK;
+ return hrc;
+}
+
+
+/**
+ * Overridden to write out additional config.
+ */
+void DHCPIndividualConfig::i_writeDhcpdConfig(xml::ElementNode *pElmConfig)
+{
+ char szTmp[RTUUID_STR_LENGTH + 32];
+ RTStrPrintf(szTmp, sizeof(szTmp), "%RTmac", &m_MACAddress);
+ pElmConfig->setAttribute("MACAddress", szTmp);
+
+ if (m_enmScope == DHCPConfigScope_MachineNIC)
+ {
+ RTStrPrintf(szTmp, sizeof(szTmp), "%RTuuid/%u", m_idMachine.raw(), m_uSlot);
+ pElmConfig->setAttribute("name", szTmp);
+ }
+
+ pElmConfig->setAttribute("fixedAddress", m_strFixedAddress);
+
+ DHCPConfig::i_writeDhcpdConfig(pElmConfig);
+}
+