diff --git a/common/net/servertalk_server_connection.cpp b/common/net/servertalk_server_connection.cpp index ad86727ef..591bb0f4e 100644 --- a/common/net/servertalk_server_connection.cpp +++ b/common/net/servertalk_server_connection.cpp @@ -123,7 +123,7 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer() { size_t current = 0; size_t total = m_buffer.size(); - constexpr size_t ls_info_size = sizeof(ServerNewLSInfo_Struct); + constexpr size_t ls_info_size = sizeof(LoginserverNewWorldRequest); while (current < total) { auto left = total - current; @@ -138,7 +138,7 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer() //this creates a small edge case where the exact size of a //packet from the modern protocol can't be "43061256" //so in send we pad it one byte if that's the case - if (leg_opcode == ServerOP_NewLSInfo && leg_size == sizeof(ServerNewLSInfo_Struct)) { + if (leg_opcode == ServerOP_NewLSInfo && leg_size == sizeof(LoginserverNewWorldRequest)) { m_legacy_mode = true; m_identifier = "World"; m_parent->ConnectionIdentified(this); diff --git a/common/repositories/login_accounts_repository.h b/common/repositories/login_accounts_repository.h index 2ad6797ae..052f4fb16 100644 --- a/common/repositories/login_accounts_repository.h +++ b/common/repositories/login_accounts_repository.h @@ -4,47 +4,94 @@ #include "../database.h" #include "../strings.h" #include "base/base_login_accounts_repository.h" +#include "../../loginserver/encryption.h" +#include "../../loginserver/login_types.h" -class LoginAccountsRepository: public BaseLoginAccountsRepository { +class LoginAccountsRepository : public BaseLoginAccountsRepository { public: + static int64 GetFreeID(Database &db, const std::string &loginserver) + { + auto query = fmt::format( + "SELECT IFNULL(MAX(id), 0) + 1 FROM login_accounts WHERE source_loginserver = '{}'", + Strings::Escape(loginserver) + ); - /** - * This file was auto generated and can be modified and extended upon - * - * Base repository methods are automatically - * generated in the "base" version of this repository. The base repository - * is immutable and to be left untouched, while methods in this class - * are used as extension methods for more specific persistence-layer - * accessors or mutators. - * - * Base Methods (Subject to be expanded upon in time) - * - * Note: Not all tables are designed appropriately to fit functionality with all base methods - * - * InsertOne - * UpdateOne - * DeleteOne - * FindOne - * GetWhere(std::string where_filter) - * DeleteWhere(std::string where_filter) - * InsertMany - * All - * - * Example custom methods in a repository - * - * LoginAccountsRepository::GetByZoneAndVersion(int zone_id, int zone_version) - * LoginAccountsRepository::GetWhereNeverExpires() - * LoginAccountsRepository::GetWhereXAndY() - * LoginAccountsRepository::DeleteWhereXAndY() - * - * Most of the above could be covered by base methods, but if you as a developer - * find yourself re-using logic for other parts of the code, its best to just make a - * method that can be re-used easily elsewhere especially if it can use a base repository - * method and encapsulate filters there - */ + auto results = db.QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) { + return 0; + } - // Custom extended repository methods here + auto row = results.begin(); + return Strings::ToUnsignedInt(row[0]); + } + + static LoginAccountsRepository::LoginAccounts CreateAccountFromContext( + Database &db, + LoginAccountContext c + ) + { + auto a = LoginAccountsRepository::NewEntity(); + + if (!c.password_is_encrypted) { + auto e = EncryptPasswordFromContext(c); + a.account_password = e.password; + } + + a.id = c.login_account_id > 0 ? c.login_account_id : GetFreeID(db, c.source_loginserver); + a.account_name = c.username; + a.account_email = !c.email.empty() ? c.email : "local_creation"; + a.source_loginserver = c.source_loginserver; + a.last_ip_address = "127.0.0.1"; + a.last_login_date = std::time(nullptr); + a.created_at = std::time(nullptr); + LoginAccountsRepository::InsertOne(db, a); + + return GetAccountFromContext(db, c).id > 0 ? a : NewEntity(); + } + + static LoginAccountsRepository::LoginAccounts GetAccountFromContext( + Database &db, + LoginAccountContext c + ) + { + std::string where = fmt::format( + "account_name = '{}' AND source_loginserver = '{}'", + Strings::Escape(c.username), + Strings::Escape(c.source_loginserver) + ); + + if (!c.email.empty()) { + where += fmt::format(" AND account_email = '{}'", Strings::Escape(c.email)); + } + if (c.login_account_id > 0) { + where += fmt::format(" AND id = {}", c.login_account_id); + } + + where += " LIMIT 1"; + + auto results = LoginAccountsRepository::GetWhere(db, where); + + auto e = LoginAccountsRepository::NewEntity(); + if (results.size() == 1) { + e = results.front(); + } + + return e; + } + + static LoginAccounts UpdateAccountPassword(Database &db, LoginAccounts a, std::string password) + { + LoginAccountContext c; + c.username = a.account_name; + c.password = password; + auto e = EncryptPasswordFromContext(c); + a.account_password = e.password; + + int success = LoginAccountsRepository::UpdateOne(db, a); + + return success ? a : NewEntity(); + } }; #endif //EQEMU_LOGIN_ACCOUNTS_REPOSITORY_H diff --git a/common/repositories/login_server_admins_repository.h b/common/repositories/login_server_admins_repository.h index 8afda242c..f8b126a60 100644 --- a/common/repositories/login_server_admins_repository.h +++ b/common/repositories/login_server_admins_repository.h @@ -5,46 +5,24 @@ #include "../strings.h" #include "base/base_login_server_admins_repository.h" -class LoginServerAdminsRepository: public BaseLoginServerAdminsRepository { +class LoginServerAdminsRepository : public BaseLoginServerAdminsRepository { public: + static LoginServerAdmins GetByName(Database &db, std::string account_name) + { + auto admins = GetWhere( + db, + fmt::format( + "account_name = '{}' LIMIT 1", + Strings::Escape(account_name) + ) + ); - /** - * This file was auto generated and can be modified and extended upon - * - * Base repository methods are automatically - * generated in the "base" version of this repository. The base repository - * is immutable and to be left untouched, while methods in this class - * are used as extension methods for more specific persistence-layer - * accessors or mutators. - * - * Base Methods (Subject to be expanded upon in time) - * - * Note: Not all tables are designed appropriately to fit functionality with all base methods - * - * InsertOne - * UpdateOne - * DeleteOne - * FindOne - * GetWhere(std::string where_filter) - * DeleteWhere(std::string where_filter) - * InsertMany - * All - * - * Example custom methods in a repository - * - * LoginServerAdminsRepository::GetByZoneAndVersion(int zone_id, int zone_version) - * LoginServerAdminsRepository::GetWhereNeverExpires() - * LoginServerAdminsRepository::GetWhereXAndY() - * LoginServerAdminsRepository::DeleteWhereXAndY() - * - * Most of the above could be covered by base methods, but if you as a developer - * find yourself re-using logic for other parts of the code, its best to just make a - * method that can be re-used easily elsewhere especially if it can use a base repository - * method and encapsulate filters there - */ - - // Custom extended repository methods here + if (admins.size() == 1) { + return admins.front(); + } + return NewEntity(); + } }; #endif //EQEMU_LOGIN_SERVER_ADMINS_REPOSITORY_H diff --git a/common/repositories/login_world_servers_repository.h b/common/repositories/login_world_servers_repository.h index 8cda28e32..50516686c 100644 --- a/common/repositories/login_world_servers_repository.h +++ b/common/repositories/login_world_servers_repository.h @@ -7,44 +7,27 @@ class LoginWorldServersRepository: public BaseLoginWorldServersRepository { public: + static LoginWorldServers GetFromWorldContext(Database &db, LoginWorldContext c) { + std::string where = fmt::format( + "short_name = '{}' AND long_name = '{}'", + Strings::Escape(c.short_name), + Strings::Escape(c.long_name) + ); - /** - * This file was auto generated and can be modified and extended upon - * - * Base repository methods are automatically - * generated in the "base" version of this repository. The base repository - * is immutable and to be left untouched, while methods in this class - * are used as extension methods for more specific persistence-layer - * accessors or mutators. - * - * Base Methods (Subject to be expanded upon in time) - * - * Note: Not all tables are designed appropriately to fit functionality with all base methods - * - * InsertOne - * UpdateOne - * DeleteOne - * FindOne - * GetWhere(std::string where_filter) - * DeleteWhere(std::string where_filter) - * InsertMany - * All - * - * Example custom methods in a repository - * - * LoginWorldServersRepository::GetByZoneAndVersion(int zone_id, int zone_version) - * LoginWorldServersRepository::GetWhereNeverExpires() - * LoginWorldServersRepository::GetWhereXAndY() - * LoginWorldServersRepository::DeleteWhereXAndY() - * - * Most of the above could be covered by base methods, but if you as a developer - * find yourself re-using logic for other parts of the code, its best to just make a - * method that can be re-used easily elsewhere especially if it can use a base repository - * method and encapsulate filters there - */ + if (c.admin_id > 0) { + where += fmt::format(" AND login_server_admin_id = {}", c.admin_id); + } - // Custom extended repository methods here + where += " LIMIT 1"; + auto results = GetWhere(db, where); + auto e = NewEntity(); + if (results.size() == 1) { + e = results.front(); + } + + return e; + } }; #endif //EQEMU_LOGIN_WORLD_SERVERS_REPOSITORY_H diff --git a/common/servertalk.h b/common/servertalk.h index 25830afb4..d493313ef 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -646,7 +646,7 @@ struct ServerLSInfo_Struct { uint8 servertype; // 0=world, 1=chat, 2=login, 3=MeshLogin }; -struct ServerNewLSInfo_Struct { +struct LoginserverNewWorldRequest { 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 @@ -658,21 +658,21 @@ struct ServerNewLSInfo_Struct { uint8 server_process_type; // 0=world, 1=chat, 2=login, 3=MeshLogin }; -struct ServerLSAccountUpdate_Struct { // for updating info on login server - char worldaccount[31]; // account name for the worldserver - char worldpassword[31]; // password for the name - uint32 useraccountid; // player account ID - char useraccount[31]; // player account name - char userpassword[51]; // player account password - char user_email[101]; // player account email address +struct LoginserverAccountUpdate { // for updating info on login server + char world_account[31]; // account name for the worldserver + char world_password[31]; // password for the name + uint32 user_account_id; // player account ID + char user_account_name[31]; // player account name + char user_account_password[51]; // player account password + char user_email[101]; // player account email address }; -struct ServerLSStatus_Struct { +struct LoginserverWorldStatusUpdate { int32 status; int32 num_players; int32 num_zones; }; -struct ZoneInfo_Struct { +struct LoginserverZoneInfoUpdate { uint32 zone; uint16 count; uint32 zone_wid; diff --git a/loginserver/CMakeLists.txt b/loginserver/CMakeLists.txt index e55c32c1d..5f2b43248 100644 --- a/loginserver/CMakeLists.txt +++ b/loginserver/CMakeLists.txt @@ -4,12 +4,11 @@ SET(eqlogin_sources account_management.cpp client.cpp client_manager.cpp - database.cpp encryption.cpp loginserver_command_handler.cpp loginserver_webserver.cpp main.cpp - server_manager.cpp + world_server_manager.cpp world_server.cpp ) @@ -17,14 +16,13 @@ SET(eqlogin_headers account_management.h client.h client_manager.h - database.h encryption.h loginserver_command_handler.h loginserver_webserver.h login_server.h login_types.h options.h - server_manager.h + world_server_manager.h world_server.h ) diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp index b95000ed1..a76ae894b 100644 --- a/loginserver/account_management.cpp +++ b/loginserver/account_management.cpp @@ -1,259 +1,114 @@ #include "account_management.h" #include "login_server.h" #include "../common/event/task_scheduler.h" -#include "../common/event/event_loop.h" -#include "../common/net/dns.h" -#include "../common/strings.h" +#include "../common/repositories/login_accounts_repository.h" -extern LoginServer server; EQ::Event::TaskScheduler task_runner; -/** - * @param username - * @param password - * @param email - * @param source_loginserver - * @param login_account_id - * @return - */ -int32 AccountManagement::CreateLoginServerAccount( - std::string username, - std::string password, - std::string email, - const std::string &source_loginserver, - uint32 login_account_id -) +uint64 AccountManagement::CreateLoginServerAccount(LoginAccountContext c) { - auto mode = server.options.GetEncryptionMode(); - auto hash = eqcrypt_hash(username, password, mode); - - LogInfo( - "Attempting to create local login account for user [{0}] encryption algorithm [{1}] ({2})", - username, - GetEncryptionByModeId(mode), - mode - ); - - unsigned int db_id = 0; - if (server.db->DoesLoginServerAccountExist(username, hash, source_loginserver, 1)) { + if (LoginAccountsRepository::GetAccountFromContext(database, c).id > 0) { LogWarning( - "Attempting to create local login account for user [{0}] login [{1}] but already exists!", - username, - source_loginserver + "Attempting to create local login account for user [{}] but already exists!", + c.username ); return -1; } - uint32 created_account_id = 0; - if (login_account_id > 0) { - created_account_id = server.db->CreateLoginDataWithID(username, hash, source_loginserver, login_account_id); - } - else { - created_account_id = server.db->CreateLoginAccount(username, hash, source_loginserver, email); + auto a = LoginAccountsRepository::CreateAccountFromContext(database, c); + if (a.id > 0) { + return (int64) a.id; } - if (created_account_id > 0) { - LogInfo( - "Account creation success for user [{0}] encryption algorithm [{1}] ({2}) id: [{3}]", - username, - GetEncryptionByModeId(mode), - mode, - created_account_id - ); - - return (int32) created_account_id; - } - - LogError("Failed to create local login account for user [{0}]!", username); + LogError("Failed to create local login account for user [{}] !", c.username); return 0; } -/** - * @param username - * @param password - * @param email - * @return - */ -bool AccountManagement::CreateLoginserverWorldAdminAccount( - const std::string &username, - const std::string &password, - const std::string &email, - const std::string &first_name, - const std::string &last_name, - const std::string &ip_address -) +uint64 AccountManagement::CheckLoginserverUserCredentials(LoginAccountContext c) { auto mode = server.options.GetEncryptionMode(); - auto hash = eqcrypt_hash(username, password, mode); - - LogInfo( - "Attempting to create world admin account | username [{0}] encryption algorithm [{1}] ({2})", - username, - GetEncryptionByModeId(mode), - mode - ); - - if (server.db->DoesLoginserverWorldAdminAccountExist(username)) { - LogWarning( - "Attempting to create world admin account for user [{0}] but already exists!", - username - ); - - return false; - } - - uint32 created_world_admin_id = server.db->CreateLoginserverWorldAdminAccount( - username, - hash, - first_name, - last_name, - email, - ip_address - ); - - if (created_world_admin_id > 0) { - LogInfo( - "Account creation success for user [{0}] encryption algorithm [{1}] ({2}) new admin id [{3}]", - username, - GetEncryptionByModeId(mode), - mode, - created_world_admin_id - ); - return true; - } - - LogError("Failed to create world admin account account for user [{0}]!", username); - - return false; -} - -/** - * @param in_account_username - * @param in_account_password - * @return - */ -uint32 AccountManagement::CheckLoginserverUserCredentials( - const std::string &in_account_username, - const std::string &in_account_password, - const std::string &source_loginserver -) -{ - auto mode = server.options.GetEncryptionMode(); - - Database::DbLoginServerAccount - login_server_admin = server.db->GetLoginServerAccountByAccountName( - in_account_username, - source_loginserver - ); - - if (!login_server_admin.loaded) { + auto a = LoginAccountsRepository::GetAccountFromContext(database, c); + if (!a.id) { LogError( - "account [{0}] source_loginserver [{1}] not found!", - in_account_username, - source_loginserver + "account [{}] source_loginserver [{}] not found!", + c.username, + c.source_loginserver ); - return false; + return 0; } - bool validated_credentials = eqcrypt_verify_hash( - in_account_username, - in_account_password, - login_server_admin.account_password, - mode - ); - + bool validated_credentials = eqcrypt_verify_hash(c.username, c.password, a.account_password, mode); if (!validated_credentials) { LogError( - "account [{0}] source_loginserver [{1}] invalid credentials!", - in_account_username, - source_loginserver + "account [{}] source_loginserver [{}] invalid credentials!", + c.username, + c.source_loginserver ); return 0; } LogInfo( - "account [{0}] source_loginserver [{1}] credentials validated success!", - in_account_username, - source_loginserver + "account [{}] source_loginserver [{}] credentials validated success!", + c.username, + c.source_loginserver ); - return login_server_admin.id; + return a.id; } - -/** - * @param in_account_username - * @param in_account_password - * @return - */ -bool AccountManagement::UpdateLoginserverUserCredentials( - const std::string &in_account_username, - const std::string &in_account_password, - const std::string &source_loginserver -) +bool AccountManagement::UpdateLoginserverUserCredentials(LoginAccountContext c) { - auto mode = server.options.GetEncryptionMode(); - - Database::DbLoginServerAccount - login_server_account = server.db->GetLoginServerAccountByAccountName( - in_account_username, - source_loginserver - ); - - if (!login_server_account.loaded) { + auto a = LoginAccountsRepository::GetAccountFromContext(database, c); + if (!a.id) { LogError( - "account [{0}] source_loginserver [{1}] not found!", - in_account_username, - source_loginserver + "account [{}] source_loginserver [{}] not found!", + c.username, + c.source_loginserver ); return false; } - server.db->UpdateLoginserverAccountPasswordHash( - in_account_username, - source_loginserver, - eqcrypt_hash( - in_account_username, - in_account_password, - mode - ) - ); + LoginAccountsRepository::UpdateAccountPassword(database, a, c.password); LogInfo( - "account [{0}] source_loginserver [{1}] credentials updated!", - in_account_username, - source_loginserver + "account [{}] source_loginserver [{}] credentials updated!", + c.username, + c.source_loginserver ); return true; } -/** - * @param in_account_username - * @param in_account_password - */ -bool AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName( - const std::string &in_account_username, - const std::string &in_account_password -) +bool AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName(LoginAccountContext c) { auto mode = server.options.GetEncryptionMode(); - auto hash = eqcrypt_hash(in_account_username, in_account_password, mode); - - bool updated_account = server.db->UpdateLoginWorldAdminAccountPasswordByUsername( - in_account_username, - hash + auto hash = eqcrypt_hash( + c.username, + c.password, + mode ); + auto a = LoginServerAdminsRepository::GetByName(database, c.username); + if (!a.id) { + LogError( + "account_name [{}] not found!", + c.username + ); + + return false; + } + + a.account_password = hash; + auto updated_account = LoginServerAdminsRepository::UpdateOne(database, a); + LogInfo( - "[{}] account_name [{}] status [{}]", - __func__, - in_account_username, + "account_name [{}] status [{}]", + c.username, (updated_account ? "success" : "failed") ); @@ -262,15 +117,7 @@ bool AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName( constexpr int REQUEST_TIMEOUT_MS = 1500; -/** - * @param in_account_username - * @param in_account_password - * @return - */ -uint32 AccountManagement::CheckExternalLoginserverUserCredentials( - const std::string &in_account_username, - const std::string &in_account_password -) +uint64 AccountManagement::CheckExternalLoginserverUserCredentials(LoginAccountContext c) { auto res = task_runner.Enqueue( [&]() -> uint32 { @@ -278,11 +125,11 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( uint32 ret = 0; EQ::Net::DaybreakConnectionManager mgr; - std::shared_ptr c; + std::shared_ptr conn; mgr.OnNewConnection( [&](std::shared_ptr connection) { - c = connection; + conn = connection; } ); @@ -296,7 +143,7 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( EQ::Net::DynamicPacket p; p.PutUInt16(0, 1); //OP_SessionReady p.PutUInt32(2, 2); - c->QueuePacket(p); + conn->QueuePacket(p); } else if (EQ::Net::StatusDisconnected == to) { running = false; @@ -310,13 +157,12 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( switch (opcode) { case 0x0017: //OP_ChatMessage { - size_t buffer_len = - in_account_username.length() + in_account_password.length() + 2; + size_t buffer_len = c.username.length() + c.password.length() + 2; std::unique_ptr buffer(new char[buffer_len]); - strcpy(&buffer[0], in_account_username.c_str()); - strcpy(&buffer[in_account_username.length() + 1], in_account_password.c_str()); + strcpy(&buffer[0], c.username.c_str()); + strcpy(&buffer[c.username.length() + 1], c.password.c_str()); size_t encrypted_len = buffer_len; @@ -330,11 +176,11 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( p.PutUInt32(2, 3); eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true); - c->QueuePacket(p); + conn->QueuePacket(p); break; } case 0x0018: { - auto encrypt_size = p.Length() - 12; + auto encrypt_size = p.Length() - 12; if (encrypt_size % 8 > 0) { encrypt_size = (encrypt_size / 8) * 8; } @@ -394,15 +240,15 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( return res.get(); } -uint32 AccountManagement::HealthCheckUserLogin() +uint64 AccountManagement::HealthCheckUserLogin() { std::string in_account_username = "healthcheckuser"; std::string in_account_password = "healthcheckpassword"; auto res = task_runner.Enqueue( - [&]() -> uint32 { + [&]() -> uint64 { bool running = true; - uint32 ret = 0; + uint64 ret = 0; EQ::Net::DaybreakConnectionManager mgr; std::shared_ptr c; @@ -461,7 +307,7 @@ uint32 AccountManagement::HealthCheckUserLogin() break; } case 0x0018: { - auto encrypt_size = p.Length() - 12; + auto encrypt_size = p.Length() - 12; if (encrypt_size % 8 > 0) { encrypt_size = (encrypt_size / 8) * 8; } @@ -487,11 +333,11 @@ uint32 AccountManagement::HealthCheckUserLogin() mgr.Connect("127.0.0.1", 5999); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); - auto &loop = EQ::EventLoop::Get(); + auto &loop = EQ::EventLoop::Get(); while (running) { std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); if (std::chrono::duration_cast(end - begin).count() > 2000) { - ret = 0; + ret = 0; running = false; } @@ -504,3 +350,59 @@ uint32 AccountManagement::HealthCheckUserLogin() return res.get(); } + +bool AccountManagement::CreateLoginserverWorldAdminAccount( + const std::string &username, + const std::string &password, + const std::string &email, + const std::string &first_name, + const std::string &last_name, + const std::string &ip_address +) +{ + auto mode = server.options.GetEncryptionMode(); + auto hash = eqcrypt_hash(username, password, mode); + + LogInfo( + "Attempting to create world admin account | username [{}] encryption algorithm [{}] ({})", + username, + GetEncryptionByModeId(mode), + mode + ); + + auto a = LoginServerAdminsRepository::GetByName(database, username); + if (a.id > 0) { + LogWarning( + "Attempting to create world admin account for user [{}] but already exists!", + username + ); + + return false; + } + + a = LoginServerAdminsRepository::NewEntity(); + a.account_name = username; + a.account_password = hash; + a.first_name = first_name; + a.last_name = last_name; + a.email = email; + a.registration_ip_address = ip_address; + a.registration_date = std::time(nullptr); + + a = LoginServerAdminsRepository::InsertOne(database, a); + + if (a.id > 0) { + LogInfo( + "Account creation success for user [{}] encryption algorithm [{}] ({}) new admin id [{}]", + username, + GetEncryptionByModeId(mode), + mode, + a.id + ); + return true; + } + + LogError("Failed to create world admin account account for user [{}] !", username); + + return false; +} diff --git a/loginserver/account_management.h b/loginserver/account_management.h index 190c26437..e047e4468 100644 --- a/loginserver/account_management.h +++ b/loginserver/account_management.h @@ -3,32 +3,22 @@ #include "iostream" #include "../common/types.h" +#include "login_types.h" +#include "encryption.h" +#include "login_server.h" + +extern LoginServer server; +extern Database database; class AccountManagement { public: + static uint64 CreateLoginServerAccount(LoginAccountContext c); + static uint64 CheckLoginserverUserCredentials(LoginAccountContext c); + static bool UpdateLoginserverUserCredentials(LoginAccountContext c); + static uint64 CheckExternalLoginserverUserCredentials(LoginAccountContext c); + static bool UpdateLoginserverWorldAdminAccountPasswordByName(LoginAccountContext c); + static uint64 HealthCheckUserLogin(); - /** - * @param username - * @param password - * @param email - * @param source_loginserver - * @param login_account_id - * @return - */ - static int32 CreateLoginServerAccount( - std::string username, - std::string password, - std::string email = "", - const std::string &source_loginserver = "local", - uint32 login_account_id = 0 - ); - - /** - * @param username - * @param password - * @param email - * @return - */ static bool CreateLoginserverWorldAdminAccount( const std::string &username, const std::string &password, @@ -37,50 +27,6 @@ public: const std::string &last_name = "", const std::string &ip_address = "" ); - - /** - * @param in_account_username - * @param in_account_password - * @return - */ - static uint32 CheckLoginserverUserCredentials( - const std::string &in_account_username, - const std::string &in_account_password, - const std::string &source_loginserver = "local" - ); - - /** - * @param in_account_username - * @param in_account_password - * @return - */ - static bool UpdateLoginserverUserCredentials( - const std::string &in_account_username, - const std::string &in_account_password, - const std::string &source_loginserver = "local" - ); - - /** - * @param in_account_username - * @param in_account_password - * @return - */ - static uint32 CheckExternalLoginserverUserCredentials( - const std::string &in_account_username, - const std::string &in_account_password - ); - - /** - * @param in_account_username - * @param in_account_password - * @return - */ - static bool UpdateLoginserverWorldAdminAccountPasswordByName( - const std::string &in_account_username, - const std::string &in_account_password - ); - - static uint32 HealthCheckUserLogin(); }; diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 62f7fb1f2..41cbec896 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -1,25 +1,18 @@ #include "client.h" #include "login_server.h" -#include "../common/misc_functions.h" -#include "../common/eqemu_logsys.h" -#include "../common/strings.h" #include "encryption.h" #include "account_management.h" extern LoginServer server; -/** - * @param c - * @param v - */ Client::Client(std::shared_ptr c, LSClientVersion v) { - m_connection = c; - m_client_version = v; - m_client_status = cs_not_sent_session_ready; - m_account_id = 0; - m_play_server_id = 0; - m_play_sequence_id = 0; + m_connection = c; + m_client_version = v; + m_client_status = cs_not_sent_session_ready; + m_account_id = 0; + m_selected_play_server_id = 0; + m_play_sequence_id = 0; } bool Client::Process() @@ -43,8 +36,8 @@ bool Client::Process() switch (app->GetOpcode()) { case OP_SessionReady: { - LogInfo("Session ready received from client account {}", GetClientDescription()); - Handle_SessionReady((const char *) app->pBuffer, app->Size()); + LogInfo("Session ready received from client account {}", GetClientLoggingDescription()); + HandleSessionReady((const char *) app->pBuffer, app->Size()); break; } case OP_Login: { @@ -53,9 +46,9 @@ bool Client::Process() break; } - LogInfo("Login received from client {}", GetClientDescription()); + LogInfo("Login received from client {}", GetClientLoggingDescription()); - Handle_Login((const char *) app->pBuffer, app->Size()); + HandleLogin((const char *) app->pBuffer, app->Size()); break; } case OP_ServerListRequest: { @@ -64,18 +57,18 @@ bool Client::Process() break; } - LogInfo("Server list request received from client {}", GetClientDescription()); + LogInfo("Server list request received from client {}", GetClientLoggingDescription()); SendServerListPacket(*(uint32_t *) app->pBuffer); break; } case OP_PlayEverquestRequest: { - if (app->Size() < sizeof(PlayEverquestRequest_Struct)) { + if (app->Size() < sizeof(PlayEverquestRequest)) { LogError("Play received but it is too small, discarding"); break; } - Handle_Play((const char *) app->pBuffer); + SendPlayToWorld((const char *) app->pBuffer); break; } } @@ -87,13 +80,7 @@ bool Client::Process() return true; } -/** - * Sends our reply to session ready packet - * - * @param data - * @param size - */ -void Client::Handle_SessionReady(const char *data, unsigned int size) +void Client::HandleSessionReady(const char *data, unsigned int size) { if (m_client_status != cs_not_sent_session_ready) { LogError("Session ready received again after already being received"); @@ -107,11 +94,8 @@ void Client::Handle_SessionReady(const char *data, unsigned int size) m_client_status = cs_waiting_for_login; - /** - * The packets are identical between the two versions - */ - auto *outapp = new EQApplicationPacket(OP_ChatMessage, sizeof(LoginHandShakeReply_Struct)); - auto buf = reinterpret_cast(outapp->pBuffer); + auto *outapp = new EQApplicationPacket(OP_ChatMessage, sizeof(LoginHandShakeReply)); + auto buf = reinterpret_cast(outapp->pBuffer); buf->base_header.sequence = 0x02; buf->base_reply.success = true; buf->base_reply.error_str_id = 0x65; // 101 "No Error" @@ -120,13 +104,7 @@ void Client::Handle_SessionReady(const char *data, unsigned int size) delete outapp; } -/** - * Verifies login and send a reply - * - * @param data - * @param size - */ -void Client::Handle_Login(const char *data, unsigned int size) +void Client::HandleLogin(const char *data, unsigned int size) { if (m_client_status != cs_waiting_for_login) { LogError("Login received after already having logged in"); @@ -134,41 +112,37 @@ void Client::Handle_Login(const char *data, unsigned int size) } // login user/pass are variable length after unencrypted opcode and base message header (size includes opcode) - constexpr int header_size = sizeof(uint16_t) + sizeof(LoginBaseMessage_Struct); - int data_size = size - header_size; + constexpr int header_size = sizeof(uint16_t) + sizeof(LoginBaseMessage); + int data_size = size - header_size; if (size <= header_size) { - LogError("Login received packet of size: {0}, this would cause a buffer overflow, discarding", size); + LogError("Login received packet of size: {}, this would cause a buffer overflow, discarding", size); return; } if (data_size % 8 != 0) { - LogError("Login received packet of size: {0}, this would cause a block corruption, discarding", size); + LogError("Login received packet of size: {}, this would cause a block corruption, discarding", size); return; } - char *login_packet_buffer = nullptr; - unsigned int db_account_id = 0; std::string db_loginserver = "local"; - if (server.options.CanAutoLinkAccounts()) { + if (std::getenv("LSPX")) { db_loginserver = "eqemu"; } - std::string db_account_password_hash; - std::string outbuffer; outbuffer.resize(data_size); - if (outbuffer.length() == 0) { + if (outbuffer.empty()) { LogError("Corrupt buffer sent to server, no length"); return; } // data starts at base message header (opcode not included) - auto r = eqcrypt_block(data + sizeof(LoginBaseMessage_Struct), data_size, &outbuffer[0], 0); + auto r = eqcrypt_block(data + sizeof(LoginBaseMessage), data_size, &outbuffer[0], false); if (r == nullptr) { LogError("Failed to decrypt eqcrypt block"); return; @@ -182,131 +156,114 @@ void Client::Handle_Login(const char *data, unsigned int size) return; } +// std::cout << "User: " << user << std::endl; + // only need to copy the base header for reply options, ignore login info - memcpy(&m_llrs, data, sizeof(LoginBaseMessage_Struct)); + memcpy(&m_login_base_message, data, sizeof(LoginBaseMessage)); - bool result = false; - if (outbuffer[0] == 0 && outbuffer[1] == 0) { +// std::cout << "Seq: " << m_login_base_message.sequence << std::endl; +// std::cout << "compressed: " << m_login_base_message.compressed << std::endl; +// std::cout << "encrypt_type: " << m_login_base_message.encrypt_type << std::endl; +// std::cout << "unk3: " << m_login_base_message.unk3 << std::endl; + + bool login_success = false; + bool token_login = outbuffer[0] == 0 && outbuffer[1] == 0; + if (token_login) { if (server.options.IsTokenLoginAllowed()) { - cred = (&outbuffer[2 + user.length()]); - result = server.db->GetLoginTokenDataFromToken( - cred, - m_connection->GetRemoteAddr(), - db_account_id, - db_loginserver, - user - ); + cred = (&outbuffer[2 + user.length()]); + // todo: implement token login + // SELECT login_server, username, account_id FROM login_tickets WHERE expires > NOW() AND id='{}' AND ip_address='{}' LIMIT 1 +// login_success ? DoSuccessfulLogin(user, db_account_id, db_loginserver) : SendFailedLogin(); + SendFailedLogin(); } + + return; } - else { - if (server.options.IsPasswordLoginAllowed()) { - cred = (&outbuffer[1 + user.length()]); - auto components = Strings::Split(user, ':'); - if (components.size() == 2) { - db_loginserver = components[0]; - user = components[1]; - } - // health checks - if (ProcessHealthCheck(user)) { - DoFailedLogin(); - return; - } + // normal login + cred = (&outbuffer[1 + user.length()]); + auto components = Strings::Split(user, ':'); + if (components.size() == 2) { + db_loginserver = components[0]; + user = components[1]; + } - LogInfo( - "Attempting password based login [{0}] login [{1}]", - user, - db_loginserver - ); + // health checks + if (ProcessHealthCheck(user)) { + SendFailedLogin(); + return; + } - ParseAccountString(user, user, db_loginserver); + LogInfo( + "Attempting password based login [{}] login [{}]", + user, + db_loginserver + ); - if (server.db->GetLoginDataFromAccountInfo(user, db_loginserver, db_account_password_hash, db_account_id)) { - result = VerifyLoginHash(user, db_loginserver, cred, db_account_password_hash); + ParseAccountString(user, user, db_loginserver); -#ifdef LSPX - // if user updated their password on the login server, update it here by validating their credentials with the login server - if (!result && db_loginserver == "eqemu") { - uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(user, cred); - if (account_id > 0) { - auto encryption_mode = server.options.GetEncryptionMode(); - server.db->UpdateLoginserverAccountPasswordHash( - user, - db_loginserver, - eqcrypt_hash(user, cred, encryption_mode) - ); - LogInfo("Updating eqemu account [{}] password hash", account_id); - result = true; - } + LoginAccountContext c = {}; + c.username = user; + c.password = cred; + c.source_loginserver = db_loginserver; + + auto a = LoginAccountsRepository::GetAccountFromContext(database, c); + if (a.id > 0) { + login_success = VerifyAndUpdateLoginHash(c, a); + + // if user updated their password on the login server, update it here by validating their credentials with the login server + if (std::getenv("LSPX") && !login_success && db_loginserver == "eqemu") { + LogInfo("LSPX | Attempting login account via [{}]", db_loginserver); + uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(c); + LogInfo("LSPX | External login account id [{}]", account_id); + if (account_id > 0) { + auto updated_account = LoginAccountsRepository::UpdateAccountPassword(database, a, cred); + if (!updated_account.id) { + LogError("Failed to update eqemu account [{}] password hash", account_id); + SendFailedLogin(); + return; } -#endif - - LogDebug("Success [{0}]", (result ? "true" : "false")); - } - else { - m_client_status = cs_creating_account; - AttemptLoginAccountCreation(user, cred, db_loginserver); + LogInfo("Updating eqemu account [{}] password hash", account_id); + DoSuccessfulLogin(updated_account); return; } } + + LogInfo("Successful login [{}]", (login_success ? "true" : "false")); + login_success ? DoSuccessfulLogin(a) : SendFailedLogin(); + return; } - /** - * Login accepted - */ - if (result) { - LogInfo( - "login [{0}] user [{1}] Login succeeded", - db_loginserver, - user - ); - - DoSuccessfulLogin(user, db_account_id, db_loginserver); - } - else { - LogInfo( - "login [{0}] user [{1}] Login failed", - db_loginserver, - user - ); - - DoFailedLogin(); - } + // if we are here, the account does not exist + m_client_status = cs_creating_account; + AttemptLoginAccountCreation(c); } -/** - * Sends a packet to the requested server to see if the client is allowed or not - * - * @param data - */ -void Client::Handle_Play(const char *data) +void Client::SendPlayToWorld(const char *data) { if (m_client_status != cs_logged_in) { LogError("Client sent a play request when they were not logged in, discarding"); return; } - const auto *play = (const PlayEverquestRequest_Struct *) data; + const auto *play = (const PlayEverquestRequest *) data; auto server_id_in = (unsigned int) play->server_number; auto sequence_in = (unsigned int) play->base_header.sequence; LogInfo( - "[Handle_Play] Play received from client [{}] server number [{}] sequence [{}]", + "[SendPlayToWorld] Play received from client [{}] server number [{}] sequence [{}]", GetAccountName(), server_id_in, sequence_in ); - m_play_server_id = (unsigned int) play->server_number; - m_play_sequence_id = sequence_in; - m_play_server_id = server_id_in; - server.server_manager->SendUserToWorldRequest(server_id_in, m_account_id, m_loginserver_name); + m_selected_play_server_id = (unsigned int) play->server_number; + m_play_sequence_id = sequence_in; + m_selected_play_server_id = server_id_in; + server.server_manager->SendUserLoginToWorldRequest(server_id_in, m_account_id, m_loginserver_name); } -/** - * @param seq - */ void Client::SendServerListPacket(uint32 seq) { auto app = server.server_manager->CreateServerListPacket(this, seq); @@ -316,11 +273,11 @@ void Client::SendServerListPacket(uint32 seq) void Client::SendPlayResponse(EQApplicationPacket *outapp) { - LogInfo("Sending play response for {}", GetClientDescription()); + LogInfo("Sending play response for {}", GetClientLoggingDescription()); m_connection->QueuePacket(outapp); } -void Client::GenerateKey() +void Client::GenerateRandomLoginKey() { m_key.clear(); int count = 0; @@ -339,229 +296,193 @@ void Client::GenerateKey() } } -/** - * @param user - * @param pass - * @param loginserver - */ -void Client::AttemptLoginAccountCreation( - const std::string &user, - const std::string &pass, - const std::string &loginserver -) +void Client::AttemptLoginAccountCreation(LoginAccountContext c) { - LogInfo("user [{}] loginserver [{}]", user, loginserver); + LogInfo("user [{}] loginserver [{}]", c.username, c.source_loginserver); -#ifdef LSPX - if (loginserver == "eqemu") { - LogInfo("Attempting login account creation via '{0}'", loginserver); - - if (!server.options.CanAutoLinkAccounts()) { - LogInfo("CanAutoLinkAccounts disabled - sending failed login"); - DoFailedLogin(); - return; - } - - - uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials( - user, - pass - ); + if (std::getenv("LSPX") && c.source_loginserver == "eqemu") { + LogInfo("LSPX | Attempting login account creation via [{}]", c.source_loginserver); + uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(c); + c.login_account_id = account_id; if (account_id > 0) { - LogInfo("Found and creating eqemu account [{}]", account_id); - CreateEQEmuAccount(user, pass, account_id); + LogInfo("LSPX | Found and creating eqemu account [{}]", account_id); + auto a = LoginAccountsRepository::CreateAccountFromContext(database, c); + if (a.id > 0) { + DoSuccessfulLogin(a); + return; + } + } + + LogInfo("LSPX | External authentication failed for user [{}]", c.username); + + SendFailedLogin(); + return; + } + + if (server.options.CanAutoCreateAccounts() && c.source_loginserver == "local") { + LogInfo("CanAutoCreateAccounts enabled, attempting to crate account [{}]", c.username); + auto a = LoginAccountsRepository::CreateAccountFromContext(database, c); + if (a.id > 0) { + DoSuccessfulLogin(a); return; } - DoFailedLogin(); - return; - } -#endif - - if (server.options.CanAutoCreateAccounts() && loginserver == "local") { - LogInfo("CanAutoCreateAccounts enabled, attempting to creating account [{0}]", user); - CreateLocalAccount(user, pass); return; } - DoFailedLogin(); + SendFailedLogin(); } -void Client::DoFailedLogin() +void Client::SendFailedLogin() { - m_stored_user.clear(); - m_stored_pass.clear(); + m_stored_username.clear(); + m_stored_password.clear(); // unencrypted - LoginBaseMessage_Struct base_header{}; - base_header.sequence = m_llrs.sequence; // login (3) - base_header.encrypt_type = m_llrs.encrypt_type; + LoginBaseMessage h{}; + h.sequence = m_login_base_message.sequence; // login (3) + h.encrypt_type = m_login_base_message.encrypt_type; // encrypted - PlayerLoginReply_Struct login_reply{}; - login_reply.base_reply.success = false; - login_reply.base_reply.error_str_id = 105; // Error - The username and/or password were not valid + PlayerLoginReply r{}; + r.base_reply.success = false; + r.base_reply.error_str_id = 105; // Error - The username and/or password were not valid char encrypted_buffer[80] = {0}; - auto rc = eqcrypt_block((const char*)&login_reply, sizeof(login_reply), encrypted_buffer, 1); + auto rc = eqcrypt_block((const char *) &r, sizeof(r), encrypted_buffer, 1); if (rc == nullptr) { LogDebug("Failed to encrypt eqcrypt block for failed login"); } - constexpr int outsize = sizeof(LoginBaseMessage_Struct) + sizeof(encrypted_buffer); + constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer); EQApplicationPacket outapp(OP_LoginAccepted, outsize); - outapp.WriteData(&base_header, sizeof(base_header)); + outapp.WriteData(&h, sizeof(h)); outapp.WriteData(&encrypted_buffer, sizeof(encrypted_buffer)); m_connection->QueuePacket(&outapp); m_client_status = cs_failed_to_login; } -/** - * Verifies a login hash, will also attempt to update a login hash if needed - * - * @param account_username - * @param source_loginserver - * @param account_password - * @param password_hash - * @return - */ -bool Client::VerifyLoginHash( - const std::string &account_username, - const std::string &source_loginserver, - const std::string &account_password, - const std::string &password_hash -) +bool Client::VerifyAndUpdateLoginHash(LoginAccountContext c, const LoginAccountsRepository::LoginAccounts &a) { auto encryption_mode = server.options.GetEncryptionMode(); - if (eqcrypt_verify_hash(account_username, account_password, password_hash, encryption_mode)) { + if (eqcrypt_verify_hash(a.account_name, c.password, a.account_password, encryption_mode)) { return true; } - else { - if (server.options.IsUpdatingInsecurePasswords()) { - if (encryption_mode < EncryptionModeArgon2) { - encryption_mode = EncryptionModeArgon2; - } - uint32 insecure_source_encryption_mode = 0; - if (password_hash.length() == CryptoHash::md5_hash_length) { - for (int i = EncryptionModeMD5; i <= EncryptionModeMD5Triple; ++i) { - if (i != encryption_mode && - eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { - insecure_source_encryption_mode = i; - } - } - } - else if (password_hash.length() == CryptoHash::sha1_hash_length && insecure_source_encryption_mode == 0) { - for (int i = EncryptionModeSHA; i <= EncryptionModeSHATriple; ++i) { - if (i != encryption_mode && - eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { - insecure_source_encryption_mode = i; - } - } - } - else if (password_hash.length() == CryptoHash::sha512_hash_length && insecure_source_encryption_mode == 0) { - for (int i = EncryptionModeSHA512; i <= EncryptionModeSHA512Triple; ++i) { - if (i != encryption_mode && - eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { - insecure_source_encryption_mode = i; - } - } - } + if (encryption_mode < EncryptionModeArgon2) { + encryption_mode = EncryptionModeArgon2; + } - if (insecure_source_encryption_mode > 0) { - LogInfo( - "[{}] Updated insecure password user [{}] loginserver [{}] from mode [{}] ({}) to mode [{}] ({})", - __func__, - account_username, - source_loginserver, - GetEncryptionByModeId(insecure_source_encryption_mode), - insecure_source_encryption_mode, - GetEncryptionByModeId(encryption_mode), - encryption_mode - ); + uint32 insecure_source_encryption_mode = 0; - server.db->UpdateLoginserverAccountPasswordHash( - account_username, - source_loginserver, - eqcrypt_hash( - account_username, - account_password, - encryption_mode - ) - ); - - return true; + auto verify_encryption_mode = [&](int start, int end) { + for (int i = start; i <= end; ++i) { + if (i != encryption_mode && eqcrypt_verify_hash(a.account_name, c.password, a.account_password, i)) { + insecure_source_encryption_mode = i; } } + }; + + switch (a.account_password.length()) { + case CryptoHash::md5_hash_length: + verify_encryption_mode(EncryptionModeMD5, EncryptionModeMD5Triple); + break; + case CryptoHash::sha1_hash_length: + if (insecure_source_encryption_mode == 0) { + verify_encryption_mode(EncryptionModeSHA, EncryptionModeSHATriple); + } + break; + case CryptoHash::sha512_hash_length: + if (insecure_source_encryption_mode == 0) { + verify_encryption_mode(EncryptionModeSHA512, EncryptionModeSHA512Triple); + } + break; + } + + if (insecure_source_encryption_mode > 0) { + LogInfo( + "Updated insecure password user [{}] loginserver [{}] from mode [{}] ({}) to mode [{}] ({})", + c.username, + c.source_loginserver, + GetEncryptionByModeId(insecure_source_encryption_mode), + insecure_source_encryption_mode, + GetEncryptionByModeId(encryption_mode), + encryption_mode + ); + + LoginAccountsRepository::UpdateAccountPassword(database, a, c.password); + + return true; } return false; } -/** - * @param in_account_name - * @param db_account_id - * @param db_loginserver - */ -void Client::DoSuccessfulLogin( - const std::string& in_account_name, - int db_account_id, - const std::string &db_loginserver -) +void Client::DoSuccessfulLogin(LoginAccountsRepository::LoginAccounts &a) { - m_stored_user.clear(); - m_stored_pass.clear(); + m_stored_username.clear(); + m_stored_password.clear(); - server.client_manager->RemoveExistingClient(db_account_id, db_loginserver); + LogInfo( + "Successful login for user id [{}] account name [{}] login server [{}]", + a.id, + a.account_name, + a.source_loginserver + ); + + server.client_manager->RemoveExistingClient(a.id, a.source_loginserver); in_addr in{}; in.s_addr = m_connection->GetRemoteIP(); - server.db->UpdateLSAccountData(db_account_id, std::string(inet_ntoa(in))); - GenerateKey(); + a.last_ip_address = std::string(inet_ntoa(in)); + LoginAccountsRepository::UpdateOne(database, a); - m_account_id = db_account_id; - m_account_name = in_account_name; - m_loginserver_name = db_loginserver; + GenerateRandomLoginKey(); + + m_account_id = a.id; + m_account_name = a.account_name; + m_loginserver_name = a.source_loginserver; // unencrypted - LoginBaseMessage_Struct base_header{}; - base_header.sequence = m_llrs.sequence; - base_header.compressed = false; - base_header.encrypt_type = m_llrs.encrypt_type; - base_header.unk3 = m_llrs.unk3; + LoginBaseMessage h{}; + h.sequence = m_login_base_message.sequence; + h.compressed = false; + h.encrypt_type = m_login_base_message.encrypt_type; + h.unk3 = m_login_base_message.unk3; // not serializing any of the variable length strings so just use struct directly - PlayerLoginReply_Struct login_reply{}; - login_reply.base_reply.success = true; - login_reply.base_reply.error_str_id = 101; // No Error - login_reply.unk1 = 0; - login_reply.unk2 = 0; - login_reply.lsid = db_account_id; - login_reply.failed_attempts = 0; - login_reply.show_player_count = server.options.IsShowPlayerCountEnabled(); - login_reply.offer_min_days = 99; - login_reply.offer_min_views = -1; - login_reply.offer_cooldown_minutes = 0; - login_reply.web_offer_number = 0; - login_reply.web_offer_min_days = 99; - login_reply.web_offer_min_views = -1; - login_reply.web_offer_cooldown_minutes = 0; - memcpy(login_reply.key, m_key.c_str(), m_key.size()); + PlayerLoginReply r{}; + r.base_reply.success = true; + r.base_reply.error_str_id = 101; // No Error + r.unk1 = 0; + r.unk2 = 0; + r.lsid = a.id; + r.failed_attempts = 0; + r.show_player_count = server.options.IsShowPlayerCountEnabled(); + r.offer_min_days = 99; + r.offer_min_views = -1; + r.offer_cooldown_minutes = 0; + r.web_offer_number = 0; + r.web_offer_min_days = 99; + r.web_offer_min_views = -1; + r.web_offer_cooldown_minutes = 0; + memcpy(r.key, m_key.c_str(), m_key.size()); - SendExpansionPacketData(login_reply); + SendExpansionPacketData(r); char encrypted_buffer[80] = {0}; - auto rc = eqcrypt_block((const char*)&login_reply, sizeof(login_reply), encrypted_buffer, 1); + + auto rc = eqcrypt_block((const char *) &r, sizeof(r), encrypted_buffer, 1); if (rc == nullptr) { LogDebug("Failed to encrypt eqcrypt block"); } - constexpr int outsize = sizeof(LoginBaseMessage_Struct) + sizeof(encrypted_buffer); - auto outapp = std::make_unique(OP_LoginAccepted, outsize); - outapp->WriteData(&base_header, sizeof(base_header)); + constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer); + auto outapp = std::make_unique(OP_LoginAccepted, outsize); + outapp->WriteData(&h, sizeof(h)); outapp->WriteData(&encrypted_buffer, sizeof(encrypted_buffer)); m_connection->QueuePacket(outapp.get()); @@ -569,19 +490,21 @@ void Client::DoSuccessfulLogin( m_client_status = cs_logged_in; } -void Client::SendExpansionPacketData(PlayerLoginReply_Struct& plrs) +void Client::SendExpansionPacketData(PlayerLoginReply &plrs) { SerializeBuffer buf; //from eqlsstr_us.txt id of each expansion, excluding 'Everquest' - int ExpansionLookup[20] = { 3007, 3008, 3009, 3010, 3012, - 3014, 3031, 3033, 3036, 3040, - 3045, 3046, 3047, 3514, 3516, - 3518, 3520, 3522, 3524 }; + int ExpansionLookup[20] = { + 3007, 3008, 3009, 3010, 3012, + 3014, 3031, 3033, 3036, 3040, + 3045, 3046, 3047, 3514, 3516, + 3518, 3520, 3522, 3524 + }; if (server.options.IsDisplayExpansions()) { - int32_t expansion = server.options.GetMaxExpansions(); + int32_t expansion = server.options.GetMaxExpansions(); int32_t owned_expansion = (expansion << 1) | 1; if (m_client_version == cv_sod) { @@ -593,15 +516,14 @@ void Client::SendExpansionPacketData(PlayerLoginReply_Struct& plrs) buf.WriteInt32(19); //number of expansions to include in packet //generate expansion data - for (int i = 0; i < 19; i++) - { - buf.WriteInt32(i); //sequenctial number - buf.WriteInt32((expansion & (1 << i)) == (1 << i) ? 0x01 : 0x00); //1 own 0 not own + for (int i = 0; i < 19; i++) { + buf.WriteInt32(i); //sequenctial number + buf.WriteInt32((expansion & (1 << i)) == (1 << i) ? 0x01 : 0x00); //1 own 0 not own buf.WriteInt8(0x00); - buf.WriteInt32(ExpansionLookup[i]); //from eqlsstr_us.txt - buf.WriteInt32(0x179E); //from eqlsstr_us.txt for buttons/order - buf.WriteInt32(0xFFFFFFFF); //end identification - buf.WriteInt8(0x0); //force order window to appear 1 appear 0 not appear + buf.WriteInt32(ExpansionLookup[i]); //from eqlsstr_us.txt + buf.WriteInt32(0x179E); //from eqlsstr_us.txt for buttons/order + buf.WriteInt32(0xFFFFFFFF); //end identification + buf.WriteInt8(0x0); //force order window to appear 1 appear 0 not appear buf.WriteInt8(0x0); buf.WriteInt32(0x0000); buf.WriteInt32(0x0000); @@ -612,79 +534,21 @@ void Client::SendExpansionPacketData(PlayerLoginReply_Struct& plrs) m_connection->QueuePacket(out.get()); } - else if (m_client_version == cv_titanium) - { - if (expansion >= EQ::expansions::bitPoR) - { + else if (m_client_version == cv_titanium) { + if (expansion >= EQ::expansions::bitPoR) { // Titanium shipped with 10 expansions. Set owned expansions to be max 10. plrs.offer_min_days = ((EQ::expansions::bitDoD << 2) | 1) - 2; } - else - { + else { plrs.offer_min_days = owned_expansion; } // Titanium shipped with 10 expansions. Set owned expansions to be max 10. plrs.web_offer_min_views = ((EQ::expansions::bitDoD << 2) | 1) - 2; } - - } - -} - -/** - * @param username - * @param password - */ -void Client::CreateLocalAccount(const std::string &username, const std::string &password) -{ - auto mode = server.options.GetEncryptionMode(); - auto hash = eqcrypt_hash(username, password, mode); - unsigned int db_id = 0; - if (!server.db->CreateLoginData(username, hash, "local", db_id)) { - DoFailedLogin(); - } - else { - DoSuccessfulLogin(username, db_id, "local"); } } -/** - * @param in_account_name - * @param in_account_password - * @param loginserver_account_id - */ -void Client::CreateEQEmuAccount( - const std::string &in_account_name, - const std::string &in_account_password, - unsigned int loginserver_account_id -) -{ - auto mode = server.options.GetEncryptionMode(); - auto hash = eqcrypt_hash(in_account_name, in_account_password, mode); - - if (server.db->DoesLoginServerAccountExist(in_account_name, hash, "eqemu", loginserver_account_id)) { - DoSuccessfulLogin(in_account_name, loginserver_account_id, "eqemu"); - return; - } - - if (!server.db->CreateLoginDataWithID(in_account_name, hash, "eqemu", loginserver_account_id)) { - DoFailedLogin(); - } - else { - DoSuccessfulLogin(in_account_name, loginserver_account_id, "eqemu"); - } -} - -bool Client::ProcessHealthCheck(std::string username) -{ - if (username == "healthcheckuser") { - return true; - } - - return false; -} - -std::string Client::GetClientDescription() +std::string Client::GetClientLoggingDescription() { in_addr in{}; in.s_addr = GetConnection()->GetRemoteIP(); diff --git a/loginserver/client.h b/loginserver/client.h index cc24ebe23..5c62f2c83 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -8,204 +8,60 @@ #include "../common/net/dns.h" #include "../common/net/daybreak_connection.h" #include "login_types.h" +#include "../common/repositories/login_accounts_repository.h" #include -/** - * Client class, controls a single client and it's connection to the login server - */ class Client { public: - - /** - * Constructor, sets our connection to c and version to v - * - * @param c - * @param v - */ Client(std::shared_ptr c, LSClientVersion v); - - /** - * Destructor - */ ~Client() {} - - /** - * Processes the client's connection and does various actions - * - * @return - */ bool Process(); + void HandleSessionReady(const char *data, unsigned int size); + void HandleLogin(const char *data, unsigned int size); - /** - * Sends our reply to session ready packet - * - * @param data - * @param size - */ - void Handle_SessionReady(const char *data, unsigned int size); - - /** - * Verifies login and send a reply - * - * @param data - * @param size - */ - void Handle_Login(const char *data, unsigned int size); - - /** - * Sends the expansion data packet - * - * Titanium uses the encrypted data block to contact the expansion (You own xxx:) and the max expansions (of yyy) - * Rof uses a seperate data packet specifically for the expansion data - * Live, as of July 2021 uses a similar but slightly different seperate data packet - * - * @param PlayerLoginReply_Struct - * - */ - void SendExpansionPacketData(PlayerLoginReply_Struct& plrs); - - /** - * Sends a packet to the requested server to see if the client is allowed or not - * - * @param data - */ - void Handle_Play(const char *data); - - /** - * Sends a server list packet to the client - * - * @param seq - */ + // Sends the expansion data packet + // Titanium uses the encrypted data block to contact the expansion (You own xxx:) and the max expansions (of yyy) + // Rof uses a separate data packet specifically for the expansion data + // Live, as of July 2021 uses a similar but slightly different seperate data packet + void SendExpansionPacketData(PlayerLoginReply &plrs); + void SendPlayToWorld(const char *data); void SendServerListPacket(uint32 seq); - - /** - * Sends the input packet to the client and clears our play response states - * - * @param outapp - */ void SendPlayResponse(EQApplicationPacket *outapp); - - /** - * Generates a random login key for the client during login - */ - void GenerateKey(); - - /** - * Gets the account id of this client - * - * @return - */ + void GenerateRandomLoginKey(); unsigned int GetAccountID() const { return m_account_id; } - - /** - * Gets the loginserver name of this client - * - * @return - */ std::string GetLoginServerName() const { return m_loginserver_name; } - - /** - * Gets the account name of this client - * - * @return - */ std::string GetAccountName() const { return m_account_name; } - - /** - * Returns a description for the client for logging - * @return std::string - */ - std::string GetClientDescription(); - - /** - * Gets the key generated at login for this client - * - * @return - */ - std::string GetKey() const { return m_key; } - - /** - * Gets the server selected to be played on for this client - * - * @return - */ - unsigned int GetPlayServerID() const { return m_play_server_id; } - - /** - * Gets the play sequence state for this client - * - * @return - */ - unsigned int GetPlaySequence() const { return m_play_sequence_id; } - - /** - * Gets the client version - * - * @return - */ + std::string GetClientLoggingDescription(); + std::string GetLoginKey() const { return m_key; } + unsigned int GetSelectedPlayServerID() const { return m_selected_play_server_id; } + unsigned int GetCurrentPlaySequence() const { return m_play_sequence_id; } LSClientVersion GetClientVersion() const { return m_client_version; } - - /** - * Gets the connection for this client - * - * @return - */ std::shared_ptr GetConnection() { return m_connection; } - /** - * Attempts to create a login account - * - * @param user - * @param pass - * @param loginserver - */ - void AttemptLoginAccountCreation(const std::string &user, const std::string &pass, const std::string &loginserver); - - /** - * Does a failed login - */ - void DoFailedLogin(); - - /** - * Verifies a login hash, will also attempt to update a login hash if needed - * - * @param account_username - * @param source_loginserver - * @param account_password - * @param password_hash - * @return - */ - bool VerifyLoginHash( - const std::string &account_username, - const std::string &source_loginserver, - const std::string &account_password, - const std::string &password_hash - ); - - void DoSuccessfulLogin(const std::string& in_account_name, int db_account_id, const std::string &db_loginserver); - void CreateLocalAccount(const std::string &username, const std::string &password); - void CreateEQEmuAccount(const std::string &in_account_name, const std::string &in_account_password, unsigned int loginserver_account_id); + void AttemptLoginAccountCreation(LoginAccountContext c); + void SendFailedLogin(); + bool VerifyAndUpdateLoginHash(LoginAccountContext c, const LoginAccountsRepository::LoginAccounts& a); + void DoSuccessfulLogin(LoginAccountsRepository::LoginAccounts& a); private: - EQ::Random m_random; - std::shared_ptr m_connection; - LSClientVersion m_client_version; - LSClientStatus m_client_status; - - std::string m_account_name; - unsigned int m_account_id; - std::string m_loginserver_name; - unsigned int m_play_server_id; - unsigned int m_play_sequence_id; - std::string m_key; - + EQ::Random m_random; + std::shared_ptr m_connection; + LSClientVersion m_client_version; + LSClientStatus m_client_status; + std::string m_account_name; + unsigned int m_account_id; + std::string m_loginserver_name; + unsigned int m_selected_play_server_id; + unsigned int m_play_sequence_id; + std::string m_key; std::unique_ptr m_login_connection_manager; std::shared_ptr m_login_connection; - LoginBaseMessage_Struct m_llrs; - - std::string m_stored_user; - std::string m_stored_pass; - static bool ProcessHealthCheck(std::string username); + LoginBaseMessage m_login_base_message; + std::string m_stored_username; + std::string m_stored_password; + static bool ProcessHealthCheck(std::string username) { + return username == "healthcheckuser"; + } }; #endif diff --git a/loginserver/client_manager.cpp b/loginserver/client_manager.cpp index c82eb0de8..b4f84c7c7 100644 --- a/loginserver/client_manager.cpp +++ b/loginserver/client_manager.cpp @@ -4,12 +4,12 @@ extern LoginServer server; extern bool run_server; -#include "../common/eqemu_logsys.h" #include "../common/misc.h" #include "../common/path_manager.h" #include "../common/file.h" -void CheckTitaniumOpcodeFile(const std::string &path) { +void CheckTitaniumOpcodeFile(const std::string &path) +{ if (File::Exists(path)) { return; } @@ -32,7 +32,8 @@ void CheckTitaniumOpcodeFile(const std::string &path) { } } -void CheckSoDOpcodeFile(const std::string& path) { +void CheckSoDOpcodeFile(const std::string &path) +{ if (File::Exists(path)) { return; } @@ -56,7 +57,8 @@ void CheckSoDOpcodeFile(const std::string& path) { } } -void CheckLarionOpcodeFile(const std::string& path) { +void CheckLarionOpcodeFile(const std::string &path) +{ if (File::Exists(path)) { return; } @@ -87,8 +89,8 @@ ClientManager::ClientManager() EQStreamManagerInterfaceOptions titanium_opts(titanium_port, false, false); - titanium_stream = new EQ::Net::EQStreamManager(titanium_opts); - titanium_ops = new RegularOpcodeManager; + m_titanium_stream = new EQ::Net::EQStreamManager(titanium_opts); + m_titanium_ops = new RegularOpcodeManager; std::string opcodes_path = fmt::format( "{}/{}", @@ -98,34 +100,34 @@ ClientManager::ClientManager() CheckTitaniumOpcodeFile(opcodes_path); - if (!titanium_ops->LoadOpcodes(opcodes_path.c_str())) { + if (!m_titanium_ops->LoadOpcodes(opcodes_path.c_str())) { LogError( - "ClientManager fatal error: couldn't load opcodes for Titanium file [{0}]", + "ClientManager fatal error: couldn't load opcodes for Titanium file [{}]", server.config.GetVariableString("client_configuration", "titanium_opcodes", "login_opcodes.conf") ); run_server = false; } - titanium_stream->OnNewConnection( + m_titanium_stream->OnNewConnection( [this](std::shared_ptr stream) { LogInfo( - "New Titanium client connection from [{0}:{1}]", + "New Titanium client connection from [{}:{}]", long2ip(stream->GetRemoteIP()), stream->GetRemotePort() ); - stream->SetOpcodeManager(&titanium_ops); + stream->SetOpcodeManager(&m_titanium_ops); Client *c = new Client(stream, cv_titanium); - clients.push_back(c); + m_clients.push_back(c); } ); int sod_port = server.config.GetVariableInt("client_configuration", "sod_port", 5999); EQStreamManagerInterfaceOptions sod_opts(sod_port, false, false); - sod_stream = new EQ::Net::EQStreamManager(sod_opts); - sod_ops = new RegularOpcodeManager; + m_sod_stream = new EQ::Net::EQStreamManager(sod_opts); + m_sod_ops = new RegularOpcodeManager; opcodes_path = fmt::format( "{}/{}", @@ -135,26 +137,26 @@ ClientManager::ClientManager() CheckSoDOpcodeFile(opcodes_path); - if (!sod_ops->LoadOpcodes(opcodes_path.c_str())) { + if (!m_sod_ops->LoadOpcodes(opcodes_path.c_str())) { LogError( - "ClientManager fatal error: couldn't load opcodes for SoD file {0}", + "ClientManager fatal error: couldn't load opcodes for SoD file {}", server.config.GetVariableString("client_configuration", "sod_opcodes", "login_opcodes.conf").c_str() ); run_server = false; } - sod_stream->OnNewConnection( + m_sod_stream->OnNewConnection( [this](std::shared_ptr stream) { LogInfo( - "New SoD+ client connection from [{0}:{1}]", + "New SoD+ client connection from [{}:{}]", long2ip(stream->GetRemoteIP()), stream->GetRemotePort() ); - stream->SetOpcodeManager(&sod_ops); + stream->SetOpcodeManager(&m_sod_ops); auto *c = new Client(stream, cv_sod); - clients.push_back(c); + m_clients.push_back(c); } ); @@ -162,8 +164,8 @@ ClientManager::ClientManager() EQStreamManagerInterfaceOptions larion_opts(larion_port, false, false); - larion_stream = new EQ::Net::EQStreamManager(larion_opts); - larion_ops = new RegularOpcodeManager; + m_larion_stream = new EQ::Net::EQStreamManager(larion_opts); + m_larion_ops = new RegularOpcodeManager; opcodes_path = fmt::format( "{}/{}", @@ -173,115 +175,98 @@ ClientManager::ClientManager() CheckLarionOpcodeFile(opcodes_path); - if (!larion_ops->LoadOpcodes(opcodes_path.c_str())) { + if (!m_larion_ops->LoadOpcodes(opcodes_path.c_str())) { LogError( - "ClientManager fatal error: couldn't load opcodes for Larion file [{0}]", + "ClientManager fatal error: couldn't load opcodes for Larion file [{}]", server.config.GetVariableString("client_configuration", "larion_opcodes", "login_opcodes.conf") ); run_server = false; } - larion_stream->OnNewConnection( + m_larion_stream->OnNewConnection( [this](std::shared_ptr stream) { LogInfo( - "New Larion client connection from [{0}:{1}]", + "New Larion client connection from [{}:{}]", long2ip(stream->GetRemoteIP()), stream->GetRemotePort() ); - stream->SetOpcodeManager(&larion_ops); - Client* c = new Client(stream, cv_larion); - clients.push_back(c); + stream->SetOpcodeManager(&m_larion_ops); + Client *c = new Client(stream, cv_larion); + m_clients.push_back(c); } ); } -ClientManager::~ClientManager() -{ - if (titanium_stream) { - delete titanium_stream; - } - - if (titanium_ops) { - delete titanium_ops; - } - - if (sod_stream) { - delete sod_stream; - } - - if (sod_ops) { - delete sod_ops; - } +ClientManager::~ClientManager() { + delete m_titanium_stream; + delete m_titanium_ops; + delete m_sod_stream; + delete m_sod_ops; } void ClientManager::Process() { ProcessDisconnect(); - auto iter = clients.begin(); - while (iter != clients.end()) { - if ((*iter)->Process() == false) { - LogWarning("Client had a fatal error and had to be removed from the login"); - delete (*iter); - iter = clients.erase(iter); - } - else { - ++iter; - } - } + m_clients.erase( + std::remove_if( + m_clients.begin(), m_clients.end(), + [](Client *c) { + if (!c->Process()) { + LogWarning("Client had a fatal error and had to be removed from the login"); + delete c; + return true; + } + return false; + } + ), + m_clients.end()); } void ClientManager::ProcessDisconnect() { - auto iter = clients.begin(); - while (iter != clients.end()) { - std::shared_ptr c = (*iter)->GetConnection(); - if (c->CheckState(CLOSED)) { - LogInfo("Client disconnected from the server, removing client"); - delete (*iter); - iter = clients.erase(iter); - } - else { - ++iter; - } - } + m_clients.erase( + std::remove_if( + m_clients.begin(), m_clients.end(), + [](Client *c) { + if (c->GetConnection()->CheckState(CLOSED)) { + LogInfo("Client disconnected from the server, removing client"); + delete c; + return true; + } + return false; + } + ), + m_clients.end()); } -/** - * @param account_id - * @param loginserver - */ void ClientManager::RemoveExistingClient(unsigned int account_id, const std::string &loginserver) { - auto iter = clients.begin(); - while (iter != clients.end()) { - if ((*iter)->GetAccountID() == account_id && (*iter)->GetLoginServerName().compare(loginserver) == 0) { - LogInfo("Client attempting to log in existing client already logged in, removing existing client"); - delete (*iter); - iter = clients.erase(iter); - } - else { - ++iter; - } - } + m_clients.erase( + std::remove_if( + m_clients.begin(), m_clients.end(), + [&](Client *c) { + if (c->GetAccountID() == account_id && c->GetLoginServerName() == loginserver) { + LogInfo("Client attempting to log in existing client already logged in, removing existing client"); + delete c; + return true; + } + return false; + } + ), + m_clients.end()); } -/** - * @param account_id - * @param loginserver - * @return - */ Client *ClientManager::GetClient(unsigned int account_id, const std::string &loginserver) { - auto iter = clients.begin(); - while (iter != clients.end()) { - if ((*iter)->GetAccountID() == account_id && (*iter)->GetLoginServerName().compare(loginserver) == 0) { - return (*iter); + auto iter = std::find_if( + m_clients.begin(), m_clients.end(), + [&](Client *c) { + return c->GetAccountID() == account_id && c->GetLoginServerName() == loginserver; } - ++iter; - } + ); - return nullptr; + return (iter != m_clients.end()) ? *iter : nullptr; } diff --git a/loginserver/client_manager.h b/loginserver/client_manager.h index 23aff232e..17c87b43d 100644 --- a/loginserver/client_manager.h +++ b/loginserver/client_manager.h @@ -7,56 +7,23 @@ #include "client.h" #include -/** -* Client manager class, holds all the client objects and does basic processing. -*/ class ClientManager { public: - /** - * Constructor: sets up the stream factories and opcode managers - */ ClientManager(); - - /** - * Destructor: shuts down the streams and opcode managers - */ ~ClientManager(); - - /** - * Processes every client in the internal list, removes them if necessary. - */ void Process(); - - /** - * Removes a client with a certain account id - * - * @param account_id - * @param loginserver - */ - void RemoveExistingClient(unsigned int account_id, const std::string &loginserver); - - /** - * Gets a client (if exists) by their account id - * - * @param account_id - * @param loginserver - * @return - */ - Client *GetClient(unsigned int account_id, const std::string &loginserver); + void RemoveExistingClient(unsigned int c, const std::string &loginserver); + Client *GetClient(unsigned int c, const std::string &loginserver); private: - - /** - * Processes disconnected clients, removes them if necessary - */ void ProcessDisconnect(); - std::list clients; - OpcodeManager *titanium_ops; - EQ::Net::EQStreamManager *titanium_stream; - OpcodeManager *sod_ops; - EQ::Net::EQStreamManager *sod_stream; - OpcodeManager *larion_ops; - EQ::Net::EQStreamManager* larion_stream; + std::list m_clients; + OpcodeManager *m_titanium_ops; + EQ::Net::EQStreamManager *m_titanium_stream; + OpcodeManager *m_sod_ops; + EQ::Net::EQStreamManager *m_sod_stream; + OpcodeManager *m_larion_ops; + EQ::Net::EQStreamManager *m_larion_stream; }; #endif diff --git a/loginserver/database.cpp b/loginserver/database.cpp deleted file mode 100644 index 7c5e6b382..000000000 --- a/loginserver/database.cpp +++ /dev/null @@ -1,680 +0,0 @@ -#include "../common/global_define.h" - -#include "database.h" -#include "login_server.h" -#include "../common/eqemu_logsys.h" -#include "../common/strings.h" -#include "../common/util/uuid.h" - -extern LoginServer server; - -/** - * Initial connect - * - * @param user - * @param pass - * @param host - * @param port - * @param name - */ -Database::Database( - std::string user, - std::string pass, - std::string host, - std::string port, - std::string name -) -{ - uint32 errnum = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - if (!Open( - host.c_str(), - user.c_str(), - pass.c_str(), - name.c_str(), - Strings::ToUnsignedInt(port), - &errnum, - errbuf - ) - ) { - LogError("Failed to connect to database: Error: [{0}]", errbuf); - exit(1); - } - else { - LogInfo("Using database [{0}] at [{1}:{2}]", name, host, port); - } -} - -/** - * Deconstructor - */ -Database::~Database() -{ - if (m_database) { - mysql_close(m_database); - } -} - -/** - * @param name - * @param loginserver - * @param password - * @param id - * @return - */ -bool Database::GetLoginDataFromAccountInfo( - const std::string &name, - const std::string &loginserver, - std::string &password, - unsigned int &id -) -{ - auto query = fmt::format( - "SELECT id, account_password FROM login_accounts WHERE account_name = '{0}' AND source_loginserver = '{1}' LIMIT 1", - Strings::Escape(name), - Strings::Escape(loginserver) - ); - - auto results = QueryDatabase(query); - - if (results.RowCount() != 1) { - LogDebug( - "Could not find account for name [{0}] login [{1}]", - name, - loginserver - ); - - return false; - } - - if (!results.Success()) { - return false; - } - - auto row = results.begin(); - - id = Strings::ToUnsignedInt(row[0]); - password = row[1]; - - LogDebug( - "Found account for name [{0}] login [{1}]", - name, - loginserver - ); - - return true; -} - -/** - * @param token - * @param ip - * @param db_account_id - * @param db_loginserver - * @param user - * @return - */ -bool Database::GetLoginTokenDataFromToken( - const std::string &token, - const std::string &ip, - unsigned int &db_account_id, - std::string &db_loginserver, - std::string &user -) -{ - auto query = fmt::format("SELECT login_server, username, account_id FROM login_tickets WHERE expires > NOW()" - " AND id='{0}' AND ip_address='{1}' LIMIT 1", - Strings::Escape(token), - Strings::Escape(ip)); - - auto results = QueryDatabase(query); - if (results.RowCount() == 0 || !results.Success()) { - return false; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - db_loginserver = row[0]; - user = row[1]; - db_account_id = Strings::ToUnsignedInt(row[2]); - - return true; - } - - return false; -} - -/** - * @param loginserver - * @return - */ -unsigned int Database::GetFreeID(const std::string &loginserver) -{ - auto query = fmt::format( - "SELECT IFNULL(MAX(id), 0) + 1 FROM login_accounts WHERE source_loginserver = '{0}'", - Strings::Escape(loginserver) - ); - - auto results = QueryDatabase(query); - if (!results.Success() || results.RowCount() != 1) { - return 0; - } - - auto row = results.begin(); - - return Strings::ToUnsignedInt(row[0]); -} - -/** - * @param name - * @param password - * @param loginserver - * @param id - * @return - */ -bool Database::CreateLoginData( - const std::string &name, - const std::string &password, - const std::string &loginserver, - unsigned int &id -) -{ - uint32 free_id = GetFreeID(loginserver); - id = free_id; - - return CreateLoginDataWithID(name, password, loginserver, free_id); -} - -/** - * @param name - * @param password - * @param loginserver - * @param email - * @return - */ -uint32 Database::CreateLoginAccount( - const std::string &name, - const std::string &password, - const std::string &loginserver, - const std::string &email -) -{ - uint32 free_id = GetFreeID(loginserver); - - if (free_id <= 0) { - return 0; - } - - auto query = fmt::format( - "INSERT INTO login_accounts (id, source_loginserver, account_name, account_password, account_email, last_login_date, last_ip_address, created_at) " - "VALUES ({0}, '{1}', '{2}', '{3}', '{4}', NOW(), '127.0.0.1', NOW())", - free_id, - Strings::Escape(loginserver), - Strings::Escape(name), - Strings::Escape(password), - Strings::Escape(email) - ); - - auto results = QueryDatabase(query); - - return (results.Success() ? free_id : 0); -} - -/** - * @param in_account_name - * @param in_account_password - * @param loginserver - * @param id - * @return - */ -bool Database::CreateLoginDataWithID( - const std::string &in_account_name, - const std::string &in_account_password, - const std::string &loginserver, - unsigned int id -) -{ - if (id == 0) { - return false; - } - - auto query = fmt::format( - "INSERT INTO login_accounts (id, source_loginserver, account_name, account_password, account_email, last_login_date, last_ip_address, created_at) " - "VALUES ({0}, '{1}', '{2}', '{3}', 'local_creation', NOW(), '127.0.0.1', NOW())", - id, - Strings::Escape(loginserver), - Strings::Escape(in_account_name), - Strings::Escape(in_account_password) - ); - - auto results = QueryDatabase(query); - - return results.Success(); -} - -/** - * @param name - * @param password - * @param loginserver - * @param id - * @return - */ -bool Database::DoesLoginServerAccountExist( - const std::string &name, - const std::string &password, - const std::string &loginserver, - unsigned int id -) -{ - if (id == 0) { - return false; - } - - auto query = fmt::format( - "SELECT account_name FROM login_accounts WHERE account_name = '{0}' AND source_loginserver = '{1}'", - Strings::Escape(name), - Strings::Escape(loginserver) - ); - - auto results = QueryDatabase(query); - if (!results.Success() || results.RowCount() != 1) { - return false; - } - - return true; -} - -/** - * @param name - * @param loginserver - * @param hash - */ -void Database::UpdateLoginserverAccountPasswordHash( - const std::string &name, - const std::string &loginserver, - const std::string &hash -) -{ - LogDebug( - "name [{0}] loginserver [{1}] hash [{2}]", - name, - loginserver, - hash - ); - - auto query = fmt::format( - "UPDATE login_accounts SET account_password = '{0}' WHERE account_name = '{1}' AND source_loginserver = '{2}'", - hash, - Strings::Escape(name), - Strings::Escape(loginserver) - ); - - QueryDatabase(query); -} - -/** - * @param short_name - * @param long_name - * @param login_world_server_admin_id - * @return - */ -Database::DbWorldRegistration Database::GetWorldRegistration( - const std::string &short_name, - const std::string &long_name, - uint32 login_world_server_admin_id -) -{ - auto query = fmt::format( - "SELECT\n" - " WSR.id,\n" - " WSR.tag_description,\n" - " WSR.is_server_trusted,\n" - " SLT.id,\n" - " SLT.description,\n" - " ifnull(WSR.login_server_admin_id, 0) AS login_server_admin_id\n" - "FROM\n" - " 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 = '{}' AND WSR.long_name = '{}' AND WSR.login_server_admin_id = {} LIMIT 1", - Strings::Escape(short_name), - Strings::Escape(long_name), - login_world_server_admin_id - ); - - Database::DbWorldRegistration r{}; - - auto results = QueryDatabase(query); - if (!results.Success() || results.RowCount() != 1) { - return r; - } - - auto row = results.begin(); - - r.loaded = true; - r.server_id = Strings::ToInt(row[0]); - r.server_description = row[1]; - r.server_list_type = Strings::ToInt(row[3]); - r.is_server_trusted = Strings::ToInt(row[2]) > 0; - r.server_list_description = row[4]; - r.server_admin_id = Strings::ToUnsignedInt(row[5]); - - if (r.server_admin_id <= 0) { - return r; - } - - auto world_registration_query = fmt::format( - "SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1", - r.server_admin_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(); - r.server_admin_account_name = world_registration_row[0]; - r.server_admin_account_password = world_registration_row[1]; - } - - return r; -} - -/** - * @param id - * @param ip_address - */ -void Database::UpdateLSAccountData(unsigned int id, std::string ip_address) -{ - auto query = fmt::format( - "UPDATE login_accounts SET last_ip_address = '{0}', last_login_date = NOW() where id = {1}", - ip_address, - id - ); - - QueryDatabase(query); -} - -/** - * @param id - * @param name - * @param password - * @param email - */ -void Database::UpdateLSAccountInfo( - unsigned int id, - std::string name, - std::string password, - std::string email -) -{ - auto query = fmt::format( - "REPLACE login_accounts SET id = {0}, account_name = '{1}', account_password = sha('{2}'), " - "account_email = '{3}', last_ip_address = '0.0.0.0', last_login_date = now()", - id, - Strings::Escape(name), - Strings::Escape(password), - Strings::Escape(email) - ); - - QueryDatabase(query); -} - -/** - * @param id - * @param long_name - * @param ip_address - */ -void Database::UpdateWorldRegistration(unsigned int id, std::string long_name, std::string ip_address) -{ - auto query = fmt::format( - "UPDATE login_world_servers SET last_login_date = NOW(), last_ip_address = '{0}', long_name = '{1}' WHERE id = {2}", - ip_address, - Strings::Escape(long_name), - id - ); - - QueryDatabase(query); -} - -/** - * @param id - * @param admin_account_password_hash - */ -bool Database::UpdateLoginWorldAdminAccountPassword( - unsigned int id, - const std::string &admin_account_password_hash -) -{ - auto results = QueryDatabase( - fmt::format( - "UPDATE login_server_admins SET account_password = '{}' WHERE id = {}", - Strings::Escape(admin_account_password_hash), - id - ) - ); - - return results.Success(); -} - -/** - * - * @param admin_account_username - * @param admin_account_password_hash - */ -bool Database::UpdateLoginWorldAdminAccountPasswordByUsername( - const std::string &admin_account_username, - const std::string &admin_account_password_hash -) -{ - auto results = QueryDatabase( - fmt::format( - "UPDATE login_server_admins SET account_password = '{}' WHERE account_name = '{}'", - Strings::Escape(admin_account_password_hash), - Strings::Escape(admin_account_username) - ) - ); - - return results.Success(); -} - -/** - * @param server_long_name - * @param server_short_name - * @param id - * @return - */ -bool Database::CreateWorldRegistration( - std::string server_long_name, - std::string server_short_name, - std::string server_remote_ip, - unsigned int &id, - unsigned int &server_admin_id -) -{ - auto results = QueryDatabase("SELECT IFNULL(max(id), 0) + 1 FROM login_world_servers"); - if (!results.Success() || results.RowCount() != 1) { - return false; - } - - auto row = results.begin(); - - id = Strings::ToUnsignedInt(row[0]); - auto insert_query = fmt::format( - "INSERT INTO login_world_servers SET id = {0}, long_name = '{1}', short_name = '{2}', last_ip_address = '{3}', \n" - "login_server_list_type_id = 3, login_server_admin_id = {4}, is_server_trusted = 0, tag_description = ''", - id, - Strings::Escape(server_long_name), - Strings::Escape(server_short_name), - server_remote_ip, - server_admin_id - ); - - auto insert_results = QueryDatabase(insert_query); - if (!insert_results.Success()) { - LogError( - "Failed to register world server {0} - {1}", - server_long_name, - server_short_name - ); - - return false; - } - - return true; -} - -/** - * @param long_name - * @param short_name - * @param id - * @return - */ -std::string Database::CreateLoginserverApiToken( - bool write_mode, - bool read_mode -) -{ - std::string token = EQ::Util::UUID::Generate().ToString(); - auto query = fmt::format( - "INSERT INTO login_api_tokens (token, can_write, can_read, created_at) VALUES ('{0}', {1}, {2}, NOW())", - token, - (write_mode ? "1" : "0"), - (read_mode ? "1" : "0") - ); - - auto results = QueryDatabase(query); - if (!results.Success()) { - return ""; - } - - return token; -} - -/** - * @param long_name - * @param short_name - * @param id - * @return - */ -MySQLRequestResult Database::GetLoginserverApiTokens() -{ - return QueryDatabase("SELECT token, can_write, can_read FROM login_api_tokens"); -} - -/** - * @param account_name - * @param account_password - * @param first_name - * @param last_name - * @param email - * @param ip_address - * @return - */ -uint32 Database::CreateLoginserverWorldAdminAccount( - const std::string &account_name, - const std::string &account_password, - const std::string &first_name, - const std::string &last_name, - const std::string &email, - const std::string &ip_address -) -{ - auto query = fmt::format( - "INSERT INTO login_server_admins (account_name, account_password, first_name, last_name, email, registration_date, " - "registration_ip_address) " - "VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', NOW(), '{5}')", - Strings::Escape(account_name), - Strings::Escape(account_password), - Strings::Escape(first_name), - Strings::Escape(last_name), - Strings::Escape(email), - ip_address - ); - - auto results = QueryDatabase(query); - - return (results.Success() ? results.LastInsertedID() : 0); -} - -/** - * @param account_name - * @return - */ -bool Database::DoesLoginserverWorldAdminAccountExist( - const std::string &account_name -) -{ - auto query = fmt::format( - "SELECT account_name FROM login_server_admins WHERE account_name = '{0}' LIMIT 1", - Strings::Escape(account_name) - ); - - auto results = QueryDatabase(query); - - return (results.RowCount() == 1); -} - -/** - * @param account_name - * @return - */ -Database::DbLoginServerAdmin Database::GetLoginServerAdmin(const std::string &account_name) -{ - auto query = fmt::format( - "SELECT id, account_name, account_password, first_name, last_name, email, registration_date, registration_ip_address" - " FROM login_server_admins WHERE account_name = '{0}' LIMIT 1", - Strings::Escape(account_name) - ); - - auto results = QueryDatabase(query); - - Database::DbLoginServerAdmin r{}; - if (results.RowCount() == 1) { - auto row = results.begin(); - r.loaded = true; - r.id = Strings::ToUnsignedInt(row[0]); - r.account_name = row[1]; - r.account_password = row[2]; - r.first_name = row[3]; - r.last_name = row[4]; - r.email = row[5]; - r.registration_date = row[7]; - r.registration_ip_address = row[8]; - } - - return r; -} - -/** - * @param account_name - * @return - */ -Database::DbLoginServerAccount Database::GetLoginServerAccountByAccountName( - const std::string &account_name, - const std::string &source_loginserver -) -{ - auto query = fmt::format( - "SELECT id, account_name, account_password, account_email, source_loginserver, last_ip_address, last_login_date, " - "created_at, updated_at" - " FROM login_accounts WHERE account_name = '{0}' and source_loginserver = '{1}' LIMIT 1", - Strings::Escape(account_name), - Strings::Escape(source_loginserver) - ); - - auto results = QueryDatabase(query); - - Database::DbLoginServerAccount r{}; - if (results.RowCount() == 1) { - auto row = results.begin(); - r.loaded = true; - r.id = Strings::ToUnsignedInt(row[0]); - r.account_name = row[1]; - r.account_password = row[2]; - r.account_email = row[3]; - r.source_loginserver = row[4]; - r.last_ip_address = row[5]; - r.last_login_date = row[6]; - r.created_at = row[7]; - r.updated_at = row[8]; - } - - return r; -} diff --git a/loginserver/database.h b/loginserver/database.h deleted file mode 100644 index f194934a9..000000000 --- a/loginserver/database.h +++ /dev/null @@ -1,286 +0,0 @@ -#ifndef EQEMU_DATABASEMYSQL_H -#define EQEMU_DATABASEMYSQL_H - -#include "../common/dbcore.h" -#include "../common/eqemu_logsys.h" - -#include -#include -#include -#include - -class Database : public DBcore { -public: - - Database() { m_database = nullptr; } - - /** - * Constructor, tries to set our database to connect to the supplied options. - * - * @param user - * @param pass - * @param host - * @param port - * @param name - */ - Database(std::string user, std::string pass, std::string host, std::string port, std::string name); - - /** - * Destructor, frees our database if needed. - */ - ~Database(); - - /** - * Retrieves the login data (password hash and account id) from the account name provided needed for client login procedure. - * @param name - * @param loginserver - * @param password - * @param id - * @return - */ - bool GetLoginDataFromAccountInfo( - const std::string &name, - const std::string &loginserver, - std::string &password, - unsigned int &id - ); - - /** - * @param token - * @param ip - * @param db_account_id - * @param db_loginserver - * @param user - * @return - */ - bool GetLoginTokenDataFromToken( - const std::string &token, - const std::string &ip, - unsigned int &db_account_id, - std::string &db_loginserver, - std::string &user - ); - - /** - * @param loginserver - * @return - */ - unsigned int GetFreeID(const std::string &loginserver); - - /** - * @param name - * @param password - * @param loginserver - * @param id - * @return - */ - bool CreateLoginData( - const std::string &name, - const std::string &password, - const std::string &loginserver, - unsigned int &id - ); - - /** - * @param in_account_name - * @param in_account_password - * @param loginserver - * @param id - * @return - */ - bool CreateLoginDataWithID( - const std::string &in_account_name, - const std::string &in_account_password, - const std::string &loginserver, - unsigned int id - ); - - /** - * @param name - * @param loginserver - * @param hash - */ - void UpdateLoginserverAccountPasswordHash( - const std::string &name, - const std::string &loginserver, - const std::string &hash); - - /** - * @param name - * @param password - * @param loginserver - * @param id - * @return - */ - bool DoesLoginServerAccountExist( - const std::string &name, - const std::string &password, - const std::string &loginserver, - 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; - uint32 server_admin_id; - }; - - /** - * 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 - * - * @param short_name - * @param long_name - * @param login_world_server_admin_id - * @return - */ - Database::DbWorldRegistration GetWorldRegistration( - const std::string &short_name, - const std::string &long_name, - uint32 login_world_server_admin_id - ); - - /** - * @param id - * @param ip_address - */ - void UpdateLSAccountData(unsigned int id, std::string ip_address); - - /** - * @param id - * @param name - * @param password - * @param email - */ - void UpdateLSAccountInfo(unsigned int id, std::string name, std::string password, std::string email); - - /** - * @param id - * @param long_name - * @param ip_address - */ - void UpdateWorldRegistration(unsigned int id, std::string long_name, std::string ip_address); - - /** - * @param server_long_name - * @param server_short_name - * @param id - * @return - */ - bool CreateWorldRegistration( - std::string server_long_name, - std::string server_short_name, - std::string server_remote_ip, - unsigned int &id, - unsigned int &server_admin_id - ); - - /** - * @param write_mode - * @param read_mode - * @return - */ - std::string CreateLoginserverApiToken(bool write_mode, bool read_mode); - MySQLRequestResult GetLoginserverApiTokens(); - - /** - * @param account_name - * @param account_password - * @param first_name - * @param last_name - * @param email - * @param ip_address - * @return - */ - uint32 CreateLoginserverWorldAdminAccount( - const std::string &account_name, - const std::string &account_password, - const std::string &first_name, - const std::string &last_name, - const std::string &email, - const std::string &ip_address - ); - - /** - * @param account_name - * @return - */ - bool DoesLoginserverWorldAdminAccountExist(const std::string &account_name); - - struct DbLoginServerAdmin { - bool loaded = false; - uint32 id; - std::string account_name; - std::string account_password; - std::string first_name; - std::string last_name; - std::string email; - std::string registration_date; - std::string registration_ip_address; - }; - - Database::DbLoginServerAdmin GetLoginServerAdmin(const std::string &account_name); - - struct DbLoginServerAccount { - bool loaded = false; - uint32 id; - std::string account_name; - std::string account_password; - std::string account_email; - std::string source_loginserver; - std::string last_login_date; - std::string last_ip_address; - std::string created_at; - std::string updated_at; - }; - - Database::DbLoginServerAccount GetLoginServerAccountByAccountName( - const std::string &account_name, - const std::string &source_loginserver = "local" - ); - - /** - * @param id - * @param admin_account_password_hash - */ - bool UpdateLoginWorldAdminAccountPassword( - unsigned int id, - const std::string& admin_account_password_hash - ); - - /** - * @param admin_account_username - * @param admin_account_password_hash - */ - bool UpdateLoginWorldAdminAccountPasswordByUsername( - const std::string &admin_account_username, - const std::string &admin_account_password_hash - ); - - /** - * @param name - * @param password - * @param loginserver - * @param email - * @return - */ - uint32 CreateLoginAccount( - const std::string &name, - const std::string &password, - const std::string &loginserver = "local", - const std::string &email = "local_creation" - ); - -protected: - MYSQL *m_database{}; -}; - -#endif - diff --git a/loginserver/encryption.cpp b/loginserver/encryption.cpp index 7080f1996..5c15f46d4 100644 --- a/loginserver/encryption.cpp +++ b/loginserver/encryption.cpp @@ -21,10 +21,6 @@ #endif -/** - * @param mode - * @return - */ std::string GetEncryptionByModeId(uint32 mode) { switch (mode) { @@ -61,13 +57,6 @@ std::string GetEncryptionByModeId(uint32 mode) } } -/** - * @param buffer_in - * @param buffer_in_sz - * @param buffer_out - * @param enc - * @return - */ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc) { #ifdef EQEMU_USE_MBEDTLS @@ -113,7 +102,7 @@ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buff unsigned char iv[8]; memset(&key, 0, MBEDTLS_DES_KEY_SIZE); memset(&iv, 0, 8); - + mbedtls_des_context context; mbedtls_des_setkey_dec(&context, key); mbedtls_des_crypt_cbc(&context, MBEDTLS_DES_DECRYPT, buffer_in_sz, iv, (const unsigned char*)buffer_in, (unsigned char*)buffer_out); @@ -123,23 +112,19 @@ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buff #ifdef EQEMU_USE_OPENSSL DES_key_schedule k; DES_cblock v; - + memset(&k, 0, sizeof(DES_key_schedule)); memset(&v, 0, sizeof(DES_cblock)); - + if (!enc && buffer_in_sz && buffer_in_sz % 8 != 0) { return nullptr; } - + DES_ncbc_encrypt((const unsigned char*)buffer_in, (unsigned char*)buffer_out, (long)buffer_in_sz, &k, &v, enc); #endif return buffer_out; } -/** - * @param msg - * @return - */ std::string eqcrypt_md5(const std::string &msg) { std::string ret; @@ -174,10 +159,6 @@ std::string eqcrypt_md5(const std::string &msg) return ret; } -/** - * @param msg - * @return - */ std::string eqcrypt_sha1(const std::string &msg) { std::string ret; @@ -212,10 +193,6 @@ std::string eqcrypt_sha1(const std::string &msg) return ret; } -/** - * @param msg - * @return - */ std::string eqcrypt_sha512(const std::string &msg) { std::string ret; @@ -252,10 +229,6 @@ std::string eqcrypt_sha512(const std::string &msg) #ifdef ENABLE_SECURITY -/** - * @param msg - * @return - */ std::string eqcrypt_argon2(const std::string &msg) { char buffer[crypto_pwhash_STRBYTES] = {0}; @@ -275,10 +248,6 @@ std::string eqcrypt_argon2(const std::string &msg) return ret; } -/** - * @param msg - * @return - */ std::string eqcrypt_scrypt(const std::string &msg) { char buffer[crypto_pwhash_scryptsalsa208sha256_STRBYTES] = {0}; @@ -300,12 +269,6 @@ std::string eqcrypt_scrypt(const std::string &msg) #endif -/** - * @param username - * @param password - * @param mode - * @return - */ std::string eqcrypt_hash(const std::string &username, const std::string &password, int mode) { switch (mode) { @@ -346,13 +309,6 @@ std::string eqcrypt_hash(const std::string &username, const std::string &passwor } } -/** - * @param username - * @param password - * @param pwhash - * @param mode - * @return - */ bool eqcrypt_verify_hash(const std::string &username, const std::string &password, const std::string &pwhash, int mode) { switch (mode) { diff --git a/loginserver/encryption.h b/loginserver/encryption.h index d8337c25b..43d556d53 100644 --- a/loginserver/encryption.h +++ b/loginserver/encryption.h @@ -2,6 +2,9 @@ #include #include "../common/types.h" +#include "login_types.h" +#include "../common/eqemu_logsys.h" +#include "../common/strings.h" enum EncryptionMode { EncryptionModeMD5 = 1, @@ -26,11 +29,34 @@ namespace CryptoHash { const int sha512_hash_length = 128; } -/** - * @param mode - * @return - */ std::string GetEncryptionByModeId(uint32 mode); const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc); std::string eqcrypt_hash(const std::string &username, const std::string &password, int mode); bool eqcrypt_verify_hash(const std::string &username, const std::string &password, const std::string &pwhash, int mode); + +struct EncryptionResult { + std::string password; + int mode = 0; + std::string mode_name; +}; + +static EncryptionResult EncryptPasswordFromContext(LoginAccountContext c, int mode = EncryptionModeSCrypt) +{ + if (mode == 0) { + LogError("Encryption mode not set!"); + return {}; + } + + EncryptionResult r; + r.password = eqcrypt_hash( + c.username, + c.password, + mode + ); + r.mode = mode; + r.mode_name = GetEncryptionByModeId(r.mode); + + LogInfo("Encrypted password for user [{}] using mode [{}] ({})", c.username, r.mode_name, r.mode); + + return r; +} diff --git a/loginserver/login_server.h b/loginserver/login_server.h index b80cf6ee5..5104a48e0 100644 --- a/loginserver/login_server.h +++ b/loginserver/login_server.h @@ -3,29 +3,24 @@ #include #include "../common/json_config.h" -#include "database.h" #include "encryption.h" #include "options.h" -#include "server_manager.h" +#include "world_server_manager.h" #include "client_manager.h" #include "loginserver_webserver.h" -/** - * Login server struct, Contains every variable for the server that needs to exist outside the scope of main() - */ -struct LoginServer -{ +struct LoginServer { public: - LoginServer() : db(nullptr), server_manager(nullptr) { + LoginServer() : server_manager(nullptr) + { } EQ::JsonConfigFile config; - Database *db; LoginserverWebserver::TokenManager *token_manager{}; Options options; - ServerManager *server_manager; + WorldServerManager *server_manager; ClientManager *client_manager{}; }; diff --git a/loginserver/login_types.h b/loginserver/login_types.h index 1c8054a37..127514696 100644 --- a/loginserver/login_types.h +++ b/loginserver/login_types.h @@ -4,30 +4,30 @@ #pragma pack(1) // unencrypted base message header in all packets -struct LoginBaseMessage_Struct { +struct LoginBaseMessage { int32_t sequence; // request type/login sequence (2: handshake, 3: login, 4: serverlist, ...) bool compressed; // true: deflated int8_t encrypt_type; // 1: invert (unused) 2: des (2 for encrypted player logins and order expansions) (client uses what it sent, ignores in reply) int32_t unk3; // unused? }; -struct LoginBaseReplyMessage_Struct { +struct LoginBaseReplyMessage { bool success; // 0: failure (shows error string) 1: success int32_t error_str_id; // last error eqlsstr id, default: 101 (no error) char str[1]; // variable length, unknown (may be unused, this struct is a common pattern elsewhere) }; -struct LoginHandShakeReply_Struct { - LoginBaseMessage_Struct base_header; - LoginBaseReplyMessage_Struct base_reply; - char unknown[1]; // variable length string +struct LoginHandShakeReply { + LoginBaseMessage base_header; + LoginBaseReplyMessage base_reply; + char unknown[1]; // variable length string }; // variable length, can use directly if not serializing strings -struct PlayerLoginReply_Struct { +struct PlayerLoginReply { // base header excluded to make struct data easier to encrypt - //LoginBaseMessage_Struct base_header; - LoginBaseReplyMessage_Struct base_reply; + //LoginBaseMessage base_header; + LoginBaseReplyMessage base_reply; int8_t unk1; // (default: 0) int8_t unk2; // (default: 0) @@ -47,7 +47,7 @@ struct PlayerLoginReply_Struct { }; // variable length, for reference -struct LoginClientServerData_Struct { +struct LoginClientServerData { char ip[1]; int32_t server_type; // legends, preferred, standard int32_t server_id; @@ -59,24 +59,24 @@ struct LoginClientServerData_Struct { }; // variable length, for reference -struct ServerListReply_Struct { - LoginBaseMessage_Struct base_header; - LoginBaseReplyMessage_Struct base_reply; +struct ServerListReply { + LoginBaseMessage base_header; + LoginBaseReplyMessage base_reply; - int32_t server_count; - LoginClientServerData_Struct servers[0]; + int32_t server_count; + LoginClientServerData servers[0]; }; -struct PlayEverquestRequest_Struct { - LoginBaseMessage_Struct base_header; - uint32 server_number; +struct PlayEverquestRequest { + LoginBaseMessage base_header; + uint32 server_number; }; // SCJoinServerReply -struct PlayEverquestResponse_Struct { - LoginBaseMessage_Struct base_header; - LoginBaseReplyMessage_Struct base_reply; - uint32 server_number; +struct PlayEverquestResponse { + LoginBaseMessage base_header; + LoginBaseReplyMessage base_reply; + uint32 server_number; }; #pragma pack() @@ -95,6 +95,34 @@ enum LSClientStatus { cs_logged_in }; +struct LoginWorldContext { + std::string long_name; + std::string short_name; + std::string password; + std::string password_hash; + int64 admin_id = 0; +}; + +struct LoginWorldAdminAccountContext { + int64 id; + std::string username; + std::string password; + std::string password_hash; + std::string email; + std::string first_name; + std::string last_name; + std::string ip_address; +}; + +struct LoginAccountContext { + std::string username; + std::string password; + std::string email; + std::string source_loginserver = "local"; + uint32 login_account_id = 0; + bool password_is_encrypted = false; +}; + namespace LS { namespace ServerStatusFlags { enum eServerStatusFlags { @@ -123,13 +151,13 @@ namespace LS { }; namespace ErrStr { - constexpr static int ERROR_NONE = 101; // No Error - constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred - constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again. - constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later. - constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information. - constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information. - constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later. + constexpr static int ERROR_NONE = 101; // No Error + constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred + constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again. + constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later. + constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information. + constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information. + constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later. }; } diff --git a/loginserver/login_util/login_schema.sql b/loginserver/login_util/login_schema.sql index ad9805cf8..f491c4af1 100644 --- a/loginserver/login_util/login_schema.sql +++ b/loginserver/login_util/login_schema.sql @@ -33,10 +33,6 @@ CREATE TABLE `login_server_list_types` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -INSERT INTO `login_server_list_types` (`id`, `description`) VALUES ('1', 'Legends'), -('2', 'Preferred'), -('3', 'Standard'); - DROP TABLE IF EXISTS `login_world_servers`; CREATE TABLE `login_world_servers` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp index 152266902..a445bcb4e 100644 --- a/loginserver/loginserver_command_handler.cpp +++ b/loginserver/loginserver_command_handler.cpp @@ -5,15 +5,11 @@ #include "login_server.h" #include "loginserver_webserver.h" #include "account_management.h" +#include "../common/repositories/login_api_tokens_repository.h" extern LoginServer server; namespace LoginserverCommandHandler { - - /** - * @param argc - * @param argv - */ void CommandHandler(int argc, char **argv) { if (argc == 1) { return; } @@ -22,14 +18,7 @@ namespace LoginserverCommandHandler { cmd.parse(argc, argv, argh::parser::PREFER_PARAM_FOR_UNREG_OPTION); EQEmuCommand::DisplayDebug(cmd); - /** - * Declare command mapping - */ auto function_map = EQEmuCommand::function_map; - - /** - * Register commands - */ function_map["login-user:check-credentials"] = &LoginserverCommandHandler::CheckLoginserverUserCredentials; function_map["login-user:check-external-credentials"] = &LoginserverCommandHandler::CheckExternalLoginserverUserCredentials; function_map["login-user:create"] = &LoginserverCommandHandler::CreateLocalLoginserverAccount; @@ -43,12 +32,6 @@ namespace LoginserverCommandHandler { EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void CreateLoginserverApiToken(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Creates Loginserver API Token"; @@ -69,22 +52,22 @@ namespace LoginserverCommandHandler { bool can_write = cmd[{"-w", "--write"}]; if (!can_read || !can_write) { - LogInfo("[{0}] --read or --write must be set or both!", __func__); + LogInfo("--read or --write must be set or both!"); exit(1); } - std::string token = server.db->CreateLoginserverApiToken(can_write, can_read); - if (!token.empty()) { - LogInfo("[{0}] Created Loginserver API token [{1}]", __func__, token); + auto t = LoginApiTokensRepository::NewEntity(); + t.can_read = can_read; + t.can_write = can_write; + t.token = EQ::Util::UUID::Generate().ToString(); + t.created_at = std::time(nullptr); + + auto created = LoginApiTokensRepository::InsertOne(database, t); + if (created.id) { + LogInfo("Created Loginserver API token [{}] [{}]", created.id, created.token); } } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void ListLoginserverApiTokens(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Lists Loginserver API Tokens"; @@ -96,9 +79,10 @@ namespace LoginserverCommandHandler { server.token_manager = new LoginserverWebserver::TokenManager; server.token_manager->LoadApiTokens(); - for (auto &it : server.token_manager->loaded_api_tokens) { + for (auto &it: server.token_manager->loaded_api_tokens) { LogInfo( - "token [{0}] can_write [{1}] can_read [{2}]", + "token id [{}] [{}] can_write [{}] can_read [{}]", + it.second.id, it.second.token, it.second.can_write, it.second.can_read @@ -106,12 +90,6 @@ namespace LoginserverCommandHandler { } } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void CreateLocalLoginserverAccount(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Creates Local Loginserver Account"; @@ -130,19 +108,14 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - AccountManagement::CreateLoginServerAccount( - cmd(2).str(), - cmd(3).str(), - cmd("--email").str() - ); + LoginAccountContext c; + c.username = cmd(2).str(); + c.password = cmd(3).str(); + c.email = cmd("--email").str(); + + AccountManagement::CreateLoginServerAccount(c); } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void CreateLoginserverWorldAdminAccount(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Creates Loginserver World Administrator Account"; @@ -167,12 +140,6 @@ namespace LoginserverCommandHandler { ); } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void CheckLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Check user login credentials"; @@ -189,20 +156,15 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - auto res = AccountManagement::CheckLoginserverUserCredentials( - cmd(2).str(), - cmd(3).str() - ); + LoginAccountContext c; + c.username = cmd(2).str(); + c.password = cmd(3).str(); - LogInfo("Credentials were {0}", res != 0 ? "accepted" : "not accepted"); + auto res = AccountManagement::CheckLoginserverUserCredentials(c); + + LogInfo("Credentials were {}", res != 0 ? "accepted" : "not accepted"); } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Change user login credentials"; @@ -219,18 +181,12 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - AccountManagement::UpdateLoginserverUserCredentials( - cmd(2).str(), - cmd(3).str() - ); + LoginAccountContext c; + c.username = cmd(2).str(); + c.password = cmd(3).str(); + AccountManagement::UpdateLoginserverUserCredentials(c); } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Check user external login credentials"; @@ -247,20 +203,14 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - auto res = AccountManagement::CheckExternalLoginserverUserCredentials( - cmd(2).str(), - cmd(3).str() - ); + LoginAccountContext c; + c.username = cmd(2).str(); + c.password = cmd(3).str(); + auto res = AccountManagement::CheckExternalLoginserverUserCredentials(c); - LogInfo("Credentials were {0}", res ? "accepted" : "not accepted"); + LogInfo("Credentials were {}", res ? "accepted" : "not accepted"); } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Update world admin account password"; @@ -277,18 +227,12 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName( - cmd(2).str(), - cmd(3).str() - ); + LoginAccountContext c; + c.username = cmd(2).str(); + c.password = cmd(3).str(); + AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName(c); } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description) { description = "Checks login health using a test user"; diff --git a/loginserver/loginserver_webserver.cpp b/loginserver/loginserver_webserver.cpp index f7c353181..a880feb85 100644 --- a/loginserver/loginserver_webserver.cpp +++ b/loginserver/loginserver_webserver.cpp @@ -1,9 +1,10 @@ #include "loginserver_webserver.h" -#include "server_manager.h" +#include "world_server_manager.h" #include "login_server.h" #include "../common/json/json.h" #include "../common/strings.h" #include "account_management.h" +#include "../common/repositories/login_api_tokens_repository.h" extern LoginServer server; @@ -13,9 +14,6 @@ namespace LoginserverWebserver { constexpr static int HTTP_RESPONSE_BAD_REQUEST = 400; constexpr static int HTTP_RESPONSE_UNAUTHORIZED = 401; - /** - * @param api - */ void RegisterRoutes(httplib::Server &api) { server.token_manager = new LoginserverWebserver::TokenManager; @@ -27,21 +25,21 @@ namespace LoginserverWebserver { return; } - Json::Value response; - auto iter = server.server_manager->getWorldServers().begin(); - while (iter != server.server_manager->getWorldServers().end()) { + Json::Value response; + auto iter = server.server_manager->GetWorldServers().begin(); + for (const auto &s: server.server_manager->GetWorldServers()) { Json::Value row; - row["server_long_name"] = (*iter)->GetServerLongName(); - row["server_short_name"] = (*iter)->GetServerShortName(); - row["server_list_type_id"] = (*iter)->GetServerListID(); - row["server_status"] = (*iter)->GetStatus(); - row["zones_booted"] = (*iter)->GetZonesBooted(); - row["local_ip"] = (*iter)->GetLocalIP(); - row["remote_ip"] = (*iter)->GetRemoteIP(); - row["players_online"] = (*iter)->GetPlayersOnline(); - row["world_id"] = (*iter)->GetServerId(); + row["server_long_name"] = s->GetServerLongName(); + row["server_short_name"] = s->GetServerShortName(); + row["server_list_type_id"] = s->GetServerListID(); + row["server_status"] = s->GetStatus(); + row["zones_booted"] = s->GetZonesBooted(); + row["local_ip"] = s->GetLocalIP(); + row["remote_ip"] = s->GetRemoteIP(); + row["players_online"] = s->GetPlayersOnline(); + row["world_id"] = s->GetServerId(); + response.append(row); - ++iter; } LoginserverWebserver::SendResponse(response, res); @@ -54,10 +52,10 @@ namespace LoginserverWebserver { return; } - Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); - std::string username = request_body.get("username", "").asString(); - std::string password = request_body.get("password", "").asString(); - std::string email = request_body.get("email", "").asString(); + Json::Value req = LoginserverWebserver::ParseRequestBody(request); + std::string username = req.get("username", "").asString(); + std::string password = req.get("password", "").asString(); + std::string email = req.get("email", "").asString(); Json::Value response; if (username.empty() || password.empty()) { @@ -67,7 +65,12 @@ namespace LoginserverWebserver { return; } - int32 account_created_id = AccountManagement::CreateLoginServerAccount(username, password, email); + LoginAccountContext c; + c.username = username; + c.password = password; + c.email = email; + + int32 account_created_id = AccountManagement::CreateLoginServerAccount(c); if (account_created_id > 0) { response["message"] = "Account created successfully!"; response["data"]["account_id"] = account_created_id; @@ -91,11 +94,11 @@ namespace LoginserverWebserver { return; } - Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); - std::string username = request_body.get("username", "").asString(); - std::string password = request_body.get("password", "").asString(); - std::string email = request_body.get("email", "").asString(); - uint32 login_account_id = request_body.get("login_account_id", "").asInt(); + Json::Value req = LoginserverWebserver::ParseRequestBody(request); + std::string username = req.get("username", "").asString(); + std::string password = req.get("password", "").asString(); + std::string email = req.get("email", "").asString(); + uint32 login_account_id = req.get("login_account_id", "").asInt(); Json::Value response; if (username.empty() || password.empty()) { @@ -105,14 +108,14 @@ namespace LoginserverWebserver { return; } - std::string source_loginserver = "eqemu"; - int32 account_created_id = AccountManagement::CreateLoginServerAccount( - username, - password, - email, - source_loginserver, - login_account_id - ); + LoginAccountContext c; + c.username = username; + c.password = password; + c.email = email; + c.source_loginserver = "eqemu"; + c.login_account_id = login_account_id; + + int32 account_created_id = AccountManagement::CreateLoginServerAccount(c); if (account_created_id > 0) { response["message"] = "Account created successfully!"; @@ -137,9 +140,9 @@ namespace LoginserverWebserver { return; } - Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); - std::string username = request_body.get("username", "").asString(); - std::string password = request_body.get("password", "").asString(); + Json::Value req = LoginserverWebserver::ParseRequestBody(request); + std::string username = req.get("username", "").asString(); + std::string password = req.get("password", "").asString(); Json::Value response; if (username.empty() || password.empty()) { @@ -149,11 +152,11 @@ namespace LoginserverWebserver { return; } - uint32 login_account_id = AccountManagement::CheckLoginserverUserCredentials( - username, - password - ); + LoginAccountContext c; + c.username = username; + c.password = password; + uint32 login_account_id = AccountManagement::CheckLoginserverUserCredentials(c); if (login_account_id > 0) { response["message"] = "Credentials valid!"; response["data"]["account_id"] = login_account_id; @@ -173,9 +176,9 @@ namespace LoginserverWebserver { return; } - Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); - std::string username = request_body.get("username", "").asString(); - std::string password = request_body.get("password", "").asString(); + Json::Value req = LoginserverWebserver::ParseRequestBody(request); + std::string username = req.get("username", "").asString(); + std::string password = req.get("password", "").asString(); Json::Value response; if (username.empty() || password.empty()) { @@ -185,24 +188,20 @@ namespace LoginserverWebserver { return; } - Database::DbLoginServerAccount - login_server_account = server.db->GetLoginServerAccountByAccountName( - username - ); + LoginAccountContext c; + c.username = username; + c.password = password; - if (!login_server_account.loaded) { + auto a = LoginAccountsRepository::GetAccountFromContext(database, c); + if (!a.id) { res.status = HTTP_RESPONSE_BAD_REQUEST; response["error"] = "Failed to find associated loginserver account!"; LoginserverWebserver::SendResponse(response, res); return; } - bool credentials_valid = AccountManagement::UpdateLoginserverUserCredentials( - username, - password - ); - - if (credentials_valid) { + bool success = AccountManagement::UpdateLoginserverUserCredentials(c); + if (success) { response["message"] = "Loginserver account credentials updated!"; } else { @@ -214,16 +213,15 @@ namespace LoginserverWebserver { } ); - api.Post( "/v1/account/credentials/update/external", [](const httplib::Request &request, httplib::Response &res) { if (!LoginserverWebserver::TokenManager::AuthCanWrite(request, res)) { return; } - Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); - std::string username = request_body.get("username", "").asString(); - std::string password = request_body.get("password", "").asString(); + Json::Value req = LoginserverWebserver::ParseRequestBody(request); + std::string username = req.get("username", "").asString(); + std::string password = req.get("password", "").asString(); Json::Value response; if (username.empty() || password.empty()) { @@ -234,25 +232,20 @@ namespace LoginserverWebserver { std::string source_loginserver = "eqemu"; - Database::DbLoginServerAccount - login_server_account = server.db->GetLoginServerAccountByAccountName( - username, - source_loginserver - ); + LoginAccountContext c; + c.username = username; + c.password = password; + c.source_loginserver = source_loginserver; - if (!login_server_account.loaded) { + auto a = LoginAccountsRepository::GetAccountFromContext(database, c); + if (!a.id) { response["error"] = "Failed to find associated loginserver account!"; LoginserverWebserver::SendResponse(response, res); return; } - bool credentials_valid = AccountManagement::UpdateLoginserverUserCredentials( - username, - password, - source_loginserver - ); - - if (credentials_valid) { + bool success = AccountManagement::UpdateLoginserverUserCredentials(c); + if (success) { response["message"] = "Loginserver account credentials updated!"; } else { @@ -269,9 +262,9 @@ namespace LoginserverWebserver { return; } - Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); - std::string username = request_body.get("username", "").asString(); - std::string password = request_body.get("password", "").asString(); + Json::Value req = LoginserverWebserver::ParseRequestBody(request); + std::string username = req.get("username", "").asString(); + std::string password = req.get("password", "").asString(); Json::Value response; if (username.empty() || password.empty()) { @@ -280,10 +273,10 @@ namespace LoginserverWebserver { return; } - uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials( - username, - password - ); + LoginAccountContext c; + c.username = username; + c.password = password; + uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(c); if (account_id > 0) { response["message"] = "Credentials valid!"; @@ -301,7 +294,7 @@ namespace LoginserverWebserver { api.Get( "/probes/healthcheck", [](const httplib::Request &request, httplib::Response &res) { Json::Value response; - uint32 login_response = AccountManagement::HealthCheckUserLogin(); + uint32 login_response = AccountManagement::HealthCheckUserLogin(); response["status"] = login_response; if (login_response == 0) { @@ -317,10 +310,6 @@ namespace LoginserverWebserver { ); } - /** - * @param payload - * @param res - */ void SendResponse(const Json::Value &payload, httplib::Response &res) { if (res.get_header_value("response_set") == "true") { @@ -342,10 +331,6 @@ namespace LoginserverWebserver { res.set_content(response_payload.str(), "application/json"); } - /** - * @param payload - * @param res - */ Json::Value ParseRequestBody(const httplib::Request &request) { Json::Value request_body; @@ -364,13 +349,9 @@ namespace LoginserverWebserver { return request_body; } - /** - * @param request - * @param res - */ bool LoginserverWebserver::TokenManager::AuthCanRead(const httplib::Request &request, httplib::Response &res) { - LoginserverWebserver::TokenManager::token_data + LoginserverWebserver::TokenManager::Token user_token = LoginserverWebserver::TokenManager::CheckApiAuthorizationHeaders(request); if (!user_token.can_read) { @@ -384,7 +365,7 @@ namespace LoginserverWebserver { res.set_header("response_set", "true"); LogWarning( - "AuthCanRead access failure remote_address [{0}] user_agent [{1}]", + "AuthCanRead access failure remote_address [{}] user_agent [{}]", user_token.remote_address, user_token.user_agent ); @@ -395,13 +376,9 @@ namespace LoginserverWebserver { return true; } - /** - * @param request - * @param res - */ bool LoginserverWebserver::TokenManager::AuthCanWrite(const httplib::Request &request, httplib::Response &res) { - LoginserverWebserver::TokenManager::token_data + LoginserverWebserver::TokenManager::Token user_token = LoginserverWebserver::TokenManager::CheckApiAuthorizationHeaders(request); if (!user_token.can_write) { @@ -415,7 +392,7 @@ namespace LoginserverWebserver { res.set_header("response_set", "true"); LogWarning( - "AuthCanWrite access failure remote_address [{0}] user_agent [{1}]", + "AuthCanWrite access failure remote_address [{}] user_agent [{}]", user_token.remote_address, user_token.user_agent ); @@ -426,20 +403,16 @@ namespace LoginserverWebserver { return true; } - /** - * @param request - * @return - */ - LoginserverWebserver::TokenManager::token_data + LoginserverWebserver::TokenManager::Token LoginserverWebserver::TokenManager::CheckApiAuthorizationHeaders( const httplib::Request &request ) { std::string authorization_key; - LoginserverWebserver::TokenManager::token_data user_token{}; + LoginserverWebserver::TokenManager::Token user_token{}; - for (const auto &header : request.headers) { + for (const auto &header: request.headers) { auto header_key = header.first; auto header_value = header.second; if (header_key == "Authorization") { @@ -460,7 +433,7 @@ namespace LoginserverWebserver { } LogDebug( - "Authentication Request | remote_address [{0}] user_agent [{1}] authorization_key [{2}] request_path [{3}]", + "Authentication Request | remote_address [{}] user_agent [{}] authorization_key [{}] request_path [{}]", user_token.remote_address, user_token.user_agent, authorization_key, @@ -470,43 +443,24 @@ namespace LoginserverWebserver { return user_token; } - /** - * Loads API Tokens - */ void TokenManager::LoadApiTokens() { - auto results = server.db->GetLoginserverApiTokens(); - int token_count = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - LoginserverWebserver::TokenManager::token_data token_data; - token_data.token = row[0]; - token_data.can_write = Strings::ToInt(row[1]) > 0; - token_data.can_read = Strings::ToInt(row[2]) > 0; + int token_count = 0; - LogDebug( - "Inserting api token to internal list [{0}] write {1} read {2}", - token_data.token, - token_data.can_read, - token_data.can_write - ); - - server.token_manager->loaded_api_tokens.emplace( - std::make_pair( - token_data.token, - token_data - ) - ); + for (auto &t: LoginApiTokensRepository::GetWhere(database, "TRUE ORDER BY id ASC")) { + LoginserverWebserver::TokenManager::Token td; + td.id = t.id; + td.token = t.token; + td.can_write = t.can_write; + td.can_read = t.can_read; + server.token_manager->loaded_api_tokens.emplace(std::make_pair(td.token, td)); token_count++; } LogInfo("Loaded [{}] API token(s)", token_count); } - /** - * @param token - * @return - */ bool TokenManager::TokenExists(const std::string &token) { auto it = server.token_manager->loaded_api_tokens.find(token); @@ -514,11 +468,7 @@ namespace LoginserverWebserver { return !(it == server.token_manager->loaded_api_tokens.end()); } - /** - * @param token - * @return - */ - LoginserverWebserver::TokenManager::token_data TokenManager::GetToken( + LoginserverWebserver::TokenManager::Token TokenManager::GetToken( const std::string &token ) { diff --git a/loginserver/loginserver_webserver.h b/loginserver/loginserver_webserver.h index 7eb852892..9de510152 100644 --- a/loginserver/loginserver_webserver.h +++ b/loginserver/loginserver_webserver.h @@ -12,7 +12,8 @@ namespace LoginserverWebserver { public: TokenManager() = default; - struct token_data { + struct Token { + int id; std::string token; bool can_read; bool can_write; @@ -20,12 +21,12 @@ namespace LoginserverWebserver { std::string remote_address; }; - std::map loaded_api_tokens{}; + std::map loaded_api_tokens{}; void LoadApiTokens(); static bool TokenExists(const std::string &token); - token_data GetToken(const std::string &token); - static token_data CheckApiAuthorizationHeaders(const httplib::Request &request); + Token GetToken(const std::string &token); + static Token CheckApiAuthorizationHeaders(const httplib::Request &request); static bool AuthCanRead(const httplib::Request &request, httplib::Response &res); static bool AuthCanWrite(const httplib::Request &request, httplib::Response &res); }; diff --git a/loginserver/main.cpp b/loginserver/main.cpp index af02b878b..c541930ff 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -12,18 +12,23 @@ #include "loginserver_command_handler.h" #include "../common/strings.h" #include "../common/path_manager.h" +#include "../common/database.h" +#include "../common/events/player_event_logs.h" +#include "../common/zone_store.h" #include #include #include #include #include -LoginServer server; -EQEmuLogSys LogSys; -bool run_server = true; -PathManager path; +LoginServer server; +EQEmuLogSys LogSys; +bool run_server = true; +PathManager path; +Database database; +PlayerEventLogs player_event_logs; +ZoneStore zone_store; -void ResolveAddresses(); void CatchSignal(int sig_num) { } @@ -32,14 +37,16 @@ void LoadDatabaseConnection() { LogInfo("MySQL Database Init"); - server.db = new Database( + if (!database.Connect( + server.config.GetVariableString("database", "host", "localhost"), server.config.GetVariableString("database", "user", "root"), server.config.GetVariableString("database", "password", ""), - server.config.GetVariableString("database", "host", "localhost"), - server.config.GetVariableString("database", "port", "3306"), - server.config.GetVariableString("database", "db", "peq") - ); - + server.config.GetVariableString("database", "db", "peq"), + server.config.GetVariableInt("database", "port", 3306) + )) { + LogError("Cannot continue without a database connection"); + std::exit(1); + } } void LoadServerConfig() @@ -49,9 +56,6 @@ void LoadServerConfig() ); LogInfo("Config System Init"); - /** - * Worldservers - */ server.options.RejectDuplicateServers( server.config.GetVariableBool( "worldservers", @@ -82,41 +86,33 @@ void LoadServerConfig() ) ); - /** - * Expansion Display Settings - */ server.options.DisplayExpansions( server.config.GetVariableBool( "client_configuration", "display_expansions", false - )); //disable by default + ) + ); server.options.MaxExpansions( server.config.GetVariableInt( "client_configuration", "max_expansions_mask", 67108863 - )); //enable display of all expansions - - /** - * Account - */ - server.options.AutoCreateAccounts(server.config.GetVariableBool("account", "auto_create_accounts", true)); - server.options.AutoLinkAccounts(server.config.GetVariableBool("account", "auto_link_accounts", false)); - -#ifdef LSPX - server.options.EQEmuLoginServerAddress( - server.config.GetVariableString( - "general", - "eqemu_loginserver_address", - "login.eqemulator.net:5999" ) ); -#endif - /** - * Default Loginserver Name (Don't change) - */ + server.options.AutoCreateAccounts(server.config.GetVariableBool("account", "auto_create_accounts", true)); + + if (std::getenv("LSPX")) { + server.options.EQEmuLoginServerAddress( + server.config.GetVariableString( + "general", + "eqemu_loginserver_address", + "login.eqemulator.net:5999" + ) + ); + } + server.options.DefaultLoginServerName( server.config.GetVariableString( "general", @@ -125,10 +121,6 @@ void LoadServerConfig() ) ); - /** - * Security - */ - #ifdef ENABLE_SECURITY server.options.EncryptionMode(server.config.GetVariableInt("security", "mode", 13)); #else @@ -136,14 +128,6 @@ void LoadServerConfig() #endif server.options.AllowTokenLogin(server.config.GetVariableBool("security", "allow_token_login", false)); - server.options.AllowPasswordLogin(server.config.GetVariableBool("security", "allow_password_login", true)); - server.options.UpdateInsecurePasswords( - server.config.GetVariableBool( - "security", - "update_insecure_passwords", - true - ) - ); } void start_web_server() @@ -151,7 +135,7 @@ void start_web_server() Sleep(1); int web_api_port = server.config.GetVariableInt("web_api", "port", 6000); - LogInfo("Webserver API now listening on port [{0}]", web_api_port); + LogInfo("Webserver API now listening on port [{}]", web_api_port); httplib::Server api; @@ -180,9 +164,7 @@ int main(int argc, char **argv) path.LoadPaths(); - /** - * Command handler - */ + // command handler if (argc > 1) { LogSys.SilenceConsoleLogging(); @@ -197,43 +179,23 @@ int main(int argc, char **argv) } LoadServerConfig(); - - /** - * mysql connect - */ LoadDatabaseConnection(); if (argc == 1) { - LogSys.SetDatabase(server.db) + LogSys.SetDatabase(&database) ->SetLogPath("logs") ->LoadLogDatabaseSettings() ->StartFileLogs(); } - /** - * make sure our database got created okay, otherwise cleanup and exit - */ - if (!server.db) { - LogError("Database Initialization Failure"); - LogInfo("Log System Shutdown"); - return 1; - } - - /** - * create server manager - */ LogInfo("Server Manager Init"); - server.server_manager = new ServerManager(); + server.server_manager = new WorldServerManager(); if (!server.server_manager) { LogError("Server Manager Failed to Start"); LogInfo("Database System Shutdown"); - delete server.db; return 1; } - /** - * create client manager - */ LogInfo("Client Manager Init"); server.client_manager = new ClientManager(); if (!server.client_manager) { @@ -242,7 +204,6 @@ int main(int argc, char **argv) delete server.server_manager; LogInfo("Database System Shutdown"); - delete server.db; return 1; } @@ -256,37 +217,33 @@ int main(int argc, char **argv) LogInfo("Server Started"); - /** - * Web API - */ bool web_api_enabled = server.config.GetVariableBool("web_api", "enabled", true); if (web_api_enabled) { std::thread web_api_thread(start_web_server); web_api_thread.detach(); } - LogInfo("[Config] [Account] CanAutoCreateAccounts [{0}]", server.options.CanAutoCreateAccounts()); - LogInfo("[Config] [ClientConfiguration] DisplayExpansions [{0}]", server.options.IsDisplayExpansions()); - LogInfo("[Config] [ClientConfiguration] MaxExpansions [{0}]", server.options.GetMaxExpansions()); + LogInfo("[Config] [Account] CanAutoCreateAccounts [{}]", server.options.CanAutoCreateAccounts()); + LogInfo("[Config] [ClientConfiguration] DisplayExpansions [{}]", server.options.IsDisplayExpansions()); + LogInfo("[Config] [ClientConfiguration] MaxExpansions [{}]", server.options.GetMaxExpansions()); -#ifdef LSPX - LogInfo("[Config] [Account] CanAutoLinkAccounts [{0}]", server.options.CanAutoLinkAccounts()); -#endif - LogInfo("[Config] [WorldServer] IsRejectingDuplicateServers [{0}]", server.options.IsRejectingDuplicateServers()); - LogInfo("[Config] [WorldServer] IsUnregisteredAllowed [{0}]", server.options.IsUnregisteredAllowed()); - LogInfo("[Config] [WorldServer] ShowPlayerCount [{0}]", server.options.IsShowPlayerCountEnabled()); + if (std::getenv("LSPX")) { + LogInfo("[Config] [Account] LSPX [on]"); + } + + LogInfo("[Config] [WorldServer] IsRejectingDuplicateServers [{}]", server.options.IsRejectingDuplicateServers()); + LogInfo("[Config] [WorldServer] IsUnregisteredAllowed [{}]", server.options.IsUnregisteredAllowed()); + LogInfo("[Config] [WorldServer] ShowPlayerCount [{}]", server.options.IsShowPlayerCountEnabled()); LogInfo( - "[Config] [WorldServer] DevAndTestServersListBottom [{0}]", + "[Config] [WorldServer] DevAndTestServersListBottom [{}]", server.options.IsWorldDevTestServersListBottom() ); LogInfo( - "[Config] [WorldServer] SpecialCharactersStartListBottom [{0}]", + "[Config] [WorldServer] SpecialCharactersStartListBottom [{}]", server.options.IsWorldSpecialCharacterStartListBottom() ); - LogInfo("[Config] [Security] GetEncryptionMode [{0}]", server.options.GetEncryptionMode()); - LogInfo("[Config] [Security] IsTokenLoginAllowed [{0}]", server.options.IsTokenLoginAllowed()); - LogInfo("[Config] [Security] IsPasswordLoginAllowed [{0}]", server.options.IsPasswordLoginAllowed()); - LogInfo("[Config] [Security] IsUpdatingInsecurePasswords [{0}]", server.options.IsUpdatingInsecurePasswords()); + LogInfo("[Config] [Security] GetEncryptionMode [{}]", server.options.GetEncryptionMode()); + LogInfo("[Config] [Security] IsTokenLoginAllowed [{}]", server.options.IsTokenLoginAllowed()); Timer keepalive(INTERSERVER_TIMER); // does auto-reconnect @@ -295,7 +252,7 @@ int main(int argc, char **argv) if (keepalive.Check()) { keepalive.Start(); - server.db->ping(); + database.ping(); } if (!run_server) { @@ -319,8 +276,5 @@ int main(int argc, char **argv) LogInfo("Server Manager Shutdown"); delete server.server_manager; - LogInfo("Database System Shutdown"); - delete server.db; - return 0; } diff --git a/loginserver/options.h b/loginserver/options.h index 5d4546f81..31976f142 100644 --- a/loginserver/options.h +++ b/loginserver/options.h @@ -1,124 +1,55 @@ #ifndef EQEMU_OPTIONS_H #define EQEMU_OPTIONS_H -/** - * Collects options on one object, because having a bunch of global variables floating around is - * really ugly and just a little dangerous. - */ class Options { public: - - /** - * Constructor: Default options - */ Options() : - allow_unregistered(true), - display_expansions(false), - max_expansions_mask(0), - encryption_mode(5), - reject_duplicate_servers(false), - allow_password_login(true), - allow_token_login(false), - auto_create_accounts(false) {} + m_allow_unregistered(true), + m_display_expansions(false), + m_max_expansions_mask(0), + m_encryption_mode(14), + m_reject_duplicate_servers(false), + m_allow_token_login(false), + m_auto_create_accounts(false) {} - /** - * Sets allow_unregistered. - */ - inline void AllowUnregistered(bool b) { allow_unregistered = b; } - - /** - * Returns the value of expansion display settings. - */ - inline void DisplayExpansions(bool b) { display_expansions = b; } - inline void MaxExpansions(int i) { max_expansions_mask = i; } - inline bool IsDisplayExpansions() const { return display_expansions; } - inline int GetMaxExpansions() const { return max_expansions_mask; } - - /** - * Returns the value of allow_unregistered. - */ - inline bool IsUnregisteredAllowed() const { return allow_unregistered; } - - /** - * Sets encryption_mode. - */ - inline void EncryptionMode(int m) { encryption_mode = m; } - - /** - * Returns the value of encryption_mode. - */ - inline int GetEncryptionMode() const { return encryption_mode; } - - /** - * Sets whether we are rejecting duplicate servers or not. - */ - inline void RejectDuplicateServers(bool b) { reject_duplicate_servers = b; } - - /** - * Returns whether we are rejecting duplicate servers or not. - */ - inline bool IsRejectingDuplicateServers() { return reject_duplicate_servers; } - - inline void AllowTokenLogin(bool b) { allow_token_login = b; } - inline bool IsTokenLoginAllowed() const { return allow_token_login; } - - inline void AllowPasswordLogin(bool b) { allow_password_login = b; } - inline bool IsPasswordLoginAllowed() const { return allow_password_login; } - - inline void AutoCreateAccounts(bool b) { auto_create_accounts = b; } - inline bool CanAutoCreateAccounts() const { return auto_create_accounts; } - - inline void AutoLinkAccounts(bool b) { auto_link_accounts = b; } - inline bool CanAutoLinkAccounts() const { return auto_link_accounts; } - - inline void EQEmuLoginServerAddress(const std::string& v) { eqemu_loginserver_address = v; } - inline std::string GetEQEmuLoginServerAddress() const { return eqemu_loginserver_address; } - - inline void DefaultLoginServerName(const std::string& v) { default_loginserver_name = v; } - inline std::string GetDefaultLoginServerName() const { return default_loginserver_name; } - - inline void UpdateInsecurePasswords(bool b) { update_insecure_passwords = b; } - inline bool IsUpdatingInsecurePasswords() const { return update_insecure_passwords; } - - inline bool IsShowPlayerCountEnabled() const - { - return show_player_count; - } - inline void SetShowPlayerCount(bool show_player_count) - { - Options::show_player_count = show_player_count; - } - inline bool IsWorldDevTestServersListBottom() const { return world_dev_test_servers_list_bottom; } - inline void SetWorldDevTestServersListBottom(bool dev_test_servers_list_bottom) - { - Options::world_dev_test_servers_list_bottom = dev_test_servers_list_bottom; - } - - inline bool IsWorldSpecialCharacterStartListBottom() const - { - return world_special_character_start_list_bottom; - } - inline void SetWorldSpecialCharacterStartListBottom(bool world_special_character_start_list_bottom) - { - Options::world_special_character_start_list_bottom = world_special_character_start_list_bottom; - } + inline void AllowUnregistered(bool b) { m_allow_unregistered = b; } + inline void DisplayExpansions(bool b) { m_display_expansions = b; } + inline void MaxExpansions(int i) { m_max_expansions_mask = i; } + inline bool IsDisplayExpansions() const { return m_display_expansions; } + inline int GetMaxExpansions() const { return m_max_expansions_mask; } + inline bool IsUnregisteredAllowed() const { return m_allow_unregistered; } + inline void EncryptionMode(int m) { m_encryption_mode = m; } + inline int GetEncryptionMode() const { return m_encryption_mode; } + inline void RejectDuplicateServers(bool b) { m_reject_duplicate_servers = b; } + inline bool IsRejectingDuplicateServers() { return m_reject_duplicate_servers; } + inline void AllowTokenLogin(bool b) { m_allow_token_login = b; } + inline bool IsTokenLoginAllowed() const { return m_allow_token_login; } + inline void AutoCreateAccounts(bool b) { m_auto_create_accounts = b; } + inline bool CanAutoCreateAccounts() const { return m_auto_create_accounts; } + inline void EQEmuLoginServerAddress(const std::string &v) { m_eqemu_loginserver_address = v; } + inline std::string GetEQEmuLoginServerAddress() const { return m_eqemu_loginserver_address; } + inline void DefaultLoginServerName(const std::string &v) { m_default_loginserver_name = v; } + inline std::string GetDefaultLoginServerName() const { return m_default_loginserver_name; } + inline bool IsShowPlayerCountEnabled() const { return m_show_player_count; } + inline void SetShowPlayerCount(bool show_player_count) { show_player_count = show_player_count; } + inline bool IsWorldDevTestServersListBottom() const { return m_world_dev_list_bottom; } + inline void SetWorldDevTestServersListBottom(bool list_bottom) { m_world_dev_list_bottom = list_bottom; } + inline bool IsWorldSpecialCharacterStartListBottom() const { return m_special_char_list_bottom; } + inline void SetWorldSpecialCharacterStartListBottom(bool list_bottom) { m_special_char_list_bottom = list_bottom; } private: - bool allow_unregistered; - bool display_expansions; - bool reject_duplicate_servers; - bool world_dev_test_servers_list_bottom; - bool world_special_character_start_list_bottom; - bool allow_token_login; - bool allow_password_login; - bool show_player_count; - bool auto_create_accounts; - bool auto_link_accounts; - bool update_insecure_passwords; - int encryption_mode; - int max_expansions_mask; - std::string eqemu_loginserver_address; - std::string default_loginserver_name; + bool m_allow_unregistered; + bool m_display_expansions; + bool m_reject_duplicate_servers; + bool m_world_dev_list_bottom; + bool m_special_char_list_bottom; + bool m_allow_token_login; + bool m_show_player_count; + bool m_auto_create_accounts; + int m_encryption_mode; + int m_max_expansions_mask; + std::string m_eqemu_loginserver_address; + std::string m_default_loginserver_name; }; diff --git a/loginserver/server_manager.cpp b/loginserver/server_manager.cpp deleted file mode 100644 index f46a6c925..000000000 --- a/loginserver/server_manager.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include "server_manager.h" -#include "login_server.h" -#include "login_types.h" -#include - -#include "../common/eqemu_logsys.h" -#include "../common/ip_util.h" - -extern LoginServer server; -extern bool run_server; - -ServerManager::ServerManager() -{ - int listen_port = server.config.GetVariableInt("general", "listen_port", 5998); - - m_server_connection = std::make_unique(); - EQ::Net::ServertalkServerOptions opts; - opts.port = listen_port; - opts.ipv6 = false; - m_server_connection->Listen(opts); - - LogInfo("Loginserver now listening on port [{0}]", listen_port); - - m_server_connection->OnConnectionIdentified( - "World", [this](std::shared_ptr world_connection) { - LogInfo( - "New World Server connection from {0}:{1}", - world_connection->Handle()->RemoteIP(), - world_connection->Handle()->RemotePort() - ); - - auto iter = m_world_servers.begin(); - while (iter != m_world_servers.end()) { - if ((*iter)->GetConnection()->Handle()->RemoteIP().compare(world_connection->Handle()->RemoteIP()) == - 0 && - (*iter)->GetConnection()->Handle()->RemotePort() == world_connection->Handle()->RemotePort()) { - - LogInfo( - "World server already existed for {0}:{1}, removing existing connection.", - world_connection->Handle()->RemoteIP(), - world_connection->Handle()->RemotePort() - ); - - m_world_servers.erase(iter); - break; - } - - ++iter; - } - - m_world_servers.push_back(std::make_unique(world_connection)); - } - ); - - m_server_connection->OnConnectionRemoved( - "World", [this](std::shared_ptr c) { - auto iter = m_world_servers.begin(); - while (iter != m_world_servers.end()) { - if ((*iter)->GetConnection()->GetUUID() == c->GetUUID()) { - LogInfo( - "World server {0} has been disconnected, removing.", - (*iter)->GetServerLongName() - ); - m_world_servers.erase(iter); - return; - } - - ++iter; - } - } - ); -} - -ServerManager::~ServerManager() = default; - -/** - * @param client - * @param sequence - * @return - */ -std::unique_ptr ServerManager::CreateServerListPacket(Client *client, uint32 sequence) -{ - unsigned int server_count = 0; - in_addr in{}; - in.s_addr = client->GetConnection()->GetRemoteIP(); - std::string client_ip = inet_ntoa(in); - - LogDebug("ServerManager::CreateServerListPacket via client address [{0}]", client_ip); - - for (const auto& world_server : m_world_servers) - { - if (world_server->IsAuthorized()) { - ++server_count; - } - } - - SerializeBuffer buf; - - // LoginBaseMessage_Struct header - buf.WriteInt32(sequence); - buf.WriteInt8(0); - buf.WriteInt8(0); - buf.WriteInt32(0); - - // LoginBaseReplyMessage_Struct - buf.WriteInt8(true); // success (no error) - buf.WriteInt32(0x65); // 101 "No Error" eqlsstr - buf.WriteString(""); - - // ServerListReply_Struct - buf.WriteInt32(server_count); - - for (const auto& world_server : m_world_servers) - { - if (!world_server->IsAuthorized()) { - LogDebug( - "ServerManager::CreateServerListPacket | Server [{}] via IP [{}] is not authorized to be listed", - world_server->GetServerLongName(), - world_server->GetConnection()->Handle()->RemoteIP() - ); - continue; - } - - bool use_local_ip = false; - - std::string world_ip = world_server->GetConnection()->Handle()->RemoteIP(); - if (world_ip == client_ip || IpUtil::IsIpInPrivateRfc1918(client_ip)) { - use_local_ip = true; - } - - LogDebug( - "CreateServerListPacket | Building list entry | Client [{}] IP [{}] Server Long Name [{}] Server IP [{}] ({})", - client->GetAccountName(), - client_ip, - world_server->GetServerLongName(), - use_local_ip ? world_server->GetLocalIP() : world_server->GetRemoteIP(), - use_local_ip ? "Local" : "Remote" - ); - - world_server->SerializeForClientServerList(buf, use_local_ip, client->GetClientVersion()); - } - - return std::make_unique(OP_ServerListResponse, buf); -} - -/** - * @param server_id - * @param client_account_id - * @param client_loginserver - */ -void ServerManager::SendUserToWorldRequest( - unsigned int server_id, - unsigned int client_account_id, - const std::string &client_loginserver -) -{ - auto iter = m_world_servers.begin(); - bool found = false; - while (iter != m_world_servers.end()) { - if ((*iter)->GetServerId() == server_id) { - EQ::Net::DynamicPacket outapp; - outapp.Resize(sizeof(UsertoWorldRequest_Struct)); - - auto *r = (UsertoWorldRequest_Struct *) outapp.Data(); - r->worldid = server_id; - r->lsaccountid = client_account_id; - strncpy(r->login, &client_loginserver[0], 64); - (*iter)->GetConnection()->Send(ServerOP_UsertoWorldReq, outapp); - found = true; - - LogNetcode( - "[UsertoWorldRequest] [Size: {}]\n{}", - outapp.Length(), - outapp.ToString() - ); - } - ++iter; - } - - if (!found) { - LogError("Client requested a user to world but supplied an invalid id of {0}", server_id); - } -} - -/** - * @param server_long_name - * @param server_short_name - * @param ignore - * @return - */ -bool ServerManager::ServerExists( - std::string server_long_name, - std::string server_short_name, - WorldServer *ignore -) -{ - auto iter = m_world_servers.begin(); - while (iter != m_world_servers.end()) { - if ((*iter).get() == ignore) { - ++iter; - continue; - } - - if ((*iter)->GetServerLongName() == server_long_name && (*iter)->GetServerShortName() == server_short_name) { - return true; - } - - ++iter; - } - return false; -} - -/** - * @param server_long_name - * @param server_short_name - * @param ignore - */ -void ServerManager::DestroyServerByName( - std::string server_long_name, - std::string server_short_name, - WorldServer *ignore -) -{ - auto iter = m_world_servers.begin(); - while (iter != m_world_servers.end()) { - if ((*iter).get() == ignore) { - ++iter; - continue; - } - - if ((*iter)->GetServerLongName().compare(server_long_name) == 0 && - (*iter)->GetServerShortName().compare(server_short_name) == 0) { - (*iter)->GetConnection()->Handle()->Disconnect(); - iter = m_world_servers.erase(iter); - continue; - } - - ++iter; - } -} - -/** - * @return - */ -const std::list> &ServerManager::getWorldServers() const -{ - return m_world_servers; -} diff --git a/loginserver/server_manager.h b/loginserver/server_manager.h deleted file mode 100644 index 24711c6ac..000000000 --- a/loginserver/server_manager.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef EQEMU_SERVERMANAGER_H -#define EQEMU_SERVERMANAGER_H - -#include "../common/global_define.h" -#include "../common/servertalk.h" -#include "../common/packet_dump.h" -#include "../common/net/servertalk_server.h" -#include "world_server.h" -#include "client.h" -#include - -/** - * Server manager class, deals with management of the world servers - */ -class ServerManager { -public: - - /** - * Constructor, sets up the TCP server and starts listening - */ - ServerManager(); - - /** - * Destructor, shuts down the TCP server. - */ - ~ServerManager(); - - /** - * Sends a request to world to see if the client is banned or suspended - * - * @param server_id - * @param client_account_id - * @param client_loginserver - */ - void SendUserToWorldRequest( - unsigned int server_id, - unsigned int client_account_id, - const std::string &client_loginserver - ); - - /** - * Creates a server list packet for the client - * - * @param client - * @param sequence - * @return - */ - std::unique_ptr CreateServerListPacket(Client *client, uint32 sequence); - - /** - * Checks to see if there is a server exists with this name, ignoring option - * - * @param server_long_name - * @param server_short_name - * @param ignore - * @return - */ - bool ServerExists(std::string server_long_name, std::string server_short_name, WorldServer *ignore = nullptr); - - /** - * Destroys a server with this name, ignoring option - * - * @param server_long_name - * @param server_short_name - * @param ignore - */ - void DestroyServerByName(std::string server_long_name, std::string server_short_name, WorldServer *ignore = nullptr); - - /** - * @return - */ - const std::list> &getWorldServers() const; - -private: - std::unique_ptr m_server_connection; - std::list> m_world_servers; - -}; - -#endif - diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index e4b7ac358..7a82b24b9 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -3,24 +3,24 @@ #include "login_types.h" #include "../common/ip_util.h" #include "../common/strings.h" +#include "../common/repositories/login_world_servers_repository.h" +#include "../common/repositories/login_server_admins_repository.h" extern LoginServer server; +extern Database database; -/** - * @param worldserver_connection - */ WorldServer::WorldServer(std::shared_ptr worldserver_connection) { - m_connection = worldserver_connection; - m_zones_booted = 0; - m_players_online = 0; - m_server_status = 0; - m_server_id = 0; - m_server_list_type_id = 0; - m_server_process_type = 0; - m_is_server_authorized = false; - m_is_server_trusted = false; - m_is_server_logged_in = false; + m_connection = worldserver_connection; + m_zones_booted = 0; + m_players_online = 0; + m_server_status = 0; + m_server_id = 0; + m_server_list_type_id = 0; + m_server_process_type = 0; + m_is_server_authorized_to_list = false; + m_is_server_trusted = false; + m_is_server_logged_in = false; worldserver_connection->OnMessage( ServerOP_NewLSInfo, @@ -57,65 +57,53 @@ WorldServer::~WorldServer() = default; void WorldServer::Reset() { - m_server_id = 0; - m_zones_booted = 0; - m_players_online = 0; - m_server_status = 0; - m_server_list_type_id = 0; - m_server_process_type = 0; - m_is_server_authorized = false; - m_is_server_logged_in = false; + m_server_id = 0; + m_zones_booted = 0; + m_players_online = 0; + m_server_status = 0; + m_server_list_type_id = 0; + m_server_process_type = 0; + m_is_server_authorized_to_list = false; + m_is_server_logged_in = false; } -/** - * @param opcode - * @param packet - */ void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packet) { LogNetcode( "Application packet received from server [{:#04x}] [Size: {}]\n{}", - opcode, - packet.Length(), - packet.ToString() + opcode, packet.Length(), packet.ToString() ); - if (packet.Length() < sizeof(ServerNewLSInfo_Struct)) { + if (packet.Length() < sizeof(LoginserverNewWorldRequest)) { LogError( - "Received application packet from server that had opcode ServerOP_NewLSInfo, " - "but was too small. Discarded to avoid buffer overrun" + "Received application packet with opcode ServerOP_NewLSInfo, but it was too small. Discarded to avoid buffer overrun." ); - return; } - auto *info = (ServerNewLSInfo_Struct *) packet.Data(); + auto *r = (LoginserverNewWorldRequest *) packet.Data(); - // if for whatever reason the world server is not sending an address, use the local address it sends - std::string remote_ip_addr = info->remote_ip_address; - std::string local_ip_addr = info->local_ip_address; - if (remote_ip_addr.empty() && !local_ip_addr.empty() && local_ip_addr != "127.0.0.1") { - strcpy(info->remote_ip_address, local_ip_addr.c_str()); + // If remote IP is missing, use local IP unless it's 127.0.0.1 + if (r->remote_ip_address[0] == '\0' && r->local_ip_address[0] != '\0' && + strcmp(r->local_ip_address, "127.0.0.1") != 0) { + strncpy(r->remote_ip_address, r->local_ip_address, sizeof(r->remote_ip_address) - 1); + r->remote_ip_address[sizeof(r->remote_ip_address) - 1] = '\0'; // Ensure null termination } LogInfo( - "New World Server Info | name [{0}] shortname [{1}] remote_address [{2}] local_address [{3}] account [{4}] password [{5}] server_type [{6}]", - info->server_long_name, - info->server_short_name, - info->remote_ip_address, - info->local_ip_address, - info->account_name, - info->account_password, - info->server_process_type + "New World Server Info | name [{}] shortname [{}] remote_address [{}] local_address [{}] account [{}] password [{}] server_type [{}]", + r->server_long_name, + r->server_short_name, + r->remote_ip_address, + r->local_ip_address, + r->account_name, + r->account_password, + r->server_process_type ); - Handle_NewLSInfo(info); + HandleNewWorldserver(r); } -/** - * @param opcode - * @param packet - */ void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet) { LogNetcode( @@ -125,7 +113,7 @@ void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet packet.ToString() ); - if (packet.Length() < sizeof(ServerLSStatus_Struct)) { + if (packet.Length() < sizeof(LoginserverWorldStatusUpdate)) { LogError( "Received application packet from server that had opcode ServerOP_LSStatus, but was too small. Discarded to avoid buffer overrun" ); @@ -133,23 +121,19 @@ void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet return; } - auto *ls_status = (ServerLSStatus_Struct *) packet.Data(); + auto *ls_status = (LoginserverWorldStatusUpdate *) packet.Data(); LogDebug( - "World Server Status Update Received | Server [{0}] Status [{1}] Players [{2}] Zones [{3}]", - GetServerLongName(), + "World Server Status Update Received | Server [{}] Status [{}] Players [{}] Zones [{}]", + m_server_long_name, ls_status->status, ls_status->num_players, ls_status->num_zones ); - Handle_LSStatus(ls_status); + HandleWorldserverStatusUpdate(ls_status); } -/** - * @param opcode - * @param packet - */ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Net::Packet &packet) { LogNetcode( @@ -168,84 +152,74 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne return; } - auto *r = (UsertoWorldResponseLegacy_Struct *) packet.Data(); + auto *res = (UsertoWorldResponseLegacy_Struct *) packet.Data(); - LogDebug("Trying to find client with user id of [{0}]", r->lsaccountid); - Client *client = server.client_manager->GetClient(r->lsaccountid, "eqemu"); - if (client) { + LogDebug("Trying to find client with user id of [{}]", res->lsaccountid); + Client *c = server.client_manager->GetClient(res->lsaccountid, "eqemu"); + if (c) { LogDebug( - "Found client with user id of [{0}] and account name of [{1}]", - r->lsaccountid, - client->GetAccountName() + "Found client with user id of [{}] and account name of [{}]", + res->lsaccountid, + c->GetAccountName() ); auto *outapp = new EQApplicationPacket( OP_PlayEverquestResponse, - sizeof(PlayEverquestResponse_Struct) + sizeof(PlayEverquestResponse) ); - auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; - per->base_header.sequence = client->GetPlaySequence(); - per->server_number = client->GetPlayServerID(); + auto *play = (PlayEverquestResponse *) outapp->pBuffer; + play->base_header.sequence = c->GetCurrentPlaySequence(); + play->server_number = c->GetSelectedPlayServerID(); - if (r->response > 0) { - per->base_reply.success = true; - SendClientAuth( - client->GetConnection()->GetRemoteAddr(), - client->GetAccountName(), - client->GetKey(), - client->GetAccountID(), - client->GetLoginServerName() - ); + if (res->response > 0) { + play->base_reply.success = true; + SendClientAuthToWorld(c); } - switch (r->response) { + switch (res->response) { case UserToWorldStatusSuccess: - per->base_reply.error_str_id = LS::ErrStr::ERROR_NONE; + play->base_reply.error_str_id = LS::ErrStr::ERROR_NONE; break; case UserToWorldStatusWorldUnavail: - per->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE; + play->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE; break; case UserToWorldStatusSuspended: - per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED; + play->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED; break; case UserToWorldStatusBanned: - per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED; + play->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED; break; case UserToWorldStatusWorldAtCapacity: - per->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY; + play->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY; break; case UserToWorldStatusAlreadyOnline: - per->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER; + play->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER; break; default: - per->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN; + play->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN; break; } LogDebug( - "Sending play response: allowed [{0}] sequence [{1}] server number [{2}] message [{3}]", - per->base_reply.success, - per->base_header.sequence, - per->server_number, - per->base_reply.error_str_id + "Sending play response: allowed [{}] sequence [{}] server number [{}] message [{}]", + play->base_reply.success, + play->base_header.sequence, + play->server_number, + play->base_reply.error_str_id ); - client->SendPlayResponse(outapp); + c->SendPlayResponse(outapp); delete outapp; } else { LogError( - "Received User-To-World Response for [{0}] but could not find the client referenced!", - r->lsaccountid + "Received User-To-World Response for [{}] but could not find the client referenced!", + res->lsaccountid ); } } -/** - * @param opcode - * @param packet - */ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet) { LogNetcode( @@ -264,49 +238,44 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac return; } - auto user_to_world_response = (UsertoWorldResponse_Struct *) packet.Data(); - LogDebug("Trying to find client with user id of [{0}]", user_to_world_response->lsaccountid); + auto res = (UsertoWorldResponse_Struct *) packet.Data(); + LogDebug("Trying to find client with user id of [{}]", res->lsaccountid); Client *c = server.client_manager->GetClient( - user_to_world_response->lsaccountid, - user_to_world_response->login + res->lsaccountid, + res->login ); if (c) { - LogDebug("Found client with user id of [{0}] and account name of {1}", - user_to_world_response->lsaccountid, - c->GetAccountName().c_str() + LogDebug( + "Found client with user id of [{}] and account name of {}", + res->lsaccountid, + c->GetAccountName().c_str() ); auto *outapp = new EQApplicationPacket( OP_PlayEverquestResponse, - sizeof(PlayEverquestResponse_Struct) + sizeof(PlayEverquestResponse) ); - auto *r = (PlayEverquestResponse_Struct *) outapp->pBuffer; - r->base_header.sequence = c->GetPlaySequence(); - r->server_number = c->GetPlayServerID(); + auto *r = (PlayEverquestResponse *) outapp->pBuffer; + r->base_header.sequence = c->GetCurrentPlaySequence(); + r->server_number = c->GetSelectedPlayServerID(); LogDebug( - "Found sequence and play of [{0}] [{1}]", - c->GetPlaySequence(), - c->GetPlayServerID() + "Found sequence and play of [{}] [{}]", + c->GetCurrentPlaySequence(), + c->GetSelectedPlayServerID() ); - LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp)); + LogDebug("[Size: [{}]] {}", outapp->size, DumpPacketToString(outapp)); - if (user_to_world_response->response > 0) { + if (res->response > 0) { r->base_reply.success = true; - SendClientAuth( - c->GetConnection()->GetRemoteAddr(), - c->GetAccountName(), - c->GetKey(), - c->GetAccountID(), - c->GetLoginServerName() - ); + SendClientAuthToWorld(c); } - switch (user_to_world_response->response) { + switch (res->response) { case UserToWorldStatusSuccess: r->base_reply.error_str_id = LS::ErrStr::ERROR_NONE; break; @@ -331,7 +300,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac } LogDebug( - "Sending play response with following data, allowed [{0}], sequence {1}, server number {2}, message {3}", + "Sending play response with following data, allowed [{}], sequence {}, server number {}, message {}", r->base_reply.success, r->base_header.sequence, r->server_number, @@ -343,16 +312,12 @@ 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 + "Received User-To-World Response for [{}] but could not find the client referenced!.", + res->lsaccountid ); } } -/** - * @param opcode - * @param packet - */ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet) { LogNetcode( @@ -362,7 +327,7 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet packet.ToString() ); - if (packet.Length() < sizeof(ServerLSAccountUpdate_Struct)) { + if (packet.Length() < sizeof(LoginserverAccountUpdate)) { LogError( "Received application packet from server that had opcode ServerLSAccountUpdate_Struct, " "but was too small. Discarded to avoid buffer overrun" @@ -371,197 +336,184 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet return; } - LogDebug("ServerOP_LSAccountUpdate packet received from [{0}]", m_short_name); + LogDebug("ServerOP_LSAccountUpdate packet received from [{}]", m_server_short_name); - auto *loginserver_update = (ServerLSAccountUpdate_Struct *) packet.Data(); - if (IsServerTrusted()) { - LogDebug("ServerOP_LSAccountUpdate update processed for: [{0}]", loginserver_update->useraccount); - std::string name; - std::string password; - std::string email; + auto *r = (LoginserverAccountUpdate *) packet.Data(); + if (m_is_server_trusted) { + LogDebug("ServerOP_LSAccountUpdate update processed for: [{}]", r->user_account_name); - name.assign(loginserver_update->useraccount); - password.assign(loginserver_update->userpassword); + LoginAccountContext c{}; + c.username = r->user_account_name; + c.source_loginserver = "local"; + auto a = LoginAccountsRepository::GetAccountFromContext(database, c); + if (a.id > 0) { + a.account_email = r->user_email; + a.account_password = r->user_account_password; + a.last_ip_address = "0.0.0.0"; + LoginAccountsRepository::UpdateOne(database, a); + } + } +} - if (loginserver_update->user_email[0] != '\0') { - email.assign(loginserver_update->user_email); +void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req) +{ + if (m_is_server_logged_in) { + LogError( + "Login server was already marked as logged in, aborting" + ); + return; + } + + if (!HandleNewWorldserverValidation(req)) { + LogError("WorldServer::HandleNewWorldserver failed validation rules"); + return; + } + + SanitizeWorldServerName(req->server_long_name); + + m_server_long_name = req->server_long_name; + m_server_short_name = req->server_short_name; + m_account_password = req->account_password; + m_account_name = req->account_name; + m_local_ip = req->local_ip_address; + m_remote_ip_address = req->remote_ip_address; + m_server_version = req->server_version; + m_protocol = req->protocol_version; + m_server_process_type = req->server_process_type; + m_is_server_logged_in = true; + + // Handle Duplicate Servers + if (server.server_manager->DoesServerExist(m_server_long_name, m_server_short_name, this)) { + if (server.options.IsRejectingDuplicateServers()) { + LogError("World tried to login but a server with that name already exists"); + return; + } + LogInfo("World tried to login but a server with that name already exists, destroying [{}]", m_server_long_name); + server.server_manager->DestroyServerByName(m_server_long_name, m_server_short_name, this); + } + + LoginWorldContext c; + c.long_name = m_server_long_name; + c.short_name = m_server_short_name; + + LoginServerAdminsRepository::LoginServerAdmins admin; + + // Handle Admin Authentication + if (!m_account_name.empty() && !m_account_password.empty()) { + admin = LoginServerAdminsRepository::GetByName(database, m_account_name); + + LoginWorldAdminAccountContext ac; + ac.id = admin.id; + ac.username = m_account_name; + ac.password = m_account_password; + ac.password_hash = admin.account_password; + + if (admin.id && WorldServer::ValidateWorldServerAdminLogin(ac, admin)) { + LogDebug( + "Authenticated world admin [{}] ({}) for world [{}]", + m_account_name, + admin.id, + m_server_short_name + ); + c.admin_id = admin.id; + m_is_server_authorized_to_list = true; + } + } + + auto world = LoginWorldServersRepository::GetFromWorldContext(database, c); + if (!world.id) { + if (!server.options.IsUnregisteredAllowed()) { + LogError("WorldServer [{}] is not registered, and unregistered servers are not allowed", + m_server_long_name); + return; } - server.db->UpdateLSAccountInfo( - loginserver_update->useraccountid, - name, - password, - email + LogInfo("Server [{}] is not registered, handling as unregistered", m_server_long_name); + m_is_server_authorized_to_list = true; + + auto w = LoginWorldServersRepository::NewEntity(); + w.long_name = m_server_long_name; + w.short_name = m_server_short_name; + w.last_ip_address = m_remote_ip_address; + w.login_server_list_type_id = LS::ServerType::Standard; + w.last_login_date = std::time(nullptr); + auto created = LoginWorldServersRepository::InsertOne(database, w); + if (!created.id) { + LogError("Failed to auto-register world server [{}]", m_server_long_name); + return; + } + + LogInfo( + "Auto-registered world server [{}] with ID [{}]", + m_server_long_name, + created.id ); } -} - -/** - * 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 (IsServerLoggedIn()) { - LogError("WorldServer::Handle_NewLSInfo called but the login server was already marked as logged in, aborting"); - return; - } - - if (!HandleNewLoginserverInfoValidation(new_world_server_info_packet)) { - LogError("WorldServer::Handle_NewLSInfo failed validation rules"); - return; - } - - SanitizeWorldServerName(new_world_server_info_packet->server_long_name); - - 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(GetServerLongName(), GetServerShortName(), this)) { - LogError("World tried to login but there already exists a server that has that name"); - return; - } - } else { - if (server.server_manager->ServerExists(GetServerLongName(), GetServerShortName(), this)) { - LogInfo("World tried to login but there already exists a server that has that name, destroying [{}]", - m_long_name); - server.server_manager->DestroyServerByName(m_long_name, m_short_name, this); - } + m_server_description = world.tag_description; + m_server_id = world.id; + m_is_server_trusted = world.is_server_trusted; + m_server_list_type_id = world.login_server_list_type_id; + m_is_server_authorized_to_list = true; + + LogInfo( + "Server ID [{}] long_name [{}] short_name [{}] successfully authenticated", + world.id, + world.long_name, + world.short_name + ); } - uint32 world_server_admin_id = 0; - - /** - * If our world is trying to authenticate, let's try and pull the owner first to try associating - * with a world short_name - */ - if (!GetAccountName().empty() && !GetAccountPassword().empty()) { - Database::DbLoginServerAdmin - login_server_admin = server.db->GetLoginServerAdmin(GetAccountName()); - - if (login_server_admin.loaded) { - LogDebug( - "Attempting to authenticate world admin... [{0}] ({1}) against worldserver [{2}]", - GetAccountName(), - login_server_admin.id, - GetServerShortName() - ); - - if (WorldServer::ValidateWorldServerAdminLogin( - login_server_admin.id, - GetAccountName(), - GetAccountPassword(), - login_server_admin.account_password - )) { - LogDebug( - "Authenticating world admin... [{0}] ({1}) success! World ({2})", - GetAccountName(), - login_server_admin.id, - GetServerShortName() - ); - world_server_admin_id = login_server_admin.id; - - SetIsServerAuthorized(true); - } - } - } - - Database::DbWorldRegistration - world_registration = server.db->GetWorldRegistration( - GetServerShortName(), - GetServerLongName(), - world_server_admin_id + LogInfo( + "World registration id [{}] for server [{}] ip_address [{}]", + m_server_id, + m_server_long_name, + m_remote_ip_address ); - if (!server.options.IsUnregisteredAllowed()) { - if (!HandleNewLoginserverRegisteredOnly(world_registration)) { - LogError( - "WorldServer::HandleNewLoginserverRegisteredOnly checks failed with server [{0}]", - GetServerLongName() - ); - return; - } - } - else { - if (!HandleNewLoginserverInfoUnregisteredAllowed(world_registration)) { - LogError( - "WorldServer::HandleNewLoginserverInfoUnregisteredAllowed checks failed with server [{0}]", - GetServerLongName() - ); - return; - } - } - - server.db->UpdateWorldRegistration( - GetServerId(), - GetServerLongName(), - GetRemoteIp() - ); + // Update the last login date and IP address + world.last_login_date = std::time(nullptr); + world.last_ip_address = m_remote_ip_address; + LoginWorldServersRepository::UpdateOne(database, world); WorldServer::FormatWorldServerName( - new_world_server_info_packet->server_long_name, - world_registration.server_list_type + req->server_long_name, + m_server_list_type_id ); - SetLongName(new_world_server_info_packet->server_long_name); + + m_server_long_name = req->server_long_name; } -/** - * @param server_login_status - */ -void WorldServer::Handle_LSStatus(ServerLSStatus_Struct *server_login_status) +void WorldServer::HandleWorldserverStatusUpdate(LoginserverWorldStatusUpdate *u) { - SetPlayersOnline(server_login_status->num_players); - SetZonesBooted(server_login_status->num_zones); - SetServerStatus(server_login_status->status); + m_players_online = u->num_players; + m_zones_booted = u->num_zones; + m_server_status = u->status; } -/** - * @param ip - * @param account - * @param key - * @param account_id - * @param loginserver_name - */ -void WorldServer::SendClientAuth( - std::string ip, - std::string account, - std::string key, - unsigned int account_id, - const std::string &loginserver_name -) +void WorldServer::SendClientAuthToWorld(Client *c) { EQ::Net::DynamicPacket outapp; ClientAuth_Struct a{}; - a.loginserver_account_id = account_id; + a.loginserver_account_id = c->GetAccountID(); - strncpy(a.account_name, account.c_str(), 30); - strncpy(a.key, key.c_str(), 30); + strncpy(a.account_name, c->GetAccountName().c_str(), 30); + strncpy(a.key, c->GetLoginKey().c_str(), 30); a.lsadmin = 0; a.is_world_admin = 0; - a.ip = inet_addr(ip.c_str()); - strncpy(a.loginserver_name, &loginserver_name[0], 64); + a.ip = inet_addr(c->GetConnection()->GetRemoteAddr().c_str()); + strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64); - const std::string &client_address(ip); + const std::string &client_address(c->GetConnection()->GetRemoteAddr()); std::string world_address(m_connection->Handle()->RemoteIP()); if (client_address == world_address) { a.is_client_from_local_network = 1; } else if (IpUtil::IsIpInPrivateRfc1918(client_address)) { - LogInfo("Client is authenticating from a local address [{0}]", client_address); + LogInfo("Client is authenticating from a local address [{}]", client_address); a.is_client_from_local_network = 1; } else { @@ -572,14 +524,14 @@ void WorldServer::SendClientAuth( ip_addr.s_addr = a.ip; LogInfo( - "Client authentication response: world_address [{0}] client_address [{1}]", + "Client authentication response: world_address [{}] client_address [{}]", world_address, client_address ); LogInfo( - "Sending Client Authentication Response ls_account_id [{0}] ls_name [{1}] name [{2}] key [{3}] ls_admin [{4}] " - "world_admin [{5}] ip [{6}] local [{7}]", + "Sending Client Authentication Response ls_account_id [{}] ls_name [{}] name [{}] key [{}] ls_admin [{}] " + "world_admin [{}] ip [{}] local [{}]", a.loginserver_account_id, a.loginserver_name, a.account_name, @@ -610,71 +562,66 @@ constexpr static int MAX_SERVER_REMOTE_ADDRESS_LENGTH = 125; constexpr static int MAX_SERVER_VERSION_LENGTH = 64; constexpr static int MAX_SERVER_PROTOCOL_VERSION = 25; -/** - * @param new_world_server_info_packet - * @return - */ -bool WorldServer::HandleNewLoginserverInfoValidation( - ServerNewLSInfo_Struct *new_world_server_info_packet -) +bool WorldServer::HandleNewWorldserverValidation(LoginserverNewWorldRequest *r) { - 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); + if (strlen(r->account_name) >= MAX_ACCOUNT_NAME_LENGTH) { + LogError("HandleNewWorldserver error [account_name] was too long | max [{}]", 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); + else if (strlen(r->account_password) >= MAX_ACCOUNT_PASSWORD_LENGTH) { + LogError("HandleNewWorldserver error [account_password] was too long | max [{}]", 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); + else if (strlen(r->server_long_name) >= MAX_SERVER_LONG_NAME_LENGTH) { + LogError("HandleNewWorldserver error [server_long_name] was too long | max [{}]", 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); + else if (strlen(r->server_short_name) >= MAX_SERVER_SHORT_NAME_LENGTH) { + LogError("HandleNewWorldserver error [server_short_name] was too long | max [{}]", + MAX_SERVER_SHORT_NAME_LENGTH); return false; } - else if (strlen(new_world_server_info_packet->server_version) >= MAX_SERVER_VERSION_LENGTH) { - LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", MAX_SERVER_VERSION_LENGTH); + else if (strlen(r->server_version) >= MAX_SERVER_VERSION_LENGTH) { + LogError("HandleNewWorldserver error [server_version] was too long | max [{}]", 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); + else if (strlen(r->protocol_version) >= MAX_SERVER_PROTOCOL_VERSION) { + LogError("HandleNewWorldserver error [protocol_version] was too long | max [{}]", 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"); - SetLocalIp("127.0.0.1"); + if (strlen(r->local_ip_address) <= MAX_SERVER_LOCAL_ADDRESS_LENGTH) { + if (strlen(r->local_ip_address) == 0) { + LogError("HandleNewWorldserver error, local address was null, defaulting to localhost"); + m_local_ip = "127.0.0.1"; } else { - SetLocalIp(new_world_server_info_packet->local_ip_address); + m_local_ip = r->local_ip_address; } } else { - LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", MAX_SERVER_LOCAL_ADDRESS_LENGTH); + LogError("HandleNewWorldserver error, local address was too long | max [{}]", 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) { - SetRemoteIp(GetConnection()->Handle()->RemoteIP()); + if (strlen(r->remote_ip_address) <= MAX_SERVER_REMOTE_ADDRESS_LENGTH) { + if (strlen(r->remote_ip_address) == 0) { + m_remote_ip_address = GetConnection()->Handle()->RemoteIP(); LogWarning( - "Remote address was null, defaulting to stream address [{0}]", + "Remote address was null, defaulting to stream address [{}]", m_remote_ip_address ); } else { - SetRemoteIp(new_world_server_info_packet->remote_ip_address); + m_remote_ip_address = r->remote_ip_address; } } else { - SetRemoteIp(GetConnection()->Handle()->RemoteIP()); + m_remote_ip_address = GetConnection()->Handle()->RemoteIP(); LogWarning( - "Handle_NewLSInfo remote address was too long, defaulting to stream address [{0}]", + "HandleNewWorldserver remote address was too long, defaulting to stream address [{}]", m_remote_ip_address ); } @@ -682,303 +629,69 @@ bool WorldServer::HandleNewLoginserverInfoValidation( return true; } -/** - * @param world_registration - * @return - */ -bool WorldServer::HandleNewLoginserverRegisteredOnly( - Database::DbWorldRegistration &world_registration -) -{ - if (!GetAccountName().empty() && !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 == GetAccountName() && - WorldServer::ValidateWorldServerAdminLogin( - world_registration.server_admin_id, - GetAccountName(), - GetAccountPassword(), - world_registration.server_admin_account_password - ) - ); - - 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) { - - SetIsServerAuthorized(true); - - LogInfo( - "Server long_name [{0}] short_name [{1}] successfully logged into account that had no user/password requirement", - GetServerLongName(), - GetServerShortName() - ); - } - else if (does_world_server_pass_authentication_check) { - - SetIsServerAuthorized(true); - - LogInfo( - "Server long_name [{0}] short_name [{1}] successfully logged in", - GetServerLongName(), - GetServerShortName() - ); - - if (IsServerTrusted()) { - LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); - EQ::Net::DynamicPacket outapp; - m_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", - GetServerLongName(), - 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", - GetServerLongName(), - GetServerShortName() - ); - - return false; - } - } - else { - LogInfo( - "Server long_name [{0}] short_name [{1}] did not attempt to log in but only registered servers are allowed", - GetServerLongName(), - GetServerShortName() - ); - - return false; - } - - return true; -} - -/** - * @param world_registration - * @return - */ -bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( - Database::DbWorldRegistration &world_registration -) -{ - if (world_registration.loaded) { - SetServerDescription(world_registration.server_description) - ->SetServerId(world_registration.server_id) - ->SetIsServerTrusted(world_registration.is_server_trusted) - ->SetServerListTypeId(world_registration.server_list_type); - - SetIsServerAuthorized(true); - - bool does_world_server_pass_authentication_check = ( - world_registration.server_admin_account_name == GetAccountName() && - WorldServer::ValidateWorldServerAdminLogin( - world_registration.server_admin_id, - GetAccountName(), - GetAccountPassword(), - world_registration.server_admin_account_password - ) - ); - - bool does_world_server_have_non_empty_credentials = ( - !GetAccountName().empty() && - !GetAccountPassword().empty() - ); - - if (does_world_server_have_non_empty_credentials) { - if (does_world_server_pass_authentication_check) { - SetIsServerLoggedIn(true); - - LogInfo( - "Server long_name [{0}] short_name [{1}] successfully logged in", - GetServerLongName(), - GetServerShortName() - ); - - if (IsServerTrusted()) { - LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); - EQ::Net::DynamicPacket outapp; - m_connection->Send(ServerOP_LSAccountUpdate, outapp); - } - } - else { - // server is authorized to be on the loginserver list but didn't pass login check - LogInfo( - "Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not match the entry in the database. Unregistered still allowed", - GetServerLongName(), - GetServerShortName() - ); - } - } - else { - - // server is authorized to be on the loginserver list but didn't pass login check - if (!GetAccountName().empty() || !GetAccountPassword().empty()) { - LogInfo( - "Server [{0}] [{1}] did not login but this server required a password to login", - GetServerLongName(), - GetServerShortName() - ); - } - else { - LogInfo( - "Server [{0}] [{1}] did not login but unregistered servers are allowed", - GetServerLongName(), - GetServerShortName() - ); - } - } - } - else { - LogInfo( - "Server [{0}] ({1}) is not registered but unregistered servers are allowed", - GetServerLongName(), - GetServerShortName() - ); - - SetIsServerAuthorized(true); - - if (world_registration.loaded) { - return true; - } - - Database::DbLoginServerAdmin login_server_admin = server.db->GetLoginServerAdmin(GetAccountName()); - - uint32 server_admin_id = 0; - if (login_server_admin.loaded) { - if (WorldServer::ValidateWorldServerAdminLogin( - login_server_admin.id, - GetAccountName(), - GetAccountPassword(), - login_server_admin.account_password - )) { - server_admin_id = login_server_admin.id; - } - } - - // Auto create a registration - if (!server.db->CreateWorldRegistration( - GetServerLongName(), - GetServerShortName(), - GetRemoteIp(), - m_server_id, - server_admin_id - )) { - return false; - } - } - - return true; -} - -/** - * @param world_admin_id - * @param world_admin_username - * @param world_admin_password - * @param world_admin_password_hash - * @return - */ bool WorldServer::ValidateWorldServerAdminLogin( - int world_admin_id, - const std::string &world_admin_username, - const std::string &world_admin_password, - const std::string &world_admin_password_hash + LoginWorldAdminAccountContext &c, + LoginServerAdminsRepository::LoginServerAdmins &admin ) { auto encryption_mode = server.options.GetEncryptionMode(); - if (eqcrypt_verify_hash(world_admin_username, world_admin_password, world_admin_password_hash, encryption_mode)) { + if (eqcrypt_verify_hash(c.username, c.password, c.password_hash, encryption_mode)) { return true; } - else { - if (server.options.IsUpdatingInsecurePasswords()) { - if (encryption_mode < EncryptionModeArgon2) { - encryption_mode = EncryptionModeArgon2; - } - uint32 insecure_source_encryption_mode = 0; - if (world_admin_password_hash.length() == CryptoHash::md5_hash_length) { - for (int i = EncryptionModeMD5; i <= EncryptionModeMD5Triple; ++i) { - if (i != encryption_mode && - eqcrypt_verify_hash(world_admin_username, world_admin_password, world_admin_password_hash, i)) { - LogDebug("[{}] Checking for [{}] world admin", __func__, GetEncryptionByModeId(i)); - insecure_source_encryption_mode = i; - } - } - } - else if (world_admin_password_hash.length() == CryptoHash::sha1_hash_length && - insecure_source_encryption_mode == 0) { - for (int i = EncryptionModeSHA; i <= EncryptionModeSHATriple; ++i) { - if (i != encryption_mode && - eqcrypt_verify_hash(world_admin_username, world_admin_password, world_admin_password_hash, i)) { - LogDebug("[{}] Checking for [{}] world admin", __func__, GetEncryptionByModeId(i)); - insecure_source_encryption_mode = i; - } - } - } - else if (world_admin_password_hash.length() == CryptoHash::sha512_hash_length && - insecure_source_encryption_mode == 0) { - for (int i = EncryptionModeSHA512; i <= EncryptionModeSHA512Triple; ++i) { - if (i != encryption_mode && - eqcrypt_verify_hash(world_admin_username, world_admin_password, world_admin_password_hash, i)) { - LogDebug("[{}] Checking for [{}] world admin", __func__, GetEncryptionByModeId(i)); - insecure_source_encryption_mode = i; - } - } - } + if (encryption_mode < EncryptionModeArgon2) { + encryption_mode = EncryptionModeArgon2; + } - if (insecure_source_encryption_mode > 0) { - LogInfo( - "[{}] Updated insecure world_admin_username [{}] from mode [{}] ({}) to mode [{}] ({})", - __func__, - world_admin_username, - GetEncryptionByModeId(insecure_source_encryption_mode), - insecure_source_encryption_mode, - GetEncryptionByModeId(encryption_mode), - encryption_mode - ); - - std::string new_password_hash = eqcrypt_hash( - world_admin_username, - world_admin_password, - encryption_mode - ); - - server.db->UpdateLoginWorldAdminAccountPassword(world_admin_id, new_password_hash); - - return true; + uint32 insecure_source_encryption_mode = 0; + auto verify_encryption = [&](int start, int end) { + for (int i = start; i <= end; ++i) { + if (i != encryption_mode && eqcrypt_verify_hash(c.username, c.password, c.password_hash, i)) { + LogDebug("Checking for [{}] world admin", GetEncryptionByModeId(i)); + insecure_source_encryption_mode = i; } } + }; + + switch (c.password_hash.length()) { + case CryptoHash::md5_hash_length: + verify_encryption(EncryptionModeMD5, EncryptionModeMD5Triple); + break; + case CryptoHash::sha1_hash_length: + verify_encryption(EncryptionModeSHA, EncryptionModeSHATriple); + break; + case CryptoHash::sha512_hash_length: + verify_encryption(EncryptionModeSHA512, EncryptionModeSHA512Triple); + break; + } + + if (insecure_source_encryption_mode > 0) { + LogInfo( + "Updated insecure world_admin_username [{}] from mode [{}] ({}) to mode [{}] ({})", + c.username, + GetEncryptionByModeId(insecure_source_encryption_mode), + insecure_source_encryption_mode, + GetEncryptionByModeId(encryption_mode), + encryption_mode + ); + + admin.account_password = eqcrypt_hash(c.username, c.password, encryption_mode); + LoginServerAdminsRepository::UpdateOne(database, admin); + + return true; } return false; } -void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const +void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip, LSClientVersion version) const { // see LoginClientServerData_Struct if (use_local_ip) { out.WriteString(GetLocalIP()); } else { - out.WriteString(GetRemoteIP()); + out.WriteString(m_remote_ip_address); } if (version == cv_larion) { @@ -986,27 +699,27 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_lo } switch (GetServerListID()) { - case LS::ServerType::Legends: - out.WriteInt32(LS::ServerTypeFlags::Legends); - break; - case LS::ServerType::Preferred: - out.WriteInt32(LS::ServerTypeFlags::Preferred); - break; - default: - out.WriteInt32(LS::ServerTypeFlags::Standard); - break; + case LS::ServerType::Legends: + out.WriteInt32(LS::ServerTypeFlags::Legends); + break; + case LS::ServerType::Preferred: + out.WriteInt32(LS::ServerTypeFlags::Preferred); + break; + default: + out.WriteInt32(LS::ServerTypeFlags::Standard); + break; } if (version == cv_larion) { - auto server_id = GetServerId(); + auto server_id = m_server_id; //if this is 0, the client will not show the server in the list out.WriteUInt32(1); out.WriteUInt32(server_id); } else { - out.WriteUInt32(GetServerId()); + out.WriteUInt32(m_server_id); } - out.WriteString(GetServerLongName()); + out.WriteString(m_server_long_name); out.WriteString("us"); // country code out.WriteString("en"); // language code @@ -1026,281 +739,6 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_lo out.WriteUInt32(GetPlayersOnline()); } -/** - * @param in_server_list_id - * @return - */ -WorldServer *WorldServer::SetServerListTypeId(unsigned int in_server_list_id) -{ - m_server_list_type_id = in_server_list_id; - - return this; -} - -/** - * @return - */ -const std::string &WorldServer::GetServerDescription() const -{ - return m_server_description; -} - -/** - * @param in_server_description - */ -WorldServer *WorldServer::SetServerDescription(const std::string &in_server_description) -{ - WorldServer::m_server_description = in_server_description; - - return this; -} - -/** - * @return - */ -bool WorldServer::IsServerAuthorized() const -{ - return m_is_server_authorized; -} - -/** - * @param in_is_server_authorized - */ -WorldServer *WorldServer::SetIsServerAuthorized(bool in_is_server_authorized) -{ - WorldServer::m_is_server_authorized = in_is_server_authorized; - - return this; -} - -/** - * @return - */ -bool WorldServer::IsServerLoggedIn() const -{ - return m_is_server_logged_in; -} - -/** - * @param in_is_server_logged_in - */ -WorldServer *WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in) -{ - WorldServer::m_is_server_logged_in = in_is_server_logged_in; - - return this; -} - -/** - * @return - */ -bool WorldServer::IsServerTrusted() const -{ - return m_is_server_trusted; -} - -/** - * @param in_is_server_trusted - */ -WorldServer *WorldServer::SetIsServerTrusted(bool in_is_server_trusted) -{ - WorldServer::m_is_server_trusted = in_is_server_trusted; - - return this; -} - -/** - * @param in_zones_booted - */ -WorldServer *WorldServer::SetZonesBooted(unsigned int in_zones_booted) -{ - WorldServer::m_zones_booted = in_zones_booted; - - return this; -} - -/** - * @param in_players_online - */ -WorldServer *WorldServer::SetPlayersOnline(unsigned int in_players_online) -{ - WorldServer::m_players_online = in_players_online; - - return this; -} - -/** - * @param in_server_status - */ -WorldServer *WorldServer::SetServerStatus(int in_server_status) -{ - WorldServer::m_server_status = in_server_status; - - return this; -} - -/** - * @param in_server_process_type - */ -WorldServer *WorldServer::SetServerProcessType(unsigned int in_server_process_type) -{ - WorldServer::m_server_process_type = in_server_process_type; - - return this; -} - -/** - * @param in_long_name - */ -WorldServer *WorldServer::SetLongName(const std::string &in_long_name) -{ - WorldServer::m_long_name = in_long_name; - - return this; -} - -/** - * @param in_short_name - */ -WorldServer *WorldServer::SetShortName(const std::string &in_short_name) -{ - WorldServer::m_short_name = in_short_name; - - return this; -} - -/** - * @param in_account_name - */ -WorldServer *WorldServer::SetAccountName(const std::string &in_account_name) -{ - WorldServer::m_account_name = in_account_name; - - return this; -} - -/** - * @param in_account_password - */ -WorldServer *WorldServer::SetAccountPassword(const std::string &in_account_password) -{ - WorldServer::m_account_password = in_account_password; - - return this; -} - -/** - * @param in_remote_ip - */ -WorldServer *WorldServer::SetRemoteIp(const std::string &in_remote_ip) -{ - WorldServer::m_remote_ip_address = in_remote_ip; - - return this; -} - -/** - * @param in_local_ip - */ -WorldServer *WorldServer::SetLocalIp(const std::string &in_local_ip) -{ - WorldServer::m_local_ip = in_local_ip; - - return this; -} - -/** - * @param in_protocol - */ -WorldServer *WorldServer::SetProtocol(const std::string &in_protocol) -{ - WorldServer::m_protocol = in_protocol; - - return this; -} - -/** - * @param in_version - */ -WorldServer *WorldServer::SetVersion(const std::string &in_version) -{ - WorldServer::m_version = in_version; - - return this; -} - -/** - * @return - */ -int WorldServer::GetServerStatus() const -{ - return m_server_status; -} - -/** - * @return - */ -unsigned int WorldServer::GetServerListTypeId() const -{ - return m_server_list_type_id; -} - -/** - * @return - */ -unsigned int WorldServer::GetServerProcessType() const -{ - return m_server_process_type; -} - -/** - * @return - */ -const std::string &WorldServer::GetAccountName() const -{ - return m_account_name; -} - -/** - * @return - */ -const std::string &WorldServer::GetAccountPassword() const -{ - return m_account_password; -} - -/** - * @return - */ -const std::string &WorldServer::GetRemoteIp() const -{ - return m_remote_ip_address; -} - -/** - * @return - */ -const std::string &WorldServer::GetLocalIp() const -{ - return m_local_ip; -} - -/** - * @return - */ -const std::string &WorldServer::GetProtocol() const -{ - return m_protocol; -} - -/** - * @return - */ -const std::string &WorldServer::GetVersion() const -{ - return m_version; -} - void WorldServer::FormatWorldServerName(char *name, int8 server_list_type) { std::string server_long_name = name; diff --git a/loginserver/world_server.h b/loginserver/world_server.h index 45cecfebd..18b3c2010 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -5,9 +5,10 @@ #include "../common/net/servertalk_server_connection.h" #include "../common/servertalk.h" #include "../common/packet_dump.h" -#include "database.h" #include "../common/event/timer.h" #include "login_types.h" +#include "client.h" +#include "../common/repositories/login_server_admins_repository.h" #include #include @@ -17,25 +18,10 @@ class WorldServer { public: WorldServer(std::shared_ptr worldserver_connection); - - /** - * Destructor, frees our connection if it exists - */ ~WorldServer(); - - /** - * Resets the basic stats of this server. - */ void Reset(); - /** - * Accesses connection, it is intentional that this is not const (trust me). - */ std::shared_ptr GetConnection() { return m_connection; } - - /** - * @return - */ unsigned int GetServerId() const { return m_server_id; } WorldServer *SetServerId(unsigned int id) { @@ -43,25 +29,13 @@ public: return this; } - /** - * @return - */ - std::string GetServerLongName() const { return m_long_name; } - std::string GetServerShortName() const { return m_short_name; } - - /** - * Gets whether the server is authorized to show up on the server list or not - * @return - */ - bool IsAuthorized() const { return m_is_server_authorized; } + std::string GetServerLongName() const { return m_server_long_name; } + std::string GetServerShortName() const { return m_server_short_name; } + bool IsAuthorizedToList() const { return m_is_server_authorized_to_list; } std::string GetLocalIP() const { return m_local_ip; } std::string GetRemoteIP() const { return m_remote_ip_address; } - /** - * Gets what kind of server this server is (legends, preferred, normal) - * - * @return - */ + // Gets what kind of server this server is (legends, preferred, normal) unsigned int GetServerListID() const { return m_server_list_type_id; } WorldServer *SetServerListTypeId(unsigned int in_server_list_id); @@ -69,97 +43,18 @@ public: unsigned int GetZonesBooted() const { return m_zones_booted; } unsigned int GetPlayersOnline() const { return m_players_online; } - /** - * Takes the info struct we received from world and processes it - * - * @param new_world_server_info_packet - */ - void Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info_packet); - - /** - * Takes the status struct we received from world and processes it - * - * @param server_login_status - */ - 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. - * - * @param ip - * @param account - * @param key - * @param account_id - * @param loginserver_name - */ - void SendClientAuth( - std::string ip, - std::string account, - std::string key, - unsigned int account_id, - const std::string &loginserver_name - ); - - /** - * @param world_admin_id - * @param world_admin_username - * @param world_admin_password - * @param world_admin_password_hash - * @return - */ + void HandleNewWorldserver(LoginserverNewWorldRequest *req); + void HandleWorldserverStatusUpdate(LoginserverWorldStatusUpdate *u); + bool HandleNewWorldserverValidation(LoginserverNewWorldRequest *r); + void SendClientAuthToWorld(Client *c); static bool ValidateWorldServerAdminLogin( - int world_admin_id, - const std::string &world_admin_username, - const std::string &world_admin_password, - const std::string &world_admin_password_hash + LoginWorldAdminAccountContext &c, + LoginServerAdminsRepository::LoginServerAdmins &admin ); - - 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); - - void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const; + void SerializeForClientServerList(class SerializeBuffer &out, bool use_local_ip, LSClientVersion version) const; private: - /** - * Packet processing functions - * - * @param opcode - * @param packet - */ void ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packet); void ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet); void ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Net::Packet &packet); @@ -175,15 +70,15 @@ private: unsigned int m_server_list_type_id; unsigned int m_server_process_type; std::string m_server_description; - std::string m_long_name; - std::string m_short_name; + std::string m_server_long_name; + std::string m_server_short_name; std::string m_account_name; std::string m_account_password; std::string m_remote_ip_address; std::string m_local_ip; std::string m_protocol; - std::string m_version; - bool m_is_server_authorized; + std::string m_server_version; + bool m_is_server_authorized_to_list; bool m_is_server_logged_in; bool m_is_server_trusted; diff --git a/loginserver/world_server_manager.cpp b/loginserver/world_server_manager.cpp new file mode 100644 index 000000000..7bfdb2e2d --- /dev/null +++ b/loginserver/world_server_manager.cpp @@ -0,0 +1,218 @@ +#include "world_server_manager.h" +#include "login_server.h" +#include "login_types.h" +#include + +#include "../common/eqemu_logsys.h" +#include "../common/ip_util.h" + +extern LoginServer server; +extern bool run_server; + +WorldServerManager::WorldServerManager() +{ + int listen_port = server.config.GetVariableInt("general", "listen_port", 5998); + + m_server_connection = std::make_unique(); + EQ::Net::ServertalkServerOptions opts; + opts.port = listen_port; + opts.ipv6 = false; + m_server_connection->Listen(opts); + + LogInfo("Loginserver now listening on port [{}]", listen_port); + + m_server_connection->OnConnectionIdentified( + "World", [this](std::shared_ptr c) { + LogInfo( + "New World Server connection from remote_ip [{}] port [{}]", + c->Handle()->RemoteIP(), + c->Handle()->RemotePort() + ); + + auto iter = std::find_if( + m_world_servers.begin(), m_world_servers.end(), + [&](const std::unique_ptr &s) { + return s->GetConnection()->Handle()->RemoteIP() == c->Handle()->RemoteIP() && + s->GetConnection()->Handle()->RemotePort() == c->Handle()->RemotePort(); + } + ); + + if (iter != m_world_servers.end()) { + LogInfo( + "World server already existed for remote_ip [{}] port [{}] removing existing connection.", + c->Handle()->RemoteIP(), + c->Handle()->RemotePort() + ); + + m_world_servers.erase(iter); + } + + m_world_servers.push_back(std::make_unique(c)); + } + ); + + m_server_connection->OnConnectionRemoved( + "World", [this](std::shared_ptr c) { + auto iter = std::find_if( + m_world_servers.begin(), m_world_servers.end(), + [&](const std::unique_ptr &server) { + return server->GetConnection()->GetUUID() == c->GetUUID(); + } + ); + + if (iter != m_world_servers.end()) { + LogInfo( + "World server ID [{}] long_name [{}] short_name [{}] has been disconnected, removing.", + (*iter)->GetServerId(), + (*iter)->GetServerLongName(), + (*iter)->GetServerShortName() + ); + m_world_servers.erase(iter); + } + } + ); + +} + +WorldServerManager::~WorldServerManager() = default; + +std::unique_ptr WorldServerManager::CreateServerListPacket(Client *client, uint32 sequence) +{ + unsigned int server_count = 0; + in_addr in{}; + in.s_addr = client->GetConnection()->GetRemoteIP(); + std::string client_ip = inet_ntoa(in); + + LogDebug("ServerManager::CreateServerListPacket via client address [{}]", client_ip); + + for (const auto &world_server: m_world_servers) { + if (world_server->IsAuthorizedToList()) { + ++server_count; + } + } + + SerializeBuffer buf; + + // LoginBaseMessage_Struct header + buf.WriteInt32(sequence); + buf.WriteInt8(0); + buf.WriteInt8(0); + buf.WriteInt32(0); + + // LoginBaseReplyMessage_Struct + buf.WriteInt8(true); // success (no error) + buf.WriteInt32(0x65); // 101 "No Error" eqlsstr + buf.WriteString(""); + + // ServerListReply_Struct + buf.WriteInt32(server_count); + + for (const auto &s: m_world_servers) { + if (!s->IsAuthorizedToList()) { + LogDebug( + "ServerManager::CreateServerListPacket | Server [{}] via IP [{}] is not authorized to be listed", + s->GetServerLongName(), + s->GetConnection()->Handle()->RemoteIP() + ); + continue; + } + + bool use_local_ip = false; + + std::string world_ip = s->GetConnection()->Handle()->RemoteIP(); + if (world_ip == client_ip || IpUtil::IsIpInPrivateRfc1918(client_ip)) { + use_local_ip = true; + } + + LogDebug( + "CreateServerListPacket | Building list entry | Client [{}] IP [{}] Server Long Name [{}] Server IP [{}] ({})", + client->GetAccountName(), + client_ip, + s->GetServerLongName(), + use_local_ip ? s->GetLocalIP() : s->GetRemoteIP(), + use_local_ip ? "Local" : "Remote" + ); + + s->SerializeForClientServerList(buf, use_local_ip, client->GetClientVersion()); + } + + return std::make_unique(OP_ServerListResponse, buf); +} + +void WorldServerManager::SendUserLoginToWorldRequest( + unsigned int server_id, + unsigned int client_account_id, + const std::string &client_loginserver +) +{ + auto iter = std::find_if( + m_world_servers.begin(), m_world_servers.end(), + [&](const std::unique_ptr &server) { + return server->GetServerId() == server_id; + } + ); + + if (iter != m_world_servers.end()) { + EQ::Net::DynamicPacket outapp; + outapp.Resize(sizeof(UsertoWorldRequest_Struct)); + + auto *r = reinterpret_cast(outapp.Data()); + r->worldid = server_id; + r->lsaccountid = client_account_id; + strncpy(r->login, client_loginserver.c_str(), 64); + + (*iter)->GetConnection()->Send(ServerOP_UsertoWorldReq, outapp); + + LogNetcode("[UsertoWorldRequest] [Size: {}]\n{}", outapp.Length(), outapp.ToString()); + } + else { + LogError("Client requested a user to world but supplied an invalid id of {}", server_id); + } +} + +bool WorldServerManager::DoesServerExist( + const std::string &server_long_name, + const std::string &server_short_name, + WorldServer *ignore +) +{ + return std::any_of( + m_world_servers.begin(), m_world_servers.end(), [&](const std::unique_ptr &s) { + return s.get() != ignore && + s->GetServerLongName() == server_long_name && + s->GetServerShortName() == server_short_name; + } + ); +} + +void WorldServerManager::DestroyServerByName( + std::string server_long_name, + std::string server_short_name, + WorldServer *ignore +) +{ + std::erase_if( + m_world_servers, [&](const std::unique_ptr &s) { + if (s.get() == ignore) { + return false; + } + if (s->GetServerLongName() == server_long_name && + s->GetServerShortName() == server_short_name) { + s->GetConnection()->Handle()->Disconnect(); + LogInfo( + "Removing world server ID [{}] long name [{}] short name [{}]", + s->GetServerId(), + server_long_name, + server_short_name + ); + return true; + } + return false; + } + ); +} + +const std::list> &WorldServerManager::GetWorldServers() const +{ + return m_world_servers; +} diff --git a/loginserver/world_server_manager.h b/loginserver/world_server_manager.h new file mode 100644 index 000000000..711bc7560 --- /dev/null +++ b/loginserver/world_server_manager.h @@ -0,0 +1,33 @@ +#ifndef EQEMU_SERVERMANAGER_H +#define EQEMU_SERVERMANAGER_H + +#include "../common/global_define.h" +#include "../common/servertalk.h" +#include "../common/packet_dump.h" +#include "../common/net/servertalk_server.h" +#include "world_server.h" +#include "client.h" +#include + +class WorldServerManager { +public: + WorldServerManager(); + ~WorldServerManager(); + void SendUserLoginToWorldRequest( + unsigned int server_id, + unsigned int client_account_id, + const std::string &client_loginserver + ); + std::unique_ptr CreateServerListPacket(Client *client, uint32 sequence); + bool DoesServerExist(const std::string &s, const std::string &server_short_name, WorldServer *ignore = nullptr); + void DestroyServerByName(std::string s, std::string server_short_name, WorldServer *ignore = nullptr); + const std::list> &GetWorldServers() const; + +private: + std::unique_ptr m_server_connection; + std::list> m_world_servers; + +}; + +#endif + diff --git a/world/cliententry.cpp b/world/cliententry.cpp index 85dfe6ef9..08deb1429 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -164,9 +164,9 @@ void ClientListEntry::LSUpdate(ZoneServer *iZS) if (WorldConfig::get()->UpdateStats) { auto pack = new ServerPacket; pack->opcode = ServerOP_LSZoneInfo; - pack->size = sizeof(ZoneInfo_Struct); + pack->size = sizeof(LoginserverZoneInfoUpdate); pack->pBuffer = new uchar[pack->size]; - ZoneInfo_Struct *zone = (ZoneInfo_Struct *) pack->pBuffer; + auto *zone = (LoginserverZoneInfoUpdate *) pack->pBuffer; zone->count = iZS->NumPlayers(); zone->zone = iZS->GetZoneID(); zone->zone_wid = iZS->GetID(); @@ -174,6 +174,7 @@ void ClientListEntry::LSUpdate(ZoneServer *iZS) safe_delete(pack); } } + void ClientListEntry::LSZoneChange(ZoneToZone_Struct *ztz) { if (WorldConfig::get()->UpdateStats) { @@ -181,7 +182,7 @@ void ClientListEntry::LSZoneChange(ZoneToZone_Struct *ztz) pack->opcode = ServerOP_LSPlayerZoneChange; pack->size = sizeof(ServerLSPlayerZoneChange_Struct); pack->pBuffer = new uchar[pack->size]; - ServerLSPlayerZoneChange_Struct *zonechange = (ServerLSPlayerZoneChange_Struct *) pack->pBuffer; + auto *zonechange = (ServerLSPlayerZoneChange_Struct *) pack->pBuffer; zonechange->lsaccount_id = LSID(); zonechange->from = ztz->current_zone_id; zonechange->to = ztz->requested_zone_id; diff --git a/world/login_server.cpp b/world/login_server.cpp index b647aedf0..1b18889d9 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -1,20 +1,3 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - -This program 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 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY except by those people which sell it, which -are required to give you total support for your newly bought product; -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 this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ #include "../common/global_define.h" #include #include @@ -37,7 +20,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "cliententry.h" #include "world_config.h" - extern ZSList zoneserver_list; extern ClientList client_list; extern uint32 numzones; @@ -609,11 +591,11 @@ void LoginServer::SendInfo() auto pack = new ServerPacket; pack->opcode = ServerOP_NewLSInfo; - pack->size = sizeof(ServerNewLSInfo_Struct); + pack->size = sizeof(LoginserverNewWorldRequest); pack->pBuffer = new uchar[pack->size]; memset(pack->pBuffer, 0, pack->size); - auto *l = (ServerNewLSInfo_Struct *) pack->pBuffer; + auto *l = (LoginserverNewWorldRequest *) pack->pBuffer; strcpy(l->protocol_version, EQEMU_PROTOCOL_VERSION); strcpy(l->server_version, LOGIN_VERSION); strcpy(l->server_long_name, Config->LongName.c_str()); @@ -657,10 +639,10 @@ void LoginServer::SendStatus() auto pack = new ServerPacket; pack->opcode = ServerOP_LSStatus; - pack->size = sizeof(ServerLSStatus_Struct); + pack->size = sizeof(LoginserverWorldStatusUpdate); pack->pBuffer = new uchar[pack->size]; memset(pack->pBuffer, 0, pack->size); - auto loginserver_status = (ServerLSStatus_Struct *) pack->pBuffer; + auto loginserver_status = (LoginserverWorldStatusUpdate *) pack->pBuffer; if (WorldConfig::get()->Locked) { loginserver_status->status = -2; @@ -678,9 +660,6 @@ void LoginServer::SendStatus() delete pack; } -/** - * @param pack - */ void LoginServer::SendPacket(ServerPacket *pack) { if (m_legacy_client) { @@ -698,15 +677,15 @@ void LoginServer::SendAccountUpdate(ServerPacket *pack) return; } - auto *ls_account_update = (ServerLSAccountUpdate_Struct *) pack->pBuffer; + auto *req = (LoginserverAccountUpdate *) pack->pBuffer; if (CanUpdate()) { LogInfo( "Sending ServerOP_LSAccountUpdate packet to loginserver: [{0}]:[{1}]", m_loginserver_address, m_loginserver_port ); - strn0cpy(ls_account_update->worldaccount, m_login_account.c_str(), 30); - strn0cpy(ls_account_update->worldpassword, m_login_password.c_str(), 30); + strn0cpy(req->world_account, m_login_account.c_str(), 30); + strn0cpy(req->world_password, m_login_password.c_str(), 30); SendPacket(pack); } } diff --git a/zone/gm_commands/set/loginserver_info.cpp b/zone/gm_commands/set/loginserver_info.cpp index 3e42216de..5155bb284 100755 --- a/zone/gm_commands/set/loginserver_info.cpp +++ b/zone/gm_commands/set/loginserver_info.cpp @@ -14,14 +14,14 @@ void SetLoginserverInfo(Client *c, const Seperator *sep) const std::string& email = sep->arg[2]; const std::string& password = sep->arg[3]; - auto pack = new ServerPacket(ServerOP_LSAccountUpdate, sizeof(ServerLSAccountUpdate_Struct)); + auto pack = new ServerPacket(ServerOP_LSAccountUpdate, sizeof(LoginserverAccountUpdate)); - auto s = (ServerLSAccountUpdate_Struct *) pack->pBuffer; + auto s = (LoginserverAccountUpdate *) pack->pBuffer; - s->useraccountid = c->LSAccountID(); - strn0cpy(s->useraccount, c->AccountName(), 30); + s->user_account_id = c->LSAccountID(); + strn0cpy(s->user_account_name, c->AccountName(), 30); strn0cpy(s->user_email, email.c_str(), 100); - strn0cpy(s->userpassword, password.c_str(), 50); + strn0cpy(s->user_account_password, password.c_str(), 50); worldserver.SendPacket(pack); safe_delete(pack);