/* $Id: VBoxNetDHCP.cpp $ */
/** @file
 * VBoxNetDHCP - DHCP Service for connecting to IntNet.
 */

/*
 * Copyright (C) 2009-2011 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>

#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 <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                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);
    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                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);
    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 */
    ComPtr<NATNetworkListenerImpl> m_vboxListener;
    /*
     * 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_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;
    memset(&m_CurHdrs, '\0', sizeof(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);

    NetworkManager *netManager = NetworkManager::getNetworkManager();

    netManager->setOurAddress(getIpv4Address());
    netManager->setOurNetmask(getIpv4Netmask());
    netManager->setOurMac(getMacAddress());
    netManager->setService(this);

    if (isMainNeeded())
        rc = initWithMain();
    else
        rc = initNoMain();

    AssertRCReturn(rc, rc);

    return VINF_SUCCESS;
}


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.
 * @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)
    {
        NetworkManager *networkManager = NetworkManager::getNetworkManager();

        switch (uMsgType)
        {
            case RTNET_DHCP_MT_DISCOVER:
                return networkManager->handleDhcpReqDiscover(pDhcpMsg, cb);

            case RTNET_DHCP_MT_REQUEST:
                return networkManager->handleDhcpReqRequest(pDhcpMsg, cb);

            case RTNET_DHCP_MT_DECLINE:
                return networkManager->handleDhcpReqDecline(pDhcpMsg, cb);

            case RTNET_DHCP_MT_RELEASE:
                return 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 = getNetwork();

    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 aVBoxEvents;
    aVBoxEvents.push_back(VBoxEventType_OnHostNameResolutionConfigurationChange);
    rc = createNatListener(m_vboxListener, virtualbox, this, aVBoxEvents);
    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;
}


HRESULT VBoxNetDhcp::HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent)
{
    switch(aEventType)
    {
        case VBoxEventType_OnHostNameResolutionConfigurationChange:
            fetchAndUpdateDnsInfo();
            break;
    }

    return S_OK;
}

/**
 *  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;
    }
    int rc = pDhcp->parseArgs(argc - 1, argv + 1);
    if (rc)
        return rc;

    pDhcp->init();

    /*
     * Try connect the server to the network.
     */
    rc = pDhcp->tryGoOnline();
    if (RT_FAILURE(rc))
    {
        delete pDhcp;
        return 1;
    }

    /*
     * Process requests.
     */
    g_pDhcp = pDhcp;
    rc = pDhcp->run();
    g_pDhcp = NULL;
    delete pDhcp;

    return 0;
}


#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

static LRESULT CALLBACK WindowProc(HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
)
{
    if(uMsg == WM_DESTROY)
    {
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc (hwnd, uMsg, wParam, lParam);
}

static LPCWSTR g_WndClassName = L"VBoxNetDHCPClass";

static DWORD WINAPI MsgThreadProc(__in  LPVOID lpParameter)
{
     HWND                 hwnd = 0;
     HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
     bool bExit = false;

     /* Register the Window Class. */
     WNDCLASS wc;
     wc.style         = 0;
     wc.lpfnWndProc   = WindowProc;
     wc.cbClsExtra    = 0;
     wc.cbWndExtra    = sizeof(void *);
     wc.hInstance     = hInstance;
     wc.hIcon         = NULL;
     wc.hCursor       = NULL;
     wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
     wc.lpszMenuName  = NULL;
     wc.lpszClassName = g_WndClassName;

     ATOM atomWindowClass = RegisterClass(&wc);

     if (atomWindowClass != 0)
     {
         /* Create the window. */
         hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
                 g_WndClassName, g_WndClassName,
                                                   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);

             bExit = true;
         }

         UnregisterClass (g_WndClassName, hInstance);
     }

     if(bExit)
     {
         /* no need any accuracy here, in anyway the DHCP server usually gets terminated with TerminateProcess */
         exit(0);
     }

     return 0;
}


/** (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);

    HANDLE hThread = CreateThread(
      NULL, /*__in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes, */
      0, /*__in       SIZE_T dwStackSize, */
      MsgThreadProc, /*__in       LPTHREAD_START_ROUTINE lpStartAddress,*/
      NULL, /*__in_opt   LPVOID lpParameter,*/
      0, /*__in       DWORD dwCreationFlags,*/
      NULL /*__out_opt  LPDWORD lpThreadId*/
    );

    if(hThread != NULL)
        CloseHandle(hThread);

    return main(__argc, __argv);
}
# endif /* RT_OS_WINDOWS */

#endif /* !VBOX_WITH_HARDENING */

