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  {
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 (const 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 (const 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()->QueueExecuteConsoleCommand(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 
unsigned int UInt32
Definition: Globals.h:157
#define ASSERT(x)
Definition: Globals.h:276
unsigned short UInt16
Definition: Globals.h:158
unsigned char Byte
Definition: Globals.h:161
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:923
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
void LOG(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:55
#define LOGD
Definition: LoggerSimple.h:83
void LOGINFO(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:61
std::shared_ptr< cTCPLink > cTCPLinkPtr
Definition: Network.h:25
@ RCON_PACKET_LOGIN
Definition: RCONServer.cpp:31
@ RCON_PACKET_COMMAND
Definition: RCONServer.cpp:30
@ RCON_PACKET_RESPONSE
Definition: RCONServer.cpp:34
std::vector< AString > AStringVector
Definition: StringUtils.h:12
std::string AString
Definition: StringUtils.h:11
bool StringToInteger(const AString &a_str, T &a_Num)
Parses any integer type.
Definition: StringUtils.h:143
Interface for a callback that receives command output The Out() function is called for any output the...
Definition: CommandOutput.h:16
Interface that provides the methods available on a single TCP connection.
Definition: Network.h:42
std::shared_ptr< cCallbacks > cCallbacksPtr
Definition: Network.h:71
static cServerHandlePtr Listen(UInt16 a_Port, cListenCallbacksPtr a_ListenCallbacks)
Opens up the specified port for incoming connections.
Callbacks used when listening for incoming connections as a server.
Definition: Network.h:254
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
cRCONListenCallbacks(cRCONServer &a_RCONServer, UInt16 a_Port)
Definition: RCONServer.cpp:48
cRCONServer & m_RCONServer
The RCON server instance that we're attached to.
Definition: RCONServer.cpp:56
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
UInt16 m_Port
The port for which this instance is responsible.
Definition: RCONServer.cpp:59
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 void Finished(void) override
Called when the command processing has been finished.
Definition: RCONServer.cpp:97
cRCONServer::cConnection & m_Connection
Definition: RCONServer.cpp:104
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
cRCONCommandOutput(cRCONServer::cConnection &a_Connection, UInt32 a_RequestID)
Definition: RCONServer.cpp:85
cServerHandlePtrs m_ListenServers
The sockets for accepting RCON connections (one socket per port).
Definition: RCONServer.h:87
AString m_Password
Password for authentication.
Definition: RCONServer.h:90
void Initialize(cSettingsRepositoryInterface &a_Settings)
Definition: RCONServer.cpp:137
cRCONServer(cServer &a_Server)
Definition: RCONServer.cpp:116
virtual ~cRCONServer()
Definition: RCONServer.cpp:125
friend class cRCONCommandOutput
Definition: RCONServer.h:35
virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
Called when the cTCPLink for the connection is created.
Definition: RCONServer.cpp:195
virtual void OnReceivedData(const char *a_Data, size_t a_Length) override
Called when there's data incoming from the remote peer.
Definition: RCONServer.cpp:204
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
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
void UIntToBuffer(UInt32 a_Value, char *a_Buffer)
Puts 4 bytes representing the int into the buffer.
Definition: RCONServer.cpp:327
cConnection(cRCONServer &a_RCONServer, const AString &a_IPAddress)
Definition: RCONServer.cpp:184
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg) override
Called when an error is detected on the connection.
Definition: RCONServer.cpp:256
virtual void OnRemoteClosed(void) override
Called when the remote end closes the connection.
Definition: RCONServer.cpp:247
UInt32 UIntFromBuffer(const char *a_Buffer)
Reads 4 bytes from a_Buffer and returns the LE UInt32 they represent.
Definition: RCONServer.cpp:317
static cRoot * Get()
Definition: Root.h:52
void QueueExecuteConsoleCommand(const AString &a_Cmd, cCommandOutputCallback &a_Output)
Queues a console command for execution through the cServer class.
Definition: Root.cpp:496
Definition: Server.h:56
virtual bool GetValueSetB(const AString &keyname, const AString &valuename, const bool defValue=false)=0
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.