1
0

Migrated RCON server to cNetwork API.

This commit is contained in:
Mattes D 2015-01-24 13:31:42 +01:00
parent 059af2efdc
commit 9e61ad8c74
2 changed files with 119 additions and 103 deletions

View File

@ -38,6 +38,43 @@ enum
////////////////////////////////////////////////////////////////////////////////
// cRCONListenCallbacks:
class cRCONListenCallbacks:
public cNetwork::cListenCallbacks
{
public:
cRCONListenCallbacks(cRCONServer & a_RCONServer, UInt16 a_Port):
m_RCONServer(a_RCONServer),
m_Port(a_Port)
{
}
protected:
/** The RCON server instance that we're attached to. */
cRCONServer & m_RCONServer;
/** The port for which this instance is responsible. */
UInt16 m_Port;
// cNetwork::cListenCallbacks overrides:
virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override
{
LOG("RCON Client \"%s\" connected!", a_RemoteIPAddress.c_str());
return std::make_shared<cRCONServer::cConnection>(m_RCONServer, a_RemoteIPAddress);
}
virtual void OnAccepted(cTCPLink & a_Link) override {}
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
{
LOGWARNING("RCON server error on port %d: %d (%s)", m_Port, a_ErrorCode, a_ErrorMsg);
}
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cRCONCommandOutput: // cRCONCommandOutput:
@ -77,9 +114,7 @@ protected:
// cRCONServer: // cRCONServer:
cRCONServer::cRCONServer(cServer & a_Server) : cRCONServer::cRCONServer(cServer & a_Server) :
m_Server(a_Server), m_Server(a_Server)
m_ListenThread4(*this, cSocket::IPv4, "RCON"),
m_ListenThread6(*this, cSocket::IPv6, "RCON")
{ {
} }
@ -89,8 +124,10 @@ cRCONServer::cRCONServer(cServer & a_Server) :
cRCONServer::~cRCONServer() cRCONServer::~cRCONServer()
{ {
m_ListenThread4.Stop(); for (auto srv: m_ListenServers)
m_ListenThread6.Stop(); {
srv->Close();
}
} }
@ -112,42 +149,29 @@ void cRCONServer::Initialize(cIniFile & a_IniFile)
return; return;
} }
// Read and initialize both IPv4 and IPv6 ports for RCON // Read the listening ports for RCON from config:
bool HasAnyPorts = false; AStringVector Ports = ReadUpgradeIniPorts(a_IniFile, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575");
AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575");
if (m_ListenThread4.Initialize(Ports4)) // Start listening on each specified port:
for (auto port: Ports)
{ {
HasAnyPorts = true; UInt16 PortNum = static_cast<UInt16>(atol(port.c_str()));
m_ListenThread4.Start(); if (PortNum == 0)
} {
AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575"); LOGINFO("Invalid RCON port value: \"%s\". Ignoring.", port.c_str());
if (m_ListenThread6.Initialize(Ports6)) continue;
{ }
HasAnyPorts = true; auto Handle = cNetwork::Listen(PortNum, std::make_shared<cRCONListenCallbacks>(*this, PortNum));
m_ListenThread6.Start(); if (Handle->IsListening())
} {
if (!HasAnyPorts) m_ListenServers.push_back(Handle);
{ }
LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled.");
return;
}
}
void cRCONServer::OnConnectionAccepted(cSocket & a_Socket)
{
if (!a_Socket.IsValid())
{
return;
} }
LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str()); if (m_ListenServers.empty())
{
// Create a new cConnection object, it will be deleted when the connection is closed LOGWARNING("RCON is enabled but no valid ports were found. RCON is not accessible.");
m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket)); }
} }
@ -157,11 +181,10 @@ void cRCONServer::OnConnectionAccepted(cSocket & a_Socket)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cRCONServer::cConnection: // cRCONServer::cConnection:
cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) : cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, const AString & a_IPAddress) :
m_IsAuthenticated(false), m_IsAuthenticated(false),
m_RCONServer(a_RCONServer), m_RCONServer(a_RCONServer),
m_Socket(a_Socket), m_IPAddress(a_IPAddress)
m_IPAddress(a_Socket.GetIPString())
{ {
} }
@ -169,8 +192,19 @@ cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_So
bool cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size) void cRCONServer::cConnection::OnLinkCreated(cTCPLinkPtr a_Link)
{ {
m_Link = a_Link;
}
void cRCONServer::cConnection::OnReceivedData(const char * a_Data, size_t a_Size)
{
ASSERT(m_Link != nullptr);
// Append data to the buffer: // Append data to the buffer:
m_Buffer.append(a_Data, a_Size); m_Buffer.append(a_Data, a_Size);
@ -184,49 +218,45 @@ bool cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size)
LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.", LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.",
Length, m_IPAddress.c_str() Length, m_IPAddress.c_str()
); );
m_RCONServer.m_SocketThreads.RemoveClient(this); m_Link->Close();
m_Socket.CloseSocket(); m_Link.reset();
delete this; return;
return false;
} }
if (Length > (int)(m_Buffer.size() + 4)) if (Length > static_cast<int>(m_Buffer.size() + 4))
{ {
// Incomplete packet yet, wait for more data to come // Incomplete packet yet, wait for more data to come
return false; return;
} }
int RequestID = IntFromBuffer(m_Buffer.data() + 4); int RequestID = IntFromBuffer(m_Buffer.data() + 4);
int PacketType = IntFromBuffer(m_Buffer.data() + 8); int PacketType = IntFromBuffer(m_Buffer.data() + 8);
if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12))
{ {
m_RCONServer.m_SocketThreads.RemoveClient(this); m_Link->Close();
m_Socket.CloseSocket(); m_Link.reset();
delete this; return;
return false;
} }
m_Buffer.erase(0, Length + 4); m_Buffer.erase(0, Length + 4);
} // while (m_Buffer.size() >= 14) } // while (m_Buffer.size() >= 14)
return false;
} }
void cRCONServer::cConnection::GetOutgoingData(AString & a_Data) void cRCONServer::cConnection::OnRemoteClosed(void)
{ {
a_Data.assign(m_Outgoing); m_Link.reset();
m_Outgoing.clear();
} }
void cRCONServer::cConnection::SocketClosed(void) void cRCONServer::cConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
{ {
m_RCONServer.m_SocketThreads.RemoveClient(this); LOGD("Error in RCON connection %s: %d (%s)", m_IPAddress.c_str(), a_ErrorCode, a_ErrorMsg.c_str());
delete this; m_Link.reset();
} }
@ -311,22 +341,21 @@ void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer)
void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload)
{ {
ASSERT((a_PayloadLength == 0) || (a_Payload != nullptr)); // Either zero data to send, or a valid payload ptr ASSERT((a_PayloadLength == 0) || (a_Payload != nullptr)); // Either zero data to send, or a valid payload ptr
ASSERT(m_Link != nullptr);
char Buffer[4]; char Buffer[4];
int Length = a_PayloadLength + 10; int Length = a_PayloadLength + 10;
IntToBuffer(Length, Buffer); IntToBuffer(Length, Buffer);
m_Outgoing.append(Buffer, 4); m_Link->Send(Buffer, 4);
IntToBuffer(a_RequestID, Buffer); IntToBuffer(a_RequestID, Buffer);
m_Outgoing.append(Buffer, 4); m_Link->Send(Buffer, 4);
IntToBuffer(a_PacketType, Buffer); IntToBuffer(a_PacketType, Buffer);
m_Outgoing.append(Buffer, 4); m_Link->Send(Buffer, 4);
if (a_PayloadLength > 0) if (a_PayloadLength > 0)
{ {
m_Outgoing.append(a_Payload, a_PayloadLength); m_Link->Send(a_Payload, a_PayloadLength);
} }
m_Outgoing.push_back(0); m_Link->Send("\0", 2); // Send two zero chars as the padding
m_Outgoing.push_back(0);
m_RCONServer.m_SocketThreads.NotifyWrite(this);
} }

View File

@ -9,8 +9,7 @@
#pragma once #pragma once
#include "OSSupport/SocketThreads.h" #include "OSSupport/Network.h"
#include "OSSupport/ListenThread.h"
@ -24,8 +23,7 @@ class cIniFile;
class cRCONServer : class cRCONServer
public cListenThread::cCallback
{ {
public: public:
cRCONServer(cServer & a_Server); cRCONServer(cServer & a_Server);
@ -35,72 +33,61 @@ public:
protected: protected:
friend class cRCONCommandOutput; friend class cRCONCommandOutput;
friend class cRCONListenCallbacks;
class cConnection : class cConnection :
public cSocketThreads::cCallback public cTCPLink::cCallbacks
{ {
public: public:
cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); cConnection(cRCONServer & a_RCONServer, const AString & a_IPAddress);
protected: protected:
friend class cRCONCommandOutput; friend class cRCONCommandOutput;
/// Set to true if the client has successfully authenticated /** Set to true if the client has successfully authenticated */
bool m_IsAuthenticated; bool m_IsAuthenticated;
/// Buffer for the incoming data /** Buffer for the incoming data */
AString m_Buffer; AString m_Buffer;
/// Buffer for the outgoing data /** Server that owns this connection and processes requests */
AString m_Outgoing;
/// Server that owns this connection and processes requests
cRCONServer & m_RCONServer; cRCONServer & m_RCONServer;
/// The socket belonging to the client /** The TCP link to the client */
cSocket & m_Socket; cTCPLinkPtr m_Link;
/// Address of the client /** Address of the client */
AString m_IPAddress; AString m_IPAddress;
// cSocketThreads::cCallback overrides: // cTCPLink::cCallbacks overrides:
virtual bool DataReceived(const char * a_Data, size_t a_Size) override; virtual void OnLinkCreated(cTCPLinkPtr a_Link);
virtual void GetOutgoingData(AString & a_Data) override; virtual void OnReceivedData(const char * a_Data, size_t a_Length) override;
virtual void SocketClosed(void) override; virtual void OnRemoteClosed(void) override;
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;
/// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped /** Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped */
bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload);
/// Reads 4 bytes from a_Buffer and returns the int they represent /** Reads 4 bytes from a_Buffer and returns the int they represent */
int IntFromBuffer(const char * a_Buffer); int IntFromBuffer(const char * a_Buffer);
/// Puts 4 bytes representing the int into the buffer /** Puts 4 bytes representing the int into the buffer */
void IntToBuffer(int a_Value, char * a_Buffer); void IntToBuffer(int a_Value, char * a_Buffer);
/// Sends a RCON packet back to the client /** Sends a RCON packet back to the client */
void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload);
} ; } ;
/// The server object that will process the commands received /** The server object that will process the commands received */
cServer & m_Server; cServer & m_Server;
/// The thread(s) that take care of all the traffic on the RCON ports /** The sockets for accepting RCON connections (one socket per port). */
cSocketThreads m_SocketThreads; cServerHandlePtrs m_ListenServers;
/// The thread for accepting IPv4 RCON connections /** Password for authentication */
cListenThread m_ListenThread4;
/// The thread for accepting IPv6 RCON connections
cListenThread m_ListenThread6;
/// Password for authentication
AString m_Password; AString m_Password;
// cListenThread::cCallback overrides:
virtual void OnConnectionAccepted(cSocket & a_Socket) override;
} ; } ;