diff --git a/include/Helpers.h b/include/Helpers.h new file mode 100644 index 0000000..8b70923 --- /dev/null +++ b/include/Helpers.h @@ -0,0 +1,11 @@ +#include +#include +#include + +#pragma once + +using ByteSpan = std::span; + +std::string bytespan_to_string(ByteSpan span); + +std::vector strtovec(std::string_view str); diff --git a/include/Network/network.hpp b/include/Network/network.hpp index 5c0b2b3..cb9b51c 100644 --- a/include/Network/network.hpp +++ b/include/Network/network.hpp @@ -7,12 +7,15 @@ /// #pragma once +#include "Helpers.h" +#include #include #ifdef __linux__ #include "linuxfixes.h" #include #include +#include #include #endif @@ -39,16 +42,16 @@ extern std::string PrivateKey; extern std::string ListOfMods; int KillSocket(uint64_t Dead); void UUl(const std::string& R); -void UDPSend(std::string Data); +void UDPSend(const std::vector& Data); bool CheckBytes(int32_t Bytes); void GameSend(std::string_view Data); -void SendLarge(std::string Data); +void SendLarge(const std::vector& Data); std::string TCPRcv(uint64_t Sock); void SyncResources(uint64_t TCPSock); std::string GetAddr(const std::string& IP); void ServerParser(std::string_view Data); std::string Login(const std::string& fields); -void TCPSend(const std::string& Data, uint64_t Sock); +void TCPSend(const std::vector& Data, uint64_t Sock); void TCPClientMain(const std::string& IP, int Port); void UDPClientMain(const std::string& IP, int Port); void TCPGameServer(const std::string& IP, int Port); diff --git a/include/NetworkHelpers.h b/include/NetworkHelpers.h new file mode 100644 index 0000000..b8663f7 --- /dev/null +++ b/include/NetworkHelpers.h @@ -0,0 +1,11 @@ +#pragma once + +#if defined(__linux__) +#include "linuxfixes.h" +#else +#include +#include +#endif +#include + +void ReceiveFromGame(SOCKET socket, std::vector& out_data); diff --git a/src/Helpers.cpp b/src/Helpers.cpp new file mode 100644 index 0000000..1c94be9 --- /dev/null +++ b/src/Helpers.cpp @@ -0,0 +1,9 @@ +#include "Helpers.h" + +std::string bytespan_to_string(ByteSpan span) { + return std::string(span.data(), span.size()); +} + +std::vector strtovec(std::string_view str) { + return std::vector(str.begin(), str.end()); +} diff --git a/src/Network/Core.cpp b/src/Network/Core.cpp index 02820a9..54b3bcb 100644 --- a/src/Network/Core.cpp +++ b/src/Network/Core.cpp @@ -7,6 +7,7 @@ /// #include "Http.h" #include "Network/network.hpp" +#include "NetworkHelpers.h" #include "Security/Init.h" #include #include @@ -72,37 +73,38 @@ bool IsAllowedLink(const std::string& Link) { return std::regex_search(Link, link_match, link_pattern) && link_match.position() == 0; } -void Parse(std::string Data, SOCKET CSocket) { - char Code = Data.at(0), SubCode = 0; - if (Data.length() > 1) - SubCode = Data.at(1); +void Parse(std::span InData, SOCKET CSocket) { + std::string OutData; + char Code = InData[0], SubCode = 0; + if (InData.size() > 1) + SubCode = InData[1]; switch (Code) { case 'A': - Data = Data.substr(0, 1); + OutData = "A"; break; case 'B': NetReset(); Terminate = true; TCPTerminate = true; - Data = Code + HTTP::Get("https://backend.beammp.com/servers-info"); + OutData = Code + HTTP::Get("https://backend.beammp.com/servers-info"); break; case 'C': ListOfMods.clear(); - StartSync(Data); + StartSync(std::string(InData.data(), InData.size())); while (ListOfMods.empty() && !Terminate) { std::this_thread::sleep_for(std::chrono::seconds(1)); } if (ListOfMods == "-") - Data = "L"; + OutData = "L"; else - Data = "L" + ListOfMods; + OutData = "L" + ListOfMods; break; case 'O': // open default browser with URL - if (IsAllowedLink(Data.substr(1))) { + if (IsAllowedLink(bytespan_to_string(InData.subspan(1)))) { #if defined(__linux) if (char* browser = getenv("BROWSER"); browser != nullptr && !std::string_view(browser).empty()) { pid_t pid; - auto arg = Data.substr(1); + auto arg = bytespan_to_string(InData.subspan(1)); char* argv[] = { browser, arg.data() }; auto status = posix_spawn(&pid, browser, nullptr, nullptr, argv, environ); if (status == 0) { @@ -114,27 +116,27 @@ void Parse(std::string Data, SOCKET CSocket) { error(std::string("posix_spawn: ") + strerror(status)); } } else { - error("Failed to open the following link in the browser because the $BROWSER environment variable is not set: " + Data.substr(1)); + error("Failed to open the following link in the browser because the $BROWSER environment variable is not set: " + bytespan_to_string(InData.subspan(1))); } #elif defined(WIN32) - ShellExecuteA(nullptr, "open", Data.substr(1).c_str(), nullptr, nullptr, SW_SHOW); /// TODO: Look at when working on linux port + ShellExecuteA(nullptr, "open", InData.subspan(1).data(), nullptr, nullptr, SW_SHOW); /// TODO: Look at when working on linux port #endif - info("Opening Link \"" + Data.substr(1) + "\""); + info("Opening Link \"" + bytespan_to_string(InData.subspan(1)) + "\""); } - Data.clear(); + OutData.clear(); break; case 'P': - Data = Code + std::to_string(ProxyPort); + OutData = Code + std::to_string(ProxyPort); break; case 'U': if (SubCode == 'l') - Data = UlStatus; + OutData = UlStatus; if (SubCode == 'p') { if (ping > 800) { - Data = "Up-2"; + OutData = "Up-2"; } else - Data = "Up" + std::to_string(ping); + OutData = "Up" + std::to_string(ping); } if (!SubCode) { std::string Ping; @@ -142,11 +144,11 @@ void Parse(std::string Data, SOCKET CSocket) { Ping = "-2"; else Ping = std::to_string(ping); - Data = std::string(UlStatus) + "\n" + "Up" + Ping; + OutData = std::string(UlStatus) + "\n" + "Up" + Ping; } break; case 'M': - Data = MStatus; + OutData = MStatus; break; case 'Q': if (SubCode == 'S') { @@ -157,17 +159,19 @@ void Parse(std::string Data, SOCKET CSocket) { } if (SubCode == 'G') exit(2); - Data.clear(); + OutData.clear(); break; case 'R': // will send mod name - if (ConfList->find(Data) == ConfList->end()) { - ConfList->insert(Data); + { + auto str = bytespan_to_string(InData); + if (ConfList->find(str) == ConfList->end()) { + ConfList->insert(str); ModLoaded = true; } - Data.clear(); - break; + OutData.clear(); + } break; case 'Z': - Data = "Z" + GetVer(); + OutData = "Z" + GetVer(); break; case 'N': if (SubCode == 'c') { @@ -180,20 +184,21 @@ void Parse(std::string Data, SOCKET CSocket) { if (!UserRole.empty()) { Auth["role"] = UserRole; } - Data = "N" + Auth.dump(); + OutData = "N" + Auth.dump(); } else { - Data = "N" + Login(Data.substr(Data.find(':') + 1)); + auto indata_str = bytespan_to_string(InData); + OutData = "N" + Login(indata_str.substr(indata_str.find(':') + 1)); } break; default: - Data.clear(); + OutData.clear(); break; } - if (!Data.empty() && CSocket != -1) { - uint32_t DataSize = Data.size(); - std::vector ToSend(sizeof(DataSize) + Data.size()); + if (!OutData.empty() && CSocket != -1) { + uint32_t DataSize = OutData.size(); + std::vector ToSend(sizeof(DataSize) + OutData.size()); std::copy_n(reinterpret_cast(&DataSize), sizeof(DataSize), ToSend.begin()); - std::copy_n(Data.data(), Data.size(), ToSend.begin() + sizeof(DataSize)); + std::copy_n(OutData.data(), OutData.size(), ToSend.begin() + sizeof(DataSize)); int res = send(CSocket, ToSend.data(), int(ToSend.size()), 0); if (res < 0) { debug("(Core) send failed with error: " + std::to_string(WSAGetLastError())); @@ -201,46 +206,16 @@ void Parse(std::string Data, SOCKET CSocket) { } } void GameHandler(SOCKET Client) { - - int32_t Size, Temp, Rcv; - char Header[10] = { 0 }; + std::vector data {}; 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 { + ReceiveFromGame(Client, data); + Parse(data, Client); + } catch (const std::exception& e) { + error(std::string("Error while receiving from game: ") + 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); NetReset(); KillSocket(Client); } @@ -286,6 +261,11 @@ void CoreMain() { WSACleanup(); return; } +#if defined (__linux__) + int opt = 1; + if (setsockopt(LSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) + error("setsockopt(SO_REUSEADDR) failed"); +#endif iRes = bind(LSocket, res->ai_addr, int(res->ai_addrlen)); if (iRes == SOCKET_ERROR) { error("(Core) bind failed with error: " + std::to_string(WSAGetLastError())); diff --git a/src/Network/GlobalHandler.cpp b/src/Network/GlobalHandler.cpp index 2c4495f..6f9a39b 100644 --- a/src/Network/GlobalHandler.cpp +++ b/src/Network/GlobalHandler.cpp @@ -5,8 +5,11 @@ /// /// Created by Anonymous275 on 7/25/2020 /// +#include "Helpers.h" #include "Network/network.hpp" +#include "NetworkHelpers.h" #include +#include #include #include #if defined(_WIN32) @@ -89,15 +92,13 @@ void GameSend(std::string_view RawData) { return; }*/ } -void ServerSend(std::string Data, bool Rel) { + +void ServerSend(const std::vector& Data, bool Rel) { if (Terminate || Data.empty()) return; - if (Data.find("Zp") != std::string::npos && Data.size() > 500) { - abort(); - } char C = 0; bool Ack = false; - int DLen = int(Data.length()); + int DLen = int(Data.size()); if (DLen > 3) C = Data.at(0); if (C == 'O' || C == 'T') @@ -113,14 +114,6 @@ void ServerSend(std::string Data, bool Rel) { TCPSend(Data, TCPSock); } else UDPSend(Data); - - if (DLen > 1000) { - debug("(Launcher->Server) Bytes sent: " + std::to_string(Data.length()) + " : " - + Data.substr(0, 10) - + Data.substr(Data.length() - 10)); - } else if (C == 'Z') { - // debug("(Game->Launcher) : " + Data); - } } void NetReset() { @@ -178,6 +171,11 @@ SOCKET SetupListener() { WSACleanup(); return -1; } +#if defined (__linux__) + int opt = 1; + if (setsockopt(GSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) + error("setsockopt(SO_REUSEADDR) failed"); +#endif iRes = bind(GSocket, result->ai_addr, (int)result->ai_addrlen); if (iRes == SOCKET_ERROR) { error("(Proxy) bind failed with error: " + std::to_string(WSAGetLastError())); @@ -198,7 +196,7 @@ SOCKET SetupListener() { } void AutoPing() { while (!Terminate) { - ServerSend("p", false); + ServerSend(strtovec("p"), false); PingStart = std::chrono::high_resolution_clock::now(); std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -266,42 +264,18 @@ void TCPGameServer(const std::string& IP, int Port) { t1.detach(); CServer = false; } - int32_t Size, Temp, Rcv; - 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 { + ReceiveFromGame(CSocket, data); + ServerSend(data, false); + } catch (const std::exception& e) { + error(std::string("Error while receiving from game: ") + 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); } TCPTerminate = true; GConnected = false; diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp index f32c263..22722c4 100644 --- a/src/Network/Resources.cpp +++ b/src/Network/Resources.cpp @@ -73,7 +73,7 @@ void Abord() { } std::string Auth(SOCKET Sock) { - TCPSend("VC" + GetVer(), Sock); + TCPSend(strtovec("VC" + GetVer()), Sock); auto Res = TCPRcv(Sock); @@ -82,7 +82,7 @@ std::string Auth(SOCKET Sock) { return ""; } - TCPSend(PublicKey, Sock); + TCPSend(strtovec(PublicKey), Sock); if (Terminate) return ""; @@ -100,7 +100,7 @@ std::string Auth(SOCKET Sock) { UUl("Authentication failed!"); return ""; } - TCPSend("SR", Sock); + TCPSend(strtovec("SR"), Sock); if (Terminate) return ""; @@ -114,7 +114,7 @@ std::string Auth(SOCKET Sock) { if (Res.empty() || Res == "-") { info("Didn't Receive any mods..."); ListOfMods = "-"; - TCPSend("Done", Sock); + TCPSend(strtovec("Done"), Sock); info("Done!"); return ""; } @@ -320,7 +320,7 @@ void SyncResources(SOCKET Sock) { CheckForDir(); std::string FName = a.substr(a.find_last_of('/')); do { - TCPSend("f" + *FN, Sock); + TCPSend(strtovec("f" + *FN), Sock); std::string Data = TCPRcv(Sock); if (Data == "CO" || Terminate) { @@ -362,7 +362,7 @@ void SyncResources(SOCKET Sock) { } KillSocket(DSock); if (!Terminate) { - TCPSend("Done", Sock); + TCPSend(strtovec("Done"), Sock); info("Done!"); } else { UlStatus = "Ulstart"; diff --git a/src/Network/VehicleData.cpp b/src/Network/VehicleData.cpp index 2331464..2d409ea 100644 --- a/src/Network/VehicleData.cpp +++ b/src/Network/VehicleData.cpp @@ -26,12 +26,15 @@ SOCKET UDPSock = -1; sockaddr_in* ToServer = nullptr; -void UDPSend(std::string Data) { +void UDPSend(const std::vector& RawData) { if (ClientID == -1 || UDPSock == -1) return; - if (Data.length() > 400) { - auto res = Comp(std::span(Data.data(), Data.size())); + std::string Data; + if (Data.size() > 400) { + auto res = Comp(RawData); Data = "ABG:" + std::string(res.data(), res.size()); + } else { + Data = std::string(RawData.data(), RawData.size()); } std::string Packet = char(ClientID + 1) + std::string(":") + Data; int sendOk = sendto(UDPSock, Packet.c_str(), int(Packet.size()), 0, (sockaddr*)ToServer, sizeof(*ToServer)); @@ -39,12 +42,14 @@ void UDPSend(std::string Data) { error("Error Code : " + std::to_string(WSAGetLastError())); } -void SendLarge(std::string Data) { - if (Data.length() > 400) { - auto res = Comp(std::span(Data.data(), Data.size())); - Data = "ABG:" + std::string(res.data(), res.size()); +void SendLarge(const std::vector& Data) { + if (Data.size() > 400) { + auto res = Comp(Data); + res.insert(res.begin(), {'A', 'B', 'G', ':'}); + TCPSend(res, TCPSock); + } else { + TCPSend(Data, TCPSock); } - TCPSend(Data, TCPSock); } void UDPParser(std::string_view Packet) { @@ -90,8 +95,8 @@ void UDPClientMain(const std::string& IP, int Port) { inet_pton(AF_INET, IP.c_str(), &ToServer->sin_addr); UDPSock = socket(AF_INET, SOCK_DGRAM, 0); GameSend("P" + std::to_string(ClientID)); - TCPSend("H", TCPSock); - UDPSend("p"); + TCPSend(strtovec("H"), TCPSock); + UDPSend(strtovec("p")); while (!Terminate) UDPRcv(); KillSocket(UDPSock); diff --git a/src/Network/VehicleEvent.cpp b/src/Network/VehicleEvent.cpp index c02a572..139849b 100644 --- a/src/Network/VehicleEvent.cpp +++ b/src/Network/VehicleEvent.cpp @@ -46,7 +46,7 @@ void UUl(const std::string& R) { UlStatus = "UlDisconnected: " + R; } -void TCPSend(const std::string& Data, uint64_t Sock) { +void TCPSend(const std::vector& Data, uint64_t Sock) { if (Sock == -1) { Terminate = true; UUl("Invalid Socket"); @@ -57,7 +57,7 @@ void TCPSend(const std::string& Data, uint64_t Sock) { std::string Send(4, 0); Size = int32_t(Data.size()); memcpy(&Send[0], &Size, sizeof(Size)); - Send += Data; + Send += std::string(Data.data(), Data.size()); // Do not use Size before this point for anything but the header Sent = 0; Size += 4; @@ -113,7 +113,7 @@ std::string TCPRcv(SOCKET Sock) { if (Ret.substr(0, 4) == "ABG:") { auto substr = Ret.substr(4); - auto res = DeComp(std::span(substr.data(), substr.size())); + auto res = DeComp(strtovec(substr)); Ret = std::string(res.data(), res.size()); } diff --git a/src/NetworkHelpers.cpp b/src/NetworkHelpers.cpp new file mode 100644 index 0000000..0b8af33 --- /dev/null +++ b/src/NetworkHelpers.cpp @@ -0,0 +1,35 @@ +#include "NetworkHelpers.h" + +#include +#include +#include +#include +#if defined(__linux__) +#include +#else +#include +#include +#endif + +static 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!!! +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"); + } +}