/* * PROJECT: ReactOS Local Port Monitor * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Implementation of Xcv* and support functions * COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org) */ #include "precomp.h" static DWORD _HandleAddPort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded) { return ERROR_CALL_NOT_IMPLEMENTED; } /** * @name _HandleConfigureLPTPortCommandOK * * Writes the value for "TransmissionRetryTimeout" to the registry. Checks for granted SERVER_ACCESS_ADMINISTER access. * Actually the opposite of _HandleGetTransmissionRetryTimeout, but name kept for compatibility. * * @param pXcv * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port. * * @param pInputData * Pointer to a Unicode string containing the value to be written to the registry. * * @param pcbOutputNeeded * Pointer to a DWORD that will be zeroed on return. * * @return * An error code indicating success or failure. */ static DWORD _HandleConfigureLPTPortCommandOK(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded) { DWORD cbBuffer; DWORD dwErrorCode; HKEY hKey = NULL; HKEY hToken = NULL; // Sanity checks if (!pXcv || !pInputData || !pcbOutputNeeded) { dwErrorCode = ERROR_INVALID_PARAMETER; goto Cleanup; } *pcbOutputNeeded = 0; // This action can only happen at SERVER_ACCESS_ADMINISTER access level. if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER)) { dwErrorCode = ERROR_ACCESS_DENIED; goto Cleanup; } // Switch to the SYSTEM context for modifying the registry. hToken = RevertToPrinterSelf(); if (!hToken) { dwErrorCode = GetLastError(); ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode); goto Cleanup; } // Open the key where our value is stored. dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_SET_VALUE, &hKey); if (dwErrorCode != ERROR_SUCCESS) { ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); goto Cleanup; } // We don't use cbInputData here, because the buffer pInputData could be bigger than the data it contains. cbBuffer = (wcslen((PWSTR)pInputData) + 1) * sizeof(WCHAR); // Write the value to the registry. dwErrorCode = (DWORD)RegSetValueExW(hKey, L"TransmissionRetryTimeout", 0, REG_SZ, pInputData, cbBuffer); if (dwErrorCode != ERROR_SUCCESS) { ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode); goto Cleanup; } Cleanup: if (hKey) RegCloseKey(hKey); if (hToken) ImpersonatePrinterClient(hToken); return dwErrorCode; } static DWORD _HandleDeletePort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded) { return ERROR_CALL_NOT_IMPLEMENTED; } /** * @name _HandleGetDefaultCommConfig * * Gets the default configuration of a legacy port. * The opposite function is _HandleSetDefaultCommConfig. * * @param pInputData * The port name (without colon!) whose default configuration you want to get. * * @param pOutputData * Pointer to a COMMCONFIG structure that will receive the configuration information. * * @param cbOutputData * Size of the variable pointed to by pOutputData. * * @param pcbOutputNeeded * Pointer to a DWORD that contains the required size for pOutputData on return. * * @return * An error code indicating success or failure. */ static DWORD _HandleGetDefaultCommConfig(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) { // Sanity checks if (!pInputData || !pcbOutputNeeded) return ERROR_INVALID_PARAMETER; *pcbOutputNeeded = sizeof(COMMCONFIG); // Check if the supplied buffer is large enough. if (cbOutputData < *pcbOutputNeeded) return ERROR_INSUFFICIENT_BUFFER; // Finally get the port configuration. if (!GetDefaultCommConfigW((PCWSTR)pInputData, (LPCOMMCONFIG)pOutputData, pcbOutputNeeded)) return GetLastError(); return ERROR_SUCCESS; } /** * @name _HandleGetTransmissionRetryTimeout * * Reads the value for "TransmissionRetryTimeout" from the registry and converts it to a DWORD. * The opposite function is _HandleConfigureLPTPortCommandOK. * * @param pOutputData * Pointer to a DWORD that will receive the timeout value. * * @param cbOutputData * Size of the variable pointed to by pOutputData. * * @param pcbOutputNeeded * Pointer to a DWORD that contains the required size for pOutputData on return. * * @return * An error code indicating success or failure. */ static DWORD _HandleGetTransmissionRetryTimeout(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) { DWORD dwTimeout; // Sanity checks if (!pOutputData || !pcbOutputNeeded) return ERROR_INVALID_PARAMETER; *pcbOutputNeeded = sizeof(DWORD); // Check if the supplied buffer is large enough. if (cbOutputData < *pcbOutputNeeded) return ERROR_INSUFFICIENT_BUFFER; // Retrieve and copy the number. dwTimeout = GetLPTTransmissionRetryTimeout(); CopyMemory(pOutputData, &dwTimeout, sizeof(DWORD)); return ERROR_SUCCESS; } /** * @name _HandleMonitorUI * * Returns the filename of the associated UI DLL for this Port Monitor. * * @param pOutputData * Pointer to a Unicode string that will receive the DLL filename. * * @param cbOutputData * Size of the variable pointed to by pOutputData. * * @param pcbOutputNeeded * Pointer to a DWORD that contains the required size for pOutputData on return. * * @return * An error code indicating success or failure. */ static DWORD _HandleMonitorUI(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) { const WCHAR wszMonitorUI[] = L"LocalUI.dll"; // Sanity checks if (!pOutputData || !pcbOutputNeeded) return ERROR_INVALID_PARAMETER; *pcbOutputNeeded = sizeof(wszMonitorUI); // Check if the supplied buffer is large enough. if (cbOutputData < *pcbOutputNeeded) return ERROR_INSUFFICIENT_BUFFER; // Copy the string. CopyMemory(pOutputData, wszMonitorUI, sizeof(wszMonitorUI)); return ERROR_SUCCESS; } /** * @name _HandlePortExists * * Checks all Port Monitors installed on the local system to find out if a given port already exists. * * @param pInputData * Pointer to a Unicode string specifying the port name to check. * * @param pOutputData * Pointer to a BOOL that receives the result of the check. * * @param cbOutputData * Size of the variable pointed to by pOutputData. * * @param pcbOutputNeeded * Pointer to a DWORD that contains the required size for pOutputData on return. * * @return * An error code indicating success or failure. */ static DWORD _HandlePortExists(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) { // Sanity checks if (!pInputData || !pOutputData || !pcbOutputNeeded) return ERROR_INVALID_PARAMETER; *pcbOutputNeeded = sizeof(BOOL); // Check if the supplied buffer is large enough. if (cbOutputData < *pcbOutputNeeded) return ERROR_INSUFFICIENT_BUFFER; // Return the check result and error code. *(PBOOL)pOutputData = DoesPortExist((PCWSTR)pInputData); return GetLastError(); } static DWORD _HandlePortIsValid(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) { return ERROR_CALL_NOT_IMPLEMENTED; } /** * @name _HandleSetDefaultCommConfig * * Sets the default configuration of a legacy port. Checks for granted SERVER_ACCESS_ADMINISTER access. * You have to supply the port name (with colon!) in XcvOpenPort. * The opposite function is _HandleGetDefaultCommConfig. * * @param pXcv * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port. * * @param pInputData * Pointer to the COMMCONFIG structure that shall be passed to SetDefaultCommConfigW. * * @param pcbOutputNeeded * Pointer to a DWORD that will be zeroed on return. * * @return * An error code indicating success or failure. */ static DWORD _HandleSetDefaultCommConfig(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded) { DWORD dwErrorCode; HANDLE hToken = NULL; LPCOMMCONFIG pCommConfig; PWSTR pwszPortNameWithoutColon = NULL; // Sanity checks // pwszObject needs to be at least 2 characters long to be a port name with a trailing colon. if (!pXcv || !pXcv->pwszObject || !pXcv->pwszObject[0] || !pXcv->pwszObject[1] || !pInputData || !pcbOutputNeeded) { dwErrorCode = ERROR_INVALID_PARAMETER; goto Cleanup; } *pcbOutputNeeded = 0; // This action can only happen at SERVER_ACCESS_ADMINISTER access level. if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER)) { dwErrorCode = ERROR_ACCESS_DENIED; goto Cleanup; } // SetDefaultCommConfigW needs the port name without colon. dwErrorCode = GetPortNameWithoutColon(pXcv->pwszObject, &pwszPortNameWithoutColon); if (dwErrorCode != ERROR_SUCCESS) goto Cleanup; // Switch to the SYSTEM context for setting the port configuration. hToken = RevertToPrinterSelf(); if (!hToken) { dwErrorCode = GetLastError(); ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode); goto Cleanup; } // Finally pass the parameters to SetDefaultCommConfigW. pCommConfig = (LPCOMMCONFIG)pInputData; if (!SetDefaultCommConfigW(pwszPortNameWithoutColon, pCommConfig, pCommConfig->dwSize)) { dwErrorCode = GetLastError(); ERR("SetDefaultCommConfigW failed with error %lu!\n", dwErrorCode); goto Cleanup; } dwErrorCode = ERROR_SUCCESS; Cleanup: if (hToken) ImpersonatePrinterClient(hToken); if (pwszPortNameWithoutColon) DllFreeSplMem(pwszPortNameWithoutColon); return dwErrorCode; } BOOL WINAPI LocalmonXcvClosePort(HANDLE hXcv) { PLOCALMON_XCV pXcv = (PLOCALMON_XCV)hXcv; TRACE("LocalmonXcvClosePort(%p)\n", hXcv); // Sanity checks if (!pXcv) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Remove it from the list and free the memory. RemoveEntryList(&pXcv->Entry); DllFreeSplMem(pXcv); SetLastError(ERROR_SUCCESS); return TRUE; } DWORD WINAPI LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) { TRACE("LocalmonXcvDataPort(%p, %S, %p, %lu, %p, %lu, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded); // Sanity checks if (!pszDataName) return ERROR_INVALID_PARAMETER; // Call the appropriate handler for the requested data name. if (wcscmp(pszDataName, L"AddPort") == 0) return _HandleAddPort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded); if (wcscmp(pszDataName, L"ConfigureLPTPortCommandOK") == 0) return _HandleConfigureLPTPortCommandOK((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded); if (wcscmp(pszDataName, L"DeletePort") == 0) return _HandleDeletePort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded); if (wcscmp(pszDataName, L"GetDefaultCommConfig") == 0) return _HandleGetDefaultCommConfig(pInputData, pOutputData, cbOutputData, pcbOutputNeeded); if (wcscmp(pszDataName, L"GetTransmissionRetryTimeout") == 0) return _HandleGetTransmissionRetryTimeout(pOutputData, cbOutputData, pcbOutputNeeded); if (wcscmp(pszDataName, L"MonitorUI") == 0) return _HandleMonitorUI(pOutputData, cbOutputData, pcbOutputNeeded); if (wcscmp(pszDataName, L"PortExists") == 0) return _HandlePortExists(pInputData, pOutputData, cbOutputData, pcbOutputNeeded); if (wcscmp(pszDataName, L"PortIsValid") == 0) return _HandlePortIsValid(pInputData, pOutputData, cbOutputData, pcbOutputNeeded); if (wcscmp(pszDataName, L"SetDefaultCommConfig") == 0) return _HandleSetDefaultCommConfig((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded); return ERROR_INVALID_PARAMETER; } BOOL WINAPI LocalmonXcvOpenPort(HANDLE hMonitor, PCWSTR pwszObject, ACCESS_MASK GrantedAccess, PHANDLE phXcv) { DWORD cbObject = 0; DWORD dwErrorCode; PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor; PLOCALMON_XCV pXcv; TRACE("LocalmonXcvOpenPort(%p, %S, %lu, %p)\n", hMonitor, pwszObject, GrantedAccess, phXcv); // Sanity checks if (!pLocalmon || !phXcv) { dwErrorCode = ERROR_INVALID_PARAMETER; goto Cleanup; } if (pwszObject) cbObject = (wcslen(pwszObject) + 1) * sizeof(WCHAR); // Create a new LOCALMON_XCV structure and fill the relevant fields. pXcv = DllAllocSplMem(sizeof(LOCALMON_XCV) + cbObject); if (!pXcv) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); goto Cleanup; } pXcv->pLocalmon = pLocalmon; pXcv->GrantedAccess = GrantedAccess; if (cbObject) { pXcv->pwszObject = (PWSTR)((PBYTE)pXcv + sizeof(LOCALMON_XCV)); CopyMemory(pXcv->pwszObject, pwszObject, cbObject); } InsertTailList(&pLocalmon->XcvHandles, &pXcv->Entry); // Return it as the Xcv handle. *phXcv = (HANDLE)pXcv; dwErrorCode = ERROR_SUCCESS; Cleanup: SetLastError(dwErrorCode); return (dwErrorCode == ERROR_SUCCESS); }