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;
}
/**
 * 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);
}
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;
}
/**
 * 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;
        }
        else {
            /* 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;
}
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;
}
/**
 * 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);

    /* 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 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;
}
bool operator< (const Lease& lhs, const Lease& rhs)
{
    return (   (lhs.getAddress() < rhs.getAddress())
            || (lhs.issued() < rhs.issued()));
}
/**
 * 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 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;
                    }

                    char *pszDomainName = (char *)&opt.au8RawOpt[0];

                    strcpy(pszDomainName, domainName.c_str());
                    opt.cbRawOpt = domainName.length();
                }
                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;
}