summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/pipe
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/pipe
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/pipe')
-rw-r--r--winpr/libwinpr/pipe/CMakeLists.txt22
-rw-r--r--winpr/libwinpr/pipe/ModuleOptions.cmake9
-rw-r--r--winpr/libwinpr/pipe/pipe.c930
-rw-r--r--winpr/libwinpr/pipe/pipe.h75
-rw-r--r--winpr/libwinpr/pipe/test/CMakeLists.txt27
-rw-r--r--winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c510
-rw-r--r--winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c397
-rw-r--r--winpr/libwinpr/pipe/test/TestPipeCreatePipe.c72
8 files changed, 2042 insertions, 0 deletions
diff --git a/winpr/libwinpr/pipe/CMakeLists.txt b/winpr/libwinpr/pipe/CMakeLists.txt
new file mode 100644
index 0000000..a78b98a
--- /dev/null
+++ b/winpr/libwinpr/pipe/CMakeLists.txt
@@ -0,0 +1,22 @@
+# WinPR: Windows Portable Runtime
+# libwinpr-pipe cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+winpr_module_add(pipe.c pipe.h)
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
diff --git a/winpr/libwinpr/pipe/ModuleOptions.cmake b/winpr/libwinpr/pipe/ModuleOptions.cmake
new file mode 100644
index 0000000..557e6a1
--- /dev/null
+++ b/winpr/libwinpr/pipe/ModuleOptions.cmake
@@ -0,0 +1,9 @@
+
+set(MINWIN_LAYER "1")
+set(MINWIN_GROUP "core")
+set(MINWIN_MAJOR_VERSION "2")
+set(MINWIN_MINOR_VERSION "0")
+set(MINWIN_SHORT_NAME "namedpipe")
+set(MINWIN_LONG_NAME "Named Pipe Functions")
+set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}")
+
diff --git a/winpr/libwinpr/pipe/pipe.c b/winpr/libwinpr/pipe/pipe.c
new file mode 100644
index 0000000..dba1bf3
--- /dev/null
+++ b/winpr/libwinpr/pipe/pipe.c
@@ -0,0 +1,930 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Pipe Functions
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/path.h>
+#include <winpr/synch.h>
+#include <winpr/handle.h>
+
+#include <winpr/pipe.h>
+
+#ifdef WINPR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef _WIN32
+
+#include "../handle/handle.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <winpr/assert.h>
+
+#ifdef WINPR_HAVE_SYS_AIO_H
+#undef WINPR_HAVE_SYS_AIO_H /* disable for now, incomplete */
+#endif
+
+#ifdef WINPR_HAVE_SYS_AIO_H
+#include <aio.h>
+#endif
+
+#include "pipe.h"
+
+#include "../log.h"
+#define TAG WINPR_TAG("pipe")
+
+/*
+ * Since the WinPR implementation of named pipes makes use of UNIX domain
+ * sockets, it is not possible to bind the same name more than once (i.e.,
+ * SO_REUSEADDR does not work with UNIX domain sockets). As a result, the
+ * first call to CreateNamedPipe with name n creates a "shared" UNIX domain
+ * socket descriptor that gets duplicated via dup() for the first and all
+ * subsequent calls to CreateNamedPipe with name n.
+ *
+ * The following array keeps track of the references to the shared socked
+ * descriptors. If an entry's reference count is zero the base socket
+ * descriptor gets closed and the entry is removed from the list.
+ */
+
+static wArrayList* g_NamedPipeServerSockets = NULL;
+
+typedef struct
+{
+ char* name;
+ int serverfd;
+ int references;
+} NamedPipeServerSocketEntry;
+
+static BOOL PipeIsHandled(HANDLE handle)
+{
+ return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_ANONYMOUS_PIPE, FALSE);
+}
+
+static int PipeGetFd(HANDLE handle)
+{
+ WINPR_PIPE* pipe = (WINPR_PIPE*)handle;
+
+ if (!PipeIsHandled(handle))
+ return -1;
+
+ return pipe->fd;
+}
+
+static BOOL PipeCloseHandle(HANDLE handle)
+{
+ WINPR_PIPE* pipe = (WINPR_PIPE*)handle;
+
+ if (!PipeIsHandled(handle))
+ return FALSE;
+
+ if (pipe->fd != -1)
+ {
+ close(pipe->fd);
+ pipe->fd = -1;
+ }
+
+ free(handle);
+ return TRUE;
+}
+
+static BOOL PipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
+{
+ SSIZE_T io_status = 0;
+ WINPR_PIPE* pipe = NULL;
+ BOOL status = TRUE;
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ pipe = (WINPR_PIPE*)Object;
+
+ do
+ {
+ io_status = read(pipe->fd, lpBuffer, nNumberOfBytesToRead);
+ } while ((io_status < 0) && (errno == EINTR));
+
+ if (io_status < 0)
+ {
+ status = FALSE;
+
+ switch (errno)
+ {
+ case EWOULDBLOCK:
+ SetLastError(ERROR_NO_DATA);
+ break;
+ }
+ }
+
+ if (lpNumberOfBytesRead)
+ *lpNumberOfBytesRead = (DWORD)io_status;
+
+ return status;
+}
+
+static BOOL PipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
+{
+ SSIZE_T io_status = 0;
+ WINPR_PIPE* pipe = NULL;
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ pipe = (WINPR_PIPE*)Object;
+
+ do
+ {
+ io_status = write(pipe->fd, lpBuffer, nNumberOfBytesToWrite);
+ } while ((io_status < 0) && (errno == EINTR));
+
+ if ((io_status < 0) && (errno == EWOULDBLOCK))
+ io_status = 0;
+
+ *lpNumberOfBytesWritten = (DWORD)io_status;
+ return TRUE;
+}
+
+static HANDLE_OPS ops = { PipeIsHandled,
+ PipeCloseHandle,
+ PipeGetFd,
+ NULL, /* CleanupHandle */
+ PipeRead,
+ NULL, /* FileReadEx */
+ NULL, /* FileReadScatter */
+ PipeWrite,
+ NULL, /* FileWriteEx */
+ NULL, /* FileWriteGather */
+ NULL, /* FileGetFileSize */
+ NULL, /* FlushFileBuffers */
+ NULL, /* FileSetEndOfFile */
+ NULL, /* FileSetFilePointer */
+ NULL, /* SetFilePointerEx */
+ NULL, /* FileLockFile */
+ NULL, /* FileLockFileEx */
+ NULL, /* FileUnlockFile */
+ NULL, /* FileUnlockFileEx */
+ NULL /* SetFileTime */
+ ,
+ NULL };
+
+static BOOL NamedPipeIsHandled(HANDLE handle)
+{
+ return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_NAMED_PIPE, TRUE);
+}
+
+static int NamedPipeGetFd(HANDLE handle)
+{
+ WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*)handle;
+
+ if (!NamedPipeIsHandled(handle))
+ return -1;
+
+ if (pipe->ServerMode)
+ return pipe->serverfd;
+
+ return pipe->clientfd;
+}
+
+static BOOL NamedPipeCloseHandle(HANDLE handle)
+{
+ WINPR_NAMED_PIPE* pNamedPipe = (WINPR_NAMED_PIPE*)handle;
+
+ /* This check confuses the analyzer. Since not all handle
+ * types are handled here, it guesses that the memory of a
+ * NamedPipeHandle may leak. */
+#ifndef __clang_analyzer__
+ if (!NamedPipeIsHandled(handle))
+ return FALSE;
+#endif
+
+ if (pNamedPipe->pfnUnrefNamedPipe)
+ pNamedPipe->pfnUnrefNamedPipe(pNamedPipe);
+
+ free(pNamedPipe->name);
+ free(pNamedPipe->lpFileName);
+ free(pNamedPipe->lpFilePath);
+
+ if (pNamedPipe->serverfd != -1)
+ close(pNamedPipe->serverfd);
+
+ if (pNamedPipe->clientfd != -1)
+ close(pNamedPipe->clientfd);
+
+ free(pNamedPipe);
+ return TRUE;
+}
+
+BOOL NamedPipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
+{
+ SSIZE_T io_status = 0;
+ WINPR_NAMED_PIPE* pipe = NULL;
+ BOOL status = TRUE;
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ pipe = (WINPR_NAMED_PIPE*)Object;
+
+ if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
+ {
+ if (pipe->clientfd == -1)
+ return FALSE;
+
+ do
+ {
+ io_status = read(pipe->clientfd, lpBuffer, nNumberOfBytesToRead);
+ } while ((io_status < 0) && (errno == EINTR));
+
+ if (io_status == 0)
+ {
+ SetLastError(ERROR_BROKEN_PIPE);
+ status = FALSE;
+ }
+ else if (io_status < 0)
+ {
+ status = FALSE;
+
+ switch (errno)
+ {
+ case EWOULDBLOCK:
+ SetLastError(ERROR_NO_DATA);
+ break;
+
+ default:
+ SetLastError(ERROR_BROKEN_PIPE);
+ break;
+ }
+ }
+
+ if (lpNumberOfBytesRead)
+ *lpNumberOfBytesRead = (DWORD)io_status;
+ }
+ else
+ {
+ /* Overlapped I/O */
+ if (!lpOverlapped)
+ return FALSE;
+
+ if (pipe->clientfd == -1)
+ return FALSE;
+
+ pipe->lpOverlapped = lpOverlapped;
+#ifdef WINPR_HAVE_SYS_AIO_H
+ {
+ int aio_status;
+ struct aiocb cb = { 0 };
+
+ cb.aio_fildes = pipe->clientfd;
+ cb.aio_buf = lpBuffer;
+ cb.aio_nbytes = nNumberOfBytesToRead;
+ cb.aio_offset = lpOverlapped->Offset;
+ cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+ cb.aio_sigevent.sigev_signo = SIGIO;
+ cb.aio_sigevent.sigev_value.sival_ptr = (void*)lpOverlapped;
+ InstallAioSignalHandler();
+ aio_status = aio_read(&cb);
+ WLog_DBG(TAG, "aio_read status: %d", aio_status);
+
+ if (aio_status < 0)
+ status = FALSE;
+
+ return status;
+ }
+#else
+ /* synchronous behavior */
+ lpOverlapped->Internal = 0;
+ lpOverlapped->InternalHigh = (ULONG_PTR)nNumberOfBytesToRead;
+ lpOverlapped->Pointer = (PVOID)lpBuffer;
+ SetEvent(lpOverlapped->hEvent);
+#endif
+ }
+
+ return status;
+}
+
+BOOL NamedPipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
+{
+ SSIZE_T io_status = 0;
+ WINPR_NAMED_PIPE* pipe = NULL;
+ BOOL status = TRUE;
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ pipe = (WINPR_NAMED_PIPE*)Object;
+
+ if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
+ {
+ if (pipe->clientfd == -1)
+ return FALSE;
+
+ do
+ {
+ io_status = write(pipe->clientfd, lpBuffer, nNumberOfBytesToWrite);
+ } while ((io_status < 0) && (errno == EINTR));
+
+ if (io_status < 0)
+ {
+ *lpNumberOfBytesWritten = 0;
+
+ switch (errno)
+ {
+ case EWOULDBLOCK:
+ io_status = 0;
+ status = TRUE;
+ break;
+
+ default:
+ status = FALSE;
+ }
+ }
+
+ *lpNumberOfBytesWritten = (DWORD)io_status;
+ return status;
+ }
+ else
+ {
+ /* Overlapped I/O */
+ if (!lpOverlapped)
+ return FALSE;
+
+ if (pipe->clientfd == -1)
+ return FALSE;
+
+ pipe->lpOverlapped = lpOverlapped;
+#ifdef WINPR_HAVE_SYS_AIO_H
+ {
+ struct aiocb cb = { 0 };
+
+ cb.aio_fildes = pipe->clientfd;
+ cb.aio_buf = (void*)lpBuffer;
+ cb.aio_nbytes = nNumberOfBytesToWrite;
+ cb.aio_offset = lpOverlapped->Offset;
+ cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+ cb.aio_sigevent.sigev_signo = SIGIO;
+ cb.aio_sigevent.sigev_value.sival_ptr = (void*)lpOverlapped;
+ InstallAioSignalHandler();
+ io_status = aio_write(&cb);
+ WLog_DBG("aio_write status: %" PRIdz, io_status);
+
+ if (io_status < 0)
+ status = FALSE;
+
+ return status;
+ }
+#else
+ /* synchronous behavior */
+ lpOverlapped->Internal = 1;
+ lpOverlapped->InternalHigh = (ULONG_PTR)nNumberOfBytesToWrite;
+ {
+ union
+ {
+ LPCVOID cpv;
+ PVOID pv;
+ } cnv;
+ cnv.cpv = lpBuffer;
+ lpOverlapped->Pointer = cnv.pv;
+ }
+ SetEvent(lpOverlapped->hEvent);
+#endif
+ }
+
+ return TRUE;
+}
+
+static HANDLE_OPS namedOps = { NamedPipeIsHandled,
+ NamedPipeCloseHandle,
+ NamedPipeGetFd,
+ NULL, /* CleanupHandle */
+ NamedPipeRead,
+ NULL,
+ NULL,
+ NamedPipeWrite,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL };
+
+static BOOL InitWinPRPipeModule(void)
+{
+ if (g_NamedPipeServerSockets)
+ return TRUE;
+
+ g_NamedPipeServerSockets = ArrayList_New(FALSE);
+ return g_NamedPipeServerSockets != NULL;
+}
+
+/*
+ * Unnamed pipe
+ */
+
+BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes,
+ DWORD nSize)
+{
+ int pipe_fd[2];
+ WINPR_PIPE* pReadPipe = NULL;
+ WINPR_PIPE* pWritePipe = NULL;
+
+ WINPR_UNUSED(lpPipeAttributes);
+ WINPR_UNUSED(nSize);
+
+ pipe_fd[0] = -1;
+ pipe_fd[1] = -1;
+
+ if (pipe(pipe_fd) < 0)
+ {
+ WLog_ERR(TAG, "failed to create pipe");
+ return FALSE;
+ }
+
+ pReadPipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE));
+ pWritePipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE));
+
+ if (!pReadPipe || !pWritePipe)
+ {
+ free(pReadPipe);
+ free(pWritePipe);
+ return FALSE;
+ }
+
+ pReadPipe->fd = pipe_fd[0];
+ pWritePipe->fd = pipe_fd[1];
+ WINPR_HANDLE_SET_TYPE_AND_MODE(pReadPipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ);
+ pReadPipe->common.ops = &ops;
+ *((ULONG_PTR*)hReadPipe) = (ULONG_PTR)pReadPipe;
+ WINPR_HANDLE_SET_TYPE_AND_MODE(pWritePipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ);
+ pWritePipe->common.ops = &ops;
+ *((ULONG_PTR*)hWritePipe) = (ULONG_PTR)pWritePipe;
+ return TRUE;
+}
+
+/**
+ * Named pipe
+ */
+
+static void winpr_unref_named_pipe(WINPR_NAMED_PIPE* pNamedPipe)
+{
+ NamedPipeServerSocketEntry* baseSocket = NULL;
+
+ if (!pNamedPipe)
+ return;
+
+ WINPR_ASSERT(pNamedPipe->name);
+ WINPR_ASSERT(g_NamedPipeServerSockets);
+ // WLog_VRB(TAG, "%p (%s)", (void*) pNamedPipe, pNamedPipe->name);
+ ArrayList_Lock(g_NamedPipeServerSockets);
+
+ for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++)
+ {
+ baseSocket =
+ (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index);
+ WINPR_ASSERT(baseSocket->name);
+
+ if (!strcmp(baseSocket->name, pNamedPipe->name))
+ {
+ WINPR_ASSERT(baseSocket->references > 0);
+ WINPR_ASSERT(baseSocket->serverfd != -1);
+
+ if (--baseSocket->references == 0)
+ {
+ // WLog_DBG(TAG, "removing shared server socked resource");
+ // WLog_DBG(TAG, "closing shared serverfd %d", baseSocket->serverfd);
+ ArrayList_Remove(g_NamedPipeServerSockets, baseSocket);
+ close(baseSocket->serverfd);
+ free(baseSocket->name);
+ free(baseSocket);
+ }
+
+ break;
+ }
+ }
+
+ ArrayList_Unlock(g_NamedPipeServerSockets);
+}
+
+HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
+ DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+{
+ char* lpPipePath = NULL;
+ WINPR_NAMED_PIPE* pNamedPipe = NULL;
+ int serverfd = -1;
+ NamedPipeServerSocketEntry* baseSocket = NULL;
+
+ WINPR_UNUSED(lpSecurityAttributes);
+
+ if (dwOpenMode & FILE_FLAG_OVERLAPPED)
+ {
+ WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (!lpName)
+ return INVALID_HANDLE_VALUE;
+
+ if (!InitWinPRPipeModule())
+ return INVALID_HANDLE_VALUE;
+
+ pNamedPipe = (WINPR_NAMED_PIPE*)calloc(1, sizeof(WINPR_NAMED_PIPE));
+
+ if (!pNamedPipe)
+ return INVALID_HANDLE_VALUE;
+
+ ArrayList_Lock(g_NamedPipeServerSockets);
+ WINPR_HANDLE_SET_TYPE_AND_MODE(pNamedPipe, HANDLE_TYPE_NAMED_PIPE, WINPR_FD_READ);
+ pNamedPipe->serverfd = -1;
+ pNamedPipe->clientfd = -1;
+
+ if (!(pNamedPipe->name = _strdup(lpName)))
+ goto out;
+
+ if (!(pNamedPipe->lpFileName = GetNamedPipeNameWithoutPrefixA(lpName)))
+ goto out;
+
+ if (!(pNamedPipe->lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpName)))
+ goto out;
+
+ pNamedPipe->dwOpenMode = dwOpenMode;
+ pNamedPipe->dwPipeMode = dwPipeMode;
+ pNamedPipe->nMaxInstances = nMaxInstances;
+ pNamedPipe->nOutBufferSize = nOutBufferSize;
+ pNamedPipe->nInBufferSize = nInBufferSize;
+ pNamedPipe->nDefaultTimeOut = nDefaultTimeOut;
+ pNamedPipe->dwFlagsAndAttributes = dwOpenMode;
+ pNamedPipe->clientfd = -1;
+ pNamedPipe->ServerMode = TRUE;
+ pNamedPipe->common.ops = &namedOps;
+
+ for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++)
+ {
+ baseSocket =
+ (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index);
+
+ if (!strcmp(baseSocket->name, lpName))
+ {
+ serverfd = baseSocket->serverfd;
+ // WLog_DBG(TAG, "using shared socked resource for pipe %p (%s)", (void*) pNamedPipe,
+ // lpName);
+ break;
+ }
+ }
+
+ /* If this is the first instance of the named pipe... */
+ if (serverfd == -1)
+ {
+ struct sockaddr_un s = { 0 };
+ /* Create the UNIX domain socket and start listening. */
+ if (!(lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA()))
+ goto out;
+
+ if (!winpr_PathFileExists(lpPipePath))
+ {
+ if (!CreateDirectoryA(lpPipePath, 0))
+ {
+ free(lpPipePath);
+ goto out;
+ }
+
+ UnixChangeFileMode(lpPipePath, 0xFFFF);
+ }
+
+ free(lpPipePath);
+
+ if (winpr_PathFileExists(pNamedPipe->lpFilePath))
+ winpr_DeleteFile(pNamedPipe->lpFilePath);
+
+ if ((serverfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "CreateNamedPipeA: socket error, %s",
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ goto out;
+ }
+
+ s.sun_family = AF_UNIX;
+ sprintf_s(s.sun_path, ARRAYSIZE(s.sun_path), "%s", pNamedPipe->lpFilePath);
+
+ if (bind(serverfd, (struct sockaddr*)&s, sizeof(struct sockaddr_un)) == -1)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "CreateNamedPipeA: bind error, %s",
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ goto out;
+ }
+
+ if (listen(serverfd, 2) == -1)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "CreateNamedPipeA: listen error, %s",
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ goto out;
+ }
+
+ UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF);
+
+ if (!(baseSocket = (NamedPipeServerSocketEntry*)malloc(sizeof(NamedPipeServerSocketEntry))))
+ goto out;
+
+ if (!(baseSocket->name = _strdup(lpName)))
+ {
+ free(baseSocket);
+ goto out;
+ }
+
+ baseSocket->serverfd = serverfd;
+ baseSocket->references = 0;
+
+ if (!ArrayList_Append(g_NamedPipeServerSockets, baseSocket))
+ {
+ free(baseSocket->name);
+ free(baseSocket);
+ goto out;
+ }
+
+ // WLog_DBG(TAG, "created shared socked resource for pipe %p (%s). base serverfd = %d",
+ // (void*) pNamedPipe, lpName, serverfd);
+ }
+
+ pNamedPipe->serverfd = dup(baseSocket->serverfd);
+ // WLog_DBG(TAG, "using serverfd %d (duplicated from %d)", pNamedPipe->serverfd,
+ // baseSocket->serverfd);
+ pNamedPipe->pfnUnrefNamedPipe = winpr_unref_named_pipe;
+ baseSocket->references++;
+
+ if (dwOpenMode & FILE_FLAG_OVERLAPPED)
+ {
+#if 0
+ int flags = fcntl(pNamedPipe->serverfd, F_GETFL);
+
+ if (flags != -1)
+ fcntl(pNamedPipe->serverfd, F_SETFL, flags | O_NONBLOCK);
+
+#endif
+ }
+
+ // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append takes ownership of baseSocket
+ ArrayList_Unlock(g_NamedPipeServerSockets);
+ return pNamedPipe;
+out:
+ NamedPipeCloseHandle(pNamedPipe);
+
+ if (serverfd != -1)
+ close(serverfd);
+
+ ArrayList_Unlock(g_NamedPipeServerSockets);
+ return INVALID_HANDLE_VALUE;
+}
+
+HANDLE CreateNamedPipeW(LPCWSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
+ DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+{
+ WLog_ERR(TAG, "is not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return NULL;
+}
+
+BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped)
+{
+ int status = 0;
+ socklen_t length = 0;
+ WINPR_NAMED_PIPE* pNamedPipe = NULL;
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ if (!hNamedPipe)
+ return FALSE;
+
+ pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
+
+ if (!(pNamedPipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
+ {
+ struct sockaddr_un s = { 0 };
+ length = sizeof(struct sockaddr_un);
+ status = accept(pNamedPipe->serverfd, (struct sockaddr*)&s, &length);
+
+ if (status < 0)
+ {
+ WLog_ERR(TAG, "ConnectNamedPipe: accept error");
+ return FALSE;
+ }
+
+ pNamedPipe->clientfd = status;
+ pNamedPipe->ServerMode = FALSE;
+ }
+ else
+ {
+ if (!lpOverlapped)
+ return FALSE;
+
+ if (pNamedPipe->serverfd == -1)
+ return FALSE;
+
+ pNamedPipe->lpOverlapped = lpOverlapped;
+ /* synchronous behavior */
+ lpOverlapped->Internal = 2;
+ lpOverlapped->InternalHigh = (ULONG_PTR)0;
+ lpOverlapped->Pointer = (PVOID)NULL;
+ SetEvent(lpOverlapped->hEvent);
+ }
+
+ return TRUE;
+}
+
+BOOL DisconnectNamedPipe(HANDLE hNamedPipe)
+{
+ WINPR_NAMED_PIPE* pNamedPipe = NULL;
+ pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
+
+ if (pNamedPipe->clientfd != -1)
+ {
+ close(pNamedPipe->clientfd);
+ pNamedPipe->clientfd = -1;
+ }
+
+ return TRUE;
+}
+
+BOOL PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesRead,
+ LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage)
+{
+ WLog_ERR(TAG, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL TransactNamedPipe(HANDLE hNamedPipe, LPVOID lpInBuffer, DWORD nInBufferSize,
+ LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead,
+ LPOVERLAPPED lpOverlapped)
+{
+ WLog_ERR(TAG, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL WaitNamedPipeA(LPCSTR lpNamedPipeName, DWORD nTimeOut)
+{
+ BOOL status = 0;
+ DWORD nWaitTime = 0;
+ char* lpFilePath = NULL;
+ DWORD dwSleepInterval = 0;
+
+ if (!lpNamedPipeName)
+ return FALSE;
+
+ lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpNamedPipeName);
+
+ if (!lpFilePath)
+ return FALSE;
+
+ if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT)
+ nTimeOut = 50;
+
+ nWaitTime = 0;
+ status = TRUE;
+ dwSleepInterval = 10;
+
+ while (!winpr_PathFileExists(lpFilePath))
+ {
+ Sleep(dwSleepInterval);
+ nWaitTime += dwSleepInterval;
+
+ if (nWaitTime >= nTimeOut)
+ {
+ status = FALSE;
+ break;
+ }
+ }
+
+ free(lpFilePath);
+ return status;
+}
+
+BOOL WaitNamedPipeW(LPCWSTR lpNamedPipeName, DWORD nTimeOut)
+{
+ WLog_ERR(TAG, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount,
+ LPDWORD lpCollectDataTimeout)
+{
+ int fd = 0;
+ int flags = 0;
+ WINPR_NAMED_PIPE* pNamedPipe = NULL;
+ pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
+
+ if (lpMode)
+ {
+ pNamedPipe->dwPipeMode = *lpMode;
+ fd = (pNamedPipe->ServerMode) ? pNamedPipe->serverfd : pNamedPipe->clientfd;
+
+ if (fd == -1)
+ return FALSE;
+
+ flags = fcntl(fd, F_GETFL);
+
+ if (flags < 0)
+ return FALSE;
+
+ if (pNamedPipe->dwPipeMode & PIPE_NOWAIT)
+ flags = (flags | O_NONBLOCK);
+ else
+ flags = (flags & ~(O_NONBLOCK));
+
+ if (fcntl(fd, F_SETFL, flags) < 0)
+ return FALSE;
+ }
+
+ if (lpMaxCollectionCount)
+ {
+ }
+
+ if (lpCollectDataTimeout)
+ {
+ }
+
+ return TRUE;
+}
+
+BOOL ImpersonateNamedPipeClient(HANDLE hNamedPipe)
+{
+ WLog_ERR(TAG, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL GetNamedPipeClientComputerNameA(HANDLE Pipe, LPCSTR ClientComputerName,
+ ULONG ClientComputerNameLength)
+{
+ WLog_ERR(TAG, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL GetNamedPipeClientComputerNameW(HANDLE Pipe, LPCWSTR ClientComputerName,
+ ULONG ClientComputerNameLength)
+{
+ WLog_ERR(TAG, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+#endif
diff --git a/winpr/libwinpr/pipe/pipe.h b/winpr/libwinpr/pipe/pipe.h
new file mode 100644
index 0000000..192de06
--- /dev/null
+++ b/winpr/libwinpr/pipe/pipe.h
@@ -0,0 +1,75 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Pipe Functions
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WINPR_PIPE_PRIVATE_H
+#define WINPR_PIPE_PRIVATE_H
+
+#ifndef _WIN32
+
+#include <winpr/pipe.h>
+#include <winpr/collections.h>
+
+#include "../handle/handle.h"
+
+struct winpr_pipe
+{
+ WINPR_HANDLE common;
+
+ int fd;
+};
+typedef struct winpr_pipe WINPR_PIPE;
+
+typedef struct winpr_named_pipe WINPR_NAMED_PIPE;
+
+typedef void (*fnUnrefNamedPipe)(WINPR_NAMED_PIPE* pNamedPipe);
+
+struct winpr_named_pipe
+{
+ WINPR_HANDLE common;
+
+ int clientfd;
+ int serverfd;
+
+ char* name;
+ char* lpFileName;
+ char* lpFilePath;
+
+ BOOL ServerMode;
+ DWORD dwOpenMode;
+ DWORD dwPipeMode;
+ DWORD nMaxInstances;
+ DWORD nOutBufferSize;
+ DWORD nInBufferSize;
+ DWORD nDefaultTimeOut;
+ DWORD dwFlagsAndAttributes;
+ LPOVERLAPPED lpOverlapped;
+
+ fnUnrefNamedPipe pfnUnrefNamedPipe;
+};
+
+BOOL winpr_destroy_named_pipe(WINPR_NAMED_PIPE* pNamedPipe);
+
+BOOL NamedPipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
+BOOL NamedPipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
+
+#endif
+
+#endif /* WINPR_PIPE_PRIVATE_H */
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;
+}