diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/pipe | |
parent | Initial commit. (diff) | |
download | freerdp3-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.txt | 22 | ||||
-rw-r--r-- | winpr/libwinpr/pipe/ModuleOptions.cmake | 9 | ||||
-rw-r--r-- | winpr/libwinpr/pipe/pipe.c | 930 | ||||
-rw-r--r-- | winpr/libwinpr/pipe/pipe.h | 75 | ||||
-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 |
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; +} |