From d8f34651de6756374a2220036ffdd6e3b9c18399 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 8 Jul 2019 20:25:37 -0500 Subject: [PATCH] Streamline worldserver authorization handlers, cleanup and refactoring --- common/servertalk.h | 18 +- loginserver/database.cpp | 79 +-- loginserver/database.h | 37 +- loginserver/login_util/login_schema.sql | 13 +- loginserver/loginserver_webserver.cpp | 4 +- loginserver/server_manager.cpp | 36 +- loginserver/world_server.cpp | 881 ++++++++++++++++-------- loginserver/world_server.h | 70 +- world/login_server.cpp | 20 +- 9 files changed, 766 insertions(+), 392 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index 242635b1d..7584d4942 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -455,15 +455,15 @@ struct ServerLSInfo_Struct { }; struct ServerNewLSInfo_Struct { - char name[201]; // name the worldserver wants - char shortname[50]; // shortname the worldserver wants - char remote_address[125]; // DNS address of the server - char local_address[125]; // DNS address of the server - char account[31]; // account name for the worldserver - char password[31]; // password for the name - char protocolversion[25]; // Major protocol version number - char serverversion[64]; // minor server software version number - uint8 servertype; // 0=world, 1=chat, 2=login, 3=MeshLogin + char server_long_name[201]; // name the worldserver wants + char server_short_name[50]; // shortname the worldserver wants + char remote_ip_address[125]; // DNS address of the server + char local_ip_address[125]; // DNS address of the server + char account_name[31]; // account name for the worldserver + char account_password[31]; // password for the name + char protocol_version[25]; // Major protocol version number + char server_version[64]; // minor server software version number + uint8 server_process_type; // 0=world, 1=chat, 2=login, 3=MeshLogin }; struct ServerLSAccountUpdate_Struct { // for updating info on login server diff --git a/loginserver/database.cpp b/loginserver/database.cpp index e4a10d19e..82d8d2d72 100644 --- a/loginserver/database.cpp +++ b/loginserver/database.cpp @@ -319,27 +319,15 @@ void Database::UpdateLoginHash( } /** - * @param long_name * @param short_name - * @param id - * @param desc - * @param list_id - * @param trusted - * @param list_desc - * @param account - * @param password + * @param remote_ip + * @param local_ip * @return */ -bool Database::GetWorldRegistration( - std::string long_name, - std::string short_name, - unsigned int &id, - std::string &desc, - unsigned int &list_id, - unsigned int &trusted, - std::string &list_desc, - std::string &account, - std::string &password +Database::DbWorldRegistration Database::GetWorldRegistration( + const std::string &short_name, + const std::string &remote_ip, + const std::string &local_ip ) { auto query = fmt::format( @@ -354,43 +342,46 @@ bool Database::GetWorldRegistration( " login_world_servers AS WSR\n" " JOIN login_server_list_types AS SLT ON WSR.login_server_list_type_id = SLT.id\n" "WHERE\n" - " WSR.short_name = '{0}' LIMIT 1", - EscapeString(short_name) + " WSR.short_name = '{0}' AND (WSR.last_ip_address = '{1}' OR WSR.last_ip_address = '{2}') LIMIT 1", + EscapeString(short_name), + EscapeString(remote_ip), + EscapeString(local_ip) ); + Database::DbWorldRegistration world_registration{}; + auto results = QueryDatabase(query); if (!results.Success() || results.RowCount() != 1) { - return false; + return world_registration; } auto row = results.begin(); - id = atoi(row[0]); - desc = row[1]; - trusted = atoi(row[2]); - list_id = atoi(row[3]); - list_desc = row[4]; + world_registration.loaded = true; + world_registration.server_id = std::stoi(row[0]); + world_registration.server_description = row[1]; + world_registration.server_list_type = std::stoi(row[3]); + world_registration.is_server_trusted = std::stoi(row[2]) > 0; + world_registration.server_list_description = row[4]; - int db_account_id = atoi(row[5]); - if (db_account_id > 0) { - - auto world_registration_query = fmt::format( - "SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1", - db_account_id - ); - - auto world_registration_results = QueryDatabase(world_registration_query); - if (!world_registration_results.Success() || world_registration_results.RowCount() != 1) { - return false; - } - - auto world_registration_row = world_registration_results.begin(); - - account = world_registration_row[0]; - password = world_registration_row[1]; + int db_account_id = std::stoi(row[5]); + if (db_account_id <= 0) { + return world_registration; } - return true; + auto world_registration_query = fmt::format( + "SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1", + db_account_id + ); + + auto world_registration_results = QueryDatabase(world_registration_query); + if (world_registration_results.Success() && world_registration_results.RowCount() == 1) { + auto world_registration_row = world_registration_results.begin(); + world_registration.server_admin_account_name = world_registration_row[0]; + world_registration.server_admin_account_password = world_registration_row[1]; + } + + return world_registration; } /** diff --git a/loginserver/database.h b/loginserver/database.h index 69ca1c08b..44b1f95c1 100644 --- a/loginserver/database.h +++ b/loginserver/database.h @@ -95,32 +95,31 @@ public: unsigned int id ); + struct DbWorldRegistration { + bool loaded = false; + int32 server_id = 0; + int8 server_list_type = 3; + bool is_server_trusted = false; + std::string server_description; + std::string server_list_description; + std::string server_admin_account_name; + std::string server_admin_account_password; + }; + /** * Retrieves the world registration from the long and short names provided * Needed for world login procedure - * Returns true if the record was found, false otherwise. + * Returns true if the record was found, false otherwise * - * @param long_name * @param short_name - * @param id - * @param desc - * @param list_id - * @param trusted - * @param list_desc - * @param account - * @param password + * @param remote_ip + * @param local_ip * @return */ - bool GetWorldRegistration( - std::string long_name, - std::string short_name, - unsigned int &id, - std::string &desc, - unsigned int &list_id, - unsigned int &trusted, - std::string &list_desc, - std::string &account, - std::string &password + Database::DbWorldRegistration GetWorldRegistration( + const std::string& short_name, + const std::string& remote_ip, + const std::string& local_ip ); void UpdateLSAccountData(unsigned int id, std::string ip_address); diff --git a/loginserver/login_util/login_schema.sql b/loginserver/login_util/login_schema.sql index b96f08e0e..d38b5358c 100644 --- a/loginserver/login_util/login_schema.sql +++ b/loginserver/login_util/login_schema.sql @@ -1,3 +1,4 @@ +DROP TABLE IF EXISTS `login_accounts`; CREATE TABLE `login_accounts` ( `id` int(11) unsigned NOT NULL, `account_name` varchar(50) NOT NULL, @@ -8,10 +9,11 @@ CREATE TABLE `login_accounts` ( `last_login_date` datetime NOT NULL, `created_at` datetime DEFAULT NULL, `updated_at` datetime DEFAULT current_timestamp(), - PRIMARY KEY (`id`,`account_name`), + PRIMARY KEY (`id`), UNIQUE KEY `source_loginserver_account_name` (`source_loginserver`,`account_name`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; +DROP TABLE IF EXISTS `login_server_admins`; CREATE TABLE `login_server_admins` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `account_name` varchar(30) NOT NULL, @@ -21,9 +23,10 @@ CREATE TABLE `login_server_admins` ( `email` varchar(100) NOT NULL, `registration_date` datetime NOT NULL, `registration_ip_address` varchar(15) NOT NULL, - PRIMARY KEY (`id`,`account_name`) + PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; +DROP TABLE IF EXISTS `login_server_list_types`; CREATE TABLE `login_server_list_types` ( `id` int(10) unsigned NOT NULL, `description` varchar(60) NOT NULL, @@ -34,6 +37,7 @@ INSERT INTO `login_server_list_types` (`id`, `description`) VALUES ('1', 'Legend ('2', 'Preferred'), ('3', 'Standard'); +DROP TABLE IF EXISTS `login_world_servers`; CREATE TABLE `login_world_servers` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `long_name` varchar(100) NOT NULL, @@ -44,10 +48,11 @@ CREATE TABLE `login_world_servers` ( `last_ip_address` varchar(15) DEFAULT NULL, `login_server_admin_id` int(11) NOT NULL, `is_server_trusted` int(11) NOT NULL, - `note` varchar(300) DEFAULT NULL, - PRIMARY KEY (`id`,`long_name`) + `note` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; +DROP TABLE IF EXISTS `login_api_tokens`; CREATE TABLE `login_api_tokens` ( `id` int(11) NOT NULL AUTO_INCREMENT, `token` varchar(200) DEFAULT NULL, diff --git a/loginserver/loginserver_webserver.cpp b/loginserver/loginserver_webserver.cpp index 6a090f828..732ce4ff7 100644 --- a/loginserver/loginserver_webserver.cpp +++ b/loginserver/loginserver_webserver.cpp @@ -45,8 +45,8 @@ namespace LoginserverWebserver { auto iter = server.server_manager->getWorldServers().begin(); while (iter != server.server_manager->getWorldServers().end()) { Json::Value row; - row["server_long_name"] = (*iter)->GetLongName(); - row["server_short_name"] = (*iter)->GetLongName(); + row["server_long_name"] = (*iter)->GetServerLongName(); + row["server_short_name"] = (*iter)->GetServerLongName(); row["server_list_id"] = (*iter)->GetServerListID(); row["server_status"] = (*iter)->GetStatus(); row["zones_booted"] = (*iter)->GetZonesBooted(); diff --git a/loginserver/server_manager.cpp b/loginserver/server_manager.cpp index cb164b727..e91e26ac2 100644 --- a/loginserver/server_manager.cpp +++ b/loginserver/server_manager.cpp @@ -80,7 +80,7 @@ ServerManager::ServerManager() LogF(Logs::General, Logs::World_Server, "World server {0} has been disconnected, removing.", - (*iter)->GetLongName().c_str()); + (*iter)->GetServerLongName().c_str()); world_servers.erase(iter); return; } @@ -125,24 +125,30 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint3 in.s_addr = client->GetConnection()->GetRemoteIP(); std::string client_ip = inet_ntoa(in); + LogDebug("ServerManager::CreateServerListPacket via client address [{0}]", client_ip); + auto iter = world_servers.begin(); while (iter != world_servers.end()) { - if ((*iter)->IsAuthorized() == false) { + if (!(*iter)->IsAuthorized()) { + LogDebug( + "ServerManager::CreateServerListPacket | Server [{0}] via IP [{1}] is not authorized to be listed", + (*iter)->GetServerLongName(), + (*iter)->GetConnection()->Handle()->RemoteIP() + ); ++iter; continue; } std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP(); - - if (world_ip.compare(client_ip) == 0) { - packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24; + if (world_ip == client_ip) { + packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24; } else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) { LogInfo("Client is requesting server list from a local address [{0}]", client_ip); - packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24; + packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24; } else { - packet_size += (*iter)->GetLongName().size() + (*iter)->GetRemoteIP().size() + 24; + packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetRemoteIP().size() + 24; } server_count++; @@ -203,11 +209,11 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint3 data_pointer += 4; - *(unsigned int *) data_pointer = (*iter)->GetRuntimeID(); + *(unsigned int *) data_pointer = (*iter)->GetServerId(); data_pointer += 4; - memcpy(data_pointer, (*iter)->GetLongName().c_str(), (*iter)->GetLongName().size()); - data_pointer += ((*iter)->GetLongName().size() + 1); + memcpy(data_pointer, (*iter)->GetServerLongName().c_str(), (*iter)->GetServerLongName().size()); + data_pointer += ((*iter)->GetServerLongName().size() + 1); memcpy(data_pointer, "EN", 2); data_pointer += 3; @@ -252,7 +258,7 @@ void ServerManager::SendUserToWorldRequest( auto iter = world_servers.begin(); bool found = false; while (iter != world_servers.end()) { - if ((*iter)->GetRuntimeID() == server_id) { + if ((*iter)->GetServerId() == server_id) { EQ::Net::DynamicPacket outapp; outapp.Resize(sizeof(UsertoWorldRequest_Struct)); @@ -294,8 +300,8 @@ bool ServerManager::ServerExists( continue; } - if ((*iter)->GetLongName().compare(server_long_name) == 0 && - (*iter)->GetShortName().compare(server_short_name) == 0) { + if ((*iter)->GetServerLongName().compare(server_long_name) == 0 && + (*iter)->GetServerShortName().compare(server_short_name) == 0) { return true; } @@ -322,8 +328,8 @@ void ServerManager::DestroyServerByName( continue; } - if ((*iter)->GetLongName().compare(server_long_name) == 0 && - (*iter)->GetShortName().compare(server_short_name) == 0) { + if ((*iter)->GetServerLongName().compare(server_long_name) == 0 && + (*iter)->GetServerShortName().compare(server_short_name) == 0) { (*iter)->GetConnection()->Handle()->Disconnect(); iter = world_servers.erase(iter); continue; diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index 518d919bd..c85b92d8e 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -32,16 +32,16 @@ extern LoginServer server; */ WorldServer::WorldServer(std::shared_ptr worldserver_connection) { - connection = worldserver_connection; - zones_booted = 0; - players_online = 0; - server_status = 0; - runtime_id = 0; - server_list_id = 0; - server_type = 0; + connection = worldserver_connection; + zones_booted = 0; + players_online = 0; + server_status = 0; + server_id = 0; + server_list_type_id = 0; + server_process_type = 0; is_server_authorized = false; - is_server_trusted = false; - is_server_logged_in = false; + is_server_trusted = false; + is_server_logged_in = false; worldserver_connection->OnMessage( ServerOP_NewLSInfo, @@ -78,14 +78,14 @@ WorldServer::~WorldServer() = default; void WorldServer::Reset() { - runtime_id; - zones_booted = 0; - players_online = 0; - server_status = 0; - server_list_id = 0; - server_type = 0; + server_id; + zones_booted = 0; + players_online = 0; + server_status = 0; + server_list_type_id = 0; + server_process_type = 0; is_server_authorized = false; - is_server_logged_in = false; + is_server_logged_in = false; } /** @@ -129,15 +129,15 @@ void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packe " - protocolversion [{6}]\n" " - server_version [{7}]\n" " - server_type [{8}]", - info->name, - info->shortname, - info->remote_address, - info->local_address, - info->account, - info->password, - info->protocolversion, - info->serverversion, - info->servertype + info->server_long_name, + info->server_short_name, + info->remote_ip_address, + info->local_ip_address, + info->account_name, + info->account_password, + info->protocol_version, + info->server_version, + info->server_process_type ); Handle_NewLSInfo(info); @@ -173,7 +173,7 @@ void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet LogDebug( "World Server Status Update Received | Server [{0}] Status [{1}] Players [{2}] Zones [{3}]", - this->GetLongName(), + this->GetServerLongName(), ls_status->status, ls_status->num_players, ls_status->num_zones @@ -213,7 +213,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne //Because this is a part of the client login procedure it makes tracking client errors //While keeping world server spam with multiple servers connected almost impossible. if (server.options.IsTraceOn()) { - Log(Logs::General, Logs::Netcode, "User-To-World Response received."); + Log(Logs::General, Logs::Netcode, "User-To-World Response received"); } auto *user_to_world_response = (UsertoWorldResponseLegacy_Struct *) packet.Data(); @@ -314,7 +314,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac Log(Logs::General, Logs::Error, "Received application packet from server that had opcode ServerOP_UsertoWorldResp, " - "but was too small. Discarded to avoid buffer overrun."); + "but was too small. Discarded to avoid buffer overrun"); return; } @@ -322,7 +322,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac //Because this is a part of the client login procedure it makes tracking client errors //While keeping world server spam with multiple servers connected almost impossible. if (server.options.IsTraceOn()) { - Log(Logs::General, Logs::Netcode, "User-To-World Response received."); + Log(Logs::General, Logs::Netcode, "User-To-World Response received"); } auto user_to_world_response = (UsertoWorldResponse_Struct *) packet.Data(); @@ -333,8 +333,8 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac ); if (client) { LogDebug("Found client with user id of {0} and account name of {1}", - user_to_world_response->lsaccountid, - client->GetAccountName().c_str() + user_to_world_response->lsaccountid, + client->GetAccountName().c_str() ); auto *outapp = new EQApplicationPacket( @@ -403,7 +403,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac } else { LogError("Received User-To-World Response for {0} but could not find the client referenced!.", - user_to_world_response->lsaccountid); + user_to_world_response->lsaccountid); } } @@ -457,104 +457,33 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet } /** + * When a worldserver first messages the loginserver telling them who they are * * @param new_world_server_info_packet */ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info_packet) { if (is_server_logged_in) { - LogError("WorldServer::Handle_NewLSInfo called but the login server was already marked as logged in, aborting."); + LogError("WorldServer::Handle_NewLSInfo called but the login server was already marked as logged in, aborting"); return; } - if (strlen(new_world_server_info_packet->account) <= 30) { - account_name = new_world_server_info_packet->account; - } - else { - LogError("Handle_NewLSInfo error, account name was too long."); + if (!this->HandleNewLoginserverInfoValidation(new_world_server_info_packet)) { + LogError("WorldServer::Handle_NewLSInfo failed validation rules"); return; } - if (strlen(new_world_server_info_packet->password) <= 30) { - account_password = new_world_server_info_packet->password; - } - else { - LogError("Handle_NewLSInfo error, account password was too long."); - return; - } - - if (strlen(new_world_server_info_packet->name) <= 200) { - long_name = new_world_server_info_packet->name; - } - else { - LogError("Handle_NewLSInfo error, long name was too long."); - return; - } - - if (strlen(new_world_server_info_packet->shortname) <= 50) { - short_name = new_world_server_info_packet->shortname; - } - else { - LogError("Handle_NewLSInfo error, short name was too long."); - return; - } - - if (strlen(new_world_server_info_packet->local_address) <= 125) { - if (strlen(new_world_server_info_packet->local_address) == 0) { - LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost"); - local_ip = "127.0.0.1"; - } - else { - local_ip = new_world_server_info_packet->local_address; - } - } - else { - LogError("Handle_NewLSInfo error, local address was too long."); - - return; - } - - if (strlen(new_world_server_info_packet->remote_address) <= 125) { - if (strlen(new_world_server_info_packet->remote_address) == 0) { - remote_ip = GetConnection()->Handle()->RemoteIP(); - LogError( - "Remote address was null, defaulting to stream address %s.", - remote_ip.c_str() - ); - } - else { - remote_ip = new_world_server_info_packet->remote_address; - } - } - else { - remote_ip = GetConnection()->Handle()->RemoteIP(); - - Log( - Logs::General, - Logs::Error, - "Handle_NewLSInfo error, remote address was too long, defaulting to stream address %s.", - remote_ip.c_str() - ); - } - - if (strlen(new_world_server_info_packet->serverversion) <= 64) { - version = new_world_server_info_packet->serverversion; - } - else { - LogError("Handle_NewLSInfo error, server version was too long."); - return; - } - - if (strlen(new_world_server_info_packet->protocolversion) <= 25) { - protocol = new_world_server_info_packet->protocolversion; - } - else { - LogError("Handle_NewLSInfo error, protocol version was too long."); - return; - } - - server_type = new_world_server_info_packet->servertype; - is_server_logged_in = true; + this + ->SetAccountPassword(new_world_server_info_packet->account_password) + ->SetLongName(new_world_server_info_packet->server_long_name) + ->SetShortName(new_world_server_info_packet->server_short_name) + ->SetLocalIp(new_world_server_info_packet->local_ip_address) + ->SetRemoteIp(new_world_server_info_packet->remote_ip_address) + ->SetVersion(new_world_server_info_packet->server_version) + ->SetProtocol(new_world_server_info_packet->protocol_version) + ->SetServerProcessType(new_world_server_info_packet->server_process_type) + ->SetIsServerLoggedIn(true) + ->SetAccountName(new_world_server_info_packet->account_name); if (server.options.IsRejectingDuplicateServers()) { if (server.server_manager->ServerExists(long_name, short_name, this)) { @@ -569,171 +498,27 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info } } - if (!server.options.IsUnregisteredAllowed()) { - if (account_name.size() > 0 && account_password.size() > 0) { - unsigned int s_id = 0; - unsigned int s_list_type = 0; - unsigned int s_trusted = 0; - std::string s_desc; - std::string s_list_desc; - std::string s_acct_name; - std::string s_acct_pass; - if (server.db->GetWorldRegistration( - long_name, - short_name, - s_id, - s_desc, - s_list_type, - s_trusted, - s_list_desc, - s_acct_name, - s_acct_pass - )) { - if (s_acct_name.size() == 0 || s_acct_pass.size() == 0) { - Log(Logs::General, - Logs::World_Server, - "Server %s(%s) successfully logged into account that had no user/password requirement.", - long_name.c_str(), - short_name.c_str()); - is_server_authorized = true; - SetRuntimeID(s_id); - server_list_id = s_list_type; - desc = s_desc; - } - else if (s_acct_name.compare(account_name) == 0 && s_acct_pass.compare(account_password) == 0) { - Log(Logs::General, Logs::World_Server, "Server %s(%s) successfully logged in.", - long_name.c_str(), short_name.c_str()); - is_server_authorized = true; - SetRuntimeID(s_id); - server_list_id = s_list_type; - desc = s_desc; - if (s_trusted) { - Log(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate sent to world"); - is_server_trusted = true; + Database::DbWorldRegistration + world_registration = server.db->GetWorldRegistration(short_name, remote_ip_address, local_ip); - EQ::Net::DynamicPacket outapp; - connection->Send(ServerOP_LSAccountUpdate, outapp); - } - } - else { - Log(Logs::General, - Logs::World_Server, - "Server %s(%s) attempted to log in but account and password did not match the entry in the database, and only" - " registered servers are allowed.", - long_name.c_str(), - short_name.c_str()); - return; - } - } - else { - Log(Logs::General, - Logs::World_Server, - "Server %s(%s) attempted to log in but database couldn't find an entry and only registered servers are allowed.", - long_name.c_str(), - short_name.c_str()); - return; - } - } - else { - Log(Logs::General, - Logs::World_Server, - "Server %s(%s) did not attempt to log in but only registered servers are allowed.", - long_name.c_str(), - short_name.c_str()); + if (!server.options.IsUnregisteredAllowed()) { + if (!this->HandleNewLoginserverRegisteredOnly(world_registration)){ + LogError("WorldServer::HandleNewLoginserverRegisteredOnly checks failed"); return; } } else { - unsigned int server_id = 0; - unsigned int server_list_type = 0; - unsigned int is_server_trusted = 0; - std::string server_description; - std::string server_list_description; - std::string server_account_name; - std::string server_account_password; - - if (server.db->GetWorldRegistration( - long_name, - short_name, - server_id, - server_description, - server_list_type, - is_server_trusted, - server_list_description, - server_account_name, - server_account_password - )) { - - if (account_name.size() > 0 && account_password.size() > 0) { - if (server_account_name.compare(account_name) == 0 && - server_account_password.compare(account_password) == 0) { - Log(Logs::General, Logs::World_Server, "Server %s(%s) successfully logged in.", - long_name.c_str(), short_name.c_str()); - is_server_authorized = true; - SetRuntimeID(server_id); - server_list_id = server_list_type; - desc = server_description; - - if (is_server_trusted) { - Log(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate sent to world"); - is_server_trusted = true; - EQ::Net::DynamicPacket outapp; - connection->Send(ServerOP_LSAccountUpdate, outapp); - } - } - - /** - * this is the first of two cases where we should deny access even if unregistered is allowed - */ - else { - Log(Logs::General, - Logs::World_Server, - "Server %s(%s) attempted to log in but account and password did not match the entry in the database.", - long_name.c_str(), - short_name.c_str()); - } - } - else { - - /** - * this is the second of two cases where we should deny access even if unregistered is allowed - */ - if (server_account_name.size() > 0 || server_account_password.size() > 0) { - LogInfo( - "Server [{0}] [{1}] did not login but this server required a password to login", - long_name, - short_name - ); - } - else { - LogInfo( - "Server [{0}] [{1}] did not login but unregistered servers are allowed", - long_name, - short_name - ); - - is_server_authorized = true; - SetRuntimeID(server_id); - server_list_id = 3; - } - } - } - else { - LogInfo( - "Server [{0}] ({1}) is not registered but unregistered servers are allowed", - long_name, - short_name - ); - - if (server.db->CreateWorldRegistration(long_name, short_name, server_id)) { - is_server_authorized = true; - SetRuntimeID(server_id); - server_list_id = 3; - } + if (!this->HandleNewLoginserverInfoUnregisteredAllowed(world_registration)){ + LogError("WorldServer::HandleNewLoginserverInfoUnregisteredAllowed checks failed"); + return; } } - server.db->UpdateWorldRegistration(GetRuntimeID(), long_name, GetConnection()->Handle()->RemoteIP()); + server.db->UpdateWorldRegistration( + GetServerId(), + GetServerLongName(), + GetConnection()->Handle()->RemoteIP() + ); } /** @@ -769,15 +554,15 @@ void WorldServer::SendClientAuth( strncpy(client_auth.account_name, account.c_str(), 30); strncpy(client_auth.key, key.c_str(), 30); - client_auth.lsadmin = 0; + client_auth.lsadmin = 0; client_auth.is_world_admin = 0; - client_auth.ip = inet_addr(ip.c_str()); + client_auth.ip = inet_addr(ip.c_str()); strncpy(client_auth.loginserver_name, &loginserver_name[0], 64); const std::string &client_address(ip); std::string world_address(connection->Handle()->RemoteIP()); - if (client_address.compare(world_address) == 0) { + if (client_address == world_address) { client_auth.is_client_from_local_network = 1; } else if (IpUtil::IsIpInPrivateRfc1918(client_address)) { @@ -817,3 +602,543 @@ void WorldServer::SendClientAuth( DumpPacket(ServerOP_LSClientAuth, outapp); } } + +/** + * @param new_world_server_info_packet + * @return + */ +bool WorldServer::HandleNewLoginserverInfoValidation( + ServerNewLSInfo_Struct *new_world_server_info_packet +) +{ + const int max_account_name_length = 30; + const int max_account_password_length = 30; + const int max_server_long_name_length = 200; + const int max_server_short_name_length = 50; + const int max_server_local_address_length = 125; + const int max_server_remote_address_length = 125; + const int max_server_version_length = 64; + const int max_server_protocol_version = 25; + + if (strlen(new_world_server_info_packet->account_name) >= max_account_name_length) { + LogError("Handle_NewLSInfo error [account_name] was too long | max [{0}]", max_account_name_length); + return false; + } + else if (strlen(new_world_server_info_packet->account_password) >= max_account_password_length) { + LogError("Handle_NewLSInfo error [account_password] was too long | max [{0}]", max_account_password_length); + return false; + } + else if (strlen(new_world_server_info_packet->server_long_name) >= max_server_long_name_length) { + LogError("Handle_NewLSInfo error [server_long_name] was too long | max [{0}]", max_server_long_name_length); + return false; + } + else if (strlen(new_world_server_info_packet->server_short_name) >= max_server_short_name_length) { + LogError("Handle_NewLSInfo error [server_short_name] was too long | max [{0}]", max_server_short_name_length); + return false; + } + else if (strlen(new_world_server_info_packet->server_version) >= max_server_short_name_length) { + LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", max_server_version_length); + return false; + } + else if (strlen(new_world_server_info_packet->protocol_version) >= max_server_protocol_version) { + LogError("Handle_NewLSInfo error [protocol_version] was too long | max [{0}]", max_server_protocol_version); + return false; + } + + if (strlen(new_world_server_info_packet->local_ip_address) <= max_server_local_address_length) { + if (strlen(new_world_server_info_packet->local_ip_address) == 0) { + LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost"); + this->SetLocalIp("127.0.0.1"); + } + else { + this->SetLocalIp(new_world_server_info_packet->local_ip_address); + } + } + else { + LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", max_server_local_address_length); + return false; + } + + if (strlen(new_world_server_info_packet->remote_ip_address) <= max_server_remote_address_length) { + if (strlen(new_world_server_info_packet->remote_ip_address) == 0) { + this->SetRemoteIp(GetConnection()->Handle()->RemoteIP()); + + LogError( + "Remote address was null, defaulting to stream address {0}", + remote_ip_address + ); + } + else { + this->SetRemoteIp(new_world_server_info_packet->remote_ip_address); + } + } + else { + this->SetRemoteIp(GetConnection()->Handle()->RemoteIP()); + + LogError( + "Handle_NewLSInfo error, remote address was too long, defaulting to stream address [{0}]", + remote_ip_address + ); + } + + return true; +} + +/** + * @param world_registration + * @return + */ +bool WorldServer::HandleNewLoginserverRegisteredOnly( + Database::DbWorldRegistration &world_registration +) { + if (!this->GetAccountName().empty() && !this->GetAccountPassword().empty()) { + if (world_registration.loaded) { + bool does_world_server_not_require_authentication = ( + world_registration.server_admin_account_name.empty() || + world_registration.server_admin_account_password.empty() + ); + + bool does_world_server_pass_authentication_check = ( + world_registration.server_admin_account_name == this->GetAccountName() && + world_registration.server_admin_account_password == this->GetAccountPassword() + ); + + this + ->SetServerDescription(world_registration.server_description) + ->SetServerId(world_registration.server_id) + ->SetIsServerTrusted(world_registration.is_server_trusted) + ->SetServerListTypeId(world_registration.server_list_type); + + if (does_world_server_not_require_authentication) { + + this->SetIsServerAuthorized(true); + + LogInfo( + "Server long_name {0} short_name [{1}] successfully logged into account that had no user/password requirement", + this->GetServerLongName(), + this->GetServerShortName() + ); + } + else if (does_world_server_pass_authentication_check) { + + this->SetIsServerAuthorized(true); + + LogInfo( + "Server long_name {0} short_name [{1}] successfully logged in", + this->GetServerLongName(), + this->GetServerShortName() + ); + + if (IsServerTrusted()) { + LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); + EQ::Net::DynamicPacket outapp; + connection->Send(ServerOP_LSAccountUpdate, outapp); + } + } + else { + LogInfo( + "Server long_name {0} short_name [{1}] attempted to log in but account and password did not " + "match the entry in the database, and only registered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + + return false; + } + } + else { + LogInfo( + "Server long_name {0} short_name [{1}] attempted to log in but database couldn't find an entry and only registered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + + return false; + } + } + else { + LogInfo( + "Server long_name {0} short_name [{1}] did not attempt to log in but only registered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + + return false; + } + + return true; +} + +/** + * @param world_registration + * @return + */ +bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( + Database::DbWorldRegistration &world_registration +) { + if (world_registration.loaded) { + + this + ->SetServerDescription(world_registration.server_description) + ->SetServerId(world_registration.server_id) + ->SetIsServerTrusted(world_registration.is_server_trusted) + ->SetServerListTypeId(world_registration.server_list_type); + + bool does_world_server_pass_authentication_check = ( + world_registration.server_admin_account_name == this->GetAccountName() && + world_registration.server_admin_account_password == this->GetAccountPassword() + ); + + bool does_world_server_have_non_empty_credentials = ( + !this->GetAccountName().empty() && + !this->GetAccountPassword().empty() + ); + + if (does_world_server_have_non_empty_credentials) { + if (does_world_server_pass_authentication_check) { + + this->SetIsServerAuthorized(true); + + LogInfo( + "Server long_name {0} short_name [{1}] successfully logged in", + this->GetServerLongName(), + this->GetServerShortName() + ); + + if (IsServerTrusted()) { + LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); + EQ::Net::DynamicPacket outapp; + connection->Send(ServerOP_LSAccountUpdate, outapp); + } + } + else { + + /** + * this is the first of two cases where we should deny access even if unregistered is allowed + */ + LogInfo( + "Server long_name {0} short_name [{1}] attempted to log in but account and password did not match the entry in the database.", + this->GetServerLongName(), + this->GetServerShortName() + ); + } + } + else { + + /** + * this is the second of two cases where we should deny access even if unregistered is allowed + */ + if (!this->GetAccountName().empty() || !this->GetAccountPassword().empty()) { + LogInfo( + "Server [{0}] [{1}] did not login but this server required a password to login", + this->GetServerLongName(), + this->GetServerShortName() + ); + } + else { + this->SetIsServerAuthorized(true); + LogInfo( + "Server [{0}] [{1}] did not login but unregistered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + } + } + } + else { + LogInfo( + "Server [{0}] ({1}) is not registered but unregistered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + + if (world_registration.loaded) { + this->SetIsServerAuthorized(true); + return true; + } + + /** + * Auto create a registration + */ + if (!server.db->CreateWorldRegistration(long_name, short_name, server_id)) { + return false; + } + } + + return true; +} + +/** + * @param in_server_list_id + * @return + */ +WorldServer * WorldServer::SetServerListTypeId(unsigned int in_server_list_id) +{ + server_list_type_id = in_server_list_id; + + return this; +} + +/** + * @return + */ +const std::string &WorldServer::GetServerDescription() const +{ + return server_description; +} + +/** + * @param in_server_description + */ +WorldServer * WorldServer::SetServerDescription(const std::string &in_server_description) +{ + WorldServer::server_description = in_server_description; + + return this; +} + +/** + * @return + */ +bool WorldServer::IsServerAuthorized() const +{ + return is_server_authorized; +} + +/** + * @param in_is_server_authorized + */ +WorldServer * WorldServer::SetIsServerAuthorized(bool in_is_server_authorized) +{ + WorldServer::is_server_authorized = in_is_server_authorized; + + return this; +} + +/** + * @return + */ +bool WorldServer::IsServerLoggedIn() const +{ + return is_server_logged_in; +} + +/** + * @param in_is_server_logged_in + */ +WorldServer * WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in) +{ + WorldServer::is_server_logged_in = in_is_server_logged_in; + + return this; +} + +/** + * @return + */ +bool WorldServer::IsServerTrusted() const +{ + return is_server_trusted; +} + +/** + * @param in_is_server_trusted + */ +WorldServer * WorldServer::SetIsServerTrusted(bool in_is_server_trusted) +{ + WorldServer::is_server_trusted = in_is_server_trusted; + + return this; +} + +/** + * @param in_zones_booted + */ +WorldServer * WorldServer::SetZonesBooted(unsigned int in_zones_booted) +{ + WorldServer::zones_booted = in_zones_booted; + + return this; +} + +/** + * @param in_players_online + */ +WorldServer * WorldServer::SetPlayersOnline(unsigned int in_players_online) +{ + WorldServer::players_online = in_players_online; + + return this; +} + +/** + * @param in_server_status + */ +WorldServer * WorldServer::SetServerStatus(int in_server_status) +{ + WorldServer::server_status = in_server_status; + + return this; +} + +/** + * @param in_server_process_type + */ +WorldServer * WorldServer::SetServerProcessType(unsigned int in_server_process_type) +{ + WorldServer::server_process_type = in_server_process_type; + + return this; +} + +/** + * @param in_long_name + */ +WorldServer * WorldServer::SetLongName(const std::string &in_long_name) +{ + WorldServer::long_name = in_long_name; + + return this; +} + +/** + * @param in_short_name + */ +WorldServer * WorldServer::SetShortName(const std::string &in_short_name) +{ + WorldServer::short_name = in_short_name; + + return this; +} + +/** + * @param in_account_name + */ +WorldServer * WorldServer::SetAccountName(const std::string &in_account_name) +{ + WorldServer::account_name = in_account_name; + + return this; +} + +/** + * @param in_account_password + */ +WorldServer * WorldServer::SetAccountPassword(const std::string &in_account_password) +{ + WorldServer::account_password = in_account_password; + + return this; +} + +/** + * @param in_remote_ip + */ +WorldServer * WorldServer::SetRemoteIp(const std::string &in_remote_ip) +{ + WorldServer::remote_ip_address = in_remote_ip; + + return this; +} + +/** + * @param in_local_ip + */ +WorldServer * WorldServer::SetLocalIp(const std::string &in_local_ip) +{ + WorldServer::local_ip = in_local_ip; + + return this; +} + +/** + * @param in_protocol + */ +WorldServer * WorldServer::SetProtocol(const std::string &in_protocol) +{ + WorldServer::protocol = in_protocol; + + return this; +} + +/** + * @param in_version + */ +WorldServer * WorldServer::SetVersion(const std::string &in_version) +{ + WorldServer::version = in_version; + + return this; +} + +/** + * @return + */ +int WorldServer::GetServerStatus() const +{ + return server_status; +} + +/** + * @return + */ +unsigned int WorldServer::GetServerListTypeId() const +{ + return server_list_type_id; +} + +/** + * @return + */ +unsigned int WorldServer::GetServerProcessType() const +{ + return server_process_type; +} + +/** + * @return + */ +const std::string &WorldServer::GetAccountName() const +{ + return account_name; +} + +/** + * @return + */ +const std::string &WorldServer::GetAccountPassword() const +{ + return account_password; +} + +/** + * @return + */ +const std::string &WorldServer::GetRemoteIp() const +{ + return remote_ip_address; +} + +/** + * @return + */ +const std::string &WorldServer::GetLocalIp() const +{ + return local_ip; +} + +/** + * @return + */ +const std::string &WorldServer::GetProtocol() const +{ + return protocol; +} + +/** + * @return + */ +const std::string &WorldServer::GetVersion() const +{ + return version; +} \ No newline at end of file diff --git a/loginserver/world_server.h b/loginserver/world_server.h index 97cdb76d5..ffd2153cb 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -25,6 +25,7 @@ #include "../common/net/servertalk_server_connection.h" #include "../common/servertalk.h" #include "../common/packet_dump.h" +#include "database.h" #include #include @@ -51,10 +52,18 @@ public: */ std::shared_ptr GetConnection() { return connection; } void SetConnection(std::shared_ptr c) { connection = c; } - unsigned int GetRuntimeID() const { return runtime_id; } - void SetRuntimeID(unsigned int id) { runtime_id = id; } - std::string GetLongName() const { return long_name; } - std::string GetShortName() const { return short_name; } + + /** + * @return + */ + unsigned int GetServerId() const { return server_id; } + WorldServer * SetServerId(unsigned int id) { server_id = id; return this; } + + /** + * @return + */ + std::string GetServerLongName() const { return long_name; } + std::string GetServerShortName() const { return short_name; } /** * Gets whether the server is authorized to show up on the server list or not @@ -62,14 +71,16 @@ public: */ bool IsAuthorized() const { return is_server_authorized; } std::string GetLocalIP() const { return local_ip; } - std::string GetRemoteIP() const { return remote_ip; } + std::string GetRemoteIP() const { return remote_ip_address; } /** * Gets what kind of server this server is (legends, preferred, normal) * * @return */ - unsigned int GetServerListID() const { return server_list_id; } + unsigned int GetServerListID() const { return server_list_type_id; } + WorldServer * SetServerListTypeId(unsigned int in_server_list_id); + int GetStatus() const { return server_status; } unsigned int GetZonesBooted() const { return zones_booted; } unsigned int GetPlayersOnline() const { return players_online; } @@ -88,6 +99,8 @@ public: */ void Handle_LSStatus(ServerLSStatus_Struct *server_login_status); + bool HandleNewLoginserverInfoValidation(ServerNewLSInfo_Struct *new_world_server_info_packet); + /** * Informs world that there is a client incoming with the following data. * @@ -99,6 +112,40 @@ public: */ void SendClientAuth(std::string ip, std::string account, std::string key, unsigned int account_id, const std::string &loginserver_name); + WorldServer * SetZonesBooted(unsigned int in_zones_booted); + WorldServer * SetPlayersOnline(unsigned int in_players_online); + WorldServer * SetServerStatus(int in_server_status); + WorldServer * SetServerProcessType(unsigned int in_server_process_type); + WorldServer * SetLongName(const std::string &in_long_name); + WorldServer * SetShortName(const std::string &in_short_name); + WorldServer * SetAccountName(const std::string &in_account_name); + WorldServer * SetAccountPassword(const std::string &in_account_password); + WorldServer * SetRemoteIp(const std::string &in_remote_ip); + WorldServer * SetLocalIp(const std::string &in_local_ip); + WorldServer * SetProtocol(const std::string &in_protocol); + WorldServer * SetVersion(const std::string &in_version); + WorldServer * SetServerDescription(const std::string &in_server_description); + WorldServer * SetIsServerAuthorized(bool in_is_server_authorized); + WorldServer * SetIsServerLoggedIn(bool in_is_server_logged_in); + WorldServer * SetIsServerTrusted(bool in_is_server_trusted); + + bool IsServerAuthorized() const; + bool IsServerLoggedIn() const; + bool IsServerTrusted() const; + const std::string &GetAccountName() const; + const std::string &GetAccountPassword() const; + const std::string &GetLocalIp() const; + const std::string &GetProtocol() const; + const std::string &GetRemoteIp() const; + const std::string &GetServerDescription() const; + const std::string &GetVersion() const; + int GetServerStatus() const; + unsigned int GetServerListTypeId() const; + unsigned int GetServerProcessType() const; + + bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration); + bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration); + private: /** @@ -117,21 +164,22 @@ private: unsigned int zones_booted; unsigned int players_online; int server_status; - unsigned int runtime_id; - unsigned int server_list_id; - unsigned int server_type; - std::string desc; + unsigned int server_id; + unsigned int server_list_type_id; + unsigned int server_process_type; + std::string server_description; std::string long_name; std::string short_name; std::string account_name; std::string account_password; - std::string remote_ip; + std::string remote_ip_address; std::string local_ip; std::string protocol; std::string version; bool is_server_authorized; bool is_server_logged_in; bool is_server_trusted; + }; #endif diff --git a/world/login_server.cpp b/world/login_server.cpp index 18904aaaf..e1cc8a3f1 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -516,22 +516,22 @@ void LoginServer::SendInfo() pack->pBuffer = new uchar[pack->size]; memset(pack->pBuffer, 0, pack->size); ServerNewLSInfo_Struct *lsi = (ServerNewLSInfo_Struct *) pack->pBuffer; - strcpy(lsi->protocolversion, EQEMU_PROTOCOL_VERSION); - strcpy(lsi->serverversion, LOGIN_VERSION); - strcpy(lsi->name, Config->LongName.c_str()); - strcpy(lsi->shortname, Config->ShortName.c_str()); - strn0cpy(lsi->account, LoginAccount.c_str(), 30); - strn0cpy(lsi->password, LoginPassword.c_str(), 30); + strcpy(lsi->protocol_version, EQEMU_PROTOCOL_VERSION); + strcpy(lsi->server_version, LOGIN_VERSION); + strcpy(lsi->server_long_name, Config->LongName.c_str()); + strcpy(lsi->server_short_name, Config->ShortName.c_str()); + strn0cpy(lsi->account_name, LoginAccount.c_str(), 30); + strn0cpy(lsi->account_password, LoginPassword.c_str(), 30); if (Config->WorldAddress.length()) { - strcpy(lsi->remote_address, Config->WorldAddress.c_str()); + strcpy(lsi->remote_ip_address, Config->WorldAddress.c_str()); } if (Config->LocalAddress.length()) { - strcpy(lsi->local_address, Config->LocalAddress.c_str()); + strcpy(lsi->local_ip_address, Config->LocalAddress.c_str()); } else { auto local_addr = IsLegacy ? legacy_client->Handle()->LocalIP() : client->Handle()->LocalIP(); - strcpy(lsi->local_address, local_addr.c_str()); - WorldConfig::SetLocalAddress(lsi->local_address); + strcpy(lsi->local_ip_address, local_addr.c_str()); + WorldConfig::SetLocalAddress(lsi->local_ip_address); } SendPacket(pack); delete pack;