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.

641 lines
20 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: implementation of the rcon server
  4. //
  5. //===========================================================================//
  6. #if defined(_WIN32)
  7. #if !defined(_X360)
  8. #include <winsock.h>
  9. #endif
  10. #undef SetPort // winsock screws with the SetPort string... *sigh*
  11. #define MSG_NOSIGNAL 0
  12. #elif defined( _PS3 )
  13. #include "net_ws_headers.h"
  14. #define MSG_NOSIGNAL 0
  15. #elif POSIX
  16. #include <sys/types.h>
  17. #include <sys/socket.h>
  18. #include <netinet/in.h>
  19. #include <netinet/tcp.h>
  20. #include <errno.h>
  21. #include <sys/ioctl.h>
  22. #define closesocket close
  23. #define WSAGetLastError() errno
  24. #define ioctlsocket ioctl
  25. #ifdef OSX
  26. #define MSG_NOSIGNAL 0
  27. #endif
  28. #endif
  29. #include <tier0/dbg.h>
  30. #include "utlbuffer.h"
  31. #include "server.h"
  32. #include "sv_rcon.h"
  33. #include "proto_oob.h" // PORT_RCON define
  34. #include "sv_remoteaccess.h"
  35. #include "cl_rcon.h"
  36. #if defined( _X360 )
  37. #include "xbox/xbox_win32stubs.h"
  38. #endif
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. #ifdef ENABLE_RPT
  42. class CRPTServer : public CRConServer
  43. {
  44. typedef CRConServer BaseClass;
  45. public:
  46. virtual void OnSocketAccepted( SocketHandle_t hSocket, const netadr_t & netAdr, void** ppData )
  47. {
  48. BaseClass::OnSocketAccepted( hSocket, netAdr, ppData );
  49. // Enable cheats on this client only
  50. Cmd_SetRptActive( true );
  51. }
  52. virtual void OnSocketClosed( SocketHandle_t hSocket, const netadr_t & netAdr, void* pData )
  53. {
  54. Cmd_SetRptActive( false );
  55. BaseClass::OnSocketClosed( hSocket, netAdr, pData );
  56. }
  57. };
  58. static CRPTServer g_RPTServer;
  59. CRConServer & RPTServer()
  60. {
  61. return g_RPTServer;
  62. }
  63. #endif // ENABLE_RPT
  64. static CRConServer g_RCONServer;
  65. CRConServer & RCONServer()
  66. {
  67. return g_RCONServer;
  68. }
  69. static void RconPasswordChanged_f( IConVar *pConVar, const char *pOldString, float flOldValue )
  70. {
  71. ConVarRef var( pConVar );
  72. const char *pPassword = var.GetString();
  73. #ifndef DEDICATED
  74. RCONClient().SetPassword( pPassword );
  75. #endif
  76. RCONServer().SetPassword( pPassword );
  77. }
  78. ConVar rcon_password ( "rcon_password", "", FCVAR_SERVER_CANNOT_QUERY|FCVAR_DONTRECORD|FCVAR_RELEASE, "remote console password.", RconPasswordChanged_f );
  79. //-----------------------------------------------------------------------------
  80. // Purpose: Constructor
  81. //-----------------------------------------------------------------------------
  82. #pragma warning ( disable : 4355 )
  83. CRConServer::CRConServer() : m_Socket( this )
  84. {
  85. }
  86. CRConServer::CRConServer( const char *pNetAddress ) : m_Socket( this )
  87. {
  88. SetAddress( pNetAddress );
  89. }
  90. #pragma warning ( default : 4355 )
  91. //-----------------------------------------------------------------------------
  92. // Purpose: Destructor
  93. //-----------------------------------------------------------------------------
  94. CRConServer::~CRConServer()
  95. {
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Allows a server to request a listening client to connect to it
  99. //-----------------------------------------------------------------------------
  100. bool CRConServer::ConnectToListeningClient( const netadr_t &adr, bool bSingleSocket )
  101. {
  102. if ( m_Socket.ConnectSocket( adr, bSingleSocket ) < 0 )
  103. {
  104. // This should probably go into its own channel, but for now send it where it was already going (the "console" channel).
  105. LoggingChannelID_t consoleChannel = LoggingSystem_FindChannel( "Console" );
  106. Log_Warning( consoleChannel, "Unable to connect to remote client (%s)\n", adr.ToString() );
  107. return false;
  108. }
  109. return true;
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose: returns true if the listening socket is created and listening
  113. //-----------------------------------------------------------------------------
  114. bool CRConServer::IsConnected()
  115. {
  116. return m_Socket.IsListening();
  117. }
  118. void CRConServer::SetPassword( const char *pPassword )
  119. {
  120. m_Socket.CloseAllAcceptedSockets();
  121. m_Password = pPassword;
  122. }
  123. bool CRConServer::HasPassword() const
  124. {
  125. return !m_Password.IsEmpty();
  126. }
  127. bool CRConServer::IsPassword( const char *pPassword ) const
  128. {
  129. // Must have a password set to allow any rconning.
  130. if ( !HasPassword() )
  131. return false;
  132. // If the pw does not match, then not authed
  133. return ( Q_strcmp( pPassword, m_Password.Get() ) == 0 );
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose: Set the address to bind to
  137. //-----------------------------------------------------------------------------
  138. void CRConServer::SetAddress( const char *pNetAddress )
  139. {
  140. NET_StringToAdr( pNetAddress, &m_Address );
  141. if ( m_Address.GetPort() == 0 )
  142. {
  143. m_Address.SetPort( PORT_RCON );
  144. }
  145. }
  146. bool CRConServer::CreateSocket()
  147. {
  148. return m_Socket.CreateListenSocket( m_Address );
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Inherited from ISocketCreatorListener
  152. //-----------------------------------------------------------------------------
  153. bool CRConServer::ShouldAcceptSocket( SocketHandle_t hSocket, const netadr_t & netAdr )
  154. {
  155. return true;
  156. }
  157. void CRConServer::OnSocketAccepted( SocketHandle_t hSocket, const netadr_t &netAdr, void** ppData )
  158. {
  159. ConnectedRConSocket_t *pNewSocket = new ConnectedRConSocket_t;
  160. pNewSocket->lastRequestID = 0;
  161. pNewSocket->authed = false;
  162. pNewSocket->listenerID = g_ServerRemoteAccess.GetNextListenerID( true, &netAdr );
  163. *ppData = pNewSocket;
  164. }
  165. void CRConServer::OnSocketClosed( SocketHandle_t hSocket, const netadr_t &netAdr, void* pData )
  166. {
  167. m_bSocketDeleted = true;
  168. ConnectedRConSocket_t *pOldSocket = (ConnectedRConSocket_t*)( pData );
  169. delete pOldSocket;
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: accept new connections and walk open sockets and handle any incoming data
  173. //-----------------------------------------------------------------------------
  174. void CRConServer::RunFrame()
  175. {
  176. m_Socket.RunFrame();
  177. m_bSocketDeleted = false;
  178. // handle incoming data
  179. // NOTE: Have to iterate in reverse since we may be killing sockets
  180. int nCount = m_Socket.GetAcceptedSocketCount();
  181. for ( int i = nCount - 1; i >= 0; --i )
  182. {
  183. // process any outgoing data for this socket
  184. ConnectedRConSocket_t *pData = GetSocketData( i );
  185. SocketHandle_t hSocket = m_Socket.GetAcceptedSocketHandle( i );
  186. const netadr_t& socketAdr = m_Socket.GetAcceptedSocketAddress( i );
  187. while ( pData->m_OutstandingSends.Count() > 0 )
  188. {
  189. CUtlBuffer &packet = pData->m_OutstandingSends[ pData->m_OutstandingSends.Head()];
  190. bool bSent = SendRCONResponse( i, packet.PeekGet(), packet.TellPut() - packet.TellGet(), true );
  191. if ( bSent ) // all this packet was sent, remove it
  192. {
  193. pData->m_OutstandingSends.Remove( pData->m_OutstandingSends.Head() ); // delete this entry no matter what, SendRCONResponse() will re-queue if needed
  194. }
  195. else // must have blocked part way through, SendRCONResponse
  196. // fixed up the queued entry
  197. {
  198. break;
  199. }
  200. }
  201. int sendLen = g_ServerRemoteAccess.GetDataResponseSize( pData->listenerID );
  202. if ( sendLen > 0 )
  203. {
  204. char sendBuf[4096];
  205. char *pBuf = sendBuf;
  206. bool bAllocate = ( sendLen + sizeof(int) > sizeof(sendBuf) );
  207. if ( bAllocate )
  208. {
  209. pBuf = new char[sendLen + sizeof(int)];
  210. }
  211. memcpy( pBuf, &sendLen, sizeof(sendLen) ); // copy the size of the packet in
  212. g_ServerRemoteAccess.ReadDataResponse( pData->listenerID, pBuf + sizeof(int), sendLen );
  213. SendRCONResponse( i, pBuf, sendLen + sizeof(int) );
  214. if ( bAllocate )
  215. {
  216. delete [] pBuf;
  217. }
  218. }
  219. // check for incoming data
  220. int pendingLen = 0;
  221. unsigned long readLen = 0;
  222. char ch;
  223. pendingLen = recv( hSocket, &ch, sizeof(ch), MSG_PEEK );
  224. if ( pendingLen == -1 && SocketWouldBlock() )
  225. continue;
  226. if ( pendingLen == 0 )
  227. {
  228. m_Socket.CloseAcceptedSocket( i );
  229. continue;
  230. }
  231. if ( pendingLen < 0 )
  232. {
  233. //DevMsg( "RCON Cmd: peek error %s\n", NET_ErrorString(WSAGetLastError()));
  234. m_Socket.CloseAcceptedSocket( i );
  235. continue;
  236. }
  237. // find out how much we have to read
  238. #ifdef _PS3
  239. ExecuteNTimes( 5, Warning( "PS3 implementation of SV_RCON is disabled!\n" ) );
  240. readLen = 0;
  241. #else
  242. ioctlsocket( hSocket, FIONREAD, &readLen );
  243. #endif
  244. if ( readLen > sizeof(int) ) // we have a command to process
  245. {
  246. CUtlBuffer & response = pData->packetbuffer;
  247. response.EnsureCapacity( response.TellPut() + readLen );
  248. char *recvBuf = (char *)stackalloc( MIN( 1024, readLen ) ); // a buffer used for recv()
  249. unsigned int len = 0;
  250. while ( len < readLen )
  251. {
  252. int recvLen = recv( hSocket, recvBuf , MIN(1024, readLen - len) , 0 );
  253. if ( recvLen == 0 ) // socket was closed
  254. {
  255. m_Socket.CloseAcceptedSocket( i );
  256. break;
  257. }
  258. if ( recvLen < 0 && !SocketWouldBlock() )
  259. {
  260. Warning( "RCON Cmd: recv error (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  261. break;
  262. }
  263. response.Put( recvBuf, recvLen );
  264. len += recvLen;
  265. }
  266. response.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  267. int size = response.GetInt();
  268. if ( size > RCON_MAX_RCON_COMMAND_LEN )
  269. {
  270. HandleFailedRconAuth( socketAdr );
  271. m_Socket.CloseAcceptedSocket( i );
  272. continue;
  273. }
  274. while ( size > 0 && size <= response.TellPut() - response.TellGet() )
  275. {
  276. SV_RedirectStart( RD_SOCKET, &socketAdr );
  277. g_ServerRemoteAccess.WriteDataRequest( this, pData->listenerID, response.PeekGet(), size );
  278. SV_RedirectEnd();
  279. if ( m_bSocketDeleted )
  280. return;
  281. response.SeekGet( CUtlBuffer::SEEK_CURRENT, size ); // eat up the buffer we just sent
  282. if ( response.TellPut() - response.TellGet() >= sizeof(int) )
  283. {
  284. size = response.GetInt(); // read how much is in this packet
  285. }
  286. else
  287. {
  288. size = 0; // finished the packet
  289. }
  290. }
  291. // Check and see if socket was closed as a result of processing - this can happen if the user has entered too many passwords
  292. int nNewCount = m_Socket.GetAcceptedSocketCount();
  293. if ( 0 == nNewCount || i > nNewCount || pData != GetSocketData( i ) )
  294. {
  295. response.Purge();
  296. break;
  297. }
  298. if ( size > 0 || (response.TellPut() - response.TellGet() > 0))
  299. {
  300. CUtlBuffer tmpBuf;
  301. if ( response.TellPut() - response.TellGet() > 0 )
  302. {
  303. tmpBuf.Put( response.PeekGet(), response.TellPut() - response.TellGet() );
  304. }
  305. response.Purge();
  306. if ( size > 0 )
  307. {
  308. response.Put( &size, sizeof(size));
  309. }
  310. if ( tmpBuf.TellPut() > 0 )
  311. {
  312. response.Put( tmpBuf.Base(), tmpBuf.TellPut() );
  313. }
  314. }
  315. else
  316. {
  317. response.Purge();
  318. }
  319. }
  320. } // for each socket
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: flush the response of a network command back to a user
  324. //-----------------------------------------------------------------------------
  325. void CRConServer::FinishRedirect( const char *msg, const netadr_t &adr )
  326. {
  327. // NOTE: Has to iterate in reverse; SendRCONResponse can close sockets
  328. int nCount = m_Socket.GetAcceptedSocketCount();
  329. for ( int i = nCount - 1; i >= 0; --i )
  330. {
  331. const netadr_t& socketAdr = m_Socket.GetAcceptedSocketAddress( i );
  332. if ( !adr.CompareAdr( socketAdr ) )
  333. continue;
  334. CUtlBuffer response;
  335. // build the response
  336. ConnectedRConSocket_t *pSocketData = GetSocketData( i );
  337. response.PutInt(0); // the size, this gets set once we make the packet
  338. response.PutInt(pSocketData->lastRequestID);
  339. response.PutInt(SERVERDATA_RESPONSE_VALUE);
  340. response.PutString(msg);
  341. response.PutString("");
  342. int size = response.TellPut() - sizeof(int);
  343. response.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  344. response.PutInt(size); // the size
  345. response.SeekPut( CUtlBuffer::SEEK_CURRENT, size );
  346. // OutputDebugString( va("RCON: String is %i long\n", Q_strlen(msg)) ); // can't use DevMsg(), we are potentially inside the RedirectFlush() function
  347. // printf("RCON: String is %i long, packet size %i\n", Q_strlen(msg), size );
  348. SendRCONResponse( i, response.Base(), response.TellPut() );
  349. }
  350. }
  351. //-----------------------------------------------------------------------------
  352. // Purpose: set the current outstanding request ID for this connection, used by the redirect flush above
  353. //-----------------------------------------------------------------------------
  354. void CRConServer::SetRequestID( ra_listener_id listener, int iRequestID )
  355. {
  356. int nCount = m_Socket.GetAcceptedSocketCount();
  357. for ( int i = 0; i < nCount; i++ )
  358. {
  359. ConnectedRConSocket_t *pSocketData = GetSocketData( i );
  360. if ( pSocketData->listenerID == listener)
  361. {
  362. pSocketData->lastRequestID = iRequestID;
  363. }
  364. }
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose: send a buffer to a particular connection
  368. //-----------------------------------------------------------------------------
  369. bool CRConServer::SendRCONResponse( int nIndex, const void *data, int len, bool fromQueue )
  370. {
  371. SocketHandle_t hSocket = m_Socket.GetAcceptedSocketHandle( nIndex );
  372. if ( hSocket < 0 )
  373. return false;
  374. ConnectedRConSocket_t *pSocketData = GetSocketData( nIndex );
  375. // if we already have queued data pending then just add this to the end
  376. // of the queue
  377. if ( !fromQueue && pSocketData->m_OutstandingSends.Count() > 0 )
  378. {
  379. if ( pSocketData->m_OutstandingSends.Count() > RCON_MAX_OUTSTANDING_SENDS )
  380. {
  381. m_Socket.CloseAcceptedSocket( nIndex );
  382. return false;
  383. }
  384. int index = pSocketData->m_OutstandingSends.AddToTail();
  385. pSocketData->m_OutstandingSends[index].Put( data, len );
  386. return true;
  387. }
  388. Assert( !( fromQueue && data != (pSocketData->m_OutstandingSends[pSocketData->m_OutstandingSends.Head()].Base())));
  389. int sendLen = 0;
  390. #if defined(OSX)
  391. int true_val = 1;
  392. setsockopt( hSocket, SOL_SOCKET, SO_NOSIGPIPE, &true_val, sizeof(true_val));
  393. #endif
  394. while ( sendLen < len )
  395. {
  396. #if defined(OSX)
  397. int ret = send( hSocket, (const char *)data + sendLen, len - sendLen, 0 );
  398. #else
  399. int ret = send( hSocket, (const char *)data + sendLen, len - sendLen, MSG_NOSIGNAL );
  400. #endif
  401. if ( ret == -1 )
  402. {
  403. // can't finish sending this right now, push it back
  404. // on the TOP of the queue to be sent next time around
  405. if ( !SocketWouldBlock() )
  406. {
  407. m_Socket.CloseAcceptedSocket( nIndex );
  408. return false;
  409. }
  410. if ( !fromQueue ) // we don't have an entry for this
  411. // yet, add a new one
  412. {
  413. int index = pSocketData->m_OutstandingSends.AddToHead();
  414. pSocketData->m_OutstandingSends[index].Put( (void *)((char *)data + sendLen), len - sendLen );
  415. }
  416. else // update the existing queued item to show we
  417. // sent some of it (we only ever send the head of the list)
  418. {
  419. pSocketData->m_OutstandingSends[pSocketData->m_OutstandingSends.Head()].SeekGet( CUtlBuffer::SEEK_CURRENT, sendLen );
  420. }
  421. return false;
  422. }
  423. else if ( ret > 0 )
  424. {
  425. sendLen += ret;
  426. }
  427. }
  428. // printf("RCON: Sending packet %i in len\n", len);
  429. // OutputDebugString( va("RCON: Sending packet %i in len\n", len) ); // can't use DevMsg(), we are potentially inside the RedirectFlush() function
  430. return true;
  431. }
  432. ConVar sv_rcon_banpenalty( "sv_rcon_banpenalty", "0", 0, "Number of minutes to ban users who fail rcon authentication", true, 0, false, 0 );
  433. ConVar sv_rcon_maxfailures( "sv_rcon_maxfailures", "10", 0, "Max number of times a user can fail rcon authentication before being banned", true, 1, true, 20 );
  434. ConVar sv_rcon_minfailures( "sv_rcon_minfailures", "5", 0, "Number of times a user can fail rcon authentication in sv_rcon_minfailuretime before being banned", true, 1, true, 20 );
  435. ConVar sv_rcon_minfailuretime( "sv_rcon_minfailuretime", "30", 0, "Number of seconds to track failed rcon authentications", true, 1, false, 0 );
  436. ConVar sv_rcon_whitelist_address( "sv_rcon_whitelist_address", "", FCVAR_RELEASE, "When set, rcon failed authentications will never ban this address, e.g. '127.0.0.1'" );
  437. //-----------------------------------------------------------------------------
  438. // Purpose: compares failed rcons based on most recent failure time
  439. //-----------------------------------------------------------------------------
  440. bool CRConServer::FailedRCon_t::operator<(const struct CRConServer::FailedRCon_t &rhs) const
  441. {
  442. int myTime = 0;
  443. int rhsTime = 0;
  444. if ( badPasswordTimes.Count() )
  445. myTime = badPasswordTimes[ badPasswordTimes.Count() - 1 ];
  446. if ( rhs.badPasswordTimes.Count() )
  447. rhsTime = rhs.badPasswordTimes[ rhs.badPasswordTimes.Count() - 1 ];
  448. return myTime < rhsTime;
  449. }
  450. //-----------------------------------------------------------------------------
  451. // Purpose: tracks failed rcon attempts and bans repeat offenders
  452. //-----------------------------------------------------------------------------
  453. bool CRConServer::HandleFailedRconAuth( const netadr_t & adr )
  454. {
  455. if ( sv_rcon_whitelist_address.GetString()[0] )
  456. {
  457. if ( !V_strcmp( adr.ToString( true ), sv_rcon_whitelist_address.GetString() ) )
  458. {
  459. ConMsg( "Rcon auth failed from rcon whitelist address %s\n", adr.ToString() );
  460. return false;
  461. }
  462. }
  463. int i;
  464. FailedRCon_t *failedRcon = NULL;
  465. int nCount = m_failedRcons.Count();
  466. for ( i=0; i < nCount; ++i )
  467. {
  468. if ( adr.CompareAdr( m_failedRcons[i].adr, true ) )
  469. {
  470. failedRcon = &m_failedRcons[i];
  471. break;
  472. }
  473. }
  474. if ( !failedRcon )
  475. {
  476. // remove an old rcon if necessary
  477. if ( nCount >= 32 )
  478. {
  479. // look for the one with the oldest failure
  480. int indexToRemove = -1;
  481. for ( i=0; i < nCount; ++i )
  482. {
  483. if ( indexToRemove < 0 || m_failedRcons[i] < m_failedRcons[indexToRemove] )
  484. {
  485. indexToRemove = i;
  486. }
  487. }
  488. if ( indexToRemove >= 0 )
  489. {
  490. m_failedRcons.Remove( indexToRemove );
  491. }
  492. }
  493. // add the new rcon
  494. int index = m_failedRcons.AddToTail();
  495. failedRcon = &m_failedRcons[index];
  496. failedRcon->adr = adr;
  497. failedRcon->badPasswordCount = 0;
  498. failedRcon->badPasswordTimes.RemoveAll();
  499. }
  500. // update this failed rcon
  501. ++failedRcon->badPasswordCount;
  502. failedRcon->badPasswordTimes.AddToTail( sv.GetTime() );
  503. // remove old failure times (sv_rcon_maxfailures is limited to 20, so we won't be hurting anything by pruning)
  504. while ( failedRcon->badPasswordTimes.Count() > 20 )
  505. {
  506. failedRcon->badPasswordTimes.Remove( 0 );
  507. }
  508. // sanity-check the rcon banning cvars
  509. if ( sv_rcon_maxfailures.GetInt() < sv_rcon_minfailures.GetInt() )
  510. {
  511. int temp = sv_rcon_maxfailures.GetInt();
  512. sv_rcon_maxfailures.SetValue( sv_rcon_minfailures.GetInt() );
  513. sv_rcon_minfailures.SetValue( temp );
  514. }
  515. // ConMsg( "%d of %d bad password times tracked\n", failedRcon->badPasswordTimes.Count(), failedRcon->badPasswordCount );
  516. // ConMsg( "min=%d, max=%d, time=%.2f\n", sv_rcon_minfailures.GetInt(), sv_rcon_maxfailures.GetInt(), sv_rcon_minfailuretime.GetFloat() );
  517. // check if the user should be banned based on total failed attempts
  518. if ( failedRcon->badPasswordCount > sv_rcon_maxfailures.GetInt() )
  519. {
  520. ConMsg( "Banning %s for rcon hacking attempts\n", failedRcon->adr.ToString( true ) );
  521. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "addip %i %s\n", sv_rcon_banpenalty.GetInt(), failedRcon->adr.ToString( true ) ) );
  522. Cbuf_Execute();
  523. return true;
  524. }
  525. // check if the user should be banned based on recent failed attempts
  526. int recentFailures = 0;
  527. for ( i=failedRcon->badPasswordTimes.Count()-1; i>=0; --i )
  528. {
  529. if ( failedRcon->badPasswordTimes[i] + sv_rcon_minfailuretime.GetFloat() >= sv.GetTime() )
  530. {
  531. ++recentFailures;
  532. }
  533. }
  534. if ( recentFailures > sv_rcon_minfailures.GetInt() )
  535. {
  536. ConMsg( "Banning %s for rcon hacking attempts\n", failedRcon->adr.ToString( true ) );
  537. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "addip %i %s\n", sv_rcon_banpenalty.GetInt(), failedRcon->adr.ToString( true ) ) );
  538. Cbuf_Execute();
  539. return true;
  540. }
  541. return false;
  542. }
  543. bool CRConServer::BCloseAcceptedSocket( ra_listener_id listener )
  544. {
  545. int nCount = m_Socket.GetAcceptedSocketCount();
  546. for ( int i = 0; i < nCount; i++ )
  547. {
  548. ConnectedRConSocket_t *pSocketData = GetSocketData( i );
  549. if ( pSocketData->listenerID == listener )
  550. {
  551. m_Socket.CloseAcceptedSocket( i );
  552. return true;
  553. }
  554. }
  555. return false;
  556. }