20#include <winpr/config.h>
25#include <winpr/library.h>
27#include <winpr/wtsapi.h>
29#include "wtsapi_win32.h"
35#pragma comment(lib, "ntdll.lib")
37#define WTSAPI_CHANNEL_MAGIC 0x44484356
38#define TAG WINPR_TAG("wtsapi")
64static BOOL g_Initialized = FALSE;
65static HMODULE g_WinStaModule = NULL;
67typedef HANDLE(WINAPI* fnWinStationVirtualOpen)(HANDLE hServer, DWORD SessionId,
69typedef HANDLE(WINAPI* fnWinStationVirtualOpenEx)(HANDLE hServer, DWORD SessionId,
70 LPSTR pVirtualName, DWORD flags);
72static fnWinStationVirtualOpen pfnWinStationVirtualOpen = NULL;
73static fnWinStationVirtualOpenEx pfnWinStationVirtualOpenEx = NULL;
75static BOOL WINAPI Win32_WTSVirtualChannelClose(HANDLE hChannel);
92static void* _wts_malloc(
size_t size)
97 return (PVOID)LocalAlloc(LMEM_FIXED, size);
101static void* _wts_calloc(
size_t nmemb,
size_t size)
104 return calloc(nmemb, size);
106 return (PVOID)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, nmemb * size);
110static void _wts_free(
void* ptr)
115 LocalFree((HLOCAL)ptr);
119static BOOL Win32_WTSVirtualChannelReadAsync(WTSAPI_CHANNEL* pChannel)
124 if (pChannel->readAsync)
127 ZeroMemory(&(pChannel->overlapped),
sizeof(
OVERLAPPED));
128 pChannel->overlapped.hEvent = pChannel->hEvent;
129 (void)ResetEvent(pChannel->hEvent);
131 if (pChannel->showProtocol)
135 status = ReadFile(pChannel->hFile, pChannel->header,
sizeof(
CHANNEL_PDU_HEADER), &numBytes,
136 &(pChannel->overlapped));
140 status = ReadFile(pChannel->hFile, pChannel->chunk, CHANNEL_CHUNK_LENGTH, &numBytes,
141 &(pChannel->overlapped));
145 pChannel->readOffset = 0;
146 pChannel->header->length = numBytes;
148 pChannel->readDone = TRUE;
149 (void)SetEvent(pChannel->hEvent);
157 WLog_ERR(TAG,
"Unexpected ReadFile status: %" PRId32
" numBytes: %" PRIu32
"", status,
162 if (GetLastError() != ERROR_IO_PENDING)
164 WLog_ERR(TAG,
"ReadFile: GetLastError() = %" PRIu32
"", GetLastError());
168 pChannel->readAsync = TRUE;
173static HANDLE WINAPI Win32_WTSVirtualChannelOpen_Internal(HANDLE hServer, DWORD SessionId,
174 LPSTR pVirtualName, DWORD flags)
178 WTSAPI_CHANNEL* pChannel;
179 size_t virtualNameLen;
181 virtualNameLen = pVirtualName ? strlen(pVirtualName) : 0;
185 SetLastError(ERROR_INVALID_PARAMETER);
189 if (!pfnWinStationVirtualOpenEx)
191 SetLastError(ERROR_INVALID_FUNCTION);
195 hFile = pfnWinStationVirtualOpenEx(hServer, SessionId, pVirtualName, flags);
200 pChannel = (WTSAPI_CHANNEL*)_wts_calloc(1,
sizeof(WTSAPI_CHANNEL));
204 (void)CloseHandle(hFile);
205 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
209 hChannel = (HANDLE)pChannel;
210 pChannel->magic = WTSAPI_CHANNEL_MAGIC;
211 pChannel->hServer = hServer;
212 pChannel->SessionId = SessionId;
213 pChannel->hFile = hFile;
214 pChannel->VirtualName = _wts_calloc(1, virtualNameLen + 1);
215 if (!pChannel->VirtualName)
217 (void)CloseHandle(hFile);
218 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
222 memcpy(pChannel->VirtualName, pVirtualName, virtualNameLen);
224 pChannel->flags = flags;
225 pChannel->dynamic = (flags & WTS_CHANNEL_OPTION_DYNAMIC) ? TRUE : FALSE;
227 pChannel->showProtocol = pChannel->dynamic;
229 pChannel->readSize = CHANNEL_PDU_LENGTH;
230 pChannel->readBuffer = (BYTE*)_wts_malloc(pChannel->readSize);
235 pChannel->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
236 pChannel->overlapped.hEvent = pChannel->hEvent;
238 if (!pChannel->hEvent || !pChannel->VirtualName || !pChannel->readBuffer)
240 Win32_WTSVirtualChannelClose(hChannel);
241 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
248static HANDLE WINAPI Win32_WTSVirtualChannelOpen(HANDLE hServer, DWORD SessionId,
251 return Win32_WTSVirtualChannelOpen_Internal(hServer, SessionId, pVirtualName, 0);
254static HANDLE WINAPI Win32_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualName, DWORD flags)
256 return Win32_WTSVirtualChannelOpen_Internal(0, SessionId, pVirtualName, flags);
259static BOOL WINAPI Win32_WTSVirtualChannelClose(HANDLE hChannel)
262 WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel;
264 if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
266 SetLastError(ERROR_INVALID_PARAMETER);
272 if (pChannel->readAsync)
274 CancelIo(pChannel->hFile);
275 pChannel->readAsync = FALSE;
278 status = CloseHandle(pChannel->hFile);
279 pChannel->hFile = NULL;
282 if (pChannel->hEvent)
284 (void)CloseHandle(pChannel->hEvent);
285 pChannel->hEvent = NULL;
288 if (pChannel->VirtualName)
290 _wts_free(pChannel->VirtualName);
291 pChannel->VirtualName = NULL;
294 if (pChannel->readBuffer)
296 _wts_free(pChannel->readBuffer);
297 pChannel->readBuffer = NULL;
306static BOOL WINAPI Win32_WTSVirtualChannelRead_Static(WTSAPI_CHANNEL* pChannel,
307 DWORD dwMilliseconds, LPVOID lpBuffer,
308 DWORD nNumberOfBytesToRead,
309 LPDWORD lpNumberOfBytesTransferred)
311 if (pChannel->readDone)
313 DWORD numBytesRead = 0;
314 DWORD numBytesToRead = 0;
316 *lpNumberOfBytesTransferred = 0;
318 numBytesToRead = nNumberOfBytesToRead;
320 if (numBytesToRead > (pChannel->header->length - pChannel->readOffset))
321 numBytesToRead = (pChannel->header->length - pChannel->readOffset);
323 CopyMemory(lpBuffer, &(pChannel->chunk[pChannel->readOffset]), numBytesToRead);
324 *lpNumberOfBytesTransferred += numBytesToRead;
325 pChannel->readOffset += numBytesToRead;
327 if (pChannel->readOffset != pChannel->header->length)
329 SetLastError(ERROR_MORE_DATA);
334 pChannel->readDone = FALSE;
335 Win32_WTSVirtualChannelReadAsync(pChannel);
340 else if (pChannel->readSync)
344 DWORD numBytesRead = 0;
345 DWORD numBytesToRead = 0;
347 *lpNumberOfBytesTransferred = 0;
349 numBytesToRead = nNumberOfBytesToRead;
351 if (numBytesToRead > (pChannel->header->length - pChannel->readOffset))
352 numBytesToRead = (pChannel->header->length - pChannel->readOffset);
354 if (ReadFile(pChannel->hFile, lpBuffer, numBytesToRead, &numBytesRead, &overlapped))
356 *lpNumberOfBytesTransferred += numBytesRead;
357 pChannel->readOffset += numBytesRead;
359 if (pChannel->readOffset != pChannel->header->length)
361 SetLastError(ERROR_MORE_DATA);
365 pChannel->readSync = FALSE;
366 Win32_WTSVirtualChannelReadAsync(pChannel);
371 if (GetLastError() != ERROR_IO_PENDING)
374 bSuccess = GetOverlappedResult(pChannel->hFile, &overlapped, &numBytesRead, TRUE);
379 *lpNumberOfBytesTransferred += numBytesRead;
380 pChannel->readOffset += numBytesRead;
382 if (pChannel->readOffset != pChannel->header->length)
384 SetLastError(ERROR_MORE_DATA);
388 pChannel->readSync = FALSE;
389 Win32_WTSVirtualChannelReadAsync(pChannel);
393 else if (pChannel->readAsync)
396 DWORD numBytesRead = 0;
397 DWORD numBytesToRead = 0;
399 *lpNumberOfBytesTransferred = 0;
401 if (WaitForSingleObject(pChannel->hEvent, dwMilliseconds) != WAIT_TIMEOUT)
404 GetOverlappedResult(pChannel->hFile, &(pChannel->overlapped), &numBytesRead, TRUE);
406 pChannel->readOffset = 0;
407 pChannel->header->length = numBytesRead;
409 if (!bSuccess && (GetLastError() != ERROR_MORE_DATA))
412 numBytesToRead = nNumberOfBytesToRead;
414 if (numBytesRead < numBytesToRead)
416 numBytesToRead = numBytesRead;
417 nNumberOfBytesToRead = numBytesRead;
420 CopyMemory(lpBuffer, pChannel->chunk, numBytesToRead);
421 *lpNumberOfBytesTransferred += numBytesToRead;
422 lpBuffer = (BYTE*)lpBuffer + numBytesToRead;
423 nNumberOfBytesToRead -= numBytesToRead;
424 pChannel->readOffset += numBytesToRead;
426 pChannel->readAsync = FALSE;
428 if (!nNumberOfBytesToRead)
430 Win32_WTSVirtualChannelReadAsync(pChannel);
434 pChannel->readSync = TRUE;
438 bSuccess = Win32_WTSVirtualChannelRead_Static(pChannel, dwMilliseconds, lpBuffer,
439 nNumberOfBytesToRead, &numBytesRead);
441 *lpNumberOfBytesTransferred += numBytesRead;
446 SetLastError(ERROR_IO_INCOMPLETE);
454static BOOL WINAPI Win32_WTSVirtualChannelRead_Dynamic(WTSAPI_CHANNEL* pChannel,
455 DWORD dwMilliseconds, LPVOID lpBuffer,
456 DWORD nNumberOfBytesToRead,
457 LPDWORD lpNumberOfBytesTransferred)
459 if (pChannel->readSync)
463 DWORD numBytesRead = 0;
464 DWORD numBytesToRead = 0;
466 *lpNumberOfBytesTransferred = 0;
468 numBytesToRead = nNumberOfBytesToRead;
470 if (numBytesToRead > (pChannel->header->length - pChannel->readOffset))
471 numBytesToRead = (pChannel->header->length - pChannel->readOffset);
473 if (ReadFile(pChannel->hFile, lpBuffer, numBytesToRead, &numBytesRead, &overlapped))
475 *lpNumberOfBytesTransferred += numBytesRead;
476 pChannel->readOffset += numBytesRead;
478 if (pChannel->readOffset != pChannel->header->length)
480 SetLastError(ERROR_MORE_DATA);
484 pChannel->readSync = FALSE;
485 Win32_WTSVirtualChannelReadAsync(pChannel);
490 if (GetLastError() != ERROR_IO_PENDING)
493 bSuccess = GetOverlappedResult(pChannel->hFile, &overlapped, &numBytesRead, TRUE);
498 *lpNumberOfBytesTransferred += numBytesRead;
499 pChannel->readOffset += numBytesRead;
501 if (pChannel->readOffset != pChannel->header->length)
503 SetLastError(ERROR_MORE_DATA);
507 pChannel->readSync = FALSE;
508 Win32_WTSVirtualChannelReadAsync(pChannel);
512 else if (pChannel->readAsync)
515 DWORD numBytesRead = 0;
517 *lpNumberOfBytesTransferred = 0;
519 if (WaitForSingleObject(pChannel->hEvent, dwMilliseconds) != WAIT_TIMEOUT)
522 GetOverlappedResult(pChannel->hFile, &(pChannel->overlapped), &numBytesRead, TRUE);
524 if (pChannel->showProtocol)
529 if (!bSuccess && (GetLastError() != ERROR_MORE_DATA))
532 CopyMemory(lpBuffer, pChannel->header, numBytesRead);
533 *lpNumberOfBytesTransferred += numBytesRead;
534 lpBuffer = (BYTE*)lpBuffer + numBytesRead;
535 nNumberOfBytesToRead -= numBytesRead;
538 pChannel->readAsync = FALSE;
540 if (!pChannel->header->length)
542 Win32_WTSVirtualChannelReadAsync(pChannel);
546 pChannel->readSync = TRUE;
547 pChannel->readOffset = 0;
549 if (!nNumberOfBytesToRead)
551 SetLastError(ERROR_MORE_DATA);
557 bSuccess = Win32_WTSVirtualChannelRead_Dynamic(pChannel, dwMilliseconds, lpBuffer,
558 nNumberOfBytesToRead, &numBytesRead);
560 *lpNumberOfBytesTransferred += numBytesRead;
565 SetLastError(ERROR_IO_INCOMPLETE);
573static BOOL WINAPI Win32_WTSVirtualChannelRead(HANDLE hChannel, DWORD dwMilliseconds,
574 PCHAR lpBuffer, DWORD nNumberOfBytesToRead,
575 LPDWORD lpNumberOfBytesTransferred)
577 WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel;
579 if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
581 SetLastError(ERROR_INVALID_PARAMETER);
585 if (!pChannel->waitObjectMode)
589 if (ReadFile(pChannel->hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesTransferred,
593 if (GetLastError() != ERROR_IO_PENDING)
598 CancelIo(pChannel->hFile);
599 *lpNumberOfBytesTransferred = 0;
603 if (WaitForSingleObject(pChannel->hFile, dwMilliseconds) != WAIT_TIMEOUT)
604 return GetOverlappedResult(pChannel->hFile, &overlapped, lpNumberOfBytesTransferred,
607 CancelIo(pChannel->hFile);
608 SetLastError(ERROR_IO_INCOMPLETE);
614 if (pChannel->dynamic)
616 return Win32_WTSVirtualChannelRead_Dynamic(pChannel, dwMilliseconds, lpBuffer,
617 nNumberOfBytesToRead,
618 lpNumberOfBytesTransferred);
622 return Win32_WTSVirtualChannelRead_Static(pChannel, dwMilliseconds, lpBuffer,
623 nNumberOfBytesToRead,
624 lpNumberOfBytesTransferred);
631static BOOL WINAPI Win32_WTSVirtualChannelWrite(HANDLE hChannel, PCHAR lpBuffer,
632 DWORD nNumberOfBytesToWrite,
633 LPDWORD lpNumberOfBytesTransferred)
636 WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel;
638 if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
640 SetLastError(ERROR_INVALID_PARAMETER);
644 if (WriteFile(pChannel->hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesTransferred,
648 if (GetLastError() == ERROR_IO_PENDING)
649 return GetOverlappedResult(pChannel->hFile, &overlapped, lpNumberOfBytesTransferred, TRUE);
654#ifndef FILE_DEVICE_TERMSRV
655#define FILE_DEVICE_TERMSRV 0x00000038
658static BOOL Win32_WTSVirtualChannelPurge_Internal(HANDLE hChannelHandle, ULONG IoControlCode)
661 WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannelHandle;
663 if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
665 SetLastError(ERROR_INVALID_PARAMETER);
670 NtDeviceIoControlFile(pChannel->hFile, 0, 0, 0, &ioStatusBlock, IoControlCode, 0, 0, 0, 0);
672 if (ntstatus == STATUS_PENDING)
674 ntstatus = NtWaitForSingleObject(pChannel->hFile, 0, 0);
678#if defined(NONAMELESSUNION) && !defined(__MINGW32__)
679 ntstatus = ioStatusBlock.DUMMYUNIONNAME.Status;
681 ntstatus = ioStatusBlock.Status;
686 if (ntstatus == STATUS_BUFFER_OVERFLOW)
688 ntstatus = STATUS_BUFFER_TOO_SMALL;
689 const DWORD error = RtlNtStatusToDosError(ntstatus);
696 const DWORD error = RtlNtStatusToDosError(ntstatus);
704static BOOL WINAPI Win32_WTSVirtualChannelPurgeInput(HANDLE hChannelHandle)
706 return Win32_WTSVirtualChannelPurge_Internal(hChannelHandle,
707 (FILE_DEVICE_TERMSRV << 16) | 0x0107);
710static BOOL WINAPI Win32_WTSVirtualChannelPurgeOutput(HANDLE hChannelHandle)
712 return Win32_WTSVirtualChannelPurge_Internal(hChannelHandle,
713 (FILE_DEVICE_TERMSRV << 16) | 0x010B);
716static BOOL WINAPI Win32_WTSVirtualChannelQuery(HANDLE hChannelHandle,
717 WTS_VIRTUAL_CLASS WtsVirtualClass, PVOID* ppBuffer,
718 DWORD* pBytesReturned)
720 WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannelHandle;
722 if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
724 SetLastError(ERROR_INVALID_PARAMETER);
728 if (WtsVirtualClass == WTSVirtualClientData)
730 SetLastError(ERROR_INVALID_PARAMETER);
733 else if (WtsVirtualClass == WTSVirtualFileHandle)
735 *pBytesReturned =
sizeof(HANDLE);
736 *ppBuffer = _wts_calloc(1, *pBytesReturned);
738 if (*ppBuffer == NULL)
740 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
744 CopyMemory(*ppBuffer, &(pChannel->hFile), *pBytesReturned);
746 else if (WtsVirtualClass == WTSVirtualEventHandle)
748 *pBytesReturned =
sizeof(HANDLE);
749 *ppBuffer = _wts_calloc(1, *pBytesReturned);
751 if (*ppBuffer == NULL)
753 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
757 CopyMemory(*ppBuffer, &(pChannel->hEvent), *pBytesReturned);
759 Win32_WTSVirtualChannelReadAsync(pChannel);
760 pChannel->waitObjectMode = TRUE;
764 SetLastError(ERROR_INVALID_PARAMETER);
771static VOID WINAPI Win32_WTSFreeMemory(PVOID pMemory)
776static BOOL WINAPI Win32_WTSFreeMemoryExW(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory,
777 ULONG NumberOfEntries)
782static BOOL WINAPI Win32_WTSFreeMemoryExA(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory,
783 ULONG NumberOfEntries)
785 return WTSFreeMemoryExW(WTSTypeClass, pMemory, NumberOfEntries);
790 g_WinStaModule = LoadLibraryA(
"winsta.dll");
795 pfnWinStationVirtualOpen =
796 GetProcAddressAs(g_WinStaModule,
"WinStationVirtualOpen", fnWinStationVirtualOpen);
797 pfnWinStationVirtualOpenEx =
798 GetProcAddressAs(g_WinStaModule,
"WinStationVirtualOpenEx", fnWinStationVirtualOpenEx);
800 if (!pfnWinStationVirtualOpen | !pfnWinStationVirtualOpenEx)
803 pWtsApi->pVirtualChannelOpen = Win32_WTSVirtualChannelOpen;
804 pWtsApi->pVirtualChannelOpenEx = Win32_WTSVirtualChannelOpenEx;
805 pWtsApi->pVirtualChannelClose = Win32_WTSVirtualChannelClose;
806 pWtsApi->pVirtualChannelRead = Win32_WTSVirtualChannelRead;
807 pWtsApi->pVirtualChannelWrite = Win32_WTSVirtualChannelWrite;
808 pWtsApi->pVirtualChannelPurgeInput = Win32_WTSVirtualChannelPurgeInput;
809 pWtsApi->pVirtualChannelPurgeOutput = Win32_WTSVirtualChannelPurgeOutput;
810 pWtsApi->pVirtualChannelQuery = Win32_WTSVirtualChannelQuery;
811 pWtsApi->pFreeMemory = Win32_WTSFreeMemory;