//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: defines a RCon class used to send rcon commands to remote servers // // $NoKeywords: $ //============================================================================= #include "rcon.h" #include "Iresponse.h" #include "RconMsgHandler.h" #include "Socket.h" #include "proto_oob.h" #include "DialogGameInfo.h" #include "inetapi.h" #include "dialogkickplayer.h" extern void v_strncpy(char *dest, const char *src, int bufsize); typedef enum { NONE = 0, INFO_REQUESTED, INFO_RECEIVED } RCONSTATUS; CRcon::CRcon(IResponse *target,serveritem_t &server,const char *password) { memcpy(&m_Server, &server,sizeof(serveritem_t)); m_pResponseTarget=target; m_bIsRefreshing = false; m_bChallenge = false; m_bNewRcon = false; m_bPasswordFail = false; m_bDisable = false; m_bGotChallenge = false; m_iChallenge = 0; m_fQuerySendTime= 0; v_strncpy(m_sPassword,password,100); int bytecode = S2A_INFO_DETAILED; m_pQuery = new CSocket("internet rcon query", -1); m_pQuery->AddMessageHandler(new CRconMsgHandlerDetails(this, CMsgHandler::MSGHANDLER_ALL, &bytecode)); } CRcon::~CRcon() { delete m_pQuery; } //----------------------------------------------------------------------------- // Purpose: resets the state of the rcon object back to startup conditions (i.e get challenge again) //----------------------------------------------------------------------------- void CRcon::Reset() { m_bIsRefreshing = false; m_bChallenge = false; m_bNewRcon = false; m_bPasswordFail = false; m_bDisable = false; m_bGotChallenge = false; m_iChallenge = 0; m_fQuerySendTime= 0; } //----------------------------------------------------------------------------- // Purpose: sends a challenge request to the server if we have yet to get one, // else it sends the rcon itself //----------------------------------------------------------------------------- void CRcon::SendRcon(const char *command) { if(m_bDisable==true) // rcon is disabled because it has failed. { m_pResponseTarget->ServerFailedToRespond(); return; } if(m_bIsRefreshing) { // we are already processing a request, lets queue this queue_requests_t queue; strncpy(queue.queued,command,1024); if(requests.Count()>10) // to many request already... return; requests.AddToTail(queue); return; } m_bIsRefreshing=true; m_bPasswordFail=false; if(m_bGotChallenge==false) // haven't got the challenge id yet { GetChallenge(); v_strncpy(m_sCmd,command,1024); // store away the command for later :) m_bChallenge=true; // note that we are requesting a challenge and need to still run this command } else { RconRequest(command,m_iChallenge); } } //----------------------------------------------------------------------------- // Purpose: runs a frame of the net handler //----------------------------------------------------------------------------- void CRcon::RunFrame() { // only the "ping" command has a timeout /* float curtime = CSocket::GetClock(); if(m_fQuerySendTime!=0 && (curtime-m_fQuerySendTime)> 10.0f) // 10 seconds { m_fQuerySendTime= 0; m_pResponseTarget->ServerFailedToRespond(); } */ if (m_pQuery) { m_pQuery->Frame(); } } //----------------------------------------------------------------------------- // Purpose: emits a challenge request //----------------------------------------------------------------------------- void CRcon::GetChallenge() { CMsgBuffer *buffer = m_pQuery->GetSendBuffer(); assert( buffer ); if ( !buffer ) { return; } netadr_t adr; adr.ip[0] = m_Server.ip[0]; adr.ip[1] = m_Server.ip[1]; adr.ip[2] = m_Server.ip[2]; adr.ip[3] = m_Server.ip[3]; adr.port = (m_Server.port & 0xff) << 8 | (m_Server.port & 0xff00) >> 8; adr.type = NA_IP; // Set state m_Server.received = (int)INFO_REQUESTED; // Create query message buffer->Clear(); // Write control sequence buffer->WriteLong(0xffffffff); // Write query string buffer->WriteString("challenge rcon"); // Sendmessage m_pQuery->SendMessage( &adr ); // set the clock for this send m_fQuerySendTime = CSocket::GetClock(); } //----------------------------------------------------------------------------- // Purpose: emits a valid rcon request, the challenge id has already been found //----------------------------------------------------------------------------- void CRcon::RconRequest(const char *command, int challenge) { CMsgBuffer *buffer = m_pQuery->GetSendBuffer(); assert( buffer ); if ( !buffer ) { return; } netadr_t adr; adr.ip[0] = m_Server.ip[0]; adr.ip[1] = m_Server.ip[1]; adr.ip[2] = m_Server.ip[2]; adr.ip[3] = m_Server.ip[3]; adr.port = (m_Server.port & 0xff) << 8 | (m_Server.port & 0xff00) >> 8; adr.type = NA_IP; // Set state m_Server.received = (int)INFO_REQUESTED; // Create query message buffer->Clear(); // Write control sequence buffer->WriteLong(0xffffffff); // Write query string char rcon_cmd[600]; _snprintf(rcon_cmd,600,"rcon %u \"%s\" %s",challenge,m_sPassword,command); buffer->WriteString(rcon_cmd); // Sendmessage m_pQuery->SendMessage( &adr ); // set the clock for this send m_fQuerySendTime = CSocket::GetClock(); } //----------------------------------------------------------------------------- // Purpose: called when an rcon responds //----------------------------------------------------------------------------- void CRcon::UpdateServer(netadr_t *adr, int challenge, const char *resp) { m_fQuerySendTime= 0; if(m_bChallenge==true) // now send the RCON request itself { m_bChallenge=false; // m_bChallenge is set to say we just requested the challenge value m_iChallenge=challenge; m_bGotChallenge=true; RconRequest(m_sCmd,m_iChallenge); } else // this is the result of the RCON request { m_bNewRcon=true; v_strncpy(m_sRconResponse,resp,2048); m_bIsRefreshing=false; // this must be before the SeverResponded() :) if(requests.Count()>0) { // we have queued requests SendRcon(requests[0].queued); requests.Remove(0); // now delete this element } // notify the UI of the new server info m_pResponseTarget->ServerResponded(); } } //----------------------------------------------------------------------------- // Purpose: run when a refresh is asked for //----------------------------------------------------------------------------- void CRcon::Refresh() { } //----------------------------------------------------------------------------- // Purpose: returns if a rcon is currently being performed //----------------------------------------------------------------------------- bool CRcon::IsRefreshing() { return m_bIsRefreshing; } //----------------------------------------------------------------------------- // Purpose: the server to which this rcon class is bound //----------------------------------------------------------------------------- serveritem_t &CRcon::GetServer() { return m_Server; } //----------------------------------------------------------------------------- // Purpose: the challenge id used in rcons //----------------------------------------------------------------------------- bool CRcon::Challenge() { return m_bChallenge; } //----------------------------------------------------------------------------- // Purpose: returns if a new rcon result is available //----------------------------------------------------------------------------- bool CRcon::NewRcon() { bool val = m_bNewRcon; m_bNewRcon=false; return val; } //----------------------------------------------------------------------------- // Purpose: returns the response of the last rcon //----------------------------------------------------------------------------- const char *CRcon::RconResponse() { return (const char *)m_sRconResponse; } //----------------------------------------------------------------------------- // Purpose: called when the wrong password is used, when a ServerFailed is called //----------------------------------------------------------------------------- bool CRcon::PasswordFail() { bool val=m_bPasswordFail; m_bPasswordFail=false; return val; //m_pResponseTarget->ServerFailedToRespond(); } //----------------------------------------------------------------------------- // Purpose: called by the rcon message handler to denote a bad password //----------------------------------------------------------------------------- void CRcon::BadPassword(const char *info) { strncpy(m_sRconResponse,info,100); m_bPasswordFail=true; m_bDisable=true; m_fQuerySendTime= 0; m_bIsRefreshing=false; /* // this must be before the ServerFailedToRespond() :) if(requests.Count()>0) { // we have queued requests SendRcon(requests[0].queued); requests.Remove(0); // now delete this element } */ m_pResponseTarget->ServerFailedToRespond(); } //----------------------------------------------------------------------------- // Purpose: return whether rcon has been disabled (due to bad passwords) //----------------------------------------------------------------------------- bool CRcon::Disabled() { return m_bDisable; } //----------------------------------------------------------------------------- // Purpose: sets the password to use for rcons //----------------------------------------------------------------------------- void CRcon::SetPassword(const char *newPass) { strncpy(m_sPassword,newPass,100); m_bDisable=false; // new password, so we can try again }