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
DoesPortExist(PCWSTR pwszPortName)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
GetLPTTransmissionRetryTimeout(VOID)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
GetPortNameWithoutColon(PCWSTR pwszPortName,PWSTR * ppwszPortNameWithoutColon)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
168 /**
169 * @name _IsNEPort
170 *
171 * Checks if the given port name is a virtual Ne port.
172 * A virtual Ne port may appear in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports and can have the formats
173 * Ne00:, Ne01:, Ne-02:, Ne456:
174 * This check is extra picky to not cause false positives (like file name ports starting with "Ne").
175 *
176 * @param pwszPortName
177 * The port name to check.
178 *
179 * @return
180 * TRUE if this is definitely a virtual Ne port, FALSE if not.
181 */
182 static __inline BOOL
_IsNEPort(PCWSTR pwszPortName)183 _IsNEPort(PCWSTR pwszPortName)
184 {
185 PCWSTR p = pwszPortName;
186
187 // First character needs to be 'N' (uppercase or lowercase)
188 if (*p != L'N' && *p != L'n')
189 return FALSE;
190
191 // Next character needs to be 'E' (uppercase or lowercase)
192 p++;
193 if (*p != L'E' && *p != L'e')
194 return FALSE;
195
196 // An optional hyphen may follow now.
197 p++;
198 if (*p == L'-')
199 p++;
200
201 // Now an arbitrary number of digits may follow.
202 while (*p >= L'0' && *p <= L'9')
203 p++;
204
205 // Finally, the virtual Ne port must be terminated by a colon.
206 if (*p != ':')
207 return FALSE;
208
209 // If this is the end of the string, we have a virtual Ne port.
210 p++;
211 return (*p == L'\0');
212 }
213
214 DWORD
GetTypeFromName(LPCWSTR name)215 GetTypeFromName(LPCWSTR name)
216 {
217 HANDLE hfile;
218
219 if (!wcsncmp(name, L"LPT", ARRAYSIZE(L"LPT") - 1) )
220 return PORT_IS_LPT;
221
222 if (!wcsncmp(name, L"COM", ARRAYSIZE(L"COM") - 1) )
223 return PORT_IS_COM;
224
225 if (!lstrcmpW(name, L"FILE:") )
226 return PORT_IS_FILE;
227
228 // if (name[0] == '/')
229 // return PORT_IS_UNIXNAME;
230
231 // if (name[0] == '|')
232 // return PORT_IS_PIPE;
233
234 if ( _IsNEPort( name ) )
235 return PORT_IS_VNET;
236
237 if (!wcsncmp(name, L"XPS", ARRAYSIZE(L"XPS") - 1))
238 return PORT_IS_XPS;
239
240 /* Must be a file or a directory. Does the file exist ? */
241 hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
242
243 FIXME("%p for OPEN_EXISTING on %s\n", hfile, debugstr_w(name));
244
245 if (hfile == INVALID_HANDLE_VALUE)
246 {
247 /* Can we create the file? */
248 hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
249 FIXME("%p for OPEN_ALWAYS\n", hfile);
250 }
251
252 if (hfile != INVALID_HANDLE_VALUE)
253 {
254 CloseHandle(hfile); FIXME("PORT_IS_FILENAME %d\n",PORT_IS_FILENAME);
255 return PORT_IS_FILENAME;
256 }
257 FIXME("PORT_IS_UNKNOWN %d\n",PORT_IS_UNKNOWN);
258 /* We can't use the name. use GetLastError() for the reason */
259 return PORT_IS_UNKNOWN;
260 }
261