summaryrefslogtreecommitdiffstats
path: root/src/VBox/NetworkServices/DHCP
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/NetworkServices/DHCP')
-rw-r--r--src/VBox/NetworkServices/DHCP/ClientDataInt.h70
-rw-r--r--src/VBox/NetworkServices/DHCP/Config.cpp1493
-rw-r--r--src/VBox/NetworkServices/DHCP/Config.h845
-rw-r--r--src/VBox/NetworkServices/DHCP/Makefile.kmk72
-rw-r--r--src/VBox/NetworkServices/DHCP/NetworkManagerDhcp.cpp189
-rw-r--r--src/VBox/NetworkServices/DHCP/README.customoptions23
-rw-r--r--src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp885
-rw-r--r--src/VBox/NetworkServices/DHCP/VBoxNetDHCP.rc55
-rw-r--r--src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp25
9 files changed, 3657 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/DHCP/ClientDataInt.h b/src/VBox/NetworkServices/DHCP/ClientDataInt.h
new file mode 100644
index 00000000..74879523
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/ClientDataInt.h
@@ -0,0 +1,70 @@
+/* $Id: ClientDataInt.h $ */
+/** @file
+ * Config.h
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_DHCP_ClientDataInt_h
+#define VBOX_INCLUDED_SRC_DHCP_ClientDataInt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+class ClientData
+{
+public:
+ ClientData()
+ {
+ m_address.u = 0;
+ m_network.u = 0;
+ fHasLease = false;
+ fHasClient = false;
+ fBinding = true;
+ u64TimestampBindingStarted = 0;
+ u64TimestampLeasingStarted = 0;
+ u32LeaseExpirationPeriod = 0;
+ u32BindExpirationPeriod = 0;
+ pCfg = NULL;
+
+ }
+ ~ClientData(){}
+
+ /* client information */
+ RTNETADDRIPV4 m_address;
+ RTNETADDRIPV4 m_network;
+ RTMAC m_mac;
+
+ bool fHasClient;
+
+ /* Lease part */
+ bool fHasLease;
+ /** lease isn't commited */
+ bool fBinding;
+
+ /** Timestamp when lease commited. */
+ uint64_t u64TimestampLeasingStarted;
+ /** Period when lease is expired in secs. */
+ uint32_t u32LeaseExpirationPeriod;
+
+ /** timestamp when lease was bound */
+ uint64_t u64TimestampBindingStarted;
+ /* Period when binding is expired in secs. */
+ uint32_t u32BindExpirationPeriod;
+
+ MapOptionId2RawOption options;
+
+ NetworkConfigEntity *pCfg;
+};
+
+#endif /* !VBOX_INCLUDED_SRC_DHCP_ClientDataInt_h */
diff --git a/src/VBox/NetworkServices/DHCP/Config.cpp b/src/VBox/NetworkServices/DHCP/Config.cpp
new file mode 100644
index 00000000..b1d09c78
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/Config.cpp
@@ -0,0 +1,1493 @@
+/* $Id: Config.cpp $ */
+/** @file
+ * Configuration for DHCP.
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/**
+ * XXX: license.
+ */
+
+#include <iprt/asm.h>
+#include <iprt/getopt.h>
+#include <iprt/net.h>
+#include <iprt/time.h>
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/version.h>
+
+#include <VBox/com/array.h>
+#include <VBox/com/string.h>
+
+#include <iprt/cpp/xml.h>
+
+#define BASE_SERVICES_ONLY
+#include "../NetLib/VBoxNetBaseService.h"
+#include "../NetLib/VBoxNetLib.h"
+#include "../NetLib/shared_ptr.h"
+
+#include <list>
+#include <vector>
+#include <map>
+#include <string>
+
+#include "Config.h"
+#include "ClientDataInt.h"
+
+bool operator== (const Lease& lhs, const Lease& rhs)
+{
+ return (lhs.m.get() == rhs.m.get());
+}
+
+
+bool operator!= (const Lease& lhs, const Lease& rhs)
+{
+ return !(lhs == rhs);
+}
+
+
+bool operator< (const Lease& lhs, const Lease& rhs)
+{
+ return ( (lhs.getAddress() < rhs.getAddress())
+ || (lhs.issued() < rhs.issued()));
+}
+/* consts */
+
+const NullConfigEntity *g_NullConfig = new NullConfigEntity();
+RootConfigEntity *g_RootConfig = new RootConfigEntity(std::string("ROOT"), 1200 /* 20 min. */);
+const ClientMatchCriteria *g_AnyClient = new AnyClientMatchCriteria();
+
+static ConfigurationManager *g_ConfigurationManager = ConfigurationManager::getConfigurationManager();
+
+NetworkManager *NetworkManager::g_NetworkManager;
+
+bool MACClientMatchCriteria::check(const Client& client) const
+{
+ return (client == m_mac);
+}
+
+
+int BaseConfigEntity::match(Client& client, BaseConfigEntity **cfg)
+{
+ int iMatch = (m_criteria && m_criteria->check(client) ? m_MatchLevel : 0);
+ if (m_children.empty())
+ {
+ if (iMatch > 0)
+ {
+ *cfg = this;
+ return iMatch;
+ }
+ }
+ else
+ {
+ *cfg = this;
+ /* XXX: hack */
+ BaseConfigEntity *matching = this;
+ int matchingLevel = m_MatchLevel;
+
+ for (std::vector<BaseConfigEntity *>::iterator it = m_children.begin();
+ it != m_children.end();
+ ++it)
+ {
+ iMatch = (*it)->match(client, &matching);
+ if (iMatch > matchingLevel)
+ {
+ *cfg = matching;
+ matchingLevel = iMatch;
+ }
+ }
+ return matchingLevel;
+ }
+ return iMatch;
+}
+
+/* Client */
+/* Configs
+ NetworkConfigEntity(std::string name,
+ ConfigEntity* pCfg,
+ ClientMatchCriteria* criteria,
+ RTNETADDRIPV4& networkID,
+ RTNETADDRIPV4& networkMask)
+*/
+static const RTNETADDRIPV4 g_AnyIpv4 = {0};
+static const RTNETADDRIPV4 g_AllIpv4 = {0xffffffff};
+RootConfigEntity::RootConfigEntity(std::string name, uint32_t expPeriod):
+ NetworkConfigEntity(name, g_NullConfig, g_AnyClient, g_AnyIpv4, g_AllIpv4)
+{
+ m_MatchLevel = 2;
+ m_u32ExpirationPeriod = expPeriod;
+}
+
+/* Configuration Manager */
+struct ConfigurationManager::Data
+{
+ Data():fFileExists(false){}
+
+ MapLease2Ip4Address m_allocations;
+ Ipv4AddressContainer m_nameservers;
+ Ipv4AddressContainer m_routers;
+
+ std::string m_domainName;
+ VecClient m_clients;
+ com::Utf8Str m_leaseStorageFilename;
+ bool fFileExists;
+};
+
+ConfigurationManager *ConfigurationManager::getConfigurationManager()
+{
+ if (!g_ConfigurationManager)
+
+
+ {
+ g_ConfigurationManager = new ConfigurationManager();
+ g_ConfigurationManager->init();
+ }
+
+ return g_ConfigurationManager;
+}
+
+
+const std::string tagXMLLeases = "Leases";
+const std::string tagXMLLeasesAttributeVersion = "version";
+const std::string tagXMLLeasesVersion_1_0 = "1.0";
+const std::string tagXMLLease = "Lease";
+const std::string tagXMLLeaseAttributeMac = "mac";
+const std::string tagXMLLeaseAttributeNetwork = "network";
+const std::string tagXMLLeaseAddress = "Address";
+const std::string tagXMLAddressAttributeValue = "value";
+const std::string tagXMLLeaseTime = "Time";
+const std::string tagXMLTimeAttributeIssued = "issued";
+const std::string tagXMLTimeAttributeExpiration = "expiration";
+const std::string tagXMLLeaseOptions = "Options";
+
+/**
+ * @verbatim
+ <Leases version="1.0">
+ <Lease mac="" network=""/>
+ <Address value=""/>
+ <Time issued="" expiration=""/>
+ <options>
+ <option name="" type=""/>
+ </option>
+ </options>
+ </Lease>
+ </Leases>
+ @endverbatim
+ */
+int ConfigurationManager::loadFromFile(const com::Utf8Str& leaseStorageFileName)
+{
+ m->m_leaseStorageFilename = leaseStorageFileName;
+
+ xml::XmlFileParser parser;
+ xml::Document doc;
+
+ try {
+ parser.read(m->m_leaseStorageFilename.c_str(), doc);
+ }
+ catch (...)
+ {
+ return VINF_SUCCESS;
+ }
+
+ /* XML parsing */
+ xml::ElementNode *root = doc.getRootElement();
+
+ if (!root || !root->nameEquals(tagXMLLeases.c_str()))
+ {
+ m->fFileExists = false;
+ return VERR_NOT_FOUND;
+ }
+
+ com::Utf8Str version;
+ if (root)
+ root->getAttributeValue(tagXMLLeasesAttributeVersion.c_str(), version);
+
+ /* XXX: version check */
+ xml::NodesLoop leases(*root);
+
+ const xml::ElementNode *lease;
+ while ((lease = leases.forAllNodes()))
+ {
+ if (!lease->nameEquals(tagXMLLease.c_str()))
+ continue;
+
+ ClientData *data = new ClientData();
+ Lease l(data);
+ if (l.fromXML(lease))
+ {
+
+ m->m_allocations.insert(MapLease2Ip4AddressPair(l, l.getAddress()));
+
+
+ NetworkConfigEntity *pNetCfg = NULL;
+ Client c(data);
+ int rc = g_RootConfig->match(c, (BaseConfigEntity **)&pNetCfg);
+ Assert(rc >= 0 && pNetCfg); RT_NOREF(rc);
+
+ l.setConfig(pNetCfg);
+
+ m->m_clients.push_back(c);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int ConfigurationManager::saveToFile()
+{
+ if (m->m_leaseStorageFilename.isEmpty())
+ return VINF_SUCCESS;
+
+ xml::Document doc;
+
+ xml::ElementNode *root = doc.createRootElement(tagXMLLeases.c_str());
+ if (!root)
+ return VERR_INTERNAL_ERROR;
+
+ root->setAttribute(tagXMLLeasesAttributeVersion.c_str(), tagXMLLeasesVersion_1_0.c_str());
+
+ for(MapLease2Ip4AddressConstIterator it = m->m_allocations.begin();
+ it != m->m_allocations.end(); ++it)
+ {
+ xml::ElementNode *lease = root->createChild(tagXMLLease.c_str());
+ if (!it->first.toXML(lease))
+ {
+ /* XXX: todo logging + error handling */
+ }
+ }
+
+ try {
+ xml::XmlFileWriter writer(doc);
+ writer.write(m->m_leaseStorageFilename.c_str(), true);
+ } catch(...){}
+
+ return VINF_SUCCESS;
+}
+
+
+int ConfigurationManager::extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt)
+{
+ return ConfigurationManager::findOption(RTNET_DHCP_OPT_PARAM_REQ_LIST, pDhcpMsg, cbDhcpMsg, rawOpt);
+}
+
+
+Client ConfigurationManager::getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg)
+{
+
+ VecClientIterator it;
+ bool fDhcpValid = false;
+ uint8_t uMsgType = 0;
+
+ fDhcpValid = RTNetIPv4IsDHCPValid(NULL, pDhcpMsg, cbDhcpMsg, &uMsgType);
+ AssertReturn(fDhcpValid, Client::NullClient);
+
+ LogFlowFunc(("dhcp:mac:%RTmac\n", &pDhcpMsg->bp_chaddr.Mac));
+ /* 1st. client IDs */
+ for ( it = m->m_clients.begin();
+ it != m->m_clients.end();
+ ++it)
+ {
+ if ((*it) == pDhcpMsg->bp_chaddr.Mac)
+ {
+ LogFlowFunc(("client:mac:%RTmac\n", it->getMacAddress()));
+ /* check timestamp that request wasn't expired. */
+ return (*it);
+ }
+ }
+
+ if (it == m->m_clients.end())
+ {
+ /* We hasn't got any session for this client */
+ Client c;
+ c.initWithMac(pDhcpMsg->bp_chaddr.Mac);
+ m->m_clients.push_back(c);
+ return m->m_clients.back();
+ }
+
+ return Client::NullClient;
+}
+
+/**
+ * Finds an option.
+ *
+ * @returns On success, a pointer to the first byte in the option data (no none
+ * then it'll be the byte following the 0 size field) and *pcbOpt set
+ * to the option length.
+ * On failure, NULL is returned and *pcbOpt unchanged.
+ *
+ * @param uOption The option to search for.
+ * @param pDhcpMsg The DHCP message.
+ * that this is adjusted if the option length is larger
+ * than the message buffer.
+ * @param cbDhcpMsg Size of the DHCP message.
+ * @param opt The actual option we found.
+ */
+int
+ConfigurationManager::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt)
+{
+ Assert(uOption != RTNET_DHCP_OPT_PAD);
+ Assert(uOption != RTNET_DHCP_OPT_END);
+
+ /*
+ * Validate the DHCP bits and figure the max size of the options in the vendor field.
+ */
+ if (cbDhcpMsg <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
+ return VERR_INVALID_PARAMETER;
+
+ if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
+ return VERR_INVALID_PARAMETER;
+
+ size_t cbLeft = cbDhcpMsg - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
+ if (cbLeft > RTNET_DHCP_OPT_SIZE)
+ cbLeft = RTNET_DHCP_OPT_SIZE;
+
+ /*
+ * Search the vendor field.
+ */
+ uint8_t const *pb = &pDhcpMsg->bp_vend.Dhcp.dhcp_opts[0];
+ while (pb && cbLeft > 0)
+ {
+ uint8_t uCur = *pb;
+ if (uCur == RTNET_DHCP_OPT_PAD)
+ {
+ cbLeft--;
+ pb++;
+ }
+ else if (uCur == RTNET_DHCP_OPT_END)
+ break;
+ else if (cbLeft <= 1)
+ break;
+ else
+ {
+ uint8_t cbCur = pb[1];
+ if (cbCur > cbLeft - 2)
+ cbCur = (uint8_t)(cbLeft - 2);
+ if (uCur == uOption)
+ {
+ opt.u8OptId = uCur;
+ memcpy(opt.au8RawOpt, pb+2, cbCur);
+ opt.cbRawOpt = cbCur;
+ return VINF_SUCCESS;
+ }
+ pb += cbCur + 2;
+ cbLeft -= cbCur + 2;
+ }
+ }
+
+ /** @todo search extended dhcp option field(s) when present */
+
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * We bind lease for client till it continue with it on DHCPREQUEST.
+ */
+Lease ConfigurationManager::allocateLease4Client(const Client& client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg)
+{
+ {
+ /**
+ * This mean that client has already bound or commited lease.
+ * If we've it happens it means that we received DHCPDISCOVER twice.
+ */
+ const Lease l = client.lease();
+ if (l != Lease::NullLease)
+ {
+ /* Here we should take lease from the m_allocation which was feed with leases
+ * on start
+ */
+ if (l.isExpired())
+ {
+ expireLease4Client(const_cast<Client&>(client));
+ if (!l.isExpired())
+ return l;
+ }
+ else
+ {
+ AssertReturn(l.getAddress().u != 0, Lease::NullLease);
+ return l;
+ }
+ }
+ }
+
+ RTNETADDRIPV4 hintAddress;
+ RawOption opt;
+ NetworkConfigEntity *pNetCfg;
+
+ Client cl(client);
+ AssertReturn(g_RootConfig->match(cl, (BaseConfigEntity **)&pNetCfg) > 0, Lease::NullLease);
+
+ /* DHCPDISCOVER MAY contain request address */
+ hintAddress.u = 0;
+ int rc = findOption(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cbDhcpMsg, opt);
+ if (RT_SUCCESS(rc))
+ {
+ hintAddress.u = *(uint32_t *)opt.au8RawOpt;
+ if ( RT_H2N_U32(hintAddress.u) < RT_H2N_U32(pNetCfg->lowerIp().u)
+ || RT_H2N_U32(hintAddress.u) > RT_H2N_U32(pNetCfg->upperIp().u))
+ hintAddress.u = 0; /* clear hint */
+ }
+
+ if ( hintAddress.u
+ && !isAddressTaken(hintAddress))
+ {
+ Lease l(cl);
+ l.setConfig(pNetCfg);
+ l.setAddress(hintAddress);
+ m->m_allocations.insert(MapLease2Ip4AddressPair(l, hintAddress));
+ return l;
+ }
+
+ uint32_t u32 = 0;
+ for(u32 = RT_H2N_U32(pNetCfg->lowerIp().u);
+ u32 <= RT_H2N_U32(pNetCfg->upperIp().u);
+ ++u32)
+ {
+ RTNETADDRIPV4 address;
+ address.u = RT_H2N_U32(u32);
+ if (!isAddressTaken(address))
+ {
+ Lease l(cl);
+ l.setConfig(pNetCfg);
+ l.setAddress(address);
+ m->m_allocations.insert(MapLease2Ip4AddressPair(l, address));
+ return l;
+ }
+ }
+
+ return Lease::NullLease;
+}
+
+
+int ConfigurationManager::commitLease4Client(Client& client)
+{
+ Lease l = client.lease();
+ AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
+
+ l.bindingPhase(false);
+ const NetworkConfigEntity *pCfg = l.getConfig();
+
+ AssertPtr(pCfg);
+ l.setExpiration(pCfg->expirationPeriod());
+ l.phaseStart(RTTimeMilliTS());
+
+ saveToFile();
+
+ return VINF_SUCCESS;
+}
+
+
+int ConfigurationManager::expireLease4Client(Client& client)
+{
+ Lease l = client.lease();
+ AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
+
+ if (l.isInBindingPhase())
+ {
+
+ MapLease2Ip4AddressIterator it = m->m_allocations.find(l);
+ AssertReturn(it != m->m_allocations.end(), VERR_NOT_FOUND);
+
+ /*
+ * XXX: perhaps it better to keep this allocation ????
+ */
+ m->m_allocations.erase(it);
+
+ l.expire();
+ return VINF_SUCCESS;
+ }
+
+ l = Lease(client); /* re-new */
+ return VINF_SUCCESS;
+}
+
+
+bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr, Lease& lease)
+{
+ MapLease2Ip4AddressIterator it;
+
+ for (it = m->m_allocations.begin();
+ it != m->m_allocations.end();
+ ++it)
+ {
+ if (it->second.u == addr.u)
+ {
+ if (lease != Lease::NullLease)
+ lease = it->first;
+
+ return true;
+ }
+ }
+ lease = Lease::NullLease;
+ return false;
+}
+
+
+bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr)
+{
+ Lease ignore;
+ return isAddressTaken(addr, ignore);
+}
+
+
+NetworkConfigEntity *ConfigurationManager::addNetwork(NetworkConfigEntity *,
+ const RTNETADDRIPV4& networkId,
+ const RTNETADDRIPV4& netmask,
+ RTNETADDRIPV4& LowerAddress,
+ RTNETADDRIPV4& UpperAddress)
+{
+ static int id;
+ char name[64];
+
+ RTStrPrintf(name, RT_ELEMENTS(name), "network-%d", id);
+ std::string strname(name);
+ id++;
+
+
+ if (!LowerAddress.u)
+ LowerAddress = networkId;
+
+ if (!UpperAddress.u)
+ UpperAddress.u = networkId.u | (~netmask.u);
+
+ return new NetworkConfigEntity(strname,
+ g_RootConfig,
+ g_AnyClient,
+ 5,
+ networkId,
+ netmask,
+ LowerAddress,
+ UpperAddress);
+}
+
+HostConfigEntity *ConfigurationManager::addHost(NetworkConfigEntity* pCfg,
+ const RTNETADDRIPV4& address,
+ ClientMatchCriteria *criteria)
+{
+ static int id;
+ char name[64];
+
+ RTStrPrintf(name, RT_ELEMENTS(name), "host-%d", id);
+ std::string strname(name);
+ id++;
+
+ return new HostConfigEntity(address, strname, pCfg, criteria);
+}
+
+int ConfigurationManager::addToAddressList(uint8_t u8OptId, RTNETADDRIPV4& address)
+{
+ switch(u8OptId)
+ {
+ case RTNET_DHCP_OPT_DNS:
+ m->m_nameservers.push_back(address);
+ break;
+ case RTNET_DHCP_OPT_ROUTERS:
+ m->m_routers.push_back(address);
+ break;
+ default:
+ Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
+ }
+ return VINF_SUCCESS;
+}
+
+
+int ConfigurationManager::flushAddressList(uint8_t u8OptId)
+{
+ switch(u8OptId)
+ {
+ case RTNET_DHCP_OPT_DNS:
+ m->m_nameservers.clear();
+ break;
+ case RTNET_DHCP_OPT_ROUTERS:
+ m->m_routers.clear();
+ break;
+ default:
+ Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
+ }
+ return VINF_SUCCESS;
+}
+
+
+const Ipv4AddressContainer& ConfigurationManager::getAddressList(uint8_t u8OptId)
+{
+ switch(u8OptId)
+ {
+ case RTNET_DHCP_OPT_DNS:
+ return m->m_nameservers;
+
+ case RTNET_DHCP_OPT_ROUTERS:
+ return m->m_routers;
+
+ }
+ /* XXX: Grrr !!! */
+ return m_empty;
+}
+
+
+int ConfigurationManager::setString(uint8_t u8OptId, const std::string& str)
+{
+ switch (u8OptId)
+ {
+ case RTNET_DHCP_OPT_DOMAIN_NAME:
+ m->m_domainName = str;
+ break;
+ default:
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+const std::string &ConfigurationManager::getString(uint8_t u8OptId)
+{
+ switch (u8OptId)
+ {
+ case RTNET_DHCP_OPT_DOMAIN_NAME:
+ if (m->m_domainName.length())
+ return m->m_domainName;
+ return m_noString;
+ default:
+ break;
+ }
+
+ return m_noString;
+}
+
+
+void ConfigurationManager::init()
+{
+ m = new ConfigurationManager::Data();
+}
+
+
+ConfigurationManager::~ConfigurationManager() { if (m) delete m; }
+
+/**
+ * Network manager
+ */
+struct NetworkManager::Data
+{
+ Data()
+ {
+ RT_ZERO(BootPReplyMsg);
+ cbBooPReplyMsg = 0;
+
+ m_OurAddress.u = 0;
+ m_OurNetmask.u = 0;
+ RT_ZERO(m_OurMac);
+ }
+
+ union {
+ RTNETBOOTP BootPHeader;
+ uint8_t au8Storage[1024];
+ } BootPReplyMsg;
+ int cbBooPReplyMsg;
+
+ RTNETADDRIPV4 m_OurAddress;
+ RTNETADDRIPV4 m_OurNetmask;
+ RTMAC m_OurMac;
+
+ ComPtr<IDHCPServer> m_DhcpServer;
+ const VBoxNetHlpUDPService *m_service;
+};
+
+
+NetworkManager::NetworkManager():m(NULL)
+{
+ m = new NetworkManager::Data();
+}
+
+
+NetworkManager::~NetworkManager()
+{
+ delete m;
+ m = NULL;
+}
+
+
+NetworkManager *NetworkManager::getNetworkManager(ComPtr<IDHCPServer> aDhcpServer)
+{
+ if (!g_NetworkManager)
+ {
+ g_NetworkManager = new NetworkManager();
+ g_NetworkManager->m->m_DhcpServer = aDhcpServer;
+ }
+
+ return g_NetworkManager;
+}
+
+
+const RTNETADDRIPV4& NetworkManager::getOurAddress() const
+{
+ return m->m_OurAddress;
+}
+
+
+const RTNETADDRIPV4& NetworkManager::getOurNetmask() const
+{
+ return m->m_OurNetmask;
+}
+
+
+const RTMAC& NetworkManager::getOurMac() const
+{
+ return m->m_OurMac;
+}
+
+
+void NetworkManager::setOurAddress(const RTNETADDRIPV4& aAddress)
+{
+ m->m_OurAddress = aAddress;
+}
+
+
+void NetworkManager::setOurNetmask(const RTNETADDRIPV4& aNetmask)
+{
+ m->m_OurNetmask = aNetmask;
+}
+
+
+void NetworkManager::setOurMac(const RTMAC& aMac)
+{
+ m->m_OurMac = aMac;
+}
+
+
+void NetworkManager::setService(const VBoxNetHlpUDPService *srv)
+{
+ m->m_service = srv;
+}
+
+/**
+ * Network manager creates DHCPOFFER datagramm
+ */
+int NetworkManager::offer4Client(const Client& client, uint32_t u32Xid,
+ uint8_t *pu8ReqList, int cReqList)
+{
+ Lease l(client); /* XXX: oh, it looks badly, but now we have lease */
+ prepareReplyPacket4Client(client, u32Xid);
+
+ RTNETADDRIPV4 address = l.getAddress();
+ m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;
+
+ /* Ubuntu ???*/
+ m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
+
+ /* options:
+ * - IP lease time
+ * - message type
+ * - server identifier
+ */
+ RawOption opt;
+ RT_ZERO(opt);
+
+ std::vector<RawOption> extra;
+ opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
+ opt.au8RawOpt[0] = RTNET_DHCP_MT_OFFER;
+ opt.cbRawOpt = 1;
+ extra.push_back(opt);
+
+ opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
+
+ const NetworkConfigEntity *pCfg = l.getConfig();
+ AssertPtr(pCfg);
+
+ *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(pCfg->expirationPeriod());
+ opt.cbRawOpt = sizeof(RTNETADDRIPV4);
+
+ extra.push_back(opt);
+
+ processParameterReqList(client, pu8ReqList, cReqList, extra);
+
+ return doReply(client, extra);
+}
+
+/**
+ * Network manager creates DHCPACK
+ */
+int NetworkManager::ack(const Client& client, uint32_t u32Xid,
+ uint8_t *pu8ReqList, int cReqList)
+{
+ RTNETADDRIPV4 address;
+
+ prepareReplyPacket4Client(client, u32Xid);
+
+ Lease l = client.lease();
+ address = l.getAddress();
+ m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
+
+
+ /* rfc2131 4.3.1 is about DHCPDISCOVER and this value is equal to ciaddr from
+ * DHCPREQUEST or 0 ...
+ * XXX: Using addressHint is not correct way to initialize [cy]iaddress...
+ */
+ m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
+ m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;
+
+ Assert(m->BootPReplyMsg.BootPHeader.bp_yiaddr.u);
+
+ /* options:
+ * - IP address lease time (if DHCPREQUEST)
+ * - message type
+ * - server identifier
+ */
+ RawOption opt;
+ RT_ZERO(opt);
+
+ std::vector<RawOption> extra;
+ opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
+ opt.au8RawOpt[0] = RTNET_DHCP_MT_ACK;
+ opt.cbRawOpt = 1;
+ extra.push_back(opt);
+
+ /*
+ * XXX: lease time should be conditional. If on dhcprequest then tim should be provided,
+ * else on dhcpinform it mustn't.
+ */
+ opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
+ *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(l.getExpiration());
+ opt.cbRawOpt = sizeof(RTNETADDRIPV4);
+ extra.push_back(opt);
+
+ processParameterReqList(client, pu8ReqList, cReqList, extra);
+
+ return doReply(client, extra);
+}
+
+/**
+ * Network manager creates DHCPNAK
+ */
+int NetworkManager::nak(const Client& client, uint32_t u32Xid)
+{
+
+ Lease l = client.lease();
+ if (l == Lease::NullLease)
+ return VERR_INTERNAL_ERROR;
+
+ prepareReplyPacket4Client(client, u32Xid);
+
+ /* this field filed in prepareReplyPacket4Session, and
+ * RFC 2131 require to have it zero fo NAK.
+ */
+ m->BootPReplyMsg.BootPHeader.bp_yiaddr.u = 0;
+
+ /* options:
+ * - message type (if DHCPREQUEST)
+ * - server identifier
+ */
+ RawOption opt;
+ std::vector<RawOption> extra;
+
+ opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
+ opt.au8RawOpt[0] = RTNET_DHCP_MT_NAC;
+ opt.cbRawOpt = 1;
+ extra.push_back(opt);
+
+ return doReply(client, extra);
+}
+
+/**
+ *
+ */
+int NetworkManager::prepareReplyPacket4Client(const Client& client, uint32_t u32Xid)
+{
+ RT_ZERO(m->BootPReplyMsg);
+
+ m->BootPReplyMsg.BootPHeader.bp_op = RTNETBOOTP_OP_REPLY;
+ m->BootPReplyMsg.BootPHeader.bp_htype = RTNET_ARP_ETHER;
+ m->BootPReplyMsg.BootPHeader.bp_hlen = sizeof(RTMAC);
+ m->BootPReplyMsg.BootPHeader.bp_hops = 0;
+ m->BootPReplyMsg.BootPHeader.bp_xid = u32Xid;
+ m->BootPReplyMsg.BootPHeader.bp_secs = 0;
+ /* XXX: bp_flags should be processed specially */
+ m->BootPReplyMsg.BootPHeader.bp_flags = 0;
+ m->BootPReplyMsg.BootPHeader.bp_ciaddr.u = 0;
+ m->BootPReplyMsg.BootPHeader.bp_giaddr.u = 0;
+
+ m->BootPReplyMsg.BootPHeader.bp_chaddr.Mac = client.getMacAddress();
+
+ const Lease l = client.lease();
+ m->BootPReplyMsg.BootPHeader.bp_yiaddr = l.getAddress();
+ m->BootPReplyMsg.BootPHeader.bp_siaddr.u = 0;
+
+
+ m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
+
+ memset(&m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_opts[0],
+ '\0',
+ RTNET_DHCP_OPT_SIZE);
+
+ return VINF_SUCCESS;
+}
+
+
+int NetworkManager::doReply(const Client& client, const std::vector<RawOption>& extra)
+{
+ int rc;
+
+ /*
+ Options....
+ */
+ VBoxNetDhcpWriteCursor Cursor(&m->BootPReplyMsg.BootPHeader, RTNET_DHCP_NORMAL_SIZE);
+
+ /* The basics */
+
+ Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m->m_OurAddress);
+
+ const Lease l = client.lease();
+ const std::map<uint8_t, RawOption>& options = l.options();
+
+ for(std::vector<RawOption>::const_iterator it = extra.begin();
+ it != extra.end(); ++it)
+ {
+ if (!Cursor.begin(it->u8OptId, it->cbRawOpt))
+ break;
+ Cursor.put(it->au8RawOpt, it->cbRawOpt);
+
+ }
+
+ for(std::map<uint8_t, RawOption>::const_iterator it = options.begin();
+ it != options.end(); ++it)
+ {
+ if (!Cursor.begin(it->second.u8OptId, it->second.cbRawOpt))
+ break;
+ Cursor.put(it->second.au8RawOpt, it->second.cbRawOpt);
+
+ }
+
+ Cursor.optEnd();
+
+ /*
+ */
+#if 0
+ /** @todo need to see someone set this flag to check that it's correct. */
+ if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST))
+ {
+ rc = VBoxNetUDPUnicast(m_pSession,
+ m_hIf,
+ m_pIfBuf,
+ m_OurAddress,
+ &m_OurMac,
+ RTNETIPV4_PORT_BOOTPS, /* sender */
+ IPv4AddrBrdCast,
+ &BootPReplyMsg.BootPHeader->bp_chaddr.Mac,
+ RTNETIPV4_PORT_BOOTPC, /* receiver */
+ &BootPReplyMsg, cbBooPReplyMsg);
+ }
+ else
+#endif
+ rc = m->m_service->hlpUDPBroadcast(RTNETIPV4_PORT_BOOTPS, /* sender */
+ RTNETIPV4_PORT_BOOTPC,
+ &m->BootPReplyMsg,
+ RTNET_DHCP_NORMAL_SIZE);
+
+ AssertRCReturn(rc,rc);
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * XXX: TODO: Share decoding code with DHCPServer::addOption.
+ */
+static int parseDhcpOptionText(const char *pszText,
+ int *pOptCode, char **ppszOptText, int *pOptEncoding)
+{
+ uint8_t u8Code;
+ uint32_t u32Enc;
+ char *pszNext;
+ int rc;
+
+ rc = RTStrToUInt8Ex(pszText, &pszNext, 10, &u8Code);
+ if (!RT_SUCCESS(rc))
+ return VERR_PARSE_ERROR;
+
+ switch (*pszNext)
+ {
+ case ':': /* support legacy format too */
+ {
+ u32Enc = 0;
+ break;
+ }
+
+ case '=':
+ {
+ u32Enc = 1;
+ break;
+ }
+
+ case '@':
+ {
+ rc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
+ if (!RT_SUCCESS(rc))
+ return VERR_PARSE_ERROR;
+ if (*pszNext != '=')
+ return VERR_PARSE_ERROR;
+ break;
+ }
+
+ default:
+ return VERR_PARSE_ERROR;
+ }
+
+ *pOptCode = u8Code;
+ *ppszOptText = pszNext + 1;
+ *pOptEncoding = (int)u32Enc;
+
+ return VINF_SUCCESS;
+}
+
+
+static int fillDhcpOption(RawOption &opt, const std::string &OptText, int OptEncoding)
+{
+ int rc;
+
+ if (OptEncoding == DhcpOptEncoding_Hex)
+ {
+ if (OptText.empty())
+ return VERR_INVALID_PARAMETER;
+
+ size_t cbRawOpt = 0;
+ char *pszNext = const_cast<char *>(OptText.c_str());
+ while (*pszNext != '\0')
+ {
+ if (cbRawOpt >= RT_ELEMENTS(opt.au8RawOpt))
+ return VERR_INVALID_PARAMETER;
+
+ uint8_t u8Byte;
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &u8Byte);
+ if (!RT_SUCCESS(rc))
+ return rc;
+
+ if (*pszNext == ':')
+ ++pszNext;
+ else if (*pszNext != '\0')
+ return VERR_PARSE_ERROR;
+
+ opt.au8RawOpt[cbRawOpt] = u8Byte;
+ ++cbRawOpt;
+ }
+ opt.cbRawOpt = (uint8_t)cbRawOpt;
+ }
+ else if (OptEncoding == DhcpOptEncoding_Legacy)
+ {
+ /*
+ * XXX: TODO: encode "known" option opt.u8OptId
+ */
+ return VERR_INVALID_PARAMETER;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int NetworkManager::processParameterReqList(const Client& client, const uint8_t *pu8ReqList,
+ int cReqList, std::vector<RawOption>& extra)
+{
+ int rc;
+
+ const Lease l = client.lease();
+
+ const NetworkConfigEntity *pNetCfg = l.getConfig();
+
+ /*
+ * XXX: Brute-force. Unfortunately, there's no notification event
+ * for changes. Should at least cache the options for a short
+ * time, enough to last discover/offer/request/ack cycle.
+ */
+ typedef std::map< int, std::pair<std::string, int> > DhcpOptionMap;
+ DhcpOptionMap OptMap;
+
+ if (!m->m_DhcpServer.isNull())
+ {
+ com::SafeArray<BSTR> strings;
+ com::Bstr str;
+ HRESULT hrc;
+ int OptCode, OptEncoding;
+ char *pszOptText;
+
+ strings.setNull();
+ hrc = m->m_DhcpServer->COMGETTER(GlobalOptions)(ComSafeArrayAsOutParam(strings));
+ AssertComRC(hrc);
+ for (size_t i = 0; i < strings.size(); ++i)
+ {
+ com::Utf8Str encoded(strings[i]);
+ rc = parseDhcpOptionText(encoded.c_str(),
+ &OptCode, &pszOptText, &OptEncoding);
+ if (!RT_SUCCESS(rc))
+ continue;
+
+ OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
+ }
+
+ const RTMAC &mac = client.getMacAddress();
+ char strMac[6*2+1] = "";
+ RTStrPrintf(strMac, sizeof(strMac), "%02x%02x%02x%02x%02x%02x",
+ mac.au8[0], mac.au8[1], mac.au8[2],
+ mac.au8[3], mac.au8[4], mac.au8[5]);
+
+ strings.setNull();
+ hrc = m->m_DhcpServer->GetMacOptions(com::Bstr(strMac).raw(),
+ ComSafeArrayAsOutParam(strings));
+ AssertComRC(hrc);
+ for (size_t i = 0; i < strings.size(); ++i)
+ {
+ com::Utf8Str text(strings[i]);
+ rc = parseDhcpOptionText(text.c_str(),
+ &OptCode, &pszOptText, &OptEncoding);
+ if (!RT_SUCCESS(rc))
+ continue;
+
+ OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
+ }
+ }
+
+ /* request parameter list */
+ RawOption opt;
+ bool fIgnore;
+ uint8_t u8Req;
+ for (int idxParam = 0; idxParam < cReqList; ++idxParam)
+ {
+ fIgnore = false;
+ RT_ZERO(opt);
+ u8Req = opt.u8OptId = pu8ReqList[idxParam];
+
+ switch(u8Req)
+ {
+ case RTNET_DHCP_OPT_SUBNET_MASK:
+ ((PRTNETADDRIPV4)opt.au8RawOpt)->u = pNetCfg->netmask().u;
+ opt.cbRawOpt = sizeof(RTNETADDRIPV4);
+
+ break;
+
+ case RTNET_DHCP_OPT_ROUTERS:
+ case RTNET_DHCP_OPT_DNS:
+ {
+ const Ipv4AddressContainer lst =
+ g_ConfigurationManager->getAddressList(u8Req);
+ PRTNETADDRIPV4 pAddresses = (PRTNETADDRIPV4)&opt.au8RawOpt[0];
+
+ for (Ipv4AddressConstIterator it = lst.begin();
+ it != lst.end();
+ ++it)
+ {
+ *pAddresses = (*it);
+ pAddresses++;
+ opt.cbRawOpt += sizeof(RTNETADDRIPV4);
+ }
+
+ if (lst.empty())
+ fIgnore = true;
+ }
+ break;
+ case RTNET_DHCP_OPT_DOMAIN_NAME:
+ {
+ std::string domainName = g_ConfigurationManager->getString(u8Req);
+ if (domainName == g_ConfigurationManager->m_noString)
+ {
+ fIgnore = true;
+ break;
+ }
+
+ size_t cchLength = domainName.length();
+ if (cchLength >= sizeof(opt.au8RawOpt))
+ cchLength = sizeof(opt.au8RawOpt) - 1;
+ memcpy(&opt.au8RawOpt[0], domainName.c_str(), cchLength);
+ opt.au8RawOpt[cchLength] = '\0';
+ opt.cbRawOpt = (uint8_t)cchLength;
+ }
+ break;
+ default:
+ {
+ DhcpOptionMap::const_iterator it = OptMap.find((int)u8Req);
+ if (it == OptMap.end())
+ {
+ Log(("opt: %d is ignored\n", u8Req));
+ fIgnore = true;
+ }
+ else
+ {
+ std::string OptText((*it).second.first);
+ int OptEncoding((*it).second.second);
+
+ rc = fillDhcpOption(opt, OptText, OptEncoding);
+ if (!RT_SUCCESS(rc))
+ {
+ fIgnore = true;
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ if (!fIgnore)
+ extra.push_back(opt);
+
+ }
+
+ return VINF_SUCCESS;
+}
+
+/* Client */
+Client::Client()
+{
+ m = SharedPtr<ClientData>();
+}
+
+
+void Client::initWithMac(const RTMAC& mac)
+{
+ m = SharedPtr<ClientData>(new ClientData());
+ m->m_mac = mac;
+}
+
+
+bool Client::operator== (const RTMAC& mac) const
+{
+ return (m.get() && m->m_mac == mac);
+}
+
+
+const RTMAC& Client::getMacAddress() const
+{
+ return m->m_mac;
+}
+
+
+Lease Client::lease()
+{
+ if (!m.get()) return Lease::NullLease;
+
+ if (m->fHasLease)
+ return Lease(*this);
+ else
+ return Lease::NullLease;
+}
+
+
+const Lease Client::lease() const
+{
+ return const_cast<Client *>(this)->lease();
+}
+
+
+Client::Client(ClientData *data):m(SharedPtr<ClientData>(data)){}
+
+/* Lease */
+Lease::Lease()
+{
+ m = SharedPtr<ClientData>();
+}
+
+
+Lease::Lease (const Client& c)
+{
+ m = SharedPtr<ClientData>(c.m);
+ if ( !m->fHasLease
+ || ( isExpired()
+ && !isInBindingPhase()))
+ {
+ m->fHasLease = true;
+ m->fBinding = true;
+ phaseStart(RTTimeMilliTS());
+ }
+}
+
+
+bool Lease::isExpired() const
+{
+ AssertPtrReturn(m.get(), false);
+
+ if (!m->fBinding)
+ return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampLeasingStarted, 1000)
+ > m->u32LeaseExpirationPeriod);
+ else
+ return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampBindingStarted, 1000)
+ > m->u32BindExpirationPeriod);
+}
+
+
+void Lease::expire()
+{
+ /* XXX: TODO */
+}
+
+
+void Lease::phaseStart(uint64_t u64Start)
+{
+ if (m->fBinding)
+ m->u64TimestampBindingStarted = u64Start;
+ else
+ m->u64TimestampLeasingStarted = u64Start;
+}
+
+
+void Lease::bindingPhase(bool fOnOff)
+{
+ m->fBinding = fOnOff;
+}
+
+
+bool Lease::isInBindingPhase() const
+{
+ return m->fBinding;
+}
+
+
+uint64_t Lease::issued() const
+{
+ return m->u64TimestampLeasingStarted;
+}
+
+
+void Lease::setExpiration(uint32_t exp)
+{
+ if (m->fBinding)
+ m->u32BindExpirationPeriod = exp;
+ else
+ m->u32LeaseExpirationPeriod = exp;
+}
+
+
+uint32_t Lease::getExpiration() const
+{
+ if (m->fBinding)
+ return m->u32BindExpirationPeriod;
+ else
+ return m->u32LeaseExpirationPeriod;
+}
+
+
+RTNETADDRIPV4 Lease::getAddress() const
+{
+ return m->m_address;
+}
+
+
+void Lease::setAddress(RTNETADDRIPV4 address)
+{
+ m->m_address = address;
+}
+
+
+const NetworkConfigEntity *Lease::getConfig() const
+{
+ return m->pCfg;
+}
+
+
+void Lease::setConfig(NetworkConfigEntity *pCfg)
+{
+ m->pCfg = pCfg;
+}
+
+
+const MapOptionId2RawOption& Lease::options() const
+{
+ return m->options;
+}
+
+
+Lease::Lease(ClientData *pd):m(SharedPtr<ClientData>(pd)){}
+
+
+bool Lease::toXML(xml::ElementNode *node) const
+{
+ xml::AttributeNode *pAttribNode = node->setAttribute(tagXMLLeaseAttributeMac.c_str(),
+ com::Utf8StrFmt("%RTmac", &m->m_mac));
+ if (!pAttribNode)
+ return false;
+
+ pAttribNode = node->setAttribute(tagXMLLeaseAttributeNetwork.c_str(),
+ com::Utf8StrFmt("%RTnaipv4", m->m_network));
+ if (!pAttribNode)
+ return false;
+
+ xml::ElementNode *pLeaseAddress = node->createChild(tagXMLLeaseAddress.c_str());
+ if (!pLeaseAddress)
+ return false;
+
+ pAttribNode = pLeaseAddress->setAttribute(tagXMLAddressAttributeValue.c_str(),
+ com::Utf8StrFmt("%RTnaipv4", m->m_address));
+ if (!pAttribNode)
+ return false;
+
+ xml::ElementNode *pLeaseTime = node->createChild(tagXMLLeaseTime.c_str());
+ if (!pLeaseTime)
+ return false;
+
+ pAttribNode = pLeaseTime->setAttribute(tagXMLTimeAttributeIssued.c_str(),
+ m->u64TimestampLeasingStarted);
+ if (!pAttribNode)
+ return false;
+
+ pAttribNode = pLeaseTime->setAttribute(tagXMLTimeAttributeExpiration.c_str(),
+ m->u32LeaseExpirationPeriod);
+ if (!pAttribNode)
+ return false;
+
+ return true;
+}
+
+
+bool Lease::fromXML(const xml::ElementNode *node)
+{
+ com::Utf8Str mac;
+ bool valueExists = node->getAttributeValue(tagXMLLeaseAttributeMac.c_str(), mac);
+ if (!valueExists) return false;
+ int rc = RTNetStrToMacAddr(mac.c_str(), &m->m_mac);
+ if (RT_FAILURE(rc)) return false;
+
+ com::Utf8Str network;
+ valueExists = node->getAttributeValue(tagXMLLeaseAttributeNetwork.c_str(), network);
+ if (!valueExists) return false;
+ rc = RTNetStrToIPv4Addr(network.c_str(), &m->m_network);
+ if (RT_FAILURE(rc)) return false;
+
+ /* Address */
+ const xml::ElementNode *address = node->findChildElement(tagXMLLeaseAddress.c_str());
+ if (!address) return false;
+ com::Utf8Str addressValue;
+ valueExists = address->getAttributeValue(tagXMLAddressAttributeValue.c_str(), addressValue);
+ if (!valueExists) return false;
+ rc = RTNetStrToIPv4Addr(addressValue.c_str(), &m->m_address);
+
+ /* Time */
+ const xml::ElementNode *time = node->findChildElement(tagXMLLeaseTime.c_str());
+ if (!time) return false;
+
+ valueExists = time->getAttributeValue(tagXMLTimeAttributeIssued.c_str(),
+ &m->u64TimestampLeasingStarted);
+ if (!valueExists) return false;
+ m->fBinding = false;
+
+ valueExists = time->getAttributeValue(tagXMLTimeAttributeExpiration.c_str(),
+ &m->u32LeaseExpirationPeriod);
+ if (!valueExists) return false;
+
+ m->fHasLease = true;
+ return true;
+}
+
+
+const Lease Lease::NullLease;
+
+const Client Client::NullClient;
diff --git a/src/VBox/NetworkServices/DHCP/Config.h b/src/VBox/NetworkServices/DHCP/Config.h
new file mode 100644
index 00000000..a8431686
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/Config.h
@@ -0,0 +1,845 @@
+/* $Id: Config.h $ */
+/** @file
+ * Config.h
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_DHCP_Config_h
+#define VBOX_INCLUDED_SRC_DHCP_Config_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/asm-math.h>
+#include <iprt/cpp/utils.h>
+
+#include <VBox/com/ptr.h>
+#include <VBox/com/string.h>
+#include <VBox/com/VirtualBox.h>
+
+#include "../NetLib/cpp/utils.h"
+
+
+class RawOption
+{
+public:
+ RawOption()
+ {
+ /** @todo r=bird: this is crazy. */
+ RT_ZERO(*this);
+ }
+ uint8_t u8OptId;
+ uint8_t cbRawOpt;
+ uint8_t au8RawOpt[255];
+};
+
+class ClientData;
+class Client;
+class Lease;
+class BaseConfigEntity;
+
+class NetworkConfigEntity;
+class HostConfigEntity;
+class ClientMatchCriteria;
+class ConfigurationManager;
+
+/*
+ * it's a basic representation of
+ * of out undestanding what client is
+ * XXX: Client might sends Option 61 (RFC2132 9.14 "Client-identifier") signalling
+ * that we may identify it in special way
+ *
+ * XXX: Client might send Option 60 (RFC2132 9.13 "Vendor class undentifier")
+ * in response it's expected server sends Option 43 (RFC2132 8.4. "Vendor Specific Information")
+ */
+class Client
+{
+ friend class Lease;
+ friend class ConfigurationManager;
+
+ public:
+ Client();
+ void initWithMac(const RTMAC& mac);
+ bool operator== (const RTMAC& mac) const;
+ const RTMAC& getMacAddress() const;
+
+ /** Dumps client query */
+ void dump();
+
+ Lease lease();
+ const Lease lease() const;
+
+ public:
+ static const Client NullClient;
+
+ private:
+ Client(ClientData *);
+ SharedPtr<ClientData> m;
+};
+
+
+bool operator== (const Lease&, const Lease&);
+bool operator!= (const Lease&, const Lease&);
+bool operator< (const Lease&, const Lease&);
+
+
+typedef std::map<uint8_t, RawOption> MapOptionId2RawOption;
+typedef MapOptionId2RawOption::iterator MapOptionId2RawOptionIterator;
+typedef MapOptionId2RawOption::const_iterator MapOptionId2RawOptionConstIterator;
+typedef MapOptionId2RawOption::value_type MapOptionId2RawOptionValue;
+
+namespace xml {
+ class ElementNode;
+}
+
+class Lease
+{
+ friend class Client;
+ friend bool operator== (const Lease&, const Lease&);
+ //friend int ConfigurationManager::loadFromFile(const std::string&);
+ friend class ConfigurationManager;
+
+ public:
+ Lease();
+ Lease(const Client&);
+
+ bool isExpired() const;
+ void expire();
+
+ /* Depending on phase *Expiration and phaseStart initialize different values. */
+ void bindingPhase(bool);
+ void phaseStart(uint64_t u64Start);
+ bool isInBindingPhase() const;
+ /* returns 0 if in binding state */
+ uint64_t issued() const;
+
+ void setExpiration(uint32_t);
+ uint32_t getExpiration() const;
+
+ RTNETADDRIPV4 getAddress() const;
+ void setAddress(RTNETADDRIPV4);
+
+ const NetworkConfigEntity *getConfig() const;
+ void setConfig(NetworkConfigEntity *);
+
+ const MapOptionId2RawOption& options() const;
+
+ bool toXML(xml::ElementNode *) const;
+ bool fromXML(const xml::ElementNode *);
+
+ public:
+ static const Lease NullLease;
+
+ private:
+ Lease(ClientData *);
+ SharedPtr<ClientData> m;
+};
+
+
+typedef std::vector<Client> VecClient;
+typedef VecClient::iterator VecClientIterator;
+typedef VecClient::const_iterator VecClientConstIterator;
+
+typedef std::vector<RTMAC> MacAddressContainer;
+typedef MacAddressContainer::iterator MacAddressIterator;
+
+typedef std::vector<RTNETADDRIPV4> Ipv4AddressContainer;
+typedef Ipv4AddressContainer::iterator Ipv4AddressIterator;
+typedef Ipv4AddressContainer::const_iterator Ipv4AddressConstIterator;
+
+typedef std::map<Lease, RTNETADDRIPV4> MapLease2Ip4Address;
+typedef MapLease2Ip4Address::iterator MapLease2Ip4AddressIterator;
+typedef MapLease2Ip4Address::const_iterator MapLease2Ip4AddressConstIterator;
+typedef MapLease2Ip4Address::value_type MapLease2Ip4AddressPair;
+
+/**
+ *
+ */
+class ClientMatchCriteria
+{
+ public:
+ virtual bool check(const Client&) const {return false;};
+};
+
+
+class ORClientMatchCriteria: ClientMatchCriteria
+{
+ ClientMatchCriteria* m_left;
+ ClientMatchCriteria* m_right;
+ ORClientMatchCriteria(ClientMatchCriteria *left, ClientMatchCriteria *right)
+ {
+ m_left = left;
+ m_right = right;
+ }
+
+ virtual bool check(const Client& client) const
+ {
+ return (m_left->check(client) || m_right->check(client));
+ }
+};
+
+
+class ANDClientMatchCriteria: ClientMatchCriteria
+{
+public:
+ ANDClientMatchCriteria(ClientMatchCriteria *left, ClientMatchCriteria *right)
+ {
+ m_left = left;
+ m_right = right;
+ }
+
+ virtual bool check(const Client& client) const
+ {
+ return (m_left->check(client) && m_right->check(client));
+ }
+
+private:
+ ClientMatchCriteria* m_left;
+ ClientMatchCriteria* m_right;
+
+};
+
+
+class AnyClientMatchCriteria: public ClientMatchCriteria
+{
+public:
+ virtual bool check(const Client&) const
+ {
+ return true;
+ }
+};
+
+
+class MACClientMatchCriteria: public ClientMatchCriteria
+{
+public:
+ MACClientMatchCriteria(const RTMAC& mac):m_mac(mac){}
+
+ virtual bool check(const Client& client) const;
+
+private:
+ RTMAC m_mac;
+};
+
+
+#if 0
+/* XXX: Later */
+class VmSlotClientMatchCriteria: public ClientMatchCriteria
+{
+ str::string VmName;
+ uint8_t u8Slot;
+ virtual bool check(const Client& client)
+ {
+ return ( client.VmName == VmName
+ && ( u8Slot == (uint8_t)~0 /* any */
+ || client.u8Slot == u8Slot));
+ }
+};
+#endif
+
+
+/* Option 60 */
+class ClassClientMatchCriteria: ClientMatchCriteria{};
+/* Option 61 */
+class ClientIdentifierMatchCriteria: ClientMatchCriteria{};
+
+
+class BaseConfigEntity
+{
+ public:
+ BaseConfigEntity(const ClientMatchCriteria *criteria = NULL,
+ int matchingLevel = 0)
+ : m_criteria(criteria),
+ m_MatchLevel(matchingLevel){};
+ virtual ~BaseConfigEntity(){};
+ /* XXX */
+ int add(BaseConfigEntity *cfg)
+ {
+ m_children.push_back(cfg);
+ return 0;
+ }
+
+ /* Should return how strong matching */
+ virtual int match(Client& client, BaseConfigEntity **cfg);
+ virtual uint32_t expirationPeriod() const = 0;
+
+ protected:
+ const ClientMatchCriteria *m_criteria;
+ int m_MatchLevel;
+ std::vector<BaseConfigEntity *> m_children;
+};
+
+
+class NullConfigEntity: public BaseConfigEntity
+{
+ public:
+ NullConfigEntity(){}
+ virtual ~NullConfigEntity(){}
+ int add(BaseConfigEntity *) const { return 0;}
+ virtual uint32_t expirationPeriod() const {return 0;}
+};
+
+
+class ConfigEntity: public BaseConfigEntity
+{
+ public:
+ /* range */
+ /* match conditions */
+ ConfigEntity(std::string& name,
+ const BaseConfigEntity *cfg,
+ const ClientMatchCriteria *criteria,
+ int matchingLevel = 0):
+ BaseConfigEntity(criteria, matchingLevel),
+ m_name(name),
+ m_parentCfg(cfg),
+ m_u32ExpirationPeriod(0)
+ {
+ unconst(m_parentCfg)->add(this);
+ }
+
+ virtual uint32_t expirationPeriod() const
+ {
+ if (!m_u32ExpirationPeriod)
+ return m_parentCfg->expirationPeriod();
+ else
+ return m_u32ExpirationPeriod;
+ }
+
+ /* XXX: private:*/
+ std::string m_name;
+ const BaseConfigEntity *m_parentCfg;
+ uint32_t m_u32ExpirationPeriod;
+};
+
+
+/**
+ * Network specific entries
+ */
+class NetworkConfigEntity:public ConfigEntity
+{
+public:
+ /* Address Pool matching with network declaration */
+ NetworkConfigEntity(std::string name,
+ const BaseConfigEntity *cfg,
+ const ClientMatchCriteria *criteria,
+ int matchlvl,
+ const RTNETADDRIPV4& networkID,
+ const RTNETADDRIPV4& networkMask,
+ const RTNETADDRIPV4& lowerIP,
+ const RTNETADDRIPV4& upperIP):
+ ConfigEntity(name, cfg, criteria, matchlvl),
+ m_NetworkID(networkID),
+ m_NetworkMask(networkMask),
+ m_UpperIP(upperIP),
+ m_LowerIP(lowerIP)
+ {
+ };
+
+ NetworkConfigEntity(std::string name,
+ const BaseConfigEntity *cfg,
+ const ClientMatchCriteria *criteria,
+ const RTNETADDRIPV4& networkID,
+ const RTNETADDRIPV4& networkMask):
+ ConfigEntity(name, cfg, criteria, 5),
+ m_NetworkID(networkID),
+ m_NetworkMask(networkMask)
+ {
+ m_UpperIP.u = m_NetworkID.u | (~m_NetworkMask.u);
+ m_LowerIP.u = m_NetworkID.u;
+ };
+
+ const RTNETADDRIPV4& upperIp() const {return m_UpperIP;}
+ const RTNETADDRIPV4& lowerIp() const {return m_LowerIP;}
+ const RTNETADDRIPV4& networkId() const {return m_NetworkID;}
+ const RTNETADDRIPV4& netmask() const {return m_NetworkMask;}
+
+ private:
+ RTNETADDRIPV4 m_NetworkID;
+ RTNETADDRIPV4 m_NetworkMask;
+ RTNETADDRIPV4 m_UpperIP;
+ RTNETADDRIPV4 m_LowerIP;
+};
+
+
+/**
+ * Host specific entry
+ * Address pool is contains one element
+ */
+class HostConfigEntity: public NetworkConfigEntity
+{
+public:
+ HostConfigEntity(const RTNETADDRIPV4& addr,
+ std::string name,
+ const NetworkConfigEntity *cfg,
+ const ClientMatchCriteria *criteria):
+ NetworkConfigEntity(name,
+ static_cast<const ConfigEntity*>(cfg), criteria, 10,
+ cfg->networkId(), cfg->netmask(), addr, addr)
+ {
+ /* upper addr == lower addr */
+ }
+};
+
+class RootConfigEntity: public NetworkConfigEntity
+{
+public:
+ RootConfigEntity(std::string name, uint32_t expirationPeriod);
+ virtual ~RootConfigEntity(){};
+};
+
+
+#if 0
+/**
+ * Shared regions e.g. some of configured networks declarations
+ * are cover each other.
+ * XXX: Shared Network is join on Network config entities with possible
+ * overlaps in address pools. for a moment we won't configure and use them them
+ */
+class SharedNetworkConfigEntity: public NetworkEntity
+{
+public:
+ SharedNetworkConfigEntity(){}
+ int match(const Client& client) const { return m_criteria.match(client)? 3 : 0;}
+
+ SharedNetworkConfigEntity(NetworkEntity& network)
+ {
+ Networks.push_back(network);
+ }
+ virtual ~SharedNetworkConfigEntity(){}
+
+ std::vector<NetworkConfigEntity> Networks;
+};
+#endif
+
+class ConfigurationManager
+{
+public:
+ static ConfigurationManager* getConfigurationManager();
+ static int extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt);
+
+ int loadFromFile(const com::Utf8Str&);
+ int saveToFile();
+ /**
+ *
+ */
+ Client getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg);
+
+ /**
+ * XXX: it's could be done on DHCPOFFER or on DHCPACK (rfc2131 gives freedom here
+ * 3.1.2, what is strict that allocation should do address check before real
+ * allocation)...
+ */
+ Lease allocateLease4Client(const Client& client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg);
+
+ /**
+ * We call this before DHCPACK sent and after DHCPREQUEST received ...
+ * when requested configuration is acceptable.
+ */
+ int commitLease4Client(Client& client);
+
+ /**
+ * Expires client lease.
+ */
+ int expireLease4Client(Client& client);
+
+ static int findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt);
+
+ NetworkConfigEntity *addNetwork(NetworkConfigEntity *pCfg,
+ const RTNETADDRIPV4& networkId,
+ const RTNETADDRIPV4& netmask,
+ RTNETADDRIPV4& UpperAddress,
+ RTNETADDRIPV4& LowerAddress);
+
+ HostConfigEntity *addHost(NetworkConfigEntity*, const RTNETADDRIPV4&, ClientMatchCriteria*);
+ int addToAddressList(uint8_t u8OptId, RTNETADDRIPV4& address);
+ int flushAddressList(uint8_t u8OptId);
+ int setString(uint8_t u8OptId, const std::string& str);
+ const std::string& getString(uint8_t u8OptId);
+ const Ipv4AddressContainer& getAddressList(uint8_t u8OptId);
+
+private:
+ ConfigurationManager():m(NULL){}
+ void init();
+
+ ~ConfigurationManager();
+ bool isAddressTaken(const RTNETADDRIPV4& addr, Lease& lease);
+ bool isAddressTaken(const RTNETADDRIPV4& addr);
+
+public:
+ /* nulls */
+ const Ipv4AddressContainer m_empty;
+ const std::string m_noString;
+
+private:
+ struct Data;
+ Data *m;
+};
+
+
+class NetworkManager
+{
+public:
+ static NetworkManager *getNetworkManager(ComPtr<IDHCPServer> aDhcpServer = ComPtr<IDHCPServer>());
+
+ const RTNETADDRIPV4& getOurAddress() const;
+ const RTNETADDRIPV4& getOurNetmask() const;
+ const RTMAC& getOurMac() const;
+
+ void setOurAddress(const RTNETADDRIPV4& aAddress);
+ void setOurNetmask(const RTNETADDRIPV4& aNetmask);
+ void setOurMac(const RTMAC& aMac);
+
+ bool handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb);
+ bool handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb);
+ bool handleDhcpReqDecline(PCRTNETBOOTP pDhcpMsg, size_t cb);
+ bool handleDhcpReqRelease(PCRTNETBOOTP pDhcpMsg, size_t cb);
+
+ void setService(const VBoxNetHlpUDPService *);
+private:
+ NetworkManager();
+ ~NetworkManager();
+
+ int offer4Client(const Client& lease, uint32_t u32Xid, uint8_t *pu8ReqList, int cReqList);
+ int ack(const Client& lease, uint32_t u32Xid, uint8_t *pu8ReqList, int cReqList);
+ int nak(const Client& lease, uint32_t u32Xid);
+
+ int prepareReplyPacket4Client(const Client& client, uint32_t u32Xid);
+ int doReply(const Client& client, const std::vector<RawOption>& extra);
+ int processParameterReqList(const Client& client, const uint8_t *pu8ReqList, int cReqList, std::vector<RawOption>& extra);
+
+private:
+ static NetworkManager *g_NetworkManager;
+
+private:
+ struct Data;
+ Data *m;
+
+};
+
+
+extern const ClientMatchCriteria *g_AnyClient;
+extern RootConfigEntity *g_RootConfig;
+extern const NullConfigEntity *g_NullConfig;
+
+/**
+ * Helper class for stuffing DHCP options into a reply packet.
+ */
+class VBoxNetDhcpWriteCursor
+{
+private:
+ uint8_t *m_pbCur; /**< The current cursor position. */
+ uint8_t *m_pbEnd; /**< The end the current option space. */
+ uint8_t *m_pfOverload; /**< Pointer to the flags of the overload option. */
+ uint8_t m_fUsed; /**< Overload fields that have been used. */
+ PRTNETDHCPOPT m_pOpt; /**< The current option. */
+ PRTNETBOOTP m_pDhcp; /**< The DHCP packet. */
+ bool m_fOverflowed; /**< Set if we've overflowed, otherwise false. */
+
+public:
+ /** Instantiate an option cursor for the specified DHCP message. */
+ VBoxNetDhcpWriteCursor(PRTNETBOOTP pDhcp, size_t cbDhcp) :
+ m_pbCur(&pDhcp->bp_vend.Dhcp.dhcp_opts[0]),
+ m_pbEnd((uint8_t *)pDhcp + cbDhcp),
+ m_pfOverload(NULL),
+ m_fUsed(0),
+ m_pOpt(NULL),
+ m_pDhcp(pDhcp),
+ m_fOverflowed(false)
+ {
+ AssertPtr(pDhcp);
+ Assert(cbDhcp > RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts[10]));
+ }
+
+ /** Destructor. */
+ ~VBoxNetDhcpWriteCursor()
+ {
+ m_pbCur = m_pbEnd = m_pfOverload = NULL;
+ m_pOpt = NULL;
+ m_pDhcp = NULL;
+ }
+
+ /**
+ * Try use the bp_file field.
+ * @returns true if not overloaded, false otherwise.
+ */
+ bool useBpFile(void)
+ {
+ if ( m_pfOverload
+ && (*m_pfOverload & 1))
+ return false;
+ m_fUsed |= 1 /* bp_file flag*/;
+ return true;
+ }
+
+
+ /**
+ * Try overload more BOOTP fields
+ */
+ bool overloadMore(void)
+ {
+ /* switch option area. */
+ uint8_t *pbNew;
+ uint8_t *pbNewEnd;
+ uint8_t fField;
+ if (!(m_fUsed & 1))
+ {
+ fField = 1;
+ pbNew = &m_pDhcp->bp_file[0];
+ pbNewEnd = &m_pDhcp->bp_file[sizeof(m_pDhcp->bp_file)];
+ }
+ else if (!(m_fUsed & 2))
+ {
+ fField = 2;
+ pbNew = &m_pDhcp->bp_sname[0];
+ pbNewEnd = &m_pDhcp->bp_sname[sizeof(m_pDhcp->bp_sname)];
+ }
+ else
+ return false;
+
+ if (!m_pfOverload)
+ {
+ /* Add an overload option. */
+ *m_pbCur++ = RTNET_DHCP_OPT_OPTION_OVERLOAD;
+ *m_pbCur++ = fField;
+ m_pfOverload = m_pbCur;
+ *m_pbCur++ = 1; /* bp_file flag */
+ }
+ else
+ *m_pfOverload |= fField;
+
+ /* pad current option field */
+ while (m_pbCur != m_pbEnd)
+ *m_pbCur++ = RTNET_DHCP_OPT_PAD; /** @todo not sure if this stuff is at all correct... */
+
+ /* switch */
+ m_pbCur = pbNew;
+ m_pbEnd = pbNewEnd;
+ return true;
+ }
+
+ /**
+ * Begin an option.
+ *
+ * @returns true on success, false if we're out of space.
+ *
+ * @param uOption The option number.
+ * @param cb The amount of data.
+ */
+ bool begin(uint8_t uOption, size_t cb)
+ {
+ /* Check that the data of the previous option has all been written. */
+ Assert( !m_pOpt
+ || (m_pbCur - m_pOpt->dhcp_len == (uint8_t *)(m_pOpt + 1)));
+ AssertMsg(cb <= 255, ("%#x\n", cb));
+
+ /* Check if we need to overload more stuff. */
+ if ((uintptr_t)(m_pbEnd - m_pbCur) < cb + 2 + (m_pfOverload ? 1 : 3))
+ {
+ m_pOpt = NULL;
+ if (!overloadMore())
+ {
+ m_fOverflowed = true;
+ AssertMsgFailedReturn(("%u %#x\n", uOption, cb), false);
+ }
+ if ((uintptr_t)(m_pbEnd - m_pbCur) < cb + 2 + 1)
+ {
+ m_fOverflowed = true;
+ AssertMsgFailedReturn(("%u %#x\n", uOption, cb), false);
+ }
+ }
+
+ /* Emit the option header. */
+ m_pOpt = (PRTNETDHCPOPT)m_pbCur;
+ m_pOpt->dhcp_opt = uOption;
+ m_pOpt->dhcp_len = (uint8_t)cb;
+ m_pbCur += 2;
+ return true;
+ }
+
+ /**
+ * Puts option data.
+ *
+ * @param pvData The data.
+ * @param cb The amount to put.
+ */
+ void put(void const *pvData, size_t cb)
+ {
+ Assert(m_pOpt || m_fOverflowed);
+ if (RT_LIKELY(m_pOpt))
+ {
+ Assert((uintptr_t)m_pbCur - (uintptr_t)(m_pOpt + 1) + cb <= (size_t)m_pOpt->dhcp_len);
+ memcpy(m_pbCur, pvData, cb);
+ m_pbCur += cb;
+ }
+ }
+
+ /**
+ * Puts an IPv4 Address.
+ *
+ * @param IPv4Addr The address.
+ */
+ void putIPv4Addr(RTNETADDRIPV4 IPv4Addr)
+ {
+ put(&IPv4Addr, 4);
+ }
+
+ /**
+ * Adds an IPv4 address option.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param IPv4Addr The address.
+ */
+ bool optIPv4Addr(uint8_t uOption, RTNETADDRIPV4 IPv4Addr)
+ {
+ if (!begin(uOption, 4))
+ return false;
+ putIPv4Addr(IPv4Addr);
+ return true;
+ }
+
+ /**
+ * Adds an option taking 1 or more IPv4 address.
+ *
+ * If the vector contains no addresses, the option will not be added.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param rIPv4Addrs Reference to the address vector.
+ */
+ bool optIPv4Addrs(uint8_t uOption, std::vector<RTNETADDRIPV4> const &rIPv4Addrs)
+ {
+ size_t const c = rIPv4Addrs.size();
+ if (!c)
+ return true;
+
+ if (!begin(uOption, 4*c))
+ return false;
+ for (size_t i = 0; i < c; i++)
+ putIPv4Addr(rIPv4Addrs[i]);
+ return true;
+ }
+
+ /**
+ * Puts an 8-bit integer.
+ *
+ * @param u8 The integer.
+ */
+ void putU8(uint8_t u8)
+ {
+ put(&u8, 1);
+ }
+
+ /**
+ * Adds an 8-bit integer option.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param u8 The integer
+ */
+ bool optU8(uint8_t uOption, uint8_t u8)
+ {
+ if (!begin(uOption, 1))
+ return false;
+ putU8(u8);
+ return true;
+ }
+
+ /**
+ * Puts an 32-bit integer (network endian).
+ *
+ * @param u32 The integer.
+ */
+ void putU32(uint32_t u32)
+ {
+ put(&u32, 4);
+ }
+
+ /**
+ * Adds an 32-bit integer (network endian) option.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param u32 The integer.
+ */
+ bool optU32(uint8_t uOption, uint32_t u32)
+ {
+ if (!begin(uOption, 4))
+ return false;
+ putU32(u32);
+ return true;
+ }
+
+ /**
+ * Puts a std::string.
+ *
+ * @param rStr Reference to the string.
+ */
+ void putStr(std::string const &rStr)
+ {
+ put(rStr.c_str(), rStr.size());
+ }
+
+ /**
+ * Adds an std::string option if the string isn't empty.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param rStr Reference to the string.
+ */
+ bool optStr(uint8_t uOption, std::string const &rStr)
+ {
+ const size_t cch = rStr.size();
+ if (!cch)
+ return true;
+
+ if (!begin(uOption, cch))
+ return false;
+ put(rStr.c_str(), cch);
+ return true;
+ }
+
+ /**
+ * Whether we've overflowed.
+ *
+ * @returns true on overflow, false otherwise.
+ */
+ bool hasOverflowed(void) const
+ {
+ return m_fOverflowed;
+ }
+
+ /**
+ * Adds the terminating END option.
+ *
+ * The END will always be added as we're reserving room for it, however, we
+ * might have dropped previous options due to overflows and that is what the
+ * return status indicates.
+ *
+ * @returns true on success, false on a (previous) overflow.
+ */
+ bool optEnd(void)
+ {
+ Assert((uintptr_t)(m_pbEnd - m_pbCur) < 4096);
+ *m_pbCur++ = RTNET_DHCP_OPT_END;
+ return !hasOverflowed();
+ }
+};
+
+#endif /* !VBOX_INCLUDED_SRC_DHCP_Config_h */
diff --git a/src/VBox/NetworkServices/DHCP/Makefile.kmk b/src/VBox/NetworkServices/DHCP/Makefile.kmk
new file mode 100644
index 00000000..41e9bb8e
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/Makefile.kmk
@@ -0,0 +1,72 @@
+ # $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for VBoxNetDHCP.
+#
+
+#
+# Copyright (C) 2009-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+VBOX_PATH_NET_DHCP_SRC := $(PATH_SUB_CURRENT)
+
+#
+# Targets.
+#
+ifdef VBOX_WITH_HARDENING
+ PROGRAMS += VBoxNetDHCPHardened
+ DLLS += VBoxNetDHCP
+else
+ PROGRAMS += VBoxNetDHCP
+endif
+
+
+#
+# Hardened VBoxNetDHCP.
+#
+VBoxNetDHCPHardened_TEMPLATE = VBOXR3HARDENEDEXE
+VBoxNetDHCPHardened_SOURCES = VBoxNetDHCPHardened.cpp
+VBoxNetDHCPHardened_SOURCES.win = $(VBoxNetDHCP_0_OUTDIR)/VBoxNetDHCP-icon.rc
+VBoxNetDHCPHardened_NAME = VBoxNetDHCP
+VBoxNetDHCPHardened_LDFLAGS.win = /SUBSYSTEM:windows
+
+
+#
+# VBoxNetDHCP
+#
+VBoxNetDHCP_TEMPLATE := VBOX$(if-expr defined(VBOX_WITH_HARDENING),MAINDLL,MAINCLIENTEXE)
+VBoxNetDHCP_SOURCES = \
+ VBoxNetDHCP.cpp \
+ Config.cpp \
+ NetworkManagerDhcp.cpp \
+ $(VBOX_PATH_NET_DHCP_SRC)/../NetLib/VBoxNetIntIf.cpp \
+ $(VBOX_PATH_NET_DHCP_SRC)/../NetLib/VBoxNetUDP.cpp \
+ $(VBOX_PATH_NET_DHCP_SRC)/../NetLib/VBoxNetARP.cpp \
+ $(VBOX_PATH_NET_DHCP_SRC)/../NetLib/VBoxNetBaseService.cpp \
+ $(VBOX_PATH_NET_DHCP_SRC)/../NetLib/ComHostUtils.cpp
+VBoxNetDHCP_LIBS = \
+ $(LIB_RUNTIME)
+VBoxNetDHCP_LDFLAGS.win = /SUBSYSTEM:windows
+
+ifeq ($(KBUILD_TARGET),win)
+# Icon include file.
+VBoxNetDHCP_SOURCES += VBoxNetDHCP.rc
+VBoxNetDHCP.rc_INCS = $(VBoxNetDHCP_0_OUTDIR)
+VBoxNetDHCP.rc_DEPS = $(VBoxNetDHCP_0_OUTDIR)/VBoxNetDHCP-icon.rc
+VBoxNetDHCP.rc_CLEAN = $(VBoxNetDHCP_0_OUTDIR)/VBoxNetDHCP-icon.rc
+$$(VBoxNetDHCP_0_OUTDIR)/VBoxNetDHCP-icon.rc: $(VBOX_WINDOWS_ICON_FILE) $$(VBoxNetDHCP_DEFPATH)/Makefile.kmk | $$(dir $$@)
+ $(RM) -f $@
+ $(APPEND) $@ 'IDI_VIRTUALBOX ICON DISCARDABLE "$(subst /,\\,$(VBOX_WINDOWS_ICON_FILE))"'
+endif # win
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/NetworkServices/DHCP/NetworkManagerDhcp.cpp b/src/VBox/NetworkServices/DHCP/NetworkManagerDhcp.cpp
new file mode 100644
index 00000000..591da5b8
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/NetworkManagerDhcp.cpp
@@ -0,0 +1,189 @@
+/* $Id: NetworkManagerDhcp.cpp $ */
+/** @file
+ * NetworkManagerDhcp - Network Manager part handling Dhcp.
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/cdefs.h>
+#include <iprt/getopt.h>
+#include <iprt/net.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/time.h>
+#include <iprt/string.h>
+
+#include "../NetLib/shared_ptr.h"
+
+#include <vector>
+#include <list>
+#include <string>
+#include <map>
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+
+#define BASE_SERVICES_ONLY
+#include "../NetLib/VBoxNetBaseService.h"
+#include "Config.h"
+#include "ClientDataInt.h"
+
+/**
+ * The client is requesting an offer.
+ *
+ * @returns true.
+ *
+ * @param pDhcpMsg The message.
+ * @param cb The message size.
+ */
+bool NetworkManager::handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ RawOption opt;
+ RT_ZERO(opt);
+
+ /* 1. Find client */
+ ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager();
+ Client client = confManager->getClientByDhcpPacket(pDhcpMsg, cb);
+
+ /* 2. Find/Bind lease for client */
+ Lease lease = confManager->allocateLease4Client(client, pDhcpMsg, cb);
+ AssertReturn(lease != Lease::NullLease, VINF_SUCCESS);
+
+ int rc = ConfigurationManager::extractRequestList(pDhcpMsg, cb, opt);
+ NOREF(rc); /** @todo check */
+
+ /* 3. Send of offer */
+
+ lease.bindingPhase(true);
+ lease.phaseStart(RTTimeMilliTS());
+ lease.setExpiration(300); /* 3 min. */
+ offer4Client(client, pDhcpMsg->bp_xid, opt.au8RawOpt, opt.cbRawOpt);
+
+ return true;
+}
+
+
+/**
+ * The client is requesting an offer.
+ *
+ * @returns true.
+ *
+ * @param pDhcpMsg The message.
+ * @param cb The message size.
+ */
+bool NetworkManager::handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager();
+
+ /* 1. find client */
+ Client client = confManager->getClientByDhcpPacket(pDhcpMsg, cb);
+
+ /* 2. find bound lease */
+ Lease l = client.lease();
+ if (l != Lease::NullLease)
+ {
+
+ if (l.isExpired())
+ {
+ /* send client to INIT state */
+ Client c(client);
+ nak(client, pDhcpMsg->bp_xid);
+ confManager->expireLease4Client(c);
+ return true;
+ }
+ /* XXX: Validate request */
+ RawOption opt;
+ RT_ZERO(opt);
+
+ Client c(client);
+ int rc = confManager->commitLease4Client(c);
+ AssertRCReturn(rc, false);
+
+ rc = ConfigurationManager::extractRequestList(pDhcpMsg, cb, opt);
+ AssertRCReturn(rc, false);
+
+ ack(client, pDhcpMsg->bp_xid, opt.au8RawOpt, opt.cbRawOpt);
+ }
+ else
+ {
+ nak(client, pDhcpMsg->bp_xid);
+ }
+ return true;
+}
+
+
+/**
+ * The client is declining an offer we've made.
+ *
+ * @returns true.
+ *
+ * @param pDhcpMsg The message.
+ * @param cb The message size.
+ */
+bool NetworkManager::handleDhcpReqDecline(PCRTNETBOOTP, size_t)
+{
+ /** @todo Probably need to match the server IP here to work correctly with
+ * other servers. */
+
+ /*
+ * The client is supposed to pass us option 50, requested address,
+ * from the offer. We also match the lease state. Apparently the
+ * MAC address is not supposed to be checked here.
+ */
+
+ /** @todo this is not required in the initial implementation, do it later. */
+ return true;
+}
+
+
+/**
+ * The client is releasing its lease - good boy.
+ *
+ * @returns true.
+ *
+ * @param pDhcpMsg The message.
+ * @param cb The message size.
+ */
+bool NetworkManager::handleDhcpReqRelease(PCRTNETBOOTP, size_t)
+{
+ /** @todo Probably need to match the server IP here to work correctly with
+ * other servers. */
+
+ /*
+ * The client may pass us option 61, client identifier, which we should
+ * use to find the lease by.
+ *
+ * We're matching MAC address and lease state as well.
+ */
+
+ /*
+ * If no client identifier or if we couldn't find a lease by using it,
+ * we will try look it up by the client IP address.
+ */
+
+
+ /*
+ * If found, release it.
+ */
+
+
+ /** @todo this is not required in the initial implementation, do it later. */
+ return true;
+}
+
diff --git a/src/VBox/NetworkServices/DHCP/README.customoptions b/src/VBox/NetworkServices/DHCP/README.customoptions
new file mode 100644
index 00000000..c121d318
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/README.customoptions
@@ -0,0 +1,23 @@
+To configure custom DHCP options for a VM use the following command adapted
+to your needs:
+
+$ VBoxManage dhcpserver modify \
+ --netname test-0 --options --vm "Test Client" --slot 0 \
+ --id 0 --value "224=c0:a8:02:01:c0:a8:02:02" \
+ --id 0 --value "225=0:0"
+
+Note that custom DHCP options must be specified with ID 0 and the actual
+number in the value. This has technical reasons which may change in future
+VirtualBox releases.
+
+
+It corresponds to the following bit of ISC 'dhcpd.conf':
+
+option sample1 code 224 = array of ip-address;
+option sample2 code 225 = array of integer 8;
+
+...
+ option sample1 192.168.2.1,192.168.2.2;
+ option sample2 0,0;
+...
+
diff --git a/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp b/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp
new file mode 100644
index 00000000..8c543272
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp
@@ -0,0 +1,885 @@
+/* $Id: VBoxNetDHCP.cpp $ */
+/** @file
+ * VBoxNetDHCP - DHCP Service for connecting to IntNet.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @page pg_net_dhcp VBoxNetDHCP
+ *
+ * Write a few words...
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/com.h>
+#include <VBox/com/listeners.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/EventQueue.h>
+#include <VBox/com/VirtualBox.h>
+
+#include <iprt/alloca.h>
+#include <iprt/buildconfig.h>
+#include <iprt/err.h>
+#include <iprt/net.h> /* must come before getopt */
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/time.h>
+#include <iprt/string.h>
+#ifdef RT_OS_WINDOWS
+# include <iprt/thread.h>
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/version.h>
+
+#include "../NetLib/VBoxNetLib.h"
+#include "../NetLib/shared_ptr.h"
+
+#include <vector>
+#include <list>
+#include <string>
+#include <map>
+
+#include "../NetLib/VBoxNetBaseService.h"
+#include "../NetLib/utils.h"
+
+#ifdef RT_OS_WINDOWS /* WinMain */
+# include <iprt/win/windows.h>
+# include <stdlib.h>
+# ifdef INET_ADDRSTRLEN
+/* On Windows INET_ADDRSTRLEN defined as 22 Ws2ipdef.h, because it include port number */
+# undef INET_ADDRSTRLEN
+# endif
+# define INET_ADDRSTRLEN 16
+#else
+# include <netinet/in.h>
+#endif
+
+
+#include "Config.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * DHCP server instance.
+ */
+class VBoxNetDhcp : public VBoxNetBaseService, public NATNetworkEventAdapter
+{
+public:
+ VBoxNetDhcp();
+ virtual ~VBoxNetDhcp();
+
+ int init();
+ void done();
+ void usage(void) { /* XXX: document options */ };
+ int parseOpt(int rc, const RTGETOPTUNION& getOptVal);
+ int processFrame(void *, size_t) {return VERR_IGNORED; };
+ int processGSO(PCPDMNETWORKGSO, size_t) {return VERR_IGNORED; };
+ int processUDP(void *, size_t);
+
+protected:
+ bool handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb);
+
+ void debugPrintV(int32_t iMinLevel, bool fMsg, const char *pszFmt, va_list va) const;
+ static const char *debugDhcpName(uint8_t uMsgType);
+
+private:
+ int initNoMain();
+ int initWithMain();
+ HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent);
+
+ static int hostDnsServers(const ComHostPtr& host,
+ const RTNETADDRIPV4& networkid,
+ const AddressToOffsetMapping& mapping,
+ AddressList& servers);
+ int fetchAndUpdateDnsInfo();
+
+protected:
+ /** @name The DHCP server specific configuration data members.
+ * @{ */
+ /*
+ * XXX: what was the plan? SQL3 or plain text file?
+ * How it will coexists with managment from VBoxManagement, who should manage db
+ * in that case (VBoxManage, VBoxSVC ???)
+ */
+ std::string m_LeaseDBName;
+
+ /** @} */
+
+ /* corresponding dhcp server description in Main */
+ ComPtr<IDHCPServer> m_DhcpServer;
+
+ ComPtr<INATNetwork> m_NATNetwork;
+
+ /** Listener for Host DNS changes */
+ ComNatListenerPtr m_VBoxListener;
+ ComNatListenerPtr m_VBoxClientListener;
+
+ NetworkManager *m_NetworkManager;
+
+ /*
+ * We will ignore cmd line parameters IFF there will be some DHCP specific arguments
+ * otherwise all paramters will come from Main.
+ */
+ bool m_fIgnoreCmdLineParameters;
+
+ /*
+ * -b -n 10.0.1.2 -m 255.255.255.0 -> to the list processing in
+ */
+ typedef struct
+ {
+ char Key;
+ std::string strValue;
+ } CMDLNPRM;
+ std::list<CMDLNPRM> CmdParameterll;
+ typedef std::list<CMDLNPRM>::iterator CmdParameterIterator;
+
+ /** @name Debug stuff
+ * @{ */
+ int32_t m_cVerbosity;
+ uint8_t m_uCurMsgType;
+ size_t m_cbCurMsg;
+ PCRTNETBOOTP m_pCurMsg;
+ VBOXNETUDPHDRS m_CurHdrs;
+ /** @} */
+};
+
+
+static inline int configGetBoundryAddress(const ComDhcpServerPtr& dhcp, bool fUpperBoundry, RTNETADDRIPV4& boundryAddress)
+{
+ boundryAddress.u = INADDR_ANY;
+
+ HRESULT hrc;
+ com::Bstr strAddress;
+ if (fUpperBoundry)
+ hrc = dhcp->COMGETTER(UpperIP)(strAddress.asOutParam());
+ else
+ hrc = dhcp->COMGETTER(LowerIP)(strAddress.asOutParam());
+ AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
+
+ return RTNetStrToIPv4Addr(com::Utf8Str(strAddress).c_str(), &boundryAddress);
+}
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to the DHCP server. */
+static VBoxNetDhcp *g_pDhcp;
+
+/* DHCP server specific options */
+static RTGETOPTDEF g_aOptionDefs[] =
+{
+ { "--lease-db", 'D', RTGETOPT_REQ_STRING },
+ { "--begin-config", 'b', RTGETOPT_REQ_NOTHING },
+ { "--gateway", 'g', RTGETOPT_REQ_IPV4ADDR },
+ { "--lower-ip", 'l', RTGETOPT_REQ_IPV4ADDR },
+ { "--upper-ip", 'u', RTGETOPT_REQ_IPV4ADDR },
+};
+
+/**
+ * Construct a DHCP server with a default configuration.
+ */
+VBoxNetDhcp::VBoxNetDhcp()
+ : VBoxNetBaseService("VBoxNetDhcp", "VBoxNetDhcp"),
+ m_NetworkManager(NULL)
+{
+ /* m_enmTrunkType = kIntNetTrunkType_WhateverNone; */
+ RTMAC mac;
+ mac.au8[0] = 0x08;
+ mac.au8[1] = 0x00;
+ mac.au8[2] = 0x27;
+ mac.au8[3] = 0x40;
+ mac.au8[4] = 0x41;
+ mac.au8[5] = 0x42;
+ setMacAddress(mac);
+
+ RTNETADDRIPV4 address;
+ address.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 5)));
+ setIpv4Address(address);
+
+ setSendBufSize(8 * _1K);
+ setRecvBufSize(50 * _1K);
+
+ m_uCurMsgType = UINT8_MAX;
+ m_cbCurMsg = 0;
+ m_pCurMsg = NULL;
+ RT_ZERO(m_CurHdrs);
+
+ m_fIgnoreCmdLineParameters = true;
+
+ for(unsigned int i = 0; i < RT_ELEMENTS(g_aOptionDefs); ++i)
+ addCommandLineOption(&g_aOptionDefs[i]);
+}
+
+
+/**
+ * Destruct a DHCP server.
+ */
+VBoxNetDhcp::~VBoxNetDhcp()
+{
+}
+
+
+
+
+/**
+ * Parse the DHCP specific arguments.
+ *
+ * This callback caled for each paramenter so
+ * ....
+ * we nee post analisys of the parameters, at least
+ * for -b, -g, -l, -u, -m
+ */
+int VBoxNetDhcp::parseOpt(int rc, const RTGETOPTUNION& Val)
+{
+ CMDLNPRM prm;
+
+ /* Ok, we've entered here, thus we can't ignore cmd line parameters anymore */
+ m_fIgnoreCmdLineParameters = false;
+
+ prm.Key = rc;
+
+ switch (rc)
+ {
+ case 'l':
+ case 'u':
+ case 'g':
+ {
+ char buf[17];
+ RTStrPrintf(buf, 17, "%RTnaipv4", Val.IPv4Addr.u);
+ prm.strValue = buf;
+ CmdParameterll.push_back(prm);
+ }
+ break;
+
+ case 'b': // ignore
+ case 'D': // ignore
+ break;
+
+ default:
+ rc = RTGetOptPrintError(rc, &Val);
+ RTPrintf("Use --help for more information.\n");
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+int VBoxNetDhcp::init()
+{
+ int rc = this->VBoxNetBaseService::init();
+ AssertRCReturn(rc, rc);
+
+ if (isMainNeeded())
+ rc = initWithMain();
+ else
+ rc = initNoMain();
+ AssertRCReturn(rc, rc);
+
+ m_NetworkManager = NetworkManager::getNetworkManager(m_DhcpServer);
+ AssertPtrReturn(m_NetworkManager, VERR_INTERNAL_ERROR);
+
+ m_NetworkManager->setOurAddress(getIpv4Address());
+ m_NetworkManager->setOurNetmask(getIpv4Netmask());
+ m_NetworkManager->setOurMac(getMacAddress());
+ m_NetworkManager->setService(this);
+
+ return VINF_SUCCESS;
+}
+
+void VBoxNetDhcp::done()
+{
+ destroyNatListener(m_VBoxListener, virtualbox);
+ destroyClientListener(m_VBoxClientListener, virtualboxClient);
+}
+
+int VBoxNetDhcp::processUDP(void *pv, size_t cbPv)
+{
+ PCRTNETBOOTP pDhcpMsg = (PCRTNETBOOTP)pv;
+ m_pCurMsg = pDhcpMsg;
+ m_cbCurMsg = cbPv;
+
+ uint8_t uMsgType;
+ if (RTNetIPv4IsDHCPValid(NULL /* why is this here? */, pDhcpMsg, cbPv, &uMsgType))
+ {
+ m_uCurMsgType = uMsgType;
+ {
+ /* To avoid fight with event processing thread */
+ VBoxNetALock(this);
+ handleDhcpMsg(uMsgType, pDhcpMsg, cbPv);
+ }
+ m_uCurMsgType = UINT8_MAX;
+ }
+ else
+ debugPrint(1, true, "VBoxNetDHCP: Skipping invalid DHCP packet.\n"); /** @todo handle pure bootp clients too? */
+
+ m_pCurMsg = NULL;
+ m_cbCurMsg = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Handles a DHCP message.
+ *
+ * @returns true if handled, false if not. (IGNORED BY CALLER)
+ * @param uMsgType The message type.
+ * @param pDhcpMsg The DHCP message.
+ * @param cb The size of the DHCP message.
+ */
+bool VBoxNetDhcp::handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ if (pDhcpMsg->bp_op == RTNETBOOTP_OP_REQUEST)
+ {
+ AssertPtrReturn(m_NetworkManager, false);
+
+ switch (uMsgType)
+ {
+ case RTNET_DHCP_MT_DISCOVER:
+ return m_NetworkManager->handleDhcpReqDiscover(pDhcpMsg, cb);
+
+ case RTNET_DHCP_MT_REQUEST:
+ return m_NetworkManager->handleDhcpReqRequest(pDhcpMsg, cb);
+
+ case RTNET_DHCP_MT_DECLINE:
+ return m_NetworkManager->handleDhcpReqDecline(pDhcpMsg, cb);
+
+ case RTNET_DHCP_MT_RELEASE:
+ return m_NetworkManager->handleDhcpReqRelease(pDhcpMsg, cb);
+
+ case RTNET_DHCP_MT_INFORM:
+ debugPrint(0, true, "Should we handle this?");
+ break;
+
+ default:
+ debugPrint(0, true, "Unexpected.");
+ break;
+ }
+ }
+ return false;
+}
+
+/**
+ * Print debug message depending on the m_cVerbosity level.
+ *
+ * @param iMinLevel The minimum m_cVerbosity level for this message.
+ * @param fMsg Whether to dump parts for the current DHCP message.
+ * @param pszFmt The message format string.
+ * @param va Optional arguments.
+ */
+void VBoxNetDhcp::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const
+{
+ if (iMinLevel <= m_cVerbosity)
+ {
+ va_list vaCopy; /* This dude is *very* special, thus the copy. */
+ va_copy(vaCopy, va);
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: %s: %N\n", iMinLevel >= 2 ? "debug" : "info", pszFmt, &vaCopy);
+ va_end(vaCopy);
+
+ if ( fMsg
+ && m_cVerbosity >= 2
+ && m_pCurMsg)
+ {
+ /* XXX: export this to debugPrinfDhcpMsg or variant and other method export
+ * to base class
+ */
+ const char *pszMsg = m_uCurMsgType != UINT8_MAX ? debugDhcpName(m_uCurMsgType) : "";
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: debug: %8s chaddr=%.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d siaddr=%d.%d.%d.%d xid=%#x\n",
+ pszMsg,
+ &m_pCurMsg->bp_chaddr,
+ m_pCurMsg->bp_ciaddr.au8[0], m_pCurMsg->bp_ciaddr.au8[1], m_pCurMsg->bp_ciaddr.au8[2], m_pCurMsg->bp_ciaddr.au8[3],
+ m_pCurMsg->bp_yiaddr.au8[0], m_pCurMsg->bp_yiaddr.au8[1], m_pCurMsg->bp_yiaddr.au8[2], m_pCurMsg->bp_yiaddr.au8[3],
+ m_pCurMsg->bp_siaddr.au8[0], m_pCurMsg->bp_siaddr.au8[1], m_pCurMsg->bp_siaddr.au8[2], m_pCurMsg->bp_siaddr.au8[3],
+ m_pCurMsg->bp_xid);
+ }
+ }
+}
+
+
+/**
+ * Gets the name of given DHCP message type.
+ *
+ * @returns Readonly name.
+ * @param uMsgType The message number.
+ */
+/* static */ const char *VBoxNetDhcp::debugDhcpName(uint8_t uMsgType)
+{
+ switch (uMsgType)
+ {
+ case 0: return "MT_00";
+ case RTNET_DHCP_MT_DISCOVER: return "DISCOVER";
+ case RTNET_DHCP_MT_OFFER: return "OFFER";
+ case RTNET_DHCP_MT_REQUEST: return "REQUEST";
+ case RTNET_DHCP_MT_DECLINE: return "DECLINE";
+ case RTNET_DHCP_MT_ACK: return "ACK";
+ case RTNET_DHCP_MT_NAC: return "NAC";
+ case RTNET_DHCP_MT_RELEASE: return "RELEASE";
+ case RTNET_DHCP_MT_INFORM: return "INFORM";
+ case 9: return "MT_09";
+ case 10: return "MT_0a";
+ case 11: return "MT_0b";
+ case 12: return "MT_0c";
+ case 13: return "MT_0d";
+ case 14: return "MT_0e";
+ case 15: return "MT_0f";
+ case 16: return "MT_10";
+ case 17: return "MT_11";
+ case 18: return "MT_12";
+ case 19: return "MT_13";
+ case UINT8_MAX: return "MT_ff";
+ default: return "UNKNOWN";
+ }
+}
+
+
+int VBoxNetDhcp::initNoMain()
+{
+ CmdParameterIterator it;
+
+ RTNETADDRIPV4 address = getIpv4Address();
+ RTNETADDRIPV4 netmask = getIpv4Netmask();
+ RTNETADDRIPV4 networkId;
+ networkId.u = address.u & netmask.u;
+
+ RTNETADDRIPV4 UpperAddress;
+ RTNETADDRIPV4 LowerAddress = networkId;
+ UpperAddress.u = RT_H2N_U32(RT_N2H_U32(LowerAddress.u) | RT_N2H_U32(netmask.u));
+
+ for (it = CmdParameterll.begin(); it != CmdParameterll.end(); ++it)
+ {
+ switch(it->Key)
+ {
+ case 'l':
+ RTNetStrToIPv4Addr(it->strValue.c_str(), &LowerAddress);
+ break;
+
+ case 'u':
+ RTNetStrToIPv4Addr(it->strValue.c_str(), &UpperAddress);
+ break;
+ case 'b':
+ break;
+
+ }
+ }
+
+ ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager();
+ AssertPtrReturn(confManager, VERR_INTERNAL_ERROR);
+ confManager->addNetwork(unconst(g_RootConfig),
+ networkId,
+ netmask,
+ LowerAddress,
+ UpperAddress);
+
+ return VINF_SUCCESS;
+}
+
+
+int VBoxNetDhcp::initWithMain()
+{
+ /* ok, here we should initiate instance of dhcp server
+ * and listener for Dhcp configuration events
+ */
+ AssertRCReturn(virtualbox.isNull(), VERR_INTERNAL_ERROR);
+ std::string networkName = getNetworkName();
+
+ int rc = findDhcpServer(virtualbox, networkName, m_DhcpServer);
+ AssertRCReturn(rc, rc);
+
+ rc = findNatNetwork(virtualbox, networkName, m_NATNetwork);
+ AssertRCReturn(rc, rc);
+
+ BOOL fNeedDhcpServer = isDhcpRequired(m_NATNetwork);
+ if (!fNeedDhcpServer)
+ return VERR_CANCELLED;
+
+ RTNETADDRIPV4 gateway;
+ com::Bstr strGateway;
+ HRESULT hrc = m_NATNetwork->COMGETTER(Gateway)(strGateway.asOutParam());
+ AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
+ RTNetStrToIPv4Addr(com::Utf8Str(strGateway).c_str(), &gateway);
+
+ ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager();
+ AssertPtrReturn(confManager, VERR_INTERNAL_ERROR);
+ confManager->addToAddressList(RTNET_DHCP_OPT_ROUTERS, gateway);
+
+ rc = fetchAndUpdateDnsInfo();
+ AssertMsgRCReturn(rc, ("Wasn't able to fetch Dns info"), rc);
+
+ {
+ ComEventTypeArray eventTypes;
+ eventTypes.push_back(VBoxEventType_OnHostNameResolutionConfigurationChange);
+ eventTypes.push_back(VBoxEventType_OnNATNetworkStartStop);
+ rc = createNatListener(m_VBoxListener, virtualbox, this, eventTypes);
+ AssertRCReturn(rc, rc);
+ }
+
+ {
+ ComEventTypeArray eventTypes;
+ eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
+ rc = createClientListener(m_VBoxClientListener, virtualboxClient, this, eventTypes);
+ AssertRCReturn(rc, rc);
+ }
+
+ RTNETADDRIPV4 LowerAddress;
+ rc = configGetBoundryAddress(m_DhcpServer, false, LowerAddress);
+ AssertMsgRCReturn(rc, ("can't get lower boundrary adderss'"),rc);
+
+ RTNETADDRIPV4 UpperAddress;
+ rc = configGetBoundryAddress(m_DhcpServer, true, UpperAddress);
+ AssertMsgRCReturn(rc, ("can't get upper boundrary adderss'"),rc);
+
+ RTNETADDRIPV4 address = getIpv4Address();
+ RTNETADDRIPV4 netmask = getIpv4Netmask();
+ RTNETADDRIPV4 networkId = networkid(address, netmask);
+ std::string name = std::string("default");
+
+ confManager->addNetwork(unconst(g_RootConfig),
+ networkId,
+ netmask,
+ LowerAddress,
+ UpperAddress);
+
+ com::Bstr bstr;
+ hrc = virtualbox->COMGETTER(HomeFolder)(bstr.asOutParam());
+ com::Utf8StrFmt strXmlLeaseFile("%ls%c%s.leases",
+ bstr.raw(), RTPATH_DELIMITER, networkName.c_str());
+ confManager->loadFromFile(strXmlLeaseFile);
+
+ return VINF_SUCCESS;
+}
+
+
+int VBoxNetDhcp::fetchAndUpdateDnsInfo()
+{
+ ComHostPtr host;
+ if (SUCCEEDED(virtualbox->COMGETTER(Host)(host.asOutParam())))
+ {
+ AddressToOffsetMapping mapIp4Addr2Off;
+ int rc = localMappings(m_NATNetwork, mapIp4Addr2Off);
+ /* XXX: here could be several cases: 1. COM error, 2. not found (empty) 3. ? */
+ AssertMsgRCReturn(rc, ("Can't fetch local mappings"), rc);
+
+ RTNETADDRIPV4 address = getIpv4Address();
+ RTNETADDRIPV4 netmask = getIpv4Netmask();
+
+ AddressList nameservers;
+ rc = hostDnsServers(host, networkid(address, netmask), mapIp4Addr2Off, nameservers);
+ AssertMsgRCReturn(rc, ("Debug me!!!"), rc);
+ /* XXX: Search strings */
+
+ std::string domain;
+ rc = hostDnsDomain(host, domain);
+ AssertMsgRCReturn(rc, ("Debug me!!"), rc);
+
+ {
+ VBoxNetALock(this);
+ ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager();
+ confManager->flushAddressList(RTNET_DHCP_OPT_DNS);
+
+ for (AddressList::iterator it = nameservers.begin(); it != nameservers.end(); ++it)
+ confManager->addToAddressList(RTNET_DHCP_OPT_DNS, *it);
+
+ confManager->setString(RTNET_DHCP_OPT_DOMAIN_NAME, domain);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int VBoxNetDhcp::hostDnsServers(const ComHostPtr& host,
+ const RTNETADDRIPV4& networkid,
+ const AddressToOffsetMapping& mapping,
+ AddressList& servers)
+{
+ ComBstrArray strs;
+
+ HRESULT hrc = host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(strs));
+ if (FAILED(hrc))
+ return VERR_NOT_FOUND;
+
+ /*
+ * Recent fashion is to run dnsmasq on 127.0.1.1 which we
+ * currently can't map. If that's the only nameserver we've got,
+ * we need to use DNS proxy for VMs to reach it.
+ */
+ bool fUnmappedLoopback = false;
+
+ for (size_t i = 0; i < strs.size(); ++i)
+ {
+ RTNETADDRIPV4 addr;
+ int rc;
+
+ rc = RTNetStrToIPv4Addr(com::Utf8Str(strs[i]).c_str(), &addr);
+ if (RT_FAILURE(rc))
+ continue;
+
+ if (addr.u == INADDR_ANY)
+ {
+ /*
+ * This doesn't seem to be very well documented except for
+ * RTFS of res_init.c, but INADDR_ANY is a valid value for
+ * for "nameserver".
+ */
+ addr.u = RT_H2N_U32_C(INADDR_LOOPBACK);
+ }
+
+ if (addr.au8[0] == 127)
+ {
+ AddressToOffsetMapping::const_iterator remap(mapping.find(addr));
+
+ if (remap != mapping.end())
+ {
+ int offset = remap->second;
+ addr.u = RT_H2N_U32(RT_N2H_U32(networkid.u) + offset);
+ }
+ else
+ {
+ fUnmappedLoopback = true;
+ continue;
+ }
+ }
+
+ servers.push_back(addr);
+ }
+
+ if (servers.empty() && fUnmappedLoopback)
+ {
+ RTNETADDRIPV4 proxy;
+
+ proxy.u = networkid.u | RT_H2N_U32_C(1U);
+ servers.push_back(proxy);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+HRESULT VBoxNetDhcp::HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent)
+{
+ switch (aEventType)
+ {
+ case VBoxEventType_OnHostNameResolutionConfigurationChange:
+ fetchAndUpdateDnsInfo();
+ break;
+
+ case VBoxEventType_OnNATNetworkStartStop:
+ {
+ ComPtr <INATNetworkStartStopEvent> pStartStopEvent = pEvent;
+
+ com::Bstr networkName;
+ HRESULT hrc = pStartStopEvent->COMGETTER(NetworkName)(networkName.asOutParam());
+ AssertComRCReturn(hrc, hrc);
+ if (networkName.compare(getNetworkName().c_str()))
+ break; /* change not for our network */
+
+ BOOL fStart = TRUE;
+ hrc = pStartStopEvent->COMGETTER(StartEvent)(&fStart);
+ AssertComRCReturn(hrc, hrc);
+ if (!fStart)
+ shutdown();
+ break;
+ }
+
+ case VBoxEventType_OnVBoxSVCAvailabilityChanged:
+ {
+ shutdown();
+ break;
+ }
+
+ default: break; /* Shut up MSC. */
+ }
+
+ return S_OK;
+}
+
+#ifdef RT_OS_WINDOWS
+
+/** The class name for the DIFx-killable window. */
+static WCHAR g_wszWndClassName[] = L"VBoxNetDHCPClass";
+/** Whether to exit the process on quit. */
+static bool g_fExitProcessOnQuit = true;
+
+/**
+ * Window procedure for making us DIFx-killable.
+ */
+static LRESULT CALLBACK DIFxKillableWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WM_DESTROY)
+ {
+ PostQuitMessage(0);
+ return 0;
+ }
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+/** @callback_method_impl{FNRTTHREAD,
+ * Thread that creates service a window the DIFx can destroy, thereby
+ * triggering process termination. }
+ */
+static DECLCALLBACK(int) DIFxKillableProcessThreadProc(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf, pvUser);
+ HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
+
+ /* Register the Window Class. */
+ WNDCLASSW WndCls;
+ WndCls.style = 0;
+ WndCls.lpfnWndProc = DIFxKillableWindowProc;
+ WndCls.cbClsExtra = 0;
+ WndCls.cbWndExtra = sizeof(void *);
+ WndCls.hInstance = hInstance;
+ WndCls.hIcon = NULL;
+ WndCls.hCursor = NULL;
+ WndCls.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ WndCls.lpszMenuName = NULL;
+ WndCls.lpszClassName = g_wszWndClassName;
+
+ ATOM atomWindowClass = RegisterClassW(&WndCls);
+ if (atomWindowClass != 0)
+ {
+ /* Create the window. */
+ HWND hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
+ g_wszWndClassName, g_wszWndClassName,
+ WS_POPUPWINDOW,
+ -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
+ if (hwnd)
+ {
+ SetWindowPos(hwnd, HWND_TOPMOST, -200, -200, 0, 0,
+ SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
+
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DestroyWindow(hwnd);
+ }
+
+ UnregisterClassW(g_wszWndClassName, hInstance);
+
+ if (hwnd && g_fExitProcessOnQuit)
+ exit(0);
+ }
+ return 0;
+}
+
+#endif /* RT_OS_WINDOWS */
+
+/**
+ * Entry point.
+ */
+extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv)
+{
+ /*
+ * Instantiate the DHCP server and hand it the options.
+ */
+ VBoxNetDhcp *pDhcp = new VBoxNetDhcp();
+ if (!pDhcp)
+ {
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: new VBoxNetDhcp failed!\n");
+ return 1;
+ }
+
+ RTEXITCODE rcExit = (RTEXITCODE)pDhcp->parseArgs(argc - 1, argv + 1);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+#ifdef RT_OS_WINDOWS
+ /* DIFx hack. */
+ RTTHREAD hMakeUseKillableThread = NIL_RTTHREAD;
+ int rc2 = RTThreadCreate(&hMakeUseKillableThread, DIFxKillableProcessThreadProc, NULL, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "DIFxKill");
+ if (RT_FAILURE(rc2))
+ hMakeUseKillableThread = NIL_RTTHREAD;
+#endif
+
+ pDhcp->init();
+
+ /*
+ * Try connect the server to the network.
+ */
+ int rc = pDhcp->tryGoOnline();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Process requests.
+ */
+ g_pDhcp = pDhcp;
+ rc = pDhcp->run();
+ pDhcp->done();
+
+ g_pDhcp = NULL;
+ }
+ delete pDhcp;
+
+#ifdef RT_OS_WINDOWS
+ /* Kill DIFx hack. */
+ if (hMakeUseKillableThread != NIL_RTTHREAD)
+ {
+ g_fExitProcessOnQuit = false;
+ PostThreadMessage((DWORD)RTThreadGetNative(hMakeUseKillableThread), WM_QUIT, 0, 0);
+ RTThreadWait(hMakeUseKillableThread, RT_MS_1SEC * 5U, NULL);
+ }
+#endif
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+#ifndef VBOX_WITH_HARDENING
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ return TrustedMain(argc, argv);
+}
+
+# ifdef RT_OS_WINDOWS
+
+
+
+/** (We don't want a console usually.) */
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+{
+ NOREF(hInstance); NOREF(hPrevInstance); NOREF(lpCmdLine); NOREF(nCmdShow);
+ return main(__argc, __argv);
+}
+# endif /* RT_OS_WINDOWS */
+
+#endif /* !VBOX_WITH_HARDENING */
+
diff --git a/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.rc b/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.rc
new file mode 100644
index 00000000..5a8b64aa
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.rc
@@ -0,0 +1,55 @@
+/* $Id: VBoxNetDHCP.rc $ */
+/** @file
+ * VBoxNetDHCP - Resource file containing version info.
+ */
+
+/*
+ * Copyright (C) 2015-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileDescription", "VirtualBox DHCP Server\0"
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "InternalName", "VBoxNetDHCP\0"
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "OriginalFilename", "VBoxNetDHCP.dll\0"
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+/* Creates the application icon. */
+#include "VBoxNetDHCP-icon.rc"
+
diff --git a/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp b/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp
new file mode 100644
index 00000000..8ed60a98
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp
@@ -0,0 +1,25 @@
+/* $Id: VBoxNetDHCPHardened.cpp $ */
+/** @file
+ * VBoxNetDHCP - Hardened main().
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <VBox/sup.h>
+
+
+int main(int argc, char **argv, char **envp)
+{
+ return SUPR3HardenedMain("VBoxNetDHCP", 0 /* fFlags */, argc, argv, envp);
+}
+