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.
636 lines
19 KiB
636 lines
19 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: implementation of the rcon server |
|
// |
|
//===========================================================================// |
|
|
|
|
|
#if defined(_WIN32) |
|
#if !defined(_X360) |
|
#include <winsock.h> |
|
#endif |
|
#undef SetPort // winsock screws with the SetPort string... *sigh* |
|
#define socklen_t int |
|
#define MSG_NOSIGNAL 0 |
|
#elif POSIX |
|
#include <sys/types.h> |
|
#include <sys/socket.h> |
|
#include <netinet/in.h> |
|
#include <netinet/tcp.h> |
|
#include <errno.h> |
|
#include <sys/ioctl.h> |
|
#define closesocket close |
|
#define WSAGetLastError() errno |
|
#define ioctlsocket ioctl |
|
#ifdef OSX |
|
#define MSG_NOSIGNAL 0 |
|
#endif |
|
#endif |
|
#include <tier0/dbg.h> |
|
#include "utlbuffer.h" |
|
#include "server.h" |
|
#include "sv_rcon.h" |
|
#include "proto_oob.h" // PORT_RCON define |
|
#include "sv_remoteaccess.h" |
|
#include "cl_rcon.h" |
|
#include "sv_filter.h" |
|
|
|
#if defined( _X360 ) |
|
#include "xbox/xbox_win32stubs.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#ifdef ENABLE_RPT |
|
class CRPTServer : public CRConServer |
|
{ |
|
typedef CRConServer BaseClass; |
|
|
|
public: |
|
virtual void OnSocketAccepted( SocketHandle_t hSocket, const netadr_t & netAdr, void** ppData ) |
|
{ |
|
BaseClass::OnSocketAccepted( hSocket, netAdr, ppData ); |
|
|
|
// Enable cheats on this client only |
|
Cmd_SetRptActive( true ); |
|
} |
|
|
|
virtual void OnSocketClosed( SocketHandle_t hSocket, const netadr_t & netAdr, void* pData ) |
|
{ |
|
Cmd_SetRptActive( false ); |
|
BaseClass::OnSocketClosed( hSocket, netAdr, pData ); |
|
} |
|
}; |
|
|
|
|
|
static CRPTServer g_RPTServer; |
|
CRConServer & RPTServer() |
|
{ |
|
return g_RPTServer; |
|
} |
|
#endif // ENABLE_RPT |
|
|
|
static CRConServer g_RCONServer; |
|
CRConServer & RCONServer() |
|
{ |
|
return g_RCONServer; |
|
} |
|
|
|
static void RconPasswordChanged_f( IConVar *pConVar, const char *pOldString, float flOldValue ) |
|
{ |
|
ConVarRef var( pConVar ); |
|
const char *pPassword = var.GetString(); |
|
#ifndef SWDS |
|
RCONClient().SetPassword( pPassword ); |
|
#endif |
|
RCONServer().SetPassword( pPassword ); |
|
|
|
} |
|
ConVar rcon_password ( "rcon_password", "", FCVAR_SERVER_CANNOT_QUERY|FCVAR_DONTRECORD, "remote console password.", RconPasswordChanged_f ); |
|
|
|
ConVar sv_rcon_banpenalty( "sv_rcon_banpenalty", "0", 0, "Number of minutes to ban users who fail rcon authentication", true, 0, false, 0 ); |
|
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 ); |
|
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 ); |
|
ConVar sv_rcon_minfailuretime( "sv_rcon_minfailuretime", "30", 0, "Number of seconds to track failed rcon authentications", true, 1, false, 0 ); |
|
ConVar sv_rcon_whitelist_address( "sv_rcon_whitelist_address", "", 0, "When set, rcon failed authentications will never ban this address, e.g. '127.0.0.1'" ); |
|
|
|
ConVar sv_rcon_maxpacketsize( "sv_rcon_maxpacketsize", "1024", 0, "The maximum number of bytes to allow in a command packet", true, 0, false, 0 ); |
|
ConVar sv_rcon_maxpacketbans( "sv_rcon_maxpacketbans", "1", 0, "Ban IPs for sending RCON packets exceeding the value specified in sv_rcon_maxpacketsize", true, 0, true, 1 ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
#pragma warning ( disable : 4355 ) |
|
|
|
CRConServer::CRConServer() : m_Socket( this ) |
|
{ |
|
} |
|
|
|
CRConServer::CRConServer( const char *pNetAddress ) : m_Socket( this ) |
|
{ |
|
SetAddress( pNetAddress ); |
|
} |
|
|
|
#pragma warning ( default : 4355 ) |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor |
|
//----------------------------------------------------------------------------- |
|
CRConServer::~CRConServer() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Allows a server to request a listening client to connect to it |
|
//----------------------------------------------------------------------------- |
|
bool CRConServer::ConnectToListeningClient( const netadr_t &adr, bool bSingleSocket ) |
|
{ |
|
if ( m_Socket.ConnectSocket( adr, bSingleSocket ) < 0 ) |
|
{ |
|
ConWarning( "Unable to connect to remote client (%s)\n", adr.ToString() ); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: returns true if the listening socket is created and listening |
|
//----------------------------------------------------------------------------- |
|
bool CRConServer::IsConnected() |
|
{ |
|
return m_Socket.IsListening(); |
|
} |
|
|
|
void CRConServer::SetPassword( const char *pPassword ) |
|
{ |
|
m_Socket.CloseAllAcceptedSockets(); |
|
m_Password = pPassword; |
|
} |
|
|
|
bool CRConServer::HasPassword() const |
|
{ |
|
return !m_Password.IsEmpty(); |
|
} |
|
|
|
bool CRConServer::IsPassword( const char *pPassword ) const |
|
{ |
|
// Must have a password set to allow any rconning. |
|
if ( !HasPassword() ) |
|
return false; |
|
|
|
// If the pw does not match, then not authed |
|
return ( Q_strcmp( pPassword, m_Password.Get() ) == 0 ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set the address to bind to |
|
//----------------------------------------------------------------------------- |
|
void CRConServer::SetAddress( const char *pNetAddress ) |
|
{ |
|
NET_StringToAdr( pNetAddress, &m_Address ); |
|
if ( m_Address.GetPort() == 0 ) |
|
{ |
|
m_Address.SetPort( PORT_RCON ); |
|
} |
|
} |
|
|
|
bool CRConServer::CreateSocket() |
|
{ |
|
return m_Socket.CreateListenSocket( m_Address ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Inherited from ISocketCreatorListener |
|
//----------------------------------------------------------------------------- |
|
bool CRConServer::ShouldAcceptSocket( SocketHandle_t hSocket, const netadr_t & netAdr ) |
|
{ |
|
return !Filter_ShouldDiscard( netAdr ); |
|
} |
|
|
|
void CRConServer::OnSocketAccepted( SocketHandle_t hSocket, const netadr_t &netAdr, void** ppData ) |
|
{ |
|
ConnectedRConSocket_t *pNewSocket = new ConnectedRConSocket_t; |
|
pNewSocket->lastRequestID = 0; |
|
pNewSocket->authed = false; |
|
pNewSocket->listenerID = g_ServerRemoteAccess.GetNextListenerID( true, &netAdr ); |
|
*ppData = pNewSocket; |
|
} |
|
|
|
void CRConServer::OnSocketClosed( SocketHandle_t hSocket, const netadr_t &netAdr, void* pData ) |
|
{ |
|
m_bSocketDeleted = true; |
|
ConnectedRConSocket_t *pOldSocket = (ConnectedRConSocket_t*)( pData ); |
|
delete pOldSocket; |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: accept new connections and walk open sockets and handle any incoming data |
|
//----------------------------------------------------------------------------- |
|
void CRConServer::RunFrame() |
|
{ |
|
m_Socket.RunFrame(); |
|
m_bSocketDeleted = false; |
|
|
|
// handle incoming data |
|
// NOTE: Have to iterate in reverse since we may be killing sockets |
|
int nCount = m_Socket.GetAcceptedSocketCount(); |
|
for ( int i = nCount - 1; i >= 0; --i ) |
|
{ |
|
// process any outgoing data for this socket |
|
ConnectedRConSocket_t *pData = GetSocketData( i ); |
|
SocketHandle_t hSocket = m_Socket.GetAcceptedSocketHandle( i ); |
|
const netadr_t& socketAdr = m_Socket.GetAcceptedSocketAddress( i ); |
|
while ( pData->m_OutstandingSends.Count() > 0 ) |
|
{ |
|
CUtlBuffer &packet = pData->m_OutstandingSends[ pData->m_OutstandingSends.Head()]; |
|
bool bSent = SendRCONResponse( i, packet.PeekGet(), packet.TellPut() - packet.TellGet(), true ); |
|
if ( bSent ) // all this packet was sent, remove it |
|
{ |
|
pData->m_OutstandingSends.Remove( pData->m_OutstandingSends.Head() ); // delete this entry no matter what, SendRCONResponse() will re-queue if needed |
|
} |
|
else // must have blocked part way through, SendRCONResponse |
|
// fixed up the queued entry |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
int sendLen = g_ServerRemoteAccess.GetDataResponseSize( pData->listenerID ); |
|
if ( sendLen > 0 ) |
|
{ |
|
char sendBuf[4096]; |
|
char *pBuf = sendBuf; |
|
bool bAllocate = ( sendLen + sizeof(int) > sizeof(sendBuf) ); |
|
if ( bAllocate ) |
|
{ |
|
pBuf = new char[sendLen + sizeof(int)]; |
|
} |
|
memcpy( pBuf, &sendLen, sizeof(sendLen) ); // copy the size of the packet in |
|
g_ServerRemoteAccess.ReadDataResponse( pData->listenerID, pBuf + sizeof(int), sendLen ); |
|
SendRCONResponse( i, pBuf, sendLen + sizeof(int) ); |
|
if ( bAllocate ) |
|
{ |
|
delete [] pBuf; |
|
} |
|
} |
|
|
|
// check for incoming data |
|
int pendingLen = 0; |
|
unsigned long readLen = 0; |
|
char ch; |
|
pendingLen = recv( hSocket, &ch, sizeof(ch), MSG_PEEK ); |
|
if ( pendingLen == -1 && SocketWouldBlock() ) |
|
continue; |
|
|
|
if ( pendingLen == 0 ) |
|
{ |
|
m_Socket.CloseAcceptedSocket( i ); |
|
continue; |
|
} |
|
|
|
if ( pendingLen < 0 ) |
|
{ |
|
//DevMsg( "RCON Cmd: peek error %s\n", NET_ErrorString(WSAGetLastError())); |
|
m_Socket.CloseAcceptedSocket( i ); |
|
continue; |
|
} |
|
|
|
// find out how much we have to read |
|
ioctlsocket( hSocket, FIONREAD, &readLen ); |
|
if ( readLen > sizeof(int) ) // we have a command to process |
|
{ |
|
CUtlBuffer & response = pData->packetbuffer; |
|
response.EnsureCapacity( response.TellPut() + readLen ); |
|
char *recvBuf = (char *)_alloca( min( 1024ul, readLen ) ); // a buffer used for recv() |
|
unsigned int len = 0; |
|
while ( len < readLen ) |
|
{ |
|
int recvLen = recv( hSocket, recvBuf , min(1024ul, readLen - len) , 0 ); |
|
if ( recvLen == 0 ) // socket was closed |
|
{ |
|
m_Socket.CloseAcceptedSocket( i ); |
|
break; |
|
} |
|
|
|
if ( recvLen < 0 && !SocketWouldBlock() ) |
|
{ |
|
Warning( "RCON Cmd: recv error (%s)\n", NET_ErrorString( WSAGetLastError() ) ); |
|
break; |
|
} |
|
|
|
response.Put( recvBuf, recvLen ); |
|
len += recvLen; |
|
} |
|
|
|
response.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); |
|
|
|
int size = response.GetInt(); |
|
|
|
if ( sv_rcon_maxpacketsize.GetInt() > 0 && size > sv_rcon_maxpacketsize.GetInt() ) |
|
{ |
|
if ( sv_rcon_maxpacketbans.GetBool() ) |
|
{ |
|
HandleFailedRconAuth( socketAdr ); |
|
} |
|
|
|
m_Socket.CloseAcceptedSocket( i ); |
|
continue; |
|
} |
|
|
|
while ( size > 0 && size <= response.TellPut() - response.TellGet() ) |
|
{ |
|
SV_RedirectStart( RD_SOCKET, &socketAdr ); |
|
g_ServerRemoteAccess.WriteDataRequest( this, pData->listenerID, response.PeekGet(), size ); |
|
SV_RedirectEnd(); |
|
if ( m_bSocketDeleted ) |
|
return; |
|
response.SeekGet( CUtlBuffer::SEEK_CURRENT, size ); // eat up the buffer we just sent |
|
|
|
if ( response.TellPut() - response.TellGet() >= sizeof(int) ) |
|
{ |
|
size = response.GetInt(); // read how much is in this packet |
|
} |
|
else |
|
{ |
|
size = 0; // finished the packet |
|
} |
|
} |
|
|
|
// Check and see if socket was closed as a result of processing - this can happen if the user has entered too many passwords |
|
int nNewCount = m_Socket.GetAcceptedSocketCount(); |
|
if ( 0 == nNewCount || i > nNewCount || pData != GetSocketData( i ) ) |
|
{ |
|
response.Purge(); |
|
break; |
|
} |
|
|
|
if ( size > 0 || (response.TellPut() - response.TellGet() > 0)) |
|
{ |
|
// trim the bytes that were just processed |
|
CUtlBuffer tmpBuf; |
|
if ( response.TellPut() - response.TellGet() > 0 ) |
|
{ |
|
tmpBuf.Put( response.PeekGet(), response.TellPut() - response.TellGet() ); |
|
} |
|
|
|
response.Purge(); |
|
|
|
if ( size > 0 ) |
|
{ |
|
response.Put( &size, sizeof(size)); |
|
} |
|
|
|
if ( tmpBuf.TellPut() > 0 ) |
|
{ |
|
response.Put( tmpBuf.Base(), tmpBuf.TellPut() ); |
|
} |
|
} |
|
else |
|
{ |
|
response.Purge(); |
|
} |
|
} |
|
} // for each socket |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: flush the response of a network command back to a user |
|
//----------------------------------------------------------------------------- |
|
void CRConServer::FinishRedirect( const char *msg, const netadr_t &adr ) |
|
{ |
|
// NOTE: Has to iterate in reverse; SendRCONResponse can close sockets |
|
int nCount = m_Socket.GetAcceptedSocketCount(); |
|
for ( int i = nCount - 1; i >= 0; --i ) |
|
{ |
|
const netadr_t& socketAdr = m_Socket.GetAcceptedSocketAddress( i ); |
|
if ( !adr.CompareAdr( socketAdr ) ) |
|
continue; |
|
|
|
CUtlBuffer response; |
|
|
|
// build the response |
|
ConnectedRConSocket_t *pSocketData = GetSocketData( i ); |
|
response.PutInt(0); // the size, this gets set once we make the packet |
|
response.PutInt(pSocketData->lastRequestID); |
|
response.PutInt(SERVERDATA_RESPONSE_VALUE); |
|
response.PutString(msg); |
|
response.PutString(""); |
|
int size = response.TellPut() - sizeof(int); |
|
response.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); |
|
response.PutInt(size); // the size |
|
response.SeekPut( CUtlBuffer::SEEK_CURRENT, size ); |
|
|
|
|
|
// OutputDebugString( va("RCON: String is %i long\n", Q_strlen(msg)) ); // can't use DevMsg(), we are potentially inside the RedirectFlush() function |
|
// printf("RCON: String is %i long, packet size %i\n", Q_strlen(msg), size ); |
|
|
|
SendRCONResponse( i, response.Base(), response.TellPut() ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: set the current outstanding request ID for this connection, used by the redirect flush above |
|
//----------------------------------------------------------------------------- |
|
void CRConServer::SetRequestID( ra_listener_id listener, int iRequestID ) |
|
{ |
|
int nCount = m_Socket.GetAcceptedSocketCount(); |
|
for ( int i = 0; i < nCount; i++ ) |
|
{ |
|
ConnectedRConSocket_t *pSocketData = GetSocketData( i ); |
|
if ( pSocketData->listenerID == listener) |
|
{ |
|
pSocketData->lastRequestID = iRequestID; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: send a buffer to a particular connection |
|
//----------------------------------------------------------------------------- |
|
bool CRConServer::SendRCONResponse( int nIndex, const void *data, int len, bool fromQueue ) |
|
{ |
|
SocketHandle_t hSocket = m_Socket.GetAcceptedSocketHandle( nIndex ); |
|
if ( hSocket < 0 ) |
|
return false; |
|
|
|
ConnectedRConSocket_t *pSocketData = GetSocketData( nIndex ); |
|
|
|
// if we already have queued data pending then just add this to the end |
|
// of the queue |
|
if ( !fromQueue && pSocketData->m_OutstandingSends.Count() > 0 ) |
|
{ |
|
if ( pSocketData->m_OutstandingSends.Count() > RCON_MAX_OUTSTANDING_SENDS ) |
|
{ |
|
m_Socket.CloseAcceptedSocket( nIndex ); |
|
return false; |
|
} |
|
|
|
int index = pSocketData->m_OutstandingSends.AddToTail(); |
|
pSocketData->m_OutstandingSends[index].Put( data, len ); |
|
return true; |
|
} |
|
|
|
Assert( !( fromQueue && data != (pSocketData->m_OutstandingSends[pSocketData->m_OutstandingSends.Head()].Base()))); |
|
|
|
int sendLen = 0; |
|
while ( sendLen < len ) |
|
{ |
|
int ret = send( hSocket, (const char *)data + sendLen, len - sendLen, MSG_NOSIGNAL ); |
|
if ( ret == -1 ) |
|
{ |
|
// can't finish sending this right now, push it back |
|
// on the TOP of the queue to be sent next time around |
|
if ( !SocketWouldBlock() ) |
|
{ |
|
m_Socket.CloseAcceptedSocket( nIndex ); |
|
return false; |
|
} |
|
|
|
if ( !fromQueue ) // we don't have an entry for this |
|
// yet, add a new one |
|
{ |
|
int index = pSocketData->m_OutstandingSends.AddToHead(); |
|
pSocketData->m_OutstandingSends[index].Put( (void *)((char *)data + sendLen), len - sendLen ); |
|
} |
|
else // update the existing queued item to show we |
|
// sent some of it (we only ever send the head of the list) |
|
{ |
|
pSocketData->m_OutstandingSends[pSocketData->m_OutstandingSends.Head()].SeekGet( CUtlBuffer::SEEK_CURRENT, sendLen ); |
|
} |
|
return false; |
|
} |
|
else if ( ret > 0 ) |
|
{ |
|
sendLen += ret; |
|
} |
|
} |
|
// printf("RCON: Sending packet %i in len\n", len); |
|
// OutputDebugString( va("RCON: Sending packet %i in len\n", len) ); // can't use DevMsg(), we are potentially inside the RedirectFlush() function |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: compares failed rcons based on most recent failure time |
|
//----------------------------------------------------------------------------- |
|
bool CRConServer::FailedRCon_t::operator<(const struct CRConServer::FailedRCon_t &rhs) const |
|
{ |
|
int myTime = 0; |
|
int rhsTime = 0; |
|
|
|
if ( badPasswordTimes.Count() ) |
|
myTime = badPasswordTimes[ badPasswordTimes.Count() - 1 ]; |
|
|
|
if ( rhs.badPasswordTimes.Count() ) |
|
rhsTime = rhs.badPasswordTimes[ rhs.badPasswordTimes.Count() - 1 ]; |
|
|
|
return myTime < rhsTime; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: tracks failed rcon attempts and bans repeat offenders |
|
//----------------------------------------------------------------------------- |
|
bool CRConServer::HandleFailedRconAuth( const netadr_t & adr ) |
|
{ |
|
if ( sv_rcon_whitelist_address.GetString()[0] ) |
|
{ |
|
if ( !V_strcmp( adr.ToString( true ), sv_rcon_whitelist_address.GetString() ) ) |
|
{ |
|
ConMsg( "Rcon auth failed from rcon whitelist address %s\n", adr.ToString() ); |
|
return false; |
|
} |
|
} |
|
|
|
int i; |
|
FailedRCon_t *failedRcon = NULL; |
|
int nCount = m_failedRcons.Count(); |
|
for ( i=0; i < nCount; ++i ) |
|
{ |
|
if ( adr.CompareAdr( m_failedRcons[i].adr, true ) ) |
|
{ |
|
failedRcon = &m_failedRcons[i]; |
|
break; |
|
} |
|
} |
|
|
|
if ( !failedRcon ) |
|
{ |
|
// remove an old rcon if necessary |
|
if ( nCount >= 32 ) |
|
{ |
|
// look for the one with the oldest failure |
|
int indexToRemove = -1; |
|
for ( i=0; i < nCount; ++i ) |
|
{ |
|
if ( indexToRemove < 0 || m_failedRcons[i] < m_failedRcons[indexToRemove] ) |
|
{ |
|
indexToRemove = i; |
|
} |
|
} |
|
if ( indexToRemove >= 0 ) |
|
{ |
|
m_failedRcons.Remove( indexToRemove ); |
|
} |
|
} |
|
|
|
// add the new rcon |
|
int index = m_failedRcons.AddToTail(); |
|
failedRcon = &m_failedRcons[index]; |
|
failedRcon->adr = adr; |
|
failedRcon->badPasswordCount = 0; |
|
failedRcon->badPasswordTimes.RemoveAll(); |
|
} |
|
|
|
// update this failed rcon |
|
++failedRcon->badPasswordCount; |
|
failedRcon->badPasswordTimes.AddToTail( sv.GetTime() ); |
|
|
|
// remove old failure times (sv_rcon_maxfailures is limited to 20, so we won't be hurting anything by pruning) |
|
while ( failedRcon->badPasswordTimes.Count() > 20 ) |
|
{ |
|
failedRcon->badPasswordTimes.Remove( 0 ); |
|
} |
|
|
|
// sanity-check the rcon banning cvars |
|
if ( sv_rcon_maxfailures.GetInt() < sv_rcon_minfailures.GetInt() ) |
|
{ |
|
int temp = sv_rcon_maxfailures.GetInt(); |
|
sv_rcon_maxfailures.SetValue( sv_rcon_minfailures.GetInt() ); |
|
sv_rcon_minfailures.SetValue( temp ); |
|
} |
|
|
|
// ConMsg( "%d of %d bad password times tracked\n", failedRcon->badPasswordTimes.Count(), failedRcon->badPasswordCount ); |
|
// ConMsg( "min=%d, max=%d, time=%.2f\n", sv_rcon_minfailures.GetInt(), sv_rcon_maxfailures.GetInt(), sv_rcon_minfailuretime.GetFloat() ); |
|
|
|
// check if the user should be banned based on total failed attempts |
|
if ( failedRcon->badPasswordCount > sv_rcon_maxfailures.GetInt() ) |
|
{ |
|
ConMsg( "Banning %s for rcon hacking attempts\n", failedRcon->adr.ToString( true ) ); |
|
Cbuf_AddText( va( "addip %i %s\n", sv_rcon_banpenalty.GetInt(), failedRcon->adr.ToString( true ) ) ); |
|
Cbuf_Execute(); |
|
return true; |
|
} |
|
|
|
// check if the user should be banned based on recent failed attempts |
|
int recentFailures = 0; |
|
for ( i=failedRcon->badPasswordTimes.Count()-1; i>=0; --i ) |
|
{ |
|
if ( failedRcon->badPasswordTimes[i] + sv_rcon_minfailuretime.GetFloat() >= sv.GetTime() ) |
|
{ |
|
++recentFailures; |
|
} |
|
} |
|
if ( recentFailures > sv_rcon_minfailures.GetInt() ) |
|
{ |
|
ConMsg( "Banning %s for rcon hacking attempts\n", failedRcon->adr.ToString( true ) ); |
|
Cbuf_AddText( va( "addip %i %s\n", sv_rcon_banpenalty.GetInt(), failedRcon->adr.ToString( true ) ) ); |
|
Cbuf_Execute(); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool CRConServer::BCloseAcceptedSocket( ra_listener_id listener ) |
|
{ |
|
int nCount = m_Socket.GetAcceptedSocketCount(); |
|
for ( int i = 0; i < nCount; i++ ) |
|
{ |
|
ConnectedRConSocket_t *pSocketData = GetSocketData( i ); |
|
if ( pSocketData->listenerID == listener ) |
|
{ |
|
m_Socket.CloseAcceptedSocket( i ); |
|
return true; |
|
} |
|
} |
|
return false; |
|
}
|
|
|