/**
 * @interface_method_impl{TXSTRANSPORT,pfnInit}
 */
static DECLCALLBACK(int) txsTcpInit(void)
{
    int rc = RTCritSectInit(&g_TcpCritSect);
    if (RT_SUCCESS(rc) && g_enmTcpMode != TXSTCPMODE_CLIENT)
    {
        rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
        if (RT_FAILURE(rc))
        {
            if (rc == VERR_NET_DOWN)
            {
                RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
                          g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
                uint64_t StartMs = RTTimeMilliTS();
                do
                {
                    RTThreadSleep(1000);
                    rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
                } while (   rc == VERR_NET_DOWN
                         && RTTimeMilliTS() - StartMs < 20000);
                if (RT_SUCCESS(rc))
                    RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
            }
            if (RT_FAILURE(rc))
            {
                g_pTcpServer = NULL;
                RTCritSectDelete(&g_TcpCritSect);
                RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
                           g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
            }
        }
    }

    return rc;
}
/**
 * Creates a TCP server that listens for the source machine and passes control
 * over to Console::teleporterTrgServeConnection().
 *
 * @returns VBox status code.
 * @param   pUVM                The user-mode VM handle
 * @param   pMachine            The IMachine for the virtual machine.
 * @param   pErrorMsg           Pointer to the error string for VMSetError.
 * @param   fStartPaused        Whether to start it in the Paused (true) or
 *                              Running (false) state,
 * @param   pProgress           Pointer to the progress object.
 * @param   pfPowerOffOnFailure Whether the caller should power off
 *                              the VM on failure.
 *
 * @remarks The caller expects error information to be set on failure.
 * @todo    Check that all the possible failure paths sets error info...
 */
HRESULT
Console::teleporterTrg(PUVM pUVM, IMachine *pMachine, Utf8Str *pErrorMsg, bool fStartPaused,
                       Progress *pProgress, bool *pfPowerOffOnFailure)
{
    LogThisFunc(("pUVM=%p pMachine=%p fStartPaused=%RTbool pProgress=%p\n", pUVM, pMachine, fStartPaused, pProgress));

    *pfPowerOffOnFailure = true;

    /*
     * Get the config.
     */
    ULONG uPort;
    HRESULT hrc = pMachine->COMGETTER(TeleporterPort)(&uPort);
    if (FAILED(hrc))
        return hrc;
    ULONG const uPortOrg = uPort;

    Bstr bstrAddress;
    hrc = pMachine->COMGETTER(TeleporterAddress)(bstrAddress.asOutParam());
    if (FAILED(hrc))
        return hrc;
    Utf8Str strAddress(bstrAddress);
    const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();

    Bstr bstrPassword;
    hrc = pMachine->COMGETTER(TeleporterPassword)(bstrPassword.asOutParam());
    if (FAILED(hrc))
        return hrc;
    Utf8Str strPassword(bstrPassword);
    strPassword.append('\n');           /* To simplify password checking. */

    /*
     * Create the TCP server.
     */
    int vrc;
    PRTTCPSERVER hServer;
    if (uPort)
        vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
    else
    {
        for (int cTries = 10240; cTries > 0; cTries--)
        {
            uPort = RTRandU32Ex(cTries >= 8192 ? 49152 : 1024, 65534);
            vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
            if (vrc != VERR_NET_ADDRESS_IN_USE)
                break;
        }
        if (RT_SUCCESS(vrc))
        {
            hrc = pMachine->COMSETTER(TeleporterPort)(uPort);
            if (FAILED(hrc))
            {
                RTTcpServerDestroy(hServer);
                return hrc;
            }
        }
    }
    if (RT_FAILURE(vrc))
        return setError(E_FAIL, tr("RTTcpServerCreateEx failed with status %Rrc"), vrc);

    /*
     * Create a one-shot timer for timing out after 5 mins.
     */
    RTTIMERLR hTimerLR;
    vrc = RTTimerLRCreateEx(&hTimerLR, 0 /*ns*/, RTTIMER_FLAGS_CPU_ANY, teleporterDstTimeout, hServer);
    if (RT_SUCCESS(vrc))
    {
        vrc = RTTimerLRStart(hTimerLR, 5*60*UINT64_C(1000000000) /*ns*/);
        if (RT_SUCCESS(vrc))
        {
            /*
             * Do the job, when it returns we're done.
             */
            TeleporterStateTrg theState(this, pUVM, pProgress, pMachine, mControl, &hTimerLR, fStartPaused);
            theState.mstrPassword      = strPassword;
            theState.mhServer          = hServer;

            void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(&theState));
            if (pProgress->setCancelCallback(teleporterProgressCancelCallback, pvUser))
            {
                LogRel(("Teleporter: Waiting for incoming VM...\n"));
                hrc = pProgress->SetNextOperation(Bstr(tr("Waiting for incoming VM")).raw(), 1);
                if (SUCCEEDED(hrc))
                {
                    vrc = RTTcpServerListen(hServer, Console::teleporterTrgServeConnection, &theState);
                    pProgress->setCancelCallback(NULL, NULL);

                    if (vrc == VERR_TCP_SERVER_STOP)
                    {
                        vrc = theState.mRc;
                        /* Power off the VM on failure unless the state callback
                           already did that. */
                        *pfPowerOffOnFailure = false;
                        if (RT_SUCCESS(vrc))
                            hrc = S_OK;
                        else
                        {
                            VMSTATE enmVMState = VMR3GetStateU(pUVM);
                            if (    enmVMState != VMSTATE_OFF
                                    &&  enmVMState != VMSTATE_POWERING_OFF)
                                *pfPowerOffOnFailure = true;

                            /* Set error. */
                            if (pErrorMsg->length())
                                hrc = setError(E_FAIL, "%s", pErrorMsg->c_str());
                            else
                                hrc = setError(E_FAIL, tr("Teleporation failed (%Rrc)"), vrc);
                        }
                    }
                    else if (vrc == VERR_TCP_SERVER_SHUTDOWN)
                    {
                        BOOL fCanceled = TRUE;
                        hrc = pProgress->COMGETTER(Canceled)(&fCanceled);
                        if (FAILED(hrc) || fCanceled)
                            hrc = setError(E_FAIL, tr("Teleporting canceled"));
                        else
                            hrc = setError(E_FAIL, tr("Teleporter timed out waiting for incoming connection"));
                        LogRel(("Teleporter: RTTcpServerListen aborted - %Rrc\n", vrc));
                    }
                    else
                    {
                        hrc = setError(E_FAIL, tr("Unexpected RTTcpServerListen status code %Rrc"), vrc);
                        LogRel(("Teleporter: Unexpected RTTcpServerListen rc: %Rrc\n", vrc));
                    }
                }
                else
                    LogThisFunc(("SetNextOperation failed, %Rhrc\n", hrc));
            }
            else
            {
                LogThisFunc(("Canceled - check point #1\n"));
                hrc = setError(E_FAIL, tr("Teleporting canceled"));
            }
        }
        else
            hrc = setError(E_FAIL, "RTTimerLRStart -> %Rrc", vrc);

        RTTimerLRDestroy(hTimerLR);
    }
    else
        hrc = setError(E_FAIL, "RTTimerLRCreate -> %Rrc", vrc);
    RTTcpServerDestroy(hServer);

    /*
     * If we change TeleporterPort above, set it back to it's original
     * value before returning.
     */
    if (uPortOrg != uPort)
    {
        ErrorInfoKeeper Eik;
        pMachine->COMSETTER(TeleporterPort)(uPortOrg);
    }

    return hrc;
}
/**
 * Construct a TCP socket stream driver instance.
 *
 * @copydoc FNPDMDRVCONSTRUCT
 */
static DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
    RT_NOREF(fFlags);
    PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
    PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);

    /*
     * Init the static parts.
     */
    pThis->pDrvIns                      = pDrvIns;
    pThis->pszLocation                  = NULL;
    pThis->fIsServer                    = false;

    pThis->hTcpServ                     = NULL;
    pThis->hTcpSock                     = NIL_RTSOCKET;

    pThis->hPollSet                     = NIL_RTPOLLSET;
    pThis->hPipeWakeR                   = NIL_RTPIPE;
    pThis->hPipeWakeW                   = NIL_RTPIPE;
    pThis->fTcpSockInPollSet            = false;

    pThis->ListenThread                 = NIL_RTTHREAD;
    pThis->fShutdown                    = false;
    /* IBase */
    pDrvIns->IBase.pfnQueryInterface    = drvTCPQueryInterface;
    /* IStream */
    pThis->IStream.pfnPoll              = drvTcpPoll;
    pThis->IStream.pfnPollInterrupt     = drvTcpPollInterrupt;
    pThis->IStream.pfnRead              = drvTcpRead;
    pThis->IStream.pfnWrite             = drvTcpWrite;

    /*
     * Validate and read the configuration.
     */
    PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");

    int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
    rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);

    rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);

    rc = RTPollSetCreate(&pThis->hPollSet);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);

    rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
                            RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
                            DRVTCP_POLLSET_ID_WAKEUP);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
                                   pDrvIns->iInstance, pThis->pszLocation);

    /*
     * Create/Open the socket.
     */
    if (pThis->fIsServer)
    {
        uint32_t uPort = 0;
        rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort);
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                       N_("DrvTCP#%d: The port part of the location is not a numerical value"),
                                       pDrvIns->iInstance);

        /** @todo Allow binding to distinct interfaces. */
        rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ);
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc,  RT_SRC_POS,
                                       N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance);

        rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
                            RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc,  RT_SRC_POS,
                                       N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance);
    }
    else
    {
        char *pszPort = strchr(pThis->pszLocation, ':');
        if (!pszPort)
            return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
                                       N_("DrvTCP#%d: The location misses the port to connect to"),
                                       pDrvIns->iInstance);

        *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
        uint32_t uPort = 0;
        rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                       N_("DrvTCP#%d: The port part of the location is not a numerical value"),
                                       pDrvIns->iInstance);

        rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock);
        *pszPort = ':'; /* Restore delimiter before checking the status. */
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                       N_("DrvTCP#%d failed to connect to socket %s"),
                                       pDrvIns->iInstance, pThis->pszLocation);

        rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
                                RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR,
                                DRVTCP_POLLSET_ID_SOCKET);
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                       N_("DrvTCP#%d failed to add socket for %s to poll set"),
                                       pDrvIns->iInstance, pThis->pszLocation);

        pThis->fTcpSockInPollSet = true;
    }

    LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
    return VINF_SUCCESS;
}