62 Commits

Author SHA1 Message Date
Lion Kortlepel
f65fbf60fe start implementing ipv6 support, of course it all sucks
so time to rewrite 90% of it fucking kill me
2024-07-12 21:04:28 +02:00
Lion Kortlepel
188a31c69e fix browser open 2024-06-29 22:56:11 +02:00
Lion Kortlepel
caab92375d properly clean up posix_spawn_file_actions 2024-06-28 15:40:50 +02:00
Lion Kortlepel
b76930b0bd fix game's stdout/stderr printing to launcher console on linux 2024-06-28 15:37:46 +02:00
Lion Kortlepel
96d579f64b fix bug which caused user path to print multiple times 2024-06-28 15:37:27 +02:00
Lion Kortlepel
ba35d039ae add --dev, --no-dev, --no-update flags
these will be replaced by #90 eventually
2024-06-28 09:23:42 +02:00
Lion Kortlepel
3535923f40 set dev to false 2024-06-28 09:23:42 +02:00
Lion Kortlepel
3ca6e7fd3d reduce compression to 3 instead of 6 2024-06-28 09:23:42 +02:00
Lion Kortlepel
7c24020124 implement receiving new header format 2024-06-28 09:23:30 +02:00
snepsnepsnep
88a9d4a1b1 flush console prints 2024-06-28 09:13:26 +02:00
Lion Kortlepel
4a5728d421 implement binary header 2024-06-28 09:13:26 +02:00
Lion Kortlepel
137d9dd1e2 implement string int header 2024-06-28 09:13:26 +02:00
Lion Kortlepel
7b733bf8eb temporarily set dev to true always 2024-06-27 09:34:52 +02:00
Lion Kortlepel
b034072027 fix potential UB in decompression 2024-06-23 23:04:55 +02:00
Lion Kortlepel
f94b9adf7a print game's U S E R path 2024-06-22 23:26:10 +02:00
Lion Kortlepel
c95178ea59 dont auto-update in dev mode 2024-06-22 23:20:46 +02:00
Lion Kortlepel
1f7c498bd9 fix compiler error in decomp 2024-06-22 23:05:01 +02:00
Lion Kortlepel
e46d4b2f0e Merge branch 'performance-improvements' 2024-06-22 23:01:15 +02:00
Lion Kortlepel
f2b34543f9 switch to compression with limit at 30 MB 2024-06-22 22:48:00 +02:00
Lion Kortlepel
d32da036bc fix mod name bug 2024-06-21 17:30:47 +02:00
Lion Kortlepel
8b0f4f99f6 use thread_local static buffer to receive into, null term manually 2024-06-19 16:53:17 +02:00
Lion Kortlepel
17e887442c avoid a substr() which costs us ~20% of runtime performance 2024-06-19 16:18:11 +02:00
Lion Kortlepel
fc454cd11e avoid creating a thread every packet 2024-06-19 15:53:49 +02:00
Lion
e0e2607632 Merge pull request #96 from BeamMP/finalize-linux-merge
Finalize linux merge
2024-06-18 09:57:24 +02:00
Lion Kortlepel
25f28e7fee add common runtime files to gitignore 2024-06-18 09:57:05 +02:00
Lion Kortlepel
ba9719ed67 fix auth packet prefix 2024-06-18 09:46:33 +02:00
Lion Kortlepel
bb04d1bfe1 remove self update on linux for now 2024-06-18 08:51:23 +02:00
Lion Kortlepel
82e58e6513 add toolchain to linux build
oops
2024-06-17 22:36:15 +02:00
Lion Kortlepel
274a1dac7c fix workflows vcpkg version 2024-06-17 22:30:26 +02:00
Lion Kortlepel
ae7f8f44e3 re-implement the website launch feature for linux 2024-06-17 22:16:17 +02:00
Lion Kortlepel
a82b9fb36f reformat 2024-06-17 22:01:15 +02:00
Lion Kortlepel
3488136ca4 add clang format 2024-06-17 21:59:59 +02:00
Lion Kortlepel
05ffa0b638 Merge branch 'linux' 2024-06-17 21:57:41 +02:00
Lion
942cc78406 remove debug print 2024-06-15 22:24:19 +02:00
Lion
46690b5bbf Merge pull request #79 from BeamMP/hotfix-stuff
Fixup various bits
2024-06-15 21:46:07 +02:00
Lion Kortlepel
adba66afb9 add http debug error logging 2024-03-22 12:19:18 +01:00
Lion Kortlepel
cc42a5e0ab add ssl verify debug logging in a few places 2024-03-22 11:34:14 +01:00
Lion Kortlepel
c8a1b77a54 remove discord 2024-03-22 11:23:49 +01:00
Lion Kortlepel
b9d252fd8a remove old stuff 2024-03-22 11:22:17 +01:00
Lion Kortlepel
2b02475fd4 add build instructions 2024-03-22 11:09:02 +01:00
Lion Kortlepel
8aa7a67100 add vcpkg.json 2024-03-22 11:07:30 +01:00
Lion Kortlepel
348090a127 fix perms on more source files
... wtf
2024-03-22 10:50:58 +01:00
Lion Kortlepel
c0df995e28 fix perms on all source files 2024-03-22 10:47:08 +01:00
Lion Kortlepel
8c9d3a5455 fix not returning error from Login, remove old code 2024-02-24 20:26:26 +01:00
Lion Kortlepel
9dcfa1dca4 set username or role to auth if they're empty (not set) 2024-02-24 20:26:26 +01:00
Lion Kortlepel
bd4cfe06b1 reset username and role on logout 2024-02-24 20:26:26 +01:00
Lion Kortlepel
9c7034e401 store username and role on key login as well 2024-02-24 20:26:26 +01:00
Lion Kortlepel
aeb167c1e8 forward user's role on login 2024-02-24 20:26:26 +01:00
Lion Kortlepel
7967ec38e8 bump version to *.85 2024-02-24 20:26:26 +01:00
Lion Kortlepel
250be2ccdc fix more breaking bug 2024-02-24 20:26:26 +01:00
Lion Kortlepel
6158069d4d fix game breaking bug 2024-02-24 20:26:26 +01:00
Lion Kortlepel
b2e5b8d2d3 print error when throwing exception in auth 2024-02-24 20:26:26 +01:00
Lion Kortlepel
5db1b48e07 Revert "debug print error in case of unexpected login error"
This reverts commit 68d64105de.
2024-02-24 20:26:26 +01:00
Lion Kortlepel
56dcfcc5ed debug print error in case of unexpected login error 2024-02-24 20:26:26 +01:00
Lion Kortlepel
7c1106a46a add username to auth packet 2024-02-24 20:26:26 +01:00
Anonymous275
9afdfd4d1b v2.0.84
- add Access-Control response headers
2023-12-16 14:30:35 +00:00
Anonymous275
c2f260a86c v2.0.84
- remove comment
2023-12-15 19:39:55 +00:00
Anonymous275
2781179b4b v2.0.84
- proxy tweaking
2023-12-15 19:38:47 +00:00
Anonymous275
3b479abf64 v2.0.84
- add hash check
- new routes for updates
- use C++ 20
2023-12-15 18:16:12 +00:00
Anonymous275
c731718f50 v2.0.84
- HTTP Proxy for backend.beammp.com
- Fix Attempt for mod loading, game detecting partial zip file
- Use nlohmann JSON
- Update vcpkg parameters and commit ID
- Add ability to open URL using default browser with filter
2023-12-15 17:38:47 +00:00
Anonymous-275
0fd0a9fe7e Merge remote-tracking branch 'origin/master' 2023-10-26 00:09:04 +01:00
Anonymous-275
302582bfe1 v2.0.83
- removed code that is no longer needed
2023-10-26 00:08:56 +01:00
79 changed files with 6566 additions and 9387 deletions

5
.clang-format Normal file
View File

@@ -0,0 +1,5 @@
---
BasedOnStyle: WebKit
BreakBeforeBraces: Attach
SpaceAfterTemplateKeyword: false
...

View File

@@ -14,13 +14,21 @@ jobs:
with: with:
submodules: 'true' submodules: 'true'
- name: Restore artifacts, or run vcpkg, build and cache artifacts
uses: lukka/run-vcpkg@v7
id: runvcpkg
with:
vcpkgArguments: 'zlib nlohmann-json openssl cpp-httplib[openssl]'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '40616a5e954f7be1077ef37db3fbddbd5dcd1ca6'
- name: Create Build Environment - name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build-linux run: cmake -E make_directory ${{github.workspace}}/build-linux
- name: Configure CMake - name: Configure CMake
shell: bash shell: bash
working-directory: ${{github.workspace}}/build-linux working-directory: ${{github.workspace}}/build-linux
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake'
- name: Build - name: Build
working-directory: ${{github.workspace}}/build-linux working-directory: ${{github.workspace}}/build-linux

View File

@@ -18,9 +18,9 @@ jobs:
uses: lukka/run-vcpkg@v7 uses: lukka/run-vcpkg@v7
id: runvcpkg id: runvcpkg
with: with:
vcpkgArguments: 'discord-rpc zlib rapidjson openssl' vcpkgArguments: 'discord-rpc zlib nlohmann-json openssl cpp-httplib[openssl]'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '06b5f4a769d848d1a20fa0acd556019728b56273' vcpkgGitCommitId: '40616a5e954f7be1077ef37db3fbddbd5dcd1ca6'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment

View File

@@ -43,9 +43,9 @@ jobs:
uses: lukka/run-vcpkg@main uses: lukka/run-vcpkg@main
id: runvcpkg id: runvcpkg
with: with:
vcpkgArguments: 'discord-rpc zlib rapidjson openssl' vcpkgArguments: 'discord-rpc zlib nlohmann-json openssl cpp-httplib[openssl]'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '06b5f4a769d848d1a20fa0acd556019728b56273' vcpkgGitCommitId: '16ee2ecb31788c336ace8bb14c21801efb6836e4'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment

7
.gitignore vendored
View File

@@ -5,3 +5,10 @@ cmake-build-release
/*.sh /*.sh
/*.obj /*.obj
/*.exe /*.exe
.cache/
.https_debug/
Launcher.cfg
Resources/
bin/
compile_commands.json
key

View File

@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(Launcher) project(Launcher)
if (WIN32) if (WIN32)
@@ -7,11 +8,15 @@ if (WIN32)
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
endif(WIN32) endif(WIN32)
set(CMAKE_CXX_STANDARD 17) 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")
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(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")
@@ -19,20 +24,16 @@ set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher")
if (WIN32) if (WIN32)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
#-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static target_link_libraries(${PROJECT_NAME} PRIVATE
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}) ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json asio::asio fmt::fmt)
include_directories(${VcpkgRoot}/include)
link_directories(${VcpkgRoot}/lib)
target_link_libraries(${PROJECT_NAME} PRIVATE ${VcpkgRoot}/lib/discord-rpc.lib
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32)
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) ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto asio::asio fmt::fmt)
else(WIN32) #MINGW elseif (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} discord-rpc ssl crypto ws2_32 ssp crypt32 z) target_link_libraries(${PROJECT_NAME} ssl crypto ws2_32 ssp crypt32 z asio::asio fmt::fmt)
endif(WIN32) endif(WIN32)
target_include_directories(${PROJECT_NAME} PRIVATE "include") target_include_directories(${PROJECT_NAME} PRIVATE "include")

View File

@@ -2,6 +2,10 @@
The launcher is the way we communitcate to outside the game, it does a few automated actions such as but not limited to: downloading the mod, launching the game, and create a connection to a server. The launcher is the way we communitcate to outside the game, it does a few automated actions such as but not limited to: downloading the mod, launching the game, and create a connection to a server.
## How to build
1. `cmake . -B bin -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static`
2. `cmake --build bin --parallel`
Copyright (c) 2019-present Anonymous275. Copyright (c) 2019-present Anonymous275.
BeamMP Launcher code is not in the public domain and is not free software. BeamMP Launcher code is not in the public domain and is not free software.

View File

@@ -1,15 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Launcher code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/16/2020
///
#pragma once
#include <string>
void Discord_Main();
std::string GetDName();
std::string GetDTag();
std::string GetDID();
void DAboard();

View File

@@ -1,26 +0,0 @@
#pragma once
#if defined(DISCORD_DYNAMIC_LIB)
#if defined(_WIN32)
#if defined(DISCORD_BUILDING_SDK)
#define DISCORD_EXPORT __declspec(dllexport)
#else
#define DISCORD_EXPORT __declspec(dllimport)
#endif
#else
#define DISCORD_EXPORT __attribute__((visibility("default")))
#endif
#else
#define DISCORD_EXPORT
#endif
#ifdef __cplusplus
extern "C" {
#endif
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
#ifdef __cplusplus
}
#endif

View File

@@ -1,87 +0,0 @@
#pragma once
#include <cstdint>
// clang-format off
#if defined(DISCORD_DYNAMIC_LIB)
# if defined(_WIN32)
# if defined(DISCORD_BUILDING_SDK)
# define DISCORD_EXPORT __declspec(dllexport)
# else
# define DISCORD_EXPORT __declspec(dllimport)
# endif
# else
# define DISCORD_EXPORT __attribute__((visibility("default")))
# endif
#else
# define DISCORD_EXPORT
#endif
// clang-format on
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DiscordRichPresence {
const char* state; /* max 128 bytes */
const char* details; /* max 128 bytes */
int64_t startTimestamp;
int64_t endTimestamp;
const char* largeImageKey; /* max 32 bytes */
const char* largeImageText; /* max 128 bytes */
const char* smallImageKey; /* max 32 bytes */
const char* smallImageText; /* max 128 bytes */
const char* partyId; /* max 128 bytes */
int partySize;
int partyMax;
const char* matchSecret; /* max 128 bytes */
const char* joinSecret; /* max 128 bytes */
const char* spectateSecret; /* max 128 bytes */
int8_t instance;
} DiscordRichPresence;
typedef struct DiscordUser {
const char* userId;
const char* username;
const char* discriminator;
const char* avatar;
} DiscordUser;
typedef struct DiscordEventHandlers {
void (*ready)(const DiscordUser* request);
void (*disconnected)(int errorCode, const char* message);
void (*errored)(int errorCode, const char* message);
void (*joinGame)(const char* joinSecret);
void (*spectateGame)(const char* spectateSecret);
void (*joinRequest)(const DiscordUser* request);
} DiscordEventHandlers;
#define DISCORD_REPLY_NO 0
#define DISCORD_REPLY_YES 1
#define DISCORD_REPLY_IGNORE 2
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId);
DISCORD_EXPORT void Discord_Shutdown(void);
/* checks for incoming messages, dispatches callbacks */
DISCORD_EXPORT void Discord_RunCallbacks(void);
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
#ifdef DISCORD_DISABLE_IO_THREAD
DISCORD_EXPORT void Discord_UpdateConnection(void);
#endif
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
DISCORD_EXPORT void Discord_ClearPresence(void);
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
#ifdef __cplusplus
} /* extern "C" */
#endif

11
include/Helpers.h Normal file
View File

@@ -0,0 +1,11 @@
#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);

4
include/Http.h Executable file → Normal file
View File

@@ -6,15 +6,15 @@
/// Created by Anonymous275 on 7/18/2020 /// Created by Anonymous275 on 7/18/2020
/// ///
#pragma once #pragma once
#include <string>
#include "Logger.h" #include "Logger.h"
#include <string>
class HTTP { class HTTP {
public: public:
static bool Download(const std::string& IP, const std::string& Path); static bool Download(const std::string& IP, const std::string& Path);
static std::string Post(const std::string& IP, const std::string& Fields); static std::string Post(const std::string& IP, const std::string& Fields);
static std::string Get(const std::string& IP); static std::string Get(const std::string& IP);
static bool ProgressBar(size_t c, size_t t); static bool ProgressBar(size_t c, size_t t);
public: public:
static bool isDownload; static bool isDownload;
static std::string Codes_[];
}; };

View File

@@ -1,12 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Launcher code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 11/27/2020
///
#pragma once
#include "rapidjson/stringbuffer.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
namespace json = rapidjson;

3
include/Logger.h Executable file → Normal file
View File

@@ -6,8 +6,8 @@
/// Created by Anonymous275 on 4/2/2020. /// Created by Anonymous275 on 4/2/2020.
/// ///
#pragma once #pragma once
#include <string>
#include <iostream> #include <iostream>
#include <string>
void InitLog(); void InitLog();
void except(const std::string& toPrint); void except(const std::string& toPrint);
void fatal(const std::string& toPrint); void fatal(const std::string& toPrint);
@@ -15,3 +15,4 @@ void debug(const std::string& toPrint);
void error(const std::string& toPrint); void error(const std::string& toPrint);
void info(const std::string& toPrint); void info(const std::string& toPrint);
void warn(const std::string& toPrint); void warn(const std::string& toPrint);
std::string getDate();

42
include/Network/network.hpp Executable file → Normal file
View File

@@ -6,51 +6,61 @@
/// Created by Anonymous275 on 7/18/2020 /// Created by Anonymous275 on 7/18/2020
/// ///
#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__
#include <cstdint>
#include "linuxfixes.h" #include "linuxfixes.h"
#include <bits/types/siginfo_t.h> #include <bits/types/siginfo_t.h>
#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;
[[noreturn]] void CoreNetwork(); [[noreturn]] void CoreNetwork();
extern int ProxyPort;
extern int ClientID; extern int ClientID;
extern int LastPort; extern int LastPort;
extern bool ModLoaded; extern bool ModLoaded;
extern bool Terminate; extern bool Terminate;
extern int DEFAULT_PORT; extern int DEFAULT_PORT;
extern uint64_t UDPSock; extern std::shared_ptr<asio::ip::udp::socket> UDPSock;
extern uint64_t TCPSock; extern std::shared_ptr<asio::ip::tcp::socket> TCPSock;
extern std::string Branch; extern std::string Branch;
extern bool TCPTerminate; extern bool TCPTerminate;
extern std::string LastIP; extern std::string LastIP;
extern std::string MStatus; extern std::string MStatus;
extern std::string UlStatus; extern std::string UlStatus;
extern std::string PublicKey; extern std::string PublicKey;
extern std::string PrivateKey;
extern std::string ListOfMods; extern std::string ListOfMods;
int KillSocket(uint64_t Dead); void KillSocket(std::shared_ptr<asio::ip::tcp::socket>& 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(std::string Data); void UDPSend(const std::vector<char>& Data);
bool CheckBytes(int32_t Bytes); bool CheckBytes(int32_t Bytes);
void GameSend(std::string Data); void GameSend(std::string_view Data);
void SendLarge(std::string Data); void SendLarge(const std::vector<char>& Data);
std::string TCPRcv(uint64_t Sock); std::string TCPRcv(asio::ip::tcp::socket& Sock);
void SyncResources(uint64_t TCPSock); void SyncResources(asio::ip::tcp::socket& TCPSock);
std::string GetAddr(const std::string& IP); std::string GetAddr(const std::string& IP);
void ServerParser(const std::string& 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::string&Data,uint64_t Sock); void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock);
void TCPClientMain(const std::string& IP,int Port); void TCPClientMain(asio::ip::tcp::socket&& Socket);
void UDPClientMain(const std::string& IP,int Port); void UDPClientMain(asio::ip::address addr, uint16_t port);
void TCPGameServer(const std::string& IP, int Port); void TCPGameServer(asio::ip::tcp::socket&& Socket);

6
include/NetworkHelpers.h Normal file
View File

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

0
include/Security/Game.h Executable file → Normal file
View File

0
include/Security/Init.h Executable file → Normal file
View File

5
include/Startup.h Executable file → Normal file
View File

@@ -6,11 +6,16 @@
/// Created by Anonymous275 on 7/18/2020 /// Created by Anonymous275 on 7/18/2020
/// ///
#pragma once #pragma once
#include <compare>
#include <string> #include <string>
#include <vector>
void InitLauncher(int argc, char* argv[]); void InitLauncher(int argc, char* argv[]);
std::string GetEP(char* P = nullptr); std::string GetEP(char* P = nullptr);
std::string GetGamePath(); std::string GetGamePath();
std::string GetVer(); std::string GetVer();
std::string GetEN(); std::string GetEN();
void StartProxy();
void ConfigInit(); void ConfigInit();
extern bool Dev; extern bool Dev;

8
include/Zlib/Compressor.h Executable file → Normal file
View File

@@ -6,6 +6,8 @@
/// Created by Anonymous275 on 7/24/2020 /// Created by Anonymous275 on 7/24/2020
/// ///
#pragma once #pragma once
#include <string> #include <span>
std::string Comp(std::string Data); #include <vector>
std::string DeComp(std::string Compressed);
std::vector<char> Comp(std::span<const char> input);
std::vector<char> DeComp(std::span<const char> input);

4642
include/hashpp.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

0
include/rapidjson/allocators.h Executable file → Normal file
View File

0
include/rapidjson/cursorstreamwrapper.h Executable file → Normal file
View File

0
include/rapidjson/document.h Executable file → Normal file
View File

0
include/rapidjson/encodedstream.h Executable file → Normal file
View File

0
include/rapidjson/encodings.h Executable file → Normal file
View File

0
include/rapidjson/error/en.h Executable file → Normal file
View File

0
include/rapidjson/error/error.h Executable file → Normal file
View File

0
include/rapidjson/filereadstream.h Executable file → Normal file
View File

0
include/rapidjson/filewritestream.h Executable file → Normal file
View File

0
include/rapidjson/fwd.h Executable file → Normal file
View File

0
include/rapidjson/internal/biginteger.h Executable file → Normal file
View File

0
include/rapidjson/internal/clzll.h Executable file → Normal file
View File

0
include/rapidjson/internal/diyfp.h Executable file → Normal file
View File

0
include/rapidjson/internal/dtoa.h Executable file → Normal file
View File

0
include/rapidjson/internal/ieee754.h Executable file → Normal file
View File

0
include/rapidjson/internal/itoa.h Executable file → Normal file
View File

0
include/rapidjson/internal/meta.h Executable file → Normal file
View File

0
include/rapidjson/internal/pow10.h Executable file → Normal file
View File

0
include/rapidjson/internal/regex.h Executable file → Normal file
View File

0
include/rapidjson/internal/stack.h Executable file → Normal file
View File

0
include/rapidjson/internal/strfunc.h Executable file → Normal file
View File

0
include/rapidjson/internal/strtod.h Executable file → Normal file
View File

0
include/rapidjson/internal/swap.h Executable file → Normal file
View File

0
include/rapidjson/istreamwrapper.h Executable file → Normal file
View File

0
include/rapidjson/memorybuffer.h Executable file → Normal file
View File

0
include/rapidjson/memorystream.h Executable file → Normal file
View File

0
include/rapidjson/msinttypes/inttypes.h Executable file → Normal file
View File

0
include/rapidjson/msinttypes/stdint.h Executable file → Normal file
View File

0
include/rapidjson/ostreamwrapper.h Executable file → Normal file
View File

0
include/rapidjson/pointer.h Executable file → Normal file
View File

0
include/rapidjson/prettywriter.h Executable file → Normal file
View File

0
include/rapidjson/rapidjson.h Executable file → Normal file
View File

0
include/rapidjson/reader.h Executable file → Normal file
View File

0
include/rapidjson/schema.h Executable file → Normal file
View File

0
include/rapidjson/stream.h Executable file → Normal file
View File

0
include/rapidjson/stringbuffer.h Executable file → Normal file
View File

0
include/rapidjson/writer.h Executable file → Normal file
View File

90
src/Compressor.cpp Executable file → Normal file
View File

@@ -6,52 +6,58 @@
/// Created by Anonymous275 on 7/15/2020 /// Created by Anonymous275 on 7/15/2020
/// ///
#include <iostream> #include "Logger.h"
#include <span>
#include <vector>
#include <zconf.h>
#include <zlib.h> #include <zlib.h>
#ifdef __linux__ #ifdef __linux__
#include <cstring> #include <cstring>
#endif #endif
#define Biggest 30000 std::vector<char> Comp(std::span<const char> input) {
std::string Comp(std::string Data){ auto max_size = compressBound(input.size());
char*C = new char[Biggest]; std::vector<char> output(max_size);
memset(C, 0, Biggest); uLongf output_size = output.size();
z_stream defstream; int res = compress2(
defstream.zalloc = Z_NULL; reinterpret_cast<Bytef*>(output.data()),
defstream.zfree = Z_NULL; &output_size,
defstream.opaque = Z_NULL; reinterpret_cast<const Bytef*>(input.data()),
defstream.avail_in = (uInt)Data.length(); static_cast<uLongf>(input.size()),
defstream.next_in = (Bytef *)&Data[0]; 3);
defstream.avail_out = Biggest; if (res != Z_OK) {
defstream.next_out = reinterpret_cast<Bytef *>(C); error("zlib compress() failed: " + std::to_string(res));
deflateInit(&defstream, Z_BEST_COMPRESSION); throw std::runtime_error("zlib compress() failed");
deflate(&defstream, Z_SYNC_FLUSH);
deflate(&defstream, Z_FINISH);
deflateEnd(&defstream);
int TO = defstream.total_out;
std::string Ret(TO,0);
memcpy(&Ret[0],C,TO);
delete [] C;
return Ret;
} }
std::string DeComp(std::string Compressed){ debug("zlib compressed " + std::to_string(input.size()) + " B to " + std::to_string(output_size) + " B");
char*C = new char[Biggest]; output.resize(output_size);
memset(C, 0, Biggest); return output;
z_stream infstream; }
infstream.zalloc = Z_NULL;
infstream.zfree = Z_NULL; std::vector<char> DeComp(std::span<const char> input) {
infstream.opaque = Z_NULL; std::vector<char> output_buffer(std::min<size_t>(input.size() * 5, 15 * 1024 * 1024));
infstream.avail_in = Biggest;
infstream.next_in = (Bytef *)(&Compressed[0]); uLongf output_size = output_buffer.size();
infstream.avail_out = Biggest;
infstream.next_out = (Bytef *)(C); while (true) {
inflateInit(&infstream); int res = uncompress(
inflate(&infstream, Z_SYNC_FLUSH); reinterpret_cast<Bytef*>(output_buffer.data()),
inflate(&infstream, Z_FINISH); &output_size,
inflateEnd(&infstream); reinterpret_cast<const Bytef*>(input.data()),
int TO = infstream.total_out; static_cast<uLongf>(input.size()));
std::string Ret(TO,0); if (res == Z_BUF_ERROR) {
memcpy(&Ret[0],C,TO); if (output_buffer.size() > 30 * 1024 * 1024) {
delete [] C; throw std::runtime_error("decompressed packet size of 30 MB exceeded");
return Ret; }
debug("zlib uncompress() failed, trying with 2x buffer size of " + std::to_string(output_buffer.size() * 2));
output_buffer.resize(output_buffer.size() * 2);
output_size = output_buffer.size();
} else if (res != Z_OK) {
error("zlib uncompress() failed: " + std::to_string(res));
throw std::runtime_error("zlib uncompress() failed");
} else if (res == Z_OK) {
break;
}
} output_buffer.resize(output_size);
return output_buffer;
} }

32
src/Config.cpp Executable file → Normal file
View File

@@ -2,18 +2,18 @@
/// Created by Anonymous275 on 2/23/2021 /// Created by Anonymous275 on 2/23/2021
/// ///
#include "Network/network.hpp"
#include <filesystem>
#include "Logger.h" #include "Logger.h"
#include <fstream> #include "Network/network.hpp"
#include "Json.h"
#include <cstdint> #include <cstdint>
#include <filesystem>
#include <fstream>
#include <nlohmann/json.hpp>
namespace fs = std::filesystem; namespace fs = std::filesystem;
std::string Branch; std::string Branch;
void ParseConfig(const json::Document& d){ void ParseConfig(const nlohmann::json& d) {
if(d["Port"].IsInt()){ if (d["Port"].is_number()) {
DEFAULT_PORT = d["Port"].GetInt(); DEFAULT_PORT = d["Port"].get<int>();
} }
// Default -1 // Default -1
// Release 1 // Release 1
@@ -21,9 +21,10 @@ void ParseConfig(const json::Document& d){
// Dev 3 // Dev 3
// Custom 3 // Custom 3
if(d["Build"].IsString()){ if (d["Build"].is_string()) {
Branch = d["Build"].GetString(); Branch = d["Build"].get<std::string>();
for(char& c : Branch)c = char(tolower(c)); for (char& c : Branch)
c = char(tolower(c));
} }
} }
@@ -35,13 +36,13 @@ void ConfigInit(){
std::string Buffer(Size, 0); std::string Buffer(Size, 0);
cfg.read(&Buffer[0], Size); cfg.read(&Buffer[0], Size);
cfg.close(); cfg.close();
json::Document d; nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false);
d.Parse(Buffer.c_str()); if (d.is_discarded()) {
if(d.HasParseError()){ fatal("Config failed to parse make sure it's valid JSON!");
fatal("Config failed to parse make sure it's valid JSON! Code : " + std::to_string(d.GetParseError()));
} }
ParseConfig(d); ParseConfig(d);
}else fatal("Failed to open Launcher.cfg!"); } else
fatal("Failed to open Launcher.cfg!");
} else { } else {
std::ofstream cfg("Launcher.cfg"); std::ofstream cfg("Launcher.cfg");
if (cfg.is_open()) { if (cfg.is_open()) {
@@ -56,4 +57,3 @@ void ConfigInit(){
} }
} }
} }

View File

@@ -1,116 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Launcher code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/16/2020
///
#ifndef __linux__
#include "Discord/discord_rpc.h"
#include "Logger.h"
#include <cstring>
#include <thread>
#include <ctime>
struct DInfo{
std::string Name;
std::string Tag;
std::string DID;
};
DInfo* DiscordInfo = nullptr;
int64_t StartTime;
void updateDiscordPresence(){
//if (SendPresence) {
//char buffer[256];
DiscordRichPresence discordPresence;
memset(&discordPresence, 0, sizeof(discordPresence));
std::string P = "Playing with friends!"; ///to be revisited
discordPresence.state = P.c_str();
//sprintf(buffer, "Frustration level: %d", FrustrationLevel);
//discordPresence.details = buffer;
discordPresence.startTimestamp = StartTime;
//discordPresence.endTimestamp = time(0) + 5 * 60;
discordPresence.largeImageKey = "mainlogo";
//discordPresence.smallImageKey = "logo";
//discordPresence.partyId = "party1234";
//discordPresence.partySize = 1;
//discordPresence.partyMax = 6;
//discordPresence.matchSecret = "xyzzy";
//discordPresence.joinSecret = "join";
//discordPresence.spectateSecret = "look";
//discordPresence.instance = 0;
Discord_UpdatePresence(&discordPresence);
//}
//else {
// Discord_ClearPresence();
//}
}
void handleDiscordReady(const DiscordUser* User){
DiscordInfo = new DInfo{
User->username,
User->discriminator,
User->userId
};
}
void discordInit(){
DiscordEventHandlers handlers;
memset(&handlers, 0, sizeof(handlers));
handlers.ready = handleDiscordReady;
/*handlers.disconnected = handleDiscordDisconnected;
handlers.errored = handleDiscordError;
handlers.joinGame = handleDiscordJoin;
handlers.spectateGame = handleDiscordSpectate;
handlers.joinRequest = handleDiscordJoinRequest;*/
Discord_Initialize("629743237988352010", &handlers, 1,nullptr);
}
[[noreturn]] void Loop(){
StartTime = time(nullptr);
while (true) {
updateDiscordPresence();
#ifdef DISCORD_DISABLE_IO_THREAD
Discord_UpdateConnection();
#endif
Discord_RunCallbacks();
if(DiscordInfo == nullptr){
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}else std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
void DMain(){
discordInit();
Loop();
}
std::string GetDName(){
return DiscordInfo->Name;
}
std::string GetDTag(){
return DiscordInfo->Tag;
}
std::string GetDID(){
return DiscordInfo->DID;
}
void DAboard(){
DiscordInfo = nullptr;
}
void ErrorAboard(){
error("Discord timeout! please start the discord app and try again after 30 secs");
std::this_thread::sleep_for(std::chrono::seconds(5));
exit(6);
}
void Discord_Main(){
/*std::thread t1(DMain);
t1.detach();*/
/*info("Connecting to discord client...");
int C = 0;
while(DiscordInfo == nullptr && C < 80){
std::this_thread::sleep_for(std::chrono::milliseconds(300));
C++;
}
if(DiscordInfo == nullptr)ErrorAboard();*/
}
#endif

41
src/GameStart.cpp Executable file → Normal file
View File

@@ -6,22 +6,23 @@
/// Created by Anonymous275 on 7/19/2020 /// Created by Anonymous275 on 7/19/2020
/// ///
#if defined(_WIN32) #if defined(_WIN32)
#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 <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <spawn.h> #include <spawn.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif #endif
#include "Logger.h"
#include "Startup.h"
#include <Security/Init.h> #include <Security/Init.h>
#include <filesystem> #include <filesystem>
#include "Startup.h"
#include "Logger.h"
#include <thread> #include <thread>
unsigned long GamePID = 0; unsigned long GamePID = 0;
@@ -29,7 +30,8 @@ unsigned long GamePID = 0;
std::string QueryKey(HKEY hKey, int ID); std::string QueryKey(HKEY hKey, int ID);
std::string GetGamePath() { std::string GetGamePath() {
static std::string Path; static std::string Path;
if(!Path.empty())return Path; if (!Path.empty())
return Path;
HKEY hKey; HKEY hKey;
LPCTSTR sk = "Software\\BeamNG\\BeamNG.drive"; LPCTSTR sk = "Software\\BeamNG\\BeamNG.drive";
@@ -90,11 +92,27 @@ 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");
@@ -104,6 +122,11 @@ 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);
} }

9
src/Helpers.cpp Normal file
View File

@@ -0,0 +1,9 @@
#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());
}

22
src/Logger.cpp Executable file → Normal file
View File

@@ -6,11 +6,11 @@
/// Created by Anonymous275 on 7/17/2020 /// Created by Anonymous275 on 7/17/2020
/// ///
#include "Startup.h"
#include "Logger.h" #include "Logger.h"
#include "Startup.h"
#include <chrono>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <chrono>
#include <thread> #include <thread>
std::string getDate() { std::string getDate() {
@@ -39,7 +39,8 @@ void InitLog(){
LFS.open(GetEP() + "Launcher.log"); LFS.open(GetEP() + "Launcher.log");
if (!LFS.is_open()) { if (!LFS.is_open()) {
error("logger file init failed!"); error("logger file init failed!");
}else LFS.close(); } else
LFS.close();
} }
void addToLog(const std::string& Line) { void addToLog(const std::string& Line) {
std::ofstream LFS; std::ofstream LFS;
@@ -49,34 +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::cout << Print << std::flush;
addToLog(Print); addToLog(Print);
} }
void debug(const std::string& toPrint) { void debug(const std::string& toPrint) {
if(!Dev)return; if (!Dev)
return;
std::string Print = getDate() + "[DEBUG] " + toPrint + "\n"; std::string Print = getDate() + "[DEBUG] " + toPrint + "\n";
std::cout << Print; std::cout << Print << std::flush;
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::cout << Print << std::flush;
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::cout << Print << std::flush;
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::cout << Print << std::flush;
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::cout << Print << std::flush;
addToLog(Print); addToLog(Print);
} }

352
src/Network/Core.cpp Executable file → Normal file
View File

@@ -5,27 +5,31 @@
/// ///
/// Created by Anonymous275 on 7/20/2020 /// Created by Anonymous275 on 7/20/2020
/// ///
#include "Network/network.hpp"
#include "Security/Init.h"
#include "Http.h" #include "Http.h"
#if defined(_WIN32) #include "Network/network.hpp"
#include <winsock2.h> #include "NetworkHelpers.h"
#include <ws2tcpip.h> #include "Security/Init.h"
#include <asio/io_context.hpp>
#elif defined(__linux__) #include <cstdlib>
#include <sys/types.h> #include <optional>
#include <sys/socket.h> #include <regex>
#include <netdb.h> #if defined(__linux__)
#include <cstring> #include <cstring>
#include <errno.h> #include <errno.h>
#include <netdb.h>
#include <spawn.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif #endif
#include "Startup.h"
#include "Logger.h" #include "Logger.h"
#include "Startup.h"
#include <charconv> #include <charconv>
#include <thread> #include <fmt/format.h>
#include <nlohmann/json.hpp>
#include <set> #include <set>
#include <thread>
extern int TraceBack; extern int TraceBack;
std::set<std::string>* ConfList = nullptr; std::set<std::string>* ConfList = nullptr;
@@ -33,68 +37,168 @@ bool TCPTerminate = false;
int DEFAULT_PORT = 4444; int DEFAULT_PORT = 4444;
bool Terminate = false; bool Terminate = false;
bool LoginAuth = false; bool LoginAuth = false;
std::string Username = "";
std::string UserRole = "";
std::string UlStatus; std::string UlStatus;
std::string MStatus; std::string MStatus;
bool ModLoaded; bool ModLoaded;
int ping = -1; int ping = -1;
void StartSync(const std::string &Data){ asio::io_context io {};
std::string IP = GetAddr(Data.substr(1,Data.find(':')-1));
if(IP.find('.') == -1){ static asio::ip::tcp::socket ResolveAndConnect(const std::string& host_port_string) {
if(IP == "DNS")UlStatus ="UlConnection Failed! (DNS Lookup Failed)";
else UlStatus = "UlConnection Failed! (WSA failed to start)"; using namespace asio;
ListOfMods = "-"; ip::tcp::resolver resolver(io);
Terminate = true; asio::error_code ec;
return; 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()));
} }
CheckLocalKey(); bool connected = false;
UlStatus = "UlLoading..."; 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) {
try {
auto Socket = ResolveAndConnect(Data.substr(1));
ListOfMods = "-";
CheckLocalKey();
TCPTerminate = false; TCPTerminate = false;
Terminate = false; Terminate = false;
ConfList->clear(); ConfList->clear();
ping = -1; ping = -1;
std::thread GS(TCPGameServer,IP,std::stoi(Data.substr(Data.find(':')+1))); std::thread GS(TCPGameServer, std::move(Socket));
GS.detach(); GS.detach();
info("Connecting to server"); info("Connecting to server");
} catch (const std::exception& e) {
UlStatus = "UlConnection Failed!";
error(fmt::format("Client: connect failed! Error: {}", e.what()));
WSACleanup();
Terminate = true;
} }
void Parse(std::string Data,SOCKET CSocket){ }
char Code = Data.at(0), SubCode = 0;
if(Data.length() > 1)SubCode = Data.at(1); bool IsAllowedLink(const std::string& Link) {
std::vector<std::string> allowed_links = {
R"(patreon\.com\/beammp$)",
R"(discord\.gg\/beammp$)",
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) {
std::string OutData;
char Code = InData[0], SubCode = 0;
if (InData.size() > 1)
SubCode = InData[1];
switch (Code) { switch (Code) {
case 'A': case 'A':
Data = Data.substr(0,1); OutData = "A";
break; break;
case 'B': case 'B':
NetReset(); NetReset();
Terminate = true; Terminate = true;
TCPTerminate = true; TCPTerminate = true;
Data = Code + HTTP::Get("https://backend.beammp.com/servers-info"); OutData = Code + HTTP::Get("https://backend.beammp.com/servers-info");
break; break;
case 'C': case 'C':
ListOfMods.clear(); ListOfMods.clear();
StartSync(Data); StartSync(std::string(InData.data(), InData.size()));
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 == "-")Data = "L"; if (ListOfMods == "-")
else Data = "L"+ListOfMods; OutData = "L";
else
OutData = "L" + ListOfMods;
break;
case 'O': // open default browser with URL
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 = bytespan_to_string(InData.subspan(1));
char* argv[] = { browser, arg.data() };
auto status = posix_spawn(&pid, browser, nullptr, nullptr, argv, environ);
if (status == 0) {
debug("Browser PID: " + std::to_string(pid));
// we don't wait for it to exit, because we just don't care.
// typically, you'd waitpid() here.
} else {
error("Failed to open the following link in the browser (error follows below): " + arg);
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: " + bytespan_to_string(InData.subspan(1)));
}
#elif defined(WIN32)
ShellExecuteA(nullptr, "open", InData.subspan(1).data(), nullptr, nullptr, SW_SHOW); /// TODO: Look at when working on linux port
#endif
info("Opening Link \"" + bytespan_to_string(InData.subspan(1)) + "\"");
}
OutData.clear();
break;
case 'P':
OutData = Code + std::to_string(ProxyPort);
break; break;
case 'U': case 'U':
if(SubCode == 'l')Data = UlStatus; if (SubCode == 'l')
OutData = UlStatus;
if (SubCode == 'p') { if (SubCode == 'p') {
if (ping > 800) { if (ping > 800) {
Data = "Up-2"; OutData = "Up-2";
}else Data = "Up" + std::to_string(ping); } else
OutData = "Up" + std::to_string(ping);
} }
if (!SubCode) { if (!SubCode) {
std::string Ping; std::string Ping;
if(ping > 800)Ping = "-2"; if (ping > 800)
else Ping = std::to_string(ping); Ping = "-2";
Data = std::string(UlStatus) + "\n" + "Up" + Ping; else
Ping = std::to_string(ping);
OutData = std::string(UlStatus) + "\n" + "Up" + Ping;
} }
break; break;
case 'M': case 'M':
Data = MStatus; OutData = MStatus;
break; break;
case 'Q': case 'Q':
if (SubCode == 'S') { if (SubCode == 'S') {
@@ -103,75 +207,66 @@ void Parse(std::string Data,SOCKET CSocket){
TCPTerminate = true; TCPTerminate = true;
ping = -1; ping = -1;
} }
if(SubCode == 'G')exit(2); if (SubCode == 'G')
Data.clear(); exit(2);
OutData.clear();
break; break;
case 'R': // will send mod name 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; ModLoaded = true;
} }
Data.clear(); OutData.clear();
break; } break;
case 'Z': case 'Z':
Data = "Z" + GetVer(); OutData = "Z" + GetVer();
break; break;
case 'N': case 'N':
if (SubCode == 'c') { if (SubCode == 'c') {
Data = "N{\"Auth\":"+std::to_string(LoginAuth)+"}"; nlohmann::json Auth = {
{ "Auth", LoginAuth ? 1 : 0 },
};
if (!Username.empty()) {
Auth["username"] = Username;
}
if (!UserRole.empty()) {
Auth["role"] = UserRole;
}
OutData = "N" + Auth.dump();
} else { } 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; break;
default: default:
Data.clear(); OutData.clear();
break; break;
} }
if(!Data.empty() && CSocket != -1){ if (!OutData.empty() && CSocket.is_open()) {
int res = send(CSocket, (Data+"\n").c_str(), int(Data.size())+1, 0); uint32_t DataSize = OutData.size();
if(res < 0){ std::vector<char> ToSend(sizeof(DataSize) + OutData.size());
debug("(Core) send failed with error: " + std::to_string(WSAGetLastError())); std::copy_n(reinterpret_cast<char*>(&DataSize), sizeof(DataSize), ToSend.begin());
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(SOCKET Client){ void GameHandler(asio::ip::tcp::socket& Client) {
std::vector<char> data {};
int32_t Size,Temp,Rcv;
char Header[10] = {0};
do { do {
Rcv = 0; try {
do{ ReceiveFromGame(Client, data);
Temp = recv(Client,&Header[Rcv],1,0); Parse(data, Client);
if(Temp < 1)break; } catch (const std::exception& e) {
if(!isdigit(Header[Rcv]) && Header[Rcv] != '>') { error(std::string("Error while receiving from game: ") + e.what());
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;
} }
std::string Ret(Size,0); } while (true);
Rcv = 0;
do{
Temp = recv(Client,&Ret[Rcv],Size-Rcv,0);
if(Temp < 1)break;
Rcv += Temp;
}while(Rcv < Size);
if(Temp < 1)break;
std::thread Respond(Parse, Ret, Client);
Respond.detach();
}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);
} }
@@ -187,62 +282,52 @@ 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
ZeroMemory(&hints, sizeof(hints)); asio::ip::tcp::endpoint listen_ep(asio::ip::address::from_string("0.0.0.0"), static_cast<uint16_t>(DEFAULT_PORT));
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;
}
hints.ai_family = AF_INET; auto acceptor = asio::ip::tcp::acceptor(io, listen_ep);
hints.ai_socktype = SOCK_STREAM; acceptor.listen(asio::ip::tcp::socket::max_listen_connections, ec);
hints.ai_protocol = IPPROTO_TCP; if (ec) {
hints.ai_flags = AI_PASSIVE; ::error(fmt::format("listen() failed, which is needed for the launcher to operate. Error: {}",
iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT).c_str(), &hints, &res); ec.message()));
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 {
CSocket = accept(LSocket, nullptr, nullptr); auto CSocket = acceptor.accept(ec);
if (CSocket == -1) { if (ec) {
error("(Core) accept failed with error: " + std::to_string(WSAGetLastError())); error(fmt::format("(Core) accept failed with error: {}", ec.message()));
continue; continue;
} }
localRes(); localRes();
info("Game Connected!"); info("Game Connected!");
GameHandler(CSocket); GameHandler(CSocket);
warn("Game Reconnecting..."); warn("Game Reconnecting...");
}while(CSocket); } while (LSocket.is_open());
KillSocket(LSocket); KillSocket(LSocket);
WSACleanup(); WSACleanup();
} }
@@ -268,7 +353,8 @@ int Handle(EXCEPTION_POINTERS *ep){
#if not defined(__MINGW32__) and not defined(__linux__) #if not defined(__MINGW32__) and not defined(__linux__)
} __except (Handle(GetExceptionInformation())) { } } __except (Handle(GetExceptionInformation())) { }
#elif not defined(__MINGW32__) and defined(__linux__) #elif not defined(__MINGW32__) and defined(__linux__)
} catch(...){ }
catch (...) {
except("(Core) Code : " + std::string(strerror(errno))); except("(Core) Code : " + std::string(strerror(errno)));
} }
#endif #endif

5
src/Network/DNS.cpp Executable file → Normal file
View File

@@ -12,14 +12,15 @@
#include <winsock2.h> #include <winsock2.h>
#elif defined(__linux__) #elif defined(__linux__)
#include "linuxfixes.h" #include "linuxfixes.h"
#include <netdb.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h>
#endif #endif
#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)return IP; if (IP.find_first_not_of("0123456789.") == -1)
return IP;
hostent* host; hostent* host;
#ifdef _WIN32 #ifdef _WIN32
WSADATA wsaData; WSADATA wsaData;

294
src/Network/GlobalHandler.cpp Executable file → Normal file
View File

@@ -5,43 +5,62 @@
/// ///
/// 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>
#if defined(_WIN32) #if defined(_WIN32)
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#elif defined(__linux__) #elif defined(__linux__)
#include "linuxfixes.h" #include "linuxfixes.h"
#include <arpa/inet.h>
#include <cstring>
#include <netdb.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <cstring>
#endif #endif
#include "Logger.h" #include "Logger.h"
#include <charconv> #include <charconv>
#include <fmt/format.h>
#include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <mutex>
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;
SOCKET CSocket = -1; std::shared_ptr<asio::ip::tcp::socket> CSocket = nullptr;
SOCKET GSocket = -1; std::shared_ptr<asio::ip::tcp::socket> GSocket = nullptr;
int KillSocket(uint64_t Dead){ void KillSocket(std::shared_ptr<asio::ip::tcp::socket>& Dead) {
if(Dead == (SOCKET)-1){ if (!Dead)
debug("Kill socket got -1 returning..."); return;
return 0; asio::error_code ec;
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;
Dead->shutdown(asio::socket_base::shutdown_both, ec);
} }
return a;
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) {
@@ -55,50 +74,49 @@ bool CheckBytes(uint32_t Bytes){
return true; return true;
} }
void GameSend(std::string Data){ void GameSend(std::string_view RawData) {
static std::mutex Lock; static std::mutex Lock;
std::scoped_lock Guard(Lock); std::scoped_lock Guard(Lock);
if(TCPTerminate || !GConnected || CSocket == -1)return; if (TCPTerminate || !GConnected || CSocket == nullptr)
return;
int32_t Size, Temp, Sent; int32_t Size, Temp, Sent;
Data += '\n'; 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
if(Size > 1000){ asio::error_code ec;
debug("Launcher -> game (" +std::to_string(Size)+")"); asio::write(*CSocket, asio::buffer(Data), ec);
if (ec) {
debug(fmt::format("(TCP CB) recv failed with error: {}", ec.message()));
KillSocket(TCPSock);
Terminate = true;
} }
#endif
do{
if(Sent > -1){
Temp = send(CSocket, &Data[Sent], Size - Sent, 0);
}
if(!CheckBytes(Temp))return;
Sent += Temp;
}while(Sent < Size);
}
void ServerSend(std::string Data, bool Rel){
if(Terminate || Data.empty())return;
if(Data.find("Zp") != std::string::npos && Data.size() > 500){
abort();
} }
void ServerSend(const std::vector<char>& Data, bool Rel) {
if (Terminate || Data.empty())
return;
char C = 0; char C = 0;
bool Ack = false; bool Ack = false;
int DLen = int(Data.length()); int DLen = int(Data.size());
if(DLen > 3)C = Data.at(0); if (DLen > 3)
if (C == 'O' || C == 'T')Ack = true; C = Data.at(0);
if(C == 'N' || C == 'W' || C == 'Y' || C == 'V' || C == 'E' || C == 'C')Rel = true; if (C == 'O' || C == 'T')
Ack = true;
if (C == 'N' || C == 'W' || C == 'Y' || C == 'V' || C == 'E' || C == 'C')
Rel = true;
if (compressBound(Data.size()) > 1024)
Rel = true;
if (Ack || Rel) { if (Ack || Rel) {
if(Ack || DLen > 1000)SendLarge(Data); if (Ack || DLen > 1000)
else TCPSend(Data,TCPSock); SendLarge(Data);
}else UDPSend(Data); else if (TCPSock)
TCPSend(Data, *TCPSock);
if (DLen > 1000) { } else
debug("(Launcher->Server) Bytes sent: " + std::to_string(Data.length()) + " : " UDPSend(Data);
+ Data.substr(0, 10)
+ Data.substr(Data.length() - 10));
}else if(C == 'Z'){
//debug("(Game->Launcher) : " + Data);
}
} }
void NetReset() { void NetReset() {
@@ -107,89 +125,41 @@ void NetReset(){
Terminate = false; Terminate = false;
UlStatus = "Ulstart"; UlStatus = "Ulstart";
MStatus = " "; MStatus = " ";
if(UDPSock != (SOCKET)(-1)){ if (UDPSock != nullptr) {
debug("Terminating UDP Socket : " + std::to_string(TCPSock)); KillSocket(*UDPSock);
KillSocket(UDPSock);
} }
UDPSock = -1; UDPSock = nullptr;
if(TCPSock != (SOCKET)(-1)){ if (TCPSock != nullptr) {
debug("Terminating TCP Socket : " + std::to_string(TCPSock)); KillSocket(*TCPSock);
KillSocket(TCPSock);
} }
TCPSock = -1; TCPSock = nullptr;
if(GSocket != (SOCKET)(-1)){ if (GSocket != nullptr) {
debug("Terminating GTCP Socket : " + std::to_string(GSocket)); KillSocket(*GSocket);
KillSocket(GSocket);
} }
GSocket = -1; GSocket = nullptr;
} }
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_INET;
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("p",false); ServerSend(strtovec("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));
} }
} }
int ClientID = -1; int ClientID = -1;
void ParserAsync(const std::string& Data){ void ParserAsync(std::string_view Data) {
if(Data.empty())return; if (Data.empty())
return;
char Code = Data.at(0), SubCode = 0; char Code = Data.at(0), SubCode = 0;
if(Data.length() > 1)SubCode = Data.at(1); if (Data.length() > 1)
SubCode = Data.at(1);
switch (Code) { switch (Code) {
case 'p': case 'p':
PingEnd = std::chrono::high_resolution_clock::now(); PingEnd = std::chrono::high_resolution_clock::now();
if(PingStart > PingEnd)ping = 0; if (PingStart > PingEnd)
else ping = int(std::chrono::duration_cast<std::chrono::milliseconds>(PingEnd-PingStart).count()); ping = 0;
else
ping = int(std::chrono::duration_cast<std::chrono::milliseconds>(PingEnd - PingStart).count());
return; return;
case 'M': case 'M':
MStatus = Data; MStatus = Data;
@@ -200,20 +170,50 @@ void ParserAsync(const std::string& Data){
} }
GameSend(Data); GameSend(Data);
} }
void ServerParser(const std::string& Data){ void ServerParser(std::string_view Data) {
ParserAsync(Data); ParserAsync(Data);
} }
void NetMain(const std::string& IP, int Port){ void NetMain(asio::ip::address addr, uint16_t port) {
std::thread Ping(AutoPing); std::thread Ping(AutoPing);
Ping.detach(); Ping.detach();
UDPClientMain(IP,Port); UDPClientMain(addr, port);
CServer = true; CServer = true;
Terminate = true; Terminate = true;
info("Connection Terminated!"); info("Connection Terminated!");
} }
void TCPGameServer(const std::string& IP, int Port){ void TCPGameServer(asio::ip::tcp::socket&& Socket) {
GSocket = SetupListener(); asio::ip::tcp::endpoint listen_ep(asio::ip::address::from_string("0.0.0.0"), static_cast<uint16_t>(DEFAULT_PORT + 1));
while (!TCPTerminate && GSocket != -1){ asio::ip::tcp::socket listener(io);
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,55 +224,35 @@ void TCPGameServer(const std::string& IP, int Port){
break; break;
} }
if (CServer) { if (CServer) {
std::thread Client(TCPClientMain, IP, Port); std::thread Client(TCPClientMain, std::move(Socket));
Client.detach(); Client.detach();
} }
CSocket = accept(GSocket, nullptr, nullptr);
if (CSocket == -1) { CSocket = std::make_shared<asio::ip::tcp::socket>(acceptor.accept());
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, IP, Port); std::thread t1(NetMain, CSocket->remote_endpoint().address(), CSocket->remote_endpoint().port());
t1.detach(); t1.detach();
CServer = false; CServer = false;
} }
int32_t Size,Temp,Rcv; std::vector<char> data {};
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
do{ while (!TCPTerminate && !CSocket) {
Rcv = 0; try {
ReceiveFromGame(*CSocket, data);
do{ ServerSend(data, false);
Temp = recv(CSocket,&Header[Rcv],1,0); } catch (const std::exception& e) {
if(Temp < 1 || TCPTerminate)break; error(std::string("Error while receiving from game: ") + e.what());
}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 != SOCKET_ERROR)KillSocket(CSocket); if (CSocket != nullptr)
KillSocket(CSocket);
debug("END OF GAME SERVER"); debug("END OF GAME SERVER");
} }

91
src/Network/Http.cpp Executable file → Normal file
View File

@@ -7,21 +7,56 @@
/// ///
#define CPPHTTPLIB_OPENSSL_SUPPORT #define CPPHTTPLIB_OPENSSL_SUPPORT
#include <iostream>
#include <Logger.h>
#include <fstream>
#include "Http.h" #include "Http.h"
#include <mutex> #include <Logger.h>
#include <cmath> #include <cmath>
#include <filesystem>
#include <fstream>
#include <httplib.h> #include <httplib.h>
#include <iostream>
#include <mutex>
#include <nlohmann/json.hpp>
std::string HTTP::Codes_[] = void WriteHttpDebug(const httplib::Client& client, const std::string& method, const std::string& target, const httplib::Result& result) try {
{ const std::filesystem::path folder = ".https_debug";
"Success","Unknown","Connection","BindIPAddress", std::filesystem::create_directories(folder);
"Read","Write","ExceedRedirectCount","Canceled", if (!std::filesystem::exists(folder / "WHAT IS THIS FOLDER.txt")) {
"SSLConnection","SSLLoadingCerts","SSLServerVerification", std::ofstream ignore { folder / "WHAT IS THIS FOLDER.txt" };
"UnsupportedMultipartBoundaryChars","Compression" ignore << "This folder exists to help debug current issues with the backend. Do not share this folder with anyone but BeamMP staff. It contains detailed logs of any failed http requests." << std::endl;
}
const auto file = folder / (method + ".json");
// 1 MB limit
if (std::filesystem::exists(file) && std::filesystem::file_size(file) > 1'000'000) {
std::filesystem::rename(file, file.generic_string() + ".bak");
}
std::ofstream of { file, std::ios::app };
nlohmann::json js {
{ "utc", std::chrono::system_clock::now().time_since_epoch().count() },
{ "target", target },
{ "client_info", {
{ "openssl_verify_result", client.get_openssl_verify_result() },
{ "host", client.host() },
{ "port", client.port() },
{ "socket_open", client.is_socket_open() },
{ "valid", client.is_valid() },
} },
}; };
if (result) {
auto value = result.value();
js["result"] = {};
js["result"]["body"] = value.body;
js["result"]["status"] = value.status;
js["result"]["headers"] = value.headers;
js["result"]["version"] = value.version;
js["result"]["location"] = value.location;
js["result"]["reason"] = value.reason;
}
of << js.dump();
} catch (const std::exception& e) {
error(e.what());
}
bool HTTP::isDownload = false; bool HTTP::isDownload = false;
std::string HTTP::Get(const std::string& IP) { std::string HTTP::Get(const std::string& IP) {
static std::mutex Lock; static std::mutex Lock;
@@ -35,16 +70,19 @@ std::string HTTP::Get(const std::string &IP) {
auto res = cli.Get(IP.substr(pos).c_str(), ProgressBar); auto res = cli.Get(IP.substr(pos).c_str(), ProgressBar);
std::string Ret; std::string Ret;
if(res.error() == 0){ if (res) {
if (res->status == 200) { if (res->status == 200) {
Ret = res->body; Ret = res->body;
}else error(res->reason); } else {
WriteHttpDebug(cli, "GET", IP, res);
error("Failed to GET '" + IP + "': " + res->reason + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result()));
}
} else { } else {
if (isDownload) { if (isDownload) {
std::cout << "\n"; std::cout << "\n";
} }
error("HTTP Get failed on " + Codes_[res.error()]); WriteHttpDebug(cli, "GET", IP, res);
error("HTTP Get failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result()));
} }
return Ret; return Ret;
@@ -63,28 +101,32 @@ std::string HTTP::Post(const std::string& IP, const std::string& Fields) {
if (!Fields.empty()) { if (!Fields.empty()) {
httplib::Result res = cli.Post(IP.substr(pos).c_str(), Fields, "application/json"); httplib::Result res = cli.Post(IP.substr(pos).c_str(), Fields, "application/json");
if(res.error() == 0) { if (res) {
if (res->status != 200) { if (res->status != 200) {
error(res->reason); error(res->reason);
} }
Ret = res->body; Ret = res->body;
} else { } else {
error("HTTP Post failed on " + Codes_[res.error()]); WriteHttpDebug(cli, "POST", IP, res);
error("HTTP Post failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result()));
} }
} else { } else {
httplib::Result res = cli.Post(IP.substr(pos).c_str()); httplib::Result res = cli.Post(IP.substr(pos).c_str());
if(res.error() == 0) { if (res) {
if (res->status != 200) { if (res->status != 200) {
error(res->reason); error(res->reason);
} }
Ret = res->body; Ret = res->body;
} else { } else {
error("HTTP Post failed on " + Codes_[res.error()]); WriteHttpDebug(cli, "POST", IP, res);
error("HTTP Post failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result()));
} }
} }
if(Ret.empty())return "-1"; if (Ret.empty())
else return Ret; return "-1";
else
return Ret;
} }
bool HTTP::ProgressBar(size_t c, size_t t) { bool HTTP::ProgressBar(size_t c, size_t t) {
@@ -96,8 +138,10 @@ bool HTTP::ProgressBar(size_t c, size_t t){
std::cout << round(c / double(t) * 100); std::cout << round(c / double(t) * 100);
std::cout << "% ] ["; std::cout << "% ] [";
int i; int i;
for (i = 0; i <= progress_bar_adv; i++)std::cout << "#"; for (i = 0; i <= progress_bar_adv; i++)
for (i = 0; i < 25 - progress_bar_adv; i++)std::cout << "."; std::cout << "#";
for (i = 0; i < 25 - progress_bar_adv; i++)
std::cout << ".";
std::cout << "]"; std::cout << "]";
last_progress = round(c / double(t) * 100); last_progress = round(c / double(t) * 100);
} }
@@ -112,7 +156,8 @@ bool HTTP::Download(const std::string &IP, const std::string &Path) {
std::string Ret = Get(IP); std::string Ret = Get(IP);
isDownload = false; isDownload = false;
if(Ret.empty())return false; if (Ret.empty())
return false;
std::ofstream File(Path, std::ios::binary); std::ofstream File(Path, std::ios::binary);
if (File.is_open()) { if (File.is_open()) {

149
src/Network/Resources.cpp Executable file → Normal file
View File

@@ -7,30 +7,32 @@
/// ///
#include "Network/network.hpp" #include "Network/network.hpp"
#include "fmt/core.h"
#if defined(_WIN32) #if defined(_WIN32)
#include <ws2tcpip.h> #include <ws2tcpip.h>
#elif defined(__linux__) #elif defined(__linux__)
#include <sys/socket.h> #include <arpa/inet.h>
#include <sys/types.h>
#include <cstring> #include <cstring>
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
#include <arpa/inet.h> #include <sys/socket.h>
#include <sys/types.h>
#endif #endif
#include <filesystem>
#include "Startup.h"
#include "Logger.h" #include "Logger.h"
#include <iostream> #include "Startup.h"
#include <atomic>
#include <cmath>
#include <cstring> #include <cstring>
#include <filesystem>
#include <fstream> #include <fstream>
#include <future>
#include <asio.hpp>
#include <iostream>
#include <string> #include <string>
#include <thread> #include <thread>
#include <atomic>
#include <vector> #include <vector>
#include <future>
#include <cmath>
namespace fs = std::filesystem; namespace fs = std::filesystem;
std::string ListOfMods; std::string ListOfMods;
@@ -40,10 +42,12 @@ std::vector<std::string> Split(const std::string& String,const std::string& deli
std::string token, s = String; std::string token, s = String;
while ((pos = s.find(delimiter)) != std::string::npos) { while ((pos = s.find(delimiter)) != std::string::npos) {
token = s.substr(0, pos); token = s.substr(0, pos);
if(!token.empty())Val.push_back(token); if (!token.empty())
Val.push_back(token);
s.erase(0, pos + delimiter.length()); s.erase(0, pos + delimiter.length());
} }
if(!s.empty())Val.push_back(s); if (!s.empty())
Val.push_back(s);
return Val; return Val;
} }
@@ -64,15 +68,14 @@ void WaitForConfirm(){
ModLoaded = false; ModLoaded = false;
} }
void Abord() { void Abord() {
Terminate = true; Terminate = true;
TCPTerminate = true; TCPTerminate = true;
info("Terminated!"); info("Terminated!");
} }
std::string Auth(SOCKET Sock){ std::string Auth(asio::ip::tcp::socket& Sock) {
TCPSend("VC" + GetVer(),Sock); TCPSend(strtovec("VC" + GetVer()), Sock);
auto Res = TCPRcv(Sock); auto Res = TCPRcv(Sock);
@@ -81,8 +84,9 @@ std::string Auth(SOCKET Sock){
return ""; return "";
} }
TCPSend(PublicKey,Sock); TCPSend(strtovec(PublicKey), Sock);
if(Terminate)return ""; if (Terminate)
return "";
Res = TCPRcv(Sock); Res = TCPRcv(Sock);
if (Res.empty() || Res[0] != 'P') { if (Res.empty() || Res[0] != 'P') {
@@ -98,8 +102,9 @@ std::string Auth(SOCKET Sock){
UUl("Authentication failed!"); UUl("Authentication failed!");
return ""; return "";
} }
TCPSend("SR",Sock); TCPSend(strtovec("SR"), Sock);
if(Terminate)return ""; if (Terminate)
return "";
Res = TCPRcv(Sock); Res = TCPRcv(Sock);
@@ -111,7 +116,7 @@ std::string Auth(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("Done",Sock); TCPSend(strtovec("Done"), Sock);
info("Done!"); info("Done!");
return ""; return "";
} }
@@ -119,8 +124,10 @@ std::string Auth(SOCKET Sock){
} }
void UpdateUl(bool D, const std::string& msg) { void UpdateUl(bool D, const std::string& msg) {
if(D)UlStatus = "UlDownloading Resource " + msg; if (D)
else UlStatus = "UlLoading Resource " + msg; UlStatus = "UlDownloading Resource " + msg;
else
UlStatus = "UlLoading Resource " + msg;
} }
void AsyncUpdate(uint64_t& Rcv, uint64_t Size, const std::string& Name) { void AsyncUpdate(uint64_t& Rcv, uint64_t Size, const std::string& Name) {
@@ -132,21 +139,18 @@ void AsyncUpdate(uint64_t& Rcv,uint64_t Size,const std::string& Name){
} while (!Terminate && Rcv < Size); } while (!Terminate && Rcv < Size);
} }
char* TCPRcvRaw(SOCKET Sock,uint64_t& GRcv, uint64_t Size){ char* TCPRcvRaw(asio::ip::tcp::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)Len = 1000000; if (Len > 1000000)
int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL); Len = 1000000;
if(Temp < 1){ int32_t Temp = asio::read(Sock, asio::buffer(&File[Rcv], Len), ec);
info(std::to_string(Temp)); if (ec) {
UUl("Socket Closed Code 1"); ::error(fmt::format("Failed to receive data from server: {}", ec.message()));
UUl("Failed to receive data from server, connection closed (Code 1)");
KillSocket(Sock); KillSocket(Sock);
Terminate = true; Terminate = true;
delete[] File; delete[] File;
@@ -157,29 +161,23 @@ char* TCPRcvRaw(SOCKET Sock,uint64_t& GRcv, uint64_t Size){
} while (Rcv < Size && !Terminate); } while (Rcv < Size && !Terminate);
return File; return File;
} }
void MultiKill(SOCKET Sock,SOCKET Sock1){ void MultiKill(asio::ip::tcp::socket& Sock, asio::ip::tcp::socket& Sock1) {
KillSocket(Sock1); KillSocket(Sock1);
KillSocket(Sock); KillSocket(Sock);
Terminate = true; Terminate = true;
} }
SOCKET InitDSock(){ std::shared_ptr<asio::ip::tcp::socket> InitDSock(asio::ip::tcp::endpoint ep) {
SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); auto DSock = std::make_shared<asio::ip::tcp::socket>(io);
SOCKADDR_IN ServerAddr; asio::error_code ec;
if(DSock < 1){ DSock->connect(ep, ec);
if (ec) {
KillSocket(DSock); KillSocket(DSock);
Terminate = true; Terminate = true;
return 0; return nullptr;
}
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(LastPort);
inet_pton(AF_INET, 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) };
if(send(DSock,Code,2,0) != 2){ asio::write(*DSock, asio::buffer(Code, 2), ec);
if (ec) {
KillSocket(DSock); KillSocket(DSock);
Terminate = true; Terminate = true;
return 0; return 0;
@@ -187,7 +185,7 @@ SOCKET InitDSock(){
return DSock; return DSock;
} }
std::string MultiDownload(SOCKET MSock,SOCKET DSock, uint64_t Size, const std::string& Name){ std::string MultiDownload(asio::ip::tcp::socket& MSock, asio::ip::tcp::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;
@@ -213,8 +211,8 @@ std::string MultiDownload(SOCKET MSock,SOCKET DSock, uint64_t Size, const std::s
return ""; return "";
} }
if(Au.joinable())Au.join(); if (Au.joinable())
Au.join();
/// omg yes very ugly my god but i was in a rush will revisit /// omg yes very ugly my god but i was in a rush will revisit
std::string Ret(Size, 0); std::string Ret(Size, 0);
@@ -233,9 +231,10 @@ void InvalidResource(const std::string& File){
Terminate = true; Terminate = true;
} }
void SyncResources(SOCKET Sock){ void SyncResources(asio::ip::tcp::socket& Sock) {
std::string Ret = Auth(Sock); std::string Ret = Auth(Sock);
if(Ret.empty())return; if (Ret.empty())
return;
info("Checking Resources..."); info("Checking Resources...");
CheckForDir(); CheckForDir();
@@ -253,8 +252,10 @@ void SyncResources(SOCKET Sock){
t += name.substr(name.find_last_of('/') + 1) + ";"; t += name.substr(name.find_last_of('/') + 1) + ";";
} }
} }
if(t.empty())ListOfMods = "-"; if (t.empty())
else ListOfMods = t; ListOfMods = "-";
else
ListOfMods = t;
t.clear(); t.clear();
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('/');
@@ -263,19 +264,23 @@ void SyncResources(SOCKET Sock){
InvalidResource(*FN); InvalidResource(*FN);
return; return;
} }
if (pos == std::string::npos)continue; if (pos == std::string::npos)
continue;
Amount++; Amount++;
} }
if(!FNames.empty())info("Syncing..."); if (!FNames.empty())
SOCKET DSock = InitDSock(); info("Syncing...");
auto DSock = InitDSock(Sock.remote_endpoint());
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) {
a = "Resources" + FN->substr(pos); a = "Resources" + FN->substr(pos);
} else continue; } else
continue;
Pos++; Pos++;
if (fs::exists(a)) { if (fs::exists(a)) {
if (FS->find_first_not_of("0123456789") != std::string::npos)continue; if (FS->find_first_not_of("0123456789") != std::string::npos)
continue;
if (fs::file_size(a) == std::stoull(*FS)) { if (fs::file_size(a) == std::stoull(*FS)) {
UpdateUl(false, std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + a.substr(a.find_last_of('/'))); UpdateUl(false, std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + a.substr(a.find_last_of('/')));
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
@@ -283,17 +288,17 @@ void SyncResources(SOCKET Sock){
if (!fs::exists(GetGamePath() + "mods/multiplayer")) { if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
fs::create_directories(GetGamePath() + "mods/multiplayer"); fs::create_directories(GetGamePath() + "mods/multiplayer");
} }
auto modname = a.substr(a.find_last_of('/'));
std::string FName = a.substr(a.find_last_of('/'));
// Linux version of the game doesnt support uppercase letters in mod names
#if defined(__linux__) #if defined(__linux__)
for(char &c : FName){ // Linux version of the game doesnt support uppercase letters in mod names
for (char& c : modname) {
c = ::tolower(c); c = ::tolower(c);
} }
#endif #endif
auto name = GetGamePath() + "mods/multiplayer" + modname;
fs::copy_file(a, GetGamePath() + "mods/multiplayer" + FName, auto tmp_name = name + ".tmp";
fs::copy_options::overwrite_existing); fs::copy_file(a, tmp_name, fs::copy_options::overwrite_existing);
fs::rename(tmp_name, name);
} catch (std::exception& e) { } catch (std::exception& e) {
error("Failed copy to the mods folder! " + std::string(e.what())); error("Failed copy to the mods folder! " + std::string(e.what()));
Terminate = true; Terminate = true;
@@ -301,12 +306,13 @@ void SyncResources(SOCKET Sock){
} }
WaitForConfirm(); WaitForConfirm();
continue; continue;
}else remove(a.c_str()); } else
remove(a.c_str());
} }
CheckForDir(); CheckForDir();
std::string FName = a.substr(a.find_last_of('/')); std::string FName = a.substr(a.find_last_of('/'));
do { do {
TCPSend("f" + *FN,Sock); TCPSend(strtovec("f" + *FN), Sock);
std::string Data = TCPRcv(Sock); std::string Data = TCPRcv(Sock);
if (Data == "CO" || Terminate) { if (Data == "CO" || Terminate) {
@@ -317,9 +323,10 @@ void SyncResources(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)break; if (Terminate)
break;
UpdateUl(false, std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName); UpdateUl(false, std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName);
std::ofstream LFS; std::ofstream LFS;
LFS.open(a.c_str(), std::ios_base::app | std::ios::binary); LFS.open(a.c_str(), std::ios_base::app | std::ios::binary);
@@ -347,7 +354,7 @@ void SyncResources(SOCKET Sock){
} }
KillSocket(DSock); KillSocket(DSock);
if (!Terminate) { if (!Terminate) {
TCPSend("Done",Sock); TCPSend(strtovec("Done"), Sock);
info("Done!"); info("Done!");
} else { } else {
UlStatus = "Ulstart"; UlStatus = "Ulstart";

131
src/Network/VehicleData.cpp Executable file → Normal file
View File

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

125
src/Network/VehicleEvent.cpp Executable file → Normal file
View File

@@ -6,28 +6,29 @@
/// Created by Anonymous275 on 5/8/2020 /// Created by Anonymous275 on 5/8/2020
/// ///
#include <chrono>
#include <vector>
#include "Logger.h" #include "Logger.h"
#include <iostream> #include "fmt/format.h"
#include <Zlib/Compressor.h> #include <Zlib/Compressor.h>
#include <chrono>
#include <iostream>
#include <vector>
#if defined(_WIN32) #if defined(_WIN32)
#include <ws2tcpip.h> #include <ws2tcpip.h>
#elif defined(__linux__) #elif defined(__linux__)
#include <sys/socket.h> #include <arpa/inet.h>
#include <sys/types.h>
#include <cstring> #include <cstring>
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
#include <arpa/inet.h> #include <sys/socket.h>
#include <sys/types.h>
#endif #endif
#include "Network/network.hpp" #include "Network/network.hpp"
int LastPort; int LastPort;
std::string LastIP; std::string LastIP;
SOCKET TCPSock = -1; std::shared_ptr<asio::ip::tcp::socket> TCPSock = nullptr;
bool CheckBytes(int32_t Bytes) { bool CheckBytes(int32_t Bytes) {
if (Bytes == 0) { if (Bytes == 0) {
@@ -46,8 +47,8 @@ void UUl(const std::string& R){
UlStatus = "UlDisconnected: " + R; UlStatus = "UlDisconnected: " + R;
} }
void TCPSend(const std::string&Data,uint64_t Sock){ void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock) {
if(Sock == -1){ if (!Sock.is_open()) {
Terminate = true; Terminate = true;
UUl("Invalid Socket"); UUl("Invalid Socket");
return; return;
@@ -57,116 +58,72 @@ void TCPSend(const std::string&Data,uint64_t 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 += Data; Send += std::string(Data.data(), Data.size());
// 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;
do{ asio::error_code ec;
if (size_t(Sent) >= Send.size()) { asio::write(Sock, asio::buffer(Send), ec);
error("string OOB in " + std::string(__func__)); if (ec) {
UUl("TCP Send OOB"); UUl(fmt::format("Failed to send data: {}", ec.message()));
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(SOCKET Sock){ std::string TCPRcv(asio::ip::tcp::socket& Sock) {
if(Sock == -1){ if (!Sock.is_open()) {
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));
do{ asio::error_code ec;
Temp = recv(Sock,&Data[BytesRcv],4-BytesRcv,0); asio::read(Sock, asio::buffer(Data), ec);
if(!CheckBytes(Temp)){ if (ec) {
UUl("Socket Closed Code 3"); UUl(fmt::format("Failed to receive header: {}", ec.message()));
return "";
} }
BytesRcv += Temp;
}while(BytesRcv < 4);
memcpy(&Header, &Data[0], sizeof(Header)); memcpy(&Header, &Data[0], sizeof(Header));
if(!CheckBytes(BytesRcv)){
UUl("Socket Closed Code 4");
return "";
}
Data.resize(Header); Data.resize(Header);
BytesRcv = 0; asio::read(Sock, asio::buffer(Data), ec);
do{ if (ec) {
Temp = recv(Sock,&Data[BytesRcv],Header-BytesRcv,0); UUl(fmt::format("Failed to receive data: {}", ec.message()));
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:") {
Ret = DeComp(Ret.substr(4)); auto substr = Ret.substr(4);
auto res = DeComp(strtovec(substr));
Ret = std::string(res.data(), res.size());
} }
#ifdef DEBUG #ifdef DEBUG
// debug("Parsing from server -> " + std::to_string(Ret.size())); // debug("Parsing from server -> " + std::to_string(Ret.size()));
#endif #endif
if(Ret[0] == 'E' || Ret[0] == 'K')UUl(Ret.substr(1)); if (Ret[0] == 'E' || Ret[0] == 'K')
UUl(Ret.substr(1));
return Ret; return Ret;
} }
void TCPClientMain(const std::string& IP,int Port){ void TCPClientMain(asio::ip::tcp::socket&& socket) {
LastIP = IP; if (!TCPSock) {
LastPort = Port; return;
}
LastIP = socket.remote_endpoint().address().to_string();
LastPort = socket.remote_endpoint().port();
SOCKADDR_IN ServerAddr; SOCKADDR_IN ServerAddr;
int RetCode; int RetCode;
#ifdef _WIN32 TCPSock = std::make_shared<asio::ip::tcp::socket>(std::move(socket));
WSADATA wsaData;
WSAStartup(514, &wsaData); //2.2
#endif
TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(TCPSock == -1){
printf("Client: socket failed! Error code: %d\n", WSAGetLastError());
WSACleanup();
return;
}
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port);
inet_pton(AF_INET, 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';
send(TCPSock, &Code, 1, 0); asio::write(*TCPSock, asio::buffer(&Code, 1));
SyncResources(TCPSock); SyncResources(*TCPSock);
while(!Terminate){ while (!Terminate && TCPSock) {
ServerParser(TCPRcv(TCPSock)); ServerParser(TCPRcv(*TCPSock));
} }
GameSend("T"); GameSend("T");
////Game Send Terminate KillSocket(TCPSock);
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
} }

35
src/NetworkHelpers.cpp Normal file
View File

@@ -0,0 +1,35 @@
#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");
}
}

73
src/Security/BeamNG.cpp Executable file → Normal file
View File

@@ -11,9 +11,9 @@
#include <windows.h> #include <windows.h>
#elif defined(__linux__) #elif defined(__linux__)
#include "vdf_parser.hpp" #include "vdf_parser.hpp"
#include <vector>
#include <pwd.h> #include <pwd.h>
#include <unistd.h> #include <unistd.h>
#include <vector>
#endif #endif
#include "Logger.h" #include "Logger.h"
#include <fstream> #include <fstream>
@@ -29,8 +29,7 @@ std::string GameDir;
void lowExit(int code) { void lowExit(int code) {
TraceBack = 0; TraceBack = 0;
std::string msg = std::string msg = "Failed to find the game please launch it. Report this if the issue persists code ";
"Failed to find the game please launch it. Report this if the issue persists code ";
error(msg + std::to_string(code)); error(msg + std::to_string(code));
std::this_thread::sleep_for(std::chrono::seconds(10)); std::this_thread::sleep_for(std::chrono::seconds(10));
exit(2); exit(2);
@@ -122,18 +121,30 @@ std::string QueryKey(HKEY hKey,int ID){
std::string key = achValue; std::string key = achValue;
switch (ID) { switch (ID) {
case 1: if(key == "SteamExe"){ case 1:
if (key == "SteamExe") {
auto p = data.find_last_of("/\\"); auto p = data.find_last_of("/\\");
if (p != std::string::npos) { if (p != std::string::npos) {
return data.substr(0, p); return data.substr(0, p);
} }
} }
break; break;
case 2: if(key == "Name" && data == "BeamNG.drive")return data;break; case 2:
case 3: if(key == "rootpath")return data;break; if (key == "Name" && data == "BeamNG.drive")
case 4: if(key == "userpath_override")return data; return data;
case 5: if(key == "Local AppData")return data; break;
default: break; case 3:
if (key == "rootpath")
return data;
break;
case 4:
if (key == "userpath_override")
return data;
case 5:
if (key == "Local AppData")
return data;
default:
break;
} }
} }
} }
@@ -158,9 +169,9 @@ void FileList(std::vector<std::string>&a,const std::string& Path){
for (const auto& entry : fs::directory_iterator(Path)) { for (const auto& entry : fs::directory_iterator(Path)) {
const auto& DPath = entry.path(); const auto& DPath = entry.path();
if (!entry.is_directory()) { if (!entry.is_directory()) {
a.emplace_back(DPath.u8string()); a.emplace_back(DPath.string());
}else if(NameValid(DPath.filename().u8string())){ } else if (NameValid(DPath.filename().string())) {
FileList(a, DPath.u8string()); FileList(a, DPath.string());
} }
} }
} }
@@ -179,9 +190,11 @@ bool Find(const std::string& FName,const std::string& Path){
bool FindHack(const std::string& Path) { bool FindHack(const std::string& Path) {
bool s = true; bool s = true;
for (const auto& entry : fs::directory_iterator(Path)) { for (const auto& entry : fs::directory_iterator(Path)) {
std::string Name = entry.path().filename().u8string(); std::string Name = entry.path().filename().string();
for(char&c : Name)c = char(tolower(c)); for (char& c : Name)
if(Name == "steam.exe")s = false; c = char(tolower(c));
if (Name == "steam.exe")
s = false;
if (Name.find("greenluma") != -1) { if (Name.find("greenluma") != -1) {
error("Found malicious file/folder \"" + Name + "\""); error("Found malicious file/folder \"" + Name + "\"");
return true; return true;
@@ -203,10 +216,12 @@ std::vector<std::string> GetID(const std::string& log){
std::stringstream ss(vec); std::stringstream ss(vec);
bool S = false; bool S = false;
while (std::getline(ss, t, '{')) { while (std::getline(ss, t, '{')) {
if(!S)S = true; if (!S)
S = true;
else { else {
for (char& c : t) { for (char& c : t) {
if(isdigit(c))r += c; if (isdigit(c))
r += c;
} }
break; break;
} }
@@ -222,7 +237,8 @@ std::vector<std::string> GetID(const std::string& log){
} }
for (char& c : t) { for (char& c : t) {
if (c == '"') { if (c == '"') {
if(!S)S = true; if (!S)
S = true;
else { else {
if (r.length() > 10) { if (r.length() > 10) {
Ret.emplace_back(r); Ret.emplace_back(r);
@@ -232,7 +248,8 @@ std::vector<std::string> GetID(const std::string& log){
continue; continue;
} }
} }
if(isdigit(c))r += c; if (isdigit(c))
r += c;
} }
} }
vec.clear(); vec.clear();
@@ -253,7 +270,8 @@ std::string GetManifest(const std::string& Man){
pos += int(ToFind.length()); pos += int(ToFind.length());
vec = vec.substr(pos); vec = vec.substr(pos);
return vec.substr(0, vec.find('\"')); return vec.substr(0, vec.find('\"'));
}else return ""; } else
return "";
} }
bool IDCheck(std::string Man, std::string steam) { bool IDCheck(std::string Man, std::string steam) {
bool a = false, b = true; bool a = false, b = true;
@@ -263,10 +281,12 @@ bool IDCheck(std::string Man, std::string steam){
steam += "\\config\\loginusers.vdf"; steam += "\\config\\loginusers.vdf";
if (fs::exists(Man) && fs::exists(steam)) { if (fs::exists(Man) && fs::exists(steam)) {
for (const std::string& ID : GetID(steam)) { for (const std::string& ID : GetID(steam)) {
if(ID == GetManifest(Man))b = false; if (ID == GetManifest(Man))
b = false;
} }
// if(b)Exit(6); // if(b)Exit(6);
}else a = true; } else
a = true;
return a; return a;
} }
void LegitimacyCheck() { void LegitimacyCheck() {
@@ -307,11 +327,13 @@ void LegitimacyCheck(){
LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey); LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey);
if (dwRegOPenKey == ERROR_SUCCESS) { if (dwRegOPenKey == ERROR_SUCCESS) {
Result = QueryKey(hKey, 3); Result = QueryKey(hKey, 3);
if(Result.empty())lowExit(3); if (Result.empty())
lowExit(3);
// if(IDCheck(Result,T))lowExit(5); // if(IDCheck(Result,T))lowExit(5);
GameDir = Result; GameDir = Result;
// TraceBack++; // TraceBack++;
}else lowExit(4); } else
lowExit(4);
K3.clear(); K3.clear();
Result.clear(); Result.clear();
RegCloseKey(hKey); RegCloseKey(hKey);
@@ -345,7 +367,8 @@ std::string CheckVer(const std::string &dir){
vec = vec.substr(vec.find_last_of("version"), vec.find_last_of('"')); vec = vec.substr(vec.find_last_of("version"), vec.find_last_of('"'));
for (const char& a : vec) { for (const char& a : vec) {
if(isdigit(a) || a == '.')temp+=a; if (isdigit(a) || a == '.')
temp += a;
} }
return temp; return temp;
} }

77
src/Security/Login.cpp Executable file → Normal file
View File

@@ -7,23 +7,27 @@
/// ///
#include "Http.h" #include "Http.h"
#include <filesystem>
#include "Logger.h" #include "Logger.h"
#include <filesystem>
#include <fstream> #include <fstream>
#include "Json.h" #include <nlohmann/json.hpp>
namespace fs = std::filesystem; namespace fs = std::filesystem;
std::string PublicKey; std::string PublicKey;
std::string PrivateKey;
extern bool LoginAuth; extern bool LoginAuth;
std::string Role; extern std::string Username;
extern std::string UserRole;
void UpdateKey(const char* newKey) { void UpdateKey(const char* newKey) {
if (newKey && std::isalnum(newKey[0])) { if (newKey && std::isalnum(newKey[0])) {
PrivateKey = newKey;
std::ofstream Key("key"); std::ofstream Key("key");
if (Key.is_open()) { if (Key.is_open()) {
Key << newKey; Key << newKey;
Key.close(); Key.close();
}else fatal("Cannot write to disk!"); } else
fatal("Cannot write to disk!");
} else if (fs::exists("key")) { } else if (fs::exists("key")) {
remove("key"); remove("key");
} }
@@ -42,41 +46,52 @@ std::string GetFail(const std::string& R){
std::string Login(const std::string& fields) { std::string Login(const std::string& fields) {
if (fields == "LO") { if (fields == "LO") {
Username = "";
UserRole = "";
LoginAuth = false; LoginAuth = false;
UpdateKey(nullptr); UpdateKey(nullptr);
return ""; return "";
} }
info("Attempting to authenticate..."); info("Attempting to authenticate...");
try {
std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields); std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields);
json::Document d;
d.Parse(Buffer.c_str());
if (Buffer == "-1") { if (Buffer == "-1") {
return GetFail("Failed to communicate with the auth system!"); return GetFail("Failed to communicate with the auth system!");
} }
if (Buffer.at(0) != '{' || d.HasParseError()) { nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false);
if (Buffer.at(0) != '{' || d.is_discarded()) {
error(Buffer); error(Buffer);
return GetFail("Invalid answer from authentication servers, please try again later!"); return GetFail("Invalid answer from authentication servers, please try again later!");
} }
if(d.HasMember("success") && !d["success"].IsNull() && d["success"].GetBool()){ if (d.contains("success") && d["success"].get<bool>()) {
LoginAuth = true; LoginAuth = true;
if(d.HasMember("private_key") && !d["private_key"].IsNull()){ if (d.contains("username")) {
UpdateKey(d["private_key"].GetString()); Username = d["username"].get<std::string>();
} }
if(d.HasMember("public_key") && !d["public_key"].IsNull()){ if (d.contains("role")) {
PublicKey = d["public_key"].GetString(); UserRole = d["role"].get<std::string>();
}
if (d.contains("private_key")) {
UpdateKey(d["private_key"].get<std::string>().c_str());
}
if (d.contains("public_key")) {
PublicKey = d["public_key"].get<std::string>();
} }
info("Authentication successful!"); info("Authentication successful!");
}else info("Authentication failed!"); } else
if(d.HasMember("message") && !d["message"].IsNull()){ info("Authentication failed!");
d.RemoveMember("private_key"); if (d.contains("message")) {
d.RemoveMember("public_key"); d.erase("private_key");
rapidjson::StringBuffer buffer; d.erase("public_key");
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); return d.dump();
d.Accept(writer);
return buffer.GetString();
} }
return GetFail("Invalid message parsing!"); return GetFail("Invalid message parsing!");
} catch (const std::exception& e) {
return GetFail(e.what());
}
} }
void CheckLocalKey() { void CheckLocalKey() {
@@ -97,18 +112,23 @@ void CheckLocalKey(){
Buffer = HTTP::Post("https://auth.beammp.com/userlogin", R"({"pk":")" + Buffer + "\"}"); Buffer = HTTP::Post("https://auth.beammp.com/userlogin", R"({"pk":")" + Buffer + "\"}");
json::Document d; nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false);
d.Parse(Buffer.c_str());
if (Buffer == "-1" || Buffer.at(0) != '{' || d.HasParseError()) { if (Buffer == "-1" || Buffer.at(0) != '{' || d.is_discarded()) {
error(Buffer); error(Buffer);
info("Invalid answer from authentication servers."); info("Invalid answer from authentication servers.");
UpdateKey(nullptr); UpdateKey(nullptr);
} }
if(d.HasMember("success") && d["success"].GetBool()){ if (d["success"].get<bool>()) {
LoginAuth = true; LoginAuth = true;
UpdateKey(d["private_key"].GetString()); UpdateKey(d["private_key"].get<std::string>().c_str());
PublicKey = d["public_key"].GetString(); PublicKey = d["public_key"].get<std::string>();
Role = d["role"].GetString(); if (d.contains("username")) {
Username = d["username"].get<std::string>();
}
if (d.contains("role")) {
UserRole = d["role"].get<std::string>();
}
// info(Role); // info(Role);
} else { } else {
info("Auto-Authentication unsuccessful please re-login!"); info("Auto-Authentication unsuccessful please re-login!");
@@ -118,5 +138,6 @@ void CheckLocalKey(){
warn("Could not open saved key!"); warn("Could not open saved key!");
UpdateKey(nullptr); UpdateKey(nullptr);
} }
}else UpdateKey(nullptr); } else
UpdateKey(nullptr);
} }

275
src/Startup.cpp Executable file → Normal file
View File

@@ -5,43 +5,86 @@
/// ///
/// Created by Anonymous275 on 7/16/2020 /// Created by Anonymous275 on 7/16/2020
/// ///
#include "zip_file.h" #include "zip_file.h"
#include <charconv>
#include <httplib.h>
#include <nlohmann/json.hpp>
#include <string> #include <string>
#if defined(_WIN32) #if defined(_WIN32)
#include <windows.h> #include <windows.h>
#elif defined(__linux__) #elif defined(__linux__)
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "Discord/discord_info.h" #include "Http.h"
#include "Logger.h"
#include "Network/network.hpp" #include "Network/network.hpp"
#include "Security/Init.h" #include "Security/Init.h"
#include <filesystem>
#include "Startup.h" #include "Startup.h"
#include "Logger.h" #include "hashpp.h"
#include <filesystem>
#include <fstream> #include <fstream>
#include <thread> #include <thread>
#include "Http.h"
#include "Json.h"
extern int TraceBack; extern int TraceBack;
bool Dev = false; bool Dev = false;
int ProxyPort = 0;
namespace fs = std::filesystem; namespace fs = std::filesystem;
struct Version {
uint8_t major;
uint8_t minor;
uint8_t patch;
Version(uint8_t major, uint8_t minor, uint8_t patch);
Version(const std::array<uint8_t, 3>& v);
};
std::array<uint8_t, 3> VersionStrToInts(const std::string& str) {
std::array<uint8_t, 3> Version;
std::stringstream ss(str);
for (uint8_t& i : Version) {
std::string Part;
std::getline(ss, Part, '.');
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
}
return Version;
}
bool IsOutdated(const Version& Current, const Version& Newest) {
if (Newest.major > Current.major) {
return true;
} else if (Newest.major == Current.major && Newest.minor > Current.minor) {
return true;
} else if (Newest.major == Current.major && Newest.minor == Current.minor && Newest.patch > Current.patch) {
return true;
} else {
return false;
}
}
Version::Version(uint8_t major, uint8_t minor, uint8_t patch)
: major(major)
, minor(minor)
, patch(patch) { }
Version::Version(const std::array<uint8_t, 3>& v)
: Version(v[0], v[1], v[2]) {
}
std::string GetEN() {
#if defined(_WIN32) #if defined(_WIN32)
std::string GetEN(){
return "BeamMP-Launcher.exe"; return "BeamMP-Launcher.exe";
}
#elif defined(__linux__) #elif defined(__linux__)
std::string GetEN(){
return "BeamMP-Launcher"; return "BeamMP-Launcher";
}
#endif #endif
}
std::string GetVer() { std::string GetVer() {
return "2.0"; return "2.1";
} }
std::string GetPatch() { std::string GetPatch() {
return ".82"; return ".0";
} }
std::string GetEP(char* P) { std::string GetEP(char* P) {
@@ -106,54 +149,46 @@ void CheckName(int argc,char* args[]){
std::string DN = GetEN(), CDir = args[0], FN = CDir.substr(CDir.find_last_of('/') + 1); std::string DN = GetEN(), CDir = args[0], FN = CDir.substr(CDir.find_last_of('/') + 1);
#endif #endif
if (FN != DN) { if (FN != DN) {
if(fs::exists(DN))remove(DN.c_str()); if (fs::exists(DN))
if(fs::exists(DN))ReLaunch(argc,args); remove(DN.c_str());
if (fs::exists(DN))
ReLaunch(argc, args);
std::rename(FN.c_str(), DN.c_str()); std::rename(FN.c_str(), DN.c_str());
URelaunch(argc, args); URelaunch(argc, args);
} }
} }
void CheckForUpdates(int argc, char* args[], const std::string& CV) { void CheckForUpdates(int argc, char* args[], const std::string& CV) {
std::string link; std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/launcher?branch=" + Branch + "&pk=" + PublicKey);
std::string HTTP = HTTP::Get("https://beammp.com/builds/launcher?version=true"); std::string LatestVersion = HTTP::Get(
bool fallback = false; "https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey);
if(HTTP.find_first_of("0123456789") == std::string::npos){
HTTP = HTTP::Get("https://backup1.beammp.com/builds/launcher?version=true");
fallback = true;
if(HTTP.find_first_of("0123456789") == std::string::npos) {
fatal("Primary Servers Offline! sorry for the inconvenience!");
}
}
if(fallback){
link = "https://backup1.beammp.com/builds/launcher?download=true";
}else link = "https://beammp.com/builds/launcher?download=true";
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back"); std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back");
if(fs::exists(Back))remove(Back.c_str()); std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, EP);
if(HTTP > CV){
#if defined(_WIN32) #if defined(_WIN32)
system("cls");
#elif defined(__linux__) #elif defined(__linux__)
system("clear"); system("clear");
#endif #endif
info("Update found!");
info("Updating...");
if(std::rename(EP.c_str(), Back.c_str()))error("failed creating a backup!");
if(!HTTP::Download(link, EP)){ if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
error("Launcher Update failed! trying again..."); info("Launcher update found!");
std::this_thread::sleep_for(std::chrono::seconds(2)); #if defined(__linux__)
error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches.");
if(!HTTP::Download(link, EP)){ #else
error("Launcher Update failed!"); fs::remove(Back);
std::this_thread::sleep_for(std::chrono::seconds(5)); fs::rename(EP, Back);
ReLaunch(argc,args); info("Downloading Launcher update " + LatestHash);
} HTTP::Download(
} "https://backend.beammp.com/builds/launcher?download=true"
"&pk="
+ PublicKey + "&branch=" + Branch,
EP);
URelaunch(argc, args); URelaunch(argc, args);
}else info("Launcher version is up to date"); #endif
} else
info("Launcher version is up to date");
TraceBack++; TraceBack++;
} }
@@ -166,7 +201,15 @@ void CustomPort(int argc, char* argv[]){
warn("Running on custom port : " + std::to_string(DEFAULT_PORT)); warn("Running on custom port : " + std::to_string(DEFAULT_PORT));
} }
} }
if(argc > 2)Dev = true; if (argc > 2)
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;
}
} }
} }
@@ -174,7 +217,8 @@ void CustomPort(int argc, char* argv[]){
void LinuxPatch() { void LinuxPatch() {
HKEY hKey = nullptr; HKEY hKey = nullptr;
LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Wine)", 0, KEY_READ, &hKey); LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Wine)", 0, KEY_READ, &hKey);
if (result != ERROR_SUCCESS || getenv("USER") == nullptr)return; if (result != ERROR_SUCCESS || getenv("USER") == nullptr)
return;
RegCloseKey(hKey); RegCloseKey(hKey);
info("Wine/Proton Detected! If you are on windows delete HKEY_CURRENT_USER\\Software\\Wine in regedit"); info("Wine/Proton Detected! If you are on windows delete HKEY_CURRENT_USER\\Software\\Wine in regedit");
info("Applying patches..."); info("Applying patches...");
@@ -194,31 +238,6 @@ void LinuxPatch(){
} }
RegCloseKey(hKey); RegCloseKey(hKey);
std::string Path = R"(Z:\home\)" + std::string(getenv("USER")) + R"(\.steam\steam\Steam.exe)";
if(!fs::exists(Path)) {
std::ofstream ofs(Path);
if (!ofs.is_open()) {
fatal("Failed to create file \"" + Path + "\"");
return;
} else ofs.close();
}
result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Valve\Steam)", 0, KEY_ALL_ACCESS, &hKey);
if (result != ERROR_SUCCESS){
fatal(R"(failed to open HKEY_CURRENT_USER\Software\Valve\Steam)");
return;
}
result = RegSetValueEx(hKey, "SteamExe", 0, REG_SZ, (BYTE*)Path.c_str(), Path.size());
if (result != ERROR_SUCCESS){
fatal(R"(failed to create the value "Name" under HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)");
return;
}
RegCloseKey(hKey);
info("Patched!"); info("Patched!");
} }
#endif #endif
@@ -233,7 +252,6 @@ void InitLauncher(int argc, char* argv[]) {
CheckLocalKey(); CheckLocalKey();
ConfigInit(); ConfigInit();
CustomPort(argc, argv); CustomPort(argc, argv);
Discord_Main();
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch()); CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
} }
#elif defined(__linux__) #elif defined(__linux__)
@@ -244,8 +262,16 @@ void InitLauncher(int argc, char* argv[]) {
CheckLocalKey(); CheckLocalKey();
ConfigInit(); ConfigInit();
CustomPort(argc, argv); CustomPort(argc, argv);
bool update = true;
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()); CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
} }
}
#endif #endif
size_t DirCount(const std::filesystem::path& path) { size_t DirCount(const std::filesystem::path& path) {
@@ -253,46 +279,46 @@ size_t DirCount(const std::filesystem::path& path){
} }
void CheckMP(const std::string& Path) { void CheckMP(const std::string& Path) {
if (!fs::exists(Path))return; if (!fs::exists(Path))
return;
size_t c = DirCount(fs::path(Path)); size_t c = DirCount(fs::path(Path));
try { try {
for (auto& p : fs::directory_iterator(Path)) { for (auto& p : fs::directory_iterator(Path)) {
if (p.exists() && !p.is_directory()) { if (p.exists() && !p.is_directory()) {
std::string Name = p.path().filename().u8string(); std::string Name = p.path().filename().string();
for(char&Ch : Name)Ch = char(tolower(Ch)); for (char& Ch : Name)
if(Name != "beammp.zip")fs::remove(p.path()); Ch = char(tolower(Ch));
if (Name != "beammp.zip")
fs::remove(p.path());
} }
} }
} catch (...) { } catch (...) {
fatal("We were unable to clean the multiplayer mods folder! Is the game still running or do you have something open in that folder?"); fatal("We were unable to clean the multiplayer mods folder! Is the game still running or do you have something open in that folder?");
} }
} }
void EnableMP() { void EnableMP() {
std::string File(GetGamePath() + "mods/db.json"); std::string File(GetGamePath() + "mods/db.json");
if(!fs::exists(File))return; if (!fs::exists(File))
return;
auto Size = fs::file_size(File); auto Size = fs::file_size(File);
if(Size < 2)return; if (Size < 2)
return;
std::ifstream db(File); std::ifstream db(File);
if (db.is_open()) { if (db.is_open()) {
std::string Data(Size, 0); std::string Data(Size, 0);
db.read(&Data[0], Size); db.read(&Data[0], Size);
db.close(); db.close();
json::Document d; nlohmann::json d = nlohmann::json::parse(Data, nullptr, false);
d.Parse(Data.c_str()); if (Data.at(0) != '{' || d.is_discarded()) {
if(Data.at(0) != '{' || d.HasParseError()){
// error("Failed to parse " + File); //TODO illegal formatting // error("Failed to parse " + File); //TODO illegal formatting
return; return;
} }
if(d.HasMember("mods") && !d["mods"].IsNull() && d["mods"].HasMember("multiplayerbeammp") && !d["mods"]["multiplayerbeammp"].IsNull()){ if (d.contains("mods") && d["mods"].contains("multiplayerbeammp")) {
d["mods"]["multiplayerbeammp"]["active"] = true; d["mods"]["multiplayerbeammp"]["active"] = true;
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
d.Accept(writer);
std::ofstream ofs(File); std::ofstream ofs(File);
if (ofs.is_open()) { if (ofs.is_open()) {
ofs << buffer.GetString(); ofs << d.dump();
ofs.close(); ofs.close();
} else { } else {
error("Failed to write " + File); error("Failed to write " + File);
@@ -307,8 +333,15 @@ void PreGame(const std::string& GamePath){
CheckMP(GetGamePath() + "mods/multiplayer"); CheckMP(GetGamePath() + "mods/multiplayer");
info("Game user path: '" + GetGamePath() + "'");
if (!Dev) { if (!Dev) {
info("Downloading mod please wait..."); std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey);
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(),
[](auto const& c) -> bool { return !std::isalnum(c); }),
LatestHash.end());
try { try {
if (!fs::exists(GetGamePath() + "mods/multiplayer")) { if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
fs::create_directories(GetGamePath() + "mods/multiplayer"); fs::create_directories(GetGamePath() + "mods/multiplayer");
@@ -324,16 +357,76 @@ void PreGame(const std::string& GamePath){
std::string ZipPath(GetGamePath() + R"(mods/multiplayer/beammp.zip)"); std::string ZipPath(GetGamePath() + R"(mods/multiplayer/beammp.zip)");
#endif #endif
std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, ZipPath);
if (FileHash != LatestHash) {
info("Downloading BeamMP Update " + LatestHash);
HTTP::Download("https://backend.beammp.com/builds/client?download=true" HTTP::Download("https://backend.beammp.com/builds/client?download=true"
"&pk=" + PublicKey + "&branch=" + Branch, ZipPath); "&pk="
+ PublicKey + "&branch=" + Branch,
ZipPath);
}
std::string Target(GetGamePath() + "mods/unpacked/beammp"); std::string Target(GetGamePath() + "mods/unpacked/beammp");
if (fs::is_directory(Target)) { if (fs::is_directory(Target)) {
fs::remove_all(Target); fs::remove_all(Target);
} }
}
//HTTP::Download("beammp.com/builds/client", GetGamePath() + R"(mods\multiplayer\BeamMP.zip)");
} }
void set_headers(httplib::Response& res) {
res.set_header("Access-Control-Allow-Origin", "*");
res.set_header("Access-Control-Request-Method", "POST, OPTIONS, GET");
res.set_header("Access-Control-Request-Headers", "X-API-Version");
}
void StartProxy() {
std::thread proxy([&]() {
httplib::Server HTTPProxy;
httplib::Headers headers = {
{ "User-Agent", "BeamMP-Launcher/" + GetVer() + GetPatch() },
{ "Accept", "*/*" }
};
std::string pattern = "/:any1";
for (int i = 2; i <= 4; i++) {
HTTPProxy.Get(pattern, [&](const httplib::Request& req, httplib::Response& res) {
httplib::Client cli("https://backend.beammp.com");
set_headers(res);
if (req.has_header("X-BMP-Authentication")) {
headers.emplace("X-BMP-Authentication", PrivateKey);
}
if (req.has_header("X-API-Version")) {
headers.emplace("X-API-Version", req.get_header_value("X-API-Version"));
}
if (auto cli_res = cli.Get(req.path, headers); cli_res) {
res.set_content(cli_res->body, cli_res->get_header_value("Content-Type"));
} else {
res.set_content(to_string(cli_res.error()), "text/plain");
}
});
HTTPProxy.Post(pattern, [&](const httplib::Request& req, httplib::Response& res) {
httplib::Client cli("https://backend.beammp.com");
set_headers(res);
if (req.has_header("X-BMP-Authentication")) {
headers.emplace("X-BMP-Authentication", PrivateKey);
}
if (req.has_header("X-API-Version")) {
headers.emplace("X-API-Version", req.get_header_value("X-API-Version"));
}
if (auto cli_res = cli.Post(req.path, headers, req.body,
req.get_header_value("Content-Type"));
cli_res) {
res.set_content(cli_res->body, cli_res->get_header_value("Content-Type"));
} else {
res.set_content(to_string(cli_res.error()), "text/plain");
}
});
pattern += "/:any" + std::to_string(i);
}
ProxyPort = HTTPProxy.bind_to_any_port("0.0.0.0");
HTTPProxy.listen_after_bind();
});
proxy.detach();
} }

6
src/main.cpp Executable file → Normal file
View File

@@ -5,13 +5,13 @@
/// ///
/// Created by Anonymous275 on 7/16/2020 /// Created by Anonymous275 on 7/16/2020
/// ///
#include "Http.h"
#include "Logger.h"
#include "Network/network.hpp" #include "Network/network.hpp"
#include "Security/Init.h" #include "Security/Init.h"
#include "Startup.h" #include "Startup.h"
#include <iostream> #include <iostream>
#include "Logger.h"
#include <thread> #include <thread>
#include "Http.h"
[[noreturn]] void flush() { [[noreturn]] void flush() {
while (true) { while (true) {
@@ -25,6 +25,7 @@ int main(int argc, char* argv[]) {
std::thread th(flush); std::thread th(flush);
th.detach(); th.detach();
#endif #endif
GetEP(argv[0]); GetEP(argv[0]);
InitLauncher(argc, argv); InitLauncher(argc, argv);
@@ -35,6 +36,7 @@ int main(int argc, char* argv[]) {
fatal("Main 1 : " + std::string(e.what())); fatal("Main 1 : " + std::string(e.what()));
} }
StartProxy();
PreGame(GetGameDir()); PreGame(GetGameDir());
InitGame(GetGameDir()); InitGame(GetGameDir());
CoreNetwork(); CoreNetwork();

10
vcpkg.json Normal file
View File

@@ -0,0 +1,10 @@
{
"dependencies": [
"cpp-httplib",
"nlohmann-json",
"zlib",
"openssl",
"asio",
"fmt"
]
}