diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index 49ca4fc61..2ec7f191f 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -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(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: @@ -77,9 +114,7 @@ protected: // cRCONServer: cRCONServer::cRCONServer(cServer & a_Server) : - m_Server(a_Server), - m_ListenThread4(*this, cSocket::IPv4, "RCON"), - m_ListenThread6(*this, cSocket::IPv6, "RCON") + m_Server(a_Server) { } @@ -89,8 +124,10 @@ cRCONServer::cRCONServer(cServer & a_Server) : cRCONServer::~cRCONServer() { - m_ListenThread4.Stop(); - m_ListenThread6.Stop(); + for (auto srv: m_ListenServers) + { + srv->Close(); + } } @@ -112,42 +149,29 @@ void cRCONServer::Initialize(cIniFile & a_IniFile) return; } - // Read and initialize both IPv4 and IPv6 ports for RCON - bool HasAnyPorts = false; - AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575"); - if (m_ListenThread4.Initialize(Ports4)) + // Read the listening ports for RCON from config: + AStringVector Ports = ReadUpgradeIniPorts(a_IniFile, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575"); + + // Start listening on each specified port: + for (auto port: Ports) { - HasAnyPorts = true; - m_ListenThread4.Start(); - } - AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575"); - if (m_ListenThread6.Initialize(Ports6)) - { - HasAnyPorts = true; - m_ListenThread6.Start(); - } - if (!HasAnyPorts) - { - 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; + UInt16 PortNum = static_cast(atol(port.c_str())); + if (PortNum == 0) + { + LOGINFO("Invalid RCON port value: \"%s\". Ignoring.", port.c_str()); + continue; + } + auto Handle = cNetwork::Listen(PortNum, std::make_shared(*this, PortNum)); + if (Handle->IsListening()) + { + m_ListenServers.push_back(Handle); + } } - LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str()); - - // Create a new cConnection object, it will be deleted when the connection is closed - m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket)); + if (m_ListenServers.empty()) + { + LOGWARNING("RCON is enabled but no valid ports were found. RCON is not accessible."); + } } @@ -157,11 +181,10 @@ void cRCONServer::OnConnectionAccepted(cSocket & a_Socket) //////////////////////////////////////////////////////////////////////////////// // 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_RCONServer(a_RCONServer), - m_Socket(a_Socket), - m_IPAddress(a_Socket.GetIPString()) + m_IPAddress(a_IPAddress) { } @@ -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: 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.", Length, m_IPAddress.c_str() ); - m_RCONServer.m_SocketThreads.RemoveClient(this); - m_Socket.CloseSocket(); - delete this; - return false; + m_Link->Close(); + m_Link.reset(); + return; } - if (Length > (int)(m_Buffer.size() + 4)) + if (Length > static_cast(m_Buffer.size() + 4)) { // Incomplete packet yet, wait for more data to come - return false; + return; } int RequestID = IntFromBuffer(m_Buffer.data() + 4); int PacketType = IntFromBuffer(m_Buffer.data() + 8); if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) { - m_RCONServer.m_SocketThreads.RemoveClient(this); - m_Socket.CloseSocket(); - delete this; - return false; + m_Link->Close(); + m_Link.reset(); + return; } m_Buffer.erase(0, Length + 4); } // 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_Outgoing.clear(); + m_Link.reset(); } -void cRCONServer::cConnection::SocketClosed(void) +void cRCONServer::cConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg) { - m_RCONServer.m_SocketThreads.RemoveClient(this); - delete this; + LOGD("Error in RCON connection %s: %d (%s)", m_IPAddress.c_str(), a_ErrorCode, a_ErrorMsg.c_str()); + 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) { ASSERT((a_PayloadLength == 0) || (a_Payload != nullptr)); // Either zero data to send, or a valid payload ptr + ASSERT(m_Link != nullptr); char Buffer[4]; int Length = a_PayloadLength + 10; IntToBuffer(Length, Buffer); - m_Outgoing.append(Buffer, 4); + m_Link->Send(Buffer, 4); IntToBuffer(a_RequestID, Buffer); - m_Outgoing.append(Buffer, 4); + m_Link->Send(Buffer, 4); IntToBuffer(a_PacketType, Buffer); - m_Outgoing.append(Buffer, 4); + m_Link->Send(Buffer, 4); if (a_PayloadLength > 0) { - m_Outgoing.append(a_Payload, a_PayloadLength); + m_Link->Send(a_Payload, a_PayloadLength); } - m_Outgoing.push_back(0); - m_Outgoing.push_back(0); - m_RCONServer.m_SocketThreads.NotifyWrite(this); + m_Link->Send("\0", 2); // Send two zero chars as the padding } diff --git a/src/RCONServer.h b/src/RCONServer.h index 47c746736..1d599b279 100644 --- a/src/RCONServer.h +++ b/src/RCONServer.h @@ -9,8 +9,7 @@ #pragma once -#include "OSSupport/SocketThreads.h" -#include "OSSupport/ListenThread.h" +#include "OSSupport/Network.h" @@ -24,8 +23,7 @@ class cIniFile; -class cRCONServer : - public cListenThread::cCallback +class cRCONServer { public: cRCONServer(cServer & a_Server); @@ -35,72 +33,61 @@ public: protected: friend class cRCONCommandOutput; + friend class cRCONListenCallbacks; class cConnection : - public cSocketThreads::cCallback + public cTCPLink::cCallbacks { public: - cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); + cConnection(cRCONServer & a_RCONServer, const AString & a_IPAddress); protected: friend class cRCONCommandOutput; - /// Set to true if the client has successfully authenticated + /** Set to true if the client has successfully authenticated */ bool m_IsAuthenticated; - /// Buffer for the incoming data + /** Buffer for the incoming data */ AString m_Buffer; - /// Buffer for the outgoing data - AString m_Outgoing; - - /// Server that owns this connection and processes requests + /** Server that owns this connection and processes requests */ cRCONServer & m_RCONServer; - /// The socket belonging to the client - cSocket & m_Socket; + /** The TCP link to the client */ + cTCPLinkPtr m_Link; - /// Address of the client + /** Address of the client */ AString m_IPAddress; - // cSocketThreads::cCallback overrides: - virtual bool DataReceived(const char * a_Data, size_t a_Size) override; - virtual void GetOutgoingData(AString & a_Data) override; - virtual void SocketClosed(void) override; + // cTCPLink::cCallbacks overrides: + virtual void OnLinkCreated(cTCPLinkPtr a_Link); + virtual void OnReceivedData(const char * a_Data, size_t a_Length) 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); - /// 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); - /// 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); - /// 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); } ; - /// The server object that will process the commands received + /** The server object that will process the commands received */ cServer & m_Server; - /// The thread(s) that take care of all the traffic on the RCON ports - cSocketThreads m_SocketThreads; + /** The sockets for accepting RCON connections (one socket per port). */ + cServerHandlePtrs m_ListenServers; - /// The thread for accepting IPv4 RCON connections - cListenThread m_ListenThread4; - - /// The thread for accepting IPv6 RCON connections - cListenThread m_ListenThread6; - - /// Password for authentication + /** Password for authentication */ AString m_Password; - - - // cListenThread::cCallback overrides: - virtual void OnConnectionAccepted(cSocket & a_Socket) override; } ;