|
|
//========= 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;
|