Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,23 @@
set(MODULE_NAME "TestPipe")
set(MODULE_PREFIX "TEST_PIPE")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS TestPipeCreatePipe.c TestPipeCreateNamedPipe.c TestPipeCreateNamedPipeOverlapped.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} winpr)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")

View File

@@ -0,0 +1,510 @@
#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/print.h>
#include <winpr/synch.h>
#include <winpr/wlog.h>
#include <winpr/thread.h>
#ifndef _WIN32
#include <signal.h>
#endif
#include "../pipe.h"
#define PIPE_BUFFER_SIZE 32
static HANDLE ReadyEvent;
static LPTSTR lpszPipeNameMt = _T("\\\\.\\pipe\\winpr_test_pipe_mt");
static LPTSTR lpszPipeNameSt = _T("\\\\.\\pipe\\winpr_test_pipe_st");
static BOOL testFailed = FALSE;
static DWORD WINAPI named_pipe_client_thread(LPVOID arg)
{
HANDLE hNamedPipe = nullptr;
BYTE* lpReadBuffer = nullptr;
BYTE* lpWriteBuffer = nullptr;
BOOL fSuccess = FALSE;
DWORD nNumberOfBytesToRead = 0;
DWORD nNumberOfBytesToWrite = 0;
DWORD lpNumberOfBytesRead = 0;
DWORD lpNumberOfBytesWritten = 0;
(void)WaitForSingleObject(ReadyEvent, INFINITE);
hNamedPipe = CreateFile(lpszPipeNameMt, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
0, nullptr);
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
printf("%s: Named Pipe CreateFile failure: INVALID_HANDLE_VALUE\n", __func__);
goto out;
}
if (!(lpReadBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE)))
{
printf("%s: Error allocating read buffer\n", __func__);
goto out;
}
if (!(lpWriteBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE)))
{
printf("%s: Error allocating write buffer\n", __func__);
goto out;
}
lpNumberOfBytesWritten = 0;
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
FillMemory(lpWriteBuffer, PIPE_BUFFER_SIZE, 0x59);
if (!WriteFile(hNamedPipe, lpWriteBuffer, nNumberOfBytesToWrite, &lpNumberOfBytesWritten,
nullptr) ||
lpNumberOfBytesWritten != nNumberOfBytesToWrite)
{
printf("%s: Client NamedPipe WriteFile failure\n", __func__);
goto out;
}
lpNumberOfBytesRead = 0;
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
ZeroMemory(lpReadBuffer, PIPE_BUFFER_SIZE);
if (!ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, &lpNumberOfBytesRead, nullptr) ||
lpNumberOfBytesRead != nNumberOfBytesToRead)
{
printf("%s: Client NamedPipe ReadFile failure\n", __func__);
goto out;
}
printf("Client ReadFile: %" PRIu32 " bytes\n", lpNumberOfBytesRead);
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, lpNumberOfBytesRead);
fSuccess = TRUE;
out:
free(lpReadBuffer);
free(lpWriteBuffer);
(void)CloseHandle(hNamedPipe);
if (!fSuccess)
testFailed = TRUE;
ExitThread(0);
return 0;
}
static DWORD WINAPI named_pipe_server_thread(LPVOID arg)
{
HANDLE hNamedPipe = nullptr;
BYTE* lpReadBuffer = nullptr;
BYTE* lpWriteBuffer = nullptr;
BOOL fSuccess = FALSE;
BOOL fConnected = FALSE;
DWORD nNumberOfBytesToRead = 0;
DWORD nNumberOfBytesToWrite = 0;
DWORD lpNumberOfBytesRead = 0;
DWORD lpNumberOfBytesWritten = 0;
hNamedPipe = CreateNamedPipe(
lpszPipeNameMt, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, nullptr);
if (!hNamedPipe)
{
printf("%s: CreateNamedPipe failure: nullptr handle\n", __func__);
goto out;
}
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
printf("%s: CreateNamedPipe failure: INVALID_HANDLE_VALUE\n", __func__);
goto out;
}
(void)SetEvent(ReadyEvent);
/**
* Note:
* If a client connects before ConnectNamedPipe is called, the function returns zero and
* GetLastError returns ERROR_PIPE_CONNECTED. This can happen if a client connects in the
* interval between the call to CreateNamedPipe and the call to ConnectNamedPipe.
* In this situation, there is a good connection between client and server, even though
* the function returns zero.
*/
fConnected =
ConnectNamedPipe(hNamedPipe, nullptr) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (!fConnected)
{
printf("%s: ConnectNamedPipe failure\n", __func__);
goto out;
}
if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE)))
{
printf("%s: Error allocating read buffer\n", __func__);
goto out;
}
if (!(lpWriteBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE)))
{
printf("%s: Error allocating write buffer\n", __func__);
goto out;
}
lpNumberOfBytesRead = 0;
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
if (!ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, &lpNumberOfBytesRead, nullptr) ||
lpNumberOfBytesRead != nNumberOfBytesToRead)
{
printf("%s: Server NamedPipe ReadFile failure\n", __func__);
goto out;
}
printf("Server ReadFile: %" PRIu32 " bytes\n", lpNumberOfBytesRead);
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, lpNumberOfBytesRead);
lpNumberOfBytesWritten = 0;
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
FillMemory(lpWriteBuffer, PIPE_BUFFER_SIZE, 0x45);
if (!WriteFile(hNamedPipe, lpWriteBuffer, nNumberOfBytesToWrite, &lpNumberOfBytesWritten,
nullptr) ||
lpNumberOfBytesWritten != nNumberOfBytesToWrite)
{
printf("%s: Server NamedPipe WriteFile failure\n", __func__);
goto out;
}
fSuccess = TRUE;
out:
free(lpReadBuffer);
free(lpWriteBuffer);
(void)CloseHandle(hNamedPipe);
if (!fSuccess)
testFailed = TRUE;
ExitThread(0);
return 0;
}
#define TESTNUMPIPESST 16
static DWORD WINAPI named_pipe_single_thread(LPVOID arg)
{
HANDLE servers[TESTNUMPIPESST] = WINPR_C_ARRAY_INIT;
HANDLE clients[TESTNUMPIPESST] = WINPR_C_ARRAY_INIT;
DWORD dwRead = 0;
DWORD dwWritten = 0;
int numPipes = 0;
BOOL bSuccess = FALSE;
numPipes = TESTNUMPIPESST;
(void)WaitForSingleObject(ReadyEvent, INFINITE);
for (int i = 0; i < numPipes; i++)
{
if (!(servers[i] = CreateNamedPipe(lpszPipeNameSt, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE,
PIPE_BUFFER_SIZE, 0, nullptr)))
{
printf("%s: CreateNamedPipe #%d failed\n", __func__, i);
goto out;
}
}
#ifndef _WIN32
for (int i = 0; i < numPipes; i++)
{
WINPR_NAMED_PIPE* p = (WINPR_NAMED_PIPE*)servers[i];
if (strcmp(lpszPipeNameSt, p->name) != 0)
{
printf("%s: Pipe name mismatch for pipe #%d ([%s] instead of [%s])\n", __func__, i,
p->name, lpszPipeNameSt);
goto out;
}
if (p->clientfd != -1)
{
printf("%s: Unexpected client fd value for pipe #%d (%d instead of -1)\n", __func__, i,
p->clientfd);
goto out;
}
if (p->serverfd < 1)
{
printf("%s: Unexpected server fd value for pipe #%d (%d is not > 0)\n", __func__, i,
p->serverfd);
goto out;
}
if (p->ServerMode == FALSE)
{
printf("%s: Unexpected ServerMode value for pipe #%d (0 instead of 1)\n", __func__, i);
goto out;
}
}
#endif
for (int i = 0; i < numPipes; i++)
{
BOOL fConnected = 0;
if ((clients[i] = CreateFile(lpszPipeNameSt, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, 0, nullptr)) == INVALID_HANDLE_VALUE)
{
printf("%s: CreateFile #%d failed\n", __func__, i);
goto out;
}
/**
* Note:
* If a client connects before ConnectNamedPipe is called, the function returns zero and
* GetLastError returns ERROR_PIPE_CONNECTED. This can happen if a client connects in the
* interval between the call to CreateNamedPipe and the call to ConnectNamedPipe.
* In this situation, there is a good connection between client and server, even though
* the function returns zero.
*/
fConnected =
ConnectNamedPipe(servers[i], nullptr) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (!fConnected)
{
printf("%s: ConnectNamedPipe #%d failed. (%" PRIu32 ")\n", __func__, i, GetLastError());
goto out;
}
}
#ifndef _WIN32
for (int i = 0; i < numPipes; i++)
{
WINPR_NAMED_PIPE* p = servers[i];
if (p->clientfd < 1)
{
printf("%s: Unexpected client fd value for pipe #%d (%d is not > 0)\n", __func__, i,
p->clientfd);
goto out;
}
if (p->ServerMode)
{
printf("%s: Unexpected ServerMode value for pipe #%d (1 instead of 0)\n", __func__, i);
goto out;
}
}
for (int i = 0; i < numPipes; i++)
{
{
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
/* Test writing from clients to servers */
(void)sprintf_s(sndbuf, sizeof(sndbuf), "CLIENT->SERVER ON PIPE #%05d", i);
if (!WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr) ||
dwWritten != sizeof(sndbuf))
{
printf("%s: Error writing to client end of pipe #%d\n", __func__, i);
goto out;
}
if (!ReadFile(servers[i], rcvbuf, dwWritten, &dwRead, nullptr) || dwRead != dwWritten)
{
printf("%s: Error reading on server end of pipe #%d\n", __func__, i);
goto out;
}
if (memcmp(sndbuf, rcvbuf, sizeof(sndbuf)) != 0)
{
printf("%s: Error data read on server end of pipe #%d is corrupted\n", __func__, i);
goto out;
}
}
{
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
/* Test writing from servers to clients */
(void)sprintf_s(sndbuf, sizeof(sndbuf), "SERVER->CLIENT ON PIPE #%05d", i);
if (!WriteFile(servers[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr) ||
dwWritten != sizeof(sndbuf))
{
printf("%s: Error writing to server end of pipe #%d\n", __func__, i);
goto out;
}
if (!ReadFile(clients[i], rcvbuf, dwWritten, &dwRead, nullptr) || dwRead != dwWritten)
{
printf("%s: Error reading on client end of pipe #%d\n", __func__, i);
goto out;
}
if (memcmp(sndbuf, rcvbuf, sizeof(sndbuf)) != 0)
{
printf("%s: Error data read on client end of pipe #%d is corrupted\n", __func__, i);
goto out;
}
}
}
#endif
/**
* After DisconnectNamedPipe on server end
* ReadFile/WriteFile must fail on client end
*/
int i = numPipes - 1;
DisconnectNamedPipe(servers[i]);
{
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
if (ReadFile(clients[i], rcvbuf, sizeof(rcvbuf), &dwRead, nullptr))
{
printf("%s: Error ReadFile on client should have failed after DisconnectNamedPipe on "
"server\n",
__func__);
goto out;
}
if (WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr))
{
printf(
"%s: Error WriteFile on client end should have failed after DisconnectNamedPipe on "
"server\n",
__func__);
goto out;
}
}
(void)CloseHandle(servers[i]);
(void)CloseHandle(clients[i]);
numPipes--;
/**
* After CloseHandle (without calling DisconnectNamedPipe first) on server end
* ReadFile/WriteFile must fail on client end
*/
i = numPipes - 1;
(void)CloseHandle(servers[i]);
{
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
if (ReadFile(clients[i], rcvbuf, sizeof(rcvbuf), &dwRead, nullptr))
{
printf(
"%s: Error ReadFile on client end should have failed after CloseHandle on server\n",
__func__);
goto out;
}
if (WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr))
{
printf("%s: Error WriteFile on client end should have failed after CloseHandle on "
"server\n",
__func__);
goto out;
}
}
(void)CloseHandle(clients[i]);
numPipes--;
/**
* After CloseHandle on client end
* ReadFile/WriteFile must fail on server end
*/
i = numPipes - 1;
(void)CloseHandle(clients[i]);
{
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
if (ReadFile(servers[i], rcvbuf, sizeof(rcvbuf), &dwRead, nullptr))
{
printf(
"%s: Error ReadFile on server end should have failed after CloseHandle on client\n",
__func__);
goto out;
}
if (WriteFile(servers[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr))
{
printf("%s: Error WriteFile on server end should have failed after CloseHandle on "
"client\n",
__func__);
goto out;
}
}
DisconnectNamedPipe(servers[i]);
(void)CloseHandle(servers[i]);
numPipes--;
/* Close all remaining pipes */
for (int i = 0; i < numPipes; i++)
{
DisconnectNamedPipe(servers[i]);
(void)CloseHandle(servers[i]);
(void)CloseHandle(clients[i]);
}
bSuccess = TRUE;
out:
if (!bSuccess)
testFailed = TRUE;
return 0;
}
int TestPipeCreateNamedPipe(int argc, char* argv[])
{
HANDLE SingleThread = nullptr;
HANDLE ClientThread = nullptr;
HANDLE ServerThread = nullptr;
HANDLE hPipe = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
/* Verify that CreateNamedPipe returns INVALID_HANDLE_VALUE on failure */
hPipe = CreateNamedPipeA(nullptr, 0, 0, 0, 0, 0, 0, nullptr);
if (hPipe != INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe unexpectedly returned %p instead of INVALID_HANDLE_VALUE (%p)\n",
hPipe, INVALID_HANDLE_VALUE);
return -1;
}
#ifndef _WIN32
(void)signal(SIGPIPE, SIG_IGN);
#endif
if (!(ReadyEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
{
printf("CreateEvent failure: (%" PRIu32 ")\n", GetLastError());
return -1;
}
if (!(SingleThread = CreateThread(nullptr, 0, named_pipe_single_thread, nullptr, 0, nullptr)))
{
printf("CreateThread (SingleThread) failure: (%" PRIu32 ")\n", GetLastError());
return -1;
}
if (!(ClientThread = CreateThread(nullptr, 0, named_pipe_client_thread, nullptr, 0, nullptr)))
{
printf("CreateThread (ClientThread) failure: (%" PRIu32 ")\n", GetLastError());
return -1;
}
if (!(ServerThread = CreateThread(nullptr, 0, named_pipe_server_thread, nullptr, 0, nullptr)))
{
printf("CreateThread (ServerThread) failure: (%" PRIu32 ")\n", GetLastError());
return -1;
}
(void)WaitForSingleObject(SingleThread, INFINITE);
(void)WaitForSingleObject(ClientThread, INFINITE);
(void)WaitForSingleObject(ServerThread, INFINITE);
(void)CloseHandle(SingleThread);
(void)CloseHandle(ClientThread);
(void)CloseHandle(ServerThread);
return testFailed;
}

View 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;
}

View File

@@ -0,0 +1,72 @@
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/pipe.h>
#include <winpr/tchar.h>
#include <winpr/winpr.h>
#define BUFFER_SIZE 16
int TestPipeCreatePipe(int argc, char* argv[])
{
BOOL status = 0;
DWORD dwRead = 0;
DWORD dwWrite = 0;
HANDLE hReadPipe = nullptr;
HANDLE hWritePipe = nullptr;
BYTE readBuffer[BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
BYTE writeBuffer[BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
status = CreatePipe(&hReadPipe, &hWritePipe, nullptr, BUFFER_SIZE * 2);
if (!status)
{
_tprintf(_T("CreatePipe failed\n"));
return -1;
}
FillMemory(writeBuffer, sizeof(writeBuffer), 0xAA);
status = WriteFile(hWritePipe, &writeBuffer, sizeof(writeBuffer), &dwWrite, nullptr);
if (!status)
{
_tprintf(_T("WriteFile failed\n"));
return -1;
}
if (dwWrite != sizeof(writeBuffer))
{
_tprintf(_T("WriteFile: unexpected number of bytes written: Actual: %") _T(
PRIu32) _T(", Expected: %") _T(PRIuz) _T("\n"),
dwWrite, sizeof(writeBuffer));
return -1;
}
status = ReadFile(hReadPipe, &readBuffer, sizeof(readBuffer), &dwRead, nullptr);
if (!status)
{
_tprintf(_T("ReadFile failed\n"));
return -1;
}
if (dwRead != sizeof(readBuffer))
{
_tprintf(_T("ReadFile: unexpected number of bytes read: Actual: %") _T(
PRIu32) _T(", Expected: %") _T(PRIuz) _T("\n"),
dwWrite, sizeof(readBuffer));
return -1;
}
if (memcmp(readBuffer, writeBuffer, BUFFER_SIZE) != 0)
{
_tprintf(_T("ReadFile: read buffer is different from write buffer\n"));
return -1;
}
(void)CloseHandle(hReadPipe);
(void)CloseHandle(hWritePipe);
return 0;
}