Cuberite
A lightweight, fast and extensible game server for Minecraft
RCONServer.cpp
Go to the documentation of this file.
1 
2 // RCONServer.cpp
3 
4 // Implements the cRCONServer class representing the RCON server
5 
6 #include "Globals.h"
7 #include "IniFile.h"
8 #include "RCONServer.h"
9 #include "Server.h"
10 #include "Root.h"
11 #include "CommandOutput.h"
12 
13 
14 
15 
16 
17 // Disable MSVC warnings:
18 #if defined(_MSC_VER)
19  #pragma warning(push)
20  #pragma warning(disable:4355) // 'this' : used in base member initializer list
21 #endif
22 
23 
24 
25 
26 
27 enum
28 {
29  // Client -> Server:
32 
33  // Server -> Client:
35 } ;
36 
37 
38 
39 
40 
42 // cRCONListenCallbacks:
43 
46 {
47 public:
48  cRCONListenCallbacks(cRCONServer & a_RCONServer, UInt16 a_Port):
49  m_RCONServer(a_RCONServer),
50  m_Port(a_Port)
51  {
52  }
53 
54 protected:
57 
60 
61  // cNetwork::cListenCallbacks overrides:
62  virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override
63  {
64  LOG("RCON Client \"%s\" connected!", a_RemoteIPAddress.c_str());
65  return std::make_shared<cRCONServer::cConnection>(m_RCONServer, a_RemoteIPAddress);
66  }
67  virtual void OnAccepted(cTCPLink & a_Link) override {}
68  virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
69  {
70  LOGWARNING("RCON server error on port %d: %d (%s)", m_Port, a_ErrorCode, a_ErrorMsg.c_str());
71  }
72 };
73 
74 
75 
76 
77 
79 // cRCONCommandOutput:
80 
83 {
84 public:
85  cRCONCommandOutput(cRCONServer::cConnection & a_Connection, UInt32 a_RequestID) :
86  m_Connection(a_Connection),
87  m_RequestID(a_RequestID)
88  {
89  }
90 
91  // cCommandOutputCallback overrides:
92  virtual void Out(const AString & a_Text) override
93  {
94  m_Buffer.append(a_Text);
95  }
96 
97  virtual void Finished(void) override
98  {
99  m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, static_cast<UInt32>(m_Buffer.size()), m_Buffer.c_str());
100  delete this;
101  }
102 
103 protected:
107 } ;
108 
109 
110 
111 
112 
114 // cRCONServer:
115 
117  m_Server(a_Server)
118 {
119 }
120 
121 
122 
123 
124 
126 {
127  for (auto srv: m_ListenServers)
128  {
129  srv->Close();
130  }
131 }
132 
133 
134 
135 
136 
138 {
139  if (!a_Settings.GetValueSetB("RCON", "Enabled", false))
140  {
141  return;
142  }
143 
144  // Read the password, don't allow an empty one:
145  m_Password = a_Settings.GetValueSet("RCON", "Password", "");
146  if (m_Password.empty())
147  {
148  LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled.");
149  return;
150  }
151 
152  // Read the listening ports for RCON from config:
153  AStringVector Ports = ReadUpgradeIniPorts(a_Settings, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575");
154 
155  // Start listening on each specified port:
156  for (auto port: Ports)
157  {
158  UInt16 PortNum;
159  if (!StringToInteger(port, PortNum))
160  {
161  LOGINFO("Invalid RCON port value: \"%s\". Ignoring.", port.c_str());
162  continue;
163  }
164  auto Handle = cNetwork::Listen(PortNum, std::make_shared<cRCONListenCallbacks>(*this, PortNum));
165  if (Handle->IsListening())
166  {
167  m_ListenServers.push_back(Handle);
168  }
169  }
170 
171  if (m_ListenServers.empty())
172  {
173  LOGWARNING("RCON is enabled but no valid ports were found. RCON is not accessible.");
174  }
175 }
176 
177 
178 
179 
180 
182 // cRCONServer::cConnection:
183 
184 cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, const AString & a_IPAddress) :
185  m_IsAuthenticated(false),
186  m_RCONServer(a_RCONServer),
187  m_IPAddress(a_IPAddress)
188 {
189 }
190 
191 
192 
193 
194 
196 {
197  m_Link = a_Link;
198 }
199 
200 
201 
202 
203 
204 void cRCONServer::cConnection::OnReceivedData(const char * a_Data, size_t a_Size)
205 {
206  ASSERT(m_Link != nullptr);
207 
208  // Append data to the buffer:
209  m_Buffer.append(a_Data, a_Size);
210 
211  // Process the packets in the buffer:
212  while (m_Buffer.size() >= 14)
213  {
214  UInt32 Length = UIntFromBuffer(m_Buffer.data());
215  if (Length > 1500)
216  {
217  // Too long, drop the connection
218  LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.",
219  Length, m_IPAddress.c_str()
220  );
221  m_Link->Close();
222  m_Link.reset();
223  return;
224  }
225  if (Length > static_cast<UInt32>(m_Buffer.size() + 4))
226  {
227  // Incomplete packet yet, wait for more data to come
228  return;
229  }
230 
231  UInt32 RequestID = UIntFromBuffer(m_Buffer.data() + 4);
232  UInt32 PacketType = UIntFromBuffer(m_Buffer.data() + 8);
233  if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12))
234  {
235  m_Link->Close();
236  m_Link.reset();
237  return;
238  }
239  m_Buffer.erase(0, Length + 4);
240  } // while (m_Buffer.size() >= 14)
241 }
242 
243 
244 
245 
246 
248 {
249  m_Link.reset();
250 }
251 
252 
253 
254 
255 
256 void cRCONServer::cConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
257 {
258  LOGD("Error in RCON connection %s: %d (%s)", m_IPAddress.c_str(), a_ErrorCode, a_ErrorMsg.c_str());
259  m_Link.reset();
260 }
261 
262 
263 
264 
265 
266 bool cRCONServer::cConnection::ProcessPacket(UInt32 a_RequestID, UInt32 a_PacketType, UInt32 a_PayloadLength, const char * a_Payload)
267 {
268  switch (a_PacketType)
269  {
270  case RCON_PACKET_LOGIN:
271  {
272  if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0)
273  {
274  LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str());
275  SendResponse(0xffffffffU, RCON_PACKET_RESPONSE, 0, nullptr);
276  return false;
277  }
278  m_IsAuthenticated = true;
279 
280  LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str());
281 
282  // Send OK response:
283  SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, nullptr);
284  return true;
285  }
286 
287  case RCON_PACKET_COMMAND:
288  {
289  if (!m_IsAuthenticated)
290  {
291  char AuthNeeded[] = "You need to authenticate first!";
292  SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded);
293  return false;
294  }
295 
296  AString cmd(a_Payload, a_PayloadLength);
297  LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str());
298  cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID)));
299 
300  // Send an empty response:
301  SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, nullptr);
302  return true;
303  }
304  }
305 
306  // Unknown packet type, drop the connection:
307  LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.",
308  m_IPAddress.c_str(), a_PacketType
309  );
310  return false;
311 }
312 
313 
314 
315 
316 
318 {
319  const Byte * Buffer = reinterpret_cast<const Byte *>(a_Buffer);
320  return static_cast<UInt32>((Buffer[3] << 24) | (Buffer[2] << 16) | (Buffer[1] << 8) | Buffer[0]);
321 }
322 
323 
324 
325 
326 
327 void cRCONServer::cConnection::UIntToBuffer(UInt32 a_Value, char * a_Buffer)
328 {
329  a_Buffer[0] = static_cast<char>(a_Value & 0xff);
330  a_Buffer[1] = static_cast<char>((a_Value >> 8) & 0xff);
331  a_Buffer[2] = static_cast<char>((a_Value >> 16) & 0xff);
332  a_Buffer[3] = static_cast<char>((a_Value >> 24) & 0xff);
333 }
334 
335 
336 
337 
338 
339 void cRCONServer::cConnection::SendResponse(UInt32 a_RequestID, UInt32 a_PacketType, UInt32 a_PayloadLength, const char * a_Payload)
340 {
341  ASSERT((a_PayloadLength == 0) || (a_Payload != nullptr)); // Either zero data to send, or a valid payload ptr
342  ASSERT(m_Link != nullptr);
343 
344  char Buffer[12];
345  UInt32 Length = a_PayloadLength + 10;
346  UIntToBuffer(Length, Buffer);
347  UIntToBuffer(a_RequestID, Buffer + 4);
348  UIntToBuffer(a_PacketType, Buffer + 8);
349  m_Link->Send(Buffer, 12);
350  if (a_PayloadLength > 0)
351  {
352  m_Link->Send(a_Payload, a_PayloadLength);
353  }
354  m_Link->Send("\0", 2); // Send two zero chars as the padding
355 }
356 
357 
358 
359 
void ExecuteConsoleCommand(const AString &a_Cmd, cCommandOutputCallback &a_Output)
Executes a console command through the cServer class; does special handling for "stop" and "restart"...
Definition: Root.cpp:715
AString m_Password
Password for authentication.
Definition: RCONServer.h:90
cServerHandlePtrs m_ListenServers
The sockets for accepting RCON connections (one socket per port).
Definition: RCONServer.h:87
bool StringToInteger(const AString &a_str, T &a_Num)
Parses any integer type.
Definition: StringUtils.h:161
virtual void Out(const AString &a_Text) override
Called when the command wants to output anything; may be called multiple times.
Definition: RCONServer.cpp:92
bool ProcessPacket(UInt32 a_RequestID, UInt32 a_PacketType, UInt32 a_PayloadLength, const char *a_Payload)
Processes the given packet and sends the response; returns true if successful, false if the connectio...
Definition: RCONServer.cpp:266
cRCONServer & m_RCONServer
Server that owns this connection and processes requests.
Definition: RCONServer.h:54
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg) override
Called when the socket fails to listen on the specified port.
Definition: RCONServer.cpp:68
virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString &a_RemoteIPAddress, UInt16 a_RemotePort) override
Called when the TCP server created with Listen() receives a new incoming connection.
Definition: RCONServer.cpp:62
virtual ~cRCONServer()
Definition: RCONServer.cpp:125
void UIntToBuffer(UInt32 a_Value, char *a_Buffer)
Puts 4 bytes representing the int into the buffer.
Definition: RCONServer.cpp:327
cRCONServer(cServer &a_Server)
Definition: RCONServer.cpp:116
virtual void OnAccepted(cTCPLink &a_Link) override
Called when the TCP server created with Listen() creates a new link for an incoming connection...
Definition: RCONServer.cpp:67
cRCONServer & m_RCONServer
The RCON server instance that we&#39;re attached to.
Definition: RCONServer.cpp:56
Definition: Server.h:55
cTCPLinkPtr m_Link
The TCP link to the client.
Definition: RCONServer.h:57
std::vector< AString > AStringVector
Definition: StringUtils.h:14
cRCONServer::cConnection & m_Connection
Definition: RCONServer.cpp:104
virtual void OnRemoteClosed(void) override
Called when the remote end closes the connection.
Definition: RCONServer.cpp:247
virtual void OnReceivedData(const char *a_Data, size_t a_Length) override
Called when there&#39;s data incoming from the remote peer.
Definition: RCONServer.cpp:204
AString m_Buffer
Buffer for the incoming data.
Definition: RCONServer.h:51
virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
Called when the cTCPLink for the connection is created.
Definition: RCONServer.cpp:195
cRCONListenCallbacks(cRCONServer &a_RCONServer, UInt16 a_Port)
Definition: RCONServer.cpp:48
std::shared_ptr< cTCPLink > cTCPLinkPtr
Definition: Network.h:17
void LOGINFO(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:165
#define ASSERT(x)
Definition: Globals.h:335
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
#define LOGD(...)
Definition: LoggerSimple.h:40
virtual AString GetValueSet(const AString &keyname, const AString &valuename, const AString &defValue="")=0
Gets the value; if not found, write the default to the repository.
cConnection(cRCONServer &a_RCONServer, const AString &a_IPAddress)
Definition: RCONServer.cpp:184
unsigned short UInt16
Definition: Globals.h:114
std::shared_ptr< cCallbacks > cCallbacksPtr
Definition: Network.h:63
Callbacks used when listening for incoming connections as a server.
Definition: Network.h:244
std::string AString
Definition: StringUtils.h:13
virtual bool GetValueSetB(const AString &keyname, const AString &valuename, const bool defValue=false)=0
virtual void Finished(void) override
Called when the command processing has been finished.
Definition: RCONServer.cpp:97
void Initialize(cSettingsRepositoryInterface &a_Settings)
Definition: RCONServer.cpp:137
static cRoot * Get()
Definition: Root.h:51
void LOG(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:156
UInt16 m_Port
The port for which this instance is responsible.
Definition: RCONServer.cpp:59
static cServerHandlePtr Listen(UInt16 a_Port, cListenCallbacksPtr a_ListenCallbacks)
Opens up the specified port for incoming connections.
Interface that provides the methods available on a single TCP connection.
Definition: Network.h:33
unsigned int UInt32
Definition: Globals.h:113
AString m_IPAddress
Address of the client.
Definition: RCONServer.h:60
cRCONCommandOutput(cRCONServer::cConnection &a_Connection, UInt32 a_RequestID)
Definition: RCONServer.cpp:85
unsigned char Byte
Definition: Globals.h:117
AStringVector ReadUpgradeIniPorts(cSettingsRepositoryInterface &a_Settings, const AString &a_KeyName, const AString &a_PortsValueName, const AString &a_OldIPv4ValueName, const AString &a_OldIPv6ValueName, const AString &a_DefaultValue)
Reads the list of ports from the INI file, possibly upgrading from IPv4 / IPv6-specific values into n...
Definition: IniFile.cpp:924
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg) override
Called when an error is detected on the connection.
Definition: RCONServer.cpp:256
bool m_IsAuthenticated
Set to true if the client has successfully authenticated.
Definition: RCONServer.h:48
UInt32 UIntFromBuffer(const char *a_Buffer)
Reads 4 bytes from a_Buffer and returns the LE UInt32 they represent.
Definition: RCONServer.cpp:317
Interface for a callback that receives command output The Out() function is called for any output the...
Definition: CommandOutput.h:13
void SendResponse(UInt32 a_RequestID, UInt32 a_PacketType, UInt32 a_PayloadLength, const char *a_Payload)
Sends a RCON packet back to the client.
Definition: RCONServer.cpp:339
friend class cRCONCommandOutput
Definition: RCONServer.h:45