/* * 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) { DWORD res, cbPortName; HKEY hroot; HKEY hToken = NULL; PLOCALMON_PORT pPort; PLOCALMON_HANDLE pLocalmon = pXcv->pLocalmon; PWSTR PortName = (PWSTR)pInputData; FIXME("LcmXcvAddPort : %s\n", debugstr_w( (LPWSTR) PortName ) ); if (!pLocalmon ) { res = ERROR_INVALID_PARAMETER; goto Cleanup; } // This action can only happen at SERVER_ACCESS_ADMINISTER access level. if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER)) { res = ERROR_ACCESS_DENIED; goto Cleanup; } // Switch to the SYSTEM context for modifying the registry. hToken = RevertToPrinterSelf(); if (!hToken) { res = GetLastError(); ERR("RevertToPrinterSelf failed with error %lu!\n", res); goto Cleanup; } res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot); if (res == ERROR_SUCCESS) { if ( DoesPortExist( PortName ) ) { RegCloseKey(hroot); FIXME("=> %u\n", ERROR_ALREADY_EXISTS); res = ERROR_ALREADY_EXISTS; goto Cleanup; } cbPortName = (wcslen( PortName ) + 1) * sizeof(WCHAR); // Create a new LOCALMON_PORT structure for it. pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + cbPortName); if (!pPort) { res = ERROR_NOT_ENOUGH_MEMORY; RegCloseKey( hroot ); goto Cleanup; } memset( pPort, 0, sizeof(LOCALMON_PORT) + cbPortName ); pPort->hFile = INVALID_HANDLE_VALUE; pPort->pLocalmon = pLocalmon; pPort->pwszPortName = wcscpy( (PWSTR)(pPort+1), PortName ); // Insert it into the Registry list. InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry); res = RegSetValueExW(hroot, PortName, 0, REG_SZ, (const BYTE *) L"", sizeof(L"")); RegCloseKey(hroot); } FIXME("=> %u\n", res); Cleanup: if (hToken) ImpersonatePrinterClient(hToken); SetLastError(res); return res; } /** * @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) { DWORD res; HKEY hroot; HKEY hToken = NULL; PLOCALMON_HANDLE pLocalmon = pXcv->pLocalmon; PLOCALMON_PORT pPort = NULL; PLIST_ENTRY pEntry; PWSTR pPortName = (PWSTR)pInputData; FIXME("LcmXcvDeletePort : %s\n", debugstr_w( pPortName ) ); if (!pLocalmon ) { res = ERROR_INVALID_PARAMETER; goto Cleanup; } // This action can only happen at SERVER_ACCESS_ADMINISTER access level. if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER)) { res = ERROR_ACCESS_DENIED; goto Cleanup; } // Switch to the SYSTEM context for modifying the registry. hToken = RevertToPrinterSelf(); if (!hToken) { res = GetLastError(); ERR("RevertToPrinterSelf failed with error %lu!\n", res); goto Cleanup; } res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot); if ( res == ERROR_SUCCESS ) { res = RegDeleteValueW(hroot, pPortName ); RegCloseKey(hroot); if ( res == ERROR_SUCCESS ) { EnterCriticalSection(&pLocalmon->Section); if (!IsListEmpty(&pLocalmon->RegistryPorts) ) { for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink) { pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry); if (wcscmp(pPort->pwszPortName, pPortName) == 0) break; } } LeaveCriticalSection(&pLocalmon->Section); if ( pPort ) { FIXME("LcmXcvDeletePort removed Port Entry\n"); EnterCriticalSection(&pPort->pLocalmon->Section); RemoveEntryList(&pPort->Entry); LeaveCriticalSection(&pPort->pLocalmon->Section); DllFreeSplMem(pPort); } } FIXME("LcmXcvDeletePort => %u with %u\n", res, GetLastError() ); } Cleanup: if (hToken) ImpersonatePrinterClient(hToken); SetLastError(res); return res; } /** * @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 (!pcbOutputNeeded) return ERROR_INVALID_PARAMETER; *pcbOutputNeeded = sizeof(wszMonitorUI); // Check if the supplied buffer is large enough. if (cbOutputData < *pcbOutputNeeded) return ERROR_INSUFFICIENT_BUFFER; if (!pOutputData) return ERROR_INVALID_PARAMETER; // 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) { DWORD res; TRACE("HandlePortIsValid : pInputData %s\n", debugstr_w( (LPWSTR) pInputData)); res = GetTypeFromName((LPCWSTR) pInputData); TRACE("HandlePortIsValid : detected as %u\n", res); /* names, that we have recognized, are valid */ if (res) return ERROR_SUCCESS; TRACE("=> %u\n", GetLastError()); /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */ return GetLastError(); } /** * @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. EnterCriticalSection(&pXcv->pLocalmon->Section); RemoveEntryList(&pXcv->Entry); LeaveCriticalSection(&pXcv->pLocalmon->Section); DllFreeSplMem(pXcv); SetLastError(ERROR_SUCCESS); return TRUE; } DWORD WINAPI LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) { FIXME("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; FIXME("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); }