Counter Strike : Global Offensive Source Code
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.

1167 lines
37 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: steam state machine that handles authenticating steam users
  4. //
  5. //===========================================================================//
  6. #ifdef _WIN32
  7. #if !defined( _X360 )
  8. #include "winlite.h"
  9. #include <winsock2.h> // INADDR_ANY defn
  10. #endif
  11. #elif POSIX
  12. #include <netinet/in.h>
  13. #endif
  14. #include "sv_steamauth.h"
  15. #include "sv_filter.h"
  16. #include "inetchannel.h"
  17. #include "netadr.h"
  18. #include "server.h"
  19. #include "proto_oob.h"
  20. #ifndef DEDICATED
  21. #include "Steam.h"
  22. #include "client.h"
  23. #endif
  24. #include "host.h"
  25. #include "sv_plugin.h"
  26. #include "sv_log.h"
  27. #include "filesystem_engine.h"
  28. #include "filesystem_init.h"
  29. #include "tier0/icommandline.h"
  30. #include "steam/steam_gameserver.h"
  31. #include "hltvserver.h"
  32. #if defined( REPLAY_ENABLED )
  33. #include "replayserver.h"
  34. #endif
  35. #include "pr_edict.h"
  36. #include "steam/steamclientpublic.h"
  37. #include "mathlib/expressioncalculator.h"
  38. #include "sys_dll.h"
  39. #include "host_cmd.h"
  40. #include "tier1/fmtstr.h"
  41. extern EUniverse GetSteamUniverse( void );
  42. extern ConVar sv_lan;
  43. extern ConVar sv_region;
  44. extern ConVar cl_hideserverip;
  45. #if defined( _X360 )
  46. #include "xbox/xbox_win32stubs.h"
  47. #endif
  48. #if defined( _PS3 )
  49. #include "ps3/ps3_win32stubs.h"
  50. #endif
  51. // memdbgon must be the last include file in a .cpp file!!!
  52. #include "tier0/memdbgon.h"
  53. #pragma warning( disable: 4355 ) // disables ' 'this' : used in base member initializer list'
  54. ConVar sv_master_share_game_socket( "sv_master_share_game_socket", "1", 0,
  55. "Use the game's socket to communicate to the master server. "
  56. "If this is 0, then it will create a socket on -steamport + 1 "
  57. "to communicate to the master server on." );
  58. ConVar sv_steamauth_enforce( "sv_steamauth_enforce", "2", FCVAR_RELEASE,
  59. "By default, player must maintain a reliable connection to Steam servers. When player Steam session drops, enforce it: "
  60. "2 = instantly kick, 1 = kick at next spawn, 0 = do not kick." );
  61. //-----------------------------------------------------------------------------
  62. // Purpose: singleton accessor
  63. //-----------------------------------------------------------------------------
  64. static CSteam3Server s_Steam3Server;
  65. CSteam3Server &Steam3Server()
  66. {
  67. return s_Steam3Server;
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Purpose: Constructor
  71. //-----------------------------------------------------------------------------
  72. CSteam3Server::CSteam3Server()
  73. #if !defined(NO_STEAM)
  74. :
  75. m_CallbackLogonSuccess( this, &CSteam3Server::OnLogonSuccess ),
  76. m_CallbackLogonFailure( this, &CSteam3Server::OnLogonFailure ),
  77. m_CallbackLoggedOff( this, &CSteam3Server::OnLoggedOff ),
  78. m_CallbackValidateAuthTicketResponse( this, &CSteam3Server::OnValidateAuthTicketResponse ),
  79. m_CallbackGSPolicyResponse( this, &CSteam3Server::OnGSPolicyResponse )
  80. #endif
  81. {
  82. m_bHasActivePlayers = false;
  83. m_bLogOnResult = false;
  84. m_eServerMode = eServerModeInvalid;
  85. m_bWantsSecure = false; // default to insecure currently, this may change
  86. m_bInitialized = false;
  87. m_bLogOnFinished = false;
  88. m_bMasterServerUpdaterSharingGameSocket = false;
  89. m_steamIDLanOnly.InstancedSet( 0,0, k_EUniversePublic, k_EAccountTypeInvalid );
  90. m_SteamIDGS.InstancedSet( 1, 0, k_EUniverseInvalid, k_EAccountTypeInvalid );
  91. m_QueryPort = 0;
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose: detect current server mode based on cvars & settings
  95. //-----------------------------------------------------------------------------
  96. EServerMode CSteam3Server::GetCurrentServerMode()
  97. {
  98. if ( sv_lan.GetBool() )
  99. {
  100. return eServerModeNoAuthentication;
  101. }
  102. #ifdef _PS3
  103. else if ( MAX( XBX_GetNumGameUsers(), 1 ) >= sv.GetMaxClients() ) // PS3 local game
  104. {
  105. return eServerModeNoAuthentication;
  106. }
  107. #endif
  108. else if ( !Host_IsSecureServerAllowed() || CommandLine()->FindParm( "-insecure" ) )
  109. {
  110. return eServerModeAuthentication;
  111. }
  112. else
  113. {
  114. return eServerModeAuthenticationAndSecure;
  115. }
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose: Destructor
  119. //-----------------------------------------------------------------------------
  120. CSteam3Server::~CSteam3Server()
  121. {
  122. Shutdown();
  123. }
  124. void CSteam3Server::DeactivateAndLogoff()
  125. {
  126. if ( serverGameDLL )
  127. serverGameDLL->GameServerSteamAPIActivated( false );
  128. if ( SteamGameServer() )
  129. SteamGameServer()->LogOff();
  130. Shutdown();
  131. }
  132. void CSteam3Server::Activate()
  133. {
  134. // we are active, check if sv_lan changed
  135. if ( GetCurrentServerMode() == m_eServerMode )
  136. {
  137. // we are active and LANmode didnt change. done.
  138. return;
  139. }
  140. if ( BIsActive() )
  141. {
  142. // shut down before we change server mode
  143. Shutdown();
  144. }
  145. m_unIP = INADDR_ANY;
  146. m_usPort = 26900;
  147. int nPort = CommandLine()->FindParm( "-steamport" );
  148. if ( nPort )
  149. {
  150. char const *pPortNum = CommandLine()->GetParm( nPort + 1 );
  151. m_usPort = EvaluateExpression( pPortNum, 26900 );
  152. }
  153. static ConVarRef ipname( "ip" );
  154. if ( ipname.IsValid() && ipname.GetString()[0] )
  155. {
  156. netadr_t ipaddr;
  157. NET_StringToAdr( ipname.GetString(), &ipaddr );
  158. if ( !ipaddr.IsLoopback() && !ipaddr.IsLocalhost() )
  159. {
  160. m_unIP = ipaddr.GetIPHostByteOrder();
  161. }
  162. }
  163. static ConVarRef ipname_steam( "ip_steam" ); // Override Steam network interface if needed
  164. if ( ipname_steam.IsValid() && ipname_steam.GetString()[0] )
  165. {
  166. netadr_t ipaddr;
  167. NET_StringToAdr( ipname_steam.GetString(), &ipaddr );
  168. if ( !ipaddr.IsLoopback() && !ipaddr.IsLocalhost() )
  169. {
  170. m_unIP = ipaddr.GetIPHostByteOrder();
  171. }
  172. }
  173. m_eServerMode = GetCurrentServerMode();
  174. char gamedir[MAX_OSPATH];
  175. Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) );
  176. // Figure out the game port. If we're doing a SrcTV relay, then ignore the NS_SERVER port and don't tell Steam that we have a game server.
  177. uint16 usGamePort = NET_GetUDPPort( NS_SERVER );
  178. uint16 usSpectatorPort = NET_GetUDPPort( NS_HLTV ); // ???TODO: what about GOTV[1]?
  179. uint16 usMasterServerUpdaterPort;
  180. if ( sv_master_share_game_socket.GetBool() )
  181. {
  182. m_bMasterServerUpdaterSharingGameSocket = true;
  183. usMasterServerUpdaterPort = MASTERSERVERUPDATERPORT_USEGAMESOCKETSHARE;
  184. if ( sv.IsActive() )
  185. m_QueryPort = usGamePort;
  186. else
  187. m_QueryPort = usSpectatorPort;
  188. }
  189. else
  190. {
  191. m_bMasterServerUpdaterSharingGameSocket = false;
  192. usMasterServerUpdaterPort = m_usPort;
  193. m_QueryPort = m_usPort;
  194. }
  195. if ( NET_IsDedicatedForXbox() )
  196. {
  197. Warning( "************************************************\n" );
  198. Warning( "* Dedicated Server for Xbox. Not using STEAM. *\n" );
  199. Warning( "* This server will operate in LAN mode only. *\n" );
  200. Warning( "************************************************\n" );
  201. m_eServerMode = eServerModeNoAuthentication;
  202. sv_lan.SetValue( true );
  203. return;
  204. }
  205. #ifndef _X360
  206. #if defined( NO_STEAM )
  207. m_eServerMode = eServerModeNoAuthentication;
  208. sv_lan.SetValue( true );
  209. return;
  210. #else
  211. switch ( m_eServerMode )
  212. {
  213. case eServerModeNoAuthentication:
  214. Msg( "Initializing Steam libraries for LAN server\n" );
  215. break;
  216. case eServerModeAuthentication:
  217. Msg( "Initializing Steam libraries for INSECURE Internet server. Authentication and VAC not requested.\n" );
  218. break;
  219. case eServerModeAuthenticationAndSecure:
  220. Msg( "Initializing Steam libraries for secure Internet server\n" );
  221. break;
  222. default:
  223. Warning( "Bogus eServermode %d!\n", m_eServerMode );
  224. Assert( !"Bogus server mode?!" );
  225. break;
  226. }
  227. #ifdef _PS3
  228. extern SteamPS3Params_t g_EngineSteamPS3Params;
  229. if ( !SteamGameServer_Init( &g_EngineSteamPS3Params,
  230. #else
  231. SteamAPI_SetTryCatchCallbacks( false ); // We don't use exceptions, so tell steam not to use try/catch in callback handlers
  232. if ( !SteamGameServer_InitSafe(
  233. #endif
  234. m_unIP,
  235. m_usPort+1, // Steam lives on -steamport + 1, master server updater lives on -steamport.
  236. usGamePort,
  237. usMasterServerUpdaterPort,
  238. m_eServerMode,
  239. GetHostVersionString() ) )
  240. {
  241. steam_no_good:
  242. #if !defined( NO_STEAM )
  243. Warning( "************************************************\n" );
  244. Warning( "* Unable to load Steam support library. *\n" );
  245. Warning( "* This server will operate in LAN mode only. *\n" );
  246. Warning( "************************************************\n" );
  247. #endif
  248. m_eServerMode = eServerModeNoAuthentication;
  249. if ( !IsPS3() )
  250. sv_lan.SetValue( true );
  251. return;
  252. }
  253. // note that SteamGameServer_InitSafe() calls SteamAPI_SetBreakpadAppID() for you, which is what we don't want if we wish
  254. // to report crashes under a different AppId. Reset it back to our crashing one now.
  255. extern AppId_t g_nDedicatedServerAppIdBreakpad;
  256. if ( g_nDedicatedServerAppIdBreakpad ) // Actually force breakpad interfaces to use an override AppID
  257. SteamAPI_SetBreakpadAppID( g_nDedicatedServerAppIdBreakpad );
  258. // Steam API context init
  259. Init();
  260. if ( SteamGameServer() == NULL )
  261. {
  262. Assert( false );
  263. goto steam_no_good;
  264. }
  265. #endif // NO_STEAM
  266. // Set some stuff that should NOT change while the server is
  267. // running
  268. SteamGameServer()->SetProduct( GetHostProductString() );
  269. SteamGameServer()->SetGameDescription( serverGameDLL->GetGameDescription() );
  270. SteamGameServer()->SetDedicatedServer( sv.IsDedicated() );
  271. SteamGameServer()->SetModDir( gamedir );
  272. // Use anonymous logon, or persistent?
  273. if ( m_sAccountToken.IsEmpty() )
  274. {
  275. if ( serverGameDLL && serverGameDLL->IsValveDS() )
  276. {
  277. Msg( "Logging in as official server anonymously.\n" );
  278. }
  279. else if ( NET_IsDedicated() || sv.IsDedicated() )
  280. {
  281. Warning( "****************************************************\n" );
  282. Warning( "* *\n" );
  283. Warning( "* No Steam account token was specified. *\n" );
  284. Warning( "* Logging into anonymous game server account. *\n" );
  285. Warning( "* Connections will be restricted to LAN only. *\n" );
  286. Warning( "* *\n" );
  287. Warning( "* To create a game server account go to *\n" );
  288. Warning( "* http://steamcommunity.com/dev/managegameservers *\n" );
  289. Warning( "* *\n" );
  290. Warning( "****************************************************\n" );
  291. }
  292. else
  293. {
  294. Msg( "Logging into anonymous listen server account.\n" );
  295. }
  296. SteamGameServer()->LogOnAnonymous();
  297. }
  298. else
  299. {
  300. Msg( "Logging into Steam gameserver account with logon token '%.8sxxxxxxxxxxxxxxxxxxxxxxxx'\n", m_sAccountToken.String() );
  301. // TODO: Change this to use just the token when the SDK is updated
  302. SteamGameServer()->LogOn( m_sAccountToken );
  303. }
  304. #endif
  305. NET_SteamDatagramServerListen();
  306. SendUpdatedServerDetails();
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose: game server stopped, shutdown Steam game server session
  310. //-----------------------------------------------------------------------------
  311. void CSteam3Server::Shutdown()
  312. {
  313. if ( !BIsActive() )
  314. return;
  315. #if !defined( NO_STEAM )
  316. SteamGameServer_Shutdown();
  317. #endif
  318. m_bHasActivePlayers = false;
  319. m_bLogOnResult = false;
  320. m_SteamIDGS = k_steamIDNotInitYetGS;
  321. m_eServerMode = eServerModeInvalid;
  322. #if !defined( NO_STEAM )
  323. Clear(); // Steam API context shutdown
  324. #endif
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Purpose: returns true if the userid's are the same
  328. //-----------------------------------------------------------------------------
  329. bool CSteam3Server::CompareUserID( const USERID_t & id1, const USERID_t & id2 )
  330. {
  331. if ( id1.idtype != id2.idtype )
  332. return false;
  333. switch ( id1.idtype )
  334. {
  335. case IDTYPE_STEAM:
  336. case IDTYPE_VALVE:
  337. {
  338. return ( id1.uid.steamid.m_SteamInstanceID==id2.uid.steamid.m_SteamInstanceID &&
  339. id1.uid.steamid.m_SteamLocalUserID.As64bits == id2.uid.steamid.m_SteamLocalUserID.As64bits);
  340. }
  341. default:
  342. break;
  343. }
  344. return false;
  345. }
  346. //-----------------------------------------------------------------------------
  347. // Purpose: returns true if this userid is already on this server
  348. //-----------------------------------------------------------------------------
  349. bool CSteam3Server::CheckForDuplicateSteamID( const CBaseClient *client )
  350. {
  351. // in LAN mode we allow reuse of SteamIDs
  352. if ( BLanOnly() )
  353. return false;
  354. // Compare connecting client's ID to other IDs on the server
  355. for ( int i=0 ; i< sv.GetClientCount() ; i++ )
  356. {
  357. const IClient *cl = sv.GetClient( i );
  358. // Not connected, no SteamID yet
  359. if ( !cl->IsConnected() || cl->IsFakeClient() )
  360. continue;
  361. if ( cl->GetNetworkID().idtype != IDTYPE_STEAM )
  362. continue;
  363. // don't compare this client against himself in the list
  364. if ( client == cl )
  365. continue;
  366. if ( !CompareUserID( client->GetNetworkID(), cl->GetNetworkID() ) )
  367. continue;
  368. // SteamID is reused
  369. return true;
  370. }
  371. return false;
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Purpose: Called when secure policy is set
  375. //-----------------------------------------------------------------------------
  376. const CSteamID &CSteam3Server::GetGSSteamID() const
  377. {
  378. if ( BLanOnly() )
  379. {
  380. // return special LAN mode SteamID
  381. static CSteamID s_LAN = k_steamIDLanModeGS;
  382. return s_LAN;
  383. }
  384. else
  385. {
  386. return m_SteamIDGS;
  387. }
  388. }
  389. #if !defined(NO_STEAM)
  390. //-----------------------------------------------------------------------------
  391. // Purpose: Called when secure policy is set
  392. //-----------------------------------------------------------------------------
  393. void CSteam3Server::OnGSPolicyResponse( GSPolicyResponse_t *pPolicyResponse )
  394. {
  395. if ( !BIsActive() )
  396. return;
  397. // We need to make sure we include our "secure" tag in the server data
  398. sv.UpdateGameType();
  399. if ( SteamGameServer() && SteamGameServer()->BSecure() )
  400. {
  401. Msg( "VAC secure mode is activated.\n" );
  402. }
  403. else
  404. {
  405. Msg( "VAC secure mode disabled.\n" );
  406. }
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Purpose:
  410. //-----------------------------------------------------------------------------
  411. void CSteam3Server::OnLogonSuccess( SteamServersConnected_t *pLogonSuccess )
  412. {
  413. if ( !BIsActive() )
  414. return;
  415. if ( !m_bLogOnResult )
  416. {
  417. m_bLogOnResult = true;
  418. }
  419. #ifndef DEDICATED
  420. // Notify the client that they should retry their connection. This avoids a ~six second timeout
  421. // when connecting to a local server.
  422. GetBaseLocalClient().ResetConnectionRetries();
  423. #endif
  424. if ( !BLanOnly() )
  425. {
  426. Msg( "Connection to Steam servers successful.\n" );
  427. if ( SteamGameServer() && ( cl_hideserverip.GetInt()<=0 ) )
  428. {
  429. uint32 ip = SteamGameServer()->GetPublicIP();
  430. Msg( " Public IP is %d.%d.%d.%d.\n", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255 );
  431. static bool s_bAddressCertificateVerified = !!CommandLine()->FindParm( "-ignore_certificate_address" );
  432. if ( !s_bAddressCertificateVerified )
  433. {
  434. s_bAddressCertificateVerified = true;
  435. const byte *pbNetEncryptPublic = NULL;
  436. int cbNetEncryptPublic = 0;
  437. const byte *pbNetEncryptSignature = NULL;
  438. int cbNetEncryptSignature = 0;
  439. bool bServerRequiresEncryptedChannels =
  440. NET_CryptGetNetworkCertificate( k_ENetworkCertificate_PublicKey, &pbNetEncryptPublic, &cbNetEncryptPublic ) &&
  441. NET_CryptGetNetworkCertificate( k_ENetworkCertificate_Signature, &pbNetEncryptSignature, &cbNetEncryptSignature );
  442. byte *pbAllocatedSampleKey = NULL;
  443. int cbAllocatedSampleKeyCryptoBlock = 0;
  444. if ( bServerRequiresEncryptedChannels &&
  445. !NET_CryptVerifyServerCertificateAndAllocateSessionKey( true, ns_address( netadr_t( ip, 0 ) ),
  446. pbNetEncryptPublic, cbNetEncryptPublic,
  447. pbNetEncryptSignature, cbNetEncryptSignature,
  448. &pbAllocatedSampleKey, &cbAllocatedSampleKeyCryptoBlock ) )
  449. {
  450. Warning( "NET_CryptVerifyServerCertificateAndAllocateSessionKey failed to validate gameserver certificate using server public address %s!\n", netadr_t( ip, 0 ).ToString( true ) );
  451. Plat_ExitProcess( 0 );
  452. }
  453. delete pbAllocatedSampleKey;
  454. }
  455. }
  456. }
  457. if ( SteamGameServer() )
  458. {
  459. m_SteamIDGS = SteamGameServer()->GetSteamID();
  460. if ( m_SteamIDGS.BAnonGameServerAccount() )
  461. {
  462. Msg( "Assigned anonymous gameserver Steam ID %s.\n", m_SteamIDGS.Render() );
  463. }
  464. else if ( m_SteamIDGS.BPersistentGameServerAccount() )
  465. {
  466. Msg( "Assigned persistent gameserver Steam ID %s.\n", m_SteamIDGS.Render() );
  467. }
  468. else
  469. {
  470. Warning( "Assigned Steam ID %s, which is of an unexpected type!\n", m_SteamIDGS.Render() );
  471. Assert( !"Unexpected steam ID type!" );
  472. }
  473. switch ( m_SteamIDGS.GetEUniverse() )
  474. {
  475. case k_EUniversePublic:
  476. break;
  477. case k_EUniverseBeta:
  478. Msg( "Connected to Steam BETA universe.\n" );
  479. break;
  480. case k_EUniverseDev:
  481. Msg( "Connected to Steam DEV universe.\n" );
  482. break;
  483. default:
  484. Msg( "Connected to Steam universe %d\n", m_SteamIDGS.GetEUniverse() );
  485. break;
  486. }
  487. }
  488. else
  489. {
  490. m_SteamIDGS = k_steamIDNotInitYetGS;
  491. }
  492. // send updated server details
  493. // OnLogonSuccess() gets called each time we logon, so if we get dropped this gets called
  494. // again and we get need to retell the AM our details
  495. SendUpdatedServerDetails();
  496. if ( SteamGameServer() )
  497. {
  498. uint32 ip = SteamGameServer()->GetPublicIP();
  499. sv.OnSteamServerLogonSuccess( ip );
  500. }
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: callback on unable to connect to the steam3 backend
  504. // Input : eResult -
  505. //-----------------------------------------------------------------------------
  506. void CSteam3Server::OnLogonFailure( SteamServerConnectFailure_t *pLogonFailure )
  507. {
  508. if ( !BIsActive() )
  509. return;
  510. if ( !m_bLogOnResult )
  511. {
  512. char const *szFatalError = NULL;
  513. switch ( pLogonFailure->m_eResult )
  514. {
  515. case k_EResultAccountNotFound: // account not found
  516. szFatalError = "* Invalid Steam account token was specified. *\n";
  517. break;
  518. case k_EResultGSLTDenied: // a game server login token owned by this token's owner has been banned
  519. szFatalError = "* Steam account token specified was revoked. *\n";
  520. break;
  521. case k_EResultGSOwnerDenied: // game server owner is denied for other reason (account lock, community ban, vac ban, missing phone)
  522. szFatalError = "* Steam account token owner no longer eligible. *\n";
  523. break;
  524. case k_EResultGSLTExpired: // this game server login token was disused for a long time and was marked expired
  525. szFatalError = "* Steam account token has expired from inactivity.*\n";
  526. break;
  527. case k_EResultServiceUnavailable:
  528. if ( !BLanOnly() )
  529. {
  530. Msg( "Connection to Steam servers successful (SU).\n" );
  531. }
  532. break;
  533. default:
  534. if ( !BLanOnly() )
  535. {
  536. Warning( "Could not establish connection to Steam servers.\n" );
  537. }
  538. break;
  539. }
  540. if ( szFatalError )
  541. {
  542. if ( NET_IsDedicated() || sv.IsDedicated() )
  543. {
  544. Warning( "****************************************************\n" );
  545. Warning( "* FATAL ERROR *\n" );
  546. Warning( "%s", szFatalError );
  547. Warning( "* Double-check your sv_setsteamaccount setting. *\n" );
  548. Warning( "* *\n" );
  549. Warning( "* To create a game server account go to *\n" );
  550. Warning( "* http://steamcommunity.com/dev/managegameservers *\n" );
  551. Warning( "* *\n" );
  552. Warning( "****************************************************\n" );
  553. Plat_ExitProcess( 0 );
  554. return;
  555. }
  556. if ( !BLanOnly() )
  557. {
  558. Msg( "Connection to Steam servers successful (%d).\n", pLogonFailure->m_eResult );
  559. }
  560. }
  561. }
  562. m_bLogOnResult = true;
  563. }
  564. //-----------------------------------------------------------------------------
  565. // Purpose:
  566. // Input : eResult -
  567. //-----------------------------------------------------------------------------
  568. void CSteam3Server::OnLoggedOff( SteamServersDisconnected_t *pLoggedOff )
  569. {
  570. if ( !BLanOnly() )
  571. {
  572. Warning( "Connection to Steam servers lost. (Result = %d)\n", pLoggedOff->m_eResult );
  573. }
  574. if ( GetGSSteamID().BPersistentGameServerAccount() )
  575. {
  576. switch ( pLoggedOff->m_eResult )
  577. {
  578. case k_EResultLoggedInElsewhere:
  579. case k_EResultLogonSessionReplaced:
  580. Warning( "****************************************************\n" );
  581. Warning( "* *\n" );
  582. Warning( "* Steam account token was reused elsewhere. *\n" );
  583. Warning( "* Make sure you are using a separate account *\n" );
  584. Warning( "* token for each game server that you operate. *\n" );
  585. Warning( "* *\n" );
  586. Warning( "* To create additional game server accounts go to *\n" );
  587. Warning( "* http://steamcommunity.com/dev/managegameservers *\n" );
  588. Warning( "* *\n" );
  589. Warning( "* This game server instance will now shut down! *\n" );
  590. Warning( "* *\n" );
  591. Warning( "****************************************************\n" );
  592. Cbuf_AddText( CBUF_SERVER, "quit;\n" );
  593. return;
  594. }
  595. }
  596. }
  597. //-----------------------------------------------------------------------------
  598. // Purpose:
  599. //-----------------------------------------------------------------------------
  600. void CSteam3Server::OnValidateAuthTicketResponse( ValidateAuthTicketResponse_t *pValidateAuthTicketResponse )
  601. {
  602. //Msg("Steam backend:Got approval for %x\n", pGSClientApprove->m_SteamID.ConvertToUint64() );
  603. // We got the approval message from the back end.
  604. // Note that if we dont get it, we default to approved anyway
  605. // dont need to send anything back
  606. if ( !BIsActive() )
  607. return;
  608. CBaseClient *client = ClientFindFromSteamID( pValidateAuthTicketResponse->m_SteamID );
  609. if ( !client )
  610. return;
  611. if ( pValidateAuthTicketResponse->m_eAuthSessionResponse != k_EAuthSessionResponseOK )
  612. {
  613. OnValidateAuthTicketResponseHelper( client, pValidateAuthTicketResponse->m_eAuthSessionResponse );
  614. return;
  615. }
  616. if ( Filter_IsUserBanned( client->GetNetworkID() ) )
  617. {
  618. sv.RejectConnection( client->GetNetChannel()->GetRemoteAddress(), "#Valve_Reject_Banned_From_Server" );
  619. client->Disconnect( va( "STEAM UserID %s is not allowed to join this server", client->GetNetworkIDString() ) );
  620. }
  621. else if ( CheckForDuplicateSteamID( client ) )
  622. {
  623. client->Disconnect( CFmtStr( "STEAM UserID %s is already in use on this server", client->GetNetworkIDString() ) );
  624. }
  625. else
  626. {
  627. char msg[ 512 ];
  628. sprintf( msg, "\"%s<%i><%s><>\" STEAM USERID validated\n", client->GetClientName(), client->GetUserID(), client->GetNetworkIDString() );
  629. DevMsg( "%s", msg );
  630. g_Log.Printf( "%s", msg );
  631. g_pServerPluginHandler->NetworkIDValidated( client->GetClientName(), client->GetNetworkIDString() );
  632. // Tell IServerGameClients if its version is high enough.
  633. if ( g_iServerGameClientsVersion >= 4 )
  634. {
  635. serverGameClients->NetworkIDValidated( client->GetClientName(), client->GetNetworkIDString(), pValidateAuthTicketResponse->m_SteamID );
  636. }
  637. }
  638. client->SetFullyAuthenticated();
  639. }
  640. //-----------------------------------------------------------------------------
  641. // Purpose: invalid steam logon errors can be enforced differently, this function takes care of all the rules
  642. //-----------------------------------------------------------------------------
  643. void CSteam3Server::OnInvalidSteamLogonErrorForClient( CBaseClient *cl )
  644. {
  645. if ( BLanOnly() )
  646. return;
  647. bool bDisconnectRightNow = true;
  648. if ( cl->IsFullyAuthenticated() )
  649. {
  650. if ( sv_steamauth_enforce.GetInt() == 0 )
  651. {
  652. bDisconnectRightNow = false;
  653. }
  654. else if ( sv_steamauth_enforce.GetInt() == 1 )
  655. {
  656. KeyValues *kvCommand = new KeyValues( "InvalidSteamLogon" );
  657. KeyValues::AutoDeleteInline autodelete( kvCommand );
  658. serverGameClients->ClientCommandKeyValues( EDICT_NUM( cl->m_nEntityIndex ), kvCommand );
  659. if ( !kvCommand->GetBool( "disconnect" ) )
  660. bDisconnectRightNow = false;
  661. }
  662. }
  663. if ( bDisconnectRightNow )
  664. {
  665. cl->Disconnect( INVALID_STEAM_LOGON );
  666. }
  667. else
  668. {
  669. Warning( "STEAMAUTH: Client %s not immediately kicked because sv_steamauth_enforce=%d\n", cl->GetClientName(), sv_steamauth_enforce.GetInt() );
  670. g_Log.Printf( "STEAMAUTH: Client %s not immediately kicked because sv_steamauth_enforce=%d\n", cl->GetClientName(), sv_steamauth_enforce.GetInt() );
  671. }
  672. }
  673. //-----------------------------------------------------------------------------
  674. // Purpose: helper for receiving a response to authenticating a user
  675. // Input : steamID
  676. // eAuthSessionResponse - reason
  677. //-----------------------------------------------------------------------------
  678. void CSteam3Server::OnValidateAuthTicketResponseHelper( CBaseClient *cl, EAuthSessionResponse eAuthSessionResponse )
  679. {
  680. INetChannel *netchan = cl->GetNetChannel();
  681. // If the client is timing out, the Steam failure is probably related (e.g. game crashed). Let's just print that the client timed out.
  682. if ( netchan && netchan->IsTimingOut() )
  683. {
  684. cl->Disconnect( CFmtStr( "%s timed out", cl->GetClientName() ) );
  685. return;
  686. }
  687. // Emit a more detailed diagnostic.
  688. Warning( "STEAMAUTH: Client %s received failure code %d\n", cl->GetClientName(), (int)eAuthSessionResponse );
  689. g_Log.Printf( "STEAMAUTH: Client %s received failure code %d\n", cl->GetClientName(), (int)eAuthSessionResponse );
  690. switch ( eAuthSessionResponse )
  691. {
  692. case k_EAuthSessionResponseUserNotConnectedToSteam:
  693. OnInvalidSteamLogonErrorForClient( cl );
  694. break;
  695. case k_EAuthSessionResponseLoggedInElseWhere:
  696. if ( !BLanOnly() )
  697. cl->Disconnect( INVALID_STEAM_LOGGED_IN_ELSEWHERE );
  698. break;
  699. case k_EAuthSessionResponseNoLicenseOrExpired:
  700. cl->Disconnect( "This Steam account does not own this game. \nPlease login to the correct Steam account" );
  701. break;
  702. case k_EAuthSessionResponseVACBanned:
  703. if ( !BLanOnly() )
  704. cl->Disconnect( INVALID_STEAM_VACBANSTATE );
  705. break;
  706. case k_EAuthSessionResponseAuthTicketCanceled:
  707. OnInvalidSteamLogonErrorForClient( cl );
  708. break;
  709. case k_EAuthSessionResponseAuthTicketInvalidAlreadyUsed:
  710. case k_EAuthSessionResponseAuthTicketInvalid:
  711. OnInvalidSteamLogonErrorForClient( cl );
  712. break;
  713. case k_EAuthSessionResponseVACCheckTimedOut:
  714. switch ( sv_steamauth_enforce.GetInt() )
  715. {
  716. case 0:
  717. case 1: // we may need to defer the kick till a bit later
  718. OnInvalidSteamLogonErrorForClient( cl );
  719. break;
  720. default:
  721. cl->Disconnect( "VAC authentication error" );
  722. break;
  723. }
  724. break;
  725. default:
  726. cl->Disconnect( "Client dropped by server" );
  727. break;
  728. }
  729. }
  730. #endif
  731. //-----------------------------------------------------------------------------
  732. // Purpose:
  733. // Input : steamIDFind -
  734. // Output : IClient
  735. //-----------------------------------------------------------------------------
  736. CBaseClient *CSteam3Server::ClientFindFromSteamID( CSteamID & steamIDFind )
  737. {
  738. for ( int i=0 ; i< sv.GetClientCount() ; i++ )
  739. {
  740. CBaseClient *cl = (CBaseClient *)sv.GetClient( i );
  741. // Not connected, no SteamID yet
  742. if ( !cl->IsConnected() || cl->IsFakeClient() )
  743. continue;
  744. if ( cl->GetNetworkID().idtype != IDTYPE_STEAM )
  745. continue;
  746. USERID_t id = cl->GetNetworkID();
  747. CSteamID steamIDClient;
  748. steamIDClient.SetFromSteam2( &id.uid.steamid, steamIDFind.GetEUniverse() );
  749. if (steamIDClient == steamIDFind )
  750. {
  751. return cl;
  752. }
  753. }
  754. return NULL;
  755. }
  756. //-----------------------------------------------------------------------------
  757. // Purpose: tell Steam that a new user connected
  758. //-----------------------------------------------------------------------------
  759. bool CSteam3Server::NotifyClientConnect( CBaseClient *client, uint32 unUserID, const ns_address & adr, const void *pvCookie, uint32 ucbCookie )
  760. {
  761. if ( !BIsActive() )
  762. return true;
  763. if ( !client || client->IsFakeClient() )
  764. return false;
  765. // steamID is prepended to the ticket
  766. CUtlBuffer buffer( pvCookie, ucbCookie, CUtlBuffer::READ_ONLY );
  767. uint64 ulSteamID = LittleQWord( buffer.GetInt64() );
  768. CSteamID steamID( ulSteamID );
  769. // skip the steamID
  770. pvCookie = (uint8 *)pvCookie + sizeof( uint64 );
  771. ucbCookie -= sizeof( uint64 );
  772. EBeginAuthSessionResult eResult = SteamGameServer()->BeginAuthSession( pvCookie, ucbCookie, steamID );
  773. switch ( eResult )
  774. {
  775. case k_EBeginAuthSessionResultOK:
  776. //Msg("S3: BeginAuthSession request for %x was good.\n", steamID.ConvertToUint64( ) );
  777. break;
  778. case k_EBeginAuthSessionResultInvalidTicket:
  779. Msg("S3: Client connected with invalid ticket: UserID: %x\n", unUserID );
  780. return false;
  781. case k_EBeginAuthSessionResultDuplicateRequest:
  782. Msg("S3: Duplicate client connection: UserID: %x SteamID %llx\n", unUserID, steamID.ConvertToUint64( ) );
  783. return false;
  784. case k_EBeginAuthSessionResultInvalidVersion:
  785. Msg("S3: Client connected with invalid ticket ( old version ): UserID: %x\n", unUserID );
  786. return false;
  787. case k_EBeginAuthSessionResultGameMismatch:
  788. // This error would be very useful to present to the client.
  789. Msg("S3: Client connected with ticket for the wrong game: UserID: %x\n", unUserID );
  790. return false;
  791. case k_EBeginAuthSessionResultExpiredTicket:
  792. Msg("S3: Client connected with expired ticket: UserID: %x\n", unUserID );
  793. return false;
  794. }
  795. extern ConVar sv_mmqueue_reservation;
  796. if ( steamID.IsValid() && sv_mmqueue_reservation.GetString()[0] == 'Q' && ( client->GetServer() == &sv ) )
  797. {
  798. // For queue reservation client ID must match
  799. static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL );
  800. bool bQMMplayer = !!strstr( sv_mmqueue_reservation.GetString(), CFmtStr( "[%x]", steamID.GetAccountID() ) );
  801. bool bQMMcaster = ( !bQMMplayer && s_pchTournamentServer && !!strstr( sv_mmqueue_reservation.GetString(), CFmtStr( "{%x}", steamID.GetAccountID() ) ) );
  802. if ( !bQMMplayer && !bQMMcaster )
  803. {
  804. Msg( "Q: Client connected with forged reservation ticket: userid %x, steamid: %llx\n", unUserID, steamID.ConvertToUint64() );
  805. return false;
  806. }
  807. }
  808. if ( steamID.IsValid() && steamID.BIndividualAccount() )
  809. {
  810. uint32 uiAccountIdSupplied = Q_atoi( client->GetUserSetting( "accountid" ) );
  811. if ( uiAccountIdSupplied && uiAccountIdSupplied != steamID.GetAccountID() )
  812. {
  813. Msg( "SteamID.AccountID: Client connected with forged accountid: userid %x, steamid: %llx, accountid: %u\n", unUserID, steamID.ConvertToUint64(), uiAccountIdSupplied );
  814. return false;
  815. }
  816. if ( adr.GetAddressType() == NSAT_PROXIED_CLIENT )
  817. {
  818. if ( adr.m_steamID.GetSteamID().GetAccountID() != steamID.GetAccountID() )
  819. {
  820. Msg( "SteamID.AccountID: Client connected with forged accountid: userid %x, steamid: %llx, SDR: %llx\n", unUserID, steamID.ConvertToUint64(), adr.m_steamID.GetSteamID().ConvertToUint64() );
  821. return false;
  822. }
  823. }
  824. }
  825. // first checks ok, we know now the SteamID
  826. client->SetSteamID( steamID );
  827. if ( steamID.IsValid() && steamID.BIndividualAccount() && ( adr.GetAddressType() == NSAT_PROXIED_CLIENT ) )
  828. client->SetFullyAuthenticated(); // always flag SDR clients as fully authenticated
  829. SendUpdatedServerDetails();
  830. return true;
  831. }
  832. bool CSteam3Server::NotifyLocalClientConnect( CBaseClient *client )
  833. {
  834. CSteamID steamID;
  835. extern bool CanShowHostTvStatus();
  836. bool bClientIsTV = client->IsHLTV() || ( client->IsFakeClient() && !Q_strcmp( client->GetClientName(), "GOTV" ) );
  837. if ( SteamGameServer() && ( CanShowHostTvStatus() || !bClientIsTV ) )
  838. {
  839. steamID = SteamGameServer()->CreateUnauthenticatedUserConnection();
  840. }
  841. client->SetSteamID( steamID );
  842. SendUpdatedServerDetails();
  843. return true;
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose:
  847. // Input : *client -
  848. //-----------------------------------------------------------------------------
  849. void CSteam3Server::NotifyClientDisconnect( CBaseClient *client )
  850. {
  851. if ( !client || !BIsActive() || !client->IsConnected() || !client->m_SteamID.IsValid() )
  852. return;
  853. // Check if the client has a local (anonymous) steam account. This is the
  854. // case for bots. Currently it's also the case for people who connect
  855. // directly to the SourceTV port.
  856. if ( client->m_SteamID.GetEAccountType() == k_EAccountTypeAnonGameServer )
  857. {
  858. extern bool CanShowHostTvStatus();
  859. if ( !client->IsHLTV() || CanShowHostTvStatus() )
  860. {
  861. SteamGameServer()->SendUserDisconnect( client->m_SteamID );
  862. }
  863. // Clear the steam ID, as it was a dummy one that should not be used again
  864. client->m_SteamID = CSteamID();
  865. }
  866. else
  867. {
  868. // All bots should have an anonymous account ID
  869. Assert( !client->IsFakeClient() );
  870. USERID_t id = client->GetNetworkID();
  871. if ( id.idtype != IDTYPE_STEAM )
  872. return;
  873. // !FIXME! Use ticket auth here and call EndAuthSession
  874. // Msg("S3: Sending client disconnect for %x\n", steamIDClient.ConvertToUint64( ) );
  875. SteamGameServer()->EndAuthSession( client->m_SteamID );
  876. }
  877. }
  878. //-----------------------------------------------------------------------------
  879. // Purpose:
  880. //-----------------------------------------------------------------------------
  881. void CSteam3Server::NotifyOfLevelChange()
  882. {
  883. // we're changing levels, so we may not respond for a while
  884. if ( m_bHasActivePlayers )
  885. {
  886. m_bHasActivePlayers = false;
  887. SendUpdatedServerDetails();
  888. }
  889. }
  890. //-----------------------------------------------------------------------------
  891. // Purpose:
  892. //-----------------------------------------------------------------------------
  893. void CSteam3Server::NotifyOfServerNameChange()
  894. {
  895. SendUpdatedServerDetails();
  896. }
  897. //-----------------------------------------------------------------------------
  898. // Purpose:
  899. //-----------------------------------------------------------------------------
  900. void CSteam3Server::RunFrame()
  901. {
  902. bool bHasPlayers = ( sv.GetNumClients() > 0 );
  903. if ( m_bHasActivePlayers != bHasPlayers )
  904. {
  905. m_bHasActivePlayers = bHasPlayers;
  906. SendUpdatedServerDetails();
  907. }
  908. static double s_fLastRunCallback = 0.0f;
  909. double fCurtime = Plat_FloatTime();
  910. if ( fCurtime - s_fLastRunCallback > 0.1f )
  911. {
  912. s_fLastRunCallback = fCurtime;
  913. #ifndef NO_STEAM
  914. SteamGameServer_RunCallbacks();
  915. #endif
  916. }
  917. }
  918. //-----------------------------------------------------------------------------
  919. // Purpose: lets the steam3 servers know our full details
  920. // Input : bChangingLevels - true if we're going to heartbeat slowly for a while
  921. //-----------------------------------------------------------------------------
  922. void CSteam3Server::SendUpdatedServerDetails()
  923. {
  924. if ( !BIsActive() || SteamGameServer() == NULL )
  925. return;
  926. // Check if we are running with a special flag and not advertise to Steam
  927. KeyValues* nextNextSubkey = g_pLaunchOptions->GetFirstSubKey()->GetNextKey()->GetNextKey();
  928. if ( nextNextSubkey && !V_strcmp( nextNextSubkey->GetString(), "server_is_unavailable" ) )
  929. return;
  930. int nHumans;
  931. int nMaxHumans;
  932. int nBots;
  933. sv.GetMasterServerPlayerCounts( nHumans, nMaxHumans, nBots );
  934. SteamGameServer()->SetBotPlayerCount( nBots );
  935. SteamGameServer()->SetMaxPlayerCount( nMaxHumans );
  936. SteamGameServer()->SetPasswordProtected( sv.GetPassword() != NULL );
  937. SteamGameServer()->SetRegion( sv_region.GetString() );
  938. SteamGameServer()->SetServerName( sv.GetName() );
  939. char const *pszMapName = NULL;
  940. CHltvServerIterator hltv;
  941. if ( hltv && hltv->IsTVRelay() )
  942. {
  943. pszMapName = hltv->GetMapName();
  944. }
  945. else
  946. {
  947. pszMapName = sv.GetMapName();
  948. }
  949. // Drop the workshop prefix and fixup our official maps for server browser
  950. if ( char const *szStringAfterPrefix = StringAfterPrefix( pszMapName, "workshop" ) )
  951. {
  952. if ( ( szStringAfterPrefix[0] == '/' ) || ( szStringAfterPrefix[0] == '\\' ) )
  953. {
  954. pszMapName = szStringAfterPrefix;
  955. if ( ( Q_strlen( szStringAfterPrefix ) > 11 ) &&
  956. ( ( szStringAfterPrefix[10] == '/' ) || ( szStringAfterPrefix[10] == '\\' ) ) )
  957. {
  958. bool bOfficialMap = false;
  959. /** Removed for partner depot **/
  960. if ( bOfficialMap )
  961. {
  962. size_t nOfficalLen = Q_strlen( pszMapName ) + 1;
  963. char *pchOfficialName = ( char * ) stackalloc( nOfficalLen );
  964. Q_snprintf( pchOfficialName, nOfficalLen, "//official/%s", szStringAfterPrefix + 11 );
  965. pszMapName = pchOfficialName;
  966. }
  967. }
  968. }
  969. }
  970. SteamGameServer()->SetMapName( pszMapName );
  971. if ( hltv != NULL )
  972. {
  973. SteamGameServer()->SetSpectatorPort( NET_GetUDPPort( NS_HLTV + hltv->GetInstanceIndex() ) );
  974. SteamGameServer()->SetSpectatorServerName( hltv->GetName() );
  975. }
  976. else
  977. {
  978. SteamGameServer()->SetSpectatorPort( 0 );
  979. }
  980. // Msg( "CSteam3Server::SendUpdatedServerDetails: nNumClients=%d, nMaxClients=%d, nFakeClients=%d:\n", nNumClients, nMaxClients, nFakeClients );
  981. // for ( int i = 0 ; i < sv.GetClientCount() ; ++i )
  982. // {
  983. // IClient *c = sv.GetClient( i );
  984. // Msg(" %d: %s, connected=%d, replay=%d, fake=%d\n", i, c->GetClientName(), c->IsConnected() ? 1 : 0, c->IsReplay() ? 1 : 0, c->IsFakeClient() ? 1 : 0 );
  985. // }
  986. if ( serverGameDLL && GetGSSteamID().IsValid() )
  987. serverGameDLL->UpdateGCInformation();
  988. }
  989. bool CSteam3Server::IsMasterServerUpdaterSharingGameSocket()
  990. {
  991. return m_bMasterServerUpdaterSharingGameSocket;
  992. }
  993. //-----------------------------------------------------------------------------
  994. // Purpose: Select Steam gameserver account to login to
  995. //-----------------------------------------------------------------------------
  996. void sv_setsteamaccount_f( const CCommand &args )
  997. {
  998. if ( Steam3Server( ).SteamGameServer( ) && Steam3Server( ).SteamGameServer( )->BLoggedOn( ) )
  999. {
  1000. Warning( "Warning: Game server already logged into steam. You need to use the sv_setsteamaccount command earlier.\n" );
  1001. return;
  1002. }
  1003. if ( args.ArgC( ) != 2 )
  1004. {
  1005. Warning( "Usage: sv_setsteamaccount <login_token>\n" );
  1006. return;
  1007. }
  1008. Steam3Server( ).SetAccount( args[ 1 ] );
  1009. }
  1010. static ConCommand sv_setsteamaccount( "sv_setsteamaccount", sv_setsteamaccount_f, "token\nSet game server account token to use for logging in to a persistent game server account", 0 );