Index: /trunk/src/VBox/Main/UnattendedTemplates/ubuntu_preseed.cfg
===================================================================
--- /trunk/src/VBox/Main/UnattendedTemplates/ubuntu_preseed.cfg	(revision 79609)
+++ /trunk/src/VBox/Main/UnattendedTemplates/ubuntu_preseed.cfg	(revision 79610)
@@ -46,4 +46,6 @@
 d-i apt-setup/universe boolean true
 d-i pkgsel/install-language-support boolean false
+# Stuff we need to build additions modules:
+d-i pkgsel/include string build-essential linux-headers-generic perl make
 
 # Users
Index: /trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 79609)
+++ /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 79610)
@@ -1834,5 +1834,5 @@
     uuid="ea2d467f-b6c2-4b9a-8eb5-6e2f275dd72e"
     wsmap="managed"
-    reservedMethods="2" reservedAttributes="6"
+    reservedMethods="1" reservedAttributes="6"
     >
     <desc>
@@ -2043,4 +2043,43 @@
       </desc>
     </method>
+
+    <method name="findLeaseByMAC">
+      <desc>
+        Queries the persistent lease database by MAC address.
+
+        This is handy if the host wants to connect to a server running inside
+        a VM on a host only network.
+
+        <result name="VBOX_E_OBJECT_NOT_FOUND">If MAC address not in the database.</result>
+        <result name="VBOX_E_FILE_ERROR">If not able to read the lease database file.</result>
+      </desc>
+      <param name="mac" type="wstring" dir="in">
+        <desc>The MAC address to look up.</desc>
+      </param>
+      <param name="type" type="long" dir="in">
+        <desc>Reserved, MBZ.</desc>
+      </param>
+      <param name="address" type="wstring" dir="out">
+        <desc>The assigned address.</desc>
+      </param>
+      <param name="state" type="wstring" dir="out">
+        <desc>The lease state.</desc>
+      </param>
+      <param name="issued" type="long long" dir="out">
+        <desc>Timestamp of when the lease was issued, in seconds since 1970-01-01 UTC.</desc>
+      </param>
+      <param name="expire" type="long long" dir="out">
+        <desc>Timestamp of when the lease expires/expired, in seconds since 1970-01-01 UTC.</desc>
+      </param>
+    </method>
+
+    <!-- @todo Add variant of queryLeaseByMac that goes by VM name and optionally slot. -->
+    <!-- @todo Add min/default/max lease time settings -->
+    <!-- @todo Add lease database attribute (readonly) -->
+    <!-- @todo Add methods for complex group operations, that includes lists of mac address,
+               client ids, vendor/user class ids as selection criteria -->
+    <!-- @todo Add fake DHCP option for assigning fixed IP addresses.  Figure out a way to
+               do it for the host only trunk interface too (possible special group operation). -->
+
   </interface>
 
Index: /trunk/src/VBox/Main/include/DHCPServerImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/DHCPServerImpl.h	(revision 79609)
+++ /trunk/src/VBox/Main/include/DHCPServerImpl.h	(revision 79610)
@@ -36,5 +36,5 @@
 #endif
 
-#ifdef RT_OS_WINDOWS
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
 # define DHCP_EXECUTABLE_NAME "VBoxNetDHCP.exe"
 #else
@@ -42,9 +42,9 @@
 #endif
 
-class DHCPServerRunner: public NetworkServiceRunner
+class DHCPServerRunner : public NetworkServiceRunner
 {
 public:
-    DHCPServerRunner():NetworkServiceRunner(DHCP_EXECUTABLE_NAME){}
-    virtual ~DHCPServerRunner(){};
+    DHCPServerRunner() : NetworkServiceRunner(DHCP_EXECUTABLE_NAME) {}
+    virtual ~DHCPServerRunner() {};
 
     static const std::string kDsrKeyGateway;
@@ -68,6 +68,6 @@
  */
 
-class ATL_NO_VTABLE DHCPServer :
-    public DHCPServerWrap
+class ATL_NO_VTABLE DHCPServer
+    : public DHCPServerWrap
 {
 public:
@@ -134,4 +134,11 @@
     HRESULT stop();
     HRESULT restart();
+    HRESULT findLeaseByMAC(const com::Utf8Str &aMac, LONG aType,
+                           com::Utf8Str &aAddress, com::Utf8Str &aState, LONG64 *aIssued, LONG64 *aExpire) RT_OVERRIDE;
+
+    /** @name Helpers
+     * @{  */
+    HRESULT i_calcLeaseFilename(const com::Utf8Str &aNetwork) RT_NOEXCEPT;
+    /** @} */
 
     struct Data;
Index: /trunk/src/VBox/Main/src-server/DHCPServerImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/DHCPServerImpl.cpp	(revision 79609)
+++ /trunk/src/VBox/Main/src-server/DHCPServerImpl.cpp	(revision 79610)
@@ -26,4 +26,5 @@
 #include <iprt/net.h>
 #include <iprt/path.h>
+#include <iprt/cpp/path.h>
 #include <iprt/cpp/utils.h>
 #include <iprt/cpp/xml.h>
@@ -36,4 +37,5 @@
 // constructor / destructor
 /////////////////////////////////////////////////////////////////////////////
+/** @todo Convert to C strings as this is wastefull:    */
 const std::string DHCPServerRunner::kDsrKeyGateway = "--gateway";
 const std::string DHCPServerRunner::kDsrKeyLowerIp = "--lower-ip";
@@ -64,4 +66,5 @@
 
     char tempConfigFileName[RTPATH_MAX];
+    com::Utf8Str strLeaseFilename;
     com::Utf8Str networkName;
     com::Utf8Str trunkName;
@@ -665,9 +668,10 @@
 {
     if (!m->dhcp.isRunning())
-        return E_FAIL;
+        return setErrorBoth(E_FAIL, VERR_PROCESS_NOT_FOUND, tr("not running"));
+
     /*
-        * Disabled servers will be brought down, but won't be restarted.
-        * (see DHCPServer::start)
-        */
+     * Disabled servers will be brought down, but won't be restarted.
+     * (see DHCPServer::start)
+     */
     HRESULT hrc = stop();
     if (SUCCEEDED(hrc))
@@ -685,18 +689,28 @@
         return S_OK;
 
-    /*
+    /**
      * @todo: the existing code cannot handle concurrent attempts to start DHCP server.
      * Note that technically it may receive different parameters from different callers.
      */
     m->networkName = aNetworkName;
-    m->trunkName = aTrunkName;
-    m->trunkType = aTrunkType;
-
-    m->dhcp.clearOptions();
+    m->trunkName   = aTrunkName;
+    m->trunkType   = aTrunkType;
+    HRESULT hrc = i_calcLeaseFilename(aNetworkName);
+    if (FAILED(hrc))
+        return hrc;
+
+    m->dhcp.clearOptions(); /* (Not DHCP options, but command line options for the service) */
+
 #ifdef VBOX_WITH_DHCPD
+
+    /*
+     * Create configuration file path.
+     */
+    /** @todo put this next to the leases file.   */
     int rc = RTPathTemp(m->tempConfigFileName, sizeof(m->tempConfigFileName));
-    if (RT_FAILURE(rc))
-        return E_FAIL;
-    rc = RTPathAppend(m->tempConfigFileName, sizeof(m->tempConfigFileName), "dhcp-config-XXXXX.xml");
+    if (RT_SUCCESS(rc))
+        rc = RTPathAppend(m->tempConfigFileName, sizeof(m->tempConfigFileName), "dhcp-config-XXXXX.xml");
+    if (RT_SUCCESS(rc))
+        rc = RTFileCreateTemp(m->tempConfigFileName, 0600);
     if (RT_FAILURE(rc))
     {
@@ -704,37 +718,32 @@
         return E_FAIL;
     }
-    rc = RTFileCreateTemp(m->tempConfigFileName, 0600);
-    if (RT_FAILURE(rc))
-    {
-        m->tempConfigFileName[0] = '\0';
-        return E_FAIL;
-    }
-
+
+    /*
+     * Produce the DHCP server configuration.
+     */
     xml::Document doc;
     xml::ElementNode *pElmRoot = doc.createRootElement("DHCPServer");
-    pElmRoot->setAttribute("networkName", m->networkName.c_str());
-    if (!m->trunkName.isEmpty())
-        pElmRoot->setAttribute("trunkName", m->trunkName.c_str());
-    pElmRoot->setAttribute("trunkType", m->trunkType.c_str());
-    pElmRoot->setAttribute("IPAddress",  Utf8Str(m->IPAddress).c_str());
-    pElmRoot->setAttribute("networkMask", Utf8Str(m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text).c_str());
-    pElmRoot->setAttribute("lowerIP", Utf8Str(m->lowerIP).c_str());
-    pElmRoot->setAttribute("upperIP", Utf8Str(m->upperIP).c_str());
+    pElmRoot->setAttribute("networkName", m->networkName);
+    if (m->trunkName.isNotEmpty())
+        pElmRoot->setAttribute("trunkName", m->trunkName);
+    pElmRoot->setAttribute("trunkType", m->trunkType);
+    pElmRoot->setAttribute("IPAddress",  m->IPAddress);
+    pElmRoot->setAttribute("networkMask", m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text);
+    pElmRoot->setAttribute("lowerIP", m->lowerIP);
+    pElmRoot->setAttribute("upperIP", m->upperIP);
+    pElmRoot->setAttribute("leaseFilename", m->strLeaseFilename);
 
     /* Process global options */
     xml::ElementNode *pOptions = pElmRoot->createChild("Options");
-    // settings::DhcpOptionMap::const_iterator itGlobal;
-    for (settings::DhcpOptionMap::const_iterator it = m->GlobalDhcpOptions.begin();
-         it != m->GlobalDhcpOptions.end();
-         ++it)
+    for (settings::DhcpOptionMap::const_iterator it = m->GlobalDhcpOptions.begin(); it != m->GlobalDhcpOptions.end(); ++it)
         addOptionChild(pOptions, (*it).first, (*it).second);
 
     /* Process network-adapter-specific options */
     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-    HRESULT hrc = S_OK;
+    hrc = S_OK;
     ComPtr<IMachine> machine;
     ComPtr<INetworkAdapter> nic;
     settings::VmSlot2OptionsIterator it;
-    for(it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it)
+    for (it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it)
     {
         alock.release();
@@ -782,8 +791,9 @@
 
     xml::XmlFileWriter writer(doc);
-    writer.write(m->tempConfigFileName, true);
-
-    m->dhcp.setOption(DHCPServerRunner::kDsrKeyConfig, m->tempConfigFileName);
+    writer.write(m->tempConfigFileName, false);
+
+    m->dhcp.setOption(DHCPServerRunner::kDsrKeyConfig, m->tempConfigFileName);    /* command line options, not dhcp ones. */
     m->dhcp.setOption(DHCPServerRunner::kDsrKeyComment, m->networkName.c_str());
+
 #else /* !VBOX_WITH_DHCPD */
     /* Main is needed for NATNetwork */
@@ -833,4 +843,170 @@
 
 
+HRESULT DHCPServer::findLeaseByMAC(const com::Utf8Str &aMac, LONG aType,
+                                    com::Utf8Str &aAddress, com::Utf8Str &aState, LONG64 *aIssued, LONG64 *aExpire)
+{
+    /* Reset output before we start */
+    *aIssued = 0;
+    *aExpire = 0;
+    aAddress.setNull();
+    aState.setNull();
+
+    /*
+     * Convert and check input.
+     */
+    RTMAC MacAddress;
+    int vrc = RTStrConvertHexBytes(aMac.c_str(), &MacAddress, sizeof(MacAddress), RTSTRCONVERTHEXBYTES_F_SEP_COLON);
+    if (vrc != VINF_SUCCESS)
+        return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid MAC address '%s': %Rrc"), aMac.c_str(), vrc);
+    if (aType != 0)
+        return setError(E_INVALIDARG, tr("flags must be zero (not %#x)"), aType);
+
+    /*
+     * Make sure we've got a lease filename to work with.
+     */
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    if (m->strLeaseFilename.isEmpty())
+    {
+        HRESULT hrc = i_calcLeaseFilename(m->networkName.isEmpty() ? mName : m->networkName);
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    /*
+     * Try at least twice to read the lease database, more if busy.
+     */
+    uint64_t const nsStart = RTTimeNanoTS();
+    for (uint32_t uReadAttempt = 0; ; uReadAttempt++)
+    {
+        /*
+         * Try read the file.
+         */
+        xml::Document doc;
+        try
+        {
+            xml::XmlFileParser parser;
+            parser.read(m->strLeaseFilename.c_str(), doc);
+        }
+        catch (const xml::EIPRTFailure &e)
+        {
+            vrc = e.rc();
+            LogThisFunc(("caught xml::EIPRTFailure: rc=%Rrc (attempt %u, msg=%s)\n", vrc, uReadAttempt, e.what()));
+            if (   (   vrc == VERR_FILE_NOT_FOUND
+                    || vrc == VERR_OPEN_FAILED
+                    || vrc == VERR_ACCESS_DENIED
+                    || vrc == VERR_SHARING_VIOLATION
+                    || vrc == VERR_READ_ERROR /*?*/)
+                && (   uReadAttempt == 0
+                    || (   uReadAttempt < 64
+                        && RTTimeNanoTS() - nsStart < RT_NS_1SEC / 4)) )
+            {
+                alock.release();
+
+                if (uReadAttempt > 0)
+                    RTThreadYield();
+                RTThreadSleep(8/*ms*/);
+
+                alock.acquire();
+                LogThisFunc(("Retrying...\n"));
+                continue;
+            }
+            return setErrorBoth(VBOX_E_FILE_ERROR, vrc, "Reading '%s' failed: %Rrc - %s",
+                                m->strLeaseFilename.c_str(), vrc, e.what());
+        }
+        catch (const RTCError &e)
+        {
+            if (e.what())
+                return setError(VBOX_E_FILE_ERROR, "Reading '%s' failed: %s", m->strLeaseFilename.c_str(), e.what());
+            return setError(VBOX_E_FILE_ERROR, "Reading '%s' failed: RTCError", m->strLeaseFilename.c_str());
+        }
+        catch (std::bad_alloc &)
+        {
+            return E_OUTOFMEMORY;
+        }
+        catch (...)
+        {
+            AssertFailed();
+            return setError(VBOX_E_FILE_ERROR, tr("Reading '%s' failed: Unexpected exception"), m->strLeaseFilename.c_str());
+        }
+
+        /*
+         * Look for that mac address.
+         */
+        xml::ElementNode *pElmRoot = doc.getRootElement();
+        if (pElmRoot && pElmRoot->nameEquals("Leases"))
+        {
+            xml::NodesLoop          it(*pElmRoot);
+            const xml::ElementNode *pElmLease;
+            while ((pElmLease = it.forAllNodes()) != NULL)
+                if (pElmLease->nameEquals("Lease"))
+                {
+                    const char *pszCurMacAddress = pElmLease->findAttributeValue("mac");
+                    RTMAC       CurMacAddress;
+                    if (   pszCurMacAddress
+                        && RT_SUCCESS(RTNetStrToMacAddr(pszCurMacAddress, &CurMacAddress))
+                        && memcmp(&CurMacAddress, &MacAddress, sizeof(MacAddress)) == 0)
+                    {
+                        /*
+                         * Found it!
+                         */
+                        xml::ElementNode const *pElmTime    = pElmLease->findChildElement("Time");
+                        int64_t                 secIssued   = 0;
+                        uint32_t                cSecsToLive = 0;
+                        if (pElmTime)
+                        {
+                            pElmTime->getAttributeValue("issued", &secIssued);
+                            pElmTime->getAttributeValue("expiration", &cSecsToLive);
+                            *aIssued = secIssued;
+                            *aExpire = secIssued + cSecsToLive;
+                        }
+                        try
+                        {
+                            aAddress = pElmLease->findChildElementAttributeValue("Address", "value");
+                            aState   = pElmLease->findAttributeValue("state");
+                        }
+                        catch (std::bad_alloc &)
+                        {
+                            return E_OUTOFMEMORY;
+                        }
+
+                        /* Check if the lease has expired in the mean time. */
+                        HRESULT hrc = S_OK;
+                        RTTIMESPEC Now;
+                        if (   (aState.equals("acked") || aState.equals("offered") || aState.isEmpty())
+                            && secIssued + cSecsToLive < RTTimeSpecGetSeconds(RTTimeNow(&Now)))
+                            hrc = aState.assignNoThrow("expired");
+                        return hrc;
+                    }
+                }
+        }
+        break;
+    }
+
+    return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Could not find a lease for %RTmac"), &MacAddress);
+}
+
+
+/**
+ * Calculates and updates the value of strLeaseFilename given @a aNetwork.
+ */
+HRESULT DHCPServer::i_calcLeaseFilename(const com::Utf8Str &aNetwork)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    /* The lease file must be the same as we used the last time, so careful when changing this code. */
+    int vrc = m->strLeaseFilename.assignNoThrow(mVirtualBox->i_homeDir());
+    if (RT_SUCCESS(vrc))
+        vrc = RTPathAppendCxx(m->strLeaseFilename, aNetwork);
+    if (RT_SUCCESS(vrc))
+        vrc = m->strLeaseFilename.appendNoThrow("-Dhcpd.leases");
+    if (RT_SUCCESS(vrc))
+    {
+        RTPathPurgeFilename(RTPathFilename(m->strLeaseFilename.mutableRaw()), RTPATH_STR_F_STYLE_HOST);
+        m->strLeaseFilename.jolt();
+        return S_OK;
+    }
+    return setErrorBoth(E_FAIL, vrc, tr("Failed to construct lease filename: %Rrc"), vrc);
+}
+
 settings::DhcpOptionMap &DHCPServer::i_findOptMapByVmNameSlot(const com::Utf8Str &aVmName,
                                                               LONG aSlot)
@@ -838,2 +1014,3 @@
     return m->VmSlot2Options[settings::VmNameSlotKey(aVmName, aSlot)];
 }
+
Index: /trunk/src/VBox/Main/src-server/NetworkServiceRunner.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/NetworkServiceRunner.cpp	(revision 79609)
+++ /trunk/src/VBox/Main/src-server/NetworkServiceRunner.cpp	(revision 79610)
@@ -16,8 +16,14 @@
  */
 
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
 #include <map>
 #include <string>
 #include "NetworkServiceRunner.h"
+
 #include <iprt/process.h>
+#include <iprt/path.h>
 #include <iprt/param.h>
 #include <iprt/env.h>
@@ -26,4 +32,8 @@
 
 
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+/** @todo Convert to C strings as this is wastefull:    */
 const std::string NetworkServiceRunner::kNsrKeyName      = "--name";
 const std::string NetworkServiceRunner::kNsrKeyNetwork   = "--network";
@@ -35,4 +45,8 @@
 const std::string NetworkServiceRunner::kNsrKeyNeedMain  = "--need-main";
 
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
 struct NetworkServiceRunner::Data
 {
@@ -44,7 +58,10 @@
     const char *mProcName;
     RTPROCESS mProcess;
-    std::map<std::string, std::string> mOptions;
+    std::map<std::string, std::string> mOptions; /**< @todo r=bird: A map for command line option/value pairs? really?
+                                                  * Wouldn't a simple argument list have done it much much more efficiently? */
     bool mKillProcOnStop;
 };
+
+
 
 NetworkServiceRunner::NetworkServiceRunner(const char *aProcName)
@@ -86,39 +103,41 @@
         return VINF_ALREADY_INITIALIZED;
 
-    const char * args[10*2];
-
-    AssertReturn(m->mOptions.size() < 10, VERR_INTERNAL_ERROR);
-
-    /* get the path to the executable */
-    char exePathBuf[RTPATH_MAX];
-    const char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
-    char *substrSl = strrchr(exePathBuf, '/');
-    char *substrBs = strrchr(exePathBuf, '\\');
-    char *suffix = substrSl ? substrSl : substrBs;
-
-    if (suffix)
+    /*
+     * Construct the path to the executable.  ASSUME it is relative to the
+     * directory that holds VBoxSVC.
+     */
+    char szExePath[RTPATH_MAX];
+    AssertReturn(RTProcGetExecutablePath(szExePath, RTPATH_MAX), VERR_FILENAME_TOO_LONG);
+    RTPathStripFilename(szExePath);
+    int vrc = RTPathAppend(szExePath, sizeof(szExePath), m->mProcName);
+    AssertLogRelRCReturn(vrc, vrc);
+
+    /*
+     * Allocate the argument array and construct the argument vector.
+     */
+    size_t const cArgs     = 1 + m->mOptions.size() * 2 + 1;
+    char const **papszArgs = (char const **)RTMemTmpAllocZ(sizeof(papszArgs[0]) * cArgs);
+    AssertReturn(papszArgs, VERR_NO_TMP_MEMORY);
+
+    size_t iArg = 0;
+    papszArgs[iArg++] = szExePath;
+    for (std::map<std::string, std::string>::const_iterator it = m->mOptions.begin(); it != m->mOptions.end(); ++it)
     {
-        suffix++;
-        strcpy(suffix, m->mProcName);
+        papszArgs[iArg++] = it->first.c_str();
+        papszArgs[iArg++] = it->second.c_str();
     }
-
-    int index = 0;
-
-    args[index++] = exePath;
-
-    std::map<std::string, std::string>::const_iterator it;
-    for(it = m->mOptions.begin(); it != m->mOptions.end(); ++it)
-    {
-        args[index++] = it->first.c_str();
-        args[index++] = it->second.c_str();
-    }
-
-    args[index++] = NULL;
-
-    int rc = RTProcCreate(suffix ? exePath : m->mProcName, args, RTENV_DEFAULT, 0, &m->mProcess);
+    Assert(iArg + 1 == cArgs);
+    Assert(papszArgs[iArg] == NULL);
+
+    /*
+     * Start the process:
+     */
+    int rc = RTProcCreate(szExePath, papszArgs, RTENV_DEFAULT, 0, &m->mProcess);
     if (RT_FAILURE(rc))
         m->mProcess = NIL_RTPROCESS;
 
     m->mKillProcOnStop = aKillProcOnStop;
+
+    RTMemTmpFree(papszArgs);
     return rc;
 }
