diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/host_phonehome.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'engine/host_phonehome.cpp')
| -rw-r--r-- | engine/host_phonehome.cpp | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/engine/host_phonehome.cpp b/engine/host_phonehome.cpp new file mode 100644 index 0000000..48fad5e --- /dev/null +++ b/engine/host_phonehome.cpp @@ -0,0 +1,445 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifdef _WIN32 +#if !defined( _X360 ) +#include <windows.h> +#endif +#elif defined(POSIX) +#include <sys/socket.h> +#include <netinet/in.h> +#include <pwd.h> +#include <sys/types.h> +#else +#error +#endif + +#include "host.h" +#include "quakedef.h" +#include "net.h" +#include "bitbuf.h" +#include "tier0/icommandline.h" +#include "cserserverprotocol_engine.h" +#include "host_phonehome.h" +#include "mathlib/IceKey.H" +#include "tier0/vcrmode.h" +#include "blockingudpsocket.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define PHONE_HOME_TIMEOUT 1.5f +#define PHONE_HOME_RETRIES 3 + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a function, given a module +// Input : pModuleName - module name +// *pName - proc name +//----------------------------------------------------------------------------- +static char const *g_pszExitMsg = "Renderer: Out of memory, message code %i"; + +class CPhoneHome : public IPhoneHome +{ +public: + CPhoneHome() : + m_bPhoneHome( false ), + m_uSessionID( 0 ), + m_pSocket( 0 ) + { + Q_memset( &m_cserIP, 0, sizeof( m_cserIP ) ); + Q_memset( m_szBuildIdentifier, 0, sizeof( m_szBuildIdentifier ) ); + } + + virtual void Shutdown() + { + delete m_pSocket; + m_pSocket = NULL; + } + + virtual void Init() + { + char build_identifier[ 32 ]; + + Q_strncpy( build_identifier, "VLV_INTERNAL ", sizeof( build_identifier ) ); + int iBI = CommandLine()->FindParm("-bi"); + if ( iBI > 0 ) + { + if ( (iBI+1) < CommandLine()->ParmCount() ) + { + char const *pBuildParam = CommandLine()->GetParm( iBI + 1 ); + Q_memset( build_identifier, 0, sizeof( build_identifier ) ); + Q_strncpy( build_identifier, pBuildParam, sizeof( build_identifier ) ); + } + else + { + build_identifier[ 0 ] = '!'; + } + } + + if ( Q_strlen( build_identifier ) >= 1 && + Q_strnicmp( build_identifier, "VLV_INTERNAL", Q_strlen( "VLV_INTERNAL" ) ) ) + { + // Strip trailing spaces from identifer + char *identifer = &build_identifier[ Q_strlen( build_identifier ) - 1 ]; + while ( identifer > build_identifier && *identifer == ' ' ) + { + *identifer-- = 0; + } + + // FIXME: Don't hardcode CSER ip, get from Steam!!! + if ( NET_StringToAdr( "207.173.177.12:27013", &m_cserIP ) ) + { + m_bPhoneHome = true; + + Q_strncpy( m_szBuildIdentifier, build_identifier, sizeof( m_szBuildIdentifier ) ); + + m_pSocket = new CBlockingUDPSocket(); + } + } + + } + + virtual void Message( byte msgtype, char const *mapname ) + { + if ( !m_bPhoneHome ) + return; + + if ( !m_pSocket ) + return; + + switch ( msgtype ) + { + default: + break; + case PHONE_MSG_ENGINESTART: + if ( !RequestSessionId( m_uSessionID ) ) + { + ExitApp(); + } + // Note we always return here!!! + return; + case PHONE_MSG_ENGINEEND: + break; + case PHONE_MSG_MAPSTART: + { + if ( m_bLevelStarted ) + { + return; + } + + m_bLevelStarted = true; + + // Tracker 22394: Don't send map start/finish when building reslists... + if ( CommandLine()->FindParm( "-makereslists" ) ) + { + return; + } + } + break; + case PHONE_MSG_MAPEND: + { + if ( !m_bLevelStarted ) + { + return; + } + + m_bLevelStarted = false; + + // Tracker 22394: Don't send map start/finish when building reslists... + if ( CommandLine()->FindParm( "-makereslists" ) ) + { + return; + } + } + break; + } + + SendSessionMessage( msgtype, mapname ); + } + +private: + + void ExitApp() + { + byte msgtype = 212; + Error( g_pszExitMsg, msgtype ); + } + + //----------------------------------------------------------------------------- + // Purpose: encrypts an 8-byte sequence + //----------------------------------------------------------------------------- + inline void Encrypt8ByteSequence( IceKey& cipher, const unsigned char *plainText, unsigned char *cipherText) + { + cipher.encrypt(plainText, cipherText); + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void EncryptBuffer( IceKey& cipher, unsigned char *bufData, uint bufferSize) + { + unsigned char *cipherText = bufData; + unsigned char *plainText = bufData; + uint bytesEncrypted = 0; + + while (bytesEncrypted < bufferSize) + { + // encrypt 8 byte section + Encrypt8ByteSequence( cipher, plainText, cipherText); + bytesEncrypted += 8; + cipherText += 8; + plainText += 8; + } + } + + void BuildMessage( bf_write& buf, byte msgtype, char const *mapname, unsigned int uSessionID ) + { + + bf_write encrypted; + ALIGN4 byte encrypted_data[ 2048 ] ALIGN4_POST; + + buf.WriteByte( C2M_PHONEHOME ); + buf.WriteByte( '\n' ); + buf.WriteByte( C2M_PHONEHOME_PROTOCOL_VERSION ); + buf.WriteLong( uSessionID ); // sessionid (request new id by sending 0) + + // encryption object + IceKey cipher(1); /* medium encryption level */ + unsigned char ucEncryptionKey[8] = { 191, 1, 0, 222, 85, 39, 154, 1 }; + cipher.set( ucEncryptionKey ); + + encrypted.StartWriting( encrypted_data, sizeof( encrypted_data ) ); + + byte corruption_identifier = 0x01; + + encrypted.WriteByte( corruption_identifier ); + + // Data version protocol + encrypted.WriteByte( 1 ); + + // Write the "build identifier" -- unique to each person we give a build to. + encrypted.WriteString( m_szBuildIdentifier ); + { + char computername[ 64 ]; + Q_memset( computername, 0, sizeof( computername ) ); +#if defined ( _WIN32 ) + DWORD length = sizeof( computername ) - 1; + if ( !GetComputerName( computername, &length ) ) + { + Q_strncpy( computername, "???", sizeof( computername ) ); + } +#else + if ( gethostname( computername, sizeof(computername) ) == -1 ) + { + Q_strncpy( computername, "Linux????", sizeof( computername ) ); + } + computername[sizeof(computername)-1] = '\0'; +#endif + encrypted.WriteString( computername ); + } + + { + char username[ 64 ]; + Q_memset( username, 0, sizeof( username ) ); +#if defined ( _WIN32 ) + DWORD length = sizeof( username ) - 1; + if ( !GetUserName( username, &length ) ) + { + Q_strncpy( username, "???", sizeof( username ) ); + } +#else + struct passwd *pass = getpwuid( getuid() ); + if ( pass ) + { + Q_strncpy( username, pass->pw_name, sizeof( username ) ); + } + else + { + Q_strncpy( username, "LinuxUser??", sizeof( username ) ); + } + username[sizeof(username)-1] = '\0'; +#endif + encrypted.WriteString( username ); + } + + char gamedir[ 64 ]; + Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) ); + encrypted.WriteString( gamedir ); + + unsigned int uBuildNumber = build_number(); + + encrypted.WriteLong( (int)uBuildNumber ); + + // WRite timestamp of engine + encrypted.WriteFloat( (float)realtime ); + + encrypted.WriteByte( msgtype ); + if ( mapname != NULL ) + { + encrypted.WriteString( mapname ); + } + + int isDebugUser = ( Sys_IsDebuggerPresent() || CommandLine()->FindParm( "-allowdebug" ) ) ? 1 : 0; + + encrypted.WriteByte( isDebugUser ); + + while ( encrypted.GetNumBytesWritten() % 8 ) + { + encrypted.WriteByte( 0 ); + } + + EncryptBuffer( cipher, (unsigned char *)encrypted.GetData(), encrypted.GetNumBytesWritten() ); + + buf.WriteShort( (int)encrypted.GetNumBytesWritten() ); + buf.WriteBytes( (unsigned char *)encrypted.GetData(), encrypted.GetNumBytesWritten() ); + } + + void SendSessionMessage( byte msgtype, char const *mapname ) + { + if ( m_uSessionID == 0 ) + return; + + bf_write buf; + ALIGN4 byte data[ 2048 ] ALIGN4_POST; + + buf.StartWriting( data, sizeof( data ) ); + + BuildMessage( buf, msgtype, mapname, m_uSessionID ); + + struct sockaddr_in sa; + + m_cserIP.ToSockadr( (struct sockaddr *)&sa ); + + m_pSocket->SendSocketMessage( sa, (const byte *)buf.GetData(), buf.GetNumBytesWritten() ); + + // If we already have a sessionid, don't wait for the server to give us back a new one... + if ( m_uSessionID != 0 ) + { + return; + } + + if ( m_pSocket->WaitForMessage( PHONE_HOME_TIMEOUT ) ) + { + ALIGN4 byte readbuf[ 128 ] ALIGN4_POST; + + bf_read replybuf( readbuf, sizeof( readbuf ) ); + + struct sockaddr_in replyaddress; + uint bytesReceived = m_pSocket->ReceiveSocketMessage( &replyaddress, (byte *)readbuf, sizeof( readbuf ) ); + if ( bytesReceived > 0 ) + { + // Fixup actual size + replybuf.StartReading( readbuf, bytesReceived ); + + // Parse out data + byte responseType = (byte)replybuf.ReadByte(); + if ( M2C_ACKPHONEHOME == responseType ) + { + bool allowPlay = replybuf.ReadByte() == 1 ? true : false; + if ( allowPlay ) + { + m_uSessionID = replybuf.ReadLong(); + } + } + } + } + } + + bool RequestSessionId( unsigned int& id ) + { + id = 0u; + + bf_write buf; + ALIGN4 byte data[ 2048 ] ALIGN4_POST; + + buf.StartWriting( data, sizeof( data ) ); + + BuildMessage( buf, PHONE_MSG_ENGINESTART, NULL, id ); + + struct sockaddr_in sa; + + m_cserIP.ToSockadr( (struct sockaddr *)&sa ); + + for ( int retries = 0; retries < PHONE_HOME_RETRIES; ++retries ) + { + m_pSocket->SendSocketMessage( sa, (const byte *)buf.GetData(), buf.GetNumBytesWritten() ); //lint !e534 + if ( m_pSocket->WaitForMessage( PHONE_HOME_TIMEOUT ) ) + { + ALIGN4 byte readbuf[ 128 ] ALIGN4_POST; + + bf_read replybuf( readbuf, sizeof( readbuf ) ); + + struct sockaddr_in replyaddress; + uint bytesReceived = m_pSocket->ReceiveSocketMessage( &replyaddress, (byte *)readbuf, sizeof( readbuf ) ); + if ( bytesReceived > 0 ) + { + // Fixup actual size + replybuf.StartReading( readbuf, bytesReceived ); + + // Parse out data + byte responseType = (byte)replybuf.ReadByte(); + if ( M2C_ACKPHONEHOME == responseType ) + { + bool allowPlay = replybuf.ReadByte() == 1 ? true : false; + if ( allowPlay ) + { + id = replybuf.ReadLong(); + return true; + } + } + break; + } + + } + } + + return false; + } + + // FIXME, this is BS + bool IsExternalBuild() + { + if ( CommandLine()->FindParm( "-publicbuild" ) ) + { + return true; + } + + if ( !CommandLine()->FindParm( "-internalbuild" ) && + !CommandLine()->CheckParm("-dev") ) + { + return true; + } + + // It's an external build... + if ( m_bPhoneHome ) + { + if ( !Q_stricmp( m_szBuildIdentifier, "beta-playtest" ) ) + { + // Still internal + return false; + } + return true; + } + return false; + } + + + bool m_bPhoneHome; + netadr_t m_cserIP; + char m_szBuildIdentifier[ 32 ]; + bool m_bLevelStarted; + unsigned int m_uSessionID; + + CBlockingUDPSocket *m_pSocket; +}; + +CPhoneHome g_PhoneHome; + +IPhoneHome *phonehome = &g_PhoneHome; |