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