//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "basetypes.h" #ifdef WIN32 #include #elif defined(POSIX) #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #include #include #include #define closesocket close #else #error #endif #include "netadr.h" #include "tier1/strtools.h" #include #include #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( 0 ); const ProtocolAcceptanceFlag_t cuProtocolIsAcceptable = static_cast( 1 ); const Command_t cuMaxCommand = static_cast(255); const CommandResponse_t cuMaxCommandResponse = static_cast(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( eSendMethodWholeRawFileNoBlocks ) ); buf.PutInt( static_cast( 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; }