From 4ef4a0b803cd955bb86fc21dc96f11aac1ed81be Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sun, 22 Oct 2023 17:00:56 +0200 Subject: [PATCH] Improve client-side online authentication error handling and UX Display the error to the user without having to open the developer console or terminal window. This patch also adds printing to COM_ExplainDisconnection (which has been stripped out of the retail binary). --- src/common/global.cpp | 6 ++-- src/common/global.h | 1 + src/engine/client/clientstate.cpp | 29 +++++++++++++--- src/engine/client/clientstate.h | 2 +- src/engine/common.cpp | 56 ++++++++++++++++++++++++++++++ src/engine/common.h | 19 +++++++---- src/engine/sys_dll2.cpp | 2 +- src/localize/localize.h | 18 ++++++---- src/public/localize/ilocalize.h | 57 +++++++++++++++++++++++++++++++ 9 files changed, 169 insertions(+), 21 deletions(-) diff --git a/src/common/global.cpp b/src/common/global.cpp index eb1b4be2..54b8fe26 100644 --- a/src/common/global.cpp +++ b/src/common/global.cpp @@ -206,6 +206,7 @@ ConVar* cl_threaded_bone_setup = nullptr; ConVar* cl_language = nullptr; +ConVar* cl_onlineAuthEnable = nullptr; ConVar* cl_onlineAuthToken = nullptr; ConVar* cl_onlineAuthTokenSignature1 = nullptr; ConVar* cl_onlineAuthTokenSignature2 = nullptr; @@ -387,7 +388,7 @@ void ConVar_StaticInit(void) sv_minPersonaNameLength = ConVar::StaticCreate("sv_minPersonaNameLength", "4" , FCVAR_RELEASE, "The minimum length of the client's textual persona name.", true, 0.f, false, 0.f, nullptr, nullptr); sv_maxPersonaNameLength = ConVar::StaticCreate("sv_maxPersonaNameLength", "16", FCVAR_RELEASE, "The maximum length of the client's textual persona name.", true, 0.f, false, 0.f, nullptr, nullptr); - sv_onlineAuthEnable = ConVar::StaticCreate("sv_onlineAuthEnable" , "1" , FCVAR_RELEASE, "Enables the online authentication system.", true, 0.f, true, 1.f, nullptr, nullptr); + sv_onlineAuthEnable = ConVar::StaticCreate("sv_onlineAuthEnable" , "1" , FCVAR_RELEASE, "Enables the server-side online authentication system.", true, 0.f, true, 1.f, nullptr, nullptr); sv_onlineAuthValidateExpiry = ConVar::StaticCreate("sv_onlineAuthValidateExpiry" , "1" , FCVAR_RELEASE, "Validate the online authentication token 'expiry' claim.", true, 0.f, true, 1.f, nullptr, nullptr); sv_onlineAuthExpiryTolerance = ConVar::StaticCreate("sv_onlineAuthExpiryTolerance" , "1" , FCVAR_RELEASE, "The online authentication token 'expiry' claim tolerance in seconds.", true, 0.f, true, float(UINT8_MAX), nullptr, "Must range between [0,255]"); sv_onlineAuthValidateIssuedAt = ConVar::StaticCreate("sv_onlineAuthValidateIssuedAt" , "1" , FCVAR_RELEASE, "Validate the online authentication token 'issued at' claim.", true, 0.f, true, 1.f, nullptr, nullptr); @@ -425,7 +426,8 @@ void ConVar_StaticInit(void) cl_materialinfo_offset_x = ConVar::StaticCreate("cl_materialinfo_offset_x", "0" , FCVAR_DEVELOPMENTONLY, "X offset for material debug info overlay.", false, 0.f, false, 0.f, nullptr, nullptr); cl_materialinfo_offset_y = ConVar::StaticCreate("cl_materialinfo_offset_y", "420", FCVAR_DEVELOPMENTONLY, "Y offset for material debug info overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_onlineAuthToken = ConVar::StaticCreate("cl_onlineAuthToken", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "", false, 0.f, false, 0.f, nullptr, nullptr); + cl_onlineAuthEnable = ConVar::StaticCreate("cl_onlineAuthEnable" ,"1", FCVAR_RELEASE, "Enables the client-side online authentication system.", true, 0.f, true, 1.f, nullptr, nullptr); + cl_onlineAuthToken = ConVar::StaticCreate("cl_onlineAuthToken" , "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "", false, 0.f, false, 0.f, nullptr, nullptr); cl_onlineAuthTokenSignature1 = ConVar::StaticCreate("cl_onlineAuthTokenSignature1", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "", false, 0.f, false, 0.f, nullptr, nullptr); cl_onlineAuthTokenSignature2 = ConVar::StaticCreate("cl_onlineAuthTokenSignature2", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "", false, 0.f, false, 0.f, nullptr, nullptr); diff --git a/src/common/global.h b/src/common/global.h index ccd7004b..bb957a81 100644 --- a/src/common/global.h +++ b/src/common/global.h @@ -197,6 +197,7 @@ extern ConVar* cl_threaded_bone_setup; extern ConVar* cl_language; +extern ConVar* cl_onlineAuthEnable; extern ConVar* cl_onlineAuthToken; extern ConVar* cl_onlineAuthTokenSignature1; extern ConVar* cl_onlineAuthTokenSignature2; diff --git a/src/engine/client/clientstate.cpp b/src/engine/client/clientstate.cpp index 98aca786..9124f29d 100644 --- a/src/engine/client/clientstate.cpp +++ b/src/engine/client/clientstate.cpp @@ -11,6 +11,7 @@ #include "core/stdafx.h" #include "vpc/keyvalues.h" #include "tier0/frametask.h" +#include "engine/common.h" #include "engine/host.h" #include "clientstate.h" #include "common/callback.h" @@ -167,10 +168,14 @@ bool CClientState::VProcessServerTick(CClientState* pClientState, SVC_ServerTick //------------------------------------------------------------------------------ // Purpose: get authentication token for current connection context // Input : *connectParams - +// *reasonBuf - +// reasonBufLen - // Output : true on success, false otherwise //------------------------------------------------------------------------------ -bool CClientState::Authenticate(connectparams_t* connectParams) const +bool CClientState::Authenticate(connectparams_t* connectParams, char* const reasonBuf, const size_t reasonBufLen) const { +#define FORMAT_ERROR_REASON(fmt, ...) V_snprintf(reasonBuf, reasonBufLen, fmt, ##__VA_ARGS__); + string msToken; // token returned by the masterserver authorising the client to play online string message; // message returned by the masterserver about the result of the auth @@ -180,7 +185,7 @@ bool CClientState::Authenticate(connectparams_t* connectParams) const const bool ret = g_pMasterServer->AuthForConnection(*g_NucleusID, connectParams->netAdr, g_OriginAuthCode, msToken, message); if (!ret) { - Error(eDLL_T::ENGINE, NO_ERROR, "Failed to authenticate for online play: %s\n", message.c_str()); + FORMAT_ERROR_REASON("%s", message.c_str()); return false; } @@ -192,7 +197,7 @@ bool CClientState::Authenticate(connectparams_t* connectParams) const if (!tokenSignatureDelim) { - Error(eDLL_T::ENGINE, NO_ERROR, "Failed to authenticate for online play: %s\n", "Invalid token returned by MS"); + FORMAT_ERROR_REASON("Invalid token returned by MS"); return false; } @@ -219,12 +224,26 @@ bool CClientState::Authenticate(connectparams_t* connectParams) const } return true; +#undef REJECT_CONNECTION +} + +bool IsLocalHost(connectparams_t* connectParams) +{ + return (strstr(connectParams->netAdr, "localhost") || strstr(connectParams->netAdr, "127.0.0.1")); } void CClientState::VConnect(CClientState* thisptr, connectparams_t* connectParams) { - if (strncmp(connectParams->netAdr, "localhost", 9) != NULL) - thisptr->Authenticate(connectParams); + if (cl_onlineAuthEnable->GetBool() && !IsLocalHost(connectParams)) + { + char authFailReason[512]; + + if (!thisptr->Authenticate(connectParams, authFailReason, sizeof(authFailReason))) + { + COM_ExplainDisconnection(true, "Failed to authenticate for online play: %s", authFailReason); + return; + } + } CClientState__Connect(thisptr, connectParams); } diff --git a/src/engine/client/clientstate.h b/src/engine/client/clientstate.h index 3bcd0a80..a11af46e 100644 --- a/src/engine/client/clientstate.h +++ b/src/engine/client/clientstate.h @@ -57,7 +57,7 @@ public: float GetFrameTime(void) const; - bool Authenticate(connectparams_t* connectParams) const; + bool Authenticate(connectparams_t* connectParams, char* const reasonBuf, const size_t reasonBufLen) const; int m_Socket; int _padding_maybe; diff --git a/src/engine/common.cpp b/src/engine/common.cpp index c43aaf9e..6bf5e6d5 100644 --- a/src/engine/common.cpp +++ b/src/engine/common.cpp @@ -6,6 +6,7 @@ #include #include +#include #include /* @@ -42,4 +43,59 @@ const char* COM_FormatSeconds(int seconds) } return string; +} + +/* +============================== +COM_ExplainDisconnection + +============================== +*/ +void COM_ExplainDisconnection(bool bPrint, const char* fmt, ...) +{ + char szBuf[1024]; + {///////////////////////////// + va_list vArgs; + va_start(vArgs, fmt); + + vsnprintf(szBuf, sizeof(szBuf), fmt, vArgs); + + szBuf[sizeof(szBuf) - 1] = '\0'; + va_end(vArgs); + }///////////////////////////// + + if (bPrint) + { + if (szBuf[0] == '#') + { + wchar_t formatStr[1024]; + const wchar_t* wpchReason = (*g_ppVGuiLocalize) ? (*g_ppVGuiLocalize)->Find(szBuf) : nullptr; + if (wpchReason) + { + wcsncpy(formatStr, wpchReason, sizeof(formatStr) / sizeof(wchar_t)); + + char conStr[256]; + (*g_ppVGuiLocalize)->ConvertUnicodeToANSI(formatStr, conStr, sizeof(conStr)); + Error(eDLL_T::ENGINE, NO_ERROR, "%s\n", conStr); + } + else + Error(eDLL_T::ENGINE, NO_ERROR, "%s\n", szBuf); + } + else + { + Error(eDLL_T::ENGINE, NO_ERROR, "%s\n", szBuf); + } + } + + v_COM_ExplainDisconnection(bPrint, szBuf); +} + +void VCommon::Attach() const +{ + DetourAttach(&v_COM_ExplainDisconnection, COM_ExplainDisconnection); +} + +void VCommon::Detach() const +{ + DetourDetach(&v_COM_ExplainDisconnection, COM_ExplainDisconnection); } \ No newline at end of file diff --git a/src/engine/common.h b/src/engine/common.h index 7ef14682..2c540cb5 100644 --- a/src/engine/common.h +++ b/src/engine/common.h @@ -2,31 +2,38 @@ /* ==== COMMON ========================================================================================================================================================== */ inline CMemory p_COM_InitFilesystem; -inline void*(*COM_InitFilesystem)(const char* pFullModPath); +inline void*(*v_COM_InitFilesystem)(const char* pFullModPath); + +inline CMemory p_COM_GetPrintMessageBuffer; +inline char* const(*v_COM_GetPrintMessageBuffer)(void); inline CMemory p_COM_ExplainDisconnection; -inline void*(*COM_ExplainDisconnection)(uint64_t level, const char* fmt, ...); +inline void(*v_COM_ExplainDisconnection)(bool bPrint, const char* fmt, ...); const char* COM_FormatSeconds(int seconds); +void COM_ExplainDisconnection(bool bPrint, const char* fmt, ...); /////////////////////////////////////////////////////////////////////////////// class VCommon : public IDetour { virtual void GetAdr(void) const { LogFunAdr("COM_InitFilesystem", p_COM_InitFilesystem.GetPtr()); + LogFunAdr("COM_GetPrintMessageBuffer", p_COM_GetPrintMessageBuffer.GetPtr()); LogFunAdr("COM_ExplainDisconnection", p_COM_ExplainDisconnection.GetPtr()); } virtual void GetFun(void) const { p_COM_InitFilesystem = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 48 C7 44 24 ?? ?? ?? ?? ??"); + p_COM_GetPrintMessageBuffer = g_GameDll.FindPatternSIMD("48 8D 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 4C 89 44 24 ??"); p_COM_ExplainDisconnection = g_GameDll.FindPatternSIMD("48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 48 81 EC ?? ?? ?? ??"); - COM_InitFilesystem = p_COM_InitFilesystem.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 48 C7 44 24 ?? ?? ?? ?? ??*/ - COM_ExplainDisconnection = p_COM_ExplainDisconnection.RCast(); /*48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 48 81 EC ?? ?? ?? ??*/ + v_COM_InitFilesystem = p_COM_InitFilesystem.RCast(); + v_COM_GetPrintMessageBuffer = p_COM_GetPrintMessageBuffer.RCast(); + v_COM_ExplainDisconnection = p_COM_ExplainDisconnection.RCast(); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Attach(void) const; + virtual void Detach(void) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/src/engine/sys_dll2.cpp b/src/engine/sys_dll2.cpp index 0a8317c2..4f741d01 100644 --- a/src/engine/sys_dll2.cpp +++ b/src/engine/sys_dll2.cpp @@ -147,7 +147,7 @@ void CEngineAPI::VSetStartupInfo(CEngineAPI* pEngineAPI, StartupInfo_t* pStartup InitVPKSystem(); v_TRACEINIT(NULL, "COM_InitFilesystem( m_StartupInfo.m_szInitialMod )", "COM_ShutdownFileSystem()"); - COM_InitFilesystem(pEngineAPI->m_StartupInfo.m_szInitialMod); + v_COM_InitFilesystem(pEngineAPI->m_StartupInfo.m_szInitialMod); *g_bTextMode = true; #else diff --git a/src/localize/localize.h b/src/localize/localize.h index 08c06401..b96b1a49 100644 --- a/src/localize/localize.h +++ b/src/localize/localize.h @@ -3,9 +3,14 @@ bool Localize_IsLanguageSupported(const char* pLocaleName); -class CLocalize +class CLocalize : public CBaseAppSystem< ILocalize > { - // todo + char unk[400]; + unsigned __int16 m_CurrentFile; + char unk_19A; + bool m_bUseOnlyLongestLanguageString; + bool m_bSuppressChangeCallbacks; + bool m_bQueuedChangeCallback; }; inline CMemory p_CLocalize__AddFile; @@ -15,8 +20,8 @@ inline CMemory p_CLocalize__LoadLocalizationFileLists; inline bool(*v_CLocalize__LoadLocalizationFileLists)(CLocalize * thisptr); -inline CLocalize* g_pVGuiLocalize; -inline CLocalize* g_pLocalize; +inline CLocalize** g_ppVGuiLocalize; +inline CLocalize** g_ppLocalize; /////////////////////////////////////////////////////////////////////////////// class VLocalize : public IDetour @@ -25,6 +30,7 @@ class VLocalize : public IDetour { LogFunAdr("CLocalize::AddFile", p_CLocalize__AddFile.GetPtr()); LogFunAdr("CLocalize::LoadLocalizationFileLists", p_CLocalize__LoadLocalizationFileLists.GetPtr()); + LogFunAdr("g_Localize", reinterpret_cast(g_ppLocalize)); } virtual void GetFun(void) const { @@ -36,8 +42,8 @@ class VLocalize : public IDetour } virtual void GetVar(void) const { - g_pVGuiLocalize = g_GameDll.FindPatternSIMD("48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 50 40 40 38 2D ?? ?? ?? ??").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_pLocalize = g_pVGuiLocalize; // these are set to the same thing in CSourceAppSystemGroup::Create + g_ppVGuiLocalize = g_GameDll.FindPatternSIMD("48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 50 40 40 38 2D ?? ?? ?? ??").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_ppLocalize = g_ppVGuiLocalize; // these are set to the same thing in CSourceAppSystemGroup::Create } virtual void GetCon(void) const { } virtual void Attach(void) const; diff --git a/src/public/localize/ilocalize.h b/src/public/localize/ilocalize.h index cc41e76f..2c145557 100644 --- a/src/public/localize/ilocalize.h +++ b/src/public/localize/ilocalize.h @@ -1,6 +1,63 @@ #ifndef LOCALIZE_H #define LOCALIZE_H +// direct references to localized strings +typedef unsigned long StringIndex_t; +const unsigned long INVALID_LOCALIZE_STRING_INDEX = (StringIndex_t)-1; + +abstract_class ILocalize : public IAppSystem +{ +public: + virtual bool LoadLocalizationFileLists() = 0; + + // adds the contents of a file to the localization table + virtual bool AddFile(const char* fileName, const char* pPathID = NULL) = 0; + + // Remove all strings from the table + virtual void RemoveAll() = 0; + + // Finds the localized text for tokenName + virtual wchar_t* Find(char const* tokenName) = 0; + + virtual void* FindIndex_Unknown(StringIndex_t index) = 0; + + // converts an english string to unicode + // returns the number of wchar_t in resulting string, including null terminator + virtual int ConvertANSIToUnicode(const char* ansi, OUT_Z_BYTECAP(unicodeBufferSizeInBytes) wchar_t* unicode, ssize_t unicodeBufferSizeInBytes) = 0; + + // converts an unicode string to an english string + // unrepresentable characters are converted to system default + // returns the number of characters in resulting string, including null terminator + virtual int ConvertUnicodeToANSI(const wchar_t* unicode, OUT_Z_BYTECAP(ansiBufferSize) char* ansi, ssize_t ansiBufferSize) = 0; + + + virtual StringIndex_t FindIndex(StringIndex_t index) = 0; + + //!!! TODO !!! + //void* func_80[3]; + + + //virtual void ConstructString(CLocalize*, char*, __int64, __int64, ...); + + //__int64(__fastcall* GetNameByIndex)(CLocalize*, int); + //__int64(__fastcall* GetValueByIndex)(CLocalize*, int); + //__int64(__fastcall* GetFirstStringIndex)(CLocalize*); + //__int64(__fastcall* GetNextStringIndex)(CLocalize*, int); + + + //void* func_C0[6]; + + + //__int64(__fastcall* ReloadLocalizationFiles)(CLocalize*); + + + //void* func_F8[6]; + + + //__int64(__fastcall* ConvertANSIToUCS2)(CLocalize*, const char*, void*, __int64); + //void(__fastcall* ConvertUCS2ToANSI)(CLocalize*, __int16*, char*, int); +}; + inline const char* const g_LanguageNames[] = { "english", "german",