Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
22
third_party/FreeRDP/winpr/libwinpr/pipe/CMakeLists.txt
vendored
Normal file
22
third_party/FreeRDP/winpr/libwinpr/pipe/CMakeLists.txt
vendored
Normal file
@@ -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_INTERNAL OR BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
9
third_party/FreeRDP/winpr/libwinpr/pipe/ModuleOptions.cmake
vendored
Normal file
9
third_party/FreeRDP/winpr/libwinpr/pipe/ModuleOptions.cmake
vendored
Normal file
@@ -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}"
|
||||
)
|
||||
940
third_party/FreeRDP/winpr/libwinpr/pipe/pipe.c
vendored
Normal file
940
third_party/FreeRDP/winpr/libwinpr/pipe/pipe.c
vendored
Normal file
@@ -0,0 +1,940 @@
|
||||
/**
|
||||
* 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 = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
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;
|
||||
default:
|
||||
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 = nullptr;
|
||||
|
||||
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,
|
||||
nullptr, /* CleanupHandle */
|
||||
PipeRead,
|
||||
nullptr, /* FileReadEx */
|
||||
nullptr, /* FileReadScatter */
|
||||
PipeWrite,
|
||||
nullptr, /* FileWriteEx */
|
||||
nullptr, /* FileWriteGather */
|
||||
nullptr, /* FileGetFileSize */
|
||||
nullptr, /* FlushFileBuffers */
|
||||
nullptr, /* FileSetEndOfFile */
|
||||
nullptr, /* FileSetFilePointer */
|
||||
nullptr, /* SetFilePointerEx */
|
||||
nullptr, /* FileLockFile */
|
||||
nullptr, /* FileLockFileEx */
|
||||
nullptr, /* FileUnlockFile */
|
||||
nullptr, /* FileUnlockFileEx */
|
||||
nullptr /* SetFileTime */
|
||||
,
|
||||
nullptr };
|
||||
|
||||
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;
|
||||
BOOL status = TRUE;
|
||||
|
||||
WINPR_NAMED_PIPE* 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)
|
||||
{
|
||||
WLog_ERR(TAG, "requires lpOverlapped != nullptr as FILE_FLAG_OVERLAPPED is set");
|
||||
SetLastError(ERROR_NOT_SUPPORTED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pipe->clientfd == -1)
|
||||
return FALSE;
|
||||
|
||||
pipe->lpOverlapped = lpOverlapped;
|
||||
#ifdef WINPR_HAVE_SYS_AIO_H
|
||||
{
|
||||
int aio_status;
|
||||
struct aiocb cb = WINPR_C_ARRAY_INIT;
|
||||
|
||||
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->DUMMYUNIONNAME.Pointer = (PVOID)lpBuffer;
|
||||
(void)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 = nullptr;
|
||||
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 = WINPR_C_ARRAY_INIT;
|
||||
|
||||
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->DUMMYUNIONNAME.Pointer = cnv.pv;
|
||||
}
|
||||
(void)SetEvent(lpOverlapped->hEvent);
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HANDLE_OPS namedOps = { NamedPipeIsHandled,
|
||||
NamedPipeCloseHandle,
|
||||
NamedPipeGetFd,
|
||||
nullptr, /* CleanupHandle */
|
||||
NamedPipeRead,
|
||||
nullptr,
|
||||
nullptr,
|
||||
NamedPipeWrite,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr };
|
||||
|
||||
static BOOL InitWinPRPipeModule(void)
|
||||
{
|
||||
if (g_NamedPipeServerSockets)
|
||||
return TRUE;
|
||||
|
||||
g_NamedPipeServerSockets = ArrayList_New(FALSE);
|
||||
return g_NamedPipeServerSockets != nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unnamed pipe
|
||||
*/
|
||||
|
||||
BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes,
|
||||
DWORD nSize)
|
||||
{
|
||||
int pipe_fd[] = { -1, -1 };
|
||||
WINPR_PIPE* pReadPipe = nullptr;
|
||||
WINPR_PIPE* pWritePipe = nullptr;
|
||||
|
||||
WINPR_UNUSED(lpPipeAttributes);
|
||||
WINPR_UNUSED(nSize);
|
||||
|
||||
if (pipe(pipe_fd) < 0)
|
||||
{
|
||||
if (pipe_fd[0] >= 0)
|
||||
close(pipe_fd[0]);
|
||||
if (pipe_fd[1] >= 0)
|
||||
close(pipe_fd[1]);
|
||||
|
||||
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)
|
||||
{
|
||||
if (pipe_fd[0] >= 0)
|
||||
close(pipe_fd[0]);
|
||||
if (pipe_fd[1] >= 0)
|
||||
close(pipe_fd[1]);
|
||||
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 = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
WINPR_NAMED_PIPE* pNamedPipe = nullptr;
|
||||
int serverfd = -1;
|
||||
NamedPipeServerSocketEntry* baseSocket = nullptr;
|
||||
|
||||
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 = WINPR_C_ARRAY_INIT;
|
||||
/* Create the UNIX domain socket and start listening. */
|
||||
if (!(lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA()))
|
||||
goto out;
|
||||
|
||||
if (!winpr_PathFileExists(lpPipePath))
|
||||
{
|
||||
if (!CreateDirectoryA(lpPipePath, nullptr))
|
||||
{
|
||||
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] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "CreateNamedPipeA: socket error, %s",
|
||||
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
s.sun_family = AF_UNIX;
|
||||
(void)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] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "CreateNamedPipeA: bind error, %s",
|
||||
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (listen(serverfd, 2) == -1)
|
||||
{
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
||||
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)
|
||||
{
|
||||
// TODO: Implement
|
||||
WLog_ERR(TAG, "TODO: implement this");
|
||||
}
|
||||
|
||||
// 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(WINPR_ATTR_UNUSED LPCWSTR lpName, WINPR_ATTR_UNUSED DWORD dwOpenMode,
|
||||
WINPR_ATTR_UNUSED DWORD dwPipeMode, WINPR_ATTR_UNUSED DWORD nMaxInstances,
|
||||
WINPR_ATTR_UNUSED DWORD nOutBufferSize,
|
||||
WINPR_ATTR_UNUSED DWORD nInBufferSize,
|
||||
WINPR_ATTR_UNUSED DWORD nDefaultTimeOut,
|
||||
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpSecurityAttributes)
|
||||
{
|
||||
WLog_ERR(TAG, "is not implemented");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped)
|
||||
{
|
||||
int status = 0;
|
||||
socklen_t length = 0;
|
||||
WINPR_NAMED_PIPE* pNamedPipe = nullptr;
|
||||
|
||||
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 = WINPR_C_ARRAY_INIT;
|
||||
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->DUMMYUNIONNAME.Pointer = (PVOID) nullptr;
|
||||
(void)SetEvent(lpOverlapped->hEvent);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL DisconnectNamedPipe(HANDLE hNamedPipe)
|
||||
{
|
||||
WINPR_NAMED_PIPE* pNamedPipe = nullptr;
|
||||
pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
|
||||
|
||||
if (pNamedPipe->clientfd != -1)
|
||||
{
|
||||
close(pNamedPipe->clientfd);
|
||||
pNamedPipe->clientfd = -1;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL PeekNamedPipe(WINPR_ATTR_UNUSED HANDLE hNamedPipe, WINPR_ATTR_UNUSED LPVOID lpBuffer,
|
||||
WINPR_ATTR_UNUSED DWORD nBufferSize, WINPR_ATTR_UNUSED LPDWORD lpBytesRead,
|
||||
WINPR_ATTR_UNUSED LPDWORD lpTotalBytesAvail,
|
||||
WINPR_ATTR_UNUSED LPDWORD lpBytesLeftThisMessage)
|
||||
{
|
||||
WLog_ERR(TAG, "Not implemented");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL TransactNamedPipe(WINPR_ATTR_UNUSED HANDLE hNamedPipe, WINPR_ATTR_UNUSED LPVOID lpInBuffer,
|
||||
WINPR_ATTR_UNUSED DWORD nInBufferSize, WINPR_ATTR_UNUSED LPVOID lpOutBuffer,
|
||||
WINPR_ATTR_UNUSED DWORD nOutBufferSize,
|
||||
WINPR_ATTR_UNUSED LPDWORD lpBytesRead,
|
||||
WINPR_ATTR_UNUSED 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 = nullptr;
|
||||
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(WINPR_ATTR_UNUSED LPCWSTR lpNamedPipeName, WINPR_ATTR_UNUSED DWORD nTimeOut)
|
||||
{
|
||||
WLog_ERR(TAG, "Not implemented");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(readability-non-const-parameter)
|
||||
BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount,
|
||||
LPDWORD lpCollectDataTimeout)
|
||||
// NOLINTEND(readability-non-const-parameter)
|
||||
{
|
||||
int fd = 0;
|
||||
int flags = 0;
|
||||
WINPR_NAMED_PIPE* pNamedPipe = nullptr;
|
||||
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(WINPR_ATTR_UNUSED HANDLE hNamedPipe)
|
||||
{
|
||||
WLog_ERR(TAG, "Not implemented");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL GetNamedPipeClientComputerNameA(WINPR_ATTR_UNUSED HANDLE Pipe,
|
||||
WINPR_ATTR_UNUSED LPCSTR ClientComputerName,
|
||||
WINPR_ATTR_UNUSED ULONG ClientComputerNameLength)
|
||||
{
|
||||
WLog_ERR(TAG, "Not implemented");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL GetNamedPipeClientComputerNameW(WINPR_ATTR_UNUSED HANDLE Pipe,
|
||||
WINPR_ATTR_UNUSED LPCWSTR ClientComputerName,
|
||||
WINPR_ATTR_UNUSED ULONG ClientComputerNameLength)
|
||||
{
|
||||
WLog_ERR(TAG, "Not implemented");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif
|
||||
75
third_party/FreeRDP/winpr/libwinpr/pipe/pipe.h
vendored
Normal file
75
third_party/FreeRDP/winpr/libwinpr/pipe/pipe.h
vendored
Normal file
@@ -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 */
|
||||
23
third_party/FreeRDP/winpr/libwinpr/pipe/test/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/winpr/libwinpr/pipe/test/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
set(MODULE_NAME "TestPipe")
|
||||
set(MODULE_PREFIX "TEST_PIPE")
|
||||
|
||||
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
|
||||
set(${MODULE_PREFIX}_TESTS TestPipeCreatePipe.c TestPipeCreateNamedPipe.c TestPipeCreateNamedPipeOverlapped.c)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
|
||||
|
||||
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr)
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
|
||||
510
third_party/FreeRDP/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c
vendored
Normal file
510
third_party/FreeRDP/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c
vendored
Normal file
@@ -0,0 +1,510 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/pipe.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/winpr.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/thread.h>
|
||||
#ifndef _WIN32
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include "../pipe.h"
|
||||
|
||||
#define PIPE_BUFFER_SIZE 32
|
||||
|
||||
static HANDLE ReadyEvent;
|
||||
|
||||
static LPTSTR lpszPipeNameMt = _T("\\\\.\\pipe\\winpr_test_pipe_mt");
|
||||
static LPTSTR lpszPipeNameSt = _T("\\\\.\\pipe\\winpr_test_pipe_st");
|
||||
|
||||
static BOOL testFailed = FALSE;
|
||||
|
||||
static DWORD WINAPI named_pipe_client_thread(LPVOID arg)
|
||||
{
|
||||
HANDLE hNamedPipe = nullptr;
|
||||
BYTE* lpReadBuffer = nullptr;
|
||||
BYTE* lpWriteBuffer = nullptr;
|
||||
BOOL fSuccess = FALSE;
|
||||
DWORD nNumberOfBytesToRead = 0;
|
||||
DWORD nNumberOfBytesToWrite = 0;
|
||||
DWORD lpNumberOfBytesRead = 0;
|
||||
DWORD lpNumberOfBytesWritten = 0;
|
||||
(void)WaitForSingleObject(ReadyEvent, INFINITE);
|
||||
hNamedPipe = CreateFile(lpszPipeNameMt, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
|
||||
0, nullptr);
|
||||
|
||||
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("%s: Named Pipe CreateFile failure: INVALID_HANDLE_VALUE\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(lpReadBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE)))
|
||||
{
|
||||
printf("%s: Error allocating read buffer\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(lpWriteBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE)))
|
||||
{
|
||||
printf("%s: Error allocating write buffer\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
lpNumberOfBytesWritten = 0;
|
||||
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
|
||||
FillMemory(lpWriteBuffer, PIPE_BUFFER_SIZE, 0x59);
|
||||
|
||||
if (!WriteFile(hNamedPipe, lpWriteBuffer, nNumberOfBytesToWrite, &lpNumberOfBytesWritten,
|
||||
nullptr) ||
|
||||
lpNumberOfBytesWritten != nNumberOfBytesToWrite)
|
||||
{
|
||||
printf("%s: Client NamedPipe WriteFile failure\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
lpNumberOfBytesRead = 0;
|
||||
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
|
||||
ZeroMemory(lpReadBuffer, PIPE_BUFFER_SIZE);
|
||||
|
||||
if (!ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, &lpNumberOfBytesRead, nullptr) ||
|
||||
lpNumberOfBytesRead != nNumberOfBytesToRead)
|
||||
{
|
||||
printf("%s: Client NamedPipe ReadFile failure\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Client ReadFile: %" PRIu32 " bytes\n", lpNumberOfBytesRead);
|
||||
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, lpNumberOfBytesRead);
|
||||
fSuccess = TRUE;
|
||||
out:
|
||||
free(lpReadBuffer);
|
||||
free(lpWriteBuffer);
|
||||
(void)CloseHandle(hNamedPipe);
|
||||
|
||||
if (!fSuccess)
|
||||
testFailed = TRUE;
|
||||
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI named_pipe_server_thread(LPVOID arg)
|
||||
{
|
||||
HANDLE hNamedPipe = nullptr;
|
||||
BYTE* lpReadBuffer = nullptr;
|
||||
BYTE* lpWriteBuffer = nullptr;
|
||||
BOOL fSuccess = FALSE;
|
||||
BOOL fConnected = FALSE;
|
||||
DWORD nNumberOfBytesToRead = 0;
|
||||
DWORD nNumberOfBytesToWrite = 0;
|
||||
DWORD lpNumberOfBytesRead = 0;
|
||||
DWORD lpNumberOfBytesWritten = 0;
|
||||
hNamedPipe = CreateNamedPipe(
|
||||
lpszPipeNameMt, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, nullptr);
|
||||
|
||||
if (!hNamedPipe)
|
||||
{
|
||||
printf("%s: CreateNamedPipe failure: nullptr handle\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("%s: CreateNamedPipe failure: INVALID_HANDLE_VALUE\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
(void)SetEvent(ReadyEvent);
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* If a client connects before ConnectNamedPipe is called, the function returns zero and
|
||||
* GetLastError returns ERROR_PIPE_CONNECTED. This can happen if a client connects in the
|
||||
* interval between the call to CreateNamedPipe and the call to ConnectNamedPipe.
|
||||
* In this situation, there is a good connection between client and server, even though
|
||||
* the function returns zero.
|
||||
*/
|
||||
fConnected =
|
||||
ConnectNamedPipe(hNamedPipe, nullptr) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||
|
||||
if (!fConnected)
|
||||
{
|
||||
printf("%s: ConnectNamedPipe failure\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE)))
|
||||
{
|
||||
printf("%s: Error allocating read buffer\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(lpWriteBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE)))
|
||||
{
|
||||
printf("%s: Error allocating write buffer\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
lpNumberOfBytesRead = 0;
|
||||
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
|
||||
|
||||
if (!ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, &lpNumberOfBytesRead, nullptr) ||
|
||||
lpNumberOfBytesRead != nNumberOfBytesToRead)
|
||||
{
|
||||
printf("%s: Server NamedPipe ReadFile failure\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Server ReadFile: %" PRIu32 " bytes\n", lpNumberOfBytesRead);
|
||||
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, lpNumberOfBytesRead);
|
||||
lpNumberOfBytesWritten = 0;
|
||||
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
|
||||
FillMemory(lpWriteBuffer, PIPE_BUFFER_SIZE, 0x45);
|
||||
|
||||
if (!WriteFile(hNamedPipe, lpWriteBuffer, nNumberOfBytesToWrite, &lpNumberOfBytesWritten,
|
||||
nullptr) ||
|
||||
lpNumberOfBytesWritten != nNumberOfBytesToWrite)
|
||||
{
|
||||
printf("%s: Server NamedPipe WriteFile failure\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fSuccess = TRUE;
|
||||
out:
|
||||
free(lpReadBuffer);
|
||||
free(lpWriteBuffer);
|
||||
(void)CloseHandle(hNamedPipe);
|
||||
|
||||
if (!fSuccess)
|
||||
testFailed = TRUE;
|
||||
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TESTNUMPIPESST 16
|
||||
static DWORD WINAPI named_pipe_single_thread(LPVOID arg)
|
||||
{
|
||||
HANDLE servers[TESTNUMPIPESST] = WINPR_C_ARRAY_INIT;
|
||||
HANDLE clients[TESTNUMPIPESST] = WINPR_C_ARRAY_INIT;
|
||||
DWORD dwRead = 0;
|
||||
DWORD dwWritten = 0;
|
||||
int numPipes = 0;
|
||||
BOOL bSuccess = FALSE;
|
||||
numPipes = TESTNUMPIPESST;
|
||||
(void)WaitForSingleObject(ReadyEvent, INFINITE);
|
||||
|
||||
for (int i = 0; i < numPipes; i++)
|
||||
{
|
||||
if (!(servers[i] = CreateNamedPipe(lpszPipeNameSt, PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE,
|
||||
PIPE_BUFFER_SIZE, 0, nullptr)))
|
||||
{
|
||||
printf("%s: CreateNamedPipe #%d failed\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
for (int i = 0; i < numPipes; i++)
|
||||
{
|
||||
WINPR_NAMED_PIPE* p = (WINPR_NAMED_PIPE*)servers[i];
|
||||
|
||||
if (strcmp(lpszPipeNameSt, p->name) != 0)
|
||||
{
|
||||
printf("%s: Pipe name mismatch for pipe #%d ([%s] instead of [%s])\n", __func__, i,
|
||||
p->name, lpszPipeNameSt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p->clientfd != -1)
|
||||
{
|
||||
printf("%s: Unexpected client fd value for pipe #%d (%d instead of -1)\n", __func__, i,
|
||||
p->clientfd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p->serverfd < 1)
|
||||
{
|
||||
printf("%s: Unexpected server fd value for pipe #%d (%d is not > 0)\n", __func__, i,
|
||||
p->serverfd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p->ServerMode == FALSE)
|
||||
{
|
||||
printf("%s: Unexpected ServerMode value for pipe #%d (0 instead of 1)\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < numPipes; i++)
|
||||
{
|
||||
BOOL fConnected = 0;
|
||||
if ((clients[i] = CreateFile(lpszPipeNameSt, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
|
||||
OPEN_EXISTING, 0, nullptr)) == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("%s: CreateFile #%d failed\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* If a client connects before ConnectNamedPipe is called, the function returns zero and
|
||||
* GetLastError returns ERROR_PIPE_CONNECTED. This can happen if a client connects in the
|
||||
* interval between the call to CreateNamedPipe and the call to ConnectNamedPipe.
|
||||
* In this situation, there is a good connection between client and server, even though
|
||||
* the function returns zero.
|
||||
*/
|
||||
fConnected =
|
||||
ConnectNamedPipe(servers[i], nullptr) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||
|
||||
if (!fConnected)
|
||||
{
|
||||
printf("%s: ConnectNamedPipe #%d failed. (%" PRIu32 ")\n", __func__, i, GetLastError());
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
for (int i = 0; i < numPipes; i++)
|
||||
{
|
||||
WINPR_NAMED_PIPE* p = servers[i];
|
||||
|
||||
if (p->clientfd < 1)
|
||||
{
|
||||
printf("%s: Unexpected client fd value for pipe #%d (%d is not > 0)\n", __func__, i,
|
||||
p->clientfd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p->ServerMode)
|
||||
{
|
||||
printf("%s: Unexpected ServerMode value for pipe #%d (1 instead of 0)\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < numPipes; i++)
|
||||
{
|
||||
{
|
||||
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
/* Test writing from clients to servers */
|
||||
(void)sprintf_s(sndbuf, sizeof(sndbuf), "CLIENT->SERVER ON PIPE #%05d", i);
|
||||
|
||||
if (!WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr) ||
|
||||
dwWritten != sizeof(sndbuf))
|
||||
{
|
||||
printf("%s: Error writing to client end of pipe #%d\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ReadFile(servers[i], rcvbuf, dwWritten, &dwRead, nullptr) || dwRead != dwWritten)
|
||||
{
|
||||
printf("%s: Error reading on server end of pipe #%d\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memcmp(sndbuf, rcvbuf, sizeof(sndbuf)) != 0)
|
||||
{
|
||||
printf("%s: Error data read on server end of pipe #%d is corrupted\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
{
|
||||
|
||||
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
/* Test writing from servers to clients */
|
||||
|
||||
(void)sprintf_s(sndbuf, sizeof(sndbuf), "SERVER->CLIENT ON PIPE #%05d", i);
|
||||
|
||||
if (!WriteFile(servers[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr) ||
|
||||
dwWritten != sizeof(sndbuf))
|
||||
{
|
||||
printf("%s: Error writing to server end of pipe #%d\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ReadFile(clients[i], rcvbuf, dwWritten, &dwRead, nullptr) || dwRead != dwWritten)
|
||||
{
|
||||
printf("%s: Error reading on client end of pipe #%d\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memcmp(sndbuf, rcvbuf, sizeof(sndbuf)) != 0)
|
||||
{
|
||||
printf("%s: Error data read on client end of pipe #%d is corrupted\n", __func__, i);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
/**
|
||||
* After DisconnectNamedPipe on server end
|
||||
* ReadFile/WriteFile must fail on client end
|
||||
*/
|
||||
int i = numPipes - 1;
|
||||
DisconnectNamedPipe(servers[i]);
|
||||
{
|
||||
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
if (ReadFile(clients[i], rcvbuf, sizeof(rcvbuf), &dwRead, nullptr))
|
||||
{
|
||||
printf("%s: Error ReadFile on client should have failed after DisconnectNamedPipe on "
|
||||
"server\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr))
|
||||
{
|
||||
printf(
|
||||
"%s: Error WriteFile on client end should have failed after DisconnectNamedPipe on "
|
||||
"server\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
(void)CloseHandle(servers[i]);
|
||||
(void)CloseHandle(clients[i]);
|
||||
numPipes--;
|
||||
/**
|
||||
* After CloseHandle (without calling DisconnectNamedPipe first) on server end
|
||||
* ReadFile/WriteFile must fail on client end
|
||||
*/
|
||||
i = numPipes - 1;
|
||||
(void)CloseHandle(servers[i]);
|
||||
|
||||
{
|
||||
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (ReadFile(clients[i], rcvbuf, sizeof(rcvbuf), &dwRead, nullptr))
|
||||
{
|
||||
printf(
|
||||
"%s: Error ReadFile on client end should have failed after CloseHandle on server\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr))
|
||||
{
|
||||
printf("%s: Error WriteFile on client end should have failed after CloseHandle on "
|
||||
"server\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
(void)CloseHandle(clients[i]);
|
||||
numPipes--;
|
||||
/**
|
||||
* After CloseHandle on client end
|
||||
* ReadFile/WriteFile must fail on server end
|
||||
*/
|
||||
i = numPipes - 1;
|
||||
(void)CloseHandle(clients[i]);
|
||||
|
||||
{
|
||||
char sndbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
char rcvbuf[PIPE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (ReadFile(servers[i], rcvbuf, sizeof(rcvbuf), &dwRead, nullptr))
|
||||
{
|
||||
printf(
|
||||
"%s: Error ReadFile on server end should have failed after CloseHandle on client\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WriteFile(servers[i], sndbuf, sizeof(sndbuf), &dwWritten, nullptr))
|
||||
{
|
||||
printf("%s: Error WriteFile on server end should have failed after CloseHandle on "
|
||||
"client\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
DisconnectNamedPipe(servers[i]);
|
||||
(void)CloseHandle(servers[i]);
|
||||
numPipes--;
|
||||
|
||||
/* Close all remaining pipes */
|
||||
for (int i = 0; i < numPipes; i++)
|
||||
{
|
||||
DisconnectNamedPipe(servers[i]);
|
||||
(void)CloseHandle(servers[i]);
|
||||
(void)CloseHandle(clients[i]);
|
||||
}
|
||||
|
||||
bSuccess = TRUE;
|
||||
out:
|
||||
|
||||
if (!bSuccess)
|
||||
testFailed = TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestPipeCreateNamedPipe(int argc, char* argv[])
|
||||
{
|
||||
HANDLE SingleThread = nullptr;
|
||||
HANDLE ClientThread = nullptr;
|
||||
HANDLE ServerThread = nullptr;
|
||||
HANDLE hPipe = nullptr;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
/* Verify that CreateNamedPipe returns INVALID_HANDLE_VALUE on failure */
|
||||
hPipe = CreateNamedPipeA(nullptr, 0, 0, 0, 0, 0, 0, nullptr);
|
||||
if (hPipe != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("CreateNamedPipe unexpectedly returned %p instead of INVALID_HANDLE_VALUE (%p)\n",
|
||||
hPipe, INVALID_HANDLE_VALUE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
(void)signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
if (!(ReadyEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("CreateEvent failure: (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
if (!(SingleThread = CreateThread(nullptr, 0, named_pipe_single_thread, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CreateThread (SingleThread) failure: (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
if (!(ClientThread = CreateThread(nullptr, 0, named_pipe_client_thread, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CreateThread (ClientThread) failure: (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
if (!(ServerThread = CreateThread(nullptr, 0, named_pipe_server_thread, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CreateThread (ServerThread) failure: (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
(void)WaitForSingleObject(SingleThread, INFINITE);
|
||||
(void)WaitForSingleObject(ClientThread, INFINITE);
|
||||
(void)WaitForSingleObject(ServerThread, INFINITE);
|
||||
(void)CloseHandle(SingleThread);
|
||||
(void)CloseHandle(ClientThread);
|
||||
(void)CloseHandle(ServerThread);
|
||||
return testFailed;
|
||||
}
|
||||
392
third_party/FreeRDP/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c
vendored
Normal file
392
third_party/FreeRDP/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c
vendored
Normal file
@@ -0,0 +1,392 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/pipe.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/winpr.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#define PIPE_BUFFER_SIZE 32
|
||||
#define PIPE_TIMEOUT_MS 20000 // 20 seconds
|
||||
|
||||
static BYTE SERVER_MESSAGE[PIPE_BUFFER_SIZE];
|
||||
static BYTE CLIENT_MESSAGE[PIPE_BUFFER_SIZE];
|
||||
|
||||
static BOOL bClientSuccess = FALSE;
|
||||
static BOOL bServerSuccess = FALSE;
|
||||
|
||||
static HANDLE serverReadyEvent = nullptr;
|
||||
|
||||
static LPTSTR lpszPipeName = _T("\\\\.\\pipe\\winpr_test_pipe_overlapped");
|
||||
|
||||
static DWORD WINAPI named_pipe_client_thread(LPVOID arg)
|
||||
{
|
||||
DWORD status = 0;
|
||||
HANDLE hEvent = nullptr;
|
||||
HANDLE hNamedPipe = nullptr;
|
||||
BYTE* lpReadBuffer = nullptr;
|
||||
BOOL fSuccess = FALSE;
|
||||
OVERLAPPED overlapped = WINPR_C_ARRAY_INIT;
|
||||
DWORD nNumberOfBytesToRead = 0;
|
||||
DWORD nNumberOfBytesToWrite = 0;
|
||||
DWORD NumberOfBytesTransferred = 0;
|
||||
|
||||
WINPR_UNUSED(arg);
|
||||
|
||||
status = WaitForSingleObject(serverReadyEvent, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("client: failed to wait for server ready event: %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* 1: initialize overlapped structure */
|
||||
if (!(hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("client: CreateEvent failure: %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
overlapped.hEvent = hEvent;
|
||||
|
||||
/* 2: connect to server named pipe */
|
||||
|
||||
hNamedPipe = CreateFile(lpszPipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED, nullptr);
|
||||
|
||||
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("client: Named Pipe CreateFile failure: %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* 3: write to named pipe */
|
||||
|
||||
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
|
||||
NumberOfBytesTransferred = 0;
|
||||
|
||||
fSuccess = WriteFile(hNamedPipe, CLIENT_MESSAGE, nNumberOfBytesToWrite, nullptr, &overlapped);
|
||||
|
||||
if (!fSuccess)
|
||||
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("client: NamedPipe WriteFile failure (initial): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("client: failed to wait for overlapped event (write): %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("client: NamedPipe WriteFile failure (final): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
printf("client: WriteFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred);
|
||||
|
||||
/* 4: read from named pipe */
|
||||
|
||||
if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE)))
|
||||
{
|
||||
printf("client: Error allocating read buffer\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
|
||||
NumberOfBytesTransferred = 0;
|
||||
|
||||
fSuccess = ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, nullptr, &overlapped);
|
||||
|
||||
if (!fSuccess)
|
||||
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("client: NamedPipe ReadFile failure (initial): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = WaitForMultipleObjects(1, &hEvent, FALSE, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("client: failed to wait for overlapped event (read): %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, TRUE);
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("client: NamedPipe ReadFile failure (final): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("client: ReadFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred);
|
||||
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, NumberOfBytesTransferred);
|
||||
|
||||
if (NumberOfBytesTransferred != PIPE_BUFFER_SIZE ||
|
||||
memcmp(lpReadBuffer, SERVER_MESSAGE, PIPE_BUFFER_SIZE) != 0)
|
||||
{
|
||||
printf("client: received unexpected data from server\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("client: finished successfully\n");
|
||||
bClientSuccess = TRUE;
|
||||
|
||||
finish:
|
||||
free(lpReadBuffer);
|
||||
if (hNamedPipe)
|
||||
(void)CloseHandle(hNamedPipe);
|
||||
if (hEvent)
|
||||
(void)CloseHandle(hEvent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI named_pipe_server_thread(LPVOID arg)
|
||||
{
|
||||
DWORD status = 0;
|
||||
HANDLE hEvent = nullptr;
|
||||
HANDLE hNamedPipe = nullptr;
|
||||
BYTE* lpReadBuffer = nullptr;
|
||||
OVERLAPPED overlapped = WINPR_C_ARRAY_INIT;
|
||||
BOOL fSuccess = FALSE;
|
||||
BOOL fConnected = FALSE;
|
||||
DWORD nNumberOfBytesToRead = 0;
|
||||
DWORD nNumberOfBytesToWrite = 0;
|
||||
DWORD NumberOfBytesTransferred = 0;
|
||||
|
||||
WINPR_UNUSED(arg);
|
||||
|
||||
/* 1: initialize overlapped structure */
|
||||
if (!(hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("server: CreateEvent failure: %" PRIu32 "\n", GetLastError());
|
||||
(void)SetEvent(serverReadyEvent); /* unblock client thread */
|
||||
goto finish;
|
||||
}
|
||||
overlapped.hEvent = hEvent;
|
||||
|
||||
/* 2: create named pipe and set ready event */
|
||||
|
||||
hNamedPipe =
|
||||
CreateNamedPipe(lpszPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
|
||||
PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, nullptr);
|
||||
|
||||
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("server: CreateNamedPipe failure: %" PRIu32 "\n", GetLastError());
|
||||
(void)SetEvent(serverReadyEvent); /* unblock client thread */
|
||||
goto finish;
|
||||
}
|
||||
|
||||
(void)SetEvent(serverReadyEvent);
|
||||
|
||||
/* 3: connect named pipe */
|
||||
|
||||
fConnected = ConnectNamedPipe(hNamedPipe, &overlapped);
|
||||
status = GetLastError();
|
||||
|
||||
/**
|
||||
* At this point if fConnected is FALSE, we have to check GetLastError() for:
|
||||
* ERROR_PIPE_CONNECTED:
|
||||
* client has already connected before we have called ConnectNamedPipe.
|
||||
* this is quite common depending on the timings and indicates success
|
||||
* ERROR_IO_PENDING:
|
||||
* Since we're using ConnectNamedPipe asynchronously here, the function returns
|
||||
* immediately and this error code simply indicates that the operation is
|
||||
* still in progress. Hence we have to wait for the completion event and use
|
||||
* GetOverlappedResult to query the actual result of the operation (note that
|
||||
* the lpNumberOfBytesTransferred parameter is undefined/useless for a
|
||||
* ConnectNamedPipe operation)
|
||||
*/
|
||||
|
||||
if (!fConnected)
|
||||
fConnected = (status == ERROR_PIPE_CONNECTED);
|
||||
|
||||
printf("server: ConnectNamedPipe status: %" PRIu32 "\n", status);
|
||||
|
||||
if (!fConnected && status == ERROR_IO_PENDING)
|
||||
{
|
||||
DWORD dwDummy = 0;
|
||||
printf("server: waiting up to %u ms for connection ...\n", PIPE_TIMEOUT_MS);
|
||||
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
||||
if (status == WAIT_OBJECT_0)
|
||||
fConnected = GetOverlappedResult(hNamedPipe, &overlapped, &dwDummy, FALSE);
|
||||
else
|
||||
printf("server: failed to wait for overlapped event (connect): %" PRIu32 "\n", status);
|
||||
}
|
||||
|
||||
if (!fConnected)
|
||||
{
|
||||
printf("server: ConnectNamedPipe failed: %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("server: named pipe successfully connected\n");
|
||||
|
||||
/* 4: read from named pipe */
|
||||
|
||||
if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE)))
|
||||
{
|
||||
printf("server: Error allocating read buffer\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
|
||||
NumberOfBytesTransferred = 0;
|
||||
|
||||
fSuccess = ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, nullptr, &overlapped);
|
||||
|
||||
if (!fSuccess)
|
||||
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("server: NamedPipe ReadFile failure (initial): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("server: failed to wait for overlapped event (read): %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("server: NamedPipe ReadFile failure (final): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("server: ReadFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred);
|
||||
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, NumberOfBytesTransferred);
|
||||
|
||||
if (NumberOfBytesTransferred != PIPE_BUFFER_SIZE ||
|
||||
memcmp(lpReadBuffer, CLIENT_MESSAGE, PIPE_BUFFER_SIZE) != 0)
|
||||
{
|
||||
printf("server: received unexpected data from client\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* 5: write to named pipe */
|
||||
|
||||
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
|
||||
NumberOfBytesTransferred = 0;
|
||||
|
||||
fSuccess = WriteFile(hNamedPipe, SERVER_MESSAGE, nNumberOfBytesToWrite, nullptr, &overlapped);
|
||||
|
||||
if (!fSuccess)
|
||||
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("server: NamedPipe WriteFile failure (initial): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("server: failed to wait for overlapped event (write): %" PRIu32 "\n", status);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
||||
if (!fSuccess)
|
||||
{
|
||||
printf("server: NamedPipe WriteFile failure (final): %" PRIu32 "\n", GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("server: WriteFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred);
|
||||
// winpr_HexDump("pipe.test", WLOG_DEBUG, lpWriteBuffer, NumberOfBytesTransferred);
|
||||
|
||||
bServerSuccess = TRUE;
|
||||
printf("server: finished successfully\n");
|
||||
|
||||
finish:
|
||||
(void)CloseHandle(hNamedPipe);
|
||||
(void)CloseHandle(hEvent);
|
||||
free(lpReadBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[])
|
||||
{
|
||||
HANDLE ClientThread = nullptr;
|
||||
HANDLE ServerThread = nullptr;
|
||||
int result = -1;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
FillMemory(SERVER_MESSAGE, PIPE_BUFFER_SIZE, 0xAA);
|
||||
FillMemory(CLIENT_MESSAGE, PIPE_BUFFER_SIZE, 0xBB);
|
||||
|
||||
if (!(serverReadyEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("CreateEvent failed: %" PRIu32 "\n", GetLastError());
|
||||
goto out;
|
||||
}
|
||||
if (!(ClientThread = CreateThread(nullptr, 0, named_pipe_client_thread, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CreateThread (client) failed: %" PRIu32 "\n", GetLastError());
|
||||
goto out;
|
||||
}
|
||||
if (!(ServerThread = CreateThread(nullptr, 0, named_pipe_server_thread, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CreateThread (server) failed: %" PRIu32 "\n", GetLastError());
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WAIT_OBJECT_0 != WaitForSingleObject(ClientThread, INFINITE))
|
||||
{
|
||||
printf("%s: Failed to wait for client thread: %" PRIu32 "\n", __func__, GetLastError());
|
||||
goto out;
|
||||
}
|
||||
if (WAIT_OBJECT_0 != WaitForSingleObject(ServerThread, INFINITE))
|
||||
{
|
||||
printf("%s: Failed to wait for server thread: %" PRIu32 "\n", __func__, GetLastError());
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bClientSuccess && bServerSuccess)
|
||||
result = 0;
|
||||
|
||||
out:
|
||||
|
||||
if (ClientThread)
|
||||
(void)CloseHandle(ClientThread);
|
||||
if (ServerThread)
|
||||
(void)CloseHandle(ServerThread);
|
||||
if (serverReadyEvent)
|
||||
(void)CloseHandle(serverReadyEvent);
|
||||
|
||||
#ifndef _WIN32
|
||||
if (result == 0)
|
||||
{
|
||||
printf("%s: Error, this test is currently expected not to succeed on this platform.\n",
|
||||
__func__);
|
||||
result = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%s: This test is currently expected to fail on this platform.\n", __func__);
|
||||
result = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
72
third_party/FreeRDP/winpr/libwinpr/pipe/test/TestPipeCreatePipe.c
vendored
Normal file
72
third_party/FreeRDP/winpr/libwinpr/pipe/test/TestPipeCreatePipe.c
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/pipe.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/winpr.h>
|
||||
|
||||
#define BUFFER_SIZE 16
|
||||
|
||||
int TestPipeCreatePipe(int argc, char* argv[])
|
||||
{
|
||||
BOOL status = 0;
|
||||
DWORD dwRead = 0;
|
||||
DWORD dwWrite = 0;
|
||||
HANDLE hReadPipe = nullptr;
|
||||
HANDLE hWritePipe = nullptr;
|
||||
BYTE readBuffer[BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
BYTE writeBuffer[BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
status = CreatePipe(&hReadPipe, &hWritePipe, nullptr, BUFFER_SIZE * 2);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
_tprintf(_T("CreatePipe failed\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
FillMemory(writeBuffer, sizeof(writeBuffer), 0xAA);
|
||||
status = WriteFile(hWritePipe, &writeBuffer, sizeof(writeBuffer), &dwWrite, nullptr);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
_tprintf(_T("WriteFile failed\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dwWrite != sizeof(writeBuffer))
|
||||
{
|
||||
_tprintf(_T("WriteFile: unexpected number of bytes written: Actual: %") _T(
|
||||
PRIu32) _T(", Expected: %") _T(PRIuz) _T("\n"),
|
||||
dwWrite, sizeof(writeBuffer));
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = ReadFile(hReadPipe, &readBuffer, sizeof(readBuffer), &dwRead, nullptr);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
_tprintf(_T("ReadFile failed\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dwRead != sizeof(readBuffer))
|
||||
{
|
||||
_tprintf(_T("ReadFile: unexpected number of bytes read: Actual: %") _T(
|
||||
PRIu32) _T(", Expected: %") _T(PRIuz) _T("\n"),
|
||||
dwWrite, sizeof(readBuffer));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (memcmp(readBuffer, writeBuffer, BUFFER_SIZE) != 0)
|
||||
{
|
||||
_tprintf(_T("ReadFile: read buffer is different from write buffer\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)CloseHandle(hReadPipe);
|
||||
(void)CloseHandle(hWritePipe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user