//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2001-2004. // ------------------------------------------------------------------------- // File name: DefenceWall.cpp // Version: v1.00 // Created: 25/1/2004 by Timur. // Compilers: Visual Studio.NET 2003 // Description: // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "DefenceWall.h" #include "Network.h" #include "Client.h" #include "Server.h" #include "ServerSlot.h" #include "ICryPak.h" //#define LOGEVENTS // don't use in public release enum CHEAT_PROTECTION_LEVEL { CHEAT_LEVEL_DEFAULT = 1, CHEAT_LEVEL_RANDOMCHECKS, CHEAT_LEVEL_CODE, }; // Max number of retried for single request. #define MAX_CHECK_RETRIES 5 // Time in seconds during which client must return response to server. #define WAIT_FOR_RESPONSE_TIME 20 // Check about every 5 mins. #define RANDOM_CHECK_PERIOD 60*3 enum EDefenceWallRequestCode { DEFWALL_UNKNOWN = 0, DEFWALL_CHECK_PAK = 1, DEFWALL_CHECK_PROTECTED_FILE = 2, DEFWALL_CHECK_DLL_CODE = 3, }; ////////////////////////////////////////////////////////////////////////// CDefenceWall::CDefenceWall( CNetwork *pNetwork ) { m_pNetwork = pNetwork; m_pSystem = GetISystem(); m_nNextRequestId = 1; m_pServer = 0; m_bServer = false; m_bLog = true; m_nNumOpenedPacksOnServer = 0; m_nEncryptKey[0] = 2962387638; m_nEncryptKey[1] = 1782685322; m_nEncryptKey[2] = 268651613; m_nEncryptKey[3] = 156356231; #if defined(WIN64) || defined(LINUX64) m_b64bit = true; #else m_b64bit = false; #endif } ////////////////////////////////////////////////////////////////////////// CDefenceWall::~CDefenceWall() { ClearAllPendingRequests(); m_protectedFiles.clear(); ClearClients(); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::SetServer( CServer *pServer ) { m_pServer = pServer; m_pClient = 0; m_bServer = true; } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::SetClient( CClient *pClient ) { m_pServer = 0; m_pClient = pClient; m_bServer = false; } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::ClearClients() { for (Clients::iterator it = m_clients.begin(); it != m_clients.end(); ++it) { delete *it; } m_clients.clear(); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::ClearAllPendingRequests() { for (PendingChecks::iterator it = m_pendingChecks.begin(); it != m_pendingChecks.end(); it++) { SClientCheckContext* pCtx = *it; delete pCtx; } m_pendingChecks.clear(); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::FillStdServerProbes() { m_stdServerProbes.clear(); m_stdServerProbes.reserve(30); IDataProbe *pProbe = GetISystem()->GetIDataProbe(); ICryPak::PakInfo* pPakInfo = m_pSystem->GetIPak()->GetPakInfo(); unsigned int i; ////////////////////////////////////////////////////////////////////////// // Collect PAK files. ////////////////////////////////////////////////////////////////////////// m_nNumOpenedPacksOnServer = pPakInfo->numOpenPaks; for (i = 0; i < pPakInfo->numOpenPaks; i++) { SClientCheckContext ctx; ctx.nNumOpenedPaks = pPakInfo->numOpenPaks; if (ServerCreateFileProbe( pPakInfo->arrPaks[i].szFilePath,ctx,true )) { m_stdServerProbes.push_back(ctx); } } m_pSystem->GetIPak()->FreePakInfo( pPakInfo ); // Do not compare user DLL`s. /* #if !defined(LINUX) ////////////////////////////////////////////////////////////////////////// // Collect Modules Code probes. ////////////////////////////////////////////////////////////////////////// // Create module probe for all DLL modules. IDataProbe::SModuleInfo *pModules; int numModules = pProbe->GetLoadedModules( &pModules ); for (int m = 0; m < numModules; m++) { SClientCheckContext ctx; if (ServerCreateModuleProbe( pModules[m].filename.c_str(),ctx )) m_stdServerProbes.push_back(ctx); } #endif */ } ////////////////////////////////////////////////////////////////////////// bool CDefenceWall::ServerCreateModuleProbe( const char *sFilename,SClientCheckContext &ctx ) { char sModule[_MAX_PATH]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; _splitpath( sFilename,0,0,fname,ext ); _makepath( sModule,0,0,fname,ext ); strlwr( sModule ); // Skip comparing render dll code. (Can be OGL,D3D or NULL) if (strstr(sModule,"render")) return false; AddProtectedFile( (string("b")+"i"+"n"+"3"+"2"+"/"+sFilename).c_str() ); if (m_pNetwork->GetCheatProtectionLevel() >= CHEAT_LEVEL_CODE) { ctx.nRequestCode = DEFWALL_CHECK_DLL_CODE; ctx.bExecutableCode = true; ctx.probe.sFilename = sModule; #if !defined(LINUX) ctx.nFilenameHash = m_pSystem->GetIDataProbe()->GetHash( sModule ); ctx.probe.nCodeInfo = rand()%3; if (!m_pSystem->GetIDataProbe()->GetRandomModuleProbe( ctx.probe )) return false; if (!m_pSystem->GetIDataProbe()->GetCode( ctx.probe )) return false; #endif #ifdef LOGEVENTS if (m_bLog) CryLog( " CreatedExeProbe[%d] %s %I64x",ctx.nRequestId,ctx.probe.sFilename.c_str(),ctx.probe.nCode ); #endif } else { return false; } return true; } ////////////////////////////////////////////////////////////////////////// bool CDefenceWall::ServerCreateFileProbe( const char *sFilename,SClientCheckContext &ctx,bool bRandomPart ) { #if !defined(LINUX) // Get current language. ICVar *pLanguage=m_pSystem->GetIConsole()->GetCVar("g_language"); if (pLanguage && pLanguage->GetString()) { // Skip validation of language specific paks. char fname[_MAX_FNAME]; _splitpath( sFilename,NULL,NULL,fname,NULL ); if (strnicmp(fname,pLanguage->GetString(),strlen(pLanguage->GetString())) == 0) { // This is language pak... skip checking it. return false; } } string file = sFilename; GetRelativeFilename(file); ctx.nRequestCode = DEFWALL_CHECK_PAK; ctx.bExecutableCode = false; ctx.probe.sFilename = file; ctx.nFilenameHash = m_pSystem->GetIDataProbe()->GetHash( file.c_str() ); if (bRandomPart) { ctx.probe.nCodeInfo = rand()%3; if (!m_pSystem->GetIDataProbe()->GetRandomFileProbe( ctx.probe,true )) return false; if (!m_pSystem->GetIDataProbe()->GetCode( ctx.probe )) return false; } else { ctx.probe.nCodeInfo = rand()%3; ctx.probe.nSize = 0xFFFFFFFF; if (!m_pSystem->GetIDataProbe()->GetCode( ctx.probe )) return false; } #ifdef LOGEVENTS if (m_bLog) CryLog( " CreatedFileProbe[%d] %s %I64x",ctx.nRequestId,ctx.probe.sFilename.c_str(),ctx.probe.nCode ); #endif #endif return true; } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::FirstTimeClientValidation( ClientInfo *pClientInfo ) { CStream outstream; for (unsigned int i = 0; i < m_stdServerProbes.size(); i++) { SClientCheckContext &ctx = m_stdServerProbes[i]; if (ctx.bExecutableCode && pClientInfo->b64bit != m_b64bit) // Cannot compare clients with 32/64bit difference. continue; IssueRequest( pClientInfo,outstream,ctx ); } // Send network request to this client IP. SendSecurityQueryToClient( pClientInfo->ip,outstream ); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::RandomClientValidation( ClientInfo *pClientInfo ) { CStream outstream; unsigned int n = 0; // Ignore if no standart probes yet. if (m_stdServerProbes.empty()) return; bool bRepeat = false; while (bRepeat) { bRepeat = false; n = rand() % m_stdServerProbes.size(); assert( n < m_stdServerProbes.size() ); if (n >= m_stdServerProbes.size()) bRepeat = true; if (m_stdServerProbes[n].bExecutableCode && pClientInfo->b64bit != m_b64bit) // Cannot compare clients with 32/64bit difference. bRepeat = true; } SClientCheckContext &ctx = m_stdServerProbes[n]; IssueRequest( pClientInfo,outstream,ctx ); // Send network request to this client IP. SendSecurityQueryToClient( pClientInfo->ip,outstream ); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::IssueRequest( ClientInfo *pClientInfo,CStream &outstream,SClientCheckContext &ctx ) { CTimeValue currTime = m_pNetwork->GetCurrentTime(); ////////////////////////////////////////////////////////////////////////// // Schedule new random check time for this client. CTimeValue timediff; timediff.SetSeconds( RANDOM_CHECK_PERIOD + (RANDOM_CHECK_PERIOD*rand())/RAND_MAX ); if (m_pNetwork->GetCheatProtectionLevel() == 5) timediff.SetSeconds( 2 ); pClientInfo->nextRandomCheckTime = currTime + timediff; ////////////////////////////////////////////////////////////////////////// ctx.clientIP = pClientInfo->ip; if (ctx.nRetries == 0) // If repeating request do not assign new requestId. { ctx.nRequestId = m_nNextRequestId++; } ctx.requestTime = currTime; pClientInfo->lastRequestTime = currTime; SClientCheckContext *pCtx = new SClientCheckContext; *pCtx = ctx; if (ctx.nRetries == 0) { m_pendingChecks.push_back(pCtx); } #ifdef LOGEVENTS if (m_bLog) CryLog( " IssueRequest[%d] %s %I64x (ofs=%d,size=%d)",pCtx->nRequestId,pCtx->probe.sFilename.c_str(),pCtx->probe.nCode,pCtx->probe.nOffset,pCtx->probe.nSize ); #endif CStream stm; WriteStreamRequest( *pCtx,stm ); outstream.Write( stm ); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::WriteStreamRequest( SClientCheckContext &ctx,CStream &stm ) { stm.Write( ctx.nRequestCode ); stm.WritePacked( ctx.nRequestId ); stm.WritePacked( ctx.nFilenameHash ); stm.WritePacked( ctx.probe.nCodeInfo ); stm.WritePacked( ctx.probe.nOffset ); stm.WritePacked( ctx.probe.nSize ); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::ReadStreamRequest( SClientCheckContext &ctx,CStream &stm ) { stm.Read( ctx.nRequestCode ); stm.ReadPacked( ctx.nRequestId ); stm.ReadPacked( ctx.nFilenameHash ); stm.ReadPacked( ctx.probe.nCodeInfo ); stm.ReadPacked( ctx.probe.nOffset ); stm.ReadPacked( ctx.probe.nSize ); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::WriteStreamResponse( SClientCheckContext &ctx,CStream &stm ) { // Also crypt stream here. stm.Write( ctx.nRequestCode ); stm.WritePacked( ctx.nRequestId ); stm.WritePacked( ctx.nNumOpenedPaks ); stm.WritePacked( ctx.nClientStatusFlags ); stm.WriteBits( (BYTE*)&ctx.probe.nCode,64 ); // write int64 } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::ReadStreamResponse( SClientCheckContext &ctx,CStream &stm ) { stm.Read( ctx.nRequestCode ); stm.ReadPacked( ctx.nRequestId ); stm.ReadPacked( ctx.nNumOpenedPaks ); stm.ReadPacked( ctx.nClientStatusFlags ); stm.ReadBits( (BYTE*)&ctx.probe.nCode,64 ); // read int64 } ////////////////////////////////////////////////////////////////////////// SClientCheckContext* CDefenceWall::FindRequest( int nRequestId ) { for (PendingChecks::iterator it = m_pendingChecks.begin(); it != m_pendingChecks.end(); it++) { SClientCheckContext* pCtx = *it; if (pCtx->nRequestId == nRequestId) { return pCtx; } } return 0; } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::RemoveRequest( SClientCheckContext* pCtx ) { for (PendingChecks::iterator it = m_pendingChecks.begin(); it != m_pendingChecks.end(); it++) { if (pCtx == *it) { m_pendingChecks.erase(it); return; } } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::UnifyFilename( string &sFilename ) { string file = sFilename; std::replace( file.begin(),file.end(),'\\','/' ); strlwr( const_cast(file.c_str()) ); sFilename = file; } void CDefenceWall::GetRelativeFilename( string &sFilename ) { UnifyFilename(sFilename); // Get current folder. char szCurrDir[_MAX_PATH]; GetCurrentDirectory( sizeof(szCurrDir),szCurrDir ); string sCurDir = szCurrDir; UnifyFilename(sCurDir); // Get relative path. if (strncmp(sFilename.c_str(),sCurDir.c_str(),sCurDir.size()) == 0 && sFilename.size()+1 >= sCurDir.size()) { // First part of file name is current dir. sFilename = sFilename.substr(sCurDir.size()+1); } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::EncryptStream( CStream &stm ) { unsigned int* pBuffer = (unsigned int*)stm.GetPtr(); int len = stm.GetSize()/8; if (len >= 8) { TEA_ENCODE( pBuffer,pBuffer,TEA_GETSIZE(len),m_nEncryptKey ); } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::DecryptStream( CStream &stm ) { unsigned int* pBuffer = (unsigned int*)stm.GetPtr(); int len = stm.GetSize()/8; if (len >= 8) { TEA_DECODE( pBuffer,pBuffer,TEA_GETSIZE(len),m_nEncryptKey ); } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::SendSecurityQueryToClient( CIPAddress &clientIP,CStream &stm ) { if (m_pServer) { CServerSlot *pSlot = m_pServer->GetPacketOwner( clientIP ); if (pSlot) { EncryptStream(stm); #ifdef LOGEVENTS if (m_bLog) CryLog( " Sending Stream to Client %d bytes",stm.GetSize()/8 ); #endif pSlot->SendSecurityQuery(stm); } } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::SendSecurityRespToServer( CStream &stm ) { if (m_pClient) { EncryptStream(stm); m_pClient->SendSecurityResponse(stm); } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::OnServerRequest( CStream &stm ) { DecryptStream(stm); m_clientOutputStream.Reset(); while (!stm.EOS()) { // When client recieves server request. SClientCheckContext ctx; ReadStreamRequest( ctx,stm ); // Perform required check on client. switch (ctx.nRequestCode) { case DEFWALL_CHECK_PAK: case DEFWALL_CHECK_PROTECTED_FILE: case DEFWALL_CHECK_DLL_CODE: OnValidateClientContext( ctx ); break; default: // Unknown server request. #ifdef LOGEVENTS if (m_bLog) CryLog( "Unknown Server Request Code" ); #endif break; } } if (m_clientOutputStream.GetSize() > 0) { // Send client Outputstring back to server. SendSecurityRespToServer( m_clientOutputStream ); } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::OnClientResponse( CIPAddress &clientIP,CStream &stm ) { // When server recieves client response. DecryptStream(stm); int nServerStatusFlags = GetSystemStatusFlags(); while (!stm.EOS()) { SClientCheckContext clientResponse; ReadStreamResponse( clientResponse,stm ); SClientCheckContext &ctx = clientResponse; #ifdef LOGEVENTS if (m_bLog) CryLog( " OnClientResponse[%d] %s %I64x",ctx.nRequestId,ctx.probe.sFilename.c_str(),ctx.probe.nCode ); #endif if (clientResponse.nClientStatusFlags != nServerStatusFlags) { #ifdef LOGEVENTS if (m_bLog) CryLog( " Wrong Vars! %d!=%d",clientResponse.nClientStatusFlags,nServerStatusFlags ); #endif // Client have different vars set. PunkDetected( clientIP,IServerSecuritySink::CHEAT_MODIFIED_VARS ); return; } // Server here checks that client response is valid and validation code match the one stored on server. SClientCheckContext *pServerCtx = FindRequest( clientResponse.nRequestId ); if (pServerCtx) { // Compare client code. if (pServerCtx->probe.nCode != clientResponse.probe.nCode) { #ifdef LOGEVENTS if (m_bLog) CryLog( " Wrong Code! %d!=%d",clientResponse.probe.nCode,pServerCtx->probe.nCode ); #endif // Wrong Code! PunkDetected( clientIP,(pServerCtx->bExecutableCode)?IServerSecuritySink::CHEAT_MODIFIED_CODE:IServerSecuritySink::CHEAT_MODIFIED_FILE ); return; } else if (pServerCtx->nRequestCode == DEFWALL_CHECK_PAK && pServerCtx->nNumOpenedPaks != clientResponse.nNumOpenedPaks) { #ifdef LOGEVENTS if (m_bLog) CryLog( " Wrong Num Paks! %d!=%d",clientResponse.nNumOpenedPaks,pServerCtx->nNumOpenedPaks ); #endif // Client have different number of open packs then server! PunkDetected( clientIP,IServerSecuritySink::CHEAT_MODIFIED_FILE ); return; } } else { // Client send request code that server already doesnt have. if (clientResponse.nRequestId >= m_nNextRequestId) { #ifdef LOGEVENTS if (m_bLog) CryLog( " Too Big Request Id! %d!=%d",clientResponse.nClientStatusFlags,nServerStatusFlags ); #endif // Possible cheating with the protocol... Client cannot recieve such a big request id before server issues it. PunkDetected( clientIP,IServerSecuritySink::CHEAT_NET_PROTOCOL ); return; } else { // this request code was already checked. } } // Remove request from list. RemoveRequest( pServerCtx ); } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::OnValidateClientContext( SClientCheckContext &ctx ) { #if defined(LINUX) return; #else bool bGotProbe = true; // Perform required check on client. switch (ctx.nRequestCode) { case DEFWALL_CHECK_PAK: { // Find out which filename to check. ICryPak::PakInfo* pPakInfo = m_pSystem->GetIPak()->GetPakInfo(); ctx.nNumOpenedPaks = pPakInfo->numOpenPaks; for (unsigned int i = 0; i < pPakInfo->numOpenPaks; i++) { #ifdef LOGEVENTS if (m_bLog) CryLog( " PAK: %s",pPakInfo->arrPaks[i].szFilePath ); #endif string file = pPakInfo->arrPaks[i].szFilePath; GetRelativeFilename(file); u32 nFilenameHash = m_pNetwork->GetStringHash(file.c_str()); if (nFilenameHash == ctx.nFilenameHash) { // Refering to the same file. ctx.probe.sFilename = file; break; } } m_pSystem->GetIPak()->FreePakInfo( pPakInfo ); } break; case DEFWALL_CHECK_PROTECTED_FILE: { for (unsigned int i = 0; i < m_protectedFiles.size(); i++) { ProtectedFile &pf = m_protectedFiles[i]; if (pf.nFilenameHash == ctx.nFilenameHash) { // We are refering to the same file. ctx.probe.sFilename = pf.filename; break; } } } break; case DEFWALL_CHECK_DLL_CODE: { // Create module probe for all DLL modules. IDataProbe::SModuleInfo *pModules; int numModules = m_pSystem->GetIDataProbe()->GetLoadedModules( &pModules ); for (int i = 0; i < numModules; i++) { char sModule[_MAX_PATH]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; _splitpath( pModules[i].filename.c_str(),0,0,fname,ext ); _makepath( sModule,0,0,fname,ext ); strlwr( sModule ); u32 nFilenameHash = m_pSystem->GetIDataProbe()->GetHash( sModule ); if (ctx.nFilenameHash == nFilenameHash) { ctx.probe.sFilename = sModule; bGotProbe = m_pSystem->GetIDataProbe()->GetModuleProbe(ctx.probe); } } } break; default: // Unknown request... #ifdef LOGEVENTS if (m_bLog) CryLog( "Server Sent Unknown request" ); #endif return; } // initialize code to be random. ctx.probe.nCode = ((u64)rand()) | (((u64)rand())<<16) | (((u64)rand())<<32) | (((u64)rand())<<48); // Calc hash code of this file. if (bGotProbe && m_pSystem->GetIDataProbe()->GetCode( ctx.probe )) { // Code retrieved. } else { // Failed... } // Send code back to server. #ifdef LOGEVENTS if (m_bLog) CryLog( " OnServerRequest[%d] %s %I64x (ofs=%d,size=%d)",ctx.nRequestId,ctx.probe.sFilename.c_str(),ctx.probe.nCode,ctx.probe.nOffset,ctx.probe.nSize ); #endif // Every response should contain these. ctx.nClientStatusFlags = GetSystemStatusFlags(); WriteStreamResponse( ctx,m_clientOutputStream ); #endif //LINUX } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::ClearProtectedFiles() { //m_bLog = m_pNetwork->GetLogLevel() == 1024; ClearAllPendingRequests(); m_protectedFiles.clear(); if (m_pNetwork->GetCheatProtectionLevel() > 0) FillStdServerProbes(); ////////////////////////////////////////////////////////////////////////// // Add default protected files. ////////////////////////////////////////////////////////////////////////// // Bin32\FarCry.exe //AddProtectedFile( (string("b")+"i"+"n"+"3"+"2"+"/"+"f"+"a"+"r"+"c"+"r"+"y"+"."+"e"+"x"+"e").c_str() ); // Bin32\XRenderD3D9.exe //AddProtectedFile( (string("b")+"i"+"n"+"3"+"2"+"/"+"x"+"r"+"e"+"n"+"d"+"e"+"r"+"d"+"3"+"d"+"9"+"."+"d"+"l"+"l").c_str() ); // Bin32\XRenderNULL.exe //AddProtectedFile( (string("b")+"i"+"n"+"3"+"2"+"/"+"x"+"r"+"e"+"n"+"d"+"e"+"r"+"n"+"u"+"l"+"l"+"."+"d"+"l"+"l").c_str() ); // Bin32\XRenderOGL.exe //AddProtectedFile( (string("b")+"i"+"n"+"3"+"2"+"/"+"x"+"r"+"e"+"n"+"d"+"e"+"r"+"o"+"g"+"l"+"."+"d"+"l"+"l").c_str() ); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::AddProtectedFile( const char *sFilename ) { #if !defined(LINUX) string file = sFilename; UnifyFilename( file ); ProtectedFile pf; pf.filename = file; pf.nFilenameHash = m_pNetwork->GetStringHash(pf.filename.c_str()); // Create also STD server probe if on server. if (m_bServer && m_pNetwork->GetCheatProtectionLevel() > 0) { // Calc hash code of this file. SDataProbeContext probe; probe.nCodeInfo = rand()%3; probe.nOffset = 0; probe.nSize = 0xFFFFFFFF; // all file. probe.sFilename = file; if (!m_pSystem->GetIDataProbe()->GetRandomFileProbe( probe,false )) return; if (!m_pSystem->GetIDataProbe()->GetCode( probe )) return; pf.nHashCode = probe.nCode; SClientCheckContext ctx; ctx.nRequestCode = DEFWALL_CHECK_PROTECTED_FILE; ctx.bExecutableCode = false; ctx.nFilenameHash = pf.nFilenameHash; ctx.probe = probe; m_stdServerProbes.push_back(ctx); #ifdef LOGEVENTS if (m_bLog) CryLog( " CreatedFileProbe[%d] %s %I64x",ctx.nRequestId,ctx.probe.sFilename.c_str(),ctx.probe.nCode ); #endif } m_protectedFiles.push_back(pf); #endif } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::ServerUpdate() { if (!m_bServer) return; // m_bLog = m_pNetwork->GetLogLevel() == 1024; // Ignore if no standart probes yet. if (m_stdServerProbes.empty()) return; CTimeValue currTime = m_pNetwork->GetCurrentTime(); float fCurrSeconds = currTime.GetSeconds(); std::vector notRespondingClients; notRespondingClients.clear(); PendingChecks::iterator it,next; // See if any requests expired and were not responded. for (it = m_pendingChecks.begin(); it != m_pendingChecks.end(); it = next) { next = it; next++; SClientCheckContext &ctx = *(*it); // Check how long request is pending already. float fRequestTime = ctx.requestTime.GetSeconds(); if (fCurrSeconds - fRequestTime > WAIT_FOR_RESPONSE_TIME) { // More then response seconds since request. if (ctx.nRetries < MAX_CHECK_RETRIES) { #ifdef LOGEVENTS if (m_bLog) CryLog( " Repeat Request[%d] (Retry:%d)",ctx.nRequestId,ctx.nRetries ); #endif ctx.nRetries++; ClientInfo *pClientInfo = FindClientInfo(ctx.clientIP); if (pClientInfo) { // Try to issue the same request again. CStream outstream; IssueRequest( pClientInfo,outstream,ctx ); SendSecurityQueryToClient( ctx.clientIP,outstream ); } else { #ifdef LOGEVENTS if (m_bLog) CryLog( " Delete Request[%d] Unknown Client!",ctx.nRequestId ); #endif m_pendingChecks.erase(it); // something wrong with this request, not client for this ip. } } else { #ifdef LOGEVENTS if (m_bLog) CryLog( " No Resonse from Client for Request(%d)",ctx.nRequestId); #endif // 3 Requests without an answer.... (assume cheating client). notRespondingClients.push_back( ctx.clientIP ); m_pendingChecks.erase(it); } } } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// { // If any client not responding. for (unsigned int i = 0; i < notRespondingClients.size(); i++) { PunkDetected( notRespondingClients[i],IServerSecuritySink::CHEAT_NOT_RESPONDING ); } } if (m_pNetwork->GetCheatProtectionLevel() >= CHEAT_LEVEL_RANDOMCHECKS) { ////////////////////////////////////////////////////////////////////////// // Go over clients and check if they need random check yet. for (Clients::const_iterator clit = m_clients.begin(); clit != m_clients.end(); ++clit) { ClientInfo *ci = *clit; if (currTime > ci->nextRandomCheckTime) { // Make random request to this client. RandomClientValidation( ci ); } } } } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::OnAddClient( CIPAddress &clientIP ) { // Check if this client already added. for (Clients::iterator it = m_clients.begin(); it != m_clients.end(); ++it) { // This client already added. if ((*it)->ip == clientIP) return; } bool b64bit = false; CServerSlot *pSlot = m_pServer->GetPacketOwner( clientIP ); if (pSlot) { if (pSlot->IsLocalSlot()) return; b64bit = pSlot->GetClientFlags() & CLIENT_FLAGS_64BIT; } ClientInfo *ci = new ClientInfo; ci->lastRequestTime = 0; ci->nextRandomCheckTime = 0; ci->ip = clientIP; ci->b64bit = b64bit; m_clients.push_back( ci ); // Start Validation. FirstTimeClientValidation( ci ); } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::OnDisconnectClient( CIPAddress &clientIP ) { // Remove all requests to this ip. PendingChecks::iterator next; for (PendingChecks::iterator it = m_pendingChecks.begin(); it != m_pendingChecks.end(); it = next) { next = it; next++; SClientCheckContext* pCtx = *it; if (pCtx->clientIP == clientIP) { // This ip should be removed. delete pCtx; next = m_pendingChecks.erase(it); } } for (Clients::iterator clit = m_clients.begin(); clit != m_clients.end(); ++clit) { ClientInfo *ci = *clit; if (ci->ip == clientIP) { m_clients.erase(clit); delete ci; break; } } } ////////////////////////////////////////////////////////////////////////// CDefenceWall::ClientInfo* CDefenceWall::FindClientInfo( CIPAddress &clientIP ) const { for (Clients::const_iterator clit = m_clients.begin(); clit != m_clients.end(); ++clit) { ClientInfo *ci = *clit; if (ci->ip == clientIP) { return ci; } } return 0; } ////////////////////////////////////////////////////////////////////////// void CDefenceWall::PunkDetected( CIPAddress &clientIP,int type ) { ClientInfo *ci = FindClientInfo(clientIP); if (ci) { ci->bSuspectedPunk = true; } m_pNetwork->PunkDetected( clientIP ); if (m_pServer) { if (m_pServer->GetSecuritySink()) { m_pServer->GetSecuritySink()->CheaterFound( clientIP.GetAsUINT(),type,"" ); } } } enum EDFSystemStatusFlags { SYS_STATUS_FORCE_NONDEVMODE = 0x0001, SYS_STATUS_IN_DEVMODE = 0x0002, SYS_STATUS_WAS_IN_DEVMODE = 0x0004, SYS_STATUS_PAK_PRIORITY = 0x0008, }; ////////////////////////////////////////////////////////////////////////// int CDefenceWall::GetSystemStatusFlags() { int flags = 0; if (GetISystem()->GetForceNonDevMode()) flags |= SYS_STATUS_FORCE_NONDEVMODE; // Check dev mode. if (GetISystem()->IsDevMode()) flags |= SYS_STATUS_IN_DEVMODE; // Check if was started in dev mode. if (GetISystem()->WasInDevMode()) flags |= SYS_STATUS_WAS_IN_DEVMODE; ICVar *pVar = GetISystem()->GetIConsole()->GetCVar("sys_PakPriority"); if (pVar && pVar->GetIVal() != 0) flags |= SYS_STATUS_PAK_PRIORITY; return flags; }