2022-02-06 16:48:52 +01:00
//===========================================================================//
2022-02-14 23:16:24 +01:00
//
// Purpose: Implementation of the rcon client.
//
2022-02-06 16:48:52 +01:00
//===========================================================================//
# include "core/stdafx.h"
2022-04-09 16:16:40 +02:00
# include "tier1/cmd.h"
# include "tier1/cvar.h"
2024-06-01 11:24:47 +02:00
# include "protoc/netcon.pb.h"
2022-05-20 11:52:19 +02:00
# include "engine/client/cl_rcon.h"
2023-04-19 01:35:31 +02:00
# include "engine/shared/shared_rcon.h"
2022-08-02 23:58:43 +02:00
# include "engine/net.h"
2023-05-06 16:23:56 +02:00
# include "vscript/languages/squirrel_re/include/sqvm.h"
2022-02-06 16:48:52 +01:00
# include "common/igameserverdata.h"
2022-08-02 23:58:43 +02:00
2024-02-24 02:15:09 +01:00
//-----------------------------------------------------------------------------
// Purpose: console variables
//-----------------------------------------------------------------------------
2024-06-01 11:24:47 +02:00
static void RCON_AddressChanged_f ( IConVar * pConVar , const char * pOldString ) ;
static void RCON_InputOnlyChanged_f ( IConVar * pConVar , const char * pOldString ) ;
static ConVar cl_rcon_address ( " cl_rcon_address " , " " , FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE , " Remote server access address (rcon client is disabled if empty) " , &RCON_AddressChanged_f) ;
static ConVar cl_rcon_inputonly ( " cl_rcon_inputonly " , " 0 " , FCVAR_RELEASE , " Tells the rcon server whether or not we are input only. " , RCON_InputOnlyChanged_f ) ;
2024-02-24 02:15:09 +01:00
//-----------------------------------------------------------------------------
// Purpose: console commands
//-----------------------------------------------------------------------------
static void RCON_CmdQuery_f ( const CCommand & args ) ;
static ConCommand rcon ( " rcon " , RCON_CmdQuery_f , " Forward RCON query to remote server " , FCVAR_CLIENTDLL | FCVAR_RELEASE , nullptr , " rcon \" <query> \" " ) ;
2022-08-02 23:58:43 +02:00
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CRConClient : : CRConClient ( )
: m_bInitialized ( false )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CRConClient : : ~ CRConClient ( void )
{
2024-02-22 01:55:08 +01:00
// NOTE: do not call Shutdown() from the destructor as the OS's socket
// system would be shutdown by now, call Shutdown() in application
// shutdown code instead
2022-08-02 23:58:43 +02:00
}
2022-02-14 23:16:24 +01:00
//-----------------------------------------------------------------------------
// Purpose: NETCON systems init
//-----------------------------------------------------------------------------
2024-06-01 11:24:47 +02:00
void CRConClient : : Init ( const char * pNetKey )
2022-02-14 23:16:24 +01:00
{
2024-06-01 11:24:47 +02:00
SetKey ( pNetKey ) ;
2022-02-14 23:16:24 +01:00
m_bInitialized = true ;
}
//-----------------------------------------------------------------------------
// Purpose: NETCON systems shutdown
//-----------------------------------------------------------------------------
void CRConClient : : Shutdown ( void )
{
2023-04-22 16:02:54 +02:00
Disconnect ( " shutdown " ) ;
2022-02-14 23:16:24 +01:00
}
//-----------------------------------------------------------------------------
// Purpose: client rcon main processing loop
//-----------------------------------------------------------------------------
void CRConClient : : RunFrame ( void )
{
2023-04-19 01:35:31 +02:00
if ( IsInitialized ( ) & & IsConnected ( ) )
2022-02-14 23:16:24 +01:00
{
2023-04-22 16:02:54 +02:00
CConnectedNetConsoleData * pData = GetData ( ) ;
Assert ( pData ! = nullptr ) ;
if ( pData )
{
2023-08-04 10:48:22 +02:00
Recv ( * pData ) ;
2023-04-22 16:02:54 +02:00
}
2022-02-14 23:16:24 +01:00
}
}
//-----------------------------------------------------------------------------
// Purpose: disconnect from current session
//-----------------------------------------------------------------------------
2023-04-19 01:35:31 +02:00
void CRConClient : : Disconnect ( const char * szReason )
2022-02-14 23:16:24 +01:00
{
2023-04-19 01:35:31 +02:00
if ( IsConnected ( ) )
2022-02-14 23:16:24 +01:00
{
2023-04-22 16:02:54 +02:00
if ( ! szReason )
2022-02-14 23:16:24 +01:00
{
2023-04-22 16:02:54 +02:00
szReason = " unknown reason " ;
2022-02-14 23:16:24 +01:00
}
2024-02-22 01:55:08 +01:00
Msg ( eDLL_T : : CLIENT , " RCON disconnect: (%s) \n " , szReason ) ;
2023-04-19 01:35:31 +02:00
m_Socket . CloseAcceptedSocket ( 0 ) ;
2022-02-14 23:16:24 +01:00
}
}
//-----------------------------------------------------------------------------
2023-04-19 01:35:31 +02:00
// Purpose: processes received message
// Input : *pMsgBug -
// nMsgLen -
2022-02-14 23:16:24 +01:00
//-----------------------------------------------------------------------------
2023-04-22 16:02:54 +02:00
bool CRConClient : : ProcessMessage ( const char * pMsgBuf , const int nMsgLen )
2022-02-14 23:16:24 +01:00
{
2024-06-01 11:24:47 +02:00
netcon : : response response ;
2022-08-02 23:58:43 +02:00
2024-06-01 11:24:47 +02:00
if ( ! SH_NetConUnpackEnvelope ( this , pMsgBuf , nMsgLen , & response , rcon_debug . GetBool ( ) ) )
2023-04-19 01:35:31 +02:00
{
2024-06-01 11:24:47 +02:00
Disconnect ( " received invalid message " ) ;
2023-04-19 01:35:31 +02:00
return false ;
2022-02-14 23:16:24 +01:00
}
2023-04-19 01:35:31 +02:00
switch ( response . responsetype ( ) )
2022-02-14 23:16:24 +01:00
{
2024-06-01 11:24:47 +02:00
case netcon : : response_e : : SERVERDATA_RESPONSE_AUTH :
2022-02-17 18:00:29 +01:00
{
2023-04-19 01:35:31 +02:00
if ( ! response . responseval ( ) . empty ( ) )
2022-08-17 11:50:40 +02:00
{
2023-08-04 17:28:01 +02:00
const int i = atoi ( response . responseval ( ) . c_str ( ) ) ;
2023-04-16 12:05:39 +02:00
2023-08-04 17:28:01 +02:00
// '!i' means we are marked 'input only' on the rcon server.
if ( ! i & & ShouldReceive ( ) )
2022-08-17 11:50:40 +02:00
{
2023-08-04 17:28:01 +02:00
RequestConsoleLog ( true ) ;
2023-04-16 12:05:39 +02:00
}
2022-08-17 11:50:40 +02:00
}
2023-08-21 19:12:29 +02:00
Msg ( eDLL_T : : NETCON , " %s " , response . responsemsg ( ) . c_str ( ) ) ;
2022-02-17 18:00:29 +01:00
break ;
}
2024-06-01 11:24:47 +02:00
case netcon : : response_e : : SERVERDATA_RESPONSE_CONSOLE_LOG :
2022-02-14 23:16:24 +01:00
{
2023-04-19 01:35:31 +02:00
NetMsg ( static_cast < LogType_t > ( response . messagetype ( ) ) ,
static_cast < eDLL_T > ( response . messageid ( ) ) ,
response . responseval ( ) . c_str ( ) , " %s " , response . responsemsg ( ) . c_str ( ) ) ;
2022-02-14 23:16:24 +01:00
break ;
}
default :
{
break ;
}
}
2023-04-19 01:35:31 +02:00
return true ;
2022-02-14 23:16:24 +01:00
}
2023-08-04 17:28:01 +02:00
//-----------------------------------------------------------------------------
// Purpose: request the rcon server to enable/disable sending logs to us
// Input : bWantLog -
//-----------------------------------------------------------------------------
void CRConClient : : RequestConsoleLog ( const bool bWantLog )
{
// If 'IsRemoteLocal()' returns true, and you called this with 'bWantLog'
// true, you caused a bug! It means the server address and port are equal
// to the global netadr singleton, which ultimately means we are running on
// a listen server. Listen server's already log to the same console,
// sending logs will cause the print func to get called recursively forever.
Assert ( ! ( bWantLog & & IsRemoteLocal ( ) ) ) ;
const char * szEnable = bWantLog ? " 1 " : " 0 " ;
const SocketHandle_t hSocket = GetSocket ( ) ;
vector < char > vecMsg ;
2024-06-01 11:24:47 +02:00
bool ret = Serialize ( vecMsg , " " , szEnable , netcon : : request_e : : SERVERDATA_REQUEST_SEND_CONSOLE_LOG ) ;
2023-08-04 17:28:01 +02:00
if ( ret & & ! Send ( hSocket , vecMsg . data ( ) , int ( vecMsg . size ( ) ) ) )
{
Error ( eDLL_T : : CLIENT , NO_ERROR , " Failed to send RCON message: (%s) \n " , " SOCKET_ERROR " ) ;
}
}
2022-02-14 23:16:24 +01:00
//-----------------------------------------------------------------------------
// Purpose: serializes input
// Input : *svReqBuf -
// *svReqVal -
// request_t -
// Output : serialized results as string
//-----------------------------------------------------------------------------
2023-04-19 01:35:31 +02:00
bool CRConClient : : Serialize ( vector < char > & vecBuf , const char * szReqBuf ,
2024-06-01 11:24:47 +02:00
const char * szReqVal , const netcon : : request_e requestType ) const
2022-02-14 23:16:24 +01:00
{
2024-06-01 11:24:47 +02:00
return CL_NetConSerialize ( this , vecBuf , szReqBuf , szReqVal , requestType ,
rcon_encryptframes . GetBool ( ) , rcon_debug . GetBool ( ) ) ;
2022-02-14 23:16:24 +01:00
}
2023-04-22 16:02:54 +02:00
//-----------------------------------------------------------------------------
// Purpose: retrieves the remote socket
// Output : SOCKET_ERROR (-1) on failure
//-----------------------------------------------------------------------------
CConnectedNetConsoleData * CRConClient : : GetData ( void )
{
return SH_GetNetConData ( this , 0 ) ;
}
2022-02-14 23:16:24 +01:00
//-----------------------------------------------------------------------------
2023-04-19 01:35:31 +02:00
// Purpose: retrieves the remote socket
// Output : SOCKET_ERROR (-1) on failure
2022-02-14 23:16:24 +01:00
//-----------------------------------------------------------------------------
2023-04-19 01:35:31 +02:00
SocketHandle_t CRConClient : : GetSocket ( void )
2022-02-14 23:16:24 +01:00
{
2023-04-19 01:35:31 +02:00
return SH_GetNetConSocketHandle ( this , 0 ) ;
2022-02-14 23:16:24 +01:00
}
2023-08-04 17:28:01 +02:00
//-----------------------------------------------------------------------------
// Purpose: returns whether or not we should receive logs from the server
//-----------------------------------------------------------------------------
bool CRConClient : : ShouldReceive ( void )
{
2024-02-24 02:15:09 +01:00
return ( ! IsRemoteLocal ( ) & & ! cl_rcon_inputonly . GetBool ( ) ) ;
2023-08-04 17:28:01 +02:00
}
//-----------------------------------------------------------------------------
// Purpose: returns whether the rcon server is actually our own listen server
//-----------------------------------------------------------------------------
bool CRConClient : : IsRemoteLocal ( void )
{
return ( g_pNetAdr - > ComparePort ( m_Address ) & & g_pNetAdr - > CompareAdr ( m_Address ) ) ;
}
2022-02-14 23:16:24 +01:00
//-----------------------------------------------------------------------------
// Purpose: checks if client rcon is initialized
//-----------------------------------------------------------------------------
bool CRConClient : : IsInitialized ( void ) const
{
2022-02-15 02:31:41 +01:00
return m_bInitialized ;
2022-02-14 23:16:24 +01:00
}
//-----------------------------------------------------------------------------
2023-04-19 01:35:31 +02:00
// Purpose: returns whether the rcon client is connected
2022-02-14 23:16:24 +01:00
//-----------------------------------------------------------------------------
2023-04-19 01:35:31 +02:00
bool CRConClient : : IsConnected ( void )
2022-02-14 23:16:24 +01:00
{
2023-04-19 01:35:31 +02:00
return ( GetSocket ( ) ! = SOCKET_ERROR ) ;
2022-02-14 23:16:24 +01:00
}
2023-04-19 01:35:31 +02:00
2022-02-17 18:00:29 +01:00
///////////////////////////////////////////////////////////////////////////////
2024-02-22 01:55:08 +01:00
static CRConClient s_RCONClient ;
2022-11-14 21:00:41 +01:00
CRConClient * RCONClient ( ) // Singleton RCON Client.
2022-07-25 19:35:08 +02:00
{
2024-02-22 01:55:08 +01:00
return & s_RCONClient ;
2023-04-22 16:02:54 +02:00
}
2024-02-23 00:12:06 +01:00
2024-06-01 11:24:47 +02:00
/*
= = = = = = = = = = = = = = = = = = = = =
RCON_AddressChanged_f
= = = = = = = = = = = = = = = = = = = = =
*/
static void RCON_AddressChanged_f ( IConVar * pConVar , const char * pOldString )
{
if ( ConVar * pConVarRef = g_pCVar - > FindVar ( pConVar - > GetName ( ) ) )
{
const char * pNewString = pConVarRef - > GetString ( ) ;
if ( strcmp ( pOldString , pNewString ) = = NULL )
{
return ; // Same address.
}
if ( ! * pNewString ) // Empty address means nothing to network to; shutdown client...
{
RCONClient ( ) - > Shutdown ( ) ;
}
else
{
RCON_InitClientAndTrySyncKeys ( ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
RCON_InputOnlyChanged_f
request whether to recv logs
from RCON server when cvar
changes
= = = = = = = = = = = = = = = = = = = = =
*/
static void RCON_InputOnlyChanged_f ( IConVar * pConVar , const char * pOldString )
{
RCONClient ( ) - > RequestConsoleLog ( RCONClient ( ) - > ShouldReceive ( ) ) ;
}
2024-02-23 00:12:06 +01:00
/*
= = = = = = = = = = = = = = = = = = = = =
RCON_CmdQuery_f
Issues an RCON command to the
RCON server .
= = = = = = = = = = = = = = = = = = = = =
*/
static void RCON_CmdQuery_f ( const CCommand & args )
{
const int64_t argCount = args . ArgC ( ) ;
if ( argCount < 2 )
{
2024-06-01 11:24:47 +02:00
const char * pszAddress = cl_rcon_address . GetString ( ) ;
2024-02-23 00:12:06 +01:00
if ( RCONClient ( ) - > IsInitialized ( )
& & ! RCONClient ( ) - > IsConnected ( )
& & pszAddress [ 0 ] )
{
RCONClient ( ) - > Connect ( pszAddress ) ;
}
}
else
{
if ( ! RCONClient ( ) - > IsInitialized ( ) )
{
Warning ( eDLL_T : : CLIENT , " Failed to issue command to RCON server: %s \n " , " uninitialized " ) ;
return ;
}
else if ( RCONClient ( ) - > IsConnected ( ) )
{
vector < char > vecMsg ;
bool bSuccess = false ;
const SocketHandle_t hSocket = RCONClient ( ) - > GetSocket ( ) ;
2024-06-01 11:24:47 +02:00
if ( strcmp ( args . Arg ( 1 ) , " PASS " ) = = 0 )
2024-02-23 00:12:06 +01:00
{
if ( argCount > 2 )
{
2024-06-01 11:24:47 +02:00
bSuccess = RCONClient ( ) - > Serialize ( vecMsg , args . Arg ( 2 ) , " " , netcon : : request_e : : SERVERDATA_REQUEST_AUTH ) ;
2024-02-23 00:12:06 +01:00
}
2024-06-01 11:24:47 +02:00
else // Auth with RCON server using rcon_password ConVar value.
2024-02-23 00:12:06 +01:00
{
2024-06-01 11:24:47 +02:00
const char * storedPassword = rcon_password . GetString ( ) ;
if ( ! strlen ( storedPassword ) )
{
Warning ( eDLL_T : : CLIENT , " Failed to issue command to RCON server: %s \n " , " no password given " ) ;
return ;
}
bSuccess = RCONClient ( ) - > Serialize ( vecMsg , storedPassword , " " , netcon : : request_e : : SERVERDATA_REQUEST_AUTH ) ;
2024-02-23 00:12:06 +01:00
}
if ( bSuccess )
{
RCONClient ( ) - > Send ( hSocket , vecMsg . data ( ) , int ( vecMsg . size ( ) ) ) ;
}
return ;
}
else if ( strcmp ( args . Arg ( 1 ) , " disconnect " ) = = 0 ) // Disconnect from RCON server.
{
2024-06-01 11:24:47 +02:00
RCONClient ( ) - > Disconnect ( " ordered by user " ) ;
2024-02-23 00:12:06 +01:00
return ;
}
2024-06-01 11:24:47 +02:00
bSuccess = RCONClient ( ) - > Serialize ( vecMsg , args . Arg ( 1 ) , args . ArgS ( ) , netcon : : request_e : : SERVERDATA_REQUEST_EXECCOMMAND ) ;
2024-02-23 00:12:06 +01:00
if ( bSuccess )
{
RCONClient ( ) - > Send ( hSocket , vecMsg . data ( ) , int ( vecMsg . size ( ) ) ) ;
}
return ;
}
else
{
Warning ( eDLL_T : : CLIENT , " Failed to issue command to RCON server: %s \n " , " unconnected " ) ;
return ;
}
}
}