diff --git a/CMakeLists.txt b/CMakeLists.txt index bffd629..4795f90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ 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") 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}) set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher") @@ -23,15 +25,15 @@ if (WIN32) find_package(ZLIB REQUIRED) find_package(OpenSSL REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE - ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json) + ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json asio::asio fmt::fmt) elseif (LINUX) find_package(ZLIB REQUIRED) find_package(OpenSSL REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE - ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto) -else(WIN32) #MINGW + ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto asio::asio fmt::fmt) +elseif (WIN32) #MINGW add_definitions("-D_WIN32_WINNT=0x0600") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static") - target_link_libraries(${PROJECT_NAME} 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) target_include_directories(${PROJECT_NAME} PRIVATE "include") diff --git a/include/Network/network.hpp b/include/Network/network.hpp index cb9b51c..12c3421 100644 --- a/include/Network/network.hpp +++ b/include/Network/network.hpp @@ -8,6 +8,8 @@ #pragma once #include "Helpers.h" +#include "asio/io_context.hpp" +#include "asio/ip/address.hpp" #include #include @@ -15,10 +17,14 @@ #include "linuxfixes.h" #include #include -#include #include +#include #endif +#include + +extern asio::io_context io; + void NetReset(); extern bool Dev; extern int ping; @@ -30,8 +36,8 @@ extern int LastPort; extern bool ModLoaded; extern bool Terminate; extern int DEFAULT_PORT; -extern uint64_t UDPSock; -extern uint64_t TCPSock; +extern std::shared_ptr UDPSock; +extern std::shared_ptr TCPSock; extern std::string Branch; extern bool TCPTerminate; extern std::string LastIP; @@ -40,18 +46,21 @@ extern std::string UlStatus; extern std::string PublicKey; extern std::string PrivateKey; extern std::string ListOfMods; -int KillSocket(uint64_t Dead); +void KillSocket(std::shared_ptr& Dead); +void KillSocket(std::shared_ptr& Dead); +void KillSocket(asio::ip::tcp::socket& Dead); +void KillSocket(asio::ip::udp::socket& Dead); void UUl(const std::string& R); void UDPSend(const std::vector& Data); bool CheckBytes(int32_t Bytes); void GameSend(std::string_view Data); void SendLarge(const std::vector& Data); -std::string TCPRcv(uint64_t Sock); -void SyncResources(uint64_t TCPSock); +std::string TCPRcv(asio::ip::tcp::socket& Sock); +void SyncResources(asio::ip::tcp::socket& TCPSock); std::string GetAddr(const std::string& IP); void ServerParser(std::string_view Data); std::string Login(const std::string& fields); -void TCPSend(const std::vector& Data, uint64_t Sock); -void TCPClientMain(const std::string& IP, int Port); -void UDPClientMain(const std::string& IP, int Port); -void TCPGameServer(const std::string& IP, int Port); +void TCPSend(const std::vector& Data, asio::ip::tcp::socket& Sock); +void TCPClientMain(asio::ip::tcp::socket&& Socket); +void UDPClientMain(asio::ip::address addr, uint16_t port); +void TCPGameServer(asio::ip::tcp::socket&& Socket); diff --git a/include/NetworkHelpers.h b/include/NetworkHelpers.h index b8663f7..d6a2cde 100644 --- a/include/NetworkHelpers.h +++ b/include/NetworkHelpers.h @@ -1,11 +1,6 @@ #pragma once -#if defined(__linux__) -#include "linuxfixes.h" -#else -#include -#include -#endif +#include #include -void ReceiveFromGame(SOCKET socket, std::vector& out_data); +void ReceiveFromGame(asio::ip::tcp::socket& socket, std::vector& out_data); diff --git a/src/Network/Core.cpp b/src/Network/Core.cpp index e52ff7b..1538de0 100644 --- a/src/Network/Core.cpp +++ b/src/Network/Core.cpp @@ -9,17 +9,15 @@ #include "Network/network.hpp" #include "NetworkHelpers.h" #include "Security/Init.h" +#include #include +#include #include -#if defined(_WIN32) -#include -#include -#elif defined(__linux__) +#if defined(__linux__) #include #include #include #include -#include #include #include #include @@ -28,6 +26,7 @@ #include "Logger.h" #include "Startup.h" #include +#include #include #include #include @@ -45,26 +44,58 @@ std::string MStatus; bool ModLoaded; int ping = -1; -void StartSync(const std::string& Data) { - std::string IP = GetAddr(Data.substr(1, Data.find(':') - 1)); - if (IP.find('.') == -1) { - if (IP == "DNS") - UlStatus = "UlConnection Failed! (DNS Lookup Failed)"; - else - UlStatus = "UlConnection Failed! (WSA failed to start)"; - ListOfMods = "-"; - Terminate = true; - return; +asio::io_context io {}; + +static asio::ip::tcp::socket ResolveAndConnect(const std::string& host_port_string) { + + using namespace asio; + ip::tcp::resolver resolver(io); + asio::error_code ec; + auto port = host_port_string.substr(host_port_string.find_last_of(':') + 1); + auto host = host_port_string.substr(0, host_port_string.find_last_of(':')); + auto resolved = resolver.resolve(host, port, ec); + if (ec) { + ::error(fmt::format("Failed to resolve '[{}]:{}': {}", host, port, ec.message())); + throw std::runtime_error(fmt::format("Failed to resolve '{}': {}", host_port_string, ec.message())); } - CheckLocalKey(); + bool connected = false; + UlStatus = "UlLoading..."; - TCPTerminate = false; - Terminate = false; - ConfList->clear(); - ping = -1; - std::thread GS(TCPGameServer, IP, std::stoi(Data.substr(Data.find(':') + 1))); - GS.detach(); - info("Connecting to server"); + + 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; + Terminate = false; + ConfList->clear(); + ping = -1; + std::thread GS(TCPGameServer, std::move(Socket)); + GS.detach(); + info("Connecting to server"); + } catch (const std::exception& e) { + UlStatus = "UlConnection Failed!"; + error(fmt::format("Client: connect failed! Error: {}", e.what())); + WSACleanup(); + Terminate = true; + } } bool IsAllowedLink(const std::string& Link) { @@ -92,7 +123,7 @@ bool IsAllowedLink(const std::string& Link) { return false; } -void Parse(std::span InData, SOCKET CSocket) { +void Parse(std::span InData, asio::ip::tcp::socket& CSocket) { std::string OutData; char Code = InData[0], SubCode = 0; if (InData.size() > 1) @@ -213,18 +244,19 @@ void Parse(std::span InData, SOCKET CSocket) { OutData.clear(); break; } - if (!OutData.empty() && CSocket != -1) { + if (!OutData.empty() && CSocket.is_open()) { uint32_t DataSize = OutData.size(); std::vector ToSend(sizeof(DataSize) + OutData.size()); std::copy_n(reinterpret_cast(&DataSize), sizeof(DataSize), ToSend.begin()); std::copy_n(OutData.data(), OutData.size(), ToSend.begin() + sizeof(DataSize)); - int res = send(CSocket, ToSend.data(), int(ToSend.size()), 0); - if (res < 0) { - debug("(Core) send failed with error: " + std::to_string(WSAGetLastError())); + 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 data {}; do { try { @@ -250,68 +282,52 @@ void localRes() { } void CoreMain() { 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(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; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT).c_str(), &hints, &res); - if (iRes) { - debug("(Core) addr info failed with error: " + std::to_string(iRes)); - WSACleanup(); - return; - } - LSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (LSocket == -1) { - debug("(Core) socket failed with error: " + std::to_string(WSAGetLastError())); - freeaddrinfo(res); - WSACleanup(); - return; - } -#if defined(__linux__) - int opt = 1; - if (setsockopt(LSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) - error("setsockopt(SO_REUSEADDR) failed"); -#endif - iRes = bind(LSocket, res->ai_addr, int(res->ai_addrlen)); - if (iRes == SOCKET_ERROR) { - error("(Core) bind failed with error: " + std::to_string(WSAGetLastError())); - 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(); + auto acceptor = asio::ip::tcp::acceptor(io, listen_ep); + acceptor.listen(asio::ip::tcp::socket::max_listen_connections, ec); + if (ec) { + ::error(fmt::format("listen() failed, which is needed for the launcher to operate. Error: {}", + ec.message())); return; } + do { - CSocket = accept(LSocket, nullptr, nullptr); - if (CSocket == -1) { - error("(Core) accept failed with error: " + std::to_string(WSAGetLastError())); + auto CSocket = acceptor.accept(ec); + if (ec) { + error(fmt::format("(Core) accept failed with error: {}", ec.message())); continue; } localRes(); info("Game Connected!"); GameHandler(CSocket); warn("Game Reconnecting..."); - } while (CSocket); + } while (LSocket.is_open()); KillSocket(LSocket); WSACleanup(); } diff --git a/src/Network/GlobalHandler.cpp b/src/Network/GlobalHandler.cpp index 6f9a39b..408f230 100644 --- a/src/Network/GlobalHandler.cpp +++ b/src/Network/GlobalHandler.cpp @@ -8,6 +8,7 @@ #include "Helpers.h" #include "Network/network.hpp" #include "NetworkHelpers.h" +#include "asio/socket_base.hpp" #include #include #include @@ -27,6 +28,7 @@ #include "Logger.h" #include +#include #include #include #include @@ -34,20 +36,31 @@ std::chrono::time_point PingStart, PingEnd; bool GConnected = false; bool CServer = true; -SOCKET CSocket = -1; -SOCKET GSocket = -1; +std::shared_ptr CSocket = nullptr; +std::shared_ptr GSocket = nullptr; -int KillSocket(uint64_t Dead) { - if (Dead == (SOCKET)-1) { - debug("Kill socket got -1 returning..."); - return 0; - } - shutdown(Dead, SD_BOTH); - int a = closesocket(Dead); - if (a != 0) { - warn("Failed to close socket!"); - } - return a; +void KillSocket(std::shared_ptr& Dead) { + if (!Dead) + return; + asio::error_code ec; + Dead->shutdown(asio::socket_base::shutdown_both, ec); +} + +void KillSocket(std::shared_ptr& Dead) { + if (!Dead) + return; + asio::error_code ec; + Dead->shutdown(asio::socket_base::shutdown_both, ec); +} + +void KillSocket(asio::ip::tcp::socket& Dead) { + asio::error_code ec; + Dead.shutdown(asio::socket_base::shutdown_both, ec); +} + +void KillSocket(asio::ip::udp::socket& Dead) { + asio::error_code ec; + Dead.shutdown(asio::socket_base::shutdown_both, ec); } bool CheckBytes(uint32_t Bytes) { @@ -64,7 +77,7 @@ bool CheckBytes(uint32_t Bytes) { void GameSend(std::string_view RawData) { static std::mutex Lock; std::scoped_lock Guard(Lock); - if (TCPTerminate || !GConnected || CSocket == -1) + if (TCPTerminate || !GConnected || CSocket == nullptr) return; int32_t Size, Temp, Sent; uint32_t DataSize = RawData.size(); @@ -73,24 +86,14 @@ void GameSend(std::string_view RawData) { std::copy_n(RawData.data(), RawData.size(), Data.begin() + sizeof(DataSize)); Size = Data.size(); Sent = 0; -#ifdef DEBUG - if (Size > 1000) { - debug("Launcher -> game (" + std::to_string(Size) + ")"); + + asio::error_code ec; + 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); - // send separately to avoid an allocation for += "\n" - /*Temp = send(CSocket, "\n", 1, 0); - if (!CheckBytes(Temp)) { - return; - }*/ } void ServerSend(const std::vector& Data, bool Rel) { @@ -110,8 +113,8 @@ void ServerSend(const std::vector& Data, bool Rel) { if (Ack || Rel) { if (Ack || DLen > 1000) SendLarge(Data); - else - TCPSend(Data, TCPSock); + else if (TCPSock) + TCPSend(Data, *TCPSock); } else UDPSend(Data); } @@ -122,78 +125,20 @@ void NetReset() { Terminate = false; UlStatus = "Ulstart"; MStatus = " "; - if (UDPSock != (SOCKET)(-1)) { - debug("Terminating UDP Socket : " + std::to_string(TCPSock)); - KillSocket(UDPSock); + if (UDPSock != nullptr) { + KillSocket(*UDPSock); } - UDPSock = -1; - if (TCPSock != (SOCKET)(-1)) { - debug("Terminating TCP Socket : " + std::to_string(TCPSock)); - KillSocket(TCPSock); + UDPSock = nullptr; + if (TCPSock != nullptr) { + KillSocket(*TCPSock); } - TCPSock = -1; - if (GSocket != (SOCKET)(-1)) { - debug("Terminating GTCP Socket : " + std::to_string(GSocket)); - KillSocket(GSocket); + TCPSock = nullptr; + if (GSocket != nullptr) { + 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; - } -#if defined (__linux__) - int opt = 1; - if (setsockopt(GSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) - error("setsockopt(SO_REUSEADDR) failed"); -#endif - iRes = bind(GSocket, result->ai_addr, (int)result->ai_addrlen); - if (iRes == SOCKET_ERROR) { - error("(Proxy) bind failed with error: " + std::to_string(WSAGetLastError())); - 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() { while (!Terminate) { ServerSend(strtovec("p"), false); @@ -228,17 +173,47 @@ void ParserAsync(std::string_view Data) { void ServerParser(std::string_view Data) { ParserAsync(Data); } -void NetMain(const std::string& IP, int Port) { +void NetMain(asio::ip::address addr, uint16_t port) { std::thread Ping(AutoPing); Ping.detach(); - UDPClientMain(IP, Port); + UDPClientMain(addr, port); CServer = true; Terminate = true; info("Connection Terminated!"); } -void TCPGameServer(const std::string& IP, int Port) { - GSocket = SetupListener(); - while (!TCPTerminate && GSocket != -1) { +void TCPGameServer(asio::ip::tcp::socket&& Socket) { + asio::ip::tcp::endpoint listen_ep(asio::ip::address::from_string("0.0.0.0"), static_cast(DEFAULT_PORT + 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"); GConnected = false; if (!CServer) { @@ -249,38 +224,35 @@ void TCPGameServer(const std::string& IP, int Port) { break; } if (CServer) { - std::thread Client(TCPClientMain, IP, Port); + std::thread Client(TCPClientMain, std::move(Socket)); Client.detach(); } - CSocket = accept(GSocket, nullptr, nullptr); - if (CSocket == -1) { - debug("(Proxy) accept failed with error: " + std::to_string(WSAGetLastError())); - break; - } + + CSocket = std::make_shared(acceptor.accept()); debug("(Proxy) Game Connected!"); GConnected = true; if (CServer) { - std::thread t1(NetMain, IP, Port); + std::thread t1(NetMain, CSocket->remote_endpoint().address(), CSocket->remote_endpoint().port()); t1.detach(); CServer = false; } std::vector data {}; // Read byte by byte until '>' is rcved then get the size and read based on it - do { + while (!TCPTerminate && !CSocket) { try { - ReceiveFromGame(CSocket, data); + ReceiveFromGame(*CSocket, data); ServerSend(data, false); } catch (const std::exception& e) { error(std::string("Error while receiving from game: ") + e.what()); break; } - } while (!TCPTerminate); + } } TCPTerminate = true; GConnected = false; Terminate = true; - if (CSocket != SOCKET_ERROR) + if (CSocket != nullptr) KillSocket(CSocket); debug("END OF GAME SERVER"); } diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp index 22722c4..855d96f 100644 --- a/src/Network/Resources.cpp +++ b/src/Network/Resources.cpp @@ -7,6 +7,7 @@ /// #include "Network/network.hpp" +#include "fmt/core.h" #if defined(_WIN32) #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -72,7 +74,7 @@ void Abord() { info("Terminated!"); } -std::string Auth(SOCKET Sock) { +std::string Auth(asio::ip::tcp::socket& Sock) { TCPSend(strtovec("VC" + GetVer()), Sock); auto Res = TCPRcv(Sock); @@ -137,22 +139,18 @@ void AsyncUpdate(uint64_t& Rcv, uint64_t Size, const std::string& Name) { } while (!Terminate && Rcv < Size); } -char* TCPRcvRaw(SOCKET Sock, uint64_t& GRcv, uint64_t Size) { - if (Sock == -1) { - Terminate = true; - UUl("Invalid Socket"); - return nullptr; - } +char* TCPRcvRaw(asio::ip::tcp::socket& Sock, uint64_t& GRcv, uint64_t Size) { char* File = new char[Size]; uint64_t Rcv = 0; + asio::error_code ec; do { int Len = int(Size - Rcv); if (Len > 1000000) Len = 1000000; - int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL); - if (Temp < 1) { - info(std::to_string(Temp)); - UUl("Socket Closed Code 1"); + int32_t Temp = asio::read(Sock, asio::buffer(&File[Rcv], Len), ec); + if (ec) { + ::error(fmt::format("Failed to receive data from server: {}", ec.message())); + UUl("Failed to receive data from server, connection closed (Code 1)"); KillSocket(Sock); Terminate = true; delete[] File; @@ -163,29 +161,23 @@ char* TCPRcvRaw(SOCKET Sock, uint64_t& GRcv, uint64_t Size) { } while (Rcv < Size && !Terminate); return File; } -void MultiKill(SOCKET Sock, SOCKET Sock1) { +void MultiKill(asio::ip::tcp::socket& Sock, asio::ip::tcp::socket& Sock1) { KillSocket(Sock1); KillSocket(Sock); Terminate = true; } -SOCKET InitDSock() { - SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - SOCKADDR_IN ServerAddr; - if (DSock < 1) { +std::shared_ptr InitDSock(asio::ip::tcp::endpoint ep) { + auto DSock = std::make_shared(io); + asio::error_code ec; + DSock->connect(ep, ec); + if (ec) { KillSocket(DSock); Terminate = true; - return 0; - } - 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; + return nullptr; } 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); Terminate = true; return 0; @@ -193,7 +185,7 @@ SOCKET InitDSock() { 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; @@ -239,7 +231,7 @@ void InvalidResource(const std::string& File) { Terminate = true; } -void SyncResources(SOCKET Sock) { +void SyncResources(asio::ip::tcp::socket& Sock) { std::string Ret = Auth(Sock); if (Ret.empty()) return; @@ -278,7 +270,7 @@ void SyncResources(SOCKET Sock) { } if (!FNames.empty()) info("Syncing..."); - SOCKET DSock = InitDSock(); + auto DSock = InitDSock(Sock.remote_endpoint()); for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) { auto pos = FN->find_last_of('/'); if (pos != std::string::npos) { @@ -331,7 +323,7 @@ void SyncResources(SOCKET Sock) { 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; diff --git a/src/Network/VehicleData.cpp b/src/Network/VehicleData.cpp index 2d409ea..01d3547 100644 --- a/src/Network/VehicleData.cpp +++ b/src/Network/VehicleData.cpp @@ -7,6 +7,8 @@ /// #include "Network/network.hpp" #include "Zlib/Compressor.h" +#include "asio/ip/address.hpp" +#include "fmt/format.h" #if defined(_WIN32) #include @@ -23,11 +25,10 @@ #include #include -SOCKET UDPSock = -1; -sockaddr_in* ToServer = nullptr; +std::shared_ptr UDPSock = nullptr; void UDPSend(const std::vector& RawData) { - if (ClientID == -1 || UDPSock == -1) + if (ClientID == -1 || UDPSock == nullptr) return; std::string Data; if (Data.size() > 400) { @@ -37,7 +38,7 @@ void UDPSend(const std::vector& RawData) { Data = std::string(RawData.data(), RawData.size()); } std::string Packet = char(ClientID + 1) + std::string(":") + Data; - int sendOk = sendto(UDPSock, Packet.c_str(), int(Packet.size()), 0, (sockaddr*)ToServer, sizeof(*ToServer)); + int sendOk = UDPSock->send(asio::buffer(Packet)); if (sendOk == SOCKET_ERROR) error("Error Code : " + std::to_string(WSAGetLastError())); } @@ -45,10 +46,18 @@ void UDPSend(const std::vector& RawData) { void SendLarge(const std::vector& Data) { if (Data.size() > 400) { auto res = Comp(Data); - res.insert(res.begin(), {'A', 'B', 'G', ':'}); - TCPSend(res, TCPSock); + res.insert(res.begin(), { 'A', 'B', 'G', ':' }); + if (!TCPSock) { + ::debug("TCPSock is null"); + return; + } + TCPSend(res, *TCPSock); } else { - TCPSend(Data, TCPSock); + if (!TCPSock) { + ::debug("TCPSock is null"); + return; + } + TCPSend(Data, *TCPSock); } } @@ -63,39 +72,28 @@ void UDPParser(std::string_view 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); static thread_local std::array Ret {}; - if (UDPSock == -1) + if (UDPSock == nullptr) { + ::debug("UDPSock is null"); return; - int32_t Rcv = recvfrom(UDPSock, Ret.data(), Ret.size() - 1, 0, (sockaddr*)&FromServer, &clientLength); - if (Rcv == SOCKET_ERROR) + } + 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(const std::string& IP, int Port) { -#ifdef _WIN32 - WSADATA data; - if (WSAStartup(514, &data)) { - error("Can't start Winsock!"); - return; +void UDPClientMain(asio::ip::address addr, uint16_t port) { + UDPSock = std::make_shared(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; } -#endif - - delete ToServer; - ToServer = new sockaddr_in; - ToServer->sin_family = AF_INET; - ToServer->sin_port = htons(Port); - inet_pton(AF_INET, IP.c_str(), &ToServer->sin_addr); - UDPSock = socket(AF_INET, SOCK_DGRAM, 0); GameSend("P" + std::to_string(ClientID)); - TCPSend(strtovec("H"), TCPSock); + TCPSend(strtovec("H"), *TCPSock); UDPSend(strtovec("p")); while (!Terminate) UDPRcv(); diff --git a/src/Network/VehicleEvent.cpp b/src/Network/VehicleEvent.cpp index 139849b..c347677 100644 --- a/src/Network/VehicleEvent.cpp +++ b/src/Network/VehicleEvent.cpp @@ -7,6 +7,7 @@ /// #include "Logger.h" +#include "fmt/format.h" #include #include #include @@ -27,7 +28,7 @@ int LastPort; std::string LastIP; -SOCKET TCPSock = -1; +std::shared_ptr TCPSock = nullptr; bool CheckBytes(int32_t Bytes) { if (Bytes == 0) { @@ -46,8 +47,8 @@ void UUl(const std::string& R) { UlStatus = "UlDisconnected: " + R; } -void TCPSend(const std::vector& Data, uint64_t Sock) { - if (Sock == -1) { +void TCPSend(const std::vector& Data, asio::ip::tcp::socket& Sock) { + if (!Sock.is_open()) { Terminate = true; UUl("Invalid Socket"); return; @@ -61,53 +62,33 @@ void TCPSend(const std::vector& Data, uint64_t Sock) { // Do not use Size before this point for anything but the header Sent = 0; Size += 4; - do { - if (size_t(Sent) >= Send.size()) { - error("string OOB in " + std::string(__func__)); - UUl("TCP Send OOB"); - return; - } - Temp = send(Sock, &Send[Sent], Size - Sent, 0); - if (!CheckBytes(Temp)) { - UUl("Socket Closed Code 2"); - return; - } - Sent += Temp; - } while (Sent < Size); + asio::error_code ec; + asio::write(Sock, asio::buffer(Send), ec); + if (ec) { + UUl(fmt::format("Failed to send data: {}", ec.message())); + } } -std::string TCPRcv(SOCKET Sock) { - if (Sock == -1) { +std::string TCPRcv(asio::ip::tcp::socket& Sock) { + if (!Sock.is_open()) { Terminate = true; UUl("Invalid Socket"); return ""; } int32_t Header, BytesRcv = 0, Temp; std::vector Data(sizeof(Header)); - do { - Temp = recv(Sock, &Data[BytesRcv], 4 - BytesRcv, 0); - if (!CheckBytes(Temp)) { - UUl("Socket Closed Code 3"); - return ""; - } - BytesRcv += Temp; - } while (BytesRcv < 4); + asio::error_code ec; + asio::read(Sock, asio::buffer(Data), ec); + if (ec) { + UUl(fmt::format("Failed to receive header: {}", ec.message())); + } memcpy(&Header, &Data[0], sizeof(Header)); - if (!CheckBytes(BytesRcv)) { - UUl("Socket Closed Code 4"); - return ""; - } Data.resize(Header); - BytesRcv = 0; - do { - Temp = recv(Sock, &Data[BytesRcv], Header - BytesRcv, 0); - if (!CheckBytes(Temp)) { - UUl("Socket Closed Code 5"); - return ""; - } - BytesRcv += Temp; - } while (BytesRcv < Header); + asio::read(Sock, asio::buffer(Data), ec); + if (ec) { + UUl(fmt::format("Failed to receive data: {}", ec.message())); + } std::string Ret(Data.data(), Header); @@ -125,50 +106,24 @@ std::string TCPRcv(SOCKET Sock) { return Ret; } -void TCPClientMain(const std::string& IP, int Port) { - LastIP = IP; - LastPort = Port; +void TCPClientMain(asio::ip::tcp::socket&& socket) { + if (!TCPSock) { + return; + } + LastIP = socket.remote_endpoint().address().to_string(); + LastPort = socket.remote_endpoint().port(); SOCKADDR_IN ServerAddr; int RetCode; -#ifdef _WIN32 - WSADATA wsaData; - WSAStartup(514, &wsaData); // 2.2 -#endif - TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + TCPSock = std::make_shared(std::move(socket)); - 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!"); char Code = 'C'; - send(TCPSock, &Code, 1, 0); - SyncResources(TCPSock); - while (!Terminate) { - ServerParser(TCPRcv(TCPSock)); + asio::write(*TCPSock, asio::buffer(&Code, 1)); + SyncResources(*TCPSock); + while (!Terminate && TCPSock) { + ServerParser(TCPRcv(*TCPSock)); } GameSend("T"); - ////Game Send Terminate - if (KillSocket(TCPSock) != 0) - debug("(TCP) Cannot close socket. Error code: " + std::to_string(WSAGetLastError())); - -#ifdef _WIN32 - if (WSACleanup() != 0) - debug("(TCP) Client: WSACleanup() failed!..."); -#endif + KillSocket(TCPSock); } diff --git a/src/NetworkHelpers.cpp b/src/NetworkHelpers.cpp index 0b8af33..56d7d01 100644 --- a/src/NetworkHelpers.cpp +++ b/src/NetworkHelpers.cpp @@ -4,32 +4,32 @@ #include #include #include -#if defined(__linux__) -#include -#else -#include -#include -#endif -static uint32_t RecvHeader(SOCKET socket) { +using asio::ip::tcp; + +static uint32_t RecvHeader(tcp::socket& socket) { std::array header_buffer {}; - auto n = recv(socket, reinterpret_cast(header_buffer.data()), header_buffer.size(), MSG_WAITALL); - if (n < 0) { - throw std::runtime_error(std::string("recv() of header failed: ") + std::strerror(errno)); - } else if (n == 0) { + 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(header_buffer.data()); } /// Throws!!! -void ReceiveFromGame(SOCKET socket, std::vector& out_data) { +void ReceiveFromGame(tcp::socket& socket, std::vector& out_data) { auto header = RecvHeader(socket); out_data.resize(header); - auto n = recv(socket, reinterpret_cast(out_data.data()), out_data.size(), MSG_WAITALL); - if (n < 0) { - throw std::runtime_error(std::string("recv() of data failed: ") + std::strerror(errno)); - } else if (n == 0) { + 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"); } } diff --git a/vcpkg.json b/vcpkg.json index 8c1daa1..752bf55 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -3,6 +3,8 @@ "cpp-httplib", "nlohmann-json", "zlib", - "openssl" + "openssl", + "asio", + "fmt" ] }