Index: trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp
===================================================================
--- trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp	(revision 71688)
+++ trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp	(revision 71689)
@@ -16,4 +16,6 @@
  */
 
+#include "Config.h"
+
 #include <iprt/types.h>
 #include <iprt/net.h>           /* NB: must come before getopt.h */
@@ -22,19 +24,192 @@
 #include <iprt/message.h>
 #include <iprt/string.h>
-
-#include "Config.h"
-
-
-bool Config::isSane()
-{
-    int rc;
+#include <iprt/uuid.h>
+
+#include <VBox/com/com.h>
+
+#include <iostream>
+
+class ConfigFileError
+  : public RTCError
+{
+public:
+    ConfigFileError(const char *pszMessage)
+      : RTCError(pszMessage) {}
+
+    ConfigFileError(const RTCString &a_rstrMessage)
+      : RTCError(a_rstrMessage) {}
+};
+
+
+Config::Config()
+  : m_strHome(),
+    m_strNetwork(),
+    m_strBaseName(),
+    m_strTrunk(),
+    m_enmTrunkType(kIntNetTrunkType_Invalid),
+    m_MacAddress(),
+    m_IPv4Address(),
+    m_IPv4Netmask(),
+    m_IPv4PoolFirst(),
+    m_IPv4PoolLast(),
+    m_GlobalOptions(),
+    m_VMMap()
+{
+    return;
+}
+
+
+int Config::init()
+{
+    int rc;
+
+    rc = homeInit();
+    if (RT_FAILURE(rc))
+        return rc;
+
+    return VINF_SUCCESS;
+}
+
+
+int Config::homeInit()
+{
+    int rc;
+
+    /* pathname of ~/.VirtualBox or equivalent */
+    char szHome[RTPATH_MAX];
+    rc = com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome), false);
+    if (RT_FAILURE(rc))
+    {
+        LogDHCP(("unable to find VirtualBox home directory: %Rrs", rc));
+        return rc;
+    }
+
+    m_strHome.assign(szHome);
+    return VINF_SUCCESS;
+}
+
+
+void Config::setNetwork(const std::string &aStrNetwork)
+{
+    AssertReturnVoid(m_strNetwork.empty());
+
+    m_strNetwork = aStrNetwork;
+    sanitizeBaseName();
+}
+
+
+/*
+ * Requires network name to be known as the log file name depends on
+ * it.  Alternatively, consider passing the log file name via the
+ * command line?
+ */
+int Config::logInit()
+{
+    int rc;
+    size_t cch;
+
+    if (m_strHome.empty() || m_strBaseName.empty())
+        return VERR_GENERAL_FAILURE;
+
+    /* default log file name */
+    char szLogFile[RTPATH_MAX];
+    cch = RTStrPrintf(szLogFile, sizeof(szLogFile),
+                      "%s%c%s-Dhcpd.log",
+                      m_strHome.c_str(), RTPATH_DELIMITER, m_strBaseName.c_str());
+    if (cch >= sizeof(szLogFile))
+        return VERR_BUFFER_OVERFLOW;
+
+
+    /* get a writable copy of the base name */
+    char szBaseName[RTPATH_MAX];
+    rc = RTStrCopy(szBaseName, sizeof(szBaseName), m_strBaseName.c_str());
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* sanitize base name some more to be usable in an environment variable name */
+    for (char *p = szBaseName; *p != '\0'; ++p)
+    {
+        if (   *p != '_'
+            && (*p < '0' || '9' < *p)
+            && (*p < 'a' || 'z' < *p)
+            && (*p < 'A' || 'Z' < *p))
+        {
+            *p = '_';
+        }
+    }
+
+
+    /* name of the environment variable to control logging */
+    char szEnvVarBase[128];
+    cch = RTStrPrintf(szEnvVarBase, sizeof(szEnvVarBase),
+                      "VBOXDHCP_%s_RELEASE_LOG", szBaseName);
+    if (cch >= sizeof(szEnvVarBase))
+        return VERR_BUFFER_OVERFLOW;
+
+
+    rc = com::VBoxLogRelCreate("DHCP Server",
+                               szLogFile,
+                               RTLOGFLAGS_PREFIX_TIME_PROG,
+                               "all all.restrict -default.restrict",
+                               szEnvVarBase,
+                               RTLOGDEST_FILE
+#ifdef DEBUG
+                               | RTLOGDEST_STDERR
+#endif
+                               ,
+                               32768 /* cMaxEntriesPerGroup */,
+                               0 /* cHistory */,
+                               0 /* uHistoryFileTime */,
+                               0 /* uHistoryFileSize */,
+                               NULL /* pErrInfo */);
+
+    return rc;
+}
+
+
+int Config::complete()
+{
+    int rc;
+
+    if (m_strNetwork.empty())
+    {
+        LogDHCP(("network name is not specified\n"));
+        return false;
+    }
+
+    logInit();
+
+    bool fMACGenerated = false;
+    if (   m_MacAddress.au16[0] == 0
+        && m_MacAddress.au16[1] == 0
+        && m_MacAddress.au16[2] == 0)
+    {
+        RTUUID Uuid;
+        RTUuidCreate(&Uuid);
+
+        m_MacAddress.au8[0] = 0x08;
+        m_MacAddress.au8[1] = 0x00;
+        m_MacAddress.au8[2] = 0x27;
+        m_MacAddress.au8[3] = Uuid.Gen.au8Node[3];
+        m_MacAddress.au8[4] = Uuid.Gen.au8Node[4];
+        m_MacAddress.au8[5] = Uuid.Gen.au8Node[5];
+
+        LogDHCP(("MAC address is not specified: will use generated MAC %RTmac\n", &m_MacAddress));
+        fMACGenerated = true;
+    }
 
     /* unicast MAC address */
     if (m_MacAddress.au8[0] & 0x01)
-        return false;
+    {
+        LogDHCP(("MAC address is not unicast: %RTmac\n", &m_MacAddress));
+        return VERR_GENERAL_FAILURE;
+    }
 
     /* unicast IP address */
     if ((m_IPv4Address.au8[0] & 0xe0) == 0xe0)
-        return false;
+    {
+        LogDHCP(("IP address is not unicast: %RTnaipv4\n", m_IPv4Address.u));
+        return VERR_GENERAL_FAILURE;
+    }
 
     /* valid netmask */
@@ -42,19 +217,50 @@
     rc = RTNetMaskToPrefixIPv4(&m_IPv4Netmask, &iPrefixLengh);
     if (RT_FAILURE(rc) || iPrefixLengh == 0)
-        return false;
+    {
+        LogDHCP(("IP mask is not valid: %RTnaipv4\n", m_IPv4Netmask.u));
+        return VERR_GENERAL_FAILURE;
+    }
 
     /* first IP is from the same network */
     if ((m_IPv4PoolFirst.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
-        return false;
+    {
+        LogDHCP(("first pool address is outside the network %RTnaipv4/%d: %RTnaipv4\n",
+                 (m_IPv4Address.u & m_IPv4Netmask.u), iPrefixLengh,
+                 m_IPv4PoolFirst.u));
+        return VERR_GENERAL_FAILURE;
+    }
 
     /* last IP is from the same network */
     if ((m_IPv4PoolLast.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
-        return false;
+    {
+        LogDHCP(("last pool address is outside the network %RTnaipv4/%d: %RTnaipv4\n",
+                 (m_IPv4Address.u & m_IPv4Netmask.u), iPrefixLengh,
+                 m_IPv4PoolLast.u));
+        return VERR_GENERAL_FAILURE;
+    }
 
     /* the pool is valid */
     if (RT_N2H_U32(m_IPv4PoolLast.u) < RT_N2H_U32(m_IPv4PoolFirst.u))
-        return false;
-
-    return true;
+    {
+        LogDHCP(("pool range is invalid: %RTnaipv4 - %RTnaipv4\n",
+                 m_IPv4PoolFirst.u, m_IPv4PoolLast.u));
+        return VERR_GENERAL_FAILURE;
+    }
+
+    /* our own address is not inside the pool */
+    if (   RT_N2H_U32(m_IPv4PoolFirst.u) <= RT_N2H_U32(m_IPv4Address.u)
+        && RT_N2H_U32(m_IPv4Address.u)  <= RT_N2H_U32(m_IPv4PoolLast.u))
+    {
+        LogDHCP(("server address inside the pool range %RTnaipv4 - %RTnaipv4: %RTnaipv4\n",
+                 m_IPv4PoolFirst.u, m_IPv4PoolLast.u, m_IPv4Address.u));
+        return VERR_GENERAL_FAILURE;
+    }
+
+    if (!fMACGenerated)
+        LogDHCP(("MAC address %RTmac\n", &m_MacAddress));
+    LogDHCP(("IP address %RTnaipv4/%d\n", m_IPv4Address.u, iPrefixLengh));
+    LogDHCP(("address pool %RTnaipv4 - %RTnaipv4\n", m_IPv4PoolFirst.u, m_IPv4PoolLast.u));
+
+    return VINF_SUCCESS;
 }
 
@@ -62,9 +268,12 @@
 Config *Config::hardcoded()
 {
+    int rc;
+
     std::unique_ptr<Config> config(new Config());
-
-    config->m_strNetwork.assign("HostInterfaceNetworking-vboxnet0");
-    config->sanitizeBaseName(); /* nop, but be explicit */
-
+    rc = config->init();
+    if (RT_FAILURE(rc))
+        return NULL;
+
+    config->setNetwork("HostInterfaceNetworking-vboxnet0");
     config->m_strTrunk.assign("vboxnet0");
     config->m_enmTrunkType = kIntNetTrunkType_NetFlt;
@@ -90,5 +299,7 @@
 #endif
 
-    AssertReturn(config->isSane(), NULL);
+    rc = config->complete();
+    AssertRCReturn(rc, NULL);
+
     return config.release();
 }
@@ -101,4 +312,5 @@
     { "--lower-ip",       'l',   RTGETOPT_REQ_IPV4ADDR },
     { "--mac-address",    'a',   RTGETOPT_REQ_MACADDR },
+    { "--need-main",      'M',   RTGETOPT_REQ_BOOL },
     { "--netmask",        'm',   RTGETOPT_REQ_IPV4ADDR },
     { "--network",        'n',   RTGETOPT_REQ_STRING },
@@ -120,4 +332,8 @@
 
     std::unique_ptr<Config> config(new Config());
+    rc = config->init();
+    if (RT_FAILURE(rc))
+        return NULL;
+
     for (;;)
     {
@@ -131,29 +347,70 @@
         {
             case 'a': /* --mac-address */
+                if (   config->m_MacAddress.au16[0] != 0
+                    || config->m_MacAddress.au16[1] != 0
+                    || config->m_MacAddress.au16[2] != 0)
+                {
+                    RTMsgError("Duplicate --mac-address option");
+                    return NULL;
+                }
                 config->m_MacAddress = Val.MacAddr;
                 break;
 
             case 'i': /* --ip-address */
+                if (config->m_IPv4Address.u != 0)
+                {
+                    RTMsgError("Duplicate --ip-address option");
+                    return NULL;
+                }
                 config->m_IPv4Address = Val.IPv4Addr;
                 break;
 
             case 'l': /* --lower-ip */
+                if (config->m_IPv4PoolFirst.u != 0)
+                {
+                    RTMsgError("Duplicate --lower-ip option");
+                    return NULL;
+                }
                 config->m_IPv4PoolFirst = Val.IPv4Addr;
                 break;
 
+            case 'M': /* --need-main */
+                /* for backward compatibility, ignored */
+                break;
+
             case 'm': /* --netmask */
+                if (config->m_IPv4Netmask.u != 0)
+                {
+                    RTMsgError("Duplicate --netmask option");
+                    return NULL;
+                }
                 config->m_IPv4Netmask = Val.IPv4Addr;
                 break;
 
             case 'n': /* --network */
-                config->m_strNetwork.assign(Val.psz);
+                if (!config->m_strNetwork.empty())
+                {
+                    RTMsgError("Duplicate --network option");
+                    return NULL;
+                }
+                config->setNetwork(Val.psz);
                 break;
 
             case 't': /* --trunk-name */
+                if (!config->m_strTrunk.empty())
+                {
+                    RTMsgError("Duplicate --trunk-name option");
+                    return NULL;
+                }
                 config->m_strTrunk.assign(Val.psz);
                 break;
 
             case 'T': /* --trunk-type */
-                if (strcmp(Val.psz, "none") == 0)
+                if (config->m_enmTrunkType != kIntNetTrunkType_Invalid)
+                {
+                    RTMsgError("Duplicate --trunk-type option");
+                    return NULL;
+                }
+                else if (strcmp(Val.psz, "none") == 0)
                     config->m_enmTrunkType = kIntNetTrunkType_None;
                 else if (strcmp(Val.psz, "whatever") == 0)
@@ -171,4 +428,9 @@
 
             case 'u': /* --upper-ip */
+                if (config->m_IPv4PoolLast.u != 0)
+                {
+                    RTMsgError("Duplicate --upper-ip option");
+                    return NULL;
+                }
                 config->m_IPv4PoolLast = Val.IPv4Addr;
                 break;
@@ -184,7 +446,6 @@
     }
 
-    config->sanitizeBaseName();
-
-    if (!config->isSane())
+    rc = config->complete();
+    if (RT_FAILURE(rc))
         return NULL;
 
@@ -247,8 +508,7 @@
         return NULL;
 
-    if (!config->isSane())
-        return NULL;
-
-    config->sanitizeBaseName();
+    rc = config->complete();
+    if (RT_FAILURE(rc))
+        return NULL;
 
     return config.release();
@@ -258,8 +518,298 @@
 Config *Config::read(const char *pszFileName)
 {
+    int rc;
+
+    if (pszFileName == NULL || pszFileName[0] == '\0')
+        return NULL;
+
+    xml::Document doc;
+    try
+    {
+        xml::XmlFileParser parser;
+        parser.read(pszFileName, doc);
+    }
+    catch (const xml::EIPRTFailure &e)
+    {
+        LogDHCP(("%s\n", e.what()));
+        return NULL;
+    }
+    catch (const RTCError &e)
+    {
+        LogDHCP(("%s\n", e.what()));
+        return NULL;
+    }
+    catch (...)
+    {
+        LogDHCP(("Unknown exception while reading and parsing '%s'\n",
+                 pszFileName));
+        return NULL;
+    }
+
     std::unique_ptr<Config> config(new Config());
-
-    RT_NOREF(pszFileName);
+    rc = config->init();
+    if (RT_FAILURE(rc))
+        return NULL;
+
+    try
+    {
+        config->parseConfig(doc.getRootElement());
+    }
+    catch (const RTCError &e)
+    {
+        LogDHCP(("%s\n", e.what()));
+        return NULL;
+    }
+    catch (...)
+    {
+        LogDHCP(("Unexpected exception\n"));
+        return NULL;
+    }
+
     return config.release();
+}
+
+
+void Config::parseConfig(const xml::ElementNode *root)
+{
+    if (root == NULL)
+        throw ConfigFileError("Empty config file");
+
+    /*
+     * XXX: NAMESPACE API IS COMPLETELY BROKEN, SO IGNORE IT FOR NOW
+     */
+    if (!root->nameEquals("DHCPServer"))
+    {
+        const char *name = root->getName();
+        throw ConfigFileError(RTCStringFmt("Unexpected root element \"%s\"",
+                                           name ? name : "(null)"));
+    }
+
+    parseServer(root);
+
+    // XXX: debug
+    for (auto it: m_GlobalOptions) {
+        std::shared_ptr<DhcpOption> opt(it.second);
+
+        octets_t data;
+        opt->encode(data);
+
+        bool space = false;
+        for (auto c: data) {
+            if (space)
+                std::cout << " ";
+            else
+                space = true;
+            std::cout << (int)c;
+        }
+        std::cout << std::endl;
+    }
+}
+
+
+static void getIPv4AddrAttribute(const xml::ElementNode *pNode, const char *pcszAttrName,
+                                 RTNETADDRIPV4 *pAddr)
+{
+    RTCString strAddr;
+    bool fHasAttr = pNode->getAttributeValue(pcszAttrName, &strAddr);
+    if (!fHasAttr)
+        throw ConfigFileError(RTCStringFmt("%s attribute missing",
+                                           pcszAttrName));
+
+    int rc = RTNetStrToIPv4Addr(strAddr.c_str(), pAddr);
+    if (RT_FAILURE(rc))
+        throw ConfigFileError(RTCStringFmt("%s attribute invalid",
+                                           pcszAttrName));
+}
+
+
+void Config::parseServer(const xml::ElementNode *server)
+{
+    /*
+     * DHCPServer attributes
+     */
+    RTCString strNetworkName;
+    bool fHasNetworkName = server->getAttributeValue("networkName", &strNetworkName);
+    if (!fHasNetworkName)
+        throw ConfigFileError("DHCPServer/@networkName missing");
+
+    setNetwork(strNetworkName.c_str());
+
+    getIPv4AddrAttribute(server, "IPAddress", &m_IPv4Address);
+    getIPv4AddrAttribute(server, "networkMask", &m_IPv4Netmask);
+    getIPv4AddrAttribute(server, "lowerIP", &m_IPv4PoolFirst);
+    getIPv4AddrAttribute(server, "upperIP", &m_IPv4PoolLast);
+
+    /*
+     * DHCPServer children
+     */
+    xml::NodesLoop it(*server);
+    const xml::ElementNode *node;
+    while ((node = it.forAllNodes()) != NULL)
+    {
+        /*
+         * Global options
+         */
+        if (node->nameEquals("Options"))
+        {
+            parseGlobalOptions(node);
+        }
+
+        /*
+         * Per-VM configuration
+         */
+        else if (node->nameEquals("Config"))
+        {
+            parseVMConfig(node);
+        }
+    }
+}
+
+
+void Config::parseGlobalOptions(const xml::ElementNode *options)
+{
+    xml::NodesLoop it(*options);
+    const xml::ElementNode *node;
+    while ((node = it.forAllNodes()) != NULL)
+    {
+        if (node->nameEquals("Option"))
+        {
+            parseOption(node, m_GlobalOptions);
+        }
+        else
+        {
+            throw ConfigFileError(RTCStringFmt("Unexpected element \"%s\"",
+                                               node->getName()));
+        }
+    }
+}
+
+
+/*
+ * VM Config entries are generated automatically from VirtualBox.xml
+ * with the MAC fetched from the VM config.  The client id is nowhere
+ * in the picture there, so VM config is indexed with plain RTMAC, not
+ * ClientId (also see getOptions below).
+ */
+void Config::parseVMConfig(const xml::ElementNode *config)
+{
+    RTMAC mac;
+    int rc;
+
+    RTCString strMac;
+    bool fHasMac = config->getAttributeValue("MACAddress", &strMac);
+    if (!fHasMac)
+        throw ConfigFileError(RTCStringFmt("Config missing MACAddress attribute"));
+
+    rc = parseMACAddress(mac, strMac);
+    if (RT_FAILURE(rc))
+    {
+        throw ConfigFileError(RTCStringFmt("Malformed MACAddress attribute \"%s\"",
+                                           strMac.c_str()));
+    }
+
+    vmmap_t::iterator vmit( m_VMMap.find(mac) );
+    if (vmit != m_VMMap.end())
+    {
+        throw ConfigFileError(RTCStringFmt("Duplicate Config for MACAddress \"%s\"",
+                                           strMac.c_str()));
+    }
+
+    optmap_t &vmopts = m_VMMap[mac];
+
+    xml::NodesLoop it(*config);
+    const xml::ElementNode *node;
+    while ((node = it.forAllNodes()) != NULL)
+    {
+        if (node->nameEquals("Option"))
+        {
+            parseOption(node, vmopts);
+        }
+        else
+        {
+            throw ConfigFileError(RTCStringFmt("Unexpected element \"%s\"",
+                                               node->getName()));
+        }
+    }    
+}
+
+
+int Config::parseMACAddress(RTMAC &aMac, const RTCString &aStr)
+{
+    RTMAC mac;
+    int rc;
+
+    rc = RTNetStrToMacAddr(aStr.c_str(), &mac);
+    if (RT_FAILURE(rc))
+        return rc;
+    if (rc == VWRN_TRAILING_CHARS)
+        return VERR_INVALID_PARAMETER;
+
+    aMac = mac;
+    return VINF_SUCCESS;
+}
+
+
+int Config::parseClientId(OptClientId &aId, const RTCString &aStr)
+{
+    RT_NOREF(aId, aStr);
+    return VERR_GENERAL_FAILURE;
+}
+
+
+/*
+ * Parse <Option/> element and add the option to the specified optmap.
+ */
+void Config::parseOption(const xml::ElementNode *option, optmap_t &optmap)
+{
+    int rc;
+
+    uint8_t u8Opt;
+    RTCString strName;
+    bool fHasName = option->getAttributeValue("name", &strName);
+    if (fHasName)
+    {
+        const char *pcszName = strName.c_str();
+
+        rc = RTStrToUInt8Full(pcszName, 10, &u8Opt);
+        if (rc != VINF_SUCCESS) /* no warnings either */
+            throw ConfigFileError(RTCStringFmt("Bad option \"%s\"", pcszName));
+
+    }
+    else
+        throw ConfigFileError("missing option name");
+
+
+    uint32_t u32Enc = 0;        /* XXX: DhcpOptEncoding_Legacy */
+    RTCString strEncoding;
+    bool fHasEncoding = option->getAttributeValue("encoding", &strEncoding);
+    if (fHasEncoding)
+    {
+        const char *pcszEnc = strEncoding.c_str();
+
+        rc = RTStrToUInt32Full(pcszEnc, 10, &u32Enc);
+        if (rc != VINF_SUCCESS) /* no warnings either */
+            throw ConfigFileError(RTCStringFmt("Bad encoding \"%s\"", pcszEnc));
+
+        switch (u32Enc)
+        {
+        case 0:                 /* XXX: DhcpOptEncoding_Legacy */
+        case 1:                 /* XXX: DhcpOptEncoding_Hex */
+            break;
+        default:
+            throw ConfigFileError(RTCStringFmt("Unknown encoding \"%s\"", pcszEnc));
+        }
+    }
+
+
+    /* value may be omitted for OptNoValue options like rapid commit */
+    RTCString strValue;
+    option->getAttributeValue("value", &strValue);
+
+    /* XXX: TODO: encoding, handle hex */
+    DhcpOption *opt = DhcpOption::parse(u8Opt, u32Enc, strValue.c_str());
+    if (opt == NULL)
+        throw ConfigFileError(RTCStringFmt("Bad option \"%s\"", strName.c_str()));
+
+    optmap << opt;
 }
 
@@ -273,4 +823,7 @@
     int rc;
 
+    if (m_strNetwork.empty())
+        return;
+
     char szBaseName[RTPATH_MAX];
     rc = RTStrCopy(szBaseName, sizeof(szBaseName), m_strNetwork.c_str());
@@ -296,38 +849,69 @@
     optmap_t optmap;
 
-    fillDefaultOptions(optmap, reqOpts);
-    fillVendorOptions(optmap, reqOpts, vendor);
-    fillHostOptions(optmap, reqOpts, id);
+    const optmap_t *vmopts = NULL;
+    vmmap_t::const_iterator vmit( m_VMMap.find(id.mac()) );
+    if (vmit != m_VMMap.end())
+        vmopts = &vmit->second;
+
+    RT_NOREF(vendor); /* not yet */
+
+
+    optmap << new OptSubnetMask(m_IPv4Netmask);
+
+    for (auto optreq: reqOpts.value())
+    {
+        std::cout << ">>> requested option " << (int)optreq << std::endl;
+
+        if (optreq == OptSubnetMask::optcode)
+        {
+            std::cout << "... always supplied" << std::endl;
+            continue;
+        }
+
+        if (vmopts != NULL)
+        {
+            optmap_t::const_iterator it( vmopts->find(optreq) );
+            if (it != vmopts->end())
+            {
+                optmap << it->second;
+                std::cout << "... found in VM options" << std::endl;
+                continue;
+            }
+        }
+
+        optmap_t::const_iterator it( m_GlobalOptions.find(optreq) );
+        if (it != m_GlobalOptions.end())
+        {
+            optmap << it->second;
+            std::cout << "... found in global options" << std::endl;
+            continue;
+        }
+
+        // std::cout << "... not found" << std::endl;
+    }
+
+
+    /* XXX: testing ... */
+    if (vmopts != NULL)
+    {
+        for (auto it: *vmopts) {
+            std::shared_ptr<DhcpOption> opt(it.second);
+            if (optmap.count(opt->optcode()) == 0 && opt->optcode() > 127)
+            {
+                optmap << opt;
+                std::cout << "... forcing VM option " << (int)opt->optcode() << std::endl;
+            }
+        }
+    }
+
+    for (auto it: m_GlobalOptions) {
+        std::shared_ptr<DhcpOption> opt(it.second);
+        if (optmap.count(opt->optcode()) == 0 && opt->optcode() > 127)
+        {
+            optmap << opt;
+            std::cout << "... forcing global option " << (int)opt->optcode() << std::endl;
+        }
+    }
 
     return optmap;
 }
-
-
-void Config::fillDefaultOptions(optmap_t &optmap,
-                                const OptParameterRequest &reqOpts) const
-{
-    RT_NOREF(reqOpts);
-
-    optmap << new OptSubnetMask(m_IPv4Netmask);
-}
-
-
-void Config::fillHostOptions(optmap_t &optmap,
-                             const OptParameterRequest &reqOpts,
-                             const ClientId &id) const
-{
-    /* not yet */
-    RT_NOREF(optmap, reqOpts, id);
-}
-
-
-void Config::fillVendorOptions(optmap_t &optmap,
-                               const OptParameterRequest &reqOpts,
-                               const OptVendorClassId &vendor) const
-{
-    if (!vendor.present())
-        return;
-
-    /* may be some day... */
-    RT_NOREF(optmap, reqOpts);
-}
Index: trunk/src/VBox/NetworkServices/Dhcpd/Config.h
===================================================================
--- trunk/src/VBox/NetworkServices/Dhcpd/Config.h	(revision 71688)
+++ trunk/src/VBox/NetworkServices/Dhcpd/Config.h	(revision 71689)
@@ -21,4 +21,5 @@
 #include <iprt/types.h>
 #include <iprt/net.h>
+#include <iprt/cpp/xml.h>
 
 #include <VBox/intnet.h>
@@ -33,5 +34,8 @@
 class Config
 {
-    std::string m_strHome;
+    /* XXX: TODO: also store fixed address assignments, etc? */
+    typedef std::map<RTMAC, optmap_t> vmmap_t;
+
+    std::string m_strHome;   /* path of ~/.VirtualBox or equivalent */
 
     std::string m_strNetwork;
@@ -49,6 +53,14 @@
     RTNETADDRIPV4 m_IPv4PoolLast;
 
+    optmap_t m_GlobalOptions;
+    vmmap_t m_VMMap;
+
 private:
-    bool isSane();
+    Config();
+
+    int init();
+    int homeInit();
+    int logInit();
+    int complete();
 
 public: /* factory methods */
@@ -58,8 +70,9 @@
 
 public: /* accessors */
-    void setHome(const std::string &strHome) { m_strHome = strHome; }
+    const std::string &getHome() const { return m_strHome; }
 
-    const std::string &getHome() const { return m_strHome; }
     const std::string &getNetwork() const { return m_strNetwork; }
+    void setNetwork(const std::string &aStrNetwork);
+
     const std::string &getBaseName() const { return m_strBaseName; }
     const std::string &getTrunk() const { return m_strTrunk; }
@@ -80,15 +93,14 @@
 private:
     static Config *read(const char *pszFileName);
+    void parseConfig(const xml::ElementNode *root);
+    void parseServer(const xml::ElementNode *server);
+    void parseGlobalOptions(const xml::ElementNode *options);
+    void parseVMConfig(const xml::ElementNode *config);
+    void parseOption(const xml::ElementNode *option, optmap_t &optmap);
+
+    int parseMACAddress(RTMAC &aMac, const RTCString &aStr);
+    int parseClientId(OptClientId &aId, const RTCString &aStr);
 
     void sanitizeBaseName();
-
-    void fillDefaultOptions(optmap_t &optmap,
-                            const OptParameterRequest &reqOpts) const;
-    void fillVendorOptions(optmap_t &optmap,
-                           const OptParameterRequest &reqOpts,
-                           const OptVendorClassId &vendor) const;
-    void fillHostOptions(optmap_t &optmap,
-                         const OptParameterRequest &reqOpts,
-                         const ClientId &id) const;
 };
 
Index: trunk/src/VBox/NetworkServices/Dhcpd/Db.cpp
===================================================================
--- trunk/src/VBox/NetworkServices/Dhcpd/Db.cpp	(revision 71688)
+++ trunk/src/VBox/NetworkServices/Dhcpd/Db.cpp	(revision 71689)
@@ -18,7 +18,4 @@
 #include <iprt/err.h>
 #include <iprt/stream.h>
-
-#include <algorithm>
-#include <functional>
 
 #include "Db.h"
@@ -340,53 +337,4 @@
     }
 }
-
-
-Binding *Db::bindingById(const ClientId &id) const
-{
-    struct ClientMatch : public Binding::Match {
-        const ClientId &m_id;
-        ClientMatch(const ClientId &id) : m_id(id) {}
-
-        bool operator()(const Binding *b)
-        {
-            return b->m_id == m_id;
-        }
-    };
-
-    bindings_t::const_iterator found =
-        std::find_if(m_bindings.begin(), m_bindings.end(),
-                     ClientMatch(id));
-
-    if (found == m_bindings.end())
-        return NULL;
-
-    Binding *b = *found;
-    return b;
-}
-
-
-Binding *Db::bindingByAddr(RTNETADDRIPV4 addr) const
-{
-    struct AddrMatch : public Binding::Match {
-        const RTNETADDRIPV4 m_addr;
-        AddrMatch(RTNETADDRIPV4 addr) : m_addr(addr) {}
-
-        bool operator()(const Binding *b)
-        {
-            return b->m_addr.u == m_addr.u;
-        }
-    };
-
-    bindings_t::const_iterator found =
-        std::find_if(m_bindings.begin(), m_bindings.end(),
-                     AddrMatch(addr));
-
-    if (found == m_bindings.end())
-        return NULL;
-
-    Binding *b = *found;
-    return b;
-}
-
 
 
Index: trunk/src/VBox/NetworkServices/Dhcpd/Db.h
===================================================================
--- trunk/src/VBox/NetworkServices/Dhcpd/Db.h	(revision 71688)
+++ trunk/src/VBox/NetworkServices/Dhcpd/Db.h	(revision 71689)
@@ -39,6 +39,4 @@
 public:
     enum State { FREE, RELEASED, EXPIRED, OFFERED, ACKED };
-
-    typedef std::unary_function<const Binding *, bool> Match;
 
 private:
@@ -136,7 +134,4 @@
     bool addressBelongs(RTNETADDRIPV4 addr) const { return m_pool.contains(addr); }
 
-    Binding *bindingById(const ClientId &id) const;
-    Binding *bindingByAddr(RTNETADDRIPV4 addr) const;
-
     Binding *allocateBinding(const DhcpClientMessage &req);
     bool releaseBinding(const DhcpClientMessage &req);
Index: trunk/src/VBox/NetworkServices/Dhcpd/DhcpOptions.cpp
===================================================================
--- trunk/src/VBox/NetworkServices/Dhcpd/DhcpOptions.cpp	(revision 71688)
+++ trunk/src/VBox/NetworkServices/Dhcpd/DhcpOptions.cpp	(revision 71689)
@@ -48,4 +48,37 @@
 
 
+int DhcpOption::encode(octets_t &dst) const
+{
+    if (!m_fPresent)
+        return VERR_INVALID_STATE;
+
+    size_t cbOrig = dst.size();
+
+    append(dst, m_OptCode);
+    appendLength(dst, 0);  /* placeholder */
+
+    ssize_t cbValue = encodeValue(dst);
+    if (cbValue < 0 || UINT8_MAX <= cbValue)
+    {
+        dst.resize(cbOrig); /* undo */
+        return VERR_INVALID_PARAMETER;
+    }
+
+    dst[cbOrig+1] = cbValue;
+    return VINF_SUCCESS;
+}
+
+
+/* static */
+const octets_t *DhcpOption::findOption(const rawopts_t &aOptMap, uint8_t aOptCode)
+{
+    rawopts_t::const_iterator it(aOptMap.find(aOptCode));
+    if (it == aOptMap.end())
+        return NULL;
+
+    return &it->second;
+}
+
+
 int DhcpOption::decode(const rawopts_t &map)
 {
@@ -66,2 +99,144 @@
     return decode(req.rawopts());
 }
+
+
+int DhcpOption::parse1(uint8_t &aValue, const char *pcszValue)
+{
+    int rc = RTStrToUInt8Full(RTStrStripL(pcszValue), 10, &aValue);
+
+    if (rc == VERR_TRAILING_SPACES)
+        rc = VINF_SUCCESS;
+    return rc;
+}
+
+
+int DhcpOption::parse1(uint16_t &aValue, const char *pcszValue)
+{
+    int rc = RTStrToUInt16Full(RTStrStripL(pcszValue), 10, &aValue);
+
+    if (rc == VERR_TRAILING_SPACES)
+        rc = VINF_SUCCESS;
+    return rc;
+}
+
+
+int DhcpOption::parse1(uint32_t &aValue, const char *pcszValue)
+{
+    int rc = RTStrToUInt32Full(RTStrStripL(pcszValue), 10, &aValue);
+
+    if (rc == VERR_TRAILING_SPACES)
+        rc = VINF_SUCCESS;
+    return rc;
+}
+
+
+int DhcpOption::parse1(RTNETADDRIPV4 &aValue, const char *pcszValue)
+{
+    return RTNetStrToIPv4Addr(pcszValue, &aValue);
+}
+
+
+int DhcpOption::parseList(std::vector<RTNETADDRIPV4> &aList, const char *pcszValue)
+{
+    std::vector<RTNETADDRIPV4> l;
+    int rc;
+
+    pcszValue = RTStrStripL(pcszValue);
+    do {
+        RTNETADDRIPV4 Addr;
+        char *pszNext;
+
+        rc = RTNetStrToIPv4AddrEx(pcszValue, &Addr, &pszNext);
+        if (RT_FAILURE(rc))
+            return VERR_INVALID_PARAMETER;
+
+        if (rc == VWRN_TRAILING_CHARS)
+        {
+            pcszValue = RTStrStripL(pszNext);
+            if (pcszValue == pszNext) /* garbage after address */
+                return VERR_INVALID_PARAMETER;
+        }
+
+        l.push_back(Addr);
+
+        /*
+         * If we got VINF_SUCCESS or VWRN_TRAILING_SPACES then this
+         * was the last address and we are done.
+         */
+    } while (rc == VWRN_TRAILING_CHARS);
+
+    aList.swap(l);
+    return VINF_SUCCESS;
+}
+
+
+/*
+ * XXX: See DHCPServer::encodeOption()
+ */
+int DhcpOption::parseHex(octets_t &aRawValue, const char *pcszValue)
+{
+    octets_t data;
+    char *pszNext;
+    int rc;
+
+    if (pcszValue == NULL || *pcszValue == '\0')
+        return VERR_INVALID_PARAMETER;
+
+    while (*pcszValue != '\0')
+    {
+        if (data.size() > UINT8_MAX)
+            return VERR_INVALID_PARAMETER;
+
+        uint8_t u8Byte;
+        rc = RTStrToUInt8Ex(pcszValue, &pszNext, 16, &u8Byte);
+        if (!RT_SUCCESS(rc))
+            return rc;
+
+        if (*pszNext == ':')
+            ++pszNext;
+        else if (*pszNext != '\0')
+            return VERR_PARSE_ERROR;
+
+        data.push_back(u8Byte);
+        pcszValue = pszNext;
+    }
+
+    aRawValue.swap(data);
+    return VINF_SUCCESS;
+}
+
+
+DhcpOption *DhcpOption::parse(uint8_t aOptCode, int aEnc, const char *pcszValue)
+{
+    switch (aEnc)
+    {
+    case 0: /* DhcpOptEncoding_Legacy */
+        switch (aOptCode)
+        {
+#define HANDLE(_OptClass)                               \
+            case _OptClass::optcode:                    \
+                return _OptClass::parse(pcszValue);
+
+        HANDLE(OptSubnetMask);
+        HANDLE(OptRouter);
+        HANDLE(OptDNS);
+        HANDLE(OptHostName);
+        HANDLE(OptDomainName);
+        HANDLE(OptRootPath);
+        HANDLE(OptLeaseTime);
+        HANDLE(OptRenewalTime);
+        HANDLE(OptRebindingTime);
+
+#undef HANDLE
+        default:
+            return NULL;
+        }
+        break;
+
+    case 1:
+        return RawOption::parse(aOptCode, pcszValue);
+
+    default:
+        return NULL;
+    }
+}
Index: trunk/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h
===================================================================
--- trunk/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h	(revision 71688)
+++ trunk/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h	(revision 71689)
@@ -51,56 +51,23 @@
     virtual ~DhcpOption() {}
 
-  protected:
-    virtual ssize_t encodeValue(octets_t &data) const = 0;
-    virtual int decodeValue(const octets_t &data, size_t cb) = 0;
+  public:
+    static DhcpOption *parse(uint8_t aOptCode, int aEnc, const char *pcszValue);
 
   public:
     uint8_t optcode() const { return m_OptCode; }
-
     bool present() const { return m_fPresent; }
 
-    int encode(octets_t &data) const
-    {
-        if (!m_fPresent)
-            return VERR_INVALID_STATE;
-
-        size_t cbOrig = data.size();
-
-        append(data, m_OptCode);
-        appendLength(data, 0);  /* placeholder */
-
-        ssize_t cbValue = encodeValue(data);
-        if (cbValue < 0 || UINT8_MAX <= cbValue)
-        {
-            data.resize(cbOrig); /* undo */
-            return VERR_INVALID_PARAMETER;
-        }
-
-        data[cbOrig+1] = cbValue;
-        return VINF_SUCCESS;
-    }
-
-    int testEncodeValue(octets_t &data) const /* XXX */
-    {
-        if (!m_fPresent)
-            return VERR_INVALID_STATE;
-
-        return encodeValue(data) >= 0 ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
-    }
-
-  public:
+  public:
+    int encode(octets_t &dst) const;
+
     int decode(const rawopts_t &map);
     int decode(const DhcpClientMessage &req);
 
-
-  protected:
-    static const octets_t *findOption(const rawopts_t &aOptMap, uint8_t aOptCode)
-    {
-        rawopts_t::const_iterator it(aOptMap.find(aOptCode));
-        if (it == aOptMap.end())
-            return NULL;
-
-        return &it->second;
-    }
+  protected:
+    virtual ssize_t encodeValue(octets_t &dst) const = 0;
+    virtual int decodeValue(const octets_t &src, size_t cb) = 0;
+
+  protected:
+    static const octets_t *findOption(const rawopts_t &aOptMap, uint8_t aOptCode);
 
   protected:
@@ -108,40 +75,40 @@
      * Serialization
      */
-    static void append(octets_t &aData, uint8_t aValue)
-    {
-        aData.push_back(aValue);
-    }
-
-    static void append(octets_t &aData, uint16_t aValue)
+    static void append(octets_t &aDst, uint8_t aValue)
+    {
+        aDst.push_back(aValue);
+    }
+
+    static void append(octets_t &aDst, uint16_t aValue)
     {
         RTUINT16U u16 = { RT_H2N_U16(aValue) };
-        aData.insert(aData.end(), u16.au8, u16.au8 + sizeof(aValue));
-    }
-
-    static void append(octets_t &aData, uint32_t aValue)
+        aDst.insert(aDst.end(), u16.au8, u16.au8 + sizeof(aValue));
+    }
+
+    static void append(octets_t &aDst, uint32_t aValue)
     {
         RTUINT32U u32 = { RT_H2N_U32(aValue) };
-        aData.insert(aData.end(), u32.au8, u32.au8 + sizeof(aValue));
-    }
-
-    static void append(octets_t &aData, RTNETADDRIPV4 aIPv4)
-    {
-        aData.insert(aData.end(), aIPv4.au8, aIPv4.au8 + sizeof(aIPv4));
-    }
-
-    static void append(octets_t &aData, const char *pszString, size_t cb)
-    {
-        aData.insert(aData.end(), pszString, pszString + cb);
-    }
-
-    static void append(octets_t &aData, const std::string &str)
-    {
-        append(aData, str.c_str(), str.size());
+        aDst.insert(aDst.end(), u32.au8, u32.au8 + sizeof(aValue));
+    }
+
+    static void append(octets_t &aDst, RTNETADDRIPV4 aIPv4)
+    {
+        aDst.insert(aDst.end(), aIPv4.au8, aIPv4.au8 + sizeof(aIPv4));
+    }
+
+    static void append(octets_t &aDst, const char *pszString, size_t cb)
+    {
+        aDst.insert(aDst.end(), pszString, pszString + cb);
+    }
+
+    static void append(octets_t &aDst, const std::string &str)
+    {
+        append(aDst, str.c_str(), str.size());
     }
 
     /* non-overloaded name to avoid ambiguity */
-    static void appendLength(octets_t &aData, size_t cb)
-    {
-        append(aData, static_cast<uint8_t>(cb));
+    static void appendLength(octets_t &aDst, size_t cb)
+    {
+        append(aDst, static_cast<uint8_t>(cb));
     }
 
@@ -183,11 +150,24 @@
         pos += cb;
     }
-};
-
-
-inline octets_t &operator<<(octets_t &data, const DhcpOption &option)
-{
-    option.encode(data);
-    return data;
+
+
+    /*
+     * Parse textual representation (e.g. in config file)
+     */
+    static int parse1(uint8_t &aValue, const char *pcszValue);
+    static int parse1(uint16_t &aValue, const char *pcszValue);
+    static int parse1(uint32_t &aValue, const char *pcszValue);
+    static int parse1(RTNETADDRIPV4 &aValue, const char *pcszValue);
+
+    static int parseList(std::vector<RTNETADDRIPV4> &aList, const char *pcszValue);
+
+    static int parseHex(octets_t &aRawValue, const char *pcszValue);
+};
+
+
+inline octets_t &operator<<(octets_t &dst, const DhcpOption &option)
+{
+    option.encode(dst);
+    return dst;
 }
 
@@ -202,10 +182,10 @@
  */
 struct OptEnd {};
-inline octets_t &operator<<(octets_t &data, const OptEnd &end)
+inline octets_t &operator<<(octets_t &dst, const OptEnd &end)
 {
     RT_NOREF(end);
 
-    data.push_back(RTNET_DHCP_OPT_END);
-    return data;
+    dst.push_back(RTNET_DHCP_OPT_END);
+    return dst;
 }
 
@@ -225,5 +205,5 @@
       : DhcpOption(aOptCode, fPresent) {}
 
-    explicit OptNoValueBase(uint8_t aOptCode, const DhcpClientMessage &req)
+    OptNoValueBase(uint8_t aOptCode, const DhcpClientMessage &req)
       : DhcpOption(aOptCode, false)
     {
@@ -237,7 +217,7 @@
 
   protected:
-    virtual ssize_t encodeValue(octets_t &data) const
-    {
-        RT_NOREF(data);
+    virtual ssize_t encodeValue(octets_t &dst) const
+    {
+        RT_NOREF(dst);
         return 0;
     }
@@ -249,7 +229,7 @@
     }
 
-    virtual int decodeValue(const octets_t &data, size_t cb)
-    {
-        RT_NOREF(data);
+    virtual int decodeValue(const octets_t &src, size_t cb)
+    {
+        RT_NOREF(src);
 
         if (!isLengthValid(cb))
@@ -299,5 +279,5 @@
       : DhcpOption(aOptCode), m_Value(aOptValue) {}
 
-    explicit OptValueBase(uint8_t aOptCode, const DhcpClientMessage &req)
+    OptValueBase(uint8_t aOptCode, const DhcpClientMessage &req)
       : DhcpOption(aOptCode, false), m_Value()
     {
@@ -316,7 +296,7 @@
 
   protected:
-    virtual ssize_t encodeValue(octets_t &data) const
-    {
-        append(data, m_Value);
+    virtual ssize_t encodeValue(octets_t &dst) const
+    {
+        append(dst, m_Value);
         return sizeof(T);
     }
@@ -328,10 +308,10 @@
     }
 
-    virtual int decodeValue(const octets_t &data, size_t cb)
+    virtual int decodeValue(const octets_t &src, size_t cb)
     {
         if (!isLengthValid(cb))
             return VERR_INVALID_PARAMETER;
 
-        octets_t::const_iterator pos(data.begin());
+        octets_t::const_iterator pos(src.begin());
         extract(m_Value, pos);
 
@@ -346,4 +326,7 @@
 {
   public:
+    using typename OptValueBase<T>::value_t;
+
+  public:
     static const uint8_t optcode = _OptCode;
 
@@ -356,4 +339,13 @@
     explicit OptValue(const DhcpClientMessage &req)
       : OptValueBase<T>(optcode, req) {}
+
+    static OptValue *parse(const char *pcszValue)
+    {
+	value_t v;
+	int rc = DhcpOption::parse1(v, pcszValue);
+	if (RT_FAILURE(rc))
+	    return NULL;
+	return new OptValue(v);
+    }
 };
 
@@ -372,11 +364,11 @@
     std::string m_String;
 
-    OptStringBase(uint8_t aOptCode)
+    explicit OptStringBase(uint8_t aOptCode)
       : DhcpOption(aOptCode, false), m_String() {}
 
-    explicit OptStringBase(uint8_t aOptCode, const std::string &aOptString)
+    OptStringBase(uint8_t aOptCode, const std::string &aOptString)
       : DhcpOption(aOptCode), m_String(aOptString) {}
 
-    explicit OptStringBase(uint8_t aOptCode, const DhcpClientMessage &req)
+    OptStringBase(uint8_t aOptCode, const DhcpClientMessage &req)
       : DhcpOption(aOptCode, false), m_String()
     {
@@ -395,10 +387,10 @@
 
   protected:
-    virtual ssize_t encodeValue(octets_t &data) const
+    virtual ssize_t encodeValue(octets_t &dst) const
     {
         if (!isLengthValid(m_String.size()))
             return -1;
 
-        append(data, m_String);
+        append(dst, m_String);
         return m_String.size();
     }
@@ -410,10 +402,10 @@
     }
 
-    virtual int decodeValue(const octets_t &data, size_t cb)
+    virtual int decodeValue(const octets_t &src, size_t cb)
     {
         if (!isLengthValid(cb))
             return VERR_INVALID_PARAMETER;
 
-        octets_t::const_iterator pos(data.begin());
+        octets_t::const_iterator pos(src.begin());
         extract(m_String, pos, cb);
         m_fPresent = true;
@@ -437,4 +429,9 @@
     explicit OptString(const DhcpClientMessage &req)
       : OptStringBase(optcode, req) {}
+
+    static OptString *parse(const char *pcszValue)
+    {
+	return new OptString(pcszValue);
+    }
 };
 
@@ -463,5 +460,5 @@
       : DhcpOption(aOptCode), m_List(aOptList) {}
 
-    explicit OptListBase(uint8_t aOptCode, const DhcpClientMessage &req)
+    OptListBase(uint8_t aOptCode, const DhcpClientMessage &req)
       : DhcpOption(aOptCode, false), m_List()
     {
@@ -480,5 +477,5 @@
 
   protected:
-    virtual ssize_t encodeValue(octets_t &data) const
+    virtual ssize_t encodeValue(octets_t &dst) const
     {
         const size_t cbItem = sizeof(T);
@@ -490,5 +487,5 @@
                 break;
 
-            append(data, m_List[i]);
+            append(dst, m_List[i]);
             cbValue += cbItem;
         }
@@ -503,5 +500,5 @@
     }
 
-    virtual int decodeValue(const octets_t &data, size_t cb)
+    virtual int decodeValue(const octets_t &src, size_t cb)
     {
         if (!isLengthValid(cb))
@@ -510,5 +507,5 @@
         m_List.erase(m_List.begin(), m_List.end());
 
-        octets_t::const_iterator pos(data.begin());
+        octets_t::const_iterator pos(src.begin());
         for (size_t i = 0; i < cb / sizeof(T); ++i)
         {
@@ -528,4 +525,7 @@
 {
   public:
+    using typename OptListBase<T>::value_t;
+
+  public:
     static const uint8_t optcode = _OptCode;
 
@@ -536,9 +536,71 @@
       : OptListBase<T>(optcode, aOptSingle) {}
 
-    OptList(const std::vector<T> &aOptList)
+    explicit OptList(const std::vector<T> &aOptList)
       : OptListBase<T>(optcode, aOptList) {}
 
     explicit OptList(const DhcpClientMessage &req)
       : OptListBase<T>(optcode, req) {}
+
+    static OptList *parse(const char *pcszValue)
+    {
+	value_t v;
+	int rc = DhcpOption::parseList(v, pcszValue);
+	if (RT_FAILURE(rc) || v.empty())
+	    return NULL;
+	return new OptList(v);
+    }
+};
+
+
+/*
+ * Options specified by raw binary data that we don't know how to
+ * interpret.
+ */
+class RawOption
+  : public DhcpOption
+{
+  protected:
+    octets_t m_Data;
+
+  public:
+    explicit RawOption(uint8_t aOptCode)
+      : DhcpOption(aOptCode, false), m_Data() {}
+
+    RawOption(uint8_t aOptCode, const octets_t &aSrc)
+      : DhcpOption(aOptCode), m_Data(aSrc) {}
+
+  public:
+    virtual RawOption *clone() const
+    {
+        return new RawOption(*this);
+    }
+
+
+  protected:
+    virtual ssize_t encodeValue(octets_t &dst) const
+    {
+        dst.insert(dst.end(), m_Data.begin(), m_Data.end());
+        return m_Data.size();
+    }
+
+    virtual int decodeValue(const octets_t &src, size_t cb)
+    {
+        octets_t::const_iterator beg(src.begin());
+	octets_t data(beg, beg + cb);
+	m_Data.swap(data);
+
+	m_fPresent = true;
+	return VINF_SUCCESS;
+    }
+
+  public:
+    static RawOption *parse(uint8_t aOptCode, const char *pcszValue)
+    {
+	octets_t data;
+	int rc = DhcpOption::parseHex(data, pcszValue);
+	if (RT_FAILURE(rc))
+	    return NULL;
+	return new RawOption(aOptCode, data);
+    }
 };
 
Index: trunk/src/VBox/NetworkServices/Dhcpd/VBoxNetDhcpd.cpp
===================================================================
--- trunk/src/VBox/NetworkServices/Dhcpd/VBoxNetDhcpd.cpp	(revision 71688)
+++ trunk/src/VBox/NetworkServices/Dhcpd/VBoxNetDhcpd.cpp	(revision 71689)
@@ -33,6 +33,4 @@
 #include <VBox/intnetinline.h>
 
-#include <VBox/com/com.h>
-
 #include "VBoxLwipCore.h"
 #include "Config.h"
@@ -73,4 +71,6 @@
 
 private:
+    PRTLOGGER m_pStderrReleaseLogger;
+
     /* intnet plumbing */
     PSUPDRVSESSION m_pSession;
@@ -81,7 +81,4 @@
     struct netif m_LwipNetif;
 
-    /* path of ~/.VirtualBox or equivalent */
-    std::string m_strHome;
-
     Config *m_Config;
 
@@ -98,6 +95,5 @@
 
 private:
-    void homeInit();
-    int logInit(const std::string &strBaseName);
+    int logInitStderr();
 
     /*
@@ -149,5 +145,6 @@
 
 VBoxNetDhcpd::VBoxNetDhcpd()
-  : m_pSession(NIL_RTR0PTR),
+  : m_pStderrReleaseLogger(NULL),
+    m_pSession(NIL_RTR0PTR),
     m_hIf(INTNET_HANDLE_INVALID),
     m_pIfBuf(NULL),
@@ -158,4 +155,6 @@
     int rc;
 
+    logInitStderr();
+
     rc = r3Init();
     if (RT_FAILURE(rc))
@@ -170,4 +169,41 @@
     ifClose();
     r3Fini();
+}
+
+
+/*
+ * We don't know the name of the release log file until we parse our
+ * configuration because we use network name as basename.  To get
+ * early logging to work, start with stderr-only release logger.
+ *
+ * We disable "sup" for this logger to avoid spam from SUPR3Init().
+ */
+int VBoxNetDhcpd::logInitStderr()
+{
+    static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+
+    PRTLOGGER pLogger;
+    int rc;
+
+    uint32_t fFlags = 0;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+    fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+
+    rc = RTLogCreate(&pLogger, fFlags,
+                     "all -sup all.restrict -default.restrict",
+                     NULL,      /* environment base */
+                     RT_ELEMENTS(s_apszGroups), s_apszGroups,
+                     RTLOGDEST_STDERR, NULL);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("Failed to init stderr logger: %Rrs\n", rc);
+        return rc;
+    }
+
+    m_pStderrReleaseLogger = pLogger;
+    RTLogRelSetDefaultInstance(m_pStderrReleaseLogger);
+
+    return VINF_SUCCESS;
 }
 
@@ -260,5 +296,8 @@
     OpenReq.szTrunk[sizeof(OpenReq.szTrunk) - 1] = '\0';
 
-    OpenReq.enmTrunkType = enmTrunkType;
+    if (enmTrunkType != kIntNetTrunkType_Invalid)
+        OpenReq.enmTrunkType = enmTrunkType;
+    else
+        OpenReq.enmTrunkType = kIntNetTrunkType_WhateverNone;
 
     OpenReq.fFlags = 0;
@@ -588,9 +627,4 @@
         return VERR_GENERAL_FAILURE;
 
-    homeInit();
-    m_Config->setHome(m_strHome);
-
-    logInit(m_Config->getBaseName());
-
     rc = m_server.init(m_Config);
 
@@ -609,79 +643,4 @@
     ifPump();
     return VINF_SUCCESS;
-}
-
-
-void VBoxNetDhcpd::homeInit()
-{
-    int rc;
-
-    /* pathname of ~/.VirtualBox or equivalent */
-    char szHome[RTPATH_MAX];
-    rc = com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome), false);
-    if (RT_FAILURE(rc))
-        return;
-
-    m_strHome.assign(szHome);
-}
-
-
-int VBoxNetDhcpd::logInit(const std::string &strBaseName)
-{
-    int rc;
-    size_t cch;
-
-    /* default log file name */
-    char szLogFile[RTPATH_MAX];
-    cch = RTStrPrintf(szLogFile, sizeof(szLogFile),
-                      "%s%c%s-Dhcpd.log",
-                      m_strHome.c_str(), RTPATH_DELIMITER, strBaseName.c_str());
-    if (cch >= sizeof(szLogFile))
-        return VERR_BUFFER_OVERFLOW;
-
-
-    /* get a writable copy of the base name */
-    char szBaseName[RTPATH_MAX];
-    rc = RTStrCopy(szBaseName, sizeof(szBaseName), strBaseName.c_str());
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /* sanitize base name some more to be usable in an environment variable name */
-    for (char *p = szBaseName; *p != '\0'; ++p)
-    {
-        if (   *p != '_'
-            && (*p < '0' || '9' < *p)
-            && (*p < 'a' || 'z' < *p)
-            && (*p < 'A' || 'Z' < *p))
-        {
-            *p = '_';
-        }
-    }
-
-
-    /* name of the environment variable to control logging */
-    char szEnvVarBase[128];
-    cch = RTStrPrintf(szEnvVarBase, sizeof(szEnvVarBase),
-                      "VBOXDHCP_%s_RELEASE_LOG", szBaseName);
-    if (cch >= sizeof(szEnvVarBase))
-        return VERR_BUFFER_OVERFLOW;
-
-
-    rc = com::VBoxLogRelCreate("DHCP Server",
-                               szLogFile,
-                               RTLOGFLAGS_PREFIX_TIME_PROG,
-                               "all all.restrict -default.restrict",
-                               szEnvVarBase,
-                               RTLOGDEST_FILE
-#ifdef DEBUG
-                               | RTLOGDEST_STDERR
-#endif
-                               ,
-                               32768 /* cMaxEntriesPerGroup */,
-                               0 /* cHistory */,
-                               0 /* uHistoryFileTime */,
-                               0 /* uHistoryFileSize */,
-                               NULL /* pErrInfo */);
-
-    return rc;
 }
 
