AuroraRuntime/Source/IO/NT/HANDLEPipeServer.cpp

366 lines
10 KiB
C++

/***
Copyright (C) 2024 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: HANDLEPipeServer.cpp
Date: 2024-01-02
Author: Reece
Note: Rushed Windows HANDLE sharing of any unnamed kernel object
***/
#include <Source/RuntimeInternal.hpp>
#include <Source/IO/IPC/AuIPCHandle.hpp>
namespace Aurora::IO::NT
{
static AuMutex gMutex;
static AuBST<AuUInt32, HANDLE> gHANDLECookieMap;
static AuThreads::ThreadUnique_t gServerThread {};
static const auto kDefaultMessageSize = 8192u;
static auto GetIPCServerNameOfPid(AuUInt32 uPid)
{
#if defined(AURORA_PLATFORM_WIN32)
return fmt::format("\\\\.\\pipe\\_HANDLESOF_{}", uPid);
#else
return fmt::format("\\\\.\\pipe\\LOCAL\\_HANDLESOF_{}", uPid);
#endif
}
HANDLE GetHandleForToken(AuUInt32 uToken)
{
AU_LOCK_GUARD(gMutex);
auto itr = gHANDLECookieMap.find(uToken);
if (itr != gHANDLECookieMap.end())
{
return itr->second;
}
else
{
return INVALID_HANDLE_VALUE;
}
}
// TODO: use AuLoop
DWORD WINAPI WaitForCookieThread(LPVOID lpvParam)
{
AuUInt32 uTokenID {};
HANDLE hHandle { INVALID_HANDLE_VALUE }, hPipe { (HANDLE)lpvParam };
DWORD dwBytesRead {}, dwWritten {};
BOOL bSuccess { true };
if (!lpvParam)
{
return (DWORD)-1;
}
do
{
bSuccess = ReadFile(hPipe,
&uTokenID,
sizeof(uTokenID),
&dwBytesRead,
NULL);
if (!bSuccess ||
dwBytesRead == 0)
{
if (GetLastError() != ERROR_BROKEN_PIPE)
{
SysPushErrorIO("IO HANDLE Server Error");
}
break;
}
hHandle = GetHandleForToken(uTokenID);
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorIO("Tried to serve bad token: {}", uTokenID);
break;
}
{
if (!pGetNamedPipeClientProcessId)
{
SysPushErrorIO("UWP Platform Error");
break;
}
ULONG pid {};
if (!pGetNamedPipeClientProcessId(hPipe, &pid))
{
SysPushErrorIO("IO HANDLE Server Error");
break;
}
HANDLE hProcess {};
if (!(hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid)))
{
SysPushErrorIO("IO HANDLE Server Error");
break;
}
if (!DuplicateHandle(GetCurrentProcess(),
hHandle,
hProcess,
&hHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS))
{
AuWin32CloseHandle(hProcess);
SysPushErrorIO("IO HANDLE Server Error");
break;
}
AuWin32CloseHandle(hProcess);
}
bSuccess = WriteFile(hPipe,
&hHandle,
sizeof(hHandle),
&dwWritten,
NULL);
if (!bSuccess ||
sizeof(hHandle) != sizeof(HANDLE))
{
SysPushErrorIO("IO HANDLE Server Error");
break;
}
}
while (false);
AuWin32CloseHandle(hPipe);
return bSuccess ? 0 : (DWORD)-1;
}
static void IPCServer()
{
HANDLE hPipe = INVALID_HANDLE_VALUE;
HANDLE hThread = NULL;
auto name = GetIPCServerNameOfPid(GetCurrentProcessId());
while (AuIsThreadRunning())
{
// TODO: use AuLoop
hPipe = CreateNamedPipeA(name.c_str(),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
kDefaultMessageSize,
kDefaultMessageSize,
0,
NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{
SysPushErrorIOResourceFailure("NO IPC Handle Server");
return;
}
if (ConnectNamedPipe(hPipe, NULL) ||
(GetLastError() == ERROR_PIPE_CONNECTED))
{
// TODO: use AuLoop
hThread = CreateThread(NULL,
0,
WaitForCookieThread,
(LPVOID)hPipe,
0,
NULL);
if (hThread == NULL)
{
DisconnectNamedPipe(hPipe);
}
else
{
AuWin32CloseHandle(hThread);
}
}
else
{
AuWin32CloseHandle(hPipe);
}
}
}
static void StartServerIfNotAlready()
{
static AuInitOnce gInitOnce;
gInitOnce.Call([]()
{
gServerThread = AuThreads::Spawn(IPCServer);
SysAssert(gServerThread, "No NT Handle Server Thread");
gServerThread->SetName("Microsoft NT HANDLE IPC Server/IPC-Provider");
});
}
bool FDServe(HANDLE hHandle, IPC::IPCToken &outHandle)
{
if (!hHandle ||
hHandle == INVALID_HANDLE_VALUE)
{
return false;
}
{
StartServerIfNotAlready();
}
{
AU_LOCK_GUARD(gMutex);
do
{
outHandle.cookie = AuRng::RngU32();
}
while (AuExists(gHANDLECookieMap, outHandle.cookie));
outHandle.word = outHandle.cookie;
outHandle.pid = GetCurrentProcessId();
outHandle.path[0] = '\x00'; // msvc removing something it shouldn't have?
//strcat(outHandle.path, AuToString(outHandle.cookie).c_str()); // ignore warning
return AuTryInsert(gHANDLECookieMap, AuConstReference(outHandle.cookie), AuConstReference(hHandle));
}
}
void FDServeEnd(const IPC::IPCToken &handle)
{
AU_LOCK_GUARD(gMutex);
AuTryRemove(gHANDLECookieMap, handle.cookie);
}
bool FDAccept(const IPC::IPCToken &handle, HANDLE &outHandle)
{
outHandle = INVALID_HANDLE_VALUE;
HANDLE hPipe {};
HANDLE hHandle {};
BOOL bSuccess {};
DWORD dwWritten {};
auto name = GetIPCServerNameOfPid(handle.pid);
hPipe = Win32Open(AuLocale::ConvertFromUTF8(name).c_str(),
GENERIC_WRITE | GENERIC_READ,
0,
false,
OPEN_ALWAYS,
0,
FILE_ATTRIBUTE_NORMAL);
if (hPipe == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_PIPE_BUSY)
{
SysPushErrorIO("Pipe is used -> a client has already connected or the nt server is not ready");
return false;
}
else
{
SysPushErrorIO("No Server: {}", GetLastError());
return false;
}
}
bSuccess = WriteFile(hPipe,
&handle.cookie,
sizeof(AuUInt32),
&dwWritten,
NULL);
if (!bSuccess)
{
AuWin32CloseHandle(hPipe);
SysPushErrorIO("No Server");
return false;
}
FlushFileBuffers(hPipe);
bSuccess = ReadFile(hPipe,
&hHandle,
sizeof(hHandle),
&dwWritten,
NULL);
if (!bSuccess)
{
AuWin32CloseHandle(hPipe);
SysPushErrorIO("No Handle");
return false;
}
outHandle = hHandle;
return true;
}
void ShutdownNTIPCHandleServer()
{
gServerThread.reset();
}
AUKN_SYM AuString ShareFileDescriptor(HANDLE hHandle)
{
IPC::IPCToken token;
IPC::IPCHandle handle;
if (!FDServe(hHandle, token))
{
return {};
}
handle.pid = token.pid;
handle.PushId(IPC::EIPCHandleType::eIPCSharedFd, token);
return handle.ToString();
}
AUKN_SYM void ShareFileDescriptorStop(const AuROString &handle)
{
IPC::IPCHandle handle2;
if (!handle2.FromString(handle))
{
SysPushErrorIO("Invalid handle string");
return;
}
if (auto val = handle2.GetToken(IPC::EIPCHandleType::eIPCSharedFd, 0))
{
FDServeEnd(val->token);
}
else
{
SysPushErrorSyntax("Invalid handle string");
}
}
AUKN_SYM HANDLE ShareFileDescriptorAccept(const AuROString &handle)
{
IPC::IPCHandle handle2;
HANDLE hHandle { INVALID_HANDLE_VALUE };
if (!handle2.FromString(handle))
{
SysPushErrorIO("Invalid handle string");
return hHandle;
}
auto val = handle2.GetToken(IPC::EIPCHandleType::eIPCSharedFd, 0);
if (!val)
{
SysPushErrorSyntax("Invalid handle string");
return hHandle;
}
if (!FDAccept(val->token, hHandle))
{
SysPushErrorNested();
return hHandle;
}
return hHandle;
}
}