/* $Id: VUSBSniffer.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */ /** @file * Virtual USB - Sniffer facility. */ /* * Copyright (C) 2014-2020 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. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DRV_VUSB #include #include #include #include #include #include #include #include #include "VUSBSnifferInternal.h" /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * The internal VUSB sniffer state. */ typedef struct VUSBSNIFFERINT { /** The file handle to dump to. */ RTFILE hFile; /** Fast Mutex protecting the state against concurrent access. */ RTSEMFASTMUTEX hMtx; /** File stream. */ VUSBSNIFFERSTRM Strm; /** Pointer to the used format. */ PCVUSBSNIFFERFMT pFmt; /** Format specific state - variable in size. */ uint8_t abFmt[1]; } VUSBSNIFFERINT; /** Pointer to the internal VUSB sniffer state. */ typedef VUSBSNIFFERINT *PVUSBSNIFFERINT; /********************************************************************************************************************************* * Static Variables * *********************************************************************************************************************************/ static PCVUSBSNIFFERFMT s_aVUsbSnifferFmts[] = { &g_VUsbSnifferFmtPcapNg, &g_VUsbSnifferFmtUsbMon, &g_VUsbSnifferFmtVmx, }; /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ /** @interface_method_impl{VUSBSNIFFERSTRM,pfnWrite} */ static DECLCALLBACK(int) vusbSnifferStrmWrite(PVUSBSNIFFERSTRM pStrm, const void *pvBuf, size_t cbBuf) { PVUSBSNIFFERINT pThis = RT_FROM_MEMBER(pStrm, VUSBSNIFFERINT, Strm); return RTFileWrite(pThis->hFile, pvBuf, cbBuf, NULL); } /** * Returns a supporting format writer taken from the given format name. * * @returns Pointer to the format structure or NULL if none was found. * @param pszFmt The format to use. */ static PCVUSBSNIFFERFMT vusbSnifferGetFmtFromString(const char *pszFmt) { for (unsigned i = 0; i < RT_ELEMENTS(s_aVUsbSnifferFmts); i++) { if (!RTStrICmp(pszFmt, s_aVUsbSnifferFmts[i]->szName)) return s_aVUsbSnifferFmts[i]; } return NULL; } /** * Returns a supporting format writer taken from the file suffix. * * @returns Pointer to the format structure or NULL if none was found. * @param pszFilename The file name to take the suffix from. */ static PCVUSBSNIFFERFMT vusbSnifferGetFmtFromFilename(const char *pszFilename) { const char *pszFileExt = RTPathSuffix(pszFilename); if (!pszFileExt) return NULL; pszFileExt++; /* Skip the dot. */ for (unsigned i = 0; i < RT_ELEMENTS(s_aVUsbSnifferFmts); i++) { unsigned idxFileExt = 0; while (s_aVUsbSnifferFmts[i]->papszFileExts[idxFileExt]) { if (!RTStrICmp(pszFileExt, s_aVUsbSnifferFmts[i]->papszFileExts[idxFileExt])) return s_aVUsbSnifferFmts[i]; idxFileExt++; } } return NULL; } DECLHIDDEN(int) VUSBSnifferCreate(PVUSBSNIFFER phSniffer, uint32_t fFlags, const char *pszCaptureFilename, const char *pszFmt, const char *pszDesc) { RT_NOREF(pszDesc); int rc = VINF_SUCCESS; PVUSBSNIFFERINT pThis = NULL; PCVUSBSNIFFERFMT pFmt = NULL; if (pszFmt) pFmt = vusbSnifferGetFmtFromString(pszFmt); else pFmt = vusbSnifferGetFmtFromFilename(pszCaptureFilename); if (!pFmt) return VERR_NOT_FOUND; pThis = (PVUSBSNIFFERINT)RTMemAllocZ(RT_UOFFSETOF_DYN(VUSBSNIFFERINT, abFmt[pFmt->cbFmt])); if (pThis) { pThis->hFile = NIL_RTFILE; pThis->hMtx = NIL_RTSEMFASTMUTEX; pThis->pFmt = pFmt; pThis->Strm.pfnWrite = vusbSnifferStrmWrite; rc = RTSemFastMutexCreate(&pThis->hMtx); if (RT_SUCCESS(rc)) { uint32_t fFileFlags = RTFILE_O_DENY_NONE | RTFILE_O_WRITE | RTFILE_O_READ; if (fFlags & VUSBSNIFFER_F_NO_REPLACE) fFileFlags |= RTFILE_O_CREATE; else fFileFlags |= RTFILE_O_CREATE_REPLACE; rc = RTFileOpen(&pThis->hFile, pszCaptureFilename, fFileFlags); if (RT_SUCCESS(rc)) { rc = pThis->pFmt->pfnInit((PVUSBSNIFFERFMTINT)&pThis->abFmt[0], &pThis->Strm); if (RT_SUCCESS(rc)) { *phSniffer = pThis; return VINF_SUCCESS; } RTFileClose(pThis->hFile); pThis->hFile = NIL_RTFILE; RTFileDelete(pszCaptureFilename); } RTSemFastMutexDestroy(pThis->hMtx); pThis->hMtx = NIL_RTSEMFASTMUTEX; } RTMemFree(pThis); } else rc = VERR_NO_MEMORY; return rc; } /** * Destroys the given VUSB sniffer instance. * * @returns nothing. * @param hSniffer The sniffer instance to destroy. */ DECLHIDDEN(void) VUSBSnifferDestroy(VUSBSNIFFER hSniffer) { PVUSBSNIFFERINT pThis = hSniffer; int rc = RTSemFastMutexRequest(pThis->hMtx); AssertRC(rc); pThis->pFmt->pfnDestroy((PVUSBSNIFFERFMTINT)&pThis->abFmt[0]); if (pThis->hFile != NIL_RTFILE) RTFileClose(pThis->hFile); RTSemFastMutexRelease(pThis->hMtx); RTSemFastMutexDestroy(pThis->hMtx); RTMemFree(pThis); } /** * Records an VUSB event. * * @returns VBox status code. * @param hSniffer The sniffer instance. * @param pUrb The URB triggering the event. * @param enmEvent The type of event to record. */ DECLHIDDEN(int) VUSBSnifferRecordEvent(VUSBSNIFFER hSniffer, PVUSBURB pUrb, VUSBSNIFFEREVENT enmEvent) { int rc = VINF_SUCCESS; PVUSBSNIFFERINT pThis = hSniffer; /* Write the packet to the capture file. */ rc = RTSemFastMutexRequest(pThis->hMtx); if (RT_SUCCESS(rc)) { rc = pThis->pFmt->pfnRecordEvent((PVUSBSNIFFERFMTINT)&pThis->abFmt[0], pUrb, enmEvent); RTSemFastMutexRelease(pThis->hMtx); } return rc; }