From 1285d15623737a9bd4737ab6696419988a92e526 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 30 Aug 2022 12:07:09 +0200 Subject: [PATCH] Ban system and authentication improvements * Fixed bug where multiple of the same entries get added to the global ban/refuse list. * Fixed bug where we still use the client instance after deleting it in 'CBanSystem::BanListCheck()'. * Load banlist at a later state (not at construction of class), this is needed for a future change of adapting the 'business' code to feature the game's FileSystem. * CServer cleanup. * More detailed ban messages (banned, added to refused list, removed from slot, etc..). * Use localization key for banned message ("#Valve_Reject_Banned"). * Add const qualifiers to all CPylon methods. Note: * This commit requires changes on the master server, these changes are already performed, however the new master server isn't live yet until we publish the new release. --- r5dev/engine/host_state.cpp | 3 +- r5dev/engine/server/server.cpp | 60 ++-- r5dev/engine/server/server.h | 12 +- r5dev/engine/server/sv_main.cpp | 7 +- r5dev/engine/server/sv_main.h | 2 +- r5dev/networksystem/bansystem.cpp | 92 +++--- r5dev/networksystem/bansystem.h | 7 +- r5dev/networksystem/pylon.cpp | 293 +++++++++--------- r5dev/networksystem/pylon.h | 9 +- .../resource/playlist/playlists_r5_patch.txt | 1 + r5dev/tier1/IConVar.cpp | 4 +- r5dev/tier1/cvar.cpp | 2 +- r5dev/tier1/cvar.h | 2 +- 13 files changed, 264 insertions(+), 230 deletions(-) diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp index ca3df20d..a1c952ab 100644 --- a/r5dev/engine/host_state.cpp +++ b/r5dev/engine/host_state.cpp @@ -182,6 +182,7 @@ FORCEINLINE void CHostState::Init(void) FORCEINLINE void CHostState::Setup(void) { g_pHostState->LoadConfig(); + g_pBanSystem->Load(); g_pConVar->PurgeHostNames(); net_usesocketsforloopback->SetValue(1); @@ -242,7 +243,7 @@ FORCEINLINE void CHostState::Think(void) const ).count() }; - std::thread(KeepAliveToPylon, netGameServer).detach(); + std::thread(&CPylon::KeepAlive, g_pMasterServer, netGameServer).detach(); pylonTimer.Start(); } #endif // DEDICATED diff --git a/r5dev/engine/server/server.cpp b/r5dev/engine/server/server.cpp index 00232250..d71eab0f 100644 --- a/r5dev/engine/server/server.cpp +++ b/r5dev/engine/server/server.cpp @@ -59,50 +59,74 @@ int CServer::GetNumFakeClients(void) const //--------------------------------------------------------------------------------- // Purpose: client to server authentication -// Input : *this - -// *pInpacket - -// Output : pointer to client instance on success, nullptr on failure +// Input : *pChallenge - +// Output : true if user isn't banned, false otherwise //--------------------------------------------------------------------------------- -CClient* CServer::Authenticate(CServer* pServer, user_creds_s* pInpacket) +bool CServer::AuthClient(user_creds_s* pChallenge) { - string svIpAddress = pInpacket->m_nAddr.GetAddress(); + string svIpAddress = pChallenge->m_nAddr.GetAddress(); if (sv_showconnecting->GetBool()) - { - DevMsg(eDLL_T::SERVER, "Processing connectionless challenge from '%s' ('%llu')\n", svIpAddress.c_str(), pInpacket->m_nNucleusID); - } + DevMsg(eDLL_T::SERVER, "Processing connectionless challenge for '%s' ('%llu')\n", svIpAddress.c_str(), pChallenge->m_nNucleusID); if (g_pBanSystem->IsBanListValid()) // Is the banlist vector valid? { - if (g_pBanSystem->IsBanned(svIpAddress, pInpacket->m_nNucleusID)) // Is the client trying to connect banned? + if (g_pBanSystem->IsBanned(svIpAddress, pChallenge->m_nNucleusID)) // Is the client trying to connect banned? { - v_CServer_RejectConnection(pServer, pServer->m_Socket, pInpacket, "You have been banned from this server."); // RejectConnection for the client. + RejectConnection(m_Socket, pChallenge, "#Valve_Reject_Banned"); // RejectConnection for the client. if (sv_showconnecting->GetBool()) - { - Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), pInpacket->m_nNucleusID); - } + Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), pChallenge->m_nNucleusID); - return nullptr; + return false; } } if (g_bCheckCompBanDB) { - std::thread th(SV_IsClientBanned, g_pMasterServer, svIpAddress, pInpacket->m_nNucleusID); + std::thread th(SV_IsClientBanned, svIpAddress, pChallenge->m_nNucleusID); th.detach(); } - return v_CServer_Authenticate(pServer, pInpacket); + return true; +} + +//--------------------------------------------------------------------------------- +// Purpose: Initializes a CSVClient for a new net connection. This will only be called +// once for a player each game, not once for each level change. +// Input : *pServer - +// *pInpacket - +// Output : pointer to client instance on success, nullptr on failure +//--------------------------------------------------------------------------------- +CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) +{ + if (pServer->AuthClient(pChallenge)) + { + CClient* pClient = v_CServer_ConnectClient(pServer, pChallenge); + return pClient; + } + + return nullptr; +} + +//--------------------------------------------------------------------------------- +// Purpose: Rejects connection request and sends back a message +// Input : *iSocket - +// *pChallenge - +// *szMessage - +//--------------------------------------------------------------------------------- +void CServer::RejectConnection(int iSocket, user_creds_s* pChallenge, const char* szMessage) +{ + v_CServer_RejectConnection(this, iSocket, pChallenge, szMessage); } /////////////////////////////////////////////////////////////////////////////// void CServer_Attach() { - DetourAttach((LPVOID*)&v_CServer_Authenticate, &CServer::Authenticate); + DetourAttach((LPVOID*)&v_CServer_ConnectClient, &CServer::ConnectClient); } void CServer_Detach() { - DetourDetach((LPVOID*)&v_CServer_Authenticate, &CServer::Authenticate); + DetourDetach((LPVOID*)&v_CServer_ConnectClient, &CServer::ConnectClient); } /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/server/server.h b/r5dev/engine/server/server.h index ce5622fa..e02f7cfb 100644 --- a/r5dev/engine/server/server.h +++ b/r5dev/engine/server/server.h @@ -21,7 +21,7 @@ struct user_creds_s v_netadr_t m_nAddr; int32_t m_nProtocolVer; int32_t m_nchallenge; - uint8_t gap2[8]; + uint32_t m_nReservation; uint64_t m_nNucleusID; uint8_t* m_pUserID; }; @@ -40,7 +40,9 @@ public: bool IsActive(void) const { return m_State >= server_state_t::ss_active; } bool IsLoading(void) const { return m_State == server_state_t::ss_loading; } bool IsDedicated(void) const { return g_bDedicated; } - static CClient* Authenticate(CServer* pServer, user_creds_s* pInpacket); + bool AuthClient(user_creds_s* pChallenge); + void RejectConnection(int iSocket, user_creds_s* pCreds, const char* szMessage); + static CClient* ConnectClient(CServer* pServer, user_creds_s* pChallenge); #endif // !CLIENT_DLL private: @@ -86,7 +88,7 @@ inline CMemory p_CServer_Think; inline auto v_CServer_Think = p_CServer_Think.RCast<void (*)(bool bCheckClockDrift, bool bIsSimulating)>(); inline CMemory p_CServer_Authenticate; -inline auto v_CServer_Authenticate = p_CServer_Authenticate.RCast<CClient* (*)(CServer* pServer, user_creds_s* pCreds)>(); +inline auto v_CServer_ConnectClient = p_CServer_Authenticate.RCast<CClient* (*)(CServer* pServer, user_creds_s* pCreds)>(); inline CMemory p_CServer_RejectConnection; inline auto v_CServer_RejectConnection = p_CServer_RejectConnection.RCast<void* (*)(CServer* pServer, int iSocket, user_creds_s* pCreds, const char* szMessage)>(); @@ -102,7 +104,7 @@ class VServer : public IDetour virtual void GetAdr(void) const { spdlog::debug("| FUN: CServer::Think : {:#18x} |\n", p_CServer_Think.GetPtr()); - spdlog::debug("| FUN: CServer::Authenticate : {:#18x} |\n", p_CServer_Authenticate.GetPtr()); + spdlog::debug("| FUN: CServer::ConnectClient : {:#18x} |\n", p_CServer_Authenticate.GetPtr()); spdlog::debug("| FUN: CServer::RejectConnection : {:#18x} |\n", p_CServer_RejectConnection.GetPtr()); spdlog::debug("| VAR: g_pServer[128] : {:#18x} |\n", reinterpret_cast<uintptr_t>(g_pServer)); spdlog::debug("+----------------------------------------------------------------+\n"); @@ -120,7 +122,7 @@ class VServer : public IDetour p_CServer_RejectConnection = g_GameDll.FindPatternSIMD(reinterpret_cast<rsig_t>("\x4C\x89\x4C\x24\x00\x53\x55\x56\x57\x48\x81\xEC\x00\x00\x00\x00\x49\x8B\xD9"), "xxxx?xxxxxxx????xxx"); v_CServer_Think = p_CServer_Think.RCast<void (*)(bool, bool)>(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??*/ - v_CServer_Authenticate = p_CServer_Authenticate.RCast<CClient* (*)(CServer*, user_creds_s*)>(); /*40 55 57 41 55 41 57 48 8D AC 24 ?? ?? ?? ??*/ + v_CServer_ConnectClient = p_CServer_Authenticate.RCast<CClient* (*)(CServer*, user_creds_s*)>(); /*40 55 57 41 55 41 57 48 8D AC 24 ?? ?? ?? ??*/ v_CServer_RejectConnection = p_CServer_RejectConnection.RCast<void* (*)(CServer*, int, user_creds_s*, const char*)>(); /*4C 89 4C 24 ?? 53 55 56 57 48 81 EC ?? ?? ?? ?? 49 8B D9*/ } virtual void GetVar(void) const diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index f3c4ad54..f01d88b5 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -8,15 +8,13 @@ //----------------------------------------------------------------------------- // Purpose: checks if particular client is banned on the comp server //----------------------------------------------------------------------------- -void SV_IsClientBanned(CPylon* pPylon, const string& svIPAddr, const uint64_t nNucleusID) +void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID) { string svError; - bool bCompBanned = pPylon->CheckForBan(svIPAddr, nNucleusID, svError); + bool bCompBanned = g_pMasterServer->CheckForBan(svIPAddr, nNucleusID, svError); if (bCompBanned) { - DevMsg(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID); - if (!ThreadInMainThread()) { g_TaskScheduler->Dispatch([svError, nNucleusID] @@ -24,6 +22,7 @@ void SV_IsClientBanned(CPylon* pPylon, const string& svIPAddr, const uint64_t nN g_pBanSystem->AddConnectionRefuse(svError, nNucleusID); // Add to the vector. }, 0); } + Warning(eDLL_T::SERVER, "Added '%s' to refused list ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID); } } diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 9df3c5ba..d2c54ae5 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -21,7 +21,7 @@ inline bool* s_bDedicated = nullptr; /////////////////////////////////////////////////////////////////////////////// -void SV_IsClientBanned(CPylon* pPylon, const string& svIPAddr, const uint64_t nNucleusID); +void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID); /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/networksystem/bansystem.cpp b/r5dev/networksystem/bansystem.cpp index c63dca57..2fb60a14 100644 --- a/r5dev/networksystem/bansystem.cpp +++ b/r5dev/networksystem/bansystem.cpp @@ -10,23 +10,6 @@ #include "engine/client/client.h" #include "networksystem/bansystem.h" -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CBanSystem::CBanSystem(void) -{ - Load(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pair<const string&, const uint64_t> - -//----------------------------------------------------------------------------- -void CBanSystem::operator[](std::pair<const string&, const uint64_t> pair) -{ - AddEntry(pair.first, pair.second); -} - //----------------------------------------------------------------------------- // Purpose: loads and parses the banlist //----------------------------------------------------------------------------- @@ -99,7 +82,9 @@ void CBanSystem::Save(void) const //----------------------------------------------------------------------------- bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nNucleusID) { - if (!svIpAddress.empty()) + Assert(!svIpAddress.empty()); + + if (IsBanListValid()) { auto it = std::find(m_vBanList.begin(), m_vBanList.end(), std::make_pair(svIpAddress, nNucleusID)); if (it == m_vBanList.end()) @@ -108,6 +93,12 @@ bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nNucleusID) return true; } } + else + { + m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID)); + return true; + } + return false; } @@ -118,17 +109,24 @@ bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nNucleusID) //----------------------------------------------------------------------------- bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID) { - bool result = false; - for (size_t i = 0; i < m_vBanList.size(); i++) + Assert(!svIpAddress.empty()); + + if (IsBanListValid()) { - if (svIpAddress.compare(m_vBanList[i].first) == NULL || nNucleusID == m_vBanList[i].second) + auto it = std::find_if(m_vBanList.begin(), m_vBanList.end(), + [&](const pair<const string, const uint64_t>& element) + { return (svIpAddress.compare(element.first) == NULL || element.second == nNucleusID); }); + + if (it != m_vBanList.end()) { - m_vBanList.erase(m_vBanList.begin() + i); - result = true; + DeleteConnectionRefuse(it->second); + m_vBanList.erase(it); + + return true; } } - return result; + return false; } //----------------------------------------------------------------------------- @@ -136,37 +134,47 @@ bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusI // Input : &svError - // nNucleusID - //----------------------------------------------------------------------------- -void CBanSystem::AddConnectionRefuse(const string& svError, const uint64_t nNucleusID) +bool CBanSystem::AddConnectionRefuse(const string& svError, const uint64_t nNucleusID) { - if (m_vRefuseList.empty()) + if (IsRefuseListValid()) { - m_vRefuseList.push_back(std::make_pair(svError, nNucleusID)); + auto it = std::find_if(m_vRefuseList.begin(), m_vRefuseList.end(), + [&](const pair<const string, const uint64_t>& element) { return element.second == nNucleusID; }); + + if (it == m_vRefuseList.end()) + { + m_vRefuseList.push_back(std::make_pair(svError, nNucleusID)); + return true; + } } else { - for (size_t i = 0; i < m_vRefuseList.size(); i++) - { - if (m_vRefuseList[i].second != nNucleusID) - { - m_vRefuseList.push_back(std::make_pair(svError, nNucleusID)); - } - } + m_vRefuseList.push_back(std::make_pair(svError, nNucleusID)); + return true; } + + return false; } //----------------------------------------------------------------------------- // Purpose: deletes an entry in the refuselist // Input : nNucleusID - //----------------------------------------------------------------------------- -void CBanSystem::DeleteConnectionRefuse(const uint64_t nNucleusID) +bool CBanSystem::DeleteConnectionRefuse(const uint64_t nNucleusID) { - for (size_t i = 0; i < m_vRefuseList.size(); i++) + if (IsRefuseListValid()) { - if (m_vRefuseList[i].second == nNucleusID) + auto it = std::find_if(m_vRefuseList.begin(), m_vRefuseList.end(), + [&](const pair<const string, const uint64_t>& element) { return element.second == nNucleusID; }); + + if (it != m_vRefuseList.end()) { - m_vRefuseList.erase(m_vRefuseList.begin() + i); + m_vRefuseList.erase(it); + return true; } } + + return false; } //----------------------------------------------------------------------------- @@ -194,11 +202,11 @@ void CBanSystem::BanListCheck(void) string svIpAddress = pNetChan->GetAddress(); - Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), pClient->GetNucleusID()); - NET_DisconnectClient(pClient, c, m_vRefuseList[i].first.c_str(), 0, true); - - if (AddEntry(svIpAddress, pClient->GetNucleusID() && !bSave)) + Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%hu' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), pClient->GetHandle(), pClient->GetNucleusID()); + if (AddEntry(svIpAddress, pClient->GetNucleusID()) && !bSave) bSave = true; + + NET_DisconnectClient(pClient, c, m_vRefuseList[i].first.c_str(), 0, true); } } diff --git a/r5dev/networksystem/bansystem.h b/r5dev/networksystem/bansystem.h index 3015c4fa..2c8aafc1 100644 --- a/r5dev/networksystem/bansystem.h +++ b/r5dev/networksystem/bansystem.h @@ -3,17 +3,14 @@ class CBanSystem { public: - CBanSystem(void); - void operator[](std::pair<const string&, const uint64_t> pair); - void Load(void); void Save(void) const; bool AddEntry(const string& svIpAddress, const uint64_t nNucleusID); bool DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID); - void AddConnectionRefuse(const string& svError, const uint64_t nNucleusID); - void DeleteConnectionRefuse(const uint64_t nNucleusID); + bool AddConnectionRefuse(const string& svError, const uint64_t nNucleusID); + bool DeleteConnectionRefuse(const uint64_t nNucleusID); void BanListCheck(void); diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 6ea2fe8c..d61a16c9 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -12,29 +12,10 @@ #include <engine/server/server.h> #endif // !CLIENT_DLL -//----------------------------------------------------------------------------- -// Purpose: Send keep alive request to Pylon Master Server. -// NOTE: When Pylon update reaches indev remove this and implement properly. -//----------------------------------------------------------------------------- -bool KeepAliveToPylon(const NetGameServer_t& netGameServer) -{ -#ifndef CLIENT_DLL - if (g_pServer->IsActive() && sv_pylonVisibility->GetBool()) // Check for active game. - { - string m_szHostToken; - string m_szHostRequestMessage; - - bool result = g_pMasterServer->PostServerHost(m_szHostRequestMessage, m_szHostToken, netGameServer); - return result; - } - return false; -#endif // !CLIENT_DLL -} - //----------------------------------------------------------------------------- // Purpose: returns a vector of hosted servers. //----------------------------------------------------------------------------- -vector<NetGameServer_t> CPylon::GetServerList(string& svOutMessage) +vector<NetGameServer_t> CPylon::GetServerList(string& svOutMessage) const { vector<NetGameServer_t> vslList{}; @@ -43,7 +24,7 @@ vector<NetGameServer_t> CPylon::GetServerList(string& svOutMessage) string svRequestBody = jsRequestBody.dump(4); - if (pylon_showdebug->GetBool()) + if (pylon_showdebuginfo->GetBool()) { DevMsg(eDLL_T::ENGINE, "%s - Sending server list request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); } @@ -51,7 +32,7 @@ vector<NetGameServer_t> CPylon::GetServerList(string& svOutMessage) httplib::Client htClient(pylon_matchmaking_hostname->GetString()); htClient.set_connection_timeout(10); httplib::Result htResult = htClient.Post("/servers", jsRequestBody.dump(4).c_str(), jsRequestBody.dump(4).length(), "application/json"); - if (htResult && pylon_showdebug->GetBool()) + if (htResult && pylon_showdebuginfo->GetBool()) { DevMsg(eDLL_T::ENGINE, "%s - Replied with '%d'.\n", __FUNCTION__, htResult->status); } @@ -89,9 +70,9 @@ vector<NetGameServer_t> CPylon::GetServerList(string& svOutMessage) } else { - if (jsResultBody["err"].is_string()) + if (jsResultBody["error"].is_string()) { - svOutMessage = jsResultBody["err"].get<string>(); + svOutMessage = jsResultBody["error"].get<string>(); } else { @@ -107,9 +88,9 @@ vector<NetGameServer_t> CPylon::GetServerList(string& svOutMessage) { nlohmann::json jsResultBody = nlohmann::json::parse(htResult->body); - if (jsResultBody["err"].is_string()) + if (jsResultBody["error"].is_string()) { - svOutMessage = jsResultBody["err"].get<string>(); + svOutMessage = jsResultBody["error"].get<string>(); } else { @@ -135,116 +116,6 @@ vector<NetGameServer_t> CPylon::GetServerList(string& svOutMessage) return vslList; } -//----------------------------------------------------------------------------- -// Purpose: Sends host server POST request. -// Input : &svOutMessage - -// &svOutToken - -// &slServerListing - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CPylon::PostServerHost(string& svOutMessage, string& svOutToken, const NetGameServer_t& slServerListing) -{ - nlohmann::json jsRequestBody = nlohmann::json::object(); - jsRequestBody["name"] = slServerListing.m_svHostName; - jsRequestBody["description"] = slServerListing.m_svDescription; - jsRequestBody["hidden"] = slServerListing.m_bHidden; - jsRequestBody["map"] = slServerListing.m_svHostMap; - jsRequestBody["playlist"] = slServerListing.m_svPlaylist; - jsRequestBody["ip"] = slServerListing.m_svIpAddress; - jsRequestBody["port"] = slServerListing.m_svGamePort; - jsRequestBody["key"] = slServerListing.m_svEncryptionKey; - jsRequestBody["checksum"] = slServerListing.m_svRemoteChecksum; - jsRequestBody["version"] = slServerListing.m_svSDKVersion; - jsRequestBody["playerCount"] = slServerListing.m_svPlayerCount; - jsRequestBody["maxPlayers"] = slServerListing.m_svMaxPlayers; - jsRequestBody["timeStamp"] = slServerListing.m_nTimeStamp; - jsRequestBody["publicRef"] = slServerListing.m_svPublicRef; - jsRequestBody["cachedID"] = slServerListing.m_svCachedID; - - string svRequestBody = jsRequestBody.dump(4); - if (pylon_showdebug->GetBool()) - { - DevMsg(eDLL_T::ENGINE, "%s - Sending post host request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); - } - - httplib::Client htClient(pylon_matchmaking_hostname->GetString()); htClient.set_connection_timeout(10); - httplib::Result htResult = htClient.Post("/servers/add", svRequestBody.c_str(), svRequestBody.length(), "application/json"); - - if (htResult && pylon_showdebug->GetBool()) - { - DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with '%d'\n", __FUNCTION__, htResult->status); - } - - try - { - if (htResult && htResult->status == 200) // STATUS_OK - { - nlohmann::json jsResultBody = nlohmann::json::parse(htResult->body); - if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get<bool>()) - { - if (jsResultBody["token"].is_string()) - { - svOutToken = jsResultBody["token"].get<string>(); - } - else - { - svOutToken = string(); - } - - return true; - } - else - { - if (jsResultBody["err"].is_string()) - { - svOutMessage = jsResultBody["err"].get<string>(); - } - else - { - svOutMessage = "An unknown error occured!"; - } - return false; - } - } - else - { - if (htResult) - { - if (!htResult->body.empty()) - { - nlohmann::json jsResultBody = nlohmann::json::parse(htResult->body); - - if (jsResultBody["err"].is_string()) - { - svOutMessage = jsResultBody["err"].get<string>(); - } - else - { - svOutMessage = string("Failed to reach comp-server ") + std::to_string(htResult->status); - } - - svOutToken = string(); - return false; - } - - svOutToken = string(); - svOutMessage = string("Failed to reach comp-server: ") + std::to_string(htResult->status); - return false; - } - - svOutToken = string(); - svOutMessage = "Failed to reach comp-server: connection timed-out"; - return false; - } - } - catch (const std::exception& ex) - { - Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what()); - } - - return false; -} - //----------------------------------------------------------------------------- // Purpose: Gets the server by token string. // Input : &slOutServer - @@ -252,14 +123,14 @@ bool CPylon::PostServerHost(string& svOutMessage, string& svOutToken, const NetG // svToken - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- -bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage, const string& svToken) +bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage, const string& svToken) const { nlohmann::json jsRequestBody = nlohmann::json::object(); jsRequestBody["token"] = svToken; string svRequestBody = jsRequestBody.dump(4); - if (pylon_showdebug->GetBool()) + if (pylon_showdebuginfo->GetBool()) { DevMsg(eDLL_T::ENGINE, "%s - Sending token connect request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); } @@ -267,7 +138,7 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage httplib::Client htClient(pylon_matchmaking_hostname->GetString()); htClient.set_connection_timeout(10); httplib::Result htResult = htClient.Post("/server/byToken", jsRequestBody.dump(4).c_str(), jsRequestBody.dump(4).length(), "application/json"); - if (pylon_showdebug->GetBool()) + if (pylon_showdebuginfo->GetBool()) { DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with '%d'\n", __FUNCTION__, htResult->status); } @@ -304,9 +175,9 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage } else { - if (jsResultBody["err"].is_string()) + if (jsResultBody["error"].is_string()) { - svOutMessage = jsResultBody["err"].get<string>(); + svOutMessage = jsResultBody["error"].get<string>(); } else { @@ -326,9 +197,9 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage { nlohmann::json jsResultBody = nlohmann::json::parse(htResult->body); - if (jsResultBody["err"].is_string()) + if (jsResultBody["error"].is_string()) { - svOutMessage = jsResultBody["err"].get<string>(); + svOutMessage = jsResultBody["error"].get<string>(); } else { @@ -355,14 +226,144 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage return false; } +//----------------------------------------------------------------------------- +// Purpose: Sends host server POST request. +// Input : &svOutMessage - +// &svOutToken - +// &slServerListing - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPylon::PostServerHost(string& svOutMessage, string& svOutToken, const NetGameServer_t& slServerListing) const +{ + nlohmann::json jsRequestBody = nlohmann::json::object(); + jsRequestBody["name"] = slServerListing.m_svHostName; + jsRequestBody["description"] = slServerListing.m_svDescription; + jsRequestBody["hidden"] = slServerListing.m_bHidden; + jsRequestBody["map"] = slServerListing.m_svHostMap; + jsRequestBody["playlist"] = slServerListing.m_svPlaylist; + jsRequestBody["ip"] = slServerListing.m_svIpAddress; + jsRequestBody["port"] = slServerListing.m_svGamePort; + jsRequestBody["key"] = slServerListing.m_svEncryptionKey; + jsRequestBody["checksum"] = slServerListing.m_svRemoteChecksum; + jsRequestBody["version"] = slServerListing.m_svSDKVersion; + jsRequestBody["playerCount"] = slServerListing.m_svPlayerCount; + jsRequestBody["maxPlayers"] = slServerListing.m_svMaxPlayers; + jsRequestBody["timeStamp"] = slServerListing.m_nTimeStamp; + jsRequestBody["publicRef"] = slServerListing.m_svPublicRef; + jsRequestBody["cachedID"] = slServerListing.m_svCachedID; + + string svRequestBody = jsRequestBody.dump(4); + if (pylon_showdebuginfo->GetBool()) + { + DevMsg(eDLL_T::ENGINE, "%s - Sending post host request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); + } + + httplib::Client htClient(pylon_matchmaking_hostname->GetString()); htClient.set_connection_timeout(10); + httplib::Result htResult = htClient.Post("/servers/add", svRequestBody.c_str(), svRequestBody.length(), "application/json"); + + if (htResult && pylon_showdebuginfo->GetBool()) + { + DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with '%d'\n", __FUNCTION__, htResult->status); + } + + try + { + if (htResult && htResult->status == 200) // STATUS_OK + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResult->body); + if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get<bool>()) + { + if (jsResultBody["token"].is_string()) + { + svOutToken = jsResultBody["token"].get<string>(); + } + else + { + svOutToken = string(); + } + + return true; + } + else + { + if (jsResultBody["error"].is_string()) + { + svOutMessage = jsResultBody["error"].get<string>(); + } + else + { + svOutMessage = "An unknown error occured!"; + } + return false; + } + } + else + { + if (htResult) + { + if (!htResult->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResult->body); + + if (jsResultBody["error"].is_string()) + { + svOutMessage = jsResultBody["error"].get<string>(); + } + else + { + svOutMessage = string("Failed to reach comp-server ") + std::to_string(htResult->status); + } + + svOutToken = string(); + return false; + } + + svOutToken = string(); + svOutMessage = string("Failed to reach comp-server: ") + std::to_string(htResult->status); + return false; + } + + svOutToken = string(); + svOutMessage = "Failed to reach comp-server: connection timed-out"; + return false; + } + } + catch (const std::exception& ex) + { + Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what()); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Send keep alive request to Pylon Master Server. +// Input : &netGameServer - +// Output : Returns true on success, false otherwise. +//----------------------------------------------------------------------------- +bool CPylon::KeepAlive(const NetGameServer_t& netGameServer) const +{ +#ifndef CLIENT_DLL + if (g_pServer->IsActive() && sv_pylonVisibility->GetBool()) // Check for active game. + { + string m_szHostToken; + string m_szHostRequestMessage; + + bool result = g_pMasterServer->PostServerHost(m_szHostRequestMessage, m_szHostToken, netGameServer); + return result; + } + return false; +#endif // !CLIENT_DLL +} + //----------------------------------------------------------------------------- // Purpose: Checks if client is banned on the comp server. // Input : svIpAddress - // nNucleusID - -// &svOutErrCl - +// &svOutReason - // Output : Returns true if banned, false if not banned. //----------------------------------------------------------------------------- -bool CPylon::CheckForBan(const string& svIpAddress, uint64_t nNucleusID, string& svOutErrCl) +bool CPylon::CheckForBan(const string& svIpAddress, const uint64_t nNucleusID, string& svOutReason) const { nlohmann::json jsRequestBody = nlohmann::json::object(); jsRequestBody["id"] = nNucleusID; @@ -380,7 +381,7 @@ bool CPylon::CheckForBan(const string& svIpAddress, uint64_t nNucleusID, string& { if (jsResultBody["banned"].is_boolean() && jsResultBody["banned"].get<bool>()) { - svOutErrCl = jsResultBody.value("errCl", "#DISCONNECT_BANNED"); + svOutReason = jsResultBody.value("reason", "#DISCONNECT_BANNED"); return true; } } diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index b0be9e28..1c510aa7 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -6,9 +6,10 @@ bool KeepAliveToPylon(const NetGameServer_t& netGameServer); class CPylon { public: - vector<NetGameServer_t> GetServerList(string& svOutMessage); - bool PostServerHost(string& svOutMessage, string& svOutToken, const NetGameServer_t& slServerListing); - bool GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage, const string& svToken); - bool CheckForBan(const string& svIpAddress, uint64_t nNucleusID, string& svOutErrCl); + vector<NetGameServer_t> GetServerList(string& svOutMessage) const; + bool GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage, const string& svToken) const; + bool PostServerHost(string& svOutMessage, string& svOutToken, const NetGameServer_t& slServerListing) const; + bool KeepAlive(const NetGameServer_t& netGameServer) const; + bool CheckForBan(const string& svIpAddress, const uint64_t nNucleusID, string& svOutReason) const; }; extern CPylon* g_pMasterServer; diff --git a/r5dev/resource/playlist/playlists_r5_patch.txt b/r5dev/resource/playlist/playlists_r5_patch.txt index 11088ff4..1ce8807b 100644 --- a/r5dev/resource/playlist/playlists_r5_patch.txt +++ b/r5dev/resource/playlist/playlists_r5_patch.txt @@ -1020,6 +1020,7 @@ playlists "PROMO_SDK_ERROR" "You should not see this." "DISCONNECT_BANNED" "The client's game account has been banned: Banned" + "VALVE_REJECT_BANNED" "You have been banned from this server." } } } diff --git a/r5dev/tier1/IConVar.cpp b/r5dev/tier1/IConVar.cpp index 5740bb00..e3e9dfa8 100644 --- a/r5dev/tier1/IConVar.cpp +++ b/r5dev/tier1/IConVar.cpp @@ -197,12 +197,12 @@ void ConVar::Init(void) const // NETCHANNEL | net_tracePayload = ConVar::Create("net_tracePayload" , "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT , "Log the payload of the send/recv datagram to a file on the disk.", false, 0.f, false, 0.f, nullptr, nullptr); net_encryptionEnable = ConVar::Create("net_encryptionEnable" , "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED , "Use AES encryption on game packets.", false, 0.f, false, 0.f, nullptr, nullptr); - net_useRandomKey = ConVar::Create("net_useRandomKey" , "1" , FCVAR_RELEASE , "Use random base64 netkey for game packets.", false, 0.f, false, 0.f, nullptr, nullptr); + net_useRandomKey = ConVar::Create("net_useRandomKey" , "1" , FCVAR_RELEASE , "Use random AES encryption key for game packets.", false, 0.f, false, 0.f, &NET_UseRandomKeyChanged_f, nullptr); //------------------------------------------------------------------------- // NETWORKSYSTEM | pylon_matchmaking_hostname = ConVar::Create("pylon_matchmaking_hostname", "r5a-comp-sv.herokuapp.com", FCVAR_RELEASE , "Holds the pylon matchmaking hostname.", false, 0.f, false, 0.f, &MP_HostName_Changed_f, nullptr); pylon_host_update_interval = ConVar::Create("pylon_host_update_interval", "5" , FCVAR_RELEASE , "Length of time in seconds between each status update interval to master server.", true, 5.f, false, 0.f, nullptr, nullptr); - pylon_showdebug = ConVar::Create("pylon_showdebug" , "0" , FCVAR_DEVELOPMENTONLY, "Shows debug output for pylon.", false, 0.f, false, 0.f, nullptr, nullptr); + pylon_showdebuginfo = ConVar::Create("pylon_showdebuginfo" , "0" , FCVAR_DEVELOPMENTONLY, "Shows debug output for pylon.", false, 0.f, false, 0.f, nullptr, nullptr); //------------------------------------------------------------------------- // RTECH API | rtech_debug = ConVar::Create("rtech_debug", "0", FCVAR_DEVELOPMENTONLY, "Shows debug output for the RTech system.", false, 0.f, false, 0.f, nullptr, nullptr); diff --git a/r5dev/tier1/cvar.cpp b/r5dev/tier1/cvar.cpp index badb86f6..619681c3 100644 --- a/r5dev/tier1/cvar.cpp +++ b/r5dev/tier1/cvar.cpp @@ -168,7 +168,7 @@ ConVar* net_useRandomKey = nullptr; ConVar* net_usesocketsforloopback = nullptr; ConVar* pylon_matchmaking_hostname = nullptr; ConVar* pylon_host_update_interval = nullptr; -ConVar* pylon_showdebug = nullptr; +ConVar* pylon_showdebuginfo = nullptr; //----------------------------------------------------------------------------- // RTECH API | ConVar* rtech_debug = nullptr; diff --git a/r5dev/tier1/cvar.h b/r5dev/tier1/cvar.h index aa43a862..e8993bc8 100644 --- a/r5dev/tier1/cvar.h +++ b/r5dev/tier1/cvar.h @@ -163,7 +163,7 @@ extern ConVar* net_useRandomKey; extern ConVar* net_usesocketsforloopback; extern ConVar* pylon_matchmaking_hostname; extern ConVar* pylon_host_update_interval; -extern ConVar* pylon_showdebug; +extern ConVar* pylon_showdebuginfo; //------------------------------------------------------------------------- // RTECH API | extern ConVar* rtech_debug;