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 
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
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
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