diff options
Diffstat (limited to 'winpr/libwinpr/pipe/test')
-rw-r--r-- | winpr/libwinpr/pipe/test/CMakeLists.txt | 27 | ||||
-rw-r--r-- | winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c | 510 | ||||
-rw-r--r-- | winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c | 397 | ||||
-rw-r--r-- | winpr/libwinpr/pipe/test/TestPipeCreatePipe.c | 72 |
4 files changed, 1006 insertions, 0 deletions
diff --git a/winpr/libwinpr/pipe/test/CMakeLists.txt b/winpr/libwinpr/pipe/test/CMakeLists.txt new file mode 100644 index 0000000..b9bf685 --- /dev/null +++ b/winpr/libwinpr/pipe/test/CMakeLists.txt @@ -0,0 +1,27 @@ + +set(MODULE_NAME "TestPipe") +set(MODULE_PREFIX "TEST_PIPE") + +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") diff --git a/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c new file mode 100644 index 0000000..8c8ead2 --- /dev/null +++ b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c @@ -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 = NULL; + BYTE* lpReadBuffer = NULL; + BYTE* lpWriteBuffer = NULL; + BOOL fSuccess = FALSE; + DWORD nNumberOfBytesToRead = 0; + DWORD nNumberOfBytesToWrite = 0; + DWORD lpNumberOfBytesRead = 0; + DWORD lpNumberOfBytesWritten = 0; + WaitForSingleObject(ReadyEvent, INFINITE); + hNamedPipe = + CreateFile(lpszPipeNameMt, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + 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, + NULL) || + 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, NULL) || + 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); + CloseHandle(hNamedPipe); + + if (!fSuccess) + testFailed = TRUE; + + ExitThread(0); + return 0; +} + +static DWORD WINAPI named_pipe_server_thread(LPVOID arg) +{ + HANDLE hNamedPipe = NULL; + BYTE* lpReadBuffer = NULL; + BYTE* lpWriteBuffer = NULL; + 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, NULL); + + if (!hNamedPipe) + { + printf("%s: CreateNamedPipe failure: NULL handle\n", __func__); + goto out; + } + + if (hNamedPipe == INVALID_HANDLE_VALUE) + { + printf("%s: CreateNamedPipe failure: INVALID_HANDLE_VALUE\n", __func__); + goto out; + } + + 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, NULL) ? 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, NULL) || + 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, + NULL) || + lpNumberOfBytesWritten != nNumberOfBytesToWrite) + { + printf("%s: Server NamedPipe WriteFile failure\n", __func__); + goto out; + } + + fSuccess = TRUE; +out: + free(lpReadBuffer); + free(lpWriteBuffer); + 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] = { 0 }; + HANDLE clients[TESTNUMPIPESST] = { 0 }; + DWORD dwRead = 0; + DWORD dwWritten = 0; + int numPipes = 0; + BOOL bSuccess = FALSE; + numPipes = TESTNUMPIPESST; + 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, NULL))) + { + 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)) + { + 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, NULL, + OPEN_EXISTING, 0, NULL)) == 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], NULL) ? 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] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + /* Test writing from clients to servers */ + sprintf_s(sndbuf, sizeof(sndbuf), "CLIENT->SERVER ON PIPE #%05d", i); + + if (!WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, NULL) || + 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, NULL) || dwRead != dwWritten) + { + printf("%s: Error reading on server end of pipe #%d\n", __func__, i); + goto out; + } + + if (memcmp(sndbuf, rcvbuf, sizeof(sndbuf))) + { + printf("%s: Error data read on server end of pipe #%d is corrupted\n", __func__, i); + goto out; + } + } + { + + char sndbuf[PIPE_BUFFER_SIZE] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + /* Test writing from servers to clients */ + + sprintf_s(sndbuf, sizeof(sndbuf), "SERVER->CLIENT ON PIPE #%05d", i); + + if (!WriteFile(servers[i], sndbuf, sizeof(sndbuf), &dwWritten, NULL) || + 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, NULL) || dwRead != dwWritten) + { + printf("%s: Error reading on client end of pipe #%d\n", __func__, i); + goto out; + } + + if (memcmp(sndbuf, rcvbuf, sizeof(sndbuf))) + { + 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] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + if (ReadFile(clients[i], rcvbuf, sizeof(rcvbuf), &dwRead, NULL)) + { + 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, NULL)) + { + printf( + "%s: Error WriteFile on client end should have failed after DisconnectNamedPipe on " + "server\n", + __func__); + goto out; + } + } + CloseHandle(servers[i]); + CloseHandle(clients[i]); + numPipes--; + /** + * After CloseHandle (without calling DisconnectNamedPipe first) on server end + * ReadFile/WriteFile must fail on client end + */ + i = numPipes - 1; + CloseHandle(servers[i]); + + { + char sndbuf[PIPE_BUFFER_SIZE] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + + if (ReadFile(clients[i], rcvbuf, sizeof(rcvbuf), &dwRead, NULL)) + { + 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, NULL)) + { + printf("%s: Error WriteFile on client end should have failed after CloseHandle on " + "server\n", + __func__); + goto out; + } + } + CloseHandle(clients[i]); + numPipes--; + /** + * After CloseHandle on client end + * ReadFile/WriteFile must fail on server end + */ + i = numPipes - 1; + CloseHandle(clients[i]); + + { + char sndbuf[PIPE_BUFFER_SIZE] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + + if (ReadFile(servers[i], rcvbuf, sizeof(rcvbuf), &dwRead, NULL)) + { + 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, NULL)) + { + printf("%s: Error WriteFile on server end should have failed after CloseHandle on " + "client\n", + __func__); + goto out; + } + } + + DisconnectNamedPipe(servers[i]); + CloseHandle(servers[i]); + numPipes--; + + /* Close all remaining pipes */ + for (int i = 0; i < numPipes; i++) + { + DisconnectNamedPipe(servers[i]); + CloseHandle(servers[i]); + CloseHandle(clients[i]); + } + + bSuccess = TRUE; +out: + + if (!bSuccess) + testFailed = TRUE; + + return 0; +} + +int TestPipeCreateNamedPipe(int argc, char* argv[]) +{ + HANDLE SingleThread = NULL; + HANDLE ClientThread = NULL; + HANDLE ServerThread = NULL; + HANDLE hPipe = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + /* Verify that CreateNamedPipe returns INVALID_HANDLE_VALUE on failure */ + hPipe = CreateNamedPipeA(NULL, 0, 0, 0, 0, 0, 0, NULL); + 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 + signal(SIGPIPE, SIG_IGN); +#endif + if (!(ReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + printf("CreateEvent failure: (%" PRIu32 ")\n", GetLastError()); + return -1; + } + if (!(SingleThread = CreateThread(NULL, 0, named_pipe_single_thread, NULL, 0, NULL))) + { + printf("CreateThread (SingleThread) failure: (%" PRIu32 ")\n", GetLastError()); + return -1; + } + if (!(ClientThread = CreateThread(NULL, 0, named_pipe_client_thread, NULL, 0, NULL))) + { + printf("CreateThread (ClientThread) failure: (%" PRIu32 ")\n", GetLastError()); + return -1; + } + if (!(ServerThread = CreateThread(NULL, 0, named_pipe_server_thread, NULL, 0, NULL))) + { + printf("CreateThread (ServerThread) failure: (%" PRIu32 ")\n", GetLastError()); + return -1; + } + WaitForSingleObject(SingleThread, INFINITE); + WaitForSingleObject(ClientThread, INFINITE); + WaitForSingleObject(ServerThread, INFINITE); + CloseHandle(SingleThread); + CloseHandle(ClientThread); + CloseHandle(ServerThread); + return testFailed; +} diff --git a/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c new file mode 100644 index 0000000..de95840 --- /dev/null +++ b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c @@ -0,0 +1,397 @@ + +#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 = NULL; + +static LPTSTR lpszPipeName = _T("\\\\.\\pipe\\winpr_test_pipe_overlapped"); + +static DWORD WINAPI named_pipe_client_thread(LPVOID arg) +{ + DWORD status = 0; + HANDLE hEvent = NULL; + HANDLE hNamedPipe = NULL; + BYTE* lpReadBuffer = NULL; + BOOL fSuccess = FALSE; + OVERLAPPED overlapped = { 0 }; + 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(NULL, TRUE, FALSE, NULL))) + { + 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, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + + 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, NULL, &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, NULL, &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)) + { + printf("client: received unexpected data from server\n"); + goto finish; + } + + printf("client: finished successfully\n"); + bClientSuccess = TRUE; + +finish: + free(lpReadBuffer); + if (hNamedPipe) + CloseHandle(hNamedPipe); + if (hEvent) + CloseHandle(hEvent); + + return 0; +} + +static DWORD WINAPI named_pipe_server_thread(LPVOID arg) +{ + DWORD status = 0; + HANDLE hEvent = NULL; + HANDLE hNamedPipe = NULL; + BYTE* lpReadBuffer = NULL; + OVERLAPPED overlapped = { 0 }; + 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(NULL, TRUE, FALSE, NULL))) + { + printf("server: CreateEvent failure: %" PRIu32 "\n", GetLastError()); + 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, NULL); + + if (hNamedPipe == INVALID_HANDLE_VALUE) + { + printf("server: CreateNamedPipe failure: %" PRIu32 "\n", GetLastError()); + SetEvent(serverReadyEvent); /* unblock client thread */ + goto finish; + } + + SetEvent(serverReadyEvent); + + /* 3: connect named pipe */ + +#if 0 + /* This sleep will most certainly cause ERROR_PIPE_CONNECTED below */ + Sleep(2000); +#endif + + 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, NULL, &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)) + { + 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, NULL, &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: + CloseHandle(hNamedPipe); + CloseHandle(hEvent); + free(lpReadBuffer); + return 0; +} + +int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[]) +{ + HANDLE ClientThread = NULL; + HANDLE ServerThread = NULL; + 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(NULL, TRUE, FALSE, NULL))) + { + printf("CreateEvent failed: %" PRIu32 "\n", GetLastError()); + goto out; + } + if (!(ClientThread = CreateThread(NULL, 0, named_pipe_client_thread, NULL, 0, NULL))) + { + printf("CreateThread (client) failed: %" PRIu32 "\n", GetLastError()); + goto out; + } + if (!(ServerThread = CreateThread(NULL, 0, named_pipe_server_thread, NULL, 0, NULL))) + { + 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) + CloseHandle(ClientThread); + if (ServerThread) + CloseHandle(ServerThread); + if (serverReadyEvent) + 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; +} diff --git a/winpr/libwinpr/pipe/test/TestPipeCreatePipe.c b/winpr/libwinpr/pipe/test/TestPipeCreatePipe.c new file mode 100644 index 0000000..db346ef --- /dev/null +++ b/winpr/libwinpr/pipe/test/TestPipeCreatePipe.c @@ -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 = NULL; + HANDLE hWritePipe = NULL; + BYTE readBuffer[BUFFER_SIZE] = { 0 }; + BYTE writeBuffer[BUFFER_SIZE] = { 0 }; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + status = CreatePipe(&hReadPipe, &hWritePipe, NULL, BUFFER_SIZE * 2); + + if (!status) + { + _tprintf(_T("CreatePipe failed\n")); + return -1; + } + + FillMemory(writeBuffer, sizeof(writeBuffer), 0xAA); + status = WriteFile(hWritePipe, &writeBuffer, sizeof(writeBuffer), &dwWrite, NULL); + + 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, NULL); + + 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; + } + + CloseHandle(hReadPipe); + CloseHandle(hWritePipe); + + return 0; +} |