Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
392
third_party/FreeRDP/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c
vendored
Normal file
392
third_party/FreeRDP/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c
vendored
Normal file
@@ -0,0 +1,392 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/pipe.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/winpr.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#define PIPE_BUFFER_SIZE 32
|
||||
#define PIPE_TIMEOUT_MS 20000 // 20 seconds
|
||||
|
||||
static BYTE SERVER_MESSAGE[PIPE_BUFFER_SIZE];
|
||||
static BYTE CLIENT_MESSAGE[PIPE_BUFFER_SIZE];
|
||||
|
||||
static BOOL bClientSuccess = FALSE;
|
||||
static BOOL bServerSuccess = FALSE;
|
||||
|
||||
static HANDLE serverReadyEvent = nullptr;
|
||||
|
||||
static LPTSTR lpszPipeName = _T("\\\\.\\pipe\\winpr_test_pipe_overlapped");
|
||||
|
||||
static DWORD WINAPI named_pipe_client_thread(LPVOID arg)
|
||||
{
|
||||
DWORD status = 0;
|
||||
HANDLE hEvent = nullptr;
|
||||
HANDLE hNamedPipe = nullptr;
|
||||
BYTE* lpReadBuffer = nullptr;
|
||||
BOOL fSuccess = FALSE;
|
||||
OVERLAPPED overlapped = WINPR_C_ARRAY_INIT;
|
||||
DWORD nNumberOfBytesToRead = 0;
|
||||
DWORD nNumberOfBytesToWrite = 0;
|
||||
DWORD NumberOfBytesTransferred = 0;
|
||||
|
||||
WINPR_UNUSED(arg);
|
||||
|
||||
status = WaitForSingleObject(serverReadyEvent, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("client: failed to wait for server ready event: %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* 1: initialize overlapped structure */
|
||||
if (!(hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("client: CreateEvent failure: %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
overlapped.hEvent = hEvent;
|
||||
|
||||
/* 2: connect to server named pipe */
|
||||
|
||||
hNamedPipe = CreateFile(lpszPipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED, nullptr);
|
||||
|
||||
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("client: Named Pipe CreateFile failure: %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* 3: write to named pipe */
|
||||
|
||||
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
|
||||
NumberOfBytesTransferred = 0;
|
||||
|
||||
fSuccess = WriteFile(hNamedPipe, CLIENT_MESSAGE, nNumberOfBytesToWrite, nullptr, &overlapped);
|
||||
|
||||
if (!fSuccess)
|
||||
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("client: NamedPipe WriteFile failure (initial): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("client: failed to wait for overlapped event (write): %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("client: NamedPipe WriteFile failure (final): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
printf("client: WriteFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred);
|
||||
|
||||
/* 4: read from named pipe */
|
||||
|
||||
if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE)))
|
||||
{
|
||||
printf("client: Error allocating read buffer\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
|
||||
NumberOfBytesTransferred = 0;
|
||||
|
||||
fSuccess = ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, nullptr, &overlapped);
|
||||
|
||||
if (!fSuccess)
|
||||
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("client: NamedPipe ReadFile failure (initial): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = WaitForMultipleObjects(1, &hEvent, FALSE, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("client: failed to wait for overlapped event (read): %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, TRUE);
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("client: NamedPipe ReadFile failure (final): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("client: ReadFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred);
|
||||
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, NumberOfBytesTransferred);
|
||||
|
||||
if (NumberOfBytesTransferred != PIPE_BUFFER_SIZE ||
|
||||
memcmp(lpReadBuffer, SERVER_MESSAGE, PIPE_BUFFER_SIZE) != 0)
|
||||
{
|
||||
printf("client: received unexpected data from server\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("client: finished successfully\n");
|
||||
bClientSuccess = TRUE;
|
||||
|
||||
finish:
|
||||
free(lpReadBuffer);
|
||||
if (hNamedPipe)
|
||||
(void)CloseHandle(hNamedPipe);
|
||||
if (hEvent)
|
||||
(void)CloseHandle(hEvent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI named_pipe_server_thread(LPVOID arg)
|
||||
{
|
||||
DWORD status = 0;
|
||||
HANDLE hEvent = nullptr;
|
||||
HANDLE hNamedPipe = nullptr;
|
||||
BYTE* lpReadBuffer = nullptr;
|
||||
OVERLAPPED overlapped = WINPR_C_ARRAY_INIT;
|
||||
BOOL fSuccess = FALSE;
|
||||
BOOL fConnected = FALSE;
|
||||
DWORD nNumberOfBytesToRead = 0;
|
||||
DWORD nNumberOfBytesToWrite = 0;
|
||||
DWORD NumberOfBytesTransferred = 0;
|
||||
|
||||
WINPR_UNUSED(arg);
|
||||
|
||||
/* 1: initialize overlapped structure */
|
||||
if (!(hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("server: CreateEvent failure: %" PRIu32 "\n", GetLastError());
|
||||
(void)SetEvent(serverReadyEvent); /* unblock client thread */
|
||||
goto finish;
|
||||
}
|
||||
overlapped.hEvent = hEvent;
|
||||
|
||||
/* 2: create named pipe and set ready event */
|
||||
|
||||
hNamedPipe =
|
||||
CreateNamedPipe(lpszPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
|
||||
PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, nullptr);
|
||||
|
||||
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("server: CreateNamedPipe failure: %" PRIu32 "\n", GetLastError());
|
||||
(void)SetEvent(serverReadyEvent); /* unblock client thread */
|
||||
goto finish;
|
||||
}
|
||||
|
||||
(void)SetEvent(serverReadyEvent);
|
||||
|
||||
/* 3: connect named pipe */
|
||||
|
||||
fConnected = ConnectNamedPipe(hNamedPipe, &overlapped);
|
||||
status = GetLastError();
|
||||
|
||||
/**
|
||||
* At this point if fConnected is FALSE, we have to check GetLastError() for:
|
||||
* ERROR_PIPE_CONNECTED:
|
||||
* client has already connected before we have called ConnectNamedPipe.
|
||||
* this is quite common depending on the timings and indicates success
|
||||
* ERROR_IO_PENDING:
|
||||
* Since we're using ConnectNamedPipe asynchronously here, the function returns
|
||||
* immediately and this error code simply indicates that the operation is
|
||||
* still in progress. Hence we have to wait for the completion event and use
|
||||
* GetOverlappedResult to query the actual result of the operation (note that
|
||||
* the lpNumberOfBytesTransferred parameter is undefined/useless for a
|
||||
* ConnectNamedPipe operation)
|
||||
*/
|
||||
|
||||
if (!fConnected)
|
||||
fConnected = (status == ERROR_PIPE_CONNECTED);
|
||||
|
||||
printf("server: ConnectNamedPipe status: %" PRIu32 "\n", status);
|
||||
|
||||
if (!fConnected && status == ERROR_IO_PENDING)
|
||||
{
|
||||
DWORD dwDummy = 0;
|
||||
printf("server: waiting up to %u ms for connection ...\n", PIPE_TIMEOUT_MS);
|
||||
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
||||
if (status == WAIT_OBJECT_0)
|
||||
fConnected = GetOverlappedResult(hNamedPipe, &overlapped, &dwDummy, FALSE);
|
||||
else
|
||||
printf("server: failed to wait for overlapped event (connect): %" PRIu32 "\n", status);
|
||||
}
|
||||
|
||||
if (!fConnected)
|
||||
{
|
||||
printf("server: ConnectNamedPipe failed: %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("server: named pipe successfully connected\n");
|
||||
|
||||
/* 4: read from named pipe */
|
||||
|
||||
if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE)))
|
||||
{
|
||||
printf("server: Error allocating read buffer\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
|
||||
NumberOfBytesTransferred = 0;
|
||||
|
||||
fSuccess = ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, nullptr, &overlapped);
|
||||
|
||||
if (!fSuccess)
|
||||
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("server: NamedPipe ReadFile failure (initial): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("server: failed to wait for overlapped event (read): %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("server: NamedPipe ReadFile failure (final): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("server: ReadFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred);
|
||||
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, NumberOfBytesTransferred);
|
||||
|
||||
if (NumberOfBytesTransferred != PIPE_BUFFER_SIZE ||
|
||||
memcmp(lpReadBuffer, CLIENT_MESSAGE, PIPE_BUFFER_SIZE) != 0)
|
||||
{
|
||||
printf("server: received unexpected data from client\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* 5: write to named pipe */
|
||||
|
||||
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
|
||||
NumberOfBytesTransferred = 0;
|
||||
|
||||
fSuccess = WriteFile(hNamedPipe, SERVER_MESSAGE, nNumberOfBytesToWrite, nullptr, &overlapped);
|
||||
|
||||
if (!fSuccess)
|
||||
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("server: NamedPipe WriteFile failure (initial): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("server: failed to wait for overlapped event (write): %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("server: NamedPipe WriteFile failure (final): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("server: WriteFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred);
|
||||
// winpr_HexDump("pipe.test", WLOG_DEBUG, lpWriteBuffer, NumberOfBytesTransferred);
|
||||
|
||||
bServerSuccess = TRUE;
|
||||
printf("server: finished successfully\n");
|
||||
|
||||
finish:
|
||||
(void)CloseHandle(hNamedPipe);
|
||||
(void)CloseHandle(hEvent);
|
||||
free(lpReadBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[])
|
||||
{
|
||||
HANDLE ClientThread = nullptr;
|
||||
HANDLE ServerThread = nullptr;
|
||||
int result = -1;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
FillMemory(SERVER_MESSAGE, PIPE_BUFFER_SIZE, 0xAA);
|
||||
FillMemory(CLIENT_MESSAGE, PIPE_BUFFER_SIZE, 0xBB);
|
||||
|
||||
if (!(serverReadyEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("CreateEvent failed: %" PRIu32 "\n", GetLastError());
|
||||
goto out;
|
||||
}
|
||||
if (!(ClientThread = CreateThread(nullptr, 0, named_pipe_client_thread, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CreateThread (client) failed: %" PRIu32 "\n", GetLastError());
|
||||
goto out;
|
||||
}
|
||||
if (!(ServerThread = CreateThread(nullptr, 0, named_pipe_server_thread, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CreateThread (server) failed: %" PRIu32 "\n", GetLastError());
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WAIT_OBJECT_0 != WaitForSingleObject(ClientThread, INFINITE))
|
||||
{
|
||||
printf("%s: Failed to wait for client thread: %" PRIu32 "\n", __func__, GetLastError());
|
||||
goto out;
|
||||
}
|
||||
if (WAIT_OBJECT_0 != WaitForSingleObject(ServerThread, INFINITE))
|
||||
{
|
||||
printf("%s: Failed to wait for server thread: %" PRIu32 "\n", __func__, GetLastError());
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bClientSuccess && bServerSuccess)
|
||||
result = 0;
|
||||
|
||||
out:
|
||||
|
||||
if (ClientThread)
|
||||
(void)CloseHandle(ClientThread);
|
||||
if (ServerThread)
|
||||
(void)CloseHandle(ServerThread);
|
||||
if (serverReadyEvent)
|
||||
(void)CloseHandle(serverReadyEvent);
|
||||
|
||||
#ifndef _WIN32
|
||||
if (result == 0)
|
||||
{
|
||||
printf("%s: Error, this test is currently expected not to succeed on this platform.\n",
|
||||
__func__);
|
||||
result = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%s: This test is currently expected to fail on this platform.\n", __func__);
|
||||
result = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user