DrvTCP.cpp revision 0dd04aaaddd3b919138a2c0223403806e0fd4ca1
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * TCP socket driver implementing the IStream interface.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Copyright (C) 2006-2015 Oracle Corporation.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * This file was contributed by Alexey Eromenko (derived from DrvNamedPipe)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * available from http://www.virtualbox.org. This file is free software;
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * you can redistribute it and/or modify it under the terms of the GNU
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * General Public License (GPL) as published by the Free Software
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/*******************************************************************************
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync* Header Files *
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync*******************************************************************************/
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync#else /* !RT_OS_WINDOWS */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync#endif /* !RT_OS_WINDOWS */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/*******************************************************************************
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync* Defined Constants And Macros *
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync*******************************************************************************/
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/** Converts a pointer to DRVTCP::IMedia to a PDRVTCP. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync#define PDMISTREAM_2_DRVTCP(pInterface) ( (PDRVTCP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTCP, IStream)) )
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/*******************************************************************************
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync* Structures and Typedefs *
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync*******************************************************************************/
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * TCP driver instance data.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @implements PDMISTREAM
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsynctypedef struct DRVTCP
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /** The stream interface. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /** Pointer to the driver instance. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /** Pointer to the TCP server address:port or port only. (Freed by MM) */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /** Flag whether VirtualBox represents the server or client side. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /** Socket handle of the TCP socket for server. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /** Socket handle of the TCP socket connection. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /** Thread for listening for new connections. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /** Flag to signal listening thread to shut down. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync bool volatile fShutdown;
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/*******************************************************************************
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync* Internal Functions *
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync*******************************************************************************/
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/** @copydoc PDMISTREAM::pfnRead */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsyncstatic DECLCALLBACK(int) drvTCPRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync cbReallyRead = recv(pThis->TCPConnection, (char *)pvBuf, cbBuf, 0);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/** @copydoc PDMISTREAM::pfnWrite */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsyncstatic DECLCALLBACK(int) drvTCPWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync cbWritten = send(pThis->TCPConnection, (const char *)pvBuf, cbBuf, 0);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @interface_method_impl{PDMIBASE,pfnQueryInterface}
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsyncstatic DECLCALLBACK(void *) drvTCPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IStream);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/* -=-=-=-=- listen thread -=-=-=-=- */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Receive thread loop.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @returns 0 on success.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @param ThreadSelf Thread handle to this thread.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @param pvUser User argument.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsyncstatic DECLCALLBACK(int) drvTCPListenLoop(RTTHREAD ThreadSelf, void *pvUser)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogRel(("DrvTCP%d: listen failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync if (s == -1)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogRel(("DrvTCP%d: accept failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Common worker for drvTCPPowerOff and drvTCPDestructor.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @param pThis The instance data.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Signal shutdown of the listener thread.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Power off a TCP socket stream driver instance.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * This does most of the destruction work, to avoid ordering dependencies.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @param pDrvIns The driver instance data.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsyncstatic DECLCALLBACK(void) drvTCPPowerOff(PPDMDRVINS pDrvIns)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Destruct a TCP socket stream driver instance.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Most VM resources are freed by the VM. This callback is provided so that
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * any non-VM resources can be freed correctly.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @param pDrvIns The driver instance data.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsyncstatic DECLCALLBACK(void) drvTCPDestruct(PPDMDRVINS pDrvIns)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * While the thread exits, clean up as much as we can.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync int rc = shutdown(pThis->TCPConnection, SHUT_RDWR);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Wait for the thread.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync int rc = RTThreadWait(pThis->ListenThread, 30000, NULL);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Construct a TCP socket stream driver instance.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * @copydoc FNPDMDRVCONSTRUCT
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsyncstatic DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Init the static parts.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* IBase */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync pDrvIns->IBase.pfnQueryInterface = drvTCPQueryInterface;
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* IStream */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Validate and read the configuration.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * Create/Open the socket.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync if (s == -1)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync N_("DrvTCP#%d failed to create socket"), pDrvIns->iInstance);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync addr.sin_port = htons(/*PORT*/ atoi(pThis->pszLocation));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* Bind address to the telnet socket. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance);
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* Connect to the telnet socket. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync * TCP stream driver registration record.
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* u32Version */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* szName */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* szRCMod */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* szR0Mod */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pszDescription */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync "TCP serial stream driver.",
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* fFlags */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* fClass. */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* cMaxInstances */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* cbInstance */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnConstruct */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnDestruct */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnRelocate */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnIOCtl */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnPowerOn */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnReset */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnSuspend */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnResume */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnAttach */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnDetach */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnPowerOff */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* pfnSoftReset */
0dd04aaaddd3b919138a2c0223403806e0fd4ca1vboxsync /* u32EndVersion */