You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
969 lines
27 KiB
969 lines
27 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
#include "basetypes.h" |
|
#ifdef WIN32 |
|
#include <winsock.h> |
|
#elif defined(POSIX) |
|
#define INVALID_SOCKET -1 |
|
#define SOCKET_ERROR -1 |
|
#include <sys/types.h> |
|
#include <sys/socket.h> |
|
#include <netinet/in.h> |
|
#define closesocket close |
|
#else |
|
#error |
|
#endif |
|
#include "netadr.h" |
|
#include "tier1/strtools.h" |
|
#include <stdio.h> |
|
#include <stdarg.h> |
|
#include "utlbuffer.h" |
|
#include "utlvector.h" |
|
#include "filesystem_tools.h" |
|
#include "cserserverprotocol_engine.h" |
|
#include "mathlib/IceKey.H" |
|
#include "bitbuf.h" |
|
#include "blockingudpsocket.h" |
|
#include "steamcommon.h" |
|
#include "steam/steamclientpublic.h" |
|
|
|
typedef unsigned int u32; |
|
typedef unsigned char u8; |
|
typedef unsigned short u16; |
|
|
|
namespace BugReportHarvester |
|
{ |
|
|
|
enum EFileType |
|
{ |
|
eFileTypeBugReport, |
|
|
|
eFILETYPECOUNT // Count number of legal values |
|
}; |
|
|
|
|
|
enum ESendMethod |
|
{ |
|
eSendMethodWholeRawFileNoBlocks, |
|
eSendMethodCompressedBlocks, // TODO: Reenable compressed sending of minidumps |
|
|
|
eSENDMETHODCOUNT // Count number of legal values |
|
}; |
|
|
|
} |
|
|
|
using namespace BugReportHarvester; |
|
|
|
// TODO: cut protocol version down to u8 if possible, to reduce bandwidth usage |
|
// for very frequent but tiny commands. |
|
typedef u32 ProtocolVersion_t; |
|
|
|
typedef u8 ProtocolAcceptanceFlag_t; |
|
typedef u8 ProtocolUnacceptableAck_t; |
|
|
|
typedef u32 MessageSequenceId_t; |
|
|
|
typedef u32 ServerSessionHandle_t; |
|
typedef u32 ClientSessionHandle_t; |
|
|
|
typedef u32 NetworkTransactionId_t; |
|
|
|
// Command codes are intentionally as small as possible to minimize bandwidth usage |
|
// for very frequent but tiny commands (e.g. GDS 'FindServer' commands). |
|
typedef u8 Command_t; |
|
|
|
// ... likewise response codes are as small as possible - we use this when we |
|
// ... can and revert to large types on a case by case basis. |
|
typedef u8 CommandResponse_t; |
|
|
|
|
|
// This define our standard type for length prefix for variable length messages |
|
// in wire protocols. |
|
// This is specifically used by CWSABUFWrapper::PrepareToReceiveLengthPrefixedMessage() |
|
// and its supporting functions. |
|
// It is defined here for generic (portable) network code to use when constructing |
|
// messages to be sent to peers that use the above function. |
|
// e.g. SteamValidateUserIDTickets.dll uses this for that purpose. |
|
|
|
// We support u16 or u32 (obviously switching between them breaks existing protocols |
|
// unless all components are switched simultaneously). |
|
typedef u32 NetworkMessageLengthPrefix_t; |
|
|
|
|
|
// Similarly, strings should be preceeded by their length. |
|
typedef u16 StringLengthPrefix_t; |
|
|
|
|
|
const ProtocolAcceptanceFlag_t cuProtocolIsNotAcceptable |
|
= static_cast<ProtocolAcceptanceFlag_t>( 0 ); |
|
|
|
const ProtocolAcceptanceFlag_t cuProtocolIsAcceptable |
|
= static_cast<ProtocolAcceptanceFlag_t>( 1 ); |
|
|
|
const Command_t cuMaxCommand |
|
= static_cast<Command_t>(255); |
|
|
|
const CommandResponse_t cuMaxCommandResponse |
|
= static_cast<CommandResponse_t>(255); |
|
|
|
// This is for mapping requests back to error ids for placing into the database appropriately. |
|
typedef u32 ContextID_t; |
|
|
|
// This is the version of the protocol used by latest-build clients. |
|
const ProtocolVersion_t cuCurrentProtocolVersion = 1; |
|
|
|
// This is the minimum protocol version number that the client must |
|
// be able to speak in order to communicate with the server. |
|
// The client sends its protocol version this before every command, and if we |
|
// don't support that version anymore then we tell it nicely. The client |
|
// should respond by doing an auto-update. |
|
const ProtocolVersion_t cuRequiredProtocolVersion = 1; |
|
|
|
|
|
namespace Commands |
|
{ |
|
const Command_t cuGracefulClose = 0; |
|
const Command_t cuSendBugReport = 1; |
|
const Command_t cuNumCommands = 2; |
|
const Command_t cuNoCommandReceivedYet = cuMaxCommand; |
|
} |
|
|
|
|
|
namespace HarvestFileCommand |
|
{ |
|
typedef u32 SenderTypeId_t; |
|
typedef u32 SenderTypeUniqueId_t; |
|
typedef u32 SenderSourceCodeControlId_t; |
|
typedef u32 FileSize_t; |
|
|
|
// Legal values defined by EFileType |
|
typedef u32 FileType_t; |
|
|
|
// Legal values defined by ESendMethod |
|
typedef u32 SendMethod_t; |
|
|
|
const CommandResponse_t cuOkToSendFile = 0; |
|
const CommandResponse_t cuFileTooBig = 1; |
|
const CommandResponse_t cuInvalidSendMethod = 2; |
|
const CommandResponse_t cuInvalidMaxCompressedChunkSize = 3; |
|
const CommandResponse_t cuInvalidBugReportContext = 4; |
|
const uint cuNumCommandResponses = 5; |
|
} |
|
|
|
//############################################################################# |
|
// |
|
// Class declaration: CWin32UploadBugReport |
|
// |
|
//############################################################################# |
|
// |
|
// Authors: |
|
// |
|
// Yahn Bernier |
|
// |
|
// Description and general notes: |
|
// |
|
// Handles uploading bug report data blobs to the CSERServer |
|
// (Client Stats & Error Reporting Server) |
|
|
|
typedef enum |
|
{ |
|
// General status |
|
eBugReportUploadSucceeded = 0, |
|
eBugReportUploadFailed, |
|
|
|
// Specific status |
|
eBugReportBadParameter, |
|
eBugReportUnknownStatus, |
|
eBugReportSendingBugReportHeaderSucceeded, |
|
eBugReportSendingBugReportHeaderFailed, |
|
eBugReportReceivingResponseSucceeded, |
|
eBugReportReceivingResponseFailed, |
|
eBugReportConnectToCSERServerSucceeded, |
|
eBugReportConnectToCSERServerFailed, |
|
eBugReportUploadingBugReportSucceeded, |
|
eBugReportUploadingBugReportFailed |
|
} EBugReportUploadStatus; |
|
|
|
struct TBugReportProgress |
|
{ |
|
// A text string describing the current progress |
|
char m_sStatus[ 512 ]; |
|
}; |
|
|
|
typedef void ( *BUGREPORTREPORTPROGRESSFUNC )( u32 uContext, const TBugReportProgress & rBugReportProgress ); |
|
|
|
static void BugUploadProgress( u32 uContext, const TBugReportProgress & rBugReportProgress ) |
|
{ |
|
// DevMsg( "%s\n", rBugReportProgress.m_sStatus ); |
|
} |
|
|
|
struct TBugReportParameters |
|
{ |
|
// IP Address of the CSERServer to send the report to |
|
netadr_t m_ipCSERServer; |
|
|
|
TSteamGlobalUserID m_userid; |
|
|
|
// Source Control Id (or build_number) of the product |
|
u32 m_uEngineBuildNumber; |
|
// Name of the .exe |
|
char m_sExecutableName[ 64 ]; |
|
// Game directory |
|
char m_sGameDirectory[ 64 ]; |
|
// Map name the server wants to upload statistics about |
|
char m_sMapName[ 64 ]; |
|
|
|
u32 m_uRAM; |
|
u32 m_uCPU; |
|
|
|
char m_sProcessor[ 128 ]; |
|
|
|
u32 m_uDXVersionHigh; |
|
u32 m_uDXVersionLow; |
|
u32 m_uDXVendorId; |
|
u32 m_uDXDeviceId; |
|
|
|
char m_sOSVersion[ 64 ]; |
|
|
|
char m_sReportType[ 40 ]; |
|
char m_sEmail[ 80 ]; |
|
char m_sAccountName[ 64 ]; |
|
|
|
char m_sTitle[ 128 ]; |
|
|
|
char m_sBody[ 1024 ]; |
|
|
|
u32 m_uAttachmentFileSize; |
|
char m_sAttachmentFile[ 128 ]; |
|
|
|
u32 m_uProgressContext; |
|
BUGREPORTREPORTPROGRESSFUNC m_pOptionalProgressFunc; |
|
}; |
|
|
|
// Note that this API is blocking, though the callback, if passed, can occur during execution. |
|
EBugReportUploadStatus Win32UploadBugReportBlocking |
|
( |
|
const TBugReportParameters & rBugReportParameters // Input |
|
); |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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; |
|
} |
|
} |
|
|
|
bool UploadBugReport( |
|
const netadr_t& cserIP, |
|
const CSteamID &userid, |
|
int build, |
|
char const *title, |
|
char const *body, |
|
char const *exename, |
|
char const *pchGamedir, |
|
char const *mapname, |
|
char const *reporttype, |
|
char const *email, |
|
char const *accountname, |
|
int ram, |
|
int cpu, |
|
char const *processor, |
|
unsigned int high, |
|
unsigned int low, |
|
unsigned int vendor, |
|
unsigned int device, |
|
char const *osversion, |
|
char const *attachedfile, |
|
unsigned int attachedfilesize |
|
) |
|
{ |
|
TBugReportParameters params; |
|
Q_memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
params.m_ipCSERServer = cserIP; |
|
params.m_userid.m_SteamLocalUserID.As64bits = userid.ConvertToUint64(); |
|
|
|
params.m_uEngineBuildNumber = build; |
|
Q_strncpy( params.m_sExecutableName, exename, sizeof( params.m_sExecutableName ) ); |
|
Q_strncpy( params.m_sGameDirectory, pchGamedir, sizeof( params.m_sGameDirectory ) ); |
|
Q_strncpy( params.m_sMapName, mapname, sizeof( params.m_sMapName ) ); |
|
|
|
params.m_uRAM = ram; |
|
params.m_uCPU = cpu; |
|
|
|
Q_strncpy( params.m_sProcessor, processor, sizeof( params.m_sProcessor) ); |
|
|
|
params.m_uDXVersionHigh = high; |
|
params.m_uDXVersionLow = low; |
|
params.m_uDXVendorId = vendor; |
|
params.m_uDXDeviceId = device; |
|
|
|
Q_strncpy( params.m_sOSVersion, osversion, sizeof( params.m_sOSVersion ) ); |
|
|
|
Q_strncpy( params.m_sReportType, reporttype, sizeof( params.m_sReportType ) ); |
|
Q_strncpy( params.m_sEmail, email, sizeof( params.m_sEmail ) ); |
|
Q_strncpy( params.m_sAccountName, accountname, sizeof( params.m_sAccountName ) ); |
|
|
|
Q_strncpy( params.m_sTitle, title, sizeof( params.m_sTitle ) ); |
|
Q_strncpy( params.m_sBody, body, sizeof( params.m_sBody ) ); |
|
|
|
Q_strncpy( params.m_sAttachmentFile, attachedfile, sizeof( params.m_sAttachmentFile ) ); |
|
params.m_uAttachmentFileSize = attachedfilesize; |
|
|
|
params.m_uProgressContext = 1u; |
|
params.m_pOptionalProgressFunc = BugUploadProgress; |
|
|
|
EBugReportUploadStatus result = Win32UploadBugReportBlocking( params ); |
|
return ( result == eBugReportUploadSucceeded ) ? true : false; |
|
|
|
} |
|
|
|
void UpdateProgress( const TBugReportParameters & params, char const *fmt, ... ) |
|
{ |
|
if ( !params.m_pOptionalProgressFunc ) |
|
{ |
|
return; |
|
} |
|
|
|
char str[ 2048 ]; |
|
va_list argptr; |
|
va_start( argptr, fmt ); |
|
_vsnprintf( str, sizeof( str ) - 1, fmt, argptr ); |
|
va_end( argptr ); |
|
|
|
char outstr[ 2060 ]; |
|
Q_snprintf( outstr, sizeof( outstr ), "(%u): %s", params.m_uProgressContext, str ); |
|
|
|
TBugReportProgress progress; |
|
Q_strncpy( progress.m_sStatus, outstr, sizeof( progress.m_sStatus ) ); |
|
|
|
// Invoke the callback |
|
( *params.m_pOptionalProgressFunc )( params.m_uProgressContext, progress ); |
|
} |
|
|
|
class CWin32UploadBugReport |
|
{ |
|
public: |
|
explicit CWin32UploadBugReport( |
|
const netadr_t & harvester, |
|
const TBugReportParameters & rBugReportParameters, |
|
u32 contextid ); |
|
~CWin32UploadBugReport(); |
|
|
|
EBugReportUploadStatus Upload( CUtlBuffer& buf ); |
|
|
|
private: |
|
|
|
enum States |
|
{ |
|
eCreateTCPSocket = 0, |
|
eConnectToHarvesterServer, |
|
eSendProtocolVersion, |
|
eReceiveProtocolOkay, |
|
eSendUploadCommand, |
|
eReceiveOKToSendFile, |
|
eSendWholeFile, // This could push chunks onto the wire, but we'll just use a whole buffer for now. |
|
eReceiveFileUploadSuccess, |
|
eSendGracefulClose, |
|
eCloseTCPSocket |
|
}; |
|
|
|
bool CreateTCPSocket( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool ConnectToHarvesterServer( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool SendProtocolVersion( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool ReceiveProtocolOkay( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool SendUploadCommand( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool ReceiveOKToSendFile( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool SendWholeFile( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool ReceiveFileUploadSuccess( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool SendGracefulClose( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
bool CloseTCPSocket( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
|
|
typedef bool ( CWin32UploadBugReport::*pfnProtocolStateHandler )( EBugReportUploadStatus& status, CUtlBuffer& buf ); |
|
struct FSMState_t |
|
{ |
|
FSMState_t( uint f, pfnProtocolStateHandler s ) : |
|
first( f ), |
|
second( s ) |
|
{ |
|
} |
|
|
|
uint first; |
|
pfnProtocolStateHandler second; |
|
}; |
|
|
|
void AddState( uint StateIndex, pfnProtocolStateHandler handler ); |
|
void SetNextState( uint StateIndex ); |
|
bool DoBlockingReceive( uint bytesExpected, CUtlBuffer& buf ); |
|
|
|
CUtlVector< FSMState_t > m_States; |
|
uint m_uCurrentState; |
|
struct sockaddr_in m_HarvesterSockAddr; |
|
uint m_SocketTCP; |
|
const TBugReportParameters &m_rBugReportParameters; //lint !e1725 |
|
u32 m_ContextID; |
|
}; |
|
|
|
CWin32UploadBugReport::CWin32UploadBugReport( |
|
const netadr_t & harvester, |
|
const TBugReportParameters & rBugReportParameters, |
|
u32 contextid ) : |
|
m_States(), |
|
m_uCurrentState( eCreateTCPSocket ), |
|
m_HarvesterSockAddr(), |
|
m_SocketTCP( 0 ), |
|
m_rBugReportParameters( rBugReportParameters ), |
|
m_ContextID( contextid ) |
|
{ |
|
harvester.ToSockadr( (struct sockaddr *)&m_HarvesterSockAddr ); |
|
|
|
AddState( eCreateTCPSocket, &CWin32UploadBugReport::CreateTCPSocket ); |
|
AddState( eConnectToHarvesterServer, &CWin32UploadBugReport::ConnectToHarvesterServer ); |
|
AddState( eSendProtocolVersion, &CWin32UploadBugReport::SendProtocolVersion ); |
|
AddState( eReceiveProtocolOkay, &CWin32UploadBugReport::ReceiveProtocolOkay ); |
|
AddState( eSendUploadCommand, &CWin32UploadBugReport::SendUploadCommand ); |
|
AddState( eReceiveOKToSendFile, &CWin32UploadBugReport::ReceiveOKToSendFile ); |
|
AddState( eSendWholeFile, &CWin32UploadBugReport::SendWholeFile ); |
|
AddState( eReceiveFileUploadSuccess, &CWin32UploadBugReport::ReceiveFileUploadSuccess ); |
|
AddState( eSendGracefulClose, &CWin32UploadBugReport::SendGracefulClose ); |
|
AddState( eCloseTCPSocket, &CWin32UploadBugReport::CloseTCPSocket ); |
|
} |
|
|
|
CWin32UploadBugReport::~CWin32UploadBugReport() |
|
{ |
|
if ( m_SocketTCP != 0 ) |
|
{ |
|
closesocket( m_SocketTCP ); //lint !e534 |
|
m_SocketTCP = 0; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Function: DoBlockingReceive() |
|
// |
|
//----------------------------------------------------------------------------- |
|
bool CWin32UploadBugReport::DoBlockingReceive( uint bytesExpected, CUtlBuffer& buf ) |
|
{ |
|
uint totalReceived = 0; |
|
|
|
buf.Purge(); |
|
for ( ;; ) |
|
{ |
|
char temp[ 8192 ]; |
|
|
|
int bytesReceived = recv( m_SocketTCP, temp, sizeof( temp ), 0 ); |
|
if ( bytesReceived <= 0 ) |
|
return false; |
|
|
|
buf.Put( ( const void * )temp, (u32)bytesReceived ); |
|
totalReceived = buf.TellPut(); |
|
if ( totalReceived >= bytesExpected ) |
|
break; |
|
|
|
} |
|
return true; |
|
} |
|
|
|
void CWin32UploadBugReport::AddState( uint StateIndex, pfnProtocolStateHandler handler ) |
|
{ |
|
FSMState_t newState( StateIndex, handler ); |
|
m_States.AddToTail( newState ); |
|
} |
|
|
|
EBugReportUploadStatus CWin32UploadBugReport::Upload( CUtlBuffer& buf ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Commencing bug report upload connection." ); |
|
|
|
EBugReportUploadStatus result = eBugReportUploadSucceeded; |
|
// Run the state machine |
|
while ( 1 ) |
|
{ |
|
Assert( m_States[ m_uCurrentState ].first == m_uCurrentState ); |
|
pfnProtocolStateHandler handler = m_States[ m_uCurrentState ].second; |
|
|
|
if ( !(this->*handler)( result, buf ) ) |
|
{ |
|
return result; |
|
} |
|
} |
|
} |
|
|
|
void CWin32UploadBugReport::SetNextState( uint StateIndex ) |
|
{ |
|
Assert( StateIndex > m_uCurrentState ); |
|
m_uCurrentState = StateIndex; |
|
} |
|
|
|
bool CWin32UploadBugReport::CreateTCPSocket( EBugReportUploadStatus& status, CUtlBuffer& /*buf*/ ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Creating bug report upload socket." ); |
|
|
|
m_SocketTCP = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); |
|
if ( m_SocketTCP == (uint)SOCKET_ERROR ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Socket creation failed." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
SetNextState( eConnectToHarvesterServer ); |
|
return true; |
|
} |
|
|
|
bool CWin32UploadBugReport::ConnectToHarvesterServer( EBugReportUploadStatus& status, CUtlBuffer& /*buf*/ ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Connecting to bug report harvesting server." ); |
|
|
|
if ( connect( m_SocketTCP, (const sockaddr *)&m_HarvesterSockAddr, sizeof( m_HarvesterSockAddr ) ) == SOCKET_ERROR ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Connection failed." ); |
|
|
|
status = eBugReportConnectToCSERServerFailed; |
|
return false; |
|
} |
|
|
|
SetNextState( eSendProtocolVersion ); |
|
return true; |
|
} |
|
|
|
bool CWin32UploadBugReport::SendProtocolVersion( EBugReportUploadStatus& status, CUtlBuffer& buf ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Sending bug report harvester protocol info." ); |
|
buf.SetBigEndian( true ); |
|
// Send protocol version |
|
buf.Purge(); |
|
buf.PutInt( cuCurrentProtocolVersion ); |
|
|
|
if ( send( m_SocketTCP, (const char *)buf.Base(), (int)buf.TellPut(), 0 ) == SOCKET_ERROR ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Send failed." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
SetNextState( eReceiveProtocolOkay ); |
|
return true; |
|
} |
|
|
|
bool CWin32UploadBugReport::ReceiveProtocolOkay( EBugReportUploadStatus& status, CUtlBuffer& buf ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Receiving harvesting protocol acknowledgement." ); |
|
buf.Purge(); |
|
|
|
// Now receive the protocol is acceptable token from the server |
|
if ( !DoBlockingReceive( 1, buf ) ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Didn't receive protocol failure data." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
bool protocolokay = buf.GetChar() ? true : false; |
|
if ( !protocolokay ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Server rejected protocol." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
UpdateProgress( m_rBugReportParameters, "Protocol OK." ); |
|
|
|
SetNextState( eSendUploadCommand ); |
|
return true; |
|
} |
|
|
|
bool CWin32UploadBugReport::SendUploadCommand( EBugReportUploadStatus& status, CUtlBuffer& buf ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Sending harvesting protocol upload request." ); |
|
// Send upload command |
|
buf.Purge(); |
|
|
|
NetworkMessageLengthPrefix_t messageSize |
|
( |
|
sizeof( Command_t ) |
|
+ sizeof( ContextID_t ) |
|
+ sizeof( HarvestFileCommand::FileSize_t ) |
|
+ sizeof( HarvestFileCommand::SendMethod_t ) |
|
+ sizeof( HarvestFileCommand::FileSize_t ) |
|
); |
|
|
|
// Prefix the length to the command |
|
buf.PutInt( (int)messageSize ); |
|
buf.PutChar( Commands::cuSendBugReport ); |
|
buf.PutInt( (int)m_ContextID ); |
|
|
|
buf.PutInt( (int)m_rBugReportParameters.m_uAttachmentFileSize ); |
|
buf.PutInt( static_cast<HarvestFileCommand::SendMethod_t>( eSendMethodWholeRawFileNoBlocks ) ); |
|
buf.PutInt( static_cast<HarvestFileCommand::FileSize_t>( 0 ) ); |
|
|
|
// Send command to server |
|
if ( send( m_SocketTCP, (const char *)buf.Base(), (int)buf.TellPut(), 0 ) == SOCKET_ERROR ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Send failed." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
SetNextState( eReceiveOKToSendFile ); |
|
return true; |
|
} |
|
|
|
bool CWin32UploadBugReport::ReceiveOKToSendFile( EBugReportUploadStatus& status, CUtlBuffer& buf ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Receive bug report harvesting protocol upload permissible." ); |
|
|
|
// Now receive the protocol is acceptable token from the server |
|
if ( !DoBlockingReceive( 1, buf ) ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Receive failed." ); |
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
bool dosend = false; |
|
CommandResponse_t cmd = (CommandResponse_t)buf.GetChar(); |
|
switch ( cmd ) |
|
{ |
|
case HarvestFileCommand::cuOkToSendFile: |
|
{ |
|
dosend = true; |
|
} |
|
break; |
|
case HarvestFileCommand::cuFileTooBig: |
|
case HarvestFileCommand::cuInvalidSendMethod: |
|
case HarvestFileCommand::cuInvalidMaxCompressedChunkSize: |
|
case HarvestFileCommand::cuInvalidBugReportContext: |
|
default: |
|
break; |
|
} |
|
|
|
if ( !dosend ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Server rejected upload command." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
SetNextState( eSendWholeFile ); |
|
return true; |
|
} |
|
|
|
bool CWin32UploadBugReport::SendWholeFile( EBugReportUploadStatus& status, CUtlBuffer& /*buf*/ ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Uploading bug report data." ); |
|
// Send to server |
|
char *filebuf = NULL; |
|
size_t sizeactual = g_pFileSystem->Size( m_rBugReportParameters.m_sAttachmentFile ); |
|
if ( sizeactual > 0 ) |
|
{ |
|
filebuf = new char[ sizeactual + 1 ]; |
|
if ( filebuf ) |
|
{ |
|
FileHandle_t fh; |
|
fh = g_pFileSystem->Open( m_rBugReportParameters.m_sAttachmentFile, "rb" ); |
|
if ( FILESYSTEM_INVALID_HANDLE != fh ) |
|
{ |
|
g_pFileSystem->Read( (void *)filebuf, sizeactual, fh ); |
|
|
|
g_pFileSystem->Close( fh ); |
|
} |
|
filebuf[ sizeactual ] = 0; |
|
} |
|
} |
|
if ( !sizeactual || !filebuf ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "bug .zip file size zero or unable to allocate memory for file." ); |
|
|
|
status = eBugReportUploadFailed; |
|
if ( filebuf ) |
|
{ |
|
delete[] filebuf; |
|
} |
|
return false; |
|
} |
|
|
|
// Send to server |
|
bool bret = true; |
|
if ( send( m_SocketTCP, filebuf, (int)sizeactual, 0 ) == SOCKET_ERROR ) |
|
{ |
|
bret = false; |
|
UpdateProgress( m_rBugReportParameters, "Send failed." ); |
|
|
|
status = eBugReportUploadFailed; |
|
} |
|
else |
|
{ |
|
SetNextState( eReceiveFileUploadSuccess ); |
|
} |
|
|
|
delete[] filebuf; |
|
|
|
return bret; |
|
} |
|
|
|
bool CWin32UploadBugReport::ReceiveFileUploadSuccess( EBugReportUploadStatus& status, CUtlBuffer& buf ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Receiving bug report upload success/fail message." ); |
|
|
|
// Now receive the protocol is acceptable token from the server |
|
if ( !DoBlockingReceive( 1, buf ) ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Receive failed." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
bool success = buf.GetChar() == 1 ? true : false; |
|
if ( !success ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Upload failed." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
UpdateProgress( m_rBugReportParameters, "Upload OK." ); |
|
|
|
SetNextState( eSendGracefulClose ); |
|
return true; |
|
} |
|
|
|
bool CWin32UploadBugReport::SendGracefulClose( EBugReportUploadStatus& status, CUtlBuffer& buf ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Closing connection to server." ); |
|
|
|
// Now send disconnect command |
|
buf.Purge(); |
|
|
|
size_t messageSize = sizeof( Command_t ); |
|
|
|
buf.PutInt( (int)messageSize ); |
|
buf.PutChar( Commands::cuGracefulClose ); |
|
|
|
if ( send( m_SocketTCP, (const char *)buf.Base(), (int)buf.TellPut(), 0 ) == SOCKET_ERROR ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Send failed." ); |
|
|
|
status = eBugReportUploadFailed; |
|
return false; |
|
} |
|
|
|
SetNextState( eCloseTCPSocket ); |
|
return true; |
|
} |
|
|
|
bool CWin32UploadBugReport::CloseTCPSocket( EBugReportUploadStatus& status, CUtlBuffer& /*buf*/ ) |
|
{ |
|
UpdateProgress( m_rBugReportParameters, "Closing socket, upload succeeded." ); |
|
|
|
closesocket( m_SocketTCP );//lint !e534 |
|
m_SocketTCP = 0; |
|
|
|
status = eBugReportUploadSucceeded; |
|
// NOTE: Returning false here ends the state machine!!! |
|
return false; |
|
} |
|
|
|
EBugReportUploadStatus Win32UploadBugReportBlocking |
|
( |
|
const TBugReportParameters & rBugReportParameters |
|
) |
|
{ |
|
EBugReportUploadStatus status = eBugReportUploadFailed; |
|
|
|
CUtlBuffer buf( 2048 ); |
|
|
|
UpdateProgress( rBugReportParameters, "Creating initial report." ); |
|
|
|
buf.SetBigEndian( false ); |
|
|
|
buf.Purge(); |
|
buf.PutChar( C2M_BUGREPORT ); |
|
buf.PutChar( '\n' ); |
|
buf.PutChar( C2M_BUGREPORT_PROTOCOL_VERSION ); |
|
|
|
// See CSERServerProtocol.h for format |
|
|
|
// encryption object |
|
IceKey cipher(1); /* medium encryption level */ |
|
unsigned char ucEncryptionKey[8] = { 200,145,10,149,195,190,108,243 }; |
|
|
|
cipher.set( ucEncryptionKey ); |
|
|
|
CUtlBuffer encrypted( 2000 ); |
|
|
|
u8 corruption_identifier = 0x01; |
|
|
|
encrypted.PutChar( corruption_identifier ); |
|
|
|
encrypted.PutInt( rBugReportParameters.m_uEngineBuildNumber ); // build_identifier |
|
|
|
encrypted.PutString( rBugReportParameters.m_sExecutableName ); |
|
encrypted.PutString( rBugReportParameters.m_sGameDirectory ); |
|
encrypted.PutString( rBugReportParameters.m_sMapName ); |
|
|
|
encrypted.PutInt( rBugReportParameters.m_uRAM ); // ram mb |
|
encrypted.PutInt( rBugReportParameters.m_uCPU ); // cpu mhz |
|
|
|
encrypted.PutString( rBugReportParameters.m_sProcessor ); |
|
|
|
encrypted.PutInt( rBugReportParameters.m_uDXVersionHigh ); // driver version high part |
|
encrypted.PutInt( rBugReportParameters.m_uDXVersionLow ); // driver version low part |
|
encrypted.PutInt( rBugReportParameters.m_uDXVendorId ); // dxvendor id |
|
encrypted.PutInt( rBugReportParameters.m_uDXDeviceId ); // dxdevice id |
|
|
|
encrypted.PutString( rBugReportParameters.m_sOSVersion ); |
|
|
|
encrypted.PutInt( rBugReportParameters.m_uAttachmentFileSize ); |
|
|
|
// protocol version 2 stuff |
|
{ |
|
encrypted.PutString( rBugReportParameters.m_sReportType ); |
|
encrypted.PutString( rBugReportParameters.m_sEmail ); |
|
encrypted.PutString( rBugReportParameters.m_sAccountName ); |
|
} |
|
|
|
// protocol version 3 stuff |
|
{ |
|
encrypted.Put( &rBugReportParameters.m_userid, sizeof( rBugReportParameters.m_userid ) ); |
|
} |
|
|
|
encrypted.PutString( rBugReportParameters.m_sTitle ); |
|
|
|
int bodylen = Q_strlen( rBugReportParameters.m_sBody ) + 1; |
|
|
|
encrypted.PutInt( bodylen ); |
|
encrypted.Put( rBugReportParameters.m_sBody, bodylen ); |
|
|
|
while ( encrypted.TellPut() % 8 ) |
|
{ |
|
encrypted.PutChar( 0 ); |
|
} |
|
|
|
EncryptBuffer( cipher, (unsigned char *)encrypted.Base(), encrypted.TellPut() ); |
|
|
|
buf.PutShort( (int)encrypted.TellPut() ); |
|
buf.Put( (unsigned char *)encrypted.Base(), encrypted.TellPut() ); |
|
|
|
CBlockingUDPSocket bcs; |
|
if ( !bcs.IsValid() ) |
|
{ |
|
return eBugReportUploadFailed; |
|
} |
|
|
|
struct sockaddr_in sa; |
|
rBugReportParameters.m_ipCSERServer.ToSockadr( (struct sockaddr *)&sa ); |
|
|
|
UpdateProgress( rBugReportParameters, "Sending bug report to server." ); |
|
|
|
bcs.SendSocketMessage( sa, (const u8 *)buf.Base(), buf.TellPut() ); //lint !e534 |
|
|
|
UpdateProgress( rBugReportParameters, "Waiting for response." ); |
|
|
|
if ( bcs.WaitForMessage( 2.0f ) ) |
|
{ |
|
UpdateProgress( rBugReportParameters, "Received response." ); |
|
|
|
struct sockaddr_in replyaddress; |
|
buf.EnsureCapacity( 2048 ); |
|
|
|
uint bytesReceived = bcs.ReceiveSocketMessage( &replyaddress, (u8 *)buf.Base(), 2048 ); |
|
if ( bytesReceived > 0 ) |
|
{ |
|
// Fixup actual size |
|
buf.SeekPut( CUtlBuffer::SEEK_HEAD, bytesReceived ); |
|
|
|
UpdateProgress( rBugReportParameters, "Checking response." ); |
|
|
|
// Parse out data |
|
u8 msgtype = (u8)buf.GetChar(); |
|
if ( M2C_ACKBUGREPORT != msgtype ) |
|
{ |
|
UpdateProgress( rBugReportParameters, "Request denied, invalid message type." ); |
|
return eBugReportSendingBugReportHeaderFailed; |
|
} |
|
bool validProtocol = (u8)buf.GetChar() == 1 ? true : false; |
|
if ( !validProtocol ) |
|
{ |
|
UpdateProgress( rBugReportParameters, "Request denied, invalid message protocol." ); |
|
return eBugReportSendingBugReportHeaderFailed; |
|
} |
|
|
|
u8 disposition = (u8)buf.GetChar(); |
|
if ( BR_REQEST_FILES != disposition ) |
|
{ |
|
// Server doesn't want a bug report, oh well |
|
if ( rBugReportParameters.m_uAttachmentFileSize > 0 ) |
|
{ |
|
UpdateProgress( rBugReportParameters, "Bug report accepted, attachment rejected (server too busy)" ); |
|
} |
|
else |
|
{ |
|
UpdateProgress( rBugReportParameters, "Bug report accepted." ); |
|
} |
|
|
|
return eBugReportUploadSucceeded; |
|
} |
|
|
|
// Read in the bug report info parameters |
|
u32 harvester_ip = (u32)buf.GetInt(); |
|
u16 harvester_port = (u16)buf.GetShort(); |
|
u32 dumpcontext = (u32)buf.GetInt(); |
|
|
|
sockaddr_in adr; |
|
adr.sin_family = AF_INET; |
|
adr.sin_port = htons( harvester_port ); |
|
#ifdef WIN32 |
|
adr.sin_addr.S_un.S_addr = harvester_ip; |
|
#else |
|
adr.sin_addr.s_addr = harvester_ip; |
|
#endif |
|
netadr_t BugReportHarvesterFSMIPAddress; |
|
BugReportHarvesterFSMIPAddress.SetFromSockadr( (struct sockaddr *)&adr ); |
|
|
|
UpdateProgress( rBugReportParameters, "Server requested bug report upload." ); |
|
|
|
// Keep using the same scratch buffer for messaging |
|
CWin32UploadBugReport uploader( BugReportHarvesterFSMIPAddress, rBugReportParameters, dumpcontext ); |
|
status = uploader.Upload( buf ); |
|
} |
|
} |
|
else |
|
{ |
|
UpdateProgress( rBugReportParameters, "No response from server." ); |
|
} |
|
|
|
return status; |
|
}
|
|
|