diff --git a/include/Utils.h b/include/Utils.h index b7def4a..36f3570 100644 --- a/include/Utils.h +++ b/include/Utils.h @@ -13,9 +13,22 @@ #include #include #include +#include +#include #include #include #include +#include +#include +#include +#include +#if defined(__linux__) +#include +#include "linuxfixes.h" +#else +#include +#include +#endif #ifdef _WIN32 #define beammp_fs_string std::wstring @@ -31,6 +44,8 @@ #define beammp_stdout std::cout #endif +#include "Logger.h" + namespace Utils { inline std::vector Split(const std::string& String, const std::string& delimiter) { std::vector Val; @@ -45,7 +60,7 @@ namespace Utils { if (!s.empty()) Val.push_back(s); return Val; - }; + } inline std::string ExpandEnvVars(const std::string& input) { std::string result; std::regex envPattern(R"(%([^%]+)%|\$([A-Za-z_][A-Za-z0-9_]*)|\$\{([^}]+)\})"); @@ -286,4 +301,39 @@ namespace Utils { return ""; } } -}; \ No newline at end of file + + template + inline std::vector PrependHeader(const T& data) { + std::vector size_buffer(4); + uint32_t len = data.size(); + std::memcpy(size_buffer.data(), &len, 4); + std::vector buffer; + buffer.reserve(size_buffer.size() + data.size()); + buffer.insert(buffer.begin(), size_buffer.begin(), size_buffer.end()); + buffer.insert(buffer.end(), data.begin(), data.end()); + return buffer; + } + + inline uint32_t RecvHeader(SOCKET socket) { + std::array header_buffer {}; + auto n = recv(socket, reinterpret_cast(header_buffer.data()), header_buffer.size(), MSG_WAITALL); + if (n < 0) { + throw std::runtime_error(std::string("recv() of header failed: ") + std::strerror(errno)); + } else if (n == 0) { + throw std::runtime_error("Game disconnected"); + } + return *reinterpret_cast(header_buffer.data()); + } + + /// Throws!!! + inline void ReceiveFromGame(SOCKET socket, std::vector& out_data) { + auto header = RecvHeader(socket); + out_data.resize(header); + auto n = recv(socket, reinterpret_cast(out_data.data()), out_data.size(), MSG_WAITALL); + if (n < 0) { + throw std::runtime_error(std::string("recv() of data failed: ") + std::strerror(errno)); + } else if (n == 0) { + throw std::runtime_error("Game disconnected"); + } + } +}; diff --git a/src/GameStart.cpp b/src/GameStart.cpp index 0632606..9c4535b 100644 --- a/src/GameStart.cpp +++ b/src/GameStart.cpp @@ -4,6 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-or-later */ +#include +#include "Utils.h" #if defined(_WIN32) #include #elif defined(__linux__) @@ -18,7 +20,6 @@ #include "Logger.h" #include "Options.h" #include "Startup.h" -#include "Utils.h" #include #include #include @@ -57,12 +58,11 @@ std::filesystem::path GetGamePath() { if (ini.contains("filesystem") && std::get>(ini["filesystem"]).contains("UserPath")) userPath = Utils::ToWString(std::get>(ini["filesystem"])["UserPath"]); - if (!userPath.empty() && Path.empty()) - if (userPath = Utils::ExpandEnvVars(userPath); std::filesystem::exists(userPath)) { - Path = userPath; - debug(L"Using custom user folder path from startup.ini: " + Path.wstring()); - } else - warn(L"Found custom user folder path (" + userPath + L") in startup.ini but it doesn't exist, skipping"); + if (userPath = Utils::ExpandEnvVars(userPath); std::filesystem::exists(userPath)) { + Path = userPath; + debug(L"Using custom user folder path from startup.ini: " + Path.wstring()); + } else + warn(L"Found custom user folder path (" + userPath + L") in startup.ini but it doesn't exist, skipping"); } if (Path.empty()) { @@ -94,12 +94,11 @@ std::filesystem::path GetGamePath() { } - if (!userPath.empty() && Path.empty()) - if (userPath = std::filesystem::path(Utils::ExpandEnvVars(userPath)); std::filesystem::exists(userPath)) { - Path = userPath; - debug(L"Using custom user folder path from BeamNG.Drive.ini: " + Path.wstring()); - } else - warn(L"Found custom user folder path (" + userPath + L") in BeamNG.Drive.ini but it doesn't exist, skipping"); + if (userPath = std::filesystem::path(Utils::ExpandEnvVars(userPath)); std::filesystem::exists(userPath)) { + Path = userPath; + debug(L"Using custom user folder path from BeamNG.Drive.ini: " + Path.wstring()); + } else + warn(L"Found custom user folder path (" + userPath + L") in BeamNG.Drive.ini but it doesn't exist, skipping"); } } @@ -175,7 +174,6 @@ void StartGame(std::wstring Dir) { } #elif defined(__linux__) void StartGame(std::string Dir) { - int status; std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64"); std::vector argv; argv.push_back(filename.data()); @@ -185,11 +183,24 @@ void StartGame(std::string Dir) { argv.push_back(nullptr); pid_t pid; - posix_spawn_file_actions_t spawn_actions; - posix_spawn_file_actions_init(&spawn_actions); - posix_spawn_file_actions_addclose(&spawn_actions, STDOUT_FILENO); - posix_spawn_file_actions_addclose(&spawn_actions, STDERR_FILENO); - int result = posix_spawn(&pid, filename.c_str(), &spawn_actions, nullptr, const_cast(argv.data()), environ); + + posix_spawn_file_actions_t file_actions; + auto status = posix_spawn_file_actions_init(&file_actions); + // disable stdout + if (status != 0) { + error(std::string("posix_spawn_file_actions_init failed: ") + std::strerror(errno)); + } + status = posix_spawn_file_actions_addclose(&file_actions, STDOUT_FILENO); + if (status != 0) { + error(std::string("posix_spawn_file_actions_addclose for STDOUT failed: ") + std::strerror(errno)); + } + status = posix_spawn_file_actions_addclose(&file_actions, STDERR_FILENO); + if (status != 0) { + error(std::string("posix_spawn_file_actions_addclose for STDERR failed: ") + std::strerror(errno)); + } + + // launch the game + int result = posix_spawn(&pid, filename.c_str(), &file_actions, NULL, const_cast(argv.data()), environ); if (result != 0) { error("Failed to Launch the game! launcher closing soon"); diff --git a/src/Network/Core.cpp b/src/Network/Core.cpp index 902e1c7..bcb8050 100644 --- a/src/Network/Core.cpp +++ b/src/Network/Core.cpp @@ -7,6 +7,7 @@ #include "Http.h" #include "Network/network.hpp" #include "Security/Init.h" +#include "Utils.h" #include #include #if defined(_WIN32) @@ -188,7 +189,8 @@ void CoreSend(std::string data) { std::lock_guard lock(sendMutex); if (CoreSocket != -1) { - int res = send(CoreSocket, (data + "\n").c_str(), int(data.size()) + 1, 0); + auto ToSend = Utils::PrependHeader(data); + int res = send(CoreSocket, ToSend.data(), ToSend.size(), 0); if (res < 0) { debug("(Core) send failed with error: " + std::to_string(WSAGetLastError())); } @@ -278,7 +280,7 @@ void Parse(std::string Data, SOCKET CSocket) { Ping = "-2"; else Ping = std::to_string(ping); - Data = std::string(UlStatus) + "\n" + "Up" + Ping; + Data = "Up" + Ping; } break; case 'M': @@ -348,51 +350,29 @@ void Parse(std::string Data, SOCKET CSocket) { Data.clear(); break; } - if (!Data.empty()) - CoreSend(Data); + if (!Data.empty() && CSocket != -1) { + auto ToSend = Utils::PrependHeader(Data); + int res = send(CSocket, ToSend.data(), ToSend.size(), 0); + if (res < 0) { + debug("(Core) send failed with error: " + std::to_string(WSAGetLastError())); + } + } } void GameHandler(SOCKET Client) { CoreSocket = Client; - int32_t Size, Rcv; + std::vector data{}; int Temp; char Header[10] = { 0 }; do { - Rcv = 0; - do { - Temp = recv(Client, &Header[Rcv], 1, 0); - if (Temp < 1) - break; - if (!isdigit(Header[Rcv]) && Header[Rcv] != '>') { - error("(Core) Invalid lua communication"); - KillSocket(Client); - return; - } - } while (Header[Rcv++] != '>'); - if (Temp < 1) - break; - if (std::from_chars(Header, &Header[Rcv], Size).ptr[0] != '>') { - debug("(Core) Invalid lua Header -> " + std::string(Header, Rcv)); + try { + Utils::ReceiveFromGame(Client, data); + Parse(std::string(data.data(), data.size()), Client); + } catch (const std::exception& e) { + error(std::string("Error while receiving from game on core: ") + e.what()); break; } - std::string Ret(Size, 0); - Rcv = 0; - - do { - Temp = recv(Client, &Ret[Rcv], Size - Rcv, 0); - if (Temp < 1) - break; - Rcv += Temp; - } while (Rcv < Size); - if (Temp < 1) - break; - - Parse(Ret, Client); - } while (Temp > 0); - if (Temp == 0) { - debug("(Core) Connection closing"); - } else { - debug("(Core) recv failed with error: " + std::to_string(WSAGetLastError())); - } + } while (true); + debug("(Core) Connection closing"); NetReset(); KillSocket(Client); } diff --git a/src/Network/GlobalHandler.cpp b/src/Network/GlobalHandler.cpp index 776169e..da4b6f2 100644 --- a/src/Network/GlobalHandler.cpp +++ b/src/Network/GlobalHandler.cpp @@ -5,6 +5,7 @@ */ #include "Network/network.hpp" +#include "Utils.h" #include #include #if defined(_WIN32) @@ -21,6 +22,7 @@ #endif #include "Logger.h" +#include "Options.h" #include #include #include @@ -62,30 +64,13 @@ bool CheckBytes(uint32_t Bytes) { void GameSend(std::string_view Data) { static std::mutex Lock; std::scoped_lock Guard(Lock); - if (TCPTerminate || !GConnected || CSocket == -1) - return; - int32_t Size, Temp, Sent; - Size = int32_t(Data.size()); - Sent = 0; -#ifdef DEBUG - if (Size > 1000) { - debug("Launcher -> game (" + std::to_string(Size) + ")"); - } -#endif - do { - if (Sent > -1) { - Temp = send(CSocket, &Data[Sent], Size - Sent, 0); - } - if (!CheckBytes(Temp)) - return; - Sent += Temp; - } while (Sent < Size); - // send separately to avoid an allocation for += "\n" - Temp = send(CSocket, "\n", 1, 0); - if (!CheckBytes(Temp)) { - return; + auto ToSend = Utils::PrependHeader(Data); + auto Result = send(CSocket, ToSend.data(), ToSend.size(), 0); + if (Result < 0) { + error("(Game) send failed with error: " + std::to_string(WSAGetLastError())); } } + void ServerSend(std::string Data, bool Rel) { if (Terminate || Data.empty()) return; @@ -268,40 +253,19 @@ void TCPGameServer(const std::string& IP, int Port) { int32_t Size, Rcv; int Temp; char Header[10] = { 0 }; + std::vector data {}; // Read byte by byte until '>' is rcved then get the size and read based on it do { - Rcv = 0; - - do { - Temp = recv(CSocket, &Header[Rcv], 1, 0); - if (Temp < 1 || TCPTerminate) - break; - } while (Header[Rcv++] != '>'); - if (Temp < 1 || TCPTerminate) - break; - if (std::from_chars(Header, &Header[Rcv], Size).ptr[0] != '>') { - debug("(Game) Invalid lua Header -> " + std::string(Header, Rcv)); + try { + Utils::ReceiveFromGame(CSocket, data); + ServerSend(std::string(data.data(), data.size()), false); + } catch (const std::exception& e) { + error(std::string("Error while receiving from game on proxy: ") + e.what()); break; } - std::string Ret(Size, 0); - Rcv = 0; - do { - Temp = recv(CSocket, &Ret[Rcv], Size - Rcv, 0); - if (Temp < 1) - break; - Rcv += Temp; - } while (Rcv < Size && !TCPTerminate); - if (Temp < 1 || TCPTerminate) - break; - - ServerSend(Ret, false); - - } while (Temp > 0 && !TCPTerminate); - if (Temp == 0) - debug("(Proxy) Connection closing"); - else - debug("(Proxy) recv failed error : " + std::to_string(WSAGetLastError())); + } while (!TCPTerminate); + debug("(Proxy) Connection closing"); } TCPTerminate = true; GConnected = false; diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp index 4bb6d12..68b4d7c 100644 --- a/src/Security/BeamNG.cpp +++ b/src/Security/BeamNG.cpp @@ -6,6 +6,7 @@ #include +#include "Utils.h" #if defined(_WIN32) #include #elif defined(__linux__) @@ -15,8 +16,6 @@ #include #endif #include "Logger.h" -#include "Utils.h" - #include #include #include