Team Fortress 2 Source Code as on 22/4/2020
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.

445 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #ifdef _WIN32
  7. #if !defined( _X360 )
  8. #include <windows.h>
  9. #endif
  10. #elif defined(POSIX)
  11. #include <sys/socket.h>
  12. #include <netinet/in.h>
  13. #include <pwd.h>
  14. #include <sys/types.h>
  15. #else
  16. #error
  17. #endif
  18. #include "host.h"
  19. #include "quakedef.h"
  20. #include "net.h"
  21. #include "bitbuf.h"
  22. #include "tier0/icommandline.h"
  23. #include "cserserverprotocol_engine.h"
  24. #include "host_phonehome.h"
  25. #include "mathlib/IceKey.H"
  26. #include "tier0/vcrmode.h"
  27. #include "blockingudpsocket.h"
  28. #if defined( _X360 )
  29. #include "xbox/xbox_win32stubs.h"
  30. #endif
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. #define PHONE_HOME_TIMEOUT 1.5f
  34. #define PHONE_HOME_RETRIES 3
  35. //-----------------------------------------------------------------------------
  36. // Purpose: returns a pointer to a function, given a module
  37. // Input : pModuleName - module name
  38. // *pName - proc name
  39. //-----------------------------------------------------------------------------
  40. static char const *g_pszExitMsg = "Renderer: Out of memory, message code %i";
  41. class CPhoneHome : public IPhoneHome
  42. {
  43. public:
  44. CPhoneHome() :
  45. m_bPhoneHome( false ),
  46. m_uSessionID( 0 ),
  47. m_pSocket( 0 )
  48. {
  49. Q_memset( &m_cserIP, 0, sizeof( m_cserIP ) );
  50. Q_memset( m_szBuildIdentifier, 0, sizeof( m_szBuildIdentifier ) );
  51. }
  52. virtual void Shutdown()
  53. {
  54. delete m_pSocket;
  55. m_pSocket = NULL;
  56. }
  57. virtual void Init()
  58. {
  59. char build_identifier[ 32 ];
  60. Q_strncpy( build_identifier, "VLV_INTERNAL ", sizeof( build_identifier ) );
  61. int iBI = CommandLine()->FindParm("-bi");
  62. if ( iBI > 0 )
  63. {
  64. if ( (iBI+1) < CommandLine()->ParmCount() )
  65. {
  66. char const *pBuildParam = CommandLine()->GetParm( iBI + 1 );
  67. Q_memset( build_identifier, 0, sizeof( build_identifier ) );
  68. Q_strncpy( build_identifier, pBuildParam, sizeof( build_identifier ) );
  69. }
  70. else
  71. {
  72. build_identifier[ 0 ] = '!';
  73. }
  74. }
  75. if ( Q_strlen( build_identifier ) >= 1 &&
  76. Q_strnicmp( build_identifier, "VLV_INTERNAL", Q_strlen( "VLV_INTERNAL" ) ) )
  77. {
  78. // Strip trailing spaces from identifer
  79. char *identifer = &build_identifier[ Q_strlen( build_identifier ) - 1 ];
  80. while ( identifer > build_identifier && *identifer == ' ' )
  81. {
  82. *identifer-- = 0;
  83. }
  84. // FIXME: Don't hardcode CSER ip, get from Steam!!!
  85. if ( NET_StringToAdr( "207.173.177.12:27013", &m_cserIP ) )
  86. {
  87. m_bPhoneHome = true;
  88. Q_strncpy( m_szBuildIdentifier, build_identifier, sizeof( m_szBuildIdentifier ) );
  89. m_pSocket = new CBlockingUDPSocket();
  90. }
  91. }
  92. }
  93. virtual void Message( byte msgtype, char const *mapname )
  94. {
  95. if ( !m_bPhoneHome )
  96. return;
  97. if ( !m_pSocket )
  98. return;
  99. switch ( msgtype )
  100. {
  101. default:
  102. break;
  103. case PHONE_MSG_ENGINESTART:
  104. if ( !RequestSessionId( m_uSessionID ) )
  105. {
  106. ExitApp();
  107. }
  108. // Note we always return here!!!
  109. return;
  110. case PHONE_MSG_ENGINEEND:
  111. break;
  112. case PHONE_MSG_MAPSTART:
  113. {
  114. if ( m_bLevelStarted )
  115. {
  116. return;
  117. }
  118. m_bLevelStarted = true;
  119. // Tracker 22394: Don't send map start/finish when building reslists...
  120. if ( CommandLine()->FindParm( "-makereslists" ) )
  121. {
  122. return;
  123. }
  124. }
  125. break;
  126. case PHONE_MSG_MAPEND:
  127. {
  128. if ( !m_bLevelStarted )
  129. {
  130. return;
  131. }
  132. m_bLevelStarted = false;
  133. // Tracker 22394: Don't send map start/finish when building reslists...
  134. if ( CommandLine()->FindParm( "-makereslists" ) )
  135. {
  136. return;
  137. }
  138. }
  139. break;
  140. }
  141. SendSessionMessage( msgtype, mapname );
  142. }
  143. private:
  144. void ExitApp()
  145. {
  146. byte msgtype = 212;
  147. Error( g_pszExitMsg, msgtype );
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose: encrypts an 8-byte sequence
  151. //-----------------------------------------------------------------------------
  152. inline void Encrypt8ByteSequence( IceKey& cipher, const unsigned char *plainText, unsigned char *cipherText)
  153. {
  154. cipher.encrypt(plainText, cipherText);
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose:
  158. //-----------------------------------------------------------------------------
  159. void EncryptBuffer( IceKey& cipher, unsigned char *bufData, uint bufferSize)
  160. {
  161. unsigned char *cipherText = bufData;
  162. unsigned char *plainText = bufData;
  163. uint bytesEncrypted = 0;
  164. while (bytesEncrypted < bufferSize)
  165. {
  166. // encrypt 8 byte section
  167. Encrypt8ByteSequence( cipher, plainText, cipherText);
  168. bytesEncrypted += 8;
  169. cipherText += 8;
  170. plainText += 8;
  171. }
  172. }
  173. void BuildMessage( bf_write& buf, byte msgtype, char const *mapname, unsigned int uSessionID )
  174. {
  175. bf_write encrypted;
  176. ALIGN4 byte encrypted_data[ 2048 ] ALIGN4_POST;
  177. buf.WriteByte( C2M_PHONEHOME );
  178. buf.WriteByte( '\n' );
  179. buf.WriteByte( C2M_PHONEHOME_PROTOCOL_VERSION );
  180. buf.WriteLong( uSessionID ); // sessionid (request new id by sending 0)
  181. // encryption object
  182. IceKey cipher(1); /* medium encryption level */
  183. unsigned char ucEncryptionKey[8] = { 191, 1, 0, 222, 85, 39, 154, 1 };
  184. cipher.set( ucEncryptionKey );
  185. encrypted.StartWriting( encrypted_data, sizeof( encrypted_data ) );
  186. byte corruption_identifier = 0x01;
  187. encrypted.WriteByte( corruption_identifier );
  188. // Data version protocol
  189. encrypted.WriteByte( 1 );
  190. // Write the "build identifier" -- unique to each person we give a build to.
  191. encrypted.WriteString( m_szBuildIdentifier );
  192. {
  193. char computername[ 64 ];
  194. Q_memset( computername, 0, sizeof( computername ) );
  195. #if defined ( _WIN32 )
  196. DWORD length = sizeof( computername ) - 1;
  197. if ( !GetComputerName( computername, &length ) )
  198. {
  199. Q_strncpy( computername, "???", sizeof( computername ) );
  200. }
  201. #else
  202. if ( gethostname( computername, sizeof(computername) ) == -1 )
  203. {
  204. Q_strncpy( computername, "Linux????", sizeof( computername ) );
  205. }
  206. computername[sizeof(computername)-1] = '\0';
  207. #endif
  208. encrypted.WriteString( computername );
  209. }
  210. {
  211. char username[ 64 ];
  212. Q_memset( username, 0, sizeof( username ) );
  213. #if defined ( _WIN32 )
  214. DWORD length = sizeof( username ) - 1;
  215. if ( !GetUserName( username, &length ) )
  216. {
  217. Q_strncpy( username, "???", sizeof( username ) );
  218. }
  219. #else
  220. struct passwd *pass = getpwuid( getuid() );
  221. if ( pass )
  222. {
  223. Q_strncpy( username, pass->pw_name, sizeof( username ) );
  224. }
  225. else
  226. {
  227. Q_strncpy( username, "LinuxUser??", sizeof( username ) );
  228. }
  229. username[sizeof(username)-1] = '\0';
  230. #endif
  231. encrypted.WriteString( username );
  232. }
  233. char gamedir[ 64 ];
  234. Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) );
  235. encrypted.WriteString( gamedir );
  236. unsigned int uBuildNumber = build_number();
  237. encrypted.WriteLong( (int)uBuildNumber );
  238. // WRite timestamp of engine
  239. encrypted.WriteFloat( (float)realtime );
  240. encrypted.WriteByte( msgtype );
  241. if ( mapname != NULL )
  242. {
  243. encrypted.WriteString( mapname );
  244. }
  245. int isDebugUser = ( Sys_IsDebuggerPresent() || CommandLine()->FindParm( "-allowdebug" ) ) ? 1 : 0;
  246. encrypted.WriteByte( isDebugUser );
  247. while ( encrypted.GetNumBytesWritten() % 8 )
  248. {
  249. encrypted.WriteByte( 0 );
  250. }
  251. EncryptBuffer( cipher, (unsigned char *)encrypted.GetData(), encrypted.GetNumBytesWritten() );
  252. buf.WriteShort( (int)encrypted.GetNumBytesWritten() );
  253. buf.WriteBytes( (unsigned char *)encrypted.GetData(), encrypted.GetNumBytesWritten() );
  254. }
  255. void SendSessionMessage( byte msgtype, char const *mapname )
  256. {
  257. if ( m_uSessionID == 0 )
  258. return;
  259. bf_write buf;
  260. ALIGN4 byte data[ 2048 ] ALIGN4_POST;
  261. buf.StartWriting( data, sizeof( data ) );
  262. BuildMessage( buf, msgtype, mapname, m_uSessionID );
  263. struct sockaddr_in sa;
  264. m_cserIP.ToSockadr( (struct sockaddr *)&sa );
  265. m_pSocket->SendSocketMessage( sa, (const byte *)buf.GetData(), buf.GetNumBytesWritten() );
  266. // If we already have a sessionid, don't wait for the server to give us back a new one...
  267. if ( m_uSessionID != 0 )
  268. {
  269. return;
  270. }
  271. if ( m_pSocket->WaitForMessage( PHONE_HOME_TIMEOUT ) )
  272. {
  273. ALIGN4 byte readbuf[ 128 ] ALIGN4_POST;
  274. bf_read replybuf( readbuf, sizeof( readbuf ) );
  275. struct sockaddr_in replyaddress;
  276. uint bytesReceived = m_pSocket->ReceiveSocketMessage( &replyaddress, (byte *)readbuf, sizeof( readbuf ) );
  277. if ( bytesReceived > 0 )
  278. {
  279. // Fixup actual size
  280. replybuf.StartReading( readbuf, bytesReceived );
  281. // Parse out data
  282. byte responseType = (byte)replybuf.ReadByte();
  283. if ( M2C_ACKPHONEHOME == responseType )
  284. {
  285. bool allowPlay = replybuf.ReadByte() == 1 ? true : false;
  286. if ( allowPlay )
  287. {
  288. m_uSessionID = replybuf.ReadLong();
  289. }
  290. }
  291. }
  292. }
  293. }
  294. bool RequestSessionId( unsigned int& id )
  295. {
  296. id = 0u;
  297. bf_write buf;
  298. ALIGN4 byte data[ 2048 ] ALIGN4_POST;
  299. buf.StartWriting( data, sizeof( data ) );
  300. BuildMessage( buf, PHONE_MSG_ENGINESTART, NULL, id );
  301. struct sockaddr_in sa;
  302. m_cserIP.ToSockadr( (struct sockaddr *)&sa );
  303. for ( int retries = 0; retries < PHONE_HOME_RETRIES; ++retries )
  304. {
  305. m_pSocket->SendSocketMessage( sa, (const byte *)buf.GetData(), buf.GetNumBytesWritten() ); //lint !e534
  306. if ( m_pSocket->WaitForMessage( PHONE_HOME_TIMEOUT ) )
  307. {
  308. ALIGN4 byte readbuf[ 128 ] ALIGN4_POST;
  309. bf_read replybuf( readbuf, sizeof( readbuf ) );
  310. struct sockaddr_in replyaddress;
  311. uint bytesReceived = m_pSocket->ReceiveSocketMessage( &replyaddress, (byte *)readbuf, sizeof( readbuf ) );
  312. if ( bytesReceived > 0 )
  313. {
  314. // Fixup actual size
  315. replybuf.StartReading( readbuf, bytesReceived );
  316. // Parse out data
  317. byte responseType = (byte)replybuf.ReadByte();
  318. if ( M2C_ACKPHONEHOME == responseType )
  319. {
  320. bool allowPlay = replybuf.ReadByte() == 1 ? true : false;
  321. if ( allowPlay )
  322. {
  323. id = replybuf.ReadLong();
  324. return true;
  325. }
  326. }
  327. break;
  328. }
  329. }
  330. }
  331. return false;
  332. }
  333. // FIXME, this is BS
  334. bool IsExternalBuild()
  335. {
  336. if ( CommandLine()->FindParm( "-publicbuild" ) )
  337. {
  338. return true;
  339. }
  340. if ( !CommandLine()->FindParm( "-internalbuild" ) &&
  341. !CommandLine()->CheckParm("-dev") )
  342. {
  343. return true;
  344. }
  345. // It's an external build...
  346. if ( m_bPhoneHome )
  347. {
  348. if ( !Q_stricmp( m_szBuildIdentifier, "beta-playtest" ) )
  349. {
  350. // Still internal
  351. return false;
  352. }
  353. return true;
  354. }
  355. return false;
  356. }
  357. bool m_bPhoneHome;
  358. netadr_t m_cserIP;
  359. char m_szBuildIdentifier[ 32 ];
  360. bool m_bLevelStarted;
  361. unsigned int m_uSessionID;
  362. CBlockingUDPSocket *m_pSocket;
  363. };
  364. CPhoneHome g_PhoneHome;
  365. IPhoneHome *phonehome = &g_PhoneHome;