1c2c66affSColin Finck /*
2c2c66affSColin Finck * PROJECT: ReactOS Local Port Monitor
3c2c66affSColin Finck * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4c2c66affSColin Finck * PURPOSE: Various support functions shared by multiple files
5c2c66affSColin Finck * COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org)
6c2c66affSColin Finck */
7c2c66affSColin Finck
8c2c66affSColin Finck #include "precomp.h"
9c2c66affSColin Finck
10c2c66affSColin Finck /**
11c2c66affSColin Finck * @name DoesPortExist
12c2c66affSColin Finck *
13c2c66affSColin Finck * Checks all Port Monitors installed on the local system to find out if a given port already exists.
14c2c66affSColin Finck *
15c2c66affSColin Finck * @param pwszPortName
16c2c66affSColin Finck * The port name to check.
17c2c66affSColin Finck *
18c2c66affSColin Finck * @return
19c2c66affSColin Finck * TRUE if a port with that name already exists on the local system.
20c2c66affSColin Finck * If the return value is FALSE, either the port doesn't exist or an error occurred.
21c2c66affSColin Finck * Use GetLastError in this case to check the error case.
22c2c66affSColin Finck */
23c2c66affSColin Finck BOOL
DoesPortExist(PCWSTR pwszPortName)24c2c66affSColin Finck DoesPortExist(PCWSTR pwszPortName)
25c2c66affSColin Finck {
26c2c66affSColin Finck BOOL bReturnValue = FALSE;
27c2c66affSColin Finck DWORD cbNeeded;
28c2c66affSColin Finck DWORD dwErrorCode;
29c2c66affSColin Finck DWORD dwReturned;
30c2c66affSColin Finck DWORD i;
31c2c66affSColin Finck PPORT_INFO_1W p;
32c2c66affSColin Finck PPORT_INFO_1W pPortInfo1 = NULL;
33c2c66affSColin Finck
34c2c66affSColin Finck // Determine the required buffer size.
35c2c66affSColin Finck EnumPortsW(NULL, 1, NULL, 0, &cbNeeded, &dwReturned);
36c2c66affSColin Finck if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
37c2c66affSColin Finck {
38c2c66affSColin Finck dwErrorCode = GetLastError();
39c2c66affSColin Finck ERR("EnumPortsW failed with error %lu!\n", dwErrorCode);
40c2c66affSColin Finck goto Cleanup;
41c2c66affSColin Finck }
42c2c66affSColin Finck
43c2c66affSColin Finck // Allocate a buffer large enough.
44c2c66affSColin Finck pPortInfo1 = DllAllocSplMem(cbNeeded);
45c2c66affSColin Finck if (!pPortInfo1)
46c2c66affSColin Finck {
47c2c66affSColin Finck dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
48c2c66affSColin Finck ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
49c2c66affSColin Finck goto Cleanup;
50c2c66affSColin Finck }
51c2c66affSColin Finck
52c2c66affSColin Finck // Now get the actual port information.
53c2c66affSColin Finck if (!EnumPortsW(NULL, 1, (PBYTE)pPortInfo1, cbNeeded, &cbNeeded, &dwReturned))
54c2c66affSColin Finck {
55c2c66affSColin Finck dwErrorCode = GetLastError();
56c2c66affSColin Finck ERR("EnumPortsW failed with error %lu!\n", dwErrorCode);
57c2c66affSColin Finck goto Cleanup;
58c2c66affSColin Finck }
59c2c66affSColin Finck
60c2c66affSColin Finck // We were successful! Loop through all returned ports.
61c2c66affSColin Finck dwErrorCode = ERROR_SUCCESS;
62c2c66affSColin Finck p = pPortInfo1;
63c2c66affSColin Finck
64c2c66affSColin Finck for (i = 0; i < dwReturned; i++)
65c2c66affSColin Finck {
66c2c66affSColin Finck // Check if this existing port matches our queried one.
67*e4930be4STimo Kreuzer if (_wcsicmp(p->pName, pwszPortName) == 0)
68c2c66affSColin Finck {
69c2c66affSColin Finck bReturnValue = TRUE;
70c2c66affSColin Finck goto Cleanup;
71c2c66affSColin Finck }
72c2c66affSColin Finck
73c2c66affSColin Finck p++;
74c2c66affSColin Finck }
75c2c66affSColin Finck
76c2c66affSColin Finck Cleanup:
77c2c66affSColin Finck if (pPortInfo1)
78c2c66affSColin Finck DllFreeSplMem(pPortInfo1);
79c2c66affSColin Finck
80c2c66affSColin Finck SetLastError(dwErrorCode);
81c2c66affSColin Finck return bReturnValue;
82c2c66affSColin Finck }
83c2c66affSColin Finck
84c2c66affSColin Finck DWORD
GetLPTTransmissionRetryTimeout(VOID)85d56c9a89SAmine Khaldi GetLPTTransmissionRetryTimeout(VOID)
86c2c66affSColin Finck {
87c2c66affSColin Finck DWORD cbBuffer;
88c2c66affSColin Finck DWORD dwReturnValue = 90; // Use 90 seconds as default if we fail to read from registry.
89c2c66affSColin Finck HKEY hKey;
90c2c66affSColin Finck LSTATUS lStatus;
91c2c66affSColin Finck
92c2c66affSColin Finck // Six digits is the most you can enter in Windows' LocalUI.dll.
93c2c66affSColin Finck // Larger values make it crash, so introduce a limit here.
94c2c66affSColin Finck WCHAR wszBuffer[6 + 1];
95c2c66affSColin Finck
96c2c66affSColin Finck // Open the key where our value is stored.
97c2c66affSColin Finck lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_READ, &hKey);
98c2c66affSColin Finck if (lStatus != ERROR_SUCCESS)
99c2c66affSColin Finck {
100c2c66affSColin Finck ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
101c2c66affSColin Finck goto Cleanup;
102c2c66affSColin Finck }
103c2c66affSColin Finck
104c2c66affSColin Finck // Query the value.
105c2c66affSColin Finck cbBuffer = sizeof(wszBuffer);
106c2c66affSColin Finck lStatus = RegQueryValueExW(hKey, L"TransmissionRetryTimeout", NULL, NULL, (PBYTE)wszBuffer, &cbBuffer);
107c2c66affSColin Finck if (lStatus != ERROR_SUCCESS)
108c2c66affSColin Finck {
109c2c66affSColin Finck ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
110c2c66affSColin Finck goto Cleanup;
111c2c66affSColin Finck }
112c2c66affSColin Finck
113c2c66affSColin Finck // Return it converted to a DWORD.
114c2c66affSColin Finck dwReturnValue = wcstoul(wszBuffer, NULL, 10);
115c2c66affSColin Finck
116c2c66affSColin Finck Cleanup:
117c2c66affSColin Finck if (hKey)
118c2c66affSColin Finck RegCloseKey(hKey);
119c2c66affSColin Finck
120c2c66affSColin Finck return dwReturnValue;
121c2c66affSColin Finck }
122c2c66affSColin Finck
123c2c66affSColin Finck /**
124c2c66affSColin Finck * @name GetPortNameWithoutColon
125c2c66affSColin Finck *
126c2c66affSColin Finck * Most of the time, we operate on port names with a trailing colon. But some functions require the name without the trailing colon.
127c2c66affSColin Finck * This function checks if the port has a trailing colon and if so, it returns the port name without the colon.
128c2c66affSColin Finck *
129c2c66affSColin Finck * @param pwszPortName
130c2c66affSColin Finck * The port name with colon
131c2c66affSColin Finck *
132c2c66affSColin Finck * @param ppwszPortNameWithoutColon
133c2c66affSColin Finck * Pointer to a PWSTR that will contain the port name without colon.
134c2c66affSColin Finck * You have to free this buffer using DllFreeSplMem.
135c2c66affSColin Finck *
136c2c66affSColin Finck * @return
137c2c66affSColin Finck * ERROR_SUCCESS if the port name without colon was successfully copied into the buffer.
138c2c66affSColin Finck * ERROR_INVALID_PARAMETER if this port name has no trailing colon.
139c2c66affSColin Finck * ERROR_NOT_ENOUGH_MEMORY if memory allocation failed.
140c2c66affSColin Finck */
141c2c66affSColin Finck DWORD
GetPortNameWithoutColon(PCWSTR pwszPortName,PWSTR * ppwszPortNameWithoutColon)142c2c66affSColin Finck GetPortNameWithoutColon(PCWSTR pwszPortName, PWSTR* ppwszPortNameWithoutColon)
143c2c66affSColin Finck {
144c2c66affSColin Finck DWORD cchPortNameWithoutColon;
145c2c66affSColin Finck
146c2c66affSColin Finck // Compute the string length of pwszPortNameWithoutColon.
147c2c66affSColin Finck cchPortNameWithoutColon = wcslen(pwszPortName) - 1;
148c2c66affSColin Finck
149c2c66affSColin Finck // Check if pwszPortName really has a colon as the last character.
150c2c66affSColin Finck if (pwszPortName[cchPortNameWithoutColon] != L':')
151c2c66affSColin Finck return ERROR_INVALID_PARAMETER;
152c2c66affSColin Finck
153c2c66affSColin Finck // Allocate the output buffer.
154c2c66affSColin Finck *ppwszPortNameWithoutColon = DllAllocSplMem((cchPortNameWithoutColon + 1) * sizeof(WCHAR));
155c2c66affSColin Finck if (!*ppwszPortNameWithoutColon)
156c2c66affSColin Finck {
157c2c66affSColin Finck ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
158c2c66affSColin Finck return ERROR_NOT_ENOUGH_MEMORY;
159c2c66affSColin Finck }
160c2c66affSColin Finck
161c2c66affSColin Finck // Copy the port name without colon into the buffer.
162c2c66affSColin Finck // The buffer is already zero-initialized, so no additional null-termination is necessary.
163c2c66affSColin Finck CopyMemory(*ppwszPortNameWithoutColon, pwszPortName, cchPortNameWithoutColon * sizeof(WCHAR));
164c2c66affSColin Finck
165c2c66affSColin Finck return ERROR_SUCCESS;
166c2c66affSColin Finck }
16762c4b828SJames Tabor
16862c4b828SJames Tabor /**
16962c4b828SJames Tabor * @name _IsNEPort
17062c4b828SJames Tabor *
17162c4b828SJames Tabor * Checks if the given port name is a virtual Ne port.
17262c4b828SJames Tabor * A virtual Ne port may appear in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports and can have the formats
17362c4b828SJames Tabor * Ne00:, Ne01:, Ne-02:, Ne456:
17462c4b828SJames Tabor * This check is extra picky to not cause false positives (like file name ports starting with "Ne").
17562c4b828SJames Tabor *
17662c4b828SJames Tabor * @param pwszPortName
17762c4b828SJames Tabor * The port name to check.
17862c4b828SJames Tabor *
17962c4b828SJames Tabor * @return
18062c4b828SJames Tabor * TRUE if this is definitely a virtual Ne port, FALSE if not.
18162c4b828SJames Tabor */
18262c4b828SJames Tabor static __inline BOOL
_IsNEPort(PCWSTR pwszPortName)18362c4b828SJames Tabor _IsNEPort(PCWSTR pwszPortName)
18462c4b828SJames Tabor {
18562c4b828SJames Tabor PCWSTR p = pwszPortName;
18662c4b828SJames Tabor
18762c4b828SJames Tabor // First character needs to be 'N' (uppercase or lowercase)
18862c4b828SJames Tabor if (*p != L'N' && *p != L'n')
18962c4b828SJames Tabor return FALSE;
19062c4b828SJames Tabor
19162c4b828SJames Tabor // Next character needs to be 'E' (uppercase or lowercase)
19262c4b828SJames Tabor p++;
19362c4b828SJames Tabor if (*p != L'E' && *p != L'e')
19462c4b828SJames Tabor return FALSE;
19562c4b828SJames Tabor
19662c4b828SJames Tabor // An optional hyphen may follow now.
19762c4b828SJames Tabor p++;
19862c4b828SJames Tabor if (*p == L'-')
19962c4b828SJames Tabor p++;
20062c4b828SJames Tabor
20162c4b828SJames Tabor // Now an arbitrary number of digits may follow.
20262c4b828SJames Tabor while (*p >= L'0' && *p <= L'9')
20362c4b828SJames Tabor p++;
20462c4b828SJames Tabor
20562c4b828SJames Tabor // Finally, the virtual Ne port must be terminated by a colon.
20662c4b828SJames Tabor if (*p != ':')
20762c4b828SJames Tabor return FALSE;
20862c4b828SJames Tabor
20962c4b828SJames Tabor // If this is the end of the string, we have a virtual Ne port.
21062c4b828SJames Tabor p++;
21162c4b828SJames Tabor return (*p == L'\0');
21262c4b828SJames Tabor }
21362c4b828SJames Tabor
21462c4b828SJames Tabor DWORD
GetTypeFromName(LPCWSTR name)21562c4b828SJames Tabor GetTypeFromName(LPCWSTR name)
21662c4b828SJames Tabor {
21762c4b828SJames Tabor HANDLE hfile;
21862c4b828SJames Tabor
21962c4b828SJames Tabor if (!wcsncmp(name, L"LPT", ARRAYSIZE(L"LPT") - 1) )
22062c4b828SJames Tabor return PORT_IS_LPT;
22162c4b828SJames Tabor
22262c4b828SJames Tabor if (!wcsncmp(name, L"COM", ARRAYSIZE(L"COM") - 1) )
22362c4b828SJames Tabor return PORT_IS_COM;
22462c4b828SJames Tabor
22562c4b828SJames Tabor if (!lstrcmpW(name, L"FILE:") )
22662c4b828SJames Tabor return PORT_IS_FILE;
22762c4b828SJames Tabor
22862c4b828SJames Tabor // if (name[0] == '/')
22962c4b828SJames Tabor // return PORT_IS_UNIXNAME;
23062c4b828SJames Tabor
23162c4b828SJames Tabor // if (name[0] == '|')
23262c4b828SJames Tabor // return PORT_IS_PIPE;
23362c4b828SJames Tabor
23462c4b828SJames Tabor if ( _IsNEPort( name ) )
23562c4b828SJames Tabor return PORT_IS_VNET;
23662c4b828SJames Tabor
23762c4b828SJames Tabor if (!wcsncmp(name, L"XPS", ARRAYSIZE(L"XPS") - 1))
23862c4b828SJames Tabor return PORT_IS_XPS;
23962c4b828SJames Tabor
24062c4b828SJames Tabor /* Must be a file or a directory. Does the file exist ? */
24162c4b828SJames Tabor hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
24262c4b828SJames Tabor
24362c4b828SJames Tabor FIXME("%p for OPEN_EXISTING on %s\n", hfile, debugstr_w(name));
24462c4b828SJames Tabor
24562c4b828SJames Tabor if (hfile == INVALID_HANDLE_VALUE)
24662c4b828SJames Tabor {
24762c4b828SJames Tabor /* Can we create the file? */
24862c4b828SJames Tabor hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
24962c4b828SJames Tabor FIXME("%p for OPEN_ALWAYS\n", hfile);
25062c4b828SJames Tabor }
25162c4b828SJames Tabor
25262c4b828SJames Tabor if (hfile != INVALID_HANDLE_VALUE)
25362c4b828SJames Tabor {
25462c4b828SJames Tabor CloseHandle(hfile); FIXME("PORT_IS_FILENAME %d\n",PORT_IS_FILENAME);
25562c4b828SJames Tabor return PORT_IS_FILENAME;
25662c4b828SJames Tabor }
25762c4b828SJames Tabor FIXME("PORT_IS_UNKNOWN %d\n",PORT_IS_UNKNOWN);
25862c4b828SJames Tabor /* We can't use the name. use GetLastError() for the reason */
25962c4b828SJames Tabor return PORT_IS_UNKNOWN;
26062c4b828SJames Tabor }
261