5 Commits

Author SHA1 Message Date
SaltySnail
169b14490c Added IPv6 support 2024-08-21 01:38:52 +02:00
Lion
a60ff48c08 Merge pull request #105 from WiserTixx/id-from-auth
Send id from auth to game
2024-08-17 20:34:19 +02:00
Lion
da3b49aa12 Merge pull request #106 from WiserTixx/fix-http-proxy-ub
Fix UB which was causing the http proxy to crash
2024-08-17 20:32:59 +02:00
Tixx
e505874af9 Send id from auth to game 2024-08-11 11:39:14 +02:00
Tixx
2f0a9fba99 move macro definition to cmakelist 2024-08-10 23:22:17 +02:00
19 changed files with 501 additions and 523 deletions

View File

@@ -12,11 +12,11 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT)
file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h" "include/*.hpp" "include/*/*.hpp" "include/*/*/*.hpp") file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h" "include/*.hpp" "include/*/*.hpp" "include/*/*/*.hpp")
find_package(httplib CONFIG REQUIRED) find_package(httplib CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED) find_package(nlohmann_json CONFIG REQUIRED)
find_package(asio CONFIG REQUIRED)
find_package(fmt CONFIG REQUIRED)
add_executable(${PROJECT_NAME} ${source_files}) add_executable(${PROJECT_NAME} ${source_files})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher") set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher")
@@ -25,15 +25,15 @@ if (WIN32)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE target_link_libraries(${PROJECT_NAME} PRIVATE
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json asio::asio fmt::fmt) ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json)
elseif (LINUX) elseif (LINUX)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE target_link_libraries(${PROJECT_NAME} PRIVATE
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto asio::asio fmt::fmt) ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto)
elseif (WIN32) #MINGW else(WIN32) #MINGW
add_definitions("-D_WIN32_WINNT=0x0600") add_definitions("-D_WIN32_WINNT=0x0600")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static")
target_link_libraries(${PROJECT_NAME} ssl crypto ws2_32 ssp crypt32 z asio::asio fmt::fmt) target_link_libraries(${PROJECT_NAME} ssl crypto ws2_32 ssp crypt32 z)
endif(WIN32) endif(WIN32)
target_include_directories(${PROJECT_NAME} PRIVATE "include") target_include_directories(${PROJECT_NAME} PRIVATE "include")

View File

@@ -1,11 +0,0 @@
#include <span>
#include <string>
#include <vector>
#pragma once
using ByteSpan = std::span<const char>;
std::string bytespan_to_string(ByteSpan span);
std::vector<char> strtovec(std::string_view str);

View File

@@ -7,10 +7,6 @@
/// ///
#pragma once #pragma once
#include "Helpers.h"
#include "asio/io_context.hpp"
#include "asio/ip/address.hpp"
#include <span>
#include <string> #include <string>
#ifdef __linux__ #ifdef __linux__
@@ -18,13 +14,8 @@
#include <bits/types/siginfo_t.h> #include <bits/types/siginfo_t.h>
#include <cstdint> #include <cstdint>
#include <sys/ucontext.h> #include <sys/ucontext.h>
#include <vector>
#endif #endif
#include <asio.hpp>
extern asio::io_context io;
void NetReset(); void NetReset();
extern bool Dev; extern bool Dev;
extern int ping; extern int ping;
@@ -36,8 +27,8 @@ extern int LastPort;
extern bool ModLoaded; extern bool ModLoaded;
extern bool Terminate; extern bool Terminate;
extern int DEFAULT_PORT; extern int DEFAULT_PORT;
extern std::shared_ptr<asio::ip::udp::socket> UDPSock; extern uint64_t UDPSock;
extern std::shared_ptr<asio::ip::tcp::socket> TCPSock; extern uint64_t TCPSock;
extern std::string Branch; extern std::string Branch;
extern bool TCPTerminate; extern bool TCPTerminate;
extern std::string LastIP; extern std::string LastIP;
@@ -46,21 +37,18 @@ extern std::string UlStatus;
extern std::string PublicKey; extern std::string PublicKey;
extern std::string PrivateKey; extern std::string PrivateKey;
extern std::string ListOfMods; extern std::string ListOfMods;
void KillSocket(std::shared_ptr<asio::ip::tcp::socket>& Dead); int KillSocket(uint64_t Dead);
void KillSocket(std::shared_ptr<asio::ip::udp::socket>& Dead);
void KillSocket(asio::ip::tcp::socket& Dead);
void KillSocket(asio::ip::udp::socket& Dead);
void UUl(const std::string& R); void UUl(const std::string& R);
void UDPSend(const std::vector<char>& Data); void UDPSend(std::string Data);
bool CheckBytes(int32_t Bytes); bool CheckBytes(int32_t Bytes);
void GameSend(std::string_view Data); void GameSend(std::string_view Data);
void SendLarge(const std::vector<char>& Data); void SendLarge(std::string Data);
std::string TCPRcv(asio::ip::tcp::socket& Sock); std::string TCPRcv(uint64_t Sock);
void SyncResources(asio::ip::tcp::socket& TCPSock); void SyncResources(uint64_t TCPSock);
std::string GetAddr(const std::string& IP); std::string GetAddr(const std::string& IP);
void ServerParser(std::string_view Data); void ServerParser(std::string_view Data);
std::string Login(const std::string& fields); std::string Login(const std::string& fields);
void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock); void TCPSend(const std::string& Data, uint64_t Sock);
void TCPClientMain(asio::ip::tcp::socket&& Socket); void TCPClientMain(const std::string& IP, int Port);
void UDPClientMain(asio::ip::address addr, uint16_t port); void UDPClientMain(const std::string& IP, int Port);
void TCPGameServer(asio::ip::tcp::socket&& Socket); void TCPGameServer(const std::string& IP, int Port);

View File

@@ -1,6 +0,0 @@
#pragma once
#include <asio.hpp>
#include <vector>
void ReceiveFromGame(asio::ip::tcp::socket& socket, std::vector<char>& out_data);

View File

@@ -19,12 +19,11 @@ std::vector<char> Comp(std::span<const char> input) {
auto max_size = compressBound(input.size()); auto max_size = compressBound(input.size());
std::vector<char> output(max_size); std::vector<char> output(max_size);
uLongf output_size = output.size(); uLongf output_size = output.size();
int res = compress2( int res = compress(
reinterpret_cast<Bytef*>(output.data()), reinterpret_cast<Bytef*>(output.data()),
&output_size, &output_size,
reinterpret_cast<const Bytef*>(input.data()), reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size()), static_cast<uLongf>(input.size()));
3);
if (res != Z_OK) { if (res != Z_OK) {
error("zlib compress() failed: " + std::to_string(res)); error("zlib compress() failed: " + std::to_string(res));
throw std::runtime_error("zlib compress() failed"); throw std::runtime_error("zlib compress() failed");

View File

@@ -10,8 +10,6 @@
#include <windows.h> #include <windows.h>
#elif defined(__linux__) #elif defined(__linux__)
#include "vdf_parser.hpp" #include "vdf_parser.hpp"
#include <cerrno>
#include <cstring>
#include <pwd.h> #include <pwd.h>
#include <spawn.h> #include <spawn.h>
#include <sys/types.h> #include <sys/types.h>
@@ -53,6 +51,7 @@ std::string GetGamePath() {
std::string Ver = CheckVer(GetGameDir()); std::string Ver = CheckVer(GetGameDir());
Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1)); Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1));
Path += Ver + "\\"; Path += Ver + "\\";
info("Game user path: '" + Path + "'");
return Path; return Path;
} }
#elif defined(__linux__) #elif defined(__linux__)
@@ -65,6 +64,7 @@ std::string GetGamePath() {
std::string Ver = CheckVer(GetGameDir()); std::string Ver = CheckVer(GetGameDir());
Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1)); Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1));
Path += Ver + "/"; Path += Ver + "/";
info("Game user path: '" + Path + "'");
return Path; return Path;
} }
#endif #endif
@@ -92,27 +92,11 @@ void StartGame(std::string Dir) {
} }
#elif defined(__linux__) #elif defined(__linux__)
void StartGame(std::string Dir) { void StartGame(std::string Dir) {
int status;
std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64"); std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64");
char* argv[] = { filename.data(), NULL }; char* argv[] = { filename.data(), NULL };
pid_t pid; pid_t pid;
int result = posix_spawn(&pid, filename.c_str(), NULL, NULL, argv, 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, argv, environ);
if (result != 0) { if (result != 0) {
error("Failed to Launch the game! launcher closing soon"); error("Failed to Launch the game! launcher closing soon");
@@ -122,11 +106,6 @@ void StartGame(std::string Dir) {
error("Game Closed! launcher closing soon"); error("Game Closed! launcher closing soon");
} }
status = posix_spawn_file_actions_destroy(&file_actions);
if (status != 0) {
warn(std::string("posix_spawn_file_actions_destroy failed: ") + std::strerror(errno));
}
std::this_thread::sleep_for(std::chrono::seconds(5)); std::this_thread::sleep_for(std::chrono::seconds(5));
exit(2); exit(2);
} }

View File

@@ -1,9 +0,0 @@
#include "Helpers.h"
std::string bytespan_to_string(ByteSpan span) {
return std::string(span.data(), span.size());
}
std::vector<char> strtovec(std::string_view str) {
return std::vector<char>(str.begin(), str.end());
}

View File

@@ -50,35 +50,35 @@ void addToLog(const std::string& Line) {
} }
void info(const std::string& toPrint) { void info(const std::string& toPrint) {
std::string Print = getDate() + "[INFO] " + toPrint + "\n"; std::string Print = getDate() + "[INFO] " + toPrint + "\n";
std::cout << Print << std::flush; std::cout << Print;
addToLog(Print); addToLog(Print);
} }
void debug(const std::string& toPrint) { void debug(const std::string& toPrint) {
if (!Dev) if (!Dev)
return; return;
std::string Print = getDate() + "[DEBUG] " + toPrint + "\n"; std::string Print = getDate() + "[DEBUG] " + toPrint + "\n";
std::cout << Print << std::flush; std::cout << Print;
addToLog(Print); addToLog(Print);
} }
void warn(const std::string& toPrint) { void warn(const std::string& toPrint) {
std::string Print = getDate() + "[WARN] " + toPrint + "\n"; std::string Print = getDate() + "[WARN] " + toPrint + "\n";
std::cout << Print << std::flush; std::cout << Print;
addToLog(Print); addToLog(Print);
} }
void error(const std::string& toPrint) { void error(const std::string& toPrint) {
std::string Print = getDate() + "[ERROR] " + toPrint + "\n"; std::string Print = getDate() + "[ERROR] " + toPrint + "\n";
std::cout << Print << std::flush; std::cout << Print;
addToLog(Print); addToLog(Print);
} }
void fatal(const std::string& toPrint) { void fatal(const std::string& toPrint) {
std::string Print = getDate() + "[FATAL] " + toPrint + "\n"; std::string Print = getDate() + "[FATAL] " + toPrint + "\n";
std::cout << Print << std::flush; std::cout << Print;
addToLog(Print); addToLog(Print);
std::this_thread::sleep_for(std::chrono::seconds(5)); std::this_thread::sleep_for(std::chrono::seconds(5));
_Exit(-1); _Exit(-1);
} }
void except(const std::string& toPrint) { void except(const std::string& toPrint) {
std::string Print = getDate() + "[EXCEP] " + toPrint + "\n"; std::string Print = getDate() + "[EXCEP] " + toPrint + "\n";
std::cout << Print << std::flush; std::cout << Print;
addToLog(Print); addToLog(Print);
} }

View File

@@ -7,17 +7,18 @@
/// ///
#include "Http.h" #include "Http.h"
#include "Network/network.hpp" #include "Network/network.hpp"
#include "NetworkHelpers.h"
#include "Security/Init.h" #include "Security/Init.h"
#include <asio/io_context.hpp>
#include <cstdlib> #include <cstdlib>
#include <optional>
#include <regex> #include <regex>
#if defined(__linux__) #if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(__linux__)
#include <cstring> #include <cstring>
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
#include <spawn.h> #include <spawn.h>
#include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
@@ -26,7 +27,6 @@
#include "Logger.h" #include "Logger.h"
#include "Startup.h" #include "Startup.h"
#include <charconv> #include <charconv>
#include <fmt/format.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <set> #include <set>
#include <thread> #include <thread>
@@ -39,122 +39,71 @@ bool Terminate = false;
bool LoginAuth = false; bool LoginAuth = false;
std::string Username = ""; std::string Username = "";
std::string UserRole = ""; std::string UserRole = "";
int UserID = -1;
std::string UlStatus; std::string UlStatus;
std::string MStatus; std::string MStatus;
bool ModLoaded; bool ModLoaded;
int ping = -1; int ping = -1;
asio::io_context io {};
static asio::ip::tcp::socket ResolveAndConnect(const std::string& host_port_string) {
using namespace asio;
ip::tcp::resolver resolver(io);
asio::error_code ec;
auto port = host_port_string.substr(host_port_string.find_last_of(':') + 1);
auto host = host_port_string.substr(0, host_port_string.find_last_of(':'));
auto resolved = resolver.resolve(host, port, ec);
if (ec) {
::error(fmt::format("Failed to resolve '[{}]:{}': {}", host, port, ec.message()));
throw std::runtime_error(fmt::format("Failed to resolve '{}': {}", host_port_string, ec.message()));
}
bool connected = false;
UlStatus = "UlLoading...";
for (const auto& addr : resolved) {
try {
info(fmt::format("Resolved and connected to '[{}]:{}'",
addr.endpoint().address().to_string(),
addr.endpoint().port()));
ip::tcp::socket socket(io);
socket.connect(addr);
// done, connected fine
return socket;
} catch (...) {
// ignore
}
}
throw std::runtime_error(fmt::format("Failed to connect to '{}'; connection refused", host_port_string));
}
void StartSync(const std::string& Data) { void StartSync(const std::string& Data) {
try { std::string IP = GetAddr(Data.substr(1, Data.find(':') - 1));
auto Socket = ResolveAndConnect(Data.substr(1)); if (IP.find('.') == -1) {
if (IP == "DNS")
UlStatus = "UlConnection Failed! (DNS Lookup Failed)";
else
UlStatus = "UlConnection Failed! (WSA failed to start)";
ListOfMods = "-"; ListOfMods = "-";
CheckLocalKey();
TCPTerminate = false;
Terminate = false;
ConfList->clear();
ping = -1;
std::thread GS(TCPGameServer, std::move(Socket));
GS.detach();
info("Connecting to server");
} catch (const std::exception& e) {
UlStatus = "UlConnection Failed!";
error(fmt::format("Client: connect failed! Error: {}", e.what()));
WSACleanup();
Terminate = true; Terminate = true;
return;
} }
CheckLocalKey();
UlStatus = "UlLoading...";
TCPTerminate = false;
Terminate = false;
ConfList->clear();
ping = -1;
std::thread GS(TCPGameServer, IP, std::stoi(Data.substr(Data.find(':') + 1)));
GS.detach();
info("Connecting to server");
} }
bool IsAllowedLink(const std::string& Link) { bool IsAllowedLink(const std::string& Link) {
std::vector<std::string> allowed_links = { std::regex link_pattern(R"(https:\/\/(?:\w+)?(?:\.)?(?:beammp\.com|discord\.gg))");
R"(patreon\.com\/beammp$)", std::smatch link_match;
R"(discord\.gg\/beammp$)", return std::regex_search(Link, link_match, link_pattern) && link_match.position() == 0;
R"(forum\.beammp\.com$)",
R"(beammp\.com$)",
R"(patreon\.com\/beammp\/$)",
R"(discord\.gg\/beammp\/$)",
R"(forum\.beammp\.com\/$)",
R"(beammp\.com\/$)",
R"(docs\.beammp\.com$)",
R"(wiki\.beammp\.com$)",
R"(docs\.beammp\.com\/$)",
R"(wiki\.beammp\.com\/$)",
R"(docs\.beammp\.com\/.*$)",
R"(wiki\.beammp\.com\/.*$)",
};
for (const auto& allowed_link : allowed_links) {
if (std::regex_match(Link, std::regex(std::string(R"(^http(s)?:\/\/)") + allowed_link))) {
return true;
}
}
return false;
} }
void Parse(std::span<char> InData, asio::ip::tcp::socket& CSocket) { void Parse(std::string Data, SOCKET CSocket) {
std::string OutData; char Code = Data.at(0), SubCode = 0;
char Code = InData[0], SubCode = 0; if (Data.length() > 1)
if (InData.size() > 1) SubCode = Data.at(1);
SubCode = InData[1];
switch (Code) { switch (Code) {
case 'A': case 'A':
OutData = "A"; Data = Data.substr(0, 1);
break; break;
case 'B': case 'B':
NetReset(); NetReset();
Terminate = true; Terminate = true;
TCPTerminate = true; TCPTerminate = true;
OutData = Code + HTTP::Get("https://backend.beammp.com/servers-info"); Data = Code + HTTP::Get("https://backend.beammp.com/servers-info");
break; break;
case 'C': case 'C':
ListOfMods.clear(); ListOfMods.clear();
StartSync(std::string(InData.data(), InData.size())); StartSync(Data);
while (ListOfMods.empty() && !Terminate) { while (ListOfMods.empty() && !Terminate) {
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
} }
if (ListOfMods == "-") if (ListOfMods == "-")
OutData = "L"; Data = "L";
else else
OutData = "L" + ListOfMods; Data = "L" + ListOfMods;
break; break;
case 'O': // open default browser with URL case 'O': // open default browser with URL
if (IsAllowedLink(bytespan_to_string(InData.subspan(1)))) { if (IsAllowedLink(Data.substr(1))) {
#if defined(__linux) #if defined(__linux)
if (char* browser = getenv("BROWSER"); browser != nullptr && !std::string_view(browser).empty()) { if (char* browser = getenv("BROWSER"); browser != nullptr && !std::string_view(browser).empty()) {
pid_t pid; pid_t pid;
auto arg = bytespan_to_string(InData.subspan(1)); auto arg = Data.substr(1);
char* argv[] = { browser, arg.data() }; char* argv[] = { browser, arg.data() };
auto status = posix_spawn(&pid, browser, nullptr, nullptr, argv, environ); auto status = posix_spawn(&pid, browser, nullptr, nullptr, argv, environ);
if (status == 0) { if (status == 0) {
@@ -166,27 +115,27 @@ void Parse(std::span<char> InData, asio::ip::tcp::socket& CSocket) {
error(std::string("posix_spawn: ") + strerror(status)); error(std::string("posix_spawn: ") + strerror(status));
} }
} else { } else {
error("Failed to open the following link in the browser because the $BROWSER environment variable is not set: " + bytespan_to_string(InData.subspan(1))); error("Failed to open the following link in the browser because the $BROWSER environment variable is not set: " + Data.substr(1));
} }
#elif defined(WIN32) #elif defined(WIN32)
ShellExecuteA(nullptr, "open", InData.subspan(1).data(), nullptr, nullptr, SW_SHOW); /// TODO: Look at when working on linux port ShellExecuteA(nullptr, "open", Data.substr(1).c_str(), nullptr, nullptr, SW_SHOW); /// TODO: Look at when working on linux port
#endif #endif
info("Opening Link \"" + bytespan_to_string(InData.subspan(1)) + "\""); info("Opening Link \"" + Data.substr(1) + "\"");
} }
OutData.clear(); Data.clear();
break; break;
case 'P': case 'P':
OutData = Code + std::to_string(ProxyPort); Data = Code + std::to_string(ProxyPort);
break; break;
case 'U': case 'U':
if (SubCode == 'l') if (SubCode == 'l')
OutData = UlStatus; Data = UlStatus;
if (SubCode == 'p') { if (SubCode == 'p') {
if (ping > 800) { if (ping > 800) {
OutData = "Up-2"; Data = "Up-2";
} else } else
OutData = "Up" + std::to_string(ping); Data = "Up" + std::to_string(ping);
} }
if (!SubCode) { if (!SubCode) {
std::string Ping; std::string Ping;
@@ -194,11 +143,11 @@ void Parse(std::span<char> InData, asio::ip::tcp::socket& CSocket) {
Ping = "-2"; Ping = "-2";
else else
Ping = std::to_string(ping); Ping = std::to_string(ping);
OutData = std::string(UlStatus) + "\n" + "Up" + Ping; Data = std::string(UlStatus) + "\n" + "Up" + Ping;
} }
break; break;
case 'M': case 'M':
OutData = MStatus; Data = MStatus;
break; break;
case 'Q': case 'Q':
if (SubCode == 'S') { if (SubCode == 'S') {
@@ -209,19 +158,17 @@ void Parse(std::span<char> InData, asio::ip::tcp::socket& CSocket) {
} }
if (SubCode == 'G') if (SubCode == 'G')
exit(2); exit(2);
OutData.clear(); Data.clear();
break; break;
case 'R': // will send mod name case 'R': // will send mod name
{ if (ConfList->find(Data) == ConfList->end()) {
auto str = bytespan_to_string(InData); ConfList->insert(Data);
if (ConfList->find(str) == ConfList->end()) {
ConfList->insert(str);
ModLoaded = true; ModLoaded = true;
} }
OutData.clear(); Data.clear();
} break; break;
case 'Z': case 'Z':
OutData = "Z" + GetVer(); Data = "Z" + GetVer();
break; break;
case 'N': case 'N':
if (SubCode == 'c') { if (SubCode == 'c') {
@@ -234,39 +181,66 @@ void Parse(std::span<char> InData, asio::ip::tcp::socket& CSocket) {
if (!UserRole.empty()) { if (!UserRole.empty()) {
Auth["role"] = UserRole; Auth["role"] = UserRole;
} }
OutData = "N" + Auth.dump(); if (UserID != -1) {
Auth["id"] = UserID;
}
Data = "N" + Auth.dump();
} else { } else {
auto indata_str = bytespan_to_string(InData); Data = "N" + Login(Data.substr(Data.find(':') + 1));
OutData = "N" + Login(indata_str.substr(indata_str.find(':') + 1));
} }
break; break;
default: default:
OutData.clear(); Data.clear();
break; break;
} }
if (!OutData.empty() && CSocket.is_open()) { if (!Data.empty() && CSocket != -1) {
uint32_t DataSize = OutData.size(); int res = send(CSocket, (Data + "\n").c_str(), int(Data.size()) + 1, 0);
std::vector<char> ToSend(sizeof(DataSize) + OutData.size()); if (res < 0) {
std::copy_n(reinterpret_cast<char*>(&DataSize), sizeof(DataSize), ToSend.begin()); debug("(Core) send failed with error: " + std::to_string(WSAGetLastError()));
std::copy_n(OutData.data(), OutData.size(), ToSend.begin() + sizeof(DataSize));
asio::error_code ec;
asio::write(CSocket, asio::buffer(ToSend), ec);
if (ec) {
debug(fmt::format("(Core) send failed with error: {}", ec.message()));
} }
} }
} }
void GameHandler(asio::ip::tcp::socket& Client) { void GameHandler(SOCKET Client) {
std::vector<char> data {};
int32_t Size, Temp, Rcv;
char Header[10] = { 0 };
do { do {
try { Rcv = 0;
ReceiveFromGame(Client, data); do {
Parse(data, Client); Temp = recv(Client, &Header[Rcv], 1, 0);
} catch (const std::exception& e) { if (Temp < 1)
error(std::string("Error while receiving from game: ") + e.what()); 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));
break; break;
} }
} while (true); 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()));
}
NetReset(); NetReset();
KillSocket(Client); KillSocket(Client);
} }
@@ -282,52 +256,63 @@ void localRes() {
} }
void CoreMain() { void CoreMain() {
debug("Core Network on start!"); debug("Core Network on start!");
SOCKET LSocket, CSocket;
struct addrinfo* res = nullptr;
struct addrinfo hints { };
int iRes;
#ifdef _WIN32
WSADATA wsaData;
iRes = WSAStartup(514, &wsaData); // 2.2
if (iRes)
debug("WSAStartup failed with error: " + std::to_string(iRes));
#endif
asio::ip::tcp::endpoint listen_ep(asio::ip::address::from_string("0.0.0.0"), static_cast<uint16_t>(DEFAULT_PORT)); ZeroMemory(&hints, sizeof(hints));
asio::ip::tcp::socket LSocket(io);
asio::error_code ec;
LSocket.open(listen_ep.protocol(), ec);
if (ec) {
::error(fmt::format("Failed to open core socket: {}", ec.message()));
return;
}
asio::ip::tcp::socket::linger linger_opt {};
linger_opt.enabled(false);
LSocket.set_option(linger_opt, ec);
if (ec) {
::error(fmt::format("Failed to set up listening core socket to not linger / reuse address. "
"This may cause the core socket to refuse to bind(). Error: {}",
ec.message()));
return;
}
asio::ip::tcp::socket::reuse_address reuse_opt { true };
LSocket.set_option(reuse_opt, ec);
if (ec) {
::error(fmt::format("Failed to set up listening core socket to not linger / reuse address. "
"This may cause the core socket to refuse to bind(). Error: {}",
ec.message()));
return;
}
auto acceptor = asio::ip::tcp::acceptor(io, listen_ep); hints.ai_family = AF_UNSPEC;
acceptor.listen(asio::ip::tcp::socket::max_listen_connections, ec); hints.ai_socktype = SOCK_STREAM;
if (ec) { hints.ai_protocol = IPPROTO_TCP;
::error(fmt::format("listen() failed, which is needed for the launcher to operate. Error: {}", hints.ai_flags = AI_PASSIVE;
ec.message())); iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT).c_str(), &hints, &res);
if (iRes) {
debug("(Core) addr info failed with error: " + std::to_string(iRes));
WSACleanup();
return;
}
LSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (LSocket == -1) {
debug("(Core) socket failed with error: " + std::to_string(WSAGetLastError()));
freeaddrinfo(res);
WSACleanup();
return;
}
iRes = bind(LSocket, res->ai_addr, int(res->ai_addrlen));
if (iRes == SOCKET_ERROR) {
error("(Core) bind failed with error: " + std::to_string(WSAGetLastError()));
freeaddrinfo(res);
KillSocket(LSocket);
WSACleanup();
return;
}
iRes = listen(LSocket, SOMAXCONN);
if (iRes == SOCKET_ERROR) {
debug("(Core) listen failed with error: " + std::to_string(WSAGetLastError()));
freeaddrinfo(res);
KillSocket(LSocket);
WSACleanup();
return; return;
} }
do { do {
auto CSocket = acceptor.accept(ec); CSocket = accept(LSocket, nullptr, nullptr);
if (ec) { if (CSocket == -1) {
error(fmt::format("(Core) accept failed with error: {}", ec.message())); error("(Core) accept failed with error: " + std::to_string(WSAGetLastError()));
continue; continue;
} }
localRes(); localRes();
info("Game Connected!"); info("Game Connected!");
GameHandler(CSocket); GameHandler(CSocket);
warn("Game Reconnecting..."); warn("Game Reconnecting...");
} while (LSocket.is_open()); } while (CSocket);
KillSocket(LSocket); KillSocket(LSocket);
WSACleanup(); WSACleanup();
} }

View File

@@ -7,6 +7,7 @@
/// ///
#include <string> #include <string>
#include "IPRegex.h"
#if defined(_WIN32) #if defined(_WIN32)
#include <winsock2.h> #include <winsock2.h>
@@ -19,8 +20,9 @@
#include "Logger.h" #include "Logger.h"
std::string GetAddr(const std::string& IP) { std::string GetAddr(const std::string& IP) {
if (IP.find_first_not_of("0123456789.") == -1) if (!std::regex_match(IP, IP_REGEX)) {
return IP; return IP;
}
hostent* host; hostent* host;
#ifdef _WIN32 #ifdef _WIN32
WSADATA wsaData; WSADATA wsaData;
@@ -40,4 +42,4 @@ std::string GetAddr(const std::string& IP) {
std::string Ret = inet_ntoa(*((struct in_addr*)host->h_addr)); std::string Ret = inet_ntoa(*((struct in_addr*)host->h_addr));
WSACleanup(); WSACleanup();
return Ret; return Ret;
} }

View File

@@ -5,13 +5,7 @@
/// ///
/// Created by Anonymous275 on 7/25/2020 /// Created by Anonymous275 on 7/25/2020
/// ///
#include "Helpers.h"
#include "Network/network.hpp" #include "Network/network.hpp"
#include "NetworkHelpers.h"
#include "asio/socket_base.hpp"
#include <algorithm>
#include <span>
#include <vector>
#include <zlib.h> #include <zlib.h>
#if defined(_WIN32) #if defined(_WIN32)
#include <winsock2.h> #include <winsock2.h>
@@ -28,7 +22,6 @@
#include "Logger.h" #include "Logger.h"
#include <charconv> #include <charconv>
#include <fmt/format.h>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
@@ -36,31 +29,20 @@
std::chrono::time_point<std::chrono::high_resolution_clock> PingStart, PingEnd; std::chrono::time_point<std::chrono::high_resolution_clock> PingStart, PingEnd;
bool GConnected = false; bool GConnected = false;
bool CServer = true; bool CServer = true;
std::shared_ptr<asio::ip::tcp::socket> CSocket = nullptr; SOCKET CSocket = -1;
std::shared_ptr<asio::ip::tcp::socket> GSocket = nullptr; SOCKET GSocket = -1;
void KillSocket(std::shared_ptr<asio::ip::tcp::socket>& Dead) { int KillSocket(uint64_t Dead) {
if (!Dead) if (Dead == (SOCKET)-1) {
return; debug("Kill socket got -1 returning...");
asio::error_code ec; return 0;
Dead->shutdown(asio::socket_base::shutdown_both, ec); }
} shutdown(Dead, SD_BOTH);
int a = closesocket(Dead);
void KillSocket(std::shared_ptr<asio::ip::udp::socket>& Dead) { if (a != 0) {
if (!Dead) warn("Failed to close socket!");
return; }
asio::error_code ec; return a;
Dead->shutdown(asio::socket_base::shutdown_both, ec);
}
void KillSocket(asio::ip::tcp::socket& Dead) {
asio::error_code ec;
Dead.shutdown(asio::socket_base::shutdown_both, ec);
}
void KillSocket(asio::ip::udp::socket& Dead) {
asio::error_code ec;
Dead.shutdown(asio::socket_base::shutdown_both, ec);
} }
bool CheckBytes(uint32_t Bytes) { bool CheckBytes(uint32_t Bytes) {
@@ -74,34 +56,42 @@ bool CheckBytes(uint32_t Bytes) {
return true; return true;
} }
void GameSend(std::string_view RawData) { void GameSend(std::string_view Data) {
static std::mutex Lock; static std::mutex Lock;
std::scoped_lock Guard(Lock); std::scoped_lock Guard(Lock);
if (TCPTerminate || !GConnected || CSocket == nullptr) if (TCPTerminate || !GConnected || CSocket == -1)
return; return;
int32_t Size, Temp, Sent; int32_t Size, Temp, Sent;
uint32_t DataSize = RawData.size(); Size = int32_t(Data.size());
std::vector<char> Data(sizeof(DataSize) + RawData.size());
std::copy_n(reinterpret_cast<char*>(&DataSize), sizeof(DataSize), Data.begin());
std::copy_n(RawData.data(), RawData.size(), Data.begin() + sizeof(DataSize));
Size = Data.size();
Sent = 0; Sent = 0;
#ifdef DEBUG
asio::error_code ec; if (Size > 1000) {
asio::write(*CSocket, asio::buffer(Data), ec); debug("Launcher -> game (" + std::to_string(Size) + ")");
if (ec) { }
debug(fmt::format("(TCP CB) recv failed with error: {}", ec.message())); #endif
KillSocket(TCPSock); do {
Terminate = true; 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;
} }
} }
void ServerSend(std::string Data, bool Rel) {
void ServerSend(const std::vector<char>& Data, bool Rel) {
if (Terminate || Data.empty()) if (Terminate || Data.empty())
return; return;
if (Data.find("Zp") != std::string::npos && Data.size() > 500) {
abort();
}
char C = 0; char C = 0;
bool Ack = false; bool Ack = false;
int DLen = int(Data.size()); int DLen = int(Data.length());
if (DLen > 3) if (DLen > 3)
C = Data.at(0); C = Data.at(0);
if (C == 'O' || C == 'T') if (C == 'O' || C == 'T')
@@ -113,10 +103,18 @@ void ServerSend(const std::vector<char>& Data, bool Rel) {
if (Ack || Rel) { if (Ack || Rel) {
if (Ack || DLen > 1000) if (Ack || DLen > 1000)
SendLarge(Data); SendLarge(Data);
else if (TCPSock) else
TCPSend(Data, *TCPSock); TCPSend(Data, TCPSock);
} else } else
UDPSend(Data); 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() { void NetReset() {
@@ -125,23 +123,76 @@ void NetReset() {
Terminate = false; Terminate = false;
UlStatus = "Ulstart"; UlStatus = "Ulstart";
MStatus = " "; MStatus = " ";
if (UDPSock != nullptr) { if (UDPSock != (SOCKET)(-1)) {
KillSocket(*UDPSock); debug("Terminating UDP Socket : " + std::to_string(TCPSock));
KillSocket(UDPSock);
} }
UDPSock = nullptr; UDPSock = -1;
if (TCPSock != nullptr) { if (TCPSock != (SOCKET)(-1)) {
KillSocket(*TCPSock); debug("Terminating TCP Socket : " + std::to_string(TCPSock));
KillSocket(TCPSock);
} }
TCPSock = nullptr; TCPSock = -1;
if (GSocket != nullptr) { if (GSocket != (SOCKET)(-1)) {
KillSocket(*GSocket); debug("Terminating GTCP Socket : " + std::to_string(GSocket));
KillSocket(GSocket);
} }
GSocket = nullptr; GSocket = -1;
} }
SOCKET SetupListener() {
if (GSocket != -1)
return GSocket;
struct addrinfo* result = nullptr;
struct addrinfo hints { };
int iRes;
#ifdef _WIN32
WSADATA wsaData;
iRes = WSAStartup(514, &wsaData); // 2.2
if (iRes != 0) {
error("(Proxy) WSAStartup failed with error: " + std::to_string(iRes));
return -1;
}
#endif
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT + 1).c_str(), &hints, &result);
if (iRes != 0) {
error("(Proxy) info failed with error: " + std::to_string(iRes));
WSACleanup();
}
GSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (GSocket == -1) {
error("(Proxy) socket failed with error: " + std::to_string(WSAGetLastError()));
freeaddrinfo(result);
WSACleanup();
return -1;
}
iRes = bind(GSocket, result->ai_addr, (int)result->ai_addrlen);
if (iRes == SOCKET_ERROR) {
error("(Proxy) bind failed with error: " + std::to_string(WSAGetLastError()));
freeaddrinfo(result);
KillSocket(GSocket);
WSACleanup();
return -1;
}
freeaddrinfo(result);
iRes = listen(GSocket, SOMAXCONN);
if (iRes == SOCKET_ERROR) {
error("(Proxy) listen failed with error: " + std::to_string(WSAGetLastError()));
KillSocket(GSocket);
WSACleanup();
return -1;
}
return GSocket;
}
void AutoPing() { void AutoPing() {
while (!Terminate) { while (!Terminate) {
ServerSend(strtovec("p"), false); ServerSend("p", false);
PingStart = std::chrono::high_resolution_clock::now(); PingStart = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
} }
@@ -173,47 +224,17 @@ void ParserAsync(std::string_view Data) {
void ServerParser(std::string_view Data) { void ServerParser(std::string_view Data) {
ParserAsync(Data); ParserAsync(Data);
} }
void NetMain(asio::ip::address addr, uint16_t port) { void NetMain(const std::string& IP, int Port) {
std::thread Ping(AutoPing); std::thread Ping(AutoPing);
Ping.detach(); Ping.detach();
UDPClientMain(addr, port); UDPClientMain(IP, Port);
CServer = true; CServer = true;
Terminate = true; Terminate = true;
info("Connection Terminated!"); info("Connection Terminated!");
} }
void TCPGameServer(asio::ip::tcp::socket&& Socket) { void TCPGameServer(const std::string& IP, int Port) {
asio::ip::tcp::endpoint listen_ep(asio::ip::address::from_string("0.0.0.0"), static_cast<uint16_t>(DEFAULT_PORT + 1)); GSocket = SetupListener();
asio::ip::tcp::socket listener(io); while (!TCPTerminate && GSocket != -1) {
asio::error_code ec;
listener.open(listen_ep.protocol(), ec);
if (ec) {
::error(fmt::format("Failed to open game socket: {}", ec.message()));
return;
}
asio::ip::tcp::socket::linger linger_opt {};
linger_opt.enabled(false);
listener.set_option(linger_opt, ec);
if (ec) {
::error(fmt::format("Failed to set up listening game socket to not linger / reuse address. "
"This may cause the game socket to refuse to bind(). Error: {}",
ec.message()));
}
asio::ip::tcp::socket::reuse_address reuse_opt { true };
listener.set_option(reuse_opt, ec);
if (ec) {
::error(fmt::format("Failed to set up listening core socket to not linger / reuse address. "
"This may cause the core socket to refuse to bind(). Error: {}",
ec.message()));
return;
}
auto acceptor = asio::ip::tcp::acceptor(io, listen_ep);
acceptor.listen(asio::ip::tcp::socket::max_listen_connections, ec);
if (ec) {
debug(fmt::format("Proxy accept failed: {}", ec.message()));
TCPTerminate = true; // skip the loop
}
debug(fmt::format("Game server listening on {}:{}", acceptor.local_endpoint().address().to_string(), acceptor.local_endpoint().port()));
while (!TCPTerminate && acceptor.is_open()) {
debug("MAIN LOOP OF GAME SERVER"); debug("MAIN LOOP OF GAME SERVER");
GConnected = false; GConnected = false;
if (!CServer) { if (!CServer) {
@@ -224,35 +245,62 @@ void TCPGameServer(asio::ip::tcp::socket&& Socket) {
break; break;
} }
if (CServer) { if (CServer) {
std::thread Client(TCPClientMain, std::move(Socket)); std::thread Client(TCPClientMain, IP, Port);
Client.detach(); Client.detach();
} }
CSocket = accept(GSocket, nullptr, nullptr);
CSocket = std::make_shared<asio::ip::tcp::socket>(acceptor.accept()); if (CSocket == -1) {
debug("(Proxy) accept failed with error: " + std::to_string(WSAGetLastError()));
break;
}
debug("(Proxy) Game Connected!"); debug("(Proxy) Game Connected!");
GConnected = true; GConnected = true;
if (CServer) { if (CServer) {
std::thread t1(NetMain, CSocket->remote_endpoint().address(), CSocket->remote_endpoint().port()); std::thread t1(NetMain, IP, Port);
t1.detach(); t1.detach();
CServer = false; CServer = false;
} }
std::vector<char> data {}; int32_t Size, Temp, Rcv;
char Header[10] = { 0 };
// Read byte by byte until '>' is rcved then get the size and read based on it // Read byte by byte until '>' is rcved then get the size and read based on it
while (!TCPTerminate && !CSocket) { do {
try { Rcv = 0;
ReceiveFromGame(*CSocket, data);
ServerSend(data, false); do {
} catch (const std::exception& e) { Temp = recv(CSocket, &Header[Rcv], 1, 0);
error(std::string("Error while receiving from game: ") + e.what()); 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));
break; 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()));
} }
TCPTerminate = true; TCPTerminate = true;
GConnected = false; GConnected = false;
Terminate = true; Terminate = true;
if (CSocket != nullptr) if (CSocket != SOCKET_ERROR)
KillSocket(CSocket); KillSocket(CSocket);
debug("END OF GAME SERVER"); debug("END OF GAME SERVER");
} }

View File

@@ -5,7 +5,6 @@
/// ///
/// Created by Anonymous275 on 7/18/2020 /// Created by Anonymous275 on 7/18/2020
/// ///
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "Http.h" #include "Http.h"
#include <Logger.h> #include <Logger.h>

View File

@@ -7,7 +7,6 @@
/// ///
#include "Network/network.hpp" #include "Network/network.hpp"
#include "fmt/core.h"
#if defined(_WIN32) #if defined(_WIN32)
#include <ws2tcpip.h> #include <ws2tcpip.h>
@@ -28,7 +27,6 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <future> #include <future>
#include <asio.hpp>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <thread> #include <thread>
@@ -74,8 +72,8 @@ void Abord() {
info("Terminated!"); info("Terminated!");
} }
std::string Auth(asio::ip::tcp::socket& Sock) { std::string Auth(SOCKET Sock) {
TCPSend(strtovec("VC" + GetVer()), Sock); TCPSend("VC" + GetVer(), Sock);
auto Res = TCPRcv(Sock); auto Res = TCPRcv(Sock);
@@ -84,7 +82,7 @@ std::string Auth(asio::ip::tcp::socket& Sock) {
return ""; return "";
} }
TCPSend(strtovec(PublicKey), Sock); TCPSend(PublicKey, Sock);
if (Terminate) if (Terminate)
return ""; return "";
@@ -102,7 +100,7 @@ std::string Auth(asio::ip::tcp::socket& Sock) {
UUl("Authentication failed!"); UUl("Authentication failed!");
return ""; return "";
} }
TCPSend(strtovec("SR"), Sock); TCPSend("SR", Sock);
if (Terminate) if (Terminate)
return ""; return "";
@@ -116,7 +114,7 @@ std::string Auth(asio::ip::tcp::socket& Sock) {
if (Res.empty() || Res == "-") { if (Res.empty() || Res == "-") {
info("Didn't Receive any mods..."); info("Didn't Receive any mods...");
ListOfMods = "-"; ListOfMods = "-";
TCPSend(strtovec("Done"), Sock); TCPSend("Done", Sock);
info("Done!"); info("Done!");
return ""; return "";
} }
@@ -139,18 +137,22 @@ void AsyncUpdate(uint64_t& Rcv, uint64_t Size, const std::string& Name) {
} while (!Terminate && Rcv < Size); } while (!Terminate && Rcv < Size);
} }
char* TCPRcvRaw(asio::ip::tcp::socket& Sock, uint64_t& GRcv, uint64_t Size) { char* TCPRcvRaw(SOCKET Sock, uint64_t& GRcv, uint64_t Size) {
if (Sock == -1) {
Terminate = true;
UUl("Invalid Socket");
return nullptr;
}
char* File = new char[Size]; char* File = new char[Size];
uint64_t Rcv = 0; uint64_t Rcv = 0;
asio::error_code ec;
do { do {
int Len = int(Size - Rcv); int Len = int(Size - Rcv);
if (Len > 1000000) if (Len > 1000000)
Len = 1000000; Len = 1000000;
int32_t Temp = asio::read(Sock, asio::buffer(&File[Rcv], Len), ec); int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL);
if (ec) { if (Temp < 1) {
::error(fmt::format("Failed to receive data from server: {}", ec.message())); info(std::to_string(Temp));
UUl("Failed to receive data from server, connection closed (Code 1)"); UUl("Socket Closed Code 1");
KillSocket(Sock); KillSocket(Sock);
Terminate = true; Terminate = true;
delete[] File; delete[] File;
@@ -161,23 +163,29 @@ char* TCPRcvRaw(asio::ip::tcp::socket& Sock, uint64_t& GRcv, uint64_t Size) {
} while (Rcv < Size && !Terminate); } while (Rcv < Size && !Terminate);
return File; return File;
} }
void MultiKill(asio::ip::tcp::socket& Sock, asio::ip::tcp::socket& Sock1) { void MultiKill(SOCKET Sock, SOCKET Sock1) {
KillSocket(Sock1); KillSocket(Sock1);
KillSocket(Sock); KillSocket(Sock);
Terminate = true; Terminate = true;
} }
std::shared_ptr<asio::ip::tcp::socket> InitDSock(asio::ip::tcp::endpoint ep) { SOCKET InitDSock() {
auto DSock = std::make_shared<asio::ip::tcp::socket>(io); SOCKET DSock = socket(AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
asio::error_code ec; SOCKADDR_IN ServerAddr;
DSock->connect(ep, ec); if (DSock < 1) {
if (ec) {
KillSocket(DSock); KillSocket(DSock);
Terminate = true; Terminate = true;
return nullptr; return 0;
}
ServerAddr.sin_family = AF_UNSPEC;
ServerAddr.sin_port = htons(LastPort);
inet_pton(AF_UNSPEC, LastIP.c_str(), &ServerAddr.sin_addr);
if (connect(DSock, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) != 0) {
KillSocket(DSock);
Terminate = true;
return 0;
} }
char Code[2] = { 'D', char(ClientID) }; char Code[2] = { 'D', char(ClientID) };
asio::write(*DSock, asio::buffer(Code, 2), ec); if (send(DSock, Code, 2, 0) != 2) {
if (ec) {
KillSocket(DSock); KillSocket(DSock);
Terminate = true; Terminate = true;
return 0; return 0;
@@ -185,7 +193,7 @@ std::shared_ptr<asio::ip::tcp::socket> InitDSock(asio::ip::tcp::endpoint ep) {
return DSock; return DSock;
} }
std::string MultiDownload(asio::ip::tcp::socket& MSock, asio::ip::tcp::socket& DSock, uint64_t Size, const std::string& Name) { std::string MultiDownload(SOCKET MSock, SOCKET DSock, uint64_t Size, const std::string& Name) {
uint64_t GRcv = 0, MSize = Size / 2, DSize = Size - MSize; uint64_t GRcv = 0, MSize = Size / 2, DSize = Size - MSize;
@@ -231,7 +239,7 @@ void InvalidResource(const std::string& File) {
Terminate = true; Terminate = true;
} }
void SyncResources(asio::ip::tcp::socket& Sock) { void SyncResources(SOCKET Sock) {
std::string Ret = Auth(Sock); std::string Ret = Auth(Sock);
if (Ret.empty()) if (Ret.empty())
return; return;
@@ -270,7 +278,7 @@ void SyncResources(asio::ip::tcp::socket& Sock) {
} }
if (!FNames.empty()) if (!FNames.empty())
info("Syncing..."); info("Syncing...");
auto DSock = InitDSock(Sock.remote_endpoint()); SOCKET DSock = InitDSock();
for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) { for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) {
auto pos = FN->find_last_of('/'); auto pos = FN->find_last_of('/');
if (pos != std::string::npos) { if (pos != std::string::npos) {
@@ -312,7 +320,7 @@ void SyncResources(asio::ip::tcp::socket& Sock) {
CheckForDir(); CheckForDir();
std::string FName = a.substr(a.find_last_of('/')); std::string FName = a.substr(a.find_last_of('/'));
do { do {
TCPSend(strtovec("f" + *FN), Sock); TCPSend("f" + *FN, Sock);
std::string Data = TCPRcv(Sock); std::string Data = TCPRcv(Sock);
if (Data == "CO" || Terminate) { if (Data == "CO" || Terminate) {
@@ -323,7 +331,7 @@ void SyncResources(asio::ip::tcp::socket& Sock) {
std::string Name = std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName; std::string Name = std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName;
Data = MultiDownload(Sock, *DSock, std::stoull(*FS), Name); Data = MultiDownload(Sock, DSock, std::stoull(*FS), Name);
if (Terminate) if (Terminate)
break; break;
@@ -354,7 +362,7 @@ void SyncResources(asio::ip::tcp::socket& Sock) {
} }
KillSocket(DSock); KillSocket(DSock);
if (!Terminate) { if (!Terminate) {
TCPSend(strtovec("Done"), Sock); TCPSend("Done", Sock);
info("Done!"); info("Done!");
} else { } else {
UlStatus = "Ulstart"; UlStatus = "Ulstart";

View File

@@ -7,8 +7,6 @@
/// ///
#include "Network/network.hpp" #include "Network/network.hpp"
#include "Zlib/Compressor.h" #include "Zlib/Compressor.h"
#include "asio/ip/address.hpp"
#include "fmt/format.h"
#if defined(_WIN32) #if defined(_WIN32)
#include <ws2tcpip.h> #include <ws2tcpip.h>
@@ -25,40 +23,28 @@
#include <array> #include <array>
#include <string> #include <string>
std::shared_ptr<asio::ip::udp::socket> UDPSock = nullptr; SOCKET UDPSock = -1;
sockaddr_in* ToServer = nullptr;
void UDPSend(const std::vector<char>& RawData) { void UDPSend(std::string Data) {
if (ClientID == -1 || UDPSock == nullptr) if (ClientID == -1 || UDPSock == -1)
return; return;
std::string Data; if (Data.length() > 400) {
if (Data.size() > 400) { auto res = Comp(std::span<char>(Data.data(), Data.size()));
auto res = Comp(RawData);
Data = "ABG:" + std::string(res.data(), res.size()); 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; std::string Packet = char(ClientID + 1) + std::string(":") + Data;
int sendOk = UDPSock->send(asio::buffer(Packet)); int sendOk = sendto(UDPSock, Packet.c_str(), int(Packet.size()), 0, (sockaddr*)ToServer, sizeof(*ToServer));
if (sendOk == SOCKET_ERROR) if (sendOk == SOCKET_ERROR)
error("Error Code : " + std::to_string(WSAGetLastError())); error("Error Code : " + std::to_string(WSAGetLastError()));
} }
void SendLarge(const std::vector<char>& Data) { void SendLarge(std::string Data) {
if (Data.size() > 400) { if (Data.length() > 400) {
auto res = Comp(Data); auto res = Comp(std::span<char>(Data.data(), Data.size()));
res.insert(res.begin(), { 'A', 'B', 'G', ':' }); Data = "ABG:" + std::string(res.data(), res.size());
if (!TCPSock) {
::debug("TCPSock is null");
return;
}
TCPSend(res, *TCPSock);
} else {
if (!TCPSock) {
::debug("TCPSock is null");
return;
}
TCPSend(Data, *TCPSock);
} }
TCPSend(Data, TCPSock);
} }
void UDPParser(std::string_view Packet) { void UDPParser(std::string_view Packet) {
@@ -72,29 +58,40 @@ void UDPParser(std::string_view Packet) {
} }
} }
void UDPRcv() { void UDPRcv() {
sockaddr_in FromServer {};
#if defined(_WIN32)
int clientLength = sizeof(FromServer);
#elif defined(__linux__)
socklen_t clientLength = sizeof(FromServer);
#endif
ZeroMemory(&FromServer, clientLength);
static thread_local std::array<char, 10240> Ret {}; static thread_local std::array<char, 10240> Ret {};
if (UDPSock == nullptr) { if (UDPSock == -1)
::debug("UDPSock is null");
return; return;
} int32_t Rcv = recvfrom(UDPSock, Ret.data(), Ret.size() - 1, 0, (sockaddr*)&FromServer, &clientLength);
asio::error_code ec; if (Rcv == SOCKET_ERROR)
int32_t Rcv = UDPSock->receive(asio::buffer(Ret.data(), Ret.size() - 1), 0, ec);
if (ec)
return; return;
Ret[Rcv] = 0; Ret[Rcv] = 0;
UDPParser(std::string_view(Ret.data(), Rcv)); UDPParser(std::string_view(Ret.data(), Rcv));
} }
void UDPClientMain(asio::ip::address addr, uint16_t port) { void UDPClientMain(const std::string& IP, int Port) {
UDPSock = std::make_shared<asio::ip::udp::socket>(io); #ifdef _WIN32
asio::error_code ec; WSADATA data;
UDPSock->connect(asio::ip::udp::endpoint(addr, port), ec); if (WSAStartup(514, &data)) {
if (ec) { error("Can't start Winsock!");
::error(fmt::format("Failed to connect UDP to server: {}", ec.message())); return;
Terminate = true;
} }
#endif
delete ToServer;
ToServer = new sockaddr_in;
ToServer->sin_family = AF_UNSPEC;
ToServer->sin_port = htons(Port);
inet_pton(AF_UNSPEC, IP.c_str(), &ToServer->sin_addr);
UDPSock = socket(AF_UNSPEC, SOCK_DGRAM, 0);
GameSend("P" + std::to_string(ClientID)); GameSend("P" + std::to_string(ClientID));
TCPSend(strtovec("H"), *TCPSock); TCPSend("H", TCPSock);
UDPSend(strtovec("p")); UDPSend("p");
while (!Terminate) while (!Terminate)
UDPRcv(); UDPRcv();
KillSocket(UDPSock); KillSocket(UDPSock);

View File

@@ -7,7 +7,6 @@
/// ///
#include "Logger.h" #include "Logger.h"
#include "fmt/format.h"
#include <Zlib/Compressor.h> #include <Zlib/Compressor.h>
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
@@ -28,7 +27,7 @@
int LastPort; int LastPort;
std::string LastIP; std::string LastIP;
std::shared_ptr<asio::ip::tcp::socket> TCPSock = nullptr; SOCKET TCPSock = -1;
bool CheckBytes(int32_t Bytes) { bool CheckBytes(int32_t Bytes) {
if (Bytes == 0) { if (Bytes == 0) {
@@ -47,8 +46,8 @@ void UUl(const std::string& R) {
UlStatus = "UlDisconnected: " + R; UlStatus = "UlDisconnected: " + R;
} }
void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock) { void TCPSend(const std::string& Data, uint64_t Sock) {
if (!Sock.is_open()) { if (Sock == -1) {
Terminate = true; Terminate = true;
UUl("Invalid Socket"); UUl("Invalid Socket");
return; return;
@@ -58,43 +57,63 @@ void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock) {
std::string Send(4, 0); std::string Send(4, 0);
Size = int32_t(Data.size()); Size = int32_t(Data.size());
memcpy(&Send[0], &Size, sizeof(Size)); memcpy(&Send[0], &Size, sizeof(Size));
Send += std::string(Data.data(), Data.size()); Send += Data;
// Do not use Size before this point for anything but the header // Do not use Size before this point for anything but the header
Sent = 0; Sent = 0;
Size += 4; Size += 4;
asio::error_code ec; do {
asio::write(Sock, asio::buffer(Send), ec); if (size_t(Sent) >= Send.size()) {
if (ec) { error("string OOB in " + std::string(__func__));
UUl(fmt::format("Failed to send data: {}", ec.message())); UUl("TCP Send OOB");
} return;
}
Temp = send(Sock, &Send[Sent], Size - Sent, 0);
if (!CheckBytes(Temp)) {
UUl("Socket Closed Code 2");
return;
}
Sent += Temp;
} while (Sent < Size);
} }
std::string TCPRcv(asio::ip::tcp::socket& Sock) { std::string TCPRcv(SOCKET Sock) {
if (!Sock.is_open()) { if (Sock == -1) {
Terminate = true; Terminate = true;
UUl("Invalid Socket"); UUl("Invalid Socket");
return ""; return "";
} }
int32_t Header, BytesRcv = 0, Temp; int32_t Header, BytesRcv = 0, Temp;
std::vector<char> Data(sizeof(Header)); std::vector<char> Data(sizeof(Header));
asio::error_code ec; do {
asio::read(Sock, asio::buffer(Data), ec); Temp = recv(Sock, &Data[BytesRcv], 4 - BytesRcv, 0);
if (ec) { if (!CheckBytes(Temp)) {
UUl(fmt::format("Failed to receive header: {}", ec.message())); UUl("Socket Closed Code 3");
} return "";
}
BytesRcv += Temp;
} while (BytesRcv < 4);
memcpy(&Header, &Data[0], sizeof(Header)); memcpy(&Header, &Data[0], sizeof(Header));
Data.resize(Header); if (!CheckBytes(BytesRcv)) {
asio::read(Sock, asio::buffer(Data), ec); UUl("Socket Closed Code 4");
if (ec) { return "";
UUl(fmt::format("Failed to receive data: {}", ec.message()));
} }
Data.resize(Header);
BytesRcv = 0;
do {
Temp = recv(Sock, &Data[BytesRcv], Header - BytesRcv, 0);
if (!CheckBytes(Temp)) {
UUl("Socket Closed Code 5");
return "";
}
BytesRcv += Temp;
} while (BytesRcv < Header);
std::string Ret(Data.data(), Header); std::string Ret(Data.data(), Header);
if (Ret.substr(0, 4) == "ABG:") { if (Ret.substr(0, 4) == "ABG:") {
auto substr = Ret.substr(4); auto substr = Ret.substr(4);
auto res = DeComp(strtovec(substr)); auto res = DeComp(std::span<char>(substr.data(), substr.size()));
Ret = std::string(res.data(), res.size()); Ret = std::string(res.data(), res.size());
} }
@@ -106,24 +125,50 @@ std::string TCPRcv(asio::ip::tcp::socket& Sock) {
return Ret; return Ret;
} }
void TCPClientMain(asio::ip::tcp::socket&& socket) { void TCPClientMain(const std::string& IP, int Port) {
if (!TCPSock) { LastIP = IP;
return; LastPort = Port;
}
LastIP = socket.remote_endpoint().address().to_string();
LastPort = socket.remote_endpoint().port();
SOCKADDR_IN ServerAddr; SOCKADDR_IN ServerAddr;
int RetCode; int RetCode;
TCPSock = std::make_shared<asio::ip::tcp::socket>(std::move(socket)); #ifdef _WIN32
WSADATA wsaData;
WSAStartup(514, &wsaData); // 2.2
#endif
TCPSock = socket(AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
if (TCPSock == -1) {
printf("Client: socket failed! Error code: %d\n", WSAGetLastError());
WSACleanup();
return;
}
ServerAddr.sin_family = AF_UNSPEC;
ServerAddr.sin_port = htons(Port);
inet_pton(AF_UNSPEC, IP.c_str(), &ServerAddr.sin_addr);
RetCode = connect(TCPSock, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));
if (RetCode != 0) {
UlStatus = "UlConnection Failed!";
error("Client: connect failed! Error code: " + std::to_string(WSAGetLastError()));
KillSocket(TCPSock);
WSACleanup();
Terminate = true;
return;
}
info("Connected!"); info("Connected!");
char Code = 'C'; char Code = 'C';
asio::write(*TCPSock, asio::buffer(&Code, 1)); send(TCPSock, &Code, 1, 0);
SyncResources(*TCPSock); SyncResources(TCPSock);
while (!Terminate && TCPSock) { while (!Terminate) {
ServerParser(TCPRcv(*TCPSock)); ServerParser(TCPRcv(TCPSock));
} }
GameSend("T"); GameSend("T");
KillSocket(TCPSock); ////Game Send Terminate
if (KillSocket(TCPSock) != 0)
debug("(TCP) Cannot close socket. Error code: " + std::to_string(WSAGetLastError()));
#ifdef _WIN32
if (WSACleanup() != 0)
debug("(TCP) Client: WSACleanup() failed!...");
#endif
} }

View File

@@ -1,35 +0,0 @@
#include "NetworkHelpers.h"
#include <array>
#include <cerrno>
#include <cstring>
#include <stdexcept>
using asio::ip::tcp;
static uint32_t RecvHeader(tcp::socket& socket) {
std::array<uint8_t, sizeof(uint32_t)> header_buffer {};
asio::error_code ec;
auto n = asio::read(socket, asio::buffer(header_buffer), ec);
if (ec) {
throw std::runtime_error(std::string("recv() of header failed: ") + ec.message());
}
if (n == 0) {
throw std::runtime_error("Game disconnected");
}
return *reinterpret_cast<uint32_t*>(header_buffer.data());
}
/// Throws!!!
void ReceiveFromGame(tcp::socket& socket, std::vector<char>& out_data) {
auto header = RecvHeader(socket);
out_data.resize(header);
asio::error_code ec;
auto n = asio::read(socket, asio::buffer(out_data), ec);
if (ec) {
throw std::runtime_error(std::string("recv() of data failed: ") + ec.message());
}
if (n == 0) {
throw std::runtime_error("Game disconnected");
}
}

View File

@@ -18,6 +18,7 @@ std::string PrivateKey;
extern bool LoginAuth; extern bool LoginAuth;
extern std::string Username; extern std::string Username;
extern std::string UserRole; extern std::string UserRole;
extern int UserID;
void UpdateKey(const char* newKey) { void UpdateKey(const char* newKey) {
if (newKey && std::isalnum(newKey[0])) { if (newKey && std::isalnum(newKey[0])) {
@@ -48,6 +49,7 @@ std::string Login(const std::string& fields) {
if (fields == "LO") { if (fields == "LO") {
Username = ""; Username = "";
UserRole = ""; UserRole = "";
UserID = -1;
LoginAuth = false; LoginAuth = false;
UpdateKey(nullptr); UpdateKey(nullptr);
return ""; return "";
@@ -74,6 +76,9 @@ std::string Login(const std::string& fields) {
if (d.contains("role")) { if (d.contains("role")) {
UserRole = d["role"].get<std::string>(); UserRole = d["role"].get<std::string>();
} }
if (d.contains("id")) {
UserID = d["id"].get<int>();
}
if (d.contains("private_key")) { if (d.contains("private_key")) {
UpdateKey(d["private_key"].get<std::string>().c_str()); UpdateKey(d["private_key"].get<std::string>().c_str());
} }
@@ -129,6 +134,9 @@ void CheckLocalKey() {
if (d.contains("role")) { if (d.contains("role")) {
UserRole = d["role"].get<std::string>(); UserRole = d["role"].get<std::string>();
} }
if (d.contains("id")) {
UserID = d["id"].get<int>();
}
// info(Role); // info(Role);
} else { } else {
info("Auto-Authentication unsuccessful please re-login!"); info("Auto-Authentication unsuccessful please re-login!");

View File

@@ -81,10 +81,10 @@ std::string GetEN() {
} }
std::string GetVer() { std::string GetVer() {
return "2.1"; return "2.0";
} }
std::string GetPatch() { std::string GetPatch() {
return ".0"; return ".99";
} }
std::string GetEP(char* P) { std::string GetEP(char* P) {
@@ -172,7 +172,7 @@ void CheckForUpdates(int argc, char* args[], const std::string& CV) {
system("clear"); system("clear");
#endif #endif
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) { if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion))) && !Dev) {
info("Launcher update found!"); info("Launcher update found!");
#if defined(__linux__) #if defined(__linux__)
error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches."); error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches.");
@@ -204,13 +204,6 @@ void CustomPort(int argc, char* argv[]) {
if (argc > 2) if (argc > 2)
Dev = true; Dev = true;
} }
for (int i = 1; i < argc; ++i) {
if (std::string_view(argv[i]) == "--dev") {
Dev = true;
} else if (std::string_view(argv[i]) == "--no-dev") {
Dev = false;
}
}
} }
#ifdef _WIN32 #ifdef _WIN32
@@ -262,15 +255,7 @@ void InitLauncher(int argc, char* argv[]) {
CheckLocalKey(); CheckLocalKey();
ConfigInit(); ConfigInit();
CustomPort(argc, argv); CustomPort(argc, argv);
bool update = true; CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
for (int i = 1; i < argc; ++i) {
if (std::string_view(argv[i]) == "--no-update") {
update = false;
}
}
if (update) {
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
}
} }
#endif #endif
@@ -333,8 +318,6 @@ void PreGame(const std::string& GamePath) {
CheckMP(GetGamePath() + "mods/multiplayer"); CheckMP(GetGamePath() + "mods/multiplayer");
info("Game user path: '" + GetGamePath() + "'");
if (!Dev) { if (!Dev) {
std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey); std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey);
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);

View File

@@ -3,8 +3,6 @@
"cpp-httplib", "cpp-httplib",
"nlohmann-json", "nlohmann-json",
"zlib", "zlib",
"openssl", "openssl"
"asio",
"fmt"
] ]
} }