mirror of
https://github.com/decompals/wibo.git
synced 2025-12-13 07:06:18 +00:00
Implement ConnectNamedPipe
This commit is contained in:
@@ -200,6 +200,8 @@ void *resolveByName(const char *name) {
|
|||||||
return (void *)kernel32::CreatePipe;
|
return (void *)kernel32::CreatePipe;
|
||||||
if (strcmp(name, "CreateNamedPipeA") == 0)
|
if (strcmp(name, "CreateNamedPipeA") == 0)
|
||||||
return (void *)kernel32::CreateNamedPipeA;
|
return (void *)kernel32::CreateNamedPipeA;
|
||||||
|
if (strcmp(name, "ConnectNamedPipe") == 0)
|
||||||
|
return (void *)kernel32::ConnectNamedPipe;
|
||||||
|
|
||||||
// winbase.h
|
// winbase.h
|
||||||
if (strcmp(name, "FindAtomA") == 0)
|
if (strcmp(name, "FindAtomA") == 0)
|
||||||
|
|||||||
@@ -5,11 +5,13 @@
|
|||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
#include "overlapped_util.h"
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <condition_variable>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -174,6 +176,10 @@ struct NamedPipeInstance final : FileObject {
|
|||||||
DWORD accessMode;
|
DWORD accessMode;
|
||||||
DWORD pipeMode;
|
DWORD pipeMode;
|
||||||
bool clientConnected = false;
|
bool clientConnected = false;
|
||||||
|
bool connectPending = false;
|
||||||
|
LPOVERLAPPED pendingOverlapped = nullptr;
|
||||||
|
std::mutex connectMutex;
|
||||||
|
std::condition_variable connectCv;
|
||||||
|
|
||||||
NamedPipeInstance(int fd, Pin<NamedPipeState> st, int companion, DWORD open, DWORD mode)
|
NamedPipeInstance(int fd, Pin<NamedPipeState> st, int companion, DWORD open, DWORD mode)
|
||||||
: FileObject(fd), state(std::move(st)), companionFd(companion), accessMode(open), pipeMode(mode) {
|
: FileObject(fd), state(std::move(st)), companionFd(companion), accessMode(open), pipeMode(mode) {
|
||||||
@@ -183,9 +189,22 @@ struct NamedPipeInstance final : FileObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
~NamedPipeInstance() override {
|
~NamedPipeInstance() override {
|
||||||
if (companionFd >= 0) {
|
int localCompanion = -1;
|
||||||
close(companionFd);
|
{
|
||||||
|
std::lock_guard lk(connectMutex);
|
||||||
|
localCompanion = companionFd;
|
||||||
companionFd = -1;
|
companionFd = -1;
|
||||||
|
if (pendingOverlapped) {
|
||||||
|
pendingOverlapped->Internal = STATUS_PIPE_BROKEN;
|
||||||
|
pendingOverlapped->InternalHigh = 0;
|
||||||
|
kernel32::detail::signalOverlappedEvent(pendingOverlapped);
|
||||||
|
pendingOverlapped = nullptr;
|
||||||
|
}
|
||||||
|
connectPending = false;
|
||||||
|
connectCv.notify_all();
|
||||||
|
}
|
||||||
|
if (localCompanion >= 0) {
|
||||||
|
close(localCompanion);
|
||||||
}
|
}
|
||||||
if (state) {
|
if (state) {
|
||||||
state->unregisterInstance(this);
|
state->unregisterInstance(this);
|
||||||
@@ -195,7 +214,8 @@ struct NamedPipeInstance final : FileObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canAcceptClient(DWORD desiredAccess) const {
|
bool canAcceptClient(DWORD desiredAccess) {
|
||||||
|
std::lock_guard lk(connectMutex);
|
||||||
if (companionFd < 0 || clientConnected) {
|
if (companionFd < 0 || clientConnected) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -213,16 +233,29 @@ struct NamedPipeInstance final : FileObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int takeCompanion() {
|
int takeCompanion() {
|
||||||
if (companionFd < 0) {
|
std::unique_lock lk(connectMutex);
|
||||||
|
if (companionFd < 0 || clientConnected) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int fd = companionFd;
|
int fd = companionFd;
|
||||||
companionFd = -1;
|
companionFd = -1;
|
||||||
clientConnected = true;
|
clientConnected = true;
|
||||||
|
if (pendingOverlapped) {
|
||||||
|
pendingOverlapped->Internal = STATUS_SUCCESS;
|
||||||
|
pendingOverlapped->InternalHigh = 0;
|
||||||
|
kernel32::detail::signalOverlappedEvent(pendingOverlapped);
|
||||||
|
pendingOverlapped = nullptr;
|
||||||
|
}
|
||||||
|
if (connectPending) {
|
||||||
|
connectPending = false;
|
||||||
|
connectCv.notify_all();
|
||||||
|
}
|
||||||
|
lk.unlock();
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void restoreCompanion(int fd) {
|
void restoreCompanion(int fd) {
|
||||||
|
std::lock_guard lk(connectMutex);
|
||||||
companionFd = fd;
|
companionFd = fd;
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
clientConnected = false;
|
clientConnected = false;
|
||||||
@@ -502,4 +535,64 @@ HANDLE WIN_FUNC CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMo
|
|||||||
return wibo::handles().alloc(std::move(pipeObj), grantedAccess, handleFlags);
|
return wibo::handles().alloc(std::move(pipeObj), grantedAccess, handleFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL WIN_FUNC ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped) {
|
||||||
|
HOST_CONTEXT_GUARD();
|
||||||
|
DEBUG_LOG("ConnectNamedPipe(%p, %p)\n", hNamedPipe, lpOverlapped);
|
||||||
|
|
||||||
|
auto pin = wibo::handles().get(hNamedPipe);
|
||||||
|
auto *pipe = pin ? dynamic_cast<NamedPipeInstance *>(pin.get()) : nullptr;
|
||||||
|
if (!pipe) {
|
||||||
|
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool isOverlappedHandle = pipe->overlapped;
|
||||||
|
if (isOverlappedHandle && lpOverlapped == nullptr) {
|
||||||
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock lock(pipe->connectMutex);
|
||||||
|
|
||||||
|
if (pipe->clientConnected) {
|
||||||
|
wibo::lastError = ERROR_PIPE_CONNECTED;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipe->companionFd < 0) {
|
||||||
|
wibo::lastError = ERROR_PIPE_BUSY;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipe->connectPending) {
|
||||||
|
wibo::lastError = ERROR_PIPE_LISTENING;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pipe->pipeMode & PIPE_NOWAIT) != 0) {
|
||||||
|
wibo::lastError = ERROR_PIPE_LISTENING;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOverlappedHandle) {
|
||||||
|
pipe->connectPending = true;
|
||||||
|
pipe->pendingOverlapped = lpOverlapped;
|
||||||
|
lpOverlapped->Internal = STATUS_PENDING;
|
||||||
|
lpOverlapped->InternalHigh = 0;
|
||||||
|
kernel32::detail::resetOverlappedEvent(lpOverlapped);
|
||||||
|
lock.unlock();
|
||||||
|
wibo::lastError = ERROR_IO_PENDING;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe->connectPending = true;
|
||||||
|
pipe->connectCv.wait(lock, [&]() { return pipe->clientConnected || pipe->companionFd < 0; });
|
||||||
|
pipe->connectPending = false;
|
||||||
|
if (!pipe->clientConnected) {
|
||||||
|
wibo::lastError = ERROR_NO_DATA;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace kernel32
|
} // namespace kernel32
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ BOOL WIN_FUNC CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRI
|
|||||||
HANDLE WIN_FUNC CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
|
HANDLE WIN_FUNC CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
|
||||||
DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
|
DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
|
||||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||||
|
BOOL WIN_FUNC ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped);
|
||||||
bool tryCreateFileNamedPipeA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
bool tryCreateFileNamedPipeA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||||
DWORD dwFlagsAndAttributes, HANDLE &outHandle);
|
DWORD dwFlagsAndAttributes, HANDLE &outHandle);
|
||||||
|
|||||||
@@ -27,6 +27,9 @@
|
|||||||
#define ERROR_IO_PENDING 997
|
#define ERROR_IO_PENDING 997
|
||||||
#define ERROR_OPERATION_ABORTED 995
|
#define ERROR_OPERATION_ABORTED 995
|
||||||
#define ERROR_PIPE_BUSY 231
|
#define ERROR_PIPE_BUSY 231
|
||||||
|
#define ERROR_NO_DATA 232
|
||||||
|
#define ERROR_PIPE_CONNECTED 535
|
||||||
|
#define ERROR_PIPE_LISTENING 536
|
||||||
#define ERROR_NONE_MAPPED 1332
|
#define ERROR_NONE_MAPPED 1332
|
||||||
#define ERROR_RESOURCE_DATA_NOT_FOUND 1812
|
#define ERROR_RESOURCE_DATA_NOT_FOUND 1812
|
||||||
#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813
|
#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813
|
||||||
|
|||||||
@@ -6,6 +6,38 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static const char *kPipeName = "\\\\.\\pipe\\wibo_test_namedpipe";
|
static const char *kPipeName = "\\\\.\\pipe\\wibo_test_namedpipe";
|
||||||
|
static const char *kPipeNameConnect = "\\\\.\\pipe\\wibo_test_namedpipe_connect";
|
||||||
|
static const char *kPipeNameNowait = "\\\\.\\pipe\\wibo_test_namedpipe_nowait";
|
||||||
|
static const char *kPipeNameOverlapped = "\\\\.\\pipe\\wibo_test_namedpipe_overlapped";
|
||||||
|
|
||||||
|
struct ConnectThreadArgs {
|
||||||
|
const char *pipeName;
|
||||||
|
const char *message;
|
||||||
|
DWORD delayMs;
|
||||||
|
HANDLE client;
|
||||||
|
DWORD error;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DWORD WINAPI client_connect_thread(LPVOID parameter) {
|
||||||
|
struct ConnectThreadArgs *args = (struct ConnectThreadArgs *)parameter;
|
||||||
|
args->client = INVALID_HANDLE_VALUE;
|
||||||
|
args->error = ERROR_SUCCESS;
|
||||||
|
if (args->delayMs) {
|
||||||
|
Sleep(args->delayMs);
|
||||||
|
}
|
||||||
|
HANDLE client =
|
||||||
|
CreateFileA(args->pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
args->client = client;
|
||||||
|
if (client == INVALID_HANDLE_VALUE) {
|
||||||
|
args->error = GetLastError();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (args->message) {
|
||||||
|
DWORD written = 0;
|
||||||
|
WriteFile(client, args->message, (DWORD)strlen(args->message), &written, NULL);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void write_checked(HANDLE handle, const char *msg) {
|
static void write_checked(HANDLE handle, const char *msg) {
|
||||||
DWORD written = 0;
|
DWORD written = 0;
|
||||||
@@ -70,5 +102,82 @@ int main(void) {
|
|||||||
TEST_CHECK(CloseHandle(client));
|
TEST_CHECK(CloseHandle(client));
|
||||||
TEST_CHECK(CloseHandle(pipe));
|
TEST_CHECK(CloseHandle(pipe));
|
||||||
|
|
||||||
|
HANDLE connectPipe = CreateNamedPipeA(kPipeNameConnect, PIPE_ACCESS_DUPLEX,
|
||||||
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
|
||||||
|
TEST_CHECK(connectPipe != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
struct ConnectThreadArgs connectArgs = {
|
||||||
|
.pipeName = kPipeNameConnect,
|
||||||
|
.message = "hello",
|
||||||
|
.delayMs = 50,
|
||||||
|
.client = INVALID_HANDLE_VALUE,
|
||||||
|
.error = ERROR_SUCCESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
HANDLE connectThread = CreateThread(NULL, 0, client_connect_thread, &connectArgs, 0, NULL);
|
||||||
|
TEST_CHECK(connectThread != NULL);
|
||||||
|
TEST_CHECK(ConnectNamedPipe(connectPipe, NULL));
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(connectThread, 5000));
|
||||||
|
TEST_CHECK(CloseHandle(connectThread));
|
||||||
|
TEST_CHECK(connectArgs.client != INVALID_HANDLE_VALUE);
|
||||||
|
TEST_CHECK_EQ(ERROR_SUCCESS, connectArgs.error);
|
||||||
|
|
||||||
|
read_checked(connectPipe, "hello");
|
||||||
|
write_checked(connectPipe, "world");
|
||||||
|
read_checked(connectArgs.client, "world");
|
||||||
|
|
||||||
|
SetLastError(0xdeadbeefu);
|
||||||
|
TEST_CHECK(!ConnectNamedPipe(connectPipe, NULL));
|
||||||
|
TEST_CHECK_EQ(ERROR_PIPE_CONNECTED, GetLastError());
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(connectArgs.client));
|
||||||
|
TEST_CHECK(CloseHandle(connectPipe));
|
||||||
|
|
||||||
|
HANDLE nowaitPipe = CreateNamedPipeA(kPipeNameNowait, PIPE_ACCESS_DUPLEX,
|
||||||
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 1024, 1024, 0, NULL);
|
||||||
|
TEST_CHECK(nowaitPipe != INVALID_HANDLE_VALUE);
|
||||||
|
SetLastError(0xdeadbeefu);
|
||||||
|
TEST_CHECK(!ConnectNamedPipe(nowaitPipe, NULL));
|
||||||
|
TEST_CHECK_EQ(ERROR_PIPE_LISTENING, GetLastError());
|
||||||
|
TEST_CHECK(CloseHandle(nowaitPipe));
|
||||||
|
|
||||||
|
HANDLE overlappedPipe = CreateNamedPipeA(kPipeNameOverlapped, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||||
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
|
||||||
|
TEST_CHECK(overlappedPipe != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
struct ConnectThreadArgs overlappedArgs = {
|
||||||
|
.pipeName = kPipeNameOverlapped,
|
||||||
|
.message = NULL,
|
||||||
|
.delayMs = 50,
|
||||||
|
.client = INVALID_HANDLE_VALUE,
|
||||||
|
.error = ERROR_SUCCESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
OVERLAPPED ov = {0};
|
||||||
|
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
TEST_CHECK(ov.hEvent != NULL);
|
||||||
|
|
||||||
|
HANDLE overlappedThread = CreateThread(NULL, 0, client_connect_thread, &overlappedArgs, 0, NULL);
|
||||||
|
TEST_CHECK(overlappedThread != NULL);
|
||||||
|
|
||||||
|
SetLastError(0xdeadbeefu);
|
||||||
|
TEST_CHECK(!ConnectNamedPipe(overlappedPipe, &ov));
|
||||||
|
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||||
|
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(overlappedThread, 5000));
|
||||||
|
TEST_CHECK(CloseHandle(overlappedThread));
|
||||||
|
TEST_CHECK(overlappedArgs.client != INVALID_HANDLE_VALUE);
|
||||||
|
TEST_CHECK_EQ(ERROR_SUCCESS, overlappedArgs.error);
|
||||||
|
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(ov.hEvent, 5000));
|
||||||
|
|
||||||
|
DWORD transferred = 0;
|
||||||
|
TEST_CHECK(GetOverlappedResult(overlappedPipe, &ov, &transferred, FALSE));
|
||||||
|
TEST_CHECK_EQ((DWORD)0, transferred);
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(overlappedArgs.client));
|
||||||
|
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||||
|
TEST_CHECK(CloseHandle(overlappedPipe));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user