Mon, 15 Dec 2014 02:15:09 +0200
- added a simple command-line interface (you can now supply host and password on the command-line)
- the password is now masked with asterisks when being input
#include "rconsession.h" #include "../interface.h" RCONSession* g_rconSession = nullptr; // ------------------------------------------------------------------------------------------------- // RCONSession::RCONSession() : m_state (RCON_DISCONNECTED), m_lastPing (0), m_numAdmins (0) { if (g_rconSession != NULL) { g_rconSession->disconnect(); delete g_rconSession; } g_rconSession = this; } // ------------------------------------------------------------------------------------------------- // STATIC METHOD RCONSession::new_session() -> RCONSession* { RCONSession* session = new RCONSession; if (not session->socket()->set_blocking (false)) { print ("unable to set socket as non-blocking: %s\n", session->socket()->error_string().chars()); delete session; return nullptr; } return session; } // ------------------------------------------------------------------------------------------------- // RCONSession::~RCONSession() { disconnect(); if (g_rconSession == this) g_rconSession = nullptr; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::connect (IPAddress address) -> void { m_address = address; m_state = RCON_CONNECTING; Interface::update_statusbar(); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::disconnect() -> void { if (m_state > RCON_CONNECTING) { // Say goodbye to remote Bytestream packet; packet.write_byte (CLRC_DISCONNECT); this->send (packet); print ("Disconnected from %1\n", m_address.to_string (IP_WITH_PORT)); Interface::update_statusbar(); } m_state = RCON_DISCONNECTED; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::send (const Bytestream& packet) -> void { m_socket.send (m_address, packet); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::tick() -> void { time_t now; time (&now); if (m_lastPing < now) { if (m_state == RCON_CONNECTING) { send_hello(); } else if (m_state == RCON_AUTHENTICATING) { send_password(); } else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) { Bytestream packet; packet.write_byte (CLRC_PONG); send (packet); bump_last_ping(); } } for (Datagram datagram; m_socket.read (datagram);) handle_packet (datagram.data, datagram.from); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::handle_packet (Bytestream& packet, const IPAddress& from) -> void { if (from != m_address) return; try { while (packet.bytes_left() > 0) { int header = packet.read_byte(); switch (ServerResponse (header)) { case SVRC_OLDPROTOCOL: print ("Your RCON client is using outdated protocol.\n"); m_state = RCON_DISCONNECTED; break; case SVRC_BANNED: print ("You have been banned from the server.\n"); m_state = RCON_DISCONNECTED; break; case SVRC_SALT: { String salt = packet.read_string(); m_salt = salt; m_state = RCON_AUTHENTICATING; send_password(); } break; case SVRC_INVALIDPASSWORD: print ("Password incorrect.\n"); m_state = RCON_DISCONNECTED; break; case SVRC_MESSAGE: { String message = packet.read_string(); if (message.ends_with ("\n")) message.remove_from_end (1); print ("%1\n", message); } break; case SVRC_LOGGEDIN: print ("Login successful!\n"); m_serverProtocol = packet.read_byte(); m_hostname = packet.read_string(); Interface::set_title (m_hostname); m_state = RCON_CONNECTED; for (int i = packet.read_byte(); i > 0; --i) process_server_updates (packet); print ("Previous messages:\n"); for (int i = packet.read_byte(); i > 0; --i) { String message = packet.read_string(); message.normalize(); print ("--- %1\n", message); } print ("End of previous messages.\n"); break; case SVRC_UPDATE: process_server_updates (packet); break; } } } catch (std::exception& e) { print ("error while reading packet: %1\n", e.what()); } } METHOD RCONSession::process_server_updates (Bytestream& packet) -> void { switch (RCONUpdateType (packet.read_byte())) { case SVRCU_PLAYERDATA: { Vector<String> players; for (int i = packet.read_byte(); i > 0; --i) players << packet.read_string(); print ("Players: %1\n", players); } break; case SVRCU_ADMINCOUNT: m_numAdmins = packet.read_byte(); Interface::update_statusbar(); break; case SVRCU_MAP: m_level = packet.read_string(); Interface::update_statusbar(); break; } } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::socket() -> UDPSocket* { return &m_socket; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::send_hello() -> void { print ("Connecting to %1...\n", m_address.to_string (IP_WITH_PORT)); Bytestream packet; packet.write_byte (CLRC_BEGINCONNECTION); packet.write_byte (RCON_PROTOCOL_VERSION); send (packet); bump_last_ping(); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::send_password() -> void { print ("Authenticating...\n"); Bytestream packet; packet.write_byte (CLRC_PASSWORD); packet.write_string ((m_salt + m_password).md5()); send (packet); bump_last_ping(); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::set_password (const String& password) -> void { m_password = password; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::bump_last_ping() -> void { time_t now; time (&now); m_lastPing = now; } // ------------------------------------------------------------------------------------------------- // STATIC METHOD RCONSession::get_session() -> RCONSession* { return g_rconSession; } // ------------------------------------------------------------------------------------------------- // Returns true if the message was successfully sent. // METHOD RCONSession::send_command (const String& message) -> bool { if (m_state != RCON_CONNECTED or message.is_empty()) return false; Bytestream packet; packet.write_byte (CLRC_COMMAND); packet.write_string (message); send (packet); bump_last_ping(); return true; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::state() const -> RCONSessionState { return m_state; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::address() const -> const IPAddress& { return m_address; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::num_admins() const -> int { return m_numAdmins; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::level() const -> const String& { return m_level; }