/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file network_admin.cpp Server part of the admin network protocol. */ #include "../stdafx.h" #include "../strings_func.h" #include "../timer/timer_game_calendar.h" #include "../timer/timer_game_calendar.h" #include "core/network_game_info.h" #include "network_admin.h" #include "network_base.h" #include "network_server.h" #include "../command_func.h" #include "../company_base.h" #include "../console_func.h" #include "../core/pool_func.hpp" #include "../map_func.h" #include "../rev.h" #include "../game/game.hpp" #include "table/strings.h" #include "../safeguards.h" /* This file handles all the admin network commands. */ /** Redirection of the (remote) console to the admin. */ AdminID _redirect_console_to_admin = AdminID::Invalid(); /** The pool with sockets/clients. */ NetworkAdminSocketPool _networkadminsocket_pool("NetworkAdminSocket"); INSTANTIATE_POOL_METHODS(NetworkAdminSocket) static NetworkAuthenticationDefaultPasswordProvider _admin_password_provider{_settings_client.network.admin_password}; ///< Provides the password validation for the game's password. static NetworkAuthenticationDefaultAuthorizedKeyHandler _admin_authorized_key_handler{_settings_client.network.admin_authorized_keys}; ///< Provides the authorized key handling for the game authentication. /** The timeout for authorisation of the client. */ static const std::chrono::seconds ADMIN_AUTHORISATION_TIMEOUT{10}; /** Frequencies, which may be registered for a certain update type. */ static const AdminUpdateFrequencies _admin_update_type_frequencies[] = { {AdminUpdateFrequency::Poll, AdminUpdateFrequency::Daily, AdminUpdateFrequency::Weekly, AdminUpdateFrequency::Monthly, AdminUpdateFrequency::Quarterly, AdminUpdateFrequency::Annually}, // ADMIN_UPDATE_DATE {AdminUpdateFrequency::Poll, AdminUpdateFrequency::Automatic, }, // ADMIN_UPDATE_CLIENT_INFO {AdminUpdateFrequency::Poll, AdminUpdateFrequency::Automatic, }, // ADMIN_UPDATE_COMPANY_INFO {AdminUpdateFrequency::Poll, AdminUpdateFrequency::Weekly, AdminUpdateFrequency::Monthly, AdminUpdateFrequency::Quarterly, AdminUpdateFrequency::Annually}, // ADMIN_UPDATE_COMPANY_ECONOMY {AdminUpdateFrequency::Poll, AdminUpdateFrequency::Weekly, AdminUpdateFrequency::Monthly, AdminUpdateFrequency::Quarterly, AdminUpdateFrequency::Annually}, // ADMIN_UPDATE_COMPANY_STATS { AdminUpdateFrequency::Automatic, }, // ADMIN_UPDATE_CHAT { AdminUpdateFrequency::Automatic, }, // ADMIN_UPDATE_CONSOLE {AdminUpdateFrequency::Poll, }, // ADMIN_UPDATE_CMD_NAMES { AdminUpdateFrequency::Automatic, }, // ADMIN_UPDATE_CMD_LOGGING { AdminUpdateFrequency::Automatic, }, // ADMIN_UPDATE_GAMESCRIPT }; /** Sanity check. */ static_assert(lengthof(_admin_update_type_frequencies) == ADMIN_UPDATE_END); /** * Create a new socket for the server side of the admin network. * @param index The index in the admin pool. * @param s The socket to connect with. */ ServerNetworkAdminSocketHandler::ServerNetworkAdminSocketHandler(AdminID index, SOCKET s) : NetworkAdminSocketPool::PoolItem<&_networkadminsocket_pool>(index), NetworkAdminSocketHandler(s) { this->status = AdminStatus::Inactive; this->connect_time = std::chrono::steady_clock::now(); } /** * Clear everything related to this admin. */ ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler() { Debug(net, 3, "[admin] '{}' ({}) has disconnected", this->admin_name, this->admin_version); if (_redirect_console_to_admin == this->index) _redirect_console_to_admin = AdminID::Invalid(); if (this->update_frequency[ADMIN_UPDATE_CONSOLE].Test(AdminUpdateFrequency::Automatic)) { this->update_frequency[ADMIN_UPDATE_CONSOLE] = {}; DebugReconsiderSendRemoteMessages(); } } /** * Whether a connection is allowed or not at this moment. * @return Whether the connection is allowed. */ /* static */ bool ServerNetworkAdminSocketHandler::AllowConnection() { return _settings_client.network.AdminAuthenticationConfigured() && ServerNetworkAdminSocketHandler::CanAllocateItem(); } /** Send the packets for the server sockets. */ /* static */ void ServerNetworkAdminSocketHandler::Send() { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::Iterate()) { if (as->status <= AdminStatus::Authenticate && std::chrono::steady_clock::now() > as->connect_time + ADMIN_AUTHORISATION_TIMEOUT) { Debug(net, 2, "[admin] Admin did not send its authorisation within {} seconds", std::chrono::duration_cast(ADMIN_AUTHORISATION_TIMEOUT).count()); as->CloseConnection(true); continue; } if (as->writable) { as->SendPackets(); } } } /** * Handle the acceptance of a connection. * @param s The socket of the new connection. * @param address The address of the peer. */ /* static */ void ServerNetworkAdminSocketHandler::AcceptConnection(SOCKET s, const NetworkAddress &address) { ServerNetworkAdminSocketHandler *as = ServerNetworkAdminSocketHandler::Create(s); as->address = address; // Save the IP of the client } /*********** * Sending functions for admin network ************/ /** * Send an error to the admin. * @param error The error to send. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendError(NetworkErrorCode error) { /* Whatever the error might be, authentication (keys) must be released as soon as possible. */ this->authentication_handler = nullptr; auto p = std::make_unique(this, PacketAdminType::ServerError); p->Send_uint8(to_underlying(error)); this->SendPacket(std::move(p)); std::string error_message = GetString(GetNetworkErrorMsg(error)); Debug(net, 1, "[admin] The admin '{}' ({}) made an error and has been disconnected: '{}'", this->admin_name, this->admin_version, error_message); return this->CloseConnection(true); } /** * Send the protocol version to the admin. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendProtocol() { this->status = AdminStatus::Active; auto p = std::make_unique(this, PacketAdminType::ServerProtocol); /* announce the protocol version */ p->Send_uint8(NETWORK_GAME_ADMIN_VERSION); for (AdminUpdateType i : EnumRange(ADMIN_UPDATE_END)) { p->Send_bool (true); p->Send_uint16(i); p->Send_uint16(_admin_update_type_frequencies[i].base()); } p->Send_bool(false); this->SendPacket(std::move(p)); return this->SendWelcome(); } /** * Send a welcome message to the admin. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome() { auto p = std::make_unique(this, PacketAdminType::ServerWelcome); p->Send_string(_settings_client.network.server_name); p->Send_string(GetNetworkRevisionString()); p->Send_bool (_network_dedicated); p->Send_string(""); // Used to be map-name. p->Send_uint32(_settings_game.game_creation.generation_seed); p->Send_uint8 (to_underlying(_settings_game.game_creation.landscape)); p->Send_uint32(TimerGameCalendar::ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1).base()); p->Send_uint16(Map::SizeX()); p->Send_uint16(Map::SizeY()); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Tell the admin we started a new game. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendNewGame() { auto p = std::make_unique(this, PacketAdminType::ServerNewGame); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Tell the admin we're shutting down. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendShutdown() { auto p = std::make_unique(this, PacketAdminType::ServerShutdown); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Tell the admin the date. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendDate() { auto p = std::make_unique(this, PacketAdminType::ServerDate); p->Send_uint32(TimerGameCalendar::date.base()); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Tell the admin that a client joined. * @param client_id The client that joined. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientJoin(ClientID client_id) { auto p = std::make_unique(this, PacketAdminType::ServerClientJoin); p->Send_uint32(client_id); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send an initial set of data from some client's information. * @param cs The socket of the client. * @param ci The information about the client. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientInfo(const NetworkClientSocket *cs, const NetworkClientInfo *ci) { /* Only send data when we're a proper client, not just someone trying to query the server. */ if (ci == nullptr) return NETWORK_RECV_STATUS_OKAY; auto p = std::make_unique(this, PacketAdminType::ServerClientInfo); p->Send_uint32(ci->client_id); p->Send_string(cs == nullptr ? "" : const_cast(cs->client_address).GetHostname()); p->Send_string(ci->client_name); p->Send_uint8 (0); // Used to be language p->Send_uint32(ci->join_date.base()); p->Send_uint8 (ci->client_playas); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send an update for some client's information. * @param ci The information about a client. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientUpdate(const NetworkClientInfo *ci) { auto p = std::make_unique(this, PacketAdminType::ServerClientUpdate); p->Send_uint32(ci->client_id); p->Send_string(ci->client_name); p->Send_uint8 (ci->client_playas); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Tell the admin that a client quit. * @param client_id The client that quit. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientQuit(ClientID client_id) { auto p = std::make_unique(this, PacketAdminType::ServerClientQuit); p->Send_uint32(client_id); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Tell the admin that a client made an error. * @param client_id The client that made the error. * @param error The error that was made. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientError(ClientID client_id, NetworkErrorCode error) { auto p = std::make_unique(this, PacketAdminType::ServerClientError); p->Send_uint32(client_id); p->Send_uint8(to_underlying(error)); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Tell the admin that a new company was founded. * @param company_id The company that was founded. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyNew(CompanyID company_id) { auto p = std::make_unique(this, PacketAdminType::ServerCompanyNew); p->Send_uint8(company_id); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send the admin some information about a company. * @param c The company to send the information about. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyInfo(const Company *c) { auto p = std::make_unique(this, PacketAdminType::ServerCompanyInfo); p->Send_uint8 (c->index); p->Send_string(GetString(STR_COMPANY_NAME, c->index)); p->Send_string(GetString(STR_PRESIDENT_NAME, c->index)); p->Send_uint8(to_underlying(c->colour)); p->Send_bool (true); p->Send_uint32(c->inaugurated_year.base()); p->Send_bool (c->is_ai); p->Send_uint8 (CeilDiv(c->months_of_bankruptcy, 3)); // send as quarters_of_bankruptcy this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send an update about a company. * @param c The company to send the update of. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyUpdate(const Company *c) { auto p = std::make_unique(this, PacketAdminType::ServerCompanyUpdate); p->Send_uint8 (c->index); p->Send_string(GetString(STR_COMPANY_NAME, c->index)); p->Send_string(GetString(STR_PRESIDENT_NAME, c->index)); p->Send_uint8(to_underlying(c->colour)); p->Send_bool (true); p->Send_uint8 (CeilDiv(c->months_of_bankruptcy, 3)); // send as quarters_of_bankruptcy this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Tell the admin that a company got removed. * @param company_id The company that got removed. * @param acrr The reason for removal, e.g. bankruptcy or merger. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason acrr) { auto p = std::make_unique(this, PacketAdminType::ServerCompanyRemove); p->Send_uint8(company_id); p->Send_uint8(acrr); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send economic information of all companies. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyEconomy() { for (const Company *company : Company::Iterate()) { /* Get the income. */ Money income = -std::reduce(std::begin(company->yearly_expenses[0]), std::end(company->yearly_expenses[0])); auto p = std::make_unique(this, PacketAdminType::ServerCompanyEconomy); p->Send_uint8(company->index); /* Current information. */ p->Send_uint64(company->money); p->Send_uint64(company->current_loan); p->Send_uint64(income); p->Send_uint16(static_cast(std::min(UINT16_MAX, company->cur_economy.delivered_cargo.GetSum()))); /* Send stats for the last 2 quarters. */ for (uint i = 0; i < 2; i++) { p->Send_uint64(company->old_economy[i].company_value); p->Send_uint16(company->old_economy[i].performance_history); p->Send_uint16(static_cast(std::min(UINT16_MAX, company->old_economy[i].delivered_cargo.GetSum()))); } this->SendPacket(std::move(p)); } return NETWORK_RECV_STATUS_OKAY; } /** * Send statistics about the companies. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyStats() { /* Fetch the latest version of the stats. */ NetworkCompanyStatsArray company_stats = NetworkGetCompanyStats(); /* Go through all the companies. */ for (const Company *company : Company::Iterate()) { auto p = std::make_unique(this, PacketAdminType::ServerCompanyStatistics); /* Send the information. */ p->Send_uint8(company->index); for (uint16_t value : company_stats[company->index].num_vehicle) p->Send_uint16(value); for (uint16_t value : company_stats[company->index].num_station) p->Send_uint16(value); this->SendPacket(std::move(p)); } return NETWORK_RECV_STATUS_OKAY; } /** * Send a chat message. * @param action The action associated with the message. * @param desttype The destination type. * @param client_id The origin of the chat message. * @param msg The actual message. * @param data Arbitrary extra data. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendChat(NetworkAction action, NetworkChatDestinationType desttype, ClientID client_id, std::string_view msg, int64_t data) { auto p = std::make_unique(this, PacketAdminType::ServerChat); p->Send_uint8(to_underlying(action)); p->Send_uint8(to_underlying(desttype)); p->Send_uint32(client_id); p->Send_string(msg); p->Send_uint64(data); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send a notification indicating the rcon command has completed. * @param command The original command sent. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendRconEnd(std::string_view command) { auto p = std::make_unique(this, PacketAdminType::ServerRemoteConsoleCommandEnd); p->Send_string(command); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send the reply of an rcon command. * @param colour The colour of the text. * @param result The result of the command. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendRcon(uint16_t colour, std::string_view result) { auto p = std::make_unique(this, PacketAdminType::ServerRemoteConsoleCommand); p->Send_uint16(colour); p->Send_string(result); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminRemoteConsoleCommand(Packet &p) { if (this->status <= AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); std::string command = p.Recv_string(NETWORK_RCONCOMMAND_LENGTH); Debug(net, 3, "[admin] Rcon command from '{}' ({}): {}", this->admin_name, this->admin_version, command); _redirect_console_to_admin = this->index; IConsoleCmdExec(command); _redirect_console_to_admin = AdminID::Invalid(); return this->SendRconEnd(command); } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminGameScript(Packet &p) { if (this->status <= AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); std::string json = p.Recv_string(NETWORK_GAMESCRIPT_JSON_LENGTH); Debug(net, 6, "[admin] GameScript JSON from '{}' ({}): {}", this->admin_name, this->admin_version, json); Game::NewEvent(new ScriptEventAdminPort(json)); return NETWORK_RECV_STATUS_OKAY; } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminPing(Packet &p) { if (this->status <= AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); uint32_t d1 = p.Recv_uint32(); Debug(net, 6, "[admin] Ping from '{}' ({}): {}", this->admin_name, this->admin_version, d1); return this->SendPong(d1); } /** * Send console output of other clients. * @param origin The origin of the string. * @param string The string that's put on the console. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendConsole(std::string_view origin, std::string_view string) { /* If the length of both strings, plus the 2 '\0' terminations and 3 bytes of the packet * are bigger than the MTU, just ignore the message. Better safe than sorry. It should * never occur though as the longest strings are chat messages, which are still 30% * smaller than COMPAT_MTU. */ if (origin.size() + string.size() + 2 + 3 >= COMPAT_MTU) return NETWORK_RECV_STATUS_OKAY; auto p = std::make_unique(this, PacketAdminType::ServerConsole); p->Send_string(origin); p->Send_string(string); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send GameScript JSON output. * @param json The JSON string. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendGameScript(std::string_view json) { auto p = std::make_unique(this, PacketAdminType::ServerGameScript); p->Send_string(json); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send ping-reply (pong) to admin. * @param d1 Request ID from the admin, to return back to them. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendPong(uint32_t d1) { auto p = std::make_unique(this, PacketAdminType::ServerPong); p->Send_uint32(d1); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send the names of the commands. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames() { auto p = std::make_unique(this, PacketAdminType::ServerCommandNames); for (uint16_t i = 0; i < to_underlying(Commands::End); i++) { std::string_view cmdname = GetCommandName(static_cast(i)); /* Should COMPAT_MTU be exceeded, start a new packet * (magic 5: 1 bool "more data" and one uint16_t "command id", one * byte for string '\0' termination and 1 bool "no more data" */ if (!p->CanWriteToPacket(cmdname.size() + 5)) { p->Send_bool(false); this->SendPacket(std::move(p)); p = std::make_unique(this, PacketAdminType::ServerCommandNames); } p->Send_bool(true); p->Send_uint16(i); p->Send_string(cmdname); } /* Marker to notify the end of the packet has been reached. */ p->Send_bool(false); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send a command for logging purposes. * @param client_id The client executing the command. * @param cp The command that would be executed. * @return The new state the network. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdLogging(ClientID client_id, const CommandPacket &cp) { auto p = std::make_unique(this, PacketAdminType::ServerCommandLogging); p->Send_uint32(client_id); p->Send_uint8 (cp.company); p->Send_uint16(to_underlying(cp.cmd)); p->Send_buffer(cp.data); p->Send_uint32(cp.frame); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /*********** * Receiving functions ************/ NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminJoin(Packet &p) { if (this->status != AdminStatus::Inactive) return this->SendError(NetworkErrorCode::NotExpected); if (!_settings_client.network.allow_insecure_admin_login) { /* You're not authorized to login using this method. */ return this->SendError(NetworkErrorCode::NotAuthorized); } std::string password = p.Recv_string(NETWORK_PASSWORD_LENGTH); if (_settings_client.network.admin_password.empty() || _settings_client.network.admin_password != password) { /* Password is invalid */ return this->SendError(NetworkErrorCode::WrongPassword); } this->admin_name = p.Recv_string(NETWORK_CLIENT_NAME_LENGTH); this->admin_version = p.Recv_string(NETWORK_REVISION_LENGTH); if (this->admin_name.empty() || this->admin_version.empty()) { /* no name or version supplied */ return this->SendError(NetworkErrorCode::IllegalPacket); } Debug(net, 3, "[admin] '{}' ({}) has connected", this->admin_name, this->admin_version); return this->SendProtocol(); } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminQuit(Packet &) { /* The admin is leaving nothing else to do */ return this->CloseConnection(); } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminUpdateFrequency(Packet &p) { if (this->status <= AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); AdminUpdateType type = (AdminUpdateType)p.Recv_uint16(); AdminUpdateFrequencies freq = static_cast(p.Recv_uint16()); if (type >= ADMIN_UPDATE_END || !_admin_update_type_frequencies[type].All(freq)) { /* The server does not know of this UpdateType. */ Debug(net, 1, "[admin] Not supported update frequency {} ({}) from '{}' ({})", type, freq, this->admin_name, this->admin_version); return this->SendError(NetworkErrorCode::IllegalPacket); } this->update_frequency[type] = freq; if (type == ADMIN_UPDATE_CONSOLE) DebugReconsiderSendRemoteMessages(); return NETWORK_RECV_STATUS_OKAY; } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminPoll(Packet &p) { if (this->status <= AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); AdminUpdateType type = (AdminUpdateType)p.Recv_uint8(); uint32_t d1 = p.Recv_uint32(); switch (type) { case ADMIN_UPDATE_DATE: /* The admin is requesting the current date. */ this->SendDate(); break; case ADMIN_UPDATE_CLIENT_INFO: /* The admin is requesting client info. */ if (d1 == UINT32_MAX) { this->SendClientInfo(nullptr, NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER)); for (const NetworkClientSocket *cs : NetworkClientSocket::Iterate()) { this->SendClientInfo(cs, cs->GetInfo()); } } else { if (d1 == CLIENT_ID_SERVER) { this->SendClientInfo(nullptr, NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER)); } else { const NetworkClientSocket *cs = NetworkClientSocket::GetByClientID((ClientID)d1); if (cs != nullptr) this->SendClientInfo(cs, cs->GetInfo()); } } break; case ADMIN_UPDATE_COMPANY_INFO: /* The admin is asking for company info. */ if (d1 == UINT32_MAX) { for (const Company *company : Company::Iterate()) { this->SendCompanyInfo(company); } } else { const Company *company = Company::GetIfValid(d1); if (company != nullptr) this->SendCompanyInfo(company); } break; case ADMIN_UPDATE_COMPANY_ECONOMY: /* The admin is requesting economy info. */ this->SendCompanyEconomy(); break; case ADMIN_UPDATE_COMPANY_STATS: /* the admin is requesting company stats. */ this->SendCompanyStats(); break; case ADMIN_UPDATE_CMD_NAMES: /* The admin is requesting the names of DoCommands. */ this->SendCmdNames(); break; default: /* An unsupported "poll" update type. */ Debug(net, 1, "[admin] Not supported poll {} ({}) from '{}' ({}).", type, d1, this->admin_name, this->admin_version); return this->SendError(NetworkErrorCode::IllegalPacket); } return NETWORK_RECV_STATUS_OKAY; } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminChat(Packet &p) { if (this->status <= AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); NetworkAction action = static_cast(p.Recv_uint8()); NetworkChatDestinationType desttype = static_cast(p.Recv_uint8()); int dest = p.Recv_uint32(); std::string msg = p.Recv_string(NETWORK_CHAT_LENGTH); switch (action) { case NetworkAction::ChatBroadcast: case NetworkAction::ChatClient: case NetworkAction::ChatTeam: case NetworkAction::ServerMessage: NetworkServerSendChat(action, desttype, dest, msg, _network_own_client_id, 0, true); break; default: Debug(net, 1, "[admin] Invalid chat action {} from admin '{}' ({}).", action, this->admin_name, this->admin_version); return this->SendError(NetworkErrorCode::IllegalPacket); } return NETWORK_RECV_STATUS_OKAY; } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminExternalChat(Packet &p) { if (this->status <= AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); std::string source = p.Recv_string(NETWORK_CHAT_LENGTH); ExtendedTextColour colour = ExtendedTextColour::FromNetwork(p.Recv_uint16()); std::string user = p.Recv_string(NETWORK_CHAT_LENGTH); std::string msg = p.Recv_string(NETWORK_CHAT_LENGTH); if (!IsValidConsoleColour(colour)) { Debug(net, 1, "[admin] Not supported chat colour {} ({}, {}, {}) from '{}' ({}).", colour.ToNetwork(), source, user, msg, this->admin_name, this->admin_version); return this->SendError(NetworkErrorCode::IllegalPacket); } NetworkServerSendExternalChat(source, colour, user, msg); return NETWORK_RECV_STATUS_OKAY; } /* * Secure authentication send and receive methods. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminJoinSecure(Packet &p) { if (this->status != AdminStatus::Inactive) return this->SendError(NetworkErrorCode::NotExpected); this->admin_name = p.Recv_string(NETWORK_CLIENT_NAME_LENGTH); this->admin_version = p.Recv_string(NETWORK_REVISION_LENGTH); NetworkAuthenticationMethodMask method_mask{p.Recv_uint16()}; /* Always exclude key exchange only, as that provides no credential checking. */ method_mask.Reset(NetworkAuthenticationMethod::X25519_KeyExchangeOnly); if (this->admin_name.empty() || this->admin_version.empty()) { /* No name or version supplied. */ return this->SendError(NetworkErrorCode::IllegalPacket); } auto handler = NetworkAuthenticationServerHandler::Create(&_admin_password_provider, &_admin_authorized_key_handler, method_mask); if (!handler->CanBeUsed()) return this->SendError(NetworkErrorCode::NoAuthenticationMethodAvailable); this->authentication_handler = std::move(handler); Debug(net, 3, "[admin] '{}' ({}) has connected", this->admin_name, this->admin_version); return this->SendAuthRequest(); } /** * Send the client a request to authenticate. * @return The state the network should have. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendAuthRequest() { this->status = AdminStatus::Authenticate; Debug(net, 6, "[admin] '{}' ({}) authenticating using {}", this->admin_name, this->admin_version, this->authentication_handler->GetName()); auto p = std::make_unique(this, PacketAdminType::ServerAuthenticationRequest); this->authentication_handler->SendRequest(*p); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } /** * Send the client the message to enable encryption. * @return The state the network should have. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendEnableEncryption() { if (this->status != AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); auto p = std::make_unique(this, PacketAdminType::ServerEnableEncryption); this->authentication_handler->SendEnableEncryption(*p); this->SendPacket(std::move(p)); return NETWORK_RECV_STATUS_OKAY; } NetworkRecvStatus ServerNetworkAdminSocketHandler::ReceiveAdminAuthenticationResponse(Packet &p) { if (this->status != AdminStatus::Authenticate) return this->SendError(NetworkErrorCode::NotExpected); switch (this->authentication_handler->ReceiveResponse(p)) { case NetworkAuthenticationServerHandler::ResponseResult::Authenticated: Debug(net, 3, "[admin] '{}' ({}) authenticated", this->admin_name, this->admin_version); this->SendEnableEncryption(); this->receive_encryption_handler = this->authentication_handler->CreateClientToServerEncryptionHandler(); this->send_encryption_handler = this->authentication_handler->CreateServerToClientEncryptionHandler(); this->authentication_handler = nullptr; return this->SendProtocol(); case NetworkAuthenticationServerHandler::ResponseResult::RetryNextMethod: Debug(net, 6, "[admin] '{}' ({}) authentication failed, trying next method", this->admin_name, this->admin_version); return this->SendAuthRequest(); case NetworkAuthenticationServerHandler::ResponseResult::NotAuthenticated: default: Debug(net, 3, "[admin] '{}' ({}) authentication failed", this->admin_name, this->admin_version); return this->SendError(NetworkErrorCode::WrongPassword); } } /* * Useful wrapper functions */ /** * Notify the admin network of a new client (if they did opt in for the respective update). * @param cs the client info. * @param new_client if this is a new client, send the respective packet too. */ void NetworkAdminClientInfo(const NetworkClientSocket *cs, bool new_client) { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_CLIENT_INFO].Test(AdminUpdateFrequency::Automatic)) { as->SendClientInfo(cs, cs->GetInfo()); if (new_client) { as->SendClientJoin(cs->client_id); } } } } /** * Notify the admin network of a client update (if they did opt in for the respective update). * @param ci the client info. */ void NetworkAdminClientUpdate(const NetworkClientInfo *ci) { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_CLIENT_INFO].Test(AdminUpdateFrequency::Automatic)) { as->SendClientUpdate(ci); } } } /** * Notify the admin network that a client quit (if they have opt in for the respective update). * @param client_id of the client that quit. */ void NetworkAdminClientQuit(ClientID client_id) { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_CLIENT_INFO].Test(AdminUpdateFrequency::Automatic)) { as->SendClientQuit(client_id); } } } /** * Notify the admin network of a client error (if they have opt in for the respective update). * @param client_id the client that made the error. * @param error_code the error that was caused. */ void NetworkAdminClientError(ClientID client_id, NetworkErrorCode error_code) { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_CLIENT_INFO].Test(AdminUpdateFrequency::Automatic)) { as->SendClientError(client_id, error_code); } } } /** * Notify the admin network of a new company. * @param company the company of which details will be sent into the admin network. */ void NetworkAdminCompanyNew(const Company *company) { if (company == nullptr) { Debug(net, 1, "[admin] Empty company given for update"); return; } for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_COMPANY_INFO] != AdminUpdateFrequency::Automatic) continue; as->SendCompanyNew(company->index); as->SendCompanyInfo(company); } } /** * Notify the admin network of company updates. * @param company company of which updates are going to be sent into the admin network. */ void NetworkAdminCompanyUpdate(const Company *company) { if (company == nullptr) return; for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_COMPANY_INFO] != AdminUpdateFrequency::Automatic) continue; as->SendCompanyUpdate(company); } } /** * Notify the admin network of a company to be removed (including the reason why). * @param company_id ID of the company that got removed. * @param bcrr the reason why the company got removed (e.g. bankruptcy). */ void NetworkAdminCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason bcrr) { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { as->SendCompanyRemove(company_id, bcrr); } } /** * Send chat to the admin network (if they did opt in for the respective update). * @param action The action that's performed. * @param desttype The type of destination. * @param client_id The destination client. * @param msg The actual message. * @param data Arbitrary data. * @param from_admin Whether the message is coming from the admin. */ void NetworkAdminChat(NetworkAction action, NetworkChatDestinationType desttype, ClientID client_id, std::string_view msg, int64_t data, bool from_admin) { if (from_admin) return; for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_CHAT].Test(AdminUpdateFrequency::Automatic)) { as->SendChat(action, desttype, client_id, msg, data); } } } /** * Pass the rcon reply to the admin. * @param admin_index The admin to give the reply. * @param colour_code The colour of the string. * @param string The string to show. */ void NetworkServerSendAdminRcon(AdminID admin_index, ExtendedTextColour colour_code, std::string_view string) { ServerNetworkAdminSocketHandler::Get(admin_index)->SendRcon(colour_code.ToNetwork(), string); } /** * Send console to the admin network (if they did opt in for the respective update). * @param origin the origin of the message. * @param string the message as printed on the console. */ void NetworkAdminConsole(std::string_view origin, std::string_view string) { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_CONSOLE].Test(AdminUpdateFrequency::Automatic)) { as->SendConsole(origin, string); } } } /** * Send GameScript JSON to the admin network (if they did opt in for the respective update). * @param json The JSON data as received from the GameScript. */ void NetworkAdminGameScript(std::string_view json) { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_GAMESCRIPT].Test(AdminUpdateFrequency::Automatic)) { as->SendGameScript(json); } } } /** * Distribute CommandPacket details over the admin network for logging purposes. * @param owner The owner of the CommandPacket (who sent us the CommandPacket). * @param cp The CommandPacket to be distributed. */ void NetworkAdminCmdLogging(const NetworkClientSocket *owner, const CommandPacket &cp) { ClientID client_id = owner == nullptr ? _network_own_client_id : owner->client_id; for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { if (as->update_frequency[ADMIN_UPDATE_CMD_LOGGING].Test(AdminUpdateFrequency::Automatic)) { as->SendCmdLogging(client_id, cp); } } } /** * Send a Welcome packet to all connected admins */ void ServerNetworkAdminSocketHandler::WelcomeAll() { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { as->SendWelcome(); } } /** * Send (push) updates to the admin network as they have registered for these updates. * @param freq the frequency to be processed. */ void NetworkAdminUpdate(AdminUpdateFrequency freq) { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) { for (AdminUpdateType i : EnumRange(ADMIN_UPDATE_END)) { if (as->update_frequency[i].Test(freq)) { /* Update the admin for the required details */ switch (i) { case ADMIN_UPDATE_DATE: as->SendDate(); break; case ADMIN_UPDATE_COMPANY_ECONOMY: as->SendCompanyEconomy(); break; case ADMIN_UPDATE_COMPANY_STATS: as->SendCompanyStats(); break; default: NOT_REACHED(); } } } } }