1 /*
2  * PROJECT:     ReactOS Local Port Monitor
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Various support functions shared by multiple files
5  * COPYRIGHT:   Copyright 2015 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 /**
11  * @name DoesPortExist
12  *
13  * Checks all Port Monitors installed on the local system to find out if a given port already exists.
14  *
15  * @param pwszPortName
16  * The port name to check.
17  *
18  * @return
19  * TRUE if a port with that name already exists on the local system.
20  * If the return value is FALSE, either the port doesn't exist or an error occurred.
21  * Use GetLastError in this case to check the error case.
22  */
23 BOOL
24 DoesPortExist(PCWSTR pwszPortName)
25 {
26     BOOL bReturnValue = FALSE;
27     DWORD cbNeeded;
28     DWORD dwErrorCode;
29     DWORD dwReturned;
30     DWORD i;
31     PPORT_INFO_1W p;
32     PPORT_INFO_1W pPortInfo1 = NULL;
33 
34     // Determine the required buffer size.
35     EnumPortsW(NULL, 1, NULL, 0, &cbNeeded, &dwReturned);
36     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
37     {
38         dwErrorCode = GetLastError();
39         ERR("EnumPortsW failed with error %lu!\n", dwErrorCode);
40         goto Cleanup;
41     }
42 
43     // Allocate a buffer large enough.
44     pPortInfo1 = DllAllocSplMem(cbNeeded);
45     if (!pPortInfo1)
46     {
47         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
48         ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
49         goto Cleanup;
50     }
51 
52     // Now get the actual port information.
53     if (!EnumPortsW(NULL, 1, (PBYTE)pPortInfo1, cbNeeded, &cbNeeded, &dwReturned))
54     {
55         dwErrorCode = GetLastError();
56         ERR("EnumPortsW failed with error %lu!\n", dwErrorCode);
57         goto Cleanup;
58     }
59 
60     // We were successful! Loop through all returned ports.
61     dwErrorCode = ERROR_SUCCESS;
62     p = pPortInfo1;
63 
64     for (i = 0; i < dwReturned; i++)
65     {
66         // Check if this existing port matches our queried one.
67         if (wcsicmp(p->pName, pwszPortName) == 0)
68         {
69             bReturnValue = TRUE;
70             goto Cleanup;
71         }
72 
73         p++;
74     }
75 
76 Cleanup:
77     if (pPortInfo1)
78         DllFreeSplMem(pPortInfo1);
79 
80     SetLastError(dwErrorCode);
81     return bReturnValue;
82 }
83 
84 DWORD
85 GetLPTTransmissionRetryTimeout(VOID)
86 {
87     DWORD cbBuffer;
88     DWORD dwReturnValue = 90;       // Use 90 seconds as default if we fail to read from registry.
89     HKEY hKey;
90     LSTATUS lStatus;
91 
92     // Six digits is the most you can enter in Windows' LocalUI.dll.
93     // Larger values make it crash, so introduce a limit here.
94     WCHAR wszBuffer[6 + 1];
95 
96     // Open the key where our value is stored.
97     lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_READ, &hKey);
98     if (lStatus != ERROR_SUCCESS)
99     {
100         ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
101         goto Cleanup;
102     }
103 
104     // Query the value.
105     cbBuffer = sizeof(wszBuffer);
106     lStatus = RegQueryValueExW(hKey, L"TransmissionRetryTimeout", NULL, NULL, (PBYTE)wszBuffer, &cbBuffer);
107     if (lStatus != ERROR_SUCCESS)
108     {
109         ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
110         goto Cleanup;
111     }
112 
113     // Return it converted to a DWORD.
114     dwReturnValue = wcstoul(wszBuffer, NULL, 10);
115 
116 Cleanup:
117     if (hKey)
118         RegCloseKey(hKey);
119 
120     return dwReturnValue;
121 }
122 
123 /**
124  * @name GetPortNameWithoutColon
125  *
126  * Most of the time, we operate on port names with a trailing colon. But some functions require the name without the trailing colon.
127  * This function checks if the port has a trailing colon and if so, it returns the port name without the colon.
128  *
129  * @param pwszPortName
130  * The port name with colon
131  *
132  * @param ppwszPortNameWithoutColon
133  * Pointer to a PWSTR that will contain the port name without colon.
134  * You have to free this buffer using DllFreeSplMem.
135  *
136  * @return
137  * ERROR_SUCCESS if the port name without colon was successfully copied into the buffer.
138  * ERROR_INVALID_PARAMETER if this port name has no trailing colon.
139  * ERROR_NOT_ENOUGH_MEMORY if memory allocation failed.
140  */
141 DWORD
142 GetPortNameWithoutColon(PCWSTR pwszPortName, PWSTR* ppwszPortNameWithoutColon)
143 {
144     DWORD cchPortNameWithoutColon;
145 
146     // Compute the string length of pwszPortNameWithoutColon.
147     cchPortNameWithoutColon = wcslen(pwszPortName) - 1;
148 
149     // Check if pwszPortName really has a colon as the last character.
150     if (pwszPortName[cchPortNameWithoutColon] != L':')
151         return ERROR_INVALID_PARAMETER;
152 
153     // Allocate the output buffer.
154     *ppwszPortNameWithoutColon = DllAllocSplMem((cchPortNameWithoutColon + 1) * sizeof(WCHAR));
155     if (!*ppwszPortNameWithoutColon)
156     {
157         ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
158         return ERROR_NOT_ENOUGH_MEMORY;
159     }
160 
161     // Copy the port name without colon into the buffer.
162     // The buffer is already zero-initialized, so no additional null-termination is necessary.
163     CopyMemory(*ppwszPortNameWithoutColon, pwszPortName, cchPortNameWithoutColon * sizeof(WCHAR));
164 
165     return ERROR_SUCCESS;
166 }
167